previous chapter contents page top page next chapter

FixedList

June 10, 1992
last updated January 21, 1994

Defined in List.Def 
Inherits from AbstractList



Class Description

A fixed list is an ordered collection of data elements, all of which are the same, fixed size. Class FixedList defines the generic behavior of all such lists and provides the foundation for a large family of more specialized list classes, as shown in the hierarchy above. Note that the data elements belonging to the list need not be descended from class Object. (The subclass ObjectList represents a list whose elements are all objects.)

Remember that if the documentation and the software (especially the definition files) disagree, always trust the software.

Related Classes

Class AbstractList defines the fundamentals of list structure. Class FixedList has many subclasses, the most notable of which is class ObjectList. You will probably also find class DataList useful.

Using a FixedList Object

If your program needs to use a fixed list, you can create one by specifying it in your object definition file. In practice, however, you'll rarely create any direct instances of class FixedList. Instead, you'll usually use one of its many system-defined subclasses: for example, class TaskList represents a chronologically sorted list of "to-do" tasks for the Datebook, and ObjectStack is an arbitrary list of objects accessed in last-in-first-out order. If none of the built-in subclasses is appropriate for your purposes, you can define a subclass of your own.

Programming Information

Instantiate: rarely
Subclass: rarely
Call its methods: sometimes

Class FixedList is a subclass of AbstractList, the abstract base class for all list objects. Like all lists, a fixed list keeps its elements arranged in a definite sequential order. Each list element can be accessed by its index, which is an integer value indicating the element's location within the list. The index numbering is one-based: that is, the first element in the list is number one, which is the way most humans think.

The distinguishing characteristic of a fixed list is that all of its elements must be the same size; you specify the size when you create the list. In addition to the fields it inherits from AbstractList (the element count and the index of the current element), a fixed list has an extra field, elemSize, that gives the size of each element in bytes.

Methods you might call

Class FixedList has the following methods that you might Call:

Method Description
Initialization
Init Initialize fields
List structure
Stride Get size of each element in bytes
TextInfo Get information about list in text form
Element access
ElementAt Get element at a given index
ElementValue Get value of a given element
ValueAt Get value of element at a given index
Adding and removing elements
AddElemAt Add an element at a given index
RemoveAt Remove element at a given index
ReplaceElemAt Replace element at a given index
Iterating
EachElem Apply a function to each element
EachElemAfter Apply a function to each element after a particular index
EachExtraField Apply a function to each field of extra data
Searching
FindElemAfter Search for an element, beginning at a given index
Low-level object management
Stabilize Stabilize storage format
Compact Free unneeded memory

Description of fields

Class FixedList defines just one field:

Field Type Description
Inherited from AbstractList
length Unsigned Number of elements in list

Method Descriptions

Initialization

Init

overrides Init (*params: NewFixedListParameters)

Call: rarely
Override: often

When a new object is created, the system calls Init to allow the new object to set up its fields. The params parameter points to a structure containing initial values for the object's fields. In the case of a fixed list, the structure looks like this:

typedef struct
    {
    Parameters   header;
    ulong        initialEntries;
    } NewFixedListParameters;

FixedList_Init first calls its inherited init, then initializes length to params->initialEntries. Finally, FixedList_Init allocates space for the number of elements specified by params->initialEntries and initializes all elements to zero.

You should override Init if your list subclass defines any additional fields that should be initialized when a new instance is created, or if it needs to initialize its inherited fields to values other than the ones shown here. Be sure to include a call to InheritedInit so that the inherited fields will be properly set up.

See the description of Object_Init for more information.

List Structure

Stride

attribute Stride: Unsigned, readOnly;
// operation Stride(): Unsigned

Call: rarely
Override: sometimes

Call Stride to find out the size of a list's elements in bytes. By default, Stride returns 4, since the default element is a 4-byte object ID. Override Stride if your list element size is different, or use the provided subclass DataList. Class DataList adds a stride field and overrides Init to set up the list using the stride.

TextInfo

overrides TextInfo(index: UnsignedShort; VAR info: String; abbrevFlag: 
Boolean)

Call: sometimes
Override: sometimes

TextInfo provides a way for objects to return information about themselves in text form. The index parameter identifies the specific information being requested; a text representation of this information is returned in the info parameter.

FixedList_GetTextInfo recognizes one special value for the index parameter:

#define kCount 230

If index is equal to kCount, the method returns a string representing the list's current element count; otherwise, it simply passes the request on to its superclass by calling InheritedGetTextInfo.

Element Access

ElementAt

operation ElementAt (index: Unsigned; returnElement: Pointer)

Call: sometimes
Override: rarely

Call ElementAt to get the element at a given index position in a list.

The index parameter designates the position of the desired element within the list. If index is less than one or greater than the length of the list, the operation fails with the exception cannotFindIndex.

