123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461 |
- /*
- <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 <string.h>
- #include <ctype.h>
- #include "tiny_json.h"
- /** Structure to handle a heap of JSON properties. */
- typedef struct jsonStaticPool_s {
- json_t* mem; /**< Pointer to array of json properties. */
- unsigned int 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 != '{' && *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;
- spool.mem = mem;
- spool.qty = qty;
- spool.pool.init = poolInit;
- spool.pool.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.
- * @retval 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.
- * @retval '\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 ptr 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 ptr 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 ptr 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 ptr 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 ptr 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 ptr 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 ptr 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( (int)(*ptr) ) ) return 0;
- ptr = goNum( ++ptr );
- return ptr;
- }
- /** Analyze the decimal part of a real number.
- * @param ptr 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( (int)(*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 ptr 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( (int)(*ptr) ) ) return 0;
- if ( *ptr != '0' ) {
- ptr = goNum( ptr );
- if ( !ptr ) return 0;
- }
- else if ( isdigit( (int)(*++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 = ( unsigned int const ) ( 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 ptr Pointer to first character.
- * @param obj The handler of the JSON root object or array.
- * @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 = *ptr == '{' ? JSON_OBJ : JSON_ARRAY;
- 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( (int)(*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 );
- }
|