forked from nique_372/TSNStd
616 lines
18 KiB
MQL5
616 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 void ULLToString(ulong uvalue, string& str, int pos);
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
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
| |
//---
| |
// VALUE= entero
| |
// uv= ulong
| |
// lo que hace es "un alias" para añadir a un indice la logintud de UV...
| |
#define STRCONV_VALTOBUFFER_ADDDIG(VALUE, UV) VALUE+=CBitTricks::CountDigitsBinary(UV);
| |
| |
//---
| |
#define STRCONV_VALTOBUFFER_ULLSTR_WR(UV, STR, PREFIX) \
| |
const int __PREFIX = CBitTricks::CountDigitsBinary(UV); \
| |
StringInit(STR, __PREFIX, ' '); \
| |
CValueToBuffer::ULLToString(UV, STR, __PREFIX);
| |
| |
//+------------------------------------------------------------------+
| |
static void CValueToBuffer::ULLToString(ulong uvalue, string& str, int 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));
| |
}
| |
| |
}
| |
| |
//---
| |
#endif // TSNSTD_SRC_STRING_CONV_MQH
| |
//+------------------------------------------------------------------+
|