blob: c15af44aed77bb2d6ebb3305f5604eda3513100c [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/tools/huffman_trie/trie/trie_bit_buffer.h"
#include <bit>
#include <cstdint>
#include <ostream>
#include "base/check.h"
#include "net/tools/huffman_trie/bit_writer.h"
namespace net::huffman_trie {
TrieBitBuffer::TrieBitBuffer() = default;
TrieBitBuffer::~TrieBitBuffer() = default;
void TrieBitBuffer::WriteBit(uint8_t bit) {
current_byte_ |= bit << (7 - used_);
used_++;
if (used_ == 8) {
Flush();
}
}
void TrieBitBuffer::WriteBits(uint32_t bits, uint8_t number_of_bits) {
DCHECK(number_of_bits <= 32);
for (uint8_t i = 1; i <= number_of_bits; i++) {
uint8_t bit = 1 & (bits >> (number_of_bits - i));
WriteBit(bit);
}
}
void TrieBitBuffer::WritePosition(uint32_t position, int32_t* last_position) {
// NOTE: If either of these values are changed, the corresponding values in
// net::extras::PreloadDecoder::Decode must also be changed.
constexpr uint8_t kShortOffsetMaxLength = 7;
constexpr uint8_t kLongOffsetLengthLength = 4;
// The maximum number of lengths in the long form is
// 2^kLongOffsetLengthLength, which added to kShortOffsetMaxLength gives the
// maximum bit length for |position|.
constexpr uint8_t kMaxBitLength =
kShortOffsetMaxLength + (1 << kLongOffsetLengthLength);
if (*last_position != -1) {
int32_t delta = position - *last_position;
DCHECK(delta > 0) << "delta position is not positive.";
uint8_t number_of_bits = std::bit_width<uint32_t>(delta);
DCHECK(number_of_bits <= kMaxBitLength)
<< "positive position delta too large.";
if (number_of_bits <= kShortOffsetMaxLength) {
WriteBits(0, 1);
WriteBits(delta, kShortOffsetMaxLength);
} else {
WriteBits(1, 1);
// The smallest length written when using the long offset form is one
// more than kShortOffsetMaxLength, and it is written as 0.
WriteBits(number_of_bits - kShortOffsetMaxLength - 1,
kLongOffsetLengthLength);
WriteBits(delta, number_of_bits);
}
*last_position = position;
return;
}
if (used_ != 0) {
Flush();
}
AppendPositionElement(position);
*last_position = position;
}
void TrieBitBuffer::WriteChar(uint8_t byte,
const HuffmanRepresentationTable& table,
HuffmanBuilder* huffman_builder) {
HuffmanRepresentationTable::const_iterator item;
item = table.find(byte);
DCHECK(item != table.end());
if (huffman_builder) {
huffman_builder->RecordUsage(byte);
}
WriteBits(item->second.bits, item->second.number_of_bits);
}
void TrieBitBuffer::WriteSize(size_t size) {
switch (size) {
case 0:
WriteBits(0b00, 2);
break;
case 1:
WriteBits(0b100, 3);
break;
case 2:
WriteBits(0b101, 3);
break;
case 3:
WriteBits(0b110, 3);
break;
default: {
WriteBit(size % 2);
for (size_t len = (size + 1) / 2; len > 0; --len) {
WriteBit(1);
}
WriteBit(0);
}
}
}
void TrieBitBuffer::AppendBitsElement(uint8_t bits, uint8_t number_of_bits) {
BitsOrPosition element;
element.bits = current_byte_;
element.number_of_bits = used_;
elements_.push_back(element);
}
void TrieBitBuffer::AppendPositionElement(uint32_t position) {
BitsOrPosition element;
element.position = position;
element.number_of_bits = 0;
elements_.push_back(element);
}
uint32_t TrieBitBuffer::WriteToBitWriter(BitWriter* writer) {
Flush();
uint32_t old_position = writer->position();
for (auto const& element : elements_) {
if (element.number_of_bits) {
writer->WriteBits(element.bits >> (8 - element.number_of_bits),
element.number_of_bits);
} else {
uint32_t current = old_position;
uint32_t target = element.position;
DCHECK(target < current) << "Reference is not backwards";
uint32_t delta = current - target;
uint8_t delta_number_of_bits = std::bit_width(delta);
DCHECK(delta_number_of_bits < 32) << "Delta too large";
writer->WriteBits(delta_number_of_bits, 5);
writer->WriteBits(delta, delta_number_of_bits);
}
}
return old_position;
}
void TrieBitBuffer::Flush() {
if (used_) {
AppendBitsElement(current_byte_, used_);
used_ = 0;
current_byte_ = 0;
}
}
} // namespace net::huffman_trie