The C code generator mentioned in doc/libdax_model.txt. See there.

This commit is contained in:
Thomas Schmitt 2007-08-19 17:46:19 +00:00
parent 504ae1b2c8
commit d1a351c1fe
7 changed files with 2756 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,35 @@
#ifndef Cgen_includeD
#define Cgen_includeD Yes
struct CgeN {
char *classname;
char *structname;
char *functname;
int is_managed_list;
int is_bossless_list;
int gen_for_stic; /* 0=no smem,srgex,sfile , 1=all three, 2=smem only */
int make_ansi;
int make_lowercase;
char global_include_file[4096];
FILE *global_include_fp;
struct CtyP *elements;
struct CtyP *last_element;
int may_overwrite;
FILE *fp;
char filename[4096];
FILE *ptt_fp;
char ptt_filename[4096];
char msg[8192];
};
#endif /* Cgen_includeD */

View File

@ -0,0 +1,222 @@
Description of the helper program stic*/bin/cgen
cgen is copyright 2001 to 2007, Thomas Schmitt <stic-source@gmx.net>
and provided under BSD license.
Compilation:
cc -g -o cgen cgen.c ctyp.c smem.c
cgen produces a class stub in C programming language. The data structure of
the class is described by some lines which get read from stdin. The stub will
consist of four files which emerge in the current working directory:
<classname>.h public header file of the class
<classname>.c automatically generated C code of the class
plus a copy of <classname>.c.methods
<classname>_private.h private header file of the class
<classname>.c.methods safe storage for manually created functions.
From here they get copied into the generated stub.
If such a file is missing, a dummy gets created.
It will define a struct <ClassnamE> for representing the class data aspects,
construtor <Classname>_new(), destructor <Classname>_destroy(),
getter <Classname>_<element>_get() for each structure element.
Some more functions get added for particular class and element roles.
cgen normally refuses to overwrite existing files because it supposes that
those contain code added by the human programmer.
Human programmer enhancements may be explanatory comments, class specific
methods, initial element values and other special precautions within the
generated functions.
As long as the modelling phase is going on, one may store such human code
in <classname>.c.methods and may use command line option -overwrite for
modelling development cycles.
At some point of model matureness one may decide to give up cgen and the
.c.method files and to go on only with _private.h , .h and .c files.
Command line options
-no_stic prevents usage of stic_dir/s_tools/*.[ch]
-ansi generates ANSI C function heads and makes file <classname>.h hold
only public definitions: an opaque declaration of the class struct
and a list of function prototypes. The definiton of the class
struct is then in <classname>_private.h .
-global_include filename
sets the name of a file which will contain globally necessary
declarations. Currently it lists the existence of all class
structs.
-lowercase generate struct <classname> rather than struct <ClassnamE> and
function names <classname>_func() rather than <Classname>_func() .
-overwrite allows to overwrite files <classname>_private.h, <classname>.h
and <classname>.c, but not <classname>.c.methods.
Input line format:
There are two states of input: class level and element level.
Exampes are shown below with class roles and element roles.
Input starts at class level. A class level line may be one of
- Comment. A line which begins with '#' is ignored on class level.
- Empty. A line with no characters is a comment with empty text (i.e. ignored).
- Class. Options which begin with '-' and finally a word in lowercase letters
which defines the <classname>. The classname leads to a struct ClassnamE
and some class methods implemented as C functions <Classnname>_<func>().
- End of input. Line "@@@" or EOF at stdin end the program run.
After a class line, input switches to element level where a line may be:
- Comment. A line which after some white space begins with '#' is considered
a comment. The preceeding white space is ignored and the text after '#' is
eventuellay trimmed by a single blank at both ends. This text will be part
of the class struct definition within file <classname_private>.h as a single
C comment line /* ... */. The sequence of elements and comments is preserved.
An empty comment text leads to an empty line in <classname_private>.h.
- Empty. A line with no characters is a comment with empty text.
- Element. Options which begin with '-', eventual C keywords "unsigned" or
"volatile", type or "struct <NamE>", element name. This leads to a struct
element which is taken into respect in some class methods. Depending on the
options in this line, some element methods <Classnname>_<func>_<element>()
may get generated.
- End of class. A single '@' marks the end of the element list and brings
input back to class level. I.e. next is expected another class name or
"@@@" or EOF at stdin.
Input semantics:
A class can have one of two roles:
- Standalone class.
Input example:
my_class
- Listable class, which has pointers to peer instances: .prev and .next
Such classes get a list destructor <Classname>_destroy_all() which destroys
all members of a list (which is given by any of the list members).
Such a class should have a pointer *boss as first element in which case
the constructor will look like
<Classname>_new(struct <ClassnamE> **o,struct <Some_clasS> *boss,int flag);
There is a function <Classname>_link() which inserts an instance into a list
and a function <Classname>_count() which tells the number of list members.
For pseudo random access there is function <Classname>_by_idx().
Input example:
-l my_class
A modifier is defined for classes:
- Bossless. Disables a special boss-subordinate relationship which is created
if the first element of a class is a struct pointer with the name "boss".
Like
-l <classname>
-v struct Some_clasS *boss
Normally such a parameter *boss becomes part of the constructor method
<Classname>_new(struct <ClassnamE> **o, struct Some_clasS *boss, int flag);
This relationship is typical for a listable class and a single class which
is designed to host instances of that listable class. Therefore one gets a
warning if a listable class does not begin with a struct pointer *boss.
But if -b is given, then CgeN ill not include a parameter *boss into the
constructor. It will rather look normal:
<Classname>_new(struct <ClassnamE> **o, int flag);
It will not warn if the first element of a listable class is not struct
pointer *boss.
Elements have one of the following roles:
- Value. It provides only storage for a C data type (which may be a C pointer
despite the role name "value"), a getter method <Classname>_<element>_get(),
and a setter method <Classname>_<element>_set().
Input examples:
-v int i
-v int a[100]
-v char *cpt
-v struct xyz x
-v struct xyz *xpt
- Managed. This has to be a pointer to a struct <XyZ> or to char. It will not
get attached to an object by the stub's code but its destructor
<Xyz>_destroy() will be called by <Classname>_destruct(). In case of (char *)
it is supposed that a non-NULL value has been allocated by malloc().
Managed (char *) types get a setter function <Classname>_<element>_set()
which allocates memory and copies the textstring from its parameter.
Input examples:
-m struct XyZ *xyzpt
-m char *textstring
- Chainlink. A pair of prev-next-style pointers to the own class struct.
Function <Classname>_destruct() will unlink the affected instance and
put together its link partners.
Input example (there must always be two consequtive -c lines):
-c struct My_clasS *up
-c struct My_clasS *down
- List. A pair of pointers to the struct <XyZ> of a listable class. The first
one <ls> holds the start of the list, the second one <eol> holds the end.
The getter function has an additional argument idx:
<Classname>_get_<ls>(struct <ClassnamE> *o, int idx, struct <XyZ> **pt,
int flag)
idx == 0 is the start of the list, idx=1 the next element, ...
idx == -1 retrieves the last element of the list.
For insertion of list items there is provided method <Classname>_new_<ls>().
The inserted item is reachable via the getter function with idx == -1
<Classname>_destroy() instance calls <Xyz>_destroy_all(). Note that the end
pointer is always generated as private element (-p).
Input example (there must always be a -l and a -v line):
-l struct XyZ *list_start
-v struct XyZ *list_end
The availability of getter method <Classname>_get_<element>(), and setter
method <Classname>_set_<element>_set() can be controled by two modifiers:
- Readonly. Only a getter method.
Input example
-r -v int broadcasted_status
- Private. Neither getter nor setter method.
Input example
-p -v int private_value
- Bossless listable. This marks elements which are listable objects but do not
expect a boss pointer in their constructor. See above: Listable class and
the bossless modifier for classes.
Input example
-b -l struct XyZ *list
-v struct XyZ *last_in_list
- Initialization free. The class constructor will not initialize this element.
This modifier has to be used if neither NULL nor 0 are suitable
initialization values.
Example run:
rm class_x.c class_x.h class_y.c class_y.h
bin/cgen <<+
-l class_x
-r -v struct Boss_clasS *boss
-v int x
-r -v struct stat stbuf
-m struct Class_Y *y
-m char *text
-c struct Class_X *master
-c struct Class_X *slave
-b -l struct Class_X *provider
-p -v struct Class_X *last_provider
@
-b -l class_y
-r -v char providername[80]
@
+

View File

@ -0,0 +1,364 @@
/*
cc -g -o ctyp.c
*/
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "smem.h"
extern char *Sfile_fgets();
extern int Sregex_string();
extern int Sregex_trimline();
#include "ctyp.h"
/* -------------------------- CtyP ----------------------- */
int Ctyp_new(objpt,link,flag)
struct CtyP **objpt;
struct CtyP *link;
int flag;
{
struct CtyP *o;
int ret;
*objpt= o= TSOB_FELD(struct CtyP,1);
if(o==NULL)
return(-1);
o->is_comment= 0;
o->is_pointer= 0;
o->is_struct= 0;
o->is_unsigned= 0;
o->is_volatile= 0;
o->array_size= 0;
o->management= 0;
o->with_getter= 1;
o->with_setter= 1;
o->bossless_list= 0;
o->no_initializer= 0;
o->dtype= NULL;
o->name= NULL;
o->prev= NULL;
o->next= NULL;
if(link!=NULL)
link->next= o;
o->prev= link;
return(1);
failed:;
Ctyp_destroy(objpt,0);
return(-1);
}
int Ctyp_destroy(objpt,flag)
struct CtyP **objpt;
int flag;
{
struct CtyP *o;
o= *objpt;
if(o==NULL)
return(0);
if(o->prev!=NULL)
o->prev->next= o->next;
if(o->next!=NULL)
o->next->prev= o->prev;
Sregex_string(&(o->dtype),NULL,0);
Sregex_string(&(o->name),NULL,0);
free((char *) o);
*objpt= NULL;
return(1);
}
int Ctyp_get_pointer_level(ct,flag)
struct CtyP *ct;
int flag;
{
return(ct->is_pointer);
}
int Ctyp_is_struct(ct,flag)
struct CtyP *ct;
int flag;
{
return(ct->is_struct);
}
int Ctyp_get_array_size(ct,flag)
struct CtyP *ct;
int flag;
{
return(ct->array_size);
}
int Ctyp_get_management(ct,flag)
struct CtyP *ct;
int flag;
{
return(ct->management);
}
int Ctyp_get_with_getter(ct,flag)
struct CtyP *ct;
int flag;
{
return(ct->with_getter);
}
int Ctyp_get_with_setter(ct,flag)
struct CtyP *ct;
int flag;
{
return(ct->with_setter);
}
int Ctyp_get_dtype(ct,text,flag)
struct CtyP *ct;
char **text; /* must point to NULL of freeable memory */
int flag;
/*
bit0=eventually prepend "struct "
*/
{
if((flag&1) && ct->is_struct) {
if(Sregex_string(text,"struct ",0)<=0)
return(-1);
} else {
if(Sregex_string(text,"",0)<=0)
return(-1);
}
if(Sregex_string(text,ct->dtype,1)<=0)
return(-1);
return(1);
}
int Ctyp_get_name(ct,text,flag)
struct CtyP *ct;
char **text; /* must point to NULL of freeable memory */
int flag;
{
if(Sregex_string(text,ct->name,0)<=0)
return(-1);
return(1);
}
int Ctyp_get_type_mod(ct,is_spointer,is_struct,array_size,flag)
struct CtyP *ct;
int *is_spointer,*is_struct,*array_size;
int flag;
{
*is_spointer= ct->is_pointer;
*is_struct= ct->is_struct;
*array_size= ct->array_size;
}
int Ctyp_new_from_line(ct,link,line,msg,flag)
struct CtyP **ct;
struct CtyP *link;
char *line;
char *msg;
int flag;
/*
bit0= make struct ClassnamE to struct classname
*/
{
struct CtyP *o;
char *cpt,*bpt;
int ret,l;
char orig_line[4096];
ret= Ctyp_new(ct,*ct,0);
if(ret<=0) {
sprintf(msg,"Failed to create CtyP object (due to lack of memory ?)");
goto ex;
}
o= *ct;
strcpy(orig_line,line);
cpt= line;
while(*cpt!=0 && isspace(*cpt)) cpt++;
if(cpt[0]=='#') {
cpt++;
if(cpt[1]==' ')
cpt++;
l= strlen(cpt);
if(cpt[0]==' ')
cpt++;
if(l>1)
if(cpt[l-1]==' ')
cpt[l-1]= 0;
if(Sregex_string(&(o->name),cpt,0)<=0)
{ret= -1; goto ex;}
o->is_comment= 1;
{ret= 1; goto ex;}
} else if(cpt[0]==0) {
if(Sregex_string(&(o->name),cpt,0)<=0)
{ret= -1; goto ex;}
o->is_comment= 1;
{ret= 1; goto ex;}
} else if(cpt[0]=='/' && cpt[1]=='*') {
sprintf(msg,
"C-style multi line comments (/* ... */) not supported yet. Use #.");
goto ex;
/* >>> */
}
cpt= line;
while(cpt[0]=='-') {
/* look for management specifiers:
-v* just a value
-m* allocated memory which needs to be freed
-c* mutual link (like prev+next)
-l* list of -m chained by mutual links prev and next
-r* read-only : no setter function
-p* private : neither setter nor getter function
-b* bossless_list : Class_new(o,flag), not Class_new(o,boss,flag)
-i* no_initializer : do not initialize element in <Class>_new()
#... line is a comment
*/
if(cpt[1]=='v' || cpt[1]=='V') {
o->management= 0;
} else if(cpt[1]=='m' || cpt[1]=='M') {
o->management= 1;
} else if(cpt[1]=='c' || cpt[1]=='C') {
o->management= 2;
if(o->prev!=NULL)
if(o->prev->management==2)
o->management= 3;
} else if(cpt[1]=='l' || cpt[1]=='L') {
o->management= 4;
} else if(cpt[1]=='r' || cpt[1]=='R') {
o->with_setter= 0;
} else if(cpt[1]=='p' || cpt[1]=='P') {
o->with_setter= 0;
o->with_getter= 0;
} else if(cpt[1]=='b' || cpt[1]=='B') {
o->bossless_list= 1;
} else if(cpt[1]=='i' || cpt[1]=='I') {
o->no_initializer= 1;
}
while(*cpt!=0 && !isspace(*cpt)) cpt++;
while(*cpt!=0 && isspace(*cpt)) cpt++;
if(*cpt==0)
goto no_name;
}
if(strncmp(cpt,"struct ",7)==0) {
o->is_struct= 1;
cpt+= 7;
} else if(strncmp(cpt,"unsigned ",9)==0) {
o->is_unsigned= 1;
cpt+= 9;
} else if(strncmp(cpt,"volatile ",9)==0) {
o->is_volatile= 1;
cpt+= 9;
if(strncmp(cpt,"unsigned ",9)==0) {
o->is_unsigned= 1;
cpt+= 9;
}
}
if(*cpt==0)
goto no_name;
while(*cpt!=0 && isspace(*cpt)) cpt++;
bpt= cpt;
while(*bpt!=0 && !isspace(*bpt)) bpt++;
if(*bpt==0)
goto no_name;
if(*bpt==0) {
no_name:;
sprintf(msg,"No name found after type description : %s",orig_line);
ret= 0; goto ex;
}
*bpt= 0;
if(Sregex_string(&(o->dtype),cpt,0)<=0)
{ret= -1; goto ex;}
if((flag&1) && o->is_struct && strlen(o->dtype)>=3)
if(isupper(o->dtype[0]) && islower(o->dtype[1]) &&
isupper(o->dtype[strlen(o->dtype)-1])) {
o->dtype[0]= tolower(o->dtype[0]);
o->dtype[strlen(o->dtype)-1]= tolower(o->dtype[strlen(o->dtype)-1]);
}
cpt= bpt+1;
while(*cpt!=0 && isspace(*cpt)) cpt++;
if(*cpt==0)
goto no_name;
for(;*cpt=='*';cpt++)
o->is_pointer++;
if(*cpt==0)
goto no_name;
bpt= strchr(cpt,'[');
if(bpt!=NULL) {
if(strchr(bpt,']')!=NULL)
*strchr(bpt,']')= 0;
sscanf(bpt,"%lu",&(o->array_size));
*bpt= 0;
}
if(Sregex_string(&(o->name),cpt,0)<=0)
{ret= -1; goto ex;}
if(o->management==1) {
if((!(o->is_pointer>=1 && o->is_pointer<=2)) ||
((!o->is_struct) && strcmp(o->dtype,"char")!=0 &&
(strcmp(o->dtype,"unsigned char")!=0))) {
sprintf(msg,"-m can only be applied to pointers of struct or char : %s",
orig_line);
ret= 0; goto ex;
}
}
ret= 1;
ex:;
return(ret);
}
int Ctyp_read_fp(ct,fp,msg,flag)
struct CtyP **ct;
FILE *fp;
char msg[]; /* at least [4096+256] */
int flag;
/*
bit0= make struct ClassnamE to struct classname
*/
{
int ret;
char line[4096];
struct CtyP *o;
line[0]= 0;
printf(
"[-value|-managed|-chain|-list] class element ? (e.g.: -l struct XyZ)\n");
if(Sfile_fgets(line,sizeof(line)-1,fp)==NULL)
{ret= 2; goto ex;}
printf("%s\n",line);
Sregex_trimline(line,0);
if(strcmp(line,"@")==0)
{ret= 2; goto ex;}
ret= Ctyp_new_from_line(ct,*ct,line,msg,flag&1);
if(ret<=0)
goto ex;
ret= 1;
ex:;
return(ret);
}

