You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lnav/src/yajl/yajl_gen.c

363 lines
11 KiB
C

/*
* Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "api/yajl_gen.h"
#include "yajl_buf.h"
#include "yajl_encode.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdarg.h>
typedef enum {
yajl_gen_start,
yajl_gen_map_start,
yajl_gen_map_key,
yajl_gen_map_val,
yajl_gen_array_start,
yajl_gen_in_array,
yajl_gen_complete,
yajl_gen_error
} yajl_gen_state;
struct yajl_gen_t
{
unsigned int flags;
unsigned int depth;
const char * indentString;
yajl_gen_state state[YAJL_MAX_DEPTH];
yajl_print_t print;
void * ctx; /* yajl_buf */
/* memory allocation routines */
yajl_alloc_funcs alloc;
};
int
yajl_gen_config(yajl_gen g, yajl_gen_option opt, ...)
{
int rv = 1;
va_list ap;
va_start(ap, opt);
switch(opt) {
case yajl_gen_beautify:
case yajl_gen_validate_utf8:
case yajl_gen_escape_solidus:
if (va_arg(ap, int)) g->flags |= opt;
else g->flags &= ~opt;
break;
case yajl_gen_indent_string: {
const char *indent = va_arg(ap, const char *);
g->indentString = indent;
for (; *indent; indent++) {
if (*indent != '\n'
&& *indent != '\v'
&& *indent != '\f'
&& *indent != '\t'
&& *indent != '\r'
&& *indent != ' ')
{
g->indentString = NULL;
rv = 0;
}
}
break;
}
case yajl_gen_print_callback:
yajl_buf_free(g->ctx);
g->print = va_arg(ap, const yajl_print_t);
g->ctx = va_arg(ap, void *);
break;
default:
rv = 0;
}
va_end(ap);
return rv;
}
yajl_gen
yajl_gen_alloc(const yajl_alloc_funcs * afs)
{
yajl_gen g = NULL;
yajl_alloc_funcs afsBuffer;
/* first order of business is to set up memory allocation routines */
if (afs != NULL) {
if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL)
{
return NULL;
}
} else {
yajl_set_default_alloc_funcs(&afsBuffer);
afs = &afsBuffer;
}
g = (yajl_gen) YA_MALLOC(afs, sizeof(struct yajl_gen_t));
if (!g) return NULL;
memset((void *) g, 0, sizeof(struct yajl_gen_t));
/* copy in pointers to allocation routines */
memcpy((void *) &(g->alloc), (void *) afs, sizeof(yajl_alloc_funcs));
g->print = (yajl_print_t)&yajl_buf_append;
g->ctx = yajl_buf_alloc(&(g->alloc));
g->indentString = " ";
return g;
}
void
yajl_gen_reset(yajl_gen g, const char * sep)
{
g->depth = 0;
memset((void *) &(g->state), 0, sizeof(g->state));
if (sep != NULL) g->print(g->ctx, sep, strlen(sep));
}
void
yajl_gen_free(yajl_gen g)
{
if (g->print == (yajl_print_t)&yajl_buf_append) yajl_buf_free((yajl_buf)g->ctx);
YA_FREE(&(g->alloc), g);
}
#define INSERT_SEP \
if (g->state[g->depth] == yajl_gen_map_key || \
g->state[g->depth] == yajl_gen_in_array) { \
g->print(g->ctx, ",", 1); \
if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); \
} else if (g->state[g->depth] == yajl_gen_map_val) { \
g->print(g->ctx, ":", 1); \
if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, " ", 1); \
}
#define INSERT_WHITESPACE \
if ((g->flags & yajl_gen_beautify)) { \
if (g->state[g->depth] != yajl_gen_map_val) { \
unsigned int _i; \
for (_i=0;_i<g->depth;_i++) \
g->print(g->ctx, \
g->indentString, \
(unsigned int)strlen(g->indentString)); \
} \
}
#define ENSURE_NOT_KEY \
if (g->state[g->depth] == yajl_gen_map_key || \
g->state[g->depth] == yajl_gen_map_start) { \
return yajl_gen_keys_must_be_strings; \
} \
/* check that we're not complete, or in error state. in a valid state
* to be generating */
#define ENSURE_VALID_STATE \
if (g->state[g->depth] == yajl_gen_error) { \
return yajl_gen_in_error_state;\
} else if (g->state[g->depth] == yajl_gen_complete) { \
return yajl_gen_generation_complete; \
}
#define INCREMENT_DEPTH \
if (++(g->depth) >= YAJL_MAX_DEPTH) return yajl_max_depth_exceeded;
#define DECREMENT_DEPTH \
if (--(g->depth) >= YAJL_MAX_DEPTH) return yajl_gen_generation_complete;
#define APPENDED_ATOM \
switch (g->state[g->depth]) { \
case yajl_gen_start: \
g->state[g->depth] = yajl_gen_complete; \
break; \
case yajl_gen_map_start: \
case yajl_gen_map_key: \
g->state[g->depth] = yajl_gen_map_val; \
break; \
case yajl_gen_array_start: \
g->state[g->depth] = yajl_gen_in_array; \
break; \
case yajl_gen_map_val: \
g->state[g->depth] = yajl_gen_map_key; \
break; \
default: \
break; \
} \
#define FINAL_NEWLINE \
if ((g->flags & yajl_gen_beautify) && g->state[g->depth] == yajl_gen_complete) \
g->print(g->ctx, "\n", 1);
yajl_gen_status
yajl_gen_integer(yajl_gen g, long long int number)
{
char i[32];
ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
sprintf(i, "%lld", number);
g->print(g->ctx, i, (unsigned int)strlen(i));
APPENDED_ATOM;
FINAL_NEWLINE;
return yajl_gen_status_ok;
}
#if defined(_WIN32) || defined(WIN32)
#include <float.h>
#define isnan _isnan
#define isinf !_finite
#endif
yajl_gen_status
yajl_gen_double(yajl_gen g, double number)
{
char i[32];
ENSURE_VALID_STATE; ENSURE_NOT_KEY;
if (isnan(number) || isinf(number)) return yajl_gen_invalid_number;
INSERT_SEP; INSERT_WHITESPACE;
sprintf(i, "%.20g", number);
if (strspn(i, "0123456789-") == strlen(i)) {
strcat(i, ".0");
}
g->print(g->ctx, i, (unsigned int)strlen(i));
APPENDED_ATOM;
FINAL_NEWLINE;
return yajl_gen_status_ok;
}
yajl_gen_status
yajl_gen_number(yajl_gen g, const char * s, size_t l)
{
ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
g->print(g->ctx, s, l);
APPENDED_ATOM;
FINAL_NEWLINE;
return yajl_gen_status_ok;
}
yajl_gen_status
yajl_gen_string(yajl_gen g, const unsigned char * str,
size_t len)
{
// if validation is enabled, check that the string is valid utf8
// XXX: This checking could be done a little faster, in the same pass as
// the string encoding
if (g->flags & yajl_gen_validate_utf8) {
if (!yajl_string_validate_utf8(str, len)) {
return yajl_gen_invalid_string;
}
}
ENSURE_VALID_STATE; INSERT_SEP; INSERT_WHITESPACE;
g->print(g->ctx, "\"", 1);
yajl_string_encode(g->print, g->ctx, str, len, g->flags & yajl_gen_escape_solidus);
g->print(g->ctx, "\"", 1);
APPENDED_ATOM;
FINAL_NEWLINE;
return yajl_gen_status_ok;
}
yajl_gen_status
yajl_gen_null(yajl_gen g)
{
ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
g->print(g->ctx, "null", strlen("null"));
APPENDED_ATOM;
FINAL_NEWLINE;
return yajl_gen_status_ok;
}
yajl_gen_status
yajl_gen_bool(yajl_gen g, int boolean)
{
const char * val = boolean ? "true" : "false";
ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
g->print(g->ctx, val, (unsigned int)strlen(val));
APPENDED_ATOM;
FINAL_NEWLINE;
return yajl_gen_status_ok;
}
yajl_gen_status
yajl_gen_map_open(yajl_gen g)
{
ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
INCREMENT_DEPTH;
g->state[g->depth] = yajl_gen_map_start;
g->print(g->ctx, "{", 1);
if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1);
FINAL_NEWLINE;
return yajl_gen_status_ok;
}
yajl_gen_status
yajl_gen_map_close(yajl_gen g)
{
ENSURE_VALID_STATE;
DECREMENT_DEPTH;
if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1);
APPENDED_ATOM;
INSERT_WHITESPACE;
g->print(g->ctx, "}", 1);
FINAL_NEWLINE;
return yajl_gen_status_ok;
}
yajl_gen_status
yajl_gen_array_open(yajl_gen g)
{
ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
INCREMENT_DEPTH;
g->state[g->depth] = yajl_gen_array_start;
g->print(g->ctx, "[", 1);
if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1);
FINAL_NEWLINE;
return yajl_gen_status_ok;
}
yajl_gen_status
yajl_gen_array_close(yajl_gen g)
{
ENSURE_VALID_STATE;
DECREMENT_DEPTH;
if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1);
APPENDED_ATOM;
INSERT_WHITESPACE;
g->print(g->ctx, "]", 1);
FINAL_NEWLINE;
return yajl_gen_status_ok;
}
yajl_gen_status
yajl_gen_get_buf(yajl_gen g, const unsigned char ** buf,
size_t * len)
{
if (g->print != (yajl_print_t)&yajl_buf_append) return yajl_gen_no_buf;
*buf = yajl_buf_data((yajl_buf)g->ctx);
*len = yajl_buf_len((yajl_buf)g->ctx);
return yajl_gen_status_ok;
}
void
yajl_gen_clear(yajl_gen g)
{
if (g->print == (yajl_print_t)&yajl_buf_append) yajl_buf_clear((yajl_buf)g->ctx);
}