|
465 | 465 | ==(a::HDF5ReferenceObj, b::HDF5ReferenceObj) = a.r == b.r
|
466 | 466 | hash(x::HDF5ReferenceObj, h::UInt) = hash(x.r, h)
|
467 | 467 |
|
468 |
| -# Compound types |
469 |
| -struct HDF5Compound{N} |
470 |
| - data::NTuple{N,Any} |
471 |
| - membername::NTuple{N,String} |
472 |
| - membertype::NTuple{N,Type} |
473 |
| -end |
474 |
| - |
475 | 468 | # Opaque types
|
476 | 469 | struct HDF5Opaque
|
477 | 470 | data
|
|
482 | 475 | struct EmptyArray{T} end
|
483 | 476 |
|
484 | 477 | # Stub types to encode fixed-size arrays for H5T_ARRAY
|
485 |
| -struct FixedArray{T,D} end |
486 |
| -size(::Type{FixedArray{T,D}}) where {T,D} = D |
487 |
| -eltype(::Type{FixedArray{T,D}}) where {T,D} = T |
| 478 | +struct FixedArray{T,D,L} |
| 479 | + data::NTuple{L, T} |
| 480 | +end |
| 481 | +size(::Type{FixedArray{T,D,L}}) where {T,D,L} = D |
| 482 | +size(x::T) where T <: FixedArray = size(T) |
| 483 | +eltype(::Type{FixedArray{T,D,L}}) where {T,D,L} = T |
| 484 | +eltype(x::T) where T <: FixedArray = eltype(T) |
| 485 | + |
| 486 | +struct FixedString{N} |
| 487 | + data::NTuple{N, Cchar} |
| 488 | +end |
| 489 | +length(::Type{FixedString{N}}) where N = N |
| 490 | + |
| 491 | +struct VariableArray{T} |
| 492 | + len::Csize_t |
| 493 | + p::Ptr{Cvoid} |
| 494 | +end |
| 495 | +eltype(::Type{VariableArray{T}}) where T = T |
488 | 496 |
|
489 | 497 | # VLEN objects
|
490 | 498 | struct HDF5Vlen{T}
|
@@ -1459,73 +1467,45 @@ function getindex(parent::Union{HDF5File, HDF5Group, HDF5Dataset}, r::HDF5Refere
|
1459 | 1467 | h5object(obj_id, parent)
|
1460 | 1468 | end
|
1461 | 1469 |
|
1462 |
| -# Helper for reading compound types |
1463 |
| -function read_row(io::IO, membertype, membersize) |
1464 |
| - row = Any[] |
1465 |
| - for (dtype, dsize) in zip(membertype, membersize) |
1466 |
| - if dtype === String |
1467 |
| - push!(row, unpad(read!(io, Vector{UInt8}(undef,dsize)), H5T_STR_NULLPAD)) |
1468 |
| - elseif dtype<:HDF5.FixedArray && eltype(dtype)<:HDF5BitsKind |
1469 |
| - val = read!(io, Vector{eltype(dtype)}(undef,prod(size(dtype)))) |
1470 |
| - push!(row, reshape(val, size(dtype))) |
1471 |
| - elseif dtype<:HDF5BitsKind |
1472 |
| - push!(row, read(io, dtype)) |
1473 |
| - else |
1474 |
| - # for other types, just store the raw bytes and let the user |
1475 |
| - # decide what to do |
1476 |
| - push!(row, read!(io, Vector{UInt8}(undef,dsize))) |
1477 |
| - end |
1478 |
| - end |
1479 |
| - return (row...,) |
1480 |
| -end |
| 1470 | +# convert special types to native julia types |
| 1471 | +normalize_types(x) = x |
| 1472 | +normalize_types(x::NamedTuple{T}) where T = NamedTuple{T}(map(normalize_types, values(x))) |
| 1473 | +normalize_types(x::Cstring) = unsafe_string(x) |
| 1474 | +normalize_types(x::FixedString) = join(Char.(x.data)) |
| 1475 | +normalize_types(x::FixedArray) = reshape(collect(x.data), size(x)...) |
| 1476 | +normalize_types(x::VariableArray) = copy(unsafe_wrap(Array, convert(Ptr{eltype(x)}, x.p), x.len, own=false)) |
1481 | 1477 |
|
1482 |
| -# Read compound type |
1483 |
| -function read(obj::HDF5Dataset, T::Union{Type{Array{HDF5Compound{N}}},Type{HDF5Compound{N}}}) where {N} |
1484 |
| - t = datatype(obj) |
1485 |
| - local sz = 0; local n; |
1486 |
| - local membername; local membertype; |
1487 |
| - local memberoffset; local memberfiletype; local membersize; |
1488 |
| - try |
1489 |
| - memberfiletype = Vector{HDF5Datatype}(undef,N) |
1490 |
| - membertype = Vector{Type}(undef,N) |
1491 |
| - membername = Vector{String}(undef,N) |
1492 |
| - memberoffset = Vector{UInt64}(undef,N) |
1493 |
| - membersize = Vector{UInt32}(undef,N) |
1494 |
| - for i = 1:N |
1495 |
| - filetype = HDF5Datatype(h5t_get_member_type(t.id, i-1)) |
1496 |
| - memberfiletype[i] = filetype |
1497 |
| - membertype[i] = hdf5_to_julia_eltype(filetype) |
1498 |
| - memberoffset[i] = sz |
1499 |
| - membersize[i] = sizeof(filetype) |
1500 |
| - sz += sizeof(filetype) |
1501 |
| - membername[i] = h5t_get_member_name(t.id, i-1) |
1502 |
| - end |
1503 |
| - finally |
1504 |
| - close(t) |
1505 |
| - end |
1506 |
| - # Build the "memory type" |
1507 |
| - memtype_id = h5t_create(H5T_COMPOUND, sz) |
1508 |
| - for i = 1:N |
1509 |
| - h5t_insert(memtype_id, membername[i], memberoffset[i], memberfiletype[i].id) # FIXME strings |
1510 |
| - end |
1511 |
| - # Read the raw data |
1512 |
| - buf = Vector{UInt8}(undef,length(obj)*sz) |
1513 |
| - h5d_read(obj.id, memtype_id, H5S_ALL, H5S_ALL, obj.xfer, buf) |
1514 |
| - |
1515 |
| - # Convert to the appropriate data format using iobuffer |
1516 |
| - iobuff = IOBuffer(buf) |
1517 |
| - data = Any[] |
1518 |
| - while !eof(iobuff) |
1519 |
| - push!(data, read_row(iobuff, membertype, membersize)) |
1520 |
| - end |
1521 |
| - # convert HDF5Compound type parameters to tuples |
1522 |
| - membername = (membername...,) |
1523 |
| - membertype = (membertype...,) |
1524 |
| - if T === HDF5Compound{N} |
1525 |
| - return HDF5Compound(data[1], membername, membertype) |
1526 |
| - else |
1527 |
| - return [HDF5Compound(elem, membername, membertype) for elem in data] |
1528 |
| - end |
| 1478 | +do_normalize(::Type{T}) where T = false |
| 1479 | +do_normalize(::Type{NamedTuple{T, U}}) where T where U = any(i -> do_normalize(fieldtype(U,i)), 1:fieldcount(U)) |
| 1480 | +do_normalize(::Type{T}) where T <: Union{Cstring, FixedString, FixedArray, VariableArray} = true |
| 1481 | + |
| 1482 | +do_reclaim(::Type{T}) where T = false |
| 1483 | +do_reclaim(::Type{NamedTuple{T, U}}) where T where U = any(i -> do_reclaim(fieldtype(U,i)), 1:fieldcount(U)) |
| 1484 | +do_reclaim(::Type{T}) where T <: Union{Cstring, VariableArray} = true |
| 1485 | + |
| 1486 | +function read(dset::HDF5Dataset, T::Union{Type{Array{U}}, Type{U}}) where U <: NamedTuple |
| 1487 | + filetype = HDF5.datatype(dset) |
| 1488 | + memtype_id = HDF5.h5t_get_native_type(filetype.id) # padded layout in memory |
| 1489 | + @assert sizeof(U) == HDF5.h5t_get_size(memtype_id) "Type sizes mismatch!" |
| 1490 | + |
| 1491 | + buf = Array{U}(undef, size(dset)) |
| 1492 | + |
| 1493 | + HDF5.h5d_read(dset.id, memtype_id, HDF5.H5S_ALL, HDF5.H5S_ALL, HDF5.H5P_DEFAULT, buf) |
| 1494 | + out = do_normalize(U) ? normalize_types.(buf) : buf |
| 1495 | + |
| 1496 | + if do_reclaim(U) |
| 1497 | + dspace = dataspace(dset) |
| 1498 | + # NOTE I have seen this call fail but I cannot reproduce |
| 1499 | + h5d_vlen_reclaim(memtype_id, dspace.id, H5P_DEFAULT, buf) |
| 1500 | + end |
| 1501 | + |
| 1502 | + HDF5.h5t_close(memtype_id) |
| 1503 | + |
| 1504 | + if T <: NamedTuple |
| 1505 | + return out[1] |
| 1506 | + else |
| 1507 | + return out |
| 1508 | + end |
1529 | 1509 | end
|
1530 | 1510 |
|
1531 | 1511 | # Read OPAQUE datasets and attributes
|
@@ -2006,19 +1986,42 @@ function hdf5_to_julia_eltype(objtype)
|
2006 | 1986 | super_id = h5t_get_super(objtype.id)
|
2007 | 1987 | T = HDF5Vlen{hdf5_to_julia_eltype(HDF5Datatype(super_id))}
|
2008 | 1988 | elseif class_id == H5T_COMPOUND
|
2009 |
| - N = Int(h5t_get_nmembers(objtype.id)) |
2010 |
| - # check if should be interpreted as complex |
2011 |
| - if COMPLEX_SUPPORT[] && N == 2 |
2012 |
| - membernames = ntuple(N) do i |
2013 |
| - h5t_get_member_name(objtype.id, i-1) |
2014 |
| - end |
2015 |
| - membertypes = ntuple(N) do i |
2016 |
| - hdf5_to_julia_eltype(HDF5Datatype(h5t_get_member_type(objtype.id, i-1))) |
| 1989 | + N = h5t_get_nmembers(objtype.id) |
| 1990 | + |
| 1991 | + membernames = ntuple(N) do i |
| 1992 | + h5t_get_member_name(objtype.id, i-1) |
| 1993 | + end |
| 1994 | + |
| 1995 | + membertypes = ntuple(N) do i |
| 1996 | + dtype = HDF5Datatype(h5t_get_member_type(objtype.id, i-1)) |
| 1997 | + ci = h5t_get_class(dtype.id) |
| 1998 | + |
| 1999 | + if ci == H5T_STRING |
| 2000 | + if h5t_is_variable_str(dtype.id) |
| 2001 | + return Cstring |
| 2002 | + else |
| 2003 | + n = h5t_get_size(dtype.id) |
| 2004 | + return FixedString{Int(n)} |
| 2005 | + end |
| 2006 | + elseif ci == H5T_VLEN |
| 2007 | + superid = h5t_get_super(dtype.id) |
| 2008 | + T = VariableArray{hdf5_to_julia_eltype(HDF5Datatype(superid))} |
| 2009 | + else |
| 2010 | + return hdf5_to_julia_eltype(dtype) |
2017 | 2011 | end
|
2018 |
| - iscomplex = (membernames == COMPLEX_FIELD_NAMES[]) && (membertypes[1] == membertypes[2]) && (membertypes[1] <: HDF5.HDF5Scalar) |
2019 |
| - T = iscomplex ? Complex{membertypes[1]} : HDF5Compound{N} |
| 2012 | + end |
| 2013 | + |
| 2014 | + # check if should be interpreted as complex |
| 2015 | + iscomplex = COMPLEX_SUPPORT[] && |
| 2016 | + N == 2 && |
| 2017 | + (membernames == COMPLEX_FIELD_NAMES[]) && |
| 2018 | + (membertypes[1] == membertypes[2]) && |
| 2019 | + (membertypes[1] <: HDF5.HDF5Scalar) |
| 2020 | + |
| 2021 | + if iscomplex |
| 2022 | + T = Complex{membertypes[1]} |
2020 | 2023 | else
|
2021 |
| - T = HDF5Compound{N} |
| 2024 | + T = NamedTuple{Symbol.(membernames), Tuple{membertypes...}} |
2022 | 2025 | end
|
2023 | 2026 | elseif class_id == H5T_ARRAY
|
2024 | 2027 | T = hdf5array(objtype)
|
@@ -2423,7 +2426,7 @@ function hdf5array(objtype)
|
2423 | 2426 | eltyp = HDF5Datatype(h5t_get_super(objtype.id))
|
2424 | 2427 | T = hdf5_to_julia_eltype(eltyp)
|
2425 | 2428 | dimsizes = ntuple(i -> Int(dims[nd-i+1]), nd) # reverse order
|
2426 |
| - FixedArray{T, dimsizes} |
| 2429 | + FixedArray{T, dimsizes, prod(dimsizes)} |
2427 | 2430 | end
|
2428 | 2431 |
|
2429 | 2432 | ### Property manipulation ###
|
|
0 commit comments