Skip to main content

rust_hdf5/format/
creation_order.rs

1//! How an object records the creation order of one of its collections.
2//!
3//! HDF5 keeps two of these per object and never merges them: a group's links
4//! are governed by `H5Pset_link_creation_order` and read back from the **Link
5//! Info** message's flag bits (`H5Gint.c::H5G__get_create_plist`), while every
6//! object's attributes are governed by `H5Pset_attr_creation_order` and read
7//! back from the **object header's own** flag bits (`H5Pocpl.c`, bits
8//! `H5O_HDR_ATTR_CRT_ORDER_TRACKED` / `..._INDEXED`). A file is free to set
9//! either one alone, so anything that carries "creation order is tracked" as a
10//! single fact for a whole object is already wrong for half the files libhdf5
11//! writes.
12
13/// The creation-order policy of one collection — a group's links, or an
14/// object's attributes.
15///
16/// Both property-list setters accept exactly three values: nothing,
17/// `H5P_CRT_ORDER_TRACKED`, or `H5P_CRT_ORDER_TRACKED | H5P_CRT_ORDER_INDEXED`.
18/// `INDEXED` on its own is rejected (`H5Pset_link_creation_order`,
19/// `H5Pset_attr_creation_order`), which is why this is one enum rather than
20/// two booleans: the fourth combination cannot be constructed.
21#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
22pub enum CreationOrder {
23    /// No creation order is recorded. Members carry the "no creation index"
24    /// sentinel and the object declares neither flag.
25    #[default]
26    Untracked,
27    /// Every member carries its creation index and the object declares a
28    /// running maximum, but no creation-order B-tree exists.
29    Tracked,
30    /// Tracked, plus a v2 B-tree indexing the members by creation order.
31    Indexed,
32}
33
34impl CreationOrder {
35    /// Whether members carry a creation index — the `TRACKED` flag bit.
36    pub fn is_tracked(self) -> bool {
37        !matches!(self, Self::Untracked)
38    }
39
40    /// Whether a creation-order index exists — the `INDEXED` flag bit.
41    pub fn is_indexed(self) -> bool {
42        matches!(self, Self::Indexed)
43    }
44
45    /// Recover the policy from the two on-disk flag bits.
46    ///
47    /// A lone `indexed` bit is read as [`Untracked`](Self::Untracked): the
48    /// library refuses to set it, and an index over records that carry no
49    /// creation index cannot be walked in creation order anyway.
50    pub fn from_flags(tracked: bool, indexed: bool) -> Self {
51        match (tracked, indexed) {
52            (true, true) => Self::Indexed,
53            (true, false) => Self::Tracked,
54            (false, _) => Self::Untracked,
55        }
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62
63    #[test]
64    fn indexed_implies_tracked() {
65        assert!(CreationOrder::Indexed.is_tracked());
66        assert!(CreationOrder::Indexed.is_indexed());
67        assert!(CreationOrder::Tracked.is_tracked());
68        assert!(!CreationOrder::Tracked.is_indexed());
69        assert!(!CreationOrder::Untracked.is_tracked());
70        assert!(!CreationOrder::Untracked.is_indexed());
71    }
72
73    #[test]
74    fn a_lone_indexed_flag_reads_as_untracked() {
75        assert_eq!(
76            CreationOrder::from_flags(false, true),
77            CreationOrder::Untracked
78        );
79        assert_eq!(
80            CreationOrder::from_flags(false, false),
81            CreationOrder::Untracked
82        );
83        assert_eq!(
84            CreationOrder::from_flags(true, false),
85            CreationOrder::Tracked
86        );
87        assert_eq!(
88            CreationOrder::from_flags(true, true),
89            CreationOrder::Indexed
90        );
91    }
92
93    #[test]
94    fn the_default_is_untracked() {
95        assert_eq!(CreationOrder::default(), CreationOrder::Untracked);
96    }
97}