// Copyright (c) 2016 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/tools/huffman_trie/huffman/huffman_builder.h" #include #include "base/logging.h" namespace net { namespace huffman_trie { namespace { class HuffmanNode { public: HuffmanNode(uint8_t value, uint32_t count, std::unique_ptr left, std::unique_ptr right) : value_(value), count_(count), left_(std::move(left)), right_(std::move(right)) {} ~HuffmanNode() = default; bool IsLeaf() const { return left_.get() == nullptr && right_.get() == nullptr; } uint8_t value() const { return value_; } uint32_t count() const { return count_; } const std::unique_ptr& left() const { return left_; } const std::unique_ptr& right() const { return right_; } private: uint8_t value_; uint32_t count_; std::unique_ptr left_; std::unique_ptr right_; }; bool CompareNodes(const std::unique_ptr& lhs, const std::unique_ptr& rhs) { return lhs->count() < rhs->count(); } } // namespace HuffmanBuilder::HuffmanBuilder() = default; HuffmanBuilder::~HuffmanBuilder() = default; void HuffmanBuilder::RecordUsage(uint8_t character) { DCHECK(character < 128); counts_[character & 127] += 1; } HuffmanRepresentationTable HuffmanBuilder::ToTable() { HuffmanRepresentationTable table; std::unique_ptr node(BuildTree()); TreeToTable(node.get(), 0, 0, &table); return table; } void HuffmanBuilder::TreeToTable(HuffmanNode* node, uint32_t bits, uint32_t number_of_bits, HuffmanRepresentationTable* table) { if (node->IsLeaf()) { HuffmanRepresentation item; item.bits = bits; item.number_of_bits = number_of_bits; table->insert(HuffmanRepresentationPair(node->value(), item)); } else { uint32_t new_bits = bits << 1; TreeToTable(node->left().get(), new_bits, number_of_bits + 1, table); TreeToTable(node->right().get(), new_bits | 1, number_of_bits + 1, table); } } std::vector HuffmanBuilder::ToVector() { std::vector bytes; std::unique_ptr node(BuildTree()); WriteToVector(node.get(), &bytes); return bytes; } uint32_t HuffmanBuilder::WriteToVector(HuffmanNode* node, std::vector* vector) { uint8_t left_value; uint8_t right_value; uint32_t child_position; if (node->left()->IsLeaf()) { left_value = 128 | node->left()->value(); } else { child_position = WriteToVector(node->left().get(), vector); DCHECK(child_position < 512) << "huffman tree too large"; left_value = child_position / 2; } if (node->right()->IsLeaf()) { right_value = 128 | node->right()->value(); } else { child_position = WriteToVector(node->right().get(), vector); DCHECK(child_position < 512) << "huffman tree to large"; right_value = child_position / 2; } uint32_t position = static_cast(vector->size()); vector->push_back(left_value); vector->push_back(right_value); return position; } std::unique_ptr HuffmanBuilder::BuildTree() { std::vector> nodes; nodes.reserve(counts_.size()); for (const auto& item : counts_) { nodes.push_back(std::make_unique(item.first, item.second, nullptr, nullptr)); } // At least 2 entries are required for everything to work properly. Add // arbitrary values to fill the tree. for (uint8_t i = 0; nodes.size() < 2 && i < 2; ++i) { for (const auto& node : nodes) { if (node->value() == i) { break; } } nodes.push_back(std::make_unique(i, 0, nullptr, nullptr)); } std::stable_sort(nodes.begin(), nodes.end(), CompareNodes); while (nodes.size() > 1) { std::unique_ptr a = std::move(nodes[0]); std::unique_ptr b = std::move(nodes[1]); uint32_t count_a = a->count(); uint32_t count_b = b->count(); std::unique_ptr parent( new HuffmanNode(0, count_a + count_b, std::move(a), std::move(b))); nodes.erase(nodes.begin()); nodes[0] = std::move(parent); std::stable_sort(nodes.begin(), nodes.end(), CompareNodes); } return std::move(nodes[0]); } } // namespace huffman_trie } // namespace net