Skip to main content

async_hdf5/messages/
link.rs

1use bytes::Bytes;
2
3use crate::endian::HDF5Reader;
4use crate::error::{HDF5Error, Result};
5
6/// Link type.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum LinkType {
9    /// Hard link — points directly to an object header.
10    Hard,
11    /// Soft link — symbolic reference by path string.
12    Soft,
13    /// External link — reference to an object in another file.
14    External,
15}
16
17/// A link message — describes a single named link within a group (v2 groups).
18///
19/// Message type 0x0006.
20#[derive(Debug, Clone)]
21pub struct LinkMessage {
22    /// Link name.
23    pub name: String,
24    /// Link type.
25    pub link_type: LinkType,
26    /// For hard links: address of the target object header.
27    pub target_address: Option<u64>,
28    /// For soft links: the link value string.
29    pub soft_link_value: Option<String>,
30    /// Creation order (if tracked).
31    pub creation_order: Option<u64>,
32}
33
34impl LinkMessage {
35    /// Parse from the raw message bytes.
36    pub fn parse(data: &Bytes, size_of_offsets: u8, size_of_lengths: u8) -> Result<Self> {
37        let mut r = HDF5Reader::with_sizes(data.clone(), size_of_offsets, size_of_lengths);
38
39        let _version = r.read_u8()?;
40        let flags = r.read_u8()?;
41
42        // Flags bits:
43        // 0-1: Size of link name length field (0=1, 1=2, 2=4, 3=8 bytes)
44        // 2: Creation Order field present
45        // 3: Link Type field present
46        // 4: Link Name Character Set field present
47
48        let link_type = if flags & 0x08 != 0 {
49            match r.read_u8()? {
50                0 => LinkType::Hard,
51                1 => LinkType::Soft,
52                64 => LinkType::External,
53                t => return Err(HDF5Error::UnsupportedLinkType(t)),
54            }
55        } else {
56            LinkType::Hard // default
57        };
58
59        let creation_order = if flags & 0x04 != 0 {
60            Some(r.read_u64()?)
61        } else {
62            None
63        };
64
65        let _charset = if flags & 0x10 != 0 {
66            r.read_u8()? // 0=ASCII, 1=UTF-8
67        } else {
68            0
69        };
70
71        let name_size_field_width = 1u8 << (flags & 0x03);
72        let name_length = match name_size_field_width {
73            1 => r.read_u8()? as usize,
74            2 => r.read_u16()? as usize,
75            4 => r.read_u32()? as usize,
76            8 => r.read_u64()? as usize,
77            _ => unreachable!(),
78        };
79
80        let name_bytes = r.read_bytes(name_length)?;
81        let name = String::from_utf8_lossy(&name_bytes).to_string();
82
83        let (target_address, soft_link_value) = match link_type {
84            LinkType::Hard => {
85                let addr = r.read_offset()?;
86                (Some(addr), None)
87            }
88            LinkType::Soft => {
89                let value_length = r.read_u16()? as usize;
90                let value_bytes = r.read_bytes(value_length)?;
91                let value = String::from_utf8_lossy(&value_bytes).to_string();
92                (None, Some(value))
93            }
94            LinkType::External => {
95                let value_length = r.read_u16()? as usize;
96                let value_bytes = r.read_bytes(value_length)?;
97                let value = String::from_utf8_lossy(&value_bytes).to_string();
98                (None, Some(value))
99            }
100        };
101
102        Ok(Self {
103            name,
104            link_type,
105            target_address,
106            soft_link_value,
107            creation_order,
108        })
109    }
110}