Class: HDF5::AttributeManager

Inherits:
Object
  • Object
show all
Defined in:
lib/hdf5/attribute.rb

Instance Method Summary collapse

Constructor Details

#initialize(dataset_id, context = nil) ⇒ AttributeManager



83
84
85
86
# File 'lib/hdf5/attribute.rb', line 83

def initialize(dataset_id, context = nil)
  @dataset_id = dataset_id
  @context = context
end

Instance Method Details

#[](attr_name) ⇒ Object



88
89
90
91
92
93
94
# File 'lib/hdf5/attribute.rb', line 88

def [](attr_name)
  @context&.ensure_open!(@dataset_id)
  attr = Attribute.new(@dataset_id, attr_name, @context)
  attr.read
ensure
  attr.close if attr
end

#[]=(attr_name, value) ⇒ Object



96
97
98
# File 'lib/hdf5/attribute.rb', line 96

def []=(attr_name, value)
  write(attr_name, value)
end

#create(attr_name, value) ⇒ Object

Raises:



131
132
133
134
135
# File 'lib/hdf5/attribute.rb', line 131

def create(attr_name, value)
  raise HDF5::Error, "Attribute already exists: #{attr_name}" if key?(attr_name)

  write(attr_name, value)
end

#delete(attr_name) ⇒ Object

Raises:



123
124
125
126
127
128
129
# File 'lib/hdf5/attribute.rb', line 123

def delete(attr_name)
  @context&.ensure_open!(@dataset_id)
  status = HDF5::FFI.H5Adelete(@dataset_id, attr_name)
  raise HDF5::Error, "Failed to delete attribute: #{attr_name}" if status < 0

  self
end

#key?(attr_name) ⇒ Boolean

Raises:



115
116
117
118
119
120
121
# File 'lib/hdf5/attribute.rb', line 115

def key?(attr_name)
  @context&.ensure_open!(@dataset_id)
  exists = HDF5::FFI.H5Aexists(@dataset_id, attr_name)
  raise HDF5::Error, "Failed to check attribute existence: #{attr_name}" if exists.negative?

  exists.positive?
end

#keys ⇒ Object

Raises:



100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/hdf5/attribute.rb', line 100

def keys
  @context&.ensure_open!(@dataset_id)
  names = []
  index = ::FFI::MemoryPointer.new(:ulong_long)
  index.write_ulong_long(0)
  callback = ::FFI::Function.new(:int, %i[int64_t string pointer pointer]) do |_, name, _, _|
    names << name
    0
  end
  status = HDF5::FFI.H5Aiterate2(@dataset_id, :H5_INDEX_NAME, :H5_ITER_NATIVE, index, callback, nil)
  raise HDF5::Error, 'Failed to iterate over attributes' if status < 0

  names
end

#modify(attr_name, value) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/hdf5/attribute.rb', line 137

def modify(attr_name, value)
  @context&.ensure_open!(@dataset_id)
  raise HDF5::Error, "Attribute not found: #{attr_name}" unless key?(attr_name)

  attr_id = HDF5::FFI.H5Aopen(@dataset_id, attr_name, HDF5::DEFAULT_PROPERTY_LIST)
  raise HDF5::Error, "Failed to open attribute: #{attr_name}" if attr_id < 0

  type_id = HDF5::FFI.H5Aget_type(attr_id)
  space_id = HDF5::FFI.H5Aget_space(attr_id)
  raise HDF5::Error, "Failed to inspect attribute: #{attr_name}" if type_id < 0 || space_id < 0

  if HDF5::FFI.H5Tget_class(type_id) == :H5T_STRING
    unless HDF5::StringCodec.variable?(type_id)
      raise UnsupportedTypeError, 'Fixed-length string attributes are not yet supported'
    end

    string_values, string_shape = HDF5::StringCodec.normalize_data(value)
    unless string_shape == attribute_shape(space_id)
      raise HDF5::ShapeError,
            'Attribute shape must not change when modifying'
    end

    buffer, _string_pointers = HDF5::StringCodec.buffer_for_values(string_values)
    status = HDF5::FFI.H5Awrite(attr_id, type_id, buffer)
  else
    dtype_object = DType.for_hdf5(type_id)
    values = HDF5::DataHelpers.normalize_data(value, label: 'Attribute data')
    expected_shape = attribute_shape(space_id)
    raise HDF5::Error, 'Attribute shape must not change when modifying' unless values.shape == expected_shape

    converted = dtype_object.numo_class.cast(values)
    status = HDF5::FFI.H5Awrite(attr_id, dtype_object.memory_type_id, HDF5::DataHelpers.buffer_for(converted))
  end
  raise HDF5::Error, "Failed to modify attribute: #{attr_name}" if status < 0

  value
ensure
  HDF5::FFI.H5Sclose(space_id) if space_id && space_id >= 0
  HDF5::FFI.H5Tclose(type_id) if type_id && type_id >= 0
  HDF5::FFI.H5Aclose(attr_id) if attr_id && attr_id >= 0
end

#write(attr_name, value) ⇒ Object



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/hdf5/attribute.rb', line 179

def write(attr_name, value)
  @context&.ensure_open!(@dataset_id)
  string_data = HDF5::StringCodec.string_data?(value)
  string_values, string_shape = HDF5::StringCodec.normalize_data(value) if string_data
  values = HDF5::DataHelpers.normalize_data(value, label: 'Attribute data') unless string_data
  dtype_object = DType.for_numo(values) unless string_data
  type_id = string_data ? HDF5::StringCodec.datatype_id : dtype_object.storage_type_id

  exists = HDF5::FFI.H5Aexists(@dataset_id, attr_name)
  raise HDF5::Error, "Failed to check attribute existence: #{attr_name}" if exists.negative?

  if exists.positive?
    status = HDF5::FFI.H5Adelete(@dataset_id, attr_name)
    raise HDF5::Error, "Failed to replace attribute: #{attr_name}" if status < 0
  end

  dataspace_id = create_dataspace(string_data ? string_shape : values.shape)
  raise HDF5::Error, 'Failed to create attribute dataspace' if dataspace_id < 0

  attr_id = HDF5::FFI.H5Acreate2(
    @dataset_id,
    attr_name,
    type_id,
    dataspace_id,
    HDF5::DEFAULT_PROPERTY_LIST,
    HDF5::DEFAULT_PROPERTY_LIST
  )
  raise HDF5::Error, "Failed to create attribute: #{attr_name}" if attr_id < 0

  buffer, _string_pointers = if string_data
                               HDF5::StringCodec.buffer_for_values(string_values)
                             else
                               [HDF5::DataHelpers.buffer_for(values), nil]
                             end
  memory_type_id = string_data ? type_id : dtype_object.memory_type_id
  status = HDF5::FFI.H5Awrite(attr_id, memory_type_id, buffer)
  raise HDF5::Error, "Failed to write attribute: #{attr_name}" if status < 0

  value
ensure
  HDF5::FFI.H5Aclose(attr_id) if attr_id && attr_id >= 0
  HDF5::FFI.H5Sclose(dataspace_id) if dataspace_id && dataspace_id >= 0
  HDF5::FFI.H5Tclose(type_id) if string_data && type_id && type_id >= 0
end