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[Bytes ~[]byte | ~string](b Bytes) string {
63 r, n := utf8.DecodeRuneInString(string(truncateMaxUTF8(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[Bytes ~[]byte | ~string](x, y Bytes) 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.DecodeRuneInString(string(truncateMaxUTF8(x)))
97 ry, ny := utf8.DecodeRuneInString(string(truncateMaxUTF8(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
128
129
130
131
132
133
134
135
136
137
138
139 func truncateMaxUTF8[Bytes ~[]byte | ~string](b Bytes) Bytes {
140
141
142 if len(b) > utf8.UTFMax {
143 return b[:utf8.UTFMax]
144 }
145 return b
146 }
147
148
149 var ErrInvalidUTF8 = errors.New("invalid UTF-8")
150
151 func NewInvalidCharacterError[Bytes ~[]byte | ~string](prefix Bytes, where string) error {
152 what := QuoteRune(prefix)
153 return errors.New("invalid character " + what + " " + where)
154 }
155
156 func NewInvalidEscapeSequenceError[Bytes ~[]byte | ~string](what Bytes) error {
157 label := "escape sequence"
158 if len(what) > 6 {
159 label = "surrogate pair"
160 }
161 needEscape := strings.IndexFunc(string(what), func(r rune) bool {
162 return r == '`' || r == utf8.RuneError || unicode.IsSpace(r) || !unicode.IsPrint(r)
163 }) >= 0
164 if needEscape {
165 return errors.New("invalid " + label + " " + strconv.Quote(string(what)) + " in string")
166 } else {
167 return errors.New("invalid " + label + " `" + string(what) + "` in string")
168 }
169 }
170
171
172
173 func TruncatePointer(s string, n int) string {
174 if len(s) <= n {
175 return s
176 }
177 i := n / 2
178 j := len(s) - n/2
179
180
181 if k := strings.LastIndexByte(s[:i], '/'); k > 0 {
182 i = k
183 }
184 if k := strings.IndexByte(s[j:], '/'); k >= 0 {
185 j += k + len("/")
186 }
187
188
189 for i > 0 && isInvalidUTF8(utf8.DecodeLastRuneInString(s[:i])) {
190 i--
191 }
192 for j < len(s) && isInvalidUTF8(utf8.DecodeRuneInString(s[j:])) {
193 j++
194 }
195
196
197 var middle string
198 switch strings.Count(s[i:j], "/") {
199 case 0:
200 middle = "…"
201 case 1:
202 middle = "…/…"
203 default:
204 middle = "…/…/…"
205 }
206 if strings.HasPrefix(s[i:j], "/") && middle != "…" {
207 middle = strings.TrimPrefix(middle, "…")
208 }
209 if strings.HasSuffix(s[i:j], "/") && middle != "…" {
210 middle = strings.TrimSuffix(middle, "…")
211 }
212 return s[:i] + middle + s[j:]
213 }
214
215 func isInvalidUTF8(r rune, rn int) bool {
216 return r == utf8.RuneError && rn == 1
217 }
218
View as plain text