mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-12-01 01:36:09 +03:00
255 lines
6.8 KiB
C++
255 lines
6.8 KiB
C++
// Copyright 2018 The Chromium 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 "net/extras/preload_data/decoder.h"
|
|
#include "base/logging.h"
|
|
|
|
namespace net {
|
|
|
|
namespace extras {
|
|
|
|
PreloadDecoder::BitReader::BitReader(const uint8_t* bytes, size_t num_bits)
|
|
: bytes_(bytes),
|
|
num_bits_(num_bits),
|
|
num_bytes_((num_bits + 7) / 8),
|
|
current_byte_index_(0),
|
|
num_bits_used_(8) {}
|
|
|
|
// Next sets |*out| to the next bit from the input. It returns false if no
|
|
// more bits are available or true otherwise.
|
|
bool PreloadDecoder::BitReader::Next(bool* out) {
|
|
if (num_bits_used_ == 8) {
|
|
if (current_byte_index_ >= num_bytes_) {
|
|
return false;
|
|
}
|
|
current_byte_ = bytes_[current_byte_index_++];
|
|
num_bits_used_ = 0;
|
|
}
|
|
|
|
*out = 1 & (current_byte_ >> (7 - num_bits_used_));
|
|
num_bits_used_++;
|
|
return true;
|
|
}
|
|
|
|
// Read sets the |num_bits| least-significant bits of |*out| to the value of
|
|
// the next |num_bits| bits from the input. It returns false if there are
|
|
// insufficient bits in the input or true otherwise.
|
|
bool PreloadDecoder::BitReader::Read(unsigned num_bits, uint32_t* out) {
|
|
DCHECK_LE(num_bits, 32u);
|
|
|
|
uint32_t ret = 0;
|
|
for (unsigned i = 0; i < num_bits; ++i) {
|
|
bool bit;
|
|
if (!Next(&bit)) {
|
|
return false;
|
|
}
|
|
ret |= static_cast<uint32_t>(bit) << (num_bits - 1 - i);
|
|
}
|
|
|
|
*out = ret;
|
|
return true;
|
|
}
|
|
|
|
// Unary sets |*out| to the result of decoding a unary value from the input.
|
|
// It returns false if there were insufficient bits in the input and true
|
|
// otherwise.
|
|
bool PreloadDecoder::BitReader::Unary(size_t* out) {
|
|
size_t ret = 0;
|
|
|
|
for (;;) {
|
|
bool bit;
|
|
if (!Next(&bit)) {
|
|
return false;
|
|
}
|
|
if (!bit) {
|
|
break;
|
|
}
|
|
ret++;
|
|
}
|
|
|
|
*out = ret;
|
|
return true;
|
|
}
|
|
|
|
// Seek sets the current offest in the input to bit number |offset|. It
|
|
// returns true if |offset| is within the range of the input and false
|
|
// otherwise.
|
|
bool PreloadDecoder::BitReader::Seek(size_t offset) {
|
|
if (offset >= num_bits_) {
|
|
return false;
|
|
}
|
|
current_byte_index_ = offset / 8;
|
|
current_byte_ = bytes_[current_byte_index_++];
|
|
num_bits_used_ = offset % 8;
|
|
return true;
|
|
}
|
|
|
|
PreloadDecoder::HuffmanDecoder::HuffmanDecoder(const uint8_t* tree,
|
|
size_t tree_bytes)
|
|
: tree_(tree), tree_bytes_(tree_bytes) {}
|
|
|
|
bool PreloadDecoder::HuffmanDecoder::Decode(PreloadDecoder::BitReader* reader,
|
|
char* out) const {
|
|
const uint8_t* current = &tree_[tree_bytes_ - 2];
|
|
|
|
for (;;) {
|
|
bool bit;
|
|
if (!reader->Next(&bit)) {
|
|
return false;
|
|
}
|
|
|
|
uint8_t b = current[bit];
|
|
if (b & 0x80) {
|
|
*out = static_cast<char>(b & 0x7f);
|
|
return true;
|
|
}
|
|
|
|
unsigned offset = static_cast<unsigned>(b) * 2;
|
|
DCHECK_LT(offset, tree_bytes_);
|
|
if (offset >= tree_bytes_) {
|
|
return false;
|
|
}
|
|
|
|
current = &tree_[offset];
|
|
}
|
|
}
|
|
|
|
PreloadDecoder::PreloadDecoder(const uint8_t* huffman_tree,
|
|
size_t huffman_tree_size,
|
|
const uint8_t* trie,
|
|
size_t trie_bits,
|
|
size_t trie_root_position)
|
|
: huffman_decoder_(huffman_tree, huffman_tree_size),
|
|
bit_reader_(trie, trie_bits),
|
|
trie_root_position_(trie_root_position) {}
|
|
|
|
PreloadDecoder::~PreloadDecoder() {}
|
|
|
|
bool PreloadDecoder::Decode(const std::string& search, bool* out_found) {
|
|
size_t bit_offset = trie_root_position_;
|
|
*out_found = false;
|
|
|
|
// current_search_offset contains one more than the index of the current
|
|
// character in the search keyword that is being considered. It's one greater
|
|
// so that we can represent the position just before the beginning (with
|
|
// zero).
|
|
size_t current_search_offset = search.size();
|
|
|
|
for (;;) {
|
|
// Seek to the desired location.
|
|
if (!bit_reader_.Seek(bit_offset)) {
|
|
return false;
|
|
}
|
|
|
|
// Decode the unary length of the common prefix.
|
|
size_t prefix_length;
|
|
if (!bit_reader_.Unary(&prefix_length)) {
|
|
return false;
|
|
}
|
|
|
|
// Match each character in the prefix.
|
|
for (size_t i = 0; i < prefix_length; ++i) {
|
|
if (current_search_offset == 0) {
|
|
// We can't match the terminator with a prefix string.
|
|
return true;
|
|
}
|
|
|
|
char c;
|
|
if (!huffman_decoder_.Decode(&bit_reader_, &c)) {
|
|
return false;
|
|
}
|
|
if (search[current_search_offset - 1] != c) {
|
|
return true;
|
|
}
|
|
current_search_offset--;
|
|
}
|
|
|
|
bool is_first_offset = true;
|
|
size_t current_offset = 0;
|
|
|
|
// Next is the dispatch table.
|
|
for (;;) {
|
|
char c;
|
|
if (!huffman_decoder_.Decode(&bit_reader_, &c)) {
|
|
return false;
|
|
}
|
|
if (c == kEndOfTable) {
|
|
// No exact match.
|
|
return true;
|
|
}
|
|
|
|
if (c == kEndOfString) {
|
|
if (!ReadEntry(&bit_reader_, search, current_search_offset,
|
|
out_found)) {
|
|
return false;
|
|
}
|
|
if (current_search_offset == 0) {
|
|
CHECK(*out_found);
|
|
return true;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// The entries in a dispatch table are in order thus we can tell if there
|
|
// will be no match if the current character past the one that we want.
|
|
if (current_search_offset == 0 || search[current_search_offset - 1] < c) {
|
|
return true;
|
|
}
|
|
|
|
if (is_first_offset) {
|
|
// The first offset is backwards from the current position.
|
|
uint32_t jump_delta_bits;
|
|
uint32_t jump_delta;
|
|
if (!bit_reader_.Read(5, &jump_delta_bits) ||
|
|
!bit_reader_.Read(jump_delta_bits, &jump_delta)) {
|
|
return false;
|
|
}
|
|
|
|
if (bit_offset < jump_delta) {
|
|
return false;
|
|
}
|
|
|
|
current_offset = bit_offset - jump_delta;
|
|
is_first_offset = false;
|
|
} else {
|
|
// Subsequent offsets are forward from the target of the first offset.
|
|
uint32_t is_long_jump;
|
|
if (!bit_reader_.Read(1, &is_long_jump)) {
|
|
return false;
|
|
}
|
|
|
|
uint32_t jump_delta;
|
|
if (!is_long_jump) {
|
|
if (!bit_reader_.Read(7, &jump_delta)) {
|
|
return false;
|
|
}
|
|
} else {
|
|
uint32_t jump_delta_bits;
|
|
if (!bit_reader_.Read(4, &jump_delta_bits) ||
|
|
!bit_reader_.Read(jump_delta_bits + 8, &jump_delta)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
current_offset += jump_delta;
|
|
if (current_offset >= bit_offset) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
DCHECK_LT(0u, current_search_offset);
|
|
if (search[current_search_offset - 1] == c) {
|
|
bit_offset = current_offset;
|
|
current_search_offset--;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace extras
|
|
|
|
} // namespace net
|