Skip to content

Commit

Permalink
Merge pull request #827 from chuckyvt/hashap-keytype-generic
Browse files Browse the repository at this point in the history
Addition of key generic interfaces for hash maps
  • Loading branch information
jvdp1 committed Jun 19, 2024
2 parents ad42828 + 87192e9 commit d996e43
Show file tree
Hide file tree
Showing 8 changed files with 775 additions and 150 deletions.
137 changes: 88 additions & 49 deletions doc/specs/stdlib_hashmaps.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ Procedures to manipulate `key_type` data:
`key_in`, to contents of the key, `key_out`.

* `get( key, value )` - extracts the contents of `key` into `value`,
an `int8` array, 'int32' array, or character string.
an `int8` array, `int32` array, or character string.

* `free_key( key )` - frees the memory in `key`.

Expand Down Expand Up @@ -474,9 +474,9 @@ is an `intent(in)` argument.
`other`: shall be a scalar expression of type `other_type`. It
is an `intent(in)` argument.

`value`: if the the first argument is of `key_type` `value` shall be
an allocatable default character string variable, or
an allocatable vector variable of type integer and kind `int8` or
`value`: if the the first argument is of `key_type`, `value` shall be
an allocatable default `character` string variable, or
an allocatable vector variable of type `integer` and kind `int8` or
`int32`, otherwise the first argument is of `other_type` and `value`
shall be an allocatable of `class(*)`. It is an `intent(out)` argument.

Expand Down Expand Up @@ -751,8 +751,8 @@ is an `intent(out)` argument.
`other`: shall be a scalar variable of type `other_type`. It
is an `intent(out)` argument.

`value`: if the first argument is `key` `value` shall be a default
character string scalar expression, or a vector expression of type integer
`value`: if the first argument is `key`, `value` shall be a default
`character` string scalar expression, or a vector expression of type `integer`
and kind `int8` or `int32`, while for a first argument of type
`other` `value` shall be of type `class(*)`. It is an `intent(in)`
argument.
Expand Down Expand Up @@ -790,6 +790,14 @@ overall structure and performance of the hash map object:`calls`,
`max_bits`, `int_calls`, `int_depth`, `int_index`,
`int_probes`, `success`, `alloc_fault`, and `array_size_error`.

Generic key interfaces for `key_test`, `map_entry`, `get_other_data`,
`remove`, and `set_other_data` are povided so that the supported types
of `int8` arrays, `int32` arrays and `character` scalars can be used in the
key field as well as the base `key` type. So for `key_test`,
`key_key_test` specifies key type for the key field, `int8_key_test` is `int8`
for the key field and so on. Procedures other than `key_key_test` will call
the `set` function to generate a key type and pass to `key_key_test`.

### The `stdlib_hashmaps` module's public constants

The module defines several categories of public constants. Some are
Expand Down Expand Up @@ -924,6 +932,7 @@ The type's definition is below:

