/*************** 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
