// Copyright (c) 2017 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Contains utils for reading, writing and debug printing bit streams. #ifndef SOURCE_COMP_HUFFMAN_CODEC_H_ #define SOURCE_COMP_HUFFMAN_CODEC_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace spvtools { namespace comp { // Used to generate and apply a Huffman coding scheme. // |Val| is the type of variable being encoded (for example a string or a // literal). template class HuffmanCodec { public: // Huffman tree node. struct Node { Node() {} // Creates Node from serialization leaving weight and id undefined. Node(const Val& in_value, uint32_t in_left, uint32_t in_right) : value(in_value), left(in_left), right(in_right) {} Val value = Val(); uint32_t weight = 0; // Ids are issued sequentially starting from 1. Ids are used as an ordering // tie-breaker, to make sure that the ordering (and resulting coding scheme) // are consistent accross multiple platforms. uint32_t id = 0; // Handles of children. uint32_t left = 0; uint32_t right = 0; }; // Creates Huffman codec from a histogramm. // Histogramm counts must not be zero. explicit HuffmanCodec(const std::map& hist) { if (hist.empty()) return; // Heuristic estimate. nodes_.reserve(3 * hist.size()); // Create NIL. CreateNode(); // The queue is sorted in ascending order by weight (or by node id if // weights are equal). std::vector queue_vector; queue_vector.reserve(hist.size()); std::priority_queue, std::function> queue(std::bind(&HuffmanCodec::LeftIsBigger, this, std::placeholders::_1, std::placeholders::_2), std::move(queue_vector)); // Put all leaves in the queue. for (const auto& pair : hist) { const uint32_t node = CreateNode(); MutableValueOf(node) = pair.first; MutableWeightOf(node) = pair.second; assert(WeightOf(node)); queue.push(node); } // Form the tree by combining two subtrees with the least weight, // and pushing the root of the new tree in the queue. while (true) { // We push a node at the end of each iteration, so the queue is never // supposed to be empty at this point, unless there are no leaves, but // that case was already handled. assert(!queue.empty()); const uint32_t right = queue.top(); queue.pop(); // If the queue is empty at this point, then the last node is // the root of the complete Huffman tree. if (queue.empty()) { root_ = right; break; } const uint32_t left = queue.top(); queue.pop(); // Combine left and right into a new tree and push it into the queue. const uint32_t parent = CreateNode(); MutableWeightOf(parent) = WeightOf(right) + WeightOf(left); MutableLeftOf(parent) = left; MutableRightOf(parent) = right; queue.push(parent); } // Traverse the tree and form encoding table. CreateEncodingTable(); } // Creates Huffman codec from saved tree structure. // |nodes| is the list of nodes of the tree, nodes[0] being NIL. // |root_handle| is the index of the root node. HuffmanCodec(uint32_t root_handle, std::vector&& nodes) { nodes_ = std::move(nodes); assert(!nodes_.empty()); assert(root_handle > 0 && root_handle < nodes_.size()); assert(!LeftOf(0) && !RightOf(0)); root_ = root_handle; // Traverse the tree and form encoding table. CreateEncodingTable(); } // Serializes the codec in the following text format: // (, { // {0, 0, 0}, // {val1, left1, right1}, // {val2, left2, right2}, // ... // }) std::string SerializeToText(int indent_num_whitespaces) const { const bool value_is_text = std::is_same::value; const std::string indent1 = std::string(indent_num_whitespaces, ' '); const std::string indent2 = std::string(indent_num_whitespaces + 2, ' '); std::stringstream code; code << "(" << root_ << ", {\n"; for (const Node& node : nodes_) { code << indent2 << "{"; if (value_is_text) code << "\""; code << node.value; if (value_is_text) code << "\""; code << ", " << node.left << ", " << node.right << "},\n"; } code << indent1 << "})"; return code.str(); } // Prints the Huffman tree in the following format: // w------w------'x' // w------'y' // Where w stands for the weight of the node. // Right tree branches appear above left branches. Taking the right path // adds 1 to the code, taking the left adds 0. void PrintTree(std::ostream& out) const { PrintTreeInternal(out, root_, 0); } // Traverses the tree and prints the Huffman table: value, code // and optionally node weight for every leaf. void PrintTable(std::ostream& out, bool print_weights = true) { std::queue> queue; queue.emplace(root_, ""); while (!queue.empty()) { const uint32_t node = queue.front().first; const std::string code = queue.front().second; queue.pop(); if (!RightOf(node) && !LeftOf(node)) { out << ValueOf(node); if (print_weights) out << " " << WeightOf(node); out << " " << code << std::endl; } else { if (LeftOf(node)) queue.emplace(LeftOf(node), code + "0"); if (RightOf(node)) queue.emplace(RightOf(node), code + "1"); } } } // Returns the Huffman table. The table was built at at construction time, // this function just returns a const reference. const std::unordered_map>& GetEncodingTable() const { return encoding_table_; } // Encodes |val| and stores its Huffman code in the lower |num_bits| of // |bits|. Returns false of |val| is not in the Huffman table. bool Encode(const Val& val, uint64_t* bits, size_t* num_bits) const { auto it = encoding_table_.find(val); if (it == encoding_table_.end()) return false; *bits = it->second.first; *num_bits = it->second.second; return true; } // Reads bits one-by-one using callback |read_bit| until a match is found. // Matching value is stored in |val|. Returns false if |read_bit| terminates // before a code was mathced. // |read_bit| has type bool func(bool* bit). When called, the next bit is // stored in |bit|. |read_bit| returns false if the stream terminates // prematurely. bool DecodeFromStream(const std::function& read_bit, Val* val) const { uint32_t node = root_; while (true) { assert(node); if (!RightOf(node) && !LeftOf(node)) { *val = ValueOf(node); return true; } bool go_right; if (!read_bit(&go_right)) return false; if (go_right) node = RightOf(node); else node = LeftOf(node); } assert(0); return false; } private: // Returns value of the node referenced by |handle|. Val ValueOf(uint32_t node) const { return nodes_.at(node).value; } // Returns left child of |node|. uint32_t LeftOf(uint32_t node) const { return nodes_.at(node).left; } // Returns right child of |node|. uint32_t RightOf(uint32_t node) const { return nodes_.at(node).right; } // Returns weight of |node|. uint32_t WeightOf(uint32_t node) const { return nodes_.at(node).weight; } // Returns id of |node|. uint32_t IdOf(uint32_t node) const { return nodes_.at(node).id; } // Returns mutable reference to value of |node|. Val& MutableValueOf(uint32_t node) { assert(node); return nodes_.at(node).value; } // Returns mutable reference to handle of left child of |node|. uint32_t& MutableLeftOf(uint32_t node) { assert(node); return nodes_.at(node).left; } // Returns mutable reference to handle of right child of |node|. uint32_t& MutableRightOf(uint32_t node) { assert(node); return nodes_.at(node).right; } // Returns mutable reference to weight of |node|. uint32_t& MutableWeightOf(uint32_t node) { return nodes_.at(node).weight; } // Returns mutable reference to id of |node|. uint32_t& MutableIdOf(uint32_t node) { return nodes_.at(node).id; } // Returns true if |left| has bigger weight than |right|. Node ids are // used as tie-breaker. bool LeftIsBigger(uint32_t left, uint32_t right) const { if (WeightOf(left) == WeightOf(right)) { assert(IdOf(left) != IdOf(right)); return IdOf(left) > IdOf(right); } return WeightOf(left) > WeightOf(right); } // Prints subtree (helper function used by PrintTree). void PrintTreeInternal(std::ostream& out, uint32_t node, size_t depth) const { if (!node) return; const size_t kTextFieldWidth = 7; if (!RightOf(node) && !LeftOf(node)) { out << ValueOf(node) << std::endl; } else { if (RightOf(node)) { std::stringstream label; label << std::setfill('-') << std::left << std::setw(kTextFieldWidth) << WeightOf(RightOf(node)); out << label.str(); PrintTreeInternal(out, RightOf(node), depth + 1); } if (LeftOf(node)) { out << std::string(depth * kTextFieldWidth, ' '); std::stringstream label; label << std::setfill('-') << std::left << std::setw(kTextFieldWidth) << WeightOf(LeftOf(node)); out << label.str(); PrintTreeInternal(out, LeftOf(node), depth + 1); } } } // Traverses the Huffman tree and saves paths to the leaves as bit // sequences to encoding_table_. void CreateEncodingTable() { struct Context { Context(uint32_t in_node, uint64_t in_bits, size_t in_depth) : node(in_node), bits(in_bits), depth(in_depth) {} uint32_t node; // Huffman tree depth cannot exceed 64 as histogramm counts are expected // to be positive and limited by numeric_limits::max(). // For practical applications tree depth would be much smaller than 64. uint64_t bits; size_t depth; }; std::queue queue; queue.emplace(root_, 0, 0); while (!queue.empty()) { const Context& context = queue.front(); const uint32_t node = context.node; const uint64_t bits = context.bits; const size_t depth = context.depth; queue.pop(); if (!RightOf(node) && !LeftOf(node)) { auto insertion_result = encoding_table_.emplace( ValueOf(node), std::pair(bits, depth)); assert(insertion_result.second); (void)insertion_result; } else { if (LeftOf(node)) queue.emplace(LeftOf(node), bits, depth + 1); if (RightOf(node)) queue.emplace(RightOf(node), bits | (1ULL << depth), depth + 1); } } } // Creates new Huffman tree node and stores it in the deleter array. uint32_t CreateNode() { const uint32_t handle = static_cast(nodes_.size()); nodes_.emplace_back(Node()); nodes_.back().id = next_node_id_++; return handle; } // Huffman tree root handle. uint32_t root_ = 0; // Huffman tree deleter. std::vector nodes_; // Encoding table value -> {bits, num_bits}. // Huffman codes are expected to never exceed 64 bit length (this is in fact // impossible if frequencies are stored as uint32_t). std::unordered_map> encoding_table_; // Next node id issued by CreateNode(); uint32_t next_node_id_ = 1; }; } // namespace comp } // namespace spvtools #endif // SOURCE_COMP_HUFFMAN_CODEC_H_