001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.commons.lang; 020 021import java.io.ByteArrayOutputStream; 022import java.io.ObjectOutputStream; 023import java.lang.reflect.Method; 024import java.util.HashSet; 025import java.util.Iterator; 026import java.util.List; 027import java.util.Map; 028import java.util.Set; 029 030import lucee.loader.engine.CFMLEngineFactory; 031import lucee.loader.util.Util; 032import lucee.runtime.exp.PageException; 033import lucee.runtime.type.Collection; 034import lucee.runtime.type.Query; 035import lucee.runtime.util.Creation; 036 037public class SizeAndCount { 038 039 private static final int OBJECT_GRANULARITY_IN_BYTES = 8; 040 private static final int WORD_SIZE = Arch.getVMArchitecture().getWordSize(); 041 private static final int HEADER_SIZE = 2 * WORD_SIZE; 042 043 private static final int DOUBLE_SIZE = 8; 044 private static final int FLOAT_SIZE = 4; 045 private static final int LONG_SIZE = 8; 046 private static final int INT_SIZE = 4; 047 private static final int SHORT_SIZE = 2; 048 private static final int BYTE_SIZE = 1; 049 private static final int BOOLEAN_SIZE = 1; 050 private static final int CHAR_SIZE = 2; 051 private static final int REF_SIZE = WORD_SIZE; 052 053 public static Size sizeOf(Object obj) throws PageException { 054 Creation creator = CFMLEngineFactory.getInstance().getCreationUtil(); 055 Size size = new Size(0,0); 056 sizeOf(creator,size, obj, new HashSet<Object>()); 057 return size; 058 } 059 060 private static void sizeOf(Creation creator,Size size,Object obj, Set<Object> parents) throws PageException { 061 if(obj==null) return; 062 Object raw=obj; 063 064 // TODO this is just a patch solution, find a better way to handle this kind of situation (Wrapper classes) 065 if(isInstaneOf(obj.getClass(),"lucee.runtime.text.xml.struct.XMLStruct")) { 066 try { 067 Method toNode = raw.getClass().getMethod("toNode", new Class[0]); 068 raw=toNode.invoke(obj, new Object[0]); 069 } 070 catch (Throwable e) { 071 ExceptionUtil.rethrowIfNecessary(e); 072 e.printStackTrace(); 073 } 074 } 075 076 077 if(parents.contains(raw)) return; 078 parents.add(raw); 079 try { 080 if(obj instanceof Collection) { 081 if(obj instanceof Query) 082 sizeOf(creator,size,(Query)obj,parents); 083 else 084 sizeOf(creator,size,((Collection)obj).valueIterator(),parents); 085 return; 086 } 087 // Map 088 else if(obj instanceof Map) { 089 sizeOf(creator,size,((Map)obj).values().iterator(),parents); 090 return; 091 } 092 // List 093 else if(obj instanceof List) { 094 sizeOf(creator,size,((List)obj).iterator(),parents); 095 return; 096 } 097 098 // String 099 else if(obj instanceof String){ 100 size.size+= (CHAR_SIZE*((String)obj).length())+REF_SIZE; 101 } 102 // Number 103 else if(obj instanceof Number){ 104 if(obj instanceof Double) size.size+= DOUBLE_SIZE+REF_SIZE; 105 else if(obj instanceof Float) size.size+= FLOAT_SIZE+REF_SIZE; 106 else if(obj instanceof Long) size.size+= LONG_SIZE+REF_SIZE; 107 else if(obj instanceof Integer) size.size+= INT_SIZE+REF_SIZE; 108 else if(obj instanceof Short) size.size+= SHORT_SIZE+REF_SIZE; 109 else if(obj instanceof Byte) size.size+= BYTE_SIZE+REF_SIZE; 110 } 111 112 else if(obj instanceof Boolean) size.size+= REF_SIZE+BOOLEAN_SIZE; 113 else if(obj instanceof Character) size.size+= REF_SIZE+CHAR_SIZE; 114 115 else size.size+=_sizeOf(obj); 116 117 size.count++; 118 } 119 finally { 120 //parents.remove(raw);// TODO should we not remove, to see if sister is me. 121 } 122 } 123 124 private static void sizeOf(Creation creator,Size size,Iterator it, Set<Object> parents) throws PageException { 125 size.count++; 126 size.size+=REF_SIZE; 127 while(it.hasNext()){ 128 sizeOf(creator,size,it.next(),parents); 129 } 130 } 131 132 private static void sizeOf(Creation creator,Size size,Query qry, Set<Object> parents) throws PageException { 133 size.count++; 134 size.size+=REF_SIZE; 135 int rows=qry.getRecordcount(); 136 String[] strColumns = qry.getColumns(); 137 Collection.Key[] columns = new Collection.Key[strColumns.length]; 138 for(int col=0;col<columns.length;col++){ 139 columns[col]=creator.createKey(strColumns[col]); 140 } 141 142 143 for(int row=1;row<=rows;row++){ 144 for(int col=0;col<columns.length;col++){ 145 sizeOf(creator,size,qry.getAt(columns[col], row),parents); 146 } 147 } 148 } 149 150 public static boolean isInstaneOf(Class src ,String className) { 151 if(src==null) return false; 152 if(className.equals(src.getName()))return true; 153 154 // interfaces 155 Class[] interfaces = src.getInterfaces(); 156 for(int i=0;i<interfaces.length;i++){ 157 if(isInstaneOf(interfaces[i], className)) return true; 158 } 159 return isInstaneOf(src.getSuperclass(), className); 160 } 161 162 public static int _sizeOf(Object o) { 163 //System.err.println(o.getClass().getName()); 164 ByteArrayOutputStream os = new ByteArrayOutputStream(); 165 ObjectOutputStream oos=null; 166 try { 167 oos = new ObjectOutputStream(os); 168 oos.writeObject(o); 169 } 170 catch(Throwable t){ 171 ExceptionUtil.rethrowIfNecessary(t); 172 } 173 finally { 174 Util.closeEL(oos); 175 } 176 return os.toByteArray().length; 177 } 178 179 180 public static class Size { 181 182 public int count; 183 public int size; 184 185 public Size(int count, int size) { 186 this.count=count; 187 this.size=size; 188 } 189 190 } 191 192} 193 194 195class Arch { 196 197 private static final Arch ARCH_32_BITS=new Arch(32, 4); 198 private static final Arch ARCH_64_BITS=new Arch(64, 8); 199 private static final Arch ARCH_UNKNOWN=new Arch(32, 4); 200 201 private int bits; 202 private int wordSize; 203 204 private Arch(int bits, int wordSize) { 205 this.bits = bits; 206 this.wordSize = wordSize; 207 } 208 209 public int getBits() { 210 return bits; 211 } 212 213 public int getWordSize() { 214 return wordSize; 215 } 216 217 public static Arch getVMArchitecture() { 218 String archString = System.getProperty("sun.arch.data.model"); 219 if (archString != null) { 220 if (archString.equals("32")) { 221 return ARCH_32_BITS; 222 } else if (archString.equals("64")) { 223 return ARCH_64_BITS; 224 } 225 } 226 return ARCH_UNKNOWN; 227 } 228 229}