Skip to content

Commit

Permalink
refactor: better bytes fuzzy match (fuzzy is wrong word but...)
Browse files Browse the repository at this point in the history
  • Loading branch information
carlmontanari committed Sep 6, 2023
1 parent c884aae commit 2ae88e4
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 4 deletions.
5 changes: 1 addition & 4 deletions channel/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import (
"regexp"
"time"

"github.com/lithammer/fuzzysearch/fuzzy"

"github.com/scrapli/scrapligo/util"
)

Expand Down Expand Up @@ -132,8 +130,7 @@ func (c *Channel) ReadUntilFuzzy(b []byte) ([]byte, error) {

rb = append(rb, nb...)

rankedMatch := fuzzy.RankMatch(string(b), string(rb))
if rankedMatch == 0 || rankedMatch >= 50 {
if util.BytesRoughlyContains(b, rb) {
c.l.Debugf("channel read %#v", string(rb))

return rb, nil
Expand Down
70 changes: 70 additions & 0 deletions util/bytes.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,73 @@ func ByteContainsAny(b []byte, l [][]byte) bool {

return false
}

// BytesRoughlyContains returns true if all bytes from the given byte slice `input` exist in the
// given `output` byte slice -- the elements must be found in order. This is basically the same as
// what you can see in @lithammer's(1) fuzzysearch `Match` function (thank you to them!) but converted

Check failure on line 55 in util/bytes.go

View workflow job for this annotation

GitHub Actions / unit-test (ubuntu-latest, 1.18)

line is 102 characters (lll)

Check failure on line 55 in util/bytes.go

View workflow job for this annotation

GitHub Actions / unit-test (ubuntu-latest, 1.16)

line is 102 characters (lll)
// to work for bytes and to not use a continuation block. Some examples:
//
// input 'aa', output 'b' = false
// input 'aa', output 'bba' = false
// input 'aa', output 'bbaa' = true
// input 'aba', output 'bba' = false
//
// In the context of scrapligo this is basically used for "fuzzy" matching our inputs. This lets us
// cope with devices that do things like the following srlinux banner entry output:
//
// ```
// --{ !* candidate shared default }--[ ]--
// A:srl# system banner login-banner "
// ...my banner
// ...has
// ...some lines
// ...that are neat
// ..."
// --{ !* candidate shared default }--[ ]--
//
// The "..." at the beginning of each line would historically be problematic for scrapli because in
// a very brute force/ham-fisted way we would demand to read back exactly what we sent to the device
// in the output -- so the "..." broke that. Not cool! This can be used to ensure that doesn't
// happen!
//
// Note: @lithammer's fuzzy search `Match` function here:
// https://github.com/lithammer/fuzzysearch/blob/master/fuzzy/fuzzy.go#L60-L83
func BytesRoughlyContains(input, output []byte) bool {
switch diffLen := len(output) - len(input); {
case diffLen < 0:
// output is not long enough to hold all our inputs, so definitely not roughly contains!
return false
case diffLen == 0:
// diff is same length, can directly test equality
if bytes.Equal(input, output) {
return true
}
}

for _, inputChar := range input {
var shouldContinue bool

shouldContinue, output = innerBytesRoughlyContains(inputChar, output)

if shouldContinue {
continue
}

return false
}

return true
}

func innerBytesRoughlyContains(
inputChar byte,
output []byte,
) (shouldContinue bool, newOutput []byte) {
for idx, outputChar := range output {
if inputChar == outputChar {
return true, output[idx+1:]
}
}

return false, output
}

0 comments on commit 2ae88e4

Please sign in to comment.