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

import (
	
	
	
	
	
	
	
	
	
	
)

// A PkgDecoder provides methods for decoding a package's Unified IR
// export data.
type PkgDecoder struct {
	// version is the file format version.
	version uint32

	// sync indicates whether the file uses sync markers.
	sync bool

	// pkgPath is the package path for the package to be decoded.
	//
	// TODO(mdempsky): Remove; unneeded since CL 391014.
	pkgPath string

	// elemData is the full data payload of the encoded package.
	// Elements are densely and contiguously packed together.
	//
	// The last 8 bytes of elemData are the package fingerprint.
	elemData string

	// elemEnds stores the byte-offset end positions of element
	// bitstreams within elemData.
	//
	// For example, element I's bitstream data starts at elemEnds[I-1]
	// (or 0, if I==0) and ends at elemEnds[I].
	//
	// Note: elemEnds is indexed by absolute indices, not
	// section-relative indices.
	elemEnds []uint32

	// elemEndsEnds stores the index-offset end positions of relocation
	// sections within elemEnds.
	//
	// For example, section K's end positions start at elemEndsEnds[K-1]
	// (or 0, if K==0) and end at elemEndsEnds[K].
	elemEndsEnds [numRelocs]uint32

	scratchRelocEnt []RelocEnt
}

// PkgPath returns the package path for the package
//
// TODO(mdempsky): Remove; unneeded since CL 391014.
func ( *PkgDecoder) () string { return .pkgPath }

// SyncMarkers reports whether pr uses sync markers.
func ( *PkgDecoder) () bool { return .sync }

// NewPkgDecoder returns a PkgDecoder initialized to read the Unified
// IR export data from input. pkgPath is the package path for the
// compilation unit that produced the export data.
//
// TODO(mdempsky): Remove pkgPath parameter; unneeded since CL 391014.
func (,  string) PkgDecoder {
	 := PkgDecoder{
		pkgPath: ,
	}

	// TODO(mdempsky): Implement direct indexing of input string to
	// avoid copying the position information.

	 := strings.NewReader()

	assert(binary.Read(, binary.LittleEndian, &.version) == nil)

	switch .version {
	default:
		panic(fmt.Errorf("unsupported version: %v", .version))
	case 0:
		// no flags
	case 1:
		var  uint32
		assert(binary.Read(, binary.LittleEndian, &) == nil)
		.sync = &flagSyncMarkers != 0
	}

	assert(binary.Read(, binary.LittleEndian, .elemEndsEnds[:]) == nil)

	.elemEnds = make([]uint32, .elemEndsEnds[len(.elemEndsEnds)-1])
	assert(binary.Read(, binary.LittleEndian, .elemEnds[:]) == nil)

	,  := .Seek(0, io.SeekCurrent)
	assert( == nil)

	.elemData = [:]
	assert(len(.elemData)-8 == int(.elemEnds[len(.elemEnds)-1]))

	return 
}

// NumElems returns the number of elements in section k.
func ( *PkgDecoder) ( RelocKind) int {
	 := int(.elemEndsEnds[])
	if  > 0 {
		 -= int(.elemEndsEnds[-1])
	}
	return 
}

// TotalElems returns the total number of elements across all sections.
func ( *PkgDecoder) () int {
	return len(.elemEnds)
}

// Fingerprint returns the package fingerprint.
func ( *PkgDecoder) () [8]byte {
	var  [8]byte
	copy([:], .elemData[len(.elemData)-8:])
	return 
}

// AbsIdx returns the absolute index for the given (section, index)
// pair.
func ( *PkgDecoder) ( RelocKind,  Index) int {
	 := int()
	if  > 0 {
		 += int(.elemEndsEnds[-1])
	}
	if  >= int(.elemEndsEnds[]) {
		errorf("%v:%v is out of bounds; %v", , , .elemEndsEnds)
	}
	return 
}

// DataIdx returns the raw element bitstream for the given (section,
// index) pair.
func ( *PkgDecoder) ( RelocKind,  Index) string {
	 := .AbsIdx(, )

	var  uint32
	if  > 0 {
		 = .elemEnds[-1]
	}
	 := .elemEnds[]

	return .elemData[:]
}

// StringIdx returns the string value for the given string index.
func ( *PkgDecoder) ( Index) string {
	return .DataIdx(RelocString, )
}

// NewDecoder returns a Decoder for the given (section, index) pair,
// and decodes the given SyncMarker from the element bitstream.
func ( *PkgDecoder) ( RelocKind,  Index,  SyncMarker) Decoder {
	 := .NewDecoderRaw(, )
	.Sync()
	return 
}

// TempDecoder returns a Decoder for the given (section, index) pair,
// and decodes the given SyncMarker from the element bitstream.
// If possible the Decoder should be RetireDecoder'd when it is no longer
// needed, this will avoid heap allocations.
func ( *PkgDecoder) ( RelocKind,  Index,  SyncMarker) Decoder {
	 := .TempDecoderRaw(, )
	.Sync()
	return 
}

