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_map.h>
18#include <aerospike/as_iterator.h>
19#include <aerospike/as_map_iterator.h>
20#include <aerospike/as_msgpack.h>
21#include <aerospike/as_serializer.h>
22#include <aerospike/as_val.h>
23#include <aerospike/mod_lua_iterator.h>
24#include <aerospike/mod_lua_reg.h>
25#include <citrusleaf/alloc.h>
26
27#include "internal.h"
28
29/*******************************************************************************
30 * MACROS
31 ******************************************************************************/
32
33#define OBJECT_NAME "map"
34#define CLASS_NAME "Map"
35
36/*******************************************************************************
37 * FUNCTIONS
38 ******************************************************************************/
39
40 as_map * mod_lua_tomap(lua_State * l, int index) {
41 mod_lua_box * box = mod_lua_tobox(l, index, CLASS_NAME);
42 return (as_map *) mod_lua_box_value(box);
43}
44
45as_map * mod_lua_pushmap(lua_State * l, as_map * map) {
46 mod_lua_box * box = mod_lua_pushbox(l, MOD_LUA_SCOPE_LUA, map, CLASS_NAME);
47 return (as_map *) mod_lua_box_value(box);
48}
49
50static as_map * mod_lua_checkmap(lua_State * l, int index) {
51 mod_lua_box * box = mod_lua_checkbox(l, index, CLASS_NAME);
52 return (as_map *) mod_lua_box_value(box);
53}
54
55static int mod_lua_map_gc(lua_State * l) {
56 mod_lua_freebox(l, 1, CLASS_NAME);
57 return 0;
58}
59
60
61
62static int mod_lua_map_size(lua_State * l) {
63 as_map * map = mod_lua_checkmap(l, 1);
64 uint32_t size = as_map_size(map);
65 lua_pushinteger(l, size);
66 return 1;
67}
68
69static int mod_lua_map_nbytes(lua_State * l) {
70 as_map * map = mod_lua_checkmap(l, 1);
71 uint32_t nbytes = 0;
72 if ( map ) {
73 as_serializer s;
74 as_msgpack_init(&s);
75 nbytes = as_serializer_serialize_getsize(&s, (as_val *) map);
76 as_serializer_destroy(&s);
77 }
78 lua_pushinteger(l, nbytes);
79 return 1;
80}
81
82static int mod_lua_map_new(lua_State * l) {
83 int n = lua_gettop(l);
84 if (n != 1) {
85 return 0;
86 }
87 lua_Integer capacity = luaL_optinteger(l, 1, 0);
88 if (capacity < 1) {
89 return 0;
90 }
91 as_map * map = (as_map *) as_hashmap_new((uint32_t)capacity);
92 mod_lua_pushmap(l, map);
93 return 1;
94}
95
96static int mod_lua_map_cons(lua_State * l) {
97 lua_Integer capacity = luaL_optinteger(l, 3, 32);
98 if (capacity < 1) {
99 capacity = 32;
100 }
101 as_map * map = (as_map *) as_hashmap_new((uint32_t)capacity);
102 int n = lua_gettop(l);
103 if ( (n == 2 || n == 3) && lua_type(l, 2) == LUA_TTABLE ) {
104 lua_pushnil(l);
105 while ( lua_next(l, 2) != 0 ) {
106 // this will leak or crash if these are not as_val, or k is and v isn't
107 as_val * k = mod_lua_takeval(l, -2);
108 as_val * v = mod_lua_takeval(l, -1);
109 if ( !k || !v ) {
110 as_val_destroy(k);
111 as_val_destroy(v);
112 }
113 else {
114 as_map_set(map, k, v);
115 }
116 lua_pop(l, 1);
117 }
118 }
119 mod_lua_pushmap(l, map);
120 return 1;
121}
122
123static int mod_lua_map_index(lua_State * l) {
124 mod_lua_box * box = mod_lua_checkbox(l, 1, CLASS_NAME);
125 as_map * map = (as_map *) mod_lua_box_value(box);
126 as_val * val = NULL;
127
128 if ( map ) {
129 as_val * key = mod_lua_takeval(l, 2);
130 if ( key ) {
131 val = as_map_get(map, key);
132 as_val_destroy(key);
133 }
134 }
135
136 if ( val ) {
137 mod_lua_pushval(l, val);
138 }
139 else {
140 lua_pushnil(l);
141 }
142
143 return 1;
144}
145
146static int mod_lua_map_newindex(lua_State * l) {
147 as_map * map = mod_lua_checkmap(l, 1);
148 if ( map ) {
149 as_val * key = mod_lua_takeval(l, 2);
150 as_val * val = mod_lua_takeval(l, 3);
151 if ( !key ) {
152 as_val_destroy(key);
153 as_val_destroy(val);
154 }
155 else if ( !val ) {
156 as_map_remove(map, key);
157 as_val_destroy(key);
158 }
159 else {
160 as_map_set(map, key, val);
161 }
162 }
163 return 0;
164}
165
166static int mod_lua_map_remove(lua_State * l) {
167 as_map * map = mod_lua_checkmap(l, 1);
168 if ( map ) {
169 as_val * key = mod_lua_takeval(l, 2);
170 if ( key ) {
171 as_map_remove(map, key);
172 as_val_destroy(key);
173 }
174 }
175 return 0;
176}
177
178static int mod_lua_map_len(lua_State * l) {
179 mod_lua_box * box = mod_lua_checkbox(l, 1, CLASS_NAME);
180 as_map * map = (as_map *) mod_lua_box_value(box);
181 if ( map ) {
182 lua_pushinteger(l, as_map_size(map));
183 }
184 else {
185 lua_pushinteger(l, 0);
186 }
187 return 1;
188}
189
190static int mod_lua_map_tostring(lua_State * l) {
191 mod_lua_box * box = mod_lua_checkbox(l, 1, CLASS_NAME);
192 as_val * val = mod_lua_box_value(box);
193 char * str = NULL;
194
195 if ( val ) {
196 str = as_val_tostring(val);
197 }
198
199 if ( str ) {
200 lua_pushstring(l, str);
201 cf_free(str);
202 }
203 else {
204 lua_pushstring(l, "Map()");
205 }
206
207 return 1;
208}
209
210/**
211 * Generator for map.pairs()
212 */
213static int mod_lua_map_pairs_next(lua_State * l) {
214 as_iterator * iter = mod_lua_toiterator(l, 1);
215 if ( iter && as_iterator_has_next(iter) ) {
216 as_pair * pair = (as_pair *) as_iterator_next(iter);
217 if ( pair ) {
218 mod_lua_pushval(l, pair->_1);
219 mod_lua_pushval(l, pair->_2);
220 return 2;
221 }
222 }
223 return 0;
224}
225
226/**
227 * USAGE:
228 * for k,v in map.pairs(m) do
229 * end
230 * USAGE:
231 * for k,v in map.iterator(m) do
232 * end
233 */
234static int mod_lua_map_pairs(lua_State * l) {
235 mod_lua_box * box = mod_lua_checkbox(l, 1, CLASS_NAME);
236 as_map * map = (as_map *) mod_lua_box_value(box);
237 if ( map ) {
238 lua_pushcfunction(l, mod_lua_map_pairs_next);
239 as_map_iterator * itr = (as_map_iterator *) mod_lua_pushiterator(l, sizeof(as_map_iterator));
240 as_map_iterator_init(itr,map);
241 return 2;
242 }
243
244 return 0;
245}
246
247/**
248 * Generator for map.keys()
249 */
250static int mod_lua_map_keys_next(lua_State * l) {
251 as_iterator * iter = mod_lua_toiterator(l, 1);
252 if ( iter && as_iterator_has_next(iter) ) {
253 as_pair * pair = (as_pair *) as_iterator_next(iter);
254 if ( pair ) {
255 mod_lua_pushval(l, pair->_1);
256 return 1;
257 }
258 }
259 return 0;
260}
261
262/**
263 * USAGE:
264 * for k in map.keys(m) do
265 * end
266 */
267static int mod_lua_map_keys(lua_State * l) {
268 mod_lua_box * box = mod_lua_checkbox(l, 1, CLASS_NAME);
269 as_map * map = (as_map *) mod_lua_box_value(box);
270 if ( map ) {
271 lua_pushcfunction(l, mod_lua_map_keys_next);
272 as_map_iterator * iter = (as_map_iterator *) mod_lua_pushiterator(l, sizeof(as_map_iterator));
273 as_map_iterator_init(iter, map);
274 return 2;
275 }
276
277 return 0;
278}
279
280/**
281 * Generator for map.values()
282 */
283static int mod_lua_map_values_next(lua_State * l) {
284 as_iterator * iter = mod_lua_toiterator(l, 1);
285 if ( iter && as_iterator_has_next(iter) ) {
286 as_pair * pair = (as_pair *) as_iterator_next(iter);
287 if ( pair ) {
288 mod_lua_pushval(l, pair->_2);
289 return 1;
290 }
291 }
292 return 0;
293}
294
295/**
296 * USAGE:
297 * for v in map.values(m) do
298 * end
299 */
300static int mod_lua_map_values(lua_State * l) {
301 mod_lua_box * box = mod_lua_checkbox(l, 1, CLASS_NAME);
302 as_map * map = (as_map *) mod_lua_box_value(box);
303 if ( map ) {
304 lua_pushcfunction(l, mod_lua_map_values_next);
305 as_map_iterator * itr = (as_map_iterator *) mod_lua_pushiterator(l, sizeof(as_map_iterator));
306 as_map_iterator_init(itr, map);
307 return 2;
308 }
309
310 return 0;
311}
312
313/******************************************************************************
314 * OBJECT TABLE
315 *****************************************************************************/
316
317static const luaL_reg object_table[] = {
318 {"new", mod_lua_map_new}, // Only supported in C.
319 {"create", mod_lua_map_new}, // Supported in all languages.
320 {"iterator", mod_lua_map_pairs},
321 {"pairs", mod_lua_map_pairs},
322 {"keys", mod_lua_map_keys},
323 {"values", mod_lua_map_values},
324 {"remove", mod_lua_map_remove},
325 {"size", mod_lua_map_size},
326 {"nbytes", mod_lua_map_nbytes},
327 {"tostring", mod_lua_map_tostring},
328 {0, 0}
329};
330
331static const luaL_reg object_metatable[] = {
332 {"__call", mod_lua_map_cons},
333 {0, 0}
334};
335
336/******************************************************************************
337 * CLASS TABLE
338 *****************************************************************************/
339
340/*
341static const luaL_reg class_table[] = {
342 {0, 0}
343};
344*/
345
346static const luaL_reg class_metatable[] = {
347 {"__index", mod_lua_map_index},
348 {"__newindex", mod_lua_map_newindex},
349 {"__len", mod_lua_map_len},
350 {"__tostring", mod_lua_map_tostring},
351 {"__gc", mod_lua_map_gc},
352 {0, 0}
353};
354
355/******************************************************************************
356 * REGISTER
357 *****************************************************************************/
358
359int mod_lua_map_register(lua_State * l) {
360 mod_lua_reg_object(l, OBJECT_NAME, object_table, object_metatable);
361 mod_lua_reg_class(l, CLASS_NAME, NULL, class_metatable);
362 return 1;
363}
364