package reflectlite

import (
	"math"
	"unsafe"
)

type valueFlags uint8

// Flags list some useful flags that contain some extra information not
// contained in an interface{} directly, like whether this value was exported at
// all (it is possible to read unexported fields using reflection, but it is not
// possible to modify them).
const (
	valueFlagIndirect valueFlags = 1 << iota
	valueFlagExported
	valueFlagEmbedRO
	valueFlagStickyRO

	valueFlagRO = valueFlagEmbedRO | valueFlagStickyRO
)

func (v valueFlags) ro() valueFlags {
	if v&valueFlagRO != 0 {
		return valueFlagStickyRO
	}
	return 0
}

type Value struct {
	typecode *RawType
	value    unsafe.Pointer
	flags    valueFlags
}

// isIndirect returns whether the value pointer in this Value is always a
// pointer to the value. If it is false, it is only a pointer to the value if
// the value is bigger than a pointer.
func (v Value) isIndirect() bool {
	return v.flags&valueFlagIndirect != 0
}

// isExported returns whether the value represented by this Value could be
// accessed without violating type system constraints. For example, it is not
// set for unexported struct fields.
func (v Value) isExported() bool {
	return v.flags&valueFlagExported != 0
}

func (v Value) isRO() bool {
	return v.flags&(valueFlagRO) != 0
}

func (v Value) checkRO() {
	if v.isRO() {
		panic("reflect: value is not settable")
	}
}

func Indirect(v Value) Value {
	if v.Kind() != Ptr {
		return v
	}
	return v.Elem()
}

//go:linkname composeInterface runtime.composeInterface
func composeInterface(unsafe.Pointer, unsafe.Pointer) interface{}

//go:linkname decomposeInterface runtime.decomposeInterface
func decomposeInterface(i interface{}) (unsafe.Pointer, unsafe.Pointer)

func ValueOf(i interface{}) Value {
	typecode, value := decomposeInterface(i)
	return Value{
		typecode: (*RawType)(typecode),
		value:    value,
		flags:    valueFlagExported,
	}
}

func (v Value) Interface() interface{} {
	if !v.isExported() {
		panic("(reflect.Value).Interface: unexported")
	}
	return valueInterfaceUnsafe(v)
}

// valueInterfaceUnsafe is used by the runtime to hash map keys. It should not
// be subject to the isExported check.
func valueInterfaceUnsafe(v Value) interface{} {
	if v.typecode.Kind() == Interface {
		// The value itself is an interface. This can happen when getting the
		// value of a struct field of interface type, like this:
		//     type T struct {
		//         X interface{}
		//     }
		return *(*interface{})(v.value)
	}
	if v.isIndirect() && v.typecode.Size() <= unsafe.Sizeof(uintptr(0)) {
		// Value was indirect but must be put back directly in the interface
		// value.
		var value uintptr
		for j := v.typecode.Size(); j != 0; j-- {
			value = (value << 8) | uintptr(*(*uint8)(unsafe.Add(v.value, j-1)))
		}
		v.value = unsafe.Pointer(value)
	}
	return composeInterface(unsafe.Pointer(v.typecode), v.value)
}

func (v Value) Type() Type {
	return v.typecode
}

// IsZero reports whether v is the zero value for its type.
// It panics if the argument is invalid.
func (v Value) IsZero() bool {
	switch v.Kind() {
	case Bool:
		return !v.Bool()
	case Int, Int8, Int16, Int32, Int64:
		return v.Int() == 0
	case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
		return v.Uint() == 0
	case Float32, Float64:
		return v.Float() == 0
	case Complex64, Complex128:
		return v.Complex() == 0
	case Array:
		for i := 0; i < v.Len(); i++ {
			if !v.Index(i).IsZero() {
				return false
			}
		}
		return true
	case Chan, Func, Interface, Map, Pointer, Slice, UnsafePointer:
		return v.IsNil()
	case String:
		return v.Len() == 0
	case Struct:
		for i := 0; i < v.NumField(); i++ {
			if !v.Field(i).IsZero() && v.Type().Field(i).Name != "_" {
				return false
			}
		}
		return true
	default:
		// This should never happens, but will act as a safeguard for
		// later, as a default value doesn't makes sense here.
		panic(&ValueError{Method: "reflect.Value.IsZero", Kind: v.Kind()})
	}
}

// Internal function only, do not use.
//
// RawType returns the raw, underlying type code. It is used in the runtime
// package and needs to be exported for the runtime package to access it.
func (v Value) RawType() *RawType {
	return v.typecode
}

func (v Value) Kind() Kind {
	return v.typecode.Kind()
}

// IsNil returns whether the value is the nil value. It panics if the value Kind
// is not a channel, map, pointer, function, slice, or interface.
func (v Value) IsNil() bool {
	switch v.Kind() {
	case Chan, Map, Ptr, UnsafePointer:
		return v.pointer() == nil
	case Func:
		if v.value == nil {
			return true
		}
		fn := (*funcHeader)(v.value)
		return fn.Code == nil
	case Slice:
		if v.value == nil {
			return true
		}
		slice := (*sliceHeader)(v.value)
		return slice.data == nil
	case Interface:
		val := *(*interface{})(v.value)
		return val == nil
	default:
		panic(&ValueError{Method: "IsNil", Kind: v.Kind()})
	}
}

// Pointer returns the underlying pointer of the given value for the following
// types: chan, map, pointer, unsafe.Pointer, slice, func.
func (v Value) Pointer() uintptr {
	return uintptr(v.UnsafePointer())
}

// UnsafePointer returns the underlying pointer of the given value for the
// following types: chan, map, pointer, unsafe.Pointer, slice, func.
func (v Value) UnsafePointer() unsafe.Pointer {
	switch v.Kind() {
	case Chan, Map, Ptr, UnsafePointer:
		return v.pointer()
	case Slice:
		slice := (*sliceHeader)(v.value)
		return slice.data
	case Func:
		fn := (*funcHeader)(v.value)
		if fn.Context != nil {
			return fn.Context
		}
		return fn.Code
	default:
		panic(&ValueError{Method: "UnsafePointer", Kind: v.Kind()})
	}
}

// pointer returns the underlying pointer represented by v.
// v.Kind() must be Ptr, Map, Chan, or UnsafePointer
func (v Value) pointer() unsafe.Pointer {
	if v.isIndirect() {
		return *(*unsafe.Pointer)(v.value)
	}
	return v.value
}

func (v Value) IsValid() bool {
	return v.typecode != nil
}

func (v Value) CanInterface() bool {
	return v.isExported() && !v.isRO()
}

func (v Value) CanAddr() bool {
	return v.flags&(valueFlagIndirect) == valueFlagIndirect
}

func (v Value) Comparable() bool {
	k := v.Kind()
	switch k {
	case Invalid:
		return false

	case Array:
		switch v.Type().Elem().Kind() {
		case Interface, Array, Struct:
			for i := 0; i < v.Type().Len(); i++ {
				if !v.Index(i).Comparable() {
					return false
				}
			}
			return true
		}
		return v.Type().Comparable()

	case Interface:
		return v.Elem().Comparable()

	case Struct:
		for i := 0; i < v.NumField(); i++ {
			if !v.Field(i).Comparable() {
				return false
			}
		}
		return true

	default:
		return v.Type().Comparable()
	}
}

