Files
epics-base/src/std/link/lnkConst.c
2017-04-21 16:08:15 -04:00

581 lines
15 KiB
C

/*************************************************************************\
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* lnkConst.c */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "dbDefs.h"
#include "errlog.h"
#include "epicsAssert.h"
#include "epicsString.h"
#include "epicsTypes.h"
#include "dbAccessDefs.h"
#include "dbConvertFast.h"
#include "dbLink.h"
#include "dbJLink.h"
#include "epicsExport.h"
/* Change 'undef' to 'define' to turn on debug statements: */
#undef DEBUG_LINK
#ifdef DEBUG_LINK
int lnkConstDebug = 10;
# define IFDEBUG(n) \
if (lnkConstDebug >= n) /* block or statement */
#else
# define IFDEBUG(n) \
if(0) /* Compiler will elide the block or statement */
#endif
typedef long (*FASTCONVERT)();
typedef struct clink {
jlink jlink; /* embedded object */
int nElems;
enum {s0, si32, sf64, sc40, a0, ai32, af64, ac40} type;
union {
epicsInt32 scalar_integer; /* si32 */
epicsFloat64 scalar_double; /* sf64 */
char * scalar_string; /* s0 and sc40 */
void *pmem;
epicsInt32 *pintegers; /* ai32 */
epicsFloat64 *pdoubles; /* af64 */
char **pstrings; /* ac40 */
} value;
} clink;
static lset lnkConst_lset;
/*************************** jlif Routines **************************/
static jlink* lnkConst_alloc(short dbfType)
{
clink *clink = calloc(1, sizeof(struct clink));
IFDEBUG(10)
printf("lnkConst_alloc()\n");
clink->type = s0;
clink->nElems = 0;
clink->value.pmem = NULL;
IFDEBUG(10)
printf("lnkConst_alloc -> const@%p\n", clink);
return &clink->jlink;
}
static void lnkConst_free(jlink *pjlink)
{
clink *clink = CONTAINER(pjlink, struct clink, jlink);
IFDEBUG(10)
printf("lnkConst_free(const@%p) type=%d\n", pjlink, pvt->type);
switch (clink->type) {
int i;
case ac40:
for (i=0; i<clink->nElems; i++)
free(clink->value.pstrings[i]);
/* fall through */
case s0:
case sc40:
case ai32:
case af64:
free(clink->value.pmem);
break;
case a0:
case si32:
case sf64:
break;
}
free(clink);
}
static jlif_result lnkConst_integer(jlink *pjlink, long num)
{
clink *clink = CONTAINER(pjlink, struct clink, jlink);
IFDEBUG(10)
printf("lnkConst_integer(const@%p, %ld)\n", pjlink, num);
switch (clink->type) {
void *buf;
int nElems;
case s0:
clink->nElems = 1;
clink->type = si32;
clink->value.scalar_integer = num;
break;
case a0:
clink->type = ai32;
/* fall through */
case ai32:
nElems = clink->nElems + 1;
buf = realloc(clink->value.pmem, nElems * sizeof(epicsInt32));
if (!buf) return jlif_stop;
clink->value.pmem = buf;
clink->value.pintegers[clink->nElems] = num;
clink->nElems = nElems;
break;
case af64:
nElems = clink->nElems + 1;
buf = realloc(clink->value.pmem, nElems * sizeof(epicsFloat64));
if (!buf) return jlif_stop;
clink->value.pmem = buf;
clink->value.pdoubles[clink->nElems++] = num;
break;
case ac40:
errlogPrintf("lnkConst: Mixed data types in array\n");
/* fall through */
default:
return jlif_stop;
}
return jlif_continue;
}
static jlif_result lnkConst_boolean(jlink *pjlink, int val) {
IFDEBUG(10)
printf("lnkConst_boolean(const@%p, %d)\n", pjlink, val);
return lnkConst_integer(pjlink, val);
}
static jlif_result lnkConst_double(jlink *pjlink, double num)
{
clink *clink = CONTAINER(pjlink, struct clink, jlink);
IFDEBUG(10)
printf("lnkConst_double(const@%p, %g)\n", pjlink, num);
switch (clink->type) {
epicsFloat64 *f64buf;
int nElems, i;
case s0:
clink->nElems = 1;
clink->type = sf64;
clink->value.scalar_double = num;
break;
case a0:
clink->type = af64;
/* fall through */
case af64:
nElems = clink->nElems + 1;
f64buf = realloc(clink->value.pmem, nElems * sizeof(epicsFloat64));
if (!f64buf) return jlif_stop;
clink->value.pdoubles = f64buf;
clink->value.pdoubles[clink->nElems++] = num;
break;
case ai32: /* promote earlier ai32 values to af64 */
f64buf = calloc(clink->nElems + 1, sizeof(epicsFloat64));
if (!f64buf) return jlif_stop;
for (i = 0; i < clink->nElems; i++) {
f64buf[i] = clink->value.pintegers[i];
}
f64buf[clink->nElems++] = num;
free(clink->value.pmem);
clink->value.pdoubles = f64buf;
clink->type = af64;
break;
case ac40:
errlogPrintf("lnkConst: Mixed data types in array\n");
/* fall through */
default:
return jlif_stop;
}
return jlif_continue;
}
static jlif_result lnkConst_string(jlink *pjlink, const char *val, size_t len)
{
clink *clink = CONTAINER(pjlink, struct clink, jlink);
IFDEBUG(10)
printf("lnkConst_string(const@%p, \"%.*s\")\n", clink, (int) len, val);
switch (clink->type) {
char **vec;
int nElems;
case s0:
clink->nElems = 1;
clink->type = sc40;
free(clink->value.scalar_string);
clink->value.scalar_string = malloc(len+1);
if(!clink->value.scalar_string)
return jlif_stop;
strncpy(clink->value.scalar_string, val, len);
clink->value.scalar_string[len] = '\0';
break;
case a0:
clink->type = ac40;
/* fall thorough */
case ac40:
nElems = clink->nElems + 1;
vec = realloc(clink->value.pmem, nElems * sizeof(char *));
if (!vec)
return jlif_stop;
vec[clink->nElems] = malloc(len+1);
if(!vec[clink->nElems])
return jlif_stop;
strncpy(vec[clink->nElems], val, len);
vec[clink->nElems][len] = '\0';
clink->value.pstrings = vec;
clink->nElems++;
break;
case af64:
case ai32:
errlogPrintf("lnkConst: Mixed data types in array\n");
/* fall thorough */
default:
return jlif_stop;
}
return jlif_continue;
}
static jlif_result lnkConst_start_array(jlink *pjlink)
{
clink *clink = CONTAINER(pjlink, struct clink, jlink);
IFDEBUG(10)
printf("lnkConst_start_array(const@%p)\n", pjlink);
if (clink->type != s0) {
errlogPrintf("lnkConst: Embedded array value\n");
return jlif_stop;
}
clink->type = a0;
return jlif_continue;
}
static jlif_result lnkConst_end_array(jlink *pjlink)
{
IFDEBUG(10)
printf("lnkConst_end_array(const@%p)\n", pjlink);
return jlif_continue;
}
static struct lset* lnkConst_get_lset(const jlink *pjlink)
{
IFDEBUG(10)
printf("lnkConst_get_lset(const@%p)\n", pjlink);
return &lnkConst_lset;
}
/* Report outputs:
* 'const': integer 21
* 'const': double 5.23
* 'const': string "something"
* 'const': array of 999 integers
* [1, 2, 3]
* 'const': array of 1 double
* [1.2345]
* 'const': array of 2 strings
* ["hello", "world"]
*
* Array values are only printed at level 2
* because there might be quite a few of them.
*/
static void lnkConst_report(const jlink *pjlink, int level, int indent)
{
clink *clink = CONTAINER(pjlink, struct clink, jlink);
const char * const type_names[4] = {
"bug", "integer", "double", "string"
};
const char * const dtype = type_names[clink->type & 3];
IFDEBUG(10)
printf("lnkConst_report(const@%p)\n", clink);
if (clink->type > a0) {
const char * const plural = clink->nElems > 1 ? "s" : "";
printf("%*s'const': array of %d %s%s", indent, "",
clink->nElems, dtype, plural);
if (level < 2) {
putchar('\n');
}
else {
int i;
switch (clink->type) {
case ai32:
printf("\n%*s[%d", indent+2, "", clink->value.pintegers[0]);
for (i = 1; i < clink->nElems; i++) {
printf(", %d", clink->value.pintegers[i]);
}
break;
case af64:
printf("\n%*s[%g", indent+2, "", clink->value.pdoubles[0]);
for (i = 1; i < clink->nElems; i++) {
printf(", %g", clink->value.pdoubles[i]);
}
break;
case ac40:
printf("\n%*s[\"%s\"", indent+2, "", clink->value.pstrings[0]);
for (i = 1; i < clink->nElems; i++) {
printf(", \"%s\"", clink->value.pstrings[i]);
}
break;
default:
break;
}
printf("]\n");
}
return;
}
printf("%*s'const': %s", indent, "", dtype);
switch (clink->type) {
case si32:
printf(" %d\n", clink->value.scalar_integer);
return;
case sf64:
printf(" %g\n", clink->value.scalar_double);
return;
case sc40:
printf(" \"%s\"\n", clink->value.scalar_string);
return;
default:
printf(" -- type=%d\n", clink->type);
return;
}
}
/*************************** lset Routines **************************/
static void lnkConst_remove(struct dbLocker *locker, struct link *plink)
{
clink *clink = CONTAINER(plink->value.json.jlink, struct clink, jlink);
IFDEBUG(10)
printf("lnkConst_remove(const@%p)\n", clink);
lnkConst_free(plink->value.json.jlink);
}
static long lnkConst_loadScalar(struct link *plink, short dbrType, void *pbuffer)
{
clink *clink = CONTAINER(plink->value.json.jlink, struct clink, jlink);
long status;
IFDEBUG(10)
printf("lnkConst_loadScalar(const@%p, %d, %p)\n",
clink, dbrType, pbuffer);
switch (clink->type) {
case si32:
status = dbFastPutConvertRoutine[DBF_LONG][dbrType]
(&clink->value.scalar_integer, pbuffer, NULL);
break;
case sf64:
status = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType]
(&clink->value.scalar_double, pbuffer, NULL);
break;
case sc40:
status = dbFastPutConvertRoutine[DBF_STRING][dbrType]
(clink->value.scalar_string, pbuffer, NULL);
break;
case ai32:
status = dbFastPutConvertRoutine[DBF_LONG][dbrType]
(clink->value.pintegers, pbuffer, NULL);
break;
case af64:
status = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType]
(clink->value.pdoubles, pbuffer, NULL);
break;
case ac40:
status = dbFastPutConvertRoutine[DBF_STRING][dbrType]
(clink->value.pstrings[0], pbuffer, NULL);
break;
default:
status = S_db_badField;
break;
}
return status;
}
static long lnkConst_loadLS(struct link *plink, char *pbuffer, epicsUInt32 size,
epicsUInt32 *plen)
{
clink *clink = CONTAINER(plink->value.json.jlink, struct clink, jlink);
const char *pstr;
IFDEBUG(10)
printf("lnkConst_loadLS(const@%p, %p, %d, %d)\n",
clink, pbuffer, size, *plen);
if(!size) return 0;
switch (clink->type) {
case sc40:
pstr = clink->value.scalar_string;
break;
case ac40:
pstr = clink->value.pstrings[0];
break;
default:
return S_db_badField;
}
strncpy(pbuffer, pstr, --size);
pbuffer[size] = 0;
*plen = (epicsUInt32) strlen(pbuffer) + 1;
return 0;
}
static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer,
long *pnReq)
{
clink *clink = CONTAINER(plink->value.json.jlink, struct clink, jlink);
short dbrSize = dbValueSize(dbrType);
char *pdest = pbuffer;
int nElems = clink->nElems;
FASTCONVERT conv;
long status;
IFDEBUG(10)
printf("lnkConst_loadArray(const@%p, %d, %p, (%ld))\n",
clink, dbrType, pbuffer, *pnReq);
if (nElems > *pnReq)
nElems = *pnReq;
switch (clink->type) {
int i;
case si32:
status = dbFastPutConvertRoutine[DBF_LONG][dbrType]
(&clink->value.scalar_integer, pdest, NULL);
break;
case sf64:
status = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType]
(&clink->value.scalar_double, pdest, NULL);
break;
case sc40:
status = dbFastPutConvertRoutine[DBF_STRING][dbrType]
(clink->value.scalar_string, pbuffer, NULL);
break;
case ai32:
conv = dbFastPutConvertRoutine[DBF_LONG][dbrType];
for (i = 0; i < nElems; i++) {
conv(&clink->value.pintegers[i], pdest, NULL);
pdest += dbrSize;
}
status = 0;
break;
case af64:
conv = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType];
for (i = 0; i < nElems; i++) {
conv(&clink->value.pdoubles[i], pdest, NULL);
pdest += dbrSize;
}
status = 0;
break;
case ac40:
conv = dbFastPutConvertRoutine[DBF_STRING][dbrType];
for (i = 0; i < nElems; i++) {
conv(clink->value.pstrings[i], pdest, NULL);
pdest += dbrSize;
}
status = 0;
break;
default:
status = S_db_badField;
}
*pnReq = nElems;
return status;
}
static long lnkConst_getNelements(const struct link *plink, long *nelements)
{
IFDEBUG(10)
printf("lnkConst_getNelements(const@%p, (%ld))\n",
plink->value.json.jlink, *nelements);
*nelements = 0;
return 0;
}
static long lnkConst_getValue(struct link *plink, short dbrType, void *pbuffer,
long *pnRequest)
{
IFDEBUG(10)
printf("lnkConst_getValue(const@%p, %d, %p, ... (%ld))\n",
plink->value.json.jlink, dbrType, pbuffer, *pnRequest);
if (pnRequest)
*pnRequest = 0;
return 0;
}
/************************* Interface Tables *************************/
static lset lnkConst_lset = {
1, 0, /* Constant, not Volatile */
NULL, lnkConst_remove,
lnkConst_loadScalar, lnkConst_loadLS, lnkConst_loadArray, NULL,
NULL, lnkConst_getNelements, lnkConst_getValue,
NULL, NULL, NULL,
NULL, NULL,
NULL, NULL,
NULL, NULL,
NULL, NULL
};
static jlif lnkConstIf = {
"const", lnkConst_alloc, lnkConst_free,
NULL, lnkConst_boolean, lnkConst_integer, lnkConst_double, lnkConst_string,
NULL, NULL, NULL,
lnkConst_start_array, lnkConst_end_array,
NULL, lnkConst_get_lset,
lnkConst_report, NULL
};
epicsExportAddress(jlif, lnkConstIf);