// 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 implements Go's builtin map type. package maps import ( "internal/abi" "unsafe" ) const debugLog = false func (t *table) checkInvariants(typ *abi.SwissMapType, m *Map) { if !debugLog { return } // For every non-empty slot, verify we can retrieve the key using Get. // Count the number of used and deleted slots. var used uint16 var deleted uint16 var empty uint16 for i := uint64(0); i <= t.groups.lengthMask; i++ { g := t.groups.group(typ, i) for j := uintptr(0); j < abi.SwissMapGroupSlots; j++ { c := g.ctrls().get(j) switch { case c == ctrlDeleted: deleted++ case c == ctrlEmpty: empty++ default: used++ key := g.key(typ, j) if typ.IndirectKey() { key = *((*unsafe.Pointer)(key)) } // Can't lookup keys that don't compare equal // to themselves (e.g., NaN). if !typ.Key.Equal(key, key) { continue } if _, ok := t.Get(typ, m, key); !ok { hash := typ.Hasher(key, m.seed) print("invariant failed: slot(", i, "/", j, "): key ") dump(key, typ.Key.Size_) print(" not found [hash=", hash, ", h2=", h2(hash), " h1=", h1(hash), "]\n") t.Print(typ, m) panic("invariant failed: slot: key not found") } } } } if used != t.used { print("invariant failed: found ", used, " used slots, but used count is ", t.used, "\n") t.Print(typ, m) panic("invariant failed: found mismatched used slot count") } growthLeft := (t.capacity*maxAvgGroupLoad)/abi.SwissMapGroupSlots - t.used - deleted if growthLeft != t.growthLeft { print("invariant failed: found ", t.growthLeft, " growthLeft, but expected ", growthLeft, "\n") t.Print(typ, m) panic("invariant failed: found mismatched growthLeft") } if deleted != t.tombstones() { print("invariant failed: found ", deleted, " tombstones, but expected ", t.tombstones(), "\n") t.Print(typ, m) panic("invariant failed: found mismatched tombstones") } if empty == 0 { print("invariant failed: found no empty slots (violates probe invariant)\n") t.Print(typ, m) panic("invariant failed: found no empty slots (violates probe invariant)") } } func (t *table) Print(typ *abi.SwissMapType, m *Map) { print(`table{ index: `, t.index, ` localDepth: `, t.localDepth, ` capacity: `, t.capacity, ` used: `, t.used, ` growthLeft: `, t.growthLeft, ` groups: `) for i := uint64(0); i <= t.groups.lengthMask; i++ { print("\t\tgroup ", i, "\n") g := t.groups.group(typ, i) ctrls := g.ctrls() for j := uintptr(0); j < abi.SwissMapGroupSlots; j++ { print("\t\t\tslot ", j, "\n") c := ctrls.get(j) print("\t\t\t\tctrl ", c) switch c { case ctrlEmpty: print(" (empty)\n") case ctrlDeleted: print(" (deleted)\n") default: print("\n") } print("\t\t\t\tkey ") dump(g.key(typ, j), typ.Key.Size_) println("") print("\t\t\t\telem ") dump(g.elem(typ, j), typ.Elem.Size_) println("") } } } // TODO(prattmic): not in hex because print doesn't have a way to print in hex // outside the runtime. func dump(ptr unsafe.Pointer, size uintptr) { for size > 0 { print(*(*byte)(ptr), " ") ptr = unsafe.Pointer(uintptr(ptr) + 1) size-- } }