//+------------------------------------------------------------------+ //| Conv.mqh | //| Copyright 2026, Niquel Mendoza. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, Niquel Mendoza." #property link "https://www.mql5.com" #property strict #ifndef TSNSTD_SRC_STRING_CONV_MQH #define TSNSTD_SRC_STRING_CONV_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "ConvDef\\Dbl.mqh" #include //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ namespace TSN { //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CStringConvert { private: public: CStringConvert(void) {} ~CStringConvert(void) {} //--- // static int s_len; //--- to value genericos template static void ToValueNSigned(const string& str, int& pos, T& v, const int s_len); template static void ToValueNUnsigned(const string& str, int& pos, T& v, const int s_len); //--- to number signed static __forceinline void ToValue(const string& str, int& pos, long& v, const int s_len) { ToValueNSigned(str, pos, v, s_len); } static __forceinline void ToValue(const string& str, int& pos, int& v, const int s_len) { ToValueNSigned(str, pos, v, s_len); } static __forceinline void ToValue(const string& str, int& pos, short& v, const int s_len) { ToValueNSigned(str, pos, v, s_len); } //--- ungisend static __forceinline void ToValue(const string& str, int& pos, ulong& v, const int s_len) { ToValueNUnsigned(str, pos, v, s_len); } static __forceinline void ToValue(const string& str, int& pos, uint& v, const int s_len) { ToValueNUnsigned(str, pos, v, s_len); } static __forceinline void ToValue(const string& str, int& pos, ushort& v, const int s_len) { ToValueNUnsigned(str, pos, v, s_len); } static __forceinline void ToValue(const string& str, int& pos, uchar& v, const int s_len) { ToValueNUnsigned(str, pos, v, s_len); } //--- static void ToValue(const string& str, int& pos, double& val, const int s_len); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ static void CStringConvert::ToValue(const string &str, int &pos, double &val, const int s_len) { //--- Inicial val = 0.0; ulong v = 0; uchar ch = (uchar)str[pos] ^ '0'; long mask = 0; int lock = pos; //--- primer valor if(ch > 9) { switch(ch) { case('+' ^ '0'): { if(++pos >= s_len) return; lock++; if((ch = (uchar)str[pos] ^ '0') > 9) { if(!STRCONV_DECX_CH_IS_PDEC(ch)) // si en caso la mascara no es -1 return; ch = 0; pos = 0; // justo en + } break; } case(',' ^ '0'): return; case('-' ^ '0'): { if(++pos >= s_len) return; mask = -1; if((ch = (uchar)str[pos] ^ '0') > 9) { if(!STRCONV_DECX_CH_IS_PDEC(ch)) // si en caso la mascara no es -1 return; ch = 0; pos = 0; // justo en - } break; } case('.' ^ '0'): { ch = 0; pos = -1; break; } default: return; // invalido } } //--- Enteros while(true) { //--- 20 iteraciones maximo (unrolling) STRCONV_TO_VALUE_IT(v, ch) if(!v) // leading zero inicial (no cuenta) { do { if(++pos >= s_len) return; } while((ch = (uchar)str[pos]) == '0'); if((ch ^= '0') > 9) // punto o e\E. { lock = pos - 1; // antes de dicho valor break; // sale } lock = pos; // justo en este valor //--- iteracion extra para empzar bien STRCONV_TO_VALUE_IT(v, ch) } STRCONV_U18_IT(v, ch) //--- apartir de aqui ya no impota la parte decimal asi que "truncamos a 19 justo como lo tenemos" const int ps = pos; // incio de la parte truncada while(++pos < s_len && ((uchar)str[pos] ^ '0') < 10) {} if(STRCONV_DECX_CH_IS_DEC(ch)) // es decimal en todo caso.. break; //--- e base int e = pos - ps; ulong vf; // vfinal int ef; // efinal CDoubleParser::ComputeDbl(e, v, vf, ef); //--- e +1 ulong p1_v; // pv int p1_e; // ep CDoubleParser::ComputeDbl(e, v + 1, p1_v, p1_e); //--- if(p1_v == vf && ef == p1_e) { STRCONV_DOUBLE_MAKE_DBL_WMASK(ef, vf, mask, val) } else // fallback a nativo (como nuestro big integer) val = StringToDouble(str.Substr(lock, (pos - lock))); return; } //--- Garantia: Si llega hasta aqui es que se cumple: // 1. v en caso llege con ov si o si es decimal (asi qeu la ruta 2 de v tal cual esta bien), si no tiene ov // puede ser o dec o ent //--- Ahora double if(STRCONV_DECX_CH_IS_DEC(ch)) // decimal aqui empezamos... { long exponente = 0; int digitos = (pos - lock); // ojo tal cual luego hay que sumar mask // int k = 0; //--- switch(0) { default: { if(STRCONV_DECX_CH_IS_PDEC(ch)) { if(++pos >= s_len || (ch = (uchar)str[pos] ^ '0') > 9) break; //--- int ds = pos; while(true) { STRCONV_TO_VALUE_IT(v, ch) if(!v) // patron probale: 0.05 { if(ch == 0) { while(++pos < s_len && (ch = (uchar)str[pos]) == '0') {} digitos = (ds - pos); // calculamos if(!(ch ^= '0') || ch > 9) // sale por 0 o mayor a 9 break; } else digitos = (ds - pos); // otra mas por que se perdio un valor para sumar los 19 digitos STRCONV_TO_VALUE_IT(v, ch) } STRCONV_U18_IT(v, ch) STRCONV_SKIP_NUMBER break; } //--- ds = (pos - ds); exponente -= ds; digitos += ds; } if(STRCONV_DECX_CH_IS_E(ch)) { if(++pos >= s_len) break; //--- bool exp_positivo = true; ch = (uchar)str[pos] ^ '0'; if(ch > 9) { if(STRCONV_DECX_CH_IS_MINUS(ch)) exp_positivo = false; if(++pos >= s_len || (ch = (uchar)str[pos] ^ '0') > 9) break; // luego del +\- no hay un numero } int expo = 0; //--- while(true) // siguimos acomulando en v { STRCONV_TO_VALUE_IT(expo, ch) if(expo == 0) // leading zeros.. ahora mismo aun hay digitos pero el primero es 0.. saltamos { STRCONV_LEADING_ZEROS_SKIP_STRICT(break) } STRCONV_8_IT(expo, ch) STRCONV_TOV_ALG(expo, ch) if(++pos >= s_len || (ch = (uchar)str[pos] ^ '0') > 9) { if(expo > 0) // es valido break; // no ov } else // sigue saltamos todo lo que hya { STRCONV_SKIP_NUMBER } // satura.. expo = INT_MAX; break; // } exponente += exp_positivo ? expo : -expo; } break; } } //--- Fase 3 final... digitos += (int)mask; if(STRCONV_DOUBLE_SAFE_DBL(digitos, exponente)) // clinger fast path { __UDoubleToLong u; u.d = STRCONV_TO_DOUBLE_ALG(exponente, v); u.l ^= (mask & (1LL << 63)); val = u.d; } else { //--- if(digitos < 20) // lemire -mid path primero vemos si se puede aun { int e = int(exponente); CDoubleParser::ComputeDbl(e, v, v, e); STRCONV_DOUBLE_MAKE_DBL_WMASK(e, v, mask, val) return; } //--- slow path big integer. val = StringToDouble(str.Substr(lock, (pos - lock))); } } else { __UDoubleToLong u; u.d = double(v); u.l ^= (mask & (1LL << 63)); val = u.d; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // 1. No se acepta ws adelante // 2. Se asume como minimo un digiito valid (len minimo 1 por parte del caller) // 3. Se para justo en el primer char no numero // 4. solo tipos nativos (menoes a 64 bits.. y solo se acepta 1\2\4\8 bytes) // osea long,ulong,int,uint,char,uchar,short,ushort,color,datetime (todos los numeros nativos) //--- template static void CStringConvert::ToValueNSigned(const string &str, int& pos, T& v, const int s_len) { //--- Inicial v = 0; uchar ch = (uchar)str[pos] ^ '0'; T mask = 0; //--- primer valor (invalido) if(ch > 9) { if(++pos >= s_len) return; if(ch == STRCONV_ZERO_XOR_MENOS) mask = -1; if((ch = (uchar)str[pos] ^ '0') > 9) return; } //--- leading zeros if(!ch) { STRCONV_LEADING_ZEROS_SKIP_STRICT(return) } //--- ahora si const int lock = pos; // bucle sin ramas ni nada.. while(true) { //--- 19 iteraciones maximo (unrolling) STRCONV_U18_IT(v, ch) // la iteracion 19.. tambien se salva STRCONV_TO_VALUE_IT(v, ch) // si logra pasar ya es que aun sigue siendo valido... pero ya esto es mas de 20 digitos ov seguro //--- // la idea es dejar el pos hasta donde podmoas.. ya aqui ya no acomularemso v no tiene caso. STRCONV_SKIP_NUMBER //--- overflow o underf. seguro (caulquier caso) v = NUMBERUTILS_MAX_VALUE_OFT_SIGNED(T) ^ mask; //--- return; } //--- Comportamiento igual que el original saturamos.. if((pos - lock) >= STRCONV_DIGITOS_SIGNED(T)) // en caso sea mayor o igual.. hay posible ov { const int digits = (pos - lock); // calculamos el numero de digitos if(digits == STRCONV_DIGITOS_SIGNED(T)) // maximo.. posible revisamo { ch = (uchar)str[lock] ^ '0'; const bool sat = (ch > STRCONV_I_FIRST_DIG(T) || (ch == STRCONV_I_FIRST_DIG(T) && v < 0)); if(!sat) return; } // Se pasa.. entonces si o si hay ov... saturamos.. //--- v = NUMBERUTILS_MAX_VALUE_OFT_SIGNED(T) ^ mask; return; // sale. } //--- v ^= mask; v -= mask; } //+------------------------------------------------------------------+ template static void CStringConvert::ToValueNUnsigned(const string& str, int& pos, T& v, const int s_len) { //--- Inicial v = 0; uchar ch = (uchar)str[pos] ^ '0'; //--- primer valor (invalido) if(ch > 9) return; //--- leading zeros if(!ch) { STRCONV_LEADING_ZEROS_SKIP_STRICT(return) } //--- ahora si const int lock = pos; while(true) { //--- 20 iteraciones maximo (unrolling) STRCONV_U18_IT(v, ch) STRCONV_TO_VALUE_IT(v, ch) // la iteracion 20.. tambien se salva STRCONV_TO_VALUE_IT(v, ch) // si logra pasar ya es que aun sigue siendo valido... pero ya esto es mas de 20 digitos ov seguro //--- // la idea es dejar el pos hasta donde podmoas.. ya aqui ya no acomularemso v no tiene caso. STRCONV_SKIP_NUMBER //--- overflow seguro (caulquier caso) v = NUMBERUTILS_MAX_VALUE_OFT_USIGNED(T); return; } //--- Comportamiento igual que el original saturamos.. if((pos - lock) >= STRCONV_DIGITOS_UNSIGNED(T)) // en caso sea mayor o igual.. hay posible ov { const int digits = (pos - lock); // calculamos el numero de digitos if(digits == STRCONV_DIGITOS_UNSIGNED(T)) // maximo.. posible revisamo { ch = (uchar)str[lock] ^ '0'; const bool sat = (ch > STRCONV_U_FIRST_DIG(T) || (ch == STRCONV_U_FIRST_DIG(T) && v < (T)NUMBERUTILS_MAX_VALUE_OFT_SIGNED(T))); if(!sat) return; } // Se pasa.. entonces si o si hay ov... saturamos.. //--- v = NUMBERUTILS_MAX_VALUE_OFT_USIGNED(T); } } //+------------------------------------------------------------------+ //| Maximo numero de digitos | //+------------------------------------------------------------------+ //--- incluye el signo para i\l (+1) #define MQLARTICLES_CONV_MAXDIG_I (11) #define MQLARTICLES_CONV_MAXDIG_L (20) //--- #define MQLARTICLES_CONV_MAXDIG_UL (20) //--- // 16 + 1 + 1 + 4 (2 a la 64 divido entre 10 a la 16..) // 6 si o si mas digitos #define MQLARTICLES_CONV_MAXDIG_DBL(dig) (6 + dig) //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CValueToBuffer { public: CValueToBuffer(void) {} ~CValueToBuffer(void) {} //--- convertir enteros //- unsigned template static void _ToValUSigned(TUValue uvalue, uchar& in[], int pos); //--- static void ToValue(double v, uchar& in[], int& w, uchar digits); //--- static string ULLToStr(ulong uvalue); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ static void CValueToBuffer::ToValue(double v, uchar &in[], int &w, uchar digits) { //--- signo if(v < 0) { in[w++] = '-'; v = -v; } //--- const double factor = g_potencias_de_10[digits]; // potencias positivas const ulong escalado = (ulong)MathRound(v * factor); const ulong factor_u = (ulong)factor; ulong ent = escalado / factor_u; ulong dec = escalado - ent * factor_u; //--- { int pos = w + CBitTricks::CountDigitsBinary(ent); w = pos; //--- while(ent >= 100) { const ulong q = ent / 100ULL; const ulong idx = (ent - q * 100ULL) << 1; ent = q; in[--pos] = g_tsn_digit_pairs[idx + 1]; in[--pos] = g_tsn_digit_pairs[idx]; } if(ent >= 10) { const ulong idx = ent << 1ULL; in[--pos] = g_tsn_digit_pairs[idx + 1]; in[--pos] = g_tsn_digit_pairs[idx]; } else in[--pos] = uchar('0' + ent); } //--- in[w++] = '.'; //--- int pos = w + digits; w = pos; //--- int i = 0; for(; i + 1 < digits; i += 2) { const ulong q = dec / 100ULL; const ulong idx = (dec - q * 100ULL) << 1; dec = q; in[--pos] = g_tsn_digit_pairs[idx + 1]; in[--pos] = g_tsn_digit_pairs[idx]; } if(i < digits) in[--pos] = uchar('0' + (dec % 10ULL)); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // helper #define STRCONV_VALTOBUFFER_USIGNED(uvalue, in, w) \ w += CBitTricks::CountDigitsBinary(uvalue); \ TSN::CValueToBuffer::_ToValUSigned(uvalue, in, w); //+------------------------------------------------------------------+ #define STRCONV_VALTOBUFFER_SIGNED(value, in, w, TUValue, VAR) \ TUValue VAR; \ if(value < 0) \ { \ in[w++] = '-'; \ VAR = TUValue(-(value + 1)) + 1; \ } \ else \ VAR = TUValue(value); \ STRCONV_VALTOBUFFER_USIGNED(VAR, in, w) //+------------------------------------------------------------------+ // asume pos ya aumentado si no puede ser algo consistente (seria raro no?¿) use los macros template static void CValueToBuffer::_ToValUSigned(TUValue uvalue, uchar &in[], int pos) { //--- while(uvalue >= 100) { const TUValue prev = uvalue; uvalue /= 100U; const TUValue idx = (prev - (uvalue * 100U)) << 1; in[--pos] = g_tsn_digit_pairs[idx + 1]; in[--pos] = g_tsn_digit_pairs[idx]; } if(uvalue >= 10) { const TUValue idx = uvalue << 1; in[--pos] = g_tsn_digit_pairs[idx + 1]; in[--pos] = g_tsn_digit_pairs[idx]; } else in[--pos] = uchar('0' + uvalue); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // Extra para string static string CValueToBuffer::ULLToStr(ulong uvalue) { //--- int pos = CBitTricks::CountDigitsBinary(uvalue); string str; StringInit(str, pos, ' '); //--- while(uvalue >= 100) { const ulong prev = uvalue; uvalue /= 100U; const ulong idx = (prev - (uvalue * 100U)) << 1; str.SetChar(--pos, g_tsn_digit_pairs[idx + 1]); str.SetChar(--pos, g_tsn_digit_pairs[idx]); } if(uvalue >= 10) { const ulong idx = uvalue << 1; str.SetChar(--pos, g_tsn_digit_pairs[idx + 1]); str.SetChar(--pos, g_tsn_digit_pairs[idx]); } else str.SetChar(--pos, uchar('0' + uvalue)); //--- return str; } } //--- #endif // TSNSTD_SRC_STRING_CONV_MQH //+------------------------------------------------------------------+