Skip to main content

async_hdf5/heap/
local.rs

1use std::sync::Arc;
2
3use bytes::Bytes;
4
5use crate::endian::HDF5Reader;
6use crate::error::{HDF5Error, Result};
7use crate::reader::AsyncFileReader;
8
9/// Parsed local heap header.
10///
11/// Local heaps store small strings (link names) for v1 groups.
12/// Symbol table entries reference strings by byte offset into the heap's data segment.
13///
14/// Binary layout (signature "HEAP"):
15///   - Signature (4 bytes): "HEAP"
16///   - Version (1 byte): 0
17///   - Reserved (3 bytes)
18///   - Data Segment Size (L bytes)
19///   - Offset to Head of Free-list (L bytes)
20///   - Address of Data Segment (O bytes)
21#[derive(Debug, Clone)]
22pub struct LocalHeap {
23    /// The heap's data segment, fetched from the file.
24    data_segment: Bytes,
25}
26
27impl LocalHeap {
28    /// Parse the local heap header and fetch the data segment.
29    pub async fn read(
30        reader: &Arc<dyn AsyncFileReader>,
31        address: u64,
32        size_of_offsets: u8,
33        size_of_lengths: u8,
34    ) -> Result<Self> {
35        // Fetch enough for the header: 4 + 1 + 3 + 2*L + O
36        let header_size = 8 + 2 * size_of_lengths as u64 + size_of_offsets as u64;
37        let header_data = reader.get_bytes(address..address + header_size).await?;
38
39        let mut r = HDF5Reader::with_sizes(header_data, size_of_offsets, size_of_lengths);
40
41        // Verify signature
42        let sig = r.read_bytes(4)?;
43        if &sig != b"HEAP" {
44            return Err(HDF5Error::InvalidHeapSignature {
45                expected: "HEAP".into(),
46                got: String::from_utf8_lossy(&sig).into(),
47            });
48        }
49
50        let version = r.read_u8()?;
51        if version != 0 {
52            return Err(HDF5Error::UnsupportedHeapVersion(version));
53        }
54        r.skip(3); // reserved
55
56        let data_segment_size = r.read_length()?;
57        let _free_list_offset = r.read_length()?;
58        let data_segment_address = r.read_offset()?;
59
60        // Fetch the data segment
61        let data_segment = reader
62            .get_bytes(data_segment_address..data_segment_address + data_segment_size)
63            .await?;
64
65        Ok(Self { data_segment })
66    }
67
68    /// Read a null-terminated string at the given offset in the data segment.
69    pub fn get_string(&self, offset: u64) -> Result<String> {
70        let start = offset as usize;
71        if start >= self.data_segment.len() {
72            return Err(HDF5Error::UnexpectedEof {
73                needed: start + 1,
74                available: self.data_segment.len(),
75            });
76        }
77
78        let bytes = &self.data_segment[start..];
79        let end = bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len());
80
81        Ok(String::from_utf8_lossy(&bytes[..end]).into())
82    }
83}