func ( *PkgDecoder) ( *Decoder) {
	.scratchRelocEnt = .Relocs
	.Relocs = nil
}

// NewDecoderRaw returns a Decoder for the given (section, index) pair.
//
// Most callers should use NewDecoder instead.
func ( *PkgDecoder) ( RelocKind,  Index) Decoder {
	 := Decoder{
		common: ,
		k:      ,
		Idx:    ,
	}

	.Data.Reset(.DataIdx(, ))
	.Sync(SyncRelocs)
	.Relocs = make([]RelocEnt, .Len())
	for  := range .Relocs {
		.Sync(SyncReloc)
		.Relocs[] = RelocEnt{RelocKind(.Len()), Index(.Len())}
	}

	return 
}

func ( *PkgDecoder) ( RelocKind,  Index) Decoder {
	 := Decoder{
		common: ,
		k:      ,
		Idx:    ,
	}

	.Data.Reset(.DataIdx(, ))
	.Sync(SyncRelocs)
	 := .Len()
	if cap(.scratchRelocEnt) >=  {
		.Relocs = .scratchRelocEnt[:]
		.scratchRelocEnt = nil
	} else {
		.Relocs = make([]RelocEnt, )
	}
	for  := range .Relocs {
		.Sync(SyncReloc)
		.Relocs[] = RelocEnt{RelocKind(.Len()), Index(.Len())}
	}

	return 
}

// A Decoder provides methods for decoding an individual element's
// bitstream data.
type Decoder struct {
	common *PkgDecoder

	Relocs []RelocEnt
	Data   strings.Reader

	k   RelocKind
	Idx Index
}

func ( *Decoder) ( error) {
	if  != nil {
		errorf("unexpected decoding error: %w", )
	}
}

func ( *Decoder) () uint64 {
	,  := readUvarint(&.Data)
	.checkErr()
	return 
}

// readUvarint is a type-specialized copy of encoding/binary.ReadUvarint.
// This avoids the interface conversion and thus has better escape properties,
// which flows up the stack.
func readUvarint( *strings.Reader) (uint64, error) {
	var  uint64
	var  uint
	for  := 0;  < binary.MaxVarintLen64; ++ {
		,  := .ReadByte()
		if  != nil {
			if  > 0 &&  == io.EOF {
				 = io.ErrUnexpectedEOF
			}
			return , 
		}
		if  < 0x80 {
			if  == binary.MaxVarintLen64-1 &&  > 1 {
				return , overflow
			}
			return  | uint64()<<, nil
		}
		 |= uint64(&0x7f) << 
		 += 7
	}
	return , overflow
}

var overflow = errors.New("pkgbits: readUvarint overflows a 64-bit integer")

func ( *Decoder) () int64 {
	 := .rawUvarint()

	// Zig-zag decode.
	 := int64( >> 1)
	if &1 != 0 {
		 = ^
	}
	return 
}

func ( *Decoder) ( RelocKind,  int) Index {
	 := .Relocs[]
	assert(.Kind == )
	return .Idx
}

// Sync decodes a sync marker from the element bitstream and asserts
// that it matches the expected marker.
//
// If EnableSync is false, then Sync is a no-op.
func ( *Decoder) ( SyncMarker) {
	if !.common.sync {
		return
	}

	,  := .Data.Seek(0, io.SeekCurrent)
	 := SyncMarker(.rawUvarint())
	 := make([]int, .rawUvarint())
	for  := range  {
		[] = int(.rawUvarint())
	}

	if  ==  {
		return
	}

	// There's some tension here between printing:
	//
	// (1) full file paths that tools can recognize (e.g., so emacs
	//     hyperlinks the "file:line" text for easy navigation), or
	//
	// (2) short file paths that are easier for humans to read (e.g., by
	//     omitting redundant or irrelevant details, so it's easier to
	//     focus on the useful bits that remain).
	//
	// The current formatting favors the former, as it seems more
	// helpful in practice. But perhaps the formatting could be improved
	// to better address both concerns. For example, use relative file
	// paths if they would be shorter, or rewrite file paths to contain
	// "$GOROOT" (like objabi.AbsFile does) if tools can be taught how
	// to reliably expand that again.

	fmt.Printf("export data desync: package %q, section %v, index %v, offset %v\n", .common.pkgPath, .k, .Idx, )

	fmt.Printf("\nfound %v, written at:\n", )
	if len() == 0 {
		fmt.Printf("\t[stack trace unavailable; recompile package %q with -d=syncframes]\n", .common.pkgPath)
	}
	for ,  := range  {
		fmt.Printf("\t%s\n", .common.StringIdx(.rawReloc(RelocString, )))
	}

	fmt.Printf("\nexpected %v, reading at:\n", )
	var  [32]uintptr // TODO(mdempsky): Dynamically size?
	 := runtime.Callers(2, [:])
	for ,  := range fmtFrames([:]...) {
		fmt.Printf("\t%s\n", )
	}

	// We already printed a stack trace for the reader, so now we can
	// simply exit. Printing a second one with panic or base.Fatalf
	// would just be noise.
	os.Exit(1)
}

