22//! `Arena` is exported at the root of the crate.
33
44use std:: mem:: size_of;
5+ use std:: ops:: Deref ;
56use std:: cell:: Cell ;
67use std:: borrow:: Cow ;
78
@@ -28,10 +29,10 @@ pub struct Uninitialized<'arena, T: 'arena> {
2829impl < ' arena , T : ' arena > Uninitialized < ' arena , T > {
2930 /// Initialize the memory at the pointer with a given value.
3031 #[ inline]
31- pub fn init ( self , value : T ) -> & ' arena T {
32+ pub fn init ( self , value : T ) -> & ' arena mut T {
3233 * self . pointer = value;
3334
34- & * self . pointer
35+ self . pointer
3536 }
3637
3738 /// Get a reference to the pointer without writing to it.
@@ -46,7 +47,7 @@ impl<'arena, T: 'arena> Uninitialized<'arena, T> {
4647 ///
4748 /// **Reading from this reference without calling `init` is undefined behavior.**
4849 #[ inline]
49- pub unsafe fn into_mut ( self ) -> & ' arena mut T {
50+ pub unsafe fn as_mut_ref ( self ) -> & ' arena mut T {
5051 self . pointer
5152 }
5253
@@ -69,6 +70,60 @@ impl<'arena, T: 'arena> From<&'arena mut T> for Uninitialized<'arena, T> {
6970 }
7071}
7172
73+ /// A wrapper around a `str` slice that has an extra `0` byte allocated following
74+ /// its contents.
75+ pub struct NulTermStr < ' arena > ( & ' arena str ) ;
76+
77+ impl < ' arena > NulTermStr < ' arena > {
78+ /// Read byte at a given `index`. This does not check for length boundaries,
79+ /// but is guaranteed to return `0` for `index` equal to the length.
80+ ///
81+ /// This can be a very useful optimization when reading a long string one
82+ /// byte at a time until termination, if checking for `0` can replace what
83+ /// would otherwise have to be length checks.
84+ ///
85+ /// ```rust
86+ /// # extern crate toolshed;
87+ /// # use toolshed::Arena;
88+ /// # fn main() {
89+ /// let arena = Arena::new();
90+ /// let str = arena.alloc_nul_term_str("foo");
91+ ///
92+ /// // We can safely get the underlying `&str` at any time.
93+ /// assert_eq!(&str[..], "foo");
94+ ///
95+ /// unsafe {
96+ /// // First 3 bytes are known to us
97+ /// assert_eq!(str.byte_unchecked(0), b'f');
98+ /// assert_eq!(str.byte_unchecked(1), b'o');
99+ /// assert_eq!(str.byte_unchecked(2), b'o');
100+ ///
101+ /// // Following is safe and guaranteed to be '0'
102+ /// assert_eq!(str.byte_unchecked(3), 0);
103+ ///
104+ /// // Reading index 4 would be undefined behavior!
105+ /// }
106+ /// # }
107+ /// ```
108+ pub unsafe fn byte_unchecked ( & self , index : usize ) -> u8 {
109+ * self . 0 . as_ptr ( ) . add ( index)
110+ }
111+ }
112+
113+ impl < ' arena > AsRef < str > for NulTermStr < ' arena > {
114+ fn as_ref ( & self ) -> & str {
115+ self . 0
116+ }
117+ }
118+
119+ impl < ' arena > Deref for NulTermStr < ' arena > {
120+ type Target = str ;
121+
122+ fn deref ( & self ) -> & str {
123+ self . 0
124+ }
125+ }
126+
72127impl Arena {
73128 /// Create a new arena with a single preallocated 64KiB page.
74129 pub fn new ( ) -> Self {
@@ -84,7 +139,7 @@ impl Arena {
84139
85140 /// Put the value onto the page of the arena and return a reference to it.
86141 #[ inline]
87- pub fn alloc < ' arena , T : Sized + Copy > ( & ' arena self , value : T ) -> & ' arena T {
142+ pub fn alloc < ' arena , T : Sized + Copy > ( & ' arena self , value : T ) -> & ' arena mut T {
88143 self . alloc_uninitialized ( ) . init ( value)
89144 }
90145
@@ -159,16 +214,19 @@ impl Arena {
159214 /// No checks are performed on the source and whether or not it already contains
160215 /// any nul bytes. While this does not create any memory issues, it assumes that
161216 /// the reader of the source can deal with malformed source.
162- pub fn alloc_str_with_nul < ' arena > ( & ' arena self , val : & str ) -> * const u8 {
217+ pub fn alloc_nul_term_str < ' arena > ( & ' arena self , val : & str ) -> NulTermStr {
163218 let len_with_zero = val. len ( ) + 1 ;
164219 let ptr = self . require ( len_with_zero) ;
165220
166221 unsafe {
167222 use std:: ptr:: copy_nonoverlapping;
223+ use std:: slice:: from_raw_parts;
224+ use std:: str:: from_utf8_unchecked;
168225
169226 copy_nonoverlapping ( val. as_ptr ( ) , ptr, val. len ( ) ) ;
170- * ptr. offset ( val. len ( ) as isize ) = 0 ;
171- ptr
227+ * ptr. add ( val. len ( ) ) = 0 ;
228+
229+ NulTermStr ( from_utf8_unchecked ( from_raw_parts ( ptr, val. len ( ) ) ) )
172230 }
173231 }
174232
@@ -223,7 +281,7 @@ impl Arena {
223281 self . ptr . get ( )
224282 } else {
225283 self . offset . set ( cap) ;
226- unsafe { self . ptr . get ( ) . offset ( offset as isize ) }
284+ unsafe { self . ptr . get ( ) . add ( offset) }
227285 }
228286 }
229287
@@ -361,10 +419,10 @@ mod test {
361419 }
362420
363421 #[ test]
364- fn alloc_str_with_nul ( ) {
422+ fn alloc_nul_term_str ( ) {
365423 let arena = Arena :: new ( ) ;
366- let ptr = arena. alloc_str_with_nul ( "abcdefghijk" ) ;
367- let allocated = unsafe { :: std:: slice:: from_raw_parts ( ptr , 12 ) } ;
424+ let nts = arena. alloc_nul_term_str ( "abcdefghijk" ) ;
425+ let allocated = unsafe { :: std:: slice:: from_raw_parts ( nts . as_ptr ( ) , 12 ) } ;
368426
369427 assert_eq ! ( arena. offset. get( ) , 16 ) ;
370428 assert_eq ! (
0 commit comments