views:

249

answers:

1

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; }
};
+2  A: 

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
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
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
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
@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
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