From 615c7c18a70e0d6638accdb0fcc5f60c57a2118b Mon Sep 17 00:00:00 2001 From: Mikhail Fesenko Date: Wed, 28 Oct 2020 20:35:23 +0000 Subject: [PATCH] cmd/buildid: move and reuse duplicated HashToString code to cmd/internal/buildid/buildid Change-Id: I1e1ac770d4aac12d7d7ec57ef95f77a3e14a678c GitHub-Last-Rev: c01db4346eb08ffe0c1953892fb4222764048e30 GitHub-Pull-Request: golang/go#42052 Reviewed-on: https://go-review.googlesource.com/c/go/+/263418 Run-TryBot: Jay Conrod TryBot-Result: Go Bot Reviewed-by: Jay Conrod Trust: Jay Conrod Trust: Michael Matloob --- src/cmd/buildid/buildid.go | 19 ++-------------- src/cmd/go/internal/work/buildid.go | 35 +++++------------------------ src/cmd/internal/buildid/buildid.go | 29 ++++++++++++++++++++---- 3 files changed, 32 insertions(+), 51 deletions(-) diff --git a/src/cmd/buildid/buildid.go b/src/cmd/buildid/buildid.go index 1c7b228c98..699d977950 100644 --- a/src/cmd/buildid/buildid.go +++ b/src/cmd/buildid/buildid.go @@ -22,21 +22,6 @@ func usage() { var wflag = flag.Bool("w", false, "write build ID") -// taken from cmd/go/internal/work/buildid.go -func hashToString(h [32]byte) string { - const b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" - const chunks = 5 - var dst [chunks * 4]byte - for i := 0; i < chunks; i++ { - v := uint32(h[3*i])<<16 | uint32(h[3*i+1])<<8 | uint32(h[3*i+2]) - dst[4*i+0] = b64[(v>>18)&0x3F] - dst[4*i+1] = b64[(v>>12)&0x3F] - dst[4*i+2] = b64[(v>>6)&0x3F] - dst[4*i+3] = b64[v&0x3F] - } - return string(dst[:]) -} - func main() { log.SetPrefix("buildid: ") log.SetFlags(0) @@ -63,12 +48,12 @@ func main() { log.Fatal(err) } matches, hash, err := buildid.FindAndHash(f, id, 0) + f.Close() if err != nil { log.Fatal(err) } - f.Close() - newID := id[:strings.LastIndex(id, "/")] + "/" + hashToString(hash) + newID := id[:strings.LastIndex(id, "/")] + "/" + buildid.HashToString(hash) if len(newID) != len(id) { log.Fatalf("%s: build ID length mismatch %q vs %q", file, id, newID) } diff --git a/src/cmd/go/internal/work/buildid.go b/src/cmd/go/internal/work/buildid.go index 5cd3124e54..9ef141c619 100644 --- a/src/cmd/go/internal/work/buildid.go +++ b/src/cmd/go/internal/work/buildid.go @@ -31,7 +31,7 @@ import ( // // actionID/[.../]contentID // -// where the actionID and contentID are prepared by hashToString below. +// where the actionID and contentID are prepared by buildid.HashToString below. // and are found by looking for the first or last slash. // Usually the buildID is simply actionID/contentID, but see below for an // exception. @@ -108,31 +108,6 @@ func contentID(buildID string) string { return buildID[strings.LastIndex(buildID, buildIDSeparator)+1:] } -// hashToString converts the hash h to a string to be recorded -// in package archives and binaries as part of the build ID. -// We use the first 120 bits of the hash (5 chunks of 24 bits each) and encode -// it in base64, resulting in a 20-byte string. Because this is only used for -// detecting the need to rebuild installed files (not for lookups -// in the object file cache), 120 bits are sufficient to drive the -// probability of a false "do not need to rebuild" decision to effectively zero. -// We embed two different hashes in archives and four in binaries, -// so cutting to 20 bytes is a significant savings when build IDs are displayed. -// (20*4+3 = 83 bytes compared to 64*4+3 = 259 bytes for the -// more straightforward option of printing the entire h in base64). -func hashToString(h [cache.HashSize]byte) string { - const b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" - const chunks = 5 - var dst [chunks * 4]byte - for i := 0; i < chunks; i++ { - v := uint32(h[3*i])<<16 | uint32(h[3*i+1])<<8 | uint32(h[3*i+2]) - dst[4*i+0] = b64[(v>>18)&0x3F] - dst[4*i+1] = b64[(v>>12)&0x3F] - dst[4*i+2] = b64[(v>>6)&0x3F] - dst[4*i+3] = b64[v&0x3F] - } - return string(dst[:]) -} - // toolID returns the unique ID to use for the current copy of the // named tool (asm, compile, cover, link). // @@ -404,7 +379,7 @@ func (b *Builder) fileHash(file string) string { if err != nil { return "" } - return hashToString(sum) + return buildid.HashToString(sum) } // useCache tries to satisfy the action a, which has action ID actionHash, @@ -427,7 +402,7 @@ func (b *Builder) useCache(a *Action, actionHash cache.ActionID, target string) // the actionID half; if it also appeared in the input that would be like an // engineered 120-bit partial SHA256 collision. a.actionID = actionHash - actionID := hashToString(actionHash) + actionID := buildid.HashToString(actionHash) if a.json != nil { a.json.ActionID = actionID } @@ -480,7 +455,7 @@ func (b *Builder) useCache(a *Action, actionHash cache.ActionID, target string) // build IDs of completed actions. oldBuildID := a.buildID a.buildID = id[1] + buildIDSeparator + id[2] - linkID := hashToString(b.linkActionID(a.triggers[0])) + linkID := buildid.HashToString(b.linkActionID(a.triggers[0])) if id[0] == linkID { // Best effort attempt to display output from the compile and link steps. // If it doesn't work, it doesn't work: reusing the cached binary is more @@ -654,7 +629,7 @@ func (b *Builder) updateBuildID(a *Action, target string, rewrite bool) error { if err != nil { return err } - newID := a.buildID[:strings.LastIndex(a.buildID, buildIDSeparator)] + buildIDSeparator + hashToString(hash) + newID := a.buildID[:strings.LastIndex(a.buildID, buildIDSeparator)] + buildIDSeparator + buildid.HashToString(hash) if len(newID) != len(a.buildID) { return fmt.Errorf("internal error: build ID length mismatch %q vs %q", a.buildID, newID) } diff --git a/src/cmd/internal/buildid/buildid.go b/src/cmd/internal/buildid/buildid.go index 1d6563cafc..1e8855d3ac 100644 --- a/src/cmd/internal/buildid/buildid.go +++ b/src/cmd/internal/buildid/buildid.go @@ -17,12 +17,8 @@ import ( ) var ( - errBuildIDToolchain = fmt.Errorf("build ID only supported in gc toolchain") errBuildIDMalformed = fmt.Errorf("malformed object file") - errBuildIDUnknown = fmt.Errorf("lost build ID") -) -var ( bangArch = []byte("!") pkgdef = []byte("__.PKGDEF") goobject = []byte("go object ") @@ -320,3 +316,28 @@ func readRaw(name string, data []byte) (id string, err error) { } return id, nil } + +// HashToString converts the hash h to a string to be recorded +// in package archives and binaries as part of the build ID. +// We use the first 120 bits of the hash (5 chunks of 24 bits each) and encode +// it in base64, resulting in a 20-byte string. Because this is only used for +// detecting the need to rebuild installed files (not for lookups +// in the object file cache), 120 bits are sufficient to drive the +// probability of a false "do not need to rebuild" decision to effectively zero. +// We embed two different hashes in archives and four in binaries, +// so cutting to 20 bytes is a significant savings when build IDs are displayed. +// (20*4+3 = 83 bytes compared to 64*4+3 = 259 bytes for the +// more straightforward option of printing the entire h in base64). +func HashToString(h [32]byte) string { + const b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" + const chunks = 5 + var dst [chunks * 4]byte + for i := 0; i < chunks; i++ { + v := uint32(h[3*i])<<16 | uint32(h[3*i+1])<<8 | uint32(h[3*i+2]) + dst[4*i+0] = b64[(v>>18)&0x3F] + dst[4*i+1] = b64[(v>>12)&0x3F] + dst[4*i+2] = b64[(v>>6)&0x3F] + dst[4*i+3] = b64[v&0x3F] + } + return string(dst[:]) +} -- 2.48.1