More Standard Templates for Assembly Programmers



Hi All,
I've now added Deque (double-ended queue) support to the HLA Standard
Template library. I've also modified the existing vector template to
let you selectively choose which methods you want to include/exclude in
a class type you create. For example, if you don't need an insert
method in an "int32Vector" class, you can specify that as follows:

type
int32Vector: stl.vector( int32, supportsAppend:0 );

This gives you better control over the code generation for the
templates, so you don't wind up emitting lots of code for methods you
never call. Several other bug fixes and improvements have been added
as well. For example, rather than having a boolean variable for every
trait the class supports, there are now just three dword values (a
bitmap) for this purpose. And the traits have been expanded a bit.
Further, you can control the traits when declaring a template type
(e.g., see "supportsAppend" above).

Enjoy!
Cheers,
Randy Hyde


// stl.hhf -- HLA Standard Template Library
//
// 11/09/2005-
// Added vector template to library.

#includeonce( "memory.hhf" )
#includeonce( "strings.hhf" )
#includeonce( "cset.hhf" )

namespace stl;

const
true := @global:true;
false := @global:false;

hierarchyNames:string[] :=
[
"isContainer",
"isRandomAccess",
"isArray",
"isVector",
"isDeque"
];

capabilityNames:string[] :=
[
"supportsOutput",
"supportsCompare",
"supportsInsert",
"supportsRemove",
"supportsAppend",
"supportsPrepend",
"supportsSwap",
"supportsForeach",
"supportsrForeach",
"supportsCursor",
"supportsElementSwap",
"supportsObjSwap",
"elementsAreObjects"
];

performanceNames:string[] :=
[
"fastInsert",
"fastRemove",
"fastAppend",
"fastPrepend",
"fastSwap",
"fastElementSwap"
];

// Generate a list of constant declarations, based on the
// above strings. Each declaration takes the form:
//
// <name>_c := @{i};
//
// where "<name>" corresponds to one of the above strings and
// i is an incrementing value (0..n).

?bitNum :uns32 := 0;
#for( cn in capabilityNames )

@text( cn + "_c := @{" + string(bitNum) + "}" );
?bitNum := bitNum + 1;

#endfor

?bitNum :uns32 := 0;
#for( cn in hierarchyNames )

@text( cn + "_c := @{" + string(bitNum) + "}" );
?bitNum := bitNum + 1;

#endfor

?bitNum :uns32 := 0;
#for( cn in performanceNames )

@text( cn + "_c := @{" + string(bitNum) + "}" );
?bitNum := bitNum + 1;

#endfor


/////////////////////////////////////////////////////////////////////////////
//
// parseCapabilities-
// parseHierarchy-
// parsePerformance-
//
// Processes user-defined capability lists.
//
// Each entry in a capability list takes the following form:
//
// <name>:1
// -or-
// <name>:0
//
// Where <name> is one of the names in one of the capability lists.
// Note that no spaces may appear around the ":" character.
// A value of zero disables the capability, a value of one enables it.

#macro parseCapabilities( capabilities, userList ):
caps,
curCapability,
index,
setting,
found;

?caps := capabilities;

#for( curCapability in userList )

// See if the current user-specified entry begins with
// one of the valid capability names:

?index := 0;
?found := false;
#while( !found & index < @elements(stl.capabilityNames) )

?found :=
@index( curCapability, 0, stl.capabilityNames[index] )
= 0;

?index := index + 1;

#endwhile

// Okay, if we found a valid name, let's process it.

#if( found )

?index := index - 1;
?setting :uns32:=
uns32
(
@substr
(
curCapability,
@length( stl.capabilityNames[index] )+1,
10000
)
);

#if( setting = 0 )

?caps := caps &
!@text
(
"stl." +stl.capabilityNames[index] + "_c"
);

#else

?caps := caps |
@text
(
"stl." +stl.capabilityNames[index] + "_c"
);

#endif

#endif

#endfor
caps
#endmacro



#macro parsePerformance( performance, userList ):
perform,
curPerform,
index,
setting,
found;

?perform := performance;

#for( curPerform in userList )

// See if the current user-specified entry begins with
// one of the valid capability names:

?index := 0;
?found := false;
#while( !found & index < @elements(stl.performanceNames) )

?found :=
@index( curPerform, 0, stl.performanceNames[index] ) =
0;

?index := index + 1;

#endwhile

// Okay, if we found a valid name, let's process it.

#if( found )

?index := index - 1;
?setting :=
uns32
(
@substr
(
curPerform,
@length( stl.performanceNames[index] )+1,
10000
)
);

#if( setting = 0 )

?perform := perform &
!@text
(
"stl." +stl.performanceNames[index] + "_c"
);

#else

?perform := perform |
@text
(
"stl." +stl.performanceNames[index] + "_c"
);

#endif

#endif

#endfor
perform
#endmacro


/////////////////////////////////////////////////////////////////////////////
//
// checkTraits-
// Verifies that all user-specified traits (found in "toCheck") are
present
// in the list of valid traits ("validTraits")

#macro checkTraits( toCheck, validTraits ):
curTrait,
userTrait,
found;

#for( userTrait in toCheck )

// See if the current user-specified entry begins with
// one of the valid capability names:

?found :boolean := false;
#for( curTrait in validTraits )

?found :=
found | (@index( userTrait, 0, curTrait ) = 0);

#endfor

// If we didn't find the user-specified trait, complain:

#if( !found )

#error
(
"Unknown trait specified in template declaration: " +
userTrait
)

#endif

#endfor
#endmacro



//////////////////////////////////////////////////////////////////////////////
//
// stdCompares-
//
// Generates a standard set of comparison operations for built-in HLA
types.

#macro stdCompares( theType );

#if
(
@ptype( theType ) = hla.ptBoolean
| @ptype( theType ) = hla.ptByte
| @ptype( theType ) = hla.ptUns8
| @ptype( theType ) = hla.ptChar
)

method symbol.isEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isEqual;

push( ebx );
mov( left, ebx );
mov( [ebx], al );
mov( right, ebx );
cmp( al, [ebx] );
sete( al );
and( $ff, eax );
pop( ebx );

end isEqual;

method symbol.isLess
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLess;

push( ebx );
mov( left, ebx );
mov( [ebx], al );
mov( right, ebx );
cmp( al, [ebx] );
setb( al );
and( $ff, eax );
pop( ebx );

end isLess;

method symbol.isLessEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLessEqual;


push( ebx );
mov( left, ebx );
mov( [ebx], al );
mov( right, ebx );
cmp( al, [ebx] );
setbe( al );
and( $ff, eax );
pop( ebx );

end isLessEqual;


#elseif
(
@ptype( theType ) = hla.ptWord
| @ptype( theType ) = hla.ptUns16
| @ptype( theType ) = hla.ptWChar
)

method symbol.isEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isEqual;

push( ebx );
mov( left, ebx );
mov( [ebx], ax );
mov( right, ebx );
cmp( ax, [ebx] );
sete( al );
and( $ff, eax );
pop( ebx );

end isEqual;

method symbol.isLess
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLess;

push( ebx );
mov( left, ebx );
mov( [ebx], ax );
mov( right, ebx );
cmp( ax, [ebx] );
setb( al );
and( $ff, eax );
pop( ebx );

end isLess;

method symbol.isLessEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLessEqual;


push( ebx );
mov( left, ebx );
mov( [ebx], ax );
mov( right, ebx );
cmp( ax, [ebx] );
setbe( al );
and( $ff, eax );
pop( ebx );

end isLessEqual;


#elseif
(
@ptype( theType ) = hla.ptDWord
| @ptype( theType ) = hla.ptUns32
)

method symbol.isEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isEqual;

push( ebx );
mov( left, ebx );
mov( [ebx], eax );
mov( right, ebx );
cmp( eax, [ebx] );
sete( al );
and( $ff, eax );
pop( ebx );

end isEqual;

method symbol.isLess
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLess;

push( ebx );
mov( left, ebx );
mov( [ebx], eax );
mov( right, ebx );
cmp( eax, [ebx] );
setb( al );
and( $ff, eax );
pop( ebx );

end isLess;

method symbol.isLessEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLessEqual;


push( ebx );
mov( left, ebx );
mov( [ebx], eax );
mov( right, ebx );
cmp( eax, [ebx] );
setbe( al );
and( $ff, eax );
pop( ebx );

end isLessEqual;


#elseif
(
@ptype( theType ) = hla.ptQWord
| @ptype( theType ) = hla.ptUns64
)

method symbol.isEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isEqual;

push( ebx );
push( ecx );
mov( left, ebx );
mov( [ebx+4], eax );
mov( right, ecx );
cmp( eax, [ecx+4] );
if( @e ) then

mov( [ebx], eax );
cmp( eax, [ecx] );

endif;
sete( al );
and( $ff, eax );
pop( ecx );
pop( ebx );

end isEqual;

method symbol.isLess
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLess;

push( ebx );
push( ecx );
mov( left, ebx );
mov( [ebx+4], eax );
mov( right, ecx );
cmp( eax, [ecx+4] );
if( @e ) then

mov( [ebx], eax );
cmp( eax, [ecx] );

endif;
setb( al );
and( $ff, eax );
pop( ecx );
pop( ebx );

end isLess;

method symbol.isLessEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLessEqual;

push( ebx );
push( ecx );
mov( left, ebx );
mov( [ebx+4], eax );
mov( right, ecx );
cmp( eax, [ecx+4] );
if( @e ) then

mov( [ebx], eax );
cmp( eax, [ecx] );

endif;
setbe( al );
and( $ff, eax );
pop( ecx );
pop( ebx );

end isLessEqual;


#elseif
(
@ptype( theType ) = hla.ptLWord
| @ptype( theType ) = hla.ptUns128
)

method symbol.isEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isEqual;

push( ebx );
push( ecx );
mov( left, ebx );
mov( [ebx+12], eax );
mov( right, ecx );
cmp( eax, [ecx+12] );
if( @e ) then

mov( [ebx+8], eax );
cmp( eax, [ecx+8] );
if( @e ) then

mov( [ebx+4], eax );
cmp( eax, [ecx+4] );
if( @e ) then

mov( [ebx], eax );
cmp( eax, [ecx] );

endif;

endif;

endif;
sete( al );
and( $ff, eax );
pop( ecx );
pop( ebx );

end isEqual;

method symbol.isLess
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLess;

push( ebx );
push( ecx );
mov( left, ebx );
mov( [ebx+12], eax );
mov( right, ecx );
cmp( eax, [ecx+12] );
if( @e ) then

mov( [ebx+8], eax );
cmp( eax, [ecx+8] );
if( @e ) then

mov( [ebx+4], eax );
cmp( eax, [ecx+4] );
if( @e ) then

mov( [ebx], eax );
cmp( eax, [ecx] );

endif;

endif;

endif;
setb( al );
and( $ff, eax );
pop( ecx );
pop( ebx );

end isLess;

method symbol.isLessEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLessEqual;

push( ebx );
push( ecx );
mov( left, ebx );
mov( [ebx+12], eax );
mov( right, ecx );
cmp( eax, [ecx+12] );
if( @e ) then

mov( [ebx+8], eax );
cmp( eax, [ecx+8] );
if( @e ) then

mov( [ebx+4], eax );
cmp( eax, [ecx+4] );
if( @e ) then

mov( [ebx], eax );
cmp( eax, [ecx] );

endif;

endif;

endif;
setbe( al );
and( $ff, eax );
pop( ecx );
pop( ebx );

end isLessEqual;


#elseif( @ptype( theType ) = hla.ptInt8 )

method symbol.isEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isEqual;

push( ebx );
mov( left, ebx );
mov( [ebx], al );
mov( right, ebx );
cmp( al, [ebx] );
sete( al );
and( $ff, eax );
pop( ebx );

end isEqual;

method symbol.isLess
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLess;

push( ebx );
mov( left, ebx );
mov( [ebx], al );
mov( right, ebx );
cmp( al, [ebx] );
setl( al );
and( $ff, eax );
pop( ebx );

end isLess;

method symbol.isLessEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLessEqual;


push( ebx );
mov( left, ebx );
mov( [ebx], al );
mov( right, ebx );
cmp( al, [ebx] );
setle( al );
and( $ff, eax );
pop( ebx );

end isLessEqual;

#elseif( @ptype( theType ) = hla.ptInt16 )

method symbol.isEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isEqual;

push( ebx );
mov( left, ebx );
mov( [ebx], ax );
mov( right, ebx );
cmp( ax, [ebx] );
sete( al );
and( $ff, eax );
pop( ebx );

end isEqual;

method symbol.isLess
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLess;

push( ebx );
mov( left, ebx );
mov( [ebx], ax );
mov( right, ebx );
cmp( ax, [ebx] );
setl( al );
and( $ff, eax );
pop( ebx );

end isLess;

method symbol.isLessEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLessEqual;


push( ebx );
mov( left, ebx );
mov( [ebx], ax );
mov( right, ebx );
cmp( ax, [ebx] );
setle( al );
and( $ff, eax );
pop( ebx );

end isLessEqual;


#elseif( @ptype( theType ) = hla.ptInt32 )

method symbol.isEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isEqual;

push( ebx );
mov( left, ebx );
mov( [ebx], eax );
mov( right, ebx );
cmp( eax, [ebx] );
sete( al );
and( $ff, eax );
pop( ebx );

end isEqual;

method symbol.isLess
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLess;

push( ebx );
mov( left, ebx );
mov( [ebx], eax );
mov( right, ebx );
cmp( eax, [ebx] );
setl( al );
and( $ff, eax );
pop( ebx );

end isLess;

method symbol.isLessEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLessEqual;


push( ebx );
mov( left, ebx );
mov( [ebx], eax );
mov( right, ebx );
cmp( eax, [ebx] );
setle( al );
and( $ff, eax );
pop( ebx );

end isLessEqual;


#elseif( @ptype( theType ) = hla.ptInt64 )

method symbol.isEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isEqual;

push( ebx );
push( ecx );
mov( left, ebx );
mov( [ebx+4], eax );
mov( right, ecx );
cmp( eax, [ecx+4] );
if( @e ) then

mov( [ebx], eax );
cmp( eax, [ecx] );

endif;
sete( al );
and( $ff, eax );
pop( ecx );
pop( ebx );

end isEqual;

method symbol.isLess
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLess;

push( ebx );
push( ecx );
mov( left, ebx );
mov( [ebx+4], eax );
mov( right, ecx );
cmp( eax, [ecx+4] );
if( @e ) then

mov( [ebx], eax );
cmp( eax, [ecx] );

endif;
setl( al );
and( $ff, eax );
pop( ecx );
pop( ebx );

end isLess;

method symbol.isLessEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLessEqual;

