// 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

// This package contains APIs and helpers for encoding the meta-data
// "blob" for a single Go package, created when coverage
// instrumentation is turned on.

import (
	
	
	
	
	
	
	
	
	
	
)

type CoverageMetaDataBuilder struct {
	stab    stringtab.Writer
	funcs   []funcDesc
	tmp     []byte // temp work slice
	h       hash.Hash
	pkgpath uint32
	pkgname uint32
	modpath uint32
	debug   bool
	werr    error
}

func ( string,  string,  string) (*CoverageMetaDataBuilder, error) {
	if  == "" {
		return nil, fmt.Errorf("invalid empty package path")
	}
	 := &CoverageMetaDataBuilder{
		tmp: make([]byte, 0, 256),
		h:   md5.New(),
	}
	.stab.InitWriter()
	.stab.Lookup("")
	.pkgpath = .stab.Lookup()
	.pkgname = .stab.Lookup()
	.modpath = .stab.Lookup()
	io.WriteString(.h, )
	io.WriteString(.h, )
	io.WriteString(.h, )
	return , nil
}

func h32( uint32,  hash.Hash,  []byte) {
	 = [:0]
	 = append(, 0, 0, 0, 0)
	binary.LittleEndian.PutUint32(, )
	.Write()
}

type funcDesc struct {
	encoded []byte
}

// AddFunc registers a new function with the meta data builder.
func ( *CoverageMetaDataBuilder) ( coverage.FuncDesc) uint {
	hashFuncDesc(.h, &, .tmp)
	 := funcDesc{}
	.tmp = .tmp[:0]
	.tmp = uleb128.AppendUleb128(.tmp, uint(len(.Units)))
	.tmp = uleb128.AppendUleb128(.tmp, uint(.stab.Lookup(.Funcname)))
	.tmp = uleb128.AppendUleb128(.tmp, uint(.stab.Lookup(.Srcfile)))
	for ,  := range .Units {
		.tmp = uleb128.AppendUleb128(.tmp, uint(.StLine))
		.tmp = uleb128.AppendUleb128(.tmp, uint(.StCol))
		.tmp = uleb128.AppendUleb128(.tmp, uint(.EnLine))
		.tmp = uleb128.AppendUleb128(.tmp, uint(.EnCol))
		.tmp = uleb128.AppendUleb128(.tmp, uint(.NxStmts))
	}
	 := uint(0)
	if .Lit {
		 = 1
	}
	.tmp = uleb128.AppendUleb128(.tmp, )
	.encoded = bytes.Clone(.tmp)
	 := uint(len(.funcs))
	.funcs = append(.funcs, )
	return 
}

func ( *CoverageMetaDataBuilder) ( io.WriteSeeker,  int64) int64 {
	 := len(.funcs)
	var  int64 = coverage.CovMetaHeaderSize + int64(.stab.Size()) + int64()*4
	for  := 0;  < ; ++ {
		.wrUint32(, uint32())
		 += int64(len(.funcs[].encoded))
	}
	return  + (int64(len(.funcs)) * 4)
}

func ( *CoverageMetaDataBuilder) ( io.WriteSeeker,  int64,  funcDesc) (int64, error) {
	 := len(.encoded)
	if ,  := .Write(.encoded);  != nil {
		return 0, 
	} else if  !=  {
		return 0, fmt.Errorf("short write emitting coverage meta-data")
	}
	return  + int64(), nil
}

func ( *CoverageMetaDataBuilder) ( error) {
	if .werr != nil {
		.werr = 
	}
}

func ( *CoverageMetaDataBuilder) ( io.WriteSeeker,  uint32) {
	.tmp = .tmp[:0]
	.tmp = append(.tmp, 0, 0, 0, 0)
	binary.LittleEndian.PutUint32(.tmp, )
	if ,  := .Write(.tmp);  != nil {
		.reportWriteError()
	} else if  != 4 {
		.reportWriteError(fmt.Errorf("short write"))
	}
}

// Emit writes the meta-data accumulated so far in this builder to 'w'.
// Returns a hash of the meta-data payload and an error.
func ( *CoverageMetaDataBuilder) ( io.WriteSeeker) ([16]byte, error) {
	// Emit header.  Length will initially be zero, we'll
	// back-patch it later.
	var  [16]byte
	copy([:], .h.Sum(nil))
	 := coverage.MetaSymbolHeader{
		// hash and length initially zero, will be back-patched
		PkgPath:    uint32(.pkgpath),
		PkgName:    uint32(.pkgname),
		ModulePath: uint32(.modpath),
		NumFiles:   uint32(.stab.Nentries()),
		NumFuncs:   uint32(len(.funcs)),
		MetaHash:   ,
	}
	if .debug {
		fmt.Fprintf(os.Stderr, "=-= writing header: %+v\n", )
	}
	if  := binary.Write(, binary.LittleEndian, );  != nil {
		return , fmt.Errorf("error writing meta-file header: %v", )
	}
	 := int64(coverage.CovMetaHeaderSize)

	// Write function offsets section
	 = .emitFuncOffsets(, )

	// Check for any errors up to this point.
	if .werr != nil {
		return , .werr
	}

	// Write string table.
	if  := .stab.Write();  != nil {
		return , 
	}
	 += int64(.stab.Size())

	// Write functions
	for ,  := range .funcs {
		var  error
		,  = .emitFunc(, , )
		if  != nil {
			return , 
		}
	}

	// Back-patch the length.
	 := uint32()
	if ,  := .Seek(0, io.SeekStart);  != nil {
		return , 
	}
	.wrUint32(, )
	if .werr != nil {
		return , .werr
	}
	return , nil
}

// HashFuncDesc computes an md5 sum of a coverage.FuncDesc and returns
// a digest for it.
func ( *coverage.FuncDesc) [16]byte {
	 := md5.New()
	 := make([]byte, 0, 32)
	hashFuncDesc(, , )
	var  [16]byte
	copy([:], .Sum(nil))
	return 
}

// hashFuncDesc incorporates a given function 'f' into the hash 'h'.
func hashFuncDesc( hash.Hash,  *coverage.FuncDesc,  []byte) {
	io.WriteString(, .Funcname)
	io.WriteString(, .Srcfile)
	for ,  := range .Units {
		h32(.StLine, , )
		h32(.StCol, , )
		h32(.EnLine, , )
		h32(.EnCol, , )
		h32(.NxStmts, , )
	}
	 := uint32(0)
	if .Lit {
		 = 1
	}
	h32(, , )
}