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

import (
	
	
	
	
	
	
	
	
	
)

// This file contains helpers for reading counter data files created
// during the executions of a coverage-instrumented binary.

type CounterDataReader struct {
	stab     *stringtab.Reader
	args     map[string]string
	osargs   []string
	goarch   string // GOARCH setting from run that produced counter data
	goos     string // GOOS setting from run that produced counter data
	mr       io.ReadSeeker
	hdr      coverage.CounterFileHeader
	ftr      coverage.CounterFileFooter
	shdr     coverage.CounterSegmentHeader
	u32b     []byte
	u8b      []byte
	fcnCount uint32
	segCount uint32
	debug    bool
}

func ( string,  io.ReadSeeker) (*CounterDataReader, error) {
	 := &CounterDataReader{
		mr:   ,
		u32b: make([]byte, 4),
		u8b:  make([]byte, 1),
	}
	// Read header
	if  := binary.Read(, binary.LittleEndian, &.hdr);  != nil {
		return nil, 
	}
	if .debug {
		fmt.Fprintf(os.Stderr, "=-= counter file header: %+v\n", .hdr)
	}
	if !checkMagic(.hdr.Magic) {
		return nil, fmt.Errorf("invalid magic string: not a counter data file")
	}
	if .hdr.Version > coverage.CounterFileVersion {
		return nil, fmt.Errorf("version data incompatibility: reader is %d data is %d", coverage.CounterFileVersion, .hdr.Version)
	}

	// Read footer.
	if  := .readFooter();  != nil {
		return nil, 
	}
	// Seek back to just past the file header.
	 := int64(unsafe.Sizeof(.hdr))
	if ,  := .mr.Seek(, io.SeekStart);  != nil {
		return nil, 
	}
	// Read preamble for first segment.
	if  := .readSegmentPreamble();  != nil {
		return nil, 
	}
	return , nil
}

func checkMagic( [4]byte) bool {
	 := coverage.CovCounterMagic
	return [0] == [0] && [1] == [1] && [2] == [2] && [3] == [3]
}

func ( *CounterDataReader) () error {
	 := int64(unsafe.Sizeof(.ftr))
	if ,  := .mr.Seek(-, io.SeekEnd);  != nil {
		return 
	}
	if  := binary.Read(.mr, binary.LittleEndian, &.ftr);  != nil {
		return 
	}
	if !checkMagic(.ftr.Magic) {
		return fmt.Errorf("invalid magic string (not a counter data file)")
	}
	if .ftr.NumSegments == 0 {
		return fmt.Errorf("invalid counter data file (no segments)")
	}
	return nil
}

// readSegmentPreamble reads and consumes the segment header, segment string
// table, and segment args table.
func ( *CounterDataReader) () error {
	// Read segment header.
	if  := binary.Read(.mr, binary.LittleEndian, &.shdr);  != nil {
		return 
	}
	if .debug {
		fmt.Fprintf(os.Stderr, "=-= read counter segment header: %+v", .shdr)
		fmt.Fprintf(os.Stderr, " FcnEntries=0x%x StrTabLen=0x%x ArgsLen=0x%x\n",
			.shdr.FcnEntries, .shdr.StrTabLen, .shdr.ArgsLen)
	}

	// Read string table and args.
	if  := .readStringTable();  != nil {
		return 
	}
	if  := .readArgs();  != nil {
		return 
	}
	// Seek past any padding to bring us up to a 4-byte boundary.
	if ,  := .mr.Seek(0, io.SeekCurrent);  != nil {
		return 
	} else {
		 :=  % 4
		if  != 0 {
			 := 4 - 
			if ,  := .mr.Seek(, io.SeekCurrent);  != nil {
				return 
			}
		}
	}
	return nil
}

func ( *CounterDataReader) () error {
	 := make([]byte, .shdr.StrTabLen)
	,  := .mr.Read()
	if  != nil {
		return 
	}
	if  != int(.shdr.StrTabLen) {
		return fmt.Errorf("error: short read on string table")
	}
	 := slicereader.NewReader(, false /* not readonly */)
	.stab = stringtab.NewReader()
	.stab.Read()
	return nil
}

func ( *CounterDataReader) () error {
	 := make([]byte, .shdr.ArgsLen)
	,  := .mr.Read()
	if  != nil {
		return 
	}
	if  != int(.shdr.ArgsLen) {
		return fmt.Errorf("error: short read on args table")
	}
	 := slicereader.NewReader(, false /* not readonly */)
	 := func() (string, error) {
		 := .ReadULEB128()
		if int() >= .stab.Entries() {
			return "", fmt.Errorf("malformed string table ref")
		}
		return .stab.Get(uint32()), nil
	}
	 := .ReadULEB128()
	.args = make(map[string]string, int())
	for  := uint64(0);  < ; ++ {
		,  := ()
		if  != nil {
			return 
		}
		,  := ()
		if  != nil {
			return 
		}
		if ,  := .args[];  {
			return fmt.Errorf("malformed args table")
		}
		.args[] = 
	}
	if ,  := .args["argc"];  {
		,  := strconv.Atoi()
		if  != nil {
			return fmt.Errorf("malformed argc in counter data file args section")
		}
		.osargs = make([]string, 0, )
		for  := 0;  < ; ++ {
			 := .args[fmt.Sprintf("argv%d", )]
			.osargs = append(.osargs, )
		}
	}
	if ,  := .args["GOOS"];  {
		.goos = 
	}
	if ,  := .args["GOARCH"];  {
		.goarch = 
	}
	return nil
}

