4
4
A writeup about the reverse engineering of `typedstream` can be found [here](https://chrissardegna.com/blog/reverse-engineering-apples-typedstream-format/).
5
5
*/
6
6
7
- use std:: collections:: HashSet ;
8
-
9
7
use crate :: {
10
8
deserializer:: {
11
9
constants:: { EMPTY , END , START } ,
@@ -40,7 +38,7 @@ pub struct TypedStreamDeserializer<'a> {
40
38
/// As we parse the `typedstream`, build a table of seen [`Archived`] data to reference in the future
41
39
pub object_table : Vec < Archived < ' a > > ,
42
40
/// We want to copy embedded types the first time they are seen, even if the types were resolved through references
43
- pub ( crate ) seen_embedded_types : HashSet < usize > ,
41
+ pub ( crate ) seen_embedded_types : Vec < usize > ,
44
42
}
45
43
46
44
impl < ' a > TypedStreamDeserializer < ' a > {
@@ -61,7 +59,7 @@ impl<'a> TypedStreamDeserializer<'a> {
61
59
position : 0 ,
62
60
type_table : Vec :: with_capacity ( 16 ) ,
63
61
object_table : Vec :: with_capacity ( 32 ) ,
64
- seen_embedded_types : HashSet :: with_capacity ( 8 ) ,
62
+ seen_embedded_types : Vec :: with_capacity ( 8 ) ,
65
63
}
66
64
}
67
65
@@ -153,12 +151,15 @@ impl<'a> TypedStreamDeserializer<'a> {
153
151
}
154
152
155
153
/// Reads the next byte from the stream, advancing the position.
154
+ #[ inline( always) ]
156
155
fn consume_current_byte ( & mut self ) -> Result < & u8 > {
157
156
let byte = read_byte_at ( self . data , self . position ) ?;
158
157
self . position += 1 ;
159
158
Ok ( byte)
160
159
}
161
160
161
+ /// Reads an unsigned integer from the stream, advancing the position.
162
+ #[ inline( always) ]
162
163
fn read_unsigned_int ( & mut self ) -> Result < u64 > {
163
164
let unsigned_int = read_unsigned_int ( & self . data [ self . position ..] ) ?;
164
165
self . position += unsigned_int. bytes_consumed ;
@@ -211,11 +212,11 @@ impl<'a> TypedStreamDeserializer<'a> {
211
212
}
212
213
213
214
fn read_class ( & mut self ) -> Result < Option < usize > > {
214
- // index of the first START we encounter (the bottom-most child)
215
+ // Index of the first START we encounter (the bottom-most child)
215
216
let mut first_new: Option < usize > = None ;
216
- // index of the most recently pushed class (current “child”)
217
+ // Index of the most recently pushed class (current “child”)
217
218
let mut prev_new: Option < usize > = None ;
218
- // parent for the outer-most new class (set by EMPTY or a pointer)
219
+ // Parent for the outer-most new class (set by EMPTY or a pointer)
219
220
let final_parent: Option < usize > ;
220
221
221
222
loop {
@@ -316,8 +317,8 @@ impl<'a> TypedStreamDeserializer<'a> {
316
317
}
317
318
318
319
/// Reads numeric types (signed, unsigned, float, double) and returns the corresponding `OutputData`
319
- fn read_number ( & mut self , ty : & Type < ' a > ) -> Result < OutputData < ' a > > {
320
- match ty {
320
+ fn read_number ( & mut self , table_index : usize , type_index : usize ) -> Result < OutputData < ' a > > {
321
+ match self . type_table [ table_index ] [ type_index ] {
321
322
Type :: SignedInt => {
322
323
let signed_int = read_signed_int ( & self . data [ self . position ..] ) ?;
323
324
self . position += signed_int. bytes_consumed ;
@@ -343,12 +344,13 @@ impl<'a> TypedStreamDeserializer<'a> {
343
344
}
344
345
345
346
fn read_types ( & mut self , types_index : usize ) -> Result < Option < Vec < OutputData < ' a > > > > {
346
- // Clone types to avoid holding an immutable borrow on self during parsing
347
- let types = self . type_table [ types_index] . clone ( ) ;
348
- let mut out_v = Vec :: with_capacity ( types . len ( ) ) ;
347
+ // Start reading types from the specified index in the type table
348
+ let len = self . type_table [ types_index] . len ( ) ;
349
+ let mut out_v = Vec :: with_capacity ( len) ;
349
350
350
- for ty in types {
351
- match ty {
351
+ for i in 0 ..len {
352
+ // Read the next type from the type table
353
+ match self . type_table [ types_index] [ i] {
352
354
Type :: Utf8String => {
353
355
let str_data = read_string ( & self . data [ self . position ..] ) ?;
354
356
self . position += str_data. bytes_consumed ;
@@ -378,9 +380,9 @@ impl<'a> TypedStreamDeserializer<'a> {
378
380
// Read a single byte for unknown data
379
381
out_v. push ( OutputData :: Byte ( byte) ) ;
380
382
}
381
- // numeric types
383
+ // Handle all numeric types
382
384
Type :: SignedInt | Type :: UnsignedInt | Type :: Float | Type :: Double => {
383
- let val = self . read_number ( & ty ) ?;
385
+ let val = self . read_number ( types_index , i ) ?;
384
386
out_v. push ( val) ;
385
387
}
386
388
}
@@ -400,20 +402,19 @@ impl<'a> TypedStreamDeserializer<'a> {
400
402
// Get the type of the object
401
403
let new_types = Type :: read_new_type ( & self . data [ self . position ..] ) ?;
402
404
let new_type_index = self . type_table . len ( ) ;
403
- // Embedded data is stored as a String in the objects table
405
+ // Embedded data is stored as a Type in the objects table
404
406
if is_embedded_type {
405
407
self . object_table . push ( Archived :: Type ( new_type_index) ) ;
406
408
// We only want to include the first embedded reference tag, not subsequent references to the same embed
407
409
self . seen_embedded_types
408
- . insert ( self . object_table . len ( ) . saturating_sub ( 1 ) ) ;
410
+ . push ( self . object_table . len ( ) . saturating_sub ( 1 ) ) ;
409
411
}
410
412
411
413
self . type_table . push ( new_types. value ) ;
412
414
self . position += new_types. bytes_consumed ;
413
415
Ok ( Some ( self . type_table . len ( ) - 1 ) )
414
416
}
415
- EMPTY => Ok ( None ) ,
416
- END => Ok ( None ) ,
417
+ END | EMPTY => Ok ( None ) ,
417
418
ptr => {
418
419
let pointer = read_pointer ( & ptr) ?;
419
420
let ref_tag = pointer. value as usize ;
@@ -428,7 +429,7 @@ impl<'a> TypedStreamDeserializer<'a> {
428
429
&& self . type_table . get ( ref_tag as usize ) . is_some ( )
429
430
{
430
431
self . object_table . push ( Archived :: Type ( ref_tag) ) ;
431
- self . seen_embedded_types . insert ( ref_tag) ;
432
+ self . seen_embedded_types . push ( ref_tag) ;
432
433
}
433
434
}
434
435
0 commit comments