From ea0f50c7507ab79a0df2afa35799b5607ec20e68 Mon Sep 17 00:00:00 2001 From: Joseph A Gerardot Date: Wed, 12 Jun 2019 05:39:34 -0400 Subject: [PATCH 1/2] Functional api (#10) * Add iterator-based slice allocator and associated test * Make lazy slice test more descriptive by limiting it both ways * Correct potential bug with small iterator size correction --- src/arena.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/arena.rs b/src/arena.rs index e00b5a4..a548135 100644 --- a/src/arena.rs +++ b/src/arena.rs @@ -192,6 +192,31 @@ impl Arena { } } + /// Allocate a statically-sized but lazily-generated slice `[T]` out of an iterator + /// This is useful if you're going to make a slice of something and put it on the arena, + /// but you don't want to make an allocation first just to have something to copy in. + /// + /// The slice will be at maximum length `n`, further elements of the iterator ignored and not evaluated. + /// If the iterator yields less than `n` elements, a shorter slice will simply be returned. + pub fn alloc_lazy_slice<'arena, T, I: Iterator>(&'arena self, vals: I, n: usize) -> &'arena [T] { + // Grab space for `n` elements even if it may turn out we have to walk it back + let ptr = self.require(n * size_of::()) as *mut T; + let mut i: usize = 0; + + unsafe { + use std::slice::from_raw_parts; + + for val in vals.take(n) { + *ptr.offset(i as isize) = val; + i += 1; + } + // Now fix the slice length and arena offset + let diff = n - i; + self.reset_to( self.offset() - diff * size_of::() ); + from_raw_parts(ptr, i) + } + } + /// Put a `Vec` on the arena without reallocating. pub fn alloc_vec<'arena, T: Copy>(&'arena self, mut val: Vec) -> &'arena [T] { use std::{mem, slice}; @@ -420,6 +445,24 @@ mod test { assert_eq!(arena.offset.get(), 8); } + #[test] + fn alloc_lazy_slices() { + let arena = Arena::new(); + let nums: [u32; 6] = [1, 2, 3, 4, 5, 1000]; + let big_nums: [u32; 6] = [100, 200, 300, 400, 500, 1050]; + + // Put the whole array in the arena + let all_nums = arena.alloc_lazy_slice(nums.iter().map(|x| *x), 6); + // Truncate it using the `n` argument + let trunc_nums = arena.alloc_lazy_slice(big_nums.iter().map(|x| *x), 3); + // Put a whole array of half the nums in the arena + let half_nums = arena.alloc_lazy_slice(nums[0..3].iter().map(|x| *x), 6); + + assert!(nums.iter().eq(all_nums.iter())); + assert!(nums[0..3].iter().eq(half_nums.iter())); + assert!(big_nums[0..3].iter().eq(trunc_nums.iter())); + } + #[test] fn aligns_slice_allocs() { let arena = Arena::new(); From 8cff0c97ab34a70f1d45f39d854489637a0215c5 Mon Sep 17 00:00:00 2001 From: Maciej Hirsz Date: Wed, 12 Jun 2019 11:40:54 +0200 Subject: [PATCH 2/2] Version bump --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1c0083e..62bb750 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "toolshed" -version = "0.8.0" +version = "0.8.1" authors = ["maciejhirsz "] license = "MIT/Apache-2.0" description = "Arena allocator and a handful of useful data structures"