diff --git a/src/java/org/apache/cassandra/utils/NativeLibrary.java b/src/java/org/apache/cassandra/utils/NativeLibrary.java index ec00162e195f..0cc1ba50959f 100644 --- a/src/java/org/apache/cassandra/utils/NativeLibrary.java +++ b/src/java/org/apache/cassandra/utils/NativeLibrary.java @@ -226,16 +226,27 @@ public static void trySkipCache(String path, long offset, long len) } public static void trySkipCache(int fd, long offset, long len, String path) + { + trySkipCache(offset, len, (subOffset, subLength) -> trySkipCache(fd, subOffset, subLength, path)); + } + + @FunctionalInterface + interface SkipCache + { + void skip(long offset, int len); + } + + static void trySkipCache(long offset, long len, SkipCache skipCache) { if (len == 0) - trySkipCache(fd, 0, 0, path); + skipCache.skip(0, 0); while (len > 0) { int sublen = (int) Math.min(Integer.MAX_VALUE, len); - trySkipCache(fd, offset, sublen, path); + skipCache.skip(offset, sublen); len -= sublen; - offset -= sublen; + offset += sublen; } } diff --git a/test/unit/org/apache/cassandra/utils/NativeLibraryTest.java b/test/unit/org/apache/cassandra/utils/NativeLibraryTest.java index 1856af1a645b..530f8236d865 100644 --- a/test/unit/org/apache/cassandra/utils/NativeLibraryTest.java +++ b/test/unit/org/apache/cassandra/utils/NativeLibraryTest.java @@ -18,6 +18,8 @@ */ package org.apache.cassandra.utils; +import java.util.ArrayList; +import java.util.List; import org.junit.Assert; import org.junit.Test; @@ -41,4 +43,48 @@ public void getPid() long pid = NativeLibrary.getProcessID(); Assert.assertTrue(pid > 0); } + + @Test + public void testSkipCacheSplitsLargeRangeAtIncreasingOffsets() + { + long offset = 1024; + long length = 2L * Integer.MAX_VALUE + 1; + List ranges = new ArrayList<>(); + + NativeLibrary.trySkipCache(offset, length, (subOffset, subLength) -> ranges.add(new long[]{ subOffset, subLength })); + + Assert.assertEquals(3, ranges.size()); + assertRange(ranges.get(0), offset, Integer.MAX_VALUE); + assertRange(ranges.get(1), offset + Integer.MAX_VALUE, Integer.MAX_VALUE); + assertRange(ranges.get(2), offset + 2L * Integer.MAX_VALUE, 1); + } + + @Test + public void testSkipCacheDoesNotSplitMaximumIntegerRange() + { + long offset = 4096; + List ranges = new ArrayList<>(); + + NativeLibrary.trySkipCache(offset, Integer.MAX_VALUE, (subOffset, subLength) -> ranges.add(new long[]{ subOffset, subLength })); + + Assert.assertEquals(1, ranges.size()); + assertRange(ranges.get(0), offset, Integer.MAX_VALUE); + } + + @Test + public void testSkipCacheZeroLengthTargetsEntireFile() + { + List ranges = new ArrayList<>(); + + NativeLibrary.trySkipCache(4096, 0, (subOffset, subLength) -> ranges.add(new long[]{ subOffset, subLength })); + + Assert.assertEquals(1, ranges.size()); + assertRange(ranges.get(0), 0, 0); + } + + private static void assertRange(long[] range, long offset, int length) + { + Assert.assertEquals(offset, range[0]); + Assert.assertEquals(length, range[1]); + } }