Răsfoiți Sursa

ncd: NCDVal: document that NCDVal_NewString() and NCDVal_NewStringBin() require the passed buffer is outside the memory
object we're building a value in. Fix NCDVal_NewCopy() which failed in that respect. Add a test.

ambrop7 13 ani în urmă
părinte
comite
ee998e23d6
3 a modificat fișierele cu 91 adăugiri și 3 ștergeri
  1. 31 0
      examples/ncdval_test.c
  2. 44 1
      ncd/NCDVal.c
  3. 16 2
      ncd/NCDVal.h

+ 31 - 0
examples/ncdval_test.c

@@ -54,6 +54,8 @@ static void print_value (NCDValRef val, unsigned int indent)
 
 int main ()
 {
+    // Some basic usage of values.
+    
     NCDValMem mem;
     NCDValMem_Init(&mem);
     
@@ -94,5 +96,34 @@ int main ()
     print_value(m1, 0);
     
     NCDValMem_Free(&mem);
+    
+    // Try to make copies of a string within the same memory object.
+    // This is an evil test because we cannot simply copy a string using e.g.
+    // NCDVal_NewStringBin() - it requires that the buffer passed
+    // be outside the memory object of the new string.
+    // We use NCDVal_NewCopy(), which takes care of this by creating
+    // an uninitialized string using NCDVal_NewStringUninitialized() and
+    // then copyng the data.
+    
+    NCDValMem_Init(&mem);
+    
+    NCDValRef s[100];
+    
+    s[0] = NCDVal_NewString(&mem, "Eeeeeeeeeeeevil.");
+    FORCE( !NCDVal_IsInvalid(s[0]) )
+    
+    for (int i = 1; i < 100; i++) {
+        s[i] = NCDVal_NewCopy(&mem, s[i - 1]);
+        FORCE( !NCDVal_IsInvalid(s[i]) )
+        ASSERT( !strcmp(NCDVal_StringValue(s[i - 1]), "Eeeeeeeeeeeevil.") )
+        ASSERT( !strcmp(NCDVal_StringValue(s[i]), "Eeeeeeeeeeeevil.") )
+    }
+    
+    for (int i = 0; i < 100; i++) {
+        ASSERT( !strcmp(NCDVal_StringValue(s[i]), "Eeeeeeeeeeeevil.") )
+    }
+    
+    NCDValMem_Free(&mem);
+    
     return 0;
 }

+ 44 - 1
ncd/NCDVal.c

@@ -111,6 +111,13 @@ static void NCDVal__AssertMem (NCDValMem *mem)
     ASSERT(!mem->buf || mem->size >= NCDVAL_FIRST_SIZE)
 }
 
