; _______________________________________________________________________________________
;|                                                                                       |
;| ..::FreshLib::..  Free, open source. Licensed under "BSD 2-clause" license."          |
;|_______________________________________________________________________________________|
;
;  Description: Dynamic memory management library. Win32 part.
;
;  Target OS: Win32
;
;  Dependencies:
;
;  Notes: Uses Heap functions of Win32 API. Also contains some debug and profiling code,
;         that needs some revision in order to be either removed, or legalized.
;_________________________________________________________________________________________

uses kernel32

uglobal
  var _hHeap=?
endg


uglobal
  var ptrMemoryList  = ?
  var MemMutex       = ?
endg


if DebugMemory

    struct TMemoryItem
      .next     dd ?
      .pointer  dd ?
      .resized  dd ?
      .callfrom dd ?
    ends

    proc InitAllocStack
    begin
            stdcall MutexCreate, 0, MemMutex
            stdcall MutexRelease, MemMutex
            return
    endp

end if


if used SpaceAllocate & ~ used GetMem
initialize ReserveAddressSpace
begin
        invoke  VirtualAlloc, 0, 1024*1024*1024, MEM_RESERVE, PAGE_READWRITE
        mov     [_MemoryFreeSpace], eax
        return
endp
end if


if used GetMem & ~ used SpaceAllocate
initialize InitMemoryManager
begin
        invoke  GetProcessHeap
        mov     [_hHeap], eax

if DebugMemory
        call    InitAllocStack
end if
        return
endp
end if



if DebugMemory
finalize FinalizeMemoryManager
begin
        stdcall MutexDestroy, MemMutex

        mov     eax, [ptrMemoryList]

        int3
        return
endp
end if



; Allocates memory block with given size.
; Returns: CF=1 if error.
;          if CF=0, eax = pointer to the memory block allocated.

body GetMem
begin
        push    ecx edx
        if defined options.NoZeroMemory & options.NoZeroMemory = 1
          invoke  HeapAlloc, [_hHeap], 0, [.size]
        else
          invoke  HeapAlloc, [_hHeap], HEAP_ZERO_MEMORY, [.size]
        end if
        test    eax, eax
        jz      .error

if DebugMemory
            pushad

            mov     esi, eax    ; allocated address

            stdcall WaitForMutex, MemMutex, -1


            invoke  HeapAlloc, [_hHeap], HEAP_ZERO_MEMORY, sizeof.TMemoryItem
            mov     edi, eax

            mov     [edi+TMemoryItem.pointer], esi

            mov     ecx, [esp+11*4]
            cmp     ecx, CreateArray.retaddr
            jne     @f
            mov     ecx, [esp+11*4+5*4]
@@:
            mov     [edi+TMemoryItem.callfrom], ecx

            mov     ecx, edi
            xchg    [ptrMemoryList], ecx
            mov     [edi+TMemoryItem.next], ecx

            stdcall MutexRelease, MemMutex
            popad
end if
        clc
        pop     edx ecx
        return

.error:
        stc
        pop     edx ecx
        return
endp



body FreeMem
begin
        push    eax ecx edx

if DebugMemory
            pushad

            stdcall WaitForMutex, MemMutex, -1

            mov     ebx, ptrMemoryList
            mov     esi, [ptrMemoryList]
            mov     eax, [.ptr]

    .loop:
            test    esi, esi
            jz      .notfound

            cmp     [esi+TMemoryItem.pointer], eax
            je      .found

            mov     ebx, esi
            mov     esi, [esi+TMemoryItem.next]
            jmp     .loop

    .notfound:
            int3
            jmp     .exit2

    .found:
            mov     eax, [esi+TMemoryItem.next]
            mov     [ebx+TMemoryItem.next], eax

            invoke  HeapFree, [_hHeap], 0, esi

    .exit2:
            stdcall MutexRelease, MemMutex
            popad
end if

        cmp     [.ptr], 0
        je      .finish

        invoke  HeapFree, [_hHeap], 0 , [.ptr]
        test    eax, eax
        jnz     .finish

        if defined hApplication & defined StrLib
          pushad
          stdcall StrDupMem, <'Error deallocating memory! Please report the caller address.', 13, 10, 'Caller address: $'>
          mov     ebx, eax

          stdcall NumToStr, [ebp+4], ntsHex or ntsUnsigned
          stdcall StrCat, ebx, eax
          stdcall StrDel, eax

          stdcall StrPtr, ebx

          invoke MessageBoxA, [hApplication], eax, 'Memory problem!', MB_ICONERROR or MB_OK

          stdcall StrDel, ebx
          popad
        end if

        if defined options.DebugMode & options.DebugMode
          OutputValue "Error deallocating memory. Call from $", [ebp+4], 16, 8
        end if

.finish:
        pop     edx ecx eax
        return
endp




body ResizeMem
begin
        push    ecx edx

        invoke  HeapReAlloc, [_hHeap], HEAP_ZERO_MEMORY, [.ptr], [.newsize]
        test    eax, eax
        jz      .error

if DebugMemory
            pushad

            stdcall WaitForMutex, MemMutex, -1

            mov     esi, [ptrMemoryList]
            mov     ecx, [.ptr]

    .loop:
            test    esi, esi
            jz      .notfound

            cmp     [esi+TMemoryItem.pointer], ecx
                        je      .found

            mov     esi, [esi+TMemoryItem.next]
            jmp     .loop

    .notfound:
            int3
            jmp     .exit2

    .found:
            mov     [esi+TMemoryItem.pointer], eax
            inc     [esi+TMemoryItem.resized]
            mov     ecx, [esp+11*4]
            cmp     ecx, AddArrayItems.readdr
            jne     @f
            mov     ecx, [esp+11*4+6*4]
@@:
            mov     [esi+TMemoryItem.callfrom], ecx

    .exit2:
            stdcall MutexRelease, MemMutex
            popad
end if

        clc
        pop     edx ecx
        return

.error:
        mov     eax, [.ptr]
        stc
        pop     edx ecx
        return
endp



;
;body GetMemSize, .ptr
;begin
;        push    ecx edx
;        invoke  HeapSize, [_hHeap], 0, [.ptr]
;        cmp     eax, -1
;        je      .error
;        clc
;        pop     edx ecx
;        return
;
;.error:
;        stc
;        pop     edx ecx
;        return
;endp



body SpaceAllocate
begin
        push    ecx edx
        invoke  VirtualAlloc, [_MemoryFreeSpace], [.size], MEM_COMMIT, PAGE_READWRITE
        test    eax, eax
        pop     edx ecx
        return
endp


