From 1bfcd35aed8585d7a3532e11888eaee939f8358c Mon Sep 17 00:00:00 2001 From: AhoyISki Date: Thu, 26 Jun 2025 18:51:14 -0300 Subject: [PATCH 01/15] const. --- src/gap_buffer.rs | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/gap_buffer.rs b/src/gap_buffer.rs index 92fae72..5baa470 100644 --- a/src/gap_buffer.rs +++ b/src/gap_buffer.rs @@ -85,7 +85,7 @@ impl GapBuffer { /// buf.push_back(5); /// ``` #[inline] - pub fn new() -> Self { + pub const fn new() -> Self { GapBuffer(RawGapBuffer::new()) } @@ -116,8 +116,8 @@ impl GapBuffer { /// assert_eq!(buf.capacity(), 10); /// ``` #[inline] - pub fn capacity(&self) -> usize { - self.cap + pub const fn capacity(&self) -> usize { + self.0.0.cap } /// Reserves capacity for at least additional more elements to be inserted in the given `GapBuffer`. @@ -239,8 +239,8 @@ impl GapBuffer { /// Return gap offset of the `GapBuffer`. #[inline] - pub fn gap(&self) -> usize { - self.gap + pub const fn gap(&self) -> usize { + self.0.0.gap } #[inline] @@ -689,7 +689,7 @@ impl<'a, T: 'a + Copy> Extend<&'a T> for GapBuffer { struct RawGapBuffer(Slice); impl RawGapBuffer { - fn new() -> Self { + const fn new() -> Self { RawGapBuffer(Slice::empty()) } @@ -718,6 +718,7 @@ impl RawGapBuffer { } self.0.cap = new_cap; } + fn get_layout(cap: usize) -> Layout { let new_size = size_of::() .checked_mul(cap) @@ -754,7 +755,7 @@ pub struct RangeMut<'a, T: 'a> { } impl<'a, T: 'a> Range<'a, T> { #[inline] - unsafe fn new(s: Slice) -> Self { + const unsafe fn new(s: Slice) -> Self { Range { s, _phantom: PhantomData, @@ -763,7 +764,7 @@ impl<'a, T: 'a> Range<'a, T> { /// Construct a new, empty `Range`. #[inline] - pub fn empty() -> Self { + pub const fn empty() -> Self { unsafe { Range::new(Slice::empty()) } } @@ -792,7 +793,7 @@ impl<'a, T: 'a> Range<'a, T> { } impl<'a, T: 'a> RangeMut<'a, T> { #[inline] - unsafe fn new(s: Slice) -> Self { + const unsafe fn new(s: Slice) -> Self { RangeMut { s, _phantom: PhantomData, @@ -801,7 +802,7 @@ impl<'a, T: 'a> RangeMut<'a, T> { /// Construct a new, empty `RangeMut`. #[inline] - pub fn empty() -> Self { + pub const fn empty() -> Self { unsafe { RangeMut::new(Slice::empty()) } } } @@ -856,7 +857,7 @@ pub struct Slice { } impl Slice { /// Construct a new, empty `Slice`. - pub fn empty() -> Self { + pub const fn empty() -> Self { Slice { ptr: NonNull::dangling(), cap: 0, @@ -867,13 +868,13 @@ impl Slice { /// Returns the number of elements in the GapBuffer. #[inline] - pub fn len(&self) -> usize { + pub const fn len(&self) -> usize { self.len } /// Returns true if the GapBuffer contains no elements. #[inline] - pub fn is_empty(&self) -> bool { + pub const fn is_empty(&self) -> bool { self.len() == 0 } @@ -904,7 +905,7 @@ impl Slice { /// # Panics /// Panics if `a >= self.len()` or `b >= self.len()`. #[inline] - pub fn swap(&mut self, a: usize, b: usize) { + pub const fn swap(&mut self, a: usize, b: usize) { let oa = self.get_offset(a).expect("a is out of bounds."); let ob = self.get_offset(b).expect("b is out of bounds."); let p = self.as_mut_ptr(); @@ -1027,7 +1028,7 @@ impl Slice { pub fn as_slices(&self) -> (&[T], &[T]) { unsafe { self.as_slices_with_lifetime() } } - unsafe fn as_slices_with_lifetime<'a>(&self) -> (&'a [T], &'a [T]) { + const unsafe fn as_slices_with_lifetime<'a>(&self) -> (&'a [T], &'a [T]) { let p0 = self.as_ptr(); let c1 = self.len - self.gap; let p1 = p0.add(self.cap - c1); @@ -1053,7 +1054,7 @@ impl Slice { /// } /// assert_eq!(buf, [10, 2, 11, 4, 5]); /// ``` - pub fn as_mut_slices(&mut self) -> (&mut [T], &mut [T]) { + pub const fn as_mut_slices(&mut self) -> (&mut [T], &mut [T]) { unsafe { let p0 = self.as_mut_ptr(); let c1 = self.len - self.gap; @@ -1078,7 +1079,7 @@ impl Slice { } #[inline] - fn get_offset(&self, index: usize) -> Option { + const fn get_offset(&self, index: usize) -> Option { if index < self.gap { Some(index) } else if index < self.len { @@ -1089,17 +1090,17 @@ impl Slice { } #[inline] - fn gap_len(&self) -> usize { + const fn gap_len(&self) -> usize { self.cap - self.len } #[inline] - fn as_ptr(&self) -> *const T { + const fn as_ptr(&self) -> *const T { self.ptr.as_ptr() } #[inline] - fn as_mut_ptr(&mut self) -> *mut T { + const fn as_mut_ptr(&mut self) -> *mut T { self.ptr.as_ptr() } } From 305927deb328f70cabe5755b9aa858bd6bb91718 Mon Sep 17 00:00:00 2001 From: AhoyISki Date: Tue, 27 Jan 2026 15:17:21 -0300 Subject: [PATCH 02/15] Removed empty dependencies table. --- Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index efad701..1dd2aad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,6 @@ edition = "2021" [features] docs-rs = [] -[dependencies] - [dev-dependencies] test-strategy = "0.2.0" proptest = "1.0.0" From 2727202e69c66e6731b0334ceb671c9e75754c7a Mon Sep 17 00:00:00 2001 From: AhoyISki Date: Tue, 27 Jan 2026 15:47:15 -0300 Subject: [PATCH 03/15] Added extract_if. --- src/gap_buffer.rs | 182 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 129 insertions(+), 53 deletions(-) diff --git a/src/gap_buffer.rs b/src/gap_buffer.rs index 94f29cb..6f53087 100644 --- a/src/gap_buffer.rs +++ b/src/gap_buffer.rs @@ -524,6 +524,41 @@ impl GapBuffer { } } + /// Creates an extracting iterator that removes elements from the specified range in the GapBuffer based on a predicate + /// + /// Note that this iterator will only remove elements that are consumed. If dropped, it will retain the remaining elements. + /// + /// # Panics + /// + /// Panics if the `range` is out of bounds. + /// + /// # Examples + /// + /// ``` + /// use gapbuf::gap_buffer; + /// + /// let mut buf = gap_buffer![1, 2, 3, 4]; + /// + /// let d : Vec<_> = buf.extract_if(.., |num| *num % 2 == 1).collect(); + /// assert_eq!(buf, [2, 4]); + /// assert_eq!(d, [1, 3]); + /// + /// buf.extract_if(.., |_| true); + /// assert_eq!(buf.is_empty(), false); + /// ``` + pub fn extract_if(&mut self, range: impl RangeBounds, filter: F) -> ExtractIf<'_, T, F> + where + F: FnMut(&mut T) -> bool + { + let (idx, end) = self.to_idx_len(range); + ExtractIf { + buf: self, + idx, + end, + pred: filter + } + } + /// Creates a splicing iterator /// that replaces the specified range in the GapBuffer with the given replace_with iterator and /// yields the removed items. @@ -563,13 +598,13 @@ impl GapBuffer { /// A splicing iterator for [`GapBuffer`]. /// /// This struct is created by [`GapBuffer::splice`]. -pub struct Splice<'a, T: 'a, I: Iterator> { - buf: &'a mut GapBuffer, +pub struct Splice<'gb, T: 'gb, I: Iterator> { + buf: &'gb mut GapBuffer, idx: usize, end: usize, iter: Fuse, } -impl<'a, T: 'a, I: Iterator> Iterator for Splice<'a, T, I> { +impl<'gb, T: 'gb, I: Iterator> Iterator for Splice<'gb, T, I> { type Item = T; fn next(&mut self) -> Option { @@ -592,15 +627,15 @@ impl<'a, T: 'a, I: Iterator> Iterator for Splice<'a, T, I> { (size, Some(size)) } } -impl<'a, T: 'a, I: Iterator> Drop for Splice<'a, T, I> { +impl<'gb, T: 'gb, I: Iterator> Drop for Splice<'gb, T, I> { fn drop(&mut self) { while self.next().is_some() {} self.buf.insert_many(self.idx, &mut self.iter); } } -impl<'a, T: 'a, I: Iterator> ExactSizeIterator for Splice<'a, T, I> {} -impl<'a, T: 'a, I: Iterator> FusedIterator for Splice<'a, T, I> {} -impl<'a, T: 'a, I: DoubleEndedIterator> DoubleEndedIterator for Splice<'a, T, I> { +impl<'gb, T: 'gb, I: Iterator> ExactSizeIterator for Splice<'gb, T, I> {} +impl<'gb, T: 'gb, I: Iterator> FusedIterator for Splice<'gb, T, I> {} +impl<'gb, T: 'gb, I: DoubleEndedIterator> DoubleEndedIterator for Splice<'gb, T, I> { fn next_back(&mut self) -> Option { if self.idx < self.end { let i = self.end - 1; @@ -679,8 +714,8 @@ impl Extend for GapBuffer { self.insert_many(len, iter); } } -impl<'a, T: 'a + Copy> Extend<&'a T> for GapBuffer { - fn extend>(&mut self, iter: I) { +impl<'gb, T: 'gb + Copy> Extend<&'gb T> for GapBuffer { + fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().cloned()); } } @@ -740,20 +775,20 @@ impl Drop for RawGapBuffer { /// /// This struct is created by [`Slice::range`]. #[derive(Hash)] -pub struct Range<'a, T: 'a> { +pub struct Range<'gb, T: 'gb> { s: Slice, - _phantom: PhantomData<&'a [T]>, + _phantom: PhantomData<&'gb [T]>, } /// Mutable sub-range of [`GapBuffer`]. /// /// This struct is created by [`Slice::range_mut`]. #[derive(Hash)] -pub struct RangeMut<'a, T: 'a> { +pub struct RangeMut<'gb, T: 'gb> { s: Slice, - _phantom: PhantomData<&'a mut [T]>, + _phantom: PhantomData<&'gb mut [T]>, } -impl<'a, T: 'a> Range<'a, T> { +impl<'gb, T: 'gb> Range<'gb, T> { #[inline] const unsafe fn new(s: Slice) -> Self { Range { @@ -772,14 +807,14 @@ impl<'a, T: 'a> Range<'a, T> { /// /// Unlike [`Slice::get`], return value not borrow `self`. #[inline] - pub fn get(&self, index: usize) -> Option<&'a T> { + pub fn get(&self, index: usize) -> Option<&'gb T> { unsafe { self.s.get_with_lifetime(index) } } /// Return a immutable sub-range of this Slice. /// /// Unlike [`Slice::range`], return value not borrow `self`. - pub fn range(&self, range: impl RangeBounds) -> Range<'a, T> { + pub fn range(&self, range: impl RangeBounds) -> Range<'gb, T> { unsafe { self.range_with_lifetime(range) } } @@ -787,11 +822,11 @@ impl<'a, T: 'a> Range<'a, T> { /// First slice is before gap. Second slice is after gap. /// /// Unlike [`Slice::as_slices`], return value not borrow `self`. - pub fn as_slices(&self) -> (&'a [T], &'a [T]) { + pub fn as_slices(&self) -> (&'gb [T], &'gb [T]) { unsafe { self.as_slices_with_lifetime() } } } -impl<'a, T: 'a> RangeMut<'a, T> { +impl<'gb, T: 'gb> RangeMut<'gb, T> { #[inline] const unsafe fn new(s: Slice) -> Self { RangeMut { @@ -884,7 +919,7 @@ impl Slice { unsafe { self.get_with_lifetime(index) } } #[inline] - unsafe fn get_with_lifetime<'a>(&self, index: usize) -> Option<&'a T> { + unsafe fn get_with_lifetime<'gb>(&self, index: usize) -> Option<&'gb T> { self.get_offset(index).map(|o| &*self.as_ptr().add(o)) } @@ -932,7 +967,7 @@ impl Slice { pub fn range(&self, range: impl RangeBounds) -> Range<'_, T> { unsafe { self.range_with_lifetime(range) } } - unsafe fn range_with_lifetime<'a>(&self, range: impl RangeBounds) -> Range<'a, T> { + unsafe fn range_with_lifetime<'gb>(&self, range: impl RangeBounds) -> Range<'gb, T> { Range::new(self.range_slice(range)) } @@ -1028,7 +1063,7 @@ impl Slice { pub fn as_slices(&self) -> (&[T], &[T]) { unsafe { self.as_slices_with_lifetime() } } - const unsafe fn as_slices_with_lifetime<'a>(&self) -> (&'a [T], &'a [T]) { + const unsafe fn as_slices_with_lifetime<'gb>(&self) -> (&'gb [T], &'gb [T]) { let p0 = self.as_ptr(); let c1 = self.len - self.gap; let p1 = p0.add(self.cap - c1); @@ -1362,10 +1397,10 @@ impl Ord for Slice { //////////////////////////////////////////////////////////////////////////////// /// Immutable GapBuffer iterator. -pub type Iter<'a, T> = Chain, slice::Iter<'a, T>>; +pub type Iter<'gb, T> = Chain, slice::Iter<'gb, T>>; /// Mutable GapBuffer iterator. -pub type IterMut<'a, T> = Chain, slice::IterMut<'a, T>>; +pub type IterMut<'gb, T> = Chain, slice::IterMut<'gb, T>>; /// An iterator that moves out of a [`GapBuffer`]. pub struct IntoIter { @@ -1398,54 +1433,56 @@ impl IntoIterator for GapBuffer { } } -impl<'a, T> IntoIterator for &'a GapBuffer { - type Item = &'a T; - type IntoIter = Iter<'a, T>; - fn into_iter(self) -> Iter<'a, T> { +impl<'gb, T> IntoIterator for &'gb GapBuffer { + type Item = &'gb T; + type IntoIter = Iter<'gb, T>; + fn into_iter(self) -> Iter<'gb, T> { self.iter() } } -impl<'a, T> IntoIterator for &'a Range<'_, T> { - type Item = &'a T; - type IntoIter = Iter<'a, T>; - fn into_iter(self) -> Iter<'a, T> { +impl<'gb, T> IntoIterator for &'gb Range<'_, T> { + type Item = &'gb T; + type IntoIter = Iter<'gb, T>; + fn into_iter(self) -> Iter<'gb, T> { self.iter() } } -impl<'a, T> IntoIterator for &'a RangeMut<'_, T> { - type Item = &'a T; - type IntoIter = Iter<'a, T>; - fn into_iter(self) -> Iter<'a, T> { +impl<'gb, T> IntoIterator for &'gb RangeMut<'_, T> { + type Item = &'gb T; + type IntoIter = Iter<'gb, T>; + fn into_iter(self) -> Iter<'gb, T> { self.iter() } } -impl<'a, T> IntoIterator for &'a Slice { - type Item = &'a T; - type IntoIter = Iter<'a, T>; - fn into_iter(self) -> Iter<'a, T> { +impl<'gb, T> IntoIterator for &'gb Slice { + type Item = &'gb T; + type IntoIter = Iter<'gb, T>; + fn into_iter(self) -> Iter<'gb, T> { self.iter() } } -impl<'a, T> IntoIterator for &'a mut GapBuffer { - type Item = &'a mut T; - type IntoIter = IterMut<'a, T>; - fn into_iter(self) -> IterMut<'a, T> { +impl<'gb, T> IntoIterator for &'gb mut GapBuffer { + type Item = &'gb mut T; + type IntoIter = IterMut<'gb, T>; + fn into_iter(self) -> IterMut<'gb, T> { self.iter_mut() } } -impl<'a, T> IntoIterator for &'a mut RangeMut<'a, T> { - type Item = &'a mut T; - type IntoIter = IterMut<'a, T>; - fn into_iter(self) -> IterMut<'a, T> { + +impl<'gb, T> IntoIterator for &'gb mut RangeMut<'gb, T> { + type Item = &'gb mut T; + type IntoIter = IterMut<'gb, T>; + fn into_iter(self) -> IterMut<'gb, T> { self.iter_mut() } } -impl<'a, T> IntoIterator for &'a mut Slice { - type Item = &'a mut T; - type IntoIter = IterMut<'a, T>; - fn into_iter(self) -> IterMut<'a, T> { + +impl<'gb, T> IntoIterator for &'gb mut Slice { + type Item = &'gb mut T; + type IntoIter = IterMut<'gb, T>; + fn into_iter(self) -> IterMut<'gb, T> { self.iter_mut() } } @@ -1453,11 +1490,12 @@ impl<'a, T> IntoIterator for &'a mut Slice { /// A draining iterator for [`GapBuffer`]. /// /// This struct is created by [`GapBuffer::drain`]. -pub struct Drain<'a, T: 'a> { - buf: &'a mut GapBuffer, +pub struct Drain<'gb, T: 'gb> { + buf: &'gb mut GapBuffer, idx: usize, len: usize, } + impl Iterator for Drain<'_, T> { type Item = T; fn next(&mut self) -> Option { @@ -1472,6 +1510,7 @@ impl Iterator for Drain<'_, T> { (self.len, Some(self.len)) } } + impl Drop for Drain<'_, T> { fn drop(&mut self) { let len = self.len; @@ -1481,3 +1520,40 @@ impl Drop for Drain<'_, T> { impl ExactSizeIterator for Drain<'_, T> {} impl FusedIterator for Drain<'_, T> {} + +/// An iterator that conditionally extracts from a [`GapBuffer`]. +/// +/// this struct is created by [`GapBuffer::extract_if`]. +#[must_use] +pub struct ExtractIf<'gb, T: 'gb, F> { + buf: &'gb mut GapBuffer, + idx: usize, + end: usize, + pred: F, +} + +impl Iterator for ExtractIf<'_, T, F> +where + F: FnMut(&mut T) -> bool +{ + type Item = T; + + fn next(&mut self) -> Option { + while self.idx < self.end { + if (self.pred)(self.buf.get_mut(self.idx).unwrap()) { + self.end -= 1; + return Some(self.buf.remove(self.idx)); + } else { + self.idx += 1; + } + } + + None + } +} + +impl FusedIterator for ExtractIf<'_, T, F> +where + F: FnMut(&mut T) -> bool +{ +} From 673887c47ef6ce1da97fe50700836f92a91feb6a Mon Sep 17 00:00:00 2001 From: AhoyISki Date: Tue, 27 Jan 2026 15:51:59 -0300 Subject: [PATCH 04/15] Renamed the crate. --- Cargo.toml | 12 ++++++------ README.md | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1dd2aad..7c9e3c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,15 +1,15 @@ [package] -name = "gapbuf" -version = "0.1.4" -authors = ["FrozenLib"] +name = "gapbuffer" +version = "0.1.0" +authors = ["FrozenLib", "AhoyISki"] license = "MIT OR Apache-2.0" readme = "README.md" -repository = "https://github.com/frozenlib/gapbuf-rs" -documentation = "https://docs.rs/gapbuf/" +repository = "https://github.com/AhoyISki/gapbuffer-rs" +documentation = "https://docs.rs/gapbuffer/" keywords = ["gap", "gapbuffer"] categories = ["data-structures"] description = "Generic gap buffer." -edition = "2021" +edition = "2024" [features] docs-rs = [] diff --git a/README.md b/README.md index 77f5ab2..e7dc2f4 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# gapbuf-rs +# gapbuffer-rs -[![Crates.io](https://img.shields.io/crates/v/gapbuf.svg)](https://crates.io/crates/gapbuf) -[![Docs.rs](https://docs.rs/gapbuf/badge.svg)](https://docs.rs/gapbuf) -[![Actions Status](https://github.com/frozenlib/gapbuf-rs/workflows/CI/badge.svg)](https://github.com/frozenlib/gapbuf-rs/actions) +[![Crates.io](https://img.shields.io/crates/v/gapbuffer.svg)](https://crates.io/crates/gapbuf) +[![Docs.rs](https://docs.rs/gapbuf/badge.svg)](https://docs.rs/gapbuffer) +[![Actions Status](https://github.com/AhoyISki/gapbuffer-rs/workflows/CI/badge.svg)](https://github.com/AhoyISki/gapbuffer-rs/actions) Generic gap buffer implementation in Rust. From 6dd4a5ecc0749cf6412c90d21af800df556465e3 Mon Sep 17 00:00:00 2001 From: AhoyISki Date: Tue, 27 Jan 2026 16:01:35 -0300 Subject: [PATCH 05/15] Renamed the crate again. --- Cargo.toml | 6 +++--- README.md | 10 +++++----- examples/simple.rs | 2 +- src/gap_buffer.rs | 42 ++++++++++++++++++++--------------------- src/lib.rs | 6 +++--- tests/tests.rs | 18 +++++++++--------- tests/tests_proptest.rs | 2 +- 7 files changed, 42 insertions(+), 44 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7c9e3c1..0799356 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [package] -name = "gapbuffer" +name = "gap-buf" version = "0.1.0" authors = ["FrozenLib", "AhoyISki"] license = "MIT OR Apache-2.0" readme = "README.md" -repository = "https://github.com/AhoyISki/gapbuffer-rs" -documentation = "https://docs.rs/gapbuffer/" +repository = "https://github.com/AhoyISki/gap-buf-rs" +documentation = "https://docs.rs/gap-buf/" keywords = ["gap", "gapbuffer"] categories = ["data-structures"] description = "Generic gap buffer." diff --git a/README.md b/README.md index e7dc2f4..48b72c3 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# gapbuffer-rs +# gap-buf-rs -[![Crates.io](https://img.shields.io/crates/v/gapbuffer.svg)](https://crates.io/crates/gapbuf) -[![Docs.rs](https://docs.rs/gapbuf/badge.svg)](https://docs.rs/gapbuffer) -[![Actions Status](https://github.com/AhoyISki/gapbuffer-rs/workflows/CI/badge.svg)](https://github.com/AhoyISki/gapbuffer-rs/actions) +[![Crates.io](https://img.shields.io/crates/v/gap-buf.svg)](https://crates.io/crates/gap-buf) +[![Docs.rs](https://docs.rs/gapbuf/badge.svg)](https://docs.rs/gap-buf) +[![Actions Status](https://github.com/AhoyISki/gap-buf-rs/workflows/CI/badge.svg)](https://github.com/AhoyISki/gap-buf-rs/actions) Generic gap buffer implementation in Rust. @@ -12,7 +12,7 @@ This type has methods similar to `Vec`. ## Examples ```rust -use gapbuf::gap_buffer; +use gap_buf::gap_buffer; let mut b = gap_buffer![1, 2, 3]; b.insert(1, 10); diff --git a/examples/simple.rs b/examples/simple.rs index 2031195..da5aa9c 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -1,4 +1,4 @@ -use gapbuf::gap_buffer; +use gap_buf::gap_buffer; fn main() { let mut b = gap_buffer![1, 2, 3]; diff --git a/src/gap_buffer.rs b/src/gap_buffer.rs index 6f53087..a2e6bb3 100644 --- a/src/gap_buffer.rs +++ b/src/gap_buffer.rs @@ -17,7 +17,7 @@ use std::slice; /// - Create a [`GapBuffer`] containing a given list of elements: /// /// ``` -/// use gapbuf::gap_buffer; +/// use gap_buf::gap_buffer; /// let b = gap_buffer![1, 2, 3]; /// assert_eq!(b.len(), 3); /// assert_eq!(b[0], 1); @@ -28,14 +28,12 @@ use std::slice; /// - Create a [`GapBuffer`] from a given element and size: /// /// ``` -/// use gapbuf::gap_buffer; +/// use gap_buf::gap_buffer; /// let b = gap_buffer!["abc"; 2]; /// assert_eq!(b.len(), 2); /// assert_eq!(b[0], "abc"); /// assert_eq!(b[1], "abc"); /// ``` -/// -/// [`GapBuffer`]: ../gapbuf/struct.GapBuffer.html #[macro_export] macro_rules! gap_buffer { ($elem:expr; $n:expr) => ( @@ -70,7 +68,7 @@ impl GapBuffer { /// /// # Examples /// ``` - /// # use gapbuf::GapBuffer; + /// # use gap_buf::GapBuffer; /// let mut buf = GapBuffer::::new(); /// /// assert_eq!(buf.is_empty(), true); @@ -79,7 +77,7 @@ impl GapBuffer { /// ``` /// /// ``` - /// use gapbuf::GapBuffer; + /// use gap_buf::GapBuffer; /// /// let mut buf = GapBuffer::new(); /// buf.push_back(5); @@ -93,7 +91,7 @@ impl GapBuffer { /// /// # Examples /// ``` - /// use gapbuf::GapBuffer; + /// use gap_buf::GapBuffer; /// /// let buf: GapBuffer = GapBuffer::with_capacity(5); /// assert_eq!(buf.is_empty(), true); @@ -110,7 +108,7 @@ impl GapBuffer { /// /// # Examples /// ``` - /// use gapbuf::GapBuffer; + /// use gap_buf::GapBuffer; /// /// let buf: GapBuffer = GapBuffer::with_capacity(10); /// assert_eq!(buf.capacity(), 10); @@ -130,7 +128,7 @@ impl GapBuffer { /// /// # Examples /// ``` - /// use gapbuf::GapBuffer; + /// use gap_buf::GapBuffer; /// /// let mut buf = GapBuffer::new(); /// buf.push_back(1); @@ -154,7 +152,7 @@ impl GapBuffer { /// /// # Examples /// ``` - /// use gapbuf::GapBuffer; + /// use gap_buf::GapBuffer; /// /// let mut buf = GapBuffer::new(); /// buf.push_back(1); @@ -191,7 +189,7 @@ impl GapBuffer { /// /// # Examples /// ``` - /// use gapbuf::GapBuffer; + /// use gap_buf::GapBuffer; /// /// let mut buf = GapBuffer::new(); /// buf.push_back(1); @@ -350,7 +348,7 @@ impl GapBuffer { /// /// # Examples /// ``` - /// use gapbuf::gap_buffer; + /// use gap_buf::gap_buffer; /// /// let mut buf = gap_buffer![1, 2, 3, 4, 5]; /// buf.set_gap(5); @@ -390,7 +388,7 @@ impl GapBuffer { /// /// # Examples /// ``` - /// use gapbuf::gap_buffer; + /// use gap_buf::gap_buffer; /// /// let mut buf = gap_buffer![1, 2, 3, 4, 5]; /// let value = buf.remove(0); @@ -431,7 +429,7 @@ impl GapBuffer { /// # Examples /// /// ``` - /// use gapbuf::gap_buffer; + /// use gap_buf::gap_buffer; /// /// let mut buf = gap_buffer![1, 2, 3, 4]; /// buf.truncate(2); @@ -458,7 +456,7 @@ impl GapBuffer { /// # Examples /// /// ``` - /// use gapbuf::gap_buffer; + /// use gap_buf::gap_buffer; /// /// let mut buf = gap_buffer![1, 2, 3, 4]; /// buf.retain(|&x| x%2 == 0); @@ -504,7 +502,7 @@ impl GapBuffer { /// # Examples /// /// ``` - /// use gapbuf::gap_buffer; + /// use gap_buf::gap_buffer; /// /// let mut buf = gap_buffer![1, 2, 3, 4]; /// @@ -535,7 +533,7 @@ impl GapBuffer { /// # Examples /// /// ``` - /// use gapbuf::gap_buffer; + /// use gap_buf::gap_buffer; /// /// let mut buf = gap_buffer![1, 2, 3, 4]; /// @@ -572,7 +570,7 @@ impl GapBuffer { /// # Examples /// /// ``` - /// use gapbuf::gap_buffer; + /// use gap_buf::gap_buffer; /// /// let mut b = gap_buffer![1, 2, 3, 4]; /// let r : Vec<_> = b.splice(1..3, vec![7, 8, 9]).collect(); @@ -954,7 +952,7 @@ impl Slice { /// /// # Examples /// ``` - /// use gapbuf::gap_buffer; + /// use gap_buf::gap_buffer; /// /// let buf = gap_buffer![1, 2, 3, 4, 5]; /// @@ -978,7 +976,7 @@ impl Slice { /// /// # Examples /// ``` - /// use gapbuf::gap_buffer; + /// use gap_buf::gap_buffer; /// /// let mut buf = gap_buffer![1, 2, 3, 4, 5]; /// { @@ -1052,7 +1050,7 @@ impl Slice { /// /// # Examples /// ``` - /// use gapbuf::gap_buffer; + /// use gap_buf::gap_buffer; /// /// let mut buf = gap_buffer![1, 2, 3, 4, 5]; /// buf.set_gap(2); @@ -1078,7 +1076,7 @@ impl Slice { /// /// # Examples /// ``` - /// use gapbuf::gap_buffer; + /// use gap_buf::gap_buffer; /// /// let mut buf = gap_buffer![1, 2, 3, 4, 5]; /// buf.set_gap(2); diff --git a/src/lib.rs b/src/lib.rs index 6343d90..3b3dcee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,10 @@ -//! `gapbuf` provides the type [`GapBuffer`]. +//! `gap_buf` provides the type [`GapBuffer`]. //! `GapBuffer` has methods similar to [`Vec`](std::vec::Vec). //! //! # Examples //! //! ``` -//! use gapbuf::gap_buffer; +//! use gap_buf::gap_buffer; //! //! let mut b = gap_buffer![1, 2, 3]; //! @@ -15,7 +15,7 @@ //! assert_eq!(b, [1, 10, 3]); //! ``` //! -#![doc(html_root_url = "https://docs.rs/gapbuf/0.1.2")] +#![doc(html_root_url = "https://docs.rs/gap_buf/0.1.2")] #[macro_use] mod finally; diff --git a/tests/tests.rs b/tests/tests.rs index 5f7aa1d..b66e4c0 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,4 +1,4 @@ -use gapbuf::{gap_buffer, GapBuffer}; +use gap_buf::{gap_buffer, GapBuffer}; use std::cell::RefCell; use std::collections::HashSet; use std::panic; @@ -700,7 +700,7 @@ fn eq_slice2() { } #[test] -fn eq_gapbuf1() { +fn eq_gap_buf1() { let mut buf = GapBuffer::new(); buf.push_back(1); @@ -708,7 +708,7 @@ fn eq_gapbuf1() { } #[test] -fn eq_gapbuf2() { +fn eq_gap_buf2() { let mut buf = GapBuffer::new(); buf.push_back(2); buf.push_back(8); @@ -838,23 +838,23 @@ fn covariant() { let s = String::from("bbb"); // `&GapBuffer<&static str>` can convert `&GapBuffer<&a>` - fn c_gapbuf<'a>(_buf: &GapBuffer<&'a str>, _s: &'a str) {} - c_gapbuf(&b, &s); + fn c_gap_buf<'a>(_buf: &GapBuffer<&'a str>, _s: &'a str) {} + c_gap_buf(&b, &s); // `Range<'b, &'static str>` can convert `Range<'b, &'a str>` - fn c_range<'a>(_buf: gapbuf::Range<&'a str>, _s: &'a str) {} + fn c_range<'a>(_buf: gap_buf::Range<&'a str>, _s: &'a str) {} c_range(b.range(0..1), &s); // `Range<'b, &'static str>` can not convert `Range<'b, &'a str>` - // fn c_range_mut<'a, 'b>(_buf: gapbuf::RangeMut<'b, &'a str>, _s: &'a str) {} + // fn c_range_mut<'a, 'b>(_buf: gap_buf::RangeMut<'b, &'a str>, _s: &'a str) {} // c_range_mut(b.range_mut(0..1), &s); // `&Slice<&static str>` can convert `&Slice<&a>` - fn c_slice<'a>(_buf: &gapbuf::Slice<&'a str>, _s: &'a str) {} + fn c_slice<'a>(_buf: &gap_buf::Slice<&'a str>, _s: &'a str) {} c_slice(&b, &s); // `&mut Slice<&static str>` can not convert `&mut Slice<&a>` - // fn c_mut_slice<'a>(_buf: &mut gapbuf::Slice<&'a str>, _s: &'a str) {} + // fn c_mut_slice<'a>(_buf: &mut gap_buf::Slice<&'a str>, _s: &'a str) {} // c_mut_slice(&mut b, &s); } diff --git a/tests/tests_proptest.rs b/tests/tests_proptest.rs index e2dba51..0b21670 100644 --- a/tests/tests_proptest.rs +++ b/tests/tests_proptest.rs @@ -1,4 +1,4 @@ -use gapbuf::GapBuffer; +use gap_buf::GapBuffer; use proptest::{collection::vec, prelude::*}; use test_strategy::{proptest, Arbitrary}; From 5138625b32029e139179194ff3fbc7935bbfabfd Mon Sep 17 00:00:00 2001 From: AhoyISki Date: Tue, 27 Jan 2026 16:04:48 -0300 Subject: [PATCH 06/15] Finished renaming. --- benchmarks/Cargo.toml | 2 +- benchmarks/benches/benches.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/Cargo.toml b/benchmarks/Cargo.toml index 194ba99..c484bb4 100644 --- a/benchmarks/Cargo.toml +++ b/benchmarks/Cargo.toml @@ -7,4 +7,4 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -gapbuf = { path = "../" } +gap_buf = { path = "../" } diff --git a/benchmarks/benches/benches.rs b/benchmarks/benches/benches.rs index f254047..649ffbb 100644 --- a/benchmarks/benches/benches.rs +++ b/benchmarks/benches/benches.rs @@ -3,7 +3,7 @@ extern crate test; use self::test::Bencher; -use gapbuf::GapBuffer; +use gap_buf::GapBuffer; use std::collections::VecDeque; #[bench] From 358e06ed79dd696778db86179d59fb2d1ed2f85e Mon Sep 17 00:00:00 2001 From: AhoyISki Date: Fri, 27 Feb 2026 19:24:09 -0300 Subject: [PATCH 07/15] Added bincode support and made conversions from Vec O(1). --- Cargo.toml | 10 +++- src/gap_buffer.rs | 149 +++++++++++++++++++++++++++++++++++----------- 2 files changed, 122 insertions(+), 37 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0799356..b360d10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gap-buf" -version = "0.1.0" +version = "0.2.0" authors = ["FrozenLib", "AhoyISki"] license = "MIT OR Apache-2.0" readme = "README.md" @@ -11,12 +11,16 @@ categories = ["data-structures"] description = "Generic gap buffer." edition = "2024" -[features] -docs-rs = [] +[dependencies] +bincode = { version = "2.0.1", optional = true } [dev-dependencies] test-strategy = "0.2.0" proptest = "1.0.0" +[features] +docs-rs = [] +bincode = ["dep:bincode"] + [package.metadata.docs.rs] features = ["docs-rs"] diff --git a/src/gap_buffer.rs b/src/gap_buffer.rs index a2e6bb3..1bc4e38 100644 --- a/src/gap_buffer.rs +++ b/src/gap_buffer.rs @@ -1,12 +1,12 @@ -use std::alloc::{alloc, dealloc, handle_alloc_error, realloc, Layout}; -use std::cmp::{max, Ordering}; +use std::alloc::{Layout, alloc, dealloc, handle_alloc_error, realloc}; +use std::cmp::{Ordering, max}; use std::fmt::{Debug, Error, Formatter}; use std::hash::{Hash, Hasher}; use std::iter::{Chain, Fuse, FusedIterator}; use std::marker::PhantomData; use std::mem::{self, align_of, needs_drop, size_of}; use std::ops::{Deref, DerefMut, Drop, FnMut, Index, IndexMut, RangeBounds}; -use std::ptr::{self, copy, drop_in_place, write, NonNull}; +use std::ptr::{self, NonNull, copy, drop_in_place, write}; use std::slice; /// Creates a [`GapBuffer`] containing the arguments. @@ -214,6 +214,7 @@ impl GapBuffer { /// # Computational amount /// `O(n)` , `n = |self.gap() - gap|` #[inline] + #[track_caller] pub fn set_gap(&mut self, gap: usize) { assert!(gap <= self.len()); if gap != self.gap() { @@ -221,19 +222,6 @@ impl GapBuffer { self.gap = gap; } } - fn move_values(&mut self, gap: usize) { - let gap_old = self.gap; - let gap_len = self.gap_len(); - let (src, dest, count) = if gap < gap_old { - (gap, gap + gap_len, gap_old - gap) - } else { - (gap_old + gap_len, gap_old, gap - gap_old) - }; - let p = self.as_mut_ptr(); - unsafe { - copy(p.add(src), p.add(dest), count); - } - } /// Return gap offset of the `GapBuffer`. #[inline] @@ -257,6 +245,7 @@ impl GapBuffer { /// # Computational amount /// `O(n)` , `n = |index - self.gap()|` #[inline] + #[track_caller] pub fn insert(&mut self, index: usize, element: T) { assert!(index <= self.len()); if self.gap() != index || self.len == self.capacity() { @@ -280,6 +269,7 @@ impl GapBuffer { /// Panics if `index > len`. /// /// Panics if the number of elements in the gap buffer overflows a usize. + #[track_caller] pub fn insert_many(&mut self, mut index: usize, iter: impl IntoIterator) { assert!(index <= self.len()); let mut iter = iter.into_iter(); @@ -356,6 +346,7 @@ impl GapBuffer { /// assert_eq!(value, 1); /// assert_eq!(buf, [5, 2, 3, 4]); /// ``` + #[track_caller] pub fn swap_remove(&mut self, index: usize) -> T { assert!(index < self.len()); @@ -395,6 +386,7 @@ impl GapBuffer { /// assert_eq!(value, 1); /// assert_eq!(buf, [2, 3, 4, 5]); /// ``` + #[track_caller] pub fn remove(&mut self, index: usize) -> T { assert!(index <= self.len()); let offset; @@ -544,16 +536,20 @@ impl GapBuffer { /// buf.extract_if(.., |_| true); /// assert_eq!(buf.is_empty(), false); /// ``` - pub fn extract_if(&mut self, range: impl RangeBounds, filter: F) -> ExtractIf<'_, T, F> + pub fn extract_if( + &mut self, + range: impl RangeBounds, + filter: F, + ) -> ExtractIf<'_, T, F> where - F: FnMut(&mut T) -> bool + F: FnMut(&mut T) -> bool, { let (idx, end) = self.to_idx_len(range); ExtractIf { buf: self, idx, end, - pred: filter + pred: filter, } } @@ -693,6 +689,13 @@ impl DerefMut for GapBuffer { } } +impl From> for GapBuffer { + /// An `O(1)` conversion from a [`Vec`]. + fn from(value: Vec) -> Self { + Self(RawGapBuffer::from_vec(value)) + } +} + impl FromIterator for GapBuffer { fn from_iter>(s: S) -> GapBuffer { let mut buf = GapBuffer::new(); @@ -706,12 +709,14 @@ impl Clone for GapBuffer { self.iter().cloned().collect() } } + impl Extend for GapBuffer { fn extend>(&mut self, iter: I) { let len = self.len; self.insert_many(len, iter); } } + impl<'gb, T: 'gb + Copy> Extend<&'gb T> for GapBuffer { fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().cloned()); @@ -726,6 +731,10 @@ impl RawGapBuffer { RawGapBuffer(Slice::empty()) } + const fn from_vec(vec: Vec) -> Self { + Self(Slice::from_vec(vec)) + } + fn realloc(&mut self, new_cap: usize) { let old_cap = self.0.cap; if old_cap == new_cap { @@ -751,7 +760,7 @@ impl RawGapBuffer { } self.0.cap = new_cap; } - + fn get_layout(cap: usize) -> Layout { let new_size = size_of::() .checked_mul(cap) @@ -863,6 +872,7 @@ impl DerefMut for RangeMut<'_, T> { &mut self.s } } + impl Clone for Range<'_, T> { fn clone(&self) -> Self { unsafe { @@ -899,6 +909,23 @@ impl Slice { } } + /// Constructs a `Slice` from a [`Vec`] at `O(1)`. + const fn from_vec(mut vec: Vec) -> Self { + let slice = Slice { + ptr: match NonNull::new(vec.as_mut_ptr()) { + Some(non_null) => non_null, + None => NonNull::dangling(), + }, + cap: vec.capacity(), + gap: vec.len(), + len: vec.len(), + }; + + let _dont_drop = std::mem::ManuallyDrop::new(vec); + + slice + } + /// Returns the number of elements in the GapBuffer. #[inline] pub const fn len(&self) -> usize { @@ -918,7 +945,8 @@ impl Slice { } #[inline] unsafe fn get_with_lifetime<'gb>(&self, index: usize) -> Option<&'gb T> { - self.get_offset(index).map(|o| &*self.as_ptr().add(o)) + self.get_offset(index) + .map(|o| unsafe { &*self.as_ptr().add(o) }) } /// Returns a mutable reference to an element at index or None if out of bounds. @@ -928,6 +956,21 @@ impl Slice { .map(|o| unsafe { &mut *self.as_mut_ptr().add(o) }) } + #[inline] + fn move_values(&mut self, gap: usize) { + let gap_old = self.gap; + let gap_len = self.gap_len(); + let (src, dest, count) = if gap < gap_old { + (gap, gap + gap_len, gap_old - gap) + } else { + (gap_old + gap_len, gap_old, gap - gap_old) + }; + let p = self.as_mut_ptr(); + unsafe { + copy(p.add(src), p.add(dest), count); + } + } + /// Swaps two elements in the GapBuffer. /// /// # Arguments @@ -966,7 +1009,7 @@ impl Slice { unsafe { self.range_with_lifetime(range) } } unsafe fn range_with_lifetime<'gb>(&self, range: impl RangeBounds) -> Range<'gb, T> { - Range::new(self.range_slice(range)) + unsafe { Range::new(self.range_slice(range)) } } /// Return a mutable sub-range of this Slice. @@ -1009,7 +1052,7 @@ impl Slice { let end = if !gap_is_after { self.gap_len() } else { 0 } + idx + len; Slice { - ptr: NonNull::new(self.ptr.as_ptr().add(begin)).unwrap(), + ptr: NonNull::new(unsafe { self.ptr.as_ptr().add(begin) }).unwrap(), cap: end - begin, gap, len, @@ -1064,11 +1107,10 @@ impl Slice { const unsafe fn as_slices_with_lifetime<'gb>(&self) -> (&'gb [T], &'gb [T]) { let p0 = self.as_ptr(); let c1 = self.len - self.gap; - let p1 = p0.add(self.cap - c1); - ( - slice::from_raw_parts(p0, self.gap), - slice::from_raw_parts(p1, c1), - ) + let p1 = unsafe { p0.add(self.cap - c1) }; + (unsafe { slice::from_raw_parts(p0, self.gap) }, unsafe { + slice::from_raw_parts(p1, c1) + }) } /// Returns a pair of slices. @@ -1137,9 +1179,52 @@ impl Slice { self.ptr.as_ptr() } } + +impl Clone for Slice { + fn clone(&self) -> Self { + let vec = self.iter().cloned().collect(); + Self::from_vec(vec) + } +} + unsafe impl Sync for Slice {} unsafe impl Send for Slice {} +#[cfg(feature = "bincode")] +impl + 'static, Context> bincode::Decode for Slice { + fn decode>( + decoder: &mut D, + ) -> Result { + let vec: Vec = bincode::Decode::decode(decoder)?; + Ok(Self::from_vec(vec)) + } +} + +#[cfg(feature = "bincode")] +impl bincode::Encode for Slice { + fn encode( + &self, + encoder: &mut E, + ) -> Result<(), bincode::error::EncodeError> { + let (s0, s1) = self.as_slices(); + bincode::Encode::encode(s0, encoder)?; + bincode::Encode::encode(s1, encoder) + } +} + +impl From> for Vec { + fn from(mut value: Slice) -> Self { + if value.gap != value.len { + value.move_values(value.len); + } + let vec = unsafe { Self::from_raw_parts(value.ptr.as_ptr(), value.len, value.cap) }; + + let _dont_drop = std::mem::ManuallyDrop::new(value); + + vec + } +} + //////////////////////////////////////////////////////////////////////////////// // Default //////////////////////////////////////////////////////////////////////////////// @@ -1532,7 +1617,7 @@ pub struct ExtractIf<'gb, T: 'gb, F> { impl Iterator for ExtractIf<'_, T, F> where - F: FnMut(&mut T) -> bool + F: FnMut(&mut T) -> bool, { type Item = T; @@ -1550,8 +1635,4 @@ where } } -impl FusedIterator for ExtractIf<'_, T, F> -where - F: FnMut(&mut T) -> bool -{ -} +impl FusedIterator for ExtractIf<'_, T, F> where F: FnMut(&mut T) -> bool {} From db52f5606dac645642d599e42a2eb7e34f871ae6 Mon Sep 17 00:00:00 2001 From: AhoyISki Date: Fri, 27 Feb 2026 20:23:23 -0300 Subject: [PATCH 08/15] Actually made this work now fr. --- Cargo.toml | 2 ++ src/gap_buffer.rs | 19 +++++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b360d10..77c5ac4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,12 +13,14 @@ edition = "2024" [dependencies] bincode = { version = "2.0.1", optional = true } +unty = "0.0.5" [dev-dependencies] test-strategy = "0.2.0" proptest = "1.0.0" [features] +default = ["bincode"] docs-rs = [] bincode = ["dep:bincode"] diff --git a/src/gap_buffer.rs b/src/gap_buffer.rs index 1bc4e38..13eb5aa 100644 --- a/src/gap_buffer.rs +++ b/src/gap_buffer.rs @@ -1206,9 +1206,24 @@ impl bincode::Encode for Slice { &self, encoder: &mut E, ) -> Result<(), bincode::error::EncodeError> { + use bincode::enc::write::Writer; let (s0, s1) = self.as_slices(); - bincode::Encode::encode(s0, encoder)?; - bincode::Encode::encode(s1, encoder) + + bincode::Encode::encode(&(self.len as u64), encoder)?; + + if unty::type_equal::() { + // Safety: T = u8 + let s0: &[u8] = unsafe { core::mem::transmute(s0) }; + encoder.writer().write(s0)?; + let s1: &[u8] = unsafe { core::mem::transmute(s1) }; + encoder.writer().write(s1)?; + } else { + for item in self { + item.encode(encoder)?; + } + } + + Ok(()) } } From e3a497e01c38541d33125b78340f8a135a320237 Mon Sep 17 00:00:00 2001 From: AhoyISki Date: Fri, 27 Feb 2026 20:23:40 -0300 Subject: [PATCH 09/15] New version. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 77c5ac4..3aca029 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gap-buf" -version = "0.2.0" +version = "0.3.0" authors = ["FrozenLib", "AhoyISki"] license = "MIT OR Apache-2.0" readme = "README.md" From 2a76110f6b566d4b4b2e6d44dd1fed462f079d08 Mon Sep 17 00:00:00 2001 From: AhoyISki Date: Fri, 27 Feb 2026 20:25:20 -0300 Subject: [PATCH 10/15] Made unty optional. --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3aca029..05c395d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gap-buf" -version = "0.3.0" +version = "0.3.1" authors = ["FrozenLib", "AhoyISki"] license = "MIT OR Apache-2.0" readme = "README.md" @@ -13,7 +13,7 @@ edition = "2024" [dependencies] bincode = { version = "2.0.1", optional = true } -unty = "0.0.5" +unty = { version = "0.0.5", optional = true } [dev-dependencies] test-strategy = "0.2.0" @@ -22,7 +22,7 @@ proptest = "1.0.0" [features] default = ["bincode"] docs-rs = [] -bincode = ["dep:bincode"] +bincode = ["dep:bincode", "dep:unty"] [package.metadata.docs.rs] features = ["docs-rs"] From a306f1301fdc0a428a8576dd0f72eebdb9511714 Mon Sep 17 00:00:00 2001 From: AhoyISki Date: Fri, 27 Feb 2026 20:33:50 -0300 Subject: [PATCH 11/15] Removed the unnecessary requirement on 'static. --- src/gap_buffer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gap_buffer.rs b/src/gap_buffer.rs index 13eb5aa..e0e13a6 100644 --- a/src/gap_buffer.rs +++ b/src/gap_buffer.rs @@ -1191,7 +1191,7 @@ unsafe impl Sync for Slice {} unsafe impl Send for Slice {} #[cfg(feature = "bincode")] -impl + 'static, Context> bincode::Decode for Slice { +impl, Context> bincode::Decode for Slice { fn decode>( decoder: &mut D, ) -> Result { @@ -1201,7 +1201,7 @@ impl + 'static, Context> bincode::Decode fo } #[cfg(feature = "bincode")] -impl bincode::Encode for Slice { +impl bincode::Encode for Slice { fn encode( &self, encoder: &mut E, From 9119e38bc0810d5ceee4edd9f56e37f105a0dd87 Mon Sep 17 00:00:00 2001 From: AhoyISki Date: Fri, 27 Feb 2026 20:34:18 -0300 Subject: [PATCH 12/15] New version. --- Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 05c395d..b6e847c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gap-buf" -version = "0.3.1" +version = "0.4.0" authors = ["FrozenLib", "AhoyISki"] license = "MIT OR Apache-2.0" readme = "README.md" @@ -20,7 +20,6 @@ test-strategy = "0.2.0" proptest = "1.0.0" [features] -default = ["bincode"] docs-rs = [] bincode = ["dep:bincode", "dep:unty"] From bcfd659ae736a85b568f2f4e1bc9d9cc7bf813fd Mon Sep 17 00:00:00 2001 From: AhoyISki Date: Fri, 27 Feb 2026 20:42:04 -0300 Subject: [PATCH 13/15] =?UTF-8?q?Actually=20derive=20the=20bincode=20trait?= =?UTF-8?q?s=20=F0=9F=A5=80.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 2 +- src/gap_buffer.rs | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b6e847c..22cbb1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gap-buf" -version = "0.4.0" +version = "0.5.0" authors = ["FrozenLib", "AhoyISki"] license = "MIT OR Apache-2.0" readme = "README.md" diff --git a/src/gap_buffer.rs b/src/gap_buffer.rs index e0e13a6..166e137 100644 --- a/src/gap_buffer.rs +++ b/src/gap_buffer.rs @@ -59,6 +59,7 @@ macro_rules! gap_buffer { /// /// `GapBuffer` has methods similar to [`Vec`](std::vec::Vec). #[derive(Hash)] +#[cfg_attr(feature = "bincode", derive(bincode::Decode, bincode::Encode))] pub struct GapBuffer(RawGapBuffer); impl GapBuffer { @@ -724,6 +725,7 @@ impl<'gb, T: 'gb + Copy> Extend<&'gb T> for GapBuffer { } #[derive(Hash)] +#[cfg_attr(feature = "bincode", derive(bincode::Decode, bincode::Encode))] struct RawGapBuffer(Slice); impl RawGapBuffer { @@ -1200,6 +1202,19 @@ impl, Context> bincode::Decode for Slice } } +#[cfg(feature = "bincode")] +impl<'de, T, Context> bincode::BorrowDecode<'de, Context> for Slice +where + T: bincode::BorrowDecode<'de, Context>, +{ + fn borrow_decode>( + decoder: &mut D, + ) -> Result { + let vec: Vec = bincode::BorrowDecode::borrow_decode(decoder)?; + Ok(Self::from_vec(vec)) + } +} + #[cfg(feature = "bincode")] impl bincode::Encode for Slice { fn encode( From f5d17a3f2ee8cb8706cf67986a0704ca8baa233a Mon Sep 17 00:00:00 2001 From: AhoyISki Date: Mon, 16 Mar 2026 18:52:20 -0300 Subject: [PATCH 14/15] New version. --- Cargo.toml | 2 +- src/gap_buffer.rs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 22cbb1f..4e07367 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gap-buf" -version = "0.5.0" +version = "0.6.0" authors = ["FrozenLib", "AhoyISki"] license = "MIT OR Apache-2.0" readme = "README.md" diff --git a/src/gap_buffer.rs b/src/gap_buffer.rs index 166e137..2af2728 100644 --- a/src/gap_buffer.rs +++ b/src/gap_buffer.rs @@ -506,6 +506,7 @@ impl GapBuffer { /// buf.drain(..); /// assert_eq!(buf.is_empty(), true); /// ``` + #[track_caller] pub fn drain(&mut self, range: impl RangeBounds) -> Drain<'_, T> { let (idx, len) = self.to_idx_len(range); Drain { @@ -537,6 +538,7 @@ impl GapBuffer { /// buf.extract_if(.., |_| true); /// assert_eq!(buf.is_empty(), false); /// ``` + #[track_caller] pub fn extract_if( &mut self, range: impl RangeBounds, @@ -575,6 +577,7 @@ impl GapBuffer { /// assert_eq!(b, [1, 7, 8, 9, 4]); /// assert_eq!(r, [2, 3]); /// ``` + #[track_caller] pub fn splice>( &mut self, range: impl RangeBounds, @@ -983,6 +986,7 @@ impl Slice { /// # Panics /// Panics if `a >= self.len()` or `b >= self.len()`. #[inline] + #[track_caller] pub const fn swap(&mut self, a: usize, b: usize) { let oa = self.get_offset(a).expect("a is out of bounds."); let ob = self.get_offset(b).expect("b is out of bounds."); @@ -1007,9 +1011,12 @@ impl Slice { /// let r2 = r1.range(1..3); /// assert_eq!(r2, [3, 4]); /// ``` + #[track_caller] pub fn range(&self, range: impl RangeBounds) -> Range<'_, T> { unsafe { self.range_with_lifetime(range) } } + + #[track_caller] unsafe fn range_with_lifetime<'gb>(&self, range: impl RangeBounds) -> Range<'gb, T> { unsafe { Range::new(self.range_slice(range)) } } @@ -1031,9 +1038,12 @@ impl Slice { /// } /// assert_eq!(buf, [1, 0, 3, 4, 5]); /// ``` + #[track_caller] pub fn range_mut(&mut self, range: impl RangeBounds) -> RangeMut<'_, T> { unsafe { RangeMut::new(self.range_slice(range)) } } + + #[track_caller] unsafe fn range_slice(&self, range: impl RangeBounds) -> Slice { let (idx, len) = self.to_idx_len(range); if len == 0 { @@ -1060,6 +1070,8 @@ impl Slice { len, } } + + #[track_caller] fn to_idx_len(&self, range: impl RangeBounds) -> (usize, usize) { use std::ops::Bound::*; const MAX: usize = usize::MAX; From 303d43e2adc56f36efe17b984ea8672fd962a9e2 Mon Sep 17 00:00:00 2001 From: AhoyISki Date: Mon, 16 Mar 2026 18:55:06 -0300 Subject: [PATCH 15/15] Erroneous error message fixed. --- Cargo.toml | 2 +- src/gap_buffer.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4e07367..5dea02c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gap-buf" -version = "0.6.0" +version = "0.6.1" authors = ["FrozenLib", "AhoyISki"] license = "MIT OR Apache-2.0" readme = "README.md" diff --git a/src/gap_buffer.rs b/src/gap_buffer.rs index 2af2728..4f61d57 100644 --- a/src/gap_buffer.rs +++ b/src/gap_buffer.rs @@ -1097,7 +1097,7 @@ impl Slice { } if end < idx { - panic!("slice index starts at {idx} but ends at {len}"); + panic!("slice index starts at {idx} but ends at {end}"); } (idx, end - idx) }