mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 04:22:27 +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.
1795 lines
48 KiB
C++
1795 lines
48 KiB
C++
/*************** bson CPP Declares Source Code File (.H) ***************/
|
|
/* Name: bson.cpp Version 1.0 */
|
|
/* */
|
|
/* (C) Copyright to the author Olivier BERTRAND 2020 */
|
|
/* */
|
|
/* This file contains the BJSON 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. */
|
|
/* bson.h is header containing the BSON classes declarations. */
|
|
/***********************************************************************/
|
|
#include "global.h"
|
|
#include "plgdbsem.h"
|
|
#include "bson.h"
|
|
|
|
/***********************************************************************/
|
|
/* Check macro. */
|
|
/***********************************************************************/
|
|
#if defined(_DEBUG)
|
|
#define CheckType(X,Y) if (!X || X ->Type != Y) throw MSG(VALTYPE_NOMATCH);
|
|
#else
|
|
#define CheckType(X,Y)
|
|
#endif
|
|
|
|
#if defined(_WIN32)
|
|
#define EL "\r\n"
|
|
#else
|
|
#define EL "\n"
|
|
#undef SE_CATCH // Does not work for Linux
|
|
#endif
|
|
|
|
int GetJsonDefPrec(void);
|
|
|
|
#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
|
|
|
|
/* --------------------------- Class BDOC ---------------------------- */
|
|
|
|
/***********************************************************************/
|
|
/* BDOC constructor. */
|
|
/***********************************************************************/
|
|
BDOC::BDOC(PGLOBAL G) : BJSON(G, NULL)
|
|
{
|
|
jp = NULL;
|
|
s = NULL;
|
|
len = 0;
|
|
pretty = 3;
|
|
pty[0] = pty[1] = pty[2] = true;
|
|
comma = false;
|
|
} // end of BDOC constructor
|
|
|
|
/***********************************************************************/
|
|
/* Parse a json string. */
|
|
/* Note: when pretty is not known, the caller set pretty to 3. */
|
|
/***********************************************************************/
|
|
PBVAL BDOC::ParseJson(PGLOBAL g, char* js, size_t lng)
|
|
{
|
|
size_t i;
|
|
bool b = false;
|
|
PBVAL bvp = NULL;
|
|
|
|
s = js;
|
|
len = lng;
|
|
xtrc(1, "BDOC::ParseJson: s=%.10s len=%zd\n", s, len);
|
|
|
|
if (!s || !len) {
|
|
strcpy(g->Message, "Void JSON object");
|
|
return NULL;
|
|
} // endif s
|
|
|
|
// Trying to guess the pretty format
|
|
if (s[0] == '[' && (s[1] == '\n' || (s[1] == '\r' && s[2] == '\n')))
|
|
pty[0] = false;
|
|
|
|
try {
|
|
bvp = NewVal();
|
|
bvp->Type = TYPE_UNKNOWN;
|
|
|
|
for (i = 0; i < len; i++)
|
|
switch (s[i]) {
|
|
case '[':
|
|
if (bvp->Type != TYPE_UNKNOWN)
|
|
bvp->To_Val = ParseAsArray(i);
|
|
else
|
|
bvp->To_Val = ParseArray(++i);
|
|
|
|
bvp->Type = TYPE_JAR;
|
|
break;
|
|
case '{':
|
|
if (bvp->Type != TYPE_UNKNOWN) {
|
|
bvp->To_Val = ParseAsArray(i);
|
|
bvp->Type = TYPE_JAR;
|
|
} else {
|
|
bvp->To_Val = ParseObject(++i);
|
|
bvp->Type = TYPE_JOB;
|
|
} // endif Type
|
|
|
|
break;
|
|
case ' ':
|
|
case '\t':
|
|
case '\n':
|
|
case '\r':
|
|
break;
|
|
case ',':
|
|
if (bvp->Type != TYPE_UNKNOWN && (pretty == 1 || pretty == 3)) {
|
|
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
|
|
|
|
default:
|
|
if (bvp->Type != TYPE_UNKNOWN) {
|
|
bvp->To_Val = ParseAsArray(i);
|
|
bvp->Type = TYPE_JAR;
|
|
} else if ((bvp->To_Val = MOF(ParseValue(i, NewVal()))))
|
|
bvp->Type = TYPE_JVAL;
|
|
else
|
|
throw 4;
|
|
|
|
break;
|
|
}; // endswitch s[i]
|
|
|
|
if (bvp->Type == TYPE_UNKNOWN)
|
|
snprintf(g->Message, sizeof(g->Message), "Invalid Json string '%.*s'", MY_MIN((int)len, 50), s);
|
|
else if (pretty == 3) {
|
|
for (i = 0; i < 3; i++)
|
|
if (pty[i]) {
|
|
pretty = i;
|
|
break;
|
|
} // endif pty
|
|
|
|
} // endif ptyp
|
|
|
|
} catch (int n) {
|
|
if (trace(1))
|
|
htrc("Exception %d: %s\n", n, G->Message);
|
|
GetMsg(g);
|
|
bvp = NULL;
|
|
} catch (const char* msg) {
|
|
strcpy(g->Message, msg);
|
|
bvp = NULL;
|
|
} // end catch
|
|
|
|
return bvp;
|
|
} // end of ParseJson
|
|
|
|
/***********************************************************************/
|
|
/* Parse several items as being in an array. */
|
|
/***********************************************************************/
|
|
OFFSET BDOC::ParseAsArray(size_t& i) {
|
|
if (pty[0] && (!pretty || pretty > 2)) {
|
|
OFFSET jsp;
|
|
|
|
if ((jsp = ParseArray((i = 0))) && pretty == 3)
|
|
pretty = (pty[0]) ? 0 : 3;
|
|
|
|
return jsp;
|
|
} else
|
|
strcpy(G->Message, "More than one item in file");
|
|
|
|
return 0;
|
|
} // end of ParseAsArray
|
|
|
|
/***********************************************************************/
|
|
/* Parse a JSON Array. */
|
|
/***********************************************************************/
|
|
OFFSET BDOC::ParseArray(size_t& i)
|
|
{
|
|
int level = 0;
|
|
bool b = (!i);
|
|
PBVAL vlp, firstvlp, lastvlp;
|
|
|
|
vlp = firstvlp = lastvlp = NULL;
|
|
|
|
for (; i < len; i++)
|
|
switch (s[i]) {
|
|
case ',':
|
|
if (level < 2) {
|
|
snprintf(G->Message, sizeof(G->Message), "Unexpected ',' near %.*s", (int) ARGS);
|
|
throw 1;
|
|
} else
|
|
level = 1;
|
|
|
|
break;
|
|
case ']':
|
|
if (level == 1) {
|
|
snprintf(G->Message, sizeof(G->Message), "Unexpected ',]' near %.*s", (int) ARGS);
|
|
throw 1;
|
|
} // endif level
|
|
|
|
return MOF(firstvlp);
|
|
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", (int) ARGS);
|
|
throw 1;
|
|
} else if (lastvlp) {
|
|
vlp = ParseValue(i, NewVal());
|
|
lastvlp->Next = MOF(vlp);
|
|
lastvlp = vlp;
|
|
} else
|
|
firstvlp = lastvlp = ParseValue(i, NewVal());
|
|
|
|
level = (b) ? 1 : 2;
|
|
break;
|
|
}; // endswitch s[i]
|
|
|
|
if (b) {
|
|
// Case of Pretty == 0
|
|
return MOF(firstvlp);
|
|
} // endif b
|
|
|
|
throw ("Unexpected EOF in array");
|
|
} // end of ParseArray
|
|
|
|
/***********************************************************************/
|
|
/* Parse a JSON Object. */
|
|
/***********************************************************************/
|
|
OFFSET BDOC::ParseObject(size_t& i)
|
|
{
|
|
OFFSET key;
|
|
int level = 0;
|
|
PBPR bpp, firstbpp, lastbpp;
|
|
|
|
bpp = firstbpp = lastbpp = NULL;
|
|
|
|
for (; i < len; i++)
|
|
switch (s[i]) {
|
|
case '"':
|
|
if (level < 2) {
|
|
key = ParseString(++i);
|
|
bpp = NewPair(key);
|
|
|
|
if (lastbpp) {
|
|
lastbpp->Vlp.Next = MOF(bpp);
|
|
lastbpp = bpp;
|
|
} else
|
|
firstbpp = lastbpp = bpp;
|
|
|
|
level = 2;
|
|
} else {
|
|
snprintf(G->Message, sizeof(G->Message), "misplaced string near %.*s", (int) ARGS);
|
|
throw 2;
|
|
} // endif level
|
|
|
|
break;
|
|
case ':':
|
|
if (level == 2) {
|
|
ParseValue(++i, GetVlp(lastbpp));
|
|
level = 3;
|
|
} else {
|
|
snprintf(G->Message, sizeof(G->Message), "Unexpected ':' near %.*s", (int) ARGS);
|
|
throw 2;
|
|
} // endif level
|
|
|
|
break;
|
|
case ',':
|
|
if (level < 3) {
|
|
snprintf(G->Message, sizeof(G->Message), "Unexpected ',' near %.*s", (int) ARGS);
|
|
throw 2;
|
|
} else
|
|
level = 1;
|
|
|
|
break;
|
|
case '}':
|
|
if (!(level == 0 || level == 3)) {
|
|
snprintf(G->Message, sizeof(G->Message), "Unexpected '}' near %.*s", (int) ARGS);
|
|
throw 2;
|
|
} // endif level
|
|
|
|
return MOF(firstbpp);
|
|
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], (int) ARGS);
|
|
throw 2;
|
|
}; // endswitch s[i]
|
|
|
|
strcpy(G->Message, "Unexpected EOF in Object");
|
|
throw 2;
|
|
} // end of ParseObject
|
|
|
|
/***********************************************************************/
|
|
/* Parse a JSON Value. */
|
|
/***********************************************************************/
|
|
PBVAL BDOC::ParseValue(size_t& i, PBVAL bvp)
|
|
{
|
|
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 '[':
|
|
bvp->To_Val = ParseArray(++i);
|
|
bvp->Type = TYPE_JAR;
|
|
break;
|
|
case '{':
|
|
bvp->To_Val = ParseObject(++i);
|
|
bvp->Type = TYPE_JOB;
|
|
break;
|
|
case '"':
|
|
bvp->To_Val = ParseString(++i);
|
|
bvp->Type = TYPE_STRG;
|
|
break;
|
|
case 't':
|
|
if (!strncmp(s + i, "true", 4)) {
|
|
bvp->B = true;
|
|
bvp->Type = TYPE_BOOL;
|
|
i += 3;
|
|
} else
|
|
goto err;
|
|
|
|
break;
|
|
case 'f':
|
|
if (!strncmp(s + i, "false", 5)) {
|
|
bvp->B = false;
|
|
bvp->Type = TYPE_BOOL;
|
|
i += 4;
|
|
} else
|
|
goto err;
|
|
|
|
break;
|
|
case 'n':
|
|
if (!strncmp(s + i, "null", 4)) {
|
|
bvp->Type = TYPE_NULL;
|
|
i += 3;
|
|
} else
|
|
goto err;
|
|
|
|
break;
|
|
case '-':
|
|
default:
|
|
if (s[i] == '-' || isdigit(s[i]))
|
|
ParseNumeric(i, bvp);
|
|
else
|
|
goto err;
|
|
|
|
}; // endswitch s[i]
|
|
|
|
return bvp;
|
|
|
|
err:
|
|
snprintf(G->Message, sizeof(G->Message), "Unexpected character '%c' near %.*s", s[i], (int) ARGS);
|
|
throw 3;
|
|
} // end of ParseValue
|
|
|
|
/***********************************************************************/
|
|
/* Unescape and parse a JSON string. */
|
|
/***********************************************************************/
|
|
OFFSET BDOC::ParseString(size_t& 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*)BsonSubAlloc(0);
|
|
|
|
for (; i < len; i++)
|
|
switch (s[i]) {
|
|
case '"':
|
|
p[n++] = 0;
|
|
BsonSubAlloc(n);
|
|
return MOF(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 BDOC::ParseNumeric(size_t& i, PBVAL vlp)
|
|
{
|
|
char buf[50];
|
|
int n = 0;
|
|
short nd = 0;
|
|
bool has_dot = false;
|
|
bool has_e = false;
|
|
bool found_digit = false;
|
|
|
|
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 = atof(buf);
|
|
|
|
if (nd >= 6 || dv > FLT_MAX || dv < FLT_MIN) {
|
|
double* dvp = (double*)PlugSubAlloc(G, NULL, sizeof(double));
|
|
|
|
*dvp = dv;
|
|
vlp->To_Val = MOF(dvp);
|
|
vlp->Type = TYPE_DBL;
|
|
} else {
|
|
vlp->F = (float)dv;
|
|
vlp->Type = TYPE_FLOAT;
|
|
} // endif nd
|
|
|
|
vlp->Nd = MY_MIN(nd, 16);
|
|
} else {
|
|
longlong iv = strtoll(buf, NULL, 10);
|
|
|
|
if (iv > INT_MAX32 || iv < INT_MIN32) {
|
|
longlong *llp = (longlong*)PlugSubAlloc(G, NULL, sizeof(longlong));
|
|
|
|
*llp = iv;
|
|
vlp->To_Val = MOF(llp);
|
|
vlp->Type = TYPE_BINT;
|
|
} else {
|
|
vlp->N = (int)iv;
|
|
vlp->Type = 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 BJSON document tree: */
|
|
/***********************************************************************/
|
|
PSZ BDOC::Serialize(PGLOBAL g, PBVAL bvp, char* fn, int pretty)
|
|
{
|
|
PSZ str = NULL;
|
|
bool b = false, err = true;
|
|
FILE* fs = NULL;
|
|
|
|
G->Message[0] = 0;
|
|
|
|
try {
|
|
if (!bvp) {
|
|
safe_strcpy(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
|
|
|
|
switch (bvp->Type) {
|
|
case TYPE_JAR:
|
|
err = SerializeArray(bvp->To_Val, b);
|
|
break;
|
|
case TYPE_JOB:
|
|
err = ((b && jp->Prty()) && jp->WriteChr('\t'));
|
|
err |= SerializeObject(bvp->To_Val);
|
|
break;
|
|
case TYPE_JVAL:
|
|
err = SerializeValue(MVP(bvp->To_Val));
|
|
break;
|
|
default:
|
|
err = SerializeValue(bvp, true);
|
|
} // endswitch Type
|
|
|
|
if (fs) {
|
|
fputs(EL, fs);
|
|
fclose(fs);
|
|
str = (err) ? NULL : strcpy(g->Message, "Ok");
|
|
} else if (!err) {
|
|
str = ((JOUTSTR*)jp)->Strp;
|
|
jp->WriteChr('\0');
|
|
PlugSubAlloc(g, NULL, ((JOUTSTR*)jp)->N);
|
|
} else if (G->Message[0])
|
|
strcpy(g->Message, "Error in Serialize");
|
|
else
|
|
GetMsg(g);
|
|
|
|
} catch (int n) {
|
|
if (trace(1))
|
|
htrc("Exception %d: %s\n", n, G->Message);
|
|
GetMsg(g);
|
|
str = NULL;
|
|
} catch (const char* msg) {
|
|
strcpy(g->Message, msg);
|
|
str = NULL;
|
|
} // end catch
|
|
|
|
return str;
|
|
} // end of Serialize
|
|
|
|
|
|
/***********************************************************************/
|
|
/* Serialize a JSON Array. */
|
|
/***********************************************************************/
|
|
bool BDOC::SerializeArray(OFFSET arp, bool b)
|
|
{
|
|
bool first = true;
|
|
PBVAL vp = MVP(arp);
|
|
|
|
if (b) {
|
|
if (jp->Prty()) {
|
|
if (jp->WriteChr('['))
|
|
return true;
|
|
else if (jp->Prty() == 1 && (jp->WriteStr(EL) || jp->WriteChr('\t')))
|
|
return true;
|
|
|
|
} // endif Prty
|
|
|
|
} else if (jp->WriteChr('['))
|
|
return true;
|
|
|
|
for (vp; vp; vp = MVP(vp->Next)) {
|
|
if (first)
|
|
first = false;
|
|
else if ((!b || jp->Prty()) && jp->WriteChr(','))
|
|
return true;
|
|
else if (b) {
|
|
if (jp->Prty() < 2 && jp->WriteStr(EL))
|
|
return true;
|
|
else if (jp->Prty() == 1 && jp->WriteChr('\t'))
|
|
return true;
|
|
|
|
} // endif b
|
|
|
|
if (SerializeValue(vp))
|
|
return true;
|
|
|
|
} // endfor vp
|
|
|
|
if (b && jp->Prty() == 1 && jp->WriteStr(EL))
|
|
return true;
|
|
|
|
return ((!b || jp->Prty()) && jp->WriteChr(']'));
|
|
} // end of SerializeArray
|
|
|
|
/***********************************************************************/
|
|
/* Serialize a JSON Object. */
|
|
/***********************************************************************/
|
|
bool BDOC::SerializeObject(OFFSET obp)
|
|
{
|
|
bool first = true;
|
|
PBPR prp = MPP(obp);
|
|
|
|
if (jp->WriteChr('{'))
|
|
return true;
|
|
|
|
for (prp; prp; prp = GetNext(prp)) {
|
|
if (first)
|
|
first = false;
|
|
else if (jp->WriteChr(','))
|
|
return true;
|
|
|
|
if (jp->WriteChr('"') ||
|
|
jp->WriteStr(MZP(prp->Key)) ||
|
|
jp->WriteChr('"') ||
|
|
jp->WriteChr(':') ||
|
|
SerializeValue(GetVlp(prp)))
|
|
return true;
|
|
|
|
} // endfor i
|
|
|
|
return jp->WriteChr('}');
|
|
} // end of SerializeObject
|
|
|
|
/***********************************************************************/
|
|
/* Serialize a JSON Value. */
|
|
/***********************************************************************/
|
|
bool BDOC::SerializeValue(PBVAL jvp, bool b)
|
|
{
|
|
char buf[64];
|
|
|
|
if (jvp) switch (jvp->Type) {
|
|
case TYPE_JAR:
|
|
return SerializeArray(jvp->To_Val, false);
|
|
case TYPE_JOB:
|
|
return SerializeObject(jvp->To_Val);
|
|
case TYPE_BOOL:
|
|
return jp->WriteStr(jvp->B ? "true" : "false");
|
|
case TYPE_STRG:
|
|
case TYPE_DTM:
|
|
if (b) {
|
|
return jp->WriteStr(MZP(jvp->To_Val));
|
|
} else
|
|
return jp->Escape(MZP(jvp->To_Val));
|
|
|
|
case TYPE_INTG:
|
|
snprintf(buf, sizeof(buf), "%d", jvp->N);
|
|
return jp->WriteStr(buf);
|
|
case TYPE_BINT:
|
|
snprintf(buf, sizeof(buf), "%lld", *(longlong*)MakePtr(Base, jvp->To_Val));
|
|
return jp->WriteStr(buf);
|
|
case TYPE_FLOAT:
|
|
snprintf(buf, sizeof(buf), "%.*f", jvp->Nd, jvp->F);
|
|
return jp->WriteStr(buf);
|
|
case TYPE_DBL:
|
|
snprintf(buf, sizeof(buf), "%.*lf", jvp->Nd, *(double*)MakePtr(Base, jvp->To_Val));
|
|
return jp->WriteStr(buf);
|
|
case TYPE_NULL:
|
|
return jp->WriteStr("null");
|
|
case TYPE_JVAL:
|
|
return SerializeValue(MVP(jvp->To_Val));
|
|
default:
|
|
return jp->WriteStr("???"); // TODO
|
|
} // endswitch Type
|
|
|
|
return jp->WriteStr("null");
|
|
} // end of SerializeValue
|
|
|
|
/* --------------------------- Class BJSON --------------------------- */
|
|
|
|
/***********************************************************************/
|
|
/* Program for sub-allocating Bjson structures. */
|
|
/***********************************************************************/
|
|
void* BJSON::BsonSubAlloc(size_t size)
|
|
{
|
|
PPOOLHEADER pph; /* Points on area header. */
|
|
void* memp = G->Sarea;
|
|
|
|
size = ((size + 3) / 4) * 4; /* Round up size to multiple of 4 */
|
|
pph = (PPOOLHEADER)memp;
|
|
|
|
xtrc(16, "SubAlloc in %p size=%zd used=%zd free=%zd\n",
|
|
memp, size, pph->To_Free, pph->FreeBlk);
|
|
|
|
if (size > pph->FreeBlk) { /* Not enough memory left in pool */
|
|
snprintf(G->Message, sizeof(G->Message),
|
|
"Not enough memory for request of %zd (used=%zd free=%zd)",
|
|
size, pph->To_Free, pph->FreeBlk);
|
|
xtrc(1, "BsonSubAlloc: %s\n", G->Message);
|
|
|
|
if (Throw)
|
|
throw(1234);
|
|
else
|
|
return NULL;
|
|
|
|
} /* endif size OS32 code */
|
|
|
|
// Do the suballocation the simplest way
|
|
memp = MakePtr(memp, pph->To_Free); /* Points to suballocated block */
|
|
pph->To_Free += size; /* New offset of pool free block */
|
|
pph->FreeBlk -= size; /* New size of pool free block */
|
|
xtrc(16, "Done memp=%p used=%zd free=%zd\n",
|
|
memp, pph->To_Free, pph->FreeBlk);
|
|
return memp;
|
|
} // end of BsonSubAlloc
|
|
|
|
/*********************************************************************************/
|
|
/* Program for SubSet re-initialization of the memory pool. */
|
|
/*********************************************************************************/
|
|
PSZ BJSON::NewStr(PSZ str)
|
|
{
|
|
if (str) {
|
|
PSZ sm = (PSZ)BsonSubAlloc(strlen(str) + 1);
|
|
|
|
strcpy(sm, str);
|
|
return sm;
|
|
} else
|
|
return NULL;
|
|
|
|
} // end of NewStr
|
|
|
|
/*********************************************************************************/
|
|
/* Program for SubSet re-initialization of the memory pool. */
|
|
/*********************************************************************************/
|
|
void BJSON::SubSet(bool b)
|
|
{
|
|
PPOOLHEADER pph = (PPOOLHEADER)G->Sarea;
|
|
|
|
pph->To_Free = (G->Saved_Size) ? G->Saved_Size : sizeof(POOLHEADER);
|
|
pph->FreeBlk = G->Sarea_Size - pph->To_Free;
|
|
|
|
if (b)
|
|
G->Saved_Size = 0;
|
|
|
|
} // end of SubSet
|
|
|
|
/*********************************************************************************/
|
|
/* Set the beginning of suballocations. */
|
|
/*********************************************************************************/
|
|
void BJSON::MemSet(size_t size)
|
|
{
|
|
PPOOLHEADER pph = (PPOOLHEADER)G->Sarea;
|
|
|
|
pph->To_Free = size + sizeof(POOLHEADER);
|
|
pph->FreeBlk = G->Sarea_Size - pph->To_Free;
|
|
} // end of MemSet
|
|
|
|
/* ------------------------ Bobject functions ------------------------ */
|
|
|
|
/***********************************************************************/
|
|
/* Set a pair vlp to some PVAL values. */
|
|
/***********************************************************************/
|
|
void BJSON::SetPairValue(PBPR brp, PBVAL bvp)
|
|
{
|
|
if (bvp) {
|
|
brp->Vlp.To_Val = bvp->To_Val;
|
|
brp->Vlp.Nd = bvp->Nd;
|
|
brp->Vlp.Type = bvp->Type;
|
|
} else {
|
|
brp->Vlp.To_Val = 0;
|
|
brp->Vlp.Nd = 0;
|
|
brp->Vlp.Type = TYPE_NULL;
|
|
} // endif bvp
|
|
|
|
} // end of SetPairValue
|
|
|
|
/***********************************************************************/
|
|
/* Sub-allocate and initialize a BPAIR. */
|
|
/***********************************************************************/
|
|
PBPR BJSON::NewPair(OFFSET key, int type)
|
|
{
|
|
PBPR bpp = (PBPR)BsonSubAlloc(sizeof(BPAIR));
|
|
|
|
bpp->Key = key;
|
|
bpp->Vlp.Type = type;
|
|
bpp->Vlp.To_Val = 0;
|
|
bpp->Vlp.Nd = 0;
|
|
bpp->Vlp.Next = 0;
|
|
return bpp;
|
|
} // end of SubAllocPair
|
|
|
|
/***********************************************************************/
|
|
/* Return the number of pairs in this object. */
|
|
/***********************************************************************/
|
|
int BJSON::GetObjectSize(PBVAL bop, bool b)
|
|
{
|
|
CheckType(bop, TYPE_JOB);
|
|
int n = 0;
|
|
|
|
for (PBPR brp = GetObject(bop); brp; brp = GetNext(brp))
|
|
// If b return only non null pairs
|
|
if (!b || (brp->Vlp.To_Val && brp->Vlp.Type != TYPE_NULL))
|
|
n++;
|
|
|
|
return n;
|
|
} // end of GetObjectSize
|
|
|
|
/***********************************************************************/
|
|
/* Add a new pair to an Object and return it. */
|
|
/***********************************************************************/
|
|
PBVAL BJSON::AddPair(PBVAL bop, PSZ key, int type)
|
|
{
|
|
CheckType(bop, TYPE_JOB);
|
|
PBPR brp;
|
|
OFFSET nrp = NewPair(key, type);
|
|
|
|
if (bop->To_Val) {
|
|
for (brp = GetObject(bop); brp->Vlp.Next; brp = GetNext(brp));
|
|
|
|
brp->Vlp.Next = nrp;
|
|
} else
|
|
bop->To_Val = nrp;
|
|
|
|
bop->Nd++;
|
|
return GetVlp(MPP(nrp));
|
|
} // end of AddPair
|
|
|
|
/***********************************************************************/
|
|
/* Return all object keys as an array. */
|
|
/***********************************************************************/
|
|
PBVAL BJSON::GetKeyList(PBVAL bop)
|
|
{
|
|
CheckType(bop, TYPE_JOB);
|
|
PBVAL arp = NewVal(TYPE_JAR);
|
|
|
|
for (PBPR brp = GetObject(bop); brp; brp = GetNext(brp))
|
|
AddArrayValue(arp, MOF(SubAllocVal(brp->Key, TYPE_STRG)));
|
|
|
|
return arp;
|
|
} // end of GetKeyList
|
|
|
|
/***********************************************************************/
|
|
/* Return all object values as an array. */
|
|
/***********************************************************************/
|
|
PBVAL BJSON::GetObjectValList(PBVAL bop)
|
|
{
|
|
CheckType(bop, TYPE_JOB);
|
|
PBVAL arp = NewVal(TYPE_JAR);
|
|
|
|
for (PBPR brp = GetObject(bop); brp; brp = GetNext(brp))
|
|
AddArrayValue(arp, DupVal(GetVlp(brp)));
|
|
|
|
return arp;
|
|
} // end of GetObjectValList
|
|
|
|
/***********************************************************************/
|
|
/* Get the value corresponding to the given key. */
|
|
/***********************************************************************/
|
|
PBVAL BJSON::GetKeyValue(PBVAL bop, PSZ key)
|
|
{
|
|
CheckType(bop, TYPE_JOB);
|
|
|
|
for (PBPR brp = GetObject(bop); brp; brp = GetNext(brp))
|
|
if (!strcmp(GetKey(brp), key))
|
|
return GetVlp(brp);
|
|
|
|
return NULL;
|
|
} // end of GetKeyValue;
|
|
|
|
/***********************************************************************/
|
|
/* Return the text corresponding to all keys (XML like). */
|
|
/***********************************************************************/
|
|
PSZ BJSON::GetObjectText(PGLOBAL g, PBVAL bop, PSTRG text)
|
|
{
|
|
CheckType(bop, TYPE_JOB);
|
|
PBPR brp = GetObject(bop);
|
|
|
|
if (brp) {
|
|
bool b;
|
|
|
|
if (!text) {
|
|
text = new(g) STRING(g, 256);
|
|
b = true;
|
|
} else {
|
|
if (text->GetLastChar() != ' ')
|
|
text->Append(' ');
|
|
|
|
b = false;
|
|
} // endif text
|
|
|
|
if (b && !brp->Vlp.Next && !strcmp(MZP(brp->Key), "$date")) {
|
|
int i;
|
|
PSZ s;
|
|
|
|
GetValueText(g, GetVlp(brp), 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 (; brp; brp = GetNext(brp)) {
|
|
GetValueText(g, GetVlp(brp), text);
|
|
|
|
if (brp->Vlp.Next)
|
|
text->Append(' ');
|
|
|
|
} // endfor brp
|
|
|
|
if (b) {
|
|
text->Trim();
|
|
return text->GetStr();
|
|
} // endif b
|
|
|
|
} // endif bop
|
|
|
|
return NULL;
|
|
} // end of GetObjectText;
|
|
|
|
/***********************************************************************/
|
|
/* Set or add a value corresponding to the given key. */
|
|
/***********************************************************************/
|
|
void BJSON::SetKeyValue(PBVAL bop, OFFSET bvp, PSZ key)
|
|
{
|
|
CheckType(bop, TYPE_JOB);
|
|
PBPR brp, prp = NULL;
|
|
|
|
if (bop->To_Val) {
|
|
for (brp = GetObject(bop); brp; brp = GetNext(brp))
|
|
if (!strcmp(GetKey(brp), key))
|
|
break;
|
|
else
|
|
prp = brp;
|
|
|
|
if (!brp)
|
|
brp = MPP(prp->Vlp.Next = NewPair(key));
|
|
|
|
} else
|
|
brp = MPP(bop->To_Val = NewPair(key));
|
|
|
|
SetPairValue(brp, MVP(bvp));
|
|
bop->Nd++;
|
|
} // end of SetKeyValue
|
|
|
|
/***********************************************************************/
|
|
/* Merge two objects. */
|
|
/***********************************************************************/
|
|
PBVAL BJSON::MergeObject(PBVAL bop1, PBVAL bop2)
|
|
{
|
|
CheckType(bop1, TYPE_JOB);
|
|
CheckType(bop2, TYPE_JOB);
|
|
|
|
if (bop1->To_Val)
|
|
for (PBPR brp = GetObject(bop2); brp; brp = GetNext(brp))
|
|
SetKeyValue(bop1, GetVlp(brp), GetKey(brp));
|
|
|
|
else {
|
|
bop1->To_Val = bop2->To_Val;
|
|
bop1->Nd = bop2->Nd;
|
|
} // endelse To_Val
|
|
|
|
return bop1;
|
|
} // end of MergeObject;
|
|
|
|
/***********************************************************************/
|
|
/* Delete a value corresponding to the given key. */
|
|
/***********************************************************************/
|
|
bool BJSON::DeleteKey(PBVAL bop, PCSZ key)
|
|
{
|
|
CheckType(bop, TYPE_JOB);
|
|
PBPR brp, pbrp = NULL;
|
|
|
|
for (brp = GetObject(bop); brp; brp = GetNext(brp))
|
|
if (!strcmp(MZP(brp->Key), key)) {
|
|
if (pbrp) {
|
|
pbrp->Vlp.Next = brp->Vlp.Next;
|
|
} else
|
|
bop->To_Val = brp->Vlp.Next;
|
|
|
|
bop->Nd--;
|
|
return true;;
|
|
} else
|
|
pbrp = brp;
|
|
|
|
return false;
|
|
} // end of DeleteKey
|
|
|
|
/***********************************************************************/
|
|
/* True if void or if all members are nulls. */
|
|
/***********************************************************************/
|
|
bool BJSON::IsObjectNull(PBVAL bop)
|
|
{
|
|
CheckType(bop, TYPE_JOB);
|
|
|
|
for (PBPR brp = GetObject(bop); brp; brp = GetNext(brp))
|
|
if (brp->Vlp.To_Val && brp->Vlp.Type != TYPE_NULL)
|
|
return false;
|
|
|
|
return true;
|
|
} // end of IsObjectNull
|
|
|
|
/* ------------------------- Barray functions ------------------------ */
|
|
|
|
/***********************************************************************/
|
|
/* Return the number of values in this object. */
|
|
/***********************************************************************/
|
|
int BJSON::GetArraySize(PBVAL bap, bool b)
|
|
{
|
|
CheckType(bap, TYPE_JAR);
|
|
int n = 0;
|
|
|
|
for (PBVAL bvp = GetArray(bap); bvp; bvp = GetNext(bvp))
|
|
// If b, return only non null values
|
|
if (!b || bvp->Type != TYPE_NULL)
|
|
n++;
|
|
|
|
return n;
|
|
} // end of GetArraySize
|
|
|
|
/***********************************************************************/
|
|
/* Get the Nth value of an Array. */
|
|
/***********************************************************************/
|
|
PBVAL BJSON::GetArrayValue(PBVAL bap, int n)
|
|
{
|
|
CheckType(bap, TYPE_JAR);
|
|
int i = 0;
|
|
|
|
if (n < 0)
|
|
n += GetArraySize(bap);
|
|
|
|
for (PBVAL bvp = GetArray(bap); bvp; bvp = GetNext(bvp), i++)
|
|
if (i == n)
|
|
return bvp;
|
|
|
|
return NULL;
|
|
} // end of GetArrayValue
|
|
|
|
/***********************************************************************/
|
|
/* Add a Value to the Array Value list. */
|
|
/***********************************************************************/
|
|
void BJSON::AddArrayValue(PBVAL bap, OFFSET nbv, int* x)
|
|
{
|
|
CheckType(bap, TYPE_JAR);
|
|
int i = 0;
|
|
PBVAL bvp, lbp = NULL;
|
|
|
|
if (!nbv)
|
|
nbv = MOF(NewVal());
|
|
|
|
for (bvp = GetArray(bap); bvp; bvp = GetNext(bvp), i++)
|
|
if (x && i == *x)
|
|
break;
|
|
else
|
|
lbp = bvp;
|
|
|
|
if (lbp) {
|
|
MVP(nbv)->Next = lbp->Next;
|
|
lbp->Next = nbv;
|
|
} else {
|
|
MVP(nbv)->Next = bap->To_Val;
|
|
bap->To_Val = nbv;
|
|
} // endif lbp
|
|
|
|
bap->Nd++;
|
|
} // end of AddArrayValue
|
|
|
|
/***********************************************************************/
|
|
/* Merge two arrays. */
|
|
/***********************************************************************/
|
|
void BJSON::MergeArray(PBVAL bap1, PBVAL bap2)
|
|
{
|
|
CheckType(bap1, TYPE_JAR);
|
|
CheckType(bap2, TYPE_JAR);
|
|
|
|
if (bap1->To_Val) {
|
|
for (PBVAL bvp = GetArray(bap2); bvp; bvp = GetNext(bvp))
|
|
AddArrayValue(bap1, MOF(DupVal(bvp)));
|
|
|
|
} else {
|
|
bap1->To_Val = bap2->To_Val;
|
|
bap1->Nd = bap2->Nd;
|
|
} // endif To_Val
|
|
|
|
} // end of MergeArray
|
|
|
|
/***********************************************************************/
|
|
/* Set the nth Value of the Array Value list or add it. */
|
|
/***********************************************************************/
|
|
void BJSON::SetArrayValue(PBVAL bap, PBVAL nvp, int n)
|
|
{
|
|
CheckType(bap, TYPE_JAR);
|
|
int i = 0;
|
|
PBVAL bvp = NULL;
|
|
|
|
for (bvp = GetArray(bap); i < n; i++, bvp = bvp ? GetNext(bvp) : NULL)
|
|
if (!bvp)
|
|
AddArrayValue(bap, NewVal());
|
|
|
|
if (!bvp)
|
|
AddArrayValue(bap, MOF(nvp));
|
|
else
|
|
SetValueVal(bvp, nvp);
|
|
|
|
} // end of SetValue
|
|
|
|
/***********************************************************************/
|
|
/* Return the text corresponding to all values. */
|
|
/***********************************************************************/
|
|
PSZ BJSON::GetArrayText(PGLOBAL g, PBVAL bap, PSTRG text)
|
|
{
|
|
CheckType(bap, TYPE_JAR);
|
|
|
|
if (bap->To_Val) {
|
|
bool b;
|
|
|
|
if (!text) {
|
|
text = new(g) STRING(g, 256);
|
|
b = true;
|
|
} else {
|
|
if (text->GetLastChar() != ' ')
|
|
text->Append(" (");
|
|
else
|
|
text->Append('(');
|
|
|
|
b = false;
|
|
} // endif text
|
|
|
|
for (PBVAL bvp = GetArray(bap); bvp; bvp = GetNext(bvp)) {
|
|
GetValueText(g, bvp, text);
|
|
|
|
if (bvp->Next)
|
|
text->Append(", ");
|
|
else if (!b)
|
|
text->Append(')');
|
|
|
|
} // endfor bvp
|
|
|
|
if (b) {
|
|
text->Trim();
|
|
return text->GetStr();
|
|
} // endif b
|
|
|
|
} // endif To_Val
|
|
|
|
return NULL;
|
|
} // end of GetText;
|
|
|
|
/***********************************************************************/
|
|
/* Delete a Value from the Arrays Value list. */
|
|
/***********************************************************************/
|
|
bool BJSON::DeleteValue(PBVAL bap, int n)
|
|
{
|
|
CheckType(bap, TYPE_JAR);
|
|
int i = 0;
|
|
PBVAL bvp, pvp = NULL;
|
|
|
|
for (bvp = GetArray(bap); bvp; i++, bvp = GetNext(bvp))
|
|
if (i == n) {
|
|
if (pvp)
|
|
pvp->Next = bvp->Next;
|
|
else
|
|
bap->To_Val = bvp->Next;
|
|
|
|
bap->Nd--;
|
|
return true;;
|
|
} else
|
|
pvp = bvp;
|
|
|
|
return false;
|
|
} // end of DeleteValue
|
|
|
|
/***********************************************************************/
|
|
/* True if void or if all members are nulls. */
|
|
/***********************************************************************/
|
|
bool BJSON::IsArrayNull(PBVAL bap)
|
|
{
|
|
CheckType(bap, TYPE_JAR);
|
|
|
|
for (PBVAL bvp = GetArray(bap); bvp; bvp = GetNext(bvp))
|
|
if (bvp->Type != TYPE_NULL)
|
|
return false;
|
|
|
|
return true;
|
|
} // end of IsNull
|
|
|
|
/* ------------------------- Bvalue functions ------------------------ */
|
|
|
|
/***********************************************************************/
|
|
/* Sub-allocate and clear a BVAL. */
|
|
/***********************************************************************/
|
|
PBVAL BJSON::NewVal(int type)
|
|
{
|
|
PBVAL bvp = (PBVAL)BsonSubAlloc(sizeof(BVAL));
|
|
|
|
bvp->To_Val = 0;
|
|
bvp->Nd = 0;
|
|
bvp->Type = type;
|
|
bvp->Next = 0;
|
|
return bvp;
|
|
} // end of SubAllocVal
|
|
|
|
/***********************************************************************/
|
|
/* Sub-allocate and initialize a BVAL as type. */
|
|
/***********************************************************************/
|
|
PBVAL BJSON::SubAllocVal(OFFSET toval, int type, short nd)
|
|
{
|
|
PBVAL bvp = NewVal(type);
|
|
|
|
bvp->To_Val = toval;
|
|
bvp->Nd = nd;
|
|
return bvp;
|
|
} // end of SubAllocVal
|
|
|
|
/***********************************************************************/
|
|
/* Sub-allocate and initialize a BVAL as string. */
|
|
/***********************************************************************/
|
|
PBVAL BJSON::SubAllocStr(OFFSET toval, short nd)
|
|
{
|
|
PBVAL bvp = NewVal(TYPE_STRG);
|
|
|
|
bvp->To_Val = toval;
|
|
bvp->Nd = nd;
|
|
return bvp;
|
|
} // end of SubAllocStr
|
|
|
|
/***********************************************************************/
|
|
/* Allocate a BVALUE with a given string or numeric value. */
|
|
/***********************************************************************/
|
|
PBVAL BJSON::NewVal(PVAL valp)
|
|
{
|
|
PBVAL vlp = NewVal();
|
|
|
|
SetValue(vlp, valp);
|
|
return vlp;
|
|
} // end of SubAllocVal
|
|
|
|
/***********************************************************************/
|
|
/* Sub-allocate and initialize a BVAL from another BVAL. */
|
|
/***********************************************************************/
|
|
PBVAL BJSON::DupVal(PBVAL bvlp)
|
|
{
|
|
if (bvlp) {
|
|
PBVAL bvp = NewVal();
|
|
|
|
*bvp = *bvlp;
|
|
bvp->Next = 0;
|
|
return bvp;
|
|
} else
|
|
return NULL;
|
|
|
|
} // end of DupVal
|
|
|
|
/***********************************************************************/
|
|
/* Return the size of value's value. */
|
|
/***********************************************************************/
|
|
int BJSON::GetSize(PBVAL vlp, bool b)
|
|
{
|
|
switch (vlp->Type) {
|
|
case TYPE_JAR:
|
|
return GetArraySize(vlp);
|
|
case TYPE_JOB:
|
|
return GetObjectSize(vlp);
|
|
default:
|
|
return 1;
|
|
} // enswitch Type
|
|
|
|
} // end of GetSize
|
|
|
|
PBVAL BJSON::GetBson(PBVAL bvp)
|
|
{
|
|
PBVAL bp = NULL;
|
|
|
|
switch (bvp->Type) {
|
|
case TYPE_JAR:
|
|
bp = MVP(bvp->To_Val);
|
|
break;
|
|
case TYPE_JOB:
|
|
bp = GetVlp(MPP(bvp->To_Val));
|
|
break;
|
|
default:
|
|
bp = bvp;
|
|
break;
|
|
} // endswitch Type
|
|
|
|
return bp;
|
|
} // end of GetBson
|
|
|
|
/***********************************************************************/
|
|
/* Return the Value's as a Value struct. */
|
|
/***********************************************************************/
|
|
PVAL BJSON::GetValue(PGLOBAL g, PBVAL vp)
|
|
{
|
|
double d;
|
|
PVAL valp;
|
|
PBVAL vlp = vp->Type == TYPE_JVAL ? MVP(vp->To_Val) : vp;
|
|
|
|
switch (vlp->Type) {
|
|
case TYPE_STRG:
|
|
case TYPE_DBL:
|
|
case TYPE_BINT:
|
|
valp = AllocateValue(g, MP(vlp->To_Val), vlp->Type, vlp->Nd);
|
|
break;
|
|
case TYPE_INTG:
|
|
case TYPE_BOOL:
|
|
valp = AllocateValue(g, vlp, vlp->Type);
|
|
break;
|
|
case TYPE_FLOAT:
|
|
d = (double)vlp->F;
|
|
valp = AllocateValue(g, &d, TYPE_DOUBLE, vlp->Nd);
|
|
break;
|
|
default:
|
|
valp = NULL;
|
|
break;
|
|
} // endswitch Type
|
|
|
|
return valp;
|
|
} // end of GetValue
|
|
|
|
/***********************************************************************/
|
|
/* Return the Value's Integer value. */
|
|
/***********************************************************************/
|
|
int BJSON::GetInteger(PBVAL vp) {
|
|
int n;
|
|
PBVAL vlp = (vp->Type == TYPE_JVAL) ? MVP(vp->To_Val) : vp;
|
|
|
|
switch (vlp->Type) {
|
|
case TYPE_INTG:
|
|
n = vlp->N;
|
|
break;
|
|
case TYPE_FLOAT:
|
|
n = (int)vlp->F;
|
|
break;
|
|
case TYPE_DTM:
|
|
case TYPE_STRG:
|
|
n = atoi(MZP(vlp->To_Val));
|
|
break;
|
|
case TYPE_BOOL:
|
|
n = (vlp->B) ? 1 : 0;
|
|
break;
|
|
case TYPE_BINT:
|
|
n = (int)*(longlong*)MP(vlp->To_Val);
|
|
break;
|
|
case TYPE_DBL:
|
|
n = (int)*(double*)MP(vlp->To_Val);
|
|
break;
|
|
default:
|
|
n = 0;
|
|
} // endswitch Type
|
|
|
|
return n;
|
|
} // end of GetInteger
|
|
|
|
/***********************************************************************/
|
|
/* Return the Value's Big integer value. */
|
|
/***********************************************************************/
|
|
longlong BJSON::GetBigint(PBVAL vp) {
|
|
longlong lln;
|
|
PBVAL vlp = (vp->Type == TYPE_JVAL) ? MVP(vp->To_Val) : vp;
|
|
|
|
switch (vlp->Type) {
|
|
case TYPE_BINT:
|
|
lln = *(longlong*)MP(vlp->To_Val);
|
|
break;
|
|
case TYPE_INTG:
|
|
lln = (longlong)vlp->N;
|
|
break;
|
|
case TYPE_FLOAT:
|
|
lln = (longlong)vlp->F;
|
|
break;
|
|
case TYPE_DBL:
|
|
lln = (longlong)*(double*)MP(vlp->To_Val);
|
|
break;
|
|
case TYPE_DTM:
|
|
case TYPE_STRG:
|
|
lln = atoll(MZP(vlp->To_Val));
|
|
break;
|
|
case TYPE_BOOL:
|
|
lln = (vlp->B) ? 1 : 0;
|
|
break;
|
|
default:
|
|
lln = 0;
|
|
} // endswitch Type
|
|
|
|
return lln;
|
|
} // end of GetBigint
|
|
|
|
/***********************************************************************/
|
|
/* Return the Value's Double value. */
|
|
/***********************************************************************/
|
|
double BJSON::GetDouble(PBVAL vp)
|
|
{
|
|
double d;
|
|
PBVAL vlp = (vp->Type == TYPE_JVAL) ? MVP(vp->To_Val) : vp;
|
|
|
|
switch (vlp->Type) {
|
|
case TYPE_DBL:
|
|
d = *(double*)MP(vlp->To_Val);
|
|
break;
|
|
case TYPE_BINT:
|
|
d = (double)*(longlong*)MP(vlp->To_Val);
|
|
break;
|
|
case TYPE_INTG:
|
|
d = (double)vlp->N;
|
|
break;
|
|
case TYPE_FLOAT:
|
|
d = (double)vlp->F;
|
|
break;
|
|
case TYPE_DTM:
|
|
case TYPE_STRG:
|
|
d = atof(MZP(vlp->To_Val));
|
|
break;
|
|
case TYPE_BOOL:
|
|
d = (vlp->B) ? 1.0 : 0.0;
|
|
break;
|
|
default:
|
|
d = 0.0;
|
|
} // endswitch Type
|
|
|
|
return d;
|
|
} // end of GetDouble
|
|
|
|
/***********************************************************************/
|
|
/* Return the Value's String value. */
|
|
/***********************************************************************/
|
|
PSZ BJSON::GetString(PBVAL vp, char* buff)
|
|
{
|
|
char buf[32];
|
|
char* p = (buff) ? buff : buf;
|
|
PBVAL vlp = (vp->Type == TYPE_JVAL) ? MVP(vp->To_Val) : vp;
|
|
|
|
switch (vlp->Type) {
|
|
case TYPE_DTM:
|
|
case TYPE_STRG:
|
|
p = MZP(vlp->To_Val);
|
|
break;
|
|
case TYPE_INTG:
|
|
sprintf(p, "%d", vlp->N);
|
|
break;
|
|
case TYPE_FLOAT:
|
|
sprintf(p, "%.*f", vlp->Nd, vlp->F);
|
|
break;
|
|
case TYPE_BINT:
|
|
sprintf(p, "%lld", *(longlong*)MP(vlp->To_Val));
|
|
break;
|
|
case TYPE_DBL:
|
|
sprintf(p, "%.*lf", vlp->Nd, *(double*)MP(vlp->To_Val));
|
|
break;
|
|
case TYPE_BOOL:
|
|
p = (PSZ)((vlp->B) ? "true" : "false");
|
|
break;
|
|
case TYPE_NULL:
|
|
p = (PSZ)"null";
|
|
break;
|
|
default:
|
|
p = NULL;
|
|
} // endswitch Type
|
|
|
|
return (p == buf) ? (PSZ)PlugDup(G, buf) : p;
|
|
} // end of GetString
|
|
|
|
/***********************************************************************/
|
|
/* Return the Value's String value. */
|
|
/***********************************************************************/
|
|
PSZ BJSON::GetValueText(PGLOBAL g, PBVAL vlp, PSTRG text)
|
|
{
|
|
if (vlp->Type == TYPE_JOB)
|
|
return GetObjectText(g, vlp, text);
|
|
else if (vlp->Type == TYPE_JAR)
|
|
return GetArrayText(g, vlp, text);
|
|
|
|
char buff[32];
|
|
PSZ s = (vlp->Type == TYPE_NULL) ? NULL : GetString(vlp, buff);
|
|
|
|
if (s)
|
|
text->Append(s);
|
|
else if (GetJsonNull())
|
|
text->Append(GetJsonNull());
|
|
|
|
return NULL;
|
|
} // end of GetText
|
|
|
|
void BJSON::SetValueObj(PBVAL vlp, PBVAL bop)
|
|
{
|
|
CheckType(bop, TYPE_JOB);
|
|
vlp->To_Val = bop->To_Val;
|
|
vlp->Nd = bop->Nd;
|
|
vlp->Type = TYPE_JOB;
|
|
} // end of SetValueObj;
|
|
|
|
void BJSON::SetValueArr(PBVAL vlp, PBVAL bap)
|
|
{
|
|
CheckType(bap, TYPE_JAR);
|
|
vlp->To_Val = bap->To_Val;
|
|
vlp->Nd = bap->Nd;
|
|
vlp->Type = TYPE_JAR;
|
|
} // end of SetValue;
|
|
|
|
void BJSON::SetValueVal(PBVAL vlp, PBVAL vp)
|
|
{
|
|
vlp->To_Val = vp->To_Val;
|
|
vlp->Nd = vp->Nd;
|
|
vlp->Type = vp->Type;
|
|
} // end of SetValue;
|
|
|
|
PBVAL BJSON::SetValue(PBVAL vlp, PVAL valp)
|
|
{
|
|
if (!vlp)
|
|
vlp = NewVal();
|
|
|
|
if (!valp || valp->IsNull()) {
|
|
vlp->Type = TYPE_NULL;
|
|
} else switch (valp->GetType()) {
|
|
case TYPE_DATE:
|
|
if (((DTVAL*)valp)->IsFormatted())
|
|
vlp->To_Val = DupStr(valp->GetCharValue());
|
|
else {
|
|
char buf[32];
|
|
|
|
vlp->To_Val = DupStr(valp->GetCharString(buf));
|
|
} // endif Formatted
|
|
|
|
vlp->Type = TYPE_DTM;
|
|
break;
|
|
case TYPE_STRING:
|
|
vlp->To_Val = DupStr(valp->GetCharValue());
|
|
vlp->Type = TYPE_STRG;
|
|
break;
|
|
case TYPE_DOUBLE:
|
|
case TYPE_DECIM:
|
|
{ double d = valp->GetFloatValue();
|
|
int nd = (IsTypeNum(valp->GetType())) ? valp->GetValPrec() : 0;
|
|
|
|
if (nd > 0 && nd <= 6 && d >= FLT_MIN && d <= FLT_MAX) {
|
|
vlp->F = (float)valp->GetFloatValue();
|
|
vlp->Type = TYPE_FLOAT;
|
|
} else {
|
|
double* dp = (double*)BsonSubAlloc(sizeof(double));
|
|
|
|
*dp = d;
|
|
vlp->To_Val = MOF(dp);
|
|
vlp->Type = TYPE_DBL;
|
|
} // endif Nd
|
|
|
|
vlp->Nd = MY_MIN(nd, 16);
|
|
} break;
|
|
case TYPE_TINY:
|
|
vlp->B = valp->GetTinyValue() != 0;
|
|
vlp->Type = TYPE_BOOL;
|
|
break;
|
|
case TYPE_INT:
|
|
vlp->N = valp->GetIntValue();
|
|
vlp->Type = TYPE_INTG;
|
|
break;
|
|
case TYPE_BIGINT:
|
|
if (valp->GetBigintValue() >= INT_MIN32 &&
|
|
valp->GetBigintValue() <= INT_MAX32) {
|
|
vlp->N = valp->GetIntValue();
|
|
vlp->Type = TYPE_INTG;
|
|
} else {
|
|
longlong* llp = (longlong*)BsonSubAlloc(sizeof(longlong));
|
|
|
|
*llp = valp->GetBigintValue();
|
|
vlp->To_Val = MOF(llp);
|
|
vlp->Type = TYPE_BINT;
|
|
} // endif BigintValue
|
|
|
|
break;
|
|
default:
|
|
snprintf(G->Message, sizeof(G->Message), "Unsupported typ %d\n", valp->GetType());
|
|
throw(777);
|
|
} // endswitch Type
|
|
|
|
return vlp;
|
|
} // end of SetValue
|
|
|
|
/***********************************************************************/
|
|
/* Set the Value's value as the given integer. */
|
|
/***********************************************************************/
|
|
void BJSON::SetInteger(PBVAL vlp, int n)
|
|
{
|
|
vlp->N = n;
|
|
vlp->Type = TYPE_INTG;
|
|
} // end of SetInteger
|
|
|
|
/***********************************************************************/
|
|
/* Set the Value's Boolean value as a tiny integer. */
|
|
/***********************************************************************/
|
|
void BJSON::SetBool(PBVAL vlp, bool b)
|
|
{
|
|
vlp->B = b;
|
|
vlp->Type = TYPE_BOOL;
|
|
} // end of SetTiny
|
|
|
|
/***********************************************************************/
|
|
/* Set the Value's value as the given big integer. */
|
|
/***********************************************************************/
|
|
void BJSON::SetBigint(PBVAL vlp, longlong ll)
|
|
{
|
|
if (ll >= INT_MIN32 && ll <= INT_MAX32) {
|
|
vlp->N = (int)ll;
|
|
vlp->Type = TYPE_INTG;
|
|
} else {
|
|
longlong* llp = (longlong*)PlugSubAlloc(G, NULL, sizeof(longlong));
|
|
|
|
*llp = ll;
|
|
vlp->To_Val = MOF(llp);
|
|
vlp->Type = TYPE_BINT;
|
|
} // endif ll
|
|
|
|
} // end of SetBigint
|
|
|
|
/***********************************************************************/
|
|
/* Set the Value's value as the given DOUBLE. */
|
|
/***********************************************************************/
|
|
void BJSON::SetFloat(PBVAL vlp, double d, int prec)
|
|
{
|
|
int nd = MY_MIN((prec < 0) ? GetJsonDefPrec() : prec, 16);
|
|
|
|
if (nd < 6 && d >= FLT_MIN && d <= FLT_MAX) {
|
|
vlp->F = (float)d;
|
|
vlp->Type = TYPE_FLOAT;
|
|
} else {
|
|
double* dp = (double*)BsonSubAlloc(sizeof(double));
|
|
|
|
*dp = d;
|
|
vlp->To_Val = MOF(dp);
|
|
vlp->Type = TYPE_DBL;
|
|
} // endif nd
|
|
|
|
vlp->Nd = nd;
|
|
} // end of SetFloat
|
|
|
|
/***********************************************************************/
|
|
/* Set the Value's value as the given DOUBLE representation. */
|
|
/***********************************************************************/
|
|
void BJSON::SetFloat(PBVAL vlp, PSZ s)
|
|
{
|
|
char *p = strchr(s, '.');
|
|
int nd = 0;
|
|
double d = atof(s);
|
|
|
|
if (p) {
|
|
for (++p; isdigit(*p); nd++, p++);
|
|
for (--p; *p == '0'; nd--, p--);
|
|
} // endif p
|
|
|
|
SetFloat(vlp, d, nd);
|
|
} // end of SetFloat
|
|
|
|
/***********************************************************************/
|
|
/* Set the Value's value as the given string. */
|
|
/***********************************************************************/
|
|
void BJSON::SetString(PBVAL vlp, PSZ s, int ci)
|
|
{
|
|
vlp->To_Val = MOF(s);
|
|
vlp->Nd = ci;
|
|
vlp->Type = TYPE_STRG;
|
|
} // end of SetString
|
|
|
|
/***********************************************************************/
|
|
/* True when its JSON or normal value is null. */
|
|
/***********************************************************************/
|
|
bool BJSON::IsValueNull(PBVAL vlp)
|
|
{
|
|
bool b;
|
|
|
|
switch (vlp->Type) {
|
|
case TYPE_NULL:
|
|
b = true;
|
|
break;
|
|
case TYPE_JOB:
|
|
b = IsObjectNull(vlp);
|
|
break;
|
|
case TYPE_JAR:
|
|
b = IsArrayNull(vlp);
|
|
break;
|
|
default:
|
|
b = false;
|
|
} // endswitch Type
|
|
|
|
return b;
|
|
} // end of IsNull
|