4
4
//!
5
5
//! C header: [`include/linux/types.h`](../../../../include/linux/types.h)
6
6
7
+ use core:: ops:: Deref ;
8
+ use link_time_panic:: link_time_assert;
9
+
7
10
use crate :: bindings;
8
11
use crate :: c_types;
9
12
@@ -123,6 +126,103 @@ impl CStr {
123
126
}
124
127
}
125
128
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
+
126
226
/// Creates a new `CStr` from a string literal.
127
227
///
128
228
/// The string literal should not contain any `NUL` bytes.
@@ -142,3 +242,53 @@ macro_rules! c_str {
142
242
C
143
243
} } ;
144
244
}
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