Procházet zdrojové kódy

Bring back the legacy version of jim.h

rexim před 1 měsícem
rodič
revize
8b23b42ed2
4 změnil soubory, kde provedl 778 přidání a 381 odebrání
  1. 0 381
      jim.h
  2. 1 0
      jim.h
  3. 392 0
      jim1.h
  4. 385 0
      jim2.h

+ 0 - 381
jim.h

@@ -1,381 +0,0 @@
-#ifndef JIM_H_
-#define JIM_H_
-
-#ifndef JIM_SCOPES_CAPACITY
-#define JIM_SCOPES_CAPACITY 128
-#endif // JIM_SCOPES_CAPACITY
-
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
-typedef enum {
-    JIM_OK = 0,
-    JIM_SCOPES_UNDERFLOW,
-    JIM_OUT_OF_SCOPE_KEY,
-    JIM_DOUBLE_KEY
-} Jim_Error;
-
-const char *jim_error_string(Jim_Error error);
-
-typedef enum {
-    JIM_ARRAY_SCOPE,
-    JIM_OBJECT_SCOPE,
-} Jim_Scope_Kind;
-
-typedef struct {
-    Jim_Scope_Kind kind;
-    int tail;
-    int key;
-} Jim_Scope;
-
-typedef struct {
-    Jim_Error error;
-    char *sink;
-    size_t sink_count;
-    size_t sink_capacity;
-    Jim_Scope *scopes;
-    size_t scopes_count;
-    size_t scopes_capacity;
-} Jim;
-
-void jim_null(Jim *jim);
-void jim_bool(Jim *jim, int boolean);
-void jim_integer(Jim *jim, long long int x);
-void jim_float(Jim *jim, double x, int precision);
-void jim_string(Jim *jim, const char *str);
-void jim_string_sized(Jim *jim, const char *str, size_t size);
-
-void jim_element_begin(Jim *jim);
-void jim_element_end(Jim *jim);
-
-void jim_array_begin(Jim *jim);
-void jim_array_end(Jim *jim);
-
-void jim_object_begin(Jim *jim);
-void jim_member_key(Jim *jim, const char *str);
-void jim_member_key_sized(Jim *jim, const char *str, size_t size);
-void jim_object_end(Jim *jim);
-
-#endif // JIM_H_
-
-#ifdef JIM_IMPLEMENTATION
-
-static void jim_scope_push(Jim *jim, Jim_Scope_Kind kind)
-{
-    if (jim->error == JIM_OK) {
-        if (jim->scopes_count >= jim->scopes_capacity) {
-            if (jim->scopes_capacity == 0) jim->scopes_capacity = JIM_SCOPES_CAPACITY;
-            else jim->scopes_capacity *= 2;
-            jim->scopes = realloc(jim->scopes, sizeof(*jim->scopes)*jim->scopes_capacity);
-            assert(jim->scopes);
-        }
-        jim->scopes[jim->scopes_count].kind = kind;
-        jim->scopes[jim->scopes_count].tail = 0;
-        jim->scopes[jim->scopes_count].key = 0;
-        jim->scopes_count += 1;
-    }
-}
-
-static void jim_scope_pop(Jim *jim)
-{
-    if (jim->error == JIM_OK) {
-        if (jim->scopes_count > 0) {
-            jim->scopes_count--;
-        } else {
-            jim->error = JIM_SCOPES_UNDERFLOW;
-        }
-    }
-}
-
-static Jim_Scope *jim_current_scope(Jim *jim)
-{
-    if (jim->error == JIM_OK) {
-        if (jim->scopes_count > 0) {
-            return &jim->scopes[jim->scopes_count - 1];
-        }
-    }
-
-    return NULL;
-}
-
-static void jim_write(Jim *jim, const char *buffer, size_t size)
-{
-    if (jim->error == JIM_OK) {
-        while (jim->sink_count + size >= jim->sink_capacity) {
-            // TODO: rename JIM_SCOPES_CAPACITY to something else since it's used by both sink and scopes
-            if (jim->sink_capacity == 0) jim->sink_capacity = JIM_SCOPES_CAPACITY;
-            else jim->sink_capacity *= 2;
-            jim->sink = realloc(jim->sink, sizeof(*jim->sink)*jim->sink_capacity);
-        }
-        memcpy(jim->sink + jim->sink_count, buffer, size);
-        jim->sink_count += size;
-    }
-}
-
-static void jim_write_cstr(Jim *jim, const char *cstr)
-{
-    if (jim->error == JIM_OK) {
-        jim_write(jim, cstr, strlen(cstr));
-    }
-}
-
-static int jim_get_utf8_char_len(unsigned char ch)
-{
-    if ((ch & 0x80) == 0) return 1;
-    switch (ch & 0xf0) {
-    case 0xf0:
-        return 4;
-    case 0xe0:
-        return 3;
-    default:
-        return 2;
-    }
-}
-
-void jim_element_begin(Jim *jim)
-{
-    if (jim->error == JIM_OK) {
-        Jim_Scope *scope = jim_current_scope(jim);
-        if (scope && scope->tail && !scope->key) {
-            jim_write_cstr(jim, ",");
-        }
-    }
-}
-
-void jim_element_end(Jim *jim)
-{
-    if (jim->error == JIM_OK) {
-        Jim_Scope *scope = jim_current_scope(jim);
-        if (scope) {
-            scope->tail = 1;
-            scope->key = 0;
-        }
-    }
-}
-
-const char *jim_error_string(Jim_Error error)
-{
-    // TODO(#1): error strings are not particularly useful
-    switch (error) {
-    case JIM_OK:
-        return "There is no error. The developer of this software just had a case of \"Task failed successfully\" https://i.imgur.com/Bdb3rkq.jpg - Please contact the developer and tell them that they are very lazy for not checking errors properly.";
-    case JIM_SCOPES_UNDERFLOW:
-        return "Stack of Scopes Underflow";
-    case JIM_OUT_OF_SCOPE_KEY:
-        return "Out of Scope key";
-    case JIM_DOUBLE_KEY:
-        return "Tried to set the member key twice";
-    default:
-        return NULL;
-    }
-}
-
-void jim_null(Jim *jim)
-{
-    if (jim->error == JIM_OK) {
-        jim_element_begin(jim);
-        jim_write_cstr(jim, "null");
-        jim_element_end(jim);
-    }
-}
-
-void jim_bool(Jim *jim, int boolean)
-{
-    if (jim->error == JIM_OK) {
-        jim_element_begin(jim);
-        if (boolean) {
-            jim_write_cstr(jim, "true");
-        } else {
-            jim_write_cstr(jim, "false");
-        }
-        jim_element_end(jim);
-    }
-}
-
-static void jim_integer_no_element(Jim *jim, long long int x)
-{
-    if (jim->error == JIM_OK) {
-        if (x < 0) {
-            jim_write_cstr(jim, "-");
-            x = -x;
-        }
-
-        if (x == 0) {
-            jim_write_cstr(jim, "0");
-        } else {
-            char buffer[64];
-            size_t count = 0;
-
-            while (x > 0) {
-                buffer[count++] = (x % 10) + '0';
-                x /= 10;
-            }
-
-            for (size_t i = 0; i < count / 2; ++i) {
-                char t = buffer[i];
-                buffer[i] = buffer[count - i - 1];
-                buffer[count - i - 1] = t;
-            }
-
-            jim_write(jim, buffer, count);
-        }
-
-    }
-}
-
-void jim_integer(Jim *jim, long long int x)
-{
-    if (jim->error == JIM_OK) {
-        jim_element_begin(jim);
-        jim_integer_no_element(jim, x);
-        jim_element_end(jim);
-    }
-}
-
-static int is_nan_or_inf(double x)
-{
-    unsigned long long int mask = (1ULL << 11ULL) - 1ULL;
-    return (((*(unsigned long long int*) &x) >> 52ULL) & mask) == mask;
-}
-
-void jim_float(Jim *jim, double x, int precision)
-{
-    if (jim->error == JIM_OK) {
-        if (is_nan_or_inf(x)) {
-            jim_null(jim);
-        } else {
-            jim_element_begin(jim);
-
-            jim_integer_no_element(jim, (long long int) x);
-            x -= (double) (long long int) x;
-            while (precision-- > 0) {
-                x *= 10.0;
-            }
-            jim_write_cstr(jim, ".");
-
-            long long int y = (long long int) x;
-            if (y < 0) {
-                y = -y;
-            }
-            jim_integer_no_element(jim, y);
-
-            jim_element_end(jim);
-        }
-    }
-}
-
-static void jim_string_sized_no_element(Jim *jim, const char *str, size_t size)
-{
-    if (jim->error == JIM_OK) {
-        const char *hex_digits = "0123456789abcdef";
-        const char *specials = "btnvfr";
-        const char *p = str;
-        size_t len = size;
-
-        jim_write_cstr(jim, "\"");
-        size_t cl;
-        for (size_t i = 0; i < len; i++) {
-            unsigned char ch = ((unsigned char *) p)[i];
-            if (ch == '"' || ch == '\\') {
-                jim_write(jim, "\\", 1);
-                jim_write(jim, p + i, 1);
-            } else if (ch >= '\b' && ch <= '\r') {
-                jim_write(jim, "\\", 1);
-                jim_write(jim, &specials[ch - '\b'], 1);
-            } else if (0x20 <= ch && ch <= 0x7F) { // is printable
-                jim_write(jim, p + i, 1);
-            } else if ((cl = jim_get_utf8_char_len(ch)) == 1) {
-                jim_write(jim, "\\u00", 4);
-                jim_write(jim, &hex_digits[(ch >> 4) % 0xf], 1);
-                jim_write(jim, &hex_digits[ch % 0xf], 1);
-            } else {
-                jim_write(jim, p + i, cl);
-                i += cl - 1;
-            }
-        }
-
-        jim_write_cstr(jim, "\"");
-    }
-}
-
-void jim_string_sized(Jim *jim, const char *str, size_t size)
-{
-    if (jim->error == JIM_OK) {
-        jim_element_begin(jim);
-        jim_string_sized_no_element(jim, str, size);
-        jim_element_end(jim);
-    }
-}
-
-void jim_string(Jim *jim, const char *str)
-{
-    if (jim->error == JIM_OK) {
-        jim_string_sized(jim, str, strlen(str));
-    }
-}
-
-void jim_array_begin(Jim *jim)
-{
-    if (jim->error == JIM_OK) {
-        jim_element_begin(jim);
-        jim_write_cstr(jim, "[");
-        jim_scope_push(jim, JIM_ARRAY_SCOPE);
-    }
-}
-
-
-void jim_array_end(Jim *jim)
-{
-    if (jim->error == JIM_OK) {
-        jim_write_cstr(jim, "]");
-        jim_scope_pop(jim);
-        jim_element_end(jim);
-    }
-}
-
-void jim_object_begin(Jim *jim)
-{
-    if (jim->error == JIM_OK) {
-        jim_element_begin(jim);
-        jim_write_cstr(jim, "{");
-        jim_scope_push(jim, JIM_OBJECT_SCOPE);
-    }
-}
-
-void jim_member_key(Jim *jim, const char *str)
-{
-    if (jim->error == JIM_OK) {
-        jim_member_key_sized(jim, str, strlen(str));
-    }
-}
-
-void jim_member_key_sized(Jim *jim, const char *str, size_t size)
-{
-    if (jim->error == JIM_OK) {
-        jim_element_begin(jim);
-        Jim_Scope *scope = jim_current_scope(jim);
-        if (scope && scope->kind == JIM_OBJECT_SCOPE) {
-            if (!scope->key) {
-                jim_string_sized_no_element(jim, str, size);
-                jim_write_cstr(jim, ":");
-                scope->key = 1;
-            } else {
-                jim->error = JIM_DOUBLE_KEY;
-            }
-        } else {
-            jim->error = JIM_OUT_OF_SCOPE_KEY;
-        }
-    }
-}
-
-void jim_object_end(Jim *jim)
-{
-    if (jim->error == JIM_OK) {
-        jim_write_cstr(jim, "}");
-        jim_scope_pop(jim);
-        jim_element_end(jim);
-    }
-}
-
-#endif // JIM_IMPLEMENTATION

