2026-08-24 16:40:07 -05:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Node.mqh |
|
|
|
|
|
//| Copyright 2026, Niquel Mendoza |
|
|
|
|
|
//| https://www.mql5.com |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#property copyright "Copyright 2026, Niquel Mendoza"
|
|
|
|
|
#property link "https://www.mql5.com"
|
|
|
|
|
#property strict
|
|
|
|
|
|
|
|
|
|
#ifndef CRYPTOBYLEO_RSPPARSER_NODE_MQH
|
|
|
|
|
#define CRYPTOBYLEO_RSPPARSER_NODE_MQH
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#include "RSP.mqh"
|
2026-08-25 08:02:51 -05:00
|
|
|
#include <TSN/MQLArticles/Utils/StructBytesCre.mqh>
|
2026-08-24 16:40:07 -05:00
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
namespace TSN
|
|
|
|
|
{
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-08-24 21:32:18 -05:00
|
|
|
#define RSPDATA_TRIPLET_KEYVALUEMETA (3)
|
|
|
|
|
#define RSPDATA_IS_VALID (m_ctx!=NULL)
|
|
|
|
|
#define RSPDATA_IS_NOT_VALID (m_ctx==NULL)
|
|
|
|
|
#define RSPDATA_IS_VALID_F (RSPDATA_IS_VALID && m_idx!=-1)
|
|
|
|
|
#define RSPDATA_IS_VAL_TO_REVISE (((m_ctx.m_cinta[m_idx-1] >> RSPDATA_BITSTART_ISVAL_CLEAN) & RSPDATA_MASK_META_IS) == 0)
|
2026-08-24 16:40:07 -05:00
|
|
|
|
2026-08-25 08:02:51 -05:00
|
|
|
//---
|
|
|
|
|
#define RSPNODE_NH(NH) TSN::MQLARTICLES_STRUCT_BYTES_##NH
|
|
|
|
|
|
|
|
|
|
|
2026-08-24 21:55:43 -05:00
|
|
|
//--- forward
|
|
|
|
|
class CRSPIteradorBase;
|
|
|
|
|
|
2026-08-25 08:02:51 -05:00
|
|
|
template <typename TNumHashes, typename TBaseHash>
|
|
|
|
|
class CRSPIteratorHash;
|
|
|
|
|
|
2026-08-24 22:42:17 -05:00
|
|
|
//---
|
|
|
|
|
#define RSPNODE_CORRECT_VAL \
|
|
|
|
|
if(RSPDATA_IS_VAL_TO_REVISE)\
|
|
|
|
|
{\
|
|
|
|
|
int i = (start + slen) - 1;\
|
|
|
|
|
while(i >= start && m_ctx.m_raw[i] < 33)\
|
|
|
|
|
{\
|
|
|
|
|
i--;\
|
|
|
|
|
}\
|
|
|
|
|
slen = (i - start) + 1;\
|
|
|
|
|
m_ctx.m_cinta[m_idx] = long(RSPDATA_TYPE_PLAIN_VALUE)\
|
|
|
|
|
| long(slen) << TSN_SBL_BIT_STR_LEN\
|
|
|
|
|
| long(start) << TSN_SBL_BIT_STR_START;\
|
|
|
|
|
m_ctx.m_cinta[m_idx - 1] |= RSPDATA_BITSTART_ISVAL_CLEAN_F;\
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-25 08:02:51 -05:00
|
|
|
//---
|
|
|
|
|
|
2026-08-24 16:40:07 -05:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-08-24 21:32:18 -05:00
|
|
|
struct CRSPNode
|
2026-08-24 16:40:07 -05:00
|
|
|
{
|
|
|
|
|
CRSPParser* m_ctx;
|
|
|
|
|
int m_idx;
|
|
|
|
|
|
2026-08-24 21:32:18 -05:00
|
|
|
//----
|
2026-08-24 22:33:45 -05:00
|
|
|
CRSPNode(const int _idx, CRSPParser* _ctx) : m_idx(_idx), m_ctx(_ctx) {}
|
2026-08-24 21:32:18 -05:00
|
|
|
CRSPNode() : m_idx(-1), m_ctx(NULL) {}
|
|
|
|
|
|
2026-08-24 22:33:45 -05:00
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
__forceinline int RootSize() const { return m_ctx.m_root_values; }
|
|
|
|
|
|
2026-08-24 16:40:07 -05:00
|
|
|
//---
|
|
|
|
|
// Navega en el root
|
2026-08-24 21:32:18 -05:00
|
|
|
CRSPNode operator[](const string& key);
|
|
|
|
|
CRSPNode At(const int index) const;
|
|
|
|
|
string AtKey(const int index);
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
bool HasKey(const string& key);
|
2026-08-24 16:40:07 -05:00
|
|
|
|
|
|
|
|
//---
|
2026-08-24 21:32:18 -05:00
|
|
|
__forceinline ENUM_RSPDATA_TYPE GetType() const { return ENUM_RSPDATA_TYPE(m_ctx.m_cinta[m_idx] & TSN_SBL_BIT_MASK_TYPE); }
|
|
|
|
|
__forceinline uint GetFlag() const { return 1 << uint(m_ctx.m_cinta[m_idx] & TSN_SBL_BIT_MASK_TYPE); }
|
2026-08-24 16:40:07 -05:00
|
|
|
|
|
|
|
|
//--- Value
|
2026-08-24 21:32:18 -05:00
|
|
|
CRSPNode Value() const;
|
|
|
|
|
|
2026-08-24 16:40:07 -05:00
|
|
|
//-- to str
|
|
|
|
|
string ToString(const string def = "") const;
|
|
|
|
|
int ToStringUArray(uchar& data[]) const;
|
|
|
|
|
int StrLenRaw() const;
|
|
|
|
|
__forceinline bool CompareStrWith(const string& str) const;
|
|
|
|
|
|
2026-08-24 21:32:18 -05:00
|
|
|
//-- to bytes (asume hex string)
|
2026-08-25 09:19:11 -05:00
|
|
|
int HexToBytes(uchar& buf[]) const;
|
2026-08-24 16:40:07 -05:00
|
|
|
|
|
|
|
|
//--- Is
|
2026-08-24 21:32:18 -05:00
|
|
|
__forceinline bool IsValid() const { return RSPDATA_IS_VALID_F; }
|
|
|
|
|
__forceinline bool IsInvalid() const { return !RSPDATA_IS_VALID_F; }
|
2026-08-24 16:40:07 -05:00
|
|
|
|
|
|
|
|
//--- Meta
|
2026-08-24 21:32:18 -05:00
|
|
|
__forceinline bool MetaIsCor() const { return ((m_ctx.m_cinta[m_idx] >> TSN_SBL_BIT_START_ENDTYPE) & RSPDATA_MASK_META_IS) != 0; }
|
|
|
|
|
__forceinline bool MetaCleanedKey() const { return ((m_ctx.m_cinta[m_idx] >> RSPDATA_BITSTART_ISKEY_CLEAN) & RSPDATA_MASK_META_IS) != 0; }
|
|
|
|
|
__forceinline bool MetaCleanedValue() const { return ((m_ctx.m_cinta[m_idx] >> RSPDATA_BITSTART_ISVAL_CLEAN) & RSPDATA_MASK_META_IS) != 0; }
|
2026-08-24 16:40:07 -05:00
|
|
|
|
|
|
|
|
//---
|
2026-08-25 08:02:51 -05:00
|
|
|
template <typename TNumHashes, typename THashBase>
|
|
|
|
|
TSN::CRSPIteratorHash<TNumHashes, THashBase> MakeHashIterator(const string& keys[]) const
|
|
|
|
|
{
|
|
|
|
|
if(RSPDATA_IS_NOT_VALID)
|
|
|
|
|
return CRSPIteratorHash<TNumHashes, THashBase>::EMPTY;
|
|
|
|
|
return CRSPIteratorHash<TNumHashes, THashBase>(keys, m_ctx, m_idx);
|
|
|
|
|
}
|
2026-08-24 21:32:18 -05:00
|
|
|
|
2026-08-24 21:55:43 -05:00
|
|
|
//---
|
|
|
|
|
template <typename THashBase>
|
|
|
|
|
CRSPIteradorBase* MakeInteligentIterador(const string& keys[]) const;
|
|
|
|
|
|
2026-08-24 21:32:18 -05:00
|
|
|
//---
|
|
|
|
|
static const CRSPNode EMPTY_NODE;
|
2026-08-25 08:02:51 -05:00
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
bool MoveNextPositionsThis(const int posiciones);
|
|
|
|
|
CRSPNode MoveNextPositions(const int posiciones) const;
|
2026-08-25 09:19:11 -05:00
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
static bool BytesEq(const uchar& a[], const uchar& b[]);
|
2026-08-24 16:40:07 -05:00
|
|
|
};
|
|
|
|
|
|
2026-08-24 21:32:18 -05:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
const CRSPNode CRSPNode::EMPTY_NODE;
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
CRSPNode CRSPNode::operator[](const string& key)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
if(RSPDATA_IS_NOT_VALID)
|
|
|
|
|
return CRSPNode::EMPTY_NODE;
|
|
|
|
|
//---
|
2026-08-24 22:33:45 -05:00
|
|
|
int cur = 0;
|
2026-08-24 21:32:18 -05:00
|
|
|
while(cur < m_ctx.m_cinta_pos)
|
|
|
|
|
{
|
|
|
|
|
if(m_ctx.KeysEqual(key, cur)) // + 1 para estar en META
|
|
|
|
|
return CRSPNode(cur + 1, m_ctx);
|
|
|
|
|
cur += RSPDATA_TRIPLET_KEYVALUEMETA;
|
|
|
|
|
}
|
|
|
|
|
return CRSPNode::EMPTY_NODE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CRSPNode::HasKey(const string &key)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
if(RSPDATA_IS_NOT_VALID)
|
|
|
|
|
return false;
|
|
|
|
|
//---
|
2026-08-24 22:33:45 -05:00
|
|
|
int cur = 0;
|
2026-08-24 21:32:18 -05:00
|
|
|
while(cur < m_ctx.m_cinta_pos)
|
|
|
|
|
{
|
|
|
|
|
if(m_ctx.KeysEqual(key, cur)) // + 1 para estar en META
|
|
|
|
|
return true;
|
|
|
|
|
cur += RSPDATA_TRIPLET_KEYVALUEMETA;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
CRSPNode CRSPNode::At(const int index) const
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
if(RSPDATA_IS_NOT_VALID || index < 0 || index >= m_ctx.m_cinta_pos)
|
|
|
|
|
return CRSPNode::EMPTY_NODE;
|
|
|
|
|
//--- devuelve META
|
|
|
|
|
return CRSPNode((index * RSPDATA_TRIPLET_KEYVALUEMETA) + 1, m_ctx);
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
string CRSPNode::AtKey(const int index)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
if(RSPDATA_IS_NOT_VALID || index < 0 || index >= m_ctx.m_cinta_pos)
|
|
|
|
|
return "";
|
|
|
|
|
//--- devuelve string
|
|
|
|
|
const int base = (index * RSPDATA_TRIPLET_KEYVALUEMETA);
|
|
|
|
|
//---
|
|
|
|
|
const int start = int(m_ctx.m_cinta[base] >> TSN_SBL_BIT_STR_START);
|
|
|
|
|
int slen = int((m_ctx.m_cinta[base] >> TSN_SBL_BIT_START_ENDTYPE) & TSN_SBL_BIT_STR_MASK_LEN);
|
|
|
|
|
//---
|
|
|
|
|
m_ctx.VerifyCleanKey(base, start, slen);
|
|
|
|
|
//---
|
|
|
|
|
return CharArrayToString(m_ctx.m_raw, start, slen);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
CRSPNode CRSPNode::Value(void) const
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
if(RSPDATA_IS_NOT_VALID)
|
|
|
|
|
return CRSPNode::EMPTY_NODE;
|
|
|
|
|
// si estamos en META +1 = value
|
|
|
|
|
return CRSPNode(m_idx + 1, m_ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
string CRSPNode::ToString(const string def = "") const
|
|
|
|
|
{
|
|
|
|
|
if(RSPDATA_IS_NOT_VALID || (m_ctx.m_cinta[m_idx]&TSN_SBL_BIT_MASK_TYPE) != RSPDATA_TYPE_PLAIN_VALUE)
|
|
|
|
|
return def;
|
|
|
|
|
// ahora si en value
|
|
|
|
|
// tenemos que recortar por la parte alta
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
const int start = int(m_ctx.m_cinta[m_idx] >> TSN_SBL_BIT_STR_START);
|
|
|
|
|
int slen = int((m_ctx.m_cinta[m_idx] >> TSN_SBL_BIT_START_ENDTYPE) & TSN_SBL_BIT_STR_MASK_LEN);
|
|
|
|
|
//---
|
2026-08-24 22:42:17 -05:00
|
|
|
RSPNODE_CORRECT_VAL
|
2026-08-24 21:32:18 -05:00
|
|
|
//---
|
|
|
|
|
return CharArrayToString(m_ctx.m_raw, start, slen);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
int CRSPNode::ToStringUArray(uchar &data[]) const
|
|
|
|
|
{
|
|
|
|
|
if(RSPDATA_IS_NOT_VALID || (m_ctx.m_cinta[m_idx]&TSN_SBL_BIT_MASK_TYPE) != RSPDATA_TYPE_PLAIN_VALUE)
|
|
|
|
|
return 0;
|
|
|
|
|
// ahora si en value
|
|
|
|
|
// tenemos que recortar por la parte alta
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
const int start = int(m_ctx.m_cinta[m_idx] >> TSN_SBL_BIT_STR_START);
|
|
|
|
|
int slen = int((m_ctx.m_cinta[m_idx] >> TSN_SBL_BIT_START_ENDTYPE) & TSN_SBL_BIT_STR_MASK_LEN);
|
|
|
|
|
|
|
|
|
|
//---
|
2026-08-24 22:42:17 -05:00
|
|
|
RSPNODE_CORRECT_VAL
|
2026-08-24 21:32:18 -05:00
|
|
|
//---
|
|
|
|
|
return ArrayCopy(data, m_ctx.m_raw, 0, start, slen);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
// interpretna un mensaje multiplo de 2 len como una cadena de bytes LE
|
|
|
|
|
// Si no hay caracters NO HEX se intemapn como FF (invalidos) nota eso
|
|
|
|
|
// osea que
|
|
|
|
|
// "kkkk" da buf[] = {0xff, 0xff}
|
2026-08-25 09:19:11 -05:00
|
|
|
int CRSPNode::HexToBytes(uchar &buf[]) const
|
2026-08-24 21:32:18 -05:00
|
|
|
{
|
|
|
|
|
if(RSPDATA_IS_NOT_VALID || (m_ctx.m_cinta[m_idx]&TSN_SBL_BIT_MASK_TYPE) != RSPDATA_TYPE_PLAIN_VALUE)
|
|
|
|
|
return 0;
|
|
|
|
|
// ahora si en value
|
|
|
|
|
// tenemos que recortar por la parte alta
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
const int start = int(m_ctx.m_cinta[m_idx] >> TSN_SBL_BIT_STR_START);
|
|
|
|
|
int slen = int((m_ctx.m_cinta[m_idx] >> TSN_SBL_BIT_START_ENDTYPE) & TSN_SBL_BIT_STR_MASK_LEN);
|
|
|
|
|
|
|
|
|
|
//---
|
2026-08-24 22:42:17 -05:00
|
|
|
RSPNODE_CORRECT_VAL
|
2026-08-24 21:32:18 -05:00
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
const int sob = slen & 1; // % 2 (vemos si hay sobrante o si es multiplo de dos exacto)
|
2026-08-26 08:21:19 -05:00
|
|
|
int exact_len = slen - sob;
|
2026-08-24 21:32:18 -05:00
|
|
|
ArrayResize(buf, (slen >> 1) + (sob << 1));
|
|
|
|
|
int w = 0;
|
|
|
|
|
|
|
|
|
|
//---
|
2026-08-26 08:21:19 -05:00
|
|
|
exact_len += start; // para que peuda empzar bien..
|
2026-08-24 21:32:18 -05:00
|
|
|
int i = start;
|
2026-08-26 08:21:19 -05:00
|
|
|
//---
|
2026-08-24 21:32:18 -05:00
|
|
|
for(i; i < exact_len; i += 2)
|
|
|
|
|
buf[w++] = g_arr_valid_hex_chars[m_ctx.m_raw[i]] | g_arr_valid_hex_chars[m_ctx.m_raw[i + 1]] << 4;
|
|
|
|
|
if(sob) // solo rellenmaso alto (la parte alta la inteprntamos como 0)
|
|
|
|
|
buf[w++] = m_ctx.m_raw[i];
|
|
|
|
|
//---
|
|
|
|
|
return w;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
int CRSPNode::StrLenRaw(void) const
|
|
|
|
|
{
|
|
|
|
|
if(RSPDATA_IS_NOT_VALID || (m_ctx.m_cinta[m_idx]&TSN_SBL_BIT_MASK_TYPE) != RSPDATA_TYPE_PLAIN_VALUE)
|
|
|
|
|
return 0;
|
|
|
|
|
return int((m_ctx.m_cinta[m_idx] >> TSN_SBL_BIT_START_ENDTYPE) & TSN_SBL_BIT_STR_MASK_LEN);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CRSPNode::CompareStrWith(const string &str) const
|
|
|
|
|
{
|
|
|
|
|
if(RSPDATA_IS_NOT_VALID || (m_ctx.m_cinta[m_idx]&TSN_SBL_BIT_MASK_TYPE) != RSPDATA_TYPE_PLAIN_VALUE)
|
|
|
|
|
return false;
|
|
|
|
|
return m_ctx.StrEquals(str, m_idx);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-25 08:02:51 -05:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CRSPNode::MoveNextPositionsThis(const int posiciones)
|
|
|
|
|
{
|
|
|
|
|
if(RSPDATA_IS_NOT_VALID)
|
|
|
|
|
return false;
|
|
|
|
|
const int next_pos = m_idx + posiciones;
|
|
|
|
|
if(next_pos >= m_ctx.m_cinta_pos)
|
|
|
|
|
return false;
|
|
|
|
|
m_idx = next_pos;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
CRSPNode CRSPNode::MoveNextPositions(const int posiciones) const
|
|
|
|
|
{
|
|
|
|
|
if(RSPDATA_IS_NOT_VALID)
|
|
|
|
|
return CRSPNode::EMPTY_NODE;
|
|
|
|
|
const int next_pos = m_idx + posiciones;
|
|
|
|
|
if(next_pos >= m_ctx.m_cinta_pos)
|
|
|
|
|
return CRSPNode::EMPTY_NODE;
|
|
|
|
|
return CRSPNode(next_pos, m_ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-08-25 09:19:11 -05:00
|
|
|
static bool CRSPNode::BytesEq(const uchar &a[], const uchar &b[])
|
|
|
|
|
{
|
|
|
|
|
const int ta = ArraySize(a);
|
|
|
|
|
if(ta != ArraySize(b))
|
|
|
|
|
return false;
|
|
|
|
|
for(int i = 0; i < ta; i++)
|
|
|
|
|
{
|
|
|
|
|
if(a[i] != b[i])
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2026-08-24 16:40:07 -05:00
|
|
|
|
2026-08-24 22:33:45 -05:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-08-24 22:42:17 -05:00
|
|
|
CRSPNode CRSPParser::GetRoot(void)
|
2026-08-24 22:33:45 -05:00
|
|
|
{
|
|
|
|
|
if(m_cinta_pos == 0)
|
|
|
|
|
return CRSPNode::EMPTY_NODE;
|
|
|
|
|
return CRSPNode(0, &this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2026-08-25 08:02:51 -05:00
|
|
|
|
2026-08-24 16:40:07 -05:00
|
|
|
}
|
|
|
|
|
#endif // CRYPTOBYLEO_RSPPARSER_NODE_MQH
|