diff options
Diffstat (limited to 'src/internal/coverage/encodemeta/encodefile.go')
-rw-r--r-- | src/internal/coverage/encodemeta/encodefile.go | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/src/internal/coverage/encodemeta/encodefile.go b/src/internal/coverage/encodemeta/encodefile.go new file mode 100644 index 0000000..38ae46e --- /dev/null +++ b/src/internal/coverage/encodemeta/encodefile.go @@ -0,0 +1,132 @@ +// 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 encodemeta + +import ( + "bufio" + "crypto/md5" + "encoding/binary" + "fmt" + "internal/coverage" + "internal/coverage/stringtab" + "io" + "os" + "unsafe" +) + +// This package contains APIs and helpers for writing out a meta-data +// file (composed of a file header, offsets/lengths, and then a series of +// meta-data blobs emitted by the compiler, one per Go package). + +type CoverageMetaFileWriter struct { + stab stringtab.Writer + mfname string + w *bufio.Writer + tmp []byte + debug bool +} + +func NewCoverageMetaFileWriter(mfname string, w io.Writer) *CoverageMetaFileWriter { + r := &CoverageMetaFileWriter{ + mfname: mfname, + w: bufio.NewWriter(w), + tmp: make([]byte, 64), + } + r.stab.InitWriter() + r.stab.Lookup("") + return r +} + +func (m *CoverageMetaFileWriter) Write(finalHash [16]byte, blobs [][]byte, mode coverage.CounterMode, granularity coverage.CounterGranularity) error { + mhsz := uint64(unsafe.Sizeof(coverage.MetaFileHeader{})) + stSize := m.stab.Size() + stOffset := mhsz + uint64(16*len(blobs)) + preambleLength := stOffset + uint64(stSize) + + if m.debug { + fmt.Fprintf(os.Stderr, "=+= sizeof(MetaFileHeader)=%d\n", mhsz) + fmt.Fprintf(os.Stderr, "=+= preambleLength=%d stSize=%d\n", preambleLength, stSize) + } + + // Compute total size + tlen := preambleLength + for i := 0; i < len(blobs); i++ { + tlen += uint64(len(blobs[i])) + } + + // Emit header + mh := coverage.MetaFileHeader{ + Magic: coverage.CovMetaMagic, + Version: coverage.MetaFileVersion, + TotalLength: tlen, + Entries: uint64(len(blobs)), + MetaFileHash: finalHash, + StrTabOffset: uint32(stOffset), + StrTabLength: stSize, + CMode: mode, + CGranularity: granularity, + } + var err error + if err = binary.Write(m.w, binary.LittleEndian, mh); err != nil { + return fmt.Errorf("error writing %s: %v", m.mfname, err) + } + + if m.debug { + fmt.Fprintf(os.Stderr, "=+= len(blobs) is %d\n", mh.Entries) + } + + // Emit package offsets section followed by package lengths section. + off := preambleLength + off2 := mhsz + buf := make([]byte, 8) + for _, blob := range blobs { + binary.LittleEndian.PutUint64(buf, off) + if _, err = m.w.Write(buf); err != nil { + return fmt.Errorf("error writing %s: %v", m.mfname, err) + } + if m.debug { + fmt.Fprintf(os.Stderr, "=+= pkg offset %d 0x%x\n", off, off) + } + off += uint64(len(blob)) + off2 += 8 + } + for _, blob := range blobs { + bl := uint64(len(blob)) + binary.LittleEndian.PutUint64(buf, bl) + if _, err = m.w.Write(buf); err != nil { + return fmt.Errorf("error writing %s: %v", m.mfname, err) + } + if m.debug { + fmt.Fprintf(os.Stderr, "=+= pkg len %d 0x%x\n", bl, bl) + } + off2 += 8 + } + + // Emit string table + if err = m.stab.Write(m.w); err != nil { + return err + } + + // Now emit blobs themselves. + for k, blob := range blobs { + if m.debug { + fmt.Fprintf(os.Stderr, "=+= writing blob %d len %d at off=%d hash %s\n", k, len(blob), off2, fmt.Sprintf("%x", md5.Sum(blob))) + } + if _, err = m.w.Write(blob); err != nil { + return fmt.Errorf("error writing %s: %v", m.mfname, err) + } + if m.debug { + fmt.Fprintf(os.Stderr, "=+= wrote package payload of %d bytes\n", + len(blob)) + } + off2 += uint64(len(blob)) + } + + // Flush writer, and we're done. + if err = m.w.Flush(); err != nil { + return fmt.Errorf("error writing %s: %v", m.mfname, err) + } + return nil +} |