mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
2ff01e763e
Old style C functions `strcpy()`, `strcat()` and `sprintf()` are vulnerable to security issues due to lacking memory boundary checks. Replace these in the Connect storage engine with safe new and/or custom functions such as `snprintf()` `safe_strcpy()` and `safe_strcat()`. With this change FlawFinder and other static security analyzers report 287 fewer findings. All new code of the whole pull request, including one or several files that are either new files or modified ones, are contributed under the BSD-new license. I am contributing on behalf of my employer Amazon Web Services, Inc.
2065 lines
55 KiB
C++
2065 lines
55 KiB
C++
/*************** json CPP Declares Source Code File (.H) ***************/
|
|
/* Name: json.cpp Version 1.6 */
|
|
/* */
|
|
/* (C) Copyright to the author Olivier BERTRAND 2014 - 2021 */
|
|
/* */
|
|
/* This file contains the JSON classes functions. */
|
|
/***********************************************************************/
|
|
|
|
/***********************************************************************/
|
|
/* Include relevant sections of the MariaDB header file. */
|
|
/***********************************************************************/
|
|
#include <my_global.h>
|
|
#include <m_string.h>
|
|
|
|
/***********************************************************************/
|
|
/* Include application header files: */
|
|
/* global.h is header containing all global declarations. */
|
|
/* plgdbsem.h is header containing the DB application declarations. */
|
|
/* xjson.h is header containing the JSON classes declarations. */
|
|
/***********************************************************************/
|
|
#include "global.h"
|
|
#include "plgdbsem.h"
|
|
#include "json.h"
|
|
|
|
#define ARGS MY_MIN(24,(int)len-i),s+MY_MAX(i-3,0)
|
|
|
|
#if defined(_WIN32)
|
|
#define EL "\r\n"
|
|
#else
|
|
#define EL "\n"
|
|
#undef SE_CATCH // Does not work for Linux
|
|
#endif
|
|
|
|
#if defined(SE_CATCH)
|
|
/**************************************************************************/
|
|
/* This is the support of catching C interrupts to prevent crashes. */
|
|
/**************************************************************************/
|
|
#include <eh.h>
|
|
|
|
class SE_Exception {
|
|
public:
|
|
SE_Exception(unsigned int n, PEXCEPTION_RECORD p) : nSE(n), eRec(p) {}
|
|
~SE_Exception() {}
|
|
|
|
unsigned int nSE;
|
|
PEXCEPTION_RECORD eRec;
|
|
}; // end of class SE_Exception
|
|
|
|
void trans_func(unsigned int u, _EXCEPTION_POINTERS* pExp)
|
|
{
|
|
throw SE_Exception(u, pExp->ExceptionRecord);
|
|
} // end of trans_func
|
|
|
|
char *GetExceptionDesc(PGLOBAL g, unsigned int e);
|
|
#endif // SE_CATCH
|
|
|
|
char *GetJsonNull(void);
|
|
int GetDefaultPrec(void);
|
|
int PrepareColist(char*);
|
|
|
|
/***********************************************************************/
|
|
/* IsNum: check whether this string is all digits. */
|
|
/***********************************************************************/
|
|
bool IsNum(PSZ s)
|
|
{
|
|
char* p = s;
|
|
|
|
if (*p == '-')
|
|
p++;
|
|
|
|
if (*p == ']')
|
|
return false;
|
|
else for (; *p; p++)
|
|
if (*p == ']')
|
|
break;
|
|
else if (!isdigit(*p))
|
|
return false;
|
|
|
|
return true;
|
|
} // end of IsNum
|
|
|
|
/***********************************************************************/
|
|
/* IsArray: check whether this is a Mongo array path. */
|
|
/***********************************************************************/
|
|
bool IsArray(PSZ s)
|
|
{
|
|
char* p = s;
|
|
|
|
if (!p || !*p)
|
|
return false;
|
|
else for (; *p; p++)
|
|
if (*p == '.')
|
|
break;
|
|
else if (!isdigit(*p))
|
|
return false;
|
|
|
|
return true;
|
|
} // end of IsArray
|
|
|
|
/***********************************************************************/
|
|
/* NextChr: return the first found '[' or Sep pointer. */
|
|
/***********************************************************************/
|
|
char* NextChr(PSZ s, char sep)
|
|
{
|
|
char* p1 = strchr(s, '[');
|
|
char* p2 = strchr(s, sep);
|
|
|
|
if (!p2)
|
|
return p1;
|
|
else if (p1)
|
|
return MY_MIN(p1, p2);
|
|
|
|
return p2;
|
|
} // end of NextChr
|
|
|
|
/***********************************************************************/
|
|
/* Stringified: check that this column is in the stringified list. */
|
|
/***********************************************************************/
|
|
bool Stringified(PCSZ strfy, char *colname)
|
|
{
|
|
if (strfy) {
|
|
char *p, colist[512];
|
|
int n;
|
|
|
|
strncpy(colist, strfy, sizeof(colist) - 1);
|
|
n = PrepareColist(colist);
|
|
|
|
for (p = colist; n && p; p += (strlen(p) + 1), n--)
|
|
if (!stricmp(p, colname))
|
|
return true;
|
|
|
|
} // endif strfy
|
|
|
|
return false;
|
|
} // end of Stringified
|
|
|
|
#if 0
|
|
/***********************************************************************/
|
|
/* Allocate a VAL structure, make sure common field and Nd are zeroed. */
|
|
/***********************************************************************/
|
|
PVL AllocVal(PGLOBAL g, JTYP type)
|
|
{
|
|
PVL vlp = (PVL)PlugSubAlloc(g, NULL, sizeof(VAL));
|
|
|
|
vlp->LLn = 0;
|
|
vlp->Nd = 0;
|
|
vlp->Type = type;
|
|
return vlp;
|
|
} // end of AllocVal
|
|
#endif // 0
|
|
|
|
/***********************************************************************/
|
|
/* Parse a json string. */
|
|
/* Note: when pretty is not known, the caller set pretty to 3. */
|
|
/***********************************************************************/
|
|
PJSON ParseJson(PGLOBAL g, char* s, size_t len, int* ptyp, bool* comma)
|
|
{
|
|
int i, pretty = (ptyp) ? *ptyp : 3;
|
|
bool b = false, pty[3] = { true,true,true };
|
|
PJSON jsp = NULL;
|
|
PJDOC jdp = NULL;
|
|
|
|
if (trace(1))
|
|
htrc("ParseJson: s=%.10s len=%zd\n", s, len);
|
|
|
|
if (!s || !len) {
|
|
snprintf(g->Message, sizeof(g->Message), "Void JSON object");
|
|
return NULL;
|
|
} else if (comma)
|
|
*comma = false;
|
|
|
|
// Trying to guess the pretty format
|
|
if (s[0] == '[' && (s[1] == '\n' || (s[1] == '\r' && s[2] == '\n')))
|
|
pty[0] = false;
|
|
|
|
try {
|
|
jdp = new(g) JDOC;
|
|
jdp->s = s;
|
|
jdp->len = len;
|
|
jdp->pty = pty;
|
|
|
|
for (i = 0; i < jdp->len; i++)
|
|
switch (s[i]) {
|
|
case '[':
|
|
if (jsp)
|
|
jsp = jdp->ParseAsArray(g, i, pretty, ptyp);
|
|
else
|
|
jsp = jdp->ParseArray(g, ++i);
|
|
|
|
break;
|
|
case '{':
|
|
if (jsp)
|
|
jsp = jdp->ParseAsArray(g, i, pretty, ptyp);
|
|
else if (!(jsp = jdp->ParseObject(g, ++i)))
|
|
throw 2;
|
|
|
|
break;
|
|
case ' ':
|
|
case '\t':
|
|
case '\n':
|
|
case '\r':
|
|
break;
|
|
case ',':
|
|
if (jsp && (pretty == 1 || pretty == 3)) {
|
|
if (comma)
|
|
*comma = true;
|
|
|
|
pty[0] = pty[2] = false;
|
|
break;
|
|
} // endif pretty
|
|
|
|
snprintf(g->Message, sizeof(g->Message), "Unexpected ',' (pretty=%d)", pretty);
|
|
throw 3;
|
|
case '(':
|
|
b = true;
|
|
break;
|
|
case ')':
|
|
if (b) {
|
|
b = false;
|
|
break;
|
|
} // endif b
|
|
/* falls through */
|
|
default:
|
|
if (jsp)
|
|
jsp = jdp->ParseAsArray(g, i, pretty, ptyp);
|
|
else if (!(jsp = jdp->ParseValue(g, i)))
|
|
throw 4;
|
|
|
|
break;
|
|
}; // endswitch s[i]
|
|
|
|
if (!jsp)
|
|
snprintf(g->Message, sizeof(g->Message), "Invalid Json string '%.*s'", MY_MIN((int)len, 50), s);
|
|
else if (ptyp && pretty == 3) {
|
|
*ptyp = 3; // Not recognized pretty
|
|
|
|
for (i = 0; i < 3; i++)
|
|
if (pty[i]) {
|
|
*ptyp = i;
|
|
break;
|
|
} // endif pty
|
|
|
|
} // endif ptyp
|
|
|
|
} catch (int n) {
|
|
if (trace(1))
|
|
htrc("Exception %d: %s\n", n, g->Message);
|
|
jsp = NULL;
|
|
} catch (const char* msg) {
|
|
snprintf(g->Message, sizeof(g->Message), "%s", msg);
|
|
jsp = NULL;
|
|
} // end catch
|
|
|
|
return jsp;
|
|
} // end of ParseJson
|
|
|
|
/***********************************************************************/
|
|
/* Serialize a JSON document tree: */
|
|
/***********************************************************************/
|
|
PSZ Serialize(PGLOBAL g, PJSON jsp, char* fn, int pretty) {
|
|
PSZ str = NULL;
|
|
bool b = false, err = true;
|
|
JOUT* jp;
|
|
FILE* fs = NULL;
|
|
PJDOC jdp = NULL;
|
|
|
|
g->Message[0] = 0;
|
|
|
|
try {
|
|
jdp = new(g) JDOC; // MUST BE ALLOCATED BEFORE jp !!!!!
|
|
jdp->dfp = GetDefaultPrec();
|
|
|
|
if (!jsp) {
|
|
snprintf(g->Message, sizeof(g->Message), "Null json tree");
|
|
throw 1;
|
|
} else if (!fn) {
|
|
// Serialize to a string
|
|
jp = new(g) JOUTSTR(g);
|
|
b = pretty == 1;
|
|
} else {
|
|
if (!(fs = fopen(fn, "wb"))) {
|
|
snprintf(g->Message, sizeof(g->Message), MSG(OPEN_MODE_ERROR) ": %s",
|
|
"w", (int)errno, fn, strerror(errno));
|
|
throw 2;
|
|
} else if (pretty >= 2) {
|
|
// Serialize to a pretty file
|
|
jp = new(g)JOUTPRT(g, fs);
|
|
} else {
|
|
// Serialize to a flat file
|
|
b = true;
|
|
jp = new(g)JOUTFILE(g, fs, pretty);
|
|
} // endif's
|
|
|
|
} // endif's
|
|
|
|
jdp->SetJp(jp);
|
|
|
|
switch (jsp->GetType()) {
|
|
case TYPE_JAR:
|
|
err = jdp->SerializeArray((PJAR)jsp, b);
|
|
break;
|
|
case TYPE_JOB:
|
|
err = ((b && jp->Prty()) && jp->WriteChr('\t'));
|
|
err |= jdp->SerializeObject((PJOB)jsp);
|
|
break;
|
|
case TYPE_JVAL:
|
|
err = jdp->SerializeValue((PJVAL)jsp);
|
|
break;
|
|
default:
|
|
snprintf(g->Message, sizeof(g->Message), "Invalid json tree");
|
|
} // endswitch Type
|
|
|
|
if (fs) {
|
|
fputs(EL, fs);
|
|
fclose(fs);
|
|
if(err) {
|
|
str = NULL;
|
|
} else {
|
|
snprintf(g->Message, sizeof(g->Message), "Ok");
|
|
str = g->Message;
|
|
}
|
|
} else if (!err) {
|
|
str = ((JOUTSTR*)jp)->Strp;
|
|
jp->WriteChr('\0');
|
|
PlugSubAlloc(g, NULL, ((JOUTSTR*)jp)->N);
|
|
} else {
|
|
if (!g->Message[0])
|
|
snprintf(g->Message, sizeof(g->Message), "Error in Serialize");
|
|
|
|
} // endif's
|
|
|
|
} catch (int n) {
|
|
if (trace(1))
|
|
htrc("Exception %d: %s\n", n, g->Message);
|
|
str = NULL;
|
|
} catch (const char* msg) {
|
|
snprintf(g->Message, sizeof(g->Message), "%s", msg);
|
|
str = NULL;
|
|
} // end catch
|
|
|
|
return str;
|
|
} // end of Serialize
|
|
|
|
|
|
/* -------------------------- Class JOUTSTR -------------------------- */
|
|
|
|
/***********************************************************************/
|
|
/* JOUTSTR constructor. */
|
|
/***********************************************************************/
|
|
JOUTSTR::JOUTSTR(PGLOBAL g) : JOUT(g) {
|
|
PPOOLHEADER pph = (PPOOLHEADER)g->Sarea;
|
|
|
|
N = 0;
|
|
Max = pph->FreeBlk;
|
|
Max = (Max > 32) ? Max - 32 : Max;
|
|
Strp = (char*)PlugSubAlloc(g, NULL, 0); // Size not know yet
|
|
} // end of JOUTSTR constructor
|
|
|
|
/***********************************************************************/
|
|
/* Concatenate a string to the Serialize string. */
|
|
/***********************************************************************/
|
|
bool JOUTSTR::WriteStr(const char* s) {
|
|
if (s) {
|
|
size_t len = strlen(s);
|
|
|
|
if (N + len > Max)
|
|
return true;
|
|
|
|
memcpy(Strp + N, s, len);
|
|
N += len;
|
|
return false;
|
|
} else
|
|
return true;
|
|
|
|
} // end of WriteStr
|
|
|
|
/***********************************************************************/
|
|
/* Concatenate a character to the Serialize string. */
|
|
/***********************************************************************/
|
|
bool JOUTSTR::WriteChr(const char c) {
|
|
if (N + 1 > Max)
|
|
return true;
|
|
|
|
Strp[N++] = c;
|
|
return false;
|
|
} // end of WriteChr
|
|
|
|
/***********************************************************************/
|
|
/* Escape and Concatenate a string to the Serialize string. */
|
|
/***********************************************************************/
|
|
bool JOUTSTR::Escape(const char* s)
|
|
{
|
|
if (s) {
|
|
WriteChr('"');
|
|
|
|
for (unsigned int i = 0; s[i]; i++)
|
|
switch (s[i]) {
|
|
case '"':
|
|
case '\\':
|
|
case '\t':
|
|
case '\n':
|
|
case '\r':
|
|
case '\b':
|
|
case '\f': WriteChr('\\');
|
|
// fall through
|
|
default:
|
|
WriteChr(s[i]);
|
|
break;
|
|
} // endswitch s[i]
|
|
|
|
WriteChr('"');
|
|
} else
|
|
WriteStr("null");
|
|
|
|
return false;
|
|
} // end of Escape
|
|
|
|
/* ------------------------- Class JOUTFILE -------------------------- */
|
|
|
|
/***********************************************************************/
|
|
/* Write a string to the Serialize file. */
|
|
/***********************************************************************/
|
|
bool JOUTFILE::WriteStr(const char* s)
|
|
{
|
|
// This is temporary
|
|
fputs(s, Stream);
|
|
return false;
|
|
} // end of WriteStr
|
|
|
|
/***********************************************************************/
|
|
/* Write a character to the Serialize file. */
|
|
/***********************************************************************/
|
|
bool JOUTFILE::WriteChr(const char c)
|
|
{
|
|
// This is temporary
|
|
fputc(c, Stream);
|
|
return false;
|
|
} // end of WriteChr
|
|
|
|
/***********************************************************************/
|
|
/* Escape and Concatenate a string to the Serialize string. */
|
|
/***********************************************************************/
|
|
bool JOUTFILE::Escape(const char* s)
|
|
{
|
|
// This is temporary
|
|
if (s) {
|
|
fputc('"', Stream);
|
|
|
|
for (unsigned int i = 0; s[i]; i++)
|
|
switch (s[i]) {
|
|
case '"': fputs("\\\"", Stream); break;
|
|
case '\\': fputs("\\\\", Stream); break;
|
|
case '\t': fputs("\\t", Stream); break;
|
|
case '\n': fputs("\\n", Stream); break;
|
|
case '\r': fputs("\\r", Stream); break;
|
|
case '\b': fputs("\\b", Stream); break;
|
|
case '\f': fputs("\\f", Stream); break;
|
|
default:
|
|
fputc(s[i], Stream);
|
|
break;
|
|
} // endswitch s[i]
|
|
|
|
fputc('"', Stream);
|
|
} else
|
|
fputs("null", Stream);
|
|
|
|
return false;
|
|
} // end of Escape
|
|
|
|
/* ------------------------- Class JOUTPRT --------------------------- */
|
|
|
|
/***********************************************************************/
|
|
/* Write a string to the Serialize pretty file. */
|
|
/***********************************************************************/
|
|
bool JOUTPRT::WriteStr(const char* s)
|
|
{
|
|
// This is temporary
|
|
if (B) {
|
|
fputs(EL, Stream);
|
|
M--;
|
|
|
|
for (int i = 0; i < M; i++)
|
|
fputc('\t', Stream);
|
|
|
|
B = false;
|
|
} // endif B
|
|
|
|
fputs(s, Stream);
|
|
return false;
|
|
} // end of WriteStr
|
|
|
|
/***********************************************************************/
|
|
/* Write a character to the Serialize pretty file. */
|
|
/***********************************************************************/
|
|
bool JOUTPRT::WriteChr(const char c)
|
|
{
|
|
switch (c) {
|
|
case ':':
|
|
fputs(": ", Stream);
|
|
break;
|
|
case '{':
|
|
case '[':
|
|
#if 0
|
|
if (M)
|
|
fputs(EL, Stream);
|
|
|
|
for (int i = 0; i < M; i++)
|
|
fputc('\t', Stream);
|
|
#endif // 0
|
|
|
|
fputc(c, Stream);
|
|
fputs(EL, Stream);
|
|
M++;
|
|
|
|
for (int i = 0; i < M; i++)
|
|
fputc('\t', Stream);
|
|
|
|
break;
|
|
case '}':
|
|
case ']':
|
|
M--;
|
|
fputs(EL, Stream);
|
|
|
|
for (int i = 0; i < M; i++)
|
|
fputc('\t', Stream);
|
|
|
|
fputc(c, Stream);
|
|
B = true;
|
|
break;
|
|
case ',':
|
|
fputc(c, Stream);
|
|
fputs(EL, Stream);
|
|
|
|
for (int i = 0; i < M; i++)
|
|
fputc('\t', Stream);
|
|
|
|
B = false;
|
|
break;
|
|
default:
|
|
fputc(c, Stream);
|
|
} // endswitch c
|
|
|
|
return false;
|
|
} // end of WriteChr
|
|
|
|
/* --------------------------- Class JDOC ---------------------------- */
|
|
|
|
/***********************************************************************/
|
|
/* Parse several items as being in an array. */
|
|
/***********************************************************************/
|
|
PJAR JDOC::ParseAsArray(PGLOBAL g, int& i, int pretty, int *ptyp)
|
|
{
|
|
if (pty[0] && (!pretty || pretty > 2)) {
|
|
PJAR jsp;
|
|
|
|
if ((jsp = ParseArray(g, (i = 0))) && ptyp && pretty == 3)
|
|
*ptyp = (pty[0]) ? 0 : 3;
|
|
|
|
return jsp;
|
|
} else
|
|
snprintf(g->Message, sizeof(g->Message), "More than one item in file");
|
|
|
|
return NULL;
|
|
} // end of ParseAsArray
|
|
|
|
/***********************************************************************/
|
|
/* Parse a JSON Array. */
|
|
/***********************************************************************/
|
|
PJAR JDOC::ParseArray(PGLOBAL g, int& i)
|
|
{
|
|
int level = 0;
|
|
bool b = (!i);
|
|
PJAR jarp = new(g) JARRAY;
|
|
|
|
for (; i < len; i++)
|
|
switch (s[i]) {
|
|
case ',':
|
|
if (level < 2) {
|
|
snprintf(g->Message, sizeof(g->Message), "Unexpected ',' near %.*s",ARGS);
|
|
throw 1;
|
|
} else
|
|
level = 1;
|
|
|
|
break;
|
|
case ']':
|
|
if (level == 1) {
|
|
snprintf(g->Message, sizeof(g->Message), "Unexpected ',]' near %.*s", ARGS);
|
|
throw 1;
|
|
} // endif level
|
|
|
|
jarp->InitArray(g);
|
|
return jarp;
|
|
case '\n':
|
|
if (!b)
|
|
pty[0] = pty[1] = false;
|
|
case '\r':
|
|
case ' ':
|
|
case '\t':
|
|
break;
|
|
default:
|
|
if (level == 2) {
|
|
snprintf(g->Message, sizeof(g->Message), "Unexpected value near %.*s", ARGS);
|
|
throw 1;
|
|
} else
|
|
jarp->AddArrayValue(g, ParseValue(g, i));
|
|
|
|
level = (b) ? 1 : 2;
|
|
break;
|
|
}; // endswitch s[i]
|
|
|
|
if (b) {
|
|
// Case of Pretty == 0
|
|
jarp->InitArray(g);
|
|
return jarp;
|
|
} // endif b
|
|
|
|
throw ("Unexpected EOF in array");
|
|
} // end of ParseArray
|
|
|
|
/***********************************************************************/
|
|
/* Parse a JSON Object. */
|
|
/***********************************************************************/
|
|
PJOB JDOC::ParseObject(PGLOBAL g, int& i)
|
|
{
|
|
PSZ key;
|
|
int level = -1;
|
|
PJOB jobp = new(g) JOBJECT;
|
|
PJPR jpp = NULL;
|
|
|
|
for (; i < len; i++)
|
|
switch (s[i]) {
|
|
case '"':
|
|
if (level < 2) {
|
|
key = ParseString(g, ++i);
|
|
jpp = jobp->AddPair(g, key);
|
|
level = 1;
|
|
} else {
|
|
snprintf(g->Message, sizeof(g->Message), "misplaced string near %.*s", ARGS);
|
|
throw 2;
|
|
} // endif level
|
|
|
|
break;
|
|
case ':':
|
|
if (level == 1) {
|
|
jpp->Val = ParseValue(g, ++i);
|
|
level = 2;
|
|
} else {
|
|
snprintf(g->Message, sizeof(g->Message), "Unexpected ':' near %.*s", ARGS);
|
|
throw 2;
|
|
} // endif level
|
|
|
|
break;
|
|
case ',':
|
|
if (level < 2) {
|
|
snprintf(g->Message, sizeof(g->Message), "Unexpected ',' near %.*s", ARGS);
|
|
throw 2;
|
|
} else
|
|
level = 0;
|
|
|
|
break;
|
|
case '}':
|
|
if (level == 0 || level == 1) {
|
|
snprintf(g->Message, sizeof(g->Message), "Unexpected '}' near %.*s", ARGS);
|
|
throw 2;
|
|
} // endif level
|
|
|
|
return jobp;
|
|
case '\n':
|
|
pty[0] = pty[1] = false;
|
|
case '\r':
|
|
case ' ':
|
|
case '\t':
|
|
break;
|
|
default:
|
|
snprintf(g->Message, sizeof(g->Message), "Unexpected character '%c' near %.*s",
|
|
s[i], ARGS);
|
|
throw 2;
|
|
}; // endswitch s[i]
|
|
|
|
snprintf(g->Message, sizeof(g->Message), "Unexpected EOF in Object");
|
|
throw 2;
|
|
} // end of ParseObject
|
|
|
|
/***********************************************************************/
|
|
/* Parse a JSON Value. */
|
|
/***********************************************************************/
|
|
PJVAL JDOC::ParseValue(PGLOBAL g, int& i)
|
|
{
|
|
PJVAL jvp = new(g) JVALUE;
|
|
|
|
for (; i < len; i++)
|
|
switch (s[i]) {
|
|
case '\n':
|
|
pty[0] = pty[1] = false;
|
|
case '\r':
|
|
case ' ':
|
|
case '\t':
|
|
break;
|
|
default:
|
|
goto suite;
|
|
} // endswitch
|
|
|
|
suite:
|
|
switch (s[i]) {
|
|
case '[':
|
|
jvp->Jsp = ParseArray(g, ++i);
|
|
jvp->DataType = TYPE_JSON;
|
|
break;
|
|
case '{':
|
|
jvp->Jsp = ParseObject(g, ++i);
|
|
jvp->DataType = TYPE_JSON;
|
|
break;
|
|
case '"':
|
|
// jvp->Val = AllocVal(g, TYPE_STRG);
|
|
jvp->Strp = ParseString(g, ++i);
|
|
jvp->DataType = TYPE_STRG;
|
|
break;
|
|
case 't':
|
|
if (!strncmp(s + i, "true", 4)) {
|
|
// jvp->Val = AllocVal(g, TYPE_BOOL);
|
|
jvp->B = true;
|
|
jvp->DataType = TYPE_BOOL;
|
|
i += 3;
|
|
} else
|
|
goto err;
|
|
|
|
break;
|
|
case 'f':
|
|
if (!strncmp(s + i, "false", 5)) {
|
|
// jvp->Val = AllocVal(g, TYPE_BOOL);
|
|
jvp->B = false;
|
|
jvp->DataType = TYPE_BOOL;
|
|
i += 4;
|
|
} else
|
|
goto err;
|
|
|
|
break;
|
|
case 'n':
|
|
if (!strncmp(s + i, "null", 4)) {
|
|
jvp->DataType = TYPE_NULL;
|
|
i += 3;
|
|
} else
|
|
goto err;
|
|
|
|
break;
|
|
case '-':
|
|
default:
|
|
if (s[i] == '-' || isdigit(s[i]))
|
|
ParseNumeric(g, i, jvp);
|
|
else
|
|
goto err;
|
|
|
|
}; // endswitch s[i]
|
|
|
|
return jvp;
|
|
|
|
err:
|
|
snprintf(g->Message, sizeof(g->Message), "Unexpected character '%c' near %.*s", s[i], ARGS);
|
|
throw 3;
|
|
} // end of ParseValue
|
|
|
|
/***********************************************************************/
|
|
/* Unescape and parse a JSON string. */
|
|
/***********************************************************************/
|
|
char *JDOC::ParseString(PGLOBAL g, int& i)
|
|
{
|
|
uchar *p;
|
|
int n = 0;
|
|
|
|
// Be sure of memory availability
|
|
if (((size_t)len + 1 - i) > ((PPOOLHEADER)g->Sarea)->FreeBlk)
|
|
throw("ParseString: Out of memory");
|
|
|
|
// The size to allocate is not known yet
|
|
p = (uchar*)PlugSubAlloc(g, NULL, 0);
|
|
|
|
for (; i < len; i++)
|
|
switch (s[i]) {
|
|
case '"':
|
|
p[n++] = 0;
|
|
PlugSubAlloc(g, NULL, n);
|
|
return (char*)p;
|
|
case '\\':
|
|
if (++i < len) {
|
|
if (s[i] == 'u') {
|
|
if (len - i > 5) {
|
|
// if (charset == utf8) {
|
|
char xs[5];
|
|
uint hex;
|
|
|
|
xs[0] = s[++i];
|
|
xs[1] = s[++i];
|
|
xs[2] = s[++i];
|
|
xs[3] = s[++i];
|
|
xs[4] = 0;
|
|
hex = strtoul(xs, NULL, 16);
|
|
|
|
if (hex < 0x80) {
|
|
p[n] = (uchar)hex;
|
|
} else if (hex < 0x800) {
|
|
p[n++] = (uchar)(0xC0 | (hex >> 6));
|
|
p[n] = (uchar)(0x80 | (hex & 0x3F));
|
|
} else if (hex < 0x10000) {
|
|
p[n++] = (uchar)(0xE0 | (hex >> 12));
|
|
p[n++] = (uchar)(0x80 | ((hex >> 6) & 0x3f));
|
|
p[n] = (uchar)(0x80 | (hex & 0x3f));
|
|
} else
|
|
p[n] = '?';
|
|
|
|
#if 0
|
|
} else {
|
|
char xs[3];
|
|
UINT hex;
|
|
|
|
i += 2;
|
|
xs[0] = s[++i];
|
|
xs[1] = s[++i];
|
|
xs[2] = 0;
|
|
hex = strtoul(xs, NULL, 16);
|
|
p[n] = (char)hex;
|
|
} // endif charset
|
|
#endif // 0
|
|
} else
|
|
goto err;
|
|
|
|
} else switch(s[i]) {
|
|
case 't': p[n] = '\t'; break;
|
|
case 'n': p[n] = '\n'; break;
|
|
case 'r': p[n] = '\r'; break;
|
|
case 'b': p[n] = '\b'; break;
|
|
case 'f': p[n] = '\f'; break;
|
|
default: p[n] = s[i]; break;
|
|
} // endswitch
|
|
|
|
n++;
|
|
} else
|
|
goto err;
|
|
|
|
break;
|
|
default:
|
|
p[n++] = s[i];
|
|
break;
|
|
}; // endswitch s[i]
|
|
|
|
err:
|
|
throw("Unexpected EOF in String");
|
|
} // end of ParseString
|
|
|
|
/***********************************************************************/
|
|
/* Parse a JSON numeric value. */
|
|
/***********************************************************************/
|
|
void JDOC::ParseNumeric(PGLOBAL g, int& i, PJVAL vlp)
|
|
{
|
|
char buf[50];
|
|
int n = 0;
|
|
short nd = 0;
|
|
bool has_dot = false;
|
|
bool has_e = false;
|
|
bool found_digit = false;
|
|
//PVL vlp = NULL;
|
|
|
|
for (; i < len; i++) {
|
|
switch (s[i]) {
|
|
case '.':
|
|
if (!found_digit || has_dot || has_e)
|
|
goto err;
|
|
|
|
has_dot = true;
|
|
break;
|
|
case 'e':
|
|
case 'E':
|
|
if (!found_digit || has_e)
|
|
goto err;
|
|
|
|
has_e = true;
|
|
found_digit = false;
|
|
break;
|
|
case '+':
|
|
if (!has_e)
|
|
goto err;
|
|
|
|
// fall through
|
|
case '-':
|
|
if (found_digit)
|
|
goto err;
|
|
|
|
break;
|
|
default:
|
|
if (isdigit(s[i])) {
|
|
if (has_dot && !has_e)
|
|
nd++; // Number of decimals
|
|
|
|
found_digit = true;
|
|
} else
|
|
goto fin;
|
|
|
|
}; // endswitch s[i]
|
|
|
|
buf[n++] = s[i];
|
|
} // endfor i
|
|
|
|
fin:
|
|
if (found_digit) {
|
|
buf[n] = 0;
|
|
|
|
if (has_dot || has_e) {
|
|
double dv = strtod(buf, NULL);
|
|
|
|
// vlp = AllocVal(g, TYPE_DBL);
|
|
vlp->F = dv;
|
|
vlp->Nd = nd;
|
|
vlp->DataType = TYPE_DBL;
|
|
} else {
|
|
long long iv = strtoll(buf, NULL, 10);
|
|
|
|
if (iv > INT_MAX32 || iv < INT_MIN32) {
|
|
// vlp = AllocVal(g, TYPE_BINT);
|
|
vlp->LLn = iv;
|
|
vlp->DataType = TYPE_BINT;
|
|
} else {
|
|
// vlp = AllocVal(g, TYPE_INTG);
|
|
vlp->N = (int)iv;
|
|
vlp->DataType = TYPE_INTG;
|
|
} // endif iv
|
|
|
|
} // endif has
|
|
|
|
i--; // Unstack following character
|
|
return;
|
|
} else
|
|
throw("No digit found");
|
|
|
|
err:
|
|
throw("Unexpected EOF in number");
|
|
} // end of ParseNumeric
|
|
|
|
/***********************************************************************/
|
|
/* Serialize a JSON Array. */
|
|
/***********************************************************************/
|
|
bool JDOC::SerializeArray(PJAR jarp, bool b)
|
|
{
|
|
bool first = true;
|
|
|
|
if (b) {
|
|
if (js->Prty()) {
|
|
if (js->WriteChr('['))
|
|
return true;
|
|
else if (js->Prty() == 1 && (js->WriteStr(EL) || js->WriteChr('\t')))
|
|
return true;
|
|
|
|
} // endif Prty
|
|
|
|
} else if (js->WriteChr('['))
|
|
return true;
|
|
|
|
for (int i = 0; i < jarp->size(); i++) {
|
|
if (first)
|
|
first = false;
|
|
else if ((!b || js->Prty()) && js->WriteChr(','))
|
|
return true;
|
|
else if (b) {
|
|
if (js->Prty() < 2 && js->WriteStr(EL))
|
|
return true;
|
|
else if (js->Prty() == 1 && js->WriteChr('\t'))
|
|
return true;
|
|
|
|
} // endif b
|
|
|
|
if (SerializeValue(jarp->GetArrayValue(i)))
|
|
return true;
|
|
|
|
} // endfor i
|
|
|
|
if (b && js->Prty() == 1 && js->WriteStr(EL))
|
|
return true;
|
|
|
|
return ((!b || js->Prty()) && js->WriteChr(']'));
|
|
} // end of SerializeArray
|
|
|
|
/***********************************************************************/
|
|
/* Serialize a JSON Object. */
|
|
/***********************************************************************/
|
|
bool JDOC::SerializeObject(PJOB jobp)
|
|
{
|
|
bool first = true;
|
|
|
|
if (js->WriteChr('{'))
|
|
return true;
|
|
|
|
for (PJPR pair = jobp->GetFirst(); pair; pair = pair->Next) {
|
|
if (first)
|
|
first = false;
|
|
else if (js->WriteChr(','))
|
|
return true;
|
|
|
|
if (js->WriteChr('"') ||
|
|
js->WriteStr(pair->Key) ||
|
|
js->WriteChr('"') ||
|
|
js->WriteChr(':') ||
|
|
SerializeValue(pair->Val))
|
|
return true;
|
|
|
|
} // endfor i
|
|
|
|
return js->WriteChr('}');
|
|
} // end of SerializeObject
|
|
|
|
/***********************************************************************/
|
|
/* Serialize a JSON Value. */
|
|
/***********************************************************************/
|
|
bool JDOC::SerializeValue(PJVAL jvp)
|
|
{
|
|
char buf[64];
|
|
PJAR jap;
|
|
PJOB jop;
|
|
//PVL vlp;
|
|
|
|
if ((jap = jvp->GetArray()))
|
|
return SerializeArray(jap, false);
|
|
else if ((jop = jvp->GetObject()))
|
|
return SerializeObject(jop);
|
|
//else if (!(vlp = jvp->Val))
|
|
// return js->WriteStr("null");
|
|
else switch (jvp->DataType) {
|
|
case TYPE_BOOL:
|
|
return js->WriteStr(jvp->B ? "true" : "false");
|
|
case TYPE_STRG:
|
|
case TYPE_DTM:
|
|
return js->Escape(jvp->Strp);
|
|
case TYPE_INTG:
|
|
snprintf(buf, sizeof(buf), "%d", jvp->N);
|
|
return js->WriteStr(buf);
|
|
case TYPE_BINT:
|
|
snprintf(buf, sizeof(buf), "%lld", jvp->LLn);
|
|
return js->WriteStr(buf);
|
|
case TYPE_DBL: // dfp to limit to the default number of decimals
|
|
snprintf(buf, sizeof(buf), "%.*f", MY_MIN(jvp->Nd, dfp), jvp->F);
|
|
return js->WriteStr(buf);
|
|
case TYPE_NULL:
|
|
return js->WriteStr("null");
|
|
default:
|
|
return js->WriteStr("???"); // TODO
|
|
} // endswitch Type
|
|
|
|
strcpy(js->g->Message, "Unrecognized value");
|
|
return true;
|
|
} // end of SerializeValue
|
|
|
|
/* -------------------------- Class JOBJECT -------------------------- */
|
|
|
|
/***********************************************************************/
|
|
/* Return the number of pairs in this object. */
|
|
/***********************************************************************/
|
|
int JOBJECT::GetSize(bool b) {
|
|
int n = 0;
|
|
|
|
for (PJPR jpp = First; jpp; jpp = jpp->Next)
|
|
// If b return only non null pairs
|
|
if (!b || jpp->Val && !jpp->Val->IsNull())
|
|
n++;
|
|
|
|
return n;
|
|
} // end of GetSize
|
|
|
|
/***********************************************************************/
|
|
/* Add a new pair to an Object. */
|
|
/***********************************************************************/
|
|
PJPR JOBJECT::AddPair(PGLOBAL g, PCSZ key)
|
|
{
|
|
PJPR jpp = (PJPR)PlugSubAlloc(g, NULL, sizeof(JPAIR));
|
|
|
|
jpp->Key = key;
|
|
jpp->Next = NULL;
|
|
jpp->Val = NULL;
|
|
|
|
if (Last)
|
|
Last->Next = jpp;
|
|
else
|
|
First = jpp;
|
|
|
|
Last = jpp;
|
|
return jpp;
|
|
} // end of AddPair
|
|
|
|
/***********************************************************************/
|
|
/* Return all keys as an array. */
|
|
/***********************************************************************/
|
|
PJAR JOBJECT::GetKeyList(PGLOBAL g)
|
|
{
|
|
PJAR jarp = new(g) JARRAY();
|
|
|
|
for (PJPR jpp = First; jpp; jpp = jpp->Next)
|
|
jarp->AddArrayValue(g, new(g) JVALUE(g, jpp->Key));
|
|
|
|
jarp->InitArray(g);
|
|
return jarp;
|
|
} // end of GetKeyList
|
|
|
|
/***********************************************************************/
|
|
/* Return all values as an array. */
|
|
/***********************************************************************/
|
|
PJAR JOBJECT::GetValList(PGLOBAL g)
|
|
{
|
|
PJAR jarp = new(g) JARRAY();
|
|
|
|
for (PJPR jpp = First; jpp; jpp = jpp->Next)
|
|
jarp->AddArrayValue(g, jpp->Val);
|
|
|
|
jarp->InitArray(g);
|
|
return jarp;
|
|
} // end of GetValList
|
|
|
|
/***********************************************************************/
|
|
/* Get the value corresponding to the given key. */
|
|
/***********************************************************************/
|
|
PJVAL JOBJECT::GetKeyValue(const char* key)
|
|
{
|
|
for (PJPR jp = First; jp; jp = jp->Next)
|
|
if (!strcmp(jp->Key, key))
|
|
return jp->Val;
|
|
|
|
return NULL;
|
|
} // end of GetValue;
|
|
|
|
/***********************************************************************/
|
|
/* Return the text corresponding to all keys (XML like). */
|
|
/***********************************************************************/
|
|
PSZ JOBJECT::GetText(PGLOBAL g, PSTRG text)
|
|
{
|
|
if (First) {
|
|
bool b;
|
|
|
|
if (!text) {
|
|
text = new(g) STRING(g, 256);
|
|
b = true;
|
|
} else {
|
|
if (text->GetLastChar() != ' ')
|
|
text->Append(' ');
|
|
|
|
b = false;
|
|
} // endif text
|
|
|
|
if (b && !First->Next && !strcmp(First->Key, "$date")) {
|
|
int i;
|
|
PSZ s;
|
|
|
|
First->Val->GetText(g, text);
|
|
s = text->GetStr();
|
|
i = (s[1] == '-' ? 2 : 1);
|
|
|
|
if (IsNum(s + i)) {
|
|
// Date is in milliseconds
|
|
int j = text->GetLength();
|
|
|
|
if (j >= 4 + i) {
|
|
s[j - 3] = 0; // Change it to seconds
|
|
text->SetLength((uint)strlen(s));
|
|
} else
|
|
text->Set(" 0");
|
|
|
|
} // endif text
|
|
|
|
} else for (PJPR jp = First; jp; jp = jp->Next) {
|
|
jp->Val->GetText(g, text);
|
|
|
|
if (jp->Next)
|
|
text->Append(' ');
|
|
|
|
} // endfor jp
|
|
|
|
if (b) {
|
|
text->Trim();
|
|
return text->GetStr();
|
|
} // endif b
|
|
|
|
} // endif First
|
|
|
|
return NULL;
|
|
} // end of GetText;
|
|
|
|
/***********************************************************************/
|
|
/* Merge two objects. */
|
|
/***********************************************************************/
|
|
bool JOBJECT::Merge(PGLOBAL g, PJSON jsp)
|
|
{
|
|
if (jsp->GetType() != TYPE_JOB) {
|
|
snprintf(g->Message, sizeof(g->Message), "Second argument is not an object");
|
|
return true;
|
|
} // endif Type
|
|
|
|
PJOB jobp = (PJOB)jsp;
|
|
|
|
for (PJPR jpp = jobp->First; jpp; jpp = jpp->Next)
|
|
SetKeyValue(g, jpp->Val, jpp->Key);
|
|
|
|
return false;
|
|
} // end of Marge;
|
|
|
|
/***********************************************************************/
|
|
/* Set or add a value corresponding to the given key. */
|
|
/***********************************************************************/
|
|
void JOBJECT::SetKeyValue(PGLOBAL g, PJVAL jvp, PCSZ key)
|
|
{
|
|
PJPR jp;
|
|
|
|
for (jp = First; jp; jp = jp->Next)
|
|
if (!strcmp(jp->Key, key)) {
|
|
jp->Val = jvp;
|
|
break;
|
|
} // endif key
|
|
|
|
if (!jp) {
|
|
jp = AddPair(g, key);
|
|
jp->Val = jvp;
|
|
} // endif jp
|
|
|
|
} // end of SetValue
|
|
|
|
/***********************************************************************/
|
|
/* Delete a value corresponding to the given key. */
|
|
/***********************************************************************/
|
|
void JOBJECT::DeleteKey(PCSZ key)
|
|
{
|
|
PJPR jp, *pjp = &First;
|
|
|
|
for (jp = First; jp; jp = jp->Next)
|
|
if (!strcmp(jp->Key, key)) {
|
|
*pjp = jp->Next;
|
|
break;
|
|
} else
|
|
pjp = &jp->Next;
|
|
|
|
} // end of DeleteKey
|
|
|
|
/***********************************************************************/
|
|
/* True if void or if all members are nulls. */
|
|
/***********************************************************************/
|
|
bool JOBJECT::IsNull(void)
|
|
{
|
|
for (PJPR jp = First; jp; jp = jp->Next)
|
|
if (!jp->Val->IsNull())
|
|
return false;
|
|
|
|
return true;
|
|
} // end of IsNull
|
|
|
|
/* -------------------------- Class JARRAY --------------------------- */
|
|
|
|
/***********************************************************************/
|
|
/* JARRAY constructor. */
|
|
/***********************************************************************/
|
|
JARRAY::JARRAY(void) : JSON()
|
|
{
|
|
Type = TYPE_JAR;
|
|
Size = 0;
|
|
Alloc = 0;
|
|
First = Last = NULL;
|
|
Mvals = NULL;
|
|
} // end of JARRAY constructor
|
|
|
|
/***********************************************************************/
|
|
/* Return the number of values in this object. */
|
|
/***********************************************************************/
|
|
int JARRAY::GetSize(bool b)
|
|
{
|
|
if (b) {
|
|
// Return only non null values
|
|
int n = 0;
|
|
|
|
for (PJVAL jvp = First; jvp; jvp = jvp->Next)
|
|
if (!jvp->IsNull())
|
|
n++;
|
|
|
|
return n;
|
|
} else
|
|
return Size;
|
|
|
|
} // end of GetSize
|
|
|
|
/***********************************************************************/
|
|
/* Make the array of values from the values list. */
|
|
/***********************************************************************/
|
|
void JARRAY::InitArray(PGLOBAL g)
|
|
{
|
|
int i;
|
|
PJVAL jvp, *pjvp = &First;
|
|
|
|
for (Size = 0, jvp = First; jvp; jvp = jvp->Next)
|
|
if (!jvp->Del)
|
|
Size++;
|
|
|
|
if (Size > Alloc) {
|
|
// No need to realloc after deleting values
|
|
Mvals = (PJVAL*)PlugSubAlloc(g, NULL, Size * sizeof(PJVAL));
|
|
Alloc = Size;
|
|
} // endif Size
|
|
|
|
for (i = 0, jvp = First; jvp; jvp = jvp->Next)
|
|
if (!jvp->Del) {
|
|
Mvals[i++] = jvp;
|
|
pjvp = &jvp->Next;
|
|
Last = jvp;
|
|
} else
|
|
*pjvp = jvp->Next;
|
|
|
|
} // end of InitArray
|
|
|
|
/***********************************************************************/
|
|
/* Get the Nth value of an Array. */
|
|
/***********************************************************************/
|
|
PJVAL JARRAY::GetArrayValue(int i)
|
|
{
|
|
if (Mvals && i >= 0 && i < Size)
|
|
return Mvals[i];
|
|
else if (Mvals && i < 0 && i >= -Size)
|
|
return Mvals[Size + i];
|
|
else
|
|
return NULL;
|
|
} // end of GetValue
|
|
|
|
/***********************************************************************/
|
|
/* Add a Value to the Array Value list. */
|
|
/***********************************************************************/
|
|
PJVAL JARRAY::AddArrayValue(PGLOBAL g, PJVAL jvp, int *x)
|
|
{
|
|
if (!jvp)
|
|
jvp = new(g) JVALUE;
|
|
|
|
if (x) {
|
|
int i = 0, n = *x;
|
|
PJVAL jp, *jpp = &First;
|
|
|
|
for (jp = First; jp && i < n; i++, jp = *(jpp = &jp->Next));
|
|
|
|
(*jpp) = jvp;
|
|
|
|
if (!(jvp->Next = jp))
|
|
Last = jvp;
|
|
|
|
} else {
|
|
if (!First)
|
|
First = jvp;
|
|
else if (Last == First)
|
|
First->Next = Last = jvp;
|
|
else
|
|
Last->Next = jvp;
|
|
|
|
Last = jvp;
|
|
Last->Next = NULL;
|
|
} // endif x
|
|
|
|
return jvp;
|
|
} // end of AddValue
|
|
|
|
/***********************************************************************/
|
|
/* Merge two arrays. */
|
|
/***********************************************************************/
|
|
bool JARRAY::Merge(PGLOBAL g, PJSON jsp)
|
|
{
|
|
if (jsp->GetType() != TYPE_JAR) {
|
|
snprintf(g->Message, sizeof(g->Message), "Second argument is not an array");
|
|
return true;
|
|
} // endif Type
|
|
|
|
PJAR arp = (PJAR)jsp;
|
|
|
|
for (int i = 0; i < arp->size(); i++)
|
|
AddArrayValue(g, arp->GetArrayValue(i));
|
|
|
|
InitArray(g);
|
|
return false;
|
|
} // end of Merge
|
|
|
|
/***********************************************************************/
|
|
/* Set the nth Value of the Array Value list or add it. */
|
|
/***********************************************************************/
|
|
void JARRAY::SetArrayValue(PGLOBAL g, PJVAL jvp, int n)
|
|
{
|
|
int i = 0;
|
|
PJVAL jp, *jpp = &First;
|
|
|
|
for (jp = First; i < n; i++, jp = *(jpp = &jp->Next))
|
|
if (!jp)
|
|
*jpp = jp = new(g) JVALUE;
|
|
|
|
*jpp = jvp;
|
|
jvp->Next = (jp ? jp->Next : NULL);
|
|
} // end of SetValue
|
|
|
|
/***********************************************************************/
|
|
/* Return the text corresponding to all values. */
|
|
/***********************************************************************/
|
|
PSZ JARRAY::GetText(PGLOBAL g, PSTRG text)
|
|
{
|
|
if (First) {
|
|
bool b;
|
|
PJVAL jp;
|
|
|
|
if (!text) {
|
|
text = new(g) STRING(g, 256);
|
|
b = true;
|
|
} else {
|
|
if (text->GetLastChar() != ' ')
|
|
text->Append(" (");
|
|
else
|
|
text->Append('(');
|
|
|
|
b = false;
|
|
}
|
|
|
|
for (jp = First; jp; jp = jp->Next) {
|
|
jp->GetText(g, text);
|
|
|
|
if (jp->Next)
|
|
text->Append(", ");
|
|
else if (!b)
|
|
text->Append(')');
|
|
|
|
} // endfor jp
|
|
|
|
if (b) {
|
|
text->Trim();
|
|
return text->GetStr();
|
|
} // endif b
|
|
|
|
} // endif First
|
|
|
|
return NULL;
|
|
} // end of GetText;
|
|
|
|
/***********************************************************************/
|
|
/* Delete a Value from the Arrays Value list. */
|
|
/***********************************************************************/
|
|
bool JARRAY::DeleteValue(int n)
|
|
{
|
|
PJVAL jvp = GetArrayValue(n);
|
|
|
|
if (jvp) {
|
|
jvp->Del = true;
|
|
return false;
|
|
} else
|
|
return true;
|
|
|
|
} // end of DeleteValue
|
|
|
|
/***********************************************************************/
|
|
/* True if void or if all members are nulls. */
|
|
/***********************************************************************/
|
|
bool JARRAY::IsNull(void)
|
|
{
|
|
for (int i = 0; i < Size; i++)
|
|
if (!Mvals[i]->IsNull())
|
|
return false;
|
|
|
|
return true;
|
|
} // end of IsNull
|
|
|
|
/* -------------------------- Class JVALUE- -------------------------- */
|
|
|
|
/***********************************************************************/
|
|
/* Constructor for a JVALUE. */
|
|
/***********************************************************************/
|
|
JVALUE::JVALUE(PJSON jsp) : JSON()
|
|
{
|
|
if (jsp && jsp->GetType() == TYPE_JVAL) {
|
|
PJVAL jvp = (PJVAL)jsp;
|
|
|
|
// Val = ((PJVAL)jsp)->GetVal();
|
|
if (jvp->DataType == TYPE_JSON) {
|
|
Jsp = jvp->GetJsp();
|
|
DataType = TYPE_JSON;
|
|
Nd = 0;
|
|
} else {
|
|
LLn = jvp->LLn; // Must be LLn on 32 bit machines
|
|
Nd = jvp->Nd;
|
|
DataType = jvp->DataType;
|
|
} // endelse Jsp
|
|
|
|
} else {
|
|
Jsp = jsp;
|
|
// Val = NULL;
|
|
DataType = Jsp ? TYPE_JSON : TYPE_NULL;
|
|
Nd = 0;
|
|
} // endif Type
|
|
|
|
Next = NULL;
|
|
Del = false;
|
|
Type = TYPE_JVAL;
|
|
} // end of JVALUE constructor
|
|
|
|
#if 0
|
|
/***********************************************************************/
|
|
/* Constructor for a JVALUE with a given string or numeric value. */
|
|
/***********************************************************************/
|
|
JVALUE::JVALUE(PGLOBAL g, PVL vlp) : JSON()
|
|
{
|
|
Jsp = NULL;
|
|
Val = vlp;
|
|
Next = NULL;
|
|
Del = false;
|
|
Type = TYPE_JVAL;
|
|
} // end of JVALUE constructor
|
|
#endif // 0
|
|
|
|
/***********************************************************************/
|
|
/* Constructor for a JVALUE with a given string or numeric value. */
|
|
/***********************************************************************/
|
|
JVALUE::JVALUE(PGLOBAL g, PVAL valp) : JSON() {
|
|
Jsp = NULL;
|
|
//Val = NULL;
|
|
SetValue(g, valp);
|
|
Next = NULL;
|
|
Del = false;
|
|
Type = TYPE_JVAL;
|
|
} // end of JVALUE constructor
|
|
|
|
/***********************************************************************/
|
|
/* Constructor for a given string. */
|
|
/***********************************************************************/
|
|
JVALUE::JVALUE(PGLOBAL g, PCSZ strp) : JSON()
|
|
{
|
|
Jsp = NULL;
|
|
//Val = AllocVal(g, TYPE_STRG);
|
|
Strp = (char*)strp;
|
|
DataType = TYPE_STRG;
|
|
Nd = 0;
|
|
Next = NULL;
|
|
Del = false;
|
|
Type = TYPE_JVAL;
|
|
} // end of JVALUE constructor
|
|
|
|
/***********************************************************************/
|
|
/* Set or reset all Jvalue members. */
|
|
/***********************************************************************/
|
|
void JVALUE::Clear(void)
|
|
{
|
|
Jsp = NULL;
|
|
Next = NULL;
|
|
Type = TYPE_JVAL;
|
|
Del = false;
|
|
Nd = 0;
|
|
DataType = TYPE_NULL;
|
|
} // end of Clear
|
|
|
|
/***********************************************************************/
|
|
/* Returns the type of the Value's value. */
|
|
/***********************************************************************/
|
|
JTYP JVALUE::GetValType(void)
|
|
{
|
|
if (DataType == TYPE_JSON)
|
|
return Jsp->GetType();
|
|
//else if (Val)
|
|
// return Val->Type;
|
|
else
|
|
return DataType;
|
|
|
|
} // end of GetValType
|
|
|
|
/***********************************************************************/
|
|
/* Return the Value's Object value. */
|
|
/***********************************************************************/
|
|
PJOB JVALUE::GetObject(void)
|
|
{
|
|
if (DataType == TYPE_JSON && Jsp->GetType() == TYPE_JOB)
|
|
return (PJOB)Jsp;
|
|
|
|
return NULL;
|
|
} // end of GetObject
|
|
|
|
/***********************************************************************/
|
|
/* Return the Value's Array value. */
|
|
/***********************************************************************/
|
|
PJAR JVALUE::GetArray(void)
|
|
{
|
|
if (DataType == TYPE_JSON && Jsp->GetType() == TYPE_JAR)
|
|
return (PJAR)Jsp;
|
|
|
|
return NULL;
|
|
} // end of GetArray
|
|
|
|
/***********************************************************************/
|
|
/* Return the Value's as a Value class. */
|
|
/***********************************************************************/
|
|
PVAL JVALUE::GetValue(PGLOBAL g)
|
|
{
|
|
PVAL valp = NULL;
|
|
|
|
if (DataType != TYPE_JSON)
|
|
if (DataType == TYPE_STRG)
|
|
valp = AllocateValue(g, Strp, DataType, Nd);
|
|
else
|
|
valp = AllocateValue(g, &LLn, DataType, Nd);
|
|
|
|
return valp;
|
|
} // end of GetValue
|
|
|
|
/***********************************************************************/
|
|
/* Return the Value's Integer value. */
|
|
/***********************************************************************/
|
|
int JVALUE::GetInteger(void) {
|
|
int n;
|
|
|
|
switch (DataType) {
|
|
case TYPE_INTG: n = N; break;
|
|
case TYPE_DBL: n = (int)F; break;
|
|
case TYPE_DTM:
|
|
case TYPE_STRG: n = atoi(Strp); break;
|
|
case TYPE_BOOL: n = (B) ? 1 : 0; break;
|
|
case TYPE_BINT: n = (int)LLn; break;
|
|
default:
|
|
n = 0;
|
|
} // endswitch Type
|
|
|
|
return n;
|
|
} // end of GetInteger
|
|
|
|
/***********************************************************************/
|
|
/* Return the Value's Big integer value. */
|
|
/***********************************************************************/
|
|
long long JVALUE::GetBigint(void)
|
|
{
|
|
long long lln;
|
|
|
|
switch (DataType) {
|
|
case TYPE_BINT: lln = LLn; break;
|
|
case TYPE_INTG: lln = (long long)N; break;
|
|
case TYPE_DBL: lln = (long long)F; break;
|
|
case TYPE_DTM:
|
|
case TYPE_STRG: lln = atoll(Strp); break;
|
|
case TYPE_BOOL: lln = (B) ? 1 : 0; break;
|
|
default:
|
|
lln = 0;
|
|
} // endswitch Type
|
|
|
|
return lln;
|
|
} // end of GetBigint
|
|
|
|
/***********************************************************************/
|
|
/* Return the Value's Double value. */
|
|
/***********************************************************************/
|
|
double JVALUE::GetFloat(void)
|
|
{
|
|
double d;
|
|
|
|
switch (DataType) {
|
|
case TYPE_DBL: d = F; break;
|
|
case TYPE_BINT: d = (double)LLn; break;
|
|
case TYPE_INTG: d = (double)N; break;
|
|
case TYPE_DTM:
|
|
case TYPE_STRG: d = atof(Strp); break;
|
|
case TYPE_BOOL: d = (B) ? 1.0 : 0.0; break;
|
|
default:
|
|
d = 0.0;
|
|
} // endswitch Type
|
|
|
|
return d;
|
|
} // end of GetFloat
|
|
|
|
/***********************************************************************/
|
|
/* Return the Value's String value. */
|
|
/***********************************************************************/
|
|
PSZ JVALUE::GetString(PGLOBAL g, char *buff)
|
|
{
|
|
char buf[32];
|
|
char *p = (buff) ? buff : buf;
|
|
|
|
switch (DataType) {
|
|
case TYPE_DTM:
|
|
case TYPE_STRG:
|
|
p = Strp;
|
|
break;
|
|
case TYPE_INTG:
|
|
sprintf(p, "%d", N);
|
|
break;
|
|
case TYPE_BINT:
|
|
sprintf(p, "%lld", LLn);
|
|
break;
|
|
case TYPE_DBL:
|
|
sprintf(p, "%.*lf", Nd, F);
|
|
break;
|
|
case TYPE_BOOL:
|
|
p = (char*)((B) ? "true" : "false");
|
|
break;
|
|
case TYPE_NULL:
|
|
p = (char*)"null";
|
|
break;
|
|
default:
|
|
p = NULL;
|
|
} // endswitch Type
|
|
|
|
|
|
return (p == buf) ? (char*)PlugDup(g, buf) : p;
|
|
} // end of GetString
|
|
|
|
/***********************************************************************/
|
|
/* Return the Value's String value. */
|
|
/***********************************************************************/
|
|
PSZ JVALUE::GetText(PGLOBAL g, PSTRG text)
|
|
{
|
|
if (DataType == TYPE_JSON)
|
|
return Jsp->GetText(g, text);
|
|
|
|
char buff[32];
|
|
PSZ s = (DataType == TYPE_NULL) ? NULL : GetString(g, buff);
|
|
|
|
if (s)
|
|
text->Append(s);
|
|
else if (GetJsonNull())
|
|
text->Append(GetJsonNull());
|
|
|
|
return NULL;
|
|
} // end of GetText
|
|
|
|
void JVALUE::SetValue(PJSON jsp)
|
|
{
|
|
if (DataType == TYPE_JSON && jsp->GetType() == TYPE_JVAL) {
|
|
Jsp = jsp->GetJsp();
|
|
Nd = ((PJVAL)jsp)->Nd;
|
|
DataType = ((PJVAL)jsp)->DataType;
|
|
// Val = ((PJVAL)jsp)->GetVal();
|
|
} else {
|
|
Jsp = jsp;
|
|
DataType = TYPE_JSON;
|
|
} // endif Type
|
|
|
|
} // end of SetValue;
|
|
|
|
void JVALUE::SetValue(PGLOBAL g, PVAL valp)
|
|
{
|
|
//if (!Val)
|
|
// Val = AllocVal(g, TYPE_VAL);
|
|
|
|
if (!valp || valp->IsNull()) {
|
|
DataType = TYPE_NULL;
|
|
} else switch (valp->GetType()) {
|
|
case TYPE_DATE:
|
|
if (((DTVAL*)valp)->IsFormatted())
|
|
Strp = PlugDup(g, valp->GetCharValue());
|
|
else {
|
|
char buf[32];
|
|
|
|
Strp = PlugDup(g, valp->GetCharString(buf));
|
|
} // endif Formatted
|
|
|
|
DataType = TYPE_DTM;
|
|
break;
|
|
case TYPE_STRING:
|
|
Strp = PlugDup(g, valp->GetCharValue());
|
|
DataType = TYPE_STRG;
|
|
break;
|
|
case TYPE_DOUBLE:
|
|
case TYPE_DECIM:
|
|
F = valp->GetFloatValue();
|
|
|
|
if (IsTypeNum(valp->GetType()))
|
|
Nd = valp->GetValPrec();
|
|
|
|
DataType = TYPE_DBL;
|
|
break;
|
|
case TYPE_TINY:
|
|
B = valp->GetTinyValue() != 0;
|
|
DataType = TYPE_BOOL;
|
|
case TYPE_INT:
|
|
N = valp->GetIntValue();
|
|
DataType = TYPE_INTG;
|
|
break;
|
|
case TYPE_BIGINT:
|
|
LLn = valp->GetBigintValue();
|
|
DataType = TYPE_BINT;
|
|
break;
|
|
default:
|
|
snprintf(g->Message, sizeof(g->Message), "Unsupported typ %d\n", valp->GetType());
|
|
throw(777);
|
|
} // endswitch Type
|
|
|
|
} // end of SetValue
|
|
|
|
/***********************************************************************/
|
|
/* Set the Value's value as the given integer. */
|
|
/***********************************************************************/
|
|
void JVALUE::SetInteger(PGLOBAL g, int n)
|
|
{
|
|
N = n;
|
|
DataType = TYPE_INTG;
|
|
} // end of SetInteger
|
|
|
|
/***********************************************************************/
|
|
/* Set the Value's Boolean value as a tiny integer. */
|
|
/***********************************************************************/
|
|
void JVALUE::SetBool(PGLOBAL g, bool b)
|
|
{
|
|
B = b;
|
|
DataType = TYPE_BOOL;
|
|
} // end of SetTiny
|
|
|
|
/***********************************************************************/
|
|
/* Set the Value's value as the given big integer. */
|
|
/***********************************************************************/
|
|
void JVALUE::SetBigint(PGLOBAL g, long long ll)
|
|
{
|
|
LLn = ll;
|
|
DataType = TYPE_BINT;
|
|
} // end of SetBigint
|
|
|
|
/***********************************************************************/
|
|
/* Set the Value's value as the given DOUBLE. */
|
|
/***********************************************************************/
|
|
void JVALUE::SetFloat(PGLOBAL g, double f)
|
|
{
|
|
F = f;
|
|
Nd = GetDefaultPrec();
|
|
DataType = TYPE_DBL;
|
|
} // end of SetFloat
|
|
|
|
/***********************************************************************/
|
|
/* Set the Value's value as the given string. */
|
|
/***********************************************************************/
|
|
void JVALUE::SetString(PGLOBAL g, PSZ s, int ci)
|
|
{
|
|
Strp = s;
|
|
Nd = ci;
|
|
DataType = TYPE_STRG;
|
|
} // end of SetString
|
|
|
|
/***********************************************************************/
|
|
/* True when its JSON or normal value is null. */
|
|
/***********************************************************************/
|
|
bool JVALUE::IsNull(void)
|
|
{
|
|
return (DataType == TYPE_JSON) ? Jsp->IsNull() : DataType == TYPE_NULL;
|
|
} // end of IsNull
|
|
|
|
|
|
/* ---------------------------- Class SWAP --------------------------- */
|
|
|
|
/***********************************************************************/
|
|
/* Replace all pointers by offsets or the opposite. */
|
|
/***********************************************************************/
|
|
void SWAP::SwapJson(PJSON jsp, bool move)
|
|
{
|
|
if (move)
|
|
MoffJson(jsp);
|
|
else
|
|
MptrJson((PJSON)MakeOff(Base, jsp));
|
|
|
|
return;
|
|
} // end of SwapJson
|
|
|
|
/***********************************************************************/
|
|
/* Replace all pointers by offsets. */
|
|
/***********************************************************************/
|
|
size_t SWAP::MoffJson(PJSON jsp) {
|
|
size_t res = 0;
|
|
|
|
if (jsp)
|
|
switch (jsp->Type) {
|
|
case TYPE_JAR:
|
|
res = MoffArray((PJAR)jsp);
|
|
break;
|
|
case TYPE_JOB:
|
|
res = MoffObject((PJOB)jsp);
|
|
break;
|
|
case TYPE_JVAL:
|
|
res = MoffJValue((PJVAL)jsp);
|
|
break;
|
|
default:
|
|
throw "Invalid json tree";
|
|
} // endswitch Type
|
|
|
|
return res;
|
|
} // end of MoffJson
|
|
|
|
/***********************************************************************/
|
|
/* Replace all array pointers by offsets. */
|
|
/***********************************************************************/
|
|
size_t SWAP::MoffArray(PJAR jarp)
|
|
{
|
|
if (jarp->First) {
|
|
for (int i = 0; i < jarp->Size; i++)
|
|
jarp->Mvals[i] = (PJVAL)MakeOff(Base, jarp->Mvals[i]);
|
|
|
|
jarp->Mvals = (PJVAL*)MakeOff(Base, jarp->Mvals);
|
|
jarp->First = (PJVAL)MoffJValue(jarp->First);
|
|
jarp->Last = (PJVAL)MakeOff(Base, jarp->Last);
|
|
} // endif First
|
|
|
|
return MakeOff(Base, jarp);
|
|
} // end of MoffArray
|
|
|
|
/***********************************************************************/
|
|
/* Replace all object pointers by offsets. */
|
|
/***********************************************************************/
|
|
size_t SWAP::MoffObject(PJOB jobp) {
|
|
if (jobp->First) {
|
|
jobp->First = (PJPR)MoffPair(jobp->First);
|
|
jobp->Last = (PJPR)MakeOff(Base, jobp->Last);
|
|
} // endif First
|
|
|
|
return MakeOff(Base, jobp);
|
|
} // end of MoffObject
|
|
|
|
/***********************************************************************/
|
|
/* Replace all pair pointers by offsets. */
|
|
/***********************************************************************/
|
|
size_t SWAP::MoffPair(PJPR jpp) {
|
|
jpp->Key = (PCSZ)MakeOff(Base, (void*)jpp->Key);
|
|
|
|
if (jpp->Val)
|
|
jpp->Val = (PJVAL)MoffJValue(jpp->Val);
|
|
|
|
if (jpp->Next)
|
|
jpp->Next = (PJPR)MoffPair(jpp->Next);
|
|
|
|
return MakeOff(Base, jpp);
|
|
} // end of MoffPair
|
|
|
|
/***********************************************************************/
|
|
/* Replace all jason value pointers by offsets. */
|
|
/***********************************************************************/
|
|
size_t SWAP::MoffJValue(PJVAL jvp) {
|
|
if (!jvp->Del) {
|
|
if (jvp->DataType == TYPE_JSON)
|
|
jvp->Jsp = (PJSON)MoffJson(jvp->Jsp);
|
|
else if (jvp->DataType == TYPE_STRG)
|
|
jvp->Strp = (PSZ)MakeOff(Base, (jvp->Strp));
|
|
|
|
// if (jvp->Val)
|
|
// jvp->Val = (PVL)MoffVal(jvp->Val);
|
|
|
|
} // endif Del
|
|
|
|
if (jvp->Next)
|
|
jvp->Next = (PJVAL)MoffJValue(jvp->Next);
|
|
|
|
return MakeOff(Base, jvp);
|
|
} // end of MoffJValue
|
|
|
|
#if 0
|
|
/***********************************************************************/
|
|
/* Replace string pointers by offset. */
|
|
/***********************************************************************/
|
|
size_t SWAP::MoffVal(PVL vlp) {
|
|
if (vlp->Type == TYPE_STRG)
|
|
vlp->Strp = (PSZ)MakeOff(Base, (vlp->Strp));
|
|
|
|
return MakeOff(Base, vlp);
|
|
} // end of MoffVal
|
|
#endif // 0
|
|
|
|
/***********************************************************************/
|
|
/* Replace all offsets by pointers. */
|
|
/***********************************************************************/
|
|
PJSON SWAP::MptrJson(PJSON ojp) { // ojp is an offset
|
|
PJSON jsp = (PJSON)MakePtr(Base, (size_t)ojp);
|
|
|
|
if (ojp)
|
|
switch (jsp->Type) {
|
|
case TYPE_JAR:
|
|
jsp = MptrArray((PJAR)ojp);
|
|
break;
|
|
case TYPE_JOB:
|
|
jsp = MptrObject((PJOB)ojp);
|
|
break;
|
|
case TYPE_JVAL:
|
|
jsp = MptrJValue((PJVAL)ojp);
|
|
break;
|
|
default:
|
|
throw "Invalid json tree";
|
|
} // endswitch Type
|
|
|
|
return jsp;
|
|
} // end of MptrJson
|
|
|
|
/***********************************************************************/
|
|
/* Replace all array offsets by pointers. */
|
|
/***********************************************************************/
|
|
PJAR SWAP::MptrArray(PJAR ojar) {
|
|
PJAR jarp = (PJAR)MakePtr(Base, (size_t)ojar);
|
|
|
|
jarp = (PJAR)new((long long)jarp) JARRAY(0);
|
|
|
|
if (jarp->First) {
|
|
jarp->Mvals = (PJVAL*)MakePtr(Base, (size_t)jarp->Mvals);
|
|
|
|
for (int i = 0; i < jarp->Size; i++)
|
|
jarp->Mvals[i] = (PJVAL)MakePtr(Base, (size_t)jarp->Mvals[i]);
|
|
|
|
jarp->First = (PJVAL)MptrJValue(jarp->First);
|
|
jarp->Last = (PJVAL)MakePtr(Base, (size_t)jarp->Last);
|
|
} // endif First
|
|
|
|
return jarp;
|
|
} // end of MptrArray
|
|
|
|
/***********************************************************************/
|
|
/* Replace all object offsets by pointers. */
|
|
/***********************************************************************/
|
|
PJOB SWAP::MptrObject(PJOB ojob) {
|
|
PJOB jobp = (PJOB)MakePtr(Base, (size_t)ojob);
|
|
|
|
jobp = (PJOB)new((long long)jobp) JOBJECT(0);
|
|
|
|
if (jobp->First) {
|
|
jobp->First = (PJPR)MptrPair(jobp->First);
|
|
jobp->Last = (PJPR)MakePtr(Base, (size_t)jobp->Last);
|
|
} // endif First
|
|
|
|
return jobp;
|
|
} // end of MptrObject
|
|
|
|
/***********************************************************************/
|
|
/* Replace all pair offsets by pointers. */
|
|
/***********************************************************************/
|
|
PJPR SWAP::MptrPair(PJPR ojp) {
|
|
PJPR jpp = (PJPR)MakePtr(Base, (size_t)ojp);
|
|
|
|
jpp->Key = (PCSZ)MakePtr(Base, (size_t)jpp->Key);
|
|
|
|
if (jpp->Val)
|
|
jpp->Val = (PJVAL)MptrJValue(jpp->Val);
|
|
|
|
if (jpp->Next)
|
|
jpp->Next = (PJPR)MptrPair(jpp->Next);
|
|
|
|
return jpp;
|
|
} // end of MptrPair
|
|
|
|
/***********************************************************************/
|
|
/* Replace all value offsets by pointers. */
|
|
/***********************************************************************/
|
|
PJVAL SWAP::MptrJValue(PJVAL ojv) {
|
|
PJVAL jvp = (PJVAL)MakePtr(Base, (size_t)ojv);
|
|
|
|
jvp = (PJVAL)new((long long)jvp) JVALUE(0);
|
|
|
|
if (!jvp->Del) {
|
|
if (jvp->DataType == TYPE_JSON)
|
|
jvp->Jsp = (PJSON)MptrJson(jvp->Jsp);
|
|
else if (jvp->DataType == TYPE_STRG)
|
|
jvp->Strp = (PSZ)MakePtr(Base, (size_t)jvp->Strp);
|
|
|
|
// if (jvp->Val)
|
|
// jvp->Val = (PVL)MptrVal(jvp->Val);
|
|
|
|
} // endif Del
|
|
|
|
if (jvp->Next)
|
|
jvp->Next = (PJVAL)MptrJValue(jvp->Next);
|
|
|
|
return jvp;
|
|
} // end of MptrJValue
|
|
|
|
#if 0
|
|
/***********************************************************************/
|
|
/* Replace string offsets by a pointer. */
|
|
/***********************************************************************/
|
|
PVL SWAP::MptrVal(PVL ovl) {
|
|
PVL vlp = (PVL)MakePtr(Base, (size_t)ovl);
|
|
|
|
if (vlp->Type == TYPE_STRG)
|
|
vlp->Strp = (PSZ)MakePtr(Base, (size_t)vlp->Strp);
|
|
|
|
return vlp;
|
|
} // end of MptrValue
|
|
#endif // 0
|