]> Cypherpunks repositories - gostls13.git/commitdiff
exp/html/atom: faster Lookup with smaller tables
authorRuss Cox <rsc@golang.org>
Sun, 3 Jun 2012 02:43:11 +0000 (22:43 -0400)
committerRuss Cox <rsc@golang.org>
Sun, 3 Jun 2012 02:43:11 +0000 (22:43 -0400)
Use perfect cuckoo hash, to avoid binary search.
Define Atom bits as offset+len in long string instead
of enumeration, to avoid string headers.

Before: 1909 string bytes + 6060 tables = 7969 total data
After: 1406 string bytes + 2048 tables = 3454 total data

benchmark          old ns/op    new ns/op    delta
BenchmarkLookup        83878        64681  -22.89%

R=nigeltao, r
CC=golang-dev
https://golang.org/cl/6262051

src/pkg/exp/html/atom/atom.go
src/pkg/exp/html/atom/atom_test.go
src/pkg/exp/html/atom/gen.go
src/pkg/exp/html/atom/table.go
src/pkg/exp/html/atom/table_test.go [new file with mode: 0644]

index b67428066d030afedd98ae305a63f1e1092abf32..cc53ec401649ab5393a8c1eb20132e7cfc278b2f 100644 (file)
 // whether atom.H1 < atom.H2 may also change. The codes are not guaranteed to
 // be dense. The only guarantees are that e.g. looking up "div" will yield
 // atom.Div, calling atom.Div.String will return "div", and atom.Div != 0.
+//
+// TODO(rsc): When this package moves out of exp we need to freeze atom values
+// across releases.
 package atom
 
-// The hash function must be the same as the one used in gen.go
-func hash(s []byte) (h uint32) {
-       for i := 0; i < len(s); i++ {
-               h = h<<5 ^ h>>27 ^ uint32(s[i])
-       }
-       return h
-}
-
 // Atom is an integer code for a string. The zero value maps to "".
-type Atom int
+type Atom uint32
 
 // String returns the atom's name.
 func (a Atom) String() string {
-       if 0 <= a && a < Atom(len(table)) {
-               return table[a]
+       start := uint32(a >> 8)
+       n := uint32(a & 0xff)
+       if start+n > uint32(len(atomText)) {
+               return ""
        }
-       return ""
+       return atomText[start : start+n]
+}
+
+func (a Atom) string() string {
+       return atomText[a>>8 : a>>8+a&0xff]
+}
+
+// fnv computes the FNV hash with an arbitrary starting value h.
+func fnv(h uint32, s []byte) uint32 {
+       for i := range s {
+               h ^= uint32(s[i])
+               h *= 16777619
+       }
+       return h
+}
+
+func match(s string, t []byte) bool {
+       for i, c := range t {
+               if s[i] != c {
+                       return false
+               }
+       }
+       return true
 }
 
 // Lookup returns the atom whose name is s. It returns zero if there is no
 // such atom.
 func Lookup(s []byte) Atom {
-       if len(s) == 0 || len(s) > maxLen {
+       if len(s) == 0 || len(s) > maxAtomLen {
                return 0
        }
-       if len(s) == 1 {
-               x := s[0]
-               if x < 'a' || x > 'z' {
-                       return 0
-               }
-               return oneByteAtoms[x-'a']
+       h := fnv(hash0, s)
+       if a := table[h&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) {
+               return a
        }
-       hs := hash(s)
-       // Binary search for hs. Unlike sort.Search, this returns early on an exact match.
-       // A loop invariant is that len(table[i]) == len(s) for all i in [lo, hi).
-       lo := Atom(loHi[len(s)])
-       hi := Atom(loHi[len(s)+1])
-       for lo < hi {
-               mid := (lo + hi) / 2
-               if ht := hashes[mid]; hs == ht {
-                       // The gen.go program ensures that each atom's name has a distinct hash.
-                       // However, arbitrary strings may collide with the atom's name. We have
-                       // to check that string(s) == table[mid].
-                       t := table[mid]
-                       for i, si := range s {
-                               if si != t[i] {
-                                       return 0
-                               }
-                       }
-                       return mid
-               } else if hs > ht {
-                       lo = mid + 1
-               } else {
-                       hi = mid
-               }
+       if a := table[(h>>16)&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) {
+               return a
        }
        return 0
 }
 
 // String returns a string whose contents are equal to s. In that sense, it is
-// equivalent to string(s), but may be more efficient.
+// equivalent to string(s) but may be more efficient.
 func String(s []byte) string {
        if a := Lookup(s); a != 0 {
                return a.String()
index 9b0726899bff685852ebfc12ea7bda5340e7cebb..e0cae2db8d03432fa924e203de72f69dc1c0b9c4 100644 (file)
@@ -9,11 +9,22 @@ import (
        "testing"
 )
 
+func TestKnown(t *testing.T) {
+       for _, s := range testAtomList {
+               if atom := Lookup([]byte(s)); atom.String() != s {
+                       t.Errorf("Lookup(%q) = %#x (%q)", s, uint32(atom), atom.String())
+               }
+       }
+}
+
 func TestHits(t *testing.T) {
-       for i, s := range table {
-               got := Lookup([]byte(s))
-               if got != Atom(i) {
-                       t.Errorf("Lookup(%q): got %d, want %d", s, got, i)
+       for _, a := range table {
+               if a == 0 {
+                       continue
+               }
+               got := Lookup([]byte(a.String()))
+               if got != a {
+                       t.Errorf("Lookup(%q) = %#x, want %#x", a.String(), uint32(got), uint32(a))
                }
        }
 }
@@ -55,8 +66,12 @@ func TestMisses(t *testing.T) {
 }
 
 func BenchmarkLookup(b *testing.B) {
-       sortedTable := make([]string, len(table))
-       copy(sortedTable, table[:])
+       sortedTable := make([]string, 0, len(table))
+       for _, a := range table {
+               if a != 0 {
+                       sortedTable = append(sortedTable, a.String())
+               }
+       }
        sort.Strings(sortedTable)
 
        x := make([][]byte, 1000)
index fc4407e0f811c584fc7830f0f667a44b5a5486db..4adb44073c5d51597e3539b873bef694a0da94fe 100644 (file)
@@ -6,37 +6,21 @@
 
 package main
 
-// This program generates table.go
+// This program generates table.go and table_test.go.
 // Invoke as
 //
 //     go run gen.go |gofmt >table.go
+//     go run gen.go -test |gofmt >table_test.go
 
 import (
+       "flag"
        "fmt"
+       "math/rand"
        "os"
        "sort"
+       "strings"
 )
 
-// The hash function must be the same as the one used in atom.go
-func hash(s string) (h uint32) {
-       for i := 0; i < len(s); i++ {
-               h = h<<5 ^ h>>27 ^ uint32(s[i])
-       }
-       return h
-}
-
-// lhash returns a uint64 whose high 32 bits are len(s) and whose low 32 bits
-// are hash(s).
-func lhash(s string) uint64 {
-       return uint64(len(s))<<32 | uint64(hash(s))
-}
-
-type byLhash []string
-
-func (b byLhash) Len() int           { return len(b) }
-func (b byLhash) Less(i, j int) bool { return lhash(b[i]) < lhash(b[j]) }
-func (b byLhash) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
-
 // identifier converts s to a Go exported identifier.
 // It converts "div" to "Div" and "accept-charset" to "AcceptCharset".
 func identifier(s string) string {
@@ -56,94 +40,247 @@ func identifier(s string) string {
        return string(b)
 }
 
+var test = flag.Bool("test", false, "generate table_test.go")
+
 func main() {
-       // Construct a list of atoms, sorted by their lhash.
-       m0 := map[string]bool{
-               "": true,
-       }
-       for _, list := range [][]string{elements, attributes, eventHandlers, extra} {
-               for _, s := range list {
-                       m0[s] = true
+       flag.Parse()
+
+       var all []string
+       all = append(all, elements...)
+       all = append(all, attributes...)
+       all = append(all, eventHandlers...)
+       all = append(all, extra...)
+       sort.Strings(all)
+
+       if *test {
+               fmt.Printf("// generated by go run gen.go -test; DO NOT EDIT\n\n")
+               fmt.Printf("package atom\n\n")
+               fmt.Printf("var testAtomList = []string{\n")
+               for _, s := range all {
+                       fmt.Printf("\t%q,\n", s)
                }
+               fmt.Printf("}\n")
+               return
        }
-       atoms := make([]string, 0, len(m0))
-       for s := range m0 {
-               atoms = append(atoms, s)
-       }
-       sort.Sort(byLhash(atoms))
 
-       // Calculate the magic constants to output as table.go.
-       byInt := []string{}
-       byStr := map[string]int{}
-       ident := []string{}
-       lhashes := []uint64{}
+       // uniq - lists have dups
+       // compute max len too
        maxLen := 0
-       for i, s := range atoms {
-               byInt = append(byInt, s)
-               byStr[s] = i
-               ident = append(ident, identifier(s))
-               lhashes = append(lhashes, lhash(s))
-               if maxLen < len(s) {
-                       maxLen = len(s)
+       w := 0
+       for _, s := range all {
+               if w == 0 || all[w-1] != s {
+                       if maxLen < len(s) {
+                               maxLen = len(s)
+                       }
+                       all[w] = s
+                       w++
                }
        }
+       all = all[:w]
 
-       // Check for hash collisions.
-       m1 := map[uint64]int{}
-       for i, h := range lhashes {
-               h &= 1<<32 - 1
-               if j, ok := m1[h]; ok {
-                       fmt.Fprintf(os.Stderr, "hash collision at 0x%08x: %q, %q\n", h, byInt[i], byInt[j])
-                       os.Exit(1)
+       // Find hash that minimizes table size.
+       var best *table
+       for i := 0; i < 1000000; i++ {
+               if best != nil && 1<<(best.k-1) < len(all) {
+                       break
                }
-               m1[h] = i
+               h := rand.Uint32()
+               for k := uint(0); k <= 16; k++ {
+                       if best != nil && k >= best.k {
+                               break
+                       }
+                       var t table
+                       if t.init(h, k, all) {
+                               best = &t
+                               break
+                       }
+               }
+       }
+       if best == nil {
+               fmt.Fprintf(os.Stderr, "failed to construct string table\n")
+               os.Exit(1)
        }
 
-       // Generate the Go code.
-       fmt.Printf("package atom\n\nconst (\n")
-       {
-               // Print the Atoms in alphabetical order.
-               lines := []string{}
-               for i, _ := range byInt {
-                       if i == 0 {
+       // Lay out strings, using overlaps when possible.
+       layout := append([]string{}, all...)
+
+       // Remove strings that are substrings of other strings
+       for changed := true; changed; {
+               changed = false
+               for i, s := range layout {
+                       if s == "" {
+                               continue
+                       }
+                       for j, t := range layout {
+                               if i != j && t != "" && strings.Contains(s, t) {
+                                       changed = true
+                                       layout[j] = ""
+                               }
+                       }
+               }
+       }
+
+       // Join strings where one suffix matches another prefix.
+       for {
+               // Find best i, j, k such that layout[i][len-k:] == layout[j][:k],
+               // maximizing overlap length k.
+               besti := -1
+               bestj := -1
+               bestk := 0
+               for i, s := range layout {
+                       if s == "" {
                                continue
                        }
-                       lines = append(lines, fmt.Sprintf("\t%s Atom = %d", ident[i], i))
+                       for j, t := range layout {
+                               if i == j {
+                                       continue
+                               }
+                               for k := bestk + 1; k <= len(s) && k <= len(t); k++ {
+                                       if s[len(s)-k:] == t[:k] {
+                                               besti = i
+                                               bestj = j
+                                               bestk = k
+                                       }
+                               }
+                       }
                }
-               sort.Strings(lines)
-               for _, line := range lines {
-                       fmt.Println(line)
+               if bestk > 0 {
+                       layout[besti] += layout[bestj][bestk:]
+                       layout[bestj] = ""
+                       continue
+               }
+               break
+       }
+
+       text := strings.Join(layout, "")
+
+       atom := map[string]uint32{}
+       for _, s := range all {
+               off := strings.Index(text, s)
+               if off < 0 {
+                       panic("lost string " + s)
                }
-               fmt.Printf(")\n\n")
+               atom[s] = uint32(off<<8 | len(s))
        }
-       fmt.Printf("const maxLen = %d\n\n", maxLen)
-       fmt.Printf("var table = [...]string{\n")
-       for _, s := range byInt {
-               fmt.Printf("\t%q,\n", s)
+
+       // Generate the Go code.
+       fmt.Printf("// generated by go run gen.go; DO NOT EDIT\n\n")
+       fmt.Printf("package atom\n\nconst (\n")
+       for _, s := range all {
+               fmt.Printf("\t%s Atom = %#x\n", identifier(s), atom[s])
        }
-       fmt.Printf("}\n\n")
-       fmt.Printf("var hashes = [...]uint32{\n")
-       for _, s := range byInt {
-               fmt.Printf("\t0x%08x,\n", hash(s))
+       fmt.Printf(")\n\n")
+
+       fmt.Printf("const hash0 = %#x\n\n", best.h0)
+       fmt.Printf("const maxAtomLen = %d\n\n", maxLen)
+
+       fmt.Printf("var table = [1<<%d]Atom{\n", best.k)
+       for i, s := range best.tab {
+               if s == "" {
+                       continue
+               }
+               fmt.Printf("\t%#x: %#x, // %s\n", i, atom[s], s)
        }
-       fmt.Printf("}\n\n")
-       fmt.Printf("var loHi = [maxLen + 2]uint16{\n")
-       for n := 0; n <= maxLen; n++ {
-               fmt.Printf("\t%d,\n", sort.Search(len(byInt), func(i int) bool {
-                       return int(lhashes[i]>>32) >= n
-               }))
+       fmt.Printf("}\n")
+       datasize := (1 << best.k) * 4
+
+       fmt.Printf("const atomText =\n")
+       textsize := len(text)
+       for len(text) > 60 {
+               fmt.Printf("\t%q +\n", text[:60])
+               text = text[60:]
        }
-       fmt.Printf("\t%d,\n", len(byInt))
-       fmt.Printf("}\n\n")
-       fmt.Printf("var oneByteAtoms = [26]Atom{\n")
-       for i := 'a'; i <= 'z'; i++ {
-               val := "0"
-               if x := byStr[string(i)]; x != 0 {
-                       val = ident[x]
+       fmt.Printf("\t%q\n\n", text)
+
+       fmt.Fprintf(os.Stderr, "%d atoms; %d string bytes + %d tables = %d total data\n", len(all), textsize, datasize, textsize+datasize)
+}
+
+type byLen []string
+
+func (x byLen) Less(i, j int) bool { return len(x[i]) > len(x[j]) }
+func (x byLen) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
+func (x byLen) Len() int           { return len(x) }
+
+// fnv computes the FNV hash with an arbitrary starting value h.
+func fnv(h uint32, s string) uint32 {
+       for i := 0; i < len(s); i++ {
+               h ^= uint32(s[i])
+               h *= 16777619
+       }
+       return h
+}
+
+// A table represents an attempt at constructing the lookup table.
+// The lookup table uses cuckoo hashing, meaning that each string
+// can be found in one of two positions.
+type table struct {
+       h0   uint32
+       k    uint
+       mask uint32
+       tab  []string
+}
+
+// hash returns the two hashes for s.
+func (t *table) hash(s string) (h1, h2 uint32) {
+       h := fnv(t.h0, s)
+       h1 = h & t.mask
+       h2 = (h >> 16) & t.mask
+       return
+}
+
+// init initializes the table with the given parameters.
+// h0 is the initial hash value,
+// k is the number of bits of hash value to use, and
+// x is the list of strings to store in the table.
+// init returns false if the table cannot be constructed.
+func (t *table) init(h0 uint32, k uint, x []string) bool {
+       t.h0 = h0
+       t.k = k
+       t.tab = make([]string, 1<<k)
+       t.mask = 1<<k - 1
+       for _, s := range x {
+               if !t.insert(s) {
+                       return false
                }
-               fmt.Printf("\t%s,\n", val)
        }
-       fmt.Printf("}\n\n")
+       return true
+}
+
+// insert inserts s in the table.
+func (t *table) insert(s string) bool {
+       h1, h2 := t.hash(s)
+       if t.tab[h1] == "" {
+               t.tab[h1] = s
+               return true
+       }
+       if t.tab[h2] == "" {
+               t.tab[h2] = s
+               return true
+       }
+       if t.push(h1, 0) {
+               t.tab[h1] = s
+               return true
+       }
+       if t.push(h2, 0) {
+               t.tab[h2] = s
+               return true
+       }
+       return false
+}
+
+// push attempts to push aside the entry in slot i.
+func (t *table) push(i uint32, depth int) bool {
+       if depth > len(t.tab) {
+               return false
+       }
+       s := t.tab[i]
+       h1, h2 := t.hash(s)
+       j := h1 + h2 - i
+       if t.tab[j] != "" && !t.push(j, depth+1) {
+               return false
+       }
+       t.tab[j] = s
+       return true
 }
 
 // The lists of element names and attribute keys were taken from
index 27cd7d18b44eebaadfa97d244cb71ef05819db3e..05f05f51056826ee9c5637dd035ad20857c5c1c4 100644 (file)
+// generated by go run gen.go; DO NOT EDIT
+
 package atom
 
 const (
-       A                Atom = 1
-       Abbr             Atom = 58
-       Accept           Atom = 126
-       AcceptCharset    Atom = 288
-       Accesskey        Atom = 230
-       Action           Atom = 127
-       Address          Atom = 182
-       Align            Atom = 91
-       Alt              Atom = 32
-       Annotation       Atom = 251
-       Applet           Atom = 128
-       Area             Atom = 59
-       Article          Atom = 184
-       Aside            Atom = 92
-       Async            Atom = 93
-       Audio            Atom = 94
-       Autocomplete     Atom = 278
-       Autofocus        Atom = 243
-       Autoplay         Atom = 221
-       B                Atom = 2
-       Base             Atom = 56
-       Bdi              Atom = 30
-       Bdo              Atom = 31
-       Blockquote       Atom = 248
-       Body             Atom = 57
-       Border           Atom = 124
-       Br               Atom = 8
-       Button           Atom = 125
-       Canvas           Atom = 121
-       Caption          Atom = 160
-       Center           Atom = 122
-       Challenge        Atom = 242
-       Charset          Atom = 163
-       Checked          Atom = 164
-       Cite             Atom = 53
-       Class            Atom = 90
-       Code             Atom = 54
-       Col              Atom = 29
-       Colgroup         Atom = 193
-       Color            Atom = 89
-       Cols             Atom = 55
-       Colspan          Atom = 167
-       Command          Atom = 166
-       Content          Atom = 165
-       Contenteditable  Atom = 292
-       Contextmenu      Atom = 277
-       Controls         Atom = 192
-       Coords           Atom = 123
-       Crossorigin      Atom = 266
-       Data             Atom = 62
-       Datalist         Atom = 219
-       Datetime         Atom = 220
-       Dd               Atom = 10
-       Default          Atom = 188
-       Defer            Atom = 97
-       Del              Atom = 35
-       Details          Atom = 189
-       Dfn              Atom = 34
-       Dialog           Atom = 131
-       Dir              Atom = 36
-       Dirname          Atom = 190
-       Disabled         Atom = 216
-       Div              Atom = 37
-       Dl               Atom = 11
-       Download         Atom = 195
-       Draggable        Atom = 235
-       Dropzone         Atom = 202
-       Dt               Atom = 12
-       Em               Atom = 9
-       Embed            Atom = 96
-       Enctype          Atom = 183
-       Fieldset         Atom = 212
-       Figcaption       Atom = 247
-       Figure           Atom = 129
-       Font             Atom = 60
-       Footer           Atom = 130
-       For              Atom = 33
-       Form             Atom = 61
-       Formaction       Atom = 256
-       Formenctype      Atom = 273
-       Formmethod       Atom = 261
-       Formnovalidate   Atom = 289
-       Formtarget       Atom = 263
-       Frame            Atom = 95
-       Frameset         Atom = 198
-       H1               Atom = 13
-       H2               Atom = 14
-       H3               Atom = 15
-       H4               Atom = 16
-       H5               Atom = 17
-       H6               Atom = 18
-       Head             Atom = 65
-       Header           Atom = 136
-       Headers          Atom = 187
-       Height           Atom = 137
-       Hgroup           Atom = 135
-       Hidden           Atom = 138
-       High             Atom = 66
-       Hr               Atom = 20
-       Href             Atom = 67
-       Hreflang         Atom = 199
-       Html             Atom = 68
-       HttpEquiv        Atom = 254
-       I                Atom = 3
-       Icon             Atom = 64
-       Id               Atom = 19
-       Iframe           Atom = 133
-       Img              Atom = 40
-       Inert            Atom = 98
-       Input            Atom = 99
-       Ins              Atom = 39
-       Ismap            Atom = 100
-       Itemid           Atom = 134
-       Itemprop         Atom = 223
-       Itemref          Atom = 185
-       Itemscope        Atom = 240
-       Itemtype         Atom = 224
-       Kbd              Atom = 38
-       Keygen           Atom = 132
-       Keytype          Atom = 162
-       Kind             Atom = 63
-       Label            Atom = 104
-       Lang             Atom = 75
-       Legend           Atom = 149
-       Li               Atom = 22
-       Link             Atom = 76
-       List             Atom = 77
-       Loop             Atom = 78
-       Low              Atom = 45
-       Manifest         Atom = 213
-       Map              Atom = 42
-       Mark             Atom = 72
-       Max              Atom = 43
-       Maxlength        Atom = 245
-       Media            Atom = 101
-       Mediagroup       Atom = 257
-       Menu             Atom = 73
-       Meta             Atom = 74
-       Meter            Atom = 102
-       Method           Atom = 148
-       Min              Atom = 44
-       Multiple         Atom = 215
-       Muted            Atom = 103
-       Name             Atom = 70
-       Nav              Atom = 41
-       Nobr             Atom = 71
-       Noscript         Atom = 194
-       Novalidate       Atom = 262
-       Object           Atom = 139
-       Ol               Atom = 21
-       Onabort          Atom = 170
-       Onafterprint     Atom = 280
-       Onbeforeprint    Atom = 287
-       Onbeforeunload   Atom = 291
-       Onblur           Atom = 140
-       Oncancel         Atom = 196
-       Oncanplay        Atom = 227
-       Oncanplaythrough Atom = 295
-       Onchange         Atom = 197
-       Onclick          Atom = 168
-       Onclose          Atom = 169
-       Oncontextmenu    Atom = 286
-       Oncuechange      Atom = 267
-       Ondblclick       Atom = 255
-       Ondrag           Atom = 141
-       Ondragend        Atom = 246
-       Ondragenter      Atom = 270
-       Ondragleave      Atom = 269
-       Ondragover       Atom = 252
-       Ondragstart      Atom = 268
-       Ondrop           Atom = 142
-       Ondurationchange Atom = 293
-       Onemptied        Atom = 241
-       Onended          Atom = 172
-       Onerror          Atom = 173
-       Onfocus          Atom = 171
-       Onhashchange     Atom = 283
-       Oninput          Atom = 175
-       Oninvalid        Atom = 239
-       Onkeydown        Atom = 231
-       Onkeypress       Atom = 264
-       Onkeyup          Atom = 174
-       Onload           Atom = 143
-       Onloadeddata     Atom = 284
-       Onloadedmetadata Atom = 294
-       Onloadstart      Atom = 271
-       Onmessage        Atom = 236
-       Onmousedown      Atom = 274
-       Onmousemove      Atom = 275
-       Onmouseout       Atom = 250
-       Onmouseover      Atom = 276
-       Onmouseup        Atom = 237
-       Onmousewheel     Atom = 279
-       Onoffline        Atom = 228
-       Ononline         Atom = 201
-       Onpagehide       Atom = 259
-       Onpageshow       Atom = 258
-       Onpause          Atom = 177
-       Onplay           Atom = 145
-       Onplaying        Atom = 244
-       Onpopstate       Atom = 249
-       Onprogress       Atom = 253
-       Onratechange     Atom = 282
-       Onreset          Atom = 176
-       Onresize         Atom = 207
-       Onscroll         Atom = 203
-       Onseeked         Atom = 204
-       Onseeking        Atom = 229
-       Onselect         Atom = 205
-       Onshow           Atom = 144
-       Onstalled        Atom = 233
-       Onstorage        Atom = 234
-       Onsubmit         Atom = 206
-       Onsuspend        Atom = 232
-       Ontimeupdate     Atom = 281
-       Onunload         Atom = 208
-       Onvolumechange   Atom = 290
-       Onwaiting        Atom = 226
-       Open             Atom = 69
-       Optgroup         Atom = 225
-       Optimum          Atom = 179
-       Option           Atom = 146
-       Output           Atom = 147
-       P                Atom = 4
-       Param            Atom = 111
-       Pattern          Atom = 186
-       Ping             Atom = 85
-       Placeholder      Atom = 272
-       Poster           Atom = 156
-       Pre              Atom = 50
-       Preload          Atom = 191
-       Progress         Atom = 200
-       Q                Atom = 5
-       Radiogroup       Atom = 260
-       Readonly         Atom = 210
-       Rel              Atom = 49
-       Required         Atom = 217
-       Reversed         Atom = 218
-       Rows             Atom = 83
-       Rowspan          Atom = 181
-       Rp               Atom = 23
-       Rt               Atom = 24
-       Ruby             Atom = 84
-       S                Atom = 6
-       Samp             Atom = 79
-       Sandbox          Atom = 159
-       Scope            Atom = 105
-       Scoped           Atom = 150
-       Script           Atom = 151
-       Seamless         Atom = 211
-       Section          Atom = 161
-       Select           Atom = 152
-       Selected         Atom = 214
-       Shape            Atom = 107
-       Size             Atom = 80
-       Sizes            Atom = 106
-       Small            Atom = 108
-       Source           Atom = 153
-       Span             Atom = 81
-       Spellcheck       Atom = 265
-       Src              Atom = 46
-       Srcdoc           Atom = 154
-       Srclang          Atom = 178
-       Start            Atom = 109
-       Step             Atom = 82
-       Strong           Atom = 155
-       Style            Atom = 110
-       Sub              Atom = 47
-       Summary          Atom = 180
-       Sup              Atom = 48
-       Tabindex         Atom = 209
-       Table            Atom = 116
-       Target           Atom = 158
-       Tbody            Atom = 115
-       Td               Atom = 26
-       Textarea         Atom = 222
-       Tfoot            Atom = 117
-       Th               Atom = 27
-       Thead            Atom = 119
-       Time             Atom = 87
-       Title            Atom = 118
-       Tr               Atom = 28
-       Track            Atom = 120
-       Translate        Atom = 238
-       Type             Atom = 88
-       Typemustmatch    Atom = 285
-       U                Atom = 7
-       Ul               Atom = 25
-       Usemap           Atom = 157
-       Value            Atom = 113
-       Var              Atom = 52
-       Video            Atom = 114
-       Wbr              Atom = 51
-       Width            Atom = 112
-       Wrap             Atom = 86
+       A                Atom = 0x1
+       Abbr             Atom = 0x4
+       Accept           Atom = 0x3606
+       AcceptCharset    Atom = 0x360e
+       Accesskey        Atom = 0x4809
+       Action           Atom = 0x21506
+       Address          Atom = 0x22507
+       Align            Atom = 0x8605
+       Alt              Atom = 0x8b03
+       Annotation       Atom = 0x16d0a
+       Applet           Atom = 0x2d706
+       Area             Atom = 0xd004
+       Article          Atom = 0x38307
+       Aside            Atom = 0x9f05
+       Async            Atom = 0x9705
+       Audio            Atom = 0xad05
+       Autocomplete     Atom = 0xc20c
+       Autofocus        Atom = 0xd909
+       Autoplay         Atom = 0xe808
+       B                Atom = 0x101
+       Base             Atom = 0xf004
+       Bdi              Atom = 0xbb03
+       Bdo              Atom = 0xfe03
+       Blockquote       Atom = 0x1110a
+       Body             Atom = 0x4404
+       Border           Atom = 0x11b06
+       Br               Atom = 0x202
+       Button           Atom = 0x12106
+       Canvas           Atom = 0x9b06
+       Caption          Atom = 0x1e607
+       Center           Atom = 0x1aa06
+       Challenge        Atom = 0x24409
+       Charset          Atom = 0x3d07
+       Checked          Atom = 0x1ba07
+       Cite             Atom = 0x1d104
+       Class            Atom = 0x13905
+       Code             Atom = 0x14f04
+       Col              Atom = 0x15603
+       Colgroup         Atom = 0x15608
+       Color            Atom = 0x16305
+       Cols             Atom = 0x16804
+       Colspan          Atom = 0x16807
+       Command          Atom = 0x17c07
+       Content          Atom = 0x2b907
+       Contenteditable  Atom = 0x2b90f
+       Contextmenu      Atom = 0x3320b
+       Controls         Atom = 0x19f08
+       Coords           Atom = 0x1b006
+       Crossorigin      Atom = 0x1c10b
+       Data             Atom = 0x40904
+       Datalist         Atom = 0x40908
+       Datetime         Atom = 0x26108
+       Dd               Atom = 0x22602
+       Default          Atom = 0xa207
+       Defer            Atom = 0x15105
+       Del              Atom = 0x49d03
+       Details          Atom = 0x2907
+       Dfn              Atom = 0x5d03
+       Dialog           Atom = 0xbc06
+       Dir              Atom = 0x6703
+       Dirname          Atom = 0x6707
+       Disabled         Atom = 0x1d708
+       Div              Atom = 0x1de03
+       Dl               Atom = 0x18202
+       Download         Atom = 0x3e608
+       Draggable        Atom = 0x19209
+       Dropzone         Atom = 0x38c08
+       Dt               Atom = 0x4ab02
+       Em               Atom = 0x2502
+       Embed            Atom = 0x2505
+       Enctype          Atom = 0x23607
+       Fieldset         Atom = 0x2e308
+       Figcaption       Atom = 0x1e30a
+       Figure           Atom = 0x1f806
+       Font             Atom = 0x20404
+       Footer           Atom = 0x8e06
+       For              Atom = 0x1ef03
+       Form             Atom = 0x21104
+       Formaction       Atom = 0x2110a
+       Formenctype      Atom = 0x2320b
+       Formmethod       Atom = 0x24d0a
+       Formnovalidate   Atom = 0x2570e
+       Formtarget       Atom = 0x26c0a
+       Frame            Atom = 0x2c905
+       Frameset         Atom = 0x2c908
+       H1               Atom = 0x10f02
+       H2               Atom = 0x29702
+       H3               Atom = 0x4ad02
+       H4               Atom = 0x27602
+       H5               Atom = 0x27802
+       H6               Atom = 0x27a02
+       Head             Atom = 0x30504
+       Header           Atom = 0x30506
+       Headers          Atom = 0x30507
+       Height           Atom = 0x27c06
+       Hgroup           Atom = 0x28806
+       Hidden           Atom = 0x28e06
+       High             Atom = 0x29404
+       Hr               Atom = 0x10a02
+       Href             Atom = 0x29904
+       Hreflang         Atom = 0x29908
+       Html             Atom = 0x28004
+       HttpEquiv        Atom = 0x2a10a
+       I                Atom = 0x601
+       Icon             Atom = 0x2b804
+       Id               Atom = 0xa102
+       Iframe           Atom = 0x2c806
+       Img              Atom = 0x2d103
+       Inert            Atom = 0x48805
+       Input            Atom = 0x3d305
+       Ins              Atom = 0x1ca03
+       Ismap            Atom = 0x2d405
+       Itemid           Atom = 0x1d206
+       Itemprop         Atom = 0x53608
+       Itemref          Atom = 0x2dd07
+       Itemscope        Atom = 0x2eb09
+       Itemtype         Atom = 0x2f508
+       Kbd              Atom = 0xba03
+       Keygen           Atom = 0x4e06
+       Keytype          Atom = 0x14807
+       Kind             Atom = 0x2b404
+       Label            Atom = 0x14105
+       Lang             Atom = 0x22e04
+       Legend           Atom = 0x19906
+       Li               Atom = 0x8702
+       Link             Atom = 0x14504
+       List             Atom = 0x40d04
+       Loop             Atom = 0x18304
+       Low              Atom = 0x28303
+       Manifest         Atom = 0x1008
+       Map              Atom = 0x2d603
+       Mark             Atom = 0x56e04
+       Max              Atom = 0x2fd03
+       Maxlength        Atom = 0x2fd09
+       Media            Atom = 0x6c05
+       Mediagroup       Atom = 0x6c0a
+       Menu             Atom = 0x33904
+       Meta             Atom = 0x41d04
+       Meter            Atom = 0x26705
+       Method           Atom = 0x25106
+       Min              Atom = 0x31003
+       Multiple         Atom = 0x31308
+       Muted            Atom = 0x31b05
+       Name             Atom = 0x6a04
+       Nav              Atom = 0x1f03
+       Nobr             Atom = 0x5304
+       Noscript         Atom = 0x5f08
+       Novalidate       Atom = 0x25b0a
+       Object           Atom = 0xb106
+       Ol               Atom = 0x7b02
+       Onabort          Atom = 0x17507
+       Onafterprint     Atom = 0x1250c
+       Onbeforeprint    Atom = 0x1eb0d
+       Onbeforeunload   Atom = 0x2190e
+       Onblur           Atom = 0x32a06
+       Oncancel         Atom = 0x57608
+       Oncanplay        Atom = 0x10009
+       Oncanplaythrough Atom = 0x10010
+       Onchange         Atom = 0x3a208
+       Onclick          Atom = 0x2ae07
+       Onclose          Atom = 0x32007
+       Oncontextmenu    Atom = 0x3300d
+       Oncuechange      Atom = 0x33d0b
+       Ondblclick       Atom = 0x3480a
+       Ondrag           Atom = 0x35206
+       Ondragend        Atom = 0x35209
+       Ondragenter      Atom = 0x35b0b
+       Ondragleave      Atom = 0x3660b
+       Ondragover       Atom = 0x3710a
+       Ondragstart      Atom = 0x37b0b
+       Ondrop           Atom = 0x38a06
+       Ondurationchange Atom = 0x39a10
+       Onemptied        Atom = 0x39109
+       Onended          Atom = 0x3aa07
+       Onerror          Atom = 0x3b107
+       Onfocus          Atom = 0x3b807
+       Onhashchange     Atom = 0x3c50c
+       Oninput          Atom = 0x3d107
+       Oninvalid        Atom = 0x3d809
+       Onkeydown        Atom = 0x3e109
+       Onkeypress       Atom = 0x3ee0a
+       Onkeyup          Atom = 0x3fa07
+       Onload           Atom = 0x40106
+       Onloadeddata     Atom = 0x4010c
+       Onloadedmetadata Atom = 0x41510
+       Onloadstart      Atom = 0x42b0b
+       Onmessage        Atom = 0x43609
+       Onmousedown      Atom = 0x43f0b
+       Onmousemove      Atom = 0x44a0b
+       Onmouseout       Atom = 0x4550a
+       Onmouseover      Atom = 0x4620b
+       Onmouseup        Atom = 0x46d09
+       Onmousewheel     Atom = 0x4760c
+       Onoffline        Atom = 0x48209
+       Ononline         Atom = 0x48d08
+       Onpagehide       Atom = 0x4950a
+       Onpageshow       Atom = 0x4a00a
+       Onpause          Atom = 0x4af07
+       Onplay           Atom = 0x4b906
+       Onplaying        Atom = 0x4b909
+       Onpopstate       Atom = 0x4c20a
+       Onprogress       Atom = 0x4cc0a
+       Onratechange     Atom = 0x4d60c
+       Onreset          Atom = 0x4e207
+       Onresize         Atom = 0x4e908
+       Onscroll         Atom = 0x4f208
+       Onseeked         Atom = 0x4fa08
+       Onseeking        Atom = 0x50209
+       Onselect         Atom = 0x50b08
+       Onshow           Atom = 0x51506
+       Onstalled        Atom = 0x51e09
+       Onstorage        Atom = 0x52709
+       Onsubmit         Atom = 0x53008
+       Onsuspend        Atom = 0x54009
+       Ontimeupdate     Atom = 0x2050c
+       Onunload         Atom = 0x54908
+       Onvolumechange   Atom = 0x5510e
+       Onwaiting        Atom = 0x55f09
+       Open             Atom = 0x53c04
+       Optgroup         Atom = 0x18508
+       Optimum          Atom = 0x56807
+       Option           Atom = 0x57206
+       Output           Atom = 0x45c06
+       P                Atom = 0xc01
+       Param            Atom = 0xc05
+       Pattern          Atom = 0x1907
+       Ping             Atom = 0x3204
+       Placeholder      Atom = 0x750b
+       Poster           Atom = 0x15d06
+       Pre              Atom = 0x18c03
+       Preload          Atom = 0x18c07
+       Progress         Atom = 0x4ce08
+       Q                Atom = 0x11601
+       Radiogroup       Atom = 0x30a
+       Readonly         Atom = 0xd108
+       Rel              Atom = 0x18d03
+       Required         Atom = 0x1fc08
+       Reversed         Atom = 0x5608
+       Rows             Atom = 0x7f04
+       Rowspan          Atom = 0x7f07
+       Rp               Atom = 0x12b02
+       Rt               Atom = 0x17a02
+       Ruby             Atom = 0x9304
+       S                Atom = 0x1601
+       Samp             Atom = 0x2f04
+       Sandbox          Atom = 0xe107
+       Scope            Atom = 0x2ef05
+       Scoped           Atom = 0x2ef06
+       Script           Atom = 0x6106
+       Seamless         Atom = 0xf208
+       Section          Atom = 0x32507
+       Select           Atom = 0x50d06
+       Selected         Atom = 0x50d08
+       Shape            Atom = 0xf905
+       Size             Atom = 0x4ed04
+       Sizes            Atom = 0x4ed05
+       Small            Atom = 0x13d05
+       Source           Atom = 0x1a606
+       Span             Atom = 0x8204
+       Spellcheck       Atom = 0x1b50a
+       Src              Atom = 0x1cc03
+       Srcdoc           Atom = 0x1cc06
+       Srclang          Atom = 0x22b07
+       Start            Atom = 0x38105
+       Step             Atom = 0x1604
+       Strong           Atom = 0x40f06
+       Style            Atom = 0x30b05
+       Sub              Atom = 0x53203
+       Summary          Atom = 0x3be07
+       Sup              Atom = 0x3f703
+       Tabindex         Atom = 0x42308
+       Table            Atom = 0x2c305
+       Target           Atom = 0x27006
+       Tbody            Atom = 0x4305
+       Td               Atom = 0x6602
+       Textarea         Atom = 0xcc08
+       Tfoot            Atom = 0x8d05
+       Th               Atom = 0x10902
+       Thead            Atom = 0x30405
+       Time             Atom = 0x20704
+       Title            Atom = 0xa805
+       Tr               Atom = 0xb602
+       Track            Atom = 0xb605
+       Translate        Atom = 0x13009
+       Type             Atom = 0x14b04
+       Typemustmatch    Atom = 0x2390d
+       U                Atom = 0xb01
+       Ul               Atom = 0xa602
+       Usemap           Atom = 0x4b306
+       Value            Atom = 0x2105
+       Var              Atom = 0x1e003
+       Video            Atom = 0x2aa05
+       Wbr              Atom = 0x28503
+       Width            Atom = 0x4a905
+       Wrap             Atom = 0x51a04
 )
 
-const maxLen = 16
+const hash0 = 0x516c42b0
 
-var table = [...]string{
-       "",
-       "a",
-       "b",
-       "i",
-       "p",
-       "q",
-       "s",
-       "u",
-       "br",
-       "em",
-       "dd",
-       "dl",
-       "dt",
-       "h1",
-       "h2",
-       "h3",
-       "h4",
-       "h5",
-       "h6",
-       "id",
-       "hr",
-       "ol",
-       "li",
-       "rp",
-       "rt",
-       "ul",
-       "td",
-       "th",
-       "tr",
-       "col",
-       "bdi",
-       "bdo",
-       "alt",
-       "for",
-       "dfn",
-       "del",
-       "dir",
-       "div",
-       "kbd",
-       "ins",
-       "img",
-       "nav",
-       "map",
-       "max",
-       "min",
-       "low",
-       "src",
-       "sub",
-       "sup",
-       "rel",
-       "pre",
-       "wbr",
-       "var",
-       "cite",
-       "code",
-       "cols",
-       "base",
-       "body",
-       "abbr",
-       "area",
-       "font",
-       "form",
-       "data",
-       "kind",
-       "icon",
-       "head",
-       "high",
-       "href",
-       "html",
-       "open",
-       "name",
-       "nobr",
-       "mark",
-       "menu",
-       "meta",
-       "lang",
-       "link",
-       "list",
-       "loop",
-       "samp",
-       "size",
-       "span",
-       "step",
-       "rows",
-       "ruby",
-       "ping",
-       "wrap",
-       "time",
-       "type",
-       "color",
-       "class",
-       "align",
-       "aside",
-       "async",
-       "audio",
-       "frame",
-       "embed",
-       "defer",
-       "inert",
-       "input",
-       "ismap",
-       "media",
-       "meter",
-       "muted",
-       "label",
-       "scope",
-       "sizes",
-       "shape",
-       "small",
-       "start",
-       "style",
-       "param",
-       "width",
-       "value",
-       "video",
-       "tbody",
-       "table",
-       "tfoot",
-       "title",
-       "thead",
-       "track",
-       "canvas",
-       "center",
-       "coords",
-       "border",
-       "button",
-       "accept",
-       "action",
-       "applet",
-       "figure",
-       "footer",
-       "dialog",
-       "keygen",
-       "iframe",
-       "itemid",
-       "hgroup",
-       "header",
-       "height",
-       "hidden",
-       "object",
-       "onblur",
-       "ondrag",
-       "ondrop",
-       "onload",
-       "onshow",
-       "onplay",
-       "option",
-       "output",
-       "method",
-       "legend",
-       "scoped",
-       "script",
-       "select",
-       "source",
-       "srcdoc",
-       "strong",
-       "poster",
-       "usemap",
-       "target",
-       "sandbox",
-       "caption",
-       "section",
-       "keytype",
-       "charset",
-       "checked",
-       "content",
-       "command",
-       "colspan",
-       "onclick",
-       "onclose",
-       "onabort",
-       "onfocus",
-       "onended",
-       "onerror",
-       "onkeyup",
-       "oninput",
-       "onreset",
-       "onpause",
-       "srclang",
-       "optimum",
-       "summary",
-       "rowspan",
-       "address",
-       "enctype",
-       "article",
-       "itemref",
-       "pattern",
-       "headers",
-       "default",
-       "details",
-       "dirname",
-       "preload",
-       "controls",
-       "colgroup",
-       "noscript",
-       "download",
-       "oncancel",
-       "onchange",
-       "frameset",
-       "hreflang",
-       "progress",
-       "ononline",
-       "dropzone",
-       "onscroll",
-       "onseeked",
-       "onselect",
-       "onsubmit",
-       "onresize",
-       "onunload",
-       "tabindex",
-       "readonly",
-       "seamless",
-       "fieldset",
-       "manifest",
-       "selected",
-       "multiple",
-       "disabled",
-       "required",
-       "reversed",
-       "datalist",
-       "datetime",
-       "autoplay",
-       "textarea",
-       "itemprop",
-       "itemtype",
-       "optgroup",
-       "onwaiting",
-       "oncanplay",
-       "onoffline",
-       "onseeking",
-       "accesskey",
-       "onkeydown",
-       "onsuspend",
-       "onstalled",
-       "onstorage",
-       "draggable",
-       "onmessage",
-       "onmouseup",
-       "translate",
-       "oninvalid",
-       "itemscope",
-       "onemptied",
-       "challenge",
-       "autofocus",
-       "onplaying",
-       "maxlength",
-       "ondragend",
-       "figcaption",
-       "blockquote",
-       "onpopstate",
-       "onmouseout",
-       "annotation",
-       "ondragover",
-       "onprogress",
-       "http-equiv",
-       "ondblclick",
-       "formaction",
-       "mediagroup",
-       "onpageshow",
-       "onpagehide",
-       "radiogroup",
-       "formmethod",
-       "novalidate",
-       "formtarget",
-       "onkeypress",
-       "spellcheck",
-       "crossorigin",
-       "oncuechange",
-       "ondragstart",
-       "ondragleave",
-       "ondragenter",
-       "onloadstart",
-       "placeholder",
-       "formenctype",
-       "onmousedown",
-       "onmousemove",
-       "onmouseover",
-       "contextmenu",
-       "autocomplete",
-       "onmousewheel",
-       "onafterprint",
-       "ontimeupdate",
-       "onratechange",
-       "onhashchange",
-       "onloadeddata",
-       "typemustmatch",
-       "oncontextmenu",
-       "onbeforeprint",
-       "accept-charset",
-       "formnovalidate",
-       "onvolumechange",
-       "onbeforeunload",
-       "contenteditable",
-       "ondurationchange",
-       "onloadedmetadata",
-       "oncanplaythrough",
-}
+const maxAtomLen = 16
 
-var hashes = [...]uint32{
-       0x00000000,
-       0x00000061,
-       0x00000062,
-       0x00000069,
-       0x00000070,
-       0x00000071,
-       0x00000073,
-       0x00000075,
-       0x00000c32,
-       0x00000ccd,
-       0x00000ce4,
-       0x00000cec,
-       0x00000cf4,
-       0x00000d31,
-       0x00000d32,
-       0x00000d33,
-       0x00000d34,
-       0x00000d35,
-       0x00000d36,
-       0x00000d44,
-       0x00000d72,
-       0x00000d8c,
-       0x00000de9,
-       0x00000e30,
-       0x00000e34,
-       0x00000ecc,
-       0x00000ee4,
-       0x00000ee8,
-       0x00000ef2,
-       0x0001818c,
-       0x000184e9,
-       0x000184ef,
-       0x000189f4,
-       0x00019592,
-       0x00019cae,
-       0x00019ccc,
-       0x00019d52,
-       0x00019d56,
-       0x0001a024,
-       0x0001a9b3,
-       0x0001a9c7,
-       0x0001b456,
-       0x0001b850,
-       0x0001b858,
-       0x0001b94e,
-       0x0001bd97,
-       0x0001c223,
-       0x0001c2c2,
-       0x0001c2d0,
-       0x0001c4cc,
-       0x0001ce25,
-       0x0001d032,
-       0x0001d452,
-       0x00302ae5,
-       0x003030e5,
-       0x003031f3,
-       0x00308a05,
-       0x0030b0f9,
-       0x00310432,
-       0x003144c1,
-       0x0032b1b4,
-       0x0032b22d,
-       0x00338ae1,
-       0x003429a4,
-       0x0035018e,
-       0x00359844,
-       0x0035a888,
-       0x0035c4c6,
-       0x0035ddcc,
-       0x00364cce,
-       0x003689c5,
-       0x0036b032,
-       0x00370a2b,
-       0x003719b5,
-       0x00371ae1,
-       0x003789a7,
-       0x0037a9ab,
-       0x0037aa14,
-       0x0037b190,
-       0x003809d0,
-       0x00382b25,
-       0x00384c4e,
-       0x00385cd0,
-       0x0038b293,
-       0x0038d839,
-       0x0039a9a7,
-       0x003a4450,
-       0x003ba9c5,
-       0x003bea65,
-       0x06063d92,
-       0x06078a13,
-       0x0627a88e,
-       0x062828e5,
-       0x062869a3,
-       0x062b1d4f,
-       0x065889c5,
-       0x066704c4,
-       0x067314d2,
-       0x06a69a34,
-       0x06a6ced4,
-       0x06a83850,
-       0x06e31d41,
-       0x06e35cd2,
-       0x06eb5cc4,
-       0x06f104cc,
-       0x07003265,
-       0x070564d3,
-       0x07058a65,
-       0x070709ec,
-       0x070b8a34,
-       0x070be9e5,
-       0x0731444d,
-       0x07451ee8,
-       0x07513ec5,
-       0x07551ccf,
-       0x0770b0f9,
-       0x077105e5,
-       0x0772b194,
-       0x07755de5,
-       0x07759844,
-       0x0778880b,
-       0xc026d453,
-       0xc066dcd2,
-       0xc0c644f3,
-       0xc2c89cd2,
-       0xc36bdd8e,
-       0xc4001a74,
-       0xc40ba98e,
-       0xc539bcd4,
-       0xcaa25a25,
-       0xcac65cd2,
-       0xcea13d87,
-       0xd06d10ce,
-       0xd45889c5,
-       0xd5733944,
-       0xd648b2d0,
-       0xd6611cd2,
-       0xd6651174,
-       0xd6a39cce,
-       0xd8149814,
-       0xd8d0bed2,
-       0xd8d3c447,
-       0xd8d3c590,
-       0xd8d7b044,
-       0xd8d82d97,
-       0xd8d9bc59,
-       0xd93ba98e,
-       0xd96bced4,
-       0xdc6bad84,
-       0xde6219a4,
-       0xe0064cc4,
-       0xe008aa74,
-       0xe0679814,
-       0xe0cb4405,
-       0xe1101d83,
-       0xe178b1a7,
-       0xe6c85cd2,
-       0xed033850,
-       0xee2890d4,
-       0x04d38584,
-       0x053ba996,
-       0x0c0ba992,
-       0x0dabea7f,
-       0x1628c0cc,
-       0x166020dc,
-       0x18db99ac,
-       0x18e709bc,
-       0x18f84c56,
-       0x1a07a810,
-       0x1a07b21e,
-       0x1a20b22f,
-       0x1a5602c8,
-       0x1a669cdf,
-       0x1a68c589,
-       0x1a836acb,
-       0x1aa6cecf,
-       0x1b1340cf,
-       0x1b315a1e,
-       0x220789bb,
-       0x27753ad6,
-       0x2ce70a25,
-       0x59484c52,
-       0x8e789a0b,
-       0x9a0bea7c,
-       0xa37501fd,
-       0xae6744dc,
-       0xc57b9a32,
-       0xcc239a29,
-       0xcc5159ed,
-       0xcd7129ea,
-       0xd51689dc,
-       0xe267b058,
-       0x1b78b2f0,
-       0x1e48b1d3,
-       0x2008a91f,
-       0x28d7b37f,
-       0x402683af,
-       0x40b137e6,
-       0x44e343f8,
-       0x4c578afb,
-       0x5848998f,
-       0x58d7aac6,
-       0x593cb299,
-       0x6008b28f,
-       0x606323a7,
-       0x60679b77,
-       0x6160ba37,
-       0x62682846,
-       0x6cd7b327,
-       0x82a69f60,
-       0x84763670,
-       0x84e79992,
-       0x8cf3c3fe,
-       0x9aa29964,
-       0x9e605f45,
-       0x9f754e90,
-       0xa020bffe,
-       0xa565474d,
-       0xaa68c34d,
-       0xae27a92c,
-       0xae6baafd,
-       0xaec9bf4c,
-       0xb7714778,
-       0xcce9c6c5,
-       0xccebe930,
-       0xee48b1b4,
-       0x04abc5ca,
-       0x04d9d031,
-       0x0a57c5ce,
-       0x0c6445cb,
-       0x0d0842d9,
-       0x0da3dee4,
-       0x2d09f5c8,
-       0x2e27d0a8,
-       0x2ec8e4e9,
-       0x8841626d,
-       0x8d0864ee,
-       0x996876bb,
-       0x9b07fd6d,
-       0x9b51512e,
-       0x9d0058dc,
-       0x9d3bc4ad,
-       0x9ef354dd,
-       0xd8566066,
-       0xde2d45cb,
-       0xde66fcfe,
-       0xe22275cd,
-       0x053703ae,
-       0x11271d85,
-       0x2706077e,
-       0x2d0ebfa7,
-       0x2e27e4e5,
-       0x444bd9ee,
-       0x5845178f,
-       0x5c642eea,
-       0x5e0a2533,
-       0x84070505,
-       0x844574ea,
-       0x8865a00f,
-       0x8868257d,
-       0x984690ea,
-       0x9c67010f,
-       0x9eae264d,
-       0xae243c5f,
-       0xb5351752,
-       0xde0b8b38,
-       0x18973dca,
-       0x81009434,
-       0x88ba2dbc,
-       0x8942ad2d,
-       0x89d77b5a,
-       0x8eba2554,
-       0x970a7ed3,
-       0x9b9e7b14,
-       0xa1d21ceb,
-       0xa1d69cc0,
-       0xa1d7fab7,
-       0xb6f6940c,
-       0x2c6d76e6,
-       0x3b705478,
-       0x950cec0d,
-       0x9b056094,
-       0xb687163c,
-       0xf6845607,
-       0xfa4666f0,
-       0x53ad92bb,
-       0x71f6940a,
-       0x8bbc6cd6,
-       0x1632b560,
-       0x561a2687,
-       0x5a00c22c,
-       0x7c4f1c15,
-       0x0ee8aacc,
-       0x2838bda9,
-       0x6f3c2ece,
-       0xf1d8d91d,
+var table = [1 << 9]Atom{
+       0x2:   0x1f03,  // nav
+       0x3:   0x17507, // onabort
+       0x4:   0x1aa06, // center
+       0x5:   0x14f04, // code
+       0x7:   0x27802, // h5
+       0xb:   0x1110a, // blockquote
+       0xd:   0x4404,  // body
+       0xe:   0x10a02, // hr
+       0x11:  0x25b0a, // novalidate
+       0x14:  0x2c305, // table
+       0x16:  0x4cc0a, // onprogress
+       0x17:  0x3b807, // onfocus
+       0x19:  0x39a10, // ondurationchange
+       0x1c:  0x22e04, // lang
+       0x1f:  0xb01,   // u
+       0x20:  0x3e608, // download
+       0x21:  0x26705, // meter
+       0x22:  0x28303, // low
+       0x24:  0x4f208, // onscroll
+       0x26:  0x19f08, // controls
+       0x27:  0x6703,  // dir
+       0x29:  0x18c03, // pre
+       0x2a:  0x1b50a, // spellcheck
+       0x2b:  0x28806, // hgroup
+       0x2d:  0x4e908, // onresize
+       0x2e:  0x35b0b, // ondragenter
+       0x30:  0x48805, // inert
+       0x32:  0x2390d, // typemustmatch
+       0x33:  0x6a04,  // name
+       0x35:  0x28503, // wbr
+       0x36:  0x1eb0d, // onbeforeprint
+       0x39:  0x4af07, // onpause
+       0x3b:  0x24d0a, // formmethod
+       0x3e:  0x2fd03, // max
+       0x3f:  0x2d103, // img
+       0x40:  0xc01,   // p
+       0x41:  0x19906, // legend
+       0x43:  0x2c806, // iframe
+       0x44:  0x55f09, // onwaiting
+       0x45:  0x18c07, // preload
+       0x46:  0x1e607, // caption
+       0x47:  0xba03,  // kbd
+       0x49:  0x20704, // time
+       0x4a:  0x1ca03, // ins
+       0x4d:  0xbb03,  // bdi
+       0x4e:  0x14105, // label
+       0x4f:  0x18d03, // rel
+       0x50:  0x2ef05, // scope
+       0x51:  0x2050c, // ontimeupdate
+       0x53:  0xd909,  // autofocus
+       0x54:  0xc20c,  // autocomplete
+       0x55:  0x28004, // html
+       0x56:  0x1e30a, // figcaption
+       0x59:  0x17c07, // command
+       0x5d:  0x2c905, // frame
+       0x5f:  0x1ef03, // for
+       0x60:  0x1250c, // onafterprint
+       0x61:  0x2f04,  // samp
+       0x62:  0x30507, // headers
+       0x63:  0x4b306, // usemap
+       0x65:  0x14b04, // type
+       0x6b:  0x26108, // datetime
+       0x6d:  0xa102,  // id
+       0x6e:  0x30405, // thead
+       0x6f:  0x15d06, // poster
+       0x70:  0x18202, // dl
+       0x71:  0x2b804, // icon
+       0x74:  0x51e09, // onstalled
+       0x75:  0x16804, // cols
+       0x76:  0x4ed05, // sizes
+       0x78:  0x13d05, // small
+       0x79:  0x3a208, // onchange
+       0x7b:  0x3b107, // onerror
+       0x7c:  0x4a905, // width
+       0x7d:  0x20404, // font
+       0x7e:  0x28e06, // hidden
+       0x7f:  0x10009, // oncanplay
+       0x81:  0xe808,  // autoplay
+       0x82:  0x1d104, // cite
+       0x84:  0x3d07,  // charset
+       0x85:  0x3710a, // ondragover
+       0x86:  0x2502,  // em
+       0x87:  0x1cc03, // src
+       0x89:  0x1ba07, // checked
+       0x8a:  0xad05,  // audio
+       0x8b:  0x19209, // draggable
+       0x8d:  0x1c10b, // crossorigin
+       0x8e:  0x18304, // loop
+       0x90:  0x2dd07, // itemref
+       0x93:  0x4ce08, // progress
+       0x94:  0x3d305, // input
+       0x96:  0x101,   // b
+       0x98:  0x5510e, // onvolumechange
+       0x99:  0x27006, // target
+       0x9c:  0x4a00a, // onpageshow
+       0x9d:  0x2eb09, // itemscope
+       0x9e:  0x54908, // onunload
+       0x9f:  0xf208,  // seamless
+       0xa3:  0x8e06,  // footer
+       0xa6:  0x2907,  // details
+       0xa7:  0x3ee0a, // onkeypress
+       0xaa:  0x1d708, // disabled
+       0xab:  0x31308, // multiple
+       0xac:  0x3d809, // oninvalid
+       0xad:  0x46d09, // onmouseup
+       0xaf:  0x2d405, // ismap
+       0xb0:  0x8204,  // span
+       0xb2:  0x1d206, // itemid
+       0xb3:  0x6106,  // script
+       0xb6:  0x21104, // form
+       0xb8:  0x9f05,  // aside
+       0xba:  0x38307, // article
+       0xbb:  0x12b02, // rp
+       0xbc:  0x29404, // high
+       0xbe:  0x1a606, // source
+       0xbf:  0xe107,  // sandbox
+       0xc0:  0x5d03,  // dfn
+       0xc1:  0x3204,  // ping
+       0xc2:  0x4ed04, // size
+       0xc3:  0x2ae07, // onclick
+       0xc5:  0x29908, // hreflang
+       0xc7:  0x2f508, // itemtype
+       0xc8:  0x1cc06, // srcdoc
+       0xc9:  0x40d04, // list
+       0xcc:  0x2d706, // applet
+       0xcf:  0x4760c, // onmousewheel
+       0xd0:  0x22507, // address
+       0xd1:  0x25106, // method
+       0xd5:  0x49d03, // del
+       0xd7:  0x35206, // ondrag
+       0xd9:  0x41510, // onloadedmetadata
+       0xda:  0xcc08,  // textarea
+       0xdb:  0x4e207, // onreset
+       0xdc:  0x57206, // option
+       0xdd:  0x2505,  // embed
+       0xdf:  0x3d107, // oninput
+       0xe0:  0x40908, // datalist
+       0xe1:  0x4ad02, // h3
+       0xe3:  0x202,   // br
+       0xe5:  0x40f06, // strong
+       0xe6:  0x5608,  // reversed
+       0xea:  0x22b07, // srclang
+       0xec:  0x10902, // th
+       0xef:  0x45c06, // output
+       0xf2:  0x27602, // h4
+       0xf5:  0x42308, // tabindex
+       0xf6:  0x2b907, // content
+       0xf9:  0x1601,  // s
+       0xfb:  0x3320b, // contextmenu
+       0xfc:  0x33d0b, // oncuechange
+       0xfe:  0x52709, // onstorage
+       0x100: 0x4305,  // tbody
+       0x101: 0x50d06, // select
+       0x102: 0x2320b, // formenctype
+       0x103: 0x1,     // a
+       0x104: 0x51a04, // wrap
+       0x108: 0x22602, // dd
+       0x109: 0xa602,  // ul
+       0x10a: 0x4950a, // onpagehide
+       0x10c: 0x43609, // onmessage
+       0x10d: 0xa207,  // default
+       0x10f: 0x38c08, // dropzone
+       0x111: 0x53008, // onsubmit
+       0x114: 0x9705,  // async
+       0x119: 0x50d08, // selected
+       0x11a: 0x2fd09, // maxlength
+       0x11c: 0x15105, // defer
+       0x11d: 0x16807, // colspan
+       0x11e: 0x3480a, // ondblclick
+       0x121: 0x2b90f, // contenteditable
+       0x125: 0x16d0a, // annotation
+       0x12a: 0x31003, // min
+       0x12c: 0x4fa08, // onseeked
+       0x12e: 0x11b06, // border
+       0x12f: 0x4b906, // onplay
+       0x130: 0x2ef06, // scoped
+       0x134: 0x2e308, // fieldset
+       0x135: 0x1b006, // coords
+       0x136: 0x6707,  // dirname
+       0x137: 0x32007, // onclose
+       0x138: 0x6602,  // td
+       0x13c: 0x32a06, // onblur
+       0x140: 0x9304,  // ruby
+       0x141: 0x50b08, // onselect
+       0x143: 0x3300d, // oncontextmenu
+       0x144: 0x12106, // button
+       0x146: 0xa805,  // title
+       0x147: 0x16305, // color
+       0x14a: 0x4620b, // onmouseover
+       0x14b: 0x23607, // enctype
+       0x14e: 0x29702, // h2
+       0x150: 0x3e109, // onkeydown
+       0x151: 0x3c50c, // onhashchange
+       0x152: 0x1604,  // step
+       0x153: 0x2aa05, // video
+       0x155: 0x4d60c, // onratechange
+       0x156: 0x17a02, // rt
+       0x157: 0x33904, // menu
+       0x15d: 0x37b0b, // ondragstart
+       0x160: 0x14504, // link
+       0x163: 0x7f07,  // rowspan
+       0x164: 0x4550a, // onmouseout
+       0x165: 0x29904, // href
+       0x167: 0x26c0a, // formtarget
+       0x169: 0xd004,  // area
+       0x16b: 0x8b03,  // alt
+       0x16d: 0x15608, // colgroup
+       0x16e: 0x30a,   // radiogroup
+       0x170: 0x30506, // header
+       0x172: 0x53203, // sub
+       0x174: 0x10010, // oncanplaythrough
+       0x175: 0x13009, // translate
+       0x176: 0x48d08, // ononline
+       0x179: 0x24409, // challenge
+       0x17c: 0x3f703, // sup
+       0x17d: 0x8605,  // align
+       0x17f: 0x5304,  // nobr
+       0x180: 0x1f806, // figure
+       0x181: 0xc05,   // param
+       0x184: 0x35209, // ondragend
+       0x185: 0x18508, // optgroup
+       0x186: 0x31b05, // muted
+       0x187: 0x6c0a,  // mediagroup
+       0x18a: 0x21506, // action
+       0x18c: 0x53c04, // open
+       0x18e: 0xf905,  // shape
+       0x18f: 0x54009, // onsuspend
+       0x190: 0x38a06, // ondrop
+       0x191: 0x56807, // optimum
+       0x192: 0x53608, // itemprop
+       0x193: 0x39109, // onemptied
+       0x195: 0xf004,  // base
+       0x198: 0x40904, // data
+       0x19a: 0x27a02, // h6
+       0x19b: 0x601,   // i
+       0x19c: 0x2110a, // formaction
+       0x19d: 0x360e,  // accept-charset
+       0x19e: 0x1e003, // var
+       0x19f: 0x57608, // oncancel
+       0x1a0: 0x750b,  // placeholder
+       0x1a1: 0x4e06,  // keygen
+       0x1a2: 0x3660b, // ondragleave
+       0x1a4: 0x4010c, // onloadeddata
+       0x1a6: 0x2d603, // map
+       0x1a7: 0x2a10a, // http-equiv
+       0x1a8: 0x1907,  // pattern
+       0x1a9: 0x4c20a, // onpopstate
+       0x1ab: 0x2570e, // formnovalidate
+       0x1ad: 0x44a0b, // onmousemove
+       0x1af: 0x42b0b, // onloadstart
+       0x1b0: 0xb605,  // track
+       0x1b2: 0x2b404, // kind
+       0x1b3: 0x7b02,  // ol
+       0x1b4: 0x6c05,  // media
+       0x1b5: 0x3be07, // summary
+       0x1b7: 0x14807, // keytype
+       0x1b8: 0x5f08,  // noscript
+       0x1b9: 0x1fc08, // required
+       0x1bb: 0x1de03, // div
+       0x1bd: 0x3fa07, // onkeyup
+       0x1be: 0xd108,  // readonly
+       0x1bf: 0x3aa07, // onended
+       0x1c5: 0x4b909, // onplaying
+       0x1c7: 0x32507, // section
+       0x1c8: 0x3606,  // accept
+       0x1c9: 0x4809,  // accesskey
+       0x1ca: 0x30b05, // style
+       0x1cb: 0x2c908, // frameset
+       0x1cc: 0x38105, // start
+       0x1cd: 0x43f0b, // onmousedown
+       0x1d2: 0x30504, // head
+       0x1d3: 0x11601, // q
+       0x1d4: 0x48209, // onoffline
+       0x1d5: 0x41d04, // meta
+       0x1d8: 0x4ab02, // dt
+       0x1da: 0xb602,  // tr
+       0x1db: 0x50209, // onseeking
+       0x1dc: 0xbc06,  // dialog
+       0x1e0: 0x51506, // onshow
+       0x1e1: 0x2105,  // value
+       0x1e2: 0x9b06,  // canvas
+       0x1e3: 0x4,     // abbr
+       0x1e5: 0x7f04,  // rows
+       0x1e7: 0xb106,  // object
+       0x1e8: 0x13905, // class
+       0x1eb: 0x27c06, // height
+       0x1ed: 0x2190e, // onbeforeunload
+       0x1ee: 0x8d05,  // tfoot
+       0x1f1: 0x56e04, // mark
+       0x1f2: 0x10f02, // h1
+       0x1f5: 0x40106, // onload
+       0x1f9: 0x1008,  // manifest
+       0x1fa: 0x15603, // col
+       0x1fc: 0x8702,  // li
+       0x1ff: 0xfe03,  // bdo
 }
 
-var loHi = [maxLen + 2]uint16{
-       0,
-       1,
-       8,
-       29,
-       53,
-       89,
-       121,
-       159,
-       192,
-       226,
-       247,
-       266,
-       278,
-       285,
-       288,
-       292,
-       293,
-       296,
-}
-
-var oneByteAtoms = [26]Atom{
-       A,
-       B,
-       0,
-       0,
-       0,
-       0,
-       0,
-       0,
-       I,
-       0,
-       0,
-       0,
-       0,
-       0,
-       0,
-       P,
-       Q,
-       0,
-       S,
-       0,
-       U,
-       0,
-       0,
-       0,
-       0,
-       0,
-}
+const atomText = "abbradiogrouparamanifestepatternavaluembedetailsampingaccept" +
+       "-charsetbodyaccesskeygenobreversedfnoscriptdirnamediagroupla" +
+       "ceholderowspanalignaltfooterubyasyncanvasidefaultitleaudiobj" +
+       "ectrackbdialogautocompletextareadonlyautofocusandboxautoplay" +
+       "baseamlesshapebdoncanplaythrough1blockquoteborderbuttonafter" +
+       "printranslateclassmallabelinkeytypecodefercolgroupostercolor" +
+       "colspannotationabortcommandlooptgroupreloadraggablegendcontr" +
+       "olsourcentercoordspellcheckedcrossoriginsrcdocitemidisabledi" +
+       "varfigcaptionbeforeprintfigurequiredfontimeupdateformactionb" +
+       "eforeunloaddressrclangformenctypemustmatchallengeformmethodf" +
+       "ormnovalidatetimeterformtargeth4h5h6heightmlowbrhgrouphidden" +
+       "high2hreflanghttp-equivideonclickindicontenteditableiframese" +
+       "timgismappletitemrefieldsetitemscopeditemtypemaxlengtheaders" +
+       "tyleminmultiplemutedonclosectionbluroncontextmenuoncuechange" +
+       "ondblclickondragendondragenterondragleaveondragoverondragsta" +
+       "rticleondropzonemptiedondurationchangeonendedonerroronfocusu" +
+       "mmaryonhashchangeoninputoninvalidonkeydownloadonkeypressupon" +
+       "keyuponloadeddatalistrongonloadedmetadatabindexonloadstarton" +
+       "messageonmousedownonmousemoveonmouseoutputonmouseoveronmouse" +
+       "uponmousewheelonofflinertononlineonpagehidelonpageshowidth3o" +
+       "npausemaponplayingonpopstateonprogressonratechangeonresetonr" +
+       "esizesonscrollonseekedonseekingonselectedonshowraponstalledo" +
+       "nstorageonsubmitempropenonsuspendonunloadonvolumechangeonwai" +
+       "tingoptimumarkoptioncancel"
diff --git a/src/pkg/exp/html/atom/table_test.go b/src/pkg/exp/html/atom/table_test.go
new file mode 100644 (file)
index 0000000..b47a3f9
--- /dev/null
@@ -0,0 +1,309 @@
+// generated by go run gen.go -test; DO NOT EDIT
+
+package atom
+
+var testAtomList = []string{
+       "a",
+       "abbr",
+       "accept",
+       "accept-charset",
+       "accesskey",
+       "action",
+       "address",
+       "align",
+       "alt",
+       "annotation",
+       "applet",
+       "area",
+       "article",
+       "aside",
+       "async",
+       "audio",
+       "autocomplete",
+       "autofocus",
+       "autoplay",
+       "b",
+       "base",
+       "bdi",
+       "bdo",
+       "blockquote",
+       "body",
+       "border",
+       "br",
+       "button",
+       "canvas",
+       "caption",
+       "center",
+       "challenge",
+       "charset",
+       "checked",
+       "cite",
+       "cite",
+       "class",
+       "code",
+       "col",
+       "colgroup",
+       "color",
+       "cols",
+       "colspan",
+       "command",
+       "command",
+       "content",
+       "contenteditable",
+       "contextmenu",
+       "controls",
+       "coords",
+       "crossorigin",
+       "data",
+       "data",
+       "datalist",
+       "datetime",
+       "dd",
+       "default",
+       "defer",
+       "del",
+       "details",
+       "dfn",
+       "dialog",
+       "dir",
+       "dirname",
+       "disabled",
+       "div",
+       "dl",
+       "download",
+       "draggable",
+       "dropzone",
+       "dt",
+       "em",
+       "embed",
+       "enctype",
+       "fieldset",
+       "figcaption",
+       "figure",
+       "font",
+       "footer",
+       "for",
+       "form",
+       "form",
+       "formaction",
+       "formenctype",
+       "formmethod",
+       "formnovalidate",
+       "formtarget",
+       "frame",
+       "frameset",
+       "h1",
+       "h2",
+       "h3",
+       "h4",
+       "h5",
+       "h6",
+       "head",
+       "header",
+       "headers",
+       "height",
+       "hgroup",
+       "hidden",
+       "high",
+       "hr",
+       "href",
+       "hreflang",
+       "html",
+       "http-equiv",
+       "i",
+       "icon",
+       "id",
+       "iframe",
+       "img",
+       "inert",
+       "input",
+       "ins",
+       "ismap",
+       "itemid",
+       "itemprop",
+       "itemref",
+       "itemscope",
+       "itemtype",
+       "kbd",
+       "keygen",
+       "keytype",
+       "kind",
+       "label",
+       "label",
+       "lang",
+       "legend",
+       "li",
+       "link",
+       "list",
+       "loop",
+       "low",
+       "manifest",
+       "map",
+       "mark",
+       "max",
+       "maxlength",
+       "media",
+       "mediagroup",
+       "menu",
+       "meta",
+       "meter",
+       "method",
+       "min",
+       "multiple",
+       "muted",
+       "name",
+       "nav",
+       "nobr",
+       "noscript",
+       "novalidate",
+       "object",
+       "ol",
+       "onabort",
+       "onafterprint",
+       "onbeforeprint",
+       "onbeforeunload",
+       "onblur",
+       "oncancel",
+       "oncanplay",
+       "oncanplaythrough",
+       "onchange",
+       "onclick",
+       "onclose",
+       "oncontextmenu",
+       "oncuechange",
+       "ondblclick",
+       "ondrag",
+       "ondragend",
+       "ondragenter",
+       "ondragleave",
+       "ondragover",
+       "ondragstart",
+       "ondrop",
+       "ondurationchange",
+       "onemptied",
+       "onended",
+       "onerror",
+       "onfocus",
+       "onhashchange",
+       "oninput",
+       "oninvalid",
+       "onkeydown",
+       "onkeypress",
+       "onkeyup",
+       "onload",
+       "onloadeddata",
+       "onloadedmetadata",
+       "onloadstart",
+       "onmessage",
+       "onmousedown",
+       "onmousemove",
+       "onmouseout",
+       "onmouseover",
+       "onmouseup",
+       "onmousewheel",
+       "onoffline",
+       "ononline",
+       "onpagehide",
+       "onpageshow",
+       "onpause",
+       "onplay",
+       "onplaying",
+       "onpopstate",
+       "onprogress",
+       "onratechange",
+       "onreset",
+       "onresize",
+       "onscroll",
+       "onseeked",
+       "onseeking",
+       "onselect",
+       "onshow",
+       "onstalled",
+       "onstorage",
+       "onsubmit",
+       "onsuspend",
+       "ontimeupdate",
+       "onunload",
+       "onvolumechange",
+       "onwaiting",
+       "open",
+       "optgroup",
+       "optimum",
+       "option",
+       "output",
+       "p",
+       "param",
+       "pattern",
+       "ping",
+       "placeholder",
+       "poster",
+       "pre",
+       "preload",
+       "progress",
+       "q",
+       "radiogroup",
+       "readonly",
+       "rel",
+       "required",
+       "reversed",
+       "rows",
+       "rowspan",
+       "rp",
+       "rt",
+       "ruby",
+       "s",
+       "samp",
+       "sandbox",
+       "scope",
+       "scoped",
+       "script",
+       "seamless",
+       "section",
+       "select",
+       "selected",
+       "shape",
+       "size",
+       "sizes",
+       "small",
+       "source",
+       "span",
+       "span",
+       "spellcheck",
+       "src",
+       "srcdoc",
+       "srclang",
+       "start",
+       "step",
+       "strong",
+       "style",
+       "style",
+       "sub",
+       "summary",
+       "sup",
+       "tabindex",
+       "table",
+       "target",
+       "tbody",
+       "td",
+       "textarea",
+       "tfoot",
+       "th",
+       "thead",
+       "time",
+       "title",
+       "title",
+       "tr",
+       "track",
+       "translate",
+       "type",
+       "typemustmatch",
+       "u",
+       "ul",
+       "usemap",
+       "value",
+       "var",
+       "video",
+       "wbr",
+       "width",
+       "wrap",
+}