215 lines
5.4 KiB
Go
215 lines
5.4 KiB
Go
// SPDX-License-Identifier: Apache-2.0
|
|
// Modifications by M/S Omukk
|
|
|
|
package snapshot
|
|
|
|
import "github.com/google/uuid"
|
|
|
|
// CreateMapping converts a dirty-block bitset (represented as a []bool) into
|
|
// a sorted list of BuildMap entries. Consecutive dirty blocks are merged into
|
|
// a single entry. BuildStorageOffset tracks the sequential position in the
|
|
// compact diff file.
|
|
func CreateMapping(buildID uuid.UUID, dirty []bool, blockSize int64) []*BuildMap {
|
|
var mappings []*BuildMap
|
|
var runStart int64 = -1
|
|
var runLength int64
|
|
var storageOffset uint64
|
|
|
|
for i, set := range dirty {
|
|
if !set {
|
|
if runLength > 0 {
|
|
mappings = append(mappings, &BuildMap{
|
|
Offset: uint64(runStart) * uint64(blockSize),
|
|
Length: uint64(runLength) * uint64(blockSize),
|
|
BuildID: buildID,
|
|
BuildStorageOffset: storageOffset,
|
|
})
|
|
storageOffset += uint64(runLength) * uint64(blockSize)
|
|
runLength = 0
|
|
}
|
|
runStart = -1
|
|
continue
|
|
}
|
|
|
|
if runStart < 0 {
|
|
runStart = int64(i)
|
|
runLength = 1
|
|
} else {
|
|
runLength++
|
|
}
|
|
}
|
|
|
|
if runLength > 0 {
|
|
mappings = append(mappings, &BuildMap{
|
|
Offset: uint64(runStart) * uint64(blockSize),
|
|
Length: uint64(runLength) * uint64(blockSize),
|
|
BuildID: buildID,
|
|
BuildStorageOffset: storageOffset,
|
|
})
|
|
}
|
|
|
|
return mappings
|
|
}
|
|
|
|
// MergeMappings overlays diffMapping on top of baseMapping. Where they overlap,
|
|
// diff takes priority. The result covers the entire address space.
|
|
//
|
|
// Both inputs must be sorted by Offset. The base mapping should cover the full size.
|
|
//
|
|
// Inspired by e2b's snapshot system (Apache 2.0, modified by Omukk).
|
|
func MergeMappings(baseMapping, diffMapping []*BuildMap) []*BuildMap {
|
|
if len(diffMapping) == 0 {
|
|
return baseMapping
|
|
}
|
|
|
|
// Work on a copy of baseMapping to avoid mutating the original.
|
|
baseCopy := make([]*BuildMap, len(baseMapping))
|
|
for i, m := range baseMapping {
|
|
cp := *m
|
|
baseCopy[i] = &cp
|
|
}
|
|
|
|
var result []*BuildMap
|
|
var bi, di int
|
|
|
|
for bi < len(baseCopy) && di < len(diffMapping) {
|
|
base := baseCopy[bi]
|
|
diff := diffMapping[di]
|
|
|
|
if base.Length == 0 {
|
|
bi++
|
|
continue
|
|
}
|
|
if diff.Length == 0 {
|
|
di++
|
|
continue
|
|
}
|
|
|
|
// No overlap: base entirely before diff.
|
|
if base.Offset+base.Length <= diff.Offset {
|
|
result = append(result, base)
|
|
bi++
|
|
continue
|
|
}
|
|
|
|
// No overlap: diff entirely before base.
|
|
if diff.Offset+diff.Length <= base.Offset {
|
|
result = append(result, diff)
|
|
di++
|
|
continue
|
|
}
|
|
|
|
// Base fully inside diff — skip base.
|
|
if base.Offset >= diff.Offset && base.Offset+base.Length <= diff.Offset+diff.Length {
|
|
bi++
|
|
continue
|
|
}
|
|
|
|
// Diff fully inside base — split base around diff.
|
|
if diff.Offset >= base.Offset && diff.Offset+diff.Length <= base.Offset+base.Length {
|
|
leftLen := int64(diff.Offset) - int64(base.Offset)
|
|
if leftLen > 0 {
|
|
result = append(result, &BuildMap{
|
|
Offset: base.Offset,
|
|
Length: uint64(leftLen),
|
|
BuildID: base.BuildID,
|
|
BuildStorageOffset: base.BuildStorageOffset,
|
|
})
|
|
}
|
|
|
|
result = append(result, diff)
|
|
di++
|
|
|
|
rightShift := int64(diff.Offset) + int64(diff.Length) - int64(base.Offset)
|
|
rightLen := int64(base.Length) - rightShift
|
|
|
|
if rightLen > 0 {
|
|
baseCopy[bi] = &BuildMap{
|
|
Offset: base.Offset + uint64(rightShift),
|
|
Length: uint64(rightLen),
|
|
BuildID: base.BuildID,
|
|
BuildStorageOffset: base.BuildStorageOffset + uint64(rightShift),
|
|
}
|
|
} else {
|
|
bi++
|
|
}
|
|
continue
|
|
}
|
|
|
|
// Base starts after diff with overlap — emit diff, trim base.
|
|
if base.Offset > diff.Offset {
|
|
result = append(result, diff)
|
|
di++
|
|
|
|
rightShift := int64(diff.Offset) + int64(diff.Length) - int64(base.Offset)
|
|
rightLen := int64(base.Length) - rightShift
|
|
|
|
if rightLen > 0 {
|
|
baseCopy[bi] = &BuildMap{
|
|
Offset: base.Offset + uint64(rightShift),
|
|
Length: uint64(rightLen),
|
|
BuildID: base.BuildID,
|
|
BuildStorageOffset: base.BuildStorageOffset + uint64(rightShift),
|
|
}
|
|
} else {
|
|
bi++
|
|
}
|
|
continue
|
|
}
|
|
|
|
// Diff starts after base with overlap — emit left part of base.
|
|
if diff.Offset > base.Offset {
|
|
leftLen := int64(diff.Offset) - int64(base.Offset)
|
|
if leftLen > 0 {
|
|
result = append(result, &BuildMap{
|
|
Offset: base.Offset,
|
|
Length: uint64(leftLen),
|
|
BuildID: base.BuildID,
|
|
BuildStorageOffset: base.BuildStorageOffset,
|
|
})
|
|
}
|
|
bi++
|
|
continue
|
|
}
|
|
}
|
|
|
|
// Append remaining entries.
|
|
result = append(result, baseCopy[bi:]...)
|
|
result = append(result, diffMapping[di:]...)
|
|
|
|
return result
|
|
}
|
|
|
|
// NormalizeMappings merges adjacent entries with the same BuildID.
|
|
func NormalizeMappings(mappings []*BuildMap) []*BuildMap {
|
|
if len(mappings) == 0 {
|
|
return nil
|
|
}
|
|
|
|
result := make([]*BuildMap, 0, len(mappings))
|
|
current := &BuildMap{
|
|
Offset: mappings[0].Offset,
|
|
Length: mappings[0].Length,
|
|
BuildID: mappings[0].BuildID,
|
|
BuildStorageOffset: mappings[0].BuildStorageOffset,
|
|
}
|
|
|
|
for i := 1; i < len(mappings); i++ {
|
|
m := mappings[i]
|
|
if m.BuildID == current.BuildID {
|
|
current.Length += m.Length
|
|
} else {
|
|
result = append(result, current)
|
|
current = &BuildMap{
|
|
Offset: m.Offset,
|
|
Length: m.Length,
|
|
BuildID: m.BuildID,
|
|
BuildStorageOffset: m.BuildStorageOffset,
|
|
}
|
|
}
|
|
}
|
|
result = append(result, current)
|
|
|
|
return result
|
|
}
|