-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Expand file tree
/
Copy pathboxed.go
More file actions
378 lines (329 loc) · 9.29 KB
/
boxed.go
File metadata and controls
378 lines (329 loc) · 9.29 KB
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
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
package tuple
import (
"encoding/binary"
"math"
"unsafe"
"github.com/google/uuid"
)
// A boxedType represents the value contained in a Boxed.
type boxedType byte
const (
// Zero value
boxedUnknown boxedType = iota
// A control operator. The data being 1 = start of nested tuple, 2 = end of tuple.
boxedCtrl
// A boxed nil value.
boxedNil
// A boxed boolean value. The data being 1 = true, 0 = false.
boxedBool
// A boxed byte string. The pointer contains the slice start, and the data contains the length.
boxedBytes
// A boxed UTF-8 string. The pointer contains the slice start, and the data contains the length.
boxedString
// A boxed nested tuple. The pointer contains the slice start, and the data contains the length.
boxedTuple
// A boxed fixed-length byte string. The pointer contains the slice start, and the data contains the length.
boxedFixedLen
// A boxed int64. The data contains the value.
boxedInt64
// A boxed uint64. The data contains the value.
boxedUint64
// A boxed float32. The data contains the float32's bits in the lower 32 bits.
boxedFloat32
// A boxed float64. The data contains the float64's bits.
boxedFloat64
// A boxed UUID. The pointer points to the start of the 16 bytes of the UUID.
boxedUUID
// A boxed 12-byte versionstamp. The pointer points to the start of the 12 bytes
// of the versionstamp.
boxedVersionstamp
)
// A Boxed is a tagged union representing a tuple value that can be
// unboxed to retrieve the content.
//
// A boxed containing an integer can be cast to any integer type as long
// as it does not overflow. When using Unbox(), int64 is preferred over
// uint64.
type Boxed struct {
// The type contained in the box.
bt boxedType
// The pointer in the box.
ptr unsafe.Pointer
// The data in the box, or the length of the data.
data uint64
}
// A BoxedTuple is a tuple with values represented as Boxed.
type BoxedTuple []Boxed
func newBoxedNil() Boxed {
return Boxed{bt: boxedNil}
}
func newBoxedBool(b bool) Boxed {
val := Boxed{bt: boxedBool, data: 0}
if b {
val.data = 1
}
return val
}
func newBoxedBytes(b []byte) Boxed {
val := Boxed{bt: boxedBytes, ptr: unsafe.Pointer(unsafe.SliceData(b)), data: uint64(len(b))}
return val
}
func newBoxedFixedLen(b []byte) Boxed {
val := Boxed{bt: boxedFixedLen, ptr: unsafe.Pointer(unsafe.SliceData(b)), data: uint64(len(b))}
return val
}
func newBoxedTuple(b BoxedTuple) Boxed {
val := Boxed{bt: boxedTuple, ptr: unsafe.Pointer(unsafe.SliceData(b)), data: uint64(len(b))}
return val
}
func newBoxedUUID(b []byte) Boxed {
val := Boxed{bt: boxedUUID, ptr: unsafe.Pointer(unsafe.SliceData(b)), data: 16}
return val
}
func newBoxedVersionstamp(b []byte) Boxed {
val := Boxed{bt: boxedVersionstamp, ptr: unsafe.Pointer(unsafe.SliceData(b)), data: 12}
return val
}
func newBoxedString(b []byte) Boxed {
val := Boxed{bt: boxedString, ptr: unsafe.Pointer(unsafe.SliceData(b)), data: uint64(len(b))}
return val
}
func newBoxedInt64(b int64) Boxed {
return Boxed{bt: boxedInt64, data: uint64(b)}
}
func newBoxedUInt64(b uint64) Boxed {
return Boxed{bt: boxedInt64, data: b}
}
func newBoxedFloat64(b float64) Boxed {
return Boxed{bt: boxedFloat64, data: math.Float64bits(b)}
}
func newBoxedFloat32(b float32) Boxed {
return Boxed{bt: boxedFloat32, data: uint64(math.Float32bits(b))}
}
func (b Boxed) assert(ok bool, msg string) {
if !ok {
panic(msg)
}
}
// Checks if the given Boxed contains a nil value.
func (b Boxed) IsNil() bool {
return b.bt == boxedNil
}
// Tries to cast the Boxed to a boolean, and returns the
// value and if the cast succeded.
func (b Boxed) SafeBool() (bool, bool) {
if b.bt != boxedBool {
return false, false
}
return b.data != 0, true
}
// Tries to cast the Boxed to a boolean and panics if it fails.
func (b Boxed) Bool() bool {
sb, ok := b.SafeBool()
b.assert(ok, "cannot cast to bool")
return sb
}
// Tries to cast the Boxed to a float64, and returns the
// cast value and if the cast succeded.
func (b Boxed) SafeFloat64() (float64, bool) {
if b.bt != boxedFloat64 {
return 0.0, false
}
return math.Float64frombits(b.data), true
}
// Tries to cast the Boxed to a float64 and panics if it fails.
func (b Boxed) Float64() float64 {
v, ok := b.SafeFloat64()
b.assert(ok, "cannot cast to float64")
return v
}
// Tries to cast the Boxed to a float32, and returns the
// cast value and if the cast succeded.
func (b Boxed) SafeFloat32() (float32, bool) {
if b.bt != boxedFloat32 {
return 0.0, false
}
return math.Float32frombits(uint32(b.data)), true
}
// Tries to cast the Boxed to a float32 and panics if it fails.
func (b Boxed) Float32() float32 {
v, ok := b.SafeFloat32()
b.assert(ok, "cannot cast to float32")
return v
}
// Tries to cast the Boxed to a BoxedTuple, and returns the
// cast value and if the cast succeded.
func (b Boxed) SafeTuple() (BoxedTuple, bool) {
if b.bt != boxedTuple {
return nil, false
}
return unsafe.Slice((*Boxed)(b.ptr), int(b.data)), true
}
// Tries to cast the Boxed to a Tuple and panics if it fails.
func (b Boxed) Tuple() BoxedTuple {
v, ok := b.SafeTuple()
b.assert(ok, "cannot cast to BoxedTuple")
return v
}
// Tries to cast the Boxed to a byte slice, and returns the
// cast value and if the cast succeded.
func (b Boxed) SafeBytes() ([]byte, bool) {
if b.bt != boxedBytes {
return nil, false
}
return unsafe.Slice((*byte)(b.ptr), int(b.data)), true
}
// Tries to cast the Boxed to a byte slice and panics if it fails.
func (b Boxed) Bytes() []byte {
v, ok := b.SafeBytes()
b.assert(ok, "cannot cast to []byte")
return v
}
// Tries to cast the Boxed to a string, and returns the
// cast value and if the cast succeded.
func (b Boxed) SafeString() (string, bool) {
if b.bt != boxedString {
return "", false
}
return unsafe.String((*byte)(b.ptr), int(b.data)), true
}
// Tries to cast the Boxed to a string and panics if it fails.
//
// This is not named String() to not conflict with the function used to
// cast to a string for printing.
func (b Boxed) AsString() string {
v, ok := b.SafeString()
b.assert(ok, "cannot cast to string")
return v
}
// Tries to cast the Boxed to a FixedLen, and returns the
// cast value and if the cast succeded.
func (b Boxed) SafeFixedLen() (FixedLen, bool) {
if b.bt != boxedFixedLen {
return nil, false
}
return unsafe.Slice((*byte)(b.ptr), int(b.data)), true
}
// Tries to cast the Boxed to a FixedLen and panics if it fails.
func (b Boxed) FixedLen() FixedLen {
v, ok := b.SafeFixedLen()
b.assert(ok, "cannot cast to FixedLen")
return v
}
// Tries to cast the Boxed to a UUID, and returns the
// cast value and if the cast succeded.
func (b Boxed) SafeUUID() (uuid.UUID, bool) {
if b.bt != boxedUUID {
return uuid.UUID{}, false
}
return uuid.UUID(unsafe.Slice((*byte)(b.ptr), 16)), true
}
// Tries to cast the Boxed to a UUID and panics if it fails.
func (b Boxed) UUID() uuid.UUID {
v, ok := b.SafeUUID()
b.assert(ok, "cannot cast to UUID")
return v
}
// Tries to cast the Boxed to a int64, and returns the
// cast value and if the cast succeded.
func (b Boxed) SafeInt64() (int64, bool) {
switch b.bt {
case boxedInt64:
return int64(b.data), true
case boxedUint64:
if b.data < 0x8000_0000_0000_0000 {
return int64(b.data), true
}
return 0, false
default:
return 0, false
}
}
// Tries to cast the Boxed to a int64 and panics if it fails.
func (b Boxed) Int64() int64 {
v, ok := b.SafeInt64()
b.assert(ok, "cannot cast to int64")
return v
}
// Tries to cast the Boxed to a Versionstamp, and returns the
// cast value and if the cast succeded.
func (b Boxed) SafeVersionstamp() (Versionstamp, bool) {
if b.bt != boxedVersionstamp {
return Versionstamp{}, false
}
slice := unsafe.Slice((*byte)(b.ptr), 12)
out := Versionstamp{}
out.TransactionVersion = [10]byte(slice[0:10])
out.UserVersion = binary.BigEndian.Uint16(slice[10:12])
return out, true
}
// Tries to cast the Boxed to a Versionstamp and panics if it fails.
func (b Boxed) Versionstamp() Versionstamp {
v, ok := b.SafeVersionstamp()
b.assert(ok, "cannot cast to Versionstamp")
return v
}
// Tries to cast the Boxed to a uint64, and returns the
// cast value and if the cast succeded.
func (b Boxed) SafeUint64() (uint64, bool) {
switch b.bt {
case boxedUint64:
return b.data, true
case boxedInt64:
if b.data < 0x8000_0000_0000_0000 {
return b.data, true
}
return 0, false
default:
return 0, false
}
}
// Tries to cast the Boxed to a uint64 and panics if it fails.
func (b Boxed) Uint64() uint64 {
v, ok := b.SafeUint64()
b.assert(ok, "cannot cast to uint64")
return v
}
// Unboxes the content of the Boxed to its value.
func (b Boxed) Unbox() any {
switch b.bt {
case boxedNil:
return nil
case boxedString:
return b.AsString()
case boxedBytes:
return b.Bytes()
case boxedFixedLen:
return b.FixedLen()
case boxedFloat32:
return b.Float32()
case boxedFloat64:
return b.Float64()
case boxedTuple:
return b.Tuple()
case boxedInt64:
return b.Int64()
case boxedUint64:
return b.Uint64()
case boxedBool:
return b.Bool()
case boxedUUID:
return b.UUID()
case boxedVersionstamp:
return b.Versionstamp()
default:
panic("unknown type")
}
}
// Converts the BoxedTuple to a normal Tuple.
func (bt BoxedTuple) ToTuple() Tuple {
out := make(Tuple, len(bt))
for i, entry := range bt {
if entry.bt == boxedTuple {
out[i] = entry.Tuple().ToTuple()
} else {
out[i] = entry.Unbox()
}
}
return out
}