1/*
2 * Copyright 2008-2018 Aerospike, Inc.
3 *
4 * Portions may be licensed to Aerospike, Inc. under one or more contributor
5 * license agreements.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
8 * use this file except in compliance with the License. You may obtain a copy of
9 * the License at http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations under
15 * the License.
16 */
17#include <aerospike/mod_lua_list.h>
18#include <aerospike/as_iterator.h>
19#include <aerospike/as_list.h>
20#include <aerospike/as_list_iterator.h>
21#include <aerospike/as_msgpack.h>
22#include <aerospike/as_serializer.h>
23#include <aerospike/as_val.h>
24#include <aerospike/mod_lua_val.h>
25#include <aerospike/mod_lua_iterator.h>
26#include <aerospike/mod_lua_reg.h>
27#include <citrusleaf/alloc.h>
28
29#include "internal.h"
30
31/*******************************************************************************
32 * MACROS
33 ******************************************************************************/
34
35#define OBJECT_NAME "list"
36#define CLASS_NAME "List"
37
38/*******************************************************************************
39 * FUNCTIONS
40 ******************************************************************************/
41
42as_list * mod_lua_tolist(lua_State * l, int index) {
43 mod_lua_box * box = mod_lua_tobox(l, index, CLASS_NAME);
44 return (as_list *) mod_lua_box_value(box);
45}
46
47as_list * mod_lua_pushlist(lua_State * l, as_list * list) {
48 mod_lua_box * box = mod_lua_pushbox(l, MOD_LUA_SCOPE_LUA, list, CLASS_NAME);
49 return (as_list *) mod_lua_box_value(box);
50}
51
52static as_list * mod_lua_checklist(lua_State * l, int index) {
53 mod_lua_box * box = mod_lua_checkbox(l, index, CLASS_NAME);
54 return (as_list *) mod_lua_box_value(box);
55}
56
57static int mod_lua_list_gc(lua_State * l) {
58 LOG("mod_lua_list_gc: begin");
59 mod_lua_freebox(l, 1, CLASS_NAME);
60 LOG("mod_lua_list_gc: end");
61 return 0;
62}
63
64static int mod_lua_list_insert(lua_State * l) {
65 as_list * list = mod_lua_checklist(l, 1);
66 if (list) {
67 lua_Integer idx = luaL_optinteger(l, 2, 0);
68 // Lua index is 1-based.
69 if (idx > 0) {
70 // increases ref, correct - held by box and this list
71 as_val * value = mod_lua_toval(l, 3);
72 if (value) {
73 as_list_insert(list, (uint32_t)idx - 1, value);
74 }
75 }
76 }
77 return 0;
78}
79
80static int mod_lua_list_append(lua_State * l) {
81 as_list * list = mod_lua_checklist(l, 1);
82 if ( list ) {
83 // increases ref, correct - held by box and this list
84 as_val * value = mod_lua_toval(l, 2);
85 if ( value ) {
86 as_list_append(list,value);
87 }
88 }
89 return 0;
90}
91
92static int mod_lua_list_prepend(lua_State * l) {
93 as_list * list = mod_lua_checklist(l, 1);
94 if ( list ) {
95 as_val * value = mod_lua_toval(l, 2);
96 if ( value ) {
97 as_list_prepend(list,value);
98 }
99 }
100 return 0;
101}
102
103static int mod_lua_list_remove(lua_State * l) {
104 as_list * list = mod_lua_checklist(l, 1);
105 if (list) {
106 lua_Integer idx = luaL_optinteger(l, 2, 0);
107 // Lua index is 1-based.
108 if (idx > 0) {
109 as_list_remove(list, (uint32_t)idx - 1);
110 }
111 }
112 return 0;
113}
114
115static int mod_lua_list_concat(lua_State * l) {
116 as_list * list = mod_lua_checklist(l, 1);
117 if (list) {
118 as_list * list2 = mod_lua_checklist(l, 2);
119 if (list2) {
120 as_list_concat(list, list2);
121 }
122 }
123 return 0;
124}
125
126static int mod_lua_list_trim(lua_State * l) {
127 as_list * list = mod_lua_checklist(l, 1);
128 if (list) {
129 lua_Integer idx = luaL_optinteger(l, 2, 0);
130 // Lua index is 1-based.
131 if (idx > 0) {
132 as_list_trim(list, (uint32_t)idx - 1);
133 }
134 }
135 return 0;
136}
137
138static int mod_lua_list_drop(lua_State * l) {
139 mod_lua_box * box = mod_lua_checkbox(l, 1, CLASS_NAME);
140 as_list * list = (as_list *) mod_lua_box_value(box);
141 as_list * sub = NULL;
142
143 if ( list ) {
144 lua_Integer n = luaL_optinteger(l, 2, 0);
145 if (n > 0) {
146 sub = as_list_drop(list, (uint32_t) n);
147 }
148 }
149
150 if ( sub ) {
151 mod_lua_pushlist(l, sub);
152 }
153 else {
154 lua_pushnil(l);
155 }
156
157 return 1;
158}
159
160static int mod_lua_list_take(lua_State * l) {
161 mod_lua_box * box = mod_lua_checkbox(l, 1, CLASS_NAME);
162 as_list * list = (as_list *) mod_lua_box_value(box);
163 as_list * sub = NULL;
164
165 if ( list ) {
166 lua_Integer n = luaL_optinteger(l, 2, 0);
167 if (n > 0) {
168 sub = as_list_take(list, (uint32_t) n);
169 }
170 }
171
172 if ( sub ) {
173 mod_lua_pushlist(l, sub);
174 }
175 else {
176 lua_pushnil(l);
177 }
178
179 return 1;
180}
181
182static int mod_lua_list_size(lua_State * l) {
183 as_list * list = mod_lua_checklist(l, 1);
184 uint32_t size = 0;
185
186 if ( list ) {
187 size = as_list_size(list);
188 }
189
190 lua_pushinteger(l, size);
191 return 1;
192}
193
194static int mod_lua_list_nbytes(lua_State * l) {
195 as_list * list = mod_lua_checklist(l, 1);
196 uint32_t nbytes = 0;
197
198 if ( list ) {
199 as_serializer s;
200 as_msgpack_init(&s);
201 nbytes = as_serializer_serialize_getsize(&s, (as_val *) list);
202 as_serializer_destroy(&s);
203 }
204 lua_pushinteger(l, nbytes);
205 return 1;
206}
207
208static int mod_lua_list_new(lua_State * l) {
209 int n = lua_gettop(l);
210 if (n < 1 || n > 2) {
211 return 0;
212 }
213 lua_Integer capacity = luaL_optinteger(l, 1, -1);
214 if (capacity < 0) {
215 return 0;
216 }
217 lua_Integer capacity_step = luaL_optinteger(l, 2, 10);
218 if (capacity_step < 0) {
219 return 0;
220 }
221 as_list * ll = (as_list *) as_arraylist_new((uint32_t)capacity, (uint32_t)capacity_step);
222 mod_lua_pushlist(l, ll);
223 return 1;
224}
225
226static int mod_lua_list_cons(lua_State * l) {
227 as_list * ll = (as_list *) as_arraylist_new(5,10);
228 int n = lua_gettop(l);
229 if ( n == 2 && lua_type(l, 2) == LUA_TTABLE) {
230 lua_pushnil(l);
231 while ( lua_next(l, 2) != 0 ) {
232 if ( lua_type(l, -2) == LUA_TNUMBER ) {
233 as_list_append(ll, mod_lua_takeval(l, -1));
234 }
235 lua_pop(l, 1);
236 }
237 }
238 mod_lua_pushlist(l, ll);
239 return 1;
240}
241
242//static int mod_lua_list_iterator(lua_State * l) {
243// as_list * list = mod_lua_checklist(l, 1);
244// if ( list ) {
245// as_iterator * itr = mod_lua_pushiterator(l, as_list_iterator_new(list));
246// as_list_iterator_init(itr, l);
247// }
248// else {
249// lua_pushnil(l);
250// }
251// return 1;
252//}
253
254static int mod_lua_list_index(lua_State * l) {
255 mod_lua_box * box = mod_lua_checkbox(l, 1, CLASS_NAME);
256 as_list * list = (as_list *) mod_lua_box_value(box);
257 as_val * val = NULL;
258
259 if ( list ) {
260 const uint32_t idx = (uint32_t) luaL_optlong(l, 2, 0);
261 if (idx > 0) {
262 // Lua is 1 index, C is 0
263 val = as_list_get(list, idx-1);
264 }
265 }
266
267 if ( val ) {
268 mod_lua_pushval(l, val);
269 }
270 else {
271 lua_pushnil(l);
272 }
273
274 return 1;
275}
276
277static int mod_lua_list_newindex(lua_State * l) {
278 as_list * list = mod_lua_checklist(l, 1);
279
280 if ( list ) {
281 const uint32_t idx = (uint32_t) luaL_optlong(l, 2, 0);
282 if (idx > 0) { // Lua is 1 index, C is 0
283 as_val * val = mod_lua_takeval(l, 3);
284 if ( val ) {
285 as_list_set(list, idx - 1, val);
286 }
287 }
288 }
289 return 0;
290}
291
292static int mod_lua_list_len(lua_State * l) {
293 mod_lua_box * box = mod_lua_checkbox(l, 1, CLASS_NAME);
294 as_list * list = (as_list *) mod_lua_box_value(box);
295 if ( list ) {
296 lua_pushinteger(l, as_list_size(list));
297 }
298 else {
299 lua_pushinteger(l, 0);
300 }
301 return 1;
302}
303
304static int mod_lua_list_tostring(lua_State * l) {
305 mod_lua_box * box = mod_lua_checkbox(l, 1, CLASS_NAME);
306 as_val * val = mod_lua_box_value(box);
307 char * str = NULL;
308
309 if ( val ) {
310 str = as_val_tostring(val);
311 }
312
313 if ( str ) {
314 lua_pushstring(l, str);
315 cf_free(str);
316 }
317 else {
318 lua_pushstring(l, "List()");
319 }
320
321 return 1;
322}
323
324
325/**
326 * Generator for list.iterator()
327 */
328static int mod_lua_list_iterator_next(lua_State * l) {
329 as_iterator * iter = mod_lua_toiterator(l, 1);
330 if ( iter && as_iterator_has_next(iter) ) {
331 const as_val * val = as_iterator_next(iter);
332 if ( val ) {
333 mod_lua_pushval(l, val);
334 return 1;
335 }
336 }
337 return 0;
338}
339
340/**
341 * USAGE:
342 * for v in list.iterator(m) do
343 * end
344 */
345static int mod_lua_list_iterator(lua_State * l) {
346 mod_lua_box * box = mod_lua_checkbox(l, 1, CLASS_NAME);
347 as_list * list = (as_list *) mod_lua_box_value(box);
348 if ( list ) {
349 lua_pushcfunction(l, mod_lua_list_iterator_next);
350 as_list_iterator * itr = (as_list_iterator *) mod_lua_pushiterator(l, sizeof(as_list_iterator));
351 as_list_iterator_init(itr, list);
352 return 2;
353 }
354
355 return 0;
356}
357
358
359/******************************************************************************
360 * OBJECT TABLE
361 *****************************************************************************/
362
363static const luaL_reg object_table[] = {
364 {"new", mod_lua_list_new}, // Only supported in C.
365 {"create", mod_lua_list_new}, // Supported in all languages.
366 {"insert", mod_lua_list_insert},
367 {"append", mod_lua_list_append},
368 {"prepend", mod_lua_list_prepend},
369 {"remove", mod_lua_list_remove},
370 {"concat", mod_lua_list_concat},
371 {"trim", mod_lua_list_trim},
372 {"take", mod_lua_list_take},
373 {"drop", mod_lua_list_drop},
374 {"size", mod_lua_list_size},
375 {"nbytes", mod_lua_list_nbytes},
376 {"iterator", mod_lua_list_iterator},
377 {"tostring", mod_lua_list_tostring},
378 {0, 0}
379};
380
381static const luaL_reg object_metatable[] = {
382 {"__call", mod_lua_list_cons},
383 {0, 0}
384};
385
386/******************************************************************************
387 * CLASS TABLE
388 *****************************************************************************/
389
390/*
391static const luaL_reg class_table[] = {
392 {0, 0}
393};
394*/
395
396static const luaL_reg class_metatable[] = {
397 {"__index", mod_lua_list_index},
398 {"__newindex", mod_lua_list_newindex},
399 {"__len", mod_lua_list_len},
400 {"__tostring", mod_lua_list_tostring},
401 {"__gc", mod_lua_list_gc},
402 {0, 0}
403};
404
405/******************************************************************************
406 * REGISTER
407 *****************************************************************************/
408
409int mod_lua_list_register(lua_State * l) {
410 mod_lua_reg_object(l, OBJECT_NAME, object_table, object_metatable);
411 mod_lua_reg_class(l, CLASS_NAME, NULL, class_metatable);
412 return 1;
413}
414