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. */ {