The parameter returnElement should point to a variable of the appropriate type for the elements of the list; ElementAt copies the value of the requested list element into this variable. The identity of the list's current element remains unchanged.

For example, suppose that a list named testList contains the following elements:

Index Element
1 $01 23
2 $12 34
3 $44 72
4 $9A A9

Here are some sample calls:

ElementAt(testList, 2, &theElem); /* Puts $1234 in theElem*/
ElementAt(testList, 4, &aLong); /* Puts $9AA9 in aLong */
ElementAt(testList, 1, &theElem);/* Puts $0123 in theElem */

ElementValue

operation ElementValue (theElement: Pointer): Unsigned

Call: rarely
Override: rarely

Call ElementValue to get the value of a list element. theElement is a pointer to the element; ElementValue returns its value as an unsigned long (4-byte) integer.

This method is intended primarily for the common cases in which the list's element size is 1, 2, or 4 bytes. For 1- and 2-byte elements, the value is padded to 4 bytes with leading zeroes. Longer elements are truncated to their first 4 bytes; this might be useful or it might be nonsense, depending on circumstances and the phase of the moon. In the (highly unusual) 3-byte case, the value is truncated to its first 2 bytes and then padded to 4 with leading zeroes. This is more likely to be nonsense than useful, but you never know.

ValueAt

operation ValueAt (index: Unsigned): Unsigned; noFail

Call: rarely 
Override: rarely

Call ValueAt to get the value of the element at a given index position in a list. The identity of the list's current element remains unchanged.

The index parameter designates the position of the desired element within the list. If index is less than one or greater than the length of the list, the operation fails with the exception cannotFindIndex.

ValueAt returns a 4-byte value. If the list's element size is less than this, the value is padded to 4 bytes with leading zeroes.

Adding and Removing Elements

AddElemAt

operation AddElemAt (index: Unsigned; newElement: Pointer), safe;

Call: sometimes
Override: rarely

Call AddElemAt to add an element to a list at a given index position. newElement is a pointer to the value to be added; a new element with that value is inserted in the list at the position indicated by index. If index is less than one or greater than the length of the list plus one, the operation fails with the exception cannotFindIndex.

The element previously at the specified index position and all elements following it in the list are moved back one position. If necessary, the list is enlarged to make room for the new element.

For example, if a list named testList contains the following elements

Index Element
1 $01 23
2 $44 72
3 $9A A9

and you execute this code

newValue = 0x1234;
AddElemAt (testList, 2, &newValue);

testList now looks like this:

Index Element
1 $01 23
2 $12 34
3 $44 72
4 $9A A9

RemoveAt

operation RemoveAt (index: Unsigned)

Call: sometimes
Override: rarely

Call RemoveAt to remove the element at a given index position from a list. The index parameter designates the position of the element to be removed. If index is less than one or greater than the length of the list, the operation fails with the exception cannotFindIndex.

All elements following the one being removed are moved forward one position in the list. For example, suppose you have a list named testList that contains the following elements

Index Element
1 $01 23
2 $12 34
3 $44 72
4 $9A A9

If you execute this code

RemoveAt (testList, 3);

testList looks like this:

Index Element
1 $01 23
2 $12 34
3 $9A A9

The extra space previously occupied by the last element of the list is not deallocated, but is cleared to zeroes; since the list's element count is reduced by one, this space becomes inaccessible as part of the list. (It may eventually be reclaimed via the Compact method.)

ReplaceElemAt

operation ReplaceElemAt (index: Unsigned; theElement: Pointer)

Call: sometimes
Override: rarely

Call ReplaceElemAt to store a new value into the list element at a given index position. theElement is a pointer to the value to be stored; this becomes the new value of the list element at the position indicated by index. The previous value of the list element, if any, is lost; other elements of the list are not affected.

For example, if a list named testList contains the following elements

Index Element
1 $01 23
2 $66 55
3 $44 72
4 $9A A9

and you execute this code

newValue = 0x1234;
ReplaceElemAt (testList, 2, &newValue);

testList now looks like this:

Index Element
1 $01 23
2 $12 34
3 $44 72
4 $9A A9

If index is less than one, the operation fails with the exception cannotFindIndex. If index is greater than the length of the list, the list is enlarged to include a new element at that index; any intervening elements are initialized to zero. For example, if you now execute the statements

newValue = 0xFEDC;
ReplaceElemAt (testList, 7, &newValue);

testList has the following contents:

Index Element
1 $01 23
2 $12 34
3 $44 72
4 $9A A9
5 $00 00
6 $00 00
7 $FE DC

Iterating

EachElem

operation EachElem (theProc: EachElemFunction; params: Pointer)

Call: sometimes
Override: rarely

