Skip to content

Commit 457c7d3

Browse files
Writing MatlabOpaque objects to MAT-files (#208)
* Rearrange methods for readability * Add some subsys methods to save objects * Full subsystem code, some updates to writers to support writing subsystem * Some fixes to writing filewrapper metadata * Fix dimensions of data in filewrapper * Write nested objects in subsystem * Improve MAT_subsys readability * Fix: Write empty dicts as 0x0 structs in HDF5 * subsystem shared metadata fields use these 0x0 structs * Update empty struct handling; Add tests * Add test for empty struct 1x1
1 parent 34bee49 commit 457c7d3

File tree

5 files changed

+681
-129
lines changed

5 files changed

+681
-129
lines changed

src/MAT.jl

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -204,31 +204,43 @@ Write a dictionary containing variable names as keys and values as values
204204
to a Matlab file, opening and closing it automatically.
205205
"""
206206
function matwrite(filename::AbstractString, dict::AbstractDict{S, T}; compress::Bool = false, version::String ="v7.3") where {S, T}
207-
if version == "v4"
208-
file = open(filename, "w")
209-
m = MAT_v4.Matlabv4File(file, false)
210-
_write_dict(m, dict)
211-
elseif version == "v7.3"
212-
file = matopen(filename, "w"; compress = compress)
213-
_write_dict(file, dict)
214-
else
215-
error("writing for \"$(version)\" is not supported")
207+
file = nothing
208+
try
209+
if version == "v4"
210+
file = open(filename, "w")
211+
file = MAT_v4.Matlabv4File(file, false)
212+
_write_dict(file, dict)
213+
elseif version == "v7.3"
214+
file = matopen(filename, "w"; compress = compress)
215+
_write_dict(file, dict)
216+
else
217+
error("writing for \"$(version)\" is not supported")
218+
end
219+
finally
220+
if file !== nothing
221+
close(file)
222+
end
216223
end
217224
end
218225

219226
function _write_dict(fileio, dict::AbstractDict)
220-
try
221-
for (k, v) in dict
222-
local kstring
223-
try
224-
kstring = ascii(convert(String, k))
225-
catch x
226-
error("matwrite requires a Dict with ASCII keys")
227-
end
228-
write(fileio, kstring, v)
227+
228+
for (k, v) in dict
229+
local kstring
230+
try
231+
kstring = ascii(convert(String, k))
232+
catch x
233+
error("matwrite requires a Dict with ASCII keys")
234+
end
235+
write(fileio, kstring, v)
236+
end
237+
238+
if hasproperty(fileio, :subsystem) && fileio.subsystem !== nothing
239+
# will always be nothing for MATv4 so we can ignore that case
240+
subsys_data = MAT_subsys.set_subsystem_data!(fileio.subsystem)
241+
if subsys_data !== nothing
242+
MAT_HDF5.write_subsys(fileio, subsys_data)
229243
end
230-
finally
231-
close(fileio)
232244
end
233245
end
234246

src/MAT_HDF5.jl

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import HDF5: Reference
3636
import Dates
3737
import Tables
3838
import PooledArrays: PooledArray
39-
import ..MAT_types: MatlabStructArray, StructArrayField, convert_struct_array, MatlabClassObject, MatlabOpaque, MatlabTable
39+
import ..MAT_types: MatlabStructArray, StructArrayField, convert_struct_array, MatlabClassObject, MatlabOpaque, MatlabTable, EmptyStruct
4040

4141
const HDF5Parent = Union{HDF5.File, HDF5.Group}
4242
const HDF5BitsOrBool = Union{HDF5.BitsType,Bool}
@@ -130,10 +130,12 @@ function matopen(filename::AbstractString, rd::Bool, wr::Bool, cr::Bool, tr::Boo
130130
close(g)
131131
end
132132
subsys_refs = "#subsystem#"
133-
if haskey(fid.plain, subsys_refs)
133+
if rd && haskey(fid.plain, subsys_refs)
134134
fid.subsystem.table_type = table
135135
subsys_data = m_read(fid.plain[subsys_refs], fid.subsystem)
136136
MAT_subsys.load_subsys!(fid.subsystem, subsys_data, endian_indicator)
137+
elseif wr
138+
MAT_subsys.init_save!(fid.subsystem)
137139
end
138140
fid
139141
end
@@ -464,7 +466,7 @@ function _normalize_arr(x)
464466
end
465467

466468
# Write a scalar or array
467-
function m_write(mfile::MatlabHDF5File, parent::HDF5Parent, name::String, data::Union{T, Complex{T}, AbstractArray{T}, AbstractArray{Complex{T}}}) where {T<:HDF5BitsOrBool}
469+
function m_write(mfile::MatlabHDF5File, parent::HDF5Parent, name::String, data::Union{T, Complex{T}, AbstractArray{T}, AbstractArray{Complex{T}}}, ) where {T<:HDF5BitsOrBool}
468470
data = _normalize_arr(data)
469471
if isempty(data)
470472
m_writeempty(parent, name, data)
@@ -541,14 +543,19 @@ function m_write(mfile::MatlabHDF5File, parent::HDF5Parent, name::String, c::Abs
541543
end
542544

543545
# Write cell arrays
544-
function m_write(mfile::MatlabHDF5File, parent::HDF5Parent, name::String, data::AbstractArray{T}) where T
546+
function m_write(mfile::MatlabHDF5File, parent::HDF5Parent, name::String, data::AbstractArray{T}, object_decode::UInt32=UInt32(0)) where T
545547
data = _normalize_arr(data)
546548
refs = _write_references!(mfile, parent, data)
547549
# Write the references as the chosen variable
548550
cset, ctype = create_dataset(parent, name, refs)
549551
try
550552
write_dataset(cset, ctype, refs)
551-
write_attribute(cset, name_type_attr_matlab, "cell")
553+
if object_decode == UInt32(3)
554+
write_attribute(cset, object_decode_attr_matlab, object_decode)
555+
write_attribute(cset, name_type_attr_matlab, "FileWrapper__")
556+
else
557+
write_attribute(cset, name_type_attr_matlab, "cell")
558+
end
552559
finally
553560
close(ctype)
554561
close(cset)
@@ -680,8 +687,34 @@ function m_write(mfile::MatlabHDF5File, parent::HDF5Parent, name::String, obj::M
680687
end
681688
end
682689

690+
# Write empty (zero-dimensional) structs with no fields
691+
function m_write(mfile::MatlabHDF5File, parent::HDF5Parent, name::String, s::EmptyStruct)
692+
dset, dtype = create_dataset(parent, name, s.dims)
693+
try
694+
write_attribute(dset, empty_attr_matlab, 0x01)
695+
write_attribute(dset, name_type_attr_matlab, "struct")
696+
write_dataset(dset, dtype, s.dims)
697+
finally
698+
close(dtype); close(dset)
699+
end
700+
end
701+
683702
# Write a struct from arrays of keys and values
684703
function m_write(mfile::MatlabHDF5File, parent::HDF5Parent, name::String, k::Vector{String}, v::Vector)
704+
if length(k) == 0
705+
# empty struct
706+
adata = UInt64[1, 1]
707+
dset, dtype = create_dataset(parent, name, adata)
708+
try
709+
write_attribute(dset, empty_attr_matlab, 0x01)
710+
write_attribute(dset, name_type_attr_matlab, "struct")
711+
write_dataset(dset, dtype, adata)
712+
finally
713+
close(dtype); close(dset)
714+
end
715+
return
716+
end
717+
685718
g = create_group(parent, name)
686719
try
687720
write_attribute(g, name_type_attr_matlab, "struct")
@@ -719,11 +752,35 @@ function m_write(mfile::MatlabHDF5File, parent::HDF5Parent, name::String, dat::D
719752
end
720753

721754
function m_write(mfile::MatlabHDF5File, parent::HDF5Parent, name::String, obj::MatlabOpaque)
722-
error("writing of MatlabOpaque types is not yet supported")
755+
if obj.class == "FileWrapper__"
756+
m_write(mfile, parent, name, obj["__filewrapper__"], UInt32(3))
757+
return
758+
end
759+
760+
metadata = MAT_subsys.set_mcos_object_metadata(mfile.subsystem, obj)
761+
dset, dtype = create_dataset(parent, name, metadata)
762+
try
763+
write_dataset(dset, dtype, metadata)
764+
write_attribute(dset, name_type_attr_matlab, obj.class)
765+
write_attribute(dset, object_type_attr_matlab, UInt32(3))
766+
finally
767+
close(dset)
768+
close(dtype)
769+
end
723770
end
724771

725772
function m_write(mfile::MatlabHDF5File, parent::HDF5Parent, name::String, obj::AbstractArray{MatlabOpaque})
726-
error("writing of MatlabOpaque types is not yet supported")
773+
metadata = MAT_subsys.set_mcos_object_metadata(mfile.subsystem, obj)
774+
dset, dtype = create_dataset(parent, name, metadata)
775+
try
776+
# TODO: Handle empty array case
777+
write_dataset(dset, dtype, metadata)
778+
write_attribute(dset, name_type_attr_matlab, first(obj).class)
779+
write_attribute(dset, object_type_attr_matlab, UInt32(3))
780+
finally
781+
close(dset)
782+
close(dtype)
783+
end
727784
end
728785

729786
function m_write(mfile::MatlabHDF5File, parent::HDF5Parent, name::String, arr::PooledArray)
@@ -743,6 +800,11 @@ function write(parent::MatlabHDF5File, name::String, thing)
743800
m_write(parent, parent.plain, name, thing)
744801
end
745802

803+
function write_subsys(mfile::MatlabHDF5File, subsys_data::Dict{String,Any})
804+
name = "#subsystem#"
805+
m_write(mfile, mfile.plain, name, subsys_data)
806+
end
807+
746808
## Type conversion operations ##
747809

748810
struct MatlabString end

0 commit comments

Comments
 (0)