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

// This package contains APIs and helpers for reading and decoding
// meta-data output files emitted by the runtime when a
// coverage-instrumented binary executes. A meta-data file contains
// top-level info (counter mode, number of packages) and then a
// separate self-contained meta-data section for each Go package.

import (
	
	
	
	
	
	
	
	
	
)

// CoverageMetaFileReader provides state and methods for reading
// a meta-data file from a code coverage run.
type CoverageMetaFileReader struct {
	f          *os.File
	hdr        coverage.MetaFileHeader
	tmp        []byte
	pkgOffsets []uint64
	pkgLengths []uint64
	strtab     *stringtab.Reader
	fileRdr    *bufio.Reader
	fileView   []byte
	debug      bool
}

// NewCoverageMetaFileReader returns a new helper object for reading
// the coverage meta-data output file 'f'. The param 'fileView' is a
// read-only slice containing the contents of 'f' obtained by mmap'ing
// the file read-only; 'fileView' may be nil, in which case the helper
// will read the contents of the file using regular file Read
// operations.
func ( *os.File,  []byte) (*CoverageMetaFileReader, error) {
	 := &CoverageMetaFileReader{
		f:        ,
		fileView: ,
		tmp:      make([]byte, 256),
	}

	if  := .readFileHeader();  != nil {
		return nil, 
	}
	return , nil
}

func ( *CoverageMetaFileReader) () error {
	var  error

	.fileRdr = bufio.NewReader(.f)

	// Read file header.
	if  := binary.Read(.fileRdr, binary.LittleEndian, &.hdr);  != nil {
		return 
	}

	// Verify magic string
	 := .hdr.Magic
	 := coverage.CovMetaMagic
	if [0] != [0] || [1] != [1] || [2] != [2] || [3] != [3] {
		return fmt.Errorf("invalid meta-data file magic string")
	}

	// Vet the version. If this is a meta-data file from the future,
	// we won't be able to read it.
	if .hdr.Version > coverage.MetaFileVersion {
		return fmt.Errorf("meta-data file withn unknown version %d (expected %d)", .hdr.Version, coverage.MetaFileVersion)
	}

	// Read package offsets for good measure
	.pkgOffsets = make([]uint64, .hdr.Entries)
	for  := uint64(0);  < .hdr.Entries; ++ {
		if .pkgOffsets[],  = .rdUint64();  != nil {
			return 
		}
		if .pkgOffsets[] > .hdr.TotalLength {
			return fmt.Errorf("insane pkg offset %d: %d > totlen %d",
				, .pkgOffsets[], .hdr.TotalLength)
		}
	}
	.pkgLengths = make([]uint64, .hdr.Entries)
	for  := uint64(0);  < .hdr.Entries; ++ {
		if .pkgLengths[],  = .rdUint64();  != nil {
			return 
		}
		if .pkgLengths[] > .hdr.TotalLength {
			return fmt.Errorf("insane pkg length %d: %d > totlen %d",
				, .pkgLengths[], .hdr.TotalLength)
		}
	}

	// Read string table.
	 := make([]byte, .hdr.StrTabLength)
	,  := .fileRdr.Read()
	if  != nil {
		return 
	}
	if  != int(.hdr.StrTabLength) {
		return fmt.Errorf("error: short read on string table")
	}
	 := slicereader.NewReader(, false /* not readonly */)
	.strtab = stringtab.NewReader()
	.strtab.Read()

	if .debug {
		fmt.Fprintf(os.Stderr, "=-= read-in header is: %+v\n", *)
	}

	return nil
}

func ( *CoverageMetaFileReader) () (uint64, error) {
	.tmp = .tmp[:0]
	.tmp = append(.tmp, make([]byte, 8)...)
	,  := .fileRdr.Read(.tmp)
	if  != nil {
		return 0, 
	}
	if  != 8 {
		return 0, fmt.Errorf("premature end of file on read")
	}
	 := binary.LittleEndian.Uint64(.tmp)
	return , nil
}

// NumPackages returns the number of packages for which this file
// contains meta-data.
func ( *CoverageMetaFileReader) () uint64 {
	return .hdr.Entries
}

// CounterMode returns the counter mode (set, count, atomic) used
// when building for coverage for the program that produce this
// meta-data file.
func ( *CoverageMetaFileReader) () coverage.CounterMode {
	return .hdr.CMode
}

// CounterGranularity returns the counter granularity (single counter per
// function, or counter per block) selected when building for coverage
// for the program that produce this meta-data file.
func ( *CoverageMetaFileReader) () coverage.CounterGranularity {
	return .hdr.CGranularity
}

// FileHash returns the hash computed for all of the package meta-data
// blobs. Coverage counter data files refer to this hash, and the
// hash will be encoded into the meta-data file name.
func ( *CoverageMetaFileReader) () [16]byte {
	return .hdr.MetaFileHash
}

// GetPackageDecoder requests a decoder object for the package within
// the meta-data file whose index is 'pkIdx'. If the
// CoverageMetaFileReader was set up with a read-only file view, a
// pointer into that file view will be returned, otherwise the buffer
// 'payloadbuf' will be written to (or if it is not of sufficient
// size, a new buffer will be allocated). Return value is the decoder,
// a byte slice with the encoded meta-data, and an error.
func ( *CoverageMetaFileReader) ( uint32,  []byte) (*CoverageMetaDataDecoder, []byte, error) {
	,  := .GetPackagePayload(, )
	if .debug {
		fmt.Fprintf(os.Stderr, "=-= pkidx=%d payload length is %d hash=%s\n",
			, len(), fmt.Sprintf("%x", md5.Sum()))
	}
	if  != nil {
		return nil, nil, 
	}
	,  := NewCoverageMetaDataDecoder(, .fileView != nil)
	if  != nil {
		return nil, nil, 
	}
	return , , nil
}

// GetPackagePayload returns the raw (encoded) meta-data payload for the
// package with index 'pkIdx'. As with GetPackageDecoder, if the
// CoverageMetaFileReader was set up with a read-only file view, a
// pointer into that file view will be returned, otherwise the buffer
// 'payloadbuf' will be written to (or if it is not of sufficient
// size, a new buffer will be allocated). Return value is the decoder,
// a byte slice with the encoded meta-data, and an error.
func ( *CoverageMetaFileReader) ( uint32,  []byte) ([]byte, error) {

	// Determine correct offset/length.
	if uint64() >= .hdr.Entries {
		return nil, fmt.Errorf("GetPackagePayload: illegal pkg index %d", )
	}
	 := .pkgOffsets[]
	 := .pkgLengths[]

	if .debug {
		fmt.Fprintf(os.Stderr, "=-= for pk %d, off=%d len=%d\n", , , )
	}

	if .fileView != nil {
		return .fileView[ : +], nil
	}

	 := [:0]
	if cap() < int() {
		 = make([]byte, 0, )
	}
	 = append(, make([]byte, )...)
	if ,  := .f.Seek(int64(), io.SeekStart);  != nil {
		return nil, 
	}
	if ,  := io.ReadFull(.f, );  != nil {
		return nil, 
	}
	return , nil
}