Kaynağa Gözat

ncd: NCDVal: implement external strings

ambrop7 13 yıl önce
ebeveyn
işleme
6fd2495b9d
5 değiştirilmiş dosya ile 397 ekleme ve 19 silme
  1. 6 1
      ncd/CMakeLists.txt
  2. 71 0
      ncd/NCDRefTarget.c
  3. 75 0
      ncd/NCDRefTarget.h
  4. 203 18
      ncd/NCDVal.c
  5. 42 0
      ncd/NCDVal.h

+ 6 - 1
ncd/CMakeLists.txt

@@ -78,10 +78,15 @@ add_library(ncdstringindex
     NCDStringIndex.c
 )
 
+add_library(ncdreftarget
+    NCDRefTarget.c
+)
+target_link_libraries(ncdreftarget base)
+
 add_library(ncdval
     NCDVal.c
 )
-target_link_libraries(ncdval base ncdstringindex)
+target_link_libraries(ncdval base ncdstringindex ncdreftarget)
 
 add_library(ncdvalgenerator
     NCDValGenerator.c

+ 71 - 0
ncd/NCDRefTarget.c

@@ -0,0 +1,71 @@
+/**
+ * @file NCDRefTarget.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 <limits.h>
+
+#include <misc/debug.h>
+
+#include "NCDRefTarget.h"
+
+void NCDRefTarget_Init (NCDRefTarget *o, NCDRefTarget_func_release func_release)
+{
+    ASSERT(func_release)
+    
+    o->func_release = func_release;
+    o->refcnt = 1;
+    
+    DebugObject_Init(&o->d_obj);
+}
+
+void NCDRefTarget_Deref (NCDRefTarget *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->refcnt > 0)
+    
+    o->refcnt--;
+    
+    if (o->refcnt == 0) {
+        DebugObject_Free(&o->d_obj);
+        o->func_release(o);
+    }
+}
+
+int NCDRefTarget_Ref (NCDRefTarget *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->refcnt > 0)
+    
+    if (o->refcnt == INT_MAX) {
+        return 0;
+    }
+    
+    o->refcnt++;
+    
+    return 1;
+}

+ 75 - 0
ncd/NCDRefTarget.h

@@ -0,0 +1,75 @@
+/**
+ * @file NCDRefTarget.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_NCD_REF_TARGET_H
+#define BADVPN_NCD_REF_TARGET_H
+
+#include <misc/debug.h>
+#include <base/DebugObject.h>
+
+/**
+ * Represents a reference-counted object.
+ */
+typedef struct NCDRefTarget_s NCDRefTarget;
+
+/**
+ * Callback function called after the reference count of a {@link NCDRefTarget}
+ * reaches has reached zero. At this point the NCDRefTarget object has already
+ * been invalidated, i.e. {@link NCDRefTarget_Ref} must not be called on this
+ * object after this handler is called.
+ */
+typedef void (*NCDRefTarget_func_release) (NCDRefTarget *o);
+
+struct NCDRefTarget_s {
+    NCDRefTarget_func_release func_release;
+    int refcnt;
+    DebugObject d_obj;
+};
+
+/**
+ * Initializes a reference target object. The initial reference count of the object
+ * is 1. The \a func_release argument specifies the function to be called from
+ * {@link NCDRefTarget_Deref} when the reference count reaches zero.
+ */
+void NCDRefTarget_Init (NCDRefTarget *o, NCDRefTarget_func_release func_release);
+
+/**
+ * Decrements the reference count of a reference target object. If the reference
+ * count has reached zero, the object's {@link NCDRefTarget_func_release} function
+ * is called, and the object is considered destroyed.
+ */
+void NCDRefTarget_Deref (NCDRefTarget *o);
+
+/**
+ * Increments the reference count of a reference target object.
+ * Returns 1 on success and 0 on failure.
+ */
+int NCDRefTarget_Ref (NCDRefTarget *o) WARN_UNUSED;
+
+#endif

+ 203 - 18
ncd/NCDVal.c

@@ -35,14 +35,19 @@
 
 #include <misc/bsize.h>
 #include <misc/balloc.h>
+#include <misc/strdup.h>
+#include <misc/offset.h>
 #include <base/BLog.h>
 
 #include "NCDVal.h"
 
 #include <generated/blog_channel_NCDVal.h>
 
+//#define NCDVAL_TEST_EXTERNAL_STRINGS
+
 #define EXTERNAL_TYPE_MASK ((1 << 3) - 1)
 #define IDSTRING_TYPE (NCDVAL_STRING | (1 << 3))
+#define EXTERNALSTRING_TYPE (NCDVAL_STRING | (2 << 3))
 
 static void * NCDValMem__BufAt (NCDValMem *o, NCDVal__idx idx)
 {
@@ -169,6 +174,14 @@ static void NCDVal__AssertValOnly (NCDValMem *mem, NCDVal__idx idx)
             ASSERT(ids_e->string_id >= 0)
             ASSERT(ids_e->string_index)
         } break;
+        case EXTERNALSTRING_TYPE: {
+            ASSERT(idx + sizeof(struct NCDVal__externalstring) <= mem->used)
+            struct NCDVal__externalstring *exs_e = NCDValMem__BufAt(mem, idx);
+            ASSERT(exs_e->data)
+            ASSERT(exs_e->ref.next >= -1)
+            ASSERT(exs_e->ref.next < mem->used)
+            ASSERT(exs_e->ref.target)
+        } break;
         default: ASSERT(0);
     }
 #endif
