2
2
3
3
use alloc:: borrow:: Cow ;
4
4
use alloc:: string:: String as StdString ;
5
- use core:: { ffi, slice} ;
5
+ use core:: { ffi, fmt , ptr , slice} ;
6
6
use std:: path:: { Path , PathBuf } ;
7
7
8
8
use luajit as lua;
@@ -19,7 +19,15 @@ use crate::NonOwning;
19
19
#[ repr( C ) ]
20
20
pub struct String {
21
21
pub ( super ) data : * mut ffi:: c_char ,
22
- pub ( super ) size : usize ,
22
+ pub ( super ) len : usize ,
23
+ }
24
+
25
+ /// A builder that can be used to efficiently build a [`nvim_oxi::String`](String).
26
+ pub struct StringBuilder {
27
+ /// The underlying string being constructed.
28
+ pub ( super ) inner : String ,
29
+ /// Current capacity (i.e., allocated memory) of this builder in bytes.
30
+ pub ( super ) cap : usize ,
23
31
}
24
32
25
33
impl Default for String {
@@ -43,14 +51,28 @@ impl core::fmt::Display for String {
43
51
}
44
52
}
45
53
54
+ impl Default for StringBuilder {
55
+ #[ inline]
56
+ fn default ( ) -> Self {
57
+ Self :: new ( )
58
+ }
59
+ }
60
+
61
+ impl fmt:: Write for StringBuilder {
62
+ fn write_str ( & mut self , s : & str ) -> fmt:: Result {
63
+ self . push_bytes ( s. as_bytes ( ) ) ;
64
+ Ok ( ( ) )
65
+ }
66
+ }
67
+
46
68
impl String {
47
69
#[ inline]
48
70
pub fn as_bytes ( & self ) -> & [ u8 ] {
49
71
if self . data . is_null ( ) {
50
72
& [ ]
51
73
} else {
52
74
assert ! ( self . len( ) <= isize :: MAX as usize ) ;
53
- unsafe { slice:: from_raw_parts ( self . data as * const u8 , self . size ) }
75
+ unsafe { slice:: from_raw_parts ( self . data as * const u8 , self . len ) }
54
76
}
55
77
}
56
78
@@ -65,20 +87,9 @@ impl String {
65
87
/// by a null byte.
66
88
#[ inline]
67
89
pub fn from_bytes ( bytes : & [ u8 ] ) -> Self {
68
- let data =
69
- unsafe { libc:: malloc ( bytes. len ( ) + 1 ) as * mut ffi:: c_char } ;
70
-
71
- unsafe {
72
- libc:: memcpy (
73
- data as * mut _ ,
74
- bytes. as_ptr ( ) as * const _ ,
75
- bytes. len ( ) ,
76
- )
77
- } ;
78
-
79
- unsafe { * data. add ( bytes. len ( ) ) = 0 } ;
80
-
81
- Self { data : data as * mut _ , size : bytes. len ( ) }
90
+ let mut s = StringBuilder :: new ( ) ;
91
+ s. push_bytes ( bytes) ;
92
+ s. finish ( )
82
93
}
83
94
84
95
/// Returns `true` if the `String` has a length of zero.
@@ -90,13 +101,13 @@ impl String {
90
101
/// Returns the length of the `String`, *not* including the final null byte.
91
102
#[ inline]
92
103
pub fn len ( & self ) -> usize {
93
- self . size
104
+ self . len
94
105
}
95
106
96
107
/// Creates a new, empty `String`.
97
108
#[ inline]
98
109
pub fn new ( ) -> Self {
99
- Self { data : core :: ptr:: null_mut ( ) , size : 0 }
110
+ Self { data : ptr:: null_mut ( ) , len : 0 }
100
111
}
101
112
102
113
/// Makes a non-owning version of this `String`.
@@ -115,6 +126,86 @@ impl String {
115
126
}
116
127
}
117
128
129
+ impl StringBuilder {
130
+ /// Create a new empty `StringBuilder`.
131
+ #[ inline]
132
+ pub fn new ( ) -> Self {
133
+ Self { inner : String :: new ( ) , cap : 0 }
134
+ }
135
+
136
+ /// Push new bytes to the builder.
137
+ #[ inline]
138
+ pub fn push_bytes ( & mut self , bytes : & [ u8 ] ) {
139
+ if self . inner . data . is_null ( ) {
140
+ let len = bytes. len ( ) ;
141
+ let cap = len + 1 ;
142
+
143
+ let data = unsafe {
144
+ let data = libc:: malloc ( cap) as * mut ffi:: c_char ;
145
+
146
+ libc:: memcpy ( data as * mut _ , bytes. as_ptr ( ) as * const _ , len) ;
147
+
148
+ * data. add ( len) = 0 ;
149
+
150
+ data
151
+ } ;
152
+
153
+ self . inner . data = data;
154
+ self . inner . len = len;
155
+ self . cap = cap;
156
+
157
+ return ;
158
+ }
159
+
160
+ let slice_len = bytes. len ( ) ;
161
+ let required_cap = self . inner . len + slice_len + 1 ;
162
+
163
+ // Reallocate if pushing the bytes overflows the allocated memory.
164
+ if self . cap < required_cap {
165
+ // The smallest number `n`, such that `required_cap <= * 2^n`.
166
+ let n = ( required_cap - 1 ) . ilog2 ( ) + 1 ;
167
+ let new_cap = 2_usize . pow ( n) . max ( 4 ) ;
168
+
169
+ self . inner . data = unsafe {
170
+ libc:: realloc ( self . inner . data as * mut _ , new_cap)
171
+ as * mut ffi:: c_char
172
+ } ;
173
+
174
+ self . cap = new_cap;
175
+ debug_assert ! ( self . inner. len < self . cap)
176
+ }
177
+
178
+ // Pushing the `bytes` is safe now.
179
+ let new_len = unsafe {
180
+ libc:: memcpy (
181
+ self . inner . data . add ( self . inner . len ) as * mut _ ,
182
+ bytes. as_ptr ( ) as * const _ ,
183
+ slice_len,
184
+ ) ;
185
+
186
+ let new_len = self . inner . len + slice_len;
187
+
188
+ * self . inner . data . add ( new_len) = 0 ;
189
+
190
+ new_len
191
+ } ;
192
+
193
+ self . inner . len = new_len;
194
+ debug_assert ! ( self . inner. len < self . cap) ;
195
+ }
196
+
197
+ /// Build the `String`.
198
+ #[ inline]
199
+ pub fn finish ( self ) -> String {
200
+ let s = String { data : self . inner . data , len : self . inner . len } ;
201
+
202
+ // Prevent self's destructor from being called.
203
+ std:: mem:: forget ( self ) ;
204
+
205
+ s
206
+ }
207
+ }
208
+
118
209
impl Clone for String {
119
210
#[ inline]
120
211
fn clone ( & self ) -> Self {
@@ -132,6 +223,14 @@ impl Drop for String {
132
223
}
133
224
}
134
225
226
+ impl Drop for StringBuilder {
227
+ fn drop ( & mut self ) {
228
+ if !self . inner . data . is_null ( ) {
229
+ unsafe { libc:: free ( self . inner . data as * mut _ ) }
230
+ }
231
+ }
232
+ }
233
+
135
234
impl From < & str > for String {
136
235
#[ inline]
137
236
fn from ( s : & str ) -> Self {
@@ -229,7 +328,7 @@ impl PartialEq<std::string::String> for String {
229
328
impl core:: hash:: Hash for String {
230
329
fn hash < H : core:: hash:: Hasher > ( & self , state : & mut H ) {
231
330
self . as_bytes ( ) . hash ( state) ;
232
- self . size . hash ( state) ;
331
+ self . len . hash ( state) ;
233
332
}
234
333
}
235
334
@@ -359,4 +458,21 @@ mod tests {
359
458
360
459
assert_eq ! ( lhs, rhs) ;
361
460
}
461
+
462
+ #[ test]
463
+ fn builder ( ) {
464
+ let str = "foo bar" ;
465
+ let bytes = b"baz foo bar" ;
466
+
467
+ let mut s = StringBuilder :: new ( ) ;
468
+ s. push_bytes ( str. as_bytes ( ) ) ;
469
+ s. push_bytes ( bytes) ;
470
+
471
+ assert_eq ! ( s. inner. len, str . len( ) + bytes. len( ) ) ;
472
+ assert_eq ! ( s. cap, 32 ) ; // Allocation size
473
+ assert_eq ! ( unsafe { * s. inner. data. add( s. inner. len) } , 0 ) ; // Null termination
474
+
475
+ let s = s. finish ( ) ;
476
+ assert_eq ! ( s. len( ) , str . len( ) + bytes. len( ) ) ;
477
+ }
362
478
}
0 commit comments