1package huff0
2
3import (
4	"bytes"
5	"testing"
6)
7
8func TestDecompress1X(t *testing.T) {
9	for _, test := range testfiles {
10		t.Run(test.name, func(t *testing.T) {
11			var s = &Scratch{}
12			buf0, err := test.fn()
13			if err != nil {
14				t.Fatal(err)
15			}
16			if len(buf0) > BlockSizeMax {
17				buf0 = buf0[:BlockSizeMax]
18			}
19			b, re, err := Compress1X(buf0, s)
20			if err != test.err1X {
21				t.Errorf("want error %v (%T), got %v (%T)", test.err1X, test.err1X, err, err)
22			}
23			if err != nil {
24				t.Log(test.name, err.Error())
25				return
26			}
27			if b == nil {
28				t.Error("got no output")
29				return
30			}
31			if len(s.OutTable) == 0 {
32				t.Error("got no table definition")
33			}
34			if re {
35				t.Error("claimed to have re-used.")
36			}
37			if len(s.OutData) == 0 {
38				t.Error("got no data output")
39			}
40
41			wantRemain := len(s.OutData)
42			t.Logf("%s: %d -> %d bytes (%.2f:1) %t (table: %d bytes)", test.name, len(buf0), len(b), float64(len(buf0))/float64(len(b)), re, len(s.OutTable))
43
44			s.Out = nil
45			var remain []byte
46			s, remain, err = ReadTable(b, s)
47			if err != nil {
48				t.Error(err)
49				return
50			}
51			var buf bytes.Buffer
52			if s.matches(s.prevTable, &buf); buf.Len() > 0 {
53				t.Error(buf.String())
54			}
55			if len(remain) != wantRemain {
56				t.Fatalf("remain mismatch, want %d, got %d bytes", wantRemain, len(remain))
57			}
58			t.Logf("remain: %d bytes, ok", len(remain))
59			dc, err := s.Decompress1X(remain)
60			if err != nil {
61				t.Error(err)
62				return
63			}
64			if len(buf0) != len(dc) {
65				t.Errorf(test.name+"decompressed, want size: %d, got %d", len(buf0), len(dc))
66				if len(buf0) > len(dc) {
67					buf0 = buf0[:len(dc)]
68				} else {
69					dc = dc[:len(buf0)]
70				}
71				if !bytes.Equal(buf0, dc) {
72					if len(dc) > 1024 {
73						t.Log(string(dc[:1024]))
74						t.Errorf(test.name+"decompressed, got delta: \n(in)\t%02x !=\n(out)\t%02x\n", buf0[:1024], dc[:1024])
75					} else {
76						t.Log(string(dc))
77						t.Errorf(test.name+"decompressed, got delta: (in) %v != (out) %v\n", buf0, dc)
78					}
79				}
80				return
81			}
82			if !bytes.Equal(buf0, dc) {
83				if len(buf0) > 1024 {
84					t.Log(string(dc[:1024]))
85				} else {
86					t.Log(string(dc))
87				}
88				//t.Errorf(test.name+": decompressed, got delta: \n%s")
89				t.Errorf(test.name + ": decompressed, got delta")
90			}
91			if !t.Failed() {
92				t.Log("... roundtrip ok!")
93			}
94		})
95	}
96}
97
98func TestDecompress4X(t *testing.T) {
99	for _, test := range testfiles {
100		t.Run(test.name, func(t *testing.T) {
101			var s = &Scratch{}
102			buf0, err := test.fn()
103			if err != nil {
104				t.Fatal(err)
105			}
106			if len(buf0) > BlockSizeMax {
107				buf0 = buf0[:BlockSizeMax]
108			}
109			b, re, err := Compress4X(buf0, s)
110			if err != test.err4X {
111				t.Errorf("want error %v (%T), got %v (%T)", test.err1X, test.err1X, err, err)
112			}
113			if err != nil {
114				t.Log(test.name, err.Error())
115				return
116			}
117			if b == nil {
118				t.Error("got no output")
119				return
120			}
121			if len(s.OutTable) == 0 {
122				t.Error("got no table definition")
123			}
124			if re {
125				t.Error("claimed to have re-used.")
126			}
127			if len(s.OutData) == 0 {
128				t.Error("got no data output")
129			}
130
131			wantRemain := len(s.OutData)
132			t.Logf("%s: %d -> %d bytes (%.2f:1) %t (table: %d bytes)", test.name, len(buf0), len(b), float64(len(buf0))/float64(len(b)), re, len(s.OutTable))
133
134			s.Out = nil
135			var remain []byte
136			s, remain, err = ReadTable(b, s)
137			if err != nil {
138				t.Error(err)
139				return
140			}
141			var buf bytes.Buffer
142			if s.matches(s.prevTable, &buf); buf.Len() > 0 {
143				t.Error(buf.String())
144			}
145			if len(remain) != wantRemain {
146				t.Fatalf("remain mismatch, want %d, got %d bytes", wantRemain, len(remain))
147			}
148			t.Logf("remain: %d bytes, ok", len(remain))
149			dc, err := s.Decompress4X(remain, len(buf0))
150			if err != nil {
151				t.Error(err)
152				return
153			}
154			if len(buf0) != len(dc) {
155				t.Errorf(test.name+"decompressed, want size: %d, got %d", len(buf0), len(dc))
156				if len(buf0) > len(dc) {
157					buf0 = buf0[:len(dc)]
158				} else {
159					dc = dc[:len(buf0)]
160				}
161				if !bytes.Equal(buf0, dc) {
162					if len(dc) > 1024 {
163						t.Log(string(dc[:1024]))
164						t.Errorf(test.name+"decompressed, got delta: \n(in)\t%02x !=\n(out)\t%02x\n", buf0[:1024], dc[:1024])
165					} else {
166						t.Log(string(dc))
167						t.Errorf(test.name+"decompressed, got delta: (in) %v != (out) %v\n", buf0, dc)
168					}
169				}
170				return
171			}
172			if !bytes.Equal(buf0, dc) {
173				if len(buf0) > 1024 {
174					t.Log(string(dc[:1024]))
175				} else {
176					t.Log(string(dc))
177				}
178				//t.Errorf(test.name+": decompressed, got delta: \n%s")
179				t.Errorf(test.name + ": decompressed, got delta")
180			}
181			if !t.Failed() {
182				t.Log("... roundtrip ok!")
183			}
184		})
185	}
186}
187
188func TestRoundtrip1XFuzz(t *testing.T) {
189	for _, test := range testfilesExtended {
190		t.Run(test.name, func(t *testing.T) {
191			var s = &Scratch{}
192			buf0, err := test.fn()
193			if err != nil {
194				t.Fatal(err)
195			}
196			if len(buf0) > BlockSizeMax {
197				buf0 = buf0[:BlockSizeMax]
198			}
199			b, re, err := Compress1X(buf0, s)
200			if err != nil {
201				if err == ErrIncompressible || err == ErrUseRLE || err == ErrTooBig {
202					t.Log(test.name, err.Error())
203					return
204				}
205				t.Error(test.name, err.Error())
206				return
207			}
208			if b == nil {
209				t.Error("got no output")
210				return
211			}
212			if len(s.OutTable) == 0 {
213				t.Error("got no table definition")
214			}
215			if re {
216				t.Error("claimed to have re-used.")
217			}
218			if len(s.OutData) == 0 {
219				t.Error("got no data output")
220			}
221
222			wantRemain := len(s.OutData)
223			t.Logf("%s: %d -> %d bytes (%.2f:1) %t (table: %d bytes)", test.name, len(buf0), len(b), float64(len(buf0))/float64(len(b)), re, len(s.OutTable))
224
225			s.Out = nil
226			var remain []byte
227			s, remain, err = ReadTable(b, s)
228			if err != nil {
229				t.Error(err)
230				return
231			}
232			var buf bytes.Buffer
233			if s.matches(s.prevTable, &buf); buf.Len() > 0 {
234				t.Error(buf.String())
235			}
236			if len(remain) != wantRemain {
237				t.Fatalf("remain mismatch, want %d, got %d bytes", wantRemain, len(remain))
238			}
239			t.Logf("remain: %d bytes, ok", len(remain))
240			dc, err := s.Decompress1X(remain)
241			if err != nil {
242				t.Error(err)
243				return
244			}
245			if len(buf0) != len(dc) {
246				t.Errorf(test.name+"decompressed, want size: %d, got %d", len(buf0), len(dc))
247				if len(buf0) > len(dc) {
248					buf0 = buf0[:len(dc)]
249				} else {
250					dc = dc[:len(buf0)]
251				}
252				if !bytes.Equal(buf0, dc) {
253					if len(dc) > 1024 {
254						t.Log(string(dc[:1024]))
255						t.Errorf(test.name+"decompressed, got delta: \n(in)\t%02x !=\n(out)\t%02x\n", buf0[:1024], dc[:1024])
256					} else {
257						t.Log(string(dc))
258						t.Errorf(test.name+"decompressed, got delta: (in) %v != (out) %v\n", buf0, dc)
259					}
260				}
261				return
262			}
263			if !bytes.Equal(buf0, dc) {
264				if len(buf0) > 1024 {
265					t.Log(string(dc[:1024]))
266				} else {
267					t.Log(string(dc))
268				}
269				//t.Errorf(test.name+": decompressed, got delta: \n%s")
270				t.Errorf(test.name + ": decompressed, got delta")
271			}
272			if !t.Failed() {
273				t.Log("... roundtrip ok!")
274			}
275		})
276	}
277}
278
279func TestRoundtrip4XFuzz(t *testing.T) {
280	for _, test := range testfilesExtended {
281		t.Run(test.name, func(t *testing.T) {
282			var s = &Scratch{}
283			buf0, err := test.fn()
284			if err != nil {
285				t.Fatal(err)
286			}
287			if len(buf0) > BlockSizeMax {
288				buf0 = buf0[:BlockSizeMax]
289			}
290			b, re, err := Compress4X(buf0, s)
291			if err != nil {
292				if err == ErrIncompressible || err == ErrUseRLE || err == ErrTooBig {
293					t.Log(test.name, err.Error())
294					return
295				}
296				t.Error(test.name, err.Error())
297				return
298			}
299			if b == nil {
300				t.Error("got no output")
301				return
302			}
303			if len(s.OutTable) == 0 {
304				t.Error("got no table definition")
305			}
306			if re {
307				t.Error("claimed to have re-used.")
308			}
309			if len(s.OutData) == 0 {
310				t.Error("got no data output")
311			}
312
313			wantRemain := len(s.OutData)
314			t.Logf("%s: %d -> %d bytes (%.2f:1) %t (table: %d bytes)", test.name, len(buf0), len(b), float64(len(buf0))/float64(len(b)), re, len(s.OutTable))
315
316			s.Out = nil
317			var remain []byte
318			s, remain, err = ReadTable(b, s)
319			if err != nil {
320				t.Error(err)
321				return
322			}
323			var buf bytes.Buffer
324			if s.matches(s.prevTable, &buf); buf.Len() > 0 {
325				t.Error(buf.String())
326			}
327			if len(remain) != wantRemain {
328				t.Fatalf("remain mismatch, want %d, got %d bytes", wantRemain, len(remain))
329			}
330			t.Logf("remain: %d bytes, ok", len(remain))
331			dc, err := s.Decompress4X(remain, len(buf0))
332			if err != nil {
333				t.Error(err)
334				return
335			}
336			if len(buf0) != len(dc) {
337				t.Errorf(test.name+"decompressed, want size: %d, got %d", len(buf0), len(dc))
338				if len(buf0) > len(dc) {
339					buf0 = buf0[:len(dc)]
340				} else {
341					dc = dc[:len(buf0)]
342				}
343				if !bytes.Equal(buf0, dc) {
344					if len(dc) > 1024 {
345						t.Log(string(dc[:1024]))
346						t.Errorf(test.name+"decompressed, got delta: \n(in)\t%02x !=\n(out)\t%02x\n", buf0[:1024], dc[:1024])
347					} else {
348						t.Log(string(dc))
349						t.Errorf(test.name+"decompressed, got delta: (in) %v != (out) %v\n", buf0, dc)
350					}
351				}
352				return
353			}
354			if !bytes.Equal(buf0, dc) {
355				if len(buf0) > 1024 {
356					t.Log(string(dc[:1024]))
357				} else {
358					t.Log(string(dc))
359				}
360				//t.Errorf(test.name+": decompressed, got delta: \n%s")
361				t.Errorf(test.name + ": decompressed, got delta")
362			}
363			if !t.Failed() {
364				t.Log("... roundtrip ok!")
365			}
366		})
367	}
368}
369
370func BenchmarkDecompress1XTable(b *testing.B) {
371	for _, tt := range testfiles {
372		test := tt
373		if test.err1X != nil {
374			continue
375		}
376		b.Run(test.name, func(b *testing.B) {
377			var s = &Scratch{}
378			s.Reuse = ReusePolicyNone
379			buf0, err := test.fn()
380			if err != nil {
381				b.Fatal(err)
382			}
383			if len(buf0) > BlockSizeMax {
384				buf0 = buf0[:BlockSizeMax]
385			}
386			compressed, _, err := Compress1X(buf0, s)
387			if err != test.err1X {
388				b.Fatal("unexpected error:", err)
389			}
390			s.Out = nil
391			s, remain, _ := ReadTable(compressed, s)
392			s.Decompress1X(remain)
393			b.ResetTimer()
394			b.ReportAllocs()
395			b.SetBytes(int64(len(buf0)))
396			for i := 0; i < b.N; i++ {
397				s, remain, err := ReadTable(compressed, s)
398				if err != nil {
399					b.Fatal(err)
400				}
401				_, err = s.Decompress1X(remain)
402				if err != nil {
403					b.Fatal(err)
404				}
405			}
406		})
407	}
408}
409
410func BenchmarkDecompress1XNoTable(b *testing.B) {
411	for _, tt := range testfiles {
412		test := tt
413		if test.err1X != nil {
414			continue
415		}
416		b.Run(test.name, func(b *testing.B) {
417			var s = &Scratch{}
418			s.Reuse = ReusePolicyNone
419			buf0, err := test.fn()
420			if err != nil {
421				b.Fatal(err)
422			}
423			if len(buf0) > BlockSizeMax {
424				buf0 = buf0[:BlockSizeMax]
425			}
426			compressed, _, err := Compress1X(buf0, s)
427			if err != test.err1X {
428				b.Fatal("unexpected error:", err)
429			}
430			s.Out = nil
431			s, remain, _ := ReadTable(compressed, s)
432			s.Decompress1X(remain)
433			b.ResetTimer()
434			b.ReportAllocs()
435			b.SetBytes(int64(len(buf0)))
436			for i := 0; i < b.N; i++ {
437				_, err = s.Decompress1X(remain)
438				if err != nil {
439					b.Fatal(err)
440				}
441			}
442		})
443	}
444}
445
446func BenchmarkDecompress4XNoTable(b *testing.B) {
447	for _, tt := range testfiles {
448		test := tt
449		if test.err4X != nil {
450			continue
451		}
452		b.Run(test.name, func(b *testing.B) {
453			var s = &Scratch{}
454			s.Reuse = ReusePolicyNone
455			buf0, err := test.fn()
456			if err != nil {
457				b.Fatal(err)
458			}
459			if len(buf0) > BlockSizeMax {
460				buf0 = buf0[:BlockSizeMax]
461			}
462			compressed, _, err := Compress4X(buf0, s)
463			if err != test.err1X {
464				b.Fatal("unexpected error:", err)
465			}
466			s.Out = nil
467			s, remain, _ := ReadTable(compressed, s)
468			s.Decompress4X(remain, len(buf0))
469			b.ResetTimer()
470			b.ReportAllocs()
471			b.SetBytes(int64(len(buf0)))
472			for i := 0; i < b.N; i++ {
473				_, err = s.Decompress4X(remain, len(buf0))
474				if err != nil {
475					b.Fatal(err)
476				}
477			}
478		})
479	}
480}
481
482func BenchmarkDecompress4XTable(b *testing.B) {
483	for _, tt := range testfiles {
484		test := tt
485		if test.err4X != nil {
486			continue
487		}
488		b.Run(test.name, func(b *testing.B) {
489			var s = &Scratch{}
490			s.Reuse = ReusePolicyNone
491			buf0, err := test.fn()
492			if err != nil {
493				b.Fatal(err)
494			}
495			if len(buf0) > BlockSizeMax {
496				buf0 = buf0[:BlockSizeMax]
497			}
498			compressed, _, err := Compress4X(buf0, s)
499			if err != test.err1X {
500				b.Fatal("unexpected error:", err)
501			}
502			s.Out = nil
503			b.ResetTimer()
504			b.ReportAllocs()
505			b.SetBytes(int64(len(buf0)))
506			for i := 0; i < b.N; i++ {
507				s, remain, err := ReadTable(compressed, s)
508				if err != nil {
509					b.Fatal(err)
510				}
511				_, err = s.Decompress4X(remain, len(buf0))
512				if err != nil {
513					b.Fatal(err)
514				}
515			}
516		})
517	}
518}
519