/*
	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License version 2 
	as published by the Free Software Foundation.

	This program 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 General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA


	Copyright (C) 2006  Thierry Berger-Perrin <tbptbp@gmail.com>
*/
#ifndef SYS_MEM_H
#define SYS_MEM_H

#include "specifics.h"

namespace sys { 
	namespace mem {
		enum { page_size = 4096 };

		void *allocate(const size_t size);
		void liberate(void * const p);

		/*
			Strictly growing pool.
		*/
		template<typename T> class growing_pool_t {
			struct bank_node_t;
			struct bookeeper_t {
				uint_t		count;
				bank_node_t	*prev;
			};

			enum { 
				num_pages = 256 * /*4*/ 16,	// that's our allocation granularity

				size_bank = page_size*num_pages,
				size_bookeeper = sizeof(bookeeper_t),
				size_payload = size_bank-size_bookeeper,

				max_elements = size_payload/sizeof(T)
			};

			struct bank_node_t {
				char		raw[size_payload];
				bookeeper_t	bookeeper;
			};

			bank_node_t	*tail;
			int			num_banks;

		public:
			growing_pool_t() : tail(0), num_banks(0) { }

			int get_num_banks() const { return num_banks; }

			// we'd need some locking, or be sure to use one pool per thread.
			T *allocate() {
				if (!tail || (tail->bookeeper.count >= max_elements)) {
					bank_node_t *n = (bank_node_t *) sys::mem::allocate(sizeof(bank_node_t));
					n->bookeeper.count	= 0;
					n->bookeeper.prev	= tail;
					tail = n;
					++num_banks;
				}
				T *p = new(tail->raw + tail->bookeeper.count*sizeof(T)) T(); // call at least some constructor.
				++tail->bookeeper.count;
				return p;
			}

			// no destructor called; if you need them, specialize.
			void purge() {
				while (tail) {
					bank_node_t *p = tail;
					tail = tail->bookeeper.prev;
					--num_banks;
					sys::mem::liberate(p);
				}
			}
		};

	}
}

#endif
