]> source.dussan.org Git - rspamd.git/commitdiff
[Minor] Add some hacks to convert lua table into either object or an array
authorVsevolod Stakhov <vsevolod@rspamd.com>
Fri, 16 Aug 2024 12:21:28 +0000 (13:21 +0100)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Fri, 16 Aug 2024 12:21:28 +0000 (13:21 +0100)
contrib/libucl/lua_ucl.c

index c2b37b14c6910cac7addcc3b5fc2bca04c78c0d9..19ac9cb1277fa35d1248e6a8e6cb60e033e5d11b 100644 (file)
@@ -44,6 +44,7 @@
 #include "ucl.h"
 #include "ucl_internal.h"
 #include "lua_ucl.h"
+#include "kvec.h"
 #include <strings.h>
 
 /***
@@ -1423,67 +1424,141 @@ lua_ucl_newindex(lua_State *L)
        obj = lua_ucl_object_get(L, 1);
        int key_type = lua_type(L, 2);
 
-       if (ucl_object_type(obj) == UCL_OBJECT && (key_type == LUA_TSTRING)) {
-               lua_Integer keylen;
-               const char *key = lua_tolstring(L, 2, &keylen);
+       if (ucl_object_type(obj) == UCL_OBJECT) {
+               if (key_type == LUA_TSTRING) {
+                       lua_Integer keylen;
+                       const char *key = lua_tolstring(L, 2, &keylen);
 
-               ucl_object_t *value_obj = lua_ucl_object_get(L, 3);
-               if (value_obj) {
-                       value_obj = ucl_object_ref(value_obj);
-               }
-               else {
-                       value_obj = ucl_object_lua_import(L, 3);
+                       ucl_object_t *value_obj = lua_ucl_object_get(L, 3);
+                       if (value_obj) {
+                               value_obj = ucl_object_ref(value_obj);
+                       }
+                       else {
+                               value_obj = ucl_object_lua_import(L, 3);
+                       }
+
+                       if (ucl_object_lookup_len(obj, key, keylen) != NULL) {
+                               if (value_obj != NULL) {
+                                       ucl_object_replace_key(obj, value_obj, key, keylen, true);
+                               }
+                               else {
+                                       /* Delete key */
+                                       ucl_object_delete_keyl(obj, key, keylen);
+                               }
+                       }
+                       else {
+                               if (value_obj != NULL) {
+                                       ucl_object_insert_key(obj, value_obj, key, keylen, true);
+                               }
+                               /* Do nothing if value_obj is null, like Lua does */
+                       }
                }
