public DecodedChar decodeNextChar() {
        // Clear the buffers ready for a new char's bytes
        inputBuffer.clear();
        outputBuffer.clear();
        outputBuffer.put((char) 0);
        outputBuffer.put((char) 0);
        outputBuffer.clear();

        DecodedChar decodedChar = null;
        // Count of the number of bytes we have consumed for this char
        int byteCnt = 0;

        try {
            while (decodedChar == null && byteCnt < MAX_BYTES_PER_CHAR) {
                byte b = 0;
                try {
                    final Byte suppliedByte = getNextByte();
                    if (suppliedByte == null) {
                        break;
                    } else {
                        byteCnt++;
                    }
                    b = suppliedByte;
                } catch (Exception e) {
                    throw new RuntimeException("Error getting next byte: " + e.getMessage(), e);
                }

                // Add the byte to our input buffer and get it ready for reading
                inputBuffer.put(b);
                inputBuffer.flip();

                // Attempt to decode the content of out input buffer
                outputBuffer.clear();
                final CoderResult coderResult = charsetDecoder.decode(
                        inputBuffer,
                        outputBuffer,
                        true);
                outputBuffer.flip();

                if (LOGGER.isTraceEnabled()) {
                    LOGGER.trace("coderResult: {}, byteCnt: {}, inPos: {}, inLimit: {}, " +
                                    "inBytes: [{}], outPos:{}, outLimit: {}, outputBuffer: [{}]",
                            coderResult,
                            byteCnt,
                            inputBuffer.position(),
                            inputBuffer.limit(),
                            ByteBufferUtils.byteBufferToHex(inputBuffer),
                            outputBuffer.position(),
                            outputBuffer.limit(),
                            outputBuffer);
                }

                // We may have only one byte of a multibyte char so a malformed result is likely
                // for any non ascii chars.
                if (!coderResult.isMalformed()) {
                    // We have decoded something so output it
                    final String decodedStr;
                    if (outputBuffer.remaining() == 2) {
//                    if (outputBuffer.array()[0] != 0 && outputBuffer.array()[1] != 0) {
                        int codePoint = Character.toCodePoint(outputBuffer.get(), outputBuffer.get());
                        decodedStr = new String(new int[]{codePoint}, 0, 1);

                        LOGGER.trace("Multi-char character found with codePoint: [{}], decodedStr: [{}]",
                                codePoint, decodedStr);
                    } else {
                        decodedStr = String.valueOf(outputBuffer.get());
                    }

                    LOGGER.trace("Decoded char {}, with byte count {}", decodedStr, byteCnt);
                    decodedChar = new DecodedChar(decodedStr, byteCnt);
                } else {
                    LOGGER.trace(() -> LogUtil.message("inputBuffer: {}, coderResult: {}, len: {}",
                            ByteBufferUtils.byteBufferInfo(inputBuffer), coderResult, coderResult.length()));
                    // Malformed so go round again as we obvs don't have enough bytes to form the char
                    // Update the input buffer to take the next byte
                    if (byteCnt < MAX_BYTES_PER_CHAR) {
                        charsetDecoder.reset();
                        inputBuffer.limit(byteCnt + 1);
                        inputBuffer.position(byteCnt);
                    } else {
                        inputBuffer.limit(MAX_BYTES_PER_CHAR);
                        inputBuffer.position(0);
                    }
                }
            }

            if (decodedChar == null && byteCnt > 0 && mode == Mode.LENIENT) {
                decodedChar = getAllMalformedBytes(byteCnt);
            }

            // Only throw if we have actually consumed something and couldn't decode it
            if (decodedChar == null && byteCnt > 0) {
                throw createDecoderException(byteCnt);
            }
        } catch (DecoderException e) {
            throw e;
        } catch (RuntimeException e) {
            throw new RuntimeException(LogUtil.message("Error decoding bytes after {} iterations: {}",
                    byteCnt, e.getMessage()), e);
        }

        return decodedChar;
    }
@return A {@link DecodedChar} representing the character decoded from n bytes supplied by byteSupplier or
null if byteSupplier has no bytes to supply.
@throws DecoderException If it fails to decode a char after consuming MAX_BYTES_PER_CHAR bytes
                         from byteSupplier
stroom-pipeline/src/main/java/stroom/pipeline/reader/ByteStreamDecoder.java