feat(c-miner): port miner pipeline to modular C implementation
This commit is contained in:
401
json.c
Normal file
401
json.c
Normal file
@@ -0,0 +1,401 @@
|
||||
#include "json.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define JSMN_ERROR_NOMEM -1
|
||||
#define JSMN_ERROR_INVAL -2
|
||||
#define JSMN_ERROR_PART -3
|
||||
|
||||
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, size_t num_tokens) {
|
||||
jsmntok_t *tok;
|
||||
|
||||
if (parser->toknext >= num_tokens) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tok = &tokens[parser->toknext++];
|
||||
tok->start = -1;
|
||||
tok->end = -1;
|
||||
tok->size = 0;
|
||||
tok->parent = -1;
|
||||
tok->type = JSMN_UNDEFINED;
|
||||
return tok;
|
||||
}
|
||||
|
||||
static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, int start, int end) {
|
||||
token->type = type;
|
||||
token->start = start;
|
||||
token->end = end;
|
||||
token->size = 0;
|
||||
}
|
||||
|
||||
void jsmn_init(jsmn_parser *parser) {
|
||||
parser->pos = 0;
|
||||
parser->toknext = 0;
|
||||
parser->toksuper = -1;
|
||||
}
|
||||
|
||||
static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens) {
|
||||
int start = (int)parser->pos;
|
||||
|
||||
for (; parser->pos < len; parser->pos++) {
|
||||
char c = js[parser->pos];
|
||||
if (c == ':' || c == '\t' || c == '\r' || c == '\n' || c == ' ' || c == ',' || c == ']' || c == '}') {
|
||||
jsmntok_t *token = jsmn_alloc_token(parser, tokens, num_tokens);
|
||||
if (token == NULL) {
|
||||
parser->pos = (unsigned int)start;
|
||||
return JSMN_ERROR_NOMEM;
|
||||
}
|
||||
jsmn_fill_token(token, JSMN_PRIMITIVE, start, (int)parser->pos);
|
||||
token->parent = parser->toksuper;
|
||||
parser->pos--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (c < 32 || c >= 127) {
|
||||
parser->pos = (unsigned int)start;
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
}
|
||||
|
||||
parser->pos = (unsigned int)start;
|
||||
return JSMN_ERROR_PART;
|
||||
}
|
||||
|
||||
static int jsmn_parse_string(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens) {
|
||||
int start = (int)parser->pos;
|
||||
|
||||
parser->pos++;
|
||||
|
||||
for (; parser->pos < len; parser->pos++) {
|
||||
char c = js[parser->pos];
|
||||
|
||||
if (c == '"') {
|
||||
jsmntok_t *token = jsmn_alloc_token(parser, tokens, num_tokens);
|
||||
if (token == NULL) {
|
||||
parser->pos = (unsigned int)start;
|
||||
return JSMN_ERROR_NOMEM;
|
||||
}
|
||||
jsmn_fill_token(token, JSMN_STRING, start + 1, (int)parser->pos);
|
||||
token->parent = parser->toksuper;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (c == '\\' && parser->pos + 1 < len) {
|
||||
char esc = js[parser->pos + 1];
|
||||
|
||||
if (esc == '"' || esc == '/' || esc == '\\' || esc == 'b' || esc == 'f' || esc == 'r' || esc == 'n' || esc == 't') {
|
||||
parser->pos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (esc == 'u') {
|
||||
int i;
|
||||
parser->pos++;
|
||||
for (i = 0; i < 4 && parser->pos + 1 < len; i++) {
|
||||
char hc = js[parser->pos + 1];
|
||||
if (!((hc >= '0' && hc <= '9') || (hc >= 'A' && hc <= 'F') || (hc >= 'a' && hc <= 'f'))) {
|
||||
parser->pos = (unsigned int)start;
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
parser->pos++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
parser->pos = (unsigned int)start;
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
}
|
||||
|
||||
parser->pos = (unsigned int)start;
|
||||
return JSMN_ERROR_PART;
|
||||
}
|
||||
|
||||
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, unsigned int num_tokens) {
|
||||
int r;
|
||||
unsigned int i;
|
||||
|
||||
for (; parser->pos < len; parser->pos++) {
|
||||
char c = js[parser->pos];
|
||||
|
||||
switch (c) {
|
||||
case '{':
|
||||
case '[': {
|
||||
jsmntype_t type = (c == '{') ? JSMN_OBJECT : JSMN_ARRAY;
|
||||
jsmntok_t *token = jsmn_alloc_token(parser, tokens, num_tokens);
|
||||
|
||||
if (token == NULL) {
|
||||
return JSMN_ERROR_NOMEM;
|
||||
}
|
||||
|
||||
if (parser->toksuper != -1) {
|
||||
tokens[parser->toksuper].size++;
|
||||
token->parent = parser->toksuper;
|
||||
}
|
||||
|
||||
jsmn_fill_token(token, type, (int)parser->pos, -1);
|
||||
parser->toksuper = (int)(parser->toknext - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
case '}':
|
||||
case ']': {
|
||||
jsmntype_t type = (c == '}') ? JSMN_OBJECT : JSMN_ARRAY;
|
||||
|
||||
for (i = parser->toknext; i > 0; i--) {
|
||||
jsmntok_t *token = &tokens[i - 1];
|
||||
if (token->start != -1 && token->end == -1) {
|
||||
if (token->type != type) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
token->end = (int)parser->pos + 1;
|
||||
parser->toksuper = token->parent;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case '"':
|
||||
r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
if (parser->toksuper != -1) {
|
||||
tokens[parser->toksuper].size++;
|
||||
}
|
||||
break;
|
||||
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
case ' ':
|
||||
case ':':
|
||||
case ',':
|
||||
break;
|
||||
|
||||
default:
|
||||
r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
if (parser->toksuper != -1) {
|
||||
tokens[parser->toksuper].size++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = parser->toknext; i > 0; i--) {
|
||||
if (tokens[i - 1].start != -1 && tokens[i - 1].end == -1) {
|
||||
return JSMN_ERROR_PART;
|
||||
}
|
||||
}
|
||||
|
||||
return (int)parser->toknext;
|
||||
}
|
||||
|
||||
int json_doc_parse(JsonDoc *doc, const char *json) {
|
||||
jsmn_parser p;
|
||||
int r;
|
||||
int cap = 1024;
|
||||
|
||||
doc->json = json;
|
||||
doc->tokens = NULL;
|
||||
doc->token_count = 0;
|
||||
|
||||
while (1) {
|
||||
doc->tokens = (jsmntok_t *)malloc((size_t)cap * sizeof(jsmntok_t));
|
||||
if (doc->tokens == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
jsmn_init(&p);
|
||||
r = jsmn_parse(&p, json, strlen(json), doc->tokens, (unsigned int)cap);
|
||||
if (r >= 0) {
|
||||
doc->token_count = r;
|
||||
return 1;
|
||||
}
|
||||
|
||||
free(doc->tokens);
|
||||
doc->tokens = NULL;
|
||||
|
||||
if (r == JSMN_ERROR_NOMEM) {
|
||||
cap *= 2;
|
||||
if (cap > (1 << 21)) {
|
||||
return 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void json_doc_free(JsonDoc *doc) {
|
||||
free(doc->tokens);
|
||||
doc->tokens = NULL;
|
||||
doc->token_count = 0;
|
||||
doc->json = NULL;
|
||||
}
|
||||
|
||||
int json_token_streq(const JsonDoc *doc, int tok_idx, const char *s) {
|
||||
const jsmntok_t *tok;
|
||||
size_t len;
|
||||
|
||||
if (tok_idx < 0 || tok_idx >= doc->token_count) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
tok = &doc->tokens[tok_idx];
|
||||
if (tok->type != JSMN_STRING && tok->type != JSMN_PRIMITIVE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = (size_t)(tok->end - tok->start);
|
||||
if (len != strlen(s)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return strncmp(doc->json + tok->start, s, len) == 0;
|
||||
}
|
||||
|
||||
int json_skip_token(const JsonDoc *doc, int tok_idx) {
|
||||
int i;
|
||||
int next;
|
||||
|
||||
if (tok_idx < 0 || tok_idx >= doc->token_count) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (doc->tokens[tok_idx].type == JSMN_STRING || doc->tokens[tok_idx].type == JSMN_PRIMITIVE) {
|
||||
return tok_idx + 1;
|
||||
}
|
||||
|
||||
if (doc->tokens[tok_idx].type == JSMN_ARRAY) {
|
||||
next = tok_idx + 1;
|
||||
for (i = 0; i < doc->tokens[tok_idx].size; i++) {
|
||||
next = json_skip_token(doc, next);
|
||||
if (next < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
if (doc->tokens[tok_idx].type == JSMN_OBJECT) {
|
||||
int pairs = doc->tokens[tok_idx].size / 2;
|
||||
next = tok_idx + 1;
|
||||
for (i = 0; i < pairs; i++) {
|
||||
next = json_skip_token(doc, next);
|
||||
if (next < 0) {
|
||||
return -1;
|
||||
}
|
||||
next = json_skip_token(doc, next);
|
||||
if (next < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int json_object_get(const JsonDoc *doc, int obj_idx, const char *key) {
|
||||
int i;
|
||||
int cur;
|
||||
int pairs;
|
||||
|
||||
if (obj_idx < 0 || obj_idx >= doc->token_count) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (doc->tokens[obj_idx].type != JSMN_OBJECT) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
pairs = doc->tokens[obj_idx].size / 2;
|
||||
cur = obj_idx + 1;
|
||||
for (i = 0; i < pairs; i++) {
|
||||
int key_idx = cur;
|
||||
int value_idx;
|
||||
|
||||
cur = json_skip_token(doc, cur);
|
||||
if (cur < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
value_idx = cur;
|
||||
cur = json_skip_token(doc, cur);
|
||||
if (cur < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (json_token_streq(doc, key_idx, key)) {
|
||||
return value_idx;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int json_array_get(const JsonDoc *doc, int arr_idx, int elem_index) {
|
||||
int i;
|
||||
int cur;
|
||||
|
||||
if (arr_idx < 0 || arr_idx >= doc->token_count) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (doc->tokens[arr_idx].type != JSMN_ARRAY) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (elem_index < 0 || elem_index >= doc->tokens[arr_idx].size) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
cur = arr_idx + 1;
|
||||
for (i = 0; i < elem_index; i++) {
|
||||
cur = json_skip_token(doc, cur);
|
||||
if (cur < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
||||
char *json_token_strdup(const JsonDoc *doc, int tok_idx) {
|
||||
int start;
|
||||
int end;
|
||||
size_t len;
|
||||
char *out;
|
||||
|
||||
if (tok_idx < 0 || tok_idx >= doc->token_count) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
start = doc->tokens[tok_idx].start;
|
||||
end = doc->tokens[tok_idx].end;
|
||||
if (start < 0 || end < start) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len = (size_t)(end - start);
|
||||
out = (char *)malloc(len + 1);
|
||||
if (out == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(out, doc->json + start, len);
|
||||
out[len] = '\0';
|
||||
return out;
|
||||
}
|
||||
Reference in New Issue
Block a user