forked from go-avro/avro
-
Notifications
You must be signed in to change notification settings - Fork 0
/
encoder.go
190 lines (154 loc) · 4.81 KB
/
encoder.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
package avro
import (
"encoding/binary"
"io"
"math"
)
// Encoder is an interface that provides low-level support for serializing Avro values.
type Encoder interface {
// Writes a null value. Doesn't actually do anything but may advance the state of Encoder implementation if it
// is stateful.
WriteNull(interface{})
// Writes a boolean value.
WriteBoolean(bool)
// Writes an int value.
WriteInt(int32)
// Writes a long value.
WriteLong(int64)
// Writes a float value.
WriteFloat(float32)
// Writes a double value.
WriteDouble(float64)
// Writes a bytes value.
WriteBytes([]byte)
// Writes a string value.
WriteString(string)
// WriteArrayStart should be called when starting to serialize an array providing it with a number of items in
// array block.
WriteArrayStart(int64)
// WriteArrayNext should be called after finishing writing an array block either passing it the number of items in
// next block or 0 indicating the end of array.
WriteArrayNext(int64)
// WriteMapStart should be called when starting to serialize a map providing it with a number of items in
// map block.
WriteMapStart(int64)
// WriteMapNext should be called after finishing writing a map block either passing it the number of items in
// next block or 0 indicating the end of map.
WriteMapNext(int64)
// Writes raw bytes to this Encoder.
WriteRaw([]byte)
}
// BinaryEncoder implements Encoder and provides low-level support for serializing Avro values.
type binaryEncoder struct {
buffer io.Writer
}
// NewBinaryEncoder creates a new BinaryEncoder that will write to a given io.Writer.
func NewBinaryEncoder(buffer io.Writer) Encoder {
return newBinaryEncoder(buffer)
}
func newBinaryEncoder(buffer io.Writer) *binaryEncoder {
return &binaryEncoder{buffer: buffer}
}
// WriteNull writes a null value. Doesn't actually do anything in this implementation.
func (be *binaryEncoder) WriteNull(_ interface{}) {
//do nothing
}
// The encodings of true and false, for reuse
var encBoolTrue = []byte{0x01}
var encBoolFalse = []byte{0x00}
// WriteBoolean writes a boolean value.
func (be *binaryEncoder) WriteBoolean(x bool) {
if x {
_, _ = be.buffer.Write(encBoolTrue)
} else {
_, _ = be.buffer.Write(encBoolFalse)
}
}
// WriteInt writes an int value.
func (be *binaryEncoder) WriteInt(x int32) {
_, _ = be.buffer.Write(be.encodeVarint32(x))
}
// WriteLong writes a long value.
func (be *binaryEncoder) WriteLong(x int64) {
_, _ = be.buffer.Write(be.encodeVarint64(x))
}
// WriteFloat writes a float value.
func (be *binaryEncoder) WriteFloat(x float32) {
bytes := make([]byte, 4)
binary.LittleEndian.PutUint32(bytes, math.Float32bits(x))
_, _ = be.buffer.Write(bytes)
}
// WriteDouble writes a double value.
func (be *binaryEncoder) WriteDouble(x float64) {
bytes := make([]byte, 8)
binary.LittleEndian.PutUint64(bytes, math.Float64bits(x))
_, _ = be.buffer.Write(bytes)
}
// WriteRaw writes raw bytes to this Encoder.
func (be *binaryEncoder) WriteRaw(x []byte) {
_, _ = be.buffer.Write(x)
}
// WriteBytes writes a bytes value.
func (be *binaryEncoder) WriteBytes(x []byte) {
be.WriteLong(int64(len(x)))
_, _ = be.buffer.Write(x)
}
// WriteString writes a string value.
func (be *binaryEncoder) WriteString(x string) {
be.WriteLong(int64(len(x)))
// call writers that happen to provide WriteString to avoid extra byte allocations for a copy of a string when possible.
_, _ = io.WriteString(be.buffer, x)
}
// WriteArrayStart should be called when starting to serialize an array providing it with a number of items in
// array block.
func (be *binaryEncoder) WriteArrayStart(count int64) {
be.writeItemCount(count)
}
// WriteArrayNext should be called after finishing writing an array block either passing it the number of items in
// next block or 0 indicating the end of array.
func (be *binaryEncoder) WriteArrayNext(count int64) {
be.writeItemCount(count)
}
// WriteMapStart should be called when starting to serialize a map providing it with a number of items in
// map block.
func (be *binaryEncoder) WriteMapStart(count int64) {
be.writeItemCount(count)
}
// WriteMapNext should be called after finishing writing a map block either passing it the number of items in
// next block or 0 indicating the end of map.
func (be *binaryEncoder) WriteMapNext(count int64) {
be.writeItemCount(count)
}
func (be *binaryEncoder) writeItemCount(count int64) {
be.WriteLong(count)
}
func (be *binaryEncoder) encodeVarint32(n int32) []byte {
var buf [5]byte
ux := uint32(n) << 1
if n < 0 {
ux = ^ux
}
i := 0
for ux >= 0x80 {
buf[i] = byte(ux) | 0x80
ux >>= 7
i++
}
buf[i] = byte(ux)
return buf[0 : i+1]
}
func (be *binaryEncoder) encodeVarint64(x int64) []byte {
var buf [10]byte
ux := uint64(x) << 1
if x < 0 {
ux = ^ux
}
i := 0
for ux >= 0x80 {
buf[i] = byte(ux) | 0x80
ux >>= 7
i++
}
buf[i] = byte(ux)
return buf[0 : i+1]
}