1 // Copyright 2012 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
9 // This program generates table.go and table_test.go.
12 // go run gen.go |gofmt >table.go
13 // go run gen.go -test |gofmt >table_test.go
24 // identifier converts s to a Go exported identifier.
25 // It converts "div" to "Div" and "accept-charset" to "AcceptCharset".
26 func identifier(s string) string {
27 b := make([]byte, 0, len(s))
34 if cap && 'a' <= c && c <= 'z' {
38 b = append(b, byte(c))
43 var test = flag.Bool("test", false, "generate table_test.go")
49 all = append(all, elements...)
50 all = append(all, attributes...)
51 all = append(all, eventHandlers...)
52 all = append(all, extra...)
56 fmt.Printf("// generated by go run gen.go -test; DO NOT EDIT\n\n")
57 fmt.Printf("package atom\n\n")
58 fmt.Printf("var testAtomList = []string{\n")
59 for _, s := range all {
60 fmt.Printf("\t%q,\n", s)
66 // uniq - lists have dups
67 // compute max len too
70 for _, s := range all {
71 if w == 0 || all[w-1] != s {
81 // Find hash that minimizes table size.
83 for i := 0; i < 1000000; i++ {
84 if best != nil && 1<<(best.k-1) < len(all) {
88 for k := uint(0); k <= 16; k++ {
89 if best != nil && k >= best.k {
93 if t.init(h, k, all) {
100 fmt.Fprintf(os.Stderr, "failed to construct string table\n")
104 // Lay out strings, using overlaps when possible.
105 layout := append([]string{}, all...)
107 // Remove strings that are substrings of other strings
108 for changed := true; changed; {
110 for i, s := range layout {
114 for j, t := range layout {
115 if i != j && t != "" && strings.Contains(s, t) {
123 // Join strings where one suffix matches another prefix.
125 // Find best i, j, k such that layout[i][len-k:] == layout[j][:k],
126 // maximizing overlap length k.
130 for i, s := range layout {
134 for j, t := range layout {
138 for k := bestk + 1; k <= len(s) && k <= len(t); k++ {
139 if s[len(s)-k:] == t[:k] {
148 layout[besti] += layout[bestj][bestk:]
155 text := strings.Join(layout, "")
157 atom := map[string]uint32{}
158 for _, s := range all {
159 off := strings.Index(text, s)
161 panic("lost string " + s)
163 atom[s] = uint32(off<<8 | len(s))
166 // Generate the Go code.
167 fmt.Printf("// generated by go run gen.go; DO NOT EDIT\n\n")
168 fmt.Printf("package atom\n\nconst (\n")
169 for _, s := range all {
170 fmt.Printf("\t%s Atom = %#x\n", identifier(s), atom[s])
174 fmt.Printf("const hash0 = %#x\n\n", best.h0)
175 fmt.Printf("const maxAtomLen = %d\n\n", maxLen)
177 fmt.Printf("var table = [1<<%d]Atom{\n", best.k)
178 for i, s := range best.tab {
182 fmt.Printf("\t%#x: %#x, // %s\n", i, atom[s], s)
185 datasize := (1 << best.k) * 4
187 fmt.Printf("const atomText =\n")
188 textsize := len(text)
190 fmt.Printf("\t%q +\n", text[:60])
193 fmt.Printf("\t%q\n\n", text)
195 fmt.Fprintf(os.Stderr, "%d atoms; %d string bytes + %d tables = %d total data\n", len(all), textsize, datasize, textsize+datasize)
200 func (x byLen) Less(i, j int) bool { return len(x[i]) > len(x[j]) }
201 func (x byLen) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
202 func (x byLen) Len() int { return len(x) }
204 // fnv computes the FNV hash with an arbitrary starting value h.
205 func fnv(h uint32, s string) uint32 {
206 for i := 0; i < len(s); i++ {
213 // A table represents an attempt at constructing the lookup table.
214 // The lookup table uses cuckoo hashing, meaning that each string
215 // can be found in one of two positions.
223 // hash returns the two hashes for s.
224 func (t *table) hash(s string) (h1, h2 uint32) {
227 h2 = (h >> 16) & t.mask
231 // init initializes the table with the given parameters.
232 // h0 is the initial hash value,
233 // k is the number of bits of hash value to use, and
234 // x is the list of strings to store in the table.
235 // init returns false if the table cannot be constructed.
236 func (t *table) init(h0 uint32, k uint, x []string) bool {
239 t.tab = make([]string, 1<<k)
241 for _, s := range x {
249 // insert inserts s in the table.
250 func (t *table) insert(s string) bool {
271 // push attempts to push aside the entry in slot i.
272 func (t *table) push(i uint32, depth int) bool {
273 if depth > len(t.tab) {
279 if t.tab[j] != "" && !t.push(j, depth+1) {
286 // The lists of element names and attribute keys were taken from
287 // https://html.spec.whatwg.org/multipage/indices.html#index
288 // as of the "HTML Living Standard - Last Updated 21 February 2015" version.
290 var elements = []string{
405 // https://html.spec.whatwg.org/multipage/indices.html#attributes-3
407 var attributes = []string{
529 var eventHandlers = []string{
532 "onautocompleteerror",
605 // extra are ad-hoc values not covered by any of the lists above.
606 var extra = []string{
620 "foreignObject", // HTML is case-insensitive, but SVG-embedded-in-HTML is case-sensitive.