Files
steampipe/pkg/modinstaller/byte_sequence.go

108 lines
2.5 KiB
Go

package modinstaller
import (
"sort"
)
type ChangeOperation int
const (
Insert ChangeOperation = iota
Delete
Replace
)
type Change struct {
Content []byte
Operation ChangeOperation
OffsetStart int
OffsetEnd int
}
type ChangeSet []*Change
func EmptyChangeSet() ChangeSet { return ChangeSet{} }
// MergeChangeSet creates a ChangeSet by merging the given ChangeSets in order
func MergeChangeSet(changeSets ...ChangeSet) ChangeSet {
changeSet := ChangeSet{}
for _, cs := range changeSets {
changeSet = append(changeSet, cs...)
}
return changeSet
}
// NewChangeSet creates a ChangeSet from the given changes
func NewChangeSet(changes ...*Change) ChangeSet {
return ChangeSet(changes)
}
func (c ChangeSet) SortByOffset() {
// sort the changes into descending order of byte offset
// this way, when a change is applied, even if it's replacement
// does not have the exact same bytes, we don't lose the offset information
// of the changes preceeding it
sort.Slice(c, func(i, j int) bool {
return c[i].OffsetStart > c[j].OffsetStart
})
}
type OperatorFunc func(*Change, []byte) []byte
type ByteSequence struct {
operators map[ChangeOperation]OperatorFunc
_underlying []byte
}
func NewByteSequence(b []byte) *ByteSequence {
byteSequence := new(ByteSequence)
byteSequence._underlying = make([]byte, len(b))
copy(byteSequence._underlying, b)
byteSequence.operators = map[ChangeOperation]OperatorFunc{
Insert: insert,
Delete: clear,
Replace: replace,
}
return byteSequence
}
func (b *ByteSequence) ApplyChanges(changeSet ChangeSet) {
changeSet.SortByOffset()
for _, change := range changeSet {
operation := change.Operation
if operator, ok := b.operators[operation]; ok {
b._underlying = operator(change, b._underlying)
}
}
}
// Apply applies the given function on the byte sequence
func (bseq *ByteSequence) Apply(apply func([]byte) []byte) {
bseq._underlying = apply(bseq._underlying)
}
// Bytes returns the current underlying byte sequence
func (bseq *ByteSequence) Bytes() []byte {
return bseq._underlying
}
func clear(change *Change, source []byte) []byte {
left := source[:change.OffsetStart]
right := source[change.OffsetEnd:]
return append(left, right...)
}
func insert(change *Change, source []byte) []byte {
left := source[:change.OffsetStart]
right := source[change.OffsetStart:]
// prepend the content before the right part
right = append(change.Content, right...)
return append(left, right...)
}
func replace(change *Change, source []byte) []byte {
return insert(change, clear(change, source))
}