1 // Copyright (c) 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/tools/huffman_trie/trie/trie_writer.h"
6
7 #include <algorithm>
8 #include <ostream>
9
10 #include "base/check.h"
11 #include "net/tools/huffman_trie/trie/trie_bit_buffer.h"
12
13 namespace net {
14
15 namespace huffman_trie {
16
17 namespace {
18
CompareReversedEntries(const std::unique_ptr<net::huffman_trie::ReversedEntry> & lhs,const std::unique_ptr<net::huffman_trie::ReversedEntry> & rhs)19 bool CompareReversedEntries(
20 const std::unique_ptr<net::huffman_trie::ReversedEntry>& lhs,
21 const std::unique_ptr<net::huffman_trie::ReversedEntry>& rhs) {
22 return lhs->reversed_name < rhs->reversed_name;
23 }
24
25 // Searches for the longest common prefix for all entries between |start| and
26 // |end|.
LongestCommonPrefix(ReversedEntries::const_iterator start,ReversedEntries::const_iterator end)27 std::vector<uint8_t> LongestCommonPrefix(ReversedEntries::const_iterator start,
28 ReversedEntries::const_iterator end) {
29 if (start == end) {
30 return std::vector<uint8_t>();
31 }
32
33 std::vector<uint8_t> prefix;
34 for (size_t i = 0;; ++i) {
35 if (i > (*start)->reversed_name.size()) {
36 break;
37 }
38
39 uint8_t candidate = (*start)->reversed_name.at(i);
40 if (candidate == kTerminalValue) {
41 break;
42 }
43
44 bool ok = true;
45 for (auto it = start + 1; it != end; ++it) {
46 if (i > (*it)->reversed_name.size() ||
47 (*it)->reversed_name.at(i) != candidate) {
48 ok = false;
49 break;
50 }
51 }
52
53 if (!ok) {
54 break;
55 }
56
57 prefix.push_back(candidate);
58 }
59
60 return prefix;
61 }
62
63 // Returns the reversed |hostname| as a vector of bytes. The reversed hostname
64 // will be terminated by |kTerminalValue|.
ReverseName(const std::string & hostname)65 std::vector<uint8_t> ReverseName(const std::string& hostname) {
66 size_t hostname_size = hostname.size();
67 std::vector<uint8_t> reversed_name(hostname_size + 1);
68
69 for (size_t i = 0; i < hostname_size; ++i) {
70 reversed_name[i] = hostname[hostname_size - i - 1];
71 }
72
73 reversed_name[reversed_name.size() - 1] = kTerminalValue;
74 return reversed_name;
75 }
76
77 // Removes the first |length| characters from all entries between |start| and
78 // |end|.
RemovePrefix(size_t length,ReversedEntries::iterator start,ReversedEntries::iterator end)79 void RemovePrefix(size_t length,
80 ReversedEntries::iterator start,
81 ReversedEntries::iterator end) {
82 for (auto it = start; it != end; ++it) {
83 (*it)->reversed_name.erase((*it)->reversed_name.begin(),
84 (*it)->reversed_name.begin() + length);
85 }
86 }
87
88 } // namespace
89
ReversedEntry(std::vector<uint8_t> reversed_name,const TrieEntry * entry)90 ReversedEntry::ReversedEntry(std::vector<uint8_t> reversed_name,
91 const TrieEntry* entry)
92 : reversed_name(reversed_name), entry(entry) {}
93
94 ReversedEntry::~ReversedEntry() = default;
95
TrieWriter(const huffman_trie::HuffmanRepresentationTable & huffman_table,huffman_trie::HuffmanBuilder * huffman_builder)96 TrieWriter::TrieWriter(
97 const huffman_trie::HuffmanRepresentationTable& huffman_table,
98 huffman_trie::HuffmanBuilder* huffman_builder)
99 : huffman_table_(huffman_table), huffman_builder_(huffman_builder) {}
100
101 TrieWriter::~TrieWriter() = default;
102
WriteEntries(const TrieEntries & entries,uint32_t * root_position)103 bool TrieWriter::WriteEntries(const TrieEntries& entries,
104 uint32_t* root_position) {
105 if (entries.empty())
106 return false;
107
108 ReversedEntries reversed_entries;
109 for (auto* const entry : entries) {
110 std::unique_ptr<ReversedEntry> reversed_entry(
111 new ReversedEntry(ReverseName(entry->name()), entry));
112 reversed_entries.push_back(std::move(reversed_entry));
113 }
114
115 std::stable_sort(reversed_entries.begin(), reversed_entries.end(),
116 CompareReversedEntries);
117
118 return WriteDispatchTables(reversed_entries.begin(), reversed_entries.end(),
119 root_position);
120 }
121
WriteDispatchTables(ReversedEntries::iterator start,ReversedEntries::iterator end,uint32_t * position)122 bool TrieWriter::WriteDispatchTables(ReversedEntries::iterator start,
123 ReversedEntries::iterator end,
124 uint32_t* position) {
125 DCHECK(start != end) << "No entries passed to WriteDispatchTables";
126
127 TrieBitBuffer writer;
128
129 std::vector<uint8_t> prefix = LongestCommonPrefix(start, end);
130 writer.WriteSize(prefix.size());
131
132 if (prefix.size()) {
133 for (size_t i = 0; i < prefix.size(); ++i) {
134 writer.WriteChar(prefix.at(i), huffman_table_, huffman_builder_);
135 }
136 }
137
138 RemovePrefix(prefix.size(), start, end);
139 int32_t last_position = -1;
140
141 while (start != end) {
142 uint8_t candidate = (*start)->reversed_name.at(0);
143 auto sub_entries_end = start + 1;
144
145 for (; sub_entries_end != end; sub_entries_end++) {
146 if ((*sub_entries_end)->reversed_name.at(0) != candidate) {
147 break;
148 }
149 }
150
151 writer.WriteChar(candidate, huffman_table_, huffman_builder_);
152
153 if (candidate == kTerminalValue) {
154 if (sub_entries_end - start != 1) {
155 return false;
156 }
157 if (!(*start)->entry->WriteEntry(&writer)) {
158 return false;
159 }
160 } else {
161 RemovePrefix(1, start, sub_entries_end);
162 uint32_t table_position;
163 if (!WriteDispatchTables(start, sub_entries_end, &table_position)) {
164 return false;
165 }
166
167 writer.WritePosition(table_position, &last_position);
168 }
169
170 start = sub_entries_end;
171 }
172
173 writer.WriteChar(kEndOfTableValue, huffman_table_, huffman_builder_);
174
175 *position = buffer_.position();
176 writer.Flush();
177 writer.WriteToBitWriter(&buffer_);
178 return true;
179 }
180
position() const181 uint32_t TrieWriter::position() const {
182 return buffer_.position();
183 }
184
Flush()185 void TrieWriter::Flush() {
186 buffer_.Flush();
187 }
188
189 } // namespace huffman_trie
190
191 } // namespace net
192