switch from internal jReadWrite to external
This commit is contained in:
parent
9358549667
commit
fb0fc0a30a
|
|
@ -13,13 +13,10 @@ idf_component_register(
|
||||||
"src/MQTT.c"
|
"src/MQTT.c"
|
||||||
"src/MQTTSysHandler.c"
|
"src/MQTTSysHandler.c"
|
||||||
"src/OTA.c"
|
"src/OTA.c"
|
||||||
"extlibs/jRead.c"
|
|
||||||
"extlibs/jWrite.c"
|
|
||||||
|
|
||||||
INCLUDE_DIRS "."
|
INCLUDE_DIRS "."
|
||||||
"include"
|
"include"
|
||||||
"src"
|
"src"
|
||||||
"extlibs"
|
|
||||||
|
|
||||||
REQUIRES nvs_flash
|
REQUIRES nvs_flash
|
||||||
libespfs
|
libespfs
|
||||||
|
|
@ -30,4 +27,5 @@ idf_component_register(
|
||||||
esp_modem
|
esp_modem
|
||||||
esp_https_ota
|
esp_https_ota
|
||||||
app_update
|
app_update
|
||||||
|
jReadWrite
|
||||||
)
|
)
|
||||||
784
extlibs/jRead.c
784
extlibs/jRead.c
|
|
@ -1,784 +0,0 @@
|
||||||
// jRead.cpp
|
|
||||||
// Version 1v6
|
|
||||||
//
|
|
||||||
// jRead - an in-place JSON element reader
|
|
||||||
// =======================================
|
|
||||||
//
|
|
||||||
// Instead of parsing JSON into some structure, this maintains the input JSON as unaltered text
|
|
||||||
// and allows queries to be made on it directly.
|
|
||||||
//
|
|
||||||
// e.g. with the simple JSON:
|
|
||||||
// {
|
|
||||||
// "astring":"This is a string",
|
|
||||||
// "anumber":42,
|
|
||||||
// "myarray":[ "one", 2, {"description":"element 3"}, null ],
|
|
||||||
// "yesno":true,
|
|
||||||
// "HowMany":"1234",
|
|
||||||
// "foo":null
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// calling:
|
|
||||||
// jRead( json, "{'myarray'[0", &jElem );
|
|
||||||
//
|
|
||||||
// would return:
|
|
||||||
// jElem.dataType= JREAD_STRING;
|
|
||||||
// jElem.elements= 1
|
|
||||||
// jElem.bytelen= 3
|
|
||||||
// jElem.pValue -> "one"
|
|
||||||
//
|
|
||||||
// or you could call the helper functions:
|
|
||||||
// jRead_string( json, "{'astring'", destString, MAXLEN );
|
|
||||||
// jRead_int( json, "{'anumber'", &myint );
|
|
||||||
// jRead_string( json, "{'myarray'[3", destString, MAXLEN );
|
|
||||||
// etc.
|
|
||||||
//
|
|
||||||
// Note that the helper functions do type coersion and always return a value
|
|
||||||
// (on error an empty string is returned or value of zero etc.)
|
|
||||||
//
|
|
||||||
// The query string simply defines the route to the required data item
|
|
||||||
// as an arbitary list of object or array specifiers:
|
|
||||||
// object element= "{'keyname'"
|
|
||||||
// array element= "[INDEX"
|
|
||||||
//
|
|
||||||
// The jRead() function fills a jReadElement structure to describe the located element
|
|
||||||
// this can be used to locate any element, not just terminal values
|
|
||||||
// e.g.
|
|
||||||
// jRead( json, "{'myarray'", &jElem );
|
|
||||||
//
|
|
||||||
// in this case jElem would contain:
|
|
||||||
// jElem.dataType= JSON_ARRAY
|
|
||||||
// jElem.elements= 4
|
|
||||||
// jElem.bytelen= 46
|
|
||||||
// jElem.pValue -> [ "one", 2, {"descripton":"element 3"}, null ] ...
|
|
||||||
//
|
|
||||||
// allowing jRead to be called again on the array:
|
|
||||||
// e.g.
|
|
||||||
// jRead( jElem.pValue, "[3", &jElem ); // get 4th element - the null value
|
|
||||||
//
|
|
||||||
// .oO! see main.c runExamples() for a whole bunch of examples !Oo.
|
|
||||||
// -------------------------------------------------------
|
|
||||||
//
|
|
||||||
// Note that jRead never modifies the source JSON and does not allocate any memory.
|
|
||||||
// i.e. elements are returned as pointer and length into the source text.
|
|
||||||
//
|
|
||||||
// Functions
|
|
||||||
// =========
|
|
||||||
// Main JSON reader:
|
|
||||||
// int jRead( char * JsonSource, char *query, jReadElement &pResult );
|
|
||||||
//
|
|
||||||
// Extended function using query parameters for indexing:
|
|
||||||
// int jRead( char * JsonSource, char *query, jReadElement &pResult, int *queryParams );
|
|
||||||
//
|
|
||||||
// Function to step thru JSON arrays instead of indexing:
|
|
||||||
// char *jReadArrayStep( char *pJsonArray, struct jReadElement *pResult );
|
|
||||||
//
|
|
||||||
// Optional Helper functions:
|
|
||||||
// long jRead_long( char *pJson, char *pQuery );
|
|
||||||
// int jRead_int( char *pJson, char *pQuery );
|
|
||||||
// double jRead_double( char *pJson, char *pQuery );
|
|
||||||
// int jRead_string( char *pJson, char *pQuery, char *pDest, int destlen );
|
|
||||||
//
|
|
||||||
// Optional String output Functions
|
|
||||||
// char * jReadTypeToString( int dataType ); // string describes dataType
|
|
||||||
// char * jReadErrorToString( int error ); // string descibes error code
|
|
||||||
//
|
|
||||||
// *NEW* in 1v2
|
|
||||||
// - "{NUMBER" returns the "key" value at that index within an object
|
|
||||||
// - jReadParam() adds queryParams which can be used as indexes into arrays (or into
|
|
||||||
// objects to return key values) by specifying '*' in the query string
|
|
||||||
// e.g. jReadParam( pJson, "[*", &result, &index )
|
|
||||||
// *NEW in 1v4
|
|
||||||
// - fixed a couple of error return values
|
|
||||||
// - added #define JREAD_DOUBLE_QUOTE_IN_QUERY
|
|
||||||
// *NEW* in 1v5 (11mar2015)
|
|
||||||
// - fixed null ptr if '[*' used when null param passed
|
|
||||||
// *NEW* in 1v6 (24sep2016)
|
|
||||||
// - fixed handling of empty arrays and objects
|
|
||||||
//
|
|
||||||
// TonyWilk, 24sep2016
|
|
||||||
// mail at tonywilk . co .uk
|
|
||||||
//
|
|
||||||
// License: "Free as in You Owe Me a Beer"
|
|
||||||
// - actually, since some people really worry about licenses, you are free to apply
|
|
||||||
// whatever licence you want.
|
|
||||||
//
|
|
||||||
// Note: jRead_atol() and jRead_atof() are modified from original routines
|
|
||||||
// fast_atol() and fast_atof() 09-May-2009 Tom Van Baak (tvb) www.LeapSecond.com
|
|
||||||
//
|
|
||||||
// You may want to replace the use of jRead_atol() and jRead_atof() in helper functions
|
|
||||||
// of your own. Especially note that my atof does not handle exponents.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
#include <jRead.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
|
|
||||||
// By default we use single quote in query strings so it's a lot easier
|
|
||||||
// to type in code i.e. "{'key'" instead of "{\"key\""
|
|
||||||
//
|
|
||||||
#ifdef JREAD_DOUBLE_QUOTE_IN_QUERY
|
|
||||||
#define QUERY_QUOTE '\"'
|
|
||||||
#else
|
|
||||||
#define QUERY_QUOTE '\''
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//------------------------------------------------------
|
|
||||||
// Internal Functions
|
|
||||||
|
|
||||||
char * jReadSkipWhitespace( char *sp );
|
|
||||||
char * jReadFindTok( char *sp, int *tokType );
|
|
||||||
char * jReadGetString( char *pJson, struct jReadElement *pElem, char quote );
|
|
||||||
int jReadTextLen( char *pJson );
|
|
||||||
int jReadStrcmp( struct jReadElement *j1, struct jReadElement *j2 );
|
|
||||||
char * jReadCountObject( char *pJson, struct jReadElement *pResult, int keyIndex );
|
|
||||||
char * jReadCountArray( char *pJson, struct jReadElement *pResult );
|
|
||||||
char * jRead_atoi( char *p, unsigned int *result );
|
|
||||||
char * jRead_atol( char *p, long *result );
|
|
||||||
char * jRead_atof( char *p, double *result);
|
|
||||||
|
|
||||||
//=======================================================
|
|
||||||
|
|
||||||
char *jReadSkipWhitespace( char *sp )
|
|
||||||
{
|
|
||||||
while( (*sp != '\0') && (*sp <= ' ') )
|
|
||||||
sp++;
|
|
||||||
return sp;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Find start of a token
|
|
||||||
// - returns pointer to start of next token or element
|
|
||||||
// returns type via tokType
|
|
||||||
//
|
|
||||||
char *jReadFindTok( char *sp, int *tokType )
|
|
||||||
{
|
|
||||||
char c;
|
|
||||||
sp= jReadSkipWhitespace(sp);
|
|
||||||
c= *sp;
|
|
||||||
if( c == '\0' ) *tokType= JREAD_EOL;
|
|
||||||
else if((c == '"') || (c == QUERY_QUOTE))*tokType= JREAD_STRING;
|
|
||||||
else if((c >= '0') && (c <= '9')) *tokType= JREAD_NUMBER;
|
|
||||||
else if( c == '-') *tokType= JREAD_NUMBER;
|
|
||||||
else if( c == '{') *tokType= JREAD_OBJECT;
|
|
||||||
else if( c == '[') *tokType= JREAD_ARRAY;
|
|
||||||
else if( c == '}') *tokType= JREAD_EOBJECT;
|
|
||||||
else if( c == ']') *tokType= JREAD_EARRAY;
|
|
||||||
else if((c == 't') || (c == 'f')) *tokType= JREAD_BOOL;
|
|
||||||
else if( c == 'n') *tokType= JREAD_NULL;
|
|
||||||
else if( c == ':') *tokType= JREAD_COLON;
|
|
||||||
else if( c == ',') *tokType= JREAD_COMMA;
|
|
||||||
else if( c == '*') *tokType= JREAD_QPARAM;
|
|
||||||
else *tokType= JREAD_ERROR;
|
|
||||||
return sp;
|
|
||||||
};
|
|
||||||
|
|
||||||
// jReadGetString
|
|
||||||
// - assumes next element is "string" which may include "\" sequences
|
|
||||||
// - returns pointer to -------------^
|
|
||||||
// - pElem contains result ( JREAD_STRING, length, pointer to string)
|
|
||||||
// - pass quote = '"' for Json, quote = '\'' for query scanning
|
|
||||||
//
|
|
||||||
// returns: pointer into pJson after the string (char after the " terminator)
|
|
||||||
// pElem contains pointer and length of string (or dataType=JREAD_ERROR)
|
|
||||||
//
|
|
||||||
char * jReadGetString( char *pJson, struct jReadElement *pElem, char quote )
|
|
||||||
{
|
|
||||||
short skipch;
|
|
||||||
pElem->dataType= JREAD_ERROR;
|
|
||||||
pElem->elements= 1;
|
|
||||||
pElem->bytelen= 0;
|
|
||||||
pJson= jReadSkipWhitespace( pJson );
|
|
||||||
if( *pJson == quote )
|
|
||||||
{
|
|
||||||
pJson++;
|
|
||||||
pElem->pValue= pJson; // -> start of actual string
|
|
||||||
pElem->bytelen=0;
|
|
||||||
skipch= 0;
|
|
||||||
while( *pJson != '\0' )
|
|
||||||
{
|
|
||||||
if( skipch )
|
|
||||||
skipch= 0;
|
|
||||||
else if( *pJson == '\\' ) // "\" sequence
|
|
||||||
skipch= 1;
|
|
||||||
else if( *pJson == quote )
|
|
||||||
{
|
|
||||||
pElem->dataType= JREAD_STRING;
|
|
||||||
pJson++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pElem->bytelen++;
|
|
||||||
pJson++;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
return pJson;
|
|
||||||
};
|
|
||||||
|
|
||||||
// jReadTextLen
|
|
||||||
// - used to identify length of element text
|
|
||||||
// - returns no. of chars from pJson upto a terminator
|
|
||||||
// - terminators: ' ' , } ]
|
|
||||||
//
|
|
||||||
int jReadTextLen( char *pJson )
|
|
||||||
{
|
|
||||||
int len= 0;
|
|
||||||
while( (*pJson > ' ' ) && // any ctrl char incl '\0'
|
|
||||||
(*pJson != ',' ) &&
|
|
||||||
(*pJson != '}' ) &&
|
|
||||||
(*pJson != ']' ) )
|
|
||||||
{
|
|
||||||
len++;
|
|
||||||
pJson++;
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
// compare two json elements
|
|
||||||
// returns: 0 if they are identical strings, else 1
|
|
||||||
//
|
|
||||||
int jReadStrcmp( struct jReadElement *j1, struct jReadElement *j2 )
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
if( (j1->dataType != JREAD_STRING) ||
|
|
||||||
(j2->dataType != JREAD_STRING) ||
|
|
||||||
(j1->bytelen != j2->bytelen ) )
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
for( i=0; i< j1->bytelen; i++ )
|
|
||||||
if( ((char *)(j1->pValue))[i] != ((char *)(j2->pValue))[i] )
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// read unsigned int from string
|
|
||||||
char * jRead_atoi( char *p, unsigned int *result )
|
|
||||||
{
|
|
||||||
unsigned int x = 0;
|
|
||||||
while (*p >= '0' && *p <= '9') {
|
|
||||||
x = (x*10) + (*p - '0');
|
|
||||||
++p;
|
|
||||||
}
|
|
||||||
*result= x;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
// read long int from string
|
|
||||||
//
|
|
||||||
char * jRead_atol( char *p, long *result )
|
|
||||||
{
|
|
||||||
long x = 0;
|
|
||||||
int neg = 0;
|
|
||||||
if (*p == '-') {
|
|
||||||
neg = 1;
|
|
||||||
++p;
|
|
||||||
}
|
|
||||||
while (*p >= '0' && *p <= '9') {
|
|
||||||
x = (x*10) + (*p - '0');
|
|
||||||
++p;
|
|
||||||
}
|
|
||||||
if (neg) {
|
|
||||||
x = -x;
|
|
||||||
}
|
|
||||||
*result= x;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#define valid_digit(c) ((c) >= '0' && (c) <= '9')
|
|
||||||
|
|
||||||
// read double from string
|
|
||||||
// *CAUTION* does not handle exponents
|
|
||||||
//
|
|
||||||
//
|
|
||||||
char * jRead_atof( char *p, double *result)
|
|
||||||
{
|
|
||||||
double sign, value;
|
|
||||||
|
|
||||||
// Get sign, if any.
|
|
||||||
sign = 1.0;
|
|
||||||
if (*p == '-') {
|
|
||||||
sign = -1.0;
|
|
||||||
p += 1;
|
|
||||||
|
|
||||||
} else if (*p == '+') {
|
|
||||||
p += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get digits before decimal point or exponent, if any.
|
|
||||||
for (value = 0.0; valid_digit(*p); p += 1) {
|
|
||||||
value = value * 10.0 + (*p - '0');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get digits after decimal point, if any.
|
|
||||||
if (*p == '.') {
|
|
||||||
double pow10 = 10.0;
|
|
||||||
p += 1;
|
|
||||||
while (valid_digit(*p)) {
|
|
||||||
value += (*p - '0') / pow10;
|
|
||||||
pow10 *= 10.0;
|
|
||||||
p += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*result= sign * value;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
// read element into destination buffer and add '\0' terminator
|
|
||||||
// - always copies element irrespective of dataType (unless it's an error)
|
|
||||||
// - destBuffer is always '\0'-terminated (even on zero lenght returns)
|
|
||||||
// - returns pointer to destBuffer
|
|
||||||
//
|
|
||||||
char *jRead_strcpy( char *destBuffer, int destLength, struct jReadElement *pElement )
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int len= pElement->bytelen;
|
|
||||||
char *pdest= destBuffer;
|
|
||||||
char *psrc= (char *)pElement->pValue;
|
|
||||||
if( pElement->error == 0 )
|
|
||||||
{
|
|
||||||
if( len >= destLength )
|
|
||||||
len= destLength;
|
|
||||||
for( i=0; i<destLength; i++ )
|
|
||||||
*pdest++= *psrc++;
|
|
||||||
}
|
|
||||||
*pdest= '\0';
|
|
||||||
return destBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// jReadCountObject
|
|
||||||
// - used when query ends at an object, we want to return the object length
|
|
||||||
// - on entry pJson -> "{... "
|
|
||||||
// - used to skip unwanted values which are objects
|
|
||||||
// - keyIndex normally passed as -1 unless we're looking for the nth "key" value
|
|
||||||
// in which case keyIndex is the index of the key we want
|
|
||||||
//
|
|
||||||
char * jReadCountObject( char *pJson, struct jReadElement *pResult, int keyIndex )
|
|
||||||
{
|
|
||||||
struct jReadElement jElement;
|
|
||||||
int jTok;
|
|
||||||
char *sp;
|
|
||||||
pResult->dataType= JREAD_OBJECT;
|
|
||||||
pResult->error= 0;
|
|
||||||
pResult->elements= 0;
|
|
||||||
pResult->pValue= pJson;
|
|
||||||
sp= jReadFindTok( pJson+1, &jTok ); // check for empty object
|
|
||||||
if( jTok == JREAD_EOBJECT )
|
|
||||||
{
|
|
||||||
pJson= sp+1;
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
while( 1 )
|
|
||||||
{
|
|
||||||
pJson= jReadGetString( ++pJson, &jElement, '\"' );
|
|
||||||
if( jElement.dataType != JREAD_STRING )
|
|
||||||
{
|
|
||||||
pResult->error= 3; // Expected "key"
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if( pResult->elements == keyIndex ) // if passed keyIndex
|
|
||||||
{
|
|
||||||
*pResult= jElement; // we return "key" at this index
|
|
||||||
pResult->dataType= JREAD_KEY;
|
|
||||||
return pJson;
|
|
||||||
}
|
|
||||||
pJson= jReadFindTok( pJson, &jTok );
|
|
||||||
if( jTok != JREAD_COLON )
|
|
||||||
{
|
|
||||||
pResult->error= 4; // Expected ":"
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pJson= jRead( ++pJson, "", &jElement );
|
|
||||||
if( pResult->error )
|
|
||||||
break;
|
|
||||||
pJson= jReadFindTok( pJson, &jTok );
|
|
||||||
pResult->elements++;
|
|
||||||
if( jTok == JREAD_EOBJECT )
|
|
||||||
{
|
|
||||||
pJson++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if( jTok != JREAD_COMMA )
|
|
||||||
{
|
|
||||||
pResult->error= 6; // Expected "," in object
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( keyIndex >= 0 )
|
|
||||||
{
|
|
||||||
// we wanted a "key" value - that we didn't find
|
|
||||||
pResult->dataType= JREAD_ERROR;
|
|
||||||
pResult->error= 11; // Object key not found (bad index)
|
|
||||||
}else{
|
|
||||||
pResult->bytelen= pJson - (char *)pResult->pValue;
|
|
||||||
}
|
|
||||||
return pJson;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// jReadCountArray
|
|
||||||
// - used when query ends at an array, we want to return the array length
|
|
||||||
// - on entry pJson -> "[... "
|
|
||||||
// - used to skip unwanted values which are arrays
|
|
||||||
//
|
|
||||||
char * jReadCountArray( char *pJson, struct jReadElement *pResult )
|
|
||||||
{
|
|
||||||
struct jReadElement jElement;
|
|
||||||
int jTok;
|
|
||||||
char *sp;
|
|
||||||
pResult->dataType= JREAD_ARRAY;
|
|
||||||
pResult->error= 0;
|
|
||||||
pResult->elements= 0;
|
|
||||||
pResult->pValue= pJson;
|
|
||||||
sp= jReadFindTok( pJson+1, &jTok ); // check for empty array
|
|
||||||
if( jTok == JREAD_EARRAY )
|
|
||||||
{
|
|
||||||
pJson= sp+1;
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
while( 1 )
|
|
||||||
{
|
|
||||||
pJson= jRead( ++pJson, "", &jElement ); // array value
|
|
||||||
if( pResult->error )
|
|
||||||
break;
|
|
||||||
pJson= jReadFindTok( pJson, &jTok ); // , or ]
|
|
||||||
pResult->elements++;
|
|
||||||
if( jTok == JREAD_EARRAY )
|
|
||||||
{
|
|
||||||
pJson++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if( jTok != JREAD_COMMA )
|
|
||||||
{
|
|
||||||
pResult->error= 9; // Expected "," in array
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pResult->bytelen= pJson - (char *)pResult->pValue;
|
|
||||||
return pJson;
|
|
||||||
}
|
|
||||||
|
|
||||||
// jReadArrayStep()
|
|
||||||
// - reads one value from an array
|
|
||||||
// - assumes pJsonArray points at the start of an array or array element
|
|
||||||
//
|
|
||||||
char *jReadArrayStep( char *pJsonArray, struct jReadElement *pResult )
|
|
||||||
{
|
|
||||||
int jTok;
|
|
||||||
|
|
||||||
pJsonArray= jReadFindTok( pJsonArray, &jTok );
|
|
||||||
switch( jTok )
|
|
||||||
{
|
|
||||||
case JREAD_ARRAY: // start of array
|
|
||||||
case JREAD_COMMA: // element separator
|
|
||||||
return jRead( ++pJsonArray, "", pResult );
|
|
||||||
|
|
||||||
case JREAD_EARRAY: // end of array
|
|
||||||
pResult->error= 13; // End of array found
|
|
||||||
break;
|
|
||||||
default: // some other error
|
|
||||||
pResult->error= 9; // expected comma in array
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pResult->dataType= JREAD_ERROR;
|
|
||||||
return pJsonArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// jRead
|
|
||||||
// - reads a complete JSON <value>
|
|
||||||
// - matches pQuery against pJson, results in pResult
|
|
||||||
// returns: pointer into pJson
|
|
||||||
//
|
|
||||||
// Note: is recursive
|
|
||||||
//
|
|
||||||
char * jRead( char *pJson, char *pQuery, struct jReadElement *pResult )
|
|
||||||
{
|
|
||||||
return jReadParam( pJson, pQuery, pResult, NULL );
|
|
||||||
}
|
|
||||||
|
|
||||||
char * jReadParam( char *pJson, char *pQuery, struct jReadElement *pResult, int *queryParams )
|
|
||||||
{
|
|
||||||
int qTok, jTok, bytelen;
|
|
||||||
unsigned int index, count;
|
|
||||||
struct jReadElement qElement, jElement;
|
|
||||||
|
|
||||||
pJson= jReadFindTok( pJson, &jTok );
|
|
||||||
pQuery= jReadFindTok( pQuery, &qTok );
|
|
||||||
|
|
||||||
pResult->dataType= jTok;
|
|
||||||
pResult->bytelen= pResult->elements= pResult->error= 0;
|
|
||||||
pResult->pValue= pJson;
|
|
||||||
|
|
||||||
if( (qTok != JREAD_EOL) && (qTok != jTok) )
|
|
||||||
{
|
|
||||||
pResult->error= 1; // JSON does not match Query
|
|
||||||
return pJson;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch( jTok )
|
|
||||||
{
|
|
||||||
case JREAD_ERROR: // general error, eof etc.
|
|
||||||
pResult->error= 2; // Error reading JSON value
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JREAD_OBJECT: // "{"
|
|
||||||
if( qTok == JREAD_EOL )
|
|
||||||
return jReadCountObject( pJson, pResult, -1 ); // return length of object
|
|
||||||
|
|
||||||
pQuery= jReadFindTok( ++pQuery, &qTok ); // "('key'...", "{NUMBER", "{*" or EOL
|
|
||||||
if( qTok != JREAD_STRING )
|
|
||||||
{
|
|
||||||
index= 0;
|
|
||||||
switch( qTok )
|
|
||||||
{
|
|
||||||
case JREAD_NUMBER:
|
|
||||||
pQuery= jRead_atoi( (char *)pQuery, &index ); // index value
|
|
||||||
break;
|
|
||||||
case JREAD_QPARAM:
|
|
||||||
pQuery++;
|
|
||||||
index= (queryParams != NULL) ? *queryParams++ : 0; // substitute parameter
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
pResult->error= 12; // Bad Object key
|
|
||||||
return pJson;
|
|
||||||
}
|
|
||||||
return jReadCountObject( pJson, pResult, index );
|
|
||||||
}
|
|
||||||
|
|
||||||
pQuery= jReadGetString( pQuery, &qElement, QUERY_QUOTE ); // qElement = query 'key'
|
|
||||||
//
|
|
||||||
// read <key> : <value> , ... }
|
|
||||||
// loop 'til key matched
|
|
||||||
//
|
|
||||||
while( 1 )
|
|
||||||
{
|
|
||||||
pJson= jReadGetString( ++pJson, &jElement, '\"' );
|
|
||||||
if( jElement.dataType != JREAD_STRING )
|
|
||||||
{
|
|
||||||
pResult->error= 3; // Expected "key"
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pJson= jReadFindTok( pJson, &jTok );
|
|
||||||
if( jTok != JREAD_COLON )
|
|
||||||
{
|
|
||||||
pResult->error= 4; // Expected ":"
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// compare object keys
|
|
||||||
if( jReadStrcmp( &qElement, &jElement ) == 0 )
|
|
||||||
{
|
|
||||||
// found object key
|
|
||||||
return jReadParam( ++pJson, pQuery, pResult, queryParams );
|
|
||||||
}
|
|
||||||
// no key match... skip this value
|
|
||||||
pJson= jRead( ++pJson, "", pResult );
|
|
||||||
pJson= jReadFindTok( pJson, &jTok );
|
|
||||||
if( jTok == JREAD_EOBJECT )
|
|
||||||
{
|
|
||||||
pResult->error= 5; // Object key not found
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if( jTok != JREAD_COMMA )
|
|
||||||
{
|
|
||||||
pResult->error= 6; // Expected "," in object
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case JREAD_ARRAY: // "[NUMBER" or "[*"
|
|
||||||
//
|
|
||||||
// read index, skip values 'til index
|
|
||||||
//
|
|
||||||
if( qTok == JREAD_EOL )
|
|
||||||
return jReadCountArray( pJson, pResult ); // return length of object
|
|
||||||
|
|
||||||
index= 0;
|
|
||||||
pQuery= jReadFindTok( ++pQuery, &qTok ); // "[NUMBER" or "[*"
|
|
||||||
if( qTok == JREAD_NUMBER )
|
|
||||||
{
|
|
||||||
pQuery= jRead_atoi( pQuery, &index ); // get array index
|
|
||||||
}else if( qTok == JREAD_QPARAM )
|
|
||||||
{
|
|
||||||
pQuery++;
|
|
||||||
index= (queryParams != NULL) ? *queryParams++ : 0; // substitute parameter
|
|
||||||
}
|
|
||||||
|
|
||||||
count=0;
|
|
||||||
while( 1 )
|
|
||||||
{
|
|
||||||
if( count == index )
|
|
||||||
return jReadParam( ++pJson, pQuery, pResult, queryParams ); // return value at index
|
|
||||||
// not this index... skip this value
|
|
||||||
pJson= jRead( ++pJson, "", &jElement );
|
|
||||||
if( pResult->error )
|
|
||||||
break;
|
|
||||||
count++;
|
|
||||||
pJson= jReadFindTok( pJson, &jTok ); // , or ]
|
|
||||||
if( jTok == JREAD_EARRAY )
|
|
||||||
{
|
|
||||||
pResult->error= 10; // Array element not found (bad index)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if( jTok != JREAD_COMMA )
|
|
||||||
{
|
|
||||||
pResult->error= 9; // Expected "," in array
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case JREAD_STRING: // "string"
|
|
||||||
pJson= jReadGetString( pJson, pResult, '\"' );
|
|
||||||
break;
|
|
||||||
case JREAD_NUMBER: // number (may be -ve) int or float
|
|
||||||
case JREAD_BOOL: // true or false
|
|
||||||
case JREAD_NULL: // null
|
|
||||||
bytelen= jReadTextLen( pJson );
|
|
||||||
pResult->dataType= jTok;
|
|
||||||
pResult->bytelen= bytelen;
|
|
||||||
pResult->pValue= pJson;
|
|
||||||
pResult->elements= 1;
|
|
||||||
pJson += bytelen;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
pResult->error= 8; // unexpected character (in pResult->dataType)
|
|
||||||
}
|
|
||||||
// We get here on a 'terminal value'
|
|
||||||
// - make sure the query string is empty also
|
|
||||||
pQuery= jReadFindTok( pQuery, &qTok );
|
|
||||||
if( !pResult->error && (qTok != JREAD_EOL) )
|
|
||||||
pResult->error= 7; // terminal value found before end of query
|
|
||||||
if( pResult->error )
|
|
||||||
{
|
|
||||||
pResult->dataType= JREAD_ERROR;
|
|
||||||
pResult->elements= pResult->bytelen= 0;
|
|
||||||
pResult->pValue= pJson; // return pointer into JSON at error point
|
|
||||||
}
|
|
||||||
return pJson;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------
|
|
||||||
// Optional helper functions
|
|
||||||
// - simple routines to extract values from JSON
|
|
||||||
// - does coercion of types where possible
|
|
||||||
// - always returns a value (e.g. 0 or "" on error)
|
|
||||||
//
|
|
||||||
// Note: by default, pass NULL for queryParams
|
|
||||||
// unless you are using '*' in the query for indexing
|
|
||||||
//
|
|
||||||
|
|
||||||
// jRead_long
|
|
||||||
// - reads signed long value from JSON
|
|
||||||
// - returns number from NUMBER or STRING elements (if possible)
|
|
||||||
// returns 1 or 0 from BOOL elements
|
|
||||||
// otherwise returns 0
|
|
||||||
//
|
|
||||||
long jRead_long( char *pJson, char *pQuery, int *queryParams )
|
|
||||||
{
|
|
||||||
struct jReadElement elem;
|
|
||||||
long result;
|
|
||||||
jReadParam( pJson, pQuery, &elem, queryParams );
|
|
||||||
if( (elem.dataType == JREAD_ERROR) || (elem.dataType == JREAD_NULL))
|
|
||||||
return 0;
|
|
||||||
if( elem.dataType == JREAD_BOOL )
|
|
||||||
return *((char *)elem.pValue)=='t' ? 1 : 0;
|
|
||||||
|
|
||||||
jRead_atol( (char *)elem.pValue, &result );
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int jRead_int( char *pJson, char *pQuery, int *queryParams )
|
|
||||||
{
|
|
||||||
return (int)jRead_long( pJson, pQuery, queryParams );
|
|
||||||
}
|
|
||||||
|
|
||||||
// jRead_double
|
|
||||||
// - returns double from JSON
|
|
||||||
// - returns number from NUMBER or STRING elements
|
|
||||||
// otherwise returns 0.0
|
|
||||||
//
|
|
||||||
double jRead_double( char *pJson, char *pQuery, int *queryParams )
|
|
||||||
{
|
|
||||||
struct jReadElement elem;
|
|
||||||
double result;
|
|
||||||
jReadParam( pJson, pQuery, &elem, queryParams );
|
|
||||||
if( elem.dataType == JREAD_ERROR )
|
|
||||||
return 0.0;
|
|
||||||
jRead_atof( (char *)elem.pValue, &result );
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// jRead_string
|
|
||||||
// Copy string to pDest and '\0'-terminate it (upto destlen total bytes)
|
|
||||||
// returns: character length of string (excluding '\0' terminator)
|
|
||||||
//
|
|
||||||
// Note: any element can be returned as a string
|
|
||||||
//
|
|
||||||
int jRead_string( char *pJson, char *pQuery, char *pDest, int destlen, int *queryParams )
|
|
||||||
{
|
|
||||||
struct jReadElement elem;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
*pDest= '\0';
|
|
||||||
jReadParam( pJson, pQuery, &elem, queryParams );
|
|
||||||
if( elem.dataType == JREAD_ERROR )
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
for( i=0; (i<elem.bytelen) && (i<destlen-1); i++ )
|
|
||||||
*pDest++ = ((char *)elem.pValue)[i];
|
|
||||||
*pDest= '\0';
|
|
||||||
return elem.bytelen;
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------
|
|
||||||
// Optional String output Functions
|
|
||||||
//
|
|
||||||
char *jReadTypeStrings[]={
|
|
||||||
"Error", // 0
|
|
||||||
"Object", // 1
|
|
||||||
"Array", // 2
|
|
||||||
"String", // 3
|
|
||||||
"Number", // 4
|
|
||||||
"Bool", // 5
|
|
||||||
"null", // 6
|
|
||||||
"Object key", // 7
|
|
||||||
"colon", // 8
|
|
||||||
"eol", // 9
|
|
||||||
"comma", // 10
|
|
||||||
"}", // 11
|
|
||||||
"]", // 12
|
|
||||||
"* parameter" // 13
|
|
||||||
};
|
|
||||||
|
|
||||||
char *jReadTypeToString( int dataType )
|
|
||||||
{
|
|
||||||
return jReadTypeStrings[ dataType ];
|
|
||||||
};
|
|
||||||
|
|
||||||
char * jReadErrorStrings[]={
|
|
||||||
"Ok", // 0
|
|
||||||
"JSON does not match Query", // 1
|
|
||||||
"Error reading JSON value", // 2
|
|
||||||
"Expected \"key\"", // 3
|
|
||||||
"Expected ':'", // 4
|
|
||||||
"Object key not found", // 5
|
|
||||||
"Expected ',' in object", // 6
|
|
||||||
"Terminal value found before end of query", // 7
|
|
||||||
"Unexpected character", // 8
|
|
||||||
"Expected ',' in array", // 9
|
|
||||||
"Array element not found (bad index)", // 10
|
|
||||||
"Object key not found (bad index)", // 11
|
|
||||||
"Bad object key", // 12
|
|
||||||
"End of array found", // 13
|
|
||||||
"End of object found" // 14
|
|
||||||
};
|
|
||||||
char * jReadErrorToString( int error )
|
|
||||||
{
|
|
||||||
if( (error >=0 ) && (error <= 14))
|
|
||||||
return jReadErrorStrings[ error ];
|
|
||||||
return "Unknown error";
|
|
||||||
};
|
|
||||||
|
|
||||||
// end of jRead.c
|
|
||||||
128
extlibs/jRead.h
128
extlibs/jRead.h
|
|
@ -1,128 +0,0 @@
|
||||||
// jRead.h
|
|
||||||
//
|
|
||||||
// see jRead.c for more information
|
|
||||||
//
|
|
||||||
|
|
||||||
// uncomment this if you really want to use double quotes in query strings instead of '
|
|
||||||
//#define JREAD_DOUBLE_QUOTE_IN_QUERY
|
|
||||||
|
|
||||||
//
|
|
||||||
// return dataTypes:
|
|
||||||
#define JREAD_ERROR 0 // general error, eof etc.
|
|
||||||
#define JREAD_OBJECT 1 // "{"
|
|
||||||
#define JREAD_ARRAY 2 // "["
|
|
||||||
#define JREAD_STRING 3 // "string"
|
|
||||||
#define JREAD_NUMBER 4 // number (may be -ve) int or float
|
|
||||||
#define JREAD_BOOL 5 // true or false
|
|
||||||
#define JREAD_NULL 6 // null
|
|
||||||
#define JREAD_KEY 7 // object "key"
|
|
||||||
// internal values:
|
|
||||||
#define JREAD_COLON 8 // ":"
|
|
||||||
#define JREAD_EOL 9 // end of input string (ptr at '\0')
|
|
||||||
#define JREAD_COMMA 10 // ","
|
|
||||||
#define JREAD_EOBJECT 11 // "}"
|
|
||||||
#define JREAD_EARRAY 12 // "]"
|
|
||||||
#define JREAD_QPARAM 13 // "*" query string parameter
|
|
||||||
|
|
||||||
//------------------------------------------------------
|
|
||||||
// jReadElement
|
|
||||||
// - structure to return JSON elements
|
|
||||||
// - error=0 for valid returns
|
|
||||||
//
|
|
||||||
// *NOTES*
|
|
||||||
// the returned pValue pointer points into the passed JSON
|
|
||||||
// string returns are not '\0' terminated.
|
|
||||||
// bytelen specifies the length of the returned data pointed to by pValue
|
|
||||||
//
|
|
||||||
struct jReadElement{
|
|
||||||
int dataType; // one of JREAD_...
|
|
||||||
int elements; // number of elements (e.g. elements in array or object)
|
|
||||||
int bytelen; // byte length of element (e.g. length of string, array text "[ ... ]" etc.)
|
|
||||||
void * pValue; // pointer to value string in JSON text
|
|
||||||
int error; // error value if dataType == JREAD_ERROR
|
|
||||||
};
|
|
||||||
|
|
||||||
//------------------------------------------------------
|
|
||||||
// The JSON reader function
|
|
||||||
//
|
|
||||||
// - reads a '\0'-terminated JSON text string from pJson
|
|
||||||
// - traverses the JSON according to the pQuery string
|
|
||||||
// - returns the result value in pResult
|
|
||||||
//
|
|
||||||
// returns: pointer into pJson after the queried value
|
|
||||||
//
|
|
||||||
// e.g.
|
|
||||||
// With JSON like: "{ ..., "key":"value", ... }"
|
|
||||||
//
|
|
||||||
// jRead( pJson, "{'key'", &result );
|
|
||||||
// returns with:
|
|
||||||
// result.dataType= JREAD_STRING, result.pValue->'value', result.bytelen=5
|
|
||||||
//
|
|
||||||
char * jRead( char *pJson, char *pQuery, struct jReadElement *pResult );
|
|
||||||
|
|
||||||
// version of jRead which allows one or more queryParam integers to be substituted
|
|
||||||
// for array or object indexes marked by a '*' in the query
|
|
||||||
//
|
|
||||||
// e.g. jReadParam( pJson, "[*", &resultElement, &arrayIndex );
|
|
||||||
//
|
|
||||||
// *!* CAUTION *!*
|
|
||||||
// You can supply an array of integers which are indexed for each '*' in pQuery
|
|
||||||
// however, horrid things will happen if you don't supply enough parameters
|
|
||||||
//
|
|
||||||
char * jReadParam( char *pJson, char *pQuery, struct jReadElement *pResult, int *queryParams );
|
|
||||||
|
|
||||||
// Array Stepping function
|
|
||||||
// - assumes pJsonArray is JSON source of an array "[ ... ]"
|
|
||||||
// - returns next element of the array in pResult
|
|
||||||
// - returns pointer to end of element, to be passed to next call of jReadArrayStep()
|
|
||||||
// - if end of array is encountered, pResult->error = 13 "End of array found"
|
|
||||||
//
|
|
||||||
// e.g.
|
|
||||||
// With JSON like: "{ ... "arrayInObject":[ elem1,elem2,... ], ... }"
|
|
||||||
//
|
|
||||||
// pJson= jRead( pJson, "{'arrayInObject'", &theArray );
|
|
||||||
// if( theArray.dataType == JREAD_ARRAY )
|
|
||||||
// {
|
|
||||||
// char *pArray= (char *)theArray.pValue;
|
|
||||||
// jReadElement arrayElement;
|
|
||||||
// int index;
|
|
||||||
// for( index=0; index < theArray.elements; index++ )
|
|
||||||
// {
|
|
||||||
// pArray= jReadArrayStep( pArray, &arrayElement );
|
|
||||||
// ...
|
|
||||||
//
|
|
||||||
// Note: this significantly speeds up traversing arrays.
|
|
||||||
//
|
|
||||||
char *jReadArrayStep( char *pJsonArray, struct jReadElement *pResult );
|
|
||||||
|
|
||||||
|
|
||||||
#define EXPORT_OPTIONAL_FUNCTIONS
|
|
||||||
#ifdef EXPORT_OPTIONAL_FUNCTIONS
|
|
||||||
|
|
||||||
//------------------------------------------------------
|
|
||||||
// Optional Helper Functions
|
|
||||||
//
|
|
||||||
long jRead_long( char *pJson, char *pQuery, int *queryParams );
|
|
||||||
int jRead_int( char *pJson, char *pQuery, int *queryParams );
|
|
||||||
double jRead_double( char *pJson, char *pQuery, int *queryParams );
|
|
||||||
int jRead_string( char *pJson, char *pQuery, char *pDest, int destlen, int *queryParams );
|
|
||||||
|
|
||||||
//------------------------------------------------------
|
|
||||||
// Optional String output Functions
|
|
||||||
//
|
|
||||||
char *jReadTypeToString( int dataType ); // string describes dataType
|
|
||||||
char * jReadErrorToString( int error ); // string descibes error code
|
|
||||||
|
|
||||||
//------------------------------------------------------
|
|
||||||
// Other jRead utilities which may be useful...
|
|
||||||
//
|
|
||||||
char * jRead_atoi( char *p, unsigned int *result ); // string to unsigned int
|
|
||||||
char * jRead_atol( char *p, long *result ); // string to signed long
|
|
||||||
char * jRead_atof( char *p, double *result); // string to double (does not do exponents)
|
|
||||||
int jReadStrcmp( struct jReadElement *j1, struct jReadElement *j2 ); // compare STRING elements
|
|
||||||
|
|
||||||
// copy element to '\0'-terminated buffer
|
|
||||||
char * jRead_strcpy( char *destBuffer, int destLength, struct jReadElement *pElement );
|
|
||||||
|
|
||||||
#endif
|
|
||||||
// end of jRead.h
|
|
||||||
561
extlibs/jWrite.c
561
extlibs/jWrite.c
|
|
@ -1,561 +0,0 @@
|
||||||
//
|
|
||||||
// jWrite.c version 1v2
|
|
||||||
//
|
|
||||||
// A *really* simple JSON writer in C
|
|
||||||
//
|
|
||||||
// see: jWrite.h for info
|
|
||||||
//
|
|
||||||
// TonyWilk, Mar 2015
|
|
||||||
//
|
|
||||||
#define _CRT_SECURE_NO_WARNINGS // stop complaining about deprecated functions
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h> // memset()
|
|
||||||
|
|
||||||
#include <jWrite.h>
|
|
||||||
|
|
||||||
#include <stdint.h> // definintion of uint32_t, int32_t
|
|
||||||
//typedef unsigned int uint32_t;
|
|
||||||
//typedef int int32_t;
|
|
||||||
|
|
||||||
|
|
||||||
// the jWrite functions take the above jWriteControl structure pointer
|
|
||||||
// to maintain state while writing a JSON string.
|
|
||||||
//
|
|
||||||
// You can opt to use a single global instance of a jWriteControl structure
|
|
||||||
// which simplifies the function parameters or to supply your own structure
|
|
||||||
//
|
|
||||||
#ifdef JW_GLOBAL_CONTROL_STRUCT
|
|
||||||
struct jWriteControl g_jWriteControl; // global control struct
|
|
||||||
#define JWC_DECL // function parameter decl is empty
|
|
||||||
#define JWC_DECL0
|
|
||||||
#define JWC(x) g_jWriteControl.x // functions access global
|
|
||||||
#define JWC_PARAM // pointer to struct is empty
|
|
||||||
#define JWC_PARAM0
|
|
||||||
#else
|
|
||||||
#define JWC_DECL struct jWriteControl *jwc, // function parameter is ptr to control struct
|
|
||||||
#define JWC_DECL0 struct jWriteControl *jwc // function parameter, no params
|
|
||||||
#define JWC(x) jwc->x // functions use pointer
|
|
||||||
#define JWC_PARAM jwc, // pointer to stuct
|
|
||||||
#define JWC_PARAM0 jwc // pointer to stuct, no params
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//------------------------------------------
|
|
||||||
// Internal functions
|
|
||||||
//
|
|
||||||
void jwPutch( JWC_DECL char c );
|
|
||||||
void jwPutstr( JWC_DECL char *str );
|
|
||||||
void jwPutraw( JWC_DECL char *str );
|
|
||||||
void modp_itoa10(int32_t value, char* str);
|
|
||||||
void modp_dtoa2(double value, char* str, int prec);
|
|
||||||
void jwPretty( JWC_DECL0 );
|
|
||||||
enum jwNodeType jwPop( JWC_DECL0 );
|
|
||||||
void jwPush( JWC_DECL enum jwNodeType nodeType );
|
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------
|
|
||||||
// jwOpen
|
|
||||||
// - open writing of JSON starting with rootType = JW_OBJECT or JW_ARRAY
|
|
||||||
// - initialise with user string buffer of length buflen
|
|
||||||
// - isPretty=JW_PRETTY adds \n and spaces to prettify output (else JW_COMPACT)
|
|
||||||
//
|
|
||||||
void jwOpen( JWC_DECL char *buffer, unsigned int buflen,
|
|
||||||
enum jwNodeType rootType, int isPretty )
|
|
||||||
{
|
|
||||||
memset( buffer, 0, buflen ); // zap the whole destination buffer
|
|
||||||
JWC(buffer)= buffer;
|
|
||||||
JWC(buflen)= buflen;
|
|
||||||
JWC(bufp)= buffer;
|
|
||||||
JWC(nodeStack)[0].nodeType= rootType;
|
|
||||||
JWC(nodeStack)[0].elementNo= 0;
|
|
||||||
JWC(stackpos)=0;
|
|
||||||
JWC(error)= JWRITE_OK;
|
|
||||||
JWC(callNo)= 1;
|
|
||||||
JWC(isPretty)= isPretty;
|
|
||||||
jwPutch( JWC_PARAM (rootType==JW_OBJECT) ? '{' : '[' );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------
|
|
||||||
// jwClose
|
|
||||||
// - closes the root JSON object started by jwOpen()
|
|
||||||
// - returns error code
|
|
||||||
//
|
|
||||||
int jwClose( JWC_DECL0 )
|
|
||||||
{
|
|
||||||
if( JWC(error) == JWRITE_OK )
|
|
||||||
{
|
|
||||||
if( JWC(stackpos) == 0 )
|
|
||||||
{
|
|
||||||
enum jwNodeType node= JWC(nodeStack)[0].nodeType;
|
|
||||||
if( JWC(isPretty) )
|
|
||||||
jwPutch( JWC_PARAM '\n' );
|
|
||||||
jwPutch( JWC_PARAM (node == JW_OBJECT) ? '}' : ']');
|
|
||||||
}else{
|
|
||||||
JWC(error)= JWRITE_NEST_ERROR; // nesting error, not all objects closed when jwClose() called
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return JWC(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------
|
|
||||||
// End the current array/object
|
|
||||||
//
|
|
||||||
int jwEnd( JWC_DECL0 )
|
|
||||||
{
|
|
||||||
if( JWC(error) == JWRITE_OK )
|
|
||||||
{
|
|
||||||
enum jwNodeType node;
|
|
||||||
int lastElemNo= JWC(nodeStack)[JWC(stackpos)].elementNo;
|
|
||||||
node= jwPop( JWC_PARAM0 );
|
|
||||||
if( lastElemNo > 0 )
|
|
||||||
jwPretty( JWC_PARAM0 );
|
|
||||||
jwPutch( JWC_PARAM (node == JW_OBJECT) ? '}' : ']');
|
|
||||||
}
|
|
||||||
return JWC(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------
|
|
||||||
// jwErrorPos
|
|
||||||
// - Returns position of error: the nth call to a jWrite function
|
|
||||||
//
|
|
||||||
int jwErrorPos( JWC_DECL0 )
|
|
||||||
{
|
|
||||||
return JWC(callNo);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------
|
|
||||||
// Object insert functions
|
|
||||||
//
|
|
||||||
int _jwObj( JWC_DECL char *key );
|
|
||||||
|
|
||||||
// put raw string to object (i.e. contents of rawtext without quotes)
|
|
||||||
//
|
|
||||||
void jwObj_raw( JWC_DECL char *key, char *rawtext )
|
|
||||||
{
|
|
||||||
if(_jwObj( JWC_PARAM key ) == JWRITE_OK)
|
|
||||||
jwPutraw( JWC_PARAM rawtext);
|
|
||||||
}
|
|
||||||
|
|
||||||
// put "quoted" string to object
|
|
||||||
//
|
|
||||||
void jwObj_string( JWC_DECL char *key, char *value )
|
|
||||||
{
|
|
||||||
if(_jwObj( JWC_PARAM key ) == JWRITE_OK)
|
|
||||||
jwPutstr( JWC_PARAM value );
|
|
||||||
}
|
|
||||||
|
|
||||||
void jwObj_int( JWC_DECL char *key, int value )
|
|
||||||
{
|
|
||||||
modp_itoa10( value, JWC(tmpbuf) );
|
|
||||||
jwObj_raw( JWC_PARAM key, JWC(tmpbuf) );
|
|
||||||
}
|
|
||||||
|
|
||||||
void jwObj_double( JWC_DECL char *key, double value )
|
|
||||||
{
|
|
||||||
modp_dtoa2( value, JWC(tmpbuf), 6 );
|
|
||||||
jwObj_raw( JWC_PARAM key, JWC(tmpbuf) );
|
|
||||||
}
|
|
||||||
|
|
||||||
void jwObj_bool( JWC_DECL char *key, int oneOrZero )
|
|
||||||
{
|
|
||||||
jwObj_raw( JWC_PARAM key, (oneOrZero) ? "true" : "false" );
|
|
||||||
}
|
|
||||||
|
|
||||||
void jwObj_null( JWC_DECL char *key )
|
|
||||||
{
|
|
||||||
jwObj_raw( JWC_PARAM key, "null" );
|
|
||||||
}
|
|
||||||
|
|
||||||
// put Object in Object
|
|
||||||
//
|
|
||||||
void jwObj_object( JWC_DECL char *key )
|
|
||||||
{
|
|
||||||
if(_jwObj( JWC_PARAM key ) == JWRITE_OK)
|
|
||||||
{
|
|
||||||
jwPutch( JWC_PARAM '{' );
|
|
||||||
jwPush( JWC_PARAM JW_OBJECT );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// put Array in Object
|
|
||||||
//
|
|
||||||
void jwObj_array( JWC_DECL char *key )
|
|
||||||
{
|
|
||||||
if(_jwObj( JWC_PARAM key ) == JWRITE_OK)
|
|
||||||
{
|
|
||||||
jwPutch( JWC_PARAM '[' );
|
|
||||||
jwPush( JWC_PARAM JW_ARRAY );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------
|
|
||||||
// Array insert functions
|
|
||||||
//
|
|
||||||
int _jwArr( JWC_DECL0 );
|
|
||||||
|
|
||||||
// put raw string to array (i.e. contents of rawtext without quotes)
|
|
||||||
//
|
|
||||||
void jwArr_raw( JWC_DECL char *rawtext )
|
|
||||||
{
|
|
||||||
if(_jwArr( JWC_PARAM0 ) == JWRITE_OK)
|
|
||||||
jwPutraw( JWC_PARAM rawtext);
|
|
||||||
}
|
|
||||||
|
|
||||||
// put "quoted" string to array
|
|
||||||
//
|
|
||||||
void jwArr_string( JWC_DECL char *value )
|
|
||||||
{
|
|
||||||
if(_jwArr( JWC_PARAM0 ) == JWRITE_OK)
|
|
||||||
jwPutstr( JWC_PARAM value );
|
|
||||||
}
|
|
||||||
|
|
||||||
void jwArr_int( JWC_DECL int value )
|
|
||||||
{
|
|
||||||
modp_itoa10( value, JWC(tmpbuf) );
|
|
||||||
jwArr_raw( JWC_PARAM JWC(tmpbuf) );
|
|
||||||
}
|
|
||||||
|
|
||||||
void jwArr_double( JWC_DECL double value )
|
|
||||||
{
|
|
||||||
modp_dtoa2( value, JWC(tmpbuf), 6 );
|
|
||||||
jwArr_raw( JWC_PARAM JWC(tmpbuf) );
|
|
||||||
}
|
|
||||||
|
|
||||||
void jwArr_bool( JWC_DECL int oneOrZero )
|
|
||||||
{
|
|
||||||
jwArr_raw( JWC_PARAM (oneOrZero) ? "true" : "false" );
|
|
||||||
}
|
|
||||||
|
|
||||||
void jwArr_null( JWC_DECL0 )
|
|
||||||
{
|
|
||||||
jwArr_raw( JWC_PARAM "null" );
|
|
||||||
}
|
|
||||||
|
|
||||||
void jwArr_object( JWC_DECL0 )
|
|
||||||
{
|
|
||||||
if(_jwArr( JWC_PARAM0 ) == JWRITE_OK)
|
|
||||||
{
|
|
||||||
jwPutch( JWC_PARAM '{' );
|
|
||||||
jwPush( JWC_PARAM JW_OBJECT );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void jwArr_array( JWC_DECL0 )
|
|
||||||
{
|
|
||||||
if(_jwArr( JWC_PARAM0 ) == JWRITE_OK)
|
|
||||||
{
|
|
||||||
jwPutch( JWC_PARAM '[' );
|
|
||||||
jwPush( JWC_PARAM JW_ARRAY );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------
|
|
||||||
// jwErrorToString
|
|
||||||
// - returns string describing error code
|
|
||||||
//
|
|
||||||
char *jwErrorToString( int err )
|
|
||||||
{
|
|
||||||
switch( err )
|
|
||||||
{
|
|
||||||
case JWRITE_OK: return "OK";
|
|
||||||
case JWRITE_BUF_FULL: return "output buffer full";
|
|
||||||
case JWRITE_NOT_ARRAY: return "tried to write Array value into Object";
|
|
||||||
case JWRITE_NOT_OBJECT: return "tried to write Object key/value into Array";
|
|
||||||
case JWRITE_STACK_FULL: return "array/object nesting > JWRITE_STACK_DEPTH";
|
|
||||||
case JWRITE_STACK_EMPTY:return "stack underflow error (too many 'end's)";
|
|
||||||
case JWRITE_NEST_ERROR: return "nesting error, not all objects closed when jwClose() called";
|
|
||||||
}
|
|
||||||
return "Unknown error";
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
// Internal functions
|
|
||||||
//
|
|
||||||
void jwPretty( JWC_DECL0 )
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
if( JWC(isPretty) )
|
|
||||||
{
|
|
||||||
jwPutch( JWC_PARAM '\n' );
|
|
||||||
for( i=0; i<JWC(stackpos)+1; i++ )
|
|
||||||
jwPutraw( JWC_PARAM " " );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push / Pop node stack
|
|
||||||
//
|
|
||||||
void jwPush( JWC_DECL enum jwNodeType nodeType )
|
|
||||||
{
|
|
||||||
if( (JWC(stackpos)+1) >= JWRITE_STACK_DEPTH )
|
|
||||||
JWC(error)= JWRITE_STACK_FULL; // array/object nesting > JWRITE_STACK_DEPTH
|
|
||||||
else
|
|
||||||
{
|
|
||||||
JWC(nodeStack[++JWC(stackpos)]).nodeType= nodeType;
|
|
||||||
JWC(nodeStack[JWC(stackpos)]).elementNo= 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum jwNodeType jwPop( JWC_DECL0 )
|
|
||||||
{
|
|
||||||
enum jwNodeType retval= JWC(nodeStack[JWC(stackpos)]).nodeType;
|
|
||||||
if( JWC(stackpos) == 0 )
|
|
||||||
JWC(error)= JWRITE_STACK_EMPTY; // stack underflow error (too many 'end's)
|
|
||||||
else
|
|
||||||
JWC(stackpos)--;
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
void jwPutch( JWC_DECL char c )
|
|
||||||
{
|
|
||||||
if( (unsigned int)(JWC(bufp) - JWC(buffer)) >= JWC(buflen) )
|
|
||||||
{
|
|
||||||
JWC(error)= JWRITE_BUF_FULL;
|
|
||||||
}else{
|
|
||||||
*JWC(bufp)++ = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// put string enclosed in quotes
|
|
||||||
//
|
|
||||||
void jwPutstr( JWC_DECL char *str )
|
|
||||||
{
|
|
||||||
jwPutch( JWC_PARAM '\"' );
|
|
||||||
while( *str != '\0' )
|
|
||||||
jwPutch( JWC_PARAM *str++ );
|
|
||||||
jwPutch( JWC_PARAM '\"' );
|
|
||||||
}
|
|
||||||
|
|
||||||
// put raw string
|
|
||||||
//
|
|
||||||
void jwPutraw( JWC_DECL char *str )
|
|
||||||
{
|
|
||||||
while( *str != '\0' )
|
|
||||||
jwPutch( JWC_PARAM *str++ );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// *common Object function*
|
|
||||||
// - checks error
|
|
||||||
// - checks current node is OBJECT
|
|
||||||
// - adds comma if reqd
|
|
||||||
// - adds "key" :
|
|
||||||
//
|
|
||||||
int _jwObj( JWC_DECL char *key )
|
|
||||||
{
|
|
||||||
if(JWC(error) == JWRITE_OK)
|
|
||||||
{
|
|
||||||
JWC(callNo)++;
|
|
||||||
if( JWC(nodeStack)[JWC(stackpos)].nodeType != JW_OBJECT )
|
|
||||||
JWC(error)= JWRITE_NOT_OBJECT; // tried to write Object key/value into Array
|
|
||||||
else if( JWC(nodeStack)[JWC(stackpos)].elementNo++ > 0 )
|
|
||||||
jwPutch( JWC_PARAM ',' );
|
|
||||||
jwPretty( JWC_PARAM0 );
|
|
||||||
jwPutstr( JWC_PARAM key );
|
|
||||||
jwPutch( JWC_PARAM ':' );
|
|
||||||
if( JWC(isPretty) )
|
|
||||||
jwPutch( JWC_PARAM ' ' );
|
|
||||||
}
|
|
||||||
return JWC(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// *common Array function*
|
|
||||||
// - checks error
|
|
||||||
// - checks current node is ARRAY
|
|
||||||
// - adds comma if reqd
|
|
||||||
//
|
|
||||||
int _jwArr( JWC_DECL0 )
|
|
||||||
{
|
|
||||||
if(JWC(error) == JWRITE_OK)
|
|
||||||
{
|
|
||||||
JWC(callNo)++;
|
|
||||||
if( JWC(nodeStack)[JWC(stackpos)].nodeType != JW_ARRAY )
|
|
||||||
JWC(error)= JWRITE_NOT_ARRAY; // tried to write array value into Object
|
|
||||||
else if( JWC(nodeStack)[JWC(stackpos)].elementNo++ > 0 )
|
|
||||||
jwPutch( JWC_PARAM ',' );
|
|
||||||
jwPretty( JWC_PARAM0 );
|
|
||||||
}
|
|
||||||
return JWC(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
//=================================================================
|
|
||||||
//
|
|
||||||
// modp value-to-string functions
|
|
||||||
// - modified for C89
|
|
||||||
//
|
|
||||||
// We use these functions as they are a lot faster than sprintf()
|
|
||||||
//
|
|
||||||
// Origin of these routines:
|
|
||||||
/*
|
|
||||||
* <pre>
|
|
||||||
* Copyright © 2007, Nick Galbreath -- nickg [at] modp [dot] com
|
|
||||||
* All rights reserved.
|
|
||||||
* http://code.google.com/p/stringencoders/
|
|
||||||
* Released under the bsd license.
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void strreverse(char* begin, char* end)
|
|
||||||
{
|
|
||||||
char aux;
|
|
||||||
while (end > begin)
|
|
||||||
aux = *end, *end-- = *begin, *begin++ = aux;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** \brief convert an signed integer to char buffer
|
|
||||||
*
|
|
||||||
* \param[in] value
|
|
||||||
* \param[out] buf the output buffer. Should be 16 chars or more.
|
|
||||||
*/
|
|
||||||
void modp_itoa10(int32_t value, char* str)
|
|
||||||
{
|
|
||||||
char* wstr=str;
|
|
||||||
// Take care of sign
|
|
||||||
unsigned int uvalue = (value < 0) ? -value : value;
|
|
||||||
// Conversion. Number is reversed.
|
|
||||||
do *wstr++ = (char)(48 + (uvalue % 10)); while(uvalue /= 10);
|
|
||||||
if (value < 0) *wstr++ = '-';
|
|
||||||
*wstr='\0';
|
|
||||||
|
|
||||||
// Reverse string
|
|
||||||
strreverse(str,wstr-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Powers of 10
|
|
||||||
* 10^0 to 10^9
|
|
||||||
*/
|
|
||||||
static const double pow10[] = {1, 10, 100, 1000, 10000, 100000, 1000000,
|
|
||||||
10000000, 100000000, 1000000000};
|
|
||||||
|
|
||||||
/** \brief convert a floating point number to char buffer with a
|
|
||||||
* variable-precision format, and no trailing zeros
|
|
||||||
*
|
|
||||||
* This is similar to "%.[0-9]f" in the printf style, except it will
|
|
||||||
* NOT include trailing zeros after the decimal point. This type
|
|
||||||
* of format oddly does not exists with printf.
|
|
||||||
*
|
|
||||||
* If the input value is greater than 1<<31, then the output format
|
|
||||||
* will be switched exponential format.
|
|
||||||
*
|
|
||||||
* \param[in] value
|
|
||||||
* \param[out] buf The allocated output buffer. Should be 32 chars or more.
|
|
||||||
* \param[in] precision Number of digits to the right of the decimal point.
|
|
||||||
* Can only be 0-9.
|
|
||||||
*/
|
|
||||||
void modp_dtoa2(double value, char* str, int prec)
|
|
||||||
{
|
|
||||||
/* if input is larger than thres_max, revert to exponential */
|
|
||||||
const double thres_max = (double)(0x7FFFFFFF);
|
|
||||||
int count;
|
|
||||||
double diff = 0.0;
|
|
||||||
char* wstr = str;
|
|
||||||
int neg= 0;
|
|
||||||
int whole;
|
|
||||||
double tmp;
|
|
||||||
uint32_t frac;
|
|
||||||
|
|
||||||
/* Hacky test for NaN
|
|
||||||
* under -fast-math this won't work, but then you also won't
|
|
||||||
* have correct nan values anyways. The alternative is
|
|
||||||
* to link with libmath (bad) or hack IEEE double bits (bad)
|
|
||||||
*/
|
|
||||||
if (! (value == value)) {
|
|
||||||
str[0] = 'n'; str[1] = 'a'; str[2] = 'n'; str[3] = '\0';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prec < 0) {
|
|
||||||
prec = 0;
|
|
||||||
} else if (prec > 9) {
|
|
||||||
/* precision of >= 10 can lead to overflow errors */
|
|
||||||
prec = 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* we'll work in positive values and deal with the
|
|
||||||
negative sign issue later */
|
|
||||||
if (value < 0) {
|
|
||||||
neg = 1;
|
|
||||||
value = -value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
whole = (int) value;
|
|
||||||
tmp = (value - whole) * pow10[prec];
|
|
||||||
frac = (uint32_t)(tmp);
|
|
||||||
diff = tmp - frac;
|
|
||||||
|
|
||||||
if (diff > 0.5) {
|
|
||||||
++frac;
|
|
||||||
/* handle rollover, e.g. case 0.99 with prec 1 is 1.0 */
|
|
||||||
if (frac >= pow10[prec]) {
|
|
||||||
frac = 0;
|
|
||||||
++whole;
|
|
||||||
}
|
|
||||||
} else if (diff == 0.5 && ((frac == 0) || (frac & 1))) {
|
|
||||||
/* if halfway, round up if odd, OR
|
|
||||||
if last digit is 0. That last part is strange */
|
|
||||||
++frac;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* for very large numbers switch back to native sprintf for exponentials.
|
|
||||||
anyone want to write code to replace this? */
|
|
||||||
/*
|
|
||||||
normal printf behavior is to print EVERY whole number digit
|
|
||||||
which can be 100s of characters overflowing your buffers == bad
|
|
||||||
*/
|
|
||||||
if (value > thres_max) {
|
|
||||||
sprintf(str, "%e", neg ? -value : value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prec == 0) {
|
|
||||||
diff = value - whole;
|
|
||||||
if (diff > 0.5) {
|
|
||||||
/* greater than 0.5, round up, e.g. 1.6 -> 2 */
|
|
||||||
++whole;
|
|
||||||
} else if (diff == 0.5 && (whole & 1)) {
|
|
||||||
/* exactly 0.5 and ODD, then round up */
|
|
||||||
/* 1.5 -> 2, but 2.5 -> 2 */
|
|
||||||
++whole;
|
|
||||||
}
|
|
||||||
|
|
||||||
//vvvvvvvvvvvvvvvvvvv Diff from modp_dto2
|
|
||||||
} else if (frac) {
|
|
||||||
count = prec;
|
|
||||||
// now do fractional part, as an unsigned number
|
|
||||||
// we know it is not 0 but we can have leading zeros, these
|
|
||||||
// should be removed
|
|
||||||
while (!(frac % 10)) {
|
|
||||||
--count;
|
|
||||||
frac /= 10;
|
|
||||||
}
|
|
||||||
//^^^^^^^^^^^^^^^^^^^ Diff from modp_dto2
|
|
||||||
|
|
||||||
// now do fractional part, as an unsigned number
|
|
||||||
do {
|
|
||||||
--count;
|
|
||||||
*wstr++ = (char)(48 + (frac % 10));
|
|
||||||
} while (frac /= 10);
|
|
||||||
// add extra 0s
|
|
||||||
while (count-- > 0) *wstr++ = '0';
|
|
||||||
// add decimal
|
|
||||||
*wstr++ = '.';
|
|
||||||
}
|
|
||||||
|
|
||||||
// do whole part
|
|
||||||
// Take care of sign
|
|
||||||
// Conversion. Number is reversed.
|
|
||||||
do *wstr++ = (char)(48 + (whole % 10)); while (whole /= 10);
|
|
||||||
if (neg) {
|
|
||||||
*wstr++ = '-';
|
|
||||||
}
|
|
||||||
*wstr='\0';
|
|
||||||
strreverse(str, wstr-1);
|
|
||||||
}
|
|
||||||
//=================================================================
|
|
||||||
|
|
||||||
/* end of jWrite.c */
|
|
||||||
217
extlibs/jWrite.h
217
extlibs/jWrite.h
|
|
@ -1,217 +0,0 @@
|
||||||
//
|
|
||||||
// jWrite.h
|
|
||||||
//
|
|
||||||
// A *really* simple JSON writer in C (C89)
|
|
||||||
// - a collection of functions to generate JSON semi-automatically
|
|
||||||
//
|
|
||||||
// The idea is to simplify writing native C values into a JSON string and
|
|
||||||
// to provide some error trapping to ensure that the result is valid JSON.
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
// jwOpen( buffer, buflen, JW_OBJECT, JW_PRETTY ); // open root node as object
|
|
||||||
// jwObj_string( "key", "value" );
|
|
||||||
// jwObj_int( "int", 1 );
|
|
||||||
// jwObj_array( "anArray");
|
|
||||||
// jwArr_int( 0 );
|
|
||||||
// jwArr_int( 1 );
|
|
||||||
// jwArr_int( 2 );
|
|
||||||
// jwEnd();
|
|
||||||
// err= jwClose(); // close root object
|
|
||||||
//
|
|
||||||
// results in:
|
|
||||||
//
|
|
||||||
// {
|
|
||||||
// "key": "value",
|
|
||||||
// "int": 1,
|
|
||||||
// "anArray": [
|
|
||||||
// 0,
|
|
||||||
// 1,
|
|
||||||
// 2
|
|
||||||
// ]
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Note that jWrite handles string quoting and getting commas in the right place.
|
|
||||||
// If the sequence of calls is incorrect
|
|
||||||
// e.g.
|
|
||||||
// jwOpen( buffer, buflen, JW_OBJECT, 1 );
|
|
||||||
// jwObj_string( "key", "value" );
|
|
||||||
// jwArr_int( 0 );
|
|
||||||
// ...
|
|
||||||
//
|
|
||||||
// then the error code returned from jwClose() would indicate that you attempted to
|
|
||||||
// put an array element into an object (instead of a key:value pair)
|
|
||||||
// To locate the error, the supplied buffer has the JSON created upto the error point
|
|
||||||
// and a call to jwErrorPos() would return the function call at which the error occurred
|
|
||||||
// - in this case 3, the 3rd function call "jwArr_int(0)" is not correct at this point.
|
|
||||||
//
|
|
||||||
// The root JSON type can be JW_OBJECT or JW_ARRAY.
|
|
||||||
//
|
|
||||||
// For more information on each function, see the prototypes below.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// GLOBAL vs. Application-Supplied Control Structure
|
|
||||||
// -------------------------------------------------
|
|
||||||
// jWrite requires a jWriteControl structure to save the internal state.
|
|
||||||
// For many applications it is much simpler for this to be a global variable as
|
|
||||||
// used by the above examples.
|
|
||||||
//
|
|
||||||
// To use multiple instances of jWrite, an application has to supply unique instances
|
|
||||||
// of jWriteControl structures.
|
|
||||||
//
|
|
||||||
// This feature is enabled by commenting out the definition of JW_GLOBAL_CONTROL_STRUCT
|
|
||||||
//
|
|
||||||
// All the jWrite functions then take an additional parameter: a ptr to the structure
|
|
||||||
// e.g.
|
|
||||||
// struct jWriteControl jwc;
|
|
||||||
//
|
|
||||||
// jwOpen( &jwc, buffer, buflen, JW_OBJECT, 1 );
|
|
||||||
// jwObj_string( &jwc, "key", "value" );
|
|
||||||
// jwObj_int( &jwc, "int", 1 );
|
|
||||||
// jwObj_array( &jwc, "anArray");
|
|
||||||
// jwArr_int( &jwc, 0 );
|
|
||||||
// jwArr_int( &jwc, 1 );
|
|
||||||
// jwArr_int( &jwc, 2 );
|
|
||||||
// jwEnd( &jwc );
|
|
||||||
// err= jwClose( &jwc );
|
|
||||||
//
|
|
||||||
// - which is more flexible, but a pain to type in !
|
|
||||||
//
|
|
||||||
// TonyWilk, Mar 2015
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef JWRITE_H_
|
|
||||||
#define JWRITE_H_
|
|
||||||
|
|
||||||
|
|
||||||
#define JW_GLOBAL_CONTROL_STRUCT // <--- comment this out to use applic-supplied jWriteControl
|
|
||||||
#define JWRITE_STACK_DEPTH 32 // max nesting depth of objects/arrays
|
|
||||||
|
|
||||||
#define JW_COMPACT 0 // output string control for jwOpen()
|
|
||||||
#define JW_PRETTY 1 // pretty adds \n and indentation
|
|
||||||
|
|
||||||
enum jwNodeType{
|
|
||||||
JW_OBJECT= 1,
|
|
||||||
JW_ARRAY
|
|
||||||
};
|
|
||||||
|
|
||||||
struct jwNodeStack{
|
|
||||||
enum jwNodeType nodeType;
|
|
||||||
int elementNo;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct jWriteControl{
|
|
||||||
char *buffer; // pointer to application's buffer
|
|
||||||
unsigned int buflen; // length of buffer
|
|
||||||
char *bufp; // current write position in buffer
|
|
||||||
char tmpbuf[32]; // local buffer for int/double convertions
|
|
||||||
int error; // error code
|
|
||||||
int callNo; // API call on which error occurred
|
|
||||||
struct jwNodeStack nodeStack[JWRITE_STACK_DEPTH]; // stack of array/object nodes
|
|
||||||
int stackpos;
|
|
||||||
int isPretty; // 1= pretty output (inserts \n and spaces)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Error Codes
|
|
||||||
// -----------
|
|
||||||
#define JWRITE_OK 0
|
|
||||||
#define JWRITE_BUF_FULL 1 // output buffer full
|
|
||||||
#define JWRITE_NOT_ARRAY 2 // tried to write Array value into Object
|
|
||||||
#define JWRITE_NOT_OBJECT 3 // tried to write Object key/value into Array
|
|
||||||
#define JWRITE_STACK_FULL 4 // array/object nesting > JWRITE_STACK_DEPTH
|
|
||||||
#define JWRITE_STACK_EMPTY 5 // stack underflow error (too many 'end's)
|
|
||||||
#define JWRITE_NEST_ERROR 6 // nesting error, not all objects closed when jwClose() called
|
|
||||||
|
|
||||||
|
|
||||||
// API functions
|
|
||||||
// -------------
|
|
||||||
|
|
||||||
// Returns '\0'-termianted string describing the error (as returned by jwClose())
|
|
||||||
//
|
|
||||||
char *jwErrorToString( int err );
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef JW_GLOBAL_CONTROL_STRUCT /* USING GLOBAL g_jWriteControl */
|
|
||||||
|
|
||||||
// jwOpen
|
|
||||||
// - initialises jWrite with the application supplied 'buffer' of length 'buflen'
|
|
||||||
// in operation, the buffer will always contain a valid '\0'-terminated string
|
|
||||||
// - jWrite will not overrun the buffer (it returns an "output buffer full" error)
|
|
||||||
// - rootType is the base JSON type: JW_OBJECT or JW_ARRAY
|
|
||||||
// - isPretty controls 'prettifying' the output: JW_PRETTY or JW_COMPACT
|
|
||||||
void jwOpen( char *buffer, unsigned int buflen, enum jwNodeType rootType, int isPretty );
|
|
||||||
|
|
||||||
// jwClose
|
|
||||||
// - closes the element opened by jwOpen()
|
|
||||||
// - returns error code (0 = JWRITE_OK)
|
|
||||||
// - after an error, all following jWrite calls are skipped internally
|
|
||||||
// so the error code is for the first error detected
|
|
||||||
int jwClose( );
|
|
||||||
|
|
||||||
// jwErrorPos
|
|
||||||
// - if jwClose returned an error, this function returns the number of the jWrite function call
|
|
||||||
// which caused that error.
|
|
||||||
int jwErrorPos( );
|
|
||||||
|
|
||||||
// Object insertion functions
|
|
||||||
// - used to insert "key":"value" pairs into an object
|
|
||||||
//
|
|
||||||
void jwObj_string( char *key, char *value );
|
|
||||||
void jwObj_int( char *key, int value );
|
|
||||||
void jwObj_double( char *key, double value );
|
|
||||||
void jwObj_bool( char *key, int oneOrZero );
|
|
||||||
void jwObj_null( char *key );
|
|
||||||
void jwObj_object( char *key );
|
|
||||||
void jwObj_array( char *key );
|
|
||||||
|
|
||||||
// Array insertion functions
|
|
||||||
// - used to insert "value" elements into an array
|
|
||||||
//
|
|
||||||
void jwArr_string( char *value );
|
|
||||||
void jwArr_int( int value );
|
|
||||||
void jwArr_double( double value );
|
|
||||||
void jwArr_bool( int oneOrZero );
|
|
||||||
void jwArr_null( );
|
|
||||||
void jwArr_object( );
|
|
||||||
void jwArr_array( );
|
|
||||||
|
|
||||||
// jwEnd
|
|
||||||
// - defines the end of an Object or Array definition
|
|
||||||
int jwEnd( );
|
|
||||||
|
|
||||||
|
|
||||||
// these 'raw' routines write the JSON value as the contents of rawtext
|
|
||||||
// i.e. enclosing quotes are not added
|
|
||||||
// - use if your app. supplies its own value->string functions
|
|
||||||
//
|
|
||||||
void jwObj_raw( char *key, char *rawtext );
|
|
||||||
void jwArr_raw( char *rawtext );
|
|
||||||
|
|
||||||
#else /* JW_GLOBAL_CONTROL_STRUCT not defined */
|
|
||||||
// Same API functions with app-supplied control struct option
|
|
||||||
//
|
|
||||||
void jwOpen( struct jWriteControl *jwc, char *buffer, unsigned int buflen, enum jwNodeType rootType, int isPretty );
|
|
||||||
int jwClose( struct jWriteControl *jwc );
|
|
||||||
int jwErrorPos( struct jWriteControl *jwc );
|
|
||||||
void jwObj_string( struct jWriteControl *jwc, char *key, char *value );
|
|
||||||
void jwObj_int( struct jWriteControl *jwc, char *key, int value );
|
|
||||||
void jwObj_double( struct jWriteControl *jwc, char *key, double value );
|
|
||||||
void jwObj_bool( struct jWriteControl *jwc, char *key, int oneOrZero );
|
|
||||||
void jwObj_null( struct jWriteControl *jwc, char *key );
|
|
||||||
void jwObj_object( struct jWriteControl *jwc, char *key );
|
|
||||||
void jwObj_array( struct jWriteControl *jwc, char *key );
|
|
||||||
void jwArr_string( struct jWriteControl *jwc, char *value );
|
|
||||||
void jwArr_int( struct jWriteControl *jwc, int value );
|
|
||||||
void jwArr_double( struct jWriteControl *jwc, double value );
|
|
||||||
void jwArr_bool( struct jWriteControl *jwc, int oneOrZero );
|
|
||||||
void jwArr_null( struct jWriteControl *jwc );
|
|
||||||
void jwArr_object( struct jWriteControl *jwc );
|
|
||||||
void jwArr_array( struct jWriteControl *jwc );
|
|
||||||
int jwEnd( struct jWriteControl *jwc );
|
|
||||||
void jwObj_raw( struct jWriteControl *jwc, char *key, char *rawtext );
|
|
||||||
void jwArr_raw( struct jWriteControl *jwc, char *rawtext );
|
|
||||||
|
|
||||||
#endif /* JW_GLOBAL_CONTROL_STRUCT */
|
|
||||||
|
|
||||||
#endif /*JWRITE_H_ */
|
|
||||||
/* end of jWrite.h */
|
|
||||||
Loading…
Reference in New Issue
Block a user