11module HDF5
22 class Dataset
3+ H5P_DEFAULT = 0
4+
5+ class << self
6+ def create ( parent_id , name , data )
7+ values = normalize_data ( data )
8+ dims = ::FFI ::MemoryPointer . new ( :ulong_long , 1 )
9+ dims . write_array_of_ulong_long ( [ values . length ] )
10+ datatype_id = datatype_id_for ( values )
11+ dataspace_id = HDF5 ::FFI . H5Screate_simple ( 1 , dims , nil )
12+ raise "Failed to create dataspace for dataset: #{ name } " if dataspace_id < 0
13+
14+ dataset_id = HDF5 ::FFI . H5Dcreate2 ( parent_id , name , datatype_id , dataspace_id , H5P_DEFAULT , H5P_DEFAULT ,
15+ H5P_DEFAULT )
16+ dataset = from_id ( dataset_id , name )
17+ dataset . write ( values )
18+ dataset
19+ ensure
20+ HDF5 ::FFI . H5Sclose ( dataspace_id ) if dataspace_id && dataspace_id >= 0
21+ end
22+
23+ def open ( parent_id , name )
24+ from_id ( HDF5 ::FFI . H5Dopen2 ( parent_id , name , H5P_DEFAULT ) , name )
25+ end
26+
27+ def normalize_data ( data )
28+ values = data . is_a? ( Array ) ? data : [ data ]
29+ raise 'Dataset data must not be empty' if values . empty?
30+ raise 'Nested arrays are not supported' if values . any? { |value | value . is_a? ( Array ) }
31+
32+ values
33+ end
34+
35+ def datatype_id_for ( data )
36+ if data . all? { |value | value . is_a? ( Integer ) }
37+ HDF5 ::FFI . H5T_NATIVE_INT
38+ elsif data . all? { |value | value . is_a? ( Numeric ) }
39+ HDF5 ::FFI . H5T_NATIVE_DOUBLE
40+ else
41+ raise 'Only numeric dataset data is supported'
42+ end
43+ end
44+
45+ def buffer_for ( data )
46+ if data . all? { |value | value . is_a? ( Integer ) }
47+ buffer = ::FFI ::MemoryPointer . new ( :int , data . length )
48+ buffer . write_array_of_int ( data )
49+ else
50+ buffer = ::FFI ::MemoryPointer . new ( :double , data . length )
51+ buffer . write_array_of_double ( data . map ( &:to_f ) )
52+ end
53+
54+ buffer
55+ end
56+
57+ private
58+
59+ def from_id ( dataset_id , name )
60+ dataset = allocate
61+ dataset . send ( :initialize_from_id , dataset_id , name )
62+ dataset
63+ end
64+ end
65+
366 def initialize ( parent_id , name )
4- @dataset_id = HDF5 ::FFI . H5Dopen1 ( parent_id , name )
67+ initialize_from_id ( HDF5 ::FFI . H5Dopen2 ( parent_id , name , H5P_DEFAULT ) , name )
568 end
669
770 def attrs
871 @attrs ||= AttributeManager . new ( @dataset_id )
972 end
1073
1174 def write ( data )
12- HDF5 ::FFI . H5Dwrite ( @dataset_id , data )
75+ values = self . class . normalize_data ( data )
76+ buffer = self . class . buffer_for ( values )
77+ mem_type_id = self . class . datatype_id_for ( values )
78+ status = HDF5 ::FFI . H5Dwrite ( @dataset_id , mem_type_id , H5P_DEFAULT , H5P_DEFAULT , H5P_DEFAULT , buffer )
79+ raise 'Failed to write dataset' if status < 0
80+
81+ data
1382 end
1483
1584 def close
@@ -18,7 +87,11 @@ def close
1887
1988 def dtype
2089 datatype_id = HDF5 ::FFI . H5Dget_type ( @dataset_id )
90+ raise 'Failed to get datatype' if datatype_id < 0
91+
2192 HDF5 ::FFI . H5Tget_class ( datatype_id )
93+ ensure
94+ HDF5 ::FFI . H5Tclose ( datatype_id ) if datatype_id && datatype_id >= 0
2295 end
2396
2497 def shape
@@ -55,20 +128,32 @@ def read
55128
56129 def read_integer_data ( total_elements )
57130 buffer = ::FFI ::MemoryPointer . new ( :int , total_elements )
58- mem_type_id = HDF5 ::FFI . H5T_NATIVE_INT
59- HDF5 ::FFI . H5Dread ( @dataset_id , mem_type_id , 0 , 0 , 0 , buffer )
131+ status = HDF5 ::FFI . H5Dread ( @dataset_id , HDF5 ::FFI . H5T_NATIVE_INT , H5P_DEFAULT , H5P_DEFAULT , H5P_DEFAULT , buffer )
132+ raise 'Failed to read integer dataset' if status < 0
133+
60134 buffer . read_array_of_int ( total_elements )
61135 end
62136
63137 def read_float_data ( total_elements )
64- buffer = ::FFI ::MemoryPointer . new ( :float , total_elements )
65- mem_type_id = HDF5 ::FFI . H5T_NATIVE_FLOAT
66- HDF5 ::FFI . H5Dread ( @dataset_id , mem_type_id , 0 , 0 , 0 , buffer )
67- buffer . read_array_of_float ( total_elements )
138+ buffer = ::FFI ::MemoryPointer . new ( :double , total_elements )
139+ status = HDF5 ::FFI . H5Dread ( @dataset_id , HDF5 ::FFI . H5T_NATIVE_DOUBLE , H5P_DEFAULT , H5P_DEFAULT , H5P_DEFAULT ,
140+ buffer )
141+ raise 'Failed to read float dataset' if status < 0
142+
143+ buffer . read_array_of_double ( total_elements )
68144 end
69145
70146 def read_string_data ( total_elements )
71147 raise NotImplementedError
72148 end
149+
150+ private
151+
152+ def initialize_from_id ( dataset_id , name )
153+ raise "Failed to open dataset: #{ name } " if dataset_id < 0
154+
155+ @dataset_id = dataset_id
156+ @name = name
157+ end
73158 end
74159end
0 commit comments