File: FXHash.cpp

package info (click to toggle)
gogglesmm 1.2.5-6
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 16,812 kB
  • sloc: cpp: 231,960; ansic: 893; xml: 222; makefile: 33
file content (304 lines) | stat: -rw-r--r-- 9,683 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
/********************************************************************************
*                                                                               *
*                       H a s h   T a b l e   C l a s s                         *
*                                                                               *
*********************************************************************************
* Copyright (C) 2003,2022 by Jeroen van der Zijp.   All Rights Reserved.        *
*********************************************************************************
* This library is free software; you can redistribute it and/or modify          *
* it under the terms of the GNU Lesser General Public License as published by   *
* the Free Software Foundation; either version 3 of the License, or             *
* (at your option) any later version.                                           *
*                                                                               *
* This library is distributed in the hope that it will be useful,               *
* but WITHOUT ANY WARRANTY; without even the implied warranty of                *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                 *
* GNU Lesser General Public License for more details.                           *
*                                                                               *
* You should have received a copy of the GNU Lesser General Public License      *
* along with this program.  If not, see <http://www.gnu.org/licenses/>          *
********************************************************************************/
#include "xincs.h"
#include "fxver.h"
#include "fxdefs.h"
#include "fxmath.h"
#include "FXArray.h"
#include "FXHash.h"
#include "FXException.h"
#include "FXElement.h"


/*
  Notes:
  - The table hashes pointers to pointers, or things which fit into a pointer to
    things which fit into a pointers.

  - The hash table does not know anything about the keys, or the values; thus, subclasses
    are responsible for managing the memory of the contents.

  - Initially the table is initialized to the empty-table pointer.  This will have only
    a single free slot; this means the table pointer is NEVER null.

  - The hash-table object requires only one pointer's worth of memory.  Thus an empty
    hash table requires very little space, and no table is allocated until at least one
    element is added.

  - Probe position p, probe increment b, and index x MUST be unsigned.

  - The table resize algorithm:

      - Table maintains count of used slots, and count of free slots; when an entry
        is removed, used slot count will be decremented, but free slot count will
        stay the same, because the slot is voided, not freed.  This is because the
        removed slot may still be part of another entry's probe sequence.
        When an entry is added, it will be placed in a voided slot if possible;
        if not, then it will be placed in a free slot.

      - When the table is resized, all entries wil be re-hashed, and voided slots be
        reclaimed as free slots.

      - Table is grown PRIOR to adding entries, and shrunk AFTER removing entries.

      - Table is grown when the number of remaining free slots is less than or
        equal to 1/4 of the table size (plus 1, because the resize occurs PRIOR to
        adding the entry).

      - Table is shrunk when the number of used slots is less than or equal to 1/4
        of the table size.

      - Table must always maintain at least one free slot to insure that probing
        sequence is always finite (all locations are eventually visited, and thus
        the probing sequence is guaranteed to be finite).

      - Thus the new implementation will be at least 1/4 used, and at most 3/4 filled
        (used and voided slots do not exceed 3/4 of table size).  The "hysteresis"
        between growing and shrinking means that adding, then removing a single item
        will not cause a table resize (except when the table is near empty, of course).
*/

#define EMPTY     (const_cast<Entry*>((const Entry*)(__hash__empty__+3)))
#define HASH(x)   ((FXival)(x)^(((FXival)(x))>>13))
#define VOID      ((const void*)-1L)
#define LEGAL(p)  ((p)!=nullptr && (p)!=VOID)
#define BSHIFT    5


using namespace FX;

/*******************************************************************************/

