1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18/** 19* @author Alexander Y. Kleymenov 20* @version $Revision$ 21*/ 22 23package org.apache.harmony.security.provider.cert; 24 25import java.util.Arrays; 26 27/** 28 * The caching mechanism designed to speed up the process 29 * of Certificates/CRLs generation in the case of their repeated 30 * generation. 31 * 32 * It keeps correspondences between Objects (Certificates or CLRs) 33 * and arrays of bytes on the base of which the Objects have been generated, 34 * and provides the means to determine whether it contains the object built on 35 * the base of particular encoded form or not. If there are such 36 * objects they are returned from the cache, if not - newly generated 37 * objects can be saved in the cache.<br> 38 * 39 * The process of Certificate/CRL generation 40 * (implemented in <code>X509CertFactoryImpl</code>) is accompanied with 41 * prereading of the beginning of encoded form. This prefix is used to determine 42 * whether provided form is PEM encoding or not.<br> 43 * 44 * So the use of the prefix is the first point to (approximately) 45 * determine whether object to be generated is in the cache or not. 46 * 47 * The failure of the predetermination process tells us that there were not 48 * object generated from the encoded form with such prefix and we should 49 * generate (decode) the object. If predetermination is successful, 50 * we conduct the accurate search on the base of whole encoded form. <br> 51 * 52 * So to speed up the object generation process this caching mechanism provides 53 * the following functionality:<br> 54 * 55 * 1. With having of the beginning of the encoded form (prefix) 56 * it is possible to predetermine whether object has already been 57 * generated on the base of the encoding with the SIMILAR prefix or not. 58 * This process is not computationally expensive and takes a little time. 59 * But it prevents us from use of expensive full encoding 60 * search in the case of its failure.<br> 61 * 62 * 2. If predetermination ends with success, the whole encoding 63 * form should be provided to make the final answer: whether object has 64 * already been generated on the base of this PARTICULAR encoded form or not. 65 * If it is so - the cached object is returned from the cache, 66 * if not - new object should be generated and saved in the cache.<br> 67 * 68 * Note: The length of the prefixes of the encoded forms should not be 69 * less than correspondence (default value is 28). 70 */ 71public class Cache { 72 73 // Hash code consist of 6 bytes: AABB00 74 // where: 75 // AA - 2 bytes for prefix hash 76 // value generated on the base of the prefix of encoding 77 // BB - 2 bytes for tail hash 78 // value generated on the base of the tail of encoding 79 // 00 - 2 reserved bytes equals to 0 80 // 81 // Note, that it is possible for 2 different arrays to have 82 // the similar hash codes. 83 84 // The masks to work with hash codes: 85 // the hash code without the reserved bytes 86 private static final long HASH_MASK = 0xFFFFFFFFFFFF0000L; 87 // the hash code of the prefix 88 private static final long PREFIX_HASH_MASK = 0xFFFFFFFF00000000L; 89 // the index value contained in reserved bytes 90 private static final int INDEX_MASK = 0x00FFFF; 91 92 // size of the cache 93 private final int cache_size; 94 // the number of bytes which will be used for array hash generation. 95 private final int prefix_size; 96 97 // The following 3 arrays contain the information about cached objects. 98 // This information includes: hash of the array, encoded form of the object, 99 // and the object itself. 100 // The hash-encoding-object correspondence is made by means of index 101 // in the particular array. I.e. for index N hash contained in hashes[N] 102 // corresponds to the encoding contained in encodings[N] which corresponds 103 // to the object cached at cache[N] 104 105 // array containing the hash codes of encodings 106 private final long[] hashes; 107 // array containing the encodings of the cached objects 108 private final byte[][] encodings; 109 // array containing the cached objects 110 private final Object[] cache; 111 112 // This array is used to speed up the process of the search in the cache. 113 // This is an ordered array of the hash codes from 'hashes' array (described 114 // above) with last 2 (reserved) bytes equals to the index of 115 // the hash in the 'hashes' array. I.e. hash code ABCD00 with index 10 in 116 // the hashes array will be represented in this array as ABCD0A (10==0x0A) 117 // So this array contains ordered <hash to index> correspondences. 118 // Note, that every item in this array is unique. 119 private final long[] hashes_idx; 120 121 // the index of the last cached object 122 private int last_cached = 0; 123 // cache population indicator 124 private boolean cache_is_full = false; 125 126 /** 127 * Creates the Cache object. 128 * @param pref_size specifies how many leading/trailing bytes of object's 129 * encoded form will be used for hash computation 130 * @param size capacity of the cache to be created. 131 */ 132 public Cache(int pref_size, int size) { 133 cache_size = size; 134 prefix_size = pref_size; 135 hashes = new long[cache_size]; 136 hashes_idx = new long[cache_size]; 137 encodings = new byte[cache_size][]; 138 cache = new Object[cache_size]; 139 } 140 141 // BEGIN android-removed 142 // /** 143 // * Creates the Cache object of size of 900. 144 // * @param pref_size specifies how many leading/trailing bytes of object's 145 // * encoded form will be used for hash computation 146 // */ 147 // public Cache(int pref_size) { 148 // this(pref_size, 900); 149 // } 150 // 151 // /** 152 // * Creates the Cache object of size of 900. 153 // */ 154 // public Cache() { 155 // this(28, 900); 156 // } 157 // END android-removed 158 159 // BEGIN android-added 160 /** 161 * Creates the Cache object of size of 9. 162 * @param pref_size specifies how many leading/trailing bytes of object's 163 * encoded form will be used for hash computation 164 */ 165 public Cache(int pref_size) { 166 this(pref_size, 9); 167 } 168 169 /** 170 * Creates the Cache object of size of 9. 171 */ 172 public Cache() { 173 this(28, 9); 174 } 175 // END android-added 176 177 /** 178 * Returns the hash code for the array. This code is used to 179 * predetermine whether the object was built on the base of the 180 * similar encoding or not (by means of <code>contains(long)</code> method), 181 * to exactly determine whether object is contained in the cache or not, 182 * and to put the object in the cache. 183 * Note: parameter array should be of length not less than 184 * specified by <code>prefix_size</code> (default 28) 185 * @param arr the byte array containing at least prefix_size leading bytes 186 * of the encoding. 187 * @return hash code for specified encoding prefix 188 */ 189 public long getHash(byte[] arr) { 190 long hash = 0; 191 for (int i=1; i<prefix_size; i++) { 192 hash += (arr[i] & 0xFF); 193 } // it takes about 2 bytes for prefix_size == 28 194 195 // shift to the correct place 196 hash = hash << 32; 197 return hash; 198 } 199 200 /** 201 * Checks if there are any object in the cache generated 202 * on the base of encoding with prefix corresponding 203 * to the specified hash code. 204 * @param prefix_hash the hash code for the prefix 205 * of the encoding (retrieved by method <code>getHash(byte[]))</code> 206 * @return false if there were not any object generated 207 * on the base of encoding with specified hash code, true 208 * otherwise. 209 */ 210 public boolean contains(long prefix_hash) { 211 int idx = -1*Arrays.binarySearch(hashes_idx, prefix_hash)-1; 212 if (idx == cache_size) { 213 return false; 214 } else { 215 return (hashes_idx[idx] & PREFIX_HASH_MASK) == prefix_hash; 216 } 217 } 218 219 /** 220 * Returns the object built on the base on the specified encoded 221 * form if it is contained in the cache and null otherwise. 222 * This method is computationally expensive and should be called only if 223 * the method <code>contains(long)</code> for the hash code returned true. 224 * @param hash the hash code for the prefix of the encoding 225 * (retrieved by method <code>getHash(byte[])</code>) 226 * @param encoding encoded form of the required object. 227 * @return the object corresponding to specified encoding or null if 228 * there is no such correspondence. 229 */ 230 public Object get(long hash, byte[] encoding) { 231 hash |= getSuffHash(encoding); 232 int idx = -1*Arrays.binarySearch(hashes_idx, hash)-1; 233 if (idx == cache_size) { 234 return null; 235 } 236 while ((hashes_idx[idx] & HASH_MASK) == hash) { 237 int i = (int) (hashes_idx[idx] & INDEX_MASK) - 1; 238 if (Arrays.equals(encoding, encodings[i])) { 239 return cache[i]; 240 } 241 idx++; 242 if (idx == cache_size) { 243 return null; 244 } 245 } 246 return null; 247 } 248 249 /** 250 * Puts the object into the cache. 251 * @param hash hash code for the prefix of the encoding 252 * @param encoding the encoded form of the object 253 * @param object the object to be saved in the cache 254 */ 255 public void put(long hash, byte[] encoding, Object object) { 256 // check for empty space in the cache 257 if (last_cached == cache_size) { 258 // so cache is full, will erase the first entry in the 259 // cache (oldest entry). it could be better to throw out 260 // rarely used value instead of oldest one.. 261 last_cached = 0; 262 cache_is_full = true; 263 } 264 // index pointing to the item of the table to be overwritten 265 int index = last_cached++; 266 267 // improve the hash value with info from the tail of encoding 268 hash |= getSuffHash(encoding); 269 270 if (cache_is_full) { 271 // indexing hash value to be overwritten: 272 long idx_hash = (hashes[index] | (index+1)); 273 int idx = Arrays.binarySearch(hashes_idx, idx_hash); 274 if (idx < 0) { 275 // it will never happen because we use saved hash value 276 // (hashes[index]) 277 System.out.println("WARNING! "+idx); //$NON-NLS-1$ 278 idx = -(idx + 1); 279 } 280 long new_hash_idx = (hash | (index + 1)); 281 int new_idx = Arrays.binarySearch(hashes_idx, new_hash_idx); 282 if (new_idx >= 0) { 283 // it's possible when we write the same hash in the same cell 284 if (idx != new_idx) { 285 // it will never happen because we use the same 286 // hash and the same index in hash table 287 System.out.println("WARNING: "); //$NON-NLS-1$ 288 System.out.println(">> idx: "+idx+" new_idx: "+new_idx); //$NON-NLS-1$ //$NON-NLS-2$ 289 } 290 } else { 291 new_idx = -(new_idx + 1); 292 // replace in sorted array 293 if (new_idx > idx) { 294 System.arraycopy(hashes_idx, idx+1, hashes_idx, idx, 295 new_idx - idx - 1); 296 hashes_idx[new_idx-1] = new_hash_idx; 297 } else if (idx > new_idx) { 298 System.arraycopy(hashes_idx, new_idx, hashes_idx, new_idx+1, 299 idx - new_idx); 300 hashes_idx[new_idx] = new_hash_idx; 301 } else { // idx == new_idx 302 hashes_idx[new_idx] = new_hash_idx; 303 } 304 } 305 } else { 306 long idx_hash = (hash | (index + 1)); 307 int idx = Arrays.binarySearch(hashes_idx, idx_hash); 308 if (idx < 0) { 309 // it will always be true because idx_hash depends on index 310 idx = -(idx + 1); 311 } 312 idx = idx - 1; 313 if (idx != cache_size - index - 1) { 314 // if not in the cell containing 0 (free cell), do copy: 315 System.arraycopy(hashes_idx, cache_size - index, 316 hashes_idx, cache_size - index - 1, 317 idx - (cache_size - index) + 1); 318 } 319 hashes_idx[idx] = idx_hash; 320 } 321 // overwrite the values in the tables: 322 hashes[index] = hash; 323 encodings[index] = encoding; 324 cache[index] = object; 325 } 326 327 // Returns the hash code built on the base of the tail of the encoded form 328 // @param arr - the array containing at least prefix_size trailing bytes 329 // of encoded form 330 private long getSuffHash(byte[] arr) { 331 long hash_addon = 0; 332 for (int i=arr.length-1; i>arr.length - prefix_size; i--) { 333 hash_addon += (arr[i] & 0xFF); 334 } 335 return hash_addon << 16; 336 } 337 338} 339 340