| Index | Manual | Download | Source | Macros | Embedded word Case ins.
Sitemap | Links | Forum | Tests | Projects

# memory32.htm
Classes
BUFFER
LIST
POOL
STACK
STREAM
Macros
BufferClear
BufferCreate
BufferDecrement
BufferNew
BufferResize
BufferRetrieve
BufferStore
BufferStore$
BufferStoreByte
BufferStoreDword
BufferStoreQword
BufferStorePascalString
BufferStoreWord
ListCreate
ListGetFirst
ListGetLast
ListGetNext
ListGetPrev
ListInsert
ListNew
ListRemove
ListStore
PoolCreate
PoolDestroy
PoolGetSIze
PoolNew
PoolStore
StackClear
StackCreate
StackPeekLast
StackPeekPrev
StackPop
StackPush
StreamClear
StreamCreate
StreamDump
StreamGetLines
StreamGetSize
StreamReadByte
StreamReadLn
StreamReset
StreamRetrieve
StreamStore
StreamStoreByte
StreamStore$
StreamStoreDword
StreamStoreLn
StreamStoreWord

This file can be included to 32bit programs written in Euro Assembler. It contains OS-independent macros for dynamic memory management.
The file memory32.htm defines memory allocation class POOL (similar to system heap functions), and its derivations BUFFER, LIST, STACK, STREAM. See also Memory management.

Derived methods Buffer*, List*, Stack*, Stream* allocate their memory from the object Pool, and the Pool allocation and deallocation depends on macroinstructions MemAlloc and MemFree which are defined in winapi or linapi.

  Memory management dependency ┌──────┬──────┬──────┬──────┐ │Buffer│ List │Stack │Stream│ └──────┴──────┴──────┴──────┘ ↓ ┌───────────────────────────┐ │ Pool │ └───────────────────────────┘ ↓ ┌───────────────────────────┐ │ Sys │ └───────────────────────────┘ ↓ ┌───────────────────────────┐ │ OS │ └───────────────────────────┘

All macros in this library return carry flag set if some error occurs, which usually signalizes lack of system memory or bad parameter. Returned values are not valid when CF=1.

Keyword parameter ErrorHandler= of macro PoolCreate allows to specify a callback procedure which will be invoked on allocation error. Allocation exceptions then can be treated by ErrorHandler (for instance by aborting the program gracefully), so the derived methods don't need to catch errors.

    memory32 HEAD                          ; Library interface.

↑ POOL

  Object POOL allocates virtual memory from operating system in big blocks (their size is multiple of virtual-memory granularity). Block are not continuous in addressing space, they are bound in a unidirectional list. Each pool block allocated from OS has the block size and the pointer to previous block in 2 DWORDs at its bottom. The POOL structure itself is created in the very first pool block.

  Amount of continuos memory requested by PoolNew, BufferStore*, StreamStore* etc. is not limited. Whenever there is no enough free memory in the last pool block, PoolNew or PoolStore will request an additional empty pool block from OS, with size of the requested memory rounded up to allocation granularity.

  Allocated memory block cannot be returned to OS individually. The whole list of pool blocks will be freed at once by PoolDestroy.

  After PoolCreate. ┌─────────┐ ┐ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┌ ├─────────┤<─┐ │ │ │.Ptr ├──┘ │ │ ├─────────┤ │ │ ┌─┤.Last │ │ POOL│ │ ├─────────┤ ┌─┤ │ │ │.ErrH │ │ │ │ │ ├─────────┤ │ │ │ │ │.Gran=64K│ │ │ └ │ ├─────────┤ │ │ │ │Prev=0 │ │ │ │ ├─────────┤ │ │ │ │Size=64K │<─┘ │ └>└─────────┘ ┘ After many PoolNew allocations. ┌─────────┐ ┐ The last block was increased. │ │ │ │ │ │ │ │ │ ┌──────────────────>│ │ │ │ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ ┌─────────┐ │ ┌─────────┐ │░░░░░░░░░│ ┌─┤ │░░░░░░░░░│ │ │░░░░░░░░░│ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │░░░░░░░░░│ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │░░░░░░░░░│ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │░░░░░░░░░│ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │░░░░░░░░░│ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │░░░░░░░░░│ │░░░░░░░░░│ │ │ ┌ ├─────────┤ │ │░░░░░░░░░│ │░░░░░░░░░│ │ │ │ │.Ptr ├─┘ │░░░░░░░░░│ │░░░░░░░░░│ │ │ │ ├─────────┤ │░░░░░░░░░│ │░░░░░░░░░│ │ │ │ │.Last ├───┐ │░░░░░░░░░│ │░░░░░░░░░│ │ │ POOL│ ├─────────┤ │ │░░░░░░░░░│ │░░░░░░░░░│ │ │ │ │.ErrH │ │ │░░░░░░░░░│ │░░░░░░░░░│ │ │ │ ├─────────┤ │ │░░░░░░░░░│ │░░░░░░░░░│ │ │ │ │.Gran=64K│ │ │░░░░░░░░░│ │░░░░░░░░░│ │ │ └ ├─────────┤ │ ├─────────┤ ├─────────┤ │ │ │Prev=0 │ ┌─┼─┤ Prev │ ┌───┤ Prev │ │ │ ├─────────┤ │ │ ├─────────┤ │ ├─────────┤ │ │ │Size=64K │ │ │ │ Size=64K│ │ │Size=128K│<─┘ │ └─────────┘<┘ │ └─────────┘<┘ ┌>└─────────┘ ┘ │ │ └───────────────┘

    POOL STRUC
     .Gran      D D ; Granularity of memory allocation in bytes (64 KB).
     .ErrH      D D ; Address of error handler callback procedure. Ignored when 0.
     .Last      D D ; Pointer to the last pool block allocated from OS.
     .Ptr       D D ; Pointer to the unoccupied memory in the last pool block.
     ENDSTRUC POOL

↑ PoolCreate Size=64K, ErrorHandler=0

  will allocate one block from OS virtual memory and create the POOL structure near its bottom.
  This is the first pool block, other blocks may be allocated later on demand.

Input
  Size=64K is the default size of memory blocks which will be automatically allocated from OS whenever the current block is full.
  ErrorHandler=0 is a pointer to the callback procedure which is called when some POOL method detects an error. The procedure gets no input and it may change any register. This parameter is ignored when ErrorHandler=0 (default value).
Output
  CF=0, EAX=pointer to the just created POOL structure (pool handle).
Error
  CF=1, EAX=0, ErrorHandler is called before return from PoolCreate (if it is nonzero).
