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}