diff options
Diffstat (limited to 'storage/connect/bson.cpp')
-rw-r--r-- | storage/connect/bson.cpp | 1795 |
1 files changed, 1795 insertions, 0 deletions
diff --git a/storage/connect/bson.cpp b/storage/connect/bson.cpp new file mode 100644 index 00000000..44d53ba1 --- /dev/null +++ b/storage/connect/bson.cpp @@ -0,0 +1,1795 @@ +/*************** 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 + /* fall through */ + 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 = 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 = 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 |