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