function Base.show(io::IO, fid::File) if isvalid(fid) intent = API.h5f_get_intent(fid) RW_MASK = API.H5F_ACC_RDONLY | API.H5F_ACC_RDWR SWMR_MASK = API.H5F_ACC_SWMR_READ | API.H5F_ACC_SWMR_WRITE rw = (intent & RW_MASK) == API.H5F_ACC_RDONLY ? "(read-only" : "(read-write" swmr = (intent & SWMR_MASK) != 0 ? ", swmr) " : ") " print(io, "HDF5.File: ", rw, swmr, fid.filename) else print(io, "HDF5.File: (closed) ", fid.filename) end end function Base.show(io::IO, g::Group) if isvalid(g) print(io, "HDF5.Group: ", name(g), " (file: ", g.file.filename, ")") else print(io, "HDF5.Group: (invalid)") end end function Base.show(io::IO, prop::Properties) print(io, typeof(prop)) if prop.id == API.H5P_DEFAULT print(io, "()") elseif !isvalid(prop) print(io, ": (invalid)") else print(io, "(") for name in all_propertynames(typeof(prop)) # skip deprecated names if name in (:compress, :track_times, :fapl_mpio) continue end # not all properties have getters (e.g. shuffle, deflate, etc.), # or always well-defined (e.g. chunk if layout != :chunked, dxpl_mpio if no MPI) try val = getproperty(prop, name) if name == :file_image print( io, "\n ", rpad(name, 15), " = ", repr(typeof(val)), ", length = ", length(val), "," ) else print(io, "\n ", rpad(name, 15), " = ", repr(val), ",") end catch e end end print(io, "\n)") end end function Base.show(io::IO, dset::Dataset) if isvalid(dset) print( io, "HDF5.Dataset: ", name(dset), " (file: ", dset.file.filename, " xfer_mode: ", dset.xfer.id, ")" ) else print(io, "HDF5.Dataset: (invalid)") end end function Base.show(io::IO, attr::Attribute) if isvalid(attr) print(io, "HDF5.Attribute: ", name(attr)) else print(io, "HDF5.Attribute: (invalid)") end end function Base.show(io::IO, attr::Attributes) print(io, "Attributes of ", attr.parent) end Base.show(io::IO, attrdict::AttributeDict) = summary(io, attrdict) function Base.summary(io::IO, attrdict::AttributeDict) print(io, "AttributeDict of ", attrdict.parent) if isvalid(attrdict.parent) n = length(attrdict) print(io, " with ", n, n == 1 ? " attribute" : " attributes") end end const ENDIAN_DICT = Dict( API.H5T_ORDER_LE => "little endian byte order", API.H5T_ORDER_BE => "big endian byte order", API.H5T_ORDER_VAX => "vax mixed endian byte order", API.H5T_ORDER_MIXED => "mixed endian byte order", API.H5T_ORDER_NONE => "no particular byte order", ) function Base.show(io::IO, dtype::Datatype) print(io, "HDF5.Datatype: ") if isvalid(dtype) API.h5t_committed(dtype) && print(io, name(dtype), " ") buffer = IOBuffer(; sizehint=18) print(buffer, API.h5lt_dtype_to_text(dtype)) str = String(take!(buffer)) if str == "undefined integer" println(io, str) println(io, " size: ", API.h5t_get_size(dtype), " bytes") println(io, " precision: ", API.h5t_get_precision(dtype), " bits") println(io, " offset: ", API.h5t_get_offset(dtype), " bits") print(io, " order: ", ENDIAN_DICT[API.h5t_get_order(dtype)]) else print(io, str) end else # Note that API.h5i_is_valid returns `false` on the built-in datatypes (e.g. API.H5T_NATIVE_INT), # apparently because they have refcounts of 0 yet are always valid. Just temporarily turn # off error printing and try the call to probe if dtype is valid since API.H5LTdtype_to_text # special-cases all of the built-in types internally. local text try text = API.h5lt_dtype_to_text(dtype) catch text = "(invalid)" end print(io, text) end end function Base.show(io::IO, br::BlockRange) start = Int(br.start0 + 1) # choose the simplest possible representation if br.count == 1 if br.block == 1 # integer r = start else # UnitRange r = range(start; length=Int(br.block)) end elseif br.block == 1 && br.count != API.H5S_UNLIMITED # StepRange r = range(start; step=Int(br.stride), length=Int(br.count)) else # BlockRange(int; ...) print(io, BlockRange, "(start=", start) if br.stride != 1 print(io, ", stride=", Int(br.stride)) end if br.count != 1 print(io, ", count=", br.count == API.API.H5S_UNLIMITED ? -1 : Int(br.count)) end if br.block != 1 print(io, ", block=", Int(br.block)) end print(io, ")") return nothing end compact = get(io, :compact, false) compact || print(io, BlockRange, "(") print(io, r) compact || print(io, ")") return nothing end function Base.show(io::IO, dspace::Dataspace) if !isvalid(dspace) print(io, "HDF5.Dataspace: (invalid)") return nothing end print(io, "HDF5.Dataspace: ") type = API.h5s_get_simple_extent_type(dspace) if type == API.H5S_NULL print(io, "H5S_NULL") return nothing elseif type == API.H5S_SCALAR print(io, "H5S_SCALAR") return nothing end # otherwise type == API.H5S_SIMPLE sz, maxsz = get_extent_dims(dspace) sel = API.h5s_get_select_type(dspace) if sel == API.H5S_SEL_HYPERSLABS && API.h5s_is_regular_hyperslab(dspace) io_compact = IOContext(io, :compact => true) blockranges = get_regular_hyperslab(dspace) ndims = length(blockranges) print(io_compact, "(") for ii in 1:ndims print(io_compact, blockranges[ii]) ii != ndims && print(io_compact, ", ") end print(io_compact, ") / (") for ii in 1:ndims print(io_compact, 1:maxsz[ii]) ii != ndims && print(io_compact, ", ") end print(io_compact, ")") else print(io, sz) if maxsz != sz print(io, " / ", maxsz) end if sel != API.H5S_SEL_ALL print(io, " [irregular selection]") end end end """ SHOW_TREE_ICONS = Ref{Bool}(true) Configurable option to control whether emoji icons (`true`) or a plain-text annotation (`false`) is used to indicate the object type by `show_tree`. """ const SHOW_TREE_ICONS = Ref{Bool}(true) """ SHOW_TREE_MAX_DEPTH = Ref{Int}(5) Maximum recursive depth to descend during printing. """ const SHOW_TREE_MAX_DEPTH = Ref{Int}(5) """ SHOW_TREE_MAX_CHILDREN = Ref{Int}(50) Maximum number of children to show at each node. """ const SHOW_TREE_MAX_CHILDREN = Ref{Int}(50) function Base.show( io::IO, ::MIME"text/plain", obj::Union{File,Group,Dataset,Attributes,Attribute} ) if get(io, :compact, false)::Bool show(io, obj) else show_tree(io, obj) end end _tree_icon(::Type{Attribute}) = SHOW_TREE_ICONS[] ? "🏷️" : "[A]" _tree_icon(::Type{Group}) = SHOW_TREE_ICONS[] ? "📂" : "[G]" _tree_icon(::Type{Dataset}) = SHOW_TREE_ICONS[] ? "🔢" : "[D]" _tree_icon(::Type{Datatype}) = SHOW_TREE_ICONS[] ? "📄" : "[T]" _tree_icon(::Type{File}) = SHOW_TREE_ICONS[] ? "🗂️" : "[F]" _tree_icon(::Type) = SHOW_TREE_ICONS[] ? "❓" : "[?]" _tree_icon(obj) = _tree_icon(typeof(obj)) _tree_icon(obj::Attributes) = _tree_icon(obj.parent) _tree_head(io::IO, obj) = print(io, _tree_icon(obj), " ", obj) _tree_head(io::IO, obj::Datatype) = print(io, _tree_icon(obj), " HDF5.Datatype: ", name(obj)) _tree_count(parent::Union{File,Group}, attributes::Bool) = length(parent) + (attributes ? length(HDF5.attrs(parent)) : 0) _tree_count(parent::Dataset, attributes::Bool) = attributes ? length(HDF5.attrs(parent)) : 0 _tree_count(parent::Attributes, _::Bool) = length(parent) _tree_count(parent::Union{Attribute,Datatype}, _::Bool) = 0 function _show_tree( io::IO, obj::Union{File,Group,Dataset,Datatype,Attributes,Attribute}, indent::String=""; attributes::Bool=true, depth::Int=1 ) isempty(indent) && _tree_head(io, obj) isvalid(obj) || return nothing INDENT = " " PIPE = "│ " TEE = "├─ " ELBOW = "└─ " limit = get(io, :limit, false)::Bool counter = 0 nchildren = _tree_count(obj, attributes) @inline function childstr(io, n, more=" ") print(io, "\n", indent, ELBOW * "(", n, more, n == 1 ? "child" : "children", ")") end @inline function depth_check() counter += 1 if limit && counter > max(2, SHOW_TREE_MAX_CHILDREN[] ÷ depth) childstr(io, nchildren - counter + 1, " more ") return true end return false end if limit && nchildren > 0 && depth > SHOW_TREE_MAX_DEPTH[] childstr(io, nchildren) return nothing end if attributes && !isa(obj, Attribute) obj′ = obj isa Attributes ? obj.parent : obj API.h5a_iterate(obj′, idx_type(obj′), order(obj′)) do _, cname, _ depth_check() && return API.herr_t(1) name = unsafe_string(cname) icon = _tree_icon(Attribute) islast = counter == nchildren print(io, "\n", indent, islast ? ELBOW : TEE, icon, " ", name) return API.herr_t(0) end end typeof(obj) <: Union{File,Group} || return nothing API.h5l_iterate(obj, idx_type(obj), order(obj)) do loc_id, cname, _ depth_check() && return API.herr_t(1) name = unsafe_string(cname) child = obj[name] icon = _tree_icon(child) islast = counter == nchildren print(io, "\n", indent, islast ? ELBOW : TEE, icon, " ", name) nextindent = indent * (islast ? INDENT : PIPE) _show_tree(io, child, nextindent; attributes=attributes, depth=depth + 1) close(child) return API.herr_t(0) end return nothing end show_tree(obj; kws...) = show_tree(stdout, obj; kws...) function show_tree(io::IO, obj; kws...) buf = IOBuffer() _show_tree(IOContext(buf, io), obj; kws...) print(io, String(take!(buf))) end