1// Copyright 2015, Joe Tsai. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE.md file.
4
5package meta
6
7import (
8	"bytes"
9	"io"
10	"io/ioutil"
11	"math/rand"
12	"strings"
13	"testing"
14
15	"github.com/dsnet/compress/internal/errors"
16	"github.com/dsnet/compress/internal/testutil"
17)
18
19// TestReader tests that the reader is able to properly decode a set of valid
20// input strings or properly detect corruption in a set of invalid input
21// strings. A third-party decoder should verify that it has the same behavior
22// when processing these input vectors.
23func TestReader(t *testing.T) {
24	db := testutil.MustDecodeBitGen
25	dh := testutil.MustDecodeHex
26
27	errFuncs := map[string]func(error) bool{
28		"IsEOF":           func(err error) bool { return err == io.EOF },
29		"IsUnexpectedEOF": func(err error) bool { return err == io.ErrUnexpectedEOF },
30		"IsCorrupted":     errors.IsCorrupted,
31	}
32	vectors := []struct {
33		desc   string    // Description of the test
34		input  []byte    // Test input string
35		output []byte    // Expected output string
36		final  FinalMode // Expected FinalMode value
37		errf   string    // Name of error checking callback
38	}{{
39		desc:   "empty string",
40		input:  dh(""),
41		output: dh(""),
42		errf:   "IsEOF",
43	}, {
44		desc: "bad empty meta block (FinalNil, first symbol not symZero)",
45		input: db(`<<<
46			< (0 10) (00100 00000 1010) (011 000 011 001 000 (000 000)*4 010)
47			> (111 <D7:127) (111 <D7:100) 10 (110 <D2:3) 10
48			< 0*4 0 1*3
49		`),
50		output: dh(""),
51		errf:   "IsCorrupted",
52	}, {
53		desc: "empty meta block (FinalNil)",
54		input: db(`<<<
55			< (0 10) (00011 00000 1010) (011 000 011 001 000 (000 000)*4 010) 0
56			> (111 <D7:127) (111 <D7:99) 10 (110 <D2:3) 10
57			< 0*3 0 1*3
58		`),
59		output: dh(""),
60		final:  FinalNil,
61	}, {
62		desc: "empty meta block (FinalMeta)",
63		input: db(`<<<
64			< (0 10) (00011 00000 1010) (011 000 011 001 000 (000 000)*4 010) 0
65			> 10 (111 <D7:127) (111 <D7:99) 10 (110 <D2:3)
66			< 0*3 0 1*3
67		`),
68		output: dh(""),
69		final:  FinalMeta,
70	}, {
71		desc: "bad empty meta block, contains the magic value mid way",
72		input: db(`<<<
73			< (1 10) (00000 00000 1100) (011 000 011 001 000 (000 000)*5 010) 0
74			> 10 0*14 10 0*13 (110 <D2:0) 0 (110 <D2:1) 0*4 (111 <D7:127)
75			  (111 <D7:59) 0*5 10*2
76			< 0*0 0 1*2
77		`),
78		output: dh(""),
79		errf:   "IsCorrupted",
80	}, {
81		desc: "meta block containing the string 'a'",
82		input: db(`<<<
83			< (0 10) (00010 00000 1000) (011 000 011 001 000 (000 000)*3 010) 0
84			> 10 0 10 0*4 10 0*4 10*2 (111 <D7:127) (111 <D7:82) 10 (110 <D2:3)
85			  (110 <D2:1)
86			< 0*2 0 1*4
87		`),
88		output: []byte("a"),
89		final:  FinalMeta,
90	}, {
91		desc: "meta block containing the string 'ab'",
92		input: db(`<<<
93			< (0 10) (00010 00000 1000) (011 000 011 001 000 (000 000)*3 010) 0
94			> 10 0*2 10 0*3 10 0*4 10*2 0*2 10 0*3 10*2 (111 <D7:127)
95			  (111 <D7:77) 10 (110 <D2:3) 10
96			< 0*2 0 1*4
97		`),
98		output: []byte("ab"),
99		final:  FinalMeta,
100	}, {
101		desc: "meta block containing the string 'abc'",
102		input: db(`<<<
103			< (0 10) (00010 00000 0110) (011 000 011 001 000 (000 000)*2 010) 0
104			> 10 0 10*2 0*3 10 0*4 10*2 0*2 10 0*3 10*2 0 10*2 0*3 10*2
105			  (111 <D7:127) (111 <D7:58) 10 (110 <D2:3) (110 <D2:3) (110 <D2:3)
106			< 0*2 0 1*5
107		`),
108		output: []byte("abc"),
109		final:  FinalMeta,
110	}, {
111		desc: "meta block containing the string 'Hello, world!'",
112		input: db(`<<<
113			< (0 10) (00010 00000 0100) (011 000 011 001 000 (000 000)*1 010) 0
114			> 10 0 10 0 10*2 0*4 10 0*2 10 0 10 0 10 0*2 10*2 0*3 10*2 0 10*2
115			  0*3 10*2 0 10*2 0 10 (110 <D2:0) 0 10*2 0*3 10*2 0 10 0
116			  (110 <D2:3) 10 0*2 10*3 0 10*3 0 10 (110 <D2:0) 0 10*2 0*2 10 0*2
117			  10*3 0*3 10*2 0 10*2 0*3 10 0*2 10*2 0 10 0*4 10 (111 <D7:125)
118			  10 (110 <D2:3) (110 <D2:1)
119			< 0*2 0 1*6
120		`),
121		output: []byte("Hello, world!"),
122		final:  FinalMeta,
123	}, {
124		desc: "meta block containing the hex-string '00'*4",
125		input: db(`<<<
126			< (0 10) (00110 00000 1010) (011 000 011 001 000 (000 000)*4 010) 0
127			> 10 0*3 10 (111 <D7:127) (111 <D7:96) 10 (110 <D2:2)
128			< 0*6 0 1*3
129		`),
130		output: dh("00000000"),
131		final:  FinalMeta,
132	}, {
133		desc: "meta block containing the hex-string '00'*8",
134		input: db(`<<<
135			< (0 10) (00101 00000 1010) (011 000 011 001 000 (000 000)*4 010) 0
136			> 10 0*4 10 (111 <D7:127) (111 <D7:95) 10 (110 <D2:2)
137			< 0*5 0 1*3
138		`),
139		output: dh("0000000000000000"),
140		final:  FinalMeta,
141	}, {
142		desc: "meta block containing the hex-string '00'*16",
143		input: db(`<<<
144			< (0 10) (00100 00000 1010) (011 000 011 001 000 (000 000)*4 010) 0
145			> 10 0*5 10 (111 <D7:127) (111 <D7:94) 10 (110 <D2:2)
146			< 0*4 0 1*3
147		`),
148		output: dh("00000000000000000000000000000000"),
149		final:  FinalMeta,
150	}, {
151		desc: "meta block containing the hex-string 'ff'*4",
152		input: db(`<<<
153			< (0 10) (00101 00000 1010) (011 000 011 001 000 (000 000)*4 010) 0
154			> 10*2 0*2 10 (111 <D7:127) (111 <D7:97) 10 (110 <D2:1)
155			< 0*5 0 1*3
156		`),
157		output: dh("ffffffff"),
158		final:  FinalMeta,
159	}, {
160		desc: "meta block containing the hex-string 'ff'*8",
161		input: db(`<<<
162			< (0 10) (00100 00000 1010) (011 000 011 001 000 (000 000)*4 010) 0
163			> 10*2 0*3 10 (111 <D7:127) (111 <D7:96) 10 (110 <D2:1)
164			< 0*4 0 1*3
165		`),
166		output: dh("ffffffffffffffff"),
167		final:  FinalMeta,
168	}, {
169		desc: "meta block containing the hex-string 'ff'*16",
170		input: db(`<<<
171			< (0 10) (00011 00000 1010) (011 000 011 001 000 (000 000)*4 010) 0
172			> 10*2 0*4 10 (111 <D7:127) (111 <D7:95) 10 (110 <D2:1)
173			< 0*3 0 1*3
174		`),
175		output: dh("ffffffffffffffffffffffffffffffff"),
176		final:  FinalMeta,
177	}, {
178		desc: "meta block containing the random hex-string '911fe47084a4668b'",
179		input: db(`<<<
180			< (0 10) (00011 00000 0100) (011 000 011 001 000 (000 000) 010) 0
181			> 10 0*4 10 0 10 0*3 10 0*2 10 (110 <D2:2) 0*5 10 0*2 10*3 0*4 10*3
182			  0*3 10 0*4 10 0*2 10 0*2 10 0 10 0 10*2 0*2 10*2 0 10*2 0 10 0*3
183			  10 (111 <D7:127) (111 <D7:2) 10 (110 <D2:3)*5 (110 <D2:0)
184			< 0*3 0 1*6
185		`),
186		output: dh("911fe47084a4668b"),
187		final:  FinalMeta,
188	}, {
189		desc: "meta block containing the random hex-string 'de9fa94cb16f40fc'",
190		input: db(`<<<
191			< (0 10) (00100 00000 0100) (011 000 011 001 000 (000 000) 010) 0
192			> 10*2 0*3 10 0 10 0*4 10 0 (110 <D2:3) 10*2 0*2 10*2 0 10 0 10 0
193			  10*2 0*2 10*2 0 10 0 10*2 10 0*2 10 0*5 10 0*2 10 (110 <D2:3) 0
194			  10*3 (111 <D7:127) (111 <D7:9) 10 (110 <D2:3)*5 10*2
195			< 0*4 0 1*6
196		`),
197		output: dh("de9fa94cb16f40fc"),
198		final:  FinalMeta,
199	}, {
200		desc: "empty meta block with a huffLen of 1",
201		input: db(`<<<
202			< (0 10) (00110 00000 1110) (011 000 011 001 000 (000 000)*6 010) 0
203			> 10 (111 <D7:127) (111 <D7:105) 10
204			< 0*6 0 1*1
205		`),
206		output: dh(""),
207		final:  FinalMeta,
208	}, {
209		desc: "empty meta block with a huffLen of 2",
210		input: db(`<<<
211			< (0 10) (00111 00000 1100) (011 000 011 001 000 (000 000)*5 010) 0
212			> 10 (111 <D7:127) 10*2 (111 <D7:103) 10
213			< 0*7 0 1*2
214		`),
215		output: dh(""),
216		final:  FinalMeta,
217	}, {
218		desc: "empty meta block with a huffLen of 3",
219		input: db(`<<<
220			< (0 10) (00100 00000 1010) (011 000 011 001 000 (000 000)*4 010) 0
221			> 10 (111 <D7:127) 10*6 (111 <D7:99) 10
222			< 0*4 0 1*3
223		`),
224		output: dh(""),
225		final:  FinalMeta,
226	}, {
227		desc: "empty meta block with a huffLen of 4",
228		input: db(`<<<
229			< (0 10) (00001 00000 1000) (011 000 011 001 000 (000 000)*3 010) 0
230			> 10 (111 <D7:127) 10*14 (111 <D7:91) 10
231			< 0*1 0 1*4
232		`),
233		output: dh(""),
234		final:  FinalMeta,
235	}, {
236		desc: "empty meta block with a huffLen of 5",
237		input: db(`<<<
238			< (0 10) (00110 00000 0110) (011 000 011 001 000 (000 000)*2 010) 0
239			> 10 (111 <D7:127) 10*30 (111 <D7:75) 10
240			< 0*6 0 1*5
241		`),
242		output: dh(""),
243		final:  FinalMeta,
244	}, {
245		desc: "empty meta block with a huffLen of 6",
246		input: db(`<<<
247			< (0 10) (00011 00000 0100) (011 000 011 001 000 (000 000)*1 010) 0
248			> 10 (111 <D7:127) 10*62 (111 <D7:43) 10
249			< 0*3 0 1*6
250		`),
251		output: dh(""),
252		final:  FinalMeta,
253	}, {
254		desc: "empty meta block with a huffLen of 7",
255		input: db(`<<<
256			< (0 10) (00010 00000 0010) (011 000 011 001 000 (000 000)*0 010) 0
257			> 10 (111 <D7:117) 10*127
258			< 0*2 0 1*7
259		`),
260		output: dh(""),
261		final:  FinalMeta,
262	}, {
263		desc: "shortest meta block",
264		input: db(`<<<
265			< (0 10) (00011 00000 1010) (011 000 011 001 000 (000 000)*4 010) 0
266			> (111 <D7:127) (111 <D7:99) 10 (110 <D2:3) 10
267			< 0*3 0 1*3
268		`),
269		output: dh(""),
270	}, {
271		desc: "longest meta block",
272		input: db(`<<<
273			< (0 10) (00000 00000 0010) (011 000 011 001 000 (000 000)*0 010) 0
274			> 0*2 (110 <D2:0)*42 10*128
275			< 0*0 0 1*7
276		`),
277		output: dh(""),
278	}, {
279		desc: "longest decoded meta block",
280		input: db(`<<<
281			< (0 10) (00100 00000 1010) (011 000 011 001 000 (000 000)*4 010) 0
282			> 10*7 (111 <D7:113)*2 10
283			< 0*4 0 1*3
284		`),
285		output: dh("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
286		final:  FinalMeta,
287	}, {
288		desc: "meta block truncated short",
289		input: db(`<<<
290			< (0 10) (00011 00000 0100) (011 000 011 001 000 (000 000)*1 010) 0
291			> 10 0*4 10 0 10 0*3 10 0*2 10 (110 <D2:2) 0*5 10 0*2 10*3 0*4 10*3
292			  0*3 10 0*4 10 0*2 10 0*2 10 0 10 0 10*2 0*2 10*2 0 10*2 0 10 0*3
293			  10 (111 <D7:127) (111 <D7:2) 10 (110 <D2:3)*5 (110 <D2:0)
294			< 0*3 0 1*6
295		`)[:3],
296		errf: "IsUnexpectedEOF",
297	}, {
298		desc: "meta block truncated medium-short",
299		input: db(`<<<
300			< (0 10) (00011 00000 0100) (011 000 011 001 000 (000 000)*1 010) 0
301			> 10 0*4 10 0 10 0*3 10 0*2 10 (110 <D2:2) 0*5 10 0*2 10*3 0*4 10*3
302			  0*3 10 0*4 10 0*2 10 0*2 10 0 10 0 10*2 0*2 10*2 0 10*2 0 10 0*3
303			  10 (111 <D7:127) (111 <D7:2) 10 (110 <D2:3)*5 (110 <D2:0)
304			< 0*3 0 1*6
305		`)[:4],
306		errf: "IsUnexpectedEOF",
307	}, {
308		desc: "meta block truncated medium-long",
309		input: db(`<<<
310			< (0 10) (00011 00000 0100) (011 000 011 001 000 (000 000)*1 010) 0
311			> 10 0*4 10 0 10 0*3 10 0*2 10 (110 <D2:2) 0*5 10 0*2 10*3 0*4 10*3
312			  0*3 10 0*4 10 0*2 10 0*2 10 0 10 0 10*2 0*2 10*2 0 10*2 0 10 0*3
313			  10 (111 <D7:127) (111 <D7:2) 10 (110 <D2:3)*5 (110 <D2:0)
314			< 0*3 0 1*6
315		`)[:13],
316		errf: "IsUnexpectedEOF",
317	}, {
318		desc: "meta block truncated long",
319		input: db(`<<<
320			< (0 10) (00011 00000 0100) (011 000 011 001 000 (000 000)*1 010) 0
321			> 10 0*4 10 0 10 0*3 10 0*2 10 (110 <D2:2) 0*5 10 0*2 10*3 0*4 10*3
322			  0*3 10 0*4 10 0*2 10 0*2 10 0 10 0 10*2 0*2 10*2 0 10*2 0 10 0*3
323			  10 (111 <D7:127) (111 <D7:2) 10 (110 <D2:3)*5 (110 <D2:0)
324			< 0*3 0 1*6
325		`)[:24],
326		errf: "IsUnexpectedEOF",
327	}, {
328		desc:  "random junk",
329		input: dh("911fe47084a4668b"),
330		errf:  "IsCorrupted",
331	}, {
332		desc: "meta block with invalid number of HCLen codes of 6",
333		input: db(`<<<
334			< (0 10) (00110 00000 0000) (011 000 011 001 000 (000 000)*0 000)
335			> 0*34 10 0 10 (111 <D7:127) (111 <D7:105)
336			< 000001 0 100
337		`),
338		errf: "IsCorrupted",
339	}, {
340		desc: "meta block with invalid HCLen code in the middle",
341		input: db(`<<<
342			< (0 10) (00110 00000 1110) (011 000 011 001 000 (000 010) (000 000)*5 010) 0
343			> 10 (111 <D7:127) (111 <D7:105) 10
344			< 000000 0 1
345		`),
346		errf: "IsCorrupted",
347	}, {
348		desc: "meta block with invalid HCLen code at the end",
349		input: db(`<<<
350			< (0 10) (00110 00000 1110) (011 000 011 001 000 (000 000)*6 110) 0
351			> 10 (111 <D7:127) (111 <D7:105) 10
352			< 000000 0 1
353		`),
354		errf: "IsCorrupted",
355	}, {
356		desc: "meta block first symbol being a last repeater",
357		input: db(`<<<
358			< (0 10) (00100 00000 1110) (011 000 011 001 000 (000 000)*6 010)
359			> (110 <D2:0) 10 (111 <D7:127) (111 <D7:104)
360			< 0000 0 1
361		`),
362		errf: "IsCorrupted",
363	}, {
364		desc: "meta block with too many symbols",
365		input: db(`<<<
366			< (0 10) (00110 00000 1110) (011 000 011 001 000 (000 000)*6 010) 0
367			> 10 (111 <D7:127) (111 <D7:106) 10
368			< 000000 0 1
369		`),
370		errf: "IsCorrupted",
371	}, {
372		desc:  "meta block with too few symbols",
373		input: dh("34c087050000000020fe7f3a40"),
374		errf:  "IsCorrupted",
375	}, {
376		desc: "meta block with first symbol not a zero",
377		input: db(`<<<
378			< (0 10) (00110 00000 1110) (011 000 011 001 000 (000 000)*6 010) 0
379			> 10 (111 <D7:127) (111 <D7:104) 10
380			< 000000 0 0
381		`),
382		errf: "IsCorrupted",
383	}, {
384		desc: "meta block with no EOB symbol",
385		input: db(`<<<
386			< (0 10) (00101 00000 1110) (011 000 011 001 000 (000 000)*6 010) 0
387			> 10 (111 <D7:127) (111 <D7:104) 10 0
388			< 00000 0 1
389		`),
390		errf: "IsCorrupted",
391	}, {
392		desc: "meta block with FinalStream set, but not FinalMeta",
393		input: db(`<<<
394			< (1 10) (00101 00000 1110) (011 000 011 001 000 (000 000)*6 010) 0
395			> 0 10 (111 <D7:127) (111 <D7:104) 10
396			< 00000 0 1
397		`),
398		errf: "IsCorrupted",
399	}, {
400		desc: "meta block with some padding bits not zero",
401		input: db(`<<<
402			< (0 10) (00110 00000 1110) (011 000 011 001 000 (000 000)*6 010) 0
403			> 10 (111 <D7:127) (111 <D7:105) 10
404			< 100000 0 1
405		`),
406		errf: "IsCorrupted",
407	}, {
408		desc: "meta block with the HDist tree not empty",
409		input: db(`<<<
410			< (0 10) (00110 00000 1110) (011 000 011 001 000 (000 000)*6 010) 0
411			> 10 (111 <D7:127) (111 <D7:105) 10
412			< 000000 1 1
413		`),
414		errf: "IsCorrupted",
415	}, {
416		desc: "meta block with invalid EOB",
417		input: db(`<<<
418			< (0 10) (00110 00000 1110) (011 000 011 001 000 (000 000)*6 010) 0
419			> 10 (111 <D7:127) (111 <D7:105) 10
420			< 000000 0 0
421		`),
422		errf: "IsCorrupted",
423	}, {
424		desc: "meta block with wrong number of padding bits",
425		input: db(`<<<
426			< (0 10) (00101 00000 1110) (011 000 011 001 000 (000 000)*6 010) 0
427			> 10 (111 <D7:127) (111 <D7:105) 10
428			< 00000 0 1
429		`),
430		errf: "IsCorrupted",
431	}}
432
433	for i, v := range vectors {
434		mr := NewReader(bytes.NewReader(v.input))
435		err := mr.decodeBlock()
436		output := mr.buf
437
438		if got, want, ok := testutil.BytesCompare(output, v.output); !ok {
439			t.Errorf("test %d (%s), mismatching data:\ngot  %s\nwant %s", i, v.desc, got, want)
440		}
441		if int(mr.InputOffset) != len(v.input) && err == nil {
442			t.Errorf("test %d (%s), mismatching offset: got %d, want %d", i, v.desc, mr.InputOffset, len(v.input))
443		}
444		if mr.final != v.final {
445			t.Errorf("test %d (%s), mismatching final mode: got %d, want %d", i, v.desc, mr.final, v.final)
446		}
447		if v.errf != "" && !errFuncs[v.errf](err) {
448			t.Errorf("test %d (%s), mismatching error:\ngot %v\nwant %s(err) == true", i, v.desc, err, v.errf)
449		} else if v.errf == "" && err != nil {
450			t.Errorf("test %d (%s), unexpected error: got %v", i, v.desc, err)
451		}
452	}
453}
454
455func TestReaderReset(t *testing.T) {
456	buf := make([]byte, 512)
457	mr := NewReader(nil)
458
459	// Test Reader for idempotent Close.
460	if err := mr.Close(); err != nil {
461		t.Errorf("unexpected error: Close() = %v", err)
462	}
463	if err := mr.Close(); err != nil {
464		t.Errorf("unexpected error: Close() = %v", err)
465	}
466	if _, err := mr.Read(buf); err != errClosed {
467		t.Errorf("unexpected error: Read() = %v, want %v", err, errClosed)
468	}
469
470	// Test Reader with corrupt data.
471	mr.Reset(strings.NewReader("corrupt"))
472	if _, err := mr.Read(buf); !errors.IsCorrupted(err) {
473		t.Errorf("unexpected error: Read() = %v, want IsCorrupted(err) == true", err)
474	}
475	if err := mr.Close(); !errors.IsCorrupted(err) {
476		t.Errorf("unexpected error: Close() = %v, want IsCorrupted(err) == true", err)
477	}
478
479	// Test Reader on multiple back-to-back streams.
480	data := testutil.MustDecodeBitGen(`<<<
481		# FinalNil, "The quick brown fox jumped o"
482		< (0 10) (00111 00000 0010) (011 000 011 001 000 010) 0
483		> (110 <D2:1) 10*3 0*2 10 0 10 0 10 0 (110 <D2:0) 10 0 10*2 0 10 0 10
484		  0*2 10*2 0 (110 <D2:2) 10 0*2 10 0*3 10*3 0 10 0 10 0 10*3 0 10 0*2
485		  10 0 10*2 0 10*2 0*3 10*2 0 10*2 0 10 0 10*2 0 (110 <D2:2) 10 0*3 10
486		  0*3 10*2 0*2 10 0*2 10*3 0 10 (110 <D2:0) 0 10*2 0 10*3 0 10*3 0*2
487		  10*3 0 10*2 0 (110 <D2:2) 10 0*3 10*2 0*2 10*2 0 10 (110 <D2:0) 0 10*2
488		  0 (110 <D2:0) 10 (110 <D2:0) 0 (110 <D2:2) 10 0*3 10 0 10 0 10*2 0 10
489		  0 10 0 10*3 0 10 0 10*2 0 10*2 0 (110 <D2:1) 10*3 0 10 0 10 0*2 10*2
490		  0*3 10 0*2 10*2 0 (110 <D2:2) 10 0*2 10 (110 <D2:0) 0 10*2 0
491		  (110 <D2:2) 10 (110 <D2:3) (110 <D2:3) (110 <D2:3) 10
492		< 0*7 0 1*7
493
494		# FinalMeta, "ver the lazy dog."
495		< (0 10) (00101 00000 0010) (011 000 011 001 000 010) 0
496		> 10 0 10 0*3 10 0 10*2 0 10*3 0 10 0 10 0*2 10*2 0*2 10 0*2 10*3 0
497		  (110 <D2:2) 10 0 (110 <D2:0) 10 0 10*3 0 (110 <D2:0) 10 0 10*2 0 10 0
498		  10 0*2 10*2 0 (110 <D2:2) 10 0 (110 <D2:0) 10*2 0 10*2 0 10 0
499		  (110 <D2:0) 10*2 0*2 10 0 10 (110 <D2:0) 0 10 0*2 10 (110 <D2:0) 0
500		  (110 <D2:2) 10 0 (110 <D2:0) 10 0*2 10*2 0 10 (110 <D2:0) 0 10*2 0
501		  10*3 0*2 10*2 0*2 10*3 0 10 (111 <D7:41) 10 (110 <D2:3) (110 <D2:3)
502		  (110 <D2:3) (110 <D2:3) (110 <D2:3) (110 <D2:3) (110 <D2:3)
503		  (110 <D2:3) (110 <D2:3) (110 <D2:3) 10*2
504		< 0*5 0 1*7
505
506		# FinalNil, "Lorem ipsum dolor sit amet, "
507		< (0 10) (00111 00000 0010) (011 000 011 001 000 010) 0
508		> (110 <D2:1) 10*3 0*2 10*2 0*2 10 0 10 (110 <D2:0) 0 10*2 0*2 10 0*2
509		  10*3 0 10 0 10 0*2 10*2 0 10 0 10*2 0 10*2 0 (110 <D2:2) 10 0*2 10 0*2
510		  10 0 10*2 0 (110 <D2:1) 10*3 0 10*2 0*2 10*3 0 10 0 10 0 10*3 0 10 0
511		  10*2 0 10*2 0 (110 <D2:2) 10 0 (110 <D2:0) 10 0*2 10*2 0 10
512		  (110 <D2:0) 0 10*2 0*3 10*2 0 10*2 0 10 (110 <D2:0) 0 10*2 0*2 10 0*2
513		  10*3 0 (110 <D2:2) 10 0*2 10*2 0*2 10*3 0 10 0*2 10 0 10*2 0*3 10 0
514		  10*3 0 (110 <D2:2) 10 0*2 10 0 (110 <D2:0) 10*2 0 10 0 10*2 0 10*2 0
515		  10 0 10 0*2 10*2 0*3 10 0 10*3 0*3 10*2 0 10 0 (110 <D2:3) 10 0
516		  (110 <D2:2) 10 (110 <D2:3) (110 <D2:3) (110 <D2:3) 10*2
517		< 0*7 0 1*7
518
519		# FinalStream, "consectetur adipiscing elit."
520		< (1 10) (00111 00000 0010) (011 000 011 001 000 010) 0
521		> 10 0*3 10 (110 <D2:1) 0*3 10*2 0 10 (110 <D2:0) 0 10*2 0*2 10*3 0 10*2
522		  0 10*2 0*2 10*3 0 10 0 10 0*2 10*2 0 10*2 0*3 10*2 0*3 10 0 10*3 0 10
523		  0 10 0*2 10*2 0*3 10 0 10*3 0 10 0 10 0 10*3 0*2 10 0*2 10*3 0
524		  (110 <D2:2) 10 0*2 10 0 (110 <D2:0) 10*2 0*3 10 0*2 10*2 0 10 0*2 10 0
525		  10*2 0 (110 <D2:1) 10*3 0 10 0*2 10 0 10*2 0 10*2 0*2 10*3 0 10*2 0*3
526		  10*2 0 10 0*2 10 0 10*2 0*2 10*3 0 10*2 0 10*3 0*2 10*2 0 (110 <D2:2)
527		  10 0*2 10 0 10 0*2 10*2 0*3 10*2 0 10*2 0 10 0*2 10 0 10*2 0*3 10 0
528		  10*3 0*2 10*3 0 10 (111 <D7:3) 10 (110 <D2:3) (110 <D2:3)
529		< 0*7 0 1*7
530
531		# FinalNil, "Do not communicate by sharing"
532		< (0 10) (00101 00000 0010) (011 000 011 001 000 010) 0
533		> 0*2 10 0 10*3 0*2 10 0*3 10 0 10 (110 <D2:0) 0 10*2 0 (110 <D2:2) 10
534		  0*3 10*3 0 10*2 0 10 (110 <D2:0) 0 10*2 0*3 10 0 10*3 0 (110 <D2:2)
535		  10 0*2 10*2 0*3 10*2 0 10 (110 <D2:0) 0 10*2 0 10 0 10*2 0 10*2 0 10 0
536		  10*2 0 10*2 0 10 0 10 0 10*3 0*2 10*3 0 10*2 0 10 0*2 10 0 10*2 0 10*2
537		  0*3 10*2 0 10 0 (110 <D2:0) 10*2 0*3 10 0 10*3 0 10 0 10 0*2 10*2 0
538		  (110 <D2:2) 10 0*3 10 0*3 10*2 0 10 0*2 10 (110 <D2:0) 0 (110 <D2:2)
539		  10 0*2 10*2 0*2 10*3 0 (110 <D2:0) 10 0 10*2 0 10 0 (110 <D2:0) 10*2
540		  0*2 10 0*2 10*3 0 10 0*2 10 0 10*2 0*2 10*3 0 10*2 0 10*3 0*2 10*2 0
541		  (110 <D2:3) 10 (110 <D2:3) (110 <D2:1)
542		< 0*5 0 1*7
543
544		# FinalNil, " memory; instead, share memor"
545		< (0 10) (00110 00000 0010) (011 000 011 001 000 010) 0
546		> 0*2 10 0 10*3 0 (110 <D2:1) 10 0*2 10 0 10*2 0 10*2 0 10 0 10 0*2 10*2
547		  0 10 0 10*2 0 10*2 0 10 (110 <D2:0) 0 10*2 0*2 10 0*2 10*3 0 10 0*2 10
548		  (110 <D2:0) 0 10*2 0 10*3 0 (110 <D2:3) 10 0*2 10 0*2 10 0 10*2 0*2
549		  10*3 0 10*2 0 10*2 0*2 10*3 0*3 10 0 10*3 0 10 0 10 0*2 10*2 0 10 0
550		  (110 <D2:0) 10*2 0*3 10 0*2 10*2 0*3 10*2 0 10 0 (110 <D2:3) 10 0*2
551		  10*2 0*2 10*3 0 (110 <D2:0) 10 0 10*2 0 10 0 (110 <D2:0) 10*2 0*2 10
552		  0*2 10*3 0 10 0 10 0*2 10*2 0 (110 <D2:2) 10 0*2 10 0 10*2 0 10*2 0 10
553		  0 10 0*2 10*2 0 10 0 10*2 0 10*2 0 10 (110 <D2:0) 0 10*2 0*2 10 0*2
554		  10*3 0 (110 <D2:2) 10 (110 <D2:3) (110 <D2:2)
555		< 0*6 0 1*7
556
557		# FinalNil, "y by communicating."
558		< (0 10) (00110 00000 0010) (011 000 011 001 000 010) 0
559		> 0 10*3 0*2 10 0 10*2 0 (110 <D2:0) 10 (110 <D2:2) 0 10*3 0 10*3 0*2 10
560		  0 10*2 0 (110 <D2:0) 10 (110 <D2:2) 0 10*2 0*2 10*3 0*2 10 0
561		  (110 <D2:0) 10 0*2 10 0 10 0*2 10 0*2 10 0 10 0*2 10 0*2 10 0 10 0 10
562		  0*3 10*2 0*3 10 0*2 10 0 10*2 0 10 0*2 10 0*2 10*3 0*2 10 0 10
563		  (110 <D2:0) 0*2 10*3 0 10 0*3 10 0 10*2 0 10 0*2 10*2 0*3 10 0*2 10
564		  0*3 10*2 0*2 10*2 0*3 10 0 10*2 (111 <D7:36) 10 (110 <D2:3)
565		  (110 <D2:3) (110 <D2:3) (110 <D2:3) (110 <D2:3) (110 <D2:3)
566		  (110 <D2:3) (110 <D2:3) 10
567		< 0*6 0 1*7
568	`)
569	vectors := []struct {
570		data                   string
571		inOff, outOff, numBlks int64
572		final                  FinalMode
573	}{{
574		"The quick brown fox jumped over the lazy dog.",
575		93, 45, 2, FinalMeta,
576	}, {
577		"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
578		104, 56, 2, FinalStream,
579	}, {
580		"Do not communicate by sharing memory; instead, share memory by communicating.",
581		148, 77, 3, FinalNil,
582	}}
583
584	rd := bytes.NewReader(data)
585	for i, v := range vectors {
586		mr.Reset(rd)
587		buf, err := ioutil.ReadAll(mr)
588		if err != nil {
589			t.Errorf("test %d, unexpected error: ReadAll() = %v", i, err)
590		}
591		if str := string(buf); str != v.data {
592			t.Errorf("test %d, output mismatch:\ngot  %s\nwant %s", i, str, v.data)
593		}
594		if err := mr.Close(); err != nil {
595			t.Errorf("test %d, unexpected error: Close() = %v", i, err)
596		}
597		if mr.InputOffset != v.inOff {
598			t.Errorf("test %d, input offset mismatch, got %d, want %d", i, mr.InputOffset, v.inOff)
599		}
600		if mr.OutputOffset != v.outOff {
601			t.Errorf("test %d, output offset mismatch, got %d, want %d", i, mr.OutputOffset, v.outOff)
602		}
603		if mr.NumBlocks != v.numBlks {
604			t.Errorf("test %d, block count mismatch, got %d, want %d", i, mr.NumBlocks, v.numBlks)
605		}
606		if mr.FinalMode != v.final {
607			t.Errorf("test %d, final mode mismatch, got %d, want %d", i, mr.FinalMode, v.final)
608		}
609	}
610}
611
612func BenchmarkReader(b *testing.B) {
613	data := make([]byte, 1<<16)
614	rand.Read(data)
615
616	rd := new(bytes.Reader)
617	bb := new(bytes.Buffer)
618	mr := new(Reader)
619
620	mw := NewWriter(bb)
621	mw.Write(data)
622	mw.Close()
623
624	b.ReportAllocs()
625	b.SetBytes(int64(len(data)))
626	b.ResetTimer()
627
628	for i := 0; i < b.N; i++ {
629		rd.Reset(bb.Bytes())
630		mr.Reset(rd)
631
632		cnt, err := io.Copy(ioutil.Discard, mr)
633		if cnt != int64(len(data)) || err != nil {
634			b.Fatalf("Copy() = (%d, %v), want (%d, nil)", cnt, err, len(data))
635		}
636		if err := mr.Close(); err != nil {
637			b.Fatalf("Close() = %v, want nil", err)
638		}
639	}
640}
641