#7712 & #7945 BiValue Improvements

* Turn Enum into HashMap for direct lookup
* Use stateless Lambdas instead of named classes as converter functions for better performance
* Remove redundant instanceof checks by splitting existing converter functions between Java and Ruby version
* Use most restrictive instanceof check possible for Ruby converters for performance improvements

Fixes #8087
This commit is contained in:
Armin 2017-08-28 15:11:26 +02:00 committed by Jordan Sissel
parent 69fc14f765
commit a15116c96c

View file

@ -3,135 +3,58 @@ package org.logstash.bivalues;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import org.jruby.RubyBignum;
import org.jruby.RubyFixnum;
import org.jruby.RubyInteger;
import org.jruby.RubyNil;
import org.jruby.RubySymbol;
import org.jruby.ext.bigdecimal.RubyBigDecimal;
import org.jruby.java.proxies.ConcreteJavaProxy;
import org.jruby.java.proxies.JavaProxy;
import org.jruby.runtime.builtin.IRubyObject;
public enum BiValues {
JAVA_LANG_INTEGER(BiValueType.INT),
JAVA_LANG_LONG(BiValueType.LONG),
JAVA_MATH_BIGDECIMAL(BiValueType.DECIMAL),
JAVA_MATH_BIGINTEGER(BiValueType.BIGINT),
ORG_JRUBY_EXT_BIGDECIMAL_RUBYBIGDECIMAL(BiValueType.DECIMAL),
ORG_JRUBY_JAVA_PROXIES_CONCRETEJAVAPROXY(BiValueType.JAVAPROXY),
ORG_JRUBY_RUBYBIGNUM(BiValueType.BIGINT),
ORG_JRUBY_RUBYFIXNUM(BiValueType.LONG),
ORG_JRUBY_RUBYINTEGER(BiValueType.LONG),
ORG_JRUBY_RUBYNIL(BiValueType.NULL),
ORG_JRUBY_RUBYSYMBOL(BiValueType.SYMBOL), // one way conversion, a Java string will use STRING
NULL(BiValueType.NULL);
private static HashMap<String, String> initCache() {
HashMap<String, String> hm = new HashMap<>();
hm.put("java.lang.Integer", "JAVA_LANG_INTEGER");
hm.put("java.lang.Long", "JAVA_LANG_LONG");
hm.put("java.math.BigDecimal", "JAVA_MATH_BIGDECIMAL");
hm.put("java.math.BigInteger", "JAVA_MATH_BIGINTEGER");
hm.put("org.jruby.RubyBignum", "ORG_JRUBY_RUBYBIGNUM");
hm.put("org.jruby.RubyFixnum", "ORG_JRUBY_RUBYFIXNUM");
hm.put("org.jruby.RubyInteger", "ORG_JRUBY_RUBYINTEGER");
hm.put("org.jruby.RubyNil", "ORG_JRUBY_RUBYNIL");
hm.put("org.jruby.RubySymbol", "ORG_JRUBY_RUBYSYMBOL");
hm.put("org.jruby.ext.bigdecimal.RubyBigDecimal", "ORG_JRUBY_EXT_BIGDECIMAL_RUBYBIGDECIMAL");
hm.put("org.jruby.java.proxies.ConcreteJavaProxy", "ORG_JRUBY_JAVA_PROXIES_CONCRETEJAVAPROXY");
return hm;
public final class BiValues {
private BiValues() {
}
public static final NullBiValue NULL_BI_VALUE = NullBiValue.newNullBiValue();
private final BiValueType biValueType;
BiValues(BiValueType biValueType) {
this.biValueType = biValueType;
}
private static final HashMap<String, String> NAME_CACHE = initCache();
private BiValue build(Object value) {
return biValueType.build(value);
}
private static final Map<Class<?>, BiValues.BiValueType> CONVERTER_CACHE = initCache();
public static BiValue newBiValue(Object o) {
if (o == null) {
return NULL_BI_VALUE;
}
return valueOf(fetchName(o)).build(o);
final Class<?> cls = o.getClass();
final BiValues.BiValueType type = CONVERTER_CACHE.get(cls);
if (type == null) {
throw new IllegalArgumentException("Unsupported class " + cls);
}
return type.build(o);
}
private static String fetchName(Object o) {
final String cls = o.getClass().getName();
final String name = NAME_CACHE.get(cls);
if (name != null) {
return name;
}
return cacheName(cls);
private interface BiValueType {
BiValue build(Object value);
}
private static String cacheName(final String cls) {
final String toCache = cls.toUpperCase().replace('.', '_');
// TODO[Guy] log warn that we are seeing a uncached value
NAME_CACHE.put(cls, toCache);
return toCache;
}
private enum BiValueType {
SYMBOL {
BiValue build(Object value) {
if (value instanceof IRubyObject) {
return new SymbolBiValue((RubySymbol) value);
}
return new SymbolBiValue((String) value);
}
},
LONG {
BiValue build(Object value) {
if (value instanceof IRubyObject) {
return new LongBiValue((RubyInteger) value);
}
return new LongBiValue((Long) value);
}
},
INT {
BiValue build(Object value) {
if (value instanceof IRubyObject) {
return new IntegerBiValue((RubyInteger) value);
}
return new IntegerBiValue((Integer) value);
}
},
DECIMAL {
BiValue build(Object value) {
if (value instanceof IRubyObject) {
return new BigDecimalBiValue((RubyBigDecimal) value);
}
return new BigDecimalBiValue((BigDecimal) value);
}
},
NULL {
NullBiValue build(Object value) {
return NULL_BI_VALUE;
}
},
BIGINT {
BiValue build(Object value) {
if (value instanceof IRubyObject) {
return new BigIntegerBiValue((RubyBignum) value);
}
return new BigIntegerBiValue((BigInteger) value);
}
},
JAVAPROXY {
BiValue build(Object value) {
if (value instanceof IRubyObject) {
private static Map<Class<?>, BiValues.BiValueType> initCache() {
final Map<Class<?>, BiValues.BiValueType> hm = new HashMap<>(50, 0.2F);
hm.put(Integer.class, value -> new IntegerBiValue((Integer) value));
hm.put(Long.class, value -> new LongBiValue((Long) value));
hm.put(BigDecimal.class, value -> new BigDecimalBiValue((BigDecimal) value));
hm.put(BigInteger.class, value -> new BigIntegerBiValue((BigInteger) value));
hm.put(RubyBignum.class, value -> new BigIntegerBiValue((RubyBignum) value));
hm.put(RubyFixnum.class, value -> new LongBiValue((RubyInteger) value));
hm.put(RubyInteger.class, value -> new IntegerBiValue((RubyInteger) value));
hm.put(RubyNil.class, value -> NULL_BI_VALUE);
hm.put(RubySymbol.class, value -> new SymbolBiValue((RubySymbol) value));
hm.put(RubyBigDecimal.class, value -> new BigDecimalBiValue((RubyBigDecimal) value));
hm.put(ConcreteJavaProxy.class, value -> {
if (value instanceof JavaProxy) {
return new JavaProxyBiValue((JavaProxy) value);
}
return new JavaProxyBiValue(value);
});
return hm;
}
};
abstract BiValue build(Object value);
}
}