go/internal/gcimporter: don't waste CPU copying bytes in io.ReadAll

`io.ReadAll` dynamically reallocates byte slice because it doesn't know
its size in advance. We don't need to read an entire file into memory
and therefore may use `bufio.Reader` to read its contents.

Fixes #46564

Change-Id: Id504b1512662b6dea4775d523455896fa4162ab3
Reviewed-on: https://go-review.googlesource.com/c/go/+/325429
Reviewed-by: Dominik Honnef <dominik@honnef.co>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Trust: Matthew Dempsky <mdempsky@google.com>
Trust: Dominik Honnef <dominik@honnef.co>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
This commit is contained in:
Sergey Zagursky 2021-06-04 12:25:51 +03:00 committed by Matthew Dempsky
parent 9d669ed47a
commit e3cb381704
2 changed files with 15 additions and 17 deletions

View file

@ -145,17 +145,14 @@ func Import(fset *token.FileSet, packages map[string]*types.Package, path, srcDi
err = fmt.Errorf("import %q: old textual export format no longer supported (recompile library)", path) err = fmt.Errorf("import %q: old textual export format no longer supported (recompile library)", path)
case "$$B\n": case "$$B\n":
var data []byte var exportFormat byte
data, err = io.ReadAll(buf) exportFormat, err = buf.ReadByte()
if err != nil {
break
}
// The indexed export format starts with an 'i'; the older // The indexed export format starts with an 'i'; the older
// binary export format starts with a 'c', 'd', or 'v' // binary export format starts with a 'c', 'd', or 'v'
// (from "version"). Select appropriate importer. // (from "version"). Select appropriate importer.
if len(data) > 0 && data[0] == 'i' { if err == nil && exportFormat == 'i' {
_, pkg, err = iImportData(fset, packages, data[1:], id) pkg, err = iImportData(fset, packages, buf, id)
} else { } else {
err = fmt.Errorf("import %q: old binary export format no longer supported (recompile library)", path) err = fmt.Errorf("import %q: old binary export format no longer supported (recompile library)", path)
} }

View file

@ -8,6 +8,7 @@
package gcimporter package gcimporter
import ( import (
"bufio"
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
@ -20,7 +21,7 @@ import (
) )
type intReader struct { type intReader struct {
*bytes.Reader *bufio.Reader
path string path string
} }
@ -61,7 +62,7 @@ const (
// and returns the number of bytes consumed and a reference to the package. // and returns the number of bytes consumed and a reference to the package.
// If the export data version is not recognized or the format is otherwise // If the export data version is not recognized or the format is otherwise
// compromised, an error is returned. // compromised, an error is returned.
func iImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) { func iImportData(fset *token.FileSet, imports map[string]*types.Package, dataReader *bufio.Reader, path string) (pkg *types.Package, err error) {
const currentVersion = 1 const currentVersion = 1
version := int64(-1) version := int64(-1)
defer func() { defer func() {
@ -74,7 +75,7 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, data []
} }
}() }()
r := &intReader{bytes.NewReader(data), path} r := &intReader{dataReader, path}
version = int64(r.uint64()) version = int64(r.uint64())
switch version { switch version {
@ -86,10 +87,12 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, data []
sLen := int64(r.uint64()) sLen := int64(r.uint64())
dLen := int64(r.uint64()) dLen := int64(r.uint64())
whence, _ := r.Seek(0, io.SeekCurrent) data := make([]byte, sLen+dLen)
stringData := data[whence : whence+sLen] if _, err := io.ReadFull(r, data); err != nil {
declData := data[whence+sLen : whence+sLen+dLen] errorf("cannot read %d bytes of stringData and declData: %s", len(data), err)
r.Seek(sLen+dLen, io.SeekCurrent) }
stringData := data[:sLen]
declData := data[sLen:]
p := iimporter{ p := iimporter{
ipath: path, ipath: path,
@ -165,9 +168,7 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, data []
// package was imported completely and without errors // package was imported completely and without errors
localpkg.MarkComplete() localpkg.MarkComplete()
return localpkg, nil
consumed, _ := r.Seek(0, io.SeekCurrent)
return int(consumed), localpkg, nil
} }
type iimporter struct { type iimporter struct {