5
5
use core:: ops:: { self , Deref , Index } ;
6
6
7
7
use crate :: bindings;
8
+ use crate :: build_assert;
8
9
use crate :: c_types;
9
10
10
11
/// Byte string without UTF-8 validity guarantee.
@@ -31,9 +32,13 @@ macro_rules! b_str {
31
32
} } ;
32
33
}
33
34
34
- /// Possible errors when using conversion functions in [`CStr`].
35
+ /// Possible errors when using conversion functions in [`CStr`] and [`CBoundedStr`] .
35
36
#[ derive( Debug , Clone , Copy ) ]
36
37
pub enum CStrConvertError {
38
+ /// Supplied string length exceed the bound specified. Only happen when
39
+ /// constructing a [`CBoundedStr`].
40
+ BoundExceeded ,
41
+
37
42
/// Supplied bytes contain an interior `NUL`.
38
43
InteriorNul ,
39
44
@@ -241,3 +246,187 @@ macro_rules! c_str {
241
246
C
242
247
} } ;
243
248
}
249
+
250
+ /// A `NUL`-terminated string that is guaranteed to be shorter than a given
251
+ /// length. This type is useful because C-side usually impose maximum length
252
+ /// on types.
253
+ ///
254
+ /// The size parameter `N` represent the maximum number of bytes including NUL.
255
+ /// This implies that even though `CBoundedStr<0>` is a well-formed type it cannot
256
+ /// be safely created.
257
+ #[ repr( transparent) ]
258
+ pub struct CBoundedStr < const N : usize > ( CStr ) ;
259
+
260
+ impl < const N : usize > CBoundedStr < N > {
261
+ /// Creates a [`CBoundedStr`] from a [`CStr`].
262
+ ///
263
+ /// The provided `CStr` must be shorten than `N`.
264
+ #[ inline]
265
+ pub const fn from_c_str ( c_str : & CStr ) -> Result < & Self , CStrConvertError > {
266
+ if c_str. len_with_nul ( ) > N {
267
+ return Err ( CStrConvertError :: BoundExceeded ) ;
268
+ }
269
+
270
+ // SAFETY: We just checked that all properties hold.
271
+ Ok ( unsafe { Self :: from_c_str_unchecked ( c_str) } )
272
+ }
273
+
274
+ /// Creates a [`CBoundedStr`] from a [`CStr`] without performing any sanity
275
+ /// checks.
276
+ ///
277
+ /// # Safety
278
+ ///
279
+ /// The provided CStr must be shorten than `N`.
280
+ #[ inline]
281
+ pub const unsafe fn from_c_str_unchecked ( c_str : & CStr ) -> & Self {
282
+ & * ( c_str as * const CStr as * const Self )
283
+ }
284
+
285
+ /// Creates a [`CBoundedStr`] from a `[u8]`.
286
+ ///
287
+ /// The provided slice must be nul-terminated, does not contain any
288
+ /// interior nul bytes and be shorten than `N`.
289
+ #[ inline]
290
+ pub fn from_bytes_with_nul ( bytes : & [ u8 ] ) -> Result < & Self , CStrConvertError > {
291
+ Self :: from_c_str ( CStr :: from_bytes_with_nul ( bytes) ?)
292
+ }
293
+
294
+ /// Creates a [`CBoundedStr`] from a `[u8]` without performing any sanity
295
+ /// checks.
296
+ ///
297
+ /// # Safety
298
+ ///
299
+ /// The provided slice must be nul-terminated, does not contain any
300
+ /// interior nul bytes and be shorten than `N`.
301
+ #[ inline]
302
+ pub const unsafe fn from_bytes_with_nul_unchecked ( bytes : & [ u8 ] ) -> & Self {
303
+ Self :: from_c_str_unchecked ( CStr :: from_bytes_with_nul_unchecked ( bytes) )
304
+ }
305
+
306
+ /// Creates a [`CBoundedStr`] from a `[u8; N]` without performing any sanity
307
+ /// checks.
308
+ ///
309
+ /// # Safety
310
+ ///
311
+ /// The provided slice must be nul-terminated.
312
+ #[ inline]
313
+ pub const unsafe fn from_exact_bytes_with_nul_unchecked ( bytes : & [ u8 ; N ] ) -> & Self {
314
+ Self :: from_bytes_with_nul_unchecked ( bytes)
315
+ }
316
+
317
+ /// Relaxes the bound from `N` to `M`.
318
+ ///
319
+ /// `M` must be no less than the bound `N`.
320
+ #[ inline]
321
+ pub const fn relax_bound < const M : usize > ( & self ) -> & CBoundedStr < M > {
322
+ build_assert ! ( N <= M , "relaxed bound should be no less than current bound" ) ;
323
+ unsafe { CBoundedStr :: < M > :: from_c_str_unchecked ( & self . 0 ) }
324
+ }
325
+
326
+ /// Converts the string to a c_char array with the current bound, fills the
327
+ /// remaining bytes with zero.
328
+ #[ inline]
329
+ pub const fn to_char_array ( & self ) -> [ c_types:: c_char ; N ] {
330
+ let mut ret: [ c_types:: c_char ; N ] = [ 0 ; N ] ;
331
+ let mut i = 0 ;
332
+ while i < self . 0 . 0 . len ( ) {
333
+ ret[ i] = self . 0 . 0 [ i] as _ ;
334
+ i += 1 ;
335
+ }
336
+ ret
337
+ }
338
+
339
+ /// Expands the string a c_char array, fills remaining bytes with zero.
340
+ ///
341
+ /// `M` must be no less than the bound `N`.
342
+ #[ inline]
343
+ pub const fn expand_to_char_array < const M : usize > ( & self ) -> [ c_types:: c_char ; M ] {
344
+ self . relax_bound ( ) . to_char_array ( )
345
+ }
346
+ }
347
+
348
+ impl < const N : usize > AsRef < BStr > for CBoundedStr < N > {
349
+ #[ inline]
350
+ fn as_ref ( & self ) -> & BStr {
351
+ self . as_bytes ( )
352
+ }
353
+ }
354
+
355
+ impl < const N : usize > AsRef < CStr > for CBoundedStr < N > {
356
+ #[ inline]
357
+ fn as_ref ( & self ) -> & CStr {
358
+ & self . 0
359
+ }
360
+ }
361
+
362
+ impl < const N : usize > Deref for CBoundedStr < N > {
363
+ type Target = CStr ;
364
+
365
+ #[ inline]
366
+ fn deref ( & self ) -> & Self :: Target {
367
+ & self . 0
368
+ }
369
+ }
370
+
371
+ impl < Idx , const N : usize > Index < Idx > for CBoundedStr < N >
372
+ where
373
+ CStr : Index < Idx > ,
374
+ {
375
+ type Output = <CStr as Index < Idx > >:: Output ;
376
+
377
+ #[ inline]
378
+ fn index ( & self , index : Idx ) -> & Self :: Output {
379
+ & self . 0 [ index]
380
+ }
381
+ }
382
+
383
+ /// Creates a new [`CBoundedStr`] from a string literal.
384
+ ///
385
+ /// The string literal should not contain any `NUL` bytes, and its length with NUL should not
386
+ /// exceed the bound supplied.
387
+ ///
388
+ /// # Examples
389
+ ///
390
+ /// ```rust,no_run
391
+ /// // If no bound is specified, the tighest bound will be inferred:
392
+ /// const MY_CSTR: &'static CBoundedStr<17> = c_bounded_str!("My awesome CStr!");
393
+ /// ```
394
+ ///
395
+ /// ```rust,compile_fail
396
+ /// // This would not compile as the type is `CBoundedStr<17>`.
397
+ /// const MY_CSTR: &'static CBoundedStr<100> = c_bounded_str!("My awesome CStr!");
398
+ /// ```
399
+ ///
400
+ /// ```rust,no_run
401
+ /// // You can relax the bound using the `relax_bound` method.
402
+ /// const MY_CSTR: &'static CBoundedStr<100> = c_bounded_str!("My awesome CStr!").relax_bound();
403
+ ///
404
+ /// // Or alternatively specify a bound when invoking macro.
405
+ /// // In this case the supplied bound must be a constant expression.
406
+ /// const MY_CSTR2: &'static CBoundedStr<100> = c_bounded_str!(100, "My awesome CStr!");
407
+ ///
408
+ /// // Or let the compiler infer the bound for you.
409
+ /// const MY_CSTR3: &'static CBoundedStr<100> = c_bounded_str!(_, "My awesome CStr!");
410
+ /// ```
411
+ ///
412
+ /// ```rust,compile_fail
413
+ /// // shouldn't compile as the string is longer than the specified bound.
414
+ /// const MY_CSTR: &'static CBoundedStr<10> = c_bounded_str!(10, "My awesome CStr!");
415
+ /// const MY_CSTR2: &'static CBoundedStr<10> = c_bounded_str!(_, "My awesome CStr!");
416
+ /// ```
417
+ #[ macro_export]
418
+ macro_rules! c_bounded_str {
419
+ ( $str: literal) => { {
420
+ const S : & $crate:: str :: CStr = $crate:: c_str!( $str) ;
421
+ const C : & $crate:: str :: CBoundedStr <{ S . len_with_nul( ) } > =
422
+ unsafe { $crate:: str :: CBoundedStr :: from_c_str_unchecked( S ) } ;
423
+ C
424
+ } } ;
425
+ ( _, $str: literal) => { {
426
+ $crate:: c_bounded_str!( $str) . relax_bound( )
427
+ } } ;
428
+ ( $bound: expr, $str: literal) => { {
429
+ const C : & $crate:: str :: CBoundedStr <{ $bound } > = $crate:: c_bounded_str!( $str) . relax_bound( ) ;
430
+ C
431
+ } } ;
432
+ }
0 commit comments