Next: , Previous: , Up: objects   [Index]


13.9 Struct objects

A data structure is a variable–length block of memory referenced by machine words tagged as vectors; the first machine word of a structure is a reference to its structure type descriptor (STD), which is itself a data structure; the subsequent words, if any, are the fields of the structure. A block of memory is a data structure if and only if: a reference to it is tagged as vector and its first word is tagged as vector.

|----------------|----------| reference to structure
  heap pointer    vector tag

|----------------|----------| first word of structure
  heap pointer    vector tag    = reference to STD
                                = reference to structure

The whole memory block layout of a struct with 5 fields is as follows:

|-----|------|------|------|------|------|
  STD  field0 field1 field2 field3 field4

fields are indexed starting at zero.

The type descriptor of the type descriptors is the return value of base-rtd at the Scheme level and the structure referenced by the field base_rtd in the process control block (PCB). Such base type descriptor is built at Vicare’s startup.

The graph of references for a structure and its type descriptor is as follows:

     STD ref
    |-------|---------------| structure instance
        |
 ---<---
|
|    STD ref
 -->|-------|---------------| struct type descriptor
        |
 ---<---
|
|    STD ref
+-->|-------|---------------| base struct type descriptor
|       |
 ---<---

the struct–type descriptor of the base struct–type descriptor is the base type descriptor itself.

About R6RS records and their types:

The graph of references for a R6RS record and its type descriptor is as follows:

     RTD ref
    |-------|---------------| R6RS record instance
        |
 ---<---
|
|    STD ref
 -->|-------|---------------| R6RS record-type descriptor
        |                      = struct instance of type <rtd>
 ---<---
|
|    STD ref
+-->|-------|---------------| <rtd> struct-type descriptor
        |
 ---<---
|
|    STD ref
+-->|-------|---------------| base struct-type descriptor
|       |
 ---<---

A struct–type descriptor (STD) is a fixed–length block of memory composed of 6 machine words interpreted as follows:

  1. A reference to the base STD.
  2. A fixnum representing the number of machine words in the structure minus 1; this is the number of fields in the structure excluding the reference to the STD. This value is 6 in the base STD.
  3. Scheme symbol representing the name of this structure type.
  4. Scheme list of symbols representing the names of fields in structures of this type.
  5. False or a reference to closure used to print to a Scheme port the structures of this type. This is the printer function.
  6. Scheme symbol used as unique identifier for this type.
  7. False or closure object to be used as destructor for instances of this type (see Finalisation of structures).

Type descriptors are best defined at the Scheme level using the functions from the library (vicare system structs) (see define-struct). To instantiate a structure at the C language level we should write a C function accepting the type descriptor as argument, and have the Scheme code hand the descriptor to it. For example, at the Scheme level we do:

(define-struct timeval
  (tv_sec tv_usec))

(define (gettimeofday)
  (foreign-call "ikrt_posix_gettimeofday"
                (struct-type-descriptor timeval)))

and at the C level we do:

ikptr_t
ikrt_posix_gettimeofday (ikptr_t s_rtd, ikpcb_t * pcb)
{
  /* build and return an instance of "timeval" */
}

Basic operations

Data structure objects are allocated on the heap; to perform the allocation we compute the whole size of the structure:

ikpcb_t *  pcb           = ik_the_pcb();
ikuword_t  num_of_fields = ...;
ikuword_t  block_size    = \
  disp_record_data + wordsize * num_of_fields;
ikuword_t  align_size    = IK_ALIGN(block_size);
ikptr_t s_stru = ik_safe_alloc(pcb, align_size) | record_tag;

ik_safe_alloc() returns an ikptr_t value representing the aligned pointer, having the 3 least significant bits set to zero; we add to it the record tag (an integer value fitting in 3 bits) which allows to recognise records among all the other built in objects.

We have to explicitly store a reference to the STD in the first machine word of the structure, so a full allocation looks like this:

ikptr_t
ika_struct_alloc_no_init (ikpcb_t * pcb, ikptr_t s_std)
{
  ikptr_t    s_num_of_fields = IK_STD_LENGTH(s_std);
  ikuword_t  num_of_fields   = IK_UNFIX(s_num_of_fields);
  ikuword_t  align_size      = \
    IK_ALIGN(disp_record_data + num_of_fields * wordsize);
  ikptr_t    s_stru;
  pcb->root9 = &s_std;
  {
    s_ stru = ik_safe_alloc(pcb, align_size) | record_tag;
  }
  pcb->root9 = NULL;
  IK_STRUCT_STD(s_stru) = s_std;
  return s_stru;
}

notice that we do not need to call ik_signal_dirt_in_page_of_pointer() for s_stru because s_std is an older object.

The allocation operation described above leaves the data area uninitialised: its content is undefined. This is bad if the garbage collector moves the newly built record before the elements are initialised to a correct Scheme value.

Structure fields are identified at the C level by a zero–based index; to store a value in field 2 of a structure we do:

ikptr_t  s_stru  = the_structure;
ikptr_t  s_field = the_field;

IK_REF(s_stru, off_record_data + 2 * wordsize) = the_field;
IK_SIGNAL_DIRT(pcb, \
  IK_PTR(s_stru, off_record_data + 2 * wordsize));

or, shorter:

ikptr_t  s_stru  = the_structure;
ikptr_t  s_field = the_field;

IK_FIELD(s_stru, 2) = the_field;
IK_SIGNAL_DIRT(pcb, IK_FIELD_PTR(s_stru, 2));

and to retrieve a the value of field 2 we do:

ikptr_t  s_stru  = the_structure;
ikptr_t  s_field;

s_field = IK_REF(s_stru, off_record_data + 2 * wordsize);

or, shorter:

ikptr_t  s_stru  = the_structure;
ikptr_t  s_field;