// Equal reports true if v is equal to u.
// For two invalid values, Equal will report true.
// For an interface value, Equal will compare the value within the interface.
// Otherwise, If the values have different types, Equal will report false.
// Otherwise, for arrays and structs Equal will compare each element in order,
// and report false if it finds non-equal elements.
// During all comparisons, if values of the same type are compared,
// and the type is not comparable, Equal will panic.
//
// Copyright 2009 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.
func (v Value) Equal(u Value) bool {
	if v.Kind() == Interface {
		v = v.Elem()
	}
	if u.Kind() == Interface {
		u = u.Elem()
	}

	if !v.IsValid() || !u.IsValid() {
		return v.IsValid() == u.IsValid()
	}

	if v.Kind() != u.Kind() || v.Type() != u.Type() {
		return false
	}

	// Handle each Kind directly rather than calling valueInterface
	// to avoid allocating.
	switch v.Kind() {
	default:
		panic("reflect.Value.Equal: invalid Kind")
	case Bool:
		return v.Bool() == u.Bool()
	case Int, Int8, Int16, Int32, Int64:
		return v.Int() == u.Int()
	case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
		return v.Uint() == u.Uint()
	case Float32, Float64:
		return v.Float() == u.Float()
	case Complex64, Complex128:
		return v.Complex() == u.Complex()
	case String:
		return v.String() == u.String()
	case Chan, Pointer, UnsafePointer:
		return v.Pointer() == u.Pointer()
	case Array:
		// u and v have the same type so they have the same length
		vl := v.Len()
		if vl == 0 {
			// panic on [0]func()
			if !v.Type().Elem().Comparable() {
				break
			}
			return true
		}
		for i := 0; i < vl; i++ {
			if !v.Index(i).Equal(u.Index(i)) {
				return false
			}
		}
		return true
	case Struct:
		// u and v have the same type so they have the same fields
		nf := v.NumField()
		for i := 0; i < nf; i++ {
			if !v.Field(i).Equal(u.Field(i)) {
				return false
			}
		}
		return true
	case Func, Map, Slice:
		break
	}
	panic("reflect.Value.Equal: values of type " + v.Type().String() + " are not comparable")
}

func (v Value) Addr() Value {
	if !v.CanAddr() {
		panic("reflect.Value.Addr of unaddressable value")
	}
	// Preserve flagRO instead of using v.flag.ro() so that
	// v.Addr().Elem() is equivalent to v (#32772)
	flags := v.flags & (valueFlagExported | valueFlagRO)
	return Value{
		typecode: pointerTo(v.typecode),
		value:    v.value,
		flags:    flags,
	}
}

func (v Value) UnsafeAddr() uintptr {
	return uintptr(v.Addr().UnsafePointer())
}

func (v Value) CanSet() bool {
	return v.flags&(valueFlagExported|valueFlagIndirect|valueFlagRO) == valueFlagExported|valueFlagIndirect
}

func (v Value) Bool() bool {
	switch v.Kind() {
	case Bool:
		if v.isIndirect() {
			return *((*bool)(v.value))
		} else {
			return uintptr(v.value) != 0
		}
	default:
		panic(&ValueError{Method: "Bool", Kind: v.Kind()})
	}
}

// CanInt reports whether Uint can be used without panicking.
func (v Value) CanInt() bool {
	switch v.Kind() {
	case Int, Int8, Int16, Int32, Int64:
		return true
	default:
		return false
	}
}

func (v Value) Int() int64 {
	switch v.Kind() {
	case Int:
		if v.isIndirect() || unsafe.Sizeof(int(0)) > unsafe.Sizeof(uintptr(0)) {
			return int64(*(*int)(v.value))
		} else {
			return int64(int(uintptr(v.value)))
		}
	case Int8:
		if v.isIndirect() {
			return int64(*(*int8)(v.value))
		} else {
			return int64(int8(uintptr(v.value)))
		}
	case Int16:
		if v.isIndirect() {
			return int64(*(*int16)(v.value))
		} else {
			return int64(int16(uintptr(v.value)))
		}
	case Int32:
		if v.isIndirect() || unsafe.Sizeof(int32(0)) > unsafe.Sizeof(uintptr(0)) {
			return int64(*(*int32)(v.value))
		} else {
			return int64(int32(uintptr(v.value)))
		}
	case Int64:
		if v.isIndirect() || unsafe.Sizeof(int64(0)) > unsafe.Sizeof(uintptr(0)) {
			return int64(*(*int64)(v.value))
		} else {
			return int64(int64(uintptr(v.value)))
		}
	default:
		panic(&ValueError{Method: "Int", Kind: v.Kind()})
	}
}

// CanUint reports whether Uint can be used without panicking.
func (v Value) CanUint() bool {
	switch v.Kind() {
	case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
		return true
	default:
		return false
	}
}

func (v Value) Uint() uint64 {
	switch v.Kind() {
	case Uintptr:
		if v.isIndirect() {
			return uint64(*(*uintptr)(v.value))
		} else {
			return uint64(uintptr(v.value))
		}
	case Uint8:
		if v.isIndirect() {
			return uint64(*(*uint8)(v.value))
		} else {
			return uint64(uintptr(v.value))
		}
	case Uint16:
		if v.isIndirect() {
			return uint64(*(*uint16)(v.value))
		} else {
			return uint64(uintptr(v.value))
		}
	case Uint:
		if v.isIndirect() || unsafe.Sizeof(uint(0)) > unsafe.Sizeof(uintptr(0)) {
			return uint64(*(*uint)(v.value))
		} else {
			return uint64(uintptr(v.value))
		}
	case Uint32:
		if v.isIndirect() || unsafe.Sizeof(uint32(0)) > unsafe.Sizeof(uintptr(0)) {
			return uint64(*(*uint32)(v.value))
		} else {
			return uint64(uintptr(v.value))
		}
	case Uint64:
		if v.isIndirect() || unsafe.Sizeof(uint64(0)) > unsafe.Sizeof(uintptr(0)) {
			return uint64(*(*uint64)(v.value))
		} else {
			return uint64(uintptr(v.value))
		}
	default:
		panic(&ValueError{Method: "Uint", Kind: v.Kind()})
	}
}

// CanFloat reports whether Float can be used without panicking.
func (v Value) CanFloat() bool {
	switch v.Kind() {
	case Float32, Float64:
		return true
	default:
		return false
	}
}

func (v Value) Float32() float32 {
	switch v.Kind() {
	case Float32:
		if v.isIndirect() || unsafe.Sizeof(float32(0)) > unsafe.Sizeof(uintptr(0)) {
			// The float is stored as an external value on systems with 16-bit
			// pointers.
			return *(*float32)(v.value)
		} else {
			// The float is directly stored in the interface value on systems
			// with 32-bit and 64-bit pointers.
			return *(*float32)(unsafe.Pointer(&v.value))
		}

	case Float64:
		return float32(v.Float())

	}

	panic(&ValueError{Method: "Float", Kind: v.Kind()})
}

func (v Value) Float() float64 {
	switch v.Kind() {
	case Float32:
		if v.isIndirect() || unsafe.Sizeof(float32(0)) > unsafe.Sizeof(uintptr(0)) {
			// The float is stored as an external value on systems with 16-bit
			// pointers.
			return float64(*(*float32)(v.value))
		} else {
			// The float is directly stored in the interface value on systems
			// with 32-bit and 64-bit pointers.
			return float64(*(*float32)(unsafe.Pointer(&v.value)))
		}
	case Float64:
		if v.isIndirect() || unsafe.Sizeof(float64(0)) > unsafe.Sizeof(uintptr(0)) {
			// For systems with 16-bit and 32-bit pointers.
			return *(*float64)(v.value)
		} else {
			// The float is directly stored in the interface value on systems
			// with 64-bit pointers.
			return *(*float64)(unsafe.Pointer(&v.value))
		}
	default:
		panic(&ValueError{Method: "Float", Kind: v.Kind()})
	}
}

// CanComplex reports whether Complex can be used without panicking.
func (v Value) CanComplex() bool {
	switch v.Kind() {
	case Complex64, Complex128:
		return true
	default:
		return false
	}
}

