def encodeLoop()

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")
  }