groovy.json.internal.FastStringUtils Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package groovy.json.internal;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
/**
* @author Rick Hightower
* @author Stephane Landelle
*/
public class FastStringUtils {
public static final Unsafe UNSAFE;
public static final long STRING_VALUE_FIELD_OFFSET;
public static final long STRING_OFFSET_FIELD_OFFSET;
public static final long STRING_COUNT_FIELD_OFFSET;
public static final boolean ENABLED;
private static final boolean WRITE_TO_FINAL_FIELDS = Boolean.parseBoolean(System.getProperty("groovy.json.faststringutils.write.to.final.fields", "false"));
private static final boolean DISABLE = Boolean.parseBoolean(System.getProperty("groovy.json.faststringutils.disable", "false"));
private static Unsafe loadUnsafe() {
try {
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
return (Unsafe) unsafeField.get(null);
} catch (Exception e) {
return null;
}
}
static {
UNSAFE = DISABLE ? null : loadUnsafe();
ENABLED = UNSAFE != null;
}
private static long getFieldOffset(String fieldName) {
if (ENABLED) {
try {
return UNSAFE.objectFieldOffset(String.class.getDeclaredField(fieldName));
} catch (NoSuchFieldException e) {
// field undefined
}
}
return -1L;
}
static {
STRING_VALUE_FIELD_OFFSET = getFieldOffset("value");
STRING_OFFSET_FIELD_OFFSET = getFieldOffset("offset");
STRING_COUNT_FIELD_OFFSET = getFieldOffset("count");
}
protected enum StringImplementation {
/**
* JDK 7 drops offset and count so there is special handling for later version of JDK 7.
*/
DIRECT_CHARS {
@Override
public char[] toCharArray(String string) {
return (char[]) UNSAFE.getObject(string, STRING_VALUE_FIELD_OFFSET);
}
@Override
public String noCopyStringFromChars(char[] chars) {
if (WRITE_TO_FINAL_FIELDS) {
String string = new String();
UNSAFE.putObject(string, STRING_VALUE_FIELD_OFFSET, chars);
return string;
} else {
return new String(chars);
}
}
},
/**
* JDK 4 and JDK 5 have offset and count fields.
*/
OFFSET {
@Override
public char[] toCharArray(String string) {
char[] value = (char[]) UNSAFE.getObject(string, STRING_VALUE_FIELD_OFFSET);
int offset = UNSAFE.getInt(string, STRING_OFFSET_FIELD_OFFSET);
int count = UNSAFE.getInt(string, STRING_COUNT_FIELD_OFFSET);
if (offset == 0 && count == value.length) {
// no need to copy
return value;
} else {
return string.toCharArray();
}
}
@Override
public String noCopyStringFromChars(char[] chars) {
if (WRITE_TO_FINAL_FIELDS) {
String string = new String();
UNSAFE.putObject(string, STRING_VALUE_FIELD_OFFSET, chars);
UNSAFE.putInt(string, STRING_COUNT_FIELD_OFFSET, chars.length);
return string;
} else {
return new String(chars);
}
}
},
UNKNOWN {
@Override
public char[] toCharArray(String string) {
return string.toCharArray();
}
@Override
public String noCopyStringFromChars(char[] chars) {
return new String(chars);
}
};
public abstract char[] toCharArray(String string);
public abstract String noCopyStringFromChars(char[] chars);
}
public static StringImplementation STRING_IMPLEMENTATION = computeStringImplementation();
private static StringImplementation computeStringImplementation() {
if (STRING_VALUE_FIELD_OFFSET != -1L) {
if (STRING_OFFSET_FIELD_OFFSET != -1L && STRING_COUNT_FIELD_OFFSET != -1L) {
return StringImplementation.OFFSET;
} else if (STRING_OFFSET_FIELD_OFFSET == -1L && STRING_COUNT_FIELD_OFFSET == -1L && valueFieldIsCharArray()) {
return StringImplementation.DIRECT_CHARS;
} else {
// JDK 9
// TODO: GROOVY-7716 workaround - find way to optimize JDK9 String (or rethink need for Unsafe usage)
return StringImplementation.UNKNOWN;
}
} else {
return StringImplementation.UNKNOWN;
}
}
/**
* JDK9 Compat Strings enhancement changed the internal representation of the value field from a char[]
* to a byte[] (see http://openjdk.java.net/jeps/254).
*
* @return true if internal String value field is a char[], otherwise false
*/
private static boolean valueFieldIsCharArray() {
Object o = UNSAFE.getObject("", STRING_VALUE_FIELD_OFFSET);
return (o instanceof char[]);
}
/**
* @param string string to grab array from.
* @return char array from string
*/
public static char[] toCharArray(final String string) {
return STRING_IMPLEMENTATION.toCharArray(string);
}
/**
* @param charSequence to grab array from.
* @return char array from char sequence
*/
public static char[] toCharArray(final CharSequence charSequence) {
return toCharArray(charSequence.toString());
}
/**
* @param chars to shove array into.
* @return new string with chars copied into it
*/
public static String noCopyStringFromChars(final char[] chars) {
return STRING_IMPLEMENTATION.noCopyStringFromChars(chars);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy