// Copyright 2024 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.

//go:build goexperiment.swissmap

package reflect

import (
	
	
	
)

// mapType represents a map type.
type mapType struct {
	abi.SwissMapType
}

func ( *rtype) () Type {
	if .Kind() != Map {
		panic("reflect: Key of non-map type " + .String())
	}
	 := (*mapType)(unsafe.Pointer())
	return toType(.Key)
}

// MapOf returns the map type with the given key and element types.
// For example, if k represents int and e represents string,
// MapOf(k, e) represents map[int]string.
//
// If the key type is not a valid map key type (that is, if it does
// not implement Go's == operator), MapOf panics.
func (,  Type) Type {
	 := .common()
	 := .common()

	if .Equal == nil {
		panic("reflect.MapOf: invalid key type " + stringFor())
	}

	// Look in cache.
	 := cacheKey{Map, , , 0}
	if ,  := lookupCache.Load();  {
		return .(Type)
	}

	// Look in known types.
	 := "map[" + stringFor() + "]" + stringFor()
	for ,  := range typesByString() {
		 := (*mapType)(unsafe.Pointer())
		if .Key ==  && .Elem ==  {
			,  := lookupCache.LoadOrStore(, toRType())
			return .(Type)
		}
	}

	,  := groupAndSlotOf(, )

	// Make a map type.
	// Note: flag values must match those used in the TMAP case
	// in ../cmd/compile/internal/reflectdata/reflect.go:writeType.
	var  any = (map[unsafe.Pointer]unsafe.Pointer)(nil)
	 := **(**mapType)(unsafe.Pointer(&))
	.Str = resolveReflectName(newName(, "", false, false))
	.TFlag = 0
	.Hash = fnv1(.Hash, 'm', byte(.Hash>>24), byte(.Hash>>16), byte(.Hash>>8), byte(.Hash))
	.Key = 
	.Elem = 
	.Group = .common()
	.Hasher = func( unsafe.Pointer,  uintptr) uintptr {
		return typehash(, , )
	}
	.GroupSize = .Group.Size()
	.SlotSize = .Size()
	.ElemOff = .Field(1).Offset
	.Flags = 0
	if needKeyUpdate() {
		.Flags |= abi.SwissMapNeedKeyUpdate
	}
	if hashMightPanic() {
		.Flags |= abi.SwissMapHashMightPanic
	}
	if .Size_ > abi.SwissMapMaxKeyBytes {
		.Flags |= abi.SwissMapIndirectKey
	}
	if .Size_ > abi.SwissMapMaxKeyBytes {
		.Flags |= abi.SwissMapIndirectElem
	}
	.PtrToThis = 0

	,  := lookupCache.LoadOrStore(, toRType(&.Type))
	return .(Type)
}

func groupAndSlotOf(,  Type) (Type, Type) {
	// type group struct {
	//     ctrl uint64
	//     slots [abi.SwissMapGroupSlots]struct {
	//         key  keyType
	//         elem elemType
	//     }
	// }

	if .Size() > abi.SwissMapMaxKeyBytes {
		 = PointerTo()
	}
	if .Size() > abi.SwissMapMaxElemBytes {
		 = PointerTo()
	}

	 := []StructField{
		{
			Name: "Key",
			Type: ,
		},
		{
			Name: "Elem",
			Type: ,
		},
	}
	 := StructOf()

	 = []StructField{
		{
			Name: "Ctrl",
			Type: TypeFor[uint64](),
		},
		{
			Name: "Slots",
			Type: ArrayOf(abi.SwissMapGroupSlots, ),
		},
	}
	 := StructOf()
	return , 
}

var stringType = rtypeOf("")

// MapIndex returns the value associated with key in the map v.
// It panics if v's Kind is not [Map].
// It returns the zero Value if key is not found in the map or if v represents a nil map.
// As in Go, the key's value must be assignable to the map's key type.
func ( Value) ( Value) Value {
	.mustBe(Map)
	 := (*mapType)(unsafe.Pointer(.typ()))

	// Do not require key to be exported, so that DeepEqual
	// and other programs can use all the keys returned by
	// MapKeys as arguments to MapIndex. If either the map
	// or the key is unexported, though, the result will be
	// considered unexported. This is consistent with the
	// behavior for structs, which allow read but not write
	// of unexported fields.

	var  unsafe.Pointer
	if (.Key == stringType || .kind() == String) && .Key == .typ() && .Elem.Size() <= abi.SwissMapMaxElemBytes {
		 := *(*string)(.ptr)
		 = mapaccess_faststr(.typ(), .pointer(), )
	} else {
		 = .assignTo("reflect.Value.MapIndex", .Key, nil)
		var  unsafe.Pointer
		if .flag&flagIndir != 0 {
			 = .ptr
		} else {
			 = unsafe.Pointer(&.ptr)
		}
		 = mapaccess(.typ(), .pointer(), )
	}
	if  == nil {
		return Value{}
	}
	 := .Elem
	 := (.flag | .flag).ro()
	 |= flag(.Kind())
	return copyVal(, , )
}