```fortran
type, abstract :: hashmap_type
private
integer(int_calls) :: call_count = 0
integer(int_calls) :: probe_count = 0
Expand All @@ -932,22 +941,52 @@ The type's definition is below:
integer(int_index) :: num_free = 0
integer(int32) :: nbits = default_bits
procedure(hasher_fun), pointer, nopass :: hasher => fnv_1_hasher
contains
procedure, non_overridable, pass(map) :: calls
procedure, non_overridable, pass(map) :: entries
procedure, non_overridable, pass(map) :: map_probes
procedure, non_overridable, pass(map) :: slots_bits
procedure, non_overridable, pass(map) :: num_slots
procedure(get_all_keys), deferred, pass(map) :: get_all_keys
procedure(get_other), deferred, pass(map) :: get_other_data
procedure(init_map), deferred, pass(map) :: init
procedure(key_test), deferred, pass(map) :: key_test
procedure(loading), deferred, pass(map) :: loading
procedure(map_entry), deferred, pass(map) :: map_entry
procedure(rehash_map), deferred, pass(map) :: rehash
procedure(remove_entry), deferred, pass(map) :: remove
procedure(set_other), deferred, pass(map) :: set_other_data
procedure(total_depth), deferred, pass(map) :: total_depth
procedure, non_overridable, pass(map) :: slots_bits
procedure(get_all_keys), deferred, pass(map) :: get_all_keys
procedure(init_map), deferred, pass(map) :: init
procedure(loading), deferred, pass(map) :: loading
procedure(rehash_map), deferred, pass(map) :: rehash
procedure(total_depth), deferred, pass(map) :: total_depth
!! Generic interfaces for key types.
procedure(key_key_test), deferred, pass(map) :: key_key_test
procedure, non_overridable, pass(map) :: int8_key_test
procedure, non_overridable, pass(map) :: int32_key_test
procedure, non_overridable, pass(map) :: char_key_test
procedure(key_map_entry), deferred, pass(map) :: key_map_entry
procedure, non_overridable, pass(map) :: int8_map_entry
procedure, non_overridable, pass(map) :: int32_map_entry
procedure, non_overridable, pass(map) :: char_map_entry
procedure(key_get_other_data), deferred, pass(map) :: key_get_other_data
procedure, non_overridable, pass(map) :: int8_get_other_data
procedure, non_overridable, pass(map) :: int32_get_other_data
procedure, non_overridable, pass(map) :: char_get_other_data
procedure(key_remove_entry), deferred, pass(map) :: key_remove_entry
procedure, non_overridable, pass(map) :: int8_remove_entry
procedure, non_overridable, pass(map) :: int32_remove_entry
procedure, non_overridable, pass(map) :: char_remove_entry
procedure(key_set_other_data), deferred, pass(map) :: key_set_other_data
procedure, non_overridable, pass(map) :: int8_set_other_data
procedure, non_overridable, pass(map) :: int32_set_other_data
procedure, non_overridable, pass(map) :: char_set_other_data
generic, public :: key_test => key_key_test, int8_key_test, int32_key_test, char_key_test
generic, public :: map_entry => key_map_entry, int8_map_entry, int32_map_entry, char_map_entry
generic, public :: get_other_data => key_get_other_data, int8_get_other_data, int32_get_other_data, char_get_other_data
generic, public :: remove => key_remove_entry, int8_remove_entry, int32_remove_entry, char_remove_entry
generic, public :: set_other_data => key_set_other_data, int8_set_other_data, int32_set_other_data, char_set_other_data
end type hashmap_type
```

Expand Down Expand Up @@ -1028,21 +1067,21 @@ as follows:
```fortran
type, extends(hashmap_type) :: chaining_hashmap_type
private
type(chaining_map_entry_pool), pointer :: cache => null()
type(chaining_map_entry_type), pointer :: free_list => null()
type(chaining_map_entry_ptr), allocatable :: inverse(:)
type(chaining_map_entry_pool), pointer :: cache => null()
type(chaining_map_entry_type), pointer :: free_list => null()
type(chaining_map_entry_ptr), allocatable :: inverse(:)
type(chaining_map_entry_ptr), allocatable :: slots(:)
contains
procedure :: get_all_keys => get_all_chaining_keys
procedure :: get_other_data => get_other_chaining_data
procedure :: key_get_other_data => get_other_chaining_data
procedure :: init => init_chaining_map
procedure :: key => chaining_key_test
procedure :: loading => chaining_loading
procedure :: map_entry => map_chain_entry
procedure :: key_map_entry => map_chain_entry
procedure :: rehash => rehash_chaining_map
procedure :: remove => remove_chaining_entry
procedure :: set_other_data => set_other_chaining_data
procedure :: key_remove_entry => remove_chaining_entry
procedure :: key_set_other_data => set_other_chaining_data
procedure :: total_depth => total_chaining_depth
procedure :: key_key_test => chaining_key_test
final :: free_chaining_map
end type chaining_hashmap_type
```
Expand Down Expand Up @@ -1103,24 +1142,24 @@ It also implements all of the deferred procedures of the
as follows:

```fortran
type, extends(hashmap_type) :: open_hashmap_type
private
type, extends(hashmap_type) :: open_hashmap_type
private
integer(int_index) :: index_mask = 2_int_index**default_bits-1
type(open_map_entry_pool), pointer :: cache => null()
type(open_map_entry_list), pointer :: free_list => null()
type(open_map_entry_ptr), allocatable :: inverse(:)
integer(int_index), allocatable :: slots(:)
type(open_map_entry_list), pointer :: free_list => null()
type(open_map_entry_ptr), allocatable :: inverse(:)
integer(int_index), allocatable :: slots(:)
contains
procedure :: get_all_keys => get_all_open_keys
procedure :: get_other_data => get_other_open_data
procedure :: key_get_other_data => get_other_open_data
procedure :: init => init_open_map
procedure :: key_test => open_key_test
procedure :: loading => open_loading
procedure :: map_entry => map_open_entry
procedure :: key_map_entry => map_open_entry
procedure :: rehash => rehash_open_map
procedure :: remove => remove_open_entry
procedure :: set_other_data => set_other_open_data
procedure :: key_remove_entry => remove_open_entry
procedure :: key_set_other_data => set_other_open_data
procedure :: total_depth => total_open_depth
procedure :: key_key_test => open_key_test
final :: free_open_map
end type open_hashmap_type
```
Expand Down Expand Up @@ -1323,8 +1362,8 @@ Subroutine
`intent(inout)` argument. It will be
the hash map used to store and access the other data.

`key`: shall be a scalar expression of type `key_type`. It
is an `intent(in)` argument.
`key`: shall be a of type `key_type` scalar, `character` scalar, `int8` array
or `int32` array. It is an `intent(in)` argument.

`other`: shall be a variable of type `other_data`.
It is an `intent(out)` argument. It is the other data associated
Expand Down Expand Up @@ -1435,9 +1474,9 @@ Subroutine.
It is an `intent(inout)` argument. It is the hash map whose entries
are examined.

`key`: shall be a scalar expression of type `key_type`. It
is an `intent(in)` argument. It is a `key` whose presence in the `map`
is being examined.
`key`: shall be a of type `key_type` scalar, `character` scalar, `int8` array
or `int32` array. It is an `intent(in)` argument. It is a `key` whose
presence in the `map` is being examined.

`present` (optional): shall be a scalar variable of type default
`logical`. It is an intent(out) argument. It is a logical flag where
Expand Down Expand Up @@ -1516,9 +1555,9 @@ Subroutine
is an `intent(inout)` argument. It is the hash map to receive the
entry.

`key`: shall be a scalar expression of type `key_type`.
It is an `intent(in)` argument. It is the key for the entry to be
placed in the table.
`key`: shall be a of type `key_type` scalar, `character` scalar, `int8` array
or `int32` array. It is an `intent(in)` argument. It is the key for the entry
to be placed in the table.

`other` (optional): shall be a scalar expression of type `other_type`.
It is an `intent(in)` argument. If present it is the other data to be
Expand Down Expand Up @@ -1677,9 +1716,9 @@ Subroutine
It is an `intent(inout)` argument. It is the hash map with the element
to be removed.

`key`: shall be a scalar expression of type `key_type`. It
is an `intent(in)` argument. It is the `key` identifying the entry
to be removed.
`key`: shall be a of type `key_type` scalar, `character` scalar, `int8` array
or `int32` array. It is an `intent(in)` argument. It is the `key` identifying
the entry to be removed.

`existed` (optional): shall be a scalar variable of type default
logical. It is an `intent(out)` argument. If present with the value
Expand Down Expand Up @@ -1719,9 +1758,9 @@ Subroutine
is an `intent(inout)` argument. It will be a hash map used to store
and access the entry's data.

`key`: shall be a scalar expression of type `key_type`. It
is an `intent(in)` argument. It is the `key` to the entry whose
`other` data is to be replaced.
`key`: shall be a of type `key_type` scalar, `character` scalar, `int8` array
or `int32` array. It is an `intent(in)` argument. It is the `key` to the
entry whose `other` data is to be replaced.

`other`: shall be a scalar expression of type `other_type`.
It is an `intent(in)` argument. It is the data to be stored as
Expand Down
17 changes: 5 additions & 12 deletions example/hashmaps/example_hashmaps_get_all_keys.f90
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
program example_hashmaps_get_all_keys
use stdlib_kinds, only: int32
use stdlib_hashmaps, only: chaining_hashmap_type
use stdlib_hashmap_wrappers, only: fnv_1_hasher, &
use stdlib_hashmap_wrappers, only: fnv_1_hasher, get, &
key_type, other_type, set
implicit none
type(chaining_hashmap_type) :: map
Expand All @@ -10,6 +10,8 @@ program example_hashmaps_get_all_keys

