@@ -30,16 +30,13 @@ mod ffi {
3030 XUserDefined ,
3131 }
3232
33- /// Result of a decode operation. The output pointer refers to the
33+ /// Result of a decode operation. The output slice borrows the
3434 /// decoder's internal buffer and is valid until the next `decode` or
35- /// `reset` call. Encoded as `usize` because CXX shared structs cannot
36- /// contain raw pointers.
37- struct DecodeResult {
38- /// Pointer to the UTF-16 output buffer, as `usize`.
39- /// Cast to `const uint16_t*` on the C++ side.
40- output_ptr : usize ,
41- /// Number of UTF-16 code units in the output.
42- output_len : usize ,
35+ /// `reset` call.
36+ struct DecodeResult < ' a > {
37+ /// UTF-16 code units decoded from the input, borrowing the
38+ /// decoder's reusable output buffer.
39+ output : & ' a [ u16 ] ,
4340 /// True if a fatal decoding error was encountered. Only meaningful
4441 /// when the caller requested fatal mode — in replacement mode errors
4542 /// are silently replaced with U+FFFD and this flag is not set.
@@ -61,10 +58,10 @@ mod ffi {
6158
6259 /// Decode a chunk of bytes. The decoded UTF-16 output is stored in
6360 /// the decoder's internal buffer; the returned `DecodeResult`
64- /// carries a pointer and length into that buffer. Set `flush` to
65- /// true on the final chunk. When `fatal` is true and an error is
66- /// encountered, `had_error` is set and the output may be incomplete.
67- fn decode ( decoder : & mut Decoder , input : & [ u8 ] , options : & DecodeOptions ) -> DecodeResult ;
61+ /// borrows that buffer. Set `flush` to true on the final chunk.
62+ /// When `fatal` is true and an error is encountered, `had_error`
63+ /// is set and the output may be incomplete.
64+ unsafe fn decode < ' a > ( decoder : & ' a mut Decoder , input : & [ u8 ] , options : & DecodeOptions ) -> DecodeResult < ' a > ;
6865
6966 /// Reset the decoder to its initial state (for explicit reset calls).
7067 fn reset ( decoder : & mut Decoder ) ;
@@ -109,11 +106,11 @@ pub fn new_decoder(encoding: ffi::Encoding) -> Box<Decoder> {
109106 } )
110107}
111108
112- pub fn decode (
113- state : & mut Decoder ,
109+ pub fn decode < ' a > (
110+ state : & ' a mut Decoder ,
114111 input : & [ u8 ] ,
115112 options : & ffi:: DecodeOptions ,
116- ) -> ffi:: DecodeResult {
113+ ) -> ffi:: DecodeResult < ' a > {
117114 // Lazy reset: reconstruct the inner decoder only when a previous flush
118115 // marked it as needed, avoiding the cost on one-shot decodes where the
119116 // decoder is never reused.
@@ -154,8 +151,7 @@ pub fn decode(
154151 state. inner = state. encoding . new_decoder_without_bom_handling ( ) ;
155152 state. output . truncate ( total_written) ;
156153 return ffi:: DecodeResult {
157- output_ptr : state. output . as_ptr ( ) as usize ,
158- output_len : state. output . len ( ) ,
154+ output : & state. output ,
159155 had_error : true ,
160156 } ;
161157 }
@@ -188,8 +184,7 @@ pub fn decode(
188184 }
189185
190186 ffi:: DecodeResult {
191- output_ptr : state. output . as_ptr ( ) as usize ,
192- output_len : state. output . len ( ) ,
187+ output : & state. output ,
193188 had_error : false ,
194189 }
195190}
@@ -198,4 +193,9 @@ pub fn reset(state: &mut Decoder) {
198193 state. inner = state. encoding . new_decoder_without_bom_handling ( ) ;
199194 state. needs_reset = false ;
200195 // Intentionally keep state.output — preserves the allocation for reuse.
196+ // The buffer can grow up to ~2× the largest input chunk (due to UTF-16
197+ // expansion and the doubling strategy in decode()) and stays at that high-
198+ // water mark. This is acceptable because the Decoder is owned by a JS
199+ // TextDecoder object and is GC'd with it, so the buffer lifetime is
200+ // bounded by the object's reachability.
201201}
0 commit comments