-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtable.go
More file actions
168 lines (148 loc) · 3.66 KB
/
Copy pathtable.go
File metadata and controls
168 lines (148 loc) · 3.66 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
package main
import (
"encoding/binary"
)
const (
TYPE_BYTES = 1 // string / byte array
TYPE_INT64 = 2 // 64-bit signed integer
)
// Value represents a single cell in a table
type Value struct {
Type uint32
I64 int64
Str []byte
}
// Record represents a complete row
type Record struct {
Cols []string
Vals []Value
}
// AddStr is a helper to easily build a row
func (rec *Record) AddStr(col string, val []byte) *Record {
rec.Cols = append(rec.Cols, col)
rec.Vals = append(rec.Vals, Value{Type: TYPE_BYTES, Str: val})
return rec
}
// AddInt64 is a helper to easily build a row
func (rec *Record) AddInt64(col string, val int64) *Record {
rec.Cols = append(rec.Cols, col)
rec.Vals = append(rec.Vals, Value{Type: TYPE_INT64, I64: val})
return rec
}
// Get fetches a specific column from the row
func (rec *Record) Get(col string) *Value {
for i, c := range rec.Cols {
if c == col {
return &rec.Vals[i]
}
}
return nil
}
// TableDef represents the schema of a table
type TableDef struct {
Name string
Types []uint32
Cols []string
Indexes [][]string // Indexes[0] is always the primary key
Prefixes []uint32 // Auto-assigned prefixes for each index
}
var TDEF_TABLE = &TableDef{
Name: "@table",
Types: []uint32{TYPE_BYTES, TYPE_BYTES},
Cols: []string{"name", "def"},
Indexes: [][]string{{"name"}},
Prefixes: []uint32{2},
}
var TDEF_META = &TableDef{
Name: "@meta",
Types: []uint32{TYPE_BYTES, TYPE_BYTES},
Cols: []string{"key", "val"},
Indexes: [][]string{{"key"}},
Prefixes: []uint32{1},
}
// --- ORDER-PRESERVING STRING ESCAPING ---
func escapeString(in []byte) []byte {
var out []byte
for _, b := range in {
if b == 0x00 {
out = append(out, 0x01, 0x01)
} else if b == 0x01 {
out = append(out, 0x01, 0x02)
} else {
out = append(out, b)
}
}
out = append(out, 0x00)
return out
}
func unescapeString(in []byte) ([]byte, []byte) {
var out []byte
for i := 0; i < len(in); i++ {
if in[i] == 0x00 {
return out, in[i+1:]
}
if in[i] == 0x01 {
i++
if in[i] == 0x01 {
out = append(out, 0x00)
} else if in[i] == 0x02 {
out = append(out, 0x01)
} else {
panic("bad escape sequence")
}
} else {
out = append(out, in[i])
}
}
panic("missing null terminator in encoded string")
}
// --- ORDER-PRESERVING ENCODERS ---
func encodeValues(out []byte, vals []Value) []byte {
for _, v := range vals {
out = append(out, byte(v.Type))
if v.Type == TYPE_INT64 {
var buf [8]byte
u := uint64(v.I64) + (1 << 63)
binary.BigEndian.PutUint64(buf[:], u)
out = append(out, buf[:]...)
} else if v.Type == TYPE_BYTES {
out = append(out, escapeString(v.Str)...)
} else {
panic("unsupported type")
}
}
return out
}
// decodeValues unpacks a raw byte slice back into a list of Value structs
func decodeValues(in []byte, out []Value) {
for i := range out {
out[i].Type = uint32(in[0])
in = in[1:]
if out[i].Type == TYPE_INT64 {
u := binary.BigEndian.Uint64(in[:8])
out[i].I64 = int64(u - (1 << 63))
in = in[8:]
} else if out[i].Type == TYPE_BYTES {
str, rest := unescapeString(in)
out[i].Str = str
in = rest
} else {
panic("unsupported type")
}
}
}
func encodeKey(out []byte, prefix uint32, vals []Value) []byte {
var buf [4]byte
binary.BigEndian.PutUint32(buf[:], prefix)
out = append(out, buf[:]...)
out = encodeValues(out, vals)
return out
}
// encodeKeyPartial handles missing columns for open-ended range queries
func encodeKeyPartial(out []byte, prefix uint32, vals []Value, cmp int) []byte {
out = encodeKey(out, prefix, vals)
if cmp == CMP_GT || cmp == CMP_LE {
out = append(out, 0xff) // Encode missing columns as +infinity
}
return out
}