1 // Copyright 2017 The Chromium OS 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 "gtest/gtest.h"
6 
7 #include "puffin/src/puff_reader.h"
8 #include "puffin/src/puff_writer.h"
9 #include "puffin/src/unittest_common.h"
10 
11 namespace puffin {
12 
13 namespace {
TestLiteralLength(size_t length)14 void TestLiteralLength(size_t length) {
15   Buffer buf(length + 10);
16   PuffData pd;
17 
18   BufferPuffWriter pw(buf.data(), buf.size());
19   // We need to insert a metadata otherwise it will fail.
20   pd.type = PuffData::Type::kBlockMetadata;
21   pd.length = 1;
22   ASSERT_TRUE(pw.Insert(pd));
23 
24   BufferPuffReader pr(buf.data(), buf.size());
25   ASSERT_TRUE(pr.GetNext(&pd));
26   ASSERT_EQ(pd.type, PuffData::Type::kBlockMetadata);
27   ASSERT_EQ(pd.length, 1);
28 
29   // We insert |length| bytes.
30   pd.type = PuffData::Type::kLiterals;
31   pd.length = length;
32   pd.read_fn = [](uint8_t* buffer, size_t count) {
33     std::fill(buffer, buffer + count, 10);
34     return true;
35   };
36   ASSERT_TRUE(pw.Insert(pd));
37   ASSERT_TRUE(pw.Flush());
38 
39   pd.type = PuffData::Type::kLenDist;
40   pd.distance = 1;
41   pd.length = 3;
42   ASSERT_TRUE(pw.Insert(pd));
43 
44   ASSERT_TRUE(pr.GetNext(&pd));
45   if (length == 0) {
46     // If length is zero, then nothing should've been inserted.
47     ASSERT_EQ(pd.type, PuffData::Type::kLenDist);
48   } else {
49     // We have to see |length| bytes.
50     ASSERT_EQ(pd.type, PuffData::Type::kLiterals);
51     ASSERT_EQ(pd.length, length);
52     for (size_t i = 0; i < pd.length; i++) {
53       uint8_t byte;
54       pd.read_fn(&byte, 1);
55       EXPECT_EQ(byte, 10);
56     }
57   }
58 }
59 }  // namespace
60 
61 // Testing read/write from/into a puff buffer using |PuffReader|/|PuffWriter|.
TEST(PuffIOTest,InputOutputTest)62 TEST(PuffIOTest, InputOutputTest) {
63   Buffer buf(100);
64   BufferPuffReader pr(buf.data(), buf.size());
65   BufferPuffWriter pw(buf.data(), buf.size());
66   BufferPuffWriter epw(nullptr, 0);
67   uint8_t block = 123;
68 
69   {
70     PuffData pd;
71     pd.type = PuffData::Type::kBlockMetadata;
72     pd.block_metadata[0] = 0xCC;  // header
73     memcpy(&pd.block_metadata[1], &block, sizeof(block));
74     pd.length = sizeof(block) + 1;
75     ASSERT_TRUE(pw.Insert(pd));
76     ASSERT_TRUE(epw.Insert(pd));
77     ASSERT_TRUE(pw.Flush());
78     ASSERT_TRUE(epw.Flush());
79   }
80   {
81     PuffData pd;
82     ASSERT_TRUE(pr.GetNext(&pd));
83     ASSERT_EQ(pd.type, PuffData::Type::kBlockMetadata);
84     ASSERT_EQ(pd.length, sizeof(block) + 1);
85     ASSERT_EQ(pd.block_metadata[0], 0xCC);
86     ASSERT_EQ(pd.block_metadata[1], block);
87   }
88   {
89     PuffData pd;
90     pd.type = PuffData::Type::kLenDist;
91     pd.distance = 321;
92     pd.length = 3;
93     ASSERT_TRUE(pw.Insert(pd));
94     ASSERT_TRUE(epw.Insert(pd));
95     pd.length = 127;
96     ASSERT_TRUE(pw.Insert(pd));
97     ASSERT_TRUE(epw.Insert(pd));
98     pd.length = 258;
99     ASSERT_TRUE(pw.Insert(pd));
100     ASSERT_TRUE(epw.Insert(pd));
101     ASSERT_TRUE(pw.Flush());
102     ASSERT_TRUE(epw.Flush());
103 
104     pd.length = 259;
105     ASSERT_FALSE(pw.Insert(pd));
106     ASSERT_FALSE(epw.Insert(pd));
107   }
108   {
109     PuffData pd;
110     ASSERT_TRUE(pr.GetNext(&pd));
111     ASSERT_EQ(pd.type, PuffData::Type::kLenDist);
112     ASSERT_EQ(pd.distance, 321);
113     ASSERT_EQ(pd.length, 3);
114     ASSERT_TRUE(pr.GetNext(&pd));
115     ASSERT_EQ(pd.type, PuffData::Type::kLenDist);
116     ASSERT_EQ(pd.length, 127);
117     ASSERT_TRUE(pr.GetNext(&pd));
118     ASSERT_EQ(pd.type, PuffData::Type::kLenDist);
119     ASSERT_EQ(pd.length, 258);
120   }
121   {
122     PuffData pd;
123     pd.type = PuffData::Type::kEndOfBlock;
124     ASSERT_TRUE(pw.Insert(pd));
125     ASSERT_TRUE(epw.Insert(pd));
126     ASSERT_TRUE(pw.Flush());
127     ASSERT_TRUE(epw.Flush());
128   }
129   {
130     PuffData pd;
131     ASSERT_TRUE(pr.GetNext(&pd));
132     ASSERT_EQ(pd.type, PuffData::Type::kEndOfBlock);
133   }
134   {
135     PuffData pd;
136     pd.type = PuffData::Type::kBlockMetadata;
137     block = 123;
138     pd.block_metadata[0] = 0xCC;  // header
139     memcpy(&pd.block_metadata[1], &block, sizeof(block));
140     pd.length = sizeof(block) + 1;
141     ASSERT_TRUE(pw.Insert(pd));
142     ASSERT_TRUE(epw.Insert(pd));
143     ASSERT_TRUE(pw.Flush());
144     ASSERT_TRUE(epw.Flush());
145   }
146   {
147     PuffData pd;
148     ASSERT_TRUE(pr.GetNext(&pd));
149     ASSERT_EQ(pd.type, PuffData::Type::kBlockMetadata);
150     ASSERT_EQ(pd.length, sizeof(block) + 1);
151     ASSERT_EQ(pd.block_metadata[0], 0xCC);
152     ASSERT_EQ(pd.block_metadata[1], block);
153   }
154 
155   uint8_t tmp[] = {1, 2, 100};
156   {
157     PuffData pd;
158     size_t index = 0;
159     pd.type = PuffData::Type::kLiterals;
160     pd.length = 3;
161     pd.read_fn = [&tmp, &index](uint8_t* buffer, size_t count) {
162       if (count > 3 - index)
163         return false;
164       if (buffer != nullptr) {
165         memcpy(buffer, &tmp[index], count);
166       }
167       index += count;
168       return true;
169     };
170     ASSERT_TRUE(pw.Insert(pd));
171     ASSERT_TRUE(pw.Flush());
172     // We have to refresh the read_fn function for the second insert.
173     index = 0;
174     ASSERT_TRUE(epw.Insert(pd));
175     ASSERT_TRUE(epw.Flush());
176   }
177   {
178     PuffData pd;
179     pd.type = PuffData::Type::kLiteral;
180     pd.byte = 10;
181     ASSERT_TRUE(pw.Insert(pd));
182     ASSERT_TRUE(epw.Insert(pd));
183     ASSERT_TRUE(pw.Flush());
184     ASSERT_TRUE(epw.Flush());
185   }
186 
187   uint8_t tmp3[3];
188   {
189     PuffData pd;
190     ASSERT_TRUE(pr.GetNext(&pd));
191     ASSERT_EQ(pd.type, PuffData::Type::kLiterals);
192     ASSERT_EQ(pd.length, 3);
193     ASSERT_TRUE(pd.read_fn(tmp3, 3));
194     ASSERT_FALSE(pd.read_fn(tmp3, 1));
195     ASSERT_EQ(0, memcmp(tmp3, tmp, 3));
196   }
197   {
198     PuffData pd;
199     ASSERT_TRUE(pr.GetNext(&pd));
200     ASSERT_EQ(pd.type, PuffData::Type::kLiterals);
201     ASSERT_EQ(pd.length, 1);
202     ASSERT_TRUE(pd.read_fn(tmp3, 1));
203     ASSERT_EQ(tmp3[0], 10);
204     ASSERT_FALSE(pd.read_fn(tmp3, 2));
205   }
206   {
207     PuffData pd;
208     pd.type = PuffData::Type::kEndOfBlock;
209     ASSERT_TRUE(pw.Insert(pd));
210     ASSERT_TRUE(epw.Insert(pd));
211     ASSERT_TRUE(pw.Flush());
212     ASSERT_TRUE(epw.Flush());
213   }
214   {
215     PuffData pd;
216     ASSERT_TRUE(pr.GetNext(&pd));
217     ASSERT_EQ(pd.type, PuffData::Type::kEndOfBlock);
218   }
219 
220   ASSERT_EQ(buf.size() - pr.BytesLeft(), pw.Size());
221   ASSERT_EQ(buf.size() - pr.BytesLeft(), epw.Size());
222 }
223 
224 // Testing metadata boundary.
TEST(PuffIOTest,MetadataBoundaryTest)225 TEST(PuffIOTest, MetadataBoundaryTest) {
226   PuffData pd;
227   Buffer buf(3);
228   BufferPuffWriter pw(buf.data(), buf.size());
229 
230   // Block metadata takes two + varied bytes, so on a thre byte buffer, only one
231   // bytes is left for the varied part of metadata.
232   pd.type = PuffData::Type::kBlockMetadata;
233   pd.length = 2;
234   ASSERT_FALSE(pw.Insert(pd));
235   pd.length = 0;  // length should be at least 1.
236   ASSERT_FALSE(pw.Insert(pd));
237   pd.length = 1;
238   ASSERT_TRUE(pw.Insert(pd));
239 
240   Buffer puff_buffer = {0x00, 0x03, 0x02, 0x00, 0x00};
241   BufferPuffReader pr(puff_buffer.data(), puff_buffer.size());
242   ASSERT_FALSE(pr.GetNext(&pd));
243 }
244 
TEST(PuffIOTest,InvalidCopyLengthsDistanceTest)245 TEST(PuffIOTest, InvalidCopyLengthsDistanceTest) {
246   PuffData pd;
247   Buffer puff_buffer(20);
248   BufferPuffWriter pw(puff_buffer.data(), puff_buffer.size());
249 
250   // Invalid Lenght values.
251   pd.type = PuffData::Type::kLenDist;
252   pd.distance = 1;
253   pd.length = 0;
254   EXPECT_FALSE(pw.Insert(pd));
255   pd.length = 1;
256   EXPECT_FALSE(pw.Insert(pd));
257   pd.length = 2;
258   EXPECT_FALSE(pw.Insert(pd));
259   pd.length = 3;
260   EXPECT_TRUE(pw.Insert(pd));
261   pd.length = 259;
262   EXPECT_FALSE(pw.Insert(pd));
263   pd.length = 258;
264   EXPECT_TRUE(pw.Insert(pd));
265 
266   // Invalid distance values.
267   pd.length = 3;
268   pd.distance = 0;
269   EXPECT_FALSE(pw.Insert(pd));
270   pd.distance = 1;
271   EXPECT_TRUE(pw.Insert(pd));
272   pd.distance = 32769;
273   EXPECT_FALSE(pw.Insert(pd));
274   pd.distance = 32768;
275   EXPECT_TRUE(pw.Insert(pd));
276 
277   // First three bytes header, four bytes value lit/len, and four bytes
278   // invalid lit/len.
279   puff_buffer = {0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00,
280                  0x00, 0xFF, 0x82, 0x00, 0x00};
281   BufferPuffReader pr(puff_buffer.data(), puff_buffer.size());
282   EXPECT_TRUE(pr.GetNext(&pd));
283   EXPECT_EQ(pd.type, PuffData::Type::kBlockMetadata);
284   EXPECT_TRUE(pr.GetNext(&pd));
285   EXPECT_EQ(pd.type, PuffData::Type::kLenDist);
286   EXPECT_FALSE(pr.GetNext(&pd));
287 }
288 
TEST(PuffIOTest,InvalidCopyLenghtDistanceBoundaryTest)289 TEST(PuffIOTest, InvalidCopyLenghtDistanceBoundaryTest) {
290   PuffData pd;
291   Buffer puff_buffer(5);
292 
293   pd.type = PuffData::Type::kLenDist;
294   pd.distance = 1;
295   pd.length = 129;
296   for (size_t i = 1; i < 2; i++) {
297     BufferPuffWriter pw(puff_buffer.data(), i);
298     EXPECT_FALSE(pw.Insert(pd));
299   }
300 
301   pd.length = 130;
302   for (size_t i = 1; i < 3; i++) {
303     BufferPuffWriter pw(puff_buffer.data(), i);
304     EXPECT_FALSE(pw.Insert(pd));
305   }
306 
307   // First three bytes header, three bytes value lit/len.
308   puff_buffer = {0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00};
309   BufferPuffReader pr(puff_buffer.data(), puff_buffer.size());
310   EXPECT_TRUE(pr.GetNext(&pd));
311   EXPECT_EQ(pd.type, PuffData::Type::kBlockMetadata);
312   EXPECT_FALSE(pr.GetNext(&pd));
313 }
314 
TEST(PuffIOTest,LiteralsTest)315 TEST(PuffIOTest, LiteralsTest) {
316   TestLiteralLength(0);
317   TestLiteralLength(1);
318   TestLiteralLength(2);
319   TestLiteralLength(126);
320   TestLiteralLength(127);
321   TestLiteralLength(128);
322 }
323 
324 // Testing maximum literals length.
TEST(PuffIOTest,MaxLiteralsTest)325 TEST(PuffIOTest, MaxLiteralsTest) {
326   Buffer buf((1 << 16) + 127 + 20);
327   PuffData pd;
328 
329   BufferPuffWriter pw(buf.data(), buf.size());
330   // We need to insert a metadata otherwise it will fail.
331   pd.type = PuffData::Type::kBlockMetadata;
332   pd.length = 1;
333   ASSERT_TRUE(pw.Insert(pd));
334 
335   pd.type = PuffData::Type::kLiterals;
336   pd.length = (1 << 16);
337   pd.read_fn = [](uint8_t* buffer, size_t count) {
338     std::fill(buffer, buffer + count, 10);
339     return true;
340   };
341   ASSERT_TRUE(pw.Insert(pd));
342   ASSERT_TRUE(pw.Flush());
343 
344   BufferPuffReader pr(buf.data(), buf.size());
345   ASSERT_TRUE(pr.GetNext(&pd));
346   ASSERT_EQ(pd.type, PuffData::Type::kBlockMetadata);
347   ASSERT_EQ(pd.length, 1);
348 
349   ASSERT_TRUE(pr.GetNext(&pd));
350   ASSERT_EQ(pd.type, PuffData::Type::kLiterals);
351   ASSERT_EQ(pd.length, 1 << 16);
352   for (size_t i = 0; i < pd.length; i++) {
353     uint8_t byte;
354     pd.read_fn(&byte, 1);
355     ASSERT_EQ(byte, 10);
356   }
357 
358   BufferPuffWriter pw2(buf.data(), buf.size());
359   pd.type = PuffData::Type::kBlockMetadata;
360   pd.length = 1;
361   ASSERT_TRUE(pw2.Insert(pd));
362 
363   pd.type = PuffData::Type::kLiteral;
364   pd.length = 1;
365   pd.byte = 12;
366   // We have to be able to fill 65663 bytes.
367   for (size_t i = 0; i < ((1 << 16) + 127); i++) {
368     ASSERT_TRUE(pw2.Insert(pd));
369   }
370   // If we add one more, then it should have been flushed.
371   pd.byte = 13;
372   ASSERT_TRUE(pw2.Insert(pd));
373   ASSERT_TRUE(pw2.Flush());
374 
375   // Now read it back.
376   BufferPuffReader pr2(buf.data(), buf.size());
377   ASSERT_TRUE(pr2.GetNext(&pd));
378   ASSERT_EQ(pd.type, PuffData::Type::kBlockMetadata);
379 
380   // Now we should read on kLiterals with lenght 1 << 16 and just one literal
381   // after that.
382   ASSERT_TRUE(pr2.GetNext(&pd));
383   ASSERT_EQ(pd.type, PuffData::Type::kLiterals);
384   ASSERT_EQ(pd.length, (1 << 16) + 127);
385   for (size_t i = 0; i < pd.length; i++) {
386     uint8_t byte;
387     pd.read_fn(&byte, 1);
388     ASSERT_EQ(byte, 12);
389   }
390 
391   ASSERT_TRUE(pr2.GetNext(&pd));
392   ASSERT_EQ(pd.type, PuffData::Type::kLiterals);
393   ASSERT_EQ(pd.length, 1);
394   uint8_t byte;
395   pd.read_fn(&byte, 1);
396   ASSERT_EQ(byte, 13);
397 }
398 
399 }  // namespace puffin
400