github.com/bir3/gocompiler@v0.9.2202/src/internal/coverage/encodemeta/encodefile.go (about) 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 "crypto/md5" 10 "encoding/binary" 11 "fmt" 12 "github.com/bir3/gocompiler/src/internal/coverage" 13 "github.com/bir3/gocompiler/src/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 string 26 w *bufio.Writer 27 tmp []byte 28 debug bool 29 } 30 31 func NewCoverageMetaFileWriter(mfname string, w io.Writer) *CoverageMetaFileWriter { 32 r := &CoverageMetaFileWriter{ 33 mfname: mfname, 34 w: bufio.NewWriter(w), 35 tmp: make([]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 := make([]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 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))) 116 } 117 if _, err = m.w.Write(blob); err != nil { 118 return fmt.Errorf("error writing %s: %v", m.mfname, err) 119 } 120 if m.debug { 121 fmt.Fprintf(os.Stderr, "=+= wrote package payload of %d bytes\n", 122 len(blob)) 123 } 124 off2 += uint64(len(blob)) 125 } 126 127 // Flush writer, and we're done. 128 if err = m.w.Flush(); err != nil { 129 return fmt.Errorf("error writing %s: %v", m.mfname, err) 130 } 131 return nil 132 }