PreviousNext

Arrays

Array parameters provide an efficient way to pass contiguous blocks of data with little application overhead. The stubs take care of serializing and reassembling the passed data transparently to the application. When an application is interested in passing an entire buffer or some contiguous portion of a buffer synchronously - so that all of the data is made available to the receiver at the same time - arrays provide the most efficient mechanism. Pipes provide no advantage unless the data is to be processed asynchronously.

Arrays may be passed as RPC parameters, but, as in the case of other RPC data, the stubs need to know the size of data to be marshalled. The simple solution is to declare arrays of fixed size in the IDL. This can be inefficient however, since array sizes may vary at runtime, and since not all data in an array may need to be passed on every call. Therefore, IDL provides a variety of field attributes (max_is, min_is, size_is, last_is, first_is, and length_is) to permit the size and bounds of the marshalled data to be determined at runtime. Note that passing a pointer to an array is not any more efficient as a way to deal with the problem of varying array sizes. Remember that marshalling a pointer requires marshalling the pointer's referent, so the array data will be marshalled anyway. Note also that the IDL language does not permit declaring a pointer to a varying array.

The size of the array data marshalled is determined in one of two ways. In a conformant array, the size of the array is not declared in the IDL declaration, and one of the max_is or size_is attributes is used to determine the size of the marshalled data at runtime. In a varying array, the size of the array is declared in the .idl file, but one or more of the other field attributes determines what range of elements is actually marshalled. Arrays may be both conformant and varying at the same time.

Each field attribute is associated with some variable whose value is known at runtime. The scope of this association is within either an operation declaration or a structure declaration. That is, when the array is a parameter of an operation, the field attribute variables must also be parameters of the same operation. Similarly, when the array is a member of a structure, the field attribute variables must be members of the same structure.

The following samples show a series of array declarations using some of the many possible forms:

/* An array of fixed size */

typedef char char5array[5];
typedef char5array *char5ptr;

void array_test1(
[in] handle_t handle,
[in] char5ptr a_pointer,
[out] error_status_t *status);

/* A conformant array: the size is determined at runtime */

void array_test2(
[in] handle_t handle,
[in] unsigned32 size,
[in, size_is(size)] char an_array[],
[out] error_status_t *status);

/*
* A varying array: the portion of the array transmitted is
* determined at runtime
*/

typedef struct{
unsigned32 first;
unsigned32 length;
[first_is(first), length_is(length)] char array[0..10];
}v_struct;

void array_test3(
[in] handle_t handle,
[in] v_struct v_array,
[out] error_status_t *status);

/*
* A conformant and varying array: both size and the portion
* transmitted are determined at runtime
*/

typedef struct{
unsigned32 size;
unsigned32 first;
unsigned32 length;
[size_is(size), first_is(first), length_is(length)] char array[0..*];
}cv_struct;

void array_test4(
[in] handle_t handle,
[in] cv_struct *cv_array,
[out] error_status_t *status);

The examples show clearly how field attribute variables are related to array declarations.

In the second operation declaration, a conformant array is declared as an operation parameter (an_array), so that the field attribute variable (size) must also be a parameter of the interface. In the third and fourth operations, varying and conformant-varying arrays are declared within structures, so that the field attribute variables (size, first, and length) must also be members of the same structures.

The server manager sample code to test these declarations is as follows:

void array_test1(
handle_t handle,
char5ptr a_pointer,
error_status_t *status
)
{
printf("Array test 10);
printf("%c %c 0, (*a_pointer)[0],(*a_pointer)[1]);
*status = error_status_ok;
};

void array_test2(
handle_t handle,
unsigned32 size,
idl_char an_array[],
error_status_t *status
)
{
unsigned32 i;

printf("Array test 20);
for ( i = 0; i < size; i++)
{
printf("%c ",an_array[i]);
printf("0);
}
*status = error_status_ok;
}

void array_test3(
handle_t handle,
v_struct v_array,
error_status_t *status
)
{
unsigned32 i;

printf("Array test 30);
for ( i = v_array.first; i < v_array.first + v_array.length; i++)
printf("subscript %i value %c0, i, v_array.array[i]);
*status = error_status_ok;
}

void array_test4(
handle_t handle,
cv_struct *cv_array,
error_status_t *status
)
{
unsigned32 i;

printf("Array test 40);
for (i = (*cv_array).first; i < (*cv_array).first + (*cv_array).length; i++)
printf("subscript %i value %c0, i, (*cv_array).array[i]);
*status = error_status_ok;
}

The client sample code is as follows:

char5array fixed_array = {'a','b','c','d','e'};

v_struct varying_array = {3,4,{'a','b','c','d','e','f','g','h','i','j'}};

struct {
unsigned32 size;
unsigned32 first;
unsigned32 length;
char array[10];
}cv_array = {10, 4, 5, {'a','b','c','d','e','f','g','h','i','j'}};

array_test1(binding_h, &fixed_array, &status);

array_test2(binding_h, 5, fixed_array, &status);

array_test3(binding_h, varying_array, &status);

array_test4(binding_h, &cv_array, &status);

The server output will look like this:

Array test 1
a b
Array test 2
a
b
c
d
e
Array test 3
subscript 3 value d
subscript 4 value e
subscript 5 value f
subscript 6 value g
Array test 4
subscript 4 value e
subscript 5 value f
subscript 6 value g
subscript 7 value h
subscript 8 value i

Note that for the last test, the declared structure contains a conformant and varying array. The C language does not provide any intrinsic support for conformant arrays, and the actual IDL-generated header declaration for the type cv_struct looks as follows:

typedef struct {
unsigned32 size;
unsigned32 first;
unsigned32 length;
idl_char array[1];
} cv_struct;

The declared structure contains an array only one element in length. When creating an instance of this type, the application must allocate a data structure of the correct size, either statically, as in the sample client code (the data item cv_array), or dynamically. The recipient of such a data structure (in this case, the server manager code), can then determine the actual size of the marshalled data by examining relevant field attribute variables (in this case, the structure's size member). Note also that IDL requires a structure containing a conformant array to be passed by reference; that is, as a pointer referent.

Conformant and varying arrays provide a way to pass blocks of contiguous data of varying sizes and ranges. However, there is no intrinsic mechanism for passing sparse arrays efficiently. Applications may, however, supply their own mechanisms for compressing and passing large, sparse arrays using the [transmit_as] mechanism.

There are a number of complications that can arise when using arrays of pointers. For example, an [out] or [in,out] conformant array of pointers, accompanied by an [out] or [in,out] field attribute variable, could potentially be of any size when returned to a caller. For [ref] pointers, which may not be NULL, the client must therefore ensure that all possible returned pointers in such an array actually point to valid storage. You can easily avoid such complications by sticking to the more straightforward array usages discussed here. However, if you find your application needs to use arrays in some more esoteric way, you should refer to the Application Environment Specification/Distributed Computing - RPC Volume, Chapter 4, which contains a complete set of array and pointer usage rules.