Skip to content

Commit 7899699

Browse files
committed
Implement fn alloc_bytes for Arena and fn clear for both arena types
Closes #18471 Closes #18261
1 parent 5f1b1ec commit 7899699

File tree

1 file changed

+143
-18
lines changed

1 file changed

+143
-18
lines changed

src/libarena/lib.rs

Lines changed: 143 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ use std::intrinsics;
4646
use std::marker::{PhantomData, Send};
4747
use std::mem;
4848
use std::ptr;
49+
use std::slice;
4950

5051
use alloc::heap;
5152
use alloc::raw_vec::RawVec;
@@ -122,9 +123,8 @@ impl Chunk {
122123
/// plain-old-data (`Copy` types) and means we don't need to waste time running
123124
/// their destructors.
124125
pub struct Arena<'longer_than_self> {
125-
// The head is separated out from the list as a unbenchmarked
126-
// microoptimization, to avoid needing to case on the list to access the
127-
// head.
126+
// The heads are separated out from the list as a unbenchmarked
127+
// microoptimization, to avoid needing to case on the list to access a head.
128128
head: RefCell<Chunk>,
129129
copy_head: RefCell<Chunk>,
130130
chunks: RefCell<Vec<Chunk>>,
@@ -329,6 +329,37 @@ impl<'longer_than_self> Arena<'longer_than_self> {
329329
}
330330
}
331331
}
332+
333+
/// Allocates a slice of bytes of requested length. The bytes are not guaranteed to be zero
334+
/// if the arena has previously been cleared.
335+
///
336+
/// # Panics
337+
///
338+
/// Panics if the requested length is too large and causes overflow.
339+
pub fn alloc_bytes(&self, len: usize) -> &mut [u8] {
340+
unsafe {
341+
// Check for overflow.
342+
self.copy_head.borrow().fill.get().checked_add(len).expect("length overflow");
343+
let ptr = self.alloc_copy_inner(len, 1);
344+
intrinsics::assume(!ptr.is_null());
345+
slice::from_raw_parts_mut(ptr as *mut _, len)
346+
}
347+
}
348+
349+
/// Clears the arena. Deallocates all but the longest chunk which may be reused.
350+
pub fn clear(&mut self) {
351+
unsafe {
352+
self.head.borrow().destroy();
353+
self.head.borrow().fill.set(0);
354+
self.copy_head.borrow().fill.set(0);
355+
for chunk in self.chunks.borrow().iter() {
356+
if !chunk.is_copy.get() {
357+
chunk.destroy();
358+
}
359+
}
360+
self.chunks.borrow_mut().clear();
361+
}
362+
}
332363
}
333364

334365
#[test]
@@ -495,6 +526,45 @@ impl<T> TypedArena<T> {
495526
}
496527
}
497528
}
529+
/// Clears the arena. Deallocates all but the longest chunk which may be reused.
530+
pub fn clear(&mut self) {
531+
unsafe {
532+
// Clear the last chunk, which is partially filled.
533+
let mut chunks_borrow = self.chunks.borrow_mut();
534+
let last_idx = chunks_borrow.len() - 1;
535+
self.clear_last_chunk(&mut chunks_borrow[last_idx]);
536+
// If `T` is ZST, code below has no effect.
537+
for mut chunk in chunks_borrow.drain(..last_idx) {
538+
let cap = chunk.storage.cap();
539+
chunk.destroy(cap);
540+
}
541+
}
542+
}
543+
544+
// Drops the contents of the last chunk. The last chunk is partially empty, unlike all other
545+
// chunks.
546+
fn clear_last_chunk(&self, last_chunk: &mut TypedArenaChunk<T>) {
547+
// Determine how much was filled.
548+
let start = last_chunk.start() as usize;
549+
// We obtain the value of the pointer to the first uninitialized element.
550+
let end = self.ptr.get() as usize;
551+
// We then calculate the number of elements to be dropped in the last chunk,
552+
// which is the filled area's length.
553+
let diff = if mem::size_of::<T>() == 0 {
554+
// `T` is ZST. It can't have a drop flag, so the value here doesn't matter. We get
555+
// the number of zero-sized values in the last and only chunk, just out of caution.
556+
// Recall that `end` was incremented for each allocated value.
557+
end - start
558+
} else {
559+
(end - start) / mem::size_of::<T>()
560+
};
561+
// Pass that to the `destroy` method.
562+
unsafe {
563+
last_chunk.destroy(diff);
564+
}
565+
// Reset the chunk.
566+
self.ptr.set(last_chunk.start());
567+
}
498568
}
499569

