//+------------------------------------------------------------------+ //| CDictionary.mqh | //| Copyright 2015, Vasiliy Sokolov. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2015, Vasiliy Sokolov." #property link "http://www.mql5.com" #include #include //+------------------------------------------------------------------+ //| Container to store CObject elements | //+------------------------------------------------------------------+ class KeyValuePair : public CObject { private: string m_string_key; // Stores a string key. double m_double_key; // Stores a floating-point coma. ulong m_ulong_key; // Stores an unsigned integer key. ulong m_hash; bool m_free_mode; // Object memory freeing mode public: CObject *object; KeyValuePair *next_kvp; KeyValuePair *prev_kvp; template KeyValuePair(T key,ulong hash,CObject *obj); ~KeyValuePair(); template bool EqualKey(T key); template void GetKey(T &gkey); ulong GetHash(){return m_hash;} void FreeMode(bool free_mode){m_free_mode=free_mode;} bool FreeMode(void){return m_free_mode;} }; //+------------------------------------------------------------------+ //| Default constructor. | //+------------------------------------------------------------------+ template void KeyValuePair::KeyValuePair(T key,ulong hash,CObject *obj) { m_hash=hash; string name=typename(key); if(name=="string") m_string_key=(string)key; else if(name=="double" || name=="float") m_double_key=(double)key; else m_ulong_key=(ulong)key; object=obj; m_free_mode=true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template KeyValuePair::GetKey(T &gkey) { string name=typename(gkey); if(name=="string") gkey=(T)m_string_key; else if(name=="double" || name=="float") gkey=(T)m_double_key; else gkey=(T)m_ulong_key; } //+------------------------------------------------------------------+ //| Destructor. | //+------------------------------------------------------------------+ KeyValuePair::~KeyValuePair() { if(m_free_mode) delete object; } //+------------------------------------------------------------------+ //| Returns true, if keys are equal. | //+------------------------------------------------------------------+ template bool KeyValuePair::EqualKey(T key) { string name=typename(key); if(name=="string") return m_string_key == (string)key; if(name=="double" || name=="float") return m_double_key == (double)key; else return m_ulong_key == (ulong)key; } //+------------------------------------------------------------------+ //| An associative array or dictionary storing elements as | //| . Where a key may be represented by any base type, | //| and a value may be represented be a CObject type object. | //+------------------------------------------------------------------+ class CDictionary : public CObject { private: int m_array_size; int m_total; bool m_free_mode; bool m_auto_free; int m_index; ulong m_hash; CList *m_array[]; union casting_struct { double d_value; ulong l_value; }casting; KeyValuePair *m_first_kvp; KeyValuePair *m_current_kvp; KeyValuePair *m_last_kvp; ulong Adler32(string line); int GetIndexByHash(ulong hash); template ulong GetHashByKey(T key); void Resize(); int FindNextSimpleNumber(int number); int FindNextLevel(); void Init(int capacity); public: CDictionary(); CDictionary(int capacity); ~CDictionary(); void Compress(void); /// /// Returns the total number of items. /// int Total(void){return m_total;} /// Returns the element at key    template CObject *GetObjectByKey(T key); template bool AddObject(T key,CObject *value); template bool DeleteObjectByKey(T key); template bool ContainsKey(T key); template void GetCurrentKey(T &key); bool DeleteCurrentNode(void); bool FreeMode(void) { return(m_free_mode); } void FreeMode(bool free_mode); void AutoFreeMemory(bool autoFree){m_auto_free=autoFree;} void Clear(); CObject *GetNextNode(void); CObject *GetPrevNode(void); CObject *GetCurrentNode(void); CObject *GetFirstNode(void); CObject *GetLastNode(void); }; //+------------------------------------------------------------------+ //| Default constructor. | //+------------------------------------------------------------------+ CDictionary::CDictionary() { Init(3); m_free_mode = true; m_auto_free=true; } //+------------------------------------------------------------------+ //| Creates a dictionary with predefined capacity. | //+------------------------------------------------------------------+ CDictionary::CDictionary(int capacity) { if(capacity < 3) Init(3); else Init(capacity); m_free_mode = true; m_auto_free=true; } //+------------------------------------------------------------------+ //| Destructor. | //+------------------------------------------------------------------+ CDictionary::~CDictionary() { Clear(); } //+------------------------------------------------------------------+ //| The mode sets the memory freeing for all subnodes | //+------------------------------------------------------------------+ void CDictionary::FreeMode(bool free_mode) { if(free_mode==m_free_mode) return; m_free_mode=free_mode; for(int i=0; i m_total)return (int)value; } return (int)value; } //+------------------------------------------------------------------+ //| Accepts a string and returns a hashing 32-bit number | //| characterizing this string. | //+------------------------------------------------------------------+ ulong CDictionary::Adler32(string line) { ulong s1 = 1; ulong s2 = 0; uint buflength=StringLen(line); uchar char_array[]; ArrayResize(char_array,buflength,0); StringToCharArray(line,char_array,0,-1,CP_ACP); for(uint n=0; n ulong CDictionary::GetHashByKey(T key) { ulong ukey = 0; string name=typename(key); if(name=="string") return Adler32((string)key); if(name=="double" || name=="float") { casting.d_value = (double)key; ukey=casting.l_value; } else ukey=(ulong)key; return ukey; } //+------------------------------------------------------------------+ //| Returns the key of current element | //+------------------------------------------------------------------+ template void CDictionary::GetCurrentKey(T &key) { m_current_kvp.GetKey(key); } //+------------------------------------------------------------------+ //| Returns index according to a key. | //+------------------------------------------------------------------+ int CDictionary::GetIndexByHash(ulong key) { return (int)(key%m_array_size); } //+------------------------------------------------------------------+ //| Remove all values from dictionary | //+------------------------------------------------------------------+ void CDictionary::Clear(void) { int size=ArraySize(m_array); for(int i=0; i CObject *CDictionary::GetObjectByKey(T key) { if(!ContainsKey(key)) return NULL; CObject *obj=m_current_kvp.object; return obj; } //+------------------------------------------------------------------+ //| Checks whether the dictionary contains a key of T arbitrary type. | //| RETURNS: | //| Returns true, if an object with this key already exists, | //| and lie, if otherwise. | //+------------------------------------------------------------------+ template bool CDictionary::ContainsKey(T key) { m_hash=GetHashByKey(key); m_index=GetIndexByHash(m_hash); if(CheckPointer(m_array[m_index])==POINTER_INVALID) return false; CList *list=m_array[m_index]; KeyValuePair *current_kvp=list.GetCurrentNode(); if(current_kvp == NULL)return false; if(current_kvp.EqualKey(key)) { m_current_kvp=current_kvp; return true; } current_kvp=list.GetFirstNode(); while(true) { if(current_kvp.EqualKey(key)) { m_current_kvp=current_kvp; return true; } current_kvp=list.GetNextNode(); if(current_kvp==NULL) return false; } return false; } //+------------------------------------------------------------------+ //| Adds CObject type element with a T key to the dictionary. | //| INPUT PARAMETRS: | //| T key - any base type, for instance int, double or string. | //| value - a class that derives from CObject. | //| RETURNS: | //| True value, if element has been added, and false, if otherwise. | //+------------------------------------------------------------------+ template bool CDictionary::AddObject(T key,CObject *value) { if(ContainsKey(key)) return false; if(m_total==m_array_size) { Resize(); ContainsKey(key); } if(CheckPointer(m_array[m_index])==POINTER_INVALID) { m_array[m_index]=new CList(); m_array[m_index].FreeMode(true); // The KeyValuePair elements are always removed } KeyValuePair *kv=new KeyValuePair(key,m_hash,value); kv.FreeMode(m_free_mode); if(m_array[m_index].Add(kv)!=-1) m_total++; if(CheckPointer(m_current_kvp)==POINTER_INVALID) { m_first_kvp=kv; m_current_kvp=kv; m_last_kvp=kv; } else { //we add to the very end, because the current node can be anywhere while(m_current_kvp.next_kvp!=NULL) m_current_kvp=m_current_kvp.next_kvp; m_current_kvp.next_kvp=kv; kv.prev_kvp=m_current_kvp; m_current_kvp=kv; m_last_kvp=kv; } return true; } //+------------------------------------------------------------------+ //| Returns current object. If an object is not selected, it returns | //| NULL. | //+------------------------------------------------------------------+ CObject *CDictionary::GetCurrentNode(void) { if(m_current_kvp==NULL) return NULL; return m_current_kvp.object; } //+------------------------------------------------------------------+ //| Returns the previous object. After call of method current | //| object becomes the previous one. If an object is not selected, it returns | //| NULL. | //+------------------------------------------------------------------+ CObject *CDictionary:: GetPrevNode(void) { if(m_current_kvp==NULL) return NULL; if(m_current_kvp.prev_kvp==NULL) return NULL; KeyValuePair *kvp=m_current_kvp.prev_kvp; m_current_kvp=kvp; return kvp.object; } //+------------------------------------------------------------------+ //| Returns next object. After call of method current | //| after call of the method. If object not selected, returns | //| NULL. | //+------------------------------------------------------------------+ CObject *CDictionary::GetNextNode(void) { if(m_current_kvp==NULL) return NULL; if(m_current_kvp.next_kvp==NULL) return NULL; m_current_kvp=m_current_kvp.next_kvp; return m_current_kvp.object; } //+------------------------------------------------------------------+ //| Returns first node in the node list. If there are no nodes in the dictionary, | //| it returns NULL. | //+------------------------------------------------------------------+ CObject *CDictionary::GetFirstNode(void) { if(m_first_kvp==NULL) return NULL; m_current_kvp=m_first_kvp; return m_first_kvp.object; } //+------------------------------------------------------------------+ //| Returns the last node in the node list. If there are no nodes in the dictionary, | //| it returns NULL. | //+------------------------------------------------------------------+ CObject *CDictionary::GetLastNode(void) { if(m_last_kvp==NULL) return NULL; m_current_kvp=m_last_kvp; return m_last_kvp.object; } //+------------------------------------------------------------------+ //| Deletes the current node | //+------------------------------------------------------------------+ bool CDictionary::DeleteCurrentNode(void) { if(m_current_kvp==NULL) return false; KeyValuePair* p_kvp = m_current_kvp.prev_kvp; KeyValuePair* n_kvp = m_current_kvp.next_kvp; if(CheckPointer(p_kvp)!=POINTER_INVALID) p_kvp.next_kvp=n_kvp; if(CheckPointer(n_kvp)!=POINTER_INVALID) n_kvp.prev_kvp=p_kvp; m_array[m_index].FreeMode(m_free_mode); bool res=m_array[m_index].DeleteCurrent(); if(res) { m_total--; Compress(); } return res; } //+------------------------------------------------------------------+ //| Deletes an object with a key from the dictionary. | //+------------------------------------------------------------------+ template bool CDictionary::DeleteObjectByKey(T key) { if(!ContainsKey(key)) return false; return DeleteCurrentNode(); } #define FOREACH_DICT(dict) for(CObject* node = (dict).GetFirstNode(); node != NULL; node = (dict).GetNextNode()) //+------------------------------------------------------------------+