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

import (
	
	
	
	
	
	
	
	
	
	
)

// This package contains APIs and helpers for encoding initial portions
// of the counter data files emitted at runtime when coverage instrumentation
// is enabled.  Counter data files may contain multiple segments; the file
// header and first segment are written via the "Write" method below, and
// additional segments can then be added using "AddSegment".

type CoverageDataWriter struct {
	stab    *stringtab.Writer
	w       *bufio.Writer
	csh     coverage.CounterSegmentHeader
	tmp     []byte
	cflavor coverage.CounterFlavor
	segs    uint32
	debug   bool
}

func ( io.Writer,  coverage.CounterFlavor) *CoverageDataWriter {
	 := &CoverageDataWriter{
		stab: &stringtab.Writer{},
		w:    bufio.NewWriter(),

		tmp:     make([]byte, 64),
		cflavor: ,
	}
	.stab.InitWriter()
	.stab.Lookup("")
	return 
}

// CounterVisitor describes a helper object used during counter file
// writing; when writing counter data files, clients pass a
// CounterVisitor to the write/emit routines, then the expectation is
// that the VisitFuncs method will then invoke the callback "f" with
// data for each function to emit to the file.
type CounterVisitor interface {
	VisitFuncs(f CounterVisitorFn) error
}

// CounterVisitorFn describes a callback function invoked when writing
// coverage counter data.
type CounterVisitorFn func(pkid uint32, funcid uint32, counters []uint32) error

// Write writes the contents of the count-data file to the writer
// previously supplied to NewCoverageDataWriter. Returns an error
// if something went wrong somewhere with the write.
func ( *CoverageDataWriter) ( [16]byte,  map[string]string,  CounterVisitor) error {
	if  := .writeHeader();  != nil {
		return 
	}
	return .AppendSegment(, )
}

func padToFourByteBoundary( *slicewriter.WriteSeeker) error {
	 := len(.BytesWritten())
	 := []byte{0, 0, 0, 0}
	 := uint32() % 4
	if  != 0 {
		 := [:(4 - )]
		if ,  := .Write();  != nil {
			return 
		} else if  != len() {
			return fmt.Errorf("error: short write")
		}
	}
	return nil
}

func ( *CoverageDataWriter) ( *slicewriter.WriteSeeker) error {
	// record position
	,  := .Seek(0, io.SeekCurrent)
	if  != nil {
		return fmt.Errorf("error seeking in patchSegmentHeader: %v", )
	}
	// seek back to start so that we can update the segment header
	if ,  := .Seek(0, io.SeekStart);  != nil {
		return fmt.Errorf("error seeking in patchSegmentHeader: %v", )
	}
	if .debug {
		fmt.Fprintf(os.Stderr, "=-= writing counter segment header: %+v", .csh)
	}
	if  := binary.Write(, binary.LittleEndian, .csh);  != nil {
		return 
	}
	// ... and finally return to the original offset.
	if ,  := .Seek(, io.SeekStart);  != nil {
		return fmt.Errorf("error seeking in patchSegmentHeader: %v", )
	}
	return nil
}

func ( *CoverageDataWriter) ( map[string]string,  *slicewriter.WriteSeeker) error {
	if  := binary.Write(, binary.LittleEndian, .csh);  != nil {
		return 
	}
	 := uint32(len(.BytesWritten()))

	// Write string table and args to a byte slice (since we need
	// to capture offsets at various points), then emit the slice
	// once we are done.
	.stab.Freeze()
	if  := .stab.Write();  != nil {
		return 
	}
	.csh.StrTabLen = uint32(len(.BytesWritten())) - 

	 := make([]string, 0, len())
	for  := range  {
		 = append(, )
	}
	slices.Sort()

	 := func( uint) error {
		.tmp = .tmp[:0]
		.tmp = uleb128.AppendUleb128(.tmp, )
		if ,  := .Write(.tmp);  != nil {
			return 
		}
		return nil
	}

	// Count of arg pairs.
	if  := (uint(len()));  != nil {
		return 
	}
	// Arg pairs themselves.
	for ,  := range  {
		 := uint(.stab.Lookup())
		if  := ();  != nil {
			return 
		}
		 := []
		 := uint(.stab.Lookup())
		if  := ();  != nil {
			return 
		}
	}
	if  := padToFourByteBoundary();  != nil {
		return 
	}
	.csh.ArgsLen = uint32(len(.BytesWritten())) - (.csh.StrTabLen + )

	return nil
}

// AppendSegment appends a new segment to a counter data, with a new
// args section followed by a payload of counter data clauses.
func ( *CoverageDataWriter) ( map[string]string,  CounterVisitor) error {
	.stab = &stringtab.Writer{}
	.stab.InitWriter()
	.stab.Lookup("")

	var  error
	for ,  := range  {
		.stab.Lookup()
		.stab.Lookup()
	}

	 := &slicewriter.WriteSeeker{}
	if  = .writeSegmentPreamble(, );  != nil {
		return 
	}
	if  = .writeCounters(, );  != nil {
		return 
	}
	if  = .patchSegmentHeader();  != nil {
		return 
	}
	if  := .writeBytes(.BytesWritten());  != nil {
		return 
	}
	if  = .writeFooter();  != nil {
		return 
	}
	if  := .w.Flush();  != nil {
		return fmt.Errorf("write error: %v", )
	}
	.stab = nil
	return nil
}

func ( *CoverageDataWriter) ( [16]byte) error {
	// Emit file header.
	 := coverage.CounterFileHeader{
		Magic:     coverage.CovCounterMagic,
		Version:   coverage.CounterFileVersion,
		MetaHash:  ,
		CFlavor:   .cflavor,
		BigEndian: false,
	}
	if  := binary.Write(.w, binary.LittleEndian, );  != nil {
		return 
	}
	return nil
}

func ( *CoverageDataWriter) ( []byte) error {
	if len() == 0 {
		return nil
	}
	,  := .w.Write()
	if  != nil {
		return fmt.Errorf("error writing counter data: %v", )
	}
	if len() !=  {
		return fmt.Errorf("error writing counter data: short write")
	}
	return nil
}

func ( *CoverageDataWriter) ( CounterVisitor,  *slicewriter.WriteSeeker) error {
	// Notes:
	// - this version writes everything little-endian, which means
	//   a call is needed to encode every value (expensive)
	// - we may want to move to a model in which we just blast out
	//   all counters, or possibly mmap the file and do the write
	//   implicitly.
	 := make([]byte, 4)
	 := func( uint32) error {
		var  []byte
		var  int
		if .cflavor == coverage.CtrRaw {
			binary.LittleEndian.PutUint32(, )
			 = 
			 = 4
		} else if .cflavor == coverage.CtrULeb128 {
			.tmp = .tmp[:0]
			.tmp = uleb128.AppendUleb128(.tmp, uint())
			 = .tmp
			 = len()
		} else {
			panic("internal error: bad counter flavor")
		}
		if ,  := .Write();  != nil {
			return 
		} else if  !=  {
			return fmt.Errorf("writing counters: short write")
		}
		return nil
	}

	// Write out entries for each live function.
	 := func( uint32,  uint32,  []uint32) error {
		.csh.FcnEntries++
		if  := (uint32(len()));  != nil {
			return 
		}

		if  := ();  != nil {
			return 
		}

		if  := ();  != nil {
			return 
		}
		for ,  := range  {
			if  := ();  != nil {
				return 
			}
		}
		return nil
	}
	if  := .VisitFuncs();  != nil {
		return 
	}
	return nil
}

func ( *CoverageDataWriter) () error {
	.segs++
	 := coverage.CounterFileFooter{
		Magic:       coverage.CovCounterMagic,
		NumSegments: .segs,
	}
	if  := binary.Write(.w, binary.LittleEndian, );  != nil {
		return 
	}
	return nil
}