TSNStd/Src/String/Conv.mqh

611 lines
18 KiB
MQL5

2026-10-09 12:34:15 -05:00
//+------------------------------------------------------------------+
//| Conv.mqh |
//| Copyright 2026, Niquel Mendoza. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Niquel Mendoza."
#property link "https://www.mql5.com"
#property strict
2026-10-09 12:56:38 -05:00
#ifndef TSNSTD_SRC_STRING_CONV_MQH
#define TSNSTD_SRC_STRING_CONV_MQH
2026-10-09 12:34:15 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
#include "ConvDef\\Dbl.mqh"
#include <TSN\\Tables\\DPairs.mqh>
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
namespace TSN
{
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CStringConvert
{
private:
public:
CStringConvert(void) {}
~CStringConvert(void) {}
//---
// static int s_len;
//--- to value genericos
template <typename T>
static void ToValueNSigned(const string& str, int& pos, T& v, const int s_len);
template <typename T>
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 <typename T>
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 <typename T>
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 <typename TUValue>
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 <typename TUValue>
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;
}
}
//---
2026-10-09 12:56:38 -05:00
#endif // TSNSTD_SRC_STRING_CONV_MQH
2026-10-09 12:34:15 -05:00
//+------------------------------------------------------------------+