@@ -220,12 +233,20 @@ void NCDValMem_Init (NCDValMem *o)
     o->buf = NULL;
     o->size = NCDVAL_FASTBUF_SIZE;
     o->used = 0;
+    o->first_ref = -1;
 }
 
 void NCDValMem_Free (NCDValMem *o)
 {
     NCDVal__AssertMem(o);
     
+    NCDVal__idx refidx = o->first_ref;
+    while (refidx != -1) {
+        struct NCDVal__ref *ref = NCDValMem__BufAt(o, refidx);
+        NCDRefTarget_Deref(ref->target);
+        refidx = ref->next;
+    }
+    
     if (o->buf) {
         BFree(o->buf);
     }
@@ -237,6 +258,7 @@ int NCDValMem_InitCopy (NCDValMem *o, NCDValMem *other)
     
     o->size = other->size;
     o->used = other->used;
+    o->first_ref = other->first_ref;
     
     if (!other->buf) {
         o->buf = NULL;
@@ -249,8 +271,27 @@ int NCDValMem_InitCopy (NCDValMem *o, NCDValMem *other)
         memcpy(o->buf, other->buf, other->used);
     }
     
+    NCDVal__idx refidx = o->first_ref;
+    while (refidx != -1) {
+        struct NCDVal__ref *ref = NCDValMem__BufAt(o, refidx);
+        if (!NCDRefTarget_Ref(ref->target)) {
+            goto fail1;
+        }
+        refidx = ref->next;
+    }
+    
     return 1;
     
+fail1:;
+    NCDVal__idx undo_refidx = o->first_ref;
+    while (undo_refidx != refidx) {
+        struct NCDVal__ref *ref = NCDValMem__BufAt(o, undo_refidx);
+        NCDRefTarget_Deref(ref->target);
+        undo_refidx = ref->next;
+    }
+    if (o->buf) {
+        BFree(o->buf);
+    }
 fail0:
     return 0;
 }
@@ -383,6 +424,12 @@ NCDValRef NCDVal_NewCopy (NCDValMem *mem, NCDValRef val)
             return NCDVal_NewIdString(mem, ids_e->string_id, ids_e->string_index);
         } break;
         
+        case EXTERNALSTRING_TYPE: {
+            struct NCDVal__externalstring *exs_e = ptr;
+            
+            return NCDVal_NewExternalString(mem, exs_e->data, exs_e->length, exs_e->ref.target);
+        } break;
+        
         default: ASSERT(0);
     }
     
@@ -521,6 +568,13 @@ int NCDVal_IsIdString (NCDValRef val)
     return !(val.idx < -1) && *(int *)NCDValMem__BufAt(val.mem, val.idx) == IDSTRING_TYPE;
 }
 
