1package html
  2
  3var (
  4	singleQuoteEntityBytes = []byte("'")
  5	doubleQuoteEntityBytes = []byte(""")
  6)
  7
  8// EscapeAttrVal returns the escaped attribute value bytes with quotes. Either single or double quotes are used, whichever is shorter. If there are no quotes present in the value and the value is in HTML (not XML), it will return the value without quotes.
  9func EscapeAttrVal(buf *[]byte, b []byte, origQuote byte, mustQuote, isXML bool) []byte {
 10	singles := 0
 11	doubles := 0
 12	unquoted := true
 13	for _, c := range b {
 14		if charTable[c] {
 15			unquoted = false
 16			if c == '"' {
 17				doubles++
 18			} else if c == '\'' {
 19				singles++
 20			}
 21		}
 22	}
 23	if unquoted && (!mustQuote || origQuote == 0) && !isXML {
 24		return b
 25	} else if singles == 0 && origQuote == '\'' && !isXML || doubles == 0 && origQuote == '"' {
 26		if len(b)+2 > cap(*buf) {
 27			*buf = make([]byte, 0, len(b)+2)
 28		}
 29		t := (*buf)[:len(b)+2]
 30		t[0] = origQuote
 31		copy(t[1:], b)
 32		t[1+len(b)] = origQuote
 33		return t
 34	}
 35
 36	n := len(b) + 2
 37	var quote byte
 38	var escapedQuote []byte
 39	if singles >= doubles || isXML {
 40		n += doubles * 4
 41		quote = '"'
 42		escapedQuote = doubleQuoteEntityBytes
 43		if singles == doubles && origQuote == '\'' && !isXML {
 44			quote = '\''
 45			escapedQuote = singleQuoteEntityBytes
 46		}
 47	} else {
 48		n += singles * 4
 49		quote = '\''
 50		escapedQuote = singleQuoteEntityBytes
 51	}
 52	if n > cap(*buf) {
 53		*buf = make([]byte, 0, n) // maximum size, not actual size
 54	}
 55	t := (*buf)[:n] // maximum size, not actual size
 56	t[0] = quote
 57	j := 1
 58	start := 0
 59	for i, c := range b {
 60		if c == quote {
 61			j += copy(t[j:], b[start:i])
 62			j += copy(t[j:], escapedQuote)
 63			start = i + 1
 64		}
 65	}
 66	j += copy(t[j:], b[start:])
 67	t[j] = quote
 68	return t[:j+1]
 69}
 70
 71var charTable = [256]bool{
 72	// ASCII
 73	false, false, false, false, false, false, false, false,
 74	false, true, true, false, true, true, false, false, // tab, line feed, form feed, carriage return
 75	false, false, false, false, false, false, false, false,
 76	false, false, false, false, false, false, false, false,
 77
 78	true, false, true, false, false, false, false, true, // space, "), '
 79	false, false, false, false, false, false, false, false,
 80	false, false, false, false, false, false, false, false,
 81	false, false, false, false, true, true, true, false, // <, =, >
 82
 83	false, false, false, false, false, false, false, false,
 84	false, false, false, false, false, false, false, false,
 85	false, false, false, false, false, false, false, false,
 86	false, false, false, false, false, false, false, false,
 87
 88	true, false, false, false, false, false, false, false, // `
 89	false, false, false, false, false, false, false, false,
 90	false, false, false, false, false, false, false, false,
 91	false, false, false, false, false, false, false, false,
 92
 93	// non-ASCII
 94	false, false, false, false, false, false, false, false,
 95	false, false, false, false, false, false, false, false,
 96	false, false, false, false, false, false, false, false,
 97	false, false, false, false, false, false, false, false,
 98
 99	false, false, false, false, false, false, false, false,
100	false, false, false, false, false, false, false, false,
101	false, false, false, false, false, false, false, false,
102	false, false, false, false, false, false, false, false,
103
104	false, false, false, false, false, false, false, false,
105	false, false, false, false, false, false, false, false,
106	false, false, false, false, false, false, false, false,
107	false, false, false, false, false, false, false, false,
108
109	false, false, false, false, false, false, false, false,
110	false, false, false, false, false, false, false, false,
111	false, false, false, false, false, false, false, false,
112	false, false, false, false, false, false, false, false,
113}