Skip to content

Commit 5a42241

Browse files
scivision14NGiestas
andcommitted
add %iterate method()
Co-authored-by: 14NGiestas <14NGiestas@users.noreply.github.com> add %visit method() Co-authored-by: 14NGiestas <14NGiestas@users.noreply.github.com>
1 parent 95b0f71 commit 5a42241

File tree

9 files changed

+411
-1
lines changed

9 files changed

+411
-1
lines changed

API.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,30 @@ character(*), intent(in) :: dname, attr !< dataset name, attribute name
294294
class(*), intent(out) :: attrval(:) !< character, real, integer
295295
```
296296

297+
## Iterate over all datasets in a group
298+
299+
```fortran
300+
call h%iterate(group, callback)
301+
character(*), intent(in) :: group
302+
subroutine callback(group_name, object_name, object_type)
303+
character(len=*), intent(in) :: group_name
304+
character(len=*), intent(in) :: object_name
305+
character(len=*), intent(in) :: object_type
306+
end subroutine
307+
```
308+
309+
## Visit recursively all datasets starting from a group
310+
311+
```fortran
312+
call h%visit(group, callback)
313+
character(*), intent(in) :: group
314+
subroutine callback(group_name, object_name, object_type)
315+
character(len=*), intent(in) :: group_name
316+
character(len=*), intent(in) :: object_name
317+
character(len=*), intent(in) :: object_type
318+
end subroutine
319+
```
320+
297321
## delete attribute
298322

299323
```fortran

fpm.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,11 @@ main = "test_string.f90"
7777
[[test]]
7878
name = "version"
7979
main = "test_version.f90"
80+
81+
[[test]]
82+
name = "visit"
83+
main = "test_visit.f90"
84+
85+
[[test]]
86+
name = "iterate"
87+
main = "test_iterate.f90"

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ set(s ${CMAKE_CURRENT_SOURCE_DIR})
22

33
target_sources(h5fortran PRIVATE
44
${s}/utils.f90 ${s}/datatype.f90 ${s}/deflate.f90
5+
${s}/iterate.f90 ${s}/visit.f90
56
${s}/read.f90 ${s}/read_scalar.f90 ${s}/read_ascii.f90 ${s}/reader.f90
67
${s}/write.f90 ${s}/write_scalar.f90 ${s}/writer.f90
78
${s}/reader_lt.f90 ${s}/writer_lt.f90

src/interface.f90

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ module h5fortran
5656
procedure, public :: is_open
5757
procedure, public :: delete_attr => attr_delete
5858
procedure, public :: exist_attr => attr_exist
59+
procedure, public :: iterate => hdf_iterate
60+
procedure, public :: visit => hdf_visit
5961
!! procedures without mapping
6062

6163
!> below are procedure that need generic mapping (type or rank agnostic)
@@ -774,6 +776,61 @@ module pure subroutine estop(ier, id, filename, obj_name, attr_name)
774776
character(*), intent(in), optional :: obj_name, attr_name
775777
end subroutine
776778

779+
module subroutine hdf_iterate(self, group_name, callback)
780+
!! Opens the HDF5 file and the specified group, then iterates over
781+
!! all members of the group. For each member the user‐provided
782+
!! callback is invoked with:
783+
!!
784+
!! self - the HDF5 file object
785+
!! group_name - name of the group
786+
!! object_name - name of the member object
787+
!! object_type - a short string indicating type ("group", "dataset",
788+
!! "datatype", or "other")
789+
class(hdf5_file), intent(in) :: self
790+
character(*), intent(in) :: group_name
791+
interface
792+
subroutine user_callback_interface(group_name, object_name, object_type)
793+
character(*), intent(in) :: group_name
794+
!! The name of the group being traversed.
795+
character(*), intent(in) :: object_name
796+
!! The name of the object encountered.
797+
character(*), intent(in) :: object_type
798+
!!A short description such as "group", "dataset",
799+
!! "datatype", or "other"
800+
end subroutine
801+
end interface
802+
803+
procedure(user_callback_interface) :: callback
804+
end subroutine
805+
806+
807+
module subroutine hdf_visit(self, group_name, callback)
808+
!! Opens the HDF5 file and the specified group, then visits recursively
809+
!! all members of the group. For each member the user‐provided
810+
!! callback is invoked with:
811+
!!
812+
!! self - the HDF5 file object
813+
!! group_name - name of the group
814+
!! object_name - name of the member object
815+
!! object_type - a short string indicating type ("group", "dataset",
816+
!! "datatype", or "other")
817+
class(hdf5_file), intent(in) :: self
818+
character(len=*), intent(in) :: group_name
819+
interface
820+
subroutine user_callback_interface(group_name, object_name, object_type)
821+
character(len=*), intent(in) :: group_name
822+
!! The name of the group being traversed.
823+
character(len=*), intent(in) :: object_name
824+
!! The name of the object encountered.
825+
character(len=*), intent(in) :: object_type
826+
!!A short description such as "group", "dataset",
827+
!! "datatype", or "other"
828+
end subroutine
829+
end interface
830+
831+
procedure(user_callback_interface) :: callback
832+
end subroutine
833+
777834
end interface
778835

779836

src/iterate.f90

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
submodule (h5fortran:hdf5_read) iterate_smod
2+
use hdf5, only : H5Literate_f, h5l_info_t, H5O_info_t, H5_INDEX_NAME_F, H5_ITER_NATIVE_F, &
3+
H5Gopen_f, H5Gclose_f, H5Oget_info_by_name_f, H5O_TYPE_GROUP_F, &
4+
H5O_TYPE_DATASET_F, H5O_TYPE_NAMED_DATATYPE_F
5+
use, intrinsic :: iso_c_binding, only: c_char, c_int, c_funptr, c_long, c_funloc, c_NULL_PTR, c_associated
6+
7+
implicit none
8+
9+
interface
10+
subroutine user_callback_interface(group_name, object_name, object_type)
11+
character(*), intent(in) :: group_name
12+
!! The name of the group being traversed.
13+
character(*), intent(in) :: object_name
14+
!! The name of the object encountered.
15+
character(*), intent(in) :: object_type
16+
!!A short description such as "group", "dataset",
17+
!! "datatype", or "other"
18+
end subroutine
19+
end interface
20+
21+
type :: iterate_data_t
22+
procedure(user_callback_interface), nopass, pointer :: callback => null()
23+
end type iterate_data_t
24+
25+
contains
26+
27+
module procedure hdf_iterate
28+
integer(hid_t) :: group_id
29+
integer(c_int) :: status
30+
integer(hsize_t) :: idx
31+
type(c_funptr) :: funptr
32+
type(c_ptr) :: op_data_ptr
33+
integer(c_int) :: return_value
34+
35+
type(iterate_data_t) :: data
36+
37+
! Fill the iteration data with the user’s group name and callback.
38+
data % callback => callback
39+
40+
! Open the group.
41+
call H5Gopen_f(self%file_id, trim(group_name), group_id, status)
42+
call estop(status, "hdf_iterate:H5Gopen_f", self%filename, "Error opening group: " // trim(group_name))
43+
44+
idx = 0
45+
op_data_ptr = C_NULL_PTR
46+
! Get the C function pointer for our internal callback.
47+
funptr = c_funloc(internal_iterate_callback)
48+
49+
! Call H5Literate_f to iterate over the group.
50+
call H5Literate_f(group_id, H5_INDEX_NAME_F, H5_ITER_NATIVE_F, idx, &
51+
funptr, op_data_ptr, return_value, status)
52+
call estop(status, "hdf_iterate:H5Literate_f", self%filename, "Error during iteration of group: " // trim(group_name))
53+
54+
! Close the group and file.
55+
call H5Gclose_f(group_id, status)
56+
57+
contains
58+
59+
integer(c_int) function internal_iterate_callback(grp_id, name, info, op_data) bind(C)
60+
!! internal_iterate_callback:
61+
!!
62+
!! This is the callback procedure that will be passed to H5Literate_f.
63+
!! It matches HDF5’s expected signature (using bind(C)) and is called
64+
!! for each object in the group.
65+
!!
66+
!! It extracts the object name from the provided character array,
67+
!! calls H5Oget_info_by_name_f to determine the object type, and then
68+
!! calls the user's callback with the high-level parameters.
69+
integer(c_long), intent(in), value :: grp_id
70+
character(1, kind=c_char), intent(in) :: name(0:255)
71+
type(h5l_info_t), intent(in) :: info
72+
type(c_ptr), intent(in) :: op_data
73+
74+
integer :: i, ierr
75+
type(H5O_info_t) :: infobuf
76+
character(len=256) :: name_string
77+
character(:), allocatable :: object_type
78+
79+
! avoid unused argument warning
80+
if (C_associated(op_data) .and. info % corder == 0) i = 0
81+
82+
! Build a Fortran string from the character array.
83+
do i = 0, 255
84+
if (name(i) == c_null_char) exit
85+
name_string(i+1:i+1) = name(i)(1:1)
86+
end do
87+
88+
! Retrieve object info using the object name.
89+
call H5Oget_info_by_name_f(grp_id, name_string(:i), infobuf, ierr)
90+
if (ierr /= 0) then
91+
internal_iterate_callback = ierr
92+
return
93+
end if
94+
95+
if(infobuf % type == H5O_TYPE_GROUP_F)then
96+
object_type = "group"
97+
else if(infobuf % type == H5O_TYPE_DATASET_F)then
98+
object_type = "dataset"
99+
else if(infobuf % type == H5O_TYPE_NAMED_DATATYPE_F)then
100+
object_type = "datatype"
101+
else
102+
object_type = "other"
103+
endif
104+
105+
! Call the user’s callback procedure.
106+
call data % callback(group_name, name_string(:i), object_type)
107+
108+
internal_iterate_callback = 0 ! Indicate success.
109+
end function internal_iterate_callback
110+
111+
end procedure hdf_iterate
112+
113+
end submodule

src/visit.f90

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
submodule (h5fortran:hdf5_read) visit_smod
2+
use, intrinsic :: iso_c_binding, only : c_long, c_char, c_funloc, c_int, c_funptr, c_null_ptr, C_associated
3+
use hdf5, only : h5l_info_t, h5o_info_t, H5O_TYPE_DATASET_F, H5O_TYPE_GROUP_F, &
4+
H5O_TYPE_NAMED_DATATYPE_F, H5O_TYPE_UNKNOWN_F, &
5+
H5_INDEX_NAME_F, H5_ITER_NATIVE_F, &
6+
H5Gopen_f, H5Gclose_f, H5Oget_info_by_name_f, &
7+
H5Ovisit_f
8+
9+
implicit none
10+
11+
interface
12+
subroutine user_callback_interface(group_name, object_name, object_type)
13+
character(*), intent(in) :: group_name
14+
!! The name of the group being traversed.
15+
character(*), intent(in) :: object_name
16+
!! The name of the object encountered.
17+
character(*), intent(in) :: object_type
18+
!!A short description such as "group", "dataset",
19+
!! "datatype", or "other"
20+
end subroutine
21+
end interface
22+
23+
type :: visit_data_t
24+
procedure(user_callback_interface), nopass, pointer :: callback => null()
25+
end type visit_data_t
26+
27+
contains
28+
29+
module procedure hdf_visit
30+
integer(hid_t) :: group_id
31+
integer(c_int) :: status
32+
integer(hsize_t) :: idx
33+
type(c_funptr) :: funptr
34+
type(c_ptr) :: op_data_ptr
35+
integer(c_int) :: return_value
36+
37+
type(visit_data_t) :: data
38+
39+
! Fill the iteration data with the user’s group name and callback.
40+
data % callback => callback
41+
42+
! Open the group.
43+
call H5Gopen_f(self%file_id, trim(group_name), group_id, status)
44+
call estop(status, "hdf_visit:H5Gopen_f", self%filename, "Error opening group: " // trim(group_name))
45+
46+
idx = 0
47+
op_data_ptr = C_NULL_PTR
48+
! Get the C function pointer for our internal callback.
49+
funptr = c_funloc(internal_visit_callback)
50+
51+
! Call H5Lvisit_f to visit over the group.
52+
call H5Ovisit_f(group_id, H5_INDEX_NAME_F, H5_ITER_NATIVE_F, &
53+
funptr, op_data_ptr, return_value, status)
54+
call estop(status, "hdf_visit:H5Lvisit_f", self%filename, "Error during iteration of group: " // trim(group_name))
55+
56+
! Close the group and file.
57+
call H5Gclose_f(group_id, status)
58+
59+
contains
60+
61+
integer(c_int) function internal_visit_callback(grp_id, name, info, op_data) bind(C)
62+
!! internal_visit_callback:
63+
!!
64+
!! This is the callback procedure that will be passed to H5Lvisit_f.
65+
!! It matches HDF5’s expected signature (using bind(C)) and is called
66+
!! for each object in the group.
67+
!!
68+
!! It extracts the object name from the provided character array,
69+
!! calls H5Oget_info_by_name_f to determine the object type, and then
70+
!! calls the user's callback with the high-level parameters.
71+
integer(c_long), intent(in), value :: grp_id
72+
character(1, kind=c_char), intent(in) :: name(0:255)
73+
type(h5l_info_t), intent(in) :: info
74+
type(c_ptr), intent(in) :: op_data
75+
76+
integer :: i, ierr
77+
type(H5O_info_t) :: infobuf
78+
character(256) :: name_string
79+
character(:), allocatable :: object_type
80+
81+
! avoid unused argument warning
82+
if (C_associated(op_data) .and. info % corder == 0) i = 0
83+
84+
! Build a Fortran string from the character array.
85+
do i = 0, 255
86+
if (name(i) == c_null_char) exit
87+
name_string(i+1:i+1) = name(i)(1:1)
88+
end do
89+
90+
! Retrieve object info using the object name.
91+
call H5Oget_info_by_name_f(grp_id, name_string(:i), infobuf, ierr)
92+
if (ierr /= 0) then
93+
internal_visit_callback = ierr
94+
return
95+
end if
96+
97+
if(infobuf % type == H5O_TYPE_GROUP_F)then
98+
object_type = "group"
99+
else if(infobuf % type == H5O_TYPE_DATASET_F)then
100+
object_type = "dataset"
101+
else if(infobuf % type == H5O_TYPE_NAMED_DATATYPE_F)then
102+
object_type = "datatype"
103+
else
104+
object_type = "other"
105+
endif
106+
107+
! Call the user’s callback procedure.
108+
call data % callback(group_name, name_string(:i), object_type)
109+
110+
internal_visit_callback = 0 ! Indicate success.
111+
end function internal_visit_callback
112+
113+
end procedure hdf_visit
114+
115+
end submodule

test/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ endfunction(setup_test)
5959

6060
set(test_names array attributes attributes_read
6161
cast deflate_write deflate_read deflate_props destructor exist
62-
groups layout lt scalar shape string string_read version write
62+
groups iterate layout lt scalar shape string string_read version visit write
6363
fail_read_size_mismatch fail_read_rank_mismatch fail_nonexist_variable)
6464
if(HAVE_IEEE_ARITH)
6565
list(APPEND test_names fill)

0 commit comments

Comments
 (0)