1
0
mirror of https://sourceware.org/git/glibc.git synced 2025-07-29 11:41:21 +03:00

Don't flush write buffer for ftell

[BZ #5298]
Use write pointer state along with the file offset and/or the read
pointers to get the current file position.
This commit is contained in:
Siddhesh Poyarekar
2012-09-28 18:37:23 +05:30
parent 4573c6b098
commit adb26faefe
4 changed files with 126 additions and 31 deletions

View File

@ -613,6 +613,10 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
&& (fp->_wide_data->_IO_write_base
== fp->_wide_data->_IO_write_ptr));
bool was_writing = ((fp->_wide_data->_IO_write_ptr
> fp->_wide_data->_IO_write_base)
|| _IO_in_put_mode (fp));
if (mode == 0)
{
/* XXX For wide stream with backup store it is not very
@ -644,11 +648,8 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
which assumes file_ptr() is eGptr. Anyway, since we probably
end up flushing when we close(), it doesn't make much difference.)
FIXME: simulate mem-mapped files. */
if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base
|| _IO_in_put_mode (fp))
if (_IO_switch_to_wget_mode (fp))
return WEOF;
else if (was_writing && _IO_switch_to_wget_mode (fp))
return WEOF;
if (fp->_wide_data->_IO_buf_base == NULL)
{
@ -679,29 +680,104 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
cv = fp->_codecvt;
clen = (*cv->__codecvt_do_encoding) (cv);
if (clen > 0)
if (mode != 0 || !was_writing)
{
offset -= (fp->_wide_data->_IO_read_end
- fp->_wide_data->_IO_read_ptr) * clen;
/* Adjust by readahead in external buffer. */
offset -= fp->_IO_read_end - fp->_IO_read_ptr;
if (clen > 0)
{
offset -= (fp->_wide_data->_IO_read_end
- fp->_wide_data->_IO_read_ptr) * clen;
/* Adjust by readahead in external buffer. */
offset -= fp->_IO_read_end - fp->_IO_read_ptr;
}
else
{
int nread;
flushed:
delta = (fp->_wide_data->_IO_read_ptr
- fp->_wide_data->_IO_read_base);
fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
nread = (*cv->__codecvt_do_length) (cv,
&fp->_wide_data->_IO_state,
fp->_IO_read_base,
fp->_IO_read_end, delta);
fp->_IO_read_ptr = fp->_IO_read_base + nread;
fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_read_ptr;
offset -= fp->_IO_read_end - fp->_IO_read_base - nread;
}
}
else
{
int nread;
char *new_write_ptr = fp->_IO_write_ptr;
delta = fp->_wide_data->_IO_read_ptr - fp->_wide_data->_IO_read_base;
fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
nread = (*cv->__codecvt_do_length) (cv, &fp->_wide_data->_IO_state,
fp->_IO_read_base,
fp->_IO_read_end, delta);
fp->_IO_read_ptr = fp->_IO_read_base + nread;
fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_read_ptr;
offset -= fp->_IO_read_end - fp->_IO_read_base - nread;
if (clen > 0)
offset += (fp->_wide_data->_IO_write_ptr
- fp->_wide_data->_IO_write_base) / clen;
else
{
enum __codecvt_result status;
delta = (fp->_wide_data->_IO_write_ptr
- fp->_wide_data->_IO_write_base);
const wchar_t *write_base = fp->_wide_data->_IO_write_base;
/* FIXME: This actually ends up in two iterations of conversion,
one here and the next when the buffer actually gets flushed.
It may be possible to optimize this in future so that
wdo_write identifies already converted content and does not
redo it. In any case, this is much better than having to
flush buffers for every ftell. */
do
{
/* Ugh, no point trying to avoid the flush. Just do it
and go back to how it was with the read mode. */
if (delta > 0 && new_write_ptr == fp->_IO_buf_end)
{
if (_IO_switch_to_wget_mode (fp))
return WEOF;
goto flushed;
}
const wchar_t *new_wbase = fp->_wide_data->_IO_write_base;
fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
status = (*cv->__codecvt_do_out) (cv,
&fp->_wide_data->_IO_state,
write_base,
write_base + delta,
&new_wbase,
new_write_ptr,
fp->_IO_buf_end,
&new_write_ptr);
delta -= new_wbase - write_base;
/* If there was an error, then return WEOF.
TODO: set buffer state. */
if (__builtin_expect (status == __codecvt_error, 0))
return WEOF;
}
while (delta > 0);
}
/* _IO_read_end coincides with fp._offset, so the actual file position
is fp._offset - (_IO_read_end - new_write_ptr). This is fine
even if fp._offset is not set, since fp->_IO_read_end is then at
_IO_buf_base and this adjustment is for unbuffered output. */
offset -= fp->_IO_read_end - new_write_ptr;
}
if (fp->_offset == _IO_pos_BAD)
goto dumb;
{
if (mode != 0)
goto dumb;
else
{
result = _IO_SYSSEEK (fp, 0, dir);
if (result == EOF)
return result;
fp->_offset = result;
}
}
/* Make offset absolute, assuming current pointer is file_ptr(). */
offset += fp->_offset;