QUOTE: Be your own kind of beautiful.

feat: Initial commit - libjson - A simple JSON parser

libjson

A simple JSON parser
git clone git://192.168.2.2/libjson
Log | Files | Refs | README

commit f84a5d091339eb8ce74ab54d9ae81ad53db6eeff
Author: typable <contact@typable.dev>
Date:   Wed, 18 Sep 2024 15:12:32 +0200

feat: Initial commit

Diffstat:
AMakefile | 18++++++++++++++++++
AREADME.md | 9+++++++++
Alibjson.c | 203+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alibjson.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);