Skip to content

Commit

Permalink
Introduce allocator system
Browse files Browse the repository at this point in the history
  • Loading branch information
edubart committed Feb 8, 2020
1 parent 440baca commit 096c3c9
Show file tree
Hide file tree
Showing 11 changed files with 455 additions and 298 deletions.
7 changes: 4 additions & 3 deletions docs/pages/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -790,14 +790,15 @@ print($ap) -- outputs 3
Memory can be allocated using C malloc and free.

```nelua
require 'C.stdlib'
require 'memory'
require 'allocators.generic_allocator'
local Person = @record{name: string, age: integer}
local p = (@Person*)(C.malloc(#Person))
local p: Person* = generic_allocator.new(@Person)
p.name = "John"
p.age = 20
print(p.name, p.age)
C.free(p)
generic_allocator.delete(p)
p = nilptr
```

Expand Down
13 changes: 8 additions & 5 deletions examples/linkedlist.nelua
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
## strict = true

require 'memory'
require 'allocators.generic_allocator'

local allocator = @generic_allocator

## local memoize = require 'nelua.utils.memoize'
## local list = hygienize(memoize(function(T)
Expand All @@ -12,7 +15,7 @@ require 'memory'
}

local function new_node(prev: ListNode*, next: ListNode*, value: T): ListNode*
local node = memory.new(@ListNode)
local node = allocator.new(@ListNode)
node.prev = prev
node.next = next
node.value = value
Expand Down Expand Up @@ -77,7 +80,7 @@ require 'memory'
node.next.prev = node.prev
end
local next = node.next
memory.delete(node)
allocator.delete(node)
return next
end

Expand All @@ -91,7 +94,7 @@ require 'memory'
if unlikely(self.tail == node) then
self.tail = nilptr
end
memory.delete(node)
allocator.delete(node)
end

function List:remove_last()
Expand All @@ -104,14 +107,14 @@ require 'memory'
if unlikely(self.head == node) then
self.head = nilptr
end
memory.delete(node)
allocator.delete(node)
end

function List:clear()
local it = self.head
while it do
local next = it.next
memory.delete(it)
allocator.delete(it)
it = next
end
self.head = nilptr
Expand Down
5 changes: 3 additions & 2 deletions examples/overview.nelua
Original file line number Diff line number Diff line change
Expand Up @@ -393,14 +393,15 @@ do -- Dereferencing and referencing
end

require 'memory'
require 'allocators.generic_allocator'

do -- Allocating memory
local Person = @record{name: string, age: integer}
local p: Person* = memory.new(@Person)
local p: Person* = generic_allocator.new(@Person)
p.name = "John"
p.age = 20
print(p.name, p.age)
memory.delete(p)
generic_allocator.delete(p)
p = nilptr
end

Expand Down
109 changes: 109 additions & 0 deletions lib/allocators/allocator_interface.nelua
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
##[[
strict = true
unitname = 'nelua'
--------------------------------------------------------------------------------
-- compile time checks utilities
local function check_span_subtype(v)
staticassert(v.type:is_span(),
"in argument '%s': expected 'span(T)' but got type '%s'",
v.name, v.type)
return v.type.subtype
end
local function check_type_match(a, b)
staticassert(a.type == b.type,
"argument '%s' type '%s' differs from argument '%s' type '%s",
a.name, b.name, a.type, b.type)
end
local function check_span_subtype_match(s, x)
local stype = check_span_subtype(s)
staticassert(stype == x.type,
"in argument '%s': type '%s' differs from argument '%s' type '%s",
s.name, x.name, stype, x.type)
end
]]

## function implement_allocator_interface(allocator)

local allocator = #[allocator]#

