blob: 919888edf7dcc994ca885aff12786e8245fc3a9f [file] [log] [blame]
Scott Bakered4efab2020-01-13 19:12:25 -08001// +build !amd64 appengine !gc noasm
2
3package lz4
4
5func decodeBlock(dst, src []byte) (ret int) {
6 const hasError = -2
7 defer func() {
8 if recover() != nil {
9 ret = hasError
10 }
11 }()
12
13 var si, di int
14 for {
15 // Literals and match lengths (token).
16 b := int(src[si])
17 si++
18
19 // Literals.
20 if lLen := b >> 4; lLen > 0 {
21 switch {
divyadesai19009132020-03-04 12:58:08 +000022 case lLen < 0xF && si+16 < len(src):
Scott Bakered4efab2020-01-13 19:12:25 -080023 // Shortcut 1
24 // if we have enough room in src and dst, and the literals length
25 // is small enough (0..14) then copy all 16 bytes, even if not all
26 // are part of the literals.
27 copy(dst[di:], src[si:si+16])
28 si += lLen
29 di += lLen
30 if mLen := b & 0xF; mLen < 0xF {
31 // Shortcut 2
32 // if the match length (4..18) fits within the literals, then copy
33 // all 18 bytes, even if not all are part of the literals.
34 mLen += 4
35 if offset := int(src[si]) | int(src[si+1])<<8; mLen <= offset {
36 i := di - offset
divyadesai19009132020-03-04 12:58:08 +000037 end := i + 18
38 if end > len(dst) {
39 // The remaining buffer may not hold 18 bytes.
40 // See https://github.com/pierrec/lz4/issues/51.
41 end = len(dst)
42 }
43 copy(dst[di:], dst[i:end])
Scott Bakered4efab2020-01-13 19:12:25 -080044 si += 2
45 di += mLen
46 continue
47 }
48 }
49 case lLen == 0xF:
50 for src[si] == 0xFF {
51 lLen += 0xFF
52 si++
53 }
54 lLen += int(src[si])
55 si++
56 fallthrough
57 default:
58 copy(dst[di:di+lLen], src[si:si+lLen])
59 si += lLen
60 di += lLen
61 }
62 }
63 if si >= len(src) {
64 return di
65 }
66
67 offset := int(src[si]) | int(src[si+1])<<8
68 if offset == 0 {
69 return hasError
70 }
71 si += 2
72
73 // Match.
74 mLen := b & 0xF
75 if mLen == 0xF {
76 for src[si] == 0xFF {
77 mLen += 0xFF
78 si++
79 }
80 mLen += int(src[si])
81 si++
82 }
83 mLen += minMatch
84
85 // Copy the match.
86 expanded := dst[di-offset:]
87 if mLen > offset {
88 // Efficiently copy the match dst[di-offset:di] into the dst slice.
89 bytesToCopy := offset * (mLen / offset)
90 for n := offset; n <= bytesToCopy+offset; n *= 2 {
91 copy(expanded[n:], expanded[:n])
92 }
93 di += bytesToCopy
94 mLen -= bytesToCopy
95 }
96 di += copy(dst[di:di+mLen], expanded[:mLen])
97 }
98}