Информация об изменениях

Сообщение Re[2]: std::ifstream, MSVC 2012, бага? от 17.02.2015 11:45

Изменено 17.02.2015 11:48 nen777w

PM>Теоретически, использование stream.tellg() — это непереносимый способ узнать размер файла. Так как tellg() возвращает лишь некое положение в потоке данных, которое в дальнейшем может быть использовано чтобы вернуться к этому положению.

PM>Единственный способ узнать размер потока в С++ используя IOStreams — это прочитать весь поток до конца, проигнорировав все данные и потом узнать кол-во прочитанных байт:

[skip]
PM>Источник: http://stackoverflow.com/a/22986486/1355844

Спасибо.

PM>Самый надежный вариант — использовать API целевой платформы, ну или boost::filesystem для большинства распространенных платформ.


Ладно. Но суть не в том что бы узнать размер файла.
Суть в этом:
std::ifstream in(path, std::ifstream::ate|std::ifstream::binary);
if(!in.good()) {
   //WTF? для файлов больше 10Gb
}

Уже давайте по чесноку что бы вообще, (хотя ИМХО оно не должно от этого зависить), приложение 64-битное.
Пошел копать...
Стрим открывается, потом ему делают:
if (!atendflag || fseek(fp, 0, SEEK_END) == 0)
        return (fp);    // no need to seek to end, or seek succeeded 

    fclose(fp);    // can't position at end
    return (0);


Так вот fseek() не вернул 0.

Копаем в fseek() последнее что там делают куда без дизассемблирование не докопаться это в long __cdecl _lseek_nolock()
/*ГДЕ: osHandle - какой то 64 битный дискриптор равен: 0x20 (думаю валидный)
       pos - 0
       mthd - 2
*/
if ((osHandle = (HANDLE)_get_osfhandle(fh)) == (HANDLE)-1)
        {
            errno = EBADF;
            _ASSERTE(("Invalid file descriptor",0));
            return -1;
        }

if ((newpos = SetFilePointer(osHandle, pos, NULL, mthd)) == -1)
                dosretval = GetLastError(); <<-- UUUUPS....
        else
                dosretval = 0;


GetLastError() вернул 87

Что означает:

ERROR_INVALID_PARAMETER
87 (0x57)
The parameter is incorrect.


SetFilePointer()

Идём дальше:
std::ifstream in(path, /*std::ifstream::ate|*/std::ifstream::binary);
      
if(!in.good()) {
    return 0;
}
in.seekg(0, std::ifstream::end);

//и еще раз
if(!in.good()) {
    return 0;
}

все ок!


Копаем:
Опа... а в конце то все дело теперь делает: __int64 __cdecl _lseeki64_nolock()

И уже знакомый код, только теперь он выглядит вот так и всю работу теперь делает SetFilePointerEx:
Параметры всё те же, только заиспользовали LARGE_INTEGER newpos;

if ((osHandle = (HANDLE)_get_osfhandle(fh)) == (HANDLE)-1)
        {
            errno = EBADF;
            _ASSERTE(("Invalid file descriptor. File possibly closed by a different thread",0));
            return( -1i64 );
        }

        if (!SetFilePointerEx( osHandle,
                               *(PLARGE_INTEGER)&pos,
                               &newpos,
                               mthd) )
        {
                _dosmaperr( GetLastError() );
                return( -1i64 );
        }


newpos.QuadPart будет содержать то что и ожидалось 10737418240 т.е. длинну нашего 10Gb файла.

Ну вот так вот... у меня пока всё.
Но судя по всему звать нужно было SetFilePointerEx() т.к. SetFilePointer() оперирует с LONG а вот SetFilePointerEx() LARGE_INTEGER

Бага?
Re[2]: std::ifstream, MSVC 2012, бага?
PM>Теоретически, использование stream.tellg() — это непереносимый способ узнать размер файла. Так как tellg() возвращает лишь некое положение в потоке данных, которое в дальнейшем может быть использовано чтобы вернуться к этому положению.

PM>Единственный способ узнать размер потока в С++ используя IOStreams — это прочитать весь поток до конца, проигнорировав все данные и потом узнать кол-во прочитанных байт:

[skip]
PM>Источник: http://stackoverflow.com/a/22986486/1355844

Спасибо.

PM>Самый надежный вариант — использовать API целевой платформы, ну или boost::filesystem для большинства распространенных платформ.


Ладно. Но суть не в том что бы узнать размер файла.
Суть в этом:
std::ifstream in(path, std::ifstream::ate|std::ifstream::binary);
if(!in.good()) {
   //WTF? для файлов больше 10Gb
}

Уже давайте по чесноку что бы вообще, (хотя ИМХО оно не должно от этого зависеть) — приложение 64-битное.
Пошел копать...
Стрим открывается, потом ему делают:
if (!atendflag || fseek(fp, 0, SEEK_END) == 0)
        return (fp);    // no need to seek to end, or seek succeeded 

    fclose(fp);    // can't position at end
    return (0);


Так вот fseek() не вернул 0.

Копаем в fseek() последнее что там делают куда без дизассемблирования не докопаться это в long __cdecl _lseek_nolock()
/*ГДЕ: osHandle - какой то 64 битный дискриптор равен: 0x20 (думаю валидный)
       pos - 0
       mthd - 2
*/
if ((osHandle = (HANDLE)_get_osfhandle(fh)) == (HANDLE)-1)
        {
            errno = EBADF;
            _ASSERTE(("Invalid file descriptor",0));
            return -1;
        }

if ((newpos = SetFilePointer(osHandle, pos, NULL, mthd)) == -1)
                dosretval = GetLastError(); <<-- UUUUPS....
        else
                dosretval = 0;


GetLastError() вернул 87

Что означает:

ERROR_INVALID_PARAMETER
87 (0x57)
The parameter is incorrect.


SetFilePointer()

Идём дальше:
std::ifstream in(path, /*std::ifstream::ate|*/std::ifstream::binary);
      
if(!in.good()) {
    return 0;
}
in.seekg(0, std::ifstream::end);

//и еще раз
if(!in.good()) {
    return 0;
}

все ок!


Копаем:
Опа... а в конце то все дело теперь делает: __int64 __cdecl _lseeki64_nolock()

И уже знакомый код, только теперь он выглядит вот так и всю работу теперь делает SetFilePointerEx:
Параметры всё те же, только заиспользовали LARGE_INTEGER newpos;

if ((osHandle = (HANDLE)_get_osfhandle(fh)) == (HANDLE)-1)
        {
            errno = EBADF;
            _ASSERTE(("Invalid file descriptor. File possibly closed by a different thread",0));
            return( -1i64 );
        }

        if (!SetFilePointerEx( osHandle,
                               *(PLARGE_INTEGER)&pos,
                               &newpos,
                               mthd) )
        {
                _dosmaperr( GetLastError() );
                return( -1i64 );
        }


newpos.QuadPart будет содержать то что и ожидалось 10737418240 т.е. длинну нашего 10Gb файла.

Ну вот так вот... у меня пока всё.
Но судя по всему звать нужно было SetFilePointerEx() т.к. SetFilePointer() оперирует с LONG а вот SetFilePointerEx() LARGE_INTEGER

Бага?