-- vim: ft=terra
-- string.t: string classes

local m = {}

struct m.acc {
	buf: rawstring
	sz: intptr
	run: intptr
	space: intptr
}

local terra biggest(a: intptr, b: intptr)
	if a > b then return a else return b end
end

m.acc.methods = {
	init = terra(self: &m.acc, run: intptr)
		lib.dbg('initializing string accumulator')
		self.buf = [rawstring](lib.mem.heapa_raw(run))
		self.run = run
		self.space = run
		self.sz = 0
	end;
	free = terra(self: &m.acc)
		lib.dbg('freeing string accumulator')
		if self.buf ~= nil and self.space > 0 then
			lib.mem.heapf(self.buf)
		end
	end;
	crush = terra(self: &m.acc)
		lib.dbg('crushing string accumulator')
		self.buf = [rawstring](lib.mem.heapr_raw(self.buf, self.sz))
		self.space = self.sz
		return self
	end;
}

m.acc.methods.finalize = terra(self: &m.acc)
	lib.dbg('finalizing string accumulator')
	self:crush()
	var pt: lib.mem.ptr(int8)
	pt.ptr = self.buf
	pt.ct = self.sz
	self.buf = nil
	self.sz = 0
	return pt
end;
m.acc.methods.push = terra(self: &m.acc, str: rawstring, len: intptr)
	lib.dbg('pushing "',str,'" onto accumulator')
	if self.buf == nil then self:init(self.run) end
	if len == 0 then len = lib.str.sz(str) end
	if len >= self.space - self.sz then
		self.space = self.space + biggest(self.run,len + 1)
		self.buf = [rawstring](lib.mem.heapr_raw(self.buf, self.space))
	end
	lib.mem.cpy(self.buf + self.sz, str, len)
	self.sz = self.sz + len
	self.buf[self.sz] = 0
	return self
end;
m.acc.methods.ppush = terra(self: &m.acc, str: lib.mem.ptr(int8))
	self:push(str.ptr, str.ct)            return self end;
m.acc.methods.merge = terra(self: &m.acc, str: lib.mem.ptr(int8))
	self:push(str.ptr, str.ct) str:free() return self end;

return m
