{"id":15294906,"url":"https://github.com/joetifa2003/mm-go","last_synced_at":"2025-04-05T04:13:00.005Z","repository":{"id":64123951,"uuid":"572676614","full_name":"joetifa2003/mm-go","owner":"joetifa2003","description":"Generic manual memory management for golang","archived":false,"fork":false,"pushed_at":"2025-01-05T23:11:06.000Z","size":272,"stargazers_count":162,"open_issues_count":1,"forks_count":6,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-29T03:09:10.260Z","etag":null,"topics":["golang","malloc","memory-management"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/joetifa2003.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-11-30T19:40:41.000Z","updated_at":"2025-03-27T01:46:06.000Z","dependencies_parsed_at":"2024-03-28T19:26:34.275Z","dependency_job_id":"2a9cfd3f-ca73-4038-9367-689eef570c18","html_url":"https://github.com/joetifa2003/mm-go","commit_stats":{"total_commits":118,"total_committers":3,"mean_commits":"39.333333333333336","dds":0.3305084745762712,"last_synced_commit":"af943ad73b893bdf7cffeed7dfc5d96faef07e2b"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joetifa2003%2Fmm-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joetifa2003%2Fmm-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joetifa2003%2Fmm-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joetifa2003%2Fmm-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/joetifa2003","download_url":"https://codeload.github.com/joetifa2003/mm-go/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247284951,"owners_count":20913704,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["golang","malloc","memory-management"],"created_at":"2024-09-30T17:07:51.089Z","updated_at":"2025-04-05T04:12:59.983Z","avatar_url":"https://github.com/joetifa2003.png","language":"Go","funding_links":[],"categories":["Performance","Recently Updated","性能","Embedded Systems"],"sub_categories":["HTTP Clients","[Oct 01, 2024](/content/2024/10/01/README.md)","HTTP客户端","General use"],"readme":"[![GoReportCard example](https://goreportcard.com/badge/github.com/joetifa2003/mm-go)](https://goreportcard.com/report/github.com/joetifa2003/mm-go)\n[![GoDoc reference example](https://img.shields.io/badge/godoc-reference-blue.svg)](https://pkg.go.dev/github.com/joetifa2003/mm-go)\n\n# mm-go Generic manual memory management for golang\n\nGolang manages memory via GC and it's good for almost every use case but sometimes it can be a bottleneck.\nand this is where mm-go comes in to play.\n\n## Before using mm-go\n\n-   Golang doesn't have any way to manually allocate/free memory, so how does mm-go allocate/free?\n    It does so via **cgo**.\n-   Before considering using this try to optimize your program to use less pointers, as golang GC most of the time performs worse when there is a lot of pointers, if you can't use this lib.\n-   Manual memory management provides better performance (most of the time) but you are **100% responsible** for managing it (bugs, segfaults, use after free, double free, ....)\n-   **Don't mix** Manually and Managed memory (example if you put a slice in a manually managed struct it will get collected because go GC doesn't see the manually allocated struct, use Vector instead)\n-   All data structures provided by the package are manually managed and thus can be safely included in manually managed structs without the GC freeing them, but **you have to free them yourself!**\n-   Try to minimize calls to cgo by preallocating (using batchallocator/Arena/AllocMany).\n-   Check the docs, test files and read the README.\n\n## Star History\n\n[![Star History Chart](https://api.star-history.com/svg?repos=joetifa2003/mm-go\u0026type=Date)](https://star-history.com/#joetifa2003/mm-go\u0026Date)\n\n## Installing\n\n```\ngo get -u github.com/joetifa2003/mm-go\n```\n\n## At a glance\n\n```go\ntype MyStruct struct {\n\ta int\n\tb float32\n}\n\nfunc Example_datastructures() {\n\talloc := allocator.NewC()\n\tdefer alloc.Destroy() \n\n\tp := allocator.Alloc[MyStruct](alloc)\n\tdefer allocator.Free(alloc, p)\n\n\tp.a = 100\n\tp.b = 200\n\n\tfmt.Println(*p)\n\n\tv := vector.New[int](alloc)\n\tdefer v.Free()\n\tv.Push(15)\n\tv.Push(70)\n\n\tfor _, i := range v.Iter() {\n\t\tfmt.Println(i)\n\t}\n\n\tl := linkedlist.New[*mmstring.MMString](alloc)\n\tdefer l.Free()\n\tl.PushBack(mmstring.From(alloc, \"hello\"))\n\tl.PushBack(mmstring.From(alloc, \"world\"))\n\n\tfor _, i := range l.Iter() {\n\t\tfmt.Println(i.GetGoString())\n\t}\n\n\t// Output:\n\t// {100 200}\n\t// 15\n\t// 70\n\t// hello\n\t// world\n}\n```\n\n`mm-go` is built around the concept of Allocators, which is an interface that can be implemented and passed around to the library.\nYou use these allocators to allocate memory, and also allocate datastructures like vectors, linkedlists, hashmaps, etc.\n\n## Benchmarks\n\nCheck the test files and github actions for the benchmarks (linux, macos, windows).\nmm-go can sometimes be 5-10 times faster.\n\n```\nRun go test ./... -bench=. -count 5 \u003e out.txt \u0026\u0026 benchstat out.txt\n\n\ngoos: linux\ngoarch: amd64\npkg: github.com/joetifa2003/mm-go\ncpu: AMD Ryzen 7 5800H with Radeon Graphics\n                                              │   out.txt    │\n                                              │    sec/op    │\nLinkedListManaged-16                            605.7µ ± ∞ ¹\nLinkedListCAlloc-16                             933.1µ ± ∞ ¹\nLinkedListBatchAllocator/bucket_size_100-16     513.3µ ± ∞ ¹\nLinkedListBatchAllocator/bucket_size_200-16     405.8µ ± ∞ ¹\nLinkedListBatchAllocator/bucket_size_500-16     425.4µ ± ∞ ¹\nLinkedListBatchAllocator/bucket_size_10000-16   200.7µ ± ∞ ¹\nLinkedListTypedArena/chunk_size_100-16          105.3µ ± ∞ ¹\nLinkedListTypedArena/chunk_size_200-16          95.50µ ± ∞ ¹\nLinkedListTypedArena/chunk_size_500-16          83.02µ ± ∞ ¹\nLinkedListTypedArena/chunk_size_10000-16        75.96µ ± ∞ ¹\ngeomean                                         240.1µ\n¹ need \u003e= 6 samples for confidence interval at level 0.95\n\npkg: github.com/joetifa2003/mm-go/hashmap\n                     │   out.txt    │\n                     │    sec/op    │\nHashmapGo-16           210.7µ ± ∞ ¹\nHashmapCAlloc-16       189.1µ ± ∞ ¹\nHashmapBatchAlloc-16   118.2µ ± ∞ ¹\ngeomean                167.6µ\n¹ need \u003e= 6 samples for confidence interval at level 0.95\n\n```\n\n\u003c!-- gomarkdoc:embed:start --\u003e\n\n\u003c!-- Code generated by gomarkdoc. DO NOT EDIT --\u003e\n\n# mm\n\n```go\nimport \"github.com/joetifa2003/mm-go\"\n```\n\n## Index\n\n- [func SizeOf\\[T any\\]\\(\\) int](\u003c#SizeOf\u003e)\n- [func Zero\\[T any\\]\\(\\) T](\u003c#Zero\u003e)\n\n\n\u003ca name=\"SizeOf\"\u003e\u003c/a\u003e\n## func [SizeOf](\u003chttps://github.com/joetifa2003/mm-go/blob/master/mm.go#L6\u003e)\n\n```go\nfunc SizeOf[T any]() int\n```\n\nSizeOf returns the size of T in bytes\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\u003cp\u003e\n\n\n\n```go\nfmt.Println(mm.SizeOf[int32]())\nfmt.Println(mm.SizeOf[int64]())\n// Output:\n// 4\n// 8\n```\n\n#### Output\n\n```\n4\n8\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003ca name=\"Zero\"\u003e\u003c/a\u003e\n## func [Zero](\u003chttps://github.com/joetifa2003/mm-go/blob/master/mm.go#L12\u003e)\n\n```go\nfunc Zero[T any]() T\n```\n\nZero returns a zero value of T\n\n# allocator\n\n```go\nimport \"github.com/joetifa2003/mm-go/allocator\"\n```\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\u003cp\u003e\n\n\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/joetifa2003/mm-go/allocator\"\n)\n\nfunc main() {\n\talloc := allocator.NewC()\n\tdefer alloc.Destroy()\n\n\tptr := allocator.Alloc[int](alloc)\n\tdefer allocator.Free(alloc, ptr)\n\n\t*ptr = 15\n\tfmt.Println(*ptr)\n\n}\n```\n\n#### Output\n\n```\n15\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eExample (Datastructures)\u003c/summary\u003e\n\u003cp\u003e\n\n\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/joetifa2003/mm-go/allocator\"\n\t\"github.com/joetifa2003/mm-go/linkedlist\"\n\t\"github.com/joetifa2003/mm-go/mmstring\"\n\t\"github.com/joetifa2003/mm-go/vector\"\n)\n\ntype MyStruct struct {\n\ta int\n\tb float32\n}\n\nfunc main() {\n\talloc := allocator.NewC()\n\tdefer alloc.Destroy() // all the memory allocated bellow will be freed, no need to free it manually.\n\n\tp := allocator.Alloc[MyStruct](alloc)\n\tdefer allocator.Free(alloc, p)\n\n\tp.a = 100\n\tp.b = 200\n\n\tfmt.Println(*p)\n\n\tv := vector.New[int](alloc)\n\tdefer v.Free()\n\tv.Push(15)\n\tv.Push(70)\n\n\tfor _, i := range v.Iter() {\n\t\tfmt.Println(i)\n\t}\n\n\tl := linkedlist.New[*mmstring.MMString](alloc)\n\tdefer l.Free()\n\tl.PushBack(mmstring.From(alloc, \"hello\"))\n\tl.PushBack(mmstring.From(alloc, \"world\"))\n\n\tfor _, i := range l.Iter() {\n\t\tfmt.Println(i.GetGoString())\n\t}\n\n}\n```\n\n#### Output\n\n```\n{100 200}\n15\n70\nhello\nworld\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n## Index\n\n- [func Alloc\\[T any\\]\\(a Allocator\\) \\*T](\u003c#Alloc\u003e)\n- [func AllocMany\\[T any\\]\\(a Allocator, n int\\) \\[\\]T](\u003c#AllocMany\u003e)\n- [func Free\\[T any\\]\\(a Allocator, ptr \\*T\\)](\u003c#Free\u003e)\n- [func FreeMany\\[T any\\]\\(a Allocator, slice \\[\\]T\\)](\u003c#FreeMany\u003e)\n- [func Realloc\\[T any\\]\\(a Allocator, slice \\[\\]T, newN int\\) \\[\\]T](\u003c#Realloc\u003e)\n- [type Allocator](\u003c#Allocator\u003e)\n  - [func NewAllocator\\(allocator unsafe.Pointer, alloc func\\(allocator unsafe.Pointer, size int\\) unsafe.Pointer, free func\\(allocator unsafe.Pointer, ptr unsafe.Pointer\\), realloc func\\(allocator unsafe.Pointer, ptr unsafe.Pointer, size int\\) unsafe.Pointer, destroy func\\(allocator unsafe.Pointer\\)\\) Allocator](\u003c#NewAllocator\u003e)\n  - [func NewC\\(\\) Allocator](\u003c#NewC\u003e)\n  - [func \\(a Allocator\\) Alloc\\(size int\\) unsafe.Pointer](\u003c#Allocator.Alloc\u003e)\n  - [func \\(a Allocator\\) Destroy\\(\\)](\u003c#Allocator.Destroy\u003e)\n  - [func \\(a Allocator\\) Free\\(ptr unsafe.Pointer\\)](\u003c#Allocator.Free\u003e)\n  - [func \\(a Allocator\\) Realloc\\(ptr unsafe.Pointer, size int\\) unsafe.Pointer](\u003c#Allocator.Realloc\u003e)\n\n\n\u003ca name=\"Alloc\"\u003e\u003c/a\u003e\n## func [Alloc](\u003chttps://github.com/joetifa2003/mm-go/blob/master/allocator/allocator.go#L60\u003e)\n\n```go\nfunc Alloc[T any](a Allocator) *T\n```\n\nAlloc allocates T and returns a pointer to it.\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\u003cp\u003e\n\n\n\n```go\nalloc := allocator.NewC()\ndefer alloc.Destroy()\n\n// So you can do this:\nptr := allocator.Alloc[int](alloc) // allocates a single int and returns a ptr to it\ndefer allocator.Free(alloc, ptr)   // frees the int (defer recommended to prevent leaks)\n*ptr = 15\nfmt.Println(*ptr)\n\n// instead of doing this:\nptr2 := (*int)(alloc.Alloc(mm.SizeOf[int]()))\ndefer alloc.Free(unsafe.Pointer(ptr2))\n*ptr2 = 15\n\nfmt.Println(*ptr2)\n\n// Output:\n// 15\n// 15\n```\n\n#### Output\n\n```\n15\n15\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003ca name=\"AllocMany\"\u003e\u003c/a\u003e\n## func [AllocMany](\u003chttps://github.com/joetifa2003/mm-go/blob/master/allocator/allocator.go#L74\u003e)\n\n```go\nfunc AllocMany[T any](a Allocator, n int) []T\n```\n\nAllocMany allocates n of T and returns a slice representing the heap. CAUTION: don't append to the slice, the purpose of it is to replace pointer arithmetic with slice indexing\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\u003cp\u003e\n\n\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/joetifa2003/mm-go/allocator\"\n)\n\nfunc main() {\n\talloc := allocator.NewC()\n\tdefer alloc.Destroy()\n\n\theap := allocator.AllocMany[int](alloc, 2) // allocates 2 ints and returns it as a slice of ints with length 2\n\tdefer allocator.FreeMany(alloc, heap)      // it's recommended to make sure the data gets deallocated (defer recommended to prevent leaks)\n\n\theap[0] = 15    // changes the data in the slice (aka the heap)\n\tptr := \u0026heap[0] // takes a pointer to the first int in the heap\n\t// Be careful if you do ptr := heap[0] this will take a copy from the data on the heap\n\t*ptr = 45 // changes the value from 15 to 45\n\theap[1] = 70\n\n\tfmt.Println(heap[0])\n\tfmt.Println(heap[1])\n\n}\n```\n\n#### Output\n\n```\n45\n70\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003ca name=\"Free\"\u003e\u003c/a\u003e\n## func [Free](\u003chttps://github.com/joetifa2003/mm-go/blob/master/allocator/allocator.go#L67\u003e)\n\n```go\nfunc Free[T any](a Allocator, ptr *T)\n```\n\nFreeMany frees memory allocated by Alloc takes a ptr CAUTION: be careful not to double free, and prefer using defer to deallocate\n\n\u003ca name=\"FreeMany\"\u003e\u003c/a\u003e\n## func [FreeMany](\u003chttps://github.com/joetifa2003/mm-go/blob/master/allocator/allocator.go#L84\u003e)\n\n```go\nfunc FreeMany[T any](a Allocator, slice []T)\n```\n\nFreeMany frees memory allocated by AllocMany takes in the slice \\(aka the heap\\) CAUTION: be careful not to double free, and prefer using defer to deallocate\n\n\u003ca name=\"Realloc\"\u003e\u003c/a\u003e\n## func [Realloc](\u003chttps://github.com/joetifa2003/mm-go/blob/master/allocator/allocator.go#L89\u003e)\n\n```go\nfunc Realloc[T any](a Allocator, slice []T, newN int) []T\n```\n\nRealloc reallocates memory allocated with AllocMany and doesn't change underling data\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\u003cp\u003e\n\n\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/joetifa2003/mm-go/allocator\"\n)\n\nfunc main() {\n\talloc := allocator.NewC()\n\tdefer alloc.Destroy()\n\n\theap := allocator.AllocMany[int](alloc, 2) // allocates 2 int and returns it as a slice of ints with length 2\n\n\theap[0] = 15\n\theap[1] = 70\n\n\theap = allocator.Realloc(alloc, heap, 3)\n\theap[2] = 100\n\n\tfmt.Println(heap[0])\n\tfmt.Println(heap[1])\n\tfmt.Println(heap[2])\n\n\tallocator.FreeMany(alloc, heap)\n\n}\n```\n\n#### Output\n\n```\n15\n70\n100\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003ca name=\"Allocator\"\u003e\u003c/a\u003e\n## type [Allocator](\u003chttps://github.com/joetifa2003/mm-go/blob/master/allocator/allocator.go#L7-L13\u003e)\n\nAllocator is an interface that defines some methods needed for most allocators. It's not a golang interface, so it's safe to use in manually managed structs \\(will not get garbage collected\\).\n\n```go\ntype Allocator struct {\n    // contains filtered or unexported fields\n}\n```\n\n\u003ca name=\"NewAllocator\"\u003e\u003c/a\u003e\n### func [NewAllocator](\u003chttps://github.com/joetifa2003/mm-go/blob/master/allocator/allocator.go#L16-L22\u003e)\n\n```go\nfunc NewAllocator(allocator unsafe.Pointer, alloc func(allocator unsafe.Pointer, size int) unsafe.Pointer, free func(allocator unsafe.Pointer, ptr unsafe.Pointer), realloc func(allocator unsafe.Pointer, ptr unsafe.Pointer, size int) unsafe.Pointer, destroy func(allocator unsafe.Pointer)) Allocator\n```\n\nNewAllocator creates a new Allocator\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\u003cp\u003e\n\n\n\n```go\npackage main\n\nimport (\n\t\"unsafe\"\n\n\t\"github.com/joetifa2003/mm-go/allocator\"\n)\n\nfunc main() {\n\t// Create a custom allocator\n\talloc := allocator.NewAllocator(\n\t\tnil,\n\t\tmyallocator_alloc,\n\t\tmyallocator_free,\n\t\tmyallocator_realloc,\n\t\tmyallocator_destroy,\n\t)\n\n\t// Check how C allocator is implemented\n\t// or batchallocator source for a reference\n\n\t_ = alloc\n}\n\nfunc myallocator_alloc(allocator unsafe.Pointer, size int) unsafe.Pointer {\n\treturn nil\n}\n\nfunc myallocator_free(allocator unsafe.Pointer, ptr unsafe.Pointer) {\n}\n\nfunc myallocator_realloc(allocator unsafe.Pointer, ptr unsafe.Pointer, size int) unsafe.Pointer {\n\treturn nil\n}\n\nfunc myallocator_destroy(allocator unsafe.Pointer) {\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003ca name=\"NewC\"\u003e\u003c/a\u003e\n### func [NewC](\u003chttps://github.com/joetifa2003/mm-go/blob/master/allocator/callocator.go#L9\u003e)\n\n```go\nfunc NewC() Allocator\n```\n\nNewC returns an allocator that uses C calloc, realloc and free.\n\n\u003ca name=\"Allocator.Alloc\"\u003e\u003c/a\u003e\n### func \\(Allocator\\) [Alloc](\u003chttps://github.com/joetifa2003/mm-go/blob/master/allocator/allocator.go#L33\u003e)\n\n```go\nfunc (a Allocator) Alloc(size int) unsafe.Pointer\n```\n\nAlloc allocates size bytes and returns an unsafe pointer to it.\n\n\u003ca name=\"Allocator.Destroy\"\u003e\u003c/a\u003e\n### func \\(Allocator\\) [Destroy](\u003chttps://github.com/joetifa2003/mm-go/blob/master/allocator/allocator.go#L50\u003e)\n\n```go\nfunc (a Allocator) Destroy()\n```\n\nDestroy destroys the allocator. After calling this, the allocator is no longer usable. This is useful for cleanup, freeing allocator internal resources, etc.\n\n\u003ca name=\"Allocator.Free\"\u003e\u003c/a\u003e\n### func \\(Allocator\\) [Free](\u003chttps://github.com/joetifa2003/mm-go/blob/master/allocator/allocator.go#L38\u003e)\n\n```go\nfunc (a Allocator) Free(ptr unsafe.Pointer)\n```\n\nFree frees the memory pointed by ptr\n\n\u003ca name=\"Allocator.Realloc\"\u003e\u003c/a\u003e\n### func \\(Allocator\\) [Realloc](\u003chttps://github.com/joetifa2003/mm-go/blob/master/allocator/allocator.go#L43\u003e)\n\n```go\nfunc (a Allocator) Realloc(ptr unsafe.Pointer, size int) unsafe.Pointer\n```\n\nRealloc reallocates the memory pointed by ptr with a new size and returns a new pointer to it.\n\n# batchallocator\n\n```go\nimport \"github.com/joetifa2003/mm-go/batchallocator\"\n```\n\nThis allocator purpose is to reduce the overhead of calling CGO on every allocation/free, it also acts as an arena since it frees all the memory when \\`Destroy\\` is called. It allocats large chunks of memory at once and then divides them when you allocate, making it much faster. This allocator has to take another allocator for it to work, usually with the C allocator. You can optionally call \\`Free\\` on the pointers allocated by batchallocator manually, and it will free the memory as soon as it can. \\`Destroy\\` must be called to free internal resources and free all the memory allocated by the allocator.\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\u003cp\u003e\n\n\n\n```go\npackage main\n\nimport (\n\t\"github.com/joetifa2003/mm-go/allocator\"\n\t\"github.com/joetifa2003/mm-go/batchallocator\"\n)\n\nfunc main() {\n\talloc := batchallocator.New(allocator.NewC()) // by default it allocates page, which is usually 4kb\n\tdefer alloc.Destroy()                         // this frees all  memory allocated by the allocator automatically\n\n\tptr := allocator.Alloc[int](alloc)\n\t// but you can still free the pointers manually if you want (will free buckets of memory if all pointers depending on it is freed)\n\tdefer allocator.Free(alloc, ptr) // this can removed and the memory will be freed.\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eExample (Arena)\u003c/summary\u003e\n\u003cp\u003e\n\n\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/joetifa2003/mm-go/allocator\"\n\t\"github.com/joetifa2003/mm-go/batchallocator\"\n\t\"github.com/joetifa2003/mm-go/linkedlist\"\n\t\"github.com/joetifa2003/mm-go/mmstring\"\n\t\"github.com/joetifa2003/mm-go/vector\"\n)\n\nfunc main() {\n\talloc := batchallocator.New(allocator.NewC())\n\tdefer alloc.Destroy() // all the memory allocated bellow will be freed, no need to free it manually.\n\n\tv := vector.New[int](alloc)\n\tv.Push(15)\n\tv.Push(70)\n\n\tfor _, i := range v.Iter() {\n\t\tfmt.Println(i)\n\t}\n\n\tl := linkedlist.New[*mmstring.MMString](alloc)\n\tl.PushBack(mmstring.From(alloc, \"hello\"))\n\tl.PushBack(mmstring.From(alloc, \"world\"))\n\n\tfor _, i := range l.Iter() {\n\t\tfmt.Println(i.GetGoString())\n\t}\n\n}\n```\n\n#### Output\n\n```\n15\n70\nhello\nworld\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n## Index\n\n- [func New\\(a allocator.Allocator, options ...BatchAllocatorOption\\) allocator.Allocator](\u003c#New\u003e)\n- [type BatchAllocator](\u003c#BatchAllocator\u003e)\n- [type BatchAllocatorOption](\u003c#BatchAllocatorOption\u003e)\n  - [func WithBucketSize\\(size int\\) BatchAllocatorOption](\u003c#WithBucketSize\u003e)\n\n\n\u003ca name=\"New\"\u003e\u003c/a\u003e\n## func [New](\u003chttps://github.com/joetifa2003/mm-go/blob/master/batchallocator/batch.go#L59\u003e)\n\n```go\nfunc New(a allocator.Allocator, options ...BatchAllocatorOption) allocator.Allocator\n```\n\nNew creates a new BatchAllocator and applies optional configuration using BatchAllocatorOption\n\n\u003ca name=\"BatchAllocator\"\u003e\u003c/a\u003e\n## type [BatchAllocator](\u003chttps://github.com/joetifa2003/mm-go/blob/master/batchallocator/batch.go#L42-L46\u003e)\n\nBatchAllocator manages a collection of memory buckets to optimize small allocations\n\n```go\ntype BatchAllocator struct {\n    // contains filtered or unexported fields\n}\n```\n\n\u003ca name=\"BatchAllocatorOption\"\u003e\u003c/a\u003e\n## type [BatchAllocatorOption](\u003chttps://github.com/joetifa2003/mm-go/blob/master/batchallocator/batch.go#L48\u003e)\n\n\n\n```go\ntype BatchAllocatorOption func(alloc *BatchAllocator)\n```\n\n\u003ca name=\"WithBucketSize\"\u003e\u003c/a\u003e\n### func [WithBucketSize](\u003chttps://github.com/joetifa2003/mm-go/blob/master/batchallocator/batch.go#L52\u003e)\n\n```go\nfunc WithBucketSize(size int) BatchAllocatorOption\n```\n\nWithBucketSize Option to specify bucket size when creating BatchAllocator You can allocate more memory than the bucketsize in one allocation, it will allocate a new bucket and put the data in it.\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\u003cp\u003e\n\n\n\n```go\nalloc := batchallocator.New(\n\tallocator.NewC(),\n\tbatchallocator.WithBucketSize(mm.SizeOf[int]()*15), // configure the allocator to allocate size of 15 ints per bucket.\n)\ndefer alloc.Destroy()\n\nptr := allocator.Alloc[int](alloc)\ndefer allocator.Free(alloc, ptr) // this can be removed and the memory will still be freed on Destroy.\n\nptr2 := allocator.Alloc[int](alloc) // will not call CGO because there is still enough memory in the Bucket.\ndefer allocator.Free(alloc, ptr2)   // this can be removed and the memory will still be freed on Destroy.\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n# hashmap\n\n```go\nimport \"github.com/joetifa2003/mm-go/hashmap\"\n```\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\u003cp\u003e\n\n\n\n```go\nalloc := batchallocator.New(allocator.NewC())\ndefer alloc.Destroy()\n\nhm := New[int, int](alloc)\ndefer hm.Free() // can be removed\n\nhm.Set(1, 10)\nhm.Set(2, 20)\nhm.Set(3, 30)\n\nsumKeys := 0\nsumValues := 0\nfor k, v := range hm.Iter() {\n\tsumKeys += k\n\tsumValues += v\n}\n\nfmt.Println(sumKeys)\nfmt.Println(sumValues)\n\n// Output:\n// 6\n// 60\n```\n\n#### Output\n\n```\n6\n60\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n## Index\n\n- [type Hashmap](\u003c#Hashmap\u003e)\n  - [func New\\[K comparable, V any\\]\\(alloc allocator.Allocator\\) \\*Hashmap\\[K, V\\]](\u003c#New\u003e)\n  - [func \\(hm \\*Hashmap\\[K, V\\]\\) Delete\\(key K\\)](\u003c#Hashmap[K, V].Delete\u003e)\n  - [func \\(hm \\*Hashmap\\[K, V\\]\\) Free\\(\\)](\u003c#Hashmap[K, V].Free\u003e)\n  - [func \\(hm \\*Hashmap\\[K, V\\]\\) Get\\(key K\\) \\(value V, exists bool\\)](\u003c#Hashmap[K, V].Get\u003e)\n  - [func \\(hm \\*Hashmap\\[K, V\\]\\) GetPtr\\(key K\\) \\(value \\*V, exists bool\\)](\u003c#Hashmap[K, V].GetPtr\u003e)\n  - [func \\(hm \\*Hashmap\\[K, V\\]\\) Iter\\(\\) iter.Seq2\\[K, V\\]](\u003c#Hashmap[K, V].Iter\u003e)\n  - [func \\(hm \\*Hashmap\\[K, V\\]\\) Keys\\(\\) \\[\\]K](\u003c#Hashmap[K, V].Keys\u003e)\n  - [func \\(hm \\*Hashmap\\[K, V\\]\\) Set\\(key K, value V\\)](\u003c#Hashmap[K, V].Set\u003e)\n  - [func \\(hm \\*Hashmap\\[K, V\\]\\) Values\\(\\) \\[\\]V](\u003c#Hashmap[K, V].Values\u003e)\n\n\n\u003ca name=\"Hashmap\"\u003e\u003c/a\u003e\n## type [Hashmap](\u003chttps://github.com/joetifa2003/mm-go/blob/master/hashmap/hashmap.go#L15-L20\u003e)\n\nHashmap Manually managed hashmap,\n\n```go\ntype Hashmap[K comparable, V any] struct {\n    // contains filtered or unexported fields\n}\n```\n\n\u003ca name=\"New\"\u003e\u003c/a\u003e\n### func [New](\u003chttps://github.com/joetifa2003/mm-go/blob/master/hashmap/hashmap.go#L28\u003e)\n\n```go\nfunc New[K comparable, V any](alloc allocator.Allocator) *Hashmap[K, V]\n```\n\nNew creates a new Hashmap with key of type K and value of type V\n\n\u003ca name=\"Hashmap[K, V].Delete\"\u003e\u003c/a\u003e\n### func \\(\\*Hashmap\\[K, V\\]\\) [Delete](\u003chttps://github.com/joetifa2003/mm-go/blob/master/hashmap/hashmap.go#L172\u003e)\n\n```go\nfunc (hm *Hashmap[K, V]) Delete(key K)\n```\n\nDelete delete value with key K\n\n\u003ca name=\"Hashmap[K, V].Free\"\u003e\u003c/a\u003e\n### func \\(\\*Hashmap\\[K, V\\]\\) [Free](\u003chttps://github.com/joetifa2003/mm-go/blob/master/hashmap/hashmap.go#L187\u003e)\n\n```go\nfunc (hm *Hashmap[K, V]) Free()\n```\n\nFree frees the Hashmap\n\n\u003ca name=\"Hashmap[K, V].Get\"\u003e\u003c/a\u003e\n### func \\(\\*Hashmap\\[K, V\\]\\) [Get](\u003chttps://github.com/joetifa2003/mm-go/blob/master/hashmap/hashmap.go#L81\u003e)\n\n```go\nfunc (hm *Hashmap[K, V]) Get(key K) (value V, exists bool)\n```\n\nGet takes key K and return value V\n\n\u003ca name=\"Hashmap[K, V].GetPtr\"\u003e\u003c/a\u003e\n### func \\(\\*Hashmap\\[K, V\\]\\) [GetPtr](\u003chttps://github.com/joetifa2003/mm-go/blob/master/hashmap/hashmap.go#L101\u003e)\n\n```go\nfunc (hm *Hashmap[K, V]) GetPtr(key K) (value *V, exists bool)\n```\n\nGetPtr takes key K and return a pointer to value V\n\n\u003ca name=\"Hashmap[K, V].Iter\"\u003e\u003c/a\u003e\n### func \\(\\*Hashmap\\[K, V\\]\\) [Iter](\u003chttps://github.com/joetifa2003/mm-go/blob/master/hashmap/hashmap.go#L121\u003e)\n\n```go\nfunc (hm *Hashmap[K, V]) Iter() iter.Seq2[K, V]\n```\n\nIter returns an iterator over all key/value pairs\n\n\u003ca name=\"Hashmap[K, V].Keys\"\u003e\u003c/a\u003e\n### func \\(\\*Hashmap\\[K, V\\]\\) [Keys](\u003chttps://github.com/joetifa2003/mm-go/blob/master/hashmap/hashmap.go#L155\u003e)\n\n```go\nfunc (hm *Hashmap[K, V]) Keys() []K\n```\n\nKeys returns all keys as a slice\n\n\u003ca name=\"Hashmap[K, V].Set\"\u003e\u003c/a\u003e\n### func \\(\\*Hashmap\\[K, V\\]\\) [Set](\u003chttps://github.com/joetifa2003/mm-go/blob/master/hashmap/hashmap.go#L57\u003e)\n\n```go\nfunc (hm *Hashmap[K, V]) Set(key K, value V)\n```\n\nSet inserts a new value V if key K doesn't exist, Otherwise update the key K with value V\n\n\u003ca name=\"Hashmap[K, V].Values\"\u003e\u003c/a\u003e\n### func \\(\\*Hashmap\\[K, V\\]\\) [Values](\u003chttps://github.com/joetifa2003/mm-go/blob/master/hashmap/hashmap.go#L138\u003e)\n\n```go\nfunc (hm *Hashmap[K, V]) Values() []V\n```\n\nValues returns all values as a slice\n\n# linkedlist\n\n```go\nimport \"github.com/joetifa2003/mm-go/linkedlist\"\n```\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\u003cp\u003e\n\n\n\n```go\nalloc := allocator.NewC()\ndefer alloc.Destroy()\n\nll := New[int](alloc)\ndefer ll.Free()\n\nll.PushBack(1)\nll.PushBack(2)\nll.PushBack(3)\nll.PushBack(4)\n\nfmt.Println(\"PopBack:\", ll.PopBack())\nfmt.Println(\"PopFront:\", ll.PopFront())\n\nfor _, i := range ll.Iter() {\n\tfmt.Println(i)\n}\n\n// Output:\n// PopBack: 4\n// PopFront: 1\n// 2\n// 3\n```\n\n#### Output\n\n```\nPopBack: 4\nPopFront: 1\n2\n3\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n## Index\n\n- [type LinkedList](\u003c#LinkedList\u003e)\n  - [func New\\[T any\\]\\(alloc allocator.Allocator\\) \\*LinkedList\\[T\\]](\u003c#New\u003e)\n  - [func \\(ll \\*LinkedList\\[T\\]\\) At\\(idx int\\) T](\u003c#LinkedList[T].At\u003e)\n  - [func \\(ll \\*LinkedList\\[T\\]\\) AtPtr\\(idx int\\) \\*T](\u003c#LinkedList[T].AtPtr\u003e)\n  - [func \\(ll \\*LinkedList\\[T\\]\\) FindIndex\\(f func\\(value T\\) bool\\) \\(idx int, ok bool\\)](\u003c#LinkedList[T].FindIndex\u003e)\n  - [func \\(ll \\*LinkedList\\[T\\]\\) FindIndexes\\(f func\\(value T\\) bool\\) \\[\\]int](\u003c#LinkedList[T].FindIndexes\u003e)\n  - [func \\(ll \\*LinkedList\\[T\\]\\) ForEach\\(f func\\(idx int, value T\\)\\)](\u003c#LinkedList[T].ForEach\u003e)\n  - [func \\(ll \\*LinkedList\\[T\\]\\) Free\\(\\)](\u003c#LinkedList[T].Free\u003e)\n  - [func \\(ll \\*LinkedList\\[T\\]\\) Iter\\(\\) iter.Seq2\\[int, T\\]](\u003c#LinkedList[T].Iter\u003e)\n  - [func \\(ll \\*LinkedList\\[T\\]\\) Len\\(\\) int](\u003c#LinkedList[T].Len\u003e)\n  - [func \\(ll \\*LinkedList\\[T\\]\\) PopBack\\(\\) T](\u003c#LinkedList[T].PopBack\u003e)\n  - [func \\(ll \\*LinkedList\\[T\\]\\) PopFront\\(\\) T](\u003c#LinkedList[T].PopFront\u003e)\n  - [func \\(ll \\*LinkedList\\[T\\]\\) PushBack\\(value T\\)](\u003c#LinkedList[T].PushBack\u003e)\n  - [func \\(ll \\*LinkedList\\[T\\]\\) PushFront\\(value T\\)](\u003c#LinkedList[T].PushFront\u003e)\n  - [func \\(ll \\*LinkedList\\[T\\]\\) Remove\\(f func\\(idx int, value T\\) bool\\) \\(value T, ok bool\\)](\u003c#LinkedList[T].Remove\u003e)\n  - [func \\(ll \\*LinkedList\\[T\\]\\) RemoveAll\\(f func\\(idx int, value T\\) bool\\) \\[\\]T](\u003c#LinkedList[T].RemoveAll\u003e)\n  - [func \\(ll \\*LinkedList\\[T\\]\\) RemoveAt\\(idx int\\) T](\u003c#LinkedList[T].RemoveAt\u003e)\n\n\n\u003ca name=\"LinkedList\"\u003e\u003c/a\u003e\n## type [LinkedList](\u003chttps://github.com/joetifa2003/mm-go/blob/master/linkedlist/linked_list.go#L20-L26\u003e)\n\nLinkedList a doubly\\-linked list. Note: can be a lot slower than Vector but sometimes faster in specific use cases\n\n```go\ntype LinkedList[T any] struct {\n    // contains filtered or unexported fields\n}\n```\n\n\u003ca name=\"New\"\u003e\u003c/a\u003e\n### func [New](\u003chttps://github.com/joetifa2003/mm-go/blob/master/linkedlist/linked_list.go#L29\u003e)\n\n```go\nfunc New[T any](alloc allocator.Allocator) *LinkedList[T]\n```\n\nNew creates a new linked list.\n\n\u003ca name=\"LinkedList[T].At\"\u003e\u003c/a\u003e\n### func \\(\\*LinkedList\\[T\\]\\) [At](\u003chttps://github.com/joetifa2003/mm-go/blob/master/linkedlist/linked_list.go#L167\u003e)\n\n```go\nfunc (ll *LinkedList[T]) At(idx int) T\n```\n\nAt gets value T at idx.\n\n\u003ca name=\"LinkedList[T].AtPtr\"\u003e\u003c/a\u003e\n### func \\(\\*LinkedList\\[T\\]\\) [AtPtr](\u003chttps://github.com/joetifa2003/mm-go/blob/master/linkedlist/linked_list.go#L172\u003e)\n\n```go\nfunc (ll *LinkedList[T]) AtPtr(idx int) *T\n```\n\nAtPtr gets a pointer to value T at idx.\n\n\u003ca name=\"LinkedList[T].FindIndex\"\u003e\u003c/a\u003e\n### func \\(\\*LinkedList\\[T\\]\\) [FindIndex](\u003chttps://github.com/joetifa2003/mm-go/blob/master/linkedlist/linked_list.go#L241\u003e)\n\n```go\nfunc (ll *LinkedList[T]) FindIndex(f func(value T) bool) (idx int, ok bool)\n```\n\nFindIndex returns the first index of value T that pass the test implemented by the provided function.\n\n\u003ca name=\"LinkedList[T].FindIndexes\"\u003e\u003c/a\u003e\n### func \\(\\*LinkedList\\[T\\]\\) [FindIndexes](\u003chttps://github.com/joetifa2003/mm-go/blob/master/linkedlist/linked_list.go#L259\u003e)\n\n```go\nfunc (ll *LinkedList[T]) FindIndexes(f func(value T) bool) []int\n```\n\nFindIndex returns all indexes of value T that pass the test implemented by the provided function.\n\n\u003ca name=\"LinkedList[T].ForEach\"\u003e\u003c/a\u003e\n### func \\(\\*LinkedList\\[T\\]\\) [ForEach](\u003chttps://github.com/joetifa2003/mm-go/blob/master/linkedlist/linked_list.go#L125\u003e)\n\n```go\nfunc (ll *LinkedList[T]) ForEach(f func(idx int, value T))\n```\n\nForEach iterates through the linked list.\n\n\u003ca name=\"LinkedList[T].Free\"\u003e\u003c/a\u003e\n### func \\(\\*LinkedList\\[T\\]\\) [Free](\u003chttps://github.com/joetifa2003/mm-go/blob/master/linkedlist/linked_list.go#L284\u003e)\n\n```go\nfunc (ll *LinkedList[T]) Free()\n```\n\nFree frees the linked list.\n\n\u003ca name=\"LinkedList[T].Iter\"\u003e\u003c/a\u003e\n### func \\(\\*LinkedList\\[T\\]\\) [Iter](\u003chttps://github.com/joetifa2003/mm-go/blob/master/linkedlist/linked_list.go#L137\u003e)\n\n```go\nfunc (ll *LinkedList[T]) Iter() iter.Seq2[int, T]\n```\n\nIter returns an iterator over the linked list values.\n\n\u003ca name=\"LinkedList[T].Len\"\u003e\u003c/a\u003e\n### func \\(\\*LinkedList\\[T\\]\\) [Len](\u003chttps://github.com/joetifa2003/mm-go/blob/master/linkedlist/linked_list.go#L279\u003e)\n\n```go\nfunc (ll *LinkedList[T]) Len() int\n```\n\nLen gets linked list length.\n\n\u003ca name=\"LinkedList[T].PopBack\"\u003e\u003c/a\u003e\n### func \\(\\*LinkedList\\[T\\]\\) [PopBack](\u003chttps://github.com/joetifa2003/mm-go/blob/master/linkedlist/linked_list.go#L85\u003e)\n\n```go\nfunc (ll *LinkedList[T]) PopBack() T\n```\n\nPopBack pops and returns value T from the back of the linked list.\n\n\u003ca name=\"LinkedList[T].PopFront\"\u003e\u003c/a\u003e\n### func \\(\\*LinkedList\\[T\\]\\) [PopFront](\u003chttps://github.com/joetifa2003/mm-go/blob/master/linkedlist/linked_list.go#L105\u003e)\n\n```go\nfunc (ll *LinkedList[T]) PopFront() T\n```\n\nPopFront pops and returns value T from the front of the linked list.\n\n\u003ca name=\"LinkedList[T].PushBack\"\u003e\u003c/a\u003e\n### func \\(\\*LinkedList\\[T\\]\\) [PushBack](\u003chttps://github.com/joetifa2003/mm-go/blob/master/linkedlist/linked_list.go#L53\u003e)\n\n```go\nfunc (ll *LinkedList[T]) PushBack(value T)\n```\n\nPushBack pushes value T to the back of the linked list.\n\n\u003ca name=\"LinkedList[T].PushFront\"\u003e\u003c/a\u003e\n### func \\(\\*LinkedList\\[T\\]\\) [PushFront](\u003chttps://github.com/joetifa2003/mm-go/blob/master/linkedlist/linked_list.go#L69\u003e)\n\n```go\nfunc (ll *LinkedList[T]) PushFront(value T)\n```\n\nPushFront pushes value T to the back of the linked list.\n\n\u003ca name=\"LinkedList[T].Remove\"\u003e\u003c/a\u003e\n### func \\(\\*LinkedList\\[T\\]\\) [Remove](\u003chttps://github.com/joetifa2003/mm-go/blob/master/linkedlist/linked_list.go#L201\u003e)\n\n```go\nfunc (ll *LinkedList[T]) Remove(f func(idx int, value T) bool) (value T, ok bool)\n```\n\nRemove removes the first value T that pass the test implemented by the provided function. if the test succeeded it will return the value and true\n\n\u003ca name=\"LinkedList[T].RemoveAll\"\u003e\u003c/a\u003e\n### func \\(\\*LinkedList\\[T\\]\\) [RemoveAll](\u003chttps://github.com/joetifa2003/mm-go/blob/master/linkedlist/linked_list.go#L220\u003e)\n\n```go\nfunc (ll *LinkedList[T]) RemoveAll(f func(idx int, value T) bool) []T\n```\n\nRemoveAll removes all values of T that pass the test implemented by the provided function.\n\n\u003ca name=\"LinkedList[T].RemoveAt\"\u003e\u003c/a\u003e\n### func \\(\\*LinkedList\\[T\\]\\) [RemoveAt](\u003chttps://github.com/joetifa2003/mm-go/blob/master/linkedlist/linked_list.go#L177\u003e)\n\n```go\nfunc (ll *LinkedList[T]) RemoveAt(idx int) T\n```\n\nRemoveAt removes value T at specified index and returns it.\n\n# minheap\n\n```go\nimport \"github.com/joetifa2003/mm-go/minheap\"\n```\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\u003cp\u003e\n\n\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/joetifa2003/mm-go/allocator\"\n\t\"github.com/joetifa2003/mm-go/minheap\"\n)\n\nfunc int_less(a, b int) bool { return a \u003c b }\n\nfunc main() {\n\talloc := allocator.NewC()\n\tdefer alloc.Destroy()\n\n\th := minheap.New[int](alloc, int_less)\n\n\t// Push some values onto the heap\n\th.Push(2)\n\th.Push(1)\n\th.Push(4)\n\th.Push(3)\n\th.Push(5)\n\n\t// Pop the minimum value from the heap\n\tfmt.Println(h.Pop())\n\tfmt.Println(h.Pop())\n\n}\n```\n\n#### Output\n\n```\n1\n2\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eExample (-ax Heap)\u003c/summary\u003e\n\u003cp\u003e\n\n\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/joetifa2003/mm-go/allocator\"\n\t\"github.com/joetifa2003/mm-go/minheap\"\n)\n\nfunc int_greater(a, b int) bool { return a \u003e b }\n\nfunc main() {\n\talloc := allocator.NewC()\n\tdefer alloc.Destroy()\n\n\th := minheap.New[int](alloc, int_greater)\n\n\t// Push some values onto the heap\n\th.Push(2)\n\th.Push(1)\n\th.Push(4)\n\th.Push(3)\n\th.Push(5)\n\n\t// Pop the max value from the heap\n\tfmt.Println(h.Pop())\n\tfmt.Println(h.Pop())\n\n}\n```\n\n#### Output\n\n```\n5\n4\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n## Index\n\n- [type MinHeap](\u003c#MinHeap\u003e)\n  - [func New\\[T any\\]\\(alloc allocator.Allocator, less func\\(a, b T\\) bool\\) \\*MinHeap\\[T\\]](\u003c#New\u003e)\n  - [func \\(h \\*MinHeap\\[T\\]\\) Free\\(\\)](\u003c#MinHeap[T].Free\u003e)\n  - [func \\(h \\*MinHeap\\[T\\]\\) Iter\\(\\) iter.Seq2\\[int, T\\]](\u003c#MinHeap[T].Iter\u003e)\n  - [func \\(h \\*MinHeap\\[T\\]\\) Len\\(\\) int](\u003c#MinHeap[T].Len\u003e)\n  - [func \\(h \\*MinHeap\\[T\\]\\) Peek\\(\\) T](\u003c#MinHeap[T].Peek\u003e)\n  - [func \\(h \\*MinHeap\\[T\\]\\) Pop\\(\\) T](\u003c#MinHeap[T].Pop\u003e)\n  - [func \\(h \\*MinHeap\\[T\\]\\) Push\\(value T\\)](\u003c#MinHeap[T].Push\u003e)\n  - [func \\(h \\*MinHeap\\[T\\]\\) Remove\\(f func\\(T\\) bool\\)](\u003c#MinHeap[T].Remove\u003e)\n\n\n\u003ca name=\"MinHeap\"\u003e\u003c/a\u003e\n## type [MinHeap](\u003chttps://github.com/joetifa2003/mm-go/blob/master/minheap/minheap.go#L10-L14\u003e)\n\n\n\n```go\ntype MinHeap[T any] struct {\n    // contains filtered or unexported fields\n}\n```\n\n\u003ca name=\"New\"\u003e\u003c/a\u003e\n### func [New](\u003chttps://github.com/joetifa2003/mm-go/blob/master/minheap/minheap.go#L17\u003e)\n\n```go\nfunc New[T any](alloc allocator.Allocator, less func(a, b T) bool) *MinHeap[T]\n```\n\nNew creates a new MinHeap.\n\n\u003ca name=\"MinHeap[T].Free\"\u003e\u003c/a\u003e\n### func \\(\\*MinHeap\\[T\\]\\) [Free](\u003chttps://github.com/joetifa2003/mm-go/blob/master/minheap/minheap.go#L57\u003e)\n\n```go\nfunc (h *MinHeap[T]) Free()\n```\n\nFree frees the heap.\n\n\u003ca name=\"MinHeap[T].Iter\"\u003e\u003c/a\u003e\n### func \\(\\*MinHeap\\[T\\]\\) [Iter](\u003chttps://github.com/joetifa2003/mm-go/blob/master/minheap/minheap.go#L125\u003e)\n\n```go\nfunc (h *MinHeap[T]) Iter() iter.Seq2[int, T]\n```\n\nIter returns an iterator over the elements of the heap.\n\n\u003ca name=\"MinHeap[T].Len\"\u003e\u003c/a\u003e\n### func \\(\\*MinHeap\\[T\\]\\) [Len](\u003chttps://github.com/joetifa2003/mm-go/blob/master/minheap/minheap.go#L52\u003e)\n\n```go\nfunc (h *MinHeap[T]) Len() int\n```\n\nLen returns the number of elements in the heap.\n\n\u003ca name=\"MinHeap[T].Peek\"\u003e\u003c/a\u003e\n### func \\(\\*MinHeap\\[T\\]\\) [Peek](\u003chttps://github.com/joetifa2003/mm-go/blob/master/minheap/minheap.go#L44\u003e)\n\n```go\nfunc (h *MinHeap[T]) Peek() T\n```\n\nPeek returns the minimum value from the heap without removing it.\n\n\u003ca name=\"MinHeap[T].Pop\"\u003e\u003c/a\u003e\n### func \\(\\*MinHeap\\[T\\]\\) [Pop](\u003chttps://github.com/joetifa2003/mm-go/blob/master/minheap/minheap.go#L32\u003e)\n\n```go\nfunc (h *MinHeap[T]) Pop() T\n```\n\nPop removes and returns the minimum value from the heap.\n\n\u003ca name=\"MinHeap[T].Push\"\u003e\u003c/a\u003e\n### func \\(\\*MinHeap\\[T\\]\\) [Push](\u003chttps://github.com/joetifa2003/mm-go/blob/master/minheap/minheap.go#L26\u003e)\n\n```go\nfunc (h *MinHeap[T]) Push(value T)\n```\n\nPush adds a value to the heap.\n\n\u003ca name=\"MinHeap[T].Remove\"\u003e\u003c/a\u003e\n### func \\(\\*MinHeap\\[T\\]\\) [Remove](\u003chttps://github.com/joetifa2003/mm-go/blob/master/minheap/minheap.go#L63\u003e)\n\n```go\nfunc (h *MinHeap[T]) Remove(f func(T) bool)\n```\n\nRemove the first element that makes f return true\n\n# mmstring\n\n```go\nimport \"github.com/joetifa2003/mm-go/mmstring\"\n```\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\u003cp\u003e\n\n\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/joetifa2003/mm-go/allocator\"\n\t\"github.com/joetifa2003/mm-go/mmstring\"\n)\n\nfunc main() {\n\talloc := allocator.NewC()\n\tdefer alloc.Destroy()\n\n\ts := mmstring.New(alloc)\n\tdefer s.Free()\n\n\ts.AppendGoString(\"Hello \")\n\ts.AppendGoString(\"World\")\n\n\ts2 := mmstring.From(alloc, \"Foo Bar\")\n\tdefer s2.Free()\n\n\tfmt.Println(s.GetGoString())\n\tfmt.Println(s2.GetGoString())\n\n}\n```\n\n#### Output\n\n```\nHello World\nFoo Bar\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eExample (Datastructures)\u003c/summary\u003e\n\u003cp\u003e\n\n\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/joetifa2003/mm-go/allocator\"\n\t\"github.com/joetifa2003/mm-go/batchallocator\"\n\t\"github.com/joetifa2003/mm-go/mmstring\"\n\t\"github.com/joetifa2003/mm-go/vector\"\n)\n\nfunc main() {\n\talloc := batchallocator.New(allocator.NewC())\n\tdefer alloc.Destroy() // all the memory allocated bellow will be freed, no need to free it manually.\n\n\tm := vector.New[*mmstring.MMString](alloc)\n\tm.Push(mmstring.From(alloc, \"hello\"))\n\tm.Push(mmstring.From(alloc, \"world\"))\n\n\tfor k, v := range m.Iter() {\n\t\tfmt.Println(k, v.GetGoString())\n\t}\n\n}\n```\n\n#### Output\n\n```\n0 hello\n1 world\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n## Index\n\n- [type MMString](\u003c#MMString\u003e)\n  - [func From\\(alloc allocator.Allocator, input string\\) \\*MMString](\u003c#From\u003e)\n  - [func New\\(alloc allocator.Allocator\\) \\*MMString](\u003c#New\u003e)\n  - [func \\(s \\*MMString\\) AppendGoString\\(input string\\)](\u003c#MMString.AppendGoString\u003e)\n  - [func \\(s \\*MMString\\) Free\\(\\)](\u003c#MMString.Free\u003e)\n  - [func \\(s \\*MMString\\) GetGoString\\(\\) string](\u003c#MMString.GetGoString\u003e)\n\n\n\u003ca name=\"MMString\"\u003e\u003c/a\u003e\n## type [MMString](\u003chttps://github.com/joetifa2003/mm-go/blob/master/mmstring/string.go#L10-L13\u003e)\n\nMMString is a manually manged string that is basically a \\*Vector\\[rune\\] and contains all the methods of a vector plus additional helper functions\n\n```go\ntype MMString struct {\n    // contains filtered or unexported fields\n}\n```\n\n\u003ca name=\"From\"\u003e\u003c/a\u003e\n### func [From](\u003chttps://github.com/joetifa2003/mm-go/blob/master/mmstring/string.go#L25\u003e)\n\n```go\nfunc From(alloc allocator.Allocator, input string) *MMString\n```\n\nFrom creates a new manually managed string, And initialize it with a go string\n\n\u003ca name=\"New\"\u003e\u003c/a\u003e\n### func [New](\u003chttps://github.com/joetifa2003/mm-go/blob/master/mmstring/string.go#L16\u003e)\n\n```go\nfunc New(alloc allocator.Allocator) *MMString\n```\n\nNew create a new manually managed string\n\n\u003ca name=\"MMString.AppendGoString\"\u003e\u003c/a\u003e\n### func \\(\\*MMString\\) [AppendGoString](\u003chttps://github.com/joetifa2003/mm-go/blob/master/mmstring/string.go#L43\u003e)\n\n```go\nfunc (s *MMString) AppendGoString(input string)\n```\n\nAppendGoString appends go string to manually managed string\n\n\u003ca name=\"MMString.Free\"\u003e\u003c/a\u003e\n### func \\(\\*MMString\\) [Free](\u003chttps://github.com/joetifa2003/mm-go/blob/master/mmstring/string.go#L50\u003e)\n\n```go\nfunc (s *MMString) Free()\n```\n\nFree frees MMString\n\n\u003ca name=\"MMString.GetGoString\"\u003e\u003c/a\u003e\n### func \\(\\*MMString\\) [GetGoString](\u003chttps://github.com/joetifa2003/mm-go/blob/master/mmstring/string.go#L37\u003e)\n\n```go\nfunc (s *MMString) GetGoString() string\n```\n\nGetGoString returns go string from manually managed string. CAUTION: You also have to free the MMString\n\n# typedarena\n\n```go\nimport \"github.com/joetifa2003/mm-go/typedarena\"\n```\n\ntypedarena is a growable typed arena that allocates memory in fixed chunks , it's faster that batchallocator but more limited, you can use batchallocator if you want to allocate multiple different types, and you want to use an arena like behavior spanning multiple datastructures \\(like vector, linkedlist, hashmap etc..\\), typedarena is much faster when you are only allocating one type.\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\u003cp\u003e\n\n\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/joetifa2003/mm-go/allocator\"\n\t\"github.com/joetifa2003/mm-go/typedarena\"\n)\n\ntype Entity struct {\n\tVelocityX float32\n\tVelocityY float32\n\tPositionX float32\n\tPositionY float32\n}\n\nfunc main() {\n\talloc := allocator.NewC()\n\tdefer alloc.Destroy()\n\n\tarena := typedarena.New[Entity](\n\t\talloc,\n\t\t10,\n\t)\n\tdefer arena.Free() // frees all memory\n\n\tfor i := 0; i \u003c 10; i++ {\n\t\te := arena.Alloc() // *Entity\n\t\te.VelocityX = float32(i)\n\t\te.VelocityY = float32(i)\n\t\te.PositionX = float32(i)\n\t\te.PositionY = float32(i)\n\t\tfmt.Println(e.VelocityX, e.VelocityY, e.PositionX, e.PositionY)\n\t}\n\n\tentities := arena.AllocMany(10) // allocate slice of 10 entities (cannot exceed 10 here because chunk size is 10 above, this limitation doesn't exist in batchallocator)\n\n\t_ = entities\n\n}\n```\n\n#### Output\n\n```\n0 0 0 0\n1 1 1 1\n2 2 2 2\n3 3 3 3\n4 4 4 4\n5 5 5 5\n6 6 6 6\n7 7 7 7\n8 8 8 8\n9 9 9 9\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n## Index\n\n- [type TypedArena](\u003c#TypedArena\u003e)\n  - [func New\\[T any\\]\\(alloc allocator.Allocator, chunkSize int\\) \\*TypedArena\\[T\\]](\u003c#New\u003e)\n  - [func \\(ta \\*TypedArena\\[T\\]\\) Alloc\\(\\) \\*T](\u003c#TypedArena[T].Alloc\u003e)\n  - [func \\(ta \\*TypedArena\\[T\\]\\) AllocMany\\(n int\\) \\[\\]T](\u003c#TypedArena[T].AllocMany\u003e)\n  - [func \\(ta \\*TypedArena\\[T\\]\\) Free\\(\\)](\u003c#TypedArena[T].Free\u003e)\n\n\n\u003ca name=\"TypedArena\"\u003e\u003c/a\u003e\n## type [TypedArena](\u003chttps://github.com/joetifa2003/mm-go/blob/master/typedarena/typed_arena.go#L40-L44\u003e)\n\nTypedArena is a growable typed arena\n\n```go\ntype TypedArena[T any] struct {\n    // contains filtered or unexported fields\n}\n```\n\n\u003ca name=\"New\"\u003e\u003c/a\u003e\n### func [New](\u003chttps://github.com/joetifa2003/mm-go/blob/master/typedarena/typed_arena.go#L51\u003e)\n\n```go\nfunc New[T any](alloc allocator.Allocator, chunkSize int) *TypedArena[T]\n```\n\nNew creates a typed arena with the specified chunk size. a chunk is the the unit of the arena, if T is int for example and the chunk size is 5, then each chunk is going to hold 5 ints. And if the chunk is filled it will allocate another chunk that can hold 5 ints. then you can call FreeArena and it will deallocate all chunks together\n\n\u003ca name=\"TypedArena[T].Alloc\"\u003e\u003c/a\u003e\n### func \\(\\*TypedArena\\[T\\]\\) [Alloc](\u003chttps://github.com/joetifa2003/mm-go/blob/master/typedarena/typed_arena.go#L64\u003e)\n\n```go\nfunc (ta *TypedArena[T]) Alloc() *T\n```\n\nAlloc allocates T from the arena\n\n\u003ca name=\"TypedArena[T].AllocMany\"\u003e\u003c/a\u003e\n### func \\(\\*TypedArena\\[T\\]\\) [AllocMany](\u003chttps://github.com/joetifa2003/mm-go/blob/master/typedarena/typed_arena.go#L78\u003e)\n\n```go\nfunc (ta *TypedArena[T]) AllocMany(n int) []T\n```\n\nAllocMany allocates n of T and returns a slice representing the heap. CAUTION: don't append to the slice, the purpose of it is to replace pointer arithmetic with slice indexing CAUTION: n cannot exceed chunk size\n\n\u003ca name=\"TypedArena[T].Free\"\u003e\u003c/a\u003e\n### func \\(\\*TypedArena\\[T\\]\\) [Free](\u003chttps://github.com/joetifa2003/mm-go/blob/master/typedarena/typed_arena.go#L94\u003e)\n\n```go\nfunc (ta *TypedArena[T]) Free()\n```\n\nFree frees all allocated memory\n\n# vector\n\n```go\nimport \"github.com/joetifa2003/mm-go/vector\"\n```\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\u003cp\u003e\n\n\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/joetifa2003/mm-go/allocator\"\n\t\"github.com/joetifa2003/mm-go/vector\"\n)\n\nfunc main() {\n\talloc := allocator.NewC()\n\tv := vector.New[int](alloc)\n\tv.Push(1)\n\tv.Push(2)\n\tv.Push(3)\n\n\tfmt.Println(\"Length:\", v.Len())\n\tfor i := 0; i \u003c v.Len(); i++ {\n\t\tfmt.Println(v.At(i))\n\t}\n\n\tfor _, k := range v.Iter() {\n\t\tfmt.Println(k)\n\t}\n\n}\n```\n\n#### Output\n\n```\nLength: 3\n1\n2\n3\n1\n2\n3\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n## Index\n\n- [type Vector](\u003c#Vector\u003e)\n  - [func Init\\[T any\\]\\(alloc allocator.Allocator, values ...T\\) \\*Vector\\[T\\]](\u003c#Init\u003e)\n  - [func New\\[T any\\]\\(aloc allocator.Allocator, args ...int\\) \\*Vector\\[T\\]](\u003c#New\u003e)\n  - [func \\(v \\*Vector\\[T\\]\\) At\\(idx int\\) T](\u003c#Vector[T].At\u003e)\n  - [func \\(v \\*Vector\\[T\\]\\) AtPtr\\(idx int\\) \\*T](\u003c#Vector[T].AtPtr\u003e)\n  - [func \\(v \\*Vector\\[T\\]\\) Cap\\(\\) int](\u003c#Vector[T].Cap\u003e)\n  - [func \\(v \\*Vector\\[T\\]\\) Free\\(\\)](\u003c#Vector[T].Free\u003e)\n  - [func \\(v \\*Vector\\[T\\]\\) Iter\\(\\) iter.Seq2\\[int, T\\]](\u003c#Vector[T].Iter\u003e)\n  - [func \\(v \\*Vector\\[T\\]\\) Last\\(\\) T](\u003c#Vector[T].Last\u003e)\n  - [func \\(v \\*Vector\\[T\\]\\) Len\\(\\) int](\u003c#Vector[T].Len\u003e)\n  - [func \\(v \\*Vector\\[T\\]\\) Pop\\(\\) T](\u003c#Vector[T].Pop\u003e)\n  - [func \\(v \\*Vector\\[T\\]\\) Push\\(value T\\)](\u003c#Vector[T].Push\u003e)\n  - [func \\(v \\*Vector\\[T\\]\\) RemoveAt\\(idx int\\) T](\u003c#Vector[T].RemoveAt\u003e)\n  - [func \\(v \\*Vector\\[T\\]\\) Set\\(idx int, value T\\)](\u003c#Vector[T].Set\u003e)\n  - [func \\(v \\*Vector\\[T\\]\\) Slice\\(\\) \\[\\]T](\u003c#Vector[T].Slice\u003e)\n  - [func \\(v \\*Vector\\[T\\]\\) UnsafeAt\\(idx int\\) T](\u003c#Vector[T].UnsafeAt\u003e)\n\n\n\u003ca name=\"Vector\"\u003e\u003c/a\u003e\n## type [Vector](\u003chttps://github.com/joetifa2003/mm-go/blob/master/vector/vector.go#L11-L15\u003e)\n\nVector a contiguous growable array type\n\n```go\ntype Vector[T any] struct {\n    // contains filtered or unexported fields\n}\n```\n\n\u003ca name=\"Init\"\u003e\u003c/a\u003e\n### func [Init](\u003chttps://github.com/joetifa2003/mm-go/blob/master/vector/vector.go#L43\u003e)\n\n```go\nfunc Init[T any](alloc allocator.Allocator, values ...T) *Vector[T]\n```\n\nInit initializes a new vector with the T elements provided and sets it's len and cap to len\\(values\\)\n\n\u003ca name=\"New\"\u003e\u003c/a\u003e\n### func [New](\u003chttps://github.com/joetifa2003/mm-go/blob/master/vector/vector.go#L30\u003e)\n\n```go\nfunc New[T any](aloc allocator.Allocator, args ...int) *Vector[T]\n```\n\nNew creates a new empty vector, if args not provided it will create an empty vector, if only one arg is provided it will init a vector with len and cap equal to the provided arg, if two args are provided it will init a vector with len = args\\[0\\] cap = args\\[1\\]\n\n\u003ca name=\"Vector[T].At\"\u003e\u003c/a\u003e\n### func \\(\\*Vector\\[T\\]\\) [At](\u003chttps://github.com/joetifa2003/mm-go/blob/master/vector/vector.go#L88\u003e)\n\n```go\nfunc (v *Vector[T]) At(idx int) T\n```\n\nAt gets element T at specified index\n\n\u003ca name=\"Vector[T].AtPtr\"\u003e\u003c/a\u003e\n### func \\(\\*Vector\\[T\\]\\) [AtPtr](\u003chttps://github.com/joetifa2003/mm-go/blob/master/vector/vector.go#L102\u003e)\n\n```go\nfunc (v *Vector[T]) AtPtr(idx int) *T\n```\n\nAtPtr gets element a pointer of T at specified index\n\n\u003ca name=\"Vector[T].Cap\"\u003e\u003c/a\u003e\n### func \\(\\*Vector\\[T\\]\\) [Cap](\u003chttps://github.com/joetifa2003/mm-go/blob/master/vector/vector.go#L71\u003e)\n\n```go\nfunc (v *Vector[T]) Cap() int\n```\n\nCap gets vector capacity \\(underling memory length\\).\n\n\u003ca name=\"Vector[T].Free\"\u003e\u003c/a\u003e\n### func \\(\\*Vector\\[T\\]\\) [Free](\u003chttps://github.com/joetifa2003/mm-go/blob/master/vector/vector.go#L120\u003e)\n\n```go\nfunc (v *Vector[T]) Free()\n```\n\nFree deallocats the vector\n\n\u003ca name=\"Vector[T].Iter\"\u003e\u003c/a\u003e\n### func \\(\\*Vector\\[T\\]\\) [Iter](\u003chttps://github.com/joetifa2003/mm-go/blob/master/vector/vector.go#L138\u003e)\n\n```go\nfunc (v *Vector[T]) Iter() iter.Seq2[int, T]\n```\n\nIter iterates over the vector\n\n\u003ca name=\"Vector[T].Last\"\u003e\u003c/a\u003e\n### func \\(\\*Vector\\[T\\]\\) [Last](\u003chttps://github.com/joetifa2003/mm-go/blob/master/vector/vector.go#L83\u003e)\n\n```go\nfunc (v *Vector[T]) Last() T\n```\n\nLast gets the last element from a vector\n\n\u003ca name=\"Vector[T].Len\"\u003e\u003c/a\u003e\n### func \\(\\*Vector\\[T\\]\\) [Len](\u003chttps://github.com/joetifa2003/mm-go/blob/master/vector/vector.go#L66\u003e)\n\n```go\nfunc (v *Vector[T]) Len() int\n```\n\nLen gets vector length\n\n\u003ca name=\"Vector[T].Pop\"\u003e\u003c/a\u003e\n### func \\(\\*Vector\\[T\\]\\) [Pop](\u003chttps://github.com/joetifa2003/mm-go/blob/master/vector/vector.go#L60\u003e)\n\n```go\nfunc (v *Vector[T]) Pop() T\n```\n\nPop pops value T from the vector and returns it\n\n\u003ca name=\"Vector[T].Push\"\u003e\u003c/a\u003e\n### func \\(\\*Vector\\[T\\]\\) [Push](\u003chttps://github.com/joetifa2003/mm-go/blob/master/vector/vector.go#L50\u003e)\n\n```go\nfunc (v *Vector[T]) Push(value T)\n```\n\nPush pushes value T to the vector, grows if needed.\n\n\u003ca name=\"Vector[T].RemoveAt\"\u003e\u003c/a\u003e\n### func \\(\\*Vector\\[T\\]\\) [RemoveAt](\u003chttps://github.com/joetifa2003/mm-go/blob/master/vector/vector.go#L125\u003e)\n\n```go\nfunc (v *Vector[T]) RemoveAt(idx int) T\n```\n\n\n\n\u003ca name=\"Vector[T].Set\"\u003e\u003c/a\u003e\n### func \\(\\*Vector\\[T\\]\\) [Set](\u003chttps://github.com/joetifa2003/mm-go/blob/master/vector/vector.go#L111\u003e)\n\n```go\nfunc (v *Vector[T]) Set(idx int, value T)\n```\n\nSet sets element T at specified index\n\n\u003ca name=\"Vector[T].Slice\"\u003e\u003c/a\u003e\n### func \\(\\*Vector\\[T\\]\\) [Slice](\u003chttps://github.com/joetifa2003/mm-go/blob/master/vector/vector.go#L78\u003e)\n\n```go\nfunc (v *Vector[T]) Slice() []T\n```\n\nSlice gets a slice representing the vector CAUTION: don't append to this slice, this is only used if you want to loop on the vec elements\n\n\u003ca name=\"Vector[T].UnsafeAt\"\u003e\u003c/a\u003e\n### func \\(\\*Vector\\[T\\]\\) [UnsafeAt](\u003chttps://github.com/joetifa2003/mm-go/blob/master/vector/vector.go#L97\u003e)\n\n```go\nfunc (v *Vector[T]) UnsafeAt(idx int) T\n```\n\nUnsafeAT gets element T at specified index without bounds checking\n\nGenerated by [gomarkdoc](\u003chttps://github.com/princjef/gomarkdoc\u003e)\n\n\n\u003c!-- gomarkdoc:embed:end --\u003e\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoetifa2003%2Fmm-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjoetifa2003%2Fmm-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoetifa2003%2Fmm-go/lists"}