Explorar el Código

ncd: add NCDValCons, which implements efficient construction of values without knowing list/map lengths in advance

ambrop7 hace 13 años
padre
commit
7ca97e219a
Se han modificado 5 ficheros con 570 adiciones y 0 borrados
  1. 3 0
      examples/CMakeLists.txt
  2. 111 0
      examples/ncdvalcons_test.c
  3. 5 0
      ncd/CMakeLists.txt
  4. 276 0
      ncd/NCDValCons.c
  5. 175 0
      ncd/NCDValCons.h

+ 3 - 0
examples/CMakeLists.txt

@@ -40,6 +40,9 @@ if (BUILD_NCD)
 
     add_executable(ncdval_test ncdval_test.c)
     target_link_libraries(ncdval_test ncdval)
+    
+    add_executable(ncdvalcons_test ncdvalcons_test.c)
+    target_link_libraries(ncdvalcons_test ncdvalcons ncdvaluegenerator)
 endif ()
 
 if (BUILDING_UDEVMONITOR)

+ 111 - 0
examples/ncdvalcons_test.c

@@ -0,0 +1,111 @@
+/**
+ * @file ncdvalcons_test.c
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the
+ *    names of its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#include <misc/debug.h>
+#include <ncd/NCDValCons.h>
+#include <ncd/NCDValueGenerator.h>
+
+static NCDValMem mem;
+static NCDValCons cons;
+
+static NCDValConsVal make_string (const char *data)
+{
+    NCDValConsVal val;
+    int error;
+    int res = NCDValCons_NewString(&cons, (const uint8_t *)data, strlen(data), &val, &error);
+    ASSERT_FORCE(res)
+    return val;
+}
+
+static NCDValConsVal make_list (void)
+{
+    NCDValConsVal val;
+    NCDValCons_NewList(&cons, &val);
+    return val;
+}
+
+static NCDValConsVal make_map (void)
+{
+    NCDValConsVal val;
+    NCDValCons_NewMap(&cons, &val);
+    return val;
+}
+
+static NCDValConsVal list_prepend (NCDValConsVal list, NCDValConsVal elem)
+{
+    int error;
+    int res = NCDValCons_ListPrepend(&cons, &list, elem, &error);
+    ASSERT_FORCE(res)
+    return list;
+}
+
+static NCDValConsVal map_insert (NCDValConsVal map, NCDValConsVal key, NCDValConsVal value)
+{
+    int error;
+    int res = NCDValCons_MapInsert(&cons, &map, key, value, &error);
+    ASSERT_FORCE(res)
+    return map;
+}
+
+static NCDValRef complete (NCDValConsVal cval)
+{
+    int error;
+    NCDValRef val;
+    int res = NCDValCons_Complete(&cons, cval, &val, &error);
+    ASSERT_FORCE(res)
+    return val;
+}
+
+int main ()
+{
+    NCDValMem_Init(&mem);
+    
+    int res = NCDValCons_Init(&cons, &mem);
+    ASSERT_FORCE(res)
+    
+    NCDValRef val1 = complete(list_prepend(list_prepend(list_prepend(make_list(), make_string("hello")), make_string("world")), make_list()));
+    char *str1 = NCDValGenerator_Generate(val1);
+    ASSERT_FORCE(str1)
+    ASSERT_FORCE(!strcmp(str1, "{{}, \"world\", \"hello\"}"))
+    free(str1);
+    
+    NCDValRef val2 = complete(map_insert(map_insert(map_insert(make_map(), make_list(), make_list()), make_string("A"), make_list()), make_string("B"), make_list()));
+    char *str2 = NCDValGenerator_Generate(val2);
+    ASSERT_FORCE(str2)
+    printf("%s\n", str2);
+    ASSERT_FORCE(!strcmp(str2, "[\"A\":{}, \"B\":{}, {}:{}]"))
+    free(str2);
+    
+    NCDValCons_Free(&cons);
+    NCDValMem_Free(&mem);
+    return 0;
+}

+ 5 - 0
ncd/CMakeLists.txt

@@ -75,6 +75,11 @@ add_library(ncdinterfacemonitor
 )
 target_link_libraries(ncdinterfacemonitor base system)
 
+add_library(ncdvalcons
+    NCDValCons.c
+)
+target_link_libraries(ncdvalcons ncdval)
+
 add_executable(badvpn-ncd
     ncd.c
     NCDModule.c

+ 276 - 0
ncd/NCDValCons.c

@@ -0,0 +1,276 @@
+/**
+ * @file NCDValCons.c
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the
+ *    names of its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <misc/balloc.h>
+
+#include "NCDValCons.h"
+
+#define GROWARRAY_NAME NCDValCons__Array
+#define GROWARRAY_OBJECT_TYPE NCDValCons
+#define GROWARRAY_ARRAY_MEMBER elems
+#define GROWARRAY_CAPACITY_MEMBER elems_capacity
+#define GROWARRAY_MAX_CAPACITY INT_MAX
+#include <misc/grow_array.h>
+
+static int alloc_elem (NCDValCons *o)
+{
+    ASSERT(o->elems_size >= 0)
+    ASSERT(o->elems_size <= o->elems_capacity)
+    
+    if (o->elems_size == o->elems_capacity && !NCDValCons__Array_DoubleUp(o)) {
+        return -1;
+    }
+    
+    return o->elems_size++;
+}
+
+static void assert_cons_val (NCDValCons *o, NCDValConsVal *val)
+{
+    ASSERT(val)
+    
+#ifndef NDEBUG
+    switch (val->cons_type) {
+        case NCDVALCONS_TYPE_COMPLETE: {
+            ASSERT(!NCDVal_IsInvalid(NCDVal_FromSafe(o->mem, val->u.complete_ref)))
+        } break;
+        case NCDVALCONS_TYPE_INCOMPLETE_LIST:
+        case NCDVALCONS_TYPE_INCOMPLETE_MAP: {
+            ASSERT(val->u.incomplete.elems_idx >= -1)
+            ASSERT(val->u.incomplete.elems_idx < o->elems_size)
+        } break;
+        default:
+            ASSERT(0);
+    }
+#endif
+}
+
+static int complete_value (NCDValCons *o, NCDValConsVal val, NCDValSafeRef *out, int *out_error)
+{
+    assert_cons_val(o, &val);
+    ASSERT(out)
+    ASSERT(out_error)
+    
+    switch (val.cons_type) {
+        case NCDVALCONS_TYPE_COMPLETE: {
+            *out = val.u.complete_ref;
+        } break;
+        
+        case NCDVALCONS_TYPE_INCOMPLETE_LIST: {
+            NCDValRef list = NCDVal_NewList(o->mem, val.u.incomplete.count);
+            if (NCDVal_IsInvalid(list)) {
+                goto fail_memory;
+            }
+            
+            int elemidx = val.u.incomplete.elems_idx;
+            
+            while (elemidx != -1) {
+                ASSERT(elemidx >= 0)
+                ASSERT(elemidx < o->elems_size)
+                
+                NCDValRef elem = NCDVal_FromSafe(o->mem, o->elems[elemidx].ref);
+                
+                NCDVal_ListAppend(list, elem);
+                
+                elemidx = o->elems[elemidx].next;
+            }
+            
+            *out = NCDVal_ToSafe(list);
+        } break;
+        
+        case NCDVALCONS_TYPE_INCOMPLETE_MAP: {
+            NCDValRef map = NCDVal_NewMap(o->mem, val.u.incomplete.count);
+            if (NCDVal_IsInvalid(map)) {
+                goto fail_memory;
+            }
+            
+            int keyidx = val.u.incomplete.elems_idx;
+            
+            while (keyidx != -1) {
+                ASSERT(keyidx >= 0)
+                ASSERT(keyidx < o->elems_size)
+                
+                int validx = o->elems[keyidx].next;
+                ASSERT(validx >= 0)
+                ASSERT(validx < o->elems_size)
+                
+                NCDValRef key = NCDVal_FromSafe(o->mem, o->elems[keyidx].ref);
+                NCDValRef value = NCDVal_FromSafe(o->mem, o->elems[validx].ref);
+                
+                if (!NCDVal_MapInsert(map, key, value)) {
+                    *out_error = NCDVALCONS_ERROR_DUPLICATE_KEY;
+                    return 0;
+                }
+                
+                keyidx = o->elems[validx].next;
+            }
+            
+            *out = NCDVal_ToSafe(map);
+        } break;
+        
+        default:
+            ASSERT(0);
+    }
+    
+    return 1;
+    
+fail_memory:
+    *out_error = NCDVALCONS_ERROR_MEMORY;
+    return 0;
+}
+
+int NCDValCons_Init (NCDValCons *o, NCDValMem *mem)
+{
+    ASSERT(mem)
+    
+    o->mem = mem;
+    o->elems_size = 0;
+    
+    if (!NCDValCons__Array_Init(o, 1)) {
+        return 0;
+    }
+    
+    return 1;
+}
+
+void NCDValCons_Free (NCDValCons *o)
+{
+    NCDValCons__Array_Free(o);
+}
+
+int NCDValCons_NewString (NCDValCons *o, const uint8_t *data, size_t len, NCDValConsVal *out, int *out_error)
+{
+    ASSERT(out)
+    ASSERT(out_error)
+    
+    NCDValRef ref = NCDVal_NewStringBin(o->mem, data, len);
+    if (NCDVal_IsInvalid(ref)) {
+        *out_error = NCDVALCONS_ERROR_MEMORY;
+        return 0;
+    }
+    
+    out->cons_type = NCDVALCONS_TYPE_COMPLETE;
+    out->u.complete_ref = NCDVal_ToSafe(ref);
+    
+    return 1;
+}
+
+void NCDValCons_NewList (NCDValCons *o, NCDValConsVal *out)
+{
+    ASSERT(out)
+    
+    out->cons_type = NCDVALCONS_TYPE_INCOMPLETE_LIST;
+    out->u.incomplete.elems_idx = -1;
+    out->u.incomplete.count = 0;
+}
+
+void NCDValCons_NewMap (NCDValCons *o, NCDValConsVal *out)
+{
+    ASSERT(out)
+    
+    out->cons_type = NCDVALCONS_TYPE_INCOMPLETE_MAP;
+    out->u.incomplete.elems_idx = -1;
+    out->u.incomplete.count = 0;
+}
+
+int NCDValCons_ListPrepend (NCDValCons *o, NCDValConsVal *list, NCDValConsVal elem, int *out_error)
+{
+    assert_cons_val(o, list);
+    ASSERT(list->cons_type == NCDVALCONS_TYPE_INCOMPLETE_LIST)
+    assert_cons_val(o, &elem);
+    ASSERT(out_error)
+    
+    int elemidx = alloc_elem(o);
+    if (elemidx < 0) {
+        *out_error = NCDVALCONS_ERROR_MEMORY;
+        return 0;
+    }
+    
+    o->elems[elemidx].next = list->u.incomplete.elems_idx;
+    
+    if (!complete_value(o, elem, &o->elems[elemidx].ref, out_error)) {
+        return 0;
+    }
+    
+    list->u.incomplete.elems_idx = elemidx;
+    list->u.incomplete.count++;
+    
+    return 1;
+}
+
+int NCDValCons_MapInsert (NCDValCons *o, NCDValConsVal *map, NCDValConsVal key, NCDValConsVal value, int *out_error)
+{
+    assert_cons_val(o, map);
+    ASSERT(map->cons_type == NCDVALCONS_TYPE_INCOMPLETE_MAP)
+    assert_cons_val(o, &key);
+    assert_cons_val(o, &value);
+    ASSERT(out_error)
+    
+    int validx = alloc_elem(o);
+    if (validx < 0) {
+        *out_error = NCDVALCONS_ERROR_MEMORY;
+        return 0;
+    }
+    
+    int keyidx = alloc_elem(o);
+    if (keyidx < 0) {
+        *out_error = NCDVALCONS_ERROR_MEMORY;
+        return 0;
+    }
+    
+    o->elems[validx].next = map->u.incomplete.elems_idx;
+    o->elems[keyidx].next = validx;
+    
+    if (!complete_value(o, value, &o->elems[validx].ref, out_error)) {
+        return 0;
+    }
+    
+    if (!complete_value(o, key, &o->elems[keyidx].ref, out_error)) {
+        return 0;
+    }
+    
+    map->u.incomplete.elems_idx = keyidx;
+    map->u.incomplete.count++;
+    
+    return 1;
+}
+
+int NCDValCons_Complete (NCDValCons *o, NCDValConsVal val, NCDValRef *out, int *out_error)
+{
+    assert_cons_val(o, &val);
+    ASSERT(out)
+    ASSERT(out_error)
+    
+    NCDValSafeRef sref;
+    if (!complete_value(o, val, &sref, out_error)) {
+        return 0;
+    }
+    
+    *out = NCDVal_FromSafe(o->mem, sref);
+    return 1;
+}

+ 175 - 0
ncd/NCDValCons.h

@@ -0,0 +1,175 @@
+/**
+ * @file NCDValCons.h
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the
+ *    names of its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BADVPN_NCDVALCONS_H
+#define BADVPN_NCDVALCONS_H
+
+#include <limits.h>
+#include <stdint.h>
+
+#include <misc/debug.h>
+#include <ncd/NCDVal.h>
+
+struct NCDValCons__temp_elem {
+    NCDValSafeRef ref;
+    int next;
+};
+
+/**
+ * Value constructor; implements a mechanism for efficiently constructing
+ * NCD values into {@link NCDVal} compact representation, but without
+ * having to know the number of list or map elements in advance.
+ * For the purpose of value construction, values are representing using
+ * {@link NCDValConsVal} objects.
+ */
+typedef struct {
+    NCDValMem *mem;
+    struct NCDValCons__temp_elem *elems;
+    int elems_size;
+    int elems_capacity;
+} NCDValCons;
+
+#define NCDVALCONS_TYPE_COMPLETE 1
+#define NCDVALCONS_TYPE_INCOMPLETE_LIST 2
+#define NCDVALCONS_TYPE_INCOMPLETE_MAP 3
+
+/**
+ * Abstract handle which represents a value during constuction via
+ * {@link NCDValCons}. 
+ */
+typedef struct {
+    int cons_type;
+    union {
+        NCDValSafeRef complete_ref;
+        struct {
+            int elems_idx;
+            int count;
+        } incomplete;
+    } u;
+} NCDValConsVal;
+
+#define NCDVALCONS_ERROR_MEMORY 1
+#define NCDVALCONS_ERROR_DUPLICATE_KEY 2
+
+/**
+ * Initializes a value constructor.
+ * 
+ * @param o value constructor to initialize
+ * @param mem memory object where values will be stored into
+ * @return 1 on success, 0 on failure
+ */
+int NCDValCons_Init (NCDValCons *o, NCDValMem *mem) WARN_UNUSED;
+
+/**
+ * Frees the value constructor. This only means the constuctor does
+ * not exist any more; any values constructed and completed using
+ * {@link NCDValCons_Complete} remain in the memory object.
+ * 
+ * @param o value constructor to free
+ */
+void NCDValCons_Free (NCDValCons *o);
+
+/**
+ * Creates a new string value with the given data.
+ * 
+ * @param o value constructor
+ * @param data pointer to string data. This must not point into the
+ *             memory object the value constructor is using. The data
+ *             is copied.
+ * @param len length of the string
+ * @param out on success, *out will be set to a handle representing
+ *            the new string
+ * @param out_error on failure, *out_error will be set to an error code
+ * @return 1 on success, 0 on failure
+ */
+int NCDValCons_NewString (NCDValCons *o, const uint8_t *data, size_t len, NCDValConsVal *out, int *out_error) WARN_UNUSED;
+
+/**
+ * Creates an empty list value.
+ * 
+ * @param o value constructor
+ * @param out *out will be set to a handle representing the new list
+ */
+void NCDValCons_NewList (NCDValCons *o, NCDValConsVal *out);
+
+/**
+ * Creates an empty map value.
+ * 
+ * @param o value constructor
+ * @param out *out will be set to a handle representing the new map
+ */
+void NCDValCons_NewMap (NCDValCons *o, NCDValConsVal *out);
+
+/**
+ * Prepends an element to a list value.
+ * 
+ * @param o value constructor
+ * @param list pointer to the handle representing the list. On success,
+ *             the handle will be modified, and the old handle must not
+ *             be used any more.
+ * @param elem handle representing the value to be prepended. This handle
+ *             must not be used any more after being prepended to the list.
+ * @param out_error on failure, *out_error will be set to an error code
+ * @return 1 on success, 0 on failure
+ */
+int NCDValCons_ListPrepend (NCDValCons *o, NCDValConsVal *list, NCDValConsVal elem, int *out_error) WARN_UNUSED;
+
+/**
+ * Inserts an entry into a map value.
+ * 
+ * @param o value constructor
+ * @param map pointer to the handle representing the map. On success,
+ *             the handle will be modified, and the old handle must not
+ *             be used any more.
+ * @param key handle representing the key of the entry. This handle
+ *            must not be used any more after being inserted into the map.
+ * @param value handle representing the value of the entry. This handle
+ *              must not be used any more after being inserted into the
+ *              map.
+ * @param out_error on failure, *out_error will be set to an error code
+ * @return 1 on success, 0 on failure
+ */
+int NCDValCons_MapInsert (NCDValCons *o, NCDValConsVal *map, NCDValConsVal key, NCDValConsVal value, int *out_error) WARN_UNUSED;
+
+/**
+ * Completes a value represented by a {@link NCDValConsVal} handle,
+ * producing a {@link NCDValRef} object which refers to this value within
+ * the memory object.
+ * 
+ * @param o value constructor
+ * @param val handle representing the value to be completed. After a value
+ *            is completed, the handle must not be used any more.
+ * @param out on success, *out will be set to a {@link NCDValRef} object
+ *            referencing the completed value
+ * @param out_error on failure, *out_error will be set to an error code
+ * @return 1 on success, 0 on failure
+ */
+int NCDValCons_Complete (NCDValCons *o, NCDValConsVal val, NCDValRef *out, int *out_error) WARN_UNUSED;
+
+#endif