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