@@ -83,6 +83,12 @@ struct bitmap_index {
83
83
/* The checksum of the packfile or MIDX; points into map. */
84
84
const unsigned char * checksum ;
85
85
86
+ /*
87
+ * If not NULL, this point into the commit table extension
88
+ * (within the memory mapped region `map`).
89
+ */
90
+ unsigned char * table_lookup ;
91
+
86
92
/*
87
93
* Extended index.
88
94
*
@@ -186,6 +192,16 @@ static int load_bitmap_header(struct bitmap_index *index)
186
192
index -> hashes = (void * )(index_end - cache_size );
187
193
index_end -= cache_size ;
188
194
}
195
+
196
+ if (flags & BITMAP_OPT_LOOKUP_TABLE ) {
197
+ size_t table_size = st_mult (ntohl (header -> entry_count ),
198
+ BITMAP_LOOKUP_TABLE_TRIPLET_WIDTH );
199
+ if (table_size > index_end - index -> map - header_size )
200
+ return error (_ ("corrupted bitmap index file (too short to fit lookup table)" ));
201
+ if (git_env_bool ("GIT_TEST_READ_COMMIT_TABLE" , 1 ))
202
+ index -> table_lookup = (void * )(index_end - table_size );
203
+ index_end -= table_size ;
204
+ }
189
205
}
190
206
191
207
index -> entry_count = ntohl (header -> entry_count );
@@ -212,9 +228,11 @@ static struct stored_bitmap *store_bitmap(struct bitmap_index *index,
212
228
213
229
hash_pos = kh_put_oid_map (index -> bitmaps , stored -> oid , & ret );
214
230
215
- /* a 0 return code means the insertion succeeded with no changes,
216
- * because the SHA1 already existed on the map. this is bad, there
217
- * shouldn't be duplicated commits in the index */
231
+ /*
232
+ * A 0 return code means the insertion succeeded with no changes,
233
+ * because the SHA1 already existed on the map. This is bad, there
234
+ * shouldn't be duplicated commits in the index.
235
+ */
218
236
if (ret == 0 ) {
219
237
error (_ ("duplicate entry in bitmap index: '%s'" ), oid_to_hex (oid ));
220
238
return NULL ;
@@ -482,7 +500,7 @@ static int load_bitmap(struct bitmap_index *bitmap_git)
482
500
!(bitmap_git -> tags = read_bitmap_1 (bitmap_git )))
483
501
goto failed ;
484
502
485
- if (load_bitmap_entries_v1 (bitmap_git ) < 0 )
503
+ if (! bitmap_git -> table_lookup && load_bitmap_entries_v1 (bitmap_git ) < 0 )
486
504
goto failed ;
487
505
488
506
return 0 ;
@@ -570,13 +588,256 @@ struct include_data {
570
588
struct bitmap * seen ;
571
589
};
572
590
591
+ struct bitmap_lookup_table_triplet {
592
+ uint32_t commit_pos ;
593
+ uint64_t offset ;
594
+ uint32_t xor_row ;
595
+ };
596
+
597
+ struct bitmap_lookup_table_xor_item {
598
+ struct object_id oid ;
599
+ uint64_t offset ;
600
+ };
601
+
602
+ /*
603
+ * Given a `triplet` struct pointer and pointer `p`, this
604
+ * function reads the triplet beginning at `p` into the struct.
605
+ * Note that this function assumes that there is enough memory
606
+ * left for filling the `triplet` struct from `p`.
607
+ */
608
+ static int bitmap_lookup_table_get_triplet_by_pointer (struct bitmap_lookup_table_triplet * triplet ,
609
+ const unsigned char * p )
610
+ {
611
+ if (!triplet )
612
+ return -1 ;
613
+
614
+ triplet -> commit_pos = get_be32 (p );
615
+ p += sizeof (uint32_t );
616
+ triplet -> offset = get_be64 (p );
617
+ p += sizeof (uint64_t );
618
+ triplet -> xor_row = get_be32 (p );
619
+ return 0 ;
620
+ }
621
+
622
+ /*
623
+ * This function gets the raw triplet from `row`'th row in the
624
+ * lookup table and fills that data to the `triplet`.
625
+ */
626
+ static int bitmap_lookup_table_get_triplet (struct bitmap_index * bitmap_git ,
627
+ uint32_t pos ,
628
+ struct bitmap_lookup_table_triplet * triplet )
629
+ {
630
+ unsigned char * p = NULL ;
631
+ if (pos >= bitmap_git -> entry_count )
632
+ return error (_ ("corrupt bitmap lookup table: triplet position out of index" ));
633
+
634
+ p = bitmap_git -> table_lookup + st_mult (pos , BITMAP_LOOKUP_TABLE_TRIPLET_WIDTH );
635
+
636
+ return bitmap_lookup_table_get_triplet_by_pointer (triplet , p );
637
+ }
638
+
639
+ /*
640
+ * Searches for a matching triplet. `commit_pos` is a pointer
641
+ * to the wanted commit position value. `table_entry` points to
642
+ * a triplet in lookup table. The first 4 bytes of each
643
+ * triplet (pointed by `table_entry`) are compared with `*commit_pos`.
644
+ */
645
+ static int triplet_cmp (const void * commit_pos , const void * table_entry )
646
+ {
647
+
648
+ uint32_t a = * (uint32_t * )commit_pos ;
649
+ uint32_t b = get_be32 (table_entry );
650
+ if (a > b )
651
+ return 1 ;
652
+ else if (a < b )
653
+ return -1 ;
654
+
655
+ return 0 ;
656
+ }
657
+
658
+ static uint32_t bitmap_bsearch_pos (struct bitmap_index * bitmap_git ,
659
+ struct object_id * oid ,
660
+ uint32_t * result )
661
+ {
662
+ int found ;
663
+
664
+ if (bitmap_is_midx (bitmap_git ))
665
+ found = bsearch_midx (oid , bitmap_git -> midx , result );
666
+ else
667
+ found = bsearch_pack (oid , bitmap_git -> pack , result );
668
+
669
+ return found ;
670
+ }
671
+
672
+ /*
673
+ * `bsearch_triplet_by_pos` function searches for the raw triplet
674
+ * having commit position same as `commit_pos` and fills `triplet`
675
+ * object from the raw triplet. Returns 1 on success and 0 on
676
+ * failure.
677
+ */
678
+ static int bitmap_bsearch_triplet_by_pos (uint32_t commit_pos ,
679
+ struct bitmap_index * bitmap_git ,
680
+ struct bitmap_lookup_table_triplet * triplet )
681
+ {
682
+ unsigned char * p = bsearch (& commit_pos , bitmap_git -> table_lookup , bitmap_git -> entry_count ,
683
+ BITMAP_LOOKUP_TABLE_TRIPLET_WIDTH , triplet_cmp );
684
+
685
+ if (!p )
686
+ return -1 ;
687
+
688
+ return bitmap_lookup_table_get_triplet_by_pointer (triplet , p );
689
+ }
690
+
691
+ static struct stored_bitmap * lazy_bitmap_for_commit (struct bitmap_index * bitmap_git ,
692
+ struct commit * commit )
693
+ {
694
+ uint32_t commit_pos , xor_row ;
695
+ uint64_t offset ;
696
+ int flags ;
697
+ struct bitmap_lookup_table_triplet triplet ;
698
+ struct object_id * oid = & commit -> object .oid ;
699
+ struct ewah_bitmap * bitmap ;
700
+ struct stored_bitmap * xor_bitmap = NULL ;
701
+ const int bitmap_header_size = 6 ;
702
+ static struct bitmap_lookup_table_xor_item * xor_items = NULL ;
703
+ static size_t xor_items_nr = 0 , xor_items_alloc = 0 ;
704
+ static int is_corrupt = 0 ;
705
+ int xor_flags ;
706
+ khiter_t hash_pos ;
707
+ struct bitmap_lookup_table_xor_item * xor_item ;
708
+
709
+ if (is_corrupt )
710
+ return NULL ;
711
+
712
+ if (!bitmap_bsearch_pos (bitmap_git , oid , & commit_pos ))
713
+ return NULL ;
714
+
715
+ if (bitmap_bsearch_triplet_by_pos (commit_pos , bitmap_git , & triplet ) < 0 )
716
+ return NULL ;
717
+
718
+ xor_items_nr = 0 ;
719
+ offset = triplet .offset ;
720
+ xor_row = triplet .xor_row ;
721
+
722
+ while (xor_row != 0xffffffff ) {
723
+ ALLOC_GROW (xor_items , xor_items_nr + 1 , xor_items_alloc );
724
+
725
+ if (xor_items_nr + 1 >= bitmap_git -> entry_count ) {
726
+ error (_ ("corrupt bitmap lookup table: xor chain exceed entry count" ));
727
+ goto corrupt ;
728
+ }
729
+
730
+ if (bitmap_lookup_table_get_triplet (bitmap_git , xor_row , & triplet ) < 0 )
731
+ goto corrupt ;
732
+
733
+ xor_item = & xor_items [xor_items_nr ];
734
+ xor_item -> offset = triplet .offset ;
735
+
736
+ if (nth_bitmap_object_oid (bitmap_git , & xor_item -> oid , triplet .commit_pos ) < 0 ) {
737
+ error (_ ("corrupt bitmap lookup table: commit index %u out of range" ),
738
+ triplet .commit_pos );
739
+ goto corrupt ;
740
+ }
741
+
742
+ hash_pos = kh_get_oid_map (bitmap_git -> bitmaps , xor_item -> oid );
743
+
744
+ /*
745
+ * If desired bitmap is already stored, we don't need
746
+ * to iterate further. Because we know that bitmaps
747
+ * that are needed to be parsed to parse this bitmap
748
+ * has already been stored. So, assign this stored bitmap
749
+ * to the xor_bitmap.
750
+ */
751
+ if (hash_pos < kh_end (bitmap_git -> bitmaps ) &&
752
+ (xor_bitmap = kh_value (bitmap_git -> bitmaps , hash_pos )))
753
+ break ;
754
+ xor_items_nr ++ ;
755
+ xor_row = triplet .xor_row ;
756
+ }
757
+
758
+ while (xor_items_nr ) {
759
+ xor_item = & xor_items [xor_items_nr - 1 ];
760
+ bitmap_git -> map_pos = xor_item -> offset ;
761
+ if (bitmap_git -> map_size - bitmap_git -> map_pos < bitmap_header_size ) {
762
+ error (_ ("corrupt ewah bitmap: truncated header for bitmap of commit \"%s\"" ),
763
+ oid_to_hex (& xor_item -> oid ));
764
+ goto corrupt ;
765
+ }
766
+
767
+ bitmap_git -> map_pos += sizeof (uint32_t ) + sizeof (uint8_t );
768
+ xor_flags = read_u8 (bitmap_git -> map , & bitmap_git -> map_pos );
769
+ bitmap = read_bitmap_1 (bitmap_git );
770
+
771
+ if (!bitmap )
772
+ goto corrupt ;
773
+
774
+ xor_bitmap = store_bitmap (bitmap_git , bitmap , & xor_item -> oid , xor_bitmap , xor_flags );
775
+ xor_items_nr -- ;
776
+ }
777
+
778
+ bitmap_git -> map_pos = offset ;
779
+ if (bitmap_git -> map_size - bitmap_git -> map_pos < bitmap_header_size ) {
780
+ error (_ ("corrupt ewah bitmap: truncated header for bitmap of commit \"%s\"" ),
781
+ oid_to_hex (oid ));
782
+ goto corrupt ;
783
+ }
784
+
785
+ /*
786
+ * Don't bother reading the commit's index position or its xor
787
+ * offset:
788
+ *
789
+ * - The commit's index position is irrelevant to us, since
790
+ * load_bitmap_entries_v1 only uses it to learn the object
791
+ * id which is used to compute the hashmap's key. We already
792
+ * have an object id, so no need to look it up again.
793
+ *
794
+ * - The xor_offset is unusable for us, since it specifies how
795
+ * many entries previous to ours we should look at. This
796
+ * makes sense when reading the bitmaps sequentially (as in
797
+ * load_bitmap_entries_v1()), since we can keep track of
798
+ * each bitmap as we read them.
799
+ *
800
+ * But it can't work for us, since the bitmap's don't have a
801
+ * fixed size. So we learn the position of the xor'd bitmap
802
+ * from the commit table (and resolve it to a bitmap in the
803
+ * above if-statement).
804
+ *
805
+ * Instead, we can skip ahead and immediately read the flags and
806
+ * ewah bitmap.
807
+ */
808
+ bitmap_git -> map_pos += sizeof (uint32_t ) + sizeof (uint8_t );
809
+ flags = read_u8 (bitmap_git -> map , & bitmap_git -> map_pos );
810
+ bitmap = read_bitmap_1 (bitmap_git );
811
+
812
+ if (!bitmap )
813
+ goto corrupt ;
814
+
815
+ return store_bitmap (bitmap_git , bitmap , oid , xor_bitmap , flags );
816
+
817
+ corrupt :
818
+ free (xor_items );
819
+ is_corrupt = 1 ;
820
+ return NULL ;
821
+ }
822
+
573
823
struct ewah_bitmap * bitmap_for_commit (struct bitmap_index * bitmap_git ,
574
824
struct commit * commit )
575
825
{
576
826
khiter_t hash_pos = kh_get_oid_map (bitmap_git -> bitmaps ,
577
827
commit -> object .oid );
578
- if (hash_pos >= kh_end (bitmap_git -> bitmaps ))
579
- return NULL ;
828
+ if (hash_pos >= kh_end (bitmap_git -> bitmaps )) {
829
+ struct stored_bitmap * bitmap = NULL ;
830
+ if (!bitmap_git -> table_lookup )
831
+ return NULL ;
832
+
833
+ trace2_region_enter ("pack-bitmap" , "reading_lookup_table" , the_repository );
834
+ /* NEEDSWORK: cache misses aren't recorded */
835
+ bitmap = lazy_bitmap_for_commit (bitmap_git , commit );
836
+ trace2_region_leave ("pack-bitmap" , "reading_lookup_table" , the_repository );
837
+ if (!bitmap )
838
+ return NULL ;
839
+ return lookup_stored_bitmap (bitmap );
840
+ }
580
841
return lookup_stored_bitmap (kh_value (bitmap_git -> bitmaps , hash_pos ));
581
842
}
582
843
@@ -1712,8 +1973,10 @@ void test_bitmap_walk(struct rev_info *revs)
1712
1973
if (revs -> pending .nr != 1 )
1713
1974
die (_ ("you must specify exactly one commit to test" ));
1714
1975
1715
- fprintf_ln (stderr , "Bitmap v%d test (%d entries loaded)" ,
1716
- bitmap_git -> version , bitmap_git -> entry_count );
1976
+ fprintf_ln (stderr , "Bitmap v%d test (%d entries%s)" ,
1977
+ bitmap_git -> version ,
1978
+ bitmap_git -> entry_count ,
1979
+ bitmap_git -> table_lookup ? "" : " loaded" );
1717
1980
1718
1981
root = revs -> pending .objects [0 ].item ;
1719
1982
bm = bitmap_for_commit (bitmap_git , (struct commit * )root );
@@ -1766,13 +2029,22 @@ void test_bitmap_walk(struct rev_info *revs)
1766
2029
1767
2030
int test_bitmap_commits (struct repository * r )
1768
2031
{
1769
- struct bitmap_index * bitmap_git = prepare_bitmap_git (r );
1770
2032
struct object_id oid ;
1771
2033
MAYBE_UNUSED void * value ;
2034
+ struct bitmap_index * bitmap_git = prepare_bitmap_git (r );
1772
2035
1773
2036
if (!bitmap_git )
1774
2037
die (_ ("failed to load bitmap indexes" ));
1775
2038
2039
+ /*
2040
+ * As this function is only used to print bitmap selected
2041
+ * commits, we don't have to read the commit table.
2042
+ */
2043
+ if (bitmap_git -> table_lookup ) {
2044
+ if (load_bitmap_entries_v1 (bitmap_git ) < 0 )
2045
+ die (_ ("failed to load bitmap indexes" ));
2046
+ }
2047
+
1776
2048
kh_foreach (bitmap_git -> bitmaps , oid , value , {
1777
2049
printf_ln ("%s" , oid_to_hex (& oid ));
1778
2050
});
0 commit comments