@@ -455,17 +455,8 @@ impl Vfs {
455455#[ derive( Debug ) ]
456456pub struct OpenFile {
457457 // The list of blobs
458- content : Vec < BlobInfo > ,
459- }
460-
461- // Information about the blob: 1) The id 2) The cumulated sizes of all blobs prior to this one, a.k.a the starting point of this blob.
462- #[ derive( Debug ) ]
463- struct BlobInfo {
464- // [`Id`] of the blob
465- id : DataId ,
466-
467- // the start position of this blob within the file
468- starts_at : usize ,
458+ content : Vec < DataId > ,
459+ offsets : ContentOffset ,
469460}
470461
471462impl OpenFile {
@@ -488,26 +479,15 @@ impl OpenFile {
488479 ///
489480 /// * Panics if the `Node` has no content
490481 pub fn from_node < P , S : IndexedFull > ( repo : & Repository < P , S > , node : & Node ) -> Self {
491- let mut start = 0 ;
492- let mut content: Vec < _ > = node
493- . content
494- . as_ref ( )
495- . unwrap ( )
496- . iter ( )
497- . map ( |id| {
498- let starts_at = start;
499- start += repo. index ( ) . get_data ( id) . unwrap ( ) . data_length ( ) as usize ;
500- BlobInfo { id : * id, starts_at }
501- } )
502- . collect ( ) ;
482+ let content: Vec < _ > = node. content . as_ref ( ) . unwrap ( ) . clone ( ) ;
503483
504- // content is assumed to be partitioned, so we add a starts_at:MAX entry
505- content . push ( BlobInfo {
506- id : DataId :: default ( ) ,
507- starts_at : usize:: MAX ,
508- } ) ;
484+ let offsets = ContentOffset :: from_sizes (
485+ content
486+ . iter ( )
487+ . map ( |id| repo . index ( ) . get_data ( id ) . unwrap ( ) . data_length ( ) as usize ) ,
488+ ) ;
509489
510- Self { content }
490+ Self { content, offsets }
511491 }
512492
513493 /// Read the `OpenFile` at the given `offset` from the `repo`.
@@ -530,19 +510,16 @@ impl OpenFile {
530510 pub fn read_at < P , S : IndexedFull > (
531511 & self ,
532512 repo : & Repository < P , S > ,
533- mut offset : usize ,
513+ offset : usize ,
534514 mut length : usize ,
535515 ) -> RusticResult < Bytes > {
536- // find the start of relevant blobs => find the largest index such that self.content[i].starts_at <= offset, but
537- // self.content[i+1] > offset (note that a last dummy element has been added)
538- let mut i = self . content . partition_point ( |c| c. starts_at <= offset) - 1 ;
539-
540- offset -= self . content [ i] . starts_at ;
516+ let ( mut i, mut offset) = self . offsets . offset ( offset) ;
541517
542518 let mut result = BytesMut :: with_capacity ( length) ;
543519
544- while length > 0 && i < self . content . len ( ) - 1 {
545- let data = repo. get_blob_cached ( & BlobId :: from ( * self . content [ i] . id ) , BlobType :: Data ) ?;
520+ // The case of empty node.content is also correctly handled here
521+ while length > 0 && i < self . content . len ( ) {
522+ let data = repo. get_blob_cached ( & BlobId :: from ( self . content [ i] ) , BlobType :: Data ) ?;
546523
547524 if offset > data. len ( ) {
548525 // we cannot read behind the blob. This only happens if offset is too large to fit in the last blob
@@ -563,3 +540,66 @@ impl OpenFile {
563540 Ok ( result. into ( ) )
564541 }
565542}
543+
544+ #[ derive( Debug ) ]
545+ struct ContentOffset ( Vec < usize > ) ;
546+
547+ impl ContentOffset {
548+ fn from_sizes ( sizes : impl IntoIterator < Item = usize > ) -> Self {
549+ let mut start = 0 ;
550+ let mut offsets: Vec < _ > = sizes
551+ . into_iter ( )
552+ . map ( |size| {
553+ let starts_at = start;
554+ start += size;
555+ starts_at
556+ } )
557+ . collect ( ) ;
558+
559+ if !offsets. is_empty ( ) {
560+ // offsets is assumed to be partitioned, so we add a starts_at:MAX entry
561+ offsets. push ( usize:: MAX ) ;
562+ }
563+ Self ( offsets)
564+ }
565+
566+ fn offset ( & self , mut offset : usize ) -> ( usize , usize ) {
567+ if self . 0 . is_empty ( ) {
568+ return ( 0 , 0 ) ;
569+ }
570+ // find the start of relevant blobs => find the largest index such that self.offsets[i] <= offset, but
571+ // self.offsets[i+1] > offset (note that a last dummy element with usize::MAX has been added to ensure we always have two partitions)
572+ // If offsets is non-empty, then offsets[0] = 0, hence partition_point returns an index >=1.
573+ let i = self . 0 . partition_point ( |o| o <= & offset) - 1 ;
574+ offset -= self . 0 [ i] ;
575+ ( i, offset)
576+ }
577+ }
578+
579+ #[ cfg( test) ]
580+ mod tests {
581+ use super :: * ;
582+
583+ #[ test]
584+ fn content_offsets_empty_sizes ( ) {
585+ let offsets = ContentOffset :: from_sizes ( [ ] ) ;
586+ assert_eq ! ( offsets. offset( 0 ) , ( 0 , 0 ) ) ;
587+ assert_eq ! ( offsets. offset( 42 ) , ( 0 , 0 ) ) ;
588+ }
589+
590+ #[ test]
591+ fn content_offsets_size ( ) {
592+ let offsets = ContentOffset :: from_sizes ( [ 15 ] ) ;
593+ assert_eq ! ( offsets. offset( 0 ) , ( 0 , 0 ) ) ;
594+ assert_eq ! ( offsets. offset( 5 ) , ( 0 , 5 ) ) ;
595+ assert_eq ! ( offsets. offset( 20 ) , ( 0 , 20 ) ) ;
596+ }
597+ #[ test]
598+ fn content_offsets_sizes ( ) {
599+ let offsets = ContentOffset :: from_sizes ( [ 15 , 24 ] ) ;
600+ assert_eq ! ( offsets. offset( 0 ) , ( 0 , 0 ) ) ;
601+ assert_eq ! ( offsets. offset( 5 ) , ( 0 , 5 ) ) ;
602+ assert_eq ! ( offsets. offset( 20 ) , ( 1 , 5 ) ) ;
603+ assert_eq ! ( offsets. offset( 42 ) , ( 1 , 27 ) ) ;
604+ }
605+ }
0 commit comments