refactor(parser/diff): rune based strings compare
All checks were successful
submodules sync / sync (push) Successful in 1m0s
build / build (push) Successful in 2m18s
build / trigger-build-image (push) Successful in 9s

This commit is contained in:
张泊明518370910136 2025-03-28 08:41:08 -04:00
parent 37c0d76bf3
commit 29c05f4b36
GPG Key ID: D47306D7062CDA9D
3 changed files with 48 additions and 58 deletions

View File

@ -1,46 +0,0 @@
package diff
// compareStrings compares two strings character by character, optionally ignoring whitespace.
func compareStrings(str1, str2 string, compareSpace bool) bool {
if compareSpace {
return str1 == str2
}
var i, j int
l1 := len(str1)
l2 := len(str2)
for i < l1 && j < l2 {
for i < l1 && isWhitespace(str1[i]) {
i++
}
for j < l2 && isWhitespace(str2[j]) {
j++
}
if i < l1 && j < l2 && str1[i] != str2[j] {
return false
}
if i < l1 {
i++
}
if j < l2 {
j++
}
}
for i < l1 && isWhitespace(str1[i]) {
i++
}
for j < l2 && isWhitespace(str2[j]) {
j++
}
return i == l1 && j == l2
}
func isWhitespace(b byte) bool {
return b == ' ' ||
b == '\t' ||
b == '\n' ||
b == '\r' ||
b == '\v' ||
b == '\f' ||
b == 0x85 ||
b == 0xA0
}

View File

@ -39,7 +39,7 @@ func (*Diff) Run(results []stage.ExecutorResult, confAny any) (
if err != nil { if err != nil {
return nil, true, err return nil, true, err
} }
isSame := compareStrings( isSame := stringsEqual(
string(answer), string(answer),
result.Files[output.FileName], result.Files[output.FileName],
output.CompareSpace, output.CompareSpace,
@ -82,13 +82,13 @@ func (*Diff) Run(results []stage.ExecutorResult, confAny any) (
} }
answerLines := strings.Split(answerStr, "\n") answerLines := strings.Split(answerStr, "\n")
resultLines := strings.Split(resultStr, "\n") resultLines := strings.Split(resultStr, "\n")
diffs := PatienceDiff( diffs := patienceDiff(
answerLines, answerLines,
resultLines, resultLines,
func(a, b string) bool { func(a, b string) bool {
return compareStrings(a, b, output.CompareSpace) return stringsEqual(a, b, output.CompareSpace)
}) })
diffOutput := DiffText(diffs) diffOutput := diffText(diffs)
diffOutput = strings.TrimSuffix(diffOutput, "\n ") diffOutput = strings.TrimSuffix(diffOutput, "\n ")
if truncated { if truncated {
diffOutput += "\n\n(truncated)" diffOutput += "\n\n(truncated)"

View File

@ -5,8 +5,44 @@ package diff
import ( import (
"fmt" "fmt"
"strings" "strings"
"unicode"
) )
// stringsEqual compares two strings character by character, optionally ignoring whitespace.
func stringsEqual(str1, str2 string, compareSpace bool) bool {
if compareSpace {
return str1 == str2
}
runes1 := []rune(str1)
runes2 := []rune(str2)
var i, j, l1, l2 int
l1 = len(runes1)
l2 = len(runes2)
for i < l1 && j < l2 {
for i < l1 && unicode.IsSpace(runes1[i]) {
i++
}
for j < l2 && unicode.IsSpace(runes2[j]) {
j++
}
if i >= l1 || j >= l2 {
break
}
if runes1[i] != runes2[j] {
return false
}
i++
j++
}
for i < l1 && unicode.IsSpace(runes1[i]) {
i++
}
for j < l2 && unicode.IsSpace(runes2[j]) {
j++
}
return i == l1 && j == l2
}
// DiffType defines the type of a diff element. // DiffType defines the type of a diff element.
type DiffType int8 type DiffType int8
@ -39,8 +75,8 @@ func typeSymbol(t DiffType) string {
} }
} }
// DiffText returns the source and destination texts (all equalities, insertions and deletions). // diffText returns the source and destination texts (all equalities, insertions and deletions).
func DiffText(diffs []DiffLine) string { func diffText(diffs []DiffLine) string {
s := make([]string, len(diffs)) s := make([]string, len(diffs))
for i, l := range diffs { for i, l := range diffs {
s[i] = fmt.Sprintf("%s%s", typeSymbol(l.Type), l.Text) s[i] = fmt.Sprintf("%s%s", typeSymbol(l.Type), l.Text)
@ -120,8 +156,8 @@ func uniqueElements(a []string) ([]string, []int) {
return elements, indices return elements, indices
} }
// PatienceDiff returns the patience diff of two slices of strings. // patienceDiff returns the patience diff of two slices of strings.
func PatienceDiff(a, b []string, equal func(a, b string) bool) []DiffLine { func patienceDiff(a, b []string, equal func(a, b string) bool) []DiffLine {
switch { switch {
case len(a) == 0 && len(b) == 0: case len(a) == 0 && len(b) == 0:
return nil return nil
@ -139,7 +175,7 @@ func PatienceDiff(a, b []string, equal func(a, b string) bool) []DiffLine {
if i > 0 { if i > 0 {
return append( return append(
toDiffLines(a[:i], Equal), toDiffLines(a[:i], Equal),
PatienceDiff(a[i:], b[i:], equal)..., patienceDiff(a[i:], b[i:], equal)...,
) )
} }
@ -150,7 +186,7 @@ func PatienceDiff(a, b []string, equal func(a, b string) bool) []DiffLine {
} }
if j > 0 { if j > 0 {
return append( return append(
PatienceDiff(a[:len(a)-j], b[:len(b)-j], equal), patienceDiff(a[:len(a)-j], b[:len(b)-j], equal),
toDiffLines(a[len(a)-j:], Equal)..., toDiffLines(a[len(a)-j:], Equal)...,
) )
} }
@ -175,14 +211,14 @@ func PatienceDiff(a, b []string, equal func(a, b string) bool) []DiffLine {
ga, gb := 0, 0 ga, gb := 0, 0
for _, ip := range lcs { for _, ip := range lcs {
// PatienceDiff the gaps between the lcs elements. // PatienceDiff the gaps between the lcs elements.
diffs = append(diffs, PatienceDiff(a[ga:ip[0]], b[gb:ip[1]], equal)...) diffs = append(diffs, patienceDiff(a[ga:ip[0]], b[gb:ip[1]], equal)...)
// Append the LCS elements to the diff. // Append the LCS elements to the diff.
diffs = append(diffs, DiffLine{Type: Equal, Text: a[ip[0]]}) diffs = append(diffs, DiffLine{Type: Equal, Text: a[ip[0]]})
ga = ip[0] + 1 ga = ip[0] + 1
gb = ip[1] + 1 gb = ip[1] + 1
} }
// PatienceDiff the remaining elements of a and b after the final LCS element. // PatienceDiff the remaining elements of a and b after the final LCS element.
diffs = append(diffs, PatienceDiff(a[ga:], b[gb:], equal)...) diffs = append(diffs, patienceDiff(a[ga:], b[gb:], equal)...)
return diffs return diffs
} }