push( ebx );
push( ecx );
mov( left, ebx );
mov( [ebx+4], eax );
mov( right, ecx );
cmp( eax, [ecx+4] );
if( @e ) then

mov( [ebx], eax );
cmp( eax, [ecx] );

endif;
setle( al );
and( $ff, eax );
pop( ecx );
pop( ebx );

end isLessEqual;


#elseif( @ptype( theType ) = hla.ptInt128 )

method symbol.isEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isEqual;

push( ebx );
push( ecx );
mov( left, ebx );
mov( [ebx+12], eax );
mov( right, ecx );
cmp( eax, [ecx+12] );
if( @e ) then

mov( [ebx+8], eax );
cmp( eax, [ecx+8] );
if( @e ) then

mov( [ebx+4], eax );
cmp( eax, [ecx+4] );
if( @e ) then

mov( [ebx], eax );
cmp( eax, [ecx] );

endif;

endif;

endif;
sete( al );
and( $ff, eax );
pop( ecx );
pop( ebx );

end isEqual;

method symbol.isLess
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLess;

push( ebx );
push( ecx );
mov( left, ebx );
mov( [ebx+12], eax );
mov( right, ecx );
cmp( eax, [ecx+12] );
if( @e ) then

mov( [ebx+8], eax );
cmp( eax, [ecx+8] );
if( @e ) then

mov( [ebx+4], eax );
cmp( eax, [ecx+4] );
if( @e ) then

mov( [ebx], eax );
cmp( eax, [ecx] );

endif;

endif;

endif;
setl( al );
and( $ff, eax );
pop( ecx );
pop( ebx );

end isLess;

method symbol.isLessEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLessEqual;

push( ebx );
push( ecx );
mov( left, ebx );
mov( [ebx+12], eax );
mov( right, ecx );
cmp( eax, [ecx+12] );
if( @e ) then

mov( [ebx+8], eax );
cmp( eax, [ecx+8] );
if( @e ) then

mov( [ebx+4], eax );
cmp( eax, [ecx+4] );
if( @e ) then

mov( [ebx], eax );
cmp( eax, [ecx] );

endif;

endif;

endif;
setle( al );
and( $ff, eax );
pop( ecx );
pop( ebx );

end isLessEqual;


#elseif
(
@ptype( theType ) = hla.ptReal32
| @ptype( theType ) = hla.ptReal64
| @ptype( theType ) = hla.ptReal80
)


method symbol.isEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isEqual;

mov( right, eax );
fld( (type theType [eax]) );
mov( left, eax );
fld( (type theType [eax]) );
fcomip();
fstp( left );
sete( al );
and( $ff, eax );

end isEqual;

method symbol.isLess
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLess;

mov( right, eax );
fld( (type theType [eax]) );
mov( left, eax );
fld( (type theType [eax]) );
fcomip();
fstp( left );
setb( al );
and( $ff, eax );

end isLess;

method symbol.isLessEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLessEqual;

mov( right, eax );
fld( (type theType [eax]) );
mov( left, eax );
fld( (type theType [eax]) );
fcomip();
fstp( left );
setbe( al );
and( $ff, eax );

end isLessEqual;


#elseif( @ptype( theType ) = hla.ptString )

method symbol.isEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;

begin isEqual;

mov( left, eax );
pushd( [eax] );
mov( right, eax );
pushd( [eax] );
call( str.eq );
and( $ff, eax );

end isEqual;

method symbol.isLess
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;

begin isLess;

mov( left, eax );
pushd( [eax] );
mov( right, eax );
pushd( [eax] );
call( str.lt );
and( $ff, eax );

end isLess;

method symbol.isLessEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;

begin isLessEqual;

mov( left, eax );
pushd( [eax] );
mov( right, eax );
pushd( [eax] );
call( str.le );
and( $ff, eax );

end isLessEqual;


#elseif( @ptype( theType ) = hla.ptCset )

method symbol.isEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;

begin isEqual;

mov( left, eax );
pushd( [eax] );
mov( right, eax );
pushd( [eax] );
call( cs.eq );
and( $ff, eax );

end isEqual;

method symbol.isLess
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;

begin isLess;

mov( left, eax );
pushd( [eax] );
mov( right, eax );
pushd( [eax] );
call( cs.lt );
and( $ff, eax );

end isLess;

method symbol.isLessEqual
(
var left:theType;
var right:theType
); @nodisplay; @nostackalign;
begin isLessEqual;

mov( left, eax );
pushd( [eax] );
mov( right, eax );
pushd( [eax] );
call( cs.le );
and( $ff, eax );

end isLessEqual;


#else

// For non-primitive types, the user must write the
// comparison routines, assuming the elements are
// comparable.

#endif

#endmacro


//////////////////////////////////////////////////////////////////////////////
//
// base-
// All STL objects are a subclass of base:

type

base:
class

// isSTL_c exists so we can use the @defined compile-time
// function to determine if an object is an STL object.

const
isSTL_c :boolean := @global:true;

// Compile-time "traits":
//
// Once we know it's an STL object, we can use the
// capabilities constant (at compile-time) to determine
// the capabilities of an unknown STL type:

val
capabilities_c :dword := 0;
hierarchy_c :dword := 0;
performance_c :dword := 0;



var
align( 4 );

// Run-time traits:
//
// At run-time, we can test the capabilities variable
to
// determine the capabilities of an STL object.

capabilities :dword;
hierarchy :dword;
performance :dword;

// Type name is initialized with a string specifying
the
// actual (user-defined) data type:

typeName :string;


// This flag tells us whether the object was
// allocated on the heap by the constructor.

isAlloc :boolean;


// Method that will convert an STL object to a string (for
output):

method toString; abstract;

endclass;

static(4)
vmt(base);



//////////////////////////////////////////////////////////////////////////////
//
// Container-
//
// All container objects are a subclass of "Container"

type
Container:
class inherits( base );
val
hierarchy_c := hierarchy_c | isContainer_c;

var
align(4);

// # of elements (nodes, whatever) in the container

numElements :uns32;

// For containers (i.e., the isContainer_c bit is set
in
// capabilities), containerName specifies the name of
// the container (e.g., "vector")

containerName :string;

method getSize; @returns( "eax" );

endclass;

// getSize-
// Returns the number of container items in EAX:

method Container.getSize; @noframe;
begin getSize;

mov( this.numElements, eax );
ret();

end getSize;

static
vmt(Container);


////////////////////////////////////////////////////////////////////////////
//
// RandomAccessContainer-
// All Random Access class objects are a subclass of
RandomAccessContainer:

type
RandomAccessContainer:
class inherits( Container );
val
hierarchy_c := hierarchy_c | isRandomAccess_c;

endclass;




////////////////////////////////////////////////////////////////////////////
//
// ArrayContainer-
// All Array class objects are a subclass of ArrayContainer:

type
ArrayContainer:
class inherits( RandomAccessContainer );
val
hierarchy_c := hierarchy_c | isArray_c;

var
align( 4 );

// Pointer to allocated storage:

allocData :dword;

// Pointer to end of allocated storage

endAllocData :dword;

// Max space for current allocation

allocSize :uns32;

// Pointer to start of array data:

data :dword;

// Pointer to end of array data:

endData :dword;

// Size of array data currently in use

dataSize :uns32;


method getAllocSize; @returns( "eax" );
method getDataSize; @returns( "eax" );
method createArray( n:uns32; elementSize:uns32 );
method clear;
method destroy;

endclass;


method ArrayContainer.getAllocSize; @noframe;
begin getAllocSize;

mov( this.allocSize, eax );
ret();

end getAllocSize;


method ArrayContainer.getDataSize; @noframe;
begin getDataSize;

mov( this.dataSize, eax );
ret();

end getDataSize;

// Create a generic array object
// (this is a private routine for use by derivative classes)

method ArrayContainer.createArray( n:uns32; elementSize:uns32
);
@nodisplay; @nostackalign;

begin createArray;

push( eax );

// Allocate storage for the array and initialize
// the appropriate fields:

mov( 0, this.numElements ); // No actual elements in use
yet.
mov( 0, this.dataSize );
mov( n, eax );
intmul( elementSize, eax );
mov( eax, this.allocSize ); // Allocated size for vector.
@global:mem.alloc( eax );
mov( eax, this.allocData ); // Pointer to allocated data.
mov( eax, this.data );
mov( eax, this.endData ); // Empty vector, endData is
same as data
add( this.allocSize, eax ); // Compute endAllocData
mov( eax, this.endAllocData );
pop( eax );

end createArray;

// Destroy-
// This is the generic destructor for array types.

method ArrayContainer.destroy; @noframe;
begin destroy;

@global:mem.free( this.allocData ); // Free the data
storage
xor( eax, eax );
mov( eax, this.allocData );
mov( eax, this.endAllocData );
mov( eax, this.data );
mov( eax, this.endData );
mov( eax, this.allocSize );
mov( eax, this.numElements );
mov( eax, this.dataSize );
if( this.isAlloc ) then

// If object was created on the heap, free it here:

@global:mem.free( esi );

endif;
ret();

end destroy;

// Clear-
// Clears (but does not deallocate storage) all the elements
// in the array.

method ArrayContainer.clear; @noframe;
begin clear;

push( eax );
mov( 0, this.numElements );
mov( 0, this.dataSize );
mov( this.data, eax );
mov( eax, this.endData );
pop( eax );
ret();

end clear;



static
vmt( ArrayContainer );



//////////////////////////////////////////////////////////////////////////////
//
// _vector
// This is a special "helper" class for the vector template that moves
a lot
// of code common to all vector types into a single class in order to
reduce
// the size of the code base produced by expanding a vector template.
No
// program should actually declare variables of type _vector, this
class is
// for internal use only.

type
_vector:
class inherits( ArrayContainer );

procedure _append( toAppend:dword; appendSize:dword ); @use
eax;
procedure _insert
(
toInsert:dword;
insertWhere:dword;
insertSize:dword
);

procedure _remove( removeWhere:dword; removeSize:dword );


endclass;

// _append-
// Appends a new element to the end of the vector.
//
// Inputs:
//
// toAppend-
// Pointer to the data to append to the vector
//
// appendSize-
// Size of the data object to append.

procedure _vector._append( toAppend:dword; appendSize:dword );
@nodisplay; @noalignstack;

readonly
sixteenCases:dword[17] :=
[
&defaultCase,
&case1,
&case2,
&defaultCase,
&case4,
&defaultCase,
&defaultCase,
&defaultCase,
&case8,
&defaultCase,
&defaultCase,
&defaultCase,
&defaultCase,
&defaultCase,
&defaultCase,
&defaultCase,
&case16
];
begin _append;

// Begin by making sure we've got enough room in the
// data array to hold the new object:

mov( this.dataSize, edi );
add( appendSize, edi );
if( edi >= this.allocSize ) then

// If the new element we're adding pushes us
// beyond the end of the allocated storage,
// reallocate by doubling the storage.

shl( 1, edi );
mov( edi, this.allocSize );
@global:mem.realloc( this.allocData, edi );
mov( eax, this.data );
mov( eax, this.allocData );
add( eax, edi );
mov( edi, this.endAllocData );

endif;

// Special case out various small object sizes
// for performance reasons.

mov( appendSize, ecx );
mov( this.data, edi );
add( this.dataSize, edi );
push( esi );
mov( toAppend, esi );
cld();
cmp( ecx, 16 );
ja defaultCase;
jmp( sixteenCases[ ecx*4 ]);
case1:
movsb();
jmp caseDone;

case2:
movsw();
jmp caseDone;

case4:
movsd();
jmp caseDone;

case8:
movsd();
movsd();
jmp caseDone;

case16:
movsd();
movsd();
movsd();
movsd();
jmp caseDone;

defaultCase:
rep.movsb();

caseDone:
pop( esi );
mov( edi, this.endData ); // Maintain ptr to end of data.

// Okay, bump up the element count by one:

add( 1, this.numElements );
mov( appendSize, eax );
add(eax, this.dataSize );

end _append;



// _insert-
// Inserts an arbitrary "vector" object into a vector.
//
// Inputs:
//
// toInsert-
// Pointer to vector element to insert into the list.
//
// insertWhere-
// Pointer to the spot in the vector where the new element
// is to be inserted.
//
// insertSize-
// Size of the object being inserted.

procedure _vector._insert
(
toInsert:dword;
insertWhere:dword;
insertSize:dword
);
begin _insert;

push( esi );

// As we're expanding the array by one element, let's make
// sure we have room for the expansion:

mov( this.dataSize, ecx );
add( insertSize, ecx );
if( ecx >= this.allocSize ) then

// If the new element we're adding pushes us
// beyond the end of the allocated storage,
// reallocate by doubling the storage.

lea( ecx, [ecx+ecx] );
mov( ecx, this.allocSize );
@global:mem.realloc( this.allocData, ecx );
mov( eax, this.data );
mov( eax, this.allocData );
add( ecx, eax );
mov( eax, this.endAllocData );
sub( ecx, eax );
add( this.dataSize, eax );
mov( eax, this.endData );

endif;


// Okay, make room for the item we're going to insert:

mov( insertWhere, eax );
if( eax >= this.endData ) then

mov( this.endData, eax );

else

// If we are inserting data at some point other than
// at the end of the array, then we've got to move
// everything along by one position to open up space
// for the new element:

std();
mov( this.endData, edi );
mov( edi, ecx );
sub( eax, ecx );

// Three variants of the actual insertion code,
// based on the size of the object we're inserting:

test( %11, insertSize );
jz do4;
test( %1, insertSize );
jz do2;

lea( esi, [edi-1] );
add( insertSize, edi );
sub( 1, edi );
rep.movsb();
jmp SpaceMade;

do2:
// Do this if the object's size is a multiple of 2
bytes:

lea( esi, [edi-2] );
add( insertSize, edi );
sub( 2, edi );
shr( 1, ecx );
rep.movsw();
jmp SpaceMade;


do4:

lea( esi, [edi-4] );
add( insertSize, edi );
sub( 4, edi );
shr( 2, ecx );
rep.movsd();

SpaceMade:

endif;

// Okay, copy the data passed in toInsert into
// the spot we've opened up:
//
// Three variants of the actual copy code,
// based on the size of the object we're inserting:

test( %11, insertSize );
jz do4a;
test( %1, insertSize );
jz do2a;

// Okay, copy the data passed in the toInsert
// parameter into the location we've emptied up:

cld();
mov( insertSize, ecx );
mov( toInsert, esi );
mov( eax, edi );
rep.movsb();
jmp copyDone;

do2a:
// Do this if the object's size is a multiple of 2
bytes:

cld();
mov( insertSize, ecx );
mov( toInsert, esi );
shr( 1, ecx );
mov( eax, edi );
rep.movsw();
jmp copyDone;


do4a:
// Okay, copy the data passed in the toInsert
// parameter into the location we've emptied up:

cld();
mov( insertSize, ecx );
mov( toInsert, esi );
shr( 2, ecx );
mov( eax, edi );
rep.movsd();

copyDone:
pop( esi );

// Update the endData pointer to reflect the new
// array size:

mov( insertSize, eax );
add( eax, this.endData );
add( eax, this.dataSize );

// We're one element larger, so take that into
// consideration:

add( 1, this.numElements );

end _insert;


// _remove
// Helper routine that removes a vector element from a vector.
//
// Inputs:
//
// removeWhere-
// Address of element to remove.
//
// removeSize-
// Size of element to remove.

procedure _vector._remove( removeWhere:dword; removeSize:dword
);
begin _remove;

mov( removeWhere, eax );
if( eax < this.endData ) then

push( ecx );
push( edi );
push( esi );
mov( this.endData, ecx );
sub( eax, ecx );
mov( eax, esi );
add( removeSize, esi );
mov( eax, edi );

// Special cases for vectorTypes whoses sizes
// are multiples of two or four bytes:

mov( removeSize, eax );
test( %11, eax );
jz do4;
test( %1, eax );
jz do2;

rep.movsb();
jmp RemoveDone;


do2:
shr( 1, ecx );
rep.movsw();
jmp RemoveDone;

do4:
shr( 2, ecx );
rep.movsd();

RemoveDone:
pop( esi );
pop( edi );
pop( ecx );
sub( 1, this.numElements );
sub( eax, this.dataSize );
sub( eax, this.endData );

endif;

end _remove;






//////////////////////////////////////////////////////////////////////////////
//
// The vector type.
//
// This is a dynamic array (single dimension) whose size can change
// as needed at runtime.
//
// Arguments:
//
// vectorType-
// data type for each element in the vector
//
// specificCapabilities-
// Enabled/disabled capabilities for this vector (used to
alter
// the default settings for a vector).
//
//
// Produces:
//
// Three different types:
//
// vectorType-
// a class for the specified vector type.
//
// vectorType_cursor-
// a type for cursors that point into the vectorType
class.
//
// p_vectorType-
// a pointer to the vectorType class.
//
// (Note: substitute the actual parameter name for "vectorType"
// in each of the above)
//
// Publically accessible fields in the class created by this macro
// (these fields should be treated as read-only):
//
// isSTL_c-
// a constant, set to true, that you can use @define on to
// determine that this type is an STL type.
//
// capabilities_c (compile-time)
// capabilities (run-time)
// a bit-mapped array of booleans that specify the
capabilities of
// a vector object. Test for the presence of a capability by
ANDing
// with one of the *_c constants.
//
// typeName-
// a string holding the vectorType type name.
//
// containerName-
// the string "vector".
//
//
// procedures, methods, and iterators:
//
// procedure create( numElements:uns32 );
// Constructor for the vector class. If called with the class
// name (e.g., symbol.create(n)) then it will create the
object
// on the heap and return a pointer to the new "symbol" object
// in the ESI register. If called with an actual "symbol"
variable,
// this constructor initializes that variable.
//
// Note: if the underlying vector type is a class type, this
// constructor does *not* call the create procedure for each
// of the underlying elements in the vector. That is the
caller's
// responsibility.
//
// method destroy;
// Destructor for the vector template class. Deallocates the
storage
// associated with the object. Note that if the underlying
elements
// are a class type, this destructor does not call the
destructor
// for each of those elements. You must explicitly destroy
them
// prior to calling this destructor.
//
// method getSize;
// returns the number of vector elements in EAX. Operates in
O(1) time.
//
// method getAllocSize; @returns( "eax" );
// Returns the number of bytes allocated for the data in the
// vector (in EAX). Operates in O(1) time.
//
// method getDataSize; @returns( "eax" );
// Returns the number of bytes allocated for the data in
actual
// use in the vector. Operates in O(1) time.
//
// method clear;
// Removes all elements from the vector. Operates in O(1)
time.
//
// iterator forEachElement;
// Iterates through the vector from beginning to end and on
each
// iteration returns a pointer to the current vector element
in
// the EAX register.
//
// iterator rForEachElement;
// As above, except it iterates through the vector from the
end
// back to the beginning.
//
// method appendRef( var toAppend:vectorType );
// Appends the object "toAppend" (passed by reference) to the
end
// of the vector. Operates in O(1) time.
//
// method appendVal( toAppend:vectorType );
// Appends the object "toAppend" (passed by value) to the end
// of the vector. Operates in O(1) time.
//
// method prependRef( var toPrepend:vectorType );
// Inserts the object "toPrepend" at the beginning of the
list.
// toPrepend is passed by reference (great for large
vectorType
// objects). Operates in O(n) time.
//
// method prependVal( toPrepend:vectorType );
// Inserts the object "toPrepend" at the beginning of the
list.
// toPrepend is passed by value (great for smaller vectorType
// objects). Operates in O(n) time.
//
// method insertRef( var toInsert:vectorType; posn:uns32 );
// Inserts the object "toInsert" in front the the posnTH
object.
// toInsert is passed by reference (great for large vectorType
// objects). Operates in O(n) time. Note that items are
indexed
// starting with position zero. If posn is greater than the
// number of vector elements, this method appends the object
// to the end of the vector.
//
// method insertVal( toInsert:vectorType; posn:uns32 );
// Inserts the object "toInsert" in front the the posnTH
object.
// toInsert is passed by value (great for small vectorType
// objects). Operates in O(n) time. Note that items are
indexed
// starting with position zero. If posn is greater than the
// number of vector elements, this method appends the object
// to the end of the vector.
//
// method remove( n:uns32 );
// Removes the object at posn "n" from the vector. If n is
greater
// than the number of vector elements, this method has no
effect.
// Operates in O(n) time.
//
// method remove_first;
// Removes the first element from the vector.
// Operates in O(n) time.
//
// method remove_last;
// Removes the last element from the vector.
// Operates in O(1) time.
//
// Cursor methods:
// Note: you should not assume that there is a correspondance
// between cursors and pointers to vector elements. Though
this
// may be the actual implementation, subclassed vector types
// may not guarantee this same implementation. Treat cursors
as
// opaque (private) types that are used internally by vector
methods.
//
// method nextCursor( var cursor:cursorType );
// Modifies the cursor object passed by reference so that it
// points at the next element of the vector (relative to where
// it currently points). If this would advance the cursor
beyond
// the end of the vector, this method sets "cursor" to point
at
// just beyond the end of the vector (i.e., endCursor).
Operates
// in O(1) time.
//
// method prevCursor( var cursor:cursorType );
// Modifies the cursor object passed by reference so that it
// points at the previous element of the vector (relative to
where
// it currently points). If this would advance the cursor
before
// the beginning of the vector, this method sets "cursor" to
point at
// the start of the vector (i.e., beginCursor). Operates in
O(1)
// time.
//
// method beginCursor( var cursor:cursorType );
// Points the cursor object passed by reference at the start
of
// the vector. Operates in O(1) time.
//
// method endCursor( var cursor:cursorType );
// Points the cursor object passed by reference to the point
// just beyond the end of the vector. Operates in O(1) time.
//
// method front; @returns( "eax" );
// Returns a cursor value in EAX that corresponds to the start
// of the vector. Operates in O(1) time.
//
// method back; @returns( "eax" );
// Returns a cursor value in EAX that corresponds to the end
// of the vector (points beyond the end of the vector).
Operates
// in O(1) time.
//
// method atBack( cursor:cursorType ); @returns( "@z" );
// Compares the cursor passed by reference against the end of
// the vector and sets the Z flag if the cursor is at the end
// of the vector. Operates in O(1) time.
//
// method atFront( cursor:cursorType ); @returns( "@z" );
// Compares the cursor passed by reference against the
beginning
// of the vector and sets the Z flag if the cursor as at the
// start of the vector. Operates in O(1) time.
//
// method at( cursor:cursorType in eax ); @returns( "eax" );
// Returns a pointer (in EAX) to the vector element data
referenced by
// the cursor passed by reference. You should use this method
to
// convert a cursor to a data element pointer and not assume
that
// cursors and data element pointers are equivalent. Operates
in
// O(1) time.
//
// method getAt( cursor:cursorType; var dest:vectorType );
// Copies the data at the vector element referenced by the
cursor
// you pass as the first argument to the location specified by
// the second argument. Operates in O(1) time relative to n
(the
// number of vector elements).
//
// method insertAtVal( toInsert:vectorType; cursor:cursorType );
// Inserts the value "toInsert" before the item referenced by
// the cursor passed as the second argument. Value (toInsert)
is
// passed by value, making this routine good for small
vectorType
// objects. Operates in O(n) time.
//
// method insertAtRef( var toInsert:vectorType; cursor:cursorType
);
// Inserts the value "toInsert" before the item referenced by
// the cursor passed as the second argument. Value (toInsert)
is
// passed by reference, making this routine good for large
vectorType
// objects. Operates in O(n) time.
//
// method removeAt( cursor:cursorType );
// Removes the vector entry referenced by the cursor passed
// as the argument. Operates in O(n) time.
//
// method getRef( n:uns32 in eax ); @returns( "eax" );
// Computes and returns the address (in EAX) of the nTH
element
// in the vector. Operates in O(1) time.
//
// method getVal( n:uns32; var dest:vectorType );
// Copies the value in the nTH vector element to the location
// specified by the second parameter.
//
// method swapObj( var obj:symbol );
// Swaps the current vector object (THIS) with the one
specified
// by the parameter. This routine operates in O(1) time and is
// very fast.
//
// method swapElements( var first:vectorType; second:vectorType );
// Swaps two vector elements (not necessarily in the same
vector).
// Operates in O(1) time.
//
// method isEqual
// (
// var left:vectorType;
// var right:vectorType
// ); @returns( "al" );
//
// Compares two vectorType objects that are passed by
reference
// and returns AL=1 (and clears the Z flag) if they are equal.
//
// method isLess
// (
// var left:vectorType;
// var right:vectorType
// ); @returns( "al" );
//
// Compares two vectorType objects that are passed by
reference
// and returns AL=1 (and clears the Z flag) if left < right.
//
//
// method isLessEqual
// (
// var left:vectorType;
// var right:vectorType
// ); @returns( "al" );
//
// Compares two vectorType objects that are passed by
reference
// and returns AL=1 (and clears the Z flag) if left <= right.
//
// Note: you can simulate <>, >, and >= by inverting the value in
AL
// upon return from the above three routines.
//
// method toString; @external( outputFunc[0] );
// (Optional implementation) Converts an object of type
"symbol"
// to a string for use by output routines like stdout.put.
//
//
// Note class _vector is a generic helper class for the vector template
// that is used to reduce the amount of code generated for multiple
vector
// types.



#macro vector( rawVectorType, specificTraits[] ):
symbol,
vectorType,
section,
cursorType,
ptrType,
temp;

@forward( symbol );
vectorType : rawVectorType;

?section := @section;
#if( (section & @global:hla.inType) <> @global:hla.inType )

#error( "VECTOR declarations may only appear in TYPE section" )

#else

// Create a cursor type for this class:

?temp :string := @string( @text( symbol ) );
?cursorType :string := temp + "_cursor";
?cursorType :text := cursorType;
cursorType :pointer to vectorType;

// Create a pointer type for this class:

?ptrType :string := "p_" + temp;
?ptrType :text := ptrType;
ptrType :pointer to symbol;

// Create the actual class:

symbol:
class inherits( stl._vector );

// Set up the default capabilities for a vector:

val
hierarchy_c := hierarchy_c | stl.isVector_c;

capabilities_c :=
capabilities_c
#if( @isClass( vectorType ))
| stl.elementsAreObjects_c
#endif

| stl.supportsAppend_c
| stl.supportsPrepend_c
| stl.supportsInsert_c
| stl.supportsRemove_c
| stl.supportsSwap_c
| stl.supportsForeach_c
| stl.supportsrForeach_c
| stl.supportsCursor_c
| stl.supportsElementSwap_c
| stl.supportsObjSwap_c;


// Okay, process any specific capabilities provided
in
// the vector declaration:

capabilities_c :=
stl.parseCapabilities
(
capabilities_c,
specificTraits
);


// Set up the default performance traits:

performance_c :=
performance_c
#if( @size( vectorType ) <= 16 )
| stl.fastElementSwap_c
#endif
| stl.fastAppend_c
| stl.fastSwap_c;


// Okay, process any specific performance traits
// provided in the vector declaration:

performance_c :=
stl.parsePerformance
(
performance_c,
specificTraits
);


// Just as a safety net, let's verify that all
// the user-specified traits are valid:

stl.checkTraits
(
specificTraits,
[[stl.performanceNames],[stl.capabilityNames]]
);


readonly
typeName_ro :string := temp;


// Public entities begin here:
//
// Constructor and destructor.
// Note that all vector types have a constructor and
destructor

procedure create( numElements:uns32 );

// Note: inherits destructor from arrayContainer class
// method destroy;


// iterators:

#if( (symbol.capabilities_c & stl.supportsForeach_c) <>
0 )

iterator forEachElement;

#endif

#if( (symbol.capabilities_c & stl.supportsrForeach_c)
<> 0 )

iterator rForEachElement;

#endif

// Element append, insert, and removal:

#if( (symbol.capabilities_c & stl.supportsAppend_c) <>
0 )

method appendRef( var toAppend:vectorType );
method appendVal( toAppend:vectorType );

#endif

#if( (symbol.capabilities_c & stl.supportsPrepend_c) <>
0 )

method prependRef( var toPrepend:vectorType );
method prependVal( toPrepend:vectorType );

#endif


#if( (symbol.capabilities_c & stl.supportsInsert_c) <>
0 )

method insertRef( var toInsert:vectorType;
posn:uns32 );
method insertVal( toInsert:vectorType; posn:uns32
);

#endif


#if( (symbol.capabilities_c & stl.supportsRemove_c) <>
0 )

method remove( n:uns32 );
method remove_first;
method remove_last;

#endif



#if( (symbol.capabilities_c & stl.supportsCursor_c) <>
0 )

// Cursor manipulation routines:

method nextCursor( var cursor:cursorType );
method prevCursor( var cursor:cursorType );
method beginCursor( var cursor:cursorType );
method endCursor( var cursor:cursorType );
method front; @returns( "eax" );
method back; @returns( "eax" );

method atBack( cursor:cursorType ); @returns( "@z"
);
method atFront( cursor:cursorType ); @returns( "@z"
);
method at( cursor:cursorType in eax ); @returns(
"eax" );

method getAt( cursor:cursorType; var
dest:vectorType );

#if( (symbol.capabilities_c & stl.supportsInsert_c)
<> 0 )

