Skip to content

Commit 88f024c

Browse files
scivision14NGiestas
andcommitted
add %visit method()
Co-authored-by: 14NGiestas <14NGiestas@users.noreply.github.com>
1 parent f2fd32b commit 88f024c

File tree

7 files changed

+226
-3
lines changed

7 files changed

+226
-3
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 & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +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
5+
${s}/iterate.f90 ${s}/visit.f90
66
${s}/read.f90 ${s}/read_scalar.f90 ${s}/read_ascii.f90 ${s}/reader.f90
77
${s}/write.f90 ${s}/write_scalar.f90 ${s}/writer.f90
88
${s}/reader_lt.f90 ${s}/writer_lt.f90

src/interface.f90

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ module h5fortran
5757
procedure, public :: delete_attr => attr_delete
5858
procedure, public :: exist_attr => attr_exist
5959
procedure, public :: iterate => hdf_iterate
60-
60+
procedure, public :: visit => hdf_visit
6161
!! procedures without mapping
6262

6363
!> below are procedure that need generic mapping (type or rank agnostic)
@@ -803,6 +803,34 @@ subroutine user_callback_interface(group_name, object_name, object_type)
803803
procedure(user_callback_interface) :: callback
804804
end subroutine
805805

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+
806834
end interface
807835

808836

src/visit.f90

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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 :: status, i, len
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+
len = i
87+
if (name(i) == c_null_char) exit
88+
name_string(i+1:i+1) = name(i)(1:1)
89+
end do
90+
91+
! Retrieve object info using the object name.
92+
call H5Oget_info_by_name_f(grp_id, name_string(1:len), infobuf, status)
93+
if (status /= 0) then
94+
internal_visit_callback = status
95+
return
96+
end if
97+
98+
if(infobuf % type == H5O_TYPE_GROUP_F)then
99+
object_type = "group"
100+
else if(infobuf % type == H5O_TYPE_DATASET_F)then
101+
object_type = "dataset"
102+
else if(infobuf % type == H5O_TYPE_NAMED_DATATYPE_F)then
103+
object_type = "datatype"
104+
else
105+
object_type = "other"
106+
endif
107+
108+
! Call the user’s callback procedure.
109+
call data % callback(group_name, name_string(1:len), object_type)
110+
111+
internal_visit_callback = 0 ! Indicate success.
112+
end function internal_visit_callback
113+
114+
end procedure hdf_visit
115+
116+
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 iterate 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)

test/test_visit.f90

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
program test_visit
2+
3+
use, intrinsic :: iso_fortran_env, only: real64
4+
use h5fortran, only: hdf5_file
5+
6+
implicit none
7+
8+
type(hdf5_file) :: h
9+
character(*), parameter :: filename='test_visit.h5'
10+
integer :: i = 0
11+
12+
! Create a sample HDF5 file
13+
call h%open(filename, "w")
14+
15+
call h%create_group("/group1")
16+
call h%create_group("/group1/group2")
17+
call h%write("/dataset1", 1.0_real64)
18+
call h%write("/group1/dataset2", 2.0_real64)
19+
20+
call h%close()
21+
22+
! Reopen the file for testing
23+
call h%open(filename, "r")
24+
25+
! visit the root group
26+
print '(a)', "test_visit: visiting root group"
27+
call h%visit("/", my_callback)
28+
29+
print '(a)', "test_visit: visiting /group1"
30+
! visit a subgroup
31+
call h%visit("/group1", my_callback)
32+
33+
call h%close()
34+
35+
print '(a,i0,a)', "test_visit: found ", i, " objects"
36+
if (i /= 8) error stop "test_visit: expected 8 objects"
37+
38+
contains
39+
40+
! Define a callback subroutine
41+
subroutine my_callback(group_name, object_name, object_type)
42+
character(*), intent(in) :: group_name, object_name, object_type
43+
print '(6a)', "test_visit: at group ", trim(group_name), ' we found ', trim(object_name), ' that is a ', trim(object_type)
44+
i = i + 1
45+
end subroutine my_callback
46+
47+
end program test_visit

0 commit comments

Comments
 (0)