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}