1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-20 19:50:24 +02:00

Make multi-byte reads on unbuffered ports more efficient.

Idea and original patch were by Ludovic Courts, this is Neil Jerram's
reworking of it.

	* libguile/srfi-4.c (scm_uniform_vector_read_x): Use scm_c_read,
	instead of equivalent code here.

	* libguile/ports.c (scm_fill_input): Add assertion that read
	buffer is empty when called.
	(port_and_swap_buffer, swap_buffer): New, for...
	(scm_c_read): Use caller's buffer for reading, to avoid making N
	1-byte low-level read calls, in the case where the port is
	unbuffered (or has a very small buffer).
This commit is contained in:
Neil Jerram 2008-09-15 00:25:25 +01:00
parent 4a281c91b4
commit bed2e15fc9
2 changed files with 71 additions and 51 deletions

View file

@ -28,6 +28,7 @@
#include <stdio.h> #include <stdio.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> /* for chsize on mingw */ #include <fcntl.h> /* for chsize on mingw */
#include <assert.h>
#include "libguile/_scm.h" #include "libguile/_scm.h"
#include "libguile/async.h" #include "libguile/async.h"
@ -974,6 +975,8 @@ scm_fill_input (SCM port)
{ {
scm_t_port *pt = SCM_PTAB_ENTRY (port); scm_t_port *pt = SCM_PTAB_ENTRY (port);
assert (pt->read_pos == pt->read_end);
if (pt->read_buf == pt->putback_buf) if (pt->read_buf == pt->putback_buf)
{ {
/* finished reading put-back chars. */ /* finished reading put-back chars. */
@ -1036,12 +1039,39 @@ scm_lfwrite (const char *ptr, size_t size, SCM port)
* *
* Warning: Doesn't update port line and column counts! */ * Warning: Doesn't update port line and column counts! */
/* This structure, and the following swap_buffer function, are used
for temporarily swapping a port's own read buffer, and the buffer
that the caller of scm_c_read provides. */
struct port_and_swap_buffer
{
scm_t_port *pt;
unsigned char *buffer;
size_t size;
};
static void
swap_buffer (void *data)
{
struct port_and_swap_buffer *psb = (struct port_and_swap_buffer *) data;
unsigned char *old_buf = psb->pt->read_buf;
size_t old_size = psb->pt->read_buf_size;
/* Make the port use (buffer, size) from the struct. */
psb->pt->read_pos = psb->pt->read_buf = psb->pt->read_end = psb->buffer;
psb->pt->read_buf_size = psb->size;
/* Save the port's old (buffer, size) in the struct. */
psb->buffer = old_buf;
psb->size = old_size;
}
size_t size_t
scm_c_read (SCM port, void *buffer, size_t size) scm_c_read (SCM port, void *buffer, size_t size)
#define FUNC_NAME "scm_c_read" #define FUNC_NAME "scm_c_read"
{ {
scm_t_port *pt; scm_t_port *pt;
size_t n_read = 0, n_available; size_t n_read = 0, n_available;
struct port_and_swap_buffer psb;
SCM_VALIDATE_OPINPORT (1, port); SCM_VALIDATE_OPINPORT (1, port);
@ -1052,35 +1082,52 @@ scm_c_read (SCM port, void *buffer, size_t size)
if (pt->rw_random) if (pt->rw_random)
pt->rw_active = SCM_PORT_READ; pt->rw_active = SCM_PORT_READ;
if (SCM_READ_BUFFER_EMPTY_P (pt)) /* Take bytes first from the port's read buffer. */
{ if (pt->read_pos < pt->read_end)
if (scm_fill_input (port) == EOF)
return 0;
}
n_available = pt->read_end - pt->read_pos;
while (n_available < size)
{ {
n_available = min (size, pt->read_end - pt->read_pos);
memcpy (buffer, pt->read_pos, n_available); memcpy (buffer, pt->read_pos, n_available);
buffer = (char *) buffer + n_available; buffer = (char *) buffer + n_available;
pt->read_pos += n_available; pt->read_pos += n_available;
n_read += n_available; n_read += n_available;
if (SCM_READ_BUFFER_EMPTY_P (pt))
{
if (scm_fill_input (port) == EOF)
return n_read;
}
size -= n_available; size -= n_available;
n_available = pt->read_end - pt->read_pos;
} }
memcpy (buffer, pt->read_pos, size); /* Avoid the scm_dynwind_* costs if we now have enough data. */
pt->read_pos += size; if (size == 0)
return n_read;
return n_read + size; /* Now we will call scm_fill_input repeatedly until we have read the
requested number of bytes. (Note that a single scm_fill_input
call does not guarantee to fill the whole of the port's read
buffer.) For these calls, since we already have a buffer here to
read into, we bypass the port's own read buffer (if it has one),
by saving it off and modifying the port structure to point to our
own buffer.
We need to make sure that the port's normal buffer is reinstated
in case one of the scm_fill_input () calls throws an exception;
we use the scm_dynwind_* API to achieve that. */
psb.pt = pt;
psb.buffer = buffer;
psb.size = size;
scm_dynwind_begin (SCM_F_DYNWIND_REWINDABLE);
scm_dynwind_rewind_handler (swap_buffer, &psb, SCM_F_WIND_EXPLICITLY);
scm_dynwind_unwind_handler (swap_buffer, &psb, SCM_F_WIND_EXPLICITLY);
/* Call scm_fill_input until we have all the bytes that we need, or
we hit EOF. */
while (pt->read_buf_size && (scm_fill_input (port) != EOF))
{
pt->read_buf_size -= (pt->read_end - pt->read_pos);
pt->read_pos = pt->read_buf = pt->read_end;
}
n_read += pt->read_buf - (unsigned char *) buffer;
/* Reinstate the port's normal buffer. */
scm_dynwind_end ();
return n_read;
} }
#undef FUNC_NAME #undef FUNC_NAME

View file

@ -886,38 +886,11 @@ SCM_DEFINE (scm_uniform_vector_read_x, "uniform-vector-read!", 1, 3, 0,
if (SCM_NIMP (port_or_fd)) if (SCM_NIMP (port_or_fd))
{ {
scm_t_port *pt = SCM_PTAB_ENTRY (port_or_fd);
if (pt->rw_active == SCM_PORT_WRITE)
scm_flush (port_or_fd);
ans = cend - cstart; ans = cend - cstart;
while (remaining > 0) remaining -= scm_c_read (port_or_fd, base + off, remaining);
{ if (remaining % sz != 0)
if (pt->read_pos < pt->read_end) SCM_MISC_ERROR ("unexpected EOF", SCM_EOL);
{ ans -= remaining / sz;
size_t to_copy = min (pt->read_end - pt->read_pos,
remaining);
memcpy (base + off, pt->read_pos, to_copy);
pt->read_pos += to_copy;
remaining -= to_copy;
off += to_copy;
}
else
{
if (scm_fill_input (port_or_fd) == EOF)
{
if (remaining % sz != 0)
SCM_MISC_ERROR ("unexpected EOF", SCM_EOL);
ans -= remaining / sz;
break;
}
}
}
if (pt->rw_random)
pt->rw_active = SCM_PORT_READ;
} }
else /* file descriptor. */ else /* file descriptor. */
{ {