-
Notifications
You must be signed in to change notification settings - Fork 1
/
keys.go
339 lines (323 loc) · 9.25 KB
/
keys.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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
package bstore
import (
"encoding/binary"
"fmt"
"math"
"reflect"
"time"
)
/*
The records buckets map a primary key to the record data. The primary key is of
a form that we can scan/range over. So fixed width for integers. For strings and
bytes they are just their byte representation. We do not store the PK in the
record data. This means we cannot store a time.Time as primary key, because we
cannot have the timezone encoded for comparison reasons.
Index keys are similar to PK's. Unique and non-unique indices are encoded the
same. The stored values are always empty, the key consists of the field values
the index was created for, followed by the PK. The encoding of a field is nearly
the same as the encoding of that type as a primary key. The differences: strings
end with a \0 to make them self-delimiting; byte slices are not allowed because
they are not self-delimiting; time.Time is allowed because the time is available
in full (with timezone) in the record data.
*/
// packPK returns the PK bytes representation for the PK value rv.
func packPK(rv reflect.Value) ([]byte, error) {
kv := rv.Interface()
var buf []byte
switch k := kv.(type) {
case string:
buf = []byte(k)
case []byte:
buf = k
case bool:
var b byte
if k {
b = 1
}
buf = []byte{b}
case int8:
buf = []byte{byte(uint8(k + math.MinInt8))}
case int16:
buf = binary.BigEndian.AppendUint16(nil, uint16(k+math.MinInt16))
case int32:
buf = binary.BigEndian.AppendUint32(nil, uint32(k+math.MinInt32))
case int:
if k < math.MinInt32 || k > math.MaxInt32 {
return nil, fmt.Errorf("%w: int %d does not fit in int32", ErrParam, k)
}
buf = binary.BigEndian.AppendUint32(nil, uint32(k+math.MinInt32))
case int64:
buf = binary.BigEndian.AppendUint64(nil, uint64(k+math.MinInt64))
case uint8:
buf = []byte{k}
case uint16:
buf = binary.BigEndian.AppendUint16(nil, k)
case uint32:
buf = binary.BigEndian.AppendUint32(nil, k)
case uint:
if k > math.MaxUint32 {
return nil, fmt.Errorf("%w: uint %d does not fit in uint32", ErrParam, k)
}
buf = binary.BigEndian.AppendUint32(nil, uint32(k))
case uint64:
buf = binary.BigEndian.AppendUint64(nil, k)
default:
return nil, fmt.Errorf("%w: unsupported primary key type %T", ErrType, kv)
}
return buf, nil
}
// parsePK parses primary key bk into rv.
func parsePK(rv reflect.Value, bk []byte) error {
k, err := typeKind(rv.Type())
if err != nil {
return err
}
switch k {
case kindBytes:
buf := make([]byte, len(bk))
copy(buf, bk)
rv.SetBytes(buf)
return nil
case kindString:
rv.SetString(string(bk))
return nil
}
var need int
switch k {
case kindBool, kindInt8, kindUint8:
need = 1
case kindInt16, kindUint16:
need = 2
case kindInt32, kindUint32, kindInt, kindUint:
need = 4
case kindInt64, kindUint64:
need = 8
}
if len(bk) != need {
return fmt.Errorf("%w: got %d bytes for PK, need %d", ErrStore, len(bk), need)
}
switch k {
case kindBool:
rv.SetBool(bk[0] != 0)
case kindInt8:
rv.SetInt(int64(int8(bk[0]) - math.MinInt8))
case kindInt16:
rv.SetInt(int64(int16(binary.BigEndian.Uint16(bk)) - math.MinInt16))
case kindInt32, kindInt:
rv.SetInt(int64(int32(binary.BigEndian.Uint32(bk)) - math.MinInt32))
case kindInt64:
rv.SetInt(int64(int64(binary.BigEndian.Uint64(bk)) - math.MinInt64))
case kindUint8:
rv.SetUint(uint64(bk[0]))
case kindUint16:
rv.SetUint(uint64(binary.BigEndian.Uint16(bk)))
case kindUint32, kindUint:
rv.SetUint(uint64(binary.BigEndian.Uint32(bk)))
case kindUint64:
rv.SetUint(uint64(binary.BigEndian.Uint64(bk)))
default:
// note: we cannot have kindTime as primary key at the moment.
return fmt.Errorf("%w: unsupported primary key type %v", ErrType, rv.Type())
}
return nil
}
// parseKey parses the PK (last element) of an index key.
// If all is set, also gathers the values before and returns them in the second
// parameter.
// If witnull is set, string values will get their ending \0 included.
func (idx *index) parseKey(buf []byte, all bool, withnull bool) ([]byte, [][]byte, error) {
var err error
var keys [][]byte
take := func(n int) {
if len(buf) < n {
err = fmt.Errorf("%w: not enough bytes in index key", ErrStore)
return
}
if all {
keys = append(keys, buf[:n])
}
buf = buf[n:]
}
fields:
for _, f := range idx.Fields {
if err != nil {
break
}
ft := f.Type
if ft.Kind == kindSlice {
// For an index on a slice, we store each value in the slice in a separate index key.
ft = *ft.ListElem
}
switch ft.Kind {
case kindString:
for i, b := range buf {
if b == 0 {
if all {
o := i
if withnull {
o++
}
keys = append(keys, buf[:o])
}
buf = buf[i+1:]
continue fields
}
}
err = fmt.Errorf("%w: bad string without 0 in index key", ErrStore)
case kindBool:
take(1)
case kindInt8, kindUint8:
take(1)
case kindInt16, kindUint16:
take(2)
case kindInt32, kindUint32, kindInt, kindUint:
take(4)
case kindInt64, kindUint64:
take(8)
case kindTime:
take(8 + 4)
default:
err = fmt.Errorf("%w: unhandled kind %v for index key", ErrStore, ft.Kind)
}
}
if err != nil {
return nil, nil, err
}
pk := buf
switch idx.tv.Fields[0].Type.Kind {
case kindBool:
take(1)
case kindInt8, kindUint8:
take(1)
case kindInt16, kindUint16:
take(2)
case kindInt32, kindInt, kindUint32, kindUint:
take(4)
case kindInt64, kindUint64:
take(8)
}
if len(pk) != len(buf) && len(buf) != 0 {
return nil, nil, fmt.Errorf("%w: leftover bytes in index key (%x)", ErrStore, buf)
}
if all {
return pk, keys[:len(keys)-1], nil
}
return pk, nil, nil
}
type indexkey struct {
pre []byte // Packed fields excluding PK, a slice of full.
full []byte // Packed fields including PK.
}
// packKey returns keys to store in an index: first the key prefixes without pk, then
// the prefixes including pk.
func (idx *index) packKey(rv reflect.Value, pk []byte) ([]indexkey, error) {
var l []reflect.Value
for _, f := range idx.Fields {
frv := rv.FieldByIndex(f.structField.Index)
l = append(l, frv)
}
return packIndexKeys(l, pk)
}
// packIndexKeys packs values from l, followed by the pk.
// It returns the key prefixes (without pk), and full keys with pk.
func packIndexKeys(l []reflect.Value, pk []byte) ([]indexkey, error) {
ikl := []indexkey{{}}
for _, frv := range l {
bufs, err := packIndexKey(frv)
if err != nil {
return nil, err
}
if len(bufs) == 1 {
for i := range ikl {
ikl[i].full = append(ikl[i].full, bufs[0]...)
}
} else if len(ikl) == 1 && len(bufs) > 1 {
nikl := make([]indexkey, len(bufs))
for i, buf := range bufs {
nikl[i] = indexkey{full: append(append([]byte{}, ikl[0].full...), buf...)}
}
ikl = nikl
} else if len(bufs) == 0 {
return nil, nil
} else {
return nil, fmt.Errorf("%w: multiple index fields that result in multiple values, or no data for index key, %d keys so far, %d new buffers", ErrStore, len(ikl), len(bufs))
}
}
for i := range ikl {
n := len(ikl[i].full)
ikl[i].full = append(ikl[i].full, pk...)
ikl[i].pre = ikl[i].full[:n]
}
return ikl, nil
}
func packIndexKey(frv reflect.Value) ([][]byte, error) {
k, err := typeKind(frv.Type())
if err != nil {
return nil, err
}
var buf []byte
switch k {
case kindBool:
buf = []byte{0}
if frv.Bool() {
buf[0] = 1
}
case kindInt8:
buf = []byte{byte(int8(frv.Int()) + math.MinInt8)}
case kindInt16:
buf = binary.BigEndian.AppendUint16(nil, uint16(int16(frv.Int())+math.MinInt16))
case kindInt32:
buf = binary.BigEndian.AppendUint32(nil, uint32(int32(frv.Int())+math.MinInt32))
case kindInt:
i := frv.Int()
if i < math.MinInt32 || i > math.MaxInt32 {
return nil, fmt.Errorf("%w: int value %d does not fit in int32", ErrParam, i)
}
buf = binary.BigEndian.AppendUint32(nil, uint32(int32(i)+math.MinInt32))
case kindInt64:
buf = binary.BigEndian.AppendUint64(nil, uint64(frv.Int()+math.MinInt64))
case kindUint8:
buf = []byte{byte(frv.Uint())}
case kindUint16:
buf = binary.BigEndian.AppendUint16(nil, uint16(frv.Uint()))
case kindUint32:
buf = binary.BigEndian.AppendUint32(nil, uint32(frv.Uint()))
case kindUint:
i := frv.Uint()
if i > math.MaxUint32 {
return nil, fmt.Errorf("%w: uint value %d does not fit in uint32", ErrParam, i)
}
buf = binary.BigEndian.AppendUint32(nil, uint32(i))
case kindUint64:
buf = binary.BigEndian.AppendUint64(nil, uint64(frv.Uint()))
case kindString:
buf = []byte(frv.String())
for _, c := range buf {
if c == 0 {
return nil, fmt.Errorf("%w: string used as index key cannot have \\0", ErrParam)
}
}
buf = append(buf, 0)
case kindTime:
tm := frv.Interface().(time.Time)
buf = binary.BigEndian.AppendUint64(nil, uint64(tm.Unix()+math.MinInt64))
buf = binary.BigEndian.AppendUint32(buf, uint32(tm.Nanosecond()))
case kindSlice:
n := frv.Len()
bufs := make([][]byte, n)
for i := 0; i < n; i++ {
nbufs, err := packIndexKey(frv.Index(i))
if err != nil {
return nil, fmt.Errorf("packing element from slice field: %w", err)
}
if len(nbufs) != 1 {
return nil, fmt.Errorf("packing element from slice field resulted in multiple buffers (%d)", len(bufs))
}
bufs[i] = nbufs[0]
}
return bufs, nil
default:
return nil, fmt.Errorf("internal error: bad type %v for index", frv.Type()) // todo: should be caught when making index type
}
return [][]byte{buf}, nil
}