All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codehaus.cake.cache.policy.spi.AbstractHeapReplacementPolicy Maven / Gradle / Ivy

/*
 * Copyright 2008 Kasper Nielsen.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://cake.codehaus.org/LICENSE
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package org.codehaus.cake.cache.policy.spi;

import java.util.Comparator;

import org.codehaus.cake.attribute.IntAttribute;
import org.codehaus.cake.cache.CacheEntry;
import org.codehaus.cake.internal.util.ArrayUtils;

// TODO shouldn't implement Comparator, much better just to add a method that
// should be overriden
public abstract class AbstractHeapReplacementPolicy extends
        AbstractReplacementPolicy implements Comparator> {

    /**
     * Priority queue represented as a balanced binary heap: the two children of
     * queue[n] are queue[2*n+1] and queue[2*(n+1)]. The priority queue is
     * ordered by comparator For each node n in the heap and each descendant d
     * of n, n <= d. The element with the lowest value is in queue[0], assuming
     * the queue is nonempty.
     */
    CacheEntry[] queue = new CacheEntry[0];
    private final Comparator> comparator = this;
    /**
     * The number of elements in the priority queue.
     */
    private int size = 0;

    private final IntAttribute index = new IntAttribute("index", 0) {
    };

    public AbstractHeapReplacementPolicy() {
        attachToEntry(index);
    }

    private int indexOf(CacheEntry entry) {
        return index.get(entry);
    }

    private void setIndexOf(CacheEntry entry, int index) {
        entry.getAttributes().put(this.index, index);
    }
    /** {@inheritDoc} */
    public boolean add(CacheEntry entry) {
        int i = size;
        if (i >= queue.length)
            grow(i + 1);
        size = i + 1;
        if (i == 0) {
            queue[0] = entry;
            setIndexOf(entry, 0);
        } else {
            siftUp(i, entry);
        }
        return true;
    }
    /** {@inheritDoc} */
    public void clear() {
        for (int i = 0; i < size; i++) {
            queue[i] = null;
        }
        size = 0;
    }

    protected CacheEntry peek() {
        if (size == 0)
            return null;
        return (CacheEntry) queue[0];
    }

    /** {@inheritDoc} */
    public CacheEntry evictNext() {
        if (size == 0)
            return null;
        int s = --size;
        CacheEntry result = (CacheEntry) queue[0];
        CacheEntry x = (CacheEntry) queue[s];
        queue[s] = null;
        if (s != 0)
            siftDown(0, x);
        return result;
    }
    /** {@inheritDoc} */
    public void remove(CacheEntry entry) {
        removeAt(indexOf(entry));
    }

    /**
     * Removes the ith element from queue.
     * 
     * Normally this method leaves the elements at up to i-1, inclusive,
     * untouched. Under these circumstances, it returns null. Occasionally, in
     * order to maintain the heap invariant, it must swap a later element of the
     * list with one earlier than i. Under these circumstances, this method
     * returns the element that was previously at the end of the list and is now
     * at some position before i. This fact is used by iterator.remove so as to
     * avoid missing traversing elements.
     */
    private CacheEntry removeAt(int i) {
        // assert i >= 0 && i < size;
        int s = --size;
        if (s == i) // removed last element
            queue[i] = null;
        else {
            CacheEntry moved = queue[s];
            queue[s] = null;
            siftDown(i, moved);
            if (queue[i] == moved) {
                siftUp(i, moved);
                if (queue[i] != moved)
                    return moved;
            }
        }
        return null;
    }
    /** {@inheritDoc} */
    public CacheEntry replace(CacheEntry previous,
            CacheEntry newEntry) {
        int i = comparator.compare(previous, newEntry);
        int index = indexOf(previous);
        setIndexOf(newEntry, index);
        queue[index] = newEntry;
        if (i > 0) {
            siftUp(index, newEntry);
        } else if (i < 0) {
            siftDown(index, newEntry);
        }
        return newEntry;
    }

    protected abstract int compareEntry(CacheEntry o1, CacheEntry o2);

    /**
     * Inserts item x at position k, maintaining heap invariant by promoting x
     * up the tree until it is greater than or equal to its parent, or is the
     * root.
     * 
     * To simplify and speed up coercions and comparisons. the Comparable and
     * Comparator versions are separated into different methods that are
     * otherwise identical. (Similarly for siftDown.)
     * 
     * @param k
     *            the position to fill
     * @param x
     *            the item to insert
     */
    private void siftUp(int k, CacheEntry x) {
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            CacheEntry e = queue[parent];
            if (comparator.compare(x, (CacheEntry) e) >= 0)
                break;
            queue[k] = e;
            setIndexOf(e, k);
            k = parent;
        }
        queue[k] = x;
        setIndexOf(x, k);
    }

    protected void siftDown(CacheEntry x) {
        siftDown(indexOf(x), x);
    }

    protected void siftUp(CacheEntry x) {
        siftUp(indexOf(x), x);
    }

    /**
     * Inserts item x at position k, maintaining heap invariant by demoting x
     * down the tree repeatedly until it is less than or equal to its children
     * or is a leaf.
     * 
     * @param k
     *            the position to fill
     * @param x
     *            the item to insert
     */
    private void siftDown(int k, CacheEntry x) {
        int half = size >>> 1;
        while (k < half) {
            int child = (k << 1) + 1;
            CacheEntry c = queue[child];
            int right = child + 1;
            if (right < size
                    && comparator.compare((CacheEntry) c,
                            (CacheEntry) queue[right]) > 0) {
                c = queue[child = right];
            }
            if (comparator.compare(x, (CacheEntry) c) <= 0) {
                break;
            }
            queue[k] = c;
            setIndexOf(c, k);
            k = child;
        }
        queue[k] = x;
        setIndexOf(x, k);
    }

    /**
     * Increases the capacity of the array.
     * 
     * @param minCapacity
     *            the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // / CLOVER:OFF
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        // / CLOVER:ON
        int oldCapacity = queue.length;
        // Double size if small; else grow by 50%
        int newCapacity = ((oldCapacity < 64) ? ((oldCapacity + 1) * 2)
                : ((oldCapacity / 2) * 3));
        // / CLOVER:OFF
        if (newCapacity < 0) // overflow
            newCapacity = Integer.MAX_VALUE;
        if (newCapacity < minCapacity)
            newCapacity = minCapacity;
        // / CLOVER:ON
        queue = ArrayUtils.copyOf(queue, newCapacity);
    }

    public final int compare(CacheEntry o1, CacheEntry o2) {
        return compareEntry(o1, o2);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy