From e87221c93897676f54884cb3e86f30724b9b378c Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 19 May 2020 23:29:30 +0100 Subject: [PATCH] Add Cache.getCachedBytes This will replace the need to use CacheUtil.getCached, and is part of refactoring CacheUtil to only do writing (it will be renamed to CacheWriter in a subsequent change). PiperOrigin-RevId: 312366040 --- .../exoplayer2/upstream/cache/Cache.java | 12 ++++ .../upstream/cache/SimpleCache.java | 23 +++++++ .../upstream/cache/SimpleCacheTest.java | 66 +++++++++++++++++++ 3 files changed, 101 insertions(+) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/Cache.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/Cache.java index 781b5619d0..33f0dc35f2 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/Cache.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/Cache.java @@ -279,6 +279,18 @@ public interface Cache { */ long getCachedLength(String key, long position, long length); + /** + * Returns the total number of cached bytes between {@code position} (inclusive) and {@code + * (position + length)} (exclusive) of a resource. + * + * @param key The cache key of the resource. + * @param position The starting position of the data in the resource. + * @param length The length of the data to check. {@link C#LENGTH_UNSET} is permitted, and is + * equivalent to passing {@link Long#MAX_VALUE}. + * @return The total number of cached bytes. + */ + long getCachedBytes(String key, long position, long length); + /** * Applies {@code mutations} to the {@link ContentMetadata} for the given resource. A new {@link * CachedContent} is added if there isn't one already for the resource. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCache.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCache.java index c71d8e2714..1cb6d13fc0 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCache.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCache.java @@ -501,6 +501,29 @@ public final class SimpleCache implements Cache { return cachedContent != null ? cachedContent.getCachedBytesLength(position, length) : -length; } + @Override + public synchronized long getCachedBytes(String key, long position, long length) { + long endPosition = length == C.LENGTH_UNSET ? Long.MAX_VALUE : position + length; + if (endPosition < 0) { + // The calculation rolled over (length is probably Long.MAX_VALUE). + endPosition = Long.MAX_VALUE; + } + long currentPosition = position; + long cachedBytes = 0; + while (currentPosition < endPosition) { + long maxRemainingLength = endPosition - currentPosition; + long blockLength = getCachedLength(key, currentPosition, maxRemainingLength); + if (blockLength > 0) { + cachedBytes += blockLength; + } else { + // There's a hole of length -blockLength. + blockLength = -blockLength; + } + currentPosition += blockLength; + } + return cachedBytes; + } + @Override public synchronized void applyContentMetadataMutations( String key, ContentMetadataMutations mutations) throws CacheException { diff --git a/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/SimpleCacheTest.java b/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/SimpleCacheTest.java index 73cde3dee4..2b43ff9d3b 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/SimpleCacheTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/SimpleCacheTest.java @@ -409,6 +409,72 @@ public class SimpleCacheTest { .isEqualTo(15); } + @Test + public void getCachedBytes_noCachedContent_returnsZero() { + SimpleCache simpleCache = getSimpleCache(); + + assertThat(simpleCache.getCachedBytes(KEY_1, /* position= */ 0, /* length= */ 100)) + .isEqualTo(0); + assertThat(simpleCache.getCachedBytes(KEY_1, /* position= */ 0, /* length= */ Long.MAX_VALUE)) + .isEqualTo(0); + assertThat(simpleCache.getCachedBytes(KEY_1, /* position= */ 0, /* length= */ LENGTH_UNSET)) + .isEqualTo(0); + assertThat(simpleCache.getCachedBytes(KEY_1, /* position= */ 20, /* length= */ 100)) + .isEqualTo(0); + assertThat(simpleCache.getCachedBytes(KEY_1, /* position= */ 20, /* length= */ Long.MAX_VALUE)) + .isEqualTo(0); + assertThat(simpleCache.getCachedBytes(KEY_1, /* position= */ 20, /* length= */ LENGTH_UNSET)) + .isEqualTo(0); + } + + @Test + public void getCachedBytes_withMultipleAdjacentSpans_returnsCachedBytes() throws Exception { + SimpleCache simpleCache = getSimpleCache(); + CacheSpan cacheSpan = simpleCache.startReadWrite(KEY_1, /* position= */ 0); + addCache(simpleCache, KEY_1, /* position= */ 0, /* length= */ 25); + addCache(simpleCache, KEY_1, /* position= */ 25, /* length= */ 25); + simpleCache.releaseHoleSpan(cacheSpan); + + assertThat(simpleCache.getCachedBytes(KEY_1, /* position= */ 0, /* length= */ 100)) + .isEqualTo(50); + assertThat(simpleCache.getCachedBytes(KEY_1, /* position= */ 0, /* length= */ Long.MAX_VALUE)) + .isEqualTo(50); + assertThat(simpleCache.getCachedBytes(KEY_1, /* position= */ 0, /* length= */ LENGTH_UNSET)) + .isEqualTo(50); + assertThat(simpleCache.getCachedBytes(KEY_1, /* position= */ 20, /* length= */ 100)) + .isEqualTo(30); + assertThat(simpleCache.getCachedBytes(KEY_1, /* position= */ 20, /* length= */ Long.MAX_VALUE)) + .isEqualTo(30); + assertThat(simpleCache.getCachedBytes(KEY_1, /* position= */ 20, /* length= */ LENGTH_UNSET)) + .isEqualTo(30); + assertThat(simpleCache.getCachedBytes(KEY_1, /* position= */ 20, /* length= */ 15)) + .isEqualTo(15); + } + + @Test + public void getCachedBytes_withMultipleNonAdjacentSpans_returnsCachedBytes() throws Exception { + SimpleCache simpleCache = getSimpleCache(); + CacheSpan cacheSpan = simpleCache.startReadWrite(KEY_1, /* position= */ 0); + addCache(simpleCache, KEY_1, /* position= */ 0, /* length= */ 10); + addCache(simpleCache, KEY_1, /* position= */ 15, /* length= */ 35); + simpleCache.releaseHoleSpan(cacheSpan); + + assertThat(simpleCache.getCachedBytes(KEY_1, /* position= */ 0, /* length= */ 100)) + .isEqualTo(45); + assertThat(simpleCache.getCachedBytes(KEY_1, /* position= */ 0, /* length= */ Long.MAX_VALUE)) + .isEqualTo(45); + assertThat(simpleCache.getCachedBytes(KEY_1, /* position= */ 0, /* length= */ LENGTH_UNSET)) + .isEqualTo(45); + assertThat(simpleCache.getCachedBytes(KEY_1, /* position= */ 20, /* length= */ 100)) + .isEqualTo(30); + assertThat(simpleCache.getCachedBytes(KEY_1, /* position= */ 20, /* length= */ Long.MAX_VALUE)) + .isEqualTo(30); + assertThat(simpleCache.getCachedBytes(KEY_1, /* position= */ 20, /* length= */ LENGTH_UNSET)) + .isEqualTo(30); + assertThat(simpleCache.getCachedBytes(KEY_1, /* position= */ 20, /* length= */ 10)) + .isEqualTo(10); + } + /* Tests https://github.com/google/ExoPlayer/issues/3260 case. */ @Test public void exceptionDuringEvictionByLeastRecentlyUsedCacheEvictorNotHang() throws Exception {