package encodemeta
import (
"bytes"
"crypto/md5"
"encoding/binary"
"fmt"
"hash"
"internal/coverage"
"internal/coverage/stringtab"
"internal/coverage/uleb128"
"io"
"os"
)
type CoverageMetaDataBuilder struct {
stab stringtab .Writer
funcs []funcDesc
tmp []byte
h hash .Hash
pkgpath uint32
pkgname uint32
modpath uint32
debug bool
werr error
}
func NewCoverageMetaDataBuilder (pkgpath string , pkgname string , modulepath string ) (*CoverageMetaDataBuilder , error ) {
if pkgpath == "" {
return nil , fmt .Errorf ("invalid empty package path" )
}
x := &CoverageMetaDataBuilder {
tmp : make ([]byte , 0 , 256 ),
h : md5 .New (),
}
x .stab .InitWriter ()
x .stab .Lookup ("" )
x .pkgpath = x .stab .Lookup (pkgpath )
x .pkgname = x .stab .Lookup (pkgname )
x .modpath = x .stab .Lookup (modulepath )
io .WriteString (x .h , pkgpath )
io .WriteString (x .h , pkgname )
io .WriteString (x .h , modulepath )
return x , nil
}
func h32(x uint32 , h hash .Hash , tmp []byte ) {
tmp = tmp [:0 ]
tmp = append (tmp , 0 , 0 , 0 , 0 )
binary .LittleEndian .PutUint32 (tmp , x )
h .Write (tmp )
}
type funcDesc struct {
encoded []byte
}
func (b *CoverageMetaDataBuilder ) AddFunc (f coverage .FuncDesc ) uint {
hashFuncDesc (b .h , &f , b .tmp )
fd := funcDesc {}
b .tmp = b .tmp [:0 ]
b .tmp = uleb128 .AppendUleb128 (b .tmp , uint (len (f .Units )))
b .tmp = uleb128 .AppendUleb128 (b .tmp , uint (b .stab .Lookup (f .Funcname )))
b .tmp = uleb128 .AppendUleb128 (b .tmp , uint (b .stab .Lookup (f .Srcfile )))
for _ , u := range f .Units {
b .tmp = uleb128 .AppendUleb128 (b .tmp , uint (u .StLine ))
b .tmp = uleb128 .AppendUleb128 (b .tmp , uint (u .StCol ))
b .tmp = uleb128 .AppendUleb128 (b .tmp , uint (u .EnLine ))
b .tmp = uleb128 .AppendUleb128 (b .tmp , uint (u .EnCol ))
b .tmp = uleb128 .AppendUleb128 (b .tmp , uint (u .NxStmts ))
}
lit := uint (0 )
if f .Lit {
lit = 1
}
b .tmp = uleb128 .AppendUleb128 (b .tmp , lit )
fd .encoded = bytes .Clone (b .tmp )
rv := uint (len (b .funcs ))
b .funcs = append (b .funcs , fd )
return rv
}
func (b *CoverageMetaDataBuilder ) emitFuncOffsets (w io .WriteSeeker , off int64 ) int64 {
nFuncs := len (b .funcs )
var foff int64 = coverage .CovMetaHeaderSize + int64 (b .stab .Size ()) + int64 (nFuncs )*4
for idx := 0 ; idx < nFuncs ; idx ++ {
b .wrUint32 (w , uint32 (foff ))
foff += int64 (len (b .funcs [idx ].encoded ))
}
return off + (int64 (len (b .funcs )) * 4 )
}
func (b *CoverageMetaDataBuilder ) emitFunc (w io .WriteSeeker , off int64 , f funcDesc ) (int64 , error ) {
ew := len (f .encoded )
if nw , err := w .Write (f .encoded ); err != nil {
return 0 , err
} else if ew != nw {
return 0 , fmt .Errorf ("short write emitting coverage meta-data" )
}
return off + int64 (ew ), nil
}
func (b *CoverageMetaDataBuilder ) reportWriteError (err error ) {
if b .werr != nil {
b .werr = err
}
}
func (b *CoverageMetaDataBuilder ) wrUint32 (w io .WriteSeeker , v uint32 ) {
b .tmp = b .tmp [:0 ]
b .tmp = append (b .tmp , 0 , 0 , 0 , 0 )
binary .LittleEndian .PutUint32 (b .tmp , v )
if nw , err := w .Write (b .tmp ); err != nil {
b .reportWriteError (err )
} else if nw != 4 {
b .reportWriteError (fmt .Errorf ("short write" ))
}
}
func (b *CoverageMetaDataBuilder ) Emit (w io .WriteSeeker ) ([16 ]byte , error ) {
var digest [16 ]byte
copy (digest [:], b .h .Sum (nil ))
mh := coverage .MetaSymbolHeader {
PkgPath : uint32 (b .pkgpath ),
PkgName : uint32 (b .pkgname ),
ModulePath : uint32 (b .modpath ),
NumFiles : uint32 (b .stab .Nentries ()),
NumFuncs : uint32 (len (b .funcs )),
MetaHash : digest ,
}
if b .debug {
fmt .Fprintf (os .Stderr , "=-= writing header: %+v\n" , mh )
}
if err := binary .Write (w , binary .LittleEndian , mh ); err != nil {
return digest , fmt .Errorf ("error writing meta-file header: %v" , err )
}
off := int64 (coverage .CovMetaHeaderSize )
off = b .emitFuncOffsets (w , off )
if b .werr != nil {
return digest , b .werr
}
if err := b .stab .Write (w ); err != nil {
return digest , err
}
off += int64 (b .stab .Size ())
for _ , f := range b .funcs {
var err error
off , err = b .emitFunc (w , off , f )
if err != nil {
return digest , err
}
}
totalLength := uint32 (off )
if _ , err := w .Seek (0 , io .SeekStart ); err != nil {
return digest , err
}
b .wrUint32 (w , totalLength )
if b .werr != nil {
return digest , b .werr
}
return digest , nil
}
func HashFuncDesc (f *coverage .FuncDesc ) [16 ]byte {
h := md5 .New ()
tmp := make ([]byte , 0 , 32 )
hashFuncDesc (h , f , tmp )
var r [16 ]byte
copy (r [:], h .Sum (nil ))
return r
}
func hashFuncDesc(h hash .Hash , f *coverage .FuncDesc , tmp []byte ) {
io .WriteString (h , f .Funcname )
io .WriteString (h , f .Srcfile )
for _ , u := range f .Units {
h32 (u .StLine , h , tmp )
h32 (u .StCol , h , tmp )
h32 (u .EnLine , h , tmp )
h32 (u .EnCol , h , tmp )
h32 (u .NxStmts , h , tmp )
}
lit := uint32 (0 )
if f .Lit {
lit = 1
}
h32 (lit , h , tmp )
}
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 .