encodefile.mx raw

   1  // Copyright 2021 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  package encodemeta
   6  
   7  import (
   8  	"bufio"
   9  	"encoding/binary"
  10  	"fmt"
  11  	"hash/fnv"
  12  	"internal/coverage"
  13  	"internal/coverage/stringtab"
  14  	"io"
  15  	"os"
  16  	"unsafe"
  17  )
  18  
  19  // This package contains APIs and helpers for writing out a meta-data
  20  // file (composed of a file header, offsets/lengths, and then a series of
  21  // meta-data blobs emitted by the compiler, one per Go package).
  22  
  23  type CoverageMetaFileWriter struct {
  24  	stab   stringtab.Writer
  25  	mfname []byte
  26  	w      *bufio.Writer
  27  	tmp    []byte
  28  	debug  bool
  29  }
  30  
  31  func NewCoverageMetaFileWriter(mfname []byte, w io.Writer) *CoverageMetaFileWriter {
  32  	r := &CoverageMetaFileWriter{
  33  		mfname: mfname,
  34  		w:      bufio.NewWriter(w),
  35  		tmp:    []byte{:64},
  36  	}
  37  	r.stab.InitWriter()
  38  	r.stab.Lookup("")
  39  	return r
  40  }
  41  
  42  func (m *CoverageMetaFileWriter) Write(finalHash [16]byte, blobs [][]byte, mode coverage.CounterMode, granularity coverage.CounterGranularity) error {
  43  	mhsz := uint64(unsafe.Sizeof(coverage.MetaFileHeader{}))
  44  	stSize := m.stab.Size()
  45  	stOffset := mhsz + uint64(16*len(blobs))
  46  	preambleLength := stOffset + uint64(stSize)
  47  
  48  	if m.debug {
  49  		fmt.Fprintf(os.Stderr, "=+= sizeof(MetaFileHeader)=%d\n", mhsz)
  50  		fmt.Fprintf(os.Stderr, "=+= preambleLength=%d stSize=%d\n", preambleLength, stSize)
  51  	}
  52  
  53  	// Compute total size
  54  	tlen := preambleLength
  55  	for i := 0; i < len(blobs); i++ {
  56  		tlen += uint64(len(blobs[i]))
  57  	}
  58  
  59  	// Emit header
  60  	mh := coverage.MetaFileHeader{
  61  		Magic:        coverage.CovMetaMagic,
  62  		Version:      coverage.MetaFileVersion,
  63  		TotalLength:  tlen,
  64  		Entries:      uint64(len(blobs)),
  65  		MetaFileHash: finalHash,
  66  		StrTabOffset: uint32(stOffset),
  67  		StrTabLength: stSize,
  68  		CMode:        mode,
  69  		CGranularity: granularity,
  70  	}
  71  	var err error
  72  	if err = binary.Write(m.w, binary.LittleEndian, mh); err != nil {
  73  		return fmt.Errorf("error writing %s: %v", m.mfname, err)
  74  	}
  75  
  76  	if m.debug {
  77  		fmt.Fprintf(os.Stderr, "=+= len(blobs) is %d\n", mh.Entries)
  78  	}
  79  
  80  	// Emit package offsets section followed by package lengths section.
  81  	off := preambleLength
  82  	off2 := mhsz
  83  	buf := []byte{:8}
  84  	for _, blob := range blobs {
  85  		binary.LittleEndian.PutUint64(buf, off)
  86  		if _, err = m.w.Write(buf); err != nil {
  87  			return fmt.Errorf("error writing %s: %v", m.mfname, err)
  88  		}
  89  		if m.debug {
  90  			fmt.Fprintf(os.Stderr, "=+= pkg offset %d 0x%x\n", off, off)
  91  		}
  92  		off += uint64(len(blob))
  93  		off2 += 8
  94  	}
  95  	for _, blob := range blobs {
  96  		bl := uint64(len(blob))
  97  		binary.LittleEndian.PutUint64(buf, bl)
  98  		if _, err = m.w.Write(buf); err != nil {
  99  			return fmt.Errorf("error writing %s: %v", m.mfname, err)
 100  		}
 101  		if m.debug {
 102  			fmt.Fprintf(os.Stderr, "=+= pkg len %d 0x%x\n", bl, bl)
 103  		}
 104  		off2 += 8
 105  	}
 106  
 107  	// Emit string table
 108  	if err = m.stab.Write(m.w); err != nil {
 109  		return err
 110  	}
 111  
 112  	// Now emit blobs themselves.
 113  	for k, blob := range blobs {
 114  		if m.debug {
 115  			h := fnv.New128a()
 116  			h.Write(blob)
 117  			fmt.Fprintf(os.Stderr, "=+= writing blob %d len %d at off=%d hash %s\n", k, len(blob), off2, fmt.Sprintf("%x", h.Sum(nil)))
 118  		}
 119  		if _, err = m.w.Write(blob); err != nil {
 120  			return fmt.Errorf("error writing %s: %v", m.mfname, err)
 121  		}
 122  		if m.debug {
 123  			fmt.Fprintf(os.Stderr, "=+= wrote package payload of %d bytes\n",
 124  				len(blob))
 125  		}
 126  		off2 += uint64(len(blob))
 127  	}
 128  
 129  	// Flush writer, and we're done.
 130  	if err = m.w.Flush(); err != nil {
 131  		return fmt.Errorf("error writing %s: %v", m.mfname, err)
 132  	}
 133  	return nil
 134  }
 135