void  OutOfMemoryPanic(size_t size);

void *SysAllocRaw(size_t size, int reqsize);
void  SysFreeRaw(void *ptr, size_t size);

void *AllocRaw4KB(int reqsize);
void *AllocRaw64KB(int reqsize);
void *LAlloc(size_t& size);
void  LFree(void *ptr);

struct Heap {
	enum { CACHE = 16 };

	static int Ksz(int k) {
		return k < 14 ? (k + 1) << 4 : k == 17 ? 576 : k == 16 ? 448 : k == 15 ? 368 : 288;
	}

	struct FreeLink {
		FreeLink *next;
	};

	struct Page {
		byte         klass;
		byte         active;
		Heap        *heap;
		FreeLink    *freelist;
		Page        *next;
		Page        *prev;

		void         LinkSelf()            { Dbl_Self(this); }
		void         Unlink()              { Dbl_Unlink(this); }
		void         Link(Page *lnk)       { Dbl_LinkAfter(this, lnk);  }

		void         Format(int k);

		byte *Begin()                      { return (byte *)this + sizeof(Page); }
		byte *End()                        { return (byte *)this + 4096; }
		int   Count()                      { return (int)(uintptr_t)(End() - Begin()) / Ksz(klass); }
	};

	struct Header;

	struct DLink {
		DLink       *next;
		DLink       *prev;

		void         LinkSelf()            { Dbl_Self(this); }
		void         Unlink()              { Dbl_Unlink(this); }
		void         Link(DLink *lnk)      { Dbl_LinkAfter(this, lnk);  }

		Header      *GetHeader()           { return (Header *)this - 1; }
	};

	struct Header {
		byte    free;
		byte    filler1, filler2, filler3;
		word    size;
		word    prev;
		Heap   *heap;
	#ifdef CPU_32
		dword   filler4;
	#endif

		DLink      *GetBlock()             { return (DLink *)(this + 1); }
		Header     *Next()                 { return (Header *)((byte *)this + size) + 1; }
		Header     *Prev()                 { return (Header *)((byte *)this - prev) - 1; }
	};

	struct BigHdr : DLink {
		size_t       size;
	};

	enum {
		NKLASS = 18,
		LBINS = 113,
		LARGEHDRSZ = 24,
		MAXBLOCK = 65536 - 2 * sizeof(Header) - LARGEHDRSZ,
		BIGHDRSZ = 56,
	};
	static StaticMutex mutex;

	Page      work[NKLASS][1];
	Page      full[NKLASS][1];
	Page     *empty[NKLASS];
	FreeLink *cache[NKLASS];
	int       cachen[NKLASS];

	bool      initialized;

	static word  BinSz[LBINS];
	static byte  SzBin[MAXBLOCK / 8 + 1];
	static byte  BlBin[MAXBLOCK / 8 + 1];

	DLink  large[1];
	int    lcount;
	DLink  freebin[LBINS][1];
	static DLink lempty[1];

	FreeLink *remote_free;

	static DLink big[1];
	static Heap  aux;

#ifdef HEAPDBG
	static void  DbgFreeFill(void *ptr, size_t size);
	static void  DbgFreeCheck(void *ptr, size_t size);
	static void  DbgFreeFillK(void *ptr, int k);
	static void *DbgFreeCheckK(void *p, int k);
#else
	static void  DbgFreeFill(void *ptr, size_t size) {}
	static void  DbgFreeCheck(void *ptr, size_t size) {}
	static void  DbgFreeFillK(void *ptr, int k) {}
	static void *DbgFreeCheckK(void *p, int k) { return p; }
#endif

#ifdef flagHEAPSTAT
	static void  Stat(size_t sz);
#else
	static void  Stat(size_t sz) {}
#endif

	void  FreeRemoteRaw();
	void  FreeRemote();

	void  Init();

	static int   CheckPageFree(FreeLink *l, int k);
	void  Check();
	static void  Assert(bool b);
	static void  DblCheck(Page *p);
	static void  AssertLeaks(bool b);

	Page *WorkPage(int k);
	void *AllocK(int k);
	void  FreeK(void *ptr, Page *page, int k);
	void *Allok(int k);
	void  Freek(void *ptr, int k);
	void  FreeDirect(void *ptr);
	void  MoveLarge(Heap *dest, DLink *l);
	void  MoveToEmpty(DLink *l, Header *bh);

	static void GlobalLInit();
	static int  SizeToBin(int n) { return SzBin[(n - 1) >> 3]; }

	void   LinkFree(DLink *b, int size);
	DLink *AddChunk(int reqsize);
	void  *DivideBlock(DLink *b, int size, int ii);
	void  *TryLAlloc(int ii, size_t size);
	void  *LAlloc(size_t& size);
	void   LFree(void *ptr);
	void   Make(MemoryProfile& f);

	void RemoteFree(void *ptr);
	void Shutdown();
	static void AuxFinalCheck();

	void *Alloc(size_t sz);
	void *AllocSz(size_t& sz);
	void  Free(void *ptr);
	void *Alloc32();
	void  Free32(void *ptr);
	void *Alloc48();
	void  Free48(void *ptr);
};

extern thread__ Heap heap;
