From 29576fee7db25a33ac780ac721c8f92d1f2a1731 Mon Sep 17 00:00:00 2001 From: erickson Date: Mon, 6 Jun 2005 17:44:29 +0000 Subject: [PATCH] adding early custom json code git-svn-id: svn://svn.open-ils.org/ILS/trunk@760 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- OpenSRF/src/objson/Makefile | 29 ++ OpenSRF/src/objson/json_parser.c | 635 +++++++++++++++++++++++++++++++ OpenSRF/src/objson/json_parser.h | 59 +++ OpenSRF/src/objson/object.c | 527 +++++++++++++++++++++++++ OpenSRF/src/objson/object.h | 197 ++++++++++ 5 files changed, 1447 insertions(+) create mode 100644 OpenSRF/src/objson/Makefile create mode 100644 OpenSRF/src/objson/json_parser.c create mode 100644 OpenSRF/src/objson/json_parser.h create mode 100644 OpenSRF/src/objson/object.c create mode 100644 OpenSRF/src/objson/object.h diff --git a/OpenSRF/src/objson/Makefile b/OpenSRF/src/objson/Makefile new file mode 100644 index 0000000000..d5562580cb --- /dev/null +++ b/OpenSRF/src/objson/Makefile @@ -0,0 +1,29 @@ +CC = gcc +HEADER_DIR = ../../include +CFLAGS = -g -O2 -Wall -fPIC -I $(HEADER_DIR) #-D_REENTRANT +LDFLAGS = -shared -W1 +LIB_DIR = ../../lib +LDLIBS = -L . -lobjson -L $(LIB_DIR) +OBJS = object.o json_parser.o utils.o +UTIL_DIR = ../utils + +all: test + +test: lib objson_test.c + $(CC) $(CFLAGS) $(LDLIBS) objson_test.c -o $@ + +lib: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) -o $(LIB_DIR)/libobjson.so + cp *.h $(HEADER_DIR)/objson/ + +object.o: object.h object.c + $(CC) -c $(CFLAGS) object.c -o $@ + +json_parser.o: json_parser.h json_parser.c + $(CC) -c $(CFLAGS) json_parser.c -o $@ + +utils.o: $(HEADER_DIR)/utils.h $(UTIL_DIR)/utils.c + $(CC) -c $(CFLAGS) $(UTIL_DIR)/utils.c -o $@ + +clean: + /bin/rm -f *.o test diff --git a/OpenSRF/src/objson/json_parser.c b/OpenSRF/src/objson/json_parser.c new file mode 100644 index 0000000000..dd4931afa0 --- /dev/null +++ b/OpenSRF/src/objson/json_parser.c @@ -0,0 +1,635 @@ +#include "json_parser.h" + +/* keep a copy of the length of the current json string so we don't + * have to calculate it in each function + */ +int current_strlen; /* XXX need to move this into the function params for thread support */ + +object* json_parse_string(char* string) { + + if(string == NULL) { + return NULL; + } + + current_strlen = strlen(string); + + if(current_strlen == 0) { + return NULL; + } + + object* obj = new_object(NULL); + unsigned long index = 0; + + int status = _json_parse_string(string, &index, obj); + if(!status) + return obj; + + if(status == -2) + return NULL; + + return NULL; +} + + +int _json_parse_string(char* string, unsigned long* index, object* obj) { + assert(string && index && *index < current_strlen); + + int status = 0; /* return code from parsing routines */ + char* classname = NULL; /* object class hint */ + json_eat_ws(string, index, 1); /* remove leading whitespace */ + + char c = string[*index]; + + /* remove any leading comments */ + if( c == '/' ) { + + while(1) { + (*index)++; /* move to second comment char */ + status = json_eat_comment(string, index, &classname, 1); + if(status) return status; + + json_eat_ws(string, index, 1); + c = string[*index]; + if(c != '/') + break; + } + } + + json_eat_ws(string, index, 1); /* remove leading whitespace */ + + if(*index >= current_strlen) + return -2; + + switch(c) { + + /* json string */ + case '"': + (*index)++; + status = json_parse_json_string(string, index, obj); + break; + + /* json array */ + case '[': + (*index)++; + status = json_parse_json_array(string, index, obj); + break; + + case '{': + (*index)++; + status = json_parse_json_object(string, index, obj); + break; + + case 'n': + case 'N': + status = json_parse_json_null(string, index, obj); + break; + + + case 'f': + case 'F': + case 't': + case 'T': + status = json_parse_json_bool(string, index, obj); + break; + + /* we should never get here */ + default: + if(is_number(c) || c == '.' || c == '-') { /* are we a number? */ + status = json_parse_json_number(string, index, obj); + if(status) return status; + break; + } + + (*index)--; + return json_handle_error(string, index, "_json_parse_string() final switch clause"); + } + + if(status) return status; + + json_eat_ws(string, index, 1); + + if( *index < current_strlen ) { + /* remove any trailing comments */ + c = string[*index]; + if( c == '/' ) { + (*index)++; + status = json_eat_comment(string, index, NULL, 0); + if(status) return status; + } + } + + if(classname){ + obj->set_class(obj, classname); + free(classname); + } + + return 0; +} + + +int json_parse_json_null(char* string, unsigned long* index, object* obj) { + + if(*index >= (current_strlen - 3)) { + return json_handle_error(string, index, + "_parse_json_string(): invalid null" ); + } + + if(!strncasecmp(string + (*index), "null", 4)) { + (*index) += 4; + obj->is_null = 1; + return 0; + } else { + return json_handle_error(string, index, + "_parse_json_string(): invalid null" ); + } +} + +/* should be at the first character of the bool at this point */ +int json_parse_json_bool(char* string, unsigned long* index, object* obj) { + assert(string && obj && *index < current_strlen); + + char* ret = "json_parse_json_bool(): truncated bool"; + + if( *index >= (current_strlen - 5)) + return json_handle_error(string, index, ret); + + if(!strncasecmp( string + (*index), "false", 5)) { + (*index) += 5; + return 0; + } + + if( *index >= (current_strlen - 4)) + return json_handle_error(string, index, ret); + + if(!strncasecmp( string + (*index), "true", 4)) { + (*index) += 4; + return 0; + } + + return json_handle_error(string, index, ret); +} + + +/* expecting the first character of the number */ +int json_parse_json_number(char* string, unsigned long* index, object* obj) { + assert(string && obj && *index < current_strlen); + + growing_buffer* buf = buffer_init(64); + char c = string[*index]; + + int done = 0; + int dot_seen = 0; + + /* negative number? */ + if(c == '-') { buffer_add(buf, "-"); (*index)++; } + + while(*index < current_strlen) { + + if(is_number(c)) + buffer_add_char(buf, c); + + else if( c == '.' ) { + if(dot_seen) { + return json_handle_error(string, index, + "json_parse_json_number(): malformed json number"); + } + dot_seen = 1; + } else { + done = 1; break; + } + (*index)++; + c = string[*index]; + if(done) break; + } + + if(dot_seen) { + obj->is_double = 1; + obj->double_value = strtod(buf->buf, NULL); + buffer_free(buf); + return 0; + + } else { + obj->is_number = 1; + obj->num_value = atol(buf->buf); + buffer_free(buf); + return 0; + } +} + +/* index should point to the character directly following the '['. when done + * index will point to the character directly following the ']' character + */ +int json_parse_json_array(char* string, unsigned long* index, object* obj) { + assert(string && obj && index && *index < current_strlen); + + int status; + int in_parse = 0; /* true if this array already contains one item */ + obj->is_array = 1; + while(*index < current_strlen) { + + json_eat_ws(string, index, 1); + + if(string[*index] == ']') { + (*index)++; + break; + } + + if(in_parse) { + json_eat_ws(string, index, 1); + if(string[*index] != ',') { + return json_handle_error(string, index, + "json_parse_json_array(): array not followed by a ','"); + } + (*index)++; + json_eat_ws(string, index, 1); + } + + object* item = new_object(NULL); + status = _json_parse_string(string, index, item); + + if(status) return status; + obj->push(obj, item); + in_parse = 1; + } + + return 0; +} + + +/* index should point to the character directly following the '{'. when done + * index will point to the character directly following the '}' + */ +int json_parse_json_object(char* string, unsigned long* index, object* obj) { + assert(string && obj && index && *index < current_strlen); + + obj->is_hash = 1; + int status; + int in_parse = 0; /* true if we've already added one item to this object */ + + while(*index < current_strlen) { + + json_eat_ws(string, index, 1); + + if(string[*index] == '}') { + (*index)++; + break; + } + + if(in_parse) { + if(string[*index] != ',') { + return json_handle_error(string, index, + "json_parse_json_object(): object missing ',' betweenn elements" ); + } + (*index)++; + json_eat_ws(string, index, 1); + } + + /* first we grab the hash key */ + object* key_obj = new_object(NULL); + status = _json_parse_string(string, index, key_obj); + if(status) return status; + + if(!key_obj->is_string) { + return json_handle_error(string, index, + "_json_parse_json_object(): hash key not a string"); + } + + char* key = key_obj->string_data; + + json_eat_ws(string, index, 1); + + if(string[*index] != ':') { + return json_handle_error(string, index, + "json_parse_json_object(): hash key not followed by ':' character"); + } + + (*index)++; + + /* now grab the value object */ + json_eat_ws(string, index, 1); + object* value_obj = new_object(NULL); + status = _json_parse_string(string, index, value_obj); + if(status) return status; + + /* put the data into the object and continue */ + obj->add_key(obj, key, value_obj); + free_object(key_obj); + in_parse = 1; + } + return 0; +} + + + +/* when done, index will point to the character after the closing quote */ +int json_parse_json_string(char* string, unsigned long* index, object* obj) { + assert(string && index && *index < current_strlen); + + int in_escape = 0; + int done = 0; + growing_buffer* buf = buffer_init(64); + + while(*index < current_strlen) { + + char c = string[*index]; + + switch(c) { + + case '\\': + if(in_escape) { + buffer_add(buf, "\\"); + in_escape = 0; + } else + in_escape = 1; + break; + + case '"': + if(in_escape) { + buffer_add(buf, "\""); + in_escape = 0; + } else + done = 1; + break; + + case 't': + if(in_escape) { + buffer_add(buf,"\t"); + in_escape = 0; + } else + buffer_add_char(buf, c); + break; + + case 'b': + if(in_escape) { + buffer_add(buf,"\b"); + in_escape = 0; + } else + buffer_add_char(buf, c); + break; + + case 'f': + if(in_escape) { + buffer_add(buf,"\f"); + in_escape = 0; + } else + buffer_add_char(buf, c); + break; + + case 'r': + if(in_escape) { + buffer_add(buf,"\r"); + in_escape = 0; + } else + buffer_add_char(buf, c); + break; + + case 'n': + if(in_escape) { + buffer_add(buf,"\n"); + in_escape = 0; + } else + buffer_add_char(buf, c); + break; + + case 'u': + if(in_escape) { + (*index)++; + + if(*index >= (current_strlen - 4)) { + return json_handle_error(string, index, + "json_parse_json_string(): truncated escaped unicode"); } + + char buff[5]; + memset(buff,0,5); + memcpy(buff, string + (*index), 4); + + + /* ------------------------------------------------------------------- */ + /* ------------------------------------------------------------------- */ + /* This was taken directly from json-c http://oss.metaparadigm.com/json-c/ */ + unsigned char utf_out[3]; + memset(utf_out,0,3); + + #define hexdigit(x) ( ((x) <= '9') ? (x) - '0' : ((x) & 7) + 9) + + unsigned int ucs_char = + (hexdigit(string[*index] ) << 12) + + (hexdigit(string[*index + 1]) << 8) + + (hexdigit(string[*index + 2]) << 4) + + hexdigit(string[*index + 3]); + + if (ucs_char < 0x80) { + utf_out[0] = ucs_char; + buffer_add(buf, utf_out); + + } else if (ucs_char < 0x800) { + utf_out[0] = 0xc0 | (ucs_char >> 6); + utf_out[1] = 0x80 | (ucs_char & 0x3f); + buffer_add(buf, utf_out); + + } else { + utf_out[0] = 0xe0 | (ucs_char >> 12); + utf_out[1] = 0x80 | ((ucs_char >> 6) & 0x3f); + utf_out[2] = 0x80 | (ucs_char & 0x3f); + buffer_add(buf, utf_out); + } + /* ------------------------------------------------------------------- */ + /* ------------------------------------------------------------------- */ + + + (*index) += 3; + in_escape = 0; + + } else { + + buffer_add_char(buf, c); + } + + break; + + default: + buffer_add_char(buf, c); + } + + (*index)++; + if(done) break; + } + + obj->set_string(obj, buf->buf); + buffer_free(buf); + return 0; +} + + +void json_eat_ws(char* string, unsigned long* index, int eat_all) { + assert(string && index); + if(*index >= current_strlen) + return; + + if( eat_all ) { /* removes newlines, etc */ + while(string[*index] == ' ' || + string[*index] == '\n' || + string[*index] == '\t') + (*index)++; + } + + else + while(string[*index] == ' ') (*index)++; +} + + +/* index should be at the '*' character at the beginning of the comment. + * when done, index will point to the first character after the final / + */ +int json_eat_comment(char* string, unsigned long* index, char** buffer, int parse_class) { + assert(string && index && *index < current_strlen); + + if(string[*index] != '*' && string[*index] != '/' ) + return json_handle_error(string, index, + "json_eat_comment(): invalid character after /"); + + /* chop out any // style comments */ + if(string[*index] == '/') { + (*index)++; + char c = string[*index]; + while(*index < current_strlen) { + (*index)++; + if(c == '\n') + return 0; + c = string[*index]; + } + return 0; + } + + (*index)++; + + int on_star = 0; /* true if we just saw a '*' character */ + + /* we're just past the '*' */ + if(!parse_class) { /* we're not concerned with class hints */ + while(*index < current_strlen) { + if(string[*index] == '/') { + if(on_star) { + (*index)++; + return 0; + } + } + + if(string[*index] == '*') on_star = 1; + else on_star = 0; + + (*index)++; + } + return 0; + } + + + + growing_buffer* buf = buffer_init(64); + + int first_dash = 0; + int second_dash = 0; + int third_dash = 0; + int fourth_dash = 0; + + int in_hint = 0; + int done = 0; + + /*--S hint--*/ /* <-- Hints look like this */ + /*--E hint--*/ + + while(*index < current_strlen) { + char c = string[*index]; + + switch(c) { + + case '-': + on_star = 0; + if(third_dash) fourth_dash = 1; + else if(in_hint) third_dash = 1; + else if(first_dash) second_dash = 1; + else first_dash = 1; + break; + + case 'S': + on_star = 0; + if(second_dash && !in_hint) { + (*index)++; + json_eat_ws(string, index, 1); + (*index)--; /* this will get incremented at the bottom of the loop */ + in_hint = 1; + break; + } + + case 'E': + on_star = 0; + if(second_dash && !in_hint) { + (*index)++; + json_eat_ws(string, index, 1); + (*index)--; /* this will get incremented at the bottom of the loop */ + in_hint = 1; + break; + } + + case '*': + on_star = 1; + break; + + case '/': + if(on_star) + done = 1; + else + on_star = 0; + break; + + default: + on_star = 0; + if(in_hint) + buffer_add_char(buf, c); + } + + (*index)++; + if(done) break; + } + + if( buf->n_used > 0 && buffer) + *buffer = buffer_data(buf); + + buffer_free(buf); + return 0; +} + +int is_number(char c) { + switch(c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return 1; + } + return 0; +} + +int json_handle_error(char* string, unsigned long* index, char* err_msg) { + + char buf[60]; + memset(buf, 0, 60); + + if(*index > 30) + strncpy( buf, string + (*index - 30), 59 ); + else + strncpy( buf, string, 59 ); + + fprintf(stderr, + "\nError parsing json string at charracter %c (code %d) and index %ld\nMsg:\t%s\nNear:\t%s\n\n", + string[*index], string[*index], *index, err_msg, buf ); + return -1; +} + + diff --git a/OpenSRF/src/objson/json_parser.h b/OpenSRF/src/objson/json_parser.h new file mode 100644 index 0000000000..29d795d873 --- /dev/null +++ b/OpenSRF/src/objson/json_parser.h @@ -0,0 +1,59 @@ +/* --------------------------------------------------------------------------------------- + JSON parser. + * --------------------------------------------------------------------------------------- */ + + +#include +#include "object.h" +#include "utils.h" + +#ifndef JSON_PARSER_H +#define JSON_PARSER_H + + + + +/* returns NULL on error. if string is NULL, returns an object whose is_null flag + * is set to true */ +object* json_parse_string(char* string); + +/* does the actual parsing work. returns 0 on success. -1 on error and + * -2 if there was no object to build (string was all comments) + */ +int _json_parse_string(char* string, unsigned long* index, object* obj); + +/* returns 0 on success and turns obj into a string object */ +int json_parse_json_string(char* string, unsigned long* index, object* obj); + +/* returns 0 on success and turns obj into a number or double object */ +int json_parse_json_number(char* string, unsigned long* index, object* obj); + +int json_parse_json_object(char* string, unsigned long* index, object* obj); + +/* returns 0 on success and turns object into an array object */ +int json_parse_json_array(char* string, unsigned long* index, object* obj); + +/* churns through whitespace and increments index as it goes. + * eat_all means we should eat newlines, tabs + */ +void json_eat_ws(char* string, unsigned long* index, int eat_all); + +int json_parse_json_bool(char* string, unsigned long* index, object* obj); + +/* removes comments from a json string. if the comment contains a class hint + * and class_hint isn't NULL, an allocated char* with the class name will be + * shoved into *class_hint. returns 0 on success, -1 on parse error. + * 'index' is assumed to be at the second character (*) of the comment + */ +int json_eat_comment(char* string, unsigned long* index, char** class_hint, int parse_class); + +/* prints a useful error message to stderr. always returns -1 */ +int json_handle_error(char* string, unsigned long* index, char* err_msg); + +/* returns true if c is 0-9 */ +int is_number(char c); + +int json_parse_json_null(char* string, unsigned long* index, object* obj); + + +#endif diff --git a/OpenSRF/src/objson/object.c b/OpenSRF/src/objson/object.c new file mode 100644 index 0000000000..c8d5222d59 --- /dev/null +++ b/OpenSRF/src/objson/object.c @@ -0,0 +1,527 @@ +#include "object.h" +#include "json_parser.h" +#include + + + +/* ---------------------------------------------------------------------- */ +/* See object.h for function info */ +/* ---------------------------------------------------------------------- */ + +object* new_object(char* string_value) { + return _init_object(string_value); +} + +object* _init_object(char* string_value) { + + object* obj = (object*) safe_malloc(sizeof(object)); + obj->size = 0; + obj->data = NULL; + + obj->push = &object_push; + obj->add_index = &object_add_index; + obj->add_key = &object_add_key; + obj->get_index = &object_get_index; + obj->get_key = &object_get_key; + obj->get_string = &object_get_string; + obj->set_string = &object_set_string; + obj->set_number = &object_set_number; + obj->set_class = &object_set_class; + obj->set_double = &object_set_double; + obj->remove_index = &object_remove_index; + obj->remove_key = &object_remove_key; + obj->to_json = &object_to_json; + obj->set_comment = &object_set_comment; + + if(string_value) { + obj->is_string = 1; + obj->string_data = strdup(string_value); + } else + obj->is_null = 1; + + return obj; +} + +object_node* new_object_node(object* obj) { + object_node* node = (object_node*) safe_malloc(sizeof(object_node)); + node->item = obj; + node->next = NULL; + node->index = -1; + return node; +} + +unsigned long object_push(object* obj, object* new_obj) { + assert(obj != NULL); + object_clear_type(obj); + obj->is_array = 1; + + if(new_obj == NULL) { + new_obj = new_object(NULL); + new_obj->is_null = 1; + } + + object_node* node = new_object_node(new_obj); + node->index = obj->size++; + + if( obj->size > MAX_OBJECT_NODES ) + return -1; + + if(obj->data == NULL) { + obj->data = node; + + } else { + /* append the node onto the end */ + object_node* tmp = obj->data; + while(tmp) { + if(tmp->next == NULL) break; + tmp = tmp->next; + } + tmp->next = node; + } + return obj->size; +} + +unsigned long object_add_index(object* obj, unsigned long index, object* new_obj) { + assert(obj != NULL && index <= MAX_OBJECT_NODES); + object_clear_type(obj); + obj->is_array = 1; + + if(obj->size <= index) + obj->size = index + 1; + + if(new_obj == NULL) { + new_obj = new_object(NULL); + new_obj->is_null = 1; + } + + object_node* node = new_object_node(new_obj); + node->index = index; + + if( obj->data == NULL ) { + obj->data = node; + + } else { + + if(obj->data->index == index) { + object_node* tmp = obj->data->next; + free_object_node(obj->data); + obj->data = node; + node->next = tmp; + + } else { + + object_node* prev = obj->data; + object_node* cur = prev->next; + int inserted = 0; + + while(cur != NULL) { + + /* replace an existing node */ + if( cur->index == index ) { + object_node* tmp = cur->next; + free_object_node(cur); + node->next = tmp; + prev->next = node; + inserted = 1; + break; + + /* instert between two nodes */ + } else if( prev->index < index && cur->index > index ) { + prev->next = node; + node->next = cur; + inserted = 1; + break; + } + prev = cur; + cur = cur->next; + } + + /* shove on to the end */ + if(!inserted) + prev->next = node; + } + } + + return obj->size; +} + + +void object_shift_index(object* obj, unsigned long index) { + assert(obj && index <= MAX_OBJECT_NODES); + if(obj->data == NULL) { + obj->size = 0; + return; + } + + object_node* data = obj->data; + while(data) { + if(data->index >= index) + data->index--; + data = data->next; + } + obj->size--; +} + +unsigned long object_remove_index(object* obj, unsigned long index) { + assert(obj != NULL && index <= MAX_OBJECT_NODES); + if(obj->data == NULL) return 0; + + /* removing the first item in the list */ + if(obj->data->index == index) { + object_node* tmp = obj->data->next; + free_object_node(obj->data); + obj->data = tmp; + object_shift_index(obj,index); + return obj->size; + } + + + object_node* prev = obj->data; + object_node* cur = prev->next; + + while(cur) { + if(cur->index == index) { + object_node* tmp = cur->next; + free_object_node(cur); + prev->next = tmp; + object_shift_index(obj,index); + break; + } + prev = cur; + cur = cur->next; + } + + return obj->size; +} + + +unsigned long object_remove_key(object* obj, char* key) { + assert(obj && key); + if(obj->data == NULL) return 0; + + /* removing the first item in the list */ + if(!strcmp(obj->data->key, key)) { + object_node* tmp = obj->data->next; + free_object_node(obj->data); + obj->data = tmp; + if(!obj->data) + obj->size = 0; + + return obj->size; + } + + object_node* prev = obj->data; + object_node* cur = prev->next; + + while(cur) { + if(!strcmp(cur->key,key)) { + object_node* tmp = cur->next; + free_object_node(cur); + prev->next = tmp; + obj->size--; + break; + } + prev = cur; + cur = cur->next; + } + + return obj->size; +} + + +unsigned long object_add_key(object* obj, char* key, object* new_obj) { + + assert(obj != NULL && key != NULL); + object_clear_type(obj); + obj->is_hash = 1; + + + if(new_obj == NULL) { + new_obj = new_object(NULL); + new_obj->is_null = 1; + } + + object_node* node = new_object_node(new_obj); + node->key = strdup(key); + + if( obj->data == NULL ) { + obj->data = node; + obj->size++; + + } else { + + /* replace the first node */ + if(!strcmp(obj->data->key, key)) { + object_node* tmp = obj->data->next; + free_object_node(obj->data); + obj->data = node; + node->next = tmp; + + } else { + + object_node* prev = obj->data; + object_node* cur = prev->next; + int inserted = 0; + + while(cur != NULL) { + + /* replace an existing node */ + if( !strcmp(cur->key, key) ) { + object_node* tmp = cur->next; + free_object_node(cur); + node->next = tmp; + prev->next = node; + inserted = 1; + break; + } + + prev = cur; + cur = cur->next; + } + + /* shove on to the end */ + if(!inserted) { + prev->next = node; + obj->size++; + } + } + } + + return obj->size; +} + + +void free_object(object* obj) { + + if(obj == NULL) return; + if(obj->classname) free(obj->classname); + if(obj->comment) free(obj->comment); + + while(obj->data) { + object_node* tmp = obj->data->next; + free_object_node(obj->data); + obj->data = tmp; + } + + if(obj->string_data) + free(obj->string_data); + free(obj); +} + +void free_object_node(object_node* node) { + if(node == NULL) return; + if(node->key) free(node->key); + free_object(node->item); + free(node); +} + +object* object_get_index( object* obj, unsigned long index ) { + assert(obj != NULL && index <= MAX_OBJECT_NODES); + object_node* node = obj->data; + while(node) { + if(node->index == index) + return node->item; + node = node->next; + } + return NULL; +} + +object* object_get_key( object* obj, char* key ) { + assert(obj && key); + object_node* node = obj->data; + + while(node) { + if(node->key && !strcmp(node->key, key)) + return node->item; + node = node->next; + } + + return NULL; +} + +char* object_get_string(object* obj) { + assert(obj != NULL); + return obj->string_data; +} + +void object_set_string(object* obj, char* string) { + assert(obj); + object_clear_type(obj); + obj->is_string = 1; + if(string) + obj->string_data = strdup(string); +} + + +void object_set_number(object* obj, long num) { + assert(obj); + object_clear_type(obj); + obj->is_number = 1; + obj->num_value = num; +} + +void object_set_double(object* obj, double num) { + assert(obj); + object_clear_type(obj); + obj->is_double = 1; + obj->double_value = num; +} + + +void object_set_class(object* obj, char* classname) { + assert(obj && classname); + obj->classname = strdup(classname); +} + + + +char* object_to_json(object* obj) { + + if(obj == NULL) + return strdup("null"); + + growing_buffer* buf = buffer_init(64); + + /* add class hints if we have a class name */ + if(obj->classname) { + buffer_add(buf,"/*--S "); + buffer_add(buf,obj->classname); + buffer_add(buf, "--*/"); + } + + if(obj->is_bool && obj->bool_value) + buffer_add(buf, "true"); + + else if(obj->is_bool && ! obj->bool_value) + buffer_add(buf, "false"); + + else if(obj->is_number) { + char b[128]; + memset(b, 0, 128); + sprintf(b, "%ld", obj->num_value); + buffer_add(buf, b); + } + + else if(obj->is_double) { + char b[128]; + memset(b, 0, 128); + sprintf(b, "%lf", obj->double_value); + buffer_add(buf, b); + } + + if(obj->is_null) + buffer_add(buf, "null"); + + else if (obj->is_string) { + + buffer_add(buf, "\""); + char* data = obj->string_data; + int len = strlen(data); + + char* output = uescape(data, len, 1); + buffer_add(buf, output); + free(output); + buffer_add(buf, "\""); + + } else if(obj->is_array) { + + buffer_add(buf, "["); + int i; + for( i = 0; i!= obj->size; i++ ) { + char* data = object_to_json(obj->get_index(obj,i)); + buffer_add(buf, data); + free(data); + if(i != obj->size - 1) + buffer_add(buf, ","); + } + buffer_add(buf, "]"); + + } else if(obj->is_hash) { + buffer_add(buf, "{"); + object_iterator* itr = new_iterator(obj); + object_node* tmp; + while( (tmp = itr->next(itr)) ) { + buffer_add(buf, "\""); + buffer_add(buf, tmp->key); + buffer_add(buf, "\":"); + char* data = object_to_json(tmp->item); + buffer_add(buf, data); + if(itr->has_next(itr)) + buffer_add(buf, ","); + free(data); + } + free_iterator(itr); + buffer_add(buf, "}"); + } + + /* close out the object hint */ + if(obj->classname) { + buffer_add(buf, "/*--E "); + buffer_add(buf, obj->classname); + buffer_add(buf, "--*/"); + } + + if(obj->comment) { + buffer_add(buf, " /*"); + buffer_add(buf, obj->comment); + buffer_add(buf, "*/"); + } + + char* data = buffer_data(buf); + buffer_free(buf); + return data; + +} + + +void object_clear_type(object* obj) { + if(obj == NULL) return; + obj->is_string = 0; + obj->is_hash = 0; + obj->is_array = 0; + obj->is_bool = 0; + obj->is_null = 0; +} + + +void object_set_comment(object* obj, char* com) { + assert(obj && com); + obj->comment = strdup(com); +} + + + +/* ---------------------------------------------------------------------- */ +/* Iterator */ + +object_iterator* new_iterator(object* obj) { + object_iterator* iter = safe_malloc(sizeof(object_iterator)); + iter->obj = obj; + iter->current = obj->data; + iter->next = &object_iterator_next; + iter->has_next = &object_iterator_has_next; + return iter; +} + +object_node* object_iterator_next(object_iterator* itr) { + assert( itr != NULL ); + + object_node* tmp = itr->current; + if(tmp == NULL) return NULL; + itr->current = itr->current->next; + + return tmp; +} + +void free_iterator(object_iterator* iter) { + if(iter == NULL) return; + free(iter); +} + +int object_iterator_has_next(object_iterator* itr) { + assert(itr); + if(itr->current) return 1; + return 0; +} + + diff --git a/OpenSRF/src/objson/object.h b/OpenSRF/src/objson/object.h new file mode 100644 index 0000000000..f4ad5f2f8f --- /dev/null +++ b/OpenSRF/src/objson/object.h @@ -0,0 +1,197 @@ +/* --------------------------------------------------------------------------------------- + Generic object framework for C. An object can be either a string, boolean, null, + number, array or hash (think Perl hash, dictionary, etc.). + * --------------------------------------------------------------------------------------- */ + +#include +#include +#include +#include + +#include +#include "utils.h" + +/* does we need this? */ +#define MAX_OBJECT_NODES 1000000 + +#ifndef OBJECT_H +#define OBJECT_H + +/* top leve generic object structuure */ +struct object_struct { + + /* how many sub-objects do we contain. Note that this includes null + * array elements in sparse arrays */ + unsigned long size; + + /* optional class hint */ + char* classname; + + /* these determine how we define a given object */ + int is_array; + int is_hash; + int is_string; + int is_null; + int is_bool; + int is_number; + int is_double; + + /* attached accessor/mutator methods for the OO inclined*/ + unsigned long (*push) (struct object_struct* src, struct object_struct*); + unsigned long (*add_index) (struct object_struct* src, unsigned long index, struct object_struct*); + unsigned long (*add_key) (struct object_struct* src, char* key, struct object_struct*); + struct object_struct* (*get_index) (struct object_struct*, unsigned long index); + struct object_struct* (*get_key) (struct object_struct*, char* key); + void (*set_string) (struct object_struct*, char*); + void (*set_number) (struct object_struct*, long number); + void (*set_double) (struct object_struct*, double number); + void (*set_class) (struct object_struct*, char* classname); + unsigned long (*remove_index) (struct object_struct*, unsigned long index); + unsigned long (*remove_key) (struct object_struct*, char* key); + char* (*get_string) (struct object_struct*); + char* (*to_json) (struct object_struct*); + void (*set_comment) (struct object_struct*, char* com); + + /* our list of sub-objects */ + struct object_node_struct* data; + + /* if we're a string, here's our data */ + char* string_data; + + /* if we're a boolean value, here's our value */ + int bool_value; + + /* if we're a number, here's our value */ + long num_value; + + /* if we're a double, here's our value */ + double double_value; + + /* client may provide a comment string which will be + * added serialized object when applicable + */ + char* comment; +}; +typedef struct object_struct object; + + +/* this contains a single element of the object along with the elements + * index (if this object is an array) and key (if this object is a hash) + */ +struct object_node_struct { + unsigned long index; /* our array position */ + char* key; /* our hash key */ + object* item; /* our object */ + struct object_node_struct* next; /* pointer to the next object node */ +}; +typedef struct object_node_struct object_node; + +/* utility object for iterating over hash objects */ +struct object_iterator_struct { + object* obj; /* the topic object */ + object_node* current; /* the current node within the object */ + object_node* (*next) (struct object_iterator_struct*); + int (*has_next) (struct object_iterator_struct*); +}; +typedef struct object_iterator_struct object_iterator; + +/* allocates a new iterator */ +object_iterator* new_iterator(object* obj); + +/* de-allocates an iterator */ +void free_iterator(object_iterator*); + +/* returns the object_node currently pointed to by the iterator + * and increments the pointer to the next node + */ +object_node* object_iterator_next(object_iterator*); + +/* returns true if there is another node after the node + * currently pointed to + */ +int object_iterator_has_next(object_iterator*); + + +/* allocates a new object. classname is optional */ +object* new_object(char* string); + +/* utility method for initing an object */ +object* _init_object(); + +/* returns a pointer to the object at the given index */ +object* object_get_index( object* obj, unsigned long index ); + + +/* returns a pointer to the object with the given key */ +object* object_get_key( object* obj, char* key ); + +/* de-allocates a object */ +void free_object(object*); + +/* allocates a new object node */ +object_node* new_object_node(object* obj); + +/* de-allocates a object node */ +void free_object_node(object_node*); + +/* pushes the given object onto the end of the list, + * returns the size on success, -1 on error + */ +unsigned long object_push(object*, object* obj); + +/* removes the object at the given index (if one exists) and inserts + * the new one. returns the size on success, -1 on error + */ +unsigned long object_add_index(object*, unsigned long index, object* obj); + +/* inserts the new object, overwriting any previous object with the given key + * returns the size on success, -1 on error + * if 'obj' is NULL, a new object is inserted at key 'key' with 'is_null' + * set to true + */ +unsigned long object_add_key(object*, char* key, object* obj); + +/* removes the object at the given index and, if more items exist, + * re-indexes (shifts down by 1) the rest of the objects in the array + */ +unsigned long object_remove_index(object*, unsigned long index); + +/* de-allocates the object at index 'index' and sets the field to null. + * this will *not* change the object size */ +//unsigned long object_clear_index(object*, unsigned long index); + +/* removes the object with key 'key' if it exists */ +unsigned long object_remove_key(object*, char* key); + +/* returns a pointer to the string data held by this object */ +char* object_get_string(object*); + +/* sets the string data */ +void object_set_string(object*, char* string); + +/* sets the number value for the object */ +void object_set_number(object*, long num); + +/* sets the double value for this object */ +void object_set_double(object*, double num); + +/* sets the class hint for this object */ +void object_set_class(object*, char* classname); + +/* converts an object to a json string. client is responsible for freeing the return string */ +char* object_to_json(object*); + +/* utility function. clears all of the is_* flags */ +void object_clear_type(object*); + +/* set this object's comment string */ +void object_set_comment(object*, char*); + +/* starting at index 'index', shifts all indices down by one and + * decrements the objects size by 1 + */ +void object_shift_index(object*, unsigned long index); + + + +#endif -- 2.43.2