# sqtable.cpp

    00001 /*
    00002 see copyright notice in squirrel.h
    00003 */
    00004 #include "sqpcheader.h"
    00005 #include "sqvm.h"
    00006 #include "sqtable.h"
    00007 #include "sqfuncproto.h"
    00008 #include "sqclosure.h"
    00009
    00010 SQTable::SQTable(SQSharedState *ss,SQInteger nInitialSize)
    00011 {
    00012   SQInteger pow2size=MINPOWER2;
    00013   while(nInitialSize>pow2size)pow2size=pow2size<<1;
    00014   AllocNodes(pow2size);
    00015   _usednodes = 0;
    00016   _delegate = NULL;
    00017   INIT_CHAIN();
    00018   ADD_TO_CHAIN(&_sharedstate->_gc_chain,this);
    00019 }
    00020
    00021 void SQTable::Remove(const SQObjectPtr &key)
    00022 {
    00023
    00024   _HashNode *n = _Get(key, HashObj(key) & (_numofnodes - 1));
    00025   if (n) {
    00026     n->val = n->key = _null_;
    00027     _usednodes--;
    00028     Rehash(false);
    00029   }
    00030 }
    00031
    00032 void SQTable::AllocNodes(SQInteger nSize)
    00033 {
    00034   _HashNode *nodes=(_HashNode *)SQ_MALLOC(sizeof(_HashNode)*nSize);
    00035   for(SQInteger i=0;i<nSize;i++){
    00036     new (&nodes[i]) _HashNode;
    00037     nodes[i].next=NULL;
    00038   }
    00039   _numofnodes=nSize;
    00040   _nodes=nodes;
    00041   _firstfree=&_nodes[_numofnodes-1];
    00042 }
    00043
    00044 void SQTable::Rehash(bool force)
    00045 {
    00046   SQInteger oldsize=_numofnodes;
    00047   //prevent problems with the integer division
    00048   if(oldsize<4)oldsize=4;
    00049   _HashNode *nold=_nodes;
    00050   SQInteger nelems=CountUsed();
    00051   if (nelems >= oldsize-oldsize/4)  /* using more than 3/4? */
    00052     AllocNodes(oldsize*2);
    00053   else if (nelems <= oldsize/4 &&  /* less than 1/4? */
    00054     oldsize > MINPOWER2)
    00055     AllocNodes(oldsize/2);
    00056   else if(force)
    00057     AllocNodes(oldsize);
    00058   else
    00059     return;
    00060   _usednodes = 0;
    00061   for (SQInteger i=0; i<oldsize; i++) {
    00062     _HashNode *old = nold+i;
    00063     if (type(old->key) != OT_NULL)
    00064       NewSlot(old->key,old->val);
    00065   }
    00066   for(SQInteger k=0;k<oldsize;k++)
    00067     nold[k].~_HashNode();
    00068   SQ_FREE(nold,oldsize*sizeof(_HashNode));
    00069 }
    00070
    00071 SQTable *SQTable::Clone()
    00072 {
    00073   SQTable *nt=Create(_opt_ss(this),_numofnodes);
    00074   SQInteger ridx=0;
    00075   SQObjectPtr key,val;
    00076   while((ridx=Next(true,ridx,key,val))!=-1){
    00077     nt->NewSlot(key,val);
    00078   }
    00079   nt->SetDelegate(_delegate);
    00080   return nt;
    00081 }
    00082
    00083 bool SQTable::Get(const SQObjectPtr &key,SQObjectPtr &val)
    00084 {
    00085   if(type(key) == OT_NULL)
    00086     return false;
    00087   _HashNode *n = _Get(key, HashObj(key) & (_numofnodes - 1));
    00088   if (n) {
    00089     val = _realval(n->val);
    00090     return true;
    00091   }
    00092   return false;
    00093 }
    00094 bool SQTable::NewSlot(const SQObjectPtr &key,const SQObjectPtr &val)
    00095 {
    00096   assert(type(key) != OT_NULL);
    00097   SQHash h = HashObj(key) & (_numofnodes - 1);
    00098   _HashNode *n = _Get(key, h);
    00099   if (n) {
    00100     n->val = val;
    00101     return false;
    00102   }
    00103   _HashNode *mp = &_nodes[h];
    00104   n = mp;
    00105
    00106
    00107   //key not found I'll insert it
    00108   //main pos is not free
    00109
    00110   if(type(mp->key) != OT_NULL) {
    00111     n = _firstfree;  /* get a free place */
    00112     SQHash mph = HashObj(mp->key) & (_numofnodes - 1);
    00113     _HashNode *othern;  /* main position of colliding node */
    00114
    00115     if (mp > n && (othern = &_nodes[mph]) != mp){
    00116       /* yes; move colliding node into free position */
    00117       while (othern->next != mp){
    00118         assert(othern->next != NULL);
    00119         othern = othern->next;  /* find previous */
    00120       }
    00121       othern->next = n;  /* redo the chain with `n' in place of `mp' */
    00122       n->key = mp->key;
    00123       n->val = mp->val;/* copy colliding node into free pos. (mp->next also goes) */
    00124       n->next = mp->next;
    00125       mp->key = _null_;
    00126       mp->val = _null_;
    00127       mp->next = NULL;  /* now `mp' is free */
    00128     }
    00129     else{
    00130       /* new node will go into free position */
    00131       n->next = mp->next;  /* chain new position */
    00132       mp->next = n;
    00133       mp = n;
    00134     }
    00135   }
    00136   mp->key = key;
    00137
    00138   for (;;) {  /* correct `firstfree' */
    00139     if (type(_firstfree->key) == OT_NULL && _firstfree->next == NULL) {
    00140       mp->val = val;
    00141       _usednodes++;
    00142       return true;  /* OK; table still has a free place */
    00143     }
    00144     else if (_firstfree == _nodes) break;  /* cannot decrement from here */
    00145     else (_firstfree)--;
    00146   }
    00147   Rehash(true);
    00148   return NewSlot(key, val);
    00149 }
    00150
    00151 SQInteger SQTable::Next(bool getweakrefs,const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval)
    00152 {
    00153   SQInteger idx = (SQInteger)TranslateIndex(refpos);
    00154   while (idx < _numofnodes) {
    00155     if(type(_nodes[idx].key) != OT_NULL) {
    00156       //first found
    00157       _HashNode &n = _nodes[idx];
    00158       outkey = n.key;
    00159       outval = getweakrefs?(SQObject)n.val:_realval(n.val);
    00160       //return idx for the next iteration
    00161       return ++idx;
    00162     }
    00163     ++idx;
    00164   }
    00165   //nothing to iterate anymore
    00166   return -1;
    00167 }
    00168
    00169
    00170 bool SQTable::Set(const SQObjectPtr &key, const SQObjectPtr &val)
    00171 {
    00172   _HashNode *n = _Get(key, HashObj(key) & (_numofnodes - 1));
    00173   if (n) {
    00174     n->val = val;
    00175     return true;
    00176   }
    00177   return false;
    00178 }
    00179
    00180 void SQTable::_ClearNodes()
    00181 {
    00182   for(SQInteger i = 0;i < _numofnodes; i++) { _nodes[i].key = _null_; _nodes[i].val = _null_; }
    00183 }
    00184
    00185 void SQTable::Finalize()
    00186 {
    00187   _ClearNodes();
    00188   SetDelegate(NULL);
    00189 }
    00190
    00191 void SQTable::Clear()
    00192 {
    00193   _ClearNodes();
    00194   _usednodes = 0;
    00195   Rehash(true);
    00196 }