diff --git a/libguile/ChangeLog b/libguile/ChangeLog
index 5c30574..d1bf0d9 100644
--- a/libguile/ChangeLog
+++ b/libguile/ChangeLog
@@ -1,3 +1,15 @@
+2008-07-15 Neil Jerram
+
+ * srfi-4.c (scm_uniform_vector_read_x): Use scm_c_read, instead of
+ equivalent code here.
+
+ * 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).
+
2008-07-05 Ludovic Courtès
* strings.c (scm_c_symbol_length): New function.
diff --git a/libguile/ports.c b/libguile/ports.c
index b97c826..9563001 100644
--- a/libguile/ports.c
+++ b/libguile/ports.c
@@ -28,6 +28,7 @@
#include
#include
#include /* for chsize on mingw */
+#include
#include "libguile/_scm.h"
#include "libguile/async.h"
@@ -974,6 +975,8 @@ scm_fill_input (SCM port)
{
scm_t_port *pt = SCM_PTAB_ENTRY (port);
+ assert (pt->read_pos == pt->read_end);
+
if (pt->read_buf == pt->putback_buf)
{
/* finished reading put-back chars. */
@@ -1036,12 +1039,35 @@ scm_lfwrite (const char *ptr, size_t size, SCM port)
*
* Warning: Doesn't update port line and column counts! */
+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
scm_c_read (SCM port, void *buffer, size_t size)
#define FUNC_NAME "scm_c_read"
{
scm_t_port *pt;
size_t n_read = 0, n_available;
+ struct port_and_swap_buffer psb;
SCM_VALIDATE_OPINPORT (1, port);
@@ -1052,35 +1078,48 @@ scm_c_read (SCM port, void *buffer, size_t size)
if (pt->rw_random)
pt->rw_active = SCM_PORT_READ;
- if (SCM_READ_BUFFER_EMPTY_P (pt))
- {
- if (scm_fill_input (port) == EOF)
- return 0;
- }
-
- n_available = pt->read_end - pt->read_pos;
-
- while (n_available < size)
+ /* Take bytes first from the port's read buffer. */
+ if (pt->read_pos < pt->read_end)
{
+ n_available = min (size, pt->read_end - pt->read_pos);
memcpy (buffer, pt->read_pos, n_available);
buffer = (char *) buffer + n_available;
pt->read_pos += 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;
- n_available = pt->read_end - pt->read_pos;
}
- memcpy (buffer, pt->read_pos, size);
- pt->read_pos += 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 + size;
+ return n_read;
}
#undef FUNC_NAME
diff --git a/libguile/srfi-4.c b/libguile/srfi-4.c
index 7d22f8b..a01f86e 100644
--- a/libguile/srfi-4.c
+++ b/libguile/srfi-4.c
@@ -886,38 +886,11 @@ SCM_DEFINE (scm_uniform_vector_read_x, "uniform-vector-read!", 1, 3, 0,
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;
- while (remaining > 0)
- {
- if (pt->read_pos < pt->read_end)
- {
- 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;
+ remaining -= scm_c_read (port_or_fd, base + off, remaining);
+ if (remaining % sz != 0)
+ SCM_MISC_ERROR ("unexpected EOF", SCM_EOL);
+ ans -= remaining / sz;
}
else /* file descriptor. */
{