diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ByteArrayDataBlock.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ByteArrayDataBlock.java index d1a4f7fcf9..3c1d4b4138 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ByteArrayDataBlock.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ByteArrayDataBlock.java @@ -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; } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipString.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipString.java index 6ffc4d7d68..4533f45a51 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipString.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipString.java @@ -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) { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/zip/ZipStringTests.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/zip/ZipStringTests.java index d421c25145..0716d65aac 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/zip/ZipStringTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/zip/ZipStringTests.java @@ -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 }