-// you can also take pointers from the slice -intPtr1 := &ints[0] // taking pointer from the manually managed heap -*intPtr1 = 15 // changing the value using pointers -assert.Equal(1, *int1) -assert.Equal(2, len(ints)) -assert.Equal(15, ints[0]) -assert.Equal(3, ints[1]) + +```go +fmt.Println(mm.SizeOf[int32]()) +fmt.Println(mm.SizeOf[int64]()) +// Output: +// 4 +// 8 +``` + +#### Output + +``` +4 +8 ``` -## Alloc/Free +
++ + + ```go -type Node struct { - value int +package main + +import ( + "fmt" + + "github.com/joetifa2003/mm-go/allocator" +) + +func main() { + alloc := allocator.NewC() + defer alloc.Destroy() + + ptr := allocator.Alloc[int](alloc) + defer allocator.Free(alloc, ptr) + + *ptr = 15 + fmt.Println(*ptr) + } +``` -ptr := mm.Alloc[Node]() // allocates a single Node struct and returns a ptr to it -defer mm.Free(ptr) // frees the struct (defer recommended to prevent leaks) +#### Output + +``` +15 ``` -## AllocMany/FreeMany +
++ -AllocMany is a generic function that allocates n of T and returns a slice that represents the heap (instead of pointer arithmetic => slice indexing) that you can free later using FreeMany ```go -allocated := mm.AllocMany[int](2) // allocates 2 ints and returns it as a slice of ints with length 2 -defer mm.FreeMany(allocated) // it's recommended to make sure the data gets deallocated (defer recommended to prevent leaks) -assert.Equal(2, len(allocated)) -allocated[0] = 15 // changes the data in the slice (aka the heap) -ptr := &allocated[0] // takes a pointer to the first int in the heap -// Be careful if you do ptr := allocated[0] this will take a copy from the data on the heap -*ptr = 45 // changes the value from 15 to 45 +package main + +import ( + "fmt" + + "github.com/joetifa2003/mm-go/allocator" + "github.com/joetifa2003/mm-go/linkedlist" + "github.com/joetifa2003/mm-go/mmstring" + "github.com/joetifa2003/mm-go/vector" +) + +type MyStruct struct { + a int + b float32 +} + +func main() { + alloc := allocator.NewC() + defer alloc.Destroy() // all the memory allocated bellow will be freed, no need to free it manually. + + p := allocator.Alloc[MyStruct](alloc) + defer allocator.Free(alloc, p) + + p.a = 100 + p.b = 200 + + fmt.Println(*p) + + v := vector.New[int](alloc) + defer v.Free() + v.Push(15) + v.Push(70) + + for _, i := range v.Iter() { + fmt.Println(i) + } + + l := linkedlist.New[*mmstring.MMString](alloc) + defer l.Free() + l.PushBack(mmstring.From(alloc, "hello")) + l.PushBack(mmstring.From(alloc, "world")) + + for _, i := range l.Iter() { + fmt.Println(i.GetGoString()) + } + +} +``` + +#### Output -assert.Equal(45, allocated[0]) ``` +{100 200} +15 +70 +hello +world +``` + +
++ -A contiguous growable array type. -You can think of the Vector as a manually managed slice that you can put in manually managed structs, if you put a slice in a manually managed struct it will get collected because go GC doesn't see the manually allocated struct. ```go -v := vector.New[int]() -defer v.Free() +alloc := allocator.NewC() +defer alloc.Destroy() + +// So you can do this: +ptr := allocator.Alloc[int](alloc) // allocates a single int and returns a ptr to it +defer allocator.Free(alloc, ptr) // frees the int (defer recommended to prevent leaks) +*ptr = 15 +fmt.Println(*ptr) + +// instead of doing this: +ptr2 := (*int)(alloc.Alloc(mm.SizeOf[int]())) +defer alloc.Free(unsafe.Pointer(ptr2)) +*ptr2 = 15 -v.Push(1) -v.Push(2) -v.Push(3) +fmt.Println(*ptr2) -assert.Equal(3, v.Len()) -assert.Equal(4, v.Cap()) -assert.Equal([]int{1, 2, 3}, v.Slice()) -assert.Equal(3, v.Pop()) -assert.Equal(2, v.Pop()) -assert.Equal(1, v.Pop()) +// Output: +// 15 +// 15 ``` +#### Output + +``` +15 +15 +``` + +
++ + + ```go -v := vector.New[int](5) -defer v.Free() +package main + +import ( + "fmt" + + "github.com/joetifa2003/mm-go/allocator" +) + +func main() { + alloc := allocator.NewC() + defer alloc.Destroy() -assert.Equal(5, v.Len()) -assert.Equal(5, v.Cap()) + heap := allocator.AllocMany[int](alloc, 2) // allocates 2 ints and returns it as a slice of ints with length 2 + defer allocator.FreeMany(alloc, heap) // it's recommended to make sure the data gets deallocated (defer recommended to prevent leaks) + + heap[0] = 15 // changes the data in the slice (aka the heap) + ptr := &heap[0] // takes a pointer to the first int in the heap + // Be careful if you do ptr := heap[0] this will take a copy from the data on the heap + *ptr = 45 // changes the value from 15 to 45 + heap[1] = 70 + + fmt.Println(heap[0]) + fmt.Println(heap[1]) + +} +``` + +#### Output + +``` +45 +70 ``` +
++ + + ```go -v := vector.Init(1, 2, 3) -defer v.Free() +package main + +import ( + "fmt" + + "github.com/joetifa2003/mm-go/allocator" +) + +func main() { + alloc := allocator.NewC() + defer alloc.Destroy() + + heap := allocator.AllocMany[int](alloc, 2) // allocates 2 int and returns it as a slice of ints with length 2 + + heap[0] = 15 + heap[1] = 70 + + heap = allocator.Realloc(alloc, heap, 3) + heap[2] = 100 + + fmt.Println(heap[0]) + fmt.Println(heap[1]) + fmt.Println(heap[2]) + + allocator.FreeMany(alloc, heap) + +} +``` -assert.Equal(3, v.Len()) -assert.Equal(3, v.Cap()) +#### Output -assert.Equal(3, v.Pop()) -assert.Equal(2, v.Pop()) -assert.Equal(1, v.Pop()) ``` +15 +70 +100 +``` + +
++ + ```go -// Push pushes value T to the vector, grows if needed. -func (v *Vector[T]) Push(value T) +package main + +import ( + "unsafe" + + "github.com/joetifa2003/mm-go/allocator" +) + +func main() { + // Create a custom allocator + alloc := allocator.NewAllocator( + nil, + myallocator_alloc, + myallocator_free, + myallocator_realloc, + myallocator_destroy, + ) + + // Check how C allocator is implemented + // or batchallocator source for a reference + + _ = alloc +} + +func myallocator_alloc(allocator unsafe.Pointer, size int) unsafe.Pointer { + return nil +} + +func myallocator_free(allocator unsafe.Pointer, ptr unsafe.Pointer) { +} + +func myallocator_realloc(allocator unsafe.Pointer, ptr unsafe.Pointer, size int) unsafe.Pointer { + return nil +} + +func myallocator_destroy(allocator unsafe.Pointer) { +} ``` -#### Pop +
++ + ```go -// AtPtr gets element a pointer of T at specified index -func (v *Vector[T]) AtPtr(idx int) *T +package main + +import ( + "github.com/joetifa2003/mm-go/allocator" + "github.com/joetifa2003/mm-go/batchallocator" +) + +func main() { + alloc := batchallocator.New(allocator.NewC()) // by default it allocates page, which is usually 4kb + defer alloc.Destroy() // this frees all memory allocated by the allocator automatically + + ptr := allocator.Alloc[int](alloc) + // but you can still free the pointers manually if you want (will free buckets of memory if all pointers depending on it is freed) + defer allocator.Free(alloc, ptr) // this can removed and the memory will be freed. +} ``` -#### Free +
++ + ```go -// Free deallocats the vector -func (v *Vector[T]) Free() +package main + +import ( + "fmt" + + "github.com/joetifa2003/mm-go/allocator" + "github.com/joetifa2003/mm-go/batchallocator" + "github.com/joetifa2003/mm-go/linkedlist" + "github.com/joetifa2003/mm-go/mmstring" + "github.com/joetifa2003/mm-go/vector" +) + +func main() { + alloc := batchallocator.New(allocator.NewC()) + defer alloc.Destroy() // all the memory allocated bellow will be freed, no need to free it manually. + + v := vector.New[int](alloc) + v.Push(15) + v.Push(70) + + for _, i := range v.Iter() { + fmt.Println(i) + } + + l := linkedlist.New[*mmstring.MMString](alloc) + l.PushBack(mmstring.From(alloc, "hello")) + l.PushBack(mmstring.From(alloc, "world")) + + for _, i := range l.Iter() { + fmt.Println(i.GetGoString()) + } + +} ``` -## linkedlist +#### Output -LinkedList a doubly-linked list. -Note: can be a lot slower than Vector but sometimes faster in specific use cases +``` +15 +70 +hello +world +``` + +
++ + ```go -// PopFront pops and returns value T from the front of the linked list. -func (ll *LinkedList[T]) PopFront() T +alloc := batchallocator.New( + allocator.NewC(), + batchallocator.WithBucketSize(mm.SizeOf[int]()*15), // configure the allocator to allocate size of 15 ints per bucket. +) +defer alloc.Destroy() + +ptr := allocator.Alloc[int](alloc) +defer allocator.Free(alloc, ptr) // this can be removed and the memory will still be freed on Destroy. + +ptr2 := allocator.Alloc[int](alloc) // will not call CGO because there is still enough memory in the Bucket. +defer allocator.Free(alloc, ptr2) // this can be removed and the memory will still be freed on Destroy. ``` -#### ForEach +
++ + ```go -// At gets value T at idx. -func (ll *LinkedList[T]) At(idx int) T +alloc := batchallocator.New(allocator.NewC()) +defer alloc.Destroy() + +hm := New[int, int](alloc) +defer hm.Free() // can be removed + +hm.Set(1, 10) +hm.Set(2, 20) +hm.Set(3, 30) + +sumKeys := 0 +sumValues := 0 +for k, v := range hm.Iter() { + sumKeys += k + sumValues += v +} + +fmt.Println(sumKeys) +fmt.Println(sumValues) + +// Output: +// 6 +// 60 +``` + +#### Output + +``` +6 +60 ``` -#### AtPtr +
++ + + +```go +alloc := allocator.NewC() +defer alloc.Destroy() + +ll := New[int](alloc) +defer ll.Free() + +ll.PushBack(1) +ll.PushBack(2) +ll.PushBack(3) +ll.PushBack(4) + +fmt.Println("PopBack:", ll.PopBack()) +fmt.Println("PopFront:", ll.PopFront()) + +for _, i := range ll.Iter() { + fmt.Println(i) +} + +// Output: +// PopBack: 4 +// PopFront: 1 +// 2 +// 3 +``` + +#### Output + +``` +PopBack: 4 +PopFront: 1 +2 +3 +``` + +
++ + + +```go +package main + +import ( + "fmt" + + "github.com/joetifa2003/mm-go/allocator" + "github.com/joetifa2003/mm-go/minheap" +) + +func int_less(a, b int) bool { return a < b } + +func main() { + alloc := allocator.NewC() + defer alloc.Destroy() + + h := minheap.New[int](alloc, int_less) + + // Push some values onto the heap + h.Push(2) + h.Push(1) + h.Push(4) + h.Push(3) + h.Push(5) + + // Pop the minimum value from the heap + fmt.Println(h.Pop()) + fmt.Println(h.Pop()) + +} +``` + +#### Output + +``` +1 +2 +``` + +
++ + + +```go +package main + +import ( + "fmt" + + "github.com/joetifa2003/mm-go/allocator" + "github.com/joetifa2003/mm-go/minheap" +) + +func int_greater(a, b int) bool { return a > b } + +func main() { + alloc := allocator.NewC() + defer alloc.Destroy() + + h := minheap.New[int](alloc, int_greater) + + // Push some values onto the heap + h.Push(2) + h.Push(1) + h.Push(4) + h.Push(3) + h.Push(5) + + // Pop the max value from the heap + fmt.Println(h.Pop()) + fmt.Println(h.Pop()) + +} +``` + +#### Output + +``` +5 +4 +``` + +
++ + + +```go +package main + +import ( + "fmt" + + "github.com/joetifa2003/mm-go/allocator" + "github.com/joetifa2003/mm-go/mmstring" +) + +func main() { + alloc := allocator.NewC() + defer alloc.Destroy() + + s := mmstring.New(alloc) + defer s.Free() + + s.AppendGoString("Hello ") + s.AppendGoString("World") + + s2 := mmstring.From(alloc, "Foo Bar") + defer s2.Free() + + fmt.Println(s.GetGoString()) + fmt.Println(s2.GetGoString()) + +} +``` + +#### Output + +``` +Hello World +Foo Bar +``` + +
++ + + +```go +package main + +import ( + "fmt" + + "github.com/joetifa2003/mm-go/allocator" + "github.com/joetifa2003/mm-go/batchallocator" + "github.com/joetifa2003/mm-go/mmstring" + "github.com/joetifa2003/mm-go/vector" +) + +func main() { + alloc := batchallocator.New(allocator.NewC()) + defer alloc.Destroy() // all the memory allocated bellow will be freed, no need to free it manually. + + m := vector.New[*mmstring.MMString](alloc) + m.Push(mmstring.From(alloc, "hello")) + m.Push(mmstring.From(alloc, "world")) + + for k, v := range m.Iter() { + fmt.Println(k, v.GetGoString()) + } + +} +``` + +#### Output + +``` +0 hello +1 world +``` + +
++ + + +```go +package main + +import ( + "fmt" + + "github.com/joetifa2003/mm-go/allocator" + "github.com/joetifa2003/mm-go/typedarena" +) + +type Entity struct { + VelocityX float32 + VelocityY float32 + PositionX float32 + PositionY float32 +} + +func main() { + alloc := allocator.NewC() + defer alloc.Destroy() + + arena := typedarena.New[Entity]( + alloc, + 10, + ) + defer arena.Free() // frees all memory + + for i := 0; i < 10; i++ { + e := arena.Alloc() // *Entity + e.VelocityX = float32(i) + e.VelocityY = float32(i) + e.PositionX = float32(i) + e.PositionY = float32(i) + fmt.Println(e.VelocityX, e.VelocityY, e.PositionX, e.PositionY) + } + + entities := 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) + + _ = entities + +} +``` + +#### Output + +``` +0 0 0 0 +1 1 1 1 +2 2 2 2 +3 3 3 3 +4 4 4 4 +5 5 5 5 +6 6 6 6 +7 7 7 7 +8 8 8 8 +9 9 9 9 +``` + +
++ + + +```go +package main + +import ( + "fmt" + + "github.com/joetifa2003/mm-go/allocator" + "github.com/joetifa2003/mm-go/vector" +) + +func main() { + alloc := allocator.NewC() + v := vector.New[int](alloc) + v.Push(1) + v.Push(2) + v.Push(3) + + fmt.Println("Length:", v.Len()) + for i := 0; i < v.Len(); i++ { + fmt.Println(v.At(i)) + } + + for _, k := range v.Iter() { + fmt.Println(k) + } + +} +``` + +#### Output + +``` +Length: 3 +1 +2 +3 +1 +2 +3 +``` + +
+