/* * Copyright (c) [2016] [ <ether.camp> ] * This file is part of the ethereumJ library. * * The ethereumJ library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The ethereumJ library 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the ethereumJ library. If not, see <http://www.gnu.org/licenses/>. */ package org.ethereum.datasource; /** * Abstract cache implementation which tracks the cache size with * supplied key and value MemSizeEstimator's * * Created by Anton Nashatyrev on 01.12.2016. */ public abstract class AbstractCachedSource <Key, Value> extends AbstractChainedSource<Key, Value, Key, Value> implements CachedSource<Key, Value> { private final Object lock = new Object(); /** * Like the Optional interface represents either the value cached * or null cached (i.e. cache knows that underlying storage contain null) */ public interface Entry<V> { V value(); } static final class SimpleEntry<V> implements Entry<V> { private V val; public SimpleEntry(V val) { this.val = val; } public V value() { return val; } } protected MemSizeEstimator<Key> keySizeEstimator; protected MemSizeEstimator<Value> valueSizeEstimator; private int size = 0; public AbstractCachedSource(Source<Key, Value> source) { super(source); } /** * Returns the cached value if exist. * Method doesn't look into the underlying storage * @return The value Entry if it cached (Entry may has null value if null value is cached), * or null if no information in the cache for this key */ abstract Entry<Value> getCached(Key key); /** * Needs to be called by the implementation when cache entry is added * Only new entries should be accounted for accurate size tracking * If the value for the key is changed the {@link #cacheRemoved} * needs to be called first */ protected void cacheAdded(Key key, Value value) { synchronized (lock) { if (keySizeEstimator != null) { size += keySizeEstimator.estimateSize(key); } if (valueSizeEstimator != null) { size += valueSizeEstimator.estimateSize(value); } } } /** * Needs to be called by the implementation when cache entry is removed */ protected void cacheRemoved(Key key, Value value) { synchronized (lock) { if (keySizeEstimator != null) { size -= keySizeEstimator.estimateSize(key); } if (valueSizeEstimator != null) { size -= valueSizeEstimator.estimateSize(value); } } } /** * Needs to be called by the implementation when cache is cleared */ protected void cacheCleared() { synchronized (lock) { size = 0; } } /** * Sets the key/value size estimators */ public AbstractCachedSource <Key, Value> withSizeEstimators(MemSizeEstimator<Key> keySizeEstimator, MemSizeEstimator<Value> valueSizeEstimator) { this.keySizeEstimator = keySizeEstimator; this.valueSizeEstimator = valueSizeEstimator; return this; } @Override public long estimateCacheSize() { return size; } }