method insertAtVal
(
toInsert:vectorType;
cursor:cursorType
);
method insertAtRef
(
var toInsert:vectorType;
cursor:cursorType
);

#endif

#if( (symbol.capabilities_c & stl.supportsRemove_c)
<> 0 )

method removeAt( cursor:cursorType );

#endif

#endif

// Accessor methods:

method getRef( n:uns32 in eax ); @returns( "eax" );
method getVal( n:uns32; var dest:vectorType );

// Element & object swapping:

#if( (symbol.capabilities_c & stl.supportsObjSwap_c) <>
0 )

method swapObj( var obj:symbol );

#endif

#if( (symbol.capabilities_c &
stl.supportsElementSwap_c) <> 0 )

method swapElements
(
var first:vectorType;
second:vectorType
);

#endif

// Comparisons:

#if( (capabilities_c & stl.supportsCompare_c) <> 0 )

method isEqual
(
var left:vectorType;
var right:vectorType
); @returns( "al" );

method isLess
(
var left:vectorType;
var right:vectorType
); @returns( "al" );


method isLessEqual
(
var left:vectorType;
var right:vectorType
); @returns( "al" );

#endif



// Output method

#if( (capabilities_c & stl.supportsOutput_c) <> 0 )

override method toString;

#endif


endclass;

// Emit the VMT for this class:

static
vmt( stl.symbol );


// Object constructor.
// If called with ESI = 0 (class constructor call) then
// this routine allocates storage for the object itself
// and returns a pointer to that object in ESI. For all
// objects, this code also allocates storage for a vector
// with "numElements" entries.

procedure symbol.create( numElements:uns32 );
@nodisplay; @noalignstack;
begin create;

push( eax );
xor( eax, eax ); // Init isAlloc to false.
if( esi = 0 ) then

mem.alloc( @size( symbol ));
mov( eax, esi );
mov( true, al ); // Set isAlloc true.

endif;
mov( al, this.isAlloc );

// Initialize the VMT pointer:

mov( &symbol._VMT_, this._pVMT_ );

// Allocate storage for the vector and initialize
// the array fields:

this.createArray( numElements, @size( vectorType ));

// Runtime information:
// Begin by initializing the standard boolean variables
with
// their respective values for a vector.

mov( symbol.capabilities_c, this.capabilities );
mov( symbol.performance_c, this.performance );
mov( symbol.hierarchy_c, this.hierarchy );

// Other vector specific-initialization:

lea( eax, "vector" );
mov( eax, this.containerName );

mov( symbol.typeName_ro, eax );
mov( eax, this.typeName );


pop( eax );

end create;



#if( (symbol.capabilities_c & stl.supportsAppend_c) <> 0 )

// appendRef-
// Appends a new element to the end of the array.
// The value passed as the argument is passed by reference.
// You should use this append function when the vector element
// type is large.

method symbol.appendRef( var toAppend:vectorType );
@nodisplay; @noalignstack;
begin appendRef;

// Begin by making sure we've got enough room in the
// data array to hold the new object:

pushfd();
push( eax );
push( ecx );
push( edi );
cld();

this._append
(
#{ push( toAppend ); }#,
@size( vectorType )
);

pop( edi );
pop( ecx );
pop( eax );
popfd();

end appendRef;



// appendVal-
// Appends a new element to the end of the array.
// The value passed as the argument is passed by value.
// You should use this append function when the vector element
// type is small (typically 16 bytes or less).


method symbol.appendVal( toAppend:vectorType );
@nodisplay; @noalignstack;
begin appendVal;

// Begin by making sure we've got enough room in the
// data array to hold the new object:

pushfd();
push( eax );
push( ecx );
push( edi );
cld();

this._append
(
#{
lea( eax, toAppend );
push( eax );
}#,
@size( vectorType )
);

pop( edi );
pop( ecx );
pop( eax );
popfd();

end appendVal;

#endif



#if( (symbol.capabilities_c & stl.supportsPrepend_c) <> 0 )


// prependRef-
//
// Inserts an array element (toInsert) at the beginning of
// the array.
//
// prependRef passes toInsert by reference, so you should use
this
// function to insert large objects into the array.

method symbol.prependRef( var toPrepend:vectorType );
@nodisplay; @noalignstack;
begin prependRef;

push( eax );
push( ecx );
pushfd();
push( edi );

this._insert
(
#{ push( toPrepend ); }#,
this.data,
@size( vectorType )
);


pop( edi );
popfd();
pop( ecx );
pop( eax );

end prependRef;



// prependVal-
//
// Inserts an array element (toInsert) at the beginning of
// the array.
//
// prependVal passes toInsert by value, so you should use this
// function to insert small objects into the array.


method symbol.prependVal( toPrepend:vectorType );
@nodisplay; @noalignstack;
begin prependVal;

push( eax );
push( ecx );
pushfd();
push( edi );

this._insert
(
#{
lea( eax, toPrepend );
push( eax );
}#,
this.data,
@size( vectorType )
);


pop( edi );
popfd();
pop( ecx );
pop( eax );

end prependVal;


#endif



#if( (symbol.capabilities_c & stl.supportsInsert_c) <> 0 )


// insertRef-
//
// Inserts an array element (toInsert) at position posn within
// the array. If posn is beyond the end of the array, then
this
// code appends the value to the end of the array.
//
// insertRef passes toInsert by reference, so you should use
this
// function to insert large objects into the array.

method symbol.insertRef( var toInsert:vectorType; posn:uns32 );
@nodisplay; @noalignstack;
begin insertRef;

push( eax );
push( ecx );
pushfd();
push( edi );

intmul( @size( vectorType ), posn, eax );
add( this.data, eax );
this._insert
(
#{ push( toInsert ); }#,
eax,
@size( vectorType )
);


pop( edi );
popfd();
pop( ecx );
pop( eax );

end insertRef;



// insertVal-
//
// Inserts an array element (toInsert) at position posn within
// the array. If posn is beyond the end of the array, then
this
// code appends the value to the end of the array.
//
// insertVal passes toInsert by value, so you should use this
// function to insert small objects into the array.


method symbol.insertVal( toInsert:vectorType; posn:uns32 );
@nodisplay; @noalignstack;
begin insertVal;

push( eax );
push( ecx );
pushfd();
push( edi );

this._insert
(
#{
lea( eax, toInsert );
push( eax );
}#,
#{
intmul( @size( vectorType ), posn, eax );
add( this.data, eax );
push( eax );
}#,
@size( vectorType )
);

pop( edi );
popfd();
pop( ecx );
pop( eax );

end insertVal;


#if( (symbol.capabilities_c & stl.supportsCursor_c) <> 0 )

// insertAtRef-
//
// Inserts an array element just before the element
specified
// by the cursor passed as the second argument.
//
// insertAtRef passes toInsert by reference, so you should
use this
// function to insert large objects into the array.

method symbol.insertAtRef( var toInsert:vectorType;
cursor:cursorType );
@nodisplay; @noalignstack;
begin insertAtRef;

push( eax );
push( ecx );
pushfd();
push( edi );

this._insert
(
#{ push( toInsert ); }#,
cursor,
@size( vectorType )
);


pop( edi );
popfd();
pop( ecx );
pop( eax );

end insertAtRef;

// insertAtVal-
//
// Inserts an array element (toInsert) in front of the
item
// specified by the cursor passed as the second argument.
//
// insertAtVal passes toInsert by value, so you should use
this
// function to insert small objects into the array.


method symbol.insertAtVal( toInsert:vectorType;
cursor:cursorType );
@nodisplay; @noalignstack;
begin insertAtVal;

push( eax );
push( ecx );
pushfd();
push( edi );

this._insert
(
#{
lea( eax, toInsert );
push( eax );
}#,
cursor,
@size( vectorType )
);

pop( edi );
popfd();
pop( ecx );
pop( eax );

end insertAtVal;

#endif

#endif


#if( (symbol.capabilities_c & stl.supportsRemove_c) <> 0 )


// remove-
// Removes the nTH item from the end of the array.
//
// If n is greater than the number of items in the array,
// then this function has no effect.

method symbol.remove( n:uns32 );
@nodisplay; @noalignstack;
begin remove;

push( eax );
intmul( @size( vectorType ), n, eax );
add( this.data, eax );
this._remove( eax, @size( vectorType ));
pop( eax );

end remove;

// remove_first-
// Removes the first item from a vector.


method symbol.remove_first; @noframe;
begin remove_first;

push( eax );
this._remove( this.data, @size( vectorType ));
pop( eax );
ret();

end remove_first;


// remove_last-
// Removes the last item from a vector.

method symbol.remove_last; @noframe;
begin remove_last;

push( eax );
mov( this.endData, eax );
sub( @size( vectorType ), eax );
if( eax > this.data ) then

mov( eax, this.endData );
sub( 1, this.numElements );

endif;
pop( eax );

end remove_last;


#if( (symbol.capabilities_c & stl.supportsCursor_c) <> 0 )

// removeAt-
// Removes the item appearing at the location specified
// by the cursor passed as a parameter.

method symbol.removeAt( cursor:cursorType );
@nodisplay; @noalignstack;
begin removeAt;

push( eax );
mov( cursor, eax );
this._remove( eax, @size( vectorType ));
pop( eax );

end removeAt;

#endif

#endif


#if( (symbol.capabilities_c & stl.supportsForeach_c) <> 0 )

// The following iterators will sequence through
// all the elements of the array. On each iteration
// of the corresponding foreach loop, this iterator
// will return the *address* of the respective vector
// element.
//
// forEachElement returns items from the start to the end of
the object
// rForEachElement returns items from the end to the start.

iterator symbol.forEachElement;
@nodisplay; @noalignstack;
begin forEachElement;

push( eax );
push( edx );
mov( this.data, edx );
while( edx < this.endData ) do

push( esi );
push( edx );
mov( edx, eax );
yield();
pop( edx );
pop( esi );
add( @size( vectorType ), edx );

endwhile;
pop( edx );
pop( eax );

end forEachElement;


#endif

#if( (symbol.capabilities_c & stl.supportsrForeach_c) <> 0 )


iterator symbol.rForEachElement;
@nodisplay; @noalignstack;
begin rForEachElement;

push( eax );
push( edx );
mov( this.endData, edx );
while( edx > this.data ) do

sub( @size( vectorType ), edx );
push( esi );
push( edx );
mov( edx, eax );
yield();
pop( edx );
pop( esi );

endwhile;
pop( edx );
pop( eax );

end rForEachElement;

#endif


#if( (symbol.capabilities_c & stl.supportsCursor_c) <> 0 )

// nextCursor-
// Adjusts the cursor to point at the next element of the
// vector and returns this pointer in EAX. If this operation
// would cause the cursor to move beyond the end of the
// vector, then it just returns this.endData.

method symbol.nextCursor( var cursor:cursorType ); @noframe;
begin nextCursor;

push( eax );
mov( [esp+@offset( cursor )], eax );
push( ebx );
mov( [eax], ebx );
add( @size( vectorType ), ebx );
if( ebx > this.endData ) then

mov( this.endData, ebx );

endif;
mov( ebx, [eax] );
pop( ebx );
pop( eax );
ret();

end nextCursor;

// prevCursor-
// Adjust the cursor to point at the previous element in the
vector.
// If doing this would position the cursor before the start of
the
// array, then this just sets the cursor position to the start
of
// the array.

method symbol.prevCursor( var cursor:cursorType ); @noframe;
begin prevCursor;

push( eax );
mov( [esp+@offset( cursor )], eax );
push( ebx );
mov( [eax], ebx );
sub( @size( vectorType ), ebx );
if( ebx < this.data ) then

mov( this.data, ebx );

endif;
mov( ebx, [eax] );
pop( ebx );
pop( eax );
ret();

end prevCursor;



// beginCursor-
// Puts a pointer to the start of the vector in cursor:

method symbol.beginCursor( var cursor:cursorType ); @noframe;
begin beginCursor;

push( eax );
mov( [esp+@offset( cursor )], eax );
mov( this.data, [eax] );
pop( eax );
ret();

end beginCursor;

// endCursor-
// Puts a pointer to the end of the vector in cursor:

method symbol.endCursor( var cursor:cursorType ); @noframe;
begin endCursor;

push( eax );
mov( [esp+@offset( cursor )], eax );
mov( this.endData, [eax] );
pop( eax );
ret();

end endCursor;

// at-
// Moves the address of the data element referenced by the
// cursor passed as the argument into the EAX register.

method symbol.at( cursor:cursorType in eax ); @noframe;
begin at;

// This is a trivial function, as cursors and pointers
// to data elements are the same thing for vectors.

ret();

end at;




// getAt-
// Copies the data item specified by the cursor to
// the variable specified by the second argument.
// If cursor is out of range, no action is taken.

method symbol.getAt( cursor:cursorType; var dest:vectorType );
@nodisplay; @noalignstack;
begin getAt;

pushfd();
push( edi );
cld();
mov( cursor, edi );
if( edi < this.endData ) then

push( esi );
mov( edi, esi );
mov( dest, edi );
#if( @size( vectorType ) = 1 )

movsb();

#elseif( @size( vectorType ) = 2 )

movsw();

#elseif( @size( vectorType ) = 4 )

movsd();

#else

push( ecx );
#if( (@size( vectorType ) & %11) = 0 )

mov( @size( vectorType ) div 4, ecx );
rep.movsd();

#elseif( (@size( vectorType ) & %01) = 0 )

mov( @size( vectorType ) div 2, ecx );
rep.movsw();

#else

mov( @size( vectorType ), ecx );
rep.movsb();

#endif
pop( ecx );

#endif
pop( esi );

endif;
pop( edi );
popfd();

end getAt;

// front-
// Returns a cursor value that points at the first element
// of the vector in EAX.

method symbol.front; @noframe;
begin front;

mov( this.data, eax );
ret();

end front;


// back-
// Returns a pointer to the first item *beyond* the end of
// the vector in EAX:

method symbol.back; @noframe;
begin back;

mov( this.endData, eax );
ret();

end back;


// atFront
// Compares the cursor passed as a parameter against the
// start of the vector and sets the zero flag if they
// are equal (that is, the cursor points at the start
// of the vector).

method symbol.atFront( cursor:cursorType ); @noframe;
begin atFront;

push( eax );
mov( [esp+@offset(cursor)], eax );
cmp( eax, this.data );
pop( eax );
ret();

end atFront;


// atBack
// Compares the cursor passed as a parameter against the
// end of the vector and sets the zero flag if they
// are equal (that is, the cursor points just beyond the
// end of the vector).

method symbol.atBack( cursor:cursorType ); @noframe;
begin atBack;

push( eax );
mov( [esp+@offset(cursor)], eax );
cmp( eax, this.endData );
pop( eax );
ret();

end atBack;

#endif




