blob: c0c48bd707a6c61097370b2d67a78b946c9b5808 [file] [log] [blame]
khenaidoo7d3c5582021-08-11 18:09:44 -04001package huff0
2
3import (
4 "errors"
5 "fmt"
6 "io"
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +05307 "sync"
khenaidoo7d3c5582021-08-11 18:09:44 -04008
9 "github.com/klauspost/compress/fse"
10)
11
12type dTable struct {
13 single []dEntrySingle
khenaidoo7d3c5582021-08-11 18:09:44 -040014}
15
16// single-symbols decoding
17type dEntrySingle struct {
18 entry uint16
19}
20
khenaidoo7d3c5582021-08-11 18:09:44 -040021// Uses special code for all tables that are < 8 bits.
22const use8BitTables = true
23
24// ReadTable will read a table from the input.
25// The size of the input may be larger than the table definition.
26// Any content remaining after the table definition will be returned.
27// If no Scratch is provided a new one is allocated.
28// The returned Scratch can be used for encoding or decoding input using this table.
29func ReadTable(in []byte, s *Scratch) (s2 *Scratch, remain []byte, err error) {
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +053030 s, err = s.prepare(nil)
khenaidoo7d3c5582021-08-11 18:09:44 -040031 if err != nil {
32 return s, nil, err
33 }
34 if len(in) <= 1 {
35 return s, nil, errors.New("input too small for table")
36 }
37 iSize := in[0]
38 in = in[1:]
39 if iSize >= 128 {
40 // Uncompressed
41 oSize := iSize - 127
42 iSize = (oSize + 1) / 2
43 if int(iSize) > len(in) {
44 return s, nil, errors.New("input too small for table")
45 }
46 for n := uint8(0); n < oSize; n += 2 {
47 v := in[n/2]
48 s.huffWeight[n] = v >> 4
49 s.huffWeight[n+1] = v & 15
50 }
51 s.symbolLen = uint16(oSize)
52 in = in[iSize:]
53 } else {
54 if len(in) < int(iSize) {
55 return s, nil, fmt.Errorf("input too small for table, want %d bytes, have %d", iSize, len(in))
56 }
57 // FSE compressed weights
58 s.fse.DecompressLimit = 255
59 hw := s.huffWeight[:]
60 s.fse.Out = hw
61 b, err := fse.Decompress(in[:iSize], s.fse)
62 s.fse.Out = nil
63 if err != nil {
64 return s, nil, err
65 }
66 if len(b) > 255 {
67 return s, nil, errors.New("corrupt input: output table too large")
68 }
69 s.symbolLen = uint16(len(b))
70 in = in[iSize:]
71 }
72
73 // collect weight stats
74 var rankStats [16]uint32
75 weightTotal := uint32(0)
76 for _, v := range s.huffWeight[:s.symbolLen] {
77 if v > tableLogMax {
78 return s, nil, errors.New("corrupt input: weight too large")
79 }
80 v2 := v & 15
81 rankStats[v2]++
82 // (1 << (v2-1)) is slower since the compiler cannot prove that v2 isn't 0.
83 weightTotal += (1 << v2) >> 1
84 }
85 if weightTotal == 0 {
86 return s, nil, errors.New("corrupt input: weights zero")
87 }
88
89 // get last non-null symbol weight (implied, total must be 2^n)
90 {
91 tableLog := highBit32(weightTotal) + 1
92 if tableLog > tableLogMax {
93 return s, nil, errors.New("corrupt input: tableLog too big")
94 }
95 s.actualTableLog = uint8(tableLog)
96 // determine last weight
97 {
98 total := uint32(1) << tableLog
99 rest := total - weightTotal
100 verif := uint32(1) << highBit32(rest)
101 lastWeight := highBit32(rest) + 1
102 if verif != rest {
103 // last value must be a clean power of 2
104 return s, nil, errors.New("corrupt input: last value not power of two")
105 }
106 s.huffWeight[s.symbolLen] = uint8(lastWeight)
107 s.symbolLen++
108 rankStats[lastWeight]++
109 }
110 }
111
112 if (rankStats[1] < 2) || (rankStats[1]&1 != 0) {
113 // by construction : at least 2 elts of rank 1, must be even
114 return s, nil, errors.New("corrupt input: min elt size, even check failed ")
115 }
116
117 // TODO: Choose between single/double symbol decoding
118
119 // Calculate starting value for each rank
120 {
121 var nextRankStart uint32
122 for n := uint8(1); n < s.actualTableLog+1; n++ {
123 current := nextRankStart
124 nextRankStart += rankStats[n] << (n - 1)
125 rankStats[n] = current
126 }
127 }
128
129 // fill DTable (always full size)
130 tSize := 1 << tableLogMax
131 if len(s.dt.single) != tSize {
132 s.dt.single = make([]dEntrySingle, tSize)
133 }
134 cTable := s.prevTable
135 if cap(cTable) < maxSymbolValue+1 {
136 cTable = make([]cTableEntry, 0, maxSymbolValue+1)
137 }
138 cTable = cTable[:maxSymbolValue+1]
139 s.prevTable = cTable[:s.symbolLen]
140 s.prevTableLog = s.actualTableLog
141
142 for n, w := range s.huffWeight[:s.symbolLen] {
143 if w == 0 {
144 cTable[n] = cTableEntry{
145 val: 0,
146 nBits: 0,
147 }
148 continue
149 }
150 length := (uint32(1) << w) >> 1
151 d := dEntrySingle{
152 entry: uint16(s.actualTableLog+1-w) | (uint16(n) << 8),
153 }
154
155 rank := &rankStats[w]
156 cTable[n] = cTableEntry{
157 val: uint16(*rank >> (w - 1)),
158 nBits: uint8(d.entry),
159 }
160
161 single := s.dt.single[*rank : *rank+length]
162 for i := range single {
163 single[i] = d
164 }
165 *rank += length
166 }
167
168 return s, in, nil
169}
170
171// Decompress1X will decompress a 1X encoded stream.
172// The length of the supplied input must match the end of a block exactly.
173// Before this is called, the table must be initialized with ReadTable unless
174// the encoder re-used the table.
175// deprecated: Use the stateless Decoder() to get a concurrent version.
176func (s *Scratch) Decompress1X(in []byte) (out []byte, err error) {
177 if cap(s.Out) < s.MaxDecodedSize {
178 s.Out = make([]byte, s.MaxDecodedSize)
179 }
180 s.Out = s.Out[:0:s.MaxDecodedSize]
181 s.Out, err = s.Decoder().Decompress1X(s.Out, in)
182 return s.Out, err
183}
184
185// Decompress4X will decompress a 4X encoded stream.
186// Before this is called, the table must be initialized with ReadTable unless
187// the encoder re-used the table.
188// The length of the supplied input must match the end of a block exactly.
189// The destination size of the uncompressed data must be known and provided.
190// deprecated: Use the stateless Decoder() to get a concurrent version.
191func (s *Scratch) Decompress4X(in []byte, dstSize int) (out []byte, err error) {
192 if dstSize > s.MaxDecodedSize {
193 return nil, ErrMaxDecodedSizeExceeded
194 }
195 if cap(s.Out) < dstSize {
196 s.Out = make([]byte, s.MaxDecodedSize)
197 }
198 s.Out = s.Out[:0:dstSize]
199 s.Out, err = s.Decoder().Decompress4X(s.Out, in)
200 return s.Out, err
201}
202
203// Decoder will return a stateless decoder that can be used by multiple
204// decompressors concurrently.
205// Before this is called, the table must be initialized with ReadTable.
206// The Decoder is still linked to the scratch buffer so that cannot be reused.
207// However, it is safe to discard the scratch.
208func (s *Scratch) Decoder() *Decoder {
209 return &Decoder{
210 dt: s.dt,
211 actualTableLog: s.actualTableLog,
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530212 bufs: &s.decPool,
khenaidoo7d3c5582021-08-11 18:09:44 -0400213 }
214}
215
216// Decoder provides stateless decoding.
217type Decoder struct {
218 dt dTable
219 actualTableLog uint8
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530220 bufs *sync.Pool
khenaidoo7d3c5582021-08-11 18:09:44 -0400221}
222
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530223func (d *Decoder) buffer() *[4][256]byte {
224 buf, ok := d.bufs.Get().(*[4][256]byte)
225 if ok {
226 return buf
khenaidoo7d3c5582021-08-11 18:09:44 -0400227 }
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530228 return &[4][256]byte{}
khenaidoo7d3c5582021-08-11 18:09:44 -0400229}
230
231// decompress1X8Bit will decompress a 1X encoded stream with tablelog <= 8.
232// The cap of the output buffer will be the maximum decompressed size.
233// The length of the supplied input must match the end of a block exactly.
234func (d *Decoder) decompress1X8Bit(dst, src []byte) ([]byte, error) {
235 if d.actualTableLog == 8 {
236 return d.decompress1X8BitExactly(dst, src)
237 }
238 var br bitReaderBytes
239 err := br.init(src)
240 if err != nil {
241 return dst, err
242 }
243 maxDecodedSize := cap(dst)
244 dst = dst[:0]
245
246 // Avoid bounds check by always having full sized table.
247 dt := d.dt.single[:256]
248
249 // Use temp table to avoid bound checks/append penalty.
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530250 bufs := d.buffer()
251 buf := &bufs[0]
khenaidoo7d3c5582021-08-11 18:09:44 -0400252 var off uint8
253
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530254 switch d.actualTableLog {
255 case 8:
256 const shift = 8 - 8
257 for br.off >= 4 {
258 br.fillFast()
259 v := dt[uint8(br.value>>(56+shift))]
260 br.advance(uint8(v.entry))
261 buf[off+0] = uint8(v.entry >> 8)
khenaidoo7d3c5582021-08-11 18:09:44 -0400262
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530263 v = dt[uint8(br.value>>(56+shift))]
264 br.advance(uint8(v.entry))
265 buf[off+1] = uint8(v.entry >> 8)
khenaidoo7d3c5582021-08-11 18:09:44 -0400266
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530267 v = dt[uint8(br.value>>(56+shift))]
268 br.advance(uint8(v.entry))
269 buf[off+2] = uint8(v.entry >> 8)
khenaidoo7d3c5582021-08-11 18:09:44 -0400270
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530271 v = dt[uint8(br.value>>(56+shift))]
272 br.advance(uint8(v.entry))
273 buf[off+3] = uint8(v.entry >> 8)
khenaidoo7d3c5582021-08-11 18:09:44 -0400274
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530275 off += 4
276 if off == 0 {
277 if len(dst)+256 > maxDecodedSize {
278 br.close()
279 d.bufs.Put(bufs)
280 return nil, ErrMaxDecodedSizeExceeded
281 }
282 dst = append(dst, buf[:]...)
khenaidoo7d3c5582021-08-11 18:09:44 -0400283 }
khenaidoo7d3c5582021-08-11 18:09:44 -0400284 }
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530285 case 7:
286 const shift = 8 - 7
287 for br.off >= 4 {
288 br.fillFast()
289 v := dt[uint8(br.value>>(56+shift))]
290 br.advance(uint8(v.entry))
291 buf[off+0] = uint8(v.entry >> 8)
292
293 v = dt[uint8(br.value>>(56+shift))]
294 br.advance(uint8(v.entry))
295 buf[off+1] = uint8(v.entry >> 8)
296
297 v = dt[uint8(br.value>>(56+shift))]
298 br.advance(uint8(v.entry))
299 buf[off+2] = uint8(v.entry >> 8)
300
301 v = dt[uint8(br.value>>(56+shift))]
302 br.advance(uint8(v.entry))
303 buf[off+3] = uint8(v.entry >> 8)
304
305 off += 4
306 if off == 0 {
307 if len(dst)+256 > maxDecodedSize {
308 br.close()
309 d.bufs.Put(bufs)
310 return nil, ErrMaxDecodedSizeExceeded
311 }
312 dst = append(dst, buf[:]...)
313 }
314 }
315 case 6:
316 const shift = 8 - 6
317 for br.off >= 4 {
318 br.fillFast()
319 v := dt[uint8(br.value>>(56+shift))]
320 br.advance(uint8(v.entry))
321 buf[off+0] = uint8(v.entry >> 8)
322
323 v = dt[uint8(br.value>>(56+shift))]
324 br.advance(uint8(v.entry))
325 buf[off+1] = uint8(v.entry >> 8)
326
327 v = dt[uint8(br.value>>(56+shift))]
328 br.advance(uint8(v.entry))
329 buf[off+2] = uint8(v.entry >> 8)
330
331 v = dt[uint8(br.value>>(56+shift))]
332 br.advance(uint8(v.entry))
333 buf[off+3] = uint8(v.entry >> 8)
334
335 off += 4
336 if off == 0 {
337 if len(dst)+256 > maxDecodedSize {
338 d.bufs.Put(bufs)
339 br.close()
340 return nil, ErrMaxDecodedSizeExceeded
341 }
342 dst = append(dst, buf[:]...)
343 }
344 }
345 case 5:
346 const shift = 8 - 5
347 for br.off >= 4 {
348 br.fillFast()
349 v := dt[uint8(br.value>>(56+shift))]
350 br.advance(uint8(v.entry))
351 buf[off+0] = uint8(v.entry >> 8)
352
353 v = dt[uint8(br.value>>(56+shift))]
354 br.advance(uint8(v.entry))
355 buf[off+1] = uint8(v.entry >> 8)
356
357 v = dt[uint8(br.value>>(56+shift))]
358 br.advance(uint8(v.entry))
359 buf[off+2] = uint8(v.entry >> 8)
360
361 v = dt[uint8(br.value>>(56+shift))]
362 br.advance(uint8(v.entry))
363 buf[off+3] = uint8(v.entry >> 8)
364
365 off += 4
366 if off == 0 {
367 if len(dst)+256 > maxDecodedSize {
368 d.bufs.Put(bufs)
369 br.close()
370 return nil, ErrMaxDecodedSizeExceeded
371 }
372 dst = append(dst, buf[:]...)
373 }
374 }
375 case 4:
376 const shift = 8 - 4
377 for br.off >= 4 {
378 br.fillFast()
379 v := dt[uint8(br.value>>(56+shift))]
380 br.advance(uint8(v.entry))
381 buf[off+0] = uint8(v.entry >> 8)
382
383 v = dt[uint8(br.value>>(56+shift))]
384 br.advance(uint8(v.entry))
385 buf[off+1] = uint8(v.entry >> 8)
386
387 v = dt[uint8(br.value>>(56+shift))]
388 br.advance(uint8(v.entry))
389 buf[off+2] = uint8(v.entry >> 8)
390
391 v = dt[uint8(br.value>>(56+shift))]
392 br.advance(uint8(v.entry))
393 buf[off+3] = uint8(v.entry >> 8)
394
395 off += 4
396 if off == 0 {
397 if len(dst)+256 > maxDecodedSize {
398 d.bufs.Put(bufs)
399 br.close()
400 return nil, ErrMaxDecodedSizeExceeded
401 }
402 dst = append(dst, buf[:]...)
403 }
404 }
405 case 3:
406 const shift = 8 - 3
407 for br.off >= 4 {
408 br.fillFast()
409 v := dt[uint8(br.value>>(56+shift))]
410 br.advance(uint8(v.entry))
411 buf[off+0] = uint8(v.entry >> 8)
412
413 v = dt[uint8(br.value>>(56+shift))]
414 br.advance(uint8(v.entry))
415 buf[off+1] = uint8(v.entry >> 8)
416
417 v = dt[uint8(br.value>>(56+shift))]
418 br.advance(uint8(v.entry))
419 buf[off+2] = uint8(v.entry >> 8)
420
421 v = dt[uint8(br.value>>(56+shift))]
422 br.advance(uint8(v.entry))
423 buf[off+3] = uint8(v.entry >> 8)
424
425 off += 4
426 if off == 0 {
427 if len(dst)+256 > maxDecodedSize {
428 d.bufs.Put(bufs)
429 br.close()
430 return nil, ErrMaxDecodedSizeExceeded
431 }
432 dst = append(dst, buf[:]...)
433 }
434 }
435 case 2:
436 const shift = 8 - 2
437 for br.off >= 4 {
438 br.fillFast()
439 v := dt[uint8(br.value>>(56+shift))]
440 br.advance(uint8(v.entry))
441 buf[off+0] = uint8(v.entry >> 8)
442
443 v = dt[uint8(br.value>>(56+shift))]
444 br.advance(uint8(v.entry))
445 buf[off+1] = uint8(v.entry >> 8)
446
447 v = dt[uint8(br.value>>(56+shift))]
448 br.advance(uint8(v.entry))
449 buf[off+2] = uint8(v.entry >> 8)
450
451 v = dt[uint8(br.value>>(56+shift))]
452 br.advance(uint8(v.entry))
453 buf[off+3] = uint8(v.entry >> 8)
454
455 off += 4
456 if off == 0 {
457 if len(dst)+256 > maxDecodedSize {
458 d.bufs.Put(bufs)
459 br.close()
460 return nil, ErrMaxDecodedSizeExceeded
461 }
462 dst = append(dst, buf[:]...)
463 }
464 }
465 case 1:
466 const shift = 8 - 1
467 for br.off >= 4 {
468 br.fillFast()
469 v := dt[uint8(br.value>>(56+shift))]
470 br.advance(uint8(v.entry))
471 buf[off+0] = uint8(v.entry >> 8)
472
473 v = dt[uint8(br.value>>(56+shift))]
474 br.advance(uint8(v.entry))
475 buf[off+1] = uint8(v.entry >> 8)
476
477 v = dt[uint8(br.value>>(56+shift))]
478 br.advance(uint8(v.entry))
479 buf[off+2] = uint8(v.entry >> 8)
480
481 v = dt[uint8(br.value>>(56+shift))]
482 br.advance(uint8(v.entry))
483 buf[off+3] = uint8(v.entry >> 8)
484
485 off += 4
486 if off == 0 {
487 if len(dst)+256 > maxDecodedSize {
488 d.bufs.Put(bufs)
489 br.close()
490 return nil, ErrMaxDecodedSizeExceeded
491 }
492 dst = append(dst, buf[:]...)
493 }
494 }
495 default:
496 d.bufs.Put(bufs)
497 return nil, fmt.Errorf("invalid tablelog: %d", d.actualTableLog)
khenaidoo7d3c5582021-08-11 18:09:44 -0400498 }
499
500 if len(dst)+int(off) > maxDecodedSize {
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530501 d.bufs.Put(bufs)
khenaidoo7d3c5582021-08-11 18:09:44 -0400502 br.close()
503 return nil, ErrMaxDecodedSizeExceeded
504 }
505 dst = append(dst, buf[:off]...)
506
507 // br < 4, so uint8 is fine
508 bitsLeft := int8(uint8(br.off)*8 + (64 - br.bitsRead))
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530509 shift := (8 - d.actualTableLog) & 7
510
khenaidoo7d3c5582021-08-11 18:09:44 -0400511 for bitsLeft > 0 {
512 if br.bitsRead >= 64-8 {
513 for br.off > 0 {
514 br.value |= uint64(br.in[br.off-1]) << (br.bitsRead - 8)
515 br.bitsRead -= 8
516 br.off--
517 }
518 }
519 if len(dst) >= maxDecodedSize {
520 br.close()
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530521 d.bufs.Put(bufs)
khenaidoo7d3c5582021-08-11 18:09:44 -0400522 return nil, ErrMaxDecodedSizeExceeded
523 }
524 v := dt[br.peekByteFast()>>shift]
525 nBits := uint8(v.entry)
526 br.advance(nBits)
527 bitsLeft -= int8(nBits)
528 dst = append(dst, uint8(v.entry>>8))
529 }
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530530 d.bufs.Put(bufs)
khenaidoo7d3c5582021-08-11 18:09:44 -0400531 return dst, br.close()
532}
533
534// decompress1X8Bit will decompress a 1X encoded stream with tablelog <= 8.
535// The cap of the output buffer will be the maximum decompressed size.
536// The length of the supplied input must match the end of a block exactly.
537func (d *Decoder) decompress1X8BitExactly(dst, src []byte) ([]byte, error) {
538 var br bitReaderBytes
539 err := br.init(src)
540 if err != nil {
541 return dst, err
542 }
543 maxDecodedSize := cap(dst)
544 dst = dst[:0]
545
546 // Avoid bounds check by always having full sized table.
547 dt := d.dt.single[:256]
548
549 // Use temp table to avoid bound checks/append penalty.
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530550 bufs := d.buffer()
551 buf := &bufs[0]
khenaidoo7d3c5582021-08-11 18:09:44 -0400552 var off uint8
553
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530554 const shift = 56
khenaidoo7d3c5582021-08-11 18:09:44 -0400555
556 //fmt.Printf("mask: %b, tl:%d\n", mask, d.actualTableLog)
557 for br.off >= 4 {
558 br.fillFast()
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530559 v := dt[uint8(br.value>>shift)]
khenaidoo7d3c5582021-08-11 18:09:44 -0400560 br.advance(uint8(v.entry))
561 buf[off+0] = uint8(v.entry >> 8)
562
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530563 v = dt[uint8(br.value>>shift)]
khenaidoo7d3c5582021-08-11 18:09:44 -0400564 br.advance(uint8(v.entry))
565 buf[off+1] = uint8(v.entry >> 8)
566
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530567 v = dt[uint8(br.value>>shift)]
khenaidoo7d3c5582021-08-11 18:09:44 -0400568 br.advance(uint8(v.entry))
569 buf[off+2] = uint8(v.entry >> 8)
570
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530571 v = dt[uint8(br.value>>shift)]
khenaidoo7d3c5582021-08-11 18:09:44 -0400572 br.advance(uint8(v.entry))
573 buf[off+3] = uint8(v.entry >> 8)
574
575 off += 4
576 if off == 0 {
577 if len(dst)+256 > maxDecodedSize {
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530578 d.bufs.Put(bufs)
khenaidoo7d3c5582021-08-11 18:09:44 -0400579 br.close()
580 return nil, ErrMaxDecodedSizeExceeded
581 }
582 dst = append(dst, buf[:]...)
583 }
584 }
585
586 if len(dst)+int(off) > maxDecodedSize {
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530587 d.bufs.Put(bufs)
khenaidoo7d3c5582021-08-11 18:09:44 -0400588 br.close()
589 return nil, ErrMaxDecodedSizeExceeded
590 }
591 dst = append(dst, buf[:off]...)
592
593 // br < 4, so uint8 is fine
594 bitsLeft := int8(uint8(br.off)*8 + (64 - br.bitsRead))
595 for bitsLeft > 0 {
596 if br.bitsRead >= 64-8 {
597 for br.off > 0 {
598 br.value |= uint64(br.in[br.off-1]) << (br.bitsRead - 8)
599 br.bitsRead -= 8
600 br.off--
601 }
602 }
603 if len(dst) >= maxDecodedSize {
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530604 d.bufs.Put(bufs)
khenaidoo7d3c5582021-08-11 18:09:44 -0400605 br.close()
606 return nil, ErrMaxDecodedSizeExceeded
607 }
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530608 v := dt[br.peekByteFast()]
khenaidoo7d3c5582021-08-11 18:09:44 -0400609 nBits := uint8(v.entry)
610 br.advance(nBits)
611 bitsLeft -= int8(nBits)
612 dst = append(dst, uint8(v.entry>>8))
613 }
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530614 d.bufs.Put(bufs)
khenaidoo7d3c5582021-08-11 18:09:44 -0400615 return dst, br.close()
616}
617
618// Decompress4X will decompress a 4X encoded stream.
619// The length of the supplied input must match the end of a block exactly.
620// The *capacity* of the dst slice must match the destination size of
621// the uncompressed data exactly.
khenaidoo7d3c5582021-08-11 18:09:44 -0400622func (d *Decoder) decompress4X8bit(dst, src []byte) ([]byte, error) {
623 if d.actualTableLog == 8 {
624 return d.decompress4X8bitExactly(dst, src)
625 }
626
627 var br [4]bitReaderBytes
628 start := 6
629 for i := 0; i < 3; i++ {
630 length := int(src[i*2]) | (int(src[i*2+1]) << 8)
631 if start+length >= len(src) {
632 return nil, errors.New("truncated input (or invalid offset)")
633 }
634 err := br[i].init(src[start : start+length])
635 if err != nil {
636 return nil, err
637 }
638 start += length
639 }
640 err := br[3].init(src[start:])
641 if err != nil {
642 return nil, err
643 }
644
645 // destination, offset to match first output
646 dstSize := cap(dst)
647 dst = dst[:dstSize]
648 out := dst
649 dstEvery := (dstSize + 3) / 4
650
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530651 shift := (56 + (8 - d.actualTableLog)) & 63
khenaidoo7d3c5582021-08-11 18:09:44 -0400652
653 const tlSize = 1 << 8
khenaidoo7d3c5582021-08-11 18:09:44 -0400654 single := d.dt.single[:tlSize]
655
656 // Use temp table to avoid bound checks/append penalty.
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530657 buf := d.buffer()
khenaidoo7d3c5582021-08-11 18:09:44 -0400658 var off uint8
659 var decoded int
660
661 // Decode 4 values from each decoder/loop.
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530662 const bufoff = 256
khenaidoo7d3c5582021-08-11 18:09:44 -0400663 for {
664 if br[0].off < 4 || br[1].off < 4 || br[2].off < 4 || br[3].off < 4 {
665 break
666 }
667
668 {
669 // Interleave 2 decodes.
670 const stream = 0
671 const stream2 = 1
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530672 br1 := &br[stream]
673 br2 := &br[stream2]
674 br1.fillFast()
675 br2.fillFast()
khenaidoo7d3c5582021-08-11 18:09:44 -0400676
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530677 v := single[uint8(br1.value>>shift)].entry
678 v2 := single[uint8(br2.value>>shift)].entry
679 br1.bitsRead += uint8(v)
680 br1.value <<= v & 63
681 br2.bitsRead += uint8(v2)
682 br2.value <<= v2 & 63
683 buf[stream][off] = uint8(v >> 8)
684 buf[stream2][off] = uint8(v2 >> 8)
khenaidoo7d3c5582021-08-11 18:09:44 -0400685
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530686 v = single[uint8(br1.value>>shift)].entry
687 v2 = single[uint8(br2.value>>shift)].entry
688 br1.bitsRead += uint8(v)
689 br1.value <<= v & 63
690 br2.bitsRead += uint8(v2)
691 br2.value <<= v2 & 63
692 buf[stream][off+1] = uint8(v >> 8)
693 buf[stream2][off+1] = uint8(v2 >> 8)
khenaidoo7d3c5582021-08-11 18:09:44 -0400694
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530695 v = single[uint8(br1.value>>shift)].entry
696 v2 = single[uint8(br2.value>>shift)].entry
697 br1.bitsRead += uint8(v)
698 br1.value <<= v & 63
699 br2.bitsRead += uint8(v2)
700 br2.value <<= v2 & 63
701 buf[stream][off+2] = uint8(v >> 8)
702 buf[stream2][off+2] = uint8(v2 >> 8)
khenaidoo7d3c5582021-08-11 18:09:44 -0400703
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530704 v = single[uint8(br1.value>>shift)].entry
705 v2 = single[uint8(br2.value>>shift)].entry
706 br1.bitsRead += uint8(v)
707 br1.value <<= v & 63
708 br2.bitsRead += uint8(v2)
709 br2.value <<= v2 & 63
710 buf[stream][off+3] = uint8(v >> 8)
711 buf[stream2][off+3] = uint8(v2 >> 8)
khenaidoo7d3c5582021-08-11 18:09:44 -0400712 }
713
714 {
715 const stream = 2
716 const stream2 = 3
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530717 br1 := &br[stream]
718 br2 := &br[stream2]
719 br1.fillFast()
720 br2.fillFast()
khenaidoo7d3c5582021-08-11 18:09:44 -0400721
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530722 v := single[uint8(br1.value>>shift)].entry
723 v2 := single[uint8(br2.value>>shift)].entry
724 br1.bitsRead += uint8(v)
725 br1.value <<= v & 63
726 br2.bitsRead += uint8(v2)
727 br2.value <<= v2 & 63
728 buf[stream][off] = uint8(v >> 8)
729 buf[stream2][off] = uint8(v2 >> 8)
khenaidoo7d3c5582021-08-11 18:09:44 -0400730
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530731 v = single[uint8(br1.value>>shift)].entry
732 v2 = single[uint8(br2.value>>shift)].entry
733 br1.bitsRead += uint8(v)
734 br1.value <<= v & 63
735 br2.bitsRead += uint8(v2)
736 br2.value <<= v2 & 63
737 buf[stream][off+1] = uint8(v >> 8)
738 buf[stream2][off+1] = uint8(v2 >> 8)
khenaidoo7d3c5582021-08-11 18:09:44 -0400739
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530740 v = single[uint8(br1.value>>shift)].entry
741 v2 = single[uint8(br2.value>>shift)].entry
742 br1.bitsRead += uint8(v)
743 br1.value <<= v & 63
744 br2.bitsRead += uint8(v2)
745 br2.value <<= v2 & 63
746 buf[stream][off+2] = uint8(v >> 8)
747 buf[stream2][off+2] = uint8(v2 >> 8)
khenaidoo7d3c5582021-08-11 18:09:44 -0400748
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530749 v = single[uint8(br1.value>>shift)].entry
750 v2 = single[uint8(br2.value>>shift)].entry
751 br1.bitsRead += uint8(v)
752 br1.value <<= v & 63
753 br2.bitsRead += uint8(v2)
754 br2.value <<= v2 & 63
755 buf[stream][off+3] = uint8(v >> 8)
756 buf[stream2][off+3] = uint8(v2 >> 8)
khenaidoo7d3c5582021-08-11 18:09:44 -0400757 }
758
759 off += 4
760
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530761 if off == 0 {
khenaidoo7d3c5582021-08-11 18:09:44 -0400762 if bufoff > dstEvery {
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530763 d.bufs.Put(buf)
khenaidoo7d3c5582021-08-11 18:09:44 -0400764 return nil, errors.New("corruption detected: stream overrun 1")
765 }
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530766 copy(out, buf[0][:])
767 copy(out[dstEvery:], buf[1][:])
768 copy(out[dstEvery*2:], buf[2][:])
769 copy(out[dstEvery*3:], buf[3][:])
khenaidoo7d3c5582021-08-11 18:09:44 -0400770 out = out[bufoff:]
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530771 decoded += bufoff * 4
khenaidoo7d3c5582021-08-11 18:09:44 -0400772 // There must at least be 3 buffers left.
773 if len(out) < dstEvery*3 {
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530774 d.bufs.Put(buf)
khenaidoo7d3c5582021-08-11 18:09:44 -0400775 return nil, errors.New("corruption detected: stream overrun 2")
776 }
777 }
778 }
779 if off > 0 {
780 ioff := int(off)
781 if len(out) < dstEvery*3+ioff {
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530782 d.bufs.Put(buf)
khenaidoo7d3c5582021-08-11 18:09:44 -0400783 return nil, errors.New("corruption detected: stream overrun 3")
784 }
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530785 copy(out, buf[0][:off])
786 copy(out[dstEvery:], buf[1][:off])
787 copy(out[dstEvery*2:], buf[2][:off])
788 copy(out[dstEvery*3:], buf[3][:off])
khenaidoo7d3c5582021-08-11 18:09:44 -0400789 decoded += int(off) * 4
790 out = out[off:]
791 }
792
793 // Decode remaining.
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530794 // Decode remaining.
795 remainBytes := dstEvery - (decoded / 4)
khenaidoo7d3c5582021-08-11 18:09:44 -0400796 for i := range br {
797 offset := dstEvery * i
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530798 endsAt := offset + remainBytes
799 if endsAt > len(out) {
800 endsAt = len(out)
801 }
khenaidoo7d3c5582021-08-11 18:09:44 -0400802 br := &br[i]
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530803 bitsLeft := br.remaining()
khenaidoo7d3c5582021-08-11 18:09:44 -0400804 for bitsLeft > 0 {
805 if br.finished() {
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530806 d.bufs.Put(buf)
khenaidoo7d3c5582021-08-11 18:09:44 -0400807 return nil, io.ErrUnexpectedEOF
808 }
809 if br.bitsRead >= 56 {
810 if br.off >= 4 {
811 v := br.in[br.off-4:]
812 v = v[:4]
813 low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
814 br.value |= uint64(low) << (br.bitsRead - 32)
815 br.bitsRead -= 32
816 br.off -= 4
817 } else {
818 for br.off > 0 {
819 br.value |= uint64(br.in[br.off-1]) << (br.bitsRead - 8)
820 br.bitsRead -= 8
821 br.off--
822 }
823 }
824 }
825 // end inline...
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530826 if offset >= endsAt {
827 d.bufs.Put(buf)
khenaidoo7d3c5582021-08-11 18:09:44 -0400828 return nil, errors.New("corruption detected: stream overrun 4")
829 }
830
831 // Read value and increment offset.
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530832 v := single[uint8(br.value>>shift)].entry
khenaidoo7d3c5582021-08-11 18:09:44 -0400833 nBits := uint8(v)
834 br.advance(nBits)
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530835 bitsLeft -= uint(nBits)
khenaidoo7d3c5582021-08-11 18:09:44 -0400836 out[offset] = uint8(v >> 8)
837 offset++
838 }
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530839 if offset != endsAt {
840 d.bufs.Put(buf)
841 return nil, fmt.Errorf("corruption detected: short output block %d, end %d != %d", i, offset, endsAt)
842 }
khenaidoo7d3c5582021-08-11 18:09:44 -0400843 decoded += offset - dstEvery*i
844 err = br.close()
845 if err != nil {
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530846 d.bufs.Put(buf)
khenaidoo7d3c5582021-08-11 18:09:44 -0400847 return nil, err
848 }
849 }
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530850 d.bufs.Put(buf)
khenaidoo7d3c5582021-08-11 18:09:44 -0400851 if dstSize != decoded {
852 return nil, errors.New("corruption detected: short output block")
853 }
854 return dst, nil
855}
856
857// Decompress4X will decompress a 4X encoded stream.
858// The length of the supplied input must match the end of a block exactly.
859// The *capacity* of the dst slice must match the destination size of
860// the uncompressed data exactly.
861func (d *Decoder) decompress4X8bitExactly(dst, src []byte) ([]byte, error) {
862 var br [4]bitReaderBytes
863 start := 6
864 for i := 0; i < 3; i++ {
865 length := int(src[i*2]) | (int(src[i*2+1]) << 8)
866 if start+length >= len(src) {
867 return nil, errors.New("truncated input (or invalid offset)")
868 }
869 err := br[i].init(src[start : start+length])
870 if err != nil {
871 return nil, err
872 }
873 start += length
874 }
875 err := br[3].init(src[start:])
876 if err != nil {
877 return nil, err
878 }
879
880 // destination, offset to match first output
881 dstSize := cap(dst)
882 dst = dst[:dstSize]
883 out := dst
884 dstEvery := (dstSize + 3) / 4
885
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530886 const shift = 56
khenaidoo7d3c5582021-08-11 18:09:44 -0400887 const tlSize = 1 << 8
khenaidoo7d3c5582021-08-11 18:09:44 -0400888 single := d.dt.single[:tlSize]
889
890 // Use temp table to avoid bound checks/append penalty.
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530891 buf := d.buffer()
khenaidoo7d3c5582021-08-11 18:09:44 -0400892 var off uint8
893 var decoded int
894
895 // Decode 4 values from each decoder/loop.
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530896 const bufoff = 256
khenaidoo7d3c5582021-08-11 18:09:44 -0400897 for {
898 if br[0].off < 4 || br[1].off < 4 || br[2].off < 4 || br[3].off < 4 {
899 break
900 }
901
902 {
903 // Interleave 2 decodes.
904 const stream = 0
905 const stream2 = 1
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530906 br1 := &br[stream]
907 br2 := &br[stream2]
908 br1.fillFast()
909 br2.fillFast()
khenaidoo7d3c5582021-08-11 18:09:44 -0400910
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530911 v := single[uint8(br1.value>>shift)].entry
912 v2 := single[uint8(br2.value>>shift)].entry
913 br1.bitsRead += uint8(v)
914 br1.value <<= v & 63
915 br2.bitsRead += uint8(v2)
916 br2.value <<= v2 & 63
917 buf[stream][off] = uint8(v >> 8)
918 buf[stream2][off] = uint8(v2 >> 8)
khenaidoo7d3c5582021-08-11 18:09:44 -0400919
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530920 v = single[uint8(br1.value>>shift)].entry
921 v2 = single[uint8(br2.value>>shift)].entry
922 br1.bitsRead += uint8(v)
923 br1.value <<= v & 63
924 br2.bitsRead += uint8(v2)
925 br2.value <<= v2 & 63
926 buf[stream][off+1] = uint8(v >> 8)
927 buf[stream2][off+1] = uint8(v2 >> 8)
khenaidoo7d3c5582021-08-11 18:09:44 -0400928
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530929 v = single[uint8(br1.value>>shift)].entry
930 v2 = single[uint8(br2.value>>shift)].entry
931 br1.bitsRead += uint8(v)
932 br1.value <<= v & 63
933 br2.bitsRead += uint8(v2)
934 br2.value <<= v2 & 63
935 buf[stream][off+2] = uint8(v >> 8)
936 buf[stream2][off+2] = uint8(v2 >> 8)
khenaidoo7d3c5582021-08-11 18:09:44 -0400937
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530938 v = single[uint8(br1.value>>shift)].entry
939 v2 = single[uint8(br2.value>>shift)].entry
940 br1.bitsRead += uint8(v)
941 br1.value <<= v & 63
942 br2.bitsRead += uint8(v2)
943 br2.value <<= v2 & 63
944 buf[stream][off+3] = uint8(v >> 8)
945 buf[stream2][off+3] = uint8(v2 >> 8)
khenaidoo7d3c5582021-08-11 18:09:44 -0400946 }
947
948 {
949 const stream = 2
950 const stream2 = 3
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530951 br1 := &br[stream]
952 br2 := &br[stream2]
953 br1.fillFast()
954 br2.fillFast()
khenaidoo7d3c5582021-08-11 18:09:44 -0400955
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530956 v := single[uint8(br1.value>>shift)].entry
957 v2 := single[uint8(br2.value>>shift)].entry
958 br1.bitsRead += uint8(v)
959 br1.value <<= v & 63
960 br2.bitsRead += uint8(v2)
961 br2.value <<= v2 & 63
962 buf[stream][off] = uint8(v >> 8)
963 buf[stream2][off] = uint8(v2 >> 8)
khenaidoo7d3c5582021-08-11 18:09:44 -0400964
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530965 v = single[uint8(br1.value>>shift)].entry
966 v2 = single[uint8(br2.value>>shift)].entry
967 br1.bitsRead += uint8(v)
968 br1.value <<= v & 63
969 br2.bitsRead += uint8(v2)
970 br2.value <<= v2 & 63
971 buf[stream][off+1] = uint8(v >> 8)
972 buf[stream2][off+1] = uint8(v2 >> 8)
khenaidoo7d3c5582021-08-11 18:09:44 -0400973
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530974 v = single[uint8(br1.value>>shift)].entry
975 v2 = single[uint8(br2.value>>shift)].entry
976 br1.bitsRead += uint8(v)
977 br1.value <<= v & 63
978 br2.bitsRead += uint8(v2)
979 br2.value <<= v2 & 63
980 buf[stream][off+2] = uint8(v >> 8)
981 buf[stream2][off+2] = uint8(v2 >> 8)
khenaidoo7d3c5582021-08-11 18:09:44 -0400982
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530983 v = single[uint8(br1.value>>shift)].entry
984 v2 = single[uint8(br2.value>>shift)].entry
985 br1.bitsRead += uint8(v)
986 br1.value <<= v & 63
987 br2.bitsRead += uint8(v2)
988 br2.value <<= v2 & 63
989 buf[stream][off+3] = uint8(v >> 8)
990 buf[stream2][off+3] = uint8(v2 >> 8)
khenaidoo7d3c5582021-08-11 18:09:44 -0400991 }
992
993 off += 4
994
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530995 if off == 0 {
khenaidoo7d3c5582021-08-11 18:09:44 -0400996 if bufoff > dstEvery {
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +0530997 d.bufs.Put(buf)
khenaidoo7d3c5582021-08-11 18:09:44 -0400998 return nil, errors.New("corruption detected: stream overrun 1")
999 }
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +05301000 copy(out, buf[0][:])
1001 copy(out[dstEvery:], buf[1][:])
1002 copy(out[dstEvery*2:], buf[2][:])
1003 copy(out[dstEvery*3:], buf[3][:])
khenaidoo7d3c5582021-08-11 18:09:44 -04001004 out = out[bufoff:]
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +05301005 decoded += bufoff * 4
khenaidoo7d3c5582021-08-11 18:09:44 -04001006 // There must at least be 3 buffers left.
1007 if len(out) < dstEvery*3 {
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +05301008 d.bufs.Put(buf)
khenaidoo7d3c5582021-08-11 18:09:44 -04001009 return nil, errors.New("corruption detected: stream overrun 2")
1010 }
1011 }
1012 }
1013 if off > 0 {
1014 ioff := int(off)
1015 if len(out) < dstEvery*3+ioff {
1016 return nil, errors.New("corruption detected: stream overrun 3")
1017 }
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +05301018 copy(out, buf[0][:off])
1019 copy(out[dstEvery:], buf[1][:off])
1020 copy(out[dstEvery*2:], buf[2][:off])
1021 copy(out[dstEvery*3:], buf[3][:off])
khenaidoo7d3c5582021-08-11 18:09:44 -04001022 decoded += int(off) * 4
1023 out = out[off:]
1024 }
1025
1026 // Decode remaining.
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +05301027 remainBytes := dstEvery - (decoded / 4)
khenaidoo7d3c5582021-08-11 18:09:44 -04001028 for i := range br {
1029 offset := dstEvery * i
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +05301030 endsAt := offset + remainBytes
1031 if endsAt > len(out) {
1032 endsAt = len(out)
1033 }
khenaidoo7d3c5582021-08-11 18:09:44 -04001034 br := &br[i]
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +05301035 bitsLeft := br.remaining()
khenaidoo7d3c5582021-08-11 18:09:44 -04001036 for bitsLeft > 0 {
1037 if br.finished() {
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +05301038 d.bufs.Put(buf)
khenaidoo7d3c5582021-08-11 18:09:44 -04001039 return nil, io.ErrUnexpectedEOF
1040 }
1041 if br.bitsRead >= 56 {
1042 if br.off >= 4 {
1043 v := br.in[br.off-4:]
1044 v = v[:4]
1045 low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
1046 br.value |= uint64(low) << (br.bitsRead - 32)
1047 br.bitsRead -= 32
1048 br.off -= 4
1049 } else {
1050 for br.off > 0 {
1051 br.value |= uint64(br.in[br.off-1]) << (br.bitsRead - 8)
1052 br.bitsRead -= 8
1053 br.off--
1054 }
1055 }
1056 }
1057 // end inline...
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +05301058 if offset >= endsAt {
1059 d.bufs.Put(buf)
khenaidoo7d3c5582021-08-11 18:09:44 -04001060 return nil, errors.New("corruption detected: stream overrun 4")
1061 }
1062
1063 // Read value and increment offset.
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +05301064 v := single[br.peekByteFast()].entry
khenaidoo7d3c5582021-08-11 18:09:44 -04001065 nBits := uint8(v)
1066 br.advance(nBits)
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +05301067 bitsLeft -= uint(nBits)
khenaidoo7d3c5582021-08-11 18:09:44 -04001068 out[offset] = uint8(v >> 8)
1069 offset++
1070 }
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +05301071 if offset != endsAt {
1072 d.bufs.Put(buf)
1073 return nil, fmt.Errorf("corruption detected: short output block %d, end %d != %d", i, offset, endsAt)
1074 }
1075
khenaidoo7d3c5582021-08-11 18:09:44 -04001076 decoded += offset - dstEvery*i
1077 err = br.close()
1078 if err != nil {
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +05301079 d.bufs.Put(buf)
khenaidoo7d3c5582021-08-11 18:09:44 -04001080 return nil, err
1081 }
1082 }
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +05301083 d.bufs.Put(buf)
khenaidoo7d3c5582021-08-11 18:09:44 -04001084 if dstSize != decoded {
1085 return nil, errors.New("corruption detected: short output block")
1086 }
1087 return dst, nil
1088}
1089
1090// matches will compare a decoding table to a coding table.
1091// Errors are written to the writer.
1092// Nothing will be written if table is ok.
1093func (s *Scratch) matches(ct cTable, w io.Writer) {
1094 if s == nil || len(s.dt.single) == 0 {
1095 return
1096 }
1097 dt := s.dt.single[:1<<s.actualTableLog]
1098 tablelog := s.actualTableLog
1099 ok := 0
1100 broken := 0
1101 for sym, enc := range ct {
1102 errs := 0
1103 broken++
1104 if enc.nBits == 0 {
1105 for _, dec := range dt {
1106 if uint8(dec.entry>>8) == byte(sym) {
1107 fmt.Fprintf(w, "symbol %x has decoder, but no encoder\n", sym)
1108 errs++
1109 break
1110 }
1111 }
1112 if errs == 0 {
1113 broken--
1114 }
1115 continue
1116 }
1117 // Unused bits in input
1118 ub := tablelog - enc.nBits
1119 top := enc.val << ub
1120 // decoder looks at top bits.
1121 dec := dt[top]
1122 if uint8(dec.entry) != enc.nBits {
1123 fmt.Fprintf(w, "symbol 0x%x bit size mismatch (enc: %d, dec:%d).\n", sym, enc.nBits, uint8(dec.entry))
1124 errs++
1125 }
1126 if uint8(dec.entry>>8) != uint8(sym) {
1127 fmt.Fprintf(w, "symbol 0x%x decoder output mismatch (enc: %d, dec:%d).\n", sym, sym, uint8(dec.entry>>8))
1128 errs++
1129 }
1130 if errs > 0 {
1131 fmt.Fprintf(w, "%d errros in base, stopping\n", errs)
1132 continue
1133 }
1134 // Ensure that all combinations are covered.
1135 for i := uint16(0); i < (1 << ub); i++ {
1136 vval := top | i
1137 dec := dt[vval]
1138 if uint8(dec.entry) != enc.nBits {
1139 fmt.Fprintf(w, "symbol 0x%x bit size mismatch (enc: %d, dec:%d).\n", vval, enc.nBits, uint8(dec.entry))
1140 errs++
1141 }
1142 if uint8(dec.entry>>8) != uint8(sym) {
1143 fmt.Fprintf(w, "symbol 0x%x decoder output mismatch (enc: %d, dec:%d).\n", vval, sym, uint8(dec.entry>>8))
1144 errs++
1145 }
1146 if errs > 20 {
1147 fmt.Fprintf(w, "%d errros, stopping\n", errs)
1148 break
1149 }
1150 }
1151 if errs == 0 {
1152 ok++
1153 broken--
1154 }
1155 }
1156 if broken > 0 {
1157 fmt.Fprintf(w, "%d broken, %d ok\n", broken, ok)
1158 }
1159}