Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hashap key generic interfaces #827

Merged
merged 16 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading