This repository has been archived by the owner on Aug 2, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 84
/
table.go
221 lines (198 loc) · 5.17 KB
/
table.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
//func (x *Table) Next(key Value) (Value, Value) {}
//func (x *Table) Length() int {}
//func (x *Table) Append(value Value) {}
//func (x *Table) Insert(index int, value Value) {}
//func (x *Table) Remove(index int) (value Value) {}
//func (x *Table) RawGet(key Value) (value Value) {}
//func (x *Table) RawGetInt(key int) (value Value) {}
//func (x *Table) RawGetKey(key Value) (value Value) {}
//func (x *Table) RawGetStr(key string) (value Value) {}
//func (x *Table) RawSet(key, value Value) {}
//func (x *Table) RawSetKey(key, value Value) {}
//func (x *Table) RawSetInt(key int, value Value) {}
//func (x *Table) RawSetStr(key string, value Value) {}
package lua
import (
"fmt"
"math"
)
// Implementation of tables (aka arrays, objects, or hash tables). Tables keep
// its elements in two parts: an array part and a hash part. Non-negative integer
// keys are all candidates to be kept in the array part. The actual size of the
// array is the largest 'n' such that more than half the slots between 1 and n
// are in use. Hash uses a mix of chained scatter table with Brent's variation.
// A main invariant of these tables is that, if an element is not in its main
// position (i.e. the 'original' position that its hash gives to it), then the
// colliding element is in its own main position. Hence even when the load factor
// reaches 100%, performance remains good.
type table struct {
// thread state
state *State
// table state
hash map[Value]Value
list []Value
meta *table
// iterator state
iter []Value
keys map[Value]int
}
func (x *table) String() string { return fmt.Sprintf("table: %p", x) }
func (x *table) Length() int { return len(x.list) }
func (x *table) Type() Type { return TableType }
func (x *table) ForEach(fn func(Value, Value)) {
if x.list != nil {
for i, v := range x.list {
fn(Int(i), v)
}
}
if x.hash != nil {
for k, v := range x.hash {
fn(k, v)
}
}
}
func (x *table) Index(index Value) Value {
return x.get(index)
}
// newtable returns a new table initialized using the provided sizes
// arrayN and hashN to create the underlying hash and array part.
func newTable(state *State, arrayN, hashN int) *table {
t := table{state: state}
if arrayN > 0 {
t.list = make([]Value, arrayN)
}
if hashN > 0 {
t.hash = make(map[Value]Value, hashN)
} else {
t.hash = make(map[Value]Value)
}
return &t
}
func (t *table) set(k, v Value) {
if IsNone(k) {
return
}
if n, ok := k.(Number); ok {
i := arrayIndex(n) - 1
if i >= 0 && i < len(t.list) {
t.list[i] = v
return
}
if i == len(t.list) {
t.list = append(t.list, v)
return
}
// TODO: resize & rehash
}
if IsNone(v) {
delete(t.hash, k)
return
}
t.hash[k] = v
}
func (t *table) get(k Value) Value {
if IsNone(k) {
return None
}
if n, ok := k.(Number); ok {
i := arrayIndex(n) - 1
// fmt.Printf("table[%v] (%T) @ %d\n", k, k, i)
if i >= 0 && i < len(t.list) {
return t.list[i]
}
}
if v, ok := t.hash[k]; ok {
return v
}
return None
}
func (t *table) getStr(key string) Value {
return t.get(String(key))
}
func (t *table) getInt(key int64) Value {
return t.get(Int(key))
}
func (t *table) setStr(key string, value Value) {
t.set(String(key), value)
}
func (t *table) setInt(key int64, value Value) {
t.set(Int(key), value)
}
func (t *table) exists(key Value) bool {
return !IsNone(t.get(key))
}
func (t *table) length() int {
return len(t.list)
}
func (t *table) next(key Value) (k, v Value, more bool) {
if IsNone(key) || t.keys == nil { // first iteration?
t.keys = make(map[Value]int, len(t.hash))
t.iter = make([]Value, 0, len(t.hash))
for k := range t.hash {
t.iter = append(t.iter, k)
t.keys[k] = len(t.iter) - 1
}
}
if index := t.iterKey(key); index < len(t.list) {
for index++; index <= len(t.list); index++ {
if !IsNone(t.list[index-1]) {
k = Int(index)
v = t.list[index-1]
return k, v, true
}
}
} else {
if index = index - len(t.list); index < len(t.iter) {
k := t.iter[index]
v := t.hash[k]
return k, v, true
}
}
// Key did not exist or iteration ended.
t.iter = nil
t.keys = nil
return None, None, false
}
// iterKey returns the index of a 'key' for table traversals. First goes
// all elements in the array part, then elements in the hash part. The
// beginning of a traversal is signaled by 0.
func (t *table) iterKey(key Value) (index int) {
if IsNone(key) {
return 0
} // first iteration?
index = arrayIndex(key)
if index != 0 && index <= len(t.list) { // key in array?
return index // found index
}
// otherwise key is in hash part.
var found bool
if index, found = t.keys[key]; !found {
panic(runtimeErr(fmt.Errorf("invalid key (%v) to 'next'", key)))
}
// hash elements are numbered after array ones.
return index + 1 + len(t.list)
}
const maxInt = int(^uint(0) >> 1)
func arrayIndex(val Value) int {
switch val := val.(type) {
case Float:
if x, ok := float2int(float64(val)); ok {
return x
}
case Int:
if x := int(val); x > 0 && x < maxInt {
return x
}
}
return 0
}
func float2int(f64 float64) (int, bool) {
if math.IsInf(f64, 0) || math.IsNaN(f64) {
return 0, false
} else {
if i64 := int64(f64); float64(i64) == f64 {
return int(i64), true
}
return 0, false
}
}