func (v Value) Complex() complex128 {
	switch v.Kind() {
	case Complex64:
		if v.isIndirect() || unsafe.Sizeof(complex64(0)) > unsafe.Sizeof(uintptr(0)) {
			// The complex number is stored as an external value on systems with
			// 16-bit and 32-bit pointers.
			return complex128(*(*complex64)(v.value))
		} else {
			// The complex number is directly stored in the interface value on
			// systems with 64-bit pointers.
			return complex128(*(*complex64)(unsafe.Pointer(&v.value)))
		}
	case Complex128:
		// This is a 128-bit value, which is always stored as an external value.
		// It may be stored in the pointer directly on very uncommon
		// architectures with 128-bit pointers, however.
		return *(*complex128)(v.value)
	default:
		panic(&ValueError{Method: "Complex", Kind: v.Kind()})
	}
}

func (v Value) String() string {
	switch v.Kind() {
	case String:
		// A string value is always bigger than a pointer as it is made of a
		// pointer and a length.
		return *(*string)(v.value)
	default:
		// Special case because of the special treatment of .String() in Go.
		return "<" + v.typecode.String() + " Value>"
	}
}

func (v Value) Bytes() []byte {
	switch v.Kind() {
	case Slice:
		if v.typecode.elem().Kind() != Uint8 {
			panic(&ValueError{Method: "Bytes", Kind: v.Kind()})
		}
		return *(*[]byte)(v.value)

	case Array:
		v.checkAddressable()

		if v.typecode.elem().Kind() != Uint8 {
			panic(&ValueError{Method: "Bytes", Kind: v.Kind()})
		}

		// Small inline arrays are not addressable, so we only have to
		// handle addressable arrays which will be stored as pointers
		// in v.value
		return unsafe.Slice((*byte)(v.value), v.Len())
	}

	panic(&ValueError{Method: "Bytes", Kind: v.Kind()})
}

func (v Value) Slice(i, j int) Value {
	switch v.Kind() {
	case Slice:
		hdr := *(*sliceHeader)(v.value)
		i, j := uintptr(i), uintptr(j)

		if j < i || hdr.cap < j {
			slicePanic()
		}

		elemSize := v.typecode.underlying().elem().Size()

		hdr.len = j - i
		hdr.cap = hdr.cap - i
		hdr.data = unsafe.Add(hdr.data, i*elemSize)

		return Value{
			typecode: v.typecode,
			value:    unsafe.Pointer(&hdr),
			flags:    v.flags,
		}

	case Array:
		v.checkAddressable()
		buf, length := buflen(v)
		i, j := uintptr(i), uintptr(j)
		if j < i || length < j {
			slicePanic()
		}

		elemSize := v.typecode.underlying().elem().Size()

		var hdr sliceHeader
		hdr.len = j - i
		hdr.cap = length - i
		hdr.data = unsafe.Add(buf, i*elemSize)

		sliceType := (*arrayType)(unsafe.Pointer(v.typecode.underlying())).slicePtr
		return Value{
			typecode: sliceType,
			value:    unsafe.Pointer(&hdr),
			flags:    v.flags,
		}

	case String:
		i, j := uintptr(i), uintptr(j)
		str := *(*stringHeader)(v.value)

		if j < i || str.len < j {
			slicePanic()
		}

		hdr := stringHeader{
			data: unsafe.Add(str.data, i),
			len:  j - i,
		}

		return Value{
			typecode: v.typecode,
			value:    unsafe.Pointer(&hdr),
			flags:    v.flags,
		}
	}

	panic(&ValueError{Method: "Slice", Kind: v.Kind()})
}

func (v Value) Slice3(i, j, k int) Value {
	switch v.Kind() {
	case Slice:
		hdr := *(*sliceHeader)(v.value)
		i, j, k := uintptr(i), uintptr(j), uintptr(k)

		if j < i || k < j || hdr.len < k {
			slicePanic()
		}

		elemSize := v.typecode.underlying().elem().Size()

		hdr.len = j - i
		hdr.cap = k - i
		hdr.data = unsafe.Add(hdr.data, i*elemSize)

		return Value{
			typecode: v.typecode,
			value:    unsafe.Pointer(&hdr),
			flags:    v.flags,
		}

	case Array:
		v.checkAddressable()
		buf, length := buflen(v)
		i, j, k := uintptr(i), uintptr(j), uintptr(k)
		if j < i || k < j || length < k {
			slicePanic()
		}

		elemSize := v.typecode.underlying().elem().Size()

		var hdr sliceHeader
		hdr.len = j - i
		hdr.cap = k - i
		hdr.data = unsafe.Add(buf, i*elemSize)

		sliceType := (*arrayType)(unsafe.Pointer(v.typecode.underlying())).slicePtr
		return Value{
			typecode: sliceType,
			value:    unsafe.Pointer(&hdr),
			flags:    v.flags,
		}
	}

	panic("unimplemented: (reflect.Value).Slice3()")
}

//go:linkname maplen runtime.hashmapLen
func maplen(p unsafe.Pointer) int

//go:linkname chanlen runtime.chanLen
func chanlen(p unsafe.Pointer) int

// Len returns the length of this value for slices, strings, arrays, channels,
// and maps. For other types, it panics.
func (v Value) Len() int {
	switch v.typecode.Kind() {
	case Array:
		return v.typecode.Len()
	case Chan:
		return chanlen(v.pointer())
	case Map:
		return maplen(v.pointer())
	case Slice:
		return int((*sliceHeader)(v.value).len)
	case String:
		return int((*stringHeader)(v.value).len)
	default:
		panic(&ValueError{Method: "Len", Kind: v.Kind()})
	}
}

//go:linkname chancap runtime.chanCap
func chancap(p unsafe.Pointer) int

// Cap returns the capacity of this value for arrays, channels and slices.
// For other types, it panics.
func (v Value) Cap() int {
	switch v.typecode.Kind() {
	case Array:
		return v.typecode.Len()
	case Chan:
		return chancap(v.pointer())
	case Slice:
		return int((*sliceHeader)(v.value).cap)
	default:
		panic(&ValueError{Method: "Cap", Kind: v.Kind()})
	}
}

//go:linkname mapclear runtime.hashmapClear
func mapclear(p unsafe.Pointer)

// Clear clears the contents of a map or zeros the contents of a slice
//
// It panics if v's Kind is not Map or Slice.
func (v Value) Clear() {
	switch v.typecode.Kind() {
	case Map:
		mapclear(v.pointer())
	case Slice:
		hdr := (*sliceHeader)(v.value)
		elemSize := v.typecode.underlying().elem().Size()
		memzero(hdr.data, elemSize*hdr.len)
	default:
		panic(&ValueError{Method: "Clear", Kind: v.Kind()})
	}
}

// NumField returns the number of fields of this struct. It panics for other
// value types.
func (v Value) NumField() int {
	return v.typecode.NumField()
}

func (v Value) Elem() Value {
	switch v.Kind() {
	case Ptr:
		ptr := v.pointer()
		if ptr == nil {
			return Value{}
		}
		// Don't copy RO flags
		flags := (v.flags & (valueFlagIndirect | valueFlagExported)) | valueFlagIndirect
		return Value{
			typecode: v.typecode.elem(),
			value:    ptr,
			flags:    flags,
		}
	case Interface:
		typecode, value := decomposeInterface(*(*interface{})(v.value))
		return Value{
			typecode: (*RawType)(typecode),
			value:    value,
			flags:    v.flags &^ valueFlagIndirect,
		}
	default:
		panic(&ValueError{Method: "Elem", Kind: v.Kind()})
	}
}

// Field returns the value of the i'th field of this struct.
func (v Value) Field(i int) Value {
	if v.Kind() != Struct {
		panic(&ValueError{Method: "Field", Kind: v.Kind()})
	}
	structField := v.typecode.rawField(i)

	// Copy flags but clear EmbedRO; we're not an embedded field anymore
	flags := v.flags & ^valueFlagEmbedRO
	if structField.PkgPath != "" {
		// No PkgPath => not exported.
		// Clear exported flag even if the parent was exported.
		flags &^= valueFlagExported

		// Update the RO flag
		if structField.Anonymous {
			// Embedded field
			flags |= valueFlagEmbedRO
		} else {
			flags |= valueFlagStickyRO
		}
	} else {
		// Parent field may not have been exported but we are
		flags |= valueFlagExported
	}

	size := v.typecode.Size()
	fieldType := structField.Type
	fieldSize := fieldType.Size()
	if v.isIndirect() || fieldSize > unsafe.Sizeof(uintptr(0)) {
		// v.value was already a pointer to the value and it should stay that
		// way.
		return Value{
			flags:    flags,
			typecode: fieldType,
			value:    unsafe.Add(v.value, structField.Offset),
		}
	}

	// The fieldSize is smaller than uintptr, which means that the value will
	// have to be stored directly in the interface value.

	if fieldSize == 0 {
		// The struct field is zero sized.
		// This is a rare situation, but because it's undefined behavior
		// to shift the size of the value (zeroing the value), handle this
		// situation explicitly.
		return Value{
			flags:    flags,
			typecode: fieldType,
			value:    unsafe.Pointer(nil),
		}
	}

	if size > unsafe.Sizeof(uintptr(0)) {
		// The value was not stored in the interface before but will be
		// afterwards, so load the value (from the correct offset) and return
		// it.
		ptr := unsafe.Add(v.value, structField.Offset)
		value := unsafe.Pointer(loadValue(ptr, fieldSize))
		return Value{
			flags:    flags &^ valueFlagIndirect,
			typecode: fieldType,
			value:    value,
		}
	}

	// The value was already stored directly in the interface and it still
	// is. Cut out the part of the value that we need.
	value := maskAndShift(uintptr(v.value), structField.Offset, fieldSize)
	return Value{
		flags:    flags,
		typecode: fieldType,
		value:    unsafe.Pointer(value),
	}
}

var uint8Type = TypeOf(uint8(0)).(*RawType)

func (v Value) Index(i int) Value {
	switch v.Kind() {
	case Slice:
		// Extract an element from the slice.
		slice := *(*sliceHeader)(v.value)
		if uint(i) >= uint(slice.len) {
			panic("reflect: slice index out of range")
		}
		flags := (v.flags & (valueFlagExported | valueFlagIndirect)) | valueFlagIndirect | v.flags.ro()
		elem := Value{
			typecode: v.typecode.elem(),
			flags:    flags,
		}
		elem.value = unsafe.Add(slice.data, elem.typecode.Size()*uintptr(i)) // pointer to new value
		return elem
	case String:
		// Extract a character from a string.
		// A string is never stored directly in the interface, but always as a
		// pointer to the string value.
		// Keeping valueFlagExported if set, but don't set valueFlagIndirect
		// otherwise CanSet will return true for string elements (which is bad,
		// strings are read-only).
		s := *(*stringHeader)(v.value)
		if uint(i) >= uint(s.len) {
			panic("reflect: string index out of range")
		}
		return Value{
			typecode: uint8Type,
			value:    unsafe.Pointer(uintptr(*(*uint8)(unsafe.Add(s.data, i)))),
			flags:    v.flags & valueFlagExported,
		}
	case Array:
		// Extract an element from the array.
		elemType := v.typecode.elem()
		elemSize := elemType.Size()
		size := v.typecode.Size()
		if size == 0 {
			// The element size is 0 and/or the length of the array is 0.
			return Value{
				typecode: v.typecode.elem(),
				flags:    v.flags,
			}
		}
		if elemSize > unsafe.Sizeof(uintptr(0)) {
			// The resulting value doesn't fit in a pointer so must be
			// indirect. Also, because size != 0 this implies that the array
			// length must be != 0, and thus that the total size is at least
			// elemSize.
			addr := unsafe.Add(v.value, elemSize*uintptr(i)) // pointer to new value
			return Value{
				typecode: v.typecode.elem(),
				flags:    v.flags,
				value:    addr,
			}
		}

		if size > unsafe.Sizeof(uintptr(0)) || v.isIndirect() {
			// The element fits in a pointer, but the array is not stored in the pointer directly.
			// Load the value from the pointer.
			addr := unsafe.Add(v.value, elemSize*uintptr(i)) // pointer to new value
			value := addr
			if !v.isIndirect() {
				// Use a pointer to the value (don't load the value) if the
				// 'indirect' flag is set.
				value = unsafe.Pointer(loadValue(addr, elemSize))
			}
			return Value{
				typecode: v.typecode.elem(),
				flags:    v.flags,
				value:    value,
			}
		}

		// The value fits in a pointer, so extract it with some shifting and
		// masking.
		offset := elemSize * uintptr(i)
		value := maskAndShift(uintptr(v.value), offset, elemSize)
		return Value{
			typecode: v.typecode.elem(),
			flags:    v.flags,
			value:    unsafe.Pointer(value),
		}
	default:
		panic(&ValueError{Method: "Index", Kind: v.Kind()})
	}
}

func (v Value) NumMethod() int {
	if v.typecode == nil {
		panic(&ValueError{Method: "reflect.Value.NumMethod", Kind: Invalid})
	}
	return v.typecode.NumMethod()
}

// OverflowFloat reports whether the float64 x cannot be represented by v's type.
// It panics if v's Kind is not Float32 or Float64.
func (v Value) OverflowFloat(x float64) bool {
	k := v.Kind()
	switch k {
	case Float32:
		return overflowFloat32(x)
	case Float64:
		return false
	}
	panic(&ValueError{Method: "reflect.Value.OverflowFloat", Kind: v.Kind()})
}

func overflowFloat32(x float64) bool {
	if x < 0 {
		x = -x
	}
	return math.MaxFloat32 < x && x <= math.MaxFloat64
}

func (v Value) MapKeys() []Value {
	if v.Kind() != Map {
		panic(&ValueError{Method: "MapKeys", Kind: v.Kind()})
	}

	// empty map
	if v.Len() == 0 {
		return nil
	}

	keys := make([]Value, 0, v.Len())

	it := hashmapNewIterator()
	k := New(v.typecode.Key())
	e := New(v.typecode.Elem())

	keyType := v.typecode.key()
	keyTypeIsEmptyInterface := keyType.Kind() == Interface && keyType.NumMethod() == 0
	shouldUnpackInterface := !keyTypeIsEmptyInterface && keyType.Kind() != String && !keyType.isBinary()

	for hashmapNext(v.pointer(), it, k.value, e.value) {
		if shouldUnpackInterface {
			intf := *(*interface{})(k.value)
			v := ValueOf(intf)
			keys = append(keys, v)
		} else {
			keys = append(keys, k.Elem())
		}
		k = New(v.typecode.Key())
	}

	return keys
}

//go:linkname hashmapStringGet runtime.hashmapStringGet
func hashmapStringGet(m unsafe.Pointer, key string, value unsafe.Pointer, valueSize uintptr) bool

//go:linkname hashmapBinaryGet runtime.hashmapBinaryGet
func hashmapBinaryGet(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr) bool

//go:linkname hashmapInterfaceGet runtime.hashmapInterfaceGet
func hashmapInterfaceGet(m unsafe.Pointer, key interface{}, value unsafe.Pointer, valueSize uintptr) bool

func (v Value) MapIndex(key Value) Value {
	if v.Kind() != Map {
		panic(&ValueError{Method: "MapIndex", Kind: v.Kind()})
	}

	vkey := v.typecode.key()

	// compare key type with actual key type of map
	if !key.typecode.AssignableTo(vkey) {
		// type error?
		panic("reflect.Value.MapIndex: incompatible types for key")
	}

	elemType := v.typecode.Elem()
	elem := New(elemType)

	if vkey.Kind() == String {
		if ok := hashmapStringGet(v.pointer(), *(*string)(key.value), elem.value, elemType.Size()); !ok {
			return Value{}
		}
		return elem.Elem()
	} else if vkey.isBinary() {
		var keyptr unsafe.Pointer
		if key.isIndirect() || key.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
			keyptr = key.value
		} else {
			keyptr = unsafe.Pointer(&key.value)
		}
		//TODO(dgryski): zero out padding bytes in key, if any
		if ok := hashmapBinaryGet(v.pointer(), keyptr, elem.value, elemType.Size()); !ok {
			return Value{}
		}
		return elem.Elem()
	} else {
		if ok := hashmapInterfaceGet(v.pointer(), key.Interface(), elem.value, elemType.Size()); !ok {
			return Value{}
		}
		return elem.Elem()
	}
}

