Você está perdendo oportunidades de negociação:
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Registro
Login
Você concorda com a política do site e com os termos de uso
Se você não tem uma conta, por favor registre-se
Fórum de negociação, sistemas de negociação automatizados e testes de estratégias de negociação
Bibliotecas: JSON Library for LLMs
fxsaber, 2026.02.19 21:56
I assume that in MQL5, due to the forced check for array out-of-bounds, this condition will be executed slower than the following one.
Man, these are fantastic suggestions. I really appreciate you taking the time to dig into the source code and point out where we could squeeze more performance.
You were absolutely right about the array bounds check overhead in MQL5. Even though g_cc is fast, the compiler's safety checks add up in a tight loop. I've scrapped the table lookup for digits and implemented your bitwise ALU check (c ^ '0') <= 9 . It’s cleaner and definitely faster.
I also took your advice on the number parsing and rewrote it to be Single-Pass. Now it consumes digits directly into the accumulator and only switches to float logic if it hits a decimal point or exponent. No more double-scanning.
Plus, I reordered the main loop branches to prioritize Strings ( " ) and Numbers, which should help with CPU branch prediction since those are the most common tokens.
Thanks again for the push. The library is significantly better because of your inputs!
🔗 v3.5.0 is live: GitHub/Forge
Olá, Jônatas,
Obrigado pela excelente biblioteca.
Ao integrá-la com os fluxos Binance WebSocket, encontrei dois erros. Ambos se reproduzem na versão pública atual. Script de teste e saída no final da postagem.
(Ao integrá-la aos fluxos do Binance WebSocket, encontrei dois erros).
Bug nº 1 - true e false produzem entradas de fita idênticas
(Bug nº 1 - true e false produzem entradas de fita idênticas.)
No analisador, os ramos dos literais 't' e 'f' escrevem o mesmo bit de carga útil:
(No analisador, os ramos para os literais 't' e 'f' escrevem o mesmo bit de carga útil:)
GetBool() já lê o bit 0 corretamente ( tape[idx] & 1 ), portanto, isso é apenas um erro de digitação de um caractere no analisador:
(GetBool() já lê o bit 0 corretamente, portanto, isso é apenas um erro de digitação de um caractere no analisador:)
Após essa correção, ToBool() retorna o valor correto tanto para true quanto para false .
(Após essa correção, ToBool() retorna o valor correto tanto para true quanto para false).
Bug #2 - ToString() lê slots de fita adjacentes em nós que não são de string
(Bug #2 - ToString() lê slots de fita adjacentes em nós que não são strings)
CJsonNode::ToString() não tem verificação de tipo:
(CJsonNode::ToString() não tem verificação de tipo:).
GetStr() interpreta cegamente tape[idx + 1] como um par compactado (offset, length):
(GetStr() interpreta cegamente tape[idx + 1] como um par (offset, length):)
Para nós que não sejam de string, isso lê um slot de fita que não pertence ao nó atual. A manifestação mais visível ocorre em nós inteiros, em que tape[idx + 1] contém o próprio valor inteiro. Para {"v":42}, chamar r["v"].ToString() retorna todo o buffer de entrada (8 bytes), porque a carga útil 42 é decodificada como (offset=0, length=8).
Com números inteiros maiores ou deslocamentos diferentes, o mesmo caminho pode retornar um conteúdo de memória arbitrário do buffer de entrada, possivelmente fora dos limites.
Correção sugerida - corresponder à convenção usada por ToInt() :
(Para nós que não sejam strings, isso lê um slot de fita que não pertence ao nó atual. A manifestação mais perceptível ocorre em nós inteiros, onde tape[idx + 1] armazena o próprio valor inteiro. Para {"v":42}, chamar r["v"].ToString() retorna todo o buffer de entrada (8 bytes) - porque a carga útil 42 é decodificada como (offset=0, length=8). Com números inteiros maiores ou outros deslocamentos, o mesmo caminho pode retornar conteúdos arbitrários do buffer de entrada, possivelmente fora dele. A correção sugerida é seguir a convenção usada em ToInt().
Se o mesmo caminho retornar números inteiros ou outros deslocamentos, ele poderá retornar conteúdos arbitrários da memória do buffer de entrada, possivelmente fora dos limites.
Correção sugerida - siga a convenção usada por ToInt() :
Ou uma versão mais avançada que retorne uma representação de string de qualquer tipo de escalar (depende de o Bug nº 1 ser corrigido primeiro):
Ou uma versão mais avançada que retorne uma representação de string de qualquer tipo escalar (depende de o Bug nº 1 ser corrigido primeiro):
Reprodução
Saída na versão atual:
Observação Casos D e E: ToString() em um nó com valor int retornou todo o buffer de entrada bruto, porque a carga útil do int foi decodificada como offset=0, length=(tamanho do buffer) . O comprimento escala linearmente com a entrada - confirmando uma leitura fora do nó, não apenas um artefato de memória obsoleta.
(Observação sobre os casos D e E: ToString() no nó com int retornou todo o buffer de entrada original porque o payload 42 foi decodificado como (offset=0, length=buffer size). O comprimento cresce linearmente com o tamanho da entrada - isso confirma a leitura fora do nó, não apenas um artefato de memória).
Terei prazer em testar um patch se você quiser validar a correção antes de publicá-la.
Obrigado pelo trabalho nessa biblioteca!
Bug #2 - ToString() lê slots de fita adjacentes em nós que não são de string
CJsonNode::ToString() não tem verificação de tipo:
Bug #2 - ToString() lê slots de fita adjacentes em nós que não são strings
CJsonNode::ToString() não tem verificação de tipo:
GetStr() interpreta cegamente tape[idx + 1] como um par empacotado (offset, length):
GetStr() interpreta cegamente tape[idx + 1] como um par (offset, length):
Para nós que não são de string, isso lê um slot de fita que não pertence ao nó atual. A manifestação mais visível ocorre em nós inteiros, em que tape[idx + 1] contém o próprio valor inteiro. Para {"v":42}, chamar r["v"].ToString() retorna todo o buffer de entrada (8 bytes), porque a carga útil 42 é decodificada como (offset=0, length=8).
Com números inteiros maiores ou deslocamentos diferentes, o mesmo caminho pode retornar um conteúdo de memória arbitrário do buffer de entrada, possivelmente fora dos limites.
Correção sugerida - corresponder à convenção usada por ToInt() :
Para nós que não sejam strings, isso lê um slot de fita que não pertence ao nó atual. A manifestação mais perceptível ocorre em nós inteiros, onde tape[idx + 1] armazena o próprio valor inteiro. Para {"v":42}, chamar r["v"].ToString() retorna todo o buffer de entrada (8 bytes) - porque a carga útil 42 é decodificada como (offset=0, length=8). Com números inteiros maiores ou outros deslocamentos, o mesmo caminho pode retornar conteúdos arbitrários do buffer de entrada, possivelmente fora dele. A correção sugerida é seguir a convenção usada em ToInt().
Ou uma versão mais avançada que retorne uma representação de string de qualquer tipo escalar (depende de o Bug nº 1 ser corrigido primeiro):
Ou uma versão mais avançada que retorna uma representação de string de qualquer tipo escalar (depende de o Bug nº 1 ser corrigido primeiro):
Reprodução
Saída na versão atual:
Observação Casos D e E: ToString() em um nó com valor int retornou todo o buffer de entrada bruto, porque a carga útil do int foi decodificada como offset=0, length=(tamanho do buffer) . O comprimento é escalonado linearmente com a entrada - confirmando uma leitura fora do nó, não apenas um artefato de memória obsoleta.
(Observação sobre os casos D e E: ToString() no nó com int retornou todo o buffer de entrada original porque o payload 42 foi decodificado como (offset=0, length=buffer size). O comprimento cresce linearmente com o tamanho da entrada - isso confirma a leitura fora do nó, não apenas um artefato de memória).
Terei prazer em testar um patch se você quiser validar a correção antes de publicá-la.
Obrigado pelo trabalho nessa biblioteca!
Thank you all for the contributions.
@fxsaber — Analyzed both files. The XOR-first pattern applied consistently across the number parsing pipeline (each byte XOR'd exactly once, all comparisons in the transformed domain) was incorporated in v3.6.0 and refined in v3.7.0. Surgical contribution, as always. Thank you.
@Sunriser — Both bugs confirmed and fixed in v3.7.0:
v3.7.0 also includes: ParseBuffer(uchar &data[], int data_len) for direct buffer parsing without StringToCharArray , SWAR Unescape, key length stored in tape (eliminates scan-for-quote during serialization), direct byte writes in the serializer, ArrayResize with reserve, and HexToDec via lookup table.
Backward compatible. Zero breaking changes.
Note: v3.6.0 (with extended Pow10 tables, digit-pair serialization and 4x unrolled hash) was ready a while ago — I just forgot to post it. 😅
Thanks for the feedback — the library evolves thanks to contributions like these.