s_field = IK_FIELD(s_stru, 2);
Preprocessor Symbol: record_mask
Preprocessor Symbol: record_tag

Integer values used to tag and recognise ikptr_t references to structures. record_mask isolates the tag bits from an ikptr_t and record_tag represents the tag bits. These values are the same used for vectors.

Preprocessor Symbol: disp_record_rtd

Displacement of the STD from the beginning of a structure block. The number of bytes to add to an untagged pointer to structure to get the pointer to the first byte in the word holding the STD.

Preprocessor Symbol: disp_record_data

Displacement of data area. The number of bytes to add to an untagged pointer to structure to get the pointer to the first byte of the first field in the data area.

Preprocessor Symbol: off_record_rtd

An integer to add to a tagged ikptr_t structure reference to retrieve the pointer to the first byte of the STD.

Preprocessor Symbol: off_record_data

An integer to add to a tagged ikptr_t structure reference to retrieve the pointer to the first byte of the first field of the structure.

Preprocessor Symbol: disp_rtd_rtd

Displacement of the base STD from the beginning of an STD block. The number of bytes to add to an untagged pointer to STD to get the pointer to the first byte of the reference to STD.

Preprocessor Symbol: disp_rtd_name
Preprocessor Symbol: disp_rtd_length
Preprocessor Symbol: disp_rtd_fields
Preprocessor Symbol: disp_rtd_printer
Preprocessor Symbol: disp_rtd_symbol

Displacements of the fields of an STD.

Preprocessor Symbol: rtd_size

The total number of bytes in a memory block holding an STD.

Preprocessor Symbol: off_rtd_rtd
Preprocessor Symbol: off_rtd_name
Preprocessor Symbol: off_rtd_length
Preprocessor Symbol: off_rtd_fields
Preprocessor Symbol: off_rtd_printer
Preprocessor Symbol: off_rtd_symbol

Integer to add to a tagged ikptr_t STD reference to retrieve the pointer to the first byte of the fields.

Convenience preprocessor macros

Preprocessor Macro: int IK_IS_STRUCT (ikptr_t obj)

Evaluate to true if obj is a reference to a structure; otherwise evaluate to false.

Preprocessor Macro: ikptr_t IK_STD_STD (ikptr_t std)
Preprocessor Macro: ikptr_t IK_STD_NAME (ikptr_t std)
Preprocessor Macro: ikptr_t IK_STD_LENGTH (ikptr_t std)
Preprocessor Macro: ikptr_t IK_STD_FIELDS (ikptr_t std)
Preprocessor Macro: ikptr_t IK_STD_PRINTER (ikptr_t std)
Preprocessor Macro: ikptr_t IK_STD_SYMBOL (ikptr_t std)
Preprocessor Macro: ikptr_t IK_STD_DESTRUCTOR (ikptr_t std)

Evaluate to the location of the field in the struct–type descriptor std; std must be a tagged reference to a struct–type descriptor. A use of this macro can appear both as operand and as left–side of an assignment.

Preprocessor Macro: ikptr_t IK_STRUCT_STD (ikptr_t stru)
Preprocessor Macro: ikptr_t IK_STRUCT_RTD (ikptr_t stru)

Evaluate to the location of the struct–type descriptor field for the structure stru; stru must be a tagged reference to a struct object. A use of this macro can appear both as operand and as left–side of an assignment.

ikptr_t  s_stru = the_structure;
ikptr_t  s_std;

s_std = IK_STRUCT_STD(s_stru);
IK_STRUCT_STD(s_stru) = s_std;
Preprocessor Macro: ikptr_t * IK_STRUCT_STD_PTR (ikptr_t stru)
Preprocessor Macro: ikptr_t * IK_STRUCT_RTD_PTR (ikptr_t stru)

Evaluate to a pointer to the struct–type descriptor field for the structure stru; stru must be a tagged reference to a struct object.

These macros are useful to build the second argument for a call to ik_signal_dirt_in_page_of_pointer().

Preprocessor Macro: ikptr_t IK_FIELD (ikptr_t stru, ikuword_t idx)

Evaluate to the location of the field at zero–based index idx for the structure stru. A use of this macro can appear both as operand and as left–side of an assignment.

ikptr_t   s_stru = the_structure;
ikptr_t   s_field;

s_field = IK_FIELD(s_stru, 2);

IK_FIELD(s_stru, 2) = s_field;
IK_SIGNAL_DIRT(pcb, IK_FIELD_PTR(s_stru, 2));
Preprocessor Macro: ikptr_t * IK_FIELD_PTR (str, ikuword_t idx)

Evaluate to a pointer to the field at index idx for the structure stru; stru must be a tagged reference to a struct object.

This macro is useful to build the second argument for a call to ik_signal_dirt_in_page_of_pointer().

Preprocessor Macro: void * IK_STRUCT_FIELDS_VOIDP (ikptr_t stru)

Given a tagged pointer referencing a struct: return a pointer to the first machine word in the data area of the struct’s memory block; this is a pointer to the location of the first struct field.

Operations on structs

Function: ikptr_t ika_struct_alloc_no_init (ikpcb_t * pcb, ikptr_t std)

Allocate, initialise and return a new structure instance of type std. The first word of the allocated block is initialised with std, the other words are left uninitialised.

NOTE This function makes use of the field root9 of pcb.

Function: ikptr_t ika_struct_alloc_and_init (ikpcb_t * pcb, ikptr_t std)

Allocate, initialise and return a new structure instance of type std. The first word of the allocated block is initialised with std, the other words are initialised to the fixnum zero.

NOTE This function makes use of the field root9 of pcb.

Function: int ik_is_struct (ikptr_t R)

Return true if R is a reference to a structure; otherwise return false.


Next: , Previous: , Up: objects   [Index]