package org.infinispan.container.entries;
import java.lang.reflect.Array;
import org.infinispan.commons.util.AbstractEntrySizeCalculatorHelper;
import sun.misc.Unsafe;
/**
* Entry Size calculator that returns an approximation of how much various primitives, primitive wrappers, Strings,
* and arrays
* @author wburns
* @since 8.0
*/
public class PrimitiveEntrySizeCalculator extends AbstractEntrySizeCalculatorHelper<Object, Object> {
public long calculateSize(Object key, Object value) {
return handleObject(key) + handleObject(value);
}
protected long handleObject(Object object) {
Class<?> objClass = object.getClass();
if (objClass == String.class) {
String realString = (String) object;
// The string is an object and has a reference to its class, int for the hash code and a pointer to the char[]
long objectSize = roundUpToNearest8(OBJECT_SIZE + POINTER_SIZE + 4 + POINTER_SIZE);
// We then include the char[] offset and size
return objectSize + roundUpToNearest8(Unsafe.ARRAY_CHAR_BASE_OFFSET + realString.length() *
Unsafe.ARRAY_CHAR_INDEX_SCALE);
} else if (objClass == Long.class) {
long longValue = ((Long) object).longValue();
if (longValue >= LongCacheConstraints.MIN_CACHE_VALUE &&
longValue <= LongCacheConstraints.MAX_CACHE_VALUE) {
return 0;
}
// We add in the size for a long, plus the object reference and the class ref
return roundUpToNearest8(Unsafe.ARRAY_LONG_INDEX_SCALE + OBJECT_SIZE + POINTER_SIZE);
} else if (objClass == Integer.class) {
int intValue = ((Integer) object).intValue();
if (intValue >= IntegerCacheConstraints.MIN_CACHE_VALUE &&
intValue <= IntegerCacheConstraints.MAX_CACHE_VALUE) {
return 0;
}
// We add in the size for a long, plus the object reference and the class ref
return roundUpToNearest8(Unsafe.ARRAY_INT_INDEX_SCALE + OBJECT_SIZE + POINTER_SIZE);
} else if (objClass == Short.class) {
short shortValue = ((Short) object).shortValue();
if (shortValue >= ShortCacheConstraints.MIN_CACHE_VALUE &&
shortValue <= ShortCacheConstraints.MAX_CACHE_VALUE) {
return 0;
}
return roundUpToNearest8(Unsafe.ARRAY_SHORT_INDEX_SCALE + OBJECT_SIZE + POINTER_SIZE);
} else if (objClass == Double.class) {
return roundUpToNearest8(Unsafe.ARRAY_DOUBLE_INDEX_SCALE + OBJECT_SIZE + POINTER_SIZE);
} else if (objClass == Float.class) {
return roundUpToNearest8(Unsafe.ARRAY_FLOAT_INDEX_SCALE + OBJECT_SIZE + POINTER_SIZE);
} else if (objClass == Boolean.class) {
// We assume all provided booleans are cached
return 0;
} else if (objClass == Character.class) {
char charValue = ((Character) object).charValue();
if (charValue >= CharacterCacheConstraints.MIN_CACHE_VALUE &&
charValue <= CharacterCacheConstraints.MAX_CACHE_VALUE) {
return 0;
}
return roundUpToNearest8(Unsafe.ARRAY_CHAR_INDEX_SCALE + OBJECT_SIZE + POINTER_SIZE);
} else if (objClass == Byte.class) {
// All byte values are cached
return 0;
} else if (objClass.isArray()) {
// We assume the array is of a type that supports shallow copy, such as the ones above
// We don't verify cached values if the array contains Booleans for example
Unsafe unsafe = getUnsafe();
Class<?> compClass = objClass.getComponentType();
int arrayLength = Array.getLength(object);
// Every array has a base offset which defines how large the array is and other overhead.
// Then each element in the array is indexed contiguously in memory thus we can simply multiply how
// many elements are in the array by how much of an offset each element requires. A normal object for example
// takes up the standard Object pointer worth of size but primitives take up space equivalent to how many byte
// they occupy.
long arraySize = roundUpToNearest8(unsafe.arrayBaseOffset(objClass) + unsafe.arrayIndexScale(objClass) *
arrayLength);
// If the component type isn't primitive we have to add in each of the instances
if (!compClass.isPrimitive()) {
// TODO: we could assume some values for given primitive wrappers.
for (int i = 0; i < arrayLength; ++i) {
arraySize += handleObject(Array.get(object, i));
}
}
return arraySize;
} else {
throw new IllegalArgumentException("Size of Class " + objClass +
" cannot be determined using given entry size calculator :" + getClass());
}
}
static class CharacterCacheConstraints {
static final short MAX_CACHE_VALUE = 127;
static final short MIN_CACHE_VALUE = 0;
}
static class ShortCacheConstraints {
static final short MAX_CACHE_VALUE = 127;
static final short MIN_CACHE_VALUE = -128;
}
static class LongCacheConstraints {
static final long MAX_CACHE_VALUE = 127;
static final long MIN_CACHE_VALUE = -128;
}
static class IntegerCacheConstraints {
static final int MAX_CACHE_VALUE = calculateMaxIntCache();
static final int MIN_CACHE_VALUE = -128;
static int calculateMaxIntCache() {
//We start from 128 as caching numbers up to 127 is required by the JLS:
//see 5.1.7 Boxing Conversion
for (int i = 128; i < Integer.MAX_VALUE; i++) {
if (Integer.valueOf(i) == Integer.valueOf(i))
continue;
else
return i - 1;
}
return Integer.MAX_VALUE;
}
}
/**
* Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package.
* Replace with a simple call to Unsafe.getUnsafe when integrating
* into a jdk.
*
* @return a sun.misc.Unsafe
*/
static Unsafe getUnsafe() {
try {
return Unsafe.getUnsafe();
} catch (SecurityException tryReflectionInstead) {}
try {
return java.security.AccessController.doPrivileged
(new java.security.PrivilegedExceptionAction<Unsafe>() {
public Unsafe run() throws Exception {
Class<Unsafe> k = Unsafe.class;
for (java.lang.reflect.Field f : k.getDeclaredFields()) {
f.setAccessible(true);
Object x = f.get(null);
if (k.isInstance(x))
return k.cast(x);
}
throw new NoSuchFieldError("the Unsafe");
}});
} catch (java.security.PrivilegedActionException e) {
throw new RuntimeException("Could not initialize intrinsics",
e.getCause());
}
}
}