Skip to main content

hdf5_reader/messages/
fill_value.rs

1//! HDF5 Fill Value messages.
2//!
3//! Two message types carry fill value information:
4//! - Old fill value (type 0x0004): raw bytes, length = message size.
5//! - New fill value (type 0x0005): versioned, with allocation/write time and defined flag.
6
7use crate::error::{Error, Result};
8use crate::io::Cursor;
9
10/// When to write the fill value.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum FillTime {
13    /// Write fill value only if the user explicitly set one.
14    IfSet,
15    /// Always write a fill value (use default if none set).
16    Always,
17    /// Never write a fill value.
18    Never,
19}
20
21/// Parsed fill value message.
22#[derive(Debug, Clone)]
23pub struct FillValueMessage {
24    /// Whether a fill value is defined.
25    pub defined: bool,
26    /// When to write the fill value.
27    pub fill_time: FillTime,
28    /// The raw fill value bytes, if defined.
29    pub value: Option<Vec<u8>>,
30}
31
32/// Parse the old fill value message (type 0x0004).
33///
34/// The entire message body is the raw fill value bytes.
35pub fn parse_old(
36    cursor: &mut Cursor<'_>,
37    _offset_size: u8,
38    _length_size: u8,
39    msg_size: usize,
40) -> Result<FillValueMessage> {
41    let value = if msg_size > 0 {
42        Some(cursor.read_bytes(msg_size)?.to_vec())
43    } else {
44        None
45    };
46
47    Ok(FillValueMessage {
48        defined: value.is_some(),
49        fill_time: FillTime::IfSet,
50        value,
51    })
52}
53
54/// Parse the new fill value message (type 0x0005).
55pub fn parse_new(
56    cursor: &mut Cursor<'_>,
57    _offset_size: u8,
58    _length_size: u8,
59    msg_size: usize,
60) -> Result<FillValueMessage> {
61    let start = cursor.position();
62    let version = cursor.read_u8()?;
63
64    match version {
65        1 | 2 => parse_new_v1_v2(cursor, version),
66        3 => parse_new_v3(cursor),
67        v => Err(Error::UnsupportedFillValueVersion(v)),
68    }
69    .and_then(|msg| {
70        let consumed = (cursor.position() - start) as usize;
71        if consumed < msg_size {
72            cursor.skip(msg_size - consumed)?;
73        }
74        Ok(msg)
75    })
76}
77
78fn parse_new_v1_v2(cursor: &mut Cursor<'_>, _version: u8) -> Result<FillValueMessage> {
79    let _alloc_time = cursor.read_u8()?;
80    let fill_time_byte = cursor.read_u8()?;
81    let defined_flag = cursor.read_u8()?;
82
83    let fill_time = match fill_time_byte {
84        0 => FillTime::IfSet,
85        1 => FillTime::Always,
86        2 => FillTime::Never,
87        _ => FillTime::IfSet,
88    };
89
90    let defined = defined_flag != 0;
91
92    let value = if defined {
93        let size = cursor.read_u32_le()? as usize;
94        if size > 0 {
95            Some(cursor.read_bytes(size)?.to_vec())
96        } else {
97            None
98        }
99    } else {
100        None
101    };
102
103    Ok(FillValueMessage {
104        defined,
105        fill_time,
106        value,
107    })
108}
109
110fn parse_new_v3(cursor: &mut Cursor<'_>) -> Result<FillValueMessage> {
111    let flags = cursor.read_u8()?;
112
113    let _alloc_time = flags & 0x03;
114    let fill_time_bits = (flags >> 2) & 0x03;
115    let undefined = (flags & 0x20) != 0;
116    let defined = (flags & 0x10) != 0;
117
118    let fill_time = match fill_time_bits {
119        0 => FillTime::IfSet,
120        1 => FillTime::Always,
121        2 => FillTime::Never,
122        _ => FillTime::IfSet,
123    };
124
125    if undefined {
126        return Ok(FillValueMessage {
127            defined: false,
128            fill_time,
129            value: None,
130        });
131    }
132
133    let value = if defined {
134        let size = cursor.read_u32_le()? as usize;
135        if size > 0 {
136            Some(cursor.read_bytes(size)?.to_vec())
137        } else {
138            None
139        }
140    } else {
141        None
142    };
143
144    Ok(FillValueMessage {
145        defined,
146        fill_time,
147        value,
148    })
149}
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154
155    #[test]
156    fn test_parse_old_fill() {
157        let data = [0x01, 0x02, 0x03, 0x04];
158        let mut cursor = Cursor::new(&data);
159        let msg = parse_old(&mut cursor, 8, 8, 4).unwrap();
160        assert!(msg.defined);
161        assert_eq!(msg.value.unwrap(), vec![0x01, 0x02, 0x03, 0x04]);
162    }
163
164    #[test]
165    fn test_parse_old_fill_empty() {
166        let data = [];
167        let mut cursor = Cursor::new(&data);
168        let msg = parse_old(&mut cursor, 8, 8, 0).unwrap();
169        assert!(!msg.defined);
170        assert!(msg.value.is_none());
171    }
172
173    #[test]
174    fn test_parse_new_v2_defined() {
175        let mut data = vec![
176            0x02, // version 2
177            0x01, // alloc time
178            0x01, // fill time = always
179            0x01, // defined = yes
180        ];
181        // fill value size = 8
182        data.extend_from_slice(&8u32.to_le_bytes());
183        // fill value bytes
184        data.extend_from_slice(&[0xFF; 8]);
185
186        let mut cursor = Cursor::new(&data);
187        let msg = parse_new(&mut cursor, 8, 8, data.len()).unwrap();
188        assert!(msg.defined);
189        assert_eq!(msg.fill_time, FillTime::Always);
190        assert_eq!(msg.value.unwrap(), vec![0xFF; 8]);
191    }
192
193    #[test]
194    fn test_parse_new_v3_undefined() {
195        let data = [
196            0x03, // version 3
197            0x20, // flags: undefined bit set
198        ];
199        let mut cursor = Cursor::new(&data);
200        let msg = parse_new(&mut cursor, 8, 8, data.len()).unwrap();
201        assert!(!msg.defined);
202        assert!(msg.value.is_none());
203    }
204}