commit f84a5d091339eb8ce74ab54d9ae81ad53db6eeff
Author: typable <contact@typable.dev>
Date: Wed, 18 Sep 2024 15:12:32 +0200
feat: Initial commit
Diffstat:
A | Makefile | | | 18 | ++++++++++++++++++ |
A | README.md | | | 9 | +++++++++ |
A | libjson.c | | | 203 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | libjson.h | | | 26 | ++++++++++++++++++++++++++ |
4 files changed, 256 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -0,0 +1,18 @@
+default: build
+
+build:
+ cc -o libjson.so -shared -fPIC -lpthread -lssl -Wall -Wextra libjson.c
+
+install:
+ cc -o libjson.so -shared -fPIC libjson.c
+ sudo cp libjson.h /usr/local/include/
+ sudo cp libjson.so /usr/local/lib/
+ sudo ldconfig
+ rm libjson.so
+
+uninstall:
+ sudo rm /usr/local/include/libjson.h
+ sudo rm /usr/local/lib/libjson.so
+
+clean:
+ rm libjson.so
diff --git a/README.md b/README.md
@@ -0,0 +1,9 @@
+# libjson
+
+A simple JSON parser
+
+## Installation
+
+```
+sudo make install
+```
diff --git a/libjson.c b/libjson.c
@@ -0,0 +1,203 @@
+#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
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <jsmn.h>
+
+typedef enum {
+ JSON_OK = 0,
+ JSON_ERR_INVALID_TYPE = 1,
+ JSON_ERR_INVALID_SIZE = 2,
+ JSON_ERR_INVALID_FORMAT = 3,
+ JSON_ERR_INVALID_MISSING = 4,
+} json_err_t;
+
+typedef struct {
+ jsmntok_t *tokens;
+ int len;
+ int ptr;
+ const char *str;
+ json_err_t err;
+} json_ptr_t;
+
+char *json_str(json_ptr_t json_ptr);
+int json_size(json_ptr_t json_ptr);
+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);