// MapKeys returns a slice containing all the keys present in the map,
// in unspecified order.
// It panics if v's Kind is not [Map].
// It returns an empty slice if v represents a nil map.
func ( Value) () []Value {
	.mustBe(Map)
	 := (*mapType)(unsafe.Pointer(.typ()))
	 := .Key

	 := .flag.ro() | flag(.Kind())

	 := .pointer()
	 := int(0)
	if  != nil {
		 = maplen()
	}
	var  maps.Iter
	mapiterinit(.typ(), , &)
	 := make([]Value, )
	var  int
	for  = 0;  < len(); ++ {
		 := .Key()
		if  == nil {
			// Someone deleted an entry from the map since we
			// called maplen above. It's a data race, but nothing
			// we can do about it.
			break
		}
		[] = copyVal(, , )
		mapiternext(&)
	}
	return [:]
}

// A MapIter is an iterator for ranging over a map.
// See [Value.MapRange].
type MapIter struct {
	m     Value
	hiter maps.Iter
}

// TODO(prattmic): only for sharing the linkname declarations with old maps.
// Remove with old maps.
type hiter = maps.Iter

// Key returns the key of iter's current map entry.
func ( *MapIter) () Value {
	if !.hiter.Initialized() {
		panic("MapIter.Key called before Next")
	}
	 := .hiter.Key()
	if  == nil {
		panic("MapIter.Key called on exhausted iterator")
	}

	 := (*mapType)(unsafe.Pointer(.m.typ()))
	 := .Key
	return copyVal(, .m.flag.ro()|flag(.Kind()), )
}

// SetIterKey assigns to v the key of iter's current map entry.
// It is equivalent to v.Set(iter.Key()), but it avoids allocating a new Value.
// As in Go, the key must be assignable to v's type and
// must not be derived from an unexported field.
// It panics if [Value.CanSet] returns false.
func ( Value) ( *MapIter) {
	if !.hiter.Initialized() {
		panic("reflect: Value.SetIterKey called before Next")
	}
	 := .hiter.Key()
	if  == nil {
		panic("reflect: Value.SetIterKey called on exhausted iterator")
	}

	.mustBeAssignable()
	var  unsafe.Pointer
	if .kind() == Interface {
		 = .ptr
	}

	 := (*mapType)(unsafe.Pointer(.m.typ()))
	 := .Key

	.m.mustBeExported() // do not let unexported m leak
	 := Value{, , .m.flag | flag(.Kind()) | flagIndir}
	 = .assignTo("reflect.MapIter.SetKey", .typ(), )
	typedmemmove(.typ(), .ptr, .ptr)
}

// Value returns the value of iter's current map entry.
func ( *MapIter) () Value {
	if !.hiter.Initialized() {
		panic("MapIter.Value called before Next")
	}
	 := .hiter.Elem()
	if  == nil {
		panic("MapIter.Value called on exhausted iterator")
	}

	 := (*mapType)(unsafe.Pointer(.m.typ()))
	 := .Elem
	return copyVal(, .m.flag.ro()|flag(.Kind()), )
}

// SetIterValue assigns to v the value of iter's current map entry.
// It is equivalent to v.Set(iter.Value()), but it avoids allocating a new Value.
// As in Go, the value must be assignable to v's type and
// must not be derived from an unexported field.
// It panics if [Value.CanSet] returns false.
func ( Value) ( *MapIter) {
	if !.hiter.Initialized() {
		panic("reflect: Value.SetIterValue called before Next")
	}
	 := .hiter.Elem()
	if  == nil {
		panic("reflect: Value.SetIterValue called on exhausted iterator")
	}

	.mustBeAssignable()
	var  unsafe.Pointer
	if .kind() == Interface {
		 = .ptr
	}

	 := (*mapType)(unsafe.Pointer(.m.typ()))
	 := .Elem

	.m.mustBeExported() // do not let unexported m leak
	 := Value{, , .m.flag | flag(.Kind()) | flagIndir}
	 = .assignTo("reflect.MapIter.SetValue", .typ(), )
	typedmemmove(.typ(), .ptr, .ptr)
}

