/**************************************************************************
* Copyright (c) 2001, 2002, 2003 by Punch Telematix. All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* 1. Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* 2. Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* 3. Neither the name of Punch Telematix nor the names of *
* other contributors may be used to endorse or promote products *
* derived from this software without specific prior written permission.*
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. *
* IN NO EVENT SHALL PUNCH TELEMATIX OR OTHER CONTRIBUTORS BE LIABLE *
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF *
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR *
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, *
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE *
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN *
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
**************************************************************************/
package com.acunia.wonka.rudolph;
import java.lang.ref.*;
public class ImageCache {
protected int[] keys;
protected SoftReference[] values;
private float loadFactor;
private int threshold;
private transient int capacity;
private transient int occupancy;
private final static int DEFAULT_CAPACITY = 101;
private transient int modCount = 0;
public ImageCache(int initialCapacity, float loadFactor) throws IllegalArgumentException {
if (initialCapacity < 0 || loadFactor <= 0.0f) throw new IllegalArgumentException();
if (loadFactor > 1.0f) loadFactor = (float)0.75;
this.capacity = (initialCapacity < 5 ? 5 :initialCapacity);
this.keys = new int[capacity];
this.values = new SoftReference[capacity];
this.loadFactor = loadFactor;
this.threshold = (int)(capacity*loadFactor);
this.occupancy = 0;
}
public ImageCache(int initialCapacity) throws IllegalArgumentException {
this(initialCapacity,(float)0.75);
}
public ImageCache() {
this(DEFAULT_CAPACITY,(float)0.75);
}
private void resize(int newsize) {
int oldsize = this.capacity;
int[] oldkeys = this.keys;
SoftReference[] oldvalues = this.values;
int[] newkeys = new int[newsize];
SoftReference[] newvalues = new SoftReference[newsize];
this.capacity = newsize;
this.threshold = (int)(capacity*loadFactor);
this.occupancy = 0;
this.keys = newkeys;
this.values = newvalues;
int oldindex;//, newindex;
for (oldindex=0;oldindex<oldsize;++oldindex) {
int key = oldkeys[oldindex];
if (key != 0)
this.putref(key,oldvalues[oldindex]);
}
}
protected int firstBusySlot(int i) {
int j;
if (i<0 || i>=capacity || this.keys == null) {
return -1;
}
for(j=i;j<capacity;++j)
if (this.keys[j] != 0) {
return j;
}
return -1;
}
private int probe(int hashcode, int sequence) {
int rehash = (hashcode-sequence) % this.capacity;
if (rehash<0) rehash = rehash + this.capacity;
return rehash;
}
private void deleteSlot(int slotIndex) {
int vacant, current, home, distance1, distance2;
boolean happy;
--occupancy;
current = slotIndex;
while(true) {
// R1
keys[current] = 0;
vacant = current;
// R2
happy = true;
while(happy) {
current = current==0 ? capacity-1 : current-1;
// R3
if (keys[current] == 0) {
return; // normal termination
}
home = probe(keys[current],0);
distance1 = current<=home ? home - current : capacity + home - current;
distance2 = current<=vacant ? vacant - current : capacity + vacant - current;
happy = distance1<distance2;
// back to R2
}
// R4
keys[vacant] = keys[current];
values[vacant] = values[current];
// repeat from R1 ...
}
}
public Object get(int key) {
if(key == 0) return null;
synchronized(this) {
if (this.keys == null) {
return null;
}
int i;
int j = 0;
int hash = key;
while(true) {
i = probe(hash, j);
if(this.keys[i] == 0) {
return null;
} else if(key == this.keys[i]) {
return this.values[i].get();
} else ++j;
}
}
}
private int checkSize(int direction) {
float targetsquared, current, currentsquared;
targetsquared = loadFactor * loadFactor;
if (capacity > 0) {
current = (float)occupancy / (float)capacity;
}
else {
current = 1;
}
currentsquared = current * current;
if (direction>=0 && currentsquared<loadFactor
|| direction<=0 && targetsquared>current) {
return 0;
}
int newsize = (int)((float)occupancy/loadFactor);
if (newsize<occupancy+2) newsize = occupancy+2;
if (direction>=0 && newsize<=capacity) newsize = 0;
if (direction<=0 && newsize>=capacity) newsize = 0;
return newsize;
}
public Object putref(int key, SoftReference newvalue)
throws NullPointerException {
if(key == 0 || newvalue ==null)
throw new NullPointerException();
int i;
int j = 0;
int hashcode = key;
int newsize;
synchronized(this) {
if (this.keys == null) {
this.keys = new int[DEFAULT_CAPACITY];
this.values = new SoftReference[DEFAULT_CAPACITY];
this.capacity = DEFAULT_CAPACITY;
this.threshold = (int)(capacity*loadFactor);
}
while(true) {
i = probe(hashcode, j);
if(this.keys[i] == 0) {
this.keys[i] = key;
this.values[i] = newvalue;
++this.occupancy;
newsize = this.checkSize(+1);
if(newsize!=0) resize(newsize);
return null;
} else if(key == this.keys[i]) {
SoftReference oldvalue = this.values[i];
this.values[i] = newvalue;
return oldvalue;
}
++j;
}
}
}
public Object put(int key, Object newvalue)
throws NullPointerException {
return putref(key, new SoftReference(newvalue));
}
public Object remove(int key) {
if(key == 0) return null;
int i;
int j = 0;
int hashcode = key;
int newsize;
synchronized(this) {
if (this.keys == null) {
return null;
}
while(true) {
i = probe(hashcode,j);
if(this.keys[i] == 0) {
return null;
} else {
if(key == this.keys[i]) {
Object oldvalue = this.values[i].get();
deleteSlot(i);
newsize = this.checkSize(-1);
if(newsize!=0) resize(newsize);
return oldvalue;
}
}
++j;
}
}
}
public void clear() {
int idx;
int i, j;
if(keys != null) {
for(idx=0;idx<capacity;++idx) {
if(this.keys[idx] != 0) {
this.deleteSlot(idx);
}
}
this.occupancy = 0;
}
}
}