diff --git a/.gitignore b/.gitignore index 496ee2c..b38f511 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.DS_Store \ No newline at end of file +.DS_Store +test diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0893bca --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +CFLAGS = -O0 -ggdb -Wall -Wextra -std=c89 + +all: test + +.PHONY: test +test: tests.c parson.c + $(CC) $(CFLAGS) -o $@ tests.c parson.c + ./$@ + +clean: + rm -f test *.o \ No newline at end of file diff --git a/parson.c b/parson.c index 2596023..f7c009f 100644 --- a/parson.c +++ b/parson.c @@ -1,17 +1,17 @@ /* Parson ( http://kgabis.github.com/parson/ ) Copyright (c) 2012 Krzysztof Gabis - + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -27,6 +27,7 @@ #include #include #include +#include #define ERROR 0 #define SUCCESS 1 @@ -647,3 +648,141 @@ void json_value_free(JSON_Value *value) { } parson_free(value); } + +/* -------------------------------------------------------------------------- */ + +struct _serialize_str { + char *str; + size_t len; +}; + +/* + * Callback which appends data to the string + */ +void _serialize_str_cb( const char *str, size_t len, void *data ) { + struct _serialize_str *self = (struct _serialize_str*)data; + if( self->str == NULL ) { + self->str = parson_malloc(len + 1); + assert( self->str != NULL ); + strncpy( self->str, str, len ); + self->str[ len ] = 0; + self->len = len; + } + else { + self->str = parson_realloc( self->str, self->len + len + 1 ); + assert( self->str != NULL ); + strncpy( self->str + (self->len), str, len); + self->str[ self->len + len ] = 0; + self->len += len; + } +} + +static void +json_serialize_string( const char *str, json_print_cb cb, void *arg ) { + cb("\"", 1, arg); + /* TODO: handle unicode encoding of string? + * XXX: need to be aware of unicode escape sequences so that we don't mess up + */ + cb(str, strlen(str), arg); + cb("\"", 1, arg); +} + +static void +json_serialize_number( const JSON_Value *value, json_print_cb cb, void *arg ) { + char buf[64]; + size_t len = sprintf(buf, "%.16g", json_value_get_number(value)); + cb(buf, len, arg); +} + +static void +json_serialize_object( const JSON_Value *value, json_print_cb cb, void *arg ) { + int first = 1; + size_t idx; + JSON_Object* obj = json_value_get_object(value); + const char *name = NULL; + JSON_Value *obj_value = json_object_get_value(obj, name); + + cb("{", 1, arg); + for( idx = 0; idx < json_object_get_count(obj); idx++ ) { + name = json_object_get_name(obj, idx); + if( first ) { + first = 0; + } + else { + cb(",", 1, arg); + } + json_serialize_string( name, cb, arg ); + cb(":", 1, arg); + json_serialize_cb(obj_value, cb, arg); + } + cb("}", 1, arg); +} + +static void +json_serialize_array( const JSON_Value *value, json_print_cb cb, void *arg ) { + int first = 1; + size_t idx; + JSON_Array *array = json_value_get_array(value); + cb("[", 1, arg); + for( idx = 0; idx < json_array_get_count(array); idx++ ) { + if( first ) { + first = 0; + } + else { + cb(",", 1, arg); + } + json_serialize_cb( json_array_get_value(array, idx), cb, arg ); + } + cb("]", 1, arg); +} + +void +json_serialize_cb( const JSON_Value *value, json_print_cb cb, void *arg ) { + assert( json_value_get_type(value) != JSONError ); + + switch( json_value_get_type(value) ) { + case JSONNull: + cb( "null", 4, arg ); + break; + + case JSONString: + json_serialize_string( json_value_get_string(value), cb, arg ); + break; + + case JSONNumber: + json_serialize_number(value, cb, arg); + break; + + case JSONObject: + json_serialize_object(value, cb, arg); + break; + + case JSONArray: + json_serialize_array(value, cb, arg); + break; + + case JSONBoolean: + if( json_value_get_boolean(value) ) { + cb("true", 4, arg); + } + else { + cb("false", 5, arg); + } + break; + + default: + printf("Got unknown type!\n"); + /* XXX: this shouldn't ever happen! */ + break; + } +} + +char * +json_serialize( const JSON_Value *value ) { + struct _serialize_str str; + str.str = NULL; + str.len = 0; + + json_serialize_cb( value, &_serialize_str_cb, &str ); + return str.str; +} diff --git a/parson.h b/parson.h index 00728b1..5fd3f23 100644 --- a/parson.h +++ b/parson.h @@ -1,17 +1,17 @@ /* Parson ( http://kgabis.github.com/parson/ ) Copyright (c) 2012 Krzysztof Gabis - + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -27,10 +27,10 @@ #ifdef __cplusplus extern "C" { -#endif - -#include /* size_t */ - +#endif + +#include /* size_t */ + /* Types and enums */ typedef struct json_object_t JSON_Object; typedef struct json_array_t JSON_Array; @@ -74,7 +74,7 @@ int json_object_dotget_boolean(const JSON_Object *object, const char * /* Functions to get available names */ size_t json_object_get_count(const JSON_Object *object); const char * json_object_get_name (const JSON_Object *object, size_t index); - + /* JSON Array */ JSON_Value * json_array_get_value (const JSON_Array *array, size_t index); const char * json_array_get_string (const JSON_Array *array, size_t index); @@ -92,7 +92,12 @@ const char * json_value_get_string (const JSON_Value *value); double json_value_get_number (const JSON_Value *value); int json_value_get_boolean(const JSON_Value *value); void json_value_free (JSON_Value *value); - + +/* Functions for serializing JSON */ +typedef void (*json_print_cb)(const char *str, size_t len, void *data); +void json_serialize_cb( const JSON_Value *value, json_print_cb cb, void *arg ); +char *json_serialize( const JSON_Value *value ); + #ifdef __cplusplus } #endif diff --git a/tests.c b/tests.c index a65b709..7988243 100644 --- a/tests.c +++ b/tests.c @@ -1,17 +1,17 @@ /* Parson ( http://kgabis.github.com/parson/ ) Copyright (c) 2012 Krzysztof Gabis - + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -31,10 +31,12 @@ if(A){puts(" OK");tests_passed++;} \ else{puts(" FAIL");tests_failed++;} #define STREQ(A, B) (A && B ? strcmp(A, B) == 0 : 0) +#define RESERIALIZE(A) (strcmp(json_serialize(json_parse_string(A)), A) == 0) void test_suite_1(void); void test_suite_2(void); void test_suite_3(void); +void test_suite_4(void); void print_commits_info(const char *username, const char *repo); @@ -47,6 +49,7 @@ int main(int argc, const char * argv[]) { test_suite_1(); test_suite_2(); test_suite_3(); + test_suite_4(); printf("Tests failed: %d\n", tests_failed); printf("Tests passed: %d\n", tests_passed); return 0; @@ -68,7 +71,7 @@ void test_suite_2(void) { JSON_Value *root_value; JSON_Object *object; JSON_Array *array; - int i; + size_t i; const char *filename = "tests/test_2.txt"; printf("Testing %s:\n", filename); root_value = json_parse_file(filename); @@ -83,7 +86,7 @@ void test_suite_2(void) { TEST(json_object_get_boolean(object, "boolean true") == 1); TEST(json_object_get_boolean(object, "boolean false") == 0); TEST(json_value_get_type(json_object_get_value(object, "null")) == JSONNull); - + array = json_object_get_array(object, "string array"); if (array != NULL && json_array_get_count(array) > 1) { TEST(STREQ(json_array_get_string(array, 0), "lorem")); @@ -91,7 +94,7 @@ void test_suite_2(void) { } else { tests_failed++; } - + array = json_object_get_array(object, "x^2 array"); if (array != NULL) { for (i = 0; i < json_array_get_count(array); i++) { @@ -100,19 +103,19 @@ void test_suite_2(void) { } else { tests_failed++; } - + TEST(json_object_get_array(object, "non existent array") == NULL); TEST(STREQ(json_object_dotget_string(object, "object.nested string"), "str")); TEST(json_object_dotget_boolean(object, "object.nested true") == 1); TEST(json_object_dotget_boolean(object, "object.nested false") == 0); TEST(json_object_dotget_value(object, "object.nested null") != NULL); TEST(json_object_dotget_number(object, "object.nested number") == 123); - + TEST(json_object_dotget_value(object, "should.be.null") == NULL); TEST(json_object_dotget_value(object, "should.be.null.") == NULL); TEST(json_object_dotget_value(object, ".") == NULL); TEST(json_object_dotget_value(object, "") == NULL); - + array = json_object_dotget_array(object, "object.nested array"); if (array != NULL && json_array_get_count(array) > 1) { TEST(STREQ(json_array_get_string(array, 0), "lorem")); @@ -120,7 +123,7 @@ void test_suite_2(void) { } else { tests_failed++; } - TEST(json_object_dotget_boolean(object, "nested true")); + TEST(json_object_dotget_boolean(object, "nested true")); json_value_free(root_value); } @@ -169,30 +172,36 @@ void test_suite_3(void) { TEST(json_parse_string("[-07.0]") == NULL); } +/* Test serialization */ +void test_suite_4( void ) { + TEST(RESERIALIZE("[1]")); + TEST(RESERIALIZE("[\"hello\"]")); +} + void print_commits_info(const char *username, const char *repo) { JSON_Value *root_value; JSON_Array *commits; JSON_Object *commit; - int i; - + size_t i; + char curl_command[512]; char cleanup_command[256]; char output_filename[] = "commits.json"; - + /* it ain't pretty, but it's not a libcurl tutorial */ - sprintf(curl_command, + sprintf(curl_command, "curl -s \"https://api.github.com/repos/%s/%s/commits\" > %s", username, repo, output_filename); sprintf(cleanup_command, "rm -f %s", output_filename); system(curl_command); - + /* parsing json and validating output */ root_value = json_parse_file(output_filename); if (json_value_get_type(root_value) != JSONArray) { system(cleanup_command); return; } - + /* getting array from root value and printing commit info */ commits = json_value_get_array(root_value); printf("%-10.10s %-10.10s %s\n", "Date", "SHA", "Author"); @@ -203,7 +212,7 @@ void print_commits_info(const char *username, const char *repo) { json_object_get_string(commit, "sha"), json_object_dotget_string(commit, "commit.author.name")); } - + /* cleanup code */ json_value_free(root_value); system(cleanup_command);