Patryk
Patryk

Reputation: 24130

How to implement custom std::streambuf's seekoff()?

I have the following implementation based on e.g. this question and answer

struct membuf : std::streambuf
{
  membuf(char* begin, char* end)
  {
    this->setg(begin, begin, end);
  }

protected:
  virtual pos_type seekoff(off_type off,
                           std::ios_base::seekdir dir,
                           std::ios_base::openmode which = std::ios_base::in)
  {
    std::istream::pos_type ret;
    if(dir == std::ios_base::cur)
    {
      this->gbump(off);
    }
    // something is missing here...
  }
};

I would like to use it in my methods in the following way:

  char buffer[] = { 0x01, 0x0a };
  membuf sbuf(buffer, buffer + sizeof(buffer));
  std::istream in(&sbuf);

and then call e.g. tellg() on in and get the correct result.

So far it's almost perfect - it doesn't stop at the end of the stream.

How should I upgrade this so that it works correctly?

My main motivation is to mimic std::ifstream behavior but with binary char[] fed into them in tests (instead of relying on binary files).

Upvotes: 7

Views: 2655

Answers (3)

Yankes
Yankes

Reputation: 2135

At least for GCC you will get hard crash if seekg past end pointer like:

file.seekg(10, std::ios::end);
file.read(ptr, 10);

To prevent crash you need signal out of bund error:

    virtual  std::streambuf::pos_type seekoff(
        std::streambuf::off_type off, std::ios_base::seekdir dir,
        std::ios_base::openmode) override
    {
        auto pos = gptr();
        if (dir == std::ios_base::cur)
            pos += off;
        else if (dir == std::ios_base::end)
            pos = egptr() + off;
        else if (dir == std::ios_base::beg)
            pos = eback() + off;

        //check bunds
        if (pos < eback())
            return std::streambuf::pos_type(-1);
        else if (pos > egptr())
            return std::streambuf::pos_type(-1);

        setg(eback(), pos, egptr());
        return gptr() - eback();
    }

Upvotes: 0

Jan Michael Auer
Jan Michael Auer

Reputation: 731

The accepted answer does not work for cases where the seek direction is set to either std::ios_base::beg or std::ios_base::end. To support these cases, extend the implementation by:

pos_type seekoff(off_type off,
                 std::ios_base::seekdir dir,
                 std::ios_base::openmode which = std::ios_base::in) {
  if (dir == std::ios_base::cur)
    gbump(off);
  else if (dir == std::ios_base::end)
    setg(eback(), egptr() + off, egptr());
  else if (dir == std::ios_base::beg)
    setg(eback(), eback() + off, egptr());
  return gptr() - eback();
}

Upvotes: 9

Patryk
Patryk

Reputation: 24130

It seems that I was missing the return with current position. So the final implementation of seekoff looks like:

  pos_type seekoff(off_type off,
                   std::ios_base::seekdir dir,
                   std::ios_base::openmode which = std::ios_base::in)
  {
    if (dir == std::ios_base::cur) gbump(off);

    return gptr() - eback();
  }

Upvotes: 5

Related Questions