1981 lines
48 KiB
MQL5
1981 lines
48 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Qwen3.8Max.mqh |
|
|
//| Copyright 2026, Niquel Mendoza. |
|
|
//| https://www.mql5.com/ |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2026, Niquel Mendoza."
|
|
#property link "https://www.mql5.com/"
|
|
#property strict
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
// Same prompt claude\mql5 lite
|
|
/*
|
|
1. ITERACION
|
|
Tengo errores:
|
|
wrong parameters count Qwen3.8Max.mqh 1753 17
|
|
built-in: string DoubleToString(double,int) Qwen3.8Max.mqh 1753 17
|
|
implicit conversion from 'unknown' to 'string' Qwen3.8Max.mqh 1753 17
|
|
wrong parameters count Qwen3.8Max.mqh 1784 13
|
|
built-in: double StringToDouble(const string) Qwen3.8Max.mqh 1784 13
|
|
cannot convert parameter 'CJson&' to 'CJson*' Qwen3.8Max.mqh 1825 22
|
|
wrong parameters count, 2 passed, but 0 requires Qwen3.8Max.mqh 1825 14
|
|
could be one of 3 function(s) Qwen3.8Max.mqh 1825 14
|
|
implicit JsonRef::JsonRef(const JsonRef&) Qwen3.8Max.mqh 92 8
|
|
JsonRef::JsonRef(CJson*,int) Qwen3.8Max.mqh 104 4
|
|
JsonRef::JsonRef() Qwen3.8Max.mqh 98 4
|
|
cannot convert parameter 'CJson&' to 'CJson*' Qwen3.8Max.mqh 1830 22
|
|
wrong parameters count, 2 passed, but 0 requires Qwen3.8Max.mqh 1830 14
|
|
could be one of 3 function(s) Qwen3.8Max.mqh 1830 14
|
|
implicit JsonRef::JsonRef(const JsonRef&) Qwen3.8Max.mqh 92 8
|
|
JsonRef::JsonRef(CJson*,int) Qwen3.8Max.mqh 104 4
|
|
JsonRef::JsonRef() Qwen3.8Max.mqh 98 4
|
|
6 errors, 1 warnings 6 1
|
|
|
|
Los corregir todos yo ire dejando comentarios..
|
|
|
|
2. Iteracion
|
|
Al probarlo da error ni siuieqera funciona
|
|
2026.07.27 19:52:46.618 Qwen3.8Max (EURUSD,M1) false
|
|
2026.07.27 19:52:46.618 Qwen3.8Max (EURUSD,M1) array out of range in 'Qwen3.8Max.mqh' (1695,40)
|
|
|
|
Bueno lo tengo
|
|
2026.07.27 20:05:30.350 Qwen3.8Max (EURUSD,M1) false
|
|
2026.07.27 20:05:30.350 Qwen3.8Max (EURUSD,M1) JSON error: Invalid escape sequence pos=220961 line=6031 col=28
|
|
|
|
|
|
BUeno conitnuare otro dia con qwen me canse ya con glm.. hoy... ..
|
|
|
|
*/
|
|
|
|
//// Code ai start..
|
|
//+------------------------------------------------------------------+
|
|
//| High Performance JSON Parser for MQL5 |
|
|
//| Pure MQL5, no DLL, no external libraries |
|
|
//+------------------------------------------------------------------+
|
|
#ifndef JSON_MQH
|
|
#define JSON_MQH
|
|
|
|
#define JSON_CP_UTF8 65001
|
|
#define JSON_FNV_INIT ((uint)0x811C9DC5)
|
|
#define JSON_FNV_PRIME ((uint)16777619)
|
|
#define JSON_FLAG_KEY_DECODED 1
|
|
#define JSON_FLAG_VAL_DECODED 2
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Error codes |
|
|
//+------------------------------------------------------------------+
|
|
enum ENUM_JSON_ERROR
|
|
{
|
|
JSON_OK = 0,
|
|
JSON_UNKNOWN_ERROR,
|
|
JSON_UNEXPECTED_CHARACTER,
|
|
JSON_UNEXPECTED_END,
|
|
JSON_INVALID_NUMBER,
|
|
JSON_INVALID_STRING,
|
|
JSON_INVALID_ESCAPE,
|
|
JSON_INVALID_UNICODE,
|
|
JSON_EXPECTED_COLON,
|
|
JSON_EXPECTED_COMMA,
|
|
JSON_EXPECTED_OBJECT,
|
|
JSON_EXPECTED_ARRAY,
|
|
JSON_EXPECTED_KEY,
|
|
JSON_EXPECTED_VALUE,
|
|
JSON_INVALID_LITERAL,
|
|
JSON_MAX_DEPTH,
|
|
JSON_MEMORY_ERROR,
|
|
JSON_TRAILING_DATA
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Node types |
|
|
//+------------------------------------------------------------------+
|
|
enum ENUM_JSON_TYPE
|
|
{
|
|
JSON_NONE = -1,
|
|
JSON_NULL = 0,
|
|
JSON_BOOL = 1,
|
|
JSON_NUMBER = 2,
|
|
JSON_STRING = 3,
|
|
JSON_ARRAY = 4,
|
|
JSON_OBJECT = 5
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Parser states |
|
|
//+------------------------------------------------------------------+
|
|
enum ENUM_JSON_STATE
|
|
{
|
|
JSON_STATE_OBJ_KEY_OR_END = 0,
|
|
JSON_STATE_OBJ_KEY = 1,
|
|
JSON_STATE_OBJ_COLON = 2,
|
|
JSON_STATE_OBJ_VALUE = 3,
|
|
JSON_STATE_OBJ_COMMA_OR_END = 4,
|
|
JSON_STATE_ARRAY_VALUE_OR_END = 5,
|
|
JSON_STATE_ARRAY_VALUE = 6,
|
|
JSON_STATE_ARRAY_COMMA_OR_END = 7
|
|
};
|
|
|
|
class CJson;
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Lightweight reference to a JSON node |
|
|
//+------------------------------------------------------------------+
|
|
struct JsonRef
|
|
{
|
|
public:
|
|
CJson *json;
|
|
int node;
|
|
|
|
JsonRef()
|
|
{
|
|
json = NULL;
|
|
node = -1;
|
|
}
|
|
|
|
JsonRef(CJson *j, int n)
|
|
{
|
|
json = j;
|
|
node = n;
|
|
}
|
|
|
|
JsonRef operator[](const string key); // quite el & por "implicit conversion from 'string' to 'int' Qwen3.8Max.mq5 28 23"
|
|
JsonRef operator[](const int index);
|
|
|
|
bool Exists();
|
|
int Type();
|
|
int Size();
|
|
|
|
double ToDouble();
|
|
long ToInteger();
|
|
bool ToBool();
|
|
string ToString();
|
|
|
|
bool IsObject();
|
|
bool IsArray();
|
|
bool IsString();
|
|
bool IsNumber();
|
|
bool IsBool();
|
|
bool IsNull();
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Internal node |
|
|
//+------------------------------------------------------------------+
|
|
struct JsonNode
|
|
{
|
|
public:
|
|
uchar type;
|
|
uchar flags;
|
|
int parent;
|
|
int first_child;
|
|
int last_child;
|
|
int next_sibling;
|
|
int child_count;
|
|
|
|
int key_start;
|
|
int key_len;
|
|
uint key_hash;
|
|
|
|
int val_start;
|
|
int val_len;
|
|
|
|
double num;
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Main JSON parser class |
|
|
//+------------------------------------------------------------------+
|
|
class CJson
|
|
{
|
|
public:
|
|
// Public input buffer. Fill it directly for best performance.
|
|
uchar Buffer[];
|
|
|
|
// If InputSize >= 0, parser uses only InputSize bytes from Buffer.
|
|
// If InputSize < 0, parser uses ArraySize(Buffer).
|
|
int InputSize;
|
|
|
|
// Maximum nested container depth.
|
|
int MaxDepth;
|
|
|
|
// Build hash index for object member lookup.
|
|
bool UseHashIndex;
|
|
|
|
// Minimum number of object members before building hash index.
|
|
int HashThreshold;
|
|
|
|
protected:
|
|
int m_size;
|
|
int m_root;
|
|
|
|
ENUM_JSON_ERROR m_error;
|
|
int m_error_pos;
|
|
|
|
int m_node_used;
|
|
JsonNode m_nodes[];
|
|
|
|
int m_children[];
|
|
|
|
uchar m_pool[];
|
|
int m_pool_used;
|
|
|
|
int m_stack[];
|
|
uchar m_state[];
|
|
int m_stack_used;
|
|
|
|
int m_member_count;
|
|
|
|
int m_hash_size;
|
|
int m_hash_mask;
|
|
int m_hash_parent[];
|
|
uint m_hash_key[];
|
|
int m_hash_node[];
|
|
|
|
int m_pending_key_start;
|
|
int m_pending_key_len;
|
|
uint m_pending_key_hash;
|
|
uchar m_pending_flags;
|
|
bool m_pending_valid;
|
|
|
|
//+---------------------------------------------------------------+
|
|
//| Small helpers |
|
|
//+---------------------------------------------------------------+
|
|
int Resolve(int node) const
|
|
{
|
|
return(node < 0 ? m_root : node);
|
|
}
|
|
|
|
bool SetError(ENUM_JSON_ERROR code, int pos)
|
|
{
|
|
m_error = code;
|
|
m_error_pos = pos;
|
|
return(false);
|
|
}
|
|
|
|
void SkipWs(int &pos)
|
|
{
|
|
while(pos < m_size)
|
|
{
|
|
uchar c = Buffer[pos];
|
|
if(c != 0x20 && c != 0x09 && c != 0x0A && c != 0x0D)
|
|
break;
|
|
pos++;
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------+
|
|
//| Memory helpers |
|
|
//+---------------------------------------------------------------+
|
|
bool EnsureNodes(int count, int pos)
|
|
{
|
|
int cap = ArraySize(m_nodes);
|
|
if(count <= cap)
|
|
return(true);
|
|
|
|
int new_cap = cap * 2;
|
|
if(new_cap < count)
|
|
new_cap = count;
|
|
if(new_cap < 1024)
|
|
new_cap = 1024;
|
|
|
|
if(ArrayResize(m_nodes, new_cap, new_cap + 4096) < 0)
|
|
return(SetError(JSON_MEMORY_ERROR, pos));
|
|
|
|
return(true);
|
|
}
|
|
|
|
bool EnsureStack(int count, int pos)
|
|
{
|
|
int cap = ArraySize(m_stack);
|
|
if(count <= cap)
|
|
return(true);
|
|
|
|
int new_cap = cap * 2;
|
|
if(new_cap < count)
|
|
new_cap = count;
|
|
if(new_cap < 64)
|
|
new_cap = 64;
|
|
|
|
if(ArrayResize(m_stack, new_cap, new_cap + 256) < 0)
|
|
return(SetError(JSON_MEMORY_ERROR, pos));
|
|
|
|
if(ArrayResize(m_state, new_cap, new_cap + 256) < 0)
|
|
return(SetError(JSON_MEMORY_ERROR, pos));
|
|
|
|
return(true);
|
|
}
|
|
|
|
bool EnsurePool(int add, int pos)
|
|
{
|
|
int cap = ArraySize(m_pool);
|
|
if(m_pool_used + add <= cap)
|
|
return(true);
|
|
|
|
int new_cap = cap * 2;
|
|
if(new_cap < m_pool_used + add)
|
|
new_cap = m_pool_used + add;
|
|
if(new_cap < 1024)
|
|
new_cap = 1024;
|
|
|
|
if(ArrayResize(m_pool, new_cap, new_cap + 4096) < 0)
|
|
return(SetError(JSON_MEMORY_ERROR, pos));
|
|
|
|
return(true);
|
|
}
|
|
|
|
//+---------------------------------------------------------------+
|
|
//| Pool and hashing helpers |
|
|
//+---------------------------------------------------------------+
|
|
bool AppendPoolByte(uchar b, uint &h, int pos)
|
|
{
|
|
if(!EnsurePool(1, pos))
|
|
return(false);
|
|
|
|
m_pool[m_pool_used++] = b;
|
|
h = ((h ^ b) * JSON_FNV_PRIME);
|
|
return(true);
|
|
}
|
|
|
|
bool AppendPoolUtf8(uint cp, uint &h, int pos)
|
|
{
|
|
if(cp < 0x80)
|
|
return(AppendPoolByte((uchar)cp, h, pos));
|
|
|
|
if(cp < 0x800)
|
|
{
|
|
if(!AppendPoolByte((uchar)(0xC0 | (cp >> 6)), h, pos))
|
|
return(false);
|
|
return(AppendPoolByte((uchar)(0x80 | (cp & 0x3F)), h, pos));
|
|
}
|
|
|
|
if(cp < 0x10000)
|
|
{
|
|
if(!AppendPoolByte((uchar)(0xE0 | (cp >> 12)), h, pos))
|
|
return(false);
|
|
if(!AppendPoolByte((uchar)(0x80 | ((cp >> 6) & 0x3F)), h, pos))
|
|
return(false);
|
|
return(AppendPoolByte((uchar)(0x80 | (cp & 0x3F)), h, pos));
|
|
}
|
|
|
|
if(cp <= 0x10FFFF)
|
|
{
|
|
if(!AppendPoolByte((uchar)(0xF0 | (cp >> 18)), h, pos))
|
|
return(false);
|
|
if(!AppendPoolByte((uchar)(0x80 | ((cp >> 12) & 0x3F)), h, pos))
|
|
return(false);
|
|
if(!AppendPoolByte((uchar)(0x80 | ((cp >> 6) & 0x3F)), h, pos))
|
|
return(false);
|
|
return(AppendPoolByte((uchar)(0x80 | (cp & 0x3F)), h, pos));
|
|
}
|
|
|
|
return(SetError(JSON_INVALID_UNICODE, pos));
|
|
}
|
|
|
|
bool ParseHex4(int &cur, uint &val)
|
|
{
|
|
if(cur + 4 > m_size)
|
|
return(SetError(JSON_INVALID_UNICODE, cur));
|
|
|
|
val = 0;
|
|
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
uchar c = Buffer[cur];
|
|
int hv;
|
|
|
|
if(c >= 0x30 && c <= 0x39)
|
|
hv = c - 0x30;
|
|
else if(c >= 0x41 && c <= 0x46)
|
|
hv = c - 0x41 + 10;
|
|
else if(c >= 0x61 && c <= 0x66)
|
|
hv = c - 0x61 + 10;
|
|
else
|
|
return(SetError(JSON_INVALID_UNICODE, cur));
|
|
|
|
val = (val << 4) | (uint)hv;
|
|
cur++;
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
//+---------------------------------------------------------------+
|
|
//| String parser |
|
|
//+---------------------------------------------------------------+
|
|
bool ParseStringSlow(int start,
|
|
int p,
|
|
uint h,
|
|
int &pos,
|
|
int &out_start,
|
|
int &out_len,
|
|
bool &out_decoded,
|
|
uint &out_hash)
|
|
{
|
|
int pool_start = m_pool_used;
|
|
|
|
int n = p - start;
|
|
if(n > 0)
|
|
{
|
|
if(!EnsurePool(n, start))
|
|
return(false);
|
|
|
|
int base = m_pool_used;
|
|
for(int i = 0; i < n; i++)
|
|
m_pool[base + i] = Buffer[start + i];
|
|
|
|
m_pool_used += n;
|
|
}
|
|
|
|
int cur = p;
|
|
|
|
while(cur < m_size)
|
|
{
|
|
int run = cur;
|
|
|
|
while(run < m_size)
|
|
{
|
|
uchar c = Buffer[run];
|
|
if(c == 0x22 || c == 0x5C || c < 0x20)
|
|
break;
|
|
run++;
|
|
}
|
|
|
|
int rn = run - cur;
|
|
if(rn > 0)
|
|
{
|
|
if(!EnsurePool(rn, cur))
|
|
return(false);
|
|
|
|
int base = m_pool_used;
|
|
for(int i = 0; i < rn; i++)
|
|
{
|
|
uchar b = Buffer[cur + i];
|
|
m_pool[base + i] = b;
|
|
h = ((h ^ b) * JSON_FNV_PRIME);
|
|
}
|
|
|
|
m_pool_used += rn;
|
|
cur = run;
|
|
}
|
|
|
|
if(cur >= m_size)
|
|
return(SetError(JSON_UNEXPECTED_END, pos));
|
|
|
|
uchar c = Buffer[cur];
|
|
|
|
if(c == 0x22)
|
|
{
|
|
out_start = pool_start;
|
|
out_len = m_pool_used - pool_start;
|
|
out_decoded = true;
|
|
out_hash = h;
|
|
pos = cur + 1;
|
|
return(true);
|
|
}
|
|
|
|
if(c < 0x20)
|
|
return(SetError(JSON_INVALID_STRING, cur));
|
|
|
|
// c == backslash
|
|
cur++;
|
|
if(cur >= m_size)
|
|
return(SetError(JSON_UNEXPECTED_END, cur));
|
|
|
|
uchar e = Buffer[cur];
|
|
|
|
switch(e)
|
|
{
|
|
case 0x22: // \"
|
|
if(!AppendPoolByte(0x22, h, cur))
|
|
return(false);
|
|
cur++;
|
|
break;
|
|
|
|
case 0x5C: // \\
|
|
if(!AppendPoolByte(0x5C, h, cur))
|
|
return(false);
|
|
cur++;
|
|
break;
|
|
|
|
case 0x2F: // \/
|
|
if(!AppendPoolByte(0x2F, h, cur))
|
|
return(false);
|
|
cur++;
|
|
break;
|
|
|
|
case 0x62: // \b
|
|
if(!AppendPoolByte(0x08, h, cur))
|
|
return(false);
|
|
cur++;
|
|
break;
|
|
|
|
case 0x66: // \f
|
|
if(!AppendPoolByte(0x0C, h, cur))
|
|
return(false);
|
|
cur++;
|
|
break;
|
|
|
|
case 0x6E: // \n
|
|
if(!AppendPoolByte(0x0A, h, cur))
|
|
return(false);
|
|
cur++;
|
|
break;
|
|
|
|
case 0x72: // \r
|
|
if(!AppendPoolByte(0x0D, h, cur))
|
|
return(false);
|
|
cur++;
|
|
break;
|
|
|
|
case 0x74: // \t
|
|
if(!AppendPoolByte(0x09, h, cur))
|
|
return(false);
|
|
cur++;
|
|
break;
|
|
|
|
case 0x75: // \uXXXX
|
|
{
|
|
cur++;
|
|
uint cp;
|
|
if(!ParseHex4(cur, cp))
|
|
return(false);
|
|
|
|
if(cp >= 0xD800 && cp <= 0xDBFF)
|
|
{
|
|
if(cur + 6 <= m_size && Buffer[cur] == 0x5C && Buffer[cur + 1] == 0x75)
|
|
{
|
|
cur += 2;
|
|
uint low;
|
|
if(!ParseHex4(cur, low))
|
|
return(false);
|
|
|
|
if(low >= 0xDC00 && low <= 0xDFFF)
|
|
cp = 0x10000 + ((cp - 0xD800) << 10) + (low - 0xDC00);
|
|
else
|
|
return(SetError(JSON_INVALID_UNICODE, cur));
|
|
}
|
|
else
|
|
{
|
|
return(SetError(JSON_INVALID_UNICODE, cur));
|
|
}
|
|
}
|
|
else if(cp >= 0xDC00 && cp <= 0xDFFF)
|
|
{
|
|
return(SetError(JSON_INVALID_UNICODE, cur));
|
|
}
|
|
|
|
if(!AppendPoolUtf8(cp, h, cur))
|
|
return(false);
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return(SetError(JSON_INVALID_ESCAPE, cur));
|
|
}
|
|
}
|
|
|
|
return(SetError(JSON_UNEXPECTED_END, pos));
|
|
}
|
|
|
|
bool ParseStringToken(int &pos,
|
|
int &out_start,
|
|
int &out_len,
|
|
bool &out_decoded,
|
|
uint &out_hash)
|
|
{
|
|
if(pos >= m_size || Buffer[pos] != 0x22)
|
|
return(SetError(JSON_INVALID_STRING, pos));
|
|
|
|
int start = pos + 1;
|
|
int p = start;
|
|
uint h = JSON_FNV_INIT;
|
|
|
|
while(p < m_size)
|
|
{
|
|
uchar c = Buffer[p];
|
|
|
|
if(c == 0x22)
|
|
{
|
|
out_start = start;
|
|
out_len = p - start;
|
|
out_decoded = false;
|
|
out_hash = h;
|
|
pos = p + 1;
|
|
return(true);
|
|
}
|
|
|
|
if(c == 0x5C)
|
|
return(ParseStringSlow(start, p, h, pos, out_start, out_len, out_decoded, out_hash));
|
|
|
|
if(c < 0x20)
|
|
return(SetError(JSON_INVALID_STRING, p));
|
|
|
|
h = ((h ^ c) * JSON_FNV_PRIME);
|
|
p++;
|
|
}
|
|
|
|
return(SetError(JSON_UNEXPECTED_END, pos));
|
|
}
|
|
|
|
//+---------------------------------------------------------------+
|
|
//| Number parser |
|
|
//+---------------------------------------------------------------+
|
|
bool ParseNumberToken(int &pos, double &val)
|
|
{
|
|
int start = pos;
|
|
bool neg = false;
|
|
|
|
if(pos < m_size && Buffer[pos] == 0x2D)
|
|
{
|
|
neg = true;
|
|
pos++;
|
|
}
|
|
|
|
if(pos >= m_size)
|
|
return(SetError(JSON_INVALID_NUMBER, start));
|
|
|
|
uchar c = Buffer[pos];
|
|
double intpart = 0.0;
|
|
|
|
if(c == 0x30)
|
|
{
|
|
pos++;
|
|
if(pos < m_size && Buffer[pos] >= 0x30 && Buffer[pos] <= 0x39)
|
|
return(SetError(JSON_INVALID_NUMBER, pos));
|
|
}
|
|
else if(c >= 0x31 && c <= 0x39)
|
|
{
|
|
while(pos < m_size)
|
|
{
|
|
uchar d = Buffer[pos];
|
|
if(d < 0x30 || d > 0x39)
|
|
break;
|
|
|
|
intpart = intpart * 10.0 + (double)(d - 0x30);
|
|
pos++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return(SetError(JSON_INVALID_NUMBER, pos));
|
|
}
|
|
|
|
if(pos < m_size && Buffer[pos] == 0x2E)
|
|
{
|
|
pos++;
|
|
|
|
if(pos >= m_size || Buffer[pos] < 0x30 || Buffer[pos] > 0x39)
|
|
return(SetError(JSON_INVALID_NUMBER, pos));
|
|
|
|
double frac = 0.0;
|
|
double scale = 0.1;
|
|
|
|
while(pos < m_size)
|
|
{
|
|
uchar d = Buffer[pos];
|
|
if(d < 0x30 || d > 0x39)
|
|
break;
|
|
|
|
frac += (double)(d - 0x30) * scale;
|
|
scale *= 0.1;
|
|
pos++;
|
|
}
|
|
|
|
intpart += frac;
|
|
}
|
|
|
|
int exp = 0;
|
|
bool exp_neg = false;
|
|
|
|
if(pos < m_size && (Buffer[pos] == 0x65 || Buffer[pos] == 0x45))
|
|
{
|
|
pos++;
|
|
|
|
if(pos < m_size && (Buffer[pos] == 0x2B || Buffer[pos] == 0x2D))
|
|
{
|
|
exp_neg = (Buffer[pos] == 0x2D);
|
|
pos++;
|
|
}
|
|
|
|
if(pos >= m_size || Buffer[pos] < 0x30 || Buffer[pos] > 0x39)
|
|
return(SetError(JSON_INVALID_NUMBER, pos));
|
|
|
|
while(pos < m_size)
|
|
{
|
|
uchar d = Buffer[pos];
|
|
if(d < 0x30 || d > 0x39)
|
|
break;
|
|
|
|
if(exp < 100000)
|
|
exp = exp * 10 + (d - 0x30);
|
|
|
|
pos++;
|
|
}
|
|
}
|
|
|
|
val = neg ? -intpart : intpart;
|
|
|
|
if(exp != 0)
|
|
{
|
|
int e = exp;
|
|
if(e > 308)
|
|
e = 308;
|
|
|
|
double de = (double)e;
|
|
val *= MathPow(10.0, exp_neg ? -de : de);
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
//+---------------------------------------------------------------+
|
|
//| Node construction helpers |
|
|
//+---------------------------------------------------------------+
|
|
int NewNode(uchar type, int parent, int pos)
|
|
{
|
|
if(!EnsureNodes(m_node_used + 1, pos))
|
|
return(-1);
|
|
|
|
int idx = m_node_used++;
|
|
|
|
m_nodes[idx].type = type;
|
|
m_nodes[idx].flags = 0;
|
|
m_nodes[idx].parent = parent;
|
|
m_nodes[idx].first_child = -1;
|
|
m_nodes[idx].last_child = -1;
|
|
m_nodes[idx].next_sibling = -1;
|
|
m_nodes[idx].child_count = 0;
|
|
|
|
m_nodes[idx].key_start = 0;
|
|
m_nodes[idx].key_len = 0;
|
|
m_nodes[idx].key_hash = 0;
|
|
|
|
m_nodes[idx].val_start = 0;
|
|
m_nodes[idx].val_len = 0;
|
|
|
|
m_nodes[idx].num = 0.0;
|
|
|
|
return(idx);
|
|
}
|
|
|
|
bool AssignPendingKey(int node, int parent, int pos)
|
|
{
|
|
if(parent < 0)
|
|
{
|
|
m_pending_valid = false;
|
|
return(true);
|
|
}
|
|
|
|
if(m_nodes[parent].type != (uchar)JSON_OBJECT)
|
|
{
|
|
m_pending_valid = false;
|
|
return(true);
|
|
}
|
|
|
|
if(!m_pending_valid)
|
|
return(SetError(JSON_EXPECTED_KEY, pos));
|
|
|
|
m_nodes[node].key_start = m_pending_key_start;
|
|
m_nodes[node].key_len = m_pending_key_len;
|
|
m_nodes[node].key_hash = m_pending_key_hash;
|
|
|
|
if((m_pending_flags & JSON_FLAG_KEY_DECODED) != 0)
|
|
m_nodes[node].flags = (uchar)(m_nodes[node].flags | JSON_FLAG_KEY_DECODED);
|
|
|
|
m_pending_valid = false;
|
|
return(true);
|
|
}
|
|
|
|
void AddChild(int parent, int child)
|
|
{
|
|
if(parent < 0)
|
|
return;
|
|
|
|
if(m_nodes[parent].first_child < 0)
|
|
{
|
|
m_nodes[parent].first_child = child;
|
|
}
|
|
else
|
|
{
|
|
m_nodes[m_nodes[parent].last_child].next_sibling = child;
|
|
}
|
|
|
|
m_nodes[parent].last_child = child;
|
|
m_nodes[parent].child_count++;
|
|
|
|
if(m_nodes[parent].type == (uchar)JSON_OBJECT)
|
|
m_member_count++;
|
|
}
|
|
|
|
bool PushStack(int node, uchar state, int pos)
|
|
{
|
|
if(m_stack_used >= MaxDepth)
|
|
return(SetError(JSON_MAX_DEPTH, pos));
|
|
|
|
if(!EnsureStack(m_stack_used + 1, pos))
|
|
return(false);
|
|
|
|
m_stack[m_stack_used] = node;
|
|
m_state[m_stack_used] = state;
|
|
m_stack_used++;
|
|
|
|
return(true);
|
|
}
|
|
|
|
bool ParseObjectKey(int &pos)
|
|
{
|
|
int s, l;
|
|
bool dec;
|
|
uint h;
|
|
|
|
if(!ParseStringToken(pos, s, l, dec, h))
|
|
return(false);
|
|
|
|
m_pending_key_start = s;
|
|
m_pending_key_len = l;
|
|
m_pending_key_hash = h;
|
|
m_pending_flags = dec ? (uchar)JSON_FLAG_KEY_DECODED : 0;
|
|
m_pending_valid = true;
|
|
|
|
return(true);
|
|
}
|
|
|
|
//+---------------------------------------------------------------+
|
|
//| Create a value node at current position |
|
|
//+---------------------------------------------------------------+
|
|
bool CreateValueNode(int &pos, int parent, int &out_node)
|
|
{
|
|
if(pos >= m_size)
|
|
return(SetError(JSON_UNEXPECTED_END, pos));
|
|
|
|
uchar c = Buffer[pos];
|
|
|
|
// Object
|
|
if(c == 0x7B)
|
|
{
|
|
int node = NewNode((uchar)JSON_OBJECT, parent, pos);
|
|
if(node < 0)
|
|
return(false);
|
|
|
|
if(!AssignPendingKey(node, parent, pos))
|
|
return(false);
|
|
|
|
AddChild(parent, node);
|
|
pos++;
|
|
|
|
if(!PushStack(node, (uchar)JSON_STATE_OBJ_KEY_OR_END, pos))
|
|
return(false);
|
|
|
|
out_node = node;
|
|
return(true);
|
|
}
|
|
|
|
// Array
|
|
if(c == 0x5B)
|
|
{
|
|
int node = NewNode((uchar)JSON_ARRAY, parent, pos);
|
|
if(node < 0)
|
|
return(false);
|
|
|
|
if(!AssignPendingKey(node, parent, pos))
|
|
return(false);
|
|
|
|
AddChild(parent, node);
|
|
pos++;
|
|
|
|
if(!PushStack(node, (uchar)JSON_STATE_ARRAY_VALUE_OR_END, pos))
|
|
return(false);
|
|
|
|
out_node = node;
|
|
return(true);
|
|
}
|
|
|
|
// String
|
|
if(c == 0x22)
|
|
{
|
|
int s, l;
|
|
bool dec;
|
|
uint h;
|
|
|
|
if(!ParseStringToken(pos, s, l, dec, h))
|
|
return(false);
|
|
|
|
int node = NewNode((uchar)JSON_STRING, parent, pos);
|
|
if(node < 0)
|
|
return(false);
|
|
|
|
if(!AssignPendingKey(node, parent, pos))
|
|
return(false);
|
|
|
|
m_nodes[node].val_start = s;
|
|
m_nodes[node].val_len = l;
|
|
|
|
if(dec)
|
|
m_nodes[node].flags = (uchar)(m_nodes[node].flags | JSON_FLAG_VAL_DECODED);
|
|
|
|
AddChild(parent, node);
|
|
|
|
out_node = node;
|
|
return(true);
|
|
}
|
|
|
|
// true
|
|
if(c == 0x74)
|
|
{
|
|
if(pos + 4 > m_size ||
|
|
Buffer[pos + 1] != 0x72 ||
|
|
Buffer[pos + 2] != 0x75 ||
|
|
Buffer[pos + 3] != 0x65)
|
|
{
|
|
return(SetError(JSON_INVALID_LITERAL, pos));
|
|
}
|
|
|
|
int node = NewNode((uchar)JSON_BOOL, parent, pos);
|
|
if(node < 0)
|
|
return(false);
|
|
|
|
if(!AssignPendingKey(node, parent, pos))
|
|
return(false);
|
|
|
|
m_nodes[node].num = 1.0;
|
|
AddChild(parent, node);
|
|
|
|
pos += 4;
|
|
out_node = node;
|
|
return(true);
|
|
}
|
|
|
|
// false
|
|
if(c == 0x66)
|
|
{
|
|
if(pos + 5 > m_size ||
|
|
Buffer[pos + 1] != 0x61 ||
|
|
Buffer[pos + 2] != 0x6C ||
|
|
Buffer[pos + 3] != 0x73 ||
|
|
Buffer[pos + 4] != 0x65)
|
|
{
|
|
return(SetError(JSON_INVALID_LITERAL, pos));
|
|
}
|
|
|
|
int node = NewNode((uchar)JSON_BOOL, parent, pos);
|
|
if(node < 0)
|
|
return(false);
|
|
|
|
if(!AssignPendingKey(node, parent, pos))
|
|
return(false);
|
|
|
|
m_nodes[node].num = 0.0;
|
|
AddChild(parent, node);
|
|
|
|
pos += 5;
|
|
out_node = node;
|
|
return(true);
|
|
}
|
|
|
|
// null
|
|
if(c == 0x6E)
|
|
{
|
|
if(pos + 4 > m_size ||
|
|
Buffer[pos + 1] != 0x75 ||
|
|
Buffer[pos + 2] != 0x6C ||
|
|
Buffer[pos + 3] != 0x6C)
|
|
{
|
|
return(SetError(JSON_INVALID_LITERAL, pos));
|
|
}
|
|
|
|
int node = NewNode((uchar)JSON_NULL, parent, pos);
|
|
if(node < 0)
|
|
return(false);
|
|
|
|
if(!AssignPendingKey(node, parent, pos))
|
|
return(false);
|
|
|
|
m_nodes[node].num = 0.0;
|
|
AddChild(parent, node);
|
|
|
|
pos += 4;
|
|
out_node = node;
|
|
return(true);
|
|
}
|
|
|
|
// Number
|
|
if(c == 0x2D || (c >= 0x30 && c <= 0x39))
|
|
{
|
|
double v;
|
|
if(!ParseNumberToken(pos, v))
|
|
return(false);
|
|
|
|
int node = NewNode((uchar)JSON_NUMBER, parent, pos);
|
|
if(node < 0)
|
|
return(false);
|
|
|
|
if(!AssignPendingKey(node, parent, pos))
|
|
return(false);
|
|
|
|
m_nodes[node].num = v;
|
|
AddChild(parent, node);
|
|
|
|
out_node = node;
|
|
return(true);
|
|
}
|
|
|
|
return(SetError(JSON_UNEXPECTED_CHARACTER, pos));
|
|
}
|
|
|
|
//+---------------------------------------------------------------+
|
|
//| Build contiguous child index |
|
|
//+---------------------------------------------------------------+
|
|
bool BuildChildren(int pos)
|
|
{
|
|
int total = 0;
|
|
|
|
for(int i = 0; i < m_node_used; i++)
|
|
{
|
|
m_nodes[i].last_child = total;
|
|
total += m_nodes[i].child_count;
|
|
}
|
|
|
|
if(total == 0)
|
|
{
|
|
ArrayFree(m_children);
|
|
|
|
for(int i = 0; i < m_node_used; i++)
|
|
m_nodes[i].first_child = 0;
|
|
|
|
return(true);
|
|
}
|
|
|
|
if(ArrayResize(m_children, total, total + 1024) < 0)
|
|
return(SetError(JSON_MEMORY_ERROR, pos));
|
|
|
|
for(int i = 0; i < m_node_used; i++)
|
|
{
|
|
int offset = m_nodes[i].last_child;
|
|
int child = m_nodes[i].first_child;
|
|
int idx = 0;
|
|
|
|
while(child != -1)
|
|
{
|
|
m_children[offset + idx] = child;
|
|
child = m_nodes[child].next_sibling;
|
|
idx++;
|
|
}
|
|
|
|
m_nodes[i].first_child = offset;
|
|
m_nodes[i].last_child = -1;
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
//+---------------------------------------------------------------+
|
|
//| Hash helpers |
|
|
//+---------------------------------------------------------------+
|
|
uint HashMix(int parent, uint key_hash) const
|
|
{
|
|
uint h = key_hash ^ ((uint)parent * (uint)0x9E3779B9);
|
|
h ^= (h >> 16);
|
|
h *= (uint)0x85EBCA6B;
|
|
h ^= (h >> 13);
|
|
h *= (uint)0xC2B2AE35;
|
|
h ^= (h >> 16);
|
|
return(h);
|
|
}
|
|
|
|
uint HashBytes(uchar &data[], int len) const
|
|
{
|
|
uint h = JSON_FNV_INIT;
|
|
|
|
for(int i = 0; i < len; i++)
|
|
h = ((h ^ data[i]) * JSON_FNV_PRIME);
|
|
|
|
return(h);
|
|
}
|
|
|
|
bool KeyEquals(int node, uchar &key[], int len, uint hash)
|
|
{
|
|
if(node < 0 || node >= m_node_used)
|
|
return(false);
|
|
|
|
if(m_nodes[node].key_len != len)
|
|
return(false);
|
|
|
|
if(m_nodes[node].key_hash != hash)
|
|
return(false);
|
|
|
|
if(len == 0)
|
|
return(true);
|
|
|
|
int start = m_nodes[node].key_start;
|
|
|
|
if((m_nodes[node].flags & JSON_FLAG_KEY_DECODED) != 0)
|
|
{
|
|
for(int i = 0; i < len; i++)
|
|
{
|
|
if(m_pool[start + i] != key[i])
|
|
return(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(int i = 0; i < len; i++)
|
|
{
|
|
if(Buffer[start + i] != key[i])
|
|
return(false);
|
|
}
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
void InsertHash(int parent, uint key_hash, int node)
|
|
{
|
|
if(m_hash_size <= 0)
|
|
return;
|
|
|
|
uint pos = HashMix(parent, key_hash) & (uint)m_hash_mask;
|
|
int scanned = 0;
|
|
|
|
while(m_hash_parent[pos] != -1 && scanned < m_hash_size)
|
|
{
|
|
pos = (pos + 1) & (uint)m_hash_mask;
|
|
scanned++;
|
|
}
|
|
|
|
if(scanned >= m_hash_size)
|
|
return;
|
|
|
|
m_hash_parent[pos] = parent;
|
|
m_hash_key[pos] = key_hash;
|
|
m_hash_node[pos] = node;
|
|
}
|
|
|
|
bool BuildHash(int pos)
|
|
{
|
|
m_hash_size = 0;
|
|
m_hash_mask = 0;
|
|
|
|
if(m_member_count <= 0)
|
|
return(true);
|
|
|
|
int target = m_member_count * 2;
|
|
if(target <= 0)
|
|
target = 0x40000000;
|
|
if(target < 16)
|
|
target = 16;
|
|
|
|
int table_size = 1;
|
|
while(table_size < target && table_size < 0x40000000)
|
|
table_size <<= 1;
|
|
|
|
if(ArrayResize(m_hash_parent, table_size, table_size + 1024) < 0)
|
|
return(SetError(JSON_MEMORY_ERROR, pos));
|
|
|
|
if(ArrayResize(m_hash_key, table_size, table_size + 1024) < 0)
|
|
return(SetError(JSON_MEMORY_ERROR, pos));
|
|
|
|
if(ArrayResize(m_hash_node, table_size, table_size + 1024) < 0)
|
|
return(SetError(JSON_MEMORY_ERROR, pos));
|
|
|
|
for(int i = 0; i < table_size; i++)
|
|
m_hash_parent[i] = -1;
|
|
|
|
m_hash_size = table_size;
|
|
m_hash_mask = table_size - 1;
|
|
|
|
for(int i = 0; i < m_node_used; i++)
|
|
{
|
|
int p = m_nodes[i].parent;
|
|
if(p >= 0 && m_nodes[p].type == (uchar)JSON_OBJECT)
|
|
InsertHash(p, m_nodes[i].key_hash, i);
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
public:
|
|
|
|
//+---------------------------------------------------------------+
|
|
//| Reset / Clear |
|
|
//+---------------------------------------------------------------+
|
|
void Reset()
|
|
{
|
|
m_size = 0;
|
|
m_root = -1;
|
|
|
|
m_error = JSON_OK;
|
|
m_error_pos = -1;
|
|
|
|
m_node_used = 0;
|
|
m_pool_used = 0;
|
|
m_stack_used = 0;
|
|
|
|
m_member_count = 0;
|
|
|
|
m_hash_size = 0;
|
|
m_hash_mask = 0;
|
|
|
|
m_pending_key_start = 0;
|
|
m_pending_key_len = 0;
|
|
m_pending_key_hash = 0;
|
|
m_pending_flags = 0;
|
|
m_pending_valid = false;
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
ArrayFree(m_nodes);
|
|
ArrayFree(m_children);
|
|
ArrayFree(m_pool);
|
|
ArrayFree(m_stack);
|
|
ArrayFree(m_state);
|
|
|
|
ArrayFree(m_hash_parent);
|
|
ArrayFree(m_hash_key);
|
|
ArrayFree(m_hash_node);
|
|
|
|
Reset();
|
|
}
|
|
|
|
//+---------------------------------------------------------------+
|
|
//| Constructor / Destructor |
|
|
//+---------------------------------------------------------------+
|
|
CJson()
|
|
{
|
|
InputSize = -1;
|
|
MaxDepth = 1024;
|
|
UseHashIndex = true;
|
|
HashThreshold = 16;
|
|
|
|
Reset();
|
|
}
|
|
|
|
~CJson()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
//+---------------------------------------------------------------+
|
|
//| Main parse function |
|
|
//+---------------------------------------------------------------+
|
|
bool Parse()
|
|
{
|
|
Reset();
|
|
|
|
if(MaxDepth <= 0)
|
|
MaxDepth = 1024;
|
|
|
|
if(HashThreshold < 0)
|
|
HashThreshold = 0;
|
|
|
|
ArraySetAsSeries(Buffer, false);
|
|
|
|
int buf_size = ArraySize(Buffer);
|
|
|
|
if(InputSize >= 0)
|
|
m_size = (InputSize < buf_size ? InputSize : buf_size);
|
|
else
|
|
m_size = buf_size;
|
|
|
|
if(m_size < 0)
|
|
m_size = 0;
|
|
|
|
int pos = 0;
|
|
|
|
// Optional UTF-8 BOM.
|
|
if(m_size >= 3 && Buffer[0] == 0xEF && Buffer[1] == 0xBB && Buffer[2] == 0xBF)
|
|
pos = 3;
|
|
|
|
SkipWs(pos);
|
|
|
|
if(pos >= m_size)
|
|
return(SetError(JSON_UNEXPECTED_END, pos));
|
|
|
|
int root;
|
|
if(!CreateValueNode(pos, -1, root))
|
|
return(false);
|
|
|
|
m_root = root;
|
|
|
|
while(m_stack_used > 0)
|
|
{
|
|
int si = m_stack_used - 1;
|
|
int node = m_stack[si];
|
|
uchar st = m_state[si];
|
|
|
|
if(st == (uchar)JSON_STATE_OBJ_KEY_OR_END)
|
|
{
|
|
SkipWs(pos);
|
|
if(pos >= m_size)
|
|
return(SetError(JSON_UNEXPECTED_END, pos));
|
|
|
|
uchar c = Buffer[pos];
|
|
|
|
if(c == 0x7D)
|
|
{
|
|
pos++;
|
|
m_stack_used--;
|
|
continue;
|
|
}
|
|
|
|
if(c != 0x22)
|
|
return(SetError(JSON_EXPECTED_KEY, pos));
|
|
|
|
if(!ParseObjectKey(pos))
|
|
return(false);
|
|
|
|
m_state[si] = (uchar)JSON_STATE_OBJ_COLON;
|
|
}
|
|
else if(st == (uchar)JSON_STATE_OBJ_KEY)
|
|
{
|
|
SkipWs(pos);
|
|
if(pos >= m_size)
|
|
return(SetError(JSON_UNEXPECTED_END, pos));
|
|
|
|
uchar c = Buffer[pos];
|
|
|
|
if(c != 0x22)
|
|
return(SetError(JSON_EXPECTED_KEY, pos));
|
|
|
|
if(!ParseObjectKey(pos))
|
|
return(false);
|
|
|
|
m_state[si] = (uchar)JSON_STATE_OBJ_COLON;
|
|
}
|
|
else if(st == (uchar)JSON_STATE_OBJ_COLON)
|
|
{
|
|
SkipWs(pos);
|
|
if(pos >= m_size)
|
|
return(SetError(JSON_UNEXPECTED_END, pos));
|
|
|
|
if(Buffer[pos] != 0x3A)
|
|
return(SetError(JSON_EXPECTED_COLON, pos));
|
|
|
|
pos++;
|
|
m_state[si] = (uchar)JSON_STATE_OBJ_VALUE;
|
|
}
|
|
else if(st == (uchar)JSON_STATE_OBJ_VALUE)
|
|
{
|
|
SkipWs(pos);
|
|
if(pos >= m_size)
|
|
return(SetError(JSON_UNEXPECTED_END, pos));
|
|
|
|
m_state[si] = (uchar)JSON_STATE_OBJ_COMMA_OR_END;
|
|
|
|
int child;
|
|
if(!CreateValueNode(pos, node, child))
|
|
return(false);
|
|
}
|
|
else if(st == (uchar)JSON_STATE_OBJ_COMMA_OR_END)
|
|
{
|
|
SkipWs(pos);
|
|
if(pos >= m_size)
|
|
return(SetError(JSON_UNEXPECTED_END, pos));
|
|
|
|
uchar c = Buffer[pos];
|
|
|
|
if(c == 0x2C)
|
|
{
|
|
pos++;
|
|
m_state[si] = (uchar)JSON_STATE_OBJ_KEY;
|
|
}
|
|
else if(c == 0x7D)
|
|
{
|
|
pos++;
|
|
m_stack_used--;
|
|
}
|
|
else
|
|
{
|
|
return(SetError(JSON_EXPECTED_COMMA, pos));
|
|
}
|
|
}
|
|
else if(st == (uchar)JSON_STATE_ARRAY_VALUE_OR_END)
|
|
{
|
|
SkipWs(pos);
|
|
if(pos >= m_size)
|
|
return(SetError(JSON_UNEXPECTED_END, pos));
|
|
|
|
uchar c = Buffer[pos];
|
|
|
|
if(c == 0x5D)
|
|
{
|
|
pos++;
|
|
m_stack_used--;
|
|
continue;
|
|
}
|
|
|
|
m_state[si] = (uchar)JSON_STATE_ARRAY_COMMA_OR_END;
|
|
|
|
int child;
|
|
if(!CreateValueNode(pos, node, child))
|
|
return(false);
|
|
}
|
|
else if(st == (uchar)JSON_STATE_ARRAY_VALUE)
|
|
{
|
|
SkipWs(pos);
|
|
if(pos >= m_size)
|
|
return(SetError(JSON_UNEXPECTED_END, pos));
|
|
|
|
uchar c = Buffer[pos];
|
|
|
|
if(c == 0x5D)
|
|
return(SetError(JSON_EXPECTED_VALUE, pos));
|
|
|
|
m_state[si] = (uchar)JSON_STATE_ARRAY_COMMA_OR_END;
|
|
|
|
int child;
|
|
if(!CreateValueNode(pos, node, child))
|
|
return(false);
|
|
}
|
|
else if(st == (uchar)JSON_STATE_ARRAY_COMMA_OR_END)
|
|
{
|
|
SkipWs(pos);
|
|
if(pos >= m_size)
|
|
return(SetError(JSON_UNEXPECTED_END, pos));
|
|
|
|
uchar c = Buffer[pos];
|
|
|
|
if(c == 0x2C)
|
|
{
|
|
pos++;
|
|
m_state[si] = (uchar)JSON_STATE_ARRAY_VALUE;
|
|
}
|
|
else if(c == 0x5D)
|
|
{
|
|
pos++;
|
|
m_stack_used--;
|
|
}
|
|
else
|
|
{
|
|
return(SetError(JSON_EXPECTED_COMMA, pos));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return(SetError(JSON_UNKNOWN_ERROR, pos));
|
|
}
|
|
}
|
|
|
|
SkipWs(pos);
|
|
|
|
if(pos != m_size)
|
|
return(SetError(JSON_TRAILING_DATA, pos));
|
|
|
|
if(!BuildChildren(pos))
|
|
return(false);
|
|
|
|
if(UseHashIndex && m_member_count >= HashThreshold)
|
|
{
|
|
if(!BuildHash(pos))
|
|
return(false);
|
|
}
|
|
else
|
|
{
|
|
m_hash_size = 0;
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
//+---------------------------------------------------------------+
|
|
//| Error API |
|
|
//+---------------------------------------------------------------+
|
|
ENUM_JSON_ERROR GetError() const
|
|
{
|
|
return(m_error);
|
|
}
|
|
|
|
bool HasError() const
|
|
{
|
|
return(m_error != JSON_OK);
|
|
}
|
|
|
|
string GetErrorMessage() const
|
|
{
|
|
switch(m_error)
|
|
{
|
|
case JSON_OK: return("OK");
|
|
case JSON_UNKNOWN_ERROR: return("Unknown error");
|
|
case JSON_UNEXPECTED_CHARACTER: return("Unexpected character");
|
|
case JSON_UNEXPECTED_END: return("Unexpected end of input");
|
|
case JSON_INVALID_NUMBER: return("Invalid number");
|
|
case JSON_INVALID_STRING: return("Invalid string");
|
|
case JSON_INVALID_ESCAPE: return("Invalid escape sequence");
|
|
case JSON_INVALID_UNICODE: return("Invalid unicode sequence");
|
|
case JSON_EXPECTED_COLON: return("Expected colon");
|
|
case JSON_EXPECTED_COMMA: return("Expected comma");
|
|
case JSON_EXPECTED_OBJECT: return("Expected object");
|
|
case JSON_EXPECTED_ARRAY: return("Expected array");
|
|
case JSON_EXPECTED_KEY: return("Expected object key");
|
|
case JSON_EXPECTED_VALUE: return("Expected value");
|
|
case JSON_INVALID_LITERAL: return("Invalid literal");
|
|
case JSON_MAX_DEPTH: return("Maximum depth exceeded");
|
|
case JSON_MEMORY_ERROR: return("Memory allocation error");
|
|
case JSON_TRAILING_DATA: return("Trailing data after JSON document");
|
|
}
|
|
|
|
return("Unknown error");
|
|
}
|
|
|
|
int GetErrorPosition() const
|
|
{
|
|
return(m_error_pos);
|
|
}
|
|
|
|
int GetLine() const
|
|
{
|
|
if(m_error_pos < 0)
|
|
return(0);
|
|
|
|
int limit = m_error_pos;
|
|
if(limit > m_size)
|
|
limit = m_size;
|
|
|
|
int line = 1;
|
|
|
|
for(int i = 0; i < limit; i++)
|
|
{
|
|
if(Buffer[i] == 0x0A)
|
|
line++;
|
|
}
|
|
|
|
return(line);
|
|
}
|
|
|
|
int GetColumn() const
|
|
{
|
|
if(m_error_pos < 0)
|
|
return(0);
|
|
|
|
int limit = m_error_pos;
|
|
if(limit > m_size)
|
|
limit = m_size;
|
|
|
|
int col = 1;
|
|
|
|
for(int i = limit - 1; i >= 0; i--)
|
|
{
|
|
if(Buffer[i] == 0x0A)
|
|
break;
|
|
|
|
col++;
|
|
}
|
|
|
|
return(col);
|
|
}
|
|
|
|
//+---------------------------------------------------------------+
|
|
//| Basic node API |
|
|
//+---------------------------------------------------------------+
|
|
int Root() const
|
|
{
|
|
return(m_root);
|
|
}
|
|
|
|
bool Exists(int node = -1)
|
|
{
|
|
int n = Resolve(node);
|
|
return(n >= 0 && n < m_node_used);
|
|
}
|
|
|
|
ENUM_JSON_TYPE GetType(int node = -1)
|
|
{
|
|
int n = Resolve(node);
|
|
|
|
if(n < 0 || n >= m_node_used)
|
|
return(JSON_NONE);
|
|
|
|
return((ENUM_JSON_TYPE)m_nodes[n].type);
|
|
}
|
|
|
|
bool IsObject(int node = -1)
|
|
{
|
|
return(GetType(node) == JSON_OBJECT);
|
|
}
|
|
|
|
bool IsArray(int node = -1)
|
|
{
|
|
return(GetType(node) == JSON_ARRAY);
|
|
}
|
|
|
|
bool IsString(int node = -1)
|
|
{
|
|
return(GetType(node) == JSON_STRING);
|
|
}
|
|
|
|
bool IsNumber(int node = -1)
|
|
{
|
|
return(GetType(node) == JSON_NUMBER);
|
|
}
|
|
|
|
bool IsBool(int node = -1)
|
|
{
|
|
return(GetType(node) == JSON_BOOL);
|
|
}
|
|
|
|
bool IsNull(int node = -1)
|
|
{
|
|
return(GetType(node) == JSON_NULL);
|
|
}
|
|
|
|
int Size(int node = -1)
|
|
{
|
|
int n = Resolve(node);
|
|
|
|
if(n < 0 || n >= m_node_used)
|
|
return(0);
|
|
|
|
uchar t = m_nodes[n].type;
|
|
|
|
if(t == (uchar)JSON_OBJECT || t == (uchar)JSON_ARRAY)
|
|
return(m_nodes[n].child_count);
|
|
|
|
return(0);
|
|
}
|
|
|
|
int Count(int node = -1)
|
|
{
|
|
return(Size(node));
|
|
}
|
|
|
|
//+---------------------------------------------------------------+
|
|
//| Child access |
|
|
//+---------------------------------------------------------------+
|
|
int GetChild(int parent, int index)
|
|
{
|
|
int p = (parent < 0 ? m_root : parent);
|
|
|
|
if(p < 0 || p >= m_node_used)
|
|
return(-1);
|
|
|
|
uchar t = m_nodes[p].type;
|
|
|
|
if(t != (uchar)JSON_OBJECT && t != (uchar)JSON_ARRAY)
|
|
return(-1);
|
|
|
|
if(index < 0 || index >= m_nodes[p].child_count)
|
|
return(-1);
|
|
|
|
return(m_children[m_nodes[p].first_child + index]);
|
|
}
|
|
|
|
int FindChild(int parent, uchar &key[], int key_len)
|
|
{
|
|
int p = (parent < 0 ? m_root : parent);
|
|
|
|
if(p < 0 || p >= m_node_used)
|
|
return(-1);
|
|
|
|
if(m_nodes[p].type != (uchar)JSON_OBJECT)
|
|
return(-1);
|
|
|
|
if(key_len < 0)
|
|
return(-1);
|
|
|
|
uint qhash = HashBytes(key, key_len);
|
|
|
|
if(m_hash_size > 0)
|
|
{
|
|
uint pos = HashMix(p, qhash) & (uint)m_hash_mask;
|
|
int scanned = 0;
|
|
|
|
while(m_hash_parent[pos] != -1 && scanned < m_hash_size)
|
|
{
|
|
if(m_hash_parent[pos] == p && m_hash_key[pos] == qhash)
|
|
{
|
|
int child = m_hash_node[pos];
|
|
if(KeyEquals(child, key, key_len, qhash))
|
|
return(child);
|
|
}
|
|
|
|
pos = (pos + 1) & (uint)m_hash_mask;
|
|
scanned++;
|
|
}
|
|
|
|
return(-1);
|
|
}
|
|
|
|
int offset = m_nodes[p].first_child;
|
|
int count = m_nodes[p].child_count;
|
|
|
|
for(int i = 0; i < count; i++)
|
|
{
|
|
int child = m_children[offset + i];
|
|
if(KeyEquals(child, key, key_len, qhash))
|
|
return(child);
|
|
}
|
|
|
|
return(-1);
|
|
}
|
|
|
|
int FindChild(int parent, const string &key)
|
|
{
|
|
uchar bytes[];
|
|
int len = StringToCharArray(key, bytes, 0, -1, JSON_CP_UTF8);
|
|
|
|
if(len < 0)
|
|
{
|
|
len = StringToCharArray(key, bytes);
|
|
if(len < 0)
|
|
return(-1);
|
|
}
|
|
|
|
if(len > 0 && bytes[len - 1] == 0)
|
|
len--;
|
|
|
|
return(FindChild(parent, bytes, len));
|
|
}
|
|
|
|
//+---------------------------------------------------------------+
|
|
//| Key and value access |
|
|
//+---------------------------------------------------------------+
|
|
string GetKey(int node)
|
|
{
|
|
if(node < 0 || node >= m_node_used)
|
|
return("");
|
|
|
|
int parent = m_nodes[node].parent;
|
|
|
|
if(parent < 0 || parent >= m_node_used)
|
|
return("");
|
|
|
|
if(m_nodes[parent].type != (uchar)JSON_OBJECT)
|
|
return("");
|
|
|
|
int len = m_nodes[node].key_len;
|
|
|
|
if(len <= 0)
|
|
return("");
|
|
|
|
int start = m_nodes[node].key_start;
|
|
|
|
if((m_nodes[node].flags & JSON_FLAG_KEY_DECODED) != 0)
|
|
return(CharArrayToString(m_pool, start, len, JSON_CP_UTF8));
|
|
|
|
return(CharArrayToString(Buffer, start, len, JSON_CP_UTF8));
|
|
}
|
|
|
|
string GetString(int node = -1)
|
|
{
|
|
int n = Resolve(node);
|
|
|
|
if(n < 0 || n >= m_node_used)
|
|
return("");
|
|
|
|
uchar t = m_nodes[n].type;
|
|
|
|
if(t == (uchar)JSON_STRING)
|
|
{
|
|
int len = m_nodes[n].val_len;
|
|
|
|
if(len <= 0)
|
|
return("");
|
|
|
|
int start = m_nodes[n].val_start;
|
|
|
|
if((m_nodes[n].flags & JSON_FLAG_VAL_DECODED) != 0)
|
|
return(CharArrayToString(m_pool, start, len, JSON_CP_UTF8));
|
|
|
|
return(CharArrayToString(Buffer, start, len, JSON_CP_UTF8));
|
|
}
|
|
|
|
// [CORRECION POR NIQUE](DOUBLE TO STRING NO TIENE UN 3 PARAMETRO)
|
|
// VERSION ORIGINAL: DoubleToString(m_nodes[n].num, 16, false)
|
|
// Correcion:
|
|
if(t == (uchar)JSON_NUMBER)
|
|
return(DoubleToString(m_nodes[n].num, 16));
|
|
|
|
if(t == (uchar)JSON_BOOL)
|
|
return(m_nodes[n].num != 0.0 ? "true" : "false");
|
|
|
|
if(t == (uchar)JSON_NULL)
|
|
return("null");
|
|
|
|
return("");
|
|
}
|
|
|
|
double GetDouble(int node = -1)
|
|
{
|
|
int n = Resolve(node);
|
|
|
|
if(n < 0 || n >= m_node_used)
|
|
return(0.0);
|
|
|
|
uchar t = m_nodes[n].type;
|
|
|
|
if(t == (uchar)JSON_NUMBER)
|
|
return(m_nodes[n].num);
|
|
|
|
if(t == (uchar)JSON_BOOL)
|
|
return(m_nodes[n].num);
|
|
|
|
if(t == (uchar)JSON_STRING)
|
|
{
|
|
string s = GetString(n);
|
|
// CORRECION POR NIQUE_372
|
|
// SOLO RECIBE 1 PARAMETRO NO 2.. ORIGINAL "StringToDouble(s, v)" (infiero que se pasa por referncia..)
|
|
// Correcion:
|
|
return StringToDouble(s);
|
|
}
|
|
|
|
return(0.0);
|
|
}
|
|
|
|
long GetInteger(int node = -1)
|
|
{
|
|
return((long)GetDouble(node));
|
|
}
|
|
|
|
bool GetBool(int node = -1)
|
|
{
|
|
int n = Resolve(node);
|
|
|
|
if(n < 0 || n >= m_node_used)
|
|
return(false);
|
|
|
|
uchar t = m_nodes[n].type;
|
|
|
|
if(t == (uchar)JSON_BOOL)
|
|
return(m_nodes[n].num != 0.0);
|
|
|
|
if(t == (uchar)JSON_NUMBER)
|
|
return(m_nodes[n].num != 0.0);
|
|
|
|
if(t == (uchar)JSON_STRING)
|
|
{
|
|
string s = GetString(n);
|
|
return(s == "true" || s == "1");
|
|
}
|
|
|
|
return(false);
|
|
}
|
|
|
|
//+---------------------------------------------------------------+
|
|
//| Convenient root access |
|
|
//+---------------------------------------------------------------+
|
|
JsonRef operator[](const string key)
|
|
{
|
|
// ORIGINAL: return(JsonRef(this, FindChild(m_root, key)));
|
|
// CORRECION POR NIQUE (THIS ES REF NO PTR)
|
|
return(JsonRef(&this, FindChild(m_root, key)));
|
|
}
|
|
|
|
JsonRef operator[](const int index)
|
|
{
|
|
// ORIGINAL: return(JsonRef(this, GetChild(m_root, index)));
|
|
// CORRECION POR NIQUE (THIS ES REF NO PTR)
|
|
return(JsonRef(&this, GetChild(m_root, index)));
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| JsonRef implementation |
|
|
//+------------------------------------------------------------------+
|
|
JsonRef JsonRef::operator[](const string key) // qyuite el & por ese error que mencione
|
|
{
|
|
if(json == NULL || node < 0)
|
|
return(JsonRef());
|
|
|
|
return(JsonRef(json, json.FindChild(node, key)));
|
|
}
|
|
|
|
JsonRef JsonRef::operator[](const int index)
|
|
{
|
|
if(json == NULL || node < 0)
|
|
return(JsonRef());
|
|
|
|
return(JsonRef(json, json.GetChild(node, index)));
|
|
}
|
|
|
|
bool JsonRef::Exists()
|
|
{
|
|
if(json == NULL || node < 0)
|
|
return(false);
|
|
|
|
return(json.Exists(node));
|
|
}
|
|
|
|
int JsonRef::Type()
|
|
{
|
|
if(json == NULL || node < 0)
|
|
return(JSON_NONE);
|
|
|
|
return(json.GetType(node));
|
|
}
|
|
|
|
int JsonRef::Size()
|
|
{
|
|
if(json == NULL || node < 0)
|
|
return(0);
|
|
|
|
return(json.Size(node));
|
|
}
|
|
|
|
double JsonRef::ToDouble()
|
|
{
|
|
if(json == NULL || node < 0)
|
|
return(0.0);
|
|
|
|
return(json.GetDouble(node));
|
|
}
|
|
|
|
long JsonRef::ToInteger()
|
|
{
|
|
if(json == NULL || node < 0)
|
|
return(0);
|
|
|
|
return(json.GetInteger(node));
|
|
}
|
|
|
|
bool JsonRef::ToBool()
|
|
{
|
|
if(json == NULL || node < 0)
|
|
return(false);
|
|
|
|
return(json.GetBool(node));
|
|
}
|
|
|
|
string JsonRef::ToString()
|
|
{
|
|
if(json == NULL || node < 0)
|
|
return("");
|
|
|
|
return(json.GetString(node));
|
|
}
|
|
|
|
bool JsonRef::IsObject()
|
|
{
|
|
return(Type() == JSON_OBJECT);
|
|
}
|
|
|
|
bool JsonRef::IsArray()
|
|
{
|
|
return(Type() == JSON_ARRAY);
|
|
}
|
|
|
|
bool JsonRef::IsString()
|
|
{
|
|
return(Type() == JSON_STRING);
|
|
}
|
|
|
|
bool JsonRef::IsNumber()
|
|
{
|
|
return(Type() == JSON_NUMBER);
|
|
}
|
|
|
|
bool JsonRef::IsBool()
|
|
{
|
|
return(Type() == JSON_BOOL);
|
|
}
|
|
|
|
bool JsonRef::IsNull()
|
|
{
|
|
return(Type() == JSON_NULL);
|
|
}
|
|
|
|
#endif
|
|
|