//go:linkname hashmapNewIterator runtime.hashmapNewIterator
func hashmapNewIterator() unsafe.Pointer

//go:linkname hashmapNext runtime.hashmapNext
func hashmapNext(m unsafe.Pointer, it unsafe.Pointer, key, value unsafe.Pointer) bool

func (v Value) MapRange() *MapIter {
	iter := &MapIter{}
	iter.Reset(v)
	return iter
}

type MapIter struct {
	m   Value
	it  unsafe.Pointer
	key Value
	val Value

	valid              bool
	unpackKeyInterface bool
}

func (it *MapIter) Key() Value {
	if !it.valid {
		panic("reflect.MapIter.Key called on invalid iterator")
	}

	if it.unpackKeyInterface {
		intf := *(*interface{})(it.key.value)
		v := ValueOf(intf)
		return v
	}

	return it.key.Elem()
}

func (v Value) SetIterKey(iter *MapIter) {
	v.Set(iter.Key())
}

func (it *MapIter) Value() Value {
	if !it.valid {
		panic("reflect.MapIter.Value called on invalid iterator")
	}

	return it.val.Elem()
}

func (v Value) SetIterValue(iter *MapIter) {
	v.Set(iter.Value())
}

func (it *MapIter) Next() bool {
	it.key = New(it.m.typecode.Key())
	it.val = New(it.m.typecode.Elem())

	it.valid = hashmapNext(it.m.pointer(), it.it, it.key.value, it.val.value)
	return it.valid
}

func (iter *MapIter) Reset(v Value) {
	if v.Kind() != Map {
		panic(&ValueError{Method: "MapRange", Kind: v.Kind()})
	}

	keyType := v.typecode.key()

	keyTypeIsEmptyInterface := keyType.Kind() == Interface && keyType.NumMethod() == 0
	shouldUnpackInterface := !keyTypeIsEmptyInterface && keyType.Kind() != String && !keyType.isBinary()

	*iter = MapIter{
		m:                  v,
		it:                 hashmapNewIterator(),
		unpackKeyInterface: shouldUnpackInterface,
	}
}

func (v Value) Set(x Value) {
	v.checkAddressable()
	v.checkRO()
	if !x.typecode.AssignableTo(v.typecode) {
		panic("reflect.Value.Set: value of type " + x.typecode.String() + " cannot be assigned to type " + v.typecode.String())
	}

	if v.typecode.Kind() == Interface && x.typecode.Kind() != Interface {
		// move the value of x back into the interface, if possible
		if x.isIndirect() && x.typecode.Size() <= unsafe.Sizeof(uintptr(0)) {
			x.value = unsafe.Pointer(loadValue(x.value, x.typecode.Size()))
		}

		intf := composeInterface(unsafe.Pointer(x.typecode), x.value)
		x = Value{
			typecode: v.typecode,
			value:    unsafe.Pointer(&intf),
		}
	}

	size := v.typecode.Size()
	if size <= unsafe.Sizeof(uintptr(0)) && !x.isIndirect() {
		storeValue(v.value, size, uintptr(x.value))
	} else {
		memcpy(v.value, x.value, size)
	}
}

func (v Value) SetZero() {
	v.checkAddressable()
	v.checkRO()
	size := v.typecode.Size()
	memzero(v.value, size)
}

func (v Value) SetBool(x bool) {
	v.checkAddressable()
	v.checkRO()
	switch v.Kind() {
	case Bool:
		*(*bool)(v.value) = x
	default:
		panic(&ValueError{Method: "SetBool", Kind: v.Kind()})
	}
}

func (v Value) SetInt(x int64) {
	v.checkAddressable()
	v.checkRO()
	switch v.Kind() {
	case Int:
		*(*int)(v.value) = int(x)
	case Int8:
		*(*int8)(v.value) = int8(x)
	case Int16:
		*(*int16)(v.value) = int16(x)
	case Int32:
		*(*int32)(v.value) = int32(x)
	case Int64:
		*(*int64)(v.value) = x
	default:
		panic(&ValueError{Method: "SetInt", Kind: v.Kind()})
	}
}

func (v Value) SetUint(x uint64) {
	v.checkAddressable()
	v.checkRO()
	switch v.Kind() {
	case Uint:
		*(*uint)(v.value) = uint(x)
	case Uint8:
		*(*uint8)(v.value) = uint8(x)
	case Uint16:
		*(*uint16)(v.value) = uint16(x)
	case Uint32:
		*(*uint32)(v.value) = uint32(x)
	case Uint64:
		*(*uint64)(v.value) = x
	case Uintptr:
		*(*uintptr)(v.value) = uintptr(x)
	default:
		panic(&ValueError{Method: "SetUint", Kind: v.Kind()})
	}
}

func (v Value) SetFloat(x float64) {
	v.checkAddressable()
	v.checkRO()
	switch v.Kind() {
	case Float32:
		*(*float32)(v.value) = float32(x)
	case Float64:
		*(*float64)(v.value) = x
	default:
		panic(&ValueError{Method: "SetFloat", Kind: v.Kind()})
	}
}

func (v Value) SetComplex(x complex128) {
	v.checkAddressable()
	v.checkRO()
	switch v.Kind() {
	case Complex64:
		*(*complex64)(v.value) = complex64(x)
	case Complex128:
		*(*complex128)(v.value) = x
	default:
		panic(&ValueError{Method: "SetComplex", Kind: v.Kind()})
	}
}

func (v Value) SetString(x string) {
	v.checkAddressable()
	v.checkRO()
	switch v.Kind() {
	case String:
		*(*string)(v.value) = x
	default:
		panic(&ValueError{Method: "SetString", Kind: v.Kind()})
	}
}

func (v Value) SetBytes(x []byte) {
	v.checkAddressable()
	v.checkRO()
	if v.typecode.Kind() != Slice || v.typecode.elem().Kind() != Uint8 {
		panic("reflect.Value.SetBytes called on not []byte")
	}

	// copy the header contents over
	*(*[]byte)(v.value) = x
}

func (v Value) SetCap(n int) {
	panic("unimplemented: (reflect.Value).SetCap()")
}

func (v Value) SetLen(n int) {
	if v.typecode.Kind() != Slice {
		panic(&ValueError{Method: "reflect.Value.SetLen", Kind: v.Kind()})
	}
	v.checkAddressable()
	hdr := (*sliceHeader)(v.value)
	if int(uintptr(n)) != n || uintptr(n) > hdr.cap {
		panic("reflect.Value.SetLen: slice length out of range")
	}
	hdr.len = uintptr(n)
}

func (v Value) checkAddressable() {
	if !v.isIndirect() {
		panic("reflect: value is not addressable")
	}
}

// OverflowInt reports whether the int64 x cannot be represented by v's type.
// It panics if v's Kind is not Int, Int8, Int16, Int32, or Int64.
func (v Value) OverflowInt(x int64) bool {
	switch v.Kind() {
	case Int, Int8, Int16, Int32, Int64:
		bitSize := v.typecode.Size() * 8
		trunc := (x << (64 - bitSize)) >> (64 - bitSize)
		return x != trunc
	}
	panic(&ValueError{Method: "reflect.Value.OverflowInt", Kind: v.Kind()})
}

