diff --git a/tiny-json.c b/tiny-json.c index 0324bc5..7e016ee 100644 --- a/tiny-json.c +++ b/tiny-json.c @@ -44,24 +44,25 @@ char const* json_getPropertyValue( json_t const* obj, char const* property ) { } /* Internal prototypes: */ -static char* _goWhiteSpace( char* str ); -static char* _goNum( char* str ); -static json_t* _poolInit( jsonPool_t* pool ); -static json_t* _poolNew( jsonPool_t* pool ); -static char* _objValue( char* ptr, json_t* obj, jsonPool_t* pool ); -static char* _setToNull( char* ch ); -static bool _isEndOfPrimitive( char ch ); +static char* goBlank( char* str ); +static bool isNum( unsigned char ch ); +static char* goNum( char* str ); +static json_t* poolInit( jsonPool_t* pool ); +static json_t* poolNew( 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_create( char* str, json_t mem[], unsigned int qty ) { - char* ptr = _goWhiteSpace( str ); + char* ptr = goBlank( str ); if ( !ptr || *ptr != '{' ) return 0; jsonPool_t pool = { .mem = mem, .qty = qty }; - json_t* obj = _poolInit( &pool ); + json_t* obj = poolInit( &pool ); obj->name = 0; obj->sibling = 0; obj->u.child = 0; - ptr = _objValue( ptr, obj, &pool ); + ptr = objValue( ptr, obj, &pool ); if ( !ptr ) return 0; return obj; } @@ -70,7 +71,7 @@ json_t const* json_create( char* str, json_t mem[], unsigned int qty ) { * 'b' -> '\b', 'n' -> '\n', 't' -> '\t' * @param ch The escape character. * @return The character code. */ -static char _getEscape( char ch ) { +static char getEscape( char ch ) { static struct { char ch; char code; } const pair[] = { { '\"', '\"' }, { '\\', '\\' }, @@ -89,7 +90,7 @@ static char _getEscape( char ch ) { } /** Check if a character is a hexadecimal digit. */ -static bool _isHexaDigit( unsigned char nibble ) { +static bool isHexaDigit( unsigned char nibble ) { if ( nibble < '0' ) return false; if ( nibble <= '9' ) return true; if ( nibble < 'A' ) return false; @@ -103,10 +104,10 @@ static bool _isHexaDigit( unsigned char nibble ) { * @Param str Pointer to first digit. * @retval '?' If the four characters are hexadecimal digits. * @retcal '\0' In other cases. */ -static char _getCharFromUnicode( char const* str ) { +static char getCharFromUnicode( char const* str ) { unsigned int i; for( i = 0; i < 4; ++i ) - if ( !_isHexaDigit( str[i] ) ) + if ( !isHexaDigit( str[i] ) ) return '\0'; return '?'; } @@ -116,7 +117,7 @@ static char _getCharFromUnicode( char const* str ) { * @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 ) { +static char* parseString( char* str ) { char* head = str; char* tail = str; for( ; *head >= ' '; ++head, ++tail ) { @@ -126,13 +127,13 @@ static char* _parseString( char* str ) { } if ( *head == '\\' ) { if ( *++head == 'u' ) { - char const ch = _getCharFromUnicode( ++head ); + char const ch = getCharFromUnicode( ++head ); if ( ch == '\0' ) return 0; *tail = ch; head += 3; } else { - char const esc = _getEscape( *head ); + char const esc = getEscape( *head ); if ( esc == '\0' ) return 0; *tail = esc; } @@ -147,14 +148,14 @@ static char* _parseString( char* str ) { * @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 ) { +static char* propertyName( char* ptr, json_t* property ) { property->name = ++ptr; - ptr = _parseString( ptr ); + ptr = parseString( ptr ); if ( !ptr ) return 0; - ptr = _goWhiteSpace( ptr ); + ptr = goBlank( ptr ); if ( !ptr ) return 0; if ( *ptr++ != ':' ) return 0; - return _goWhiteSpace( ptr ); + return goBlank( ptr ); } /** Parse a string to get the value of a property when its type is JSON_TEXT. @@ -162,9 +163,9 @@ static char* _propertyName( char* ptr, json_t* property ) { * @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 ) { +static char* textValue( char* ptr, json_t* property ) { ++property->u.value; - ptr = _parseString( ++ptr ); + ptr = parseString( ++ptr ); if ( !ptr ) return 0; property->type = JSON_TEXT; return ptr; @@ -175,7 +176,7 @@ static char* _textValue( char* ptr, json_t* property ) { * @param str main string * @retval Pointer to next chraracter. * @retval Null pointer if any error occur. */ -static char* _checkStr( char* ptr, char const* str ) { +static char* checkStr( char* ptr, char const* str ) { while( *str ) if ( *ptr++ != *str++ ) return 0; @@ -190,10 +191,10 @@ static char* _checkStr( char* ptr, char const* str ) { * @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 ); +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; } @@ -204,8 +205,8 @@ static char* _primitiveValue( char* ptr, json_t* property, char const* value, js * @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 ); +static char* trueValue( char* ptr, json_t* property ) { + return primitiveValue( ptr, property, "true", JSON_BOOLEAN ); } /** Parser a string to get a false value. @@ -214,8 +215,8 @@ static char* _trueValue( char* ptr, json_t* property ) { * @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 ); +static char* falseValue( char* ptr, json_t* property ) { + return primitiveValue( ptr, property, "false", JSON_BOOLEAN ); } /** Parser a string to get a null value. @@ -224,61 +225,81 @@ static char* _falseValue( char* ptr, json_t* property ) { * @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 ); +static char* nullValue( char* ptr, json_t* property ) { + return primitiveValue( ptr, property, "null", JSON_NULL ); } -static bool _isNum( unsigned char ch ); - -static char* _expValue( char* ptr, json_t* property ) { +/** 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 ( !_isNum( *ptr ) ) return 0; - ptr = _goNum( ++ptr ); - property->type = JSON_REAL; + if ( !isNum( *ptr ) ) return 0; + ptr = goNum( ++ptr ); return ptr; } -static char* _fraqValue( char* ptr, json_t* property ) { - if ( !_isNum( *ptr ) ) return 0; - ptr = _goNum( ++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 ( !isNum( *ptr ) ) return 0; + ptr = goNum( ++ptr ); if ( !ptr ) return 0; - if ( *ptr == 'e' || *ptr == 'E' ) return _expValue( ++ptr, property ); - property->type = JSON_REAL; return ptr; } -/** Parser a string to get a numerial value. +/** Parser a string to get a numerical value. * If the first character after the value is diferent 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 ) { +static char* numValue( char* ptr, json_t* property ) { if ( *ptr == '-' ) ++ptr; if ( *ptr != '0' ) { - ptr = _goNum( ptr ); + ptr = goNum( ptr ); if ( !ptr ) return 0; } - else if ( _isNum( *++ptr ) ) return 0; + else if ( isNum( *++ptr ) ) return 0; + property->type = JSON_INTEGER; if ( *ptr == '.' ) { - ptr = _fraqValue( ++ptr, property ); + ptr = fraqValue( ++ptr ); if ( !ptr ) return 0; + property->type = JSON_REAL; } - else if ( *ptr == 'e' || *ptr == 'E' ) { - ptr = _expValue( ++ptr, property ); + 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 == '-'; + unsigned int const maxdigits = negative ? 20: 19; + unsigned int const len = ptr - value; + if ( len > maxdigits ) return 0; + if ( len == maxdigits ) { + char const tmp = *ptr; + *ptr = '\0'; + char const* const min = "-9223372036854775808"; + char const* const max = "9223372036854775807"; + char const* const threshold = negative ? min: max; + if ( 0 > strcmp( threshold, value ) ) return 0; + *ptr = tmp; + } } - else if ( _isEndOfPrimitive( *ptr ) ) - property->type = JSON_INTEGER; - else return 0; - ptr = _setToNull( ptr ); + 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 ) { +static void add( json_t* obj, json_t* property ) { property->sibling = 0; if ( !obj->u.child ) obj->u.child = property; else { @@ -293,13 +314,13 @@ static void _add( json_t* obj, json_t* property ) { * @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 ) { +static char* objValue( char* ptr, json_t* obj, jsonPool_t* pool ) { obj->type = JSON_OBJ; obj->u.child = 0; obj->sibling = 0; ptr++; for(;;) { - ptr = _goWhiteSpace( ptr ); + ptr = goBlank( ptr ); if ( !ptr ) return 0; char const endchar = ( obj->type == JSON_OBJ )? '}': ']'; if ( *ptr == endchar ) { @@ -312,18 +333,18 @@ static char* _objValue( char* ptr, json_t* obj, jsonPool_t* pool ) { continue; } if ( *ptr == ',' ) { - ptr = _goWhiteSpace( ++ptr ); + ptr = goBlank( ++ptr ); if ( !ptr ) return 0; } - json_t* property = _poolNew( pool ); + json_t* property = poolNew( pool ); if ( !property ) return 0; if( obj->type != JSON_ARRAY ) { if ( *ptr != '\"' ) return 0; - ptr = _propertyName( ptr, property ); + ptr = propertyName( ptr, property ); if ( !ptr ) return 0; } else property->name = 0; - _add( obj, property ); + add( obj, property ); property->u.value = ptr; switch( *ptr ) { case '{': @@ -340,11 +361,11 @@ static char* _objValue( char* ptr, json_t* obj, jsonPool_t* pool ) { 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; + 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; } @@ -353,7 +374,7 @@ static char* _objValue( char* ptr, json_t* obj, jsonPool_t* pool ) { /** Initialize a json pool. * @param pool The handler of the pool. * @return a instace of a json. */ -static json_t* _poolInit( jsonPool_t* pool ) { +static json_t* poolInit( jsonPool_t* pool ) { pool->nextFree = 1; return &pool->mem[0]; } @@ -362,7 +383,7 @@ static json_t* _poolInit( jsonPool_t* 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* _poolNew( jsonPool_t* pool ) { +static json_t* poolNew( jsonPool_t* pool ) { if ( pool->nextFree >= pool->qty ) return 0; return &pool->mem[pool->nextFree++]; } @@ -371,7 +392,7 @@ static json_t* _poolNew( jsonPool_t* pool ) { * @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 ) { +static bool isOneOfThem( char ch, char const* set ) { while( *set != '\0' ) if ( ch == *set++ ) return true; @@ -382,49 +403,52 @@ static bool _isOneOfThem( char ch, char const* 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 ) { +static char* goWhile( char* str, char const* set ) { for(; *str != '\0'; ++str ) { - if ( !_isOneOfThem( *str, set ) ) + if ( !isOneOfThem( *str, set ) ) return str; } return 0; } -static char const* const whitespace = " \n\r\t\f"; +/** 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* _goWhiteSpace( char* str ) { - return _goWhile( str, whitespace ); +static char* goBlank( char* str ) { + return goWhile( str, blank ); } /** Checks if a character is a decimal digit. */ -static bool _isNum( unsigned char ch ) { +static bool isNum( unsigned char ch ) { return ch >= '0' && ch <= '9'; } /** 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 ) { +static char* goNum( char* str ) { for( ; *str != '\0'; ++str ) { - if ( !_isNum( *str ) ) + if ( !isNum( *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 diferent to '}' or ']'. * @param ch Pointer to character. * @return Final value pointer. */ -static char* _setToNull( char* ch ) { - if ( !_isOneOfThem( *ch, endofblock ) ) *ch++ = '\0'; +static char* setToNull( char* ch ) { + if ( !isOneOfThem( *ch, endofblock ) ) *ch++ = '\0'; return ch; } -static bool _isEndOfPrimitive( char ch ) { - return ch == ',' || _isOneOfThem( ch, whitespace ) || _isOneOfThem( ch, endofblock ); +/** Indicate if a character is the end of a primitive value. */ +static bool isEndOfPrimitive( char ch ) { + return ch == ',' || isOneOfThem( ch, blank ) || isOneOfThem( ch, endofblock ); } \ No newline at end of file