//+------------------------------------------------------------------+ //| Main.mqh | //| Copyright 2026, Niquel Mendoza. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, Niquel Mendoza." #property link "https://www.mql5.com" #property strict #ifndef BASESPARSERSLAN_UE_MAIN_MQH #define BASESPARSERSLAN_UE_MAIN_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include #include "Def.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ namespace TSN { //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // Esta clase como tal se encargar de desescapar strings.. con reglas de escape // ya depende del elngauje final... // aparte el backend como tal debera de proporcional una tabla de escapes validos //--- optimizaciones // estuve busncado buen rato un par de horas... un buen algoritmo paara utf8 branchless // etc.. pero no tuve buenos reusltoas el tema es uqe un CLZ banrhacles etc para contar bytes requerido para utf8 // en mql5 es mas costoso incluso con miss predict que una verisno de ternarios ifs... // y en parte tiene snrinfgo CLZ + otra talba son mas cosots, acceso a memoeira luego vaiors shifts or etc.. // dado qeu eMQL5 no tieen clz nativo, hay que simualres con brujin y patrones de bits.. // asi qeu creo que sincmante este algoetim de ifelseif parece ser el indicado para MQL5 // no veo uan alternativa mejor para esto.. o que logre superarlo las version con CLZ o branhclees // son en promedio un 40-300% mas lentas, asi que me quedare con la via "simple y legible" en MQL6 // no todo es brancless aqui .. se veia venir tmb //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CBSFLUnescape { public: CBSFLUnescape(void) {} ~CBSFLUnescape(void) {} //--- Basicos // string \ uarray // decodifica los ecapes a utf16 template static bool MakeString(const uchar &in[], int i, int len, string& res); // decodifica los escapes a utf8 template static int MakeUArray(const uchar &in[], int i, int len, uchar &res[], int pos = 0); // decodifica los ecapes a un hash compatible con vm template static bool MakeHash(const uchar &in[], int& i, int len, ulong& hash); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template static bool CBSFLUnescape::MakeHash(const uchar &in[], int& i, int len, ulong &hash) { //--- hash = 14695981039346656037ULL; for(;; i++) // nota aqui el " se nos garantiza pero igual en uncidoe hay que vertiicar el len { //--- const uchar ch = in[i]; if(ch == '\\') { const uchar next = TBackend::s_unescape_table[in[++i]]; switch(next) { case TSN_TABLE_UNESCAPE_U32: { //--- U32 si requiere "desescaparlo" if(i + 8 >= len) return false; DOMBASEULTIS_U_U32_MAKESTRING_CODE(code, i) if(code > 0x10FFFF) return false; //--- if(code <= 0xffff) { DOMBASEULTIS_U_MKH_ITER(code) } else // surrogates { code -= 0x10000; DOMBASEULTIS_U_MKH_ITER(ushort(0xD800 + (code >> 10))) DOMBASEULTIS_U_MKH_ITER(ushort(0xDC00 + (code & 0x3FF))) } break; } case TSN_TABLE_UNESCAPE_HEX: { if(i + 2 >= len) return false; //--- uchar v = g_arr_valid_hex_chars[in[++i]] << 4; v |= g_arr_valid_hex_chars[in[++i]]; DOMBASEULTIS_U_MKH_ITER(v) break; } case TSN_TABLE_UNESCAPE_UNICODE: { if(i + 4 >= len) return false; //--- DOMBASEULTIS_U_UNICODE_MAKESTRING_CODE(code, i) DOMBASEULTIS_U_MKH_ITER(code) break; } case TSN_TABLE_UNESCAPE_INVALID: return false; default: DOMBASEULTIS_U_MKH_ITER(next) break; } continue; } if(ch == '"') { i++; // lo dejamos luego del " return true; } //-- DOMBASEULTIS_U_MKH_ITER(ch) } return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // 1. se saume un input valido en utf8 etc... // 2. Ahora lo qeu hace esta funcion es desescapar y aparte los \uXXXX como mql5 es UTF16 // los pone tal cual.. ahora los UTF32 (\UXXXXXXXX) auqi si hay logica de surrogate etc.. template static bool CBSFLUnescape::MakeString(const uchar &in[], int i, int len, string& res) { //--- StringInit(res, len, ' '); int pos = 0; //--- len += i; for(; i < len; i++) { //--- const uchar ch = in[i]; if(ch == '\\') { const uchar next = TBackend::s_unescape_table[in[++i]]; switch(next) { case TSN_TABLE_UNESCAPE_U32: { //--- U32 si requiere "desescaparlo" if(i + 8 >= len) return false; DOMBASEULTIS_U_U32_MAKESTRING_CODE(code, i) if(code > 0x10FFFF) return false; //--- if(code <= 0xffff) { res.SetChar(pos++, (ushort)code); } else // surrogates { code -= 0x10000; res.SetChar(pos++, ushort(0xD800 + (code >> 10))); // high res.SetChar(pos++, ushort(0xDC00 + (code & 0x3FF))); // low } break; } case TSN_TABLE_UNESCAPE_HEX: { if(i + 2 >= len) return false; //--- uchar v = g_arr_valid_hex_chars[in[++i]] << 4; v |= g_arr_valid_hex_chars[in[++i]]; res.SetChar(pos++, v); break; } case TSN_TABLE_UNESCAPE_UNICODE: { if(i + 4 >= len) return false; //--- DOMBASEULTIS_U_UNICODE_MAKESTRING_CODE(code, i) res.SetChar(pos++, code); break; } case TSN_TABLE_UNESCAPE_INVALID: return false; default: res.SetChar(pos++, next); break; } continue; } //-- res.SetChar(pos++, ch); } //--- res.Truncate(pos); return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // Toma los bytes in y luego para los escapes \u \U \x los pasa utf8 template static int CBSFLUnescape::MakeUArray(const uchar &in[], int i, int len, uchar &res[], int pos = 0) { //--- ArrayResize(res, len); //--- len += i; for(; i < len; i++) { //--- const uchar ch = in[i]; if(ch == '\\') { const uchar next = TBackend::s_unescape_table[in[++i]]; switch(next) { case TSN_TABLE_UNESCAPE_U32: { if(i + 8 >= len) return false; //--- DOMBASEULTIS_U_U32_MAKESTRING_CODE(code, i) if(code > 0x10FFFF) return false; //--- logica aqui... BSFL_WRITE_ASCCI(code) else if(code < 0x10000) { BSFL_UTF8_WRITE_3(code) } else { BSFL_UTF8_WRITE_4(code) } //--- break; } case TSN_TABLE_UNESCAPE_HEX: { if(i + 2 >= len) return false; //--- uchar v = g_arr_valid_hex_chars[in[++i]] << 4; v |= g_arr_valid_hex_chars[in[++i]]; //--- if(v < 0x80) { res[pos++] = (v); } else { res[pos++] = (0xC0 | (v >> 6)); res[pos++] = (0x80 | (v & 0x3F)); } break; } case TSN_TABLE_UNESCAPE_UNICODE: { if(i + 4 >= len) return -1; // error //--- DOMBASEULTIS_U_UNICODE_MAKESTRING_CODE(code, i) //--- BSFL_WRITE_ASCCI(code) //--- verificamos si el unicode one esta presente (osea si no veriicamos surrogates si no tal cual como yaml) // o json que si hay que verificar... if(sizeof(TUnicodeOne) == sizeof(BSFL_UE_SURROGATES)) { uint cp = code; if(code >= 0xD800 && code <= 0xDBFF) // Esta en rango buscamos otro mas. { if(i + 6 < len && in[i + 1] == '\\' && in[i + 2] == 'u') { int j = i + 2; DOMBASEULTIS_U_UNICODE_MAKESTRING_CODE(low, j) if(low >= 0xDC00 && low <= 0xDFFF) { cp = 0x10000 + ((code - 0xD800) << 10) + (low - 0xDC00); i = j; //--- emoji BSFL_UTF8_WRITE_4(cp) continue; } } //--- // if(sizeof(TStrict) == JSONUNESCAPE_STRICT) // return -1; code = 0xFFFD; // huerfano, no hay low surrogate despues } else if(code >= 0xDC00 && code <= 0xDFFF) { // if(sizeof(TStrict) == JSONUNESCAPE_STRICT) // return -1; code = 0xFFFD; } } // si no lo hay entonces siremp max 0xffff unico //--- BSFL_UTF8_WRITE_3(code) continue; } case TSN_TABLE_UNESCAPE_INVALID: return -1; // error default: res[pos++] = next; continue; } } //--- res[pos++] = ch; } //--- return ArrayResize(res, pos); } //--- } #endif // BASESPARSERSLAN_UE_MAIN_MQH //+------------------------------------------------------------------+