final override def putBigInt()

in daffodil-io/src/main/scala/org/apache/daffodil/io/DataOutputStreamImplMixin.scala [414:526]


  final override def putBigInt(
    bigInt: JBigInt,
    bitLengthFrom1: Int,
    signed: Boolean,
    finfo: FormatInfo
  ): Boolean = {
    Assert.usage(isWritable)
    Assert.usage(bitLengthFrom1 > 0)
    Assert.usage(signed || (!signed && bigInt.signum() >= 0))

    val res = {
      if (bitLengthFrom1 <= 64) {
        // output as a long
        val longInt = bigInt.longValue()
        putLongChecked(longInt, bitLengthFrom1, finfo)
      } else {
        // Note that we do not need to distinguish between signed and unsigned
        // here. Checks should have been performend earlier to ensure that the
        // bigInt value matches the signed/unsignedness. So we just need to
        // convert this to a byte array, ensure that the number of bits in that
        // bit array fit the bit length, and put the array.
        val array = bigInt.toByteArray
        val numWholeBytesNeeded = (bitLengthFrom1 + 7) / 8

        val maybeArrayToPut =
          if (array.size > numWholeBytesNeeded) {
            // This is the only case where we care about signedness. If the
            // BigInt is a positive value and the most significant bit is a 1,
            // then toByteArray will create one extra byte that is all zeros so
            // that the two's complement isn't negative. In this case, the array
            // size is 1 greater than numWholeBytesNeeded and the most
            // significant byte is zero. For a signed type (e.g. xs:integer),
            // having this extra byte, regardless of its value, means the BigInt
            // value was too big to fit into bitLengthFrom1 bits. However, if
            // this is a unsigned type (e.g. xs:nonNegativeInteger), this extra
            // byte can be zero and still fit into bitLengthFrom1. So if this is
            // the case, then just chop off that byte and write the array.
            // Otherwise, this results in Nope which ends up returning false.
            if (!signed && (array.size == numWholeBytesNeeded + 1) && array(0) == 0) {
              One(array.tail)
            } else {
              Nope
            }
          } else if (array.size < numWholeBytesNeeded) {
            // This bigInt value can definitely fit in the number of bits,
            // however, we need to pad it up to numWholeBytesNeed. We may need to
            // sign extend with the most significant bit of most significant byte
            // of the array
            val paddedArray = new Array[Byte](numWholeBytesNeeded)
            val numPaddingBytes = numWholeBytesNeeded - array.size

            array.copyToArray(paddedArray, numPaddingBytes)
            val sign = 0x80 & array(0)
            if (sign > 0) {
              // The most significant bit of the most significant byte was 1. That
              // means this was a negative number and these padding bytes must be
              // sign extended
              Assert.invariant(bigInt.signum() < 0)
              var i = 0
              while (i < numPaddingBytes) {
                paddedArray(i) = 0xff.toByte
                i += 1
              }
            }
            One(paddedArray)
          } else {
            // We got the right amount of bytes, however, it is possible that
            // more significant bits were set that can fit in bitLengthFrom1.
            // Determine if that is the case.
            val fragBits = bitLengthFrom1 % 8
            if (fragBits == 0) {
              // no frag bits, so the most significant bit is fine and no sign
              // extending is needed to be checked
              One(array)
            } else {
              val shifted =
                array(
                  0
                ) >> (fragBits - 1) // shift off the bits we don't care about (with sign extend shift)
              val signBit = shifted & 0x1 // get the most significant bit
              val signExtendedBits = shifted >> 1 // shift off the sign bit

              // At this point, signExtendedBits should be all 1's (i.e. -1) if
              // the sign bit was 1, or all zeros (i.e. 0) if the sign bit was 0.
              // If this isn't the case, then there were non-signed extended bits
              // above our most significant bit, and so this BigInt was too big
              // for the number of bits. If this is the case, result in a Nope
              // which ends up returning false.
              if (
                (signBit == 1 && signExtendedBits != -1) || (signBit == 0 && signExtendedBits != 0)
              ) {
                // error
                Nope
              } else {
                // The most significant byte is properly sign extended for the
                // for the number of bits, so the array is good
                One(array)
              }
            }
          }

        if (maybeArrayToPut.isDefined) {
          // at this point, we have an array that is of the right size and properly
          // sign extended. It is also BE MSBF, so we can put it just like we would
          // put a hexBinary array
          putByteArray(maybeArrayToPut.get, bitLengthFrom1, finfo)
        } else {
          false
        }
      }
    }
    res
  }