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:
parent
4a281c91b4
commit
bed2e15fc9
2 changed files with 71 additions and 51 deletions
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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. */
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue