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 exceeds the specified bound. Only happens when
39
+ /// constructing a [`CBoundedStr`].
40
+ BoundExceeded ,
41
+
37
42
/// Supplied bytes contain an interior `NUL`.
38
43
InteriorNul ,
39
44
@@ -247,3 +252,188 @@ 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 the C side usually imposes a maximum length
258
+ /// on types.
259
+ ///
260
+ /// The size parameter `N` represents 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 shorter 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 shorter 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, must not contain any
294
+ /// interior `NUL` bytes and must be shorter 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, must not contain any
306
+ /// interior `NUL` bytes and must be shorter 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 of the same bound, filling
333
+ /// the 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 to a `c_char` array of higher bound, filling
346
+ /// the remaining bytes with zero.
347
+ ///
348
+ /// `M` must be no less than the bound `N`.
349
+ #[ inline]
350
+ pub const fn expand_to_char_array < const M : usize > ( & self ) -> [ c_types:: c_char ; M ] {
351
+ self . relax_bound ( ) . to_char_array ( )
352
+ }
353
+ }
354
+
355
+ impl < const N : usize > AsRef < BStr > for CBoundedStr < N > {
356
+ #[ inline]
357
+ fn as_ref ( & self ) -> & BStr {
358
+ self . as_bytes ( )
359
+ }
360
+ }
361
+
362
+ impl < const N : usize > AsRef < CStr > for CBoundedStr < N > {
363
+ #[ inline]
364
+ fn as_ref ( & self ) -> & CStr {
365
+ & self . 0
366
+ }
367
+ }
368
+
369
+ impl < const N : usize > Deref for CBoundedStr < N > {
370
+ type Target = CStr ;
371
+
372
+ #[ inline]
373
+ fn deref ( & self ) -> & Self :: Target {
374
+ & self . 0
375
+ }
376
+ }
377
+
378
+ impl < Idx , const N : usize > Index < Idx > for CBoundedStr < N >
379
+ where
380
+ CStr : Index < Idx > ,
381
+ {
382
+ type Output = <CStr as Index < Idx > >:: Output ;
383
+
384
+ #[ inline]
385
+ fn index ( & self , index : Idx ) -> & Self :: Output {
386
+ & self . 0 [ index]
387
+ }
388
+ }
389
+
390
+ /// Creates a new [`CBoundedStr`] from a string literal.
391
+ ///
392
+ /// The string literal should not contain any `NUL` bytes, and its length with `NUL` should not
393
+ /// exceed the bound supplied.
394
+ ///
395
+ /// # Examples
396
+ ///
397
+ /// ```rust,no_run
398
+ /// // If no bound is specified, the tighest bound will be inferred:
399
+ /// const MY_CSTR: &'static CBoundedStr<17> = c_bounded_str!("My awesome CStr!");
400
+ /// ```
401
+ ///
402
+ /// ```rust,compile_fail
403
+ /// // This does not compile as the inferred type is `CBoundedStr<17>`.
404
+ /// const MY_CSTR: &'static CBoundedStr<100> = c_bounded_str!("My awesome CStr!");
405
+ /// ```
406
+ ///
407
+ /// ```rust,no_run
408
+ /// // You can relax the bound using the `relax_bound` method.
409
+ /// const MY_CSTR: &'static CBoundedStr<100> = c_bounded_str!("My awesome CStr!").relax_bound();
410
+ ///
411
+ /// // Or alternatively specify a bound.
412
+ /// // In this case the supplied bound must be a constant expression.
413
+ /// const MY_CSTR2: &'static CBoundedStr<100> = c_bounded_str!(100, "My awesome CStr!");
414
+ ///
415
+ /// // Or let the compiler infer the bound for you.
416
+ /// const MY_CSTR3: &'static CBoundedStr<100> = c_bounded_str!(_, "My awesome CStr!");
417
+ /// ```
418
+ ///
419
+ /// ```rust,compile_fail
420
+ /// // These do not compile as the string is longer than the specified bound.
421
+ /// const MY_CSTR: &'static CBoundedStr<4> = c_bounded_str!(4, "My awesome CStr!");
422
+ /// const MY_CSTR2: &'static CBoundedStr<4> = c_bounded_str!(_, "My awesome CStr!");
423
+ /// ```
424
+ #[ macro_export]
425
+ macro_rules! c_bounded_str {
426
+ ( $str: literal) => { {
427
+ const S : & $crate:: str :: CStr = $crate:: c_str!( $str) ;
428
+ const C : & $crate:: str :: CBoundedStr <{ S . len_with_nul( ) } > =
429
+ unsafe { $crate:: str :: CBoundedStr :: from_c_str_unchecked( S ) } ;
430
+ C
431
+ } } ;
432
+ ( _, $str: literal) => { {
433
+ $crate:: c_bounded_str!( $str) . relax_bound( )
434
+ } } ;
435
+ ( $bound: expr, $str: literal) => { {
436
+ const C : & $crate:: str :: CBoundedStr <{ $bound } > = $crate:: c_bounded_str!( $str) . relax_bound( ) ;
437
+ C
438
+ } } ;
439
+ }
0 commit comments