aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/aws/smithy-go/encoding/xml/escape.go
blob: 1c5479af677d5c996cdc96b55387b03157e89ae1 (plain) (blame)
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Copied and modified from Go 1.14 stdlib's encoding/xml

package xml

import (
	"unicode/utf8"
)

// Copied from Go 1.14 stdlib's encoding/xml
var (
	escQuot = []byte(""") // shorter than """
	escApos = []byte("'") // shorter than "'"
	escAmp  = []byte("&")
	escLT   = []byte("<")
	escGT   = []byte(">")
	escTab  = []byte("	")
	escNL   = []byte("
")
	escCR   = []byte("
")
	escFFFD = []byte("\uFFFD") // Unicode replacement character

	// Additional Escapes
	escNextLine = []byte("…")
	escLS       = []byte("
")
)

// Decide whether the given rune is in the XML Character Range, per
// the Char production of https://www.xml.com/axml/testaxml.htm,
// Section 2.2 Characters.
func isInCharacterRange(r rune) (inrange bool) {
	return r == 0x09 ||
		r == 0x0A ||
		r == 0x0D ||
		r >= 0x20 && r <= 0xD7FF ||
		r >= 0xE000 && r <= 0xFFFD ||
		r >= 0x10000 && r <= 0x10FFFF
}

// TODO: When do we need to escape the string?
// Based on encoding/xml escapeString from the Go Standard Library.
// https://golang.org/src/encoding/xml/xml.go
func escapeString(e writer, s string) {
	var esc []byte
	last := 0
	for i := 0; i < len(s); {
		r, width := utf8.DecodeRuneInString(s[i:])
		i += width
		switch r {
		case '"':
			esc = escQuot
		case '\'':
			esc = escApos
		case '&':
			esc = escAmp
		case '<':
			esc = escLT
		case '>':
			esc = escGT
		case '\t':
			esc = escTab
		case '\n':
			esc = escNL
		case '\r':
			esc = escCR
		case '\u0085':
			// Not escaped by stdlib
			esc = escNextLine
		case '\u2028':
			// Not escaped by stdlib
			esc = escLS
		default:
			if !isInCharacterRange(r) || (r == 0xFFFD && width == 1) {
				esc = escFFFD
				break
			}
			continue
		}
		e.WriteString(s[last : i-width])
		e.Write(esc)
		last = i
	}
	e.WriteString(s[last:])
}

// escapeText writes to w the properly escaped XML equivalent
// of the plain text data s. If escapeNewline is true, newline
// characters will be escaped.
//
// Based on encoding/xml escapeText from the Go Standard Library.
// https://golang.org/src/encoding/xml/xml.go
func escapeText(e writer, s []byte) {
	var esc []byte
	last := 0
	for i := 0; i < len(s); {
		r, width := utf8.DecodeRune(s[i:])
		i += width
		switch r {
		case '"':
			esc = escQuot
		case '\'':
			esc = escApos
		case '&':
			esc = escAmp
		case '<':
			esc = escLT
		case '>':
			esc = escGT
		case '\t':
			esc = escTab
		case '\n':
			// This always escapes newline, which is different than stdlib's optional
			// escape of new line.
			esc = escNL
		case '\r':
			esc = escCR
		case '\u0085':
			// Not escaped by stdlib
			esc = escNextLine
		case '\u2028':
			// Not escaped by stdlib
			esc = escLS
		default:
			if !isInCharacterRange(r) || (r == 0xFFFD && width == 1) {
				esc = escFFFD
				break
			}
			continue
		}
		e.Write(s[last : i-width])
		e.Write(esc)
		last = i
	}
	e.Write(s[last:])
}