-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlengthencode.go
154 lines (143 loc) · 3.78 KB
/
lengthencode.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package fastmsgpack
import (
"fmt"
"math"
"sync"
"github.com/hexon/fastmsgpack/internal"
)
var lengthEncoderPool = sync.Pool{New: func() any { return make([]lengthEncoderAction, 128) }}
// LengthEncode injects a length-encoding extension before every map and array to make skipping over it faster.
// The result is appended to dst and returned. dst can be nil.
func LengthEncode(dst, data []byte) ([]byte, error) {
le := lengthEncoder{
data: data,
currentChunk: lengthEncoderPool.Get().([]lengthEncoderAction)[:0],
}
l, err := le.parseValue()
le.listOfChunks = append(le.listOfChunks, le.currentChunk)
if err != nil {
for _, chunks := range le.listOfChunks {
lengthEncoderPool.Put(chunks)
}
return nil, err
}
if cap(dst) < l {
d := make([]byte, 0, len(dst)+l)
copy(d, dst)
dst = d
}
offset := 0
for _, chunks := range le.listOfChunks {
for _, c := range chunks {
switch c := c.(type) {
case lengthEncoderCopy:
dst = append(dst, data[offset:offset+int(c)]...)
offset += int(c)
case lengthEncoderSkip:
offset += int(c)
case lengthEncoderHeader:
dst = appendLengthHeader(dst, *c)
}
}
lengthEncoderPool.Put(chunks)
}
return dst, nil
}
type lengthEncoder struct {
data []byte
listOfChunks [][]lengthEncoderAction
currentChunk []lengthEncoderAction
offset int
}
type lengthEncoderAction interface{}
type lengthEncoderCopy int
type lengthEncoderSkip int
type lengthEncoderHeader *int
func (le *lengthEncoder) parseValue() (int, error) {
if l := internal.DecodeLengthPrefixExtension(le.data[le.offset:]); l > 0 {
le.appendAction(lengthEncoderSkip(l))
le.offset += l
}
elements, consume, isMap := internal.DecodeUnwrappedMapLen(le.data[le.offset:])
if !isMap {
var ok bool
elements, consume, ok = internal.DecodeUnwrappedArrayLen(le.data[le.offset:])
if !ok {
sz, err := Size(le.data[le.offset:])
if err != nil {
return 0, err
}
le.offset += sz
le.appendCopy(sz)
return sz, nil
}
}
le.offset += consume
h := lengthEncoderHeader(new(int))
*h = consume
le.appendAction(h)
le.appendAction(lengthEncoderCopy(consume))
if isMap {
elements *= 2
}
for i := 0; elements > i; i++ {
sz, err := le.parseValue()
if err != nil {
return 0, err
}
*h += sz
}
if *h > math.MaxUint32 {
return 0, fmt.Errorf("fastmsgpack.LengthEncode: array/map data too long to encode (len %d)", *h)
}
hdrSize := sizeOfLengthHeader(*h)
return hdrSize + *h, nil
}
func (le *lengthEncoder) appendCopy(sz int) {
if len(le.currentChunk) > 0 {
if l, ok := le.currentChunk[len(le.currentChunk)-1].(lengthEncoderCopy); ok {
le.currentChunk[len(le.currentChunk)-1] = l + lengthEncoderCopy(sz)
return
}
}
le.appendAction(lengthEncoderCopy(sz))
}
func (le *lengthEncoder) appendAction(a lengthEncoderAction) {
if len(le.currentChunk) == cap(le.currentChunk) {
le.listOfChunks = append(le.listOfChunks, le.currentChunk)
le.currentChunk = lengthEncoderPool.Get().([]lengthEncoderAction)[:0]
}
le.currentChunk = append(le.currentChunk, a)
}
func sizeOfLengthHeader(wrapped int) int {
if wrapped <= math.MaxUint8 {
switch wrapped {
case 1, 2, 4, 8, 16:
return 2
}
return 3
} else if wrapped <= math.MaxUint16 {
return 4
}
return 6
}
func appendLengthHeader(dst []byte, wrapped int) []byte {
if wrapped <= math.MaxUint8 {
switch wrapped {
case 1:
return append(dst, 0xd4, 17)
case 2:
return append(dst, 0xd5, 17)
case 4:
return append(dst, 0xd6, 17)
case 8:
return append(dst, 0xd7, 17)
case 16:
return append(dst, 0xd8, 17)
}
return append(dst, 0xc7, byte(wrapped), 17)
} else if wrapped <= math.MaxUint16 {
return append(dst, 0xc8, byte(wrapped>>8), byte(wrapped), 17)
}
return append(dst, 0xc9, byte(wrapped>>24), byte(wrapped>>16), byte(wrapped>>8), byte(wrapped), 17)
}