Skip to content

Commit b4a4e91

Browse files
committed
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
1 parent afad358 commit b4a4e91

File tree

3 files changed

+41
-22
lines changed

3 files changed

+41
-22
lines changed

spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ByteArrayDataBlock.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,19 @@ class ByteArrayDataBlock implements CloseableDataBlock {
2828

2929
private final byte[] bytes;
3030

31+
private final int maxReadSize;
32+
3133
/**
3234
* Create a new {@link ByteArrayDataBlock} backed by the given bytes.
3335
* @param bytes the bytes to use
3436
*/
3537
ByteArrayDataBlock(byte... bytes) {
38+
this(bytes, -1);
39+
}
40+
41+
ByteArrayDataBlock(byte[] bytes, int maxReadSize) {
3642
this.bytes = bytes;
43+
this.maxReadSize = maxReadSize;
3744
}
3845

3946
@Override
@@ -49,6 +56,9 @@ public int read(ByteBuffer dst, long pos) throws IOException {
4956
private int read(ByteBuffer dst, int pos) {
5057
int remaining = dst.remaining();
5158
int length = Math.min(this.bytes.length - pos, remaining);
59+
if (this.maxReadSize > 0 && length > this.maxReadSize) {
60+
length = this.maxReadSize;
61+
}
5262
dst.put(this.bytes, pos, length);
5363
return length;
5464
}

spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipString.java

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -108,19 +108,15 @@ static int hash(ByteBuffer buffer, DataBlock dataBlock, long pos, int len, boole
108108
byte[] bytes = buffer.array();
109109
int hash = 0;
110110
char lastChar = 0;
111+
int codePointSize = 1;
111112
while (len > 0) {
112-
int count = readInBuffer(dataBlock, pos, buffer, len);
113-
len -= count;
114-
pos += count;
113+
int count = readInBuffer(dataBlock, pos, buffer, len, codePointSize);
115114
for (int byteIndex = 0; byteIndex < count;) {
116-
int codePointSize = getCodePointSize(bytes, byteIndex);
115+
codePointSize = getCodePointSize(bytes, byteIndex);
117116
if (!hasEnoughBytes(byteIndex, codePointSize, count)) {
118-
pos--;
119-
len++;
120117
break;
121118
}
122119
int codePoint = getCodePoint(bytes, byteIndex, codePointSize);
123-
byteIndex += codePointSize;
124120
if (codePoint <= 0xFFFF) {
125121
lastChar = (char) (codePoint & 0xFFFF);
126122
hash = 31 * hash + lastChar;
@@ -130,6 +126,10 @@ static int hash(ByteBuffer buffer, DataBlock dataBlock, long pos, int len, boole
130126
hash = 31 * hash + Character.highSurrogate(codePoint);
131127
hash = 31 * hash + Character.lowSurrogate(codePoint);
132128
}
129+
byteIndex += codePointSize;
130+
pos += codePointSize;
131+
len -= codePointSize;
132+
codePointSize = 1;
133133
}
134134
}
135135
hash = (addEndSlash && lastChar != '/') ? 31 * hash + '/' : hash;
@@ -198,19 +198,15 @@ private static int compare(ByteBuffer buffer, DataBlock dataBlock, long pos, int
198198
int maxCharSequenceLength = (!addSlash) ? charSequence.length() : charSequence.length() + 1;
199199
int result = 0;
200200
byte[] bytes = buffer.array();
201+
int codePointSize = 1;
201202
while (len > 0) {
202-
int count = readInBuffer(dataBlock, pos, buffer, len);
203-
len -= count;
204-
pos += count;
203+
int count = readInBuffer(dataBlock, pos, buffer, len, codePointSize);
205204
for (int byteIndex = 0; byteIndex < count;) {
206-
int codePointSize = getCodePointSize(bytes, byteIndex);
205+
codePointSize = getCodePointSize(bytes, byteIndex);
207206
if (!hasEnoughBytes(byteIndex, codePointSize, count)) {
208-
pos--;
209-
len++;
210207
break;
211208
}
212209
int codePoint = getCodePoint(bytes, byteIndex, codePointSize);
213-
result += codePointSize;
214210
if (codePoint <= 0xFFFF) {
215211
char ch = (char) (codePoint & 0xFFFF);
216212
if (charSequenceIndex >= maxCharSequenceLength
@@ -230,10 +226,14 @@ private static int compare(ByteBuffer buffer, DataBlock dataBlock, long pos, int
230226
return -1;
231227
}
232228
}
229+
byteIndex += codePointSize;
230+
pos += codePointSize;
231+
len -= codePointSize;
232+
result += codePointSize;
233+
codePointSize = 1;
233234
if (compareType == CompareType.STARTS_WITH && charSequenceIndex >= charSequence.length()) {
234235
return result;
235236
}
236-
byteIndex += codePointSize;
237237
}
238238
}
239239
return (charSequenceIndex >= charSequence.length()) ? result : -1;
@@ -273,16 +273,22 @@ static String readString(DataBlock data, long pos, long len) {
273273
}
274274
}
275275

276-
private static int readInBuffer(DataBlock dataBlock, long pos, ByteBuffer buffer, int maxLen) throws IOException {
276+
private static int readInBuffer(DataBlock dataBlock, long pos, ByteBuffer buffer, int maxLen, int minLen)
277+
throws IOException {
277278
buffer.clear();
278279
if (buffer.remaining() > maxLen) {
279280
buffer.limit(maxLen);
280281
}
281-
int count = dataBlock.read(buffer, pos);
282-
if (count <= 0) {
283-
throw new EOFException();
282+
int result = 0;
283+
while (result < minLen) {
284+
int count = dataBlock.read(buffer, pos);
285+
if (count <= 0) {
286+
throw new EOFException();
287+
}
288+
result += count;
289+
pos += count;
284290
}
285-
return count;
291+
return result;
286292
}
287293

288294
private static int getCodePointSize(byte[] bytes, int i) {

spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/zip/ZipStringTests.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,10 @@ void testHash(HashSourceType sourceType, String source, boolean addEndSlash, int
8686
case DATA_BLOCK -> {
8787
ByteArrayDataBlock dataBlock = new ByteArrayDataBlock(source.getBytes(StandardCharsets.UTF_8));
8888
assertThat(ZipString.hash(null, dataBlock, 0, (int) dataBlock.size(), addEndSlash)).isEqualTo(expected);
89-
89+
}
90+
case SINGLE_BYTE_READ_DATA_BLOCK -> {
91+
ByteArrayDataBlock dataBlock = new ByteArrayDataBlock(source.getBytes(StandardCharsets.UTF_8), 1);
92+
assertThat(ZipString.hash(null, dataBlock, 0, (int) dataBlock.size(), addEndSlash)).isEqualTo(expected);
9093
}
9194
}
9295
}
@@ -187,7 +190,7 @@ private AbstractIntegerAssert<?> assertStartsWith(String source, CharSequence ch
187190

188191
enum HashSourceType {
189192

190-
STRING, CHAR_SEQUENCE, DATA_BLOCK
193+
STRING, CHAR_SEQUENCE, DATA_BLOCK, SINGLE_BYTE_READ_DATA_BLOCK
191194

192195
}
193196

0 commit comments

Comments
 (0)