Create jj.h
This commit is contained in:
parent
7f857341a1
commit
8bc5953ed0
488
jj.h
Normal file
488
jj.h
Normal file
|
@ -0,0 +1,488 @@
|
|||
/*
|
||||
* jj - json parser in C
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/*includes****************************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/*macros******************************************************************************************/
|
||||
|
||||
#define PARSE_STRING \
|
||||
ptr->value->string = process_between_quotes(*p, &ptr->value->raw_string_length); \
|
||||
ptr->value->string_length = strlen(ptr->value->string); \
|
||||
*p += ptr->value->raw_string_length;
|
||||
|
||||
#define PARSE_BOOLEAN \
|
||||
switch(**p) { \
|
||||
case 't': \
|
||||
if(!strncmp("true", *p, 4)) { \
|
||||
ptr->value->number = 1; \
|
||||
*p += 4; \
|
||||
} else goto bad; \
|
||||
break; \
|
||||
case 'f': \
|
||||
if(!strncmp("false", *p, 5)) { \
|
||||
ptr->value->number = 0; \
|
||||
*p += 5; \
|
||||
} else goto bad; \
|
||||
break; \
|
||||
}
|
||||
|
||||
#define PARSE_NUMBER \
|
||||
ptr->value->decimal = atof(*p); \
|
||||
ptr->value->number = (int)ptr->value->decimal; \
|
||||
while(apart_of_number(*++(*p))) {}
|
||||
|
||||
#define PARSE_NULL \
|
||||
if(!strncmp("null", *p, 4)) { \
|
||||
free(ptr->value); \
|
||||
*p += 4; \
|
||||
} else goto bad; \
|
||||
|
||||
#define PARSE_OBJECT \
|
||||
(*p)++; \
|
||||
ptr->value->object = parse_object(p); \
|
||||
if(!ptr->value->object) goto bad;
|
||||
|
||||
#define PARSE_ARRAY \
|
||||
(*p)++; \
|
||||
ptr->value->array = parse_array(p); \
|
||||
if(!ptr->value->array) goto bad; \
|
||||
ptr->value->array_length = get_array_length(ptr->value->array);
|
||||
|
||||
#define VALUE_FETCH \
|
||||
switch(ptr->value->type) { \
|
||||
case STRING: PARSE_STRING; break; \
|
||||
case BOOLEAN: PARSE_BOOLEAN; break; \
|
||||
case NUMBER: PARSE_NUMBER; break; \
|
||||
case NOTHING: PARSE_NULL; break; \
|
||||
case OBJECT: PARSE_OBJECT; break; \
|
||||
case ARRAY: PARSE_ARRAY; break; \
|
||||
case CLOSER: state = PARSE_FINISH; break; \
|
||||
} \
|
||||
state = CHECK_COMMA;
|
||||
|
||||
#define COMMA_NEXT(t, n) \
|
||||
if(**p == ',') { \
|
||||
ptr->next = (struct json_##t*)malloc(sizeof(struct json_##t)); \
|
||||
if(!ptr->next) goto bad; \
|
||||
ptr->next->prev = ptr; \
|
||||
ptr = ptr->next; \
|
||||
state = n; \
|
||||
} else state = FETCH_CLOSE;
|
||||
|
||||
#define ALLOC_VALUE \
|
||||
ptr->value = (struct json_value*)malloc(sizeof(struct json_value)); \
|
||||
if(!ptr->value) goto bad; \
|
||||
ptr->value->type = get_value_type(**p); \
|
||||
if(ptr->value->type == CLOSER) { \
|
||||
state = PARSE_FINISH; \
|
||||
free(ptr->value); \
|
||||
} else state = FETCH_VALUE;
|
||||
|
||||
/*structs & types*********************************************************************************/
|
||||
|
||||
enum json_type
|
||||
{
|
||||
STRING,
|
||||
NUMBER,
|
||||
OBJECT,
|
||||
ARRAY,
|
||||
BOOLEAN,
|
||||
NOTHING,
|
||||
CLOSER
|
||||
};
|
||||
|
||||
enum json_fetch_state
|
||||
{
|
||||
FETCH_NAME,
|
||||
PRE_FETCH_VALUE_TYPE,
|
||||
FETCH_VALUE_TYPE,
|
||||
FETCH_VALUE,
|
||||
CHECK_COMMA,
|
||||
FETCH_CLOSE,
|
||||
PARSE_FINISH
|
||||
};
|
||||
|
||||
struct json_object
|
||||
{
|
||||
int key;
|
||||
struct json_value* value;
|
||||
|
||||
struct json_object* prev;
|
||||
struct json_object* next;
|
||||
};
|
||||
|
||||
struct json_array
|
||||
{
|
||||
struct json_value* value;
|
||||
|
||||
struct json_array* prev;
|
||||
struct json_array* next;
|
||||
};
|
||||
|
||||
struct json_value
|
||||
{
|
||||
enum json_type type;
|
||||
|
||||
char* string;
|
||||
int string_length;
|
||||
int raw_string_length;
|
||||
|
||||
int number;
|
||||
float decimal;
|
||||
|
||||
struct json_array* array;
|
||||
int array_length;
|
||||
|
||||
struct json_object* object;
|
||||
};
|
||||
|
||||
/*function prototypes*****************************************************************************/
|
||||
|
||||
char* process_between_quotes(char*, int*);
|
||||
unsigned int crc32(char*);
|
||||
int apart_of_number(char);
|
||||
enum json_type get_value_type(char);
|
||||
int get_array_length(struct json_array*);
|
||||
|
||||
struct json_array* parse_array(char**);
|
||||
struct json_object* parse_object(char**);
|
||||
|
||||
void free_object(struct json_object*);
|
||||
void free_array(struct json_array*);
|
||||
void free_json(struct json_value);
|
||||
|
||||
struct json_value parse_string(char*);
|
||||
|
||||
struct json_value* get_value_from_object(struct json_value*, char*);
|
||||
struct json_value* get_value_from_array(struct json_value*, int);
|
||||
|
||||
/*functions***************************************************************************************/
|
||||
|
||||
char* process_between_quotes(char* p, int* l)
|
||||
{
|
||||
char* ptr1 = p, *ptr2 = NULL;
|
||||
char* pre = NULL, *new = NULL;
|
||||
int length = 0;
|
||||
|
||||
do {
|
||||
length++;
|
||||
if(*ptr1 == '\\') {
|
||||
if(*++ptr1 != '\0') continue;
|
||||
}
|
||||
} while(*++ptr1 != '"');
|
||||
|
||||
pre = malloc(length);
|
||||
|
||||
if(!pre) return NULL;
|
||||
|
||||
strncpy(pre, p+1, length);
|
||||
|
||||
new = malloc(length);
|
||||
|
||||
if(!new) {
|
||||
free(pre);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ptr1 = pre;
|
||||
ptr2 = new;
|
||||
|
||||
do {
|
||||
if(*ptr1 == '\\') {
|
||||
switch(*++ptr1) {
|
||||
case '"': *ptr2 = '"'; break;
|
||||
case '\\': *ptr2 = '\\'; break;
|
||||
case '/': *ptr2 = '/'; break;
|
||||
case 'b': *ptr2 = '\b'; break;
|
||||
case 'f': *ptr2 = '\f'; break;
|
||||
case 'n': *ptr2 = '\n'; break;
|
||||
case 'r': *ptr2 = '\r'; break;
|
||||
case 't': *ptr2 = '\t'; break;
|
||||
}
|
||||
} else {
|
||||
*ptr2 = *ptr1;
|
||||
}
|
||||
|
||||
ptr2++;
|
||||
} while(*++ptr1 != '\0' && *ptr1 != '"');
|
||||
|
||||
free(pre);
|
||||
if(l) *l = length + 1;
|
||||
return new;
|
||||
}
|
||||
|
||||
unsigned int crc32(char* p)
|
||||
{
|
||||
int i = 0, j;
|
||||
unsigned int crc = 0xFFFFFFFF, mask;
|
||||
|
||||
while (p[i] != 0) {
|
||||
crc = crc ^ p[i++];
|
||||
for (j = 7; j >= 0; j--) {
|
||||
mask = -(crc & 1);
|
||||
crc = (crc >> 1) ^ (0xEDB88320 & mask);
|
||||
}
|
||||
}
|
||||
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
int apart_of_number(char c)
|
||||
{
|
||||
return isdigit(c) ||
|
||||
c == 'E' ||
|
||||
c == 'e' ||
|
||||
c == '+' ||
|
||||
c == '-' ||
|
||||
c == '.';
|
||||
}
|
||||
|
||||
enum json_type get_value_type(char c)
|
||||
{
|
||||
if(apart_of_number(c)) return NUMBER;
|
||||
switch(c) {
|
||||
case '"': return STRING;
|
||||
case '[': return ARRAY;
|
||||
case '{': return OBJECT;
|
||||
case 't': case 'f': return BOOLEAN;
|
||||
case '}': case ']': return CLOSER;
|
||||
default: case 'n': return NOTHING;
|
||||
}
|
||||
}
|
||||
|
||||
int get_array_length(struct json_array* array)
|
||||
{
|
||||
int l = 1;
|
||||
struct json_array* ptr = array;
|
||||
|
||||
for(; ptr != NULL &&
|
||||
ptr->next && ptr->value;
|
||||
ptr = ptr->next, l++) {}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
struct json_array* parse_array(char** p)
|
||||
{
|
||||
struct json_array* array = (struct json_array*)malloc(sizeof(struct json_array));
|
||||
struct json_array* ptr = NULL;
|
||||
|
||||
enum json_fetch_state state;
|
||||
|
||||
if(!array) return NULL;
|
||||
|
||||
ptr = array;
|
||||
|
||||
state = FETCH_VALUE_TYPE;
|
||||
do {
|
||||
if(isspace(**p)) continue;
|
||||
|
||||
switch(state) {
|
||||
case PARSE_FINISH: return array;
|
||||
case FETCH_NAME:
|
||||
case PRE_FETCH_VALUE_TYPE:
|
||||
goto bad;
|
||||
|
||||
case FETCH_VALUE_TYPE: ALLOC_VALUE;
|
||||
|
||||
/* intentional fallthrough */
|
||||
|
||||
case FETCH_VALUE: VALUE_FETCH;
|
||||
|
||||
/* intentional fallthrough */
|
||||
|
||||
case CHECK_COMMA: COMMA_NEXT(array, FETCH_VALUE_TYPE);
|
||||
|
||||
/* intentional fallthrough */
|
||||
|
||||
case FETCH_CLOSE: if(**p == ']') state = PARSE_FINISH;
|
||||
}
|
||||
} while(*(*p)++ != '\0');
|
||||
|
||||
return array;
|
||||
|
||||
bad:
|
||||
/*
|
||||
if(state != PARSE_FINISH)
|
||||
puts("ERROR: parse cycle incomplete");
|
||||
*/
|
||||
|
||||
free(array);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct json_object* parse_object(char** p)
|
||||
{
|
||||
struct json_object* object = (struct json_object*)malloc(sizeof(struct json_object));
|
||||
struct json_object* ptr;
|
||||
char* obj_name = NULL;
|
||||
enum json_fetch_state state;
|
||||
|
||||
if(!object) return NULL;
|
||||
|
||||
ptr = object;
|
||||
|
||||
state = FETCH_NAME;
|
||||
do {
|
||||
if(isspace(**p)) continue;
|
||||
|
||||
switch(state) {
|
||||
case PARSE_FINISH: return object;
|
||||
case FETCH_NAME:
|
||||
if(**p == '"') {
|
||||
obj_name = process_between_quotes(*p, NULL);
|
||||
if(!obj_name) goto bad;
|
||||
|
||||
ptr->key = (int)crc32(obj_name);
|
||||
free(obj_name);
|
||||
obj_name = NULL;
|
||||
|
||||
state = PRE_FETCH_VALUE_TYPE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PRE_FETCH_VALUE_TYPE:
|
||||
if(**p == ':') {
|
||||
state = FETCH_VALUE_TYPE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case FETCH_VALUE_TYPE: ALLOC_VALUE;
|
||||
|
||||
/* intentional fallthrough */
|
||||
|
||||
case FETCH_VALUE: VALUE_FETCH;
|
||||
|
||||
/* intentional fallthrough */
|
||||
|
||||
case CHECK_COMMA: COMMA_NEXT(object, FETCH_NAME);
|
||||
|
||||
/* intentional fallthrough */
|
||||
|
||||
case FETCH_CLOSE: if(**p == '}') state = PARSE_FINISH;
|
||||
}
|
||||
} while(*(*p)++ != '\0');
|
||||
|
||||
return object;
|
||||
|
||||
bad:
|
||||
/*
|
||||
if(state != PARSE_FINISH)
|
||||
puts("ERROR: parse cycle incomplete");
|
||||
*/
|
||||
|
||||
free(object);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct json_value parse_string(char* buf)
|
||||
{
|
||||
char* p = buf;
|
||||
struct json_value value;
|
||||
|
||||
do {
|
||||
switch(*p) {
|
||||
case '{':
|
||||
++p;
|
||||
value.type = OBJECT;
|
||||
value.object = parse_object(&p);
|
||||
if(!value.object)
|
||||
return value;
|
||||
break;
|
||||
|
||||
case '[':
|
||||
++p;
|
||||
value.type = ARRAY;
|
||||
value.array = parse_array(&p);
|
||||
value.array_length = get_array_length(value.array);
|
||||
if(!value.array)
|
||||
return value;
|
||||
}
|
||||
} while(*p++ != '\0');
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
struct json_value* get_value_from_object(struct json_value* val, char* name)
|
||||
{
|
||||
struct json_object* ptr = val->object;
|
||||
int h = (int)crc32(name);
|
||||
|
||||
for(; ptr != NULL; ptr = ptr->next) {
|
||||
if(ptr->key == h)
|
||||
return ptr->value;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct json_value* get_value_from_array(struct json_value* val, int index)
|
||||
{
|
||||
int i = 0;
|
||||
struct json_array* ptr = val->array;
|
||||
|
||||
if(index >= val->array_length) return NULL;
|
||||
while(i++ != index && ptr != NULL) ptr = ptr->next;
|
||||
return ptr->value;
|
||||
}
|
||||
|
||||
void free_value(struct json_value* value)
|
||||
{
|
||||
if(value) {
|
||||
free(value->string);
|
||||
free_object(value->object);
|
||||
free_array(value->array);
|
||||
free(value);
|
||||
}
|
||||
}
|
||||
|
||||
void free_array(struct json_array* array)
|
||||
{
|
||||
struct json_array* ptr = NULL;
|
||||
|
||||
while(array) {
|
||||
ptr = array->next;
|
||||
free_value(array->value);
|
||||
free(array);
|
||||
array = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
void free_object(struct json_object* object)
|
||||
{
|
||||
struct json_object* ptr = NULL;
|
||||
|
||||
while(object) {
|
||||
ptr = object->next;
|
||||
free_value(object->value);
|
||||
free(object);
|
||||
object = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
void free_json(struct json_value v)
|
||||
{
|
||||
switch(v.type) {
|
||||
case STRING:
|
||||
case BOOLEAN:
|
||||
case NOTHING:
|
||||
case NUMBER:
|
||||
case CLOSER:
|
||||
break;
|
||||
|
||||
case OBJECT: free_object(v.object); break;
|
||||
case ARRAY: free_array(v.array); break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
Loading…
Reference in New Issue
Block a user