summaryrefslogtreecommitdiff
path: root/file_io
diff options
context:
space:
mode:
authorivan <ivan@13f79535-47bb-0310-9956-ffa450edef68>2017-03-02 06:16:20 +0000
committerivan <ivan@13f79535-47bb-0310-9956-ffa450edef68>2017-03-02 06:16:20 +0000
commita702a648d672aa9618ba40cd6e66c21f1ae9dbed (patch)
tree6da5a06372e8bf08d1bd4b336c820fd173165f8b /file_io
parenta1d31503ac290edbb898a095850b38c056637239 (diff)
downloadlibapr-a702a648d672aa9618ba40cd6e66c21f1ae9dbed.tar.gz
Win32: Improve apr_file_gets() performance on buffered files by not calling
apr_file_read() on each byte. The benchmark shows that this makes the function roughly 4 times faster: 4.202 ms -> 1.042 ms (I measured multiple calls for the same test file) Also see https://svn.apache.org/r65294 * file_io/win32/readwrite.c (apr_file_read): Factor out the part of this function that handles reading from buffered files ... (read_buffered): ...into this new helper. (apr_file_gets): Use the buffer directly for buffered files, the same way as in the Unix implementation. * test/testfile.c (test_gets, test_gets_buffered): Extend these tests with read-after-EOF checks. (test_gets_empty, test_gets_multiline, test_gets_small_buf, test_gets_ungetc, test_gets_buffered_big): New tests. (testfile): Run the new tests. Patch by: Evgeny Kotkov <evgeny.kotkov {at} visualsvn.com> git-svn-id: http://svn.apache.org/repos/asf/apr/apr/trunk@1785072 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'file_io')
-rw-r--r--file_io/win32/readwrite.c210
1 files changed, 145 insertions, 65 deletions
diff --git a/file_io/win32/readwrite.c b/file_io/win32/readwrite.c
index ce61b4877..f473b1831 100644
--- a/file_io/win32/readwrite.c
+++ b/file_io/win32/readwrite.c
@@ -140,6 +140,56 @@ static apr_status_t read_with_timeout(apr_file_t *file, void *buf, apr_size_t le
return rv;
}
+static apr_status_t read_buffered(apr_file_t *thefile, void *buf, apr_size_t *len)
+{
+ apr_status_t rv;
+ char *pos = (char *)buf;
+ apr_size_t blocksize;
+ apr_size_t size = *len;
+
+ if (thefile->direction == 1) {
+ rv = apr_file_flush(thefile);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ thefile->bufpos = 0;
+ thefile->direction = 0;
+ thefile->dataRead = 0;
+ }
+
+ rv = 0;
+ while (rv == 0 && size > 0) {
+ if (thefile->bufpos >= thefile->dataRead) {
+ apr_size_t read;
+ rv = read_with_timeout(thefile, thefile->buffer,
+ thefile->bufsize, &read);
+ if (read == 0) {
+ if (rv == APR_EOF)
+ thefile->eof_hit = TRUE;
+ break;
+ }
+ else {
+ thefile->dataRead = read;
+ thefile->filePtr += thefile->dataRead;
+ thefile->bufpos = 0;
+ }
+ }
+
+ blocksize = size > thefile->dataRead - thefile->bufpos ? thefile->dataRead - thefile->bufpos : size;
+ memcpy(pos, thefile->buffer + thefile->bufpos, blocksize);
+ thefile->bufpos += blocksize;
+ pos += blocksize;
+ size -= blocksize;
+ }
+
+ *len = pos - (char *)buf;
+ if (*len) {
+ rv = APR_SUCCESS;
+ }
+
+ return rv;
+}
+
APR_DECLARE(apr_status_t) apr_file_read(apr_file_t *thefile, void *buf, apr_size_t *len)
{
apr_status_t rv;
@@ -177,57 +227,10 @@ APR_DECLARE(apr_status_t) apr_file_read(apr_file_t *thefile, void *buf, apr_size
}
}
if (thefile->buffered) {
- char *pos = (char *)buf;
- apr_size_t blocksize;
- apr_size_t size = *len;
-
if (thefile->flags & APR_FOPEN_XTHREAD) {
apr_thread_mutex_lock(thefile->mutex);
}
-
- if (thefile->direction == 1) {
- rv = apr_file_flush(thefile);
- if (rv != APR_SUCCESS) {
- if (thefile->flags & APR_FOPEN_XTHREAD) {
- apr_thread_mutex_unlock(thefile->mutex);
- }
- return rv;
- }
- thefile->bufpos = 0;
- thefile->direction = 0;
- thefile->dataRead = 0;
- }
-
- rv = 0;
- while (rv == 0 && size > 0) {
- if (thefile->bufpos >= thefile->dataRead) {
- apr_size_t read;
- rv = read_with_timeout(thefile, thefile->buffer,
- thefile->bufsize, &read);
- if (read == 0) {
- if (rv == APR_EOF)
- thefile->eof_hit = TRUE;
- break;
- }
- else {
- thefile->dataRead = read;
- thefile->filePtr += thefile->dataRead;
- thefile->bufpos = 0;
- }
- }
-
- blocksize = size > thefile->dataRead - thefile->bufpos ? thefile->dataRead - thefile->bufpos : size;
- memcpy(pos, thefile->buffer + thefile->bufpos, blocksize);
- thefile->bufpos += blocksize;
- pos += blocksize;
- size -= blocksize;
- }
-
- *len = pos - (char *)buf;
- if (*len) {
- rv = APR_SUCCESS;
- }
-
+ rv = read_buffered(thefile, buf, len);
if (thefile->flags & APR_FOPEN_XTHREAD) {
apr_thread_mutex_unlock(thefile->mutex);
}
@@ -466,30 +469,107 @@ APR_DECLARE(apr_status_t) apr_file_puts(const char *str, apr_file_t *thefile)
APR_DECLARE(apr_status_t) apr_file_gets(char *str, int len, apr_file_t *thefile)
{
- apr_size_t readlen;
apr_status_t rv = APR_SUCCESS;
- int i;
-
- for (i = 0; i < len-1; i++) {
- readlen = 1;
- rv = apr_file_read(thefile, str+i, &readlen);
+ apr_size_t nbytes;
+ const char *str_start = str;
+ char *final = str + len - 1;
- if (rv != APR_SUCCESS && rv != APR_EOF)
+ /* If the file is open for xthread support, allocate and
+ * initialize the overlapped and io completion event (hEvent).
+ * Threads should NOT share an apr_file_t or its hEvent.
+ */
+ if ((thefile->flags & APR_FOPEN_XTHREAD) && !thefile->pOverlapped) {
+ thefile->pOverlapped = (OVERLAPPED*) apr_pcalloc(thefile->pool,
+ sizeof(OVERLAPPED));
+ thefile->pOverlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (!thefile->pOverlapped->hEvent) {
+ rv = apr_get_os_error();
return rv;
+ }
+ }
- if (readlen == 0) {
- /* If we have bytes, defer APR_EOF to the next call */
- if (i > 0)
- rv = APR_SUCCESS;
- break;
+ /* Handle the ungetchar if there is one. */
+ if (thefile->ungetchar != -1 && str < final) {
+ *str = thefile->ungetchar;
+ thefile->ungetchar = -1;
+ if (*str == '\n') {
+ *(++str) = '\0';
+ return APR_SUCCESS;
}
-
- if (str[i] == '\n') {
- i++; /* don't clobber this char below */
- break;
+ ++str;
+ }
+
+ /* If we have an underlying buffer, we can be *much* more efficient
+ * and skip over the read_with_timeout() calls.
+ */
+ if (thefile->buffered) {
+ if (thefile->flags & APR_FOPEN_XTHREAD) {
+ apr_thread_mutex_lock(thefile->mutex);
+ }
+
+ if (thefile->direction == 1) {
+ rv = apr_file_flush(thefile);
+ if (rv) {
+ if (thefile->flags & APR_FOPEN_XTHREAD) {
+ apr_thread_mutex_unlock(thefile->mutex);
+ }
+ return rv;
+ }
+
+ thefile->direction = 0;
+ thefile->bufpos = 0;
+ thefile->dataRead = 0;
+ }
+
+ while (str < final) { /* leave room for trailing '\0' */
+ if (thefile->bufpos < thefile->dataRead) {
+ *str = thefile->buffer[thefile->bufpos++];
+ }
+ else {
+ nbytes = 1;
+ rv = read_buffered(thefile, str, &nbytes);
+ if (rv != APR_SUCCESS) {
+ break;
+ }
+ }
+ if (*str == '\n') {
+ ++str;
+ break;
+ }
+ ++str;
+ }
+ if (thefile->flags & APR_FOPEN_XTHREAD) {
+ apr_thread_mutex_unlock(thefile->mutex);
+ }
+ }
+ else {
+ while (str < final) { /* leave room for trailing '\0' */
+ nbytes = 1;
+ rv = read_with_timeout(thefile, str, nbytes, &nbytes);
+ if (rv == APR_EOF)
+ thefile->eof_hit = TRUE;
+
+ if (rv != APR_SUCCESS) {
+ break;
+ }
+ if (*str == '\n') {
+ ++str;
+ break;
+ }
+ ++str;
}
}
- str[i] = 0;
+
+ /* We must store a terminating '\0' if we've stored any chars. We can
+ * get away with storing it if we hit an error first.
+ */
+ *str = '\0';
+ if (str > str_start) {
+ /* We stored chars; don't report EOF or any other errors;
+ * the app will find out about that on the next call.
+ */
+ return APR_SUCCESS;
+ }
return rv;
}