// OverflowUint reports whether the uint64 x cannot be represented by v's type.
// It panics if v's Kind is not Uint, Uintptr, Uint8, Uint16, Uint32, or Uint64.
func (v Value) OverflowUint(x uint64) bool {
	k := v.Kind()
	switch k {
	case Uint, Uintptr, Uint8, Uint16, Uint32, Uint64:
		bitSize := v.typecode.Size() * 8
		trunc := (x << (64 - bitSize)) >> (64 - bitSize)
		return x != trunc
	}
	panic(&ValueError{Method: "reflect.Value.OverflowUint", Kind: v.Kind()})
}

func (v Value) CanConvert(t Type) bool {
	// TODO: Optimize this to not actually perform a conversion
	_, ok := convertOp(v, t)
	return ok
}

func (v Value) Convert(t Type) Value {
	if v, ok := convertOp(v, t); ok {
		return v
	}

	panic("reflect.Value.Convert: value of type " + v.typecode.String() + " cannot be converted to type " + t.String())
}

func convertOp(src Value, typ Type) (Value, bool) {

	// Easy check first.  Do we even need to do anything?
	if src.typecode.underlying() == typ.(*RawType).underlying() {
		return Value{
			typecode: typ.(*RawType),
			value:    src.value,
			flags:    src.flags,
		}, true
	}

	if rtype := typ.(*RawType); rtype.Kind() == Interface && rtype.NumMethod() == 0 {
		iface := composeInterface(unsafe.Pointer(src.typecode), src.value)
		return Value{
			typecode: rtype,
			value:    unsafe.Pointer(&iface),
			flags:    valueFlagExported,
		}, true
	}

	switch src.Kind() {
	case Int, Int8, Int16, Int32, Int64:
		switch rtype := typ.(*RawType); rtype.Kind() {
		case Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
			return cvtInt(src, rtype), true
		case Float32, Float64:
			return cvtIntFloat(src, rtype), true
		case String:
			return cvtIntString(src, rtype), true
		}

	case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
		switch rtype := typ.(*RawType); rtype.Kind() {
		case Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
			return cvtUint(src, rtype), true
		case Float32, Float64:
			return cvtUintFloat(src, rtype), true
		case String:
			return cvtUintString(src, rtype), true
		}

	case Float32, Float64:
		switch rtype := typ.(*RawType); rtype.Kind() {
		case Int, Int8, Int16, Int32, Int64:
			return cvtFloatInt(src, rtype), true
		case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
			return cvtFloatUint(src, rtype), true
		case Float32, Float64:
			return cvtFloat(src, rtype), true
		}

		/*
			case Complex64, Complex128:
				switch src.Kind() {
				case Complex64, Complex128:
					return cvtComplex
				}
		*/

	case Slice:
		switch rtype := typ.(*RawType); rtype.Kind() {
		case Array:
			if src.typecode.elem() == rtype.elem() && rtype.Len() <= src.Len() {
				return Value{
					typecode: rtype,
					value:    (*sliceHeader)(src.value).data,
					flags:    src.flags | valueFlagIndirect,
				}, true
			}
		case Pointer:
			if rtype.Elem().Kind() == Array {
				if src.typecode.elem() == rtype.elem().elem() && rtype.elem().Len() <= src.Len() {
					return Value{
						typecode: rtype,
						value:    (*sliceHeader)(src.value).data,
						flags:    src.flags & (valueFlagExported | valueFlagRO),
					}, true
				}
			}
		case String:
			if !src.typecode.elem().isNamed() {
				switch src.Type().Elem().Kind() {
				case Uint8:
					return cvtBytesString(src, rtype), true
				case Int32:
					return cvtRunesString(src, rtype), true
				}
			}
		}

	case String:
		rtype := typ.(*RawType)
		if typ.Kind() == Slice && !rtype.elem().isNamed() {
			switch typ.Elem().Kind() {
			case Uint8:
				return cvtStringBytes(src, rtype), true
			case Int32:
				return cvtStringRunes(src, rtype), true
			}
		}
	}

	// TODO(dgryski): Unimplemented:
	// Chan
	// Non-defined pointers types with same underlying base type
	// Interface <-> Type conversions

	return Value{}, false
}

func cvtInt(v Value, t *RawType) Value {
	return makeInt(v.flags, uint64(v.Int()), t)
}

func cvtUint(v Value, t *RawType) Value {
	return makeInt(v.flags, v.Uint(), t)
}

func cvtIntFloat(v Value, t *RawType) Value {
	return makeFloat(v.flags, float64(v.Int()), t)
}

func cvtUintFloat(v Value, t *RawType) Value {
	return makeFloat(v.flags, float64(v.Uint()), t)
}

func cvtFloatInt(v Value, t *RawType) Value {
	return makeInt(v.flags, uint64(int64(v.Float())), t)
}

func cvtFloatUint(v Value, t *RawType) Value {
	return makeInt(v.flags, uint64(v.Float()), t)
}

func cvtFloat(v Value, t *RawType) Value {
	if v.Type().Kind() == Float32 && t.Kind() == Float32 {
		// Don't do any conversion if both types have underlying type float32.
		// This avoids converting to float64 and back, which will
		// convert a signaling NaN to a quiet NaN. See issue 36400.
		return makeFloat32(v.flags, v.Float32(), t)
	}
	return makeFloat(v.flags, v.Float(), t)
}

//go:linkname stringToBytes runtime.stringToBytes
func stringToBytes(x string) []byte

func cvtStringBytes(v Value, t *RawType) Value {
	b := stringToBytes(*(*string)(v.value))
	return Value{
		typecode: t,
		value:    unsafe.Pointer(&b),
		flags:    v.flags,
	}
}

//go:linkname stringFromBytes runtime.stringFromBytes
func stringFromBytes(x []byte) string

func cvtBytesString(v Value, t *RawType) Value {
	s := stringFromBytes(*(*[]byte)(v.value))
	return Value{
		typecode: t,
		value:    unsafe.Pointer(&s),
		flags:    v.flags,
	}
}

func makeInt(flags valueFlags, bits uint64, t *RawType) Value {
	size := t.Size()

	v := Value{
		typecode: t,
		flags:    flags,
	}

	ptr := unsafe.Pointer(&v.value)
	if size > unsafe.Sizeof(uintptr(0)) {
		ptr = alloc(size, nil)
		v.value = ptr
	}

	switch size {
	case 1:
		*(*uint8)(ptr) = uint8(bits)
	case 2:
		*(*uint16)(ptr) = uint16(bits)
	case 4:
		*(*uint32)(ptr) = uint32(bits)
	case 8:
		*(*uint64)(ptr) = bits
	}
	return v
}

func makeFloat(flags valueFlags, f float64, t *RawType) Value {
	size := t.Size()

	v := Value{
		typecode: t,
		flags:    flags,
	}

	ptr := unsafe.Pointer(&v.value)
	if size > unsafe.Sizeof(uintptr(0)) {
		ptr = alloc(size, nil)
		v.value = ptr
	}

	switch size {
	case 4:
		*(*float32)(ptr) = float32(f)
	case 8:
		*(*float64)(ptr) = f
	}
	return v
}

func makeFloat32(flags valueFlags, f float32, t *RawType) Value {
	v := Value{
		typecode: t,
		flags:    flags,
	}
	*(*float32)(unsafe.Pointer(&v.value)) = float32(f)
	return v
}

func cvtIntString(src Value, t *RawType) Value {
	panic("cvtUintString: unimplemented")
}

func cvtUintString(src Value, t *RawType) Value {
	panic("cvtUintString: unimplemented")
}

func cvtStringRunes(src Value, t *RawType) Value {
	panic("cvsStringRunes: unimplemented")
}

func cvtRunesString(src Value, t *RawType) Value {
	panic("cvsRunesString: unimplemented")
}

//go:linkname slicePanic runtime.slicePanic
func slicePanic()

