Skip to content

hashmap-init-update #974

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

Merged
merged 10 commits into from
Apr 15, 2025
13 changes: 8 additions & 5 deletions doc/specs/stdlib_hashmaps.md
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,7 @@ type. Each of these types are described below.

The `hashmap_type` abstract type serves as the parent type for the two
types `chaining_hashmap_type` and `open_hashmap_type`. It defines
seven private components:
eight private components:

* `call_count` - the number of procedure calls on the map;

Expand All @@ -782,6 +782,8 @@ seven private components:

* `hasher` - a pointer to the hash function used by the map.

* `initialized` - track if map has been initialized

It also defines five non-overridable procedures:

* `calls` - returns the number of procedure calls on the map;
Expand Down Expand Up @@ -1074,7 +1076,7 @@ are listed below.

Procedure to initialize a chaining hash map:

* `map % init( hasher[, slots_bits, status] )` - Routine
* `map % init( [hasher, slots_bits, status] )` - Routine
to initialize a chaining hash map.

Procedure to modify the structure of a map:
Expand Down Expand Up @@ -1295,7 +1297,7 @@ Initializes a `hashmap_type` object.

##### Syntax

`call map % ` [[hashmap_type(type):init(bound)]] `( hasher [, slots_bits, status ] )`
`call map % ` [[hashmap_type(type):init(bound)]] `( [hasher, slots_bits, status ] )`

##### Class

Expand All @@ -1308,9 +1310,10 @@ Subroutine
`intent(out)` argument. It will
be a hash map used to store and access the entries.

`hasher`: shall be a procedure with interface `hash_fun`.
`hasher`: (optional): shall be a procedure with interface `hash_fun`.
It is an `intent(in)` argument. It is the procedure to be used to
generate the hashes for the table from the keys of the entries.
generate the hashes for the table from the keys of the entries.
Defaults to fnv_1_hasher if not provided.

`slots_bits` (optional): shall be a scalar default integer
expression. It is an `intent(in)` argument. The initial number of
Expand Down
3 changes: 1 addition & 2 deletions example/hashmaps/example_hashmaps_calls.f90
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
program example_calls
use stdlib_hashmaps, only: chaining_hashmap_type, int_calls
use stdlib_hashmap_wrappers, only: fnv_1_hasher
implicit none
type(chaining_hashmap_type) :: map
integer(int_calls) :: initial_calls
call map%init(fnv_1_hasher)
call map%init()
initial_calls = map%calls()
print *, "INITIAL_CALLS = ", initial_calls
end program example_calls
3 changes: 1 addition & 2 deletions example/hashmaps/example_hashmaps_entries.f90
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
program example_entries
use stdlib_hashmaps, only: open_hashmap_type, int_index
use stdlib_hashmap_wrappers, only: fnv_1_hasher
implicit none
type(open_hashmap_type) :: map
integer(int_index) :: initial_entries
call map%init(fnv_1_hasher)
call map%init()
initial_entries = map%entries()
print *, "INITIAL_ENTRIES = ", initial_entries
end program example_entries
5 changes: 1 addition & 4 deletions example/hashmaps/example_hashmaps_get_all_keys.f90
Original file line number Diff line number Diff line change
@@ -1,8 +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, get, &
key_type, set
use stdlib_hashmap_wrappers, only: get, key_type, set
implicit none
type(chaining_hashmap_type) :: map
type(key_type) :: key
Expand All @@ -12,8 +11,6 @@ program example_hashmaps_get_all_keys

character(:), allocatable :: str

call map%init(fnv_1_hasher)

! adding key-value pairs to the map
call set(key, "initial key")
call map%map_entry(key, "value 1")
Expand Down
5 changes: 1 addition & 4 deletions example/hashmaps/example_hashmaps_get_other_data.f90
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
program example_get_other_data
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, set, get
use stdlib_hashmap_wrappers, only: key_type, set
implicit none
logical :: conflict
type(key_type) :: key
Expand All @@ -14,9 +14,6 @@ program example_get_other_data
integer(int8), allocatable :: key_array(:)
integer :: int_scalar

! Initialize hashmap
call map%init(fnv_1_hasher)

! Hashmap functions are setup to store scalar value types (other). Use a dervied
! type wrapper to store arrays.
dummy%value = [4, 3, 2, 1]
Expand Down
23 changes: 21 additions & 2 deletions example/hashmaps/example_hashmaps_init.f90
Original file line number Diff line number Diff line change
@@ -1,7 +1,26 @@
program example_init
use stdlib_hashmaps, only: chaining_hashmap_type
use stdlib_hashmaps, only: chaining_hashmap_type, open_hashmap_type
use stdlib_hashmap_wrappers, only: fnv_1_hasher
implicit none
type(chaining_hashmap_type) :: map
call map%init(fnv_1_hasher, slots_bits=10)
logical :: present


!If default values are used, then init can be typically be skipped as the first map_entry call will initialize the map using default values.
call map%map_entry('key', 'value')
call map%key_test('key', present)
print *, "Key exists without explicit init call = ", present

! Init can be called to clear all items in a map.
call map%init()
call map%key_test('key', present)
print *, "Key exists after re-initalization = ", present

! User can optional specify hasher type and slots_bits instead of using default values.
! Number of slots in the hashmap will initially equal 2**slots_bits.
! The hashmap will automatically re-size as needed; however for better performance, a rule of thumb is to size so that number of slots is ~2X expected number of entries.
! In this example with slots_bits=10, there will initially be 1024 slots in the map.
call map%init(hasher=fnv_1_hasher, slots_bits=10)
call map%map_entry('key', 'value')

end program example_init
6 changes: 4 additions & 2 deletions example/hashmaps/example_hashmaps_key_test.f90
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
program example_key_test
use stdlib_kinds, only: int8
use stdlib_hashmaps, only: chaining_hashmap_type
use stdlib_hashmap_wrappers, only: fnv_1_hasher, key_type, set
use stdlib_hashmap_wrappers, only: key_type, set
implicit none
type(chaining_hashmap_type) :: map
type(key_type) :: key
logical :: present
call map%init(fnv_1_hasher)

call map%init()
call set(key, [0_int8, 1_int8])
call map%key_test(key, present)
print *, "Initial key of 10 present for empty map = ", present

end program example_key_test
3 changes: 1 addition & 2 deletions example/hashmaps/example_hashmaps_loading.f90
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
program example_loading
use stdlib_hashmaps, only: open_hashmap_type
use stdlib_hashmap_wrappers, only: fnv_1_hasher
implicit none
type(open_hashmap_type) :: map
real :: ratio
call map%init(fnv_1_hasher)
call map%init()
ratio = map%loading()
print *, "Initial loading = ", ratio
end program example_loading
4 changes: 2 additions & 2 deletions example/hashmaps/example_hashmaps_map_entry.f90
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
program example_map_entry
use, intrinsic:: iso_fortran_env, only: int8, int64
use stdlib_hashmaps, only: chaining_hashmap_type
use stdlib_hashmap_wrappers, only: fnv_1_hasher, key_type, set
use stdlib_hashmap_wrappers, only: key_type, set
implicit none
type(chaining_hashmap_type) :: map
type(key_type) :: key
Expand All @@ -16,7 +16,7 @@ program example_map_entry

! Initialize hashmap with 2^10 slots.
! Hashmap will dynamically increase size if needed.
call map%init(fnv_1_hasher, slots_bits=10)
call map%init(slots_bits=10)

! Explicitly set key using set function
call set(key, [1, 2, 3])
Expand Down
3 changes: 1 addition & 2 deletions example/hashmaps/example_hashmaps_num_slots.f90
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
program example_num_slots
use stdlib_hashmaps, only: chaining_hashmap_type, int_index
use stdlib_hashmap_wrappers, only: fnv_1_hasher
implicit none
type(chaining_hashmap_type) :: map
integer(int_index) :: initial_slots
call map%init(fnv_1_hasher)
call map%init()
initial_slots = map%num_slots()
print *, "Initial slots = ", initial_slots
end program example_num_slots
3 changes: 1 addition & 2 deletions example/hashmaps/example_hashmaps_probes.f90
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
program example_probes
use stdlib_hashmaps, only: chaining_hashmap_type
use stdlib_hashmap_wrappers, only: fnv_1_hasher
implicit none
type(chaining_hashmap_type) :: map
integer :: nprobes
call map%init(fnv_1_hasher)
call map%init()
nprobes = map%map_probes()
print *, "Initial probes = ", nprobes
end program example_probes
4 changes: 2 additions & 2 deletions example/hashmaps/example_hashmaps_rehash.f90
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
program example_rehash
use stdlib_kinds, only: int8
use stdlib_hashmaps, only: open_hashmap_type
use stdlib_hashmap_wrappers, only: fnv_1_hasher, fnv_1a_hasher, &
use stdlib_hashmap_wrappers, only: fnv_1a_hasher, &
key_type, set
implicit none
type(open_hashmap_type) :: map
type(key_type) :: key
call map%init(fnv_1_hasher, slots_bits=10)
call map%init(slots_bits=10)
call set(key, [5_int8, 7_int8, 4_int8, 13_int8])
call map%map_entry(key, 'A value')
call map%rehash(fnv_1a_hasher)
Expand Down
5 changes: 2 additions & 3 deletions example/hashmaps/example_hashmaps_remove.f90
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
program example_remove
use stdlib_kinds, only: int8, int64
use stdlib_hashmaps, only: open_hashmap_type
use stdlib_hashmap_wrappers, only: fnv_1_hasher, &
fnv_1a_hasher, key_type, set
use stdlib_hashmap_wrappers, only: key_type, set
implicit none
type(open_hashmap_type) :: map
type(key_type) :: key
Expand All @@ -11,7 +10,7 @@ program example_remove

