JsonParserByLeo/Src/JsonCore/JsonParser.mqh
Nique_372 87f98380ae
2026-10-19 13:26:09 -05:00

1926 lines
64 KiB
MQL5

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//+------------------------------------------------------------------+
//| JsonParser.mqh |
//| Copyright 2026, Niquel Mendoza. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Niquel Mendoza."
#property link "https://www.mql5.com"
#property strict
#ifndef JSONPARSERBYLEO_SRC_JSONCORE_JSONPARSER_MQH
#define JSONPARSERBYLEO_SRC_JSONCORE_JSONPARSER_MQH
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
#include "JsonDefines.mqh"
//---
#ifdef JSONPARSERBYLEO_DLL
#include "..\\DLL\\Main.mqh"
#endif // JSONPARSERBYLEO_DLL
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
namespace TSN
{
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CJsonParser : public CBaseStructuredLan
{
protected:
//---
#ifdef JSONPARSERBYLEO_DLL
int m_index[];
int m_index_r;
#endif // JSONPARSERBYLEO_DLL
//---
ENUM_TSN_JSON_LAST_ERR m_last_err;
//---
int CalcRegType(const string& file_name) override final;
public:
CJsonParser(void);
~CJsonParser(void) {}
//--- Correct padding
// Para data raw se puede usar el json uchar directamtne
void CorrectPadding();
#ifdef JSONPARSERBYLEO_DLL
void CorrectPaddingSimd();
#endif // JSONPARSERBYLEO_DLL
//--- Parseo
bool AutoParseByFileExt(const int file_ext_type);
bool Parse();
#ifdef JSONPARSERBYLEO_DLL
__forceinline bool ParseSimd();
#endif // JSONPARSERBYLEO_DLL
//--- Errores
__forceinline ENUM_TSN_JSON_LAST_ERR LastErr() const { return m_last_err; }
void CatchErr();
void ErrorInfo(const string header = "");
//--- Config
void Options(const uint f);
//--- Step
__forceinline int GetStep(const int idx) const;
__forceinline int GetStepPure(const int idx) const;
//--- GetRoot
CJsonNode GetRoot();
//--- Info
void PrintCintaTypes(const int start, const int num) const;
//--- Node
void NodeHashing(int curr, const int obj_count, const int bend, int& table_pos);
bool SerializeToDom(CDomNodeBase* root, CDomNodeManager* manager, int cur, const int end);
//--- Strings\Keys
// uso en get value o exatcion
template<typename TKey>
__forceinline string GetRawAsStr(int idx) const
{
return CharArrayToString(m_raw, TSN_SBL_CLS_STR_START(idx, int), TSN_SBL_CLS_STR_LEN(idx, (2 - sizeof(TKey)), int));
}
template<typename TKey>
__forceinline int GetRawAsUArr(int idx, uchar &res[], int pos = 0) const
{
return ArrayCopy(res, m_raw, pos, TSN_SBL_CLS_STR_START(idx, int), TSN_SBL_CLS_STR_LEN(idx, (2 - sizeof(TKey)), int));
}
template<typename TKey>
__forceinline bool GetStrAsStr(int idx, string &res) const
{
return CBSFLUnescape::MakeString<CJsonParser>(m_raw,
TSN_SBL_CLS_STR_START(idx, int),
TSN_SBL_CLS_STR_LEN(idx, (2 - sizeof(TKey)), int), res);
}
template<typename TKey>
__forceinline int GetStrAsUArr(int idx, uchar &res[], int pos = 0) const
{
return CBSFLUnescape::MakeUArray<CJsonParser, BSFL_UE_SURROGATES>(m_raw,
TSN_SBL_CLS_STR_START(idx, int),
TSN_SBL_CLS_STR_LEN(idx, (2 - sizeof(TKey)), int), res, pos);
}
//--- Funciones para node (interno de uso interno)
template <typename T>
void ToArray(int cur, T& out_arr[], const int end) const;
template <typename T>
bool ExistInArray(int cur, const T val, const uint flags, const int end) const;
template <typename T>
bool ExistInArrayHomogeneo(int cur, const T val, const int end) const;
__forceinline int NodeAt(int cur, const int index) const;
int NodeFindByHashFvn1A64(const ulong kh, int cur, const int end);
__forceinline int NodeObjAtSlow(int cur, const int index) const;
int NodeKeyToPositionSlow(const ulong kh, int cur, const int end);
int NodeGetKeys(int cur, string &keys[], const int end) const;
//--- Print error general
static void PrintError(const string& header, const uchar& buf[], const int len,
int col, int line, ENUM_TSN_JSON_LAST_ERR err, string& salida, bool print = true);
//--- Tablas generales
static const string s_dtype_to_str[JSON_VTYPE_TOTAL];
// unescape
static const uchar s_unescape_table[256];
// escape
static const uchar s_escape_table[256];
static string s_str;
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CJsonParser::CJsonParser(void)
{
m_next = TSN_JSON_TOK_INVALID + 1; // Apunta a un valor invalido
// Estro para que en caso se usa parse simd (no de errores "fantasma" dado que m_next no se usa ahi)
#ifdef JSONPARSERBYLEO_DLL
m_index_r = ArrayResize(m_index, 256, 256);
#endif // JSONPARSERBYLEO_DLL
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CJsonParser::Options(const uint f)
{
m_jit_enabled = (f & BSFL_OPT_F_ENABLE_JIT) != 0 ? TSN_SBL_BIT_JIT_INITIAL_V : TSN_SBL_BIT_JIT_INV_VAL;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
#ifdef JSONPARSERBYLEO_DLL
void CJsonParser::CorrectPaddingSimd()
{
//---
const int new_len = m_len + 64;
ArrayResize(m_raw, new_len);
//---
int i = m_len;
m_raw[i++] = ' ';
m_raw[i++] = '^'; // para cor ini para que falle bien..
// esto es para el caso de " no cerrado o { asi tal cual.. entonces ahi
// porzariamos a econnotrar un "" el cual ya lo manejaria y seria invalido
m_raw[i++] = '"';
m_raw[i++] = ' ';
m_raw[i++] = '"';
// esto es para el key... los dos primeors eran para que no de fuera de rango en index..
// en caso tengos un { asi tal cual... ecnontrar una key... pero luego le sigue : pero ahora pondmores
// , asi que == ':' fallaria retornando false, lo neceismtos dado que , se procesa como estrucuutra en la dll
m_raw[i++] = ',';
//---
for(; i < new_len; i++)
m_raw[i] = ' '; // vacio
//--- Reservamos
if(m_len > m_cinta_reserve)
{
m_cinta_reserve = m_len;
ArrayResize(m_cinta, m_len);
}
//---
if(m_len > m_index_r)
{
m_index_r = m_len;
ArrayResize(m_index, m_index_r);
}
}
#endif // JSONPARSERBYLEO_DLL
// {"a":0}
//+------------------------------------------------------------------+
void CJsonParser::CorrectPadding()
{
//---
//const int new_len = ;
ArrayResize(m_raw, m_len + 11);
//--- Rellenamos
// esto es por el escapado. caso borde donde el json su ultimo char sea \ y avanze ++
// entonces con esto apaleamos eso..
m_raw[m_len] = ' ';
// este caracter es invalido por lo que en casos donde haya [ (sin mas asi tal cual)
// como no tenemos chekeos de rango entonces se tompara con este daond un error de malformacion
m_raw[m_len + 1] = '^';
m_raw[m_len + 2] = '"';
// justo luego 8 caracteres
m_raw[m_len + 3] = '\n'; // en una key invalida cargara a paritr de aqui 7 caracterres mas..
m_raw[m_len + 4] = '"'; // esto para poder encontrar el cierre de key... aparte en asm
// un token " asi suelto no es valido asi que daria error en caso haya un ; asi no mas.. sin \n (lo enocnr)
// igual por el padding pero luego avnazar a " pero este como tal no es token valido
// invalidos para que sigmoas si un valor esta fuera de rango como un string o algo asi
// falle aqui....
m_raw[m_len + 5] = '^'; //
m_raw[m_len + 6] = '^';
m_raw[m_len + 7] = '^';
m_raw[m_len + 8] = '^';
m_raw[m_len + 9] = '^';
m_raw[m_len + 10] = '^';
//--- Reservamos
if(m_len > m_cinta_reserve)
{
m_cinta_reserve = m_len;
ArrayResize(m_cinta, m_len);
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
// ponemos el jit attemp en vlao rinvalido, y table size le hacemos un pop..
#define BASESPARSERSAN_NODEHASHING_FINISH_ERR \
m_cinta[lock] = TSN_SBL_BIT_JIT_INV_VAL; \
m_tables_size--;
//---
void CJsonParser::NodeHashing(int curr, const int obj_count, const int bend, int& table_pos)
{
//--- inicial
BASESPARSERSLAN_START_NODE_HASH(table_pos, curr)
//---
m_tables[table_pos].table_size = obj_count; // Numero de objetos
//--- 4 siempre dado que menos no se permite..
m_pfh.MaxValSeed(m_max_att++);
m_pfh.InitAlg(obj_count, 0.70, 4);
//---
ArrayResize(m_tables[table_pos].pos_arr, obj_count);
//--- Obtnemos las posiciones de incio
ulong keys_h[];
int posiciones[];
ArrayResize(keys_h, obj_count);
ArrayResize(posiciones, obj_count);
int pos_c = 0;
const int lock = curr + 1;
curr += TSN_SBL_SLOT_OBJ_SIZE; // Ahora en key
//---
while(curr < bend)
{
//---
m_tables[table_pos].pos_arr[pos_c] = curr; // Assing
//---
const ulong v = m_cinta[curr];
ulong hash;
if(TSN_SBL_KEY_HAS_ISN_HASH(v)) // no tiene hash
{
int i = int(v >> TSN_SBL_BIT_STR_START);
if(!CBSFLUnescape::MakeHash<CJsonParser>(m_raw, i, m_len, hash))
{
BASESPARSERSAN_NODEHASHING_FINISH_ERR
return;
}
m_cinta[curr] |= TSN_SBL_KEY_FLAG_HASH; // lo ponemos como valido...
m_cinta[curr + TSN_SBL_KEY_OFF_HASH] = hash; // para reproducibildiad
}
else
hash = m_cinta[curr + TSN_SBL_KEY_OFF_HASH]; // en la siguiente posicion esta el hash lo obtenemos tal cual
//---
keys_h[pos_c] = hash;
posiciones[pos_c] = pos_c;
pos_c++;
//---
curr += TSN_SBL_KEYSLOT_SIZE; // salta KEY
curr += GetStep(curr); // salta VAL
}
//--- assing de tamaños
const int fn = m_pfh.m_final_table_size;
m_tables[table_pos].sizes = BSFL_T_PACK(m_pfh.m_buckets_size_last, m_pfh.m_final_table_size_last);
//--- resize de arrays iniciales
ArrayResize(m_tables[table_pos].pos_index, fn);
ArrayResize(m_tables[table_pos].key_hash, fn);
ArrayInitialize(m_tables[table_pos].key_hash, 0ULL);
//---
if(m_pfh.RunWValue(keys_h, posiciones, m_tables[table_pos].seeds, m_tables[table_pos].pos_index, m_tables[table_pos].key_hash))
return;
//----
BASESPARSERSAN_NODEHASHING_FINISH_ERR
return;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
int CJsonParser::CalcRegType(const string& file_name)
{
return (int)JPBL_HashExt(file_name);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CJsonParser::AutoParseByFileExt(const int file_ext_type)
{
switch(file_ext_type)
{
case TSN_SLF_RAW_FILE:
{
CorrectPadding(); // Auto
return Parse();
}
case TSN_SLF_RAW_EMBEBED:
{
return m_cinta_pos > 0 && m_len > 0;
}
default:
{
m_last_err = TSN_JSON_ERR_INVALID_EXT_FILE;
m_pos = 0;
return false;
}
}
}
//+------------------------------------------------------------------+
//| Macros de avanze |
//+------------------------------------------------------------------+
//--- para aumtenar el numero de elementos.
#define TSN_JSON_AUMENT curr_count++;
//--- Generico
// Luego de un valor siempre , o su fin
// 1 en la tabl to token es INVALID (dara error..de malformed..)
// Este se usa luego de un valor.. la idea es que luego de un valor "" numero t\f\n ] }
// tiene que haber o un }] o en todo caso , de continuacino solo eso...
#define TSN_JSON_NEXT_NO_NEXT_KEY \
uchar _c = m_raw[m_pos]; \
while(_c < 33) { _c = m_raw[++m_pos]; } \
m_next = g_table_json_tokens[_c]; \
if(((1 << m_next) & curr_state) == 0) \
{ \
m_last_err= TSN_JSON_ERR_MALFORDMED_JSON_AFTER_VAL; \
return false; \
}
//--- Luego de una coma en array
// Luego de una , y KEY siempre siempre valor
#define TSN_JSON_NEXT_VALUE_COMM_KEY \
uchar _c; \
while((_c=m_raw[++m_pos])<33) {} \
m_next = g_table_json_tokens[_c]; \
if(((1 << m_next) & TSN_JSON_F_ALL_I_VAL) == 0) \
{ \
m_last_err= TSN_JSON_ERR_MALFORDMED_JSON_FIND_VAL; \
return false; \
}
//--- unrolled find key
#define TSN_JSON_FINDKEY_FINAL_U4 \
if(m_raw[m_pos] == '"') \
break; \
m_pos++; \
if(m_raw[m_pos] == '"') \
break; \
m_pos++; \
if(m_raw[m_pos] == '"') \
break; \
m_pos++; \
if(m_raw[m_pos] == '"') \
break; \
m_pos++;
//--- unrooled find normal
#define JSONPARSERBYLEO_FIND_FINAL_STR_IT \
if((c = m_raw[m_pos]) == '"') \
break; \
m_pos += (c == '\\') + 1; \
if((c = m_raw[m_pos]) == '"') \
break; \
m_pos += (c == '\\') + 1; \
if((c = m_raw[m_pos]) == '"') \
break; \
m_pos += (c == '\\') + 1; \
if((c = m_raw[m_pos]) == '"') \
break; \
m_pos += (c == '\\') + 1;
//+------------------------------------------------------------------+
//| Parseo de numeros |
//+------------------------------------------------------------------+
//--- make string
#define JSONPARSERBYLEO_NUM_MAKE_STRING m_cinta[m_cinta_pos++] = JSON_VTYPE_STRING \
| ulong(m_pos - start) << TSN_SBL_BIT_STR_LEN(TSN_SBL_SLEN_STR) \
| ulong(start) << TSN_SBL_BIT_STR_START;
//+------------------------------------------------------------------+
//| Parse con simd |
//+------------------------------------------------------------------+
// m_last_err = TSN_JSON_NOT_ERR; quizas no hague falta dado qeu en la DLL siemrpe se stea con noterr o eso
#ifdef JSONPARSERBYLEO_DLL
__forceinline bool CJsonParser::ParseSimd()
{
//--- Iniciales
m_pos = m_offset;
m_tables_size = 0;
return JsonParserResolve(m_raw,
m_cinta,
m_index,
m_len,
m_jit_enabled,
(int&)m_last_err,
m_cinta_pos,
m_pos
);
}
#endif // JSONPARSERBYLEO_DLL
//+------------------------------------------------------------------+
//| Parse normal |
//+------------------------------------------------------------------+
bool CJsonParser::Parse()
{
//---
m_last_err = TSN_JSON_NOT_ERR;
m_pos = m_offset;
m_cinta_pos = 0;
m_tables_size = 0;
//---
uint curr_state = 0;
uint curr_count = 0;
uint curr_padre = 0;
//--- Inicial
const uchar _f_c = m_raw[m_pos];
if(_f_c == '{')
m_next = TSN_JSON_TOK_LLAVE_INI;
else
if(_f_c == '[')
m_next = TSN_JSON_TOK_COR_INI;
else // No easy
{
uchar __c = m_raw[++m_pos];
while(__c < 33)
__c = m_raw[++m_pos];
//---
if(__c == '{')
m_next = TSN_JSON_TOK_LLAVE_INI;
else
if(__c == '[')
m_next = TSN_JSON_TOK_COR_INI;
else // Single value
{
m_next = TSN_JSON_TOK_INVALID;
}
}
//--- Bucle
while(true)
{
switch(m_next)
{
//---
case TSN_JSON_TOK_NUMBER_NEG:
{
//--- solo 3 varaibles nada mas en general (garantizado)
long v = 0;
const uint start = m_pos;
uchar ch = m_raw[++m_pos] ^ '0';
//---
switch(0)
{
default: // scope con psoiblidad de break.
{
//--- primera iteracion sin cheking
DOMBASEUTIL_PARSE_NUM_U1(v)
//--- Interesante: sigue siendo cero pero me pide mas digitios ?¿
if(!v)
{
m_last_err = TSN_JSON_ERR_LEADING_ZEROS;
m_next = TSN_JSON_TOK_INVALID;
continue;
}
//--- 19 iteraciones maximo
DOMBASEUTIL_PARSE_NUM_UNROLLED_17(v)
STRCONV_TOV_ALG(v, ch)
if((ch = m_raw[++m_pos] ^ '0') > 9)
{
if(v > 0)
break;
}
else // aun es valido asi que seguimos saltando el numero completo (parte entera) (pero ya sabmeos que es invalido)
{
DOMBASEUTIL_NUM_SKIP_NUMBER
}
//--- revismoos si es dec, si lo es saltamos la parte dec
if(STRCONV_DECX_CH_IS_DEC(ch))
break;
//--- pasamos a str y ya no caemos abajo de aqui:
JSONPARSERBYLEO_NUM_MAKE_STRING
TSN_JSON_AUMENT
TSN_JSON_NEXT_NO_NEXT_KEY
continue; // esto aplica a nivel de while
}
}
//--- vemos si decimal
if(STRCONV_DECX_CH_IS_DEC(ch))
{
long exponente = 0; // exponente
uint digitos = m_pos - start; // digitos que se van acomulando (parte ent + dec)
if(STRCONV_DECX_CH_IS_PDEC(ch))
{
uint ds = ++m_pos; // inicio
if((ch = m_raw[m_pos] ^ '0') > 9)
{
m_last_err = TSN_JSON_ERR_EXPECTED_NUMBER_AFECTER_DECIMALP;
return false;
}
while(true)
{
//DOMBASEUTIL_NUM_PROCCES_FIRST_DEC(v, ch, m_pos, ds, digitos)
DOMBASEUTIL_PARSE_NUM_UNROLLED_18(v)
DOMBASEUTIL_NUM_SKIP_NUMBER
break;
}
//---
ds = (m_pos - ds); // numero de digitos de la parte decimal
digitos += ds;
exponente -= ds;
}
if(STRCONV_DECX_CH_IS_E(ch)) //
{
bool exp_positivo = true;
ch = m_raw[++m_pos] ^ '0';
if(ch > 9)
{
if(STRCONV_DECX_CH_IS_MINUS(ch))
exp_positivo = false;
if((ch = m_raw[++m_pos] ^ '0') > 9)
{
m_last_err = TSN_JSON_ERR_EXPECTED_NUMBER_AFECTER_EXP;
return false; // sale
}
}
int expo = 0;
while(true) // siguimos acomulando en v
{
DOMBASEUTIL_PARSE_NUM_U1(expo)
if(expo == 0) // leading zeros.. ahora mismo aun hay digitos pero el primero es 0..
{
DOMBASEUTIL_PARSE_SKIP_LZ
} // pos apunta al curr no cero ch ya tiene su valor en caso sea 00000x (no numero) sale..
DOMBASEUTIL_PARSE_NUM_UNROLLED_8(expo)
STRCONV_TOV_ALG(expo, ch)
if((ch = m_raw[++m_pos] ^ '0') > 9) // 10cimo digito
{
if(expo > 0) // es valido
break; // no ov
}
else // sigue saltamos todo lo que hya
{
DOMBASEUTIL_NUM_SKIP_NUMBER
}
expo = INT_MAX;
break;
}
exponente += exp_positivo ? expo : -expo;
}
//--- 1 dado que start incluye el -
if(STRCONV_DOUBLE_SAFE_DBL_P(digitos, exponente, 1))
{
TsnBitInterprete bit;
bit.double_value = -STRCONV_TO_DOUBLE_ALG(exponente, v);
m_cinta[m_cinta_pos++] = JSON_VTYPE_REAL;
m_cinta[m_cinta_pos++] = bit.ulong_value;
}
else // make_string: numero grande o que ya no se peude representar con precision (a string)
{
JSONPARSERBYLEO_NUM_MAKE_STRING
}
}
else
{
m_cinta[m_cinta_pos++] = JSON_VTYPE_INTEGER;
m_cinta[m_cinta_pos++] = -v;
}
//---
TSN_JSON_AUMENT
TSN_JSON_NEXT_NO_NEXT_KEY
break;
}
//---
case TSN_JSON_TOK_NUMBER:
{
//---
const uint start = m_pos;
// Primer valor
const uchar fc = m_raw[start] ^ '0';
ulong v = fc; // Tal cual sin mas..
// Segundo valor
uchar ch = m_raw[++m_pos] ^ '0';
//---
switch(0)
{
default: // scope con psoiblidad de break.
{
//---
if(ch > 9) // Mayor
break;
if(!v) // LZ
{
m_last_err = TSN_JSON_ERR_LEADING_ZEROS;
m_next = TSN_JSON_TOK_INVALID;
continue;
}
//--- 19 iteraciones maximo
DOMBASEUTIL_PARSE_NUM_UNROLLED_18(v)
STRCONV_TOV_ALG(v, ch)
if((ch = m_raw[++m_pos] ^ '0') > 9)
{
if(fc == 1 && v > LONG_MAX)
break;
}
else // aun es valido asi que seguimos saltando el numero completo (parte entera) (pero ya sabmeos que es invalido)
{
DOMBASEUTIL_NUM_SKIP_NUMBER
}
//--- revismoos si es dec, si es asi lo mandamos para la funcion dec aqui solo big integer
if(STRCONV_DECX_CH_IS_DEC(ch))
break;
//--- pasamos a str y ya no caemos abajo de aqui:
JSONPARSERBYLEO_NUM_MAKE_STRING
TSN_JSON_AUMENT
TSN_JSON_NEXT_NO_NEXT_KEY
continue; // esto aplica a nivel de while
}
}
//--- vemos si decimal
if(STRCONV_DECX_CH_IS_DEC(ch)) // v por lo tanto llega bien aqui como entero unsigned valido
{
long exponente = 0; // exponente
uint digitos = m_pos - start; // digitos que se van acomulando (parte ent + dec)
if(STRCONV_DECX_CH_IS_PDEC(ch)) // punto decimal (tomar precuacion aqui si v puede dar overflow y voltear no hay chekeo)
{
uint ds = ++m_pos;
if((ch = m_raw[m_pos] ^ '0') > 9)
{
m_last_err = TSN_JSON_ERR_EXPECTED_NUMBER_AFECTER_DECIMALP;
return false;
}
while(true)
{
//DOMBASEUTIL_NUM_PROCCES_FIRST_DEC(v, ch, m_pos, ds, digitos)
DOMBASEUTIL_PARSE_NUM_UNROLLED_18(v)
DOMBASEUTIL_NUM_SKIP_NUMBER
break;
}
//---
ds = (m_pos - ds); // numero de digitos de la parte decimal
digitos += ds;
exponente -= ds;
}
if(STRCONV_DECX_CH_IS_E(ch)) //
{
bool exp_positivo = true;
ch = m_raw[++m_pos] ^ '0';
if(ch > 9)
{
if(STRCONV_DECX_CH_IS_MINUS(ch))
exp_positivo = false;
if((ch = m_raw[++m_pos] ^ '0') > 9)
{
m_last_err = TSN_JSON_ERR_EXPECTED_NUMBER_AFECTER_EXP;
return false; // sale
}
}
int expo = 0;
while(true)
{
DOMBASEUTIL_PARSE_NUM_U1(expo)
if(expo == 0) // leading zeros.. ahora mismo aun hay digitos pero el primero es 0..
{
DOMBASEUTIL_PARSE_SKIP_LZ
} // pos apunta al curr no cero ch ya tiene su valor en caso sea 00000x (no numero) sale..
DOMBASEUTIL_PARSE_NUM_UNROLLED_8(expo)
STRCONV_TOV_ALG(expo, ch)
if((ch = m_raw[++m_pos] ^ '0') > 9) // 10cimo digito
{
if(expo > 0) // es valido
break; // no ov
}
else // sigue saltamos todo lo que hya
{
DOMBASEUTIL_NUM_SKIP_NUMBER
}
expo = INT_MAX;
break;
}
exponente += exp_positivo ? expo : -expo;
}
//---
if(STRCONV_DOUBLE_SAFE_DBL(digitos, exponente))
{
TsnBitInterprete bit;
bit.double_value = STRCONV_TO_DOUBLE_ALG(exponente, v);
m_cinta[m_cinta_pos++] = JSON_VTYPE_REAL;
m_cinta[m_cinta_pos++] = bit.ulong_value;
}
else
{
JSONPARSERBYLEO_NUM_MAKE_STRING
}
}
else
{
m_cinta[m_cinta_pos++] = JSON_VTYPE_UINTEGER;
m_cinta[m_cinta_pos++] = v;
}
//---
TSN_JSON_AUMENT
TSN_JSON_NEXT_NO_NEXT_KEY
break;
}
//---
case TSN_JSON_TOK_STRING:
{
//---
const uint start = ++m_pos;
//---
#ifndef JSONPARSERBYLEO_DESACTIVE_SWAR
//--- Pase inicial 8 en 8
while(true)
{
// PrintFormat("Analizando: '%s'", CharArrayToString(m_raw, m_pos, 8));
const ulong chunk = (ulong)m_raw[m_pos]
| (ulong)m_raw[m_pos + 1] << 8
| (ulong)m_raw[m_pos + 2] << 16
| (ulong)m_raw[m_pos + 3] << 24
| (ulong)m_raw[m_pos + 4] << 32
| (ulong)m_raw[m_pos + 5] << 40
| (ulong)m_raw[m_pos + 6] << 48
| (ulong)m_raw[m_pos + 7] << 56;
//---
const ulong x_bs = chunk ^ TSNTABLES_SWAR_MASK_BACK_SLASH;
const ulong x_qt = chunk ^ TSNTABLES_SWAR_MASK_COMILLA;
ulong has_bs = TSNTABLES_SWAR_HAS(x_bs);
ulong has_qt = TSNTABLES_SWAR_HAS(x_qt);
//---
if((has_bs | has_qt) == 0) // Fast path
{
m_pos += 8;
continue;
}
//---
if(has_bs == 0) // Si not iene bs (pero confirmamos que no es 0) (Fast path)
{
const ulong flag_real = TSNTABLES_BIT_DEJAR_SOLO_MINUS_SIG_BYTE(has_qt);
m_pos += TSNTABLES_CTZ_64_GET_BYTE(flag_real);
m_cinta[m_cinta_pos++] = JSON_VTYPE_STRING
| ulong(m_pos - start) << TSN_SBL_BIT_STR_LEN(TSN_SBL_SLEN_STR)
| ulong(start) << TSN_SBL_BIT_STR_START;
m_pos++; // Luego del "
break;
}
//--- Slow path
has_bs = TSNTABLES_SWAR_HAS_EXACT(x_bs);
has_qt = TSNTABLES_SWAR_HAS_EXACT(x_qt);
//--- Hay bs si o si pero tambien puede haber "
const uchar compacted_bs = TSNTABLES_COMPACT_UL(has_bs);
const uchar prev = (((compacted_bs << 1) | TSNTABLES_MASK_IMPARES) - compacted_bs) ^ TSNTABLES_MASK_IMPARES;
const uchar escaped = prev ^ compacted_bs;
//---
const uchar compacted_qt = TSNTABLES_COMPACT_UL(has_qt);
const uchar qt_final = compacted_qt & ~escaped;
//Print("nada");
//---
if(qt_final) // Hay un qt real de cierre
{
//---
// Avanzamos y cerramos..
m_pos += g_tsntables_table_ctz_i8[qt_final];
m_cinta[m_cinta_pos++] = JSON_VTYPE_STRING
| ulong(m_pos - start) << TSN_SBL_BIT_STR_LEN(TSN_SBL_SLEN_STR)
| ulong(start) << TSN_SBL_BIT_STR_START;
//---
m_pos++;
break;
}
else
{
m_pos += 8;// + ((prev & compacted_bs) >> 7U);
// Print(CharToString(m_raw[m_pos]));
// escape
if(((prev & compacted_bs) >> 7U) != 0) // Overflow el ultimo char es \ y el siguiente escapa
{
//Print("siuu");
m_pos++; // Uno mas.. luego del currend
}
}
}
#else // JSONPARSERBYLEO_DESACTIVE_SWAR
//--- Char por char
uchar c;
while(true) // Find unrrolled 20
{
JSONPARSERBYLEO_FIND_FINAL_STR_IT
JSONPARSERBYLEO_FIND_FINAL_STR_IT
JSONPARSERBYLEO_FIND_FINAL_STR_IT
JSONPARSERBYLEO_FIND_FINAL_STR_IT
JSONPARSERBYLEO_FIND_FINAL_STR_IT
}
//---
m_cinta[m_cinta_pos++] = JSON_VTYPE_STRING | ulong(m_pos - start) << TSN_SBL_BIT_STR_LEN(TSN_SBL_SLEN_STR) |
ulong(start) << TSN_SBL_BIT_STR_START;
m_pos++;
#endif // JSONPARSERBYLEO_DESACTIVE_SWAR
//---
TSN_JSON_AUMENT
TSN_JSON_NEXT_NO_NEXT_KEY
//---
break;
}
case TSN_JSON_TOK_BOOL_TRUE:
{
const uint k = (uint)m_raw[++m_pos] | (uint)m_raw[++m_pos] << 8 | (uint)m_raw[++m_pos] << 16;
if(k != TSNJSON_VAL_TRUE)
return false;
//---
m_pos++;
m_cinta[m_cinta_pos++] = (JSON_VTYPE_BOOLEAN | 1ULL << TSN_SBL_BIT_START_BOOL);
//---
TSN_JSON_AUMENT
TSN_JSON_NEXT_NO_NEXT_KEY
break;
}
case TSN_JSON_TOK_BOOL_FALSE:
{
//const uint k = (uint)m_raw[++m_pos] | (uint)m_raw[++m_pos] << 8 | (uint)m_raw[++m_pos] << 16 | (uint)m_raw[++m_pos] << 24;
if(m_raw[++m_pos] != 'a' || m_raw[++m_pos] != 'l' || m_raw[++m_pos] != 's' || m_raw[++m_pos] != 'e')
return false;
//---
m_pos++;
m_cinta[m_cinta_pos++] = JSON_VTYPE_BOOLEAN;
//---
TSN_JSON_AUMENT
TSN_JSON_NEXT_NO_NEXT_KEY
break;
}
case TSN_JSON_TOK_NULL:
{
const uint k = (uint)m_raw[++m_pos] | (uint)m_raw[++m_pos] << 8 | (uint)m_raw[++m_pos] << 16;
if(k != TSNJSON_VAL_NULL)
return false;
//---
m_pos++;
m_cinta[m_cinta_pos++] = JSON_VTYPE_NULL;
//---
TSN_JSON_AUMENT
TSN_JSON_NEXT_NO_NEXT_KEY
break;
}
case TSN_JSON_TOK_COMMA:
{
if(curr_state == TSN_JSON_CTX_IN_OBJ)
{
while(m_raw[++m_pos] != '"') {}
m_next = TSN_JSON_TOK_KEY;
}
else
{
TSN_JSON_NEXT_VALUE_COMM_KEY
}
break;
}
case TSN_JSON_TOK_LLAVE_INI:
{
//--- guardar lo uqe tenaismos (numero de elementos) (+1 para contar este nuevo elemento)
m_cinta[curr_padre] = curr_count + 1;
//--- nuevo, ahora guardamos la direccion de nuesto padre y la nuestar (rempalzao num el por mientas)
m_cinta[m_cinta_pos] = JSON_VTYPE_OBJ |
ulong(m_cinta_pos) << TSN_SBL_PARSE_START_CINTAPOS |
ulong(curr_padre) << TSN_SBL_BIT_START_NUM_C;
curr_padre = ++m_cinta_pos; // apunta al inicio + 1 (lo modificamos para count, ahora apunta aqui..)
m_cinta_pos++; // salta la posicion 1
//--- reiniciamos los estados
curr_state = TSN_JSON_CTX_IN_OBJ;
curr_count = 0;
//---
uchar _c = m_raw[++m_pos];
while(_c < 33)
_c = m_raw[++m_pos];
//---
if(_c == '"')
m_next = TSN_JSON_TOK_KEY;
else
if(_c == '}')
m_next = TSN_JSON_TOK_LLAVE_END;
else
{
m_last_err = TSN_JSON_ERR_MALFORDMED_JSON_AFTER_LLAVE_INI;
m_next = TSN_JSON_TOK_INVALID;
}
//---
break;
}
case TSN_JSON_TOK_COR_INI:
{
//--- guardar lo uqe tenaismos (numero de elementos) (+1 para contar este nuevo elemento)
m_cinta[curr_padre] = curr_count + 1;
//--- nuevo, ahora guardamos la direccion de nuesto padre y la nuestar (rempalzao num el por mientas)
m_cinta[m_cinta_pos] = JSON_VTYPE_ARR |
ulong(m_cinta_pos) << TSN_SBL_PARSE_START_CINTAPOS |
ulong(curr_padre) << TSN_SBL_BIT_START_NUM_C;
curr_padre = ++m_cinta_pos; // apunta al inicio + 1 (lo modificamos para count, ahora apunta aqui..)
m_cinta_pos++; // salta la posicion 1
//--- reiniciamos los estados
curr_state = TSN_JSON_CTX_IN_ARR;
curr_count = 0;
//--- Find
//- con el pre salta.. el [
uchar _c = m_raw[++m_pos];
while(_c < 33)
_c = m_raw[++m_pos];
//- check
m_next = g_table_json_tokens[_c];
if(((1 << m_next) & TSN_JSON_F_VAL_COR) == 0)
{
m_last_err = TSN_JSON_ERR_MALFORDMED_JSON_AFTER_COR_INI;
return false;
}
//---
break;
}
case TSN_JSON_TOK_LLAVE_END:
{
//--- cacheamos el valor de cinta [curr_padre-1] = apunta a los datos
m_cinta[curr_padre] = m_jit_enabled; // le damos su valor final
const uint idata = --curr_padre;
const ulong v = m_cinta[idata];
// ahora si rehacemos el valor con todo ya listo.. (curr padre apunta a la posicion correcta)
m_cinta[idata] = JSON_VTYPE_OBJ | ulong(curr_count) << TSN_SBL_BIT_START_NUM_EL |
ulong(m_cinta_pos - ((v >> 1) & TSN_SBL_PARSE_MASK_CINTAPOS_S)) << TSN_SBL_BIT_START_NUM_C;
curr_padre = uint(v >> TSN_SBL_BIT_START_NUM_C); // ahora obtenemos su padre..
//---
if(curr_padre == idata) // dado que cur_padre apunta a la psocion 2.. [0][1 aqui] entonces con 1...
return true;
//---
curr_count = (uint)m_cinta[curr_padre]; // le devolvemos..
curr_state = TSN_JSON_MAKE_STATE(m_cinta[curr_padre - 1] & TSN_SBL_PARSE_MASK_OA_T);
m_pos++;
//---
TSN_JSON_NEXT_NO_NEXT_KEY
break;
}
case TSN_JSON_TOK_COR_END:
{
//--- cacheamos el valor de cinta [curr_padre-1] = apunta a los datos
const uint idata = --curr_padre;
const ulong v = m_cinta[idata];
// ahora si rehacemos el valor con todo ya listo.. (curr padre apunta a la posicion correcta)
m_cinta[idata] = JSON_VTYPE_ARR | ulong(curr_count) << TSN_SBL_BIT_START_NUM_EL |
ulong(m_cinta_pos - ((v >> 1) & TSN_SBL_PARSE_MASK_CINTAPOS_S)) << TSN_SBL_BIT_START_NUM_C;
curr_padre = uint(v >> TSN_SBL_BIT_START_NUM_C); // ahora obtenemos su padre..
//---
if(curr_padre == idata) // dado que cur_padre apunta a la psocion 2.. [0][1 aqui] entonces con 1...
return true;
//---
curr_count = (uint)m_cinta[curr_padre]; // le devolvemos..
curr_state = TSN_JSON_MAKE_STATE(m_cinta[curr_padre - 1] & TSN_SBL_PARSE_MASK_OA_T);
m_pos++;
//---
TSN_JSON_NEXT_NO_NEXT_KEY
break;
}
case TSN_JSON_TOK_KEY:
{
const uint start = ++m_pos;
//--- Char por char
#ifdef JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS
while(true)
{
const uchar c = m_raw[m_pos];
if(c == '\\')
{
m_pos += 2;
continue;
}
if(c == '"')
{
m_cinta[m_cinta_pos++] = JSON_VTYPE_KEY
| ulong(m_pos - start) << TSN_SBL_BIT_STR_LEN(TSN_SBL_SLEN_KEY)
| ulong(start) << TSN_SBL_BIT_STR_START;
m_pos++;
break;
}
m_pos++;
}
#else // JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS*/
//---
while(true)
{
TSN_JSON_FINDKEY_FINAL_U4
TSN_JSON_FINDKEY_FINAL_U4
TSN_JSON_FINDKEY_FINAL_U4
TSN_JSON_FINDKEY_FINAL_U4
}
//---
m_cinta[m_cinta_pos++] = JSON_VTYPE_KEY
| ulong(m_pos - start) << TSN_SBL_BIT_STR_LEN(TSN_SBL_SLEN_KEY)
| ulong(start) << TSN_SBL_BIT_STR_START;
m_pos++;
#endif // JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS
//---
m_cinta_pos++; // hash ignoramos.
//--- Find :
while(m_raw[m_pos] != ':')
{
//---
//if(m_raw[m_pos] == ':')
// break;
//---
m_pos++;
if(m_pos >= m_len)
{
m_last_err = TSN_JSON_ERR_MALFODERD_KEY_EXPECTED_DOS_PUNTOS;
return false;
}
}
//--- Buscamos valor
TSN_JSON_NEXT_VALUE_COMM_KEY
//---
break;
}
default:
return false;
}
}
}
//+------------------------------------------------------------------+
//| GetStep |
//+------------------------------------------------------------------+
__forceinline int CJsonParser::GetStepPure(const int idx) const
{
switch(int(m_cinta[idx] & TSN_SBL_BIT_MASK_TYPE))
{
case JSON_VTYPE_ARR:
case JSON_VTYPE_OBJ:
case JSON_VTYPE_UINTEGER:
case JSON_VTYPE_INTEGER:
case JSON_VTYPE_REAL:
return 2;
case JSON_VTYPE_BOOLEAN:
case JSON_VTYPE_STRING:
case JSON_VTYPE_NULL:
return 1;
case JSON_VTYPE_KEY:
return TSN_SBL_KEYSLOT_SIZE;
default:
return 1;
}
}
//+------------------------------------------------------------------+
__forceinline int CJsonParser::GetStep(const int idx)
const
{
switch(int(m_cinta[idx] & TSN_SBL_BIT_MASK_TYPE))
{
case JSON_VTYPE_ARR:
case JSON_VTYPE_OBJ:
return int(m_cinta[idx] >> TSN_SBL_BIT_START_NUM_C);
case JSON_VTYPE_UINTEGER:
case JSON_VTYPE_INTEGER:
case JSON_VTYPE_REAL:
return 2;
case JSON_VTYPE_BOOLEAN:
case JSON_VTYPE_STRING:
case JSON_VTYPE_NULL:
return 1;
case JSON_VTYPE_KEY:
return TSN_SBL_KEYSLOT_SIZE;
default:
return 1;
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CJsonParser::SerializeToDom(CDomNodeBase *root, CDomNodeManager* manager, int cur, const int end)
{
//--- Temp
m_last_err = TSN_JSON_NOT_ERR;
CDomNodeBase* prev_value; // valolr qeu devuelve key
CDomNodeBase* current_container[JSONPARSERBYLEO_MAX_STACK]; // contenedor
int current_ends[JSONPARSERBYLEO_MAX_STACK];
int sp = 0;
//--- Inicial
const int c = int((m_cinta[cur] >> TSN_SBL_BIT_START_NUM_EL) & TSN_SBL_BIT_MASK_NUM_EL);
if((m_cinta[cur]&TSN_SBL_BIT_MASK_TYPE) == JSON_VTYPE_OBJ)
root.NewObj(c);
else
root.NewArr(c);
//---
current_container[0] = root;
current_ends[sp] = end;
//---
cur += 2;
//--- It
while(true)
{
//--- Check
while(cur >= current_ends[sp])
{
sp--; // Nivel menos
//Print("sp luego: ", sp);
if(sp < 0)
return true;
}
//---
const ENUM_JSON_VTYPE tipo = ENUM_JSON_VTYPE(m_cinta[cur] & TSN_SBL_BIT_MASK_TYPE);
//Print(EnumToString(tipo), " | ", cur, " | " , sp);
//---
switch(tipo)
{
case JSON_VTYPE_ARR:
{
const int count = int((m_cinta[cur] >> TSN_SBL_BIT_START_NUM_EL) & TSN_SBL_BIT_MASK_NUM_EL);
if(current_container[sp].m_type == JSON_VTYPE_ARR)
{
const int lock = sp++;
current_container[sp] = new CDomNodeBase(manager);
current_container[sp].NewArr(count);
current_container[lock].AddItem(current_container[sp]);
}
else // {key:[]}
{
sp++;
prev_value.NewArr(count);
current_container[sp] = prev_value;
}
current_ends[sp] = cur + int(m_cinta[cur] >> TSN_SBL_BIT_START_NUM_C);
break;
}
case JSON_VTYPE_OBJ:
{
const int count = (int((m_cinta[cur] >> TSN_SBL_BIT_START_NUM_EL) & TSN_SBL_BIT_MASK_NUM_EL)) << 1;
if(current_container[sp].m_type == JSON_VTYPE_ARR) // [{}]
{
const int lock = sp++;
current_container[sp] = new CDomNodeBase(manager);
current_container[sp].NewObj(count);
current_container[lock].AddItem(current_container[sp]);
}
else // {key:{}}
{
sp++;
prev_value.NewObj(count);
current_container[sp] = prev_value;
}
current_ends[sp] = cur + int(m_cinta[cur] >> TSN_SBL_BIT_START_NUM_C);
break;
}
case JSON_VTYPE_UINTEGER:
{
if(current_container[sp].m_type == JSON_VTYPE_ARR)
current_container[sp].AddItem(new CDomNodeBase(manager, m_cinta[cur + 1]));
else
prev_value = m_cinta[cur + 1];
break;
}
case JSON_VTYPE_INTEGER:
{
if(current_container[sp].m_type == JSON_VTYPE_ARR)
current_container[sp].AddItem(new CDomNodeBase(manager, (long)m_cinta[cur + 1]));
else
prev_value = (long)m_cinta[cur + 1];
break;
}
case JSON_VTYPE_REAL:
{
TsnBitInterprete un;
un.ulong_value = m_cinta[cur + 1];
//---
if(current_container[sp].m_type == JSON_VTYPE_ARR)
current_container[sp].AddItem(new CDomNodeBase(manager, un.double_value));
else
prev_value = un.double_value;
break;
}
case JSON_VTYPE_BOOLEAN:
{
const bool v = (m_cinta[cur] >> TSN_SBL_BIT_START_BOOL) != 0;
//---
if(current_container[sp].m_type == JSON_VTYPE_ARR)
current_container[sp].AddItem(new CDomNodeBase(manager, v));
else
prev_value = v;
break;
}
case JSON_VTYPE_STRING:
{
//---
const int s = TSN_SBL_CLS_STR_START(cur, int);
const int slen = TSN_SBL_CLS_STR_LEN(cur, TSN_SBL_SLEN_STR, int);
//---
if(current_container[sp].m_type == JSON_VTYPE_ARR)
{
CDomNodeBase* ptr = new CDomNodeBase(TSN_SBL_BASE_TYPE_STR, manager);
current_container[sp].AddItem(ptr); // lo agregamos
const int idx = ptr.m_v.ix;
if(!JSONPBL_MAKESTRING(m_raw, s, slen, manager.m_string_stack[idx]))
{
m_pos = s;
m_last_err = TSN_JSON_ERR_IN_MATERIALIZE_STRING_PARSE_ERR;
return false;
}
}
else
{
prev_value.Type(TSN_SBL_BASE_TYPE_STR); // lo hacemos string
const int idx = prev_value.m_v.ix;
if(!JSONPBL_MAKESTRING(m_raw, s, slen, manager.m_string_stack[idx]))
{
m_pos = s;
m_last_err = TSN_JSON_ERR_IN_MATERIALIZE_STRING_PARSE_ERR;
return false;
}
}
break;
}
case JSON_VTYPE_NULL:
{
//---
if(current_container[sp].m_type == JSON_VTYPE_ARR)
current_container[sp].AddItem(new CDomNodeBase(manager));
else
prev_value.m_type = JSON_VTYPE_NULL;
break;
}
case JSON_VTYPE_KEY:
{
const int s = TSN_SBL_CLS_STR_START(cur, int);
const int slen = TSN_SBL_CLS_STR_LEN(cur, TSN_SBL_SLEN_KEY, int);
if(!JSONPBL_MAKESTRING(m_raw, s, slen, s_str))
{
m_pos = s;
m_last_err = TSN_JSON_ERR_IN_MATERIALIZE_STRING_PARSE_ERR;
return false;
}
prev_value = current_container[sp][s_str];
break;
}
default:
break;
}
//---
cur += GetStepPure(cur);
}
//---
return false;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CJsonParser::PrintCintaTypes(const int start, const int num) const
{
//---
int p = fmax(start, 0);
const int end = (num == WHOLE_ARRAY || (p + num) > m_cinta_pos) ? m_cinta_pos : (p + num);
//---
string text;
StringReserve(text, 256); // algo razonable.
//---
while(p < end)
{
const ENUM_JSON_VTYPE tipo = ENUM_JSON_VTYPE(m_cinta[p] & TSN_SBL_BIT_MASK_TYPE);
text = "[" + (string)p + "] " + EnumToString(tipo) + " | ";
//---
switch(tipo)
{
case JSON_VTYPE_ARR:
{
const int count = TSN_SBL_CLS_AO_NUM_EL(p, int);
const int num_c = TSN_SBL_CLS_AO_STEP(p, int);
text += "count=" + (string)count + " num_c=" + (string)num_c;
break;
}
case JSON_VTYPE_OBJ:
{
const int count = TSN_SBL_CLS_AO_NUM_EL(p, int);
const int num_c = TSN_SBL_CLS_AO_STEP(p, int);
const bool jit = TSN_SBL_CLS_OBJ_JIT(p + 1) != 0;
const ulong attemps = TSN_SBL_CLS_OBJ_JIT_ATTMP(p + 1);
text += "count=" + (string)count + " num_c=" + (string)num_c + " is_jit=" + (string)jit + " jit_attemps=" + (string)attemps;
break;
}
case JSON_VTYPE_UINTEGER:
{
text += "val=" + CValueToBuffer::ULLToStr(ulong(m_cinta[p + 1]));
break;
}
case JSON_VTYPE_INTEGER:
{
text += "val=" + (string)m_cinta[p + 1];
break;
}
case JSON_VTYPE_REAL:
{
TsnBitInterprete un;
un.ulong_value = m_cinta[p + 1];
text += "val=" + (string)un.double_value;
break;
}
case JSON_VTYPE_BOOLEAN:
text += "val=" + (string)(bool)((m_cinta[p] >> TSN_SBL_BIT_START_BOOL) & 1);
break;
case JSON_VTYPE_STRING:
{
const int s = int(m_cinta[p] >> TSN_SBL_BIT_STR_START);
const int slen = int((m_cinta[p] >> TSN_SBL_BIT_STR_LEN(TSN_SBL_SLEN_STR)) & TSN_SBL_BIT_STR_MASK_LEN(TSN_SBL_SLEN_STR));
//---
if(JSONPBL_MAKESTRING(m_raw, s, slen, s_str))
text += "start=" + (string)s + " len=" + (string)slen + " val=\"" + s_str + "\"";
else
text += "start=" + (string)s + " len=" + (string)slen + " val=Invalid";
break;
}
case JSON_VTYPE_NULL:
text += "null";
break;
case JSON_VTYPE_KEY:
{
const int s = int(m_cinta[p] >> TSN_SBL_BIT_STR_START);
const int slen = int((m_cinta[p] >> TSN_SBL_BIT_STR_LEN(TSN_SBL_SLEN_KEY)) & TSN_SBL_BIT_STR_MASK_LEN(TSN_SBL_SLEN_KEY));
//---
if(JSONPBL_MAKESTRING(m_raw, s, slen, s_str))
text += "start=" + (string)s + " len=" + (string)slen + " val=\"" + s_str + "\"";
else
text += "start=" + (string)s + " len=" + (string)slen + " val=Invalid";
break;
}
//---
default:
break;
}
Print(text);
p += GetStepPure(p);
}
}
//+------------------------------------------------------------------+
//| Errores |
//+------------------------------------------------------------------+
static void CJsonParser::PrintError(const string& header, const uchar& buf[], const int len,
int col, int line, ENUM_TSN_JSON_LAST_ERR err, string& salida, bool print = true)
{
//---
string out = "";
out.Reserve(256);
//---
if(header.Length())
out += "[" + header + "]\n";
//---
out += StringFormat("JSON error at line %d, col %d:\n", line, col);
//---
switch(err)
{
case TSN_JSON_ERR_INVALID_EXT_FILE:
out += "Invalid file extension por JsonParserByLeo";
break;
case TSN_JSON_ERR_INVALID_CHAR:
out += "Invalid character found in json";
break;
case TSN_JSON_ERR_MALFORDMED_JSON_AFTER_VAL:
out += "Invalid character find in json, finding comma or context close (], })";
break;
case TSN_JSON_ERR_MALFORDMED_JSON_AFTER_COR_INI:
out += "Invalid character find in json, finding valid value after '['";
break;
case TSN_JSON_ERR_MALFORDMED_JSON_AFTER_LLAVE_INI:
out += "Invalid character find in json, finding valid value ['}' or '\"'] after '{'";
break;
case TSN_JSON_ERR_MALFORDMED_JSON_FIND_VAL:
out += "Invalid character find in json, finding valid value";
break;
case TSN_JSON_ERR_MALFODERD_KEY_EXPECTED_DOS_PUNTOS:
out += "Invalid JSON key, One of two errors: \"Expected a ':' after a JSON key\" or \"Malformed JSON while searching for characters.\"";
break;
case TSN_JSON_ERR_MALFOMED_LEN_SUPERADO:
out += "Len has superated, but json are not closed, malformed json";
break;
case TSN_JSON_ERR_JSON_NOT_FINISH:
out += "Json read has finish but not closed in ] or }, malformed json";
break;
case TSN_JSON_ERR_LEADING_ZEROS:
out += "A number in JSON notation cannot have leading zeros.";
break;
case TSN_JSON_ERR_EXPECTED_NUMBER_AFECTER_EXP:
out += "A number was expected after the 'E' exponent; an invalid character was found.";
break;
case TSN_JSON_ERR_EXPECTED_NUMBER_AFECTER_DECIMALP:
out += "A numeric value was expected after the decimal point.";
break;
case TSN_JSON_ERR_MALFORMED_TRUE:
out += "Malformed 'true' in json";
break;
case TSN_JSON_ERR_MALFORMED_FALSE:
out += "Malformed 'false' in json";
break;
case TSN_JSON_ERR_MALFORMED_NULL:
out += "Malformed 'null' in json";
break;
case TSN_JSON_ERR_IN_MATERIALIZE_STRING_PARSE_ERR:
out += "Error materializing string; possible causes include an invalid escape character or a malformed \\u sequence.";
break;
case TSN_JSON_ERR_NOT_STRUCUTURAL_CHARS_FOUND:
out += "Not structural character found in this json ({ | } | [ | ] | : | ,)";
break;
case TSN_JSON_ERR_INVALID_STATE_IN_PARSE_DOUBLE_E:
out += "Invalid state for Json Sax Parsing, in parse double value";
break;
//---
default:
out += "Unknow error";
break;
}
//---
CParserErrFormat::Format(len, buf, line, col, out, salida);
//---
if(print)
Print(salida);
}
//+------------------------------------------------------------------+
// Solo a que tener cuidado con true\false\null
// aparte tok invalid tambien dado que este ultimo retorna false
void CJsonParser::CatchErr()
{
//---
if(m_pos >= m_len)
m_pos = m_len - 1;
//---
if(m_last_err != TSN_JSON_NOT_ERR)
return; // ya tiene valor no hace falta
//---
switch(m_next)
{
case TSN_JSON_TOK_BOOL_TRUE:
m_last_err = TSN_JSON_ERR_MALFORMED_TRUE;
break;
case TSN_JSON_TOK_BOOL_FALSE:
m_last_err = TSN_JSON_ERR_MALFORMED_FALSE;
break;
case TSN_JSON_TOK_NULL:
m_last_err = TSN_JSON_ERR_MALFORMED_NULL;
break;
default:
{
if(m_next == TSN_JSON_TOK_INVALID) // Solo si es tok invalid (si no nada)
m_last_err = TSN_JSON_ERR_INVALID_CHAR;
break;
}
}
}
//+------------------------------------------------------------------+
void CJsonParser::ErrorInfo(const string header = "")
{
//---
CatchErr();
//---
if(m_last_err == TSN_JSON_NOT_ERR)
return;
//---
int col, line;
GetLastErrorLocation(col, line);
//---
string out = "";
PrintError(header, m_raw, m_len, col, line, m_last_err, out);
}
//+------------------------------------------------------------------+
//| Tablas finales |
//+------------------------------------------------------------------+
string CJsonParser::s_str = ""; // streimp para las funciones internas (que requiran string)
const string CJsonParser::s_dtype_to_str[JSON_VTYPE_TOTAL] =
{
"",
"",
"",
"ulong",
"long",
"double",
"bool",
"string",
""
};
const uchar CJsonParser::s_unescape_table[256] =
{
// char idx ascii
//--- WS \ Invalid
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
//---
'"', // 0 0x22 \" = "
0xFF, // 1 0x23 #
0xFF, // 2 0x24 $
0xFF, // 3 0x25 %
0xFF, // 4 0x26 &
0xFF, // 5 0x27 ' \' = '
0xFF, // 6 0x28 (
0xFF, // 7 0x29 )
0xFF, // 8 0x2A *
0xFF, // 9 0x2B +
0xFF, // 10 0x2C ,
0xFF, // 11 0x2D -
0xFF, // 12 0x2E .
'/', // 13 0x2F / \/ = /
0xFF, // 14 0x30
0xFF, // 15 0x31 1
0xFF, // 16 0x32 2
0xFF, // 17 0x33 3
0xFF, // 18 0x34 4
0xFF, // 19 0x35 5
0xFF, // 20 0x36 6
0xFF, // 21 0x37 7
0xFF, // 22 0x38 8
0xFF, // 23 0x39 9
0xFF, // 24 0x3A :
0xFF, // 25 0x3B ;
0xFF, // 26 0x3C <
0xFF, // 27 0x3D =
0xFF, // 28 0x3E >
0xFF, // 29 0x3F ?
0xFF, // 30 0x40 @
0xFF, // 31 0x41 A
0xFF, // 32 0x42 B
0xFF, // 33 0x43 C
0xFF, // 34 0x44 D
0xFF, // 35 0x45 E
0xFF, // 36 0x46 F
0xFF, // 37 0x47 G
0xFF, // 38 0x48 H
0xFF, // 39 0x49 I
0xFF, // 40 0x4A J
0xFF, // 41 0x4B K
0xFF, // 42 0x4C L
0xFF, // 43 0x4D M
0xFF, // 44 0x4E N
0xFF, // 45 0x4F O
0xFF, // 46 0x50 P
0xFF, // 47 0x51 Q
0xFF, // 48 0x52 R
0xFF, // 49 0x53 S
0xFF, // 50 0x54 T
0xFF, // 51 0x55 U
0xFF, // 52 0x56 V
0xFF, // 53 0x57 W
0xFF, // 54 0x58 X
0xFF, // 55 0x59 Y
0xFF, // 56 0x5A Z
0xFF, // 57 0x5B [
'\\', // 58 0x5C \ \\ =
0xFF, // 59 0x5D ]
0xFF, // 60 0x5E ^
0xFF, // 61 0x5F _
0xFF, // 62 0x60 `
0xFF, // 63 0x61 a
0x08, // 64 0x62 b \b = backspace
0xFF, // 65 0x63 c
0xFF, // 66 0x64 d
0xFF, // 67 0x65 e
0x0C, // 68 0x66 f \f = form feed
0xFF, // 69 0x67 g
0xFF, // 70 0x68 h
0xFF, // 71 0x69 i
0xFF, // 72 0x6A j
0xFF, // 73 0x6B k
0xFF, // 74 0x6C l
0xFF, // 75 0x6D m
0x0A, // 76 0x6E n \n = newline
0xFF, // 77 0x6F o
0xFF, // 78 0x70 p
0xFF, // 79 0x71 q
0x0D, // 80 0x72 r \r = carriage return
0xFF, // 81 0x73 s
0x09, // 82 0x74 t \t = tab
TSN_TABLE_UNESCAPE_UNICODE, // 83 0x75 u \uXXXX unicode
0xFF, // 84 0x76 v
0xFF, // 85 0x77 w
0xFF, // 86 0x78 x
0xFF, // 87 0x79 y
0xFF, // 88 0x7A z
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x7B - 0x7F
//--- Extended / high range (0x80 - 0xFF): all invalid for JSON escape context
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
};
const uchar CJsonParser::s_escape_table[256] =
{
//--- 0x00–0x1F: caracteres de control
TSN_TABLE_ESCAPE_UNICODE, // 0x00 \0 null
TSN_TABLE_ESCAPE_UNICODE, // 0x01 
TSN_TABLE_ESCAPE_UNICODE, // 0x02 
TSN_TABLE_ESCAPE_UNICODE, // 0x03 
TSN_TABLE_ESCAPE_UNICODE, // 0x04 
TSN_TABLE_ESCAPE_UNICODE, // 0x05 
TSN_TABLE_ESCAPE_UNICODE, // 0x06 
TSN_TABLE_ESCAPE_UNICODE, // 0x07
'b', // 0x08 \b backspace
't', // 0x09 \t tab
'n', // 0x0A \n newline
TSN_TABLE_ESCAPE_UNICODE, // 0x0B \v
'f', // 0x0C \f form feed
'r', // 0x0D \r carriage return
TSN_TABLE_ESCAPE_UNICODE, // 0x0E 
TSN_TABLE_ESCAPE_UNICODE, // 0x0F 
TSN_TABLE_ESCAPE_UNICODE, // 0x10 
TSN_TABLE_ESCAPE_UNICODE, // 0x11 
TSN_TABLE_ESCAPE_UNICODE, // 0x12 
TSN_TABLE_ESCAPE_UNICODE, // 0x13 
TSN_TABLE_ESCAPE_UNICODE, // 0x14 
TSN_TABLE_ESCAPE_UNICODE, // 0x15 
TSN_TABLE_ESCAPE_UNICODE, // 0x16 
TSN_TABLE_ESCAPE_UNICODE, // 0x17 
TSN_TABLE_ESCAPE_UNICODE, // 0x18 
TSN_TABLE_ESCAPE_UNICODE, // 0x19 
TSN_TABLE_ESCAPE_UNICODE, // 0x1A 
TSN_TABLE_ESCAPE_UNICODE, // 0x1B
TSN_TABLE_ESCAPE_UNICODE, // 0x1C 
TSN_TABLE_ESCAPE_UNICODE, // 0x1D 
TSN_TABLE_ESCAPE_UNICODE, // 0x1E 
TSN_TABLE_ESCAPE_UNICODE, // 0x1F 
//--- 0x20–0x21: imprimibles
0xFF, // 0x20 ' ' space
0xFF, // 0x21 !
//--- 0x22: comilla doble
'"', // 0x22 \"
//--- 0x23–0x26: imprimibles
0xFF, // 0x23 #
0xFF, // 0x24 $
0xFF, // 0x25 %
0xFF, // 0x26 &
//--- 0x27: comilla simple
0xFF, // 0x27 \'
//--- 0x28–0x2E: imprimibles
0xFF, // 0x28 (
0xFF, // 0x29 )
0xFF, // 0x2A *
0xFF, // 0x2B +
0xFF, // 0x2C ,
0xFF, // 0x2D -
0xFF, // 0x2E .
//--- 0x2F: slash
'/', // 0x2F \/
//--- 0x30–0x5B: imprimibles
0xFF, // 0x30 0
0xFF, // 0x31 1
0xFF, // 0x32 2
0xFF, // 0x33 3
0xFF, // 0x34 4
0xFF, // 0x35 5
0xFF, // 0x36 6
0xFF, // 0x37 7
0xFF, // 0x38 8
0xFF, // 0x39 9
0xFF, // 0x3A :
0xFF, // 0x3B ;
0xFF, // 0x3C <
0xFF, // 0x3D =
0xFF, // 0x3E >
0xFF, // 0x3F ?
0xFF, // 0x40 @
0xFF, // 0x41 A
0xFF, // 0x42 B
0xFF, // 0x43 C
0xFF, // 0x44 D
0xFF, // 0x45 E
0xFF, // 0x46 F
0xFF, // 0x47 G
0xFF, // 0x48 H
0xFF, // 0x49 I
0xFF, // 0x4A J
0xFF, // 0x4B K
0xFF, // 0x4C L
0xFF, // 0x4D M
0xFF, // 0x4E N
0xFF, // 0x4F O
0xFF, // 0x50 P
0xFF, // 0x51 Q
0xFF, // 0x52 R
0xFF, // 0x53 S
0xFF, // 0x54 T
0xFF, // 0x55 U
0xFF, // 0x56 V
0xFF, // 0x57 W
0xFF, // 0x58 X
0xFF, // 0x59 Y
0xFF, // 0x5A Z
0xFF, // 0x5B [
//--- 0x5C: backslash
'\\', // 0x5C \\
//--- 0x5D–0x7E: imprimibles
0xFF, // 0x5D ]
0xFF, // 0x5E ^
0xFF, // 0x5F _
0xFF, // 0x60 `
0xFF, // 0x61 a
0xFF, // 0x62 b
0xFF, // 0x63 c
0xFF, // 0x64 d
0xFF, // 0x65 e
0xFF, // 0x66 f
0xFF, // 0x67 g
0xFF, // 0x68 h
0xFF, // 0x69 i
0xFF, // 0x6A j
0xFF, // 0x6B k
0xFF, // 0x6C l
0xFF, // 0x6D m
0xFF, // 0x6E n
0xFF, // 0x6F o
0xFF, // 0x70 p
0xFF, // 0x71 q
0xFF, // 0x72 r
0xFF, // 0x73 s
0xFF, // 0x74 t
0xFF, // 0x75 u
0xFF, // 0x76 v
0xFF, // 0x77 w
0xFF, // 0x78 x
0xFF, // 0x79 y
0xFF, // 0x7A z
0xFF, // 0x7B {
0xFF, // 0x7C |
0xFF, // 0x7D }
0xFF, // 0x7E ~
//--- 0x7F: DEL (control)
TSN_TABLE_ESCAPE_UNICODE, // 0x7F 
//--- 0x80–0xFF: extended ASCII → \uXXXX
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE,
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, // 0x80–0x87
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE,
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE,
TSN_TABLE_ESCAPE_UNICODE, // 0x88–0x8F
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE,
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, // 0x90–0x97
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE,
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, // 0x98–0x9F
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE,
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, // 0xA0–0xA7
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE,
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, // 0xA8–0xAF
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE,
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, // 0xB0–0xB7
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE,
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, // 0xB8–0xBF
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE,
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, // 0xC0–0xC7
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE,
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, // 0xC8–0xCF
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE,
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, // 0xD0–0xD7
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE,
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, // 0xD8–0xDF
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE,
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, // 0xE0–0xE7
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE,
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, // 0xE8–0xEF
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE,
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, // 0xF0–0xF7
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE,
TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, TSN_TABLE_ESCAPE_UNICODE, // 0xF8–0xFF
};
}
#endif // JSONPARSERBYLEO_SRC_JSONCORE_JSONPARSER_MQH
//+------------------------------------------------------------------+