Skip to content

Commit f203bb1

Browse files
committed
Refactor HDF5 attribute handling and improve error reporting
1 parent 2aa6d81 commit f203bb1

3 files changed

Lines changed: 69 additions & 13 deletions

File tree

lib/hdf5/attribute.rb

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,45 +3,51 @@ class Attribute
33
def initialize(dataset_id, attr_name)
44
@dataset_id = dataset_id
55
@attr_name = attr_name
6-
@attr_id = FFI.H5Aopen(@dataset_id, @attr_name, 0)
7-
raise 'Failed to open attribute' if @attr_id < 0
6+
@attr_id = HDF5::FFI.H5Aopen(@dataset_id, @attr_name, HDF5::DEFAULT_PROPERTY_LIST)
7+
raise HDF5::Error, 'Failed to open attribute' if @attr_id < 0
88
end
99

1010
def read
11-
type_id = FFI.H5Aget_type(@attr_id)
12-
space_id = FFI.H5Aget_space(@attr_id)
11+
type_id = HDF5::FFI.H5Aget_type(@attr_id)
12+
space_id = HDF5::FFI.H5Aget_space(@attr_id)
1313

14-
size = FFI.H5Sget_simple_extent_npoints(space_id)
14+
size = HDF5::FFI.H5Sget_simple_extent_npoints(space_id)
1515

1616
buffer = \
17-
case FFI.H5Tget_class(type_id)
17+
case HDF5::FFI.H5Tget_class(type_id)
1818
when :H5T_INTEGER
1919
::FFI::MemoryPointer.new(:int, size)
2020
when :H5T_FLOAT
2121
::FFI::MemoryPointer.new(:double, size)
2222
when :H5T_STRING
2323
::FFI::MemoryPointer.new(:pointer, size)
2424
else
25-
raise 'Unsupported data type'
25+
raise HDF5::Error, 'Unsupported data type'
2626
end
2727

28-
status = FFI.H5Aread(@attr_id, type_id, buffer)
29-
raise 'Failed to read attribute' if status < 0
28+
status = HDF5::FFI.H5Aread(@attr_id, type_id, buffer)
29+
raise HDF5::Error, 'Failed to read attribute' if status < 0
3030

31-
case FFI.H5Tget_class(type_id)
31+
case HDF5::FFI.H5Tget_class(type_id)
3232
when :H5T_INTEGER
3333
buffer.read_array_of_int(size)
3434
when :H5T_FLOAT
3535
buffer.read_array_of_double(size)
3636
when :H5T_STRING
3737
buffer.read_pointer.read_string
3838
else
39-
raise 'Unsupported data type'
39+
raise HDF5::Error, 'Unsupported data type'
4040
end
41+
ensure
42+
HDF5::FFI.H5Tclose(type_id) if type_id && type_id >= 0
43+
HDF5::FFI.H5Sclose(space_id) if space_id && space_id >= 0
4144
end
4245

4346
def close
44-
FFI.H5Aclose(@attr_id)
47+
return if @attr_id.nil?
48+
49+
HDF5::FFI.H5Aclose(@attr_id)
50+
@attr_id = nil
4551
end
4652
end
4753

lib/hdf5/group.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def list_datasets
6666
else
6767
HDF5::FFI.H5Literate2(@group_id, :H5_INDEX_NAME, :H5_ITER_NATIVE, nil, callback, nil)
6868
end).negative? &&
69-
raise('Failed to list datasets')
69+
raise(HDF5::Error, 'Failed to list datasets')
7070

7171
datasets
7272
end

test/hdf5_test.rb

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,56 @@ class HDF5Test < Test::Unit::TestCase
9090
end
9191
end
9292

93+
test 'dataset attrs reads integer attribute' do
94+
Dir.mktmpdir do |dir|
95+
path = File.join(dir, 'attribute.h5')
96+
97+
HDF5::File.create(path) do |file|
98+
dataset = file.create_dataset('values', [1, 2, 3])
99+
dataset_id = dataset.instance_variable_get(:@dataset_id)
100+
101+
dims = FFI::MemoryPointer.new(:ulong_long, 1)
102+
dims.write_array_of_ulong_long([1])
103+
attr_space_id = HDF5::FFI.H5Screate_simple(1, dims, nil)
104+
attr_id = HDF5::FFI.H5Acreate2(
105+
dataset_id,
106+
'scale',
107+
HDF5::FFI.H5T_NATIVE_INT,
108+
attr_space_id,
109+
HDF5::DEFAULT_PROPERTY_LIST,
110+
HDF5::DEFAULT_PROPERTY_LIST
111+
)
112+
value = FFI::MemoryPointer.new(:int, 1)
113+
value.write_int(42)
114+
status = HDF5::FFI.H5Awrite(attr_id, HDF5::FFI.H5T_NATIVE_INT, value)
115+
assert_equal(0, status)
116+
ensure
117+
HDF5::FFI.H5Aclose(attr_id) if attr_id && attr_id >= 0
118+
HDF5::FFI.H5Sclose(attr_space_id) if attr_space_id && attr_space_id >= 0
119+
dataset.close if dataset
120+
end
121+
122+
HDF5::File.open(path) do |file|
123+
assert_equal([42], file['values'].attrs['scale'])
124+
end
125+
end
126+
end
127+
128+
test 'dataset attrs raises HDF5 error for missing attribute' do
129+
Dir.mktmpdir do |dir|
130+
path = File.join(dir, 'missing-attribute.h5')
131+
132+
HDF5::File.create(path) do |file|
133+
dataset = file.create_dataset('values', [1, 2, 3])
134+
assert_raise(HDF5::Error) do
135+
dataset.attrs['does-not-exist']
136+
end
137+
ensure
138+
dataset.close if dataset
139+
end
140+
end
141+
end
142+
93143
test 'raises HDF5 error for missing file' do
94144
assert_raise(HDF5::Error) do
95145
HDF5::File.open('/tmp/does-not-exist-ruby-hdf5.h5')

0 commit comments

Comments
 (0)