-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
http2/hpack: build static table with go generate
As the static version of headerFieldTable in hpack is generated in runtime, we may use the go:generate to prepare the struct before the initialization phase. This is supposed to save init time and allocations for many binaries, as net/http imports hpack. Before: init golang.org/x/net/http2/hpack @1.1 ms, 0.097 ms clock, 21240 bytes, 29 allocs After: init golang.org/x/net/http2/hpack @0.67 ms, 0.015 ms clock, 8120 bytes, 9 allocs Fixes golang/go#55881 Change-Id: Ia6575f67ffcba7cc4d75899b24a9c56deb58ccac Reviewed-on: https://go-review.googlesource.com/c/net/ /434875 Run-TryBot: Damien Neil <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Damien Neil <[email protected]> Reviewed-by: Bryan Mills <[email protected]>
- Loading branch information
Showing
4 changed files
with
379 additions
and
77 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 1,184 @@ | ||
// Copyright 2022 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
//go:build ignore | ||
// build ignore | ||
|
||
package main | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"go/format" | ||
"io/ioutil" | ||
"os" | ||
"sort" | ||
|
||
"golang.org/x/net/http2/hpack" | ||
) | ||
|
||
// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07#appendix-B | ||
var staticTableEntries = [...]hpack.HeaderField{ | ||
{Name: ":authority"}, | ||
{Name: ":method", Value: "GET"}, | ||
{Name: ":method", Value: "POST"}, | ||
{Name: ":path", Value: "/"}, | ||
{Name: ":path", Value: "/index.html"}, | ||
{Name: ":scheme", Value: "http"}, | ||
{Name: ":scheme", Value: "https"}, | ||
{Name: ":status", Value: "200"}, | ||
{Name: ":status", Value: "204"}, | ||
{Name: ":status", Value: "206"}, | ||
{Name: ":status", Value: "304"}, | ||
{Name: ":status", Value: "400"}, | ||
{Name: ":status", Value: "404"}, | ||
{Name: ":status", Value: "500"}, | ||
{Name: "accept-charset"}, | ||
{Name: "accept-encoding", Value: "gzip, deflate"}, | ||
{Name: "accept-language"}, | ||
{Name: "accept-ranges"}, | ||
{Name: "accept"}, | ||
{Name: "access-control-allow-origin"}, | ||
{Name: "age"}, | ||
{Name: "allow"}, | ||
{Name: "authorization"}, | ||
{Name: "cache-control"}, | ||
{Name: "content-disposition"}, | ||
{Name: "content-encoding"}, | ||
{Name: "content-language"}, | ||
{Name: "content-length"}, | ||
{Name: "content-location"}, | ||
{Name: "content-range"}, | ||
{Name: "content-type"}, | ||
{Name: "cookie"}, | ||
{Name: "date"}, | ||
{Name: "etag"}, | ||
{Name: "expect"}, | ||
{Name: "expires"}, | ||
{Name: "from"}, | ||
{Name: "host"}, | ||
{Name: "if-match"}, | ||
{Name: "if-modified-since"}, | ||
{Name: "if-none-match"}, | ||
{Name: "if-range"}, | ||
{Name: "if-unmodified-since"}, | ||
{Name: "last-modified"}, | ||
{Name: "link"}, | ||
{Name: "location"}, | ||
{Name: "max-forwards"}, | ||
{Name: "proxy-authenticate"}, | ||
{Name: "proxy-authorization"}, | ||
{Name: "range"}, | ||
{Name: "referer"}, | ||
{Name: "refresh"}, | ||
{Name: "retry-after"}, | ||
{Name: "server"}, | ||
{Name: "set-cookie"}, | ||
{Name: "strict-transport-security"}, | ||
{Name: "transfer-encoding"}, | ||
{Name: "user-agent"}, | ||
{Name: "vary"}, | ||
{Name: "via"}, | ||
{Name: "www-authenticate"}, | ||
} | ||
|
||
type pairNameValue struct { | ||
name, value string | ||
} | ||
|
||
type byNameItem struct { | ||
name string | ||
id uint64 | ||
} | ||
|
||
type byNameValueItem struct { | ||
pairNameValue | ||
id uint64 | ||
} | ||
|
||
func headerFieldToString(f hpack.HeaderField) string { | ||
return fmt.Sprintf("{Name: \"%s\", Value:\"%s\", Sensitive: %t}", f.Name, f.Value, f.Sensitive) | ||
} | ||
|
||
func pairNameValueToString(v pairNameValue) string { | ||
return fmt.Sprintf("{name: \"%s\", value:\"%s\"}", v.name, v.value) | ||
} | ||
|
||
const header = ` | ||
// go generate gen.go | ||
// Code generated by the command above; DO NOT EDIT. | ||
package hpack | ||
var staticTable = &headerFieldTable{ | ||
evictCount: 0, | ||
byName: map[string]uint64{ | ||
` | ||
|
||
//go:generate go run gen.go | ||
func main() { | ||
var bb bytes.Buffer | ||
fmt.Fprintf(&bb, header) | ||
byName := make(map[string]uint64) | ||
byNameValue := make(map[pairNameValue]uint64) | ||
for index, entry := range staticTableEntries { | ||
id := uint64(index) 1 | ||
byName[entry.Name] = id | ||
byNameValue[pairNameValue{entry.Name, entry.Value}] = id | ||
} | ||
// Sort maps for deterministic generation. | ||
byNameItems := sortByName(byName) | ||
byNameValueItems := sortByNameValue(byNameValue) | ||
|
||
for _, item := range byNameItems { | ||
fmt.Fprintf(&bb, "\"%s\":%d,\n", item.name, item.id) | ||
} | ||
fmt.Fprintf(&bb, "},\n") | ||
fmt.Fprintf(&bb, "byNameValue: map[pairNameValue]uint64{\n") | ||
for _, item := range byNameValueItems { | ||
fmt.Fprintf(&bb, "%s:%d,\n", pairNameValueToString(item.pairNameValue), item.id) | ||
} | ||
fmt.Fprintf(&bb, "},\n") | ||
fmt.Fprintf(&bb, "ents: []HeaderField{\n") | ||
for _, value := range staticTableEntries { | ||
fmt.Fprintf(&bb, "%s,\n", headerFieldToString(value)) | ||
} | ||
fmt.Fprintf(&bb, "},\n") | ||
fmt.Fprintf(&bb, "}\n") | ||
genFile("static_table.go", &bb) | ||
} | ||
|
||
func sortByNameValue(byNameValue map[pairNameValue]uint64) []byNameValueItem { | ||
var byNameValueItems []byNameValueItem | ||
for k, v := range byNameValue { | ||
byNameValueItems = append(byNameValueItems, byNameValueItem{k, v}) | ||
} | ||
sort.Slice(byNameValueItems, func(i, j int) bool { | ||
return byNameValueItems[i].id < byNameValueItems[j].id | ||
}) | ||
return byNameValueItems | ||
} | ||
|
||
func sortByName(byName map[string]uint64) []byNameItem { | ||
var byNameItems []byNameItem | ||
for k, v := range byName { | ||
byNameItems = append(byNameItems, byNameItem{k, v}) | ||
} | ||
sort.Slice(byNameItems, func(i, j int) bool { | ||
return byNameItems[i].id < byNameItems[j].id | ||
}) | ||
return byNameItems | ||
} | ||
|
||
func genFile(name string, buf *bytes.Buffer) { | ||
b, err := format.Source(buf.Bytes()) | ||
if err != nil { | ||
fmt.Fprintln(os.Stderr, err) | ||
os.Exit(1) | ||
} | ||
if err := ioutil.WriteFile(name, b, 0644); err != nil { | ||
fmt.Fprintln(os.Stderr, err) | ||
os.Exit(1) | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.