namespace FX {


// Empty object list
extern const FXival __hash__empty__[];
const FXival __hash__empty__[7]={1,0,1,0,0};


// Adjust the size of the table
FXbool FXHash::no(FXival n){
  FXival m=no();
  if(__likely(m!=n)){
    Entry* elbat;
    void*  p;

    // Release old table
    if(1<m){
      destructElms(table,m);
      ::free(((FXival*)table)-3);
      table=EMPTY;
      }

    // Allocate new table
    if(1<n){
      if(__unlikely((p=::calloc(sizeof(FXival)*3+sizeof(Entry)*n,1))==nullptr)) return false;
      elbat=(Entry*)(((FXival*)p)+3);
      ((FXival*)elbat)[-3]=n;
      ((FXival*)elbat)[-2]=0;
      ((FXival*)elbat)[-1]=n;
      constructElms(elbat,n);
      table=elbat;
      }
    }
  return true;
  }


// Resize the table to the given size, keeping contents
FXbool FXHash::resize(FXival n){
  FXHash elbat;
  FXASSERT((n&(n-1))==0);       // Power of 2
  FXASSERT((n-used())>0);       // At least one free slot
  if(elbat.no(n)){
    if(1<elbat.no() && 1<no()){
      const void* ky;
      void* da;
      FXuval p,b,x;
      FXival i;
      for(i=0; i<no(); ++i){    // Hash existing entries into new table
        ky=table[i].key;
        da=table[i].data;
        if(LEGAL(ky)){
          p=b=HASH(ky);
          while(elbat.table[x=p&(n-1)].key){    // Locate slot
            p=(p<<2)+p+b+1;
            b>>=BSHIFT;
            }
          elbat.table[x].key=ky;
          elbat.table[x].data=da;
          }
        }
      elbat.free(n-used());     // All non-empty slots now free
      elbat.used(used());       // Used slots not changed
      }
    adopt(elbat);
    return true;
    }
  return false;
  }


// Make empty table
FXHash::FXHash():table(EMPTY){
  FXASSERT_STATIC(sizeof(FXHash)==sizeof(void*));
  FXASSERT_STATIC(sizeof(Entry)<=sizeof(FXival)*2);
  }


// Construct from another table
FXHash::FXHash(const FXHash& other):table(EMPTY){
  FXASSERT_STATIC(sizeof(FXHash)==sizeof(void*));
  FXASSERT_STATIC(sizeof(Entry)<=sizeof(FXival)*2);
  if(__likely(1<other.no() && no(other.no()))){
    copyElms(table,other.table,no());
    free(other.free());
    used(other.used());
    }
  }


// Assign from another table
FXHash& FXHash::operator=(const FXHash& other){
  if(__likely(table!=other.table && no(other.no()))){
    copyElms(table,other.table,no());
    free(other.free());
    used(other.used());
    }
  return *this;
  }


// Adopt table from another
FXHash& FXHash::adopt(FXHash& other){
  if(__likely(table!=other.table)){
    swap(table,other.table);
    other.clear();
    }
  return *this;
  }


// Find position of given key
FXival FXHash::find(const void* ky) const {
  if(__likely(LEGAL(ky))){
    FXuval p,b,x;
    p=b=HASH(ky);
    while(table[x=p&(no()-1)].key){
      if(__likely(table[x].key==ky)) return x;
      p=(p<<2)+p+b+1;
      b>>=BSHIFT;
      }
    }
  return -1;
  }


// Return reference to slot assocated with given key
void*& FXHash::at(const void* ky){
  if(__likely(LEGAL(ky))){
    FXuval p,b,h,x;
    p=b=h=HASH(ky);
    while(table[x=p&(no()-1)].key){
      if(__likely(table[x].key==ky)) goto x;    // Replace existing slot
      p=(p<<2)+p+b+1;
      b>>=BSHIFT;
      }
    if(__unlikely(free()<=1+(no()>>2)) && __unlikely(!resize(no()<<1))){ throw FXMemoryException("FXHash::at: out of memory\n"); }
    p=b=h;
    while(table[x=p&(no()-1)].key){
      if(__likely(table[x].key==VOID)) goto y;  // Put into voided slot
      p=(p<<2)+p+b+1;
      b>>=BSHIFT;
      }
    free(free()-1);                             // Put into empty slot
y:  used(used()+1);
    table[x].key=ky;
x:  return table[x].data;
    }
  return *((void**)nullptr);            // Can NOT be referenced; will generate segfault!
  }


// Return constant reference to slot assocated with given key
void *const& FXHash::at(const void* ky) const {
  if(__likely(LEGAL(ky))){
    FXuval p,b,x;
    p=b=HASH(ky);
    while(table[x=p&(no()-1)].key){
      if(__likely(table[x].key==ky)) return table[x].data;      // Return existing slot
      p=(p<<2)+p+b+1;
      b>>=BSHIFT;
      }
    }
  return EMPTY[0].data;                 // Can be safely referenced, will read as NULL
  }


// Remove association from the table
void* FXHash::remove(const void* ky){
  void* old=nullptr;
  if(__likely(LEGAL(ky))){
    FXuval p,b,x;
    p=b=HASH(ky);
    while(table[x=p&(no()-1)].key!=ky){
      if(table[x].key==nullptr) goto x;
      p=(p<<2)+p+b+1;
      b>>=BSHIFT;
      }
    old=table[x].data;
    table[x].key=VOID;          // Void the slot (not empty!)
    table[x].data=nullptr;
    used(used()-1);
    if(__unlikely(used()<=(no()>>2))) resize(no()>>1);
    }
x:return old;
  }


// Erase data at pos in the table, returning old pointer
void* FXHash::erase(FXival pos){
  void* old=nullptr;
  if(__unlikely(pos<0 || no()<=pos)){ throw FXRangeException("FXHash::erase: argument out of range\n"); }
  if(__likely(LEGAL(table[pos].key))){
    old=table[pos].data;
    table[pos].key=VOID;        // Void the slot (not empty!)
    table[pos].data=nullptr;
    used(used()-1);
    if(__unlikely(used()<=(no()>>2))) resize(no()>>1);
    }
  return old;
  }


// Clear hash table, marking all slots as free
FXbool FXHash::clear(){
  return no(1);
  }


// Destroy table
FXHash::~FXHash(){
  clear();
  }

}