mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 23:00:17 +02:00
648 lines
22 KiB
C
648 lines
22 KiB
C
|
|
/*
|
|
|
|
<https://github.com/rafagafe/tiny-json>
|
|
|
|
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
|
SPDX-License-Identifier: MIT
|
|
Copyright (c) 2016-2018 Rafa Garcia <rafagarcia77@gmail.com>.
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <stddef.h> // For NULL
|
|
#include "tiny-json.h"
|
|
|
|
/** Structure to handle a heap of JSON properties. */
|
|
typedef struct jsonStaticPool_s {
|
|
json_t* const mem; /**< Pointer to array of json properties. */
|
|
unsigned int const qty; /**< Length of the array of json properties. */
|
|
unsigned int nextFree; /**< The index of the next free json property. */
|
|
jsonPool_t pool;
|
|
} jsonStaticPool_t;
|
|
|
|
/* Search a property by its name in a JSON object. */
|
|
json_t const* json_getProperty( json_t const* obj, char const* property ) {
|
|
json_t const* sibling;
|
|
for( sibling = obj->u.c.child; sibling; sibling = sibling->sibling )
|
|
if ( sibling->name && !strcmp( sibling->name, property ) )
|
|
return sibling;
|
|
return 0;
|
|
}
|
|
|
|
/* Search a property by its name in a JSON object and return its value. */
|
|
char const* json_getPropertyValue( json_t const* obj, char const* property ) {
|
|
json_t const* field = json_getProperty( obj, property );
|
|
if ( !field ) return 0;
|
|
jsonType_t type = json_getType( field );
|
|
if ( JSON_ARRAY >= type ) return 0;
|
|
return json_getValue( field );
|
|
}
|
|
|
|
/* Internal prototypes: */
|
|
static char* goBlank( char* str );
|
|
static char* goNum( char* str );
|
|
static json_t* poolInit( jsonPool_t* pool );
|
|
static json_t* poolAlloc( jsonPool_t* pool );
|
|
static char* objValue( char* ptr, json_t* obj, jsonPool_t* pool );
|
|
static char* setToNull( char* ch );
|
|
static bool isEndOfPrimitive( char ch );
|
|
|
|
/* Parse a string to get a json. */
|
|
json_t const* json_createWithPool( char *str, jsonPool_t *pool ) {
|
|
char* ptr = goBlank( str );
|
|
if ( !ptr || *ptr != '{' ) return 0;
|
|
json_t* obj = pool->init( pool );
|
|
obj->name = 0;
|
|
obj->sibling = 0;
|
|
obj->u.c.child = 0;
|
|
ptr = objValue( ptr, obj, pool );
|
|
if ( !ptr ) return 0;
|
|
return obj;
|
|
}
|
|
|
|
/* Parse a string to get a json. */
|
|
json_t const* json_create( char* str, json_t mem[], unsigned int qty ) {
|
|
jsonStaticPool_t spool = {
|
|
.mem = mem,
|
|
.qty = qty,
|
|
.pool = {
|
|
.init = poolInit,
|
|
.alloc = poolAlloc
|
|
}
|
|
};
|
|
return json_createWithPool( str, &spool.pool );
|
|
}
|
|
|
|
/** Get a special character with its escape character. Examples:
|
|
* 'b' -> '\b', 'n' -> '\n', 't' -> '\t'
|
|
* @param ch The escape character.
|
|
* @return The character code. */
|
|
static char getEscape( char ch ) {
|
|
static struct { char ch; char code; } const pair[] = {
|
|
{ '\"', '\"' }, { '\\', '\\' },
|
|
{ '/', '/' }, { 'b', '\b' },
|
|
{ 'f', '\f' }, { 'n', '\n' },
|
|
{ 'r', '\r' }, { 't', '\t' },
|
|
};
|
|
unsigned int i;
|
|
for( i = 0; i < sizeof pair / sizeof *pair; ++i )
|
|
if ( pair[i].ch == ch )
|
|
return pair[i].code;
|
|
return '\0';
|
|
}
|
|
|
|
/** Parse 4 characters.
|
|
* @Param str Pointer to first digit.
|
|
* @retval '?' If the four characters are hexadecimal digits.
|
|
* @retcal '\0' In other cases. */
|
|
static unsigned char getCharFromUnicode( unsigned char const* str ) {
|
|
unsigned int i;
|
|
for( i = 0; i < 4; ++i )
|
|
if ( !isxdigit( str[i] ) )
|
|
return '\0';
|
|
return '?';
|
|
}
|
|
|
|
/** Parse a string and replace the scape characters by their meaning characters.
|
|
* This parser stops when finds the character '\"'. Then replaces '\"' by '\0'.
|
|
* @param str Pointer to first character.
|
|
* @retval Pointer to first non white space after the string. If success.
|
|
* @retval Null pointer if any error occur. */
|
|
static char* parseString( char* str ) {
|
|
unsigned char* head = (unsigned char*)str;
|
|
unsigned char* tail = (unsigned char*)str;
|
|
for( ; *head >= ' '; ++head, ++tail ) {
|
|
if ( *head == '\"' ) {
|
|
*tail = '\0';
|
|
return (char*)++head;
|
|
}
|
|
if ( *head == '\\' ) {
|
|
if ( *++head == 'u' ) {
|
|
char const ch = getCharFromUnicode( ++head );
|
|
if ( ch == '\0' ) return 0;
|
|
*tail = ch;
|
|
head += 3;
|
|
}
|
|
else {
|
|
char const esc = getEscape( *head );
|
|
if ( esc == '\0' ) return 0;
|
|
*tail = esc;
|
|
}
|
|
}
|
|
else *tail = *head;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/** Parse a string to get the name of a property.
|
|
* @param str Pointer to first character.
|
|
* @param property The property to assign the name.
|
|
* @retval Pointer to first of property value. If success.
|
|
* @retval Null pointer if any error occur. */
|
|
static char* propertyName( char* ptr, json_t* property ) {
|
|
property->name = ++ptr;
|
|
ptr = parseString( ptr );
|
|
if ( !ptr ) return 0;
|
|
ptr = goBlank( ptr );
|
|
if ( !ptr ) return 0;
|
|
if ( *ptr++ != ':' ) return 0;
|
|
return goBlank( ptr );
|
|
}
|
|
|
|
/** Parse a string to get the value of a property when its type is JSON_TEXT.
|
|
* @param str Pointer to first character ('\"').
|
|
* @param property The property to assign the name.
|
|
* @retval Pointer to first non white space after the string. If success.
|
|
* @retval Null pointer if any error occur. */
|
|
static char* textValue( char* ptr, json_t* property ) {
|
|
++property->u.value;
|
|
ptr = parseString( ++ptr );
|
|
if ( !ptr ) return 0;
|
|
property->type = JSON_TEXT;
|
|
return ptr;
|
|
}
|
|
|
|
/** Compare two strings until get the null character in the second one.
|
|
* @param ptr sub string
|
|
* @param str main string
|
|
* @retval Pointer to next character.
|
|
* @retval Null pointer if any error occur. */
|
|
static char* checkStr( char* ptr, char const* str ) {
|
|
while( *str )
|
|
if ( *ptr++ != *str++ )
|
|
return 0;
|
|
return ptr;
|
|
}
|
|
|
|
/** Parser a string to get a primitive value.
|
|
* If the first character after the value is different of '}' or ']' is set to '\0'.
|
|
* @param str Pointer to first character.
|
|
* @param property Property handler to set the value and the type, (true, false or null).
|
|
* @param value String with the primitive literal.
|
|
* @param type The code of the type. ( JSON_BOOLEAN or JSON_NULL )
|
|
* @retval Pointer to first non white space after the string. If success.
|
|
* @retval Null pointer if any error occur. */
|
|
static char* primitiveValue( char* ptr, json_t* property, char const* value, jsonType_t type ) {
|
|
ptr = checkStr( ptr, value );
|
|
if ( !ptr || !isEndOfPrimitive( *ptr ) ) return 0;
|
|
ptr = setToNull( ptr );
|
|
property->type = type;
|
|
return ptr;
|
|
}
|
|
|
|
/** Parser a string to get a true value.
|
|
* If the first character after the value is different of '}' or ']' is set to '\0'.
|
|
* @param str Pointer to first character.
|
|
* @param property Property handler to set the value and the type, (true, false or null).
|
|
* @retval Pointer to first non white space after the string. If success.
|
|
* @retval Null pointer if any error occur. */
|
|
static char* trueValue( char* ptr, json_t* property ) {
|
|
return primitiveValue( ptr, property, "true", JSON_BOOLEAN );
|
|
}
|
|
|
|
/** Parser a string to get a false value.
|
|
* If the first character after the value is different of '}' or ']' is set to '\0'.
|
|
* @param str Pointer to first character.
|
|
* @param property Property handler to set the value and the type, (true, false or null).
|
|
* @retval Pointer to first non white space after the string. If success.
|
|
* @retval Null pointer if any error occur. */
|
|
static char* falseValue( char* ptr, json_t* property ) {
|
|
return primitiveValue( ptr, property, "false", JSON_BOOLEAN );
|
|
}
|
|
|
|
/** Parser a string to get a null value.
|
|
* If the first character after the value is different of '}' or ']' is set to '\0'.
|
|
* @param str Pointer to first character.
|
|
* @param property Property handler to set the value and the type, (true, false or null).
|
|
* @retval Pointer to first non white space after the string. If success.
|
|
* @retval Null pointer if any error occur. */
|
|
static char* nullValue( char* ptr, json_t* property ) {
|
|
return primitiveValue( ptr, property, "null", JSON_NULL );
|
|
}
|
|
|
|
/** Analyze the exponential part of a real number.
|
|
* @param str Pointer to first character.
|
|
* @retval Pointer to first non numerical after the string. If success.
|
|
* @retval Null pointer if any error occur. */
|
|
static char* expValue( char* ptr ) {
|
|
if ( *ptr == '-' || *ptr == '+' ) ++ptr;
|
|
if ( !isdigit( *ptr ) ) return 0;
|
|
ptr = goNum( ++ptr );
|
|
return ptr;
|
|
}
|
|
|
|
/** Analyze the decimal part of a real number.
|
|
* @param str Pointer to first character.
|
|
* @retval Pointer to first non numerical after the string. If success.
|
|
* @retval Null pointer if any error occur. */
|
|
static char* fraqValue( char* ptr ) {
|
|
if ( !isdigit( *ptr ) ) return 0;
|
|
ptr = goNum( ++ptr );
|
|
if ( !ptr ) return 0;
|
|
return ptr;
|
|
}
|
|
|
|
/** Parser a string to get a numerical value.
|
|
* If the first character after the value is different of '}' or ']' is set to '\0'.
|
|
* @param str Pointer to first character.
|
|
* @param property Property handler to set the value and the type: JSON_REAL or JSON_INTEGER.
|
|
* @retval Pointer to first non white space after the string. If success.
|
|
* @retval Null pointer if any error occur. */
|
|
static char* numValue( char* ptr, json_t* property ) {
|
|
if ( *ptr == '-' ) ++ptr;
|
|
if ( !isdigit( *ptr ) ) return 0;
|
|
if ( *ptr != '0' ) {
|
|
ptr = goNum( ptr );
|
|
if ( !ptr ) return 0;
|
|
}
|
|
else if ( isdigit( *++ptr ) ) return 0;
|
|
property->type = JSON_INTEGER;
|
|
if ( *ptr == '.' ) {
|
|
ptr = fraqValue( ++ptr );
|
|
if ( !ptr ) return 0;
|
|
property->type = JSON_REAL;
|
|
}
|
|
if ( *ptr == 'e' || *ptr == 'E' ) {
|
|
ptr = expValue( ++ptr );
|
|
if ( !ptr ) return 0;
|
|
property->type = JSON_REAL;
|
|
}
|
|
if ( !isEndOfPrimitive( *ptr ) ) return 0;
|
|
if ( JSON_INTEGER == property->type ) {
|
|
char const* value = property->u.value;
|
|
bool const negative = *value == '-';
|
|
static char const min[] = "-9223372036854775808";
|
|
static char const max[] = "9223372036854775807";
|
|
unsigned int const maxdigits = ( negative? sizeof min: sizeof max ) - 1;
|
|
unsigned int const len = ptr - value;
|
|
if ( len > maxdigits ) return 0;
|
|
if ( len == maxdigits ) {
|
|
char const tmp = *ptr;
|
|
*ptr = '\0';
|
|
char const* const threshold = negative ? min: max;
|
|
if ( 0 > strcmp( threshold, value ) ) return 0;
|
|
*ptr = tmp;
|
|
}
|
|
}
|
|
ptr = setToNull( ptr );
|
|
return ptr;
|
|
}
|
|
|
|
/** Add a property to a JSON object or array.
|
|
* @param obj The handler of the JSON object or array.
|
|
* @param property The handler of the property to be added. */
|
|
static void add( json_t* obj, json_t* property ) {
|
|
property->sibling = 0;
|
|
if ( !obj->u.c.child ){
|
|
obj->u.c.child = property;
|
|
obj->u.c.last_child = property;
|
|
} else {
|
|
obj->u.c.last_child->sibling = property;
|
|
obj->u.c.last_child = property;
|
|
}
|
|
}
|
|
|
|
/** Parser a string to get a json object value.
|
|
* @param str Pointer to first character.
|
|
* @param pool The handler of a json pool for creating json instances.
|
|
* @retval Pointer to first character after the value. If success.
|
|
* @retval Null pointer if any error occur. */
|
|
static char* objValue( char* ptr, json_t* obj, jsonPool_t* pool ) {
|
|
obj->type = JSON_OBJ;
|
|
obj->u.c.child = 0;
|
|
obj->sibling = 0;
|
|
ptr++;
|
|
for(;;) {
|
|
ptr = goBlank( ptr );
|
|
if ( !ptr ) return 0;
|
|
if ( *ptr == ',' ) {
|
|
++ptr;
|
|
continue;
|
|
}
|
|
char const endchar = ( obj->type == JSON_OBJ )? '}': ']';
|
|
if ( *ptr == endchar ) {
|
|
*ptr = '\0';
|
|
json_t* parentObj = obj->sibling;
|
|
if ( !parentObj ) return ++ptr;
|
|
obj->sibling = 0;
|
|
obj = parentObj;
|
|
++ptr;
|
|
continue;
|
|
}
|
|
json_t* property = pool->alloc( pool );
|
|
if ( !property ) return 0;
|
|
if( obj->type != JSON_ARRAY ) {
|
|
if ( *ptr != '\"' ) return 0;
|
|
ptr = propertyName( ptr, property );
|
|
if ( !ptr ) return 0;
|
|
}
|
|
else property->name = 0;
|
|
add( obj, property );
|
|
property->u.value = ptr;
|
|
switch( *ptr ) {
|
|
case '{':
|
|
property->type = JSON_OBJ;
|
|
property->u.c.child = 0;
|
|
property->sibling = obj;
|
|
obj = property;
|
|
++ptr;
|
|
break;
|
|
case '[':
|
|
property->type = JSON_ARRAY;
|
|
property->u.c.child = 0;
|
|
property->sibling = obj;
|
|
obj = property;
|
|
++ptr;
|
|
break;
|
|
case '\"': ptr = textValue( ptr, property ); break;
|
|
case 't': ptr = trueValue( ptr, property ); break;
|
|
case 'f': ptr = falseValue( ptr, property ); break;
|
|
case 'n': ptr = nullValue( ptr, property ); break;
|
|
default: ptr = numValue( ptr, property ); break;
|
|
}
|
|
if ( !ptr ) return 0;
|
|
}
|
|
}
|
|
|
|
/** Initialize a json pool.
|
|
* @param pool The handler of the pool.
|
|
* @return a instance of a json. */
|
|
static json_t* poolInit( jsonPool_t* pool ) {
|
|
jsonStaticPool_t *spool = json_containerOf( pool, jsonStaticPool_t, pool );
|
|
spool->nextFree = 1;
|
|
return spool->mem;
|
|
}
|
|
|
|
/** Create an instance of a json from a pool.
|
|
* @param pool The handler of the pool.
|
|
* @retval The handler of the new instance if success.
|
|
* @retval Null pointer if the pool was empty. */
|
|
static json_t* poolAlloc( jsonPool_t* pool ) {
|
|
jsonStaticPool_t *spool = json_containerOf( pool, jsonStaticPool_t, pool );
|
|
if ( spool->nextFree >= spool->qty ) return 0;
|
|
return spool->mem + spool->nextFree++;
|
|
}
|
|
|
|
/** Checks whether an character belongs to set.
|
|
* @param ch Character value to be checked.
|
|
* @param set Set of characters. It is just a null-terminated string.
|
|
* @return true or false there is membership or not. */
|
|
static bool isOneOfThem( char ch, char const* set ) {
|
|
while( *set != '\0' )
|
|
if ( ch == *set++ )
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/** Increases a pointer while it points to a character that belongs to a set.
|
|
* @param str The initial pointer value.
|
|
* @param set Set of characters. It is just a null-terminated string.
|
|
* @return The final pointer value or null pointer if the null character was found. */
|
|
static char* goWhile( char* str, char const* set ) {
|
|
for(; *str != '\0'; ++str ) {
|
|
if ( !isOneOfThem( *str, set ) )
|
|
return str;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/** Set of characters that defines a blank. */
|
|
static char const* const blank = " \n\r\t\f";
|
|
|
|
/** Increases a pointer while it points to a white space character.
|
|
* @param str The initial pointer value.
|
|
* @return The final pointer value or null pointer if the null character was found. */
|
|
static char* goBlank( char* str ) {
|
|
return goWhile( str, blank );
|
|
}
|
|
|
|
/** Increases a pointer while it points to a decimal digit character.
|
|
* @param str The initial pointer value.
|
|
* @return The final pointer value or null pointer if the null character was found. */
|
|
static char* goNum( char* str ) {
|
|
for( ; *str != '\0'; ++str ) {
|
|
if ( !isdigit( *str ) )
|
|
return str;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/** Set of characters that defines the end of an array or a JSON object. */
|
|
static char const* const endofblock = "}]";
|
|
|
|
/** Set a char to '\0' and increase its pointer if the char is different to '}' or ']'.
|
|
* @param ch Pointer to character.
|
|
* @return Final value pointer. */
|
|
static char* setToNull( char* ch ) {
|
|
if ( !isOneOfThem( *ch, endofblock ) ) *ch++ = '\0';
|
|
return ch;
|
|
}
|
|
|
|
/** Indicate if a character is the end of a primitive value. */
|
|
static bool isEndOfPrimitive( char ch ) {
|
|
return ch == ',' || isOneOfThem( ch, blank ) || isOneOfThem( ch, endofblock );
|
|
}
|
|
|
|
/** Add a character at the end of a string.
|
|
* @param dest Pointer to the null character of the string
|
|
* @param ch Value to be added.
|
|
* @return Pointer to the null character of the destination string. */
|
|
static char* chtoa( char* dest, char ch ) {
|
|
*dest = ch;
|
|
*++dest = '\0';
|
|
return dest;
|
|
}
|
|
|
|
/** Copy a null-terminated string.
|
|
* @param dest Destination memory block.
|
|
* @param src Source string.
|
|
* @return Pointer to the null character of the destination string. */
|
|
static char* atoa( char* dest, char const* src ) {
|
|
for( ; *src != '\0'; ++dest, ++src )
|
|
*dest = *src;
|
|
*dest = '\0';
|
|
return dest;
|
|
}
|
|
|
|
/* Open a JSON object in a JSON string. */
|
|
char* json_objOpen( char* dest, char const* name ) {
|
|
if ( NULL == name )
|
|
dest = chtoa( dest, '{' );
|
|
else {
|
|
dest = chtoa( dest, '\"' );
|
|
dest = atoa( dest, name );
|
|
dest = atoa( dest, "\":{" );
|
|
}
|
|
return dest;
|
|
}
|
|
|
|
/* Close a JSON object in a JSON string. */
|
|
char* json_objClose( char* dest ) {
|
|
if ( dest[-1] == ',' )
|
|
--dest;
|
|
return atoa( dest, "}," );
|
|
}
|
|
|
|
/* Open an array in a JSON string. */
|
|
char* json_arrOpen( char* dest, char const* name ) {
|
|
if ( NULL == name )
|
|
dest = chtoa( dest, '[' );
|
|
else {
|
|
dest = chtoa( dest, '\"' );
|
|
dest = atoa( dest, name );
|
|
dest = atoa( dest, "\":[" );
|
|
}
|
|
return dest;
|
|
}
|
|
|
|
/* Close an array in a JSON string. */
|
|
char* json_arrClose( char* dest ) {
|
|
if ( dest[-1] == ',' )
|
|
--dest;
|
|
return atoa( dest, "]," );
|
|
}
|
|
|
|
/** Add the name of a text property.
|
|
* @param dest Destination memory.
|
|
* @param name The name of the property.
|
|
* @return Pointer to the next char. */
|
|
static char* strname( char* dest, char const* name ) {
|
|
dest = chtoa( dest, '\"' );
|
|
if ( NULL != name ) {
|
|
dest = atoa( dest, name );
|
|
dest = atoa( dest, "\":\"" );
|
|
}
|
|
return dest;
|
|
}
|
|
|
|
/** Get the hexadecimal digit of the least significant nibble of a integer. */
|
|
static int nibbletoch( int nibble ) {
|
|
return "0123456789ABCDEF"[ nibble % 16u ];
|
|
}
|
|
|
|
/** Get the escape character of a non-printable.
|
|
* @param ch Character source.
|
|
* @return The escape character or null character if error. */
|
|
static int escape( int ch ) {
|
|
static struct { char code; char ch; } const pair[] = {
|
|
{ '\"', '\"' }, { '\\', '\\' }, { '/', '/' }, { 'b', '\b' },
|
|
{ 'f', '\f' }, { 'n', '\n' }, { 'r', '\r' }, { 't', '\t' },
|
|
};
|
|
for( int i = 0; i < sizeof pair / sizeof *pair; ++i )
|
|
if ( ch == pair[i].ch )
|
|
return pair[i].code;
|
|
return '\0';
|
|
}
|
|
|
|
/** Copy a null-terminated string inserting escape characters if needed.
|
|
* @param dest Destination memory block.
|
|
* @param src Source string.
|
|
* @return Pointer to the null character of the destination string. */
|
|
static char* atoesc( char* dest, char const* src ) {
|
|
for( ; *src != '\0'; ++dest, ++src ) {
|
|
if ( *src >= ' ' && *src != '\"' && *src != '\\' && *src != '/' )
|
|
*dest = *src;
|
|
else {
|
|
*dest++ = '\\';
|
|
int const esc = escape( *src );
|
|
if ( esc )
|
|
*dest = esc;
|
|
else {
|
|
*dest++ = 'u';
|
|
*dest++ = '0';
|
|
*dest++ = '0';
|
|
*dest++ = nibbletoch( *src / 16 );
|
|
*dest++ = nibbletoch( *src );
|
|
}
|
|
}
|
|
}
|
|
*dest = '\0';
|
|
return dest;
|
|
}
|
|
|
|
/* Add a text property in a JSON string. */
|
|
char* json_str( char* dest, char const* name, char const* value ) {
|
|
dest = strname( dest, name );
|
|
dest = atoesc( dest, value );
|
|
dest = atoa( dest, "\"," );
|
|
return dest;
|
|
}
|
|
|
|
/** Add the name of a primitive property.
|
|
* @param dest Destination memory.
|
|
* @param name The name of the property.
|
|
* @return Pointer to the next char. */
|
|
static char* primitivename( char* dest, char const* name ) {
|
|
if( NULL == name )
|
|
return dest;
|
|
dest = chtoa( dest, '\"' );
|
|
dest = atoa( dest, name );
|
|
dest = atoa( dest, "\":" );
|
|
return dest;
|
|
}
|
|
|
|
/* Add a boolean property in a JSON string. */
|
|
char* json_bool( char* dest, char const* name, int value ) {
|
|
dest = primitivename( dest, name );
|
|
dest = atoa( dest, value ? "true," : "false," );
|
|
return dest;
|
|
}
|
|
|
|
/* Add a null property in a JSON string. */
|
|
char* json_null( char* dest, char const* name ) {
|
|
dest = primitivename( dest, name );
|
|
dest = atoa( dest, "null," );
|
|
return dest;
|
|
}
|
|
|
|
/* Used to finish the root JSON object. After call json_objClose(). */
|
|
char* json_end( char* dest ) {
|
|
if ( ',' == dest[-1] ) {
|
|
dest[-1] = '\0';
|
|
--dest;
|
|
}
|
|
return dest;
|
|
}
|
|
|
|
#define ALL_TYPES \
|
|
X( json_int, int, "%d" ) \
|
|
X( json_long, long, "%ld" ) \
|
|
X( json_uint, unsigned int, "%u" ) \
|
|
X( json_ulong, unsigned long, "%lu" ) \
|
|
X( json_verylong, long long, "%lld" ) \
|
|
X( json_double, double, "%g" ) \
|
|
|
|
|
|
#define json_num( funcname, type, fmt ) \
|
|
char* funcname( char* dest, char const* name, type value ) { \
|
|
dest = primitivename( dest, name ); \
|
|
dest += sprintf( dest, fmt, value ); \
|
|
dest = chtoa( dest, ',' ); \
|
|
return dest; \
|
|
}
|
|
|
|
#define X( name, type, fmt ) json_num( name, type, fmt )
|
|
ALL_TYPES
|
|
#undef X
|