| //+------------------------------------------------------------------+
|
| //| 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_JSONPARSER_MQH
|
| #define JSONPARSERBYLEO_SRC_JSONPARSER_MQH
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| #include "JsonAsmDef.mqh"
|
| #include <TSN\\BSFL\\Dom.mqh>
|
|
|
| //---
|
| #ifdef JSONPARSERBYLEO_DLL
|
| #include "DLL\\Main.mqh"
|
| #endif // JSONPARSERBYLEO_DLL
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| namespace TSN
|
| {
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| //#define JSONPARSERBYLEO_DESACTIVE_SWAR
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| class CJsonParser : public CBaseStructuredLan
|
| {
|
| protected:
|
| //---
|
| uint m_stak_num[JSONPARSERBYLEO_MAX_STACK];
|
| int m_stak_pos[JSONPARSERBYLEO_MAX_STACK];
|
| ushort m_stak_state[JSONPARSERBYLEO_MAX_STACK];
|
| int m_stak_curr;
|
| ushort m_stak_curr_state;
|
|
|
| //---
|
| #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) {}
|
|
|
| //---
|
| // Para data raw se puede usar el json uchar directamtne
|
| bool AutoParseByFileExt(const int file_ext_type);
|
| void CorrectPadding();
|
| #ifdef JSONPARSERBYLEO_DLL
|
| void CorrectPaddingSimd();
|
| #endif // JSONPARSERBYLEO_DLL
|
|
|
| //---
|
| bool Parse();
|
| bool ParseAssembly();
|
| #ifdef JSONPARSERBYLEO_DLL
|
| __forceinline bool ParseSimd();
|
| #endif // JSONPARSERBYLEO_DLL
|
|
|
| //---
|
| int ToJsonAsm(uchar& text[]) const;
|
|
|
| //---
|
| __forceinline ENUM_TSN_JSON_LAST_ERR LastErr() const { return m_last_err; }
|
|
|
| //--- Unescape
|
| string Unescape(const int start, const int end) const;
|
| int Unescape(const int start, const int end, uchar& res[]) const;
|
| static ulong EscapePointer(const string &json_pointer, int& p, const int l);
|
|
|
| //---
|
| __forceinline int GetStep(const int idx) const;
|
| __forceinline int GetStepPure(const int idx) const;
|
|
|
|
|
| //---
|
| void PrintCintaTypes(const int start, const int num) const;
|
|
|
| //--- Comparacion
|
| // keys
|
| bool KeysEqual(const ulong key_hash_fnv1a64, const int curr) const;
|
| bool KeysEqual(const string& str, const int curr) const;
|
| // strings
|
| bool StrEquals(const string &str, const int curr) const;
|
|
|
| //--
|
| string CleanNode(int i, const int end) const;
|
| int CleanNode(int i, const int end, uchar &out[], int k, bool ajustar_arr) const;
|
|
|
| //---
|
| CJsonNode GetRoot();
|
|
|
| //---
|
| bool NodeHashing(int curr, const int obj_count, const int bend, int& table_pos);
|
|
|
| //---
|
| void ErrorInfo(bool with_avanze = false) const;
|
|
|
| //---
|
| void SerializeToDom(CDomNodeBase* root, CDomNodeManager* manager, int cur, const int end) const;
|
|
|
| //--- Tablas generales
|
| static const string s_dtype_to_str[8];
|
| static const int s_slots_size_arrobj;
|
| static const bool s_dtype_is_valid[8];
|
| };
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| CJsonParser::CJsonParser(void)
|
| {
|
| m_next = TSN_JSON_TOK_INVALID;
|
| #ifdef JSONPARSERBYLEO_DLL
|
| m_index_r = ArrayResize(m_index, 256, 256);
|
| #endif // JSONPARSERBYLEO_DLL
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| const string CJsonParser::s_dtype_to_str[8] =
|
| {
|
| "",
|
| "",
|
| "",
|
| "long",
|
| "double",
|
| "bool",
|
| "string",
|
| ""
|
| };
|
| const bool CJsonParser::s_dtype_is_valid[8] =
|
| {
|
| false,
|
| false,
|
| false,
|
| true,
|
| true,
|
| true,
|
| true,
|
| false
|
| };
|
| const int CJsonParser::s_slots_size_arrobj = 2;
|
|
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| #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
|
|
|
| //+------------------------------------------------------------------+
|
| void CJsonParser::CorrectPadding()
|
| {
|
| //---
|
| const int new_len = m_len + 11;
|
| ArrayResize(m_raw, new_len);
|
|
|
| //--- 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
|
| m_raw[m_len + 5] = ' '; // para que luego de una key o string mal cerrado falle
|
| 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);
|
| }
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| bool CJsonParser::NodeHashing(int curr, const int obj_count, const int bend, int& table_pos)
|
| {
|
| //---
|
| table_pos = FindNodePosOrCreate(curr); // Reservamos o obtenemos la posicion de este nodo
|
|
|
| //---
|
| m_tables[table_pos].table_size = obj_count; // Numero de objetos
|
| m_tables[table_pos].start = curr;
|
|
|
| m_pfh.MaxValSeed(m_max_att++);
|
| m_pfh.InitAlg(obj_count, 0.70, fmin(obj_count, 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;
|
| curr += 2; // Ahora en key
|
|
|
| //---
|
| while(curr < bend)
|
| {
|
| if(int(m_cinta[curr] & 0xF) != JSON_VTYPE_KEY)
|
| {
|
| curr += GetStep(curr);
|
| continue;
|
| }
|
|
|
| //---
|
| m_tables[table_pos].pos_arr[pos_c] = curr; // Assing
|
|
|
| //---
|
| ulong key_hash = FNV_OFFSET_BASIS;
|
| int i = int(m_cinta[curr] >> TSN_SBL_BIT_STR_START); // len
|
| const int len = i + int((m_cinta[curr] >> TSN_SBL_BIT_STR_LEN) & TSN_SBL_BIT_STR_MASK_LEN); // len
|
|
|
| //--- Hash 64
|
| for(; i < len; i++)
|
| {
|
| key_hash ^= m_raw[i];
|
| key_hash *= FNV_PRIME;
|
| }
|
| // PrintFormat("Hash para %s = %I64u", CharArrayToString(m_raw, int(m_cinta[curr] >> TSN_SBL_BIT_STR_START),
|
| // int((m_cinta[curr] >> TSN_SBL_BIT_STR_LEN) & TSN_SBL_BIT_STR_MASK_LEN)), key_hash);
|
|
|
|
|
| //---
|
| keys_h[pos_c] = key_hash;
|
| posiciones[pos_c] = pos_c;
|
| pos_c++;
|
|
|
| //---
|
| curr++; // salta KEY
|
| curr += GetStep(curr); // salta VAL
|
| }
|
|
|
| // Cacheado
|
| const int fn = m_pfh.m_final_table_size;
|
|
|
| //--- Last
|
| m_tables[table_pos].final_table_l = m_pfh.m_final_table_size_last;
|
| m_tables[table_pos].bucket_l = m_pfh.m_buckets_size_last;
|
|
|
|
|
| // Iniciamos..
|
| ArrayResize(m_tables[table_pos].pos_index, fn);
|
| ArrayResize(m_tables[table_pos].key_hash, fn);
|
| ArrayInitialize(m_tables[table_pos].pos_index, -1);
|
| ArrayInitialize(m_tables[table_pos].key_hash, 0ULL);
|
|
|
| //---
|
| return m_pfh.RunWValue(keys_h, posiciones, m_tables[table_pos].seeds, m_tables[table_pos].pos_index, m_tables[table_pos].key_hash);
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| int CJsonParser::CalcRegType(const string& file_name)
|
| {
|
| return (int)JPBL_HashExt(file_name);
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| bool CJsonParser::KeysEqual(const string &str, const int curr) const
|
| {
|
| //---
|
| const int start = int(m_cinta[curr] >> TSN_SBL_BIT_STR_START);
|
| const int len = int((m_cinta[curr] >> TSN_SBL_BIT_STR_LEN) & TSN_SBL_BIT_STR_MASK_LEN);
|
| const int l = StringLen(str);
|
| //---
|
| if(l != len)
|
| return false;
|
| //---
|
| for(int i = 0; i < l; i++)
|
| if(str[i] != m_raw[start + i])
|
| return false;
|
| //---
|
| return true;
|
| }
|
| //+------------------------------------------------------------------+
|
| bool CJsonParser::KeysEqual(const ulong key_hash_fnv1a64, const int curr) const
|
| {
|
| const int start = int(m_cinta[curr] >> TSN_SBL_BIT_STR_START);
|
| const int len = int((m_cinta[curr] >> TSN_SBL_BIT_STR_LEN) & TSN_SBL_BIT_STR_MASK_LEN);
|
| ulong hash = FNV_OFFSET_BASIS;
|
| for(int i = start; i < (start + len); i++)
|
| {
|
| hash ^= m_raw[i];
|
| hash *= FNV_PRIME;
|
| }
|
| return hash == key_hash_fnv1a64;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| bool CJsonParser::StrEquals(const string &str, const int curr) const
|
| {
|
| //---
|
| // m_cinta[m_cinta_pos++] = long(m_pos - 1) | long(start) << TSN_JSON_BIT_STR_START_END;
|
| const int start = int(m_cinta[curr] >> TSN_SBL_BIT_STR_START);
|
| const int len = int((m_cinta[curr] >> TSN_SBL_BIT_STR_LEN) & TSN_SBL_BIT_STR_MASK_LEN);
|
| //PrintFormat("Exact = '%s', other = '%s'", CharArrayToString(m_raw, start, len), str);
|
|
|
| const int l = StringLen(str);
|
|
|
| //---
|
| if(l != len)
|
| return false;
|
| for(int i = 0; i < l; i++)
|
| {
|
| if(str[i] != m_raw[start + i])
|
| return false;
|
| }
|
| return true;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| string CJsonParser::CleanNode(int i, const int end) const
|
| {
|
| //---
|
| string str;
|
| int k = 0;
|
| StringInit(str, (end - i) + 1, ' ');
|
|
|
| // "\"\""
|
| //---
|
| bool in = false;
|
| for(i; i <= end; i++)
|
| {
|
| const uchar c = m_raw[i];
|
|
|
| //---
|
| if(in)
|
| {
|
| str.SetChar(k++, c);
|
| if(c == '\\')
|
| {
|
| str.SetChar(k++, m_raw[++i]);
|
| continue;
|
| }
|
| in = (c != '"');
|
| }
|
| else
|
| {
|
| if(c < 33)
|
| continue;
|
| str.SetChar(k++, c);
|
| in = (c == '"');
|
| }
|
| }
|
| //--
|
| StringSetLength(str, k);
|
| return str;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| int CJsonParser::CleanNode(int i, const int end, uchar &out[], int k, bool ajustar_arr) const
|
| {
|
| //---
|
| const int needed = (end - i) + 1;
|
| const int rsize = ArraySize(out);
|
| const int e = needed + k;
|
| if(rsize <= e)
|
| {
|
| ArrayResize(out, rsize + e);
|
| }
|
|
|
| //---
|
| bool in = false;
|
| for(i; i <= end; i++)
|
| {
|
| const uchar c = m_raw[i];
|
| //---
|
| if(in)
|
| {
|
| out[k++] = c;
|
| if(c == '\\')
|
| {
|
| out[k++] = m_raw[++i];
|
| continue;
|
| }
|
| in = (c != '"');
|
| }
|
| else
|
| {
|
| if(c < 33)
|
| continue;
|
| out[k++] = c;
|
| in = (c == '"');
|
| }
|
| }
|
|
|
| //--
|
| if(ajustar_arr) // Ajustamos exacto
|
| ArrayResize(out, k);
|
|
|
| //--
|
| return k - (e - needed);
|
| }
|
|
|
|
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| 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;
|
| }
|
| case TSNJSON_RI_JSONASM_FILE:
|
| {
|
| CorrectPadding(); // Auto
|
| return ParseAssembly();
|
| }
|
| default:
|
| {
|
| m_last_err = TSN_JSON_ERR_INVALID_EXT_FILE;
|
| m_pos = 0;
|
| return false;
|
| }
|
| }
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Macros de avanze |
|
| //+------------------------------------------------------------------+
|
| //--- Especifico (C=caracter que se busca, T=Token asignado)
|
| #define TSN_JSON_NEXT_ESPECIFIC_DO(C, T) \
|
| while(true) \
|
| { \
|
| if(m_raw[m_pos] == C) \
|
| { \
|
| m_next=T; \
|
| break; \
|
| } \
|
| m_pos++;\
|
| }
|
|
|
| //---
|
| #define TSN_JSON_AUMENT m_stak_num[m_stak_curr]++;
|
|
|
| //--- 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 \
|
| while(true) \
|
| { \
|
| const uchar _c = m_raw[m_pos]; \
|
| if(_c < 33) \
|
| { \
|
| m_pos++; \
|
| if(m_pos>=m_len) \
|
| { \
|
| m_len=m_pos;\
|
| m_last_err=TSN_JSON_ERR_MALFORMED_LEN_SUP_AFTER_VAL; \
|
| return false; \
|
| } \
|
| continue; \
|
| } \
|
| m_next = g_table_json_tokens[_c];\
|
| if(((1 << m_next) & m_stak_curr_state) == 0)\
|
| { \
|
| m_last_err= TSN_JSON_ERR_MALFORDMED_JSON_AFTER_VAL; \
|
| return false; \
|
| }\
|
| break; \
|
| }
|
|
|
| //--- Se usa solo al incio de un llave ini validos " y }
|
| #define TSN_JSON_NEXT_LLAVE_INI \
|
| while(true) \
|
| { \
|
| const uchar _c = m_raw[m_pos]; \
|
| if(_c == '"') \
|
| { \
|
| m_next=TSN_JSON_TOK_KEY; \
|
| break; \
|
| } \
|
| if(_c== '}') \
|
| { \
|
| m_next=TSN_JSON_TOK_LLAVE_END; \
|
| break; \
|
| } \
|
| m_pos++;\
|
| }
|
| //--- Se usa solo luego de un cor ini aqui peuden ir " [ { t\f\n (1-9\-) ]
|
| #define TSN_JSON_NEXT_COR_INI \
|
| while(true) \
|
| { \
|
| const uchar _c = m_raw[m_pos];\
|
| if(_c < 33) \
|
| { \
|
| m_pos++; \
|
| continue; \
|
| } \
|
| 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; \
|
| }
|
|
|
| //--- Luego de una coma en array
|
| // Luego de una , y KEY siempre siempre valor
|
| #define TSN_JSON_NEXT_VALUE_COMM_KEY \
|
| while(true) \
|
| { \
|
| const uchar _c = m_raw[m_pos];\
|
| if(_c < 33) \
|
| { \
|
| m_pos++; \
|
| continue; \
|
| } \
|
| 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; \
|
| }\
|
| break; \
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| #define JSONPARSERBYLEO_PARSE_NUMBER(SING) \
|
| long v = 0;\
|
| uchar ch = m_raw[m_pos] ^ '0';\
|
| while(true)\
|
| {\
|
| v = (v << 3) + (v << 1) + ch;\
|
| m_pos++;\
|
| ch = m_raw[m_pos] ^ '0';\
|
| if(ch < 10)\
|
| continue;\
|
| else\
|
| if(ch == 30 || (ch | 0x20) == 117)\
|
| {\
|
| double val = double(v);\
|
| if(ch == 30)\
|
| {\
|
| m_pos++;\
|
| long frac_int = 0;\
|
| ushort fract_digits = 0;\
|
| while(true)\
|
| {\
|
| ch = m_raw[m_pos] ^ '0';\
|
| if(ch > 9)\
|
| break;\
|
| frac_int = (frac_int << 3) + (frac_int << 1) + ch;\
|
| fract_digits++;\
|
| m_pos++;\
|
| }\
|
| val += frac_int * g_Exp10[fract_digits];\
|
| }\
|
| if((ch | 0x20) == 117) \
|
| {\
|
| bool exp_sign = true;\
|
| ch = m_raw[++m_pos] ^ '0';\
|
| if(ch>9)\
|
| {\
|
| if(ch==29)\
|
| exp_sign = false;\
|
| ch = m_raw[++m_pos] ^ '0';\
|
| } \
|
| ushort exp_val = 0;\
|
| while(true)\
|
| {\
|
| exp_val = (exp_val << 3) + (exp_val << 1) + ch;\
|
| ch = m_raw[++m_pos] ^ '0';\
|
| if(ch > 9)\
|
| break;\
|
| }\
|
| val *= (exp_sign) ? g_Pow10[exp_val] : g_Exp10[exp_val];\
|
| }\
|
| TsnBitInterprete bit;\
|
| bit.double_value = SING(val);\
|
| m_cinta[m_cinta_pos++] = JSON_VTYPE_REAL;\
|
| m_cinta[m_cinta_pos++] = bit.long_value;\
|
| break;\
|
| }\
|
| else\
|
| {\
|
| m_cinta[m_cinta_pos++] = JSON_VTYPE_INTEGER;\
|
| m_cinta[m_cinta_pos++] = SING(v);\
|
| break; \
|
| }\
|
| }
|
|
|
|
|
| //+------------------------------------------------------------------+
|
| //| Parse con simd |
|
| //+------------------------------------------------------------------+
|
| #ifdef JSONPARSERBYLEO_DLL
|
| __forceinline bool CJsonParser::ParseSimd()
|
| {
|
| //--- Iniciales
|
| m_last_err = TSN_JSON_NOT_ERR;
|
| m_tables_size = 0;
|
| return JsonParserResolve(m_raw,
|
| m_cinta,
|
| m_index,
|
| m_len,
|
| m_offset,
|
| (int&)m_last_err,
|
| m_cinta_pos,
|
| m_pos
|
| );
|
| }
|
| // "llabe":"llabe";
|
| #endif // JSONPARSERBYLEO_DLL
|
|
|
|
|
|
|
| //+------------------------------------------------------------------+
|
| //| Parse normal |
|
| //+------------------------------------------------------------------+
|
| bool CJsonParser::Parse()
|
| {
|
| //---
|
| m_stak_curr = -1;
|
| //m_stak_curr_state = -1;
|
| //m_stak_curr = -1;
|
| m_last_err = TSN_JSON_NOT_ERR;
|
| //m_next = TSN_JSON_TOK_INVALID;
|
| m_pos = m_offset;
|
| m_cinta_pos = 0;
|
| m_tables_size = 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
|
| {
|
| m_pos++;
|
| while(true)
|
| {
|
| const uchar _f__c = m_raw[m_pos];
|
| if(_f__c < 33)
|
| {
|
| m_pos++;
|
| continue;
|
| }
|
|
|
| //---
|
| if(_f__c == '{')
|
| {
|
| m_next = TSN_JSON_TOK_LLAVE_INI;
|
| }
|
| else
|
| if(_f__c == '[')
|
| {
|
| m_next = TSN_JSON_TOK_COR_INI;
|
| }
|
| else
|
| {
|
| m_next = TSN_JSON_TOK_INVALID;
|
| }
|
| break;
|
| }
|
| }
|
|
|
| //--- Bucle
|
| while(true)
|
| {
|
| switch(m_next)
|
| {
|
| //---
|
| case TSN_JSON_TOK_NUMBER_NEG:
|
| {
|
| m_pos++; // luego del -
|
|
|
| //---
|
| JSONPARSERBYLEO_PARSE_NUMBER(-)
|
|
|
| //---
|
| TSN_JSON_AUMENT
|
| TSN_JSON_NEXT_NO_NEXT_KEY
|
| break;
|
| }
|
|
|
| //---
|
| case TSN_JSON_TOK_NUMBER:
|
| {
|
| //---
|
| JSONPARSERBYLEO_PARSE_NUMBER(+)
|
|
|
| //--- no hago el m_pos++ dado que ahora mismo estamo en el seg char
|
| TSN_JSON_AUMENT
|
| TSN_JSON_NEXT_NO_NEXT_KEY
|
| break;
|
| }
|
|
|
| //---
|
| case TSN_JSON_TOK_STRING:
|
| {
|
| //---
|
| //TSN_JSON_RESERVAR(2)
|
|
|
| //---
|
| const int 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;
|
| ulong has_bs = TSNTABLES_SWAR_HAS(x_bs);
|
| const ulong x_qt = chunk ^ TSNTABLES_SWAR_MASK_COMILLA;
|
| const ulong has_qt = TSNTABLES_SWAR_HAS(x_qt);
|
|
|
| //---
|
| if((has_bs | has_qt) == 0) // Caso comun. .. ninguno de los dos.. continuamos..
|
| {
|
| m_pos += 8;
|
| continue;
|
| }
|
|
|
| //---
|
| if(has_bs == 0) // Si not iene bs (pero confirmamos que no es 0)
|
| // Entonces no tirne bs pero si qt salimos de una..
|
| {
|
| 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
|
| | long(m_pos - start) << TSN_SBL_BIT_STR_LEN
|
| | long(start) << TSN_SBL_BIT_STR_START;
|
| m_pos++; // Luego del "
|
| break;
|
| }
|
|
|
| //--- 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
|
| | long(m_pos - start) << TSN_SBL_BIT_STR_LEN
|
| | long(start) << TSN_SBL_BIT_STR_START;
|
|
|
| //---
|
| m_pos++;
|
| break;
|
| }
|
| else
|
| {
|
| m_pos += 8;
|
| // 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
|
| do
|
| {
|
| const uchar c = m_raw[m_pos];
|
|
|
| //---
|
| if(c == '\\')
|
| {
|
| m_pos += 2;
|
| continue;
|
| }
|
| if(c == '"')
|
| {
|
| // STRING 1 slot: [tipo(4)|len(28low)|start(32high)]
|
| m_cinta[m_cinta_pos++] = JSON_VTYPE_STRING | long(m_pos - start) << TSN_SBL_BIT_STR_LEN |
|
| long(start) << TSN_SBL_BIT_STR_START;
|
| // curr_end = INT_MAX;
|
| m_pos++;
|
| break;
|
| }
|
|
|
| //---
|
| m_pos++;
|
| }
|
| while(m_pos < m_len);
|
|
|
| #endif // JSONPARSERBYLEO_DESACTIVE_SWAR
|
|
|
| //---
|
| TSN_JSON_AUMENT
|
| TSN_JSON_NEXT_NO_NEXT_KEY
|
|
|
| //---
|
| break;
|
| }
|
| case TSN_JSON_TOK_BOOL_TRUE:
|
| {
|
| //TSN_JSON_RESERVAR(1)
|
| // t r u e W
|
| // [-4] [-3] [-2] [-1] [0]
|
|
|
| m_pos += 4;
|
| // 5 | 1 << TSN_SBL_BIT_START_BOOL(4) = 21
|
| m_cinta[m_cinta_pos++] = 21;
|
|
|
| //---
|
| TSN_JSON_AUMENT
|
| TSN_JSON_NEXT_NO_NEXT_KEY
|
| break;
|
| }
|
| case TSN_JSON_TOK_BOOL_FALSE:
|
| {
|
| //TSN_JSON_RESERVAR(1)
|
| // f a l s e W
|
| // [] [-4] [-3] [-2 ] [-1] [0]
|
| m_pos += 5;
|
| m_cinta[m_cinta_pos++] = JSON_VTYPE_BOOLEAN;
|
|
|
| //---
|
| TSN_JSON_AUMENT
|
| TSN_JSON_NEXT_NO_NEXT_KEY
|
| break;
|
| }
|
| case TSN_JSON_TOK_NULL:
|
| {
|
| //TSN_JSON_RESERVAR(1)
|
| // n u l l W
|
| // [-4][-3][-2][-1][]
|
|
|
| //---
|
| m_pos += 4;
|
| m_cinta[m_cinta_pos++] = JSON_VTYPE_NULL;
|
|
|
| //---
|
| TSN_JSON_AUMENT
|
| TSN_JSON_NEXT_NO_NEXT_KEY
|
| break;
|
| }
|
| case TSN_JSON_TOK_COMMA:
|
| {
|
| //---
|
| m_pos++;
|
|
|
| //---
|
| if(m_stak_curr_state == TSN_JSON_CTX_IN_OBJ)
|
| {
|
| TSN_JSON_NEXT_ESPECIFIC_DO('"', TSN_JSON_TOK_KEY)
|
| }
|
| else
|
| {
|
| TSN_JSON_NEXT_VALUE_COMM_KEY
|
| }
|
| break;
|
| }
|
| case TSN_JSON_TOK_LLAVE_INI:
|
| {
|
| //TSN_JSON_RESERVAR(1)
|
| m_cinta[m_cinta_pos] = JSON_VTYPE_OBJ;
|
|
|
| //---
|
| m_stak_curr++;
|
| m_stak_curr_state = TSN_JSON_CTX_IN_OBJ;
|
| m_stak_state[m_stak_curr] = TSN_JSON_CTX_IN_OBJ;
|
| m_stak_num[m_stak_curr] = 0;
|
| m_stak_pos[m_stak_curr] = m_cinta_pos++;
|
|
|
| //---
|
| m_cinta[m_cinta_pos++] = m_pos++;
|
|
|
| //---
|
| TSN_JSON_NEXT_LLAVE_INI
|
|
|
| //---
|
| break;
|
| }
|
| case TSN_JSON_TOK_COR_INI:
|
| {
|
| //TSN_JSON_RESERVAR(1)
|
| m_cinta[m_cinta_pos] = JSON_VTYPE_ARR;
|
|
|
| //---
|
| m_stak_curr++;
|
| m_stak_curr_state = TSN_JSON_CTX_IN_ARR;
|
| m_stak_state[m_stak_curr] = TSN_JSON_CTX_IN_ARR;
|
| m_stak_num[m_stak_curr] = 0;
|
| m_stak_pos[m_stak_curr] = m_cinta_pos++;
|
|
|
| //---
|
| m_cinta[m_cinta_pos++] = m_pos++;
|
|
|
| //---
|
| TSN_JSON_NEXT_COR_INI
|
|
|
| //---
|
| break;
|
| }
|
| case TSN_JSON_TOK_LLAVE_END:
|
| case TSN_JSON_TOK_COR_END:
|
| {
|
| //---
|
| const int lp = m_stak_pos[m_stak_curr];
|
| m_cinta[lp] |= long(m_stak_num[m_stak_curr]) << TSN_SBL_BIT_START_NUM_EL | long(m_cinta_pos - lp) << TSN_SBL_BIT_START_NUM_C;
|
| m_cinta[lp + 1] |= long(m_pos) << TSN_JSON_BIT_END_T;
|
|
|
| //---
|
| if(--m_stak_curr < 0)
|
| return true;
|
|
|
| //--- nuevo elemento
|
| //TSN_JSON_AUMENT
|
|
|
| //---
|
| m_stak_curr_state = m_stak_state[m_stak_curr];
|
|
|
| //---
|
| m_pos++;
|
|
|
| //---
|
| TSN_JSON_AUMENT
|
| TSN_JSON_NEXT_NO_NEXT_KEY
|
|
|
| //---
|
| break;
|
| }
|
| case TSN_JSON_TOK_KEY:
|
| {
|
| const int start = ++m_pos;
|
|
|
| //---
|
| #ifndef JSONPARSERBYLEO_ACTIVE_SWAR_KEYS
|
|
|
| //--- Pase inicial 8 en 8
|
| while(true)
|
| {
|
| // "asdasdasd".........."
|
| // 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;
|
|
|
| //---
|
| #ifdef JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS
|
| const ulong x_bs = chunk ^ TSNTABLES_SWAR_MASK_BACK_SLASH;
|
| ulong has_bs = TSNTABLES_SWAR_HAS(x_bs);
|
| #endif // JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS
|
|
|
| const ulong x_qt = chunk ^ TSNTABLES_SWAR_MASK_COMILLA;
|
| const ulong has_qt = TSNTABLES_SWAR_HAS(x_qt);
|
|
|
| //---
|
| #ifdef JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS
|
| if((has_bs | has_qt) == 0) // Caso comun. .. ninguno de los dos.. continuamos..
|
| {
|
| m_pos += 8;
|
| continue;
|
| }
|
| #else // JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS
|
| if(has_qt == 0) // Caso comun. .. ninguno de los dos.. continuamos..
|
| {
|
| m_pos += 8;
|
| continue;
|
| }
|
| #endif // JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS
|
|
|
|
|
| //---
|
| #ifdef JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS
|
| if(has_bs == 0) // Si not iene bs (pero confirmamos que no es 0)
|
| // Entonces no tirne bs pero si qt salimos de una..
|
| {
|
| #endif // JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS
|
| 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_KEY
|
| | long(m_pos - start) << TSN_SBL_BIT_STR_LEN
|
| | long(start) << TSN_SBL_BIT_STR_START;
|
| m_pos++; // Luego del "
|
| break;
|
| #ifdef JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS
|
| }
|
|
|
| //---
|
| 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;
|
|
|
| //---
|
| 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
|
| | long(m_pos - start) << TSN_SBL_BIT_STR_LEN
|
| | long(start) << TSN_SBL_BIT_STR_START;
|
| m_pos++; // Luego del "
|
| break;
|
| }
|
| else
|
| {
|
| m_pos += 8;
|
| // Print(CharToString(m_raw[m_pos]));
|
| if(((prev & compacted_bs) >> 7U) != 0) // Overflow el ultimo char es \ y el siguiente escapa
|
| {
|
| //Print("siuu");
|
| m_pos++; // Uno mas.. luego del currend
|
| }
|
| }
|
| #endif // JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS
|
| }
|
|
|
|
|
| #else // JSONPARSERBYLEO_ACTIVE_SWAR_KEYS
|
|
|
| //--- Char por char
|
| while(true)
|
| {
|
|
|
| #ifdef JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS
|
| const uchar c = m_raw[m_pos];
|
| if(c == '\\')
|
| {
|
| m_pos += 2;
|
| continue;
|
| }
|
| if(c == '"')
|
| {
|
| //---
|
| m_cinta[m_cinta_pos++] = JSON_VTYPE_KEY
|
| | long(m_pos - start) << TSN_SBL_BIT_STR_LEN
|
| | long(start) << TSN_SBL_BIT_STR_START;
|
| // curr_end = INT_MAX;
|
| m_pos++;
|
| break;
|
| }
|
| #else // JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS
|
| if(m_raw[m_pos] == '"')
|
| {
|
| m_cinta[m_cinta_pos++] = JSON_VTYPE_KEY
|
| | long(m_pos - start) << TSN_SBL_BIT_STR_LEN
|
| | long(start) << TSN_SBL_BIT_STR_START;
|
| // curr_end = INT_MAX;
|
| m_pos++;
|
| break;
|
| }
|
| #endif // JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS
|
| }
|
| #endif // JSONPARSERBYLEO_ACTIVE_SWAR_KEYS
|
|
|
| //--- Find :
|
| while(true)
|
| {
|
| //---
|
| if(m_raw[m_pos] == ':')
|
| break;
|
|
|
| //---
|
| m_pos++;
|
| if(m_pos >= m_len)
|
| {
|
| m_pos = m_len;
|
| m_last_err = TSN_JSON_ERR_MALFODERD_KEY_EXPECTED_DOS_PUNTOS;
|
| return false;
|
| }
|
| }
|
| //---
|
| m_pos++; // Luego del
|
|
|
|
|
| //--- Buscamos valor
|
| TSN_JSON_NEXT_VALUE_COMM_KEY
|
|
|
| //---
|
| break;
|
| }
|
|
|
| default:
|
| {
|
| m_last_err = TSN_JSON_ERR_INVALID_CHAR;
|
| return false;
|
| }
|
| }
|
| }
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| int CJsonParser::Unescape(const int start, const int end, uchar & res[]) const
|
| {
|
| //---
|
| ArrayResize(res, (end - start) + 1);
|
| int pos = 0;
|
|
|
| //---
|
| for(int i = start; i <= end; i++)
|
| {
|
| //---
|
| if(m_raw[i] == '\\')
|
| {
|
| i++;
|
| const uchar c = m_raw[i];
|
| const uchar next = g_tsn_table_json_unescape_table[c];
|
| switch(next)
|
| {
|
| case TSN_TABLE_UNESCAPE_UNICODE:
|
| {
|
| if(i + 4 <= end)
|
| {
|
| i++;
|
| uint code = g_arr_valid_hex_chars[m_raw[i++]];
|
| code = (code << 4) | g_arr_valid_hex_chars[m_raw[i++]];
|
| code = (code << 4) | g_arr_valid_hex_chars[m_raw[i++]];
|
| code = (code << 4) | g_arr_valid_hex_chars[m_raw[i]];
|
|
|
| //--- Vericiacino para emojis....
|
| if(code >= 0xD800 && code <= 0xDBFF)
|
| {
|
| // Otro mas \uxxxx
|
| if(i + 6 <= end && m_raw[i + 1] == '\\' && m_raw[i + 2] == 'u')
|
| {
|
| const int j = i + 3;
|
| uint low = g_arr_valid_hex_chars[m_raw[j]];
|
| low = (low << 4) | g_arr_valid_hex_chars[m_raw[j + 1]];
|
| low = (low << 4) | g_arr_valid_hex_chars[m_raw[j + 2]];
|
| low = (low << 4) | g_arr_valid_hex_chars[m_raw[j + 3]];
|
|
|
| //---
|
| if(low >= 0xDC00 && low <= 0xDFFF)
|
| {
|
| code = 0x10000 + ((code - 0xD800) << 10) + (low - 0xDC00);
|
| i = j + 3; // consumimos tambien el low surrogate
|
| }
|
| else
|
| {
|
| code = 0xFFFD; // low surrogate invalido
|
| }
|
| }
|
| else
|
| {
|
| code = 0xFFFD; // huerfano, no hay low surrogate despues
|
| }
|
| }
|
| //--- Low surrogate suelto (sin high antes) -> invalido
|
| else
|
| if(code >= 0xDC00 && code <= 0xDFFF)
|
| {
|
| code = 0xFFFD;
|
| }
|
|
|
| //---
|
| if(code < 0x80)
|
| {
|
| res[pos++] = uchar(code);
|
| }
|
| else
|
| if(code < 0x800)
|
| {
|
| res[pos++] = uchar(0xC0 | (code >> 6));
|
| res[pos++] = uchar(0x80 | (code & 0x3F));
|
| }
|
| else
|
| if(code < 0x10000)
|
| {
|
| res[pos++] = uchar(0xE0 | (code >> 12));
|
| res[pos++] = uchar(0x80 | ((code >> 6) & 0x3F));
|
| res[pos++] = uchar(0x80 | (code & 0x3F));
|
| }
|
| else
|
| {
|
| res[pos++] = uchar(0xF0 | (code >> 18));
|
| res[pos++] = uchar(0x80 | ((code >> 12) & 0x3F));
|
| res[pos++] = uchar(0x80 | ((code >> 6) & 0x3F));
|
| res[pos++] = uchar(0x80 | (code & 0x3F));
|
| }
|
| }
|
| else
|
| res[pos++] = 'u';
|
| break;
|
| }
|
| case TSN_TABLE_UNESCAPE_INVALID:
|
| {
|
| res[pos++] = '\\'; // tal cual
|
| res[pos++] = c;
|
| break;
|
| }
|
| default:
|
| res[pos++] = next;
|
| break;
|
| }
|
|
|
| //---
|
| continue;
|
| }
|
|
|
| //---
|
| res[pos++] = m_raw[i];
|
| }
|
|
|
| //---
|
| return ArrayResize(res, pos);
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| string CJsonParser::Unescape(const int start, const int end) const
|
| {
|
| //---
|
| string res = "";
|
| StringInit(res, (end - start) + 1, ' ');
|
| int pos = 0;
|
|
|
| //---
|
| for(int i = start; i <= end; i++)
|
| {
|
| //---
|
| if(m_raw[i] == '\\')
|
| {
|
| i++;
|
| const uchar c = m_raw[i];
|
| const uchar next = g_tsn_table_json_unescape_table[c];
|
| switch(next)
|
| {
|
| case TSN_TABLE_UNESCAPE_UNICODE:
|
| {
|
| if(i + 4 <= end)
|
| {
|
| i++;
|
| uint code = g_arr_valid_hex_chars[m_raw[i++]];
|
| code = (code << 4) | g_arr_valid_hex_chars[m_raw[i++]];
|
| code = (code << 4) | g_arr_valid_hex_chars[m_raw[i++]];
|
| code = (code << 4) | g_arr_valid_hex_chars[m_raw[i]];
|
|
|
| //--- High surrogate (0xD800-0xDBFF): el string es UTF-16, basta con
|
| // validar que venga acompañado de un low surrogate valido despues
|
| // y escribir ambas unidades tal cual (no hace falta combinar).
|
| if(code >= 0xD800 && code <= 0xDBFF)
|
| {
|
| if(i + 6 <= end && m_raw[i + 1] == '\\' && m_raw[i + 2] == 'u')
|
| {
|
| const int j = i + 3;
|
| uint low = g_arr_valid_hex_chars[m_raw[j]];
|
| low = (low << 4) | g_arr_valid_hex_chars[m_raw[j + 1]];
|
| low = (low << 4) | g_arr_valid_hex_chars[m_raw[j + 2]];
|
| low = (low << 4) | g_arr_valid_hex_chars[m_raw[j + 3]];
|
|
|
| //---
|
| if(low >= 0xDC00 && low <= 0xDFFF)
|
| {
|
| res.SetChar(pos++, (ushort)code);
|
| res.SetChar(pos++, (ushort)low);
|
| i = j + 3; // consumimos tambien el low surrogate
|
| }
|
| else
|
| {
|
| res.SetChar(pos++, 0xFFFD); // low surrogate invalido
|
| }
|
| }
|
| else
|
| {
|
| res.SetChar(pos++, 0xFFFD); // huerfano, no hay low surrogate despues
|
| }
|
| }
|
| //--- Low surrogate suelto (sin high antes) -> invalido
|
| else
|
| if(code >= 0xDC00 && code <= 0xDFFF)
|
| {
|
| res.SetChar(pos++, 0xFFFD);
|
| }
|
| else
|
| {
|
| res.SetChar(pos++, (ushort)code);
|
| }
|
| }
|
| else
|
| res.SetChar(pos++, 'u');
|
| break;
|
| }
|
| case TSN_TABLE_UNESCAPE_INVALID:
|
| {
|
| res.SetChar(pos++, '\\');
|
| res.SetChar(pos++, c);
|
| break;
|
| }
|
| default:
|
| res.SetChar(pos++, next);
|
| break;
|
| }
|
|
|
| //---
|
| continue;
|
| }
|
|
|
| //---
|
| res.SetChar(pos++, m_raw[i]);
|
| }
|
|
|
| //---
|
| res.Truncate(pos);
|
|
|
| //---
|
| return res;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| static ulong CJsonParser::EscapePointer(const string &json_pointer, int& p, const int l)
|
| {
|
| //---
|
| ulong hash = 14695981039346656037ULL;
|
| // hash ^= (ulong)key[i];
|
| // hash *= 1099511628211ULL;
|
|
|
| //---
|
| for(; p < l; p++)
|
| {
|
| //---
|
| const ushort ch = json_pointer[p];
|
| if(ch == '/')
|
| {
|
| p++;
|
| return hash;
|
| }
|
|
|
| //---
|
| if(ch == '\\')
|
| {
|
| p++;
|
| const uchar c = (uchar)json_pointer[p];
|
| const uchar next = TSN::g_tsn_table_json_unescape_table[c];
|
| switch(next)
|
| {
|
| case TSN_TABLE_UNESCAPE_UNICODE:
|
| {
|
| if(p + 4 < l)
|
| {
|
| p++;
|
| uint code = TSN::g_arr_valid_hex_chars[(uchar)json_pointer[p++]];
|
| code = (code << 4) | TSN::g_arr_valid_hex_chars[(uchar)json_pointer[p++]];
|
| code = (code << 4) | TSN::g_arr_valid_hex_chars[(uchar)json_pointer[p++]];
|
| code = (code << 4) | TSN::g_arr_valid_hex_chars[(uchar)json_pointer[p]];
|
|
|
| //--- High surrogate (0xD800-0xDBFF):
|
| if(code >= 0xD800 && code <= 0xDBFF)
|
| {
|
| if(p + 6 < l && json_pointer[p + 1] == '\\' && json_pointer[p + 2] == 'u')
|
| {
|
| const int j = p + 3;
|
| uint low = TSN::g_arr_valid_hex_chars[(uchar)json_pointer[j]];
|
| low = (low << 4) | TSN::g_arr_valid_hex_chars[(uchar)json_pointer[j + 1]];
|
| low = (low << 4) | TSN::g_arr_valid_hex_chars[(uchar)json_pointer[j + 2]];
|
| low = (low << 4) | TSN::g_arr_valid_hex_chars[(uchar)json_pointer[j + 3]];
|
|
|
| //---
|
| if(low >= 0xDC00 && low <= 0xDFFF)
|
| {
|
| hash ^= (ulong)code;
|
| hash *= 1099511628211ULL;
|
| hash ^= (ulong)low;
|
| hash *= 1099511628211ULL;
|
|
|
| //---
|
| p = j + 3;
|
| }
|
| else
|
| {
|
| hash ^= (ulong)0xFFFD;
|
| hash *= 1099511628211ULL;
|
| }
|
| }
|
| else
|
| {
|
| hash ^= (ulong)0xFFFD;
|
| hash *= 1099511628211ULL;
|
| }
|
| }
|
| //--- Low surrogate suelto (sin high antes) -> invalido
|
| else
|
| if(code >= 0xDC00 && code <= 0xDFFF)
|
| {
|
| hash ^= (ulong)0xFFFD;
|
| hash *= 1099511628211ULL;
|
| }
|
| else
|
| {
|
| hash ^= (ulong)code;
|
| hash *= 1099511628211ULL;
|
| }
|
| }
|
| else
|
| {
|
| hash ^= (ulong)'u';
|
| hash *= 1099511628211ULL;
|
| }
|
| break;
|
| }
|
| case TSN_TABLE_UNESCAPE_INVALID:
|
| {
|
| hash ^= (ulong)'\\';
|
| hash *= 1099511628211ULL;
|
| hash ^= (ulong)c;
|
| hash *= 1099511628211ULL;
|
| break;
|
| }
|
| default:
|
| {
|
| hash ^= (ulong)next;
|
| hash *= 1099511628211ULL;
|
| break;
|
| }
|
| }
|
|
|
| //---
|
| continue;
|
| }
|
|
|
| //--- Escapado especial... para js pointer
|
| if(ch == '~')
|
| {
|
| const ushort n = json_pointer[++p];
|
| if(n == '1')
|
| {
|
| hash ^= (ulong)'/';
|
| hash *= 1099511628211ULL;
|
| continue;
|
| }
|
| else
|
| if(n == '0')
|
| {
|
| hash ^= (ulong)'~';
|
| hash *= 1099511628211ULL;
|
| continue;
|
| }
|
|
|
| //--- No es
|
| return 0ULL; // error escape invalido ~X
|
| }
|
|
|
|
|
| //---
|
| hash ^= (ulong)ch;
|
| hash *= 1099511628211ULL;
|
| }
|
|
|
| //---
|
| return hash;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| __forceinline int CJsonParser::GetStepPure(const int idx) const
|
| {
|
| const int tipo = int(m_cinta[idx] & TSN_SBL_BIT_MASK_TYPE);
|
| switch(tipo)
|
| {
|
| case JSON_VTYPE_KEY:
|
| return 1;
|
| case JSON_VTYPE_OBJ:
|
| case JSON_VTYPE_ARR:
|
| case JSON_VTYPE_INTEGER:
|
| case JSON_VTYPE_REAL:
|
| return 2;
|
| case JSON_VTYPE_BOOLEAN:
|
| case JSON_VTYPE_STRING:
|
| case JSON_VTYPE_NULL:
|
| return 1;
|
| }
|
| return 1;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| GetStep: posiciones que ocupa el nodo idx en la cinta |
|
| //+------------------------------------------------------------------+
|
| __forceinline int CJsonParser::GetStep(const int idx)
|
| const
|
| {
|
| switch(int(m_cinta[idx] & TSN_SBL_BIT_MASK_TYPE))
|
| {
|
| case JSON_VTYPE_KEY:
|
| return 1;
|
| case JSON_VTYPE_OBJ:
|
| case JSON_VTYPE_ARR:
|
| return int(m_cinta[idx] >> TSN_SBL_BIT_START_NUM_C);
|
| case JSON_VTYPE_INTEGER:
|
| case JSON_VTYPE_REAL:
|
| return 2;
|
| case JSON_VTYPE_BOOLEAN:
|
| case JSON_VTYPE_STRING:
|
| case JSON_VTYPE_NULL:
|
| return 1;
|
| }
|
| return 1;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| CJsonNode CJsonParser::GetRoot()
|
| {
|
| if(m_cinta_pos == 0)
|
| return CJsonNode::EMPTY_NODE;
|
| return CJsonNode(&this, 0, GetStep(0));
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| void CJsonParser::SerializeToDom(CDomNodeBase *root, CDomNodeManager* manager, int cur, const int end) const
|
| {
|
| //--- Temp
|
| 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;
|
| }
|
|
|
| //---
|
| 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_KEY:
|
| {
|
| const int s = int(m_cinta[cur] >> TSN_SBL_BIT_STR_START);
|
| const int slen = int((m_cinta[cur] >> TSN_SBL_BIT_STR_LEN) & TSN_SBL_BIT_STR_MASK_LEN);
|
| #ifdef JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS
|
| const string str = Unescape(s, s + slen - 1);
|
| #else
|
| const string str = CharArrayToString(m_raw, s, slen);
|
| #endif // JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS
|
|
|
| prev_value = current_container[sp][str];
|
| 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_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_INTEGER:
|
| {
|
| 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_REAL:
|
| {
|
| TsnBitInterprete un;
|
| un.long_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_ENDTYPE) & 1) != 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 = int(m_cinta[cur] >> TSN_SBL_BIT_STR_START);
|
| const int slen = int((m_cinta[cur] >> TSN_SBL_BIT_STR_LEN) & TSN_SBL_BIT_STR_MASK_LEN);
|
| const string str = Unescape(s, s + slen - 1);
|
|
|
| //---
|
| if(current_container[sp].m_type == JSON_VTYPE_ARR)
|
| current_container[sp].AddItem(new CDomNodeBase(manager, str));
|
| else
|
| prev_value = str;
|
| 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;
|
| }
|
| default:
|
| break;
|
| }
|
|
|
| //---
|
| cur += GetStepPure(cur);
|
| }
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| 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);
|
|
|
| //---
|
| while(p < end)
|
| {
|
| const ENUM_JSON_VTYPE tipo = ENUM_JSON_VTYPE(m_cinta[p] & TSN_SBL_BIT_MASK_TYPE);
|
| string text = "[" + (string)p + "] " + EnumToString(tipo) + " | ";
|
|
|
| //---
|
| switch(tipo)
|
| {
|
| 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_BIT_STR_MASK_LEN);
|
|
|
| //---
|
| #ifdef JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS
|
| text += "start=" + (string)s + " len=" + (string)slen + " key=\"" + Unescape(s, s + slen - 1) + "\"";
|
| #else
|
| text += "start=" + (string)s + " len=" + (string)slen + " key=\"" + CharArrayToString(m_raw, s, slen) + "\"";
|
| #endif
|
| break;
|
| }
|
| case JSON_VTYPE_OBJ:
|
| case JSON_VTYPE_ARR:
|
| {
|
| const int count = int((m_cinta[p] >> TSN_SBL_BIT_START_NUM_EL) & TSN_SBL_BIT_MASK_NUM_EL);
|
| const int num_c = int(m_cinta[p] >> TSN_SBL_BIT_START_NUM_C);
|
| const int raw_s = int(m_cinta[p + 1] & 0xFFFFFFFF);
|
| const int raw_e = int(m_cinta[p + 1] >> TSN_JSON_BIT_END_T);
|
| text += "count=" + (string)count + " num_c=" + (string)num_c + " raw_start=" + (string)raw_s + " raw_end=" + (string)raw_e;
|
| break;
|
| }
|
| case JSON_VTYPE_INTEGER:
|
| {
|
| text += "val=" + (string)m_cinta[p + 1];
|
| break;
|
| }
|
| case JSON_VTYPE_REAL:
|
| {
|
| TsnBitInterprete un;
|
| un.long_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_ENDTYPE) & 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_BIT_STR_MASK_LEN);
|
| text += "start=" + (string)s + " len=" + (string)slen + " val=\"" + Unescape(s, s + slen - 1) + "\"";
|
| break;
|
| }
|
| case JSON_VTYPE_NULL:
|
| text += "null";
|
| break;
|
| }
|
|
|
| Print(text);
|
| p += GetStepPure(p);
|
| }
|
| }
|
|
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| void CJsonParser::ErrorInfo(bool with_avanze = false) const
|
| {
|
| if(m_last_err == TSN_JSON_NOT_ERR)
|
| return;
|
|
|
| //---
|
| int col, line;
|
| GetLastErrorLocation(col, line);
|
|
|
| //---
|
| string desc;
|
| switch(m_last_err)
|
| {
|
| case TSN_JSON_ERR_INVALID_EXT_FILE:
|
| desc = "Invalid file extension por JsonParserByLeo";
|
| break;
|
| case TSN_JSON_ERR_INVALID_CHAR:
|
| desc = "Invalid character found in json";
|
| break;
|
| case TSN_JSON_ERR_MALFORMED_LEN_SUP_AFTER_VAL:
|
| desc = "Len has superated reading json, finding comma or context close (], })";
|
| break;
|
| case TSN_JSON_ERR_MALFORDMED_JSON_AFTER_VAL:
|
| desc = "Invalid character find in json, finding comma or context close (], })";
|
| break;
|
| case TSN_JSON_ERR_MALFORDMED_JSON_AFTER_COR_INI:
|
| desc = "Invalid character find in json, finding valid value after '['";
|
| break;
|
| case TSN_JSON_ERR_MALFORDMED_JSON_FIND_VAL:
|
| desc = "Invalid character find in json, finding valid value";
|
| break;
|
| case TSN_JSON_ERR_MALFODERD_KEY_EXPECTED_DOS_PUNTOS:
|
| desc = "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:
|
| desc = "Len has superated, but json are not closed, malformed json";
|
| break;
|
| case TSN_JSON_ERR_JSON_NOT_FINISH:
|
| desc = "Json read has finish but not closed in ] or }, malformed json";
|
| break;
|
|
|
| //---
|
| case TSN_JSON_ERR_OVERFLOW_IN_STACK:
|
| desc = "Overflow for maxium stack";
|
| break;
|
| case TSN_JSON_ERR_NOT_STRUCUTURAL_CHARS_FOUND:
|
| desc = "Not structural character found in this json ({ | } | [ | ] | : | ,)";
|
| break;
|
|
|
| //---
|
| case TSN_JSON_ERR_INVALID_STATE_IN_PARSE_DOUBLE_E:
|
| desc = "Invalid state for Json Sax Parsing, in parse double value";
|
| break;
|
|
|
| //---
|
| default:
|
| desc = "Unknow error";
|
| break;
|
| }
|
|
|
| //---
|
| PrintFormat("%s [Line %d, Col %d]", desc, line, col);
|
| if(with_avanze)
|
| {
|
| Print("Avanze: ");
|
| Print(CharArrayToString(m_raw, 0, m_pos));
|
| }
|
| }
|
| }
|
| #endif // JSONPARSERBYLEO_SRC_JSONPARSER_MQH
|
| //+------------------------------------------------------------------+
|
| /*
|
| //
|
| // a\\\"\nb
|
| B = 0b01110100
|
| -----
|
| ~B<<1 = 0b00010111
|
|
|
| starts= 0b00010100
|
|
|
| 0b01110100
|
| sum = 0b00010100
|
| 10001000
|
|
|
|
|
| 10001000
|
| 01110100 (and)
|
| 00000000
|
|
|
| -----
|
| B = "\\a\\bb
|
| B = 01101100
|
| B<<1 = 11011000
|
| ~B = 00100111
|
|
|
| --
|
| starts = 01101100
|
| 00100111 and
|
| 00100100
|
| --
|
| = 01101100 +
|
| 00100100
|
| 10010000
|
| --
|
| eo = 10010000
|
| 01101100
|
| 00000000
|
| */
|
| //+------------------------------------------------------------------+
|