function allocator.spanalloc(T: type, size: usize)
local s: span(T)
if likely(size > 0_u) then
s.data = (@T*)(allocator.alloc(size * #T))
check(s.data, 'allocator.spanalloc: allocation fail')
s.size = size
end
return s
end

function allocator.spanalloc0(T: type, size: usize)
local s: span(T)
if likely(size > 0_u) then
s.data = (@T*)(allocator.alloc0(size * #T))
check(s.data, 'allocator.spanalloc0: allocation fail')
s.size = size
end
return s
end

function allocator.spanrealloc(s: auto, size: usize)
local T: type = #[check_span_subtype(s)]#
if unlikely(size == 0) then
allocator.dealloc(s.data)
s.data = nilptr
s.size = 0
else
s.data = (@T*)(allocator.realloc(s.data, size * #T))
check(s.data, 'allocator.spanrealloc0: allocation fail')
s.size = size
end
return s
end

function allocator.spanrealloc0(s: auto, size: usize)
local T: type = #[check_span_subtype(s)]#
if unlikely(size == 0) then
allocator.dealloc(s.data)
s.data = nilptr
s.size = 0
else
s.data = (@T*)(allocator.realloc(s.data, size * #T))
check(s.data, 'allocator.spanrealloc0: allocation fail')
if likely(size > s.size) then
memset(&s[s.size], 0, (size - s.size) * #T)
end
s.size = size
end
return s
end

function allocator.spandealloc(s: auto)
## check_span_subtype(s)
allocator.dealloc(s.data)
end

function allocator.new(T: type, size: auto)
## if not size.type:is_nil() then
## staticassert(size.type:is_integral(), 'allocator.new: size must be an integral type')
## if not size.type:is_unsigned() then
check(size > 0, 'allocator.new: size must be greater than 0')
## end
return allocator.spanalloc0(T, (@usize)(size))
## else
return (@T*)(allocator.alloc0(#T))
## end
end

function allocator.delete(s: auto)
## staticassert(s.type:is_pointer() or s.type:is_span(), "allocator.delete: invalid type '%s'", s.type)
## if s.type:is_span() then
allocator.spandealloc(s)
## else -- pointer
allocator.dealloc(s)
## end
end

## end
41 changes: 41 additions & 0 deletions lib/allocators/gc_allocator.nelua
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
## strict = true
## unitname = 'nelua'

require 'gc'
require 'allocators.allocator_interface'

global gc_allocator = @record{}

function gc_allocator.alloc(size: usize): pointer
check(size > 0_u, 'gc_allocator.alloc: size cannot be zero')
local p: pointer = gc:alloc(size)
check(p, 'gc_allocator.alloc: allocation fail')
return p
end

function gc_allocator.alloc0(size: usize): pointer
check(size > 0_u, 'gc_allocator.alloc0: size must be greater than 0')
local p: pointer = gc:alloc0(size)
check(p, 'gc_allocator.alloc0: allocation fail')
return p
end

function gc_allocator.realloc(p: pointer, size: usize): pointer
check(size > 0_u, 'gc_allocator.realloc: size must be greater than 0')
p = gc:realloc(p, size)
check(size == 0 or p, 'gc_allocator.realloc: allocation fail')
return p
end

function gc_allocator.realloc0(p: pointer, newsize: usize, oldsize: usize): pointer
check(newsize > 0_u, 'gc_allocator.realloc0: size must be greater than 0')
p = gc:realloc0(p, newsize, oldsize)
check(newsize == 0_u or p, 'gc_allocator.realloc0: allocation fail')
return p
end

function gc_allocator.dealloc(p: pointer)
gc:dealloc(p)
end

## implement_allocator_interface(gc_allocator)
49 changes: 49 additions & 0 deletions lib/allocators/generic_allocator.nelua
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
## strict = true
## unitname = 'nelua'

require 'allocators.allocator_interface'

local function memset(s: pointer, c: cint, n: csize): pointer <cimport'memset',cinclude'<string.h>',nodecl> end
local function malloc(size: csize): pointer <cimport'malloc',cinclude'<stdlib.h>',nodecl> end
local function calloc(nmemb: csize, size: csize): pointer <cimport'calloc',cinclude'<stdlib.h>',nodecl> end
local function realloc(ptr: pointer, size: csize): pointer <cimport'realloc',cinclude'<stdlib.h>',nodecl> end
local function free(ptr: pointer): void <cimport'free',cinclude'<stdlib.h>',nodecl> end

global generic_allocator = @record{}

function generic_allocator.alloc(size: usize): pointer
check(size > 0_u, 'generic_allocator.alloc: size cannot be zero')
local p: pointer = malloc(size)
check(p, 'generic_allocator.alloc: allocation fail')
return p
end

function generic_allocator.alloc0(size: usize): pointer
check(size > 0_u, 'generic_allocator.alloc0: size must be greater than 0')
local p: pointer = calloc(size, 1)
check(p, 'generic_allocator.alloc0: allocation fail')
return p
end

function generic_allocator.realloc(p: pointer, size: usize): pointer
check(size > 0_u, 'generic_allocator.realloc: size must be greater than 0')
p = realloc(p, size)
check(size == 0 or p, 'generic_allocator.realloc: allocation fail')
return p
end

function generic_allocator.realloc0(p: pointer, newsize: usize, oldsize: usize): pointer
check(newsize > 0_u, 'generic_allocator.realloc0: size must be greater than 0')
p = realloc(p, newsize)
check(newsize == 0_u or p, 'generic_allocator.realloc0: allocation fail')
if likely(newsize > oldsize) then
memset(&(@byte[0]*)(p)[oldsize], 0, newsize - oldsize)
end
return p
end

function generic_allocator.dealloc(p: pointer)
free(p)
end

## implement_allocator_interface(generic_allocator)
Loading

0 comments on commit 096c3c9

Please sign in to comment.