// memman.cpp : Defines the entry point for the console application.
//

#include "kosSyst.h"
#include "mcsmemm.h"


void * __cdecl operator new ( size_t count, size_t element_size )
{
	return allocmem( (Dword)(count * element_size) );
}

void * __cdecl operator new [] ( size_t amount )
{
	return allocmem( (Dword)amount );
}

void * __cdecl operator new ( size_t amount )
{
	return allocmem( (Dword)amount );
}

void __cdecl operator delete ( void *pointer )
{
	if ( pointer != NULL ) freemem( pointer );
}

void __cdecl operator delete [] ( void *pointer )
{
	if ( pointer != NULL ) freemem( pointer );
}


//
Dword mmMutex = FALSE;
MemBlock *rootfree = NULL;
MemBlock *rootuser = NULL;
bool mmInitialized = false;
Byte *mmHeapTop = NULL;


//
Byte *allocmem( Dword reqsize )
{
  MemBlock *BlockForCheck;
  MemBlock *LastKnownGood;
  Dword tail;
  Byte *address;

  //ïîäðîâíÿåì ðàçìåð
  if( ( tail = reqsize % SIZE_ALIGN ) != 0 )
  {
    reqsize += SIZE_ALIGN - tail;
  }

  LastKnownGood = NULL;

  // æä¸ì îñâîáîæäåíèÿ ìüþòåêñà
  while ( rtlInterlockedExchange( &mmMutex, TRUE ) )
  {
	  //
	  kos_Pause( 1 );
  }

  //èùåì ïîäõîäÿùèé ñâîáîäíûé áëîê
  if( rootfree != NULL )
  {
    for ( BlockForCheck = rootfree; ; BlockForCheck = BlockForCheck->Next )
    {
      if ( BlockForCheck->Size >= reqsize )
      {
        //íàøëè
        if ( LastKnownGood != NULL )
        {
          if ( LastKnownGood->Size >= BlockForCheck->Size )
            LastKnownGood = BlockForCheck;
        }
        else
          LastKnownGood = BlockForCheck;
        if ( LastKnownGood->Size == reqsize )
          break;
      }
      if ( BlockForCheck->Next == NULL )
        break;
    }
  }

  if ( LastKnownGood != NULL )
  {
    //ïðîâåðèì íàéäåííûé áëîê íà âîçìîæíîñòü äåëåíèÿ
    tail = LastKnownGood->Size - reqsize;
    if ( tail >= ( sizeof(MemBlock) + SIZE_ALIGN ) )
    {
      //áóäåì ðàçáèâàòü
      BlockForCheck = (MemBlock *)( ( (Byte *)LastKnownGood ) + tail );
      BlockForCheck->Size = reqsize;
      //âñòàâèì çàíÿòûé áëîê â íà÷àëî ñïèñêà çàíàòûõ áëîêîâ
      if( rootuser != NULL )
      {
        BlockForCheck->Next = rootuser;
        rootuser->Previous = BlockForCheck;
        BlockForCheck->Previous = NULL;
        rootuser = BlockForCheck;
      }
      else
      {
        rootuser = BlockForCheck;
        BlockForCheck->Next = NULL;
        BlockForCheck->Previous = NULL;
      }

      //èçìåíèì ðàçìåð îñòàâøåéñÿ ÷àñòè
      LastKnownGood->Size = tail - sizeof(MemBlock);
      address = ( (Byte *)BlockForCheck ) + sizeof(MemBlock);

	  // îòïóñòèì ìüþòåêñ
      rtlInterlockedExchange( &mmMutex, FALSE );

      return address;
    }
    else
    {
      //ïåðåìåñòè áëîê èç î÷åðåäè ñâîáîäíûõ â íà÷àëî î÷åðåäè çàíÿòûõ
      //ñíà÷àëà âûêèíåì åãî èç î÷åðåäè ñâîáîäíûõ
      if ( LastKnownGood->Previous != NULL )
      {
        LastKnownGood->Previous->Next = LastKnownGood->Next;
      }
      else
      {
        //áëîê ñòîèò â íà÷àëå î÷åðåäè
        rootfree = LastKnownGood->Next;
      }
      if( LastKnownGood->Next != NULL )
      {
        LastKnownGood->Next->Previous = LastKnownGood->Previous;
      }
      //òåïåðü âñòàâèì åãî â î÷åðåäü çàíÿòûõ
      if( rootuser != NULL )
      {
        LastKnownGood->Next = rootuser;
        rootuser->Previous = LastKnownGood;
        LastKnownGood->Previous = NULL;
        rootuser = LastKnownGood;
      }
      else
      {
        rootuser = LastKnownGood;
        LastKnownGood->Next = NULL;
        LastKnownGood->Previous = NULL;
      }
	  //
      address = ( (Byte *)LastKnownGood ) + sizeof(MemBlock);

	  // îòïóñòèì ìüþòåêñ
      rtlInterlockedExchange( &mmMutex, FALSE );

      return address;
    }
  }
  else
  {
	// íàäî ïîëó÷èòü åù¸ áëîê ïàìÿòè
	LastKnownGood = (MemBlock *)kos_malloc(
		(reqsize > 0x10000 - sizeof(MemBlock)) ? (reqsize + sizeof(MemBlock)) : 0x10000);
	if (LastKnownGood != NULL)
	{
		LastKnownGood->Size = reqsize;
		// òåïåðü âñòàâèì åãî â î÷åðåäü çàíÿòûõ
		LastKnownGood->Next = rootuser;
		LastKnownGood->Previous = NULL;
		if (rootuser != NULL)
			rootuser->Previous = LastKnownGood;
		rootuser = LastKnownGood;
		// à òàêæå äîáàâèì õâîñò ñâåæåâûäåëåííîãî áîëüøîãî áëîêà â ñïèñîê ñâîáîäíûõ
		if (reqsize < 0x10000 - sizeof(MemBlock))
		{
			MemBlock* free = (MemBlock*)((Byte*)LastKnownGood + sizeof(MemBlock) + reqsize);
			free->Next = rootfree;
			free->Previous = NULL;
			if (rootfree != NULL)
				rootfree->Previous = free;
			rootfree = free;
		}
		address = (Byte*)LastKnownGood + sizeof(MemBlock);
		// îòïóñòèì ìüþòåêñ
		rtlInterlockedExchange(&mmMutex, FALSE);

		return address;
	}
  }

  // îòïóñòèì ìüþòåêñ
  rtlInterlockedExchange( &mmMutex, FALSE );

  //
  rtlDebugOutString( "allocmem failed." );
  kos_ExitApp();
  //
  return NULL;
}