// getRef-
// Computes the address of element n in the array
// and returns this value in EAX. If n exceeds the
// array bounds, then at returns the address of the
// end of the array.

method symbol.getRef( n:uns32 in eax ); @noframe;
begin getRef;

intmul( @size( vectorType ), eax );
add( this.data, eax );
if( eax > this.endData ) then

mov( this.endData, eax );

endif;
ret();

end getRef;


// getVal-
// Copies the nTH data item to the specified variable.
// If n is out of range, no action is taken.

method symbol.getVal( n:uns32; var dest:vectorType );
@nodisplay; @noalignstack;
begin getVal;

pushfd();
push( edi );
cld();
intmul( @size( vectorType ), n, edi );
add( this.data, edi );
if( edi < this.endData ) then

push( esi );
mov( edi, esi );
mov( dest, edi );
#if( @size( vectorType ) = 1 )

movsb();

#elseif( @size( vectorType ) = 2 )

movsw();

#elseif( @size( vectorType ) = 4 )

movsd();

#else

push( ecx );
#if( (@size( vectorType ) & %11) = 0 )

mov( @size( vectorType ) div 4, ecx );
rep.movsd();

#elseif( (@size( vectorType ) & %01) = 0 )

mov( @size( vectorType ) div 2, ecx );
rep.movsw();

#else

mov( @size( vectorType ), ecx );
rep.movsb();

#endif
pop( ecx );

#endif
pop( esi );

endif;
pop( edi );
popfd();

end getVal;



#if( (symbol.capabilities_c & stl.supportsObjSwap_c) <> 0 )

// swapObj-
// Swaps another object with this one:

method symbol.swapObj( var obj:symbol );
@nodisplay; @nostackalign;

const
objEBX:text := "(type " + @string( symbol ) + "
[ebx])";

begin swapObj;


push( eax );
push( ebx );
push( ecx );

mov( obj, ebx );
mov( this.isAlloc, al );
mov( objEBX.isAlloc, ah );
mov( al, objEBX.isAlloc );
mov( ah, this.isAlloc );

mov( this.typeName, ecx );
mov( objEBX.typeName, eax );
mov( ecx, objEBX.typeName );
mov( eax, this.typeName );

mov( this.numElements, ecx );
mov( objEBX.numElements, eax );
mov( ecx, objEBX.numElements );
mov( eax, this.numElements );

mov( this.dataSize, ecx );
mov( objEBX.dataSize, eax );
mov( ecx, objEBX.dataSize );
mov( eax, this.dataSize );

mov( this.allocSize, ecx );
mov( objEBX.allocSize, eax );
mov( ecx, objEBX.allocSize );
mov( eax, this.allocSize );

mov( this.data, ecx );
mov( objEBX.data, eax );
mov( ecx, objEBX.data );
mov( eax, this.data );

mov( this.endData, ecx );
mov( objEBX.endData, eax );
mov( ecx, objEBX.endData );
mov( eax, this.endData );

pop( ecx );
pop( ebx );
pop( eax );

end swapObj;

#endif

#if( (symbol.capabilities_c & stl.supportsElementSwap_c) <> 0 )

method symbol.swapElements
(
var first:vectorType;
second:vectorType
);
@noframe;

begin swapElements;

push( ecx );
push( esi );
push( edi );

#if( @size( vectorType ) = 1 )

mov( [esp+@offset(first)+8], edi );
mov( [esp+@offset(first)+8], esi );
mov( [esi], cl );
mov( [edi], ch );
mov( cl, [edi] );
mov( ch, [esi] );

#elseif( @size( vectorType ) = 2 )

mov( [esp+@offset(first)+8], edi );
mov( [esp+@offset(first)+8], esi );
push( eax );
mov( [esi], ax );
mov( [edi], cx );
mov( ax, [edi] );
mov( cx, [esi] );
pop( eax );

#elseif( @size( vectorType ) = 4 )

mov( [esp+@offset(first)+8], edi );
mov( [esp+@offset(first)+8], esi );
push( eax );
mov( [esi], eax );
mov( [edi], ecx );
mov( eax, [edi] );
mov( ecx, [esi] );
pop( eax );

#elseif( @size( vectorType ) = 8 )

mov( [esp+@offset(first)+8], edi );
mov( [esp+@offset(first)+8], esi );
push( eax );
mov( [esi], eax );
mov( [edi], ecx );
mov( eax, [edi] );
mov( ecx, [esi] );
mov( [esi+4], eax );
mov( [edi+4], ecx );
mov( eax, [edi+4] );
mov( ecx, [esi+4] );
pop( eax );

#elseif( @size( vectorType ) = 16 )

mov( [esp+@offset(first)+8], edi );
mov( [esp+@offset(first)+8], esi );
push( eax );
mov( [esi], eax );
mov( [edi], ecx );
mov( eax, [edi] );
mov( ecx, [esi] );
mov( [esi+4], eax );
mov( [edi+4], ecx );
mov( eax, [edi+4] );
mov( ecx, [esi+4] );
mov( [esi+8], eax );
mov( [edi+8], ecx );
mov( eax, [edi+8] );
mov( ecx, [esi+8] );
mov( [esi+12], eax );
mov( [edi+12], ecx );
mov( eax, [edi+12] );
mov( ecx, [esi+12] );
pop( eax );

#elseif( (@size( vectorType ) & %11) = 0 )

shr( 2, ecx );
rep.movsd();

#elseif( (@size( vectorType ) & %1) = 0 )

shr( 1, ecx );
rep.movsw();

#else

rep.movsb();

#endif


pop( edi );
pop( esi );
pop( ecx );
ret( 8 );

end swapElements;

#endif


// Comparisons-
// Handle the built-in types, but require the user to
// supply comparisons for other types.

#if( (symbol.capabilities_c & stl.supportsCompare_c) <> 0 )

stl.stdCompares( vectorType )

#endif





// Set us back to the TYPE section:

type

#endif

#endmacro




//////////////////////////////////////////////////////////////////////////////
//
// _deque
// This is a special "helper" class for the deque template that moves
a lot
// of code common to all deque types into a single class in order to
reduce
// the size of the code base produced by expanding a deque template.
No
// program should actually declare variables of type _deque, this
class is
// for internal use only.

type
_deque:
class inherits( ArrayContainer );

procedure _append( toAppend:dword; appendSize:dword );
procedure _prepend( toPrepend:dword; prependSize:dword );
procedure _insert
(
toInsert:dword;
insertWhere:dword;
insertSize:dword
);

procedure _remove( removeWhere:dword; removeSize:dword );


endclass;

// _append-
// Appends a new element to the end of the deque.
//
// Inputs:
//
// toAppend-
// Pointer to the data to append to the deque
//
// appendSize-
// Size of the data object to append.

procedure _deque._append( toAppend:dword; appendSize:dword );
@nodisplay; @noalignstack;

var
thisSave:dword;

readonly
sixteenCases:dword[17] :=
[
&defaultCase,
&case1,
&case2,
&defaultCase,
&case4,
&defaultCase,
&defaultCase,
&defaultCase,
&case8,
&defaultCase,
&defaultCase,
&defaultCase,
&defaultCase,
&defaultCase,
&defaultCase,
&defaultCase,
&case16
];
begin _append;

// Begin by making sure we've got enough room in the
// data array to hold the new object:

mov( this.dataSize, edi );
add( appendSize, edi );
add( this.data, edi );
if( edi > this.endAllocData ) then

// If the new element we're adding pushes us
// beyond the end of the allocated storage,
// reallocate by quadrupling the storage (we
// quadruple the storage, rather than double it
// as is done for vectors, so we can add the
// some amount of storage before and after the
// data block; as this is an append operation,
// we'll favor the end of the block and add
// more there).

mov( this.allocSize, edi );
lea( edi, [edi*4] );
mov( esi, thisSave );
mov( edi, this.allocSize );
@global:mem.realloc2
(
this.allocData,
edi,
thunk
#{
// If a memcopy is done, we need to adjust all
// the data pointers after the move.
// Also note that we aren't going to move the
// entire block, only the valid data (i.e., the
// source value passed in ESI is irrelevant).

mov( thisSave, esi );

// Divide the amount of free space in quarters
and
// place the actual data starting at the second
// quarter (so the data is in the middle of the
// allocated block):

mov( this.allocSize, eax );
shr( 2, eax );
add( eax, edi ); // True destination for
move
push( this.data ); // Save as data src for
move.
mov( edi, this.data ); // New data block
pointer.

// Mark the end of the data block:

mov( this.dataSize, ecx );
lea( eax, [edi+ecx] );
mov( eax, this.endData );

// Start of data to copy:

pop( esi );

// Okay, do the actual block move:

test( %11, ecx );
jz do4;
test( %1, ecx );
jz do2;

rep.movsb();
jmp moveDone;

do2:
shr( 1, ecx );
rep.movsw();
jmp moveDone;

do4:
shr( 2, ecx );
rep.movsd();

moveDone:

}#
);
mov( eax, this.allocData );
add( eax, edi );
mov( edi, this.endAllocData );

endif;

// Special case out various small object sizes
// for performance reasons.

mov( appendSize, ecx );
mov( this.data, edi );
add( this.dataSize, edi );
push( esi );
mov( toAppend, esi );
cld();
cmp( ecx, 16 );
ja defaultCase;
jmp( sixteenCases[ ecx*4 ]);
case1:
movsb();
jmp caseDone;

case2:
movsw();
jmp caseDone;

case4:
movsd();
jmp caseDone;

case8:
movsd();
movsd();
jmp caseDone;

case16:
movsd();
movsd();
movsd();
movsd();
jmp caseDone;

defaultCase:
rep.movsb();

caseDone:
pop( esi );
mov( edi, this.endData ); // Maintain ptr to end of data.

// Okay, bump up the element count by one:

add( 1, this.numElements );
mov( appendSize, eax );
add(eax, this.dataSize );

end _append;




// _prepend-
// Prepends a new element to the front of the deque.
//
// Inputs:
//
// toPrepend-
// Pointer to the data to prepend to the deque
//
// prependSize-
// Size of the data object to prepend.

procedure _deque._prepend( toPrepend:dword; prependSize:dword
);
@nodisplay; @noalignstack;

var
thisSave:dword;

readonly
sixteenCases:dword[17] :=
[
&defaultCase,
&case1,
&case2,
&defaultCase,
&case4,
&defaultCase,
&defaultCase,
&defaultCase,
&case8,
&defaultCase,
&defaultCase,
&defaultCase,
&defaultCase,
&defaultCase,
&defaultCase,
&defaultCase,
&case16
];
begin _prepend;

// Begin by making sure we've got enough room in the
// data array to hold the new object:

mov( this.data, edi );
add( prependSize, edi );
sub( this.allocData, edi );
if( edi > this.data ) then

// If the new element we're adding pushes us
// before the beginning of the allocated storage,
// reallocate by quadrupling the storage (we
// quadruple the storage, rather than double it
// as is done for vectors, so we can add the
// some amount of storage before and after the
// data block; as this is an prepend operation,
// we'll favor the start of the block and add
// more there).

mov( this.allocSize, edi );
lea( edi, [edi*4] );
mov( esi, thisSave );
mov( edi, this.allocSize );
@global:mem.realloc2
(
this.allocData,
edi,
thunk
#{
// If a memcopy is done, we need to adjust all
// the data pointers after the move.
// Also note that we aren't going to move the
// entire block, only the valid data (i.e., the
// source value passed in ESI is irrelevant).

mov( thisSave, esi );

// Divide the amount of free space in quarters
and
// place the actual data starting at the third
// quarter (so the data is in the middle of the
// allocated block):

mov( this.allocSize, eax );
shr( 1, eax ); // Start of third
quarter
add( eax, edi ); // True destination for
move
push( this.data ); // Save as data src for
move.
mov( edi, this.data ); // New data block
pointer.

// Mark the end of the data block:

mov( this.dataSize, ecx );
lea( eax, [edi+ecx] );
mov( eax, this.endData );

// Start of data to copy:

pop( esi );

// Okay, do the actual block move:

test( %11, ecx );
jz do4;
test( %1, ecx );
jz do2;

rep.movsb();
jmp moveDone;

do2:
shr( 1, ecx );
rep.movsw();
jmp moveDone;

do4:
shr( 2, ecx );
rep.movsd();

moveDone:

}#
);
mov( eax, this.allocData );
add( eax, edi );
mov( edi, this.endAllocData );

endif;

// Clean up the data pointers:

mov( prependSize, ecx );
add( ecx, this.dataSize );
add( 1, this.numElements );

mov( this.data, edi );
sub( ecx, edi );
mov( edi, this.data );

mov( toPrepend, esi );

// Special case out various small object sizes
// for performance reasons.

cld();
cmp( ecx, 16 );
ja defaultCase;
jmp( sixteenCases[ ecx*4 ]);
case1:
movsb();
jmp caseDone;

case2:
movsw();
jmp caseDone;

case4:
movsd();
jmp caseDone;

case8:
movsd();
movsd();
jmp caseDone;

case16:
movsd();
movsd();
movsd();
movsd();
jmp caseDone;

defaultCase:
rep.movsb();

caseDone:

end _prepend;



// _insert-
// Inserts an arbitrary "deque" object into a deque.
//
// Inputs:
//
// toInsert-
// Pointer to deque element to insert into the list.
//
// insertWhere-
// Pointer to the spot in the deque where the new element
// is to be inserted.
//
// insertSize-
// Size of the object being inserted.

procedure _deque._insert
(
toInsert:dword;
insertWhere:dword;
insertSize:dword
);
var
thisSave:dword;

begin _insert;

push( esi );

// Begin by making sure we've got enough room in the
// data array to hold the new object:

mov( this.dataSize, edi );
add( insertSize, edi );
add( this.data, edi );
if( edi > this.endAllocData ) then

// TODO: we could actually make this operation more
// efficient by opening up room for the inserted object
// if we do a data move during a reallocation. But for
// simplicity's sake we'll skip that for now.
//
//
// If the new element we're adding pushes us
// beyond the end of the allocated storage,
// reallocate by quadrupling the storage (we
// quadruple the storage, rather than double it
// as is done for vectors, so we can add the
// some amount of storage before and after the
// data block; as this is an insert operation,
// we'll favor the end of the block and add
// more there).

