package reflect
import (
"internal/abi"
"internal/runtime/maps"
"unsafe"
)
type mapType struct {
abi .SwissMapType
}
func (t *rtype ) Key () Type {
if t .Kind () != Map {
panic ("reflect: Key of non-map type " + t .String ())
}
tt := (*mapType )(unsafe .Pointer (t ))
return toType (tt .Key )
}
func MapOf (key , elem Type ) Type {
ktyp := key .common ()
etyp := elem .common ()
if ktyp .Equal == nil {
panic ("reflect.MapOf: invalid key type " + stringFor (ktyp ))
}
ckey := cacheKey {Map , ktyp , etyp , 0 }
if mt , ok := lookupCache .Load (ckey ); ok {
return mt .(Type )
}
s := "map[" + stringFor (ktyp ) + "]" + stringFor (etyp )
for _ , tt := range typesByString (s ) {
mt := (*mapType )(unsafe .Pointer (tt ))
if mt .Key == ktyp && mt .Elem == etyp {
ti , _ := lookupCache .LoadOrStore (ckey , toRType (tt ))
return ti .(Type )
}
}
group , slot := groupAndSlotOf (key , elem )
var imap any = (map [unsafe .Pointer ]unsafe .Pointer )(nil )
mt := **(**mapType )(unsafe .Pointer (&imap ))
mt .Str = resolveReflectName (newName (s , "" , false , false ))
mt .TFlag = 0
mt .Hash = fnv1 (etyp .Hash , 'm' , byte (ktyp .Hash >>24 ), byte (ktyp .Hash >>16 ), byte (ktyp .Hash >>8 ), byte (ktyp .Hash ))
mt .Key = ktyp
mt .Elem = etyp
mt .Group = group .common ()
mt .Hasher = func (p unsafe .Pointer , seed uintptr ) uintptr {
return typehash (ktyp , p , seed )
}
mt .GroupSize = mt .Group .Size ()
mt .SlotSize = slot .Size ()
mt .ElemOff = slot .Field (1 ).Offset
mt .Flags = 0
if needKeyUpdate (ktyp ) {
mt .Flags |= abi .SwissMapNeedKeyUpdate
}
if hashMightPanic (ktyp ) {
mt .Flags |= abi .SwissMapHashMightPanic
}
if ktyp .Size_ > abi .SwissMapMaxKeyBytes {
mt .Flags |= abi .SwissMapIndirectKey
}
if etyp .Size_ > abi .SwissMapMaxKeyBytes {
mt .Flags |= abi .SwissMapIndirectElem
}
mt .PtrToThis = 0
ti , _ := lookupCache .LoadOrStore (ckey , toRType (&mt .Type ))
return ti .(Type )
}
func groupAndSlotOf(ktyp , etyp Type ) (Type , Type ) {
if ktyp .Size () > abi .SwissMapMaxKeyBytes {
ktyp = PointerTo (ktyp )
}
if etyp .Size () > abi .SwissMapMaxElemBytes {
etyp = PointerTo (etyp )
}
fields := []StructField {
{
Name : "Key" ,
Type : ktyp ,
},
{
Name : "Elem" ,
Type : etyp ,
},
}
slot := StructOf (fields )
fields = []StructField {
{
Name : "Ctrl" ,
Type : TypeFor [uint64 ](),
},
{
Name : "Slots" ,
Type : ArrayOf (abi .SwissMapGroupSlots , slot ),
},
}
group := StructOf (fields )
return group , slot
}
var stringType = rtypeOf ("" )
func (v Value ) MapIndex (key Value ) Value {
v .mustBe (Map )
tt := (*mapType )(unsafe .Pointer (v .typ ()))
var e unsafe .Pointer
if (tt .Key == stringType || key .kind () == String ) && tt .Key == key .typ () && tt .Elem .Size () <= abi .SwissMapMaxElemBytes {
k := *(*string )(key .ptr )
e = mapaccess_faststr (v .typ (), v .pointer (), k )
} else {
key = key .assignTo ("reflect.Value.MapIndex" , tt .Key , nil )
var k unsafe .Pointer
if key .flag &flagIndir != 0 {
k = key .ptr
} else {
k = unsafe .Pointer (&key .ptr )
}
e = mapaccess (v .typ (), v .pointer (), k )
}
if e == nil {
return Value {}
}
typ := tt .Elem
fl := (v .flag | key .flag ).ro ()
fl |= flag (typ .Kind ())
return copyVal (typ , fl , e )
}
func (v Value ) MapKeys () []Value {
v .mustBe (Map )
tt := (*mapType )(unsafe .Pointer (v .typ ()))
keyType := tt .Key
fl := v .flag .ro () | flag (keyType .Kind ())
m := v .pointer ()
mlen := int (0 )
if m != nil {
mlen = maplen (m )
}
var it maps .Iter
mapiterinit (v .typ (), m , &it )
a := make ([]Value , mlen )
var i int
for i = 0 ; i < len (a ); i ++ {
key := it .Key ()
if key == nil {
break
}
a [i ] = copyVal (keyType , fl , key )
mapiternext (&it )
}
return a [:i ]
}
type MapIter struct {
m Value
hiter maps .Iter
}
type hiter = maps .Iter
func (iter *MapIter ) Key () Value {
if !iter .hiter .Initialized () {
panic ("MapIter.Key called before Next" )
}
iterkey := iter .hiter .Key ()
if iterkey == nil {
panic ("MapIter.Key called on exhausted iterator" )
}
t := (*mapType )(unsafe .Pointer (iter .m .typ ()))
ktype := t .Key
return copyVal (ktype , iter .m .flag .ro ()|flag (ktype .Kind ()), iterkey )
}
func (v Value ) SetIterKey (iter *MapIter ) {
if !iter .hiter .Initialized () {
panic ("reflect: Value.SetIterKey called before Next" )
}
iterkey := iter .hiter .Key ()
if iterkey == nil {
panic ("reflect: Value.SetIterKey called on exhausted iterator" )
}
v .mustBeAssignable ()
var target unsafe .Pointer
if v .kind () == Interface {
target = v .ptr
}
t := (*mapType )(unsafe .Pointer (iter .m .typ ()))
ktype := t .Key
iter .m .mustBeExported ()
key := Value {ktype , iterkey , iter .m .flag | flag (ktype .Kind ()) | flagIndir }
key = key .assignTo ("reflect.MapIter.SetKey" , v .typ (), target )
typedmemmove (v .typ (), v .ptr , key .ptr )
}
func (iter *MapIter ) Value () Value {
if !iter .hiter .Initialized () {
panic ("MapIter.Value called before Next" )
}
iterelem := iter .hiter .Elem ()
if iterelem == nil {
panic ("MapIter.Value called on exhausted iterator" )
}
t := (*mapType )(unsafe .Pointer (iter .m .typ ()))
vtype := t .Elem
return copyVal (vtype , iter .m .flag .ro ()|flag (vtype .Kind ()), iterelem )
}
func (v Value ) SetIterValue (iter *MapIter ) {
if !iter .hiter .Initialized () {
panic ("reflect: Value.SetIterValue called before Next" )
}
iterelem := iter .hiter .Elem ()
if iterelem == nil {
panic ("reflect: Value.SetIterValue called on exhausted iterator" )
}
v .mustBeAssignable ()
var target unsafe .Pointer
if v .kind () == Interface {
target = v .ptr
}
t := (*mapType )(unsafe .Pointer (iter .m .typ ()))
vtype := t .Elem
iter .m .mustBeExported ()
elem := Value {vtype , iterelem , iter .m .flag | flag (vtype .Kind ()) | flagIndir }
elem = elem .assignTo ("reflect.MapIter.SetValue" , v .typ (), target )
typedmemmove (v .typ (), v .ptr , elem .ptr )
}
func (iter *MapIter ) Next () bool {
if !iter .m .IsValid () {
panic ("MapIter.Next called on an iterator that does not have an associated map Value" )
}
if !iter .hiter .Initialized () {
mapiterinit (iter .m .typ (), iter .m .pointer (), &iter .hiter )
} else {
if iter .hiter .Key () == nil {
panic ("MapIter.Next called on exhausted iterator" )
}
mapiternext (&iter .hiter )
}
return iter .hiter .Key () != nil
}
func (iter *MapIter ) Reset (v Value ) {
if v .IsValid () {
v .mustBe (Map )
}
iter .m = v
iter .hiter = maps .Iter {}
}
func (v Value ) MapRange () *MapIter {
if v .kind () != Map {
v .panicNotMap ()
}
return &MapIter {m : v }
}
func (v Value ) SetMapIndex (key , elem Value ) {
v .mustBe (Map )
v .mustBeExported ()
key .mustBeExported ()
tt := (*mapType )(unsafe .Pointer (v .typ ()))
if (tt .Key == stringType || key .kind () == String ) && tt .Key == key .typ () && tt .Elem .Size () <= abi .SwissMapMaxElemBytes {
k := *(*string )(key .ptr )
if elem .typ () == nil {
mapdelete_faststr (v .typ (), v .pointer (), k )
return
}
elem .mustBeExported ()
elem = elem .assignTo ("reflect.Value.SetMapIndex" , tt .Elem , nil )
var e unsafe .Pointer
if elem .flag &flagIndir != 0 {
e = elem .ptr
} else {
e = unsafe .Pointer (&elem .ptr )
}
mapassign_faststr (v .typ (), v .pointer (), k , e )
return
}
key = key .assignTo ("reflect.Value.SetMapIndex" , tt .Key , nil )
var k unsafe .Pointer
if key .flag &flagIndir != 0 {
k = key .ptr
} else {
k = unsafe .Pointer (&key .ptr )
}
if elem .typ () == nil {
mapdelete (v .typ (), v .pointer (), k )
return
}
elem .mustBeExported ()
elem = elem .assignTo ("reflect.Value.SetMapIndex" , tt .Elem , nil )
var e unsafe .Pointer
if elem .flag &flagIndir != 0 {
e = elem .ptr
} else {
e = unsafe .Pointer (&elem .ptr )
}
mapassign (v .typ (), v .pointer (), k , e )
}
func (f flag ) panicNotMap () {
f .mustBe (Map )
}
The pages are generated with Golds v0.7.3 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .