From baf4e38fc70fc0414c8de7f912e9cd2c185d315b Mon Sep 17 00:00:00 2001 From: Dominik Honnef Date: Wed, 20 Oct 2021 04:46:00 +0200 Subject: [PATCH] go/gcexportdata: limit reader to the export data section of the archive When given an archive, NewReader can figure out the size of the export data and there is no need to read the entire file, which includes object code for the package in addition to export data. We could make the same change in gcimporter.Import but choose not to, as the function has no users. Fixes golang/go#48954 Change-Id: I11e8e448c3133465f0e6ab01b3532da3732387af Reviewed-on: https://go-review.googlesource.com/c/tools/+/357209 Trust: Dominik Honnef Run-TryBot: Robert Findley gopls-CI: kokoro TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- go/gcexportdata/gcexportdata.go | 23 ++++++++++++++++++----- go/internal/gcimporter/exportdata.go | 16 +++++++++++----- go/internal/gcimporter/gcimporter.go | 2 +- go/internal/gcimporter/iexport_test.go | 2 +- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/go/gcexportdata/gcexportdata.go b/go/gcexportdata/gcexportdata.go index fc8beea5d8..cec819d641 100644 --- a/go/gcexportdata/gcexportdata.go +++ b/go/gcexportdata/gcexportdata.go @@ -50,11 +50,24 @@ func Find(importPath, srcDir string) (filename, path string) { // additional trailing data beyond the end of the export data. func NewReader(r io.Reader) (io.Reader, error) { buf := bufio.NewReader(r) - _, err := gcimporter.FindExportData(buf) - // If we ever switch to a zip-like archive format with the ToC - // at the end, we can return the correct portion of export data, - // but for now we must return the entire rest of the file. - return buf, err + _, size, err := gcimporter.FindExportData(buf) + if err != nil { + return nil, err + } + + if size >= 0 { + // We were given an archive and found the __.PKGDEF in it. + // This tells us the size of the export data, and we don't + // need to return the entire file. + return &io.LimitedReader{ + R: buf, + N: size, + }, nil + } else { + // We were given an object file. As such, we don't know how large + // the export data is and must return the entire file. + return buf, nil + } } // Read reads export data from in, decodes it, and returns type diff --git a/go/internal/gcimporter/exportdata.go b/go/internal/gcimporter/exportdata.go index f33dc5613e..f6437feb1c 100644 --- a/go/internal/gcimporter/exportdata.go +++ b/go/internal/gcimporter/exportdata.go @@ -16,7 +16,7 @@ import ( "strings" ) -func readGopackHeader(r *bufio.Reader) (name string, size int, err error) { +func readGopackHeader(r *bufio.Reader) (name string, size int64, err error) { // See $GOROOT/include/ar.h. hdr := make([]byte, 16+12+6+6+8+10+2) _, err = io.ReadFull(r, hdr) @@ -28,7 +28,8 @@ func readGopackHeader(r *bufio.Reader) (name string, size int, err error) { fmt.Printf("header: %s", hdr) } s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10])) - size, err = strconv.Atoi(s) + length, err := strconv.Atoi(s) + size = int64(length) if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' { err = fmt.Errorf("invalid archive header") return @@ -42,8 +43,8 @@ func readGopackHeader(r *bufio.Reader) (name string, size int, err error) { // file by reading from it. The reader must be positioned at the // start of the file before calling this function. The hdr result // is the string before the export data, either "$$" or "$$B". -// -func FindExportData(r *bufio.Reader) (hdr string, err error) { +// The size result is the length of the export data in bytes, or -1 if not known. +func FindExportData(r *bufio.Reader) (hdr string, size int64, err error) { // Read first line to make sure this is an object file. line, err := r.ReadSlice('\n') if err != nil { @@ -54,7 +55,7 @@ func FindExportData(r *bufio.Reader) (hdr string, err error) { if string(line) == "!\n" { // Archive file. Scan to __.PKGDEF. var name string - if name, _, err = readGopackHeader(r); err != nil { + if name, size, err = readGopackHeader(r); err != nil { return } @@ -70,6 +71,7 @@ func FindExportData(r *bufio.Reader) (hdr string, err error) { err = fmt.Errorf("can't find export data (%v)", err) return } + size -= int64(len(line)) } // Now at __.PKGDEF in archive or still at beginning of file. @@ -86,8 +88,12 @@ func FindExportData(r *bufio.Reader) (hdr string, err error) { err = fmt.Errorf("can't find export data (%v)", err) return } + size -= int64(len(line)) } hdr = string(line) + if size < 0 { + size = -1 + } return } diff --git a/go/internal/gcimporter/gcimporter.go b/go/internal/gcimporter/gcimporter.go index 519ef3b769..3ab66830d7 100644 --- a/go/internal/gcimporter/gcimporter.go +++ b/go/internal/gcimporter/gcimporter.go @@ -185,7 +185,7 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func var hdr string buf := bufio.NewReader(rc) - if hdr, err = FindExportData(buf); err != nil { + if hdr, _, err = FindExportData(buf); err != nil { return } diff --git a/go/internal/gcimporter/iexport_test.go b/go/internal/gcimporter/iexport_test.go index 084679bbd4..b326f307d6 100644 --- a/go/internal/gcimporter/iexport_test.go +++ b/go/internal/gcimporter/iexport_test.go @@ -42,7 +42,7 @@ func readExportFile(filename string) ([]byte, error) { defer f.Close() buf := bufio.NewReader(f) - if _, err := gcimporter.FindExportData(buf); err != nil { + if _, _, err := gcimporter.FindExportData(buf); err != nil { return nil, err }