Skip to main content

steel_core/worldgen/feature/
vanilla_collections.rs

1use smallvec::SmallVec;
2
3use super::prelude::*;
4
5const INLINE_CAPACITY: usize = 32;
6
7/// Block-position set for feature code that vanilla models as `HashSet<BlockPos>`.
8///
9/// Java `HashSet` iteration order is implementation-defined, so the extractor normalizes these
10/// worldgen sets to insertion order. Steel follows that deterministic oracle instead of depending
11/// on JVM bucket ordering.
12#[derive(Default)]
13pub(super) struct JavaBlockPosSet {
14    entries: SmallVec<[BlockPos; INLINE_CAPACITY]>,
15}
16
17impl JavaBlockPosSet {
18    pub(super) fn insert(&mut self, pos: BlockPos) -> bool {
19        if self.entries.contains(&pos) {
20            return false;
21        }
22
23        self.entries.push(pos);
24        true
25    }
26
27    pub(super) fn contains(&self, pos: BlockPos) -> bool {
28        self.entries.contains(&pos)
29    }
30
31    pub(super) fn is_empty(&self) -> bool {
32        self.entries.is_empty()
33    }
34
35    pub(super) fn insertion_order(&self) -> impl Iterator<Item = &BlockPos> {
36        self.entries.iter()
37    }
38
39    pub(super) fn java_ordered_positions(&self) -> Vec<BlockPos> {
40        self.entries.to_vec()
41    }
42
43    pub(super) fn pop_java_ordered_position(&mut self) -> Option<BlockPos> {
44        if self.entries.is_empty() {
45            return None;
46        }
47
48        Some(self.entries.remove(0))
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55
56    #[test]
57    fn duplicate_positions_keep_first_insertion() {
58        let mut set = JavaBlockPosSet::default();
59        assert!(set.insert(BlockPos::new(1, 2, 3)));
60        assert!(!set.insert(BlockPos::new(1, 2, 3)));
61        assert_eq!(set.java_ordered_positions(), [BlockPos::new(1, 2, 3)]);
62    }
63
64    #[test]
65    fn popped_positions_do_not_iterate() {
66        let mut set = JavaBlockPosSet::default();
67        for x in 0..4 {
68            assert!(set.insert(BlockPos::new(x, 0, 0)));
69        }
70
71        assert_eq!(
72            set.pop_java_ordered_position(),
73            Some(BlockPos::new(0, 0, 0))
74        );
75
76        assert_eq!(
77            set.java_ordered_positions(),
78            [
79                BlockPos::new(1, 0, 0),
80                BlockPos::new(2, 0, 0),
81                BlockPos::new(3, 0, 0)
82            ]
83        );
84    }
85
86    #[test]
87    fn reinserted_position_uses_new_insertion_position() {
88        let mut set = JavaBlockPosSet::default();
89        let first = BlockPos::new(1, 0, 0);
90        let second = BlockPos::new(17, 0, 0);
91
92        assert!(set.insert(first));
93        assert!(set.insert(second));
94        assert_eq!(set.pop_java_ordered_position(), Some(first));
95        assert!(set.insert(first));
96
97        assert_eq!(set.java_ordered_positions(), [second, first]);
98    }
99
100    #[test]
101    fn pop_uses_insertion_order() {
102        let mut set = JavaBlockPosSet::default();
103        let first = BlockPos::new(1, 0, 0);
104        let second = BlockPos::new(2, 0, 0);
105        assert!(set.insert(first));
106        assert!(set.insert(second));
107
108        assert_eq!(set.pop_java_ordered_position(), Some(first));
109        assert_eq!(set.pop_java_ordered_position(), Some(second));
110        assert_eq!(set.pop_java_ordered_position(), None);
111    }
112}