[
  {
    "path": "BENCH.md",
    "content": "# Performance\n\nWhile this implementation was designed with performance in mind, it's not\nnecessarily better, faster, smarter, or sexier than the built-in Go hashmap.\n\nHere's a very rough comparison.\n\nThe following benchmarks were run on a Linux Desktop (3.8 GHz 8-Core AMD Ryzen 7 5800X) using Go version 1.19. The key types are either strings or ints and the values are always ints.\n\nIn all cases the maps start from zero capacity, like:\n\n```go\nm := make(map[string]int)      // go stdlib\nvar m hashmap.Map[string, int] // this package\n```\n\n```shell\nMAPBENCH=100000 go test\n```\n\n## 100,000 random string keys\n\n```shell\n## STRING KEYS\n\n-- tidwall --\nset          100,000 ops     15ms      6,535,956/sec \nget          100,000 ops      7ms     15,009,648/sec \nreset        100,000 ops      5ms     20,811,745/sec \nscan              20 ops      8ms          2,539/sec \ndelete       100,000 ops      7ms     14,557,933/sec \nmemory     4,194,288 bytes                  41/entry \n\n-- stdlib --\nset          100,000 ops     17ms      5,892,223/sec \nget          100,000 ops      8ms     12,148,359/sec \nreset        100,000 ops      4ms     24,779,419/sec \nscan              20 ops     14ms          1,395/sec \ndelete       100,000 ops      8ms     11,915,708/sec \nmemory     3,966,288 bytes                  39/entry\n```\n\n## 100,000 random int keys\n\n```shell\n## INT KEYS\n\n-- tidwall --\nset          100,000 ops      8ms     12,573,069/sec \nget          100,000 ops      4ms     24,821,181/sec \nreset        100,000 ops      4ms     25,324,412/sec \nscan              20 ops      8ms          2,430/sec \ndelete       100,000 ops      5ms     22,156,034/sec \nmemory     3,143,352 bytes                  31/entry \n\n-- stdlib --\nset          100,000 ops      7ms     13,547,121/sec \nget          100,000 ops      4ms     26,458,302/sec \nreset        100,000 ops      4ms     28,379,163/sec \nscan              20 ops     16ms          1,214/sec \ndelete       100,000 ops      4ms     24,771,495/sec \nmemory     2,784,264 bytes                  27/entry \n```\n\n## 1,000,000 random string keys\n\n```shell\n## STRING KEYS\n\n-- tidwall --\nset        1,000,000 ops    217ms      4,607,387/sec \nget        1,000,000 ops    127ms      7,872,817/sec \nreset      1,000,000 ops    130ms      7,709,027/sec \nscan              20 ops    136ms            147/sec \ndelete     1,000,000 ops    149ms      6,716,045/sec \nmemory    67,108,848 bytes                  67/entry \n\n-- stdlib --\nset        1,000,000 ops    325ms      3,078,132/sec \nget        1,000,000 ops    122ms      8,217,771/sec \nreset      1,000,000 ops    133ms      7,510,273/sec \nscan              20 ops    163ms            122/sec \ndelete     1,000,000 ops    148ms      6,761,332/sec \nmemory    57,931,472 bytes                  57/entry\n```\n\n## 1,000,000 random int keys\n\n```shell\n## INT KEYS\n\n-- tidwall --\nset        1,000,000 ops    101ms      9,901,395/sec \nget        1,000,000 ops     63ms     15,928,770/sec \nreset      1,000,000 ops     66ms     15,107,262/sec \nscan              20 ops    139ms            144/sec \ndelete     1,000,000 ops     66ms     15,216,322/sec \nmemory    50,329,272 bytes                  50/entry \n\n-- stdlib --\nset        1,000,000 ops    119ms      8,431,961/sec \nget        1,000,000 ops     61ms     16,376,595/sec \nreset      1,000,000 ops     59ms     17,032,395/sec \nscan              20 ops    153ms            130/sec \ndelete     1,000,000 ops     67ms     15,026,654/sec \nmemory    40,146,760 bytes                  40/entry \n```\n\n## 10,000,000 random string keys (int values)\n\n```shell\n## STRING KEYS\n\n-- tidwall --\nset       10,000,000 ops   2584ms      3,869,389/sec \nget       10,000,000 ops   1418ms      7,051,328/sec \nreset     10,000,000 ops   1469ms      6,807,487/sec \nscan              20 ops   1049ms             19/sec \ndelete    10,000,000 ops   1694ms      5,901,787/sec \nmemory   536,870,896 bytes                  53/entry \n\n-- stdlib --\nset       10,000,000 ops   3771ms      2,651,828/sec \nget       10,000,000 ops   1494ms      6,695,021/sec \nreset     10,000,000 ops   1480ms      6,758,881/sec \nscan              20 ops   1855ms             10/sec \ndelete    10,000,000 ops   1629ms      6,138,209/sec \nmemory   463,468,240 bytes                  46/entry\n```\n\n## 10,000,000 random int keys (int values)\n\n```shell\n## INT KEYS\n\n-- tidwall --\nset       10,000,000 ops   1428ms      7,002,173/sec \nget       10,000,000 ops    733ms     13,636,196/sec \nreset     10,000,000 ops    787ms     12,710,144/sec \nscan              20 ops   1098ms             18/sec \ndelete    10,000,000 ops    900ms     11,108,541/sec \nmemory   402,650,808 bytes                  40/entry \n\n-- stdlib --\nset       10,000,000 ops   1709ms      5,850,969/sec \nget       10,000,000 ops    797ms     12,551,221/sec \nreset     10,000,000 ops    874ms     11,437,820/sec \nscan              20 ops   1629ms             12/sec \ndelete    10,000,000 ops    910ms     10,994,436/sec \nmemory   321,976,032 bytes                  32/entry \n```\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2019, Joshua J Baker\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\nSPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\nOF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\nCONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE."
  },
  {
    "path": "README.md",
    "content": "# hashmap\n\n[![GoDoc](https://img.shields.io/badge/api-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/tidwall/hashmap)\n\nAn [efficient](BENCH.md) hashmap implementation in Go.\n\n## Features\n\n- Support for [Generics](#generics).\n- `Map` and `Set` types for unordered key-value maps and sets,\n- [xxh3 algorithm](https://github.com/zeebo/xxh3)\n- [Open addressing](https://en.wikipedia.org/wiki/Hash_table#Open_addressing) with [Robin hood hashing](https://en.wikipedia.org/wiki/Hash_table#Robin_Hood_hashing)\n- Automatically shinks memory on deletes (no memory leaks).\n- Pretty darn good performance. 🚀 ([benchmarks](BENCH.md)).\n\nFor ordered key-value data, check out the [tidwall/btree](https://github.com/tidwall/btree) package.\n\n## Getting Started\n\n### Installing\n\nTo start using `hashmap`, install Go and run `go get`:\n\n```sh\ngo get github.com/tidwall/hashmap\n```\n\nThis will retrieve the library.\n\n## Usage\n\nThe `Map` type works similar to a standard Go map, and includes the methods:\n`Set`, `Get`, `Delete`, `Len`, `Scan`, `Keys`, `Values`, and `Copy`.\n\n```go\nvar m hashmap.Map[string, string]\nm.Set(\"Hello\", \"Dolly!\")\nval, _ := m.Get(\"Hello\")\nfmt.Printf(\"%v\\n\", val)\nval, _ = m.Delete(\"Hello\")\nfmt.Printf(\"%v\\n\", val)\nval, _ = m.Get(\"Hello\")\nfmt.Printf(\"%v\\n\", val)\n\n// Output:\n// Dolly!\n// Dolly!\n//\n```\n\nThe `Set` type is like `Map` but only for keys.\nIt includes the methods: `Insert`, `Contains`, `Delete`, `Len`, `Scan` and `Keys`.\n\n```go\nvar m hashmap.Set[string]\nm.Insert(\"Andy\")\nm.Insert(\"Kate\")\nm.Insert(\"Janet\")\n\nfmt.Printf(\"%v\\n\", m.Contains(\"Kate\"))\nfmt.Printf(\"%v\\n\", m.Contains(\"Bob\"))\nfmt.Printf(\"%v\\n\", m.Contains(\"Andy\"))\n\n// Output:\n// true\n// false\n// true\n```\n\n## Performance\n\nSee [BENCH.md](BENCH.md) for more info.\n\n## Contact\n\nJosh Baker [@tidwall](http://twitter.com/tidwall)\n\n## License\n\nSource code is available under the MIT [License](LICENSE).\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/tidwall/hashmap\n\ngo 1.18\n\nrequire github.com/zeebo/xxh3 v1.0.2\n\nrequire github.com/klauspost/cpuid/v2 v2.0.9 // indirect\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=\ngithub.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=\ngithub.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=\ngithub.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=\ngithub.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=\n"
  },
  {
    "path": "map.go",
    "content": "// Copyright 2019 Joshua J Baker. All rights reserved.\n// Use of this source code is governed by an ISC-style\n// license that can be found in the LICENSE file.\n\npackage hashmap\n\nimport (\n\t\"unsafe\"\n\n\t\"github.com/zeebo/xxh3\"\n)\n\nconst (\n\tloadFactor  = 0.85                      // must be above 50%\n\tdibBitSize  = 16                        // 0xFFFF\n\thashBitSize = 64 - dibBitSize           // 0xFFFFFFFFFFFF\n\tmaxHash     = ^uint64(0) >> dibBitSize  // max 28,147,497,671,0655\n\tmaxDIB      = ^uint64(0) >> hashBitSize // max 65,535\n)\n\ntype entry[K comparable, V any] struct {\n\thdib  uint64 // bitfield { hash:48 dib:16 }\n\tvalue V      // user value\n\tkey   K      // user key\n}\n\nfunc (e *entry[K, V]) dib() int {\n\treturn int(e.hdib & maxDIB)\n}\nfunc (e *entry[K, V]) hash() int {\n\treturn int(e.hdib >> dibBitSize)\n}\nfunc (e *entry[K, V]) setDIB(dib int) {\n\te.hdib = e.hdib>>dibBitSize<<dibBitSize | uint64(dib)&maxDIB\n}\nfunc (e *entry[K, V]) setHash(hash int) {\n\te.hdib = uint64(hash)<<dibBitSize | e.hdib&maxDIB\n}\nfunc makeHDIB(hash, dib int) uint64 {\n\treturn uint64(hash)<<dibBitSize | uint64(dib)&maxDIB\n}\n\n// hash returns a 48-bit hash for 64-bit environments, or 32-bit hash for\n// 32-bit environments.\nfunc (m *Map[K, V]) hash(key K) int {\n\t// The unsafe package is used here to cast the key into a string container\n\t// so that the hasher can work. The hasher normally only accept a string or\n\t// []byte, but this effectively allows it to accept value type.\n\t// The m.kstr bool, which is set from the New function, indicates that the\n\t// key is known to already be a true string. Otherwise, a fake string is\n\t// derived by setting the string data to value of the key, and the string\n\t// length to the size of the value.\n\tvar strKey string\n\tif m.kstr {\n\t\tstrKey = *(*string)(unsafe.Pointer(&key))\n\t} else {\n\t\tstrKey = *(*string)(unsafe.Pointer(&struct {\n\t\t\tdata unsafe.Pointer\n\t\t\tlen  int\n\t\t}{unsafe.Pointer(&key), m.ksize}))\n\t}\n\t// Now for the actual hashing.\n\treturn int(xxh3.HashString(strKey) >> dibBitSize)\n}\n\n// Map is a hashmap. Like map[string]interface{}\ntype Map[K comparable, V any] struct {\n\tcap      int\n\tlength   int\n\tmask     int\n\tgrowAt   int\n\tshrinkAt int\n\tbuckets  []entry[K, V]\n\tksize    int\n\tkstr     bool\n}\n\n// New returns a new Map. Like map[string]interface{}\nfunc New[K comparable, V any](cap int) *Map[K, V] {\n\tm := new(Map[K, V])\n\tm.cap = cap\n\tsz := 8\n\tfor sz < m.cap {\n\t\tsz *= 2\n\t}\n\tif m.cap > 0 {\n\t\tm.cap = sz\n\t}\n\tm.buckets = make([]entry[K, V], sz)\n\tm.mask = len(m.buckets) - 1\n\tm.growAt = int(float64(len(m.buckets)) * loadFactor)\n\tm.shrinkAt = int(float64(len(m.buckets)) * (1 - loadFactor))\n\tm.detectHasher()\n\treturn m\n}\n\nfunc (m *Map[K, V]) detectHasher() {\n\t// Detect the key type. This is needed by the hasher.\n\tvar k K\n\tswitch ((interface{})(k)).(type) {\n\tcase string:\n\t\tm.kstr = true\n\tdefault:\n\t\tm.ksize = int(unsafe.Sizeof(k))\n\t}\n}\n\nfunc (m *Map[K, V]) resize(newCap int) {\n\tnmap := New[K, V](newCap)\n\tfor i := 0; i < len(m.buckets); i++ {\n\t\tif m.buckets[i].dib() > 0 {\n\t\t\tnmap.set(m.buckets[i].hash(), m.buckets[i].key, m.buckets[i].value)\n\t\t}\n\t}\n\tcap := m.cap\n\t*m = *nmap\n\tm.cap = cap\n}\n\n// Set assigns a value to a key.\n// Returns the previous value, or false when no value was assigned.\nfunc (m *Map[K, V]) Set(key K, value V) (V, bool) {\n\tif len(m.buckets) == 0 {\n\t\t*m = *New[K, V](0)\n\t}\n\tif m.length >= m.growAt {\n\t\tm.resize(len(m.buckets) * 2)\n\t}\n\treturn m.set(m.hash(key), key, value)\n}\n\nfunc (m *Map[K, V]) set(hash int, key K, value V) (prev V, ok bool) {\n\te := entry[K, V]{makeHDIB(hash, 1), value, key}\n\ti := e.hash() & m.mask\n\tfor {\n\t\tif m.buckets[i].dib() == 0 {\n\t\t\tm.buckets[i] = e\n\t\t\tm.length++\n\t\t\treturn prev, false\n\t\t}\n\t\tif e.hash() == m.buckets[i].hash() && e.key == m.buckets[i].key {\n\t\t\tprev = m.buckets[i].value\n\t\t\tm.buckets[i].value = e.value\n\t\t\treturn prev, true\n\t\t}\n\t\tif m.buckets[i].dib() < e.dib() {\n\t\t\te, m.buckets[i] = m.buckets[i], e\n\t\t}\n\t\ti = (i + 1) & m.mask\n\t\te.setDIB(e.dib() + 1)\n\t}\n}\n\n// Get returns a value for a key.\n// Returns false when no value has been assign for key.\nfunc (m *Map[K, V]) Get(key K) (value V, ok bool) {\n\tif len(m.buckets) == 0 {\n\t\treturn value, false\n\t}\n\thash := m.hash(key)\n\ti := hash & m.mask\n\tfor {\n\t\tif m.buckets[i].dib() == 0 {\n\t\t\treturn value, false\n\t\t}\n\t\tif m.buckets[i].hash() == hash && m.buckets[i].key == key {\n\t\t\treturn m.buckets[i].value, true\n\t\t}\n\t\ti = (i + 1) & m.mask\n\t}\n}\n\n// Len returns the number of values in map.\nfunc (m *Map[K, V]) Len() int {\n\treturn m.length\n}\n\n// Delete deletes a value for a key.\n// Returns the deleted value, or false when no value was assigned.\nfunc (m *Map[K, V]) Delete(key K) (prev V, deleted bool) {\n\tif len(m.buckets) == 0 {\n\t\treturn prev, false\n\t}\n\thash := m.hash(key)\n\ti := hash & m.mask\n\tfor {\n\t\tif m.buckets[i].dib() == 0 {\n\t\t\treturn prev, false\n\t\t}\n\t\tif m.buckets[i].hash() == hash && m.buckets[i].key == key {\n\t\t\tprev = m.buckets[i].value\n\t\t\tm.remove(i)\n\t\t\treturn prev, true\n\t\t}\n\t\ti = (i + 1) & m.mask\n\t}\n}\n\nfunc (m *Map[K, V]) remove(i int) {\n\tm.buckets[i].setDIB(0)\n\tfor {\n\t\tpi := i\n\t\ti = (i + 1) & m.mask\n\t\tif m.buckets[i].dib() <= 1 {\n\t\t\tm.buckets[pi] = entry[K, V]{}\n\t\t\tbreak\n\t\t}\n\t\tm.buckets[pi] = m.buckets[i]\n\t\tm.buckets[pi].setDIB(m.buckets[pi].dib() - 1)\n\t}\n\tm.length--\n\tif len(m.buckets) > m.cap && m.length <= m.shrinkAt {\n\t\tm.resize(m.length)\n\t}\n}\n\n// Scan iterates over all key/values.\n// It's not safe to call or Set or Delete while scanning.\nfunc (m *Map[K, V]) Scan(iter func(key K, value V) bool) {\n\tfor i := 0; i < len(m.buckets); i++ {\n\t\tif m.buckets[i].dib() > 0 {\n\t\t\tif !iter(m.buckets[i].key, m.buckets[i].value) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Keys returns all keys as a slice\nfunc (m *Map[K, V]) Keys() []K {\n\tkeys := make([]K, 0, m.length)\n\tfor i := 0; i < len(m.buckets); i++ {\n\t\tif m.buckets[i].dib() > 0 {\n\t\t\tkeys = append(keys, m.buckets[i].key)\n\t\t}\n\t}\n\treturn keys\n}\n\n// Values returns all values as a slice\nfunc (m *Map[K, V]) Values() []V {\n\tvalues := make([]V, 0, m.length)\n\tfor i := 0; i < len(m.buckets); i++ {\n\t\tif m.buckets[i].dib() > 0 {\n\t\t\tvalues = append(values, m.buckets[i].value)\n\t\t}\n\t}\n\treturn values\n}\n\n// Copy the hashmap.\nfunc (m *Map[K, V]) Copy() *Map[K, V] {\n\tm2 := new(Map[K, V])\n\t*m2 = *m\n\tm2.buckets = make([]entry[K, V], len(m.buckets))\n\tcopy(m2.buckets, m.buckets)\n\treturn m2\n}\n\n// GetPos gets a single keys/value nearby a position.\n// The pos param can be any valid uint64. Useful for grabbing a random item\n// from the map.\nfunc (m *Map[K, V]) GetPos(pos uint64) (key K, value V, ok bool) {\n\tfor i := 0; i < len(m.buckets); i++ {\n\t\tindex := (pos + uint64(i)) & uint64(m.mask)\n\t\tif m.buckets[index].dib() > 0 {\n\t\t\treturn m.buckets[index].key, m.buckets[index].value, true\n\t\t}\n\t}\n\t// Empty map\n\treturn key, value, false\n}\n"
  },
  {
    "path": "map_test.go",
    "content": "// Copyright 2019 Joshua J Baker. All rights reserved.\n// Use of this source code is governed by an ISC-style\n// license that can be found in the LICENSE file.\n\npackage hashmap\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"os\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"sort\"\n\t\"strconv\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n)\n\ntype keyT = string\ntype valueT = interface{}\n\nfunc k(key int) keyT {\n\treturn strconv.FormatInt(int64(key), 10)\n}\n\nfunc add(x keyT, delta int) int {\n\ti, err := strconv.ParseInt(x, 10, 64)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn int(i + int64(delta))\n}\n\n// /////////////////////////\nfunc random(N int, perm bool) []keyT {\n\tnums := make([]keyT, N)\n\tif perm {\n\t\tfor i, x := range rand.Perm(N) {\n\t\t\tnums[i] = k(x)\n\t\t}\n\t} else {\n\t\tm := make(map[keyT]bool)\n\t\tfor len(m) < N {\n\t\t\tm[k(int(rand.Uint64()))] = true\n\t\t}\n\t\tvar i int\n\t\tfor k := range m {\n\t\t\tnums[i] = k\n\t\t\ti++\n\t\t}\n\t}\n\treturn nums\n}\n\nfunc shuffle[K comparable](nums []K) {\n\tfor i := range nums {\n\t\tj := rand.Intn(i + 1)\n\t\tnums[i], nums[j] = nums[j], nums[i]\n\t}\n}\n\nfunc init() {\n\t//var seed int64 = 1519776033517775607\n\tseed := (time.Now().UnixNano())\n\tprintln(\"seed:\", seed)\n\trand.Seed(seed)\n}\n\ntype imap struct {\n\tm *Map[string, interface{}]\n}\n\nfunc newimap(cap int) *imap {\n\tm := new(imap)\n\tm.m = New[string, interface{}](cap)\n\treturn m\n}\n\nfunc (m *imap) Get(key string) (interface{}, bool) {\n\tif m.m == nil {\n\t\treturn nil, false\n\t}\n\treturn m.m.Get(key)\n}\nfunc (m *imap) Set(key string, value interface{}) (interface{}, bool) {\n\tif m.m == nil {\n\t\tm.m = new(Map[string, interface{}])\n\t}\n\treturn m.m.Set(key, value)\n}\nfunc (m *imap) Delete(key string) (interface{}, bool) {\n\tif m.m == nil {\n\t\treturn nil, false\n\t}\n\treturn m.m.Delete(key)\n}\nfunc (m *imap) Len() int {\n\tif m.m == nil {\n\t\treturn 0\n\t}\n\treturn m.m.Len()\n}\nfunc (m *imap) Scan(iter func(key string, value interface{}) bool) {\n\tif m.m == nil {\n\t\treturn\n\t}\n\tm.m.Scan(iter)\n}\n\nfunc TestRandomData(t *testing.T) {\n\tN := 10000\n\tstart := time.Now()\n\tfor time.Since(start) < time.Second*2 {\n\t\tnums := random(N, true)\n\t\tvar m *imap\n\t\tswitch rand.Int() % 5 {\n\t\tdefault:\n\t\t\tm = newimap(N / ((rand.Int() % 3) + 1))\n\t\tcase 1:\n\t\t\tm = new(imap)\n\t\tcase 2:\n\t\t\tm = newimap(0)\n\t\t}\n\t\tv, ok := m.Get(k(999))\n\t\tif ok || v != nil {\n\t\t\tt.Fatalf(\"expected %v, got %v\", nil, v)\n\t\t}\n\t\tv, ok = m.Delete(k(999))\n\t\tif ok || v != nil {\n\t\t\tt.Fatalf(\"expected %v, got %v\", nil, v)\n\t\t}\n\t\tif m.Len() != 0 {\n\t\t\tt.Fatalf(\"expected %v, got %v\", 0, m.Len())\n\t\t}\n\t\t// set a bunch of items\n\t\tfor i := 0; i < len(nums); i++ {\n\t\t\tv, ok := m.Set(nums[i], nums[i])\n\t\t\tif ok || v != nil {\n\t\t\t\tt.Fatalf(\"expected %v, got %v\", nil, v)\n\t\t\t}\n\t\t}\n\t\tif m.Len() != N {\n\t\t\tt.Fatalf(\"expected %v, got %v\", N, m.Len())\n\t\t}\n\t\t// retrieve all the items\n\t\tshuffle(nums)\n\t\tfor i := 0; i < len(nums); i++ {\n\t\t\tv, ok := m.Get(nums[i])\n\t\t\tif !ok || v == nil || v != nums[i] {\n\t\t\t\tt.Fatalf(\"expected %v, got %v\", nums[i], v)\n\t\t\t}\n\t\t}\n\t\t// replace all the items\n\t\tshuffle(nums)\n\t\tfor i := 0; i < len(nums); i++ {\n\t\t\tv, ok := m.Set(nums[i], add(nums[i], 1))\n\t\t\tif !ok || v != nums[i] {\n\t\t\t\tt.Fatalf(\"expected %v, got %v\", nums[i], v)\n\t\t\t}\n\t\t}\n\t\tif m.Len() != N {\n\t\t\tt.Fatalf(\"expected %v, got %v\", N, m.Len())\n\t\t}\n\t\t// retrieve all the items\n\t\tshuffle(nums)\n\t\tfor i := 0; i < len(nums); i++ {\n\t\t\tv, ok := m.Get(nums[i])\n\t\t\tif !ok || v != add(nums[i], 1) {\n\t\t\t\tt.Fatalf(\"expected %v, got %v\", add(nums[i], 1), v)\n\t\t\t}\n\t\t}\n\t\t// remove half the items\n\t\tshuffle(nums)\n\t\tfor i := 0; i < len(nums)/2; i++ {\n\t\t\tv, ok := m.Delete(nums[i])\n\t\t\tif !ok || v != add(nums[i], 1) {\n\t\t\t\tt.Fatalf(\"expected %v, got %v\", add(nums[i], 1), v)\n\t\t\t}\n\t\t}\n\t\tif m.Len() != N/2 {\n\t\t\tt.Fatalf(\"expected %v, got %v\", N/2, m.Len())\n\t\t}\n\t\t// check to make sure that the items have been removed\n\t\tfor i := 0; i < len(nums)/2; i++ {\n\t\t\tv, ok := m.Get(nums[i])\n\t\t\tif ok || v != nil {\n\t\t\t\tt.Fatalf(\"expected %v, got %v\", nil, v)\n\t\t\t}\n\t\t}\n\t\t// check the second half of the items\n\t\tfor i := len(nums) / 2; i < len(nums); i++ {\n\t\t\tv, ok := m.Get(nums[i])\n\t\t\tif !ok || v != add(nums[i], 1) {\n\t\t\t\tt.Fatalf(\"expected %v, got %v\", add(nums[i], 1), v)\n\t\t\t}\n\t\t}\n\t\t// try to delete again, make sure they don't exist\n\t\tfor i := 0; i < len(nums)/2; i++ {\n\t\t\tv, ok := m.Delete(nums[i])\n\t\t\tif ok || v != nil {\n\t\t\t\tt.Fatalf(\"expected %v, got %v\", nil, v)\n\t\t\t}\n\t\t}\n\t\tif m.Len() != N/2 {\n\t\t\tt.Fatalf(\"expected %v, got %v\", N/2, m.Len())\n\t\t}\n\t\tm.Scan(func(key keyT, value valueT) bool {\n\t\t\tif value != add(key, 1) {\n\t\t\t\tt.Fatalf(\"expected %v, got %v\", add(key, 1), value)\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\t\tvar n int\n\t\tm.Scan(func(key keyT, value valueT) bool {\n\t\t\tn++\n\t\t\treturn false\n\t\t})\n\t\tif n != 1 {\n\t\t\tt.Fatalf(\"expected %v, got %v\", 1, n)\n\t\t}\n\t\tfor i := len(nums) / 2; i < len(nums); i++ {\n\t\t\tv, ok := m.Delete(nums[i])\n\t\t\tif !ok || v != add(nums[i], 1) {\n\t\t\t\tt.Fatalf(\"expected %v, got %v\", add(nums[i], 1), v)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestBench(t *testing.T) {\n\tN, _ := strconv.ParseUint(os.Getenv(\"MAPBENCH\"), 10, 64)\n\tif N == 0 {\n\t\tfmt.Printf(\"Enable benchmarks with MAPBENCH=1000000\\n\")\n\t\treturn\n\t}\n\n\tvar pnums []int\n\tfor i := 0; i < int(N); i++ {\n\t\tpnums = append(pnums, i)\n\t}\n\n\t{\n\t\tfmt.Printf(\"\\n## STRING KEYS\\n\\n\")\n\t\tnums := random(int(N), false)\n\t\tt.Run(\"Tidwall\", func(t *testing.T) {\n\t\t\ttestPerf(nums, pnums, \"tidwall\")\n\t\t})\n\t\tt.Run(\"Stdlib\", func(t *testing.T) {\n\t\t\ttestPerf(nums, pnums, \"stdlib\")\n\t\t})\n\t}\n\t{\n\t\tfmt.Printf(\"\\n## INT KEYS\\n\\n\")\n\t\tnums := rand.Perm(int(N))\n\t\tt.Run(\"Tidwall\", func(t *testing.T) {\n\t\t\ttestPerf(nums, pnums, \"tidwall\")\n\t\t})\n\t\tt.Run(\"Stdlib\", func(t *testing.T) {\n\t\t\ttestPerf(nums, pnums, \"stdlib\")\n\t\t})\n\t}\n\n}\n\nfunc printItem(s string, size int, dir int) {\n\tfor len(s) < size {\n\t\tif dir == -1 {\n\t\t\ts += \" \"\n\t\t} else {\n\t\t\ts = \" \" + s\n\t\t}\n\t}\n\tfmt.Printf(\"%s \", s)\n}\n\nfunc testPerf[K comparable, V any](nums []K, pnums []V, which string) {\n\tvar ms1, ms2 runtime.MemStats\n\tinitSize := 0 //len(nums) * 2\n\tdefer func() {\n\t\theapBytes := int(ms2.HeapAlloc - ms1.HeapAlloc)\n\t\tfmt.Printf(\"memory %13s bytes %19s/entry \\n\",\n\t\t\tcommaize(heapBytes), commaize(heapBytes/len(nums)))\n\t\tfmt.Printf(\"\\n\")\n\t}()\n\truntime.GC()\n\ttime.Sleep(time.Millisecond * 100)\n\truntime.ReadMemStats(&ms1)\n\n\tvar setop, getop, delop func(int, int)\n\tvar scnop func()\n\tswitch which {\n\tcase \"stdlib\":\n\t\tm := make(map[K]V, initSize)\n\t\tsetop = func(i, _ int) { m[nums[i]] = pnums[i] }\n\t\tgetop = func(i, _ int) { _ = m[nums[i]] }\n\t\tdelop = func(i, _ int) { delete(m, nums[i]) }\n\t\tscnop = func() {\n\t\t\tfor range m {\n\t\t\t}\n\t\t}\n\tcase \"tidwall\":\n\t\tvar m Map[K, V]\n\t\tsetop = func(i, _ int) { m.Set(nums[i], pnums[i]) }\n\t\tgetop = func(i, _ int) { m.Get(nums[i]) }\n\t\tdelop = func(i, _ int) { m.Delete(nums[i]) }\n\t\tscnop = func() {\n\t\t\tm.Scan(func(key K, value V) bool {\n\t\t\t\treturn true\n\t\t\t})\n\t\t}\n\t}\n\tfmt.Printf(\"-- %s --\", which)\n\tfmt.Printf(\"\\n\")\n\n\tops := []func(int, int){setop, getop, setop, nil, delop}\n\ttags := []string{\"set\", \"get\", \"reset\", \"scan\", \"delete\"}\n\tfor i := range ops {\n\t\tshuffle(nums)\n\t\tvar na bool\n\t\tvar n int\n\t\tstart := time.Now()\n\t\tif tags[i] == \"scan\" {\n\t\t\top := scnop\n\t\t\tif op == nil {\n\t\t\t\tna = true\n\t\t\t} else {\n\t\t\t\tn = 20\n\t\t\t\tfor i := 0; i < n; i++ {\n\t\t\t\t\top()\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tn = len(nums)\n\t\t\tfor j := 0; j < n; j++ {\n\t\t\t\tops[i](j, 1)\n\t\t\t}\n\t\t}\n\t\tdur := time.Since(start)\n\t\tif i == 0 {\n\t\t\truntime.GC()\n\t\t\ttime.Sleep(time.Millisecond * 100)\n\t\t\truntime.ReadMemStats(&ms2)\n\t\t}\n\t\tprintItem(tags[i], 9, -1)\n\t\tif na {\n\t\t\tprintItem(\"-- unavailable --\", 14, 1)\n\t\t} else {\n\t\t\tif n == -1 {\n\t\t\t\tprintItem(\"unknown ops\", 14, 1)\n\t\t\t} else {\n\t\t\t\tprintItem(fmt.Sprintf(\"%s ops\", commaize(n)), 14, 1)\n\t\t\t}\n\t\t\tprintItem(fmt.Sprintf(\"%.0fms\", dur.Seconds()*1000), 8, 1)\n\t\t\tif n != -1 {\n\t\t\t\tprintItem(fmt.Sprintf(\"%s/sec\", commaize(int(float64(n)/dur.Seconds()))), 18, 1)\n\t\t\t}\n\t\t}\n\t\tfmt.Printf(\"\\n\")\n\t}\n}\n\nfunc commaize(n int) string {\n\ts1, s2 := fmt.Sprintf(\"%d\", n), \"\"\n\tfor i, j := len(s1)-1, 0; i >= 0; i, j = i-1, j+1 {\n\t\tif j%3 == 0 && j != 0 {\n\t\t\ts2 = \",\" + s2\n\t\t}\n\t\ts2 = string(s1[i]) + s2\n\t}\n\treturn s2\n}\n\nfunc TestHashDIB(t *testing.T) {\n\tvar e entry[string, interface{}]\n\te.setDIB(100)\n\te.setHash(90000)\n\tif e.dib() != 100 {\n\t\tt.Fatalf(\"expected %v, got %v\", 100, e.dib())\n\t}\n\tif e.hash() != 90000 {\n\t\tt.Fatalf(\"expected %v, got %v\", 90000, e.hash())\n\t}\n}\n\nfunc TestIntInt(t *testing.T) {\n\tvar m Map[int, int]\n\n\tkeys := rand.Perm(1000000)\n\n\tfor i := 0; i < len(keys); i++ {\n\t\t_, ok := m.Set(keys[i], keys[i]*10)\n\t\tif ok {\n\t\t\tt.Fatalf(\"expected false\")\n\t\t}\n\t\tif m.Len() != i+1 {\n\t\t\tt.Fatalf(\"expected %d got %d\", i+1, m.Len())\n\t\t}\n\t}\n\n\tfor i := 0; i < len(keys); i++ {\n\t\tv, ok := m.Get(keys[i])\n\t\tif !ok {\n\t\t\tt.Fatalf(\"expected true\")\n\t\t}\n\t\tif v != keys[i]*10 {\n\t\t\tt.Fatalf(\"expected %d got %d\", keys[i]*10, v)\n\t\t}\n\t}\n\n\tfor i := 0; i < len(keys); i++ {\n\t\tv, ok := m.Delete(keys[i])\n\t\tif !ok {\n\t\t\tt.Fatalf(\"expected true\")\n\t\t}\n\t\tif v != keys[i]*10 {\n\t\t\tt.Fatalf(\"expected %d got %d\", keys[i]*10, v)\n\t\t}\n\t\tif m.Len() != len(keys)-i-1 {\n\t\t\tt.Fatalf(\"expected %d got %d\", len(keys)-i-1, m.Len())\n\t\t}\n\t}\n}\n\nfunc TestMapValues(t *testing.T) {\n\tvar m Map[int, int]\n\tm.Set(1, 2)\n\texpect := []int{2}\n\tgot := m.Values()\n\tif !reflect.DeepEqual(got, expect) {\n\t\tt.Fatal(\"expected Values equal\")\n\t}\n}\n\nfunc copyMapEntries(m *Map[int, int]) []entry[int, int] {\n\tall := make([]entry[int, int], m.Len())\n\tkeys := m.Keys()\n\tvals := m.Values()\n\tfor i := 0; i < len(keys); i++ {\n\t\tall[i].key = keys[i]\n\t\tall[i].value = vals[i]\n\t}\n\tsort.Slice(all, func(i, j int) bool {\n\t\treturn all[i].key < all[j].key\n\t})\n\treturn all\n}\n\nfunc mapEntriesEqual(a, b []entry[int, int]) bool {\n\treturn reflect.DeepEqual(a, b)\n}\n\nfunc copyMapTest(N int, m1 *Map[int, int], e11 []entry[int, int], deep bool) {\n\te12 := copyMapEntries(m1)\n\tif !mapEntriesEqual(e11, e12) {\n\t\tpanic(\"!\")\n\t}\n\n\t// Make a copy and compare the values\n\tm2 := m1.Copy()\n\te21 := copyMapEntries(m1)\n\tif !mapEntriesEqual(e21, e12) {\n\t\tpanic(\"!\")\n\t}\n\n\t// Delete every other key\n\tvar e22 []entry[int, int]\n\tfor i, j := range rand.Perm(N) {\n\t\tif i&1 == 0 {\n\t\t\te22 = append(e22, e21[j])\n\t\t} else {\n\t\t\tprev, deleted := m2.Delete(e21[j].key)\n\t\t\tif !deleted {\n\t\t\t\tpanic(\"!\")\n\t\t\t}\n\t\t\tif prev != e21[j].value {\n\n\t\t\t\tpanic(\"!\")\n\t\t\t}\n\t\t}\n\t}\n\tif m2.Len() != N/2 {\n\t\tpanic(\"!\")\n\t}\n\tsort.Slice(e22, func(i, j int) bool {\n\t\treturn e22[i].key < e22[j].key\n\t})\n\te23 := copyMapEntries(m2)\n\tif !mapEntriesEqual(e23, e22) {\n\t\tpanic(\"!\")\n\t}\n\tif !deep {\n\t\tvar wg sync.WaitGroup\n\t\twg.Add(2)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tcopyMapTest(N/2, m2, e23, true)\n\t\t}()\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tcopyMapTest(N/2, m2, e23, true)\n\t\t}()\n\t\twg.Wait()\n\t}\n\te24 := copyMapEntries(m2)\n\tif !mapEntriesEqual(e24, e23) {\n\t\tpanic(\"!\")\n\t}\n}\n\nfunc TestMapCopy(t *testing.T) {\n\tN := 1_000\n\t// create the initial map\n\tm1 := New[int, int](0)\n\tfor m1.Len() < N {\n\t\tm1.Set(rand.Int(), rand.Int())\n\t}\n\te11 := copyMapEntries(m1)\n\tdur := time.Second * 2\n\tvar wg sync.WaitGroup\n\tfor i := 0; i < 16; i++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tstart := time.Now()\n\t\t\tfor time.Since(start) < dur {\n\t\t\t\tcopyMapTest(N, m1, e11, false)\n\t\t\t}\n\t\t}()\n\t}\n\twg.Wait()\n\te12 := copyMapEntries(m1)\n\tif !mapEntriesEqual(e11, e12) {\n\t\tpanic(\"!\")\n\t}\n}\n\nfunc TestEmpty(t *testing.T) {\n\tvar m Map[int, int]\n\tif _, ok := m.Get(0); ok {\n\t\tt.Fatal()\n\t}\n\tif _, ok := m.Delete(0); ok {\n\t\tt.Fatal()\n\t}\n}\n\nfunc TestGetPos(t *testing.T) {\n\tvar m Map[int, int]\n\tif _, _, ok := m.GetPos(100); ok {\n\t\tt.Fatal()\n\t}\n\tfor i := 0; i < 1000; i++ {\n\t\tm.Set(i, i+1)\n\t}\n\tm2 := make(map[int]int)\n\tfor i := 0; i < 10000; i++ {\n\t\tkey, val, ok := m.GetPos(uint64(i))\n\t\tif !ok {\n\t\t\tt.Fatal()\n\t\t}\n\t\tm2[key] = val\n\t}\n\tif len(m2) != m.Len() {\n\t\tt.Fatal()\n\t}\n}\n\nfunc TestIssue3(t *testing.T) {\n\tm := New[string, int](50)\n\tm.Set(\"key:808943\", 1)\n\tm.Set(\"key:5834\", 2)\n\tm.Set(\"key:51630\", 3)\n\tm.Set(\"key:49504\", 4)\n\tm.Set(\"key:346528\", 5)\n\tm.Set(\"key:189743\", 6)\n\tm.Set(\"key:4112608\", 7)\n\tm.Set(\"key:21749\", 8)\n\tm.Set(\"key:844131\", 9)\n\tif v, _ := m.Delete(\"key:844131\"); v != 9 {\n\t\tt.Fatal()\n\t}\n\tif _, ok := m.Get(\"key:844131\"); ok {\n\t\tt.Fatal()\n\t}\n\n\tfor j := 0; j < 1000; j++ {\n\t\tm = New[string, int](50)\n\t\tkeys := make([]string, j)\n\t\tfor i := 0; i < len(keys); i++ {\n\t\t\tkeys[i] = fmt.Sprintf(\"key:%d\", i)\n\t\t\tm.Set(keys[i], i)\n\t\t}\n\t\tfor i := 0; i < len(keys); i++ {\n\t\t\tif v, _ := m.Get(keys[i]); v != i {\n\t\t\t\tt.Fatal()\n\t\t\t}\n\t\t\tif v, _ := m.Delete(keys[i]); v != i {\n\t\t\t\tt.Fatal()\n\t\t\t}\n\t\t\tif _, ok := m.Get(keys[i]); ok {\n\t\t\t\tt.Fatal()\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "set.go",
    "content": "package hashmap\n\ntype Set[K comparable] struct {\n\tbase Map[K, struct{}]\n}\n\n// Insert an item\nfunc (tr *Set[K]) Insert(key K) {\n\ttr.base.Set(key, struct{}{})\n}\n\n// Get a value for key\nfunc (tr *Set[K]) Contains(key K) bool {\n\t_, ok := tr.base.Get(key)\n\treturn ok\n}\n\n// Len returns the number of items in the tree\nfunc (tr *Set[K]) Len() int {\n\treturn tr.base.Len()\n}\n\n// Delete an item\nfunc (tr *Set[K]) Delete(key K) {\n\ttr.base.Delete(key)\n}\n\nfunc (tr *Set[K]) Scan(iter func(key K) bool) {\n\ttr.base.Scan(func(key K, value struct{}) bool {\n\t\treturn iter(key)\n\t})\n}\n\n// Keys returns all keys as a slice\nfunc (tr *Set[K]) Keys() []K {\n\treturn tr.base.Keys()\n}\n\n// Copy the set. This is a copy-on-write operation and is very fast because\n// it only performs a shadow copy.\nfunc (tr *Set[K]) Copy() *Set[K] {\n\ttr2 := new(Set[K])\n\ttr2.base = *tr.base.Copy()\n\treturn tr2\n}\n\n// GetPos gets a single keys/value nearby a position.\n// The pos param can be any valid uint64. Useful for grabbing a random item\n// from the Set.\nfunc (s *Set[K]) GetPos(pos uint64) (key K, ok bool) {\n\tkey, _, ok = s.base.GetPos(pos)\n\treturn key, ok\n}\n"
  },
  {
    "path": "set_test.go",
    "content": "package hashmap\n\nimport (\n\t\"math/rand\"\n\t\"reflect\"\n\t\"sort\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestSet(t *testing.T) {\n\tvar s Set[int]\n\n\tkeys := rand.Perm(1000000)\n\n\tfor i := 0; i < len(keys); i++ {\n\t\ts.Insert(keys[i])\n\t\tif s.Len() != i+1 {\n\t\t\tt.Fatalf(\"expected %d got %d\", i+1, s.Len())\n\t\t}\n\t}\n\n\tfor i := 0; i < len(keys); i++ {\n\t\tok := s.Contains(keys[i])\n\t\tif !ok {\n\t\t\tt.Fatalf(\"expected true\")\n\t\t}\n\t}\n\n\tvar skeys []int\n\ts.Scan(func(key int) bool {\n\t\tskeys = append(skeys, key)\n\t\treturn true\n\t})\n\tif len(skeys) != s.Len() {\n\t\tt.Fatalf(\"expected %d got %d\", len(skeys), s.Len())\n\t}\n\n\tfor i := 0; i < len(keys); i++ {\n\t\ts.Delete(keys[i])\n\t\tif s.Len() != len(keys)-i-1 {\n\t\t\tt.Fatalf(\"expected %d got %d\", len(keys)-i-1, s.Len())\n\t\t}\n\t}\n}\n\nfunc TestSetKeys(t *testing.T) {\n\tvar s Set[string]\n\ts.Insert(\"key\")\n\texpect := []string{\"key\"}\n\tgot := s.Keys()\n\tif !reflect.DeepEqual(got, expect) {\n\t\tt.Fatal(\"expected Keys equal\")\n\t}\n}\n\nfunc copySetEntries(m *Set[int]) []int {\n\tall := m.Keys()\n\tsort.Ints(all)\n\treturn all\n}\n\nfunc setEntriesEqual(a, b []int) bool {\n\treturn reflect.DeepEqual(a, b)\n}\n\nfunc copySetTest(N int, s1 *Set[int], e11 []int, deep bool) {\n\te12 := copySetEntries(s1)\n\tif !setEntriesEqual(e11, e12) {\n\t\tpanic(\"!\")\n\t}\n\n\t// Make a copy and compare the values\n\ts2 := s1.Copy()\n\te21 := copySetEntries(s1)\n\tif !setEntriesEqual(e21, e12) {\n\t\tpanic(\"!\")\n\t}\n\n\t// Delete every other key\n\tvar e22 []int\n\tfor i, j := range rand.Perm(N) {\n\t\tif i&1 == 0 {\n\n\t\t\te22 = append(e22, e21[j])\n\t\t} else {\n\t\t\ts2.Delete(e21[j])\n\t\t}\n\t}\n\n\tif s2.Len() != N/2 {\n\t\tpanic(\"!\")\n\t}\n\tsort.Ints(e22)\n\te23 := copySetEntries(s2)\n\tif !setEntriesEqual(e23, e22) {\n\t\tpanic(\"!\")\n\t}\n\tif !deep {\n\t\tvar wg sync.WaitGroup\n\t\twg.Add(2)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tcopySetTest(N/2, s2, e23, true)\n\t\t}()\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tcopySetTest(N/2, s2, e23, true)\n\t\t}()\n\t\twg.Wait()\n\t}\n\te24 := copySetEntries(s2)\n\tif !setEntriesEqual(e24, e23) {\n\t\tpanic(\"!\")\n\t}\n\n}\n\nfunc TestSetCopy(t *testing.T) {\n\tN := 1_000\n\t// create the initial map\n\n\ts1 := new(Set[int])\n\tfor s1.Len() < N {\n\t\ts1.Insert(rand.Int())\n\t}\n\te11 := copySetEntries(s1)\n\tdur := time.Second * 2\n\tvar wg sync.WaitGroup\n\tfor i := 0; i < 16; i++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tstart := time.Now()\n\t\t\tfor time.Since(start) < dur {\n\t\t\t\tcopySetTest(N, s1, e11, false)\n\t\t\t}\n\t\t}()\n\t}\n\twg.Wait()\n\te12 := copySetEntries(s1)\n\tif !setEntriesEqual(e11, e12) {\n\t\tpanic(\"!\")\n\t}\n}\n\nfunc TestSetGetPos(t *testing.T) {\n\tvar m Set[int]\n\tif _, ok := m.GetPos(100); ok {\n\t\tt.Fatal()\n\t}\n\tfor i := 0; i < 1000; i++ {\n\t\tm.Insert(i)\n\t}\n\tm2 := make(map[int]int)\n\tfor i := 0; i < 10000; i++ {\n\t\tkey, ok := m.GetPos(uint64(i))\n\t\tif !ok {\n\t\t\tt.Fatal()\n\t\t}\n\t\tm2[key] = key\n\t}\n\tif len(m2) != m.Len() {\n\t\tt.Fatal()\n\t}\n}\n"
  }
]