/* ===========================================================================
 * Copyright (c) 2016-2018, The Linux Foundation.
 * Copyright (c) 2018-2025, Laurence Lundblade.
 * Copyright (c) 2021, Arm Limited.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of The Linux Foundation nor the names of its
 *       contributors, nor the name "Laurence Lundblade" may be used to
 *       endorse or promote products derived from this software without
 *       specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * ========================================================================= */


#include "qcbor/qcbor_number_encode.h"
#include "ieee754.h"

#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
#include <math.h> /* Only for NAN definition */
#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */


/**
 * @file qcbor_number_encode.c
 *
 */



/*
 * Public function for adding signed integers. See qcbor/qcbor_encode.h
 */
void
QCBOREncode_AddInt64(QCBOREncodeContext *pMe, const int64_t nNum)
{
   uint8_t  uMajorType;
   uint64_t uValue;

   if(nNum < 0) {
      /* In CBOR -1 encodes as 0x00 with major type negative int.
       * First add one as a signed integer because that will not
       * overflow. Then change the sign as needed for encoding (the
       * opposite order, changing the sign and subtracting, can cause
       * an overflow when encoding INT64_MIN). */
      int64_t nTmp = nNum + 1;
      uValue = (uint64_t)-nTmp;
      uMajorType = CBOR_MAJOR_TYPE_NEGATIVE_INT;
   } else {
      uValue = (uint64_t)nNum;
      uMajorType = CBOR_MAJOR_TYPE_POSITIVE_INT;
   }
   QCBOREncode_Private_AppendCBORHead(pMe, uMajorType, uValue, 0);
}


#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
/**
 * @brief Semi-private method to add a double using preferred encoding.
 *
 * @param[in] pMe   The encode context.
 * @param[in] dNum  The double to add.
 *
 * This converts the double to a float or half-precision if it can be done
 * without a loss of precision. See QCBOREncode_AddDouble().
 */
void
QCBOREncode_Private_AddPreferredDouble(QCBOREncodeContext *pMe, double dNum)
{
   IEEE754_union        FloatResult;
   bool                 bNoNaNPayload;
   struct IEEE754_ToInt IntResult;
   uint64_t             uNegValue;

#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
   if(IEEE754_DoubleHasNaNPayload(dNum) && !(pMe->uConfigFlags & QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD)) {
      pMe->uError = QCBOR_ERR_NOT_ALLOWED;
      return;
   }
#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */

   if(pMe->uConfigFlags & QCBOR_ENCODE_CONFIG_FLOAT_REDUCTION) {
      IntResult = IEEE754_DoubleToInt(dNum);
      switch(IntResult.type) {
         case IEEE754_ToInt_IS_INT:
            QCBOREncode_AddInt64(pMe, IntResult.integer.is_signed);
            return;
         case IEEE754_ToInt_IS_UINT:
            QCBOREncode_AddUInt64(pMe, IntResult.integer.un_signed);
            return;
         case IEEE754_ToInt_IS_65BIT_NEG:
            {
               if(IntResult.integer.un_signed == 0) {
                  uNegValue = UINT64_MAX;
               } else {
                  uNegValue = IntResult.integer.un_signed-1;
               }
               QCBOREncode_AddNegativeUInt64(pMe, uNegValue);
            }
            return;
         case IEEE754_ToInt_NaN:
            dNum = NAN;
            bNoNaNPayload = true;
            break;
         case IEEE754_ToInt_NO_CONVERSION:
            bNoNaNPayload = true;
      }
   } else  {
      bNoNaNPayload = false;
   }

   FloatResult = IEEE754_DoubleToSmaller(dNum, true, bNoNaNPayload);

   QCBOREncode_Private_AddType7(pMe,
                                (uint8_t)FloatResult.uSize,
                                FloatResult.uValue);
}


/**
 * @brief Semi-private method to add a float using preferred encoding.
 *
 * @param[in] pMe   The encode context.
 * @param[in] fNum  The float to add.
 *
 * This converts the float to a half-precision if it can be done
 * without a loss of precision. See QCBOREncode_AddFloat().
 */
void
QCBOREncode_Private_AddPreferredFloat(QCBOREncodeContext *pMe, float fNum)
{
   IEEE754_union        FloatResult;
   bool                 bNoNaNPayload;
   struct IEEE754_ToInt IntResult;
   uint64_t             uNegValue;

    const uint32_t uSingle = UsefulBufUtil_CopyFloatToUint32(fNum);
#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
   if(IEEE754_SingleHasNaNPayload(uSingle) && !(pMe->uConfigFlags & QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD)) {
      pMe->uError = QCBOR_ERR_NOT_ALLOWED;
      return;
   }
#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */

   if(pMe->uConfigFlags & QCBOR_ENCODE_CONFIG_FLOAT_REDUCTION) {
      IntResult = IEEE754_SingleToInt(uSingle);
      switch(IntResult.type) {
         case IEEE754_ToInt_IS_INT:
            QCBOREncode_AddInt64(pMe, IntResult.integer.is_signed);
            return;
         case IEEE754_ToInt_IS_UINT:
            QCBOREncode_AddUInt64(pMe, IntResult.integer.un_signed);
            return;
         case IEEE754_ToInt_IS_65BIT_NEG:
            {
               if(IntResult.integer.un_signed == 0) {
                  uNegValue = UINT64_MAX;
               } else {
                  uNegValue = IntResult.integer.un_signed-1;
               }
               QCBOREncode_AddNegativeUInt64(pMe, uNegValue);
            }
            return;
         case IEEE754_ToInt_NaN:
            fNum = NAN;
            bNoNaNPayload = true;
            break;
         case IEEE754_ToInt_NO_CONVERSION:
            bNoNaNPayload = true;
      }
   } else  {
      bNoNaNPayload = false;
   }

   FloatResult = IEEE754_SingleToHalf(uSingle, bNoNaNPayload);

   QCBOREncode_Private_AddType7(pMe,
                                (uint8_t)FloatResult.uSize,
                                FloatResult.uValue);
}
#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */




/**
 * @brief Convert a big number to unsigned integer.
 *
 * @param[in]  BigNumber  Big number to convert.
 *
 * @return Converted unsigned.
 *
 * The big number must be less than 8 bytes long.
 **/
static uint64_t
QCBOREncode_Private_BigNumberToUInt(const UsefulBufC BigNumber)
{
   uint64_t uInt;
   size_t   uIndex;

   uInt = 0;
   for(uIndex = 0; uIndex < BigNumber.len; uIndex++) {
      uInt = (uInt << 8) + UsefulBufC_NTH_BYTE(BigNumber, uIndex);
   }

   return uInt;
}


/**
 * @brief Is there a carry when you subtract 1 from the BigNumber?
 *
 * @param[in]  BigNumber  Big number to check for carry.
 *
 * @return If there is a carry, \c true.
 *
 * If this returns @c true, then @c BigNumber - 1 is one byte shorter
 * than @c BigNumber.
 **/
static bool
QCBOREncode_Private_BigNumberCarry(const UsefulBufC BigNumber)
{
   bool       bCarry;
   UsefulBufC SubBigNum;

   // Improvement: rework without recursion?

   if(BigNumber.len == 0) {
      return true; /* Subtracting one from zero-length string gives a carry */
   } else {
      SubBigNum = UsefulBuf_Tail(BigNumber, 1);
      bCarry = QCBOREncode_Private_BigNumberCarry(SubBigNum);
      if(UsefulBufC_NTH_BYTE(BigNumber, 0) == 0x00 && bCarry) {
         /* Subtracting one from 0 gives a carry */
         return true;
      } else {
         return false;
      }
   }
}


/**
 * @brief Output negative bignum bytes with subtraction of 1.
 *
 * @param[in] pMe              The decode context.
 * @param[in] uTagRequirement  Either @ref QCBOR_ENCODE_AS_TAG or
 *                             @ref QCBOR_ENCODE_AS_BORROWED.
 * @param[in] BigNumber        The negative big number.
 */
static void
QCBOREncode_Private_AddTNegativeBigNumber(QCBOREncodeContext *pMe,
                                          const uint8_t       uTagRequirement,
                                          const UsefulBufC    BigNumber)
{
   size_t     uLen;
   bool       bCarry;
   bool       bCopiedSomething;
   uint8_t    uByte;
   UsefulBufC SubString;
   UsefulBufC NextSubString;

   QCBOREncode_Private_BigNumberTag(pMe, uTagRequirement, true);

   /* This works on any length without the need of an additional buffer */

   /* This subtracts one, possibly making the string shorter by one
    * 0x01 -> 0x00
    * 0x01 0x00 -> 0xff
    * 0x00 0x01 0x00 -> 0x00 0xff
    * 0x02 0x00 -> 0x01 0xff
    * 0xff -> 0xfe
    * 0xff 0x00 -> 0xfe 0xff
    * 0x01 0x00 0x00 -> 0xff 0xff
    *
    * This outputs the big number a byte at a time to be able to operate on
    * a big number of any length without memory allocation.
    */

   /* Compute the length up front because it goes in the encoded head */
   bCarry = QCBOREncode_Private_BigNumberCarry(UsefulBuf_Tail(BigNumber, 1));
   uLen = BigNumber.len;
   if(bCarry && BigNumber.len > 1 && UsefulBufC_NTH_BYTE(BigNumber, 0) >= 1) {
      uLen--;
   }
   QCBOREncode_Private_AppendCBORHead(pMe, CBOR_MAJOR_TYPE_BYTE_STRING, uLen,0);

   SubString = BigNumber;
   bCopiedSomething = false;
   while(SubString.len) {
      uByte = UsefulBufC_NTH_BYTE(SubString, 0);
      NextSubString = UsefulBuf_Tail(SubString, 1);
      bCarry = QCBOREncode_Private_BigNumberCarry(NextSubString);
      if(bCarry) {
         uByte--;
      }
      /* This avoids all but the last leading zero. See
       * QCBOREncode_Private_SkipLeadingZeros() */
      if(bCopiedSomething || NextSubString.len == 0 || uByte != 0) {
         UsefulOutBuf_AppendByte(&(pMe->OutBuf), uByte);
         bCopiedSomething = true;
      }
      SubString = NextSubString;
   }
}


