1
0
Fork 0
icecast-server/src/json.c

363 lines
8.0 KiB
C

/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2018-2020, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
/**
* This file contains functions for rendering JSON.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "json.h"
#include "logging.h"
#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)
have--;
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;
renderer->bufferlen = want;
}
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 **rendererptr)
{
json_renderer_t *renderer;
char *ret;
if (!rendererptr)
return NULL;
renderer = *rendererptr;
*rendererptr = NULL;
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 (((unsigned char)*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);
}