Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Full big num implementation with preferred serialization and 65-bit negs #219

Merged
merged 20 commits into from
Jul 25, 2024
Merged
4 changes: 3 additions & 1 deletion inc/qcbor/qcbor_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ========================================================================= */


#ifndef qcbor_common_h
#define qcbor_common_h

Expand Down Expand Up @@ -556,6 +555,9 @@ typedef enum {
/** Conformance checking requested, preferred serialization disabled, float in the input. */
QCBOR_ERR_CANT_CHECK_FLOAT_CONFORMANCE = 86,

/* Can't output a negative zero big num */
QCBOR_ERR_NO_NEGATIVE_ZERO = 87,

/** A range of error codes that can be made use of by the
* caller. QCBOR internally does nothing with these except notice
* that they are not QCBOR_SUCCESS. See QCBORDecode_SetError(). */
Expand Down
77 changes: 74 additions & 3 deletions inc/qcbor/qcbor_decode.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ typedef enum {

/** Type for an integer that decoded either between @c INT64_MIN and
* @c INT32_MIN or @c INT32_MAX and @c INT64_MAX. Data is in member
* @c val.int64. */
* @c val.int64. See also \ref QCBOR_TYPE_65BIT_NEG_INT */
#define QCBOR_TYPE_INT64 2

/** Type for an integer that decoded to a more than @c INT64_MAX and
Expand All @@ -269,11 +269,19 @@ typedef enum {
#define QCBOR_TYPE_TEXT_STRING 7

/** Type for a positive big number. Data is in @c val.bignum, a
* pointer and a length. */
* pointer and a length. See QCBORDecode_BignumPreferred(). */
#define QCBOR_TYPE_POSBIGNUM 9

/** Type for a negative big number. Data is in @c val.bignum, a
* pointer and a length. */
* pointer and a length. Type 1 integers in the range of [-2^64,
* -2^63 - 1] are returned in this type. 1 MUST be subtracted from
* what is returned to get the actual value. This is because of the
* way CBOR negative numbers are represented. QCBOR doesn't do this
* because it can't be done without storage allocation and QCBOR
* avoids storage allocation for the most part. For example, if 1 is
* subtraced from a negative big number that is the two bytes 0xff
* 0xff, the result would be 0x01 0x00 0x00, one byte longer than
* what was received. See QCBORDecode_BignumPreferred(). */
#define QCBOR_TYPE_NEGBIGNUM 10

/** Type for [RFC 3339] (https://tools.ietf.org/html/rfc3339) date
Expand Down Expand Up @@ -504,6 +512,7 @@ typedef struct _QCBORItem {
/** The value for @c uDataType @ref QCBOR_TYPE_POSBIGNUM and
* @ref QCBOR_TYPE_NEGBIGNUM. */
UsefulBufC bigNum;

/** See @ref QCBOR_TYPE_UKNOWN_SIMPLE */
uint8_t uSimple;
#ifndef QCBOR_DISABLE_EXP_AND_MANTISSA
Expand Down Expand Up @@ -1373,6 +1382,68 @@ static void
QCBORDecode_SetError(QCBORDecodeContext *pCtx, QCBORError uError);


/**
* @brief Decode a preferred serialization big number.
*
* @param[in] Item The number to process.
* @param[in] BigNumBuf The buffer to output to.
* @param[out] pBigNum The resulting big number.
* @param[out] pIsNegative The sign of the resulting big number.
*
* This exists to process an item that is expected to be a big number
* encoded with preferred serialization. This processing is not part
* of the main decoding because of the number of CBOR types it
* involves, because it needs a buffer to output to, and to keep code
* size of the core decoding small.
*
* This can also be used to do the subtraction of 1 for negative big
* numbers even if preferred serialization of big numbers is not in
* use.
*
* This works on all CBOR type 0 and 1 integers and all tag 2 and 3
* big numbers. In terms of QCBOR types, this works on
* \ref QCBOR_TYPE_INT64, \ref QCBOR_TYPE_UINT64,
* \ref QCBOR_TYPE_65BIT_NEG, \ref QCBOR_TYPE_POSBIGNUM and
* \ref QCBOR_TYPE_NEGBIGNUM.
*
* This always returns the result as a big number. The integer types 0
* and 1 are converted. Leading zeros are removed. The value 0 is
* always returned as a one-byte big number with the value 0x00.

* If \c BigNumBuf is too small, \c pBigNum.ptr will be \c NULL and \c
* pBigNum.len reports the required length. The size of \c BigNumBuf
* might have to be one larger than the size of the tag 2 or 3 being
* decode because of two cases. In CBOR the value of a tag 3 big
* number is -n - 1. The subtraction of one might have a carry. For
* example, an encoded tag 3 that is 0xff, is returned here as 0x01
* 0x00. The other case is a empty tag 2 which is returned as a
* one-byte big number with the value 0x00. (This is the only place
* in all of RFC 8949 except for indefinite length strings where the
* encoded buffer off the wire can't be returned directly, the only
* place some storage allocation is required.)
*
* This is the decode-side implementation of preferred serialization
* of big numbers described in section 3.4.3 of RFC 8949. It
* implements the decode-side unification of big numbers and regular
* integers.
*
* This can also be used if you happen to want type 0 and type 1
* integers converted to big numbers.
*
* If QCBOR is being used in an environment with a full big number
* library, it may be better (less object code) to use the big number
* library than this, particularly to subtract one for tag 3.
*
* Finally, the object code for this function is suprisingly large,
* almost 1KB. This is due to the number of CBOR data types, and the
* big number math required to subtract one and the buffer sizing
* issue it brings.
*/
QCBORError
QCBORDecode_BignumPreferred(const QCBORItem Item,
UsefulBuf BigNumBuf,
UsefulBufC *pBigNum,
bool *pIsNegative);


/**
Expand Down
Loading
Loading