// OsArgs returns the program arguments (saved from os.Args during
// the run of the instrumented binary) read from the counter
// data file. Not all coverage data files will have os.Args values;
// for example, if a data file is produced by merging coverage
// data from two distinct runs, no os args will be available (an
// empty list is returned).
func ( *CounterDataReader) () []string {
	return .osargs
}

// Goos returns the GOOS setting in effect for the "-cover" binary
// that produced this counter data file. The GOOS value may be
// empty in the case where the counter data file was produced
// from a merge in which more than one GOOS value was present.
func ( *CounterDataReader) () string {
	return .goos
}

// Goarch returns the GOARCH setting in effect for the "-cover" binary
// that produced this counter data file. The GOARCH value may be
// empty in the case where the counter data file was produced
// from a merge in which more than one GOARCH value was present.
func ( *CounterDataReader) () string {
	return .goarch
}

// FuncPayload encapsulates the counter data payload for a single
// function as read from a counter data file.
type FuncPayload struct {
	PkgIdx   uint32
	FuncIdx  uint32
	Counters []uint32
}

// NumSegments returns the number of execution segments in the file.
func ( *CounterDataReader) () uint32 {
	return .ftr.NumSegments
}

// BeginNextSegment sets up the reader to read the next segment,
// returning TRUE if we do have another segment to read, or FALSE
// if we're done with all the segments (also an error if
// something went wrong).
func ( *CounterDataReader) () (bool, error) {
	if .segCount >= .ftr.NumSegments {
		return false, nil
	}
	.segCount++
	.fcnCount = 0
	// Seek past footer from last segment.
	 := int64(unsafe.Sizeof(.ftr))
	if ,  := .mr.Seek(, io.SeekCurrent);  != nil {
		return false, 
	}
	// Read preamble for this segment.
	if  := .readSegmentPreamble();  != nil {
		return false, 
	}
	return true, nil
}

// NumFunctionsInSegment returns the number of live functions
// in the currently selected segment.
func ( *CounterDataReader) () uint32 {
	return uint32(.shdr.FcnEntries)
}

const supportDeadFunctionsInCounterData = false

// NextFunc reads data for the next function in this current segment
// into "p", returning TRUE if the read was successful or FALSE
// if we've read all the functions already (also an error if
// something went wrong with the read or we hit a premature
// EOF).
func ( *CounterDataReader) ( *FuncPayload) (bool, error) {
	if .fcnCount >= uint32(.shdr.FcnEntries) {
		return false, nil
	}
	.fcnCount++
	var  func() (uint32, error)
	if .hdr.CFlavor == coverage.CtrULeb128 {
		 = func() (uint32, error) {
			var  uint
			var  uint64
			for {
				,  := .mr.Read(.u8b)
				if  != nil {
					return 0, 
				}
				 := .u8b[0]
				 |= (uint64(&0x7F) << )
				if &0x80 == 0 {
					break
				}
				 += 7
			}
			return uint32(), nil
		}
	} else if .hdr.CFlavor == coverage.CtrRaw {
		if .hdr.BigEndian {
			 = func() (uint32, error) {
				,  := .mr.Read(.u32b)
				if  != nil {
					return 0, 
				}
				if  != 4 {
					return 0, io.EOF
				}
				return binary.BigEndian.Uint32(.u32b), nil
			}
		} else {
			 = func() (uint32, error) {
				,  := .mr.Read(.u32b)
				if  != nil {
					return 0, 
				}
				if  != 4 {
					return 0, io.EOF
				}
				return binary.LittleEndian.Uint32(.u32b), nil
			}
		}
	} else {
		panic("internal error: unknown counter flavor")
	}

	// Alternative/experimental path: one way we could handling writing
	// out counter data would be to just memcpy the counter segment
	// out to a file, meaning that a region in the counter memory
	// corresponding to a dead (never-executed) function would just be
	// zeroes. The code path below handles this case.
	var  uint32
	var  error
	if supportDeadFunctionsInCounterData {
		for {
			,  = ()
			if  == io.EOF {
				return false, io.EOF
			} else if  != nil {
				break
			}
			if  != 0 {
				break
			}
		}
	} else {
		,  = ()
	}
	if  != nil {
		return false, 
	}

	// Read package and func indices.
	.PkgIdx,  = ()
	if  != nil {
		return false, 
	}
	.FuncIdx,  = ()
	if  != nil {
		return false, 
	}
	if cap(.Counters) < 1024 {
		.Counters = make([]uint32, 0, 1024)
	}
	.Counters = .Counters[:0]
	for  := uint32(0);  < ; ++ {
		,  := ()
		if  != nil {
			return false, 
		}
		.Counters = append(.Counters, )
	}
	return true, nil
}