// // CachingCoordinateSystem.java // /* VisAD system for interactive analysis and visualization of numerical data. Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and Tommy Jasmin. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ package visad.data; import java.util.Arrays; import java.util.Enumeration; import java.util.Hashtable; import visad.util.Util; /** * This class is used by the CachingCoordinateSystem to do the actual caching mapping one array to another one * @version $Revision: 1.5 $ $Date: 2010-01-05 21:02:43 $ */ public class ArrayCache { /** Do we cache */ private boolean enabled = Boolean.parseBoolean(System.getProperty("visad.data.arraycache.enabled", "true")); /** lower size threshold */ private int lowerThreshold = Integer.parseInt(System.getProperty("visad.data.arraycache.lowerthreshold", "1000")); /** upper size threshold */ private int upperThreshold = Integer.parseInt(System.getProperty("visad.data.arraycache.upperthreshold", "1000000")); private boolean useDataCacheManager = Boolean.parseBoolean(System.getProperty("visad.data.arraycache.usedatacachemanager", "false")); private Hashtable<String,Integer> misses = new Hashtable<String,Integer>(); /** * ctor */ public ArrayCache() {} /** * ctor * * @param enabled If false then never cache */ public ArrayCache(boolean enabled) { this.enabled = enabled; } private String getKey(String key, int size) { return key +"_" + size; } /** * Get the converted value for the specified key and input pairs * * @param key The key (e.g., "toReference", "fromReference") * @param input The input * * @return value for supplied key */ public FloatResult get(String key, float[][] input) { if (!shouldHandle(input)) { return new FloatResult(false); } return getInner(key, input); } private synchronized FloatResult getInner(String key, float[][] input) { key = getKey(key, input[0].length); float[][][] pair = getFloatValue(key); if (pair == null) { return handleCacheMiss(key, input); } float[][] lastInput = pair[0]; float[][] lastOutput = pair[1]; if (lastInput.length != input.length) return null; for (int i = 0; i < input.length; i++) { if (!Arrays.equals(input[i], lastInput[i])) { return handleCacheMiss(key, input); } } misses.remove(key); //?? should we clone the output return new FloatResult(Util.clone(lastOutput)); } /** * Get the converted value for the specified key and input pairs * * @param key The key (e.g., "toReference", "fromReference") * @param input The input * * @return value for the supplied key */ public DoubleResult get(String key, double[][] input) { if (!shouldHandle(input)) { return new DoubleResult(false); } return getInner(key, input); } private synchronized DoubleResult getInner(String key, double[][] input) { key = getKey(key, input[0].length); double[][][] pair = getDoubleValue(key); if (pair == null) { return handleCacheMiss(key, input); } double[][] lastInput = pair[0]; double[][] lastOutput = pair[1]; if (lastInput.length != input.length) return null; for (int i = 0; i < input.length; i++) { if (!Arrays.equals(input[i], lastInput[i])) { return handleCacheMiss(key, input); } } misses.remove(key); return new DoubleResult(Util.clone(lastOutput)); } private DoubleResult handleCacheMiss(String key, double[][]input) { Integer numMisses = misses.get(key); if(numMisses==null) { misses.put(key, numMisses = new Integer(1)); } else { misses.put(key, new Integer(numMisses.intValue()+1)); } if(numMisses.intValue()>3) { removeValue(key); } return new DoubleResult(numMisses.intValue()<=1); } private FloatResult handleCacheMiss(String key, float[][]input) { Integer numMisses = misses.get(key); if(numMisses==null) { misses.put(key, numMisses = new Integer(1)); } else { misses.put(key, new Integer(numMisses.intValue()+1)); } if(numMisses.intValue()>3) { removeValue(key); } return new FloatResult(numMisses.intValue()<=1); } private boolean shouldHandle(double[][]input) { if(input == null) return false; if(input.length==0) return false; if(input[0]==null) return false; if(!enabled) return false; if (input[0].length <= lowerThreshold) return false; if (input[0].length > upperThreshold) return false; return true; } private boolean shouldHandle(float[][]input) { if(input == null) return false; if(input.length==0) return false; if(input[0]==null) return false; if(!enabled) return false; if (input[0].length <= lowerThreshold) return false; if (input[0].length > upperThreshold) return false; return true; } /** * Put the converted value for the specified key and input pairs * * @param key The key * @param input The input array * @param results The array to store */ public void put(String key, double[][] input, DoubleResult results) { if(!shouldHandle(input)) return; putInner(key, input, results); } private synchronized void putInner(String key, double[][] input, DoubleResult results) { if(!results.shouldCache || results.values==null) return; key = getKey(key, input[0].length); storeValue(key, new double[][][] { (double[][])Util.clone(input), (double[][])Util.clone(results.values) }); } /** * Put the converted value for the specified key and input pairs * * @param key The key * @param input The input array * @param results The array to store */ public void put(String key, float[][] input, FloatResult results) { if(!shouldHandle(input)) return; putInner(key, input, results); } private synchronized void putInner(String key, float[][] input, FloatResult results) { if(!results.shouldCache || results.values==null) return; key = getKey(key, input[0].length); storeValue(key, new float[][][] { (float[][])Util.clone(input), (float[][])Util.clone(results.values) }); } /** holds float arrays or DataCacheManager ids */ private Hashtable<Object, Object> map = new Hashtable<Object, Object>(); private void storeValue(String key, double[][][]value) { checkCache(); Object object = value; if(useDataCacheManager) { removeValue(key); object = DataCacheManager.getCacheManager().addToCache("ArrayCache", value, true); } map.put(key, object); } private void storeValue(String key, float[][][]value) { checkCache(); Object object = value; if(useDataCacheManager) { removeValue(key); object = DataCacheManager.getCacheManager().addToCache("ArrayCache", value, true); } map.put(key, object); } public void finalize() throws Throwable { super.finalize(); if(useDataCacheManager) { // System.err.println ("arraycache finalize"); clearCache(); } } private void checkCache() { if(map.size()>4) { clearCache(); } } private void removeValue(Object key) { Object object = map.get(key); if(object!=null) { if(useDataCacheManager) DataCacheManager.getCacheManager().removeFromCache(object); map.remove(key); } } private void clearCache() { for(Enumeration keys= map.keys();keys.hasMoreElements(); ) { Object key= keys.nextElement(); Object object = map.get(key); if(useDataCacheManager) { DataCacheManager.getCacheManager().removeFromCache(object); } } map = new Hashtable<Object, Object>(); } private double[][][] getDoubleValue(String key) { Object object = map.get(key); if(object==null) { return null; } if(useDataCacheManager) return DataCacheManager.getCacheManager().getDoubleArray3D(object); return (double[][][])object; } private float[][][] getFloatValue(String key) { Object object = map.get(key); if(object==null) { return null; } if(useDataCacheManager) return DataCacheManager.getCacheManager().getFloatArray3D(object); return (float[][][])object; } public static class DoubleResult { public boolean shouldCache = true; public double[][]values; public DoubleResult() { shouldCache = false; values = null; } public DoubleResult(boolean shouldCache) { this.shouldCache = shouldCache; } public DoubleResult(double[][]values) { this.values = values; this.shouldCache = true; } public double[][]cloneForCache(double[][]a) { if(!shouldCache) return null; return Util.clone(a); } public boolean getShouldCache() { return shouldCache; } } public static class FloatResult { public boolean shouldCache = true; public float[][]values; public FloatResult() { shouldCache = false; values = null; } public FloatResult(boolean shouldCache) { this.shouldCache = shouldCache; } public FloatResult(float[][]values) { this.values = values; this.shouldCache = true; } public float[][]cloneForCache(float[][]a) { if(!shouldCache) return null; return Util.clone(a); } public boolean getShouldCache() { return shouldCache; } } }