Skip to content

Commit

Permalink
internal/go/gcexportdata: read file directly when possible
Browse files Browse the repository at this point in the history
  • Loading branch information
dominikh committed May 3, 2020
1 parent c0cf87d commit 009a146
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 7 deletions.
45 changes: 41 additions & 4 deletions internal/go/gcexportdata/gcexportdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,32 @@ import (
"go/types"
"io"
"io/ioutil"
"os"

"honnef.co/go/tools/internal/go/gcimporter"
)

type bufferedReader struct {
r io.Reader
buf *bufio.Reader
}

func (r *bufferedReader) Read(b []byte) (int, error) {
return r.buf.Read(b)
}

func (r *bufferedReader) ReadSlice(delim byte) (line []byte, err error) {
return r.buf.ReadSlice(delim)
}

// NewReader returns a reader for the export data section of an object
// (.o) or archive (.a) file read from r. The new reader may provide
// additional trailing data beyond the end of the export data.
func NewReader(r io.Reader) (io.Reader, error) {
buf := bufio.NewReader(r)
buf := &bufferedReader{
r: r,
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,
Expand All @@ -57,9 +74,29 @@ func NewReader(r io.Reader) (io.Reader, error) {
//
// On return, the state of the reader is undefined.
func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package, path string) (*types.Package, error) {
data, err := ioutil.ReadAll(in)
if err != nil {
return nil, fmt.Errorf("reading export data for %q: %v", path, err)
var data []byte
if br, ok := in.(*bufferedReader); ok {
if f, ok := br.r.(*os.File); ok {
fi, err := f.Stat()
if err == nil {
// we expect to be close to the start of the file,
// which is why we don't bother checking with
// SEEK_CUR.
data = make([]byte, fi.Size())
n, err := io.ReadFull(in, data)
data = data[:n]
if err != nil && err != io.ErrUnexpectedEOF {
data = nil
}
}
}
}
if data == nil {
var err error
data, err = ioutil.ReadAll(in)
if err != nil {
return nil, fmt.Errorf("reading export data for %q: %v", path, err)
}
}

if bytes.HasPrefix(data, []byte("!<arch>")) {
Expand Down
10 changes: 7 additions & 3 deletions internal/go/gcimporter/exportdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@
package gcimporter

import (
"bufio"
"fmt"
"io"
"strconv"
"strings"
)

func readGopackHeader(r *bufio.Reader) (name string, size int, err error) {
func readGopackHeader(r io.Reader) (name string, size int, err error) {
// See $GOROOT/include/ar.h.
hdr := make([]byte, 16+12+6+6+8+10+2)
_, err = io.ReadFull(r, hdr)
Expand All @@ -37,13 +36,18 @@ func readGopackHeader(r *bufio.Reader) (name string, size int, err error) {
return
}

type BufferedReader interface {
Read(b []byte) (int, error)
ReadSlice(delim byte) (line []byte, err error)
}

// FindExportData positions the reader r at the beginning of the
// export data section of an underlying GC-created object/archive
// 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) {
func FindExportData(r BufferedReader) (hdr string, err error) {
// Read first line to make sure this is an object file.
line, err := r.ReadSlice('\n')
if err != nil {
Expand Down

0 comments on commit 009a146

Please sign in to comment.