Kawe Mazidjatari b3a68ed095 Add EABase, EAThread and DirtySDK to R5sdk
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
2024-04-05 18:29:03 +02:00

1230 lines
38 KiB
C

/*H*************************************************************************************/
/*!
\File jsonparse.c
\Description
Simple JSON parser.
\Copyright
Copyright (c) Electronic Arts 2012.
\Notes
Written by Greg Schaefer outside of EA for a personal project, but donated
back for a good cause.
The parse buffer contains a list of objects, consisting of an offset and size.
The offset is the offset of the object in the JSON buffer. The size is the
size of the object in the JSON buffer, except for arrays and objects, where it
is the size of the object in the parse buffer (used when skipping past an array
or object). Values are written as uint16_t, with progressive encoding employed
when required.
UNICODE support is limited to extracting ASCII characters that are encoded
as UNICODE.
\Todo
Implement support for getting real numbers
\Version 12/11/2012 (jbrookes) Added to DirtySDK, added some new functionality
*/
/*************************************************************************************H*/
/*** Include files *********************************************************************/
#include <string.h>
#include "DirtySDK/platform.h"
#include "DirtySDK/dirtysock/dirtylib.h"
#include "DirtySDK/dirtysock/dirtymem.h"
#include "DirtySDK/util/jsonparse.h"
/*** Defines ***************************************************************************/
/*** Type Definitions ******************************************************************/
/*** Variables *************************************************************************/
//! decode table to classify characters as whitespace, number, letter, bracket, brace, or invalid
static const uint8_t _strDecode[256] =
{
0,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', // 00..0f
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', // 10..1f
' ', 0,'"', 0, 0, 0, 0, 0, 0, 0, 0, 0,',','1', 0, 0, // 20..2f
'1','1','1','1','1','1','1','1','1','1',':', 0, 0, 0, 0, 0, // 30..3f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..4f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'[','\\',']', 0, 0, // 50..5f
' ','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a', // 60..6f
'a','a','a','a','a','a','a','a','a','a','a','{',0 ,'}', 0, 0, // 70..7f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80..8f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 90..9f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // a0..af
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // b0..bf
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // c0..cf
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // d0..df
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // e0..ef
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // f0..ff
};
//! characters that need to be escaped
static const uint8_t _strEscape[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..0f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10..1f
0, 0,'"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/', // 20..2f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 30..3f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..4f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50..5f
0, 0, 8, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 10, 0, // 60..6f
0, 0, 0, 0, 0,'u', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 70..7f
0, 0, 13, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80..8f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 90..9f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // a0..af
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // b0..bf
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // c0..cf
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // d0..df
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // e0..ef
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // f0..ff
};
//! hex->int decode table
static const uint8_t _HexDecode[256] =
{
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,128,128,128,128,128,128,
128, 10, 11, 12, 13, 14, 15,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128, 10, 11, 12, 13, 14, 15,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128
};
/*** Private Functions *****************************************************************/
/*F*************************************************************************************/
/*!
\Function _JsonGetQuoted
\Description
Get text from a quoted string
\Input *pDst - [out] storage for text
\Input iLim - size of output buffer
\Input *pSrc - pointer to quoted string
\Input iSkip - output buffer increment when writing (zero for length check)
\Output
int32_t - returns string length
\Version 12/13/2012 (jbrookes)
*/
/*************************************************************************************F*/
static int32_t _JsonGetQuoted(uint8_t *pDst, int32_t iLim, const uint8_t *pSrc, int32_t iSkip)
{
int32_t iLen = 0;
uint8_t c;
// make room for terminator
iLim -= 1;
// make sure its a quoted string
if (*pSrc == '"')
{
for (++pSrc; (iLen < iLim) && (c = *pSrc) != '"'; ++pSrc)
{
// handle escape sequence
if ((c == '\\') && (pSrc[1] > ' '))
{
c = _strEscape[*++pSrc];
if ((c == 'u') && (pSrc[1] != '"') && (pSrc[2] != '"') && (pSrc[3] != '"') && (pSrc[4] != '"'))
{
c = (_HexDecode[pSrc[3]]<<4)|(_HexDecode[pSrc[4]]<<0), pSrc += 4;
}
}
// conditional save (to use as length check)
*pDst = c;
pDst += iSkip;
++iLen;
}
*pDst = '\0';
}
return(iLen);
}
/*F*************************************************************************************/
/*!
\Function _JsonParseWrite
\Description
Write a value to the parse buffer. Depending on size, the value will be encoded
in one or two 16 bit values using a progressive encoding scheme.
\Input *pParse - [out] buffer for parse value
\Input *pMax - limit of parse buffer
\Input uValue - value to write to parse buffer
\Input bWide - if TRUE, write using wide encoding
\Output
uint16_t * - pointer to next value
\Notes
Values from zero to 0x7fff are written as-is using a uint16_t. Larger values
are written with the lower 15 bits or'd with 0x8000, followed by bits 15-31 in
the following uint16_t. The largest number that can be encoded this way is
0x7fffffff, as one bit is sacrificed for the progressive bit.
\Version 02/02/2016 (jbrookes)
*/
/*************************************************************************************F*/
static uint16_t *_JsonParseWrite(uint16_t *pParse, uint16_t *pMax, uint32_t uValue, uint8_t bWide)
{
if ((uValue > 0x7fff) || (bWide == TRUE))
{
if (pParse < pMax)
{
*pParse = (uint16_t)uValue | 0x8000;
}
uValue >>= 15;
pParse += 1;
}
if (pParse < pMax)
{
*pParse = (uint16_t)uValue;
}
pParse += 1;
return(pParse);
}
/*F*************************************************************************************/
/*!
\Function _JsonParseRead
\Description
Read a progressively-encoded value from the parse buffer.
\Input *pParse - parse buffer
\Input *pValue - [out] storage for value read from parse buffer
\Input *pParseMax - end of parse buffer
\Output
uint16_t * - pointer to next value
\Notes
See _JsonParseWrite for a description of the progressive encoding scheme.
\Version 02/02/2016 (jbrookes)
*/
/*************************************************************************************F*/
static const uint16_t *_JsonParseRead(const uint16_t *pParse, uint32_t *pValue, const uint16_t *pParseMax)
{
*pValue = 0;
if (pParse < pParseMax)
{
*pValue = *pParse;
}
pParse += 1;
if (*pValue & 0x8000)
{
*pValue &= ~0x8000;
if (pParse < pParseMax)
{
*pValue |= (uint32_t)*pParse << 15;
}
pParse += 1;
}
return(pParse);
}
/*F*************************************************************************************/
/*!
\Function _JsonParseReadOffsetAndSize
\Description
Read offset and size values. If offset exceeds parse buffer size, an offset and
size of zero are returned.
\Input *pParse - parse buffer
\Input *pOffset - [out] storage for offset
\Input *pSize - [out] storage for size
\Input *pParseMax - end of parse buffer
\Output
uint16_t * - pointer to next parse object
\Notes
See _JsonParseWrite for a description of the progressive encoding scheme.
\Version 02/02/2016 (jbrookes)
*/
/*************************************************************************************F*/
static const uint16_t *_JsonParseReadOffsetAndSize(const uint16_t *pParse, uint32_t *pOffset, uint32_t *pSize, const uint16_t *pParseMax)
{
pParse = _JsonParseRead(pParse, pOffset, pParseMax);
pParse = _JsonParseRead(pParse, pSize, pParseMax);
if (pParse > pParseMax)
{
*pOffset = 0;
*pSize = 0;
}
return(pParse);
}
/*** Public Functions ******************************************************************/
/*F*************************************************************************************/
/*!
\Function JsonParse
\Description
Pre-parse a JSON buffer for fast parsing. Must be called on a buffer before
any Find() or Get() functions are used. Pass pDst=NULL and iMax=0 to query
the required table size.
\Input *pDst - [out] storage for lookup table, two elements per object
\Input iMax - number of uint16_t elements in output table
\Input *_pSrc - pointer to JSON buffer
\Input iLen - length of JSON buffer; if -1 a strlen is performed
\Output
int32_t - zero=table too small, else size of table in bytes
\Version 12/13/2012 (jbrookes)
*/
/*************************************************************************************F*/
int32_t JsonParse(uint16_t *pDst, int32_t iMax, const char *_pSrc, int32_t iLen)
{
uint8_t c;
int32_t iArray = 0;
int32_t iObject = 0;
uint16_t *pArray[16];
uint16_t *pObject[16];
const uint8_t *pSrc = (const uint8_t *)_pSrc;
const uint8_t *pOff = pSrc;
const uint8_t *pEnd;
uint16_t *pBeg = pDst;
uint16_t *pMax = pDst+iMax;
uint32_t uStart;
uint8_t bTruncated = FALSE;
int32_t iBufSize;
// easy string length
if (iLen < 0)
{
iLen = (int32_t)strlen(_pSrc);
}
pEnd = pSrc+iLen;
// check size and save json pointer in parse buffer
if (iMax != 0)
{
// make sure we have enough space to store json pointer plus at least one max-sized json parse object
if (iMax < (int32_t)(sizeof(pSrc)/sizeof(uint16_t)) + 4 + 4)
{
return(0);
}
// save json pointer at start of parse buffer
ds_memcpy(pDst, &pSrc, sizeof(pSrc));
// save max size
ds_memcpy(pDst + (sizeof(pSrc)/sizeof(*pDst)), &iMax, sizeof(iMax));
}
// add space for pointer
pDst += sizeof(pSrc)/sizeof(*pDst);
// add space for max size
pDst += sizeof(iMax)/sizeof(*pDst);
// find the tokens
for (; pSrc != pEnd; ++pSrc)
{
c = _strDecode[*pSrc];
// handle end of decoding
if (c == '\0')
{
break;
}
// handle white space
if (c == ' ')
{
continue;
}
// save start of token
pDst = _JsonParseWrite(pDst, pMax, uStart = (uint32_t)(pSrc-pOff), FALSE);
// handle number
if (c == '1')
{
// skip past number
for (++pSrc; (pSrc != pEnd) && (*pSrc >= '0') && (*pSrc <= '9'); ++pSrc)
;
// handle decimal ending
if ((pSrc != pEnd) && (*pSrc == '.'))
{
for (++pSrc; (pSrc != pEnd) && (*pSrc >= '0') && (*pSrc <= '9'); ++pSrc)
;
}
// handle exponent
if ((pSrc != pEnd) && ((*pSrc|32) == 'e'))
{
// gobble extension after exponent
++pSrc;
if ((pSrc != pEnd) && ((*pSrc == '+') || (*pSrc == '-')))
{
++pSrc;
}
for (; (pSrc != pEnd) && (*pSrc >= '0') && (*pSrc <= '9'); ++pSrc)
;
}
--pSrc;
pDst = _JsonParseWrite(pDst, pMax, (uint32_t)((pSrc-pOff)-uStart+1), FALSE);
}
// handle string
else if (c == '"')
{
// walk the string
for (++pSrc; (pSrc != pEnd) && (*pSrc != '"'); ++pSrc)
{
if ((*pSrc == '\\') && (pSrc[1] > ' '))
{
++pSrc;
}
}
pDst = _JsonParseWrite(pDst, pMax, (uint32_t)((pSrc-pOff)-uStart+1), FALSE);
}
// handle token
else if (c == 'a')
{
for (++pSrc; (pSrc != pEnd) && (_strDecode[*pSrc] == 'a'); ++pSrc)
;
--pSrc;
pDst = _JsonParseWrite(pDst, pMax, (uint32_t)((pSrc-pOff)-uStart+1), FALSE);
}
// handle object open
else if ((c == '{') && (iObject < (int32_t)(sizeof(pObject)/sizeof(pObject[0]))))
{
pObject[iObject++] = pDst;
pDst = _JsonParseWrite(pDst, pMax, 0, TRUE);
}
// handle object close
else if ((c == '}') && (iObject > 0))
{
--iObject;
_JsonParseWrite(pObject[iObject], pMax, (uint32_t)(pDst-pObject[iObject]), TRUE);
pDst = _JsonParseWrite(pDst, pMax, 1, FALSE);
}
// handle array open
else if ((c == '[') && (iArray < (int32_t)(sizeof(pArray)/sizeof(pArray[0]))))
{
pArray[iArray++] = pDst;
pDst = _JsonParseWrite(pDst, pMax, 0, TRUE);
}
// handle array close
else if ((c == ']') && (iArray > 0))
{
--iArray;
_JsonParseWrite(pArray[iArray], pMax, (uint32_t)(pDst-pArray[iArray]), TRUE);
pDst = _JsonParseWrite(pDst, pMax, 1, FALSE);
}
else
{
pDst = _JsonParseWrite(pDst, pMax, 1, FALSE);
}
}
// close any remaining objects -- invalid JSON?
while (iObject-- > 0)
{
_JsonParseWrite(pObject[iObject], pMax, (uint32_t)(pDst-pObject[iObject]), TRUE);
}
while (iArray-- > 0)
{
_JsonParseWrite(pArray[iArray], pMax, (uint32_t)(pDst-pArray[iArray]), TRUE);
}
// make sure we always terminate output buffer
if ((iMax != 0) && (pDst > pMax-2))
{
pDst = pMax-2;
bTruncated = TRUE;
}
// terminate output buffer
pDst = _JsonParseWrite(pDst, pMax, 0, FALSE);
pDst = _JsonParseWrite(pDst, pMax, 0, FALSE);
// calculate size of buffer in bytes
iBufSize = (int32_t)(((uint8_t *)pDst) - ((uint8_t *)pBeg));
// return size in bytes, or zero if the table was truncated
return(bTruncated ? 0 : iBufSize);
}
/*F*************************************************************************************/
/*!
\Function JsonParse2
\Description
Pre-parse a JSON buffer for fast parsing. Must be called on a buffer before
any Find() or Get() functions are used. This version allocates the required
amount of memory internally rather than taking the buffer as input.
\Input *pSrc - pointer to JSON buffer
\Input iLen - length of JSON buffer; if -1 a strlen is performed
\Input iMemModule - memory module passed to DirtyMemAlloc
\Input iMemGroup - memory group passed to DirtyMemAlloc
\Input *pMemGroupUserData - memory group user data passed to DirtyMemAlloc
\Output
uint16_t * - allocated parse table, or NULL on error
\Version 12/05/2016 (jbrookes)
*/
/*************************************************************************************F*/
uint16_t *JsonParse2(const char *pSrc, int32_t iLen, int32_t iMemModule, int32_t iMemGroup, void *pMemGroupUserData)
{
uint16_t *pBuff;
int32_t iSize;
// get buffer size
if ((iSize = JsonParse(NULL, 0, pSrc, iLen)) == 0)
{
return(NULL);
}
// allocate buffer space
if ((pBuff = DirtyMemAlloc(iSize, iMemModule, iMemGroup, pMemGroupUserData)) == NULL)
{
return(NULL);
}
// parse the buffer
if (JsonParse(pBuff, iSize, pSrc, iLen) != iSize)
{
DirtyMemFree(pBuff, iMemModule, iMemGroup, pMemGroupUserData);
pBuff = NULL;
}
return(pBuff);
}
/*F*************************************************************************************/
/*!
\Function JsonFind
\Description
Locate a JSON element
\Input *pParse - pre-parsed lookup from JsonParse
\Input *pName - name of element to locate
\Output
const char * - pointer to element, or NULL if not found
\Notes
A name can include a period character '.' which indicates an object reference,
or an open bracket '[' which indicates an array reference. A bracket must be
the last character in the name.
\Version 12/13/2012 (jbrookes)
*/
/*************************************************************************************F*/
const char *JsonFind(const uint16_t *pParse, const char *pName)
{
return(JsonFind2(pParse, NULL, pName, 0));
}
/*F*************************************************************************************/
/*!
\Function JsonFind2
\Description
Locate a JSON element, starting from an offset, with an optional array index
\Input *pParse - pre-parsed lookup from JsonParse
\Input *_pJsonOff - offset into JSON buffer to begin search
\Input *pName - name of element to locate
\Input iIndex - [optional] index of array element to return
\Output
const uint8_t * - pointer to element, or NULL if not found
\Notes
This version of Find is useful for parsing arrays of scalars or objects, or
for starting a parse at an offset within the JSON document.
\Version 12/13/2012 (jbrookes)
*/
/*************************************************************************************F*/
const char *JsonFind2(const uint16_t *pParse, const char *_pJsonOff, const char *pName, int32_t iIndex)
{
const uint8_t *pJsonOff = (const uint8_t *)_pJsonOff;
const uint8_t *pJson;
const char *pLast, *pElem;
const uint16_t *pParseMax, *pTemp, *pTemp2;
int32_t iLen, iMax;
uint32_t uOffset, uOffsetNext, uSize, uSizeNext, uTemp;
uint8_t c;
// save pointer for parse max
pParseMax = pParse;
// get json data pointer; note cast to avoid invalid vc warning -- thinks &pJson is const whereas it points to something const
ds_memcpy((uint8_t *)&pJson, pParse, sizeof(pJson));
pParse += sizeof(pJson)/sizeof(*pParse);
// get parse buffer max size
ds_memcpy(&iMax, pParse, sizeof(iMax));
pParse += sizeof(iMax)/sizeof(*pParse);
// point to end of parse buffer
pParseMax += iMax;
// if json has outer object, then skip it
pTemp = _JsonParseRead(pParse, &uOffset, pParseMax);
if (pJson[uOffset] == '{')
{
pParse = _JsonParseRead(pTemp, &uTemp, pParseMax);
}
// if they gave us an offset, move the parse forward to match
if (pJsonOff != NULL)
{
uint32_t uJsonOff = (uint32_t)(pJsonOff - pJson);
while (1)
{
pTemp = _JsonParseReadOffsetAndSize(pParse, &uOffset, &uSize, pParseMax);
if ((uOffset >= uJsonOff) || (uSize == 0))
{
break;
}
pParse = pTemp;
}
}
// parse the find string
for (uSize = 1; (*pName != 0) && (uSize != 0); )
{
pTemp = _JsonParseReadOffsetAndSize(pParse, &uOffset, &uSize, pParseMax);
// handle traversing into an object
if (*pName == '.')
{
// if not an object, then its an error
if (pJson[uOffset] != '{')
{
break;
}
// move into object and keep parsing
pParse = pTemp;
++pName;
continue;
}
// handle traversing into an array
else if (*pName == '[')
{
// if not an array, then its an error
if (pJson[uOffset] != '[')
{
break;
}
// skip to nth element
for (pParse = pTemp, uSize = 1; (iIndex > 0) && (uSize != 0); iIndex -= 1)
{
pTemp = _JsonParseReadOffsetAndSize(pParse, &uOffset, &uSize, pParseMax);
c = pJson[uOffset];
if (c == ']')
{
break;
}
else if (c == '{')
{
// skip to end of object
pParse += uSize;
pTemp = pParse;
}
// skip object
if (uSize != 0)
{
pTemp = _JsonParseReadOffsetAndSize(pParse, &uOffset, &uSize, pParseMax);
}
// skip trailing comma?
pTemp = _JsonParseReadOffsetAndSize(pTemp, &uOffset, &uSize, pParseMax);
if ((uSize != 0) && (pJson[uOffset] == ','))
{
pParse = pTemp;
}
}
// if we didn't end on a close bracket we've found our element
if ((uSize != 0) && (pJson[uOffset] != ']')) // must re-check because we could end exactly on the close bracket
{
pName = "\0";
}
break;
}
// check to see if object name matches
else if (((*pName|32) >= 'a') && ((*pName|32) <= 'z'))
{
// figure out length
for (pLast = pName; (*pLast != 0) && (*pLast != '.') && (*pLast != '['); ++pLast)
;
iLen = (int32_t)(pLast-pName);
// search for this name
for (uSize = 1; uSize != 0; pParse = pTemp)
{
pTemp = _JsonParseReadOffsetAndSize(pParse, &uOffset, &uSize, pParseMax);
c = pJson[uOffset];
// skip arrays & objects
if ((c == '[') || (c == '{'))
{
pTemp = _JsonParseReadOffsetAndSize(pParse+uSize, &uOffset, &uSize, pParseMax);
if (uSize == 0)
{
break;
}
continue;
}
// end of array/object is terminator, or we hit the end of the parse buffer
if ((c == ']') || (c == '}') || (uSize == 0))
{
return(NULL);
}
// look for a label
pTemp2 = _JsonParseReadOffsetAndSize(pTemp, &uOffsetNext, &uSizeNext, pParseMax);
if ((c == '"') && (pJson[uOffsetNext] == ':') && (iLen+2 == (signed)uSize))
{
if (memcmp(pJson+uOffset+1, pName, iLen) == 0)
{
// skip the label
pParse = pTemp2;
uOffset = uOffsetNext;
uSize = uSizeNext;
pName = pLast;
break;
}
}
}
}
else
{
// invalid pattern
return(NULL);
}
}
// get current offset and size from parse
_JsonParseReadOffsetAndSize(pParse, &uOffset, &uSize, pParseMax);
/* point to element; validate the name matches and that we are pointing at a valid element. both checks
are required, as we could have found the name successfully, but then failed to find the element */
if ((*pName == '\0') && (uSize != 0))
{
pElem = (const char *)(pJson+uOffset);
}
else
{
pElem = NULL;
}
return(pElem);
}
/*F*************************************************************************************/
/*!
\Function JsonGetString
\Description
Extract a JSON string element. Pass pBuffer=NULL for length check.
\Input *pJson - JSON buffer
\Input *pBuffer - [out] storage for string (may be NULL for length query)
\Input iLength - length of output buffer
\Input *pDefault - default value to use, if string could not be extracted
\Output
int32_t - length of string, or negative on error
\Version 12/13/2012 (jbrookes)
*/
/*************************************************************************************F*/
int32_t JsonGetString(const char *pJson, char *pBuffer, int32_t iLength, const char *pDefault)
{
return(JsonGetString2(pJson, pBuffer, iLength, pDefault, NULL));
}
/*F*************************************************************************************/
/*!
\Function JsonGetString2
\Description
Extract a JSON string element. Pass pBuffer=NULL for length check. Error
accumulating version.
\Input *pJson - JSON buffer
\Input *pBuffer - [out] storage for string (may be NULL for length query)
\Input iLength - length of output buffer
\Input *pDefault - default value to use, if string could not be extracted
\Input *pError - [in/out] accumulation flag for parse errors; 1=error, 0=no error
\Output
int32_t - length of string, or negative on error
\Version 12/05/2016 (jbrookes)
*/
/*************************************************************************************F*/
int32_t JsonGetString2(const char *pJson, char *pBuffer, int32_t iLength, const char *pDefault, uint8_t *pError)
{
uint8_t c;
// handle default value
if (pJson == NULL)
{
// error if no default string
if (pDefault == NULL)
{
if (pError != NULL)
{
*pError = 1;
}
return(-1);
}
// copy string if it isn't a length-only query
if (pBuffer != NULL)
{
ds_strnzcpy(pBuffer, pDefault, iLength);
pDefault = pBuffer;
}
// return the length
return((int32_t)strlen(pDefault));
}
// handle length query
if (pBuffer == NULL)
{
return(_JsonGetQuoted(&c, INT32_MAX, (const uint8_t *)pJson, 0));
}
// make sure return buffer has terminator length
if (iLength < 1)
{
if (pError != NULL)
{
*pError = 1;
}
return(-1);
}
// copy the string
return(_JsonGetQuoted((uint8_t *)pBuffer, iLength, (const uint8_t *)pJson, 1));
}
/*F*************************************************************************************/
/*!
\Function JsonGetInteger
\Description
Extract a JSON integer number element.
\Input *pJson - JSON buffer
\Input iDefault - default value to use, if number could not be extracted
\Output
int64_t - integer, or default value
\Version 12/13/2012 (jbrookes)
*/
/*************************************************************************************F*/
int64_t JsonGetInteger(const char *pJson, int64_t iDefault)
{
return(JsonGetInteger2(pJson, iDefault, INT64_MIN, INT64_MAX, NULL));
}
/*F*************************************************************************************/
/*!
\Function JsonGetInteger2
\Description
Extract a JSON integer number element with range checking and error accumulation.
Pass zero for both iMin and iMax to disable range checking.
\Input *pJson - JSON buffer
\Input iDefault - default value to use, if number could not be extracted
\Input iMin - min value for range checking
\Input iMax - max value for range checking
\Input *pError - [in/out] accumulation flag for parse errors; 1=error, 0=no error
\Output
int64_t - integer, or default value
\Version 12/05/2016 (jbrookes)
*/
/*************************************************************************************F*/
int64_t JsonGetInteger2(const char *pJson, int64_t iDefault, int64_t iMin, int64_t iMax, uint8_t *pError)
{
int64_t iSign = 1;
int64_t iValue;
// handle default
if (pJson == NULL)
{
if (pError != NULL)
{
*pError = 1;
}
return(iDefault);
}
// handle negative
if (*pJson == '-')
{
++pJson;
iSign = -1;
}
// parse the value
for (iValue = 0; (*pJson >= '0') && (*pJson <= '9'); ++pJson)
{
iValue = (iValue * 10) + (*pJson & 15);
}
// range check
if ((iMin != 0) || (iMax != 0))
{
iValue = DS_CLAMP(iValue, iMin, iMax);
}
// return with sign
return(iSign*iValue);
}
/*F*************************************************************************************/
/*!
\Function JsonGetDate
\Description
Extract a JSON date element
\Input *pJson - JSON buffer
\Input uDefault - default date value to use
\Output
uint32_t - extracted date, or default
\Notes
Date is assumed to be in ISO_8601 format
\Version 12/13/2012 (jbrookes)
*/
/*************************************************************************************F*/
uint32_t JsonGetDate(const char *pJson, uint32_t uDefault)
{
return(JsonGetDate2(pJson, uDefault, NULL));
}
/*F*************************************************************************************/
/*!
\Function JsonGetDate2
\Description
Extract a JSON date element
\Input *pJson - JSON buffer
\Input uDefault - default date value to use
\Input *pError - [in/out] accumulation flag for parse errors; 1=error, 0=no error
\Output
uint32_t - extracted date, or default
\Notes
Date is assumed to be in ISO_8601 format
\Version 12/13/2012 (jbrookes)
*/
/*************************************************************************************F*/
uint32_t JsonGetDate2(const char *pJson, uint32_t uDefault, uint8_t *pError)
{
uint32_t uDate = uDefault;
char strValue[128];
JsonGetString(pJson, strValue, sizeof(strValue), "");
if ((uDate = (uint32_t)ds_strtotime2(strValue, TIMETOSTRING_CONVERSION_ISO_8601)) == 0)
{
uDate = uDefault;
if (pError != NULL)
{
*pError = 1;
}
}
return(uDate);
}
/*F*************************************************************************************/
/*!
\Function JsonGetBoolean
\Description
Extract a JSON boolean
\Input *pJson - JSON buffer
\Input bDefault - default boolean value to use
\Output
uint8_t - extracted bool, or default
\Version 12/13/2012 (jbrookes)
*/
/*************************************************************************************F*/
uint8_t JsonGetBoolean(const char *pJson, uint8_t bDefault)
{
return(JsonGetBoolean2(pJson, bDefault, NULL));
}
/*F*************************************************************************************/
/*!
\Function JsonGetBoolean2
\Description
Extract a JSON boolean, with accumulating error
\Input *pJson - JSON buffer
\Input bDefault - default boolean value to use
\Input *pError - [in/out] accumulation flag for parse errors; 1=error, 0=no error
\Output
uint8_t - extracted bool, or default
\Version 12/13/2012 (jbrookes)
*/
/*************************************************************************************F*/
uint8_t JsonGetBoolean2(const char *pJson, uint8_t bDefault, uint8_t *pError)
{
uint8_t bValue = bDefault;
// handle default
if (pJson == NULL)
{
if (pError != NULL)
{
*pError = 1;
}
return(bDefault);
}
// check for true
if (strncmp((const char *)pJson, "true", 4) == 0)
{
bValue = TRUE;
}
else if (strncmp((const char *)pJson, "false", 4) == 0)
{
bValue = FALSE;
}
else if (pError != NULL)
{
*pError = 1;
}
// return result
return(bValue);
}
/*F*************************************************************************************/
/*!
\Function JsonGetEnum
\Description
Extract a JSON enumerated value. The source type is assumed to be a string,
and pEnumArray contains a NULL-terminated list of strings that may be converted
to an enum value.
\Input *pJson - JSON buffer
\Input *pEnumArray - NULL-terminated list of enum strings
\Input iDefault - default enum value to use if no match is found
\Output
int32_t - enum value; or default
\Version 12/13/2012 (jbrookes)
*/
/*************************************************************************************F*/
int32_t JsonGetEnum(const char *pJson, const char *pEnumArray[], int32_t iDefault)
{
return(JsonGetEnum2(pJson, pEnumArray, iDefault, NULL));
}
/*F*************************************************************************************/
/*!
\Function JsonGetEnum2
\Description
Extract a JSON enumerated value, with accumulating error. The source type is
assumed to be a string, and pEnumArray contains a NULL-terminated list of strings
that may be converted to an enum value.
\Input *pJson - JSON buffer
\Input *pEnumArray - NULL-terminated list of enum strings
\Input iDefault - default enum value to use if no match is found
\Input *pError - [in/out] accumulation flag for parse errors; 1=error, 0=no error
\Output
int32_t - enum value; or default
\Version 12/13/2012 (jbrookes)
*/
/*************************************************************************************F*/
int32_t JsonGetEnum2(const char *pJson, const char *pEnumArray[], int32_t iDefault, uint8_t *pError)
{
int32_t iEnum = iDefault;
char strValue[128];
JsonGetString(pJson, strValue, sizeof(strValue), "");
for (iEnum = 0; pEnumArray[iEnum] != NULL; iEnum += 1)
{
if (!strcmp(pEnumArray[iEnum], strValue))
{
return(iEnum);
}
}
if (pError != NULL)
{
*pError = 1;
}
return(iDefault);
}
/*F*************************************************************************************************/
/*!
\Function JsonSeekObjectEnd
\Description
Seek to the end of an object within the JSON text buffer
\Input *pList - pointer to the beginning of object
\Output
const char * - pointer to the end of the object, NULL for error
\Version 04/17/2013 (amakoukji)
*/
/*************************************************************************************************F*/
const char *JsonSeekObjectEnd(const char *pList)
{
const char *pCurrent = pList;
const char *pEnd = pList + (int32_t)strlen(pList);
uint32_t uDepth = 0;
char c;
while (pCurrent < pEnd)
{
c = *pCurrent;
switch (c)
{
case '"':
{
// walk the string
for (++pCurrent; (pCurrent != pEnd) && (*pCurrent != '"'); ++pCurrent)
{
if ((*pCurrent == '\\') && (pCurrent[1] > ' '))
{
++pCurrent;
}
}
}
break;
case '{':
{
uDepth += 1;
}
break;
case '}':
{
uDepth -= 1;
}
break;
case ',':
{
if (uDepth == 0)
{
return(pCurrent);
}
}
break;
case ']':
{
if (uDepth == 0)
{
return(pCurrent);
}
}
break;
}
pCurrent += 1;
}
// error
return(NULL);
}
/*F*************************************************************************************************/
/*!
\Function JsonSeekValue
\Description
Given a pointer to the beginning of a simple JSON key value pair, seeks the
start of the value
\Input *pKey - module ref
\Output
int32_t - pointer to the start of the value, NULL for error
\Version 11/28/2013 (amakoukji)
*/
/*************************************************************************************************F*/
const char *JsonSeekValue(const char *pKey)
{
const char *pTemp = pKey;
if (pKey == NULL)
{
return(NULL);
}
// seek to the ':'
while (*pTemp != ':')
{
if ((*pTemp == '\0') || (*pTemp == '\n'))
{
return(NULL);
}
pTemp += 1;
}
pTemp += 1;
// seek to non-whitespace
while ((*pTemp == ' ') || (*pTemp == '\t'))
{
if ((*pTemp == '\0') || (*pTemp == '\n'))
{
return(NULL);
}
pTemp += 1;
}
return(pTemp);
}