// Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "bsdiff/suffix_array_index.h" #include #include #include #include #include "bsdiff/logging.h" namespace { // libdivsufsort C++ overloaded functions used to allow calling the right // implementation based on the pointer size. int CallDivSufSort(const uint8_t* text, saidx_t* sa, size_t n) { return divsufsort(text, sa, n); } int CallDivSufSort(const uint8_t* text, saidx64_t* sa, size_t n) { return divsufsort64(text, sa, n); } saidx_t CallSaSearch(const uint8_t* text, size_t text_size, const uint8_t* pattern, size_t pattern_size, const saidx_t* sa, size_t sa_size, saidx_t* left) { return sa_search(text, text_size, pattern, pattern_size, sa, sa_size, left); } saidx64_t CallSaSearch(const uint8_t* text, size_t text_size, const uint8_t* pattern, size_t pattern_size, const saidx64_t* sa, size_t sa_size, saidx64_t* left) { return sa_search64(text, text_size, pattern, pattern_size, sa, sa_size, left); } } // namespace namespace bsdiff { // The SAIDX template type must be either saidx_t or saidx64_t, which will // depend on the maximum size of the input text needed. template class SuffixArrayIndex : public SuffixArrayIndexInterface { public: SuffixArrayIndex() = default; // Initialize and construct the suffix array of the |text| string of length // |n|. The memory pointed by |text| must be kept alive. Returns whether the // construction succeeded. bool Init(const uint8_t* text, size_t n); // SuffixArrayIndexInterface overrides. void SearchPrefix(const uint8_t* target, size_t length, size_t* out_length, uint64_t* out_pos) const override; private: const uint8_t* text_{nullptr}; // Owned by the caller. size_t n_{0}; std::vector sa_; }; template bool SuffixArrayIndex::Init(const uint8_t* text, size_t n) { if (!sa_.empty()) { // Already initialized. LOG(ERROR) << "SuffixArray already initialized"; return false; } if (static_cast(n) > static_cast(std::numeric_limits::max())) { LOG(ERROR) << "Input too big (" << n << ") for this implementation"; return false; } text_ = text; n_ = n; sa_.resize(n + 1); if (n > 0 && CallDivSufSort(text_, sa_.data(), n) != 0) { LOG(ERROR) << "divsufsrot() failed"; return false; } return true; } template void SuffixArrayIndex::SearchPrefix(const uint8_t* target, size_t length, size_t* out_length, uint64_t* out_pos) const { SAIDX suf_left; SAIDX count = CallSaSearch(text_, n_, target, length, sa_.data(), n_, &suf_left); if (count > 0) { // This is the simple case where we found the whole |target| string was // found. *out_pos = sa_[suf_left]; *out_length = length; return; } // In this case, |suf_left| points to the first suffix array position such // that the suffix at that position is lexicographically larger than |target|. // We only need to check whether the previous entry or the current entry is a // longer match. size_t prev_suffix_len = 0; if (suf_left > 0) { const size_t prev_max_len = std::min(n_ - static_cast(sa_[suf_left - 1]), length); const uint8_t* prev_suffix = text_ + sa_[suf_left - 1]; prev_suffix_len = std::mismatch(target, target + prev_max_len, prev_suffix).first - target; } size_t next_suffix_len = 0; if (static_cast(suf_left) < n_) { const uint8_t* next_suffix = text_ + sa_[suf_left]; const size_t next_max_len = std::min(n_ - static_cast(sa_[suf_left]), length); next_suffix_len = std::mismatch(target, target + next_max_len, next_suffix).first - target; } *out_length = std::max(next_suffix_len, prev_suffix_len); if (!*out_length) { *out_pos = 0; } else if (next_suffix_len >= prev_suffix_len) { *out_pos = sa_[suf_left]; } else { *out_pos = sa_[suf_left - 1]; } } std::unique_ptr CreateSuffixArrayIndex( const uint8_t* text, size_t n) { // The maximum supported size when using the suffix array based on the 32-bit // saidx_t type. We limit this to something a bit smaller (16 bytes smaller) // than the maximum representable number so references like "n + 1" are don't // overflow. const size_t kMaxSaidxSize = std::numeric_limits::max() - 16; std::unique_ptr ret; if (n > kMaxSaidxSize) { SuffixArrayIndex* sa_ptr = new SuffixArrayIndex(); ret.reset(sa_ptr); if (!sa_ptr->Init(text, n)) return nullptr; } else { SuffixArrayIndex* sa_ptr = new SuffixArrayIndex(); ret.reset(sa_ptr); if (!sa_ptr->Init(text, n)) return nullptr; } return ret; } } // namespace bsdiff