Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions src/main/java/io/airlift/slice/DynamicSliceOutput.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,47 +83,47 @@ public int writableBytes()
public void writeByte(int value)
{
slice = Slices.ensureSize(slice, size + SIZE_OF_BYTE);
slice.setByte(size, value);
slice.setByteUnchecked(size, value);
size += SIZE_OF_BYTE;
}

@Override
public void writeShort(int value)
{
slice = Slices.ensureSize(slice, size + SIZE_OF_SHORT);
slice.setShort(size, value);
slice.setShortUnchecked(size, value);
size += SIZE_OF_SHORT;
}

@Override
public void writeInt(int value)
{
slice = Slices.ensureSize(slice, size + SIZE_OF_INT);
slice.setInt(size, value);
slice.setIntUnchecked(size, value);
size += SIZE_OF_INT;
}

@Override
public void writeLong(long value)
{
slice = Slices.ensureSize(slice, size + SIZE_OF_LONG);
slice.setLong(size, value);
slice.setLongUnchecked(size, value);
size += SIZE_OF_LONG;
}

@Override
public void writeFloat(float value)
{
slice = Slices.ensureSize(slice, size + SIZE_OF_FLOAT);
slice.setFloat(size, value);
slice.setFloatUnchecked(size, value);
size += SIZE_OF_FLOAT;
}

@Override
public void writeDouble(double value)
{
slice = Slices.ensureSize(slice, size + SIZE_OF_DOUBLE);
slice.setDouble(size, value);
slice.setDoubleUnchecked(size, value);
size += SIZE_OF_DOUBLE;
}

Expand Down
129 changes: 105 additions & 24 deletions src/main/java/io/airlift/slice/Slice.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import static io.airlift.slice.SizeOf.SIZE_OF_SHORT;
import static io.airlift.slice.SizeOf.instanceSize;
import static io.airlift.slice.SizeOf.sizeOf;
import static java.lang.Long.numberOfTrailingZeros;
import static java.lang.invoke.MethodHandles.byteArrayViewVarHandle;
import static java.nio.ByteOrder.LITTLE_ENDIAN;
import static java.nio.charset.StandardCharsets.UTF_8;
Expand Down Expand Up @@ -199,7 +200,7 @@ public void clear()

public void clear(int offset, int length)
{
Arrays.fill(base, baseOffset, baseOffset + size, (byte) 0);
Arrays.fill(base, baseOffset + offset, baseOffset + offset + length, (byte) 0);
Comment thread
wendigo marked this conversation as resolved.
}

/**
Expand Down Expand Up @@ -779,6 +780,11 @@ void setLongUnchecked(int index, long value)
public void setFloat(int index, float value)
{
checkFromIndexSize(index, SIZE_OF_FLOAT, length());
setFloatUnchecked(index, value);
}

void setFloatUnchecked(int index, float value)
{
FLOAT_HANDLE.set(base, baseOffset + index, value);
}

Expand All @@ -792,6 +798,11 @@ public void setFloat(int index, float value)
public void setDouble(int index, double value)
{
checkFromIndexSize(index, SIZE_OF_DOUBLE, length());
setDoubleUnchecked(index, value);
}

void setDoubleUnchecked(int index, double value)
{
DOUBLE_HANDLE.set(base, baseOffset + index, value);
}

Expand Down Expand Up @@ -1085,9 +1096,25 @@ public int indexOfByte(int b)

public int indexOfByte(byte b)
{
for (int i = 0; i < size; i++) {
if (getByteUnchecked(i) == b) {
return i;
return indexOfByte(b, 0, size);
}

private int indexOfByte(byte b, int offset, int size)
{
long pattern = (b & 0xFFL) * 0x01010101_01010101L;

for (; offset < size - 7; offset += 8) {
long value = getLongUnchecked(offset);
long xor = value ^ pattern;
long hasZero = (xor - 0x01010101_01010101L) & ~xor & 0x80808080_80808080L;
if (hasZero != 0) {
return offset + (numberOfTrailingZeros(hasZero) >>> 3);
}
}

for (; offset < size; offset++) {
if (getByteUnchecked(offset) == b) {
return offset;
}
}
return -1;
Expand Down Expand Up @@ -1150,7 +1177,7 @@ public int indexOf(Slice pattern, int offset)
}

// Try fast match of head and the rest
if (value == head && equalsUnchecked(index, pattern, 0, pattern.length())) {
if (value == head && equalsUnchecked(index, pattern.byteArray(), pattern.byteArrayOffset(), pattern.length())) {
return index;
}

Expand All @@ -1160,6 +1187,63 @@ public int indexOf(Slice pattern, int offset)
return -1;
}

/**
* Returns the index of the last occurrence of the pattern within this slice.
* If the pattern is not found -1 is returned. If pattern is empty, the
* length of this slice is returned.
*/
public int lastIndexOf(Slice slice)
{
return lastIndexOf(slice, size);
}

