package com.cloudhopper.commons.charset;
/*
* #%L
* ch-commons-charset
* %%
* Copyright (C) 2012 Cloudhopper by Twitter
* %%
* Licensed 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.
* #L%
*/
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
/**
* Utility class for accessing private fields in common CharSequence classes
* such as String, StringBuilder, etc.
*
* @author joelauer
*/
public class CharSequenceAccessor {
// tests prove accessing raw char[] value from various common types of
// of CharSequence implementations like String, StringBuilder, etc. speed
// up calculations vs. using charAt()
static private final Constructor<String> stringConstructor;
static private final Field stringValueField;
static private final Field stringOffsetField;
static private final Field stringCountField;
static private final boolean hasStringFields;
static private final Field stringBuilderValueField;
static private final Field stringBuilderCountField;
static private final boolean hasStringBuilderFields;
static {
Constructor<String> stringConstructorTemp = null;
Field stringValueFieldTemp = null, stringOffsetFieldTemp = null, stringCountFieldTemp = null;
Field stringBuilderValueFieldTemp = null, stringBuilderCountFieldTemp = null;
boolean hasStringFieldsTemp = false, hasStringBuilderFieldsTemp = false;
try {
stringValueFieldTemp = String.class.getDeclaredField("value");
stringValueFieldTemp.setAccessible(true);
stringOffsetFieldTemp = String.class.getDeclaredField("offset");
stringOffsetFieldTemp.setAccessible(true);
stringCountFieldTemp = String.class.getDeclaredField("count");
stringCountFieldTemp.setAccessible(true);
hasStringFieldsTemp = true;
stringConstructorTemp = String.class.getDeclaredConstructor(int.class, int.class, char[].class);
} catch (Throwable t) {
// do nothing -- these optimizations just won't be used
//throw new RuntimeException("Unable to get [count] field from String.class", t);
}
try {
// string builder defines value[] in its parent class
stringBuilderValueFieldTemp = StringBuilder.class.getSuperclass().getDeclaredField("value");
stringBuilderValueFieldTemp.setAccessible(true);
// string builder defines value[] in its parent class
stringBuilderCountFieldTemp = StringBuilder.class.getSuperclass().getDeclaredField("count");
stringBuilderCountFieldTemp.setAccessible(true);
hasStringBuilderFieldsTemp = true;
} catch (Throwable t) {
// do nothing -- these optimizations just won't be used
//throw new RuntimeException("Unable to get [count] field from StringBuilder.class", t);
}
stringValueField = stringValueFieldTemp;
stringOffsetField = stringOffsetFieldTemp;
stringCountField = stringCountFieldTemp;
hasStringFields = hasStringFieldsTemp;
stringConstructor = stringConstructorTemp;
stringBuilderValueField = stringBuilderValueFieldTemp;
stringBuilderCountField = stringBuilderCountFieldTemp;
hasStringBuilderFields = hasStringBuilderFieldsTemp;
}
public static class CharArrayWrapper {
public char[] value;
public int offset;
public int length;
}
static public CharArrayWrapper access(CharSequence str) {
if (str == null) {
return null;
}
try {
if (str instanceof String) {
if (hasStringFields) {
CharArrayWrapper w = new CharArrayWrapper();
w.value = (char[])stringValueField.get(str);
w.offset = stringOffsetField.getInt(str);
w.length = stringCountField.getInt(str);
return w;
}
} else if (str instanceof StringBuilder) {
if (hasStringBuilderFields) {
CharArrayWrapper w = new CharArrayWrapper();
w.value = (char[])stringBuilderValueField.get(str);
w.length = stringBuilderCountField.getInt(str);
return w;
}
}
} catch (Throwable t) {
// do nothing
}
return null;
}
static public String createOptimizedString(char[] buffer, int offset, int length) {
try {
if (hasStringFields) {
// create a string
String s = new String();
stringValueField.set(s, buffer);
if (offset != 0)
stringOffsetField.setInt(s, offset);
stringCountField.setInt(s, length);
return s;
}
} catch (Throwable t) {
// do nothing
}
return new String(buffer, offset, length);
}
static public void updateStringBuilder(StringBuilder sb, int newLength) {
try {
if (hasStringBuilderFields) {
stringBuilderCountField.setInt(sb, newLength);
}
} catch (Throwable t) {
throw new RuntimeException("Unable to update count field for StringBuilder");
}
}
}