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

package maps

import (
	
	
	
	
	
	
)

func ( *Map) ( *abi.MapType,  string) unsafe.Pointer {
	 := groupReference{
		data: .dirPtr,
	}

	 := *.ctrls()
	 := .key(, 0)
	var  uintptr
	if goexperiment.MapSplitGroup {
		 = 2 * goarch.PtrSize // keys are contiguous in split layout
	} else {
		 = .KeyStride // == SlotSize in interleaved layout
	}

	// The 64 threshold was chosen based on performance of BenchmarkMapStringKeysEight,
	// where there are 8 keys to check, all of which don't quick-match the lookup key.
	// In that case, we can save hashing the lookup key. That savings is worth this extra code
	// for strings that are long enough that hashing is expensive.
	if len() > 64 {
		// String hashing and equality might be expensive. Do a quick check first.
		 := abi.MapGroupSlots
		for  := range abi.MapGroupSlots {
			if &(1<<7) == 0 && longStringQuickEqualityTest(, *(*string)()) {
				if  < abi.MapGroupSlots {
					// 2 strings both passed the quick equality test.
					// Break out of this loop and do it the slow way.
					goto 
				}
				 = 
			}
			 = unsafe.Pointer(uintptr() + )
			 >>= 8
		}
		if  == abi.MapGroupSlots {
			// No slot passed the quick test.
			return nil
		}
		// There's exactly one slot that passed the quick test. Do the single expensive comparison.
		 = .key(, uintptr())
		if  == *(*string)() {
			if goexperiment.MapSplitGroup {
				return .elem(, uintptr())
			} else {
				return unsafe.Pointer(uintptr() + 2*goarch.PtrSize)
			}
		}
		return nil
	}

:
	// This path will cost 1 hash and 1+ε comparisons.
	var  uintptr
	// See the related comment in runtime_mapaccess2_fast32
	if memHashAESImplemented && UseAeshash {
		 = memHashAES(unsafe.Pointer(unsafe.StringData()), .seed, uintptr(len()))
	} else {
		 = memHashFallback(unsafe.Pointer(unsafe.StringData()), .seed, uintptr(len()))
	}
	 := uint8(h2())
	 = *.ctrls()
	 = .key(, 0)

	for  := range uintptr(abi.MapGroupSlots) {
		if uint8() ==  &&  == *(*string)() {
			if goexperiment.MapSplitGroup {
				return .elem(, )
			} else {
				return unsafe.Pointer(uintptr() + 2*goarch.PtrSize)
			}
		}
		 = unsafe.Pointer(uintptr() + )
		 >>= 8
	}
	return nil
}

// Returns true if a and b might be equal.
// Returns false if a and b are definitely not equal.
// Requires len(a)>=8.
func (,  string) bool {
	if len() != len() {
		return false
	}
	,  := unsafe.Pointer(unsafe.StringData()), unsafe.Pointer(unsafe.StringData())
	// Check first 8 bytes.
	if *(*[8]byte)() != *(*[8]byte)() {
		return false
	}
	// Check last 8 bytes.
	 = add(, uintptr(len()-8))
	 = add(, uintptr(len()-8))
	if *(*[8]byte)() != *(*[8]byte)() {
		return false
	}
	return true
}

//go:linkname runtime_mapaccess1_faststr runtime.mapaccess1_faststr
func ( *abi.MapType,  *Map,  string) unsafe.Pointer {
	,  := runtime_mapaccess2_faststr(, , )
	return 
}

//go:linkname runtime_mapaccess2_faststr runtime.mapaccess2_faststr
func ( *abi.MapType,  *Map,  string) (unsafe.Pointer, bool) {
	if race.Enabled &&  != nil {
		 := sys.GetCallerPC()
		 := abi.FuncPCABIInternal()
		race.ReadPC(unsafe.Pointer(), , )
	}

	if  == nil || .Used() == 0 {
		return unsafe.Pointer(&zeroVal[0]), false
	}

	if .writing != 0 {
		fatal("concurrent map read and map write")
		return nil, false
	}

	if .dirLen <= 0 {
		 := .getWithoutKeySmallFastStr(, )
		if  == nil {
			return unsafe.Pointer(&zeroVal[0]), false
		}
		return , true
	}

	var  uintptr
	// See the related comment in runtime_mapaccess2_fast32
	if memHashAESImplemented && UseAeshash {
		 = memHashAES(unsafe.Pointer(unsafe.StringData()), .seed, uintptr(len()))
	} else {
		 = memHashFallback(unsafe.Pointer(unsafe.StringData()), .seed, uintptr(len()))
	}

	// Select table.
	 := .directoryIndex()
	 := .directoryAt()

	// Probe table.
	 := makeProbeSeq(h1(), .groups.lengthMask)
	 := h2()
	for ; ;  = .next() {
		 := .groups.group(, .offset)

		 := .ctrls().matchH2()

		for  != 0 {
			 := .first()

			 := .key(, )
			if  == *(*string)() {
				if goexperiment.MapSplitGroup {
					return .elem(, ), true
				} else {
					return unsafe.Pointer(uintptr() + 2*goarch.PtrSize), true
				}
			}
			 = .removeFirst()
		}

		 = .ctrls().matchEmpty()
		if  != 0 {
			// Finding an empty slot means we've reached the end of
			// the probe sequence.
			return unsafe.Pointer(&zeroVal[0]), false
		}
	}
}

func ( *Map) ( *abi.MapType,  uintptr,  string) unsafe.Pointer {
	 := groupReference{
		data: .dirPtr,
	}

	 := .ctrls().matchH2(h2())

	// Look for an existing slot containing this key.
	for  != 0 {
		 := .first()

		 := .key(, )
		if  == *(*string)() {
			// Key needs update, as the backing storage may differ.
			*(*string)() = 
			 := .elem(, )
			return 
		}
		 = .removeFirst()
	}

	// There can't be deleted slots, small maps can't have them
	// (see deleteSmall). Use matchEmptyOrDeleted as it is a bit
	// more efficient than matchEmpty.
	 = .ctrls().matchEmptyOrDeleted()
	if  == 0 {
		// No empty slot found. Need to grow the map.
		return nil
	}

	 := .first()

	 := .key(, )
	*(*string)() = 

	 := .elem(, )

	.ctrls().set(, ctrl(h2()))
	.used++

	return 
}

func ( *table) ( *abi.MapType,  uintptr,  string) unsafe.Pointer {
	if .growthLeft == 0 {
		panic("invariant failed: growthLeft is unexpectedly 0")
	}

	// Given key and its hash hash(key), to insert it, we construct a
	// probeSeq, and use it to find the first group with an unoccupied (empty
	// or deleted) slot. We place the key/value into the first such slot in
	// the group and mark it as full with key's H2.
	 := makeProbeSeq(h1(), .groups.lengthMask)
	for ; ;  = .next() {
		 := .groups.group(, .offset)

		 := .ctrls().matchEmptyOrDeleted()
		if  != 0 {
			 := .first()

			 := .key(, )
			*(*string)() = 

			 := .elem(, )

			.growthLeft--
			.used++
			.ctrls().set(, ctrl(h2()))
			return 
		}
	}
}

//go:linkname runtime_mapassign_faststr runtime.mapassign_faststr
func ( *abi.MapType,  *Map,  string) unsafe.Pointer {
	if  == nil {
		panic(errNilAssign)
	}
	if race.Enabled {
		 := sys.GetCallerPC()
		 := abi.FuncPCABIInternal()
		race.WritePC(unsafe.Pointer(), , )
	}
	if .writing != 0 {
		fatal("concurrent map writes")
	}

	var  uintptr
	// See the related comment in runtime_mapaccess2_fast32
	if memHashAESImplemented && UseAeshash {
		 = memHashAES(unsafe.Pointer(unsafe.StringData()), .seed, uintptr(len()))
	} else {
		 = memHashFallback(unsafe.Pointer(unsafe.StringData()), .seed, uintptr(len()))
	}

	// Set writing after calling Hasher, since Hasher may panic, in which
	// case we have not actually done a write.
	.writing ^= 1 // toggle, see comment on writing

	if .dirPtr == nil {
		.growToSmall()
	}

	if .dirLen == 0 {
		 := .putSlotSmallFastStr(, , )
		if  == nil {
			// Can't fit another entry, grow to full size map.
			 := .growToTable()

			 = .uncheckedPutSlotForAssignFastStr(, , )
			.used++

			.checkInvariants(, )
		}

		if .writing == 0 {
			fatal("concurrent map writes")
		}
		.writing ^= 1

		return 
	}

	var  unsafe.Pointer
:
	for {
		// Select table.
		 := .directoryIndex()
		 := .directoryAt()

		 := makeProbeSeq(h1(), .groups.lengthMask)

		// As we look for a match, keep track of the first deleted slot
		// we find, which we'll use to insert the new entry if
		// necessary.
		var  groupReference
		var  uintptr

		 := h2()
		for ; ;  = .next() {
			 := .groups.group(, .offset)
			 := .ctrls().matchH2()

			// Look for an existing slot containing this key.
			for  != 0 {
				 := .first()

				 := .key(, )
				if  == *(*string)() {
					// Key needs update, as the backing
					// storage may differ.
					*(*string)() = 
					 = .elem(, )

					.checkInvariants(, )
					break 
				}
				 = .removeFirst()
			}

			// No existing slot for this key in this group. Is this the end
			// of the probe sequence?
			 = .ctrls().matchEmptyOrDeleted()
			if  == 0 {
				continue // nothing but filled slots. Keep probing.
			}
			 := .first()
			if .ctrls().get() == ctrlDeleted {
				// There are some deleted slots. Remember
				// the first one, and keep probing.
				if .data == nil {
					 = 
					 = 
				}
				continue
			}
			// We've found an empty slot, which means we've reached the end of
			// the probe sequence.

			// If we found a deleted slot along the way, we can
			// replace it without consuming growthLeft.
			if .data != nil {
				 = 
				 = 
				.growthLeft++ // will be decremented below to become a no-op.
			}

			// If we have no space left, first try to remove some tombstones.
			if .growthLeft == 0 {
				.pruneTombstones(, )
			}

			// If there is room left to grow, just insert the new entry.
			if .growthLeft > 0 {
				 := .key(, )
				*(*string)() = 

				 = .elem(, )

				.ctrls().set(, ctrl())
				.growthLeft--
				.used++
				.used++

				.checkInvariants(, )
				break 
			}

			.rehash(, )
			continue 
		}
	}

	if .writing == 0 {
		fatal("concurrent map writes")
	}
	.writing ^= 1

	return 
}

//go:linkname runtime_mapdelete_faststr runtime.mapdelete_faststr
func ( *abi.MapType,  *Map,  string) {
	if race.Enabled {
		 := sys.GetCallerPC()
		 := abi.FuncPCABIInternal()
		race.WritePC(unsafe.Pointer(), , )
	}

	if  == nil || .Used() == 0 {
		return
	}

	.Delete(, abi.NoEscape(unsafe.Pointer(&)))
}