Previous: Intrinsic Integer Data Type, Up: Simple Referable Data Type Examples


3.4.1.2 Referable String Data Type

Consider some data type describing string data. For the interpreted languages such type would be an opaque application defined type. Consider now that it would be desirable to have multiple references to data of such type.

For the application data of such type may be represented, for an instance, as a pointer integer pair, the pointer indicating some memory address where the string bytes are stored and the integer counting the bytes.

struct this_type {
    char *data;
    unsigned size;
};

Adding a reference counter transforms to:

struct this_type {
    char *data;
    unsigned call, size;
};

The interpreters will require some struct x1f4_datatype_type definition to allow declarations of data of such type.

See struct x1f4_datatype_type.

struct x1f4_datatype_type data_type = {
    "data",
    flat_data,
    line_data,
    link_data,
    flat_data,
    HERE_DATA,
    4,
    NULL
};

The definition specifies some 4 characters long `data' name and some HERE_DATA id for the data type. HERE_DATA is some integral value, better greater than X1f4_E4_LAST.

See Symbolic Types.

The pre-initializing routine (pre-initializing helps with early program execution termination) does little:

int
link_data(void *context, void *subtext, void **address)
{
    *address = NULL;

    return 0;
}

See Execution Completion Cleanup.

The initializing routine allocates some data of the string representation struct this_type type and sets it to some sensible value (the reference counter to 1, the actual string data to some empty string).

int
line_data(void *context, void *subtext, void **address)
{
    int status;
    struct this_type *this_data;

    allocate memory for data, set status to operation success

    if (status) {
    } else {
	this_data->call = 1;

	this_data->length = 0;
	this_data->string = (char *) x1f4_c1_empty_string;

	*address = (void *) this_data;
    }

    return status;
}

See x1f4_c1_empty_string.

See Of The Intrinsic String Type.

Define some routine that actually destroys data:

int
side_data(void *context, void *subtext, void *data)
{
    int status;
    struct this_type *this_data;

    this_data = data;
    if (!this_data) {
	status = 0;
    } else {
	free actual string data if necessary, free data,
	set status to operation success
    }

    return status;
}

and the routine that accounts for references being removed:

int
flat_data(void *context, void *subtext, void **address)
{
    int status;
    struct this_type *this_data;

    this_data = *address;
    if (this_data) {
	unsigned call;

	call = this_data->call;
	call--;
	if (call) {
	    this_data->call = call;

	    *address = NULL;

	    status = 0;
	} else {
	    status = side_data(context, subtext, this_data);
	    if (status) {
	    } else {
		*address = NULL;
	    }
	}
    } else {
	status = 0;
    }

    return status;
}

Expectedly, the routine decrements the reference count, and when reaching 0 it destroys data. It also sets data to the pre-initialized NULL value.

The referable object data types definition:

See struct x1f4_nodetype_type.

struct x1f4_nodetype_type node_type = {
    "data",
    HERE_DATA,
    copy_data,
    free_data,
    NULL,
    node_data,
    NULL,
    push_data,
    X1f4_LX_LINK_ACCESS,
    4,
    NULL
};

The definition restates the 4 characters long `data' name and the HERE_DATA id for the data type.

The X1f4_LX_LINK_ACCESS indicates that the node field is set (here to some node_data routine) and to be used.

See Common Data Type Flags.

The reference removing routine much resembles the one in the struct x1f4_datatype_type definition, it is only simpler.

int
free_data(void *context, void *subtext, void *data,
	  const struct x1f4_nodelink_type *nodelink_data)
{
    int status;
    struct this_type *this_data;
    unsigned call;

    this_data = data;

    call = this_data->call;
    call--;
    if (call) {
	this_data->call = call;

	status = 0;
    } else {
	status = side_data(context, subtext, data);
    }

    return status;
}

The reference registering routine increments the reference count and copies the data pointer (as the new reference).

int
node_data(void *context, void *subtext, void **copy, void *data,
	  const struct x1f4_nodelink_type *nodelink_data)
{
    struct this_type *this_data;

    this_data = data;

    *copy = data;

    this_data->call++;

    return 0;
}

The routine transfering some reference hold by some object to some variable will copy a pointer (to data for which the reference is transferred). No reference counter for this later object needs modified. Still, the variable to which the reference is transferred is already referring some object. The reference count for this object needs decremented and if reaching 0, the object needs freed.

Stating this again: say reference to object `A' hold by object `C' needs transferred to variable `V'. The actual transfer is just a pointer copy. Yet, variable `V' is (must be) referring something, some object, call it object `B'. Once the reference transfer for object `A' is completed, `V' no longer will refer `B', hence `B' will have some reference less.

int
push_data(void *context, void *subtext, void **copy, void *data,
	  const struct x1f4_nodelink_type *nodelink_data)
{
    int status;
    struct this_type *this_data;
    unsigned call;

    this_data = *copy;

    call = this_data->call;
    call--;
    if (call) {
	this_data->call = call;

	*copy = data;

	status = 0;
    } else {
	status = side_data(context, subtext, this_data);
	if (status) {
	} else {
	    *copy = data;
	}
    }

    return status;
}

The object copying routine reserves memory for the copy being made, copies data and sets the reference count for the new object (to 1).

int
copy_data(void *context, void *subtext, void **copy, const void *data,
	  const struct x1f4_nodelink_type *nodelink_data)
{
    int status;
    struct this_type *this_data;

    allocate memory for data, set status to operation success

    if (status) {
    } else {
	unsigned length;

	length = ((struct this_type *) data)->length;
	if (!length) {
	    this_data->length = 0;
	    this_data->string = (char *) x1f4_c1_empty_string;

	    this_data->call = 1;

	    *copy = (void *) this_data;
	} else {
	    allocate memory for actual string data,
	    set status to operation success

	    if (status) {
		free memory allocated for data
	    } else {
		copy string data, set `length' and `string' fields in the
		`this_data' record

		this_data->call = 1;

		*copy = (void *) this_data;
	    }
	}
    }

    return status;
}