-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmemory_arena.go
109 lines (93 loc) · 3.03 KB
/
memory_arena.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package memoryArena
import (
"fmt"
"reflect"
"unsafe"
)
// memory: A byte array that holds the actual memory
// size the total size of the memory arena
// offset the amount of memory currently in use
type MemoryArenaBuffer struct {
memory []byte
size int
offset int
}
// this function creates a new memory arena buffer of a specified size
func NewMemoryArenaBuffer(size int) *MemoryArenaBuffer {
return &MemoryArenaBuffer{
memory: make([]byte, size),
size: size,
offset: 0,
}
}
type MemoryArena[T any] struct {
buffer MemoryArenaBuffer
}
// this function creates a new memory arena of a specified size
// it allocates a block of memory and initializes the arena's properties
func NewMemoryArena[T any](size int) (*MemoryArena[T], error) {
if size <= 0 {
return nil, fmt.Errorf("cannot initialize, size below 0")
}
arena := MemoryArena[T]{
buffer: *NewMemoryArenaBuffer(size),
}
return &arena, nil
}
// this function aligns the offset to the specified alignment
func (arena *MemoryArena[T]) alignOffset(alignment uintptr) {
if (arena.buffer.offset % int(alignment)) != 0 {
arena.buffer.offset = (arena.buffer.offset + int(alignment-1)) &^ (int(alignment) - 1)
}
}
// Remaining capacity of the arena
func (arena *MemoryArena[T]) UsedCapacity(size int) int {
return arena.buffer.offset + size
}
// checking boundries of the arena
func (arena *MemoryArena[T]) IsWithinBounds(size int) bool {
return arena.UsedCapacity(size) > arena.buffer.size
}
// this function is used to allocate memory from the arena
// it checks if there's enough space left in the arena
// if there is enough space, it returns a pointer to the available memory and updates the used amount
// if there is not enough space, it returns null(or some error indicator)
func (arena *MemoryArena[T]) Allocate(size int) (unsafe.Pointer, error) {
if size <= 0 {
return nil, fmt.Errorf("allocation size must be greater than 0")
}
alignment := unsafe.Alignof(new(T))
arena.alignOffset(alignment)
if arena.IsWithinBounds(size) {
return nil, fmt.Errorf("not enough space left in the arena")
}
result := unsafe.Pointer(&arena.buffer.memory[arena.buffer.offset])
arena.buffer.offset += size
return result, nil
}
// this function frees the memory in the arena by setting all the bytes to 0
func (arena *MemoryArena[T]) Free() {
for i := range arena.buffer.memory {
arena.buffer.memory[i] = 0
}
}
// this function resets the arena by setting the offset to 0
func (arena *MemoryArena[T]) Reset() {
arena.buffer.offset = 0
arena.Free()
}
// AllocateObject allocates memory for the given object and returns a pointer to the allocated memory.
func (arena *MemoryArena[T]) AllocateObject(obj interface{}) (unsafe.Pointer, error) {
size := reflect.TypeOf(obj).Size()
// Allocate memory
ptr, err := arena.Allocate(int(size))
if err != nil {
return nil, fmt.Errorf("allocation failed due to insufficient memory")
}
// Create a new value at the allocated memory and copy the object into it
ptr, err = SetNewValue(&ptr, obj)
if err != nil {
return nil, err
}
return ptr, nil
}