Squashed 'third_party/jsont/' content from commit 1536152d7
Change-Id: I51a80190772b74ca0d45fd3fadc130e872b57cc0
git-subtree-dir: third_party/jsont
git-subtree-split: 1536152d7c1926448d42e4a691acd9a15940b20c
diff --git a/example2.c b/example2.c
new file mode 100644
index 0000000..5077f85
--- /dev/null
+++ b/example2.c
@@ -0,0 +1,183 @@
+//
+// This is an example of parsing and building strict documents into C structs.
+//
+// The general approach is that each object type has a struct type and a
+// builder function. The struct type has members which represents its
+// properties. The builder function is more intresting: It takes a tokenizer
+// state and a struct instance. The builder function then reads each field
+// name from the tokenizer and calls other builder functions (this is how this
+// parser does flow control), and eventually stores the values into the struct
+// instance.
+//
+#include <jsont.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+
+// A simple array type
+typedef struct my_array {
+ size_t size;
+ size_t count;
+ void** items;
+} my_array_t;
+
+// Represents a user object
+typedef struct my_user {
+ const char* id;
+ const char* name;
+} my_user_t;
+
+// Represents a response from our imaginary service
+typedef struct my_response {
+ int64_t timestamp;
+ const char* viewer_id;
+ my_array_t users;
+} my_response_t;
+
+// A helper macro for allocating a new struct instance
+#define MY_NEW(T) (T*)malloc(sizeof(T))
+
+// Some helper macros for dealing with growing arrays
+#define MY_ARRAY_ALLOC(A, _size) do {\
+ (A).items = (void*)malloc(sizeof(void*)*_size); \
+ (A).count = 0; \
+ (A).size = _size; \
+ } while(0)
+#define MY_ARRAY_RESIZE(A, _size) do {\
+ (A).items = (void*)realloc((A).items, sizeof(void*)*_size); \
+ (A).size = _size; \
+ } while(0)
+#define MY_ARRAY_APPEND(A, item) (A).items[(A).count++] = (void*)(item)
+#define MY_NEXT_EXPECT(S, TOKTYPE) do { \
+ if ((tok = jsont_next(S)) != TOKTYPE) { \
+ printf("Error: Builder expected token " #TOKTYPE " (%d)\n", __LINE__); \
+ return false; \
+ }} while (0)
+
+// Builder function for user objects
+bool my_user_build(jsont_ctx_t* S, my_user_t* obj) {
+ jsont_tok_t tok = jsont_current(S);
+ if (tok != JSONT_OBJECT_START) return false;
+
+ // for each field
+ while ((tok = jsont_next(S)) == JSONT_FIELD_NAME) {
+ const uint8_t* fieldname = 0;
+ size_t len = jsont_data_value(S, &fieldname);
+
+ if (memcmp("id", fieldname, len) == 0) {
+ MY_NEXT_EXPECT(S, JSONT_STRING);
+ obj->id = jsont_strcpy_value(S);
+
+ } else if (memcmp("name", fieldname, len) == 0) {
+ MY_NEXT_EXPECT(S, JSONT_STRING);
+ obj->name = jsont_strcpy_value(S);
+
+ } else {
+ printf("%s: Unexpected field: \"%.*s\"\n", __FUNCTION__,
+ (int)len, (const char*)fieldname);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Builder function for response objects
+bool my_response_build(jsont_ctx_t* S, my_response_t* obj) {
+ jsont_tok_t tok = jsont_current(S);
+ if (tok != JSONT_OBJECT_START) return false;
+
+ // for each field
+ while ((tok = jsont_next(S)) == JSONT_FIELD_NAME) {
+ const uint8_t* fieldname = 0;
+ size_t len = jsont_data_value(S, &fieldname);
+
+ if (memcmp("timestamp", fieldname, len) == 0) {
+ MY_NEXT_EXPECT(S, JSONT_NUMBER_INT);
+ obj->timestamp = jsont_int_value(S);
+
+ } else if (memcmp("viewer_id", fieldname, len) == 0) {
+ MY_NEXT_EXPECT(S, JSONT_STRING);
+ obj->viewer_id = jsont_strcpy_value(S);
+
+ } else if (memcmp("users", fieldname, len) == 0) {
+ MY_NEXT_EXPECT(S, JSONT_ARRAY_START);
+ MY_ARRAY_ALLOC(obj->users, 10);
+
+ // for each user object
+ while ((tok = jsont_next(S)) == JSONT_OBJECT_START) {
+ if (obj->users.count == obj->users.size)
+ MY_ARRAY_RESIZE(obj->users, obj->users.size * 2);
+ my_user_t* user = MY_NEW(my_user_t);
+ if (!my_user_build(S, user))
+ return false;
+ MY_ARRAY_APPEND(obj->users, user);
+ }
+ } else {
+ printf("%s: Unexpected field: \"%.*s\"\n", __FUNCTION__,
+ (int)len, (const char*)fieldname);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Our simple response parser entry point. Returns NULL on error.
+my_response_t* my_parse_response(jsont_ctx_t* S) {
+if (jsont_next(S) != JSONT_OBJECT_START) {
+ printf("Expected JSON input to start with an object.\n");
+ return 0;
+ }
+ my_response_t* rsp = MY_NEW(my_response_t);
+ if (!my_response_build(S, rsp)) {
+ free(rsp);
+ return 0;
+ }
+ return rsp;
+}
+
+int main(int argc, const char** argv) {
+ // Create a new reusable tokenizer
+ jsont_ctx_t* S = jsont_create(0);
+
+ // Sample "response" data
+ const char* inbuf = "{"
+ "\"viewer_id\": \"abc123\","
+ "\"timestamp\": 1234567890,"
+ "\"users\":["
+ "{\"name\": \"John Smith\", \"id\": \"12c39a\"},\n"
+ "{\"name\": \"John Doe\", \"id\": \"01dk2\"},\n"
+ "{\"name\": \"Kate Smith\", \"id\": \"apru1\"},\n"
+ "{\"name\": \"Rebecca Doe\",\"id\": \"aRm26\"}\n"
+ "]"
+ "}";
+
+ // Parse the sample "response" data
+ jsont_reset(S, (const uint8_t*)inbuf, strlen(inbuf));
+ my_response_t* rsp = my_parse_response(S);
+
+ // Epic success?
+ if (rsp) {
+ printf("Built response structure.\n");
+ printf("rsp->users.items[2]->name => \"%s\"\n",
+ ((my_user_t*)rsp->users.items[2])->name );
+
+ } else {
+ printf("Failed to build response structure.\n");
+ if (jsont_error_info(S) != 0) {
+ fprintf(stderr, "Error: %s ('%c' at offset %lu)\n",
+ jsont_error_info(S),
+ (char)jsont_current_byte(S),
+ (unsigned long)jsont_current_offset(S));
+ }
+ // Exit with error. Note: In a real application, you should call
+ // `jsont_destroy` on the reusable tokenizer when done with it. Here we
+ // just exit the program.
+ return 1;
+ }
+
+ // Destroy our reusable tokenizer and exit
+ jsont_destroy(S);
+ return 0;
+}