@@ -20,6 +20,20 @@ pub enum FromSliceWithNulError {
20
20
NotNulTerminated ,
21
21
}
22
22
23
+ /// Error returned by [`CStr16::from_str_with_buf`].
24
+ #[ derive( Clone , Copy , Debug , Eq , PartialEq ) ]
25
+ pub enum FromStrWithBufError {
26
+ /// An invalid character was encountered before the end of the string
27
+ InvalidChar ( usize ) ,
28
+
29
+ /// A null character was encountered in the string
30
+ InteriorNul ( usize ) ,
31
+
32
+ /// The buffer is not big enough to hold the entire string and
33
+ /// trailing null character
34
+ BufferTooSmall ,
35
+ }
36
+
23
37
/// A Latin-1 null-terminated string
24
38
///
25
39
/// This type is largely inspired by `std::ffi::CStr`, see the documentation of
@@ -141,6 +155,52 @@ impl CStr16 {
141
155
& * ( codes as * const [ u16 ] as * const Self )
142
156
}
143
157
158
+ /// Convert a [`&str`] to a `&CStr16`, backed by a buffer.
159
+ ///
160
+ /// The input string must contain only characters representable with
161
+ /// UCS-2, and must not contain any null characters (even at the end of
162
+ /// the input).
163
+ ///
164
+ /// The backing buffer must be big enough to hold the converted string as
165
+ /// well as a trailing null character.
166
+ ///
167
+ /// # Examples
168
+ ///
169
+ /// Convert the UTF-8 string "ABC" to a `&CStr16`:
170
+ ///
171
+ /// ```
172
+ /// use uefi::CStr16;
173
+ ///
174
+ /// let mut buf = [0; 4];
175
+ /// CStr16::from_str_with_buf("ABC", &mut buf).unwrap();
176
+ /// ```
177
+ pub fn from_str_with_buf < ' a > (
178
+ input : & str ,
179
+ buf : & ' a mut [ u16 ] ,
180
+ ) -> Result < & ' a Self , FromStrWithBufError > {
181
+ let mut index = 0 ;
182
+
183
+ // Convert to UTF-16.
184
+ for c in input. encode_utf16 ( ) {
185
+ * buf. get_mut ( index)
186
+ . ok_or ( FromStrWithBufError :: BufferTooSmall ) ? = c;
187
+ index += 1 ;
188
+ }
189
+
190
+ // Add trailing null character.
191
+ * buf. get_mut ( index)
192
+ . ok_or ( FromStrWithBufError :: BufferTooSmall ) ? = 0 ;
193
+
194
+ // Convert from u16 to Char16. This checks for invalid UCS-2 chars and
195
+ // interior nulls. The NotNulTerminated case is unreachable because we
196
+ // just added a trailing null character.
197
+ Self :: from_u16_with_nul ( & buf[ ..index + 1 ] ) . map_err ( |err| match err {
198
+ FromSliceWithNulError :: InvalidChar ( p) => FromStrWithBufError :: InvalidChar ( p) ,
199
+ FromSliceWithNulError :: InteriorNul ( p) => FromStrWithBufError :: InteriorNul ( p) ,
200
+ FromSliceWithNulError :: NotNulTerminated => unreachable ! ( ) ,
201
+ } )
202
+ }
203
+
144
204
/// Returns the inner pointer to this C string
145
205
pub fn as_ptr ( & self ) -> * const Char16 {
146
206
self . 0 . as_ptr ( )
@@ -251,4 +311,35 @@ mod tests {
251
311
let s = CStr16 :: from_u16_with_nul ( & [ 65 , 66 , 67 , 0 ] ) . unwrap ( ) ;
252
312
assert_eq ! ( s. num_bytes( ) , 8 ) ;
253
313
}
314
+
315
+ #[ test]
316
+ fn test_cstr16_from_str_with_buf ( ) {
317
+ let mut buf = [ 0 ; 4 ] ;
318
+
319
+ // OK: buf is exactly the right size.
320
+ let s = CStr16 :: from_str_with_buf ( "ABC" , & mut buf) . unwrap ( ) ;
321
+ assert_eq ! ( s. to_u16_slice_with_nul( ) , [ 65 , 66 , 67 , 0 ] ) ;
322
+
323
+ // OK: buf is bigger than needed.
324
+ let s = CStr16 :: from_str_with_buf ( "A" , & mut buf) . unwrap ( ) ;
325
+ assert_eq ! ( s. to_u16_slice_with_nul( ) , [ 65 , 0 ] ) ;
326
+
327
+ // Error: buf is too small.
328
+ assert_eq ! (
329
+ CStr16 :: from_str_with_buf( "ABCD" , & mut buf) . unwrap_err( ) ,
330
+ FromStrWithBufError :: BufferTooSmall
331
+ ) ;
332
+
333
+ // Error: invalid character.
334
+ assert_eq ! (
335
+ CStr16 :: from_str_with_buf( "a😀" , & mut buf) . unwrap_err( ) ,
336
+ FromStrWithBufError :: InvalidChar ( 1 ) ,
337
+ ) ;
338
+
339
+ // Error: interior null.
340
+ assert_eq ! (
341
+ CStr16 :: from_str_with_buf( "a\0 b" , & mut buf) . unwrap_err( ) ,
342
+ FromStrWithBufError :: InteriorNul ( 1 ) ,
343
+ ) ;
344
+ }
254
345
}
0 commit comments