@@ -8,14 +8,100 @@ use std::rc::Rc;
88
99use crate :: CStringIterator ;
1010use crate :: rc:: { Ptr , PtrKind } ;
11+ use crate :: reinterpret:: { ByteRepr , with_scratch} ;
1112
12- impl fmt:: Display for Ptr < u8 > {
13+ // The element types of C strings: `char` and `signed char` map to `i8`,
14+ // `unsigned char` to `u8`.
15+ pub trait CChar : Copy + Default + PartialEq + ByteRepr + ' static {
16+ fn to_byte ( self ) -> u8 ;
17+ fn from_byte ( b : u8 ) -> Self ;
18+ // Calls `f` with `buf` viewed as a slice of `Self`; changes are copied back.
19+ fn with_bytes_mut < R > ( buf : & mut [ u8 ] , f : impl FnOnce ( & mut [ Self ] ) -> R ) -> R ;
20+ // Calls `f` with `s` viewed as bytes. Free for u8; i8 needs a copy, which
21+ // lives on the stack for short slices.
22+ fn with_u8_slice < R > ( s : & [ Self ] , f : impl FnOnce ( & [ u8 ] ) -> R ) -> R ;
23+ // Conversions between vectors of Self and of bytes, without reallocating.
24+ fn into_byte_vec ( v : Vec < Self > ) -> Vec < u8 > ;
25+ fn from_byte_vec ( v : Vec < u8 > ) -> Vec < Self > ;
26+
27+ // The array initialized by a narrow string literal, e.g., char s[] = "abc".
28+ fn array_from_literal < const N : usize > ( s : & [ u8 ; N ] ) -> Box < [ Self ] > {
29+ Box :: new ( s. map ( Self :: from_byte) )
30+ }
31+ }
32+
33+ impl CChar for u8 {
34+ #[ inline( always) ]
35+ fn to_byte ( self ) -> u8 {
36+ self
37+ }
38+ #[ inline( always) ]
39+ fn from_byte ( b : u8 ) -> Self {
40+ b
41+ }
42+ #[ inline( always) ]
43+ fn with_bytes_mut < R > ( buf : & mut [ u8 ] , f : impl FnOnce ( & mut [ Self ] ) -> R ) -> R {
44+ f ( buf)
45+ }
46+ #[ inline( always) ]
47+ fn with_u8_slice < R > ( s : & [ Self ] , f : impl FnOnce ( & [ u8 ] ) -> R ) -> R {
48+ f ( s)
49+ }
50+ #[ inline( always) ]
51+ fn into_byte_vec ( v : Vec < Self > ) -> Vec < u8 > {
52+ v
53+ }
54+ #[ inline( always) ]
55+ fn from_byte_vec ( v : Vec < u8 > ) -> Vec < Self > {
56+ v
57+ }
58+ }
59+
60+ impl CChar for i8 {
61+ #[ inline( always) ]
62+ fn to_byte ( self ) -> u8 {
63+ self as u8
64+ }
65+ #[ inline( always) ]
66+ fn from_byte ( b : u8 ) -> Self {
67+ b as i8
68+ }
69+ fn with_bytes_mut < R > ( buf : & mut [ u8 ] , f : impl FnOnce ( & mut [ Self ] ) -> R ) -> R {
70+ with_scratch ( buf. len ( ) , |chars : & mut [ i8 ] | {
71+ for ( c, & b) in chars. iter_mut ( ) . zip ( buf. iter ( ) ) {
72+ * c = b as i8 ;
73+ }
74+ let r = f ( chars) ;
75+ for ( b, & c) in buf. iter_mut ( ) . zip ( chars. iter ( ) ) {
76+ * b = c as u8 ;
77+ }
78+ r
79+ } )
80+ }
81+ fn with_u8_slice < R > ( s : & [ Self ] , f : impl FnOnce ( & [ u8 ] ) -> R ) -> R {
82+ with_scratch ( s. len ( ) , |buf : & mut [ u8 ] | {
83+ for ( b, & c) in buf. iter_mut ( ) . zip ( s) {
84+ * b = c as u8 ;
85+ }
86+ f ( buf)
87+ } )
88+ }
89+ // Both collects reuse the allocation, as i8 and u8 have the same layout.
90+ fn into_byte_vec ( v : Vec < Self > ) -> Vec < u8 > {
91+ v. into_iter ( ) . map ( |c| c as u8 ) . collect ( )
92+ }
93+ fn from_byte_vec ( v : Vec < u8 > ) -> Vec < Self > {
94+ v. into_iter ( ) . map ( |b| b as i8 ) . collect ( )
95+ }
96+ }
97+
98+ impl < T : CChar > fmt:: Display for Ptr < T > {
1399 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
14100 match & self . kind {
15101 PtrKind :: Null => write ! ( f, "NULL" ) ,
16102 _ => {
17103 for value in self {
18- let ch = value. read ( ) ;
104+ let ch = value. read ( ) . to_byte ( ) ;
19105 if ch == 0 {
20106 break ;
21107 }
@@ -62,7 +148,35 @@ impl_string_literal!(u16, STRING_LITERALS_U16);
62148impl_string_literal ! ( u32 , STRING_LITERALS_U32 ) ;
63149impl_string_literal ! ( i32 , STRING_LITERALS_I32 ) ;
64150
65- impl Ptr < u8 > {
151+ thread_local ! {
152+ static STRING_LITERALS_I8 : RefCell <HashMap <& ' static [ u8 ] , Rc <RefCell <Box <[ i8 ] >>>>> =
153+ RefCell :: new( HashMap :: new( ) ) ;
154+ }
155+
156+ impl Ptr < i8 > {
157+ // Narrow string literals are emitted as byte strings (b"..."), as Rust has
158+ // no literal syntax for `[i8]`.
159+ #[ inline]
160+ pub fn from_string_literal ( s : & ' static [ u8 ] ) -> Self {
161+ STRING_LITERALS_I8 . with ( |literals| {
162+ let mut literals = literals. borrow_mut ( ) ;
163+ let weak = Rc :: downgrade ( literals. entry ( s) . or_insert_with ( || {
164+ Rc :: new ( RefCell :: new (
165+ s. iter ( )
166+ . map ( |& c| c as i8 )
167+ . chain ( std:: iter:: once ( 0 ) )
168+ . collect ( ) ,
169+ ) )
170+ } ) ) ;
171+ Ptr {
172+ offset : 0 ,
173+ kind : PtrKind :: StackArray ( weak) ,
174+ }
175+ } )
176+ }
177+ }
178+
179+ impl < T : CChar > Ptr < T > {
66180 #[ allow( clippy:: explicit_counter_loop) ]
67181 pub fn memcpy ( & self , src : & Self , len : usize ) {
68182 if * self > * src {
@@ -89,7 +203,7 @@ impl Ptr<u8> {
89203 }
90204
91205 #[ allow( clippy:: explicit_counter_loop) ]
92- pub fn memset ( & self , value : u8 , num : usize ) {
206+ pub fn memset ( & self , value : T , num : usize ) {
93207 let mut dst = self . clone ( ) ;
94208 for _ in 0 ..num {
95209 dst. write ( value) ;
@@ -102,8 +216,9 @@ impl Ptr<u8> {
102216 let mut a = self . clone ( ) ;
103217 let mut b = other. clone ( ) ;
104218 for _ in 0 ..len {
105- let va = a. read ( ) ;
106- let vb = b. read ( ) ;
219+ // Bytes compare as unsigned char.
220+ let va = a. read ( ) . to_byte ( ) ;
221+ let vb = b. read ( ) . to_byte ( ) ;
107222 if va != vb {
108223 return ( va as i32 ) . wrapping_sub ( vb as i32 ) ;
109224 }
@@ -113,15 +228,15 @@ impl Ptr<u8> {
113228 0
114229 }
115230
116- pub fn to_c_string_iterator ( & self ) -> CStringIterator {
231+ pub fn to_c_string_iterator ( & self ) -> CStringIterator < T > {
117232 CStringIterator { ptr : self . clone ( ) }
118233 }
119234
120235 // Calls `f` with the bytes of the C string, excluding the terminating NUL.
121236 // The bytes are borrowed from the allocation, so `f` must not write to it.
122- pub fn with_c_str < R > ( & self , f : impl FnOnce ( & [ u8 ] ) -> R ) -> R {
123- fn until_nul ( tail : & [ u8 ] ) -> & [ u8 ] {
124- match tail. iter ( ) . position ( |& b| b == 0 ) {
237+ pub fn with_c_str < R > ( & self , f : impl FnOnce ( & [ T ] ) -> R ) -> R {
238+ fn until_nul < T : CChar > ( tail : & [ T ] ) -> & [ T ] {
239+ match tail. iter ( ) . position ( |& b| b == T :: default ( ) ) {
125240 Some ( len) => & tail[ ..len] ,
126241 None => panic ! ( "ub: unterminated string" ) ,
127242 }
@@ -146,10 +261,10 @@ impl Ptr<u8> {
146261 }
147262 PtrKind :: Field ( root) => {
148263 let root = crate :: field:: upgrade ( root) ;
149- let b = Ptr :: < u8 > :: borrow_field ( & * root, self . offset ) ;
264+ let b = Ptr :: < T > :: borrow_field ( & * root, self . offset ) ;
150265 f ( until_nul ( std:: slice:: from_ref ( & * b) ) )
151266 }
152- PtrKind :: Reinterpreted ( _) => f ( & self . to_c_string_iterator ( ) . collect :: < Vec < u8 > > ( ) ) ,
267+ PtrKind :: Reinterpreted ( _) => f ( & self . to_c_string_iterator ( ) . collect :: < Vec < T > > ( ) ) ,
153268 }
154269 }
155270
@@ -161,16 +276,46 @@ impl Ptr<u8> {
161276 // Copies the C string, excluding the terminating NUL, using a single
162277 // allocation. The vector has room for one more byte, which is what callers
163278 // usually append (a NUL or a newline).
164- pub fn to_c_bytes ( & self ) -> Vec < u8 > {
279+ pub fn to_c_bytes ( & self ) -> Vec < T > {
165280 self . with_c_str ( |s| {
166281 let mut bytes = Vec :: with_capacity ( s. len ( ) + 1 ) ;
167282 bytes. extend_from_slice ( s) ;
168283 bytes
169284 } )
170285 }
171286
287+ // Copies `bytes` to the buffer pointed to, without adding a NUL.
288+ pub fn write_c_bytes ( & self , bytes : & [ u8 ] ) {
289+ self . with_slice_mut ( bytes. len ( ) , |s| {
290+ for ( d, & b) in s. iter_mut ( ) . zip ( bytes) {
291+ * d = T :: from_byte ( b) ;
292+ }
293+ } )
294+ }
295+
296+ // Allocates a heap copy of `bytes` followed by a NUL, like `strdup`.
297+ pub fn alloc_c_str ( bytes : & [ u8 ] ) -> Self {
298+ Ptr :: alloc_array (
299+ bytes
300+ . iter ( )
301+ . map ( |& b| T :: from_byte ( b) )
302+ . chain ( std:: iter:: once ( T :: default ( ) ) )
303+ . collect ( ) ,
304+ )
305+ }
306+
307+ // Like `with_c_str`, with the C string viewed as bytes.
308+ pub fn with_c_bytes < R > ( & self , f : impl FnOnce ( & [ u8 ] ) -> R ) -> R {
309+ self . with_c_str ( |s| T :: with_u8_slice ( s, f) )
310+ }
311+
312+ // Like `to_c_bytes`, as bytes.
313+ pub fn to_c_u8_bytes ( & self ) -> Vec < u8 > {
314+ T :: into_byte_vec ( self . to_c_bytes ( ) )
315+ }
316+
172317 pub fn to_rust_string ( & self ) -> String {
173- self . with_c_str ( |s| String :: from_utf8_lossy ( s) . into_owned ( ) )
318+ self . with_c_bytes ( |s| String :: from_utf8_lossy ( s) . into_owned ( ) )
174319 }
175320}
176321
@@ -228,6 +373,24 @@ mod tests {
228373 assert_eq ! ( p. to_rust_string( ) , "" ) ;
229374 }
230375
376+ #[ test]
377+ fn signed_char_strings ( ) {
378+ let p = Ptr :: < i8 > :: from_string_literal ( b"\xe9 a" ) ;
379+ assert_eq ! ( p. read( ) , -23 ) ;
380+ assert_eq ! ( p. c_str_len( ) , 2 ) ;
381+ assert_eq ! ( p. to_c_bytes( ) , [ -23 , b'a' as i8 ] ) ;
382+ assert_eq ! ( p. to_c_u8_bytes( ) , b"\xe9 a" ) ;
383+ // memcmp compares bytes as unsigned char.
384+ let q = Ptr :: < i8 > :: from_string_literal ( b"a" ) ;
385+ assert ! ( p. memcmp( & q, 1 ) > 0 ) ;
386+
387+ let buf = Ptr :: < i8 > :: alloc_c_str ( b"xyz" ) ;
388+ buf. write_c_bytes ( b"\xff " ) ;
389+ assert_eq ! ( buf. read( ) , -1 ) ;
390+ assert_eq ! ( buf. to_c_u8_bytes( ) , b"\xff yz" ) ;
391+ buf. delete ( ) ;
392+ }
393+
231394 #[ test]
232395 #[ should_panic( expected = "ub: unterminated string" ) ]
233396 fn unterminated_string_panics ( ) {
0 commit comments