2026-07-13 18:47:16 -05:00 | | | //+------------------------------------------------------------------+
|
| | | //| BuilderDefs.mqh |
|
| | | //| Copyright 2026, Niquel Mendoza. |
|
| | | //| https://www.mql5.com/ |
|
| | | //+------------------------------------------------------------------+
|
| | | #property copyright "Copyright 2026, Niquel Mendoza."
|
| | | #property link "https://www.mql5.com/"
|
| | | #property strict
|
| | |
|
| | | #ifndef JSONPARSERBYLEO_SRC_BUILDERSDEFS_MQH
|
| | | #define JSONPARSERBYLEO_SRC_BUILDERSDEFS_MQH
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | #include "JsonDefines.mqh"
|
2026-07-14 09:22:38 -05:00 | | | #include <TSN\\Tables\\DPairs.mqh>
|
2026-07-13 18:47:16 -05:00 | | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | #define JSONBUILDER_EXPAND(need) if(m_pos + (need) >= m_cap) { m_cap += (need) + 4096; ArrayResize(m_buf, m_cap, m_cap); }
|
| | | #define JSONBUILDER_EXPAND_E(need) if(m_pos + (need) >= m_cap) { m_cap += (need); ArrayResize(m_buf, m_cap, m_cap); }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | #define JSON_BUILDER_PUT(c) \
|
| | | JSONBUILDER_EXPAND(1) \
|
| | | m_buf[m_pos++] = c; \
|
| | |
|
| | | //---
|
| | | #define JSON_BUILDER_COMMA \
|
| | | if(!m_stack_first[m_sp]) \
|
| | | { \
|
| | | JSON_BUILDER_PUT(',') \
|
| | | } \
|
| | | m_stack_first[m_sp] = false;
|
| | |
|
2026-07-14 09:22:38 -05:00 | | | //---
|
| | | #define JSON_BUILDER_COMMA_F \
|
| | | if(!m_stack_first[m_sp]) \
|
| | | { \
|
| | | m_buf[m_pos++] = ','; \
|
| | | } \
|
| | | m_stack_first[m_sp] = false;
|
| | |
|
| | |
|
2026-07-13 18:47:16 -05:00 | | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | namespace TSN
|
| | | {
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | class CJsonBuilderBase
|
| | | {
|
| | | public:
|
| | | uchar m_buf[];
|
| | | int m_pos;
|
| | | int m_cap;
|
| | |
|
| | | protected:
|
| | | bool m_stack_first[64];
|
| | | int m_sp;
|
| | |
|
| | | public:
|
| | | CJsonBuilderBase(int initial_cap = 1024);
|
| | | ~CJsonBuilderBase() {}
|
| | |
|
| | | //---
|
| | | void Clear();
|
| | |
|
| | | //---
|
| | | __forceinline string Build() const { return CharArrayToString(m_buf, 0, m_pos, CP_UTF8); }
|
| | | __forceinline int Build(uchar& out[], int dst_start = 0) const { return ArrayCopy(out, m_buf, dst_start, 0, m_pos); }
|
| | | __forceinline void Truncate() { m_cap = ArrayResize(m_buf, m_pos, m_cap); }
|
| | |
|
| | |
|
| | | //--- Structure
|
2026-07-14 09:22:38 -05:00 | | | CJsonBuilderBase* Obj();
|
| | | CJsonBuilderBase* EndObj();
|
| | | CJsonBuilderBase* Arr();
|
| | | CJsonBuilderBase* EndArr();
|
2026-07-13 18:47:16 -05:00 | | |
|
| | | // Valor basico
|
2026-07-14 09:22:38 -05:00 | | | CJsonBuilderBase* Val(int v);
|
| | | CJsonBuilderBase* Val(long value);
|
| | | CJsonBuilderBase* Val(double v, int digits = 8);
|
| | | CJsonBuilderBase* Val(bool v);
|
2026-07-13 18:47:16 -05:00 | | |
|
| | | // Null
|
2026-07-14 09:22:38 -05:00 | | | CJsonBuilderBase* Null();
|
2026-07-13 18:47:16 -05:00 | | |
|
| | | //---
|
| | | void CheckCap();
|
2026-07-15 12:23:27 -05:00 | | | void CheckComma();
|
2026-07-13 18:47:16 -05:00 | | |
|
| | | //--- Put raw
|
| | | void PutChar(const uchar c);
|
2026-07-14 09:22:38 -05:00 | | | void PutRawNoRef(const string s);
|
| | | void PutRaw(const string& s);
|
| | | void PutRaw(const uchar& s[], int count = WHOLE_ARRAY);
|
| | |
|
2026-07-13 18:47:16 -05:00 | | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | CJsonBuilderBase::CJsonBuilderBase(int initial_cap)
|
| | | {
|
| | | m_cap = initial_cap;
|
| | | ArrayResize(m_buf, m_cap);
|
| | | m_pos = 0;
|
| | | m_sp = 0;
|
| | | m_stack_first[0] = true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | void CJsonBuilderBase::CheckCap(void)
|
| | | {
|
| | | if(m_pos >= m_cap)
|
| | | {
|
| | | m_cap = m_pos + 4096;
|
| | | ArrayResize(m_buf, m_cap, m_cap);
|
| | | }
|
| | | }
|
| | |
|
2026-07-15 12:23:27 -05:00 | | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | void CJsonBuilderBase::CheckComma(void)
|
| | | {
|
| | | JSON_BUILDER_COMMA
|
| | | }
|
| | |
|
2026-07-13 18:47:16 -05:00 | | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | void CJsonBuilderBase::Clear()
|
| | | {
|
| | | m_pos = 0;
|
| | | m_sp = 0;
|
| | | m_stack_first[0] = true;
|
| | | }
|
| | |
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | void CJsonBuilderBase::PutChar(const uchar c)
|
| | | {
|
| | | JSON_BUILDER_PUT(c)
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | void CJsonBuilderBase::PutRaw(const uchar& s[], int count = WHOLE_ARRAY)
|
| | | {
|
| | | //---
|
| | | const int l = (count == WHOLE_ARRAY) ? ArraySize(s) : fmin(ArraySize(s), count);
|
| | | const int max_bytes = l * 4 + 1;
|
| | | JSONBUILDER_EXPAND(max_bytes)
|
| | |
|
| | | //---
|
| | | m_pos += ArrayCopy(m_buf, s, m_pos, 0, l);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | void CJsonBuilderBase::PutRaw(const string& s)
|
| | | {
|
| | | //---
|
| | | const int l = StringLen(s);
|
| | | const int max_bytes = l * 4 + 1;
|
| | | JSONBUILDER_EXPAND(max_bytes)
|
| | |
|
| | | //---
|
| | | int written = StringToCharArray(s, m_buf, m_pos, WHOLE_ARRAY, CP_UTF8);
|
| | | if(written > 0 && m_buf[m_pos + written - 1] == 0)
|
| | | written--;
|
| | | m_pos += written;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | void CJsonBuilderBase::PutRawNoRef(const string s)
|
| | | {
|
| | | //---
|
| | | const int l = StringLen(s);
|
| | | const int max_bytes = l * 4 + 1;
|
| | | JSONBUILDER_EXPAND(max_bytes)
|
| | |
|
| | | //---
|
| | | int written = StringToCharArray(s, m_buf, m_pos, WHOLE_ARRAY, CP_UTF8);
|
| | | if(written > 0 && m_buf[m_pos + written - 1] == 0)
|
| | | written--;
|
| | | m_pos += written;
|
| | | }
|
| | |
|
| | |
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | CJsonBuilderBase* CJsonBuilderBase::Obj()
|
| | | {
|
2026-07-14 09:22:38 -05:00 | | | JSONBUILDER_EXPAND_E(3)
|
| | | JSON_BUILDER_COMMA_F
|
| | | m_buf[m_pos++] = '{';
|
| | |
|
| | | //---
|
2026-07-13 18:47:16 -05:00 | | | if(m_sp < 63)
|
| | | {
|
| | | m_sp++;
|
| | | m_stack_first[m_sp] = true;
|
| | | }
|
| | | return &this;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | CJsonBuilderBase* CJsonBuilderBase::EndObj()
|
| | | {
|
| | | if(m_sp > 0)
|
| | | m_sp--;
|
2026-07-14 09:22:38 -05:00 | | |
|
| | | //---
|
2026-07-13 18:47:16 -05:00 | | | JSON_BUILDER_PUT('}')
|
| | | m_stack_first[m_sp] = false;
|
| | | return &this;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | CJsonBuilderBase* CJsonBuilderBase::Arr()
|
| | | {
|
2026-07-14 09:22:38 -05:00 | | | //---
|
| | | JSONBUILDER_EXPAND_E(3)
|
| | | JSON_BUILDER_COMMA_F
|
| | | m_buf[m_pos++] = '[';
|
| | |
|
| | | //---
|
2026-07-13 18:47:16 -05:00 | | | if(m_sp < 63)
|
| | | {
|
| | | m_sp++;
|
| | | m_stack_first[m_sp] = true;
|
| | | }
|
| | | return &this;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | CJsonBuilderBase* CJsonBuilderBase::EndArr()
|
| | | {
|
| | | if(m_sp > 0)
|
| | | m_sp--;
|
| | | JSON_BUILDER_PUT(']')
|
| | | m_stack_first[m_sp] = false;
|
| | | return &this;
|
| | | }
|
| | |
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | CJsonBuilderBase* CJsonBuilderBase::Val(int v)
|
| | | {
|
2026-07-14 09:22:38 -05:00 | | | //---
|
| | | // 11 slots para int (10 digitos + signo) + 1 comma + 1 extra
|
| | | JSONBUILDER_EXPAND_E(13)
|
| | | JSON_BUILDER_COMMA_F
|
| | |
|
| | | //---
|
| | | if(v == 0)
|
| | | {
|
| | | m_buf[m_pos++] = '0';
|
| | | return &this;
|
| | | }
|
| | |
|
| | | //---
|
| | | bool negativo = (v < 0);
|
| | | uint uvalue;
|
| | | if(negativo)
|
| | | {
|
| | | m_buf[m_pos++] = '-';
|
| | | uvalue = (uint)(-(v + 1)) + 1; // evita overflow con INT_MIN
|
| | | }
|
| | | else
|
| | | {
|
| | | uvalue = (uint)v;
|
| | | }
|
| | |
|
| | | //---
|
| | | static uchar digits[12];
|
| | | int n = 0;
|
| | |
|
| | | //---
|
| | | while(uvalue >= 100)
|
| | | {
|
| | | const uint idx = (uvalue % 100U) << 1U;
|
| | | digits[n++] = g_tsn_digit_pairs[idx + 1];
|
| | | digits[n++] = g_tsn_digit_pairs[idx];
|
| | | uvalue /= 100U;
|
| | | }
|
| | | //---
|
| | | if(uvalue >= 10)
|
| | | {
|
| | | const uint idx = uvalue << 1U;
|
| | | digits[n++] = g_tsn_digit_pairs[idx + 1];
|
| | | digits[n++] = g_tsn_digit_pairs[idx];
|
| | | }
|
| | | else
|
| | | {
|
| | | digits[n++] = (uchar)('0' + uvalue);
|
| | | }
|
| | |
|
| | | //---
|
| | | for(int j = n - 1; j >= 0; j--)
|
| | | m_buf[m_pos++] = digits[j];
|
| | |
|
| | | //---
|
2026-07-13 18:47:16 -05:00 | | | return &this;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-07-14 09:22:38 -05:00 | | | CJsonBuilderBase* CJsonBuilderBase::Val(long value)
|
2026-07-13 18:47:16 -05:00 | | | {
|
2026-07-14 09:22:38 -05:00 | | | //---
|
| | | // 25 slots para long + 1 comma+ 1 extra
|
| | | JSONBUILDER_EXPAND_E(27)
|
| | | JSON_BUILDER_COMMA_F
|
| | |
|
| | | //---
|
| | | if(value == 0)
|
| | | {
|
| | | m_buf[m_pos++] = '0';
|
| | | return &this;
|
| | | }
|
| | |
|
| | | //--
|
| | | int pos = 0;
|
| | | ulong uvalue;
|
| | | if(value < 0)
|
| | | {
|
| | | m_buf[m_pos++] = '-';
|
| | | uvalue = ulong(-(value + 1)) + 1;
|
| | | }
|
| | | else
|
| | | {
|
| | | uvalue = ulong(value);
|
| | | }
|
| | |
|
| | | //---
|
| | | static uchar digits[24];
|
| | | int n = 0;
|
| | |
|
| | | //---
|
| | | while(uvalue >= 100)
|
| | | {
|
| | | const ulong idx = (uvalue % 100ULL) << 1ULL;
|
| | | digits[n++] = g_tsn_digit_pairs[idx + 1];
|
| | | digits[n++] = g_tsn_digit_pairs[idx];
|
| | | uvalue /= 100ULL;
|
| | | }
|
| | |
|
| | | //---
|
| | | if(uvalue >= 10)
|
| | | {
|
| | | const ulong idx = (uvalue << 1);
|
| | | digits[n++] = g_tsn_digit_pairs[idx + 1];
|
| | | digits[n++] = g_tsn_digit_pairs[idx];
|
| | | }
|
| | | else
|
| | | {
|
| | | digits[n++] = uchar('0' + uvalue);
|
| | | }
|
| | |
|
| | | //---
|
| | | for(int j = n - 1; j >= 0; j--)
|
| | | m_buf[m_pos++] = digits[j];
|
| | |
|
| | | //---
|
2026-07-13 18:47:16 -05:00 | | | return &this;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-07-14 09:22:38 -05:00 | | | CJsonBuilderBase* CJsonBuilderBase::Val(double v, int digits = 8)
|
2026-07-13 18:47:16 -05:00 | | | {
|
2026-07-14 09:22:38 -05:00 | | | //---
|
| | | JSONBUILDER_EXPAND_E(32)
|
| | | JSON_BUILDER_COMMA_F
|
| | |
|
| | | //---
|
| | | // JSON no soporta NaN/Infinity
|
| | | if(!MathIsValidNumber(v))
|
| | | {
|
| | | m_buf[m_pos++] = 'n';
|
| | | m_buf[m_pos++] = 'u';
|
| | | m_buf[m_pos++] = 'l';
|
| | | m_buf[m_pos++] = 'l';
|
| | | return &this;
|
| | | }
|
| | | //---
|
| | | if(v < 0)
|
| | | {
|
| | | m_buf[m_pos++] = '-';
|
| | | v = -v;
|
| | | }
|
| | |
|
| | | //---
|
| | | const double factor = g_Pow10[digits];
|
| | | const ulong escalado = (ulong)MathRound(v * factor);
|
| | | ulong ent = escalado / (ulong)factor;
|
| | | ulong dec = escalado % (ulong)factor;
|
| | |
|
| | |
|
| | | // --- parte entera, mismo algoritmo de 2-en-2 que ya usas ---
|
| | | if(ent == 0)
|
| | | {
|
| | | m_buf[m_pos++] = '0';
|
| | | }
|
| | | else
|
| | | {
|
| | | static uchar dig_buff[24];
|
| | | int n = 0;
|
| | |
|
| | | //---
|
| | | while(ent >= 100)
|
| | | {
|
| | | const ulong idx = (ent % 100ULL) << 1ULL;
|
| | | dig_buff[n++] = g_tsn_digit_pairs[idx + 1];
|
| | | dig_buff[n++] = g_tsn_digit_pairs[idx];
|
| | | ent /= 100ULL;
|
| | | }
|
| | | if(ent >= 10)
|
| | | {
|
| | | const ulong idx = ent << 1ULL;
|
| | | dig_buff[n++] = g_tsn_digit_pairs[idx + 1];
|
| | | dig_buff[n++] = g_tsn_digit_pairs[idx];
|
| | | }
|
| | | else
|
| | | {
|
| | | dig_buff[n++] = (uchar)('0' + ent);
|
| | | }
|
| | | for(int j = n - 1; j >= 0; j--)
|
| | | m_buf[m_pos++] = dig_buff[j];
|
| | | }
|
| | |
|
| | | //--- punto decimal ---
|
| | | m_buf[m_pos++] = '.';
|
| | |
|
| | | //--- parte decimal, con ceros a la izquierda (padding fijo a 'digits') ---
|
| | | static uchar dec_buff[24];
|
| | | int dn = 0;
|
| | |
|
| | | //---
|
| | | if(dec == 0)
|
| | | {
|
| | | dec_buff[dn++] = '0';
|
| | | }
|
| | | else
|
| | | {
|
| | | while(dec >= 100)
|
| | | {
|
| | | const ulong idx = (dec % 100ULL) << 1ULL;
|
| | | dec_buff[dn++] = g_tsn_digit_pairs[idx + 1];
|
| | | dec_buff[dn++] = g_tsn_digit_pairs[idx];
|
| | | dec /= 100ULL;
|
| | | }
|
| | | if(dec >= 10)
|
| | | {
|
| | | const ulong idx = dec << 1ULL;
|
| | | dec_buff[dn++] = g_tsn_digit_pairs[idx + 1];
|
| | | dec_buff[dn++] = g_tsn_digit_pairs[idx];
|
| | | }
|
| | | else
|
| | | {
|
| | | dec_buff[dn++] = (uchar)('0' + dec);
|
| | | }
|
| | | }
|
| | | //---
|
| | | // padding de ceros a la izquierda para completar 'digits' posiciones
|
| | | const int padding = digits - dn;
|
| | | for(int p = 0; p < padding; p++)
|
| | | m_buf[m_pos++] = '0';
|
| | | for(int j = dn - 1; j >= 0; j--)
|
| | | m_buf[m_pos++] = dec_buff[j];
|
| | |
|
| | | //---
|
2026-07-13 18:47:16 -05:00 | | | return &this;
|
| | | }
|
| | |
|
| | |
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | CJsonBuilderBase* CJsonBuilderBase::Val(bool v)
|
| | | {
|
2026-07-14 09:22:38 -05:00 | | | JSONBUILDER_EXPAND_E(7)
|
| | | JSON_BUILDER_COMMA_F
|
| | | if(v)
|
| | | {
|
| | | m_buf[m_pos++] = 't';
|
| | | m_buf[m_pos++] = 'r';
|
| | | m_buf[m_pos++] = 'u';
|
| | | m_buf[m_pos++] = 'e';
|
| | | }
|
| | | else
|
| | | {
|
| | | m_buf[m_pos++] = 'f';
|
| | | m_buf[m_pos++] = 'a';
|
| | | m_buf[m_pos++] = 'l';
|
| | | m_buf[m_pos++] = 's';
|
| | | m_buf[m_pos++] = 'e';
|
| | | }
|
2026-07-13 18:47:16 -05:00 | | | return &this;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | CJsonBuilderBase* CJsonBuilderBase::Null()
|
| | | {
|
2026-07-14 09:22:38 -05:00 | | | JSONBUILDER_EXPAND_E(6)
|
| | | JSON_BUILDER_COMMA_F
|
| | | m_buf[m_pos++] = 'n';
|
| | | m_buf[m_pos++] = 'u';
|
| | | m_buf[m_pos++] = 'l';
|
| | | m_buf[m_pos++] = 'l';
|
2026-07-13 18:47:16 -05:00 | | | return &this;
|
| | | }
|
| | |
|
| | | //--- end namespace
|
| | | }
|
| | |
|
| | | #endif // JSONPARSERBYLEO_SRC_BUILDERSDEFS_MQH
|