parsav  mem.t at [45a6e815b1]

File mem.t artifact 8aa8839430 part of check-in 45a6e815b1


-- 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,autofree)
	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 autofree 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 or 0] ] end)
	t.metamethods.__add = terra(self: &t, sz: intptr): t
		var n = @self n:advance(sz) return n
	end
	t.metamethods.__update = macro(function(self,idx,rhs)
		return quote self.ptr[idx] = rhs end end)
	t.metamethods.__cast = function(from,to,exp)
		if to == t then
			if from == niltype then return `t.null()
			elseif from == &ty then return `t {ptr = exp, ct = 1}
			elseif from == ty then return `t {ptr = &exp, ct = 1}
			elseif from.N and from.type == ty then
				return `t {ptr = &exp[0], ct = from.N }
			end
			error('invalid cast to ' .. t.name .. ' from ' .. tostring(from))
		elseif from == t then
			if     to == &ty  then return `exp.ptr
			elseif to == ty   then return `@exp.ptr
			elseif to == bool then return `exp:ref() end
			error('invalid cast from ' .. t.name .. ' to ' .. tostring(to))
		end
		error('invalid pointer cast')
	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
	debris: &m.pool
}

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 oldblock = @self
		self:init(sz)
		@self.debris = oldblock
	end
	return self
end

terra m.pool:init(sz: intptr)
	var b = m.heapa_raw(sz + sizeof(m.pool))
	self.storage = [&uint8](b) + sizeof(m.pool)
	self.cursor = self.storage
	self.sz = sz
	self.debris = [&m.pool](b)
	self.debris.storage = nil
	return self
end

terra m.pool:free(): {}
	if self.storage == nil then return end
	if self.debris.storage ~= nil then self.debris:free() end
	m.heapf(self.debris) -- storage + debris field allocated in one block
	self.storage = nil
	self.cursor = nil
	self.sz = 0
	self.debris = nil
end

terra m.pool:clear()
	if self.debris.storage ~= nil then self.debris:free() end
	self.cursor = self.storage
	return self
end

terra m.pool:alloc_bytes(sz: intptr): &opaque
	var space: intptr = self.sz - ([&uint8](self.cursor) - [&uint8](self.storage))
	if space < sz then
		self:cue(self.sz + sz + 256) end
	var ptr = self.cursor
	self.cursor = [&opaque]([&uint8](self.cursor) + sz)
	return ptr
end

terra m.pool:realloc_bytes(oldptr: &opaque, oldsz: intptr, newsz: intptr): &opaque
	var space: intptr = self.sz - ([&uint8](self.cursor) - [&uint8](self.storage))
	var cur = [&uint8](self.cursor)
	if cur - [&uint8](oldptr) == oldsz and newsz - oldsz < space then
		lib.dbg('moving pool cursor')
		cur = cur + (newsz - oldsz)
		self.cursor = [&opaque](cur)
		return oldptr
	else
		lib.dbg('copying pool object')
		var new = self:alloc_bytes(newsz)
		m.cpy(new, oldptr, oldsz)
		return new
	end
end

m.pool.methods.alloc = macro(function(self,typ,sz)
 	local ty = typ:astype()
	return `[m.ptr(ty)] {
		ptr = [&ty](self:alloc_bytes(sizeof([ty]) * [sz]));
		ct = [sz];
	}
end)

m.pool.methods.realloc = macro(function(self,ptr,oldsz,newsz)
	local ty = self.tree.type.type
	return `[m.ptr(ty)] {
		ptr = [&ty](self:realloc_bytes(ptr,
			sizeof(ty) * oldsz,
			sizeof(ty) * newsz));
		ct = sz;
	}
end)

terra m.pool:frame() -- stack-style linear mgmt
	return self.cursor
end

terra m.pool:reset(frame: &opaque)
	if frame >= self.storage and frame <= self.cursor then
		self.cursor = frame
	else -- trying to rewind into a previous block! not possible
		self.cursor = self.storage
	end
	return self
end


return m