Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add stream-to-tree functionality #10

Open
wants to merge 4 commits into
base: bsdmake
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/yajl/yajl_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,18 @@ extern "C" {
# endif
#endif

/*+ error codes returned from this interface +*/
typedef enum {
/*+ no error was encountered +*/
yajl_status_ok,
/*+ a client callback returned zero, stopping the parse +*/
yajl_status_client_canceled,
/*+ An error occurred during the parse. Call yajl_get_error for
* more information about the encountered error +*/
yajl_status_error
} yajl_status;


/*+
* A pointer to a malloc() function.
+*/
Expand Down
11 changes: 1 addition & 10 deletions src/yajl/yajl_parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,7 @@
#ifdef __cplusplus
extern "C" {
#endif
/*+ error codes returned from this interface +*/
typedef enum {
/*+ no error was encountered +*/
yajl_status_ok,
/*+ a client callback returned zero, stopping the parse +*/
yajl_status_client_canceled,
/*+ An error occurred during the parse. Call yajl_get_error for
* more information about the encountered error +*/
yajl_status_error
} yajl_status;


/*+ attain a human readable, english, string for an error +*/
YAJL_API const char * yajl_status_to_string(yajl_status code);
Expand Down
47 changes: 47 additions & 0 deletions src/yajl/yajl_tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,31 @@ struct yajl_val_s
} u;
};

struct stack_elem_s;
typedef struct stack_elem_s stack_elem_t;
struct stack_elem_s
{
char * key;
yajl_val value;
stack_elem_t *next;
};

struct context_s
{
stack_elem_t *stack;
yajl_val root;
char *errbuf;
size_t errbuf_size;
};
typedef struct context_s context_t;


typedef struct yajl_stream_context_t {
context_t ctx;
struct yajl_handle_t * handle;
yajl_status status;
} yajl_stream_context_t;

YAJL_API yajl_val yajl_tree_parse (const char *input,
char *error_buffer, size_t error_buffer_size);
YAJL_API void yajl_tree_free (yajl_val v);
Expand Down Expand Up @@ -144,6 +169,28 @@ YAJL_API yajl_val yajl_tree_get(yajl_val parent, const char ** path, yajl_type t
/*+ Get a pointer to a yajl_val_array or NULL if the value is not an object. +*/
#define YAJL_GET_ARRAY(v) (YAJL_IS_ARRAY(v) ? &(v)->u.array : NULL)


yajl_stream_context_t *yajl_tree_stream_parse_start (
char *error_buffer, /*+ Pointer to a buffer in which
* an error message will be stored
* if yajl_tree_parse() fails, or
* NULL. The buffer will be
* initialized before parsing, so
* its content will be destroyed
* even if yajl_tree_parse()
* succeeds. +*/
size_t error_buffer_size); /*+ Size of the memory area
* pointed to by
* error_buffer_size. If
* error_buffer_size is
* NULL, this argument is
* ignored. +*/
yajl_status yajl_tree_stream_parse_feed(yajl_stream_context_t *stream_ctx,
const unsigned char* input,
size_t input_len);

yajl_val yajl_tree_stream_parse_finish(yajl_stream_context_t *stream_ctx);

#ifdef __cplusplus
}
#endif
Expand Down
1 change: 1 addition & 0 deletions src/yajl_gen.c
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ yajl_gen_free(yajl_gen g)
case yajl_gen_map_val: \
g->state[g->depth] = yajl_gen_map_key; \
break; \
case yajl_gen_complete: \
default: \
break; \
}
Expand Down
127 changes: 109 additions & 18 deletions src/yajl_tree.c
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -49,24 +49,6 @@

yajl_alloc_funcs *yajl_tree_parse_afs = NULL;

struct stack_elem_s;
typedef struct stack_elem_s stack_elem_t;
struct stack_elem_s
{
char * key;
yajl_val value;
stack_elem_t *next;
};