mov( this.allocSize, edi );
lea( edi, [edi*4] );
mov( esi, thisSave );
mov( edi, this.allocSize );
@global:mem.realloc2
(
this.allocData,
edi,
thunk
#{
// If a memcopy is done, we need to adjust all
// the data pointers after the move.
// Also note that we aren't going to move the
// entire block, only the valid data (i.e., the
// source value passed in ESI is irrelevant).

mov( thisSave, esi );

// Divide the amount of free space in quarters
and
// place the actual data starting at the second
// quarter. This leaves a larger block of free
space
// at the end, which is good because we're
inserting
// data here.

mov( this.allocSize, eax );
shr( 2, eax );
add( eax, edi ); // True destination for
move
mov( this.data, eax ); // Save as data src for
move.
push( eax );
mov( edi, this.data ); // New data block
pointer.

// Fix the "insertWhere" parameter, as it's now
// pointing at the wrong block:

sub( insertWhere, eax );
neg( eax ); // Really need iw-data
add( edi, eax ); // New insertWhere
value
mov( eax, insertWhere );

// Mark the end of the data block:

mov( this.dataSize, ecx );
lea( eax, [edi+ecx] );
mov( eax, this.endData );

// Start of data to copy:

pop( esi );

// Okay, do the actual block move:

test( %11, ecx );
jz do4;
test( %1, ecx );
jz do2;

rep.movsb();
jmp moveDone;

do2:
shr( 1, ecx );
rep.movsw();
jmp moveDone;

do4:
shr( 2, ecx );
rep.movsd();

moveDone:

}#
);
mov( eax, this.allocData );
add( eax, edi );
mov( edi, this.endAllocData );

endif;


// Okay, make room for the item we're going to insert:

mov( insertWhere, eax );
if( eax >= this.endData ) then

mov( this.endData, eax );

else

// If we are inserting data at some point other than
// at the end of the array, then we've got to move
// everything along by one position to open up space
// for the new element:

std();
mov( this.endData, edi );
mov( edi, ecx );
sub( eax, ecx );

// Three variants of the actual insertion code,
// based on the size of the object we're inserting:

test( %11, insertSize );
jz do4a;
test( %1, insertSize );
jz do2a;

lea( esi, [edi-1] );
add( insertSize, edi );
sub( 1, edi );
rep.movsb();
jmp SpaceMade;

do2a:
// Do this if the object's size is a multiple of 2
bytes:

lea( esi, [edi-2] );
add( insertSize, edi );
sub( 2, edi );
shr( 1, ecx );
rep.movsw();
jmp SpaceMade;


do4a:

lea( esi, [edi-4] );
add( insertSize, edi );
sub( 4, edi );
shr( 2, ecx );
rep.movsd();

SpaceMade:

endif;

// Okay, copy the data passed in toInsert into
// the spot we've opened up:
//
// Three variants of the actual copy code,
// based on the size of the object we're inserting:

test( %11, insertSize );
jz do4b;
test( %1, insertSize );
jz do2b;

// Okay, copy the data passed in the toInsert
// parameter into the location we've emptied up:

cld();
mov( insertSize, ecx );
mov( toInsert, esi );
mov( eax, edi );
rep.movsb();
jmp copyDone;

do2b:
// Do this if the object's size is a multiple of 2
bytes:

cld();
mov( insertSize, ecx );
mov( toInsert, esi );
shr( 1, ecx );
mov( eax, edi );
rep.movsw();
jmp copyDone;


do4b:
// Okay, copy the data passed in the toInsert
// parameter into the location we've emptied up:

cld();
mov( insertSize, ecx );
mov( toInsert, esi );
shr( 2, ecx );
mov( eax, edi );
rep.movsd();

copyDone:
pop( esi );

// Update the endData pointer to reflect the new
// array size:

mov( insertSize, eax );
add( eax, this.endData );
add( eax, this.dataSize );

// We're one element larger, so take that into
// consideration:

add( 1, this.numElements );

end _insert;


// _remove
// Helper routine that removes a deque element from a deque.
//
// Inputs:
//
// removeWhere-
// Address of element to remove.
//
// removeSize-
// Size of element to remove.

procedure _deque._remove( removeWhere:dword; removeSize:dword
);
begin _remove;

mov( removeWhere, eax );
if( eax < this.endData ) then

push( ecx );
push( edi );
push( esi );
mov( this.endData, ecx );
sub( eax, ecx );
mov( eax, esi );
add( removeSize, esi );
mov( eax, edi );

// Special cases for dequeTypes whoses sizes
// are multiples of two or four bytes:

mov( removeSize, eax );
test( %11, eax );
jz do4;
test( %1, eax );
jz do2;

rep.movsb();
jmp RemoveDone;


do2:
shr( 1, ecx );
rep.movsw();
jmp RemoveDone;

do4:
shr( 2, ecx );
rep.movsd();

RemoveDone:
pop( esi );
pop( edi );
pop( ecx );
sub( 1, this.numElements );
sub( eax, this.dataSize );
sub( eax, this.endData );

endif;

end _remove;







/////////////////////////////////////////////////////////////////////////////
//
// deque-
// Double-ended queue class template
//




#macro deque( rawDequeType, specificTraits[] ):
symbol,
dequeType,
section,
cursorType,
ptrType,
temp;

@forward( symbol );
dequeType : rawDequeType;

?section := @section;
#if( (section & @global:hla.inType) <> @global:hla.inType )

#error( "DEQUE declarations may only appear in TYPE section" )

#else

// Create a cursor type for this class:

?temp :string := @string( @text( symbol ) );
?cursorType :string := temp + "_cursor";
?cursorType :text := cursorType;
cursorType :pointer to dequeType;

// Create a pointer type for this class:

?ptrType :string := "p_" + temp;
?ptrType :text := ptrType;
ptrType :pointer to symbol;

// Create the actual class:

symbol:
class inherits( stl._deque );

// Set up the default capabilities for a deque:

val
hierarchy_c := hierarchy_c | stl.isDeque_c;

capabilities_c :=
capabilities_c
#if( @isClass( dequeType ))
| stl.elementsAreObjects_c
#endif

| stl.supportsAppend_c
| stl.supportsPrepend_c
| stl.supportsInsert_c
| stl.supportsRemove_c
| stl.supportsSwap_c
| stl.supportsForeach_c
| stl.supportsrForeach_c
| stl.supportsCursor_c
| stl.supportsElementSwap_c
| stl.supportsObjSwap_c;


// Okay, process any specific capabilities provided
in
// the deque declaration:

capabilities_c :=
stl.parseCapabilities
(
capabilities_c,
specificTraits
);


// Set up the default performance traits:

performance_c :=
performance_c
#if( @size( dequeType ) <= 16 )
| stl.fastElementSwap_c
#endif
| stl.fastAppend_c
| stl.fastSwap_c;


// Okay, process any specific performance traits
// provided in the deque declaration:

performance_c :=
stl.parsePerformance
(
performance_c,
specificTraits
);


// Just as a safety net, let's verify that all
// the user-specified traits are valid:

stl.checkTraits
(
specificTraits,
[[stl.performanceNames],[stl.capabilityNames]]
);


readonly
typeName_ro :string := temp;


// Public entities begin here:
//
// Constructor and destructor.
// Note that all deque types have a constructor and
destructor

procedure create( numElements:uns32 );

// Note: inherits destructor from arrayContainer class
// method destroy;


// iterators:

#if( (symbol.capabilities_c & stl.supportsForeach_c) <>
0 )

iterator forEachElement;

#endif

#if( (symbol.capabilities_c & stl.supportsrForeach_c)
<> 0 )

iterator rForEachElement;

#endif

// Element append, insert, and removal:

#if( (symbol.capabilities_c & stl.supportsAppend_c) <>
0 )

method appendRef( var toAppend:dequeType );
method appendVal( toAppend:dequeType );

#endif

#if( (symbol.capabilities_c & stl.supportsPrepend_c) <>
0 )

method prependRef( var toPrepend:dequeType );
method prependVal( toPrepend:dequeType );

#endif


#if( (symbol.capabilities_c & stl.supportsInsert_c) <>
0 )

method insertRef( var toInsert:dequeType;
posn:uns32 );
method insertVal( toInsert:dequeType; posn:uns32 );

#endif


#if( (symbol.capabilities_c & stl.supportsRemove_c) <>
0 )

method remove( n:uns32 );
method remove_first;
method remove_last;

#endif



#if( (symbol.capabilities_c & stl.supportsCursor_c) <>
0 )

// Cursor manipulation routines:

method nextCursor( var cursor:cursorType );
method prevCursor( var cursor:cursorType );
method beginCursor( var cursor:cursorType );
method endCursor( var cursor:cursorType );
method front; @returns( "eax" );
method back; @returns( "eax" );

method atBack( cursor:cursorType ); @returns( "@z"
);
method atFront( cursor:cursorType ); @returns( "@z"
);
method at( cursor:cursorType in eax ); @returns(
"eax" );

method getAt( cursor:cursorType; var dest:dequeType
);

#if( (symbol.capabilities_c & stl.supportsInsert_c)
<> 0 )

method insertAtVal
(
toInsert:dequeType;
cursor:cursorType
);
method insertAtRef
(
var toInsert:dequeType;
cursor:cursorType
);

#endif

#if( (symbol.capabilities_c & stl.supportsRemove_c)
<> 0 )

method removeAt( cursor:cursorType );

#endif

#endif

// Accessor methods:

method getRef( n:uns32 in eax ); @returns( "eax" );
method getVal( n:uns32; var dest:dequeType );

// Element & object swapping:

#if( (symbol.capabilities_c & stl.supportsObjSwap_c) <>
0 )

method swapObj( var obj:symbol );

#endif

#if( (symbol.capabilities_c &
stl.supportsElementSwap_c) <> 0 )

method swapElements
(
var first:dequeType;
second:dequeType
);

#endif

// Comparisons:

#if( (capabilities_c & stl.supportsCompare_c) <> 0 )

method isEqual
(
var left:dequeType;
var right:dequeType
); @returns( "al" );

method isLess
(
var left:dequeType;
var right:dequeType
); @returns( "al" );


method isLessEqual
(
var left:dequeType;
var right:dequeType
); @returns( "al" );

#endif



// Output method

#if( (capabilities_c & stl.supportsOutput_c) <> 0 )

override method toString;

#endif


endclass;

// Emit the VMT for this class:

static
vmt( stl.symbol );


// Object constructor.
// If called with ESI = 0 (class constructor call) then
// this routine allocates storage for the object itself
// and returns a pointer to that object in ESI. For all
// objects, this code also allocates storage for a deque
// with "numElements" entries.

procedure symbol.create( numElements:uns32 );
@nodisplay; @noalignstack;
begin create;

push( eax );
xor( eax, eax ); // Init isAlloc to false.
if( esi = 0 ) then

mem.alloc( @size( symbol ));
mov( eax, esi );
mov( true, al ); // Set isAlloc true.

endif;
mov( al, this.isAlloc );

// Initialize the VMT pointer:

mov( &symbol._VMT_, this._pVMT_ );

// Allocate storage for the deque and initialize
// the array fields. Note that we are going to cheat
// and actually allocate twice as many elements as
// the user called for, so we can grow the specified
// number of elements in *either* direction (prepend or
// append).

mov( numElements, eax );
shl( 1, eax );
this.createArray( eax, @size( dequeType ));

// Position the start of the deque in the middle of the
// allocated storage so we can efficiently grow in both
// directions.

mov( numElements, eax );
intmul( @size( dequeType ), eax );
add( this.data, eax );
mov( eax, this.data );
mov( eax, this.endData );

// Runtime information:
// Begin by initializing the standard boolean variables
with
// their respective values for a deque.

mov( symbol.capabilities_c, this.capabilities );
mov( symbol.performance_c, this.performance );
mov( symbol.hierarchy_c, this.hierarchy );

// Other deque specific-initialization:

lea( eax, "deque" );
mov( eax, this.containerName );

mov( symbol.typeName_ro, eax );
mov( eax, this.typeName );


pop( eax );

end create;



#if( (symbol.capabilities_c & stl.supportsAppend_c) <> 0 )

// appendRef-
// Appends a new element to the end of the array.
// The value passed as the argument is passed by reference.
// You should use this append function when the deque element
// type is large.

method symbol.appendRef( var toAppend:dequeType );
@nodisplay; @noalignstack;
begin appendRef;

// Begin by making sure we've got enough room in the
// data array to hold the new object:

pushfd();
push( eax );
push( ecx );
push( edi );
cld();

this._append
(
#{ push( toAppend ); }#,
@size( dequeType )
);

pop( edi );
pop( ecx );
pop( eax );
popfd();

end appendRef;



// appendVal-
// Appends a new element to the end of the array.
// The value passed as the argument is passed by value.
// You should use this append function when the deque element
// type is small (typically 16 bytes or less).


method symbol.appendVal( toAppend:dequeType );
@nodisplay; @noalignstack;
begin appendVal;

// Begin by making sure we've got enough room in the
// data array to hold the new object:

pushfd();
push( eax );
push( ecx );
push( edi );
cld();

this._append
(
#{
lea( eax, toAppend );
push( eax );
}#,
@size( dequeType )
);

pop( edi );
pop( ecx );
pop( eax );
popfd();

end appendVal;

#endif



#if( (symbol.capabilities_c & stl.supportsPrepend_c) <> 0 )


// prependRef-
//
// Inserts an array element (toInsert) at the beginning of
// the array.
//
// prependRef passes toInsert by reference, so you should use
this
// function to insert large objects into the array.

method symbol.prependRef( var toPrepend:dequeType );
@nodisplay; @noalignstack;
begin prependRef;

push( eax );
push( ecx );
pushfd();
push( edi );

this._prepend
(
#{ push( toPrepend ); }#,
@size( dequeType )
);


pop( edi );
popfd();
pop( ecx );
pop( eax );

end prependRef;



// prependVal-
//
// Inserts an array element (toInsert) at the beginning of
// the array.
//
// prependVal passes toInsert by value, so you should use this
// function to insert small objects into the array.


method symbol.prependVal( toPrepend:dequeType );
@nodisplay; @noalignstack;
begin prependVal;

push( eax );
push( ecx );
pushfd();
push( edi );

this._prepend
(
#{
lea( eax, toPrepend );
push( eax );
}#,
@size( dequeType )
);


pop( edi );
popfd();
pop( ecx );
pop( eax );

end prependVal;


#endif



#if( (symbol.capabilities_c & stl.supportsInsert_c) <> 0 )


// insertRef-
//
// Inserts an array element (toInsert) at position posn within
// the array. If posn is beyond the end of the array, then
this
// code appends the value to the end of the array.
//
// insertRef passes toInsert by reference, so you should use
this
// function to insert large objects into the array.

method symbol.insertRef( var toInsert:dequeType; posn:uns32 );
@nodisplay; @noalignstack;
begin insertRef;

push( eax );
push( ecx );
pushfd();
push( edi );

intmul( @size( dequeType ), posn, eax );
add( this.data, eax );
this._insert
(
#{ push( toInsert ); }#,
eax,
@size( dequeType )
);


pop( edi );
popfd();
pop( ecx );
pop( eax );

end insertRef;



