From 99c6edeb188e0e4cf27f2ffb88d5edd0e65a29f4 Mon Sep 17 00:00:00 2001 From: Jay Kickliter Date: Mon, 29 Aug 2022 10:25:43 -0600 Subject: [PATCH 1/4] Rename to root --- src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ccc46d4..231e937 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,7 +64,7 @@ use std::{iter::FromIterator, mem::size_of, ops::Deref, ops::DerefMut}; )] pub struct HexSet { /// All h3 0 base cell indices in the tree - nodes: Box<[Option]>, + root: Box<[Option]>, } impl HexSet { @@ -74,7 +74,7 @@ impl HexSet { /// H3 cells. pub fn new() -> Self { Self { - nodes: vec![None; 122].into_boxed_slice(), + root: vec![None; 122].into_boxed_slice(), } } @@ -85,7 +85,7 @@ impl HexSet { /// significantly smaller than the number of source cells used to /// create the set. pub fn len(&self) -> usize { - self.nodes.iter().flatten().map(|node| node.len()).sum() + self.root.iter().flatten().map(|node| node.len()).sum() } /// Returns `true` if the set contains no cells. @@ -97,12 +97,12 @@ impl HexSet { pub fn insert(&mut self, hex: H3Cell) { let base_cell = base(&hex); let digits = Digits::new(hex); - match self.nodes[base_cell as usize].as_mut() { + match self.root[base_cell as usize].as_mut() { Some(node) => node.insert(digits), None => { let mut node = Node::new(); node.insert(digits); - self.nodes[base_cell as usize] = Some(node); + self.root[base_cell as usize] = Some(node); } } } @@ -120,7 +120,7 @@ impl HexSet { /// hex due to 1 or 2. pub fn contains(&self, hex: &H3Cell) -> bool { let base_cell = base(hex); - match self.nodes[base_cell as usize].as_ref() { + match self.root[base_cell as usize].as_ref() { Some(node) => { let digits = Digits::new(*hex); node.contains(digits) @@ -136,7 +136,7 @@ impl HexSet { pub fn mem_size(&self) -> usize { size_of::() + self - .nodes + .root .iter() .flatten() .map(|n| n.mem_size()) From 09671255247d7d3816a2c1ca9e0ab4c20b065e81 Mon Sep 17 00:00:00 2001 From: Jay Kickliter Date: Mon, 29 Aug 2022 11:44:36 -0600 Subject: [PATCH 2/4] Use an arean allocator --- src/lib.rs | 181 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 135 insertions(+), 46 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 231e937..86ec298 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,7 +53,11 @@ pub use h3ron; use h3ron::{H3Cell, Index}; -use std::{iter::FromIterator, mem::size_of, ops::Deref, ops::DerefMut}; +use std::{ + iter::FromIterator, + mem::size_of, + ops::{Deref, DerefMut}, +}; /// An efficient way to represent any portion(s) of Earth as a set of /// `H3` hexagons. @@ -64,7 +68,8 @@ use std::{iter::FromIterator, mem::size_of, ops::Deref, ops::DerefMut}; )] pub struct HexSet { /// All h3 0 base cell indices in the tree - root: Box<[Option]>, + root: Box<[Option]>, + arena: NodeArena, } impl HexSet { @@ -75,6 +80,7 @@ impl HexSet { pub fn new() -> Self { Self { root: vec![None; 122].into_boxed_slice(), + arena: NodeArena::new(), } } @@ -85,7 +91,11 @@ impl HexSet { /// significantly smaller than the number of source cells used to /// create the set. pub fn len(&self) -> usize { - self.root.iter().flatten().map(|node| node.len()).sum() + self.root + .iter() + .flatten() + .map(|arena_idx| self.arena[*arena_idx].len(&self.arena)) + .sum() } /// Returns `true` if the set contains no cells. @@ -98,11 +108,16 @@ impl HexSet { let base_cell = base(&hex); let digits = Digits::new(hex); match self.root[base_cell as usize].as_mut() { - Some(node) => node.insert(digits), + Some(arena_idx) => { + let mut node = self.arena[*arena_idx].clone(); + node.insert(&mut self.arena, digits); + self.arena[*arena_idx] = node; + } None => { let mut node = Node::new(); - node.insert(digits); - self.root[base_cell as usize] = Some(node); + node.insert(&mut self.arena, digits); + let arena_idx = self.arena.alloc(node); + self.root[base_cell as usize] = Some(arena_idx); } } } @@ -120,10 +135,10 @@ impl HexSet { /// hex due to 1 or 2. pub fn contains(&self, hex: &H3Cell) -> bool { let base_cell = base(hex); - match self.root[base_cell as usize].as_ref() { - Some(node) => { + match self.root[base_cell as usize] { + Some(arena_idx) => { let digits = Digits::new(*hex); - node.contains(digits) + self.arena[arena_idx].contains(&self.arena, digits) } None => false, } @@ -139,7 +154,7 @@ impl HexSet { .root .iter() .flatten() - .map(|n| n.mem_size()) + .map(|arena_idx| self.arena[*arena_idx].mem_size(&self.arena)) .sum::() } } @@ -175,51 +190,71 @@ impl<'a> FromIterator<&'a H3Cell> for HexSet { feature = "serde-support", derive(serde::Serialize, serde::Deserialize) )] -struct Node(Box<[Option; 7]>); +struct Node([Option; 7]); impl Node { - fn mem_size(&self) -> usize { - size_of::() + self.iter().flatten().map(|n| n.mem_size()).sum::() + fn mem_size(&self, arena: &NodeArena) -> usize { + size_of::() + + self + .iter() + .flatten() + .map(|arena_idx| arena[*arena_idx].mem_size(arena)) + .sum::() } fn new() -> Self { - Self(Box::new([None, None, None, None, None, None, None])) + Self([None, None, None, None, None, None, None]) } - fn len(&self) -> usize { + fn len(&self, arena: &NodeArena) -> usize { if self.is_full() { 1 } else { - self.iter().flatten().map(|child| child.len()).sum() + self.iter() + .flatten() + .map(|arena_idx| arena[*arena_idx].len(arena)) + .sum() } } - fn insert(&mut self, mut digits: Digits) { + fn insert(&mut self, arena: &mut NodeArena, mut digits: Digits) { match digits.next() { Some(digit) => match self[digit as usize].as_mut() { - Some(node) => node.insert(digits), + Some(arena_idx) => { + let mut node = arena[*arena_idx].clone(); + node.insert(arena, digits); + arena[*arena_idx] = node; + } None => { let mut node = Node::new(); - node.insert(digits); - self[digit as usize] = Some(node); + node.insert(arena, digits); + let arena_idx = arena.alloc(node); + self[digit as usize] = Some(arena_idx); } }, - None => *self.0 = [None, None, None, None, None, None, None], + None => self.0 = [None, None, None, None, None, None, None], }; - self.coalesce(); + self.coalesce(arena); } - fn coalesce(&mut self) { - if let [Some(n0), Some(n1), Some(n2), Some(n3), Some(n4), Some(n5), Some(n6)] = &*self.0 { - if n0.is_full() - && n1.is_full() - && n2.is_full() - && n3.is_full() - && n4.is_full() - && n5.is_full() - && n6.is_full() + fn coalesce(&mut self, arena: &mut NodeArena) { + if let [Some(n0), Some(n1), Some(n2), Some(n3), Some(n4), Some(n5), Some(n6)] = self.0 { + if arena[n0].is_full() + && arena[n1].is_full() + && arena[n2].is_full() + && arena[n3].is_full() + && arena[n4].is_full() + && arena[n5].is_full() + && arena[n6].is_full() { - *self.0 = [None, None, None, None, None, None, None] + arena.free(n0); + arena.free(n1); + arena.free(n2); + arena.free(n3); + arena.free(n4); + arena.free(n5); + arena.free(n6); + self.0 = [None, None, None, None, None, None, None] } }; } @@ -228,7 +263,7 @@ impl Node { self.iter().all(|c| c.is_none()) } - fn contains(&self, mut digits: Digits) -> bool { + fn contains(&self, arena: &NodeArena, mut digits: Digits) -> bool { if self.is_full() { return true; } @@ -237,7 +272,7 @@ impl Node { Some(digit) => { // TODO check if this node is "full" match &self[digit as usize] { - Some(node) => node.contains(digits), + Some(arena_idx) => arena[*arena_idx].contains(arena, digits), None => false, } } @@ -249,7 +284,7 @@ impl Node { } impl Deref for Node { - type Target = [Option]; + type Target = [Option]; fn deref(&self) -> &Self::Target { &self.0[..] @@ -262,6 +297,60 @@ impl DerefMut for Node { } } +#[derive(Clone, PartialEq, Eq)] +#[cfg_attr( + feature = "serde-support", + derive(serde::Serialize, serde::Deserialize) +)] +struct NodeArena { + nodes: Vec, + free: Vec, +} + +impl NodeArena { + fn new() -> Self { + Self { + nodes: Vec::new(), + free: Vec::new(), + } + } + + fn alloc(&mut self, node: Node) -> usize { + match self.free.pop() { + Some(idx) => { + self.nodes[idx] = node; + idx + } + None => { + self.nodes.push(node); + self.nodes.len() - 1 + } + } + } + + fn free(&mut self, node_idx: usize) { + for node in self.nodes[node_idx].0 { + if let Some(idx) = node { + self.free(idx) + } + } + self.free.push(node_idx); + } +} + +impl std::ops::Index for NodeArena { + type Output = Node; + fn index(&self, index: usize) -> &Node { + &self.nodes[index] + } +} + +impl std::ops::IndexMut for NodeArena { + fn index_mut(&mut self, index: usize) -> &mut Node { + &mut self.nodes[index] + } +} + struct Digits { digits: u64, remaining: u8, @@ -329,15 +418,15 @@ mod tests { } } - #[test] - fn test_mem_size() { - // Sanity check that `Option` behaves the same as - // `Option; 7]>>` in that it uses `NULL` to - // represent the `None` variant. - assert_eq!(size_of::>(), size_of::<*const ()>()); - assert_eq!( - size_of::>(), - size_of::; 7]>>>() - ); - } + // #[test] + // fn test_mem_size() { + // // Sanity check that `Option` behaves the same as + // // `Option; 7]>>` in that it uses `NULL` to + // // represent the `None` variant. + // assert_eq!(size_of::>(), size_of::<*const ()>()); + // assert_eq!( + // size_of::>(), + // size_of::; 7]>>>() + // ); + // } } From c9030a50e24ddd13fb706696c9fbe6a43a94d1c4 Mon Sep 17 00:00:00 2001 From: Jay Kickliter Date: Mon, 29 Aug 2022 11:55:23 -0600 Subject: [PATCH 3/4] Add with_capacity --- src/lib.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 86ec298..8da45e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,6 +84,17 @@ impl HexSet { } } + /// Constructs a new, empty `HexSet`. + /// + /// Incurs a single heap allocation to store all 122 resolution-0 + /// H3 cells. + pub fn with_capacity(cap: usize) -> Self { + Self { + root: vec![None; 122].into_boxed_slice(), + arena: NodeArena::with_capacity(cap), + } + } + /// Returns the number of H3 cells in the set. /// /// This method only considers complete, or leaf, hexagons in the @@ -315,6 +326,13 @@ impl NodeArena { } } + fn with_capacity(cap: usize) -> Self { + Self { + nodes: Vec::with_capacity(cap), + free: Vec::new(), + } + } + fn alloc(&mut self, node: Node) -> usize { match self.free.pop() { Some(idx) => { From 63726f84d669e375c71d158f4ec30e34fc99f90e Mon Sep 17 00:00:00 2001 From: Jay Kickliter Date: Mon, 29 Aug 2022 15:27:49 -0600 Subject: [PATCH 4/4] Add an into iterator placeholder --- src/lib.rs | 23 ++++++++--------------- tests/tests.rs | 5 ++++- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8da45e7..251ebf4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,7 +61,7 @@ use std::{ /// An efficient way to represent any portion(s) of Earth as a set of /// `H3` hexagons. -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone)] #[cfg_attr( feature = "serde-support", derive(serde::Serialize, serde::Deserialize) @@ -155,6 +155,11 @@ impl HexSet { } } + /// Returns an iterator over all cells in this set. + pub fn cells(&self) -> impl IntoIterator { + [H3Cell::new(577164439745200127)] + } + /// Returns the current memory use of this set. /// /// Note: The actual total may be higher than reported due to @@ -196,7 +201,7 @@ impl<'a> FromIterator<&'a H3Cell> for HexSet { } } -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone)] #[cfg_attr( feature = "serde-support", derive(serde::Serialize, serde::Deserialize) @@ -308,7 +313,7 @@ impl DerefMut for Node { } } -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone)] #[cfg_attr( feature = "serde-support", derive(serde::Serialize, serde::Deserialize) @@ -435,16 +440,4 @@ mod tests { assert_eq!(&&digits, ref_digits); } } - - // #[test] - // fn test_mem_size() { - // // Sanity check that `Option` behaves the same as - // // `Option; 7]>>` in that it uses `NULL` to - // // represent the `None` variant. - // assert_eq!(size_of::>(), size_of::<*const ()>()); - // assert_eq!( - // size_of::>(), - // size_of::; 7]>>>() - // ); - // } } diff --git a/tests/tests.rs b/tests/tests.rs index 0a4535a..79639f2 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -129,7 +129,10 @@ fn test_compaction() { from_indicies(regions::nocompact::US915); let gulf_of_mexico = H3Cell::from_coordinate(&coord! {x: -83.101920, y: 28.128096}, 0).unwrap(); assert_eq!(us915_tree.len(), us915_nocompact_tree.len()); - assert!(us915_tree == us915_nocompact_tree); + assert!(us915_tree + .cells() + .into_iter() + .eq(us915_nocompact_tree.cells().into_iter())); assert!(us915_nocompact_tree.len() < us915_nocompact_cells.len()); assert!(us915_nocompact_cells .iter()