InputStream can't validate? MemoryInputStream raw access?


#1

I want to discard my home-grown “Pipe” classes in exchange for using InputStream and variants. But there’s a problem… readInt(), for example, returns zero if the stream is exhausted part way. There is no meaningful way to perform error checking. In my case I am putting external data into input streams and trying to read them (for example, ID3 tags, or binary database records). Its a requirement that any code which accesses external data needs to do validation on the input. What do I do? This would be nice:

Result InputStream::readInt (int32& value);

Also, I think MemoryInputStream should provide a way to get the current location and remaining bytes (yeah this can be done with getTotalLength() - getCurrentPosition()). For example, I have a block consisting of several struct fields followed by compressed preprocessed data. I would like to read the struct fields as structured data using the memory input stream, and then decode the compressed data with my own routines using the pointer provided by some function like MemoryInputStream::getCurrentPositionPointer() + MemoryInputStream::getBytesRemaining().


#2

Yeah, those classes could certainly use some more error checking. Ideally I’d make them throw exceptions, but that’d break everyone’s code…

Adding a readInt method like you suggested makes sense, but if I add that, I should probably also add all the other variations on that for the other types, which would get a bit cluttered… Is there perhaps some kind of templated trick that could be done to get the same functionality?

Happy to add a getBytesRemaining() method to MemoryInputStream though, that’d be handy.


#3

Exceptions? No…why would you want to do that? Exceptions are good for catastrophic circumstances. For example, running out of memory. But when parsing external data, or doing some sort of validation (like in an edit box), exceptions are clunky

Well yeah! Of course, I implied that a Result-returning function should be available for every existing read###(). If you don’t mind making these new routines non-virtual in InputStream then I think you could do something like (and this is rough):

template <class T>
T ByteOrder::toLittleEndian (char const* const data) { /*...*/ }

template <class T>
Result InputStream::read (T& value)
{
    const int bytes = sizeof (T);
    char temp [bytes]];

    if (read (temp, bytes) == bytes)
    {
        value = ByteOrder::toLittleEndian  <T> (temp);
        return true;
    }

    return false;
}

Why are they virtual in InputStream to begin with?

Me personally, I wouldn’t mind just seeing two functions for every one that existed previously, like this:

    virtual int64 readInt64();
    virtual Result readInt64(int64& value);

Once you have routines that return Result you can re-implement the read### series as inline functions:

inline int64 readInt64()
{
  int64 value;
  if (readInt64 (value))
    return value;
  return 0;
}  

Well that’s the easy one…and I believe you can add it to InputStream by just returning getTotalBytes() - getCurrentPosition(). The more interesting function is getCurrentPositionPointer().