4

Is it ever possible for BufferedInputStream(byte[] b, int off, int len) to return 0?

READER'S DIGEST VERSION (you can read the rest below, for context, but I think it boils down to this:) Are there InputStreams (i.e. SocketInputStream, CipherInputStream, etc. in the JDK or in commonly used libraries (i.e. Apache Commons, Guava), that don't correctly honor the contract of InputStream.read(byte[],off,len) and might return '0' even if len != 0?


(Note 1: my interest is whether it can really happen in code that uses just the JDK, or maybe a few really common Java libraries, such as Apache Commons; I am looking at the javadoc for clue, but I am also looking at the source for BufferedInputStream (Java 7, in case it matters) in case some edge case is not documented correctly -- and I'm not fully convinced one way or another, thus my question)

(Note 2: I don't mean in the trivial case, where len==0, I mean in the general case, where you pass in a non-zero array, can you ever get back 0 bytes?)

The javadoc is here and it says, in part:

This method implements the general contract of the *corresponding* [my emphasis added] read method of the InputStream class. As an additional convenience, it attempts to read as many bytes as possible by repeatedly invoking the read method of the underlying stream. This iterated read continues until one of the following conditions becomes true:

[two irrelevant conditions omitted]

- The available method of the underlying stream returns zero, indicating that further input requests would block.

And then the doc for the return value says:

Returns: the number of bytes read, or -1 if the end of the stream has been reached.

So: by my reading of this, it's possible that when you call this read function, if no data is buffered and no data is available from the underlying InputStream (say, from a stalled http transfer,) then the read method should return 0, since 0 bytes were read.

and yet… a bunch of folks who seem to know more about this than me seem to believe that this read method will always return EOF or at least one byte.

So, I looked further into InputStream, to see what the general contract of the corresponding read method of the InputStream class really means, and I found this:

If len is zero, then no bytes are read and 0 is returned; otherwise, there is an attempt to read at least one byte. If no byte is available because the stream is at end of file, the value -1 is returned; otherwise, at least one byte is read and stored into b.

So, according to the javadocs, I think this means BufferedInputStream should not return a 0. If I were looking only at the docs, I think I would be done now.

But: upon inspection, it seems to me that the implementation of BufferedInputStream doesn't really guarantee one byte or more bytes; it inherits this guarantee by depending on correct behavior of the underlying InputStream. The source for the abstract InputStream seems to get this guarantee correct (I think it can only return 0 bytes if len==0) but I don't know if this is true for all input streams in the JDK, let alone all input streams anywhere.

So. I think where I am so far is: BufferedInputStream will never return 0 unless the wrapped InputStream doesn't honor this guarantee of 1 or more bytes -- but I don't know how common this is.

1) Is my general analysis correct?

2) does anybody know of significant cases where InputStreams can return 0? (i.e. InputStreams that may return 0 with a non-zero len, so that if you wrap them in a BufferedInputStream, you need to guard against a zero return value? -- not somebody's personal, broken code, but important cases to watch out for, say in the JDK or Apache Commons or something.)

Apologies for the long question; I was doing more research as I wrote this, so the question grew.

NOTE: for context: I'm posting this question because I didn't understand a conversation I had in reference to this other question (Socket reading using BufferedInputStream) -- it might help to read that question for background.

Community
  • 1
  • 1
JVMATL
  • 2,064
  • 15
  • 25
  • Its possible (of course), but would qualify as a bug. So there is no reason to guard against it IMO. – Durandal Jan 16 '14 at 16:24
  • @Durandal Certainly, all code can have bugs, and you can go crazy trying to guard against everything not working, but if, for example, somebody were to say that every JDK from 1.2 through 1.7.25 had a bug in SocketInputStream that might return 0, then I'd say there's a pretty good reasons to guard against it in shipping code, unless you can control the JRE used - I can't imagine a client saying "oh, I see, it was a well-known JDK bug, and you didn't guard against it because it wasn't *supposed* to work that way - now I feel better about my website going down.." :) – JVMATL Jan 16 '14 at 16:31
  • Look at it from a different perspective then: What will happen *if* 0 is returned? A proper loop that terminated only on -1 (EoF) will just call read again, recovering gracefully from a spurious 0 return. If the stream continously returns 0, then it will result in an infinite loop. Nothing you *could* do against that, proper operation impossible. I think *that* would be noticed very quickly anyway, so its very unlikely such code makes it into anything released to the public. – Durandal Jan 16 '14 at 16:38
  • I tend to agree that it's hard to see what the correct 'safe' behavior is, but see http://stackoverflow.com/a/7441266/3117035 for example (comment is about javax sound). I wonder if other reader will turn up other exceptions. – JVMATL Jan 16 '14 at 18:05

1 Answers1

0

You didn’t read the spec of BufferedInputStream carefully enough. You cited:

This iterated read continues until one of the following conditions becomes true:

[two irrelevant conditions omitted]

  • The available method of the underlying stream returns zero, indicating that further input requests would block.

The BufferedInputStream will fulfill the contract of read reading at least one byte by directly delegating to the underlying stream for the first read. If the underlying stream correctly reads at least one byte for that first read, the contract has been fulfilled.

Only subsequent read attempts (the “iterated read”) are conditional, i.e. will be skipped if available returns 0 telling that another read attempt would block (again).


So the bottom line is that BufferedInputStream fulfills the contract, like all other JDK’s InputStreams — as far as I know. By the way, if you want to read an array entirely you can wrap the stream in a DataInputStream which offers a readFully method.

Community
  • 1
  • 1
Holger
  • 285,553
  • 42
  • 434
  • 765