// Bool decodes and returns a bool value from the element bitstream.
func ( *Decoder) () bool {
	.Sync(SyncBool)
	,  := .Data.ReadByte()
	.checkErr()
	assert( < 2)
	return  != 0
}

// Int64 decodes and returns an int64 value from the element bitstream.
func ( *Decoder) () int64 {
	.Sync(SyncInt64)
	return .rawVarint()
}

// Int64 decodes and returns a uint64 value from the element bitstream.
func ( *Decoder) () uint64 {
	.Sync(SyncUint64)
	return .rawUvarint()
}

// Len decodes and returns a non-negative int value from the element bitstream.
func ( *Decoder) () int {  := .Uint64();  := int(); assert(uint64() == ); return  }

// Int decodes and returns an int value from the element bitstream.
func ( *Decoder) () int {  := .Int64();  := int(); assert(int64() == ); return  }

// Uint decodes and returns a uint value from the element bitstream.
func ( *Decoder) () uint {  := .Uint64();  := uint(); assert(uint64() == ); return  }

// Code decodes a Code value from the element bitstream and returns
// its ordinal value. It's the caller's responsibility to convert the
// result to an appropriate Code type.
//
// TODO(mdempsky): Ideally this method would have signature "Code[T
// Code] T" instead, but we don't allow generic methods and the
// compiler can't depend on generics yet anyway.
func ( *Decoder) ( SyncMarker) int {
	.Sync()
	return .Len()
}

// Reloc decodes a relocation of expected section k from the element
// bitstream and returns an index to the referenced element.
func ( *Decoder) ( RelocKind) Index {
	.Sync(SyncUseReloc)
	return .rawReloc(, .Len())
}

// String decodes and returns a string value from the element
// bitstream.
func ( *Decoder) () string {
	.Sync(SyncString)
	return .common.StringIdx(.Reloc(RelocString))
}

// Strings decodes and returns a variable-length slice of strings from
// the element bitstream.
func ( *Decoder) () []string {
	 := make([]string, .Len())
	for  := range  {
		[] = .String()
	}
	return 
}

// Value decodes and returns a constant.Value from the element
// bitstream.
func ( *Decoder) () constant.Value {
	.Sync(SyncValue)
	 := .Bool()
	 := .scalar()
	if  {
		 = constant.BinaryOp(, token.ADD, constant.MakeImag(.scalar()))
	}
	return 
}

func ( *Decoder) () constant.Value {
	switch  := CodeVal(.Code(SyncVal));  {
	default:
		panic(fmt.Errorf("unexpected scalar tag: %v", ))

	case ValBool:
		return constant.MakeBool(.Bool())
	case ValString:
		return constant.MakeString(.String())
	case ValInt64:
		return constant.MakeInt64(.Int64())
	case ValBigInt:
		return constant.Make(.bigInt())
	case ValBigRat:
		 := .bigInt()
		 := .bigInt()
		return constant.Make(new(big.Rat).SetFrac(, ))
	case ValBigFloat:
		return constant.Make(.bigFloat())
	}
}

func ( *Decoder) () *big.Int {
	 := new(big.Int).SetBytes([]byte(.String()))
	if .Bool() {
		.Neg()
	}
	return 
}

func ( *Decoder) () *big.Float {
	 := new(big.Float).SetPrec(512)
	assert(.UnmarshalText([]byte(.String())) == nil)
	return 
}

// @@@ Helpers

// TODO(mdempsky): These should probably be removed. I think they're a
// smell that the export data format is not yet quite right.

// PeekPkgPath returns the package path for the specified package
// index.
func ( *PkgDecoder) ( Index) string {
	var  string
	{
		 := .TempDecoder(RelocPkg, , SyncPkgDef)
		 = .String()
		.RetireDecoder(&)
	}
	if  == "" {
		 = .pkgPath
	}
	return 
}

// PeekObj returns the package path, object name, and CodeObj for the
// specified object index.
func ( *PkgDecoder) ( Index) (string, string, CodeObj) {
	var  Index
	var  string
	var  int
	{
		 := .TempDecoder(RelocName, , SyncObject1)
		.Sync(SyncSym)
		.Sync(SyncPkg)
		 = .Reloc(RelocPkg)
		 = .String()
		 = .Code(SyncCodeObj)
		.RetireDecoder(&)
	}

	 := .PeekPkgPath()
	assert( != "")

	 := CodeObj()

	return , , 
}