reflect: add sipmlified strconv.Quote() implementation for struct tags

This commit is contained in:
Damian Gryski
2023-03-24 13:48:00 -07:00
committed by Ron Evans
parent 195de23d3b
commit 5faff2e13a
3 changed files with 101 additions and 6 deletions
+99
View File
@@ -30,6 +30,10 @@ func unhex(b byte) (v rune, ok bool) {
return
}
const (
lowerhex = "0123456789abcef"
)
// unquoteChar decodes the first character or byte in the escaped string
// or character literal represented by the string s.
// It returns four values:
@@ -231,6 +235,101 @@ func unquote(s string) (string, error) {
return string(buf), nil
}
func quote(s string) string {
buf := make([]byte, 0, 3*len(s)/2)
const quote = '"'
buf = append(buf, quote)
for width := 0; len(s) > 0; s = s[width:] {
r := rune(s[0])
width = 1
if r >= utf8.RuneSelf {
r, width = utf8.DecodeRuneInString(s)
}
if width == 1 && r == utf8.RuneError {
buf = append(buf, `\x`...)
buf = append(buf, lowerhex[s[0]>>4])
buf = append(buf, lowerhex[s[0]&0xF])
continue
}
buf = appendEscapedRune(buf, r)
}
buf = append(buf, quote)
return string(buf)
}
func appendEscapedRune(buf []byte, r rune) []byte {
const quote = '"'
var runeTmp [utf8.UTFMax]byte
if r == rune(quote) || r == '\\' { // always backslashed
buf = append(buf, '\\')
buf = append(buf, byte(r))
return buf
}
if isPrint(r) {
n := utf8.EncodeRune(runeTmp[:], r)
buf = append(buf, runeTmp[:n]...)
return buf
}
switch r {
case '\a':
buf = append(buf, `\a`...)
case '\b':
buf = append(buf, `\b`...)
case '\f':
buf = append(buf, `\f`...)
case '\n':
buf = append(buf, `\n`...)
case '\r':
buf = append(buf, `\r`...)
case '\t':
buf = append(buf, `\t`...)
case '\v':
buf = append(buf, `\v`...)
default:
switch {
case r < ' ' || r == 0x7f:
buf = append(buf, `\x`...)
buf = append(buf, lowerhex[byte(r)>>4])
buf = append(buf, lowerhex[byte(r)&0xF])
case !utf8.ValidRune(r):
r = 0xFFFD
fallthrough
case r < 0x10000:
buf = append(buf, `\u`...)
for s := 12; s >= 0; s -= 4 {
buf = append(buf, lowerhex[r>>uint(s)&0xF])
}
default:
buf = append(buf, `\U`...)
for s := 28; s >= 0; s -= 4 {
buf = append(buf, lowerhex[r>>uint(s)&0xF])
}
}
}
return buf
}
// This is only used for struct tags. Assume
func isPrint(r rune) bool {
if r <= 0xFF {
if 0x20 <= r && r <= 0x7E {
// All the ASCII is printable from space through DEL-1.
return true
}
if 0xA1 <= r && r <= 0xFF {
// Similarly for ¡ through ÿ...
return r != 0xAD // ...except for the bizarre soft hyphen.
}
return false
}
// TinyGo: Skip all other unicode processing
return false
}
// contains reports whether the string contains the byte c.
func contains(s string, c byte) bool {
return indexByteString(s, c) != -1