Vectors are variable length blocks of memory referenced by machine words tagged as vectors. The first machine word of a vector block contains a fixnum representing the vector length; this means that the first word of a vector is tagged as a fixnum.
|------------------------|-------------| reference to vector
heap pointer vector tag
|------------------------|-------------| vector first word
number of words fixnum tag
After the length machine word comes the data area: an array of machine words, one for each vector slot; slot indexes are zero–based.
0 1 2 3 4 5 6 7
|---|---|---|---|---|---|---|---|---| vector memory block
^ |...............................|
| slots = data area
length
fixnum
A vector is capable of holding at most a number of values equal to the
return value of greatest-fixnum. The fixnum representing the
vector length, interpreted as raw signed integer, also represents the
number of bytes in the data area.
A fixnum representing the index of slot N, interpreted as raw signed integer, also represents the offset in bytes of the firts byte of the slot with respect the beginning of the data area.
Vector objects are allocated on the heap; to perform the allocation we compute the whole size of the data area, add to it room for meta data and finally compute the aligned block size:
ikpcb * pcb = ik_the_pcb();
long length = the_number_of_items;
long requested_size = wordsize * length;
long block_size = disp_vector_data + requested_size;
long align_size = IK_ALIGN(block_size);
ikptr vec = ik_safe_alloc(pcb, align_size) | vector_tag;
ik_safe_alloc() returns an ikptr value representing the
aligned pointer, having the 3 least significant bits set to zero;
we add to it the vector tag (an integer value fitting in 3 bits)
which allows to recognise vectors among all the other built in objects.
We have to explicitly store the vector length in the memory block as a fixnum, so usually a full allocation looks like this:
ikptr
ika_vector_alloc (ikpcb * pcb, long number_of_items)
{
long align_size;
ikptr s_len;
ikptr s_vec;
s_len = IK_FIX(number_of_items);
align_size = IK_ALIGN(disp_vector_data + s_len);
s_vec = ik_safe_alloc(pcb, align_size) | vector_tag;
IK_REF(s_vec, off_vector_length) = s_len;
return s_vec;
}
notice how we exploit the fact that the fixnum representing the number of elements equals the number of bytes in the data area needed to hold such elements.
The allocation operations described above leaves the data area uninitialised: its content is undefined. This is bad if the garbage collector moves the newly built vector before the elements are initialised to a correct Scheme value. The following function resets the data area to a vector of zero fixnums:
ikptr
ikrt_vector_clean (ikptr s_vec)
{
ikptr s_len = IK_VECTOR_LENGTH_FX(s_vec);
memset((char*)(long)(s_vec + off_vector_data), 0, s_len);
return s_vec;
}
To fill a vector of 3 items with fixnums we should do:
ikptr s_vec = the_vector;
IK_REF(s_vec, off_vector_data + 0 * wordsize) = IK_FIX(10);
IK_REF(s_vec, off_vector_data + 1 * wordsize) = IK_FIX(20);
IK_REF(s_vec, off_vector_data + 2 * wordsize) = IK_FIX(30);
to retrieve the item at index 2 we do:
ikptr s_vec = the_vector;
ikptr s_item = IK_REF(s_vec, off_vector_data + 2 * wordsize);
and to retrieve the vector length:
ikptr s_vec = the_vector;
ikptr s_len = IK_REF(s_vec, off_vector_length);
long length = IK_UNFIX(s_len);
Displacement of length. The number of bytes to add to an untagged pointer to vector to get the pointer to the first byte in the word holding the vector length as fixnum.
Displacement of data area. The number of bytes to add to an untagged pointer to vector to get the pointer to the first byte in the data area.
An integer to add to a tagged
ikptrreference to retrieve the pointer to the first byte of the vector length as fixnum.
An integer to add to a tagged
ikptrvector reference to retrieve the pointer to the first byte of the data area.
Return a fixnum representing the number of items in the vector vec.
Return an integer representing the number of items in the vector vec.
Evaluate to the item at index idx in the vector vec. A use of this macro can appear both as operand and as left–side of an assignment; example:
long idx = the_index; ikptr s_vec = the_vector; ikptr fx; IK_ITEM(s_vec, idx) = IK_FIX(10); fx = IK_ITEM(s_vec, idx);
Allocate, initialise and return a new vector object capable of holding the specified number of items. Leave the data area uninitialised.
Allocate, initialise and return a new vector object capable of holding the specified number of items. Initialise the data area so that all the items are set to the fixnum zero.
Return true if vec is a reference to a vector object. This predicate tests that vec is tagged as vector reference and that the first machine word in the referenced memory block is a fixnum.