#include "Malloc.h"

#ifdef V4

#define ATIMING(x)

#define sHeapStat(k)
#define FreeFill(p, c)
#define CheckFree(p, k) p

inline int Ksz(int k)   { return (k + 1) << 4; }

static byte sKount[16];
static word sFreem[16];

inline void MSmall::Page::Format(int k)
{
	LOG("Formatting " << (void *)this << " to " << k);
	klass = k;
	sz = Ksz(k);
	count = sKount[k];
	ASSERT(count == (4096 - HEADERSZ) / sz);
//	freecount = count;
	active = 0;
	byte *ptr = Begin();
	byte *end = (byte *)this + 4096;
	FreeLink *l = NULL;
	while(ptr < end) {
		((FreeLink *)ptr)->next = l;
		l = (FreeLink *)ptr;
		ptr += sz;
	}
	freelist = l;
#ifdef HEAPDBG
	FreeFill((dword *)Begin(), (4096 - HEADERSZ) / 4);
#endif
}

void MSmall::Init()
{
	for(int i = 0; i < 16; i++) {
		int sz = Ksz(i);
		sKount[i] = (4096 - HEADERSZ) / sz;
		sFreem[i] = sKount[i] * sz - sz;
	}
	for(int i = 0; i < 16; i++) {
		empty[i] = NULL;
		full[i]->LinkSelf();
		work[i]->LinkSelf();
		work[i]->freelist = NULL;
		work[i]->klass = i;
	}
	emptypages = 0;
}

inline void *MSmall::AllocK(int k)
{
	Page *page = work[k]->next;
	for(;;) {
		ASSERT(page->klass == k);
		FreeLink *p = page->freelist;
		if(p) {
			page->freelist = p->next;
			++page->active;
			return p;
		}
		if(page->next != page) {
			ATIMING("NextWork");
			page->Unlink();
			page->Link(full[k]);
			page = work[k]->next;
		}
		else {
			page = empty[k];
			if(!page) {
				if(emptypages) {
					ATIMING("FromEmpty other");
					emptypages--;
					for(int i = 0; i < 16; i++) { // try inverse direction
						if(empty[i]) {
							page = empty[i];
							empty[i] = page->next;
							break;
						}
					}
					ASSERT(page);
				}
				else // remote delete should be here!
					page = (Page *)AllocRaw4KB();
				page->Format(k);
				ASSERT(page->klass == k);
			}
			else {
				ATIMING("FromEmpty");
				ASSERT(emptypages > 0);
				empty[k] = page->next;
				emptypages--;
				ASSERT(page->klass == k);
			}
			page->Link(work[k]);
			ASSERT(page->klass == k);
		}
	}
}

inline void MSmall::FreeK(void *ptr, Page *page, int k)
{
	if(page->freelist)
		((FreeLink *)ptr)->next = page->freelist;
	else {
		page->Unlink();
		page->Link(work[k]);
	}
	page->freelist = (FreeLink *)ptr;
	if(--page->active == 0) {
		ATIMING("ToEmpty");
		page->Unlink();
		page->next = empty[k];
		empty[k] = page;
		emptypages++;
		return;
	}
}

void *MSmall::Alloc(size_t sz)
{
	if(sz <= 256) {
		ATIMING("Small alloc");
		int k = ((int)sz + (sz == 0) - 1) >> 4;
		sHeapStat(k);
		return CheckFree(AllocK(k), k);
	}
	NEVER();
	return NULL;
//	return LAlloc(sz);
}

void MSmall::Free(void *ptr)
{
	if(!ptr) return;
	if(((dword)(uintptr_t)ptr) & 8)
		NEVER();
//		LFree(ptr);
	else {
		ATIMING("Small free");
		Page *page = (Page *)((uintptr_t)ptr & ~(uintptr_t)4095);
		int k = page->klass;
		ASSERT((((uintptr_t)ptr & (uintptr_t)4095) - HEADERSZ) % Ksz(k & 15) == 0);
#ifdef HEAPDBG
#ifdef CPU_64
		FreeFill((dword *)ptr + 2, (Ksz(k & 15) >> 2) - 2);
#else
		FreeFill((dword *)ptr + 1, (Ksz(k & 15) >> 2) - 1);
#endif
#endif
		FreeK(ptr, page, k);
	}
}

#endif
