1. tk_mem.e bug
Hi all,
I've been trying to track down the problem with EuCOM and the memory errors.
I've tried lot's of stuff, including using Eu's allocate/free routines,
although those would cause a crash sooner than the Win32 routines used in
tk_mem.e. When I commented out the call to HeapFree, everything worked. I
further tracked down a line in release_mem that I don't understand, but once
commented out, allows everything to run.
Right before phase 3, release_mem checks sets[1] and frees it if it's not
zero. There's no comment that I can see, and I can't see why that would be
there. Maybe there's a reason, but I think this is the reason that
release_all_mem would sometimes fail in the past. Looks like the heap got
corrupted.
So, for those who emailed me with bugs when you ran EuCOM, please comment
out the following code in tk_mem.e and let me know if it works:
--if sets[1] != 0 then
-- myFree(sets[1])
--end if
I suspect that NT does a lot more checking of the heap than Win9X
(surprise!).
Derek, unless you have a reason for this being there, I'd recommend the same
for the next release of Win32Lib. :)
Matt Lewis
PS Regarding EuCOM, it looks like IDispatch::Invoke is called. I've figured
out a fix (wrote some ASM to accept the procedure call and transfer the args
from the stack to memory), and I'll get it up later today.
3. Re: tk_mem.e bug
This is a multi-part message in MIME format.
------=_NextPart_000_0044_01C0CEEE.1AB3C060
charset="iso-8859-1"
Matt,
after doing some more testing of the tk_mem.e library, I must disagree with
you. The lines you suggested to comment out are in fact needed. The reason
is that when release_mem() is called with the parameter being a 'memset' id
rather than a memory address, it is assumed that the memset id was acquired
by calling new_memset(). That routine creates a memset_id by acquiring some
memory from the heap and using that memory address as the memset id. Thus
when releasing a memset, the id value must also be freed. That's what the
lines in question are doing.
However, in the version of tk_mem.e sent out with v0.55.1 of win32lib, there
is a bug that may be relevent to the errors you are having. If release_mem()
is called using a memory address that is invalid or not acquired using
acquire_mem(), tk_mem.e still decrements the number of allocated blocks,
even though the specified memory was not actually released. And when the
block counter gets back to zero, it releases the heap. Thus any subsequent
references to previously acquired memory will cause a GPF. This bug has been
fixed in the next release.
There was another subtle bug that might have caused EuCOM to fail by not
Win32lib apps. If the abort handler was not set up, the release_mem() check
for invalid memory addresses still tried to release the memory. This also is
fixed.
I've attached to latest version of tk_mem_full.e for you (or anyone else) to
test.
------
Derek Parnell
Melbourne, Australia
"To finish a job quickly, go slower."
----- Original Message -----
From: "Matthew Lewis" <matthewwalkerlewis at YAHOO.COM>
To: "EUforum" <EUforum at topica.com>
Sent: Friday, April 27, 2001 2:55 AM
Subject: tk_mem.e bug
> >
>
> Hi all,
>
> I've been trying to track down the problem with EuCOM and the memory
errors.
> I've tried lot's of stuff, including using Eu's allocate/free routines,
> although those would cause a crash sooner than the Win32 routines used in
> tk_mem.e. When I commented out the call to HeapFree, everything worked.
I
> further tracked down a line in release_mem that I don't understand, but
once
> commented out, allows everything to run.
>
> Right before phase 3, release_mem checks sets[1] and frees it if it's not
> zero. There's no comment that I can see, and I can't see why that would
be
> there. Maybe there's a reason, but I think this is the reason that
> release_all_mem would sometimes fail in the past. Looks like the heap got
> corrupted.
>
> So, for those who emailed me with bugs when you ran EuCOM, please comment
> out the following code in tk_mem.e and let me know if it works:
>
> --if sets[1] != 0 then
> -- myFree(sets[1])
> --end if
>
> I suspect that NT does a lot more checking of the heap than Win9X
> (surprise!).
>
> Derek, unless you have a reason for this being there, I'd recommend the
same
> for the next release of Win32Lib. :)
>
> Matt Lewis
>
> PS Regarding EuCOM, it looks like IDispatch::Invoke is called. I've
figured
> out a fix (wrote some ASM to accept the procedure call and transfer the
args
> from the stack to memory), and I'll get it up later today.
>
>
> >
> >
> >
------=_NextPart_000_0044_01C0CEEE.1AB3C060
Content-Type: application/octet-stream;
name="tk_mem_full.e"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="tk_mem_full.e"
--------------------NOTICE-------------------------------
-- Software ID: tk_mem.e
-- Version: 0.56
-- Copyright: (c) 2000 David Cuny, Derek Parnell
-- All rights reserved.
-- Licence: =20
-- This software is provided 'as-is', without any express or implied =
warranty.
-- In no event will the authors be held liable for any damages arising =
from
-- the use of this software.
--
-- Permission is granted to anyone to use this software for any purpose,
-- including commercial applications, and to alter it and redistribute =
it
-- freely, subject to the following restrictictions:
-- 1. The origin of this software must not be misrepresented; you must =
not
-- claim that you wrote the original software.
-- 2. If you use this software in a product, acknowedgement in the =
product's
-- documenation and binary are required.
-- 3. Altered source versions, and works substantially derived from the =
it,
-- must...
-- a) be plainly be marked as such,
-- b) not be misrepresented as the original software,
-- c) include this notice, unaltered.
--------------------End of NOTICE------------------------
-- Last Changed on 27/04/2001 at 7:41:04 by Derek Parnell --
-- The library was originally developed by David Cuny, and over time, a=20
-- number of other people have made additions and corrections. Where
-- appropriate the work that other people have contributed is marked =
thus...
-- Begin <person's name>
-- End <person's name>
--/topic Memory Management routines
--/info
--Low-Level Memory management routines
without warning
with trace
include machine.e
include dll.e
include tk_misc.e
constant
HEAP_NO_SERIALIZE =3D #00000001,
HEAP_GROWABLE =3D #00000002,
HEAP_GENERATE_EXCEPTIONS =3D #00000004,
HEAP_ZERO_MEMORY =3D #00000008,
HEAP_REALLOC_IN_PLACE_ONLY =3D #00000010,
HEAP_TAIL_CHECKING_ENABLED =3D #00000020,
HEAP_FREE_CHECKING_ENABLED =3D #00000040,
HEAP_DISABLE_COALESCE_ON_FREE =3D #00000080,
HEAP_CREATE_ALIGN_16 =3D #00010000,
HEAP_CREATE_ENABLE_TRACING =3D #00020000,
kernel32 =3D open_dll( "kernel32.dll" ),
xHeapCreate =3D define_c_func(kernel32, "HeapCreate", {C_LONG, C_LONG, =
C_LONG}, C_LONG),
xHeapDestroy =3D define_c_func(kernel32, "HeapDestroy", {C_LONG}, =
C_LONG),
xHeapAlloc =3D define_c_func(kernel32, "HeapAlloc", {C_LONG, C_LONG, =
C_LONG}, C_LONG),
xHeapFree =3D define_c_func(kernel32, "HeapFree", {C_LONG, C_LONG, =
C_LONG}, C_LONG),
xIsBadWritePtr =3D define_c_func(kernel32, "IsBadWritePtr", {C_LONG, =
C_LONG}, C_LONG)
--CRITICAL_SECTION CriticalSection;
--// Initialize the critical section.
--InitializeCriticalSection(&CriticalSection);
--// Request ownership of the critical section.
-- EnterCriticalSection(&CriticalSection);
-- Access the shared resource.
-- Release ownership of the critical section.
--LeaveCriticalSection(&CriticalSection);
-- Release resources used by the critical section object.
--DeleteCriticalSection(&CriticalSection)
-- stores abort handler's routine id
integer vAbortRtn vAbortRtn =3D -1
-- The number of allocations on the current heap
integer vAllocations vAllocations =3D 0
-- List of memset ids
sequence vOwners vOwners =3D {}
-- List of address sets. Each set has a list of addresses.
sequence vSets vSets =3D {}
-- stores the accumulated size of a structure as it is being defined.
integer vAlloted vAlloted =3D 0
-- The heap acquired from Windows.
atom vHeap vHeap =3D 0
--/topic Memory Management Routines
--/func llSetAbort( i )
--/desc Sets the routine id of an Abort routine.
--/ret The previous value set.
--
-- Used to indicate if an error routine needs to be
-- called in the event of a catastophic error.
-- The error routine is assumed to be a procedure
-- that accepts a single sequence (typically an=20
-- message string).
--
-- Example:
--
-- /code =20
-- integer RtnID, OldID
-- RtnID =3D routine_id("abortErr")
-- OldID =3D llSetAbort(RtnID)
-- /endcode
global function llSetAbort(integer i)
-- Set the abort handler id. The routine must take two parameters,
-- a sequence (msg) and a integer (mode =3D=3D> 1=3Dwarning, 2=3Dfatal)
integer lOldRtn
=20
lOldRtn =3D vAbortRtn
=20
vAbortRtn =3D i
=20
return lOldRtn
end function
procedure myFree(atom pAddress)
object VOID
if vHeap =3D 0 then
return
end if
if c_func(xIsBadWritePtr, {pAddress, 1}) then
return
end if
=20
if c_func(xHeapFree,{ vHeap, 0, pAddress}) =3D 0 then
return
end if
vAllocations -=3D 1
if vAllocations =3D 0 then
VOID =3D c_func(xHeapDestroy,{ vHeap })
vHeap =3D 0
end if
=20
end procedure
function myAllocate(integer pSize)
atom lAddr
if vHeap =3D 0 then
vHeap =3D c_func(xHeapCreate,{0, 16000, 0})
if vHeap =3D 0 then
return 0
end if
end if
lAddr =3D c_func(xHeapAlloc,{ vHeap, HEAP_ZERO_MEMORY, pSize})
vAllocations +=3D 1
return lAddr
end function
----
-- Memory Management memory management Routines
----
global constant
Byte =3D -1,
Word =3D -2,
Integer =3D Word,
Long =3D -3,
DWord =3D Long,
UInt =3D Long,
Ptr =3D Long,
Lpsz =3D -4,
Hndl =3D -5,
HndlAddr =3D -6,
Strz =3D -7
constant vSizeNames =3D {Byte, Word, Long, Lpsz, Hndl, HndlAddr, Strz}
constant vSizeLengs =3D { 1, 2, 4, 4, 4, 4, 1}
--/topic Memory Management Routines
--/func manage_mem( atom Owner, atom Address)
--/desc Records an acquired memory for garbage collection.
-- Normally this is handled automatically by /acquire_mem() but if you
-- are expected to manage some memory acquired by another means, such as
-- a Windows call or a 'C' routine, you can use this to record the =
memory for
-- subsequent release by /release_mem().
--
-- Example:
-- /code =20
-- atom mset, pt, pstr
--
-- -- Establish a new memory set.
-- mset =3D /new_memset()
-- -- calls a routine which returns a structure address.
-- pt =3D c_func( xyz, {abc})
-- -- register this memory
-- manage_mem(mset, pt)
-- . . .
-- give all the memory area in 'mset' back
-- release_mem(mset)
-- /endcode
global procedure manage_mem( atom pOwner, atom pAddr )
-- save location for garbage collection
-- This also is used to move a block from one memset to another.
integer lOwnerSub, lAddrSub
-- Take the address out of any existing memset.
lAddrSub =3D 0
for i =3D 1 to length(vSets) do
lAddrSub =3D find(pAddr, vSets[i])
if lAddrSub !=3D 0 then
vSets[i] =3D removeIndex(lAddrSub, vSets[i])
exit
end if
end for
=20
-- Create a new memset if required.
lOwnerSub =3D find(pOwner, vOwners)
if lOwnerSub =3D 0 then
vOwners &=3D pOwner
lOwnerSub =3D length(vOwners)
vSets =3D append(vSets, {})
end if
-- Store the address into the specified memset
vSets[lOwnerSub] &=3D pAddr
end procedure
-- The memory allocated is linked to the /i Owner and all the
-- owner's memory can be released by one call. /n
-- If /i structure is a string, it is copied to the memory
-- location along with a zero byte.
--
-- If /i structure is an atom, it specifies that amount of memory
-- to acquire (a minimum of 4 bytes will always be acquired) and
-- the memory is set to all zeros.
--
--/ret Address of allocated memory.
--
-- Example:
-- /code =20
-- atom mset, pt, pstr
--
-- -- Establish a new memory set.
-- mset =3D /new_memset()
-- -- get enough memory to hold a UInt datatype
-- xy =3D acquire_mem( UInt )
-- -- allocate a point structure
-- pt =3D acquire_mem( mset, SIZEOF_POINT )
-- -- copy a Euphoria string to a 'C' string area.
-- pstr =3D /acquire_mem( mset, "My String Data" )
-- . . .
-- give all the memory area in 'mset' back
-- release_mem(mset)
-- /endcode
global function acquire_mem( atom pOwner, object pData )
-- allocate space for a structure (mininum of 4 bytes)
-- and initialize to zero
=20
atom at
if sequence(pData) then
-- place string in memory
at =3D myAllocate( 1 + length(pData) )
if at !=3D 0 then
poke(at, pData)
poke(at + length(pData) , 0)
end if
else
-- Check for special datatype "names"
if pData < 0 then
pData =3D find(pData, vSizeNames)
if pData !=3D 0 then
pData =3D vSizeLengs[pData]
end if
end if
if pData < 4 then
pData =3D 4
end if
at =3D myAllocate( pData )
if at !=3D 0 then
mem_set( at, 0, pData )
end if
end if
=20
if at =3D 0 then
if vAbortRtn >=3D 0
then
call_proc(vAbortRtn, { "Unable to allocate space.", 2 } )
end if
else
manage_mem(pOwner, at)
end if
return at
=20
end function
--/topic Memory Management Routines
--/proc release_mem( atom structure )
--/desc Returns the memory allocated by /acquire_mem() back to the =
system.
-- If /i structure is a memory set id, as returned by /new_memset(), =
then
-- all the memory owned in the memory set is returned and the memory set =
id
-- is released. That is, it cannot be reused. /n
-- If /i structure is a memory address returned by /acquire_mem(), then =
just
-- that memory is released. The memory set it belonged to is still =
usable.
--
-- Example:
-- /code =20
-- atom mset, pt, pstr
--
-- -- Establish a new memory set.
-- mset =3D /new_memset()
-- -- get enough memory to hold a UInt datatype
-- xy =3D /acquire_mem( UInt )
-- -- allocate a point structure
-- pt =3D /acquire_mem( mset, SIZEOF_POINT )
-- -- copy a Euphoria string to a 'C' string area.
-- pstr =3D /acquire_mem( mset, "My String Data" )
-- . . .
-- give all the memory area in 'mset' back
-- release_mem(mset)
-- /endcode
global procedure release_mem( atom pData )
integer lOwnerSub
integer lAddrSub, lAddrList
integer ls, ss, Phase1
sequence sets
-- Check for "special" uninitialized memset value.
if pData =3D -1 then
return
end if
-- See if this is a memset owner.
lOwnerSub =3D find(pData, vOwners)
=20
if lOwnerSub =3D 0 then
-- If not a memset, see which memset it belongs to.
lAddrSub =3D 0
for i =3D 1 to length(vSets) do
lAddrSub =3D find(pData, vSets[i])
if lAddrSub !=3D 0 then
lAddrList =3D i
exit
end if
end for
if lAddrSub =3D 0 then
-- Not in any memset!
if vAbortRtn >=3D 0 then
call_proc(vAbortRtn, {"Trying to release unacquired =
memory", 2})
end if
=20
else
-- Remove it from the memset
vSets[lAddrList] =3D removeIndex(lAddrSub,
vSets[lAddrList])
-- Give the memory back to the system
myFree(pData)
end if
return
end if
--
sets =3D {pData}
ss =3D 1
-- Phase 1: Identified the complete hierarchy of owned memory sets.
Phase1 =3D 1
while Phase1 do
ls =3D length(sets)
for i =3D ss to ls do
lOwnerSub =3D find(sets[i], vOwners)
for j =3D 1 to length(vSets[lOwnerSub]) do
lAddrSub =3D find(vSets[lOwnerSub][j],
vOwners)
if lAddrSub !=3D 0 then
sets &=3D vOwners[lAddrSub]
end if
end for
end for
ss =3D ls + 1
Phase1 =3D (ls !=3D length(sets))
end while
-- Phase 2: Free the memory addresses.
for i =3D length(sets) to 1 by -1 do
lOwnerSub =3D find(sets[i], vOwners)
for j =3D 1 to length(vSets[lOwnerSub]) do
if vSets[lOwnerSub][j] !=3D 0 then
myFree(vSets[lOwnerSub][j])
end if
end for
end for
if sets[1] !=3D 0 then
myFree(sets[1])
end if
-- Phase 3: - clean up the allocation array
for i =3D 1 to length(sets) do
lOwnerSub =3D find(sets[i], vOwners)
vSets =3D removeIndex(lOwnerSub, vSets)
vOwners =3D removeIndex(lOwnerSub, vOwners)
for j =3D 1 to length(vSets) do
lOwnerSub =3D find(sets[i], vSets[j])
if lOwnerSub !=3D 0 then
vSets[j] =3D removeIndex(lOwnerSub,
vSets[j])
exit
end if
end for
end for
end procedure
-- This gives back to the system, all the memory acquired by calling
-- /acquire_mem(). You must not use any previously acquired memory =
blocks
-- after this has been called. /n
--
-- /b"NOTE:" When using the Win32Lib library, it is /b not required to =
call
-- this function as that is done automatically when WinMain() completes.
--
-- /b"WARNING:" Calling this before WinMain() has ended will probably =
cause the
-- the /i Win32Lib routines to crash.
--
-- Example:
--
--/code
-- -- Return all the memory areas
-- release_all_mem()
--
--/endcode
global procedure release_all_mem()
atom VOID
-- Matt Lewis 3/14/01
-- changed this to avoid GPF on close
if vHeap then
VOID =3D c_func(xHeapDestroy,{ vHeap })
=20
-- reinitialize variables
vSets =3D {}
vOwners =3D {}
vHeap =3D 0
vAllocations =3D 0
vAlloted =3D 0
end if
end procedure
--/ret SEQUENCE: Definition to allotted memory.
-- /i FldDefn is either a number of bytes to allocate, one of the =
predefined
-- datatypes (listed below), or a 2-element sequence containing a repeat =
count
-- and a datatype or length.
--
-- If a number of bytes is supplied, the field is aligned to the next =
32-bit
-- boundry before allocation.
--
-- The returned allotment definition is used by /store and /fetch. It =
has the
-- following structure. /n
-- The definition has three items: /n
-- An /i offset, a /i datatype, and a /i"repeat length" /n
-- Allowable types are: /n
-- /li /b Byte: 8 bit value
-- /li /b Word: 16 bit value
-- /li /b Integer: 16 bit value, save as /b Word
-- /li /b Long: 32 bit value
-- /li /b DWord: 32 bit value, same as /b Long
-- /li /b Ptr: 32 bit value, same as /b Long
-- /li /b Hndl: 32 bit value, a pointer to a pointer
-- /li /b HndlAddr: 32 bit value,
-- /li /b Lpsz: Long pointer (32 bits) to zero delimited string
-- /li /b Strz: Fixed size buffer that holds a zero-delim string
--=20
-- Example:
--
-- /code
-- constant
-- msLeft =3D allot( Long ),
-- msTop =3D allot( Long ),
-- msRight =3D allot( Long ),
-- msBottom =3D allot( Long ),
-- msXYZ =3D allot( {4, DWord} ),
-- msReserved =3D allot( 5 ),
-- msName =3D allot( Lpsz ),
-- msBuffer =3D allot( {128, Strz} ),
-- SIZEOF_MYSTRUCT =3D /allotted_size()
-- /endcode
global function allot( object pDataType )
integer soFar, diff, size, i, lCnt
if sequence(pDataType) then
lCnt =3D pDataType[1]
i =3D pDataType[2]
else
lCnt =3D 1
i =3D pDataType
end if
=20
-- save position =20
soFar =3D vAlloted
-- if not a pre-defined type, make sure it gets a word boundary
size =3D find(i, vSizeNames)
if size =3D 0
then
if i > 0 then
diff =3D remainder( soFar, 4 )
if diff then
-- word align
soFar =3D soFar + 4 - diff
end if
-- size is actual size
size =3D i
end if
else
size =3D vSizeLengs[size]
end if
-- allot space
vAlloted +=3D (size * lCnt)
=20
-- return offset, data type and count
return { soFar, i, lCnt }
=20
end function
instructions
-- to get the address from the handle, rather than the handle itself. /n
-- An empty sequence is returned if the parameter was invalid.
--/ret SEQUENCE: Handle's "offset" into a structure.
--
-- Example:
--
-- /code =20
-- constant
-- hDemo =3D /allot( Hndl ),
-- pDemo =3D /allot_handle( hDemo ),
-- SIZEOF_DEMO =3D /allotted_size()
-- . . .
-- x =3D allocate_struct(SIZEOF_DEMO)
-- initDEMO(x)
--
-- h =3D fetch(x, hDemo)
-- a =3D fetch(x, pDemo)
-- -- 'h' will contain the handle, and 'a' the address from the =
handle.
-- /endcode
global function allotted_handle(sequence pHandle)
if length(pHandle) =3D 3
and
pHandle[2] =3D Hndl
then
return {pHandle[1], HndlAddr, pHandle[3]}
else
return {}
end if
=20
=20
end function
--/ret INTEGER: Allotted size of structure.
--
-- Example:
--
-- /code =20
-- constant
-- rectLeft =3D /allot( Long ),
-- rectTop =3D /allot( Long ),
-- rectRight =3D /allot( Long ),
-- rectBottom =3D /allot( Long ),
-- SIZEOF_RECT =3D /allotted_size()
-- /endcode
global function allotted_size()
-- returns allotted size, and clears size
integer soFar
=20
soFar =3D vAlloted
vAlloted =3D 0
=20
return soFar
=20
end function
=20
-- Type conversion is automatic. For example, if an /b Lpsz field is=20
-- used, the value is automatically converted from a sequence to a=20
-- C-style string, and the address of that string is stored in the=20
-- structure.
--
-- Example:
--
--/code
-- -- allocate RECT structure, and populate it
-- atom rect =20
--
-- -- allocate the structure
-- rect =3D /allocate_struct( SIZEOF_RECT )
--
-- -- store values into the structure
-- /store( rect, rectLeft, x1 )
-- /store( rect, rectTop, y1 )
-- /store( rect, rectRight, x2 )
-- /store( rect, rectBottom, y2 )
--/endcode
global procedure store( atom struct, sequence s, object o )
-- Store the data based on its type
integer where, datatype, lCnt
atom at
sequence bytes
=20
-- rest data type
where =3D s[1] + struct
datatype =3D s[2]
lCnt =3D s[3]
-- For sequences, make sure no more than lCnt elements are stored.
if sequence(o) then
if length(o) < lCnt then
lCnt =3D length(o)
end if
end if
=20
-- read, based on datatype
if datatype =3D Byte then
-- poke a byte
if atom(o) then
poke( where, o )
else
poke(where, o[1 .. lCnt])
end if
=20
elsif datatype =3D Word then
-- poke a word
bytes =3D int_to_bytes( o )
poke( where, bytes[1 .. 2] )
=20
elsif datatype =3D Long then
-- poke long(s)
if atom(o) then
poke4( where, o )
else
poke4(where, o[1 .. lCnt])
end if
elsif datatype =3D Lpsz then
-- if atom, treat as long =20
if atom( o ) then
poke4( where, o )
else
-- poke the address of the allotted string
poke4( where, acquire_mem( struct, o ) )
end if
=20
elsif datatype =3D Hndl then
-- poke handle(s)
if atom(o) then
poke4( where, o )
else
poke4(where, o[1 .. lCnt])
end if
elsif datatype =3D HndlAddr then
-- poke a handle's address
at =3D acquire_mem(struct, 4)
poke4( at, o)
poke4( where, at )
elsif datatype =3D Strz then
-- poke a zero-terminated string
bytes =3D o & 0
if lCnt < s[3] then
lCnt +=3D 1
end if
poke(where, bytes[1 .. lCnt])
else
-- poke a string =20
poke( where, o[1 .. lCnt] )
end if
end procedure
--/ret SEQUENCE: containing the C-style string.
-- This is typically done automatically by the /fetch
-- function.
--
-- Example:
--
--/code
-- -- get a C-string from address
-- sequence s
--
-- s =3D /peek_string( address )
--/endcode
integer pPeekStringBufSize pPeekStringBufSize =3D 256
global function peek_string(atom a)
integer i, l, sl
sequence s
-- Initialise
s =3D {}
sl =3D 0
l =3D 0
-- Only deal with non-zero addresses
if a then
-- pick up first byte
i =3D peek(a)
-- Repeat until we find a zero byte
while i do
-- increment string size so far
l +=3D 1
-- if the string buffer size is not big enough
-- then expand it.
if sl < l then
s &=3D repeat(0,pPeekStringBufSize)
sl +=3D pPeekStringBufSize
end if
-- put into the string buffer the byte
s[l] =3D i
-- Point to the next byte
a +=3D 1
=20
-- Pick up next byte
i =3D peek(a)
end while
=20
end if
-- send back all the bytes found.
return s[1 .. l]
end function
into sequences.
-- The initial buffer is 256 bytes and should be adequate. The only =
effect this
-- has is on the speed of conversion. Larger values may speed things up, =
smaller
-- values may slow things down.
--
-- If /i newsize is 0, then 1 is used. If /i newsize is less than zero, =
then 256
-- (the default) is used.
-- Example:
--
--/code
-- -- Change buffer size to 500 bytes.
-- set_peek_string( 500 )
--/endcode
global procedure set_peek_string(integer newsize)
if newsize < 1
then
if newsize =3D 0 then
newsize =3D 1
else
newsize =3D 256
end if
end if
=20
pPeekStringBufSize =3D newsize
=20
end procedure
--/ret ATOM: address.
-- This is typically done automatically by the /fetch
-- function.
--
-- Example:
--
--/code
-- -- get the address from handle
-- atom a, h
--
-- a =3D /peek_handle( h )
--/endcode
global function peek_handle(atom a)
return peek4u(a)
end function
--/ret OBJECT: Field from a structure.
-- Data conversion is automatic. For example, if the field is=20
-- an /b Lpsz, a sequence containing the string will automatically=20
-- be returned.
--
-- Example:
-- /code =20
-- -- fetch the average character width from the text metrics =
structure
-- width =3D /fetch( tm, tmAveCharWidth )
-- /endcode
global function fetch( atom struct, sequence s )
-- fetch the data based on the type
integer size, char, cnt
atom at
-- address is struct + offset
at =3D struct + s[1]
-- get data size
size =3D s[2]
-- get repeation
cnt =3D s[3]
=20
-- read, based on size =20
if size =3D Byte then =20
-- return byte
if cnt > 1 then
return (peek( {at, cnt}) )
else
return peek( at )
end if
elsif size =3D Word then
if cnt =3D 1 then
-- return word
return bytes_to_int( peek({at, 2}) & {0, 0} )
else
s =3D {}
for i =3D 1 to cnt do
s &=3D bytes_to_int( peek({at, 2}) & {0, 0} )
at +=3D 2
end for
return s
end if
=20
elsif size =3D Long then
if cnt =3D 1 then
-- return long
return peek4s( at )
else
s =3D {}
for i =3D 1 to cnt do
s &=3D peek4s(at )
at +=3D 4
end for
return s
end if
elsif size =3D Lpsz then =20
-- get the pointer
at =3D peek4u(at) =20
=20
-- return the string
return peek_string( at )
elsif size =3D Hndl then =20
if cnt =3D 1 then
-- return handle
return peek4u( at )
else
s =3D {}
for i =3D 1 to cnt do
s &=3D peek4u(at )
at +=3D 4
end for
return s
end if
elsif size =3D HndlAddr then =20
-- return a handle's address
at =3D peek4u( at )
at =3D peek_handle(at)
return at
elsif size =3D Strz then
return peek_string(at)
=20
else
-- return the string =20
return( peek( {at, size} ) )
=20
end if
end function
--/ret ATOM: Address of the field in the structure.
-- This is typically used if the structure contains an array.
--
-- In this snippet, the /b memBitmapInfo structure contains an array
-- of /b RGBQUAD colors. The array is populated with the values in=20
-- the pal:
--
-- /code=20
-- -- get the start of the rgbQuad array
-- rgbQuad =3D address( memBitmapInfo, bmiColors )
--
-- -- copy the pal to memory =20
-- for i =3D 1 to colors do
--
-- -- store values
-- /store( rgbQuad, rgbRed, pal[i][1] )
-- /store( rgbQuad, rgbGreen, pal[i][2] )
-- /store( rgbQuad, rgbBlue, pal[i][3] )
-- /store( rgbQuad, rgbReserved, 0 )
-- =20
-- -- move to next quad
-- rgbQuad +=3D SIZEOF_RGBQUAD
--
-- end for
--
-- /endcode
global function address( atom addr, object offset )
-- return address in structure =20
if atom( offset ) then
return addr + offset
elsif length(offset) =3D 3 then
return addr + offset[1]
else
return 0
end if
end function
--/topic Memory Management Routines
--/func new_memset( a )
--/desc Allocates a unique id for a memory set.
--/ret ATOM: An id for a new memory set (memset).
-- A memset id is actually a machine address of a 4-bytes location. You =
can
-- use this 4-byte area for anything you like, until you call =
/release_mem()
--
-- Example:
--
--/code
-- atom ss
--
-- ss =3D new_memset()
-- b =3D acquire_mem(ss, "All you need is love")
-- ...
-- release_mem( ss ) -- Let go of set 'ss'
--/endcode
global function new_memset()
atom ms
ms =3D acquire_mem( 0, UInt )
return ms
end function
------=_NextPart_000_0044_01C0CEEE.1AB3C060--