/**
* Returns the index of the last occurrence of the pattern within this slice,
* searching backward starting at the given offset.
* If the pattern is not found -1 is returned. If pattern is empty, the
* offset is returned.
*/
public int lastIndexOf(Slice pattern, int offset)
{
if (size == 0 && pattern.size == 0) {
return 0;
}

if (size == 0 || offset < 0) {
return -1;
}

if (pattern.length() == 0) {
return Math.min(offset, size);
}

// Clamp offset to the last valid position
int lastValidIndex = size - pattern.length();
int index = Math.min(offset, lastValidIndex);
if (index < 0) {
return -1;
}

byte firstByte = pattern.getByteUnchecked(0);
while (index >= 0) {
// seek to first byte match
while (index > 0 && getByteUnchecked(index) != firstByte) {
index--;
}
if (getByteUnchecked(index) != firstByte) {
break;
}

if (equalsUnchecked(index, pattern.byteArray(), pattern.byteArrayOffset(), pattern.length())) {
return index;
}

index--;
}

return -1;
}

int indexOfBruteForce(Slice pattern, int offset)
{
if (size == 0 || offset >= size || offset < 0) {
Expand All @@ -1173,16 +1257,13 @@ int indexOfBruteForce(Slice pattern, int offset)
byte firstByte = pattern.getByteUnchecked(0);
int lastValidIndex = size - pattern.length();
int index = offset;
while (true) {
// seek to first byte match
while (index < lastValidIndex && getByteUnchecked(index) != firstByte) {
index++;
}
if (index > lastValidIndex) {
while (index <= lastValidIndex) {
index = indexOfByte(firstByte, index, lastValidIndex + 1);
if (index < 0) {
break;
}

if (equalsUnchecked(index, pattern, 0, pattern.length())) {
if (equalsUnchecked(index, pattern.byteArray(), pattern.byteArrayOffset(), pattern.length())) {
return index;
}

Expand Down Expand Up @@ -1217,10 +1298,6 @@ public int compareTo(int offset, int length, Slice that, int otherOffset, int ot
if ((this == that) && (offset == otherOffset) && (length == otherLength)) {
return 0;
}

checkFromIndexSize(offset, length, length());
checkFromIndexSize(otherOffset, otherLength, that.length());

return Arrays.compareUnsigned(
base,
baseOffset + offset,
Expand Down Expand Up @@ -1248,7 +1325,7 @@ public boolean equals(Object o)
return false;
}

return equalsUnchecked(0, that, 0, length());
return equalsUnchecked(0, that.byteArray(), that.byteArrayOffset(), length());
}

/**
Expand Down Expand Up @@ -1289,22 +1366,26 @@ public boolean equals(int offset, int length, Slice that, int otherOffset, int o
if ((this == that) && (offset == otherOffset)) {
return true;
}
return equalsUnchecked(offset, that.byteArray(), that.byteArrayOffset() + otherOffset, length);
}

checkFromIndexSize(offset, length, length());
checkFromIndexSize(otherOffset, otherLength, that.length());

public boolean equals(int offset, int length, byte[] that, int otherOffset, int otherLength)
{
if (length != otherLength) {
return false;
}
return equalsUnchecked(offset, that, otherOffset, length);
}

boolean equalsUnchecked(int offset, Slice that, int otherOffset, int length)
boolean equalsUnchecked(int offset, byte[] that, int otherOffset, int length)
{
return Arrays.equals(
base,
baseOffset + offset,
baseOffset + offset + length,
that.base,
that.baseOffset + otherOffset,
that.baseOffset + otherOffset + length);
that,
otherOffset,
otherOffset + length);
}

/**
Expand Down
21 changes: 12 additions & 9 deletions src/main/java/io/airlift/slice/SliceUtf8.java
Original file line number Diff line number Diff line change
Expand Up @@ -1068,26 +1068,29 @@ public static int setCodePointAt(int codePoint, Slice utf8, int position)
}
if (codePoint < 0x800) {
// 110x_xxxx 10xx_xxxx
utf8.setByte(position, 0b1100_0000 | (codePoint >>> 6));
utf8.setByte(position + 1, 0b1000_0000 | (codePoint & 0b0011_1111));
checkFromIndexSize(position, 1, utf8.length());
utf8.setByteUnchecked(position, 0b1100_0000 | (codePoint >>> 6));
utf8.setByteUnchecked(position + 1, 0b1000_0000 | (codePoint & 0b0011_1111));
return 2;
}
if (MIN_SURROGATE <= codePoint && codePoint <= MAX_SURROGATE) {
throw new InvalidCodePointException(codePoint);
}
if (codePoint < 0x1_0000) {
// 1110_xxxx 10xx_xxxx 10xx_xxxx
utf8.setByte(position, 0b1110_0000 | ((codePoint >>> 12) & 0b0000_1111));
utf8.setByte(position + 1, 0b1000_0000 | ((codePoint >>> 6) & 0b0011_1111));
utf8.setByte(position + 2, 0b1000_0000 | (codePoint & 0b0011_1111));
checkFromIndexSize(position, 2, utf8.length());
utf8.setByteUnchecked(position, 0b1110_0000 | ((codePoint >>> 12) & 0b0000_1111));
utf8.setByteUnchecked(position + 1, 0b1000_0000 | ((codePoint >>> 6) & 0b0011_1111));
utf8.setByteUnchecked(position + 2, 0b1000_0000 | (codePoint & 0b0011_1111));
return 3;
}
if (codePoint < 0x11_0000) {
checkFromIndexSize(position, 3, utf8.length());
// 1111_0xxx 10xx_xxxx 10xx_xxxx 10xx_xxxx
utf8.setByte(position, 0b1111_0000 | ((codePoint >>> 18) & 0b0000_0111));
utf8.setByte(position + 1, 0b1000_0000 | ((codePoint >>> 12) & 0b0011_1111));
utf8.setByte(position + 2, 0b1000_0000 | ((codePoint >>> 6) & 0b0011_1111));
utf8.setByte(position + 3, 0b1000_0000 | (codePoint & 0b0011_1111));
utf8.setByteUnchecked(position, 0b1111_0000 | ((codePoint >>> 18) & 0b0000_0111));
utf8.setByteUnchecked(position + 1, 0b1000_0000 | ((codePoint >>> 12) & 0b0011_1111));
utf8.setByteUnchecked(position + 2, 0b1000_0000 | ((codePoint >>> 6) & 0b0011_1111));
utf8.setByteUnchecked(position + 3, 0b1000_0000 | (codePoint & 0b0011_1111));
return 4;
}
// Per RFC3629, UTF-8 is limited to 4 bytes, so more bytes are illegal
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/io/airlift/slice/BenchmarkSlice.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public Object compareTo(BenchmarkData data)
@Benchmark
public Object equalsUnchecked(BenchmarkData data)
{
return data.slice1.equalsUnchecked(0, data.slice2, 0, data.slice1.length());
return data.slice1.equalsUnchecked(0, data.slice2.byteArray(), data.slice2.byteArrayOffset(), data.slice1.length());
}

@Benchmark
Expand Down
Loading