1
2
3
4
5
6
7
8 package jsonwire
9
10 import (
11 "cmp"
12 "errors"
13 "strconv"
14 "strings"
15 "unicode"
16 "unicode/utf16"
17 "unicode/utf8"
18 )
19
20
21 func TrimSuffixWhitespace(b []byte) []byte {
22
23 n := len(b) - 1
24 for n >= 0 && (b[n] == ' ' || b[n] == '\t' || b[n] == '\r' || b[n] == '\n') {
25 n--
26 }
27 return b[:n+1]
28 }
29
30
31
32 func TrimSuffixString(b []byte) []byte {
33
34 if len(b) > 0 && b[len(b)-1] == '"' {
35 b = b[:len(b)-1]
36 }
37 for len(b) >= 2 && !(b[len(b)-1] == '"' && b[len(b)-2] != '\\') {
38 b = b[:len(b)-1]
39 }
40 if len(b) > 0 && b[len(b)-1] == '"' {
41 b = b[:len(b)-1]
42 }
43 return b
44 }
45
46
47 func HasSuffixByte(b []byte, c byte) bool {
48
49 return len(b) > 0 && b[len(b)-1] == c
50 }
51
52
53 func TrimSuffixByte(b []byte, c byte) []byte {
54
55 if len(b) > 0 && b[len(b)-1] == c {
56 return b[:len(b)-1]
57 }
58 return b
59 }
60
61
62 func QuoteRune(b []byte) string {
63 r, n := utf8.DecodeRune(b)
64 if r == utf8.RuneError && n == 1 {
65 return `'\x` + strconv.FormatUint(uint64(b[0]), 16) + `'`
66 }
67 return strconv.QuoteRune(r)
68 }
69
70
71
72
73 func CompareUTF16(x, y []byte) int {
74
75
76
77 isUTF16Self := func(r rune) bool {
78 return ('\u0000' <= r && r <= '\uD7FF') || ('\uE000' <= r && r <= '\uFFFF')
79 }
80
81 for {
82 if len(x) == 0 || len(y) == 0 {
83 return cmp.Compare(len(x), len(y))
84 }
85
86
87 if x[0] < utf8.RuneSelf || y[0] < utf8.RuneSelf {
88 if x[0] != y[0] {
89 return cmp.Compare(x[0], y[0])
90 }
91 x, y = x[1:], y[1:]
92 continue
93 }
94
95
96 rx, nx := utf8.DecodeRune(x)
97 ry, ny := utf8.DecodeRune(y)
98
99 selfx := isUTF16Self(rx)
100 selfy := isUTF16Self(ry)
101 switch {
102
103
104 case selfx && !selfy:
105 ry, _ = utf16.EncodeRune(ry)
106
107
108 case selfy && !selfx:
109 rx, _ = utf16.EncodeRune(rx)
110 }
111 if rx != ry {
112 return cmp.Compare(rx, ry)
113 }
114
115
116
117 if isInvalidUTF8(rx, nx) || isInvalidUTF8(ry, ny) {
118 if x[0] != y[0] {
119 return cmp.Compare(x[0], y[0])
120 }
121 }
122 x, y = x[nx:], y[ny:]
123 }
124 }
125
126
127 var ErrInvalidUTF8 = errors.New("invalid UTF-8")
128
129 func NewInvalidCharacterError[Bytes ~[]byte | ~string](prefix Bytes, where string) error {
130 _, n := utf8.DecodeRune([]byte(prefix))
131 return &InvalidTextError{"character", string(prefix[:n]), where}
132 }
133
134 func NewInvalidEscapeSequenceError[Bytes ~[]byte | ~string](what Bytes) error {
135 if len(what) > 6 {
136 return &InvalidTextError{"surrogate pair", string(what), "in string"}
137 }
138 return &InvalidTextError{"escape sequence", string(what), "in string"}
139 }
140
141 type InvalidTextError struct {
142 Label string
143 What string
144 Where string
145 }
146
147 func (e *InvalidTextError) Error() string {
148 var what string
149 needEscape := strings.ContainsFunc(e.What, func(r rune) bool {
150 return r == '`' || r == utf8.RuneError || unicode.IsSpace(r) || !unicode.IsPrint(r)
151 })
152 switch {
153 case utf8.RuneCount([]byte(e.What)) == 1:
154 what = QuoteRune([]byte(e.What))
155 case needEscape:
156 what = strconv.Quote(e.What)
157 default:
158 what = "`" + e.What + "`"
159 }
160 return strings.TrimSuffix("invalid "+e.Label+" "+what+" "+e.Where, " ")
161 }
162
163
164
165 func TruncatePointer(s string, n int) string {
166 if len(s) <= n {
167 return s
168 }
169 i := n / 2
170 j := len(s) - n/2
171
172
173 if k := strings.LastIndexByte(s[:i], '/'); k > 0 {
174 i = k
175 }
176 if k := strings.IndexByte(s[j:], '/'); k >= 0 {
177 j += k + len("/")
178 }
179
180
181 for i > 0 && isInvalidUTF8(utf8.DecodeLastRuneInString(s[:i])) {
182 i--
183 }
184 for j < len(s) && isInvalidUTF8(utf8.DecodeRuneInString(s[j:])) {
185 j++
186 }
187
188
189 var middle string
190 switch strings.Count(s[i:j], "/") {
191 case 0:
192 middle = "…"
193 case 1:
194 middle = "…/…"
195 default:
196 middle = "…/…/…"
197 }
198 if strings.HasPrefix(s[i:j], "/") && middle != "…" {
199 middle = strings.TrimPrefix(middle, "…")
200 }
201 if strings.HasSuffix(s[i:j], "/") && middle != "…" {
202 middle = strings.TrimSuffix(middle, "…")
203 }
204 return s[:i] + middle + s[j:]
205 }
206
207 func isInvalidUTF8(r rune, rn int) bool {
208 return r == utf8.RuneError && rn == 1
209 }
210
View as plain text