Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add API to iterate over items in a JSON collection #74

Merged
merged 4 commits into from
Dec 8, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/doxygen/pages.dox
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ are written to cover every path of execution and achieve 100% branch coverage.
@subpage json_search_function <br>
@subpage json_searcht_function <br>
@subpage json_searchconst_function <br>
@subpage json_iterate_function <br>

@page json_validate_function JSON_Validate
@snippet core_json.h declare_json_validate
Expand All @@ -78,10 +79,17 @@ are written to cover every path of execution and achieve 100% branch coverage.
@page json_searchconst_function JSON_SearchConst
@snippet core_json.h declare_json_searchconst
@copydoc JSON_SearchConst

@page json_iterate_function JSON_Iterate
@snippet core_json.h declare_json_iterate
@copydoc JSON_Iterate
*/

<!-- We do not use doxygen ALIASes here because there have been issues in the past versions with "^^" newlines within the alias definition. -->
/**
@defgroup json_enum_types Enumerated Types
@brief Enumerated types of the JSON library

@defgroup json_struct_types Struct Types
@brief Struct types of the JSON library
*/
6 changes: 6 additions & 0 deletions lexicon.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ foo
ifndef
inc
ingroup
int
json
jsonarray
jsonbadparameter
jsonfalse
jsonillegaldocument
jsoninvalid
jsonmaxdepthexceeded
jsonnotfound
jsonnull
Expand All @@ -48,6 +50,7 @@ jsonstatus
jsonstring
jsonsuccess
jsontrue
jsontype
keylength
len
longjmp
Expand All @@ -59,7 +62,10 @@ nb
nextkeyvaluepair
noninfringement
nul
outkey
outkeylength
outlength
outpair
outtype
outvalue
outvaluelength
Expand Down
128 changes: 128 additions & 0 deletions source/core_json.c
Original file line number Diff line number Diff line change
Expand Up @@ -1676,3 +1676,131 @@ JSONStatus_t JSON_SearchT( char * buf,
return JSON_SearchConst( ( const char * ) buf, max, query, queryLength,
( const char ** ) outValue, outValueLength, outType );
}

/** @cond DO_NOT_DOCUMENT */

/**
* @brief Output the next key-value pair or value from a collection.
*
* @param[in] buf The buffer to search.
* @param[in] max size of the buffer.
* @param[in] start The index at which the collection begins.
* @param[in,out] next The index at which to seek the next value.
* @param[out] outKey A pointer to receive the index of the value found.
* @param[out] outKeyLength A pointer to receive the length of the value found.
* @param[out] outValue A pointer to receive the index of the value found.
* @param[out] outValueLength A pointer to receive the length of the value found.
*
* @return #JSONSuccess if a value is output;
* #JSONIllegalDocument if the buffer does not begin with '[' or '{';
* #JSONNotFound if there are no further values in the collection.
*/
static JSONStatus_t iterate( const char * buf,
size_t max,
size_t * start,
size_t * next,
size_t * outKey,
size_t * outKeyLength,
size_t * outValue,
size_t * outValueLength )
{
JSONStatus_t ret = JSONNotFound;
bool found = false;

assert( ( buf != NULL ) && ( max > 0U ) );
assert( ( start != NULL ) && ( next != NULL ) );
assert( ( outKey != NULL ) && ( outKeyLength != NULL ) );
assert( ( outValue != NULL ) && ( outValueLength != NULL ) );

if( *start < max )
{
switch( buf[ *start ] )
{
case '[':
found = nextValue( buf, next, max, outValue, outValueLength );

if( found == true )
{
*outKey = 0;
*outKeyLength = 0;
}

break;

case '{':
found = nextKeyValuePair( buf, next, max, outKey, outKeyLength,
outValue, outValueLength );
break;

default:
ret = JSONIllegalDocument;
break;
}
}

if( found == true )
{
ret = JSONSuccess;
( void ) skipSpaceAndComma( buf, next, max );
dan4thewin marked this conversation as resolved.
Show resolved Hide resolved
}

return ret;
}

/** @endcond */

