Merge pull request #27302 from akka/wip-27301-copyUSAsciiStrToBytes-patriknw
Harden copyUSAciiBytesToStr, so that it works on Raspberry Pi, #27301
This commit is contained in:
commit
0f01df713f
2 changed files with 126 additions and 13 deletions
|
|
@ -12,6 +12,20 @@ class AsciiStringCopySpec extends WordSpec with Matchers {
|
|||
|
||||
"The copyUSAsciiStrToBytes optimization" must {
|
||||
|
||||
"select working algorithm" in {
|
||||
if (Unsafe.isIsJavaVersion9Plus) {
|
||||
Unsafe.testUSAsciiStrToBytesAlgorithm0("abc") should ===(true)
|
||||
// this is known to fail with JDK 11 on ARM32 (Raspberry Pi),
|
||||
// and therefore algorithm 0 is selected on that architecture
|
||||
Unsafe.testUSAsciiStrToBytesAlgorithm1("abc") should ===(true)
|
||||
Unsafe.testUSAsciiStrToBytesAlgorithm2("abc") should ===(false)
|
||||
} else {
|
||||
Unsafe.testUSAsciiStrToBytesAlgorithm0("abc") should ===(true)
|
||||
Unsafe.testUSAsciiStrToBytesAlgorithm1("abc") should ===(false)
|
||||
Unsafe.testUSAsciiStrToBytesAlgorithm2("abc") should ===(true)
|
||||
}
|
||||
}
|
||||
|
||||
"copy string internal representation successfully" in {
|
||||
val ascii = "abcdefghijklmnopqrstuvxyz"
|
||||
val byteArray = new Array[Byte](ascii.length)
|
||||
|
|
|
|||
|
|
@ -4,17 +4,22 @@
|
|||
|
||||
package akka.util;
|
||||
|
||||
import akka.annotation.InternalApi;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* INTERNAL API
|
||||
*/
|
||||
@InternalApi
|
||||
public final class Unsafe {
|
||||
public static final sun.misc.Unsafe instance;
|
||||
|
||||
private static final long stringValueFieldOffset;
|
||||
private static final boolean isJavaVersion9Plus;
|
||||
private static final int copyUSAsciiStrToBytesAlgorithm;
|
||||
|
||||
static {
|
||||
try {
|
||||
|
|
@ -30,29 +35,110 @@ public final class Unsafe {
|
|||
else instance = found;
|
||||
stringValueFieldOffset = instance.objectFieldOffset(String.class.getDeclaredField("value"));
|
||||
|
||||
// See Oracle section 1.5.3 at:
|
||||
// https://docs.oracle.com/javase/8/docs/technotes/guides/versioning/spec/versioning2.html
|
||||
final int[] version = Arrays.
|
||||
stream(System.getProperty("java.specification.version").split("\\.")).
|
||||
mapToInt(Integer::parseInt).
|
||||
toArray();
|
||||
final int javaVersion = version[0] == 1 ? version[1] : version[0];
|
||||
isJavaVersion9Plus = javaVersion > 8;
|
||||
isJavaVersion9Plus = isIsJavaVersion9Plus();
|
||||
|
||||
// Select optimization algorithm for `copyUSAciiBytesToStr`.
|
||||
// For example algorithm 1 will fail with JDK 11 on ARM32 (Raspberry Pi),
|
||||
// and therefore algorithm 0 is selected on that architecture.
|
||||
String testStr = "abc";
|
||||
if (isJavaVersion9Plus && testUSAsciiStrToBytesAlgorithm1(testStr))
|
||||
copyUSAsciiStrToBytesAlgorithm = 1;
|
||||
else if (testUSAsciiStrToBytesAlgorithm2(testStr))
|
||||
copyUSAsciiStrToBytesAlgorithm = 2;
|
||||
else
|
||||
copyUSAsciiStrToBytesAlgorithm = 0;
|
||||
|
||||
} catch (Throwable t) {
|
||||
throw new ExceptionInInitializerError(t);
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyUSAsciiStrToBytes(String str, byte[] bytes) {
|
||||
if (isJavaVersion9Plus) {
|
||||
static boolean isIsJavaVersion9Plus() {
|
||||
// See Oracle section 1.5.3 at:
|
||||
// https://docs.oracle.com/javase/8/docs/technotes/guides/versioning/spec/versioning2.html
|
||||
final int[] version = Arrays.
|
||||
stream(System.getProperty("java.specification.version").split("\\.")).
|
||||
mapToInt(Integer::parseInt).
|
||||
toArray();
|
||||
final int javaVersion = version[0] == 1 ? version[1] : version[0];
|
||||
return javaVersion > 8;
|
||||
}
|
||||
|
||||
static boolean testUSAsciiStrToBytesAlgorithm0(String str) {
|
||||
try {
|
||||
byte[] bytes = new byte[str.length()];
|
||||
|
||||
// copy of implementation in copyUSAciiBytesToStr
|
||||
byte[] strBytes = str.getBytes(StandardCharsets.US_ASCII);
|
||||
System.arraycopy(strBytes, 0, bytes, 0, str.length());
|
||||
// end copy
|
||||
|
||||
String result = copyUSAciiBytesToStr(str.length(), bytes);
|
||||
return str.equals(result);
|
||||
} catch (Throwable all) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean testUSAsciiStrToBytesAlgorithm1(String str) {
|
||||
try {
|
||||
byte[] bytes = new byte[str.length()];
|
||||
|
||||
// copy of implementation in copyUSAciiBytesToStr
|
||||
final byte[] chars = (byte[]) instance.getObject(str, stringValueFieldOffset);
|
||||
System.arraycopy(chars, 0, bytes, 0, str.length());
|
||||
} else {
|
||||
// end copy
|
||||
|
||||
String result = copyUSAciiBytesToStr(str.length(), bytes);
|
||||
return str.equals(result);
|
||||
} catch (Throwable all) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean testUSAsciiStrToBytesAlgorithm2(String str) {
|
||||
try {
|
||||
byte[] bytes = new byte[str.length()];
|
||||
|
||||
// copy of implementation in copyUSAciiBytesToStr
|
||||
final char[] chars = (char[]) instance.getObject(str, stringValueFieldOffset);
|
||||
int i = 0;
|
||||
while (i < str.length()) {
|
||||
bytes[i] = (byte) chars[i++];
|
||||
}
|
||||
// end copy
|
||||
|
||||
String result = copyUSAciiBytesToStr(str.length(), bytes);
|
||||
return str.equals(result);
|
||||
} catch (Throwable all) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static String copyUSAciiBytesToStr(int length, byte[] bytes) {
|
||||
char[] resultChars = new char[length];
|
||||
int i = 0;
|
||||
while (i < length) {
|
||||
// UsAscii
|
||||
resultChars[i] = (char) bytes[i];
|
||||
i += 1;
|
||||
}
|
||||
return String.valueOf(resultChars, 0, length);
|
||||
}
|
||||
|
||||
public static void copyUSAsciiStrToBytes(String str, byte[] bytes) {
|
||||
if (copyUSAsciiStrToBytesAlgorithm == 1) {
|
||||
final byte[] chars = (byte[]) instance.getObject(str, stringValueFieldOffset);
|
||||
System.arraycopy(chars, 0, bytes, 0, str.length());
|
||||
} else if (copyUSAsciiStrToBytesAlgorithm == 2) {
|
||||
final char[] chars = (char[]) instance.getObject(str, stringValueFieldOffset);
|
||||
int i = 0;
|
||||
while (i < str.length()) {
|
||||
bytes[i] = (byte) chars[i++];
|
||||
}
|
||||
} else {
|
||||
byte[] strBytes = str.getBytes(StandardCharsets.US_ASCII);
|
||||
System.arraycopy(strBytes, 0, bytes, 0, str.length());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -61,7 +147,7 @@ public final class Unsafe {
|
|||
long s1 = 601258;
|
||||
int i = 0;
|
||||
|
||||
if (isJavaVersion9Plus) {
|
||||
if (copyUSAsciiStrToBytesAlgorithm == 1) {
|
||||
final byte[] chars = (byte[]) instance.getObject(str, stringValueFieldOffset);
|
||||
while (i < str.length()) {
|
||||
long x = s0 ^ (long)chars[i++]; // Mix character into PRNG state
|
||||
|
|
@ -74,12 +160,25 @@ public final class Unsafe {
|
|||
x ^= x >>> 17;
|
||||
s1 = x ^ y;
|
||||
}
|
||||
} else {
|
||||
} else if (copyUSAsciiStrToBytesAlgorithm == 2) {
|
||||
final char[] chars = (char[]) instance.getObject(str, stringValueFieldOffset);
|
||||
while (i < str.length()) {
|
||||
long x = s0 ^ (long)chars[i++]; // Mix character into PRNG state
|
||||
long y = s1;
|
||||
|
||||
// Xorshift128+ round
|
||||
s0 = y;
|
||||
x ^= x << 23;
|
||||
y ^= y >>> 26;
|
||||
x ^= x >>> 17;
|
||||
s1 = x ^ y;
|
||||
}
|
||||
} else {
|
||||
byte[] chars = str.getBytes(StandardCharsets.US_ASCII);
|
||||
while (i < str.length()) {
|
||||
long x = s0 ^ (long)chars[i++]; // Mix character into PRNG state
|
||||
long y = s1;
|
||||
|
||||
// Xorshift128+ round
|
||||
s0 = y;
|
||||
x ^= x << 23;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue