hopp/internal/testutil/testutil.go

95 lines
2.6 KiB
Go

package testutil
import "fmt"
import "slices"
import "strings"
// Snake lets you compare blocks of data where the ordering of certain parts may
// be swapped every which way. It is designed for comparing the encoding of
// maps where the ordering of individual elements is inconsistent.
//
// The snake is divided into sectors, which hold a number of variations. For a
// sector to be satisfied by some data, some ordering of it must match the data
// exactly. for the snake to be satisfied by some data, its sectors must match
// the data in order, but the internal ordering of each sector doesn't matter.
type Snake [] [] []byte
// snake sector variation
// S returns a new snake.
func S(data ...byte) Snake {
return (Snake { }).Add(data...)
}
// AddVar returns a new snake with the given sector added on to it. Successive
// calls of this method can be chained together to create a big ass snake.
func (sn Snake) AddVar(sector ...[]byte) Snake {
slice := make(Snake, len(sn) + 1)
copy(slice, sn)
slice[len(slice) - 1] = sector
return slice
}
// Add is like AddVar, but adds a sector with only one variation, which means it
// does not vary, hence why the method is called that.
func (sn Snake) Add(data ...byte) Snake {
return sn.AddVar(data)
}
// Check determines if the data satisfies the snake.
func (sn Snake) Check(data []byte) (ok bool, n int) {
left := data
variations := map[int] []byte { }
for _, sector := range sn {
clear(variations)
for key, variation := range sector {
variations[key] = variation
}
for len(variations) > 0 {
found := false
for key, variation := range variations {
if len(left) < len(variation) { continue }
if !slices.Equal(left[:len(variation)], variation) { continue }
n += len(variation)
left = data[n:]
delete(variations, key)
found = true
}
if !found { return false, n }
}
}
if n < len(data) {
return false, n
}
return true, n
}
func (sn Snake) String() string {
if len(sn) == 0 || len(sn[0]) == 0 || len(sn[0][0]) == 0 {
return "EMPTY"
}
out := strings.Builder { }
for index, sector := range sn {
if index > 0 { out.WriteString(" : ") }
out.WriteRune('[')
for index, variation := range sector {
if index > 0 { out.WriteString(" / ") }
for _, byt := range variation {
fmt.Fprintf(&out, "%02x", byt)
}
}
out.WriteRune(']')
}
return out.String()
}
// HexBytes formats bytes into a hexadecimal string.
func HexBytes(data []byte) string {
if len(data) == 0 { return "EMPTY" }
out := strings.Builder { }
for _, byt := range data {
fmt.Fprintf(&out, "%02x", byt)
}
return out.String()
}