! Initialize hashmap with 2^10 slots.
! Hashmap will dynamically increase size if needed.
call map%init(fnv_1_hasher, slots_bits=10)
call map%init(slots_bits=10)
! Explicitly set key type using set function
call set(key, [1, 2, 3])
call map%map_entry(key, 4.0)
Expand Down
4 changes: 2 additions & 2 deletions example/hashmaps/example_hashmaps_set_other_data.f90
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
program example_set_other_data
use stdlib_kinds, only: int8
use stdlib_hashmaps, only: open_hashmap_type, chaining_hashmap_type
use stdlib_hashmap_wrappers, only: key_type, set, fnv_1_hasher
use stdlib_hashmap_wrappers, only: key_type, set

implicit none
logical :: exists
Expand All @@ -11,7 +11,7 @@ program example_set_other_data

! Initialize hashmap with 2^10 slots.
! Hashmap will dynamically increase size if needed.
call map%init(fnv_1_hasher, slots_bits=10)
call map%init(slots_bits=10)

call set(key, [5, 7, 4, 13])

Expand Down
3 changes: 1 addition & 2 deletions example/hashmaps/example_hashmaps_slots_bits.f90
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
program example_slots_bits
use stdlib_hashmaps, only: chaining_hashmap_type
use stdlib_hashmap_wrappers, only: fnv_1_hasher
implicit none
type(chaining_hashmap_type) :: map
integer :: bits
call map%init(fnv_1_hasher)
call map%init()
bits = map%slots_bits()
print *, "Initial slot bits = ", bits
end program example_slots_bits
3 changes: 1 addition & 2 deletions example/hashmaps/example_hashmaps_total_depth.f90
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
program example_total_depth
use stdlib_hashmaps, only: chaining_hashmap_type, int_depth
use stdlib_hashmap_wrappers, only: fnv_1_hasher
implicit none
type(chaining_hashmap_type) :: map
integer(int_depth) :: initial_depth
call map%init(fnv_1_hasher)
call map%init()
initial_depth = map%total_depth()
print *, "Initial total depth = ", initial_depth
end program example_total_depth
12 changes: 9 additions & 3 deletions src/stdlib_hashmap_chaining.f90
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ module subroutine init_chaining_map( map, &
!! greater than max_bits
!
class(chaining_hashmap_type), intent(out) :: map
procedure(hasher_fun) :: hasher
procedure(hasher_fun), optional :: hasher
integer, intent(in), optional :: slots_bits
integer(int32), intent(out), optional :: status

Expand All @@ -448,8 +448,9 @@ module subroutine init_chaining_map( map, &
map % probe_count = 0
map % total_probes = 0

map % hasher => hasher

! Check if user has specified a hasher other than the default hasher.
if (present(hasher)) map % hasher => hasher

call free_chaining_map( map )

if ( present(slots_bits) ) then
Expand Down Expand Up @@ -502,6 +503,8 @@ module subroutine init_chaining_map( map, &

call extend_map_entry_pool(map)

map % initialized = .true.

if (present(status) ) status = success

end subroutine init_chaining_map
Expand Down Expand Up @@ -545,6 +548,9 @@ module subroutine map_chain_entry(map, key, other, conflict)
type(chaining_map_entry_type), pointer :: gentry, pentry, sentry
character(*), parameter :: procedure = 'MAP_ENTRY'

! Check that map is initialized.
if (.not. map % initialized) call init_chaining_map( map )

hash_val = map % hasher( key )

if ( map % probe_count > map_probe_factor * map % call_count ) then
Expand Down
14 changes: 10 additions & 4 deletions src/stdlib_hashmap_open.f90
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ module subroutine init_open_map( map, &
!! greater than max_bits

class(open_hashmap_type), intent(out) :: map
procedure(hasher_fun) :: hasher
procedure(hasher_fun), optional :: hasher
integer, intent(in), optional :: slots_bits
integer(int32), intent(out), optional :: status

Expand All @@ -424,8 +424,9 @@ module subroutine init_open_map( map, &
map % call_count = 0
map % probe_count = 0
map % total_probes = 0

map % hasher => hasher

! Check if user has specified a hasher other than the default hasher.
if (present(hasher)) map % hasher => hasher

if ( present(slots_bits) ) then
if ( slots_bits < default_bits .OR. &
Expand Down Expand Up @@ -491,6 +492,8 @@ module subroutine init_open_map( map, &
end do

call extend_map_entry_pool(map % cache)

map % initialized = .true.

if (present(status) ) status = success

Expand Down Expand Up @@ -533,7 +536,10 @@ module subroutine map_open_entry(map, key, other, conflict)
integer(int_hash) :: hash_val
integer(int_index) :: inmap, offset, test_slot
character(*), parameter :: procedure = 'MAP_ENTRY'


! Check that map is initialized.
if (.not. map % initialized) call init_open_map( map )

hash_val = map % hasher( key )

if ( map % probe_count > map_probe_factor * map % call_count .or. &
Expand Down
Loading
Loading