package com.pearson.entech.elasticsearch.search.facet.approx.datehistogram; import java.util.Arrays; import org.elasticsearch.common.CacheRecycler; import org.elasticsearch.common.joda.TimeZoneRounding; import org.elasticsearch.common.trove.map.hash.TLongLongHashMap; public class TimeZoneRoundingCache { // TODO can we actually cache TimeZoneRounding objects too? // TODO how about an object pool for long[] -- or extend CacheRecycler private final TimeZoneRounding _tzRounding; private final int _maxSize; private final TLongLongHashMap _cache; private final long _noEntryKey; private final long[] _keyRing; private int _keyRingPointer; public TimeZoneRoundingCache(final TimeZoneRounding tzRounding, final int maxSize) { _tzRounding = tzRounding; _maxSize = maxSize; _cache = CacheRecycler.popLongLongMap(); _noEntryKey = _cache.getNoEntryKey(); _keyRing = new long[maxSize]; Arrays.fill(_keyRing, _noEntryKey); _keyRingPointer = 0; } public void trash() { CacheRecycler.pushLongLongMap(_cache); } public int occupancy() { return _cache.size(); } public long round(final long timestamp) { final long rounded = _cache.get(timestamp); if(rounded == _noEntryKey) return roundAndCache(timestamp); else return rounded; } private long roundAndCache(final long timestamp) { final long rounded = _tzRounding.calc(timestamp); put(timestamp, rounded); return rounded; } private void put(final long key, final long value) { if(_cache.size() < _maxSize) { // Just store the value, advance the pointer to the next in the keyring, and save the key there _cache.put(key, value); advancePointer(); _keyRing[_keyRingPointer] = key; } else { // Advance the pointer, remove the corresponding value from the cache, store the new value, and store the pointer advancePointer(); _cache.remove(_keyRing[_keyRingPointer]); _cache.put(key, value); _keyRing[_keyRingPointer] = key; assert _cache.size() <= _maxSize; // TODO remove me after testing } } private void advancePointer() { final int newPointer = _keyRingPointer++; if(newPointer == _maxSize) _keyRingPointer = 0; else _keyRingPointer = newPointer; } }