709 lines
15 KiB
C
709 lines
15 KiB
C
|
|
#include "iron_json.h"
|
|
#include "iron_armpack.h"
|
|
#include "iron_string.h"
|
|
#include <jsmn.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
|
|
#ifdef IRON_WASM
|
|
#define PTR_SIZE 4
|
|
#else
|
|
#define PTR_SIZE 8
|
|
#endif
|
|
|
|
static char *source;
|
|
static jsmntok_t *tokens;
|
|
static int num_tokens;
|
|
static uint32_t ti; // token index
|
|
static uint8_t *decoded;
|
|
static uint32_t wi; // write index
|
|
static uint32_t bottom;
|
|
static uint32_t array_count;
|
|
|
|
static inline uint64_t pad(uint32_t di, int n) {
|
|
return (n - (di % n)) % n;
|
|
}
|
|
|
|
static void store_u8(uint8_t u8) {
|
|
*(uint8_t *)(decoded + wi) = u8;
|
|
wi += 1;
|
|
}
|
|
|
|
static void store_i32(int32_t i32) {
|
|
// TODO: signed overflow is UB
|
|
// if (i32 > INT32_MAX)
|
|
// i32 = (int32_t)(i32 - INT32_MAX - 1) - INT32_MAX - 1;
|
|
wi += pad(wi, 4);
|
|
*(int32_t *)(decoded + wi) = i32;
|
|
wi += 4;
|
|
}
|
|
|
|
static void store_u32(uint32_t u32) {
|
|
wi += pad(wi, 4);
|
|
*(uint32_t *)(decoded + wi) = u32;
|
|
wi += 4;
|
|
}
|
|
|
|
static void store_f32(float f32) {
|
|
wi += pad(wi, 4);
|
|
*(float *)(decoded + wi) = f32;
|
|
wi += 4;
|
|
}
|
|
|
|
static void store_ptr(uint32_t ptr) {
|
|
wi += pad(wi, PTR_SIZE);
|
|
*(uintptr_t *)(decoded + wi) = (uintptr_t)decoded + (uintptr_t)ptr;
|
|
wi += PTR_SIZE;
|
|
}
|
|
|
|
static void store_ptr_abs(void *ptr) {
|
|
wi += pad(wi, PTR_SIZE);
|
|
*(uintptr_t *)(decoded + wi) = (uintptr_t)ptr;
|
|
wi += PTR_SIZE;
|
|
}
|
|
|
|
static uint32_t json_string_len(char *str, uint32_t len) {
|
|
uint32_t out = 0;
|
|
for (uint32_t i = 0; i < len; ++i) {
|
|
// Escaped \" collapses to one byte
|
|
if (str[i] == '\\' && i + 1 < len && str[i + 1] == '"') {
|
|
i++;
|
|
}
|
|
out++;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
static void store_string_bytes(char *str, uint32_t len) {
|
|
for (uint32_t i = 0; i < len; ++i) {
|
|
if (str[i] == '\\' && i + 1 < len && str[i + 1] == '"') {
|
|
store_u8('"');
|
|
i++;
|
|
continue;
|
|
}
|
|
store_u8(str[i]);
|
|
}
|
|
store_u8('\0');
|
|
}
|
|
|
|
static bool is_key(char *s, jsmntok_t *t) {
|
|
return t->type == JSMN_STRING && s[t->end + 1] == ':';
|
|
}
|
|
|
|
static jsmntok_t get_token() {
|
|
jsmntok_t t = tokens[ti];
|
|
while (is_key(source, &t)) {
|
|
ti++;
|
|
t = tokens[ti];
|
|
}
|
|
return t;
|
|
}
|
|
|
|
static int traverse(uint32_t wi) {
|
|
jsmntok_t t = get_token();
|
|
if (t.type == JSMN_OBJECT) {
|
|
ti++;
|
|
uint32_t size = 0;
|
|
for (uint32_t i = 0; i < t.size; ++i) {
|
|
size += traverse(wi + size);
|
|
}
|
|
return pad(wi, PTR_SIZE) + size;
|
|
}
|
|
else if (t.type == JSMN_PRIMITIVE) {
|
|
ti++;
|
|
if (source[t.start] == 't' || source[t.start] == 'f') { // bool
|
|
return 1;
|
|
}
|
|
else if (source[t.start] == 'n') { // null
|
|
return pad(wi, PTR_SIZE) + PTR_SIZE;
|
|
}
|
|
else { // number
|
|
return pad(wi, 4) + 4;
|
|
}
|
|
}
|
|
else if (t.type == JSMN_ARRAY) {
|
|
ti++;
|
|
for (uint32_t i = 0; i < t.size; ++i) {
|
|
traverse(0);
|
|
}
|
|
return pad(wi, PTR_SIZE) + PTR_SIZE;
|
|
}
|
|
else if (t.type == JSMN_STRING) {
|
|
ti++;
|
|
return pad(wi, PTR_SIZE) + PTR_SIZE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int token_size() {
|
|
uint32_t _ti = ti;
|
|
uint32_t len = traverse(0);
|
|
ti = _ti;
|
|
return len;
|
|
}
|
|
|
|
static bool has_dot(char *str, uint32_t len) {
|
|
for (uint32_t i = 0; i < len; ++i) {
|
|
if (str[i] == '.') {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void token_write() {
|
|
jsmntok_t t = get_token();
|
|
|
|
if (t.type == JSMN_OBJECT) {
|
|
// TODO: Object containing another object
|
|
// Write object contents
|
|
uint32_t size = token_size();
|
|
size += pad(size, PTR_SIZE);
|
|
bottom += size * array_count;
|
|
ti++;
|
|
for (uint32_t i = 0; i < t.size; ++i) {
|
|
token_write();
|
|
}
|
|
}
|
|
else if (t.type == JSMN_PRIMITIVE) {
|
|
ti++;
|
|
if (source[t.start] == 't' || source[t.start] == 'f') { // bool
|
|
store_u8(source[t.start] == 't' ? 1 : 0);
|
|
}
|
|
else if (source[t.start] == 'n') { // null
|
|
store_ptr_abs(NULL);
|
|
}
|
|
else {
|
|
has_dot(source + t.start, t.end - t.start) ? store_f32(strtof(source + t.start, NULL)) :
|
|
#ifdef _WIN32
|
|
store_i32(_strtoi64(source + t.start, NULL, 10));
|
|
#else
|
|
store_i32(strtol(source + t.start, NULL, 10));
|
|
#endif
|
|
}
|
|
}
|
|
else if (t.type == JSMN_ARRAY) {
|
|
ti++;
|
|
store_ptr(bottom);
|
|
|
|
uint32_t _wi = wi;
|
|
wi = bottom;
|
|
store_ptr(bottom + PTR_SIZE + 4 + 4); // Pointer to buffer contents
|
|
store_u32(t.size); // Element count
|
|
store_u32(0); // Capacity = 0 -> do not free on first realloc
|
|
bottom = wi;
|
|
|
|
if (t.size == 0) {
|
|
wi = _wi;
|
|
return;
|
|
}
|
|
|
|
uint32_t count = t.size;
|
|
array_count = count;
|
|
t = get_token();
|
|
|
|
if (t.type == JSMN_OBJECT) {
|
|
// Struct pointers
|
|
uint32_t size = token_size();
|
|
size += pad(size, PTR_SIZE);
|
|
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
store_ptr(bottom + count * PTR_SIZE + i * size);
|
|
}
|
|
|
|
// Struct contents
|
|
bottom = pad(wi, PTR_SIZE) + wi;
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
wi = pad(wi, PTR_SIZE) + wi;
|
|
token_write();
|
|
}
|
|
}
|
|
else if (t.type == JSMN_STRING) {
|
|
// String pointers
|
|
uint32_t _ti = ti;
|
|
uint32_t strings_length = 0;
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
store_ptr(bottom + count * PTR_SIZE + strings_length);
|
|
uint32_t length = json_string_len(source + t.start, t.end - t.start);
|
|
strings_length += length;
|
|
strings_length += 1; // '\0'
|
|
ti++;
|
|
t = get_token();
|
|
}
|
|
ti = _ti;
|
|
t = get_token();
|
|
|
|
// String bytes
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
store_string_bytes(source + t.start, t.end - t.start);
|
|
ti++;
|
|
t = get_token();
|
|
}
|
|
bottom = pad(wi, PTR_SIZE) + wi;
|
|
}
|
|
else {
|
|
// Array contents
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
token_write();
|
|
}
|
|
bottom = pad(wi, PTR_SIZE) + wi;
|
|
}
|
|
|
|
wi = _wi;
|
|
array_count = 1;
|
|
}
|
|
else if (t.type == JSMN_STRING) {
|
|
ti++;
|
|
store_ptr(bottom);
|
|
|
|
uint32_t _wi = wi;
|
|
wi = bottom;
|
|
store_string_bytes(source + t.start, t.end - t.start);
|
|
bottom = pad(wi, PTR_SIZE) + wi;
|
|
wi = _wi;
|
|
}
|
|
}
|
|
|
|
static void load_tokens(char *s) {
|
|
jsmn_parser parser;
|
|
jsmn_init(&parser);
|
|
num_tokens = jsmn_parse(&parser, s, strlen(s), NULL, 0);
|
|
|
|
tokens = malloc(sizeof(jsmntok_t) * num_tokens);
|
|
jsmn_init(&parser);
|
|
jsmn_parse(&parser, s, strlen(s), tokens, num_tokens);
|
|
|
|
source = s;
|
|
ti = 0;
|
|
}
|
|
|
|
void *json_parse(char *s) {
|
|
load_tokens(s);
|
|
|
|
uint32_t out_size = strlen(s) * 2;
|
|
decoded = calloc(1, out_size);
|
|
wi = 0;
|
|
bottom = 0;
|
|
array_count = 1;
|
|
token_write();
|
|
|
|
free(tokens);
|
|
return decoded;
|
|
}
|
|
|
|
static void token_write_to_map(any_map_t *m) {
|
|
jsmntok_t t = get_token();
|
|
|
|
if (t.type == JSMN_OBJECT) {
|
|
// TODO: Object containing another object
|
|
ti++;
|
|
for (uint32_t i = 0; i < t.size; ++i) {
|
|
token_write_to_map(m);
|
|
}
|
|
}
|
|
else if (t.type == JSMN_PRIMITIVE) {
|
|
jsmntok_t tkey = tokens[ti - 1];
|
|
ti++;
|
|
any_map_set(m, substring(source, tkey.start, tkey.end), substring(source, t.start, t.end));
|
|
}
|
|
else if (t.type == JSMN_ARRAY) {
|
|
ti++;
|
|
}
|
|
else if (t.type == JSMN_STRING) {
|
|
jsmntok_t tkey = tokens[ti - 1];
|
|
ti++;
|
|
any_map_set(m, substring(source, tkey.start, tkey.end), substring(source, t.start, t.end));
|
|
}
|
|
}
|
|
|
|
any_map_t *json_parse_to_map(char *s) {
|
|
load_tokens(s);
|
|
|
|
any_map_t *m = any_map_create();
|
|
token_write_to_map(m);
|
|
|
|
free(tokens);
|
|
return m;
|
|
}
|
|
|
|
static buffer_t encoded;
|
|
static int keys;
|
|
static int array_nest = -1;
|
|
static int array_length[16];
|
|
|
|
static void enc(char *s) {
|
|
string_buffer_append(&encoded, s);
|
|
}
|
|
|
|
void json_encode_begin() {
|
|
if (encoded.buffer == NULL) {
|
|
string_buffer_init(&encoded);
|
|
}
|
|
string_buffer_reset(&encoded);
|
|
enc("{");
|
|
keys = 0;
|
|
}
|
|
|
|
char *json_encode_end() {
|
|
enc("}");
|
|
return string_copy(string_buffer_get(&encoded));
|
|
}
|
|
|
|
void json_encode_key(char *k) {
|
|
if (keys > 0) {
|
|
enc(",");
|
|
}
|
|
enc("\"");
|
|
enc(k);
|
|
enc("\":");
|
|
keys++;
|
|
}
|
|
|
|
void json_encode_null(char *k) {
|
|
json_encode_key(k);
|
|
enc("null");
|
|
}
|
|
|
|
void json_encode_string_value(char *v) {
|
|
enc("\"");
|
|
enc(v);
|
|
enc("\"");
|
|
}
|
|
|
|
void json_encode_string(char *k, char *v) {
|
|
json_encode_key(k);
|
|
json_encode_string_value(v);
|
|
}
|
|
|
|
void json_encode_string_array(char *k, string_array_t *a) {
|
|
if (a == NULL) {
|
|
json_encode_null(k);
|
|
return;
|
|
}
|
|
json_encode_begin_array(k);
|
|
for (uint32_t i = 0; i < a->length; ++i) {
|
|
if (i > 0) {
|
|
enc(",");
|
|
}
|
|
json_encode_string_value(a->buffer[i]);
|
|
}
|
|
json_encode_end_array();
|
|
}
|
|
|
|
void json_encode_f32(char *k, float f) {
|
|
json_encode_key(k);
|
|
enc(f32_to_string_with_zeros(f));
|
|
}
|
|
|
|
void json_encode_i32(char *k, int i) {
|
|
json_encode_key(k);
|
|
enc(i32_to_string(i));
|
|
}
|
|
|
|
void json_encode_f32_array(char *k, f32_array_t *a) {
|
|
if (a == NULL) {
|
|
json_encode_null(k);
|
|
return;
|
|
}
|
|
json_encode_begin_array(k);
|
|
for (uint32_t i = 0; i < a->length; ++i) {
|
|
if (i > 0) {
|
|
enc(",");
|
|
}
|
|
enc(f32_to_string_with_zeros(a->buffer[i]));
|
|
}
|
|
json_encode_end_array();
|
|
}
|
|
|
|
void json_encode_i32_array(char *k, i32_array_t *a) {
|
|
if (a == NULL) {
|
|
json_encode_null(k);
|
|
return;
|
|
}
|
|
json_encode_begin_array(k);
|
|
for (uint32_t i = 0; i < a->length; ++i) {
|
|
if (i > 0) {
|
|
enc(",");
|
|
}
|
|
enc(i32_to_string(a->buffer[i]));
|
|
}
|
|
json_encode_end_array();
|
|
}
|
|
|
|
void json_encode_bool(char *k, bool b) {
|
|
json_encode_key(k);
|
|
enc(b ? "true" : "false");
|
|
}
|
|
|
|
void json_encode_begin_array(char *k) {
|
|
array_nest++;
|
|
array_length[array_nest] = 0;
|
|
json_encode_key(k);
|
|
enc("[");
|
|
}
|
|
|
|
void json_encode_end_array() {
|
|
array_nest--;
|
|
enc("]");
|
|
}
|
|
|
|
void json_encode_begin_object() {
|
|
if (array_nest > -1) {
|
|
if (array_length[array_nest] > 0) {
|
|
enc(",");
|
|
}
|
|
array_length[array_nest]++;
|
|
}
|
|
keys = 0;
|
|
enc("{");
|
|
}
|
|
|
|
void json_encode_end_object() {
|
|
enc("}");
|
|
}
|
|
|
|
void json_encode_map(any_map_t *m) {
|
|
any_array_t *keys = map_keys(m);
|
|
for (uint32_t i = 0; i < keys->length; i++) {
|
|
json_encode_string(keys->buffer[i], any_map_get(m, keys->buffer[i]));
|
|
}
|
|
array_free(keys);
|
|
free(keys);
|
|
}
|
|
|
|
static char *jenc_src;
|
|
static jsmntok_t *jenc_tokens;
|
|
static int jenc_ti;
|
|
static uint8_t jenc_forced_array_type;
|
|
|
|
static bool jenc_has_dot(int start, int end) {
|
|
for (int i = start; i < end; i++) {
|
|
if (jenc_src[i] == '.')
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static int jenc_skip(int ti) {
|
|
jsmntok_t t = jenc_tokens[ti++];
|
|
if (t.type == JSMN_OBJECT) {
|
|
for (int i = 0; i < t.size; i++) {
|
|
ti = jenc_skip(ti); // key
|
|
ti = jenc_skip(ti); // value
|
|
}
|
|
}
|
|
else if (t.type == JSMN_ARRAY) {
|
|
for (int i = 0; i < t.size; i++) {
|
|
ti = jenc_skip(ti);
|
|
}
|
|
}
|
|
return ti;
|
|
}
|
|
|
|
static uint8_t jenc_elem_type(int ti) {
|
|
jsmntok_t t = jenc_tokens[ti];
|
|
if (t.type == JSMN_STRING)
|
|
return 0xdb;
|
|
if (t.type == JSMN_OBJECT)
|
|
return 0xdf;
|
|
if (t.type == JSMN_ARRAY)
|
|
return 0xdd;
|
|
char c = jenc_src[t.start];
|
|
if (c == 't' || c == 'f')
|
|
return 0xc2;
|
|
if (c == 'n')
|
|
return 0xc0;
|
|
if (jenc_has_dot(t.start, t.end))
|
|
return 0xca;
|
|
return 0xd2;
|
|
}
|
|
|
|
static uint8_t jenc_array_type(int count) {
|
|
if (count == 0)
|
|
return 0x0;
|
|
int ti = jenc_ti;
|
|
uint8_t first_type = jenc_elem_type(ti);
|
|
if (first_type != 0xca && first_type != 0xd2 && first_type != 0xdb) {
|
|
return 0x0; // non-scalar types -> dynamic
|
|
}
|
|
for (int i = 0; i < count; i++) {
|
|
if (jenc_elem_type(ti) != first_type)
|
|
return 0x0;
|
|
ti = jenc_skip(ti);
|
|
}
|
|
return first_type;
|
|
}
|
|
|
|
static void jenc_write_string(int start, int len) {
|
|
armpack_write_u8(0xdb);
|
|
armpack_write_u32(len);
|
|
for (int i = 0; i < len; i++) {
|
|
armpack_write_u8(jenc_src[start + i]);
|
|
}
|
|
}
|
|
|
|
static void jenc_value();
|
|
|
|
static void jenc_object(int count) {
|
|
armpack_write_u8(0xdf);
|
|
armpack_write_i32(count);
|
|
for (int i = 0; i < count; i++) {
|
|
jsmntok_t key = jenc_tokens[jenc_ti++];
|
|
int key_len = key.end - key.start;
|
|
// Check for [type] suffix in key name
|
|
uint8_t forced_type = 0;
|
|
int actual_len = key_len;
|
|
if (key_len >= 4 && jenc_src[key.end - 1] == ']') {
|
|
const char *ks = jenc_src + key.start;
|
|
if (key_len >= 5 && strncmp(ks + key_len - 5, "[f32]", 5) == 0) {
|
|
forced_type = 0xca;
|
|
actual_len = key_len - 5;
|
|
}
|
|
else if (key_len >= 5 && strncmp(ks + key_len - 5, "[i32]", 5) == 0) {
|
|
forced_type = 0xd2;
|
|
actual_len = key_len - 5;
|
|
}
|
|
else if (key_len >= 5 && strncmp(ks + key_len - 5, "[i16]", 5) == 0) {
|
|
forced_type = 0xd1;
|
|
actual_len = key_len - 5;
|
|
}
|
|
else if (key_len >= 4 && strncmp(ks + key_len - 4, "[u8]", 4) == 0) {
|
|
forced_type = 0xc4;
|
|
actual_len = key_len - 4;
|
|
}
|
|
}
|
|
jenc_write_string(key.start, actual_len);
|
|
jenc_forced_array_type = forced_type;
|
|
jenc_value();
|
|
jenc_forced_array_type = 0;
|
|
}
|
|
}
|
|
|
|
static void jenc_array(int count) {
|
|
armpack_write_u8(0xdd);
|
|
armpack_write_u32(count);
|
|
if (count == 0)
|
|
return;
|
|
|
|
uint8_t forced = jenc_forced_array_type;
|
|
jenc_forced_array_type = 0;
|
|
uint8_t elem_type = forced != 0 ? forced : jenc_array_type(count);
|
|
|
|
if (elem_type == 0xca) { // typed f32
|
|
armpack_write_u8(0xca);
|
|
for (int i = 0; i < count; i++) {
|
|
jsmntok_t t = jenc_tokens[jenc_ti++];
|
|
armpack_write_f32(strtof(jenc_src + t.start, NULL));
|
|
}
|
|
}
|
|
else if (elem_type == 0xd2) { // typed i32
|
|
armpack_write_u8(0xd2);
|
|
for (int i = 0; i < count; i++) {
|
|
jsmntok_t t = jenc_tokens[jenc_ti++];
|
|
#ifdef _WIN32
|
|
armpack_write_i32((int32_t)_strtoi64(jenc_src + t.start, NULL, 10));
|
|
#else
|
|
armpack_write_i32((int32_t)strtol(jenc_src + t.start, NULL, 10));
|
|
#endif
|
|
}
|
|
}
|
|
else if (elem_type == 0xd1) { // typed i16
|
|
armpack_write_u8(0xd1);
|
|
for (int i = 0; i < count; i++) {
|
|
jsmntok_t t = jenc_tokens[jenc_ti++];
|
|
#ifdef _WIN32
|
|
armpack_write_i16((int16_t)_strtoi64(jenc_src + t.start, NULL, 10));
|
|
#else
|
|
armpack_write_i16((int16_t)strtol(jenc_src + t.start, NULL, 10));
|
|
#endif
|
|
}
|
|
}
|
|
else if (elem_type == 0xc4) { // typed u8
|
|
armpack_write_u8(0xc4);
|
|
for (int i = 0; i < count; i++) {
|
|
jsmntok_t t = jenc_tokens[jenc_ti++];
|
|
#ifdef _WIN32
|
|
armpack_write_u8((uint8_t)_strtoi64(jenc_src + t.start, NULL, 10));
|
|
#else
|
|
armpack_write_u8((uint8_t)strtol(jenc_src + t.start, NULL, 10));
|
|
#endif
|
|
}
|
|
}
|
|
else { // dynamic
|
|
for (int i = 0; i < count; i++) {
|
|
jenc_value();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void jenc_value() {
|
|
jsmntok_t t = jenc_tokens[jenc_ti++];
|
|
if (t.type == JSMN_OBJECT) {
|
|
jenc_object(t.size);
|
|
return;
|
|
}
|
|
if (t.type == JSMN_ARRAY) {
|
|
jenc_array(t.size);
|
|
return;
|
|
}
|
|
if (t.type == JSMN_STRING) {
|
|
jenc_write_string(t.start, t.end - t.start);
|
|
return;
|
|
}
|
|
// JSMN_PRIMITIVE
|
|
char c = jenc_src[t.start];
|
|
if (c == 't') {
|
|
armpack_write_u8(0xc3); // true
|
|
}
|
|
else if (c == 'f') {
|
|
armpack_write_u8(0xc2); // false
|
|
}
|
|
else if (c == 'n') {
|
|
armpack_write_u8(0xc0); // null
|
|
}
|
|
else if (jenc_has_dot(t.start, t.end)) {
|
|
armpack_write_u8(0xca);
|
|
armpack_write_f32(strtof(jenc_src + t.start, NULL));
|
|
}
|
|
else {
|
|
armpack_write_u8(0xd2);
|
|
#ifdef _WIN32
|
|
armpack_write_i32((int32_t)_strtoi64(jenc_src + t.start, NULL, 10));
|
|
#else
|
|
armpack_write_i32((int32_t)strtol(jenc_src + t.start, NULL, 10));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
buffer_t *json_encode_to_armpack(char *json) {
|
|
jsmn_parser parser;
|
|
jsmn_init(&parser);
|
|
int n = jsmn_parse(&parser, json, strlen(json), NULL, 0);
|
|
if (n <= 0)
|
|
return NULL;
|
|
|
|
jenc_tokens = malloc(sizeof(jsmntok_t) * n);
|
|
jsmn_init(&parser);
|
|
jsmn_parse(&parser, json, strlen(json), jenc_tokens, n);
|
|
|
|
jenc_src = json;
|
|
jenc_ti = 0;
|
|
|
|
int max_size = strlen(json) * 3 + 64;
|
|
uint8_t *buf = malloc(max_size);
|
|
armpack_encode_start(buf);
|
|
jenc_value();
|
|
int size = armpack_encode_end();
|
|
|
|
free(jenc_tokens);
|
|
|
|
buffer_t *b = buffer_create(size);
|
|
memcpy(b->buffer, buf, size);
|
|
free(buf);
|
|
return b;
|
|
}
|