Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,59 @@ public static boolean arrayEquals(
return true;
}

/**
* Returns whether the {@code length}-byte region starting at {@code (base, offset)} contains
* the given byte.
*
* <p>This performs a word-at-a-time (SWAR) scan, testing eight bytes per iteration with the
* classic "a word contains a zero byte" test after broadcasting {@code target} across a word.
* The test is exact and never reports a false positive, so only the presence of a match is
* returned, not its position; this keeps the scan independent of byte order, since locating a
* matching byte within a word would depend on endianness. It is faster than a byte-at-a-time
* scan.
*
* @param base the base object of the memory region, or {@code null} for off-heap memory
* @param offset the offset of the first byte to scan, relative to {@code base}
* @param length the number of bytes to scan; must not be negative
* @param target the byte value to search for
* @return {@code true} if any of the {@code length} bytes equals {@code target},
* {@code false} otherwise
*/
public static boolean containsByte(Object base, long offset, long length, byte target) {
long i = 0;
// Broadcast the target byte into all 8 lanes of a word.
final long pattern = (target & 0xffL) * 0x0101010101010101L;

// On platforms that require aligned access, advance byte-by-byte to an 8-byte boundary first.
if (!unaligned) {
while ((offset + i) % 8 != 0 && i < length) {
if (Platform.getByte(base, offset + i) == target) {
return true;
}
i += 1;
}
}
// Scan 8 bytes at a time. XOR maps a matching byte to 0x00; the sub-expression below is
// non-zero iff some byte of the word is zero (i.e. equal to the target). It is exact.
if (unaligned || (offset + i) % 8 == 0) {
while (i <= length - 8) {
final long word = Platform.getLong(base, offset + i) ^ pattern;
if (((word - 0x0101010101010101L) & ~word & 0x8080808080808080L) != 0) {
return true;
}
i += 8;
}
}
// Finish the remaining (unaligned tail or the whole thing on aligned-only platforms).
while (i < length) {
if (Platform.getByte(base, offset + i) == target) {
return true;
}
i += 1;
}
return false;
}

public static boolean contains(byte[] arr, byte[] sub) {
if (sub.length == 0) {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,11 @@ public boolean contains(final UTF8String substring) {
if (substring.numBytes == 0) {
return true;
}
if (substring.numBytes == 1) {
// Single-byte needle (e.g. `LIKE '%x%'` with an ASCII character): a word-at-a-time byte
// search is faster than the byte-by-byte scan below, and skips the redundant `matchAt`.
return ByteArrayMethods.containsByte(base, offset, numBytes, substring.getByte(0));
}

byte first = substring.getByte(0);
for (int i = 0; i <= numBytes - substring.numBytes; i++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,13 @@ public void contains() {
assertTrue(fromString("大千世界").contains(fromString("千世界")));
assertFalse(fromString("大千世界").contains(fromString("世千")));
assertFalse(fromString("大千世界").contains(fromString("大千世界好")));
// Single-byte needle: exercises the word-at-a-time (SWAR) fast path in `contains`.
assertFalse(EMPTY_UTF8.contains(fromString("a")));
assertTrue(fromString("abcdefghijklmnop").contains(fromString("a"))); // first byte
assertTrue(fromString("abcdefghijklmnop").contains(fromString("i"))); // past first word
assertTrue(fromString("abcdefghijklmnop").contains(fromString("p"))); // last byte
assertFalse(fromString("abcdefghijklmnop").contains(fromString("z"))); // absent
assertTrue(fromString("a").contains(fromString("a"))); // length below one word
}

@Test
Expand Down