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 | |
45 | as_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 | |
50 | static 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 | |
55 | static int mod_lua_map_gc(lua_State * l) { |
56 | mod_lua_freebox(l, 1, CLASS_NAME); |
57 | return 0; |
58 | } |
59 | |
60 | |
61 | |
62 | static 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 | |
69 | static 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 | |
82 | static 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 | |
96 | static 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 | |
123 | static 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 | |
146 | static 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 | |
166 | static 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 | |
178 | static 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 | |
190 | static 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 | */ |
213 | static 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 | */ |
234 | static 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 | */ |
250 | static 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 | */ |
267 | static 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 | */ |
283 | static 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 | */ |
300 | static 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 | |
317 | static 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 | |
331 | static const luaL_reg object_metatable[] = { |
332 | {"__call" , mod_lua_map_cons}, |
333 | {0, 0} |
334 | }; |
335 | |
336 | /****************************************************************************** |
337 | * CLASS TABLE |
338 | *****************************************************************************/ |
339 | |
340 | /* |
341 | static const luaL_reg class_table[] = { |
342 | {0, 0} |
343 | }; |
344 | */ |
345 | |
346 | static 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 | |
359 | int 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 | |