The MASM Forum Archive 2004 to 2012

General Forums => The Laboratory => Topic started by: hutch-- on April 03, 2008, 10:12:58 AM

Title: Dynamic string array
Post by: hutch-- on April 03, 2008, 10:12:58 AM
I have been playing with this one for a while, here is the first attempt. This version requires the array member count to be used as an argument for each macro/function but I think it would be easy enough to write the count in the first array member and make the array a 1 based rather than zero based string array.


; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

comment * -----------------------------------------------------
                        Build this  template with
                       "CONSOLE ASSEMBLE AND LINK"
        ----------------------------------------------------- *

      arralloc$ MACRO member_count

        LOCAL lbl0

        push ebx
        push esi
        push edi

        mov eax, member_count                   ;; member count in EAX
        add eax, 1                              ;; increment member count
        lea eax, [0+eax*4]                      ;; mul by 4 for memory size
        mov esi, alloc(eax)                     ;; allocate GlobalAlloc() memory for pointer array

        xor ebx, ebx                            ;; use EBX as index
      lbl0:
        invoke SysAllocStringByteLen,0,4        ;; allocate 4 byte string
        mov DWORD PTR [eax], 0                  ;; set it to a zero length string
        mov [esi+ebx*4], eax                    ;; write its address to the pointer array
        add ebx, 1                              ;; increment index
        cmp ebx, member_count                   ;; check if it matches the member count
        jle lbl0                                ;; loop back if not

        mov eax, esi                            ;; return pointer array address in EAX

        pop edi
        pop esi
        pop ebx

        EXITM <eax>

      ENDM

      arrwrite$ MACRO arr,indx,txt
        invoke write_array_member,arr,indx,reparg(txt)
      ENDM

      arrfree$ MACRO arr,cnt
        invoke delete_array,arr,cnt
      ENDM

      write_array_member    PROTO :DWORD,:DWORD,:DWORD
      delete_array          PROTO :DWORD,:DWORD

    .code

start:
   
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

    call main
    inkey
    exit

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

main proc

    LOCAL pArr  :DWORD
    LOCAL acnt  :DWORD

    push ebx
    push esi
    push edi

; ÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷
;   write and display array members
; ÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷

    mov acnt, 100
    mov pArr,arralloc$(acnt)                    ; create the empty string array
  ; ---------------------------
  ; write "acnt" items to array
  ; ---------------------------
    xor esi, esi
  @@:
    arrwrite$ pArr,esi,str$(esi)                ; write each string to an array member
    add esi, 1
    cmp esi, acnt
    jle @B

  ; -------------------------
  ; display the array members
  ; -------------------------
    mov esi, pArr                               ; array address in ESI
    xor ebx, ebx
  @@:
    mov edi, [esi+ebx*4]                        ; load string address into register
    .if BYTE PTR [edi] != 0                     ; if 1st byte not 0
      print edi,13,10                           ; display the strings
    .else                                       ; else
      print "unused",13,10                      ; display that string is empty
    .endif
    add ebx, 1
    cmp ebx, acnt
    jle @B

  ; -------------------------

    arrfree$ pArr,acnt                          ; delete the members and pointer array

; ÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷
;   benchmark array allocation, write and delete
; ÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷

    mov acnt, 1000000                          ; 1 million members (0 based array)

  ; ==================================

    invoke GetTickCount
    push eax

    mov pArr,arralloc$(acnt)                    ; create the empty string array

    invoke GetTickCount
    pop ecx
    sub eax, ecx

    print str$(eax)," arralloc$ ms",13,10

  ; ==================================

    invoke GetTickCount
    push eax

  ; ---------------------------
  ; write "acnt" items to array
  ; ---------------------------
    xor esi, esi
  @@:
    arrwrite$ pArr,esi,str$(esi)                ; write each string to an array member
    add esi, 1
    cmp esi, acnt
    jle @B

    invoke GetTickCount
    pop ecx
    sub eax, ecx

    print str$(eax)," arrwrite$ ms",13,10

  ; ==================================

    invoke GetTickCount
    push eax

    arrfree$ pArr,acnt                          ; delete the members and pointer array

    invoke GetTickCount
    pop ecx
    sub eax, ecx

   print str$(eax)," arrfree$  ms",13,10

; ÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷

    pop edi
    pop esi
    pop ebx

    ret

main endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

delete_array proc arr:DWORD,cnt:DWORD

    push ebx
    push esi
    push edi

    mov esi, arr                            ; array address in ESI
    mov edi, cnt                            ; member count in EDI
    xor ebx, ebx                            ; use EBX as index
  @@:
    cmp DWORD PTR [esi+ebx*4], 0            ; if not empty pointer
    je nxt
    free$ [esi+ebx*4]                       ; deallocate each array member
  nxt:
    add ebx, 1
    cmp ebx, edi
    jle @B

    free arr                                ; deallocate the pointer array

    pop edi
    pop esi
    pop ebx

    ret

delete_array endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

write_array_member proc arr:DWORD,indx:DWORD,txt:DWORD

    push ebx
    push esi
    push edi

    mov esi, arr                            ; load array address
    mov edi, len(txt)                       ; get the text length
    add edi, 4                              ; add terminator and
    and edi, -4                             ; extend to next 4 byte boundary
    mov ebx, indx                           ; place member index in EBX
    cmp DWORD PTR [esi+ebx*4], 0            ; test if member is allocated or blank
    je lbl
    free$ [esi+ebx*4]                       ; if allocated free it first
  lbl:
    mov [esi+ebx*4], alloc$(edi)            ; allocate string space
    invoke szCopy,txt,[esi+ebx*4]           ; copy text to allocated memory

    pop edi
    pop esi
    pop ebx

    ret

write_array_member endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

end start

[attachment deleted by admin]
Title: Re: Dynamic string array
Post by: jj2007 on April 03, 2008, 06:25:55 PM
Impressive! arrwrite$ shows some non-linearity for acnt, see below.


100000
50 arralloc$ ms
10 arrwrite$ ms
50 arrfree$  ms


1000000
631 arralloc$ ms
190 arrwrite$ ms
511 arrfree$  ms


10000000
8833 arralloc$ ms
21031 arrwrite$ ms
5438 arrfree$  ms
Title: Re: Dynamic string array
Post by: hutch-- on April 04, 2008, 12:18:57 AM
Thanks for the test jj, I saw much the same results. I used the OLE string capacity because the system can move them around in memory and I think this is what is happening when the count gets very high. GlobalAlloc() is a lot faster with the GMEM_FIXED flag but it would lead to severe memory fragmentation so I used the slower OLE string to solve the problem.

The next trick is to store the array count in the first array member and make it a 1 rather than zero based method and it also allows for economical bounds checking below 1 or above array count.
Title: Re: Dynamic string array
Post by: Rainstorm on April 18, 2008, 03:04:02 PM
core2duo 12.66Ghz 333mhz/4M winxp pro sp2
203 arralloc$ ms
125 arrwrite$ ms
234 arrfree$  ms


Title: Re: Dynamic string array
Post by: jj2007 on April 27, 2008, 01:38:37 PM
MSDN (http://msdn2.microsoft.com/en-us/library/ms221105.aspx): In 32-bit OLE, BSTRs use Unicode like all other strings in 32-bit OLE.

With the exception of...?

SysAllocStringByteLen (http://msdn2.microsoft.com/en-us/library/ms221637.aspx):

Takes an ANSI string as input, and returns a BSTR that contains an ANSI string. Does not perform any ANSI-to-Unicode translation.
Visual C++

BSTR SysAllocStringByteLen( 
  LPCSTR  psz,   
  unsigned int  len 
);

psz
    A zero-terminated string to copy, or NULL to keep the string uninitialized.

len
    Number of bytes to copy from psz. A null character is placed afterwards, allocating a total of lenplus the size of OLECHAR bytes.
    Allocates a new string of lenbytes, copies lenbytes from the passed string into it, and then appends a null character.


Does SysReAllocString (http://msdn2.microsoft.com/en-us/library/ms220986.aspx) know that this is a byte/ANSI, not Unicode, string? MSDN is a bit vague...
Title: Re: Dynamic string array
Post by: hutch-- on April 27, 2008, 02:14:55 PM
jj,

SysAllocStringByteLen() already does a realloc from the zero terminated string used as its first argument if you pass an address to it. SysReAllocString() is not needed in this context.