2026-07-19 08:34:18 -05:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Main.mqh |
|
|
|
|
|
//| Copyright 2026,Niquel Mendoza. |
|
|
|
|
|
//| https://www.mql5.com/en/users/nique_372 |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#property copyright "Copyright 2026,Niquel Mendoza."
|
|
|
|
|
#property link "https://www.mql5.com/en/users/nique_372"
|
|
|
|
|
#property strict
|
|
|
|
|
|
2026-07-21 11:09:28 -05:00
|
|
|
#ifndef FASTCOLLECTIONSBYLEO_COLHASH_DICT_MAIN_MQH
|
|
|
|
|
#define FASTCOLLECTIONSBYLEO_COLHASH_DICT_MAIN_MQH
|
2026-07-19 08:34:18 -05:00
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#include "Defines.mqh"
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#define MQLARTICES_DICT_R_SIZE (4*MQLARTICLES_DICT_SLOT_SIZE)
|
|
|
|
|
#define MQLARTICLES_DICT_BIT_START_BOOL_V (60)
|
|
|
|
|
#define MQLARTICLES_DICT_BIT_START_SALTAR (52)
|
|
|
|
|
#define MQLARTICLES_DICT_BIT_END_C_TYPE (20)
|
|
|
|
|
#define MQLARTICLES_DICT_BIT_END_R_TYPE (4)
|
|
|
|
|
|
|
|
|
|
//--- Mascaras (en 'long' para evitar overflow de literal 32bit)
|
|
|
|
|
#define MQLARTICLES_DICT_MASK_R_TYPE (0xF)
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
#define MQLARTICLES_DICT_TYPE(ref) ENUM_MQLARTICLES_DICT_DTYPE(ref & MQLARTICLES_DICT_MASK_R_TYPE)
|
|
|
|
|
#define MQLARTICLES_DICT_CTYPE(ref) short(ref >> MQLARTICLES_DICT_BIT_END_R_TYPE)
|
|
|
|
|
#define MQLARTICLES_DICT_KEYIDX(ref) int(ref >> MQLARTICLES_DICT_BIT_END_C_TYPE)
|
|
|
|
|
#define MQLARTICLES_DICT_SALTAR(ref) int(ref >> MQLARTICLES_DICT_BIT_START_SALTAR)
|
|
|
|
|
#define MQLARTICLES_DICT_BOOLV(ref) bool((ref >> MQLARTICLES_DICT_BIT_START_BOOL_V) & 1)
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
#define MQLARTICLES_DICT_PACK(dtype, ctype, keyidx, saltar) \
|
|
|
|
|
( long(dtype) | \
|
|
|
|
|
long(ctype) << MQLARTICLES_DICT_BIT_END_R_TYPE | \
|
|
|
|
|
long(keyidx) << MQLARTICLES_DICT_BIT_END_C_TYPE | \
|
|
|
|
|
long(saltar) << MQLARTICLES_DICT_BIT_START_SALTAR )
|
|
|
|
|
|
|
|
|
|
#define MQLARTICLES_DICT_PACK_BOOL(ctype, keyidx, saltar, boolval) \
|
|
|
|
|
( MQLARTICLES_DICT_PACK(MQLARTICLES_DICT_DTYPE_BOOLEAN, ctype, keyidx, saltar) | \
|
|
|
|
|
(long(boolval) << MQLARTICLES_DICT_BIT_START_BOOL_V) )
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
enum ENUM_MQLARTICLES_DICT_DTYPE
|
|
|
|
|
{
|
|
|
|
|
MQLARTICLES_DICT_DTYPE_STRING,
|
|
|
|
|
MQLARTICLES_DICT_DTYPE_REAL,
|
|
|
|
|
MQLARTICLES_DICT_DTYPE_INTEGER,
|
|
|
|
|
MQLARTICLES_DICT_DTYPE_BOOLEAN,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define MQLARTICLES_DICT_RESERVE_STR(R) \
|
|
|
|
|
if(m_strings_size + (R) >= m_strings_reserve) \
|
|
|
|
|
{ \
|
|
|
|
|
m_strings_reserve += ((R) << 1) + 8; \
|
|
|
|
|
ArrayResize(m_strings, m_strings_reserve); \
|
|
|
|
|
} \
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
// General (ref=[4bit=RealType|16Bit=CType|KeyIndex=32Bit|ASaltar=8Bit])([64bit=KeyHash])
|
|
|
|
|
// Integer ocupa un slot mas (total 3): [64bit=Number]
|
|
|
|
|
// Real ocupa un slot mas (total 3): [64bit=Real Number]
|
|
|
|
|
// String ocupa un slot mas (total 3): [64bit=String index]
|
|
|
|
|
// Bool no ocupa otro slot.. sin oque aprovecha el slot 0 (total 2): [*ref|1bit=bool_value)
|
|
|
|
|
//
|
|
|
|
|
// "ASaltar" = tamano en slots de la entrada (2 para bool, 3 para el resto).
|
|
|
|
|
// Se usa para recorrer el bucket saltando entradas completas (i += saltar)
|
|
|
|
|
// y tambien para saber cuantos slots copiar/mover en Resize()/Remove().
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Clase CDict |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
namespace TSN
|
|
|
|
|
{
|
|
|
|
|
class CDict
|
|
|
|
|
{
|
|
|
|
|
protected:
|
|
|
|
|
//---
|
|
|
|
|
string m_strings[];
|
|
|
|
|
int m_strings_size;
|
|
|
|
|
int m_strings_reserve;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
struct DictBucket
|
|
|
|
|
{
|
|
|
|
|
long slots[MQLARTICES_DICT_R_SIZE];
|
|
|
|
|
int pos; // slots ocupados (no numero de entradas)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
union UDoubleLong { double d; long l; };
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
DictBucket m_buckets[];
|
|
|
|
|
int m_buckets_size;
|
|
|
|
|
int m_count;
|
|
|
|
|
|
|
|
|
|
//--- Resize
|
|
|
|
|
void Resize();
|
|
|
|
|
template <typename TKey>
|
|
|
|
|
void Assing(const TKey& key[], int i, const int end, string& out);
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
CDict(void) { Init(64, 4); }
|
|
|
|
|
~CDict(void) {}
|
|
|
|
|
|
|
|
|
|
//--- Init
|
|
|
|
|
void Init(int buckets_size, int slots_reserve);
|
|
|
|
|
|
|
|
|
|
//--- Set
|
|
|
|
|
bool Set(const string& key, const string val, short cts_type);
|
|
|
|
|
template <typename TKey>
|
|
|
|
|
bool Set(const TKey& key[], const int s, const int end, const string val, short cts_type);
|
|
|
|
|
|
|
|
|
|
bool Set(const string& key, const long val);
|
|
|
|
|
bool Set(const string& key, const bool val);
|
|
|
|
|
bool Set(const string& key, const double val);
|
|
|
|
|
|
|
|
|
|
//--- Get
|
|
|
|
|
bool TryGet(const string& key, string& val) const;
|
|
|
|
|
template <typename TKey>
|
|
|
|
|
bool TryGet(const TKey& key[], const int s, const int end, string& val) const;
|
|
|
|
|
|
|
|
|
|
bool TryGet(const string& key, long& val) const;
|
|
|
|
|
bool TryGet(const string& key, bool& val) const;
|
|
|
|
|
bool TryGet(const string& key, double& val) const;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//--- Utils
|
|
|
|
|
bool Contains(const string& key) const;
|
|
|
|
|
bool Remove(const string& key);
|
|
|
|
|
void Clear();
|
|
|
|
|
__forceinline int Count() const { return m_count; }
|
|
|
|
|
|
|
|
|
|
//--- Get all (values como string para "universal")
|
|
|
|
|
int GetValues(string& out[], string& keys[]) const;
|
|
|
|
|
int GetKeys(string& keys[]) const;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CDict::Init(int buckets_size, int slots_reserve)
|
|
|
|
|
{
|
|
|
|
|
m_buckets_size = fmax(buckets_size, 32);
|
|
|
|
|
slots_reserve = fmax(slots_reserve, 4);
|
|
|
|
|
m_count = 0;
|
|
|
|
|
ArrayResize(m_buckets, m_buckets_size);
|
|
|
|
|
for(int i = 0; i < m_buckets_size; i++)
|
|
|
|
|
{
|
|
|
|
|
m_buckets[i].pos = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
m_strings_size = 0;
|
|
|
|
|
m_strings_reserve = slots_reserve;
|
|
|
|
|
ArrayResize(m_strings, m_strings_reserve);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CDict::Resize()
|
|
|
|
|
{
|
|
|
|
|
const int new_size = m_buckets_size << 1;
|
|
|
|
|
DictBucket new_buckets[];
|
|
|
|
|
ArrayResize(new_buckets, new_size);
|
|
|
|
|
for(int i = 0; i < new_size; i++)
|
|
|
|
|
{
|
|
|
|
|
new_buckets[i].pos = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < m_buckets_size; i++)
|
|
|
|
|
{
|
|
|
|
|
const int t = m_buckets[i].pos;
|
|
|
|
|
int p = 0;
|
|
|
|
|
while(p < t)
|
|
|
|
|
{
|
|
|
|
|
const long ref = m_buckets[i].slots[p];
|
|
|
|
|
const int saltar = MQLARTICLES_DICT_SALTAR(ref);
|
|
|
|
|
const ulong hash = ulong(m_buckets[i].slots[p + 1]);
|
|
|
|
|
const uint idx = uint(hash % (ulong)new_size);
|
|
|
|
|
const int dst = new_buckets[idx].pos;
|
|
|
|
|
for(int k = 0; k < saltar; k++)
|
|
|
|
|
new_buckets[idx].slots[dst + k] = m_buckets[i].slots[p + k];
|
|
|
|
|
new_buckets[idx].pos += saltar;
|
|
|
|
|
p += saltar;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ArraySwap(m_buckets, new_buckets);
|
|
|
|
|
m_buckets_size = new_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
template <typename TKey>
|
|
|
|
|
void CDict::Assing(const TKey &key[], int i, const int end, string &out)
|
|
|
|
|
{
|
|
|
|
|
out.Truncate((end - i) + 1);
|
|
|
|
|
int k = 0;
|
|
|
|
|
for(; i <= end; i++)
|
|
|
|
|
{
|
|
|
|
|
out.SetChar(k++, key[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
// Notas:
|
|
|
|
|
// - En caso no exista lo crea
|
|
|
|
|
// - En caso se supera el factor de crecimiento 75% entonces resize al doble
|
|
|
|
|
bool CDict::Set(const string& key, const string val, short cts_type)
|
|
|
|
|
{
|
|
|
|
|
//--- Check size
|
|
|
|
|
if(m_count > 0 && float(m_count) / float(m_buckets_size) > MQLARTICLES_DICT_FACTOR_CRECIMIENTO)
|
|
|
|
|
Resize();
|
|
|
|
|
|
|
|
|
|
//--- Alg
|
|
|
|
|
ulong hash = FNV_OFFSET_BASIS;
|
|
|
|
|
const int len = StringLen(key);
|
|
|
|
|
FNV1a_64_AsM_Str(key, len, hash, FNV_PRIME)
|
|
|
|
|
|
|
|
|
|
//--- Index
|
|
|
|
|
const uint idx = uint(hash % (ulong)m_buckets_size);
|
|
|
|
|
const int t = m_buckets[idx].pos;
|
|
|
|
|
|
|
|
|
|
//--- Linear (recorre entradas completas, no slots sueltos)
|
|
|
|
|
int i = 0;
|
|
|
|
|
while(i < t)
|
|
|
|
|
{
|
|
|
|
|
const long ref = m_buckets[idx].slots[i];
|
|
|
|
|
if(ulong(m_buckets[idx].slots[i + 1]) == hash)
|
|
|
|
|
{
|
|
|
|
|
if(MQLARTICLES_DICT_TYPE(ref) == MQLARTICLES_DICT_DTYPE_STRING)
|
|
|
|
|
{
|
|
|
|
|
m_strings[m_buckets[idx].slots[i + 2]] = val;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
// Existe con otro tipo: no se soporta cambio de tipo in-place
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
i += MQLARTICLES_DICT_SALTAR(ref);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
if(t + 3 >= MQLARTICES_DICT_R_SIZE)
|
|
|
|
|
{
|
|
|
|
|
Resize();
|
|
|
|
|
return Set(key, val, cts_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--- Reservar strings (key + value), inline
|
|
|
|
|
MQLARTICLES_DICT_RESERVE_STR(2)
|
|
|
|
|
const int string_index = m_strings_size++;
|
|
|
|
|
m_strings[string_index] = val;
|
|
|
|
|
const int key_index = m_strings_size++;
|
|
|
|
|
m_strings[key_index] = key;
|
|
|
|
|
|
|
|
|
|
//--- Bucket
|
|
|
|
|
m_buckets[idx].slots[t] = MQLARTICLES_DICT_PACK(MQLARTICLES_DICT_DTYPE_STRING, cts_type, key_index, 3);
|
|
|
|
|
m_buckets[idx].slots[t + 1] = long(hash);
|
|
|
|
|
m_buckets[idx].slots[t + 2] = string_index;
|
|
|
|
|
m_buckets[idx].pos += 3;
|
|
|
|
|
m_count++;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
template <typename TKey>
|
|
|
|
|
bool CDict::Set(const TKey& key[], const int s, const int end, const string val, short cts_type)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
if(s < 0 || s > end)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
//--- Check size
|
|
|
|
|
if(m_count > 0 && float(m_count) / float(m_buckets_size) > MQLARTICLES_DICT_FACTOR_CRECIMIENTO)
|
|
|
|
|
Resize();
|
|
|
|
|
|
|
|
|
|
//--- Alg
|
|
|
|
|
ulong hash = FNV_OFFSET_BASIS;
|
|
|
|
|
for(int i = s; i <= end; i++)
|
|
|
|
|
{
|
|
|
|
|
h ^= (uchar)key[i];
|
|
|
|
|
h *= FNV_PRIME;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//--- Index
|
|
|
|
|
const uint idx = uint(hash % (ulong)m_buckets_size);
|
|
|
|
|
const int t = m_buckets[idx].pos;
|
|
|
|
|
|
|
|
|
|
//--- Linear (recorre entradas completas, no slots sueltos)
|
|
|
|
|
int i = 0;
|
|
|
|
|
while(i < t)
|
|
|
|
|
{
|
|
|
|
|
const long ref = m_buckets[idx].slots[i];
|
|
|
|
|
if(ulong(m_buckets[idx].slots[i + 1]) == hash)
|
|
|
|
|
{
|
|
|
|
|
if(MQLARTICLES_DICT_TYPE(ref) == MQLARTICLES_DICT_DTYPE_STRING)
|
|
|
|
|
{
|
|
|
|
|
m_strings[m_buckets[idx].slots[i + 2]] = val;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
// Existe con otro tipo: no se soporta cambio de tipo in-place
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
i += MQLARTICLES_DICT_SALTAR(ref);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
if(t + 3 >= MQLARTICES_DICT_R_SIZE)
|
|
|
|
|
{
|
|
|
|
|
Resize();
|
|
|
|
|
return Set(key, s, end, val, cts_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--- Reservar strings (key + value), inline
|
|
|
|
|
MQLARTICLES_DICT_RESERVE_STR(2)
|
|
|
|
|
const int string_index = m_strings_size++;
|
|
|
|
|
m_strings[string_index] = val;
|
|
|
|
|
const int key_index = m_strings_size++;
|
|
|
|
|
Assing(key, s, end, m_strings[key_index]);
|
|
|
|
|
|
|
|
|
|
//--- Bucket
|
|
|
|
|
m_buckets[idx].slots[t] = MQLARTICLES_DICT_PACK(MQLARTICLES_DICT_DTYPE_STRING, cts_type, key_index, 3);
|
|
|
|
|
m_buckets[idx].slots[t + 1] = long(hash);
|
|
|
|
|
m_buckets[idx].slots[t + 2] = string_index;
|
|
|
|
|
m_buckets[idx].pos += 3;
|
|
|
|
|
m_count++;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CDict::Set(const string& key, const long val)
|
|
|
|
|
{
|
|
|
|
|
if(m_count > 0 && float(m_count) / float(m_buckets_size) > MQLARTICLES_DICT_FACTOR_CRECIMIENTO)
|
|
|
|
|
Resize();
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
ulong hash = FNV_OFFSET_BASIS;
|
|
|
|
|
const int len = StringLen(key);
|
|
|
|
|
FNV1a_64_AsM_Str(key, len, hash, FNV_PRIME)
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
const uint idx = uint(hash % (ulong)m_buckets_size);
|
|
|
|
|
const int t = m_buckets[idx].pos;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
int i = 0;
|
|
|
|
|
while(i < t)
|
|
|
|
|
{
|
|
|
|
|
const long ref = m_buckets[idx].slots[i];
|
|
|
|
|
if(ulong(m_buckets[idx].slots[i + 1]) == hash)
|
|
|
|
|
{
|
|
|
|
|
if(MQLARTICLES_DICT_TYPE(ref) == MQLARTICLES_DICT_DTYPE_INTEGER)
|
|
|
|
|
{
|
|
|
|
|
m_buckets[idx].slots[i + 2] = val;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
i += MQLARTICLES_DICT_SALTAR(ref);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
if(t + 3 >= MQLARTICES_DICT_R_SIZE)
|
|
|
|
|
{
|
|
|
|
|
Resize();
|
|
|
|
|
return Set(key, val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
MQLARTICLES_DICT_RESERVE_STR(1)
|
|
|
|
|
const int key_index = m_strings_size++;
|
|
|
|
|
m_strings[key_index] = key;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
m_buckets[idx].slots[t] = MQLARTICLES_DICT_PACK(MQLARTICLES_DICT_DTYPE_INTEGER, 0, key_index, 3);
|
|
|
|
|
m_buckets[idx].slots[t + 1] = long(hash);
|
|
|
|
|
m_buckets[idx].slots[t + 2] = val;
|
|
|
|
|
m_buckets[idx].pos += 3;
|
|
|
|
|
m_count++;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CDict::Set(const string& key, const double val)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
if(m_count > 0 && float(m_count) / float(m_buckets_size) > MQLARTICLES_DICT_FACTOR_CRECIMIENTO)
|
|
|
|
|
Resize();
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
ulong hash = FNV_OFFSET_BASIS;
|
|
|
|
|
const int len = StringLen(key);
|
|
|
|
|
FNV1a_64_AsM_Str(key, len, hash, FNV_PRIME)
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
const uint idx = uint(hash % (ulong)m_buckets_size);
|
|
|
|
|
const int t = m_buckets[idx].pos;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
|
|
|
|
|
UDoubleLong conv;
|
|
|
|
|
conv.d = val;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
int i = 0;
|
|
|
|
|
while(i < t)
|
|
|
|
|
{
|
|
|
|
|
const long ref = m_buckets[idx].slots[i];
|
|
|
|
|
if(ulong(m_buckets[idx].slots[i + 1]) == hash)
|
|
|
|
|
{
|
|
|
|
|
if(MQLARTICLES_DICT_TYPE(ref) == MQLARTICLES_DICT_DTYPE_REAL)
|
|
|
|
|
{
|
|
|
|
|
m_buckets[idx].slots[i + 2] = conv.l;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
i += MQLARTICLES_DICT_SALTAR(ref);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--- ;
|
|
|
|
|
if(t + 3 >= MQLARTICES_DICT_R_SIZE)
|
|
|
|
|
{
|
|
|
|
|
Resize();
|
|
|
|
|
return Set(key, val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
MQLARTICLES_DICT_RESERVE_STR(1)
|
|
|
|
|
const int key_index = m_strings_size++;
|
|
|
|
|
m_strings[key_index] = key;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
m_buckets[idx].slots[t] = MQLARTICLES_DICT_PACK(MQLARTICLES_DICT_DTYPE_REAL, 0, key_index, 3);
|
|
|
|
|
m_buckets[idx].slots[t + 1] = long(hash);
|
|
|
|
|
m_buckets[idx].slots[t + 2] = conv.l;
|
|
|
|
|
m_buckets[idx].pos += 3;
|
|
|
|
|
m_count++;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CDict::Set(const string& key, const bool val)
|
|
|
|
|
{
|
|
|
|
|
if(m_count > 0 && float(m_count) / float(m_buckets_size) > MQLARTICLES_DICT_FACTOR_CRECIMIENTO)
|
|
|
|
|
Resize();
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
ulong hash = FNV_OFFSET_BASIS;
|
|
|
|
|
const int len = StringLen(key);
|
|
|
|
|
FNV1a_64_AsM_Str(key, len, hash, FNV_PRIME)
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
const uint idx = uint(hash % (ulong)m_buckets_size);
|
|
|
|
|
const int t = m_buckets[idx].pos;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
int i = 0;
|
|
|
|
|
while(i < t)
|
|
|
|
|
{
|
|
|
|
|
const long ref = m_buckets[idx].slots[i];
|
|
|
|
|
const int saltar = MQLARTICLES_DICT_SALTAR(ref);
|
|
|
|
|
if(ulong(m_buckets[idx].slots[i + 1]) == hash)
|
|
|
|
|
{
|
|
|
|
|
if(MQLARTICLES_DICT_TYPE(ref) == MQLARTICLES_DICT_DTYPE_BOOLEAN)
|
|
|
|
|
{
|
|
|
|
|
m_buckets[idx].slots[i] = MQLARTICLES_DICT_PACK_BOOL(MQLARTICLES_DICT_CTYPE(ref), MQLARTICLES_DICT_KEYIDX(ref), saltar, val);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
i += saltar;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
if(t + 2 >= MQLARTICES_DICT_R_SIZE)
|
|
|
|
|
{
|
|
|
|
|
Resize();
|
|
|
|
|
return Set(key, val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
MQLARTICLES_DICT_RESERVE_STR(1)
|
|
|
|
|
const int key_index = m_strings_size++;
|
|
|
|
|
m_strings[key_index] = key;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
m_buckets[idx].slots[t] = MQLARTICLES_DICT_PACK_BOOL(0, key_index, 2, val);
|
|
|
|
|
m_buckets[idx].slots[t + 1] = long(hash);
|
|
|
|
|
m_buckets[idx].pos += 2;
|
|
|
|
|
m_count++;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CDict::TryGet(const string& key, string& val) const
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
ulong hash = FNV_OFFSET_BASIS;
|
|
|
|
|
const int len = StringLen(key);
|
|
|
|
|
FNV1a_64_AsM_Str(key, len, hash, FNV_PRIME)
|
|
|
|
|
const uint idx = uint(hash % (ulong)m_buckets_size);
|
|
|
|
|
const int t = m_buckets[idx].pos;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
UDoubleLong conv;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
int i = 0;
|
|
|
|
|
while(i < t)
|
|
|
|
|
{
|
|
|
|
|
const long ref = m_buckets[idx].slots[i];
|
|
|
|
|
if(ulong(m_buckets[idx].slots[i + 1]) == hash)
|
|
|
|
|
{
|
|
|
|
|
switch(MQLARTICLES_DICT_TYPE(ref))
|
|
|
|
|
{
|
|
|
|
|
case MQLARTICLES_DICT_DTYPE_STRING:
|
|
|
|
|
val = m_strings[m_buckets[idx].slots[i + 2]];
|
|
|
|
|
break;
|
|
|
|
|
case MQLARTICLES_DICT_DTYPE_INTEGER:
|
|
|
|
|
val = string(m_buckets[idx].slots[i + 2]);
|
|
|
|
|
break;
|
|
|
|
|
case MQLARTICLES_DICT_DTYPE_REAL:
|
|
|
|
|
conv.l = m_buckets[idx].slots[i + 2];
|
|
|
|
|
val = string(conv.d);
|
|
|
|
|
break;
|
|
|
|
|
case MQLARTICLES_DICT_DTYPE_BOOLEAN:
|
|
|
|
|
val = MQLARTICLES_DICT_BOOLV(ref) ? "true" : "false";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
i += MQLARTICLES_DICT_SALTAR(ref);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
template <typename TKey>
|
|
|
|
|
bool CDict::TryGet(const TKey& key[], const int s, const int end, string& val) const
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
if(s < 0 || s > end)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
//--- Alg
|
|
|
|
|
ulong hash = FNV_OFFSET_BASIS;
|
|
|
|
|
for(int i = s; i <= end; i++)
|
|
|
|
|
{
|
|
|
|
|
hash ^= (uchar)key[i];
|
|
|
|
|
hash *= FNV_PRIME;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
const uint idx = uint(hash % (ulong)m_buckets_size);
|
|
|
|
|
const int t = m_buckets[idx].pos;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
UDoubleLong conv;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
int i = 0;
|
|
|
|
|
while(i < t)
|
|
|
|
|
{
|
|
|
|
|
const long ref = m_buckets[idx].slots[i];
|
|
|
|
|
if(ulong(m_buckets[idx].slots[i + 1]) == hash)
|
|
|
|
|
{
|
|
|
|
|
switch(MQLARTICLES_DICT_TYPE(ref))
|
|
|
|
|
{
|
|
|
|
|
case MQLARTICLES_DICT_DTYPE_STRING:
|
|
|
|
|
val = m_strings[m_buckets[idx].slots[i + 2]];
|
|
|
|
|
break;
|
|
|
|
|
case MQLARTICLES_DICT_DTYPE_INTEGER:
|
|
|
|
|
val = string(m_buckets[idx].slots[i + 2]);
|
|
|
|
|
break;
|
|
|
|
|
case MQLARTICLES_DICT_DTYPE_REAL:
|
|
|
|
|
conv.l = m_buckets[idx].slots[i + 2];
|
|
|
|
|
val = string(conv.d);
|
|
|
|
|
break;
|
|
|
|
|
case MQLARTICLES_DICT_DTYPE_BOOLEAN:
|
|
|
|
|
val = MQLARTICLES_DICT_BOOLV(ref) ? "true" : "false";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
i += MQLARTICLES_DICT_SALTAR(ref);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CDict::TryGet(const string& key, long& val) const
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
ulong hash = FNV_OFFSET_BASIS;
|
|
|
|
|
const int len = StringLen(key);
|
|
|
|
|
FNV1a_64_AsM_Str(key, len, hash, FNV_PRIME)
|
|
|
|
|
const uint idx = uint(hash % (ulong)m_buckets_size);
|
|
|
|
|
const int t = m_buckets[idx].pos;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
int i = 0;
|
|
|
|
|
while(i < t)
|
|
|
|
|
{
|
|
|
|
|
const long ref = m_buckets[idx].slots[i];
|
|
|
|
|
if(ulong(m_buckets[idx].slots[i + 1]) == hash && MQLARTICLES_DICT_TYPE(ref) == MQLARTICLES_DICT_DTYPE_INTEGER)
|
|
|
|
|
{
|
|
|
|
|
val = m_buckets[idx].slots[i + 2];
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
i += MQLARTICLES_DICT_SALTAR(ref);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CDict::TryGet(const string& key, double& val) const
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
ulong hash = FNV_OFFSET_BASIS;
|
|
|
|
|
const int len = StringLen(key);
|
|
|
|
|
FNV1a_64_AsM_Str(key, len, hash, FNV_PRIME)
|
|
|
|
|
const uint idx = uint(hash % (ulong)m_buckets_size);
|
|
|
|
|
const int t = m_buckets[idx].pos;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
|
|
|
|
|
UDoubleLong conv;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
int i = 0;
|
|
|
|
|
while(i < t)
|
|
|
|
|
{
|
|
|
|
|
const long ref = m_buckets[idx].slots[i];
|
|
|
|
|
if(ulong(m_buckets[idx].slots[i + 1]) == hash && MQLARTICLES_DICT_TYPE(ref) == MQLARTICLES_DICT_DTYPE_REAL)
|
|
|
|
|
{
|
|
|
|
|
conv.l = m_buckets[idx].slots[i + 2];
|
|
|
|
|
val = conv.d;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
i += MQLARTICLES_DICT_SALTAR(ref);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CDict::TryGet(const string& key, bool& val) const
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
ulong hash = FNV_OFFSET_BASIS;
|
|
|
|
|
const int len = StringLen(key);
|
|
|
|
|
FNV1a_64_AsM_Str(key, len, hash, FNV_PRIME)
|
|
|
|
|
const uint idx = uint(hash % (ulong)m_buckets_size);
|
|
|
|
|
const int t = m_buckets[idx].pos;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
int i = 0;
|
|
|
|
|
while(i < t)
|
|
|
|
|
{
|
|
|
|
|
const long ref = m_buckets[idx].slots[i];
|
|
|
|
|
if(ulong(m_buckets[idx].slots[i + 1]) == hash && MQLARTICLES_DICT_TYPE(ref) == MQLARTICLES_DICT_DTYPE_BOOLEAN)
|
|
|
|
|
{
|
|
|
|
|
val = MQLARTICLES_DICT_BOOLV(ref);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
i += MQLARTICLES_DICT_SALTAR(ref);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CDict::Contains(const string& key) const
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
ulong hash = FNV_OFFSET_BASIS;
|
|
|
|
|
const int len = StringLen(key);
|
|
|
|
|
FNV1a_64_AsM_Str(key, len, hash, FNV_PRIME)
|
|
|
|
|
const uint idx = uint(hash % (ulong)m_buckets_size);
|
|
|
|
|
const int t = m_buckets[idx].pos;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
int i = 0;
|
|
|
|
|
while(i < t)
|
|
|
|
|
{
|
|
|
|
|
const long ref = m_buckets[idx].slots[i];
|
|
|
|
|
if(ulong(m_buckets[idx].slots[i + 1]) == hash)
|
|
|
|
|
return true;
|
|
|
|
|
i += MQLARTICLES_DICT_SALTAR(ref);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
// Notas:
|
|
|
|
|
// - Se compacta el bucket desplazando el resto de entradas 'saltar'
|
|
|
|
|
// posiciones a la izquierda (no vale el swap-with-last clasico porque
|
|
|
|
|
// las entradas tienen tamano variable: 2 slots para bool, 3 para el resto).
|
|
|
|
|
// - No se liberan los strings huerfanos en m_strings (limitacion conocida,
|
|
|
|
|
// igual que en la version anterior).
|
|
|
|
|
bool CDict::Remove(const string& key)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
ulong hash = FNV_OFFSET_BASIS;
|
|
|
|
|
const int len = StringLen(key);
|
|
|
|
|
FNV1a_64_AsM_Str(key, len, hash, FNV_PRIME)
|
|
|
|
|
const uint idx = uint(hash % (ulong)m_buckets_size);
|
|
|
|
|
const int t = m_buckets[idx].pos;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
int i = 0;
|
|
|
|
|
while(i < t)
|
|
|
|
|
{
|
|
|
|
|
const long ref = m_buckets[idx].slots[i];
|
|
|
|
|
const int saltar = MQLARTICLES_DICT_SALTAR(ref);
|
|
|
|
|
if(ulong(m_buckets[idx].slots[i + 1]) == hash)
|
|
|
|
|
{
|
|
|
|
|
for(int k = i + saltar; k < t; k++)
|
|
|
|
|
m_buckets[idx].slots[k - saltar] = m_buckets[idx].slots[k];
|
|
|
|
|
m_buckets[idx].pos -= saltar;
|
|
|
|
|
m_count--;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
i += saltar;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CDict::Clear()
|
|
|
|
|
{
|
|
|
|
|
for(int i = 0; i < m_buckets_size; i++)
|
|
|
|
|
m_buckets[i].pos = 0;
|
|
|
|
|
m_count = 0;
|
|
|
|
|
m_strings_size = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
int CDict::GetValues(string& out[], string& keys[]) const
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
int r = ArrayResize(out, m_buckets_size);
|
|
|
|
|
ArrayResize(keys, r);
|
|
|
|
|
int f = 0;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
UDoubleLong conv;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
for(int i = 0; i < m_buckets_size; i++)
|
|
|
|
|
{
|
|
|
|
|
const int t = m_buckets[i].pos;
|
|
|
|
|
int p = 0;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
while(p < t)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
const long ref = m_buckets[i].slots[p];
|
|
|
|
|
const int saltar = MQLARTICLES_DICT_SALTAR(ref);
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
keys[f] = m_strings[MQLARTICLES_DICT_KEYIDX(ref)];
|
|
|
|
|
switch(MQLARTICLES_DICT_TYPE(ref))
|
|
|
|
|
{
|
|
|
|
|
case MQLARTICLES_DICT_DTYPE_STRING:
|
|
|
|
|
out[f] = m_strings[m_buckets[i].slots[p + 2]];
|
|
|
|
|
break;
|
|
|
|
|
case MQLARTICLES_DICT_DTYPE_INTEGER:
|
|
|
|
|
out[f] = IntegerToString(m_buckets[i].slots[p + 2]);
|
|
|
|
|
break;
|
|
|
|
|
case MQLARTICLES_DICT_DTYPE_REAL:
|
|
|
|
|
conv.l = m_buckets[i].slots[p + 2];
|
|
|
|
|
out[f] = DoubleToString(conv.d);
|
|
|
|
|
break;
|
|
|
|
|
case MQLARTICLES_DICT_DTYPE_BOOLEAN:
|
|
|
|
|
out[f] = MQLARTICLES_DICT_BOOLV(ref) ? "true" : "false";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
f++;
|
|
|
|
|
if(f >= r)
|
|
|
|
|
{
|
|
|
|
|
r <<= 1;
|
|
|
|
|
ArrayResize(out, r, r);
|
|
|
|
|
ArrayResize(keys, r, r);
|
|
|
|
|
}
|
|
|
|
|
p += saltar;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
ArrayResize(keys, f);
|
|
|
|
|
return ArrayResize(out, f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
int CDict::GetKeys(string &keys[]) const
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
int r = ArrayResize(keys, m_buckets_size);
|
|
|
|
|
ArrayResize(keys, r);
|
|
|
|
|
int f = 0;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
for(int i = 0; i < m_buckets_size; i++)
|
|
|
|
|
{
|
|
|
|
|
const int t = m_buckets[i].pos;
|
|
|
|
|
int p = 0;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
while(p < t)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
const long ref = m_buckets[i].slots[p];
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
keys[f++] = m_strings[MQLARTICLES_DICT_KEYIDX(ref)];
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
if(f >= r)
|
|
|
|
|
{
|
|
|
|
|
r <<= 1;
|
|
|
|
|
ArrayResize(keys, r, r);
|
|
|
|
|
}
|
|
|
|
|
p += MQLARTICLES_DICT_SALTAR(ref);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
return ArrayResize(keys, f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-07-21 11:09:28 -05:00
|
|
|
#endif // FASTCOLLECTIONSBYLEO_COLHASH_DICT_MAIN_MQH
|