///| Rebuilds supported data semantically, not byte-for-byte. Unsupported
///|
/// attributes and storage encodings fail rather than silently disappearing.
pub fn H5File::to_document(self : H5File) -> Document raise H5Error {
let doc = Document::new(limits=self.limits)
let entries = self.walk()
let names : Map[Int32, String] = Map([])
let mut retained = 0
for entry in entries {
names[entry.address] = entry.path
if entry.path != "/" {
if entry.dataset {
let value = self.read(entry.path)
retained = checked_add(retained, value.raw.length())
if retained > self.limits.max_file_bytes {
raise Limit("repack retained bytes")
}
doc.add_dataset(entry.path, value)
} else {
doc.add_group(entry.path)
}
}
for attribute in self.attributes(path=entry.path) {
retained = checked_add(retained, attribute.value.raw.length())
if retained > self.limits.max_file_bytes {
raise Limit("repack retained bytes")
}
doc.set_attribute(entry.path, attribute.name, attribute.value)
}
}
for entry in entries {
if entry.dataset {
continue
}
for link in self.links(path=entry.path) {
let path = (if entry.path == "/" { "" } else { entry.path }) +
"/" +
link.name
match link.target {
Hard(address) => {
guard names.get(address) is Some(target) else {
raise Invalid("unvisited hard link target", address)
}
if path != target {
doc.add_hard_link(path, target)
}
}
Soft(target) => doc.add_soft_link(path, target)
}
}
}
doc
}
///|
test "repack preserves soft links aliases attributes and cyclic groups" {
let doc = Document::new()
doc.add_group("/g")
doc.add_dataset(
"/g/data",
ArrayValue::from_signed(Simple([2]), [3L, 4L]),
compact=true,
)
doc.add_hard_link("/g/root", "/")
doc.add_hard_link("/same", "/g/data")
doc.add_soft_link("/g/relative", "data")
doc.add_soft_link("/absolute", "/g/data")
doc.add_soft_link("/dangling", "/absent")
doc.set_attribute(
"/g/data",
"unit",
ArrayValue::from_strings(Scalar, ["K"], 1),
)
let file = H5File::open(doc.encode())
assert_eq(file.read("/g/relative").signed_at(1), 4L)
let copy = H5File::open(file.to_document().encode())
assert_eq(copy.read("/absolute").signed_at(0), 3L)
assert_eq(copy.object_address("/same"), copy.object_address("/g/data"))
assert_eq(copy.walk().length(), 3)
assert_eq(copy.attributes(path="/g/data")[0].value.string_at(0), "K")
assert_true(
try {
ignore(copy.read("/dangling"))
false
} catch {
_ => true
},
)
}
///|
test "soft link cycles terminate at a depth limit" {
let doc = Document::new()
doc.add_soft_link("/a", "/b")
doc.add_soft_link("/b", "/a")
let f = H5File::open(doc.encode())
assert_true(
try {
ignore(f.object_address("/a"))
false
} catch {
_ => true
},
)
}