// insertVal-
//
// Inserts an array element (toInsert) at position posn within
// the array. If posn is beyond the end of the array, then
this
// code appends the value to the end of the array.
//
// insertVal passes toInsert by value, so you should use this
// function to insert small objects into the array.


method symbol.insertVal( toInsert:dequeType; posn:uns32 );
@nodisplay; @noalignstack;
begin insertVal;

push( eax );
push( ecx );
pushfd();
push( edi );

this._insert
(
#{
lea( eax, toInsert );
push( eax );
}#,
#{
intmul( @size( dequeType ), posn, eax );
add( this.data, eax );
push( eax );
}#,
@size( dequeType )
);

pop( edi );
popfd();
pop( ecx );
pop( eax );

end insertVal;


#if( (symbol.capabilities_c & stl.supportsCursor_c) <> 0 )

// insertAtRef-
//
// Inserts an array element just before the element
specified
// by the cursor passed as the second argument.
//
// insertAtRef passes toInsert by reference, so you should
use this
// function to insert large objects into the array.

method symbol.insertAtRef( var toInsert:dequeType;
cursor:cursorType );
@nodisplay; @noalignstack;
begin insertAtRef;

push( eax );
push( ecx );
pushfd();
push( edi );

this._insert
(
#{ push( toInsert ); }#,
cursor,
@size( dequeType )
);


pop( edi );
popfd();
pop( ecx );
pop( eax );

end insertAtRef;

// insertAtVal-
//
// Inserts an array element (toInsert) in front of the
item
// specified by the cursor passed as the second argument.
//
// insertAtVal passes toInsert by value, so you should use
this
// function to insert small objects into the array.


method symbol.insertAtVal( toInsert:dequeType;
cursor:cursorType );
@nodisplay; @noalignstack;
begin insertAtVal;

push( eax );
push( ecx );
pushfd();
push( edi );

this._insert
(
#{
lea( eax, toInsert );
push( eax );
}#,
cursor,
@size( dequeType )
);

pop( edi );
popfd();
pop( ecx );
pop( eax );

end insertAtVal;

#endif

#endif


#if( (symbol.capabilities_c & stl.supportsRemove_c) <> 0 )


// remove-
// Removes the nTH item from the end of the array.
//
// If n is greater than the number of items in the array,
// then this function has no effect.

method symbol.remove( n:uns32 );
@nodisplay; @noalignstack;
begin remove;

push( eax );
intmul( @size( dequeType ), n, eax );
add( this.data, eax );
this._remove( eax, @size( dequeType ));
pop( eax );

end remove;

// remove_first-
// Removes the first item from a deque.


method symbol.remove_first; @noframe;
begin remove_first;

push( eax );
this._remove( this.data, @size( dequeType ));
pop( eax );
ret();

end remove_first;


// remove_last-
// Removes the last item from a deque.

method symbol.remove_last; @noframe;
begin remove_last;

push( eax );
mov( this.endData, eax );
sub( @size( dequeType ), eax );
if( eax > this.data ) then

mov( eax, this.endData );
sub( 1, this.numElements );

endif;
pop( eax );

end remove_last;


#if( (symbol.capabilities_c & stl.supportsCursor_c) <> 0 )

// removeAt-
// Removes the item appearing at the location specified
// by the cursor passed as a parameter.

method symbol.removeAt( cursor:cursorType );
@nodisplay; @noalignstack;
begin removeAt;

push( eax );
mov( cursor, eax );
this._remove( eax, @size( dequeType ));
pop( eax );

end removeAt;

#endif

#endif


#if( (symbol.capabilities_c & stl.supportsForeach_c) <> 0 )

// The following iterators will sequence through
// all the elements of the array. On each iteration
// of the corresponding foreach loop, this iterator
// will return the *address* of the respective deque
// element.
//
// forEachElement returns items from the start to the end of
the object
// rForEachElement returns items from the end to the start.

iterator symbol.forEachElement;
@nodisplay; @noalignstack;
begin forEachElement;

push( eax );
push( edx );
mov( this.data, edx );
while( edx < this.endData ) do

push( esi );
push( edx );
mov( edx, eax );
yield();
pop( edx );
pop( esi );
add( @size( dequeType ), edx );

endwhile;
pop( edx );
pop( eax );

end forEachElement;


#endif

#if( (symbol.capabilities_c & stl.supportsrForeach_c) <> 0 )


iterator symbol.rForEachElement;
@nodisplay; @noalignstack;
begin rForEachElement;

push( eax );
push( edx );
mov( this.endData, edx );
while( edx > this.data ) do

sub( @size( dequeType ), edx );
push( esi );
push( edx );
mov( edx, eax );
yield();
pop( edx );
pop( esi );

endwhile;
pop( edx );
pop( eax );

end rForEachElement;

#endif


#if( (symbol.capabilities_c & stl.supportsCursor_c) <> 0 )

// nextCursor-
// Adjusts the cursor to point at the next element of the
// deque and returns this pointer in EAX. If this operation
// would cause the cursor to move beyond the end of the
// deque, then it just returns this.endData.

method symbol.nextCursor( var cursor:cursorType ); @noframe;
begin nextCursor;

push( eax );
mov( [esp+@offset( cursor )], eax );
push( ebx );
mov( [eax], ebx );
add( @size( dequeType ), ebx );
if( ebx > this.endData ) then

mov( this.endData, ebx );

endif;
mov( ebx, [eax] );
pop( ebx );
pop( eax );
ret();

end nextCursor;

// prevCursor-
// Adjust the cursor to point at the previous element in the
deque.
// If doing this would position the cursor before the start of
the
// array, then this just sets the cursor position to the start
of
// the array.

method symbol.prevCursor( var cursor:cursorType ); @noframe;
begin prevCursor;

push( eax );
mov( [esp+@offset( cursor )], eax );
push( ebx );
mov( [eax], ebx );
sub( @size( dequeType ), ebx );
if( ebx < this.data ) then

mov( this.data, ebx );

endif;
mov( ebx, [eax] );
pop( ebx );
pop( eax );
ret();

end prevCursor;



// beginCursor-
// Puts a pointer to the start of the deque in cursor:

method symbol.beginCursor( var cursor:cursorType ); @noframe;
begin beginCursor;

push( eax );
mov( [esp+@offset( cursor )], eax );
mov( this.data, [eax] );
pop( eax );
ret();

end beginCursor;

// endCursor-
// Puts a pointer to the end of the deque in cursor:

method symbol.endCursor( var cursor:cursorType ); @noframe;
begin endCursor;

push( eax );
mov( [esp+@offset( cursor )], eax );
mov( this.endData, [eax] );
pop( eax );
ret();

end endCursor;

// at-
// Moves the address of the data element referenced by the
// cursor passed as the argument into the EAX register.

method symbol.at( cursor:cursorType in eax ); @noframe;
begin at;

// This is a trivial function, as cursors and pointers
// to data elements are the same thing for deques.

ret();

end at;




// getAt-
// Copies the data item specified by the cursor to
// the variable specified by the second argument.
// If cursor is out of range, no action is taken.

method symbol.getAt( cursor:cursorType; var dest:dequeType );
@nodisplay; @noalignstack;
begin getAt;

pushfd();
push( edi );
cld();
mov( cursor, edi );
if( edi < this.endData ) then

push( esi );
mov( edi, esi );
mov( dest, edi );
#if( @size( dequeType ) = 1 )

movsb();

#elseif( @size( dequeType ) = 2 )

movsw();

#elseif( @size( dequeType ) = 4 )

movsd();

#else

push( ecx );
#if( (@size( dequeType ) & %11) = 0 )

mov( @size( dequeType ) div 4, ecx );
rep.movsd();

#elseif( (@size( dequeType ) & %01) = 0 )

mov( @size( dequeType ) div 2, ecx );
rep.movsw();

#else

mov( @size( dequeType ), ecx );
rep.movsb();

#endif
pop( ecx );

#endif
pop( esi );

endif;
pop( edi );
popfd();

end getAt;

// front-
// Returns a cursor value that points at the first element
// of the deque in EAX.

method symbol.front; @noframe;
begin front;

mov( this.data, eax );
ret();

end front;


// back-
// Returns a pointer to the first item *beyond* the end of
// the deque in EAX:

method symbol.back; @noframe;
begin back;

mov( this.endData, eax );
ret();

end back;


// atFront
// Compares the cursor passed as a parameter against the
// start of the deque and sets the zero flag if they
// are equal (that is, the cursor points at the start
// of the deque).

method symbol.atFront( cursor:cursorType ); @noframe;
begin atFront;

push( eax );
mov( [esp+@offset(cursor)], eax );
cmp( eax, this.data );
pop( eax );
ret();

end atFront;


// atBack
// Compares the cursor passed as a parameter against the
// end of the deque and sets the zero flag if they
// are equal (that is, the cursor points just beyond the
// end of the deque).

method symbol.atBack( cursor:cursorType ); @noframe;
begin atBack;

push( eax );
mov( [esp+@offset(cursor)], eax );
cmp( eax, this.endData );
pop( eax );
ret();

end atBack;

#endif




// getRef-
// Computes the address of element n in the array
// and returns this value in EAX. If n exceeds the
// array bounds, then at returns the address of the
// end of the array.

method symbol.getRef( n:uns32 in eax ); @noframe;
begin getRef;

intmul( @size( dequeType ), eax );
add( this.data, eax );
if( eax > this.endData ) then

mov( this.endData, eax );

endif;
ret();

end getRef;


// getVal-
// Copies the nTH data item to the specified variable.
// If n is out of range, no action is taken.

method symbol.getVal( n:uns32; var dest:dequeType );
@nodisplay; @noalignstack;
begin getVal;

pushfd();
push( edi );
cld();
intmul( @size( dequeType ), n, edi );
add( this.data, edi );
if( edi < this.endData ) then

push( esi );
mov( edi, esi );
mov( dest, edi );
#if( @size( dequeType ) = 1 )

movsb();

#elseif( @size( dequeType ) = 2 )

movsw();

#elseif( @size( dequeType ) = 4 )

movsd();

#else

push( ecx );
#if( (@size( dequeType ) & %11) = 0 )

mov( @size( dequeType ) div 4, ecx );
rep.movsd();

#elseif( (@size( dequeType ) & %01) = 0 )

mov( @size( dequeType ) div 2, ecx );
rep.movsw();

#else

mov( @size( dequeType ), ecx );
rep.movsb();

#endif
pop( ecx );

#endif
pop( esi );

endif;
pop( edi );
popfd();

end getVal;



#if( (symbol.capabilities_c & stl.supportsObjSwap_c) <> 0 )

// swapObj-
// Swaps another object with this one:

method symbol.swapObj( var obj:symbol );
@nodisplay; @nostackalign;

const
objEBX:text := "(type " + @string( symbol ) + "
[ebx])";

begin swapObj;


push( eax );
push( ebx );
push( ecx );

mov( obj, ebx );
mov( this.isAlloc, al );
mov( objEBX.isAlloc, ah );
mov( al, objEBX.isAlloc );
mov( ah, this.isAlloc );

mov( this.typeName, ecx );
mov( objEBX.typeName, eax );
mov( ecx, objEBX.typeName );
mov( eax, this.typeName );

mov( this.numElements, ecx );
mov( objEBX.numElements, eax );
mov( ecx, objEBX.numElements );
mov( eax, this.numElements );

mov( this.dataSize, ecx );
mov( objEBX.dataSize, eax );
mov( ecx, objEBX.dataSize );
mov( eax, this.dataSize );

mov( this.allocSize, ecx );
mov( objEBX.allocSize, eax );
mov( ecx, objEBX.allocSize );
mov( eax, this.allocSize );

mov( this.data, ecx );
mov( objEBX.data, eax );
mov( ecx, objEBX.data );
mov( eax, this.data );

mov( this.endData, ecx );
mov( objEBX.endData, eax );
mov( ecx, objEBX.endData );
mov( eax, this.endData );

pop( ecx );
pop( ebx );
pop( eax );

end swapObj;

#endif

#if( (symbol.capabilities_c & stl.supportsElementSwap_c) <> 0 )


method symbol.swapElements
(
var first:dequeType;
second:dequeType
);
@noframe;

begin swapElements;

push( ecx );
push( esi );
push( edi );

#if( @size( dequeType ) = 1 )

mov( [esp+@offset(first)+8], edi );
mov( [esp+@offset(first)+8], esi );
mov( [esi], cl );
mov( [edi], ch );
mov( cl, [edi] );
mov( ch, [esi] );

#elseif( @size( dequeType ) = 2 )

mov( [esp+@offset(first)+8], edi );
mov( [esp+@offset(first)+8], esi );
push( eax );
mov( [esi], ax );
mov( [edi], cx );
mov( ax, [edi] );
mov( cx, [esi] );
pop( eax );

#elseif( @size( dequeType ) = 4 )

mov( [esp+@offset(first)+8], edi );
mov( [esp+@offset(first)+8], esi );
push( eax );
mov( [esi], eax );
mov( [edi], ecx );
mov( eax, [edi] );
mov( ecx, [esi] );
pop( eax );

#elseif( @size( dequeType ) = 8 )

mov( [esp+@offset(first)+8], edi );
mov( [esp+@offset(first)+8], esi );
push( eax );
mov( [esi], eax );
mov( [edi], ecx );
mov( eax, [edi] );
mov( ecx, [esi] );
mov( [esi+4], eax );
mov( [edi+4], ecx );
mov( eax, [edi+4] );
mov( ecx, [esi+4] );
pop( eax );

#elseif( @size( dequeType ) = 16 )

mov( [esp+@offset(first)+8], edi );
mov( [esp+@offset(first)+8], esi );
push( eax );
mov( [esi], eax );
mov( [edi], ecx );
mov( eax, [edi] );
mov( ecx, [esi] );
mov( [esi+4], eax );
mov( [edi+4], ecx );
mov( eax, [edi+4] );
mov( ecx, [esi+4] );
mov( [esi+8], eax );
mov( [edi+8], ecx );
mov( eax, [edi+8] );
mov( ecx, [esi+8] );
mov( [esi+12], eax );
mov( [edi+12], ecx );
mov( eax, [edi+12] );
mov( ecx, [esi+12] );
pop( eax );

#elseif( (@size( dequeType ) & %11) = 0 )

shr( 2, ecx );
rep.movsd();

#elseif( (@size( dequeType ) & %1) = 0 )

shr( 1, ecx );
rep.movsw();

#else

rep.movsb();

#endif


pop( edi );
pop( esi );
pop( ecx );
ret( 8 );

end swapElements;

#endif


// Comparisons-
// Handle the built-in types, but require the user to
// supply comparisons for other types.

#if( (symbol.capabilities_c & stl.supportsCompare_c) <> 0 )