+               else if (key_type == LUA_TNUMBER) {
+                       /*
+                        * We have some object but wanted it to be an array
+                        * In Lua, this is allowed but UCL has distinction between array and object
+                        * Here, we allow one thing: if an object is empty, and we want a numeric index, we convert it to an array
+                        */
+                       lua_Integer idx = lua_tointeger(L, 2);
+                       if (idx == 1 && obj->len == 0 && obj->value.ov == NULL) {
+                               /* UCL preallocate arrays, but it is not a requirement once av is NULL */
+                               obj->type = UCL_ARRAY;
+                               ucl_object_t *value_obj = lua_ucl_object_get(L, 3);
+                               if (value_obj) {
+                                       value_obj = ucl_object_ref(value_obj);
+                               }
+                               else {
+                                       value_obj = ucl_object_lua_import(L, 3);
+                               }
 
-               if (ucl_object_lookup_len(obj, key, keylen) != NULL) {
-                       if (value_obj != NULL) {
-                               ucl_object_replace_key(obj, value_obj, key, keylen, true);
+                               if (value_obj == NULL) {
+                                       return luaL_error(L, "invalid value type: %s", lua_typename(L, lua_type(L, 3)));
+                               }
+                               ucl_array_append(obj, value_obj);
                        }
                        else {
-                               /* Delete key */
-                               ucl_object_delete_keyl(obj, key, keylen);
+                               return luaL_error(L, "invalid index type for an object: %s", lua_typename(L, key_type));
                        }
                }
                else {
-                       if (value_obj != NULL) {
-                               ucl_object_insert_key(obj, value_obj, key, keylen, true);
-                       }
-                       /* Do nothing if value_obj is null, like Lua does */
+                       return luaL_error(L, "invalid index type for an object: %s", lua_typename(L, key_type));
                }
 
                return 0;
        }
-       else if (ucl_object_type(obj) == UCL_ARRAY && (key_type == LUA_TNUMBER)) {
-               lua_Integer idx = lua_tointeger(L, 2);
+       else if (ucl_object_type(obj) == UCL_ARRAY) {
+               if (key_type == LUA_TNUMBER) {
+                       lua_Integer idx = lua_tointeger(L, 2);
 
-               ucl_object_t *value_obj = lua_ucl_object_get(L, 3);
-               if (value_obj) {
-                       value_obj = ucl_object_ref(value_obj);
-               }
-               else {
-                       value_obj = ucl_object_lua_import(L, 3);
-               }
+                       ucl_object_t *value_obj = lua_ucl_object_get(L, 3);
+                       if (value_obj) {
+                               value_obj = ucl_object_ref(value_obj);
+                       }
+                       else {
+                               value_obj = ucl_object_lua_import(L, 3);
+                       }
 
-               if (value_obj == NULL) {
-                       return luaL_error(L, "invalid value type: %s", lua_typename(L, lua_type(L, 3)));
-               }
+                       if (value_obj == NULL) {
+                               return luaL_error(L, "invalid value type: %s", lua_typename(L, lua_type(L, 3)));
+                       }
 
-               /* Lua allows sparse arrays and ucl does not
-                * So we have 3 options:
-                * 1) Idx is some existing index, so we need to replace it
-                * 3) Idx is #len, so we append it
-                * Everything else is an error
-                */
-               if (idx == ucl_array_size(obj) + 1) {
-                       ucl_array_append(obj, value_obj);
+                       /* Lua allows sparse arrays and ucl does not
+                        * So we have 2 options:
+                        * 1) Idx is some existing index, so we need to replace it
+                        * 3) Idx is #len, so we append it
+                        * Everything else is an error
+                        */
+                       if (idx == ucl_array_size(obj) + 1) {
+                               ucl_array_append(obj, value_obj);
+                       }
+                       else if (idx >= 1 && idx <= ucl_array_size(obj)) {
+                               ucl_array_replace_index(obj, value_obj, idx - 1);
+                       }
+                       else {
+                               ucl_object_unref(value_obj);
+
+                               return luaL_error(L, "invalid index for array: %d", (int) idx);
+                       }
                }
-               else if (idx >= 1 && idx <= ucl_array_size(obj)) {
-                       ucl_array_replace_index(obj, value_obj, idx - 1);
+               else if (key_type == LUA_TSTRING) {
+                       /*
+                        * We have some array but wanted it to be an object
+                        * In Lua, this is allowed but UCL has distinction between array and object
+                        * Here, we allow one thing: if an array is empty, and we want a string index, we convert it to an object
+                        * The biggest issue is that ucl array is preallocated in general, so we need to free it somehow
+                        */
+                       /*
+                        * Dirty hacks are here
+                        */
+                       kvec_t(ucl_object_t *) *real_ar = obj->value.av;
+
+                       if (real_ar) {
+                               kv_destroy(*real_ar);
+                       }
+                       UCL_FREE(sizeof(*real_ar), real_ar);
+                       obj->value.av = NULL;
+                       obj->type = UCL_OBJECT;
+
+                       lua_Integer keylen;
+                       const char *key = lua_tolstring(L, 2, &keylen);
+
+                       ucl_object_t *value_obj = lua_ucl_object_get(L, 3);
+                       if (value_obj) {
+                               value_obj = ucl_object_ref(value_obj);
+                       }
+                       else {
+                               value_obj = ucl_object_lua_import(L, 3);
+                       }
+
+                       if (value_obj) {
+                               ucl_object_insert_key(obj, value_obj, key, keylen, true);
+                       }
+                       else {
+                               return luaL_error(L, "invalid value type: %s", lua_typename(L, lua_type(L, 3)));
+                       }
                }
                else {
-                       ucl_object_unref(value_obj);
-
-                       return luaL_error(L, "invalid index for array: %d", (int) idx);
+                       return luaL_error(L, "invalid index type for an array: %s", lua_typename(L, key_type));
                }
 
                return 0;