From 3c190d116a38ec19400d49436faa0f851a3e9c47 Mon Sep 17 00:00:00 2001 From: Henning Andersen Date: Thu, 25 Apr 2024 17:10:21 +0200 Subject: [PATCH] Utility methods for rounding to aligned size Add more utility methods for rounding to aligned size and use these in computeRange and toPageAlignedSize. --- .../blobcache/BlobCacheUtils.java | 29 +++++++--- .../blobcache/BlobCacheUtilsTests.java | 56 ++++++++++++++++++- 2 files changed, 74 insertions(+), 11 deletions(-) diff --git a/x-pack/plugin/blob-cache/src/main/java/org/elasticsearch/blobcache/BlobCacheUtils.java b/x-pack/plugin/blob-cache/src/main/java/org/elasticsearch/blobcache/BlobCacheUtils.java index ff273d99d3c41..5bd8378765d6f 100644 --- a/x-pack/plugin/blob-cache/src/main/java/org/elasticsearch/blobcache/BlobCacheUtils.java +++ b/x-pack/plugin/blob-cache/src/main/java/org/elasticsearch/blobcache/BlobCacheUtils.java @@ -33,14 +33,25 @@ public static int toIntBytes(long l) { } /** - * Rounds the length up so that it is aligned on the next page size (defined by SharedBytes.PAGE_SIZE). For example + * Round down the size to the nearest aligned size <= size. + */ + public static long roundDownToAlignedSize(long size, long alignment) { + return size / alignment * alignment; + } + + /** + * Round up the size to the nearest aligned size >= size + */ + public static long roundUpToAlignedSize(long size, long alignment) { + return (((size - 1) / alignment) + 1) * alignment; + } + + /** + * Rounds the length up so that it is aligned on the next page size (defined by SharedBytes.PAGE_SIZE). */ public static long toPageAlignedSize(long length) { - int remainder = (int) (length % SharedBytes.PAGE_SIZE); - if (remainder > 0L) { - return length + (SharedBytes.PAGE_SIZE - remainder); - } - return length; + int alignment = SharedBytes.PAGE_SIZE; + return roundUpToAlignedSize(length, alignment); } public static void throwEOF(long channelPos, long len) throws EOFException { @@ -58,13 +69,13 @@ public static void ensureSeek(long pos, IndexInput input) throws IOException { public static ByteRange computeRange(long rangeSize, long position, long size, long blobLength) { return ByteRange.of( - (position / rangeSize) * rangeSize, - Math.min((((position + size - 1) / rangeSize) + 1) * rangeSize, blobLength) + roundDownToAlignedSize(position, rangeSize), + Math.min(roundUpToAlignedSize(position + size, rangeSize), blobLength) ); } public static ByteRange computeRange(long rangeSize, long position, long size) { - return ByteRange.of((position / rangeSize) * rangeSize, (((position + size - 1) / rangeSize) + 1) * rangeSize); + return ByteRange.of(roundDownToAlignedSize(position, rangeSize), roundUpToAlignedSize(position + size, rangeSize)); } public static void ensureSlice(String sliceName, long sliceOffset, long sliceLength, IndexInput input) { diff --git a/x-pack/plugin/blob-cache/src/test/java/org/elasticsearch/blobcache/BlobCacheUtilsTests.java b/x-pack/plugin/blob-cache/src/test/java/org/elasticsearch/blobcache/BlobCacheUtilsTests.java index 2b615dad05655..fbe17c1ee86fd 100644 --- a/x-pack/plugin/blob-cache/src/test/java/org/elasticsearch/blobcache/BlobCacheUtilsTests.java +++ b/x-pack/plugin/blob-cache/src/test/java/org/elasticsearch/blobcache/BlobCacheUtilsTests.java @@ -6,14 +6,16 @@ */ package org.elasticsearch.blobcache; +import org.elasticsearch.blobcache.common.ByteRange; import org.elasticsearch.blobcache.shared.SharedBytes; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.test.ESTestCase; -import org.hamcrest.Matchers; import java.io.EOFException; import java.nio.ByteBuffer; +import static org.hamcrest.Matchers.equalTo; + public class BlobCacheUtilsTests extends ESTestCase { public void testReadSafeThrows() { @@ -25,6 +27,56 @@ public void testReadSafeThrows() { public void testToPageAlignedSize() { long value = randomLongBetween(0, Long.MAX_VALUE - SharedBytes.PAGE_SIZE); long expected = ((value - 1) / SharedBytes.PAGE_SIZE + 1) * SharedBytes.PAGE_SIZE; - assertThat(BlobCacheUtils.toPageAlignedSize(value), Matchers.equalTo(expected)); + assertThat(BlobCacheUtils.toPageAlignedSize(value), equalTo(expected)); + assertThat(BlobCacheUtils.toPageAlignedSize(value), equalTo(roundUpUsingRemainder(value, SharedBytes.PAGE_SIZE))); + } + + public void testRoundUpToAlignment() { + assertThat(BlobCacheUtils.roundUpToAlignedSize(8, 4), equalTo(8L)); + assertThat(BlobCacheUtils.roundUpToAlignedSize(9, 4), equalTo(12L)); + long alignment = randomLongBetween(1, Long.MAX_VALUE / 2); + long value = randomLongBetween(0, Long.MAX_VALUE - alignment); + assertThat(BlobCacheUtils.roundUpToAlignedSize(value, alignment), equalTo(roundUpUsingRemainder(value, alignment))); + } + + public void testRoundDownToAlignment() { + assertThat(BlobCacheUtils.roundDownToAlignedSize(8, 4), equalTo(8L)); + assertThat(BlobCacheUtils.roundDownToAlignedSize(9, 4), equalTo(8L)); + long alignment = randomLongBetween(1, Long.MAX_VALUE / 2); + long value = randomLongBetween(0, Long.MAX_VALUE - alignment); + assertThat(BlobCacheUtils.roundDownToAlignedSize(value, alignment), equalTo(roundDownUsingRemainder(value, alignment))); + } + + public void testComputeRange() { + assertThat(BlobCacheUtils.computeRange(8, 8, 8), equalTo(ByteRange.of(8, 16))); + assertThat(BlobCacheUtils.computeRange(8, 9, 8), equalTo(ByteRange.of(8, 24))); + assertThat(BlobCacheUtils.computeRange(8, 8, 9), equalTo(ByteRange.of(8, 24))); + + long large = randomLongBetween(24, 64); + assertThat(BlobCacheUtils.computeRange(8, 8, 8, large), equalTo(ByteRange.of(8, 16))); + assertThat(BlobCacheUtils.computeRange(8, 9, 8, large), equalTo(ByteRange.of(8, 24))); + assertThat(BlobCacheUtils.computeRange(8, 8, 9, large), equalTo(ByteRange.of(8, 24))); + + long small = randomLongBetween(8, 16); + assertThat(BlobCacheUtils.computeRange(8, 8, 8, small), equalTo(ByteRange.of(8, small))); + assertThat(BlobCacheUtils.computeRange(8, 9, 8, small), equalTo(ByteRange.of(8, small))); + assertThat(BlobCacheUtils.computeRange(8, 8, 9, small), equalTo(ByteRange.of(8, small))); + } + + private static long roundUpUsingRemainder(long value, long alignment) { + long remainder = value % alignment; + if (remainder > 0L) { + return value + (alignment - remainder); + } + return value; + } + + private static long roundDownUsingRemainder(long value, long alignment) { + long remainder = value % alignment; + if (remainder > 0L) { + return value - remainder; + } + return value; + } }