/
serialize.go
206 lines (186 loc) · 4.72 KB
/
serialize.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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
package opentimestamps
import (
"bufio"
"bytes"
"fmt"
"io"
"math"
)
// serializationContext helps encoding values in the ots format
type serializationContext struct {
w io.Writer
}
// newSerializationContext returns a serializationContext for a writer
func newSerializationContext(w io.Writer) *serializationContext {
return &serializationContext{w}
}
// writeBytes writes the raw bytes to the underlying writer
func (s serializationContext) writeBytes(b []byte) error {
// number of bytes can be ignored
// if it is equal len(b) then err is nil
_, err := s.w.Write(b)
if err != nil {
return err
}
return nil
}
// writeByte writes a single byte
func (s serializationContext) writeByte(b byte) error {
return s.writeBytes([]byte{b})
}
// writeBool encodes and writes a boolean value
func (s serializationContext) writeBool(b bool) error {
if b {
return s.writeByte(0xff)
} else {
return s.writeByte(0x00)
}
}
// writeVarUint encodes and writes writes a variable-length integer
func (s serializationContext) writeVarUint(v uint64) error {
if v == 0 {
s.writeByte(0x00)
}
for v > 0 {
b := byte(v & 0x7f)
if v > uint64(0x7f) {
b |= 0x80
}
if err := s.writeByte(b); err != nil {
return err
}
if v <= 0x7f {
break
}
v >>= 7
}
return nil
}
// writeVarBytes encodes and writes a variable-length array
func (s serializationContext) writeVarBytes(arr []byte) error {
if err := s.writeVarUint(uint64(len(arr))); err != nil {
return err
}
return s.writeBytes(arr)
}
// deserializationContext helps decoding values from the ots format
type deserializationContext struct {
r io.Reader
}
// safety boundary for readBytes
// allocation limit for arrays
const maxReadSize = (1 << 12)
func (d deserializationContext) dump() string {
arr, _ := d.r.(*bufio.Reader).Peek(512)
return fmt.Sprintf("% x", arr)
}
// readBytes reads n bytes.
func (d deserializationContext) readBytes(n int) ([]byte, error) {
if n > maxReadSize {
return nil, fmt.Errorf("over maxReadSize: %d", maxReadSize)
}
b := make([]byte, n)
m, err := d.r.Read(b)
if err != nil {
return b, err
}
if n != m {
return b, fmt.Errorf("expected %d bytes, got %d", n, m)
}
return b[:], nil
}
// readByte reads a single byte.
func (d deserializationContext) readByte() (byte, error) {
arr, err := d.readBytes(1)
if err != nil {
return 0, err
}
return arr[0], nil
}
// readBool reads a boolean.
func (d deserializationContext) readBool() (bool, error) {
arr, err := d.readBytes(1)
if err != nil {
return false, err
}
switch v := arr[0]; v {
case 0x00:
return false, nil
case 0xff:
return true, nil
default:
return false, fmt.Errorf("unexpected value %x", v)
}
}
// readVarUint reads a variable-length uint64.
func (d deserializationContext) readVarUint() (uint64, error) {
// NOTE
// the original python implementation has no uint64 limit, but I
// don't think we'll ever need more that that.
val := uint64(0)
shift := uint(0)
for {
b, err := d.readByte()
if err != nil {
return 0, err
}
shifted := uint64(b&0x7f) << shift
// ghetto overflow check
if (shifted >> shift) != uint64(b&0x7f) {
return 0, fmt.Errorf("uint64 overflow")
}
val |= shifted
if b&0x80 == 0 {
return val, nil
}
shift += 7
}
}
// readVarBytes reads variable-length number of bytes.
func (d deserializationContext) readVarBytes(minLen, maxLen int) ([]byte, error) {
v, err := d.readVarUint()
if err != nil {
return nil, err
}
if v > math.MaxInt32 {
return nil, fmt.Errorf("int overflow")
}
vint := int(v)
if maxLen < vint || vint < minLen {
return nil, fmt.Errorf(
"varbytes length %d outside range (%d, %d)",
vint, minLen, maxLen,
)
}
return d.readBytes(vint)
}
// assertMagic removes reads the expected bytes from the stream. Returns an
// error if the bytes are unexpected.
func (d deserializationContext) assertMagic(expected []byte) error {
arr, err := d.readBytes(len(expected))
if err != nil {
return err
}
if !bytes.Equal(expected, arr) {
return fmt.Errorf(
"magic bytes mismatch, expected % x got % x",
expected, arr,
)
}
return nil
}
// assertEOF reads a byte and returns true if the end of the reader is reached.
// Careful: the read operation is a side-effect.
func (d deserializationContext) assertEOF() bool {
// Unfortunately we can't always do a zero-byte read here, since some
// reader implementations fail to return EOF. This means assertEOF
_, err := d.readByte()
return err == io.EOF
}
// newDeserializationContext returns a deserializationContext for a reader
func newDeserializationContext(r io.Reader) *deserializationContext {
// TODO
// bufio is used here to allow debugging via d.dump()
// once this code here is robust enough we can just pass r
return &deserializationContext{bufio.NewReader(r)}
}