From b5a254af7bb1ebcd04c08642f125008c7c47d297 Mon Sep 17 00:00:00 2001 From: Bodil Stokke Date: Sun, 2 Sep 2018 19:45:55 +0100 Subject: [PATCH] Wrap single chunks in a Ref, don't be so stack space hungry. --- CHANGELOG.md | 9 +++++++++ src/vector/focus.rs | 2 +- src/vector/mod.rs | 46 ++++++++++++++++++++++----------------------- 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed66a54..74ae35e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Changed +- Single chunk `Vector`s are no longer allocated directly on the stack, meaning + that they're now comparable in performance to `std::vec::Vec` rather than + slightly faster, but they also won't eat up your stack space quite as quickly, + and they'll clone without copying and share structure with clones as you'd + expect. + ## [12.0.0] - 2018-08-30 Starting with this release, the `arc` flag is gone, in favour of publishing `im` diff --git a/src/vector/focus.rs b/src/vector/focus.rs index 2e56d17..aa58542 100644 --- a/src/vector/focus.rs +++ b/src/vector/focus.rs @@ -464,7 +464,7 @@ where /// Construct a `FocusMut` for a `Vector`. pub fn new(vector: &'a mut Vector) -> Self { match vector { - Vector::Single(chunk) => FocusMut::Single(chunk.as_mut_slice()), + Vector::Single(chunk) => FocusMut::Single(Ref::make_mut(chunk).as_mut_slice()), Vector::Full(tree) => FocusMut::Full(TreeFocusMut::new(tree)), } } diff --git a/src/vector/mod.rs b/src/vector/mod.rs index 88d17e8..e5e4756 100644 --- a/src/vector/mod.rs +++ b/src/vector/mod.rs @@ -138,7 +138,7 @@ macro_rules! vector { /// [VecDeque]: https://doc.rust-lang.org/std/collections/struct.VecDeque.html pub enum Vector { #[doc(hidden)] - Single(Chunk), + Single(Ref>), #[doc(hidden)] Full(RRB), } @@ -180,11 +180,10 @@ impl Vector { /// Promote a single to a full, with the single chunk becomming inner_f. fn promote_front(&mut self) { - let chunk = Ref::new(match self { - // TODO can we do this safely without initialising a dummy chunk? - Single(chunk) => replace(chunk, Chunk::new()), + let chunk = match self { + Single(chunk) => chunk.clone(), _ => return, - }); + }; *self = Full(RRB { length: chunk.len(), middle_level: 0, @@ -198,11 +197,10 @@ impl Vector { /// Promote a single to a full, with the single chunk becomming inner_b. fn promote_back(&mut self) { - let chunk = Ref::new(match self { - // TODO can we do this safely without initialising a dummy chunk? - Single(chunk) => replace(chunk, Chunk::new()), + let chunk = match self { + Single(chunk) => chunk.clone(), _ => return, - }); + }; *self = Full(RRB { length: chunk.len(), middle_level: 0, @@ -217,13 +215,13 @@ impl Vector { /// Construct an empty vector. #[must_use] pub fn new() -> Self { - Single(Chunk::new()) + Single(Ref::new(Chunk::new())) } /// Construct a vector with a single value. #[must_use] pub fn singleton(a: A) -> Self { - Single(Chunk::unit(a)) + Single(Ref::new(Chunk::unit(a))) } /// Get the length of a vector. @@ -408,7 +406,7 @@ impl Vector { } match self { - Single(chunk) => chunk.get_mut(index), + Single(chunk) => Ref::make_mut(chunk).get_mut(index), Full(tree) => { let mut local_index = index; @@ -645,7 +643,7 @@ impl Vector { self.promote_back(); } match self { - Single(chunk) => chunk.push_front(value), + Single(chunk) => Ref::make_mut(chunk).push_front(value), Full(tree) => tree.push_front(value), } } @@ -670,7 +668,7 @@ impl Vector { self.promote_front(); } match self { - Single(chunk) => chunk.push_back(value), + Single(chunk) => Ref::make_mut(chunk).push_back(value), Full(tree) => tree.push_back(value), } } @@ -695,7 +693,7 @@ impl Vector { None } else { match self { - Single(chunk) => Some(chunk.pop_front()), + Single(chunk) => Some(Ref::make_mut(chunk).pop_front()), Full(tree) => tree.pop_front(), } } @@ -721,7 +719,7 @@ impl Vector { None } else { match self { - Single(chunk) => Some(chunk.pop_back()), + Single(chunk) => Some(Ref::make_mut(chunk).pop_back()), Full(tree) => tree.pop_back(), } } @@ -763,14 +761,14 @@ impl Vector { // If both are single chunks and left has room for right: directly // memcpy right into left Single(ref mut right) if total_length <= CHUNK_SIZE => { - left.append(right); + Ref::make_mut(left).append(Ref::make_mut(right)); return; } // If only left is a single chunk and has room for right: push // right's elements into left ref mut right if total_length <= CHUNK_SIZE => { while let Some(value) = right.pop_front() { - left.push_back(value); + Ref::make_mut(left).push_back(value); } return; } @@ -923,7 +921,7 @@ impl Vector { assert!(index <= self.len()); match self { - Single(chunk) => Single(chunk.split_off(index)), + Single(chunk) => Single(Ref::new(Ref::make_mut(chunk).split_off(index))), Full(tree) => { let mut local_index = index; @@ -1029,7 +1027,7 @@ impl Vector { let ob2 = Ref::make_mut(&mut tree.outer_b).split_off(local_index); tree.length = index; - Single(ob2) + Single(Ref::new(ob2)) } } } @@ -1115,7 +1113,7 @@ impl Vector { } assert!(index < self.len()); match self { - Single(chunk) if chunk.len() < CHUNK_SIZE => chunk.insert(index, value), + Single(chunk) if chunk.len() < CHUNK_SIZE => Ref::make_mut(chunk).insert(index, value), // TODO a lot of optimisations still possible here _ => { let right = self.split_off(index); @@ -1144,7 +1142,7 @@ impl Vector { pub fn remove(&mut self, index: usize) -> A { assert!(index < self.len()); match self { - Single(chunk) => chunk.remove(index), + Single(chunk) => Ref::make_mut(chunk).remove(index), _ => { if index == 0 { return self.pop_front().unwrap(); @@ -1169,7 +1167,7 @@ impl Vector { /// Time: O(n) pub fn clear(&mut self) { if !self.is_empty() { - *self = Single(Chunk::new()); + *self = Single(Ref::new(Chunk::new())); } } @@ -1897,7 +1895,7 @@ pub enum ConsumingIter { impl ConsumingIter { fn new(seq: Vector) -> Self { match seq { - Single(chunk) => ConsumingIter::Single(chunk.into_iter()), + Single(chunk) => ConsumingIter::Single(clone_ref(chunk).into_iter()), Full(tree) => ConsumingIter::Full(tree.into_iter()), } }