in daffodil-io/src/main/scala/org/apache/daffodil/io/processors/charset/BitsCharsetNonByteSize.scala [209:330]
def encodeLoop(in: CharBuffer, out: ByteBuffer): CoderResult = {
isReset = false
while (true) {
val inHasRemainingData = in.hasRemaining()
val outHasRemainingSpace = out.hasRemaining()
if (!inHasRemainingData) {
if (partialByteLenInBits > 0) {
if (!outHasRemainingSpace) {
// no remaining input, but there's a partial byte we need to write,
// and nowhere to write it to
return CoderResult.OVERFLOW
} else {
// no remaining input, but there's a partial byte, write the partial
// byte (includes padding) and finish
out.put(partialByte.toByte)
partialByte = 0
partialByteLenInBits = 0
return CoderResult.UNDERFLOW
}
} else {
// no remaining input, and no partial byte, nothing left to
// encode/write, finish
return CoderResult.UNDERFLOW
}
} else {
if (!outHasRemainingSpace) {
// there's input to encode, but nowhere to write it to
return CoderResult.OVERFLOW
}
// there's data to encode and enough space to write a byte, encode the
// character
val charCode = charToCharCode(in.get())
val charCodeToWrite =
if (charCode.isDefined) {
charCode.get
} else {
// character must fit in the bit width of a code unit, unmappable error
val unmappableAction = unmappableCharacterAction()
if (unmappableAction == CodingErrorAction.REPLACE) {
// CharsetEncoder, which handles character replacement, assumes
// that the replacement character is made up of full bytes. That
// isn't the case for this charset, so we will just manually
// replace the character ourselves and not let CharsetEncoder
// know there was ever a problem.
replacementChar
} else {
Assert.invariant(unmappableAction == CodingErrorAction.REPORT)
// Just report the unmappable character. Note that we should back
// up the position of the CharBuffer since we read a character
// but it was invalid. The charset encoder that called encodeLoop
// will handle skipping the invalid character. Note that the
// character is only skipped in CodingErrorAction.IGNORE and
// REPLACE. We handle REPLACE ourselves, and IGNORE isn't
// supported by DFDL, so this *shouldn't* matter, but it might
// someday if IGNORE is supported.
in.position(in.position() - 1)
return CoderResult.unmappableForLength(1)
}
}
if (partialByteLenInBits == 0) {
// no partial byte exists, make this the partial byte
if (requiredBitOrder eq BitOrder.MostSignificantBitFirst) {
partialByte = charCodeToWrite << (8 - bitWidthOfACodeUnit)
} else {
partialByte = charCodeToWrite
}
partialByteLenInBits = bitWidthOfACodeUnit
} else if ((partialByteLenInBits + bitWidthOfACodeUnit) >= 8) {
// This character's bits will fill up the byte. Some bits might be left over.
// There's a partial byte, add enough bits to make it a full byte and
// write it, then save whatever is remaining (could be 0 bits
// remaining) as a partial byte
val nUsedBits = 8 - partialByteLenInBits
if (requiredBitOrder eq BitOrder.MostSignificantBitFirst) {
val nLeftOverBits = bitWidthOfACodeUnit - nUsedBits
Assert.invariant(nLeftOverBits >= 0)
partialByte |= (charCodeToWrite >> nLeftOverBits) & 0xff
out.put(partialByte.toByte)
val leftOverMask = (1 << nLeftOverBits) - 1
val newPartialByte = (charCodeToWrite & leftOverMask) << (8 - nLeftOverBits)
partialByte = newPartialByte & 0xff
partialByteLenInBits = nLeftOverBits
} else {
// LSBF
partialByte |= (charCodeToWrite << partialByteLenInBits) & 0xff
out.put(partialByte.toByte)
partialByte = charCodeToWrite >> nUsedBits
partialByteLenInBits = bitWidthOfACodeUnit - nUsedBits
}
} else {
// there's a partial byte but there won't be enough bits to make it a full byte
if (requiredBitOrder eq BitOrder.MostSignificantBitFirst) {
partialByte |= (charCodeToWrite << (8 - partialByteLenInBits - bitWidthOfACodeUnit)) & 0xff
} else {
// LSBF
partialByte |= (charCodeToWrite << partialByteLenInBits) & 0xff
}
partialByteLenInBits = bitWidthOfACodeUnit + partialByteLenInBits
}
//
// Verify invariant that the unused bits of the partialByte are always 0
//
Assert.invariant({
val unusedPartialByteMask: Int =
if (requiredBitOrder eq BitOrder.MostSignificantBitFirst)
(1 << (8 - partialByteLenInBits)) - 1
else
(-1 << partialByteLenInBits) & 0xff
val unusedPartialByteBits = partialByte & unusedPartialByteMask
unusedPartialByteBits == 0
})
}
}
Assert.impossible("Incorrect return from encodeLoop")
}