stl.stdCompares( dequeType )

#endif





// Set us back to the TYPE section:

type

#endif

#endmacro


end stl;



/////////////////////////////////////////////////////////////////////////////

Sample program that demonstrates the use of these templates:



program stlDemo;
#include( "stdlib.hhf" )
#include( "stl.hhf" )


#macro pair(firstType, secondType);
record
first :firstType;
second :secondType;
endrecord;
#endmacro

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//
// Some demos of the vector class:

type
pairType:pair( byte, word );

int32Vector :stl.vector( int32, supportsOutput:1, supportsCompare:1
);
strVector :stl.vector( string, supportsCompare:1 );
recVector :stl.vector( pairType, supportsAppend:0 );

int32Deque :stl.deque( int32, supportsOutput:1 );

static
b :int32Vector;
b_value :int32;
bc :int32Vector_cursor;

d :int32Deque;
dc :int32Deque_cursor;

p :p_int32Vector;

s :strVector;
sc :strVector_cursor;

r:recVector;

i0 :int32 := 0;
i1 :int32 := 1;
i2 :int32 := 2;
i4 :int32 := 4;
i8 :int32 := 8;
i16 :int32 := 16;
i32 :int32 := 32;
i64 :int32 := 64;
i128 :int32 := 128;




// Demonstrate how to write an output method for a vector.
// To work within an str.put macro invocation, we need to supply
// a "toString" method that converts the data type to a string
(allocated
// on the heap) and returns a pointer to this string in EAX. Note that
// the str.put macro will automatically deallocate the storage that
// this method creates.

method int32Vector.toString; @nodisplay; @noalignstack;
var
temp:string;
begin toString;

push( ebx );
mov( str.talloc( 1024 ), temp );
mov( this.data, ebx );
while( ebx < this.endData ) do

str.length( temp );
if( eax < 1000 ) then

str.cati32( [ebx], temp );

endif;
add( @size(int32), ebx );
if( eax < 1020 && ebx < this.endData ) then

str.cat( ", ", temp );

endif;

endwhile;
str.a_cpy( temp );
pop( ebx );

end toString;


method int32Deque.toString; @nodisplay; @noalignstack;
var
temp:string;
begin toString;

push( ebx );
mov( str.talloc( 1024 ), temp );
mov( this.data, ebx );
while( ebx < this.endData ) do

str.length( temp );
if( eax < 1000 ) then

str.cati32( [ebx], temp );

endif;
add( @size(int32), ebx );
if( eax < 1020 && ebx < this.endData ) then

str.cat( ", ", temp );

endif;

endwhile;
str.a_cpy( temp );
pop( ebx );

end toString;







begin stlDemo;

stdout.put( "int32 vector operations: " nl );

// Create a vector with 10 elements preallocated and
// add five items to the vector:

b.create( 10 );
b.appendVal( 1 );
b.appendVal( 2 );
b.appendVal( 3 );
b.appendVal( 4 );
b.appendVal( 5 );

// Print those five elements:

foreach b.forEachElement() do

stdout.put( "value=", (type int32 [eax]), nl );

endfor;

// Demonstrate sequencing through the vector using a cursor:

b.beginCursor( bc );
while( !b.atBack( bc )) do

b.at( bc ); // Puts address of item bc references into EAX
stdout.put( "Cursor'd value = ", (type int32 [eax]), nl );
b.nextCursor( bc ); // Move on to next item in vector

endwhile;

// As above, but demonstrate sequencing backwards through the
// vector.

b.endCursor( bc );
while( b.atFront( bc )) do

b.prevCursor( bc );
b.at( bc );
stdout.put( "revCursor'd value = ", (type int32 [eax]), nl );

endwhile;

// Demonstrate printing the entire vector within an stdout.put
invocation:

stdout.put( "b='", b, "'" nl );

// Destroy the int32 vector:

b.destroy();




// Demonstrate the same sort of stuff with a string vector:

stdout.put( nl "String vector operations" nl );

s.create( 10 );
s.appendVal( "string #0" );
s.appendVal( "string #1" );
s.appendVal( "string #2" );
s.appendVal( "string #3" );
s.appendVal( "string #4" );
s.appendVal( "string #5" );
foreach s.forEachElement() do

stdout.put( "value=", (type string [eax]), nl );

endfor;

s.beginCursor( sc );
while( !s.atBack( sc ) ) do

s.at( sc );
stdout.put( "Cursor'd value = ", (type string [eax]), nl );
s.nextCursor( sc );

endwhile;

while( !s.atFront( sc ) ) do

s.prevCursor( sc );
s.at( sc );
stdout.put( "revCursor'd value = ", (type string [eax]), nl );

endwhile;

s.destroy();


// Deque demo:

stdout.put( nl nl "Demonstration of deque operations: " nl nl );
d.create( 2 );
d.appendVal( 0 );
d.appendVal( 1 );
d.appendVal( 2 );
stdout.put( "d=", d, nl );

d.insertVal( -1, 0 );
d.insertVal( -2, 0 );
d.insertVal( -3, 0 );
d.insertVal( -4, 0 );
d.insertVal( -5, 0 );
d.insertVal( 10, 5 );
stdout.put( "d=", d, nl );

d.remove( 5 );
stdout.put( "d=", d, nl );

d.destroy();
d.create(2);

d.prependVal( 5 );
d.prependVal( 4 );
d.prependVal( 3 );
d.prependVal( 2 );
d.prependVal( 1 );
d.prependVal( 0 );
d.prependVal( -1 );
d.prependVal( -2 );
d.prependVal( -3 );
d.prependVal( -4 );
d.prependVal( -5 );
stdout.put( "d=", d, nl );

d.beginCursor( dc );
while( !d.atFront( dc )) do

d.at(dc);
stdout.put( "deque item: ", (type int32 [eax]), nl );
d.nextCursor( dc );

endwhile;







/////////////////////////////////////////////////
//
// Code coverage test code:
//
// Check the code paths through create and delete:

stdout.put
(
nl nl "Code Coverage Tests:" nl nl
"Testing create and delete: " nl
);
int32Vector.create( 8 );
mov( esi, p );
p.destroy();

stdout.put( "Checking appendRef: " nl );

// Test reallocation in appendRef when we exceed the vector size
specified
// in the initial create call:

b.create( 4 );
b.appendRef( i0 );
b.appendRef( i1 );
b.appendRef( i2 );
b.appendRef( i4 );
b.appendRef( i8 );
b.appendRef( i16 );
b.appendRef( i32 );

// Ditto for appendVal:

stdout.put( "Checking appendVal: ", nl );
b.appendVal( 64 );
b.appendVal( 128 );
b.appendVal( 256 );
b.appendVal( 512 );
b.appendVal( 1024 );
b.appendVal( 2048 );
b.appendVal( 8192 );
b.appendVal( 16384 );
b.appendVal( 32768 );
b.appendVal( 65536 );

stdout.put( "Checking output code:" nl );
stdout.put( "b=", b, nl );

// Same tests as above, but using insertRef/insertVal rather
// than appendRef/appendVal:

stdout.put( nl "Checking out insertRef:" nl );
b.destroy();
b.create(2);

b.appendVal( -1 );
stdout.put( "b(0) = ", b, nl );

b.insertRef( i1, 10 );
stdout.put( "b(1) = ", b, nl );

b.insertRef( i0, 0 );
stdout.put( "b(2) = ", b, nl );


b.insertRef( i2, 2 );
stdout.put( "b(3) = ", b, nl );

b.insertRef( i4, 4 );
stdout.put( "b(4) = ", b, nl );

b.insertRef( i8, 0 );
stdout.put( "b(5) = ", b, nl );

b.insertRef( i16, 0 );
stdout.put( "b(6) = ", b, nl );



stdout.put( nl "Checking out insertVal:" nl );
b.destroy();
b.create(2);

b.appendVal( -1 );
stdout.put( "b(0) = ", b, nl );

b.insertVal( 1, 10 );
stdout.put( "b(1) = ", b, nl );

b.insertVal( 0, 0 );
stdout.put( "b(2) = ", b, nl );

b.insertVal( 2, 2 );
stdout.put( "b(3) = ", b, nl );

b.insertVal( 4, 4 );
stdout.put( "b(4) = ", b, nl );

b.insertVal( 8, 0 );
stdout.put( "b(5) = ", b, nl );

b.insertVal( 16, 0 );
stdout.put( "b(6) = ", b, nl );


// Test the operation of the remove method:

stdout.put( nl "Checking out remove:" nl );

b.remove( 0 );
stdout.put( "b(0) = ", b, nl );

b.remove( 0 );
stdout.put( "b(1) = ", b, nl );

b.remove( 4 );
stdout.put( "b(2) = ", b, nl );

b.remove( 2 );
stdout.put( "b(3) = ", b, nl );

b.remove( 0 );
stdout.put( "b(4) = ", b, nl );

b.remove( 10 );
stdout.put( "b(5) = ", b, "(Should be same as b(4)" nl );


// Test the operation of the remove method:

stdout.put( nl "Checking out clear:" nl );

b.clear( );
stdout.put( "b(0) = ", b, nl );


// Some iterator tests:

stdout.put( nl "Trying forEachElement with an empty data
structure:" nl );

foreach b.forEachElement() do

stdout.put( "iterator = ", (type uns32 [eax]), nl );

endfor;

stdout.put( nl "Trying rForEachElement with an empty data
structure:" nl );

foreach b.rForEachElement() do

stdout.put( "iterator = ", (type uns32 [eax]), nl );

endfor;

b.appendVal( 1 );
b.appendVal( 2 );
b.appendVal( 3 );
b.appendVal( 4 );


stdout.put( nl "Trying forEachElement with 4 items:" nl );

foreach b.forEachElement() do

stdout.put( " ", (type uns32 [eax]) );

endfor;

stdout.put( nl "Trying rForEachElement with 4 items:" nl );

foreach b.rForEachElement() do

stdout.put( " ", (type uns32 [eax]) );

endfor;



// Testing the cursor routines:

stdout.put
(
nl nl
"Trying out beginCursor, getAt, nextCursor, and atBack"
nl
);
b.beginCursor( bc );
while( !b.atBack( bc )) do

b.getAt( bc, b_value );
mov( bc, eax );
stdout.put( "getAt(", eax, ") yields ", b_value, nl );
b.nextCursor( bc );

endwhile;

stdout.put( nl "Inserting at end:" nl );
b.insertAtVal( 10, bc );
stdout.put( "b=", b, nl );

stdout.put( nl "Inserting at beginning:" nl );
b.beginCursor( bc );
b.insertAtRef( i128, bc );
stdout.put( "b=", b, nl );

stdout.put( nl "Inserting two spots from end:" nl );
b.endCursor( bc );
b.prevCursor( bc );
b.prevCursor( bc );
b.insertAtVal( 22, bc );
stdout.put( "b=", b, nl );

stdout.put( nl "Testing atFront" nl );
while( !b.atFront( bc )) do

b.getAt( bc, b_value );
mov( bc, eax );
stdout.put( "getAt(", eax, ") yields ", b_value, nl );
b.prevCursor( bc );

endwhile;

stdout.put( nl "Testing front, back, and RemoveAt:" nl );
b.front();
mov( eax, bc );
b.removeAt( bc );
stdout.put( "Remove Front =", b, nl );

b.back();
mov( eax, bc );
b.prevCursor( bc );
b.removeAt( bc );
stdout.put( "Remove Back =", b, nl );

b.prevCursor( bc );
b.prevCursor( bc );
b.removeAt( bc );
stdout.put( "Remove 2 Back =", b, nl );


// Miscellaneous tests:

b.getSize();
stdout.put( nl "Testing getSize, #elements=", (type uns32 eax), nl
);

b.getVal( 2, b_value );
stdout.put( nl "Testing getVal(2) = ", b_value, nl );





stdout.put( nl nl nl "Traits for b:" nl nl );


#for( hName in stl.hierarchyNames )

mov( b.hierarchy, eax );
test( @text( "stl."+hName+"_c"), eax );
setne( al );
stdout.put( hName, " = ", (type boolean al), nl );

#endfor
stdout.newln();

#for( cName in stl.capabilityNames )

mov( b.capabilities, eax );
test( @text( "stl."+cName+"_c"), eax );
setne( al );
stdout.put( cName, " = ", (type boolean al), nl );

#endfor
stdout.newln();

#for( pName in stl.performanceNames )

mov( b.performance, eax );
test( @text( "stl."+pName+"_c"), eax );
setne( al );
stdout.put( pName, " = ", (type boolean al), nl );

#endfor
stdout.newln();

stdout.put( "typeName=", b.typeName, nl );
stdout.put( "containerName=", b.containerName, nl );






stdout.put( nl nl nl "Traits for r:" nl nl );
r.create( 4 );

#for( hName in stl.hierarchyNames )

mov( r.hierarchy, eax );
test( @text( "stl."+hName+"_c"), eax );
setne( al );
stdout.put( hName, " = ", (type boolean al), nl );

#endfor
stdout.newln();

#for( cName in stl.capabilityNames )

mov( r.capabilities, eax );
test( @text( "stl."+cName+"_c"), eax );
setne( al );
stdout.put( cName, " = ", (type boolean al), nl );

#endfor
stdout.newln();

#for( pName in stl.performanceNames )

mov( r.performance, eax );
test( @text( "stl."+pName+"_c"), eax );
setne( al );
stdout.put( pName, " = ", (type boolean al), nl );

#endfor
stdout.newln();
stdout.put( "typeName=", r.typeName, nl );
stdout.put( "containerName=", r.containerName, nl );


end stlDemo;

.



Relevant Pages

  • Re: Understanding Macros
    ... I have the newly created macro toolbar on the template. ... There is no panel on the left of the screen that opens that looks like ...
    (microsoft.public.word.docmanagement)
  • RE: word error
    ... your Normal.dot template may be infected with a macro virus. ... How to remove WLL add-ins and templates in the Word and Office Startup ... Startup folder is causing the problem, ...
    (microsoft.public.office.misc)
  • Re: How do I set up automatically suceeding numbers in Word?
    ... So I'll suggest that for each one you take the name of the template ... then the corresponding names inside the macro could be "Contract A ... "Jay Freedman" wrote: ... digits to display -- and not the number itself. ...
    (microsoft.public.word.docmanagement)
  • Re: How to control macros
    ... values for initalising a UserForm using document variables I'd be willing to ... When I initially create a document from a template, I set a flag to indicate ... As for a "global macro template", I don't have one - at least not for the ...
    (microsoft.public.word.vba.general)
  • Re: Changing default window properties
    ... What do YOU mean by "Global Template"? ... more than one macro of the same ... not willing to mess up my AutoOpen/New macros by testing this. ...
    (microsoft.public.mac.office.word)