@@ -117,8 +117,116 @@ impl Ptr<u8> {
117117 CStringIterator { ptr : self . clone ( ) }
118118 }
119119
120+ // Calls `f` with the bytes of the C string, excluding the terminating NUL.
121+ // 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 ) {
125+ Some ( len) => & tail[ ..len] ,
126+ None => panic ! ( "ub: unterminated string" ) ,
127+ }
128+ }
129+ match & self . kind {
130+ PtrKind :: Null => panic ! ( "ub: null pointer" ) ,
131+ PtrKind :: StackSingle ( weak) | PtrKind :: HeapSingle ( weak) => {
132+ assert_eq ! ( self . offset, 0 , "ub: invalid offset" ) ;
133+ let rc = weak. upgrade ( ) . expect ( "ub: dangling pointer" ) ;
134+ let b = rc. borrow ( ) ;
135+ f ( until_nul ( std:: slice:: from_ref ( & * b) ) )
136+ }
137+ PtrKind :: StackArray ( weak) | PtrKind :: HeapArray ( weak) => {
138+ let rc = weak. upgrade ( ) . expect ( "ub: dangling pointer" ) ;
139+ let b = rc. borrow ( ) ;
140+ f ( until_nul ( & b[ self . offset ..] ) )
141+ }
142+ PtrKind :: StackVec ( weak) | PtrKind :: HeapVec ( weak) => {
143+ let rc = weak. upgrade ( ) . expect ( "ub: dangling pointer" ) ;
144+ let b = rc. borrow ( ) ;
145+ f ( until_nul ( & b[ self . offset ..] ) )
146+ }
147+ PtrKind :: Reinterpreted ( _) => f ( & self . to_c_string_iterator ( ) . collect :: < Vec < u8 > > ( ) ) ,
148+ }
149+ }
150+
151+ // The length of the C string, like `strlen`.
152+ pub fn c_str_len ( & self ) -> usize {
153+ self . with_c_str ( |s| s. len ( ) )
154+ }
155+
156+ // Copies the C string, excluding the terminating NUL, using a single
157+ // allocation. The vector has room for one more byte, which is what callers
158+ // usually append (a NUL or a newline).
159+ pub fn to_c_bytes ( & self ) -> Vec < u8 > {
160+ self . with_c_str ( |s| {
161+ let mut bytes = Vec :: with_capacity ( s. len ( ) + 1 ) ;
162+ bytes. extend_from_slice ( s) ;
163+ bytes
164+ } )
165+ }
166+
120167 pub fn to_rust_string ( & self ) -> String {
121- let bytes: Vec < u8 > = self . to_c_string_iterator ( ) . collect ( ) ;
122- String :: from_utf8_lossy ( & bytes) . into_owned ( )
168+ self . with_c_str ( |s| String :: from_utf8_lossy ( s) . into_owned ( ) )
169+ }
170+ }
171+
172+ #[ cfg( test) ]
173+ mod tests {
174+ use super :: * ;
175+ use crate :: AsPointer ;
176+
177+ // "ab\0cd\0": C string functions must stop at the first NUL.
178+ const EMBEDDED_NUL : [ u8 ; 6 ] = [ b'a' , b'b' , 0 , b'c' , b'd' , 0 ] ;
179+
180+ fn check ( p : & Ptr < u8 > ) {
181+ assert_eq ! ( p. c_str_len( ) , 2 ) ;
182+ assert_eq ! ( p. to_c_bytes( ) , b"ab" ) ;
183+ assert_eq ! ( p. to_rust_string( ) , "ab" ) ;
184+ assert_eq ! ( p. to_c_string_iterator( ) . count( ) , 2 ) ;
185+ assert_eq ! ( p. to_c_string_iterator( ) . collect:: <Vec <u8 >>( ) , b"ab" ) ;
186+ assert_eq ! ( p. with_c_str( |s| s. to_vec( ) ) , b"ab" ) ;
187+ assert_eq ! ( p. offset( 3 ) . to_c_bytes( ) , b"cd" ) ;
188+ assert_eq ! ( p. offset( 2 ) . c_str_len( ) , 0 ) ;
189+ }
190+
191+ #[ test]
192+ fn stops_at_first_nul_in_array ( ) {
193+ let p = Ptr :: alloc_array ( EMBEDDED_NUL . to_vec ( ) . into_boxed_slice ( ) ) ;
194+ check ( & p) ;
195+ p. delete ( ) ;
196+ }
197+
198+ #[ test]
199+ fn stops_at_first_nul_in_vec ( ) {
200+ let v = std:: rc:: Rc :: new ( RefCell :: new ( EMBEDDED_NUL . to_vec ( ) ) ) ;
201+ check ( & v. as_pointer ( ) ) ;
202+ }
203+
204+ #[ test]
205+ fn stops_at_first_nul_in_reinterpreted_view ( ) {
206+ let p: Ptr < u16 > = Ptr :: alloc_array (
207+ EMBEDDED_NUL
208+ . chunks ( 2 )
209+ . map ( |c| u16:: from_ne_bytes ( [ c[ 0 ] , c[ 1 ] ] ) )
210+ . collect :: < Vec < u16 > > ( )
211+ . into_boxed_slice ( ) ,
212+ ) ;
213+ let bytes = p. reinterpret_cast :: < u8 > ( ) ;
214+ check ( & bytes) ;
215+ bytes. delete ( ) ;
216+ }
217+
218+ #[ test]
219+ fn empty_string ( ) {
220+ let p = Ptr :: < u8 > :: from_string_literal ( b"" ) ;
221+ assert_eq ! ( p. c_str_len( ) , 0 ) ;
222+ assert ! ( p. to_c_bytes( ) . is_empty( ) ) ;
223+ assert_eq ! ( p. to_rust_string( ) , "" ) ;
224+ }
225+
226+ #[ test]
227+ #[ should_panic( expected = "ub: unterminated string" ) ]
228+ fn unterminated_string_panics ( ) {
229+ let p = Ptr :: alloc_array ( vec ! [ b'a' , b'b' ] . into_boxed_slice ( ) ) ;
230+ p. c_str_len ( ) ;
123231 }
124232}
0 commit comments