2020-07-08 20:18:30 +00:00
|
|
|
#include "webfuse_provider/impl/json/writer.h"
|
2020-07-09 20:12:13 +00:00
|
|
|
#include "webfuse_provider/impl/util/base64.h"
|
2020-07-08 20:18:30 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2020-07-09 20:12:13 +00:00
|
|
|
#include <stdbool.h>
|
|
|
|
#include <limits.h>
|
2020-07-08 20:18:30 +00:00
|
|
|
|
2020-07-09 20:12:13 +00:00
|
|
|
#define WFP_JSON_WRITER_MAX_LEVEL 7
|
|
|
|
#define WFP_INT_BUFFER_SIZE 64
|
2020-07-08 20:18:30 +00:00
|
|
|
|
|
|
|
enum wfp_json_writer_state
|
|
|
|
{
|
2020-07-09 20:12:13 +00:00
|
|
|
WFP_JSON_WRITER_STATE_INIT,
|
|
|
|
WFP_JSON_WRITER_STATE_ARRAY_FIRST,
|
|
|
|
WFP_JSON_WRITER_STATE_ARRAY_NEXT,
|
|
|
|
WFP_JSON_WRITER_STATE_OBJECT_FIRST,
|
|
|
|
WFP_JSON_WRITER_STATE_OBJECT_NEXT
|
2020-07-08 20:18:30 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct wfp_json_writer
|
|
|
|
{
|
2020-07-09 20:12:13 +00:00
|
|
|
enum wfp_json_writer_state state[WFP_JSON_WRITER_MAX_LEVEL + 1];
|
2020-07-08 20:18:30 +00:00
|
|
|
size_t level;
|
|
|
|
size_t capacity;
|
|
|
|
size_t pre;
|
|
|
|
size_t offset;
|
|
|
|
char * data;
|
|
|
|
char * raw_data;
|
|
|
|
};
|
|
|
|
|
2020-07-09 20:12:13 +00:00
|
|
|
static char
|
|
|
|
wfp_impl_json_writer_get_esc(
|
|
|
|
char c)
|
|
|
|
{
|
|
|
|
switch (c)
|
|
|
|
{
|
|
|
|
case '\\': return '\\';
|
|
|
|
case '\"': return '\"';
|
|
|
|
case '/' : return '/';
|
|
|
|
case '\b': return 'b';
|
|
|
|
case '\f': return 'f';
|
|
|
|
case '\n': return 'n';
|
|
|
|
case '\r': return 'r';
|
|
|
|
case '\t': return 't';
|
|
|
|
default:
|
|
|
|
// error
|
|
|
|
return '/';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
wfp_impl_json_writer_write_raw_char(
|
|
|
|
struct wfp_json_writer * writer,
|
|
|
|
char value)
|
|
|
|
{
|
|
|
|
writer->data[writer->offset++] = value;
|
|
|
|
}
|
|
|
|
|
2020-07-08 20:18:30 +00:00
|
|
|
static void
|
|
|
|
wfp_impl_json_writer_reserve(
|
|
|
|
struct wfp_json_writer * writer,
|
|
|
|
size_t needed)
|
|
|
|
{
|
|
|
|
if ((writer->capacity - writer->offset) < needed)
|
|
|
|
{
|
|
|
|
size_t new_capacity = 2 * writer->capacity;
|
|
|
|
|
|
|
|
while ((new_capacity - writer->offset) < needed)
|
|
|
|
{
|
|
|
|
new_capacity *= 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
writer->raw_data = realloc(writer->raw_data, writer->pre + new_capacity);
|
2020-07-09 20:34:18 +00:00
|
|
|
writer->data = &(writer->raw_data[writer->pre]);
|
2020-07-08 20:18:30 +00:00
|
|
|
writer->capacity = new_capacity;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-09 20:12:13 +00:00
|
|
|
static void
|
|
|
|
wfp_impl_json_writer_begin_value(
|
|
|
|
struct wfp_json_writer * writer)
|
|
|
|
{
|
|
|
|
wfp_impl_json_writer_reserve(writer, 1);
|
|
|
|
if (writer->state[writer->level] == WFP_JSON_WRITER_STATE_ARRAY_NEXT)
|
|
|
|
{
|
|
|
|
wfp_impl_json_writer_write_raw_char(writer, ',');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
wfp_impl_json_writer_end_value(
|
|
|
|
struct wfp_json_writer * writer)
|
|
|
|
{
|
|
|
|
if (WFP_JSON_WRITER_STATE_ARRAY_FIRST == writer->state[writer->level])
|
|
|
|
{
|
|
|
|
writer->state[writer->level] = WFP_JSON_WRITER_STATE_ARRAY_NEXT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
wfp_impl_json_writer_push(
|
2020-07-08 20:18:30 +00:00
|
|
|
struct wfp_json_writer * writer,
|
2020-07-09 20:12:13 +00:00
|
|
|
enum wfp_json_writer_state state)
|
2020-07-08 20:18:30 +00:00
|
|
|
{
|
2020-07-09 20:12:13 +00:00
|
|
|
if (writer->level < WFP_JSON_WRITER_MAX_LEVEL)
|
|
|
|
{
|
|
|
|
writer->level++;
|
|
|
|
writer->state[writer->level] = state;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
wfp_impl_json_writer_pop(
|
|
|
|
struct wfp_json_writer * writer)
|
|
|
|
{
|
|
|
|
if (writer->level > 0)
|
|
|
|
{
|
|
|
|
writer->level--;
|
|
|
|
}
|
2020-07-08 20:18:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
wfp_impl_json_writer_write_raw(
|
|
|
|
struct wfp_json_writer * writer,
|
|
|
|
char const * value,
|
|
|
|
size_t length)
|
|
|
|
{
|
|
|
|
memcpy(&(writer->data[writer->offset]), value, length);
|
|
|
|
writer->offset += length;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct wfp_json_writer *
|
|
|
|
wfp_impl_json_writer_create(
|
|
|
|
size_t initial_capacity,
|
|
|
|
size_t pre)
|
|
|
|
{
|
|
|
|
struct wfp_json_writer * writer = malloc(sizeof(struct wfp_json_writer));
|
|
|
|
writer->level = 0;
|
|
|
|
writer->state[writer->level] = WFP_JSON_WRITER_STATE_INIT;
|
|
|
|
writer->pre = pre;
|
|
|
|
writer->capacity = initial_capacity;
|
|
|
|
writer->offset = 0;
|
|
|
|
writer->raw_data = malloc(writer->pre + writer->capacity);
|
|
|
|
writer->data = &(writer->raw_data[pre]);
|
|
|
|
|
|
|
|
return writer;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
wfp_impl_json_writer_dispose(
|
|
|
|
struct wfp_json_writer * writer)
|
|
|
|
{
|
|
|
|
free(writer->raw_data);
|
|
|
|
free(writer);
|
|
|
|
}
|
|
|
|
|
2020-07-09 20:12:13 +00:00
|
|
|
void
|
|
|
|
wfp_impl_json_writer_reset(
|
|
|
|
struct wfp_json_writer * writer)
|
|
|
|
{
|
|
|
|
writer->level = 0;
|
|
|
|
writer->state[writer->level] = WFP_JSON_WRITER_STATE_INIT;
|
|
|
|
writer->offset = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-07-08 20:18:30 +00:00
|
|
|
char *
|
|
|
|
wfp_impl_json_writer_take_data(
|
2020-07-09 20:12:13 +00:00
|
|
|
struct wfp_json_writer * writer,
|
|
|
|
size_t * size)
|
2020-07-08 20:18:30 +00:00
|
|
|
{
|
|
|
|
wfp_impl_json_writer_reserve(writer, 1);
|
|
|
|
writer->data[writer->offset] = '\0';
|
|
|
|
|
|
|
|
char * result = writer->raw_data;
|
|
|
|
writer->raw_data = NULL;
|
|
|
|
|
2020-07-09 20:12:13 +00:00
|
|
|
if (NULL != size)
|
|
|
|
{
|
|
|
|
*size = writer->offset;
|
|
|
|
}
|
|
|
|
|
2020-07-08 20:18:30 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
wfp_impl_json_writer_write_int(
|
|
|
|
struct wfp_json_writer * writer,
|
|
|
|
int value)
|
|
|
|
{
|
2020-07-09 20:12:13 +00:00
|
|
|
wfp_impl_json_writer_begin_value(writer);
|
|
|
|
wfp_impl_json_writer_reserve(writer, WFP_INT_BUFFER_SIZE);
|
|
|
|
|
|
|
|
bool const is_signed = (0 > value);
|
|
|
|
char buffer[WFP_INT_BUFFER_SIZE];
|
|
|
|
size_t offset = WFP_INT_BUFFER_SIZE;
|
|
|
|
buffer[--offset] = '\0';
|
|
|
|
if (is_signed)
|
|
|
|
{
|
|
|
|
if (INT_MIN == value)
|
|
|
|
{
|
|
|
|
char const actual = (char) abs(value % 10);
|
|
|
|
buffer[--offset] = (char) ('0' + actual);
|
|
|
|
value /= 10;
|
|
|
|
}
|
|
|
|
value = -value;
|
|
|
|
}
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
char const actual = (char) (value % 10);
|
|
|
|
buffer[--offset] = ('0' + actual);
|
|
|
|
value /= 10;
|
|
|
|
}
|
|
|
|
while (0 != value);
|
|
|
|
|
|
|
|
if (is_signed)
|
|
|
|
{
|
|
|
|
buffer[--offset] = '-';
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t const length = (WFP_INT_BUFFER_SIZE - offset - 1);
|
|
|
|
wfp_impl_json_writer_write_raw(writer, &(buffer[offset]), length);
|
2020-07-08 20:18:30 +00:00
|
|
|
|
2020-07-09 20:12:13 +00:00
|
|
|
wfp_impl_json_writer_end_value(writer);
|
2020-07-08 20:18:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
wfp_impl_json_writer_write_string(
|
|
|
|
struct wfp_json_writer * writer,
|
|
|
|
char const * value)
|
|
|
|
{
|
2020-07-09 20:12:13 +00:00
|
|
|
wfp_impl_json_writer_begin_value(writer);
|
|
|
|
|
|
|
|
size_t length = strlen(value);
|
|
|
|
wfp_impl_json_writer_reserve(writer, length + 2);
|
|
|
|
|
|
|
|
wfp_impl_json_writer_write_raw_char(writer, '\"');
|
|
|
|
for(size_t i = 0; i < length; i++)
|
|
|
|
{
|
|
|
|
char c = value[i];
|
|
|
|
if ((' ' <= c) && (c != '\\') && (c != '\"'))
|
|
|
|
{
|
|
|
|
wfp_impl_json_writer_write_raw_char(writer, c);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char esc = wfp_impl_json_writer_get_esc(c);
|
|
|
|
|
|
|
|
wfp_impl_json_writer_reserve(writer, (length - i) + 2);
|
|
|
|
wfp_impl_json_writer_write_raw_char(writer, '\\');
|
|
|
|
wfp_impl_json_writer_write_raw_char(writer, esc);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
wfp_impl_json_writer_write_raw_char(writer, '\"');
|
|
|
|
|
|
|
|
wfp_impl_json_writer_end_value(writer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
wfp_impl_json_writer_write_string_nocheck(
|
|
|
|
struct wfp_json_writer * writer,
|
|
|
|
char const * value)
|
|
|
|
{
|
|
|
|
wfp_impl_json_writer_begin_value(writer);
|
|
|
|
|
2020-07-08 20:18:30 +00:00
|
|
|
size_t length = strlen(value);
|
|
|
|
wfp_impl_json_writer_reserve(writer, length + 2);
|
|
|
|
|
|
|
|
wfp_impl_json_writer_write_raw_char(writer, '\"');
|
|
|
|
wfp_impl_json_writer_write_raw(writer, value, length);
|
|
|
|
wfp_impl_json_writer_write_raw_char(writer, '\"');
|
|
|
|
|
2020-07-09 20:12:13 +00:00
|
|
|
wfp_impl_json_writer_end_value(writer);
|
2020-07-08 20:18:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
wfp_impl_json_writer_write_bytes(
|
|
|
|
struct wfp_json_writer * writer,
|
|
|
|
char const * data,
|
|
|
|
size_t length)
|
|
|
|
{
|
2020-07-09 20:12:13 +00:00
|
|
|
wfp_impl_json_writer_begin_value(writer);
|
|
|
|
|
|
|
|
size_t encoded_length = wfp_impl_base64_encoded_size(length);
|
|
|
|
wfp_impl_json_writer_reserve(writer, encoded_length + 2);
|
|
|
|
|
|
|
|
wfp_impl_json_writer_write_raw_char(writer, '\"');
|
|
|
|
wfp_impl_base64_encode((uint8_t const*) data, length, &(writer->data[writer->offset]), encoded_length);
|
|
|
|
writer->offset += encoded_length;
|
|
|
|
wfp_impl_json_writer_write_raw_char(writer, '\"');
|
2020-07-08 20:18:30 +00:00
|
|
|
|
2020-07-09 20:12:13 +00:00
|
|
|
wfp_impl_json_writer_end_value(writer);
|
2020-07-08 20:18:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
wfp_impl_json_writer_object_begin(
|
|
|
|
struct wfp_json_writer * writer)
|
|
|
|
{
|
2020-07-09 20:12:13 +00:00
|
|
|
wfp_impl_json_writer_begin_value(writer);
|
2020-07-08 20:18:30 +00:00
|
|
|
wfp_impl_json_writer_reserve(writer, 1);
|
2020-07-09 20:12:13 +00:00
|
|
|
|
|
|
|
wfp_impl_json_writer_push(writer, WFP_JSON_WRITER_STATE_OBJECT_FIRST);
|
2020-07-08 20:18:30 +00:00
|
|
|
wfp_impl_json_writer_write_raw_char(writer, '{');
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
wfp_impl_json_writer_object_end(
|
|
|
|
struct wfp_json_writer * writer)
|
|
|
|
{
|
|
|
|
wfp_impl_json_writer_reserve(writer, 1);
|
|
|
|
wfp_impl_json_writer_write_raw_char(writer, '}');
|
2020-07-09 20:12:13 +00:00
|
|
|
|
|
|
|
wfp_impl_json_writer_pop(writer);
|
|
|
|
wfp_impl_json_writer_end_value(writer);
|
2020-07-08 20:18:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
wfp_impl_json_writer_object_key(
|
|
|
|
struct wfp_json_writer * writer,
|
|
|
|
char const * key)
|
|
|
|
{
|
|
|
|
wfp_impl_json_writer_reserve(writer, 1);
|
2020-07-09 20:12:13 +00:00
|
|
|
|
|
|
|
size_t length = strlen(key);
|
|
|
|
wfp_impl_json_writer_reserve(writer, length + 4);
|
|
|
|
|
|
|
|
if (WFP_JSON_WRITER_STATE_OBJECT_NEXT == writer->state[writer->level])
|
|
|
|
{
|
|
|
|
wfp_impl_json_writer_write_raw_char(writer, ',');
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
writer->state[writer->level] = WFP_JSON_WRITER_STATE_OBJECT_NEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
wfp_impl_json_writer_write_raw_char(writer, '\"');
|
|
|
|
wfp_impl_json_writer_write_raw(writer, key, length);
|
|
|
|
wfp_impl_json_writer_write_raw_char(writer, '\"');
|
|
|
|
wfp_impl_json_writer_write_raw_char(writer, ':');
|
2020-07-08 20:18:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
wfp_impl_json_writer_array_begin(
|
|
|
|
struct wfp_json_writer * writer)
|
|
|
|
{
|
2020-07-09 20:12:13 +00:00
|
|
|
wfp_impl_json_writer_begin_value(writer);
|
|
|
|
wfp_impl_json_writer_push(writer, WFP_JSON_WRITER_STATE_ARRAY_FIRST);
|
|
|
|
|
2020-07-08 20:18:30 +00:00
|
|
|
wfp_impl_json_writer_reserve(writer, 1);
|
|
|
|
wfp_impl_json_writer_write_raw_char(writer, '[');
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
wfp_impl_json_writer_array_end(
|
|
|
|
struct wfp_json_writer * writer)
|
|
|
|
{
|
|
|
|
wfp_impl_json_writer_reserve(writer, 1);
|
|
|
|
wfp_impl_json_writer_write_raw_char(writer, ']');
|
2020-07-09 20:12:13 +00:00
|
|
|
|
|
|
|
wfp_impl_json_writer_pop(writer);
|
|
|
|
wfp_impl_json_writer_end_value(writer);
|
2020-07-08 20:18:30 +00:00
|
|
|
}
|