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
}