From 299f24790ff5eb09f7edb1ee69027ed97e5d7213 Mon Sep 17 00:00:00 2001 From: Markus Scherer Date: Mon, 20 Oct 2014 20:45:22 +0000 Subject: [PATCH] ICU-11341 better ICUBinary.getByteBufferFromInputStream(): minimize number of memory allocations, allocate available() bytes on good JDK, do not rely on available() for finding the end of the stream X-SVN-Rev: 36681 --- .../core/src/com/ibm/icu/impl/ICUBinary.java | 58 +++++++++++++------ 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/ICUBinary.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/ICUBinary.java index faec76a3d48..79a5d456b48 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/ICUBinary.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/ICUBinary.java @@ -519,32 +519,52 @@ public final class ICUBinary { */ public static ByteBuffer getByteBufferFromInputStream(InputStream is) throws IOException { try { + // is.available() may return 0, or 1, or the total number of bytes in the stream, + // or some other number. + // Do not try to use is.available() == 0 to find the end of the stream! + byte[] bytes; int avail = is.available(); - byte[] bytes = new byte[avail]; - readFully(is, bytes, 0, avail); - while((avail = is.available()) != 0) { - // TODO Java 6 replace new byte[] and arraycopy(): byte[] newBytes = Arrays.copyOf(bytes, bytes.length + avail); - byte[] newBytes = new byte[bytes.length + avail]; - System.arraycopy(bytes, 0, newBytes, 0, bytes.length); - readFully(is, newBytes, bytes.length, avail); - bytes = newBytes; + if (avail > 32) { + // There are more bytes available than just the ICU data header length. + // With luck, it is the total number of bytes. + bytes = new byte[avail]; + } else { + bytes = new byte[128]; // empty .res files are even smaller + } + // Call is.read(...) until one returns a negative value. + int length = 0; + for(;;) { + if (length < bytes.length) { + int numRead = is.read(bytes, length, bytes.length - length); + if (numRead < 0) { + break; // end of stream + } + length += numRead; + } else { + // See if we are at the end of the stream before we grow the array. + int nextByte = is.read(); + if (nextByte < 0) { + break; + } + int capacity = 2 * bytes.length; + if (capacity < 128) { + capacity = 128; + } else if (capacity < 0x4000) { + capacity *= 2; // Grow faster until we reach 16kB. + } + // TODO Java 6 replace new byte[] and arraycopy(): bytes = Arrays.copyOf(bytes, capacity); + byte[] newBytes = new byte[capacity]; + System.arraycopy(bytes, 0, newBytes, 0, length); + bytes = newBytes; + bytes[length++] = (byte) nextByte; + } } - return ByteBuffer.wrap(bytes); + return ByteBuffer.wrap(bytes, 0, length); } finally { is.close(); } } - private static void readFully(InputStream is, byte[] bytes, int offset, int avail) - throws IOException { - while (avail > 0) { - int numRead = is.read(bytes, offset, avail); - assert numRead > 0; - offset += numRead; - avail -= numRead; - } - } - /** * Returns a VersionInfo for the bytes in the compact version integer. */ -- 2.40.0