aboutsummaryrefslogtreecommitdiffhomepage
path: root/contrib
diff options
context:
space:
mode:
Diffstat (limited to 'contrib')
-rw-r--r--contrib/sj.h/LICENSE24
-rw-r--r--contrib/sj.h/sj.h156
2 files changed, 180 insertions, 0 deletions
diff --git a/contrib/sj.h/LICENSE b/contrib/sj.h/LICENSE
new file mode 100644
index 0000000..c32dd18
--- /dev/null
+++ b/contrib/sj.h/LICENSE
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <https://unlicense.org/> \ No newline at end of file
diff --git a/contrib/sj.h/sj.h b/contrib/sj.h/sj.h
new file mode 100644
index 0000000..60bea9e
--- /dev/null
+++ b/contrib/sj.h/sj.h
@@ -0,0 +1,156 @@
+// sj.h - v0.4 - rxi 2025
+// public domain - no warranty implied, use at your own risk
+
+#ifndef SJ_H
+#define SJ_H
+
+#include <stddef.h>
+#include <stdbool.h>
+
+typedef struct {
+ char *data, *cur, *end;
+ int depth;
+ char *error;
+} sj_Reader;
+
+typedef struct {
+ int type;
+ char *start, *end;
+ int depth;
+} sj_Value;
+
+enum { SJ_ERROR, SJ_END, SJ_ARRAY, SJ_OBJECT, SJ_NUMBER, SJ_STRING, SJ_BOOL, SJ_NULL };
+
+sj_Reader sj_reader(char *data, size_t len);
+sj_Value sj_read(sj_Reader *r);
+bool sj_iter_array(sj_Reader *r, sj_Value arr, sj_Value *val);
+bool sj_iter_object(sj_Reader *r, sj_Value obj, sj_Value *key, sj_Value *val);
+void sj_location(sj_Reader *r, int *line, int *col);
+
+#endif // #ifndef SJ_H
+
+#ifdef SJ_IMPL
+
+
+sj_Reader sj_reader(char *data, size_t len) {
+ return (sj_Reader){ .data = data, .cur = data, .end = data + len };
+}
+
+
+static bool sj__is_number_cont(char c) {
+ return (c >= '0' && c <= '9')
+ || c == 'e' || c == 'E' || c == '.' || c == '-' || c == '+';
+}
+
+static bool sj__is_string(char *cur, char *end, char *expect) {
+ while (*expect) {
+ if (cur == end || *cur != *expect) {
+ return false;
+ }
+ expect++, cur++;
+ }
+ return true;
+}
+
+
+sj_Value sj_read(sj_Reader *r) {
+ sj_Value res;
+top:
+ if (r->error) { return (sj_Value){ .type = SJ_ERROR, .start = r->cur, .end = r->cur }; }
+ if (r->cur == r->end) { r->error = "unexpected eof"; goto top; }
+ res.start = r->cur;
+
+ switch (*r->cur) {
+ case ' ': case '\n': case '\r': case '\t':
+ case ':': case ',':
+ r->cur++;
+ goto top;
+
+ case '-': case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ res.type = SJ_NUMBER;
+ while (r->cur != r->end && sj__is_number_cont(*r->cur)) { r->cur++; }
+ break;
+
+ case '"':
+ res.type = SJ_STRING;
+ res.start = ++r->cur;
+ for (;;) {
+ if ( r->cur == r->end) { r->error = "unclosed string"; goto top; }
+ if (*r->cur == '"') { break; }
+ if (*r->cur == '\\') { r->cur++; }
+ if ( r->cur != r->end) { r->cur++; }
+ }
+ res.end = r->cur++;
+ return res;
+
+ case '{': case '[':
+ res.type = (*r->cur == '{') ? SJ_OBJECT : SJ_ARRAY;
+ res.depth = ++r->depth;
+ r->cur++;
+ break;
+
+ case '}': case ']':
+ res.type = SJ_END;
+ if (--r->depth < 0) {
+ r->error = (*r->cur == '}') ? "stray '}'" : "stray ']'";
+ goto top;
+ }
+ r->cur++;
+ break;
+
+ case 'n': case 't': case 'f':
+ res.type = (*r->cur == 'n') ? SJ_NULL : SJ_BOOL;
+ if (sj__is_string(r->cur, r->end, "null")) { r->cur += 4; break; }
+ if (sj__is_string(r->cur, r->end, "true")) { r->cur += 4; break; }
+ if (sj__is_string(r->cur, r->end, "false")) { r->cur += 5; break; }
+ // fallthrough
+
+ default:
+ r->error = "unknown token";
+ goto top;
+ }
+ res.end = r->cur;
+ return res;
+}
+
+
+static void sj__discard_until(sj_Reader *r, int depth) {
+ sj_Value val;
+ val.type = SJ_NULL;
+ while (r->depth != depth && val.type != SJ_ERROR) {
+ val = sj_read(r);
+ }
+}
+
+
+bool sj_iter_array(sj_Reader *r, sj_Value arr, sj_Value *val) {
+ sj__discard_until(r, arr.depth);
+ *val = sj_read(r);
+ if (val->type == SJ_ERROR || val->type == SJ_END) { return false; }
+ return true;
+}
+
+
+bool sj_iter_object(sj_Reader *r, sj_Value obj, sj_Value *key, sj_Value *val) {
+ sj__discard_until(r, obj.depth);
+ *key = sj_read(r);
+ if (key->type == SJ_ERROR || key->type == SJ_END) { return false; }
+ *val = sj_read(r);
+ if (val->type == SJ_END) { r->error = "unexpected object end"; return false; }
+ if (val->type == SJ_ERROR) { return false; }
+ return true;
+}
+
+
+void sj_location(sj_Reader *r, int *line, int *col) {
+ int ln = 1, cl = 1;
+ for (char *p = r->data; p != r->cur; p++) {
+ if (*p == '\n') { ln++; cl = 0; }
+ cl++;
+ }
+ *line = ln;
+ *col = cl;
+}
+
+#endif // #ifdef SJ_IMPL \ No newline at end of file