/* Resp: A programming language
 * Copyright (C) 2008-2009 David Robillard <http://drobilla.net>
 *
 * Resp is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * Resp is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General
 * Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Resp.  If not, see <http://www.gnu.org/licenses/>.
 */

/** @file
 * @brief Garbage collection
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <cassert>
#include <iostream>
#include <set>

#include "resp.hpp"
#include "tlsf.h"

using namespace std;

GC::GC(size_t pool_size)
{
	_pool = tlsf_init(malloc(pool_size), pool_size);
}

GC::~GC()
{
	tlsf_destroy((tlsf_t*)_pool);
}

void*
GC::alloc(size_t size)
{
	size += (4 - (size % 4)); // Align to 32-bits
	size += sizeof(Object::Header);
	void* ret = tlsf_malloc((tlsf_t*)_pool, size);
	((Object::Header*)ret)->tag = T_UNKNOWN;
	ret = (char*)ret + sizeof(Object::Header);
	_heap.push_back((Object*)ret);
	return ret;
}

inline void
mark(const Object* obj)
{
	if (!obj || obj->marked())
		return;

	obj->mark(true);
	if (obj->tag() != T_UNKNOWN) {
		const ATuple* tup = ((const AST*)obj)->to_tuple();
		if (tup)
			FOREACHP(ATuple::const_iterator, i, tup)
				mark(*i);
	}
}

void
GC::collect(const Roots& roots)
{
	//const size_t oldSize = _heap.size();

	for (Roots::const_iterator i = roots.begin(); i != roots.end(); ++i)
		mark(*i);

	for (Heap::iterator i = _heap.begin(); i != _heap.end();) {
		Heap::iterator next = i;
		++next;

		if ((*i)->marked()) {
			(*i)->mark(false);
			assert(!(*i)->marked());
		} else {
			tlsf_free((tlsf_t*)_pool, ((char*)(*i) - sizeof(Object::Header)));
			_heap.erase(i);
		}
		i = next;
	}

	//std::cerr << "[GC] Collect " << oldSize << " => " << _heap.size() << endl;
}

extern "C" {

void*
__resp_alloc(unsigned size)
{
	static const size_t COLLECT_SIZE = 8 * 1024 * 1024; // 8 MiB

	static size_t allocated = 0;
	allocated += size;
	if (allocated > COLLECT_SIZE) {
		Object::pool.collect(Object::pool.roots());
		allocated = 0;
	}

	void* mem = Object::pool.alloc(size);
	Object* obj = new (mem) Object();
	obj->tag(T_UNKNOWN);

	return mem;
}

}