@@ -2,48 +2,54 @@ package zlog
22
33import (
44 "context"
5- "fmt"
65 "regexp"
7- "strconv"
6+ "strings"
7+ "unicode/utf8"
88
99 "go.opentelemetry.io/otel/baggage"
1010)
1111
1212// NeedEscape matches a string that needs to be escaped either into an ASCII or a percent-encoded representation.
13- var needEscape = regexp .MustCompile (`%(?:$|([0-9a-fA-F]?[^0-9a-fA-F]))|[^\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]` )
13+ var needEscape = regexp .MustCompile (`%(?:$|([0-9a-fA-F]?[^0-9a-fA-F]))|[^\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]|[\x80-\x{10FFFF}] ` )
1414
1515// PctEncode matches a string that requires some characters to be percent-encoded.
16- var pctEncode = regexp .MustCompile (`%(?:$|([0-9a-fA-F][^0-9a-fA-F])|[^0-9a-fA-F])| |"|,|;|\\` )
16+ var pctEncode = regexp .MustCompile (`%(?:$|([0-9a-fA-F][^0-9a-fA-F])|[^0-9a-fA-F])|[^\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]+|[\x80-\x{10FFFF}]+` )
17+
18+ // EscapeOne is the set of 1-byte utf8 characters that should be percent encoded.
19+ //
20+ // This could be avoided if the [pctEncode] regexp was made robust enough to
21+ // ignore correct hex escapes and only capture "lone" percent symbols.
22+ var escapeOne = regexp .MustCompile (`[^\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]|%| |"|,|;|\\` )
1723
1824func escapeValue (v string ) string {
19- v = pctEncode .ReplaceAllStringFunc (v , func (m string ) (r string ) {
20- for _ , c := range m {
21- switch c {
22- case '%' :
23- r += "%25"
24- case ' ' :
25- r += "%20"
26- case '"' :
27- r += "%22"
28- case ',' :
29- r += "%2C"
30- case ';' :
31- r += "%3B"
32- case '\\' :
33- r += "%5C"
34- default :
35- // Just copy to the return value.
36- // This is (hopefully) just picking up the positions where percent-encoded nybbles would be.
37- r += string (c )
25+ const hexchar = `0123456789ABCDEF`
26+ var b strings.Builder
27+ b .Grow (4 * 3 )
28+ return pctEncode .ReplaceAllStringFunc (v , func (v string ) string {
29+ b .Reset ()
30+ for _ , c := range v {
31+ n := utf8 .RuneLen (c )
32+ if n == 1 {
33+ c := byte (c )
34+ if escapeOne .Match ([]byte {c }) {
35+ b .WriteRune ('%' )
36+ b .WriteByte (hexchar [c >> 4 ])
37+ b .WriteByte (hexchar [c & 15 ])
38+ } else {
39+ b .WriteByte (c )
40+ }
41+ continue
42+ }
43+ p := make ([]byte , n )
44+ utf8 .EncodeRune (p , c )
45+ for _ , c := range p {
46+ b .WriteRune ('%' )
47+ b .WriteByte (hexchar [c >> 4 ])
48+ b .WriteByte (hexchar [c & 15 ])
3849 }
3950 }
40- if len (m ) == len (r ) {
41- panic (fmt .Sprintf ("programmer error: pulled odd string %q" , m ))
42- }
43- return r
51+ return b .String ()
4452 })
45- v = strconv .QuoteToASCII (v )
46- return v [1 : len (v )- 1 ]
4753}
4854
4955// ContextWithValues is a helper for the go.opentelemetry.io/otel/baggage v1
0 commit comments