1package lua
2
3const defaultArrayCap = 32
4const defaultHashCap = 32
5
6type lValueArraySorter struct {
7	L      *LState
8	Fn     *LFunction
9	Values []LValue
10}
11
12func (lv lValueArraySorter) Len() int {
13	return len(lv.Values)
14}
15
16func (lv lValueArraySorter) Swap(i, j int) {
17	lv.Values[i], lv.Values[j] = lv.Values[j], lv.Values[i]
18}
19
20func (lv lValueArraySorter) Less(i, j int) bool {
21	if lv.Fn != nil {
22		lv.L.Push(lv.Fn)
23		lv.L.Push(lv.Values[i])
24		lv.L.Push(lv.Values[j])
25		lv.L.Call(2, 1)
26		return LVAsBool(lv.L.reg.Pop())
27	}
28	return lessThan(lv.L, lv.Values[i], lv.Values[j])
29}
30
31func newLTable(acap int, hcap int) *LTable {
32	if acap < 0 {
33		acap = 0
34	}
35	if hcap < 0 {
36		hcap = 0
37	}
38	tb := &LTable{}
39	tb.Metatable = LNil
40	if acap != 0 {
41		tb.array = make([]LValue, 0, acap)
42	}
43	if hcap != 0 {
44		tb.strdict = make(map[string]LValue, hcap)
45	}
46	return tb
47}
48
49// Len returns length of this LTable.
50func (tb *LTable) Len() int {
51	if tb.array == nil {
52		return 0
53	}
54	var prev LValue = LNil
55	for i := len(tb.array) - 1; i >= 0; i-- {
56		v := tb.array[i]
57		if prev == LNil && v != LNil {
58			return i + 1
59		}
60		prev = v
61	}
62	return 0
63}
64
65// Append appends a given LValue to this LTable.
66func (tb *LTable) Append(value LValue) {
67	if value == LNil {
68		return
69	}
70	if tb.array == nil {
71		tb.array = make([]LValue, 0, defaultArrayCap)
72	}
73	if len(tb.array) == 0 || tb.array[len(tb.array)-1] != LNil {
74		tb.array = append(tb.array, value)
75	} else {
76		i := len(tb.array) - 2
77		for ; i >= 0; i-- {
78			if tb.array[i] != LNil {
79				break
80			}
81		}
82		tb.array[i+1] = value
83	}
84}
85
86// Insert inserts a given LValue at position `i` in this table.
87func (tb *LTable) Insert(i int, value LValue) {
88	if tb.array == nil {
89		tb.array = make([]LValue, 0, defaultArrayCap)
90	}
91	if i > len(tb.array) {
92		tb.RawSetInt(i, value)
93		return
94	}
95	if i <= 0 {
96		tb.RawSet(LNumber(i), value)
97		return
98	}
99	i -= 1
100	tb.array = append(tb.array, LNil)
101	copy(tb.array[i+1:], tb.array[i:])
102	tb.array[i] = value
103}
104
105// MaxN returns a maximum number key that nil value does not exist before it.
106func (tb *LTable) MaxN() int {
107	if tb.array == nil {
108		return 0
109	}
110	for i := len(tb.array) - 1; i >= 0; i-- {
111		if tb.array[i] != LNil {
112			return i + 1
113		}
114	}
115	return 0
116}
117
118// Remove removes from this table the element at a given position.
119func (tb *LTable) Remove(pos int) LValue {
120	if tb.array == nil {
121		return LNil
122	}
123	larray := len(tb.array)
124	if larray == 0 {
125		return LNil
126	}
127	i := pos - 1
128	oldval := LNil
129	switch {
130	case i >= larray:
131		// nothing to do
132	case i == larray-1 || i < 0:
133		oldval = tb.array[larray-1]
134		tb.array = tb.array[:larray-1]
135	default:
136		oldval = tb.array[i]
137		copy(tb.array[i:], tb.array[i+1:])
138		tb.array[larray-1] = nil
139		tb.array = tb.array[:larray-1]
140	}
141	return oldval
142}
143
144// RawSet sets a given LValue to a given index without the __newindex metamethod.
145// It is recommended to use `RawSetString` or `RawSetInt` for performance
146// if you already know the given LValue is a string or number.
147func (tb *LTable) RawSet(key LValue, value LValue) {
148	switch v := key.(type) {
149	case LNumber:
150		if isArrayKey(v) {
151			if tb.array == nil {
152				tb.array = make([]LValue, 0, defaultArrayCap)
153			}
154			index := int(v) - 1
155			alen := len(tb.array)
156			switch {
157			case index == alen:
158				tb.array = append(tb.array, value)
159			case index > alen:
160				for i := 0; i < (index - alen); i++ {
161					tb.array = append(tb.array, LNil)
162				}
163				tb.array = append(tb.array, value)
164			case index < alen:
165				tb.array[index] = value
166			}
167			return
168		}
169	case LString:
170		tb.RawSetString(string(v), value)
171		return
172	}
173
174	tb.RawSetH(key, value)
175}
176
177// RawSetInt sets a given LValue at a position `key` without the __newindex metamethod.
178func (tb *LTable) RawSetInt(key int, value LValue) {
179	if key < 1 || key >= MaxArrayIndex {
180		tb.RawSetH(LNumber(key), value)
181		return
182	}
183	if tb.array == nil {
184		tb.array = make([]LValue, 0, 32)
185	}
186	index := key - 1
187	alen := len(tb.array)
188	switch {
189	case index == alen:
190		tb.array = append(tb.array, value)
191	case index > alen:
192		for i := 0; i < (index - alen); i++ {
193			tb.array = append(tb.array, LNil)
194		}
195		tb.array = append(tb.array, value)
196	case index < alen:
197		tb.array[index] = value
198	}
199}
200
201// RawSetString sets a given LValue to a given string index without the __newindex metamethod.
202func (tb *LTable) RawSetString(key string, value LValue) {
203	if tb.strdict == nil {
204		tb.strdict = make(map[string]LValue, defaultHashCap)
205	}
206	if tb.keys == nil {
207		tb.keys = []LValue{}
208		tb.k2i = map[LValue]int{}
209	}
210
211	if value == LNil {
212		// TODO tb.keys and tb.k2i should also be removed
213		delete(tb.strdict, key)
214	} else {
215		tb.strdict[key] = value
216		lkey := LString(key)
217		if _, ok := tb.k2i[lkey]; !ok {
218			tb.k2i[lkey] = len(tb.keys)
219			tb.keys = append(tb.keys, lkey)
220		}
221	}
222}
223
224// RawSetH sets a given LValue to a given index without the __newindex metamethod.
225func (tb *LTable) RawSetH(key LValue, value LValue) {
226	if s, ok := key.(LString); ok {
227		tb.RawSetString(string(s), value)
228		return
229	}
230	if tb.dict == nil {
231		tb.dict = make(map[LValue]LValue, len(tb.strdict))
232	}
233	if tb.keys == nil {
234		tb.keys = []LValue{}
235		tb.k2i = map[LValue]int{}
236	}
237
238	if value == LNil {
239		// TODO tb.keys and tb.k2i should also be removed
240		delete(tb.dict, key)
241	} else {
242		tb.dict[key] = value
243		if _, ok := tb.k2i[key]; !ok {
244			tb.k2i[key] = len(tb.keys)
245			tb.keys = append(tb.keys, key)
246		}
247	}
248}
249
250// RawGet returns an LValue associated with a given key without __index metamethod.
251func (tb *LTable) RawGet(key LValue) LValue {
252	switch v := key.(type) {
253	case LNumber:
254		if isArrayKey(v) {
255			if tb.array == nil {
256				return LNil
257			}
258			index := int(v) - 1
259			if index >= len(tb.array) {
260				return LNil
261			}
262			return tb.array[index]
263		}
264	case LString:
265		if tb.strdict == nil {
266			return LNil
267		}
268		if ret, ok := tb.strdict[string(v)]; ok {
269			return ret
270		}
271		return LNil
272	}
273	if tb.dict == nil {
274		return LNil
275	}
276	if v, ok := tb.dict[key]; ok {
277		return v
278	}
279	return LNil
280}
281
282// RawGetInt returns an LValue at position `key` without __index metamethod.
283func (tb *LTable) RawGetInt(key int) LValue {
284	if tb.array == nil {
285		return LNil
286	}
287	index := int(key) - 1
288	if index >= len(tb.array) || index < 0 {
289		return LNil
290	}
291	return tb.array[index]
292}
293
294// RawGet returns an LValue associated with a given key without __index metamethod.
295func (tb *LTable) RawGetH(key LValue) LValue {
296	if s, sok := key.(LString); sok {
297		if tb.strdict == nil {
298			return LNil
299		}
300		if v, vok := tb.strdict[string(s)]; vok {
301			return v
302		}
303		return LNil
304	}
305	if tb.dict == nil {
306		return LNil
307	}
308	if v, ok := tb.dict[key]; ok {
309		return v
310	}
311	return LNil
312}
313
314// RawGetString returns an LValue associated with a given key without __index metamethod.
315func (tb *LTable) RawGetString(key string) LValue {
316	if tb.strdict == nil {
317		return LNil
318	}
319	if v, vok := tb.strdict[string(key)]; vok {
320		return v
321	}
322	return LNil
323}
324
325// ForEach iterates over this table of elements, yielding each in turn to a given function.
326func (tb *LTable) ForEach(cb func(LValue, LValue)) {
327	if tb.array != nil {
328		for i, v := range tb.array {
329			if v != LNil {
330				cb(LNumber(i+1), v)
331			}
332		}
333	}
334	if tb.strdict != nil {
335		for k, v := range tb.strdict {
336			if v != LNil {
337				cb(LString(k), v)
338			}
339		}
340	}
341	if tb.dict != nil {
342		for k, v := range tb.dict {
343			if v != LNil {
344				cb(k, v)
345			}
346		}
347	}
348}
349
350// This function is equivalent to lua_next ( http://www.lua.org/manual/5.1/manual.html#lua_next ).
351func (tb *LTable) Next(key LValue) (LValue, LValue) {
352	init := false
353	if key == LNil {
354		key = LNumber(0)
355		init = true
356	}
357
358	if init || key != LNumber(0) {
359		if kv, ok := key.(LNumber); ok && isInteger(kv) && int(kv) >= 0 && kv < LNumber(MaxArrayIndex) {
360			index := int(kv)
361			if tb.array != nil {
362				for ; index < len(tb.array); index++ {
363					if v := tb.array[index]; v != LNil {
364						return LNumber(index + 1), v
365					}
366				}
367			}
368			if tb.array == nil || index == len(tb.array) {
369				if (tb.dict == nil || len(tb.dict) == 0) && (tb.strdict == nil || len(tb.strdict) == 0) {
370					return LNil, LNil
371				}
372				key = tb.keys[0]
373				if v := tb.RawGetH(key); v != LNil {
374					return key, v
375				}
376			}
377		}
378	}
379
380	for i := tb.k2i[key] + 1; i < len(tb.keys); i++ {
381		key := tb.keys[i]
382		if v := tb.RawGetH(key); v != LNil {
383			return key, v
384		}
385	}
386	return LNil, LNil
387}
388