Descriptor Arrays

A descriptor array is a mechanism which allows descriptors to be aggregated in a convenient way.

Introduction

Descriptor arrays build on the behaviour supplied by the Dynamic Arrays API and provides normal array operations for inserting, appending, deleting, and accessing elements.

There are two types of descriptor array, based on the way data is represented by the array:

  • an array whose elements consist of non-modifiable pointer descriptors.

  • an array whose elements consist of memory pointers.

Either array can be used to represent descriptor data. The difference between them is based on the way they are implemented, and this determines which one is most suitable for a given situation.

NOTE: All array classes are provided in variants for narrow and wide characters (for example, CDesC8Array and CDesC16Array). These concrete types can be used directly, but it is usual to use typedefs (for example, CDesCArray) that are conditionally defined to map to the wide or narrow characters depending on the build. Only the conditional types are used below.

Descriptor arrays has three key concepts - descriptor array protocol (MDesC16Array), general descriptor array (CDesC16Array) and pointer descriptor array (CPtrC16Array).

Descriptor array protocol

This array defines an interface implemented by all descriptor array classes, and hence provides a degree of polymorphism. It provides a count function, and can return a TPtrC for an indexed element.

The interface is defined by MDesCArray.

General descriptor array

This array accepts elements of any descriptor type. For each descriptor added, it creates a new heap descriptor (HBufC) and copies the contents into it.

The base class is CDesCArray. Derived classes provide storage in flat arrays (CDesCArrayFlat) and segmented arrays (CDesCArraySeg).

Pointer descriptor array

This array holds only TPtrC descriptor elements, that is, the descriptor type that points to data stored elsewhere. The data pointed to by the TPtrC descriptors is not copied or moved.

The pointer descriptor array is CPtrCArray. It implements MDesCArray, and can be used polymorphically with general descriptor arrays.

Array of non-modifiable pointer descriptor elements

The array is supplied in two variants:

The array is also supplied as a build independent type, CPtrCArray. This is used whenever the descriptor elements are used to represent text strings. By using the build independent type, the appropriate variant, either 16-bit or 8-bit, is selected at build time depending on whether the _UNICODE macro has been defined or not.

Binary data always requires the 8-bit variant, regardless of the build, and this should be explicitly used in program code.

Explicit use of the 16-bit variant is rare.

The elements of this type of array consist of non-modifiable pointer descriptors. These pointer descriptors represent the data of the descriptors added to the array. The following diagram illustrates this. The diagram is also true for TPtrC8 and TPtrC16.

Figure 1. Array of non-modifiable pointer descriptor elements

NOTE: delete() and reset() removes the non-modifiable pointer descriptors from the array but does not delete the data or descriptors that they point to.

Array of pointer elements

The elements of this type of array consist of pointers to heap descriptors.

When a descriptor is added to this type of array, a heap descriptor is allocated, taking its data from the supplied descriptor. The pointer to this heap descriptor is added as an array element. The following diagram illustrates this. The diagram is also true for HBufC8 and HBufC16.

Figure 2. Array of pointer elements

There are two implementations of the array, one using a flat buffer and the other using a segmented buffer.

The flat buffer implementation is supplied in two variants:

The segmented buffer implementation is supplied in two variants:

  • the 16-bit variant implemented using a segmented buffer, a CDesC16ArraySeg, constructed from TDesC16 types.

  • the 8-bit variant implemented using a segmented buffer, a CDesC8ArraySeg, constructed from TDesC8 types.

Both array implementations are also supplied as build independent types, CDesCArrayFlat and CDesCArraySeg. These are used whenever the descriptors are used to represent text strings. By using the build independent types, the appropriate variants, either 16-bit or 8-bit, are selected at build time depending on whether the _UNICODE macro has been defined or not.

Binary data always requires the 8-bit variants, regardless of the build, and this should be explicitly used in program code.

Explicit use of the 16-bit variants is rare.

