2
2
//! `Arena` is exported at the root of the crate.
3
3
4
4
use std:: mem:: size_of;
5
+ use std:: ops:: Deref ;
5
6
use std:: cell:: Cell ;
6
7
use std:: borrow:: Cow ;
7
8
@@ -28,10 +29,10 @@ pub struct Uninitialized<'arena, T: 'arena> {
28
29
impl < ' arena , T : ' arena > Uninitialized < ' arena , T > {
29
30
/// Initialize the memory at the pointer with a given value.
30
31
#[ inline]
31
- pub fn init ( self , value : T ) -> & ' arena T {
32
+ pub fn init ( self , value : T ) -> & ' arena mut T {
32
33
* self . pointer = value;
33
34
34
- & * self . pointer
35
+ self . pointer
35
36
}
36
37
37
38
/// Get a reference to the pointer without writing to it.
@@ -46,7 +47,7 @@ impl<'arena, T: 'arena> Uninitialized<'arena, T> {
46
47
///
47
48
/// **Reading from this reference without calling `init` is undefined behavior.**
48
49
#[ inline]
49
- pub unsafe fn into_mut ( self ) -> & ' arena mut T {
50
+ pub unsafe fn as_mut_ref ( self ) -> & ' arena mut T {
50
51
self . pointer
51
52
}
52
53
@@ -69,6 +70,60 @@ impl<'arena, T: 'arena> From<&'arena mut T> for Uninitialized<'arena, T> {
69
70
}
70
71
}
71
72
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
+
72
127
impl Arena {
73
128
/// Create a new arena with a single preallocated 64KiB page.
74
129
pub fn new ( ) -> Self {
@@ -84,7 +139,7 @@ impl Arena {
84
139
85
140
/// Put the value onto the page of the arena and return a reference to it.
86
141
#[ 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 {
88
143
self . alloc_uninitialized ( ) . init ( value)
89
144
}
90
145
@@ -159,16 +214,19 @@ impl Arena {
159
214
/// No checks are performed on the source and whether or not it already contains
160
215
/// any nul bytes. While this does not create any memory issues, it assumes that
161
216
/// 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 {
163
218
let len_with_zero = val. len ( ) + 1 ;
164
219
let ptr = self . require ( len_with_zero) ;
165
220
166
221
unsafe {
167
222
use std:: ptr:: copy_nonoverlapping;
223
+ use std:: slice:: from_raw_parts;
224
+ use std:: str:: from_utf8_unchecked;
168
225
169
226
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 ( ) ) ) )
172
230
}
173
231
}
174
232
@@ -223,7 +281,7 @@ impl Arena {
223
281
self . ptr . get ( )
224
282
} else {
225
283
self . offset . set ( cap) ;
226
- unsafe { self . ptr . get ( ) . offset ( offset as isize ) }
284
+ unsafe { self . ptr . get ( ) . add ( offset) }
227
285
}
228
286
}
229
287
@@ -361,10 +419,10 @@ mod test {
361
419
}
362
420
363
421
#[ test]
364
- fn alloc_str_with_nul ( ) {
422
+ fn alloc_nul_term_str ( ) {
365
423
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 ) } ;
368
426
369
427
assert_eq ! ( arena. offset. get( ) , 16 ) ;
370
428
assert_eq ! (
0 commit comments