Subject: Update String access in StringUtils
Date: Thu, 28 Feb 2019 13:55:04 +0100
From: Andrej Shadura <andrewsh@debian.org>

See https://stackoverflow.com/questions/52620885/string-to-object-conversion-not-working-in-java-11:

> The message “class [B cannot be cast to class [C” indicates that
> the method is trying to cast a byte[] array to a char[] array. Since
> the code location also has a name like FastStringUtils.toCharArray,
> I can guess what happens here.
> 
> This class seems to hack into the java.lang.String class and read its
> value field in a questionable attempt of performance improvement. Since
> Java 9, this internal array is a byte[] array instead of a char[] array,
> which makes this hack fail at runtime.

Initially based on:

commit a8af29f932d73d8d1c6e44e49b68f9bb44905685
Author: Peter Lawrey <peter.lawrey@higherfrequencytrading.com>
Date:   Wed Mar 2 14:00:44 2016 +0000

    Tuning FIX to minimise garbage.

--- a/src/main/java/net/openhft/chronicle/core/util/StringUtils.java
+++ b/src/main/java/net/openhft/chronicle/core/util/StringUtils.java
@@ -19,7 +19,6 @@
 import net.openhft.chronicle.core.annotation.ForceInline;
 import org.jetbrains.annotations.NotNull;
 
-import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 
 import static java.lang.Character.toLowerCase;
@@ -30,14 +29,11 @@
 public enum StringUtils {
     ;
 
-    private static final Constructor<String> STRING_CONSTRUCTOR;
     private static final Field S_VALUE, SB_VALUE, SB_COUNT;
     private static final long MAX_VALUE_DIVIDE_10 = Long.MAX_VALUE / 10;
 
     static {
         try {
-            STRING_CONSTRUCTOR = String.class.getDeclaredConstructor(char[].class, boolean.class);
-            STRING_CONSTRUCTOR.setAccessible(true);
             S_VALUE = String.class.getDeclaredField("value");
             S_VALUE.setAccessible(true);
             SB_VALUE = Class.forName("java.lang.AbstractStringBuilder").getDeclaredField("value");
@@ -93,6 +89,10 @@
     public static char[] extractChars(StringBuilder sb) {
         try {
             return (char[]) SB_VALUE.get(sb);
+        } catch (ClassCastException e) {
+            final char[] data = new char[sb.length()];
+            sb.getChars(0, sb.length(), data, 0);
+            return data;
         } catch (IllegalAccessException | IllegalArgumentException e) {
             throw new AssertionError(e);
         }
@@ -101,6 +101,8 @@
     public static char[] extractChars(String s) {
         try {
             return (char[]) S_VALUE.get(s);
+        } catch (ClassCastException e) {
+            return s.toCharArray();
         } catch (IllegalAccessException | IllegalArgumentException e) {
             throw new AssertionError(e);
         }
@@ -115,11 +117,7 @@
     }
 
     public static String newString(char[] chars) {
-        try {
-            return STRING_CONSTRUCTOR.newInstance(chars, true);
-        } catch (Exception e) {
-            throw new AssertionError(e);
-        }
+        return new String(chars);
     }
 
     public static String firstLowerCase(String str) {
