package html
import (
- "bytes"
"strings"
"unicode/utf8"
)
return b
}
-const escapedChars = `&'<>"`
-
-func escape(w writer, s string) error {
- i := strings.IndexAny(s, escapedChars)
- for i != -1 {
- if _, err := w.WriteString(s[:i]); err != nil {
- return err
- }
- var esc string
- switch s[i] {
- case '&':
- esc = "&"
- case '\'':
- // "'" is shorter than "'" and apos was not in HTML until HTML5.
- esc = "'"
- case '<':
- esc = "<"
- case '>':
- esc = ">"
- case '"':
- // """ is shorter than """.
- esc = """
- default:
- panic("unrecognized escape character")
- }
- s = s[i+1:]
- if _, err := w.WriteString(esc); err != nil {
- return err
- }
- i = strings.IndexAny(s, escapedChars)
- }
- _, err := w.WriteString(s)
- return err
-}
+var htmlEscaper = strings.NewReplacer(
+ `&`, "&",
+ `'`, "'", // "'" is shorter than "'" and apos was not in HTML until HTML5.
+ `<`, "<",
+ `>`, ">",
+ `"`, """, // """ is shorter than """.
+)
// EscapeString escapes special characters like "<" to become "<". It
// escapes only five such characters: <, >, &, ' and ".
// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't
// always true.
func EscapeString(s string) string {
- if strings.IndexAny(s, escapedChars) == -1 {
- return s
- }
- var buf bytes.Buffer
- escape(&buf, s)
- return buf.String()
+ return htmlEscaper.Replace(s)
}
// UnescapeString unescapes entities like "<" to become "<". It unescapes a
// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't
// always true.
func UnescapeString(s string) string {
- for _, c := range s {
- if c == '&' {
- return string(unescape([]byte(s)))
- }
+ if !strings.Contains(s, "&") {
+ return s
}
- return s
+ return string(unescape([]byte(s)))
}
package html
-import "testing"
+import (
+ "strings"
+ "testing"
+)
type unescapeTest struct {
// A short description of the test case.
}
}
}
+
+var (
+ benchEscapeData = strings.Repeat("AAAAA < BBBBB > CCCCC & DDDDD ' EEEEE \" ", 100)
+ benchEscapeNone = strings.Repeat("AAAAA x BBBBB x CCCCC x DDDDD x EEEEE x ", 100)
+)
+
+func BenchmarkEscape(b *testing.B) {
+ n := 0
+ for i := 0; i < b.N; i++ {
+ n += len(EscapeString(benchEscapeData))
+ }
+}
+
+func BenchmarkEscapeNone(b *testing.B) {
+ n := 0
+ for i := 0; i < b.N; i++ {
+ n += len(EscapeString(benchEscapeNone))
+ }
+}
+
+func BenchmarkUnescape(b *testing.B) {
+ s := EscapeString(benchEscapeData)
+ n := 0
+ for i := 0; i < b.N; i++ {
+ n += len(UnescapeString(s))
+ }
+}
+
+func BenchmarkUnescapeNone(b *testing.B) {
+ s := EscapeString(benchEscapeNone)
+ n := 0
+ for i := 0; i < b.N; i++ {
+ n += len(UnescapeString(s))
+ }
+}