+int NCDVal_IsExternalString (NCDValRef val)
+{
+    NCDVal__AssertVal(val);
+    
+    return !(val.idx < -1) && *(int *)NCDValMem__BufAt(val.mem, val.idx) == EXTERNALSTRING_TYPE;
+}
+
 int NCDVal_IsStringNoNulls (NCDValRef val)
 {
     NCDVal__AssertVal(val);
@@ -537,6 +591,56 @@ NCDValRef NCDVal_NewString (NCDValMem *mem, const char *data)
     return NCDVal_NewStringBin(mem, (const uint8_t *)data, strlen(data));
 }
 
+#ifdef NCDVAL_TEST_EXTERNAL_STRINGS
+
+struct test_ext_str {
+    NCDRefTarget ref_target;
+    char *data;
+};
+
+static void test_ext_str_ref_target_func_dealloc (NCDRefTarget *ref_target)
+{
+    struct test_ext_str *tes = UPPER_OBJECT(ref_target, struct test_ext_str, ref_target);
+    BFree(tes->data);
+    BFree(tes);
+}
+
+NCDValRef NCDVal_NewStringBin (NCDValMem *mem, const uint8_t *data, size_t len)
+{
+    NCDVal__AssertMem(mem);
+    ASSERT(len == 0 || data)
+    NCDVal_AssertExternal(mem, data, len);
+    
+    struct test_ext_str *tes = BAlloc(sizeof(*tes));
+    if (!tes) {
+        goto fail0;
+    }
+    
+    tes->data = BAlloc(len);
+    if (!tes->data) {
+        goto fail1;
+    }
+    
+    if (len > 0) {
+        memcpy(tes->data, data, len);
+    }
+    
+    NCDRefTarget_Init(&tes->ref_target, test_ext_str_ref_target_func_dealloc);
+    
+    NCDValRef res = NCDVal_NewExternalString(mem, tes->data, len, &tes->ref_target);
+    NCDRefTarget_Deref(&tes->ref_target);
+    return res;
+    
+fail1:
+    BFree(tes);
+fail0:
+    return NCDVal_NewInvalid();
+}
+
+#endif
+
+#ifndef NCDVAL_TEST_EXTERNAL_STRINGS
+
 NCDValRef NCDVal_NewStringBin (NCDValMem *mem, const uint8_t *data, size_t len)
 {
     NCDVal__AssertMem(mem);
@@ -567,6 +671,8 @@ fail:
     return NCDVal_NewInvalid();
 }
 
+#endif
+
 NCDValRef NCDVal_NewStringUninitialized (NCDValMem *mem, size_t len)
 {
     NCDVal__AssertMem(mem);
@@ -615,16 +721,56 @@ fail:
     return NCDVal_NewInvalid();
 }
 