View File

@ -0,0 +1,41 @@
#ifndef Ctyp_includeD
#define Ctyp_includeD
struct CtyP {
/* if 1 : .name contains comment text, all other elements are invalid */
int is_comment;
int is_pointer; /* number of asterisks */
int is_struct;
int is_unsigned;
int is_volatile;
unsigned long array_size;
int management; /*
-v 0= just a value
-m 1= allocated memory which needs to be freed
-c 2= mutual link with the next element
-c 3= mutual link with the prev element
-l 4= list of -m , chained by -c pair named 'prev','next'
supposed to be followed by a -v of the same type
which will mark the end of the list
*/
int with_getter;
int with_setter;
int bossless_list;
int no_initializer;
char *dtype;
char *name;
struct CtyP *prev;
struct CtyP *next;
};
#endif /* Ctyp_includeD */

View File

@ -0,0 +1,445 @@
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define Smem_included_by_smem_C
#include "smem.h"
/* ------------------------------ SmemiteM ----------------------------- */
int Smemitem_new(item,data,size,next,hash_start,flag)
struct SmemiteM **item;
char *data;
size_t size;
struct SmemiteM *next;
struct SmemiteM **hash_start;
int flag;
{
struct SmemiteM *t;
*item= t= (struct SmemiteM *) malloc(sizeof(struct SmemiteM));
if(t==NULL)
return(-1);
t->data= data;
t->size= size;
t->prev= NULL;
t->next= next;
#ifdef Smem_with_hasH
t->hash_next= NULL;
t->hash_prev= NULL;
#endif /* Smem_with_hasH */
if(next!=NULL) {
if(next->prev!=NULL) {
t->prev= next->prev;
next->prev->next= t;
}
next->prev= t;
}
#ifdef Smem_with_hasH
if(hash_start!=NULL) {
t->hash_next= *hash_start;
if(t->hash_next!=NULL) {
t->hash_next->hash_prev= t;
}
*hash_start= t;
}
#endif /* Smem_with_hasH */
return(1);
}
int Smemitem_destroy(in_item,hash_start,flag)
struct SmemiteM **in_item;
struct SmemiteM **hash_start;
int flag;
{
struct SmemiteM *item;
item= *in_item;
if(item==NULL)
return(0);
if(item==Smem_start_iteM)
Smem_start_iteM= item->next;
if(item->prev!=NULL)
item->prev->next= item->next;
if(item->next!=NULL)
item->next->prev= item->prev;
#ifdef Smem_with_hasH
if(hash_start!=NULL) {
if(item==*hash_start)
*hash_start= item->hash_next;
if(item->hash_prev!=NULL)
item->hash_prev->hash_next= item->hash_next;
if(item->hash_next!=NULL)
item->hash_next->hash_prev= item->hash_prev;
}
#endif /* Smem_with_hasH */
free((char *) item);
*in_item= NULL;
return(1);
}
int Smemitem_report(item,line,flag)
struct SmemiteM *item;
char line[1024];
int flag;
{
char *cpt;
int i,upto;
sprintf(line,"%4lu bytes at %8.8lx ",(unsigned long) item->size,
(unsigned long) item->data);
cpt= line+strlen(line);
if(item->size<=256)
upto= item->size;
else
upto= 256;
if(item->data!=NULL) {
strcpy(cpt,"= \"");
cpt+= 3;
for(i=0;i<upto;i++){
if(item->data[i]<32 || item->data[i]>=127 || item->data[i]=='\\') {
sprintf(cpt,"\\%2.2X",(unsigned char) item->data[i]);
cpt+= 3;
} else {
*(cpt++)= item->data[i];
}
}
if(i<item->size) {
sprintf(cpt,"\" [truncated]");
} else {
*(cpt++)= '"';
*cpt= 0;
}
}
return(1);
}
int Smemitem_stderr(item,flag)
struct SmemiteM *item;
int flag;
{
char line[1024];
Smemitem_report(item,line,0);
fprintf(stderr,"%s\n",line);
return(1);
}
/* -------------------------------- Smem ------------------------------ */
int Smem_protest(line,flag)
char *line;
int flag;
{
fprintf(stderr,"%s\n",line);
return(1);
}
int Smem_hashindex(ptr,flag)
char *ptr;
int flag;
{
unsigned long idx;
idx= (unsigned long) ptr;
return((idx>>Smem_hashshifT)%(Smem_hashsizE));
}
/* find a certain memory item */
struct SmemiteM *Smem_find_item(ptr,flag)
char *ptr;
int flag;
{
int misscount= 0,idx;
struct SmemiteM *current;
#ifdef Smem_with_hasH
idx= Smem_hashindex(ptr,0);
for(current= Smem_hasH[idx];current!=NULL;current= current->hash_next) {
if(current->data==ptr)
return(current);
misscount++;
}
#else /* Smem_with_hasH */
for(current= Smem_start_iteM;current!=NULL;current= current->next) {
if(current->data==ptr)
return(current);
misscount++;
}
#endif /* ! Smem_with_hasH */
return(NULL);
}
int Smem_search_and_delete(ptr,flag)
char *ptr;
int flag;
/*
bit0= revoke registration : decrement counters
*/
{
int idx;
struct SmemiteM *current;
current= Smem_find_item(ptr,0);
if(current==NULL)
return(0);
Smem_record_counT--;
Smem_record_byteS-= current->size;
idx= Smem_hashindex(ptr,0);
Smemitem_destroy(&current,&(Smem_hasH[idx]),0);
Smem_hash_counteR[idx]-= 1.0;
if(flag&1) {
Smem_malloc_counT--;
Smem_pending_counT--;
}
return(1);
}
char *Smem_malloc(size)
size_t size;
{
int idx;
char *cpt;
if(size==0) {
Smem_protest("########### smem.c : malloc(0) caught",0);
return(NULL);
}
/* if(size==1032)
cpt= NULL; / * set breakpoint here to find requests of certain size */
cpt= (char *) malloc(size);
if(cpt==NULL) {
char text[161];
sprintf(text,"########### smem.c : malloc( %lu ) returned NULL",
(unsigned long) size);
Smem_protest(text,0);
return(NULL);
}
/* if(cpt==0x080a1e20)
cpt= NULL; / * set breakpoint here to find origin of certain address */
Smem_malloc_counT++;
Smem_pending_counT++;
if(Smem_record_itemS) {
idx= Smem_hashindex(cpt,0);
Smem_hash_counteR[idx]+= 1.0;
if(Smemitem_new(&Smem_start_iteM,cpt,size,Smem_start_iteM,
&(Smem_hasH[idx]),0)<=0) {
Smem_protest(
"########### smem.c : malloc( sizeof(SmemiteM) ) returned NULL",0);
return(NULL);
}
Smem_record_counT++;
Smem_record_byteS+= size;
}
return(cpt);
}
int Smem_free(ptr)
char *ptr;
{
if(ptr==NULL) {
Smem_protest("########### smem.c : free() of NULL pointer caught",0);
return(0);
}
if(Smem_record_itemS) {
if(Smem_search_and_delete(ptr,0)<=0) {
Smem_protest("########### smem.c : free() of unrecorded pointer caught",0);
return(0);
}
}
Smem_free_counT++;
Smem_pending_counT--;
free(ptr);
return(1);
}
int Smem_report(line,flag)
char line[1024];
int flag;
{
sprintf(line,"malloc= %.f , free= %.f , pending= %.f",
Smem_malloc_counT,Smem_free_counT,Smem_pending_counT);
if(Smem_record_itemS) {
sprintf(line+strlen(line)," , bytes=%.f , records= %.f",
Smem_record_byteS,Smem_record_counT);
}
return(1);
}
int Smem_stderr(flag)
int flag;
/*
bit0= report 50 youngest pending items too
bit1= do not report if nothing is pending
*/
{
struct SmemiteM *current;
char line[1024];
int i= 0;
if(flag&2)
if(Smem_pending_counT==0.0
&& Smem_record_counT==0.0
&& Smem_record_byteS==0.0)
return(2);
Smem_report(line,0);
fprintf(stderr,"%s\n",line);
if(flag&1) {
for(current= Smem_start_iteM;current!=NULL;current= current->next) {
Smemitem_stderr(current,0);
if(++i>=50)
break;
}
if(current!=NULL)
if(current->next!=NULL)
fprintf(stderr,"[list truncated]\n");
}
return(1);
}
int Smem_set_record_items(value)
int value;
{
int i;
if(!Smem_hash_initializeD) {
for(i=0;i<Smem_hashsizE;i++) {
Smem_hasH[i]= NULL;
Smem_hash_counteR[i]= 0.0;
}
Smem_hash_initializeD= 1;
}
Smem_record_itemS= value;
return(1);
}
int Smem_is_recorded(ptr,flag)
char *ptr;
int flag;
/*
bit0= complain if return(0)
*/
{
if(Smem_record_itemS==0)
return(2);
if(Smem_find_item(ptr,0)!=NULL)
return(1);
if(flag&1)
Smem_protest("########### smem.c : free() of unrecorded pointer caught",0);
return(0);
}
/* A simple C string cloner */
int Smem_clone_string(ptr,text)
char **ptr;
char *text;
{
*ptr= Smem_malloC(strlen(text)+1);
if(*ptr==NULL)
return(-1);
strcpy(*ptr,text);
return(1);
}
/* ----------------- for usage via debugger commands --------------------- */
/* find a certain memory item */
struct SmemiteM *Smem_find_data(ptr)
char *ptr;
{
return(Smem_find_item(ptr,0));
}
/* browsing the list */
struct SmemiteM *Smem_fetch_item(step,flag)
int step;
int flag;
/*
bit0= reset cursor (and therefore address absolutely)
*/
{
static struct SmemiteM *current= NULL;
if((flag&1)||current==NULL)
current= Smem_start_iteM;
if(step>0) {
for(;current!=NULL;current= current->next) {
if(step==0)
return(current);
step--;
}
} else if(step<0) {
for(;current!=NULL;current= current->prev) {
if(step==0)
return(current);
step++;
}
} else {
return(current);
}
return(NULL);
}
int Smem_print_hash_counter() {
int i;
for(i=0;i<Smem_hashsizE;i++)
printf("%4d : %10.f\n",i,Smem_hash_counteR[i]);
return(1);
}
/* delete all recorded memory items */
int Smem_delete_all_items()
{
int ret;
while(Smem_start_iteM!=NULL) {
ret= Smem_free(Smem_start_iteM->data);
if(ret<=0)
return(0);
}
return(1);
}

