@@ -21,8 +21,8 @@ static EMPTY_PUBKEY: LazyLock<PublicKeyBytes> = LazyLock::new(PublicKeyBytes::em
2121pub enum Error {
2222 InvalidHierarchy ,
2323 DiffDeletionsNotSupported ,
24- UnableToComputeDiff ,
25- UnableToApplyDiff ,
24+ UnableToComputeDiff ( xdelta3 :: Error ) ,
25+ UnableToApplyDiff ( xdelta3 :: Error ) ,
2626 BalancesIncompleteChunk ,
2727 Compression ( std:: io:: Error ) ,
2828 InvalidSszState ( ssz:: DecodeError ) ,
@@ -323,9 +323,15 @@ impl BytesDiff {
323323 }
324324
325325 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 ) ?;
329335 Ok ( Self { bytes } )
330336 }
331337
@@ -334,8 +340,31 @@ impl BytesDiff {
334340 }
335341
336342 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+ }
339368 }
340369
341370 /// Byte size of this instance
0 commit comments