//+------------------------------------------------------------------+ //| 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" #include //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #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; //--- #define JSON_BUILDER_COMMA_F \ if(!m_stack_first[m_sp]) \ { \ m_buf[m_pos++] = ','; \ } \ m_stack_first[m_sp] = false; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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 CJsonBuilderBase* Obj(); CJsonBuilderBase* EndObj(); CJsonBuilderBase* Arr(); CJsonBuilderBase* EndArr(); // Valor basico CJsonBuilderBase* Val(int v); CJsonBuilderBase* Val(long value); CJsonBuilderBase* Val(double v, int digits = 8); CJsonBuilderBase* Val(bool v); // Null CJsonBuilderBase* Null(); //--- void CheckCap(); void CheckComma(); //--- Put raw void PutChar(const uchar c); void PutRawNoRef(const string s); void PutRaw(const string& s); void PutRaw(const uchar& s[], int count = WHOLE_ARRAY); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CJsonBuilderBase::CheckComma(void) { JSON_BUILDER_COMMA } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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() { JSONBUILDER_EXPAND_E(3) JSON_BUILDER_COMMA_F m_buf[m_pos++] = '{'; //--- if(m_sp < 63) { m_sp++; m_stack_first[m_sp] = true; } return &this; } //+------------------------------------------------------------------+ CJsonBuilderBase* CJsonBuilderBase::EndObj() { if(m_sp > 0) m_sp--; //--- JSON_BUILDER_PUT('}') m_stack_first[m_sp] = false; return &this; } //+------------------------------------------------------------------+ CJsonBuilderBase* CJsonBuilderBase::Arr() { //--- JSONBUILDER_EXPAND_E(3) JSON_BUILDER_COMMA_F m_buf[m_pos++] = '['; //--- 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) { //--- // 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]; //--- return &this; } //+------------------------------------------------------------------+ CJsonBuilderBase* CJsonBuilderBase::Val(long value) { //--- // 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]; //--- return &this; } //+------------------------------------------------------------------+ CJsonBuilderBase* CJsonBuilderBase::Val(double v, int digits = 8) { //--- 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]; //--- return &this; } //+------------------------------------------------------------------+ CJsonBuilderBase* CJsonBuilderBase::Val(bool v) { 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'; } return &this; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CJsonBuilderBase* CJsonBuilderBase::Null() { 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'; return &this; } //--- end namespace } #endif // JSONPARSERBYLEO_SRC_BUILDERSDEFS_MQH