// Next advances the map iterator and reports whether there is another
// entry. It returns false when iter is exhausted; subsequent
// calls to [MapIter.Key], [MapIter.Value], or [MapIter.Next] will panic.
func ( *MapIter) () bool {
	if !.m.IsValid() {
		panic("MapIter.Next called on an iterator that does not have an associated map Value")
	}
	if !.hiter.Initialized() {
		mapiterinit(.m.typ(), .m.pointer(), &.hiter)
	} else {
		if .hiter.Key() == nil {
			panic("MapIter.Next called on exhausted iterator")
		}
		mapiternext(&.hiter)
	}
	return .hiter.Key() != nil
}

// Reset modifies iter to iterate over v.
// It panics if v's Kind is not [Map] and v is not the zero Value.
// Reset(Value{}) causes iter to not to refer to any map,
// which may allow the previously iterated-over map to be garbage collected.
func ( *MapIter) ( Value) {
	if .IsValid() {
		.mustBe(Map)
	}
	.m = 
	.hiter = maps.Iter{}
}

// MapRange returns a range iterator for a map.
// It panics if v's Kind is not [Map].
//
// Call [MapIter.Next] to advance the iterator, and [MapIter.Key]/[MapIter.Value] to access each entry.
// [MapIter.Next] returns false when the iterator is exhausted.
// MapRange follows the same iteration semantics as a range statement.
//
// Example:
//
//	iter := reflect.ValueOf(m).MapRange()
//	for iter.Next() {
//		k := iter.Key()
//		v := iter.Value()
//		...
//	}
func ( Value) () *MapIter {
	// This is inlinable to take advantage of "function outlining".
	// The allocation of MapIter can be stack allocated if the caller
	// does not allow it to escape.
	// See https://blog.filippo.io/efficient-go-apis-with-the-inliner/
	if .kind() != Map {
		.panicNotMap()
	}
	return &MapIter{m: }
}

// SetMapIndex sets the element associated with key in the map v to elem.
// It panics if v's Kind is not [Map].
// If elem is the zero Value, SetMapIndex deletes the key from the map.
// Otherwise if v holds a nil map, SetMapIndex will panic.
// As in Go, key's elem must be assignable to the map's key type,
// and elem's value must be assignable to the map's elem type.
func ( Value) (,  Value) {
	.mustBe(Map)
	.mustBeExported()
	.mustBeExported()
	 := (*mapType)(unsafe.Pointer(.typ()))

	if (.Key == stringType || .kind() == String) && .Key == .typ() && .Elem.Size() <= abi.SwissMapMaxElemBytes {
		 := *(*string)(.ptr)
		if .typ() == nil {
			mapdelete_faststr(.typ(), .pointer(), )
			return
		}
		.mustBeExported()
		 = .assignTo("reflect.Value.SetMapIndex", .Elem, nil)
		var  unsafe.Pointer
		if .flag&flagIndir != 0 {
			 = .ptr
		} else {
			 = unsafe.Pointer(&.ptr)
		}
		mapassign_faststr(.typ(), .pointer(), , )
		return
	}

	 = .assignTo("reflect.Value.SetMapIndex", .Key, nil)
	var  unsafe.Pointer
	if .flag&flagIndir != 0 {
		 = .ptr
	} else {
		 = unsafe.Pointer(&.ptr)
	}
	if .typ() == nil {
		mapdelete(.typ(), .pointer(), )
		return
	}
	.mustBeExported()
	 = .assignTo("reflect.Value.SetMapIndex", .Elem, nil)
	var  unsafe.Pointer
	if .flag&flagIndir != 0 {
		 = .ptr
	} else {
		 = unsafe.Pointer(&.ptr)
	}
	mapassign(.typ(), .pointer(), , )
}

// Force slow panicking path not inlined, so it won't add to the
// inlining budget of the caller.
// TODO: undo when the inliner is no longer bottom-up only.
//
//go:noinline
func ( flag) () {
	.mustBe(Map)
}