Skip to content

Commit bea378a

Browse files
committed
Add support for loading mxOPAQUE_CLASS types
* MAT_subsys.jl: New file MAT_subsys with methods to set, parse and retrieve subsystem data * MAT_v5.jl: New method "read_opaque" to handle mxOPAQUE_CLASS * MAT_v5.jl: New method "read_subsystem" to handle subsystem data * MAT.jl (matread): Update to clear subsystem and object cache after load Support for loading mxOPAQUE_CLASS objects in v7.3 HDF5 format * MAT_HDF5.jl (matopen): New argument Endian indicator, Reads and parses subsystem on load * MAT_HDF5.jl (close): Update to write endian header based on system endianness * MAT_HDF5.jl (m_read::HDF5.Dataset): Update to handle MATLAB_object_decode (mxOPAQUE_CLASS) types * MAT_HDF5.jl (m_read::HDF5.Group): Update to read subsystem data and function_handles * MAT.jl (matopen): Update function calls Updated test for struct_table_datetime.mat to ensure accurate deserialization (including nested properties) in both v7 and v7.3 formats * test/read.jl: Update tests for "function_handles.mat" and "struct_table_datetime.mat"
1 parent 2df47dd commit bea378a

File tree

6 files changed

+449
-45
lines changed

6 files changed

+449
-45
lines changed

src/MAT.jl

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@ module MAT
2626

2727
using HDF5, SparseArrays
2828

29+
include("MAT_subsys.jl")
2930
include("MAT_HDF5.jl")
3031
include("MAT_v5.jl")
3132
include("MAT_v4.jl")
3233

33-
using .MAT_HDF5, .MAT_v5, .MAT_v4
34+
using .MAT_HDF5, .MAT_v5, .MAT_v4, .MAT_subsys
3435

3536
export matopen, matread, matwrite, @read, @write
3637