+NCDValRef NCDVal_NewExternalString (NCDValMem *mem, const char *data, size_t len,
+                                    NCDRefTarget *ref_target)
+{
+    NCDVal__AssertMem(mem);
+    ASSERT(data)
+    NCDVal_AssertExternal(mem, data, len);
+    ASSERT(ref_target)
+    
+    bsize_t size = bsize_fromsize(sizeof(struct NCDVal__externalstring));
+    NCDVal__idx idx = NCDValMem__Alloc(mem, size, __alignof(struct NCDVal__externalstring));
+    if (idx < 0) {
+        goto fail;
+    }
+    
+    if (!NCDRefTarget_Ref(ref_target)) {
+        goto fail;
+    }
+    
+    struct NCDVal__externalstring *exs_e = NCDValMem__BufAt(mem, idx);
+    exs_e->type = EXTERNALSTRING_TYPE;
+    exs_e->data = data;
+    exs_e->length = len;
+    exs_e->ref.target = ref_target;
+    exs_e->ref.next = mem->first_ref;
+    
+    mem->first_ref = idx + offsetof(struct NCDVal__externalstring, ref);
+    
+    return NCDVal__Ref(mem, idx);
+    
+fail:
+    return NCDVal_NewInvalid();
+}
+
 const char * NCDVal_StringData (NCDValRef string)
 {
     ASSERT(NCDVal_IsString(string))
     
     void *ptr = NCDValMem__BufAt(string.mem, string.idx);
     
-    if (*(int *)ptr == IDSTRING_TYPE) {
-        struct NCDVal__idstring *ids_e = ptr;
-        const char *value = NCDStringIndex_Value(ids_e->string_index, ids_e->string_id);
-        return value;
+    switch (*(int *)ptr) {
+        case IDSTRING_TYPE: {
+            struct NCDVal__idstring *ids_e = ptr;
+            const char *value = NCDStringIndex_Value(ids_e->string_index, ids_e->string_id);
+            return value;
+        } break;
+        
+        case EXTERNALSTRING_TYPE: {
+            struct NCDVal__externalstring *exs_e = ptr;
+            return exs_e->data;
+        } break;
     }
     
     struct NCDVal__string *str_e = ptr;
@@ -637,10 +783,17 @@ size_t NCDVal_StringLength (NCDValRef string)
     
     void *ptr = NCDValMem__BufAt(string.mem, string.idx);
     
-    if (*(int *)ptr == IDSTRING_TYPE) {
-        struct NCDVal__idstring *ids_e = ptr;
-        const char *value = NCDStringIndex_Value(ids_e->string_index, ids_e->string_id);
-        return strlen(value);
+    switch (*(int *)ptr) {
+        case IDSTRING_TYPE: {
+            struct NCDVal__idstring *ids_e = ptr;
+            const char *value = NCDStringIndex_Value(ids_e->string_index, ids_e->string_id);
+            return strlen(value);
+        } break;
+        
+        case EXTERNALSTRING_TYPE: {
+            struct NCDVal__externalstring *exs_e = ptr;
+            return exs_e->length;
+        } break;
     }
     
     struct NCDVal__string *str_e = ptr;;
@@ -654,11 +807,26 @@ int NCDVal_StringNullTerminate (NCDValRef string, NCDValNullTermString *out)
     
     void *ptr = NCDValMem__BufAt(string.mem, string.idx);
     
-    if (*(int *)ptr == IDSTRING_TYPE) {
-        struct NCDVal__idstring *ids_e = ptr;
-        out->data = (char *)NCDStringIndex_Value(ids_e->string_index, ids_e->string_id);
-        out->is_allocated = 0;
-        return 1;
+    switch (*(int *)ptr) {
+        case IDSTRING_TYPE: {
+            struct NCDVal__idstring *ids_e = ptr;
+            out->data = (char *)NCDStringIndex_Value(ids_e->string_index, ids_e->string_id);
+            out->is_allocated = 0;
+            return 1;
+        } break;
+        
+        case EXTERNALSTRING_TYPE: {
+            struct NCDVal__externalstring *exs_e = ptr;
+            
+            char *copy = b_strdup_bin(exs_e->data, exs_e->length);
+            if (!copy) {
+                return 0;
+            }
+            
+            out->data = copy;
+            out->is_allocated = 1;
+            return 1;
+        } break;
     }
     
     struct NCDVal__string *str_e = ptr;
@@ -710,6 +878,14 @@ NCDStringIndex * NCDVal_IdStringStringIndex (NCDValRef idstring)
     return ids_e->string_index;
 }
 
+NCDRefTarget * NCDVal_ExternalStringTarget (NCDValRef externalstring)
+{
+    ASSERT(NCDVal_IsExternalString(externalstring))
+    
+    struct NCDVal__externalstring *exs_e = NCDValMem__BufAt(externalstring.mem, externalstring.idx);
+    return exs_e->ref.target;
+}
+
 int NCDVal_StringHasNulls (NCDValRef string)
 {
     ASSERT(NCDVal_IsString(string))
@@ -739,10 +915,18 @@ int NCDVal_StringEqualsId (NCDValRef string, NCD_string_id_t string_id,
     
     void *ptr = NCDValMem__BufAt(string.mem, string.idx);
     
-    if (*(int *)ptr == IDSTRING_TYPE) {
-        struct NCDVal__idstring *ids_e = ptr;
-        ASSERT(ids_e->string_index == string_index)
-        return ids_e->string_id == string_id;
+    switch (*(int *)ptr) {
+        case IDSTRING_TYPE: {
+            struct NCDVal__idstring *ids_e = ptr;
+            ASSERT(ids_e->string_index == string_index)
+            return ids_e->string_id == string_id;
+        } break;
+        
+        case EXTERNALSTRING_TYPE: {
+            struct NCDVal__externalstring *exs_e = ptr;
+            const char *string_data = NCDStringIndex_Value(string_index, string_id);
+            return strlen(string_data) == exs_e->length && !memcmp(string_data, exs_e->data, exs_e->length);
+        } break;
     }
     
     const char *string_data = NCDStringIndex_Value(string_index, string_id);
@@ -1056,7 +1240,8 @@ static void replaceprog_build_recurser (NCDValMem *mem, NCDVal__idx idx, size_t
     
     switch (*((int *)(ptr))) {
         case NCDVAL_STRING:
-        case IDSTRING_TYPE: {
+        case IDSTRING_TYPE:
+        case EXTERNALSTRING_TYPE: {
         } break;
         
         case NCDVAL_LIST: {

+ 42 - 0
ncd/NCDVal.h

@@ -36,6 +36,7 @@
 #include <misc/debug.h>
 #include <structure/CAvl.h>
 #include <ncd/NCDStringIndex.h>
+#include <ncd/NCDRefTarget.h>
 
 // these are implementation details. The interface is defined below.
 
@@ -51,6 +52,7 @@ typedef struct {
     char *buf;
     NCDVal__idx size;
     NCDVal__idx used;
+    NCDVal__idx first_ref;
     char fastbuf[NCDVAL_FASTBUF_SIZE];
 } NCDValMem;
 
@@ -63,6 +65,11 @@ typedef struct {
     NCDVal__idx idx;
 } NCDValSafeRef;
 
+struct NCDVal__ref {
+    NCDVal__idx next;
+    NCDRefTarget *target;
+};
+
 struct NCDVal__string {
     int type;
     NCDVal__idx length;
@@ -90,6 +97,13 @@ struct NCDVal__idstring {
     NCDStringIndex *string_index;
 };
 
+struct NCDVal__externalstring {
+    int type;
+    const char *data;
+    size_t length;
+    struct NCDVal__ref ref;
+};
+
 typedef struct NCDVal__mapelem NCDVal__maptree_entry;
 typedef NCDValMem *NCDVal__maptree_arg;
 
@@ -280,6 +294,14 @@ int NCDVal_IsString (NCDValRef val);
  */
 int NCDVal_IsIdString (NCDValRef val);
 
+/**
+ * Determines if a value is an external string value.
+ * See {@link NCDVal_NewExternalString} for an explanation of external
+ * string values.
+ * The value reference must not be an invalid reference.
+ */
+int NCDVal_IsExternalString (NCDValRef val);
+
 /**
  * Determines if a value is a string value which has no null bytes.
  * The value reference must not be an invalid reference.
@@ -330,6 +352,19 @@ NCDValRef NCDVal_NewStringUninitialized (NCDValMem *mem, size_t len);
 NCDValRef NCDVal_NewIdString (NCDValMem *mem, NCD_string_id_t string_id,
                               NCDStringIndex *string_index);
 
+/**
+ * Builds a new string value pointing to the given external data. A reference to
+ * the external data is taken using {@link NCDRefTarget}. The data must not change
+ * while the reference is being held. Like ID-strings, external strings are
+ * transparent for use. An external string can be recognized using
+ * {@link NCDVal_IsExternalString}.
+ * 
+ * Returns a reference to the new value, or an invalid reference
+ * on out of memory.
+ */
+NCDValRef NCDVal_NewExternalString (NCDValMem *mem, const char *data, size_t len,
+                                    NCDRefTarget *ref_target);
+
 /**
  * Returns a pointer to the data of a string value.
  * WARNING: the string data may not be null-terminated. To get a null-terminated
@@ -394,6 +429,13 @@ NCD_string_id_t NCDVal_IdStringId (NCDValRef idstring);
  */
 NCDStringIndex * NCDVal_IdStringStringIndex (NCDValRef idstring);
 
+/**
+ * Returns the reference target of an external string.
+ * The value given must be an external string value (which can be determined
+ * via {@link NCDVal_IsExternalString}).
+ */
+NCDRefTarget * NCDVal_ExternalStringTarget (NCDValRef externalstring);
+
 /**
  * Determines if the string value has any null bytes in its contents.
  * The value reference must point to a string value.