-### C allocator -The C allocator is using CGO underthehood to call calloc, realloc and free. ```go -alloc := allocator.NewC() -defer alloc.Destroy() +fmt.Println(mm.SizeOf[int32]()) +fmt.Println(mm.SizeOf[int64]()) +// Output: +// 4 +// 8 +``` -ptr := allocator.Alloc[int](alloc) -defer allocator.Free(alloc, ptr) +#### Output -*ptr = 15 +``` +4 +8 +``` + +
++ -ptr := allocator.Alloc[int](alloc) -defer allocator.Free(alloc, ptr) +```go +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 + +fmt.Println(*ptr2) + +// Output: +// 15 +// 15 +``` + +#### Output + +``` +15 +15 +``` + +
+-But if you call `Free` the memory will be freed, so it acts as both Slab allocator and an Arena. -You can specify the size of chunks that are allocated by using options. ```go -alloc := batchallocator.New(allocator.NewC(), - batchallocator.WithBucketSize(mm.SizeOf[int]()*15), +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 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 ``` -For example this configures the batch allocator to allocate at minimum 15 ints at a time (by default it allocates ` page, which is usually 4kb). +
++ + ```go -alloc := batchallocator.New(allocator.NewC()) -defer alloc.Destroy() +package main -ptr := allocator.Alloc[int](alloc) -defer allocator.Free(alloc, ptr) +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) + allocator.FreeMany(alloc, heap) + + heap[3] = 100 + + fmt.Println(heap[0]) + fmt.Println(heap[1]) + fmt.Println(heap[3]) + +} +``` + +#### Output + +``` +15 +70 +100 ``` -Yes, go doesn't have generic on pointer receivers, so these had to be implemented as functions. +
++ + + ```go -type Node struct { - value int +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 soruce 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 } -alloc := batchallocator.New(allocator.NewC()) -ptr := allocator.Alloc[Node](alloc) // allocates a single Node struct and returns a ptr to it -defer allocator.Free(alloc, ptr) // frees the struct (defer recommended to prevent leaks) +func myallocator_destroy(allocator unsafe.Pointer) { +} ``` -#### AllocMany/FreeMany +
+-assert.Equal(45, heap[0]) -assert.Equal(0, heap[1]) + + +```go +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) + +} ``` -WARNING: Do not append to the slice, this is only used to avoid pointer arithmetic and unsafe code. +#### Output + +``` +15 +``` -### ReAlloc +
++ -arena := typedarena.New[int](alloc, 3) // 3 is the chunk size which gets preallocated, if you allocated more than 3 it will preallocate another chunk of 3 T -defer arena.Free() // freeing the arena using defer to prevent leaks -int1 := arena.Alloc() // allocates 1 int from arena -*int1 = 1 // changing it's value -ints := arena.AllocMany(2) // allocates 2 ints from the arena and returns a slice representing the heap (instead of pointer arithmetic) -ints[0] = 2 // changing the first value -ints[1] = 3 // changing the second value +```go +package main + +import ( + "github.com/joetifa2003/mm-go/allocator" + "github.com/joetifa2003/mm-go/batchallocator" +) -// 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 +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 -assert.Equal(1, *int1) -assert.Equal(2, len(ints)) -assert.Equal(15, ints[0]) -assert.Equal(3, ints[1]) + 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. +} ``` -### Why does this exists while there is BatchAllocator? +
++ + + +```go +alloc := batchallocator.New( + allocator.NewC(), + batchallocator.WithBucketSize(mm.SizeOf[int]()*15), // configure the allocator to allocate size of 15 ints in one go. +) defer alloc.Destroy() -arena := typedarena.New[int](alloc, 3) +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. +``` + +
+