// Copyright 2014 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 runtime

import (
	
	
	
	
)

const (
	// TODO: remove? These are used by tests but not the actual map
	loadFactorNum = 7
	loadFactorDen = 8
)

type maptype = abi.SwissMapType

//go:linkname maps_errNilAssign internal/runtime/maps.errNilAssign
var maps_errNilAssign error = plainError("assignment to entry in nil map")

//go:linkname maps_mapKeyError internal/runtime/maps.mapKeyError
func maps_mapKeyError( *abi.SwissMapType,  unsafe.Pointer) error {
	return mapKeyError(, )
}

func makemap64( *abi.SwissMapType,  int64,  *maps.Map) *maps.Map {
	if int64(int()) !=  {
		 = 0
	}
	return makemap(, int(), )
}

// makemap_small implements Go map creation for make(map[k]v) and
// make(map[k]v, hint) when hint is known to be at most abi.SwissMapGroupSlots
// at compile time and the map needs to be allocated on the heap.
func makemap_small() *maps.Map {
	return maps.NewEmptyMap()
}

// makemap implements Go map creation for make(map[k]v, hint).
// If the compiler has determined that the map or the first group
// can be created on the stack, m and optionally m.dirPtr may be non-nil.
// If m != nil, the map can be created directly in m.
// If m.dirPtr != nil, it points to a group usable for a small map.
func makemap( *abi.SwissMapType,  int,  *maps.Map) *maps.Map {
	if  < 0 {
		 = 0
	}

	return maps.NewMap(, uintptr(), , maxAlloc)
}

// mapaccess1 returns a pointer to h[key].  Never returns nil, instead
// it will return a reference to the zero object for the elem type if
// the key is not in the map.
// NOTE: The returned pointer may keep the whole map live, so don't
// hold onto it for very long.
//
// mapaccess1 is pushed from internal/runtime/maps. We could just call it, but
// we want to avoid one layer of call.
//
//go:linkname mapaccess1
func mapaccess1( *abi.SwissMapType,  *maps.Map,  unsafe.Pointer) unsafe.Pointer

func mapaccess2( *abi.SwissMapType,  *maps.Map,  unsafe.Pointer) (unsafe.Pointer, bool)

func mapaccess1_fat( *abi.SwissMapType,  *maps.Map, ,  unsafe.Pointer) unsafe.Pointer {
	 := mapaccess1(, , )
	if  == unsafe.Pointer(&zeroVal[0]) {
		return 
	}
	return 
}

func mapaccess2_fat( *abi.SwissMapType,  *maps.Map, ,  unsafe.Pointer) (unsafe.Pointer, bool) {
	 := mapaccess1(, , )
	if  == unsafe.Pointer(&zeroVal[0]) {
		return , false
	}
	return , true
}

// mapassign is pushed from internal/runtime/maps. We could just call it, but
// we want to avoid one layer of call.
//
//go:linkname mapassign
func mapassign( *abi.SwissMapType,  *maps.Map,  unsafe.Pointer) unsafe.Pointer

func mapdelete( *abi.SwissMapType,  *maps.Map,  unsafe.Pointer) {
	if raceenabled &&  != nil {
		 := sys.GetCallerPC()
		 := abi.FuncPCABIInternal()
		racewritepc(unsafe.Pointer(), , )
		raceReadObjectPC(.Key, , , )
	}
	if msanenabled &&  != nil {
		msanread(, .Key.Size_)
	}
	if asanenabled &&  != nil {
		asanread(, .Key.Size_)
	}

	.Delete(, )
}

// mapiterinit initializes the Iter struct used for ranging over maps.
// The Iter struct pointed to by 'it' is allocated on the stack
// by the compilers order pass or on the heap by reflect_mapiterinit.
// Both need to have zeroed hiter since the struct contains pointers.
func mapiterinit( *abi.SwissMapType,  *maps.Map,  *maps.Iter) {
	if raceenabled &&  != nil {
		 := sys.GetCallerPC()
		racereadpc(unsafe.Pointer(), , abi.FuncPCABIInternal())
	}

	.Init(, )
	.Next()
}

func mapiternext( *maps.Iter) {
	if raceenabled {
		 := sys.GetCallerPC()
		racereadpc(unsafe.Pointer(.Map()), , abi.FuncPCABIInternal())
	}

	.Next()
}

// mapclear deletes all keys from a map.
func mapclear( *abi.SwissMapType,  *maps.Map) {
	if raceenabled &&  != nil {
		 := sys.GetCallerPC()
		 := abi.FuncPCABIInternal()
		racewritepc(unsafe.Pointer(), , )
	}

	.Clear()
}

// Reflect stubs. Called from ../reflect/asm_*.s

//go:linkname reflect_makemap reflect.makemap
func reflect_makemap( *abi.SwissMapType,  int) *maps.Map {
	// Check invariants and reflects math.
	if .Key.Equal == nil {
		throw("runtime.reflect_makemap: unsupported map key type")
	}
	// TODO: other checks

	return makemap(, , nil)
}

//go:linkname reflect_mapaccess reflect.mapaccess
func reflect_mapaccess( *abi.SwissMapType,  *maps.Map,  unsafe.Pointer) unsafe.Pointer {
	,  := mapaccess2(, , )
	if ! {
		// reflect wants nil for a missing element
		 = nil
	}
	return 
}

//go:linkname reflect_mapaccess_faststr reflect.mapaccess_faststr
func reflect_mapaccess_faststr( *abi.SwissMapType,  *maps.Map,  string) unsafe.Pointer {
	,  := mapaccess2_faststr(, , )
	if ! {
		// reflect wants nil for a missing element
		 = nil
	}
	return 
}

//go:linkname reflect_mapassign reflect.mapassign0
func reflect_mapassign( *abi.SwissMapType,  *maps.Map,  unsafe.Pointer,  unsafe.Pointer) {
	 := mapassign(, , )
	typedmemmove(.Elem, , )
}

//go:linkname reflect_mapassign_faststr reflect.mapassign_faststr0
func reflect_mapassign_faststr( *abi.SwissMapType,  *maps.Map,  string,  unsafe.Pointer) {
	 := mapassign_faststr(, , )
	typedmemmove(.Elem, , )
}

//go:linkname reflect_mapdelete reflect.mapdelete
func reflect_mapdelete( *abi.SwissMapType,  *maps.Map,  unsafe.Pointer) {
	mapdelete(, , )
}

//go:linkname reflect_mapdelete_faststr reflect.mapdelete_faststr
func reflect_mapdelete_faststr( *abi.SwissMapType,  *maps.Map,  string) {
	mapdelete_faststr(, , )
}

//go:linkname reflect_mapiterinit reflect.mapiterinit
func reflect_mapiterinit( *abi.SwissMapType,  *maps.Map,  *maps.Iter) {
	mapiterinit(, , )
}

//go:linkname reflect_mapiternext reflect.mapiternext
func reflect_mapiternext( *maps.Iter) {
	mapiternext()
}

//go:linkname reflect_mapiterkey reflect.mapiterkey
func reflect_mapiterkey( *maps.Iter) unsafe.Pointer {
	return .Key()
}

//go:linkname reflect_mapiterelem reflect.mapiterelem
func reflect_mapiterelem( *maps.Iter) unsafe.Pointer {
	return .Elem()
}

//go:linkname reflect_maplen reflect.maplen
func reflect_maplen( *maps.Map) int {
	if  == nil {
		return 0
	}
	if raceenabled {
		 := sys.GetCallerPC()
		racereadpc(unsafe.Pointer(), , abi.FuncPCABIInternal())
	}
	return int(.Used())
}

//go:linkname reflect_mapclear reflect.mapclear
func reflect_mapclear( *abi.SwissMapType,  *maps.Map) {
	mapclear(, )
}

//go:linkname reflectlite_maplen internal/reflectlite.maplen
func reflectlite_maplen( *maps.Map) int {
	if  == nil {
		return 0
	}
	if raceenabled {
		 := sys.GetCallerPC()
		racereadpc(unsafe.Pointer(), , abi.FuncPCABIInternal(reflect_maplen))
	}
	return int(.Used())
}

// mapinitnoop is a no-op function known the Go linker; if a given global
// map (of the right size) is determined to be dead, the linker will
// rewrite the relocation (from the package init func) from the outlined
// map init function to this symbol. Defined in assembly so as to avoid
// complications with instrumentation (coverage, etc).
func mapinitnoop()

// mapclone for implementing maps.Clone
//
//go:linkname mapclone maps.clone
func mapclone( any) any {
	 := efaceOf(&)
	.data = unsafe.Pointer(mapclone2((*abi.SwissMapType)(unsafe.Pointer(._type)), (*maps.Map)(.data)))
	return 
}

func mapclone2( *abi.SwissMapType,  *maps.Map) *maps.Map {
	 := makemap(, int(.Used()), nil)

	var  maps.Iter
	.Init(, )
	for .Next(); .Key() != nil; .Next() {
		.Put(, .Key(), .Elem())
	}

	return 
}

// keys for implementing maps.keys
//
//go:linkname keys maps.keys
func keys( any,  unsafe.Pointer) {
	// Currently unused in the maps package.
	panic("unimplemented")
}

// values for implementing maps.values
//
//go:linkname values maps.values
func values( any,  unsafe.Pointer) {
	// Currently unused in the maps package.
	panic("unimplemented")
}