@@ -21,8 +21,8 @@ static EMPTY_PUBKEY: LazyLock<PublicKeyBytes> = LazyLock::new(PublicKeyBytes::em
21
21
pub enum Error {
22
22
InvalidHierarchy ,
23
23
DiffDeletionsNotSupported ,
24
- UnableToComputeDiff ,
25
- UnableToApplyDiff ,
24
+ UnableToComputeDiff ( xdelta3 :: Error ) ,
25
+ UnableToApplyDiff ( xdelta3 :: Error ) ,
26
26
BalancesIncompleteChunk ,
27
27
Compression ( std:: io:: Error ) ,
28
28
InvalidSszState ( ssz:: DecodeError ) ,
@@ -323,9 +323,15 @@ impl BytesDiff {
323
323
}
324
324
325
325
pub fn compute_xdelta ( source_bytes : & [ u8 ] , target_bytes : & [ u8 ] ) -> Result < Self , Error > {
326
- let bytes = xdelta3:: encode ( target_bytes, source_bytes)
327
- . ok_or ( Error :: UnableToComputeDiff )
328
- . unwrap ( ) ;
326
+ // TODO(hdiff): Use a smaller estimate for the output diff buffer size, currently the
327
+ // xdelta3 lib will use 2x the size of the source plus the target length, which is 4x the
328
+ // size of the hdiff buffer. In practice, diffs are almost always smaller than buffers (by a
329
+ // signficiant factor), so this is 4-16x larger than necessary in a temporary allocation.
330
+ //
331
+ // We should use an estimated size that *should* be enough, and then dynamically increase it
332
+ // if we hit an insufficient space error.
333
+ let bytes =
334
+ xdelta3:: encode ( target_bytes, source_bytes) . map_err ( Error :: UnableToComputeDiff ) ?;
329
335
Ok ( Self { bytes } )
330
336
}
331
337
@@ -334,8 +340,31 @@ impl BytesDiff {
334
340
}
335
341
336
342
pub fn apply_xdelta ( & self , source : & [ u8 ] , target : & mut Vec < u8 > ) -> Result < ( ) , Error > {
337
- * target = xdelta3:: decode ( & self . bytes , source) . ok_or ( Error :: UnableToApplyDiff ) ?;
338
- Ok ( ( ) )
343
+ // TODO(hdiff): Dynamic buffer allocation. This is a stopgap until we implement a schema
344
+ // change to store the output buffer size inside the `BytesDiff`.
345
+ let mut output_length = ( ( source. len ( ) + self . bytes . len ( ) ) * 3 ) / 2 ;
346
+ let mut num_resizes = 0 ;
347
+ loop {
348
+ match xdelta3:: decode_with_output_len ( & self . bytes , source, output_length as u32 ) {
349
+ Ok ( result_buffer) => {
350
+ * target = result_buffer;
351
+
352
+ metrics:: observe (
353
+ & metrics:: BEACON_HDIFF_BUFFER_APPLY_RESIZES ,
354
+ num_resizes as f64 ,
355
+ ) ;
356
+ return Ok ( ( ) ) ;
357
+ }
358
+ Err ( xdelta3:: Error :: InsufficientOutputLength ) => {
359
+ // Double the output buffer length and try again.
360
+ output_length *= 2 ;
361
+ num_resizes += 1 ;
362
+ }
363
+ Err ( err) => {
364
+ return Err ( Error :: UnableToApplyDiff ( err) ) ;
365
+ }
366
+ }
367
+ }
339
368
}
340
369
341
370
/// Byte size of this instance
0 commit comments