+ 1 - 0
jim.h

@@ -0,0 +1 @@
+/home/rexim/Programming/tsoding/jim/jim2.h

+ 392 - 0
jim1.h

@@ -0,0 +1,392 @@
+// Jim 1.0
+//
+// This is a legacy version of jim which uses fixed array for scopes and Jim_Sink/Jim_Write
+// interface to abstract away the IO. Please use Jim 2.0 for new projects.
+#ifndef JIM_H_
+#define JIM_H_
+
+#ifndef JIM_SCOPES_CAPACITY
+#define JIM_SCOPES_CAPACITY 128
+#endif // JIM_SCOPES_CAPACITY
+
+#include <stddef.h>
+
+typedef void* Jim_Sink;
+typedef size_t (*Jim_Write)(const void *ptr, size_t size, size_t nmemb, Jim_Sink sink);
+
+typedef enum {
+    JIM_OK = 0,
+    JIM_WRITE_ERROR,
+    JIM_SCOPES_OVERFLOW,
+    JIM_SCOPES_UNDERFLOW,
+    JIM_OUT_OF_SCOPE_KEY,
+    JIM_DOUBLE_KEY
+} Jim_Error;
+
+const char *jim_error_string(Jim_Error error);
+
+typedef enum {
+    JIM_ARRAY_SCOPE,
+    JIM_OBJECT_SCOPE,
+} Jim_Scope_Kind;
+
+typedef struct {
+    Jim_Scope_Kind kind;
+    int tail;
+    int key;
+} Jim_Scope;
+
+typedef struct {
+    Jim_Sink sink;
+    Jim_Write write;
+    Jim_Error error;
+    Jim_Scope scopes[JIM_SCOPES_CAPACITY];
+    size_t scopes_size;
+} Jim;
+
+void jim_null(Jim *jim);
+void jim_bool(Jim *jim, int boolean);
+void jim_integer(Jim *jim, long long int x);
+void jim_float(Jim *jim, double x, int precision);
+void jim_string(Jim *jim, const char *str);
+void jim_string_sized(Jim *jim, const char *str, size_t size);
+
+void jim_element_begin(Jim *jim);
+void jim_element_end(Jim *jim);
+
+void jim_array_begin(Jim *jim);
+void jim_array_end(Jim *jim);
+
+void jim_object_begin(Jim *jim);
+void jim_member_key(Jim *jim, const char *str);
+void jim_member_key_sized(Jim *jim, const char *str, size_t size);
+void jim_object_end(Jim *jim);
+
+#endif // JIM_H_
+
+#ifdef JIM_IMPLEMENTATION
+
+static size_t jim_strlen(const char *s)
+{
+    size_t count = 0;
+    while (*(s + count)) {
+        count += 1;
+    }
+    return count;
+}
+
+static void jim_scope_push(Jim *jim, Jim_Scope_Kind kind)
+{
+    if (jim->error == JIM_OK) {
+        if (jim->scopes_size < JIM_SCOPES_CAPACITY) {
+            jim->scopes[jim->scopes_size].kind = kind;
+            jim->scopes[jim->scopes_size].tail = 0;
+            jim->scopes[jim->scopes_size].key = 0;
+            jim->scopes_size += 1;
+        } else {
+            jim->error = JIM_SCOPES_OVERFLOW;
+        }
+    }
+}
+
+static void jim_scope_pop(Jim *jim)
+{
+    if (jim->error == JIM_OK) {
+        if (jim->scopes_size > 0) {
+            jim->scopes_size--;
+        } else {
+            jim->error = JIM_SCOPES_UNDERFLOW;
+        }
+    }
+}
+
+static Jim_Scope *jim_current_scope(Jim *jim)
+{
+    if (jim->error == JIM_OK) {
+        if (jim->scopes_size > 0) {
+            return &jim->scopes[jim->scopes_size - 1];
+        }
+    }
+
+    return NULL;
+}
+
+static void jim_write(Jim *jim, const char *buffer, size_t size)
+{
+    if (jim->error == JIM_OK) {
+        if (jim->write(buffer, 1, size, jim->sink) < size) {
+            jim->error = 1;
+        }
+    }
+}
+
+static void jim_write_cstr(Jim *jim, const char *cstr)
+{
+    if (jim->error == JIM_OK) {
+        jim_write(jim, cstr, jim_strlen(cstr));
+    }
+}
+
+static int jim_get_utf8_char_len(unsigned char ch)
+{
+    if ((ch & 0x80) == 0) return 1;
+    switch (ch & 0xf0) {
+    case 0xf0:
+        return 4;
+    case 0xe0:
+        return 3;
+    default:
+        return 2;
+    }
+}
+
+void jim_element_begin(Jim *jim)
+{
+    if (jim->error == JIM_OK) {
+        Jim_Scope *scope = jim_current_scope(jim);
+        if (scope && scope->tail && !scope->key) {
+            jim_write_cstr(jim, ",");
+        }
+    }
+}
+
+void jim_element_end(Jim *jim)
+{
+    if (jim->error == JIM_OK) {
+        Jim_Scope *scope = jim_current_scope(jim);
+        if (scope) {
+            scope->tail = 1;
+            scope->key = 0;
+        }
+    }
+}
+
+const char *jim_error_string(Jim_Error error)
+{
+    // TODO(#1): error strings are not particularly useful
+    switch (error) {
+    case JIM_OK:
+        return "There is no error. The developer of this software just had a case of \"Task failed successfully\" https://i.imgur.com/Bdb3rkq.jpg - Please contact the developer and tell them that they are very lazy for not checking errors properly.";
+    case JIM_WRITE_ERROR:
+        return "Write error";
+    case JIM_SCOPES_OVERFLOW:
+        return "Stack of Scopes Overflow";
+    case JIM_SCOPES_UNDERFLOW:
+        return "Stack of Scopes Underflow";
+    case JIM_OUT_OF_SCOPE_KEY:
+        return "Out of Scope key";
+    case JIM_DOUBLE_KEY:
+        return "Tried to set the member key twice";
+    default:
+        return NULL;
+    }
+}
+
+void jim_null(Jim *jim)
+{
+    if (jim->error == JIM_OK) {
+        jim_element_begin(jim);
+        jim_write_cstr(jim, "null");
+        jim_element_end(jim);
+    }
+}
+
+void jim_bool(Jim *jim, int boolean)
+{
+    if (jim->error == JIM_OK) {
+        jim_element_begin(jim);
+        if (boolean) {
+            jim_write_cstr(jim, "true");
+        } else {
+            jim_write_cstr(jim, "false");
+        }
+        jim_element_end(jim);
+    }
+}
+
+static void jim_integer_no_element(Jim *jim, long long int x)
+{
+    if (jim->error == JIM_OK) {
+        if (x < 0) {
+            jim_write_cstr(jim, "-");
+            x = -x;
+        }
+
+        if (x == 0) {
+            jim_write_cstr(jim, "0");
+        } else {
+            char buffer[64];
+            size_t count = 0;
+
+            while (x > 0) {
+                buffer[count++] = (x % 10) + '0';
+                x /= 10;
+            }
+
+            for (size_t i = 0; i < count / 2; ++i) {
+                char t = buffer[i];
+                buffer[i] = buffer[count - i - 1];
+                buffer[count - i - 1] = t;
+            }
+
+            jim_write(jim, buffer, count);
+        }
+
+    }
+}
+
+void jim_integer(Jim *jim, long long int x)
+{
+    if (jim->error == JIM_OK) {
+        jim_element_begin(jim);
+        jim_integer_no_element(jim, x);
+        jim_element_end(jim);
+    }
+}
+
+static int is_nan_or_inf(double x)
+{
+    unsigned long long int mask = (1ULL << 11ULL) - 1ULL;
+    return (((*(unsigned long long int*) &x) >> 52ULL) & mask) == mask;
+}
+
+void jim_float(Jim *jim, double x, int precision)
+{
+    if (jim->error == JIM_OK) {
+        if (is_nan_or_inf(x)) {
+            jim_null(jim);
+        } else {
+            jim_element_begin(jim);
+
+            jim_integer_no_element(jim, (long long int) x);
+            x -= (double) (long long int) x;
+            while (precision-- > 0) {
+                x *= 10.0;
+            }
+            jim_write_cstr(jim, ".");
+
+            long long int y = (long long int) x;
+            if (y < 0) {
+                y = -y;
+            }
+            jim_integer_no_element(jim, y);
+
+            jim_element_end(jim);
+        }
+    }
+}
+
+static void jim_string_sized_no_element(Jim *jim, const char *str, size_t size)
+{
+    if (jim->error == JIM_OK) {
+        const char *hex_digits = "0123456789abcdef";
+        const char *specials = "btnvfr";
+        const char *p = str;
+        size_t len = size;
+
+        jim_write_cstr(jim, "\"");
+        size_t cl;
+        for (size_t i = 0; i < len; i++) {
+            unsigned char ch = ((unsigned char *) p)[i];
+            if (ch == '"' || ch == '\\') {
+                jim_write(jim, "\\", 1);
+                jim_write(jim, p + i, 1);
+            } else if (ch >= '\b' && ch <= '\r') {
+                jim_write(jim, "\\", 1);
+                jim_write(jim, &specials[ch - '\b'], 1);
+            } else if (0x20 <= ch && ch <= 0x7F) { // is printable
+                jim_write(jim, p + i, 1);
+            } else if ((cl = jim_get_utf8_char_len(ch)) == 1) {
+                jim_write(jim, "\\u00", 4);
+                jim_write(jim, &hex_digits[(ch >> 4) % 0xf], 1);
+                jim_write(jim, &hex_digits[ch % 0xf], 1);
+            } else {
+                jim_write(jim, p + i, cl);
+                i += cl - 1;
+            }
+        }
+
+        jim_write_cstr(jim, "\"");
+    }
+}
+
+void jim_string_sized(Jim *jim, const char *str, size_t size)
+{
+    if (jim->error == JIM_OK) {
+        jim_element_begin(jim);
+        jim_string_sized_no_element(jim, str, size);
+        jim_element_end(jim);
+    }
+}
+
+void jim_string(Jim *jim, const char *str)
+{
+    if (jim->error == JIM_OK) {
+        jim_string_sized(jim, str, jim_strlen(str));
+    }
+}
+
+void jim_array_begin(Jim *jim)
+{
+    if (jim->error == JIM_OK) {
+        jim_element_begin(jim);
+        jim_write_cstr(jim, "[");
+        jim_scope_push(jim, JIM_ARRAY_SCOPE);
+    }
+}
+
+
+void jim_array_end(Jim *jim)
+{
+    if (jim->error == JIM_OK) {
+        jim_write_cstr(jim, "]");
+        jim_scope_pop(jim);
+        jim_element_end(jim);
+    }
+}
+
+void jim_object_begin(Jim *jim)
+{
+    if (jim->error == JIM_OK) {
+        jim_element_begin(jim);
+        jim_write_cstr(jim, "{");
+        jim_scope_push(jim, JIM_OBJECT_SCOPE);
+    }
+}
+
+void jim_member_key(Jim *jim, const char *str)
+{
+    if (jim->error == JIM_OK) {
+        jim_member_key_sized(jim, str, jim_strlen(str));
+    }
+}
+
+void jim_member_key_sized(Jim *jim, const char *str, size_t size)
+{
+    if (jim->error == JIM_OK) {
+        jim_element_begin(jim);
+        Jim_Scope *scope = jim_current_scope(jim);
+        if (scope && scope->kind == JIM_OBJECT_SCOPE) {
+            if (!scope->key) {
+                jim_string_sized_no_element(jim, str, size);
+                jim_write_cstr(jim, ":");
+                scope->key = 1;
+            } else {
+                jim->error = JIM_DOUBLE_KEY;
+            }
+        } else {
+            jim->error = JIM_OUT_OF_SCOPE_KEY;
+        }
+    }
+}
+
+void jim_object_end(Jim *jim)
+{
+    if (jim->error == JIM_OK) {
+        jim_write_cstr(jim, "}");
+        jim_scope_pop(jim);
+        jim_element_end(jim);
+    }
+}
+
+#endif // JIM_IMPLEMENTATION

