| // 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 <algorithm> |
| |
| #include "base/logging.h" |
| |
| namespace net { |
| |
| namespace huffman_trie { |
| |
| namespace { |
| |
| class HuffmanNode { |
| public: |
| HuffmanNode(uint8_t value, |
| uint32_t count, |
| std::unique_ptr<HuffmanNode> left, |
| std::unique_ptr<HuffmanNode> 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<HuffmanNode>& left() const { return left_; } |
| const std::unique_ptr<HuffmanNode>& right() const { return right_; } |
| |
| private: |
| uint8_t value_; |
| uint32_t count_; |
| std::unique_ptr<HuffmanNode> left_; |
| std::unique_ptr<HuffmanNode> right_; |
| }; |
| |
| bool CompareNodes(const std::unique_ptr<HuffmanNode>& lhs, |
| const std::unique_ptr<HuffmanNode>& 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<HuffmanNode> 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<uint8_t> HuffmanBuilder::ToVector() { |
| std::vector<uint8_t> bytes; |
| std::unique_ptr<HuffmanNode> node(BuildTree()); |
| WriteToVector(node.get(), &bytes); |
| return bytes; |
| } |
| |
| uint32_t HuffmanBuilder::WriteToVector(HuffmanNode* node, |
| std::vector<uint8_t>* 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<uint32_t>(vector->size()); |
| vector->push_back(left_value); |
| vector->push_back(right_value); |
| return position; |
| } |
| |
| std::unique_ptr<HuffmanNode> HuffmanBuilder::BuildTree() { |
| std::vector<std::unique_ptr<HuffmanNode>> nodes; |
| nodes.reserve(counts_.size()); |
| |
| for (const auto& item : counts_) { |
| nodes.push_back(std::make_unique<HuffmanNode>(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<HuffmanNode>(i, 0, nullptr, nullptr)); |
| } |
| |
| std::stable_sort(nodes.begin(), nodes.end(), CompareNodes); |
| |
| while (nodes.size() > 1) { |
| std::unique_ptr<HuffmanNode> a = std::move(nodes[0]); |
| std::unique_ptr<HuffmanNode> b = std::move(nodes[1]); |
| |
| uint32_t count_a = a->count(); |
| uint32_t count_b = b->count(); |
| |
| std::unique_ptr<HuffmanNode> 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 |