Skip to content

Commit

Permalink
feat: add vectors
Browse files Browse the repository at this point in the history
  • Loading branch information
joetifa2003 committed Dec 1, 2022
1 parent c18bf92 commit dc82206
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 5 deletions.
24 changes: 19 additions & 5 deletions mm.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,39 @@ import (
// #include <stdlib.h>
import "C"

func c_calloc(n int, size int) unsafe.Pointer {
return C.calloc(C.size_t(n), C.size_t(size))
}

func c_free(ptr unsafe.Pointer) {
C.free(ptr)
}

func c_realloc(ptr unsafe.Pointer, newSize int) unsafe.Pointer {
return C.realloc(ptr, C.size_t(newSize))
}

// Alloc allocates T and returns a pointer to it.
func Alloc[T any]() *T {
var zeroV T
size := int(unsafe.Sizeof(zeroV))
ptr := C.calloc(1, C.size_t(size))
ptr := c_calloc(1, size)
return (*T)(unsafe.Pointer(ptr))
}

// FreeMany frees memory allocated by Alloc takes a ptr
// CAUTION: be careful not to double free, and prefer using defer to deallocate
func Free[T any](ptr *T) {
C.free(unsafe.Pointer(ptr))
c_free(unsafe.Pointer(ptr))
}

// AllocMany 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
func AllocMany[T any](n int) []T {
var zeroV T
size := int(unsafe.Sizeof(zeroV))
ptr := C.calloc(C.size_t(n), C.size_t(size))
ptr := c_calloc(n, size)
return unsafe.Slice(
(*T)(ptr),
n,
Expand All @@ -35,14 +49,14 @@ func AllocMany[T any](n int) []T {
// FreeMany 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
func FreeMany[T any](slice []T) {
C.free(unsafe.Pointer(&slice[0]))
c_free(unsafe.Pointer(&slice[0]))
}

// Reallocate reallocates memory allocated with AllocMany and doesn't change underling data
func Reallocate[T any](slice []T, newN int) []T {
var zeroV T
size := int(unsafe.Sizeof(zeroV))
ptr := C.realloc(unsafe.Pointer(&slice[0]), C.size_t(size*newN))
ptr := c_realloc(unsafe.Pointer(&slice[0]), size*newN)
return unsafe.Slice(
(*T)(ptr),
newN,
Expand Down
2 changes: 2 additions & 0 deletions mm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ func BenchmarkArenaManual(b *testing.B) {
}
}

const LOOP_TIMES = 1500

func TestAllocMany(t *testing.T) {
assert := assert.New(t)

Expand Down
81 changes: 81 additions & 0 deletions vector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package mm

type Vector[T any] struct {
data []T
cap int
len int
}

func createVector[T any](len int, cap int) *Vector[T] {
vector := Alloc[Vector[T]]()
vector.cap = cap
vector.len = len
vector.data = AllocMany[T](vector.cap)

return vector
}

// NewVector 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]
func NewVector[T any](args ...int) *Vector[T] {
switch len(args) {
case 0:
return createVector[T](0, 1)
case 1:
return createVector[T](args[0], args[0])
default:
return createVector[T](args[0], args[1])
}
}

// InitVector initializes a new vector with the T elements provided and sets
// it's len and cap to len(values)
func InitVector[T any](values ...T) *Vector[T] {
vector := createVector[T](len(values), len(values))
for _, v := range values {
vector.data[0] = v
}
return vector
}

// Push pushes T to the vector, grows if needed.
func (v *Vector[T]) Push(value T) {
if v.len == v.cap {
v.data = Reallocate(v.data, v.cap*2)
v.cap *= 2
}

v.data[v.len] = value
v.len++
}

// Pop pops T from the vector
func (v *Vector[T]) Pop() T {
v.len--
return v.data[v.len]
}

// Len gets vector len
func (v *Vector[T]) Len() int {
return v.len
}

// Cap gets vector capacity (underling memory length).
func (v *Vector[T]) Cap() int {
return v.cap
}

// Slice 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
func (v *Vector[T]) Slice() []T {
return v.data[:v.len]
}

// Deallocate deallocats the vector
func (v *Vector[T]) Deallocate() {
FreeMany(v.data)
Free(v)
}
97 changes: 97 additions & 0 deletions vector_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package mm

import (
"testing"

"github.com/stretchr/testify/assert"
)

func BenchmarkSlice(b *testing.B) {
for i := 0; i <= b.N; i++ {
numbers := []int{}

for j := 0; j < LOOP_TIMES; j++ {
numbers = append(numbers, j)
}

for j := 0; j < LOOP_TIMES; j++ {
// Pop
numbers = numbers[:len(numbers)-1]
}
}
}

func BenchmarkVector(b *testing.B) {
for i := 0; i <= b.N; i++ {
numbersVec := NewVector[int]()
defer numbersVec.Deallocate()

for j := 0; j < LOOP_TIMES; j++ {
numbersVec.Push(j)
}

for j := 0; j < LOOP_TIMES; j++ {
numbersVec.Pop()
}
}
}

func TestVector(t *testing.T) {
assert := assert.New(t)

v := NewVector[int]()
defer v.Deallocate()

v.Push(1)
v.Push(2)
v.Push(3)

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())
}

func TestVectorInit(t *testing.T) {
t.Run("Init with no args", func(t *testing.T) {
assert := assert.New(t)

v := NewVector[int]()
defer v.Deallocate()

assert.Equal(0, v.Len())
assert.Equal(1, v.Cap())
})

t.Run("Init with one arg", func(t *testing.T) {
assert := assert.New(t)

v := NewVector[int](5)
defer v.Deallocate()

assert.Equal(5, v.Len())
assert.Equal(5, v.Cap())
})

t.Run("Init with two args", func(t *testing.T) {
assert := assert.New(t)

v := NewVector[int](5, 6)
defer v.Deallocate()

assert.Equal(5, v.Len())
assert.Equal(6, v.Cap())
})

t.Run("Init vector with slice", func(t *testing.T) {
assert := assert.New(t)

v := InitVector(1, 2, 3)
defer v.Deallocate()

assert.Equal(3, v.Len())
assert.Equal(3, v.Cap())
})
}

0 comments on commit dc82206

Please sign in to comment.