@@ -16,8 +16,57 @@ pub struct Arena {
1616 offset : Cell < usize >
1717}
1818
19+ /// A pointer to an uninitialized region of memory.
20+ pub struct Uninitialized < ' arena , T : ' arena > {
21+ pointer : & ' arena mut T ,
22+ }
23+
24+ impl < ' arena , T : ' arena > Uninitialized < ' arena , T > {
25+ /// Initialize the memory at the pointer with a given value.
26+ #[ inline]
27+ pub fn init ( self , value : T ) -> & ' arena T {
28+ * self . pointer = value;
29+
30+ & * self . pointer
31+ }
32+
33+ /// Get a reference to the pointer without writing to it.
34+ ///
35+ /// **Reading from this reference without calling `init` is undefined behavior.**
36+ #[ inline]
37+ pub unsafe fn as_ref ( & self ) -> & ' arena T {
38+ & * ( self . pointer as * const T )
39+ }
40+
41+ /// Convert the `Uninitialized` to a regular mutable reference.
42+ ///
43+ /// **Reading from this reference without calling `init` is undefined behavior.**
44+ #[ inline]
45+ pub unsafe fn into_mut ( self ) -> & ' arena mut T {
46+ self . pointer
47+ }
48+
49+ /// Convert a raw pointer to an `Uninitialized`. This method is unsafe since it can
50+ /// bind to arbitrary lifetimes.
51+ #[ inline]
52+ pub unsafe fn from_raw ( pointer : * mut T ) -> Self {
53+ Uninitialized {
54+ pointer : & mut * pointer,
55+ }
56+ }
57+ }
58+
59+ impl < ' arena , T : ' arena > From < & ' arena mut T > for Uninitialized < ' arena , T > {
60+ #[ inline]
61+ fn from ( pointer : & ' arena mut T ) -> Self {
62+ Uninitialized {
63+ pointer
64+ }
65+ }
66+ }
67+
1968impl Arena {
20- /// Create a new arena with a single preallocated 64KiB page
69+ /// Create a new arena with a single preallocated 64KiB page.
2170 pub fn new ( ) -> Self {
2271 let mut store = vec ! [ Vec :: with_capacity( ARENA_BLOCK ) ] ;
2372 let ptr = store[ 0 ] . as_mut_ptr ( ) ;
@@ -31,55 +80,53 @@ impl Arena {
3180
3281 /// Put the value onto the page of the arena and return a reference to it.
3382 #[ inline]
34- pub fn alloc < ' a , T : Sized + Copy > ( & ' a self , val : T ) -> & ' a T {
35- unsafe {
36- let ptr = self . alloc_uninitialized ( ) ;
37- * ptr = val;
38- & * ptr
39- }
83+ pub fn alloc < ' arena , T : Sized + Copy > ( & ' arena self , value : T ) -> & ' arena T {
84+ self . alloc_uninitialized ( ) . init ( value)
4085 }
4186
42- /// Allocate enough bytes for the type `T`, then return a pointer to the memory.
43- /// Memory behind the pointer is uninitialized, can contain garbage and reading
44- /// from it is undefined behavior.
87+ /// Allocate enough bytes for the type `T`, then return an `Uninitialized` pointer to the memory.
4588 #[ inline]
46- pub unsafe fn alloc_uninitialized < ' a , T : Sized + Copy > ( & ' a self ) -> & ' a mut T {
47- & mut * ( self . require ( size_of :: < T > ( ) ) as * mut T )
89+ pub fn alloc_uninitialized < ' arena , T : Sized + Copy > ( & ' arena self ) -> Uninitialized < ' arena , T > {
90+ Uninitialized {
91+ pointer : unsafe { & mut * ( self . require ( size_of :: < T > ( ) ) as * mut T ) }
92+ }
4893 }
4994
50- /// Allocate an `&str ` slice onto the arena and return a reference to it. This is
51- /// useful when the original slice has an undefined lifetime.
95+ /// Allocate a slice of `T ` slice onto the arena and return a reference to it.
96+ /// This is useful when the original slice has an undefined lifetime.
5297 ///
53- /// Note: static slices (`&'static str `) can be safely used in place of arena-bound
98+ /// Note: static slices (`&'static [T] `) can be safely used in place of arena-bound
5499 /// slices without having to go through this method.
55- pub fn alloc_str < ' a > ( & ' a self , val : & str ) -> & ' a str {
56- let offset = self . offset . get ( ) ;
57- let alignment = size_of :: < usize > ( ) - ( val. len ( ) % size_of :: < usize > ( ) ) ;
58- let cap = offset + val. len ( ) + alignment;
59-
60- if cap > ARENA_BLOCK {
61- return self . alloc_string ( val. into ( ) ) ;
62- }
63-
64- self . offset . set ( cap) ;
100+ pub fn alloc_slice < ' arena , T : Copy > ( & ' arena self , val : & [ T ] ) -> & ' arena [ T ] {
101+ let ptr = self . require ( val. len ( ) * size_of :: < T > ( ) ) as * mut T ;
65102
66103 unsafe {
67104 use std:: ptr:: copy_nonoverlapping;
68- use std:: str:: from_utf8_unchecked;
69105 use std:: slice:: from_raw_parts;
70106
71- let ptr = self . ptr . get ( ) . offset ( offset as isize ) ;
72107 copy_nonoverlapping ( val. as_ptr ( ) , ptr, val. len ( ) ) ;
108+ from_raw_parts ( ptr, val. len ( ) )
109+ }
110+ }
111+
112+ /// Allocate an `&str` slice onto the arena and return a reference to it. This is
113+ /// useful when the original slice has an undefined lifetime.
114+ ///
115+ /// Note: static slices (`&'static str`) can be safely used in place of arena-bound
116+ /// slices without having to go through this method.
117+ pub fn alloc_str < ' arena > ( & ' arena self , val : & str ) -> & ' arena str {
118+ unsafe {
119+ use std:: str:: from_utf8_unchecked;
73120
74- from_utf8_unchecked ( from_raw_parts ( ptr , val. len ( ) ) )
121+ from_utf8_unchecked ( self . alloc_slice ( val. as_bytes ( ) ) )
75122 }
76123 }
77124
78125 /// Allocate an `&str` slice onto the arena as null terminated C-style string.
79126 /// No checks are performed on the source and whether or not it already contains
80127 /// any nul bytes. While this does not create any memory issues, it assumes that
81128 /// the reader of the source can deal with malformed source.
82- pub fn alloc_str_with_nul < ' a > ( & ' a self , val : & str ) -> * const u8 {
129+ pub fn alloc_str_with_nul < ' arena > ( & ' arena self , val : & str ) -> * const u8 {
83130 let len_with_zero = val. len ( ) + 1 ;
84131 let ptr = self . require ( len_with_zero) ;
85132
@@ -94,7 +141,7 @@ impl Arena {
94141
95142 /// Pushes the `String` as it's own page onto the arena and returns a reference to it.
96143 /// This does not copy or reallocate the original `String`.
97- pub fn alloc_string < ' a > ( & ' a self , val : String ) -> & ' a str {
144+ pub fn alloc_string < ' arena > ( & ' arena self , val : String ) -> & ' arena str {
98145 let len = val. len ( ) ;
99146 let ptr = self . alloc_vec ( val. into_bytes ( ) ) ;
100147
@@ -128,10 +175,9 @@ impl Arena {
128175 return self . alloc_bytes ( size) ;
129176 }
130177
131- // This should also be optimized away.
132178 let size = match size % size_of :: < usize > ( ) {
133179 0 => size,
134- n => size + n
180+ n => size + ( size_of :: < usize > ( ) - n ) ,
135181 } ;
136182
137183 let offset = self . offset . get ( ) ;
@@ -209,7 +255,7 @@ mod test {
209255 assert_eq ! ( arena. alloc( 0u64 ) , & 0 ) ;
210256 assert_eq ! ( arena. alloc( 42u64 ) , & 42 ) ;
211257
212- unsafe { arena. alloc_uninitialized :: < [ usize ; 1024 * 1024 ] > ( ) } ;
258+ arena. alloc_uninitialized :: < [ usize ; 1024 * 1024 ] > ( ) ;
213259
214260 // Still writes to the first page
215261 assert_eq ! ( arena. offset. get( ) , 8 * 2 ) ;
@@ -226,6 +272,25 @@ mod test {
226272 assert_eq ! ( arena. store. get_mut( ) [ 1 ] . capacity( ) , size_of:: <usize >( ) * 1024 * 1024 ) ;
227273 }
228274
275+ #[ test]
276+ fn alloc_slice ( ) {
277+ let arena = Arena :: new ( ) ;
278+
279+ assert_eq ! ( arena. alloc_slice( & [ 10u16 , 20u16 ] ) , & [ 10u16 , 20u16 ] [ ..] ) ;
280+ assert_eq ! ( arena. offset. get( ) , 8 ) ;
281+ }
282+
283+ #[ test]
284+ fn aligns_slice_allocs ( ) {
285+ let arena = Arena :: new ( ) ;
286+
287+ assert_eq ! ( arena. alloc_slice( b"foo" ) , b"foo" ) ;
288+ assert_eq ! ( arena. offset. get( ) , 8 ) ;
289+
290+ assert_eq ! ( arena. alloc_slice( b"doge to the moon!" ) , b"doge to the moon!" ) ;
291+ assert_eq ! ( arena. offset. get( ) , 32 ) ;
292+ }
293+
229294 #[ test]
230295 fn aligns_str_allocs ( ) {
231296 let arena = Arena :: new ( ) ;
0 commit comments