views:

92

answers:

2

I don't have a lot of experience with streams and buffers, but I'm having to do it for a project, and I'm stuck on an exception being thrown when the stream I'm reading is a multiple of the buffer size I've chosen. Let me show you:

My code starts by reading bufferSize (100, let's say) bytes from the stream:

numberOfBytesRead = DataReader.GetBytes(0, index, output, 0, bufferSize);

Then, I loop through a while loop:

while (numberOfBytesRead == bufferSize)
{
     BufferWriter.Write(output);
     BufferWriter.Flush();
     index += bufferSize;
     numberOfBytesRead = DataReader.GetBytes(0, index, output, 0, bufferSize);
}

... and, once we get to a non-bufferSize read, we know we've hit the end of the stream and can move on.

But if the bufferSize is 100, and the stream is 200, we'll read positions 0-99, 100-199, and then the attempt to read 200-299 errors out. I'd like it if it returned 0, but it throws an error. What I'm doing to handle that is, well, a try-catch:

catch (System.IndexOutOfRangeException)
    numberOfBytesRead = 0;

...which ends the loop, and successfully finishes the thing, but we all know I don't want to control code flow with error handling.

Is there a better (more standard?) way to handle stream reading when the stream length is unknown? This seems like a small wrinkle in a fairly reasonable strategy for reading streams, but I just don't know if I've got it wrong or what.

The specifics of this (which I've cleaned up a little bit for posting) are a MySqlDataReader hitting a LARGEBLOB column. It's working whenever the buffer is larger than the number of returned bytes, or when the number of returned bytes is not a multiple of bufferSize. Because we don't, in that case, throw an IndexOutOfRangeException.

+1  A: 

You don't have to be in the dark about the blob's size:

long blobSize = dr.GetBytes(0, 0, null, 0, 0);

...and then, before you do your read, you can just check to see that index is less than blobSize. If it isn't, you know you've capped it, and have read everything there is to read.

dnord
not sure why you moved my answer to the question - it's the answer, as far as I'm concerned.
dnord
Then rollback, it didn't read as an answer.
Henk Holterman
+2  A: 

Not sure if there is a real question here. But the code as posted is fundamentally wrong. A stream is not obligated to return the requested number of bytes. It can return less, and often does. Only when it returns 0 do you know for a fact that you've reached the end of the stream.

This allows a stream to optimize its internal buffer usage and improves overlapped I/O throughput. NetworkStream is a good example.

Hans Passant
There was, but I found an answer, and Henk moved the answer into the question, so it looks like there isn't one. My question is about the syntax of getting offset 0, offset 100, and then offset 200 in a stream that only has 200. Getting offset 200 was throwing IndexOutOfRangeException instead of returning 0. Maybe it's a bug in MySqlDataReader?
dnord
My answer was about a bug in your code.
Hans Passant
My question is about the overall strategy, which is maybe flawed. I fully expect the reader will eventually return less than the number of requested bytes - that's the only way I ever leave the while loop.
dnord