#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
//#define MEM_LEAK_TEST
#ifdef MEM_LEAK_TEST
#define BUFFER_SIZE 500
typedef char buffer_size_type[1024 * 1024];
buffer_size_type s_test_buffer[BUFFER_SIZE] = {0};
int s_test_list[BUFFER_SIZE] = {0};
#define calloc(size,counts) calloc_p(size,counts)
#define free(p) free_p(p)
static void* calloc_p(int size, int counts) {
for (int i = 0; i < sizeof (s_test_buffer) / sizeof (s_test_buffer[0]); ++i) {
if (s_test_list[i]) continue;
s_test_list[i] = 1;
return (void*) &s_test_buffer[i];
}
}
static void free_p(void* p) {
int index = (int) ((buffer_size_type*) p - s_test_buffer);
if (index < 0) {
return;
}
memset(&s_test_buffer[index], 0, sizeof (s_test_buffer[index]));
s_test_list[index] = 0;
}
static void check() {
for (int i = 0; i < sizeof (s_test_list) / sizeof (s_test_list[0]); ++i) {
if (s_test_list[i]) {
printf("%d leak...%s\n", i, (char*) &s_test_buffer[i]);
}
}
}
#endif
#if !defined(_GNU_SOURCE) && !defined(_USE_GNU)
#ifdef WIN32
#define snprintf _snprintf
#endif
int asprintf(char** dst, const char* fmt, ...) {
static FILE* tmp_file = NULL;
int size = 0;
va_list ap, rp;
va_start(ap, fmt);
va_start(rp, fmt);
if (NULL == tmp_file) {
tmp_file = tmpfile();
}
size = vfprintf(tmp_file, fmt, ap);
if (size < 0) {
goto out;
}
*dst = (char*) calloc(sizeof (char), size + 1);
size = vsnprintf(*dst, size + 1, fmt, rp);
out:
va_end(ap);
va_end(rp);
return size;
}
char* strndup(const char*src, int len) {
char* dst = (char*) calloc(sizeof (char), len + 1);
strncpy(dst, src, len);
return dst;
}
#endif
#ifndef BUCKET_SIZE
#define BUCKET_SIZE 1024
#endif
//#define LEVEL 1
#if defined(LEVEL)
#define DEBUG(fmt,...) printf("[%d,%s]:"fmt,__LINE__,__FUNCTION__,##__VA_ARGS__)
#define TRACE(fmt,...) printf("[%d,%s]:"fmt,__LINE__,__FUNCTION__,##__VA_ARGS__)
#else
#define DEBUG(fmt,...)
#define TRACE(fmt,...)
#endif
#define JSON_OBJ_BEGIN(x) const char* x[] = {
#define VALUE_ITEM(name,value) ("\"" #name "\":" #value )
#define ARRAY_ITEM(name,...) ("\"" #name "\":["#__VA_ARGS__"]")
#define STR_ITEM(name,value) ("\"" #name"\":"#value)
#define JSON_OBJ_END(x) }
typedef struct json_item_tag {
char* key;
char* value;
unsigned flag;
} json_item_t, *json_item_handle;
enum{
em_del = 0x1,
em_sri = 0x2
};
typedef struct hash_bucket_tag {
struct hash_bucket_tag* next;
int position;
} hash_bucket_t, *hash_bucket_handle;
typedef struct json_obj_tag {
int size, used;
hash_bucket_t bucket[BUCKET_SIZE];
json_item_t items[];
} json_obj_t, *json_obj_handle;
json_obj_handle create_json_obj(int size);
void destroy_json_obj(json_obj_handle h);
#define name_id(name) ((strlen(name) + *(int*)name) % BUCKET_SIZE)
static void clear_bucket(hash_bucket_handle h) {
if (NULL == h) {
return;
}
clear_bucket(h->next);
free(h);
}
#define free_and_nil(x) do{free(x);x=NULL;}while(0)
static json_item_handle get_json_item(json_obj_handle h, const char* name) {
int index = name_id(name);
hash_bucket_handle hh = &h->bucket[index];
if (0 == hh->position && NULL == hh->next) {
hh->position = h->used++;
DEBUG("new position at %d\n", hh->position);
return &h->items[hh->position];
}
for (; 0 != strcmp(h->items[hh->position].key, name);) {
if(h->items[hh->position].flag & em_del){
json_item_handle it = &h->items[hh->position];
free_and_nil(it->key);
free_and_nil(it->value);
it->flag = 0;
DEBUG("reused deleted posion at %d\n",hh->position);
break;
}
if (NULL == hh->next) {
hh->next = (hash_bucket_handle) calloc(sizeof (hash_bucket_t), 1);
hh->next->position = h->used++;
hh = hh->next;
DEBUG("%s crashed new pos at %d\n", name, hh->position);
break;
}
hh = hh->next;
}
return &h->items[hh->position];
}
#ifdef insert_json_item
#undef insert_json_item
#endif
#define insert_json_item(fmt,h,name,v) do{\
json_item_handle __ih__ = get_json_item(h,name);\
if(NULL == __ih__->key) asprintf(&__ih__->key,"%s",name);\
if(NULL != __ih__->value) free(__ih__->value);\
asprintf(&__ih__->value,fmt,v);\
}while(0)
int insert_json_obj_handle_value(json_obj_handle h, const char* name, json_obj_handle son) {
int i = 0;
for (; i < son->used; ++i) {
char* name_str = NULL;
asprintf(&name_str, "%s.%s", name, son->items[i].key);
insert_json_item("%s", h, name_str, son->items[i].value);
free(name_str);
}
return i;
}
#define insert_float_value(h,name,value) insert_json_item("%lf",h,name,value)
#define insert_int_value(h,name,value) insert_json_item("%d",h,name,value)
#define insert_double_value(h,name,value) insert_json_item("%lf",h,name,value)
#define insert_string_value(h,name,value) insert_json_item("\"%s\"",h,name,value)
#define insert_bool_value(h,name,value) insert_json_item((value ? "true" : "false"),h,name,value)
#define insert_NULL_value(h,name,value) insert_json_item("null",h,name,value)
#define insert_value(h, name,type,value) insert_##type##_value(h,name,value)
const char* get_json_value(json_obj_handle h, const char* name,const char* fmt,void* value) {
int index = name_id(name);
hash_bucket_handle hh = &h->bucket[index];
for (; NULL != hh; hh = hh->next) {
json_item_handle it = &h->items[hh->position];
if (0 != strcmp(it->key, name) || (it->flag & em_del)) {
continue;
}
if (NULL != value && NULL != fmt) {
TRACE("convert %s : %s by %s fmt\n", name, it->value, fmt);
sscanf(it->value, fmt, value);
}
return it->value;
}
DEBUG("%s not found the value\n",name);
return NULL;
}
#define get_float_value(h,name,value) (NULL != get_json_value(h,name,"%f",value))
#define get_double_value(h,name,value) get_float_value
#define get_int_value(h,name,value) (NULL != get_json_value(h,name,"%d",value))
#define get_bool_value(h,name,value) get_int_value
int get_string_value(json_obj_handle h, const char* name, char** value) {
const char* ret = get_json_value(h, name, "", NULL);
if (NULL == ret) {
*value = NULL;
DEBUG("%s not found the value\n",name);
return 0;
}
TRACE("get %s = %s \n",name,ret);
asprintf(value, "%s", ret);
return 1;
}
int get_json_obj_handle_value(json_obj_handle h,const char* name,json_obj_handle* son){
int i = 0,len = strlen(name);
*son = create_json_obj(100);
for (; i < h->used; ++i) {
if (0 != strncmp(h->items[i].key, name, len) || (h->items[i].flag & em_del)) {
continue;
} else {
const char* sub_key = &h->items[i].key[len + 1];
const char* sub_val = h->items[i].value;
json_item_handle it = get_json_item(*son, sub_key);
/*
* using "%s" copy string avoid src string had % act..
*/
TRACE("%s.%s = %s\n", name, sub_key, sub_val);
asprintf(&it->key, "%s", sub_key);
asprintf(&it->value, "%s", sub_val);
}
}
if(0 != (*son)->used){
DEBUG("new object %s had %d items created\n",name,(*son)->used);
return 1;
}
destroy_json_obj(*son);
*son = NULL;
return 0;
}
#define get_value(h,name,type,value) get_##type##_value(h,name,&(value))
#ifdef create_array_insert
#undef create_array_insert
#endif
#ifdef insert_array_item
#undef insert_array_item
#endif
#define insert_array_item(h,name,type,index,val) do{\
char* __name_str__ = NULL;\
asprintf(&__name_str__,"%s[%d]",name,index);\
insert_value(h,__name_str__,type,val);\
free(__name_str__);\
}while(0)
#define create_array_insert(f_name,type) int insert_##f_name##_array(json_obj_handle h, const char* name, ...) {\
union{\
int last;\
type val;\
}dst_val;\
int i = 0,lbreak = 0;\
va_list al;\
va_start(al, name);\
lbreak = va_arg(al,int);\
for (dst_val.val = va_arg(al, type); dst_val.last != ']'; dst_val.val = va_arg(al, type)) {\
insert_array_item(h,name,f_name,i++,dst_val.val);\
}\
return i;\
}
create_array_insert(int, int)
create_array_insert(double, double)
create_array_insert(string, char*)
create_array_insert(json_obj_handle, json_obj_handle)
#ifdef create_array_insert
#undef create_array_insert
#endif
#ifdef insert_array_item
#undef insert_array_item
#endif
#ifdef insert_array
#undef insert_array
#endif
#define insert_array(h,name,type,...) insert_##type##_array(h,name,'[',__VA_ARGS__,']')
#define get_array_item(h,name,type,index,value) do{\
char* __name_str__ = NULL;\
asprintf(&__name_str__,"%s[%d]",name,index);\
get_value(h,__name_str__,type,value);\
free(__name_str__);\
}while(0)
#define get_array(h,name,type,dst,size) do{\
int __i__ = 0,__k__ = 0;\
for (; (__i__ < h->used) && (__k__ < size); ++__i__) {\
char* __name_str__ = NULL;\
asprintf(&__name_str__, "%s[%d]", name, __i__);\
if(get_value(h, __name_str__, type, dst[__k__])){\
TRACE("%s[%d] gotted the value at %d\n",__name_str__,__i__,__k__);\
++__k__;\
};\
free(__name_str__);\
}\
size = __k__;\
DEBUG("%s got %d items\n",name,size);\
}while(0)
static void rebuild_josn_bucket(json_obj_handle h) {
int i = 0;
for (; i < BUCKET_SIZE; ++i) {
clear_bucket(h->bucket[i].next);
}
memset(h->bucket, 0, sizeof (h->bucket));
for (int i = 0; i < h->used; ++i) {
int index = name_id(h->items[i].key);
hash_bucket_handle hh = &h->bucket[index];
for (; NULL != hh; hh = hh->next) {
if (NULL == hh->next && 0 == hh->position) {
h->bucket[index].position = i;
break;
}else if(NULL == hh->next){
hh->next = (hash_bucket_t*)calloc(sizeof(hash_bucket_t),1);
hh->next->position = i;
break;
}else{
;
}
}
}
}
void pack_json_item(json_obj_handle h) {
int i = 0;
DEBUG("json had %d items\n",h->used);
for (i = 0; i < h->used; ++i) {
json_item_handle it = &h->items[i];
if(!(it->flag & em_del)){
TRACE("%d(%s) not deleleted \n",i,it->key);
continue;
}
DEBUG("removed %d(%s) item\n",i,it->key);
free_and_nil(it->key);
free_and_nil(it->value);
memmove(&h->items[i], &h->items[i + 1], (h->used -i) * sizeof(json_item_t));
memset(&h->items[h->used--], 0, sizeof (json_item_t));
--i;
}
TRACE("left %d item in %p handle\n",h->used,h);
rebuild_josn_bucket(h);
}
void remove_json_simple_item(json_obj_handle h, const char* name) {
int index = name_id(name);
hash_bucket_handle hh = &h->bucket[index];
for (; NULL != hh; hh = hh->next) {
json_item_handle it = &h->items[hh->position];
if ((it->flag & em_del)||(0 != strcmp(it->key, name))) {
continue;
}
/*
* logic delete item avoid rehash
*/
TRACE("will be remove %s at %d\n", it->key, hh->position);
it->flag |= em_del;
}
}
void remove_json_complex_item(json_obj_handle h, const char* name, const char* fix) {
int i = 0;
char* name_str = NULL;
int len = asprintf(&name_str, "%s%s", name, fix);
TRACE("try to remove %s(%d)\n",name_str,len);
for (; i < h->used; ++i) {
if ((h->items[i].flag & em_del) || (0 != strncmp(h->items[i].key, name_str, len))) {
continue;
}
/*
* logic delete item avoid rehash
*/
TRACE("will be remove %s at %d,name = %s(%d)\n", h->items[i].key, i,name_str,len);
h->items[i].flag |= em_del;
}
free(name_str);
}
#define remove_int_value(h,name) remove_json_simple_item(h,name)
#define remove_string_value(h,name) remove_json_simple_item(h,name)
#define remove_double_value(h,name) remove_json_simple_item(h,name)
#define remove_float_value(h,name) remove_json_simple_item(h,name)
#define remove_bool_value(h,name) remove_json_simple_item(h,name)
#define remove_NULL_value(h,name) remove_json_simple_item(h,name)
#define remove_json_obj_handle_value(h,name) remove_json_complex_item(h,name,".")
#define remove_array_value(h,name) remove_json_complex_item(h,name,"[")
#define remove_value(h,name,type) remove_##type##_value(h,name)
#define remove_array_item(h,name,index,type) do{\
char* __fix__ = NULL;\
asprintf(&__fix__,"%s[%d]",name,index);\
remove_value(h,__fix__,type);\
free(__fix__);\
}while(0)
json_obj_handle create_json_obj(int size) {
json_obj_t* ret = (json_obj_handle) calloc(size * sizeof (json_item_t) + sizeof (json_obj_t), 1);
ret->size = size;
return ret;
}
char** sort_json_key(json_obj_handle h) {
int i = 0, j = 0;
char** results = (char**) calloc(sizeof (char*), h->used + 1);
results[0] = h->items[0].key;
for (; i < h->used; ++i) {
for (j = 0; j < i; ++j) {
if (strcmp(h->items[i].key, results[j]) < 0) {
break;
}
}
memmove(&results[j + 1], &results[j], h->used - j - 1);
results[j] = h->items[i].key;
}
return results;
}
static int object_to_string(json_obj_handle h, const char*const skip_name, char buffer[], int size) {
int i = 0, len = 0;
char *sub_str = NULL, *array = NULL, *objec = NULL;
DEBUG("skip = %s(%d), len = %d\n", skip_name, strlen(skip_name), size);
for (; i < h->used; ++i) {
DEBUG("buffer[%d,%s] = %s\n", i, h->items[i].key, buffer);
if ((h->items[i].flag & em_sri) || (h->items[i].flag & em_del)) {
DEBUG("processed %s pass...\n", h->items[i].key);
continue;
}
if (0 != strncmp(h->items[i].key, skip_name, strlen(skip_name))) {
DEBUG("no match %s pass...\n", h->items[i].key);
continue;
}
sub_str = h->items[i].key + strlen(skip_name);
array = strstr(sub_str, "[");
objec = strstr(sub_str, ".");
DEBUG("sub_str = %s(%p),array = %p,objec = %p\n", sub_str, sub_str, array, objec);
/*
* normal key:value pair
*/
if (NULL == array && NULL == objec) {
char* str = NULL;
asprintf(&str, "\"%s\":%s", sub_str, h->items[i].value);
h->items[i].flag |= em_sri;
len += snprintf(&buffer[len], size - len, "%s,", str);
free(str);
continue;
}
DEBUG("item name = %s\n", h->items[i].key);
/*
* array list key : []
*/
if (NULL != array && (NULL == objec || array < objec)) {
char* name = strndup(sub_str, (int) (array - sub_str));
int index = 0, j = 0;
DEBUG("array sub_str = %s,name = %s\n", sub_str, name);
len += snprintf(&buffer[len], size - len, "\"%s\":[", name);
for (j = 0; j < h->used; ++j) {
char* skip = NULL;
if ((h->items[j].flag & em_sri) || (h->items[j].flag & em_del)) {
continue;
}
asprintf(&skip, "%s[%d]", name, index++);
if (0 != strncmp(h->items[j].key, skip, strlen(skip))) {
free(skip);
continue;
}
DEBUG("will be process array : %s\n", skip);
if (h->items[j].key[strlen(skip)] == '.') {
len += object_to_string(h, skip, &buffer[len], size - len);
} else {
len += snprintf(&buffer[len], size - len, "%s,", h->items[j].value);
h->items[j].flag |= em_sri;
}
free(skip);
}
free(name);
--len;
len += snprintf(&buffer[len], size - len, "],");
TRACE("array buffer = %s\n", buffer);
} else {
/*
* object key : {}
*/
char* name = strndup(sub_str, (int) (objec - sub_str));
char* skip = strndup(h->items[i].key, (int) (objec - h->items[i].key) + 1);
DEBUG("object sub_str = %s, name = %s,skip_str = %s\n", sub_str, name, skip);
if (0 != strlen(name)) {
len += snprintf(&buffer[len], size - len, "\"%s\":{", name);
} else {
len += snprintf(&buffer[len], size - len, "{");
}
len += object_to_string(h, skip, &buffer[len], size - len) - 1;
len += snprintf(&buffer[len], size - len, "},");
TRACE("object buffer = %s\n", buffer);
free(skip);
free(name);
}
}
TRACE("%s %s %d\n", skip_name, buffer, len);
return len;
}
const char* to_string(json_obj_handle h, char buffer[], int size) {
int len = snprintf(buffer, size, "{");
len += object_to_string(h, "", &buffer[len], size - len) - 1;
len = (len > 0) ? len : 1;
snprintf(&buffer[len], size - len, "}");
for (len = 0; len < h->used; ++len) {
h->items[len].flag &= ~(em_sri);
}
return buffer;
}
void destroy_json_obj(json_obj_handle h) {
int i = 0;
for (; i < h->used; ++i) {
free(h->items[i].key);
free(h->items[i].value);
}
for (i = 0; i < BUCKET_SIZE; ++i) {
clear_bucket(h->bucket[i].next);
}
free(h);
}
test
JSON_OBJ_BEGIN(obj)
VALUE_ITEM(age, 18),
ARRAY_ITEM(arrays, 1, 2, 3, 4, 5.6),
STR_ITEM(name, "zhang san"),
JSON_OBJ_END(obj);
char buffer[10240] = {0};
static void param_test() {
json_obj_handle h = create_json_obj(10240);
/*
insert value
*/
insert_value(h, "age", int, 18);
{
int age = 0;
get_value(h,"age",int,age);
assert(age == 18);
}
/*
insert string
*/
insert_value(h, "name", string, "zhangsan");
{
char* name_str = NULL;
get_value(h,"name",string,name_str);
assert(0 == strcmp(name_str,"\"zhangsan\""));
free(name_str);
}
/*
insert object
*/
{
json_obj_handle local = create_json_obj(10);
insert_value(local, "city", string, "sh");
insert_value(local, "mailNo", string, "200240");
insert_value(h, "local", json_obj_handle, local);
destroy_json_obj(local);
}
{
json_obj_handle jh = NULL;
get_value(h,"local",json_obj_handle,jh);
assert(NULL != jh);
destroy_json_obj(jh);
}
/*
insert array
*/
{
json_obj_handle f1 = create_json_obj(10);
json_obj_handle f2 = create_json_obj(10);
insert_value(f1, "city", string, "sh");
insert_value(f1, "mailNo", string, "200240");
insert_value(f1, "mailNo",int,200241);
insert_value(f2, "city", string, "sh");
insert_value(f2, "mailNo", string, "200240");
insert_array(h, "friends", json_obj_handle, f1, f2);
destroy_json_obj(f1);
destroy_json_obj(f2);
}
insert_array(h, "number", int, 1, 2, 3, 4, 5, 6, 7, 8);
insert_array(h, "string", string, "a", "b", "c", "d", "e");
{
json_obj_handle jh[10] = {NULL,NULL};
char* s[10] = {NULL};
int i[10] = {0};
int len = 10,k = 1;
get_array(h,"friends",json_obj_handle,jh,len);
assert(2 == len);
assert(NULL != jh[0]);
assert(NULL != jh[1]);
assert(NULL == jh[2]);
for(k = 0; k < 2; ++k){
destroy_json_obj(jh[k]);
}
len = 10;
get_array(h,"number",int,i,len);
assert(8 == len);
for(k = 0; k < 8; ++k){
assert( (k+1) == i[k]);
}
assert(0 == i[9]);
len = 10;
get_array(h,"string",string,s,len);
assert(5 == len);
assert(0 == strcmp(s[0],"\"a\""));
assert(NULL == s[9]);
get_array_item(h,"number",int,3,len);
assert(4 == len);
get_array_item(h,"string",string,0,s[9]);
assert(0 == strcmp(s[0],s[9]));
for(k = 0; k < 5 ; ++k){
free(s[k]);
}
free(s[9]);
}
/*
insert bool
*/
insert_value(h, "boolean1", bool, 1);
insert_value(h, "boolean2", bool, 0);
/*
insert null
*/
insert_value(h, "null", NULL, NULL);
to_string(h, buffer, sizeof (buffer));
//printf("%s\n", buffer);
/*
* remove test
*/
{
int age = 0;
remove_value(h,"age",int);
assert(0 == get_value(h,"age",int,age));
}
{
char* name = NULL;
remove_value(h,"name",string);
assert(0 == get_value(h,"name",string,name));
assert(NULL == name);
}
{
json_obj_handle jh = NULL;
remove_value(h,"local",json_obj_handle);
assert(0 == get_value(h,"local",json_obj_handle,jh));
assert(NULL == jh);
}
{
json_obj_handle ja[10] = {0},jh = NULL;
int size = 10;
remove_array_item(h,"friends",0,json_obj_handle);
get_array_item(h,"friends",json_obj_handle,0,jh);
assert(0 == jh);
get_array(h,"friends",json_obj_handle,ja,size);
assert(1 == size);
assert(NULL != ja[0]);
assert(NULL == ja[1]);
destroy_json_obj(ja[0]);
remove_value(h,"friends",array);
size = 10;
get_array(h,"friends",json_obj_handle,ja,size);
assert(0 == size);
assert(NULL == ja[0]);
}
{
int ai[10] = {0},len = 10;
remove_array_item(h,"number",3,int);
get_array(h,"number",int,ai,len);
assert(7 == len);
assert(5 == ai[3]);
assert(0 == ai[9]);
remove_value(h,"number",array);
len = 10;
get_array(h,"number",int,ai,len);
assert(0 == len);
}
{
char* ai[10] = {NULL};
int len = 10;
remove_array_item(h,"string",3,string);
get_array(h,"string",string,ai,len);
assert(4 == len);
assert(0 == strcmp("\"e\"",ai[3]));
assert(NULL == ai[9]);
remove_value(h,"string",array);
for(len = 0; len < 10; ++len){
free(ai[len]);
ai[len] = NULL;
}
len = 10;
get_array(h,"string",string,ai,len);
assert(0 == len);
}
{
remove_value(h,"boolean1",bool);
remove_value(h,"boolean2",bool);
remove_value(h,"null",NULL);
}
memset(buffer, 0, sizeof (buffer));
to_string(h, buffer, sizeof (buffer));
assert(0 == strcmp("{}",buffer));
{
assert(24 == h->used);
insert_value(h,"test",string,"this is a test");
pack_json_item(h);
assert(1 == h->used);
}
{
char* test_str = NULL;
get_value(h,"test",string,test_str);
assert(NULL != test_str);
assert(0 == strcmp("\"this is a test\"",test_str));
free(test_str);
}
destroy_json_obj(h);
}
int main(int argn, const char* argv[]) {
for(;;)param_test();
#ifdef MEM_LEAK_TEST
check();
#endif
return 0;
}