mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2024-09-29 04:25:55 -04:00
Feature: Implement JSON renderer
This commit is contained in:
parent
5a72f70dcb
commit
2400df01ca
328
src/json.c
328
src/json.c
@ -14,10 +14,338 @@
|
|||||||
#include <config.h>
|
#include <config.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "json.h"
|
#include "json.h"
|
||||||
|
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#define CATMODULE "json"
|
#define CATMODULE "json"
|
||||||
|
|
||||||
|
#define MAX_RECURSION 64
|
||||||
|
|
||||||
|
struct json_renderer_tag {
|
||||||
|
unsigned int flags;
|
||||||
|
|
||||||
|
int valid;
|
||||||
|
|
||||||
|
char *buffer;
|
||||||
|
size_t bufferlen;
|
||||||
|
size_t bufferfill;
|
||||||
|
|
||||||
|
char levelinfo[MAX_RECURSION];
|
||||||
|
size_t level;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int allocate_buffer(json_renderer_t *renderer, size_t needed)
|
||||||
|
{
|
||||||
|
size_t required = needed + renderer->level;
|
||||||
|
size_t have = renderer->bufferlen - renderer->bufferfill;
|
||||||
|
|
||||||
|
if (!renderer->valid)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (have < required) {
|
||||||
|
size_t want;
|
||||||
|
char *n;
|
||||||
|
|
||||||
|
if (required < 128)
|
||||||
|
required = 128;
|
||||||
|
|
||||||
|
want = renderer->bufferfill + required;
|
||||||
|
if (want < 512)
|
||||||
|
want = 512;
|
||||||
|
|
||||||
|
n = realloc(renderer->buffer, want);
|
||||||
|
|
||||||
|
if (!n)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
renderer->buffer = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void json_renderer_destroy(json_renderer_t *renderer)
|
||||||
|
{
|
||||||
|
if (!renderer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
renderer->valid = 0;
|
||||||
|
|
||||||
|
free(renderer->buffer);
|
||||||
|
free(renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
json_renderer_t * json_renderer_create(unsigned int flags)
|
||||||
|
{
|
||||||
|
json_renderer_t *renderer = calloc(1, sizeof(json_renderer_t));
|
||||||
|
|
||||||
|
if (!renderer)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
renderer->flags = flags;
|
||||||
|
renderer->valid = 1;
|
||||||
|
|
||||||
|
renderer->levelinfo[0] = '0';
|
||||||
|
renderer->level = 1;
|
||||||
|
|
||||||
|
if (allocate_buffer(renderer, 0) != 0) {
|
||||||
|
json_renderer_destroy(renderer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
char * json_renderer_finish(json_renderer_t *renderer)
|
||||||
|
{
|
||||||
|
char *ret;
|
||||||
|
|
||||||
|
if (!renderer)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!renderer->valid) {
|
||||||
|
json_renderer_destroy(renderer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; renderer->level; renderer->level--) {
|
||||||
|
switch (renderer->levelinfo[renderer->level-1]) {
|
||||||
|
case '0':
|
||||||
|
renderer->buffer[renderer->bufferfill++] = 0;
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
case 'O':
|
||||||
|
renderer->buffer[renderer->bufferfill++] = '}';
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
case 'A':
|
||||||
|
renderer->buffer[renderer->bufferfill++] = ']';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
json_renderer_destroy(renderer);
|
||||||
|
return NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = renderer->buffer;
|
||||||
|
renderer->buffer = NULL;
|
||||||
|
json_renderer_destroy(renderer);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write_raw(json_renderer_t *renderer, const char *raw, int begin)
|
||||||
|
{
|
||||||
|
size_t rawlen = strlen(raw);
|
||||||
|
size_t want = rawlen;
|
||||||
|
char level;
|
||||||
|
char seperator = 0;
|
||||||
|
|
||||||
|
if (!renderer->valid)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
level = renderer->levelinfo[renderer->level-1];
|
||||||
|
|
||||||
|
if (begin) {
|
||||||
|
if (level == 'O' || level == 'A') {
|
||||||
|
seperator = ',';
|
||||||
|
} else if (level == 'o') {
|
||||||
|
renderer->levelinfo[renderer->level-1] = 'O';
|
||||||
|
} else if (level == 'a') {
|
||||||
|
renderer->levelinfo[renderer->level-1] = 'A';
|
||||||
|
} else if (level == 'P') {
|
||||||
|
seperator = ':';
|
||||||
|
renderer->level--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seperator)
|
||||||
|
want++;
|
||||||
|
|
||||||
|
if (allocate_buffer(renderer, want) != 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (seperator)
|
||||||
|
renderer->buffer[renderer->bufferfill++] = seperator;
|
||||||
|
|
||||||
|
memcpy(&(renderer->buffer[renderer->bufferfill]), raw, rawlen);
|
||||||
|
renderer->bufferfill += rawlen;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int want_write_value(json_renderer_t *renderer)
|
||||||
|
{
|
||||||
|
char level;
|
||||||
|
|
||||||
|
|
||||||
|
if (!renderer || !renderer->valid)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
level = renderer->levelinfo[renderer->level-1];
|
||||||
|
if (level == 'o' || level == 'O') {
|
||||||
|
renderer->valid = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void json_renderer_begin(json_renderer_t *renderer, json_element_type_t type)
|
||||||
|
{
|
||||||
|
const char *towrite;
|
||||||
|
char next_level;
|
||||||
|
|
||||||
|
if (!renderer || !renderer->valid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (renderer->level == MAX_RECURSION) {
|
||||||
|
renderer->valid = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case JSON_ELEMENT_TYPE_OBJECT:
|
||||||
|
next_level = 'o';
|
||||||
|
towrite = "{";
|
||||||
|
break;
|
||||||
|
case JSON_ELEMENT_TYPE_ARRAY:
|
||||||
|
next_level = 'a';
|
||||||
|
towrite = "[";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
renderer->valid = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_raw(renderer, towrite, 1);
|
||||||
|
renderer->levelinfo[renderer->level++] = next_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void json_renderer_end(json_renderer_t *renderer)
|
||||||
|
{
|
||||||
|
if (!renderer || !renderer->valid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (renderer->levelinfo[renderer->level-1]) {
|
||||||
|
case 'o':
|
||||||
|
case 'O':
|
||||||
|
write_raw(renderer, "}", 0);
|
||||||
|
renderer->level--;
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
case 'A':
|
||||||
|
write_raw(renderer, "]", 0);
|
||||||
|
renderer->level--;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
renderer->valid = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void json_renderer_write_null(json_renderer_t *renderer)
|
||||||
|
{
|
||||||
|
if (want_write_value(renderer) != 0)
|
||||||
|
return;
|
||||||
|
write_raw(renderer, "null", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void json_renderer_write_boolean(json_renderer_t *renderer, int val)
|
||||||
|
{
|
||||||
|
if (want_write_value(renderer) != 0)
|
||||||
|
return;
|
||||||
|
write_raw(renderer, val ? "true" : "false", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_string(json_renderer_t *renderer, const char *string, unsigned int flags)
|
||||||
|
{
|
||||||
|
// Allocate for the quotes plus 110% of the string length to account for escapes.
|
||||||
|
if (allocate_buffer(renderer, 2 + ((strlen(string) * 110) / 100)) != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
write_raw(renderer, "\"", 1);
|
||||||
|
|
||||||
|
for (; *string && renderer->valid; string++) {
|
||||||
|
if (renderer->bufferfill == renderer->bufferlen) {
|
||||||
|
// Reallocate buffer, same rules as above, expect that we only need one additional quote.
|
||||||
|
if (allocate_buffer(renderer, 1 + ((strlen(string) * 110) / 100)) != 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*string < 0x20) {
|
||||||
|
char buf[7];
|
||||||
|
snprintf(buf, sizeof(buf), "\\u%.4x", (unsigned int)*string);
|
||||||
|
write_raw(renderer, buf, 0);
|
||||||
|
} else if (*string == '\\' || *string == '"') {
|
||||||
|
if (allocate_buffer(renderer, 2) != 0)
|
||||||
|
return;
|
||||||
|
renderer->buffer[renderer->bufferfill++] = '\\';
|
||||||
|
renderer->buffer[renderer->bufferfill++] = *string;
|
||||||
|
} else {
|
||||||
|
renderer->buffer[renderer->bufferfill++] = *string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write_raw(renderer, "\"", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void json_renderer_write_key(json_renderer_t *renderer, const char *key, unsigned int flags)
|
||||||
|
{
|
||||||
|
char level;
|
||||||
|
|
||||||
|
if (!renderer || !renderer->valid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
level = renderer->levelinfo[renderer->level-1];
|
||||||
|
if (level != 'o' && level != 'O') {
|
||||||
|
renderer->valid = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (renderer->level == MAX_RECURSION) {
|
||||||
|
renderer->valid = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_string(renderer, key, flags);
|
||||||
|
|
||||||
|
renderer->levelinfo[renderer->level++] = 'P';
|
||||||
|
}
|
||||||
|
|
||||||
|
void json_renderer_write_string(json_renderer_t *renderer, const char *string, unsigned int flags)
|
||||||
|
{
|
||||||
|
if (want_write_value(renderer) != 0)
|
||||||
|
return;
|
||||||
|
write_string(renderer, string, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void json_renderer_write_int(json_renderer_t *renderer, intmax_t val)
|
||||||
|
{
|
||||||
|
char buf[80];
|
||||||
|
|
||||||
|
if (want_write_value(renderer) != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "%" PRIdMAX, val);
|
||||||
|
|
||||||
|
write_raw(renderer, buf, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void json_renderer_write_uint(json_renderer_t *renderer, uintmax_t val)
|
||||||
|
{
|
||||||
|
char buf[80];
|
||||||
|
|
||||||
|
if (want_write_value(renderer) != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "%" PRIuMAX, val);
|
||||||
|
|
||||||
|
write_raw(renderer, buf, 1);
|
||||||
|
}
|
||||||
|
@ -23,13 +23,14 @@ typedef enum {
|
|||||||
typedef struct json_renderer_tag json_renderer_t;
|
typedef struct json_renderer_tag json_renderer_t;
|
||||||
|
|
||||||
json_renderer_t * json_renderer_create(unsigned int flags);
|
json_renderer_t * json_renderer_create(unsigned int flags);
|
||||||
void json_renderer_destroy(json_renderer_t *renderer);
|
|
||||||
|
|
||||||
char * json_renderer_finish(json_renderer_t *renderer);
|
char * json_renderer_finish(json_renderer_t *renderer);
|
||||||
|
|
||||||
void json_renderer_begin(json_renderer_t *renderer, json_element_type_t type);
|
void json_renderer_begin(json_renderer_t *renderer, json_element_type_t type);
|
||||||
void json_renderer_end(json_renderer_t *renderer);
|
void json_renderer_end(json_renderer_t *renderer);
|
||||||
|
|
||||||
|
void json_renderer_write_key(json_renderer_t *renderer, const char *key, unsigned int flags);
|
||||||
|
|
||||||
void json_renderer_write_null(json_renderer_t *renderer);
|
void json_renderer_write_null(json_renderer_t *renderer);
|
||||||
void json_renderer_write_boolean(json_renderer_t *renderer, int val);
|
void json_renderer_write_boolean(json_renderer_t *renderer, int val);
|
||||||
void json_renderer_write_string(json_renderer_t *renderer, const char *string, unsigned int flags);
|
void json_renderer_write_string(json_renderer_t *renderer, const char *string, unsigned int flags);
|
||||||
|
Loading…
Reference in New Issue
Block a user