Depends on
  macros winapi#MemAlloc or linapi#MemAlloc.

    PoolCreate %MACRO Size=64K, ErrorHandler=0
        PUSHD %ErrorHandler, %Size
        CALL PoolCreate@RT:
    PoolCreate@RT:  PROC1
        PUSHAD
          XOR EAX,EAX
          MOV [ESP+7*4],EAX  ; Initialize %ReturnEAX for the case of error.
          MOV ECX,[ESP+9*4]  ; %Size.
          MOV ESI,64K        ; Granularity, fixed at 64 KB.
          MOV EDX,ESI
          ; ESI is granularity (64KB), ECX is requested 1st block size, EDX is the final Alloc size.
     .10: CMP ECX,EDX
          JBE .20:
          ADD EDX,ESI
          JMP .10:
     .20: MemAlloc EDX
     .30: MOV ECX,[ESP+10*4]  ; Offset of %ErrorHandler.
          JNZ .50:            ; If MemAlloc worked OK, continue.
          STC
          JECXZ .90:          ; If no ErrorHandler specified.
          CALL ECX
          STC
          JMP .90:            ; Abort.
     .50: MOV EDI,EAX         ; Address of the allocated memory block.
          MOV EAX,EDX         ; Size of the allocated block.
          STOSD               ; Store the size of this pool block as the first DWORD at its bottom.
          SUB EAX,EAX
          STOSD               ; Pointer to the Prev block is zero.
          MOV EBX,EDI         ; Where the POOL structure will be created.
          MOV [ESP+7*4],EDI   ; %ReturnEAX will point to the POOL structure.
          SUB EDI,8           ; EDI now points to the first and only pool block.
          LEA EAX,[EBX+SIZE# POOL] ; EAX now points to the vacant memory space.
          MOV [EBX+POOL.Gran],64K
          MOV [EBX+POOL.ErrH],ECX
          MOV [EBX+POOL.Last],EDI
          MOV [EBX+POOL.Ptr], EAX
     .90:POPAD
         RET 2*4
      ENDPROC1 PoolCreate@RT:
     %ENDMACRO PoolCreate

↑ PoolDestroy aPool
  will free all pool blocks allocated from operating system.
Input
  aPool is a pointer to POOL structure which was returned from PoolCreate.
Output
  CF=0, EAX=total amount of just released memory in bytes.
Error
  CF=1 if deallocation failed.
  EAX=total amount of just released memory until deallocation failed.
Depends on
  macros winapi#MemFree or linapi#MemFree.

    PoolDestroy %MACRO aPool
          PUSHD %aPool
          CALL PoolDestroy@RT:
    PoolDestroy@RT:  PROC1
          PUSHAD
           MOV EBX,[ESP+9*4] ; Pointer to the POOL structure.
           SUB ECX,ECX       ; ECX is a counter of total freed memory of this pool.
           TEST EBX
           STC
           JZ .90:           ; Abort if bad aPool was provided.
           MOV ESI,[EBX+POOL.Last] ; Begin deallocation with the last block.
    .10:   TEST ESI          ; VA of the pool block which is being deallocated.
           JZ .90:           ; If there are no more pool blocks on the list.
           MOV EAX,[ESI+0]   ; Brutto size of the block.
           MOV EDI,[ESI+4]   ; Pointer to the previous block in the list.
           ADD ECX,EAX       ; Total released pool size.
           MemFree ESI,EAX   ; Ask OS to free the memory block addressed with ESI.
           JC .Error:
           MOV ESI,EDI       ; Try the previous block.
           JMP .10:
    .Error:STC
    .90:   MOV [ESP+7*4],ECX ; Total amount of freed memory in %ReturnEAX.
          POPAD
          RET 4
      ENDPROC1 PoolDestroy@RT
    %ENDMACRO PoolDestroy

↑ PoolGetSize aPool
  returns total allocated size of all pool blocks.
Input
  aPool is a pointer to POOL structure which was returned from PoolCreate.
Output
  ECX= total comitted size of all pool blocks.
Error
  -

    PoolGetSize %MACRO aPool
          PUSH EBX,ESI
            MOV EBX,%aPool
            SUB ECX,ECX
            MOV ESI,[EBX+POOL.Last]
    PoolGetSizeA%.:
            TEST ESI
            JZ PoolGetSizeZ%.:
            ADD ECX,[ESI+0]  ; Brutto size of the block.
            MOV ESI,[ESI+4]  ; Pointer to the previous block in the list.
            JMP PoolGetSizeA%.:
    PoolGetSizeZ%.:
          POP ESI,EBX
      %ENDMACRO PoolGetSize

↑ PoolNew aPool, DataSize, Align=DWORD, ZeroTerminate=No, Zeroed=No
  will reserve a piece of memory (DataSize bytes long) from aPool. Contents of the reserved memory is undefined.
  This macro PoolNew requires another macro MemAlloc be defined before, which provides the interaction with OS.
Input
  aPool is a pointer to POOL structure which was returned from PoolCreate
  DataSize is requested size in bytes.
  Align= may specify BYTE/UNICHAR/WORD/DWORD/QWORD/OWORD or shortcuts B/U/W/D/Q/O (case insensitive) as requested alignment of the allocated memory.
  ZeroTerminate=No Boolean parameter. If Yes, the requested DataSize will be incremented by 1, making room for zero-byte terminating. This is used by PoolStore.
  Zeroed=No Boolean parameter. If Yes, the entire DataSize will be filled with zeroes (0x00).
Output
  CF=0
  EAX=pointer to the allocated memory in pool block of memory.
Error
  CF=1 if MemAlloc failed. Handler `POOL.ErrH` was called.
  EAX=0
Depends on
  winapi#MemAlloc or linapi#MemAlloc.

    PoolNew %MACRO aPool, DataSize, Align=DWORD, ZeroTerminate=No, Zeroed=No
            %PoolNewFlags %SETA 0
            %IF %ZeroTerminate
              %PoolNewFlags %SETA 0x40
            %ENDIF
            %IF %Zeroed
              %PoolNewFlags %SETA 0x20
            %ENDIF
            %PoolNewFlags %SETA %PoolNewFlags | "%Align[1]" == "O" & 15
            %PoolNewFlags %SETA %PoolNewFlags | "%Align[1]" == "Q" & 7
            %PoolNewFlags %SETA %PoolNewFlags | "%Align[1]" == "D" & 3
            %PoolNewFlags %SETA %PoolNewFlags | "%Align[1]" == "W" & 1
            %PoolNewFlags %SETA %PoolNewFlags | "%Align[1]" == "U" & 1
            PUSHD %PoolNewFlags, %DataSize, %aPool
            CALL PoolNew@RT:
    PoolNew@RT:  PROC1
         PUSHAD
          MOV EBX,[ESP+9*4]  ; %aPool.
          XOR EBP,EBP
          MOV [ESP+7*4],EBP  ; %ReturnEAX in case of error.
          TEST EBX
          STC
          JZ .90:            ; Error if no pool provided.
    .10:  MOV ECX,[ESP+10*4] ; %DataSize.
          MOV EDX,[ESP+11*4] ; %PoolNewFlags.
          BTR EDX,6          ; ZeroTerminate?
          ADC ECX,2*4        ; Increment if ZeroTerminated=Yes and then add 8 bytes for two bottom DWORDs.
          MOV EDI,[EBX+POOL.Ptr]
          BTR EDX,5          ; %Zeroed?
          SBB EBP,0
          ADD EDI,EDX        ; Apply the alignment. EDX=0,1,3,7,15.
          NOT EDX
          AND EDI,EDX        ; EDI is now aligned pointer to the new area.
          LEA EDX,[EDI+ECX]  ; EDX is now pointer to the end of requested area.
          MOV ESI,[EBX+POOL.Last] ; Pointer to the current pool block with our area.
          ADD ESI,[ESI+0]    ; Add pool block size. ESI now points to the top of the last pool block.
          CMP EDX,ESI
          JA .30:            ; Skip if the new area does not fit into remaining space in the current pool block.
          MOV [EBX+POOL.Ptr],EDX ; Update pointer to the free space in the last pool block.
          JMP .80:           ; Almost done. EDI is the aligned memory pointer to allocated memory.
    .30:  ; Not enough free space is left in this pool block. Allocate a new pool block.
          ; EBX=^POOL, ECX=DataSize.
          MOV EDX,[EBX-2*4]  ; Default block size is in DWORD below the POOL pointed to with EBX.
    .40:  CMP ECX,EDX
          JBE .50:           ; Continue when the requested size ECX is below the future pool block size EDX.
          ADD EDX,[EBX+POOL.Gran] ; Otherwise this particular pool block must be enlarged.
          JMP .40:
    .50:  MemAlloc EDX
          MOV EDI,EAX
          JNZ .70:           ; If allocation succeeded.
          MOV ECX,[EBX+POOL.ErrH] ; Allocation error happened.
          JECXZ .60:
          CALL ECX
    .60:  STC
          JMP .90:
    .70:  MOV ECX,[EBX+POOL.Last]
          MOV [EBX+POOL.Last],EDI
          MOV EAX,EDX
          STOSD               ; Store the size of the new pool block.
          MOV EAX,ECX
          STOSD               ; Store the pointer to its previous pool block.
          MOV [EBX+POOL.Ptr],EDI
          JMP .10:            ; This time the reservation attempt will not fail.
     .80: MOV [ESP+7*4],EDI   ; %ReturnEAX.
          TEST EBP            ; %Zeroed?
          JZ .90:
          MOV ECX,[ESP+10*4]  ; %DataSize.
          XOR EAX,EAX
          REP STOSB
     .90:POPAD
         RET 3*4
       ENDPROC1 PoolNew@RT
     %ENDMACRO PoolNew

↑ PoolStore aPool, DataPtr, DataSize, Align=BYTE, ZeroTerminate=No
  This inline macro PoolStore will reserve DataSize bytes of memory on pool and copy the Data there.
Input
  aPool is a pointer to POOL structure which was returned from PoolCreate.
  DataPtr points to the data which should be stored on pool.
  DataSize is the size of data in bytes.
  Align= may specify BYTE/UNICHAR/WORD/DWORD/QWORD/OWORD or shortcuts B/U/W/D/Q/O (case insensitive) as alignment of the stored data.
  ZeroTerminate=No. If Yes, one NULL character is stored after the data on aPool.
Output
  CF=0
  EAX=pointer to the just stored data in pool block.
Error
  CF=1 if VirtualAlloc() failed. Handler `POOL.ErrH` was called.
  EAX=0
Depends on
  PoolNew

    PoolStore %MACRO aPool, DataPtr, DataSize, Align=BYTE, ZeroTerminate=No
                PUSH %DataSize, %DataPtr, %aPool
                PUSH ECX,ESI,EDI
                  MOV EAX,[ESP+3*4] ; %aPool.
                  MOV ESI,[ESP+4*4] ; %DataPtr.
                  MOV ECX,[ESP+5*4] ; %DataSize.
                  PoolNew EAX,ECX,Align=%Align,ZeroTerminate=%ZeroTerminate
    :             JC PoolStoreError%.:
                  MOV EDI,EAX
                  REP MOVSB
                  %IF %ZeroTerminate
                    MOV [EDI],CL
                  %ENDIF
    PoolStoreError%.:
                POP EDI,ESI,ECX
                LEA ESP,[ESP+3*4] ; Discard 3 pushed arguments, keep CF.
             %ENDMACRO PoolStore

↑ BUFFER

  Object BUFFER is an unformated storage for objects (strings) of arbitrary size.

  Items with fixed or variable size can be stored to the buffer one after another and the entire buffer content is always available as a continuous block.

  The initial Size specified at BUFFER creation should be large enough to accomodate all expected data. Whenever the total buffer size tries to exceed the allocated size specified at buffer creation, data area is reallocated with doubled size, the old buffer contents is copied to the new position and the original buffer space is abandoned. The BUFFER structure itself always stays in its original position.

  Buffer can be destroyed only when the hosting pool is destroyed but it can be cleared and reused.

  Not yet exhausted buffer ┌──>┌───────┐┐ │ │ ││ │┌─>│ ││ ││ │░░░░░░░││ ││ │░░░░░░░││Size ││ │░░░░░░░││ ││ │░░░░░░░││ ││ │░░░░░░░││ ││┌>├───────┤┘ ││└─│.Bottom│ ││ ├───────┤ │└──│.Ptr │ │ ├───────┤ └───│.Top │ ├───────┤ │.Pool │ └───────┘ Reallocated buffer with doubled Size ┌────────>┌───────┐┐ │ │ ││ │ │ ││ │ │ ││ │ │ ││ │ │ ││ │ ┌─────>│ ││ │ │ │░░░░░░░││ ┌───────┐┐ │ │ │░░░░░░░││2*Size │ ││ │ │ │░░░░░░░││ │ ││ │ │ │░░░░░░░││ │░░░░░░░││ │ │ │░░░░░░░││ │░waste░││Size│ │ │░░░░░░░││ │░░░░░░░││ │ │ │░░░░░░░││ │░░░░░░░││ │ │ │░░░░░░░││ │░░░░░░░││ │ │ │░░░░░░░││ ├───────┤┘ │ │ ┌───>└───────┘┘ │.Bottom│─────┼──┼─┘ ├───────┤ │ │ │.Ptr │─────┼──┘ ├───────┤ │ │.Top │─────┘ ├───────┤ │.Pool │ └───────┘

    BUFFER STRUC
     .Pool   D D ; Pool handle obtained from PoolCreate.
     .Top    D D ; Pointer to the end of allocated buffer space.
     .Ptr    D D ; Ptr to the next free position in buffer.
     .Bottom D D ; Ptr to the beginning of buffer data.
     ENDSTRUC BUFFER

↑ BufferCreate aPool, Size=16K
  will allocate an empty (cleared) buffer on the pool specified with the first parameter. Buffer memory is DWORD aligned.
Input
  aPool is a pointer to POOL structure which was returned from PoolCreate.
  Size= is the initial size of the BUFFER plus payload data.
  When underestimated, it will be automatically increased if necessary but this wastes memory a little.
Output
  CF=0, EAX=pointer to the BUFFER (buffer handle).
Error
  CF=1, EAX=0
Depends on
  PoolNew

    BufferCreate %MACRO aPool, Size=16K
          PUSHD %Size, %aPool
          CALL BufferCreate@RT:
    BufferCreate@RT: PROC1
         PUSHAD
          MOV EAX,1K         ; Minimal acceptable buffer size.
          MOV ECX,[ESP+10*4] ; %Size.
          MOV EDX,[ESP+ 9*4] ; %aPool.
          CMP ECX,EAX
          JAE .20:
          XCHG EAX,ECX
    .20:  PoolNew EDX,ECX, Align=DWORD
          MOV [ESP+7*4],EAX  ; %ReturnEAX.
          JC .90:
          MOV EBX,EAX
          MOV [EBX+BUFFER.Pool],EDX
          LEA EAX,[EBX+SIZE#BUFFER]
          MOV [EBX+BUFFER.Ptr],EAX
          MOV [EBX+BUFFER.Bottom],EAX
          ADD ECX,EBX        ; Also set CF=0.
          MOV [EBX+BUFFER.Top],ECX
    .90: POPAD
         RET 2*4
       ENDPROC1 BufferCreate@RT:
     %ENDMACRO BufferCreate

↑ BufferClear aBuffer
  marks the buffer as empty. It does not overwrite the old data.
Input
  aBuffer is a pointer to BUFFER structure which was returned from BufferCreate.
Output
  aBuffer was marked as empty.
Error
  -

    BufferClear %MACRO aBuffer
          PUSH ECX
            MOV ECX,%aBuffer
            JECXZ BufferClear%.: ; If bad aBuffer was provided, skip.
           PUSHD [ECX+BUFFER.Bottom]
           POPD  [ECX+BUFFER.Ptr]
          BufferClear%.:
          POP ECX
     %ENDMACRO BufferClear

↑ BufferNew aBuffer, DataSize, Zeroed=No
  will allocate DataSize bytes on aBuffer and return pointer to the allocated memory.
Input
  aBuffer is a pointer to BUFFER structure which was returned from BufferCreate.
  DataSize is the requested allocation size.
  Zeroed=No. Zeroed=Yes will clear the just allocated memory.
Output
  CF=0, EAX=pointer to the just allocated space on aBuffer.
Error
  CF=1 EAX=0
Depends on
  PoolNew

    BufferNew %MACRO aBuffer, DataSize, Zeroed=No
         %IF %Zeroed
           PUSHD -1
         %ELSE
           PUSHD 0
         %ENDIF
         PUSHD %DataSize, %aBuffer
         CALL BufferNew@RT:
    BufferNew@RT: PROC1
         PUSHAD
          SUB EAX,EAX
          MOV EBX,[ESP+9*4]    ; %aBuffer.
          MOV [ESP+7*4],EAX    ; For the case of error.
          TEST EBX
          STC
          JZ .90:              ; If bad buffer provided.
     .10: MOV ECX,[ESP+10*4]   ; %DataSize.
          MOV EAX,[EBX+BUFFER.Ptr]
          MOV [ESP+7*4],EAX    ; %ReturnEAX.
          TEST EAX
          JNZ .20:
          ; Buffer not initialized or other error. Abort.
          MOV ECX,[EBX+BUFFER.Pool]
          JECXZ .Err:
          MOV ECX,[ECX+POOL.ErrH]
          JECXZ .Err:
          CALL ECX              ; Call error handler.
    .Err: STC
          JMP .90:              ; Abort.
     .20: ADD EAX,ECX           ; EAX is now the top of wanna-be-allocated area.
          CMP EAX,[EBX+BUFFER.Top]
          JBE .70:              ; If the amount of free space on buffer is sufficient.
          ; Buffer is too small. Allocate new space with doubled (or more) size.
          MOV EAX,[EBX+BUFFER.Top]
          SUB EAX,[EBX+BUFFER.Bottom]
          MOV EDX,EAX           ; Old buffer size.
          CMP EAX,ECX
          JAE .30:              ; If doubled size is sufficient.
          MOV EAX,ECX
     .30: ADD EDX,EAX           ; Add the old buffer's size.
          PoolNew [EBX+BUFFER.Pool],EDX, Align=DWORD
          JC .90:
          MOV EDI,EAX
          MOV ESI,[EBX+BUFFER.Bottom]
          MOV [EBX+BUFFER.Bottom],EAX
          ADD EAX,EDX
          MOV [EBX+BUFFER.Top],EAX
          MOV ECX,[EBX+BUFFER.Ptr]
          SUB ECX,ESI
          REP MOVSB              ; Copy the old buffer's contents.
          MOV [EBX+BUFFER.Ptr],EDI
          JMP .10:               ; Try again. This time it will succeed.
     .70: MOV EDI,[EBX+BUFFER.Ptr]
          MOV [EBX+BUFFER.Ptr],EAX
          TESTB [ESP+11*4],1     ; Zeroed?
          JZ .90:
          SUB EAX,EDI
          MOV ECX,EAX
          XOR EAX,EAX
          REP STOSB
     .90:POPAD
         RET 3*4
       ENDPROC1 BufferNew@RT:
     %ENDMACRO BufferNew

↑ BufferStore aBuffer, DataPtr, DataSize
  will allocate DataSize bytes and copy the data specified with DataPtr,DataSize.
Input
  aBuffer is a pointer to BUFFER structure which was returned from BufferCreate.
  DataPtr is a pointer to data to be stored on the buffer.
  DataSize is the requested data size in bytes.
Output
  CF=0, data are stored to buffer.
Error
  CF=1
Depends on
  BufferNew

    BufferStore %MACRO aBuffer, DataPtr, DataSize
         PUSHD %DataSize, %DataPtr, %aBuffer
         CALL BufferStore@RT:
    BufferStore@RT: PROC1
         PUSHAD
          MOV EDX,[ESP+9*4]  ; aBuffer.
          MOV ECX,[ESP+11*4] ; DataSize.
          TEST EDX
          STC
          JZ .90:
          BufferNew EDX,ECX
          JC .90:
          MOV EDI,EAX
          MOV ESI,[ESP+10*4] ; DataPtr.
          REP MOVSB
     .90:POPAD
         RET 3*4
        ENDPROC1 BufferStore@RT:
     %ENDMACRO BufferStore

↑ BufferStoreByte aBuffer, Value
  will allocate 1 byte and copy the BYTE value to aBuffer.
Input
  aBuffer is a pointer to BUFFER structure which was returned from BufferCreate.
  Value is an immediate 8bit value or a 32bit register/memory with the value in LSB.
Output
  CF=0, Byte Value was stored to aBuffer.
Error
  CF=1
Depends on
  BufferNew

    BufferStoreByte %MACRO aBuffer, Value
         PUSHD %Value, %aBuffer
         CALL BufferStoreByte@RT:
    BufferStoreByte@RT: PROC1
         PUSHAD
          MOV EDX,[ESP+9*4]  ; aBuffer.
          MOV EBX,[ESP+10*4] ; Value.
          BufferNew EDX,1
          JC .90:
          MOV [EAX],BL       ; Store the BYTE value.
     .90:POPAD
         RET 2*4
         ENDPROC1 BufferStoreByte@RT:
     %ENDMACRO BufferStoreByte

↑ BufferStoreWord aBuffer, Value
  will allocate 2 bytes and copy the WORD value to aBuffer.
Input
  aBuffer is a pointer to BUFFER structure which was returned from BufferCreate.
  Value is an immediate 16bit value or a 32bit register/memory with the value in the lower word.
Output
  CF=0, word value was stored to buffer.
Error
  CF=1
Depends on
  BufferNew

    BufferStoreWord %MACRO aBuffer, Value
         PUSHD %Value, %aBuffer
         CALL BufferStoreWord@RT
    BufferStoreWord@RT: PROC1
         PUSHAD
          MOV EDX,[ESP+9*4]  ; aBuffer.
          MOV EBX,[ESP+10*4] ; Value.
          BufferNew EDX,2
          JC .90:
          MOV [EAX],BX ; Store the WORD value.
     .90:POPAD
         RET 2*4
        ENDPROC1 BufferStoreWord@RT:
     %ENDMACRO BufferStoreWord

↑ BufferStoreDword aBuffer, Value
  will allocate 4 bytes and copy the DWORD Value to aBuffer.
Input
  aBuffer is a pointer to BUFFER structure which was returned from BufferCreate.
  Value is a 32bit register/memory or immediate value to be stored on the buffer.
Output
  CF=0, Dword value was stored to buffer.
Error
  CF=1
Depends on
  BufferNew

    BufferStoreDword %MACRO aBuffer, Value
         PUSHD %Value, %aBuffer
         CALL BufferStoreDword@RT:
    BufferStoreDword@RT: PROC1
         PUSHAD
          MOV EDX,[ESP+9*4]  ; aBuffer.
          MOV EBX,[ESP+10*4] ; Value.
          BufferNew EDX,4
          JC .90:
          MOV [EAX],EBX ; Store the DWORD value.
     .90:POPAD
         RET 2*4
        ENDPROC1 BufferStoreDword@RT:
     %ENDMACRO BufferStoreDword

↑ BufferStoreQword aBuffer, Value
  will allocate 8 bytes, sign-extend the Value to QWORD and store it to aBuffer.
Input
  aBuffer is a pointer to BUFFER structure which was returned from BufferCreate.
  Value is a 32bit register/memory or immediate value to be sign-extended and stored on the buffer.
Output
  CF=0, Qword value was stored to buffer.
Error
  CF=1
Depends on
  BufferNew

    BufferStoreQword %MACRO aBuffer, Value
         PUSHD %Value, %aBuffer
         CALL BufferStoreQword@RT:
    BufferStoreQword@RT: PROC1
         PUSHAD
          MOV ECX,[ESP+9*4]  ; aBuffer.
          BufferNew ECX,8
          JC .90:
          MOV EDI,EAX
          MOV EAX,[ESP+10*4] ; Value.
          CDQ
          STOSD
          XCHG EDX,EAX
          STOSD
     .90:POPAD
         RET 2*4
        ENDPROC1 BufferStoreQword@RT:
     %ENDMACRO BufferStoreQword

↑ BufferStore$ aBuffer, StringPtr
  Macro BufferStore$ will store the zero-terminated string of bytes to aBuffer.
  The terminating NUL byte is not stored.
Input
  aBuffer is a pointer to BUFFER structure as returned from BufferCreate.
  StringPtr is a pointer to the first byte of the ASCIIZ string.
Output
  CF=0, data are stored to buffer.
Error
  CF=1 when aBuffer=0.
Depends on
  BufferNew

    BufferStore$ %MACRO aBuffer, StringPtr
         PUSHD %StringPtr, %aBuffer
         CALL BufferStore$@RT:
    BufferStore$@RT: PROC1
         PUSHAD
          MOV EDX,[ESP+9*4]  ; aBuffer.
          MOV EDI,[ESP+10*4] ; StringPtr.
          TEST EDX
          MOV ESI,EDI
          STC
          JZ .90:            ; Bad buffer.
          XOR ECX,ECX
          XOR EAX,EAX
          NOT ECX
          REPNE SCASB        ; Find zero terminator.
          NOT ECX
          DEC ECX            ; ESI,ECX is now the netto string.
          JZ .90:
          BufferNew EDX,ECX
          JC .90:
          MOV EDI,EAX        ; Pointer to allocated room on buffer.
          REP MOVSB          ; Store string.
     .90:POPAD
         RET 2*4
       ENDPROC1 BufferStore$@RT:
     %ENDMACRO BufferStore$

↑ BufferStorePascalString aBuffer, StringPtr, Size=-1
  Macro BufferStorePascalString will store the string to aBuffer, prefixed with one usigned byte integer which contains string size (0..255).
  Size of the string is either explicitly specified or zero-termination is assumed.
  If the string netto size exceeds 254 bytes, only 255 is stored and the macro returns CF=1.
Input
  aBuffer is a pointer to BUFFER structure as returned from BufferCreate.
  StringPtr is a pointer to the first byte of the string.
  Size= -1 specifies the maximal string size. If the string is terminated with 0 byte, only its real size is used. The terminating zero is not stored as a part of the string.
Output
  CF=0, data are stored to buffer.
Error
  CF=1 when string size exceeded 255 bytes or wrong aBuffer.
Depends on
  BufferNew

    BufferStorePascalString %MACRO aBuffer, StringPtr, Size=-1
         PUSHD %Size, %StringPtr, %aBuffer
         CALL BufferStorePascalString@RT:
    BufferStorePascalString@RT: PROC1
         PUSHAD
          MOV EDX,[ESP+9*4]  ; aBuffer.
          MOV EDI,[ESP+10*4] ; StringPtr.
          MOV ECX,[ESP+11*4] ; Size.
          TEST EDX
          MOV ESI,EDI
          STC
          JZ .90:            ; Bad buffer.
          JECXZ .20:
          SUB EAX,EAX
          REPNE SCASB        ; Find zero terminator within specified %Size.
          JNE .20:
          DEC EDI            ; Skip the terminating zero.
     .20: MOV EBX,255
          SUB EDI,ESI
          CMP EDI,EBX
          MOV ECX,EDI
          JBE .40:
          MOV ECX,EBX        ; Saturate netto size ECX to max. 0..255 characters.
     .40: LEA EDI,[ECX+1]
          BufferNew EDX,EDI
          JC .90:
          MOV EDI,EAX        ; Allocated room on buffer.
          MOV EAX,ECX
          DEC EBX            ; 254.
          STOSB              ; Store netto string size.
          CMP EBX,ECX        ; Set CF when ECX was saturated to 255.
          REP MOVSB          ; Store string.
     .90:POPAD
         RET 3*4
       ENDPROC1 BufferStorePascalString@RT:
     %ENDMACRO BufferStorePascalString

↑ BufferDecrement aBuffer, Size=1
  omits the %Size byte(s) last stored on aBuffer.
Input
  aBuffer is a pointer to BUFFER structure which was returned from BufferCreate.
  Size=1 is number of bytes to omit (immediate number or GPR other than EAX,ECX).
Output
  CF=0 buffer contents was decremented.
Error
  CF=1 (bad buffer)

    BufferDecrement %MACRO aBuffer, Size=1
         PUSH EAX,ECX
          MOV ECX,%aBuffer
          STC
          JECXZ BufferDecrementC%.:
          MOV EAX,[ECX+BUFFER.Ptr]
          SUB EAX,%Size
          CMP EAX,[ECX+BUFFER.Ptr]
          JBE BufferDecrementA%.:
          MOV EAX,[ECX+BUFFER.Ptr] ; If %Size was negative - ignore.
       BufferDecrementA%.:
          CMP EAX,[ECX+BUFFER.Bottom]
          JAE BufferDecrementB%.:
          MOV EAX,[ECX+BUFFER.Bottom] ; If %Size was greater than buffer contents.
       BufferDecrementB%.:
          MOV [ECX+BUFFER.Ptr],EAX
          CLC
       BufferDecrementC%.:
         POP ECX,EAX
      %ENDMACRO BufferDecrement

↑ BufferResize aBuffer, Bytes, Stuff=0

  moves the buffer pointer by %Bytes value forward or backward. This will virtually change the size of data in aBuffer.

  Macro does nothing when %Bytes=0. If %Bytes is negative, the data block returned with subsequent BufferRetrieve will be shrinked. If %Bytes is positive, returned data block will be expanded. The enlarged portion of data contents will be filled with %Stuff bytes.

  If the negative %Bytes number is larger than current data contents available in %aBuffer, buffer pointer is reset to bottom, which is equivalent to BufferClear.

  If the buffer data enlarged with positive %Bytes is greater than the size allocated at BufferCreate, %aBuffer will be reallocated to this enlarged new size doubled, and old data contents will be copied from previous position to the new reallocated memory.

Input
  aBuffer is a pointer to BUFFER structure which was returned from BufferCreate.
  Bytes is immediate or 32bit GPR with number of bytes to virtually resize the buffer contents.
  Stuff=0 is immediate byte or 32bit GPR which defines in its LSB what data byte will be used in case of enlarging.
Output
  CF=0 Buffer pointer moved, allocation enlarged if necessary.
Error
  CF=1 (bad buffer or overflow)
Depends on
  BufferStoreByte

    BufferResize %MACRO aBuffer, Bytes, Stuff=0
         PUSHD %Stuff, %Bytes, %aBuffer
         CALL BufferResize@RT:
    BufferResize@RT: PROC1
         PUSHAD
          MOV EBX,[ESP+9*4]               ; %aBuffer.
          MOV EDX,[ESP+10*4]              ; %Bytes. It may be negative.
          TEST EBX
          STC
          JZ .90:                         ; Error: invalid buffer.
          TEST EDX
          JG .40:
          MOV EAX,[EBX+BUFFER.Ptr]        ; Shrink the buffer by -EDX bytes.
          MOV ESI,[EBX+BUFFER.Bottom]
          ADD EAX,EDX                     ; Reposition the new Ptr in EAX.
          CMP EAX,ESI
          JNB .10:
          MOV EAX,ESI                     ; Saturate if the new Ptr is below bottom.
    .10:  MOV [EBX+BUFFER.Ptr],EAX
          JMP .80:
    .40:  MOV EAX,[ESP+11*4]              ; Expand by EDX bytes of Stuff in EDI.
    .50:  BufferStoreByte EBX,EAX
          JC .90:                         ; Abort on error.
          DEC EDX
          JNZ .50:
    .80:  CLC
    .90: POPAD
         RET 3*4
        ENDP1 BufferResize@RT:
     %ENDMACRO BufferResize

↑ BufferRetrieve aBuffer
  will return pointer and size of all data currently stored in the buffer.
Input
  aBuffer is a pointer to BUFFER structure which was returned from BufferCreate. Macro returns ECX=ESI=0,CF=1 when aBuffer is NULL.
Output
  CF=0
  ESI=Ptr to the stored data (BUFFER.Bottom)
  ECX=Size of stored data (BUFFER.Ptr - BUFFER.Bottom)
  ZF=1 when ECX=0 (buffer exists and is empty).
Error
  CF=1,ECX=ESI=0 (uncreated buffer)
Example
  BufferRetrieve EBX ; EBX is pointer to a buffer, or 0. JNA .NoData: ; Jump when the buffer is empty (ECX=0,ZF=1) or hasn't been created (CF=1).

    BufferRetrieve %MACRO aBuffer
          %IF "%aBuffer" !== "ECX"
            MOV ECX,%aBuffer
          %ENDIF
          STC
          MOV ESI,ECX
          JECXZ BufferRetrieve%.:          ; Return CF on uncreated buffer.
          MOV ESI,[ECX+BUFFER.Bottom]
          MOV ECX,[ECX+BUFFER.Ptr]
          SUB ECX,ESI
        BufferRetrieve%.:
      %ENDMACRO BufferRetrieve

↑ LIST

  Object LIST is FIFO/LIFO bidirectional storage for items (leaves) of the same size. Leaves are DWORD aligned. Below the payload data of every leaf are pointers to the next and previous leaf data.

  Leaves are appended to the list with ListNew or ListStore methods and the list can be searched sequentially with ListGetNext or ListGetPrev.

  The LIST structure is created in pool memory with ListCreate , it can be destroyed only when the hosting pool is destroyed.

Example
  ; Walking the previously created MyList backwards: ListGetLast [MyList] JZ .NoMore: .N:CALL ProcessLeafAtEAX ListGetPrev EAX JNZ .N: .NoMore:
  1st 2nd 3rd ┌ ┌────┐ ┌────┐ ┌────┐ │ │░░░░│ │░░░░│ │░░░░│ ┌──────┐ │ │░░░░│ │░░░░│ │░░░░│ │.Count│ │ │░░░░│ │░░░░│ │░░░░│ ├──────┤ │ │░░░░│ │░░░░│ │░░░░│ │.Size │─┤ │░░░░│ │░░░░│ │░░░░│ ├──────┤ │ │░░░░│ │░░░░│ │░░░░│ │.Pool │ │ │░░░░│ │░░░░│ │░░░░│ ├──────┤ └ ┌> 0├────┤<┐┌>├────┤<┐┌>├────┤<┐ │.Last │─┐ │ │ 0 │ └┼─│Prev│ └┼─│Prev│ │ ├──────┤ │ │ -4├────┤ │ ├────┤ │ ├────┤ │ │.First│─┼─┘ │Next│──┘ │Next│──┘ │ 0 │ │ └──────┘ │ -8└────┘ └────┘ └────┘ │ └────────────────────────────────┘

    LIST STRUC
     .First D D ; Pointer to the first stored leaf.
     .Last  D D ; Pointer to the last stored leaf.
     .Pool  D D ; Pool handle obtained from PoolCreate.
     .Size  D D ; Data item netto size.
     .Count D D ; Number of stored items.
     ENDSTRUC LIST

↑ ListCreate aPool, Size
  will allocate an empty LIST structure on the pool specified with the first parameter.
Input
  aPool is a pointer to a POOL structure which was returned from PoolCreate.
  Size= is the size of data items which will be stored on the list later.
Output
  CF=0, EAX=pointer to the LIST structure (list handle).
Error
  CF=1, EAX undefined (Pool error)
Depends on
  PoolNew

    ListCreate %MACRO aPool, Size
            PUSHD %Size, %aPool
            CALL ListCreate@RT:
    ListCreate@RT: PROC1
            PUSHAD
             MOV EDX,[ESP+9*4]             ; %aPool.
             MOV ECX,[ESP+10*4]            ; %Size.
             PoolNew EDX, SIZE#LIST, Align=DWORD, Zeroed=Yes
             MOV [ESP+7*4],EAX             ; %ReturnEAX.
             JC .90:
             MOV [EAX+LIST.Pool],EDX
             MOV [EAX+LIST.Size],ECX
      .90:  POPAD
            RET 2*4
      ENDPROC1 ListCreate@RT:
     %ENDMACRO ListCreate

↑ ListNew aList, Zeroed=No
  will allocate an empty space for one leaf (plus two pointers) and append it on the list.
Input
  aList is a pointer to LIST structure which was returned from ListCreate.
  Zeroed=No Zeroed=Yes will clear the just allocated memory.
Output
  CF=0, EAX=pointer to the allocated space (LIST.Size bytes).
Error
  CF=1, EAX=0 (Pool error)

    ListNew %MACRO aList, Zeroed=No
            %IF %Zeroed
              PUSHD -1
            %ELSE
              PUSHD 0
            %ENDIF
            PUSHD %aList
            CALL ListNew@RT:
    ListNew@RT: PROC1
            PUSHAD
             MOV EBX,[ESP+9*4]  ; %aList.
             MOV [ESP+7*4],EBX  ; %ReturnEAX.
             TEST EBX
             STC
             JZ .90: ; Bad object.
             MOV ECX,[EBX+LIST.Size]
             ADD ECX,8          ; Additional room for two pointers.
             PoolNew [EBX+LIST.Pool],ECX, Align=DWORD
             MOV [ESP+7*4],EAX  ; %ReturnEAX.
             JC .90:
             MOV EDI,EAX
             MOV ESI,[EBX+LIST.Last]
             SUB EAX,EAX
             STOSD              ; Pointer Next.
             MOV EAX,ESI        ; LIST.Last may be 0 if this is the first leaf.
             STOSD              ; Pointer Prev.
             TEST EAX
             JZ .30:            ; If this leaf EDI is the first.
             MOV [ESI-8],EDI    ; EDI is pointer to this new leaf.
        .30: MOV [EBX+LIST.Last],EDI
             CMPD [EBX+LIST.First],0
             JNE .80:
             MOV [EBX+LIST.First],EDI
        .80: MOV [ESP+7*4],EDI  ; %ReturnEAX.
             INCD [EBX+LIST.Count]
             XOR EAX,EAX
             CMP EAX,[ESP+10*4] ; %Zeroed.
             JE .90:            ; If Zeroed=No.
             MOV ECX,[EBX+LIST.Size]
             REP STOSB          ; Clear the leaf payload area.
             CLC
        .90:POPAD
            RET 2*4
      ENDPROC1 ListNew@RT:
     %ENDMACRO ListNew

↑ ListStore aList, DataPtr
  will allocate an empty space for one leaf, append it on the list and copy the Data to this space.
Input
  aList is a pointer to LIST structure which was returned from ListCreate.
  DataPtr is pointer to the data to be stored on list. Size of the data was specified in ListCreate.
Output
  CF=0, EAX=pointer to the stored data on the list.
Error
  CF=1, EAX=0
Depends on
  ListNew

    ListStore %MACRO aList, DataPtr
            PUSHD %DataPtr, %aList
            CALL ListStore@RT:
    ListStore@RT: PROC1
           PUSHAD
            MOV EBX,[ESP+9*4]  ; %aList.
            MOV ESI,[ESP+10*4] ; %DataPtr.
            SUB EAX,EAX
            TEST EBX
            STC
            JZ .90:
            ListNew EBX, Zeroed=No
            JC .90:
            MOV EDI,EAX
            MOV ECX,[EBX+LIST.Size]
            REP MOVSB
       .90: MOV [ESP+7*4],EAX  ; %ReturnEAX.
           POPAD
           RET 2*4
      ENDPROC1 ListStore@RT:
     %ENDMACRO ListStore

↑ ListInsert aList, PrevLeaf, DataPtr
  will insert a new leaf right behind PrevLeaf on the List, update links and copy the Data to this space.
Input
  aList is a pointer to LIST structure which was returned from ListCreate.
  PrevLeaf is pointer to an existing leaf. When it is 0, data will be stored to the 1st position
  on the list then (same as ListStore).
  DataPtr is pointer to the data to be stored on list. It may be 0, an empty list is inserted in this case. Size of the data was specified in ListCreate.
Output
  CF=0, EAX=pointer to the stored data (LIST.Size bytes).
Error
  CF=1, EAX=0

    ListInsert %MACRO aList, PrevLeaf, DataPtr
           PUSHD %DataPtr, %PrevLeaf, %aList
           CALL ListInsert@RT:
    ListInsert@RT: PROC1
          PUSHAD
           MOV EBX,[ESP+9*4]  ; %aList.
           MOV ESI,[ESP+10*4] ; %PrevLeaf.
           XOR EAX,EAX
           MOV [ESP+7*4],EAX  ; %ReturnEAX.
           TEST EBX
           STC
           JZ .90:
           MOV ECX,[EBX+LIST.Size]
           ADD ECX,8          ; Additional room for two pointers.
           PoolNew [EBX+LIST.Pool],ECX,Align=DWORD
           JC .90:
           INCD [EBX+LIST.Count]
           MOV EDI,EAX
           TEST ESI
           JNZ .30:
           MOV EAX,[EBX+LIST.First]
           MOV EDX,EAX
           STOSD              ; Next.
           MOV EAX,ESI
           STOSD              ; Prev.
           MOV [EBX+LIST.First],EDI
           JMP .40:
    .30:   MOV EAX,[ESI-8]    ; Next.
           MOV EDX,EAX
           STOSD              ; Next.
           MOV EAX,ESI
           STOSD              ; Prev.
           MOV [ESI-8],EDI
     .40:  MOV [ESP+7*4],EDI  ; %ReturnEAX.
           TEST EDX
           JZ .50:
           MOV [EDX-4],EDI    ; Prev.
     .50:  JNZ .60:
           MOV [EBX+LIST.Last],EDI
     .60:  SUB ECX,8
           MOV ESI,[ESP+11*4] ; %DataPtr.
           TEST ESI
           JNZ .80:
           XOR EAX,EAX
           REP STOSB
           JMP .90:
     .80:  REP MOVSB
     .90: POPAD
          RET 3*4.
     ENDPROC1 ListInsert@RT:
    %ENDMACRO ListInsert

↑ ListRemove aList, LeafPtr
  removes one leaf from the list. The old data stays on pool but links are updated and the removed leaf is skipped.
Input
  aList is a pointer to LIST structure which was returned from ListCreate.
  LeafPtr is pointer to the leaf data which is to be removed.
Output
  ZF=1 if the list is now empty
Error
  -

    ListRemove %MACRO aList, LeafPtr
            PUSHD %LeafPtr, %aList
            CALL ListRemove@RT:
    ListRemove@RT: PROC1
          PUSHAD
           MOV EBX,[ESP+9*4]  ; %aList.
           MOV EDX,[ESP+10*4] ; %LeafPtr.
           TEST EBX
           JZ .90:
           TEST EDX
           JZ .90:
           MOV ESI,[EDX-4]    ; Prev.
           MOV EDI,[EDX-8]    ; Next.
           TEST ESI
           JZ .30:
           MOV [ESI-8],EDI    ; Next.
           TEST EDI
           JZ .50:
     .20:  MOV [EDI-4],ESI    ; Prev.
           JMP .80:
     .30:  MOV [EBX+LIST.First],EDI ; The first leaf was removed.
           TEST EDI
           JNZ .20:
     .50:  MOV [EBX+LIST.Last],ESI ; The last leaf was removed.
     .80:  DECD [EBX+LIST.Count]
     .90: POPAD
          RET 2*4
      ENDPROC1 ListRemove@RT:
     %ENDMACRO ListRemove

↑ ListGetFirst aList
  will return pointer to the data which was stored as the first leaf.
Input
  aList is a pointer to LIST structure which was returned from ListCreate.
Output
  ZF=0, EAX=pointer to the first leaf of stored data.
Error
  ZF=1, EAX=0 if the list is empty.

    ListGetFirst %MACRO aList
           PUSH ECX
            MOV ECX,%aList
            SUB EAX,EAX
            JECXZ ListGetFirst%.:
            MOV EAX,[ECX+LIST.First]
            ListGetFirst%.:
            TEST EAX
           POP ECX
      %ENDMACRO ListGetFirst

↑ ListGetNext LeafPtr
  will return pointer to the leaf which was stored right after the leaf specified with the first parameter.
Input
  LeafPtr is a pointer to data which was returned from ListGetFirst/Next/Last/Prev.
Output
  ZF=0, EAX=pointer to the following leaf of stored data.
  ZF=1 EAX=0 if there are no more leaves on list (LeafPtr was the last).

    ListGetNext %MACRO LeafPtr
           PUSH ECX
            MOV ECX,%LeafPtr
            SUB EAX,EAX
            JECXZ ListGetNext%.:
            MOV EAX,[ECX-8]
            ListGetNext%.:
            TEST EAX
           POP ECX
      %ENDMACRO ListGetNext

↑ ListGetLast aList
  will return pointer to the data which was stored as the last leaf.
Input
  aList is a pointer to LIST structure which was returned from ListCreate.
Output
  ZF=0, EAX=pointer to the last leaf of stored data.
Error
  ZF=1, EAX=0 if the list is empty.

    ListGetLast %MACRO aList
           PUSH ECX
            MOV ECX,%aList
            SUB EAX,EAX
            JECXZ ListGetLast%.:
            MOV EAX,[ECX+LIST.Last]
            ListGetLast%.:
            TEST EAX
           POP ECX
       %ENDMACRO ListGetLast

↑ ListGetPrev LeafPtr
  will return pointer to the leaf which was stored right before the leaf specified with the 1st parameter.
Input
  LeafPtr is a pointer to data which was returned from ListGetFirst/Next/Last/Prev.
Output
  ZF=0, EAX=poiubter to the previous leaf of stored data.
  ZF=1, EAX=0 if there are no more leaves on list (LeafPtr was the first).

    ListGetPrev %MACRO LeafPtr
           PUSH ECX
            MOV ECX,%LeafPtr
            SUB EAX,EAX
            JECXZ ListGetPrev%.:
            MOV EAX,[ECX-4]
            ListGetPrev%.:
            TEST EAX
           POP ECX
      %ENDMACRO ListGetPrev

↑ STACK

  Object STACK is LIFO storage for items of the same size. Stacked items are DWORD aligned and stored continuously in one pool block. Stack grows upward, STACK.Ptr is increased with StackPush and decreased with StackPop.

  Whenever the initial stack depth specified in StackCreate is not big enough, which may happen in StackPush, the stack is reallocated with doubled size, old content is copied to the new location and abandoned. The STACK structure itself stays in its original position.

  Stack contents can be erased with StackClear.

  > The STACK object should not be confused with machine stack adressed with SS:ESP.

Example
  INCLUDE1 "winapi.htm", "memory32.htm" ; PoolCreate requires the macro MemAlloc from "winapi.htm" or "linapi.htm". MyObject STRUC ; Define object which will be using the stack. .Member1 DD DWORD .Member2 DD DWORD ENDSTRUC MyObject MyPool DD 0 MyStack DD 0 [.text] PoolCreate Size=16K ; Create a pool (or reuse previously created one). MOV [MyPool],EAX JC .Error: StackCreate EAX, SIZE# MyObject, Depth=5 MOV [MyStack],EAX JC .Error: ; MyStack can be used now (StackPush, StackPeekLast, StackPop etc). PoolDestroy [MyPool] ; Return pool memory to OS at the end of program.
  Depth=5 ┌───────┐<──┐ │ │ │ │ │ │ ├───────┤ │ │ │ │ │ │ │ ├───────┤<─┐│ │░░░░░░░│ ││ │░░░░░░░│ ││ ├───────┤ ││ │░░░░░░░│ ││ │░░░░░░░│ ││ ┌├───────┤ ││ ││░░░░░░░│ ││ ┌┤│░░░░░░░│ ││ │└├───────┤<┐││ │ │.Bottom│─┘││ │ ├───────┤ ││ │ │.Ptr │──┘│ │ ├───────┤ │ │ │.Top │───┘ │ ├───────┤ └─│.Size │ ├───────┤ │.Pool │ └───────┘

    STACK STRUC
     .Pool   D D ; Pool handle obtained from PoolCreate.
     .Size   D D ; Size of one stacked item.
     .Top    D D ; Pointer to the end of allocated space.
     .Ptr    D D ; Pointer to the free space on STACK.
     .Bottom D D ; Pointer to the oldest pushed item.
     ENDSTRUC STACK

↑ StackCreate aPool, Size, Depth=16
  will allocate an empty STACK on the pool specified with the first parameter.
Input
  aPool is a pointer to a POOL structure which was returned from PoolCreate.
  Size is the size of each data object which will be pushed on the stack later.
  Depth=16 is the estimated maximal number of pushed objects.
  Underestimated depth will be increased automatically if necessary but this will waste the memory a little.
Output
  CF=0 EAX=pointer to the STACK (Stack handle).
Error
  CF=1, EAX=0 if pool allocation failed.

    StackCreate %MACRO aPool, Size, Depth=16
          PUSHD %Depth, %Size, %aPool
          CALL StackCreate@RT:
    StackCreate@RT: PROC1
         PUSHAD
          MOV EAX,[ESP+10*4] ; %Size.
          MOV ESI,[ESP+9*4]  ; %aPool.
          MOV ECX,EAX
          MULD [ESP+11*4]    ; %Depth.
          MOV EBX,EAX
          ADD EAX,SIZE#STACK
          PoolNew ESI,EAX,Align=DWORD
          MOV [ESP+7*4],EAX  ; %ReturnEAX.
          JC .90:
          MOV [EAX+STACK.Pool],ESI
          MOV [EAX+STACK.Size],ECX
          LEA EDI,[EAX+SIZE#STACK]
          MOV [EAX+STACK.Bottom],EDI
          MOV [EAX+STACK.Ptr],EDI
          ADD EDI,EBX
          MOV [EAX+STACK.Top],EDI
     .90:POPAD
         RET 3*4
      ENDPROC1 StackCreate@RT:
     %ENDMACRO StackCreate

↑ StackClear aStack
  will empty the stack. Old data are not erased and the stack memory remains allocated.
Input
  aStack is a pointer to STACK structure which was returned from StackCreate.
Output
  -
Error
  -

    StackClear %MACRO aStack
          PUSHD EBX, %aStack
          POP EBX
          PUSHD [EBX+STACK.Bottom]
          POPD  [EBX+STACK.Ptr], EBX
       %ENDMACRO StackClear

↑ StackPush aStack, DataPtr
  will copy the data on stack and increase STACK.Ptr.
Input
  aStack is a pointer to STACK structure which was returned from StackCreate.
  DataPtr is pointer to the data to be pushed. DataPtr may be 0; zeroes will be pushed on the Stack in this case. Data size was specified in StackCreate.
Output
  CF=0, EAX=pointer to the just pushed data item on stack, i.e. STACK.Ptr before this macro was invoked.
Error
  CF=1 (Pool error)

    StackPush %MACRO aStack, DataPtr
         PUSHD %DataPtr, %aStack
         CALL StackPush@RT:
    StackPush@RT: PROC1
         PUSHAD
          MOV EBX,[ESP+9*4]                ; %aStack
          TEST EBX
          STC
          JZ .90:
     .10: MOV EDI,[EBX+STACK.Ptr]
          CMP EDI,[EBX+STACK.Top]
          JB .50:
         ; Not enough space on stack.
          MOV EAX,[EBX+STACK.Top]
          MOV ESI,[EBX+STACK.Bottom]
          SUB EAX,ESI
          MOV ECX,EAX
          LEA EDX,[EAX+EAX]                ; Double the stack room.
          PoolNew [EBX+STACK.Pool],EDX, Align=DWORD
          JC .90:
          MOV EDI,EAX                      ; New room with doubled size (STACK.Bottom).
          MOV [EBX+STACK.Bottom],EAX
          ADD EDX,EAX                      ; New STACK.Top.
          REP MOVSB                        ; Copy old stack content.
          MOV [EBX+STACK.Ptr],EDI
          MOV [EBX+STACK.Top],EDX
     .50: ; There is sufficient free space on stack now.
          MOV ESI,[ESP+10*4]               ; %DataPtr.
          MOV ECX,[EBX+STACK.Size]
          MOV [ESP+7*4],EDI                ; STACK.Ptr before StackPush.
          TEST ESI
          JZ .70:                          ; Go to store zeroes when NULL data pointer was provided.
          REP MOVSB                        ; Otherwise copy the data.
          JMP .80:
     .70: SUB EAX,EAX
          REP STOSB
     .80: MOV [EBX+STACK.Ptr],EDI          ; Update the new incremented stack pointer.
          CLC
     .90:POPAD
         RET 2*4
      ENDPROC1 StackPush@RT:
     %ENDMACRO StackPush

↑ StackPop aStack
  will decrease STACK.Ptr and return pointer to the popped data.
Input
  aStack is a pointer to STACK structure which was returned from StackCreate.
Output
  CF=0, EAX=pointer to the just popped data item on stack, i.e. STACK.Ptr after this macro was invoked.
  Although the data item is technically popped from the stack, it is still available at the pointer returned in EAX until another StackPush will overwrite it.
Error
  CF=1, EAX=undefined when he stack was empty.

    StackPop %MACRO aStack
         PUSHD ECX,%aStack
          POP ECX
          STC
          JECXZ StackPop%.:                ; Bad parameter.
          MOV EAX,[ECX+STACK.Ptr]
          SUB EAX,[ECX+STACK.Size]
          CMP EAX,[ECX+STACK.Bottom]
          JC StackPop%.:                   ; If the stack is empty.
          MOV [ECX+STACK.Ptr],EAX
     StackPop%.:
         POP ECX
     %ENDMACRO StackPop

↑ StackPeekLast aStack
  will return pointer to the latest pushed data but, unlike StackPop, it does not remove the data from stack. STACK.Ptr is unchanged.
Input
  aStack is a pointer to the STACK structure which was returned from StackCreate.
Output
  CF=0, EAX=pointer to the last data object on stack.
Error
  CF=1, EAX=undefined. The stack was empty, nothing to peek at.

    StackPeekLast %MACRO aStack
          PUSH ECX
           SUB EAX,EAX
           MOV ECX,%aStack
           STC
           JECXZ StackPeekLast%.:
           MOV EAX,[ECX+STACK.Ptr]
           SUB EAX,[ECX+STACK.Size]
           CMP EAX,[ECX+STACK.Bottom]
          StackPeekLast%.:
          POP ECX
     %ENDMACRO StackPeekLast

↑ StackPeekPrev aStack, DataPtr
  will return pointer to the data item predecessing the object specified with DataPtr. STACK.Ptr is unchanged.
Input
  aStack is a pointer to STACK structure which was returned from StackCreate.
  DataPtr is pointer to data item returned from invocation of StackPeekLast or StackPeekPrev.
Output
  CF=0, EAX=pointer to the previous data item on stack.
Error
  CF=1, EAX=undefined at the bottom of stack, nothing to peek at.

    StackPeekPrev %MACRO aStack, DataPtr
        PUSH ECX
         PUSHD %aStack
         POP ECX
         STC
         JECXZ StackPeekPrev%.:
         %IF "%DataPtr" == "ECX"
           MOV EAX,[ESP]
         %ELSE
           MOV EAX,%DataPtr
         %ENDIF
         SUB EAX,[ECX+STACK.Size]
         CMP EAX,[ECX+STACK.Bottom]
        StackPeekPrev%.:
       POP ECX
     %ENDMACRO StackPeekPrev

↑ STREAM

  Object STREAM is an unformated FIFO storage for items of unlimited size. Unlike the BUFFER, data is not guaranteed to be stored continuously. Memory blocks (containers for stored data) are allocated on demand with fixed size and their every byte is used, thus the memory is exploited very efficiently.
  The STREAM structure itself is appended below the first block, all other continuation block have pointer to the next block appended below them.

  Stream-store operations advance `STREAM.WritePtr`, stream-read operations advance `STREAM.ReadPtr`. Stream should not be read by more threads concurently.

  Typical usage of stream is collecting data with StreamStore* and then retrieving all data at once.

  ┌ ┌──────────┐ ┌──────────┐ ┌──────────┐<───┐ │ │░░░░░░░░░░│ │░░░░░░░░░░│ │ │ │ │ │░░░░░░░░░░│ │░░░░░░░░░░│ │ │ │ │ │░░░░░░░░░░│ │░░░░░░░░░░│ │ │ │ │ │░░░░░░░░░░│ │░░░░░░░░░░│ │ │<─┐ │ │ │░░░░░░░░░░│ │░░░░░░░░░░│ │░░░░░░░░░░│ │ │ ┌┤ │░░░░░░░░░░│ │░░░░░░░░░░│ │░░░░░░░░░░│ │ │ ││ │░░░░░░░░░░│ │░░░░░░░░░░│ │░░░░░░░░░░│ │ │ ││ │░░░░░░░░░░│ │░░░░░░░░░░│ │░░░░░░░░░░│ │ │ ││ │░░░░░░░░░░│ │░░░░░░░░░░│ │░░░░░░░░░░│ │ │ ││ │░░░░░░░░░░│ │░░░░░░░░░░│ │░░░░░░░░░░│ │ │ ││ │░░░░░░░░░░│ │░░░░░░░░░░│ │░░░░░░░░░░│ │ │ │└ ┌>├──────────┤ ┌─>├──────────┤ ┌─>├──────────┤ │ │ │ │ │.Next │─┘ │.Next │─┘ │.Next=0 │ │ │ │ │ ├──────────┤ └──────────┘ └──────────┘ │ │ │ │ │.WritePtr │──────────────────────────────────┘ │ │ │ ├──────────┤ │ │ └─│.ReadPtr │ │ │ ├──────────┤ │ │ │.Top │────────────────────────────────────┘ │ ├──────────┤ └────│.BufSize │ ├──────────┤ │.Pool │ └──────────┘

     STREAM STRUC
    .Pool     D D ; Pool handle obtained from PoolCreate.
    .BufSize  D D ; Netto size of each block (without the linking pointer).
    .Top      D D ; End of the last block.
    .ReadPtr  D D ; Pointer to the unread data.
    .WritePtr D D ; Pointer to the free position in the last block.
    .Next     D D ; Pointer to the bottom of the next block. NULL if only 1 block is allocated.
    ENDSTRUC STREAM

↑ StreamCreate Pool, BufSize=16K
  will allocate stream object on the pool specified with the first parameter.
Input
  Pool is a pointer to POOL structure which was returned from PoolCreate.
  BufSize= is the requested netto size of each buffer.
Output
  CF=0 EAX=pointer to the stream instance.
Error
  CF=1 EAX=undefined on pool error.
Depends on
  PoolNew

    StreamCreate %MACRO Pool, BufSize=16K
          PUSHD %BufSize, %Pool
          CALL StreamCreate@RT:
    StreamCreate@RT: PROC1
          PUSHAD
           MOV EBX,[ESP+9*4]               ; %Pool.
           MOV ECX,[ESP+10*4]              ; %BufSize.
           LEA EDX,[ECX+SIZE#STREAM]
           PoolNew EBX, EDX, Align=DWORD
           MOV [ESP+7*4],EAX               ; %ReturnEAX.
           JC .90:
           MOV [EAX+STREAM.Pool],EBX
           LEA EBX,[EAX+SIZE#STREAM]
           MOV [EAX+STREAM.BufSize],ECX
           MOV [EAX+STREAM.ReadPtr],EBX
           MOV [EAX+STREAM.WritePtr],EBX
           MOVD [EBX-4],0
           ADD EBX,ECX
           MOV [EAX+STREAM.Top],EBX
     .90: POPAD
          RET 2*4
      ENDPROC1 StreamCreate@RT:
     %ENDMACRO StreamCreate

↑ StreamGetSize Stream
  returns the total amount of data written to the stream in bytes.
Input
  Stream ^STREAM obtained in StreamCreate.
Output
  EAX= total size of data in the stream.

    StreamGetSize %MACRO Stream
          PUSHD %Stream
          CALL StreamGetSize@RT:
    StreamGetSize@RT: PROC1
          PUSHAD
           SUB EAX,EAX
           MOV EBX,[ESP+9*4]               ; %Stream.
           MOV [ESP+7*4],EAX               ; Returned size.
           LEA ESI,[EBX+SIZE#STREAM]
           MOV EAX,[EBX+STREAM.WritePtr]
           MOV ECX,[EBX+STREAM.BufSize]
     .10:  CMP EAX,ESI
           JB .20:
           LEA EDI,[ESI+ECX]
           CMP EAX,EDI
           JNA .30:
     .20:  ADD [ESP+7*4],ECX               ; %ReturnEAX.
           MOV ESI,[ESI-4]
           JMP .10:
     .30:  SUB EAX,ESI
           ADD [ESP+7*4],EAX               ; %ReturnEAX.
     .90: POPAD
          RET 1*4
      ENDPROC1 StreamGetSize@RT:
     %ENDMACRO StreamGetSize

↑ StreamReadByte Stream
  Macro StreamReadByte will retrieve one byte from the Stream at position STREAM.ReadPtr and advance the position. The stream should be reset to start with the first byte.
Input
  Stream is pointer to the STREAM object obtained in StreamCreate.
Output
  CF=0, EAX= a byte read from the stream is in AL.
End of data
  CF=1, EAX=undefined at the end of stream data.
See also
  StreamReset

    StreamReadByte %MACRO Stream
          PUSHD %Stream
          CALL StreamReadByte@RT:
    StreamReadByte@RT: PROC1
          PUSHAD
            MOV EBX,[ESP+9*4]         ; %Stream.
            MOV EBP,[EBX+STREAM.BufSize]
            MOV EDX,[EBX+STREAM.Next] ; Pointer to the next block, or 0.
            LEA ESI,[EBX+SIZE#STREAM] ; Bottom of data in the first block.
            LEA ECX,[ESI+EBP]         ; Top of the first block.
    .10:    ; If the .WritePtr is in the block ESI..ECX, decrement ECX to its value.
            CMP [EBX+STREAM.WritePtr],ESI
            JB .20:
            CMP [EBX+STREAM.WritePtr],ECX
            JAE .20:
            MOV ECX,[EBX+STREAM.WritePtr]
    .20:    MOV EDI,[EBX+STREAM.ReadPtr]
            ; If the .ReadPtr is in the block ESI..ECX, read one byte from it.
            CMP EDI,ESI
            JB .50:                   ; Try the next block.
            CMP EDI,ECX               ; Check if STREAM.ReadPtr EDI belongs to this data block ESI..ECX.
            JA .50:                   ; Try the next block.
            JE .60:                   ; If .ReadPtr points to the top of block, move it to the bottom of the next block.
            XCHG ESI,EDI
            LODSB
            MOV [ESP+28],EAX          ; %ReturnEAX.
            MOV [EBX+STREAM.ReadPtr],ESI
            CLC
            JMP .90:
    .50:    TEST EDX                  ; Is the next block allocated?
            STC
            JZ .90:
            MOV ESI,EDX               ; Bottom of data in the next block.
            JMP .70:
    .60:    TEST EDX                  ; Is the next block allocated?
            STC
            JZ .90:
            MOV ESI,EDX               ; Bottom of data in the next block.
            MOV [EBX+STREAM.ReadPtr],ESI
    .70:    MOV EDX,[EDX-4]           ; Pointer to the next block, or 0.
            LEA ECX,[ESI+EBP]
            JMP .10:
    .90:  POPAD
          RET 1*4
      ENDPROC1 StreamReadByte@RT:
     %ENDMACRO StreamReadByte

↑ StreamReadLn Stream, LineBuffer
  Macro StreamReadLn will retrieve one physical line (terminated with LineFeed `0x10` from the Stream at position STREAM.ReadPtr and copy it to the LineBuffer. The stream should be reset to start with the first line.
  The last line stored in Stream doesn't have to be necessarily terminated with LF.
Input
  Stream is pointer to the STREAM object obtained in StreamCreate.
  LineBuffer is pointer to the output BUFFER where the line will be copied. It is allocated by the caller.
Output
  CF=0, one physical line from the stream is copied to LineBuffer, Stream.ReadPtr is advanced.
End of data
  CF=1, no more lines.
Example
  MOV EBX,aStream BufferCreate aPool StreamReset EBX Loop: StreamReadLn EBX,EAX JC End: BufferRetrieve EAX CALL ProcessTheLine: JMP Loop: End:
See also
  StreamReset

    StreamReadLn %MACRO Stream, LineBuffer
          PUSHD %LineBuffer, %Stream
          CALL StreamReadLn@RT:
    StreamReadLn@RT: PROC1
          PUSHAD
            MOV EBX,[ESP+9*4]              ; %Stream.
            MOV EBP,[EBX+STREAM.BufSize]
            MOV EDX,[EBX+STREAM.Next]      ; Pointer to the next block, or 0.
            LEA ESI,[EBX+SIZE#STREAM]      ; Bottom of data in the first block.
            LEA ECX,[ESI+EBP]              ; Top of the first block.
            MOV EAX,10                     ; AL=LF, AH=0. Nonzero AH signalizes incomplete line (not LF terminated).
    .10:    ; Find the physical line starting at .ReadPtr in the block ESI..ECX.
            TEST AH,AH
            JZ .30:
            MOV [EBX+STREAM.ReadPtr],ESI   ; Bottom of the next block.
            XOR AH,AH
    .30:    ; If the .WritePtr is in the block ESI..ECX, decrement ECX to its value.
            CMP [EBX+STREAM.WritePtr],ESI
            JB .40:
            CMP [EBX+STREAM.WritePtr],ECX
            JAE .40:
            MOV ECX,[EBX+STREAM.WritePtr]
    .40:    ; If the .ReadPtr is in the block ESI..ECX, search for the LF.
            MOV EDI,[EBX+STREAM.ReadPtr]
            CMP EDI,ESI
            JB .50:                        ; Try the next block.
            CMP EDI,ECX                    ; Check if STREAM.ReadPtr EDI belongs to this data block ESI..ECX.
            JA .50:                        ; Try the next block.
            JE .60:                        ; .ReadPtr points to the top of block. Move it to the bottom of the next block.
            SUB ECX,EDI                    ; Max size of searched data in block EDI..ECX. At least 1.
            MOV ESI,EDI                    ; Remember the start of line.
            REPNE SCASB                    ; Search for the LF.
            MOV [EBX+STREAM.ReadPtr],EDI
            JE .80:                        ; If terminator LF found, we're almost done.
            OR AH,1                        ; The line ESI..EDI is incomplete, perhaps split between blocks.
            SUB EDI,ESI
            MOV ECX,[ESP+40]               ; %LineBuffer.
            BufferStore ECX,ESI,EDI        ; Store the first part of incomplete line.
            ; The line continues in the next block.
    .50:    TEST AH
            JZ .55:
            TEST EDX                       ; Is the next block allocated?
            JZ .90:                        ; If not, the last line is incomplete, return it.
    .55:    TEST EDX                       ; Is the next block allocated?
            STC
            JZ .90:                        ; If not and line not incomplete, signalize end of data.
            MOV ESI,EDX                    ; Bottom of data in the next block.
            JMP .70:
    .60:    TEST EDX                       ; Is the next block allocated?
            STC
            JZ .90:
            MOV ESI,EDX                    ; Bottom of data in the next block.
            MOV [EBX+STREAM.ReadPtr],ESI
    .70:    MOV EDX,[EDX-4]                ; Pointer to the next block, or 0.
            LEA ECX,[ESI+EBP]
            JMP .10:
    .80:    SUB EDI,ESI
            MOV EDX,[ESP+10*4]             ; %LineBuffer.
            BufferStore EDX,ESI,EDI        ; Copy line ESI,EDI to the buffer EDX.
            CLC
     .90: POPAD
          RET 2*4
      ENDPROC1 StreamReadLn@RT:
     %ENDMACRO StreamReadLn

↑ StreamGetLines StreamPtr
  returns the number of physical lines in the data in the stream. The stream should be reset first.
  Physical line ends with LineFeed character (0x10). The last line in the stream may have LF omitted.
Input
  StreamPtr is pointer to the STREAM obtained in StreamCreate.
Output
  EAX= total lines in the stream.
Depends on
  StreamReadLn

    StreamGetLines %MACRO StreamPtr
          SUB EAX,EAX
          DEC EAX
    StreamGetLines%.:
          INC EAX
          StreamReadLn %StreamPtr, 0 ; The read lines won't be stored due to NULL buffer.
          JNC StreamGetLines%.:
     %ENDMACRO StreamGetLines

↑ StreamReset Stream
  StreamReset moves the STREAM.ReadPtr to the beginning of data, so the stream contents can be retrieved once again.
Input
  Stream is pointer to the STREAM obtained in StreamCreate.
Output
  none.

    StreamReset %MACRO Stream
          PUSH EAX,EBX
            MOV EBX,%Stream
            LEA EAX,[EBX+SIZE#STREAM]
            MOV [EBX+STREAM.ReadPtr],EAX
          POP EBX,EAX
     %ENDMACRO StreamReset

↑ StreamClear Stream
  StreamClear discards the contents of a stream. In fact it just moves the .WritePtr and .ReadPtr to the beginning of data. Allocated buffers are not discarded, so their memory can be reused.
Input
  Stream is pointer to the STREAM obtained in StreamCreate.
Output
  -
Error
  -

    StreamClear %MACRO Stream
          PUSH EAX,EBX
            MOV EBX,%Stream
            LEA EAX,[EBX+SIZE#STREAM]
            MOV [EBX+STREAM.WritePtr],EAX
            MOV [EBX+STREAM.ReadPtr],EAX
            ADD EAX,[EBX+STREAM.BufSize]
            MOV [EBX+STREAM.Top],EAX
          POP EBX,EAX
     %ENDMACRO StreamClear

↑ StreamDump Stream, DumpProc
  Macro StreamDump will copy the contens of the stream to callback procedure DumpProc.
  DumpProc is provided with pointer and size of one memory block, it is expected to write the block to a file or to another stream or buffer.
Input
  Stream points to STREAM obtained in StreamCreate.
  DumpProc is the entry point of callback procedure which will write the memory block ESI,ECX.
  DumpProc input
    ESI= pointer to the data to be written.
    ECX= size of the data.
    EBP= EBP on StreamDump entry.
    EBX= pointer to the Stream. Other registers are undefined.
  DumpProc output
    CF=0 to continue with the next block. All GPR may be destroyed in DumpProc.
  DumpProc error
    CF=1 if error occured during writting the block. This will cancel further dumping.
Output
  -
See also
  StreamRetrieve
Example
  ; Flush the stream contents to aFile: FileCreate aFile StreamDump aStream, aDumpProc FileClose aFile aDumpProc PROC1 FileWrite aFile,ESI,ECX RET ENDP1 aDumpProc

    StreamDump %MACRO Stream, DumpProc
          PUSHD %DumpProc, %Stream
          CALL StreamDump@RT:
    StreamDump@RT: PROC1
          PUSHAD
           MOV EBX,[ESP+9*4]               ; %Stream.
           TEST EBX
           JZ .90:
           LEA ESI,[EBX+SIZE#STREAM]
           MOV EAX,[EBX+STREAM.WritePtr]
           TEST EAX
           JZ .90:
    .20:   MOV ECX,[EBX+STREAM.BufSize]
           LEA EDX,[ESI+ECX]
           CMP EAX,EDX
           JNB .40:
           CMP EAX,ESI
           JNB .70:
    .40:   PUSH EAX,EBX,ESI
            CALL [ESP+40+12]               ; %DumpProc, not the last block.
           POP ESI,EBX,EAX
           JC .90:
           MOV ESI,[ESI-4]
           TEST ESI
           JNZ .20:
           JMP .90:
    .70:   MOV ECX,EAX
           SUB ECX,ESI
           JZ .90:
           CALL [ESP+10*4]                 ; DumpProc, the last block.
    .90:  POPAD
          RET 8
      ENDPROC1 StreamDump@RT:
     %ENDMACRO StreamDump

↑ StreamRetrieve Stream
  Retrieving the stream returns pointer and size of one internal buffer. The .ReadPtr is then advanced to the next buffer.
  Repeated invocation of StreamRetrieve returns the next buffer. Size of the last buffer may be smaller than .BufSize.
Input
  Stream is pointer to the STREAM obtained in StreamCreate.
Output
  ZF=0, ESI= pointer to data in stream buffer.
  ECX= size of the data. In most cases ECX=STREAM.BufSize but the last data buffer may be shorter.
On end of data
  ZF=1, ECX=0 ESI=undefined
See also
  StreamDump

    StreamRetrieve %MACRO Stream
          PUSHD %Stream
          CALL StreamRetrieve@RT:
    StreamRetrieve@RT: PROC1
          PUSHAD
           MOV EBX,[ESP+9*4] ; %Stream.
           MOV ECX,[EBX+STREAM.BufSize]
           MOV EAX,[EBX+STREAM.ReadPtr]
           MOV EDX,[EBX+STREAM.WritePtr]
           LEA ESI,[EBX+SIZE#STREAM]
           MOV [ESP+1*4],EAX ; %ReturnESI.
     .10:  LEA EDI,[ESI+ECX]
           CMP EDX,ESI
           JB .20:
           CMP EDX,EDI
           JNA .70:
     .20:  CMP EAX,ESI
           JB .30:
           CMP EAX,EDI
           JNA .50:
     .30:  MOV ESI,[ESI-4]
           TEST ESI
           JNZ .10:
           JMP .80:
     .50:  CMP EAX,EDI
           JNE .60:
           MOV ESI,[ESI-4]
           TEST ESI
           JZ .80:
           MOV [EBX+STREAM.ReadPtr],ESI
           MOV [ESP+1*4],ESI               ; %ReturnESI.
           JMP .10:
     .60:  MOV ECX,EDI
           SUB ECX,EAX
           JMP .90:
     .70:  CMP EAX,ESI
           JB .80:
           CMP EAX,EDI
           JA .80:
           MOV ECX,EDX
           SUB ECX,EAX
           JA .90:
     .80:  SUB ECX,ECX
     .90:  MOV [ESP+6*4],ECX               ; %ReturnECX.
           ADD [EBX+STREAM.ReadPtr],ECX
           TEST ECX
          POPAD
          RET 1*4
      ENDPROC1 StreamRetrieve@RT:
     %ENDMACRO StreamRetrieve

↑ StreamStoreRT
  is a macro used internally by StreamStore*. It returns CF=0,EAX=written size or CF=1,EAX=0.
  This macro also defines the subprocedure StreamStore$size@RT used in streaming ASCIIZ strings.

    StreamStoreRT %MACRO ; Common runtime proc for macros StreamStore, StreamStoreLn etc.
      StreamStore@RT: PROC1
         PUSHAD
          MOV EBX,[ESP+40] ; Stream.
          SUB EAX,EAX
          LEA ESI,[ESP+44] ; 1st data pair.
          MOV [ESP+28],EAX ; ReturnEAX.
     .10: LODSD ; DataPtr.
          MOV EBP,EAX
          TEST EAX
          JZ .90:
          LODSD ; DataSize.
          MOV EDX,EAX
          ADD [ESP+28],EAX ; Total written size.
          PUSH ESI
           ; EBP=DataPtr EDX=DataSize EBX=Stream
           MOV EDI,[EBX+STREAM.WritePtr]
           ; Start with the first block.
           LEA ESI,[EBX+SIZE#STREAM]
    .15:   CMP EDI,ESI                ; Find the block pointed to with ESI, where EDI=WritePtr is in.
           JB  .20:
           MOV ECX,[EBX+STREAM.BufSize]
           ADD ECX,ESI
           CMP EDI,ECX
           JNA .30:
    .20:   ; WritePtr is not in block ESI. Try the next one.
           MOV ESI,[ESI-4]
           TEST ESI
           JNZ .15:
           STC                        ; Inconsistent data. This should never happen.
           JMP .80:
    .30:   ; EDI=WritePtr inside block ESI..ECX.
           SUB ECX,EDI                ; Remaining free room on the last buffer.
           XCHG ESI,EBP
    .35: ; ESI=DataPtr EDI=WritePtr EBP=block EDX=requested data size ECX=remaining room.
           CMP EDX,ECX
           JA .40:
           MOV ECX,EDX
     .40:  SUB EDX,ECX
           REP MOVSB
           MOV [EBX+STREAM.WritePtr],EDI
           TEST EDX                   ; Data size not written yet.
           JZ .80:
           ; EDX=remaining nonstored datasize ESI=nonstored data EBX=Stream EBP=block.
           MOV EDI,[EBP-4]
           TEST EDI
           JZ .50:
           ; Next block EDI is already allocated, reuse it.
           MOV EBP,EDI
    .45:   MOV [EBX+STREAM.WritePtr],EDI
           MOV ECX,[EBX+STREAM.BufSize]
           JMP .35:
    .50: ; Block EBP was the last in chain. A new block needs to be allocated.
           MOV ECX,[EBX+STREAM.BufSize]
           ADD ECX,4
           PoolNew [EBX+STREAM.Pool],ECX
           JC .80:
           MOVD [EAX],0
           ADD EAX,4
           MOV [EBP-4],EAX            ; Link from the old block.
           MOV EBP,EAX
           MOV EDI,EAX
           JMP .45:
     .80: POP ESI
          JNC .10:
     .90:POPAD
         RET
    StreamStore$size@RT:
         PUSH ECX,EDI
          SUB ECX,ECX
          SUB EAX,EAX
          DEC ECX
          MOV EDI,[ESP+12]            ; DataPtr
          REPNE SCASB
          SUB EAX,ECX
          SUB EAX,2
          MOV [ESP+16],EAX            ; DataSize
         POP EDI,ECX
         RET
      ENDPROC1 StreamStore@RT:
     %ENDMACRO StreamStoreRT

↑ StreamStore Stream, DataPtr1, DataSize1, DataPtr2, DataSize2,,,
  This macro writes data to the stream. The data is specified with pointer and size. More than one pointer,size pair can be specified in one invocation.
Input
  Stream ^STREAM obtained in StreamCreate.
  DataPtr* Pointer to data which will be copied to stream buffer.
  DataSize* Size of data in bytes.
Output
  CF=0, EAX= total written size.
Error
  CF=1, EAX=0

    StreamStore %MACRO Stream, DataPtr, DataSize
        %IF %# & 1 = 0
           ID=5971, 'Macro "StreamStore" expects odd number of arguments.'
          %EXITMACRO StreamStore
        %ENDIF
        PUSHD 0                            ; Mark the end of arguments.
        ArgNr %FOR %#..2,STEP=-2
          PUSHD %*{%ArgNr}, %*{%ArgNr-1}
        %ENDFOR ArgNr
        PUSHD %Stream
        PUSH ESP
         ADDD [ESP],4*(%#+1)
         CALL StreamStore@RT:
        POP ESP
        StreamStoreRT                      ; Declare the runtime procedure.
     %ENDMACRO StreamStore

↑ StreamStore$ Stream, DataPtr1, DataPtr2, DataPtr3,,,
  This macro writes data to the stream. The data is specified with pointer to a zero-terminated byte string. The terminating zero is not stored to the stream. More than one pointer can be specified in the invocation.
Input
  Stream Pointer to STREAM obtained in StreamCreate.
  DataPtr* Pointer(s) to ASCIIZ data which will be copied to stream buffer.
Output
  CF=0, EAX= total written size.
Error
  CF=1, EAX=0

    StreamStore$ %MACRO Stream, DataPtr
        PUSHD 0 ; Mark the end of arguments.
        ArgNr %FOR %#..2,STEP=-1
          PUSHD EAX, %*{%ArgNr} ;
          CALL StreamStore$size@RT:
        %ENDFOR ArgNr
        PUSHD %Stream
        PUSH ESP
         ADDD [ESP],8*(%#)
         CALL StreamStore@RT
        POP ESP
        StreamStoreRT                      ; Declare the runtime procedure.
     %ENDMACRO StreamStore$

↑ StreamStoreLn Stream, DataPtr1, DataSize1, DataPtr2, DataSize2,,,
  This macro writes data to the stream. More than one pointer/size pair can be specified in one invocation. After storing all data StreamStoreLn stores end-of-line characters CR+LF.
Input
  Stream ^STREAM obtained in StreamCreate.
  DataPtr* Pointer to data which will be copied to stream buffer.
  DataSize* Size of data in bytes.
Output
  CF=0, EAX= total written size including CR+LF.
Error
  CF=1 EAX=0

    StreamStoreLn %MACRO Stream, DataPtr, DataSize
        %IF %# & 1 = 0
          %ERROR ID=5972, 'Macro "StreamStoreLn" expects odd number of arguments.'
          %EXITMACRO StreamStoreLn
        %ENDIF
        PUSHD 0x00000A0D                   ; EOL data.
        PUSHD 0                            ; Mark the end of arguments.
        PUSHD 2                            ; Size of EOL.
        PUSHD ESP                          ; Pointer to EOL.
        ADDD [ESP],8                       ; Adjust the pointer.
        ArgNr %FOR %#..2,STEP=-2
          PUSHD %*{%ArgNr}, %*{%ArgNr-1}
        %ENDFOR ArgNr
        PUSHD %Stream
        PUSH ESP
         ADDD [ESP],4*(%#+4)
         CALL StreamStore@RT:
        POP ESP
        StreamStoreRT                      ; Declare runtime procedure.
     %ENDMACRO StreamStoreLn

↑ StreamStoreByte Stream, Data
  This macro writes 1 bytes of data to the stream.
Input
  Stream ^STREAM obtained in StreamCreate.
  Data Byte memory variable or 8bit register.
Output
  CF=0, EAX=1
Error
  CF=1, EAX=0

    StreamStoreByte %MACRO Stream, Data
        %IF "%Data" !== "AL"
          MOV AL,%Data
        %ENDIF
        PUSHD EAX
        PUSHD 0                            ; Mark the end of arguments.
        PUSHD 1                            ; Size of %Data.
        PUSHD ESP                          ; Pointer to %Data.
        ADDD [ESP],8                       ; Adjust the pointer.
        PUSHD %Stream
        PUSH ESP
         ADDD [ESP],20
         CALL StreamStore@RT:
        POP ESP
        StreamStoreRT                      ; Declare runtime procedure.
     %ENDMACRO StreamStoreByte

↑ StreamStoreWord Stream, Data
  This macro writes 2 bytes of data to the stream.
Input
  Stream ^STREAM obtained in StreamCreate.
  Data Word memory variable or 16bit register.
Output
  CF=0, EAX=2
Error
  CF=1, EAX=0

    StreamStoreWord %MACRO Stream, Data
        %IF "%Data" !== "AX"
          MOV AX,%Data
        %ENDIF
        PUSHD EAX
        PUSHD 0                            ; Mark the end of arguments.
        PUSHD 2                            ; Size of %Data.
        PUSHD ESP                          ; Pointer to %Data.
        ADDD [ESP],8                       ; Adjust the pointer.
        PUSHD %Stream
        PUSH ESP
         ADDD [ESP],20
         CALL StreamStore@RT:
        POP ESP
        StreamStoreRT                      ; Declare runtime procedure.
     %ENDMACRO StreamStoreWord

↑ StreamStoreDword Stream, Data
  This macro writes 4 bytes of data to the stream.
Input
  Stream ^STREAM obtained in StreamCreate.
  Data Doubleword memory variable or 32bit register.
Output
  CF=0, EAX=4
Error
  CF=1, EAX=0
Example
  `StreamStoreDword aStream,EBX` store the contents of EBX.
  `StreamStore aStream,EBX,4` store 4 bytes of the memory addressed with EBX.

    StreamStoreDword %MACRO Stream, Data
        PUSHD %Data
        PUSHD 0                            ; Mark the end of arguments.
        PUSHD 4                            ; Size of %Data.
        PUSHD ESP                          ; Pointer to %Data.
        ADDD [ESP],8                       ; Adjust the pointer.
        PUSHD %Stream
        PUSH ESP
         ADDD [ESP],20
         CALL StreamStore@RT:
        POP ESP
        StreamStoreRT                      ; Declare runtime procedure.
     %ENDMACRO StreamStoreDword

     ENDHEAD memory32                      ; End of library interface.

▲Back to the top▲