1
2
3
4
5 package snappy
6
7 import (
8 "bytes"
9 "encoding/binary"
10 "flag"
11 "fmt"
12 "io"
13 "math/rand"
14 "net/http"
15 "os"
16 "path/filepath"
17 "strings"
18 "testing"
19
20 "github.com/klauspost/compress/s2"
21 )
22
23 var (
24 download = flag.Bool("download", false, "If true, download any missing files before running benchmarks")
25 testdataDir = flag.String("testdataDir", "testdata", "Directory containing the test data")
26 benchdataDir = flag.String("benchdataDir", "testdata/bench", "Directory containing the benchmark data")
27 )
28
29 const (
30 tagLiteral = 0x00
31 tagCopy1 = 0x01
32 tagCopy2 = 0x02
33 tagCopy4 = 0x03
34 )
35
36 const (
37 checksumSize = 4
38 chunkHeaderSize = 4
39 magicChunk = "\xff\x06\x00\x00" + magicBody
40 magicBody = "sNaPpY"
41
42 obufHeaderLen = len(magicChunk) + checksumSize + chunkHeaderSize
43 )
44
45 const (
46 chunkTypeCompressedData = 0x00
47 chunkTypeUncompressedData = 0x01
48 chunkTypePadding = 0xfe
49 chunkTypeStreamIdentifier = 0xff
50 )
51
52 const goEncoderShouldMatchCppEncoder = false
53
54 func TestMaxEncodedLenOfMaxBlockSize(t *testing.T) {
55 maxEncodedLenOfMaxBlockSize := s2.MaxEncodedLen(maxBlockSize)
56 got := maxEncodedLenOfMaxBlockSize
57 want := MaxEncodedLen(maxBlockSize)
58 if got != want {
59 t.Fatalf("got %d, want %d", got, want)
60 }
61 }
62
63 func cmp(a, b []byte) error {
64 if bytes.Equal(a, b) {
65 return nil
66 }
67 if len(a) != len(b) {
68 return fmt.Errorf("got %d bytes, want %d", len(a), len(b))
69 }
70 for i := range a {
71 if a[i] != b[i] {
72 return fmt.Errorf("byte #%d: got 0x%02x, want 0x%02x", i, a[i], b[i])
73 }
74 }
75 return nil
76 }
77
78 func roundtrip(b, ebuf, dbuf []byte) error {
79 d, err := Decode(dbuf, Encode(ebuf, b))
80 if err != nil {
81 return fmt.Errorf("decoding error: %v", err)
82 }
83 if err := cmp(d, b); err != nil {
84 return fmt.Errorf("roundtrip mismatch: %v", err)
85 }
86 return nil
87 }
88
89 func TestEmpty(t *testing.T) {
90 if err := roundtrip(nil, nil, nil); err != nil {
91 t.Fatal(err)
92 }
93 }
94
95 func TestSmallCopy(t *testing.T) {
96 for _, ebuf := range [][]byte{nil, make([]byte, 20), make([]byte, 64)} {
97 for _, dbuf := range [][]byte{nil, make([]byte, 20), make([]byte, 64)} {
98 for i := 0; i < 32; i++ {
99 s := "aaaa" + strings.Repeat("b", i) + "aaaabbbb"
100 if err := roundtrip([]byte(s), ebuf, dbuf); err != nil {
101 t.Errorf("len(ebuf)=%d, len(dbuf)=%d, i=%d: %v", len(ebuf), len(dbuf), i, err)
102 }
103 }
104 }
105 }
106 }
107
108 func TestSmallRand(t *testing.T) {
109 rng := rand.New(rand.NewSource(1))
110 for n := 1; n < 20000; n += 23 {
111 b := make([]byte, n)
112 for i := range b {
113 b[i] = uint8(rng.Intn(256))
114 }
115 if err := roundtrip(b, nil, nil); err != nil {
116 t.Fatal(err)
117 }
118 }
119 }
120
121 func TestSmallRegular(t *testing.T) {
122 for n := 1; n < 20000; n += 23 {
123 b := make([]byte, n)
124 for i := range b {
125 b[i] = uint8(i%10 + 'a')
126 }
127 if err := roundtrip(b, nil, nil); err != nil {
128 t.Fatal(err)
129 }
130 }
131 }
132
133 func TestInvalidVarint(t *testing.T) {
134 testCases := []struct {
135 desc string
136 input string
137 }{{
138 "invalid varint, final byte has continuation bit set",
139 "\xff",
140 }, {
141 "invalid varint, value overflows uint64",
142 "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00",
143 }, {
144
145
146
147 "valid varint (as uint64), but value overflows uint32",
148 "\x80\x80\x80\x80\x10",
149 }}
150
151 for _, tc := range testCases {
152 input := []byte(tc.input)
153 if _, err := DecodedLen(input); err != ErrCorrupt {
154 t.Errorf("%s: DecodedLen: got %v, want ErrCorrupt", tc.desc, err)
155 }
156 if _, err := Decode(nil, input); err != ErrCorrupt {
157 t.Errorf("%s: Decode: got %v, want ErrCorrupt", tc.desc, err)
158 }
159 }
160 }
161
162 func TestDecode(t *testing.T) {
163 lit40Bytes := make([]byte, 40)
164 for i := range lit40Bytes {
165 lit40Bytes[i] = byte(i)
166 }
167 lit40 := string(lit40Bytes)
168
169 testCases := []struct {
170 desc string
171 input string
172 want string
173 wantErr error
174 }{{
175 `decodedLen=0; valid input`,
176 "\x00",
177 "",
178 nil,
179 }, {
180 `decodedLen=3; tagLiteral, 0-byte length; length=3; valid input`,
181 "\x03" + "\x08\xff\xff\xff",
182 "\xff\xff\xff",
183 nil,
184 }, {
185 `decodedLen=2; tagLiteral, 0-byte length; length=3; not enough dst bytes`,
186 "\x02" + "\x08\xff\xff\xff",
187 "",
188 ErrCorrupt,
189 }, {
190 `decodedLen=3; tagLiteral, 0-byte length; length=3; not enough src bytes`,
191 "\x03" + "\x08\xff\xff",
192 "",
193 ErrCorrupt,
194 }, {
195 `decodedLen=40; tagLiteral, 0-byte length; length=40; valid input`,
196 "\x28" + "\x9c" + lit40,
197 lit40,
198 nil,
199 }, {
200 `decodedLen=1; tagLiteral, 1-byte length; not enough length bytes`,
201 "\x01" + "\xf0",
202 "",
203 ErrCorrupt,
204 }, {
205 `decodedLen=3; tagLiteral, 1-byte length; length=3; valid input`,
206 "\x03" + "\xf0\x02\xff\xff\xff",
207 "\xff\xff\xff",
208 nil,
209 }, {
210 `decodedLen=1; tagLiteral, 2-byte length; not enough length bytes`,
211 "\x01" + "\xf4\x00",
212 "",
213 ErrCorrupt,
214 }, {
215 `decodedLen=3; tagLiteral, 2-byte length; length=3; valid input`,
216 "\x03" + "\xf4\x02\x00\xff\xff\xff",
217 "\xff\xff\xff",
218 nil,
219 }, {
220 `decodedLen=1; tagLiteral, 3-byte length; not enough length bytes`,
221 "\x01" + "\xf8\x00\x00",
222 "",
223 ErrCorrupt,
224 }, {
225 `decodedLen=3; tagLiteral, 3-byte length; length=3; valid input`,
226 "\x03" + "\xf8\x02\x00\x00\xff\xff\xff",
227 "\xff\xff\xff",
228 nil,
229 }, {
230 `decodedLen=1; tagLiteral, 4-byte length; not enough length bytes`,
231 "\x01" + "\xfc\x00\x00\x00",
232 "",
233 ErrCorrupt,
234 }, {
235 `decodedLen=1; tagLiteral, 4-byte length; length=3; not enough dst bytes`,
236 "\x01" + "\xfc\x02\x00\x00\x00\xff\xff\xff",
237 "",
238 ErrCorrupt,
239 }, {
240 `decodedLen=4; tagLiteral, 4-byte length; length=3; not enough src bytes`,
241 "\x04" + "\xfc\x02\x00\x00\x00\xff",
242 "",
243 ErrCorrupt,
244 }, {
245 `decodedLen=3; tagLiteral, 4-byte length; length=3; valid input`,
246 "\x03" + "\xfc\x02\x00\x00\x00\xff\xff\xff",
247 "\xff\xff\xff",
248 nil,
249 }, {
250 `decodedLen=4; tagCopy1, 1 extra length|offset byte; not enough extra bytes`,
251 "\x04" + "\x01",
252 "",
253 ErrCorrupt,
254 }, {
255 `decodedLen=4; tagCopy2, 2 extra length|offset bytes; not enough extra bytes`,
256 "\x04" + "\x02\x00",
257 "",
258 ErrCorrupt,
259 }, {
260 `decodedLen=4; tagCopy4, 4 extra length|offset bytes; not enough extra bytes`,
261 "\x04" + "\x03\x00\x00\x00",
262 "",
263 ErrCorrupt,
264 }, {
265 `decodedLen=4; tagLiteral (4 bytes "abcd"); valid input`,
266 "\x04" + "\x0cabcd",
267 "abcd",
268 nil,
269 }, {
270 `decodedLen=13; tagLiteral (4 bytes "abcd"); tagCopy1; length=9 offset=4; valid input`,
271 "\x0d" + "\x0cabcd" + "\x15\x04",
272 "abcdabcdabcda",
273 nil,
274 }, {
275 `decodedLen=8; tagLiteral (4 bytes "abcd"); tagCopy1; length=4 offset=4; valid input`,
276 "\x08" + "\x0cabcd" + "\x01\x04",
277 "abcdabcd",
278 nil,
279 }, {
280 `decodedLen=8; tagLiteral (4 bytes "abcd"); tagCopy1; length=4 offset=2; valid input`,
281 "\x08" + "\x0cabcd" + "\x01\x02",
282 "abcdcdcd",
283 nil,
284 }, {
285 `decodedLen=8; tagLiteral (4 bytes "abcd"); tagCopy1; length=4 offset=1; valid input`,
286 "\x08" + "\x0cabcd" + "\x01\x01",
287 "abcddddd",
288 nil,
289 }, {
290 `decodedLen=8; tagLiteral (4 bytes "abcd"); tagCopy1; length=4 offset=0; zero offset`,
291 "\x08" + "\x0cabcd" + "\x01\x00",
292 "",
293 ErrCorrupt,
294 }, {
295 `decodedLen=9; tagLiteral (4 bytes "abcd"); tagCopy1; length=4 offset=4; inconsistent dLen`,
296 "\x09" + "\x0cabcd" + "\x01\x04",
297 "",
298 ErrCorrupt,
299 }, {
300 `decodedLen=8; tagLiteral (4 bytes "abcd"); tagCopy1; length=4 offset=5; offset too large`,
301 "\x08" + "\x0cabcd" + "\x01\x05",
302 "",
303 ErrCorrupt,
304 }, {
305 `decodedLen=7; tagLiteral (4 bytes "abcd"); tagCopy1; length=4 offset=4; length too large`,
306 "\x07" + "\x0cabcd" + "\x01\x04",
307 "",
308 ErrCorrupt,
309 }, {
310 `decodedLen=6; tagLiteral (4 bytes "abcd"); tagCopy2; length=2 offset=3; valid input`,
311 "\x06" + "\x0cabcd" + "\x06\x03\x00",
312 "abcdbc",
313 nil,
314 }, {
315 `decodedLen=6; tagLiteral (4 bytes "abcd"); tagCopy4; length=2 offset=3; valid input`,
316 "\x06" + "\x0cabcd" + "\x07\x03\x00\x00\x00",
317 "abcdbc",
318 nil,
319 }, {
320 `decodedLen=0; tagCopy4, 4 extra length|offset bytes; with msb set (0x93); discovered by go-fuzz`,
321 "\x00\xfc000\x93",
322 "",
323 ErrCorrupt,
324 }}
325
326 const (
327
328
329
330
331
332
333
334
335 notPresentBase = 0xa0
336 notPresentLen = 37
337 )
338
339 var dBuf [100]byte
340 loop:
341 for i, tc := range testCases {
342 input := []byte(tc.input)
343 for _, x := range input {
344 if notPresentBase <= x && x < notPresentBase+notPresentLen {
345 t.Errorf("#%d (%s): input shouldn't contain %#02x\ninput: % x", i, tc.desc, x, input)
346 continue loop
347 }
348 }
349
350 dLen, n := binary.Uvarint(input)
351 if n <= 0 {
352 t.Errorf("#%d (%s): invalid varint-encoded dLen", i, tc.desc)
353 continue
354 }
355 if dLen > uint64(len(dBuf)) {
356 t.Errorf("#%d (%s): dLen %d is too large", i, tc.desc, dLen)
357 continue
358 }
359
360 for j := range dBuf {
361 dBuf[j] = byte(notPresentBase + j%notPresentLen)
362 }
363 g, gotErr := Decode(dBuf[:], input)
364 if got := string(g); got != tc.want || gotErr != tc.wantErr {
365 t.Errorf("#%d (%s):\ngot %q, %v\nwant %q, %v",
366 i, tc.desc, got, gotErr, tc.want, tc.wantErr)
367 continue
368 }
369 for j, x := range dBuf {
370 if uint64(j) < dLen {
371 continue
372 }
373 if w := byte(notPresentBase + j%notPresentLen); x != w {
374 t.Errorf("#%d (%s): Decode overrun: dBuf[%d] was modified: got %#02x, want %#02x\ndBuf: % x",
375 i, tc.desc, j, x, w, dBuf)
376 continue loop
377 }
378 }
379 }
380 }
381
382 func TestDecodeCopy4(t *testing.T) {
383 dots := strings.Repeat(".", 65536)
384
385 input := strings.Join([]string{
386 "\x89\x80\x04",
387 "\x0cpqrs",
388 "\xf4\xff\xff" + dots,
389 "\x13\x04\x00\x01\x00",
390 }, "")
391
392 gotBytes, err := Decode(nil, []byte(input))
393 if err != nil {
394 t.Fatal(err)
395 }
396 got := string(gotBytes)
397 want := "pqrs" + dots + "pqrs."
398 if len(got) != len(want) {
399 t.Fatalf("got %d bytes, want %d", len(got), len(want))
400 }
401 if got != want {
402 for i := 0; i < len(got); i++ {
403 if g, w := got[i], want[i]; g != w {
404 t.Fatalf("byte #%d: got %#02x, want %#02x", i, g, w)
405 }
406 }
407 }
408 }
409
410
411
412 func TestDecodeLengthOffset(t *testing.T) {
413 const (
414 prefix = "abcdefghijklmnopqr"
415 suffix = "ABCDEFGHIJKLMNOPQR"
416
417
418
419
420
421
422
423
424
425 notPresentBase = 0xa0
426 notPresentLen = 37
427 )
428 var gotBuf, wantBuf, inputBuf [128]byte
429 for length := 1; length <= 18; length++ {
430 for offset := 1; offset <= 18; offset++ {
431 loop:
432 for suffixLen := 0; suffixLen <= 18; suffixLen++ {
433 totalLen := len(prefix) + length + suffixLen
434
435 inputLen := binary.PutUvarint(inputBuf[:], uint64(totalLen))
436 inputBuf[inputLen] = tagLiteral + 4*byte(len(prefix)-1)
437 inputLen++
438 inputLen += copy(inputBuf[inputLen:], prefix)
439 inputBuf[inputLen+0] = tagCopy2 + 4*byte(length-1)
440 inputBuf[inputLen+1] = byte(offset)
441 inputBuf[inputLen+2] = 0x00
442 inputLen += 3
443 if suffixLen > 0 {
444 inputBuf[inputLen] = tagLiteral + 4*byte(suffixLen-1)
445 inputLen++
446 inputLen += copy(inputBuf[inputLen:], suffix[:suffixLen])
447 }
448 input := inputBuf[:inputLen]
449
450 for i := range gotBuf {
451 gotBuf[i] = byte(notPresentBase + i%notPresentLen)
452 }
453 got, err := Decode(gotBuf[:], input)
454 if err != nil {
455 t.Errorf("length=%d, offset=%d; suffixLen=%d: %v", length, offset, suffixLen, err)
456 continue
457 }
458
459 wantLen := 0
460 wantLen += copy(wantBuf[wantLen:], prefix)
461 for i := 0; i < length; i++ {
462 wantBuf[wantLen] = wantBuf[wantLen-offset]
463 wantLen++
464 }
465 wantLen += copy(wantBuf[wantLen:], suffix[:suffixLen])
466 want := wantBuf[:wantLen]
467
468 for _, x := range input {
469 if notPresentBase <= x && x < notPresentBase+notPresentLen {
470 t.Errorf("length=%d, offset=%d; suffixLen=%d: input shouldn't contain %#02x\ninput: % x",
471 length, offset, suffixLen, x, input)
472 continue loop
473 }
474 }
475 for i, x := range gotBuf {
476 if i < totalLen {
477 continue
478 }
479 if w := byte(notPresentBase + i%notPresentLen); x != w {
480 t.Errorf("length=%d, offset=%d; suffixLen=%d; totalLen=%d: "+
481 "Decode overrun: gotBuf[%d] was modified: got %#02x, want %#02x\ngotBuf: % x",
482 length, offset, suffixLen, totalLen, i, x, w, gotBuf)
483 continue loop
484 }
485 }
486 for _, x := range want {
487 if notPresentBase <= x && x < notPresentBase+notPresentLen {
488 t.Errorf("length=%d, offset=%d; suffixLen=%d: want shouldn't contain %#02x\nwant: % x",
489 length, offset, suffixLen, x, want)
490 continue loop
491 }
492 }
493
494 if !bytes.Equal(got, want) {
495 t.Errorf("length=%d, offset=%d; suffixLen=%d:\ninput % x\ngot % x\nwant % x",
496 length, offset, suffixLen, input, got, want)
497 continue
498 }
499 }
500 }
501 }
502 }
503
504 const (
505 goldenText = "Isaac.Newton-Opticks.txt"
506 goldenCompressed = goldenText + ".rawsnappy"
507 )
508
509 func TestDecodeGoldenInput(t *testing.T) {
510 tDir := filepath.FromSlash(*testdataDir)
511 src, err := os.ReadFile(filepath.Join(tDir, goldenCompressed))
512 if err != nil {
513 t.Fatalf("ReadFile: %v", err)
514 }
515 got, err := Decode(nil, src)
516 if err != nil {
517 t.Fatalf("Decode: %v", err)
518 }
519 want, err := os.ReadFile(filepath.Join(tDir, goldenText))
520 if err != nil {
521 t.Fatalf("ReadFile: %v", err)
522 }
523 if err := cmp(got, want); err != nil {
524 t.Fatal(err)
525 }
526 }
527
528
529
530 func TestSlowForwardCopyOverrun(t *testing.T) {
531 const base = 100
532
533 for length := 1; length < 18; length++ {
534 for offset := 1; offset < 18; offset++ {
535 highWaterMark := base
536 d := base
537 l := length
538 o := offset
539
540
541 for o < 8 {
542 if end := d + 8; highWaterMark < end {
543 highWaterMark = end
544 }
545 l -= o
546 d += o
547 o += o
548 }
549
550
551 a := d
552 d += l
553
554
555 for l > 0 {
556 if end := a + 8; highWaterMark < end {
557 highWaterMark = end
558 }
559 a += 8
560 l -= 8
561 }
562
563 dWant := base + length
564 overrun := highWaterMark - dWant
565 if d != dWant || overrun < 0 || 10 < overrun {
566 t.Errorf("length=%d, offset=%d: d and overrun: got (%d, %d), want (%d, something in [0, 10])",
567 length, offset, d, overrun, dWant)
568 }
569 }
570 }
571 }
572
573
574
575
576 func TestEncodeNoiseThenRepeats(t *testing.T) {
577 for _, origLen := range []int{256 * 1024, 2048 * 1024} {
578 src := make([]byte, origLen)
579 rng := rand.New(rand.NewSource(1))
580 firstHalf, secondHalf := src[:origLen/2], src[origLen/2:]
581 for i := range firstHalf {
582 firstHalf[i] = uint8(rng.Intn(256))
583 }
584 for i := range secondHalf {
585 secondHalf[i] = uint8(i >> 8)
586 }
587 dst := Encode(nil, src)
588 t.Log(origLen, "->", len(dst))
589 if got, want := len(dst), origLen*3/4; got >= want {
590 t.Errorf("origLen=%d: got %d encoded bytes, want less than %d", origLen, got, want)
591 }
592 }
593 }
594
595 func TestFramingFormat(t *testing.T) {
596
597
598
599 src := make([]byte, 1e6)
600 rng := rand.New(rand.NewSource(1))
601 for i := 0; i < 10; i++ {
602 if i%2 == 0 {
603 for j := 0; j < 1e5; j++ {
604 src[1e5*i+j] = uint8(rng.Intn(256))
605 }
606 } else {
607 for j := 0; j < 1e5; j++ {
608 src[1e5*i+j] = uint8(i)
609 }
610 }
611 }
612
613 buf := new(bytes.Buffer)
614 if _, err := NewWriter(buf).Write(src); err != nil {
615 t.Fatalf("Write: encoding: %v", err)
616 }
617 dst, err := io.ReadAll(NewReader(buf))
618 if err != nil {
619 t.Fatalf("ReadAll: decoding: %v", err)
620 }
621 if err := cmp(dst, src); err != nil {
622 t.Fatal(err)
623 }
624 }
625
626 func TestWriterGoldenOutput(t *testing.T) {
627 buf := new(bytes.Buffer)
628 w := NewBufferedWriter(buf)
629 defer w.Close()
630 w.Write([]byte("abcd"))
631 w.Flush()
632 w.Write(bytes.Repeat([]byte{'A'}, 150))
633 w.Flush()
634
635
636
637
638
639
640 w.Write(bytes.Repeat([]byte{'B'}, 68))
641 w.Write([]byte("efC"))
642 w.Write(bytes.Repeat([]byte{'C'}, 20))
643 w.Write(bytes.Repeat([]byte{'B'}, 20))
644 w.Write([]byte("g"))
645 w.Flush()
646
647 got := buf.String()
648 want := strings.Join([]string{
649 magicChunk,
650 "\x01\x08\x00\x00",
651 "\x68\x10\xe6\xb6",
652 "\x61\x62\x63\x64",
653 "\x00\x11\x00\x00",
654 "\x5f\xeb\xf2\x10",
655 "\x96\x01",
656 "\x00\x41",
657 "\xee\x01\x00",
658 "\xee\x01\x00",
659 "\x72\x01\x00",
660 "\x00\x18\x00\x00",
661 "\x30\x85\x69\xeb",
662 "\x70",
663 "\x00\x42",
664 "\xee\x01\x00",
665 "\x0d\x01",
666 "\x08\x65\x66\x43",
667 "\x4e\x01\x00",
668 "\x4e\x3a\x00",
669 "\x00\x67",
670 }, "")
671 if got != want {
672 dec := NewReader(bytes.NewBuffer(buf.Bytes()))
673 var buf bytes.Buffer
674 io.Copy(&buf, dec)
675 t.Fatalf("\ngot: % x\nwant: % x", got, want)
676 }
677 }
678
679 func TestNewBufferedWriter(t *testing.T) {
680
681
682
683
684 inputs := [][]byte{
685 bytes.Repeat([]byte{'a'}, 40000),
686 bytes.Repeat([]byte{'b'}, 150000),
687 bytes.Repeat([]byte{'c'}, 60000),
688 bytes.Repeat([]byte{'d'}, 120000),
689 bytes.Repeat([]byte{'e'}, 30000),
690 }
691 loop:
692 for i := 0; i < 1<<uint(len(inputs)); i++ {
693 var want []byte
694 buf := new(bytes.Buffer)
695 w := NewBufferedWriter(buf)
696 for j, input := range inputs {
697 if i&(1<<uint(j)) == 0 {
698 continue
699 }
700 if _, err := w.Write(input); err != nil {
701 t.Errorf("i=%#02x: j=%d: Write: %v", i, j, err)
702 continue loop
703 }
704 want = append(want, input...)
705 }
706 if err := w.Close(); err != nil {
707 t.Errorf("i=%#02x: Close: %v", i, err)
708 continue
709 }
710 got, err := io.ReadAll(NewReader(buf))
711 if err != nil {
712 t.Errorf("i=%#02x: ReadAll: %v", i, err)
713 continue
714 }
715 if err := cmp(got, want); err != nil {
716 t.Errorf("i=%#02x: %v", i, err)
717 continue
718 }
719 }
720 }
721
722 func TestFlush(t *testing.T) {
723 buf := new(bytes.Buffer)
724 w := NewBufferedWriter(buf)
725 defer w.Close()
726 if _, err := w.Write(bytes.Repeat([]byte{'x'}, 20)); err != nil {
727 t.Fatalf("Write: %v", err)
728 }
729 if n := buf.Len(); n != 0 {
730 t.Fatalf("before Flush: %d bytes were written to the underlying io.Writer, want 0", n)
731 }
732 if err := w.Flush(); err != nil {
733 t.Fatalf("Flush: %v", err)
734 }
735 if n := buf.Len(); n == 0 {
736 t.Fatalf("after Flush: %d bytes were written to the underlying io.Writer, want non-0", n)
737 }
738 }
739
740 func TestReaderUncompressedDataOK(t *testing.T) {
741 r := NewReader(strings.NewReader(magicChunk +
742 "\x01\x08\x00\x00" +
743 "\x68\x10\xe6\xb6" +
744 "\x61\x62\x63\x64",
745 ))
746 g, err := io.ReadAll(r)
747 if err != nil {
748 t.Fatal(err)
749 }
750 if got, want := string(g), "abcd"; got != want {
751 t.Fatalf("got %q, want %q", got, want)
752 }
753 }
754
755 func TestReaderUncompressedDataNoPayload(t *testing.T) {
756 r := NewReader(strings.NewReader(magicChunk +
757 "\x01\x04\x00\x00" +
758 "",
759 ))
760 if _, err := io.ReadAll(r); err != ErrCorrupt {
761 t.Fatalf("got %v, want %v", err, ErrCorrupt)
762 }
763 }
764
765 func TestReaderUncompressedDataTooLong(t *testing.T) {
766
767
768 const n = 0x10005
769
770 r := NewReader(strings.NewReader(magicChunk +
771 "\x01\x05\x00\x01" +
772 strings.Repeat("\x00", n),
773 ))
774 if _, err := io.ReadAll(r); err != ErrCorrupt {
775 t.Fatalf("got %v, want %v", err, ErrCorrupt)
776 }
777 }
778
779 func TestReaderReset(t *testing.T) {
780 gold := bytes.Repeat([]byte("All that is gold does not glitter,\n"), 10000)
781 buf := new(bytes.Buffer)
782 if _, err := NewWriter(buf).Write(gold); err != nil {
783 t.Fatalf("Write: %v", err)
784 }
785 encoded, invalid, partial := buf.String(), "invalid", "partial"
786 r := NewReader(nil)
787 for i, s := range []string{encoded, invalid, partial, encoded, partial, invalid, encoded, encoded} {
788 if s == partial {
789 r.Reset(strings.NewReader(encoded))
790 if _, err := r.Read(make([]byte, 101)); err != nil {
791 t.Errorf("#%d: %v", i, err)
792 continue
793 }
794 continue
795 }
796 r.Reset(strings.NewReader(s))
797 got, err := io.ReadAll(r)
798 switch s {
799 case encoded:
800 if err != nil {
801 t.Errorf("#%d: %v", i, err)
802 continue
803 }
804 if err := cmp(got, gold); err != nil {
805 t.Errorf("#%d: %v", i, err)
806 continue
807 }
808 case invalid:
809 if err == nil {
810 t.Errorf("#%d: got nil error, want non-nil", i)
811 continue
812 }
813 }
814 }
815 }
816
817 func TestReaderReadByte(t *testing.T) {
818
819
820
821
822
823 inputs := [][]byte{
824 bytes.Repeat([]byte{'a'}, 40000),
825 bytes.Repeat([]byte{'b'}, 150000),
826 bytes.Repeat([]byte{'c'}, 60000),
827 bytes.Repeat([]byte{'d'}, 120000),
828 bytes.Repeat([]byte{'e'}, 30000),
829 }
830 loop:
831 for i := 0; i < 1<<uint(len(inputs)); i++ {
832 var want []int
833 buf := new(bytes.Buffer)
834 w := NewBufferedWriter(buf)
835 p := make([]byte, binary.MaxVarintLen64)
836 for j, input := range inputs {
837 if i&(1<<uint(j)) == 0 {
838 continue
839 }
840 n := binary.PutUvarint(p, uint64(len(input)))
841 if _, err := w.Write(p[:n]); err != nil {
842 t.Errorf("i=%#02x: j=%d: Write Uvarint: %v", i, j, err)
843 continue loop
844 }
845 if _, err := w.Write(input); err != nil {
846 t.Errorf("i=%#02x: j=%d: Write: %v", i, j, err)
847 continue loop
848 }
849 want = append(want, j)
850 }
851 if err := w.Close(); err != nil {
852 t.Errorf("i=%#02x: Close: %v", i, err)
853 continue
854 }
855 r := NewReader(buf)
856 for _, j := range want {
857 size, err := binary.ReadUvarint(r)
858 if err != nil {
859 t.Errorf("i=%#02x: ReadUvarint: %v", i, err)
860 continue loop
861 }
862 if wantedSize := uint64(len(inputs[j])); size != wantedSize {
863 t.Errorf("i=%#02x: expected size %d, got %d", i, wantedSize, size)
864 continue loop
865 }
866 got := make([]byte, size)
867 if _, err := io.ReadFull(r, got); err != nil {
868 t.Errorf("i=%#02x: ReadFull: %v", i, err)
869 continue loop
870 }
871 if err := cmp(got, inputs[j]); err != nil {
872 t.Errorf("i=%#02x: %v", i, err)
873 continue
874 }
875 }
876 if _, err := r.ReadByte(); err != io.EOF {
877 t.Errorf("i=%#02x: expected size EOF, got %v", i, err)
878 }
879 }
880 }
881
882 func TestWriterReset(t *testing.T) {
883 gold := bytes.Repeat([]byte("Not all those who wander are lost;\n"), 10000)
884 const n = 20
885 for _, buffered := range []bool{false, true} {
886 var w *Writer
887 if buffered {
888 w = NewBufferedWriter(nil)
889 defer w.Close()
890 } else {
891 w = NewWriter(nil)
892 }
893
894 var gots, wants [][]byte
895 failed := false
896 for i := 0; i <= n; i++ {
897 buf := new(bytes.Buffer)
898 w.Reset(buf)
899 want := gold[:len(gold)*i/n]
900 if _, err := w.Write(want); err != nil {
901 t.Errorf("#%d: Write: %v", i, err)
902 failed = true
903 continue
904 }
905 if buffered {
906 if err := w.Flush(); err != nil {
907 t.Errorf("#%d: Flush: %v", i, err)
908 failed = true
909 continue
910 }
911 }
912 got, err := io.ReadAll(NewReader(buf))
913 if err != nil {
914 t.Errorf("#%d: ReadAll: %v", i, err)
915 failed = true
916 continue
917 }
918 gots = append(gots, got)
919 wants = append(wants, want)
920 }
921 if failed {
922 continue
923 }
924 for i := range gots {
925 if err := cmp(gots[i], wants[i]); err != nil {
926 t.Errorf("#%d: %v", i, err)
927 }
928 }
929 }
930 }
931
932 func TestWriterResetWithoutFlush(t *testing.T) {
933 buf0 := new(bytes.Buffer)
934 buf1 := new(bytes.Buffer)
935 w := NewBufferedWriter(buf0)
936 if _, err := w.Write([]byte("xxx")); err != nil {
937 t.Fatalf("Write #0: %v", err)
938 }
939
940 w.Reset(buf1)
941 if _, err := w.Write([]byte("yyy")); err != nil {
942 t.Fatalf("Write #1: %v", err)
943 }
944 if err := w.Flush(); err != nil {
945 t.Fatalf("Flush: %v", err)
946 }
947 got, err := io.ReadAll(NewReader(buf1))
948 if err != nil {
949 t.Fatalf("ReadAll: %v", err)
950 }
951 if err := cmp(got, []byte("yyy")); err != nil {
952 t.Fatal(err)
953 }
954 }
955
956 type writeCounter int
957
958 func (c *writeCounter) Write(p []byte) (int, error) {
959 *c++
960 return len(p), nil
961 }
962
963
964
965
966 func TestNumUnderlyingWrites(t *testing.T) {
967 testCases := []struct {
968 input []byte
969 max int
970 }{
971 {bytes.Repeat([]byte{'x'}, 100), 2},
972 {bytes.Repeat([]byte{'y'}, 100), 2},
973 {[]byte("ABCDEFGHIJKLMNOPQRST"), 2},
974 }
975
976 var c writeCounter
977 w := NewBufferedWriter(&c)
978 defer w.Close()
979 for i, tc := range testCases {
980 c = 0
981 if _, err := w.Write(tc.input); err != nil {
982 t.Errorf("#%d: Write: %v", i, err)
983 continue
984 }
985 if err := w.Flush(); err != nil {
986 t.Errorf("#%d: Flush: %v", i, err)
987 continue
988 }
989 if int(c) > tc.max {
990 t.Errorf("#%d: got %d underlying writes, want max %d", i, c, tc.max)
991 continue
992 }
993 }
994 }
995
996 func benchDecode(b *testing.B, src []byte) {
997 encoded := Encode(nil, src)
998
999 b.SetBytes(int64(len(src)))
1000 b.ResetTimer()
1001 for i := 0; i < b.N; i++ {
1002 Decode(src, encoded)
1003 }
1004 }
1005
1006 func benchEncode(b *testing.B, src []byte) {
1007
1008 b.SetBytes(int64(len(src)))
1009 dst := make([]byte, MaxEncodedLen(len(src)))
1010 b.ResetTimer()
1011 for i := 0; i < b.N; i++ {
1012 Encode(dst, src)
1013 }
1014 }
1015
1016 func testOrBenchmark(b testing.TB) string {
1017 if _, ok := b.(*testing.B); ok {
1018 return "benchmark"
1019 }
1020 return "test"
1021 }
1022
1023 func readFile(b testing.TB, filename string) []byte {
1024 src, err := os.ReadFile(filename)
1025 if err != nil {
1026 b.Skipf("skipping %s: %v", testOrBenchmark(b), err)
1027 }
1028 if len(src) == 0 {
1029 b.Fatalf("%s has zero length", filename)
1030 }
1031 return src
1032 }
1033
1034
1035 func expand(src []byte, n int) []byte {
1036 dst := make([]byte, n)
1037 for x := dst; len(x) > 0; {
1038 i := copy(x, src)
1039 x = x[i:]
1040 }
1041 return dst
1042 }
1043
1044 func benchWords(b *testing.B, n int, decode bool) {
1045
1046
1047 data := expand(readFile(b, "/usr/share/dict/words"), n)
1048 if decode {
1049 benchDecode(b, data)
1050 } else {
1051 benchEncode(b, data)
1052 }
1053 }
1054
1055 func BenchmarkWordsDecode1e1(b *testing.B) { benchWords(b, 1e1, true) }
1056 func BenchmarkWordsDecode1e2(b *testing.B) { benchWords(b, 1e2, true) }
1057 func BenchmarkWordsDecode1e3(b *testing.B) { benchWords(b, 1e3, true) }
1058 func BenchmarkWordsDecode1e4(b *testing.B) { benchWords(b, 1e4, true) }
1059 func BenchmarkWordsDecode1e5(b *testing.B) { benchWords(b, 1e5, true) }
1060 func BenchmarkWordsDecode1e6(b *testing.B) { benchWords(b, 1e6, true) }
1061 func BenchmarkWordsEncode1e1(b *testing.B) { benchWords(b, 1e1, false) }
1062 func BenchmarkWordsEncode1e2(b *testing.B) { benchWords(b, 1e2, false) }
1063 func BenchmarkWordsEncode1e3(b *testing.B) { benchWords(b, 1e3, false) }
1064 func BenchmarkWordsEncode1e4(b *testing.B) { benchWords(b, 1e4, false) }
1065 func BenchmarkWordsEncode1e5(b *testing.B) { benchWords(b, 1e5, false) }
1066 func BenchmarkWordsEncode1e6(b *testing.B) { benchWords(b, 1e6, false) }
1067
1068 func BenchmarkRandomEncode(b *testing.B) {
1069 rng := rand.New(rand.NewSource(1))
1070 data := make([]byte, 1<<20)
1071 for i := range data {
1072 data[i] = uint8(rng.Intn(256))
1073 }
1074 benchEncode(b, data)
1075 }
1076
1077
1078
1079
1080 var testFiles = []struct {
1081 label string
1082 filename string
1083 sizeLimit int
1084 }{
1085 {"html", "html", 0},
1086 {"urls", "urls.10K", 0},
1087 {"jpg", "fireworks.jpeg", 0},
1088 {"jpg_200", "fireworks.jpeg", 200},
1089 {"pdf", "paper-100k.pdf", 0},
1090 {"html4", "html_x_4", 0},
1091 {"txt1", "alice29.txt", 0},
1092 {"txt2", "asyoulik.txt", 0},
1093 {"txt3", "lcet10.txt", 0},
1094 {"txt4", "plrabn12.txt", 0},
1095 {"pb", "geo.protodata", 0},
1096 {"gaviota", "kppkn.gtb", 0},
1097 }
1098
1099 const (
1100
1101 benchURL = "https://raw.githubusercontent.com/google/snappy/master/testdata/"
1102 )
1103
1104 func downloadBenchmarkFiles(b testing.TB, basename string) (errRet error) {
1105 bDir := filepath.FromSlash(*benchdataDir)
1106 filename := filepath.Join(bDir, basename)
1107 if stat, err := os.Stat(filename); err == nil && stat.Size() != 0 {
1108 return nil
1109 }
1110
1111 if !*download {
1112 b.Skipf("test data not found; skipping %s without the -download flag", testOrBenchmark(b))
1113 }
1114
1115
1116 if err := os.MkdirAll(bDir, 0777); err != nil && !os.IsExist(err) {
1117 return fmt.Errorf("failed to create %s: %s", bDir, err)
1118 }
1119
1120 f, err := os.Create(filename)
1121 if err != nil {
1122 return fmt.Errorf("failed to create %s: %s", filename, err)
1123 }
1124 defer f.Close()
1125 defer func() {
1126 if errRet != nil {
1127 os.Remove(filename)
1128 }
1129 }()
1130 url := benchURL + basename
1131 resp, err := http.Get(url)
1132 if err != nil {
1133 return fmt.Errorf("failed to download %s: %s", url, err)
1134 }
1135 defer resp.Body.Close()
1136 if s := resp.StatusCode; s != http.StatusOK {
1137 return fmt.Errorf("downloading %s: HTTP status code %d (%s)", url, s, http.StatusText(s))
1138 }
1139 _, err = io.Copy(f, resp.Body)
1140 if err != nil {
1141 return fmt.Errorf("failed to download %s to %s: %s", url, filename, err)
1142 }
1143 return nil
1144 }
1145
1146 func benchFile(b *testing.B, i int, decode bool) {
1147 if err := downloadBenchmarkFiles(b, testFiles[i].filename); err != nil {
1148 b.Fatalf("failed to download testdata: %s", err)
1149 }
1150 bDir := filepath.FromSlash(*benchdataDir)
1151 data := readFile(b, filepath.Join(bDir, testFiles[i].filename))
1152 if n := testFiles[i].sizeLimit; 0 < n && n < len(data) {
1153 data = data[:n]
1154 }
1155 if decode {
1156 benchDecode(b, data)
1157 } else {
1158 benchEncode(b, data)
1159 }
1160 }
1161
1162
1163 func Benchmark_UFlat0(b *testing.B) { benchFile(b, 0, true) }
1164 func Benchmark_UFlat1(b *testing.B) { benchFile(b, 1, true) }
1165 func Benchmark_UFlat2(b *testing.B) { benchFile(b, 2, true) }
1166 func Benchmark_UFlat3(b *testing.B) { benchFile(b, 3, true) }
1167 func Benchmark_UFlat4(b *testing.B) { benchFile(b, 4, true) }
1168 func Benchmark_UFlat5(b *testing.B) { benchFile(b, 5, true) }
1169 func Benchmark_UFlat6(b *testing.B) { benchFile(b, 6, true) }
1170 func Benchmark_UFlat7(b *testing.B) { benchFile(b, 7, true) }
1171 func Benchmark_UFlat8(b *testing.B) { benchFile(b, 8, true) }
1172 func Benchmark_UFlat9(b *testing.B) { benchFile(b, 9, true) }
1173 func Benchmark_UFlat10(b *testing.B) { benchFile(b, 10, true) }
1174 func Benchmark_UFlat11(b *testing.B) { benchFile(b, 11, true) }
1175 func Benchmark_ZFlat0(b *testing.B) { benchFile(b, 0, false) }
1176 func Benchmark_ZFlat1(b *testing.B) { benchFile(b, 1, false) }
1177 func Benchmark_ZFlat2(b *testing.B) { benchFile(b, 2, false) }
1178 func Benchmark_ZFlat3(b *testing.B) { benchFile(b, 3, false) }
1179 func Benchmark_ZFlat4(b *testing.B) { benchFile(b, 4, false) }
1180 func Benchmark_ZFlat5(b *testing.B) { benchFile(b, 5, false) }
1181 func Benchmark_ZFlat6(b *testing.B) { benchFile(b, 6, false) }
1182 func Benchmark_ZFlat7(b *testing.B) { benchFile(b, 7, false) }
1183 func Benchmark_ZFlat8(b *testing.B) { benchFile(b, 8, false) }
1184 func Benchmark_ZFlat9(b *testing.B) { benchFile(b, 9, false) }
1185 func Benchmark_ZFlat10(b *testing.B) { benchFile(b, 10, false) }
1186 func Benchmark_ZFlat11(b *testing.B) { benchFile(b, 11, false) }
1187
View as plain text