#include "net/extras/preload_data/decoder.h"
#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/notreached.h"
namespace net::extras {
PreloadDecoder::BitReader::BitReader(base::span<const uint8_t> bytes,
size_t num_bits)
: bytes_(bytes), num_bits_(num_bits), num_bytes_((num_bits + 7) / 8) {}
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;
}
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;
}
namespace {
bool ReadBit(PreloadDecoder::BitReader* reader, uint8_t* bits) {
bool bit;
if (!reader->Next(&bit)) {
return false;
}
*bits <<= 1;
if (bit) {
(*bits)++;
}
return true;
}
}
bool PreloadDecoder::BitReader::DecodeSize(size_t* out) {
uint8_t bits = 0;
if (!ReadBit(this, &bits) || !ReadBit(this, &bits)) {
return false;
}
if (bits == 0) {
*out = 0;
return true;
}
if (!ReadBit(this, &bits)) {
return false;
}
bool is_even;
switch (bits) {
case 0b000:
case 0b001:
NOTREACHED();
case 0b010:
*out = 4;
return true;
case 0b011:
is_even = true;
break;
case 0b100:
*out = 1;
return true;
case 0b101:
*out = 2;
return true;
case 0b110:
*out = 3;
return true;
case 0b111:
is_even = false;
break;
default:
NOTREACHED();
}
size_t bit_length = 3;
while (true) {
bit_length++;
bool bit;
if (!Next(&bit)) {
return false;
}
if (!bit) {
break;
}
}
size_t ret = (bit_length - 2) * 2;
if (!is_even) {
ret--;
}
*out = ret;
return true;
}
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(base::span<const uint8_t> tree)
: tree_(tree) {}
bool PreloadDecoder::HuffmanDecoder::Decode(PreloadDecoder::BitReader* reader,
char* out) const {
base::span<const uint8_t> current = tree_.last<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;
if (offset >= tree_.size()) {
return false;
}
current = tree_.subspan(offset);
}
}
PreloadDecoder::PreloadDecoder(base::span<const uint8_t> huffman_tree,
base::span<const uint8_t> trie,
size_t trie_bits,
size_t trie_root_position)
: huffman_decoder_(huffman_tree),
bit_reader_(trie, trie_bits),
trie_root_position_(trie_root_position) {
CHECK_LE((trie_bits + 7) / 8, trie.size());
}
PreloadDecoder::~PreloadDecoder() = default;
bool PreloadDecoder::Decode(const std::string& search, bool* out_found) {
size_t bit_offset = trie_root_position_;
*out_found = false;
size_t current_search_offset = search.size();
for (;;) {
if (!bit_reader_.Seek(bit_offset)) {
return false;
}
size_t prefix_length;
if (!bit_reader_.DecodeSize(&prefix_length)) {
return false;
}
for (size_t i = 0; i < prefix_length; ++i) {
if (current_search_offset == 0) {
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;
for (;;) {
char c;
if (!huffman_decoder_.Decode(&bit_reader_, &c)) {
return false;
}
if (c == kEndOfTable) {
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;
}
if (current_search_offset == 0 || search[current_search_offset - 1] < c) {
return true;
}
if (is_first_offset) {
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 {
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;
}
}
}
NOTREACHED();
}
}