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>
This commit is contained in:
Than McIntosh 2021-09-29 16:42:55 -04:00
parent f951f697c4
commit 84f95aa811
9 changed files with 1082 additions and 3 deletions

View file

@ -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
}

View file

@ -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
}