p.tracef("%q ", s)
}
p.rawInt64(int64(len(s)))
- w, err := obj.Bwritestring(p.out, s)
- p.written += w
- if w != len(s) || err != nil {
- Fatalf("write error: %v (wrote %d bytes of %d)", err, w, len(s))
+ for i := 0; i < len(s); i++ {
+ p.byte(s[i])
}
}
p.rawInt64(int64(p.written))
}
-func (p *exporter) byte(b byte) {
- obj.Bputc(p.out, b)
- p.written++
-}
-
// rawInt64 should only be used by low-level encoders
func (p *exporter) rawInt64(x int64) {
var tmp [binary.MaxVarintLen64]byte
n := binary.PutVarint(tmp[:], x)
- w, err := p.out.Write(tmp[:n])
- p.written += w
- if err != nil {
- Fatalf("write error: %v", err)
+ for i := 0; i < n; i++ {
+ p.byte(tmp[i])
}
}
+// byte is the bottleneck interface to write to p.out.
+// byte escapes b as follows (any encoding does that
+// hides '$'):
+//
+// '$' => '|' 'S'
+// '|' => '|' '|'
+//
+// Necessary so other tools can find the end of the
+// export data by searching for "$$".
+func (p *exporter) byte(b byte) {
+ switch b {
+ case '$':
+ // write '$' as '|' 'S'
+ b = 'S'
+ fallthrough
+ case '|':
+ // write '|' as '|' '|'
+ obj.Bputc(p.out, '|')
+ p.written++
+ }
+ obj.Bputc(p.out, b)
+ p.written++
+}
+
// tracef is like fmt.Printf but it rewrites the format string
// to take care of indentation.
func (p *exporter) tracef(format string, args ...interface{}) {
} else {
p.buf = p.buf[:n]
}
- r := obj.Bread(p.in, p.buf)
- p.read += r
- if r != n {
- Fatalf("read error: read %d bytes of %d", r, n)
+ for i := 0; i < n; i++ {
+ p.buf[i] = p.byte()
}
return string(p.buf)
}
}
}
-func (p *importer) byte() byte {
- if c := obj.Bgetc(p.in); c >= 0 {
- p.read++
- return byte(c)
- }
- Fatalf("read error")
- return 0
-}
-
// rawInt64 should only be used by low-level decoders
func (p *importer) rawInt64() int64 {
i, err := binary.ReadVarint(p)
func (p *importer) ReadByte() (byte, error) {
return p.byte(), nil
}
+
+// byte is the bottleneck interface for reading from p.in.
+// It unescapes '|' 'S' to '$' and '|' '|' to '|'.
+func (p *importer) byte() byte {
+ c := obj.Bgetc(p.in)
+ p.read++
+ if c < 0 {
+ Fatalf("read error")
+ }
+ if c == '|' {
+ c = obj.Bgetc(p.in)
+ p.read++
+ if c < 0 {
+ Fatalf("read error")
+ }
+ switch c {
+ case 'S':
+ c = '$'
+ case '|':
+ // nothing to do
+ default:
+ Fatalf("unexpected escape sequence in export data")
+ }
+ }
+ return byte(c)
+}
if n, err := bout.Write(copy.Bytes()); n != size || err != nil {
Fatalf("error writing export data: got %d bytes, want %d bytes, err = %v", n, size, err)
}
-
- // verify there's no "\n$$\n" inside the export data
- // TODO(gri) fragile - the end marker needs to be fixed
- // TODO(gri) investigate if exporting a string containing "\n$$\n"
- // causes problems (old and new format)
- if bytes.Index(copy.Bytes(), []byte("\n$$\n")) >= 0 {
- Fatalf("export data contains end marker in its midst")
+ // export data must contain no '$' so that we can find the end by searching for "$$"
+ if bytes.IndexByte(copy.Bytes(), '$') >= 0 {
+ Fatalf("export data contains $")
}
// verify that we can read the copied export data back in
// If data is obviously malformed, an error is returned but in
// general it is not recommended to call BImportData on untrusted data.
func BImportData(imports map[string]*types.Package, data []byte, path string) (int, *types.Package, error) {
- // determine low-level encoding format
- read := 0
- var format byte = 'm' // missing format
- if len(data) > 0 {
- format = data[0]
- data = data[1:]
- read++
+ p := importer{
+ imports: imports,
+ data: data,
}
- if format != 'c' && format != 'd' {
- return read, nil, fmt.Errorf("invalid encoding format in export data: got %q; want 'c' or 'd'", format)
+ p.buf = p.bufarray[:]
+
+ // read low-level encoding format
+ switch format := p.byte(); format {
+ case 'c':
+ // compact format - nothing to do
+ case 'd':
+ p.debugFormat = true
+ default:
+ return p.read, nil, fmt.Errorf("invalid encoding format in export data: got %q; want 'c' or 'd'", format)
}
// --- generic export data ---
- p := importer{
- imports: imports,
- data: data,
- debugFormat: format == 'd',
- read: read,
- }
-
if v := p.string(); v != "v0" {
return p.read, nil, fmt.Errorf("unknown version: %s", v)
}
_ = p.typ().(*types.Named)
}
+ // ignore compiler-specific import data
+
// complete interfaces
for _, typ := range p.typList {
if it, ok := typ.(*types.Interface); ok {
}
type importer struct {
- imports map[string]*types.Package
- data []byte
- pkgList []*types.Package
- typList []types.Type
+ imports map[string]*types.Package
+ data []byte
+ buf []byte // for reading strings
+ bufarray [64]byte // initial underlying array for buf, large enough to avoid allocation when compiling std lib
+ pkgList []*types.Package
+ typList []types.Type
debugFormat bool
read int // bytes read
}
func (p *importer) value() constant.Value {
- switch kind := constant.Kind(p.int()); kind {
+ switch tag := p.tagOrIndex(); tag {
case falseTag:
return constant.MakeBool(false)
case trueTag:
case stringTag:
return constant.MakeString(p.string())
default:
- panic(fmt.Sprintf("unexpected value kind %d", kind))
+ panic(fmt.Sprintf("unexpected value tag %d", tag))
}
}
}
func (p *importer) int() int {
- return int(p.int64())
+ x := p.int64()
+ if int64(int(x)) != x {
+ panic("exported integer too large")
+ }
+ return int(x)
}
func (p *importer) int64() int64 {
p.marker('s')
}
- var b []byte
if n := int(p.rawInt64()); n > 0 {
- b = p.data[:n]
- p.data = p.data[n:]
- p.read += n
+ if cap(p.buf) < n {
+ p.buf = make([]byte, n)
+ } else {
+ p.buf = p.buf[:n]
+ }
+ for i := 0; i < n; i++ {
+ p.buf[i] = p.byte()
+ }
+ return string(p.buf)
}
- return string(b)
+
+ return ""
}
func (p *importer) marker(want byte) {
- if got := p.data[0]; got != want {
+ if got := p.byte(); got != want {
panic(fmt.Sprintf("incorrect marker: got %c; want %c (pos = %d)", got, want, p.read))
}
- p.data = p.data[1:]
- p.read++
pos := p.read
if n := int(p.rawInt64()); n != pos {
// rawInt64 should only be used by low-level decoders
func (p *importer) rawInt64() int64 {
- i, n := binary.Varint(p.data)
- p.data = p.data[n:]
- p.read += n
+ i, err := binary.ReadVarint(p)
+ if err != nil {
+ panic(fmt.Sprintf("read error: %v", err))
+ }
return i
}
+// needed for binary.ReadVarint in rawInt64
+func (p *importer) ReadByte() (byte, error) {
+ return p.byte(), nil
+}
+
+// byte is the bottleneck interface for reading p.data.
+// It unescapes '|' 'S' to '$' and '|' '|' to '|'.
+func (p *importer) byte() byte {
+ b := p.data[0]
+ r := 1
+ if b == '|' {
+ b = p.data[1]
+ r = 2
+ switch b {
+ case 'S':
+ b = '$'
+ case '|':
+ // nothing to do
+ default:
+ panic("unexpected escape sequence in export data")
+ }
+ }
+ p.data = p.data[r:]
+ p.read += r
+ return b
+
+}
+
// ----------------------------------------------------------------------------
// Export format