func MakeSlice(typ Type, len, cap int) Value {
	if typ.Kind() != Slice {
		panic("reflect.MakeSlice of non-slice type")
	}

	rtype := typ.(*RawType)

	ulen := uint(len)
	ucap := uint(cap)
	maxSize := (^uintptr(0)) / 2
	elem := rtype.elem()
	elementSize := elem.Size()
	if elementSize > 1 {
		maxSize /= uintptr(elementSize)
	}
	if ulen > ucap || ucap > uint(maxSize) {
		slicePanic()
	}

	// This can't overflow because of the above checks.
	size := uintptr(ucap) * elementSize

	var slice sliceHeader
	slice.cap = uintptr(ucap)
	slice.len = uintptr(ulen)
	layout := elem.gcLayout()

	slice.data = alloc(size, layout)

	return Value{
		typecode: rtype,
		value:    unsafe.Pointer(&slice),
		flags:    valueFlagExported,
	}
}

var zerobuffer unsafe.Pointer

const zerobufferLen = 32

func init() {
	// 32 characters of zero bytes
	zerobufferStr := "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
	s := (*stringHeader)(unsafe.Pointer(&zerobufferStr))
	zerobuffer = s.data
}

func Zero(typ Type) Value {
	size := typ.Size()
	if size <= unsafe.Sizeof(uintptr(0)) {
		return Value{
			typecode: typ.(*RawType),
			value:    nil,
			flags:    valueFlagExported | valueFlagRO,
		}
	}

	if size <= zerobufferLen {
		return Value{
			typecode: typ.(*RawType),
			value:    unsafe.Pointer(zerobuffer),
			flags:    valueFlagExported | valueFlagRO,
		}
	}

	return Value{
		typecode: typ.(*RawType),
		value:    alloc(size, nil),
		flags:    valueFlagExported | valueFlagRO,
	}
}

// New is the reflect equivalent of the new(T) keyword, returning a pointer to a
// new value of the given type.
func New(typ Type) Value {
	return Value{
		typecode: pointerTo(typ.(*RawType)),
		value:    alloc(typ.Size(), nil),
		flags:    valueFlagExported,
	}
}

type funcHeader struct {
	Context unsafe.Pointer
	Code    unsafe.Pointer
}

// Slice header that matches the underlying structure. Used for when we switch
// to a precise GC, which needs to know exactly where pointers live.
type sliceHeader struct {
	data unsafe.Pointer
	len  uintptr
	cap  uintptr
}

// Like sliceHeader, this type is used internally to make sure pointer and
// non-pointer fields match those of actual strings.
type stringHeader struct {
	data unsafe.Pointer
	len  uintptr
}

// Verify SliceHeader and StringHeader sizes.
// See https://github.com/tinygo-org/tinygo/pull/4156
// and https://github.com/tinygo-org/tinygo/issues/1284.
var (
	_ [unsafe.Sizeof([]byte{})]byte = [unsafe.Sizeof(sliceHeader{})]byte{}
	_ [unsafe.Sizeof("")]byte       = [unsafe.Sizeof(stringHeader{})]byte{}
)

type ValueError struct {
	Method string
	Kind   Kind
}

func (e *ValueError) Error() string {
	if e.Kind == 0 {
		return "reflect: call of " + e.Method + " on zero Value"
	}
	return "reflect: call of " + e.Method + " on " + e.Kind.String() + " Value"
}

//go:linkname memcpy runtime.memcpy
func memcpy(dst, src unsafe.Pointer, size uintptr)

//go:linkname memzero runtime.memzero
func memzero(ptr unsafe.Pointer, size uintptr)

//go:linkname alloc runtime.alloc
func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer

//go:linkname sliceAppend runtime.sliceAppend
func sliceAppend(srcBuf, elemsBuf unsafe.Pointer, srcLen, srcCap, elemsLen uintptr, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr)

//go:linkname sliceCopy runtime.sliceCopy
func sliceCopy(dst, src unsafe.Pointer, dstLen, srcLen uintptr, elemSize uintptr) int

// Copy copies the contents of src into dst until either
// dst has been filled or src has been exhausted.
func Copy(dst, src Value) int {
	compatibleTypes := false ||
		// dst and src are both slices or arrays with equal types
		((dst.typecode.Kind() == Slice || dst.typecode.Kind() == Array) &&
			(src.typecode.Kind() == Slice || src.typecode.Kind() == Array) &&
			(dst.typecode.elem() == src.typecode.elem())) ||
		// dst is array or slice of uint8 and src is string
		((dst.typecode.Kind() == Slice || dst.typecode.Kind() == Array) &&
			dst.typecode.elem().Kind() == Uint8 &&
			src.typecode.Kind() == String)

	if !compatibleTypes {
		panic("Copy: type mismatch: " + dst.typecode.String() + "/" + src.typecode.String())
	}

	// Can read from an unaddressable array but not write to one.
	if dst.typecode.Kind() == Array && !dst.isIndirect() {
		panic("reflect.Copy: unaddressable array value")
	}

	dstbuf, dstlen := buflen(dst)
	srcbuf, srclen := buflen(src)

	if srclen > 0 {
		dst.checkRO()
	}

	return sliceCopy(dstbuf, srcbuf, dstlen, srclen, dst.typecode.elem().Size())
}

func buflen(v Value) (unsafe.Pointer, uintptr) {
	var buf unsafe.Pointer
	var len uintptr
	switch v.typecode.Kind() {
	case Slice:
		hdr := (*sliceHeader)(v.value)
		buf = hdr.data
		len = hdr.len
	case Array:
		if v.isIndirect() || v.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
			buf = v.value
		} else {
			buf = unsafe.Pointer(&v.value)
		}
		len = uintptr(v.Len())
	case String:
		hdr := (*stringHeader)(v.value)
		buf = hdr.data
		len = hdr.len
	default:
		// This shouldn't happen
		panic("reflect.Copy: not slice or array or string")
	}

	return buf, len
}

//go:linkname sliceGrow runtime.sliceGrow
func sliceGrow(buf unsafe.Pointer, oldLen, oldCap, newCap, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr)

// extend slice to hold n new elements
func extendSlice(v Value, n int) sliceHeader {
	if v.Kind() != Slice {
		panic(&ValueError{Method: "extendSlice", Kind: v.Kind()})
	}

	var old sliceHeader
	if v.value != nil {
		old = *(*sliceHeader)(v.value)
	}

	nbuf, nlen, ncap := sliceGrow(old.data, old.len, old.cap, old.len+uintptr(n), v.typecode.elem().Size())

	return sliceHeader{
		data: nbuf,
		len:  nlen + uintptr(n),
		cap:  ncap,
	}
}

// Append appends the values x to a slice s and returns the resulting slice.
// As in Go, each x's value must be assignable to the slice's element type.
func Append(v Value, x ...Value) Value {
	if v.Kind() != Slice {
		panic(&ValueError{Method: "Append", Kind: v.Kind()})
	}
	oldLen := v.Len()
	newslice := extendSlice(v, len(x))
	v.flags = valueFlagExported
	v.value = (unsafe.Pointer)(&newslice)
	for i, xx := range x {
		v.Index(oldLen + i).Set(xx)
	}
	return v
}

// AppendSlice appends a slice t to a slice s and returns the resulting slice.
// The slices s and t must have the same element type.
func AppendSlice(s, t Value) Value {
	if s.typecode.Kind() != Slice || t.typecode.Kind() != Slice || s.typecode != t.typecode {
		// Not a very helpful error message, but shortened to just one error to
		// keep code size down.
		panic("reflect.AppendSlice: invalid types")
	}
	if !s.isExported() || !t.isExported() {
		// One of the sides was not exported, so can't access the data.
		panic("reflect.AppendSlice: unexported")
	}
	sSlice := (*sliceHeader)(s.value)
	tSlice := (*sliceHeader)(t.value)
	elemSize := s.typecode.elem().Size()
	ptr, len, cap := sliceAppend(sSlice.data, tSlice.data, sSlice.len, sSlice.cap, tSlice.len, elemSize)
	result := &sliceHeader{
		data: ptr,
		len:  len,
		cap:  cap,
	}
	return Value{
		typecode: s.typecode,
		value:    unsafe.Pointer(result),
		flags:    valueFlagExported,
	}
}

