611 lines
18 KiB
MQL5
611 lines
18 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| 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 <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;
|
|
}
|
|
|
|
}
|
|
|
|
//---
|
|
#endif // TSNSTD_SRC_STRING_CONV_MQH
|
|
//+------------------------------------------------------------------+
|