more integer conversions working
diff --git a/inc/qcbor/qcbor_decode.h b/inc/qcbor/qcbor_decode.h
index acd12fb..fba80be 100644
--- a/inc/qcbor/qcbor_decode.h
+++ b/inc/qcbor/qcbor_decode.h
@@ -904,7 +904,7 @@
/**
- Check whether all the bytes have been decoded and maps and arrays closed.
+ @brief Check whether all the bytes have been decoded and maps and arrays closed.
@param[in] pCtx The context to check.
@@ -944,21 +944,60 @@
static QCBORError QCBORDecode_GetLastError(QCBORDecodeContext *pCtx);
-void QCBORDecode_GetInt64Convert(QCBORDecodeContext *pMe, uint32_t uOptions, int64_t *pValue);
+/**
+ @brief Decode next item as a 64-bit integer
+ @param[in] pCtx The decode context
+ @param[in] uOptions The integer conversion options.
+ @param[out] pnValue 64-bit integer with item
+ The CBOR data item must be either a positive integer, negative integer or floating-point number.
+ \c uOptions is one of XXX and controls which conversions will be performed.
+ See also QCBORDecode_GetInt64ConvertAll() which will perform the same conversions
+ as this and a lot more at the cost of adding more object code to your executable.
-/*
- Get the next item as an int64_t. The CBOR type can be unsigned, negative, float
- a big float, a decimal fraction or a big num. Conversion will be dones as
- expected. Some cases will error out with under or over flow.
+ On error, this sets the decoder last error. If the data item is of a type that
+ can't be decoded by this function, QCBOR_ERR_UNEXPECTED_TYPE is set. If
+ the data item can be decode, but the option requesting it is not set, then
+ QCBOR_ERR_UNEXPECTED_TYPE will be set. If the data item is too large
+ or small to be represented as a 64-bit signed integer, QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW
+ us set.
+
+ When converting floating-point values, the integer is rounded to the nearest integer using
+ llround(). By default, floating-point suport is enabled for QCBOR. If it is turned off,
+ then floating-point conversion is not available.
+
*/
-void QCBORDecode_GetInt64ConvertAll(QCBORDecodeContext *pMe, uint32_t uOptions, int64_t *pValue);
+void QCBORDecode_GetInt64Convert(QCBORDecodeContext *pCtx, uint32_t uOptions, int64_t *pnValue);
+
+
+/**
+ @brief Decode next item as a 64-bit integer
+
+ @param[in] pCtx The decode context
+ @param[in] uOptions The integer conversion options.
+ @param[out] pnValue 64-bit integer with item
+
+ This is the same as QCBORDecode_GetInt64Convert() but supports many more conversions at
+ the cost of adding more object code to your executable.
+
+ The additiona data item types that are suported are positive and negative bignums,
+ decimal fractions and big floats, including decimal fractions and big floats that use bignums.
+ Not that all these types can support numbers much larger that can be represented by
+ in a 64-bit integer, so QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW will
+ often be encountered.
+ */
+void QCBORDecode_GetInt64ConvertAll(QCBORDecodeContext *pCtx, uint32_t uOptions, int64_t *pnValue);
+
+
void QCBORDecode_GetUInt64ConvertAll(QCBORDecodeContext *pMe, uint32_t uOptions, uint64_t *pValue);
+void QCBORDecode_GetDoubleConvertAll(QCBORDecodeContext *pMe, uint32_t uOptions, double *pValue);
+
+
void QCBORDecode_GetBytes(QCBORDecodeContext *pCtx, UsefulBufC *pValue);
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index 52a3d05..a061dc6 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -2812,7 +2812,7 @@
#endif
-
+#include "fenv.h"
/*
Get the next item as an int64_t. The CBOR type can be unsigned, negative, float
@@ -2839,11 +2839,17 @@
}
switch(Item.uDataType) {
- case QCBOR_TYPE_FLOAT:
+ // TODO: float when ifdefs are set
+ case QCBOR_TYPE_DOUBLE:
if(uOptions & QCBOR_CONVERT_TYPE_FLOAT) {
// TODO: what about under/overflow here?
// Invokes the floating-point HW and/or compiler-added libraries
+ feclearexcept(FE_ALL_EXCEPT);
*pValue = llround(Item.val.dfnum);
+ if(fetestexcept(FE_INVALID)) {
+ // TODO: better error code
+ pMe->uLastError = QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW;
+ }
} else {
pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
}
@@ -2906,7 +2912,7 @@
return;
}
- if(pMe->uLastError != QCBOR_SUCCESS && pMe->uLastError != QCBOR_ERR_UNEXPECTED_TYPE) {
+ if(pMe->uLastError != QCBOR_ERR_UNEXPECTED_TYPE) {
// The above conversion failed in a way that code below can't correct
return;
}
@@ -3041,12 +3047,20 @@
}
switch(Item.uDataType) {
- case QCBOR_TYPE_FLOAT:
+ // TODO: type flaot
+ case QCBOR_TYPE_DOUBLE:
if(uOptions & QCBOR_CONVERT_TYPE_FLOAT) {
- if(Item.val.dfnum >= 0) {
- // TODO: over/underflow
- // TODO: find a rounding function
- *pValue = (uint64_t)round(Item.val.dfnum);
+ feclearexcept(FE_ALL_EXCEPT);
+ double dRounded = round(Item.val.dfnum);
+ // TODO: over/underflow
+ if(fetestexcept(FE_INVALID)) {
+ // TODO: better error code
+ pMe->uLastError = QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW;
+ } else if(isnan(dRounded)) {
+ // TODO: better error code
+ pMe->uLastError = QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW;
+ } else if(dRounded >= 0) {
+ *pValue = (uint64_t)dRounded;
} else {
pMe->uLastError = QCBOR_ERR_NUMBER_SIGN_CONVERSION;
}
@@ -3104,7 +3118,13 @@
QCBORDecode_GetUInt64ConvertInternal(pMe, uOptions, pValue, &Item);
- if(pMe->uLastError != QCBOR_SUCCESS && pMe->uLastError != QCBOR_ERR_UNEXPECTED_TYPE) {
+ if(pMe->uLastError == QCBOR_SUCCESS) {
+ // The above conversion succeeded
+ return;
+ }
+
+ if(pMe->uLastError != QCBOR_ERR_UNEXPECTED_TYPE) {
+ // The above conversion failed in a way that code below can't correct
return;
}
@@ -3241,7 +3261,8 @@
switch(Item.uDataType) {
- case QCBOR_TYPE_FLOAT:
+ // TODO: type float
+ case QCBOR_TYPE_DOUBLE:
if(uOptions & QCBOR_CONVERT_TYPE_FLOAT) {
*pValue = Item.val.dfnum;
} else {
@@ -3249,7 +3270,7 @@
}
break;
- case QCBOR_TYPE_INT64:
+ case QCBOR_TYPE_INT64:
if(uOptions & QCBOR_CONVERT_TYPE_INT64) {
// TODO: how does this work?
*pValue = (double)Item.val.int64;
@@ -3258,7 +3279,7 @@
}
break;
- case QCBOR_TYPE_UINT64:
+ case QCBOR_TYPE_UINT64:
if(uOptions & QCBOR_CONVERT_TYPE_UINT64) {
// TODO: check more carefully how this cast works.
*pValue = (double)Item.val.uint64;
@@ -3267,7 +3288,7 @@
}
- case QCBOR_TYPE_DECIMAL_FRACTION:
+ case QCBOR_TYPE_DECIMAL_FRACTION:
if(uOptions & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
// TODO: rounding and overflow errors
*pValue = (double)Item.val.expAndMantissa.Mantissa.nInt *
@@ -3277,7 +3298,7 @@
}
break;
- case QCBOR_TYPE_BIGFLOAT:
+ case QCBOR_TYPE_BIGFLOAT:
if(uOptions & QCBOR_CONVERT_TYPE_BIGFLOAT ) {
*pValue = (double)Item.val.expAndMantissa.Mantissa.nInt *
exp2((double)Item.val.expAndMantissa.nExponent);
@@ -3286,7 +3307,7 @@
}
break;
- case QCBOR_TYPE_POSBIGNUM:
+ case QCBOR_TYPE_POSBIGNUM:
if(uOptions & QCBOR_CONVERT_TYPE_BIG_NUM) {
*pValue = ConvertBigNumToDouble(Item.val.bigNum);
} else {
@@ -3294,7 +3315,7 @@
}
break;
- case QCBOR_TYPE_NEGBIGNUM:
+ case QCBOR_TYPE_NEGBIGNUM:
if(uOptions & QCBOR_CONVERT_TYPE_BIG_NUM) {
*pValue = -ConvertBigNumToDouble(Item.val.bigNum);
} else {
@@ -3302,7 +3323,7 @@
}
break;
- case QCBOR_TYPE_DECIMAL_FRACTION_POS_BIGNUM:
+ case QCBOR_TYPE_DECIMAL_FRACTION_POS_BIGNUM:
if(uOptions & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
double dMantissa = ConvertBigNumToDouble(Item.val.expAndMantissa.Mantissa.bigNum);
*pValue = dMantissa * pow(10, (double)Item.val.expAndMantissa.nExponent);
@@ -3311,32 +3332,32 @@
}
break;
- case QCBOR_TYPE_DECIMAL_FRACTION_NEG_BIGNUM:
- if(uOptions & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
- double dMantissa = -ConvertBigNumToDouble(Item.val.expAndMantissa.Mantissa.bigNum);
- *pValue = dMantissa * pow(10, (double)Item.val.expAndMantissa.nExponent);
- } else {
- pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
- }
- break;
+ case QCBOR_TYPE_DECIMAL_FRACTION_NEG_BIGNUM:
+ if(uOptions & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+ double dMantissa = -ConvertBigNumToDouble(Item.val.expAndMantissa.Mantissa.bigNum);
+ *pValue = dMantissa * pow(10, (double)Item.val.expAndMantissa.nExponent);
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
+ }
+ break;
- case QCBOR_TYPE_BIGFLOAT_POS_BIGNUM:
- if(uOptions & QCBOR_CONVERT_TYPE_BIGFLOAT) {
- double dMantissa = ConvertBigNumToDouble(Item.val.expAndMantissa.Mantissa.bigNum);
- *pValue = dMantissa * exp2((double)Item.val.expAndMantissa.nExponent);
- } else {
- pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
- }
- break;
+ case QCBOR_TYPE_BIGFLOAT_POS_BIGNUM:
+ if(uOptions & QCBOR_CONVERT_TYPE_BIGFLOAT) {
+ double dMantissa = ConvertBigNumToDouble(Item.val.expAndMantissa.Mantissa.bigNum);
+ *pValue = dMantissa * exp2((double)Item.val.expAndMantissa.nExponent);
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
+ }
+ break;
- case QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM:
- if(uOptions & QCBOR_CONVERT_TYPE_BIGFLOAT) {
- double dMantissa = -ConvertBigNumToDouble(Item.val.expAndMantissa.Mantissa.bigNum);
- *pValue = dMantissa * exp2((double)Item.val.expAndMantissa.nExponent);
- } else {
- pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
- }
- break;
+ case QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM:
+ if(uOptions & QCBOR_CONVERT_TYPE_BIGFLOAT) {
+ double dMantissa = -ConvertBigNumToDouble(Item.val.expAndMantissa.Mantissa.bigNum);
+ *pValue = dMantissa * exp2((double)Item.val.expAndMantissa.nExponent);
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
+ }
+ break;
}
}
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 1520792..31d495c 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -3989,11 +3989,156 @@
}
+struct NumberConversion {
+ char *szDescription;
+ UsefulBufC CBOR;
+ int64_t nConvertedToInt64;
+ QCBORError uErrorInt64;
+ uint64_t uConvertToUInt64;
+ QCBORError uErrorUint64;
+ double dConvertToDouble;
+ QCBORError uErrorDouble;
+};
+
+static struct NumberConversion NumberConversions[] = {
+ {
+ "Postive integer 0",
+ {(uint8_t[]){0x0}, 1},
+ 0LL,
+ QCBOR_SUCCESS,
+ 0ULL,
+ QCBOR_SUCCESS,
+ 0.0,
+ QCBOR_SUCCESS
+ },
+ {
+ "-18446744073709551616",
+ {(uint8_t[]){0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, 9},
+ -9223372036854775807-1, // INT64_MIN
+ QCBOR_SUCCESS,
+ 0ULL,
+ QCBOR_ERR_NUMBER_SIGN_CONVERSION,
+ -9223372036854775808.0,
+ QCBOR_SUCCESS
+ },
+ {
+ "Floating point value 100.3",
+ {(uint8_t[]){0xfb, 0x40, 0x59, 0x13, 0x33, 0x33, 0x33, 0x33, 0x33}, 9},
+ 100L,
+ QCBOR_SUCCESS,
+ 100ULL,
+ QCBOR_SUCCESS,
+ 100.3,
+ QCBOR_SUCCESS
+ },
+ {
+ "Floating point value NaN 0xfa7fc00000",
+ {(uint8_t[]){0xfa, 0x7f, 0xc0, 0x00, 0x00}, 5},
+ 0,
+ QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW,
+ 0,
+ QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW,
+ NAN,
+ QCBOR_SUCCESS
+ },
+ {
+ "Floating point value -4",
+ {(uint8_t[]){0xf9, 0xc4, 0x00}, 3},
+ -4,
+ QCBOR_SUCCESS,
+ 0,
+ QCBOR_ERR_NUMBER_SIGN_CONVERSION,
+ -4.0,
+ QCBOR_SUCCESS
+ },
+ {
+ "Decimal fraction 3/10",
+ {(uint8_t[]){0xC4, 0x82, 0x20, 0x03}, 4},
+ 0,
+ QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW,
+ 0,
+ QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW,
+ 0.30000000000000004,
+ QCBOR_SUCCESS
+ }
+};
+
+int32_t IntegerConvertTest2()
+{
+ const size_t nNumTests = sizeof(NumberConversions)/sizeof(struct NumberConversion);
+
+ for(struct NumberConversion *pF = NumberConversions; pF < NumberConversions + nNumTests; pF++) {
+ // Set up the decoding context including a memory pool so that
+ // indefinite length items can be checked
+ QCBORDecodeContext DCtx;
+ QCBORDecode_Init(&DCtx, pF->CBOR, QCBOR_DECODE_MODE_NORMAL);
+ UsefulBuf_MAKE_STACK_UB(Pool, 100);
+ QCBORError nCBORError = QCBORDecode_SetMemPool(&DCtx, Pool, 0);
+ if(nCBORError) {
+ return -9;
+ }
+
+ int64_t nInt;
+ DCtx.uLastError = 0;
+ QCBORDecode_GetInt64ConvertAll(&DCtx, 0xffff, &nInt);
+ if(QCBORDecode_GetLastError(&DCtx) != pF->uErrorInt64) {
+ return -99;
+ }
+ if(pF->uErrorInt64 == QCBOR_SUCCESS && pF->nConvertedToInt64 != nInt) {
+ return -888;
+ }
+
+
+ QCBORDecode_Init(&DCtx, pF->CBOR, QCBOR_DECODE_MODE_NORMAL);
+ nCBORError = QCBORDecode_SetMemPool(&DCtx, Pool, 0);
+ if(nCBORError) {
+ return -9;
+ }
+ uint64_t uInt;
+ DCtx.uLastError = 0;
+ QCBORDecode_GetUInt64ConvertAll(&DCtx, 0xffff, &uInt);
+ if(QCBORDecode_GetLastError(&DCtx) != pF->uErrorUint64) {
+ return -99;
+ }
+ if(pF->uErrorUint64 == QCBOR_SUCCESS && pF->uConvertToUInt64 != uInt) {
+ return -888;
+ }
+
+
+ QCBORDecode_Init(&DCtx, pF->CBOR, QCBOR_DECODE_MODE_NORMAL);
+ nCBORError = QCBORDecode_SetMemPool(&DCtx, Pool, 0);
+ if(nCBORError) {
+ return -9;
+ }
+ double d;
+ DCtx.uLastError = 0;
+ QCBORDecode_GetDoubleConvertAll(&DCtx, 0xffff, &d);
+ if(QCBORDecode_GetLastError(&DCtx) != pF->uErrorDouble) {
+ return -99;
+ }
+ if(pF->uErrorDouble == QCBOR_SUCCESS) {
+ if(isnan(pF->dConvertToDouble)) {
+ if(!isnan(d)) {
+ return -4;
+ }
+ } else {
+ // TODO: this comparison may need a margin of error
+ if(pF->dConvertToDouble != d) {
+ return -5;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
int32_t IntegerConvertTest()
{
+ (void)IntegerConvertTest2();
QCBORDecodeContext DCtx;
QCBORError nCBORError;