2019-05-22 21:16:55 +00:00
|
|
|
package quicktemplate
|
|
|
|
|
|
|
|
import (
|
2024-07-04 22:56:39 +00:00
|
|
|
"bytes"
|
2020-07-10 09:56:03 +00:00
|
|
|
"fmt"
|
2019-05-22 21:16:55 +00:00
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2020-07-10 09:56:03 +00:00
|
|
|
func hasSpecialChars(s string) bool {
|
|
|
|
if strings.IndexByte(s, '"') >= 0 || strings.IndexByte(s, '\\') >= 0 || strings.IndexByte(s, '<') >= 0 || strings.IndexByte(s, '\'') >= 0 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
for i := 0; i < len(s); i++ {
|
|
|
|
if s[i] < 0x20 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2019-05-22 21:16:55 +00:00
|
|
|
|
2024-07-04 22:56:39 +00:00
|
|
|
// AppendJSONString appends json-encoded string s to dst and returns the result.
|
|
|
|
//
|
|
|
|
// If addQuotes is true, then the appended json string is wrapped into double quotes.
|
|
|
|
func AppendJSONString(dst []byte, s string, addQuotes bool) []byte {
|
2020-07-10 09:56:03 +00:00
|
|
|
if !hasSpecialChars(s) {
|
|
|
|
// Fast path - nothing to escape.
|
|
|
|
if !addQuotes {
|
|
|
|
return append(dst, s...)
|
|
|
|
}
|
|
|
|
dst = append(dst, '"')
|
|
|
|
dst = append(dst, s...)
|
|
|
|
dst = append(dst, '"')
|
|
|
|
return dst
|
2019-05-22 21:16:55 +00:00
|
|
|
}
|
|
|
|
|
2020-07-10 09:56:03 +00:00
|
|
|
// Slow path - there are chars to escape.
|
|
|
|
if addQuotes {
|
|
|
|
dst = append(dst, '"')
|
2019-05-22 21:16:55 +00:00
|
|
|
}
|
2024-07-04 22:56:39 +00:00
|
|
|
dst = jsonReplacer.AppendReplace(dst, s)
|
2020-07-10 09:56:03 +00:00
|
|
|
if addQuotes {
|
|
|
|
dst = append(dst, '"')
|
2019-05-22 21:16:55 +00:00
|
|
|
}
|
2020-07-10 09:56:03 +00:00
|
|
|
return dst
|
2019-05-22 21:16:55 +00:00
|
|
|
}
|
|
|
|
|
2024-07-04 22:56:39 +00:00
|
|
|
var jsonReplacer = newByteReplacer(func() ([]byte, []string) {
|
|
|
|
oldChars := []byte("\n\r\t\b\f\"\\<'")
|
|
|
|
newStrings := []string{`\n`, `\r`, `\t`, `\b`, `\f`, `\"`, `\\`, `\u003c`, `\u0027`}
|
2020-07-10 09:56:03 +00:00
|
|
|
for i := 0; i < 0x20; i++ {
|
2024-07-04 22:56:39 +00:00
|
|
|
c := byte(i)
|
|
|
|
if n := bytes.IndexByte(oldChars, c); n >= 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
oldChars = append(oldChars, byte(i))
|
|
|
|
newStrings = append(newStrings, fmt.Sprintf(`\u%04x`, i))
|
|
|
|
}
|
|
|
|
return oldChars, newStrings
|
|
|
|
}())
|
|
|
|
|
|
|
|
type byteReplacer struct {
|
|
|
|
m [256]byte
|
|
|
|
newStrings []string
|
|
|
|
}
|
|
|
|
|
|
|
|
func newByteReplacer(oldChars []byte, newStrings []string) *byteReplacer {
|
|
|
|
if len(oldChars) != len(newStrings) {
|
|
|
|
panic(fmt.Errorf("len(oldChars)=%d must be equal to len(newStrings)=%d", len(oldChars), len(newStrings)))
|
|
|
|
}
|
|
|
|
if len(oldChars) >= 255 {
|
|
|
|
panic(fmt.Errorf("len(oldChars)=%d must be smaller than 255", len(oldChars)))
|
|
|
|
}
|
|
|
|
|
|
|
|
var m [256]byte
|
|
|
|
for i := range m[:] {
|
|
|
|
m[i] = 255
|
2020-07-10 09:56:03 +00:00
|
|
|
}
|
2024-07-04 22:56:39 +00:00
|
|
|
for i, c := range oldChars {
|
|
|
|
m[c] = byte(i)
|
|
|
|
}
|
|
|
|
return &byteReplacer{
|
|
|
|
m: m,
|
|
|
|
newStrings: newStrings,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (br *byteReplacer) AppendReplace(dst []byte, s string) []byte {
|
|
|
|
m := br.m
|
|
|
|
newStrings := br.newStrings
|
|
|
|
for i := 0; i < len(s); i++ {
|
|
|
|
c := s[i]
|
|
|
|
n := m[c]
|
|
|
|
if n == 255 {
|
|
|
|
dst = append(dst, c)
|
|
|
|
} else {
|
|
|
|
dst = append(dst, newStrings[n]...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dst
|
|
|
|
}
|