@@ -15,6 +15,7 @@ pub struct Buffer {
15
15
}
16
16
17
17
impl Buffer {
18
+ /// Create an empty buffer
18
19
#[ inline]
19
20
pub const fn new ( ) -> Buffer {
20
21
Self {
@@ -24,17 +25,15 @@ impl Buffer {
24
25
}
25
26
}
26
27
27
- #[ cfg_attr ( feature = "perf- inline" , inline ) ]
28
+ #[ inline]
28
29
pub fn with_capacity ( n : usize ) -> Buffer {
29
- unsafe {
30
- if unlikely ! ( n == 0 ) {
31
- Self :: new ( )
32
- } else {
33
- Self {
34
- data : safe_alloc ( n) ,
35
- len : 0 ,
36
- capacity : n,
37
- }
30
+ if unlikely ! ( n == 0 ) {
31
+ Self :: new ( )
32
+ } else {
33
+ Self {
34
+ data : safe_alloc ( n) ,
35
+ len : 0 ,
36
+ capacity : n,
38
37
}
39
38
}
40
39
}
@@ -68,6 +67,11 @@ impl Buffer {
68
67
self . len = new_len;
69
68
}
70
69
70
+ /// Same as String::truncate
71
+ ///
72
+ /// # Panics
73
+ ///
74
+ /// This method panics if `new_len > self.len()`.
71
75
pub ( crate ) fn truncate ( & mut self , new_len : usize ) {
72
76
assert ! ( new_len <= self . len) ;
73
77
self . len = new_len;
@@ -89,12 +93,19 @@ impl Buffer {
89
93
self . len == 0
90
94
}
91
95
96
+ /// Same as String::reserve
97
+ ///
98
+ /// # Panics
99
+ ///
100
+ /// This method panics if `size` overflows `isize::MAX`.
92
101
#[ inline]
93
102
pub fn reserve ( & mut self , size : usize ) {
94
103
assert ! ( size <= isize :: MAX as usize ) ;
95
104
unsafe { self . reserve_small ( size) } ;
96
105
}
97
106
107
+ /// Same as String::reserve except that undefined behaviour can result if `size`
108
+ /// overflows `isize::MAX`.
98
109
#[ inline]
99
110
pub ( crate ) unsafe fn reserve_small ( & mut self , size : usize ) {
100
111
debug_assert ! ( size <= isize :: MAX as usize ) ;
@@ -110,13 +121,14 @@ impl Buffer {
110
121
self . len = 0 ;
111
122
}
112
123
113
- /// Converts a `Buffer` into a `String`.
114
- ///
115
- /// This consumes the `Buffer`, so we do not need to copy its contents.
124
+ /// Converts a `Buffer` into a `String` without copy/realloc operation.
116
125
#[ inline]
117
126
pub fn into_string ( self ) -> String {
118
127
debug_assert ! ( self . len <= self . capacity) ;
119
128
let buf = ManuallyDrop :: new ( self ) ;
129
+
130
+ // SAFETY: This operations satisfy all requirements specified in
131
+ // https://doc.rust-lang.org/std/string/struct.String.html#safety
120
132
unsafe { String :: from_raw_parts ( buf. data , buf. len , buf. capacity ) }
121
133
}
122
134
@@ -145,31 +157,44 @@ impl Buffer {
145
157
#[ cold]
146
158
fn reserve_internal ( & mut self , size : usize ) {
147
159
debug_assert ! ( size <= isize :: MAX as usize ) ;
148
- unsafe {
149
- let new_capacity = std:: cmp:: max ( self . capacity * 2 , self . capacity + size) ;
150
- debug_assert ! ( new_capacity > self . capacity) ;
151
- self . data = safe_realloc ( self . data , self . capacity , new_capacity) ;
152
- self . capacity = new_capacity;
153
- }
160
+
161
+ let new_capacity = std:: cmp:: max ( self . capacity * 2 , self . capacity + size) ;
162
+ debug_assert ! ( new_capacity > self . capacity) ;
163
+ self . data = unsafe { safe_realloc ( self . data , self . capacity , new_capacity) } ;
164
+ self . capacity = new_capacity;
165
+
154
166
debug_assert ! ( !self . data. is_null( ) ) ;
155
167
debug_assert ! ( self . len <= self . capacity) ;
156
168
}
157
169
}
158
170
159
- unsafe fn safe_alloc ( capacity : usize ) -> * mut u8 {
171
+ #[ inline( never) ]
172
+ fn safe_alloc ( capacity : usize ) -> * mut u8 {
173
+ assert ! ( capacity > 0 ) ;
160
174
assert ! ( capacity <= isize :: MAX as usize , "capacity is too large" ) ;
161
- let layout = Layout :: from_size_align_unchecked ( capacity, 1 ) ;
162
- let data = alloc ( layout) ;
163
- if data. is_null ( ) {
164
- handle_alloc_error ( layout) ;
165
- }
166
175
167
- data
176
+ // SAFETY: capacity is non-zero, and always multiple of alignment (1).
177
+ unsafe {
178
+ let layout = Layout :: from_size_align_unchecked ( capacity, 1 ) ;
179
+ let data = alloc ( layout) ;
180
+ if data. is_null ( ) {
181
+ handle_alloc_error ( layout) ;
182
+ }
183
+
184
+ data
185
+ }
168
186
}
169
187
188
+ /// # Safety
189
+ ///
190
+ /// - if `capacity > 0`, `capacity` is the same value that was used to allocate the block
191
+ /// of memory pointed by `ptr`.
170
192
#[ cold]
193
+ #[ inline( never) ]
171
194
unsafe fn safe_realloc ( ptr : * mut u8 , capacity : usize , new_capacity : usize ) -> * mut u8 {
195
+ assert ! ( new_capacity > 0 ) ;
172
196
assert ! ( new_capacity <= isize :: MAX as usize , "capacity is too large" ) ;
197
+
173
198
let data = if unlikely ! ( capacity == 0 ) {
174
199
let new_layout = Layout :: from_size_align_unchecked ( new_capacity, 1 ) ;
175
200
alloc ( new_layout)
@@ -188,7 +213,7 @@ unsafe fn safe_realloc(ptr: *mut u8, capacity: usize, new_capacity: usize) -> *m
188
213
impl Clone for Buffer {
189
214
fn clone ( & self ) -> Self {
190
215
unsafe {
191
- if self . capacity == 0 {
216
+ if self . is_empty ( ) {
192
217
Self :: new ( )
193
218
} else {
194
219
let buf = Self {
@@ -213,6 +238,8 @@ impl fmt::Debug for Buffer {
213
238
impl Drop for Buffer {
214
239
fn drop ( & mut self ) {
215
240
if self . capacity != 0 {
241
+ // SAFETY: when `self.capacity > 0`, `self.capacity` is the same value
242
+ // used for allocate the block of memory pointed by `self.data`.
216
243
unsafe {
217
244
let layout = Layout :: from_size_align_unchecked ( self . capacity , 1 ) ;
218
245
dealloc ( self . data , layout) ;
@@ -236,7 +263,7 @@ impl From<String> for Buffer {
236
263
#[ inline]
237
264
fn from ( other : String ) -> Buffer {
238
265
let bs = other. into_boxed_str ( ) ;
239
- let data = unsafe { & mut * Box :: into_raw ( bs) } ;
266
+ let data = Box :: leak ( bs) ;
240
267
Buffer {
241
268
data : data. as_mut_ptr ( ) ,
242
269
len : data. len ( ) ,
@@ -249,10 +276,16 @@ impl From<&str> for Buffer {
249
276
#[ inline]
250
277
fn from ( other : & str ) -> Buffer {
251
278
let mut buf = Buffer :: with_capacity ( other. len ( ) ) ;
252
- unsafe {
253
- ptr:: copy_nonoverlapping ( other. as_ptr ( ) , buf. as_mut_ptr ( ) , other. len ( ) ) ;
254
- buf. advance ( other. len ( ) ) ;
279
+
280
+ if !other. is_empty ( ) {
281
+ // SAFETY: `Buffer.capacity()` should be same as `other.len()`, so if `other`
282
+ // is not empty, `buf.as_mut_ptr()` is supporsed to point to valid memory.
283
+ unsafe {
284
+ ptr:: copy_nonoverlapping ( other. as_ptr ( ) , buf. as_mut_ptr ( ) , other. len ( ) ) ;
285
+ buf. advance ( other. len ( ) ) ;
286
+ }
255
287
}
288
+
256
289
buf
257
290
}
258
291
}
@@ -363,5 +396,8 @@ mod tests {
363
396
364
397
assert_eq ! ( s1. as_str( ) , "foobar" ) ;
365
398
assert_eq ! ( s2. as_str( ) , "foobaz" ) ;
399
+
400
+ s2. clear ( ) ;
401
+ let _ = s2. clone ( ) ;
366
402
}
367
403
}
0 commit comments