/**
* See core_json.h for docs.
*/
JSONStatus_t JSON_Iterate( const char * buf,
size_t max,
size_t * start,
size_t * next,
JSONPair_t * outPair )
{
JSONStatus_t ret;
size_t key, keyLength, value, valueLength;

if( ( buf == NULL ) || ( start == NULL ) || ( next == NULL ) ||
( outPair == NULL ) )
{
ret = JSONNullParameter;
}
else if( ( max == 0U ) || ( *start >= max ) || ( *next > max ) )
{
ret = JSONBadParameter;
}
else
{
skipSpace( buf, start, max );

if( *next <= *start )
{
*next = *start + 1U;
skipSpace( buf, next, max );
}

ret = iterate( buf, max, start, next, &key, &keyLength,
&value, &valueLength );
}

if( ret == JSONSuccess )
{
JSONTypes_t t = getType( buf[ value ] );

if( t == JSONString )
{
/* strip the surrounding quotes */
value++;
valueLength -= 2U;
}

outPair->key = ( key == 0U ) ? NULL : &buf[ key ];
outPair->keyLength = keyLength;
outPair->value = &buf[ value ];
outPair->valueLength = valueLength;
outPair->jsonType = t;
}

return ret;
}
106 changes: 98 additions & 8 deletions source/include/core_json.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,17 +173,18 @@ JSONStatus_t JSON_Validate( const char * buf,

/**
* @ingroup json_enum_types
* @brief Return codes from coreJSON library functions.
* @brief Value types from the JSON standard.
*/
typedef enum
{
JSONString = 0, /**< @brief A quote delimited sequence of Unicode characters. */
JSONNumber, /**< @brief A rational number. */
JSONTrue, /**< @brief The literal value true. */
JSONFalse, /**< @brief The literal value false. */
JSONNull, /**< @brief The literal value null. */
JSONObject, /**< @brief A collection of zero or more key-value pairs. */
JSONArray /**< @brief A collection of zero or more values. */
JSONInvalid = 0, /**< @brief Not a valid JSON type. */
JSONString, /**< @brief A quote delimited sequence of Unicode characters. */
JSONNumber, /**< @brief A rational number. */
JSONTrue, /**< @brief The literal value true. */
JSONFalse, /**< @brief The literal value false. */
JSONNull, /**< @brief The literal value null. */
JSONObject, /**< @brief A collection of zero or more key-value pairs. */
JSONArray /**< @brief A collection of zero or more values. */
} JSONTypes_t;

/**
Expand Down Expand Up @@ -231,4 +232,93 @@ JSONStatus_t JSON_SearchConst( const char * buf,
size_t * outValueLength,
JSONTypes_t * outType );
/* @[declare_json_searchconst] */

/**
* @ingroup json_struct_types
* @brief Structure to represent a key-value pair.
*/
typedef struct
{
const char * key; /**< @brief Pointer to the code point sequence for key. */
size_t keyLength; /**< @brief Length of the code point sequence for key. */
const char * value; /**< @brief Pointer to the code point sequence for value. */
size_t valueLength; /**< @brief Length of the code point sequence for value. */
JSONTypes_t jsonType; /**< @brief JSON-specific type of the value. */
} JSONPair_t;

/**
* @brief Output the next key-value pair or value from a collection.
*
* This function may be used in a loop to output each key-value pair from an object,
* or each value from an array. For the first invocation, the integers pointed to by
* start and next should be initialized to 0. These will be updated by the function.
* If another key-value pair or value is present, the output structure is populated
* and #JSONSuccess is returned; otherwise the structure is unchanged and #JSONNotFound
* is returned.
*
* @param[in] buf The buffer to search.
* @param[in] max size of the buffer.
* @param[in,out] start The index at which the collection begins.
* @param[in,out] next The index at which to seek the next value.
* @param[out] outPair A pointer to receive the next key-value pair.
*
* @note This function expects a valid JSON document; run JSON_Validate() first.
dan4thewin marked this conversation as resolved.
Show resolved Hide resolved
*
* @note For an object, the outPair structure will reference a key and its value.
* For an array, only the value will be referenced (i.e., outPair.key will be NULL).
*
* @return #JSONSuccess if a value is output;
* #JSONIllegalDocument if the buffer does not contain a collection;
* #JSONNotFound if there are no further values in the collection.
*
* <b>Example</b>
* @code{c}
* // Variables used in this example.
* static char * json_types[] =
* {
* "invalid",
* "string",
* "number",
* "true",
* "false",
* "null",
* "object",
* "array"
* };
*
* void show( const char * json,
* size_t length )
* {
* size_t start = 0, next = 0;
* JSONPair_t pair = { 0 };
* JSONStatus_t result;
*
* result = JSON_Validate( json, length );
* if( result == JSONSuccess )
* {
* result = JSON_Iterate( json, length, &start, &next, &pair );
* }
*
* while( result == JSONSuccess )
* {
* if( pair.key != NULL )
* {
* printf( "key: %.*s\t", ( int ) pair.keyLength, pair.key );
* }
*
* printf( "value: (%s) %.*s\n", json_types[ pair.jsonType ],
* ( int ) pair.valueLength, pair.value );
*
* result = JSON_Iterate( json, length, &start, &next, &pair );
* }
* }
* @endcode
*/
/* @[declare_json_iterate] */
JSONStatus_t JSON_Iterate( const char * buf,
size_t max,
size_t * start,
size_t * next,
JSONPair_t * outPair );
/* @[declare_json_iterate] */
#endif /* ifndef CORE_JSON_H_ */
5 changes: 5 additions & 0 deletions test/cbmc/include/core_json_annex.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@
/* All possible return values for JSON_Search() */
#define jsonSearchEnum( x ) ( jsonValidateEnum( x ) || ( x == JSONNotFound ) )

/* All possible return values for JSON_Iterate() */
#define jsonIterateEnum( x ) \
( parameterEnum( x ) || ( x == JSONIllegalDocument ) || \
( x == JSONNotFound ) || ( x == JSONSuccess ) )

/* All possible type values output from JSON_SearchT() */
#define jsonTypesEnum( x ) \
( ( x == JSONString ) || \
Expand Down
Loading