diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 59537c2..2518b65 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- java: ['17', '21']
+ java: ['25-ea']
steps:
- uses: actions/checkout@v4
- name: Setup JDK ${{ matrix.java }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 9195b17..b8f345c 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -34,7 +34,7 @@ jobs:
- name: Install java
uses: actions/setup-java@v4
with:
- java-version: '21'
+ java-version: '25-ea'
distribution: 'temurin'
gpg-private-key: ${{ secrets.JRELEASER_GPG_SECRET_KEY }}
gpg-passphrase: MAVEN_GPG_PASSPHRASE
diff --git a/pom.xml b/pom.xml
index 8381e00..e602780 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
io.airlift
airbase
- 160
+ 291
slice
@@ -28,8 +28,9 @@
-missing
3584m
- 17
- 17
+ 25
+ 25
+ true
8
true
true
@@ -77,6 +78,12 @@
test
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
org.junit.jupiter
junit-jupiter-params
diff --git a/src/main/java/io/airlift/slice/Slice.java b/src/main/java/io/airlift/slice/Slice.java
index fe7c388..2971088 100644
--- a/src/main/java/io/airlift/slice/Slice.java
+++ b/src/main/java/io/airlift/slice/Slice.java
@@ -13,8 +13,6 @@
*/
package io.airlift.slice;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -46,7 +44,7 @@
import static sun.misc.Unsafe.ARRAY_LONG_BASE_OFFSET;
import static sun.misc.Unsafe.ARRAY_SHORT_BASE_OFFSET;
-public final class Slice
+public record Slice(byte[] byteArray, int byteArrayOffset, int size, long retainedSize, StableValue stableHashCode)
implements Comparable
{
private static final int INSTANCE_SIZE = instanceSize(Slice.class);
@@ -61,22 +59,6 @@ public final class Slice
// Do not move this field above the constants used in the empty constructor
static final Slice EMPTY_SLICE = new Slice();
- private final byte[] base;
-
- private final int baseOffset;
-
- /**
- * Size of the slice
- */
- private final int size;
-
- /**
- * Bytes retained by the slice
- */
- private final long retainedSize;
-
- private int hash;
-
/**
* This is only used to create the EMPTY_SLICE constant.
*/
@@ -84,25 +66,24 @@ private Slice()
{
// Since this is used to create a constant in this class, be careful to not use
// other uninitialized constants.
- this.base = new byte[0];
- this.baseOffset = 0;
- this.size = 0;
- this.retainedSize = INSTANCE_SIZE;
+ this(new byte[0], 0, 0, INSTANCE_SIZE, StableValue.of());
}
/**
* Creates a slice over the specified array.
*/
- Slice(byte[] base)
+ Slice(byte[] byteArray, int baseOffset)
{
- requireNonNull(base, "base is null");
- if (base.length == 0) {
+ this(byteArray, baseOffset, byteArray.length, INSTANCE_SIZE + sizeOf(byteArray), StableValue.of());
+ requireNonNull(byteArray, "base is null");
+ if (byteArray.length == 0) {
throw new IllegalArgumentException("Empty array");
}
- this.base = base;
- this.baseOffset = 0;
- this.size = base.length;
- this.retainedSize = INSTANCE_SIZE + sizeOf(base);
+ }
+
+ Slice(byte[] byteArray)
+ {
+ this(byteArray, 0);
}
/**
@@ -113,34 +94,21 @@ private Slice()
*/
Slice(byte[] base, int offset, int length)
{
+ this(base, offset, length, INSTANCE_SIZE + sizeOf(base), StableValue.of());
requireNonNull(base, "base is null");
- if (base.length == 0) {
- throw new IllegalArgumentException("Empty array");
- }
+ requireNonNull(stableHashCode, "stableHashCode is null");
checkFromIndexSize(offset, length, base.length);
-
- this.base = base;
- this.baseOffset = offset;
- this.size = length;
- this.retainedSize = INSTANCE_SIZE + sizeOf(base);
}
/**
* Creates a slice for directly accessing the base object.
*/
- Slice(byte[] base, int baseOffset, int size, long retainedSize)
+ // INSTANCE_SIZE is not included, as the caller is responsible for including it.
+ public Slice
{
- requireNonNull(base, "base is null");
- if (base.length == 0) {
- throw new IllegalArgumentException("Empty array");
- }
- checkFromIndexSize(baseOffset, size, base.length);
-
- this.base = requireNonNull(base, "base is null");
- this.baseOffset = baseOffset;
- this.size = size;
- // INSTANCE_SIZE is not included, as the caller is responsible for including it.
- this.retainedSize = retainedSize;
+ requireNonNull(byteArray, "byteArray is null");
+ checkFromIndexSize(byteArrayOffset, size, byteArray.length);
+ requireNonNull(byteArray, "base is null");
}
/**
@@ -165,33 +133,23 @@ public long getRetainedSize()
*/
public boolean isCompact()
{
- return baseOffset == 0 && size == base.length;
+ return byteArrayOffset == 0 && size == byteArray.length;
}
/**
- * Returns the byte array wrapped by this Slice. Callers should also take care to use {@link Slice#byteArrayOffset()}
- * since the contents of this Slice may not start at array index 0.
- */
- @SuppressFBWarnings("EI_EXPOSE_REP")
- public byte[] byteArray()
- {
- return base;
- }
-
- /**
- * Returns the start index the content of this slice within the byte array wrapped by this slice.
+ * Fill the slice with the specified value;
*/
- public int byteArrayOffset()
+ public void fill(byte value)
{
- return baseOffset;
+ fill(0, size, value);
}
/**
* Fill the slice with the specified value;
*/
- public void fill(byte value)
+ public void fill(int offset, int length, byte value)
{
- Arrays.fill(base, baseOffset, baseOffset + size, value);
+ Arrays.fill(byteArray, byteArrayOffset + offset, byteArrayOffset + offset + length, value);
}
/**
@@ -204,7 +162,7 @@ public void clear()
public void clear(int offset, int length)
{
- Arrays.fill(base, baseOffset, baseOffset + size, (byte) 0);
+ fill(offset, length, (byte) 0);
}
/**
@@ -221,7 +179,7 @@ public byte getByte(int index)
public byte getByteUnchecked(int index)
{
- return base[baseOffset + index];
+ return byteArray[byteArrayOffset + index];
}
/**
@@ -251,7 +209,7 @@ public short getShort(int index)
public short getShortUnchecked(int index)
{
- return (short) SHORT_HANDLE.get(base, baseOffset + index);
+ return (short) SHORT_HANDLE.get(byteArray, byteArrayOffset + index);
}
/**
@@ -281,7 +239,7 @@ public int getInt(int index)
public int getIntUnchecked(int index)
{
- return (int) INT_HANDLE.get(base, baseOffset + index);
+ return (int) INT_HANDLE.get(byteArray, byteArrayOffset + index);
}
/**
@@ -311,7 +269,7 @@ public long getLong(int index)
public long getLongUnchecked(int index)
{
- return (long) LONG_HANDLE.get(base, baseOffset + index);
+ return (long) LONG_HANDLE.get(byteArray, byteArrayOffset + index);
}
/**
@@ -329,7 +287,7 @@ public float getFloat(int index)
public float getFloatUnchecked(int index)
{
- return (float) FLOAT_HANDLE.get(base, baseOffset + index);
+ return (float) FLOAT_HANDLE.get(byteArray, byteArrayOffset + index);
}
/**
@@ -347,7 +305,7 @@ public double getDouble(int index)
public double getDoubleUnchecked(int index)
{
- return (double) DOUBLE_HANDLE.get(base, baseOffset + index);
+ return (double) DOUBLE_HANDLE.get(byteArray, byteArrayOffset + index);
}
/**
@@ -380,7 +338,7 @@ public void getBytes(int index, Slice destination, int destinationIndex, int len
checkFromIndexSize(destinationIndex, length, destination.length());
checkFromIndexSize(index, length, length());
- System.arraycopy(base, baseOffset + index, destination.base, destination.baseOffset + destinationIndex, length);
+ System.arraycopy(byteArray, byteArrayOffset + index, destination.byteArray, destination.byteArrayOffset + destinationIndex, length);
}
/**
@@ -413,7 +371,7 @@ public void getBytes(int index, byte[] destination, int destinationIndex, int le
checkFromIndexSize(index, length, length());
checkFromIndexSize(destinationIndex, length, destination.length);
- System.arraycopy(base, baseOffset + index, destination, destinationIndex, length);
+ System.arraycopy(byteArray, byteArrayOffset + index, destination, destinationIndex, length);
}
/**
@@ -711,7 +669,7 @@ public void setByte(int index, int value)
void setByteUnchecked(int index, int value)
{
- base[baseOffset + index] = (byte) (value & 0xFF);
+ byteArray[byteArrayOffset + index] = (byte) (value & 0xFF);
}
/**
@@ -730,7 +688,7 @@ public void setShort(int index, int value)
void setShortUnchecked(int index, int value)
{
- SHORT_HANDLE.set(base, baseOffset + index, (short) (value & 0xFFFF));
+ SHORT_HANDLE.set(byteArray, byteArrayOffset + index, (short) (value & 0xFFFF));
}
/**
@@ -748,7 +706,7 @@ public void setInt(int index, int value)
void setIntUnchecked(int index, int value)
{
- INT_HANDLE.set(base, baseOffset + index, value);
+ INT_HANDLE.set(byteArray, byteArrayOffset + index, value);
}
/**
@@ -766,7 +724,7 @@ public void setLong(int index, long value)
void setLongUnchecked(int index, long value)
{
- LONG_HANDLE.set(base, baseOffset + index, value);
+ LONG_HANDLE.set(byteArray, byteArrayOffset + index, value);
}
/**
@@ -779,7 +737,7 @@ void setLongUnchecked(int index, long value)
public void setFloat(int index, float value)
{
checkFromIndexSize(index, SIZE_OF_FLOAT, length());
- FLOAT_HANDLE.set(base, baseOffset + index, value);
+ FLOAT_HANDLE.set(byteArray, byteArrayOffset + index, value);
}
/**
@@ -792,7 +750,7 @@ public void setFloat(int index, float value)
public void setDouble(int index, double value)
{
checkFromIndexSize(index, SIZE_OF_DOUBLE, length());
- DOUBLE_HANDLE.set(base, baseOffset + index, value);
+ DOUBLE_HANDLE.set(byteArray, byteArrayOffset + index, value);
}
/**
@@ -825,7 +783,7 @@ public void setBytes(int index, Slice source, int sourceIndex, int length)
checkFromIndexSize(index, length, length());
checkFromIndexSize(sourceIndex, length, source.length());
- System.arraycopy(source.base, source.baseOffset + sourceIndex, base, baseOffset + index, length);
+ System.arraycopy(source.byteArray, source.byteArrayOffset + sourceIndex, byteArray, byteArrayOffset + index, length);
}
/**
@@ -854,7 +812,7 @@ public void setBytes(int index, byte[] source, int sourceIndex, int length)
{
checkFromIndexSize(index, length, length());
checkFromIndexSize(sourceIndex, length, source.length);
- System.arraycopy(source, sourceIndex, base, baseOffset + index, length);
+ System.arraycopy(source, sourceIndex, byteArray, byteArrayOffset + index, length);
}
/**
@@ -1039,7 +997,7 @@ public Slice slice(int index, int length)
return Slices.EMPTY_SLICE;
}
- return new Slice(base, baseOffset + index, length, retainedSize);
+ return new Slice(byteArray, byteArrayOffset + index, length, retainedSize, StableValue.of());
}
/**
@@ -1051,7 +1009,7 @@ public Slice copy()
if (size == 0) {
return Slices.EMPTY_SLICE;
}
- return new Slice(Arrays.copyOfRange(base, baseOffset, baseOffset + size));
+ return new Slice(Arrays.copyOfRange(byteArray, byteArrayOffset, byteArrayOffset + size), 0);
}
/**
@@ -1064,7 +1022,7 @@ public Slice copy(int index, int length)
if (length == 0) {
return Slices.EMPTY_SLICE;
}
- return new Slice(Arrays.copyOfRange(base, baseOffset + index, baseOffset + index + length));
+ return new Slice(Arrays.copyOfRange(byteArray, byteArrayOffset + index, byteArrayOffset + index + length), 0);
}
public int indexOfByte(int b)
@@ -1212,12 +1170,12 @@ public int compareTo(int offset, int length, Slice that, int otherOffset, int ot
checkFromIndexSize(otherOffset, otherLength, that.length());
return Arrays.compareUnsigned(
- base,
- baseOffset + offset,
- baseOffset + offset + length,
- that.base,
- that.baseOffset + otherOffset,
- that.baseOffset + otherOffset + otherLength);
+ byteArray,
+ byteArrayOffset + offset,
+ byteArrayOffset + offset + length,
+ that.byteArray,
+ that.byteArrayOffset + otherOffset,
+ that.byteArrayOffset + otherOffset + otherLength);
}
/**
@@ -1249,12 +1207,7 @@ public boolean equals(Object o)
@Override
public int hashCode()
{
- if (hash != 0) {
- return hash;
- }
-
- hash = hashCode(0, size);
- return hash;
+ return stableHashCode.orElseSet(() -> hashCode(0, size));
}
/**
@@ -1289,12 +1242,12 @@ public boolean equals(int offset, int length, Slice that, int otherOffset, int o
boolean equalsUnchecked(int offset, Slice that, int otherOffset, int length)
{
return Arrays.equals(
- base,
- baseOffset + offset,
- baseOffset + offset + length,
- that.base,
- that.baseOffset + otherOffset,
- that.baseOffset + otherOffset + length);
+ byteArray,
+ byteArrayOffset + offset,
+ byteArrayOffset + offset + length,
+ that.byteArray,
+ that.byteArrayOffset + otherOffset,
+ that.byteArrayOffset + otherOffset + length);
}
/**
@@ -1388,8 +1341,8 @@ public ByteBuffer toByteBuffer(int index, int length)
public String toString()
{
StringBuilder builder = new StringBuilder("Slice{");
- builder.append("base=").append(identityToString(base)).append(", ");
- builder.append("baseOffset=").append(baseOffset);
+ builder.append("base=").append(identityToString(byteArray)).append(", ");
+ builder.append("baseOffset=").append(byteArrayOffset);
builder.append(", length=").append(length());
builder.append('}');
return builder.toString();
@@ -1405,23 +1358,23 @@ private static String identityToString(Object o)
private void copyFromBase(int index, Object dest, long destAddress, int length)
{
- int baseAddress = ARRAY_BYTE_BASE_OFFSET + baseOffset + index;
+ int baseAddress = ARRAY_BYTE_BASE_OFFSET + byteArrayOffset + index;
// The Unsafe Javadoc specifies that the transfer size is 8 iff length % 8 == 0
// so ensure that we copy big chunks whenever possible, even at the expense of two separate copy operations
// todo the optimization only works if the baseOffset is is a multiple of 8 for both src and dest
int bytesToCopy = length - (length % 8);
- unsafe.copyMemory(base, baseAddress, dest, destAddress, bytesToCopy);
- unsafe.copyMemory(base, baseAddress + bytesToCopy, dest, destAddress + bytesToCopy, length - bytesToCopy);
+ unsafe.copyMemory(byteArray, baseAddress, dest, destAddress, bytesToCopy);
+ unsafe.copyMemory(byteArray, baseAddress + bytesToCopy, dest, destAddress + bytesToCopy, length - bytesToCopy);
}
private void copyToBase(int index, Object src, long srcAddress, int length)
{
- int baseAddress = ARRAY_BYTE_BASE_OFFSET + baseOffset + index;
+ int baseAddress = ARRAY_BYTE_BASE_OFFSET + byteArrayOffset + index;
// The Unsafe Javadoc specifies that the transfer size is 8 iff length % 8 == 0
// so ensure that we copy big chunks whenever possible, even at the expense of two separate copy operations
// todo the optimization only works if the baseOffset is is a multiple of 8 for both src and dest
int bytesToCopy = length - (length % 8);
- unsafe.copyMemory(src, srcAddress, base, baseAddress, bytesToCopy);
- unsafe.copyMemory(src, srcAddress + bytesToCopy, base, baseAddress + bytesToCopy, length - bytesToCopy);
+ unsafe.copyMemory(src, srcAddress, byteArray, baseAddress, bytesToCopy);
+ unsafe.copyMemory(src, srcAddress + bytesToCopy, byteArray, baseAddress + bytesToCopy, length - bytesToCopy);
}
}