View File

@ -0,0 +1,161 @@
#ifndef Smem_includeD
#define Smem_includeD
/* compile time adjustable parameters : */
/* if not defined, flat malloc() and free() is used */
#define Smem_own_functionS
#ifdef Smem_no_own_functionS
#undef Smem_own_functionS
#endif /* Smem_no_own_functionS */
/* if not defined, the record items will be smaller by 8 byte
but deletion of items may be much slower */
#define Smem_with_hasH
struct SmemiteM {
char *data;
size_t size;
struct SmemiteM *prev,*next;
struct SmemiteM *hash_prev,*hash_next;
};
#ifdef Smem_own_functionS
char *Smem_malloc();
#define TSOB_FELD(typ,anz) (typ *) Smem_malloc((anz)*sizeof(typ));
#define Smem_malloC Smem_malloc
#define Smem_freE Smem_free
#else /* Smem_own_functionS */
#define TSOB_FELD(typ,anz) (typ *) malloc((anz)*sizeof(typ));
#define Smem_malloC malloc
#define Smem_freE free
#endif /* ! Smem_own_functionS */
#define Smem_hashsizE 251
#define Smem_hashshifT 8
#ifdef Smem_included_by_smem_C
double Smem_malloc_counT= 0.0;
double Smem_free_counT= 0.0;
double Smem_pending_counT= 0.0;
struct SmemiteM *Smem_start_iteM= NULL;
struct SmemiteM *Smem_hasH[Smem_hashsizE];
double Smem_hash_counteR[Smem_hashsizE];
/* these both init values are essential, since setting Smem_record_itemS=1
by use of Smem_set_record_items() initializes the hash array
(i do not really trust the compiler producers to have read K&R) */
int Smem_hash_initializeD= 0;
int Smem_record_itemS= 0;
double Smem_record_counT= 0.0;
double Smem_record_byteS= 0.0;
#else /* Smem_included_by_smem_C */
extern double Smem_malloc_counT;
extern double Smem_free_counT;
extern double Smem_pending_counT;
extern struct SmemiteM *Smem_start_iteM;
extern struct SmemiteM *Smem_hasH[Smem_hashsizE];
extern double Smem_hash_counteR[Smem_hashsizE];
extern int Smem_hash_initializeD;
extern int Smem_record_itemS;
extern double Smem_record_counT;
extern double Smem_record_byteS;
#endif /* ! Smem_included_by_smem_C */
#endif /* ! Smem_includeD */
/*
smem
Functions to replace malloc() and free() in order to get more control
over memory leaks or spurious errors caused by faulty usage of malloc()
and free().
Sourcecode provisions:
Use only the following macros for memory management:
TSOB_FELD(type,count) creates an array of items of given type
Smem_malloC() analogue of malloc()
Smem_freE() analogue of free()
One may #define malloc Smem_malloC resp. #define free Smem_freE
but better would be to review (and often to streamline) the sourcecode
in respect to those two functions.
Speed versus control:
In production versions, where maximum speed is required, one may undefine
the macro Smem_own_functionS in smem.h .
This causes the above macros to directly invoke malloc() and free() without
any speed reduction (and without any additional use).
Undefinitio can be done globaly by modifying smem.h or locally by defining
Smem_no_own_functionS before including smem.h .
If Smem_own_functionS remains defined, then the functions
Smem_malloc()
Smem_free()
are used rather than malloc() and free().
They count the number of calls to maintain a rough overview of memory usage.
Smem_malloc() additionally checks for 0 size and Smem_free() checks for
NULL pointers, which they both report to stderr. Eventually one should set
a breakpoint in function Smem_protest() to learn about the origin of such
messages.
A status line may be obtained by Smem_report() or printed by Smem_stderr().
As long as the variable Smem_record_itemS is set to 0, there is not very much
overhead compared with malloc() and free().
If the variable is set to 1 by Smem_set_record_items() then all malloc()
results are kept in a list where they will be deleted by their corresponding
Smem_free() calls. If a pointer is to be freed, which is not recorded in the
list then an error message will be printed to stderr. The memory will not
be freed !
This mode not only may be very slow, it also consumes at least 16 byte per
piece of data which was obtained by malloc as long as it has not been freed.
Due to the current nature of the list, large numbers of memory items are freed
much faster in the reverse order of their creation. If there is a list of
100000 strings to delete, it is very rewarding to free the youngest ones first.
A shortcut via hashing is available but consumes 24 bytes rather than 16.
(see above Smem_with_hasH )
The function Smem_is_recorded() can be used to check wether a pointer is
valid according to the list. It returns :
0 = is not in list , 1 = is in list , 2 = recording is off
If one decides to start recording malloc() results in the midst of a program
run, one has to be aware of false protests of Smem_free() if a memory piece
has been allocated before recording started. This will also cause those pieces
to be memory leaks because Smem_free() refuses to delete them. (Freeing memory
that was not obtained by malloc or was already freed previously can result in
deferred SIGSEGV or similar trouble, depending on OS and library.)
Also in that case one should stop recording before ending the program, to
avoid a lot of false complaints about longliving memory objects.
*/