aboutsummaryrefslogtreecommitdiff
path: root/src/internal/coverage/slicereader
diff options
context:
space:
mode:
authorThan McIntosh <thanm@google.com>2021-09-29 16:42:55 -0400
committerThan McIntosh <thanm@google.com>2022-09-26 20:52:13 +0000
commit84f95aa811ed4fdb316f787fe2af80b30d531abf (patch)
tree606169df535b939938e1f0f4e6507bc23ff98106 /src/internal/coverage/slicereader
parentf951f697c45ace2f00dccd8d2533463b6538dc36 (diff)
downloadgo-84f95aa811ed4fdb316f787fe2af80b30d531abf.tar.xz
internal/coverage: add coverage meta-data decoder
Add a coverage meta-data decoder, which provides APIs for reading encoded coverage meta-data and expanding it usable form. This package is intended to be used in the coverage tooling that reads data files emitted from coverage runs. Along with the new decoding package is a unit test that runs the encode/decode paths together to check to make sure that "decode(encode(X)) == X". Updates #51430. Change-Id: I81d27d8da0b2fcfa5039114a6e35a4b463d19b3c Reviewed-on: https://go-review.googlesource.com/c/go/+/353454 Reviewed-by: David Chase <drchase@google.com> Run-TryBot: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
Diffstat (limited to 'src/internal/coverage/slicereader')
-rw-r--r--src/internal/coverage/slicereader/slicereader.go105
-rw-r--r--src/internal/coverage/slicereader/slr_test.go92
2 files changed, 197 insertions, 0 deletions
diff --git a/src/internal/coverage/slicereader/slicereader.go b/src/internal/coverage/slicereader/slicereader.go
new file mode 100644
index 0000000000..c949e1723d
--- /dev/null
+++ b/src/internal/coverage/slicereader/slicereader.go
@@ -0,0 +1,105 @@
+// 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"
+ "internal/unsafeheader"
+ "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) SeekTo(off int64) {
+ r.off = off
+}
+
+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 ""
+ }
+
+ var s string
+ hdr := (*unsafeheader.String)(unsafe.Pointer(&s))
+ hdr.Data = unsafe.Pointer(&b[0])
+ hdr.Len = len(b)
+
+ return s
+}
diff --git a/src/internal/coverage/slicereader/slr_test.go b/src/internal/coverage/slicereader/slr_test.go
new file mode 100644
index 0000000000..2f7cef00f8
--- /dev/null
+++ b/src/internal/coverage/slicereader/slr_test.go
@@ -0,0 +1,92 @@
+// 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"
+ "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)
+ }
+ slr.SeekTo(4)
+ off := slr.Offset()
+ if off != 4 {
+ t.Fatalf("Offset(0 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
+}