Browse Source

Custom allocation mechanism

1. Make `json_sprinter` crash when invoked making the realloc
unreachable. It's quite difficult to get rid of it and I don't plan to
use sprintf capabilities of the library. If I ever need them the
trace_assert will remind me about that pesky realloc.

2. Make the memory allocation mechanism swappable through the
Allocator struct.
rexim 5 years ago
parent
commit
bb643f5ed0
4 changed files with 116 additions and 27 deletions
  1. 18 11
      frozen.c
  2. 11 0
      frozen.h
  3. 49 0
      stacktrace.h
  4. 38 16
      unit_test.c

+ 18 - 11
frozen.c

@@ -19,6 +19,7 @@
 #define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005+ */
 
 #include "frozen.h"
+#include "stacktrace.h"
 
 #include <ctype.h>
 #include <stdarg.h>
@@ -72,6 +73,11 @@ typedef unsigned _int64 uint64_t;
 #define JSON_ENABLE_ARRAY 1
 #endif
 
+Allocator allocator = {
+    .alloc = malloc,
+    .free = free
+};
+
 struct frozen {
   const char *end;
   const char *cur;
@@ -660,9 +666,9 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) {
            */
           pbuf = NULL;
           while (need_len < 0) {
-            free(pbuf);
+            allocator.free(pbuf);
             size *= 2;
-            if ((pbuf = (char *) malloc(size)) == NULL) break;
+            if ((pbuf = (char *) allocator.alloc(size)) == NULL) break;
             va_copy(ap_copy, ap);
             need_len = vsnprintf(pbuf, size, fmt2, ap_copy);
             va_end(ap_copy);
@@ -672,7 +678,7 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) {
            * resulting string doesn't fit into a stack-allocated buffer `buf`,
            * so we need to allocate a new buffer from heap and use it
            */
-          if ((pbuf = (char *) malloc(need_len + 1)) != NULL) {
+          if ((pbuf = (char *) allocator.alloc(need_len + 1)) != NULL) {
             va_copy(ap_copy, ap);
             vsnprintf(pbuf, need_len + 1, fmt2, ap_copy);
             va_end(ap_copy);
@@ -719,7 +725,7 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) {
 
         /* If buffer was allocated from heap, free it */
         if (pbuf != buf) {
-          free(pbuf);
+          allocator.free(pbuf);
           pbuf = NULL;
         }
       }
@@ -946,13 +952,13 @@ static void json_scanf_cb(void *callback_data, const char *name,
       } else {
         int unescaped_len = json_unescape(token->ptr, token->len, NULL, 0);
         if (unescaped_len >= 0 &&
-            (*dst = (char *) malloc(unescaped_len + 1)) != NULL) {
+            (*dst = (char *) allocator.alloc(unescaped_len + 1)) != NULL) {
           info->num_conversions++;
           if (json_unescape(token->ptr, token->len, *dst, unescaped_len) ==
               unescaped_len) {
             (*dst)[unescaped_len] = '\0';
           } else {
-            free(*dst);
+            allocator.free(*dst);
             *dst = NULL;
           }
         }
@@ -964,7 +970,7 @@ static void json_scanf_cb(void *callback_data, const char *name,
       char **dst = (char **) info->user_data;
       int i, len = token->len / 2;
       *(int *) info->target = len;
-      if ((*dst = (char *) malloc(len + 1)) != NULL) {
+      if ((*dst = (char *) allocator.alloc(len + 1)) != NULL) {
         for (i = 0; i < len; i++) {
           (*dst)[i] = hexdec(token->ptr + 2 * i);
         }
@@ -978,7 +984,7 @@ static void json_scanf_cb(void *callback_data, const char *name,
 #if JSON_ENABLE_BASE64
       char **dst = (char **) info->target;
       int len = token->len * 4 / 3 + 2;
-      if ((*dst = (char *) malloc(len + 1)) != NULL) {
+      if ((*dst = (char *) allocator.alloc(len + 1)) != NULL) {
         int n = b64dec(token->ptr, token->len, *dst);
         (*dst)[n] = '\0';
         *(int *) info->user_data = n;
@@ -1127,10 +1133,10 @@ char *json_fread(const char *path) {
     fclose(fp);
   } else {
     long size = ftell(fp);
-    if (size > 0 && (data = (char *) malloc(size + 1)) != NULL) {
+    if (size > 0 && (data = (char *) allocator.alloc(size + 1)) != NULL) {
       fseek(fp, 0, SEEK_SET); /* Some platforms might not have rewind(), Oo */
       if (fread(data, 1, size, fp) != (size_t) size) {
-        free(data);
+        allocator.free(data);
         data = NULL;
       } else {
         data[size] = '\0';
@@ -1357,7 +1363,7 @@ int json_prettify_file(const char *file_name) {
     }
     fclose(fp);
   }
-  free(s);
+  allocator.free(s);
   return res;
 }
 
@@ -1437,6 +1443,7 @@ void *json_next_elem(const char *s, int len, void *handle, const char *path,
 }
 
 static int json_sprinter(struct json_out *out, const char *str, size_t len) {
+  trace_assert(0 && "Reimplement this garbage without realloc");
   size_t old_len = out->u.buf.buf == NULL ? 0 : strlen(out->u.buf.buf);
   size_t new_len = len + old_len;
   char *p = (char *) realloc(out->u.buf.buf, new_len + 1);

+ 11 - 0
frozen.h

@@ -27,6 +27,17 @@ extern "C" {
 #include <stddef.h>
 #include <stdio.h>
 
+typedef void *(*Alloc)(size_t);
+typedef void (*Free)(void*);
+typedef void *(*Realloc)(void*, size_t);
+
+typedef struct {
+    Alloc alloc;
+    Free free;
+} Allocator;
+
+extern Allocator allocator;
+
 #if defined(_WIN32) && _MSC_VER < 1700
 typedef int bool;
 enum { false = 0, true = 1 };

+ 49 - 0
stacktrace.h

@@ -0,0 +1,49 @@
+#ifndef STACKTRACE_H_
+#define STACKTRACE_H_
+
+#include <stdlib.h>
+#include <stdio.h>
+#if defined(__GNUC__) && defined(__linux__)
+#include <execinfo.h>
+#include <unistd.h>
+#define N 100
+#endif
+
+#ifndef NDEBUG
+#define trace_assert(condition) (void)((condition) || (__trace_assert(__FILE__, __LINE__, __func__, #condition), 0))
+#else
+#define trace_assert(condition) (void)(condition)
+#endif
+
+static inline
+void print_stacktrace(void)
+{
+#if defined(__GNUC__) && defined(__linux__)
+    void *array[N];
+    int size;
+
+    size = backtrace(array, N);
+
+    if (size <= 0) {
+        return;
+    }
+
+    fprintf(stderr, "Stacktrace: \n");
+    backtrace_symbols_fd(array + 1, size - 1, STDERR_FILENO);
+#endif
+}
+
+static inline
+void __trace_assert(const char *file, int line, const char *function, const char *message)
+{
+    fprintf(
+        stderr,
+        "%s:%d: %s: Assertion `%s' failed\n",
+        file, line,
+        function,
+        message);
+    print_stacktrace();
+    abort();
+}
+
+#endif  // STACKTRACE_H_

+ 38 - 16
unit_test.c

@@ -51,11 +51,28 @@ const char *tok_type_names[] = {
     if (!(expr)) FAIL(#expr, __LINE__); \
   } while (0)
 
+#define MEMORY_CAPACITY 1024
+static char buffer[MEMORY_CAPACITY];
+static size_t buffer_size = 0;
+
+static
+void *epic_alloc(size_t size)
+{
+    trace_assert(buffer_size + size <= MEMORY_CAPACITY);
+    void *result = buffer + buffer_size;
+    buffer_size += size;
+    return result;
+}
+
+static void epic_free(void *ptr) {}
+
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 #define RUN_TEST(test)        \
   do {                        \
     const char *msg = test(); \
     if (msg) return msg;      \
+    printf("Consumed %d bytes of memory\n", buffer_size);   \
+    buffer_size = 0;                                        \
   } while (0)
 
 static int static_num_tests = 0;
@@ -503,7 +520,7 @@ static const char *test_scanf(void) {
   ASSERT(strcmp(buf, "0[17] 1[78] 2[-20] ") == 0);
   ASSERT(d != NULL);
   ASSERT(strcmp(d, "hi%20there") == 0);
-  free(d);
+  allocator.free(d);
 
   {
     /* Test errors */
@@ -541,7 +558,7 @@ static const char *test_scanf(void) {
     ASSERT(json_scanf(str, strlen(str), "{a: %Q}", &s) == 1);
     ASSERT(s != NULL);
     ASSERT(s[3] == '\0');
-    free(s);
+    allocator.free(s);
   }
 
   {
@@ -583,7 +600,7 @@ static const char *test_scanf(void) {
     char *result;
     ASSERT(json_scanf(str, strlen(str), "{a: %Q}", &result) == 1);
     ASSERT(strcmp(result, "foo\b\f\n\r\t\\") == 0);
-    free(result);
+    allocator.free(result);
 
     ASSERT(json_scanf(str, 9, "{a: %Q}", &result) == 0);
   }
@@ -593,7 +610,7 @@ static const char *test_scanf(void) {
     char *result;
     ASSERT(json_scanf(str, strlen(str), "{a: %Q}", &result) == 1);
     ASSERT(strcmp(result, "привет") == 0);
-    free(result);
+    allocator.free(result);
   }
 #if JSON_ENABLE_BASE64
   {
@@ -603,7 +620,7 @@ static const char *test_scanf(void) {
     ASSERT(json_scanf(str, strlen(str), "{a: %V}", &result, &len) == 1);
     ASSERT(len == 2);
     ASSERT(strcmp(result, "a2") == 0);
-    free(result);
+    allocator.free(result);
   }
 
   {
@@ -613,7 +630,7 @@ static const char *test_scanf(void) {
     ASSERT(json_scanf(str, strlen(str), "{a: %V}", &result, &len) == 1);
     ASSERT(len == 14);
     ASSERT(strcmp(result, "приветы") == 0);
-    free(result);
+    allocator.free(result);
   }
 #endif /* JSON_ENABLE_BASE64 */
 #if JSON_ENABLE_HEX
@@ -624,7 +641,7 @@ static const char *test_scanf(void) {
     ASSERT(json_scanf(str, strlen(str), "{a: %H}", &len, &result) == 1);
     ASSERT(len == 4);
     ASSERT(strcmp(result, "abc ") == 0);
-    free(result);
+    allocator.free(result);
   }
 #endif /* JSON_ENABLE_HEX */
   {
@@ -632,7 +649,7 @@ static const char *test_scanf(void) {
     char *result = (char *) 123;
     ASSERT(json_scanf(str, strlen(str), "{a: %Q}", &result) == 0);
     ASSERT(result == NULL);
-    free(result);
+    allocator.free(result);
   }
 
   {
@@ -742,14 +759,14 @@ static const char *test_parse_string(void) {
 static const char *test_eos(void) {
   const char *s = "{\"a\": 12345}";
   size_t n = 999;
-  char *buf = (char *) malloc(n);
+  char *buf = (char *) allocator.alloc(n);
   int s_len = strlen(s), a = 0;
   ASSERT(buf != NULL);
   memset(buf, 'x', n);
   memcpy(buf, s, s_len);
   ASSERT(json_scanf(buf, n, "{a:%d}", &a) == 1);
   ASSERT(a == 12345);
-  free(buf);
+  allocator.free(buf);
   return NULL;
 }
 
@@ -758,7 +775,7 @@ static int compare_file(const char *file_name, const char *s) {
   char *p = json_fread(file_name);
   if (p == NULL) return res;
   res = strcmp(p, s);
-  free(p);
+  allocator.free(p);
   return res == 0;
 }
 
@@ -770,7 +787,7 @@ static const char *test_fprintf(void) {
   p = json_fread(fname);
   ASSERT(p != NULL);
   ASSERT(strcmp(p, result) == 0);
-  free(p);
+  allocator.free(p);
   remove(fname);
   ASSERT(json_fread(fname) == NULL);
   return NULL;
@@ -1016,7 +1033,7 @@ static const char *test_json_printf_hex(void) {
 #else
   ASSERT(s == NULL);
 #endif
-  free(s);
+  allocator.free(s);
   return NULL;
 }
 
@@ -1028,13 +1045,15 @@ static const char *test_json_printf_base64(void) {
   const char *r = "{\"a\":77,\"b\":}";
 #endif
   ASSERT(strcmp(s, r) == 0);
-  free(s);
+  allocator.free(s);
   return NULL;
 }
 
+
+
 static const char *run_all_tests(void) {
-  RUN_TEST(test_json_printf_hex);
-  RUN_TEST(test_json_printf_base64);
+  // RUN_TEST(test_json_printf_hex);
+  // RUN_TEST(test_json_printf_base64);
   RUN_TEST(test_json_next);
   RUN_TEST(test_prettify);
   RUN_TEST(test_eos);
@@ -1052,6 +1071,9 @@ static const char *run_all_tests(void) {
 }
 
 int main(void) {
+  allocator.alloc = epic_alloc;
+  allocator.free = epic_free;
+
   const char *fail_msg = run_all_tests();
   printf("%s, tests run: %d\n", fail_msg ? "FAIL" : "PASS", static_num_tests);
   return fail_msg == NULL ? EXIT_SUCCESS : EXIT_FAILURE;