pax_global_header00006660000000000000000000000064144244570330014520gustar00rootroot0000000000000052 comment=668ee8146c494b26f4c0d279bfc8b7999a0f19b3 slice-group-by-0.3.1/000077500000000000000000000000001442445703300143625ustar00rootroot00000000000000slice-group-by-0.3.1/.github/000077500000000000000000000000001442445703300157225ustar00rootroot00000000000000slice-group-by-0.3.1/.github/workflows/000077500000000000000000000000001442445703300177575ustar00rootroot00000000000000slice-group-by-0.3.1/.github/workflows/ci.yml000066400000000000000000000015241442445703300210770ustar00rootroot00000000000000name: Cargo Build & Test on: push: pull_request: env: CARGO_TERM_COLOR: always jobs: build_and_test: name: Tests on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: matrix: os: - ubuntu-latest - macos-latest - windows-latest toolchain: - stable - beta - nightly steps: - uses: actions/checkout@v3 - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} - run: cargo build --verbose - run: cargo test --verbose miri_test: name: Test Miri on Ubuntu runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - run: rustup update nightly && rustup default nightly && rustup component add miri - run: cargo build --verbose - run: cargo miri test slice-group-by-0.3.1/.gitignore000066400000000000000000000000371442445703300163520ustar00rootroot00000000000000/Cargo.lock /target **/*.rs.bk slice-group-by-0.3.1/Cargo.toml000066400000000000000000000007041442445703300163130ustar00rootroot00000000000000[package] edition = "2018" name = "slice-group-by" description = "Iterators over groups in slices and strs" version = "0.3.1" documentation = "https://docs.rs/slice-group-by" repository = "https://github.com/Kerollmops/slice-group-by" authors = ["Kerollmops "] license = "MIT" keywords = ["str", "slice", "group"] categories = ["algorithms"] [dev-dependencies] rand = "0.6.5" [features] default = ["std"] nightly = [] std = [] slice-group-by-0.3.1/LICENSE000066400000000000000000000020611442445703300153660ustar00rootroot00000000000000MIT License Copyright (c) 2019 Clément Renault Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. slice-group-by-0.3.1/README.md000066400000000000000000000102361442445703300156430ustar00rootroot00000000000000# slice-group-by [![slice-group-by crate](https://img.shields.io/crates/v/slice-group-by.svg)](https://crates.io/crates/slice-group-by) [![slice-group-by documentation](https://docs.rs/slice-group-by/badge.svg)](https://docs.rs/slice-group-by) [![dependency status](https://deps.rs/repo/github/Kerollmops/slice-group-by/status.svg)](https://deps.rs/repo/github/Kerollmops/slice-group-by) [![build & tests worflow](https://github.com/Kerollmops/slice-group-by/actions/workflows/ci.yml/badge.svg)](https://github.com/Kerollmops/slice-group-by/actions/workflows/ci.yml) [![License](https://img.shields.io/github/license/Kerollmops/slice-group-by.svg)](https://github.com/Kerollmops/slice-group-by) An implementation of the [`groupBy` Haskell function], providing tools for efficiently iterating over `slice` and `str` by groups defined by a function that specifies if two elements are in the same group. ### Differences with `Itertools::group_by` The [`Itertools::group_by`] method use a key to compare elements, this library works like, say, [`slice::sort_by`], it uses a comparison function. It works on every `Iterator` type, `slice-group-by` work only with `slice` and `str`, which is the power of this library, it is fast thanks to [data locality]. Also `slice-group-by` support multiple search algorithms (i.e. [linear], [binary] and [exponential search]) and can return groups starting from the end. [`groupBy` Haskell function]: http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-List.html#v:groupBy [`Itertools::group_by`]: https://docs.rs/itertools/0.8.0/itertools/trait.Itertools.html#method.group_by [`slice::sort_by`]: https://doc.rust-lang.org/std/primitive.slice.html#method.sort_by [data locality]: https://en.wikipedia.org/wiki/Locality_of_reference [linear]: https://en.wikipedia.org/wiki/Linear_search [binary]: https://en.wikipedia.org/wiki/Binary_search_algorithm [exponential search]: https://en.wikipedia.org/wiki/Exponential_search ## Examples ### Linear Searched Immutable Groups You will only need to define a function that returns `true` if two elements are in the same group. The `LinearGroupBy` iterator will always gives contiguous elements to the predicate function. ```rust use slice_group_by::GroupBy; let slice = &[1, 1, 1, 3, 3, 2, 2, 2]; let mut iter = slice.linear_group_by(|a, b| a == b); assert_eq!(iter.next(), Some(&[1, 1, 1][..])); assert_eq!(iter.next(), Some(&[3, 3][..])); assert_eq!(iter.next(), Some(&[2, 2, 2][..])); assert_eq!(iter.next(), None); ``` ### Linear Searched Immutable Str Groups You will only need to define a function that returns `true` if two `char` are in the same group. The `LinearStrGroupBy` iterator will always gives contiguous `char` to the predicate function. ```rust use slice_group_by::StrGroupBy; let string = "aaaabbbbb饰饰cccc"; let mut iter = string.linear_group_by(|a, b| a == b); assert_eq!(iter.next(), Some("aaaa")); assert_eq!(iter.next(), Some("bbbbb")); assert_eq!(iter.next(), Some("饰饰")); assert_eq!(iter.next(), Some("cccc")); assert_eq!(iter.next(), None); ``` ### Binary Searched Mutable Groups It is also possible to get mutable non overlapping groups of a slice. The `BinaryGroupBy/Mut` and `ExponentialGroupBy/Mut` iterators will not necessarily gives contiguous elements to the predicate function. The predicate function should implement an order consistent with the sort order of the slice. ```rust use slice_group_by::GroupByMut; let slice = &mut [1, 1, 1, 2, 2, 2, 3, 3]; let mut iter = slice.binary_group_by_mut(|a, b| a == b); assert_eq!(iter.next(), Some(&mut [1, 1, 1][..])); assert_eq!(iter.next(), Some(&mut [2, 2, 2][..])); assert_eq!(iter.next(), Some(&mut [3, 3][..])); assert_eq!(iter.next(), None); ``` ### Exponential Searched Mutable Groups starting from the End It is also possible to get mutable non overlapping groups of a slice even starting from end of it. ```rust use slice_group_by::GroupByMut; let slice = &mut [1, 1, 1, 2, 2, 2, 3, 3]; let mut iter = slice.exponential_group_by_mut(|a, b| a == b).rev(); assert_eq!(iter.next(), Some(&mut [3, 3][..])); assert_eq!(iter.next(), Some(&mut [2, 2, 2][..])); assert_eq!(iter.next(), Some(&mut [1, 1, 1][..])); assert_eq!(iter.next(), None); ``` slice-group-by-0.3.1/src/000077500000000000000000000000001442445703300151515ustar00rootroot00000000000000slice-group-by-0.3.1/src/binary_group/000077500000000000000000000000001442445703300176515ustar00rootroot00000000000000slice-group-by-0.3.1/src/binary_group/binary_group.rs000066400000000000000000000030531442445703300227200ustar00rootroot00000000000000use crate::{BinaryGroupBy, BinaryGroupByMut}; /// An iterator that will return non-overlapping groups of equal elements, according to /// the [`PartialEq::eq`] function in the slice using *binary search*. /// /// It will not necessarily gives contiguous elements to the predicate function. /// The predicate function should implement an order consistent with the sort order of the slice. /// /// [`PartialEq::eq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html#tymethod.eq pub struct BinaryGroup<'a, T: 'a>(BinaryGroupBy<'a, T, fn(&T, &T) -> bool>); impl<'a, T: 'a> BinaryGroup<'a, T> where T: PartialEq, { pub fn new(slice: &'a [T]) -> BinaryGroup<'a, T> { BinaryGroup(BinaryGroupBy::new(slice, PartialEq::eq)) } } group_by_wrapped!{ struct BinaryGroup, &'a [T] } /// An iterator that will return non-overlapping *mutable* groups of equal elements, according to /// the [`PartialEq::eq`] function in the slice using *binary search*. /// /// It will not necessarily gives contiguous elements to the predicate function. /// The predicate function should implement an order consistent with the sort order of the slice. /// /// [`PartialEq::eq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html#tymethod.eq pub struct BinaryGroupMut<'a, T: 'a>(BinaryGroupByMut<'a, T, fn(&T, &T) -> bool>); impl<'a, T: 'a> BinaryGroupMut<'a, T> where T: PartialEq, { pub fn new(slice: &'a mut [T]) -> BinaryGroupMut<'a, T> { BinaryGroupMut(BinaryGroupByMut::new(slice, PartialEq::eq)) } } group_by_wrapped!{ struct BinaryGroupMut, &'a mut [T] } slice-group-by-0.3.1/src/binary_group/binary_group_by.rs000066400000000000000000000134151442445703300234150ustar00rootroot00000000000000use crate::offset_from; use std::cmp::Ordering::{Greater, Less}; use std::slice::{from_raw_parts, from_raw_parts_mut}; use std::{fmt, marker}; macro_rules! binary_group_by { (struct $name:ident, $elem:ty, $mkslice:ident) => { impl<'a, T: 'a, P> $name<'a, T, P> { #[inline] pub fn is_empty(&self) -> bool { self.ptr == self.end } #[inline] pub fn remainder_len(&self) -> usize { unsafe { offset_from(self.end, self.ptr) } } } impl<'a, T: 'a, P> std::iter::Iterator for $name<'a, T, P> where P: FnMut(&T, &T) -> bool, { type Item = $elem; #[inline] fn next(&mut self) -> Option { if self.is_empty() { return None; } let first = unsafe { &*self.ptr }; let len = self.remainder_len(); let tail = unsafe { $mkslice(self.ptr.add(1), len - 1) }; let predicate = |x: &T| { if (self.predicate)(first, x) { Less } else { Greater } }; let index = tail.binary_search_by(predicate).unwrap_err(); let left = unsafe { $mkslice(self.ptr, index + 1) }; self.ptr = unsafe { self.ptr.add(index + 1) }; Some(left) } fn size_hint(&self) -> (usize, Option) { if self.is_empty() { return (0, Some(0)); } let len = self.remainder_len(); (1, Some(len)) } fn last(mut self) -> Option { self.next_back() } } impl<'a, T: 'a, P> std::iter::DoubleEndedIterator for $name<'a, T, P> where P: FnMut(&T, &T) -> bool, { #[inline] fn next_back(&mut self) -> Option { if self.is_empty() { return None; } let last = unsafe { &*self.end.sub(1) }; let len = self.remainder_len(); let head = unsafe { $mkslice(self.ptr, len - 1) }; let predicate = |x: &T| { if (self.predicate)(last, x) { Greater } else { Less } }; let index = head.binary_search_by(predicate).unwrap_err(); let right = unsafe { $mkslice(self.ptr.add(index), len - index) }; self.end = unsafe { self.end.sub(len - index) }; Some(right) } } impl<'a, T: 'a, P> std::iter::FusedIterator for $name<'a, T, P> where P: FnMut(&T, &T) -> bool { } }; } /// An iterator that will return non-overlapping groups in the slice using *binary search*. /// /// It will not necessarily gives contiguous elements to the predicate function. /// The predicate function should implement an order consistent with the sort order of the slice. pub struct BinaryGroupBy<'a, T, P> { ptr: *const T, end: *const T, predicate: P, _phantom: marker::PhantomData<&'a T>, } impl<'a, T: 'a, P> BinaryGroupBy<'a, T, P> { pub fn new(slice: &'a [T], predicate: P) -> Self { BinaryGroupBy { ptr: slice.as_ptr(), end: unsafe { slice.as_ptr().add(slice.len()) }, predicate, _phantom: marker::PhantomData, } } } impl<'a, T: 'a, P> BinaryGroupBy<'a, T, P> { /// Returns the remainder of the original slice that is going to be /// returned by the iterator. pub fn remainder(&self) -> &[T] { let len = self.remainder_len(); unsafe { from_raw_parts(self.ptr, len) } } } impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for BinaryGroupBy<'a, T, P> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("BinaryGroupBy") .field("remainder", &self.remainder()) .finish() } } binary_group_by! { struct BinaryGroupBy, &'a [T], from_raw_parts } /// An iterator that will return non-overlapping *mutable* groups /// in the slice using *binary search*. /// /// It will not necessarily gives contiguous elements to the predicate function. /// The predicate function should implement an order consistent with the sort order of the slice. pub struct BinaryGroupByMut<'a, T, P> { ptr: *mut T, end: *mut T, predicate: P, _phantom: marker::PhantomData<&'a mut T>, } impl<'a, T: 'a, P> BinaryGroupByMut<'a, T, P> where P: FnMut(&T, &T) -> bool, { pub fn new(slice: &'a mut [T], predicate: P) -> Self { let ptr = slice.as_mut_ptr(); let end = unsafe { ptr.add(slice.len()) }; BinaryGroupByMut { ptr, end, predicate, _phantom: marker::PhantomData, } } } impl<'a, T: 'a, P> BinaryGroupByMut<'a, T, P> { /// Returns the remainder of the original slice that is going to be /// returned by the iterator. pub fn into_remainder(self) -> &'a mut [T] { let len = self.remainder_len(); unsafe { from_raw_parts_mut(self.ptr, len) } } } impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for BinaryGroupByMut<'a, T, P> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let len = self.remainder_len(); let remainder = unsafe { from_raw_parts(self.ptr, len) }; f.debug_struct("BinaryGroupByMut") .field("remainder", &remainder) .finish() } } binary_group_by! { struct BinaryGroupByMut, &'a mut [T], from_raw_parts_mut } slice-group-by-0.3.1/src/binary_group/binary_group_by_key.rs000066400000000000000000000131451442445703300242650ustar00rootroot00000000000000use crate::offset_from; use std::cmp::Ordering::{Greater, Less}; use std::slice::{from_raw_parts, from_raw_parts_mut}; use std::{fmt, marker}; macro_rules! binary_group_by_key { (struct $name:ident, $elem:ty, $mkslice:ident) => { impl<'a, T: 'a, F> $name<'a, T, F> { #[inline] pub fn is_empty(&self) -> bool { self.ptr == self.end } #[inline] pub fn remainder_len(&self) -> usize { unsafe { offset_from(self.end, self.ptr) } } } impl<'a, T: 'a, F, K> std::iter::Iterator for $name<'a, T, F> where F: FnMut(&T) -> K, K: PartialEq, { type Item = $elem; #[inline] fn next(&mut self) -> Option { if self.is_empty() { return None; } let first = unsafe { &*self.ptr }; let len = self.remainder_len(); let tail = unsafe { $mkslice(self.ptr.add(1), len - 1) }; let predicate = |x: &T| { if (self.func)(first) == (self.func)(x) { Less } else { Greater } }; let index = tail.binary_search_by(predicate).unwrap_err(); let left = unsafe { $mkslice(self.ptr, index + 1) }; self.ptr = unsafe { self.ptr.add(index + 1) }; Some(left) } fn size_hint(&self) -> (usize, Option) { if self.is_empty() { return (0, Some(0)); } let len = self.remainder_len(); (1, Some(len)) } fn last(mut self) -> Option { self.next_back() } } impl<'a, T: 'a, F, K> std::iter::DoubleEndedIterator for $name<'a, T, F> where F: FnMut(&T) -> K, K: PartialEq, { #[inline] fn next_back(&mut self) -> Option { if self.is_empty() { return None; } let last = unsafe { &*self.end.sub(1) }; let len = self.remainder_len(); let head = unsafe { $mkslice(self.ptr, len - 1) }; let predicate = |x: &T| { if (self.func)(last) == (self.func)(x) { Greater } else { Less } }; let index = head.binary_search_by(predicate).unwrap_err(); let right = unsafe { $mkslice(self.ptr.add(index), len - index) }; self.end = unsafe { self.end.sub(len - index) }; Some(right) } } impl<'a, T: 'a, F, K> std::iter::FusedIterator for $name<'a, T, F> where F: FnMut(&T) -> K, K: PartialEq, { } }; } /// An iterator that will return non-overlapping groups in the slice using *binary search*. /// /// It will give an element to the given function, producing a key and comparing /// the keys to determine groups. pub struct BinaryGroupByKey<'a, T, F> { ptr: *const T, end: *const T, func: F, _phantom: marker::PhantomData<&'a T>, } impl<'a, T: 'a, F> BinaryGroupByKey<'a, T, F> { pub fn new(slice: &'a [T], func: F) -> Self { BinaryGroupByKey { ptr: slice.as_ptr(), end: unsafe { slice.as_ptr().add(slice.len()) }, func, _phantom: marker::PhantomData, } } } impl<'a, T, F> BinaryGroupByKey<'a, T, F> { /// Returns the remainder of the original slice that is going to be /// returned by the iterator. pub fn remainder(&self) -> &[T] { let len = self.remainder_len(); unsafe { from_raw_parts(self.ptr, len) } } } impl<'a, T: 'a + fmt::Debug, F> fmt::Debug for BinaryGroupByKey<'a, T, F> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("BinaryGroupByKey") .field("remainder", &self.remainder()) .finish() } } binary_group_by_key! { struct BinaryGroupByKey, &'a [T], from_raw_parts } /// An iterator that will return non-overlapping *mutable* groups /// in the slice using *binary search*. /// /// It will give an element to the given function, producing a key and comparing /// the keys to determine groups. pub struct BinaryGroupByKeyMut<'a, T, F> { ptr: *mut T, end: *mut T, func: F, _phantom: marker::PhantomData<&'a mut T>, } impl<'a, T: 'a, F> BinaryGroupByKeyMut<'a, T, F> { pub fn new(slice: &'a mut [T], func: F) -> Self { let ptr = slice.as_mut_ptr(); let end = unsafe { ptr.add(slice.len()) }; BinaryGroupByKeyMut { ptr, end, func, _phantom: marker::PhantomData, } } } impl<'a, T, F> BinaryGroupByKeyMut<'a, T, F> { /// Returns the remainder of the original slice that is going to be /// returned by the iterator. pub fn remainder(&self) -> &[T] { let len = self.remainder_len(); unsafe { from_raw_parts(self.ptr, len) } } } impl<'a, T: 'a + fmt::Debug, F> fmt::Debug for BinaryGroupByKeyMut<'a, T, F> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("BinaryGroupByKeyMut") .field("remainder", &self.remainder()) .finish() } } binary_group_by_key! { struct BinaryGroupByKeyMut, &'a mut [T], from_raw_parts_mut } slice-group-by-0.3.1/src/binary_group/mod.rs000066400000000000000000000166031442445703300210040ustar00rootroot00000000000000mod binary_group; mod binary_group_by; mod binary_group_by_key; pub use self::binary_group::{BinaryGroup, BinaryGroupMut}; pub use self::binary_group_by::{BinaryGroupBy, BinaryGroupByMut}; pub use self::binary_group_by_key::{BinaryGroupByKey, BinaryGroupByKeyMut}; #[cfg(test)] mod tests { use super::*; #[derive(Debug, Eq)] enum Guard { Valid(i32), Invalid(i32), } impl PartialEq for Guard { fn eq(&self, other: &Self) -> bool { match (self, other) { (Guard::Valid(_), Guard::Valid(_)) => true, (a, b) => panic!("denied read on Guard::Invalid variant ({:?}, {:?})", a, b), } } } #[test] fn one_big_group() { let slice = &[1, 1, 1, 1]; let mut iter = BinaryGroup::new(slice); assert_eq!(iter.next(), Some(&[1, 1, 1, 1][..])); assert_eq!(iter.next(), None); } #[test] fn two_equal_groups() { let slice = &[1, 1, 1, 1, 2, 2, 2, 2]; let mut iter = BinaryGroup::new(slice); assert_eq!(iter.next(), Some(&[1, 1, 1, 1][..])); assert_eq!(iter.next(), Some(&[2, 2, 2, 2][..])); assert_eq!(iter.next(), None); } #[test] fn two_little_equal_groups() { let slice = &[1, 2]; let mut iter = BinaryGroup::new(slice); assert_eq!(iter.next(), Some(&[1][..])); assert_eq!(iter.next(), Some(&[2][..])); assert_eq!(iter.next(), None); } #[test] fn three_groups() { let slice = &[1, 1, 1, 2, 2, 2, 3, 3]; let mut iter = BinaryGroup::new(slice); assert_eq!(iter.next(), Some(&[1, 1, 1][..])); assert_eq!(iter.next(), Some(&[2, 2, 2][..])); assert_eq!(iter.next(), Some(&[3, 3][..])); assert_eq!(iter.next(), None); } #[test] fn three_little_groups() { let slice = &[1, 2, 3]; let mut iter = BinaryGroup::new(slice); assert_eq!(iter.next(), Some(&[1][..])); assert_eq!(iter.next(), Some(&[2][..])); assert_eq!(iter.next(), Some(&[3][..])); assert_eq!(iter.next(), None); } #[test] fn overflow() { let slice = &[Guard::Invalid(0), Guard::Valid(1), Guard::Valid(2), Guard::Invalid(3)]; let mut iter = BinaryGroup::new(&slice[1..3]); assert_eq!(iter.next(), Some(&[Guard::Valid(1), Guard::Valid(2)][..])); assert_eq!(iter.next(), None); } #[test] fn last_three_little_groups() { let slice = &[1, 2, 3]; let iter = BinaryGroup::new(slice); assert_eq!(iter.last(), Some(&[3][..])); } #[test] fn last_three_groups() { let slice = &[1, 1, 1, 2, 2, 2, 3, 3]; let iter = BinaryGroup::new(slice); assert_eq!(iter.last(), Some(&[3, 3][..])); } #[test] fn last_overflow() { let slice = &[Guard::Invalid(0), Guard::Valid(1), Guard::Valid(2), Guard::Invalid(3)]; println!("{:?}", (&slice[1..3]).as_ptr()); let iter = BinaryGroup::new(&slice[1..3]); assert_eq!(iter.last(), Some(&[Guard::Valid(1), Guard::Valid(2)][..])); } #[test] fn back_empty_slice() { let slice: &[i32] = &[]; let mut iter = BinaryGroup::new(slice); assert_eq!(iter.next_back(), None); } #[test] fn back_one_little_group() { let slice = &[1]; let mut iter = BinaryGroup::new(slice); assert_eq!(iter.next_back(), Some(&[1][..])); assert_eq!(iter.next_back(), None); assert_eq!(iter.next(), None); } #[test] fn back_three_little_groups() { let slice = &[1, 2, 3]; let mut iter = BinaryGroup::new(slice); assert_eq!(iter.next_back(), Some(&[3][..])); assert_eq!(iter.next_back(), Some(&[2][..])); assert_eq!(iter.next_back(), Some(&[1][..])); assert_eq!(iter.next_back(), None); } #[test] fn back_three_groups() { let slice = &[1, 1, 1, 2, 2, 2, 3, 3]; let mut iter = BinaryGroup::new(slice); assert_eq!(iter.next_back(), Some(&[3, 3][..])); assert_eq!(iter.next_back(), Some(&[2, 2, 2][..])); assert_eq!(iter.next_back(), Some(&[1, 1, 1][..])); assert_eq!(iter.next_back(), None); } #[test] fn double_ended_dont_cross() { let slice = &[1, 1, 1, 2, 2, 2, 3, 3]; let mut iter = BinaryGroup::new(slice); assert_eq!(iter.next(), Some(&[1, 1, 1][..])); assert_eq!(iter.next_back(), Some(&[3, 3][..])); assert_eq!(iter.next(), Some(&[2, 2, 2][..])); assert_eq!(iter.next_back(), None); assert_eq!(iter.next(), None); } #[test] fn fused_iterator() { let slice = &[1, 2, 3]; let mut iter = BinaryGroup::new(slice); assert_eq!(iter.next(), Some(&[1][..])); assert_eq!(iter.next(), Some(&[2][..])); assert_eq!(iter.next(), Some(&[3][..])); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); } #[test] fn back_fused_iterator() { let slice = &[1, 2, 3]; let mut iter = BinaryGroup::new(slice); assert_eq!(iter.next_back(), Some(&[3][..])); assert_eq!(iter.next_back(), Some(&[2][..])); assert_eq!(iter.next_back(), Some(&[1][..])); assert_eq!(iter.next_back(), None); assert_eq!(iter.next_back(), None); } } #[cfg(all(feature = "nightly", test))] mod bench { extern crate test; extern crate rand; use super::*; use self::rand::{Rng, SeedableRng}; use self::rand::rngs::StdRng; use self::rand::distributions::Alphanumeric; #[bench] fn vector_16_000_sorted(b: &mut test::Bencher) { let mut rng = StdRng::from_seed([42; 32]); let len = 16_000; let mut vec = Vec::with_capacity(len); for _ in 0..len { vec.push(rng.sample(Alphanumeric)); } vec.sort_unstable(); b.iter(|| { let group_by = BinaryGroup::new(vec.as_slice()); test::black_box(group_by.count()) }) } #[bench] fn vector_little_sorted(b: &mut test::Bencher) { let mut rng = StdRng::from_seed([42; 32]); let len = 30; let mut vec = Vec::with_capacity(len); for _ in 0..len { vec.push(rng.sample(Alphanumeric)); } vec.sort_unstable(); b.iter(|| { let group_by = BinaryGroup::new(vec.as_slice()); test::black_box(group_by.count()) }) } #[bench] fn vector_16_000_one_group(b: &mut test::Bencher) { let vec = vec![1; 16_000]; b.iter(|| { let group_by = BinaryGroup::new(vec.as_slice()); test::black_box(group_by.count()) }) } #[bench] fn rev_vector_16_000_sorted(b: &mut test::Bencher) { let mut rng = StdRng::from_seed([42; 32]); let len = 16_000; let mut vec = Vec::with_capacity(len); for _ in 0..len { vec.push(rng.sample(Alphanumeric)); } vec.sort_unstable(); b.iter(|| { let group_by = BinaryGroup::new(vec.as_slice()); test::black_box(group_by.rev().count()) }) } #[bench] fn rev_vector_16_000_one_group(b: &mut test::Bencher) { let vec = vec![1; 16_000]; b.iter(|| { let group_by = BinaryGroup::new(vec.as_slice()); test::black_box(group_by.rev().count()) }) } } slice-group-by-0.3.1/src/exponential_group/000077500000000000000000000000001442445703300207135ustar00rootroot00000000000000slice-group-by-0.3.1/src/exponential_group/exponential_group.rs000066400000000000000000000032051442445703300250230ustar00rootroot00000000000000use crate::{ExponentialGroupBy, ExponentialGroupByMut}; /// An iterator that will return non-overlapping groups of equal elements, according to /// the [`PartialEq::eq`] function in the slice using *exponential search*. /// /// It will not necessarily gives contiguous elements to the predicate function. /// The predicate function should implement an order consistent with the sort order of the slice. /// /// [`PartialEq::eq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html#tymethod.eq pub struct ExponentialGroup<'a, T: 'a>(ExponentialGroupBy<'a, T, fn(&T, &T) -> bool>); impl<'a, T: 'a> ExponentialGroup<'a, T> where T: PartialEq, { pub fn new(slice: &'a [T]) -> ExponentialGroup<'a, T> { ExponentialGroup(ExponentialGroupBy::new(slice, PartialEq::eq)) } } group_by_wrapped!{ struct ExponentialGroup, &'a [T] } /// An iterator that will return non-overlapping *mutable* groups of equal elements, according to /// the [`PartialEq::eq`] function in the slice using *exponential search*. /// /// It will not necessarily gives contiguous elements to the predicate function. /// The predicate function should implement an order consistent with the sort order of the slice. /// /// [`PartialEq::eq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html#tymethod.eq pub struct ExponentialGroupMut<'a, T: 'a>(ExponentialGroupByMut<'a, T, fn(&T, &T) -> bool>); impl<'a, T: 'a> ExponentialGroupMut<'a, T> where T: PartialEq, { pub fn new(slice: &'a mut [T]) -> ExponentialGroupMut<'a, T> { ExponentialGroupMut(ExponentialGroupByMut::new(slice, PartialEq::eq)) } } group_by_wrapped!{ struct ExponentialGroupMut, &'a mut [T] } slice-group-by-0.3.1/src/exponential_group/exponential_group_by.rs000066400000000000000000000136111442445703300255170ustar00rootroot00000000000000use crate::{exponential_search_by, offset_from}; use std::cmp::Ordering::{Greater, Less}; use std::slice::{from_raw_parts, from_raw_parts_mut}; use std::{fmt, marker}; macro_rules! exponential_group_by { (struct $name:ident, $elem:ty, $mkslice:ident) => { impl<'a, T: 'a, P> $name<'a, T, P> { #[inline] pub fn is_empty(&self) -> bool { self.ptr == self.end } #[inline] pub fn remainder_len(&self) -> usize { unsafe { offset_from(self.end, self.ptr) } } } impl<'a, T: 'a, P> std::iter::Iterator for $name<'a, T, P> where P: FnMut(&T, &T) -> bool, { type Item = $elem; fn next(&mut self) -> Option { if self.is_empty() { return None; } let first = unsafe { &*self.ptr }; let len = self.remainder_len(); let tail = unsafe { $mkslice(self.ptr.add(1), len - 1) }; let predicate = |x: &T| { if (self.predicate)(first, x) { Less } else { Greater } }; let index = exponential_search_by(tail, predicate).unwrap_err(); let left = unsafe { $mkslice(self.ptr, index + 1) }; self.ptr = unsafe { self.ptr.add(index + 1) }; Some(left) } fn size_hint(&self) -> (usize, Option) { if self.is_empty() { return (0, Some(0)); } let len = self.remainder_len(); (1, Some(len)) } fn last(mut self) -> Option { self.next_back() } } impl<'a, T: 'a, P> std::iter::DoubleEndedIterator for $name<'a, T, P> where P: FnMut(&T, &T) -> bool, { fn next_back(&mut self) -> Option { if self.is_empty() { return None; } let last = unsafe { &*self.end.sub(1) }; let len = self.remainder_len(); let head = unsafe { $mkslice(self.ptr, len - 1) }; let predicate = |x: &T| { if (self.predicate)(last, x) { Greater } else { Less } }; let index = exponential_search_by(head, predicate).unwrap_err(); let right = unsafe { $mkslice(self.ptr.add(index), len - index) }; self.end = unsafe { self.end.sub(len - index) }; Some(right) } } impl<'a, T: 'a, P> std::iter::FusedIterator for $name<'a, T, P> where P: FnMut(&T, &T) -> bool { } }; } /// An iterator that will reutrn non-overlapping groups in the slice using *exponential search*. /// /// It will not necessarily gives contiguous elements to the predicate function. /// The predicate function should implement an order consistent with the sort order of the slice. pub struct ExponentialGroupBy<'a, T, P> { ptr: *const T, end: *const T, predicate: P, _phantom: marker::PhantomData<&'a T>, } impl<'a, T: 'a, P> ExponentialGroupBy<'a, T, P> where P: FnMut(&T, &T) -> bool, { pub fn new(slice: &'a [T], predicate: P) -> Self { ExponentialGroupBy { ptr: slice.as_ptr(), end: unsafe { slice.as_ptr().add(slice.len()) }, predicate, _phantom: marker::PhantomData, } } } impl<'a, T: 'a, P> ExponentialGroupBy<'a, T, P> { /// Returns the remainder of the original slice that is going to be /// returned by the iterator. pub fn remainder(&self) -> &[T] { let len = self.remainder_len(); unsafe { from_raw_parts(self.ptr, len) } } } impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for ExponentialGroupBy<'a, T, P> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("ExponentialGroupBy") .field("remainder", &self.remainder()) .finish() } } exponential_group_by! { struct ExponentialGroupBy, &'a [T], from_raw_parts } /// An iterator that will reutrn non-overlapping *mutable* groups /// in the slice using *exponential search*. /// /// It will not necessarily gives contiguous elements to the predicate function. /// The predicate function should implement an order consistent with the sort order of the slice. pub struct ExponentialGroupByMut<'a, T, P> { ptr: *mut T, end: *mut T, predicate: P, _phantom: marker::PhantomData<&'a mut T>, } impl<'a, T: 'a, P> ExponentialGroupByMut<'a, T, P> where P: FnMut(&T, &T) -> bool, { pub fn new(slice: &'a mut [T], predicate: P) -> Self { let ptr = slice.as_mut_ptr(); let end = unsafe { ptr.add(slice.len()) }; ExponentialGroupByMut { ptr, end, predicate, _phantom: marker::PhantomData, } } } impl<'a, T: 'a, P> ExponentialGroupByMut<'a, T, P> { /// Returns the remainder of the original slice that is going to be /// returned by the iterator. pub fn into_remainder(self) -> &'a mut [T] { let len = self.remainder_len(); unsafe { from_raw_parts_mut(self.ptr, len) } } } impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for ExponentialGroupByMut<'a, T, P> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let len = self.remainder_len(); let remainder = unsafe { from_raw_parts(self.ptr, len) }; f.debug_struct("ExponentialGroupByMut") .field("remaining", &remainder) .finish() } } exponential_group_by! { struct ExponentialGroupByMut, &'a mut [T], from_raw_parts_mut } slice-group-by-0.3.1/src/exponential_group/exponential_group_by_key.rs000066400000000000000000000134701442445703300263720ustar00rootroot00000000000000use crate::{exponential_search_by, offset_from}; use std::cmp::Ordering::{Greater, Less}; use std::slice::{from_raw_parts, from_raw_parts_mut}; use std::{fmt, marker}; macro_rules! exponential_group_by_key { (struct $name:ident, $elem:ty, $mkslice:ident) => { impl<'a, T: 'a, F> $name<'a, T, F> { #[inline] pub fn is_empty(&self) -> bool { self.ptr == self.end } #[inline] pub fn remainder_len(&self) -> usize { unsafe { offset_from(self.end, self.ptr) } } } impl<'a, T: 'a, F, K> std::iter::Iterator for $name<'a, T, F> where F: FnMut(&T) -> K, K: PartialEq, { type Item = $elem; fn next(&mut self) -> Option { if self.is_empty() { return None; } let first = unsafe { &*self.ptr }; let len = self.remainder_len(); let tail = unsafe { $mkslice(self.ptr.add(1), len - 1) }; let predicate = |x: &T| { if (self.func)(first) == (self.func)(x) { Less } else { Greater } }; let index = exponential_search_by(tail, predicate).unwrap_err(); let left = unsafe { $mkslice(self.ptr, index + 1) }; self.ptr = unsafe { self.ptr.add(index + 1) }; Some(left) } fn size_hint(&self) -> (usize, Option) { if self.is_empty() { return (0, Some(0)); } let len = self.remainder_len(); (1, Some(len)) } fn last(mut self) -> Option { self.next_back() } } impl<'a, T: 'a, F, K> std::iter::DoubleEndedIterator for $name<'a, T, F> where F: FnMut(&T) -> K, K: PartialEq, { fn next_back(&mut self) -> Option { if self.is_empty() { return None; } let last = unsafe { &*self.end.sub(1) }; let len = self.remainder_len(); let head = unsafe { $mkslice(self.ptr, len - 1) }; let predicate = |x: &T| { if (self.func)(last) == (self.func)(x) { Greater } else { Less } }; let index = exponential_search_by(head, predicate).unwrap_err(); let right = unsafe { $mkslice(self.ptr.add(index), len - index) }; self.end = unsafe { self.end.sub(len - index) }; Some(right) } } impl<'a, T: 'a, F, K> std::iter::FusedIterator for $name<'a, T, F> where F: FnMut(&T) -> K, K: PartialEq, { } }; } /// An iterator that will reutrn non-overlapping groups in the slice using *exponential search*. /// /// It will give an element to the given function, producing a key and comparing /// the keys to determine groups. pub struct ExponentialGroupByKey<'a, T, F> { ptr: *const T, end: *const T, func: F, _phantom: marker::PhantomData<&'a T>, } impl<'a, T: 'a, F> ExponentialGroupByKey<'a, T, F> { pub fn new(slice: &'a [T], func: F) -> Self { ExponentialGroupByKey { ptr: slice.as_ptr(), end: unsafe { slice.as_ptr().add(slice.len()) }, func, _phantom: marker::PhantomData, } } } impl<'a, T: 'a, F> ExponentialGroupByKey<'a, T, F> { /// Returns the remainder of the original slice that is going to be /// returned by the iterator. pub fn remainder(&self) -> &[T] { let len = self.remainder_len(); unsafe { from_raw_parts(self.ptr, len) } } } impl<'a, T: 'a + fmt::Debug, F> fmt::Debug for ExponentialGroupByKey<'a, T, F> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("ExponentialGroupByKey") .field("remainder", &self.remainder()) .finish() } } exponential_group_by_key! { struct ExponentialGroupByKey, &'a [T], from_raw_parts } /// An iterator that will reutrn non-overlapping *mutable* groups /// in the slice using *exponential search*. /// /// It will give an element to the given function, producing a key and comparing /// the keys to determine groups. pub struct ExponentialGroupByKeyMut<'a, T, F> { ptr: *mut T, end: *mut T, func: F, _phantom: marker::PhantomData<&'a mut T>, } impl<'a, T: 'a, F> ExponentialGroupByKeyMut<'a, T, F> { pub fn new(slice: &'a mut [T], func: F) -> Self { let ptr = slice.as_mut_ptr(); let end = unsafe { ptr.add(slice.len()) }; ExponentialGroupByKeyMut { ptr, end, func, _phantom: marker::PhantomData, } } } impl<'a, T: 'a, F> ExponentialGroupByKeyMut<'a, T, F> { /// Returns the remainder of the original slice that is going to be /// returned by the iterator. pub fn into_remainder(self) -> &'a mut [T] { let len = self.remainder_len(); unsafe { from_raw_parts_mut(self.ptr, len) } } } impl<'a, T: 'a + fmt::Debug, F> fmt::Debug for ExponentialGroupByKeyMut<'a, T, F> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let len = self.remainder_len(); let remainder = unsafe { from_raw_parts(self.ptr, len) }; f.debug_struct("ExponentialGroupByKeyMut") .field("remaining", &remainder) .finish() } } exponential_group_by_key! { struct ExponentialGroupByKeyMut, &'a mut [T], from_raw_parts_mut } slice-group-by-0.3.1/src/exponential_group/mod.rs000066400000000000000000000170501442445703300220430ustar00rootroot00000000000000mod exponential_group; mod exponential_group_by; mod exponential_group_by_key; pub use self::exponential_group::{ExponentialGroup, ExponentialGroupMut}; pub use self::exponential_group_by::{ExponentialGroupBy, ExponentialGroupByMut}; pub use self::exponential_group_by_key::{ExponentialGroupByKey, ExponentialGroupByKeyMut}; #[cfg(test)] mod tests { use super::*; #[derive(Debug, Eq)] enum Guard { Valid(i32), Invalid(i32), } impl PartialEq for Guard { fn eq(&self, other: &Self) -> bool { match (self, other) { (Guard::Valid(_), Guard::Valid(_)) => true, (a, b) => panic!("denied read on Guard::Invalid variant ({:?}, {:?})", a, b), } } } #[test] fn one_big_group() { let slice = &[1, 1, 1, 1]; let mut iter = ExponentialGroup::new(slice); assert_eq!(iter.next(), Some(&[1, 1, 1, 1][..])); assert_eq!(iter.next(), None); } #[test] fn two_equal_groups() { let slice = &[1, 1, 1, 1, 2, 2, 2, 2]; let mut iter = ExponentialGroup::new(slice); assert_eq!(iter.next(), Some(&[1, 1, 1, 1][..])); assert_eq!(iter.next(), Some(&[2, 2, 2, 2][..])); assert_eq!(iter.next(), None); } #[test] fn two_little_equal_groups() { let slice = &[1, 2]; let mut iter = ExponentialGroup::new(slice); assert_eq!(iter.next(), Some(&[1][..])); assert_eq!(iter.next(), Some(&[2][..])); assert_eq!(iter.next(), None); } #[test] fn three_groups() { let slice = &[1, 1, 1, 2, 2, 2, 3, 3]; let mut iter = ExponentialGroup::new(slice); assert_eq!(iter.next(), Some(&[1, 1, 1][..])); assert_eq!(iter.next(), Some(&[2, 2, 2][..])); assert_eq!(iter.next(), Some(&[3, 3][..])); assert_eq!(iter.next(), None); } #[test] fn three_little_groups() { let slice = &[1, 2, 3]; let mut iter = ExponentialGroup::new(slice); assert_eq!(iter.next(), Some(&[1][..])); assert_eq!(iter.next(), Some(&[2][..])); assert_eq!(iter.next(), Some(&[3][..])); assert_eq!(iter.next(), None); } #[test] fn overflow() { let slice = &[Guard::Invalid(0), Guard::Valid(1), Guard::Valid(2), Guard::Invalid(3)]; let mut iter = ExponentialGroup::new(&slice[1..3]); assert_eq!(iter.next(), Some(&[Guard::Valid(1), Guard::Valid(2)][..])); assert_eq!(iter.next(), None); } #[test] fn last_three_little_groups() { let slice = &[1, 2, 3]; let iter = ExponentialGroup::new(slice); assert_eq!(iter.last(), Some(&[3][..])); } #[test] fn last_three_groups() { let slice = &[1, 1, 1, 2, 2, 2, 3, 3]; let iter = ExponentialGroup::new(slice); assert_eq!(iter.last(), Some(&[3, 3][..])); } #[test] fn last_overflow() { let slice = &[Guard::Invalid(0), Guard::Valid(1), Guard::Valid(2), Guard::Invalid(3)]; println!("{:?}", (&slice[1..3]).as_ptr()); let iter = ExponentialGroup::new(&slice[1..3]); assert_eq!(iter.last(), Some(&[Guard::Valid(1), Guard::Valid(2)][..])); } #[test] fn back_empty_slice() { let slice: &[i32] = &[]; let mut iter = ExponentialGroup::new(slice); assert_eq!(iter.next_back(), None); } #[test] fn back_one_little_group() { let slice = &[1]; let mut iter = ExponentialGroup::new(slice); assert_eq!(iter.next_back(), Some(&[1][..])); assert_eq!(iter.next_back(), None); assert_eq!(iter.next(), None); } #[test] fn back_three_little_groups() { let slice = &[1, 2, 3]; let mut iter = ExponentialGroup::new(slice); assert_eq!(iter.next_back(), Some(&[3][..])); assert_eq!(iter.next_back(), Some(&[2][..])); assert_eq!(iter.next_back(), Some(&[1][..])); assert_eq!(iter.next_back(), None); } #[test] fn back_three_groups() { let slice = &[1, 1, 1, 2, 2, 2, 3, 3]; let mut iter = ExponentialGroup::new(slice); assert_eq!(iter.next_back(), Some(&[3, 3][..])); assert_eq!(iter.next_back(), Some(&[2, 2, 2][..])); assert_eq!(iter.next_back(), Some(&[1, 1, 1][..])); assert_eq!(iter.next_back(), None); } #[test] fn double_ended_dont_cross() { let slice = &[1, 1, 1, 2, 2, 2, 3, 3]; let mut iter = ExponentialGroup::new(slice); assert_eq!(iter.next(), Some(&[1, 1, 1][..])); assert_eq!(iter.next_back(), Some(&[3, 3][..])); assert_eq!(iter.next(), Some(&[2, 2, 2][..])); assert_eq!(iter.next_back(), None); assert_eq!(iter.next(), None); } #[test] fn fused_iterator() { let slice = &[1, 2, 3]; let mut iter = ExponentialGroup::new(slice); assert_eq!(iter.next(), Some(&[1][..])); assert_eq!(iter.next(), Some(&[2][..])); assert_eq!(iter.next(), Some(&[3][..])); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); } #[test] fn back_fused_iterator() { let slice = &[1, 2, 3]; let mut iter = ExponentialGroup::new(slice); assert_eq!(iter.next_back(), Some(&[3][..])); assert_eq!(iter.next_back(), Some(&[2][..])); assert_eq!(iter.next_back(), Some(&[1][..])); assert_eq!(iter.next_back(), None); assert_eq!(iter.next_back(), None); } } #[cfg(all(feature = "nightly", test))] mod bench { extern crate test; extern crate rand; use super::*; use self::rand::{Rng, SeedableRng}; use self::rand::rngs::StdRng; use self::rand::distributions::Alphanumeric; #[bench] fn vector_16_000_sorted(b: &mut test::Bencher) { let mut rng = StdRng::from_seed([42; 32]); let len = 16_000; let mut vec = Vec::with_capacity(len); for _ in 0..len { vec.push(rng.sample(Alphanumeric)); } vec.sort_unstable(); b.iter(|| { let group_by = ExponentialGroup::new(vec.as_slice()); test::black_box(group_by.count()) }) } #[bench] fn vector_little_sorted(b: &mut test::Bencher) { let mut rng = StdRng::from_seed([42; 32]); let len = 30; let mut vec = Vec::with_capacity(len); for _ in 0..len { vec.push(rng.sample(Alphanumeric)); } vec.sort_unstable(); b.iter(|| { let group_by = ExponentialGroup::new(vec.as_slice()); test::black_box(group_by.count()) }) } #[bench] fn vector_16_000_one_group(b: &mut test::Bencher) { let vec = vec![1; 16_000]; b.iter(|| { let group_by = ExponentialGroup::new(vec.as_slice()); test::black_box(group_by.count()) }) } #[bench] fn rev_vector_16_000_sorted(b: &mut test::Bencher) { let mut rng = StdRng::from_seed([42; 32]); let len = 16_000; let mut vec = Vec::with_capacity(len); for _ in 0..len { vec.push(rng.sample(Alphanumeric)); } vec.sort_unstable(); b.iter(|| { let group_by = ExponentialGroup::new(vec.as_slice()); test::black_box(group_by.rev().count()) }) } #[bench] fn rev_vector_16_000_one_group(b: &mut test::Bencher) { let vec = vec![1; 16_000]; b.iter(|| { let group_by = ExponentialGroup::new(vec.as_slice()); test::black_box(group_by.rev().count()) }) } } slice-group-by-0.3.1/src/lib.rs000066400000000000000000000532231442445703300162720ustar00rootroot00000000000000//! Crate `slice-group-by` is a library for efficiently iterating on a slice by groups defined by //! a function that specifies if two elements are in the same group. //! //! # Example: Linear Searched Immutable Groups //! //! You will only need to define a function that returns `true` if two elements are in the same group. //! //! The `LinearGroupBy` iterator will always gives contiguous elements to the predicate function. //! //! ```rust //! use slice_group_by::GroupBy; //! //! let slice = &[1, 1, 1, 3, 3, 2, 2, 2]; //! //! let mut iter = slice.linear_group_by_key(|x| -x); //! //! assert_eq!(iter.next(), Some(&[1, 1, 1][..])); //! assert_eq!(iter.next(), Some(&[3, 3][..])); //! assert_eq!(iter.next(), Some(&[2, 2, 2][..])); //! assert_eq!(iter.next(), None); //! ``` //! //! # Example: Linear Searched Immutable Str Slices //! //! You will only need to define a function that returns `true` if two `char` are in the same group. //! //! The `LinearStrGroupBy` iterator will always gives contiguous `char` to the predicate function. //! //! ```rust //! use slice_group_by::StrGroupBy; //! //! let string = "aaaabbbbb饰饰cccc"; //! //! let mut iter = string.linear_group_by(|a, b| a == b); //! //! assert_eq!(iter.next(), Some("aaaa")); //! assert_eq!(iter.next(), Some("bbbbb")); //! assert_eq!(iter.next(), Some("饰饰")); //! assert_eq!(iter.next(), Some("cccc")); //! assert_eq!(iter.next(), None); //! ``` //! //! # Example: Binary Searched Mutable Groups //! //! It is also possible to get mutable non overlapping groups of a slice. //! //! The `BinaryGroupBy/Mut` and `ExponentialGroupBy/Mut` iterators will not necessarily //! gives contiguous elements to the predicate function. The predicate function should implement //! an order consistent with the sort order of the slice. //! //! ```rust //! use slice_group_by::GroupByMut; //! //! let slice = &mut [1, 1, 1, 2, 2, 2, 3, 3]; //! //! let mut iter = slice.binary_group_by_mut(|a, b| a == b); //! //! assert_eq!(iter.next(), Some(&mut [1, 1, 1][..])); //! assert_eq!(iter.next(), Some(&mut [2, 2, 2][..])); //! assert_eq!(iter.next(), Some(&mut [3, 3][..])); //! assert_eq!(iter.next(), None); //! ``` //! //! # Example: Exponential Searched Mutable Groups starting from the End //! //! It is also possible to get mutable non overlapping groups of a slice even starting from the end of it. //! //! ```rust //! use slice_group_by::GroupByMut; //! //! let slice = &mut [1, 1, 1, 2, 2, 2, 3, 3]; //! //! let mut iter = slice.exponential_group_by_mut(|a, b| a == b).rev(); //! //! assert_eq!(iter.next(), Some(&mut [3, 3][..])); //! assert_eq!(iter.next(), Some(&mut [2, 2, 2][..])); //! assert_eq!(iter.next(), Some(&mut [1, 1, 1][..])); //! assert_eq!(iter.next(), None); //! ``` //! #![cfg_attr(feature = "nightly", feature(ptr_offset_from))] #![cfg_attr(feature = "nightly", feature(test))] #![cfg_attr(all(not(test), not(feature = "std")), no_std)] #[cfg(all(not(test), not(feature = "std")))] extern crate core as std; macro_rules! group_by_wrapped { (struct $name:ident, $elem:ty) => { impl<'a, T: 'a> std::iter::Iterator for $name<'a, T> where T: PartialEq, { type Item = $elem; fn next(&mut self) -> Option { self.0.next() } fn size_hint(&self) -> (usize, Option) { self.0.size_hint() } fn last(self) -> Option { self.0.last() } } impl<'a, T: 'a> DoubleEndedIterator for $name<'a, T> where T: PartialEq, { fn next_back(&mut self) -> Option { self.0.next_back() } } impl<'a, T: 'a> std::iter::FusedIterator for $name<'a, T> where T: PartialEq, { } } } mod linear_group; mod binary_group; mod exponential_group; mod linear_str_group; use std::cmp::{self, Ordering}; pub use self::linear_group::{ LinearGroupByKey, LinearGroupBy, LinearGroup, LinearGroupByKeyMut, LinearGroupByMut, LinearGroupMut, }; pub use self::binary_group::{ BinaryGroupByKey, BinaryGroupBy, BinaryGroup, BinaryGroupByKeyMut, BinaryGroupByMut, BinaryGroupMut, }; pub use self::exponential_group::{ ExponentialGroupByKey, ExponentialGroupBy, ExponentialGroup, ExponentialGroupByKeyMut, ExponentialGroupByMut, ExponentialGroupMut, }; pub use self::linear_str_group::{ LinearStrGroupByKey, LinearStrGroupBy, LinearStrGroup, LinearStrGroupByKeyMut, LinearStrGroupByMut, LinearStrGroupMut, }; #[cfg(feature = "nightly")] #[inline] unsafe fn offset_from(to: *const T, from: *const T) -> usize { to.offset_from(from) as usize } #[cfg(not(feature = "nightly"))] #[inline] unsafe fn offset_from(to: *const T, from: *const T) -> usize { use std::mem; (to as usize - from as usize) / mem::size_of::() } /// Exponential searches this sorted slice for a given element. /// /// If the value is found then `Ok` is returned, containing the index of the matching element; /// if the value is not found then `Err` is returned, containing the index where a matching element /// could be inserted while maintaining sorted order. /// /// # Examples /// /// Looks up a series of four elements. The first is found, with a /// uniquely determined position; the second and third are not /// found; the fourth could match any position in `[1, 4]`. /// /// ``` /// use slice_group_by::exponential_search; /// /// let s = &[0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; /// /// assert_eq!(exponential_search(s, &13), Ok(9)); /// assert_eq!(exponential_search(s, &4), Err(7)); /// assert_eq!(exponential_search(s, &100), Err(13)); /// let r = exponential_search(s, &1); /// assert!(match r { Ok(1..=4) => true, _ => false, }); /// ``` #[inline] pub fn exponential_search(slice: &[T], elem: &T) -> Result where T: Ord { exponential_search_by(slice, |x| x.cmp(elem)) } /// Binary searches this sorted slice with a comparator function. /// /// The comparator function should implement an order consistent with the sort order of /// the underlying slice, returning an order code that indicates whether its argument /// is `Less`, `Equal` or `Greater` the desired target. /// /// If the value is found then `Ok` is returned, containing the index of the matching element; /// if the value is not found then `Err` is returned, containing the index where a matching element /// could be inserted while maintaining sorted order. /// /// # Examples /// /// Looks up a series of four elements. The first is found, with a /// uniquely determined position; the second and third are not /// found; the fourth could match any position in `[1, 4]`. /// /// ``` /// use slice_group_by::exponential_search_by; /// /// let s = &[0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; /// /// let seek = 13; /// assert_eq!(exponential_search_by(s, |probe| probe.cmp(&seek)), Ok(9)); /// let seek = 4; /// assert_eq!(exponential_search_by(s, |probe| probe.cmp(&seek)), Err(7)); /// let seek = 100; /// assert_eq!(exponential_search_by(s, |probe| probe.cmp(&seek)), Err(13)); /// let seek = 1; /// let r = exponential_search_by(s, |probe| probe.cmp(&seek)); /// assert!(match r { Ok(1..=4) => true, _ => false, }); /// ``` #[inline] pub fn exponential_search_by(slice: &[T], mut f: F) -> Result where F: FnMut(&T) -> Ordering, { let mut index = 1; while index < slice.len() && f(&slice[index]) == Ordering::Less { index *= 2; } let half_bound = index / 2; let bound = cmp::min(index + 1, slice.len()); match slice[half_bound..bound].binary_search_by(f) { Ok(pos) => Ok(half_bound + pos), Err(pos) => Err(half_bound + pos), } } /// Binary searches this sorted slice with a key extraction function. /// /// Assumes that the slice is sorted by the key. /// /// If the value is found then `Ok` is returned, containing the index of the matching element; /// if the value is not found then `Err` is returned, containing the index where a matching element /// could be inserted while maintaining sorted order. /// /// # Examples /// /// Looks up a series of four elements. The first is found, with a /// uniquely determined position; the second and third are not /// found; the fourth could match any position in `[1, 4]`. /// /// ``` /// use slice_group_by::exponential_search_by_key; /// /// let s = &[(0, 0), (2, 1), (4, 1), (5, 1), (3, 1), /// (1, 2), (2, 3), (4, 5), (5, 8), (3, 13), /// (1, 21), (2, 34), (4, 55)]; /// /// assert_eq!(exponential_search_by_key(s, &13, |&(a,b)| b), Ok(9)); /// assert_eq!(exponential_search_by_key(s, &4, |&(a,b)| b), Err(7)); /// assert_eq!(exponential_search_by_key(s, &100, |&(a,b)| b), Err(13)); /// let r = exponential_search_by_key(s, &1, |&(a,b)| b); /// assert!(match r { Ok(1..=4) => true, _ => false, }); /// ``` #[inline] pub fn exponential_search_by_key(slice: &[T], b: &B, mut f: F) -> Result where F: FnMut(&T) -> B, B: Ord { exponential_search_by(slice, |k| f(k).cmp(b)) } /// A convenient trait to construct an iterator returning non-overlapping groups /// defined by a predicate. pub trait GroupBy { /// Returns an iterator on slice groups based that will use the given function to generate keys /// and determine groups based on them. It uses *linear search* to iterate over groups. fn linear_group_by_key(&self, func: F) -> LinearGroupByKey where F: FnMut(&T) -> K, K: PartialEq; /// Returns an iterator on slice groups using the *linear search* method. fn linear_group_by

(&self, predicate: P) -> LinearGroupBy where P: FnMut(&T, &T) -> bool; /// Returns an iterator on slice groups based on the [`PartialEq::eq`] method of `T`, /// it uses *linear search* to iterate over groups. /// /// [`PartialEq::eq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html#tymethod.eq fn linear_group(&self) -> LinearGroup where T: PartialEq; /// Returns an iterator on slice groups based that will use the given function to generate keys /// and determine groups based on them. It uses *binary search* to iterate over groups. /// /// The predicate function should implement an order consistent with /// the sort order of the slice. fn binary_group_by_key(&self, func: F) -> BinaryGroupByKey where F: FnMut(&T) -> K, K: PartialEq; /// Returns an iterator on slice groups using the *binary search* method. /// /// The predicate function should implement an order consistent with /// the sort order of the slice. fn binary_group_by

(&self, predicate: P) -> BinaryGroupBy where P: FnMut(&T, &T) -> bool; /// Returns an iterator on slice groups based on the [`PartialEq::eq`] method of `T`, /// it uses *binary search* to iterate over groups. /// /// The predicate function should implement an order consistent with /// the sort order of the slice. /// /// [`PartialEq::eq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html#tymethod.eq fn binary_group(&self) -> BinaryGroup where T: PartialEq; /// Returns an iterator on slice groups based that will use the given function to generate keys /// and determine groups based on them. It uses *exponential search* to iterate over groups. /// /// The predicate function should implement an order consistent with /// the sort order of the slice. fn exponential_group_by_key(&self, func: F) -> ExponentialGroupByKey where F: Fn(&T) -> K, K: PartialEq; /// Returns an iterator on slice groups using the *exponential search* method. /// /// The predicate function should implement an order consistent with /// the sort order of the slice. fn exponential_group_by

(&self, predicate: P) -> ExponentialGroupBy where P: FnMut(&T, &T) -> bool; /// Returns an iterator on slice groups based on the [`PartialEq::eq`] method of `T`, /// it uses *exponential search* to iterate over groups. /// /// The predicate function should implement an order consistent with /// the sort order of the slice. /// /// [`PartialEq::eq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html#tymethod.eq fn exponential_group(&self) -> ExponentialGroup where T: PartialEq; } /// A convenient trait to construct an iterator returning non-overlapping *mutable* /// groups defined by a predicate. pub trait GroupByMut { /// Returns an iterator on *mutable* slice groups based that will use the given function /// to generate keys and determine groups based on them. It uses *linear search* /// to iterate over groups. fn linear_group_by_key_mut(&mut self, func: F) -> LinearGroupByKeyMut where F: FnMut(&T) -> K, K: PartialEq; /// Returns an iterator on *mutable* slice groups using the *linear search* method. fn linear_group_by_mut

(&mut self, predicate: P) -> LinearGroupByMut where P: FnMut(&T, &T) -> bool; /// Returns an iterator on *mutable* slice groups based on the [`PartialEq::eq`] method of `T`, /// it uses *linear search* to iterate over groups. /// /// [`PartialEq::eq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html#tymethod.eq fn linear_group_mut(&mut self) -> LinearGroupMut where T: PartialEq; /// Returns an iterator on *mutable* slice groups based that will use the given function /// to generate keys and determine groups based on them. It uses *binary search* /// to iterate over groups. /// /// The predicate function should implement an order consistent with /// the sort order of the slice. fn binary_group_by_key_mut(&mut self, func: F) -> BinaryGroupByKeyMut where F: FnMut(&T) -> K, K: PartialEq; /// Returns an iterator on *mutable* slice groups using the *binary search* method. /// /// The predicate function should implement an order consistent with /// the sort order of the slice. fn binary_group_by_mut

(&mut self, predicate: P) -> BinaryGroupByMut where P: FnMut(&T, &T) -> bool; /// Returns an iterator on *mutable* slice groups based on the [`PartialEq::eq`] method of `T`, /// it uses *binary search* to iterate over groups. /// /// The predicate function should implement an order consistent with /// the sort order of the slice. /// /// [`PartialEq::eq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html#tymethod.eq fn binary_group_mut(&mut self) -> BinaryGroupMut where T: PartialEq; /// Returns an iterator on *mutable* slice groups based that will use the given function /// to generate keys and determine groups based on them. It uses *exponential search* /// to iterate over groups. /// /// The predicate function should implement an order consistent with /// the sort order of the slice. fn exponential_group_by_key_mut(&mut self, func: F) -> ExponentialGroupByKeyMut where F: Fn(&T) -> K, K: PartialEq; /// Returns an iterator on *mutable* slice groups using the *exponential search* method. /// /// The predicate function should implement an order consistent with /// the sort order of the slice. fn exponential_group_by_mut

(&mut self, predicate: P) -> ExponentialGroupByMut where P: FnMut(&T, &T) -> bool; /// Returns an iterator on *mutable* slice groups based on the [`PartialEq::eq`] method of `T`, /// it uses *exponential search* to iterate over groups. /// /// The predicate function should implement an order consistent with /// the sort order of the slice. /// /// [`PartialEq::eq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html#tymethod.eq fn exponential_group_mut(&mut self) -> ExponentialGroupMut where T: PartialEq; } impl GroupBy for [T] { fn linear_group_by_key(&self, func: F) -> LinearGroupByKey where F: FnMut(&T) -> K, K: PartialEq { LinearGroupByKey::new(self, func) } fn linear_group_by

(&self, predicate: P) -> LinearGroupBy where P: FnMut(&T, &T) -> bool, { LinearGroupBy::new(self, predicate) } fn linear_group(&self) -> LinearGroup where T: PartialEq, { LinearGroup::new(self) } fn binary_group_by_key(&self, func: F) -> BinaryGroupByKey where F: FnMut(&T) -> K, K: PartialEq { BinaryGroupByKey::new(self, func) } fn binary_group_by

(&self, predicate: P) -> BinaryGroupBy where P: FnMut(&T, &T) -> bool, { BinaryGroupBy::new(self, predicate) } fn binary_group(&self) -> BinaryGroup where T: PartialEq, { BinaryGroup::new(self) } fn exponential_group_by_key(&self, func: F) -> ExponentialGroupByKey where F: Fn(&T) -> K, K: PartialEq { ExponentialGroupByKey::new(self, func) } fn exponential_group_by

(&self, predicate: P) -> ExponentialGroupBy where P: FnMut(&T, &T) -> bool, { ExponentialGroupBy::new(self, predicate) } fn exponential_group(&self) -> ExponentialGroup where T: PartialEq, { ExponentialGroup::new(self) } } impl GroupByMut for [T] { fn linear_group_by_key_mut(&mut self, func: F) -> LinearGroupByKeyMut where F: FnMut(&T) -> K, K: PartialEq { LinearGroupByKeyMut::new(self, func) } fn linear_group_by_mut

(&mut self, predicate: P) -> LinearGroupByMut where P: FnMut(&T, &T) -> bool, { LinearGroupByMut::new(self, predicate) } fn linear_group_mut(&mut self) -> LinearGroupMut where T: PartialEq, { LinearGroupMut::new(self) } fn binary_group_by_key_mut(&mut self, func: F) -> BinaryGroupByKeyMut where F: FnMut(&T) -> K, K: PartialEq { BinaryGroupByKeyMut::new(self, func) } fn binary_group_by_mut

(&mut self, predicate: P) -> BinaryGroupByMut where P: FnMut(&T, &T) -> bool, { BinaryGroupByMut::new(self, predicate) } fn binary_group_mut(&mut self) -> BinaryGroupMut where T: PartialEq, { BinaryGroupMut::new(self) } fn exponential_group_by_key_mut(&mut self, func: F) -> ExponentialGroupByKeyMut where F: Fn(&T) -> K, K: PartialEq { ExponentialGroupByKeyMut::new(self, func) } fn exponential_group_by_mut

(&mut self, predicate: P) -> ExponentialGroupByMut where P: FnMut(&T, &T) -> bool, { ExponentialGroupByMut::new(self, predicate) } fn exponential_group_mut(&mut self) -> ExponentialGroupMut where T: PartialEq, { ExponentialGroupMut::new(self) } } /// A convenient trait to construct an iterator returning non-overlapping `str` slices /// defined by a predicate. pub trait StrGroupBy { /// Returns an iterator on `str` groups based that will use the given function /// to generate keys and determine groups based on them. It uses *linear search* /// to iterate over groups. fn linear_group_by_key(&self, func: F) -> LinearStrGroupByKey where F: FnMut(char) -> K, K: PartialEq; /// Returns an iterator on `str` groups using the *linear search* method. fn linear_group_by

(&self, predicate: P) -> LinearStrGroupBy

where P: FnMut(char, char) -> bool; /// Returns an iterator on `str` groups based on the [`PartialEq::eq`] method of `char`, /// it uses *linear search* to iterate over groups. /// /// [`PartialEq::eq`]: https://doc.rust-lang.org/std/primitive.char.html#impl-PartialEq%3Cchar%3E fn linear_group(&self) -> LinearStrGroup; } /// A convenient trait to construct an iterator returning non-overlapping *mutable* `str` slices /// defined by a predicate. pub trait StrGroupByMut { /// Returns an iterator on *mutable* `str` groups based that will use the given function /// to generate keys and determine groups based on them. It uses *linear search* /// to iterate over groups. fn linear_group_by_key_mut(&mut self, func: F) -> LinearStrGroupByKeyMut where F: FnMut(char) -> K, K: PartialEq; /// Returns an iterator on *mutable* `str` groups using the *linear search* method. fn linear_group_by_mut

(&mut self, predicate: P) -> LinearStrGroupByMut

where P: FnMut(char, char) -> bool; /// Returns an iterator on *mutable* `str` groups based on the [`PartialEq::eq`] method of `char`, /// it uses *linear search* to iterate over groups. /// /// [`PartialEq::eq`]: https://doc.rust-lang.org/std/primitive.char.html#impl-PartialEq%3Cchar%3E fn linear_group_mut(&mut self) -> LinearStrGroupMut; } impl StrGroupBy for str { fn linear_group_by_key(&self, func: F) -> LinearStrGroupByKey where F: FnMut(char) -> K, K: PartialEq { LinearStrGroupByKey::new(self, func) } fn linear_group_by

(&self, predicate: P) -> LinearStrGroupBy

where P: FnMut(char, char) -> bool, { LinearStrGroupBy::new(self, predicate) } fn linear_group(&self) -> LinearStrGroup { LinearStrGroup::new(self) } } impl StrGroupByMut for str { fn linear_group_by_key_mut(&mut self, func: F) -> LinearStrGroupByKeyMut where F: FnMut(char) -> K, K: PartialEq { LinearStrGroupByKeyMut::new(self, func) } fn linear_group_by_mut

(&mut self, predicate: P) -> LinearStrGroupByMut

where P: FnMut(char, char) -> bool, { LinearStrGroupByMut::new(self, predicate) } fn linear_group_mut(&mut self) -> LinearStrGroupMut { LinearStrGroupMut::new(self) } } slice-group-by-0.3.1/src/linear_group/000077500000000000000000000000001442445703300176375ustar00rootroot00000000000000slice-group-by-0.3.1/src/linear_group/linear_group.rs000066400000000000000000000026071442445703300227000ustar00rootroot00000000000000use crate::{LinearGroupBy, LinearGroupByMut}; /// An iterator that will return non-overlapping groups of equal elements /// in the slice using *linear/sequential search*. /// /// It will give two contiguous elements to the [`PartialEq::eq`] function /// therefore the slice must not be necessarily sorted. /// /// [`PartialEq::eq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html#tymethod.eq pub struct LinearGroup<'a, T: 'a>(LinearGroupBy<'a, T, fn(&T, &T) -> bool>); impl<'a, T: 'a> LinearGroup<'a, T> where T: PartialEq, { pub fn new(slice: &'a [T]) -> LinearGroup<'a, T> { LinearGroup(LinearGroupBy::new(slice, PartialEq::eq)) } } group_by_wrapped!{ struct LinearGroup, &'a [T] } /// An iterator that will return non-overlapping *mutable* groups of equal elements /// in the slice using *linear/sequential search*. /// /// It will give two contiguous elements to the [`PartialEq::eq`] function /// therefore the slice must not be necessarily sorted. /// /// [`PartialEq::eq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html#tymethod.eq pub struct LinearGroupMut<'a, T: 'a>(LinearGroupByMut<'a, T, fn(&T, &T) -> bool>); impl<'a, T: 'a> LinearGroupMut<'a, T> where T: PartialEq, { pub fn new(slice: &'a mut [T]) -> LinearGroupMut<'a, T> { LinearGroupMut(LinearGroupByMut::new(slice, PartialEq::eq)) } } group_by_wrapped!{ struct LinearGroupMut, &'a mut [T] } slice-group-by-0.3.1/src/linear_group/linear_group_by.rs000066400000000000000000000114511442445703300233670ustar00rootroot00000000000000use std::iter::FusedIterator; use std::{mem, fmt, slice}; unsafe fn split_at_unchecked(slice: &[T], mid: usize) -> (&[T], &[T]) { (slice.get_unchecked(..mid), slice.get_unchecked(mid..)) } unsafe fn split_at_mut_unchecked(slice: &mut [T], mid: usize) -> (&mut [T], &mut [T]) { // split_at_mut_unchecked let len = slice.len(); let ptr = slice.as_mut_ptr(); // SAFETY: Caller has to check that `0 <= mid <= slice.len()`. // // `[ptr; mid]` and `[mid; len]` are not overlapping, so returning a mutable reference // is fine. (slice::from_raw_parts_mut(ptr, mid), slice::from_raw_parts_mut(ptr.add(mid), len - mid)) } pub struct LinearGroupBy<'a, T: 'a, P> { slice: &'a [T], predicate: P, } impl<'a, T: 'a, P> LinearGroupBy<'a, T, P> { pub(crate) fn new(slice: &'a [T], predicate: P) -> Self { LinearGroupBy { slice, predicate } } } impl<'a, T: 'a, P> Iterator for LinearGroupBy<'a, T, P> where P: FnMut(&T, &T) -> bool, { type Item = &'a [T]; #[inline] fn next(&mut self) -> Option { if self.slice.is_empty() { None } else { let mut len = 1; let mut iter = self.slice.windows(2); while let Some([l, r]) = iter.next() { if (self.predicate)(l, r) { len += 1 } else { break } } let (head, tail) = unsafe { split_at_unchecked(self.slice, len) }; self.slice = tail; Some(head) } } #[inline] fn size_hint(&self) -> (usize, Option) { if self.slice.is_empty() { (0, Some(0)) } else { (1, Some(self.slice.len())) } } #[inline] fn last(mut self) -> Option { self.next_back() } } impl<'a, T: 'a, P> DoubleEndedIterator for LinearGroupBy<'a, T, P> where P: FnMut(&T, &T) -> bool, { #[inline] fn next_back(&mut self) -> Option { if self.slice.is_empty() { None } else { let mut len = 1; let mut iter = self.slice.windows(2); while let Some([l, r]) = iter.next_back() { if (self.predicate)(l, r) { len += 1 } else { break } } let (head, tail) = unsafe { split_at_unchecked(self.slice, self.slice.len() - len) }; self.slice = head; Some(tail) } } } impl<'a, T: 'a, P> FusedIterator for LinearGroupBy<'a, T, P> where P: FnMut(&T, &T) -> bool {} impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for LinearGroupBy<'a, T, P> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("LinearGroupBy").field("slice", &self.slice).finish() } } pub struct LinearGroupByMut<'a, T: 'a, P> { slice: &'a mut [T], predicate: P, } impl<'a, T: 'a, P> LinearGroupByMut<'a, T, P> { pub(crate) fn new(slice: &'a mut [T], predicate: P) -> Self { LinearGroupByMut { slice, predicate } } } impl<'a, T: 'a, P> Iterator for LinearGroupByMut<'a, T, P> where P: FnMut(&T, &T) -> bool, { type Item = &'a mut [T]; #[inline] fn next(&mut self) -> Option { if self.slice.is_empty() { None } else { let mut len = 1; let mut iter = self.slice.windows(2); while let Some([l, r]) = iter.next() { if (self.predicate)(l, r) { len += 1 } else { break } } let slice = mem::take(&mut self.slice); let (head, tail) = unsafe { split_at_mut_unchecked(slice, len) }; self.slice = tail; Some(head) } } #[inline] fn size_hint(&self) -> (usize, Option) { if self.slice.is_empty() { (0, Some(0)) } else { (1, Some(self.slice.len())) } } #[inline] fn last(mut self) -> Option { self.next_back() } } impl<'a, T: 'a, P> DoubleEndedIterator for LinearGroupByMut<'a, T, P> where P: FnMut(&T, &T) -> bool, { #[inline] fn next_back(&mut self) -> Option { if self.slice.is_empty() { None } else { let mut len = 1; let mut iter = self.slice.windows(2); while let Some([l, r]) = iter.next_back() { if (self.predicate)(l, r) { len += 1 } else { break } } let slice = mem::take(&mut self.slice); let (head, tail) = unsafe { split_at_mut_unchecked(slice, slice.len() - len) }; self.slice = head; Some(tail) } } } impl<'a, T: 'a, P> FusedIterator for LinearGroupByMut<'a, T, P> where P: FnMut(&T, &T) -> bool {} impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for LinearGroupByMut<'a, T, P> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("LinearGroupByMut").field("slice", &self.slice).finish() } } slice-group-by-0.3.1/src/linear_group/linear_group_by_key.rs000066400000000000000000000165251442445703300242460ustar00rootroot00000000000000use crate::offset_from; use std::slice::{from_raw_parts, from_raw_parts_mut}; use std::{fmt, marker}; macro_rules! group_by_key { (struct $name:ident, $elem:ty, $mkslice:ident) => { impl<'a, T: 'a, P> $name<'a, T, P> { #[inline] pub fn is_empty(&self) -> bool { self.ptr == self.end } #[inline] pub fn remainder_len(&self) -> usize { unsafe { offset_from(self.end, self.ptr) } } } impl<'a, T: 'a, F, K> std::iter::Iterator for $name<'a, T, F> where F: FnMut(&T) -> K, K: PartialEq, { type Item = $elem; fn next(&mut self) -> Option { if self.is_empty() { return None; } let mut i = 0; let mut ptr = self.ptr; // we use an unsafe block to avoid bounds checking here. // this is safe because the only thing we do here is to get // two elements at `ptr` and `ptr + 1`, bounds checking is done by hand. // we need to get *two* contiguous elements so we check that: // - the first element is at the `end - 1` position because // - the second one will be read from `ptr + 1` that must // be lower or equal to `end` unsafe { while ptr != self.end.sub(1) { let a = &*ptr; ptr = ptr.add(1); let b = &*ptr; i += 1; if (self.func)(a) != (self.func)(b) { let slice = $mkslice(self.ptr, i); self.ptr = ptr; return Some(slice); } } } // `i` is either `0` or the `slice length - 1` because either: // - we have not entered the loop and so `i` is equal to `0` // the slice length is necessarily `1` because we ensure it is not empty // - we have entered the loop and we have not early returned // so `i` is equal to the slice `length - 1` let slice = unsafe { $mkslice(self.ptr, i + 1) }; self.ptr = self.end; Some(slice) } fn size_hint(&self) -> (usize, Option) { if self.is_empty() { return (0, Some(0)); } let len = self.remainder_len(); (1, Some(len)) } fn last(mut self) -> Option { self.next_back() } } impl<'a, T: 'a, F, K> std::iter::DoubleEndedIterator for $name<'a, T, F> where F: FnMut(&T) -> K, K: PartialEq, { fn next_back(&mut self) -> Option { // during the loop we retrieve two elements at `ptr` and `ptr - 1`. if self.is_empty() { return None; } let mut i = 0; unsafe { // we ensure that the first element that will be read // is not under `end` because `end` is out of bound. let mut ptr = self.end.sub(1); while ptr != self.ptr { // we first get `a` that is at the left of `ptr` // then `b` that is under the `ptr` position. let a = &*ptr.sub(1); let b = &*ptr; i += 1; if (self.func)(a) != (self.func)(b) { // the slice to return starts at the `ptr` position // and `i` is the length of it. let slice = $mkslice(ptr, i); // because `end` is always an invalid bound // we use `ptr` as `end` for the future call to `next`. self.end = ptr; return Some(slice); } ptr = ptr.sub(1); } } let slice = unsafe { $mkslice(self.ptr, i + 1) }; self.ptr = self.end; Some(slice) } } impl<'a, T: 'a, F, K> std::iter::FusedIterator for $name<'a, T, F> where F: FnMut(&T) -> K, K: PartialEq, { } }; } /// An iterator that will return non-overlapping groups of equal elements /// in the slice using *linear/sequential search*. /// /// It will give an element to the given function, producing a key and comparing /// the keys to determine groups. pub struct LinearGroupByKey<'a, T: 'a, F> { ptr: *const T, end: *const T, func: F, _phantom: marker::PhantomData<&'a T>, } impl<'a, T, F> LinearGroupByKey<'a, T, F> { pub fn new(slice: &'a [T], func: F) -> Self { LinearGroupByKey { ptr: slice.as_ptr(), end: unsafe { slice.as_ptr().add(slice.len()) }, func, _phantom: marker::PhantomData, } } } impl<'a, T: 'a, F> LinearGroupByKey<'a, T, F> { /// Returns the remainder of the original slice that is going to be /// returned by the iterator. pub fn remainder(&self) -> &[T] { let len = self.remainder_len(); unsafe { from_raw_parts(self.ptr, len) } } } impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for LinearGroupByKey<'a, T, P> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("LinearGroupByKey") .field("remainder", &self.remainder()) .finish() } } group_by_key! { struct LinearGroupByKey, &'a [T], from_raw_parts } /// An iterator that will return non-overlapping *mutable* groups in the slice /// using *linear/sequential search*. /// /// It will give an element to the given function, producing a key and comparing /// the keys to determine groups. pub struct LinearGroupByKeyMut<'a, T: 'a, F> { ptr: *mut T, end: *mut T, func: F, _phantom: marker::PhantomData<&'a mut T>, } impl<'a, T, F> LinearGroupByKeyMut<'a, T, F> { pub fn new(slice: &'a mut [T], func: F) -> Self { let ptr = slice.as_mut_ptr(); let end = unsafe { ptr.add(slice.len()) }; LinearGroupByKeyMut { ptr, end, func, _phantom: marker::PhantomData, } } } impl<'a, T: 'a, F> LinearGroupByKeyMut<'a, T, F> { /// Returns the remainder of the original slice that is going to be /// returned by the iterator. pub fn into_remainder(self) -> &'a mut [T] { let len = self.remainder_len(); unsafe { from_raw_parts_mut(self.ptr, len) } } } impl<'a, T: 'a + fmt::Debug, F> fmt::Debug for LinearGroupByKeyMut<'a, T, F> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let len = self.remainder_len(); let remainder = unsafe { from_raw_parts(self.ptr, len) }; f.debug_struct("LinearGroupByKeyMut") .field("remainder", &remainder) .finish() } } group_by_key! { struct LinearGroupByKeyMut, &'a mut [T], from_raw_parts_mut } slice-group-by-0.3.1/src/linear_group/mod.rs000066400000000000000000000215351442445703300207720ustar00rootroot00000000000000mod linear_group; mod linear_group_by; mod linear_group_by_key; pub use self::linear_group::{LinearGroup, LinearGroupMut}; pub use self::linear_group_by::{LinearGroupBy, LinearGroupByMut}; pub use self::linear_group_by_key::{LinearGroupByKey, LinearGroupByKeyMut}; #[cfg(test)] mod tests { use super::*; #[derive(Debug, Eq)] enum Guard { Valid(i32), Invalid(i32), } impl PartialEq for Guard { fn eq(&self, other: &Self) -> bool { match (self, other) { (Guard::Valid(_), Guard::Valid(_)) => true, (a, b) => panic!("denied read on Guard::Invalid variant ({:?}, {:?})", a, b), } } } #[test] fn one_big_group() { let slice = &[1, 1, 1, 1]; let mut iter = LinearGroup::new(slice); assert_eq!(iter.next(), Some(&[1, 1, 1, 1][..])); assert_eq!(iter.next(), None); } #[test] fn two_equal_groups() { let slice = &[1, 1, 1, 1, 2, 2, 2, 2]; let mut iter = LinearGroup::new(slice); assert_eq!(iter.next(), Some(&[1, 1, 1, 1][..])); assert_eq!(iter.next(), Some(&[2, 2, 2, 2][..])); assert_eq!(iter.next(), None); } #[test] fn two_little_equal_groups() { let slice = &[1, 2]; let mut iter = LinearGroup::new(slice); assert_eq!(iter.next(), Some(&[1][..])); assert_eq!(iter.next(), Some(&[2][..])); assert_eq!(iter.next(), None); } #[test] fn three_groups() { let slice = &[1, 1, 1, 3, 3, 2, 2, 2]; let mut iter = LinearGroup::new(slice); assert_eq!(iter.next(), Some(&[1, 1, 1][..])); assert_eq!(iter.next(), Some(&[3, 3][..])); assert_eq!(iter.next(), Some(&[2, 2, 2][..])); assert_eq!(iter.next(), None); } #[test] fn three_little_groups() { let slice = &[1, 3, 2]; let mut iter = LinearGroup::new(slice); assert_eq!(iter.next(), Some(&[1][..])); assert_eq!(iter.next(), Some(&[3][..])); assert_eq!(iter.next(), Some(&[2][..])); assert_eq!(iter.next(), None); } #[test] fn overflow() { let slice = &[Guard::Invalid(0), Guard::Valid(1), Guard::Valid(2), Guard::Invalid(3)]; let mut iter = LinearGroup::new(&slice[1..3]); assert_eq!(iter.next(), Some(&[Guard::Valid(1), Guard::Valid(2)][..])); assert_eq!(iter.next(), None); } #[test] fn last_three_little_groups() { let slice = &[1, 3, 2]; let iter = LinearGroup::new(slice); assert_eq!(iter.last(), Some(&[2][..])); } #[test] fn last_three_groups() { let slice = &[1, 1, 1, 3, 3, 2, 2, 2]; let iter = LinearGroup::new(slice); assert_eq!(iter.last(), Some(&[2, 2, 2][..])); } #[test] fn last_overflow() { let slice = &[Guard::Invalid(0), Guard::Valid(1), Guard::Valid(2), Guard::Invalid(3)]; println!("{:?}", (&slice[1..3]).as_ptr()); let iter = LinearGroup::new(&slice[1..3]); assert_eq!(iter.last(), Some(&[Guard::Valid(1), Guard::Valid(2)][..])); } #[test] fn back_empty_slice() { let slice: &[i32] = &[]; let mut iter = LinearGroup::new(slice); assert_eq!(iter.next_back(), None); } #[test] fn back_one_little_group() { let slice = &[1]; let mut iter = LinearGroup::new(slice); assert_eq!(iter.next_back(), Some(&[1][..])); assert_eq!(iter.next_back(), None); assert_eq!(iter.next(), None); } #[test] fn back_three_little_groups() { let slice = &[1, 3, 2]; let mut iter = LinearGroup::new(slice); assert_eq!(iter.next_back(), Some(&[2][..])); assert_eq!(iter.next_back(), Some(&[3][..])); assert_eq!(iter.next_back(), Some(&[1][..])); assert_eq!(iter.next_back(), None); } #[test] fn back_three_groups() { let slice = &[1, 1, 1, 3, 3, 2, 2, 2]; let mut iter = LinearGroup::new(slice); assert_eq!(iter.next_back(), Some(&[2, 2, 2][..])); assert_eq!(iter.next_back(), Some(&[3, 3][..])); assert_eq!(iter.next_back(), Some(&[1, 1, 1][..])); assert_eq!(iter.next_back(), None); } #[test] fn double_ended_dont_cross() { let slice = &[1, 1, 1, 3, 3, 2, 2, 2]; let mut iter = LinearGroup::new(slice); assert_eq!(iter.next(), Some(&[1, 1, 1][..])); assert_eq!(iter.next_back(), Some(&[2, 2, 2][..])); assert_eq!(iter.next(), Some(&[3, 3][..])); assert_eq!(iter.next_back(), None); assert_eq!(iter.next(), None); } #[test] fn fused_iterator() { let slice = &[1, 2, 3]; let mut iter = LinearGroup::new(slice); assert_eq!(iter.next(), Some(&[1][..])); assert_eq!(iter.next(), Some(&[2][..])); assert_eq!(iter.next(), Some(&[3][..])); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); } #[test] fn back_fused_iterator() { let slice = &[1, 2, 3]; let mut iter = LinearGroup::new(slice); assert_eq!(iter.next_back(), Some(&[3][..])); assert_eq!(iter.next_back(), Some(&[2][..])); assert_eq!(iter.next_back(), Some(&[1][..])); assert_eq!(iter.next_back(), None); assert_eq!(iter.next_back(), None); } fn panic_param_ord(a: &i32, b: &i32) -> bool { if a < b { true } else { panic!("params are not in the right order") } } #[test] fn predicate_call_param_order() { let slice = &[1, 2, 3, 4, 5]; let mut iter = LinearGroupBy::new(slice, panic_param_ord); assert_eq!(iter.next(), Some(&[1, 2, 3, 4, 5][..])); assert_eq!(iter.next(), None); } #[test] fn rev_predicate_call_param_order() { let slice = &[1, 2, 3, 4, 5]; let mut iter = LinearGroupBy::new(slice, panic_param_ord); assert_eq!(iter.next_back(), Some(&[1, 2, 3, 4, 5][..])); assert_eq!(iter.next_back(), None); } #[test] fn group_by_key_mut() { let slice = &mut [1, 2, 4, 5, 7, 8, 8]; let mut iter = LinearGroupByKeyMut::new(slice, |i: &i32| *i % 2); assert_eq!(iter.next(), Some(&mut [1][..])); assert_eq!(iter.next(), Some(&mut [2, 4][..])); assert_eq!(iter.next(), Some(&mut [5, 7][..])); assert_eq!(iter.next(), Some(&mut [8, 8][..])); assert_eq!(iter.next(), None); } } #[cfg(all(feature = "nightly", test))] mod bench { extern crate test; extern crate rand; use super::*; use self::rand::{Rng, SeedableRng}; use self::rand::rngs::StdRng; use self::rand::distributions::Alphanumeric; #[bench] fn vector_16_000(b: &mut test::Bencher) { let mut rng = StdRng::from_seed([42; 32]); let len = 16_000; let mut vec = Vec::with_capacity(len); for _ in 0..len { vec.push(rng.sample(Alphanumeric)); } b.iter(|| { let group_by = LinearGroup::new(vec.as_slice()); test::black_box(group_by.count()) }) } #[bench] fn vector_16_000_sorted(b: &mut test::Bencher) { let mut rng = StdRng::from_seed([42; 32]); let len = 16_000; let mut vec = Vec::with_capacity(len); for _ in 0..len { vec.push(rng.sample(Alphanumeric)); } vec.sort_unstable(); b.iter(|| { let group_by = LinearGroup::new(vec.as_slice()); test::black_box(group_by.count()) }) } #[bench] fn vector_little_sorted(b: &mut test::Bencher) { let mut rng = StdRng::from_seed([42; 32]); let len = 30; let mut vec = Vec::with_capacity(len); for _ in 0..len { vec.push(rng.sample(Alphanumeric)); } vec.sort_unstable(); b.iter(|| { let group_by = LinearGroup::new(vec.as_slice()); test::black_box(group_by.count()) }) } #[bench] fn vector_16_000_one_group(b: &mut test::Bencher) { let vec = vec![1; 16_000]; b.iter(|| { let group_by = LinearGroup::new(vec.as_slice()); test::black_box(group_by.count()) }) } #[bench] fn rev_vector_16_000(b: &mut test::Bencher) { let mut rng = StdRng::from_seed([42; 32]); let len = 16_000; let mut vec = Vec::with_capacity(len); for _ in 0..len { vec.push(rng.sample(Alphanumeric)); } b.iter(|| { let group_by = LinearGroup::new(vec.as_slice()); test::black_box(group_by.rev().count()) }) } #[bench] fn rev_vector_16_000_one_group(b: &mut test::Bencher) { let vec = vec![1; 16_000]; b.iter(|| { let group_by = LinearGroup::new(vec.as_slice()); test::black_box(group_by.rev().count()) }) } } slice-group-by-0.3.1/src/linear_str_group/000077500000000000000000000000001442445703300205275ustar00rootroot00000000000000slice-group-by-0.3.1/src/linear_str_group/linear_str_group.rs000066400000000000000000000024671442445703300244640ustar00rootroot00000000000000use crate::{LinearStrGroupBy, LinearStrGroupByMut}; /// An iterator that will return non-overlapping groups of equal `char` /// in the `str` using *linear/sequential search*. /// /// It will use the `char` [`PartialEq::eq`] function. /// /// [`PartialEq::eq`]: https://doc.rust-lang.org/std/primitive.char.html#impl-PartialEq%3Cchar%3E pub struct LinearStrGroup<'a>(LinearStrGroupBy<'a, fn(char, char) -> bool>); impl<'a> LinearStrGroup<'a> { pub fn new(string: &'a str) -> Self { LinearStrGroup(LinearStrGroupBy::new(string, |a, b| a == b)) } } str_group_by_wrapped!{ struct LinearStrGroup, &'a str } /// An iterator that will return non-overlapping *mutable* groups of equal `char` /// in the `str` using *linear/sequential search*. /// /// It will use the `char` [`PartialEq::eq`] function. /// /// [`PartialEq::eq`]: https://doc.rust-lang.org/std/primitive.char.html#impl-PartialEq%3Cchar%3E pub struct LinearStrGroupMut<'a>(LinearStrGroupByMut<'a, fn(char, char) -> bool>); impl<'a> LinearStrGroupMut<'a> { pub fn new(string: &'a mut str) -> LinearStrGroupMut { LinearStrGroupMut(LinearStrGroupByMut::new(string, |a, b| a == b)) } #[inline] pub fn as_str_mut(&mut self) -> &mut str { self.0.as_str_mut() } } str_group_by_wrapped!{ struct LinearStrGroupMut, &'a mut str } slice-group-by-0.3.1/src/linear_str_group/linear_str_group_by.rs000066400000000000000000000076061442445703300251560ustar00rootroot00000000000000use std::mem; use super::{str_as_ptr, str_as_mut_ptr, str_from_raw_parts, str_from_raw_parts_mut}; macro_rules! str_group_by { (struct $name:ident, $elem:ty, $as_ptr:ident, $as_str:ident) => { impl<'a, P> $name<'a, P> { #[inline] pub fn as_str(&self) -> &str { self.inner } #[inline] pub fn is_empty(&self) -> bool { self.inner.is_empty() } #[inline] pub fn remainder_len(&self) -> usize { self.inner.len() } } impl<'a, P> std::iter::Iterator for $name<'a, P> where P: FnMut(char, char) -> bool, { type Item = $elem; #[inline] fn next(&mut self) -> Option { if self.inner.is_empty() { return None } let mut iter = self.inner.char_indices().peekable(); while let (Some((_, ac)), Some((bi, bc))) = (iter.next(), iter.peek().cloned()) { if !(self.predicate)(ac, bc) { let len = self.inner.len(); let ptr = $as_ptr(self.inner); let left = unsafe { $as_str(ptr, bi) }; let right = unsafe { $as_str(ptr.add(bi), len - bi) }; self.inner = right; return Some(left); } } let output = mem::replace(&mut self.inner, Default::default()); return Some(output); } fn last(mut self) -> Option { self.next_back() } } impl<'a, P> std::iter::DoubleEndedIterator for $name<'a, P> where P: FnMut(char, char) -> bool, { #[inline] fn next_back(&mut self) -> Option { if self.inner.is_empty() { return None } let mut iter = self.inner.char_indices().rev().peekable(); while let (Some((ai, ac)), Some((_, bc))) = (iter.next(), iter.peek().cloned()) { if !(self.predicate)(ac, bc) { let len = self.inner.len(); let ptr = $as_ptr(self.inner); let left = unsafe { $as_str(ptr, ai) }; let right = unsafe { $as_str(ptr.add(ai), len - ai) }; self.inner = left; return Some(right); } } let output = mem::replace(&mut self.inner, Default::default()); return Some(output); } } impl<'a, P> std::iter::FusedIterator for $name<'a, P> where P: FnMut(char, char) -> bool, { } } } /// An iterator that will return non-overlapping groups in the `str` /// using *linear/sequential search*. /// /// It will give two contiguous `char` to the predicate function. pub struct LinearStrGroupBy<'a, P> { inner: &'a str, predicate: P, } impl<'a, P> LinearStrGroupBy<'a, P> { pub fn new(string: &'a str, predicate: P) -> Self { Self { inner: string, predicate } } } str_group_by!{ struct LinearStrGroupBy, &'a str, str_as_ptr, str_from_raw_parts } /// An iterator that will return non-overlapping *mutable* groups in the `str` /// using *linear/sequential search*. /// /// It will give two contiguous `char` to the predicate function. pub struct LinearStrGroupByMut<'a, P> { inner: &'a mut str, predicate: P, } impl<'a, P> LinearStrGroupByMut<'a, P> { pub fn new(string: &'a mut str, predicate: P) -> Self { Self { inner: string, predicate } } #[inline] pub fn as_str_mut(&mut self) -> &mut str { &mut self.inner } } str_group_by!{ struct LinearStrGroupByMut, &'a mut str, str_as_mut_ptr, str_from_raw_parts_mut } slice-group-by-0.3.1/src/linear_str_group/linear_str_group_by_key.rs000066400000000000000000000100741442445703300260170ustar00rootroot00000000000000use std::mem; use super::{str_as_ptr, str_as_mut_ptr, str_from_raw_parts, str_from_raw_parts_mut}; macro_rules! str_group_by_key { (struct $name:ident, $elem:ty, $as_ptr:ident, $as_str:ident) => { impl<'a, F> $name<'a, F> { #[inline] pub fn as_str(&self) -> &str { self.inner } #[inline] pub fn is_empty(&self) -> bool { self.inner.is_empty() } #[inline] pub fn remainder_len(&self) -> usize { self.inner.len() } } impl<'a, F, K> std::iter::Iterator for $name<'a, F> where F: FnMut(char) -> K, K: PartialEq, { type Item = $elem; #[inline] fn next(&mut self) -> Option { if self.inner.is_empty() { return None } let mut iter = self.inner.char_indices().peekable(); while let (Some((_, ac)), Some((bi, bc))) = (iter.next(), iter.peek().cloned()) { if (self.func)(ac) != (self.func)(bc) { let len = self.inner.len(); let ptr = $as_ptr(self.inner); let left = unsafe { $as_str(ptr, bi) }; let right = unsafe { $as_str(ptr.add(bi), len - bi) }; self.inner = right; return Some(left); } } let output = mem::replace(&mut self.inner, Default::default()); return Some(output); } fn last(mut self) -> Option { self.next_back() } } impl<'a, F, K> std::iter::DoubleEndedIterator for $name<'a, F> where F: FnMut(char) -> K, K: PartialEq, { #[inline] fn next_back(&mut self) -> Option { if self.inner.is_empty() { return None } let mut iter = self.inner.char_indices().rev().peekable(); while let (Some((ai, ac)), Some((_, bc))) = (iter.next(), iter.peek().cloned()) { if (self.func)(ac) != (self.func)(bc) { let len = self.inner.len(); let ptr = $as_ptr(self.inner); let left = unsafe { $as_str(ptr, ai) }; let right = unsafe { $as_str(ptr.add(ai), len - ai) }; self.inner = left; return Some(right); } } let output = mem::replace(&mut self.inner, Default::default()); return Some(output); } } impl<'a, F, K> std::iter::FusedIterator for $name<'a, F> where F: FnMut(char) -> K, K: PartialEq, { } } } /// An iterator that will return non-overlapping groups in the `str` /// using *linear/sequential search*. /// /// It will give an element to the given function, producing a key and comparing /// the keys to determine groups. pub struct LinearStrGroupByKey<'a, F> { inner: &'a str, func: F, } impl<'a, F> LinearStrGroupByKey<'a, F> { pub fn new(string: &'a str, func: F) -> Self { Self { inner: string, func } } } str_group_by_key!{ struct LinearStrGroupByKey, &'a str, str_as_ptr, str_from_raw_parts } /// An iterator that will return non-overlapping *mutable* groups in the `str` /// using *linear/sequential search*. /// /// It will give an element to the given function, producing a key and comparing /// the keys to determine groups. pub struct LinearStrGroupByKeyMut<'a, F> { inner: &'a mut str, func: F, } impl<'a, F> LinearStrGroupByKeyMut<'a, F> { pub fn new(string: &'a mut str, func: F) -> Self { Self { inner: string, func } } #[inline] pub fn as_str_mut(&mut self) -> &mut str { &mut self.inner } } str_group_by_key!{ struct LinearStrGroupByKeyMut, &'a mut str, str_as_mut_ptr, str_from_raw_parts_mut } slice-group-by-0.3.1/src/linear_str_group/mod.rs000066400000000000000000000123421442445703300216560ustar00rootroot00000000000000macro_rules! str_group_by_wrapped { (struct $name:ident, $elem:ty) => { impl<'a> $name<'a> { #[inline] pub fn as_str(&self) -> &str { self.0.as_str() } #[inline] pub fn is_empty(&self) -> bool { self.0.is_empty() } #[inline] pub fn remainder_len(&self) -> usize { self.0.remainder_len() } } impl<'a> std::iter::Iterator for $name<'a> { type Item = $elem; #[inline] fn next(&mut self) -> Option { self.0.next() } fn last(self) -> Option { self.0.last() } } impl<'a> DoubleEndedIterator for $name<'a> { #[inline] fn next_back(&mut self) -> Option { self.0.next_back() } } impl<'a> std::iter::FusedIterator for $name<'a> { } } } mod linear_str_group; mod linear_str_group_by; mod linear_str_group_by_key; pub use self::linear_str_group::{LinearStrGroup, LinearStrGroupMut}; pub use self::linear_str_group_by::{LinearStrGroupBy, LinearStrGroupByMut}; pub use self::linear_str_group_by_key::{LinearStrGroupByKey, LinearStrGroupByKeyMut}; fn str_as_ptr(string: &str) -> *const u8 { string.as_bytes().as_ptr() } fn str_as_mut_ptr(string: &mut str) -> *mut u8 { unsafe { string.as_bytes_mut().as_mut_ptr() } } unsafe fn str_from_raw_parts<'a>(data: *const u8, len: usize) -> &'a str { let slice = std::slice::from_raw_parts(data, len); std::str::from_utf8_unchecked(slice) } unsafe fn str_from_raw_parts_mut<'a>(data: *mut u8, len: usize) -> &'a mut str { let slice = std::slice::from_raw_parts_mut(data, len); std::str::from_utf8_unchecked_mut(slice) } #[cfg(test)] mod tests { use super::*; #[test] fn str_easy() { let string = "aaaabbbbbaacccc"; let mut iter = LinearStrGroup::new(string); assert_eq!(iter.next(), Some("aaaa")); assert_eq!(iter.next(), Some("bbbbb")); assert_eq!(iter.next(), Some("aa")); assert_eq!(iter.next(), Some("cccc")); assert_eq!(iter.next(), None); } #[test] fn str_mut_easy() { let mut string = String::from("aaaabbbbbaacccc"); let mut iter = LinearStrGroupMut::new(&mut string); assert_eq!(iter.next().map(|s| &*s), Some("aaaa")); assert_eq!(iter.next().map(|s| &*s), Some("bbbbb")); assert_eq!(iter.next().map(|s| &*s), Some("aa")); assert_eq!(iter.next().map(|s| &*s), Some("cccc")); assert_eq!(iter.next(), None); } #[test] fn str_kanji() { let string = "包包饰饰与与钥钥匙匙扣扣"; let mut iter = LinearStrGroup::new(string); assert_eq!(iter.next(), Some("包包")); assert_eq!(iter.next(), Some("饰饰")); assert_eq!(iter.next(), Some("与与")); assert_eq!(iter.next(), Some("钥钥")); assert_eq!(iter.next(), Some("匙匙")); assert_eq!(iter.next(), Some("扣扣")); assert_eq!(iter.next(), None); } fn is_cjk(c: char) -> bool { (c >= '\u{2e80}' && c <= '\u{2eff}') || (c >= '\u{2f00}' && c <= '\u{2fdf}') || (c >= '\u{3040}' && c <= '\u{309f}') || (c >= '\u{30a0}' && c <= '\u{30ff}') || (c >= '\u{3100}' && c <= '\u{312f}') || (c >= '\u{3200}' && c <= '\u{32ff}') || (c >= '\u{3400}' && c <= '\u{4dbf}') || (c >= '\u{4e00}' && c <= '\u{9fff}') || (c >= '\u{f900}' && c <= '\u{faff}') } #[test] fn str_ascii_cjk() { let string = "abc包包bbccdd饰饰"; let mut iter = LinearStrGroupBy::new(string, |a, b| is_cjk(a) == is_cjk(b)); assert_eq!(iter.next(), Some("abc")); assert_eq!(iter.next(), Some("包包")); assert_eq!(iter.next(), Some("bbccdd")); assert_eq!(iter.next(), Some("饰饰")); assert_eq!(iter.next(), None); } #[test] fn str_rev_easy() { let string = "aaaabbbbbaacccc"; let mut iter = LinearStrGroup::new(string).rev(); assert_eq!(iter.next(), Some("cccc")); assert_eq!(iter.next(), Some("aa")); assert_eq!(iter.next(), Some("bbbbb")); assert_eq!(iter.next(), Some("aaaa")); assert_eq!(iter.next(), None); } #[test] fn str_mut_rev_easy() { let mut string = String::from("aaaabbbbbaacccc"); let mut iter = LinearStrGroupMut::new(&mut string).rev(); assert_eq!(iter.next().map(|s| &*s), Some("cccc")); assert_eq!(iter.next().map(|s| &*s), Some("aa")); assert_eq!(iter.next().map(|s| &*s), Some("bbbbb")); assert_eq!(iter.next().map(|s| &*s), Some("aaaa")); assert_eq!(iter.next(), None); } #[test] fn str_rev_ascii_cjk() { let string = "abc包包bbccdd饰饰"; let mut iter = LinearStrGroupBy::new(string, |a, b| is_cjk(a) == is_cjk(b)).rev(); assert_eq!(iter.next(), Some("饰饰")); assert_eq!(iter.next(), Some("bbccdd")); assert_eq!(iter.next(), Some("包包")); assert_eq!(iter.next(), Some("abc")); assert_eq!(iter.next(), None); } }