From adb26faefe47b7d34c941cbfc193ca7a5fde8e3f Mon Sep 17 00:00:00 2001 From: Siddhesh Poyarekar Date: Fri, 28 Sep 2012 18:37:23 +0530 Subject: 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. --- ChangeLog | 7 ++++ NEWS | 13 ++++--- libio/fileops.c | 21 +++++++--- libio/wfileops.c | 116 +++++++++++++++++++++++++++++++++++++++++++++---------- 4 files changed, 126 insertions(+), 31 deletions(-) diff --git a/ChangeLog b/ChangeLog index 255e0751cf..7010a8e757 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2012-09-28 Siddhesh Poyarekar + + [BZ #5298] + * libio/fileops.c (_IO_new_file_seekoff): Don't flush buffer + for ftell. Compute offsets from write pointers instead. + * libio/wfileops.c (_IO_wfile_seekoff): Likewise. + 2012-09-28 Siddhesh Poyarekar [BZ #14543] diff --git a/NEWS b/NEWS index 70ddcbe93c..b8a01efe9b 100644 --- a/NEWS +++ b/NEWS @@ -9,12 +9,13 @@ Version 2.17 * The following bugs are resolved with this release: - 1349, 3479, 5044, 5400, 6778, 6808, 9685, 9914, 10014, 10038, 11607, - 13412, 13542, 13629, 13679, 13696, 13717, 13741, 13939, 13966, 14042, - 14090, 14150, 14151, 14154, 14157, 14166, 14173, 14195, 14237, 14252, - 14283, 14298, 14303, 14307, 14328, 14331, 14336, 14337, 14347, 14349, - 14376, 14459, 14476, 14505, 14510, 14516, 14518, 14519, 14530, 14532, - 14538, 14543, 14544, 14545, 14562, 14576, 14579, 14583, 14587, 14621. + 1349, 3479, 5044, 5298, 5400, 6778, 6808, 9685, 9914, 10014, 10038, + 11607, 13412, 13542, 13629, 13679, 13696, 13717, 13741, 13939, 13966, + 14042, 14090, 14150, 14151, 14154, 14157, 14166, 14173, 14195, 14237, + 14252, 14283, 14298, 14303, 14307, 14328, 14331, 14336, 14337, 14347, + 14349, 14376, 14459, 14476, 14505, 14510, 14516, 14518, 14519, 14530, + 14532, 14538, 14543, 14544, 14545, 14562, 14576, 14579, 14583, 14587, + 14621. * Support for STT_GNU_IFUNC symbols added for s390 and s390x. Optimized versions of memcpy, memset, and memcmp added for System z10 and diff --git a/libio/fileops.c b/libio/fileops.c index e22efdec13..173091cc6c 100644 --- a/libio/fileops.c +++ b/libio/fileops.c @@ -978,6 +978,9 @@ _IO_new_file_seekoff (fp, offset, dir, mode) int must_be_exact = (fp->_IO_read_base == fp->_IO_read_end && fp->_IO_write_base == fp->_IO_write_ptr); + bool was_writing = (fp->_IO_write_ptr > fp->_IO_write_base + || _IO_in_put_mode (fp)); + if (mode == 0) dir = _IO_seek_cur, offset = 0; /* Don't move any pointers. */ @@ -988,10 +991,8 @@ _IO_new_file_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->_IO_write_ptr > fp->_IO_write_base || _IO_in_put_mode (fp)) - if (_IO_switch_to_get_mode (fp)) - return EOF; + else if (was_writing && _IO_switch_to_get_mode (fp)) + return EOF; if (fp->_IO_buf_base == NULL) { @@ -1010,7 +1011,17 @@ _IO_new_file_seekoff (fp, offset, dir, mode) { case _IO_seek_cur: /* Adjust for read-ahead (bytes is buffer). */ - offset -= fp->_IO_read_end - fp->_IO_read_ptr; + if (mode != 0 || !was_writing) + offset -= fp->_IO_read_end - fp->_IO_read_ptr; + else + { + /* _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 - fp->_IO_write_ptr; + } + if (fp->_offset == _IO_pos_BAD) { if (mode != 0) diff --git a/libio/wfileops.c b/libio/wfileops.c index d3f46b2ed1..1087e8df8d 100644 --- a/libio/wfileops.c +++ b/libio/wfileops.c @@ -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; -- cgit 1.4.1