type(key_type), allocatable :: keys(:)
integer(int32) :: i

character(:), allocatable :: str

call map%init(fnv_1_hasher)

Expand All @@ -33,20 +35,11 @@ program example_hashmaps_get_all_keys
!Number of keys in the hashmap = 3

do i = 1, size(keys)
print '("Value of key ", I0, " = ", A)', i, key_to_char(keys(i))
call get( keys(i), str )
print '("Value of key ", I0, " = ", A)', i, str
end do
!Value of key 1 = initial key
!Value of key 2 = second key
!Value of key 3 = last key

contains
!Converts key type to character type
pure function key_to_char(key) result(str)
type(key_type), intent(in) :: key
character(:), allocatable :: str
character(:), allocatable :: str_mold

allocate( character(len=size(key%value)) :: str_mold )
str = transfer(key%value, str_mold)
end function key_to_char
end program example_hashmaps_get_all_keys
86 changes: 79 additions & 7 deletions example/hashmaps/example_hashmaps_get_other_data.f90
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
program example_get_other_data
use stdlib_kinds, only: int8
use stdlib_kinds, only: int8, int64
use stdlib_hashmaps, only: chaining_hashmap_type, int_index
use stdlib_hashmap_wrappers, only: fnv_1_hasher, key_type, other_type, set, get
implicit none
Expand All @@ -8,15 +8,23 @@ program example_get_other_data
type(other_type) :: other
type(chaining_hashmap_type) :: map
type dummy_type
integer(int8) :: value(4)
integer :: value(4)
end type dummy_type
type(dummy_type) :: dummy
class(*), allocatable :: data
dummy%value = [4_int8, 3_int8, 2_int8, 1_int8]
allocate (data, source=dummy)
class(*), allocatable :: data
integer(int8), allocatable :: key_array(:)
integer :: int_scalar

! Initialize hashmap
call map%init(fnv_1_hasher)
call set(key, [0_int8, 1_int8, 2_int8, 3_int8, 4_int8])
call set(other, data)

! Hashmap functions are setup to store scalar value types (other). Use a dervied
! type wrapper to store arrays.
dummy%value = [4, 3, 2, 1]
call set(other, dummy)

! Explicitly set key type using set function
call set(key, [0, 1])
call map%map_entry(key, other, conflict)
if (.not. conflict) then
call map%get_other_data(key, other)
Expand All @@ -30,4 +38,68 @@ program example_get_other_data
class default
print *, 'Invalid data type in other'
end select

! Also can use map_entry and get_other_data generic key interfaces.
! This is an exmple with integer arrays.
call map%map_entry( [2,3], other, conflict)
if (.not. conflict) then
call map%get_other_data( [2,3], other)
else
error stop 'Key is already present in the map.'
end if
call get(other, data)
select type (data)
type is (dummy_type)
print *, 'Other data % value = ', data%value
class default
print *, 'Invalid data type in other'
end select

! Integer scalars need to be passed as an array.
int_scalar = 2
call map%map_entry( [int_scalar], other, conflict)
if (.not. conflict) then
call map%get_other_data( [int_scalar], other)
else
error stop 'Key is already present in the map.'
end if
call get(other, data)
select type (data)
type is (dummy_type)
print *, 'Other data % value = ', data%value
class default
print *, 'Invalid data type in other'
end select

! Example using character type key interface
call map%map_entry( 'key_string', other, conflict)
if (.not. conflict) then
call map%get_other_data( 'key_string', other)
else
error stop 'Key is already present in the map.'
end if
call get(other, data)
select type (data)
type is (dummy_type)
print *, 'Other data % value = ', data%value
class default
print *, 'Invalid data type in other'
end select

! Transfer to int8 arrays to generate key for unsupported types.
key_array = transfer( [0_int64, 1_int64], [0_int8] )
call map%map_entry( key_array, other, conflict)
if (.not. conflict) then
call map%get_other_data( key_array, other)
else
error stop 'Key is already present in the map.'
end if
call get(other, data)
select type (data)
type is (dummy_type)
print *, 'Other data % value = ', data%value
class default
print *, 'Invalid data type in other'
end select

end program example_get_other_data
Loading

0 comments on commit d996e43

Please sign in to comment.