| | |
| | |
| | |
| |
|
| | |
| |
|
| | |
| | package jsonwire |
| |
|
| | import ( |
| | "cmp" |
| | "errors" |
| | "strconv" |
| | "strings" |
| | "unicode" |
| | "unicode/utf16" |
| | "unicode/utf8" |
| | ) |
| |
|
| | |
| | func TrimSuffixWhitespace(b []byte) []byte { |
| | |
| | n := len(b) - 1 |
| | for n >= 0 && (b[n] == ' ' || b[n] == '\t' || b[n] == '\r' || b[n] == '\n') { |
| | n-- |
| | } |
| | return b[:n+1] |
| | } |
| |
|
| | |
| | |
| | func TrimSuffixString(b []byte) []byte { |
| | |
| | if len(b) > 0 && b[len(b)-1] == '"' { |
| | b = b[:len(b)-1] |
| | } |
| | for len(b) >= 2 && !(b[len(b)-1] == '"' && b[len(b)-2] != '\\') { |
| | b = b[:len(b)-1] |
| | } |
| | if len(b) > 0 && b[len(b)-1] == '"' { |
| | b = b[:len(b)-1] |
| | } |
| | return b |
| | } |
| |
|
| | |
| | func HasSuffixByte(b []byte, c byte) bool { |
| | |
| | return len(b) > 0 && b[len(b)-1] == c |
| | } |
| |
|
| | |
| | func TrimSuffixByte(b []byte, c byte) []byte { |
| | |
| | if len(b) > 0 && b[len(b)-1] == c { |
| | return b[:len(b)-1] |
| | } |
| | return b |
| | } |
| |
|
| | |
| | func QuoteRune[Bytes ~[]byte | ~string](b Bytes) string { |
| | r, n := utf8.DecodeRuneInString(string(truncateMaxUTF8(b))) |
| | if r == utf8.RuneError && n == 1 { |
| | return `'\x` + strconv.FormatUint(uint64(b[0]), 16) + `'` |
| | } |
| | return strconv.QuoteRune(r) |
| | } |
| |
|
| | |
| | |
| | |
| | func CompareUTF16[Bytes ~[]byte | ~string](x, y Bytes) int { |
| | |
| | |
| | |
| | isUTF16Self := func(r rune) bool { |
| | return ('\u0000' <= r && r <= '\uD7FF') || ('\uE000' <= r && r <= '\uFFFF') |
| | } |
| |
|
| | for { |
| | if len(x) == 0 || len(y) == 0 { |
| | return cmp.Compare(len(x), len(y)) |
| | } |
| |
|
| | |
| | if x[0] < utf8.RuneSelf || y[0] < utf8.RuneSelf { |
| | if x[0] != y[0] { |
| | return cmp.Compare(x[0], y[0]) |
| | } |
| | x, y = x[1:], y[1:] |
| | continue |
| | } |
| |
|
| | |
| | rx, nx := utf8.DecodeRuneInString(string(truncateMaxUTF8(x))) |
| | ry, ny := utf8.DecodeRuneInString(string(truncateMaxUTF8(y))) |
| |
|
| | selfx := isUTF16Self(rx) |
| | selfy := isUTF16Self(ry) |
| | switch { |
| | |
| | |
| | case selfx && !selfy: |
| | ry, _ = utf16.EncodeRune(ry) |
| | |
| | |
| | case selfy && !selfx: |
| | rx, _ = utf16.EncodeRune(rx) |
| | } |
| | if rx != ry { |
| | return cmp.Compare(rx, ry) |
| | } |
| |
|
| | |
| | |
| | if isInvalidUTF8(rx, nx) || isInvalidUTF8(ry, ny) { |
| | if x[0] != y[0] { |
| | return cmp.Compare(x[0], y[0]) |
| | } |
| | } |
| | x, y = x[nx:], y[ny:] |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func truncateMaxUTF8[Bytes ~[]byte | ~string](b Bytes) Bytes { |
| | |
| | |
| | if len(b) > utf8.UTFMax { |
| | return b[:utf8.UTFMax] |
| | } |
| | return b |
| | } |
| |
|
| | |
| | var ErrInvalidUTF8 = errors.New("invalid UTF-8") |
| |
|
| | func NewInvalidCharacterError[Bytes ~[]byte | ~string](prefix Bytes, where string) error { |
| | what := QuoteRune(prefix) |
| | return errors.New("invalid character " + what + " " + where) |
| | } |
| |
|
| | func NewInvalidEscapeSequenceError[Bytes ~[]byte | ~string](what Bytes) error { |
| | label := "escape sequence" |
| | if len(what) > 6 { |
| | label = "surrogate pair" |
| | } |
| | needEscape := strings.IndexFunc(string(what), func(r rune) bool { |
| | return r == '`' || r == utf8.RuneError || unicode.IsSpace(r) || !unicode.IsPrint(r) |
| | }) >= 0 |
| | if needEscape { |
| | return errors.New("invalid " + label + " " + strconv.Quote(string(what)) + " in string") |
| | } else { |
| | return errors.New("invalid " + label + " `" + string(what) + "` in string") |
| | } |
| | } |
| |
|
| | |
| | |
| | func TruncatePointer(s string, n int) string { |
| | if len(s) <= n { |
| | return s |
| | } |
| | i := n / 2 |
| | j := len(s) - n/2 |
| |
|
| | |
| | if k := strings.LastIndexByte(s[:i], '/'); k > 0 { |
| | i = k |
| | } |
| | if k := strings.IndexByte(s[j:], '/'); k >= 0 { |
| | j += k + len("/") |
| | } |
| |
|
| | |
| | for i > 0 && isInvalidUTF8(utf8.DecodeLastRuneInString(s[:i])) { |
| | i-- |
| | } |
| | for j < len(s) && isInvalidUTF8(utf8.DecodeRuneInString(s[j:])) { |
| | j++ |
| | } |
| |
|
| | |
| | var middle string |
| | switch strings.Count(s[i:j], "/") { |
| | case 0: |
| | middle = "…" |
| | case 1: |
| | middle = "…/…" |
| | default: |
| | middle = "…/…/…" |
| | } |
| | if strings.HasPrefix(s[i:j], "/") && middle != "…" { |
| | middle = strings.TrimPrefix(middle, "…") |
| | } |
| | if strings.HasSuffix(s[i:j], "/") && middle != "…" { |
| | middle = strings.TrimSuffix(middle, "…") |
| | } |
| | return s[:i] + middle + s[j:] |
| | } |
| |
|
| | func isInvalidUTF8(r rune, rn int) bool { |
| | return r == utf8.RuneError && rn == 1 |
| | } |
| |
|