Call EachElem to apply a function repeatedly to every element of a list.

theProc is a pointer to the function. The type of this pointer is defined as

typedef Boolean (*EachElemFunction)(void *element, Parameters *);

so the iterative function itself should be declared like this:

Boolean IterProc (void *elemPtr, void *params)

EachElem iterates through all the elements of the list, passing each one in turn as the first parameter to your iterative function. If you want to pass any additional parameters to the function, place them in a data structure and pass a pointer to this structure as the params parameter to EachElem. EachElem hands this same pointer on unchanged as the second parameter to your iterative function.

EachElem keeps calling the iterative function as long as the function returns true and there are elements remaining in the list that haven't been processed. When the iterative function returns false, EachElem stops iterating and exits, even if there are still unprocessed elements remaining. This is useful if you want to stop iterating when your iterative function fulfills some condition, such as successfully completing a search.

The following example uses EachElem to report the value of each element in a list to the log file:

Boolean
Action (void *elemPtr, void *params)
   {
    /* Report element's value to Log file */

    
    Log (NumToStr(*elemPtr));
    return true;
   }
Method void
HelloWorld_ReportList (ObjectID self, ObjectID theList)
   {
    #pragma unused (self)
    
    EachElem(theList, (EachElemProcPtr)Action, nil);
    /* Call Action repeatedly for each element of the list */
   }

EachElemAfter

operation EachElemAfter (theProc: EachElemFunction; params: Pointer; 
afterIndex: Unsigned): Unsigned;

Call: sometimes
Override: rarely

Call EachElemAfter to apply a function repeatedly to every element of a list after the element at the given index. The interface of the EachElemAfter method is very similar to that of EachElem, and indeed to every "each" call you'll see.

EachExtraField

overrides EachExtraField

Call: sometimes
Override: rarely

Call EachExtraField to apply a function repeatedly to every field of extra data in your fixed list.

Searching

FindElemAfter

operation FindElemAfter (theElement: Pointer; afterIndex: Unsigned): 
Unsigned; noFail

Call: rarely
Override: rarely

Call FindElemAfter to search a list for a given element value, beginning at a specified index position. theElement is a pointer to the desired value; afterIndex is the index position at which to begin the search. FindElemAfter returns the index of the first element following this index whose value is equal to the requested value. If no such element exists, the result is zero.

Notice that the search begins at the next element following the specified index, so a zero value for afterIndex searches the entire list. If afterIndex is less than zero or greater than the length of the list, the operation fails with the exception cannotFindIndex. If afterIndex is equal to the length of the list, FindElemAfter returns a zero result, indicating that the requested value could not be found after the last list element.

For example, assume that testList contains the following elements:

Index Element
1 $01 23
2 $12 34
3 $44 72
4 $9A A9

Here are some example calls:

toFind = 0x4472;
result = FindElemAfter (testList, &toFind, 2); /* Returns 3 */
result = FindElemAfter (testList, &toFind, 3); /* Returns 0 */
result = FindElemAfter (testList, &toFind, 5); /* Returns 0 */
result = FindElemAfter (testList, &toFind, 0); /* Returns 3 */
result = FindElemAfter (testList, &toFind, -1); /* Fails */       

Low-Level Object Management

Stabilize

overrides Stabilize

Call: sometimes
Override: sometimes

When a new object is created and initialized, the object might go through a period of instability while its initial values are set up. When this initial unstable period is over, the system calls Stabilize to notify the object that the system will no longer be making frequent changes to it. This allows the object, if it chooses, to convert to a form that occupies less memory but isn't as easy to modify.

FixedList_Stabilize makes sure that the list's extra data area is just big enough to hold all of its current elements. This eliminates any unused space that might have been created as a result of operations such as SetLength, SetEmpty, RemoveAt, or RemoveElem; see the descriptions of those methods for more information.

If you define a subclass of FixedList, you should override Stabilize if you want to provide a way for objects of your subclass to convert to a tighter form. Be sure your new method calls InheritedStabilize to take advantage of the stabilization method described here. See the description of Object_Stabilize for more information.

Compact

overrides Compact

Call: rarely
Override: sometimes

The system calls Compact when there's a critical shortage of memory, asking each object to try to free up memory by releasing unused space or removing data that can be recreated, such as caches or indices.

FixedList_Compact makes sure that the list's extra data area is just big enough to hold all of its current elements. This eliminates any unused space that might have been created as a result of operations such as SetLength, SetEmpty, RemoveAt, or RemoveElem; see the descriptions of those methods for more information.

If you define a subclass of FixedList, you should override Compact if you want to provide a way for objects of your subclass to free unneeded memory. Be sure your new method calls InheritedCompact to take advantage of the compaction method described here. See the description of Object_Compact for more information.