mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2024-09-22 04:15:54 -04:00
363 lines
8.0 KiB
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);
|
|
}
|