/*
* Copyright (C) 2004-2008 Jive Software. All rights reserved.
*
* 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.
*/
package org.jivesoftware.util.cache;
import org.jivesoftware.util.cache.Cacheable;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
/**
* Utility class for determining the sizes in bytes of commonly used objects.
* Classes implementing the Cacheable interface should use this class to
* determine their size.
*
* @author Matt Tucker
*/
public class CacheSizes {
/**
* Returns the size in bytes of a basic Object. This method should only
* be used for actual Object objects and not classes that extend Object.
*
* @return the size of an Object.
*/
public static int sizeOfObject() {
return 4;
}
/**
* Returns the size in bytes of a String.
*
* @param string the String to determine the size of.
* @return the size of a String.
*/
public static int sizeOfString(String string) {
if (string == null) {
return 0;
}
return 4 + string.getBytes().length;
}
/**
* Returns the size in bytes of a primitive int.
*
* @return the size of a primitive int.
*/
public static int sizeOfInt() {
return 4;
}
/**
* Returns the size in bytes of a primitive char.
*
* @return the size of a primitive char.
*/
public static int sizeOfChar() {
return 2;
}
/**
* Returns the size in bytes of a primitive boolean.
*
* @return the size of a primitive boolean.
*/
public static int sizeOfBoolean() {
return 1;
}
/**
* Returns the size in bytes of a primitive long.
*
* @return the size of a primitive long.
*/
public static int sizeOfLong() {
return 8;
}
/**
* Returns the size in bytes of a primitive double.
*
* @return the size of a primitive double.
*/
public static int sizeOfDouble() {
return 8;
}
/**
* Returns the size in bytes of a Date.
*
* @return the size of a Date.
*/
public static int sizeOfDate() {
return 12;
}
/**
* Returns the size in bytes of a Map object.
*
* @param map the Map object to determine the size of.
* @return the size of the Map object.
*/
public static int sizeOfMap(Map<?,?> map)
throws CannotCalculateSizeException {
if (map == null) {
return 0;
}
// Base map object -- should be something around this size.
int size = 36;
Set<? extends Map.Entry> set = map.entrySet();
// Add in size of each value
for (Map.Entry<Object, Object> entry : set) {
size += sizeOfAnything(entry.getKey());
size += sizeOfAnything(entry.getValue());
}
return size;
}
/**
* Returns the size in bytes of a Collection object. Elements are assumed to be
* <tt>String</tt>s, <tt>Long</tt>s or <tt>Cacheable</tt> objects.
*
* @param list the Collection object to determine the size of.
* @return the size of the Collection object.
*/
public static int sizeOfCollection(Collection list)
throws CannotCalculateSizeException {
if (list == null) {
return 0;
}
// Base list object (approximate)
int size = 36;
// Add in size of each value
Object[] values = list.toArray();
for (int i = 0; i < values.length; i++) {
size += sizeOfAnything(values[i]);
}
return size;
}
/**
* Returns the size of an object in bytes. Determining size by serialization
* is only used as a last resort.
*
* @return the size of an object in bytes.
*/
public static int sizeOfAnything(Object object)
throws CannotCalculateSizeException {
// If the object is Cacheable, ask it its size.
if (object == null) {
return 0;
}
if (object instanceof Cacheable) {
return ((Cacheable)object).getCachedSize();
}
// Check for other common types of objects put into cache.
else if (object instanceof String) {
return sizeOfString((String)object);
}
else if (object instanceof Long) {
return sizeOfLong();
}
else if (object instanceof Integer) {
return sizeOfObject() + sizeOfInt();
}
else if (object instanceof Double) {
return sizeOfObject() + sizeOfDouble();
}
else if (object instanceof Boolean) {
return sizeOfObject() + sizeOfBoolean();
}
else if (object instanceof Map) {
return sizeOfMap((Map)object);
}
else if (object instanceof long[]) {
long[] array = (long[])object;
return sizeOfObject() + array.length * sizeOfLong();
}
else if (object instanceof Collection) {
return sizeOfCollection((Collection)object);
}
else if (object instanceof byte[]) {
byte [] array = (byte[])object;
return sizeOfObject() + array.length;
}
// Default behavior -- serialize the object to determine its size.
else {
int size = 1;
try {
// Default to serializing the object out to determine size.
CacheSizes.NullOutputStream out = new NullOutputStream();
ObjectOutputStream outObj = new ObjectOutputStream(out);
outObj.writeObject(object);
size = out.size();
}
catch (IOException ioe) {
throw new CannotCalculateSizeException(object);
}
return size;
}
}
private static class NullOutputStream extends OutputStream {
int size = 0;
@Override
public void write(int b) throws IOException {
size++;
}
@Override
public void write(byte[] b) throws IOException {
size += b.length;
}
@Override
public void write(byte[] b, int off, int len) {
size += len;
}
/**
* Returns the number of bytes written out through the stream.
*
* @return the number of bytes written to the stream.
*/
public int size() {
return size;
}
}
}