Depending upon how it's constructed, the available() method of a zip-file input
stream either always returns 1 until EOF is reached, or returns the total
(compressed!) size of the entry being read, regardless of how much of the entry
has been read so far. The first behavior is, in fact, enshrined in the
specification of java.util.zip.ZipInputStream:
/**
* Returns 0 after EOF has reached for the current entry data,
* otherwise always return 1.
* <p>
* Programs should not count on this method to return the actual number
* of bytes that could be read without blocking.
* ...
The specification of the InputStream.available() method only talks about
blocking, and does not explicitly say what value should be returned with
respect to EOF on a stream that (essentially) will never block, but these
behaviors are clearly not in the spirit of that specification.
-- mr@eng 2001/1/2
Name: nt126004 Date: 07/26/2002
FULL PRODUCT VERSION :
java version "1.4.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-b92)
Java HotSpot(TM) Client VM (build 1.4.0-b92, mixed mode)
FULL OPERATING SYSTEM VERSION :
Microsoft Windows 2000 [Version 5.00.2195]
No service packs.
ADDITIONAL OPERATING SYSTEMS :
This bug shouldn't depend on the operating system.
A DESCRIPTION OF THE PROBLEM :
I'm reading 1 entry from a JAR file.
InputSteam.available() returns the total number of bytes in
the uncompressed entry independent of how many bytes have
been read yet.
Thus available() exceeds the true number of available bytes,
while according to the specification it must be less or
equal to that quantity.
By tracing I came to the private inner class method
java.util.zip.ZipFile.ZipFileInputStream.available() where
I found "return size;" in the source which is the obvious
cause of the error since "size" is not changed after
initialization.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Bug is best obvious from the source code of
java.util.zip.ZipFile.ZipFileInputStream, though it can be
easliy reproduced:
1. Create a new directory
2. Create a file "data.txt" with
contents "01234567890123456789" (20 bytes) there
3. Run "jar cvf data.jar *" to create JAR
5. Place compiled class PossibleBug in the same directory
4. Run "java PossibleBug" (source code below)
EXPECTED VERSUS ACTUAL BEHAVIOR :
Expected output:
20
19
0
or (possibly) any numbers in the intervals:
1-20
1-19
0
Actual output:
20
20
20
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.io.*;
import java.net.*;
public class PossibleBug{
public static void main(String[] args) throws Exception{
URL u = new URL("jar:file:data.jar!/data.txt");
InputStream in = u.openStream();
System.out.println(in.available());
in.read();
System.out.println(in.available());
for( int j=0; j<19; j++ ) in.read();
System.out.println(in.available());
}
}
---------- END SOURCE ----------
(Review ID: 159602)
======================================================================
Name: nt126004 Date: 04/10/2003
A DESCRIPTION OF THE PROBLEM :
The anonymous class defined in ZipFile.java as an InflaterInputStream subclass incorrectly implements the available() method. It calls down to the ZipFileInputStream. It should, however, record the original size, then reduce its available count by however many bytes are inflated, and never return more than that.
i.e. the fix would be a method that looked something like this:
public int available() throws IOException {
int avail_in = super.available();
if (avail_in < inflated_remaining) {
return avail_in;
} else {
return inflated_remaining;
}
}
with inflated_remaining being the original size minus the number of bytes already inflated.
REPRODUCIBILITY :
This bug can be reproduced always.
(Review ID: 183766)
======================================================================