/**
 * @brief Remove leading zeros.
 *
 * @param[in] BigNumber  The big number.
 *
 * @return Big number with no leading zeros.
 *
 * If the big number is all zeros, this returns a big number that is
 * a single zero rather than the empty string.
 *
 * RFC 8949 3.4.3 does not explicitly say decoders MUST handle the empty
 * string, but does say decoders MUST handle leading zeros. So
 * Postel's Law is applied here and 0 is not encoded as an empty
 * string.
 */
static UsefulBufC
QCBOREncode_Private_SkipLeadingZeros(const UsefulBufC BigNumber)
{
   UsefulBufC NLZ;
   NLZ = UsefulBuf_SkipLeading(BigNumber, 0x00);

   /* An all-zero string reduces to one 0, not an empty string. */
   if(NLZ.len == 0 &&
      BigNumber.len > 0 &&
      UsefulBufC_NTH_BYTE(BigNumber, 0) == 0x00) {
      NLZ.len = 1;
   }

   return NLZ;
}


/**
 * @brief Output a big number, preferred or not, with negative offset
 *
 * @param[in] pMe              The decode context.
 * @param[in] uTagRequirement  Either @ref QCBOR_ENCODE_AS_TAG or
 *                             @ref QCBOR_ENCODE_AS_BORROWED.
 * @param[in] bPreferred       Uses preferred serialization if true
 * @param[in] bNegative        Indicates big number is negative or postive.
 * @param[in] BigNumber        The big number.
 *
 * Regardless of whether preferred serialization is used, if the big
 * number is negative, one is subtracted before is output per CBOR
 * convetion for big numbers. This requires a little big number
 * arithmetic and adds some object code.
 *
 * If preferred serialization is used, then if the number is smaller
 * than UINT64_MAX and postive it is output as type 0 and if it is
 * equal to or smaller than UINT64_MAX it is output as a type 1
 * integer minus one.
 *
 * See QCBOREncode_AddTBigNumberRaw() for simple copy through.
 */