NOTE: delete() and reset() removes the pointers from the array and also deletes the heap descriptors that they point to.

Type of array to be used

The advantages of using one type over the other are subtle.

When using an array of non-modifiable pointer descriptors, the data represented by each TPtrC exists independently of the TPtrC itself. The memory required by the array is that required to contain the TPtrC elements. The data represented by the TPtrC descriptors is not copied or moved. On the other hand, that same data must be guaranteed to remain in memory if the array is to have any purpose.

When using an array of pointers, a new heap descriptor is allocated for each descriptor to be added to the array. This increases the total memory requirements of the array. On the other hand, each array element is smaller because the size of a pointer is slightly smaller than the size of a TPtrC object. The original descriptor data can also be safely discarded once it has been added to the array.

This type also has the advantage that there is no commitment to a concrete descriptor type.

Relationship between descriptor array classes

The following diagram illustrates the relationship between the descriptor array concrete classes and the base classes which support them.

Figure 3. The class relationships for CDesCArrayFlat & CDesCArraySeg

Figure 4. The class relationships for CPtrCArray

Copying Descriptor Arrays

An array of non-modifiable pointer descriptors, a CPtrCArray type, provides a function which can copy elements from any descriptor array.

The source descriptor array must be one which satisfies the protocol defined by the MDesCArray mixin class. Add the new TPtrC elements to the CPtrCArray array to represent the source data.

The implementation of the copy does not and cannot depend on the type of the source descriptor array,that is, whether it is a CPtrCArray type or a CDesCArray type. However, the following diagram shows the effect of the copy operation based on the concrete type of the source array.

Figure 5. Copying descriptor arrays

8-Bit Variant, 16-Bit Variant and Build Independence

Descriptor arrays are supplied in two variants:

  • the 16-bit variant for 16-bit descriptors. These descriptors are used for handling Unicode strings and double byte valued data.

  • the 8-bit variant for 8-bit variant descriptors. These descriptors are used for handling non-Unicode strings and single byte valued data. (binary data).

Descriptor arrays are also supplied as build independent types. These are used for descriptors which are used to represent text strings.

By using build independent types, the appropriate variant, either 16-bit or 8-bit, is selected at build time depending on whether the _UNICODE macro has been defined or not.

Binary data always requires the 8-bit variant regardless of the build, and it must be explicitly used in program code. Explicit use of the 16-bit variant is rare. With a few exceptions, the behaviour of both 8-bit and 16-bit variants is the same.

The MDesCArray mixin class

The MDesCArray class is a mixin which defines a protocol for:

  • returning the number of elements in a descriptor array

  • returning a non-modifiable pointer descriptor, a TPtrC type representing a specific indexed element.

The use of the mixin permits a degree of polymorphism amongst the descriptor array classes. It permits the number of descriptor array elements to be returned and a TPtrC type for a specific descriptor array element to be returned without knowing the specific concrete descriptor array type being accessed.

MDesCArray example

The following code fragments illustrate how the MDesCArray mixin class is used to return:

  • the number of descriptor elements in a descriptor array.

  • a TPtrC representing a specific indexed descriptor element.

The code uses the build independent forms but the code is equally valid while using the explicit 8-bit or 16-bit variants.

In this case, CDesCArrayFlat, CDesCArraySeg and CPtrCArray can be handled by the single function foo().

 ...
 CDesCArrayFlat* descflat = new( ELeave ) CDesCArrayFlat( 4 );
 CDesCArraySeg*  descseg  = new( ELeave ) CDesCArraySeg( 4 );
 CPtrCArray*     ptrc     = new( ELeave ) CPtrCArray( 4 );
 ...
 ... // add descriptor elements to all three arrays
 ...
 foo( descflat );
 foo( descseg );
 foo( ptrc );
 ...
void foo( MDesCArray* anArray )
    {
    ..
    TInt  somenumber = anArray->MdcaCount();
    TPtrC someptrc   = anArray->MdcaPoint( someindexvalue );
    ..
    }