@@ -62,5 +62,70 @@ def [](attr_name)
6262 ensure
6363 attr . close if attr
6464 end
65+
66+ def []=( attr_name , value )
67+ write ( attr_name , value )
68+ end
69+
70+ def write ( attr_name , value )
71+ values = normalize_data ( value )
72+ datatype_id = datatype_id_for ( values )
73+
74+ dims = ::FFI ::MemoryPointer . new ( :ulong_long , 1 )
75+ dims . write_array_of_ulong_long ( [ values . length ] )
76+ dataspace_id = HDF5 ::FFI . H5Screate_simple ( 1 , dims , nil )
77+ raise HDF5 ::Error , 'Failed to create attribute dataspace' if dataspace_id < 0
78+
79+ attr_id = HDF5 ::FFI . H5Acreate2 (
80+ @dataset_id ,
81+ attr_name ,
82+ datatype_id ,
83+ dataspace_id ,
84+ HDF5 ::DEFAULT_PROPERTY_LIST ,
85+ HDF5 ::DEFAULT_PROPERTY_LIST
86+ )
87+ raise HDF5 ::Error , "Failed to create attribute: #{ attr_name } " if attr_id < 0
88+
89+ buffer = buffer_for ( values )
90+ status = HDF5 ::FFI . H5Awrite ( attr_id , datatype_id , buffer )
91+ raise HDF5 ::Error , "Failed to write attribute: #{ attr_name } " if status < 0
92+
93+ value
94+ ensure
95+ HDF5 ::FFI . H5Aclose ( attr_id ) if attr_id && attr_id >= 0
96+ HDF5 ::FFI . H5Sclose ( dataspace_id ) if dataspace_id && dataspace_id >= 0
97+ end
98+
99+ private
100+
101+ def normalize_data ( value )
102+ values = value . is_a? ( Array ) ? value : [ value ]
103+ raise HDF5 ::Error , 'Attribute data must not be empty' if values . empty?
104+ raise HDF5 ::Error , 'Nested arrays are not supported for attributes' if values . any? { |item | item . is_a? ( Array ) }
105+
106+ values
107+ end
108+
109+ def datatype_id_for ( values )
110+ if values . all? { |item | item . is_a? ( Integer ) }
111+ HDF5 ::FFI . H5T_NATIVE_INT
112+ elsif values . all? { |item | item . is_a? ( Numeric ) }
113+ HDF5 ::FFI . H5T_NATIVE_DOUBLE
114+ else
115+ raise HDF5 ::Error , 'Only numeric attribute data is supported'
116+ end
117+ end
118+
119+ def buffer_for ( values )
120+ if values . all? { |item | item . is_a? ( Integer ) }
121+ buffer = ::FFI ::MemoryPointer . new ( :int , values . length )
122+ buffer . write_array_of_int ( values )
123+ else
124+ buffer = ::FFI ::MemoryPointer . new ( :double , values . length )
125+ buffer . write_array_of_double ( values . map ( &:to_f ) )
126+ end
127+
128+ buffer
129+ end
65130 end
66131end
0 commit comments