struct context_s
{
stack_elem_t *stack;
yajl_val root;
char *errbuf;
size_t errbuf_size;
};
typedef struct context_s context_t;

#define RETURN_ERROR(ctx,retval,...) { \
if ((ctx)->errbuf != NULL) \
snprintf ((ctx)->errbuf, (ctx)->errbuf_size, __VA_ARGS__); \
Expand Down Expand Up @@ -537,6 +519,115 @@ yajl_val yajl_tree_parse (const char *input, /*+ Pointer to a null-terminated
return (ctx.root);
}


/*+
* Begin to parse a string, piece by piece.
*
* Parses a null-terminated string containing JSON data.
*
* Returns a pointer to a yajl_stream_context_t object that shall be
* passed to the function consuming the json data.
*
* The memory pointed to must be freed using yajl_tree_free(). In case of an
* error, a null terminated message describing the error in more detail is
* stored in error_buffer if it is not NULL.
+*/
yajl_stream_context_t *yajl_tree_stream_parse_start (
char *error_buffer, /*+ Pointer to a buffer in which
* an error message will be stored
* if yajl_tree_parse() fails, or
* NULL. The buffer will be
* initialized before parsing, so
* its content will be destroyed
* even if yajl_tree_parse()
* succeeds. +*/
size_t error_buffer_size) /*+ Size of the memory area
* pointed to by
* error_buffer_size. If
* error_buffer_size is
* NULL, this argument is
* ignored. +*/
{
/* pointers to parsing callbacks */
static const yajl_callbacks callbacks =
{
/* null = */ handle_null,
/* boolean = */ handle_boolean,
/* integer = */ NULL,
/* double = */ NULL,
/* number = */ handle_number,
/* string = */ handle_string,
/* start map = */ handle_start_map,
/* map key = */ handle_string,
/* end map = */ handle_end_map,
/* start array = */ handle_start_array,
/* end array = */ handle_end_array
};


yajl_stream_context_t *stream_ctx = malloc(sizeof(*stream_ctx));

stream_ctx->ctx.stack = 0;
stream_ctx->ctx.root = 0;
stream_ctx->ctx.errbuf = error_buffer;
stream_ctx->ctx.errbuf_size = error_buffer_size;

if (error_buffer != NULL)
memset (error_buffer, 0, error_buffer_size);

stream_ctx->handle = yajl_alloc(&callbacks, yajl_tree_parse_afs, &stream_ctx);
yajl_config(stream_ctx->handle, yajl_allow_trailing_garbage, 1);

return stream_ctx;
}

/*+
* Input one or more bytes of json data
+*/
yajl_status yajl_tree_stream_parse_feed(yajl_stream_context_t *stream_ctx,
const unsigned char* input,
size_t input_len) {

stream_ctx->status = yajl_parse(stream_ctx->handle,
input,
input_len);
return stream_ctx->status;
}

/*+
* Finish a stream of json data.
*
* Returns the tree corresponding to the json input.
+*/
yajl_val yajl_tree_stream_parse_finish(yajl_stream_context_t *stream_ctx) {

char * internal_err_str;

if (stream_ctx->status != yajl_status_ok) {
if (stream_ctx->ctx.errbuf != NULL && stream_ctx->ctx.errbuf_size > 0) {
internal_err_str = (char *) yajl_get_error(stream_ctx->handle, 1,
"unknown", strlen("unknown"));

snprintf(stream_ctx->ctx.errbuf, stream_ctx->ctx.errbuf_size, "%s", internal_err_str);
YA_FREE(&(stream_ctx->handle->alloc), internal_err_str);
}
while (stream_ctx->ctx.stack) {
yajl_tree_free(context_pop(&stream_ctx->ctx));
}
yajl_free (stream_ctx->handle);
return NULL;
}
yajl_complete_parse (stream_ctx->handle);

yajl_free (stream_ctx->handle);
yajl_val root = stream_ctx->ctx.root;

free(stream_ctx);

return root;
}


/*+
* Access a nested value inside a tree.
*
Expand Down