commit c5253847f287a2c820a1c7bc11a3d95bf12d9ec7
parent f84a5d091339eb8ce74ab54d9ae81ad53db6eeff
Author: Sophie <info@soophie.de>
Date: Sun, 6 Apr 2025 08:50:44 +0000
feat: Converted to single-header file
Diffstat:
D | libjson.c | | | 203 | ------------------------------------------------------------------------------- |
M | libjson.h | | | 215 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- |
2 files changed, 214 insertions(+), 204 deletions(-)
diff --git a/libjson.c b/libjson.c
@@ -1,203 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <string.h>
-#include <ctype.h>
-#include <inttypes.h>
-
-#include "libjson.h"
-
-char *json_str(json_ptr_t json_ptr) {
- jsmntok_t token = json_ptr.tokens[json_ptr.ptr];
- int token_len = token.end - token.start;
- char *str = malloc(token_len + 1);
- memcpy(str, json_ptr.str + token.start, token_len);
- str[token_len] = '\0';
- return str;
-}
-
-json_ptr_t json_ptr(jsmntok_t *tokens, int len, const char *str) {
- return (json_ptr_t) {
- .tokens = tokens,
- .len = len,
- .ptr = 0,
- .str = str,
- .err = JSON_OK,
- };
-}
-
-json_ptr_t json_err(json_ptr_t json_ptr, json_err_t json_err) {
- json_ptr.err = json_err;
- return json_ptr;
-}
-
-int json_size(json_ptr_t json_ptr) {
- return json_ptr.tokens[json_ptr.ptr].size;
-}
-
-json_ptr_t json_parse(jsmntok_t tokens[], int tokens_len, const char *str) {
- jsmn_parser parser;
- jsmn_init(&parser);
- int len = jsmn_parse(&parser, str, strlen(str), tokens, tokens_len);
- json_ptr_t ptr = json_ptr(tokens, len, str);
- if (len < 0) {
- return json_err(ptr, JSON_ERR_INVALID_FORMAT);
- }
- return ptr;
-}
-
-/**
- Increments the `jsmntok_t` pointer until the child `JSMN_OBJECT` or
- `JSMN_ARRAY` is traversed and the pointer points to the next entry.
- If no errors occured, the return code is `0`.
-*/
-void json_seek(json_ptr_t *json_ptr) {
- jsmntok_t token_root = json_ptr->tokens[json_ptr->ptr];
- if (token_root.type != JSMN_OBJECT && token_root.type != JSMN_ARRAY) {
- json_err(*json_ptr, JSON_ERR_INVALID_TYPE);
- return;
- }
- jsmntok_t token;
- for (int i = 0; i < token_root.size; i++) {
- if (token_root.type == JSMN_OBJECT) {
- token = json_ptr->tokens[++json_ptr->ptr]; // key
- }
- token = json_ptr->tokens[++json_ptr->ptr]; // value
- if (token.type == JSMN_OBJECT || token.type == JSMN_ARRAY) {
- json_seek(json_ptr);
- if (json_ptr->err) {
- return;
- }
- }
- }
-}
-
-json_ptr_t json_get(json_ptr_t json_ptr, const char *key) {
- jsmntok_t token_root = json_ptr.tokens[json_ptr.ptr];
- if (token_root.type != JSMN_OBJECT) {
- return json_err(json_ptr, JSON_ERR_INVALID_TYPE);
- }
- jsmntok_t token;
- int token_len;
- while (json_ptr.ptr < json_ptr.len) {
- token = json_ptr.tokens[++json_ptr.ptr]; // key
- token_len = token.end - token.start;
- char token_key[token_len + 1];
- memcpy(token_key, json_ptr.str + token.start, token_len);
- token_key[token_len] = '\0';
- if (strncmp(json_ptr.str + token.start, key, token_len) == 0) {
- json_ptr.ptr++;
- return json_ptr;
- }
- token = json_ptr.tokens[++json_ptr.ptr]; // value
- if (token.type == JSMN_OBJECT || token.type == JSMN_ARRAY) {
- json_seek(&json_ptr);
- if (json_ptr.err) {
- return json_ptr;
- }
- }
- }
- return json_err(json_ptr, JSON_ERR_INVALID_MISSING);
-}
-
-json_ptr_t json_at(json_ptr_t json_ptr, int idx) {
- jsmntok_t token_root = json_ptr.tokens[json_ptr.ptr];
- if (token_root.type != JSMN_ARRAY) {
- return json_err(json_ptr, JSON_ERR_INVALID_TYPE);
- }
- if (idx >= token_root.size) {
- return json_err(json_ptr, JSON_ERR_INVALID_SIZE);
- }
- jsmntok_t token;
- int token_idx = 0;
- while (json_ptr.ptr < json_ptr.len) {
- token = json_ptr.tokens[++json_ptr.ptr];
- if (token_idx == idx) {
- return json_ptr;
- }
- if (token.type == JSMN_OBJECT || token.type == JSMN_ARRAY) {
- json_seek(&json_ptr);
- if (json_ptr.err) {
- return json_ptr;
- }
- }
- token_idx++;
- }
- return json_err(json_ptr, JSON_ERR_INVALID_MISSING);
-}
-
-json_ptr_t json_query(json_ptr_t json_ptr, const char *query) {
- jsmntok_t token_root = json_ptr.tokens[json_ptr.ptr];
- if (token_root.type != JSMN_OBJECT && token_root.type != JSMN_ARRAY) {
- return json_err(json_ptr, JSON_ERR_INVALID_TYPE);
- }
- jsmntok_t token;
- int i = 0;
- int query_len = strlen(query);
- while (i < query_len) {
- token = json_ptr.tokens[json_ptr.ptr];
- char c = query[i];
- if (c == '.') {
- if (token.type != JSMN_OBJECT) {
- return json_err(json_ptr, JSON_ERR_INVALID_TYPE); // expected JSMN_OBJECT
- }
- i++;
- int j = i;
- bool found = false;
- for (; j < query_len; j++) {
- if (!isalpha(query[j])) {
- int key_len = j - i;
- if (key_len == 0) {
- return json_err(json_ptr, JSON_ERR_INVALID_FORMAT);
- }
- char key[key_len + 1];
- memcpy(key, query + i, key_len);
- key[key_len] = '\0';
- json_ptr = json_get(json_ptr, key);
- i += key_len;
- found = true;
- break;
- }
- }
- if (!found) {
- int key_len = j - i;
- if (key_len == 0) {
- return json_err(json_ptr, JSON_ERR_INVALID_FORMAT);
- }
- char key[key_len + 1];
- memcpy(key, query + i, key_len);
- key[key_len] = '\0';
- json_ptr = json_get(json_ptr, key);
- i += key_len;
- }
- continue;
- }
- if (c == '[') {
- if (token.type != JSMN_ARRAY) {
- return json_err(json_ptr, JSON_ERR_INVALID_TYPE); // expected JSMN_ARRAY
- }
- i++;
- for (int j = i; j < query_len; j++) {
- if (query[j] == ']') {
- int idx_len = j - i;
- if (idx_len == 0) {
- return json_err(json_ptr, JSON_ERR_INVALID_FORMAT);
- }
- char idx[idx_len + 1];
- memcpy(idx, query + i, idx_len);
- idx[idx_len] = '\0';
- int index = strtoimax(idx, NULL, 10);
- json_ptr = json_at(json_ptr, index);
- i += idx_len + 1;
- break;
- }
- }
- continue;
- }
- if (!isalpha(c)) {
- return json_err(json_ptr, JSON_ERR_INVALID_FORMAT); // invalid char
- }
- i++;
- }
- return json_ptr;
-}
diff --git a/libjson.h b/libjson.h
@@ -1,5 +1,9 @@
-#pragma once
+#ifndef LIB_JSON_H
+#define LIB_JSON_H
+#ifndef LIB_JSON_IMPL
+#define JSMN_HEADER
+#endif /* LIB_JSON_IMPL */
#include <jsmn.h>
typedef enum {
@@ -24,3 +28,212 @@ json_ptr_t json_parse(jsmntok_t tokens[], int tokens_len, const char *str);
json_ptr_t json_get(json_ptr_t json_ptr, const char *key);
json_ptr_t json_at(json_ptr_t json_ptr, int idx);
json_ptr_t json_query(json_ptr_t json_ptr, const char *query);
+
+#ifdef LIB_JSON_IMPL
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+#include <inttypes.h>
+
+#include "libjson.h"
+
+char *json_str(json_ptr_t json_ptr) {
+ jsmntok_t token = json_ptr.tokens[json_ptr.ptr];
+ int token_len = token.end - token.start;
+ char *str = malloc(token_len + 1);
+ memcpy(str, json_ptr.str + token.start, token_len);
+ str[token_len] = '\0';
+ return str;
+}
+
+json_ptr_t json_ptr(jsmntok_t *tokens, int len, const char *str) {
+ return (json_ptr_t) {
+ .tokens = tokens,
+ .len = len,
+ .ptr = 0,
+ .str = str,
+ .err = JSON_OK,
+ };
+}
+
+json_ptr_t json_err(json_ptr_t json_ptr, json_err_t json_err) {
+ json_ptr.err = json_err;
+ return json_ptr;
+}
+
+int json_size(json_ptr_t json_ptr) {
+ return json_ptr.tokens[json_ptr.ptr].size;
+}
+
+json_ptr_t json_parse(jsmntok_t tokens[], int tokens_len, const char *str) {
+ jsmn_parser parser;
+ jsmn_init(&parser);
+ int len = jsmn_parse(&parser, str, strlen(str), tokens, tokens_len);
+ json_ptr_t ptr = json_ptr(tokens, len, str);
+ if (len < 0) {
+ return json_err(ptr, JSON_ERR_INVALID_FORMAT);
+ }
+ return ptr;
+}
+
+/**
+ Increments the `jsmntok_t` pointer until the child `JSMN_OBJECT` or
+ `JSMN_ARRAY` is traversed and the pointer points to the next entry.
+ If no errors occured, the return code is `0`.
+*/
+void json_seek(json_ptr_t *json_ptr) {
+ jsmntok_t token_root = json_ptr->tokens[json_ptr->ptr];
+ if (token_root.type != JSMN_OBJECT && token_root.type != JSMN_ARRAY) {
+ json_err(*json_ptr, JSON_ERR_INVALID_TYPE);
+ return;
+ }
+ jsmntok_t token;
+ for (int i = 0; i < token_root.size; i++) {
+ if (token_root.type == JSMN_OBJECT) {
+ token = json_ptr->tokens[++json_ptr->ptr]; // key
+ }
+ token = json_ptr->tokens[++json_ptr->ptr]; // value
+ if (token.type == JSMN_OBJECT || token.type == JSMN_ARRAY) {
+ json_seek(json_ptr);
+ if (json_ptr->err) {
+ return;
+ }
+ }
+ }
+}
+
+json_ptr_t json_get(json_ptr_t json_ptr, const char *key) {
+ jsmntok_t token_root = json_ptr.tokens[json_ptr.ptr];
+ if (token_root.type != JSMN_OBJECT) {
+ return json_err(json_ptr, JSON_ERR_INVALID_TYPE);
+ }
+ jsmntok_t token;
+ int token_len;
+ while (json_ptr.ptr < json_ptr.len) {
+ token = json_ptr.tokens[++json_ptr.ptr]; // key
+ token_len = token.end - token.start;
+ char token_key[token_len + 1];
+ memcpy(token_key, json_ptr.str + token.start, token_len);
+ token_key[token_len] = '\0';
+ if (strncmp(json_ptr.str + token.start, key, token_len) == 0) {
+ json_ptr.ptr++;
+ return json_ptr;
+ }
+ token = json_ptr.tokens[++json_ptr.ptr]; // value
+ if (token.type == JSMN_OBJECT || token.type == JSMN_ARRAY) {
+ json_seek(&json_ptr);
+ if (json_ptr.err) {
+ return json_ptr;
+ }
+ }
+ }
+ return json_err(json_ptr, JSON_ERR_INVALID_MISSING);
+}
+
+json_ptr_t json_at(json_ptr_t json_ptr, int idx) {
+ jsmntok_t token_root = json_ptr.tokens[json_ptr.ptr];
+ if (token_root.type != JSMN_ARRAY) {
+ return json_err(json_ptr, JSON_ERR_INVALID_TYPE);
+ }
+ if (idx >= token_root.size) {
+ return json_err(json_ptr, JSON_ERR_INVALID_SIZE);
+ }
+ jsmntok_t token;
+ int token_idx = 0;
+ while (json_ptr.ptr < json_ptr.len) {
+ token = json_ptr.tokens[++json_ptr.ptr];
+ if (token_idx == idx) {
+ return json_ptr;
+ }
+ if (token.type == JSMN_OBJECT || token.type == JSMN_ARRAY) {
+ json_seek(&json_ptr);
+ if (json_ptr.err) {
+ return json_ptr;
+ }
+ }
+ token_idx++;
+ }
+ return json_err(json_ptr, JSON_ERR_INVALID_MISSING);
+}
+
+json_ptr_t json_query(json_ptr_t json_ptr, const char *query) {
+ jsmntok_t token_root = json_ptr.tokens[json_ptr.ptr];
+ if (token_root.type != JSMN_OBJECT && token_root.type != JSMN_ARRAY) {
+ return json_err(json_ptr, JSON_ERR_INVALID_TYPE);
+ }
+ jsmntok_t token;
+ int i = 0;
+ int query_len = strlen(query);
+ while (i < query_len) {
+ token = json_ptr.tokens[json_ptr.ptr];
+ char c = query[i];
+ if (c == '.') {
+ if (token.type != JSMN_OBJECT) {
+ return json_err(json_ptr, JSON_ERR_INVALID_TYPE); // expected JSMN_OBJECT
+ }
+ i++;
+ int j = i;
+ bool found = false;
+ for (; j < query_len; j++) {
+ if (!isalpha(query[j])) {
+ int key_len = j - i;
+ if (key_len == 0) {
+ return json_err(json_ptr, JSON_ERR_INVALID_FORMAT);
+ }
+ char key[key_len + 1];
+ memcpy(key, query + i, key_len);
+ key[key_len] = '\0';
+ json_ptr = json_get(json_ptr, key);
+ i += key_len;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ int key_len = j - i;
+ if (key_len == 0) {
+ return json_err(json_ptr, JSON_ERR_INVALID_FORMAT);
+ }
+ char key[key_len + 1];
+ memcpy(key, query + i, key_len);
+ key[key_len] = '\0';
+ json_ptr = json_get(json_ptr, key);
+ i += key_len;
+ }
+ continue;
+ }
+ if (c == '[') {
+ if (token.type != JSMN_ARRAY) {
+ return json_err(json_ptr, JSON_ERR_INVALID_TYPE); // expected JSMN_ARRAY
+ }
+ i++;
+ for (int j = i; j < query_len; j++) {
+ if (query[j] == ']') {
+ int idx_len = j - i;
+ if (idx_len == 0) {
+ return json_err(json_ptr, JSON_ERR_INVALID_FORMAT);
+ }
+ char idx[idx_len + 1];
+ memcpy(idx, query + i, idx_len);
+ idx[idx_len] = '\0';
+ int index = strtoimax(idx, NULL, 10);
+ json_ptr = json_at(json_ptr, index);
+ i += idx_len + 1;
+ break;
+ }
+ }
+ continue;
+ }
+ if (!isalpha(c)) {
+ return json_err(json_ptr, JSON_ERR_INVALID_FORMAT); // invalid char
+ }
+ i++;
+ }
+ return json_ptr;
+}
+
+#endif /* LIB_JSON_IMPL */
+#endif /* LIB_JSON_H */