Update ZipString to deal with reads that do not return all data

Refine the logic in `ZipString.hash` and `ZipString.compare` to deal
with the fact a read operation may not return all available bytes.

Fixes gh-38751
This commit is contained in:
Phillip Webb
2023-12-13 13:23:46 -08:00
parent afad358047
commit b4a4e91238
3 changed files with 41 additions and 22 deletions

View File

@@ -28,12 +28,19 @@ class ByteArrayDataBlock implements CloseableDataBlock {
private final byte[] bytes;
private final int maxReadSize;
/**
* Create a new {@link ByteArrayDataBlock} backed by the given bytes.
* @param bytes the bytes to use
*/
ByteArrayDataBlock(byte... bytes) {
this(bytes, -1);
}
ByteArrayDataBlock(byte[] bytes, int maxReadSize) {
this.bytes = bytes;
this.maxReadSize = maxReadSize;
}
@Override
@@ -49,6 +56,9 @@ class ByteArrayDataBlock implements CloseableDataBlock {
private int read(ByteBuffer dst, int pos) {
int remaining = dst.remaining();
int length = Math.min(this.bytes.length - pos, remaining);
if (this.maxReadSize > 0 && length > this.maxReadSize) {
length = this.maxReadSize;
}
dst.put(this.bytes, pos, length);
return length;
}

View File

@@ -108,19 +108,15 @@ final class ZipString {
byte[] bytes = buffer.array();
int hash = 0;
char lastChar = 0;
int codePointSize = 1;
while (len > 0) {
int count = readInBuffer(dataBlock, pos, buffer, len);
len -= count;
pos += count;
int count = readInBuffer(dataBlock, pos, buffer, len, codePointSize);
for (int byteIndex = 0; byteIndex < count;) {
int codePointSize = getCodePointSize(bytes, byteIndex);
codePointSize = getCodePointSize(bytes, byteIndex);
if (!hasEnoughBytes(byteIndex, codePointSize, count)) {
pos--;
len++;
break;
}
int codePoint = getCodePoint(bytes, byteIndex, codePointSize);
byteIndex += codePointSize;
if (codePoint <= 0xFFFF) {
lastChar = (char) (codePoint & 0xFFFF);
hash = 31 * hash + lastChar;
@@ -130,6 +126,10 @@ final class ZipString {
hash = 31 * hash + Character.highSurrogate(codePoint);
hash = 31 * hash + Character.lowSurrogate(codePoint);
}
byteIndex += codePointSize;
pos += codePointSize;
len -= codePointSize;
codePointSize = 1;
}
}
hash = (addEndSlash && lastChar != '/') ? 31 * hash + '/' : hash;
@@ -198,19 +198,15 @@ final class ZipString {
int maxCharSequenceLength = (!addSlash) ? charSequence.length() : charSequence.length() + 1;
int result = 0;
byte[] bytes = buffer.array();
int codePointSize = 1;
while (len > 0) {
int count = readInBuffer(dataBlock, pos, buffer, len);
len -= count;
pos += count;
int count = readInBuffer(dataBlock, pos, buffer, len, codePointSize);
for (int byteIndex = 0; byteIndex < count;) {
int codePointSize = getCodePointSize(bytes, byteIndex);
codePointSize = getCodePointSize(bytes, byteIndex);
if (!hasEnoughBytes(byteIndex, codePointSize, count)) {
pos--;
len++;
break;
}
int codePoint = getCodePoint(bytes, byteIndex, codePointSize);
result += codePointSize;
if (codePoint <= 0xFFFF) {
char ch = (char) (codePoint & 0xFFFF);
if (charSequenceIndex >= maxCharSequenceLength
@@ -230,10 +226,14 @@ final class ZipString {
return -1;
}
}
byteIndex += codePointSize;
pos += codePointSize;
len -= codePointSize;
result += codePointSize;
codePointSize = 1;
if (compareType == CompareType.STARTS_WITH && charSequenceIndex >= charSequence.length()) {
return result;
}
byteIndex += codePointSize;
}
}
return (charSequenceIndex >= charSequence.length()) ? result : -1;
@@ -273,16 +273,22 @@ final class ZipString {
}
}
private static int readInBuffer(DataBlock dataBlock, long pos, ByteBuffer buffer, int maxLen) throws IOException {
private static int readInBuffer(DataBlock dataBlock, long pos, ByteBuffer buffer, int maxLen, int minLen)
throws IOException {
buffer.clear();
if (buffer.remaining() > maxLen) {
buffer.limit(maxLen);
}
int count = dataBlock.read(buffer, pos);
if (count <= 0) {
throw new EOFException();
int result = 0;
while (result < minLen) {
int count = dataBlock.read(buffer, pos);
if (count <= 0) {
throw new EOFException();
}
result += count;
pos += count;
}
return count;
return result;
}
private static int getCodePointSize(byte[] bytes, int i) {

View File

@@ -86,7 +86,10 @@ class ZipStringTests {
case DATA_BLOCK -> {
ByteArrayDataBlock dataBlock = new ByteArrayDataBlock(source.getBytes(StandardCharsets.UTF_8));
assertThat(ZipString.hash(null, dataBlock, 0, (int) dataBlock.size(), addEndSlash)).isEqualTo(expected);
}
case SINGLE_BYTE_READ_DATA_BLOCK -> {
ByteArrayDataBlock dataBlock = new ByteArrayDataBlock(source.getBytes(StandardCharsets.UTF_8), 1);
assertThat(ZipString.hash(null, dataBlock, 0, (int) dataBlock.size(), addEndSlash)).isEqualTo(expected);
}
}
}
@@ -187,7 +190,7 @@ class ZipStringTests {
enum HashSourceType {
STRING, CHAR_SEQUENCE, DATA_BLOCK
STRING, CHAR_SEQUENCE, DATA_BLOCK, SINGLE_BYTE_READ_DATA_BLOCK
}