diff --git a/src/mo_hash_table_int.F90 b/src/mo_hash_table_int.F90 index 0d0486492f3b2af3436d3b104798ab94796c0625..d1057191d29ffa174107d05eed6d0360215c9534 100644 --- a/src/mo_hash_table_int.F90 +++ b/src/mo_hash_table_int.F90 @@ -1,26 +1,16 @@ -!! Integer-Integer hash table. -!! A container for any kind of objects that are stored under any kind -!! of keys, with O(1) complexity for insertion, lookup, and removal. -!! -!! Initial revision: N. Huebbe, DWD -!! -!! @par Copyright and License -!! -!! This code is subject to the DWD and MPI-M-Software-License-Agreement in -!! its most recent form. -!! Please see the file LICENSE in the root of the source tree for this code. -!! Where software is supplied by third parties, it is indicated in the -!! headers of the routines. -!! -!! Implementation note: -!! The loops look more complex than they are: Their bodies are -!! executed only once in most cases, sometimes zero times, and more -!! than once in even less cases. This is due to the fact that we -!! grow the hash table to be at least as large as we have entries. -!! As such, the lists have at most one entry on average. -!! +! ICON +! +! --------------------------------------------------------------- +! Copyright (C) 2004-2024, DWD, MPI-M, DKRZ, KIT, ETH, MeteoSwiss +! Contact information: icon-model.org +! +! See AUTHORS.TXT for a list of authors +! See LICENSES/ for license information +! SPDX-License-Identifier: BSD-3-Clause +! --------------------------------------------------------------- + MODULE mo_hash_table_int - USE ISO_C_BINDING, ONLY: C_INT32_T + USE ISO_C_BINDING, ONLY: c_int32_t USE mo_exception, ONLY: finish IMPLICIT NONE @@ -32,30 +22,27 @@ MODULE mo_hash_table_int PRIVATE PUBLIC :: t_HashTable_int, t_HashIterator - ! --------------------------------------------------------- ! constants ! --------------------------------------------------------- !> module name - CHARACTER(LEN = *), PARAMETER :: modname = "mo_hash_table_int" + CHARACTER(LEN=*), PARAMETER :: modname = "mo_hash_table_int" !> result return code - INTEGER, PARAMETER :: SUCCESS = 0 - + INTEGER, PARAMETER :: SUCCESS = 0 ! --------------------------------------------------------- ! derived data types ! --------------------------------------------------------- ABSTRACT INTERFACE - INTEGER(C_INT32_T) FUNCTION f_hashFunction(key) - IMPORT C_INT32_T + INTEGER(c_int32_t) FUNCTION f_hashFunction(key) + IMPORT c_int32_t INTEGER, INTENT(IN) :: key END FUNCTION f_hashFunction END INTERFACE - !> Hash table object. ! TYPE :: t_HashTable_int @@ -66,24 +53,24 @@ MODULE mo_hash_table_int !> grows in powers of two the actual entry count (<= table size && ! >= non-NULL pointers in table due to the possible chaining of ! entries) - INTEGER(C_INT32_T) :: entryCount + INTEGER(c_int32_t) :: entryCount !> current count of bits that are used to index the hash table: INTEGER :: hashBits = -1 CONTAINS - PROCEDURE :: setEntry => hashTable_setEntry - PROCEDURE :: removeEntry => hashTable_removeEntry - PROCEDURE :: getEntry => hashTable_getEntry - PROCEDURE :: getEntries_1 => hashTable_getEntries_1 - PROCEDURE :: getEntries_2 => hashTable_getEntries_2 - GENERIC :: getEntries => getEntries_1, getEntries_2 - PROCEDURE :: destruct => hashTable_destruct - PROCEDURE :: inverse => hashTable_inverse - - PROCEDURE, PRIVATE :: findBin => hashTable_findBin - PROCEDURE, PRIVATE :: findBinIndex => hashTable_findBinIndex - PROCEDURE, PRIVATE :: growTable => hashTable_growTable + PROCEDURE :: setEntry => hashTable_setEntry + PROCEDURE :: removeEntry => hashTable_removeEntry + PROCEDURE :: getEntry => hashTable_getEntry + PROCEDURE :: getEntries_1 => hashTable_getEntries_1 + PROCEDURE :: getEntries_2 => hashTable_getEntries_2 + GENERIC :: getEntries => getEntries_1, getEntries_2 + PROCEDURE :: destruct => hashTable_destruct + PROCEDURE :: inverse => hashTable_inverse + + PROCEDURE, PRIVATE :: findBin => hashTable_findBin + PROCEDURE, PRIVATE :: findBinIndex => hashTable_findBinIndex + PROCEDURE, PRIVATE :: growTable => hashTable_growTable PROCEDURE, PRIVATE :: removeFromList => hashTable_removeFromList END TYPE t_HashTable_int @@ -92,7 +79,6 @@ MODULE mo_hash_table_int MODULE PROCEDURE hashTable_make END INTERFACE t_HashTable_int - !> Provides sequential access to all entries of a hash table. ! ! WARNING: @@ -108,11 +94,10 @@ MODULE mo_hash_table_int LOGICAL :: lkey_filter = .FALSE. INTEGER :: key CONTAINS - PROCEDURE :: init => hashIterator_init - PROCEDURE :: nextEntry => hashIterator_nextEntry ! returns .TRUE. IF the operation was successful + PROCEDURE :: init => hashIterator_init + PROCEDURE :: nextEntry => hashIterator_nextEntry ! returns .TRUE. IF the operation was successful END TYPE t_HashIterator - TYPE :: t_HashEntryPtr TYPE(t_HashEntry), POINTER :: ptr END TYPE t_HashEntryPtr @@ -120,25 +105,24 @@ MODULE mo_hash_table_int TYPE :: t_HashEntry INTEGER :: key, val TYPE(t_HashEntryPtr) :: next - INTEGER(C_INT32_T) :: hash + INTEGER(c_int32_t) :: hash END TYPE t_HashEntry CONTAINS !> Auxiliary function: Integer hash function. - INTEGER(C_INT32_T) FUNCTION int_hashKey(k) RESULT(res) + INTEGER(c_int32_t) FUNCTION int_hashKey(k) RESULT(res) INTEGER, INTENT(in) :: k !< key - res = MOD(k*(k+3),2147483647) + res = MOD(k*(k + 3), 2147483647) ! res = MOD(k*(k+3),5381) END FUNCTION int_hashKey - !> Create a hashtable. FUNCTION hashTable_make(opt_lunique_keys) RESULT(resultVar) TYPE(t_HashTable_int) :: resultVar LOGICAL, INTENT(IN), OPTIONAL :: opt_lunique_keys ! local variables - CHARACTER(LEN = *), PARAMETER :: routine = modname//":hashTable_make" + CHARACTER(LEN=*), PARAMETER :: routine = modname//":hashTable_make" INTEGER :: error, i IF (PRESENT(opt_lunique_keys)) THEN @@ -148,23 +132,22 @@ CONTAINS resultVar%entryCount = 0 resultVar%hashBits = 5 - ALLOCATE(resultVar%table(2**resultVar%hashBits), STAT = error) - IF(error /= SUCCESS) CALL finish(routine, "memory allocation failure") + ALLOCATE (resultVar%table(2**resultVar%hashBits), STAT=error) + IF (error /= SUCCESS) CALL finish(routine, "memory allocation failure") DO i = 1, 2**resultVar%hashBits resultVar%table(i)%ptr => NULL() END DO END FUNCTION hashTable_make - !> Auxiliary function. Return hashtable bin for a given hash. FUNCTION hashTable_findBinIndex(me, hash) RESULT(reducedHash) CLASS(t_HashTable_int), INTENT(IN) :: me - INTEGER(C_INT32_T), VALUE :: hash - INTEGER(C_INT32_T) :: reducedHash + INTEGER(c_int32_t), VALUE :: hash + INTEGER(c_int32_t) :: reducedHash - INTEGER(C_INT32_T) :: i + INTEGER(c_int32_t) :: i - IF(hash < 0) hash = NOT(hash) !fortran has no unsigned types + IF (hash < 0) hash = NOT(hash) !fortran has no unsigned types reducedHash = 0 DO i = 1, (31 + me%hashBits - 1)/me%hashBits reducedHash = IEOR(reducedHash, hash) @@ -173,21 +156,19 @@ CONTAINS reducedHash = IAND(reducedHash, 2**me%hashBits - 1) + 1 END FUNCTION hashTable_findBinIndex - !> Auxiliary function. Return hashtable bin for a given hash. FUNCTION hashTable_findBin(me, hash) RESULT(resultVar) CLASS(t_HashTable_int), INTENT(IN) :: me - INTEGER(C_INT32_T), VALUE :: hash + INTEGER(c_int32_t), VALUE :: hash TYPE(t_HashEntryPtr), POINTER :: resultVar resultVar => me%table(me%findBinIndex(hash)) END FUNCTION hashTable_findBin - !> Resize hashtable by a factor of 2. SUBROUTINE hashTable_growTable(me) CLASS(t_HashTable_int), INTENT(INOUT) :: me ! local variables - CHARACTER(LEN = *), PARAMETER :: routine = modname//":hashTable_growTable" + CHARACTER(LEN=*), PARAMETER :: routine = modname//":hashTable_growTable" TYPE(t_HashEntry), POINTER :: curEntry, nextEntry TYPE(t_HashEntryPtr), POINTER :: oldTable(:), bin INTEGER :: i, error @@ -196,8 +177,8 @@ CONTAINS ! Create a new empty table. me%hashBits = me%hashBits + 1 - ALLOCATE(me%table(2**me%hashBits), STAT = error) - IF(error /= SUCCESS) CALL finish(routine, "memory allocation failure") + ALLOCATE (me%table(2**me%hashBits), STAT=error) + IF (error /= SUCCESS) CALL finish(routine, "memory allocation failure") DO i = 1, 2**me%hashBits me%table(i)%ptr => NULL() END DO @@ -205,7 +186,7 @@ CONTAINS ! Move over the contents of the old table. DO i = 1, SIZE(oldTable, 1) curEntry => oldTable(i)%ptr - DO WHILE(ASSOCIATED(curEntry)) + DO WHILE (ASSOCIATED(curEntry)) nextEntry => curEntry%next%ptr ! Insert curEntry into the new table. @@ -218,17 +199,16 @@ CONTAINS END DO ! Cleanup the old table. - DEALLOCATE(oldTable) + DEALLOCATE (oldTable) END SUBROUTINE hashTable_growTable - !> Auxiliary routine: remove entry from a given hashtable bin @p !> list. SUBROUTINE hashTable_removeFromList(me, list, key, hash, opt_val) CLASS(t_HashTable_int), INTENT(INOUT) :: me TYPE(t_HashEntryPtr), POINTER, INTENT(INOUT) :: list INTEGER, INTENT(IN) :: key - INTEGER(C_INT32_T), VALUE :: hash + INTEGER(c_int32_t), VALUE :: hash INTEGER, OPTIONAL, INTENT(IN) :: opt_val ! local variables TYPE(t_HashEntry), POINTER :: curEntry @@ -236,26 +216,25 @@ CONTAINS LOGICAL :: lval_eq iterator => list - DO WHILE(ASSOCIATED(iterator%ptr)) + DO WHILE (ASSOCIATED(iterator%ptr)) curEntry => iterator%ptr - IF(curEntry%hash == hash) THEN - IF(curEntry%key == key) THEN + IF (curEntry%hash == hash) THEN + IF (curEntry%key == key) THEN lval_eq = .TRUE. - IF (PRESENT(opt_val)) lval_eq = (curEntry%val == opt_val) + IF (PRESENT(opt_val)) lval_eq = (curEntry%val == opt_val) IF (lval_eq) THEN ! remove from list iterator%ptr => curEntry%next%ptr me%entryCount = me%entryCount - 1 - DEALLOCATE(curEntry) ! destroy the entry + DEALLOCATE (curEntry) ! destroy the entry CYCLE END IF END IF END IF - iterator => curEntry%next ! point to next entry + iterator => curEntry%next ! point to next entry END DO END SUBROUTINE hashTable_removeFromList - !> Insert/overwrite entry key:val into hashtable. ! ! The hash table takes possession of both the key and the val and @@ -264,20 +243,20 @@ CONTAINS CLASS(t_HashTable_int), INTENT(INOUT) :: me INTEGER, INTENT(IN) :: key, val ! local variables - CHARACTER(LEN = *), PARAMETER :: routine = modname//":hashTable_setEntry" + CHARACTER(LEN=*), PARAMETER :: routine = modname//":hashTable_setEntry" TYPE(t_HashEntry), POINTER :: newEntry TYPE(t_HashEntryPtr), POINTER :: bin INTEGER :: error ! Prepare the new entry. - ALLOCATE(newEntry, STAT = error) - IF(error /= SUCCESS) CALL finish(routine, "memory allocation failure") - newEntry%key = key - newEntry%val = val + ALLOCATE (newEntry, STAT=error) + IF (error /= SUCCESS) CALL finish(routine, "memory allocation failure") + newEntry%key = key + newEntry%val = val newEntry%hash = int_hashKey(key) newEntry%next%ptr => NULL() - ! If there is a prexisting entry for this key, remove it. + ! If there is a pre-existing entry for this key, remove it. bin => me%findBin(newEntry%hash) IF (me%lunique_keys) THEN CALL me%removeFromList(bin, key, newEntry%hash) @@ -289,17 +268,16 @@ CONTAINS me%entryCount = me%entryCount + 1 ! Check whether we need to grow the table. - IF(me%entryCount == SIZE(me%table, 1)) CALL me%growTable() + IF (me%entryCount == SIZE(me%table, 1)) CALL me%growTable() END SUBROUTINE hashTable_setEntry - !> Remove entry with key @p key from hashtable. SUBROUTINE hashTable_removeEntry(me, key, opt_val) CLASS(t_HashTable_int), INTENT(INOUT) :: me INTEGER, INTENT(IN) :: key INTEGER, INTENT(IN) :: opt_val ! local variables - INTEGER(C_INT32_T) :: hash + INTEGER(c_int32_t) :: hash TYPE(t_HashEntryPtr), POINTER :: bin hash = int_hashKey(key) @@ -307,7 +285,6 @@ CONTAINS CALL me%removeFromList(bin, key, hash, opt_val) END SUBROUTINE hashTable_removeEntry - !> Get pointer to first entry with key @p key (if entry exists). FUNCTION hashTable_getEntry(me, key, opt_ierr) RESULT(resultVar) CLASS(t_HashTable_int), INTENT(IN) :: me @@ -315,20 +292,20 @@ CONTAINS INTEGER, INTENT(INOUT), OPTIONAL :: opt_ierr INTEGER :: resultVar ! local variables - INTEGER(C_INT32_T) :: hash + INTEGER(c_int32_t) :: hash TYPE(t_HashEntryPtr), POINTER :: bin TYPE(t_HashEntry), POINTER :: curEntry - IF (PRESENT(opt_ierr)) opt_ierr = -1 + IF (PRESENT(opt_ierr)) opt_ierr = -1 resultVar = 0 hash = int_hashKey(key) bin => me%findBin(hash) curEntry => bin%ptr - DO WHILE(ASSOCIATED(curEntry)) - IF(curEntry%hash == hash) THEN - IF(curEntry%key == key) THEN + DO WHILE (ASSOCIATED(curEntry)) + IF (curEntry%hash == hash) THEN + IF (curEntry%key == key) THEN resultVar = curEntry%val - IF (PRESENT(opt_ierr)) opt_ierr = SUCCESS + IF (PRESENT(opt_ierr)) opt_ierr = SUCCESS RETURN END IF END IF @@ -336,7 +313,6 @@ CONTAINS END DO END FUNCTION hashTable_getEntry - !> Get pointer to (possibly multiple) entries with key @p key (if entry exists). SUBROUTINE hashTable_getEntries_1(me, key, val, nval) CLASS(t_HashTable_int), INTENT(IN) :: me @@ -350,34 +326,33 @@ CONTAINS nval = 0 CALL it%init(me, key) DO - IF (.NOT. it%nextEntry(ikey, ival)) EXIT + IF (.NOT. it%nextEntry(ikey, ival)) EXIT nval = nval + 1 val(nval) = ival - IF (nval == SIZE(val)) EXIT + IF (nval == SIZE(val)) EXIT END DO END SUBROUTINE hashTable_getEntries_1 - !> Get pointer to (possibly multiple) entries for multiple keys SUBROUTINE hashTable_getEntries_2(me, keys, val, nval, opt_lunique) CLASS(t_HashTable_int), INTENT(IN) :: me INTEGER, INTENT(IN) :: keys(:) INTEGER, INTENT(INOUT) :: val(:) INTEGER, INTENT(OUT) :: nval - LOGICAL, INTENT(IN), OPTIONAL :: opt_lunique ! Flag. remove duplicates from the result list + LOGICAL, INTENT(IN), OPTIONAL :: opt_lunique ! Flag. remove duplicates from the result list ! local variables - INTEGER :: nn,i, tmp(SIZE(val)),l1 + INTEGER :: nn, i, tmp(SIZE(val)), l1 LOGICAL :: lunique lunique = .FALSE. - IF (PRESENT(opt_lunique)) lunique = opt_lunique + IF (PRESENT(opt_lunique)) lunique = opt_lunique IF (lunique) THEN nval = 0 - DO i=1,SIZE(keys) - CALL me%getEntries(keys(i),tmp, nn) + DO i = 1, SIZE(keys) + CALL me%getEntries(keys(i), tmp, nn) - DO l1=1,nn + DO l1 = 1, nn IF (.NOT. ANY(tmp(l1) == val(1:nval))) THEN nval = nval + 1 val(nval) = tmp(l1) @@ -386,14 +361,13 @@ CONTAINS END DO ELSE nval = 0 - DO i=1,SIZE(keys) - CALL me%getEntries(keys(i),val(nval+1:), nn) + DO i = 1, SIZE(keys) + CALL me%getEntries(keys(i), val(nval + 1:), nn) nval = nval + nn END DO END IF END SUBROUTINE hashTable_getEntries_2 - !> Destructor. SUBROUTINE hashTable_destruct(me) CLASS(t_HashTable_int), INTENT(INOUT) :: me @@ -404,16 +378,15 @@ CONTAINS IF (.NOT. ASSOCIATED(me%table)) RETURN DO i = 1, SIZE(me%table) curEntry => me%table(i)%ptr - DO WHILE(ASSOCIATED(curEntry)) + DO WHILE (ASSOCIATED(curEntry)) nextEntry => curEntry%next%ptr - DEALLOCATE(curEntry) + DEALLOCATE (curEntry) curEntry => nextEntry END DO END DO - DEALLOCATE(me%table) + DEALLOCATE (me%table) END SUBROUTINE hashTable_destruct - !---------------------------------------------------------------- !> Compute inverse hashtable. !---------------------------------------------------------------- @@ -427,12 +400,11 @@ CONTAINS hashtable = t_hashtable_int(opt_lunique_keys=src%lunique_keys) CALL it%init(src) DO - IF (.NOT. it%nextEntry(key, val)) EXIT - CALL hashtable%setEntry(val,key) + IF (.NOT. it%nextEntry(key, val)) EXIT + CALL hashtable%setEntry(val, key) END DO END FUNCTION hashTable_inverse - !> Initialize hash iterator. ! This object allows to loop over all entries stored in a ! hashtable. @@ -441,25 +413,24 @@ CONTAINS TYPE(t_HashTable_int), TARGET, INTENT(IN) :: table INTEGER, INTENT(IN), OPTIONAL :: key ! local variables - INTEGER(C_INT32_T) :: hash + INTEGER(c_int32_t) :: hash me%table => table me%curEntry => NULL() IF (table%hashBits <= 0) RETURN - me%curBin = 0 !will be incremented IN the first nextEntry() CALL + me%curBin = 0 !will be incremented IN the first nextEntry() CALL ! the key-filtered hashiterator does not start in the bin no. 0 ! but in the first bin where key occurs: IF (PRESENT(key)) THEN - me%lkey_filter = .TRUE. ; me%key = key + me%lkey_filter = .TRUE.; me%key = key hash = int_hashKey(me%key) me%curBin = me%table%findBinIndex(hash) me%curEntry => me%table%table(me%curBin)%ptr END IF END SUBROUTINE hashIterator_init - !> Advance hash iterator by one entry. LOGICAL FUNCTION hashIterator_nextEntry(me, key, val) RESULT(resultVar) CLASS(t_HashIterator), INTENT(INOUT) :: me @@ -467,14 +438,14 @@ CONTAINS INTEGER ::this_key resultVar = .FALSE. - this_key = me%key + this_key = me%key IF (me%table%hashBits <= 0) RETURN ! search for the next entry DO ! check whether we have found the next entry - IF(ASSOCIATED(me%curEntry)) THEN + IF (ASSOCIATED(me%curEntry)) THEN key = me%curEntry%key IF (.NOT. me%lkey_filter .OR. (key == this_key)) THEN resultVar = .TRUE.