Skip to content

Commit

Permalink
Merge pull request #220 from timholy/sjk/219
Browse files Browse the repository at this point in the history
Allow writing tuples of tuples and fix #219
  • Loading branch information
simonster committed Mar 1, 2015
2 parents 0255208 + d084f1c commit f011e58
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 20 deletions.
62 changes: 42 additions & 20 deletions src/jld_types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -294,11 +294,13 @@ else
end

function h5type(parent::JldFile, T::(Type...), commit::Bool)
!isa(T, (DataType...)) && unknown_type_err(T)
T = T::(DataType...)
!isa(T, (Union(Tuple, DataType)...)) && unknown_type_err(T)
T = T::(Union(Tuple, DataType)...)

haskey(parent.jlh5type, T) && return parent.jlh5type[T]
isleaftype(T) || error("unexpected non-leaf type $T")
# Tuples should always be concretely typed, unless we're
# reconstructing a tuple, in which case commit will be false
!commit || isleaftype(T) || error("unexpected non-leaf type $T")

typeinfo = JldTypeInfo(parent, T, commit)
if isopaque(T)
Expand Down Expand Up @@ -516,7 +518,7 @@ end
## Common functions for all non-special types (including gen_h5convert)

# Whether this datatype should be stored as opaque
isopaque(t::(DataType...)) = isa(t, ())
isopaque(t::(Type...)) = isa(t, ())
isopaque(t::DataType) = isempty(t.names)

# The size of this datatype in the HDF5 file (if opaque)
Expand Down Expand Up @@ -623,7 +625,7 @@ function jldatatype(parent::JldFile, dtype::HDF5Datatype)
T = julia_type(typename)
if T == UnsupportedType
warn("type $typename not present in workspace; reconstructing")
T = reconstruct_type(parent, dtype, gensym(typename))
T = reconstruct_type(parent, dtype, typename)
end

if !(T in BUILTIN_TYPES)
Expand Down Expand Up @@ -659,7 +661,8 @@ end

# Create a Julia type based on the HDF5Datatype from the file. Used
# when the type is no longer available.
function reconstruct_type(parent::JldFile, dtype::HDF5Datatype, name::Symbol)
function reconstruct_type(parent::JldFile, dtype::HDF5Datatype, savedname::String)
name = gensym(savedname)
class_id = HDF5.h5t_get_class(dtype.id)
if class_id == HDF5.H5T_OPAQUE
if exists(dtype, "empty")
Expand All @@ -669,26 +672,45 @@ function reconstruct_type(parent::JldFile, dtype::HDF5Datatype, name::Symbol)
@eval (bitstype $sz $name; $name)
end
else
fields = Expr(:block)
for i = 0:HDF5.h5t_get_nmembers(dtype.id)-1
member_name = HDF5.h5t_get_member_name(dtype.id, i)
idx = rsearchindex(member_name, "_")
field_name = symbol(member_name[1:idx-1])
if idx != sizeof(member_name)
member_dtype = HDF5.t_open(parent.plain, string(pathtypes, '/', lpad(member_name[idx+1:end], 8, '0')))
push!(fields.args, :($(field_name)::$(jldatatype(parent, member_dtype))))
# Figure out field names and types
nfields = HDF5.h5t_get_nmembers(dtype.id)
fieldnames = Array(Symbol, nfields)
fieldtypes = Array(Type, nfields)
for i = 1:nfields
membername = HDF5.h5t_get_member_name(dtype.id, i-1)
idx = rsearchindex(membername, "_")
fieldname = fieldnames[i] = symbol(membername[1:idx-1])

if idx != sizeof(membername)
# There is something past the underscore in the HDF5 field
# name, so the type is stored in file
memberdtype = HDF5.t_open(parent.plain, string(pathtypes, '/', lpad(membername[idx+1:end], 8, '0')))
fieldtypes[i] = jldatatype(parent, memberdtype)
else
member_class = HDF5.h5t_get_member_class(dtype.id, i)
if member_class == HDF5.H5T_REFERENCE
push!(fields.args, field_name)
memberclass = HDF5.h5t_get_member_class(dtype.id, i-1)
if memberclass == HDF5.H5T_REFERENCE
# Field is a reference, so use Any
fieldtypes[i] = Any
else
member_dtype = HDF5Datatype(HDF5.h5t_get_member_type(dtype.id, i), parent.plain)
push!(fields.args, :($(field_name)::$(jldatatype(parent, member_dtype))))
# Type is built-in
memberdtype = HDF5Datatype(HDF5.h5t_get_member_type(dtype.id, i-1), parent.plain)
fieldtypes[i] = jldatatype(parent, memberdtype)
end
end
end

@eval (immutable $name; $fields; end; $name)
if startswith(savedname, "(")
# We're reconstructing a tuple
tuple(fieldtypes...)
else
# We're reconstructing some other type
@eval begin
immutable $name
$([:($(fieldnames[i])::$(fieldtypes[i])) for i = 1:nfields]...)
end
$name
end
end
end
end

Expand Down
22 changes: 22 additions & 0 deletions test/jld.jl
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ bitsparamint = BitsParams{1}()
bitsparamuint = BitsParams{0x01}()
bitsparamint16 = BitsParams{int16(1)}()

# Tuple of tuples
tuple_of_tuples = (1, 2, (3, 4, [5, 6]), [7, 8])

iseq(x,y) = isequal(x,y)
iseq(x::MyStruct, y::MyStruct) = (x.len == y.len && x.data == y.data)
Expand Down Expand Up @@ -382,6 +384,7 @@ for compress in (true,false)
@write fid bitsparamsymbol
@write fid bitsparamint
@write fid bitsparamuint
@write fid tuple_of_tuples

# Make sure we can create groups (i.e., use HDF5 features)
g = g_create(fid, "mygroup")
Expand Down Expand Up @@ -509,6 +512,7 @@ for compress in (true,false)
@check fidr bitsparamsymbol
@check fidr bitsparamint
@check fidr bitsparamuint
@check fidr tuple_of_tuples

x1 = read(fidr, "group1/x")
@assert x1 == Any[1]
Expand Down Expand Up @@ -694,6 +698,10 @@ jldopen(fn, "w") do file
write(file, "x7", reinterpret(TestType7, 0x77))
write(file, "x8", TestType8(TestType4(2), TestType5(TestType4(3)),
TestType6(), reinterpret(TestType7, 0x12)))
write(file, "x9", (TestType4(1),
(TestType5(TestType4(2)),
[TestType5(TestType4(i)) for i = 1:5]),
TestType6()))
end
end

Expand All @@ -714,17 +722,31 @@ jldopen(fn, "r") do file
println("The following missing type warnings are a sign of normal operation.")
@test read(file, "x3").x == 1
@test read(file, "x4").x.x == 2

x = read(file, "x5")
for i = 1:5
@test x[i].x.x == i
end
@test typeof(read(file, "x6")).names == ()
@test reinterpret(Uint8, read(file, "x7")) == 0x77

x = read(file, "x8")
@test x.a.x == 2
@test x.b.x.x == 3
@test typeof(x.c).names == ()
@test reinterpret(Uint8, x.d) == 0x12

x = read(file, "x9")
@test isa(x, Tuple)
@test length(x) == 3
@test x[1].x == 1
@test isa(x[2], Tuple)
@test length(x[2]) == 2
@test x[2][1].x.x == 2
for i = 1:5
@test x[2][2][i].x.x == i
end
@test typeof(x[3]).names == ()
end

# Issue #176
Expand Down

0 comments on commit f011e58

Please sign in to comment.