Skip to content

Commit

Permalink
feat: docs and update README
Browse files Browse the repository at this point in the history
  • Loading branch information
joetifa2003 committed Sep 14, 2024

Verified

This commit was signed with the committer’s verified signature.
lucacome Luca Comellini
1 parent f5dc88e commit bc6ab38
Showing 8 changed files with 1,273 additions and 201 deletions.
1,293 changes: 1,092 additions & 201 deletions README.md

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions allocator/allocator.go
Original file line number Diff line number Diff line change
@@ -2,6 +2,8 @@ package allocator

import "unsafe"

// Allocator 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).
type Allocator struct {
allocator unsafe.Pointer
alloc func(allocator unsafe.Pointer, size int) unsafe.Pointer
@@ -10,6 +12,7 @@ type Allocator struct {
destroy func(allocator unsafe.Pointer)
}

// NewAllocator creates a new Allocator
func NewAllocator(
allocator unsafe.Pointer,
alloc func(allocator unsafe.Pointer, size int) unsafe.Pointer,
@@ -26,18 +29,24 @@ func NewAllocator(
}
}

// Alloc allocates size bytes and returns an unsafe pointer to it.
func (a Allocator) Alloc(size int) unsafe.Pointer {
return a.alloc(a.allocator, size)
}

// Free frees the memory pointed by ptr
func (a Allocator) Free(ptr unsafe.Pointer) {
a.free(a.allocator, ptr)
}

// Realloc reallocates the memory pointed by ptr with a new size and returns a new pointer to it.
func (a Allocator) Realloc(ptr unsafe.Pointer, size int) unsafe.Pointer {
return a.realloc(a.allocator, ptr, size)
}

// Destroy destroys the allocator.
// After calling this, the allocator is no longer usable.
// This is useful for cleanup, freeing allocator internal resources, etc.
func (a Allocator) Destroy() {
a.destroy(a.allocator)
}
1 change: 1 addition & 0 deletions allocator/callocator.go
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import "C"

import "unsafe"

// NewC returns an allocator that uses C calloc, realloc and free.
func NewC() Allocator {
return NewAllocator(nil, callocator_alloc, callocator_free, callocator_realloc, callocator_destroy)
}
119 changes: 119 additions & 0 deletions allocator/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package allocator_test

import (
"fmt"
"unsafe"

"github.com/joetifa2003/mm-go"
"github.com/joetifa2003/mm-go/allocator"
)

func ExampleNewC() {
alloc := allocator.NewC()
defer alloc.Destroy()

ptr := allocator.Alloc[int](alloc)
defer allocator.Free(alloc, ptr)

*ptr = 15
fmt.Println(*ptr)

// Output: 15
}

func ExampleAlloc() {
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
}

func ExampleAllocMany() {
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
}

func ExampleRealloc() {
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
}

func ExampleNewAllocator() {
// 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
}

func myallocator_destroy(allocator unsafe.Pointer) {
}
5 changes: 5 additions & 0 deletions batchallocator/batch.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
// This 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.
package batchallocator

import (
31 changes: 31 additions & 0 deletions batchallocator/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package batchallocator_test

import (
"github.com/joetifa2003/mm-go"
"github.com/joetifa2003/mm-go/allocator"
"github.com/joetifa2003/mm-go/batchallocator"
)

func Example() {
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.
}

func ExampleWithBucketSize() {
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()

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.

}
15 changes: 15 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package mm_test

import (
"fmt"

"github.com/joetifa2003/mm-go"
)

func ExampleSizeOf() {
fmt.Println(mm.SizeOf[int32]())
fmt.Println(mm.SizeOf[int64]())
// Output:
// 4
// 8
}
1 change: 1 addition & 0 deletions mm.go
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ package mm

import "unsafe"

// SizeOf returns the size of T in bytes
func SizeOf[T any]() int {
var zeroV T
return int(unsafe.Sizeof(zeroV))

0 comments on commit bc6ab38

Please sign in to comment.