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,88 @@ 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
+ /// Expand the string a c_char array, filling remaining bytes with zero.
186
+ ///
187
+ /// `M` must be no less than the bound `N`.
188
+ pub const fn expand_into_char_array < const M : usize > ( & self ) -> [ c_types:: c_char ; M ] {
189
+ link_time_assert ! (
190
+ N <= M ,
191
+ "length of char array must be no less than the bound"
192
+ ) ;
193
+ let mut ret: [ c_types:: c_char ; M ] = [ 0 ; M ] ;
194
+ let mut i = 0 ;
195
+ while i < self . 0 . 0 . len ( ) {
196
+ ret[ i] = self . 0 . 0 [ i] as _ ;
197
+ i += 1 ;
198
+ }
199
+ ret
200
+ }
201
+ }
202
+
203
+ impl < const N : usize > Deref for CBoundedStr < N > {
204
+ type Target = CStr ;
205
+
206
+ fn deref ( & self ) -> & Self :: Target {
207
+ & self . 0
208
+ }
209
+ }
210
+
126
211
/// Creates a new `CStr` from a string literal.
127
212
///
128
213
/// The string literal should not contain any `NUL` bytes.
@@ -142,3 +227,33 @@ macro_rules! c_str {
142
227
C
143
228
} } ;
144
229
}
230
+
231
+ /// Creates a new `CBoundedStr` from a string literal.
232
+ ///
233
+ /// The string literal should not contain any `NUL` bytes, and its length with NUL should not
234
+ /// exceed the bound supplied.
235
+ ///
236
+ /// # Examples
237
+ ///
238
+ /// ```rust,no_run
239
+ /// const MY_CSTR: &'static CBoundedStr<100> = c_bounded_str!("My awesome CStr!");
240
+ /// ```
241
+ ///
242
+ /// ```rust,compile_fail
243
+ /// // shouldn't compile as the string is longer than the specified bound.
244
+ /// const MY_CSTR: &'static CBoundedStr<10> = c_bounded_str!("My awesome CStr!");
245
+ /// ```
246
+ #[ macro_export]
247
+ macro_rules! c_bounded_str {
248
+ ( $bound: expr, $str: literal) => { {
249
+ const C : & ' static $crate:: CBoundedStr <$bound> = {
250
+ let s = $crate:: c_str_check:: append_nul!( $str) ;
251
+ if s. len( ) > $bound {
252
+ // NOPANIC: This is a const panic.
253
+ panic!( "bound exceeded" ) ;
254
+ }
255
+ unsafe { $crate:: CBoundedStr :: <$bound>:: from_bytes_with_nul_unchecked( s) }
256
+ } ;
257
+ C
258
+ } } ;
259
+ }
0 commit comments