1 2 9 10 package org.xmlpull.mxp1; 11 12 import org.xmlpull.v1.XmlPullParserException; 13 14 21 22 public class MXParserCachingStrings extends MXParser 23 { 24 private final static boolean CACHE_STATISTICS = false; 25 private final static boolean TRACE_SIZING = false; 26 private int cacheStatCalls; 27 private int cacheStatWalks; 28 private int cacheStatResets; 29 private int cacheStatRehash; 30 31 32 private static final int CACHE_LOAD = 77; private int cacheEntriesCount; 34 private int cacheEntriesThreshold; 35 36 private char[][] keys; 38 private String [] values; 39 40 private boolean global; 41 private static int globalCacheEntriesCount; 45 private static int globalCacheEntriesThreshold; 46 private static char[][] globalKeys; 47 private static String [] globalValues; 48 49 50 public MXParserCachingStrings() { 51 super(); 52 allStringsInterned = true; 53 } 54 55 58 public void setFeature(String name, 59 boolean state) throws XmlPullParserException 60 { 61 if(FEATURE_NAMES_INTERNED.equals(name)) { 62 if(eventType != START_DOCUMENT) throw new XmlPullParserException( 63 "interning names feature can only be changed before parsing", this, null); 64 allStringsInterned = state; 65 if(state == false) { 66 if(keys != null) resetStringCache(); 67 } 68 } else { 69 super.setFeature(name, state); 70 } 71 } 72 73 public boolean getFeature(String name) 74 { 75 if(FEATURE_NAMES_INTERNED.equals(name)) { 76 return allStringsInterned; 77 } else { 78 return super.getFeature(name); 79 } 80 } 81 82 83 86 public void finalize() { 87 if(CACHE_STATISTICS) { 88 if( cacheStatCalls > 0) { 89 System.out.println("statistics: average walk:"+cacheStatWalks+"/"+cacheStatCalls+ 90 " ("+((double)cacheStatWalks)/cacheStatCalls+")"+ 91 " resets="+cacheStatResets+" rehash="+cacheStatRehash); 92 } else { 93 System.out.println("statistics: cache was not used"); 94 } 95 } 96 } 97 98 102 protected String newString(char[] cbuf, int off, int len) { 103 if(allStringsInterned) { 104 return newStringIntern(cbuf, off, len); 105 } else { 106 return super.newString(cbuf, off, len); 107 } 108 } 109 110 114 protected String newStringIntern(char[] cbuf, int off, int len) { 115 if(CACHE_STATISTICS) ++cacheStatCalls; 117 if (cacheEntriesCount >= cacheEntriesThreshold) { 118 rehash(); 119 } 120 int offset = fastHash(cbuf, off, len) % keys.length; 121 char[] k = null; 122 while( (k = keys[offset]) != null 123 && !keysAreEqual(k, 0, k.length, 124 cbuf, off, len)) 125 { 126 offset = (offset + 1) % keys.length; 127 if(CACHE_STATISTICS) ++cacheStatWalks; 128 } 129 if (k != null) { 130 return values[offset]; 131 } else { 132 k = new char[len]; 133 System.arraycopy(cbuf, off, k, 0, len); 134 String v = new String (k).intern(); 135 keys[offset] = k; 136 values[offset] = v; 137 ++cacheEntriesCount; 138 return v; 139 } 140 141 } 142 143 protected void initStringCache() { 144 if(keys == null) { 145 int initialCapacity = 13; 146 if(initialCapacity < 0) { 147 throw new IllegalArgumentException ("Illegal initial capacity: " + initialCapacity); 148 } 149 if(CACHE_LOAD < 0 || CACHE_LOAD > 99) { 150 throw new IllegalArgumentException ("Illegal load factor: " + CACHE_LOAD); 151 } 152 cacheEntriesThreshold = (int)((initialCapacity * CACHE_LOAD)/100); 153 if(cacheEntriesThreshold >= initialCapacity) throw new RuntimeException ( 154 "internal error: threshold must be less than capacity: "+initialCapacity); 155 keys = new char[initialCapacity][]; 156 values = new String [initialCapacity]; 157 cacheEntriesCount = 0; 158 } 159 } 160 161 protected void resetStringCache() { 162 if(CACHE_STATISTICS) ++cacheStatResets; 164 initStringCache(); 165 } 166 167 private void rehash() { 168 if(CACHE_STATISTICS) ++cacheStatRehash; 169 int newSize = 2 * keys.length + 1; 170 cacheEntriesThreshold = (int)((newSize * CACHE_LOAD)/100); 171 if(cacheEntriesThreshold >= newSize) throw new RuntimeException ( 172 "internal error: threshold must be less than capacity: "+newSize); 173 if(TRACE_SIZING) System.err.println("resized "+keys.length+" => "+newSize); 174 char[][] newKeys = new char[newSize][]; 175 String [] newValues = new String [newSize]; 176 for(int i = 0; i < keys.length; i++) { 177 char[] k = keys[i]; 178 keys[i] = null; 179 String v = values[i]; 180 values[i] = null; 181 if(k != null) { 182 int newOffset = fastHash(k, 0, k.length) % newSize; 183 char[] newk = null; 184 while((newk = newKeys[newOffset]) != null) { 185 if(keysAreEqual(newk, 0, newk.length, 186 k, 0, k.length)) { 187 throw new RuntimeException ("internal cache error: duplicated keys: "+ 188 new String (newk)+" and "+new String (k)); 189 } 190 newOffset = (newOffset + 1) % newSize; 191 } 192 193 newKeys[newOffset] = k; 194 newValues[newOffset] = v; 195 } 196 } 197 keys = newKeys; 198 values = newValues; 199 } 200 201 private static final boolean keysAreEqual (char[] a, int astart, int alength, 202 char[] b, int bstart, int blength) { 203 if(alength != blength) { 204 return false; 205 } else { 206 for(int i = 0; i < alength; i++) { 207 if(a[astart + i] != b[bstart + i]) { 208 return false; 209 } 210 } 211 return true; 212 } 213 } 214 215 } 216 217 218 267 268 | Popular Tags |