44//!
55//! C header: [`include/linux/types.h`](../../../../include/linux/types.h)
66
7+ use core:: ops:: Deref ;
8+ use link_time_panic:: link_time_assert;
9+
710use crate :: bindings;
811use crate :: c_types;
912
@@ -123,6 +126,103 @@ impl CStr {
123126 }
124127}
125128
129+ /// A `NUL`-terminated string that is guaranteed to be shorter than a given
130+ /// length. This type is useful because C-side usually impose maximum length
131+ /// on types.
132+ ///
133+ /// The size parameter `N` represent the maximum number of bytes including NUL.
134+ /// This implies that even though `CBoundedStr<0>` is a well-formed type it cannot
135+ /// be safely created.
136+ #[ repr( transparent) ]
137+ pub struct CBoundedStr < const N : usize > ( CStr ) ;
138+
139+ impl < const N : usize > CBoundedStr < N > {
140+ /// Creates a [`CBoundedStr`] form a [`CStr`].
141+ ///
142+ /// The provided `CStr` must be shorten than `N`.
143+ #[ inline]
144+ pub fn from_c_str ( c_str : & CStr ) -> Result < & Self , CStrConvertError > {
145+ if c_str. 0 . len ( ) > N {
146+ return Err ( CStrConvertError :: BoundExceeded ) ;
147+ }
148+
149+ // SAFETY: We just checked that all properties hold.
150+ Ok ( unsafe { Self :: from_c_str_unchecked ( c_str) } )
151+ }
152+
153+ /// Creates a [`CBoundedStr`] form a [`CStr`] without performing any sanity
154+ /// checks.
155+ ///
156+ /// # Safety
157+ ///
158+ /// The provided CStr must be shorten than `N`.
159+ #[ inline]
160+ pub const unsafe fn from_c_str_unchecked ( c_str : & CStr ) -> & Self {
161+ & * ( c_str as * const CStr as * const Self )
162+ }
163+
164+ /// Creates a [`CBoundedStr`] form a `[u8]`.
165+ ///
166+ /// The provided slice must be nul-terminated, does not contain any
167+ /// interior nul bytes and be shorten than `N`.
168+ #[ inline]
169+ pub fn from_bytes_with_nul ( bytes : & [ u8 ] ) -> Result < & Self , CStrConvertError > {
170+ Self :: from_c_str ( CStr :: from_bytes_with_nul ( bytes) ?)
171+ }
172+
173+ /// Creates a [`CBoundedStr`] form a `[u8]` without performing any sanity
174+ /// checks.
175+ ///
176+ /// # Safety
177+ ///
178+ /// The provided slice must be nul-terminated, does not contain any
179+ /// interior nul bytes and be shorten than `N`.
180+ #[ inline]
181+ pub const unsafe fn from_bytes_with_nul_unchecked ( bytes : & [ u8 ] ) -> & Self {
182+ Self :: from_c_str_unchecked ( CStr :: from_bytes_with_nul_unchecked ( bytes) )
183+ }
184+
185+ /// Creates a [`CBoundedStr`] form a `[u8; N]` without performing any sanity
186+ /// checks.
187+ ///
188+ /// # Safety
189+ ///
190+ /// The provided slice must be nul-terminated.
191+ #[ inline]
192+ pub const unsafe fn from_exact_bytes_with_nul_unchecked ( bytes : & [ u8 ; N ] ) -> & Self {
193+ Self :: from_bytes_with_nul_unchecked ( bytes)
194+ }
195+
196+ /// Relax the bound from `N` to `M`.
197+ ///
198+ /// `M` must be no less than the bound `N`.
199+ #[ inline]
200+ pub const fn relax_bound < const M : usize > ( & self ) -> & CBoundedStr < M > {
201+ link_time_assert ! ( N <= M , "relaxed bound should be no less than current bound" ) ;
202+ unsafe { CBoundedStr :: < M > :: from_c_str_unchecked ( & self . 0 ) }
203+ }
204+
205+ /// Expand the string a c_char array with current bound, filling remaining bytes with zero.
206+ #[ inline]
207+ pub const fn into_char_array ( & self ) -> [ c_types:: c_char ; N ] {
208+ let mut ret: [ c_types:: c_char ; N ] = [ 0 ; N ] ;
209+ let mut i = 0 ;
210+ while i < self . 0 . 0 . len ( ) {
211+ ret[ i] = self . 0 . 0 [ i] as _ ;
212+ i += 1 ;
213+ }
214+ ret
215+ }
216+ }
217+
218+ impl < const N : usize > Deref for CBoundedStr < N > {
219+ type Target = CStr ;
220+
221+ fn deref ( & self ) -> & Self :: Target {
222+ & self . 0
223+ }
224+ }
225+
126226/// Creates a new `CStr` from a string literal.
127227///
128228/// The string literal should not contain any `NUL` bytes.
@@ -142,3 +242,53 @@ macro_rules! c_str {
142242 C
143243 } } ;
144244}
245+
246+ /// Creates a new `CBoundedStr` from a string literal.
247+ ///
248+ /// The string literal should not contain any `NUL` bytes, and its length with NUL should not
249+ /// exceed the bound supplied.
250+ ///
251+ /// # Examples
252+ ///
253+ /// ```rust,no_run
254+ /// // If no bound is specified, the tighest bound will be inferred:
255+ /// const MY_CSTR: &'static CBoundedStr<17> = c_bounded_str!("My awesome CStr!");
256+ /// ```
257+ ///
258+ /// ```rust,compile_fail
259+ /// // This would not compile as the type is `CBoundedStr<17>`.
260+ /// const MY_CSTR: &'static CBoundedStr<100> = c_bounded_str!("My awesome CStr!");
261+ /// ```
262+ ///
263+ /// ```rust,no_run
264+ /// // You can relax the bound using the `relax_bound` method.
265+ /// const MY_CSTR: &'static CBoundedStr<100> = c_bounded_str!("My awesome CStr!").relax_bound();
266+ /// ```
267+ ///
268+ /// ```rust,no_run
269+ /// // Or alternatively specify a bound when invoking macro.
270+ /// const MY_CSTR: &'static CBoundedStr<100> = c_bounded_str!(100, "My awesome CStr!");
271+ /// ```
272+ ///
273+ /// ```rust,compile_fail
274+ /// // shouldn't compile as the string is longer than the specified bound.
275+ /// const MY_CSTR: &'static CBoundedStr<10> = c_bounded_str!(100, "My awesome CStr!");
276+ /// ```
277+ #[ macro_export]
278+ macro_rules! c_bounded_str {
279+ ( $str: literal) => { {
280+ let s = $crate:: c_str_check:: append_nul!( $str) ;
281+ unsafe { $crate:: CBoundedStr :: from_exact_bytes_with_nul_unchecked( s) }
282+ } } ;
283+ ( $bound: expr, $str: literal) => { {
284+ const C : & ' static $crate:: CBoundedStr <$bound> = {
285+ let s = $crate:: c_str_check:: append_nul!( $str) ;
286+ if s. len( ) > $bound {
287+ // NOPANIC: This is a const panic.
288+ panic!( "bound exceeded" ) ;
289+ }
290+ unsafe { $crate:: CBoundedStr :: <$bound>:: from_bytes_with_nul_unchecked( s) }
291+ } ;
292+ C
293+ } } ;
294+ }
0 commit comments