@@ -40,7 +41,7 @@ function matopen(filename::AbstractString, rd::Bool, wr::Bool, cr::Bool, tr::Boo
4041
# When creating new files, create as HDF5 by default
4142
fs = filesize(filename)
4243
if cr && (tr || fs == 0)
43-
return MAT_HDF5.matopen(filename, rd, wr, cr, tr, ff, compress)
44+
return MAT_HDF5.matopen(filename, rd, wr, cr, tr, ff, compress, Base.ENDIAN_BOM == 0x04030201)
4445
elseif fs == 0
4546
error("File \"$filename\" does not exist and create was not specified")
4647
end
@@ -76,7 +77,7 @@ function matopen(filename::AbstractString, rd::Bool, wr::Bool, cr::Bool, tr::Boo
7677
seek(rawfid, offset)
7778
if read!(rawfid, Vector{UInt8}(undef, 8)) == HDF5_HEADER
7879
close(rawfid)
79-
return MAT_HDF5.matopen(filename, rd, wr, cr, tr, ff, compress)
80+
return MAT_HDF5.matopen(filename, rd, wr, cr, tr, ff, compress, endian_indicator == 0x494D)
8081
end
8182
end
8283

@@ -133,6 +134,7 @@ function matread(filename::AbstractString)
133134
try
134135
vars = read(file)
135136
finally
137+
MAT_subsys.clear_subsys!()
136138
close(file)
137139
end
138140
vars
@@ -165,7 +167,7 @@ function matwrite(filename::AbstractString, dict::AbstractDict{S, T}; compress::
165167
end
166168

167169
else
168-
170+
169171
file = matopen(filename, "w"; compress = compress)
170172
try
171173
for (k, v) in dict

src/MAT_HDF5.jl

Lines changed: 64 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
module MAT_HDF5
3030

3131
using HDF5, SparseArrays
32+
using ..MAT_subsys
3233

3334
import Base: names, read, write, close
3435
import HDF5: Reference
@@ -69,8 +70,13 @@ function close(f::MatlabHDF5File)
6970
unsafe_copyto!(magicptr, idptr, length(identifier))
7071
end
7172
magic[126] = 0x02
72-
magic[127] = 0x49
73-
magic[128] = 0x4d
73+
if Base.ENDIAN_BOM == 0x04030201
74+
magic[127] = 0x49
75+
magic[128] = 0x4d
76+
else
77+
magic[127] = 0x4d
78+
magic[128] = 0x49
79+
end
7480
rawfid = open(f.plain.filename, "r+")
7581
write(rawfid, magic)
7682
close(rawfid)
@@ -80,7 +86,7 @@ function close(f::MatlabHDF5File)
8086
nothing
8187
end
8288

83-
function matopen(filename::AbstractString, rd::Bool, wr::Bool, cr::Bool, tr::Bool, ff::Bool, compress::Bool)
89+
function matopen(filename::AbstractString, rd::Bool, wr::Bool, cr::Bool, tr::Bool, ff::Bool, compress::Bool, endian_indicator::Bool)
8490
local f
8591
if ff && !wr
8692
error("Cannot append to a read-only file")
@@ -109,6 +115,11 @@ function matopen(filename::AbstractString, rd::Bool, wr::Bool, cr::Bool, tr::Boo
109115
fid.refcounter = length(g)-1
110116
close(g)
111117
end
118+
subsys_refs = "#subsystem#"
119+
if haskey(fid.plain, subsys_refs)
120+
subsys_data = m_read(fid.plain[subsys_refs])
121+
MAT_subsys.load_subsys!(subsys_data, endian_indicator)
122+
end
112123
fid
113124
end
114125

@@ -118,6 +129,7 @@ const name_type_attr_matlab = "MATLAB_class"
118129
const empty_attr_matlab = "MATLAB_empty"
119130
const sparse_attr_matlab = "MATLAB_sparse"
120131
const int_decode_attr_matlab = "MATLAB_int_decode"
132+
const object_type_attr_matlab = "MATLAB_object_decode"
121133

122134
### Reading
123135
function read_complex(dtype::HDF5.Datatype, dset::HDF5.Dataset, ::Type{T}) where T
@@ -128,6 +140,21 @@ function read_complex(dtype::HDF5.Datatype, dset::HDF5.Dataset, ::Type{T}) where
128140
return read(dset, Complex{T})
129141
end
130142

143+
function read_cell(dset::HDF5.Dataset)
144+
refs = read(dset, Reference)
145+
out = Array{Any}(undef, size(refs))
146+
f = HDF5.file(dset)
147+
for i = 1:length(refs)
148+
dset = f[refs[i]]
149+
try
150+
out[i] = m_read(dset)
151+
finally
152+
close(dset)
153+
end
154+
end
155+
return out
156+
end
157+
131158
function m_read(dset::HDF5.Dataset)
132159
if haskey(dset, empty_attr_matlab)
133160
# Empty arrays encode the dimensions as the dataset
@@ -150,36 +177,46 @@ function m_read(dset::HDF5.Dataset)
150177
end
151178

152179
mattype = haskey(dset, name_type_attr_matlab) ? read_attribute(dset, name_type_attr_matlab) : "cell"
180+
objecttype = haskey(dset, object_type_attr_matlab) ? read_attribute(dset, object_type_attr_matlab) : nothing
153181

154-
if mattype == "cell"
182+
if mattype == "cell" && objecttype === nothing
155183
# Cell arrays, represented as an array of refs
156-
refs = read(dset, Reference)
157-
out = Array{Any}(undef, size(refs))
158-
f = HDF5.file(dset)
159-
for i = 1:length(refs)
160-
dset = f[refs[i]]
161-
try
162-
out[i] = m_read(dset)
163-
finally
164-
close(dset)
165-
end
184+
return read_cell(dset)
185+
elseif objecttype !== nothing
186+
if objecttype != 3
187+
@warn "MATLAB Object Type $mattype is currently not supported."
188+
return missing
189+
end
190+
if mattype == "FileWrapper__"
191+
return read_cell(dset)
192+
end
193+
if haskey(dset, "MATLAB_fields")
194+
@warn "Enumeration Instances are not supported currently."
195+
return missing
166196
end
167-
return out
168197
elseif !haskey(str2type_matlab,mattype)
169-
@warn "MATLAB $mattype values are currently not supported"
198+
@warn "MATLAB $mattype values are currently not supported."
170199
return missing
171200
end
172201

173202
# Regular arrays of values
174203
# Convert to Julia type
175-
T = str2type_matlab[mattype]
204+
if objecttype === nothing
205+
T = str2type_matlab[mattype]
206+
else
207+
T = UInt32 # FIXME: Default for MATLAB objects?
208+
end
176209

177210
# Check for a COMPOUND data set, and if so handle complex numbers specially
178211
dtype = datatype(dset)
179212
try
180213
class_id = HDF5.API.h5t_get_class(dtype.id)
181214
d = class_id == HDF5.API.H5T_COMPOUND ? read_complex(dtype, dset, T) : read(dset, T)
182-
length(d) == 1 ? d[1] : d
215+
if objecttype !== nothing
216+
return MAT_subsys.load_mcos_object(d, "MCOS")
217+
else
218+
return length(d) == 1 ? d[1] : d
219+
end
183220
finally
184221
close(dtype)
185222
end
@@ -194,7 +231,11 @@ end
194231

195232
# reading a struct, struct array, or sparse matrix
196233
function m_read(g::HDF5.Group)
197-
mattype = read_attribute(g, name_type_attr_matlab)
234+
if HDF5.name(g) == "/#subsystem#"
235+
mattype = "#subsystem#"
236+
else
237+
mattype = read_attribute(g, name_type_attr_matlab)
238+
end
198239
if mattype != "struct"
199240
# Check if this is a sparse matrix.
200241
fn = keys(g)
@@ -226,10 +267,11 @@ function m_read(g::HDF5.Group)
226267
end
227268
return SparseMatrixCSC(convert(Int, read_attribute(g, sparse_attr_matlab)), length(jc)-1, jc, ir, data)
228269
elseif mattype == "function_handle"
229-
@warn "MATLAB $mattype values are currently not supported"
230-
return missing
270+
# Fall through
231271
else
232-
@warn "Unknown non-struct group of type $mattype detected; attempting to read as struct"
272+
if mattype != "#subsystem#"
273+
@warn "Unknown non-struct group of type $mattype detected; attempting to read as struct"
274+
end
233275
end
234276
end
235277
if haskey(g, "MATLAB_fields")

0 commit comments

Comments
 (0)