writer: order dense name indexes by hash, then name
libhdf5 compares the name when two hashes are equal; the writer broke ties by insertion order, and libhdf5 could not find one of two names whose lookup3 hashes collide (k69209 / k155448). Test fails before the fix with h5py's KeyError. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
@@ -910,20 +910,27 @@ pub(crate) fn build_dense_attrs(
|
||||
let heap_id_length = heap.heap_id_length;
|
||||
let heap_ids = &heap.heap_ids;
|
||||
|
||||
// Build B-tree v2 type 8 records (17 bytes each)
|
||||
// Build B-tree v2 type 8 records (17 bytes each), in the index's key
|
||||
// order: libhdf5 compares the name hash, then — for names whose hashes
|
||||
// collide — the names themselves (`strcmp`).
|
||||
let record_size: u16 = heap_id_length + 1 + 4 + 4;
|
||||
let mut records: Vec<(u32, u32, Vec<u8>)> = Vec::with_capacity(attrs.len());
|
||||
for (i, heap_id) in heap_ids.iter().enumerate() {
|
||||
let mut rec = Vec::with_capacity(record_size as usize);
|
||||
rec.extend_from_slice(heap_id);
|
||||
rec.push(0); // msg_flags
|
||||
rec.extend_from_slice(&(i as u32).to_le_bytes()); // creation_order
|
||||
rec.extend_from_slice(&name_hashes[i].to_le_bytes()); // hash
|
||||
records.push((name_hashes[i], i as u32, rec));
|
||||
}
|
||||
records.sort_by(|a, b| a.0.cmp(&b.0).then(a.1.cmp(&b.1)));
|
||||
|
||||
let records: Vec<Vec<u8>> = records.into_iter().map(|(_, _, rec)| rec).collect();
|
||||
let mut order: Vec<usize> = (0..attrs.len()).collect();
|
||||
order.sort_by(|&a, &b| {
|
||||
name_hashes[a]
|
||||
.cmp(&name_hashes[b])
|
||||
.then_with(|| attrs[a].name.as_bytes().cmp(attrs[b].name.as_bytes()))
|
||||
});
|
||||
let records: Vec<Vec<u8>> = order
|
||||
.into_iter()
|
||||
.map(|i| {
|
||||
let mut rec = Vec::with_capacity(record_size as usize);
|
||||
rec.extend_from_slice(&heap_ids[i]);
|
||||
rec.push(0); // msg_flags
|
||||
rec.extend_from_slice(&(i as u32).to_le_bytes()); // creation_order
|
||||
rec.extend_from_slice(&name_hashes[i].to_le_bytes()); // hash
|
||||
rec
|
||||
})
|
||||
.collect();
|
||||
let bthd_addr = btree_addr;
|
||||
let mut blob = heap.blob;
|
||||
blob.extend_from_slice(&single_leaf_v2_btree(
|
||||
@@ -1039,14 +1046,18 @@ pub(crate) fn build_dense_links(
|
||||
let heap = build_single_block_fractal_heap(&serialized, base_address, 32, 7)?;
|
||||
let heap_id_length = heap.heap_id_length;
|
||||
|
||||
// Type 5 records: hash(4) + heap_id. The B-tree's key is the name hash,
|
||||
// so records are sorted by (hash, order).
|
||||
// Type 5 records: hash(4) + heap_id. The B-tree's key is the name hash
|
||||
// and, for names whose hashes collide, the name (libhdf5 compares them
|
||||
// with `strcmp`): records out of that order are not found by name.
|
||||
let mut by_name: Vec<(u32, usize)> = links
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, l)| (crate::checksum::jenkins_lookup3(l.name.as_bytes()), i))
|
||||
.collect();
|
||||
by_name.sort_unstable();
|
||||
by_name.sort_unstable_by(|&(ha, a), &(hb, b)| {
|
||||
ha.cmp(&hb)
|
||||
.then_with(|| links[a].name.as_bytes().cmp(links[b].name.as_bytes()))
|
||||
});
|
||||
let name_records: Vec<Vec<u8>> = by_name
|
||||
.iter()
|
||||
.map(|&(hash, i)| {
|
||||
|
||||
Reference in New Issue
Block a user