Add bounded diagnostic prefix encoding
This commit is contained in:
48
internal/usecase/repair_prefix.go
Normal file
48
internal/usecase/repair_prefix.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package usecase
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// boundedDiagnosticJSON returns a JSON string prefix and whether it represents
|
||||
// the complete normalized input. It retains at most the useful prefix.
|
||||
func boundedDiagnosticJSON(value string, maxBytes int) ([]byte, bool) {
|
||||
if maxBytes < 2 {
|
||||
return nil, false
|
||||
}
|
||||
limit := maxBytes - 2
|
||||
var prefix strings.Builder
|
||||
used := 0
|
||||
more := false
|
||||
for i := 0; i < len(value); {
|
||||
r, size := utf8.DecodeRuneInString(value[i:])
|
||||
piece := value[i : i+size]
|
||||
if r == utf8.RuneError && size == 1 {
|
||||
piece = "\uFFFD"
|
||||
i++
|
||||
for i < len(value) {
|
||||
r, size = utf8.DecodeRuneInString(value[i:])
|
||||
if r != utf8.RuneError || size != 1 {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
} else {
|
||||
i += size
|
||||
}
|
||||
if used+len(piece) > limit {
|
||||
more = true
|
||||
break
|
||||
}
|
||||
prefix.WriteString(piece)
|
||||
used += len(piece)
|
||||
}
|
||||
normalized := prefix.String()
|
||||
encoded, _ := json.Marshal(normalized)
|
||||
if len(encoded) > maxBytes {
|
||||
return truncateDiagnosticJSONValue(normalized, maxBytes), false
|
||||
}
|
||||
return encoded, !more
|
||||
}
|
||||
33
internal/usecase/repair_prefix_test.go
Normal file
33
internal/usecase/repair_prefix_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package usecase
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func TestBoundedDiagnosticJSON(t *testing.T) {
|
||||
for _, input := range []string{"", "plain", "<&>\n\"", "雪☃", "a\xff\xfe\xffb", "\xff\xfe" + strings.Repeat("z", 100)} {
|
||||
for budget := 0; budget <= 30; budget++ {
|
||||
encoded, complete := boundedDiagnosticJSON(input, budget)
|
||||
if budget < 2 {
|
||||
if encoded != nil || complete {
|
||||
t.Fatalf("budget %d: %q %v", budget, encoded, complete)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if len(encoded) > budget || !utf8.Valid(encoded) {
|
||||
t.Fatalf("budget %d: %q", budget, encoded)
|
||||
}
|
||||
var decoded string
|
||||
if err := json.Unmarshal(encoded, &decoded); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
normalized := strings.ToValidUTF8(input, "\uFFFD")
|
||||
if !strings.HasPrefix(normalized, decoded) || complete != (decoded == normalized) {
|
||||
t.Fatalf("input %q budget %d: %q complete=%v normalized=%q", input, budget, decoded, complete, normalized)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user