-- vim: ft=terra
local m = {
zero = macro(function(r)
return quote
for i = 0, [r.tree.type.N] do r[i] = 0 end
end
end);
heapa_raw = terralib.externfunction('malloc', intptr -> &opaque);
heapr_raw = terralib.externfunction('realloc', {&opaque, intptr} -> &opaque);
heapf = terralib.externfunction('free', &opaque -> {});
cpy = terralib.externfunction('mempcpy',{&opaque, &opaque, intptr} -> &opaque);
}
m.heapa = macro(function(ty, sz)
local p = m.ptr(ty:astype())
return `p {
ptr = [&ty:astype()](m.heapa_raw(sizeof(ty) * sz));
ct = sz;
}
end)
function m.cache(ty,sz)
sz = sz or 32
local struct c {
store: ty[sz]
top: intptr
cur: intptr
}
c.name = string.format('cache<%s,%u>', tostring(ty), sz)
terra c:insert(v: ty)
if [ty.ptr_basetype ~= nil] then
if self.cur < self.top then self.store[self.cur]:free() end
end
self.store[self.cur] = v
self.top = lib.math.biggest(self.top, self.cur + 1)
self.cur = (self.cur + 1) % sz
return v
end
c.metamethods.__apply = terra(self: &c, idx: intptr) return &self.store[idx] end
if ty.ptr_basetype then
terra c:free()
for i=0,self.top do self.store[i]:free() end
end
end
return c
end
local function mkptr(ty, dyn)
local t = terralib.types.newstruct(string.format('%s<%s>', dyn and 'ptr' or 'ref', ty))
t.entries = {
{'ptr', &ty};
{'ct', intptr};
}
t.ptr_basetype = ty
local recurse = false
--if ty:isstruct() then
--if ty.methods.free then recurse = true end
--end
if dyn then
t.methods = {
free = terra(self: &t): bool
[recurse and quote
self.ptr:free()
end or {}]
if self.ct > 0 then
m.heapf(self.ptr)
self.ptr = nil
self.ct = 0
return true
end
return false
end;
init = terra(self: &t, newct: intptr): bool
if newct == 0 then self.ct = 0 self.ptr = nil return false end
var nv = [&ty](m.heapa_raw(sizeof(ty) * newct))
if nv ~= nil then
self.ptr = nv
self.ct = newct
return true
else return false end
end;
resize = terra(self: &t, newct: intptr): bool
var nv: &ty
if self.ct > 0
then nv = [&ty](m.heapr_raw(self.ptr, sizeof(ty) * newct))
else nv = [&ty](m.heapa_raw(sizeof(ty) * newct))
end
if nv ~= nil then
self.ptr = nv
self.ct = newct
return true
else return false end
end;
}
terra t:ensure(n: intptr)
if self.ptr == nil then
if not self:init(n) then return t {ptr=nil,ct=0} end
end
if self.ct >= n then return @self end
self:resize(n)
return @self
end
end
terra t:advance(n: intptr)
self.ptr = self.ptr + n
self.ct = self.ct - n
return self.ptr
end
terra t.methods.null(): t return t { ptr = nil, ct = 0 } end -- maybe should be a macro?
terra t:ref() return self.ptr ~= nil end
t.metamethods.__not = macro(function(self) return `not self:ref() end)
t.metamethods.__apply = macro(function(self,idx) return `self.ptr[idx] end)
if not ty:isstruct() then
terra t:cmp_raw(other: &ty)
for i=0, self.ct do
if self.ptr[i] ~= other[i] then return false end
end
return true
end
terra t:cmp(other: t)
if other.ct ~= self.ct then return false end
return self:cmp_raw(other.ptr)
end
end
return t
end
m.ptr = terralib.memoize(function(ty) return mkptr(ty, true) end)
m.ref = terralib.memoize(function(ty) return mkptr(ty, false) end)
m.lstptr = function(ty) return m.ptr(m.ptr(ty)) end -- make code more readable
m.vec = terralib.memoize(function(ty)
local v = terralib.types.newstruct(string.format('vec<%s>', ty.name))
v.entries = {
{field = 'storage', type = m.ptr(ty)};
{field = 'sz', type = intptr};
{field = 'run', type = intptr};
}
local terra biggest(a: intptr, b: intptr)
if a > b then return a else return b end
end
terra v:init(run: intptr): bool
if not self.storage:init(run) then return false end
self.run = run
self.sz = 0
return true
end;
terra v:assure(n: intptr)
if self.storage.ct < n then
self.storage:resize(biggest(n, self.storage.ct + self.run))
end
end
terra v:new(): &ty
self:assure(self.sz + 1)
self.sz = self.sz + 1
return self.storage.ptr + (self.sz - 1)
end;
terra v:push(val: ty)
self:assure(self.sz + 1)
self.storage.ptr[self.sz] = val
self.sz = self.sz + 1
end;
terra v:free() self.storage:free() end;
terra v:last(idx: intptr): &ty
if self.sz > idx then
return self.storage.ptr + (self.sz - (idx+1))
else lib.bail('vector underrun!') end
end;
terra v:crush()
self.storage:resize(self.sz)
return self.storage
end;
v.metamethods.__apply = terra(self: &v, idx: intptr): &ty -- no index??
if self.sz > idx then
return self.storage.ptr + idx
else lib.bail('vector overrun!') end
end
return v
end)
struct m.pool {
-- implements growable memory pools. EVERY THREAD MUST HAVE ITS OWN
storage: &opaque
cursor: &opaque
sz: intptr
}
terra m.pool:cue(sz: intptr)
if self.storage == nil then
self.storage = m.heapa_raw(sz)
self.cursor = self.storage
self.sz = sz
else
if self.sz >= sz then return self end
var ofs = [&uint8](self.cursor) - [&uint8](self.storage)
self.storage = m.heapr_raw(self.storage, sz)
self.cursor = [&opaque]([&uint8](self.storage) + ofs)
self.sz = sz
end
return self
end
terra m.pool:init(sz: intptr)
self.storage = nil
self:cue(sz)
return self
end
terra m.pool:free()
m.heapf(self.storage)
self.storage = nil
self.cursor = nil
self.sz = 0
end
terra m.pool:clear()
self.cursor = self.storage
return self
end
terra m.pool:alloc_bytes(sz: intptr): &opaque
var space = self.sz - ([&uint8](self.cursor) - [&uint8](self.storage))
if space < sz then self:cue(space + sz + 256) end
var ptr = self.cursor
self.cursor = [&opaque]([&uint8](self.cursor) + sz)
return ptr
end
m.pool.methods.alloc = macro(function(self,ty,sz)
return `[ty](self:alloc_bytes(sizeof(ty) * sz))
end)
terra m.pool:frame() -- stack-style linear mgmt
return self.cursor
end
terra m.pool:reset(frame: &opaque)
self.cursor = frame
return self
end
return m