@@ -2,6 +2,28 @@ use core::{
22 ffi:: c_char, iter:: FusedIterator , marker:: PhantomData , num:: NonZeroUsize , ptr:: NonNull , slice,
33} ;
44
5+ mod private {
6+ pub trait Sealed { }
7+
8+ impl Sealed for u8 { }
9+ impl Sealed for u16 { }
10+ }
11+
12+ /// A code unit supported by [`CStr`].
13+ pub trait CStrUnit : private:: Sealed + Copy + Eq {
14+ /// The terminating code unit.
15+ #[ doc( hidden) ]
16+ const NUL : Self ;
17+ }
18+
19+ impl CStrUnit for u8 {
20+ const NUL : Self = 0 ;
21+ }
22+
23+ impl CStrUnit for u16 {
24+ const NUL : Self = 0 ;
25+ }
26+
527/// Marks a [`CStr`] whose length is not known.
628#[ derive( Clone , Copy ) ]
729pub struct Thin {
@@ -14,50 +36,56 @@ pub struct Fat {
1436 len_with_nul : NonZeroUsize ,
1537}
1638
17- /// A borrowed NUL-terminated string.
39+ /// A borrowed NUL-terminated string of code units .
1840///
1941/// [`CStr<'_, Thin>`] stores only the string pointer, while
2042/// [`CStr<'_, Fat>`] also stores the length including the terminating NUL.
2143#[ derive( Clone , Copy ) ]
22- pub struct CStr < ' a , R > {
23- ptr : NonNull < c_char > ,
44+ pub struct CStr < ' a , R , U : CStrUnit = u8 > {
45+ ptr : NonNull < U > ,
2446 repr : R ,
25- lifetime : PhantomData < & ' a c_char > ,
47+ lifetime : PhantomData < & ' a U > ,
2648}
2749
28- /// An iterator over the non-NUL bytes of a thin C string.
50+ /// A borrowed NUL-terminated string of `u16` code units.
51+ pub type WideCStr < ' a , R > = CStr < ' a , R , u16 > ;
52+
53+ /// An iterator over the non-NUL code units of a thin C string.
2954#[ derive( Clone ) ]
30- pub struct Bytes < ' a > {
31- ptr : NonNull < u8 > ,
32- lifetime : PhantomData < & ' a u8 > ,
55+ pub struct Units < ' a , U : CStrUnit > {
56+ ptr : NonNull < U > ,
57+ lifetime : PhantomData < & ' a U > ,
3358}
3459
35- impl Iterator for Bytes < ' _ > {
36- type Item = u8 ;
60+ impl < U : CStrUnit > Iterator for Units < ' _ , U > {
61+ type Item = U ;
3762
3863 #[ inline]
3964 fn next ( & mut self ) -> Option < Self :: Item > {
4065 // SAFETY: `ptr` starts within a valid C string and is advanced only
41- // after reading a non-NUL byte , so it remains readable and never moves
42- // beyond the terminating NUL.
66+ // after reading a non-NUL code unit , so it remains readable and never
67+ // moves beyond the terminating NUL.
4368 unsafe {
44- let byte = self . ptr . read ( ) ;
45- if byte == 0 {
69+ let unit = self . ptr . read ( ) ;
70+ if unit == U :: NUL {
4671 None
4772 } else {
4873 self . ptr = self . ptr . add ( 1 ) ;
49- Some ( byte )
74+ Some ( unit )
5075 }
5176 }
5277 }
5378}
5479
55- impl FusedIterator for Bytes < ' _ > { }
80+ impl < U : CStrUnit > FusedIterator for Units < ' _ , U > { }
5681
57- impl < R > CStr < ' _ , R > {
58- /// Returns a pointer to the first byte of this C string.
82+ /// An iterator over the non-NUL bytes of a thin byte C string.
83+ pub type Bytes < ' a > = Units < ' a , u8 > ;
84+
85+ impl < R , U : CStrUnit > CStr < ' _ , R , U > {
86+ /// Returns a pointer to the first code unit of this C string.
5987 #[ must_use]
60- pub const fn as_ptr ( & self ) -> * const c_char {
88+ pub const fn as_units_ptr ( & self ) -> * const U {
6189 self . ptr . as_ptr ( )
6290 }
6391
@@ -68,6 +96,22 @@ impl<R> CStr<'_, R> {
6896 }
6997}
7098
99+ impl < R > CStr < ' _ , R > {
100+ /// Returns a pointer to the first byte of this C string.
101+ #[ must_use]
102+ pub const fn as_ptr ( & self ) -> * const c_char {
103+ self . ptr . as_ptr ( ) . cast ( )
104+ }
105+ }
106+
107+ impl < R > CStr < ' _ , R , u16 > {
108+ /// Returns a pointer to the first UTF-16 code unit of this C string.
109+ #[ must_use]
110+ pub const fn as_ptr ( & self ) -> * const u16 {
111+ self . ptr . as_ptr ( )
112+ }
113+ }
114+
71115impl Fat {
72116 /// Returns the represented length, including the terminating NUL.
73117 #[ must_use]
@@ -76,105 +120,168 @@ impl Fat {
76120 }
77121}
78122
79- impl < ' a > CStr < ' a , Thin > {
80- /// Creates a thin C string view from a non-null pointer without finding
81- /// its length.
123+ impl < ' a , U : CStrUnit > CStr < ' a , Thin , U > {
124+ /// Creates a thin C string view from a non-null code-unit pointer without
125+ /// finding its length.
82126 ///
83127 /// # Safety
84128 ///
85129 /// `ptr` must point to an immutable NUL-terminated string that remains
86130 /// valid for the lifetime of the returned view.
87131 #[ must_use]
88- pub const unsafe fn from_non_null ( ptr : NonNull < c_char > ) -> Self {
132+ pub const unsafe fn from_units_non_null ( ptr : NonNull < U > ) -> Self {
89133 Self { ptr, repr : Thin { _private : ( ) } , lifetime : PhantomData }
90134 }
91135
92- /// Creates a thin C string view without finding its length.
136+ /// Creates a thin C string view from a code-unit pointer without finding
137+ /// its length.
93138 ///
94139 /// # Safety
95140 ///
96141 /// `ptr` must be non-null and point to an immutable NUL-terminated string
97142 /// that remains valid for the lifetime of the returned view.
98143 #[ must_use]
99- pub const unsafe fn from_ptr ( ptr : * const c_char ) -> Self {
144+ pub const unsafe fn from_units_ptr ( ptr : * const U ) -> Self {
100145 // SAFETY: the caller guarantees that `ptr` is non-null.
101146 let ptr = unsafe { NonNull :: new_unchecked ( ptr. cast_mut ( ) ) } ;
102147 // SAFETY: the caller guarantees the remaining C string invariants.
103- unsafe { Self :: from_non_null ( ptr) }
148+ unsafe { Self :: from_units_non_null ( ptr) }
104149 }
105150
106- /// Returns an iterator over the bytes before the terminating NUL.
151+ /// Returns an iterator over the code units before the terminating NUL.
107152 #[ inline]
108153 #[ must_use]
109- pub const fn bytes ( self ) -> Bytes < ' a > {
110- Bytes { ptr : self . ptr . cast ( ) , lifetime : PhantomData }
154+ pub const fn units ( self ) -> Units < ' a , U > {
155+ Units { ptr : self . ptr , lifetime : PhantomData }
111156 }
112157
113158 /// Counts through the terminating NUL and returns a length-retaining view.
114159 #[ inline]
115160 #[ must_use]
116- pub fn count ( self ) -> CStr < ' a , Fat > {
117- let count = self . bytes ( ) . count ( ) ;
161+ pub fn count ( self ) -> CStr < ' a , Fat , U > {
162+ let count = self . units ( ) . count ( ) ;
118163
119164 CStr {
120165 ptr : self . ptr ,
121166 repr : Fat {
122167 // SAFETY: adding the terminator makes the represented length
123168 // nonzero, and a valid allocation cannot contain `usize::MAX`
124- // non-NUL bytes .
169+ // non-NUL code units .
125170 len_with_nul : unsafe { NonZeroUsize :: new_unchecked ( count + 1 ) } ,
126171 } ,
127172 lifetime : PhantomData ,
128173 }
129174 }
130175}
131176
132- impl < ' a > CStr < ' a , Fat > {
133- /// Creates a length-retaining C string from bytes without validation.
177+ impl < ' a > CStr < ' a , Thin > {
178+ /// Creates a thin C string view from a non-null pointer without finding
179+ /// its length.
134180 ///
135181 /// # Safety
136182 ///
137- /// `bytes ` must end with exactly one NUL byte and contain no other NUL
138- /// bytes .
183+ /// `ptr ` must point to an immutable NUL-terminated string that remains
184+ /// valid for the lifetime of the returned view .
139185 #[ must_use]
140- pub const unsafe fn from_bytes_with_nul_unchecked ( bytes : & ' a [ u8 ] ) -> Self {
186+ pub const unsafe fn from_non_null ( ptr : NonNull < c_char > ) -> Self {
187+ // SAFETY: the caller guarantees the C string invariants.
188+ unsafe { Self :: from_units_non_null ( ptr. cast ( ) ) }
189+ }
190+
191+ /// Creates a thin C string view without finding its length.
192+ ///
193+ /// # Safety
194+ ///
195+ /// `ptr` must be non-null and point to an immutable NUL-terminated string
196+ /// that remains valid for the lifetime of the returned view.
197+ #[ must_use]
198+ pub const unsafe fn from_ptr ( ptr : * const c_char ) -> Self {
199+ // SAFETY: the caller guarantees that `ptr` is non-null.
200+ let ptr = unsafe { NonNull :: new_unchecked ( ptr. cast_mut ( ) ) } ;
201+ // SAFETY: the caller guarantees the remaining C string invariants.
202+ unsafe { Self :: from_non_null ( ptr) }
203+ }
204+
205+ /// Returns an iterator over the bytes before the terminating NUL.
206+ #[ inline]
207+ #[ must_use]
208+ pub const fn bytes ( self ) -> Bytes < ' a > {
209+ self . units ( )
210+ }
211+ }
212+
213+ impl < ' a , U : CStrUnit > CStr < ' a , Fat , U > {
214+ /// Creates a length-retaining C string from code units without validation.
215+ ///
216+ /// # Safety
217+ ///
218+ /// `units` must end with exactly one NUL code unit and contain no other
219+ /// NUL code units.
220+ #[ must_use]
221+ pub const unsafe fn from_units_with_nul_unchecked ( units : & ' a [ U ] ) -> Self {
141222 Self {
142223 // SAFETY: a valid C string is nonempty, so its pointer is non-null.
143- ptr : unsafe { NonNull :: new_unchecked ( bytes . as_ptr ( ) . cast :: < c_char > ( ) . cast_mut ( ) ) } ,
224+ ptr : unsafe { NonNull :: new_unchecked ( units . as_ptr ( ) . cast_mut ( ) ) } ,
144225 repr : Fat {
145226 // SAFETY: a valid C string contains at least its terminating NUL.
146- len_with_nul : unsafe { NonZeroUsize :: new_unchecked ( bytes . len ( ) ) } ,
227+ len_with_nul : unsafe { NonZeroUsize :: new_unchecked ( units . len ( ) ) } ,
147228 } ,
148229 lifetime : PhantomData ,
149230 }
150231 }
151232
152- /// Returns the string's bytes without the terminating NUL.
233+ /// Returns the string's code units without the terminating NUL.
153234 #[ must_use]
154- pub const fn as_bytes ( & self ) -> & ' a [ u8 ] {
155- let bytes = self . as_bytes_with_nul ( ) ;
156- bytes . split_at ( bytes . len ( ) - 1 ) . 0
235+ pub const fn as_units ( & self ) -> & ' a [ U ] {
236+ let units = self . as_units_with_nul ( ) ;
237+ units . split_at ( units . len ( ) - 1 ) . 0
157238 }
158239
159- /// Returns the string's bytes , including the terminating NUL.
240+ /// Returns the string's code units , including the terminating NUL.
160241 #[ must_use]
161- pub const fn as_bytes_with_nul ( & self ) -> & ' a [ u8 ] {
242+ pub const fn as_units_with_nul ( & self ) -> & ' a [ U ] {
162243 // SAFETY: this view carries the exact initialized C string length.
163- unsafe { slice:: from_raw_parts ( self . ptr . as_ptr ( ) . cast ( ) , self . len_with_nul ( ) ) }
244+ unsafe { slice:: from_raw_parts ( self . ptr . as_ptr ( ) , self . len_with_nul ( ) ) }
164245 }
165246
166- /// Returns the number of bytes including the terminating NUL.
247+ /// Returns the number of code units including the terminating NUL.
167248 #[ must_use]
168249 pub const fn len_with_nul ( & self ) -> usize {
169250 self . repr . len_with_nul ( )
170251 }
171252}
172253
254+ impl < ' a > CStr < ' a , Fat > {
255+ /// Creates a length-retaining C string from bytes without validation.
256+ ///
257+ /// # Safety
258+ ///
259+ /// `bytes` must end with exactly one NUL byte and contain no other NUL
260+ /// bytes.
261+ #[ must_use]
262+ pub const unsafe fn from_bytes_with_nul_unchecked ( bytes : & ' a [ u8 ] ) -> Self {
263+ // SAFETY: the caller guarantees the generic C string invariants.
264+ unsafe { Self :: from_units_with_nul_unchecked ( bytes) }
265+ }
266+
267+ /// Returns the string's bytes without the terminating NUL.
268+ #[ must_use]
269+ pub const fn as_bytes ( & self ) -> & ' a [ u8 ] {
270+ self . as_units ( )
271+ }
272+
273+ /// Returns the string's bytes, including the terminating NUL.
274+ #[ must_use]
275+ pub const fn as_bytes_with_nul ( & self ) -> & ' a [ u8 ] {
276+ self . as_units_with_nul ( )
277+ }
278+ }
279+
173280#[ cfg( test) ]
174281mod tests {
175282 use core:: { mem:: size_of, ptr:: NonNull } ;
176283
177- use super :: { CStr , Fat , Thin } ;
284+ use super :: { CStr , Fat , Thin , WideCStr } ;
178285
179286 #[ test]
180287 fn representations_retain_the_expected_metadata ( ) {
@@ -185,6 +292,8 @@ mod tests {
185292
186293 assert_eq ! ( size_of:: <CStr <' _, Thin >>( ) , size_of:: <* const u8 >( ) ) ;
187294 assert_eq ! ( size_of:: <CStr <' _, Fat >>( ) , size_of:: <( * const u8 , usize ) >( ) ) ;
295+ assert_eq ! ( size_of:: <WideCStr <' _, Thin >>( ) , size_of:: <* const u16 >( ) ) ;
296+ assert_eq ! ( size_of:: <WideCStr <' _, Fat >>( ) , size_of:: <( * const u16 , usize ) >( ) ) ;
188297 assert_eq ! ( fat. len_with_nul( ) , 4 ) ;
189298 assert_eq ! ( fat. as_bytes( ) , b"abc" ) ;
190299 assert_eq ! ( fat. as_bytes_with_nul( ) , b"abc\0 " ) ;
@@ -212,4 +321,18 @@ mod tests {
212321 assert_eq ! ( bytes. next( ) , None ) ;
213322 assert_eq ! ( bytes. next( ) , None ) ;
214323 }
324+
325+ #[ test]
326+ fn wide_strings_iterate_and_retain_code_unit_lengths ( ) {
327+ let units = [ u16:: from ( b'a' ) , u16:: from ( b'b' ) , 0 ] ;
328+ // SAFETY: the input contains one trailing NUL.
329+ let fat = unsafe { WideCStr :: < Fat > :: from_units_with_nul_unchecked ( & units) } ;
330+ // SAFETY: the same input is immutable and NUL-terminated.
331+ let thin = unsafe { WideCStr :: < Thin > :: from_units_ptr ( units. as_ptr ( ) ) } ;
332+
333+ assert ! ( thin. units( ) . eq( [ u16 :: from( b'a' ) , u16 :: from( b'b' ) ] ) ) ;
334+ assert_eq ! ( thin. count( ) . as_units_with_nul( ) , units) ;
335+ assert_eq ! ( fat. as_units( ) , [ u16 :: from( b'a' ) , u16 :: from( b'b' ) ] ) ;
336+ assert_eq ! ( fat. len_with_nul( ) , 3 ) ;
337+ }
215338}
0 commit comments