+static void NCDVal_AssertExternal (NCDValMem *mem, const void *e_buf, size_t e_len)
+{
+    const char *e_cbuf = e_buf;
+    char *buf = (mem->buf ? mem->buf : mem->fastbuf);
+    ASSERT(e_cbuf >= buf + mem->size || e_cbuf + e_len <= buf)
+}
+
 static void NCDVal__AssertValOnly (NCDValMem *mem, NCDVal__idx idx)
 {
     ASSERT(idx >= 0)
@@ -234,7 +241,16 @@ NCDValRef NCDVal_NewCopy (NCDValMem *mem, NCDValRef val)
     
     switch (NCDVal_Type(val)) {
         case NCDVAL_STRING: {
-            return NCDVal_NewStringBin(mem, (const uint8_t *)NCDVal_StringValue(val), NCDVal_StringLength(val));
+            size_t len = NCDVal_StringLength(val);
+            
+            NCDValRef copy = NCDVal_NewStringUninitialized(mem, len);
+            if (NCDVal_IsInvalid(copy)) {
+                goto fail;
+            }
+            
+            memcpy((char *)NCDVal_StringValue(copy), NCDVal_StringValue(val), len);
+            
+            return copy;
         } break;
         
         case NCDVAL_LIST: {
@@ -414,6 +430,7 @@ NCDValRef NCDVal_NewString (NCDValMem *mem, const char *data)
 {
     NCDVal__AssertMem(mem);
     ASSERT(data)
+    NCDVal_AssertExternal(mem, data, strlen(data));
     
     return NCDVal_NewStringBin(mem, (const uint8_t *)data, strlen(data));
 }
@@ -422,6 +439,7 @@ NCDValRef NCDVal_NewStringBin (NCDValMem *mem, const uint8_t *data, size_t len)
 {
     NCDVal__AssertMem(mem);
     ASSERT(len == 0 || data)
+    NCDVal_AssertExternal(mem, data, len);
     
     if (len == SIZE_MAX) {
         goto fail;
@@ -447,6 +465,31 @@ fail:
     return NCDVal_NewInvalid();
 }
 
+NCDValRef NCDVal_NewStringUninitialized (NCDValMem *mem, size_t len)
+{
+    NCDVal__AssertMem(mem);
+    
+    if (len == SIZE_MAX) {
+        goto fail;
+    }
+    
+    bsize_t size = bsize_add(bsize_fromsize(sizeof(struct NCDVal__string)), bsize_fromsize(len + 1));
+    NCDVal__idx idx = NCDValMem__Alloc(mem, size, __alignof(struct NCDVal__string));
+    if (idx < 0) {
+        goto fail;
+    }
+    
+    struct NCDVal__string *str_e = NCDValMem__BufAt(mem, idx);
+    str_e->type = NCDVAL_STRING;
+    str_e->length = len;
+    str_e->data[len] = '\0';
+    
+    return NCDVal__Ref(mem, idx);
+    
+fail:
+    return NCDVal_NewInvalid();
+}
+
 const char * NCDVal_StringValue (NCDValRef string)
 {
     ASSERT(NCDVal_IsString(string))

+ 16 - 2
ncd/NCDVal.h

@@ -161,8 +161,8 @@ NCDValRef NCDVal_NewInvalid (void);
 
 /**
  * Copies a value into the specified memory object. The source
- * must not be an invalid reference, but may reside in a different
- * memory object.
+ * must not be an invalid reference, however it may reside in any memory
+ * object (including 'mem').
  * Returns a reference to the copied value. On out of memory, returns
  * an invalid reference.
  */
@@ -210,6 +210,9 @@ int NCDVal_IsStringNoNulls (NCDValRef val);
  * Equivalent to NCDVal_NewStringBin(mem, data, strlen(data)).
  * Returns a reference to the new value, or an invalid reference
  * on out of memory.
+ * WARNING: The buffer passed must NOT be part of any value in the
+ * memory object specified. In particular, you may NOT use this
+ * function to copy a string that resides in the same memory object.
  */
 NCDValRef NCDVal_NewString (NCDValMem *mem, const char *data);
 
@@ -217,9 +220,20 @@ NCDValRef NCDVal_NewString (NCDValMem *mem, const char *data);
  * Builds a new string value.
  * Returns a reference to the new value, or an invalid reference
  * on out of memory.
+ * WARNING: The buffer passed must NOT be part of any value in the
+ * memory object specified. In particular, you may NOT use this
+ * function to copy a string that resides in the same memory object.
  */
 NCDValRef NCDVal_NewStringBin (NCDValMem *mem, const uint8_t *data, size_t len);
 
+/**
+ * Builds a new string value of the given length with undefined contents.
+ * You can define the contents of the string later by copying to the address
+ * returned by {@link NCDVal_StringValue}. The terminating null byte is
+ * however automatically written.
+ */
+NCDValRef NCDVal_NewStringUninitialized (NCDValMem *mem, size_t len);
+
 /**
  * Returns a pointer to the data of a string value. An extra null byte
  * is always appended to the actual contents of the string.