500570
impl<T> Drop for TypedArena<T> {
@@ -504,24 +574,14 @@ impl<T> Drop for TypedArena<T> {
504574
// Determine how much was filled.
505575
let mut chunks_borrow = self.chunks.borrow_mut();
506576
let mut last_chunk = chunks_borrow.pop().unwrap();
507-
let start = last_chunk.start() as usize;
508-
let end = self.ptr.get() as usize;
509-
let diff = if mem::size_of::<T>() == 0 {
510-
// Avoid division by zero.
511-
end - start
512-
} else {
513-
(end - start) / mem::size_of::<T>()
514-
};
515-
516-
// Pass that to the `destroy` method.
517-
last_chunk.destroy(diff);
518-
// Destroy this chunk.
519-
let _: RawVec<T> = mem::transmute(last_chunk);
520-
577+
// Drop the contents of the last chunk.
578+
self.clear_last_chunk(&mut last_chunk);
579+
// The last chunk will be dropped. Destroy all other chunks.
521580
for chunk in chunks_borrow.iter_mut() {
522581
let cap = chunk.storage.cap();
523582
chunk.destroy(cap);
524583
}
584+
// RawVec handles deallocation of `last_chunk` and `self.chunks`.
525585
}
526586
}
527587
}
@@ -533,6 +593,7 @@ mod tests {
533593
extern crate test;
534594
use self::test::Bencher;
535595
use super::{Arena, TypedArena};
596+
use std::rc::Rc;
536597

537598
#[allow(dead_code)]
538599
struct Point {
@@ -667,10 +728,74 @@ mod tests {
667728
}
668729

669730
#[test]
670-
pub fn test_zero_sized() {
731+
pub fn test_typed_arena_zero_sized() {
671732
let arena = TypedArena::new();
672733
for _ in 0..100000 {
673734
arena.alloc(());
674735
}
675736
}
737+
738+
#[test]
739+
pub fn test_arena_zero_sized() {
740+
let arena = Arena::new();
741+
for _ in 0..1000 {
742+
for _ in 0..100 {
743+
arena.alloc(|| ());
744+
}
745+
arena.alloc(|| Point {
746+
x: 1,
747+
y: 2,
748+
z: 3,
749+
});
750+
}
751+
}
752+
753+
#[test]
754+
pub fn test_typed_arena_clear() {
755+
let mut arena = TypedArena::new();
756+
for _ in 0..10 {
757+
arena.clear();
758+
for _ in 0..10000 {
759+
arena.alloc(Point {
760+
x: 1,
761+
y: 2,
762+
z: 3,
763+
});
764+
}
765+
}
766+
}
767+
768+
#[test]
769+
pub fn test_arena_clear() {
770+
let mut arena = Arena::new();
771+
for _ in 0..10 {
772+
arena.clear();
773+
for _ in 0..10000 {
774+
arena.alloc(|| Point {
775+
x: 1,
776+
y: 2,
777+
z: 3,
778+
});
779+
arena.alloc(|| Noncopy {
780+
string: "hello world".to_string(),
781+
array: vec![],
782+
});
783+
}
784+
}
785+
}
786+
787+
#[test]
788+
pub fn test_arena_alloc_bytes() {
789+
let arena = Arena::new();
790+
for i in 0..10000 {
791+
arena.alloc(|| Point {
792+
x: 1,
793+
y: 2,
794+
z: 3,
795+
});
796+
for byte in arena.alloc_bytes(i % 42).iter_mut() {
797+
*byte = i as u8;
798+
}
799+
}
800+
}
676801
}

0 commit comments

Comments
 (0)