// Grow increases the slice's capacity, if necessary, to guarantee space for
// another n elements. After Grow(n), at least n elements can be appended
// to the slice without another allocation.
//
// It panics if v's Kind is not a Slice or if n is negative or too large to
// allocate the memory.
func (v Value) Grow(n int) {
	v.checkAddressable()
	if n < 0 {
		panic("reflect.Grow: negative length")
	}
	if v.Kind() != Slice {
		panic(&ValueError{Method: "Grow", Kind: v.Kind()})
	}
	slice := (*sliceHeader)(v.value)
	newslice := extendSlice(v, n)
	// Only copy the new data and cap: the len remains unchanged.
	slice.data = newslice.data
	slice.cap = newslice.cap
}

//go:linkname hashmapStringSet runtime.hashmapStringSet
func hashmapStringSet(m unsafe.Pointer, key string, value unsafe.Pointer)

//go:linkname hashmapBinarySet runtime.hashmapBinarySet
func hashmapBinarySet(m unsafe.Pointer, key, value unsafe.Pointer)

//go:linkname hashmapInterfaceSet runtime.hashmapInterfaceSet
func hashmapInterfaceSet(m unsafe.Pointer, key interface{}, value unsafe.Pointer)

//go:linkname hashmapStringDelete runtime.hashmapStringDelete
func hashmapStringDelete(m unsafe.Pointer, key string)

//go:linkname hashmapBinaryDelete runtime.hashmapBinaryDelete
func hashmapBinaryDelete(m unsafe.Pointer, key unsafe.Pointer)

//go:linkname hashmapInterfaceDelete runtime.hashmapInterfaceDelete
func hashmapInterfaceDelete(m unsafe.Pointer, key interface{})

func (v Value) SetMapIndex(key, elem Value) {
	v.checkRO()
	if v.Kind() != Map {
		panic(&ValueError{Method: "SetMapIndex", Kind: v.Kind()})
	}

	vkey := v.typecode.key()

	// compare key type with actual key type of map
	if !key.typecode.AssignableTo(vkey) {
		panic("reflect.Value.SetMapIndex: incompatible types for key")
	}

	// if elem is the zero Value, it means delete
	del := elem == Value{}

	if !del && !elem.typecode.AssignableTo(v.typecode.elem()) {
		panic("reflect.Value.SetMapIndex: incompatible types for value")
	}

	// make elem an interface if it needs to be converted
	if v.typecode.elem().Kind() == Interface && elem.typecode.Kind() != Interface {
		intf := composeInterface(unsafe.Pointer(elem.typecode), elem.value)
		elem = Value{
			typecode: v.typecode.elem(),
			value:    unsafe.Pointer(&intf),
		}
	}

	if key.Kind() == String {
		if del {
			hashmapStringDelete(v.pointer(), *(*string)(key.value))
		} else {
			var elemptr unsafe.Pointer
			if elem.isIndirect() || elem.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
				elemptr = elem.value
			} else {
				elemptr = unsafe.Pointer(&elem.value)
			}
			hashmapStringSet(v.pointer(), *(*string)(key.value), elemptr)
		}

	} else if key.typecode.isBinary() {
		var keyptr unsafe.Pointer
		if key.isIndirect() || key.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
			keyptr = key.value
		} else {
			keyptr = unsafe.Pointer(&key.value)
		}

		if del {
			hashmapBinaryDelete(v.pointer(), keyptr)
		} else {
			var elemptr unsafe.Pointer
			if elem.isIndirect() || elem.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
				elemptr = elem.value
			} else {
				elemptr = unsafe.Pointer(&elem.value)
			}
			hashmapBinarySet(v.pointer(), keyptr, elemptr)
		}
	} else {
		if del {
			hashmapInterfaceDelete(v.pointer(), key.Interface())
		} else {
			var elemptr unsafe.Pointer
			if elem.isIndirect() || elem.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
				elemptr = elem.value
			} else {
				elemptr = unsafe.Pointer(&elem.value)
			}

			hashmapInterfaceSet(v.pointer(), key.Interface(), elemptr)
		}
	}
}

// FieldByIndex returns the nested field corresponding to index.
func (v Value) FieldByIndex(index []int) Value {
	if len(index) == 1 {
		return v.Field(index[0])
	}
	if v.Kind() != Struct {
		panic(&ValueError{"FieldByIndex", v.Kind()})
	}
	for i, x := range index {
		if i > 0 {
			if v.Kind() == Pointer && v.typecode.elem().Kind() == Struct {
				if v.IsNil() {
					panic("reflect: indirection through nil pointer to embedded struct")
				}
				v = v.Elem()
			}
		}
		v = v.Field(x)
	}
	return v
}

// FieldByIndexErr returns the nested field corresponding to index.
func (v Value) FieldByIndexErr(index []int) (Value, error) {
	return Value{}, &ValueError{Method: "FieldByIndexErr"}
}

func (v Value) FieldByName(name string) Value {
	if v.Kind() != Struct {
		panic(&ValueError{"FieldByName", v.Kind()})
	}

	if field, ok := v.typecode.FieldByName(name); ok {
		return v.FieldByIndex(field.Index)
	}
	return Value{}
}

func (v Value) FieldByNameFunc(match func(string) bool) Value {
	if v.Kind() != Struct {
		panic(&ValueError{"FieldByName", v.Kind()})
	}

	if field, ok := v.typecode.FieldByNameFunc(match); ok {
		return v.FieldByIndex(field.Index)
	}
	return Value{}
}

//go:linkname hashmapMake runtime.hashmapMake
func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) unsafe.Pointer

// MakeMapWithSize creates a new map with the specified type and initial space
// for approximately n elements.
func MakeMapWithSize(typ Type, n int) Value {

	// TODO(dgryski): deduplicate these?  runtime and reflect both need them.
	const (
		hashmapAlgorithmBinary uint8 = iota
		hashmapAlgorithmString
		hashmapAlgorithmInterface
	)

	if typ.Kind() != Map {
		panic(&ValueError{Method: "MakeMap", Kind: typ.Kind()})
	}

	if n < 0 {
		panic("reflect.MakeMapWithSize: negative size hint")
	}

	key := typ.Key().(*RawType)
	val := typ.Elem().(*RawType)

	var alg uint8

	if key.Kind() == String {
		alg = hashmapAlgorithmString
	} else if key.isBinary() {
		alg = hashmapAlgorithmBinary
	} else {
		alg = hashmapAlgorithmInterface
	}

	m := hashmapMake(key.Size(), val.Size(), uintptr(n), alg)

	return Value{
		typecode: typ.(*RawType),
		value:    m,
		flags:    valueFlagExported,
	}
}

// MakeMap creates a new map with the specified type.
func MakeMap(typ Type) Value {
	return MakeMapWithSize(typ, 8)
}

func (v Value) Call(in []Value) []Value {
	panic("unimplemented: (reflect.Value).Call()")
}

func (v Value) CallSlice(in []Value) []Value {
	panic("unimplemented: (reflect.Value).CallSlice()")
}

func (v Value) Method(i int) Value {
	panic("unimplemented: (reflect.Value).Method()")
}

func (v Value) MethodByName(name string) Value {
	panic("unimplemented: (reflect.Value).MethodByName()")
}

func (v Value) Recv() (x Value, ok bool) {
	panic("unimplemented: (reflect.Value).Recv()")
}

func NewAt(typ Type, p unsafe.Pointer) Value {
	panic("unimplemented: reflect.New()")
}
