Package org.chaidb.db.helper.cache.algorithm

Source Code of org.chaidb.db.helper.cache.algorithm.MeasurableFIFO

/*
* Copyright (C) 2006  http://www.chaidb.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
*/

package org.chaidb.db.helper.cache.algorithm;

import org.chaidb.db.helper.cache.measurable.Measurable;
import org.chaidb.db.helper.cache.measurable.SizeTooLargeException;

import java.util.Iterator;
import java.util.LinkedHashMap;

public class MeasurableFIFO implements MeasurableReplacementAlgorithm {
    public static final String ALGORITHM_NAME = "FIFO";

    static final int DEFAULT_INITIAL_CAPACITY = 16;
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    public MeasurableFIFO(long sizeBoundaryInBytes) {
        if (sizeBoundaryInBytes <= 0) {
            throw new IllegalArgumentException("cacheSize must be larger than 0");
        } else {
            this.sizeBoundaryInBytes = sizeBoundaryInBytes;
            this.map = new LinkedHashMap(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, false);
        }
    }

    public long getSizeBoundary() {
        return this.sizeBoundaryInBytes;
    }

    public long getRetainedSize() {
        return this.retainedSizeInBytes;
    }

    public boolean containsKey(Measurable key) {
        return map.containsKey(key);
    }

    public Measurable put(Measurable key, Measurable value) throws SizeTooLargeException {
        final long keySize = (key == null) ? 0 : key.getRetainedSize();
        final long valueSize = (value == null) ? 0 : value.getRetainedSize();
        final long sizeToBePlused;
        // check if specified key exists
        final boolean keyExists = map.containsKey(key);

        if (keyExists) {
            Measurable existingValue = (Measurable) map.get(key);
            final long existingValueSize = (existingValue == null) ? 0 : existingValue.getRetainedSize();
            sizeToBePlused = valueSize - existingValueSize;
        } else {
            sizeToBePlused = keySize + valueSize;
        }

        // throw exception if size of key/value pair is even larger than the max capacity
        if (keySize + valueSize > sizeBoundaryInBytes) {
            throw new SizeTooLargeException("required=" + sizeToBePlused + "; max capacity=" + sizeBoundaryInBytes);
        }
        // shortcut for key/value pair whose size equals to the max capacity
        if (keySize + valueSize == sizeBoundaryInBytes) {
            map.clear();
            retainedSizeInBytes = sizeBoundaryInBytes;
            return (Measurable) map.put(key, value);
        }

        if (sizeToBePlused + retainedSizeInBytes <= sizeBoundaryInBytes) {
            retainedSizeInBytes += sizeToBePlused;
            return (Measurable) map.put(key, value);
        } else { // have to kickout some existing key'value pairs
            final long smallestSizeToKickout = sizeToBePlused + retainedSizeInBytes - sizeBoundaryInBytes;
            long kickoutSize = 0;
            while (kickoutSize < smallestSizeToKickout) {
                // Get the eldest key.
                Measurable kickoutKey = getEldestKey();
                // If no more key available, break out.
                if (kickoutKey == NO_KEY) {
                    break;
                }
                // Remove the key/value pair.
                Measurable kickoutValue = (Measurable) map.remove(kickoutKey);
                if ((key == kickoutKey) || (key != null && key.equals(kickoutKey))) {
                    // If existing key is kicked out, don't count their size into kickout size.
                    // Because it will be added back later.
                } else {
                    // Calculate size of key/value pair which has been kicked out
                    final long kickoutKeySize = (kickoutKey == null) ? 0 : kickoutKey.getRetainedSize();
                    final long kickoutValueSize = (kickoutValue == null) ? 0 : kickoutValue.getRetainedSize();
                    kickoutSize += (kickoutKeySize + kickoutValueSize);
                }
            }
            retainedSizeInBytes += (sizeToBePlused - kickoutSize);
            return (Measurable) map.put(key, value);
        }
    }

    public Measurable get(Measurable key) {
        return (Measurable) map.get(key);
    }

    public Measurable remove(Measurable key) {
        // check if specified key exists
        final boolean keyExists = map.containsKey(key);
        if (keyExists) {
            Measurable value = (Measurable) map.remove(key);
            final long keySize = (key == null) ? 0 : key.getRetainedSize();
            final long valueSize = (value == null) ? 0 : value.getRetainedSize();
            retainedSizeInBytes -= (keySize + valueSize);
            return value;
        } else {
            return null;
        }
    }

    public void clear() {
        retainedSizeInBytes = 0;
        map.clear();
    }

    public long calculateRetainedSize() {
        java.util.HashMap copy = new java.util.HashMap(map);
        Iterator keyIterator = copy.keySet().iterator();
        long retainedSize = 0;
        while (keyIterator.hasNext()) {
            Measurable key = (Measurable) keyIterator.next();
            Measurable value = (Measurable) copy.get(key);
            final long keySize = (key == null) ? 0 : key.getRetainedSize();
            final long valueSize = (value == null) ? 0 : value.getRetainedSize();
            retainedSize += (keySize + valueSize);
        }
        return retainedSize;
    }

    protected Measurable getEldestKey() {
        Iterator keyIterator = map.keySet().iterator();
        if (keyIterator.hasNext()) {
            return (Measurable) keyIterator.next();
        } else {
            return NO_KEY;
        }
    }

    private static final Measurable NO_KEY = new Measurable() {
        public long getRetainedSize() {
            return 0L;
        }
    };

    private final long sizeBoundaryInBytes;
    private long retainedSizeInBytes = 0;
    private LinkedHashMap map;
}
TOP

Related Classes of org.chaidb.db.helper.cache.algorithm.MeasurableFIFO

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.