66 "fmt"
77 "maps"
88 "slices"
9+ "sort"
910
10- "github.com/bits-and-blooms/bitset"
1111 "github.com/google/uuid"
1212 "go.uber.org/zap"
1313
@@ -31,27 +31,19 @@ type Header struct {
3131 // This means every V4 header has a complete map of all builds referenced
3232 // in its Mapping. V3 headers have no BuildFiles; the read path falls back
3333 // to a Size() RPC for those.
34- BuildFiles map [uuid.UUID ]BuildFileInfo
35- blockStarts * bitset.BitSet
36- startMap map [int64 ]* BuildMap
34+ BuildFiles map [uuid.UUID ]BuildFileInfo
3735
38- Mapping []* BuildMap
36+ Mapping []BuildMap
3937}
4038
4139// CloneForUpload returns a clone with copied Mapping and BuildFiles, safe to
4240// mutate for serialization without racing with concurrent readers of the
43- // original. Only serialization-relevant fields are populated (Metadata,
44- // Mapping, BuildFiles); lookup indices (blockStarts, startMap) are left nil.
41+ // original.
4542func (t * Header ) CloneForUpload () * Header {
46- mappings := make ([]* BuildMap , len (t .Mapping ))
47- for i , m := range t .Mapping {
48- mappings [i ] = m .Copy ()
49- }
50-
5143 metaCopy := * t .Metadata
5244 clone := & Header {
5345 Metadata : & metaCopy ,
54- Mapping : mappings ,
46+ Mapping : slices . Clone ( t . Mapping ) ,
5547 }
5648
5749 if t .BuildFiles != nil {
@@ -62,37 +54,23 @@ func (t *Header) CloneForUpload() *Header {
6254 return clone
6355}
6456
65- func NewHeader (metadata * Metadata , mapping []* BuildMap ) (* Header , error ) {
57+ func NewHeader (metadata * Metadata , mapping []BuildMap ) (* Header , error ) {
6658 if metadata .BlockSize == 0 {
6759 return nil , fmt .Errorf ("block size cannot be zero" )
6860 }
6961
7062 if len (mapping ) == 0 {
71- mapping = []* BuildMap {{
63+ mapping = []BuildMap {{
7264 Offset : 0 ,
7365 Length : metadata .Size ,
7466 BuildId : metadata .BuildId ,
7567 BuildStorageOffset : 0 ,
7668 }}
7769 }
7870
79- blocks := TotalBlocks (int64 (metadata .Size ), int64 (metadata .BlockSize ))
80-
81- intervals := bitset .New (uint (blocks ))
82- startMap := make (map [int64 ]* BuildMap , len (mapping ))
83-
84- for _ , m := range mapping {
85- block := BlockIdx (int64 (m .Offset ), int64 (metadata .BlockSize ))
86-
87- intervals .Set (uint (block ))
88- startMap [block ] = m
89- }
90-
9171 return & Header {
92- blockStarts : intervals ,
93- Metadata : metadata ,
94- Mapping : mapping ,
95- startMap : startMap ,
72+ Metadata : metadata ,
73+ Mapping : mapping ,
9674 }, nil
9775}
9876
@@ -147,7 +125,6 @@ func (t *Header) GetShiftedMapping(ctx context.Context, offset int64) (BuildMap,
147125 return b , nil
148126}
149127
150- // TODO: Maybe we can optimize mapping by automatically assuming the mapping is uuid.Nil if we don't find it + stopping storing the nil mapping.
151128func (t * Header ) getMapping (ctx context.Context , offset int64 ) (* BuildMap , int64 , error ) {
152129 if offset < 0 || offset >= int64 (t .Metadata .Size ) {
153130 if t .IsNormalizeFixApplied () {
@@ -172,30 +149,26 @@ func (t *Header) getMapping(ctx context.Context, offset int64) (*BuildMap, int64
172149 )
173150 }
174151
175- block := BlockIdx (offset , int64 (t .Metadata .BlockSize ))
152+ i := sort .Search (len (t .Mapping ), func (i int ) bool {
153+ return int64 (t .Mapping [i ].Offset ) > offset
154+ })
176155
177- start , ok := t .blockStarts .PreviousSet (uint (block ))
178- if ! ok {
156+ if i == 0 {
179157 return nil , 0 , fmt .Errorf ("no source found for offset %d" , offset )
180158 }
181159
182- mapping , ok := t .startMap [int64 (start )]
183- if ! ok {
184- return nil , 0 , fmt .Errorf ("no mapping found for offset %d" , offset )
185- }
186-
187- shift := (block - int64 (start )) * int64 (t .Metadata .BlockSize )
160+ mapping := & t .Mapping [i - 1 ]
161+ shift := offset - int64 (mapping .Offset )
188162
189163 // Verify that the offset falls within this mapping's range
190164 if shift >= int64 (mapping .Length ) {
191165 if t .IsNormalizeFixApplied () {
192- return nil , 0 , fmt .Errorf ("offset %d (block %d) is beyond the end of mapping at offset %d (ends at %d)" ,
193- offset , block , mapping .Offset , mapping .Offset + mapping .Length )
166+ return nil , 0 , fmt .Errorf ("offset %d is beyond the end of mapping at offset %d (ends at %d)" ,
167+ offset , mapping .Offset , mapping .Offset + mapping .Length )
194168 }
195169
196170 logger .L ().Warn (ctx , "offset is beyond the end of mapping, but normalize fix is not applied" ,
197171 zap .Int64 ("offset" , offset ),
198- zap .Int64 ("block" , block ),
199172 zap .Uint64 ("mappingOffset" , mapping .Offset ),
200173 zap .Uint64 ("mappingEnd" , mapping .Offset + mapping .Length ),
201174 logger .WithBuildID (t .Metadata .BuildId .String ()),
@@ -228,9 +201,8 @@ func ValidateHeader(h *Header) error {
228201 }
229202
230203 // Sort mappings by offset to check for gaps/overlaps
231- sortedMappings := make ([]* BuildMap , len (h .Mapping ))
232- copy (sortedMappings , h .Mapping )
233- slices .SortFunc (sortedMappings , func (a , b * BuildMap ) int {
204+ sortedMappings := slices .Clone (h .Mapping )
205+ slices .SortFunc (sortedMappings , func (a , b BuildMap ) int {
234206 return cmp .Compare (a .Offset , b .Offset )
235207 })
236208
0 commit comments