diff options
Diffstat (limited to 'src/internal/coverage/slicereader')
-rw-r--r-- | src/internal/coverage/slicereader/slicereader.go | 123 | ||||
-rw-r--r-- | src/internal/coverage/slicereader/slr_test.go | 95 |
2 files changed, 218 insertions, 0 deletions
diff --git a/src/internal/coverage/slicereader/slicereader.go b/src/internal/coverage/slicereader/slicereader.go new file mode 100644 index 0000000..d9f2a7e --- /dev/null +++ b/src/internal/coverage/slicereader/slicereader.go @@ -0,0 +1,123 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package slicereader + +import ( + "encoding/binary" + "fmt" + "io" + "unsafe" +) + +// This file contains the helper "SliceReader", a utility for +// reading values from a byte slice that may or may not be backed +// by a read-only mmap'd region. + +type Reader struct { + b []byte + readonly bool + off int64 +} + +func NewReader(b []byte, readonly bool) *Reader { + r := Reader{ + b: b, + readonly: readonly, + } + return &r +} + +func (r *Reader) Read(b []byte) (int, error) { + amt := len(b) + toread := r.b[r.off:] + if len(toread) < amt { + amt = len(toread) + } + copy(b, toread) + r.off += int64(amt) + return amt, nil +} + +func (r *Reader) Seek(offset int64, whence int) (ret int64, err error) { + switch whence { + case io.SeekStart: + if offset < 0 || offset > int64(len(r.b)) { + return 0, fmt.Errorf("invalid seek: new offset %d (out of range [0 %d]", offset, len(r.b)) + } + r.off = offset + return offset, nil + case io.SeekCurrent: + newoff := r.off + offset + if newoff < 0 || newoff > int64(len(r.b)) { + return 0, fmt.Errorf("invalid seek: new offset %d (out of range [0 %d]", newoff, len(r.b)) + } + r.off = newoff + return r.off, nil + case io.SeekEnd: + newoff := int64(len(r.b)) + offset + if newoff < 0 || newoff > int64(len(r.b)) { + return 0, fmt.Errorf("invalid seek: new offset %d (out of range [0 %d]", newoff, len(r.b)) + } + r.off = newoff + return r.off, nil + } + // other modes are not supported + return 0, fmt.Errorf("unsupported seek mode %d", whence) +} + +func (r *Reader) Offset() int64 { + return r.off +} + +func (r *Reader) ReadUint8() uint8 { + rv := uint8(r.b[int(r.off)]) + r.off += 1 + return rv +} + +func (r *Reader) ReadUint32() uint32 { + end := int(r.off) + 4 + rv := binary.LittleEndian.Uint32(r.b[int(r.off):end:end]) + r.off += 4 + return rv +} + +func (r *Reader) ReadUint64() uint64 { + end := int(r.off) + 8 + rv := binary.LittleEndian.Uint64(r.b[int(r.off):end:end]) + r.off += 8 + return rv +} + +func (r *Reader) ReadULEB128() (value uint64) { + var shift uint + + for { + b := r.b[r.off] + r.off++ + value |= (uint64(b&0x7F) << shift) + if b&0x80 == 0 { + break + } + shift += 7 + } + return +} + +func (r *Reader) ReadString(len int64) string { + b := r.b[r.off : r.off+len] + r.off += len + if r.readonly { + return toString(b) // backed by RO memory, ok to make unsafe string + } + return string(b) +} + +func toString(b []byte) string { + if len(b) == 0 { + return "" + } + return unsafe.String(&b[0], len(b)) +} diff --git a/src/internal/coverage/slicereader/slr_test.go b/src/internal/coverage/slicereader/slr_test.go new file mode 100644 index 0000000..461436d --- /dev/null +++ b/src/internal/coverage/slicereader/slr_test.go @@ -0,0 +1,95 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package slicereader + +import ( + "encoding/binary" + "io" + "testing" +) + +func TestSliceReader(t *testing.T) { + b := []byte{} + + bt := make([]byte, 4) + e32 := uint32(1030507) + binary.LittleEndian.PutUint32(bt, e32) + b = append(b, bt...) + + bt = make([]byte, 8) + e64 := uint64(907050301) + binary.LittleEndian.PutUint64(bt, e64) + b = append(b, bt...) + + b = appendUleb128(b, uint(e32)) + b = appendUleb128(b, uint(e64)) + b = appendUleb128(b, 6) + s1 := "foobar" + s1b := []byte(s1) + b = append(b, s1b...) + b = appendUleb128(b, 9) + s2 := "bazbasher" + s2b := []byte(s2) + b = append(b, s2b...) + + readStr := func(slr *Reader) string { + len := slr.ReadULEB128() + return slr.ReadString(int64(len)) + } + + for i := 0; i < 2; i++ { + slr := NewReader(b, i == 0) + g32 := slr.ReadUint32() + if g32 != e32 { + t.Fatalf("slr.ReadUint32() got %d want %d", g32, e32) + } + g64 := slr.ReadUint64() + if g64 != e64 { + t.Fatalf("slr.ReadUint64() got %d want %d", g64, e64) + } + g32 = uint32(slr.ReadULEB128()) + if g32 != e32 { + t.Fatalf("slr.ReadULEB128() got %d want %d", g32, e32) + } + g64 = slr.ReadULEB128() + if g64 != e64 { + t.Fatalf("slr.ReadULEB128() got %d want %d", g64, e64) + } + gs1 := readStr(slr) + if gs1 != s1 { + t.Fatalf("readStr got %s want %s", gs1, s1) + } + gs2 := readStr(slr) + if gs2 != s2 { + t.Fatalf("readStr got %s want %s", gs2, s2) + } + if _, err := slr.Seek(4, io.SeekStart); err != nil { + t.Fatal(err) + } + off := slr.Offset() + if off != 4 { + t.Fatalf("Offset() returned %d wanted 4", off) + } + g64 = slr.ReadUint64() + if g64 != e64 { + t.Fatalf("post-seek slr.ReadUint64() got %d want %d", g64, e64) + } + } +} + +func appendUleb128(b []byte, v uint) []byte { + for { + c := uint8(v & 0x7f) + v >>= 7 + if v != 0 { + c |= 0x80 + } + b = append(b, c) + if c&0x80 == 0 { + break + } + } + return b +} |