Automated rollback of commit ab531655c3.

PiperOrigin-RevId: 881454470
This commit is contained in:
Protobuf Team Bot 2026-03-10 08:46:33 -07:00 committed by Copybara-Service
parent ab531655c3
commit 4efe3af06b

View file

@ -18,12 +18,8 @@ import static java.lang.Character.MIN_SURROGATE;
import static java.lang.Character.isSurrogatePair;
import static java.lang.Character.toCodePoint;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
@ -56,20 +52,10 @@ final class Utf8 {
* depending on what is available on the platform. The processor is the platform-optimized
* delegate for which all methods are delegated directly to.
*/
private static final Processor processor = createProcessor();
private static Processor createProcessor() {
if (Android.isOnAndroidDevice()) {
return new SafeProcessor();
}
if (UnsafeProcessorWithEncodedLength.isAvailable()) {
return new UnsafeProcessorWithEncodedLength();
}
if (UnsafeProcessor.isAvailable()) {
return new UnsafeProcessor();
}
return new SafeProcessor();
}
private static final Processor processor =
(!Android.isOnAndroidDevice() && UnsafeProcessor.isAvailable())
? new UnsafeProcessor()
: new SafeProcessor();
/**
* A mask used when performing unsafe reads to determine if a long value contains any non-ASCII
@ -153,7 +139,41 @@ final class Utf8 {
* time and space.
*/
static int encodedLength(String string) {
return processor.encodedLength(string);
// Warning to maintainers: this implementation is highly optimized.
int utf16Length = string.length();
int utf8Length = utf16Length;
int i = 0;
// This loop optimizes for pure ASCII.
while (i < utf16Length && string.charAt(i) < 0x80) {
i++;
}
// This loop optimizes for chars less than 0x800.
for (; i < utf16Length; i++) {
char c = string.charAt(i);
if (c < 0x800) {
utf8Length += ((0x7f - c) >>> 31); // branch free!
} else {
try {
utf8Length += encodedLengthGeneral(string, i);
} catch (UnpairedSurrogateException e) {
// Our hand rolled loops don't handle unpaired surrogates here. This should be
// exceptionally rare, so we fallback to the naive implementation to find out the
// length that the Java internal implementation will return for this string after
// replacement characters.
return string.getBytes(StandardCharsets.UTF_8).length;
}
break;
}
}
if (utf8Length < utf16Length) {
// Necessary and sufficient condition for overflow because of maximum 3x expansion
throw new IllegalArgumentException(
"UTF-8 length does not fit in int: " + (utf8Length + (1L << 32)));
}
return utf8Length;
}
private static int encodedLengthGeneral(String string, int start)
@ -523,44 +543,6 @@ final class Utf8 {
/** Encodes the input character sequence to a direct {@link ByteBuffer} instance. */
protected abstract void encodeUtf8Internal(String in, ByteBuffer out);
int encodedLength(String string) {
// Warning to maintainers: this implementation is highly optimized.
int utf16Length = string.length();
int utf8Length = utf16Length;
int i = 0;
// This loop optimizes for pure ASCII.
while (i < utf16Length && string.charAt(i) < 0x80) {
i++;
}
// This loop optimizes for chars less than 0x800.
for (; i < utf16Length; i++) {
char c = string.charAt(i);
if (c < 0x800) {
utf8Length += ((0x7f - c) >>> 31); // branch free!
} else {
try {
utf8Length += encodedLengthGeneral(string, i);
} catch (UnpairedSurrogateException e) {
// Our hand rolled loops don't handle unpaired surrogates here. This should be
// exceptionally rare, so we fallback to the naive implementation to find out the
// length that the Java internal implementation will return for this string after
// replacement characters.
return string.getBytes(StandardCharsets.UTF_8).length;
}
break;
}
}
if (utf8Length < utf16Length) {
// Necessary and sufficient condition for overflow because of maximum 3x expansion
throw new IllegalArgumentException(
"UTF-8 length does not fit in int: " + (utf8Length + (1L << 32)));
}
return utf8Length;
}
}
/** {@link Processor} implementation that does not use any {@code sun.misc.Unsafe} methods. */
@ -850,7 +832,7 @@ final class Utf8 {
}
/** {@link Processor} that uses {@code sun.misc.Unsafe} where possible to improve performance. */
static class UnsafeProcessor extends Processor {
static final class UnsafeProcessor extends Processor {
/** Indicates whether or not all required unsafe operations are supported on this platform. */
static boolean isAvailable() {
if (!hasUnsafeArrayOperations() || !hasUnsafeByteBufferOperations()) {
@ -1224,43 +1206,6 @@ final class Utf8 {
}
}
/**
* {@link Processor} that extends {@link UnsafeProcessor} and uses {@code
* java.lang.String#encodedLength(Charset)} on JDK versions that support it.
*/
static final class UnsafeProcessorWithEncodedLength extends UnsafeProcessor {
private static final MethodHandle encodedLengthHandle = createEncodedLengthHandle();
private static MethodHandle createEncodedLengthHandle() {
try {
return MethodHandles.insertArguments(
MethodHandles.lookup()
.findVirtual(
String.class, "encodedLength", MethodType.methodType(int.class, Charset.class)),
1,
StandardCharsets.UTF_8);
} catch (ReflectiveOperationException e) {
return null;
}
}
static boolean isAvailable() {
return encodedLengthHandle != null;
}
@Override
int encodedLength(String in) {
try {
return (int) encodedLengthHandle.invokeExact(in);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new IllegalStateException(e);
}
}
}
/**
* Utility methods for decoding bytes into {@link String}. Callers are responsible for extracting
* bytes (possibly using Unsafe methods), and checking remaining bytes. All other UTF-8 validity