// RuntimeForStatic base library | C+- (SysRoutine) | Author: Sergei Y.Kitáev
#include "Precompiled.h"
#include "SharedIncludes.h"
namespace RuntimeForStatic {
//==================================================================================================

PVOID $Memory::Allocate($AM,SIZE_T size $not_zero) $fatal_error_on_failure
{
   return Allocate($ci,$ci, size );
}
PVOID $Memory::Allocate($AM,SIZE_T size $not_zero,int count $not_zero) $fatal_error_on_failure
{
   return Allocate($ci,$ci, size, count );
}

PVOID $Memory::CleanAlloc($AM,SIZE_T size $not_zero) $fatal_error_on_failure
{
   return CleanAlloc($ci,$ci, size );
}
PVOID $Memory::CleanAlloc($AM,SIZE_T size $not_zero,int count $not_zero) $fatal_error_on_failure
{
   return CleanAlloc($ci,$ci, size, count );
}

PVOID $Memory::ReAllocate($AM,PVOID memoryBlock,SIZE_T newSize) $fatal_error_on_failure
{
   return ReAllocate($ci,$ci, memoryBlock, newSize );
}
PVOID $Memory::ReAllocate($AM, PVOID memoryBlock, SIZE_T size, int newCount $not_zero )
                               $fatal_error_on_failure
{
   return ReAllocate($ci,$ci, memoryBlock, size, newCount );
}

//--------------------------------------------------------------------------------------------------

PVOID $Memory::Allocate($A,$M,SIZE_T size $not_zero) $fatal_error_on_failure
{
   PVOID rslt = HelpersRT::Allocate($ci,$m, size );
   if ( !rslt ) $FatalError::RaiseMemoryAllocationFailure();
   return rslt;
}
PVOID $Memory::Allocate($A,$M,SIZE_T size $not_zero,int count $not_zero) $fatal_error_on_failure
{
   PVOID rslt = HelpersRT::Allocate($ci,$m, size, count );
   if ( !rslt ) $FatalError::RaiseMemoryAllocationFailure();
   return rslt;
}

PVOID $Memory::CleanAlloc($A,$M,SIZE_T size $not_zero) $fatal_error_on_failure
{
   PVOID rslt = HelpersRT::CleanAlloc($ci,$m, size );
   if ( !rslt ) $FatalError::RaiseMemoryAllocationFailure();
   return rslt;
}
PVOID $Memory::CleanAlloc($A,$M,SIZE_T size $not_zero,int count $not_zero) $fatal_error_on_failure
{
   PVOID rslt = HelpersRT::CleanAlloc($ci,$m, size, count );
   if ( !rslt ) $FatalError::RaiseMemoryAllocationFailure();
   return rslt;
}

PVOID $Memory::ReAllocate($A,$M,PVOID memoryBlock,SIZE_T newSize) $fatal_error_on_failure
{
   PVOID rslt = HelpersRT::ReAllocate($ci,$m, memoryBlock, newSize );
   if ( !rslt ) $FatalError::RaiseMemoryAllocationFailure();
   return rslt;
}
PVOID $Memory::ReAllocate($A,$M, PVOID memoryBlock, SIZE_T size, int newCount $not_zero )
                                 $fatal_error_on_failure
{
   PVOID rslt = HelpersRT::ReAllocate($ci,$m, memoryBlock, size, newCount );
   if ( !rslt ) $FatalError::RaiseMemoryAllocationFailure();
   return rslt;
}

//--------------------------------------------------------------------------------------------------

void $Memory::Free(PVOID memoryBlock $optional)
{
   Free( memoryBlock, nullptr );
}
void $Memory::Free( PVOID memoryBlock $optional,
                    $MemFreeRoutine specialFreeRoutine $optional )
{
   if ( !memoryBlock )
      return;
   if ( specialFreeRoutine )
   {
      specialFreeRoutine( memoryBlock );
      return;
   }
#ifdef _DEBUG
   return std::_free_dbg( memoryBlock, $ALLOCATION_BLOCK );
#else
   return std::free( memoryBlock );
#endif // _DEBUG
}

void $Memory::Free( const std::initializer_list<PVOID>& memoryBlockPointers $optionals )
{
   Free( memoryBlockPointers, nullptr );
}
void $Memory::Free( const std::initializer_list<PVOID>& memoryBlockPointers $optionals,
                    $MemFreeRoutine specialFreeRoutine $optional )
{
   for (auto ptr: memoryBlockPointers)
      if ( ptr ) Free( ptr, specialFreeRoutine );
}

//--------------------------------------------------------------------------------------------------

void $Memory::FreeByPtr($A, PVOID volatile* pPointer $p_optional )
{
   FreeByPtr($ci, pPointer, nullptr );
}
void $Memory::FreeByPtr($A, PVOID volatile* pPointer $p_optional,
                            $MemFreeRoutine specialFreeRoutine $optional )
{
   $$VERIFY_POINTER_ARG( pPointer );
   if ( *pPointer )
      Free( $Interlocked::ResetPointer($, pPointer ), specialFreeRoutine );
}

void $Memory::FreeByPointers($A, const std::initializer_list<PVOID volatile*>&
                                       ptrPointers $p_optional_list )
{
   FreeByPointers($ci, ptrPointers, nullptr );
}
void $Memory::FreeByPointers($A, const std::initializer_list<PVOID volatile*>&
                                       ptrPointers $p_optional_list,
                                 $MemFreeRoutine specialFreeRoutine $optional )
{
   for (auto pPointer: ptrPointers)
      FreeByPtr($$CI, pPointer, specialFreeRoutine );
}

//==================================================================================================

PVOID $Memory::AllocateNS($AM,SIZE_T size $not_zero) $null_on_failure $last_error
{
   return AllocateNS($ci,$ci, size );
}
PVOID $Memory::AllocateNS($AM, SIZE_T size $not_zero, int count $not_zero )
                               $null_on_failure $last_error
{
   return AllocateNS($ci,$ci, size, count );
}

PVOID $Memory::CleanAllocNS($AM,SIZE_T size $not_zero) $null_on_failure $last_error
{
   return CleanAllocNS($ci,$ci, size );
}
PVOID $Memory::CleanAllocNS($AM, SIZE_T size $not_zero, int count $not_zero )
                                 $null_on_failure $last_error
{
   return CleanAllocNS($ci,$ci, size, count );
}

PVOID $Memory::ReAllocateNS($AM,PVOID memoryBlock,SIZE_T newSize) $null_on_failure $last_error
{
   return ReAllocateNS($ci,$ci, memoryBlock, newSize );
}
PVOID $Memory::ReAllocateNS($AM, PVOID memoryBlock, SIZE_T size, int newCount $not_zero )
                                 $null_on_failure $last_error
{
   return ReAllocateNS($ci,$ci, memoryBlock, size, newCount );
}

//--------------------------------------------------------------------------------------------------

PVOID $Memory::AllocateNS($A,$M,SIZE_T size $not_zero ) $null_on_failure $last_error
{
   PVOID rslt = HelpersRT::Allocate($ci,$m, size );
   return rslt ? $RESET_LE( rslt ) :
      $SET_ERROR_LE( $StaticMessages::MemoryAllocationFailure() );
}
PVOID $Memory::AllocateNS($A,$M, SIZE_T size $not_zero, int count $not_zero )
                                 $null_on_failure $last_error
{
   PVOID rslt = HelpersRT::Allocate($ci,$m, size, count );
   return rslt ? $RESET_LE( rslt ) :
      $SET_ERROR_LE( $StaticMessages::MemoryAllocationFailure() );
}

PVOID $Memory::CleanAllocNS($A,$M,SIZE_T size $not_zero) $null_on_failure $last_error
{
   PVOID rslt = HelpersRT::CleanAlloc($ci,$m, size );
   return rslt ? $RESET_LE( rslt ) :
      $SET_ERROR_LE( $StaticMessages::MemoryAllocationFailure() );
}
PVOID $Memory::CleanAllocNS($A,$M, SIZE_T size $not_zero, int count $not_zero )
                                   $null_on_failure $last_error
{
   PVOID rslt = HelpersRT::CleanAlloc($ci,$m, size, count );
   return rslt ? $RESET_LE( rslt ) :
      $SET_ERROR_LE( $StaticMessages::MemoryAllocationFailure() );
}

PVOID $Memory::ReAllocateNS($A,$M,PVOID memoryBlock,SIZE_T newSize) $null_on_failure $last_error
{
   PVOID rslt = HelpersRT::ReAllocate($ci,$m, memoryBlock, newSize );
   return rslt ? $RESET_LE( rslt ) :
      $SET_ERROR_LE( $StaticMessages::MemoryAllocationFailure() );
}
PVOID $Memory::ReAllocateNS($A,$M, PVOID memoryBlock, SIZE_T size, int newCount $not_zero )
                                   $null_on_failure $last_error
{
   PVOID rslt = HelpersRT::ReAllocate($ci,$m, memoryBlock, size, newCount );
   return rslt ? $RESET_LE( rslt ) :
      $SET_ERROR_LE( $StaticMessages::MemoryAllocationFailure() );
}

//==================================================================================================

SIZE_T $Memory::Size($A,PVOID memoryBlock) $fatal_error_on_failure
{
   $$VERIFY_POINTER_ARG( memoryBlock );
#ifdef _DEBUG
   SIZE_T rslt = _msize_dbg( memoryBlock, $ALLOCATION_BLOCK );
#else
   SIZE_T rslt = _msize( memoryBlock );
#endif // _DEBUG
   if ( rslt == -1 )
      $Memory::Fail_IncorrectPointerForBlockSizeRequest($$CI);
   return rslt;
}

//--------------------------------------------------------------------------------------------------

PVOID $Memory::TwiceEnlarge($AM,PVOID memoryBlock) $fatal_error_on_failure
{
   return TwiceEnlarge($ci,$ci, memoryBlock );
}
PVOID $Memory::TwiceEnlargeNS($AM,PVOID memoryBlock) $null_on_failure $last_error
{
   return TwiceEnlargeNS($ci,$ci, memoryBlock );
}

PVOID $Memory::TwiceEnlarge($A,$M,PVOID memoryBlock) $fatal_error_on_failure
{
   SIZE_T size_0 = Size($ci, memoryBlock ), size = size_0 * 2;
   if ( size / 2 != size_0 )
      $FATAL_ERROR( SizeTooLargeForTwiceEnlarge_Message() );
   return ReAllocate($ci,$m, memoryBlock, size );
}
PVOID $Memory::TwiceEnlargeNS($A,$M,PVOID memoryBlock) $null_on_failure $last_error
{
   SIZE_T size_0 = Size($ci, memoryBlock ), size = size_0 * 2;
   if ( size / 2 != size_0 )
      return $SET_ERROR_LE( SizeTooLargeForTwiceEnlarge_Message() );
   return ReAllocateNS($ci,$m, memoryBlock, size );
}

//==================================================================================================

void $Memory::SysLocalFree($A,PVOID memoryBlock $optional) $fatal_error_on_failure
{
   if ( !memoryBlock )
      return;
   PCWSTR errorMessage = nullptr;
   __try
   {
      if ( !::LocalFree( memoryBlock ) )
         return;
   }
   __except( ::GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
             EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH )
   {
      errorMessage = $TEXT(
         L"EXCEPTION_ACCESS_VIOLATION exception has been generated.",
         L"Было сгенерировано исключение EXCEPTION_ACCESS_VIOLATION." );
   }
   if ( !errorMessage )
      errorMessage = HelpersRT::GetLastSystemMessage($);
   if ( errorMessage )
      $INCORRECT_ARGUMENT_WITH_MESSAGE( memoryBlock, errorMessage );
   $INCORRECT_ARGUMENT( memoryBlock );
}

//==================================================================================================

PVOID $Memory::Clear($A,PVOID memoryBlock,SIZE_T size $not_zero) $ret_target
{
   $$VERIFY_POINTER_ARG( memoryBlock );
   $$VERIFY_NOT_ZERO_SIZE_ARG( size );
   ::ZeroMemory( memoryBlock, size );
   return memoryBlock;
}

PVOID $Memory::Copy($A,$PCVoid source,PVOID target,SIZE_T size $not_zero) $ret_target
{
   $$VERIFY_POINTER_ARG( source );
   $$VERIFY_POINTER_ARG( target );
   $$VERIFY_NOT_ZERO_SIZE_ARG( size );
//#ifndef $EXCLUDE_VERIFICATION
   if ( (PBYTE)source + size - size != source ||
        (PBYTE)target + size - size != target )
      $INCORRECT_ARGUMENT_WITH_MESSAGE(size, $TEXT(
         L"Value of block size causes to pointer overflow (memory copying).",
         L"Значение размера блока влечёт к переполнению указателя (копирование памяти)." ));
   if ( source == target ||
        source < target && (PBYTE)source + size > target ||
        target < source && (PBYTE)target + size > source )
      $INCORRECT_ARGUMENTS_WITH_MESSAGE( $TEXT(
         L"Overlapped source and target areas for memory copying operation.",
         L"Перехлёстывание исходной и целевой областей копирования памяти." ));
//#endif // $EXCLUDE_VERIFICATION
   ::CopyMemory( target, source, size );
   return target;
}

//==================================================================================================

int $Memory::VerifyAllocationSize($A,SIZE_T size $not_zero) $fatal_error_on_failure
{
   $$VERIFY_NOT_ZERO_SIZE_ARG( size );
   if ( size > INT_MAX )
      $FatalError::RaiseIncorrectArgumentWithMessageFmt($$CI, L"size",
      AllocationSizeExcess_MessageFmt(), INT_MAX );
   return (int)size;
}
void $Memory::Fail_AllocationSizeExcess($CI_REF) $not_returns
{
   $FatalError::RaiseIncorrectArgumentsWithMessageFmt($$CI,
      AllocationSizeExcess_MessageFmt(), INT_MAX );
}
void $Memory::Fail_IncorrectPointerForBlockSizeRequest( $CI_REF ) $not_returns
{
   $FatalError::RaiseIncorrectArgumentsWithMessage($$CI,
      IncorrectPointerForBlockSizeRequest_Message() );
}

PCWSTR $Memory::AllocationSizeExcess_MessageFmt() $static_pointer $not_null
{
   return $TEXT(
      L"Allocation size is too big.\r\n"
      L"Maximum allowed size is %d (INT_MAX) bytes.",
      L"Слишком большой размер выделяемой памяти.\r\n"
      L"Максимальный размер аллокации составляет %d (INT_MAX) байт." );
}
PCWSTR $Memory::IncorrectPointerForBlockSizeRequest_Message()
{
   return $TEXT(
      L"Incorrect pointer for memory block size request.",
      L"Некорректный указатель для запроса размера блока памяти." );
}

PCWSTR $Memory::SizeTooLargeForTwiceEnlarge_Message() $static_pointer $not_null
{
   return $TEXT(
      L"Size of memory block is too large for TwiceEnlarge.",
      L"Размер блока памяти слишком велик для применения TwiceEnlarge." );
}

//==================================================================================================
} // RuntimeForStatic
