-
Notifications
You must be signed in to change notification settings - Fork 5
/
keys.go
202 lines (162 loc) · 3.99 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
package tormenta
import (
"bytes"
"encoding/binary"
"strings"
"github.com/jpincas/gouuidv6"
)
const (
contentKeyPrefix = "c"
indexKeyPrefix = "i"
indexKeySeparator = "."
keySeparator = "~±^"
)
type key struct {
isIndex bool
entityType []byte
id gouuidv6.UUID
indexName []byte
indexContent []byte
exactMatch bool
}
// newContentKey returns a key of the correct type
func newContentKey(root []byte, id ...gouuidv6.UUID) key {
return withID(key{
isIndex: false,
entityType: root,
}, id)
}
func newIndexKey(root, indexName, indexContent []byte, id ...gouuidv6.UUID) key {
return withID(key{
isIndex: true,
entityType: root,
indexName: indexName,
indexContent: indexContent,
}, id)
}
func nestedIndexKeyRoot(base, next []byte) []byte {
return bytes.Join([][]byte{base, next}, []byte(indexKeySeparator))
}
func newIndexMatchKey(root, indexName, indexContent []byte, id ...gouuidv6.UUID) key {
return withID(key{
isIndex: true,
exactMatch: true,
entityType: root,
indexName: indexName,
indexContent: indexContent,
}, id)
}
func withID(k key, id []gouuidv6.UUID) key {
// If an ID is specified
if len(id) > 0 {
k.id = id[0]
}
return k
}
func (k key) shouldAppendID() bool {
// If ID is nil, definite no
if k.id.IsNil() {
return false
}
// For non-index keys, do append
if !k.isIndex {
return true
}
// For index keys using exact matching, do append
if k.exactMatch {
return true
}
return false
}
// c:fullStructs:sdfdsf-9sdfsdf-8dsf-sdf-9sdfsdf
// i:fullStructs:Department:3
// i:fullStructs:Department:3:sdfdsf-9sdfsdf-8dsf-sdf-9sdfsdf
func (k key) bytes() []byte {
// Use either content/index key prefix
identifierPrefix := []byte(contentKeyPrefix)
if k.isIndex {
identifierPrefix = []byte(indexKeyPrefix)
}
// Always start with identifier prefix and entity type
toJoin := [][]byte{identifierPrefix, k.entityType}
// For index keys, now append index name and content
if k.isIndex {
toJoin = append(toJoin, k.indexName, k.indexContent)
}
if k.shouldAppendID() {
toJoin = append(toJoin, k.id.Bytes())
}
return bytes.Join(toJoin, []byte(keySeparator))
}
func extractID(b []byte) (uuid gouuidv6.UUID) {
s := bytes.Split(b, []byte(keySeparator))
idBytes := s[len(s)-1]
copy(uuid[:], idBytes)
return
}
func extractIndexValue(b []byte, i interface{}) {
s := bytes.Split(b, []byte(keySeparator))
indexValueBytes := s[3]
// For unsigned ints, we need to flip the sign bit back
switch i.(type) {
case *int, *int8, *int16, *int32, *int64:
flipInt(indexValueBytes)
case *float64, *float32:
revertFloat(indexValueBytes)
}
buf := bytes.NewBuffer(indexValueBytes)
binary.Read(buf, binary.BigEndian, i) //TODO: error handling
}
func stripID(b []byte) []byte {
s := bytes.Split(b, []byte(keySeparator))
return bytes.Join(s[:len(s)-1], []byte(keySeparator))
}
// compare compares two key-byte slices
func compareKeyBytes(a, b []byte, reverse bool, removeID bool) bool {
if removeID {
b = stripID(b)
}
var r int
if !reverse {
r = bytes.Compare(a, b)
} else {
r = bytes.Compare(b, a)
}
if r < 0 {
return true
}
return false
}
func keyIsOutsideDateRange(key, start, end gouuidv6.UUID) bool {
// No dates at all? Then its definitely not outside the range
if start.IsNil() && end.IsNil() {
return false
}
// For start date only
if end.IsNil() {
return key.Compare(start)
}
// For both start and end
return key.Compare(start) || !key.Compare(end)
}
// Key construction helpers
func KeyRoot(t interface{}) []byte {
k, _ := entityTypeAndValue(t)
return k
}
func KeyRootString(entity Record) string {
return string(KeyRoot(entity))
}
func typeToKeyRoot(typeSig string) []byte {
return []byte(strings.ToLower(cleanType(typeSig)))
}
func typeToIndexString(typeSig string) []byte {
return []byte(cleanType(typeSig))
}
func cleanType(typeSig string) string {
sp := strings.Split(typeSig, ".")
s := sp[len(sp)-1]
s = strings.TrimPrefix(s, "*")
s = strings.TrimPrefix(s, "[]")
return s
}