Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ElmerCSC
GitHub Repository: ElmerCSC/elmerfem
Path: blob/devel/fem/src/HashTable.F90
3203 views
1
!/*****************************************************************************/
2
! *
3
! * Elmer, A Finite Element Software for Multiphysical Problems
4
! *
5
! * Copyright 1st April 1995 - , CSC - IT Center for Science Ltd., Finland
6
! *
7
! * This library is free software; you can redistribute it and/or
8
! * modify it under the terms of the GNU Lesser General Public
9
! * License as published by the Free Software Foundation; either
10
! * version 2.1 of the License, or (at your option) any later version.
11
! *
12
! * This library is distributed in the hope that it will be useful,
13
! * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
! * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
! * Lesser General Public License for more details.
16
! *
17
! * You should have received a copy of the GNU Lesser General Public
18
! * License along with this library (in file ../LGPL-2.1); if not, write
19
! * to the Free Software Foundation, Inc., 51 Franklin Street,
20
! * Fifth Floor, Boston, MA 02110-1301 USA
21
! *
22
! *****************************************************************************/
23
!
24
!/******************************************************************************
25
! *
26
! * Authors: Juha Ruokolainen
27
! * Email: [email protected]
28
! * Web: http://www.csc.fi/elmer
29
! * Address: CSC - IT Center for Science Ltd.
30
! * Keilaranta 14
31
! * 02101 Espoo, Finland
32
! *
33
! * Original Date: 09 Aug 1998 (Original C-version) Genesis
34
! * Original Date: 25 Oct 2000 Fortran 90/95 version
35
! *
36
! *****************************************************************************/
37
38
!> \ingroup ElmerLib
39
!> \{
40
41
!-------------------------------------------------------
42
!> Hash table build & lookup routines.
43
!-------------------------------------------------------
44
45
MODULE HashTable
46
47
USE Lists
48
49
IMPLICIT NONE
50
51
TYPE HashValue_t
52
CHARACTER(LEN=MAX_NAME_LEN) :: Block,Type
53
END TYPE HashValue_t
54
55
56
TYPE HashEntry_t
57
TYPE(HashEntry_t), POINTER :: Next
58
CHARACTER(LEN=MAX_NAME_LEN) :: Key
59
TYPE(HashValue_t), POINTER :: Value
60
END TYPE HashEntry_t
61
62
63
TYPE HashBucket_t
64
TYPE(HashEntry_t), POINTER :: Head
65
END TYPE HashBucket_t
66
67
68
TYPE HashTable_t
69
INTEGER :: BucketSize, CurrentBucket, TotalEntries
70
INTEGER :: MaxAvgEntries
71
TYPE(HashBucket_t), POINTER :: Bucket(:)
72
TYPE(HashEntry_t), POINTER :: CurrentEntry
73
END TYPE HashTable_t
74
75
76
CONTAINS
77
78
!-----------------------------------------------------------------------
79
!
80
! Call: TYPE(HashTable_t), POINTER hash = HashCreate( &
81
! InitialBucketSize, MaxAvgEntries, EqualKeys )
82
!
83
!> Initialize a hash table given initial bucket size. The size of the
84
!> bucket is rounded up to next power of two. The bucket doubles in size
85
!> whenever the size of the hash table grows over "MaxAvgEntries"
86
!> entries / bucket on the average. Keep the "MaxAvgEntries" small enough
87
!> (ordinarily from 3 entries up ?) to keep the hash table build & lookup
88
!> reasonably quick.
89
!
90
!> The hash table entries contain a "key" and an associated "value".
91
!> Currently the value structures are not copied to the hash table,
92
!> but only pointers to them are being stored. So one should not alter the
93
!> memory where the components are actually stored after adding them to the
94
!> hash table. Note that, potential problems in this respect are,
95
!> among others, automatic variables in functions. (C-Version: Bright side
96
!> is, that the "value" entries may be whatsoever as you manage them
97
!> yourself...)
98
!
99
! (Again, the C-version:
100
! The third and fourth arguments may be used to give user routines to
101
! compare equality of two keys and provide an index to the bucket array
102
! given key and array size respectively (these are the only place where
103
! any reference to the format of the keys is made). If given as NULL
104
! pointers, the default string comparison and hash functions routines
105
! respectively are implied. )
106
!
107
!-----------------------------------------------------------------------
108
FUNCTION HashCreate( InitialBucketSize, MaxAvgEntries ) RESULT(hash)
109
TYPE(HashTable_t), POINTER :: Hash
110
INTEGER :: InitialBucketSize, MaxAvgEntries
111
112
INTEGER :: i, RoundBits, Stat
113
114
NULLIFY( Hash )
115
IF ( InitialBucketSize <= 0 ) THEN
116
WRITE( Message, * ) 'HashCreate: invalid initial size given: ',InitialBucketSize
117
CALL Error( 'HashCreate', Message )
118
RETURN
119
END IF
120
121
! /*
122
! * Round bucket size up to next largest power of two...
123
! */
124
RoundBits = CEILING( LOG(1.0d0*InitialBucketSize) / LOG(2.0d0) )
125
126
! /*
127
! * Allocate the table and initialize the table entries...
128
! */
129
ALLOCATE( Hash )
130
131
Hash % BucketSize = 2**RoundBits;
132
133
! /*
134
! * Allocate the bucket array
135
! */
136
ALLOCATE( Hash % Bucket( Hash % BucketSize ), STAT=stat )
137
138
IF ( stat /= 0 ) THEN
139
CALL Error( 'HashCreate', &
140
'Hash table initialize error: unable to allocate bucket.' )
141
DEALLOCATE( Hash )
142
NULLIFY( Hash )
143
144
RETURN
145
END IF
146
147
DO i=1,Hash % BucketSize
148
NULLIFY( Hash % Bucket(i) % Head )
149
END DO
150
151
Hash % TotalEntries = 0
152
Hash % MaxAvgEntries = MaxAvgEntries;
153
154
! /*
155
! * the key comparison routine
156
! */
157
! if ( EqualKeys )
158
! hash->EqualKeys = EqualKeys;
159
! else
160
! hash->EqualKeys = (int (*)(void *,void *))HashEqualKeys;
161
!
162
! /*
163
! * the hash table index generation routine
164
! */
165
! if ( HashFunc )
166
! hash->HashFunc = HashFunc;
167
! else
168
! hash->HashFunc = (int (*)(void *,int))HashStringFunc;
169
END FUNCTION HashCreate
170
171
!--------------------------------------------------------------------------
172
! Call:index = HashStringFunc( key, mask )
173
!
174
!> Generate index to a hash table from given string. Hash table size
175
!> is assumed to be a power of two.
176
!--------------------------------------------------------------------------
177
FUNCTION HashStringFunc( key, size ) RESULT(Ind)
178
CHARACTER(LEN=*) :: key
179
INTEGER :: Size, Ind
180
181
INTEGER :: i,keylen
182
183
DO keylen=LEN(key),1,-1
184
IF ( key(keylen:keylen) /= ' ' ) EXIT
185
END DO
186
187
Ind = 0
188
DO i=1,keylen
189
Ind = Ind*8 + ICHAR(key(i:i))
190
END DO
191
192
Ind = IAND( Ind, size-1 ) + 1
193
END FUNCTION HashStringFunc
194
195
!--------------------------------------------------------------------------
196
!
197
! Call: equal = HashEqualKeys( key1, key2 )
198
!
199
!> Return equality of given two strings.
200
!> This is for internal use only.
201
!--------------------------------------------------------------------------
202
FUNCTION HashEqualKeys( key1,key2 ) RESULT(equal)
203
204
CHARACTER(LEN=*) :: key1,key2
205
LOGICAL :: equal
206
INTEGER :: n1,n2
207
208
equal = .FALSE.
209
210
n1 = LEN_TRIM(key1)
211
n2 = LEN_TRIM(key2)
212
IF ( n1 /= n2 ) RETURN
213
214
equal = key1(1:n1) == key2(1:n1)
215
END FUNCTION HashEqualKeys
216
217
!--------------------------------------------------------------------------
218
! Call: entry = HashFind( hash, key, bucket )
219
!
220
!> Search for a key from a hash table, return value is pointer to
221
!> the entry or NULL if not found. Bucket number of the entry
222
!> (if found) is given in int *bucket.
223
!> This is for internal use only.
224
!
225
!--------------------------------------------------------------------------
226
FUNCTION HashFind( hash, key, n ) RESULT( entry )
227
228
TYPE(HashTable_t), POINTER :: Hash
229
TYPE(HashEntry_t), POINTER :: Entry, ptr
230
INTEGER :: n
231
CHARACTER(LEN=*) :: key
232
233
! /*
234
! * Get bucket number for this key
235
! */
236
n = HashStringFunc( key, Hash % BucketSize )
237
238
! /*
239
! * Now go through bucket entries, and check if it is to be found
240
! */
241
ptr => Hash % Bucket(n) % Head
242
DO WHILE( ASSOCIATED(ptr) )
243
IF ( HashEqualKeys( key, ptr % key ) ) EXIT
244
ptr => ptr % Next
245
END DO
246
Entry => ptr
247
END FUNCTION HashFind
248
249
!--------------------------------------------------------------------------
250
! Call: HashAdd( HashTable_t *hash, void *key,void *value )
251
!
252
!> Add an entry to a hash table. If the key is already in the table
253
!> just change the "value" pointer.
254
!
255
!> The hash table entries contain a "key" and an associated "value".
256
!> Currently the (key) and value entries are not copied to the hash table,
257
!> but only pointers to them are being stored. So one should not alter the
258
!> memory where the components are actually stored after adding them to the
259
!> hash table. Note that, potential problems in this respect are, for
260
!> example, automatic variables in functions.
261
!>
262
! Return value is success or not...
263
!
264
!--------------------------------------------------------------------------
265
RECURSIVE FUNCTION HashAdd( hash, key, value ) RESULT(Success)
266
TYPE(HashTable_t), POINTER :: hash
267
CHARACTER(LEN=*) :: key
268
LOGICAL :: Success
269
TYPE(HashValue_t), POINTER :: Value
270
271
INTEGER :: stat
272
INTEGER :: n, keylen, count = 0
273
TYPE(HashEntry_t), POINTER :: Entry
274
275
Success = .TRUE.
276
277
entry => HashFind( hash,key, n )
278
279
IF ( ASSOCIATED( entry ) ) THEN
280
! /*
281
! * Already in, change the value pointer
282
! */
283
Entry % Value => Value
284
ELSE
285
! /*
286
! * not found add new...
287
! */
288
ALLOCATE( Entry, STAT=stat )
289
290
IF ( stat /= 0 ) THEN
291
CALL Error( 'HashAdd', 'add failed: unable to allocate ' // &
292
'(a few bytes of) memory ?' )
293
RETURN
294
END IF
295
296
Entry % Next => Hash % Bucket(n) % Head
297
Entry % Value => Value
298
Entry % Key = ' '
299
DO keylen=LEN(key),1,-1
300
IF ( key(keylen:keylen) /= ' ' ) EXIT
301
END DO
302
Entry % Key(1:keylen) = key(1:keylen)
303
304
Hash % Bucket(n) % Head => Entry
305
Hash % TotalEntries = Hash % TotalEntries + 1
306
307
IF ( Hash % TotalEntries > Hash % MaxAvgEntries*Hash % BucketSize ) THEN
308
Success = HashRebuild( Hash )
309
END IF
310
END IF
311
END FUNCTION HashAdd
312
313
!--------------------------------------------------------------------------
314
! Call: HashRemove( HashTable_t *hash, void *key )
315
!
316
!> Remove an entry from a hash table given key of the entry.
317
!--------------------------------------------------------------------------
318
SUBROUTINE HashRemove( Hash, key )
319
TYPE(HashTable_t), POINTER :: Hash
320
CHARACTER(LEN=*) :: Key
321
322
TYPE(HashEntry_t), POINTER :: entry,prev
323
INTEGER :: k,n
324
325
IF ( .NOT. ASSOCIATED(hash) ) RETURN
326
327
! /*
328
! * get bucket number for this key
329
! */
330
n = HashStringFunc( key, hash % BucketSize );
331
332
! /*
333
! * Now go through bucket entries, and check if it's there
334
! */
335
NULLIFY( Prev )
336
Entry => Hash % Bucket(n) % Head
337
DO WHILE( ASSOCIATED( Entry ) )
338
! /*
339
! * if key in, remove
340
! */
341
IF ( HashEqualKeys( key,entry % key ) ) THEN
342
IF ( ASSOCIATED(prev) ) THEN
343
prev % next => entry % next
344
ELSE
345
hash % Bucket(n) % Head => Entry % next
346
END IF
347
348
DEALLOCATE(Entry)
349
350
hash % TotalEntries = Hash % TotalEntries - 1
351
RETURN
352
END IF
353
Prev => Entry
354
Entry => Entry % Next
355
END DO
356
END SUBROUTINE HashRemove
357
358
!--------------------------------------------------------------------------
359
! Call: HashClean( HashTable_t *hash )
360
!
361
!> Clean all entries from the hash table, the bucket array is kept.
362
!> One may start refilling the hash table directly after cleaning.
363
!--------------------------------------------------------------------------
364
SUBROUTINE HashClean( hash )
365
TYPE(HashTable_t), POINTER :: Hash
366
367
TYPE(HashEntry_t), POINTER :: ptr,ptr1
368
INTEGER :: i
369
370
IF ( .NOT.ASSOCIATED(hash) ) RETURN
371
372
DO i=1,hash % BucketSize
373
ptr => hash % Bucket(i) % Head
374
DO WHILE( ASSOCIATED(ptr) )
375
ptr1 => ptr % next
376
DEALLOCATE( ptr )
377
ptr => ptr1
378
END DO
379
NULLIFY( hash % Bucket(i) % Head )
380
END DO
381
hash % TotalEntries = 0
382
hash % CurrentBucket = 0
383
NULLIFY( hash % CurrentEntry )
384
END SUBROUTINE HashClean
385
386
!--------------------------------------------------------------------------
387
! Call: HashDelete( HashTable_t *hash )
388
!
389
!> Delete a hash table by removing all the entries and freeing the
390
!> bucket and hash structures.
391
!--------------------------------------------------------------------------
392
SUBROUTINE HashDelete( Hash )
393
TYPE(HashTable_t), POINTER :: Hash
394
395
IF ( ASSOCIATED( Hash ) ) THEN
396
IF ( ASSOCIATED( Hash % Bucket ) ) THEN
397
CALL HashClean( Hash )
398
DEALLOCATE( Hash % Bucket )
399
END IF
400
DEALLOCATE( Hash )
401
END IF
402
END SUBROUTINE HashDelete
403
404
!--------------------------------------------------------------------------
405
! Call: HashRebuild( HashTable_t *hash )
406
!
407
!> Rebuild a hash table using a larger bucket array.
408
!> This is for internal use only.
409
!--------------------------------------------------------------------------
410
RECURSIVE FUNCTION HashRebuild( hash ) RESULT(Success)
411
TYPE(HashTable_t), POINTER :: Hash, NewHash
412
LOGICAL :: Success
413
414
TYPE(HashEntry_t), POINTER :: entry
415
INTEGER :: i
416
417
Success = .FALSE.
418
IF ( .NOT.ASSOCIATED(Hash) ) RETURN
419
420
NewHash => HashCreate( 4*Hash % BucketSize, Hash % MaxAvgEntries )
421
IF ( .NOT. ASSOCIATED( Newhash ) ) RETURN
422
423
DO i=1,Hash % BucketSize
424
Entry => Hash % Bucket(i) % Head
425
DO WHILE( ASSOCIATED( Entry ) )
426
IF ( .NOT. HashAdd( Newhash, Entry % Key, Entry % Value ) ) RETURN
427
Entry => Entry % Next
428
END DO
429
END DO
430
431
CALL HashClean( Hash )
432
DEALLOCATE( Hash % Bucket )
433
434
DEALLOCATE( Hash )
435
Hash => NewHash
436
Success = .TRUE.
437
END FUNCTION HashRebuild
438
439
!--------------------------------------------------------------------------
440
! Call: void *value = HashValue( HashTable_t *hash, void *key )
441
!
442
!> Given a "key" to hash table return pointer to the "value" memory or
443
!> NULL if not found in the table.
444
!--------------------------------------------------------------------------
445
FUNCTION HashValue( Hash, key ) RESULT(Value)
446
TYPE(HashTable_t), POINTER :: Hash
447
CHARACTER(LEN=*) :: Key
448
TYPE(HashValue_t), POINTER :: Value
449
450
INTEGER :: n
451
TYPE(HashEntry_t), POINTER :: Entry
452
453
NULLIFY( Value )
454
Entry => HashFind( Hash, key, n )
455
IF ( ASSOCIATED( Entry ) ) Value => Entry % Value
456
END FUNCTION HashValue
457
458
!--------------------------------------------------------------------------
459
! Call: HashInitWalk( HashTable_t *hash )
460
!
461
!> Initialize hash table walk through.
462
!--------------------------------------------------------------------------
463
SUBROUTINE HashInitWalk( Hash )
464
TYPE(HashTable_t), POINTER :: Hash
465
Hash % CurrentBucket = 0
466
NULLIFY( Hash % CurrentEntry )
467
END SUBROUTINE HashInitWalk
468
469
!--------------------------------------------------------------------------
470
!! Call: HashEntry_t *entry = HashNext( HashTable_t *hash )
471
!!
472
!> Return pointer to "next" entry in a hash table. The walk must be
473
!> initialized with a call to HashInitWalk. The "key" and "value" of
474
!> the table entry may be referenced as follows:
475
!--------------------------------------------------------------------------
476
FUNCTION HashNext( Hash ) RESULT(Entry)
477
TYPE(HashTable_t), POINTER :: Hash
478
TYPE(HashEntry_t), POINTER :: Entry
479
480
LOGICAL :: Current
481
482
NULLIFY( Entry )
483
IF ( .NOT.ASSOCIATED(Hash) ) RETURN
484
485
Current = .FALSE.
486
IF ( ASSOCIATED( Hash % CurrentEntry ) ) THEN
487
Current = ASSOCIATED( Hash % CurrentEntry % Next )
488
END IF
489
490
IF ( Current ) THEN
491
Hash % CurrentEntry => Hash % CurrentEntry % Next
492
ELSE
493
Hash % CurrentBucket = Hash % CurrentBucket + 1
494
495
DO WHILE( Hash % CurrentBucket < Hash % BucketSize .AND. &
496
.NOT.ASSOCIATED( Hash % Bucket(Hash % CurrentBucket) % Head) )
497
Hash % CurrentBucket = Hash % CurrentBucket + 1
498
END DO
499
500
IF ( Hash % CurrentBucket >= Hash % BucketSize ) RETURN
501
502
Hash % CurrentEntry => Hash % Bucket(Hash % CurrentBucket) % Head
503
END IF
504
505
Entry => Hash % CurrentEntry
506
END FUNCTION HashNext
507
508
!--------------------------------------------------------------------------
509
!! Call: void HashStats( HashTable_t *hash )
510
!!
511
!! Print info about the hash table organization.
512
!--------------------------------------------------------------------------
513
!void HashStats( HashTable_t *hash )
514
!{
515
! HashEntry_t *entry;
516
! int *BucketEntries;
517
! int n,i,j,MaxEntries=0,MinEntries=1<<30;
518
!
519
! for( i=0; i<hash->BucketSize; i++ )
520
! {
521
! n = 0;
522
! for( entry = hash->Bucket[i]; entry; entry=entry->next ) n++;
523
! MaxEntries = MAX( MaxEntries,n );
524
! MinEntries = MIN( MinEntries,n );
525
! }
526
!
527
! BucketEntries = (int *)calloc( MaxEntries+1,sizeof(int) );
528
!
529
! for( i=0; i<hash->BucketSize; i++ )
530
! {
531
! n = 0;
532
! for( entry = hash->Bucket[i]; entry; entry=entry->next ) n++;
533
! BucketEntries[n]++;
534
! }
535
!
536
! fprintf( stdout, "\n\nHash table statistics:\n\n" );
537
! fprintf( stdout, "Buckets: % 4d\n", hash->BucketSize );
538
! fprintf( stdout, "Entries: % 4d\n", hash->TotalEntries );
539
! fprintf( stdout, "Min / Bucket: %d\n", MinEntries );
540
! fprintf( stdout, "Max / Bucket: %d\n\n", MaxEntries );
541
!
542
!
543
! for( n=MinEntries; n<=MaxEntries; n++ )
544
! if ( BucketEntries[n] )
545
! fprintf( stdout, "% 4d entries in % 4d buckets.\n",
546
! n,BucketEntries[n] );
547
!
548
! free( BucketEntries );
549
!}
550
END MODULE HashTable
551
552
553
!> \}
554
555