public static String decodeUtf8(byte[] bytes, int index, int size) {
        if ((index | size | bytes.length - index - size) < 0) {
            throw new ArrayIndexOutOfBoundsException("buffer length=" + bytes.length + ", index=" + index + ", size="
                                                     + size);
        }

        int offset = index;
        final int limit = offset + size;

        // The longest possible resulting String is the same as the number of input bytes, when it is
        // all ASCII. For other cases, this over-allocates and we will truncate in the end.
        char[] resultArr = new char[size];
        int resultPos = 0;

        // Optimize for 100% ASCII (Hotspot loves small simple top-level loops like this).
        // This simple loop stops when we encounter a byte >= 0x80 (i.e. non-ASCII).
        while (offset < limit) {
            byte b = UnsafeUtil.getByte(bytes, offset);
            if (!DecodeUtil.isOneByte(b)) {
                break;
            }
            offset++;
            DecodeUtil.handleOneByte(b, resultArr, resultPos++);
        }

        while (offset < limit) {
            byte byte1 = UnsafeUtil.getByte(bytes, offset++);
            if (DecodeUtil.isOneByte(byte1)) {
                DecodeUtil.handleOneByte(byte1, resultArr, resultPos++);
                // It's common for there to be multiple ASCII characters in a run mixed in, so add an
                // extra optimized loop to take care of these runs.
                while (offset < limit) {
                    byte b = UnsafeUtil.getByte(bytes, offset);
                    if (!DecodeUtil.isOneByte(b)) {
                        break;
                    }
                    offset++;
                    DecodeUtil.handleOneByte(b, resultArr, resultPos++);
                }
            } else if (DecodeUtil.isTwoBytes(byte1)) {
                if (offset >= limit) {
                    throw invalidUtf8();
                }
                DecodeUtil.handleTwoBytes(byte1, /* byte2 */UnsafeUtil.getByte(bytes, offset++), resultArr,
                    resultPos++);
            } else if (DecodeUtil.isThreeBytes(byte1)) {
                if (offset >= limit - 1) {
                    throw invalidUtf8();
                }
                DecodeUtil.handleThreeBytes(byte1,
                /* byte2 */UnsafeUtil.getByte(bytes, offset++),
                /* byte3 */UnsafeUtil.getByte(bytes, offset++), resultArr, resultPos++);
            } else {
                if (offset >= limit - 2) {
                    throw invalidUtf8();
                }
                DecodeUtil.handleFourBytes(byte1,
                /* byte2 */UnsafeUtil.getByte(bytes, offset++),
                /* byte3 */UnsafeUtil.getByte(bytes, offset++),
                /* byte4 */UnsafeUtil.getByte(bytes, offset++), resultArr, resultPos++);
                // 4-byte case requires two chars.
                resultPos++;
            }
        }

        if (resultPos < resultArr.length) {
            resultArr = Arrays.copyOf(resultArr, resultPos);
        }
        return UnsafeUtil.moveToString(resultArr);
    }
Maximum number of bytes per Java UTF-16 char in UTF-8.

@see java.nio.charset.CharsetEncoder#maxBytesPerChar()
jraft-core/src/main/java/com/alipay/sofa/jraft/util/internal/UnsafeUtf8Util.java