From 9d4a90ac34cc6decc0e12837b5bab45586e8c518 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 22 Jun 2017 14:29:04 -0500 Subject: [PATCH] Fix link initialization from hex constants Fixes LP: #1699445 Includes regression tests (some of which will fail on 3.16) --- src/ioc/db/dbLink.c | 101 ++++++++++++++++++++++++++++++++- src/std/rec/test/regressHex.db | 28 +++++++++ src/std/rec/test/regressTest.c | 55 +++++++++++++----- 3 files changed, 169 insertions(+), 15 deletions(-) create mode 100644 src/std/rec/test/regressHex.db diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c index 822daf784..af135cff6 100644 --- a/src/ioc/db/dbLink.c +++ b/src/ioc/db/dbLink.c @@ -23,6 +23,7 @@ #include "cvtFast.h" #include "dbDefs.h" #include "ellLib.h" +#include "epicsStdlib.h" #include "epicsThread.h" #include "epicsTime.h" #include "errlog.h" @@ -96,6 +97,103 @@ static const char * link_field_name(const struct link *plink) /***************************** Constant Links *****************************/ +/* Convert functions */ + +/* The difference between these and dbFastConvert is that constants + * may contain hex numbers, whereas database conversions can't. + */ + +/* Copy to STRING */ +static long cvt_st_st(const char *from, void *pfield, const dbAddr *paddr) +{ + char *to = pfield; + size_t size; + + if (paddr && paddr->field_size < MAX_STRING_SIZE) { + size = paddr->field_size - 1; + } else { + size = MAX_STRING_SIZE - 1; + } + strncpy(to, from, size); + to[size] = 0; + return 0; +} + +/* Most integer conversions are identical */ +#define cvt_st_int(TYPE) static long \ +cvt_st_ ## TYPE(const char *from, void *pfield, const dbAddr *paddr) { \ + epics##TYPE *to = pfield; \ + char *end; \ +\ + if (*from == 0) { \ + *to = 0; \ + return 0; \ + } \ + return epicsParse##TYPE(from, to, 0, &end); \ +} + +/* Instanciate for CHAR, UCHAR, SHORT, USHORT and LONG */ +cvt_st_int(Int8) +cvt_st_int(UInt8) +cvt_st_int(Int16) +cvt_st_int(UInt16) +cvt_st_int(Int32) + +/* Conversion for ULONG is different... */ +static long cvt_st_UInt32(const char *from, void *pfield, const dbAddr *paddr) +{ + epicsUInt32 *to = pfield; + char *end; + long status; + + if (*from == 0) { + *to = 0; + return 0; + } + status = epicsParseUInt32(from, to, 0, &end); + if (status == S_stdlib_noConversion || + (!status && (*end == '.' || *end == 'e' || *end == 'E'))) { + /* + * Convert via double so numbers like 1.0e3 convert properly. + * db_access pretends ULONG fields are DOUBLE. + */ + double dval; + + status = epicsParseFloat64(from, &dval, &end); + if (!status && + dval >=0 && + dval <= ULONG_MAX) + *to = dval; + } + return status; +} + +/* Float conversions are identical */ +#define cvt_st_float(TYPE) static long \ +cvt_st_ ## TYPE(const char *from, void *pfield, const dbAddr *paddr) { \ + epics##TYPE *to = pfield; \ + char *end; \ +\ + if (*from == 0) { \ + *to = 0; \ + return 0; \ + } \ + return epicsParse##TYPE(from, to, &end); \ +} + +/* Instanciate for FLOAT32 and FLOAT64 */ +cvt_st_float(Float32) +cvt_st_float(Float64) + + +static long (*convert[DBF_DOUBLE+1])(const char *, void *, const dbAddr *) = { + cvt_st_st, + cvt_st_Int8, cvt_st_UInt8, + cvt_st_Int16, cvt_st_UInt16, + cvt_st_Int32, cvt_st_UInt32, + cvt_st_Float32, cvt_st_Float64 +}; + static long dbConstLoadLink(struct link *plink, short dbrType, void *pbuffer) { if (!plink->value.constantStr) @@ -105,8 +203,7 @@ static long dbConstLoadLink(struct link *plink, short dbrType, void *pbuffer) if (dbrType== DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE) dbrType = DBF_USHORT; - return dbFastPutConvertRoutine[DBR_STRING][dbrType] - (plink->value.constantStr, pbuffer, NULL); + return convert[dbrType](plink->value.constantStr, pbuffer, NULL); } static long dbConstGetNelements(const struct link *plink, long *nelements) diff --git a/src/std/rec/test/regressHex.db b/src/std/rec/test/regressHex.db new file mode 100644 index 000000000..83f6916e9 --- /dev/null +++ b/src/std/rec/test/regressHex.db @@ -0,0 +1,28 @@ +record(ai, "ai1") { + field(INP, 0x10) +} +record(longin, "li1") { + field(INP, 0x10) +} +record(mbbiDirect, "mi1") { + field(INP, 0x10) + field(NOBT, 8) +} +record(aSub, "as1") { + field(INPA, 0x10) + field(FTA, "CHAR") + field(INPB, 0x10) + field(FTB, "UCHAR") + field(INPC, 0x10) + field(FTC, "SHORT") + field(INPD, 0x10) + field(FTD, "USHORT") + field(INPE, 0x10) + field(FTE, "LONG") + field(INPF, 0x10) + field(FTF, "ULONG") + field(INPG, 0x10) + field(FTG, "FLOAT") + field(INPH, 0x10) + field(FTH, "DOUBLE") +} diff --git a/src/std/rec/test/regressTest.c b/src/std/rec/test/regressTest.c index ccbe53189..8028630b3 100644 --- a/src/std/rec/test/regressTest.c +++ b/src/std/rec/test/regressTest.c @@ -13,11 +13,24 @@ #include /* - * Test the some identified regressions + * Tests for specific regressions */ void regressTest_registerRecordDeviceDriver(struct dbBase *); +static +void startRegressTestIoc(const char *dbfile) +{ + testdbPrepare(); + testdbReadDatabase("regressTest.dbd", NULL, NULL); + regressTest_registerRecordDeviceDriver(pdbbase); + testdbReadDatabase(dbfile, NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); +} + /* * https://bugs.launchpad.net/epics-base/+bug/1577108 */ @@ -28,21 +41,11 @@ void testArrayLength1(void) calcoutRecord *precco; double *pbuf; - testdbPrepare(); - - testdbReadDatabase("regressTest.dbd", NULL, NULL); - - regressTest_registerRecordDeviceDriver(pdbbase); - - testdbReadDatabase("regressArray1.db", NULL, NULL); + startRegressTestIoc("regressArray1.db"); precwf = (waveformRecord*)testdbRecordPtr("wf"); precco = (calcoutRecord*)testdbRecordPtr("co"); - eltc(0); - testIocInitOk(); - eltc(1); - dbScanLock((dbCommon*)precwf); pbuf = (double*)precwf->bptr; dbScanUnlock((dbCommon*)precwf); @@ -65,9 +68,35 @@ void testArrayLength1(void) testdbCleanup(); } +/* + * https://bugs.launchpad.net/epics-base/+bug/1699445 + */ +static +void testHexConstantLinks(void) +{ + startRegressTestIoc("regressHex.db"); + + testdbGetFieldEqual("ai1", DBR_LONG, 0x10); + testdbGetFieldEqual("li1", DBR_LONG, 0x10); + testdbGetFieldEqual("mi1", DBR_LONG, 0x10); + testdbGetFieldEqual("as1.A", DBR_LONG, 0x10); + testdbGetFieldEqual("as1.B", DBR_LONG, 0x10); + testdbGetFieldEqual("as1.C", DBR_LONG, 0x10); + testdbGetFieldEqual("as1.D", DBR_LONG, 0x10); + testdbGetFieldEqual("as1.E", DBR_LONG, 0x10); + testdbGetFieldEqual("as1.F", DBR_LONG, 0x10); + testdbGetFieldEqual("as1.G", DBR_LONG, 0x10); + testdbGetFieldEqual("as1.H", DBR_LONG, 0x10); + + testIocShutdownOk(); + testdbCleanup(); +} + + MAIN(regressTest) { - testPlan(5); + testPlan(16); testArrayLength1(); + testHexConstantLinks(); return testDone(); }