Previous: Shared Arrays, Up: Arrays


5.6.7.4 Accessing Arrays from C

Arrays, especially uniform numeric arrays, are useful to efficiently represent large amounts of rectangularily organized information, such as matrices, images, or generally blobs of binary data. It is desirable to access these blobs in a C like manner so that they can be handed to external C code such as linear algebra libraries or image processing routines.

While pointers to the elements of an array are in use, the array itself must be protected so that the pointer remains valid. Such a protected array is said to be reserved. A reserved array can be read but modifications to it that would cause the pointer to its elements to become invalid are prevented. When you attempt such a modification, an error is signalled.

(This is similar to locking the array while it is in use, but without the danger of a deadlock. In a multi-threaded program, you will need additional synchronization to avoid modifying reserved arrays.)

You must take care to always unreserve an array after reserving it, also in the presence of non-local exits. To simplify this, reserving and unreserving work like a dynwind context (see Dynamic Wind): a call to scm_array_get_handle can be thought of as beginning a dynwind context and scm_array_handle_release as ending it. When a non-local exit happens between these two calls, the array is implicitely unreserved.

That is, you need to properly pair reserving and unreserving in your code, but you don't need to worry about non-local exits.

These calls and other pairs of calls that establish dynwind contexts need to be properly nested. If you begin a context prior to reserving an array, you need to unreserve the array before ending the context. Likewise, when reserving two or more arrays in a certain order, you need to unreserve them in the opposite order.

Once you have reserved an array and have retrieved the pointer to its elements, you must figure out the layout of the elements in memory. Guile allows slices to be taken out of arrays without actually making a copy, such as making an alias for the diagonal of a matrix that can be treated as a vector. Arrays that result from such an operation are not stored contiguously in memory and when working with their elements directly, you need to take this into account.

The layout of array elements in memory can be defined via a mapping function that computes a scalar position from a vector of indices. The scalar position then is the offset of the element with the given indices from the start of the storage block of the array.

In Guile, this mapping function is restricted to be affine: all mapping functions of Guile arrays can be written as p = b + c[0]*i[0] + c[1]*i[1] + ... + c[n-1]*i[n-1] where i[k] is the kth index and n is the rank of the array. For example, a matrix of size 3x3 would have b == 0, c[0] == 3 and c[1] == 1. When you transpose this matrix (with transpose-array, say), you will get an array whose mapping function has b == 0, c[0] == 1 and c[1] == 3.

The function scm_array_handle_dims gives you (indirect) access to the coefficients c[k].

Note that there are no functions for accessing the elements of a character array yet. Once the string implementation of Guile has been changed to use Unicode, we will provide them.

— C Type: scm_t_array_handle

This is a structure type that holds all information necessary to manage the reservation of arrays as explained above. Structures of this type must be allocated on the stack and must only be accessed by the functions listed below.

— C Function: void scm_array_get_handle (SCM array, scm_t_array_handle *handle)

Reserve array, which must be an array, and prepare handle to be used with the functions below. You must eventually call scm_array_handle_release on handle, and do this in a properly nested fashion, as explained above. The structure pointed to by handle does not need to be initialized before calling this function.

— C Function: void scm_array_handle_release (scm_t_array_handle *handle)

End the array reservation represented by handle. After a call to this function, handle might be used for another reservation.

— C Function: size_t scm_array_handle_rank (scm_t_array_handle *handle)

Return the rank of the array represented by handle.

— C Type: scm_t_array_dim

This structure type holds information about the layout of one dimension of an array. It includes the following fields:

ssize_t lbnd
ssize_t ubnd
The lower and upper bounds (both inclusive) of the permissible index range for the given dimension. Both values can be negative, but lbnd is always less than or equal to ubnd.
ssize_t inc
The distance from one element of this dimension to the next. Note, too, that this can be negative.

— C Function: const scm_t_array_dim * scm_array_handle_dims (scm_t_array_handle *handle)

Return a pointer to a C vector of information about the dimensions of the array represented by handle. This pointer is valid as long as the array remains reserved. As explained above, the scm_t_array_dim structures returned by this function can be used calculate the position of an element in the storage block of the array from its indices.

This position can then be used as an index into the C array pointer returned by the various scm_array_handle_<foo>_elements functions, or with scm_array_handle_ref and scm_array_handle_set.

Here is how one can compute the position pos of an element given its indices in the vector indices:

          ssize_t indices[RANK];
          scm_t_array_dim *dims;
          ssize_t pos;
          size_t i;
          
          pos = 0;
          for (i = 0; i < RANK; i++)
            {
              if (indices[i] < dims[i].lbnd || indices[i] > dims[i].ubnd)
                out_of_range ();
              pos += (indices[i] - dims[i].lbnd) * dims[i].inc;
            }
     
— C Function: ssize_t scm_array_handle_pos (scm_t_array_handle *handle, SCM indices)

Compute the position corresponding to indices, a list of indices. The position is computed as described above for scm_array_handle_dims. The number of the indices and their range is checked and an approrpiate error is signalled for invalid indices.

— C Function: SCM scm_array_handle_ref (scm_t_array_handle *handle, ssize_t pos)

Return the element at position pos in the storage block of the array represented by handle. Any kind of array is acceptable. No range checking is done on pos.

— C Function: void scm_array_handle_set (scm_t_array_handle *handle, ssize_t pos, SCM val)

Set the element at position pos in the storage block of the array represented by handle to val. Any kind of array is acceptable. No range checking is done on pos. An error is signalled when the array can not store val.

— C Function: const SCM * scm_array_handle_elements (scm_t_array_handle *handle)

Return a pointer to the elements of a ordinary array of general Scheme values (i.e., a non-uniform array) for reading. This pointer is valid as long as the array remains reserved.

— C Function: SCM * scm_array_handle_writable_elements (scm_t_array_handle *handle)

Like scm_array_handle_elements, but the pointer is good for reading and writing.

— C Function: const void * scm_array_handle_uniform_elements (scm_t_array_handle *handle)

Return a pointer to the elements of a uniform numeric array for reading. This pointer is valid as long as the array remains reserved. The size of each element is given by scm_array_handle_uniform_element_size.

— C Function: void * scm_array_handle_uniform_writable_elements (scm_t_array_handle *handle)

Like scm_array_handle_uniform_elements, but the pointer is good reading and writing.

— C Function: size_t scm_array_handle_uniform_element_size (scm_t_array_handle *handle)

Return the size of one element of the uniform numeric array represented by handle.

— C Function: const scm_t_uint8 * scm_array_handle_u8_elements (scm_t_array_handle *handle)
— C Function: const scm_t_int8 * scm_array_handle_s8_elements (scm_t_array_handle *handle)
— C Function: const scm_t_uint16 * scm_array_handle_u16_elements (scm_t_array_handle *handle)
— C Function: const scm_t_int16 * scm_array_handle_s16_elements (scm_t_array_handle *handle)
— C Function: const scm_t_uint32 * scm_array_handle_u32_elements (scm_t_array_handle *handle)
— C Function: const scm_t_int32 * scm_array_handle_s32_elements (scm_t_array_handle *handle)
— C Function: const scm_t_uint64 * scm_array_handle_u64_elements (scm_t_array_handle *handle)
— C Function: const scm_t_int64 * scm_array_handle_s64_elements (scm_t_array_handle *handle)
— C Function: const float * scm_array_handle_f32_elements (scm_t_array_handle *handle)
— C Function: const double * scm_array_handle_f64_elements (scm_t_array_handle *handle)
— C Function: const float * scm_array_handle_c32_elements (scm_t_array_handle *handle)
— C Function: const double * scm_array_handle_c64_elements (scm_t_array_handle *handle)

Return a pointer to the elements of a uniform numeric array of the indicated kind for reading. This pointer is valid as long as the array remains reserved.

The pointers for c32 and c64 uniform numeric arrays point to pairs of floating point numbers. The even index holds the real part, the odd index the imaginary part of the complex number.

— C Function: scm_t_uint8 * scm_array_handle_u8_writable_elements (scm_t_array_handle *handle)
— C Function: scm_t_int8 * scm_array_handle_s8_writable_elements (scm_t_array_handle *handle)
— C Function: scm_t_uint16 * scm_array_handle_u16_writable_elements (scm_t_array_handle *handle)
— C Function: scm_t_int16 * scm_array_handle_s16_writable_elements (scm_t_array_handle *handle)
— C Function: scm_t_uint32 * scm_array_handle_u32_writable_elements (scm_t_array_handle *handle)
— C Function: scm_t_int32 * scm_array_handle_s32_writable_elements (scm_t_array_handle *handle)
— C Function: scm_t_uint64 * scm_array_handle_u64_writable_elements (scm_t_array_handle *handle)
— C Function: scm_t_int64 * scm_array_handle_s64_writable_elements (scm_t_array_handle *handle)
— C Function: float * scm_array_handle_f32_writable_elements (scm_t_array_handle *handle)
— C Function: double * scm_array_handle_f64_writable_elements (scm_t_array_handle *handle)
— C Function: float * scm_array_handle_c32_writable_elements (scm_t_array_handle *handle)
— C Function: double * scm_array_handle_c64_writable_elements (scm_t_array_handle *handle)

Like scm_array_handle_<kind>_elements, but the pointer is good for reading and writing.

— C Function: const scm_t_uint32 * scm_array_handle_bit_elements (scm_t_array_handle *handle)

Return a pointer to the words that store the bits of the represented array, which must be a bit array.

Unlike other arrays, bit arrays have an additional offset that must be figured into index calculations. That offset is returned by scm_array_handle_bit_elements_offset.

To find a certain bit you first need to calculate its position as explained above for scm_array_handle_dims and then add the offset. This gives the absolute position of the bit, which is always a non-negative integer.

Each word of the bit array storage block contains exactly 32 bits, with the least significant bit in that word having the lowest absolute position number. The next word contains the next 32 bits.

Thus, the following code can be used to access a bit whose position according to scm_array_handle_dims is given in pos:

          SCM bit_array;
          scm_t_array_handle handle;
          scm_t_uint32 *bits;
          ssize_t pos;
          size_t abs_pos;
          size_t word_pos, mask;
          
          scm_array_get_handle (&bit_array, &handle);
          bits = scm_array_handle_bit_elements (&handle);
          
          pos = ...
          abs_pos = pos + scm_array_handle_bit_elements_offset (&handle);
          word_pos = abs_pos / 32;
          mask = 1L << (abs_pos % 32);
          
          if (bits[word_pos] & mask)
            /* bit is set. */
          
          scm_array_handle_release (&handle);
     
— C Function: scm_t_uint32 * scm_array_handle_bit_writable_elements (scm_t_array_handle *handle)

Like scm_array_handle_bit_elements but the pointer is good for reading and writing. You must take care not to modify bits outside of the allowed index range of the array, even for contiguous arrays.