File: jsonstring.go

package info (click to toggle)
golang-github-valyala-quicktemplate 1.8.0%2Bds1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 492 kB
  • sloc: makefile: 16; xml: 15
file content (100 lines) | stat: -rw-r--r-- 2,262 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package quicktemplate

import (
	"bytes"
	"fmt"
	"strings"
)

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
}

// 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 {
	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
	}

	// Slow path - there are chars to escape.
	if addQuotes {
		dst = append(dst, '"')
	}
	dst = jsonReplacer.AppendReplace(dst, s)
	if addQuotes {
		dst = append(dst, '"')
	}
	return dst
}

var jsonReplacer = newByteReplacer(func() ([]byte, []string) {
	oldChars := []byte("\n\r\t\b\f\"\\<'")
	newStrings := []string{`\n`, `\r`, `\t`, `\b`, `\f`, `\"`, `\\`, `\u003c`, `\u0027`}
	for i := 0; i < 0x20; i++ {
		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
	}
	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
}