55use core:: ops:: { self , Deref , Index } ;
66
77use crate :: bindings;
8+ use crate :: build_assert;
89use crate :: c_types;
910
1011/// Byte string without UTF-8 validity guarantee.
@@ -31,9 +32,13 @@ macro_rules! b_str {
3132 } } ;
3233}
3334
34- /// Possible errors when using conversion functions in [`CStr`].
35+ /// Possible errors when using conversion functions in [`CStr`] and [`CBoundedStr`] .
3536#[ derive( Debug , Clone , Copy ) ]
3637pub enum CStrConvertError {
38+ /// Supplied string length exceeds the specified bound. Only happens when
39+ /// constructing a [`CBoundedStr`].
40+ BoundExceeded ,
41+
3742 /// Supplied bytes contain an interior `NUL`.
3843 InteriorNul ,
3944
@@ -247,3 +252,188 @@ macro_rules! c_str {
247252 C
248253 } } ;
249254}
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