0

I have a 2884765579 bytes file. This is double checked with this function, that returns that number:

size_t GetSize() {
       const size_t current_position = mFile.tellg();
       mFile.seekg(0, std::ios::end);
       const size_t ret = mFile.tellg();
       mFile.seekg(current_position);
       return ret;
    }

I then do:

mFile.seekg(pos, std::ios::beg);
// pos = 2883426827, which is < than the file size, 2884765579

This sets the failbit. errno is not changed. What steps can I take to troubleshoot this?


I am absolutely sure that:

  • The file size is really 2884765579
  • pos is really 2884765579
  • The failbit is not set before .seekg()
  • The failbit is set right after .seekg() and no other calls are made in between
  • The file is opened with the binary flag

EDIT: in case someone runs into the same problem.. Use this code I wrote (works on windows only) and many less headaches for you:

class BinaryIFile
{
public:
    BinaryIFile(const string& path) : mPath(path), mFileSize(0) {
        mFile = open(path.c_str(), O_RDONLY | O_BINARY);

        if (mFile == -1)
            FATAL(format("Cannot open %s: %s") % path.c_str() % strerror(errno));
    }
    ~BinaryIFile() {
        if (mFile != -1)
            close(mFile);
    }

    string GetPath() const { return mPath; }
    int64 GetSize() {
        if (mFileSize)
            return mFileSize;

        const int64 current_position = _telli64(mFile);
        _lseeki64(mFile, 0, SEEK_END);
        mFileSize = _telli64(mFile);
        _lseeki64(mFile, current_position, SEEK_SET);

        return mFileSize;
    }

    int64 Read64() { return _Read<int64>(); }
    int32 Read32() { return _Read<int32>(); }
    int16 Read16() { return _Read<int16>(); }
    int8 Read8() { return _Read<int8>(); }
    float ReadFloat() { return _Read<float>(); }
    double ReadDouble() { return _Read<double>(); }

    void Skip(int64 bytes) { _lseeki64(mFile, bytes, SEEK_CUR); }
    void Seek(int64 pos) { _lseeki64(mFile, pos, SEEK_SET); }
    int64 Tell() { return _telli64(mFile); }

    template <class T>
    T Read() { return _Read<T>(); }

    void Read(char *to, size_t size) {
        const int ret = read(mFile, (void *)to, size);
        if ((int)size != ret)
            FATAL(format("Read error: attempted to read %d bytes, read() returned %d, errno: %s [we are at offset %d, file size is %d]") % size % ret % strerror(errno) % Tell() % GetSize());
    }

    template <class T>
    BinaryIFile& operator>>(T& val) { val = _Read<T>(); return *this; }

private:
    const string mPath;
    int mFile;
    int64 mFileSize;

    template <class T>
    T _Read() { T ret; if (sizeof(ret) != read(mFile, (void *)&ret, sizeof(ret))) FATAL("Read error"); return ret; }
};
Andreas Bonini
  • 44,018
  • 30
  • 122
  • 156
  • Now the real question becomes: how do you get 64 bit file positions on Windows (besides by switching to a 64 bit OS). I cannot help there, sorry. – Tronic Mar 07 '10 at 03:10
  • Yeah, that's your problem. `seekg` with one arguments takes a `streampos` argument, and `seekg` with two takes a `streamoff` argument. The latter is necessarily signed. – jamesdlin Mar 07 '10 at 03:13

1 Answers1

2

You can seekg before a given position, so pos is signed. Try it with files of size 0x7fffffff and 0x80ffffff and see if the latter triggers the problem, that's my guess.

paul
  • 36
  • 1
  • wow thanks I haven't thought of that! Do you know if there is a function for 64 bit seeks? I know there is in C, seek64 or similar (not standard), but what about C++ - Windows? – Andreas Bonini Mar 07 '10 at 03:08
  • In C++, 64-bit versions could be (and probably would be) implemented via overloads, so you could just try using 64-bit types first to see if your implementation provides them. – jamesdlin Mar 07 '10 at 03:11
  • Not an expert on Windows, but I'm guessing you're going to have to use their OS specific interface. They have pretty extensive documentation online. – paul Mar 07 '10 at 03:13
  • @jam: doesn't work. Anyway there would be no need for overloads as int would be implicitly converted to int64, so only the int64 function needs to exist – Andreas Bonini Mar 07 '10 at 03:13
  • Anyways I just decided I'll use the C library for this one and use lseek64 / _lseeki64. Thanks for the help paul! (I could also seek negatively from the end but I'd like to support files larger than 4 GB) – Andreas Bonini Mar 07 '10 at 03:14