├── go.mod ├── LICENSE.md ├── go.sum ├── README.md ├── hasher_test.go ├── hasher.go └── data_test.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/tdewolff/hasher 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/alecthomas/mph v0.0.0-20190930022807-712982e3d8a2 7 | github.com/alecthomas/unsafeslice v0.0.0-20190825002529-d95de1041e15 // indirect 8 | github.com/cespare/mph v0.0.0-20180814222818-ecff71bf0208 9 | github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc 10 | github.com/dgryski/go-mph v0.0.0-20191204212658-d9f21d2b3624 11 | ) 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/alecthomas/mph v0.0.0-20190930022807-712982e3d8a2 h1:3QU1afrYIAZZo5tf7/O7DAOUNn05wlxHZoB9YSXVg08= 2 | github.com/alecthomas/mph v0.0.0-20190930022807-712982e3d8a2/go.mod h1:HX5roj0AK3pjtDnP4HIV4zY4yW56odJ0LEwgoHL8NLI= 3 | github.com/alecthomas/unsafeslice v0.0.0-20190825002529-d95de1041e15 h1:jxwxFksE7BuJiGPn2NbC3mIl6Ii3u3B5hRfCAE5Ljq8= 4 | github.com/alecthomas/unsafeslice v0.0.0-20190825002529-d95de1041e15/go.mod h1:H7s9N0gAbfiwu02rQEexZbN/YMxm+2l3rVRa/zE2DM8= 5 | github.com/cespare/mph v0.0.0-20180814222818-ecff71bf0208 h1:aFTgPrrDbvPnMR2jzlTojtOKL1uWy1I87kaVcJfgCOk= 6 | github.com/cespare/mph v0.0.0-20180814222818-ecff71bf0208/go.mod h1:bY9whUE6zsIiluTt3rFJDiQC6bton95U7srG1Uhva/A= 7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc h1:8WFBn63wegobsYAX0YjD+8suexZDga5CctH4CCTx2+8= 9 | github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= 10 | github.com/dgryski/go-mph v0.0.0-20191204212658-d9f21d2b3624 h1:UDNZ9Bv4BCfC2UgvtGWzzFUuDJ8YrJiHL9FlR99sGKo= 11 | github.com/dgryski/go-mph v0.0.0-20191204212658-d9f21d2b3624/go.mod h1:sm2LH0eB9hET2mL0iPfQPPhbBBZgIdeJ6vA/6PfjIaQ= 12 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 13 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 14 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 15 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 16 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hasher 2 | Hasher is a tool to automate the creation of methods and tables for a string → uint32 mapper. It uses the fact that all keys are known apriori, allowing it to generate a very efficient hashtable. It has been built to work with `go generate`. New keys can be added by appending more constants to the list and rerunning `hasher`. The new keys will be assigned new ID's. Running `hasher` changes _all_ ID's, so do not store them in a file or database! 3 | 4 | It is really a mix of https://github.com/golang/tools/blob/master/cmd/stringer and https://github.com/golang/net/tree/master/html/atom with some customization. 5 | 6 | For example, given this snippet, 7 | ``` go 8 | package painkiller 9 | 10 | type Pill uint32 11 | 12 | const ( 13 | Placebo Pill = iota 14 | Aspirin 15 | Ibuprofen 16 | Paracetamol 17 | Acid // lysergic-acid-diethlamide 18 | ) 19 | ``` 20 | 21 | running this command 22 | 23 | hasher -type=Pill -file=pill.go 24 | 25 | in the same directory will __OVERWRITE__ the same file with the constant list itself (except with different values) and add tables, hashes and conversion functions between `string` and `uint32`. The output file is thus also the input file, and the tool can run over it indefinitely! This outputs 26 | 27 | ``` go 28 | package painkiller 29 | 30 | // Pill defines perfect hashes for a predefined list of strings 31 | type Pill uint32 32 | 33 | // Unique hash definitions to be used instead of strings 34 | const ( 35 | Aspirin Pill = 0x7 // aspirin 36 | Ibuprofen Pill = 0x709 // ibuprofen 37 | Acid Pill = 0x1a19 // lysergic-acid-diethlamide 38 | Paracetamol Pill = 0x100b // paracetamol 39 | Placebo Pill = 0x3307 // placebo 40 | ) 41 | 42 | func (i Pill) String() string { /* ... */ } 43 | 44 | func ToPill(s []byte) Pill { /* ... */ } 45 | 46 | /* ... */ 47 | ``` 48 | 49 | ### Amending 50 | 51 | To add more keys to the table, just add more constants to the file: 52 | 53 | ``` go 54 | // ... 55 | Aspirin Pill = 0x7 56 | Ibuprofen Pill = 0x709 57 | Paracetamol Pill = 0x100b 58 | Placebo Pill = 0x1b07 59 | NewPillA 60 | NewPillB 61 | // ... 62 | ``` 63 | 64 | or update the constant name or the comment value and rerun `hasher`! 65 | 66 | ## Installation 67 | Run the following command 68 | 69 | go get github.com/tdewolff/hasher 70 | 71 | ## Usage 72 | Typically this process would be run using go generate, like this: 73 | ``` go 74 | //go:generate hasher -type=Pill -file=pill.go 75 | ``` 76 | but `hasher` adds that row itself too, so you can use the command-line just as well `hasher -type=Pill -file=pill.go`. Just make sure the file consists solely of the new type and the constants of that type, because it will be overwritten! 77 | 78 | It must have the -type and -file flag, that accepts a single type and filename respectively. 79 | 80 | ## Lower- and uppercase and dashes 81 | By default, the first uppercase of the constants is lowered and any uppercase after an underscore. Any underscore is replace by a dash. So that: 82 | ``` go 83 | fmt.Print(painkiller.Aspirin) // aspirin 84 | fmt.Print(painkiller.StrongMorphine) // strongMorphine 85 | fmt.Print(painkiller.Amitriptyline_Gabapentin) // amitriptyline-gabapentin 86 | ``` 87 | 88 | But you can specify the name explicitly in the comment after the constant. See the initial example. 89 | 90 | ## Hash to string 91 | Translate the value of a Pill constant to the string representation of the respective constant name, so that the call `fmt.Print(painkiller.Aspirin)` will print the string `"aspirin"`. 92 | ``` go 93 | func (Pill) String() string 94 | ``` 95 | 96 | ## String to hash 97 | Translate a string to a value of a Pill constant with the same name. So that `ToPill("aspirin") == painkiller.Aspirin`. This function is called `"To" + typeName` so it changes name when the type name changes. 98 | ``` go 99 | func ToPill(s []byte) Pill 100 | ``` 101 | 102 | ## Performance 103 | The tool might be slow for large batches of constants, because it can have difficulty finding a hash that minimizes the table. For example, [the parse.Html hash.go](https://github.com/tdewolff/parse/blob/master/html/hash.go) takes about 10 seconds. 104 | 105 | The performance of lookups depends on the use case. Because a call to `ToHash` always needs to check the result, it is slower than a direct string comparison and is `O(n)`. However, comparison of a hash (int) is very fast and `O(1)`. 106 | 107 | ## License 108 | Released under the [BSD license](LICENSE.md). -------------------------------------------------------------------------------- /hasher_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "math/rand" 6 | "testing" 7 | 8 | mphCespare "github.com/cespare/mph" 9 | mphDgryski "github.com/dgryski/go-mph" 10 | ) 11 | 12 | var bkeys, bkeys1, bkeys2, bkeys3, bkeys4, bkeys5, bkeys6, bkeys7, bkeys8 [][]byte 13 | var skeys, skeys1, skeys2, skeys3, skeys4, skeys5, skeys6, skeys7, skeys8 []string 14 | 15 | var mph1Table *mphCespare.Table 16 | var mph1KeyContent uint32 17 | 18 | var mph2Table *mphDgryski.Table 19 | var mph2KeyContent int32 20 | 21 | func init() { 22 | for s := range HashMap { 23 | bkeys = append(bkeys, []byte(s)) 24 | skeys = append(skeys, s) 25 | if len(s) == 1 { 26 | bkeys1 = append(bkeys1, []byte(s)) 27 | skeys1 = append(skeys1, s) 28 | } else if len(s) == 2 { 29 | bkeys2 = append(bkeys2, []byte(s)) 30 | skeys2 = append(skeys2, s) 31 | } else if len(s) == 3 { 32 | bkeys3 = append(bkeys3, []byte(s)) 33 | skeys3 = append(skeys3, s) 34 | } else if len(s) == 4 { 35 | bkeys4 = append(bkeys4, []byte(s)) 36 | skeys4 = append(skeys4, s) 37 | } else if len(s) == 5 { 38 | bkeys5 = append(bkeys5, []byte(s)) 39 | skeys5 = append(skeys5, s) 40 | } else if len(s) == 6 { 41 | bkeys6 = append(bkeys6, []byte(s)) 42 | skeys6 = append(skeys6, s) 43 | } else if len(s) == 7 { 44 | bkeys7 = append(bkeys7, []byte(s)) 45 | skeys7 = append(skeys7, s) 46 | } else if len(s) == 8 { 47 | bkeys8 = append(bkeys8, []byte(s)) 48 | skeys8 = append(skeys8, s) 49 | } 50 | } 51 | 52 | mph1Table = mphCespare.Build(skeys) 53 | mph1KeyContent, _ = mph1Table.Lookup("content") 54 | 55 | mph2Table = mphDgryski.New(skeys) 56 | mph2KeyContent = mph2Table.Query("content") 57 | } 58 | 59 | func TestCollision(t *testing.T) { 60 | for i := 0; i < 1000; i++ { 61 | n := rand.Intn(10) 62 | b := make([]byte, n) 63 | for j := 0; j < n; j++ { 64 | b[j] = 'a' + byte(rand.Intn(int('z'-'a'+1))) 65 | } 66 | 67 | h := ToHash(b) 68 | if h != 0 && !bytes.Equal(b, h.Bytes()) { 69 | t.Errorf("bad: '%s' == %d == '%s'", string(b), h, string(h.Bytes())) 70 | } 71 | } 72 | } 73 | 74 | func BenchmarkMatchString(b *testing.B) { 75 | n := 0 76 | for i := 0; i < b.N; i++ { 77 | j := i % len(skeys) 78 | if skeys[j] == "content" { 79 | n++ 80 | } 81 | } 82 | } 83 | 84 | func BenchmarkMatchMap(b *testing.B) { 85 | n := 0 86 | for i := 0; i < b.N; i++ { 87 | j := i % len(skeys) 88 | if HashMap[skeys[j]] == Content { 89 | n++ 90 | } 91 | } 92 | } 93 | 94 | func BenchmarkMatchHash(b *testing.B) { 95 | n := 0 96 | for i := 0; i < b.N; i++ { 97 | j := i % len(bkeys) 98 | if ToHash(bkeys[j]) == Content { 99 | n++ 100 | } 101 | } 102 | } 103 | 104 | func BenchmarkMatchMPHCespare(b *testing.B) { 105 | n := 0 106 | for i := 0; i < b.N; i++ { 107 | j := i % len(skeys) 108 | if key, _ := mph1Table.Lookup(skeys[j]); key == mph1KeyContent { 109 | n++ 110 | } 111 | } 112 | } 113 | 114 | func BenchmarkMatchMPHDgryski(b *testing.B) { 115 | n := 0 116 | for i := 0; i < b.N; i++ { 117 | j := i % len(skeys) 118 | if mph2Table.Query(skeys[j]) == mph2KeyContent { 119 | n++ 120 | } 121 | } 122 | } 123 | 124 | var h Hash 125 | var k int32 126 | 127 | func BenchmarkMap(b *testing.B) { 128 | for i := 0; i < b.N; i++ { 129 | j := i % len(skeys) 130 | h = HashMap[skeys[j]] 131 | } 132 | } 133 | 134 | func BenchmarkHash(b *testing.B) { 135 | for i := 0; i < b.N; i++ { 136 | j := i % len(bkeys) 137 | h = ToHash(bkeys[j]) 138 | } 139 | } 140 | 141 | func BenchmarkMapLen1(b *testing.B) { 142 | for i := 0; i < b.N; i++ { 143 | j := i % len(skeys1) 144 | h = HashMap[skeys1[j]] 145 | } 146 | } 147 | 148 | func BenchmarkMapLen2(b *testing.B) { 149 | for i := 0; i < b.N; i++ { 150 | j := i % len(skeys2) 151 | h = HashMap[skeys2[j]] 152 | } 153 | } 154 | 155 | func BenchmarkMapLen3(b *testing.B) { 156 | for i := 0; i < b.N; i++ { 157 | j := i % len(skeys3) 158 | h = HashMap[skeys3[j]] 159 | } 160 | } 161 | 162 | func BenchmarkMapLen4(b *testing.B) { 163 | for i := 0; i < b.N; i++ { 164 | j := i % len(skeys4) 165 | h = HashMap[skeys4[j]] 166 | } 167 | } 168 | 169 | func BenchmarkMapLen5(b *testing.B) { 170 | for i := 0; i < b.N; i++ { 171 | j := i % len(skeys5) 172 | h = HashMap[skeys5[j]] 173 | } 174 | } 175 | 176 | func BenchmarkMapLen6(b *testing.B) { 177 | for i := 0; i < b.N; i++ { 178 | j := i % len(skeys6) 179 | h = HashMap[skeys6[j]] 180 | } 181 | } 182 | 183 | func BenchmarkMapLen7(b *testing.B) { 184 | for i := 0; i < b.N; i++ { 185 | j := i % len(skeys7) 186 | h = HashMap[skeys7[j]] 187 | } 188 | } 189 | 190 | func BenchmarkMapLen8(b *testing.B) { 191 | for i := 0; i < b.N; i++ { 192 | j := i % len(skeys8) 193 | h = HashMap[skeys8[j]] 194 | } 195 | } 196 | 197 | func BenchmarkHashLen1(b *testing.B) { 198 | for i := 0; i < b.N; i++ { 199 | j := i % len(bkeys1) 200 | h = ToHash(bkeys1[j]) 201 | } 202 | } 203 | 204 | func BenchmarkHashLen2(b *testing.B) { 205 | for i := 0; i < b.N; i++ { 206 | j := i % len(bkeys2) 207 | h = ToHash(bkeys2[j]) 208 | } 209 | } 210 | 211 | func BenchmarkHashLen3(b *testing.B) { 212 | for i := 0; i < b.N; i++ { 213 | j := i % len(bkeys3) 214 | h = ToHash(bkeys3[j]) 215 | } 216 | } 217 | 218 | func BenchmarkHashLen4(b *testing.B) { 219 | for i := 0; i < b.N; i++ { 220 | j := i % len(bkeys4) 221 | h = ToHash(bkeys4[j]) 222 | } 223 | } 224 | 225 | func BenchmarkHashLen5(b *testing.B) { 226 | for i := 0; i < b.N; i++ { 227 | j := i % len(bkeys5) 228 | h = ToHash(bkeys5[j]) 229 | } 230 | } 231 | 232 | func BenchmarkHashLen6(b *testing.B) { 233 | for i := 0; i < b.N; i++ { 234 | j := i % len(bkeys6) 235 | h = ToHash(bkeys6[j]) 236 | } 237 | } 238 | 239 | func BenchmarkHashLen7(b *testing.B) { 240 | for i := 0; i < b.N; i++ { 241 | j := i % len(bkeys7) 242 | h = ToHash(bkeys7[j]) 243 | } 244 | } 245 | 246 | func BenchmarkHashLen8(b *testing.B) { 247 | for i := 0; i < b.N; i++ { 248 | j := i % len(bkeys8) 249 | h = ToHash(bkeys8[j]) 250 | } 251 | } 252 | 253 | //func BenchmarkHash2Len1(b *testing.B) { 254 | // for i := 0; i < b.N; i++ { 255 | // j := i % len(bkeys1) 256 | // h = ToHash2(bkeys1[j]) 257 | // } 258 | //} 259 | // 260 | //func BenchmarkHash2Len2(b *testing.B) { 261 | // for i := 0; i < b.N; i++ { 262 | // j := i % len(bkeys2) 263 | // h = ToHash2(bkeys2[j]) 264 | // } 265 | //} 266 | // 267 | //func BenchmarkHash2Len3(b *testing.B) { 268 | // for i := 0; i < b.N; i++ { 269 | // j := i % len(bkeys3) 270 | // h = ToHash2(bkeys3[j]) 271 | // } 272 | //} 273 | // 274 | //func BenchmarkHash2Len4(b *testing.B) { 275 | // for i := 0; i < b.N; i++ { 276 | // j := i % len(bkeys4) 277 | // h = ToHash2(bkeys4[j]) 278 | // } 279 | //} 280 | // 281 | //func BenchmarkHash2Len5(b *testing.B) { 282 | // for i := 0; i < b.N; i++ { 283 | // j := i % len(bkeys5) 284 | // h = ToHash2(bkeys5[j]) 285 | // } 286 | //} 287 | // 288 | //func BenchmarkHash2Len6(b *testing.B) { 289 | // for i := 0; i < b.N; i++ { 290 | // j := i % len(bkeys6) 291 | // h = ToHash2(bkeys6[j]) 292 | // } 293 | //} 294 | // 295 | //func BenchmarkHash2Len7(b *testing.B) { 296 | // for i := 0; i < b.N; i++ { 297 | // j := i % len(bkeys7) 298 | // h = ToHash2(bkeys7[j]) 299 | // } 300 | //} 301 | // 302 | //func BenchmarkHash2Len8(b *testing.B) { 303 | // for i := 0; i < b.N; i++ { 304 | // j := i % len(bkeys8) 305 | // h = ToHash2(bkeys8[j]) 306 | // } 307 | //} 308 | // 309 | //func BenchmarkHash3Len1(b *testing.B) { 310 | // for i := 0; i < b.N; i++ { 311 | // j := i % len(bkeys1) 312 | // h = ToHash3(bkeys1[j]) 313 | // } 314 | //} 315 | // 316 | //func BenchmarkHash3Len2(b *testing.B) { 317 | // for i := 0; i < b.N; i++ { 318 | // j := i % len(bkeys2) 319 | // h = ToHash3(bkeys2[j]) 320 | // } 321 | //} 322 | // 323 | //func BenchmarkHash3Len3(b *testing.B) { 324 | // for i := 0; i < b.N; i++ { 325 | // j := i % len(bkeys3) 326 | // h = ToHash3(bkeys3[j]) 327 | // } 328 | //} 329 | // 330 | //func BenchmarkHash3Len4(b *testing.B) { 331 | // for i := 0; i < b.N; i++ { 332 | // j := i % len(bkeys4) 333 | // h = ToHash3(bkeys4[j]) 334 | // } 335 | //} 336 | // 337 | //func BenchmarkHash3Len5(b *testing.B) { 338 | // for i := 0; i < b.N; i++ { 339 | // j := i % len(bkeys5) 340 | // h = ToHash3(bkeys5[j]) 341 | // } 342 | //} 343 | // 344 | //func BenchmarkHash3Len6(b *testing.B) { 345 | // for i := 0; i < b.N; i++ { 346 | // j := i % len(bkeys6) 347 | // h = ToHash3(bkeys6[j]) 348 | // } 349 | //} 350 | // 351 | //func BenchmarkHash3Len7(b *testing.B) { 352 | // for i := 0; i < b.N; i++ { 353 | // j := i % len(bkeys7) 354 | // h = ToHash3(bkeys7[j]) 355 | // } 356 | //} 357 | // 358 | //func BenchmarkHash3Len8(b *testing.B) { 359 | // for i := 0; i < b.N; i++ { 360 | // j := i % len(bkeys8) 361 | // h = ToHash3(bkeys8[j]) 362 | // } 363 | //} 364 | // 365 | //func BenchmarkHashMetroLen1(b *testing.B) { 366 | // for i := 0; i < b.N; i++ { 367 | // j := i % len(bkeys1) 368 | // h = ToHashMetro(bkeys1[j]) 369 | // } 370 | //} 371 | // 372 | //func BenchmarkHashMetroLen2(b *testing.B) { 373 | // for i := 0; i < b.N; i++ { 374 | // j := i % len(bkeys2) 375 | // h = ToHashMetro(bkeys2[j]) 376 | // } 377 | //} 378 | // 379 | //func BenchmarkHashMetroLen3(b *testing.B) { 380 | // for i := 0; i < b.N; i++ { 381 | // j := i % len(bkeys3) 382 | // h = ToHashMetro(bkeys3[j]) 383 | // } 384 | //} 385 | // 386 | //func BenchmarkHashMetroLen4(b *testing.B) { 387 | // for i := 0; i < b.N; i++ { 388 | // j := i % len(bkeys4) 389 | // h = ToHashMetro(bkeys4[j]) 390 | // } 391 | //} 392 | // 393 | //func BenchmarkHashMetroLen5(b *testing.B) { 394 | // for i := 0; i < b.N; i++ { 395 | // j := i % len(bkeys5) 396 | // h = ToHashMetro(bkeys5[j]) 397 | // } 398 | //} 399 | // 400 | //func BenchmarkHashMetroLen6(b *testing.B) { 401 | // for i := 0; i < b.N; i++ { 402 | // j := i % len(bkeys6) 403 | // h = ToHashMetro(bkeys6[j]) 404 | // } 405 | //} 406 | // 407 | //func BenchmarkHashMetroLen7(b *testing.B) { 408 | // for i := 0; i < b.N; i++ { 409 | // j := i % len(bkeys7) 410 | // h = ToHashMetro(bkeys7[j]) 411 | // } 412 | //} 413 | // 414 | //func BenchmarkHashMetroLen8(b *testing.B) { 415 | // for i := 0; i < b.N; i++ { 416 | // j := i % len(bkeys8) 417 | // h = ToHashMetro(bkeys8[j]) 418 | // } 419 | //} 420 | -------------------------------------------------------------------------------- /hasher.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 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. 4 | // 5 | package main // import "github.com/tdewolff/hasher" 6 | 7 | import ( 8 | "bytes" 9 | "flag" 10 | "fmt" 11 | "go/ast" 12 | "go/format" 13 | "go/parser" 14 | "go/token" 15 | "go/types" 16 | "io/ioutil" 17 | "log" 18 | "math/rand" 19 | "os" 20 | "sort" 21 | "strings" 22 | ) 23 | 24 | var ( 25 | typeName = flag.String("type", "", "type name; must be set") 26 | fileName = flag.String("file", "", "file name, this file will be OVERWRITTEN and should only contain the type; must be set") 27 | ) 28 | 29 | // Usage is a replacement usage function for the flags package. 30 | func Usage() { 31 | fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) 32 | fmt.Fprintf(os.Stderr, "\thasher -type T -file F\n") 33 | fmt.Fprintf(os.Stderr, "For more information, see:\n") 34 | fmt.Fprintf(os.Stderr, "\thttp://github.com/tdewolff/hasher\n") 35 | fmt.Fprintf(os.Stderr, "Flags:\n") 36 | flag.PrintDefaults() 37 | os.Exit(2) 38 | } 39 | 40 | func main() { 41 | log.SetFlags(0) 42 | log.SetPrefix("hasher: ") 43 | flag.Usage = Usage 44 | flag.Parse() 45 | if len(*typeName) == 0 || len(*fileName) == 0 { 46 | flag.Usage() 47 | os.Exit(2) 48 | } 49 | 50 | // We accept either one directory or a list of files. Which do we have? 51 | args := flag.Args() 52 | if len(args) == 0 { 53 | // Default: process whole package in current directory. 54 | args = []string{"."} 55 | } 56 | 57 | // Parse the package once. 58 | var g Generator 59 | g.parsePackage(".", *fileName, nil) 60 | 61 | // Print the header and package clause. 62 | g.Printf("package %s\n\n", g.pkg.name) 63 | g.generate() 64 | 65 | // Format the output. 66 | src := g.format() 67 | 68 | // Write to file. 69 | if err := ioutil.WriteFile(*fileName, src, 0644); err != nil { 70 | log.Fatalf("writing output: %s", err) 71 | } 72 | } 73 | 74 | // isDirectory reports whether the named file is a directory. 75 | func isDirectory(name string) bool { 76 | info, err := os.Stat(name) 77 | if err != nil { 78 | log.Fatal(err) 79 | } 80 | return info.IsDir() 81 | } 82 | 83 | // Generator holds the state of the analysis. Primarily used to buffer 84 | // the output for format.Source. 85 | type Generator struct { 86 | buf bytes.Buffer // Accumulated output. 87 | pkg *Package // Package we are scanning. 88 | } 89 | 90 | func (g *Generator) Printf(format string, args ...interface{}) { 91 | fmt.Fprintf(&g.buf, format, args...) 92 | } 93 | 94 | type Pair struct { 95 | name string 96 | text string 97 | hash uint32 98 | } 99 | 100 | // File holds a single parsed file and associated data. 101 | type File struct { 102 | pkg *Package // Package to which this file belongs. 103 | file *ast.File // Parsed AST. 104 | // These fields are reset for each type being generated. 105 | typeName string // Name of the constant type. 106 | pairs []Pair // Accumulator for constant values of that type. 107 | } 108 | 109 | type Package struct { 110 | dir string 111 | name string 112 | defs map[*ast.Ident]types.Object 113 | file *File 114 | typesPkg *types.Package 115 | } 116 | 117 | // parsePackage analyzes the single package constructed from the named files. 118 | // If text is non-nil, it is a string to be used instead of the content of the file, 119 | // to be used for testing. parsePackage exits if there is an error. 120 | func (g *Generator) parsePackage(directory string, name string, text interface{}) { 121 | g.pkg = new(Package) 122 | fs := token.NewFileSet() 123 | 124 | if !strings.HasSuffix(name, ".go") { 125 | log.Fatalf("parsing package: %s: has no suffix .go", name) 126 | } 127 | astFile, err := parser.ParseFile(fs, name, text, parser.ParseComments) 128 | if err != nil { 129 | log.Fatalf("parsing package: %s: %s", name, err) 130 | } 131 | file := &File{ 132 | file: astFile, 133 | pkg: g.pkg, 134 | } 135 | 136 | g.pkg.name = astFile.Name.Name 137 | g.pkg.file = file 138 | g.pkg.dir = directory 139 | // Type check the package. 140 | g.pkg.check(fs, astFile) 141 | } 142 | 143 | // check type-checks the package. The package must be OK to proceed. 144 | func (pkg *Package) check(fs *token.FileSet, astFile *ast.File) { 145 | pkg.defs = make(map[*ast.Ident]types.Object) 146 | } 147 | 148 | // generate produces the String method for the named type. 149 | func (g *Generator) generate() { 150 | all := []Pair{} 151 | file := g.pkg.file 152 | // Set the state for this run of the walker. 153 | file.typeName = *typeName 154 | file.pairs = nil 155 | if file.file != nil { 156 | ast.Inspect(file.file, file.genDecl) 157 | all = file.pairs 158 | } 159 | 160 | if len(all) == 0 { 161 | log.Fatalf("no values defined for type %s", *typeName) 162 | } 163 | g.buildHashtable(all) 164 | } 165 | 166 | // format returns the gofmt-ed contents of the Generator's buffer. 167 | func (g *Generator) format() []byte { 168 | src, err := format.Source(g.buf.Bytes()) 169 | if err != nil { 170 | // Should never happen, but can arise when developing this code. 171 | // The user can compile the output to see the error. 172 | log.Printf("warning: internal error: invalid Go generated: %s", err) 173 | log.Printf("warning: compile the package to analyze the error") 174 | return g.buf.Bytes() 175 | } 176 | return src 177 | } 178 | 179 | // genDecl processes one declaration clause. 180 | func (f *File) genDecl(node ast.Node) bool { 181 | decl, ok := node.(*ast.GenDecl) 182 | if !ok || decl.Tok != token.CONST { 183 | // We only care about const declarations. 184 | return true 185 | } 186 | // The name of the type of the constants we are declaring. 187 | // Can change if this is a multi-element declaration. 188 | typ := "" 189 | // Loop over the elements of the declaration. Each element is a ValueSpec: 190 | // a list of names possibly followed by a type, possibly followed by values. 191 | // If the type and value are both missing, we carry down the type (and value, 192 | // but the "go/types" package takes care of that). 193 | for _, spec := range decl.Specs { 194 | vspec := spec.(*ast.ValueSpec) // Guaranteed to succeed as this is CONST. 195 | if vspec.Type == nil && len(vspec.Values) > 0 { 196 | // "X = 1". With no type but a value, the constant is untyped. 197 | // Skip this vspec and reset the remembered type. 198 | typ = "" 199 | continue 200 | } 201 | if vspec.Type != nil { 202 | // "X T". We have a type. Remember it. 203 | ident, ok := vspec.Type.(*ast.Ident) 204 | if !ok { 205 | continue 206 | } 207 | typ = ident.Name 208 | } 209 | if typ != f.typeName { 210 | // This is not the type we're looking for. 211 | continue 212 | } 213 | 214 | var text string 215 | if vspec.Comment != nil && len(vspec.Comment.List) > 0 && len(vspec.Comment.List[0].Text) > 2 { 216 | text = strings.TrimSpace(vspec.Comment.List[0].Text[2:]) 217 | } 218 | // We now have a list of names (from one line of source code) all being 219 | // declared with the desired type. 220 | // Grab their names and actual values and store them in f.values. 221 | name := vspec.Names[0] 222 | if name.Name == "_" { 223 | continue 224 | } 225 | 226 | if len(text) == 0 && len(name.Name) > 0 { 227 | b := []byte(name.Name) 228 | b[0] = bytes.ToLower(b[:1])[0] 229 | for j, x := range b { 230 | if x == '_' { 231 | b[j] = '-' 232 | if len(b) > j+1 { 233 | b[j+1] = bytes.ToLower(b[j+1 : j+2])[0] 234 | } 235 | } 236 | } 237 | text = string(b) 238 | } 239 | f.pairs = append(f.pairs, Pair{name.Name, text, 0}) 240 | } 241 | return false 242 | } 243 | 244 | type byText []Pair 245 | 246 | func (x byText) Less(i, j int) bool { return x[i].text < x[j].text } 247 | func (x byText) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 248 | func (x byText) Len() int { return len(x) } 249 | 250 | // buildMap handles the case where the space is so sparse a map is a reasonable fallback. 251 | // It's a rare situation but has simple code. 252 | func (g *Generator) buildHashtable(all []Pair) { 253 | sort.Sort(byText(all)) 254 | 255 | // uniq - lists have dups 256 | // compute max len too 257 | maxLen := 0 258 | w := 0 259 | for _, pair := range all { 260 | if w == 0 || all[w-1].text != pair.text { 261 | if maxLen < len(pair.text) { 262 | maxLen = len(pair.text) 263 | } 264 | all[w].text = pair.text 265 | w++ 266 | } 267 | } 268 | all = all[:w] 269 | 270 | layout := make([]string, 0, len(all)) 271 | for i := 0; i < len(all); i++ { 272 | layout = append(layout, all[i].text) 273 | } 274 | 275 | // Find hash that minimizes table size. 276 | var best *table 277 | i := 0 278 | for ; i < 1000000; i++ { 279 | if best != nil && 1<<(best.k-1) < len(all) { 280 | break 281 | } 282 | h := rand.Uint32() 283 | for k := uint(0); k <= 16; k++ { 284 | if best != nil && k >= best.k { 285 | break 286 | } 287 | var t table 288 | if t.init(h, k, layout) { 289 | best = &t 290 | break 291 | } 292 | } 293 | } 294 | if best == nil { 295 | fmt.Fprintf(os.Stderr, "failed to construct string table\n") 296 | os.Exit(1) 297 | } 298 | fmt.Printf("%d rounds before best match found\n", i) 299 | 300 | // Remove strings that are substrings of other strings 301 | for changed := true; changed; { 302 | changed = false 303 | for i, s := range layout { 304 | if s == "" { 305 | continue 306 | } 307 | for j, t := range layout { 308 | if i != j && t != "" && strings.Contains(s, t) { 309 | changed = true 310 | layout[j] = "" 311 | } 312 | } 313 | } 314 | } 315 | 316 | // Join strings where one suffix matches another prefix. 317 | for { 318 | // Find best i, j, k such that layout[i][len-k:] == layout[j][:k], 319 | // maximizing overlap length k. 320 | besti := -1 321 | bestj := -1 322 | bestk := 0 323 | for i, s := range layout { 324 | if s == "" { 325 | continue 326 | } 327 | for j, t := range layout { 328 | if i == j { 329 | continue 330 | } 331 | for k := bestk + 1; k <= len(s) && k <= len(t); k++ { 332 | if s[len(s)-k:] == t[:k] { 333 | besti = i 334 | bestj = j 335 | bestk = k 336 | } 337 | } 338 | } 339 | } 340 | if bestk > 0 { 341 | layout[besti] += layout[bestj][bestk:] 342 | layout[bestj] = "" 343 | continue 344 | } 345 | break 346 | } 347 | 348 | text := strings.Join(layout, "") 349 | 350 | hash := map[string]uint32{} 351 | for i, pair := range all { 352 | off := strings.Index(text, pair.text) 353 | if off < 0 { 354 | panic("lost string " + pair.text) 355 | } 356 | all[i].hash = uint32(off<<8 | len(pair.text)) 357 | hash[pair.text] = all[i].hash 358 | } 359 | 360 | g.Printf("// uses github.com/tdewolff/hasher\n") 361 | g.Printf("//go:generate hasher -type=%s -file=%s\n\n", *typeName, *fileName) 362 | //g.Printf("import \"github.com/dgryski/go-metro\"\n\n") 363 | g.Printf("// %s defines perfect hashes for a predefined list of strings\n", *typeName) 364 | g.Printf("type %s uint32\n\n", *typeName) 365 | g.Printf("// Identifiers for the hashes associated with the text in the comments.\n") 366 | g.Printf("const (\n") 367 | for _, pair := range all { 368 | g.Printf("\t%s %s = %#x // %s\n", pair.name, *typeName, pair.hash, pair.text) 369 | } 370 | g.Printf(")\n\n") 371 | 372 | g.Printf("//var %[1]sMap = map[string]%[1]s{\n", *typeName) 373 | for _, pair := range all { 374 | g.Printf("//\t\"%s\": %s,\n", pair.text, pair.name) 375 | } 376 | g.Printf("//}\n") 377 | 378 | g.Printf(stringFunc, *typeName) 379 | g.Printf(hashFunc, *typeName) 380 | g.Printf("\nconst _%s_hash0 = %#x\n", *typeName, best.h0) 381 | g.Printf("const _%s_maxLen = %d\n", *typeName, maxLen) 382 | g.Printf("var _%s_text = []byte(\"\" +\n", *typeName) 383 | for len(text) > 60 { 384 | g.Printf("%q +\n\t", text[:60]) 385 | text = text[60:] 386 | } 387 | g.Printf("%q)\n", text) 388 | g.Printf("\nvar _%s_table = [1<<%d]%s{\n", *typeName, best.k, *typeName) 389 | for i, s := range best.tab { 390 | if s == "" { 391 | continue 392 | } 393 | g.Printf("\t%#x: %#x, // %s\n", i, hash[s], s) 394 | } 395 | g.Printf("}\n") 396 | 397 | //k := int(math.Log2(float64(len(keys)))) 398 | //keys := []string{} 399 | //for _, pair := range all { 400 | // keys = append(keys, pair.text) 401 | //} 402 | //metroTable := New(keys) 403 | 404 | //metroMap := make([]string, len(keys)) 405 | //for _, pair := range all { 406 | // i := metroTable.Query(pair.text) 407 | // metroMap[i] = pair.text 408 | //} 409 | //g.Printf(hashMetroFunc, *typeName) 410 | //g.Printf("\nvar _%s_values = []uint32{", *typeName) 411 | //for i, val := range metroTable.values { 412 | // if i != 0 { 413 | // g.Printf(", ") 414 | // } 415 | // g.Printf("%v", val) 416 | //} 417 | //g.Printf("}\n") 418 | //g.Printf("\nvar _%s_seeds = []int32{", *typeName) 419 | //for i, val := range metroTable.seeds { 420 | // if i != 0 { 421 | // g.Printf(", ") 422 | // } 423 | // g.Printf("%v", val) 424 | //} 425 | //g.Printf("}\n") 426 | //g.Printf("\nvar _%s_table_metro = [1<<%d]%s{\n", *typeName, k+1, *typeName) 427 | //for i, s := range metroMap { 428 | // if s == "" { 429 | // continue 430 | // } 431 | // g.Printf("\t%#x: %#x, // %s\n", i, hash[s], s) 432 | //} 433 | //g.Printf("}\n") 434 | } 435 | 436 | const stringFunc = ` 437 | // String returns the text associated with the hash. 438 | func (i %[1]s) String() string { 439 | return string(i.Bytes()) 440 | } 441 | 442 | // Bytes returns the text associated with the hash. 443 | func (i %[1]s) Bytes() []byte { 444 | start := uint32(i >> 8) 445 | n := uint32(i & 0xff) 446 | if start+n > uint32(len(_%[1]s_text)) { 447 | return []byte{} 448 | } 449 | return _%[1]s_text[start : start+n] 450 | } 451 | ` 452 | 453 | const hashFunc = `// To%[1]s returns a hash %[1]s for a given []byte. %[1]s is a uint32 that is associated with the text in []byte. It returns zero if no match found. 454 | func To%[1]s(s []byte) %[1]s { 455 | if len(s) == 0 || len(s) > _Hash_maxLen { 456 | return 0 457 | } 458 | //if 3 < len(s) { 459 | // return %[1]sMap[string(s)] 460 | //} 461 | h := uint32(_%[1]s_hash0) 462 | for i := 0; i < len(s); i++ { 463 | h ^= uint32(s[i]) 464 | h *= 16777619 465 | } 466 | if i := _%[1]s_table[h&uint32(len(_%[1]s_table)-1)]; int(i&0xff) == len(s) { 467 | t := _%[1]s_text[i>>8 : i>>8+i&0xff] 468 | for i := 0; i < len(s); i++ { 469 | if t[i] != s[i] { 470 | goto NEXT 471 | } 472 | } 473 | return i 474 | } 475 | NEXT: 476 | if i := _%[1]s_table[(h>>16)&uint32(len(_%[1]s_table)-1)]; int(i&0xff) == len(s) { 477 | t := _%[1]s_text[i>>8 : i>>8+i&0xff] 478 | for i := 0; i < len(s); i++ { 479 | if t[i] != s[i] { 480 | return 0 481 | } 482 | } 483 | return i 484 | } 485 | return 0 486 | } 487 | ` 488 | 489 | const hashMetroFunc = ` 490 | func To%[1]sMetro(s []byte) %[1]s { 491 | if len(s) == 0 || len(s) > _%[1]s_maxLen { 492 | return 0 493 | } 494 | size := uint64(len(_%[1]s_values)) 495 | hash := metro.Hash64(s, 0) 496 | i := hash & (size - 1) 497 | seed := _%[1]s_seeds[i] 498 | 499 | var h uint32 500 | if seed < 0 { 501 | h = _%[1]s_values[-seed-1] 502 | } else { 503 | i = xorshiftMult64(uint64(seed)+hash) & (size - 1) 504 | h = _%[1]s_values[i] 505 | } 506 | 507 | if i := _%[1]s_table_metro[h&uint32(len(_%[1]s_table_metro)-1)]; int(i&0xff) == len(s) { 508 | t := _%[1]s_text[i>>8 : i>>8+i&0xff] 509 | for i := 0; i < len(s); i++ { 510 | if t[i] != s[i] { 511 | return 0 512 | } 513 | } 514 | return i 515 | } 516 | return 0 517 | } 518 | 519 | func xorshiftMult64(x uint64) uint64 { 520 | x ^= x >> 12 // a 521 | x ^= x << 25 // b 522 | x ^= x >> 27 // c 523 | return x * 2685821657736338717 524 | } 525 | ` 526 | 527 | //////////////////////////////////////////////////////////////// 528 | 529 | // fnv computes the FNV hash with an arbitrary starting value h. 530 | func fnv(h uint32, s string) uint32 { 531 | for i := 0; i < len(s); i++ { 532 | h ^= uint32(s[i]) 533 | h *= 16777619 534 | } 535 | return h 536 | } 537 | 538 | // A table represents an attempt at constructing the lookup table. 539 | // The lookup table uses cuckoo hashing, meaning that each string 540 | // can be found in one of two positions. 541 | type table struct { 542 | h0 uint32 543 | k uint 544 | mask uint32 545 | tab []string 546 | } 547 | 548 | // hash returns the two hashes for s. 549 | func (t *table) hash(s string) (h1, h2 uint32) { 550 | h := fnv(t.h0, s) 551 | h1 = h & t.mask 552 | h2 = (h >> 16) & t.mask 553 | return 554 | } 555 | 556 | // init initializes the table with the given parameters. 557 | // h0 is the initial hash value, 558 | // k is the number of bits of hash value to use, and 559 | // x is the list of strings to store in the table. 560 | // init returns false if the table cannot be constructed. 561 | func (t *table) init(h0 uint32, k uint, x []string) bool { 562 | t.h0 = h0 563 | t.k = k 564 | t.tab = make([]string, 1< len(t.tab) { 599 | return false 600 | } 601 | s := t.tab[i] 602 | h1, h2 := t.hash(s) 603 | j := h1 + h2 - i 604 | if t.tab[j] != "" && !t.push(j, depth+1) { 605 | return false 606 | } 607 | t.tab[j] = s 608 | return true 609 | } 610 | -------------------------------------------------------------------------------- /data_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // uses github.com/tdewolff/hasher 4 | //go:generate hasher -type=Hash -file=data_test.go 5 | 6 | //import "github.com/dgryski/go-metro" 7 | 8 | // Hash defines perfect hashes for a predefined list of strings 9 | type Hash uint32 10 | 11 | // Identifiers for the hashes associated with the text in the comments. 12 | const ( 13 | A Hash = 0x1 // a 14 | Abbr Hash = 0x4 // abbr 15 | Accept Hash = 0x3206 // accept 16 | Accept_Charset Hash = 0x320e // accept-charset 17 | Accesskey Hash = 0x4409 // accesskey 18 | Acronym Hash = 0xbb07 // acronym 19 | Action Hash = 0x2b906 // action 20 | Address Hash = 0x67607 // address 21 | Align Hash = 0x1605 // align 22 | Alink Hash = 0xd205 // alink 23 | Allowfullscreen Hash = 0x23c0f // allowfullscreen 24 | Alt Hash = 0xeb03 // alt 25 | Annotation Hash = 0x2060a // annotation 26 | AnnotationXml Hash = 0x2060d // annotationXml 27 | Applet Hash = 0x16106 // applet 28 | Area Hash = 0x38604 // area 29 | Article Hash = 0x40707 // article 30 | Aside Hash = 0x8305 // aside 31 | Async Hash = 0xf705 // async 32 | Audio Hash = 0x11305 // audio 33 | Autocomplete Hash = 0x14a0c // autocomplete 34 | Autofocus Hash = 0x15609 // autofocus 35 | Autoplay Hash = 0x16b08 // autoplay 36 | Axis Hash = 0x17304 // axis 37 | B Hash = 0x101 // b 38 | Background Hash = 0x1e0a // background 39 | Base Hash = 0x44d04 // base 40 | Basefont Hash = 0x44d08 // basefont 41 | Bdi Hash = 0xcb03 // bdi 42 | Bdo Hash = 0x18a03 // bdo 43 | Bgcolor Hash = 0x19d07 // bgcolor 44 | Bgsound Hash = 0x1a407 // bgsound 45 | Big Hash = 0x1ac03 // big 46 | Blink Hash = 0x1af05 // blink 47 | Blockquote Hash = 0x1b40a // blockquote 48 | Body Hash = 0x4004 // body 49 | Border Hash = 0x33806 // border 50 | Br Hash = 0x202 // br 51 | Button Hash = 0x1be06 // button 52 | Canvas Hash = 0x7f06 // canvas 53 | Caption Hash = 0x27e07 // caption 54 | Center Hash = 0x62306 // center 55 | Challenge Hash = 0x1eb09 // challenge 56 | Charset Hash = 0x3907 // charset 57 | Checked Hash = 0x3ad07 // checked 58 | Cite Hash = 0xfb04 // cite 59 | Class Hash = 0x1c905 // class 60 | Classid Hash = 0x1c907 // classid 61 | Clear Hash = 0x40b05 // clear 62 | Code Hash = 0x1dc04 // code 63 | Codebase Hash = 0x44908 // codebase 64 | Codetype Hash = 0x1dc08 // codetype 65 | Col Hash = 0x19f03 // col 66 | Colgroup Hash = 0x1f408 // colgroup 67 | Color Hash = 0x19f05 // color 68 | Cols Hash = 0x20104 // cols 69 | Colspan Hash = 0x20107 // colspan 70 | Command Hash = 0x21307 // command 71 | Compact Hash = 0x21a07 // compact 72 | Content Hash = 0x58107 // content 73 | Contenteditable Hash = 0x5810f // contenteditable 74 | Contextmenu Hash = 0x3b60b // contextmenu 75 | Controls Hash = 0x22908 // controls 76 | Coords Hash = 0x23506 // coords 77 | Crossorigin Hash = 0x25a0b // crossorigin 78 | Data Hash = 0x4a604 // data 79 | Datalist Hash = 0x4a608 // datalist 80 | Datetime Hash = 0x2e908 // datetime 81 | Dd Hash = 0x31602 // dd 82 | Declare Hash = 0x8607 // declare 83 | Default Hash = 0x5407 // default 84 | DefaultChecked Hash = 0x4ea0e // defaultChecked 85 | DefaultMuted Hash = 0x54b0c // defaultMuted 86 | DefaultSelected Hash = 0x540f // defaultSelected 87 | Defer Hash = 0x6205 // defer 88 | Del Hash = 0x7203 // del 89 | Desc Hash = 0x7c04 // desc 90 | Details Hash = 0x9207 // details 91 | Dfn Hash = 0xab03 // dfn 92 | Dialog Hash = 0xcc06 // dialog 93 | Dir Hash = 0xd903 // dir 94 | Dirname Hash = 0xd907 // dirname 95 | Disabled Hash = 0x10108 // disabled 96 | Div Hash = 0x10803 // div 97 | Dl Hash = 0x1aa02 // dl 98 | Download Hash = 0x47f08 // download 99 | Draggable Hash = 0x1cf09 // draggable 100 | Dropzone Hash = 0x41208 // dropzone 101 | Dt Hash = 0x5ff02 // dt 102 | Em Hash = 0x6e02 // em 103 | Embed Hash = 0x6e05 // embed 104 | Enabled Hash = 0x4e07 // enabled 105 | Enctype Hash = 0x2ce07 // enctype 106 | Face Hash = 0x62104 // face 107 | Fieldset Hash = 0x26b08 // fieldset 108 | Figcaption Hash = 0x27b0a // figcaption 109 | Figure Hash = 0x28f06 // figure 110 | Font Hash = 0x45104 // font 111 | Footer Hash = 0xee06 // footer 112 | For Hash = 0x29b03 // for 113 | ForeignObject Hash = 0x29b0d // foreignObject 114 | Foreignobject Hash = 0x2a80d // foreignobject 115 | Form Hash = 0x2b504 // form 116 | Formaction Hash = 0x2b50a // formaction 117 | Formenctype Hash = 0x2ca0b // formenctype 118 | Formmethod Hash = 0x2d50a // formmethod 119 | Formnovalidate Hash = 0x2df0e // formnovalidate 120 | Formtarget Hash = 0x2f40a // formtarget 121 | Frame Hash = 0xa305 // frame 122 | Frameborder Hash = 0x3330b // frameborder 123 | Frameset Hash = 0xa308 // frameset 124 | H1 Hash = 0x19b02 // h1 125 | H2 Hash = 0x32402 // h2 126 | H3 Hash = 0x34902 // h3 127 | H4 Hash = 0x37f02 // h4 128 | H5 Hash = 0x60102 // h5 129 | H6 Hash = 0x2fe02 // h6 130 | Head Hash = 0x36b04 // head 131 | Header Hash = 0x36b06 // header 132 | Headers Hash = 0x36b07 // headers 133 | Height Hash = 0x30006 // height 134 | Hgroup Hash = 0x30806 // hgroup 135 | Hidden Hash = 0x31406 // hidden 136 | High Hash = 0x32104 // high 137 | Hr Hash = 0xaf02 // hr 138 | Href Hash = 0xaf04 // href 139 | Hreflang Hash = 0xaf08 // hreflang 140 | Html Hash = 0x30404 // html 141 | Http_Equiv Hash = 0x3260a // http-equiv 142 | I Hash = 0x601 // i 143 | Icon Hash = 0x58004 // icon 144 | Id Hash = 0x8502 // id 145 | Iframe Hash = 0x33206 // iframe 146 | Image Hash = 0x33e05 // image 147 | Img Hash = 0x34303 // img 148 | Inert Hash = 0x53605 // inert 149 | Input Hash = 0x46c05 // input 150 | Ins Hash = 0x26303 // ins 151 | Isindex Hash = 0x17507 // isindex 152 | Ismap Hash = 0x34b05 // ismap 153 | Itemid Hash = 0xfc06 // itemid 154 | Itemprop Hash = 0x56e08 // itemprop 155 | Itemref Hash = 0x61b07 // itemref 156 | Itemscope Hash = 0x35609 // itemscope 157 | Itemtype Hash = 0x36008 // itemtype 158 | Kbd Hash = 0xca03 // kbd 159 | Keygen Hash = 0x4a06 // keygen 160 | Keytype Hash = 0x5b007 // keytype 161 | Kind Hash = 0xd604 // kind 162 | Label Hash = 0x7405 // label 163 | Lang Hash = 0xb304 // lang 164 | Language Hash = 0xb308 // language 165 | Legend Hash = 0x1d606 // legend 166 | Li Hash = 0x1702 // li 167 | Link Hash = 0xd304 // link 168 | List Hash = 0x4aa04 // list 169 | Listing Hash = 0x4aa07 // listing 170 | Longdesc Hash = 0x7808 // longdesc 171 | Loop Hash = 0x11e04 // loop 172 | Low Hash = 0x23e03 // low 173 | Main Hash = 0x1004 // main 174 | Malignmark Hash = 0xc10a // malignmark 175 | Manifest Hash = 0x65708 // manifest 176 | Map Hash = 0x16003 // map 177 | Mark Hash = 0xc704 // mark 178 | Marquee Hash = 0x69907 // marquee 179 | Math Hash = 0x36804 // math 180 | Max Hash = 0x37703 // max 181 | Maxlength Hash = 0x37709 // maxlength 182 | Media Hash = 0xde05 // media 183 | Mediagroup Hash = 0xde0a // mediagroup 184 | Menu Hash = 0x3bd04 // menu 185 | Meta Hash = 0x4b904 // meta 186 | Meter Hash = 0x2ef05 // meter 187 | Method Hash = 0x2d906 // method 188 | Mglyph Hash = 0x34406 // mglyph 189 | Mi Hash = 0x2c02 // mi 190 | Min Hash = 0x2c03 // min 191 | Mn Hash = 0x2e202 // mn 192 | Mo Hash = 0x4dd02 // mo 193 | Ms Hash = 0x35902 // ms 194 | Mtext Hash = 0x38105 // mtext 195 | Multiple Hash = 0x38f08 // multiple 196 | Muted Hash = 0x39705 // muted 197 | Name Hash = 0xdc04 // name 198 | Nav Hash = 0x1303 // nav 199 | Nobr Hash = 0x1a04 // nobr 200 | Noembed Hash = 0x6c07 // noembed 201 | Noframes Hash = 0xa108 // noframes 202 | Nohref Hash = 0xad06 // nohref 203 | Noresize Hash = 0x24a08 // noresize 204 | Noscript Hash = 0x31908 // noscript 205 | Noshade Hash = 0x4e507 // noshade 206 | Novalidate Hash = 0x2e30a // novalidate 207 | Nowrap Hash = 0x57706 // nowrap 208 | Object Hash = 0x2af06 // object 209 | Ol Hash = 0x12d02 // ol 210 | Onabort Hash = 0x1c207 // onabort 211 | Onafterprint Hash = 0x2830c // onafterprint 212 | Onbeforeprint Hash = 0x2bd0d // onbeforeprint 213 | Onbeforeunload Hash = 0x66a0e // onbeforeunload 214 | Onblur Hash = 0x14406 // onblur 215 | Oncancel Hash = 0x11708 // oncancel 216 | Oncanplay Hash = 0x18c09 // oncanplay 217 | Oncanplaythrough Hash = 0x18c10 // oncanplaythrough 218 | Onchange Hash = 0x42808 // onchange 219 | Onclick Hash = 0x6a407 // onclick 220 | Onclose Hash = 0x39c07 // onclose 221 | Oncontextmenu Hash = 0x3b40d // oncontextmenu 222 | Oncuechange Hash = 0x3c10b // oncuechange 223 | Ondblclick Hash = 0x3cc0a // ondblclick 224 | Ondrag Hash = 0x3d606 // ondrag 225 | Ondragend Hash = 0x3d609 // ondragend 226 | Ondragenter Hash = 0x3df0b // ondragenter 227 | Ondragleave Hash = 0x3ea0b // ondragleave 228 | Ondragover Hash = 0x3f50a // ondragover 229 | Ondragstart Hash = 0x3ff0b // ondragstart 230 | Ondrop Hash = 0x41006 // ondrop 231 | Ondurationchange Hash = 0x42010 // ondurationchange 232 | Onemptied Hash = 0x41709 // onemptied 233 | Onended Hash = 0x43007 // onended 234 | Onerror Hash = 0x43707 // onerror 235 | Onfocus Hash = 0x43e07 // onfocus 236 | Onhashchange Hash = 0x45e0c // onhashchange 237 | Oninput Hash = 0x46a07 // oninput 238 | Oninvalid Hash = 0x47109 // oninvalid 239 | Onkeydown Hash = 0x47a09 // onkeydown 240 | Onkeypress Hash = 0x4870a // onkeypress 241 | Onkeyup Hash = 0x49707 // onkeyup 242 | Onload Hash = 0x49e06 // onload 243 | Onloadeddata Hash = 0x49e0c // onloadeddata 244 | Onloadedmetadata Hash = 0x4b110 // onloadedmetadata 245 | Onloadstart Hash = 0x4c70b // onloadstart 246 | Onmessage Hash = 0x4d209 // onmessage 247 | Onmousedown Hash = 0x4db0b // onmousedown 248 | Onmousemove Hash = 0x4f80b // onmousemove 249 | Onmouseout Hash = 0x5030a // onmouseout 250 | Onmouseover Hash = 0x5100b // onmouseover 251 | Onmouseup Hash = 0x51b09 // onmouseup 252 | Onmousewheel Hash = 0x5240c // onmousewheel 253 | Onoffline Hash = 0x53009 // onoffline 254 | Ononline Hash = 0x53b08 // ononline 255 | Onpagehide Hash = 0x5430a // onpagehide 256 | Onpageshow Hash = 0x5570a // onpageshow 257 | Onpause Hash = 0x56307 // onpause 258 | Onplay Hash = 0x59006 // onplay 259 | Onplaying Hash = 0x59009 // onplaying 260 | Onpopstate Hash = 0x5990a // onpopstate 261 | Onprogress Hash = 0x5a30a // onprogress 262 | Onratechange Hash = 0x5b70c // onratechange 263 | Onreset Hash = 0x5c307 // onreset 264 | Onresize Hash = 0x5ca08 // onresize 265 | Onscroll Hash = 0x5d208 // onscroll 266 | Onseeked Hash = 0x5dd08 // onseeked 267 | Onseeking Hash = 0x5e509 // onseeking 268 | Onselect Hash = 0x5ee08 // onselect 269 | Onshow Hash = 0x5f806 // onshow 270 | Onstalled Hash = 0x60309 // onstalled 271 | Onstorage Hash = 0x60c09 // onstorage 272 | Onsubmit Hash = 0x61508 // onsubmit 273 | Onsuspend Hash = 0x62909 // onsuspend 274 | Ontimeupdate Hash = 0x4520c // ontimeupdate 275 | Onunload Hash = 0x63208 // onunload 276 | Onvolumechange Hash = 0x63a0e // onvolumechange 277 | Onwaiting Hash = 0x64809 // onwaiting 278 | Open Hash = 0x57404 // open 279 | Optgroup Hash = 0x12008 // optgroup 280 | Optimum Hash = 0x65107 // optimum 281 | Option Hash = 0x66606 // option 282 | Output Hash = 0x50a06 // output 283 | P Hash = 0xc01 // p 284 | Param Hash = 0xc05 // param 285 | Pattern Hash = 0x9b07 // pattern 286 | Pauseonexit Hash = 0x5650b // pauseonexit 287 | Ping Hash = 0xe704 // ping 288 | Placeholder Hash = 0x1270b // placeholder 289 | Plaintext Hash = 0x17d09 // plaintext 290 | Poster Hash = 0x1fb06 // poster 291 | Pre Hash = 0x30d03 // pre 292 | Preload Hash = 0x30d07 // preload 293 | Profile Hash = 0x34f07 // profile 294 | Progress Hash = 0x5a508 // progress 295 | Prompt Hash = 0x66006 // prompt 296 | Public Hash = 0x57c06 // public 297 | Q Hash = 0x8d01 // q 298 | Radiogroup Hash = 0x30a // radiogroup 299 | Rb Hash = 0x1d02 // rb 300 | Readonly Hash = 0x38708 // readonly 301 | Rel Hash = 0x30e03 // rel 302 | Required Hash = 0x8b08 // required 303 | Rev Hash = 0x29303 // rev 304 | Reversed Hash = 0x29308 // reversed 305 | Rows Hash = 0x6604 // rows 306 | Rowspan Hash = 0x6607 // rowspan 307 | Rp Hash = 0x28902 // rp 308 | Rt Hash = 0x1c702 // rt 309 | Rtc Hash = 0x1c703 // rtc 310 | Ruby Hash = 0xf304 // ruby 311 | Rules Hash = 0x13105 // rules 312 | S Hash = 0x3d01 // s 313 | Samp Hash = 0x9804 // samp 314 | Sandbox Hash = 0x13507 // sandbox 315 | Scope Hash = 0x35a05 // scope 316 | Scoped Hash = 0x35a06 // scoped 317 | Script Hash = 0x31b06 // script 318 | Scrolling Hash = 0x5d409 // scrolling 319 | Seamless Hash = 0x3a108 // seamless 320 | Section Hash = 0x13f07 // section 321 | Select Hash = 0x5f006 // select 322 | Selected Hash = 0x5f008 // selected 323 | Shape Hash = 0x23005 // shape 324 | Size Hash = 0x24e04 // size 325 | Sizes Hash = 0x24e05 // sizes 326 | Small Hash = 0x23a05 // small 327 | Sortable Hash = 0x25208 // sortable 328 | Source Hash = 0x26506 // source 329 | Spacer Hash = 0x37106 // spacer 330 | Span Hash = 0x6904 // span 331 | Spellcheck Hash = 0x3a80a // spellcheck 332 | Src Hash = 0x44403 // src 333 | Srcdoc Hash = 0x44406 // srcdoc 334 | Srclang Hash = 0x49007 // srclang 335 | Start Hash = 0x40505 // start 336 | Step Hash = 0x65d04 // step 337 | Strike Hash = 0x5ac06 // strike 338 | Strong Hash = 0x67c06 // strong 339 | Style Hash = 0x68205 // style 340 | Sub Hash = 0x61703 // sub 341 | Summary Hash = 0x68707 // summary 342 | Sup Hash = 0x68e03 // sup 343 | Svg Hash = 0x69103 // svg 344 | System Hash = 0x69406 // system 345 | Tabindex Hash = 0x4bf08 // tabindex 346 | Target Hash = 0x2f806 // target 347 | Tbody Hash = 0x3f05 // tbody 348 | Td Hash = 0xaa02 // td 349 | Text Hash = 0x18204 // text 350 | Textarea Hash = 0x38208 // textarea 351 | Tfoot Hash = 0xed05 // tfoot 352 | Th Hash = 0x19502 // th 353 | Thead Hash = 0x36a05 // thead 354 | Time Hash = 0x2ed04 // time 355 | Title Hash = 0x16605 // title 356 | Tr Hash = 0x18502 // tr 357 | Track Hash = 0x18505 // track 358 | Translate Hash = 0x22009 // translate 359 | Truespeed Hash = 0x27209 // truespeed 360 | Tt Hash = 0x9d02 // tt 361 | Type Hash = 0x10f04 // type 362 | Typemustmatch Hash = 0x1e00d // typemustmatch 363 | U Hash = 0xb01 // u 364 | Ul Hash = 0x5802 // ul 365 | Undeterminate Hash = 0x250d // undeterminate 366 | Usemap Hash = 0x15d06 // usemap 367 | Valign Hash = 0x1506 // valign 368 | Value Hash = 0x10a05 // value 369 | Valuetype Hash = 0x10a09 // valuetype 370 | Var Hash = 0x32f03 // var 371 | Video Hash = 0x6a005 // video 372 | Visible Hash = 0x6ab07 // visible 373 | Vlink Hash = 0x6b205 // vlink 374 | Wbr Hash = 0x56003 // wbr 375 | Width Hash = 0x5fd05 // width 376 | Wrap Hash = 0x57904 // wrap 377 | Xmlns Hash = 0x13b05 // xmlns 378 | Xmp Hash = 0x17b03 // xmp 379 | ) 380 | 381 | var HashMap = map[string]Hash{ 382 | "a": A, 383 | "abbr": Abbr, 384 | "accept": Accept, 385 | "accept-charset": Accept_Charset, 386 | "accesskey": Accesskey, 387 | "acronym": Acronym, 388 | "action": Action, 389 | "address": Address, 390 | "align": Align, 391 | "alink": Alink, 392 | "allowfullscreen": Allowfullscreen, 393 | "alt": Alt, 394 | "annotation": Annotation, 395 | "annotationXml": AnnotationXml, 396 | "applet": Applet, 397 | "area": Area, 398 | "article": Article, 399 | "aside": Aside, 400 | "async": Async, 401 | "audio": Audio, 402 | "autocomplete": Autocomplete, 403 | "autofocus": Autofocus, 404 | "autoplay": Autoplay, 405 | "axis": Axis, 406 | "b": B, 407 | "background": Background, 408 | "base": Base, 409 | "basefont": Basefont, 410 | "bdi": Bdi, 411 | "bdo": Bdo, 412 | "bgcolor": Bgcolor, 413 | "bgsound": Bgsound, 414 | "big": Big, 415 | "blink": Blink, 416 | "blockquote": Blockquote, 417 | "body": Body, 418 | "border": Border, 419 | "br": Br, 420 | "button": Button, 421 | "canvas": Canvas, 422 | "caption": Caption, 423 | "center": Center, 424 | "challenge": Challenge, 425 | "charset": Charset, 426 | "checked": Checked, 427 | "cite": Cite, 428 | "class": Class, 429 | "classid": Classid, 430 | "clear": Clear, 431 | "code": Code, 432 | "codebase": Codebase, 433 | "codetype": Codetype, 434 | "col": Col, 435 | "colgroup": Colgroup, 436 | "color": Color, 437 | "cols": Cols, 438 | "colspan": Colspan, 439 | "command": Command, 440 | "compact": Compact, 441 | "content": Content, 442 | "contenteditable": Contenteditable, 443 | "contextmenu": Contextmenu, 444 | "controls": Controls, 445 | "coords": Coords, 446 | "crossorigin": Crossorigin, 447 | "data": Data, 448 | "datalist": Datalist, 449 | "datetime": Datetime, 450 | "dd": Dd, 451 | "declare": Declare, 452 | "default": Default, 453 | "defaultChecked": DefaultChecked, 454 | "defaultMuted": DefaultMuted, 455 | "defaultSelected": DefaultSelected, 456 | "defer": Defer, 457 | "del": Del, 458 | "desc": Desc, 459 | "details": Details, 460 | "dfn": Dfn, 461 | "dialog": Dialog, 462 | "dir": Dir, 463 | "dirname": Dirname, 464 | "disabled": Disabled, 465 | "div": Div, 466 | "dl": Dl, 467 | "download": Download, 468 | "draggable": Draggable, 469 | "dropzone": Dropzone, 470 | "dt": Dt, 471 | "em": Em, 472 | "embed": Embed, 473 | "enabled": Enabled, 474 | "enctype": Enctype, 475 | "face": Face, 476 | "fieldset": Fieldset, 477 | "figcaption": Figcaption, 478 | "figure": Figure, 479 | "font": Font, 480 | "footer": Footer, 481 | "for": For, 482 | "foreignObject": ForeignObject, 483 | "foreignobject": Foreignobject, 484 | "form": Form, 485 | "formaction": Formaction, 486 | "formenctype": Formenctype, 487 | "formmethod": Formmethod, 488 | "formnovalidate": Formnovalidate, 489 | "formtarget": Formtarget, 490 | "frame": Frame, 491 | "frameborder": Frameborder, 492 | "frameset": Frameset, 493 | "h1": H1, 494 | "h2": H2, 495 | "h3": H3, 496 | "h4": H4, 497 | "h5": H5, 498 | "h6": H6, 499 | "head": Head, 500 | "header": Header, 501 | "headers": Headers, 502 | "height": Height, 503 | "hgroup": Hgroup, 504 | "hidden": Hidden, 505 | "high": High, 506 | "hr": Hr, 507 | "href": Href, 508 | "hreflang": Hreflang, 509 | "html": Html, 510 | "http-equiv": Http_Equiv, 511 | "i": I, 512 | "icon": Icon, 513 | "id": Id, 514 | "iframe": Iframe, 515 | "image": Image, 516 | "img": Img, 517 | "inert": Inert, 518 | "input": Input, 519 | "ins": Ins, 520 | "isindex": Isindex, 521 | "ismap": Ismap, 522 | "itemid": Itemid, 523 | "itemprop": Itemprop, 524 | "itemref": Itemref, 525 | "itemscope": Itemscope, 526 | "itemtype": Itemtype, 527 | "kbd": Kbd, 528 | "keygen": Keygen, 529 | "keytype": Keytype, 530 | "kind": Kind, 531 | "label": Label, 532 | "lang": Lang, 533 | "language": Language, 534 | "legend": Legend, 535 | "li": Li, 536 | "link": Link, 537 | "list": List, 538 | "listing": Listing, 539 | "longdesc": Longdesc, 540 | "loop": Loop, 541 | "low": Low, 542 | "main": Main, 543 | "malignmark": Malignmark, 544 | "manifest": Manifest, 545 | "map": Map, 546 | "mark": Mark, 547 | "marquee": Marquee, 548 | "math": Math, 549 | "max": Max, 550 | "maxlength": Maxlength, 551 | "media": Media, 552 | "mediagroup": Mediagroup, 553 | "menu": Menu, 554 | "meta": Meta, 555 | "meter": Meter, 556 | "method": Method, 557 | "mglyph": Mglyph, 558 | "mi": Mi, 559 | "min": Min, 560 | "mn": Mn, 561 | "mo": Mo, 562 | "ms": Ms, 563 | "mtext": Mtext, 564 | "multiple": Multiple, 565 | "muted": Muted, 566 | "name": Name, 567 | "nav": Nav, 568 | "nobr": Nobr, 569 | "noembed": Noembed, 570 | "noframes": Noframes, 571 | "nohref": Nohref, 572 | "noresize": Noresize, 573 | "noscript": Noscript, 574 | "noshade": Noshade, 575 | "novalidate": Novalidate, 576 | "nowrap": Nowrap, 577 | "object": Object, 578 | "ol": Ol, 579 | "onabort": Onabort, 580 | "onafterprint": Onafterprint, 581 | "onbeforeprint": Onbeforeprint, 582 | "onbeforeunload": Onbeforeunload, 583 | "onblur": Onblur, 584 | "oncancel": Oncancel, 585 | "oncanplay": Oncanplay, 586 | "oncanplaythrough": Oncanplaythrough, 587 | "onchange": Onchange, 588 | "onclick": Onclick, 589 | "onclose": Onclose, 590 | "oncontextmenu": Oncontextmenu, 591 | "oncuechange": Oncuechange, 592 | "ondblclick": Ondblclick, 593 | "ondrag": Ondrag, 594 | "ondragend": Ondragend, 595 | "ondragenter": Ondragenter, 596 | "ondragleave": Ondragleave, 597 | "ondragover": Ondragover, 598 | "ondragstart": Ondragstart, 599 | "ondrop": Ondrop, 600 | "ondurationchange": Ondurationchange, 601 | "onemptied": Onemptied, 602 | "onended": Onended, 603 | "onerror": Onerror, 604 | "onfocus": Onfocus, 605 | "onhashchange": Onhashchange, 606 | "oninput": Oninput, 607 | "oninvalid": Oninvalid, 608 | "onkeydown": Onkeydown, 609 | "onkeypress": Onkeypress, 610 | "onkeyup": Onkeyup, 611 | "onload": Onload, 612 | "onloadeddata": Onloadeddata, 613 | "onloadedmetadata": Onloadedmetadata, 614 | "onloadstart": Onloadstart, 615 | "onmessage": Onmessage, 616 | "onmousedown": Onmousedown, 617 | "onmousemove": Onmousemove, 618 | "onmouseout": Onmouseout, 619 | "onmouseover": Onmouseover, 620 | "onmouseup": Onmouseup, 621 | "onmousewheel": Onmousewheel, 622 | "onoffline": Onoffline, 623 | "ononline": Ononline, 624 | "onpagehide": Onpagehide, 625 | "onpageshow": Onpageshow, 626 | "onpause": Onpause, 627 | "onplay": Onplay, 628 | "onplaying": Onplaying, 629 | "onpopstate": Onpopstate, 630 | "onprogress": Onprogress, 631 | "onratechange": Onratechange, 632 | "onreset": Onreset, 633 | "onresize": Onresize, 634 | "onscroll": Onscroll, 635 | "onseeked": Onseeked, 636 | "onseeking": Onseeking, 637 | "onselect": Onselect, 638 | "onshow": Onshow, 639 | "onstalled": Onstalled, 640 | "onstorage": Onstorage, 641 | "onsubmit": Onsubmit, 642 | "onsuspend": Onsuspend, 643 | "ontimeupdate": Ontimeupdate, 644 | "onunload": Onunload, 645 | "onvolumechange": Onvolumechange, 646 | "onwaiting": Onwaiting, 647 | "open": Open, 648 | "optgroup": Optgroup, 649 | "optimum": Optimum, 650 | "option": Option, 651 | "output": Output, 652 | "p": P, 653 | "param": Param, 654 | "pattern": Pattern, 655 | "pauseonexit": Pauseonexit, 656 | "ping": Ping, 657 | "placeholder": Placeholder, 658 | "plaintext": Plaintext, 659 | "poster": Poster, 660 | "pre": Pre, 661 | "preload": Preload, 662 | "profile": Profile, 663 | "progress": Progress, 664 | "prompt": Prompt, 665 | "public": Public, 666 | "q": Q, 667 | "radiogroup": Radiogroup, 668 | "rb": Rb, 669 | "readonly": Readonly, 670 | "rel": Rel, 671 | "required": Required, 672 | "rev": Rev, 673 | "reversed": Reversed, 674 | "rows": Rows, 675 | "rowspan": Rowspan, 676 | "rp": Rp, 677 | "rt": Rt, 678 | "rtc": Rtc, 679 | "ruby": Ruby, 680 | "rules": Rules, 681 | "s": S, 682 | "samp": Samp, 683 | "sandbox": Sandbox, 684 | "scope": Scope, 685 | "scoped": Scoped, 686 | "script": Script, 687 | "scrolling": Scrolling, 688 | "seamless": Seamless, 689 | "section": Section, 690 | "select": Select, 691 | "selected": Selected, 692 | "shape": Shape, 693 | "size": Size, 694 | "sizes": Sizes, 695 | "small": Small, 696 | "sortable": Sortable, 697 | "source": Source, 698 | "spacer": Spacer, 699 | "span": Span, 700 | "spellcheck": Spellcheck, 701 | "src": Src, 702 | "srcdoc": Srcdoc, 703 | "srclang": Srclang, 704 | "start": Start, 705 | "step": Step, 706 | "strike": Strike, 707 | "strong": Strong, 708 | "style": Style, 709 | "sub": Sub, 710 | "summary": Summary, 711 | "sup": Sup, 712 | "svg": Svg, 713 | "system": System, 714 | "tabindex": Tabindex, 715 | "target": Target, 716 | "tbody": Tbody, 717 | "td": Td, 718 | "text": Text, 719 | "textarea": Textarea, 720 | "tfoot": Tfoot, 721 | "th": Th, 722 | "thead": Thead, 723 | "time": Time, 724 | "title": Title, 725 | "tr": Tr, 726 | "track": Track, 727 | "translate": Translate, 728 | "truespeed": Truespeed, 729 | "tt": Tt, 730 | "type": Type, 731 | "typemustmatch": Typemustmatch, 732 | "u": U, 733 | "ul": Ul, 734 | "undeterminate": Undeterminate, 735 | "usemap": Usemap, 736 | "valign": Valign, 737 | "value": Value, 738 | "valuetype": Valuetype, 739 | "var": Var, 740 | "video": Video, 741 | "visible": Visible, 742 | "vlink": Vlink, 743 | "wbr": Wbr, 744 | "width": Width, 745 | "wrap": Wrap, 746 | "xmlns": Xmlns, 747 | "xmp": Xmp, 748 | } 749 | 750 | // String returns the text associated with the hash. 751 | func (i Hash) String() string { 752 | return string(i.Bytes()) 753 | } 754 | 755 | // Bytes returns the text associated with the hash. 756 | func (i Hash) Bytes() []byte { 757 | start := uint32(i >> 8) 758 | n := uint32(i & 0xff) 759 | if start+n > uint32(len(_Hash_text)) { 760 | return []byte{} 761 | } 762 | return _Hash_text[start : start+n] 763 | } 764 | 765 | // ToHash returns a hash Hash for a given []byte. Hash is a uint32 that is associated with the text in []byte. It returns zero if no match found. 766 | func ToHash(s []byte) Hash { 767 | if 3 < len(s) { 768 | return HashMap[string(s)] 769 | } 770 | h := uint32(_Hash_hash0) 771 | for i := 0; i < len(s); i++ { 772 | h ^= uint32(s[i]) 773 | h *= 16777619 774 | } 775 | if i := _Hash_table[h&uint32(len(_Hash_table)-1)]; int(i&0xff) == len(s) { 776 | t := _Hash_text[i>>8 : i>>8+i&0xff] 777 | for i := 0; i < len(s); i++ { 778 | if t[i] != s[i] { 779 | goto NEXT 780 | } 781 | } 782 | return i 783 | } 784 | NEXT: 785 | if i := _Hash_table[(h>>16)&uint32(len(_Hash_table)-1)]; int(i&0xff) == len(s) { 786 | t := _Hash_text[i>>8 : i>>8+i&0xff] 787 | for i := 0; i < len(s); i++ { 788 | if t[i] != s[i] { 789 | return 0 790 | } 791 | } 792 | return i 793 | } 794 | return 0 795 | } 796 | 797 | const _Hash_hash0 = 0x5334b67c 798 | const _Hash_maxLen = 16 799 | 800 | var _Hash_text = []byte("" + 801 | "abbradiogrouparamainavalignobrbackgroundeterminateaccept-cha" + 802 | "rsetbodyaccesskeygenabledefaultSelectedeferowspanoembedelabe" + 803 | "longdescanvasideclarequiredetailsampatternoframesetdfnohrefl" + 804 | "anguageacronymalignmarkbdialogalinkindirnamediagroupingaltfo" + 805 | "oterubyasyncitemidisabledivaluetypeaudioncancelooptgrouplace" + 806 | "holderulesandboxmlnsectionblurautocompleteautofocusemappleti" + 807 | "tleautoplayaxisindexmplaintextrackbdoncanplaythrough1bgcolor" + 808 | "bgsoundlbigblinkblockquotebuttonabortclassidraggablegendcode" + 809 | "typemustmatchallengecolgroupostercolspannotationXmlcommandco" + 810 | "mpactranslatecontrolshapecoordsmallowfullscreenoresizesortab" + 811 | "lecrossoriginsourcefieldsetruespeedfigcaptionafterprintfigur" + 812 | "eversedforeignObjectforeignobjectformactionbeforeprintformen" + 813 | "ctypeformmethodformnovalidatetimeterformtargeth6heightmlhgro" + 814 | "upreloadhiddenoscripthigh2http-equivariframeborderimageimgly" + 815 | "ph3ismaprofileitemscopeditemtypematheaderspacermaxlength4mte" + 816 | "xtareadonlymultiplemutedoncloseamlesspellcheckedoncontextmen" + 817 | "uoncuechangeondblclickondragendondragenterondragleaveondrago" + 818 | "verondragstarticlearondropzonemptiedondurationchangeonendedo" + 819 | "nerroronfocusrcdocodebasefontimeupdateonhashchangeoninputoni" + 820 | "nvalidonkeydownloadonkeypressrclangonkeyuponloadeddatalistin" + 821 | "gonloadedmetadatabindexonloadstartonmessageonmousedownoshade" + 822 | "faultCheckedonmousemoveonmouseoutputonmouseoveronmouseuponmo" + 823 | "usewheelonofflinertononlineonpagehidefaultMutedonpageshowbro" + 824 | "npauseonexitempropenowrapublicontenteditableonplayingonpopst" + 825 | "ateonprogresstrikeytypeonratechangeonresetonresizeonscrollin" + 826 | "gonseekedonseekingonselectedonshowidth5onstalledonstorageons" + 827 | "ubmitemrefacenteronsuspendonunloadonvolumechangeonwaitingopt" + 828 | "imumanifestepromptoptionbeforeunloaddresstrongstylesummarysu" + 829 | "psvgsystemarqueevideonclickvisiblevlink") 830 | 831 | var _Hash_table = [1 << 9]Hash{ 832 | 0x0: 0x2ca0b, // formenctype 833 | 0x1: 0x2d50a, // formmethod 834 | 0x2: 0x3c10b, // oncuechange 835 | 0x3: 0x3d606, // ondrag 836 | 0x6: 0x5ac06, // strike 837 | 0x7: 0x6a005, // video 838 | 0x9: 0x58107, // content 839 | 0xa: 0x4e07, // enabled 840 | 0xb: 0x57706, // nowrap 841 | 0xc: 0xd304, // link 842 | 0xe: 0x28902, // rp 843 | 0xf: 0x2830c, // onafterprint 844 | 0x10: 0x16106, // applet 845 | 0x11: 0xed05, // tfoot 846 | 0x12: 0x4ea0e, // defaultChecked 847 | 0x13: 0x3330b, // frameborder 848 | 0x14: 0xee06, // footer 849 | 0x15: 0x5f008, // selected 850 | 0x16: 0x49007, // srclang 851 | 0x18: 0x5100b, // onmouseover 852 | 0x19: 0x1dc04, // code 853 | 0x1b: 0x47109, // oninvalid 854 | 0x1c: 0x62104, // face 855 | 0x1e: 0x3b60b, // contextmenu 856 | 0x1f: 0xa308, // frameset 857 | 0x21: 0x54b0c, // defaultMuted 858 | 0x22: 0x19f05, // color 859 | 0x23: 0x59006, // onplay 860 | 0x25: 0x2ef05, // meter 861 | 0x26: 0x60c09, // onstorage 862 | 0x27: 0x38708, // readonly 863 | 0x29: 0x34f07, // profile 864 | 0x2a: 0x5570a, // onpageshow 865 | 0x2b: 0xb01, // u 866 | 0x2c: 0x31908, // noscript 867 | 0x2d: 0x65708, // manifest 868 | 0x2e: 0x1be06, // button 869 | 0x2f: 0x2e908, // datetime 870 | 0x30: 0x46c05, // input 871 | 0x31: 0x5407, // default 872 | 0x32: 0x1dc08, // codetype 873 | 0x33: 0x2a80d, // foreignobject 874 | 0x34: 0x69907, // marquee 875 | 0x36: 0x19d07, // bgcolor 876 | 0x37: 0x19b02, // h1 877 | 0x39: 0x1e0a, // background 878 | 0x3b: 0x2f40a, // formtarget 879 | 0x41: 0x2f806, // target 880 | 0x43: 0x23a05, // small 881 | 0x44: 0x44908, // codebase 882 | 0x45: 0x53605, // inert 883 | 0x47: 0x38105, // mtext 884 | 0x48: 0x6607, // rowspan 885 | 0x49: 0x2bd0d, // onbeforeprint 886 | 0x4a: 0x53b08, // ononline 887 | 0x4c: 0x28f06, // figure 888 | 0x4d: 0x4b110, // onloadedmetadata 889 | 0x4e: 0xbb07, // acronym 890 | 0x50: 0x38f08, // multiple 891 | 0x51: 0x320e, // accept-charset 892 | 0x52: 0x24e05, // sizes 893 | 0x53: 0x29b0d, // foreignObject 894 | 0x55: 0x2e30a, // novalidate 895 | 0x56: 0x5430a, // onpagehide 896 | 0x57: 0x2e202, // mn 897 | 0x58: 0x37f02, // h4 898 | 0x5a: 0x1c702, // rt 899 | 0x5b: 0xd205, // alink 900 | 0x5e: 0x66006, // prompt 901 | 0x5f: 0x12d02, // ol 902 | 0x61: 0x5ca08, // onresize 903 | 0x64: 0x68707, // summary 904 | 0x65: 0x5990a, // onpopstate 905 | 0x66: 0x38604, // area 906 | 0x68: 0x64809, // onwaiting 907 | 0x6b: 0xdc04, // name 908 | 0x6c: 0x23506, // coords 909 | 0x6d: 0x34303, // img 910 | 0x6e: 0x65d04, // step 911 | 0x6f: 0x5e509, // onseeking 912 | 0x70: 0x32104, // high 913 | 0x71: 0x49707, // onkeyup 914 | 0x72: 0x5f006, // select 915 | 0x73: 0x18505, // track 916 | 0x74: 0x34b05, // ismap 917 | 0x77: 0x8d01, // q 918 | 0x78: 0x47a09, // onkeydown 919 | 0x79: 0x33e05, // image 920 | 0x7a: 0x2b504, // form 921 | 0x7b: 0x60309, // onstalled 922 | 0x7d: 0x42808, // onchange 923 | 0x7e: 0x1af05, // blink 924 | 0x7f: 0xeb03, // alt 925 | 0x80: 0xf705, // async 926 | 0x82: 0x1702, // li 927 | 0x84: 0x2c02, // mi 928 | 0x85: 0xfc06, // itemid 929 | 0x86: 0x11305, // audio 930 | 0x87: 0x31b06, // script 931 | 0x8b: 0x44406, // srcdoc 932 | 0x8e: 0xc704, // mark 933 | 0x8f: 0x18a03, // bdo 934 | 0x91: 0x4f80b, // onmousemove 935 | 0x93: 0x3bd04, // menu 936 | 0x94: 0x45104, // font 937 | 0x95: 0x16b08, // autoplay 938 | 0x96: 0x6b205, // vlink 939 | 0x98: 0x6e02, // em 940 | 0x9b: 0x1f408, // colgroup 941 | 0x9c: 0x57404, // open 942 | 0x9d: 0x1d606, // legend 943 | 0x9e: 0x4c70b, // onloadstart 944 | 0xa2: 0x22009, // translate 945 | 0xa3: 0x6e05, // embed 946 | 0xa4: 0x1c905, // class 947 | 0xa7: 0x36b06, // header 948 | 0xa9: 0x49e06, // onload 949 | 0xaa: 0x36a05, // thead 950 | 0xab: 0x5d409, // scrolling 951 | 0xac: 0xc05, // param 952 | 0xae: 0x9b07, // pattern 953 | 0xaf: 0x9207, // details 954 | 0xb1: 0x57c06, // public 955 | 0xb3: 0x4db0b, // onmousedown 956 | 0xb4: 0x16003, // map 957 | 0xb6: 0x25a0b, // crossorigin 958 | 0xb7: 0x1506, // valign 959 | 0xb9: 0x1c207, // onabort 960 | 0xba: 0x66606, // option 961 | 0xbb: 0x26506, // source 962 | 0xbc: 0x6205, // defer 963 | 0xbd: 0x1eb09, // challenge 964 | 0xbf: 0x10a05, // value 965 | 0xc0: 0x23c0f, // allowfullscreen 966 | 0xc1: 0xca03, // kbd 967 | 0xc2: 0x2060d, // annotationXml 968 | 0xc3: 0x5b70c, // onratechange 969 | 0xc4: 0x4dd02, // mo 970 | 0xc6: 0x3a80a, // spellcheck 971 | 0xc7: 0x2c03, // min 972 | 0xc8: 0x49e0c, // onloadeddata 973 | 0xc9: 0x40b05, // clear 974 | 0xca: 0x42010, // ondurationchange 975 | 0xcb: 0x1a04, // nobr 976 | 0xcd: 0x27209, // truespeed 977 | 0xcf: 0x30806, // hgroup 978 | 0xd0: 0x40505, // start 979 | 0xd3: 0x41208, // dropzone 980 | 0xd5: 0x7405, // label 981 | 0xd8: 0xde0a, // mediagroup 982 | 0xd9: 0x14406, // onblur 983 | 0xdb: 0x27e07, // caption 984 | 0xdd: 0x7c04, // desc 985 | 0xde: 0x13b05, // xmlns 986 | 0xdf: 0x30006, // height 987 | 0xe0: 0x21307, // command 988 | 0xe2: 0x5650b, // pauseonexit 989 | 0xe3: 0x67c06, // strong 990 | 0xe4: 0x43707, // onerror 991 | 0xe5: 0x61508, // onsubmit 992 | 0xe6: 0xb308, // language 993 | 0xe7: 0x47f08, // download 994 | 0xe9: 0x51b09, // onmouseup 995 | 0xec: 0x2ce07, // enctype 996 | 0xed: 0x5ee08, // onselect 997 | 0xee: 0x2af06, // object 998 | 0xef: 0x17d09, // plaintext 999 | 0xf0: 0x3cc0a, // ondblclick 1000 | 0xf1: 0x18c10, // oncanplaythrough 1001 | 0xf2: 0xd903, // dir 1002 | 0xf3: 0x38208, // textarea 1003 | 0xf4: 0xe704, // ping 1004 | 0xf5: 0x2d906, // method 1005 | 0xf6: 0x22908, // controls 1006 | 0xf7: 0x37106, // spacer 1007 | 0xf8: 0x69103, // svg 1008 | 0xf9: 0x30404, // html 1009 | 0xfa: 0x3d01, // s 1010 | 0xfc: 0xcc06, // dialog 1011 | 0xfe: 0x1e00d, // typemustmatch 1012 | 0xff: 0x3ad07, // checked 1013 | 0x101: 0x1fb06, // poster 1014 | 0x102: 0x3260a, // http-equiv 1015 | 0x103: 0x44403, // src 1016 | 0x104: 0x10108, // disabled 1017 | 0x105: 0x36b07, // headers 1018 | 0x106: 0x5a30a, // onprogress 1019 | 0x107: 0x26b08, // fieldset 1020 | 0x108: 0x32f03, // var 1021 | 0x10a: 0xa305, // frame 1022 | 0x10b: 0x36008, // itemtype 1023 | 0x10c: 0x3f50a, // ondragover 1024 | 0x10d: 0x15609, // autofocus 1025 | 0x10f: 0x601, // i 1026 | 0x110: 0x35902, // ms 1027 | 0x111: 0x44d04, // base 1028 | 0x113: 0x35a05, // scope 1029 | 0x114: 0x3206, // accept 1030 | 0x115: 0x56e08, // itemprop 1031 | 0x117: 0xfb04, // cite 1032 | 0x118: 0x3907, // charset 1033 | 0x119: 0x16605, // title 1034 | 0x11a: 0x5b007, // keytype 1035 | 0x11b: 0x18204, // text 1036 | 0x11c: 0x65107, // optimum 1037 | 0x11e: 0x36b04, // head 1038 | 0x121: 0x21a07, // compact 1039 | 0x123: 0x62909, // onsuspend 1040 | 0x124: 0x4aa04, // list 1041 | 0x125: 0x4520c, // ontimeupdate 1042 | 0x126: 0x62306, // center 1043 | 0x127: 0x31406, // hidden 1044 | 0x129: 0x35609, // itemscope 1045 | 0x12c: 0x1aa02, // dl 1046 | 0x12d: 0x13f07, // section 1047 | 0x12e: 0x11708, // oncancel 1048 | 0x12f: 0x6a407, // onclick 1049 | 0x130: 0xde05, // media 1050 | 0x131: 0x50a06, // output 1051 | 0x132: 0x4a608, // datalist 1052 | 0x133: 0x5240c, // onmousewheel 1053 | 0x134: 0x44d08, // basefont 1054 | 0x135: 0x37709, // maxlength 1055 | 0x136: 0x6ab07, // visible 1056 | 0x137: 0x2df0e, // formnovalidate 1057 | 0x139: 0x17b03, // xmp 1058 | 0x13a: 0x101, // b 1059 | 0x13b: 0x46a07, // oninput 1060 | 0x13c: 0xf304, // ruby 1061 | 0x13d: 0x1270b, // placeholder 1062 | 0x13e: 0x4aa07, // listing 1063 | 0x140: 0x26303, // ins 1064 | 0x141: 0x61b07, // itemref 1065 | 0x144: 0x540f, // defaultSelected 1066 | 0x146: 0x3ea0b, // ondragleave 1067 | 0x147: 0x1b40a, // blockquote 1068 | 0x148: 0x57904, // wrap 1069 | 0x14a: 0x1ac03, // big 1070 | 0x14b: 0x30e03, // rel 1071 | 0x14c: 0x41006, // ondrop 1072 | 0x14e: 0x69406, // system 1073 | 0x14f: 0x30a, // radiogroup 1074 | 0x150: 0xb304, // lang 1075 | 0x152: 0x56003, // wbr 1076 | 0x153: 0x3b40d, // oncontextmenu 1077 | 0x155: 0x250d, // undeterminate 1078 | 0x157: 0x20104, // cols 1079 | 0x158: 0x13507, // sandbox 1080 | 0x159: 0x1303, // nav 1081 | 0x15a: 0x37703, // max 1082 | 0x15b: 0x7808, // longdesc 1083 | 0x15c: 0x5fd05, // width 1084 | 0x15d: 0x34902, // h3 1085 | 0x15e: 0x1a407, // bgsound 1086 | 0x161: 0x10a09, // valuetype 1087 | 0x162: 0x68205, // style 1088 | 0x164: 0x3f05, // tbody 1089 | 0x165: 0x40707, // article 1090 | 0x169: 0xcb03, // bdi 1091 | 0x16a: 0x67607, // address 1092 | 0x16b: 0x23005, // shape 1093 | 0x16c: 0x2b906, // action 1094 | 0x16e: 0x18502, // tr 1095 | 0x16f: 0xaa02, // td 1096 | 0x170: 0x3d609, // ondragend 1097 | 0x171: 0x5802, // ul 1098 | 0x172: 0x33806, // border 1099 | 0x174: 0x4a06, // keygen 1100 | 0x175: 0x4004, // body 1101 | 0x177: 0x1cf09, // draggable 1102 | 0x178: 0x2b50a, // formaction 1103 | 0x17b: 0x34406, // mglyph 1104 | 0x17d: 0x1d02, // rb 1105 | 0x17e: 0x2fe02, // h6 1106 | 0x17f: 0x41709, // onemptied 1107 | 0x180: 0x5c307, // onreset 1108 | 0x181: 0x1004, // main 1109 | 0x182: 0x11e04, // loop 1110 | 0x183: 0x4870a, // onkeypress 1111 | 0x184: 0x9d02, // tt 1112 | 0x186: 0x20107, // colspan 1113 | 0x188: 0x36804, // math 1114 | 0x189: 0x1605, // align 1115 | 0x18a: 0xa108, // noframes 1116 | 0x18b: 0xaf02, // hr 1117 | 0x18c: 0xc10a, // malignmark 1118 | 0x18e: 0x23e03, // low 1119 | 0x18f: 0x8502, // id 1120 | 0x190: 0x6604, // rows 1121 | 0x191: 0x29303, // rev 1122 | 0x192: 0x63208, // onunload 1123 | 0x193: 0x39705, // muted 1124 | 0x194: 0x35a06, // scoped 1125 | 0x195: 0x31602, // dd 1126 | 0x196: 0x5ff02, // dt 1127 | 0x197: 0x66a0e, // onbeforeunload 1128 | 0x199: 0x2060a, // annotation 1129 | 0x19a: 0x29308, // reversed 1130 | 0x19c: 0x10f04, // type 1131 | 0x19d: 0x56307, // onpause 1132 | 0x19e: 0xd604, // kind 1133 | 0x19f: 0x4a604, // data 1134 | 0x1a0: 0x4e507, // noshade 1135 | 0x1a3: 0x13105, // rules 1136 | 0x1a4: 0x12008, // optgroup 1137 | 0x1a5: 0x202, // br 1138 | 0x1a7: 0x1, // a 1139 | 0x1a8: 0x5030a, // onmouseout 1140 | 0x1aa: 0x53009, // onoffline 1141 | 0x1ab: 0x63a0e, // onvolumechange 1142 | 0x1ae: 0x61703, // sub 1143 | 0x1b3: 0x29b03, // for 1144 | 0x1b5: 0x8b08, // required 1145 | 0x1b6: 0x5a508, // progress 1146 | 0x1b7: 0x15d06, // usemap 1147 | 0x1b8: 0x7f06, // canvas 1148 | 0x1b9: 0x58004, // icon 1149 | 0x1bb: 0x1c703, // rtc 1150 | 0x1bc: 0x8305, // aside 1151 | 0x1bd: 0x2ed04, // time 1152 | 0x1be: 0x3ff0b, // ondragstart 1153 | 0x1c0: 0x27b0a, // figcaption 1154 | 0x1c1: 0xaf04, // href 1155 | 0x1c2: 0x33206, // iframe 1156 | 0x1c3: 0x18c09, // oncanplay 1157 | 0x1c4: 0x6904, // span 1158 | 0x1c5: 0x30d03, // pre 1159 | 0x1c6: 0x6c07, // noembed 1160 | 0x1c8: 0x5dd08, // onseeked 1161 | 0x1c9: 0x4b904, // meta 1162 | 0x1ca: 0x32402, // h2 1163 | 0x1cb: 0x3a108, // seamless 1164 | 0x1cc: 0xab03, // dfn 1165 | 0x1cd: 0x17304, // axis 1166 | 0x1cf: 0x3df0b, // ondragenter 1167 | 0x1d0: 0x19502, // th 1168 | 0x1d1: 0x45e0c, // onhashchange 1169 | 0x1d2: 0x8607, // declare 1170 | 0x1d3: 0x43e07, // onfocus 1171 | 0x1d5: 0x24e04, // size 1172 | 0x1d8: 0x14a0c, // autocomplete 1173 | 0x1d9: 0xaf08, // hreflang 1174 | 0x1da: 0x9804, // samp 1175 | 0x1de: 0x19f03, // col 1176 | 0x1df: 0x10803, // div 1177 | 0x1e0: 0x25208, // sortable 1178 | 0x1e1: 0x7203, // del 1179 | 0x1e3: 0x39c07, // onclose 1180 | 0x1e6: 0xd907, // dirname 1181 | 0x1e8: 0x1c907, // classid 1182 | 0x1e9: 0x30d07, // preload 1183 | 0x1ea: 0x4bf08, // tabindex 1184 | 0x1eb: 0x60102, // h5 1185 | 0x1ec: 0x5d208, // onscroll 1186 | 0x1ed: 0x5810f, // contenteditable 1187 | 0x1ee: 0x4d209, // onmessage 1188 | 0x1ef: 0x4, // abbr 1189 | 0x1f0: 0x17507, // isindex 1190 | 0x1f1: 0x68e03, // sup 1191 | 0x1f3: 0x24a08, // noresize 1192 | 0x1f5: 0x59009, // onplaying 1193 | 0x1f6: 0x4409, // accesskey 1194 | 0x1fa: 0xc01, // p 1195 | 0x1fb: 0x43007, // onended 1196 | 0x1fc: 0x5f806, // onshow 1197 | 0x1fe: 0xad06, // nohref 1198 | } 1199 | --------------------------------------------------------------------------------