Около
Я решил бросить вызов себе и написать парсер JSON на C99 (я писал их раньше на C ++, но никогда не на старом C), и вот что у меня получилось, он хорошо работает из того, что я пробовал.
Код был протестирован с несколькими относительно сложными json-файлами, и у него не было проблем с их обработкой, я также прогнал его clang-check -analyze
, и в нем было несколько мелких предупреждений, которые я исправил.
Все (кроме алгоритма хеширования) написал я.
Информация
- Базовый тип,
JSON_Element
, — помеченный союз; JSON_Object
это хеш-таблица;JSON_Array
это динамический массив а-ляstd::vector
;JSON_String
C-строка фиксированного размера с завершающим нулем;JSON_Integer
простоint64_t
;JSON_Double
простоdouble
;true
,false
иnull
представлены как теги объединения без связанных структур;
Код
json.h
#pragma once
#include "utils.h"
#ifdef __cplusplus
#include <cstdio>
extern "C" {
#else
#include <stdio.h>
#endif // __cplusplus
typedef struct JSON_Element JSON_Element;
typedef enum JSON_Element_Type {
JSON_NULL,
JSON_ARRAY,
JSON_OBJECT,
JSON_INTEGER,
JSON_DOUBLE,
JSON_STRING,
JSON_TRUE,
JSON_FALSE,
JSON_PARSE_ERROR,//JSON_PARSE_ERROR is a JSON_STRING that describes the error that happened during json_parse
} JSON_Element_Type;
typedef struct JSON_Object_Table JSON_Object_Table;
typedef struct JSON_Object {
JSON_Element_Type type;
JSON_Object_Table * tbl;
} JSON_Object;
JSON_Object * json_make_object();
JSON_Element * json_object_get(JSON_Object *,const char * key);//pointers returned from this are 'fragile' they may break when modifying the object
JSON_Element * json_object_get_n(JSON_Object *,const char * key,size_t n);//pointers returned from this are 'fragile' they may break when modifying the object
void json_object_set(JSON_Object *,const char * key,JSON_Element * elem);//elem pointer is invalidated
void json_object_set_n(JSON_Object *,const char * key,size_t n,JSON_Element * elem);//elem pointer is invalidated
void json_free_object(JSON_Object *);
typedef struct JSON_Array {
JSON_Element_Type type;
size_t size;
size_t alloc;
JSON_Element * arr;
} JSON_Array;
JSON_Array * json_make_array();
void json_free_array(JSON_Array *);
JSON_Element * json_array_get(JSON_Array * arr,size_t index);//pointers returned from this are 'fragile' they may break when modifying the array
void json_array_set(JSON_Array * arr,JSON_Element * elem,size_t index);//elem pointer is invalidated
void json_array_push(JSON_Array * arr,JSON_Element * elem);//elem pointer is invalidated
int json_array_insert(JSON_Array * arr,JSON_Element * elem,size_t index);//if returns 1, insertion failed and passed elem pointer is still valid, otherwise elem pointer is invalidated
void json_array_remove(JSON_Array * arr,size_t index);
typedef struct JSON_String {
JSON_Element_Type type;
size_t len;
char * str;
} JSON_String;
JSON_String * json_make_string(const char * s);
JSON_String * json_make_string_n(const char * s,size_t n);
void json_set_string(JSON_String * str,const char * s);
void json_set_string_n(JSON_String * str,const char * s,size_t n);
void json_free_string(JSON_String *);
typedef struct JSON_Integer {
JSON_Element_Type type;
int64_t i;
} JSON_Integer;
JSON_Integer * json_make_integer(int64_t i);
typedef struct JSON_Double {
JSON_Element_Type type;
double d;
} JSON_Double;
JSON_Double * json_make_double(double d);
struct JSON_Element {
union {
JSON_Element_Type type;
JSON_Array _arr;
JSON_Object _obj;
JSON_String _str;
JSON_Integer _int;
JSON_Double _double;
};
};
void json_free_element(JSON_Element *);
JSON_Element * json_parse_n(const char * s,size_t n);
JSON_Element * json_parse(const char * s);
void json_write_element(FILE *f,JSON_Element *,size_t indentation);
void json_write_object(FILE *f,JSON_Object *,size_t indentation);
void json_write_array(FILE *f,JSON_Array *,size_t indentation);
void json_write_string(FILE *f,JSON_String *,size_t indentation);
//same as json_write_*(stdout,...)
void json_print_element(JSON_Element *,size_t indentation);
void json_print_object(JSON_Object *,size_t indentation);
void json_print_array(JSON_Array *,size_t indentation);
void json_print_string(JSON_String *,size_t indentation);
#ifdef __cplusplus
}
#endif // __cplusplus
utils.h
#pragma once
#ifdef __cplusplus
#include <cstddef>
#include <cstdint>
#define NORETURN [[noreturn]]
extern "C" {
#else
#include <stddef.h>
#include <stdint.h>
#define NORETURN __attribute__((__noreturn__))
#endif // __cplusplus
size_t str_hash(const char * s);
NORETURN void err_exit(const char * fmt,...);
#ifdef __cplusplus
}
#endif // __cplusplus
json.c
#include "json.h"
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <stdbool.h>
#include <string.h>
#include <stddef.h>
#include <stdint.h>
#include <stdarg.h>
#define OOM_EXIT() err_exit("Out of Memory in %s",__func__)
static void json_cleanup_element(void * p);
typedef struct JSON_Object_Table_Elem {
uint32_t size;
uint32_t alloc;
void * arr;
} JSON_Object_Table_Elem;
typedef struct JSON_Object_Table {
uint32_t num_buckets;
uint32_t item_size;
JSON_Object_Table_Elem buckets[];
} JSON_Object_Table;
typedef JSON_Object_Table table;
typedef JSON_Object_Table_Elem table_elem;
static table * alloc_table(size_t num_buckets,size_t item_size){
table * tbl=calloc(1,sizeof(table)+
(num_buckets*sizeof(table_elem)));
tbl->num_buckets=num_buckets;
tbl->item_size=item_size;
return tbl;
}
static void table_cleanup(table * tbl,void (*cleanup)(void*)){
uint32_t isz=tbl->item_size;
for(uint32_t i=0;i<tbl->num_buckets;i++){
if(tbl->buckets[i].arr){
uint32_t sz=tbl->buckets[i].size*isz;
for(uint32_t j=0;j<sz;j+=isz){
if(cleanup) cleanup(&((uint8_t *)tbl->buckets[i].arr)[j]);
}
free(tbl->buckets[i].arr);
}
}
free(tbl);
}
static void table_add_item(table * tbl,void * item,uint32_t (*hash)(void*)){
table_elem * e=&tbl->buckets[hash(item)%tbl->num_buckets];
if(e->arr){
if(e->alloc==e->size){
uint32_t new_alloc=e->alloc*2;//growth factor 2
e->arr=realloc(e->arr,new_alloc*tbl->item_size);
if(!e->arr){
OOM_EXIT();
}
e->alloc=new_alloc;
}
}else{
e->arr=calloc(4,tbl->item_size);
e->alloc=4;
}
memcpy((uint8_t*)e->arr+((e->size++)*tbl->item_size),item,tbl->item_size);
}
static void * table_find_item(table * tbl,void * key,uint32_t (*hash)(void*),int (*compare)(void*,void*)){
table_elem * e=&tbl->buckets[hash(key)%tbl->num_buckets];
if(!(e->arr&&e->size)) return NULL;
uint8_t * arr=e->arr;
uint32_t isz=tbl->item_size;
uint32_t sz=e->size*isz;
for(uint32_t i=0;i<sz;i+=isz){
if(compare(&arr[i],key)){
return &arr[i];
}
}
return NULL;
}
typedef struct JSON_ObjectEntry {
char * key;
JSON_Element elem;
} JSON_ObjectEntry;
JSON_Object * json_make_object(){
JSON_Object * obj=&((JSON_Element*)calloc(1,sizeof(JSON_Element)))->_obj;
obj->type=JSON_OBJECT;
obj->tbl=alloc_table(32,sizeof(JSON_ObjectEntry));
return obj;
}
static int json_object_find_compare_keys(void * item,void * key){
return strcmp(((JSON_ObjectEntry*)item)->key,(const char *)key)==0;
}
static uint32_t json_object_item_hash(void * item){
return str_hash(((JSON_ObjectEntry*)item)->key);
}
static uint32_t json_object_key_hash(void * key){
return str_hash((const char *)key);
}
JSON_Element * json_object_get_n(JSON_Object * obj,const char * key,size_t n){
JSON_ObjectEntry * entry=table_find_item(obj->tbl,(void*)key,json_object_key_hash,json_object_find_compare_keys);
if(entry){
return &entry->elem;
}else{
return NULL;
}
}
JSON_Element * json_object_get(JSON_Object * obj,const char * key){
return json_object_get_n(obj,key,strlen(key));
}
void json_object_set_n(JSON_Object * obj,const char * key,size_t n,JSON_Element * elem){
JSON_ObjectEntry * entry=table_find_item(obj->tbl,(void*)key,json_object_key_hash,json_object_find_compare_keys);
if(entry){
json_cleanup_element(&entry->elem);
memcpy(&entry->elem,elem,sizeof(JSON_Element));
}else{
JSON_ObjectEntry new_entry = {
.key=calloc(n+1,sizeof(char)),
.elem={{0}},
};
memcpy(new_entry.key,key,n);
memcpy(&new_entry.elem,elem,sizeof(JSON_Element));
table_add_item(obj->tbl,&new_entry,json_object_item_hash);
}
free(elem);
}
void json_object_set(JSON_Object * obj,const char * key,JSON_Element * elem){
json_object_set_n(obj,key,strlen(key),elem);
}
static void json_cleanup_object(JSON_Object * obj){
if(!obj)return;
table_cleanup(obj->tbl,json_cleanup_element);
}
void json_free_object(JSON_Object * obj){
if(!obj)return;
json_cleanup_object(obj);
free(obj);
}
JSON_Array * json_make_array(){
JSON_Array * arr=&((JSON_Element*)calloc(1,sizeof(JSON_Element)))->_arr;
arr->type=JSON_ARRAY;
return arr;
}
static void json_cleanup_array(JSON_Array * arr){
if(!arr)return;
if(arr->arr){
size_t sz=arr->size;
for(size_t i=0;i<sz;i++){
json_cleanup_element(&arr->arr[i]);
}
free(arr->arr);
}
}
void json_free_array(JSON_Array * arr){
if(!arr)return;
json_cleanup_array(arr);
free(arr);
}
JSON_Element * json_array_get(JSON_Array * arr,size_t index){
if(index>arr->size){
return NULL;
}
return arr->arr+index;
}
void json_array_set(JSON_Array * arr,JSON_Element * elem,size_t index){
if(index>arr->size)return;
json_cleanup_element(arr->arr+index);
memcpy(arr->arr+index,elem,sizeof(JSON_Element));
free(elem);
}
static void json_array_grow_by(JSON_Array * arr,size_t by){
if(arr->alloc>=(arr->size+by))return;
if(arr->arr){
arr->arr=realloc(arr->arr,(arr->size+by)*sizeof(JSON_Element));
}else{
arr->arr=calloc(by,sizeof(JSON_Element));
}
if(!arr->arr){
OOM_EXIT();
}
}
void json_array_push(JSON_Array * arr,JSON_Element * elem){
json_array_grow_by(arr,1);
memcpy(arr->arr+arr->size,elem,sizeof(JSON_Element));
++arr->size;
free(elem);
}
int json_array_insert(JSON_Array * arr,JSON_Element * elem,size_t index){
if(arr->size>index){
json_array_grow_by(arr,1);
memmove(arr->arr+index+1,arr->arr+index,((arr->size-index)-1)*sizeof(JSON_Element));
++arr->size;
memcpy(arr->arr+index,elem,sizeof(JSON_Element));
free(elem);
}else if(arr->size==index){
json_array_grow_by(arr,1);
++arr->size;
memcpy(arr->arr+index,elem,sizeof(JSON_Element));
free(elem);
}else{
//COULD NOT ADD, INVALID INDEX
return 1;
}
return 0;
}
void json_array_remove(JSON_Array * arr,size_t index){
if(arr->size>index){
json_cleanup_element(arr->arr+index);
--arr->size;
if(arr->size>index) memmove(arr->arr+index,arr->arr+index+1,(arr->size-index)*sizeof(JSON_Element));
}
}
JSON_String * json_make_string_n(const char * s,size_t n){
JSON_String * str=&((JSON_Element*)calloc(1,sizeof(JSON_Element)))->_str;
str->type=JSON_STRING;
str->str=calloc(n+1,sizeof(char));
memcpy(str->str,s,n);
str->str[n]=0;
str->len=n;
return str;
}
JSON_String * json_make_string(const char * s){
return json_make_string_n(s,strlen(s));
}
void json_set_string_n(JSON_String * str,const char * s,size_t n){
free(str->str);
str->str=calloc(n+1,sizeof(char));
memcpy(str->str,s,n);
str->str[n]=0;
str->len=n;
}
void json_set_string(JSON_String * str,const char * s){
json_set_string_n(str,s,strlen(s));
}
void json_cleanup_string(JSON_String * str){
if(!str)return;
free(str->str);
}
void json_free_string(JSON_String * str){
if(!str)return;
free(str->str);
free(str);
}
JSON_Integer * json_make_integer(int64_t i){
JSON_Integer * ie=&((JSON_Element*)calloc(1,sizeof(JSON_Element)))->_int;
ie->type=JSON_INTEGER;
ie->i=i;
return ie;
}
JSON_Double * json_make_double(double d){
JSON_Double * de=&((JSON_Element*)calloc(1,sizeof(JSON_Element)))->_double;
de->type=JSON_DOUBLE;
de->d=d;
return de;
}
static void json_cleanup_element(void * p){
if(!p)return;
JSON_Element * elem=p;
switch(elem->type){
case JSON_ARRAY:
json_cleanup_array(p);
break;
case JSON_OBJECT:
json_cleanup_object(p);
break;
case JSON_PARSE_ERROR:
case JSON_STRING:
json_cleanup_string(p);
break;
default:
break;
}
}
void json_free_element(JSON_Element * elem){
if(!elem)return;
json_cleanup_element(elem);
free(elem);
}
typedef struct parse_data {
size_t i;
size_t n;
const char * s;
} parse_data;
JSON_Element * parse_error(const char * fmt,...){
JSON_String * str=&((JSON_Element*)calloc(1,sizeof(JSON_Element)))->_str;
va_list arg1,arg2;
va_start(arg1,fmt);
va_copy(arg2,arg1);
size_t n=vsnprintf(NULL,0,fmt,arg2);
va_end(arg2);
str->type=JSON_PARSE_ERROR;
str->str=calloc(n+1,sizeof(char));
vsnprintf(str->str,n+1,fmt,arg1);
va_end(arg1);
str->str[n]=0;
str->len=n;
return (JSON_Element*)str;
}
static bool is_whitespace(char c){
return c==' '||c=='t'||c=='n'||c=='r';
}
void skip_whitespace(parse_data * p){
while(p->i<p->n){
if(is_whitespace(p->s[p->i])){
++p->i;
}else if(p->s[p->i]=="https://codereview.stackexchange.com/"&&(p->i+1<p->n)&&((p->s[p->i+1]=="https://codereview.stackexchange.com/")||(p->s[p->i+1]=='*'))){
if(p->s[p->i+1]=="https://codereview.stackexchange.com/"){
p->i+=2;
while(p->i<p->n&&p->s[p->i]!='n')++p->i;
if(p->i<p->n)++p->i;
}else{
p->i+=2;
if(p->i<p->n)++p->i;
while(p->i<p->n&&p->s[p->i-1]!='*'&&p->s[p->i]!="https://codereview.stackexchange.com/")++p->i;
if(p->i<p->n)++p->i;
}
}else{
break;
}
}
}
JSON_Element * json_parse(const char * s){
return json_parse_n(s,strlen(s));
}
static char unescape(char c){
switch(c) {
case 'a':
return 'a';
case 'b':
return 'b';
case 't':
return 't';
case 'n':
return 'n';
case 'v':
return 'v';
case 'f':
return 'f';
case 'r':
return 'r';
default:
return c;
}
}
static JSON_Element * json_parse_string(parse_data * p){
skip_whitespace(p);
bool singlequote=false;
if(p->i>=p->n){
return parse_error("Expected '"', got EOF");
}else if(p->s[p->i]=='''){
singlequote=true;
}else if(p->s[p->i]!='"'){
return parse_error("Expected '"', got %c",p->s[p->i]);
}
++p->i;
bool reading_escape=false;
size_t n=0,i=p->i;
for(;i<p->n;++i){
if(reading_escape){
n++;
reading_escape=false;
}else if(p->s[i]=='\'){
reading_escape=true;
}else if(p->s[i]==(singlequote?''':'"')){
break;
}else{
n++;
}
}
if(i>=p->n){
return parse_error(reading_escape?"Expected '"', got EOF":"Expected char got EOF");
}
JSON_String * str=&((JSON_Element*)calloc(1,sizeof(JSON_Element)))->_str;
str->type=JSON_STRING;
str->str=calloc(n+1,sizeof(char));
str->str[n]=0;
str->len=n;
reading_escape=false;
n=0;
for(;p->i<p->n;++p->i){
if(reading_escape){
str->str[n++]=unescape(p->s[p->i]);
reading_escape=false;
}else if(p->s[p->i]=='\'){
reading_escape=true;
}else if(p->s[p->i]==(singlequote?''':'"')){
break;
}else{
str->str[n++]=p->s[p->i];
}
}
++p->i;
return (JSON_Element *)str;
}
JSON_Element * json_parse_element(parse_data * p);
JSON_Element * json_parse_object(parse_data * p){
skip_whitespace(p);
if(p->i>=p->n){
return parse_error("Expected '{', got EOF");
}else if(p->s[p->i]!='{'){
return parse_error("Expected '{', got %c",p->s[p->i]);
}
++p->i;
JSON_Object * obj=json_make_object();
while(true){
JSON_String * key=(JSON_String *)json_parse_string(p);
if(key->type==JSON_PARSE_ERROR){
json_free_object(obj);
return (JSON_Element*)key;
}
skip_whitespace(p);
if(p->i>=p->n){
json_free_string(key);
json_free_object(obj);
return parse_error("Expected ':', got EOF");
}else if(p->s[p->i]!=':'){
json_free_string(key);
json_free_object(obj);
return parse_error("Expected ':', got %c",p->s[p->i]);
}
++p->i;
JSON_Element * e=json_parse_element(p);
if(e->type==JSON_PARSE_ERROR){
json_free_string(key);
json_free_object(obj);
return e;
}
json_object_set_n(obj,key->str,key->len,e);
json_free_string(key);
skip_whitespace(p);
if(p->i>=p->n){
json_free_object(obj);
return parse_error("Expected '}', got EOF");
}else if(p->s[p->i]==','){
++p->i;
skip_whitespace(p);
if(p->s[p->i]=='}'){
++p->i;
return (JSON_Element*)obj;
}
}else if(p->s[p->i]=='}'){
++p->i;
return (JSON_Element*)obj;
}else{
json_free_object(obj);
return parse_error("Expected '}', got %c",p->s[p->i]);
}
}
json_free_object(obj);
return parse_error("Expected '}', got EOF");
}
JSON_Element * json_parse_array(parse_data * p){
skip_whitespace(p);
if(p->i>=p->n){
return parse_error("Expected '[', got EOF");
}else if(p->s[p->i]!='['){
return parse_error("Expected '[', got %c",p->s[p->i]);
}
++p->i;
JSON_Array * arr=json_make_array();
while(true){
JSON_Element * e=json_parse_element(p);
if(e->type==JSON_PARSE_ERROR){
json_free_array(arr);
return e;
}
json_array_push(arr,e);
skip_whitespace(p);
if(p->i>=p->n){
json_free_array(arr);
return parse_error("Expected ']', got EOF");
}else if(p->s[p->i]==','){
++p->i;
skip_whitespace(p);
if(p->s[p->i]==']'){
++p->i;
return (JSON_Element*)arr;
}
}else if(p->s[p->i]==']'){
++p->i;
return (JSON_Element*)arr;
}else{
json_free_array(arr);
return parse_error("Expected ']', got %c",p->s[p->i]);
}
}
json_free_array(arr);
return parse_error("Expected ']', got EOF");
}
typedef union numberdata {
double d;
int64_t i;
} numberdata;
JSON_Element * json_parse_number(parse_data * p){
skip_whitespace(p);
if(p->i>=p->n) return parse_error("Expected JSON Element, got EOF");
bool is_double=false;
bool is_negative=false;
bool is_valid=false;
size_t double_digit=1;
numberdata number={0};
switch(p->s[p->i]){
case '+':
is_negative=false;
++p->i;
break;
case '-':
is_negative=true;
++p->i;
break;
case '.':
is_double=true;
number.d=0;
++p->i;
break;
default:
if(p->s[p->i]<'0'||p->s[p->i]>'9'){
return parse_error("Expected Number, got %c",p->s[p->i]);
}
break;
}
for(;p->i<p->n;++p->i){
char c=p->s[p->i];
if(c>='0'&&c<='9'){
is_valid=true;
if(is_double){
number.d+=(c-'0')/pow(10,double_digit);
double_digit++;
}else{
number.i*=10;
number.i+=c-'0';
}
}else if(c=='.'){
if(is_double){
return parse_error("Expected Number, got %c",c);
}
is_double=true;
number.d=number.i;
}else if(is_valid){
if(is_double){
JSON_Double * d=&((JSON_Element*)calloc(1,sizeof(JSON_Element)))->_double;
d->type=JSON_DOUBLE;
d->d=is_negative?-number.d:number.d;
return (JSON_Element*)d;
}else{
JSON_Integer * i=&((JSON_Element*)calloc(1,sizeof(JSON_Element)))->_int;
i->type=JSON_INTEGER;
i->i=is_negative?-number.i:number.i;
return (JSON_Element*)i;
}
}else{
return parse_error("Expected Number, got %c",c);
}
}
if(is_valid){
if(is_double){
JSON_Double * d=&((JSON_Element*)calloc(1,sizeof(JSON_Element)))->_double;
d->type=JSON_DOUBLE;
d->d=is_negative?-number.d:number.d;
return (JSON_Element*)d;
}else{
JSON_Integer * i=&((JSON_Element*)calloc(1,sizeof(JSON_Element)))->_int;
i->type=JSON_INTEGER;
i->i=is_negative?-number.i:number.i;
return (JSON_Element*)i;
}
}
return parse_error("Expected Number, got EOF");
}
JSON_Element * json_parse_element(parse_data * p){
skip_whitespace(p);
if(p->i>=p->n) return parse_error("Expected JSON Element, got EOF");
char c=p->s[p->i];
switch(c){
case '{':
return json_parse_object(p);
case '[':
return json_parse_array(p);
case '"':
case ''':
return json_parse_string(p);
default:
if((c>='0'&&c<='9')||c=='.'||c=='-'||c=='+'){
return json_parse_number(p);
}else if((p->i+4)<p->n&&p->s[p->i]=='f'&&p->s[p->i+1]=='a'&&p->s[p->i+2]=='l'&&p->s[p->i+3]=='s'&&p->s[p->i+3]=='e'){
JSON_Element * e=calloc(1,sizeof(JSON_Element));
e->type=JSON_FALSE;
p->i+=5;
return e;
}else if((p->i+3)<p->n){
if(p->s[p->i]=='t'&&p->s[p->i+1]=='r'&&p->s[p->i+2]=='u'&&p->s[p->i+3]=='e'){
JSON_Element * e=calloc(1,sizeof(JSON_Element));
e->type=JSON_TRUE;
p->i+=4;
return e;
}else if(p->s[p->i]=='n'&&p->s[p->i+1]=='u'&&p->s[p->i+2]=='l'&&p->s[p->i+3]=='l'){
JSON_Element * e=calloc(1,sizeof(JSON_Element));
e->type=JSON_NULL;
p->i+=4;
return e;
}
}
return parse_error("Expected JSON Element, got %c",c);
}
}
JSON_Element * json_parse_n(const char * data,size_t len){
parse_data p = {.i=0,.s=data,.n=len};
return json_parse_element(&p);
}
void write_indent(FILE * f,size_t indentation){
for(size_t i=0;i<indentation;i++){
fputs(" ",f);
}
}
void json_write_element(FILE * f,JSON_Element * elem,size_t indentation){
switch(elem->type){
case JSON_ARRAY:
json_write_array(f,&elem->_arr,indentation);
break;
case JSON_OBJECT:
json_write_object(f,&elem->_obj,indentation);
break;
case JSON_STRING:
json_write_string(f,&elem->_str,indentation);
break;
case JSON_INTEGER:
#if ULONG_MAX == 0xFFFFFFFFFFFFFFFFUL
fprintf(f,"%ld",elem->_int.i);
#elif ULONG_LONG_MAX == 0xFFFFFFFFFFFFFFFFUL
fprintf(f,"%lld",elem->_int.i);
#else
#error "Can't print 64-bit integer"
#endif
break;
case JSON_DOUBLE:
fprintf(f,"%f",elem->_double.d);
break;
case JSON_TRUE:
fprintf(f,"true");
break;
case JSON_FALSE:
fprintf(f,"false");
break;
case JSON_NULL:
fprintf(f,"null");
break;
case JSON_PARSE_ERROR:
fprintf(f,"PARSE ERROR: %s",elem->_str.str);
break;
}
}
static bool needs_escape(char c){
switch(c){
case 'a':
case 'b':
case 't':
case 'n':
case 'v':
case 'f':
case 'r':
case '\':
case '"':
case ''':
return true;
default:
return false;
}
}
static char escape(char c){
switch(c){
case 'a':
return 'a';
case 'b':
return 'b';
case 't':
return 't';
case 'n':
return 'n';
case 'v':
return 'v';
case 'f':
return 'f';
case 'r':
return 'r';
default:
return c;
}
}
static void write_quoted(FILE * f,const char * str){
fputc('"',f);
char c;
while((c=*(str++))){
if(needs_escape(c)){
fputc('\',f);
fputc(escape(c),f);
}else{
fputc(c,f);
}
}
fputc('"',f);
}
void json_write_object(FILE * f,JSON_Object * obj,size_t indentation){
fputc('{',f);
bool first=true;
for(uint32_t i=0;i<obj->tbl->num_buckets;i++){
if(obj->tbl->buckets[i].size&&obj->tbl->buckets[i].arr){
JSON_ObjectEntry * arr=obj->tbl->buckets[i].arr;
for(uint32_t j=0;j<obj->tbl->buckets[i].size;j++){
if(first){
first=false;
fputc('n',f);
}else{
fputc(',',f);
fputc('n',f);
}
write_indent(f,indentation+1);
write_quoted(f,arr[j].key);
fputc(':',f);
json_write_element(f,&arr[j].elem,indentation+1);
}
}
}
if(!first){
fputc('n',f);
write_indent(f,indentation);
fputc('}',f);
}else{
fputc('}',f);
}
}
void json_write_array(FILE * f,JSON_Array * arr,size_t indentation){
fputc('[',f);
bool first=true;
if(arr->size&&arr->arr){
JSON_Element * a=arr->arr;
for(uint32_t i=0;i<arr->size;i++){
if(first){
first=false;
fputc('n',f);
}else{
fputc(',',f);
fputc('n',f);
}
write_indent(f,indentation+1);
json_write_element(f,&a[i],indentation+1);
}
}
if(!first){
fputc('n',f);
write_indent(f,indentation);
fputc(']',f);
}else{
fputc(']',f);
}
}
void json_write_string(FILE * f,JSON_String * str,size_t indentation){
write_quoted(f,str->str);
}
void json_print_element(JSON_Element * elem,size_t indentation){
json_write_element(stdout,elem,indentation);
}
void json_print_object(JSON_Object * obj,size_t indentation){
json_write_object(stdout,obj,indentation);
}
void json_print_array(JSON_Array * arr,size_t indentation){
json_write_array(stdout,arr,indentation);
}
void json_print_string(JSON_String * str,size_t indentation){
json_write_string(stdout,str,indentation);
}
utils.c
#include "utils.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
size_t str_hash(const char * s){
size_t hash = 5381;
// hash * 33 + c
while(*s++)hash = ((hash << 5) + hash) + ((size_t)*s);
return hash;
}
void err_exit(const char * fmt,...){
va_list arg;
va_start(arg,fmt);
vfprintf(stderr,fmt,arg);
va_end(arg);
exit(1);
}
Тестовый код, C ++ для простоты
main.cpp
#include "json.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <cerrno>
#include <cstring>
static std::string readfile(const std::string &filename){
std::ostringstream ss;
std::ifstream f(filename);
if(!f)throw std::runtime_error(strerror(errno));
ss<<f.rdbuf();
return ss.str();
}
int main() {
std::string file=readfile("test.json");
JSON_Element * elem=json_parse_n(file.c_str(),file.size());
FILE * f=fopen("test_out.json","w");
if(!f){
json_free_element(elem);
throw std::runtime_error(strerror(errno));
}
json_write_element(f,elem,0);
json_free_element(elem);
fclose(f);
return 0;
}
Код также доступен на Github
1 ответ
Непонятный код
Я люблю C, но такие строки меня расстраивают. Они похожи на карикатуру на букву С, созданную кем-то, кто хочет проиллюстрировать, что это плохая идея.
memcpy((uint8_t*)e->arr+((e->size++)*tbl->item_size),item,tbl->item_size);
memmove(arr->arr+index+1,arr->arr+index,((arr->size-index)-1)*sizeof(JSON_Element));
if(arr->size>index) memmove(arr->arr+index,arr->arr+index+1,(arr->size-index)*sizeof(JSON_Element));
}else if(p->s[p->i]=="https://codereview.stackexchange.com/"&&(p->i+1<p->n)&&((p->s[p->i+1]=="https://codereview.stackexchange.com/")||(p->s[p->i+1]=='*'))){
и так далее. Я обещаю вам, что удобочитаемость улучшится, а производительность не снизится за счет введения пробелов и временных переменных.
Поисковые массивы
static char escape(char c){
switch(c){
case 'a':
return 'a';
case 'b':
return 'b';
case 't':
return 't';
case 'n':
return 'n';
case 'v':
return 'v';
case 'f':
return 'f';
case 'r':
return 'r';
default:
return c;
}
}
можно смоделировать как линейный массив из 256 символов. Это может быть инициализировано как буквальное значение или при запуске программы.