diff --git a/smile/src/main/java/com/fasterxml/jackson/dataformat/smile/SmileParser.java b/smile/src/main/java/com/fasterxml/jackson/dataformat/smile/SmileParser.java index d1bca9537..58783b690 100644 --- a/smile/src/main/java/com/fasterxml/jackson/dataformat/smile/SmileParser.java +++ b/smile/src/main/java/com/fasterxml/jackson/dataformat/smile/SmileParser.java @@ -3,6 +3,7 @@ import java.io.*; import java.math.BigDecimal; import java.math.BigInteger; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import com.fasterxml.jackson.core.*; @@ -56,6 +57,24 @@ private Feature(boolean defaultState) { @Override public boolean enabledIn(int flags) { return (flags & getMask()) != 0; } } + /** + * Flag to indicate if the JDK version is 11 or later. This can be used in some methods + * to choose more optimal behavior. In particular, jdk9+ have different internals for + * the String class. + */ + private static final boolean JDK11_OR_LATER; + static { + boolean recentJdk; + try { + // The strip method was added in jdk11, so use it to detect a newer version + String.class.getMethod("strip"); + recentJdk = true; + } catch (Exception e) { + recentJdk = false; + } + JDK11_OR_LATER = recentJdk; + } + /* /********************************************************** /* Configuration @@ -1538,36 +1557,46 @@ private final String _decodeShortAsciiName(int len) throws IOException { // note: caller ensures we have enough bytes available // also note that since it's a short name (64 bytes), segment WILL have enough space - char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); - int outPtr = 0; - final byte[] inBuf = _inputBuffer; - int inPtr = _inputPtr; + if (JDK11_OR_LATER) { + // On newer JDKs the String internals changed and for ASCII strings the constructor + // that takes a byte array can be used and internally is just Arrays.copyOfRange. + final int inPtr = _inputPtr; + _inputPtr = inPtr + len; + String str = new String(_inputBuffer, inPtr, len, StandardCharsets.US_ASCII); + _textBuffer.resetWithString(str); + return str; + } else { + char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); + int outPtr = 0; + final byte[] inBuf = _inputBuffer; + int inPtr = _inputPtr; - // 29-Mar-2021, tatu: Still true with Java 8 / Jackson 2.13: unrolling - // does NOT appear to help here (no change, for jvm-benchmarks test, - // probably since most of the time symbol table lookup is used - /* - for (int inEnd = inPtr + len - 3; inPtr < inEnd; ) { - outBuf[outPtr++] = (char) inBuf[inPtr++]; - outBuf[outPtr++] = (char) inBuf[inPtr++]; - outBuf[outPtr++] = (char) inBuf[inPtr++]; - outBuf[outPtr++] = (char) inBuf[inPtr++]; - } - switch (len & 3) { - case 3: - outBuf[outPtr++] = (char) inBuf[inPtr++]; - case 2: - outBuf[outPtr++] = (char) inBuf[inPtr++]; - case 1: - outBuf[outPtr++] = (char) inBuf[inPtr++]; - case 0: - } - */ - for (int inEnd = inPtr + len; inPtr < inEnd; ++inPtr) { - outBuf[outPtr++] = (char) inBuf[inPtr]; - } - _inputPtr = inPtr; - return _textBuffer.setCurrentAndReturn(len); + // 29-Mar-2021, tatu: Still true with Java 8 / Jackson 2.13: unrolling + // does NOT appear to help here (no change, for jvm-benchmarks test, + // probably since most of the time symbol table lookup is used + /* + for (int inEnd = inPtr + len - 3; inPtr < inEnd; ) { + outBuf[outPtr++] = (char) inBuf[inPtr++]; + outBuf[outPtr++] = (char) inBuf[inPtr++]; + outBuf[outPtr++] = (char) inBuf[inPtr++]; + outBuf[outPtr++] = (char) inBuf[inPtr++]; + } + switch (len & 3) { + case 3: + outBuf[outPtr++] = (char) inBuf[inPtr++]; + case 2: + outBuf[outPtr++] = (char) inBuf[inPtr++]; + case 1: + outBuf[outPtr++] = (char) inBuf[inPtr++]; + case 0: + } + */ + for (int inEnd = inPtr + len; inPtr < inEnd; ++inPtr) { + outBuf[outPtr++] = (char) inBuf[inPtr]; + } + _inputPtr = inPtr; + return _textBuffer.setCurrentAndReturn(len); + } } /** @@ -2398,36 +2427,47 @@ protected final String _decodeShortAsciiValue(int len) throws IOException if ((_inputEnd - _inputPtr) < len) { _loadToHaveAtLeast(len); } - // Note: we count on fact that buffer must have at least 'len' (<= 64) empty char slots - final char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); - int outPtr = 0; - final byte[] inBuf = _inputBuffer; - int inPtr = _inputPtr; - // 29-Mar-2021, tatu: Still true with Java 8 / Jackson 2.13: unrolling - // does NOT appear to help here -- slows things down by 5% (for one test) - /* - for (int inEnd = inPtr + len - 3; inPtr < inEnd; ) { - outBuf[outPtr++] = (char) inBuf[inPtr++]; - outBuf[outPtr++] = (char) inBuf[inPtr++]; - outBuf[outPtr++] = (char) inBuf[inPtr++]; - outBuf[outPtr++] = (char) inBuf[inPtr++]; - } - switch (len & 3) { - case 3: - outBuf[outPtr++] = (char) inBuf[inPtr++]; - case 2: - outBuf[outPtr++] = (char) inBuf[inPtr++]; - case 1: - outBuf[outPtr++] = (char) inBuf[inPtr++]; - case 0: - }*/ - - for (final int end = inPtr + len; inPtr < end; ++inPtr) { - outBuf[outPtr++] = (char) inBuf[inPtr]; - } - _inputPtr = inPtr; - return _textBuffer.setCurrentAndReturn(len); + if (JDK11_OR_LATER) { + // On newer JDKs the String internals changed and for ASCII strings the constructor + // that takes a byte array can be used and internally is just Arrays.copyOfRange. + final int inPtr = _inputPtr; + _inputPtr = inPtr + len; + String str = new String(_inputBuffer, inPtr, len, StandardCharsets.US_ASCII); + _textBuffer.resetWithString(str); + return str; + } else { + // Note: we count on fact that buffer must have at least 'len' (<= 64) empty char slots + final char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); + int outPtr = 0; + final byte[] inBuf = _inputBuffer; + int inPtr = _inputPtr; + + // 29-Mar-2021, tatu: Still true with Java 8 / Jackson 2.13: unrolling + // does NOT appear to help here -- slows things down by 5% (for one test) + /* + for (int inEnd = inPtr + len - 3; inPtr < inEnd; ) { + outBuf[outPtr++] = (char) inBuf[inPtr++]; + outBuf[outPtr++] = (char) inBuf[inPtr++]; + outBuf[outPtr++] = (char) inBuf[inPtr++]; + outBuf[outPtr++] = (char) inBuf[inPtr++]; + } + switch (len & 3) { + case 3: + outBuf[outPtr++] = (char) inBuf[inPtr++]; + case 2: + outBuf[outPtr++] = (char) inBuf[inPtr++]; + case 1: + outBuf[outPtr++] = (char) inBuf[inPtr++]; + case 0: + }*/ + + for (final int end = inPtr + len; inPtr < end; ++inPtr) { + outBuf[outPtr++] = (char) inBuf[inPtr]; + } + _inputPtr = inPtr; + return _textBuffer.setCurrentAndReturn(len); + } } protected final String _decodeShortUnicodeValue(final int byteLen) throws IOException