| // 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 ( |
| "internal/abi" |
| "internal/runtime/maps" |
| "internal/runtime/sys" |
| "unsafe" |
| ) |
| |
| 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(t *abi.SwissMapType, p unsafe.Pointer) error { |
| return mapKeyError(t, p) |
| } |
| |
| func makemap64(t *abi.SwissMapType, hint int64, m *maps.Map) *maps.Map { |
| if int64(int(hint)) != hint { |
| hint = 0 |
| } |
| return makemap(t, int(hint), m) |
| } |
| |
| // 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. |
| // |
| // makemap_small should be an internal detail, |
| // but widely used packages access it using linkname. |
| // Notable members of the hall of shame include: |
| // - github.com/bytedance/sonic |
| // |
| // Do not remove or change the type signature. |
| // See go.dev/issue/67401. |
| // |
| //go:linkname makemap_small |
| 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. |
| // |
| // makemap should be an internal detail, |
| // but widely used packages access it using linkname. |
| // Notable members of the hall of shame include: |
| // - github.com/ugorji/go/codec |
| // |
| // Do not remove or change the type signature. |
| // See go.dev/issue/67401. |
| // |
| //go:linkname makemap |
| func makemap(t *abi.SwissMapType, hint int, m *maps.Map) *maps.Map { |
| if hint < 0 { |
| hint = 0 |
| } |
| |
| return maps.NewMap(t, uintptr(hint), m, 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(t *abi.SwissMapType, m *maps.Map, key unsafe.Pointer) unsafe.Pointer |
| |
| // mapaccess2 should be an internal detail, |
| // but widely used packages access it using linkname. |
| // Notable members of the hall of shame include: |
| // - github.com/ugorji/go/codec |
| // |
| // Do not remove or change the type signature. |
| // See go.dev/issue/67401. |
| // |
| //go:linkname mapaccess2 |
| func mapaccess2(t *abi.SwissMapType, m *maps.Map, key unsafe.Pointer) (unsafe.Pointer, bool) |
| |
| func mapaccess1_fat(t *abi.SwissMapType, m *maps.Map, key, zero unsafe.Pointer) unsafe.Pointer { |
| e := mapaccess1(t, m, key) |
| if e == unsafe.Pointer(&zeroVal[0]) { |
| return zero |
| } |
| return e |
| } |
| |
| func mapaccess2_fat(t *abi.SwissMapType, m *maps.Map, key, zero unsafe.Pointer) (unsafe.Pointer, bool) { |
| e := mapaccess1(t, m, key) |
| if e == unsafe.Pointer(&zeroVal[0]) { |
| return zero, false |
| } |
| return e, true |
| } |
| |
| // mapassign is pushed from internal/runtime/maps. We could just call it, but |
| // we want to avoid one layer of call. |
| // |
| // mapassign should be an internal detail, |
| // but widely used packages access it using linkname. |
| // Notable members of the hall of shame include: |
| // - github.com/bytedance/sonic |
| // - github.com/RomiChan/protobuf |
| // - github.com/segmentio/encoding |
| // - github.com/ugorji/go/codec |
| // |
| // Do not remove or change the type signature. |
| // See go.dev/issue/67401. |
| // |
| //go:linkname mapassign |
| func mapassign(t *abi.SwissMapType, m *maps.Map, key unsafe.Pointer) unsafe.Pointer |
| |
| // mapdelete should be an internal detail, |
| // but widely used packages access it using linkname. |
| // Notable members of the hall of shame include: |
| // - github.com/ugorji/go/codec |
| // |
| // Do not remove or change the type signature. |
| // See go.dev/issue/67401. |
| // |
| //go:linkname mapdelete |
| func mapdelete(t *abi.SwissMapType, m *maps.Map, key unsafe.Pointer) { |
| if raceenabled && m != nil { |
| callerpc := sys.GetCallerPC() |
| pc := abi.FuncPCABIInternal(mapdelete) |
| racewritepc(unsafe.Pointer(m), callerpc, pc) |
| raceReadObjectPC(t.Key, key, callerpc, pc) |
| } |
| if msanenabled && m != nil { |
| msanread(key, t.Key.Size_) |
| } |
| if asanenabled && m != nil { |
| asanread(key, t.Key.Size_) |
| } |
| |
| m.Delete(t, key) |
| } |
| |
| // mapIterStart initializes the Iter struct used for ranging over maps and |
| // performs the first step of iteration. The Iter struct pointed to by 'it' is |
| // allocated on the stack by the compilers order pass or on the heap by |
| // reflect. Both need to have zeroed it since the struct contains pointers. |
| func mapIterStart(t *abi.SwissMapType, m *maps.Map, it *maps.Iter) { |
| if raceenabled && m != nil { |
| callerpc := sys.GetCallerPC() |
| racereadpc(unsafe.Pointer(m), callerpc, abi.FuncPCABIInternal(mapIterStart)) |
| } |
| |
| it.Init(t, m) |
| it.Next() |
| } |
| |
| // mapIterNext performs the next step of iteration. Afterwards, the next |
| // key/elem are in it.Key()/it.Elem(). |
| func mapIterNext(it *maps.Iter) { |
| if raceenabled { |
| callerpc := sys.GetCallerPC() |
| racereadpc(unsafe.Pointer(it.Map()), callerpc, abi.FuncPCABIInternal(mapIterNext)) |
| } |
| |
| it.Next() |
| } |
| |
| // mapclear deletes all keys from a map. |
| func mapclear(t *abi.SwissMapType, m *maps.Map) { |
| if raceenabled && m != nil { |
| callerpc := sys.GetCallerPC() |
| pc := abi.FuncPCABIInternal(mapclear) |
| racewritepc(unsafe.Pointer(m), callerpc, pc) |
| } |
| |
| m.Clear(t) |
| } |
| |
| // Reflect stubs. Called from ../reflect/asm_*.s |
| |
| // reflect_makemap is for package reflect, |
| // but widely used packages access it using linkname. |
| // Notable members of the hall of shame include: |
| // - gitee.com/quant1x/gox |
| // - github.com/modern-go/reflect2 |
| // - github.com/goccy/go-json |
| // - github.com/RomiChan/protobuf |
| // - github.com/segmentio/encoding |
| // - github.com/v2pro/plz |
| // |
| // Do not remove or change the type signature. |
| // See go.dev/issue/67401. |
| // |
| //go:linkname reflect_makemap reflect.makemap |
| func reflect_makemap(t *abi.SwissMapType, cap int) *maps.Map { |
| // Check invariants and reflects math. |
| if t.Key.Equal == nil { |
| throw("runtime.reflect_makemap: unsupported map key type") |
| } |
| // TODO: other checks |
| |
| return makemap(t, cap, nil) |
| } |
| |
| // reflect_mapaccess is for package reflect, |
| // but widely used packages access it using linkname. |
| // Notable members of the hall of shame include: |
| // - gitee.com/quant1x/gox |
| // - github.com/modern-go/reflect2 |
| // - github.com/v2pro/plz |
| // |
| // Do not remove or change the type signature. |
| // See go.dev/issue/67401. |
| // |
| //go:linkname reflect_mapaccess reflect.mapaccess |
| func reflect_mapaccess(t *abi.SwissMapType, m *maps.Map, key unsafe.Pointer) unsafe.Pointer { |
| elem, ok := mapaccess2(t, m, key) |
| if !ok { |
| // reflect wants nil for a missing element |
| elem = nil |
| } |
| return elem |
| } |
| |
| //go:linkname reflect_mapaccess_faststr reflect.mapaccess_faststr |
| func reflect_mapaccess_faststr(t *abi.SwissMapType, m *maps.Map, key string) unsafe.Pointer { |
| elem, ok := mapaccess2_faststr(t, m, key) |
| if !ok { |
| // reflect wants nil for a missing element |
| elem = nil |
| } |
| return elem |
| } |
| |
| // reflect_mapassign is for package reflect, |
| // but widely used packages access it using linkname. |
| // Notable members of the hall of shame include: |
| // - gitee.com/quant1x/gox |
| // - github.com/v2pro/plz |
| // |
| // Do not remove or change the type signature. |
| // |
| //go:linkname reflect_mapassign reflect.mapassign0 |
| func reflect_mapassign(t *abi.SwissMapType, m *maps.Map, key unsafe.Pointer, elem unsafe.Pointer) { |
| p := mapassign(t, m, key) |
| typedmemmove(t.Elem, p, elem) |
| } |
| |
| //go:linkname reflect_mapassign_faststr reflect.mapassign_faststr0 |
| func reflect_mapassign_faststr(t *abi.SwissMapType, m *maps.Map, key string, elem unsafe.Pointer) { |
| p := mapassign_faststr(t, m, key) |
| typedmemmove(t.Elem, p, elem) |
| } |
| |
| //go:linkname reflect_mapdelete reflect.mapdelete |
| func reflect_mapdelete(t *abi.SwissMapType, m *maps.Map, key unsafe.Pointer) { |
| mapdelete(t, m, key) |
| } |
| |
| //go:linkname reflect_mapdelete_faststr reflect.mapdelete_faststr |
| func reflect_mapdelete_faststr(t *abi.SwissMapType, m *maps.Map, key string) { |
| mapdelete_faststr(t, m, key) |
| } |
| |
| // reflect_maplen is for package reflect, |
| // but widely used packages access it using linkname. |
| // Notable members of the hall of shame include: |
| // - github.com/goccy/go-json |
| // - github.com/wI2L/jettison |
| // |
| // Do not remove or change the type signature. |
| // See go.dev/issue/67401. |
| // |
| //go:linkname reflect_maplen reflect.maplen |
| func reflect_maplen(m *maps.Map) int { |
| if m == nil { |
| return 0 |
| } |
| if raceenabled { |
| callerpc := sys.GetCallerPC() |
| racereadpc(unsafe.Pointer(m), callerpc, abi.FuncPCABIInternal(reflect_maplen)) |
| } |
| return int(m.Used()) |
| } |
| |
| //go:linkname reflect_mapclear reflect.mapclear |
| func reflect_mapclear(t *abi.SwissMapType, m *maps.Map) { |
| mapclear(t, m) |
| } |
| |
| //go:linkname reflectlite_maplen internal/reflectlite.maplen |
| func reflectlite_maplen(m *maps.Map) int { |
| if m == nil { |
| return 0 |
| } |
| if raceenabled { |
| callerpc := sys.GetCallerPC() |
| racereadpc(unsafe.Pointer(m), callerpc, abi.FuncPCABIInternal(reflect_maplen)) |
| } |
| return int(m.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(m any) any { |
| e := efaceOf(&m) |
| e.data = unsafe.Pointer(mapclone2((*abi.SwissMapType)(unsafe.Pointer(e._type)), (*maps.Map)(e.data))) |
| return m |
| } |
| |
| func mapclone2(t *abi.SwissMapType, src *maps.Map) *maps.Map { |
| dst := makemap(t, int(src.Used()), nil) |
| |
| var iter maps.Iter |
| iter.Init(t, src) |
| for iter.Next(); iter.Key() != nil; iter.Next() { |
| dst.Put(t, iter.Key(), iter.Elem()) |
| } |
| |
| return dst |
| } |
| |
| // keys for implementing maps.keys |
| // |
| //go:linkname keys maps.keys |
| func keys(m any, p unsafe.Pointer) { |
| // Currently unused in the maps package. |
| panic("unimplemented") |
| } |
| |
| // values for implementing maps.values |
| // |
| //go:linkname values maps.values |
| func values(m any, p unsafe.Pointer) { |
| // Currently unused in the maps package. |
| panic("unimplemented") |
| } |