package encodecounter
import (
"bufio"
"encoding/binary"
"fmt"
"internal/coverage"
"internal/coverage/slicewriter"
"internal/coverage/stringtab"
"internal/coverage/uleb128"
"io"
"os"
"slices"
)
type CoverageDataWriter struct {
stab *stringtab .Writer
w *bufio .Writer
csh coverage .CounterSegmentHeader
tmp []byte
cflavor coverage .CounterFlavor
segs uint32
debug bool
}
func NewCoverageDataWriter (w io .Writer , flav coverage .CounterFlavor ) *CoverageDataWriter {
r := &CoverageDataWriter {
stab : &stringtab .Writer {},
w : bufio .NewWriter (w ),
tmp : make ([]byte , 64 ),
cflavor : flav ,
}
r .stab .InitWriter ()
r .stab .Lookup ("" )
return r
}
type CounterVisitor interface {
VisitFuncs (f CounterVisitorFn ) error
}
type CounterVisitorFn func (pkid uint32 , funcid uint32 , counters []uint32 ) error
func (cfw *CoverageDataWriter ) Write (metaFileHash [16 ]byte , args map [string ]string , visitor CounterVisitor ) error {
if err := cfw .writeHeader (metaFileHash ); err != nil {
return err
}
return cfw .AppendSegment (args , visitor )
}
func padToFourByteBoundary(ws *slicewriter .WriteSeeker ) error {
sz := len (ws .BytesWritten ())
zeros := []byte {0 , 0 , 0 , 0 }
rem := uint32 (sz ) % 4
if rem != 0 {
pad := zeros [:(4 - rem )]
if nw , err := ws .Write (pad ); err != nil {
return err
} else if nw != len (pad ) {
return fmt .Errorf ("error: short write" )
}
}
return nil
}
func (cfw *CoverageDataWriter ) patchSegmentHeader (ws *slicewriter .WriteSeeker ) error {
off , err := ws .Seek (0 , io .SeekCurrent )
if err != nil {
return fmt .Errorf ("error seeking in patchSegmentHeader: %v" , err )
}
if _ , err := ws .Seek (0 , io .SeekStart ); err != nil {
return fmt .Errorf ("error seeking in patchSegmentHeader: %v" , err )
}
if cfw .debug {
fmt .Fprintf (os .Stderr , "=-= writing counter segment header: %+v" , cfw .csh )
}
if err := binary .Write (ws , binary .LittleEndian , cfw .csh ); err != nil {
return err
}
if _ , err := ws .Seek (off , io .SeekStart ); err != nil {
return fmt .Errorf ("error seeking in patchSegmentHeader: %v" , err )
}
return nil
}
func (cfw *CoverageDataWriter ) writeSegmentPreamble (args map [string ]string , ws *slicewriter .WriteSeeker ) error {
if err := binary .Write (ws , binary .LittleEndian , cfw .csh ); err != nil {
return err
}
hdrsz := uint32 (len (ws .BytesWritten ()))
cfw .stab .Freeze ()
if err := cfw .stab .Write (ws ); err != nil {
return err
}
cfw .csh .StrTabLen = uint32 (len (ws .BytesWritten ())) - hdrsz
akeys := make ([]string , 0 , len (args ))
for k := range args {
akeys = append (akeys , k )
}
slices .Sort (akeys )
wrULEB128 := func (v uint ) error {
cfw .tmp = cfw .tmp [:0 ]
cfw .tmp = uleb128 .AppendUleb128 (cfw .tmp , v )
if _ , err := ws .Write (cfw .tmp ); err != nil {
return err
}
return nil
}
if err := wrULEB128 (uint (len (args ))); err != nil {
return err
}
for _ , k := range akeys {
ki := uint (cfw .stab .Lookup (k ))
if err := wrULEB128 (ki ); err != nil {
return err
}
v := args [k ]
vi := uint (cfw .stab .Lookup (v ))
if err := wrULEB128 (vi ); err != nil {
return err
}
}
if err := padToFourByteBoundary (ws ); err != nil {
return err
}
cfw .csh .ArgsLen = uint32 (len (ws .BytesWritten ())) - (cfw .csh .StrTabLen + hdrsz )
return nil
}
func (cfw *CoverageDataWriter ) AppendSegment (args map [string ]string , visitor CounterVisitor ) error {
cfw .stab = &stringtab .Writer {}
cfw .stab .InitWriter ()
cfw .stab .Lookup ("" )
var err error
for k , v := range args {
cfw .stab .Lookup (k )
cfw .stab .Lookup (v )
}
ws := &slicewriter .WriteSeeker {}
if err = cfw .writeSegmentPreamble (args , ws ); err != nil {
return err
}
if err = cfw .writeCounters (visitor , ws ); err != nil {
return err
}
if err = cfw .patchSegmentHeader (ws ); err != nil {
return err
}
if err := cfw .writeBytes (ws .BytesWritten ()); err != nil {
return err
}
if err = cfw .writeFooter (); err != nil {
return err
}
if err := cfw .w .Flush (); err != nil {
return fmt .Errorf ("write error: %v" , err )
}
cfw .stab = nil
return nil
}
func (cfw *CoverageDataWriter ) writeHeader (metaFileHash [16 ]byte ) error {
ch := coverage .CounterFileHeader {
Magic : coverage .CovCounterMagic ,
Version : coverage .CounterFileVersion ,
MetaHash : metaFileHash ,
CFlavor : cfw .cflavor ,
BigEndian : false ,
}
if err := binary .Write (cfw .w , binary .LittleEndian , ch ); err != nil {
return err
}
return nil
}
func (cfw *CoverageDataWriter ) writeBytes (b []byte ) error {
if len (b ) == 0 {
return nil
}
nw , err := cfw .w .Write (b )
if err != nil {
return fmt .Errorf ("error writing counter data: %v" , err )
}
if len (b ) != nw {
return fmt .Errorf ("error writing counter data: short write" )
}
return nil
}
func (cfw *CoverageDataWriter ) writeCounters (visitor CounterVisitor , ws *slicewriter .WriteSeeker ) error {
ctrb := make ([]byte , 4 )
wrval := func (val uint32 ) error {
var buf []byte
var towr int
if cfw .cflavor == coverage .CtrRaw {
binary .LittleEndian .PutUint32 (ctrb , val )
buf = ctrb
towr = 4
} else if cfw .cflavor == coverage .CtrULeb128 {
cfw .tmp = cfw .tmp [:0 ]
cfw .tmp = uleb128 .AppendUleb128 (cfw .tmp , uint (val ))
buf = cfw .tmp
towr = len (buf )
} else {
panic ("internal error: bad counter flavor" )
}
if sz , err := ws .Write (buf ); err != nil {
return err
} else if sz != towr {
return fmt .Errorf ("writing counters: short write" )
}
return nil
}
emitter := func (pkid uint32 , funcid uint32 , counters []uint32 ) error {
cfw .csh .FcnEntries ++
if err := wrval (uint32 (len (counters ))); err != nil {
return err
}
if err := wrval (pkid ); err != nil {
return err
}
if err := wrval (funcid ); err != nil {
return err
}
for _ , val := range counters {
if err := wrval (val ); err != nil {
return err
}
}
return nil
}
if err := visitor .VisitFuncs (emitter ); err != nil {
return err
}
return nil
}
func (cfw *CoverageDataWriter ) writeFooter () error {
cfw .segs ++
cf := coverage .CounterFileFooter {
Magic : coverage .CovCounterMagic ,
NumSegments : cfw .segs ,
}
if err := binary .Write (cfw .w , binary .LittleEndian , cf ); err != nil {
return err
}
return nil
}
The pages are generated with Golds v0.7.0-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .