For interworking with external C code, Guile provides an API to allow C code to access the elements of a Scheme array. In particular, for uniform numeric arrays, the API exposes the underlying uniform data as a C array of numbers of the relevant type.
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 signaled.
(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, even in the presence of non-local exits. If a non-local exit can happen between these two calls, you should install a dynwind context that releases the array when it is left (see Dynamic Wind).
In addition, array reserving and unreserving must be properly paired. For instance, 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
k
th 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.
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.
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.
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.
size_t
scm_array_handle_rank (scm_t_array_handle *handle)
¶Return the rank of the array represented by handle.
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.
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; }
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 appropriate error is signaled for invalid
indices.
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.
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 signaled when the array can not store val.
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.
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.
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
.
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.
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.
const scm_t_uint8 *
scm_array_handle_u8_elements (scm_t_array_handle *handle)
¶const scm_t_int8 *
scm_array_handle_s8_elements (scm_t_array_handle *handle)
¶const scm_t_uint16 *
scm_array_handle_u16_elements (scm_t_array_handle *handle)
¶const scm_t_int16 *
scm_array_handle_s16_elements (scm_t_array_handle *handle)
¶const scm_t_uint32 *
scm_array_handle_u32_elements (scm_t_array_handle *handle)
¶const scm_t_int32 *
scm_array_handle_s32_elements (scm_t_array_handle *handle)
¶const scm_t_uint64 *
scm_array_handle_u64_elements (scm_t_array_handle *handle)
¶const scm_t_int64 *
scm_array_handle_s64_elements (scm_t_array_handle *handle)
¶const float *
scm_array_handle_f32_elements (scm_t_array_handle *handle)
¶const double *
scm_array_handle_f64_elements (scm_t_array_handle *handle)
¶const float *
scm_array_handle_c32_elements (scm_t_array_handle *handle)
¶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.
scm_t_uint8 *
scm_array_handle_u8_writable_elements (scm_t_array_handle *handle)
¶scm_t_int8 *
scm_array_handle_s8_writable_elements (scm_t_array_handle *handle)
¶scm_t_uint16 *
scm_array_handle_u16_writable_elements (scm_t_array_handle *handle)
¶scm_t_int16 *
scm_array_handle_s16_writable_elements (scm_t_array_handle *handle)
¶scm_t_uint32 *
scm_array_handle_u32_writable_elements (scm_t_array_handle *handle)
¶scm_t_int32 *
scm_array_handle_s32_writable_elements (scm_t_array_handle *handle)
¶scm_t_uint64 *
scm_array_handle_u64_writable_elements (scm_t_array_handle *handle)
¶scm_t_int64 *
scm_array_handle_s64_writable_elements (scm_t_array_handle *handle)
¶float *
scm_array_handle_f32_writable_elements (scm_t_array_handle *handle)
¶double *
scm_array_handle_f64_writable_elements (scm_t_array_handle *handle)
¶float *
scm_array_handle_c32_writable_elements (scm_t_array_handle *handle)
¶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.
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);
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.