~ubuntu-branches/ubuntu/maverick/freecell-solver/maverick

« back to all changes in this revision

Viewing changes to fcs_hash.c

  • Committer: Bazaar Package Importer
  • Author(s): RISKO Gergely
  • Date: 2002-04-06 11:35:18 UTC
  • Revision ID: james.westby@ubuntu.com-20020406113518-n398kvu45dixasoh
Tags: upstream-2.4.1
ImportĀ upstreamĀ versionĀ 2.4.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * fcs_hash.c - an implementation of a simplistic (keys only) hash. This
 
3
 * hash uses chaining and re-hashing and was found to be very fast. Not all
 
4
 * of the functions of the hash ADT are implemented, but it is useful enough
 
5
 * for Freecell Solver.
 
6
 *
 
7
 * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000
 
8
 *
 
9
 * This file is in the public domain (it's uncopyrighted).
 
10
 */
 
11
 
 
12
#include "config.h"
 
13
 
 
14
#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) || (defined(INDIRECT_STACK_STATES) && (FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH))
 
15
 
 
16
#include <stdlib.h>
 
17
#include <string.h>
 
18
 
 
19
#include "fcs_hash.h"
 
20
 
 
21
#ifdef DMALLOC
 
22
#include "dmalloc.h"
 
23
#endif
 
24
 
 
25
 
 
26
#define NUM_PRIMES 136
 
27
 
 
28
/*
 
29
    A list of primes in the range between 256 and 2^31. There
 
30
    are roughly 6 in an x .. 2x decade.
 
31
  */
 
32
static const int primes_list[NUM_PRIMES+1] = {
 
33
    257,
 
34
    293,
 
35
    331,
 
36
    367,
 
37
    409,
 
38
    461,
 
39
    521,
 
40
    587,
 
41
    653,
 
42
    739,
 
43
    827,
 
44
    929,
 
45
    1049,
 
46
    1181,
 
47
    1321,
 
48
    1487,
 
49
    1693,
 
50
    1879,
 
51
    2113,
 
52
    2377,
 
53
    2677,
 
54
    3011,
 
55
    3389,
 
56
    3821,
 
57
    4283,
 
58
    4813,
 
59
    5417,
 
60
    6091,
 
61
    6857,
 
62
    7717,
 
63
    8677,
 
64
    9767,
 
65
    10973,
 
66
    12343,
 
67
    13901,
 
68
    15629,
 
69
    17573,
 
70
    19777,
 
71
    22247,
 
72
    25031,
 
73
    28151,
 
74
    31663,
 
75
    35671,
 
76
    40087,
 
77
    45083,
 
78
    50723,
 
79
    57059,
 
80
    64187,
 
81
    72211,
 
82
    81239,
 
83
    91393,
 
84
    102829,
 
85
    115663,
 
86
    130121,
 
87
    146389,
 
88
    164683,
 
89
    185291,
 
90
    208433,
 
91
    234499,
 
92
    263803,
 
93
    296767,
 
94
    333857,
 
95
    375593,
 
96
    422537,
 
97
    475367,
 
98
    534799,
 
99
    601631,
 
100
    676829,
 
101
    761429,
 
102
    856627,
 
103
    963689,
 
104
    1084133,
 
105
    1219649,
 
106
    1372109,
 
107
    1543631,
 
108
    1736599,
 
109
    1953659,
 
110
    2197847,
 
111
    2472577,
 
112
    2781677,
 
113
    3129383,
 
114
    3520519,
 
115
    3960587,
 
116
    4455667,
 
117
    5012627,
 
118
    5639191,
 
119
    6344087,
 
120
    7137101,
 
121
    8029243,
 
122
    9032887,
 
123
    10161997,
 
124
    11432249,
 
125
    12861281,
 
126
    14468933,
 
127
    16277561,
 
128
    18312263,
 
129
    20601271,
 
130
    23176429,
 
131
    26073497,
 
132
    29332687,
 
133
    32999269,
 
134
    37124167,
 
135
    41764741,
 
136
    46985261,
 
137
    52858427,
 
138
    59465723,
 
139
    66898963,
 
140
    75261311,
 
141
    84668971,
 
142
    95252603,
 
143
    107159153,
 
144
    120554059,
 
145
    135623317,
 
146
    152576233,
 
147
    171648317,
 
148
    193104269,
 
149
    217242323,
 
150
    244397591,
 
151
    274947317,
 
152
    309315703,
 
153
    347980163,
 
154
    391477727,
 
155
    440412409,
 
156
    495463937,
 
157
    557396929,
 
158
    627071569,
 
159
    705455519,
 
160
    793637437,
 
161
    892842107,
 
162
    1004447359,
 
163
    1130003291,
 
164
    1271253703,
 
165
    1430160409,
 
166
    1608930451,
 
167
    1810046779,
 
168
    2036302607,
 
169
    -1
 
170
};
 
171
 
 
172
 
 
173
static void SFO_hash_rehash(SFO_hash_t * hash);
 
174
 
 
175
 
 
176
 
 
177
SFO_hash_t * freecell_solver_hash_init(
 
178
    SFO_hash_value_t wanted_size,
 
179
    int (*compare_function)(const void * key1, const void * key2, void * context),
 
180
    void * context
 
181
    )
 
182
{
 
183
    int size,i;
 
184
    SFO_hash_t * hash;
 
185
 
 
186
    /* Find a prime number that is greater than the initial wanted size */
 
187
    for(i=0;i<NUM_PRIMES;i++)
 
188
    {
 
189
        if (primes_list[i] > wanted_size)
 
190
        {
 
191
            break;
 
192
        }
 
193
    }
 
194
 
 
195
    size = primes_list[i];
 
196
 
 
197
    hash = (SFO_hash_t *)malloc(sizeof(SFO_hash_t));
 
198
 
 
199
    hash->size = size;
 
200
 
 
201
    hash->num_elems = 0;
 
202
 
 
203
    /* Allocate a table of size entries */
 
204
    hash->entries = (SFO_hash_symlink_t *)malloc(
 
205
        sizeof(SFO_hash_symlink_t) * size
 
206
        );
 
207
 
 
208
    hash->compare_function = compare_function;
 
209
    hash->context = context;
 
210
 
 
211
    /* Initialize all the cells of the hash table to NULL, which indicate
 
212
       that the cork of the linked list is right at the start */
 
213
    memset(hash->entries, 0, sizeof(SFO_hash_symlink_t)*size);
 
214
 
 
215
    return hash;
 
216
}
 
217
 
 
218
void * freecell_solver_hash_insert(
 
219
    SFO_hash_t * hash,
 
220
    void * key,
 
221
    SFO_hash_value_t hash_value,
 
222
    int optimize_for_caching
 
223
    )
 
224
{
 
225
    int place;
 
226
    SFO_hash_symlink_t * list;
 
227
    SFO_hash_symlink_item_t * item, * last_item;
 
228
 
 
229
    /* Get the index of the appropriate chain in the hash table */
 
230
    place = hash_value % (hash->size);
 
231
 
 
232
    list = &(hash->entries[place]);
 
233
    /* If first_item is non-existent */
 
234
    if (list->first_item == NULL)
 
235
    {
 
236
        /* Allocate a first item with that key */
 
237
        item = list->first_item = (SFO_hash_symlink_item_t *)malloc(sizeof(SFO_hash_symlink_item_t));
 
238
        item->next = NULL;
 
239
        item->key = key;
 
240
        item->hash_value = hash_value;
 
241
 
 
242
        goto rehash_check;
 
243
    }
 
244
 
 
245
    /* Initialize item to the chain's first_item */
 
246
    item = list->first_item;
 
247
    last_item = NULL;
 
248
 
 
249
    while (item != NULL)
 
250
    {
 
251
        /*
 
252
            We first compare the hash values, because it is faster than
 
253
            comparing the entire data structure.
 
254
 
 
255
        */
 
256
        if (
 
257
            (item->hash_value == hash_value) &&
 
258
            (!(hash->compare_function(item->key, key, hash->context)))
 
259
           )
 
260
        {
 
261
            if (optimize_for_caching)
 
262
            {
 
263
                /*
 
264
                 * Place the item in the beginning of the chain.
 
265
                 * If last_item == NULL it is already the first item so leave
 
266
                 * it alone
 
267
                 * */
 
268
                if (last_item != NULL)
 
269
                {
 
270
                    last_item->next = item->next;
 
271
                    item->next = list->first_item;
 
272
                    list->first_item = item;
 
273
                }
 
274
            }
 
275
            return item->key;
 
276
        }
 
277
        /* Cache the item before the current in last_item */
 
278
        last_item = item;
 
279
        /* Move to the next item */
 
280
        item = item->next;
 
281
    }
 
282
 
 
283
    if (optimize_for_caching)
 
284
    {
 
285
        /* Put the new element at the beginning of the list */
 
286
        item = (SFO_hash_symlink_item_t *)malloc(sizeof(SFO_hash_symlink_item_t));
 
287
        item->next = list->first_item;
 
288
        item->key = key;
 
289
        item->hash_value = hash_value;
 
290
        list->first_item = item;
 
291
    }
 
292
    else
 
293
    {
 
294
        /* Put the new element at the end of the list */
 
295
        item = last_item->next = (SFO_hash_symlink_item_t *)malloc(sizeof(SFO_hash_symlink_item_t));
 
296
        item->next = NULL;
 
297
        item->key = key;
 
298
        item->hash_value = hash_value;
 
299
    }
 
300
 
 
301
rehash_check:
 
302
 
 
303
    hash->num_elems++;
 
304
 
 
305
    if (hash->num_elems > ((hash->size*3)>>2))
 
306
    {
 
307
        SFO_hash_rehash(hash);
 
308
    }
 
309
 
 
310
    return NULL;
 
311
}
 
312
 
 
313
void freecell_solver_hash_free_with_callback(
 
314
    SFO_hash_t * hash,
 
315
    void (*function_ptr)(void * key, void * context)
 
316
    )
 
317
{
 
318
    int i;
 
319
    SFO_hash_symlink_item_t * item, * next_item;
 
320
 
 
321
    for(i=0;i<hash->size;i++)
 
322
    {
 
323
        item = hash->entries[i].first_item;
 
324
        while (item != NULL)
 
325
        {
 
326
            function_ptr(item->key, hash->context);
 
327
            next_item = item->next;
 
328
 
 
329
            free(item);
 
330
            item = next_item;
 
331
        }
 
332
    }
 
333
 
 
334
    free(hash->entries);
 
335
 
 
336
    free(hash);
 
337
}
 
338
 
 
339
void freecell_solver_hash_free(
 
340
    SFO_hash_t * hash
 
341
    )
 
342
{
 
343
    int i;
 
344
    SFO_hash_symlink_item_t * item, * next_item;
 
345
 
 
346
 
 
347
    for(i=0;i<hash->size;i++)
 
348
    {
 
349
        item = hash->entries[i].first_item;
 
350
        while (item != NULL)
 
351
        {
 
352
            next_item = item->next;
 
353
 
 
354
            free(item);
 
355
            item = next_item;
 
356
        }
 
357
    }
 
358
 
 
359
    free(hash->entries);
 
360
 
 
361
    free(hash);
 
362
}
 
363
 
 
364
 
 
365
/*
 
366
    This function "rehashes" a hash. I.e: it increases the size of its
 
367
    hash table, allowing for smaller chains, and faster lookup.
 
368
 
 
369
  */
 
370
static void SFO_hash_rehash(
 
371
    SFO_hash_t * hash
 
372
    )
 
373
{
 
374
    int old_size, new_size;
 
375
    int i;
 
376
    SFO_hash_t * new_hash;
 
377
    SFO_hash_symlink_item_t * item, * next_item;
 
378
    int place;
 
379
 
 
380
    old_size = hash->size;
 
381
 
 
382
    /* Allocate a new hash with hash_init() */
 
383
    new_hash = freecell_solver_hash_init(
 
384
        old_size * 2,
 
385
        hash->compare_function,
 
386
        hash->context
 
387
        );
 
388
 
 
389
    new_hash->num_elems = hash->num_elems;
 
390
 
 
391
    old_size = hash->size;
 
392
    new_size = new_hash->size;
 
393
 
 
394
    /* Copy the items to the new hash and deallocate the old ones in the
 
395
     * same time */
 
396
    for(i=0;i<old_size;i++)
 
397
    {
 
398
        item = hash->entries[i].first_item;
 
399
        /* traverse the chain item by item */
 
400
        while(item != NULL)
 
401
        {
 
402
            /* The place in the new hash table */
 
403
            place = item->hash_value % new_size;
 
404
 
 
405
            /* Store the next item in the linked list in a safe place,
 
406
               so we can retrieve it after the assignment */
 
407
            next_item = item->next;
 
408
            /* It is placed in front of the first element in the chain,
 
409
               so it should link to it */
 
410
            item->next = new_hash->entries[place].first_item;
 
411
 
 
412
            /* Make it the first item in its chain */
 
413
            new_hash->entries[place].first_item = item;
 
414
 
 
415
            /* Move to the next item this one. */
 
416
            item = next_item;
 
417
        }
 
418
    };
 
419
 
 
420
    /* Free the entries of the old hash */
 
421
    free(hash->entries);
 
422
 
 
423
    /* Copy the new hash to the old one */
 
424
    *hash = *new_hash;
 
425
 
 
426
    /* De-allocate the space that was allocated by new_hash's struct
 
427
       per-se.
 
428
    */
 
429
    free(new_hash);
 
430
}
 
431
 
 
432
#else
 
433
 
 
434
static void freecell_solver_hash_c_dummy(); /* ANSI C doesn't allow empty compilation */
 
435
 
 
436
#endif /* (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) || defined(INDIRECT_STACK_STATES) */