void
QCBOREncode_Private_AddTBigNumberMain(QCBOREncodeContext *pMe,
                                      const uint8_t       uTagRequirement,
                                      const bool          bPreferred,
                                      const bool          bNegative,
                                      const UsefulBufC    BigNumber)
{
   uint64_t   uInt;
   bool       bIs2exp64;
   uint8_t    uMajorType;
   UsefulBufC BigNumberNLZ;

#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
   if(!bPreferred && pMe->uConfigFlags & QCBOR_ENCODE_CONFIG_ONLY_PREFERRED_BIG_NUMBERS) {
      pMe->uError = QCBOR_ERR_NOT_PREFERRED;
      return;
   }
#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */

   BigNumberNLZ = QCBOREncode_Private_SkipLeadingZeros(BigNumber);

   static const uint8_t twoExp64[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
   bIs2exp64 = ! UsefulBuf_Compare(BigNumber, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(twoExp64));

   if(bPreferred && (BigNumberNLZ.len <= 8 || (bNegative && bIs2exp64))) {
      if(bIs2exp64) {
         /* 2^64 is a 9 byte big number. Since negative numbers are offset
          * by one in CBOR, it can be encoded as a type 1 negative. The
          * conversion below won't work because the uInt will overflow
          * before the subtraction of 1.
          */
         uInt = UINT64_MAX;
      } else {
         uInt = QCBOREncode_Private_BigNumberToUInt(BigNumberNLZ);
         if(bNegative) {
            uInt--;
         }
      }
      uMajorType = bNegative ? CBOR_MAJOR_TYPE_NEGATIVE_INT :
                               CBOR_MAJOR_TYPE_POSITIVE_INT;
      QCBOREncode_Private_AppendCBORHead(pMe, uMajorType, uInt, 0);
   } else {
      if(bNegative) {
         QCBOREncode_Private_AddTNegativeBigNumber(pMe, uTagRequirement, BigNumberNLZ);
      } else {
         QCBOREncode_AddTBigNumberRaw(pMe, false, uTagRequirement,BigNumberNLZ);
      }
   }
}




#ifndef QCBOR_DISABLE_EXP_AND_MANTISSA
/**
 * @brief  Semi-private method to add bigfloats and decimal fractions.
 *
 * @param[in] pMe               The encoding context to add the value to.
 * @param[in] uTagNumber               The type 6 tag indicating what this is to be.
 * @param[in] nMantissa          The @c int64_t mantissa if it is not a big number.
 * @param[in] nExponent          The exponent.
 *
 * This outputs either the @ref CBOR_TAG_DECIMAL_FRACTION or
 * @ref CBOR_TAG_BIGFLOAT tag. if @c uTag is @ref CBOR_TAG_INVALID64,
 * then this outputs the "borrowed" content format.
 *
 * The tag content output by this is an array with two members, the
 * exponent and then the mantissa. The mantissa can be either a big
 * number or an @c int64_t.
 *
 * This implementation cannot output an exponent further from 0 than
 * @c INT64_MAX.
 *
 * To output a mantissa that is between INT64_MAX and UINT64_MAX from 0,
 * it must be as a big number.
 *
 * Typically, QCBOREncode_AddTDecimalFraction(), QCBOREncode_AddTBigFloat(),
 * QCBOREncode_AddTDecimalFractionBigNum() or QCBOREncode_AddTBigFloatBigNum()
 * is called instead of this.
 */
void
QCBOREncode_Private_AddTExpIntMantissa(QCBOREncodeContext *pMe,
                                       const int           uTagRequirement,
                                       const uint64_t      uTagNumber,
                                       const int64_t       nExponent,
                                       const int64_t       nMantissa)
{
   /* This is for encoding either a big float or a decimal fraction,
    * both of which are an array of two items, an exponent and a
    * mantissa.  The difference between the two is that the exponent
    * is base-2 for big floats and base-10 for decimal fractions, but
    * that has no effect on the code here.
    */
   /* Separate from QCBOREncode_Private_AddTExpBigMantissa() because
    * linking QCBOREncode_AddTBigNumber() adds a lot because it
    * does preferred serialization of big numbers and the offset of 1
    * for CBOR negative numbers.
    */
   if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
      QCBOREncode_AddTagNumber(pMe, uTagNumber);
   }
   QCBOREncode_OpenArray(pMe);
   QCBOREncode_AddInt64(pMe, nExponent);
   QCBOREncode_AddInt64(pMe, nMantissa);
   QCBOREncode_CloseArray(pMe);
}

void
QCBOREncode_Private_AddTExpBigMantissa(QCBOREncodeContext *pMe,
                                       const int           uTagRequirement,
                                       const uint64_t      uTagNumber,
                                       const int64_t       nExponent,
                                       const UsefulBufC    BigNumMantissa,
                                       const bool          bBigNumIsNegative)
{
   /* This is for encoding either a big float or a decimal fraction,
    * both of which are an array of two items, an exponent and a
    * mantissa.  The difference between the two is that the exponent
    * is base-2 for big floats and base-10 for decimal fractions, but
    * that has no effect on the code here.
    */
   if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
      QCBOREncode_AddTagNumber(pMe, uTagNumber);
   }
   QCBOREncode_OpenArray(pMe);
   QCBOREncode_AddInt64(pMe, nExponent);
   QCBOREncode_AddTBigNumber(pMe, QCBOR_ENCODE_AS_TAG, bBigNumIsNegative, BigNumMantissa);
   QCBOREncode_CloseArray(pMe);
}


void
QCBOREncode_Private_AddTExpBigMantissaRaw(QCBOREncodeContext *pMe,
                                          const int           uTagRequirement,
                                          const uint64_t      uTagNumber,
                                          const int64_t       nExponent,
                                          const UsefulBufC    BigNumMantissa,
                                          const bool          bBigNumIsNegative)
{
   /* This is for encoding either a big float or a decimal fraction,
    * both of which are an array of two items, an exponent and a
    * mantissa.  The difference between the two is that the exponent
    * is base-2 for big floats and base-10 for decimal fractions, but
    * that has no effect on the code here.
    */
   /* Separate from QCBOREncode_Private_AddTExpBigMantissa() because
    * linking QCBOREncode_AddTBigNumber() adds a lot because it
    * does preferred serialization of big numbers and the offset of 1
    * for CBOR negative numbers.
    */
   if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
      QCBOREncode_AddTagNumber(pMe, uTagNumber);
   }
   QCBOREncode_OpenArray(pMe);
   QCBOREncode_AddInt64(pMe, nExponent);
   QCBOREncode_AddTBigNumberRaw(pMe, QCBOR_ENCODE_AS_TAG, bBigNumIsNegative, BigNumMantissa);
   QCBOREncode_CloseArray(pMe);
}

#endif /* ! QCBOR_DISABLE_EXP_AND_MANTISSA */

