[Mingw-users] What is the right substitute for _fseeki64 / _ftelli64 for xp targets?

Back to archive index

Keith Marshall keith****@users*****
Tue Apr 17 19:38:46 JST 2018

Hash: SHA1

I've no idea why, but for some reason the following was never
delivered to me; I discovered it in, and am reproducing it in its
entirety from, the on-line archive.

On 12/04/18 08:23, Ray Satiro wrote:
> I did a search and found that it was addressed [1] and the solution
> was at one point _lseeki64:
> __int64 _fseeki64( FILE *stream, __int64 offset, int whence ) { 
> return _lseeki64( _fileno( stream ), offset, whence ); }
> My problem with that is it's blind to the FILE buffering.

Strictly. it's the other way around: the buffering within the FILE's
__iob structure isn't aware of the lseek() activity, which occurs at a
lower level behind its back, and so may leave the __iob pointers in a
state which is inconsistent with the lower level stream pointer.

> Assume FILE *f opened rb with contents "abcdef":
> printf("%c\n", fgetc(f)); printf("%c\n", fgetc(f)); _lseeki64( 
> _fileno( f ), 4, SEEK_SET); printf("%c\n", fgetc(f));
> The third fgetc call is going to get the third character 'c' and 
> not the fifth 'e'.

Indeed, yes; the stream pointer has been relocated to the 'e', but the
__iob pointers still refer to stale data, within the __iob buffer, and
thus indicate that next available character is the 'c'.

> If I put an fflush after the lseek I will get the fifth character,

The fflush() call invalidates the __iob buffer, so forcing the next
read to load fresh data, from the relocated lower level stream pointer
position.  I might be inclined to place the fflush() *before* the call
to lseek(); however...

> but according to msdn fflush is documented to not do that and 
> explicitly retain the FILE buffer for files opened for just read 
> [2].

...the Microsoft documentation has changed, since the WinXP era;
contrast your [2] with the corresponding 2012 vintage equivalent:

Notice that the earlier documentation *does* explicitly state that a
call to fflush() on an input stream clears the (__iob) buffer, and so
the next read DTRT.  That should, thus, deliver the desired behaviour,
and indeed, does appear to do so on WinXP -- reasonably so, because we
would expect WinXP's MSVCRT.DLL implementation to behave as specified
in the earlier documentation.  That's all well and good; however, if
the behaviour in later MSVCRT.DLL versions changed, in line with the
newer documentation, then we can no longer trust fflush() to DTRT, for
any application built with an expectation for working consistently
across evolving Windows version boundaries.

It may be worth noting that, according to the ISO-C standards, the use
of fflush() on input streams results in undefined behaviour; Microsoft
may offer implementation-defined behaviour, in this case, but to rely
on it is prejudicial to portability, and in any case, we (apparently)
cannot rely on consistent implementation-defined behaviour, spanning
Windows versions.

A possibly more robust implementation of _fseeki64(), suitable for use
on any Windows version prior to Vista, may be:

  __CRT_ALIAS __int64 _fseeki64
  ( FILE *__file, __int64 __pos, int __whence )
    fseek( __file, 0, SEEK_CUR );
    return _lseeki64( _fileno(__file), __pos, __whence );

(i.e. using 32-bit fseek(), rather than fflush(), to invalidate the
__iob buffer, before the _lseeki64() call).  That should get around
any possibility of undefined behaviour, resulting from the use of
fflush() on an input stream.  It should also be suitable for use
regardless of Windows version, although it may be preferable to use
the MSVCRT.DLL implementation where it exists, (i.e. on Vista and later).

> I am thinking about using fseeko64 and ftello64. That looks ok 
> except for ftello64 I might need fflush needed prior to call on 
> write streams [3][4]. Are there any downsides / bugs in the o64 
> functions?

Yes.  It follows from the above that the need to call fflush(), (and
it is needed for fseeko64(), but I don't think it is for ftello64() as
it is currently implemented), may result in undefined behaviour when
fseeko64() is called on an input stream.

> How are you guys handling 64-bit offsets in FILE when building a 
> program for >=XP ?

Don't you mean <=XP?  It's not something I've given much thought to,
(although, perhaps I should).  The inline _fseeki64() implementation
I've proposed above certainly DTRT, on WinXP, at least for the scenario
you've described.  I'm wondering if it may be advisable to replace our
existing ftello64() and fseeko64() implementations with something
similar (untested):

  __CRT_ALIAS __off64_t ftello64( FILE *__file )
    fseek( __file, 0, SEEK_CUR );
    return (__off64_t)_telli64( _fileno(__file) );

  __CRT_ALIAS __off64_t fseeko64
  ( FILE *__file, __off64_t __pos, int __whence )
    fseek( __file, 0, SEEK_CUR );
    return (__off64_t)_lseeki64
      ( _fileno(__file), (__int64)__pos, __whence );

> Thanks
> [1]: https://sourceforge.net/p/mingw/bugs/2021/ [2]: 
> https://msdn.microsoft.com/en-us/library/9yky46tz.aspx#Anchor_2 
> [3]:
> http://mingw.5.n7.nabble.com/ftello64-returning-wrong-values-td1097.html

- -- 

Public key available from keys.gnupg.net
Key fingerprint: C19E C018 1547 DE50 E1D4 8F53 C0AD 36C6 347E 5A3F
Version: GnuPG v2.0.20 (GNU/Linux)


More information about the MinGW-Users mailing list
Back to archive index