libCom: Add routines for parsing strings into epicsTypes
Adds new routines for converting numeric strings into the standard epicsInt8, epicsUInt8, epicsInt16, epicsUInt16, epicsInt32 and epicsUInt32 types, along with Long, ULong, Double and Float. These all provide error checking and detection of extraneous characters. The epicsScanDouble and epicsScanFloat routines originally in epicsStdlib.h are replaced by macros that call the epicsParse routine, and this also provides epicsScanLong and epicsScanULong to match. A test file is added to ensure conversions work properly and report appropriate errors. This file also checks the native strtod() routine if not used to check whether the epicsStrtod() code is required on this platform.
This commit is contained in:
@@ -23,6 +23,7 @@ Com_SRCS += errSymTbl.c
|
||||
#
|
||||
ERR_S_FILES += $(LIBCOM)/osi/devLib.h
|
||||
ERR_S_FILES += $(LIBCOM)/as/asLib.h
|
||||
ERR_S_FILES += $(LIBCOM)/misc/epicsStdlib.h
|
||||
ERR_S_FILES += $(SRC)/ioc/db/dbAccessDefs.h
|
||||
ERR_S_FILES += $(SRC)/ioc/dbStatic/devSup.h
|
||||
ERR_S_FILES += $(SRC)/ioc/dbStatic/drvSup.h
|
||||
|
||||
@@ -48,6 +48,7 @@ extern "C" {
|
||||
#define M_casApp (524 <<16) /*CA server application*/
|
||||
#define M_bucket (525 <<16) /*Bucket Hash*/
|
||||
#define M_gddFuncTbl (526 <<16) /*gdd jump table*/
|
||||
#define M_stdlib (527 <<16) /*EPICS Standard library*/
|
||||
|
||||
epicsShareFunc void epicsShareAPI errSymLookup(long status, char *pBuf, unsigned bufLength);
|
||||
epicsShareFunc void epicsShareAPI errSymTest(unsigned short modnum, unsigned short begErrNum, unsigned short endErrNum);
|
||||
|
||||
@@ -1,50 +1,248 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2009 UChicago Argonna LLC, as Operator of Argonne
|
||||
* Copyright (c) 2012 UChicago Argonna LLC, 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 is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*epicsStdlib.c*/
|
||||
/*Author: Eric Norum */
|
||||
/* $Revision-Id$ */
|
||||
/* Authors: Eric Norum & Andrew Johnson */
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <float.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "epicsMath.h"
|
||||
#include "epicsStdlib.h"
|
||||
#include "epicsString.h"
|
||||
#include "epicsConvert.h"
|
||||
|
||||
|
||||
epicsShareFunc int epicsScanDouble(const char *str, double *dest)
|
||||
/* These are the conversion primitives */
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseLong(const char *str, long *to, int base)
|
||||
{
|
||||
int c;
|
||||
char *endp;
|
||||
double dtmp;
|
||||
long value;
|
||||
|
||||
while ((c = *str) && isspace(c))
|
||||
++str;
|
||||
|
||||
errno = 0;
|
||||
value = strtol(str, &endp, base);
|
||||
|
||||
dtmp = epicsStrtod(str, &endp);
|
||||
if (endp == str)
|
||||
return 0;
|
||||
*dest = dtmp;
|
||||
return 1;
|
||||
return S_stdlib_noConversion;
|
||||
if (errno == EINVAL) /* Not universally supported */
|
||||
return S_stdlib_badBase;
|
||||
|
||||
while ((c = *endp) && isspace(c))
|
||||
++endp;
|
||||
if (c)
|
||||
return S_stdlib_extraneous;
|
||||
|
||||
if (errno == ERANGE)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
epicsShareFunc int epicsScanFloat(const char *str, float *dest)
|
||||
epicsShareFunc int
|
||||
epicsParseULong(const char *str, unsigned long *to, int base)
|
||||
{
|
||||
int c;
|
||||
char *endp;
|
||||
double dtmp;
|
||||
unsigned long value;
|
||||
|
||||
while ((c = *str) && isspace(c))
|
||||
++str;
|
||||
|
||||
errno = 0;
|
||||
value = strtoul(str, &endp, base);
|
||||
|
||||
dtmp = epicsStrtod(str, &endp);
|
||||
if (endp == str)
|
||||
return 0;
|
||||
*dest = (float)dtmp;
|
||||
return 1;
|
||||
return S_stdlib_noConversion;
|
||||
if (errno == EINVAL) /* Not universally supported */
|
||||
return S_stdlib_badBase;
|
||||
|
||||
while ((c = *endp) && isspace(c))
|
||||
++endp;
|
||||
if (c)
|
||||
return S_stdlib_extraneous;
|
||||
|
||||
if (errno == ERANGE)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Systems with a working strtod() just #define epicsStrtod strtod */
|
||||
epicsShareFunc int
|
||||
epicsParseDouble(const char *str, double *to)
|
||||
{
|
||||
int c;
|
||||
char *endp;
|
||||
double value;
|
||||
|
||||
while ((c = *str) && isspace(c))
|
||||
++str;
|
||||
|
||||
errno = 0;
|
||||
value = epicsStrtod(str, &endp);
|
||||
if (endp == str)
|
||||
return S_stdlib_noConversion;
|
||||
|
||||
while ((c = *endp) && isspace(c))
|
||||
++endp;
|
||||
if (c)
|
||||
return S_stdlib_extraneous;
|
||||
|
||||
if (errno == ERANGE)
|
||||
return (value == 0) ? S_stdlib_underflow : S_stdlib_overflow;
|
||||
|
||||
*to = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* These call the primitives */
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseInt8(const char *str, epicsInt8 *to, int base)
|
||||
{
|
||||
long value;
|
||||
int status = epicsParseLong(str, &value, base);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (value < -0x80 || value > 0x7f)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseUInt8(const char *str, epicsUInt8 *to, int base)
|
||||
{
|
||||
unsigned long value;
|
||||
int status = epicsParseULong(str, &value, base);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (value > 0xff)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseInt16(const char *str, epicsInt16 *to, int base)
|
||||
{
|
||||
long value;
|
||||
int status = epicsParseLong(str, &value, base);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (value < -0x8000 || value > 0x7fff)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseUInt16(const char *str, epicsUInt16 *to, int base)
|
||||
{
|
||||
unsigned long value;
|
||||
int status = epicsParseULong(str, &value, base);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (value > 0xffff)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseInt32(const char *str, epicsInt32 *to, int base)
|
||||
{
|
||||
long value;
|
||||
int status = epicsParseLong(str, &value, base);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
#if (LONG_MAX > 0x7fffffff)
|
||||
if (value < -0x80000000L || value > 0x7fffffffL)
|
||||
return S_stdlib_overflow;
|
||||
#endif
|
||||
|
||||
*to = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseUInt32(const char *str, epicsUInt32 *to, int base)
|
||||
{
|
||||
unsigned long value;
|
||||
int status = epicsParseULong(str, &value, base);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
#if (ULONG_MAX > 0xffffffff)
|
||||
if (value > 0xffffffffL)
|
||||
return S_stdlib_overflow;
|
||||
#endif
|
||||
|
||||
*to = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseFloat(const char *str, float *to)
|
||||
{
|
||||
double value, abs;
|
||||
int status = epicsParseDouble(str, &value);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
abs = fabs(value);
|
||||
if (value > 0 && abs <= FLT_MIN)
|
||||
return S_stdlib_underflow;
|
||||
if (finite(value) && abs >= FLT_MAX)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* If strtod() works properly, osdStrtod.h defines this macro:
|
||||
* #define epicsStrtod strtod
|
||||
*
|
||||
* If strtod() is broken, osdStrtod.h defines this prototype:
|
||||
* epicsShareFunc double epicsStrtod(const char *str, char **endp);
|
||||
*/
|
||||
|
||||
#ifndef epicsStrtod
|
||||
epicsShareFunc double epicsStrtod(const char *str, char **endp)
|
||||
epicsShareFunc double
|
||||
epicsStrtod(const char *str, char **endp)
|
||||
{
|
||||
const char *cp = str;
|
||||
int negative = 0;
|
||||
@@ -60,6 +258,12 @@ epicsShareFunc double epicsStrtod(const char *str, char **endp)
|
||||
cp++;
|
||||
}
|
||||
|
||||
if (epicsStrnCaseCmp("0x", cp, 2) == 0) {
|
||||
if (negative)
|
||||
return strtol(str, endp, 16);
|
||||
else
|
||||
return strtoul(str, endp, 16);
|
||||
}
|
||||
if (!isalpha((int)*cp))
|
||||
return strtod(str, endp);
|
||||
|
||||
|
||||
@@ -1,26 +1,67 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* Copyright (c) 2012 UChicago Argonne LLC, 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
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*epicsStdlib.h*/
|
||||
/*Author: Eric Norum */
|
||||
/* epicsStdlib.h */
|
||||
/* Author: Eric Norum */
|
||||
|
||||
#include <shareLib.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "shareLib.h"
|
||||
#include "osdStrtod.h"
|
||||
#include "epicsTypes.h"
|
||||
#include "errMdef.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
epicsShareFunc int epicsScanDouble(const char *str, double *dest);
|
||||
epicsShareFunc int epicsScanFloat(const char *str, float *dest);
|
||||
#define S_stdlib_noConversion (M_stdlib | 1) /* No digits to convert */
|
||||
#define S_stdlib_extraneous (M_stdlib | 2) /* Extraneous characters */
|
||||
#define S_stdlib_underflow (M_stdlib | 3) /* Too small to represent */
|
||||
#define S_stdlib_overflow (M_stdlib | 4) /* Too large to represent */
|
||||
#define S_stdlib_badBase (M_stdlib | 5) /* Number base not supported */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <osdStrtod.h>
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseLong(const char *str, long *to, int base);
|
||||
epicsShareFunc int
|
||||
epicsParseULong(const char *str, unsigned long *to, int base);
|
||||
epicsShareFunc int
|
||||
epicsParseDouble(const char *str, double *to);
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseFloat(const char *str, float *to);
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseInt8(const char *str, epicsInt8 *to, int base);
|
||||
epicsShareFunc int
|
||||
epicsParseUInt8(const char *str, epicsUInt8 *to, int base);
|
||||
epicsShareFunc int
|
||||
epicsParseInt16(const char *str, epicsInt16 *to, int base);
|
||||
epicsShareFunc int
|
||||
epicsParseUInt16(const char *str, epicsUInt16 *to, int base);
|
||||
|
||||
epicsShareFunc int
|
||||
epicsParseInt32(const char *str, epicsInt32 *to, int base);
|
||||
epicsShareFunc int
|
||||
epicsParseUInt32(const char *str, epicsUInt32 *to, int base);
|
||||
|
||||
#define epicsParseFloat32(str, to) epicsParseFloat(str, to)
|
||||
#define epicsParseFloat64(str, to) epicsParseDouble(str, to)
|
||||
|
||||
/* These macros return 1 if successful, 0 on failure.
|
||||
* This is analagous to the return value from sscanf()
|
||||
*/
|
||||
#define epicsScanLong(str, to, base) !epicsParseLong(str, to, base)
|
||||
#define epicsScanULong(str, to, base) !epicsParseULong(str, to, base)
|
||||
#define epicsScanFloat(str, to) !epicsParseFloat(str, to)
|
||||
#define epicsScanDouble(str, to) !epicsParseDouble(str, to)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -54,6 +54,11 @@ epicsStdioTest_SRCS += epicsStdioTest.c
|
||||
testHarness_SRCS += epicsStdioTest.c
|
||||
TESTS += epicsStdioTest
|
||||
|
||||
TESTPROD_HOST += epicsStdlibTest
|
||||
epicsStdlibTest_SRCS += epicsStdlibTest.c
|
||||
testHarness_SRCS += epicsStdlibTest.c
|
||||
TESTS += epicsStdlibTest
|
||||
|
||||
TESTPROD_HOST += epicsStringTest
|
||||
epicsStringTest_SRCS += epicsStringTest.c
|
||||
testHarness_SRCS += epicsStringTest.c
|
||||
|
||||
@@ -28,6 +28,7 @@ int epicsMathTest(void);
|
||||
int epicsMessageQueueTest(void);
|
||||
int epicsMutexTest(void);
|
||||
int epicsStdioTest(void);
|
||||
int epicsStdlibTest(void);
|
||||
int epicsStringTest(void);
|
||||
int epicsThreadOnceTest(void);
|
||||
int epicsThreadPriorityTest(void);
|
||||
@@ -76,6 +77,8 @@ void epicsRunLibComTests(void)
|
||||
|
||||
runTest(epicsStdioTest);
|
||||
|
||||
runTest(epicsStdlibTest);
|
||||
|
||||
runTest(epicsStringTest);
|
||||
|
||||
runTest(epicsThreadOnceTest);
|
||||
|
||||
440
src/libCom/test/epicsStdlibTest.c
Normal file
440
src/libCom/test/epicsStdlibTest.c
Normal file
@@ -0,0 +1,440 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2012 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.
|
||||
\*************************************************************************/
|
||||
/* epicsStdlibTest.c
|
||||
*
|
||||
* Author Andrew Johnson
|
||||
*/
|
||||
|
||||
#include <float.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "epicsStdlib.h"
|
||||
#include "epicsMath.h"
|
||||
#include "epicsUnitTest.h"
|
||||
#include "testMain.h"
|
||||
|
||||
/* Implement the epicsParseDouble() API for checking the native stdtod()
|
||||
* so we can tell the user if our epicsStrtod() wrapper is unnecessary.
|
||||
*/
|
||||
int
|
||||
parseStrtod(const char *str, double *to)
|
||||
{
|
||||
int c;
|
||||
char *endp;
|
||||
double dtmp;
|
||||
|
||||
while ((c = *str) && isspace(c))
|
||||
++str;
|
||||
|
||||
errno = 0;
|
||||
dtmp = strtod(str, &endp);
|
||||
if (endp == str)
|
||||
return S_stdlib_noConversion;
|
||||
|
||||
while ((c = *endp) && isspace(c))
|
||||
++endp;
|
||||
if (c)
|
||||
return S_stdlib_extraneous;
|
||||
|
||||
if (dtmp == 0 && errno == ERANGE)
|
||||
return S_stdlib_underflow;
|
||||
if (fabs(dtmp) == HUGE_VAL && errno == ERANGE)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = dtmp;
|
||||
return 0;
|
||||
}
|
||||
#define scanStrtod(str, to) !parseStrtod(str, to)
|
||||
|
||||
|
||||
MAIN(epicsStdlibTest)
|
||||
{
|
||||
unsigned long u;
|
||||
long l;
|
||||
double d;
|
||||
float f;
|
||||
epicsInt8 i8;
|
||||
epicsUInt8 u8;
|
||||
epicsInt16 i16;
|
||||
epicsUInt16 u16;
|
||||
epicsInt32 i32;
|
||||
epicsUInt32 u32;
|
||||
|
||||
testPlan(135);
|
||||
|
||||
testOk(epicsParseLong("", &l, 0) == S_stdlib_noConversion,
|
||||
"Long '' => noConversion");
|
||||
testOk(epicsParseULong("", &u, 0) == S_stdlib_noConversion,
|
||||
"ULong '' => noConversion");
|
||||
testOk(epicsParseFloat("", &f) == S_stdlib_noConversion,
|
||||
"Float '' => noConversion");
|
||||
testOk(epicsParseDouble("", &d) == S_stdlib_noConversion,
|
||||
"Double '' => noConversion");
|
||||
|
||||
testOk(epicsParseLong("\t \n", &l, 0) == S_stdlib_noConversion,
|
||||
"Long '\\t 1\\n' => noConversion");
|
||||
testOk(epicsParseULong("\t \n", &u, 0) == S_stdlib_noConversion,
|
||||
"ULong '\\t 1\\n' => noConversion");
|
||||
testOk(epicsParseFloat("\t \n", &f) == S_stdlib_noConversion,
|
||||
"Float '\\t 1\\n' => noConversion");
|
||||
testOk(epicsParseDouble("\t \n", &d) == S_stdlib_noConversion,
|
||||
"Double '\\t 1\\n' => noConversion");
|
||||
|
||||
testOk(epicsParseLong("!", &l, 0) == S_stdlib_noConversion,
|
||||
"Long '!' => noConversion");
|
||||
testOk(epicsParseULong("!", &u, 0) == S_stdlib_noConversion,
|
||||
"ULong '!' => noConversion");
|
||||
testOk(epicsParseFloat("!", &f) == S_stdlib_noConversion,
|
||||
"Float '!' => noConversion");
|
||||
testOk(epicsParseDouble("!", &d) == S_stdlib_noConversion,
|
||||
"Double '!' => noConversion");
|
||||
|
||||
testOk(epicsScanLong("0", &l, 0) && l == 0, "Long '0'");
|
||||
testOk(epicsScanULong("0", &u, 0) && u == 0, "ULong '0'");
|
||||
testOk(epicsScanFloat("0", &f) && f == 0, "Float '0'");
|
||||
testOk(epicsScanDouble("0", &d) && d == 0, "Double '0'");
|
||||
|
||||
testOk(epicsScanLong("\t 1\n", &l, 0) && l == 1, "Long '\\t 1\\n'");
|
||||
testOk(epicsScanULong("\t 1\n", &u, 0) && u == 1, "ULong '\\t 1\\n'");
|
||||
testOk(epicsScanFloat("\t 1\n", &f) && f == 1, "Float '\\t 1\\n'");
|
||||
testOk(epicsScanDouble("\t 1\n", &d) && d == 1, "Double '\\t 1\\n'");
|
||||
|
||||
testOk(epicsParseLong("2!", &l, 0) == S_stdlib_extraneous,
|
||||
"Long '2!' => extraneous");
|
||||
testOk(epicsParseULong("2!", &u, 0) == S_stdlib_extraneous,
|
||||
"ULong '2!' => extraneous");
|
||||
testOk(epicsParseFloat("2!", &f) == S_stdlib_extraneous,
|
||||
"Float '2!' => extraneous");
|
||||
testOk(epicsParseDouble("2!", &d) == S_stdlib_extraneous,
|
||||
"Double '2!' => extraneous");
|
||||
|
||||
testOk(epicsParseLong("3 !", &l, 0) == S_stdlib_extraneous,
|
||||
"Long '3 !' => extraneous");
|
||||
testOk(epicsParseULong("3 !", &u, 0) == S_stdlib_extraneous,
|
||||
"ULong '3 !' => extraneous");
|
||||
testOk(epicsParseFloat("3 !", &f) == S_stdlib_extraneous,
|
||||
"Float '3 !' => extraneous");
|
||||
testOk(epicsParseDouble("3 !", &d) == S_stdlib_extraneous,
|
||||
"Double '3 !' => extraneous");
|
||||
|
||||
testOk(epicsScanLong("0x0", &l, 0) && l == 0, "Long '0x0'");
|
||||
testOk(epicsScanULong("0x0", &u, 0) && u == 0, "ULong '0x0'");
|
||||
testOk(epicsScanFloat("0x0", &f) && f == 0, "Float '0x0'");
|
||||
testOk(epicsScanDouble("0x0", &d) && d == 0, "Double '0x0'");
|
||||
|
||||
testOk(epicsScanLong("0x1", &l, 0) && l == 1, "Long '0x1'");
|
||||
testOk(epicsScanULong("0x1", &u, 0) && u == 1, "ULong '0x1'");
|
||||
testOk(epicsScanFloat("0x1", &f) && f == 1, "Float '0x1'");
|
||||
testOk(epicsScanDouble("0x1", &d) && d == 1, "Double '0x1'");
|
||||
|
||||
testOk(epicsScanLong("+0x1", &l, 0) && l == 1, "Long '+0x1'");
|
||||
testOk(epicsScanULong("+0x1", &u, 0) && u == 1, "ULong '+0x1'");
|
||||
testOk(epicsScanFloat("+0x1", &f) && f == 1, "Float '+0x1'");
|
||||
testOk(epicsScanDouble("+0x1", &d) && d == 1, "Double '+0x1'");
|
||||
|
||||
testOk(epicsScanLong("-0x1", &l, 0) && l == -1, "Long '-0x1'");
|
||||
testOk(epicsScanULong("-0x1", &u, 0) && u == -1, "ULong '-0x1'");
|
||||
testOk(epicsScanFloat("-0x1", &f) && f == -1, "Float '-0x1'");
|
||||
testOk(epicsScanDouble("-0x1", &d) && d == -1, "Double '-0x1'");
|
||||
|
||||
testOk(epicsScanLong("0xf", &l, 0) && l == 15, "Long '0xf'");
|
||||
testOk(epicsScanULong("0xf", &u, 0) && u == 15, "ULong '0xf'");
|
||||
testOk(epicsScanFloat("0xf", &f) && f == 15, "Float '0xf'");
|
||||
testOk(epicsScanDouble("0xf", &d) && d == 15, "Double '0xf'");
|
||||
|
||||
testOk(epicsScanLong("0XF", &l, 0) && l == 15, "Long '0XF'");
|
||||
testOk(epicsScanULong("0XF", &u, 0) && u == 15, "ULong '0XF'");
|
||||
testOk(epicsScanFloat("0XF", &f) && f == 15, "Float '0XF'");
|
||||
testOk(epicsScanDouble("0XF", &d) && d == 15, "Double '0XF'");
|
||||
|
||||
testOk(epicsParseLong("0x0", &l, 10) == S_stdlib_extraneous,
|
||||
"Long '0x0' in base 10 => extraneous");
|
||||
testOk(epicsParseULong("0x0", &u, 10) == S_stdlib_extraneous,
|
||||
"ULong '0x0' in base 10 => extraneous");
|
||||
testOk(epicsScanLong("0x10", &l, 0) && l == 0x10,
|
||||
"Long '0x10' in base 0");
|
||||
testOk(epicsScanULong("0x10", &u, 0) && u == 0x10,
|
||||
"ULong '0x10' in base 0");
|
||||
testOk(epicsScanLong("0x10", &l, 16) && l == 0x10,
|
||||
"Long '0x10' in base 16");
|
||||
testOk(epicsScanULong("0x10", &u, 16) && u == 0x10,
|
||||
"ULong '0x10' in base 16");
|
||||
testOk(epicsScanLong("10", &l, 16) && l == 0x10,
|
||||
"Long '10' in base 16");
|
||||
testOk(epicsScanULong("10", &u, 16) && u == 0x10,
|
||||
"ULong '10' in base 16");
|
||||
|
||||
testOk(epicsScanLong("0x7fffffff", &l, 0) && l == 0x7fffffff,
|
||||
"Long '0x7fffffff'");
|
||||
testOk(epicsScanULong("0xffffffff", &u, 0) && u == 0xffffffff,
|
||||
"ULong '0xffffffff'");
|
||||
testOk(epicsScanFloat("0xffffff", &f) && f == 0xffffff,
|
||||
"Float '0xffffff'");
|
||||
testOk(epicsScanDouble("0xffffffff", &d) && d == 0xffffffff,
|
||||
"Double '0xffffffff'");
|
||||
|
||||
testOk(epicsScanLong("-0x7fffffff", &l, 0) && l == -0x7fffffff,
|
||||
"Long '-0x7fffffff'");
|
||||
testOk(epicsScanULong("-0x7fffffff", &u, 0) && u == -0x7fffffff,
|
||||
"ULong '-0x7fffffff'");
|
||||
testOk(epicsScanFloat("-0xffffff", &f) && f == -0xffffff,
|
||||
"Float '-0xffffff'");
|
||||
testOk(epicsScanDouble("-0x7fffffff", &d) && d == -0x7fffffff,
|
||||
"Double '-0x7fffffff'");
|
||||
|
||||
testOk(!epicsParseInt8("0x7f", &i8, 0) && i8 == 0x7f,
|
||||
"Int8 '0x7f'");
|
||||
testOk(!epicsParseInt8("-0x80", &i8, 0) && i8 == -0x80,
|
||||
"Int8 '-0x80'");
|
||||
testOk(!epicsParseUInt8("0xff", &u8, 0) && u8 == 0xff,
|
||||
"UInt8 '0xff'");
|
||||
testOk(epicsParseInt8("0x80", &i8, 0) == S_stdlib_overflow,
|
||||
"Int8 '0x80' => overflow");
|
||||
testOk(epicsParseInt8("-0x81", &i8, 0) == S_stdlib_overflow,
|
||||
"Int8 '-0x81' => overflow");
|
||||
testOk(epicsParseUInt8("0x100", &u8, 0) == S_stdlib_overflow,
|
||||
"UInt8 '0x100' => overflow");
|
||||
|
||||
testOk(!epicsParseInt16("0x7fff", &i16, 0) && i16 == 0x7fff,
|
||||
"Int16 '0x7fff'");
|
||||
testOk(!epicsParseInt16("-0x8000", &i16, 0) && i16 == -0x8000,
|
||||
"Int16 '-0x8000'");
|
||||
testOk(!epicsParseUInt16("0xffff", &u16, 0) && u16 == 0xffff,
|
||||
"UInt16 '0xffff'");
|
||||
testOk(epicsParseInt16("0x8000", &i16, 0) == S_stdlib_overflow,
|
||||
"Int16 '0x8000' => overflow");
|
||||
testOk(epicsParseInt16("-0x8001", &i16, 0) == S_stdlib_overflow,
|
||||
"Int16 '-0x8001' => overflow");
|
||||
testOk(epicsParseUInt16("0x10000", &u16, 0) == S_stdlib_overflow,
|
||||
"UInt16 '0x10000' => overflow");
|
||||
|
||||
testOk(!epicsParseInt32("0x7fffffff", &i32, 0) && i32 == 0x7fffffff,
|
||||
"Int32 '0x7fffffff'");
|
||||
testOk(!epicsParseInt32("-0x80000000", &i32, 0) && i32 == -0x80000000L,
|
||||
"Int32 '-0x80000000'");
|
||||
testOk(!epicsParseUInt32("0xffffffff", &u32, 0) && u32 == 0xffffffff,
|
||||
"UInt32 '0xffffffff'");
|
||||
testOk(epicsParseInt32("0x80000000", &i32, 0) == S_stdlib_overflow,
|
||||
"Int32 '0x80000000' => overflow");
|
||||
testOk(epicsParseInt32("-0x80000001", &i32, 0) == S_stdlib_overflow,
|
||||
"Int32 '-0x80000001' => overflow");
|
||||
testOk(epicsParseUInt32("0x100000000", &u32, 0) == S_stdlib_overflow,
|
||||
"UInt32 '0x100000000' => overflow");
|
||||
|
||||
testOk(epicsScanFloat(".1", &f) && fabs(f - 0.1) < 1e-7,
|
||||
"Float '.1'");
|
||||
testOk(epicsScanDouble(".1", &d) && fabs(d - 0.1) < 1e-15,
|
||||
"Double '.1'");
|
||||
|
||||
testOk(epicsScanFloat("0.1", &f) && fabs(f - 0.1) < 1e-7,
|
||||
"Float '0.1'");
|
||||
testOk(epicsScanDouble("0.1", &d) && fabs(d - 0.1) < 1e-15,
|
||||
"Double '0.1'");
|
||||
|
||||
testOk(epicsScanFloat("1e-1", &f) && fabs(f - 1e-1) < 1e-7,
|
||||
"Float '1e-1'");
|
||||
testOk(epicsScanDouble("1e-1", &d) && fabs(d - 1e-1) < 1e-15,
|
||||
"Double '1e-1'");
|
||||
|
||||
testOk(epicsScanFloat("-.1", &f) && fabs(f + 0.1) < 1e-7,
|
||||
"Float '-.1'");
|
||||
testOk(epicsScanDouble("-.1", &d) && fabs(d + 0.1) < 1e-15,
|
||||
"Double '-.1'");
|
||||
|
||||
testOk(epicsScanFloat("-0.1", &f) && fabs(f + 0.1) < 1e-7,
|
||||
"Float '-0.1'");
|
||||
testOk(epicsScanDouble("-0.1", &d) && fabs(d + 0.1) < 1e-15,
|
||||
"Double '-0.1'");
|
||||
|
||||
testOk(epicsScanFloat("-1e-1", &f) && fabs(f + 1e-1) < 1e-7,
|
||||
"Float '-1e-1'");
|
||||
testOk(epicsScanDouble("-1e-1", &d) && fabs(d + 1e-1) < 1e-15,
|
||||
"Double '-1e-1'");
|
||||
|
||||
testOk(epicsScanFloat("1e-30", &f) && fabs(f - 1e-30) < 1e-36,
|
||||
"Float '1e-30'");
|
||||
testOk(epicsScanDouble("1e-300", &d) && fabs(d - 1e-300) < 1e-306,
|
||||
"Double '1e-300'");
|
||||
|
||||
testOk(epicsParseFloat("1e-40", &f) == S_stdlib_underflow,
|
||||
"Float '1e-40' => underflow");
|
||||
testOk(epicsParseDouble("1e-330", &d) == S_stdlib_underflow,
|
||||
"Double '1e-330' => underflow");
|
||||
|
||||
testOk(epicsScanFloat("1e30", &f) && fabs(f - 1e30) < 1e24,
|
||||
"Float '1e30'");
|
||||
testOk(epicsScanDouble("1e300", &d) && d == 1e300,
|
||||
"Double '1e300'");
|
||||
|
||||
testOk(epicsParseFloat("1e40", &f) == S_stdlib_overflow,
|
||||
"Float '1e40' => overflow");
|
||||
testOk(epicsParseDouble("1e310", &d) == S_stdlib_overflow,
|
||||
"Double '1e330' => overflow");
|
||||
|
||||
testOk(epicsScanLong("2147483647", &l, 0) && l == 2147483647,
|
||||
"Long '2147483647'");
|
||||
testOk(epicsScanLong("-2147483647", &l, 0) && l == -2147483647,
|
||||
"Long '-2147483647'");
|
||||
testOk(epicsScanULong("4294967295", &u, 0) && u == 4294967295u,
|
||||
"ULong '4294967295'");
|
||||
testOk(epicsScanFloat("16777214", &f) && f == 16777214.0,
|
||||
"Float '16777214'");
|
||||
testOk(epicsScanFloat("16777215", &f) && f == 16777215.0,
|
||||
"Float '16777215'");
|
||||
testOk(epicsScanFloat("-16777215", &f) && f == -16777215.0,
|
||||
"Float '-16777215'");
|
||||
testOk(epicsScanFloat("-16777216", &f) && f == -16777216.0,
|
||||
"Float '-16777216'");
|
||||
testOk(epicsScanDouble("4294967294", &d) && d == 4294967294.0,
|
||||
"Double '4294967294'");
|
||||
testOk(epicsScanDouble("4294967295", &d) && d == 4294967295.0,
|
||||
"Double '4294967295'");
|
||||
testOk(epicsScanDouble("-4294967295", &d) && d == -4294967295.0,
|
||||
"Double '-4294967295'");
|
||||
testOk(epicsScanDouble("-4294967296", &d) && d == -4294967296.0,
|
||||
"Double '-4294967296'");
|
||||
|
||||
testOk(epicsScanFloat("NAN", &f) && isnan(f), "Float 'NAN'");
|
||||
testOk(epicsScanDouble("NAN", &d) && isnan(d), "Double 'NAN'");
|
||||
testOk(epicsScanFloat("Nan", &f) && isnan(f), "Float 'Nan'");
|
||||
testOk(epicsScanDouble("Nan", &d) && isnan(d), "Double 'Nan'");
|
||||
testOk(epicsScanFloat("nan()", &f) && isnan(f), "Float 'nan()'");
|
||||
testOk(epicsScanDouble("nan()", &d) && isnan(d), "Double 'nan()'");
|
||||
|
||||
testOk(epicsScanFloat("INF", &f) && f == epicsINF,
|
||||
"Float 'INF'");
|
||||
testOk(epicsScanDouble("INF", &d) && d == epicsINF,
|
||||
"Double 'INF'");
|
||||
testOk(epicsScanFloat("Infinity", &f) && f == epicsINF,
|
||||
"Float 'Infinity'");
|
||||
testOk(epicsScanDouble("Infinity", &d) && d == epicsINF,
|
||||
"Double 'Infinity'");
|
||||
|
||||
testOk(epicsScanFloat("+INF", &f) && f == epicsINF,
|
||||
"Float '+INF'");
|
||||
testOk(epicsScanDouble("+INF", &d) && d == epicsINF,
|
||||
"Double '+INF'");
|
||||
testOk(epicsScanFloat("+Infinity", &f) && f == epicsINF,
|
||||
"Float '+Infinity'");
|
||||
testOk(epicsScanDouble("+Infinity", &d) && d == epicsINF,
|
||||
"Double '+Infinity'");
|
||||
|
||||
testOk(epicsScanFloat("-INF", &f) && f == -epicsINF,
|
||||
"Float '-INF'");
|
||||
testOk(epicsScanDouble("-INF", &d) && d == -epicsINF,
|
||||
"Double '-INF'");
|
||||
testOk(epicsScanFloat("-Infinity", &f) && f == -epicsINF,
|
||||
"Float '-Infinity'");
|
||||
testOk(epicsScanDouble("-Infinity", &d) && d == -epicsINF,
|
||||
"Double '-Infinity'");
|
||||
|
||||
#ifdef epicsStrtod
|
||||
#define CHECK_STRTOD epicsStrtod != strtod
|
||||
if (epicsStrtod == strtod)
|
||||
testDiag("This target defines epicsStrtod as strtod");
|
||||
else
|
||||
testDiag("This target defines its own epicsStrtod macro");
|
||||
#else
|
||||
#define CHECK_STRTOD 1
|
||||
testDiag("This target compiles epicsStrtod()");
|
||||
#endif
|
||||
|
||||
if (CHECK_STRTOD) {
|
||||
int pass = 0, fail = 0;
|
||||
|
||||
#define CHECK(ok, msg) if (ok) pass++; else fail++, testDiag(msg)
|
||||
|
||||
testDiag("Checking the native strtod(), only failures shown:");
|
||||
|
||||
CHECK(parseStrtod("", &d) == S_stdlib_noConversion,
|
||||
" not ok - strtod('') => noConversion");
|
||||
CHECK(parseStrtod("\t \n", &d) == S_stdlib_noConversion,
|
||||
" not ok - strtod('\\t 1\\n') => noConversion");
|
||||
CHECK(parseStrtod("!", &d) == S_stdlib_noConversion,
|
||||
" not ok - strtod('!') => noConversion");
|
||||
CHECK(scanStrtod("0", &d) && d == 0,
|
||||
" not ok - strtod('0')");
|
||||
CHECK(scanStrtod("\t 1\n", &d) && d == 1,
|
||||
" not ok - strtod('\\t 1\\n')");
|
||||
CHECK(parseStrtod("2!", &d) == S_stdlib_extraneous,
|
||||
" not ok - strtod('2!') => extraneous");
|
||||
CHECK(parseStrtod("3 !", &d) == S_stdlib_extraneous,
|
||||
" not ok - strtod('3 !') => extraneous");
|
||||
CHECK(scanStrtod("0x0", &d) && d == 0,
|
||||
" not ok - strtod('0x0')");
|
||||
CHECK(scanStrtod("0x1", &d) && d == 1,
|
||||
" not ok - strtod('0x1')");
|
||||
CHECK(scanStrtod("+0x1", &d) && d == 1,
|
||||
" not ok - strtod('+0x1')");
|
||||
CHECK(scanStrtod("-0x1", &d) && d == -1,
|
||||
" not ok - strtod('-0x1')");
|
||||
CHECK(scanStrtod("0xf", &d) && d == 15,
|
||||
" not ok - strtod('0xf')");
|
||||
CHECK(scanStrtod("0XF", &d) && d == 15,
|
||||
" not ok - strtod('0XF')");
|
||||
CHECK(scanStrtod("0xffffffff", &d) && d == 0xffffffff,
|
||||
" not ok - strtod('0xffffffff')");
|
||||
CHECK(scanStrtod("-0x7fffffff", &d) && d == -0x7fffffffl,
|
||||
" not ok - strtod('-0x7fffffff')");
|
||||
CHECK(scanStrtod(".1", &d) && fabs(d - 0.1) < 1e-15,
|
||||
" not ok - strtod('.1')");
|
||||
CHECK(scanStrtod("0.1", &d) && fabs(d - 0.1) < 1e-15,
|
||||
" not ok - strtod('0.1')");
|
||||
CHECK(scanStrtod("1e-1", &d) && fabs(d - 1e-1) < 1e-15,
|
||||
" not ok - strtod('1e-1')");
|
||||
CHECK(scanStrtod("-.1", &d) && fabs(d + 0.1) < 1e-15,
|
||||
" not ok - strtod('-.1')");
|
||||
CHECK(scanStrtod("-0.1", &d) && fabs(d + 0.1) < 1e-15,
|
||||
" not ok - strtod('-0.1')");
|
||||
CHECK(scanStrtod("-1e-1", &d) && fabs(d + 1e-1) < 1e-15,
|
||||
" not ok - strtod('-1e-1')");
|
||||
CHECK(scanStrtod("1e-300", &d) && fabs(d - 1e-300) < 1e-306,
|
||||
" not ok - strtod('1e-300')");
|
||||
CHECK(parseStrtod("1e-400", &d) == S_stdlib_underflow,
|
||||
" not ok - strtod('1e-400') => underflow");
|
||||
CHECK(scanStrtod("4294967294", &d) && d == 4294967294.0,
|
||||
" not ok - strtod('4294967294')");
|
||||
CHECK(scanStrtod("4294967295", &d) && d == 4294967295.0,
|
||||
" not ok - strtod('4294967295')");
|
||||
CHECK(scanStrtod("-4294967295", &d) && d == -4294967295.0,
|
||||
" not ok - strtod('-4294967295')");
|
||||
CHECK(scanStrtod("-4294967296", &d) && d == -4294967296.0,
|
||||
" not ok - strtod('-4294967296')");
|
||||
CHECK(scanStrtod("NAN", &d) && isnan(d),
|
||||
" not ok - strtod('NAN')");
|
||||
CHECK(scanStrtod("Nan", &d) && isnan(d),
|
||||
" not ok - strtod('Nan')");
|
||||
CHECK(scanStrtod("nan()", &d) && isnan(d),
|
||||
" not ok - strtod('nan()')");
|
||||
CHECK(scanStrtod("INF", &d) && d == epicsINF,
|
||||
" not ok - strtod('INF')");
|
||||
CHECK(scanStrtod("Infinity", &d) && d == epicsINF,
|
||||
" not ok - strtod('Infinity')");
|
||||
CHECK(scanStrtod("+INF", &d) && d == epicsINF,
|
||||
" not ok - strtod('+INF')");
|
||||
CHECK(scanStrtod("+Infinity", &d) && d == epicsINF,
|
||||
" not ok - strtod('+Infinity')");
|
||||
CHECK(scanStrtod("-INF", &d) && d == -epicsINF,
|
||||
" not ok - strtod('-INF')");
|
||||
CHECK(scanStrtod("-Infinity", &d) && d == -epicsINF,
|
||||
" not ok - strtod('-Infinity')");
|
||||
|
||||
if (fail)
|
||||
testDiag("The native strtod() passed %d and failed %d tests",
|
||||
pass, fail);
|
||||
else {
|
||||
testDiag("The native strtod() passed all %d tests", pass);
|
||||
#ifndef epicsStrtod
|
||||
testDiag("The compiled epicsStrtod() should not be needed");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return testDone();
|
||||
}
|
||||
Reference in New Issue
Block a user