webdav: have escapeXML perform fewer allocations.
escapeXML was introduced in the previous commit:
https://go-review.googlesource.com/29297
Change-Id: I7d0c982049e495b312b1b8d28ba794444dd605d4
Reviewed-on: https://go-review.googlesource.com/32370
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/webdav/prop.go b/webdav/prop.go
index 3446871..83fbfa8 100644
--- a/webdav/prop.go
+++ b/webdav/prop.go
@@ -335,9 +335,23 @@
}
func escapeXML(s string) string {
- var buf bytes.Buffer
- xml.EscapeText(&buf, []byte(s))
- return buf.String()
+ for i := 0; i < len(s); i++ {
+ // As an optimization, if s contains only ASCII letters, digits or a
+ // few special characters, the escaped value is s itself and we don't
+ // need to allocate a buffer and convert between string and []byte.
+ switch c := s[i]; {
+ case c == ' ' || c == '_' ||
+ ('+' <= c && c <= '9') || // Digits as well as + , - . and /
+ ('A' <= c && c <= 'Z') ||
+ ('a' <= c && c <= 'z'):
+ continue
+ }
+ // Otherwise, go through the full escaping process.
+ var buf bytes.Buffer
+ xml.EscapeText(&buf, []byte(s))
+ return buf.String()
+ }
+ return s
}
func findResourceType(fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
diff --git a/webdav/webdav_test.go b/webdav/webdav_test.go
index 82605cd..d9f8c8e 100644
--- a/webdav/webdav_test.go
+++ b/webdav/webdav_test.go
@@ -202,6 +202,44 @@
}
}
+func TestEscapeXML(t *testing.T) {
+ // These test cases aren't exhaustive, and there is more than one way to
+ // escape e.g. a quot (as """ or """) or an apos. We presume that
+ // the encoding/xml package tests xml.EscapeText more thoroughly. This test
+ // here is just a sanity check for this package's escapeXML function, and
+ // its attempt to provide a fast path (and avoid a bytes.Buffer allocation)
+ // when escaping filenames is obviously a no-op.
+ testCases := map[string]string{
+ "": "",
+ " ": " ",
+ "&": "&",
+ "*": "*",
+ "+": "+",
+ ",": ",",
+ "-": "-",
+ ".": ".",
+ "/": "/",
+ "0": "0",
+ "9": "9",
+ ":": ":",
+ "<": "<",
+ ">": ">",
+ "A": "A",
+ "_": "_",
+ "a": "a",
+ "~": "~",
+ "\u0201": "\u0201",
+ "&": "&amp;",
+ "foo&<b/ar>baz": "foo&<b/ar>baz",
+ }
+
+ for in, want := range testCases {
+ if got := escapeXML(in); got != want {
+ t.Errorf("in=%q: got %q, want %q", in, got, want)
+ }
+ }
+}
+
func TestFilenameEscape(t *testing.T) {
hrefRe := regexp.MustCompile(`<D:href>([^<]*)</D:href>`)
displayNameRe := regexp.MustCompile(`<D:displayname>([^<]*)</D:displayname>`)