proxygen
HuffmanTests.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015-present, Facebook, Inc.
3  * All rights reserved.
4  *
5  * This source code is licensed under the BSD-style license found in the
6  * LICENSE file in the root directory of this source tree. An additional grant
7  * of patent rights can be found in the PATENTS file in the same directory.
8  *
9  */
10 #include <folly/io/Cursor.h>
11 #include <folly/io/IOBufQueue.h>
15 #include <tuple>
16 
17 using namespace folly::io;
18 using namespace folly;
19 using namespace proxygen::huffman;
20 using namespace proxygen;
21 using namespace std;
22 using namespace testing;
23 
24 class HuffmanTests : public testing::Test {
25  protected:
26  const HuffTree& tree_ = huffTree();
27 };
28 
30  uint32_t code;
31  uint8_t bits;
32  // check 'e' for both requests and responses
33  tie(code, bits) = tree_.getCode('e');
34  EXPECT_EQ(code, 0x05);
35  EXPECT_EQ(bits, 5);
36  // some extreme cases
37  tie(code, bits) = tree_.getCode(0);
38  EXPECT_EQ(code, 0x1ff8);
39  EXPECT_EQ(bits, 13);
40  tie(code, bits) = tree_.getCode(255);
41  EXPECT_EQ(code, 0x3ffffee);
42  EXPECT_EQ(bits, 26);
43 }
44 
46  uint32_t size;
47  folly::fbstring onebyte("x");
48  size = tree_.getEncodeSize(onebyte);
49  EXPECT_EQ(size, 1);
50 
51  folly::fbstring accept("accept-encoding");
52  size = tree_.getEncodeSize(accept);
53  EXPECT_EQ(size, 11);
54 }
55 
56 TEST_F(HuffmanTests, Encode) {
57  uint32_t size;
58  // this is going to fit perfectly into 3 bytes
59  folly::fbstring gzip("gzip");
60  IOBufQueue bufQueue;
61  QueueAppender appender(&bufQueue, 512);
62  // force the allocation
63  appender.ensure(512);
64 
65  size = tree_.encode(gzip, appender);
66  EXPECT_EQ(size, 3);
67  const IOBuf* buf = bufQueue.front();
68  const uint8_t* data = buf->data();
69  EXPECT_EQ(data[0], 0x9b); // 10011011
70  EXPECT_EQ(data[1], 0xd9); // 11011001
71  EXPECT_EQ(data[2], 0xab); // 10101011
72 
73  // size must equal with the actual encoding
74  folly::fbstring accept("accept-encoding");
75  size = tree_.getEncodeSize(accept);
76  uint32_t encodedSize = tree_.encode(accept, appender);
77  EXPECT_EQ(size, encodedSize);
78 }
79 
80 TEST_F(HuffmanTests, Decode) {
81  uint8_t buffer[3];
82  // simple test with one byte
83  buffer[0] = 0x60; //
84  buffer[1] = 0xbf; //
85  folly::fbstring literal;
86  tree_.decode(buffer, 2, literal);
87  EXPECT_EQ(literal, "/e");
88 
89  // simple test with "gzip"
90  buffer[0] = 0x9b;
91  buffer[1] = 0xd9;
92  buffer[2] = 0xab;
93  literal.clear();
94  tree_.decode(buffer, 3, literal);
95  EXPECT_EQ(literal, "gzip");
96 
97  // something with padding
98  buffer[0] = 0x98;
99  buffer[1] = 0xbf;
100  literal.clear();
101  tree_.decode(buffer, 2, literal);
102  EXPECT_EQ(literal, "ge");
103 }
104 
105 /*
106  * non-printable characters, that use 3 levels
107  */
108 TEST_F(HuffmanTests, NonPrintableDecode) {
109  // character code 9 and 38 (&) that have 24 + 8 = 32 bits
110  uint8_t buffer1[4] = {
111  0xFF, 0xFF, 0xEA, 0xF8
112  };
113  folly::fbstring literal;
114  tree_.decode(buffer1, 4, literal);
115  EXPECT_EQ(literal.size(), 2);
116  EXPECT_EQ((uint8_t)literal[0], 9);
117  EXPECT_EQ((uint8_t)literal[1], 38);
118 
119  // two weird characters and padding
120  // 1 and 240 will have 26 + 23 = 49 bits + 7 bits padding
121  uint8_t buffer2[7] = {
122  0xFF, 0xFF, 0xB1, 0xFF, 0xFF, 0xF5, 0xFF
123  };
124  literal.clear();
125  tree_.decode(buffer2, 7, literal);
126  EXPECT_EQ(literal.size(), 2);
127  EXPECT_EQ((uint8_t) literal[0], 1);
128  EXPECT_EQ((uint8_t) literal[1], 240);
129 }
130 
131 TEST_F(HuffmanTests, ExampleCom) {
132  // interesting case of one bit with value 0 in the last byte
133  IOBufQueue bufQueue;
134  QueueAppender appender(&bufQueue, 512);
135  appender.ensure(512);
136 
137  folly::fbstring example("www.example.com");
138  uint32_t size = tree_.getEncodeSize(example);
139  EXPECT_EQ(size, 12);
140  uint32_t encodedSize = tree_.encode(example, appender);
141  EXPECT_EQ(size, encodedSize);
142 
143  folly::fbstring decoded;
144  tree_.decode(bufQueue.front()->data(), size, decoded);
145  CHECK_EQ(example, decoded);
146 }
147 
148 TEST_F(HuffmanTests, UserAgent) {
149  folly::fbstring user_agent(
150  "Mozilla/5.0 (iPhone; CPU iPhone OS 7_0_4 like Mac OS X) AppleWebKit/537.51"
151  ".1 (KHTML, like Gecko) Mobile/11B554a [FBAN/FBIOS;FBAV/6.7;FBBV/566055;FBD"
152  "V/iPhone5,1;FBMD/iPhone;FBSN/iPhone OS;FBSV/7.0.4;FBSS/2; FBCR/AT&T;FBID/p"
153  "hone;FBLC/en_US;FBOP/5]");
154  IOBufQueue bufQueue;
155  QueueAppender appender(&bufQueue, 512);
156  appender.ensure(512);
157  const HuffTree& tree = huffTree();
158  uint32_t size = tree.getEncodeSize(user_agent);
159  uint32_t encodedSize = tree.encode(user_agent, appender);
160  EXPECT_EQ(size, encodedSize);
161 
162  folly::fbstring decoded;
163  tree.decode(bufQueue.front()->data(), size, decoded);
164  CHECK_EQ(user_agent, decoded);
165 }
166 
167 /*
168  * this test is verifying the CHECK for length at the end of huffman::encode()
169  */
170 TEST_F(HuffmanTests, FitInBuffer) {
171  IOBufQueue bufQueue;
172  QueueAppender appender(&bufQueue, 128);
173 
174  // call with an empty string
175  folly::fbstring literal("");
176  tree_.encode(literal, appender);
177 
178  // allow just 1 byte
179  appender.ensure(128);
180  appender.append(appender.length() - 1);
181  literal = "g";
182  tree_.encode(literal, appender);
183  CHECK_EQ(appender.length(), 0);
184 }
185 
186 /*
187  * sanity checks of each node in decode tree performed by a depth first search
188  *
189  * allSnodes is an array of up to 46 SuperHuffNode's, the 46 is hardcoded
190  * in creation
191  * nodeIndex is the current SuperHuffNode being visited
192  * depth is the depth of the current SuperHuffNode being visited
193  * fullCode remembers the code from parent HuffNodes
194  * eosCode stores the code for End-Of-String characters which the tables
195  * do not store
196  * eosCodeBits stores the number of bits for the End-Of-String character
197  * codes
198  */
200  const SuperHuffNode *allSnodes,
201  const uint32_t &snodeIndex,
202  const uint32_t &depth,
203  const uint32_t &fullCode,
204  const uint32_t &eosCode,
205  const uint32_t &eosCodeBits) {
206 
207  EXPECT_TRUE(depth < 4);
208 
209  unordered_set<uint32_t> leaves;
210  uint32_t subtreeLeafCount = 0;
211 
212  for (uint32_t i = 0; i < 256; i++) {
213  const HuffNode& node = allSnodes[snodeIndex].index[i];
214 
215  uint32_t newFullCode = fullCode ^ (i << (24 - 8 * depth));
216  uint32_t eosCodeDepth = (eosCodeBits - 1) / 8;
217 
218  if(eosCodeDepth == depth
219  && (newFullCode >> (32 - eosCodeBits)) == eosCode) {
220 
221  // this condition corresponds to an EOS code
222  // this should be a leaf that doesn't have supernode or bits set
223 
224  EXPECT_TRUE(node.isLeaf());
225  EXPECT_TRUE(node.metadata.bits == 0);
226  } else if (node.isLeaf()) {
227 
228  // this condition is a normal leaf
229  // this should have bits set
230 
231  EXPECT_TRUE(node.isLeaf());
232  EXPECT_TRUE(node.metadata.bits > 0);
233  EXPECT_TRUE(node.metadata.bits <= 8);
234 
235  // used to count unique leaves at this node
236  leaves.insert(node.data.ch);
237  } else {
238 
239  // this condition is a branching node
240  // this should have the superNodeIndex set but not bits should be set
241 
242  EXPECT_TRUE(!node.isLeaf());
243  EXPECT_TRUE(node.data.superNodeIndex > 0);
244  EXPECT_TRUE(node.metadata.bits == 0);
245  EXPECT_TRUE(node.data.superNodeIndex < 46);
246 
247  // keep track of leaf counts for this subtree
248  subtreeLeafCount += treeDfs(
249  allSnodes,
250  node.data.superNodeIndex,
251  depth + 1,
252  newFullCode,
253  eosCode,
254  eosCodeBits);
255  }
256  }
257  return subtreeLeafCount + leaves.size();
258 }
259 
264 class TestingHuffTree : public HuffTree {
265  public:
266 
267  explicit TestingHuffTree(const HuffTree& tree) : HuffTree(tree) {}
268 
270  return table_;
271  }
272 
274  TestingHuffTree reqTree(huffTree());
275  return reqTree;
276  }
277 
278 };
279 
280 TEST_F(HuffmanTests, SanityChecks) {
282  const SuperHuffNode* allSnodesReq = reqTree.getInternalTable();
283  uint32_t totalReqChars = treeDfs(allSnodesReq, 0, 0, 0, 0x3fffffff, 30);
284  EXPECT_EQ(totalReqChars, 256);
285 }
size_type size() const
Definition: FBString.h:1337
std::vector< uint8_t > buffer(kBufferSize+16)
const folly::IOBuf * front() const
Definition: IOBufQueue.h:476
uint32_t treeDfs(const SuperHuffNode *allSnodes, const uint32_t &snodeIndex, const uint32_t &depth, const uint32_t &fullCode, const uint32_t &eosCode, const uint32_t &eosCodeBits)
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
STL namespace.
const uint8_t * data() const
Definition: IOBuf.h:499
uint32_t encode(folly::StringPiece literal, folly::io::QueueAppender &buf) const
Definition: Huffman.cpp:133
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
Definition: lib.cpp:18
static TestingHuffTree getHuffTree()
struct proxygen::huffman::HuffNode::@104 metadata
const SuperHuffNode * getInternalTable()
constexpr auto size(C const &c) -> decltype(c.size())
Definition: Access.h:45
void append(size_t n)
Definition: Cursor.h:1124
union proxygen::huffman::HuffNode::@103 data
uint32_t getEncodeSize(folly::StringPiece literal) const
Definition: Huffman.cpp:182
constexpr auto data(C &c) -> decltype(c.data())
Definition: Access.h:71
bool isLeaf() const
Definition: Huffman.h:42
TEST_F(AsyncSSLSocketWriteTest, write_coalescing1)
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
const HuffTree & huffTree()
Definition: Huffman.cpp:252
bool decode(const uint8_t *buf, uint32_t size, folly::fbstring &literal) const
Definition: Huffman.cpp:31
TestingHuffTree(const HuffTree &tree)
void ensure(size_t n)
Definition: Cursor.h:1130
NetworkSocket accept(NetworkSocket s, sockaddr *addr, socklen_t *addrlen)
Definition: NetOps.cpp:71