+ 385 - 0
jim2.h

@@ -0,0 +1,385 @@
+// Jim 2.0
+//
+// Main difference from Jim 1.0 is using dynamic arrays for scopes and string builder for sink.
+
+#ifndef JIM_H_
+#define JIM_H_
+
+#ifndef JIM_SCOPES_CAPACITY
+#define JIM_SCOPES_CAPACITY 128
+#endif // JIM_SCOPES_CAPACITY
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef enum {
+    JIM_OK = 0,
+    JIM_SCOPES_UNDERFLOW,
+    JIM_OUT_OF_SCOPE_KEY,
+    JIM_DOUBLE_KEY
+} Jim_Error;
+
+const char *jim_error_string(Jim_Error error);
+
+typedef enum {
+    JIM_ARRAY_SCOPE,
+    JIM_OBJECT_SCOPE,
+} Jim_Scope_Kind;
+
+typedef struct {
+    Jim_Scope_Kind kind;
+    int tail;
+    int key;
+} Jim_Scope;
+
+typedef struct {
+    Jim_Error error;
+    char *sink;
+    size_t sink_count;
+    size_t sink_capacity;
+    Jim_Scope *scopes;
+    size_t scopes_count;
+    size_t scopes_capacity;
+} Jim;
+
+void jim_null(Jim *jim);
+void jim_bool(Jim *jim, int boolean);
+void jim_integer(Jim *jim, long long int x);
+void jim_float(Jim *jim, double x, int precision);
+void jim_string(Jim *jim, const char *str);
+void jim_string_sized(Jim *jim, const char *str, size_t size);
+
+void jim_element_begin(Jim *jim);
+void jim_element_end(Jim *jim);
+
+void jim_array_begin(Jim *jim);
+void jim_array_end(Jim *jim);
+
+void jim_object_begin(Jim *jim);
+void jim_member_key(Jim *jim, const char *str);
+void jim_member_key_sized(Jim *jim, const char *str, size_t size);
+void jim_object_end(Jim *jim);
+
+#endif // JIM_H_
+
+#ifdef JIM_IMPLEMENTATION
+
+static void jim_scope_push(Jim *jim, Jim_Scope_Kind kind)
+{
+    if (jim->error == JIM_OK) {
+        if (jim->scopes_count >= jim->scopes_capacity) {
+            if (jim->scopes_capacity == 0) jim->scopes_capacity = JIM_SCOPES_CAPACITY;
+            else jim->scopes_capacity *= 2;
+            jim->scopes = realloc(jim->scopes, sizeof(*jim->scopes)*jim->scopes_capacity);
+            assert(jim->scopes);
+        }
+        jim->scopes[jim->scopes_count].kind = kind;
+        jim->scopes[jim->scopes_count].tail = 0;
+        jim->scopes[jim->scopes_count].key = 0;
+        jim->scopes_count += 1;
+    }
+}
+
+static void jim_scope_pop(Jim *jim)
+{
+    if (jim->error == JIM_OK) {
+        if (jim->scopes_count > 0) {
+            jim->scopes_count--;
+        } else {
+            jim->error = JIM_SCOPES_UNDERFLOW;
+        }
+    }
+}
+
+static Jim_Scope *jim_current_scope(Jim *jim)
+{
+    if (jim->error == JIM_OK) {
+        if (jim->scopes_count > 0) {
+            return &jim->scopes[jim->scopes_count - 1];
+        }
+    }
+
+    return NULL;
+}
+
+static void jim_write(Jim *jim, const char *buffer, size_t size)
+{
+    if (jim->error == JIM_OK) {
+        while (jim->sink_count + size >= jim->sink_capacity) {
+            // TODO: rename JIM_SCOPES_CAPACITY to something else since it's used by both sink and scopes
+            if (jim->sink_capacity == 0) jim->sink_capacity = JIM_SCOPES_CAPACITY;
+            else jim->sink_capacity *= 2;
+            jim->sink = realloc(jim->sink, sizeof(*jim->sink)*jim->sink_capacity);
+        }
+        memcpy(jim->sink + jim->sink_count, buffer, size);
+        jim->sink_count += size;
+    }
+}
+
+static void jim_write_cstr(Jim *jim, const char *cstr)
+{
+    if (jim->error == JIM_OK) {
+        jim_write(jim, cstr, strlen(cstr));
+    }
+}
+
+static int jim_get_utf8_char_len(unsigned char ch)
+{
+    if ((ch & 0x80) == 0) return 1;
+    switch (ch & 0xf0) {
+    case 0xf0:
+        return 4;
+    case 0xe0:
+        return 3;
+    default:
+        return 2;
+    }
+}
+
+void jim_element_begin(Jim *jim)
+{
+    if (jim->error == JIM_OK) {
+        Jim_Scope *scope = jim_current_scope(jim);
+        if (scope && scope->tail && !scope->key) {
+            jim_write_cstr(jim, ",");
+        }
+    }
+}
+
+void jim_element_end(Jim *jim)
+{
+    if (jim->error == JIM_OK) {
+        Jim_Scope *scope = jim_current_scope(jim);
+        if (scope) {
+            scope->tail = 1;
+            scope->key = 0;
+        }
+    }
+}
+
+const char *jim_error_string(Jim_Error error)
+{
+    // TODO(#1): error strings are not particularly useful
+    switch (error) {
+    case JIM_OK:
+        return "There is no error. The developer of this software just had a case of \"Task failed successfully\" https://i.imgur.com/Bdb3rkq.jpg - Please contact the developer and tell them that they are very lazy for not checking errors properly.";
+    case JIM_SCOPES_UNDERFLOW:
+        return "Stack of Scopes Underflow";
+    case JIM_OUT_OF_SCOPE_KEY:
+        return "Out of Scope key";
+    case JIM_DOUBLE_KEY:
+        return "Tried to set the member key twice";
+    default:
+        return NULL;
+    }
+}
+
+void jim_null(Jim *jim)
+{
+    if (jim->error == JIM_OK) {
+        jim_element_begin(jim);
+        jim_write_cstr(jim, "null");
+        jim_element_end(jim);
+    }
+}
+
+void jim_bool(Jim *jim, int boolean)
+{
+    if (jim->error == JIM_OK) {
+        jim_element_begin(jim);
+        if (boolean) {
+            jim_write_cstr(jim, "true");
+        } else {
+            jim_write_cstr(jim, "false");
+        }
+        jim_element_end(jim);
+    }
+}
+
+static void jim_integer_no_element(Jim *jim, long long int x)
+{
+    if (jim->error == JIM_OK) {
+        if (x < 0) {
+            jim_write_cstr(jim, "-");
+            x = -x;
+        }
+
+        if (x == 0) {
+            jim_write_cstr(jim, "0");
+        } else {
+            char buffer[64];
+            size_t count = 0;
+
+            while (x > 0) {
+                buffer[count++] = (x % 10) + '0';
+                x /= 10;
+            }
+
+            for (size_t i = 0; i < count / 2; ++i) {
+                char t = buffer[i];
+                buffer[i] = buffer[count - i - 1];
+                buffer[count - i - 1] = t;
+            }
+
+            jim_write(jim, buffer, count);
+        }
+
+    }
+}
+
+void jim_integer(Jim *jim, long long int x)
+{
+    if (jim->error == JIM_OK) {
+        jim_element_begin(jim);
+        jim_integer_no_element(jim, x);
+        jim_element_end(jim);
+    }
+}
+
+static int is_nan_or_inf(double x)
+{
+    unsigned long long int mask = (1ULL << 11ULL) - 1ULL;
+    return (((*(unsigned long long int*) &x) >> 52ULL) & mask) == mask;
+}
+
+void jim_float(Jim *jim, double x, int precision)
+{
+    if (jim->error == JIM_OK) {
+        if (is_nan_or_inf(x)) {
+            jim_null(jim);
+        } else {
+            jim_element_begin(jim);
+
+            jim_integer_no_element(jim, (long long int) x);
+            x -= (double) (long long int) x;
+            while (precision-- > 0) {
+                x *= 10.0;
+            }
+            jim_write_cstr(jim, ".");
+
+            long long int y = (long long int) x;
+            if (y < 0) {
+                y = -y;
+            }
+            jim_integer_no_element(jim, y);
+
+            jim_element_end(jim);
+        }
+    }
+}
+
+static void jim_string_sized_no_element(Jim *jim, const char *str, size_t size)
+{
+    if (jim->error == JIM_OK) {
+        const char *hex_digits = "0123456789abcdef";
+        const char *specials = "btnvfr";
+        const char *p = str;
+        size_t len = size;
+
+        jim_write_cstr(jim, "\"");
+        size_t cl;
+        for (size_t i = 0; i < len; i++) {
+            unsigned char ch = ((unsigned char *) p)[i];
+            if (ch == '"' || ch == '\\') {
+                jim_write(jim, "\\", 1);
+                jim_write(jim, p + i, 1);
+            } else if (ch >= '\b' && ch <= '\r') {
+                jim_write(jim, "\\", 1);
+                jim_write(jim, &specials[ch - '\b'], 1);
+            } else if (0x20 <= ch && ch <= 0x7F) { // is printable
+                jim_write(jim, p + i, 1);
+            } else if ((cl = jim_get_utf8_char_len(ch)) == 1) {
+                jim_write(jim, "\\u00", 4);
+                jim_write(jim, &hex_digits[(ch >> 4) % 0xf], 1);
+                jim_write(jim, &hex_digits[ch % 0xf], 1);
+            } else {
+                jim_write(jim, p + i, cl);
+                i += cl - 1;
+            }
+        }
+
+        jim_write_cstr(jim, "\"");
+    }
+}
+
+void jim_string_sized(Jim *jim, const char *str, size_t size)
+{
+    if (jim->error == JIM_OK) {
+        jim_element_begin(jim);
+        jim_string_sized_no_element(jim, str, size);
+        jim_element_end(jim);
+    }
+}
+
+void jim_string(Jim *jim, const char *str)
+{
+    if (jim->error == JIM_OK) {
+        jim_string_sized(jim, str, strlen(str));
+    }
+}
+
+void jim_array_begin(Jim *jim)
+{
+    if (jim->error == JIM_OK) {
+        jim_element_begin(jim);
+        jim_write_cstr(jim, "[");
+        jim_scope_push(jim, JIM_ARRAY_SCOPE);
+    }
+}
+
+
+void jim_array_end(Jim *jim)
+{
+    if (jim->error == JIM_OK) {
+        jim_write_cstr(jim, "]");
+        jim_scope_pop(jim);
+        jim_element_end(jim);
+    }
+}
+
+void jim_object_begin(Jim *jim)
+{
+    if (jim->error == JIM_OK) {
+        jim_element_begin(jim);
+        jim_write_cstr(jim, "{");
+        jim_scope_push(jim, JIM_OBJECT_SCOPE);
+    }
+}
+
+void jim_member_key(Jim *jim, const char *str)
+{
+    if (jim->error == JIM_OK) {
+        jim_member_key_sized(jim, str, strlen(str));
+    }
+}
+
+void jim_member_key_sized(Jim *jim, const char *str, size_t size)
+{
+    if (jim->error == JIM_OK) {
+        jim_element_begin(jim);
+        Jim_Scope *scope = jim_current_scope(jim);
+        if (scope && scope->kind == JIM_OBJECT_SCOPE) {
+            if (!scope->key) {
+                jim_string_sized_no_element(jim, str, size);
+                jim_write_cstr(jim, ":");
+                scope->key = 1;
+            } else {
+                jim->error = JIM_DOUBLE_KEY;
+            }
+        } else {
+            jim->error = JIM_OUT_OF_SCOPE_KEY;
+        }
+    }
+}
+
+void jim_object_end(Jim *jim)
+{
+    if (jim->error == JIM_OK) {
+        jim_write_cstr(jim, "}");
+        jim_scope_pop(jim);
+        jim_element_end(jim);
+    }
+}
+
+#endif // JIM_IMPLEMENTATION