$include (..\lib\compStch.ext) /* *============================================================================ * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE * * Permission to use for any purpose, modify, copy, and make enhancements * and derivative works of the software is granted if attribution is given to * R.M. Gillmore, dba the ACME Software Deli, as the author * * While the ACME Software Deli does not work for money, there is nonetheless * a significant amount of work involved. The ACME Software Deli maintains the * rights to all code written, though it may be used and distributed as long as * the following conditions are maintained. * * 1. The copyright statement at the top of each code block is maintained in * your distribution. * 2. You do not identify yourself as the ACME Software Deli * 3. Any changes made to the software are sent to the ACME Software Deli *============================================================================ */ ArrayHandlerModule: do; $if not noID declare IDString (*) byte data ( '@(#)arrayMgr.p86 $Author: rmgillmore $ $Date:: 2025-05-04 19:35:39#$:', 0 ); $endif $include (..\lib\comnDefs.ext) $set ( arrayManagerSource ) $include (..\lib\arrayMgr.ext) $include (..\lib\sysCalls.ext) $include (..\lib\fileIO.ext) $include (..\lib\numStrs.ext) $include (..\lib\string.ext) $include (..\lib\errCode.ext) $include (..\lib\mem.ext) $include (..\lib\ptrMath.ext) $if LOGGING $include (..\lib\logger.ext) $endif /* * Every array has these characteristics. */ declare arrayRecordType literally ' structure ( arrayPtr pointer, elementSize word, lastAccessed integer, numberElements integer, maximumElements integer )'; declare ARRAY_GRANULARITY integer data ( 16 ); elementSizeProcedure: procedure ( elementType ) word reentrant; declare elementType numberReportType, elementSize word; declare byteVar byte, wordVar word, dwordVar dword, pointerVar pointer, stringVar pointer, booleanVar boolean, integerVar integer; /* * This switch statement corresponds to the values specified in * numStrs.ext for data type (provided here for ease of * reference, and not for definition) * * declare byteType literally '0', * byteDecimalType literally '1', * wordType literally '2', * wordDecimalType literally '3', * dwordType literally '4', * dwordDecimalType literally '5', * pointerType literally '6', * stringType literally '7', * booleanType literally '8', * integerType literally '9'; */ do case ( elementType ); elementSize = size( byteVar ); /* byteType */ elementSize = size( byteVar ); /* byteDecimalType */ elementSize = size( wordVar ); /* wordType */ elementSize = size( wordVar ); /* wordDecimalType */ elementSize = size( dwordVar ); /* dwordType */ elementSize = size( dwordVar ); /* dwordDecimalType */ elementSize = size( pointerVar ); /* pointerType */ elementSize = size( pointerVar ); /* stringType */ elementSize = size( booleanVar ); /* booleanType */ elementSize = size( integerVar ); /* integerType */ end; /* do case element type */ return ( elementSize ); end elementSizeProcedure; accessor: procedure ( thisPtr, index, elementPtr ) reentrant; declare thisPtr pointer, this based thisPtr arrayRecordType, index integer, arrayBase pointer, byteArray based arrayBase (*) byte, wordArray based arrayBase (*) word, dwordArray based arrayBase (*) dword, elementPtr pointer, byteElement based elementPtr byte, wordElement based elementPtr word, dwordElement based elementPtr dword; arrayBase = this.arrayPtr; if ( this.elementSize = size( byteElement ) ) then do; byteElement = byteArray( index ); end; else if ( this.elementSize = size( wordElement ) ) then do; wordElement = wordArray( index ); end; else do; dwordElement = dwordArray( index ); end; this.lastAccessed = index; end accessor; manipulator: procedure ( thisPtr, index, elementPtr ) reentrant; declare thisPtr pointer, this based thisPtr arrayRecordType, index integer, arrayBase pointer, byteArray based arrayBase (*) byte, wordArray based arrayBase (*) word, dwordArray based arrayBase (*) dword, elementPtr pointer, byteElement based elementPtr byte, wordElement based elementPtr word, dwordElement based elementPtr dword; arrayBase = this.arrayPtr; if ( this.elementSize = size( byteElement ) ) then do; byteArray( index ) = byteElement; end; else if ( this.elementSize = size( wordElement ) ) then do; wordArray( index ) = wordElement; end; else do; dwordArray( index ) = dwordElement; end; end manipulator; makeRoomAt: procedure ( thisPtr, index ); declare thisPtr pointer, this based thisPtr arrayRecordType, index integer; declare elementAddress pointer, destinationAddress pointer, numberToMove word; if ( index < this.maximumElements ) then do; /* * move all of the elements starting at index by one element */ /* * calculate start address */ elementAddress = dwtp( ptdw( this.arrayPtr ) + dword( this.elementSize * unsign( index ) ) ); destinationAddress = dwtp( ptdw( elementAddress ) + dword( this.elementSize ) ); /* * determine how many bytes to move */ numberToMove = unsign( this.numberElements - index ) * this.elementSize; /* * move all of them as needed (backwards so that we don't destroy as * we move) */ call movrb( elementAddress, destinationAddress, numberToMove ); end; end makeRoomAt; /* ******************************************************** * Constructors ******************************************************** */ createArray: procedure ( numberElements, elementType ) pointer public; declare numberElements integer, elementType numberReportType; declare basePointer pointer, base based basePointer arrayRecordType, arraySize word; /* * an array object contains the address of the first element, * the number of array elements allocated, the number of array * elements actually in use, and the type of element. We should * think of it this way: * * arrayPtr pointer, * elementType numberReportType, * lastAccessed integer, * numberElements integer, * maximumElements integer */ /* * allocate memory for the object (without the actual array space) */ basePointer = malloc( size( base ) ); if ( basePointer <> NULL ) then do; base.elementSize = elementSizeProcedure( elementType ); /* * We have successfully allocated the area for the object header, * figure out how much space the array will take, then allocate * space for it, too */ if ( numberElements < 1 ) then numberElements = 1; arraySize = unsign( numberElements ) * base.elementSize; base.arrayPtr = malloc( arraySize ); if ( base.arrayPtr <> NULL ) then do; /* * we have successfully allocated space for the array, fill the array * with zeroes, and initialize the elements of the object header */ base.maximumElements = numberElements; base.numberElements = 0; base.lastAccessed = -1; end; else do; /* * we were unable to allocate memory for the array, so release what * we allocated for the header */ declare freeReturned integer; freeReturned = free( basePointer ); basePointer = NULL; end; end; return ( basePointer ); end createArray; deleteArray: procedure ( basePtr ) public; declare basePtr pointer, this based basePtr arrayRecordType; declare freeReturned integer; /* * if there is no basePtr, then there will also be no arrayPtr. However, * the converse can not be assumed */ if ( basePtr <> NULL ) then do; if ( this.arrayPtr <> NULL ) then do; freeReturned = free( this.arrayPtr ); end; freeReturned = free( basePtr ); end; end deleteArray; /* ******************************************************** * Accessors ******************************************************** */ arrayLength: procedure ( thisPtr ) integer public; declare thisPtr pointer, this based thisPtr arrayRecordType; return this.numberElements; end arrayLength; reportArray: procedure ( thisPtr ) public; declare thisPtr pointer, this based thisPtr arrayRecordType; declare reportString ( 128 ) byte, eachElementIsThisBig word; $if LOGGING call addToLog( DEBUG, __FILE__, __LINE__, @( 'this.arrayPtr : %p', 0 ), this.arrayPtr ); call addToLog( DEBUG, __FILE__, __LINE__, @( 'this.elementSize : %x', 0 ), this.elementSize ); call addToLog( DEBUG, __FILE__, __LINE__, @( 'this.lastAccessed : %d', 0 ), this.lastAccessed ); call addToLog( DEBUG, __FILE__, __LINE__, @( 'this.numberElements : %d', 0 ), this.numberElements ); call addToLog( DEBUG, __FILE__, __LINE__, @( 'this.maximumElements : %d', 0 ), this.maximumElements ); reportBlock: do; declare tempString ( 5 ) byte, index integer, elementIsThisSize word, arrayBase pointer, byteArray based arrayBase (*) byte, wordArray based arrayBase (*) word, dwordArray based arrayBase (*) dword, byteVar byte, wordVar word, dwordVar dword; arrayBase = this.arrayPtr; reportString( 0 ) = 0; if ( this.numberElements > 0 ) then do; do index = 0 to ( this.numberElements - 1 ); if ( size( byteArray( 0 ) ) = this.elementSize ) then call addToLog( DEBUG, __FILE__, __LINE__, @( '[%d]:%bx', 0 ), index, byteArray( index ) ); else if ( size( wordArray( 0 ) ) = this.elementSize ) then call addToLog( DEBUG, __FILE__, __LINE__, @( '[%d]:%x', 0 ), index, wordArray( index ) ); else if ( size( dwordArray( 0 ) ) = this.elementSize ) then call addToLog( DEBUG, __FILE__, __LINE__, @( '[%d]:%lx', 0 ), index, dwordArray( index ) ); end; end; end reportBlock; $else return; $endif end reportArray; getElement: procedure ( basePtr, index, elementPtr, statusPtr ) public; declare basePtr pointer, this based basePtr arrayRecordType, index integer, elementPtr pointer, statusPtr pointer, status based statusPtr word; status = EXIT_SUCCESS; if ( ( index >= 0 ) and ( index < this.numberElements ) ) then do; call accessor( basePtr, index, elementPtr ); end; /* valid index */ else status = CASE_ERROR; end getElement; nextElement: procedure ( basePtr, elementPtr, statusPtr ) public; declare basePtr pointer, this based basePtr arrayRecordType, elementPtr pointer, statusPtr pointer, status based statusPtr word; call getElement( basePtr, this.lastAccessed + 1, elementPtr, statusPtr ); end nextElement; /* ******************************************************** * Manipulators ******************************************************** */ rewindArray: procedure ( basePtr ) public; declare basePtr pointer, this based basePtr arrayRecordType; this.lastAccessed = -1; end rewindArray; setElement: procedure ( basePtr, index, elementPtr, statusPtr ) public; declare basePtr pointer, this based basePtr arrayRecordType, index integer, elementPtr pointer, statusPtr pointer, status based statusPtr word; status = EXIT_SUCCESS; if ( ( index < 0 ) or ( index >= this.numberElements ) ) then do; status = CASE_ERROR; call printStringAsciiZ( @( 'set element failed: insufficient number elements', Cr, Lf, Lf, 0 ) ); end; else if ( ( index >= 0 ) and ( index < this.numberElements ) ) then do; call manipulator( basePtr, index, elementPtr ); end; end setElement; insertElementAt: procedure ( thisPtr, index, elementValuePtr, statusPtr ) public; declare thisPtr pointer, this based thisPtr arrayRecordType, index integer, elementValuePtr pointer, statusPtr pointer, status based statusPtr word; /* * addElement is a special case of insertElementAt(), so the logic here * is really the same as addElement EXCEPT that we insert the element * at the specified index, and not at the end of the array as in addElement */ status = EXIT_SUCCESS; /* * We will add to the array as long as there is room for the element. If there is no room, * we will extend the array, and then add */ if ( this.numberElements >= this.maximumElements ) then do; /* * force the number of elements to be valid, then adjust the desired number of elements * and figure out how large each element is */ declare newBasePtr pointer; this.maximumElements = this.maximumElements + ARRAY_GRANULARITY; /* * allocate memory for the "extended" array */ newBasePtr = malloc( unsign( this.maximumElements ) * this.elementSize ); if ( newBasePtr <> NULL ) then do; /* * memory allocation was successful, so copy from the old to the new memory space, * delete the old space, and store the address to the new space in the current * instance of the array object */ declare freeReturned integer; call movb( this.arrayPtr, newBasePtr, unsign( this.numberElements ) * this.elementSize ); freeReturned = free( this.arrayPtr ); this.arrayPtr = newBasePtr; end; else do; /* * memory allocation not successful; do not copy, and do nothing else */ this.maximumElements = this.numberElements; status = INSUFFICIENT_MEMORY; end; end; if ( status = EXIT_SUCCESS ) then do; /* * now do what we came here to do */ call makeRoomAt( thisPtr, index ); call manipulator( thisPtr, index, elementValuePtr ); this.numberElements = this.numberElements + 1; end; end insertElementAt; appendElement: procedure ( thisPtr, elementValuePtr, statusPtr ) public; declare thisPtr pointer, this based thisPtr arrayRecordType, elementValuePtr pointer, statusPtr pointer, status based statusPtr word; call insertElementAt( thisPtr, this.numberElements, elementValuePtr, statusPtr ); end appendElement; detachElement: procedure ( basePtr, elementValuePtr, statusPtr ) public; declare basePtr pointer, this based basePtr arrayRecordType, elementValuePtr pointer, statusPtr pointer; call getElement( basePtr, this.numberElements - 1, elementValuePtr, statusPtr ); this.numberElements = this.numberElements - 1; end detachElement; pushElement: procedure ( basePtr, elementValuePtr, statusPtr ) public; declare basePtr pointer, this based basePtr arrayRecordType, elementValuePtr pointer, statusPtr pointer; call appendElement( basePtr, elementValuePtr, statusPtr ); end pushElement; popElement: procedure ( basePtr, elementValuePtr, statusPtr ) public; declare basePtr pointer, this based basePtr arrayRecordType, elementValuePtr pointer, statusPtr pointer; call detachElement( basePtr, elementValuePtr, statusPtr ); end popElement; removeElementAt: procedure ( basePtr, index ) public; declare basePtr pointer, this based basePtr arrayRecordType, index integer, loopIndex integer, element dword; do loopIndex = index to ( this.numberElements - 1 ); call accessor( basePtr, loopIndex + 1, @element ); call manipulator( basePtr, loopIndex, @element ); end; this.numberElements = this.numberElements - 1; end removeElementAt; end ArrayHandlerModule;