blob: 4f6f37cb2c26a1843b4b584e84b8add3feb3ea37 [file] [log] [blame]
Akash Reddy Kankanalac28f0e22025-06-16 11:00:55 +05301//go:build !amd64 || appengine || !gc || noasm
2// +build !amd64 appengine !gc noasm
3
4// This file contains a generic implementation of Decoder.Decompress4X.
5package huff0
6
7import (
8 "errors"
9 "fmt"
10)
11
12// Decompress4X will decompress a 4X encoded stream.
13// The length of the supplied input must match the end of a block exactly.
14// The *capacity* of the dst slice must match the destination size of
15// the uncompressed data exactly.
16func (d *Decoder) Decompress4X(dst, src []byte) ([]byte, error) {
17 if len(d.dt.single) == 0 {
18 return nil, errors.New("no table loaded")
19 }
20 if len(src) < 6+(4*1) {
21 return nil, errors.New("input too small")
22 }
23 if use8BitTables && d.actualTableLog <= 8 {
24 return d.decompress4X8bit(dst, src)
25 }
26
27 var br [4]bitReaderShifted
28 // Decode "jump table"
29 start := 6
30 for i := 0; i < 3; i++ {
31 length := int(src[i*2]) | (int(src[i*2+1]) << 8)
32 if start+length >= len(src) {
33 return nil, errors.New("truncated input (or invalid offset)")
34 }
35 err := br[i].init(src[start : start+length])
36 if err != nil {
37 return nil, err
38 }
39 start += length
40 }
41 err := br[3].init(src[start:])
42 if err != nil {
43 return nil, err
44 }
45
46 // destination, offset to match first output
47 dstSize := cap(dst)
48 dst = dst[:dstSize]
49 out := dst
50 dstEvery := (dstSize + 3) / 4
51
52 const tlSize = 1 << tableLogMax
53 const tlMask = tlSize - 1
54 single := d.dt.single[:tlSize]
55
56 // Use temp table to avoid bound checks/append penalty.
57 buf := d.buffer()
58 var off uint8
59 var decoded int
60
61 // Decode 2 values from each decoder/loop.
62 const bufoff = 256
63 for {
64 if br[0].off < 4 || br[1].off < 4 || br[2].off < 4 || br[3].off < 4 {
65 break
66 }
67
68 {
69 const stream = 0
70 const stream2 = 1
71 br[stream].fillFast()
72 br[stream2].fillFast()
73
74 val := br[stream].peekBitsFast(d.actualTableLog)
75 val2 := br[stream2].peekBitsFast(d.actualTableLog)
76 v := single[val&tlMask]
77 v2 := single[val2&tlMask]
78 br[stream].advance(uint8(v.entry))
79 br[stream2].advance(uint8(v2.entry))
80 buf[stream][off] = uint8(v.entry >> 8)
81 buf[stream2][off] = uint8(v2.entry >> 8)
82
83 val = br[stream].peekBitsFast(d.actualTableLog)
84 val2 = br[stream2].peekBitsFast(d.actualTableLog)
85 v = single[val&tlMask]
86 v2 = single[val2&tlMask]
87 br[stream].advance(uint8(v.entry))
88 br[stream2].advance(uint8(v2.entry))
89 buf[stream][off+1] = uint8(v.entry >> 8)
90 buf[stream2][off+1] = uint8(v2.entry >> 8)
91 }
92
93 {
94 const stream = 2
95 const stream2 = 3
96 br[stream].fillFast()
97 br[stream2].fillFast()
98
99 val := br[stream].peekBitsFast(d.actualTableLog)
100 val2 := br[stream2].peekBitsFast(d.actualTableLog)
101 v := single[val&tlMask]
102 v2 := single[val2&tlMask]
103 br[stream].advance(uint8(v.entry))
104 br[stream2].advance(uint8(v2.entry))
105 buf[stream][off] = uint8(v.entry >> 8)
106 buf[stream2][off] = uint8(v2.entry >> 8)
107
108 val = br[stream].peekBitsFast(d.actualTableLog)
109 val2 = br[stream2].peekBitsFast(d.actualTableLog)
110 v = single[val&tlMask]
111 v2 = single[val2&tlMask]
112 br[stream].advance(uint8(v.entry))
113 br[stream2].advance(uint8(v2.entry))
114 buf[stream][off+1] = uint8(v.entry >> 8)
115 buf[stream2][off+1] = uint8(v2.entry >> 8)
116 }
117
118 off += 2
119
120 if off == 0 {
121 if bufoff > dstEvery {
122 d.bufs.Put(buf)
123 return nil, errors.New("corruption detected: stream overrun 1")
124 }
125 copy(out, buf[0][:])
126 copy(out[dstEvery:], buf[1][:])
127 copy(out[dstEvery*2:], buf[2][:])
128 copy(out[dstEvery*3:], buf[3][:])
129 out = out[bufoff:]
130 decoded += bufoff * 4
131 // There must at least be 3 buffers left.
132 if len(out) < dstEvery*3 {
133 d.bufs.Put(buf)
134 return nil, errors.New("corruption detected: stream overrun 2")
135 }
136 }
137 }
138 if off > 0 {
139 ioff := int(off)
140 if len(out) < dstEvery*3+ioff {
141 d.bufs.Put(buf)
142 return nil, errors.New("corruption detected: stream overrun 3")
143 }
144 copy(out, buf[0][:off])
145 copy(out[dstEvery:], buf[1][:off])
146 copy(out[dstEvery*2:], buf[2][:off])
147 copy(out[dstEvery*3:], buf[3][:off])
148 decoded += int(off) * 4
149 out = out[off:]
150 }
151
152 // Decode remaining.
153 remainBytes := dstEvery - (decoded / 4)
154 for i := range br {
155 offset := dstEvery * i
156 endsAt := offset + remainBytes
157 if endsAt > len(out) {
158 endsAt = len(out)
159 }
160 br := &br[i]
161 bitsLeft := br.remaining()
162 for bitsLeft > 0 {
163 br.fill()
164 if offset >= endsAt {
165 d.bufs.Put(buf)
166 return nil, errors.New("corruption detected: stream overrun 4")
167 }
168
169 // Read value and increment offset.
170 val := br.peekBitsFast(d.actualTableLog)
171 v := single[val&tlMask].entry
172 nBits := uint8(v)
173 br.advance(nBits)
174 bitsLeft -= uint(nBits)
175 out[offset] = uint8(v >> 8)
176 offset++
177 }
178 if offset != endsAt {
179 d.bufs.Put(buf)
180 return nil, fmt.Errorf("corruption detected: short output block %d, end %d != %d", i, offset, endsAt)
181 }
182 decoded += offset - dstEvery*i
183 err = br.close()
184 if err != nil {
185 return nil, err
186 }
187 }
188 d.bufs.Put(buf)
189 if dstSize != decoded {
190 return nil, errors.New("corruption detected: short output block")
191 }
192 return dst, nil
193}
194
195// Decompress1X will decompress a 1X encoded stream.
196// The cap of the output buffer will be the maximum decompressed size.
197// The length of the supplied input must match the end of a block exactly.
198func (d *Decoder) Decompress1X(dst, src []byte) ([]byte, error) {
199 if len(d.dt.single) == 0 {
200 return nil, errors.New("no table loaded")
201 }
202 if use8BitTables && d.actualTableLog <= 8 {
203 return d.decompress1X8Bit(dst, src)
204 }
205 var br bitReaderShifted
206 err := br.init(src)
207 if err != nil {
208 return dst, err
209 }
210 maxDecodedSize := cap(dst)
211 dst = dst[:0]
212
213 // Avoid bounds check by always having full sized table.
214 const tlSize = 1 << tableLogMax
215 const tlMask = tlSize - 1
216 dt := d.dt.single[:tlSize]
217
218 // Use temp table to avoid bound checks/append penalty.
219 bufs := d.buffer()
220 buf := &bufs[0]
221 var off uint8
222
223 for br.off >= 8 {
224 br.fillFast()
225 v := dt[br.peekBitsFast(d.actualTableLog)&tlMask]
226 br.advance(uint8(v.entry))
227 buf[off+0] = uint8(v.entry >> 8)
228
229 v = dt[br.peekBitsFast(d.actualTableLog)&tlMask]
230 br.advance(uint8(v.entry))
231 buf[off+1] = uint8(v.entry >> 8)
232
233 // Refill
234 br.fillFast()
235
236 v = dt[br.peekBitsFast(d.actualTableLog)&tlMask]
237 br.advance(uint8(v.entry))
238 buf[off+2] = uint8(v.entry >> 8)
239
240 v = dt[br.peekBitsFast(d.actualTableLog)&tlMask]
241 br.advance(uint8(v.entry))
242 buf[off+3] = uint8(v.entry >> 8)
243
244 off += 4
245 if off == 0 {
246 if len(dst)+256 > maxDecodedSize {
247 br.close()
248 d.bufs.Put(bufs)
249 return nil, ErrMaxDecodedSizeExceeded
250 }
251 dst = append(dst, buf[:]...)
252 }
253 }
254
255 if len(dst)+int(off) > maxDecodedSize {
256 d.bufs.Put(bufs)
257 br.close()
258 return nil, ErrMaxDecodedSizeExceeded
259 }
260 dst = append(dst, buf[:off]...)
261
262 // br < 8, so uint8 is fine
263 bitsLeft := uint8(br.off)*8 + 64 - br.bitsRead
264 for bitsLeft > 0 {
265 br.fill()
266 if false && br.bitsRead >= 32 {
267 if br.off >= 4 {
268 v := br.in[br.off-4:]
269 v = v[:4]
270 low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
271 br.value = (br.value << 32) | uint64(low)
272 br.bitsRead -= 32
273 br.off -= 4
274 } else {
275 for br.off > 0 {
276 br.value = (br.value << 8) | uint64(br.in[br.off-1])
277 br.bitsRead -= 8
278 br.off--
279 }
280 }
281 }
282 if len(dst) >= maxDecodedSize {
283 d.bufs.Put(bufs)
284 br.close()
285 return nil, ErrMaxDecodedSizeExceeded
286 }
287 v := d.dt.single[br.peekBitsFast(d.actualTableLog)&tlMask]
288 nBits := uint8(v.entry)
289 br.advance(nBits)
290 bitsLeft -= nBits
291 dst = append(dst, uint8(v.entry>>8))
292 }
293 d.bufs.Put(bufs)
294 return dst, br.close()
295}