adding early custom json code
authorerickson <erickson@9efc2488-bf62-4759-914b-345cdb29e865>
Mon, 6 Jun 2005 17:44:29 +0000 (17:44 +0000)
committererickson <erickson@9efc2488-bf62-4759-914b-345cdb29e865>
Mon, 6 Jun 2005 17:44:29 +0000 (17:44 +0000)
git-svn-id: svn://svn.open-ils.org/OpenSRF/trunk@324 9efc2488-bf62-4759-914b-345cdb29e865

src/objson/Makefile [new file with mode: 0644]
src/objson/json_parser.c [new file with mode: 0644]
src/objson/json_parser.h [new file with mode: 0644]
src/objson/object.c [new file with mode: 0644]
src/objson/object.h [new file with mode: 0644]

diff --git a/src/objson/Makefile b/src/objson/Makefile
new file mode 100644 (file)
index 0000000..d556258
--- /dev/null
@@ -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/src/objson/json_parser.c b/src/objson/json_parser.c
new file mode 100644 (file)
index 0000000..dd4931a
--- /dev/null
@@ -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/src/objson/json_parser.h b/src/objson/json_parser.h
new file mode 100644 (file)
index 0000000..29d795d
--- /dev/null
@@ -0,0 +1,59 @@
+/* ---------------------------------------------------------------------------------------
+       JSON parser.
+ * --------------------------------------------------------------------------------------- */
+
+
+#include <stdio.h>
+#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/src/objson/object.c b/src/objson/object.c
new file mode 100644 (file)
index 0000000..c8d5222
--- /dev/null
@@ -0,0 +1,527 @@
+#include "object.h"
+#include "json_parser.h"
+#include <fcntl.h>
+
+
+
+/* ---------------------------------------------------------------------- */
+/* 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/src/objson/object.h b/src/objson/object.h
new file mode 100644 (file)
index 0000000..f4ad5f2
--- /dev/null
@@ -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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <assert.h>
+#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