zerobuf/
lib.rs

1//! A growable chunk of zeroed memory.
2
3pub mod params;
4
5use std::alloc;
6use std::alloc::Layout;
7use std::hash::{Hash, Hasher};
8use std::ptr;
9use std::ptr::NonNull;
10use std::mem;
11use std::slice;
12use std::ops;
13pub use self::params::{DefaultParams, Params};
14
15// https://docs.rs/owned-alloc/0.2.0/src/owned_alloc/raw_vec.rs.html#32-36
16// std:: RawVec
17
18#[derive(Clone, Debug)]
19pub struct ZeroBufInner<T> {
20    ptr: NonNull<T>,
21    capacity: usize,
22}
23
24impl<T> ZeroBufInner<T> {
25    pub fn capacity(&self) -> usize {
26        self.capacity
27    }
28
29    pub fn as_slice(&self) -> &[T] {
30        // Safe because the pointer can only be invalid when the capacity is 0,
31        // and the returned slice will panic on checked access.
32        // Same with out of bounds access: it's checked by default.
33        unsafe {
34            slice::from_raw_parts(self.ptr.as_ptr(), self.capacity())
35        }
36    }
37
38    pub fn as_slice_mut(&mut self) -> &mut [T] {
39        // Safe, same reason as `as_slice` above.
40        unsafe {
41            slice::from_raw_parts_mut(self.ptr.as_ptr(), self.capacity())
42        }
43    }
44}
45
46impl<T> ops::Deref for ZeroBufInner<T> {
47    type Target = [T];
48
49    fn deref(&self) -> &[T] {
50        self.as_slice()
51    }
52}
53
54impl<T> ops::DerefMut for ZeroBufInner<T> {
55    fn deref_mut(&mut self) -> &mut [T] {
56        self.as_slice_mut()
57    }
58}
59
60impl<T: Hash> Hash for ZeroBufInner<T> {
61    fn hash<H: Hasher>(&self, state: &mut H) {
62        Hash::hash(&**self, state)
63    }
64}
65
66/// A growable chunk of continuous zeroed memory.
67/// The same idea as a `RawVec`, but less undefined behaviour.
68/// It can be used as an alternative to a `Vec` when the length is controlled
69/// externally.
70#[derive(Clone, Debug, Hash)]
71pub struct ZeroBuf<T, P: Params<T> = DefaultParams> {
72    inner: ZeroBufInner<T>,
73    params: P,
74}
75
76impl<T> ZeroBuf<T> {
77    /// Creates an empty`ZeroBuf`, with no allocated memory.
78    pub fn new() -> Self {
79        Self::with_capacity(0)
80    }
81
82    /// Creates a `ZeroBuf` with the exact specified capacity.
83    ///
84    /// ```
85    /// use zerobuf::ZeroBuf;
86    ///
87    /// let b = ZeroBuf::<u32>::with_capacity(7);
88    /// assert!(b.capacity() == 7);
89    /// ```
90    pub fn with_capacity(capacity: usize) -> Self {
91        Self::with_capacity_and_params(capacity, DefaultParams)
92    }
93}
94
95impl<T, P: Params<T>> ZeroBuf<T, P> {
96    /// Creates an empty `ZeroBuf`, with no allocated memory and the supplied
97    /// parameters.
98    pub fn new_with_params(p: P) -> Self {
99        Self::with_capacity_and_params(0, p)
100    }
101
102    /// Creates a `ZeroBuf` with the exact specified capacity and the
103    /// supplied parameters.
104    ///
105    /// ```
106    /// use zerobuf::ZeroBuf;
107    ///
108    /// let b = ZeroBuf::<u32>::with_capacity(7);
109    /// assert!(b.capacity() == 7);
110    /// ```
111    pub fn with_capacity_and_params(capacity: usize, params: P) -> Self {
112        match (capacity, mem::size_of::<T>()) {
113            (0, _) | (_, 0) => {
114                Self {
115                    inner: ZeroBufInner {
116                        ptr: NonNull::dangling(),
117                        capacity,
118                    },
119                    params,
120                }
121            }
122            (capacity, _) => {
123                Self {
124                    inner: ZeroBufInner {
125                        ptr: Self::alloc_zeroed(capacity),
126                        capacity,
127                    },
128                    params,
129                }
130            }
131        }
132    }
133
134    /// Returns the capacity of the `ZeroBuf`.
135    pub fn capacity(&self) -> usize {
136        self.inner.capacity()
137    }
138
139    /// Returns a slice view of the allocated memory.
140    /// By default the bytes are set to zero, it is the responsability of the
141    /// caller to make sure the type has a valid value when zeroed.
142    ///
143    /// ```
144    /// use zerobuf::ZeroBuf;
145    ///
146    /// let b: ZeroBuf<i32> = ZeroBuf::with_capacity(4);
147    /// for x in b.iter() {
148    ///     assert_eq!(*x, 0);
149    /// }
150    /// ```
151    pub fn as_slice(&self) -> &[T] {
152        // Safe because the pointer can only be invalid when the capacity is 0,
153        // and the returned slice will panic on checked access.
154        // Same with out of bounds access: it's checked by default.
155        unsafe {
156            slice::from_raw_parts(self.inner.ptr.as_ptr(), self.capacity())
157        }
158    }
159
160    /// Returns a mutable slice view of the allocated memory.
161    /// By default the bytes are set to zero, it is the responsability of the
162    /// caller to make sure the type has a valid value when zeroed.
163    ///
164    /// ```
165    /// use zerobuf::ZeroBuf;
166    ///
167    /// let mut b: ZeroBuf<i32> = ZeroBuf::with_capacity(3);
168    /// b[0] = 1;
169    /// b[1..=2].copy_from_slice(&[5, 7]);
170    /// assert_eq!(b.as_slice(), &[1, 5, 7]);
171    /// ```
172    pub fn as_slice_mut(&mut self) -> &mut [T] {
173        // Safe, same reason as `as_slice` above.
174        unsafe {
175            slice::from_raw_parts_mut(self.inner.ptr.as_ptr(), self.capacity())
176        }
177    }
178
179    /// Resizes the buffer to a new capacity.
180    /// If the new capacity is bigger than the current capacity, the new
181    /// memory is zeroed.
182    /// If the new capacity is smaller than the current capacity, the exceeding
183    /// elements are dropped.
184    /// This always implies a reallocation, so frequent calls should be
185    /// avoided.
186    pub fn resize(&mut self, new_capacity: usize) {
187        match new_capacity {
188            x if x == self.capacity() => (),
189            0 => {
190                // Essentially drop the ZeroBuf:
191                // Run the destructors of the elements
192                self.params.drop(&mut self.inner);
193                // Deallocate memory, if any
194                self.free();
195            }
196            new_capacity => self.realloc_zeroed(new_capacity),
197        }
198    }
199
200    /// Increase the buffer size, by allocating a new area of memory and
201    /// copying the existing elements.
202    /// This can be used when the optimal size of the buffer is unknown,
203    /// by growing when it is full.
204    ///
205    /// ```
206    /// use zerobuf::ZeroBuf;
207    ///
208    /// let mut b: ZeroBuf<i32> = ZeroBuf::with_capacity(4);
209    /// b.grow();
210    /// assert!(b.capacity() > 4);
211    /// ```
212    pub fn grow(&mut self) {
213        let new_capacity = self.params.next_capacity(&mut self.inner);
214        self.resize(new_capacity);
215    }
216
217    fn alloc_zeroed(size: usize) -> NonNull<T> {
218        let layout = Self::make_layout(size);
219        let ptr = unsafe {
220            alloc::alloc_zeroed(layout)
221        };
222
223        NonNull::new(ptr).expect("allocation failed").cast()
224    }
225
226    fn realloc_zeroed(&mut self, size: usize) {
227        // size != 0 && size != old_size
228        let old_layout = Self::make_layout(size);
229        let old_size = self.capacity();
230        let layout = Self::make_layout(size);
231
232        if mem::size_of::<T>() == 0 {
233            if size < old_size {
234                self.params.drop(&mut self.inner[size..old_size]);
235            }
236            self.inner.capacity = size;
237            return;
238        }
239
240        let ptr = unsafe {
241            match old_size {
242                0 => Self::alloc_zeroed(size).cast().as_ptr(),
243                _ => alloc::realloc(self.inner.ptr.cast().as_ptr(), old_layout, layout.size()),
244            }
245        };
246        let ptr = NonNull::new(ptr).expect("allocation failed").cast();
247
248        if size > old_size {
249            // Zero out the new memory
250            unsafe {
251                // We start at ptr[old_size], which is
252                // ptr + size_of::<T>() * old_size
253                // And write n bytes, size_of::<T>() times the number
254                // of new elements.
255                let index_zero: *mut T = ptr.as_ptr();
256                let start = index_zero.add(old_size);
257                let count = size - old_size;
258                // count is not number of bytes, but number of T elements
259                ptr::write_bytes(start as *mut T, 0, count);
260            }
261        } else if size < old_size {
262            // Drop the truncated elements
263            self.params.drop(&mut self.inner[size..old_size]);
264        }
265
266        self.inner.ptr = ptr;
267        self.inner.capacity = size;
268    }
269
270    fn free(&mut self) {
271        // Free the allocated memory, if any.
272        // Any elements must have been dropped before this point to prevent
273        // memory leaks.
274        if mem::size_of::<T>() > 0 && self.capacity() > 0 {
275            let layout = Self::make_layout(self.capacity());
276            unsafe {
277                alloc::dealloc(self.inner.ptr.cast().as_ptr(), layout);
278            }
279            self.inner.ptr = NonNull::dangling();
280        }
281        self.inner.capacity = 0;
282    }
283
284    fn make_layout(size: usize) -> Layout {
285        let total_size = mem::size_of::<T>().checked_mul(size).expect("capacity overflow");
286        Layout::from_size_align(total_size, mem::align_of::<T>()).expect("layout error")
287    }
288
289    /// Helper function to verify that the default value is the expected one.
290    /// It is unsafe because if the value contains pointers, those pointers
291    /// will be invalid and dereferencing them is unsafe.
292    /// ```
293    /// use zerobuf::ZeroBuf;
294    ///
295    /// unsafe {
296    ///   assert_eq!(ZeroBuf::<u8>::default_value(), 0);
297    /// }
298    /// ```
299    pub unsafe fn default_value() -> T {
300        mem::zeroed()
301    }
302}
303
304impl<T, P: Params<T>> Drop for ZeroBuf<T, P> {
305    fn drop(&mut self) {
306        self.resize(0)
307    }
308}
309
310impl<T> Default for ZeroBuf<T> {
311    fn default() -> Self {
312        Self::new()
313    }
314}
315
316impl<T, P: Params<T> + Default> Default for ZeroBuf<T, P> {
317    fn default() -> Self {
318        Self::new_with_params(P::default())
319    }
320}
321
322impl<T, P: Params<T>> ops::Deref for ZeroBuf<T, P> {
323    type Target = [T];
324
325    fn deref(&self) -> &[T] {
326        self.as_slice()
327    }
328}
329
330impl<T, P: Params<T>> ops::DerefMut for ZeroBuf<T, P> {
331    fn deref_mut(&mut self) -> &mut [T] {
332        self.as_slice_mut()
333    }
334}
335
336impl<T, I> ops::Index<I> for ZeroBuf<T>
337where
338    I: slice::SliceIndex<[T]>,
339{
340    type Output = I::Output;
341
342    #[inline]
343    fn index(&self, index: I) -> &Self::Output {
344        ops::Index::index(&**self, index)
345    }
346}
347
348impl<T, I> ops::IndexMut<I> for ZeroBuf<T>
349where
350    I: slice::SliceIndex<[T]>,
351{
352    #[inline]
353    fn index_mut(&mut self, index: I) -> &mut Self::Output {
354        ops::IndexMut::index_mut(&mut **self, index)
355    }
356}
357
358#[cfg(test)]
359mod tests {
360    use super::*;
361    use std::fmt::Debug;
362
363    fn check_what_is_zero<T: Clone + Debug + PartialEq>() -> T {
364        let b = ZeroBuf::<T>::with_capacity(1);
365        let z = b[0].clone();
366        assert_eq!(z, unsafe { mem::zeroed() });
367        z
368    }
369
370    #[test]
371    fn zero_value_primitives() {
372        assert_eq!(check_what_is_zero::<u8>(), 0);
373        assert_eq!(check_what_is_zero::<u16>(), 0);
374        assert_eq!(check_what_is_zero::<u32>(), 0);
375        assert_eq!(check_what_is_zero::<u64>(), 0);
376        assert_eq!(check_what_is_zero::<u128>(), 0);
377        assert_eq!(check_what_is_zero::<i8>(), 0);
378        assert_eq!(check_what_is_zero::<i16>(), 0);
379        assert_eq!(check_what_is_zero::<i32>(), 0);
380        assert_eq!(check_what_is_zero::<i64>(), 0);
381        assert_eq!(check_what_is_zero::<i128>(), 0);
382        assert_eq!(check_what_is_zero::<bool>(), false);
383        assert_eq!(check_what_is_zero::<char>(), '\x00');
384    }
385    
386    #[test]
387    fn zero_value_pointers() {
388        use std::rc::Rc;
389        assert_eq!(check_what_is_zero::<Option<Rc<String>>>(), None);
390        assert_eq!(check_what_is_zero::<Option<String>>(), None);
391        assert_eq!(check_what_is_zero::<Option<Vec<u8>>>(), None);
392    }
393
394    #[test]
395    fn resize_to_zero() {
396        // Resize from 1 to 0
397        let mut b: ZeroBuf<u8> = ZeroBuf::with_capacity(1);
398        b.resize(0);
399        assert_eq!(b.capacity(), 0);
400
401        // Resize from 0 to 0
402        let mut b: ZeroBuf<u8> = ZeroBuf::new();
403        b.resize(0);
404        assert_eq!(b.capacity(), 0);
405    }
406
407    #[test]
408    fn simulate_push() {
409        // Simulate push in a very inefficient way, growing 1 element at a time
410        let mut b: ZeroBuf<u8> = ZeroBuf::with_capacity(0);
411        let mut v = vec![];
412        
413        let push = |b: &mut ZeroBuf<u8>, value| {
414            let cap = b.capacity();
415            b.resize(cap + 1);
416            b[cap] = value;
417        };
418
419        assert_eq!(b[..], v[..]);
420        push(&mut b, 1);
421        v.push(1);
422        assert_eq!(b[..], v[..]);
423        push(&mut b, 3);
424        v.push(3);
425        assert_eq!(b[..], v[..]);
426        for i in 0..100 {
427            push(&mut b, i);
428            v.push(i);
429            assert_eq!(b[..], v[..]);
430        }
431    }
432
433    #[test]
434    fn growth() {
435        let mut b: ZeroBuf<u32> = ZeroBuf::with_capacity(0);
436        let incr = |b: &mut ZeroBuf<u32>| {
437            for x in b.iter_mut() {
438                *x += 1;
439            }
440        };
441        let n = 10;
442        for _ in 0..n {
443            b.grow();
444            incr(&mut b);
445        }
446        assert!(b.capacity() > 0);
447        // Assume 2x growth
448        assert_eq!(b.iter().fold(0, |acc, x| acc + x), (1 << n) - 1);
449    }
450
451    #[test]
452    fn destructor() {
453        use std::rc::Rc;
454        let c = 16;
455        let mut b: ZeroBuf<Option<Rc<String>>> = ZeroBuf::with_capacity(c);
456        let hi = Rc::new(String::from("Hi"));
457        assert_eq!(Rc::strong_count(&hi), 1);
458
459        for x in b.iter_mut() {
460            *x = Some(Rc::clone(&hi));
461        }
462
463        assert_eq!(Rc::strong_count(&hi), c + 1);
464
465        // Drop one
466        b.resize(c - 1);
467        assert_eq!(Rc::strong_count(&hi), c + 1 - 1);
468
469        // Drop all
470        mem::drop(b);
471        assert_eq!(Rc::strong_count(&hi), 1);
472    }
473
474    #[test]
475    fn zero_sized() {
476        struct Foo;
477
478        impl Drop for Foo {
479            fn drop(&mut self) {
480                println!("Dropping!");
481            }
482        }
483
484        let mut b: ZeroBuf<Foo> = ZeroBuf::with_capacity(10);
485        assert_eq!(b.capacity(), 10);
486
487        b.resize(15);
488        assert_eq!(b.capacity(), 15);
489
490        b.resize(0);
491        assert_eq!(b.capacity(), 0);
492    }
493
494    #[test]
495    fn hash_equal() {
496        use std::hash::{Hash, Hasher};
497        use std::collections::hash_map::DefaultHasher;
498
499        fn hash_u64<T: Hash>(x: &T) -> u64 {
500            let mut h = DefaultHasher::new();
501            x.hash(&mut h);
502            h.finish()
503        }
504
505        let a: ZeroBuf<u8> = ZeroBuf::new();
506        let b: ZeroBuf<u8> = ZeroBuf::new();
507        assert_eq!(hash_u64(&a), hash_u64(&b));
508
509        let a: ZeroBuf<u8> = ZeroBuf::with_capacity(10);
510        let mut b: ZeroBuf<u8> = ZeroBuf::with_capacity(10);
511        assert_eq!(hash_u64(&a), hash_u64(&b));
512
513        // Check that hashes can be different
514        // Technically, this could have failed with a probability of 2^-64
515        b[2] = 99;
516        assert!(hash_u64(&a) != hash_u64(&b));
517    }
518}