Creating your own Ballista® data type:
Complex Example

What is the "commit" section used for?
The commit clause is identical in format and operation to the access clause (except it starts with "commit" instead of "access"). The only difference is that all access clauses are executed before any commit clause executes. This provides a two-phase creation cycle that is useful for preventing unintentional recycling of allocated and de-allocated resources.

For example, if we wanted a buffer pointer to point to a buffer that has been freed, we would free the buffer in the commit phase. This insures that the buffer is freed immediately before the buffer pointer is used, preventing the memory from being reallocated before _theVariable is used. For example, imagine you wanted to test memcpy(to,from,n). Suppose to was supposed to point to a freed buffer. Suppose we allocate the "to" buffer and free it in the access phase. Then we allocate "from" in access as well. The compiler might well allocate the memory for "from" in the same region of memory that "to" had been allocated in. Instead of "to" now pointing to a freed region, "to" points to the region of memory that "from" points to, which is probably not the scenario you intended to test.

A better way is to allocate both the buffers, then free "to" just before calling memcpy so that "to" really points to freed memory, just as you wanted. You can do this by calling malloc in the access phase your buffers and calling free in the commit phase for the dial settings that are supposed to point to freed buffers.

What is the "cleanup" section used for?
The cleanup section is simple. Use it to free any resources, like memory, files or FIFOs that were created for particular dial settings.

let's look at the template b_ptr_buf.tpl that uses "commit", "access", and several other features.



name void* bufxx;
//this data type is used to create several types of buffers

//its parent is a general pointer
parent ptrrandomaccessxx;


includes
[
{
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <assert.h>
#include "bTypes.h"
#include "ptrrandomaccessxx.h"


}
]

//These variables and functions can be used by access, commit, and cleanup.
//Note that we have defined a macro as well as a constant and a variable.
global_defines
[
{
#define bm_PAGESIZE 4096
#define sup_fill(buf,len) for ( int i=0 ; i<len ; i++ ) *buf++ = 'a'
char *save_loc_bufxx = NULL;
}
]




dials
[
enum_dial BUF_SIZE : BUF_SMALL,BUF_MED,BUF_LARGE,BUF_XLARGE,BUF_HUGE,BUFMAX,BUF_64k,BUF_END_MED,BUF_FAR_PAST,BUF_ODD,BUF_FREED,BUF_CODE,BUF_LOW;
int_dial ANYINT;
]

access
[

//the following code is used for all enum_dial sttings
{

char *buf_ptr;
extern int main(); /* for BUF_CODE */
const int buf_SMALL = 1; /* size of small buf */
const int buf_MED = bm_PAGESIZE; /* size of medium buf */

}

//Note how we save the location of the buffer in the global variable save_loc_bufxx.
//That way, it can be freed from the cleanup stage.

BUF_SMALL
{

save_loc_bufxx = buf_ptr = (char *) malloc (buf_SMALL);
sup_fill((char *)buf_ptr, buf_SMALL);
_theVariable = buf_ptr;

}

BUF_MED
{

save_loc_bufxx = buf_ptr = (char *)malloc (buf_MED);
sup_fill((char *)buf_ptr, buf_MED);
_theVariable = buf_ptr;

}

BUF_LARGE
{

save_loc_bufxx = buf_ptr = (char *)malloc ((1 << 29) + 1);
/* don't fill this one because it takes too long */
_theVariable = buf_ptr;

}

BUF_XLARGE
{

save_loc_bufxx = buf_ptr = (char *)malloc ((1 << 30) + 1);
/* don't fill this one because it takes too long */
_theVariable= buf_ptr;

}

BUF_HUGE
{

save_loc_bufxx = buf_ptr =(char *) malloc (((unsigned long) 1 << 31) + 1);
/* don't fill this one because it takes too long */
_theVariable= buf_ptr;

}

BUFMAX
{

save_loc_bufxx = buf_ptr = (char *)malloc (ULONG_MAX);
/* don't fill this one because it takes too long */
_theVariable= buf_ptr;

}

BUF_64k
{

save_loc_bufxx = buf_ptr = (char *)malloc ((1 << 16) + 1);
sup_fill((char *)buf_ptr, (1 << 16) + 1);
_theVariable = buf_ptr;

}

BUF_END_MED
{

save_loc_bufxx = buf_ptr = (char *)malloc (buf_MED);
sup_fill((char *)buf_ptr, buf_MED);
_theVariable = (void *) (((unsigned long) buf_ptr) + buf_MED - 1);

}

BUF_FAR_PAST
{

save_loc_bufxx = buf_ptr = (char *)malloc (buf_MED);
sup_fill((char *)buf_ptr, buf_MED);
for ( int i=0 ; i<buf_MED; i++ ) *(buf_ptr++) = 'a';
_theVariable = (void *) (((unsigned long) buf_ptr) + (bm_PAGESIZE * 1000));

}

BUF_ODD
{

save_loc_bufxx = buf_ptr = (char *)malloc (buf_MED);
sup_fill((char *)buf_ptr, buf_MED);
_theVariable = (void *) (((unsigned long) buf_ptr) + 1);

}

BUF_FREED
{

save_loc_bufxx = buf_ptr = (char *)malloc (buf_MED);
sup_fill(buf_ptr, buf_MED);
for ( int i=0 ; i<buf_MED; i++ ) *(buf_ptr++) = 'a';
_theVariable= buf_ptr;

}

BUF_CODE
{

_theVariable = (void *) &main;

}



BUF_LOW
{

_theVariable = (void *) 16;

}

]

commit
[

// For the buffer that is supposed to be freed, we free it right before we use it
BUF_FREED
{

free (save_loc_bufxx);

}

]

// In the cleanup phase we free any buffers that have not been freed.
cleanup
[
{

if (save_loc_bufxx != NULL) free(save_loc_bufxx);

}
]