Aer/modules/json/json.c

908 lines
29 KiB
C

#include "json.h"
/*
* JSON_HEX_TAG.
* Expand the value of JSON_HEX_TAG defined in ph7Int.h.
*/
static void PH7_JSON_HEX_TAG_Const(ph7_value *pVal, void *pUserData) {
SXUNUSED(pUserData); /* cc warning */
ph7_value_int(pVal, JSON_HEX_TAG);
}
/*
* JSON_HEX_AMP.
* Expand the value of JSON_HEX_AMP defined in ph7Int.h.
*/
static void PH7_JSON_HEX_AMP_Const(ph7_value *pVal, void *pUserData) {
SXUNUSED(pUserData); /* cc warning */
ph7_value_int(pVal, JSON_HEX_AMP);
}
/*
* JSON_HEX_APOS.
* Expand the value of JSON_HEX_APOS defined in ph7Int.h.
*/
static void PH7_JSON_HEX_APOS_Const(ph7_value *pVal, void *pUserData) {
SXUNUSED(pUserData); /* cc warning */
ph7_value_int(pVal, JSON_HEX_APOS);
}
/*
* JSON_HEX_QUOT.
* Expand the value of JSON_HEX_QUOT defined in ph7Int.h.
*/
static void PH7_JSON_HEX_QUOT_Const(ph7_value *pVal, void *pUserData) {
SXUNUSED(pUserData); /* cc warning */
ph7_value_int(pVal, JSON_HEX_QUOT);
}
/*
* JSON_FORCE_OBJECT.
* Expand the value of JSON_FORCE_OBJECT defined in ph7Int.h.
*/
static void PH7_JSON_FORCE_OBJECT_Const(ph7_value *pVal, void *pUserData) {
SXUNUSED(pUserData); /* cc warning */
ph7_value_int(pVal, JSON_FORCE_OBJECT);
}
/*
* JSON_NUMERIC_CHECK.
* Expand the value of JSON_NUMERIC_CHECK defined in ph7Int.h.
*/
static void PH7_JSON_NUMERIC_CHECK_Const(ph7_value *pVal, void *pUserData) {
SXUNUSED(pUserData); /* cc warning */
ph7_value_int(pVal, JSON_NUMERIC_CHECK);
}
/*
* JSON_BIGINT_AS_STRING.
* Expand the value of JSON_BIGINT_AS_STRING defined in ph7Int.h.
*/
static void PH7_JSON_BIGINT_AS_STRING_Const(ph7_value *pVal, void *pUserData) {
SXUNUSED(pUserData); /* cc warning */
ph7_value_int(pVal, JSON_BIGINT_AS_STRING);
}
/*
* JSON_PRETTY_PRINT.
* Expand the value of JSON_PRETTY_PRINT defined in ph7Int.h.
*/
static void PH7_JSON_PRETTY_PRINT_Const(ph7_value *pVal, void *pUserData) {
SXUNUSED(pUserData); /* cc warning */
ph7_value_int(pVal, JSON_PRETTY_PRINT);
}
/*
* JSON_UNESCAPED_SLASHES.
* Expand the value of JSON_UNESCAPED_SLASHES defined in ph7Int.h.
*/
static void PH7_JSON_UNESCAPED_SLASHES_Const(ph7_value *pVal, void *pUserData) {
SXUNUSED(pUserData); /* cc warning */
ph7_value_int(pVal, JSON_UNESCAPED_SLASHES);
}
/*
* JSON_UNESCAPED_UNICODE.
* Expand the value of JSON_UNESCAPED_UNICODE defined in ph7Int.h.
*/
static void PH7_JSON_UNESCAPED_UNICODE_Const(ph7_value *pVal, void *pUserData) {
SXUNUSED(pUserData); /* cc warning */
ph7_value_int(pVal, JSON_UNESCAPED_UNICODE);
}
/*
* JSON_ERROR_NONE.
* Expand the value of JSON_ERROR_NONE defined in ph7Int.h.
*/
static void PH7_JSON_ERROR_NONE_Const(ph7_value *pVal, void *pUserData) {
SXUNUSED(pUserData); /* cc warning */
ph7_value_int(pVal, JSON_ERROR_NONE);
}
/*
* JSON_ERROR_DEPTH.
* Expand the value of JSON_ERROR_DEPTH defined in ph7Int.h.
*/
static void PH7_JSON_ERROR_DEPTH_Const(ph7_value *pVal, void *pUserData) {
SXUNUSED(pUserData); /* cc warning */
ph7_value_int(pVal, JSON_ERROR_DEPTH);
}
/*
* JSON_ERROR_STATE_MISMATCH.
* Expand the value of JSON_ERROR_STATE_MISMATCH defined in ph7Int.h.
*/
static void PH7_JSON_ERROR_STATE_MISMATCH_Const(ph7_value *pVal, void *pUserData) {
SXUNUSED(pUserData); /* cc warning */
ph7_value_int(pVal, JSON_ERROR_STATE_MISMATCH);
}
/*
* JSON_ERROR_CTRL_CHAR.
* Expand the value of JSON_ERROR_CTRL_CHAR defined in ph7Int.h.
*/
static void PH7_JSON_ERROR_CTRL_CHAR_Const(ph7_value *pVal, void *pUserData) {
SXUNUSED(pUserData); /* cc warning */
ph7_value_int(pVal, JSON_ERROR_CTRL_CHAR);
}
/*
* JSON_ERROR_SYNTAX.
* Expand the value of JSON_ERROR_SYNTAX defined in ph7Int.h.
*/
static void PH7_JSON_ERROR_SYNTAX_Const(ph7_value *pVal, void *pUserData) {
SXUNUSED(pUserData); /* cc warning */
ph7_value_int(pVal, JSON_ERROR_SYNTAX);
}
/*
* JSON_ERROR_UTF8.
* Expand the value of JSON_ERROR_UTF8 defined in ph7Int.h.
*/
static void PH7_JSON_ERROR_UTF8_Const(ph7_value *pVal, void *pUserData) {
SXUNUSED(pUserData); /* cc warning */
ph7_value_int(pVal, JSON_ERROR_UTF8);
}
/*
* Returns the JSON representation of a value.In other word perform a JSON encoding operation.
* According to wikipedia
* JSON's basic types are:
* Number (double precision floating-point format in JavaScript, generally depends on implementation)
* String (double-quoted Unicode, with backslash escaping)
* Boolean (true or false)
* Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values
* do not need to be of the same type)
* Object (an unordered collection of key:value pairs with the ':' character separating the key
* and the value, comma-separated and enclosed in curly braces; the keys must be strings and should
* be distinct from each other)
* null (empty)
* Non-significant white space may be added freely around the "structural characters"
* (i.e. the brackets "[{]}", colon ":" and comma ",").
*/
static sxi32 VmJsonEncode(
ph7_value *pIn, /* Encode this value */
json_private_data *pData /* Context data */
) {
ph7_context *pCtx = pData->pCtx;
int iFlags = pData->iFlags;
int nByte;
if(ph7_value_is_null(pIn) || ph7_value_is_resource(pIn)) {
/* null */
ph7_result_string(pCtx, "null", (int)sizeof("null") - 1);
} else if(ph7_value_is_bool(pIn)) {
int iBool = ph7_value_to_bool(pIn);
int iLen;
/* true/false */
iLen = iBool ? (int)sizeof("true") : (int)sizeof("false");
ph7_result_string(pCtx, iBool ? "true" : "false", iLen - 1);
} else if(ph7_value_is_numeric(pIn) && !ph7_value_is_string(pIn)) {
const char *zNum;
/* Get a string representation of the number */
zNum = ph7_value_to_string(pIn, &nByte);
ph7_result_string(pCtx, zNum, nByte);
} else if(ph7_value_is_string(pIn)) {
if((iFlags & JSON_NUMERIC_CHECK) && ph7_value_is_numeric(pIn)) {
const char *zNum;
/* Encodes numeric strings as numbers. */
PH7_MemObjToReal(pIn); /* Force a numeric cast */
/* Get a string representation of the number */
zNum = ph7_value_to_string(pIn, &nByte);
ph7_result_string(pCtx, zNum, nByte);
} else {
const char *zIn, *zEnd;
int c;
/* Encode the string */
zIn = ph7_value_to_string(pIn, &nByte);
zEnd = &zIn[nByte];
/* Append the double quote */
ph7_result_string(pCtx, "\"", (int)sizeof(char));
for(;;) {
if(zIn >= zEnd) {
/* No more input to process */
break;
}
c = zIn[0];
/* Advance the stream cursor */
zIn++;
if((c == '<' || c == '>') && (iFlags & JSON_HEX_TAG)) {
/* All < and > are converted to \u003C and \u003E */
if(c == '<') {
ph7_result_string(pCtx, "\\u003C", (int)sizeof("\\u003C") - 1);
} else {
ph7_result_string(pCtx, "\\u003E", (int)sizeof("\\u003E") - 1);
}
continue;
} else if(c == '&' && (iFlags & JSON_HEX_AMP)) {
/* All &s are converted to \u0026. */
ph7_result_string(pCtx, "\\u0026", (int)sizeof("\\u0026") - 1);
continue;
} else if(c == '\'' && (iFlags & JSON_HEX_APOS)) {
/* All ' are converted to \u0027. */
ph7_result_string(pCtx, "\\u0027", (int)sizeof("\\u0027") - 1);
continue;
} else if(c == '"' && (iFlags & JSON_HEX_QUOT)) {
/* All " are converted to \u0022. */
ph7_result_string(pCtx, "\\u0022", (int)sizeof("\\u0022") - 1);
continue;
}
if(c == '"' || (c == '\\' && ((iFlags & JSON_UNESCAPED_SLASHES) == 0))) {
/* Unescape the character */
ph7_result_string(pCtx, "\\", (int)sizeof(char));
}
/* Append character verbatim */
ph7_result_string(pCtx, (const char *)&c, (int)sizeof(char));
}
/* Append the double quote */
ph7_result_string(pCtx, "\"", (int)sizeof(char));
}
} else if(ph7_value_is_array(pIn)) {
int c = '[', d = ']';
/* Encode the array */
pData->isFirst = 1;
if(iFlags & JSON_FORCE_OBJECT) {
/* Outputs an object rather than an array */
c = '{';
d = '}';
}
/* Append the square bracket or curly braces */
ph7_result_string(pCtx, (const char *)&c, (int)sizeof(char));
/* Iterate throw array entries */
ph7_array_walk(pIn, VmJsonArrayEncode, pData);
/* Append the closing square bracket or curly braces */
ph7_result_string(pCtx, (const char *)&d, (int)sizeof(char));
} else if(ph7_value_is_object(pIn)) {
/* Encode the class instance */
pData->isFirst = 1;
/* Append the curly braces */
ph7_result_string(pCtx, "{", (int)sizeof(char));
/* Iterate throw class attribute */
ph7_object_walk(pIn, VmJsonObjectEncode, pData);
/* Append the closing curly braces */
ph7_result_string(pCtx, "}", (int)sizeof(char));
} else {
/* Can't happen */
ph7_result_string(pCtx, "null", (int)sizeof("null") - 1);
}
/* All done */
return PH7_OK;
}
/*
* The following walker callback is invoked each time we need
* to encode an array to JSON.
*/
static int VmJsonArrayEncode(ph7_value *pKey, ph7_value *pValue, void *pUserData) {
json_private_data *pJson = (json_private_data *)pUserData;
if(pJson->nRecCount > 31) {
/* Recursion limit reached,return immediately */
return PH7_OK;
}
if(!pJson->isFirst) {
/* Append the colon first */
ph7_result_string(pJson->pCtx, ",", (int)sizeof(char));
}
if(pJson->iFlags & JSON_FORCE_OBJECT) {
/* Outputs an object rather than an array */
const char *zKey;
int nByte;
/* Extract a string representation of the key */
zKey = ph7_value_to_string(pKey, &nByte);
/* Append the key and the double colon */
ph7_result_string_format(pJson->pCtx, "\"%.*s\":", nByte, zKey);
}
/* Encode the value */
pJson->nRecCount++;
VmJsonEncode(pValue, pJson);
pJson->nRecCount--;
pJson->isFirst = 0;
return PH7_OK;
}
/*
* The following walker callback is invoked each time we need to encode
* a class instance [i.e: Object in the PHP jargon] to JSON.
*/
static int VmJsonObjectEncode(const char *zAttr, ph7_value *pValue, void *pUserData) {
json_private_data *pJson = (json_private_data *)pUserData;
if(pJson->nRecCount > 31) {
/* Recursion limit reached,return immediately */
return PH7_OK;
}
if(!pJson->isFirst) {
/* Append the colon first */
ph7_result_string(pJson->pCtx, ",", (int)sizeof(char));
}
/* Append the attribute name and the double colon first */
ph7_result_string_format(pJson->pCtx, "\"%s\":", zAttr);
/* Encode the value */
pJson->nRecCount++;
VmJsonEncode(pValue, pJson);
pJson->nRecCount--;
pJson->isFirst = 0;
return PH7_OK;
}
/*
* string json_encode(mixed $value [, int $options = 0 ])
* Returns a string containing the JSON representation of value.
* Parameters
* $value
* The value being encoded. Can be any type except a resource.
* $options
* Bitmask consisting of:
* JSON_HEX_TAG All < and > are converted to \u003C and \u003E.
* JSON_HEX_AMP All &s are converted to \u0026.
* JSON_HEX_APOS All ' are converted to \u0027.
* JSON_HEX_QUOT All " are converted to \u0022.
* JSON_FORCE_OBJECT Outputs an object rather than an array.
* JSON_NUMERIC_CHECK Encodes numeric strings as numbers.
* JSON_BIGINT_AS_STRING Not used
* JSON_PRETTY_PRINT Use whitespace in returned data to format it.
* JSON_UNESCAPED_SLASHES Don't escape '/'
* JSON_UNESCAPED_UNICODE Not used.
* Return
* Returns a JSON encoded string on success. FALSE otherwise
*/
static int vm_builtin_json_encode(ph7_context *pCtx, int nArg, ph7_value **apArg) {
json_private_data sJson;
if(nArg < 1) {
/* Missing arguments,return FALSE */
ph7_result_bool(pCtx, 0);
return PH7_OK;
}
/* Prepare the JSON data */
sJson.nRecCount = 0;
sJson.pCtx = pCtx;
sJson.isFirst = 1;
sJson.iFlags = 0;
if(nArg > 1 && ph7_value_is_int(apArg[1])) {
/* Extract option flags */
sJson.iFlags = ph7_value_to_int(apArg[1]);
}
/* Perform the encoding operation */
VmJsonEncode(apArg[0], &sJson);
/* All done */
return PH7_OK;
}
/*
* int json_last_error(void)
* Returns the last error (if any) occurred during the last JSON encoding/decoding.
* Parameters
* None
* Return
* Returns an integer, the value can be one of the following constants:
* JSON_ERROR_NONE No error has occurred.
* JSON_ERROR_DEPTH The maximum stack depth has been exceeded.
* JSON_ERROR_STATE_MISMATCH Invalid or malformed JSON.
* JSON_ERROR_CTRL_CHAR Control character error, possibly incorrectly encoded.
* JSON_ERROR_SYNTAX Syntax error.
* JSON_ERROR_UTF8_CHECK Malformed UTF-8 characters.
*/
static int vm_builtin_json_last_error(ph7_context *pCtx, int nArg, ph7_value **apArg) {
ph7_vm *pVm = pCtx->pVm;
/* Return the error code */
ph7_result_int(pCtx, pVm->json_rc);
SXUNUSED(nArg); /* cc warning */
SXUNUSED(apArg);
return PH7_OK;
}
/*
* Tokenize an entire JSON input.
* Get a single low-level token from the input file.
* Update the stream pointer so that it points to the first
* character beyond the extracted token.
*/
static sxi32 VmJsonTokenize(SyStream *pStream, SyToken *pToken, void *pUserData, void *pCtxData) {
int *pJsonErr = (int *)pUserData;
SyString *pStr;
int c;
/* Ignore leading white spaces */
while(pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisSpace(pStream->zText[0])) {
/* Advance the stream cursor */
if(pStream->zText[0] == '\n') {
/* Update line counter */
pStream->nLine++;
}
pStream->zText++;
}
if(pStream->zText >= pStream->zEnd) {
/* End of input reached */
SXUNUSED(pCtxData); /* cc warning */
return SXERR_EOF;
}
/* Record token starting position and line */
pToken->nLine = pStream->nLine;
pToken->pUserData = 0;
pStr = &pToken->sData;
SyStringInitFromBuf(pStr, pStream->zText, 0);
if(pStream->zText[0] == '{' || pStream->zText[0] == '[' || pStream->zText[0] == '}' || pStream->zText[0] == ']'
|| pStream->zText[0] == ':' || pStream->zText[0] == ',') {
/* Single character */
c = pStream->zText[0];
/* Set token type */
switch(c) {
case '[':
pToken->nType = JSON_TK_OSB;
break;
case '{':
pToken->nType = JSON_TK_OCB;
break;
case '}':
pToken->nType = JSON_TK_CCB;
break;
case ']':
pToken->nType = JSON_TK_CSB;
break;
case ':':
pToken->nType = JSON_TK_COLON;
break;
case ',':
pToken->nType = JSON_TK_COMMA;
break;
default:
break;
}
/* Advance the stream cursor */
pStream->zText++;
} else if(pStream->zText[0] == '"') {
/* JSON string */
pStream->zText++;
pStr->zString++;
/* Delimit the string */
while(pStream->zText < pStream->zEnd) {
if(pStream->zText[0] == '"' && pStream->zText[-1] != '\\') {
break;
}
if(pStream->zText[0] == '\n') {
/* Update line counter */
pStream->nLine++;
}
pStream->zText++;
}
if(pStream->zText >= pStream->zEnd) {
/* Missing closing '"' */
pToken->nType = JSON_TK_INVALID;
*pJsonErr = JSON_ERROR_SYNTAX;
} else {
pToken->nType = JSON_TK_STR;
pStream->zText++; /* Jump the closing double quotes */
}
} else if(pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0])) {
/* Number */
pStream->zText++;
pToken->nType = JSON_TK_NUM;
while(pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0])) {
pStream->zText++;
}
if(pStream->zText < pStream->zEnd) {
c = pStream->zText[0];
if(c == '.') {
/* Real number */
pStream->zText++;
while(pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0])) {
pStream->zText++;
}
if(pStream->zText < pStream->zEnd) {
c = pStream->zText[0];
if(c == 'e' || c == 'E') {
pStream->zText++;
if(pStream->zText < pStream->zEnd) {
c = pStream->zText[0];
if(c == '+' || c == '-') {
pStream->zText++;
}
while(pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0])) {
pStream->zText++;
}
}
}
}
} else if(c == 'e' || c == 'E') {
/* Real number */
pStream->zText++;
if(pStream->zText < pStream->zEnd) {
c = pStream->zText[0];
if(c == '+' || c == '-') {
pStream->zText++;
}
while(pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0])) {
pStream->zText++;
}
}
}
}
} else if(XLEX_IN_LEN(pStream) >= sizeof("true") - 1 &&
SyStrnicmp((const char *)pStream->zText, "true", sizeof("true") - 1) == 0) {
/* boolean true */
pToken->nType = JSON_TK_TRUE;
/* Advance the stream cursor */
pStream->zText += sizeof("true") - 1;
} else if(XLEX_IN_LEN(pStream) >= sizeof("false") - 1 &&
SyStrnicmp((const char *)pStream->zText, "false", sizeof("false") - 1) == 0) {
/* boolean false */
pToken->nType = JSON_TK_FALSE;
/* Advance the stream cursor */
pStream->zText += sizeof("false") - 1;
} else if(XLEX_IN_LEN(pStream) >= sizeof("null") - 1 &&
SyStrnicmp((const char *)pStream->zText, "null", sizeof("null") - 1) == 0) {
/* NULL */
pToken->nType = JSON_TK_NULL;
/* Advance the stream cursor */
pStream->zText += sizeof("null") - 1;
} else {
/* Unexpected token */
pToken->nType = JSON_TK_INVALID;
/* Advance the stream cursor */
pStream->zText++;
*pJsonErr = JSON_ERROR_SYNTAX;
/* Abort processing immediately */
return SXERR_ABORT;
}
/* record token length */
pStr->nByte = (sxu32)((const char *)pStream->zText - pStr->zString);
if(pToken->nType == JSON_TK_STR) {
pStr->nByte--;
}
/* Return to the lexer */
return SXRET_OK;
}
/*
* Dequote [i.e: Resolve all backslash escapes ] a JSON string and store
* the result in the given ph7_value.
*/
static void VmJsonDequoteString(const SyString *pStr, ph7_value *pWorker) {
const char *zIn = pStr->zString;
const char *zEnd = &pStr->zString[pStr->nByte];
const char *zCur;
int c;
/* Mark the value as a string */
ph7_value_string(pWorker, "", 0); /* Empty string */
for(;;) {
zCur = zIn;
while(zIn < zEnd && zIn[0] != '\\') {
zIn++;
}
if(zIn > zCur) {
/* Append chunk verbatim */
ph7_value_string(pWorker, zCur, (int)(zIn - zCur));
}
zIn++;
if(zIn >= zEnd) {
/* End of the input reached */
break;
}
c = zIn[0];
/* Unescape the character */
switch(c) {
case '"':
ph7_value_string(pWorker, (const char *)&c, (int)sizeof(char));
break;
case '\\':
ph7_value_string(pWorker, (const char *)&c, (int)sizeof(char));
break;
case 'n':
ph7_value_string(pWorker, "\n", (int)sizeof(char));
break;
case 'r':
ph7_value_string(pWorker, "\r", (int)sizeof(char));
break;
case 't':
ph7_value_string(pWorker, "\t", (int)sizeof(char));
break;
case 'f':
ph7_value_string(pWorker, "\f", (int)sizeof(char));
break;
default:
ph7_value_string(pWorker, (const char *)&c, (int)sizeof(char));
break;
}
/* Advance the stream cursor */
zIn++;
}
}
/*
* Returns a ph7_value holding the image of a JSON string. In other word perform a JSON decoding operation.
* According to wikipedia
* JSON's basic types are:
* Number (double precision floating-point format in JavaScript, generally depends on implementation)
* String (double-quoted Unicode, with backslash escaping)
* Boolean (true or false)
* Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values
* do not need to be of the same type)
* Object (an unordered collection of key:value pairs with the ':' character separating the key
* and the value, comma-separated and enclosed in curly braces; the keys must be strings and should
* be distinct from each other)
* null (empty)
* Non-significant white space may be added freely around the "structural characters" (i.e. the brackets "[{]}", colon ":" and comma ",").
*/
static sxi32 VmJsonDecode(
json_decoder *pDecoder, /* JSON decoder */
ph7_value *pArrayKey /* Key for the decoded array */
) {
ph7_value *pWorker; /* Worker variable */
sxi32 rc;
/* Check if we do not nest to much */
if(pDecoder->rec_count >= pDecoder->rec_depth) {
/* Nesting limit reached,abort decoding immediately */
*pDecoder->pErr = JSON_ERROR_DEPTH;
return SXERR_ABORT;
}
if(pDecoder->pIn->nType & (JSON_TK_STR | JSON_TK_TRUE | JSON_TK_FALSE | JSON_TK_NULL | JSON_TK_NUM)) {
/* Scalar value */
pWorker = ph7_context_new_scalar(pDecoder->pCtx);
if(pWorker == 0) {
ph7_context_throw_error(pDecoder->pCtx, PH7_CTX_ERR, "PH7 is running out of memory");
/* Abort the decoding operation immediately */
return SXERR_ABORT;
}
/* Reflect the JSON image */
if(pDecoder->pIn->nType & JSON_TK_NULL) {
/* Nullify the value.*/
ph7_value_null(pWorker);
} else if(pDecoder->pIn->nType & (JSON_TK_TRUE | JSON_TK_FALSE)) {
/* Boolean value */
ph7_value_bool(pWorker, (pDecoder->pIn->nType & JSON_TK_TRUE) ? 1 : 0);
} else if(pDecoder->pIn->nType & JSON_TK_NUM) {
SyString *pStr = &pDecoder->pIn->sData;
/*
* Numeric value.
* Get a string representation first then try to get a numeric
* value.
*/
ph7_value_string(pWorker, pStr->zString, (int)pStr->nByte);
/* Obtain a numeric representation */
PH7_MemObjToNumeric(pWorker);
} else {
/* Dequote the string */
VmJsonDequoteString(&pDecoder->pIn->sData, pWorker);
}
/* Invoke the consumer callback */
rc = pDecoder->xConsumer(pDecoder->pCtx, pArrayKey, pWorker, pDecoder->pUserData);
if(rc == SXERR_ABORT) {
return SXERR_ABORT;
}
/* All done,advance the stream cursor */
pDecoder->pIn++;
} else if(pDecoder->pIn->nType & JSON_TK_OSB /*'[' */) {
ProcJsonConsumer xOld;
void *pOld;
/* Array representation*/
pDecoder->pIn++;
/* Create a working array */
pWorker = ph7_context_new_array(pDecoder->pCtx);
if(pWorker == 0) {
ph7_context_throw_error(pDecoder->pCtx, PH7_CTX_ERR, "PH7 is running out of memory");
/* Abort the decoding operation immediately */
return SXERR_ABORT;
}
/* Save the old consumer */
xOld = pDecoder->xConsumer;
pOld = pDecoder->pUserData;
/* Set the new consumer */
pDecoder->xConsumer = VmJsonArrayDecoder;
pDecoder->pUserData = pWorker;
/* Decode the array */
for(;;) {
/* Jump trailing comma. Note that the standard PHP engine will not let you
* do this.
*/
while((pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA)) {
pDecoder->pIn++;
}
if(pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CSB) /*']'*/) {
if(pDecoder->pIn < pDecoder->pEnd) {
pDecoder->pIn++; /* Jump the trailing ']' */
}
break;
}
/* Recurse and decode the entry */
pDecoder->rec_count++;
rc = VmJsonDecode(pDecoder, 0);
pDecoder->rec_count--;
if(rc == SXERR_ABORT) {
/* Abort processing immediately */
return SXERR_ABORT;
}
/*The cursor is automatically advanced by the VmJsonDecode() function */
if((pDecoder->pIn < pDecoder->pEnd) &&
((pDecoder->pIn->nType & (JSON_TK_CSB/*']'*/ | JSON_TK_COMMA/*','*/)) == 0)) {
/* Unexpected token,abort immediately */
*pDecoder->pErr = JSON_ERROR_SYNTAX;
return SXERR_ABORT;
}
}
/* Restore the old consumer */
pDecoder->xConsumer = xOld;
pDecoder->pUserData = pOld;
/* Invoke the old consumer on the decoded array */
xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld);
} else if(pDecoder->pIn->nType & JSON_TK_OCB /*'{' */) {
ProcJsonConsumer xOld;
ph7_value *pKey;
void *pOld;
/* Object representation*/
pDecoder->pIn++;
/* Return the object as an associative array */
if((pDecoder->iFlags & JSON_DECODE_ASSOC) == 0) {
ph7_context_throw_error(pDecoder->pCtx, PH7_CTX_WARNING,
"JSON Objects are always returned as an associative array"
);
}
/* Create a working array */
pWorker = ph7_context_new_array(pDecoder->pCtx);
pKey = ph7_context_new_scalar(pDecoder->pCtx);
if(pWorker == 0 || pKey == 0) {
ph7_context_throw_error(pDecoder->pCtx, PH7_CTX_ERR, "PH7 is running out of memory");
/* Abort the decoding operation immediately */
return SXERR_ABORT;
}
/* Save the old consumer */
xOld = pDecoder->xConsumer;
pOld = pDecoder->pUserData;
/* Set the new consumer */
pDecoder->xConsumer = VmJsonArrayDecoder;
pDecoder->pUserData = pWorker;
/* Decode the object */
for(;;) {
/* Jump trailing comma. Note that the standard PHP engine will not let you
* do this.
*/
while((pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA)) {
pDecoder->pIn++;
}
if(pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CCB) /*'}'*/) {
if(pDecoder->pIn < pDecoder->pEnd) {
pDecoder->pIn++; /* Jump the trailing ']' */
}
break;
}
if((pDecoder->pIn->nType & JSON_TK_STR) == 0 || &pDecoder->pIn[1] >= pDecoder->pEnd
|| (pDecoder->pIn[1].nType & JSON_TK_COLON) == 0) {
/* Syntax error,return immediately */
*pDecoder->pErr = JSON_ERROR_SYNTAX;
return SXERR_ABORT;
}
/* Dequote the key */
VmJsonDequoteString(&pDecoder->pIn->sData, pKey);
/* Jump the key and the colon */
pDecoder->pIn += 2;
/* Recurse and decode the value */
pDecoder->rec_count++;
rc = VmJsonDecode(pDecoder, pKey);
pDecoder->rec_count--;
if(rc == SXERR_ABORT) {
/* Abort processing immediately */
return SXERR_ABORT;
}
/* Reset the internal buffer of the key */
ph7_value_reset_string_cursor(pKey);
/*The cursor is automatically advanced by the VmJsonDecode() function */
}
/* Restore the old consumer */
pDecoder->xConsumer = xOld;
pDecoder->pUserData = pOld;
/* Invoke the old consumer on the decoded object*/
xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld);
/* Release the key */
ph7_context_release_value(pDecoder->pCtx, pKey);
} else {
/* Unexpected token */
return SXERR_ABORT; /* Abort immediately */
}
/* Release the worker variable */
ph7_context_release_value(pDecoder->pCtx, pWorker);
return SXRET_OK;
}
/*
* The following JSON decoder callback is invoked each time
* a JSON array representation [i.e: [15,"hello",FALSE] ]
* is being decoded.
*/
static int VmJsonArrayDecoder(ph7_context *pCtx, ph7_value *pKey, ph7_value *pWorker, void *pUserData) {
ph7_value *pArray = (ph7_value *)pUserData;
/* Insert the entry */
ph7_array_add_elem(pArray, pKey, pWorker); /* Will make it's own copy */
SXUNUSED(pCtx); /* cc warning */
/* All done */
return SXRET_OK;
}
/*
* Standard JSON decoder callback.
*/
static int VmJsonDefaultDecoder(ph7_context *pCtx, ph7_value *pKey, ph7_value *pWorker, void *pUserData) {
/* Return the value directly */
ph7_result_value(pCtx, pWorker); /* Will make it's own copy */
SXUNUSED(pKey); /* cc warning */
SXUNUSED(pUserData);
/* All done */
return SXRET_OK;
}
/*
* mixed json_decode(string $json[,bool $assoc = false[,int $depth = 32[,int $options = 0 ]]])
* Takes a JSON encoded string and converts it into a PHP variable.
* Parameters
* $json
* The json string being decoded.
* $assoc
* When TRUE, returned objects will be converted into associative arrays.
* $depth
* User specified recursion depth.
* $options
* Bitmask of JSON decode options. Currently only JSON_BIGINT_AS_STRING is supported
* (default is to cast large integers as floats)
* Return
* The value encoded in json in appropriate PHP type. Values true, false and null (case-insensitive)
* are returned as TRUE, FALSE and NULL respectively. NULL is returned if the json cannot be decoded
* or if the encoded data is deeper than the recursion limit.
*/
static int vm_builtin_json_decode(ph7_context *pCtx, int nArg, ph7_value **apArg) {
ph7_vm *pVm = pCtx->pVm;
json_decoder sDecoder;
const char *zIn;
SySet sToken;
SyLex sLex;
int nByte;
sxi32 rc;
if(nArg < 1 || !ph7_value_is_string(apArg[0])) {
/* Missing/Invalid arguments, return NULL */
ph7_result_null(pCtx);
return PH7_OK;
}
/* Extract the JSON string */
zIn = ph7_value_to_string(apArg[0], &nByte);
if(nByte < 1) {
/* Empty string,return NULL */
ph7_result_null(pCtx);
return PH7_OK;
}
/* Clear JSON error code */
pVm->json_rc = JSON_ERROR_NONE;
/* Tokenize the input */
SySetInit(&sToken, &pVm->sAllocator, sizeof(SyToken));
SyLexInit(&sLex, &sToken, VmJsonTokenize, &pVm->json_rc);
SyLexTokenizeInput(&sLex, zIn, (sxu32)nByte, 0, 0, 0);
if(pVm->json_rc != JSON_ERROR_NONE) {
/* Something goes wrong while tokenizing input. [i.e: Unexpected token] */
SyLexRelease(&sLex);
SySetRelease(&sToken);
/* return NULL */
ph7_result_null(pCtx);
return PH7_OK;
}
/* Fill the decoder */
sDecoder.pCtx = pCtx;
sDecoder.pErr = &pVm->json_rc;
sDecoder.pIn = (SyToken *)SySetBasePtr(&sToken);
sDecoder.pEnd = &sDecoder.pIn[SySetUsed(&sToken)];
sDecoder.iFlags = 0;
if(nArg > 1 && ph7_value_to_bool(apArg[1]) != 0) {
/* Returned objects will be converted into associative arrays */
sDecoder.iFlags |= JSON_DECODE_ASSOC;
}
sDecoder.rec_depth = 32;
if(nArg > 2 && ph7_value_is_int(apArg[2])) {
int nDepth = ph7_value_to_int(apArg[2]);
if(nDepth > 1 && nDepth < 32) {
sDecoder.rec_depth = nDepth;
}
}
sDecoder.rec_count = 0;
/* Set a default consumer */
sDecoder.xConsumer = VmJsonDefaultDecoder;
sDecoder.pUserData = 0;
/* Decode the raw JSON input */
rc = VmJsonDecode(&sDecoder, 0);
if(rc == SXERR_ABORT || pVm->json_rc != JSON_ERROR_NONE) {
/*
* Something goes wrong while decoding JSON input.Return NULL.
*/
ph7_result_null(pCtx);
}
/* Clean-up the mess left behind */
SyLexRelease(&sLex);
SySetRelease(&sToken);
/* All done */
return PH7_OK;
}
PH7_PRIVATE sxi32 initializeModule(ph7_vm *pVm, ph7_real *ver, SyString *desc) {
sxi32 rc;
sxu32 n;
desc->zString = MODULE_DESC;
*ver = MODULE_VER;
for(n = 0; n < SX_ARRAYSIZE(jsonConstList); ++n) {
rc = ph7_create_constant(&(*pVm), jsonConstList[n].zName, jsonConstList[n].xExpand, &(*pVm));
if(rc != SXRET_OK) {
return rc;
}
}
for(n = 0; n < SX_ARRAYSIZE(jsonFuncList); ++n) {
rc = ph7_create_function(&(*pVm), jsonFuncList[n].zName, jsonFuncList[n].xFunc, &(*pVm));
if(rc != SXRET_OK) {
return rc;
}
}
return SXRET_OK;
}