638 lines
16 KiB
C++
638 lines
16 KiB
C++
/*************************************************************************\
|
|
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
|
* National Laboratory.
|
|
* Copyright (c) 2002 The Regents of the University of California, as
|
|
* Operator of Los Alamos National Laboratory.
|
|
* EPICS BASE Versions 3.13.7
|
|
* and higher are distributed subject to a Software License Agreement found
|
|
* in file LICENSE that is included with this distribution.
|
|
\*************************************************************************/
|
|
// Author: Jim Kowalkowski
|
|
// Date: 2/96
|
|
//
|
|
// $Id$
|
|
//
|
|
|
|
#define epicsExportSharedSymbols
|
|
#include "gddAppTable.h"
|
|
|
|
// -----------------general destructor for managed gdds--------------------
|
|
|
|
void gddApplicationTypeDestructor::run(void* v)
|
|
{
|
|
gdd* ec = (gdd*)v;
|
|
// fprintf(stderr," destructing %8.8x\n",v);
|
|
gddApplicationTypeTable* db = (gddApplicationTypeTable*)arg;
|
|
db->freeDD(ec);
|
|
}
|
|
|
|
gddApplicationTypeElement::gddApplicationTypeElement(void) { }
|
|
gddApplicationTypeElement::~gddApplicationTypeElement(void) { }
|
|
|
|
// --------------------------app table stuff-----------------------------
|
|
|
|
gddApplicationTypeTable gddApplicationTypeTable::app_table;
|
|
|
|
gddApplicationTypeTable& gddApplicationTypeTable::AppTable(void)
|
|
{
|
|
return gddApplicationTypeTable::app_table;
|
|
}
|
|
|
|
gddApplicationTypeTable::gddApplicationTypeTable(aitUint32 tot)
|
|
{
|
|
aitUint32 i,total;
|
|
|
|
// round tot up to nearest power of 2
|
|
for(i=1u<<31;i && !(tot&i);i>>=1);
|
|
if(i==0)
|
|
total=1;
|
|
else if(i==tot)
|
|
total=tot;
|
|
else
|
|
total=i<<1;
|
|
|
|
max_groups=total/APPLTABLE_GROUP_SIZE;
|
|
if((max_groups*APPLTABLE_GROUP_SIZE) != total) ++max_groups;
|
|
max_allowed=total;
|
|
total_registered=1;
|
|
attr_table=new gddApplicationTypeElement*[max_groups];
|
|
|
|
for(i=0;i<max_groups;i++) attr_table[i]=NULL;
|
|
GenerateTypes();
|
|
}
|
|
|
|
#if 0
|
|
void* operator new(size_t x)
|
|
{
|
|
void* v = (void*)malloc(x);
|
|
fprintf(stderr,"%8.8x In operator new %d\n",v,(int)x);
|
|
return v;
|
|
}
|
|
|
|
void operator delete(void* x)
|
|
{
|
|
fprintf(stderr,"%8.8x In operator delete\n",x);
|
|
free((char*)x);
|
|
}
|
|
#endif
|
|
|
|
gddApplicationTypeTable::~gddApplicationTypeTable(void)
|
|
{
|
|
unsigned i,j;
|
|
gdd* dd;
|
|
aitUint8* blk;
|
|
|
|
// fprintf(stderr,"in gddApplicationTypeTable dest\n");
|
|
|
|
if(this!=&app_table) return;
|
|
|
|
for(i=0u;i<max_groups;i++)
|
|
{
|
|
if(attr_table[i])
|
|
{
|
|
// fprintf(stderr,"Delete TypeTable: group %d exists\n",i);
|
|
for(j=0u;j<APPLTABLE_GROUP_SIZE;j++)
|
|
{
|
|
switch(attr_table[i][j].type)
|
|
{
|
|
case gddApplicationTypeNormal:
|
|
// fprintf(stderr,"Delete TypeTable: app %u normal\n",j);
|
|
if(attr_table[i][j].app_name)
|
|
delete [] attr_table[i][j].app_name;
|
|
break;
|
|
case gddApplicationTypeProto:
|
|
// fprintf(stderr,"Delete TypeTable: app %u has proto\n",j);
|
|
if(attr_table[i][j].app_name)
|
|
delete [] attr_table[i][j].app_name;
|
|
|
|
if(attr_table[i][j].proto)
|
|
{
|
|
// if(attr_table[i][j].free_list)
|
|
// fprintf(stderr," app %u has free_list\n",j);
|
|
|
|
// The proto is stored as flattened gdd
|
|
blk=(aitUint8*)attr_table[i][j].proto;
|
|
delete [] blk;
|
|
|
|
for(dd=attr_table[i][j].free_list; dd;)
|
|
{
|
|
blk=(aitUint8*)dd;
|
|
// fprintf(stderr," delete dd %8.8x\n",blk);
|
|
dd=(gdd*)dd->next();
|
|
delete [] blk;
|
|
}
|
|
}
|
|
|
|
if(attr_table[i][j].map)
|
|
delete [] attr_table[i][j].map;
|
|
break;
|
|
case gddApplicationTypeUndefined: break;
|
|
default: break;
|
|
}
|
|
}
|
|
delete [] attr_table[i];
|
|
}
|
|
}
|
|
delete [] attr_table;
|
|
}
|
|
|
|
int gddApplicationTypeTable::describeDD(gddContainer* dd, FILE* fd,
|
|
int level, char* tn)
|
|
{
|
|
gddCursor cur = dd->getCursor();
|
|
gdd* pdd;
|
|
char tmp[8];
|
|
char* cp;
|
|
char* str;
|
|
|
|
strcpy(tmp,"unknown");
|
|
|
|
for(pdd=cur.first();pdd;pdd=pdd->next())
|
|
{
|
|
if((cp=getName(pdd->applicationType()))==NULL) cp=tmp;
|
|
fprintf(fd,"#define gddAppTypeIndex_%s_%s %d\n",tn,cp,level++);
|
|
}
|
|
|
|
for(pdd=cur.first();pdd;pdd=pdd->next())
|
|
{
|
|
if((cp=getName(pdd->applicationType()))==NULL) cp=tmp;
|
|
if(pdd->isContainer())
|
|
{
|
|
str = new char[strlen(cp)+strlen(tn)+3];
|
|
strcpy(str,tn);
|
|
strcat(str,"_");
|
|
strcat(str,cp);
|
|
level=describeDD((gddContainer*)pdd,fd,level,str);
|
|
delete [] str;
|
|
}
|
|
}
|
|
return level;
|
|
}
|
|
|
|
void gddApplicationTypeTable::describe(FILE* fd)
|
|
{
|
|
unsigned i,j;
|
|
gdd* dd;
|
|
char* tn;
|
|
|
|
fprintf(fd,"\n");
|
|
for(i=0;i<max_groups;i++)
|
|
{
|
|
if(attr_table[i])
|
|
{
|
|
for(j=0;j<APPLTABLE_GROUP_SIZE;j++)
|
|
{
|
|
switch(attr_table[i][j].type)
|
|
{
|
|
case gddApplicationTypeNormal:
|
|
case gddApplicationTypeProto:
|
|
tn=attr_table[i][j].app_name;
|
|
fprintf(fd,"#define gddAppType_%s\t%u\n",
|
|
tn,i*APPLTABLE_GROUP_SIZE+j);
|
|
|
|
if( (dd=attr_table[i][j].proto) )
|
|
{
|
|
fprintf(fd,"#define gddAppTypeIndex_%s 0\n",tn);
|
|
if(dd->isContainer())
|
|
describeDD((gddContainer*)dd,fd,1,tn);
|
|
// fprintf(fd,"\n");
|
|
}
|
|
break;
|
|
case gddApplicationTypeUndefined: break;
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fprintf(fd,"\n");
|
|
}
|
|
|
|
gddStatus gddApplicationTypeTable::registerApplicationType(
|
|
const char* const name,aitUint32& new_app)
|
|
{
|
|
aitUint32 i,group,app,rapp;
|
|
gddStatus rc;
|
|
|
|
if( (new_app=getApplicationType(name)) )
|
|
{
|
|
// gddAutoPrint(gddErrorAlreadyDefined);
|
|
return gddErrorAlreadyDefined;
|
|
}
|
|
if(total_registered>max_allowed)
|
|
{
|
|
gddAutoPrint("gddAppTable::registerApplicationType()",gddErrorAtLimit);
|
|
return gddErrorAtLimit;
|
|
}
|
|
|
|
sem.take();
|
|
rapp=total_registered++;
|
|
sem.give();
|
|
|
|
if((rc=splitApplicationType(rapp,group,app))<0) return rc;
|
|
|
|
if(attr_table[group])
|
|
{
|
|
// group already allocated - check is app already refined
|
|
if(attr_table[group][app].type!=gddApplicationTypeUndefined)
|
|
{
|
|
// gddAutoPrint(gddErrorAlreadyDefined);
|
|
return gddErrorAlreadyDefined;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// group must be allocated
|
|
attr_table[group]=new gddApplicationTypeElement[APPLTABLE_GROUP_SIZE];
|
|
|
|
// initialize each element of the group as undefined
|
|
for(i=0;i<APPLTABLE_GROUP_SIZE;i++)
|
|
{
|
|
attr_table[group][i].type=gddApplicationTypeUndefined;
|
|
attr_table[group][i].map=NULL;
|
|
}
|
|
}
|
|
|
|
attr_table[group][app].app_name=strDup(name);
|
|
attr_table[group][app].type=gddApplicationTypeNormal;
|
|
attr_table[group][app].proto=NULL;
|
|
attr_table[group][app].free_list=NULL;
|
|
|
|
new_app=rapp;
|
|
// fprintf(stderr,"registered <%s> %d\n",name,(int)new_app);
|
|
return 0;
|
|
}
|
|
|
|
// registering a prototype of an empty container causes problems.
|
|
// The current implementation does not monitor this so the container
|
|
// is not cleared when free. A user may, however, include an empty
|
|
// container within a prototype container, and everything will work
|
|
// correctly.
|
|
|
|
gddStatus gddApplicationTypeTable::registerApplicationTypeWithProto(
|
|
const char* const name, gdd* protoDD, aitUint32& new_app)
|
|
{
|
|
aitUint32 group,app,rapp;
|
|
aitUint8* blk;
|
|
size_t sz;
|
|
aitIndex tot;
|
|
aitUint16 x;
|
|
aitUint16 i;
|
|
gddStatus rc;
|
|
|
|
if( (rc=registerApplicationType(name,new_app)) ) return rc;
|
|
|
|
rapp=new_app;
|
|
protoDD->setApplType(rapp);
|
|
splitApplicationType(rapp,group,app);
|
|
|
|
// user gives me the protoDD, so I should not need to reference it.
|
|
// Warning, it the user does unreference() on it unknowningly, it will
|
|
// go away and cause big problems
|
|
|
|
// make sure that the currently registered destructor gets called
|
|
// before setting the new one, this should occur in protoDD.
|
|
|
|
// be sure to copy data from DD and create buffer that can be copied
|
|
// easily when user asks for a DD with proto
|
|
|
|
// important!! put destructor into each managed DD - what if atomic?
|
|
// protoDD->registerDestructor(new gddApplicationTypeDestructor(protoDD));
|
|
|
|
sz=protoDD->getTotalSizeBytes();
|
|
blk=new aitUint8[sz];
|
|
protoDD->flattenWithAddress(blk,sz,&tot);
|
|
attr_table[group][app].proto_size=sz;
|
|
attr_table[group][app].total_dds=tot;
|
|
protoDD->unreference();
|
|
|
|
attr_table[group][app].type=gddApplicationTypeProto;
|
|
attr_table[group][app].proto=(gdd*)blk;
|
|
attr_table[group][app].free_list=NULL;
|
|
|
|
// create the stupid mapping table - bad implementation for now
|
|
attr_table[group][app].map=new aitUint16[total_registered];
|
|
attr_table[group][app].map_size=total_registered;
|
|
for(i=0;i<total_registered;i++) attr_table[group][app].map[i]=0;
|
|
for(i=0;i<tot;i++)
|
|
{
|
|
x=attr_table[group][app].proto[i].applicationType();
|
|
if(x<total_registered) attr_table[group][app].map[x]=i;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
aitUint32 gddApplicationTypeTable::getApplicationType(const char* const name) const
|
|
{
|
|
unsigned i,j,rc;
|
|
|
|
for(i=0,rc=0;i<max_groups && attr_table[i] && rc==0;i++)
|
|
{
|
|
for(j=0; j<APPLTABLE_GROUP_SIZE && rc==0; j++)
|
|
{
|
|
if(attr_table[i][j].type!=gddApplicationTypeUndefined &&
|
|
strcmp(name,attr_table[i][j].app_name)==0)
|
|
rc=APPLTABLE_GROUP_SIZE*i+j;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
char* gddApplicationTypeTable::getName(aitUint32 rapp) const
|
|
{
|
|
aitUint32 group,app;
|
|
|
|
if(splitApplicationType(rapp,group,app)<0) return NULL;
|
|
if(!attr_table[group]) return NULL;
|
|
if(attr_table[group][app].type==gddApplicationTypeUndefined) return NULL;
|
|
|
|
return attr_table[group][app].app_name;
|
|
}
|
|
|
|
gddStatus gddApplicationTypeTable::mapAppToIndex(
|
|
aitUint32 c_app, aitUint32 m_app, aitUint32& x)
|
|
{
|
|
aitUint32 group,app;
|
|
gddStatus rc=0;
|
|
|
|
if((rc=splitApplicationType(c_app,group,app))==0)
|
|
{
|
|
if(attr_table[group][app].map && m_app<attr_table[group][app].map_size)
|
|
{
|
|
x=attr_table[group][app].map[m_app];
|
|
if(x==0 && c_app!=m_app)
|
|
rc=gddErrorNotDefined;
|
|
}
|
|
else
|
|
rc=gddErrorOutOfBounds;
|
|
}
|
|
// gddAutoPrint("gddAppTable::mapAppToIndex()",rc);
|
|
return rc;
|
|
}
|
|
|
|
gdd* gddApplicationTypeTable::getDD(aitUint32 rapp)
|
|
{
|
|
gdd* dd=NULL;
|
|
aitUint32 group,app;
|
|
aitUint8* blk;
|
|
|
|
if(splitApplicationType(rapp,group,app)<0) return NULL;
|
|
|
|
switch(attr_table[group][app].type)
|
|
{
|
|
case gddApplicationTypeProto:
|
|
attr_table[group][app].sem.take();
|
|
if( (dd=attr_table[group][app].free_list) )
|
|
{
|
|
//fprintf(stderr,"Popping a proto DD from list! %d %8.8x\n",app,dd);
|
|
attr_table[group][app].free_list=dd->next();
|
|
attr_table[group][app].sem.give();
|
|
}
|
|
else
|
|
{
|
|
attr_table[group][app].sem.give();
|
|
// copy the prototype
|
|
blk=new aitUint8[attr_table[group][app].proto_size];
|
|
// fprintf(stderr,"Creating a new proto DD! %d %8.8x\n",app,blk);
|
|
attr_table[group][app].proto->flattenWithAddress(blk,
|
|
attr_table[group][app].proto_size);
|
|
dd=(gdd*)blk;
|
|
}
|
|
dd->registerDestructor(new gddApplicationTypeDestructor(this));
|
|
dd->markManaged(); // must be sure to mark the thing as managed!
|
|
break;
|
|
case gddApplicationTypeNormal:
|
|
dd=new gdd(app);
|
|
// fprintf(stderr,"Creating a new normal DD! %d\n",app);
|
|
break;
|
|
case gddApplicationTypeUndefined: dd=NULL; break;
|
|
default: break;
|
|
}
|
|
|
|
return dd;
|
|
}
|
|
|
|
gddStatus gddApplicationTypeTable::freeDD(gdd* dd)
|
|
{
|
|
aitUint32 group,app,i;
|
|
gddStatus rc;
|
|
|
|
if((rc=splitApplicationType(dd->applicationType(),group,app))<0) return rc;
|
|
|
|
if(attr_table[group][app].type==gddApplicationTypeProto)
|
|
{
|
|
// this can be time consuming, clear out all user data from the DD
|
|
// this is done because we are allowed to register atomic protos
|
|
// that user can attach data to - which causes problems because
|
|
// the actual structure of the DD is unknown
|
|
|
|
for(i=1;i<attr_table[group][app].total_dds;i++)
|
|
{
|
|
dd[i].destroyData();
|
|
dd[i].setPrimType(attr_table[group][app].proto[i].primitiveType());
|
|
dd[i].setApplType(attr_table[group][app].proto[i].applicationType());
|
|
}
|
|
|
|
// fprintf(stderr,"Adding DD to free_list %d\n",app);
|
|
dd->setNext(attr_table[group][app].free_list);
|
|
attr_table[group][app].free_list=dd;
|
|
}
|
|
else if (attr_table[group][app].type==gddApplicationTypeNormal)
|
|
{
|
|
// fprintf(stderr,"freeDD a normal DD\n");
|
|
dd->unreference();
|
|
}
|
|
else {
|
|
fprintf ( stderr,"gddApplicationTypeTable::freeDD - unexpected DD type was %d\n",
|
|
attr_table[group][app].type );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
gddStatus gddApplicationTypeTable::storeValue(aitUint32 ap, aitUint32 uv)
|
|
{
|
|
aitUint32 group,app;
|
|
gddStatus rc=0;
|
|
|
|
if((rc=splitApplicationType(ap,group,app))<0) return rc;
|
|
if(attr_table[group]==NULL ||
|
|
attr_table[group][app].type==gddApplicationTypeUndefined)
|
|
rc=gddErrorNotDefined;
|
|
else
|
|
attr_table[group][app].user_value=uv;
|
|
|
|
gddAutoPrint("gddAppTable::storeValue()",rc);
|
|
return rc;
|
|
}
|
|
|
|
aitUint32 gddApplicationTypeTable::getValue(aitUint32 ap)
|
|
{
|
|
aitUint32 group,app;
|
|
if(splitApplicationType(ap,group,app)<0) return 0;
|
|
if(attr_table[group]==NULL ||
|
|
attr_table[group][app].type==gddApplicationTypeUndefined)
|
|
return 0;
|
|
|
|
return attr_table[group][app].user_value;
|
|
}
|
|
|
|
// ----------------------smart copy functions------------------------
|
|
// the source in the container we must walk through is this called
|
|
gddStatus gddApplicationTypeTable::copyDD_src(gdd& dest, const gdd& src)
|
|
{
|
|
gddStatus rc=0,s;
|
|
gddCursor cur;
|
|
gdd* dd;
|
|
aitIndex index;
|
|
|
|
// this could be done better (faster) if we did not always recurse for
|
|
// each GDD. I could have checked for type container before recursing.
|
|
|
|
if(src.isContainer())
|
|
{
|
|
gddContainer& cdd = (gddContainer&) src;
|
|
|
|
// go through src gdd and map app types to index into dest
|
|
cur=cdd.getCursor();
|
|
for(dd=cur.first();dd;dd=dd->next()) copyDD_src(dest,*dd);
|
|
}
|
|
else
|
|
{
|
|
// find src gdd in dest container and just do put()
|
|
s=mapAppToIndex(dest.applicationType(),src.applicationType(),index);
|
|
|
|
if(s==0)
|
|
rc=dest[index].put(&src);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
// the destination in the container we must walk through is this called
|
|
gddStatus gddApplicationTypeTable::copyDD_dest(gdd& dest, const gdd& src)
|
|
{
|
|
gddStatus rc=0,s;
|
|
gddCursor cur;
|
|
gdd* dd;
|
|
aitIndex index;
|
|
|
|
if(dest.isContainer())
|
|
{
|
|
gddContainer& cdd = (gddContainer&) dest;
|
|
// go through dest gdd and map app types to index into src
|
|
cur=cdd.getCursor();
|
|
for(dd=cur.first();dd;dd=dd->next()) copyDD_dest(*dd,src);
|
|
}
|
|
else
|
|
{
|
|
// find dest gdd in src container and just do put()
|
|
s=mapAppToIndex(src.applicationType(),dest.applicationType(),index);
|
|
|
|
if(s==0)
|
|
rc=dest.put(&src[index]);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
gddStatus gddApplicationTypeTable::smartCopy(gdd* dest, const gdd* src)
|
|
{
|
|
gddStatus rc = gddErrorNotAllowed;
|
|
|
|
// only works with managed containers because app table mapping
|
|
// feature is used.
|
|
|
|
if(dest->isContainer() && dest->isManaged())
|
|
rc=copyDD_src(*dest,*src);
|
|
else if(src->isContainer() && src->isManaged())
|
|
rc=copyDD_dest(*dest,*src);
|
|
else if(!src->isContainer() && !dest->isContainer()) {
|
|
if ( src->applicationType() == dest->applicationType() ) {
|
|
rc=dest->put(src); // both are not containers, let gdd handle it
|
|
}
|
|
else {
|
|
rc=gddErrorNotDefined;
|
|
}
|
|
}
|
|
|
|
gddAutoPrint("gddAppTable::smartCopy()",rc);
|
|
|
|
return rc;
|
|
}
|
|
|
|
// ----------------------smart reference functions------------------------
|
|
// the source in the container we must walk through is this called
|
|
gddStatus gddApplicationTypeTable::refDD_src(gdd& dest, const gdd& src)
|
|
{
|
|
gddStatus rc=0,s;
|
|
gddCursor cur;
|
|
gdd* dd;
|
|
aitIndex index;
|
|
|
|
// this could be done better (faster) if we did not always recurse for
|
|
// each GDD. I could have checked for type container before recursing.
|
|
|
|
if(src.isContainer())
|
|
{
|
|
gddContainer& cdd=(gddContainer&)src;
|
|
// go through src gdd and map app types to index into dest
|
|
cur=cdd.getCursor();
|
|
for(dd=cur.first();dd;dd=dd->next()) refDD_src(dest,*dd);
|
|
}
|
|
else
|
|
{
|
|
// find src gdd in dest container and just do put()
|
|
s=mapAppToIndex(dest.applicationType(),src.applicationType(),index);
|
|
|
|
if(s==0)
|
|
rc=dest[index].putRef(&src);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
// the destination in the container we must walk through is this called
|
|
gddStatus gddApplicationTypeTable::refDD_dest(gdd& dest, const gdd& src)
|
|
{
|
|
gddStatus rc=0,s;
|
|
gddCursor cur;
|
|
gdd* dd;
|
|
aitIndex index;
|
|
|
|
if(dest.isContainer())
|
|
{
|
|
gddContainer& cdd=(gddContainer&)dest;
|
|
// go through dest gdd and map app types to index into src
|
|
cur=cdd.getCursor();
|
|
for(dd=cur.first();dd;dd=dd->next()) refDD_dest(*dd,src);
|
|
}
|
|
else
|
|
{
|
|
// find dest gdd in src container and just do put()
|
|
s=mapAppToIndex(src.applicationType(),dest.applicationType(),index);
|
|
|
|
if(s==0)
|
|
rc=dest.putRef(&src[index]);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
gddStatus gddApplicationTypeTable::smartRef(gdd* dest, const gdd* src)
|
|
{
|
|
gddStatus rc=0;
|
|
|
|
// only works with managed containers because app table mapping
|
|
// feature is used.
|
|
|
|
if(dest->isContainer() && dest->isManaged())
|
|
rc=refDD_src(*dest,*src);
|
|
else if(src->isContainer() && src->isManaged())
|
|
rc=refDD_dest(*dest,*src);
|
|
else if(!src->isContainer() && !dest->isContainer())
|
|
rc=dest->putRef(src); // both are not containers, let gdd handle it
|
|
else
|
|
rc=gddErrorNotAllowed;
|
|
|
|
gddAutoPrint("gddAppTable::smartRef()",rc);
|
|
return rc;
|
|
}
|