Commit e40bba28 by eguven Committed by Oliver Woodman

Add Cache.getCachedBytes() which returns the length of the cached or not data block length

This method can be used to determine not cached parts of a content.
The 'length' parameter allows quicker responses without going through all adjacent spans.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=149005688
parent 247da48e
...@@ -192,6 +192,41 @@ public class SimpleCacheTest extends InstrumentationTestCase { ...@@ -192,6 +192,41 @@ public class SimpleCacheTest extends InstrumentationTestCase {
assertEquals(0, cacheDir.listFiles().length); assertEquals(0, cacheDir.listFiles().length);
} }
public void testGetCachedBytes() throws Exception {
SimpleCache simpleCache = getSimpleCache();
CacheSpan cacheSpan = simpleCache.startReadWrite(KEY_1, 0);
// No cached bytes, returns -'length'
assertEquals(-100, simpleCache.getCachedBytes(KEY_1, 0, 100));
// Position value doesn't affect the return value
assertEquals(-100, simpleCache.getCachedBytes(KEY_1, 20, 100));
addCache(simpleCache, KEY_1, 0, 15);
// Returns the length of a single span
assertEquals(15, simpleCache.getCachedBytes(KEY_1, 0, 100));
// Value is capped by the 'length'
assertEquals(10, simpleCache.getCachedBytes(KEY_1, 0, 10));
addCache(simpleCache, KEY_1, 15, 35);
// Returns the length of two adjacent spans
assertEquals(50, simpleCache.getCachedBytes(KEY_1, 0, 100));
addCache(simpleCache, KEY_1, 60, 10);
// Not adjacent span doesn't affect return value
assertEquals(50, simpleCache.getCachedBytes(KEY_1, 0, 100));
// Returns length of hole up to the next cached span
assertEquals(-5, simpleCache.getCachedBytes(KEY_1, 55, 100));
simpleCache.releaseHoleSpan(cacheSpan);
}
private SimpleCache getSimpleCache() { private SimpleCache getSimpleCache() {
return new SimpleCache(cacheDir, new NoOpCacheEvictor()); return new SimpleCache(cacheDir, new NoOpCacheEvictor());
} }
......
...@@ -199,6 +199,18 @@ public interface Cache { ...@@ -199,6 +199,18 @@ public interface Cache {
boolean isCached(String key, long position, long length); boolean isCached(String key, long position, long length);
/** /**
* Returns the length of the cached data block starting from the {@code position} to the block end
* up to {@code length} bytes. If the {@code position} isn't cached then -(the length of the gap
* to the next cached data up to {@code length} bytes) is returned.
*
* @param key The cache key for the data.
* @param position The starting position of the data.
* @param length The maximum length of the data to be returned.
* @return the length of the cached or not cached data block length.
*/
long getCachedBytes(String key, long position, long length);
/**
* Sets the content length for the given key. * Sets the content length for the given key.
* *
* @param key The cache key for the data. * @param key The cache key for the data.
......
...@@ -106,43 +106,49 @@ import java.util.TreeSet; ...@@ -106,43 +106,49 @@ import java.util.TreeSet;
* which defines the maximum extents of the hole in the cache. * which defines the maximum extents of the hole in the cache.
*/ */
public SimpleCacheSpan getSpan(long position) { public SimpleCacheSpan getSpan(long position) {
SimpleCacheSpan span = getSpanInternal(position); SimpleCacheSpan lookupSpan = SimpleCacheSpan.createLookup(key, position);
if (!span.isCached) { SimpleCacheSpan floorSpan = cachedSpans.floor(lookupSpan);
SimpleCacheSpan ceilEntry = cachedSpans.ceiling(span); if (floorSpan != null && floorSpan.position + floorSpan.length > position) {
return ceilEntry == null ? SimpleCacheSpan.createOpenHole(key, position) return floorSpan;
: SimpleCacheSpan.createClosedHole(key, position, ceilEntry.position - position);
} }
return span; SimpleCacheSpan ceilSpan = cachedSpans.ceiling(lookupSpan);
return ceilSpan == null ? SimpleCacheSpan.createOpenHole(key, position)
: SimpleCacheSpan.createClosedHole(key, position, ceilSpan.position - position);
} }
/** Queries if a range is entirely available in the cache. */ /**
public boolean isCached(long position, long length) { * Returns the length of the cached data block starting from the {@code position} to the block end
SimpleCacheSpan floorSpan = getSpanInternal(position); * up to {@code length} bytes. If the {@code position} isn't cached then -(the length of the gap
if (!floorSpan.isCached) { * to the next cached data up to {@code length} bytes) is returned.
*
* @param position The starting position of the data.
* @param length The maximum length of the data to be returned.
* @return the length of the cached or not cached data block length.
*/
public long getCachedBytes(long position, long length) {
SimpleCacheSpan span = getSpan(position);
if (span.isHoleSpan()) {
// We don't have a span covering the start of the queried region. // We don't have a span covering the start of the queried region.
return false; return -Math.min(span.isOpenEnded() ? Long.MAX_VALUE : span.length, length);
} }
long queryEndPosition = position + length; long queryEndPosition = position + length;
long currentEndPosition = floorSpan.position + floorSpan.length; long currentEndPosition = span.position + span.length;
if (currentEndPosition >= queryEndPosition) { if (currentEndPosition < queryEndPosition) {
// floorSpan covers the queried region. for (SimpleCacheSpan next : cachedSpans.tailSet(span, false)) {
return true;
}
for (SimpleCacheSpan next : cachedSpans.tailSet(floorSpan, false)) {
if (next.position > currentEndPosition) { if (next.position > currentEndPosition) {
// There's a hole in the cache within the queried region. // There's a hole in the cache within the queried region.
return false; break;
} }
// We expect currentEndPosition to always equal (next.position + next.length), but // We expect currentEndPosition to always equal (next.position + next.length), but
// perform a max check anyway to guard against the existence of overlapping spans. // perform a max check anyway to guard against the existence of overlapping spans.
currentEndPosition = Math.max(currentEndPosition, next.position + next.length); currentEndPosition = Math.max(currentEndPosition, next.position + next.length);
if (currentEndPosition >= queryEndPosition) { if (currentEndPosition >= queryEndPosition) {
// We've found spans covering the queried region. // We've found spans covering the queried region.
return true; break;
} }
} }
// We ran out of spans before covering the queried region. }
return false; return Math.min(currentEndPosition - position, length);
} }
/** /**
...@@ -190,15 +196,4 @@ import java.util.TreeSet; ...@@ -190,15 +196,4 @@ import java.util.TreeSet;
return result; return result;
} }
/**
* Returns the span containing the position. If there isn't one, it returns the lookup span it
* used for searching.
*/
private SimpleCacheSpan getSpanInternal(long position) {
SimpleCacheSpan lookupSpan = SimpleCacheSpan.createLookup(key, position);
SimpleCacheSpan floorSpan = cachedSpans.floor(lookupSpan);
return floorSpan == null || floorSpan.position + floorSpan.length <= position ? lookupSpan
: floorSpan;
}
} }
...@@ -354,7 +354,13 @@ public final class SimpleCache implements Cache { ...@@ -354,7 +354,13 @@ public final class SimpleCache implements Cache {
@Override @Override
public synchronized boolean isCached(String key, long position, long length) { public synchronized boolean isCached(String key, long position, long length) {
CachedContent cachedContent = index.get(key); CachedContent cachedContent = index.get(key);
return cachedContent != null && cachedContent.isCached(position, length); return cachedContent != null && cachedContent.getCachedBytes(position, length) >= length;
}
@Override
public synchronized long getCachedBytes(String key, long position, long length) {
CachedContent cachedContent = index.get(key);
return cachedContent != null ? cachedContent.getCachedBytes(position, length) : -length;
} }
@Override @Override
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment