package railo.commons.lang; import java.io.ByteArrayOutputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import railo.loader.engine.CFMLEngineFactory; import railo.loader.util.Util; import railo.runtime.exp.PageException; import railo.runtime.type.Collection; import railo.runtime.type.Query; import railo.runtime.util.Creation; public class SizeAndCount { private static final int OBJECT_GRANULARITY_IN_BYTES = 8; private static final int WORD_SIZE = Arch.getVMArchitecture().getWordSize(); private static final int HEADER_SIZE = 2 * WORD_SIZE; private static final int DOUBLE_SIZE = 8; private static final int FLOAT_SIZE = 4; private static final int LONG_SIZE = 8; private static final int INT_SIZE = 4; private static final int SHORT_SIZE = 2; private static final int BYTE_SIZE = 1; private static final int BOOLEAN_SIZE = 1; private static final int CHAR_SIZE = 2; private static final int REF_SIZE = WORD_SIZE; public static Size sizeOf(Object obj) throws PageException { Creation creator = CFMLEngineFactory.getInstance().getCreationUtil(); Size size = new Size(0,0); sizeOf(creator,size, obj, new HashSet<Object>()); return size; } private static void sizeOf(Creation creator,Size size,Object obj, Set<Object> parents) throws PageException { if(obj==null) return; Object raw=obj; // TODO this is just a patch solution, find a better way to handle this kind of situation (Wrapper classes) if(isInstaneOf(obj.getClass(),"railo.runtime.text.xml.struct.XMLStruct")) { try { Method toNode = raw.getClass().getMethod("toNode", new Class[0]); raw=toNode.invoke(obj, new Object[0]); } catch (Throwable e) { e.printStackTrace(); } } if(parents.contains(raw)) return; parents.add(raw); try { if(obj instanceof Collection) { if(obj instanceof Query) sizeOf(creator,size,(Query)obj,parents); else sizeOf(creator,size,((Collection)obj).valueIterator(),parents); return; } // Map else if(obj instanceof Map) { sizeOf(creator,size,((Map)obj).values().iterator(),parents); return; } // List else if(obj instanceof List) { sizeOf(creator,size,((List)obj).iterator(),parents); return; } // String else if(obj instanceof String){ size.size+= (CHAR_SIZE*((String)obj).length())+REF_SIZE; } // Number else if(obj instanceof Number){ if(obj instanceof Double) size.size+= DOUBLE_SIZE+REF_SIZE; else if(obj instanceof Float) size.size+= FLOAT_SIZE+REF_SIZE; else if(obj instanceof Long) size.size+= LONG_SIZE+REF_SIZE; else if(obj instanceof Integer) size.size+= INT_SIZE+REF_SIZE; else if(obj instanceof Short) size.size+= SHORT_SIZE+REF_SIZE; else if(obj instanceof Byte) size.size+= BYTE_SIZE+REF_SIZE; } else if(obj instanceof Boolean) size.size+= REF_SIZE+BOOLEAN_SIZE; else if(obj instanceof Character) size.size+= REF_SIZE+CHAR_SIZE; else size.size+=_sizeOf(obj); size.count++; } finally { //parents.remove(raw);// TODO should we not remove, to see if sister is me. } } private static void sizeOf(Creation creator,Size size,Iterator it, Set<Object> parents) throws PageException { size.count++; size.size+=REF_SIZE; while(it.hasNext()){ sizeOf(creator,size,it.next(),parents); } } private static void sizeOf(Creation creator,Size size,Query qry, Set<Object> parents) throws PageException { size.count++; size.size+=REF_SIZE; int rows=qry.getRecordcount(); String[] strColumns = qry.getColumns(); Collection.Key[] columns = new Collection.Key[strColumns.length]; for(int col=0;col<columns.length;col++){ columns[col]=creator.createKey(strColumns[col]); } for(int row=1;row<=rows;row++){ for(int col=0;col<columns.length;col++){ sizeOf(creator,size,qry.getAt(columns[col], row),parents); } } } public static boolean isInstaneOf(Class src ,String className) { if(src==null) return false; if(className.equals(src.getName()))return true; // interfaces Class[] interfaces = src.getInterfaces(); for(int i=0;i<interfaces.length;i++){ if(isInstaneOf(interfaces[i], className)) return true; } return isInstaneOf(src.getSuperclass(), className); } public static int _sizeOf(Object o) { //System.err.println(o.getClass().getName()); ByteArrayOutputStream os = new ByteArrayOutputStream(); ObjectOutputStream oos=null; try { oos = new ObjectOutputStream(os); oos.writeObject(o); } catch(Throwable t){} finally { Util.closeEL(oos); } return os.toByteArray().length; } public static class Size { public int count; public int size; public Size(int count, int size) { this.count=count; this.size=size; } } } class Arch { private static final Arch ARCH_32_BITS=new Arch(32, 4); private static final Arch ARCH_64_BITS=new Arch(64, 8); private static final Arch ARCH_UNKNOWN=new Arch(32, 4); private int bits; private int wordSize; private Arch(int bits, int wordSize) { this.bits = bits; this.wordSize = wordSize; } public int getBits() { return bits; } public int getWordSize() { return wordSize; } public static Arch getVMArchitecture() { String archString = System.getProperty("sun.arch.data.model"); if (archString != null) { if (archString.equals("32")) { return ARCH_32_BITS; } else if (archString.equals("64")) { return ARCH_64_BITS; } } return ARCH_UNKNOWN; } }