//
Dword freemem( void *vaddress )
{
  Dword result;

  Byte *checknext, *address = (Byte *)vaddress;
                               
  // æä¸ì îñâîáîæäåíèÿ ìüþòåêñà
  while ( rtlInterlockedExchange( &mmMutex, TRUE ) )
  {
	  //
	  kos_Pause( 1 );
  }

  MemBlock *released = (MemBlock *)( address - sizeof(MemBlock) );

  result = released->Size;

  //óáèðàåì áëîê èç ñïèñêà çàíÿòûõ
  if ( released->Previous != NULL )
  {
    released->Previous->Next = released->Next;
  }
  else
  {
    rootuser = released->Next;
  }
  if ( released->Next != NULL )
  {
    released->Next->Previous = released->Previous;
  }
  //çàêèíåì òåïåðü ýòîò áëîê â ñïèñîê ñâîáîäíûõ
  released->Next = rootfree;
  released->Previous = NULL;
  rootfree = released;
  if ( released->Next != NULL )
  {
    released->Next->Previous = released;
  }

  //òåïåðü ïîèùåì ñìåæíûå ñâîáîäíûå áëîêè
  checknext = (Byte *)(rootfree) + ( rootfree->Size + sizeof(MemBlock) );
  //
  for ( released = rootfree->Next; released != NULL; released = released->Next )
  {
    if ( checknext == (Byte *)released )
    {
      //ñîáèðàåì áëîêè âìåñòå
      //ñíà÷àëà âûêèíåì èç î÷åðåäè ñâîáîäíûõ
      released->Previous->Next = released->Next;
      if( released->Next != NULL )
      {
        released->Next->Previous = released->Previous;
      }
      //òåïåðü óâåëè÷èì ðàçìåð êîðíåâîãî áëîêà
      rootfree->Size += released->Size + sizeof(MemBlock);
      break;
    }
  }
  //åñëè íàäî, ïîèùåì áëîêè ïåðåä òåêùèì.
  checknext = (Byte *)(rootfree);
  //
  if ( released == NULL )
  {
    for ( released = rootfree->Next; released != NULL; released = released->Next )
    {
      if ( checknext == (Byte *)released + ( released->Size + sizeof(MemBlock) ) )
      {
        //ñîáèðàåì áëîêè âìåñòå
        //óâåëè÷èì ðàçìåð áëîêà
        released->Size += rootfree->Size + sizeof(MemBlock);
        //òåïåðü âûêèíåì èç î÷åðåäè ñâîáîäíûõ
        released->Previous->Next = released->Next;
        if ( released->Next != NULL )
        {
          released->Next->Previous = released->Previous;
        }
        //è çàêèíåì åãî â íà÷àëî î÷åðåäè âìåñòî ïðèñîåäèí¸ííîãî áëîêà èç êîðíÿ ñïèñêà
        if ( rootfree->Next != NULL )
        {
          rootfree->Next->Previous = released;
        }
        released->Next = rootfree->Next;
        released->Previous = NULL;
        rootfree = released;
        break;
      }
    }
  }

  // îòïóñòèì ìüþòåêñ
  rtlInterlockedExchange( &mmMutex, FALSE );

  return result;
}