I was reasonably happy with the dynamic array code but in comparison to other systems that use similar methods the memory usage was too high and the original array allocation was simply too slow. I have been playing with the SafeArray system for OLE and it appears to have a much more efficient set of methods for allocating, reallocating and destroying a dynamic that the published interfaces for other methods have which makes the array creation time much faster while using a lot less memory.
This is the bare proof of concept test piece, I have yet to work out if the array lock method is required with the member allocation method, in the examples I have seen it is not used but whether it needs to be used is something I have not worked out yet.
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
include \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
comment * -----------------------------------------------------
Build this template with
"CONSOLE ASSEMBLE AND LINK"
----------------------------------------------------- *
SAFEARRAYBOUND STRUCT
cElements dd ?
lLbound dd ?
SAFEARRAYBOUND ENDS
; -----------------------------------------------------------------
; this is not a system defined structure, the trailing "1" is to
; designate that it is a one dimension array, the system allows
; multidimensional arrays which requre an additional SAFEARRAYBOUND
; structure for each dimension to store the bounds information in.
; -----------------------------------------------------------------
SAFEARRAY1 STRUCT
cDims dw ? ; Count of dimensions in this array.
fFeatures dw ? ; Flags used by the SafeArray
cbElements dd ? ; Size of an element of the array
cLocks dd ? ; Number of times the array has been locked
pvData dd ? ; Pointer to the data
rgsabound SAFEARRAYBOUND <> ; bounds structure
SAFEARRAY1 ENDS
.code
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
call main
inkey
exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
main proc
LOCAL parr :DWORD
LOCAL pdat :DWORD
LOCAL lcnt :DWORD
LOCAL sarb :SAFEARRAYBOUND
LOCAL sarr :SAFEARRAY1
push ebx
push esi
push edi
mov sarb.cElements, 10000000 ; set the initial array count
mov sarb.lLbound, 0
invoke SafeArrayCreate,VT_BSTR,1,ADDR sarb ; create the empty array
mov parr, eax
mov eax, parr
mov ecx, (SAFEARRAY1 PTR [eax]).rgsabound.cElements ; display the element count from the descriptor struct
print str$(ecx)," element count",13,10
mov eax, parr
mov ecx, (SAFEARRAY1 PTR [eax]).pvData ; get the pointer to the array data
mov pdat, ecx
print str$(pdat)," pointer to data",13,10
mov esi, pdat ; place the array pointer in ESI
mov ebx, 1024 ; place the array index in EBX
; --------------------------------------------------
; make a test string allocation at array member 1024
; --------------------------------------------------
.data
arr1024 db "This is array member 1024",0
.code
fn SysAllocStringByteLen,[esi+ebx*4],LENGTHOF arr1024 ; allocate a string of the required length
mov [esi+ebx*4], eax ; write its handle to the array
fn szCopy,OFFSET arr1024,[esi+ebx*4] ; write the data to the array member
; --------------------------------------------------
mov sarb.cElements, 20000000 ; reset the array count
invoke SafeArrayRedim,parr,ADDR sarb ; resize the array
mov eax, parr
mov ecx, (SAFEARRAY1 PTR [eax]).rgsabound.cElements ; display the new element count from the descriptor struct
print str$(ecx)," element count",13,10
mov sarb.cElements, 10000000 ; reset the array count
invoke SafeArrayRedim,parr,ADDR sarb ; resize the array
mov eax, parr
mov ecx, (SAFEARRAY1 PTR [eax]).rgsabound.cElements ; display the new element count from the descriptor struct
print str$(ecx)," element count",13,10
; ---------------------------
; get the array address again
; ---------------------------
mov eax, parr
mov ecx, (SAFEARRAY1 PTR [eax]).pvData ; get the pointer to the array data
mov pdat, ecx
; -----------------------------------------------
; get the member address and display its contents
; -----------------------------------------------
mov esi, pdat ; place the array pointer in ESI
mov ebx, 1024 ; place the array index in EBX
print [esi+ebx*4],13,10 ; display the content of the array member
invoke SafeArrayDestroy,parr ; delete the array
pop edi
pop esi
pop ebx
ret
main endp
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start
Can you explain this part?
fn SysAllocStringByteLen,[esi+ebx*4],LENGTHOF arr1024 ; allocate a string of the required length
mov [esi+ebx*4], eax ; write its handle to the array
fn szCopy,OFFSET arr1024,[esi+ebx*4] ; write the data to the array member
In my understanding, you can pass either 0 as first param to allocate a non-initialised string of len 1024, or directly pass the source:
fn SysAllocStringByteLen,OFFSET arr1024, LENGTHOF arr1024 ; allocate a string of the required length
mov [esi+ebx*4], eax ; write its handle to the array
I am sure you have a good reason to do so, but I simply don't understand it - excuse my ignorance :red
jj,
What I have to test is if the array as allocated BEFORE any string data is allocated to it with SysAllocStringByteLen() is whether it returns a null string by using the address of an empty member. If so it means the empty strings don't have to be allocated with SysAllocStringByteLen() when the array is created which makes the original array creation much faster. My impression is so far that the safe array creation methods interface a lot lower down in the memory alocation scheme and both create and destroy the array much faster than looping through SysAllocStringByteLen() and SysFreeString().
RE your question, the idea is so it reallocates the string freeing the old string rather than just creating a new string.
Sad to say this approach is more problematic than its worth. I have a new test piece under development allocated with GlobalAlloc() that has a pointer to a null string written at each member so it is compatible with the behaviour of an OLE string. This method is much faster than allocating an empty OLE string and uses a lot less memory.
Hutch,
Among many other roles, you are the SysAlloc guru over here:
invoke SysStringByteLen, esi
.if eax==0 || eax>=80000000h || eax>32768
mov eax, len(esi)
.endif
This is my clumsy workaround for a macro that may receive as args a BSTR or an ordinary .data item. MSDN (http://msdn.microsoft.com/en-us/library/ms221097(VS.85).aspx)does not foresee a test if esi is a valid BSTR, so I try to check for implausible string lens... it works but I don't trust it. Any ideas?
jj,
I have the next version on the way, it has solved the problem of slow array creation and seems to better manage memory as well. I have the basic alloc / dealloc stuff done and some of the existing procedures work with it but I have to rewrite the rest. Its testing so far a lot faster and does not appear to have any other associated problems so its going OK at the moment.
The main change is not allocating empty OLE strings as this wasted a lot of memory and slowed things down. It uses a null string in the .data section with a zero 4 bytes below it for the length which is much faster than allocating an empty string just so the spec of returning a null string was covered. I changed the SysAllocStringByteLen() usage as you had suggested to load the string as the first arg. It internally uses movsd/movsb so its probably fast enough.
When I get enough of it done I will post it so you can have a good look at it. Perhaps you will be able to see ways of making it faster again.
An unrelated question, I have seen in your code examples the use of a leading "gfa" with some function names. A long time ago I used a dialect of basic from GFA Systemtecnik that was an excellent 16 bit version of basic. Did you work for them back that long ago ?
Quote from: hutch-- on May 05, 2008, 11:24:10 PM
An unrelated question, I have seen in your code examples the use of a leading "gfa" with some function names. A long time ago I used a dialect of basic from GFA Systemtecnik that was an excellent 16 bit version of basic. Did you work for them back that long ago ?
No, I am neither Sjouke H. nor Frank O. :8)
But as a matter of fact, I seem to be the last fossil using that dialect for a serious project; and given that the days of 16-bit Windows are counted, I have started working on a cross-compiler. That's why you see those strange prefixes :green