1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-01 04:10:18 +02:00

Port read/write functions take bytevectors

This will allow better Scheme integration for ports.

* libguile/ports.h (scm_t_port_buffer): Change "holder" member to be a
  bytevector defined to have "buf" as its starting point.
  (scm_t_ptob_descriptor): Change read and write functions to take
  bytevectors as arguments and to return the number of octets read or
  written.
  (scm_make_port_type): Adapt accordingly.
  (scm_c_read_bytes, scm_c_write_bytes): New functions that take
  bytevectors.
* libguile/ports.c (scm_make_port_type): Adapt to read/write function
  prototype change.
  (scm_c_make_port_buffer): Arrange to populate the "bytevector" field.
  (scm_i_read_bytes_unlocked): New function.
  (scm_i_read_unlocked): Use scm_i_read_bytes_unlocked.
  (scm_c_read_bytes_unlocked): New function.
  (scm_c_read_unlocked): Update comment, and always go through the
  buffer.
  (scm_c_read_bytes): New function.
  (scm_flush_unlocked): Use scm_i_write_unlocked instead of the port's
  write function.
  (scm_i_write_bytes_unlocked): New function.
  (scm_i_write_unlocked): Use scm_i_write_bytes_unlocked.
  (scm_c_write_bytes_unlocked): New function.
  (scm_c_write_unlocked): Always write through the buffer.
  (scm_c_write_bytes): New function.
  (scm_truncate_file): Remove unused variable.
  (void_port_read, void_port_write): Adapt to read/write prototype
  change.
* libguile/fports.c (fport_read, fport_write):
* libguile/r6rs-ports.c (bytevector_input_port_read)
  (custom_binary_input_port_read, bytevector_output_port_write)
  (custom_binary_output_port_write, transcoded_port_write)
  (transcoded_port_read): Adapt to read/write prototype
  change.
  (scm_get_bytevector_n, scm_get_bytevector_n_x)
  (scm_get_bytevector_all): Use scm_c_read_bytes.
  (scm_put_bytevector): Use scm_c_write_bytes.
* libguile/strports.c (string_port_read, string_port_write):
* libguile/vports.c (soft_port_write, soft_port_read): Adapt to
  read/write prototype change.
* test-suite/standalone/test-scm-c-read.c (custom_port_read): Fix for
  read API change.
This commit is contained in:
Andy Wingo 2016-04-11 18:40:03 +02:00
parent 55fb8f4e7e
commit f7027a8b88
7 changed files with 372 additions and 240 deletions

View file

@ -578,29 +578,29 @@ fport_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
/* fill a port's read-buffer with a single read. returns the first /* fill a port's read-buffer with a single read. returns the first
char or EOF if end of file. */ char or EOF if end of file. */
static void static size_t
fport_read (SCM port, scm_t_port_buffer *dst) fport_read (SCM port, SCM dst, size_t start, size_t count)
{ {
long count; long res;
scm_t_fport *fp = SCM_FSTREAM (port); scm_t_fport *fp = SCM_FSTREAM (port);
scm_t_uint8 *ptr = dst->buf + dst->end; signed char *ptr = SCM_BYTEVECTOR_CONTENTS (dst) + start;
size_t size = dst->size - dst->end;
SCM_SYSCALL (count = read (fp->fdes, ptr, size)); SCM_SYSCALL (res = read (fp->fdes, ptr, count));
if (count == -1) if (res == -1)
scm_syserror ("fport_read"); scm_syserror ("fport_read");
dst->end += count; return res;
} }
static void static size_t
fport_write (SCM port, scm_t_port_buffer *src) fport_write (SCM port, SCM src, size_t start, size_t count)
{ {
int fd = SCM_FPORT_FDES (port); int fd = SCM_FPORT_FDES (port);
scm_t_uint8 *ptr = src->buf + src->cur; signed char *ptr = SCM_BYTEVECTOR_CONTENTS (src) + start;
size_t size = src->end - src->cur;
if (full_write (fd, ptr, size) < size) if (full_write (fd, ptr, count) < count)
scm_syserror ("fport_write"); scm_syserror ("fport_write");
return count;
} }
static scm_t_off static scm_t_off

View file

@ -229,8 +229,10 @@ static const size_t default_buffer_size = 1024;
scm_t_bits scm_t_bits
scm_make_port_type (char *name, scm_make_port_type (char *name,
void (*read) (SCM port, scm_t_port_buffer *buf), size_t (*read) (SCM port, SCM dst, size_t start,
void (*write) (SCM port, scm_t_port_buffer *buf)) size_t count),
size_t (*write) (SCM port, SCM src, size_t start,
size_t count))
{ {
scm_t_ptob_descriptor *desc; scm_t_ptob_descriptor *desc;
long ptobnum; long ptobnum;
@ -527,7 +529,8 @@ scm_c_make_port_buffer (size_t size)
scm_t_port_buffer *ret = scm_gc_typed_calloc (scm_t_port_buffer); scm_t_port_buffer *ret = scm_gc_typed_calloc (scm_t_port_buffer);
ret->size = size; ret->size = size;
ret->buf = scm_gc_malloc_pointerless (ret->size, "port buffer"); ret->bytevector = scm_c_make_bytevector (size);
ret->buf = (scm_t_uint8 *) SCM_BYTEVECTOR_CONTENTS (ret->bytevector);
return ret; return ret;
} }
@ -1432,33 +1435,51 @@ scm_peek_byte_or_eof (SCM port)
return ret; return ret;
} }
static size_t
scm_i_read_bytes_unlocked (SCM port, SCM dst, size_t start, size_t count)
{
size_t filled;
scm_t_ptob_descriptor *ptob = SCM_PORT_DESCRIPTOR (port);
assert (count <= SCM_BYTEVECTOR_LENGTH (dst));
assert (start + count <= SCM_BYTEVECTOR_LENGTH (dst));
filled = ptob->read (port, dst, start, count);
assert (filled <= count);
return filled;
}
/* scm_i_read_unlocked is used internally to add bytes to the given port /* scm_i_read_unlocked is used internally to add bytes to the given port
buffer. If the number of available bytes in the buffer does not buffer. If the number of available bytes in the buffer does not
increase after a call to scm_i_read_unlocked, that indicates EOF. */ increase after a call to scm_i_read_unlocked, that indicates EOF. */
static void static void
scm_i_read_unlocked (SCM port, scm_t_port_buffer *buf) scm_i_read_unlocked (SCM port, scm_t_port_buffer *buf)
{ {
size_t prev_end = buf->end; size_t count;
assert (buf->end < buf->size); assert (buf->end < buf->size);
SCM_PORT_DESCRIPTOR (port)->read (port, buf); count = scm_i_read_bytes_unlocked (port, buf->bytevector, buf->end,
buf->has_eof = buf->end == prev_end; buf->size - buf->end);
buf->end += count;
buf->has_eof = count == 0;
} }
/* scm_c_read /* Used by an application to read arbitrary number of bytes from an SCM
* port. Same semantics as libc read, except that scm_c_read_bytes only
* Used by an application to read arbitrary number of bytes from an SCM returns less than SIZE bytes if at end-of-file.
* port. Same semantics as libc read, except that scm_c_read only
* returns less than SIZE bytes if at end-of-file. Warning: Doesn't update port line and column counts! */
* static size_t
* Warning: Doesn't update port line and column counts! */ scm_c_read_bytes_unlocked (SCM port, SCM dst, size_t start, size_t count)
size_t #define FUNC_NAME "scm_c_read_bytes"
scm_c_read_unlocked (SCM port, void *buffer, size_t size)
#define FUNC_NAME "scm_c_read"
{ {
size_t to_read = count;
scm_t_port *pt; scm_t_port *pt;
scm_t_port_buffer *read_buf; scm_t_port_buffer *read_buf;
scm_t_port_buffer dst_buf = { buffer, 0, 0, size, 0, NULL }; signed char *dst_ptr = SCM_BYTEVECTOR_CONTENTS (dst) + start;
SCM_VALIDATE_OPINPORT (1, port); SCM_VALIDATE_OPINPORT (1, port);
@ -1475,46 +1496,115 @@ scm_c_read_unlocked (SCM port, void *buffer, size_t size)
/* Take bytes first from the port's read buffer. */ /* Take bytes first from the port's read buffer. */
if (read_buf->cur < read_buf->end) if (read_buf->cur < read_buf->end)
{ {
size_t to_read = dst_buf.size - dst_buf.end; size_t to_copy = count;
to_read = min (to_read, read_buf->end - read_buf->cur); to_copy = min (to_copy, read_buf->end - read_buf->cur);
memcpy (dst_buf.buf + dst_buf.end, read_buf->buf + read_buf->cur, memcpy (dst_ptr, read_buf->buf + read_buf->cur, to_copy);
to_read); dst_ptr += to_copy;
dst_buf.end += to_read; to_read -= to_copy;
read_buf->cur += to_read; read_buf->cur += to_copy;
} }
while (dst_buf.end < dst_buf.size) while (to_read)
/* If the read buffering is larger than our read size, buffer the {
read. Otherwise read into our buffer directly. */ /* If the read is smaller than the buffering on the read side of
if (dst_buf.size - dst_buf.end < pt->read_buffering) this port, then go through the buffer. Otherwise fill our
{ buffer directly. */
size_t to_read = dst_buf.size - dst_buf.end; if (to_read < pt->read_buffering)
read_buf = scm_fill_input_unlocked (port); {
if (to_read > read_buf->end - read_buf->cur) size_t to_copy = to_read;
to_read = read_buf->end - read_buf->cur; read_buf = scm_fill_input_unlocked (port);
if (to_read == 0) to_copy = min (to_copy, read_buf->end - read_buf->cur);
{ memcpy (dst_ptr, read_buf->buf + read_buf->cur, to_copy);
/* Consider that we've read off this EOF. */ if (to_copy == 0)
read_buf->has_eof = 0; {
break; /* Consider that we've read off this EOF. */
} read_buf->has_eof = 0;
memcpy (dst_buf.buf + dst_buf.end, break;
read_buf->buf + read_buf->cur, }
to_read); dst_ptr += to_copy;
read_buf->cur += to_read; to_read -= to_copy;
dst_buf.end += to_read; read_buf->cur += to_copy;
} }
else else
{ {
scm_i_read_unlocked (port, &dst_buf); size_t filled;
if (dst_buf.has_eof)
break;
}
return dst_buf.end; filled = scm_i_read_bytes_unlocked (port, dst,
start + count - to_read,
to_read);
if (filled == 0)
break;
to_read -= filled;
dst_ptr += filled;
}
}
return count - to_read;
} }
#undef FUNC_NAME #undef FUNC_NAME
/* Like scm_c_read_bytes, but always proxies reads through the port's
read buffer. Used by an application when it wants to read into a
memory chunk that's not owned by Guile's GC. */
size_t
scm_c_read_unlocked (SCM port, void *buffer, size_t size)
#define FUNC_NAME "scm_c_read"
{
size_t copied = 0;
scm_t_port *pt;
scm_t_port_buffer *read_buf;
scm_t_uint8 *dst = buffer;
SCM_VALIDATE_OPINPORT (1, port);
pt = SCM_PTAB_ENTRY (port);
read_buf = pt->read_buf;
if (pt->rw_random)
{
if (pt->rw_active == SCM_PORT_WRITE)
scm_flush_unlocked (port);
pt->rw_active = SCM_PORT_READ;
}
while (copied < size)
{
read_buf = scm_fill_input_unlocked (port);
/* Take bytes first from the port's read buffer. */
if (read_buf->cur < read_buf->end)
{
size_t to_copy = size - copied;
to_copy = min (to_copy, read_buf->end - read_buf->cur);
memcpy (dst + copied, read_buf->buf + read_buf->cur, to_copy);
copied += to_copy;
read_buf->cur += to_copy;
}
else
{
/* Consider that we've read off this EOF. */
read_buf->has_eof = 0;
break;
}
}
return copied;
}
#undef FUNC_NAME
size_t
scm_c_read_bytes (SCM port, SCM dst, size_t start, size_t count)
{
scm_i_pthread_mutex_t *lock;
size_t ret;
scm_c_lock_port (port, &lock);
ret = scm_c_read_bytes_unlocked (port, dst, start, count);
if (lock)
scm_i_pthread_mutex_unlock (lock);
return ret;
}
size_t size_t
scm_c_read (SCM port, void *buffer, size_t size) scm_c_read (SCM port, void *buffer, size_t size)
{ {
@ -2447,13 +2537,14 @@ SCM_DEFINE (scm_force_output, "force-output", 0, 1, 0,
} }
#undef FUNC_NAME #undef FUNC_NAME
static void scm_i_write_unlocked (SCM port, scm_t_port_buffer *src);
void void
scm_flush_unlocked (SCM port) scm_flush_unlocked (SCM port)
{ {
scm_t_port_buffer *buf = SCM_PTAB_ENTRY (port)->write_buf; scm_t_port_buffer *buf = SCM_PTAB_ENTRY (port)->write_buf;
if (buf->cur < buf->end) if (buf->cur < buf->end)
SCM_PORT_DESCRIPTOR (port)->write (port, buf); scm_i_write_unlocked (port, buf);
buf->cur = buf->end = 0;
SCM_PTAB_ENTRY (port)->rw_active = SCM_PORT_NEITHER; SCM_PTAB_ENTRY (port)->rw_active = SCM_PORT_NEITHER;
} }
@ -2520,27 +2611,56 @@ scm_puts (const char *s, SCM port)
scm_i_pthread_mutex_unlock (lock); scm_i_pthread_mutex_unlock (lock);
} }
/* scm_c_write static void
* scm_i_write_bytes_unlocked (SCM port, SCM src, size_t start, size_t count)
* Used by an application to write arbitrary number of bytes to an SCM {
* port. Similar semantics as libc write. However, unlike libc size_t written;
* write, scm_c_write writes the requested number of bytes and has no scm_t_ptob_descriptor *ptob = SCM_PORT_DESCRIPTOR (port);
* return value.
* assert (count <= SCM_BYTEVECTOR_LENGTH (src));
* Warning: Doesn't update port line and column counts! assert (start + count <= SCM_BYTEVECTOR_LENGTH (src));
*/
void written = ptob->write (port, src, start, count);
scm_c_write_unlocked (SCM port, const void *ptr, size_t size)
#define FUNC_NAME "scm_c_write" /* FIXME: Allow short writes? */
assert (written == count);
}
static void
scm_i_write_unlocked (SCM port, scm_t_port_buffer *src)
{
size_t start, count;
assert (src->cur < src->end);
assert (src->end <= src->size);
/* Update cursors before attempting to write, assuming that I/O errors
are sticky. That way if the write throws an error, causing the
computation to abort, and possibly causing the port to be collected
by GC when it's open, any subsequent close-port / force-output
won't signal *another* error. */
start = src->cur;
count = src->end - src->cur;
src->cur = src->end = 0;
scm_i_write_bytes_unlocked (port, src->bytevector, start, count);
}
/* Used by an application to write arbitrary number of bytes to an SCM
port. Similar semantics as libc write. However, unlike libc write,
scm_c_write writes the requested number of bytes.
Warning: Doesn't update port line and column counts! */
static size_t
scm_c_write_bytes_unlocked (SCM port, SCM src, size_t start, size_t count)
#define FUNC_NAME "scm_c_write_bytes"
{ {
scm_t_port *pt; scm_t_port *pt;
scm_t_port_buffer *write_buf; scm_t_port_buffer *write_buf;
scm_t_ptob_descriptor *ptob;
SCM_VALIDATE_OPOUTPORT (1, port); SCM_VALIDATE_OPOUTPORT (1, port);
pt = SCM_PTAB_ENTRY (port); pt = SCM_PTAB_ENTRY (port);
ptob = SCM_PORT_DESCRIPTOR (port);
write_buf = pt->write_buf; write_buf = pt->write_buf;
if (pt->rw_random) if (pt->rw_random)
@ -2550,7 +2670,7 @@ scm_c_write_unlocked (SCM port, const void *ptr, size_t size)
pt->rw_active = SCM_PORT_WRITE; pt->rw_active = SCM_PORT_WRITE;
} }
if (size < write_buf->size) if (count < write_buf->size)
{ {
/* Make it so that write_buf->end is only nonzero if there are /* Make it so that write_buf->end is only nonzero if there are
buffered bytes already. */ buffered bytes already. */
@ -2562,36 +2682,72 @@ scm_c_write_unlocked (SCM port, const void *ptr, size_t size)
flush it beforehand. Otherwise it could be that the buffer is flush it beforehand. Otherwise it could be that the buffer is
full after filling it with the new data; if that's the case, we full after filling it with the new data; if that's the case, we
flush then instead. */ flush then instead. */
if (write_buf->end + size > write_buf->size) if (write_buf->end + count > write_buf->size)
{ scm_i_write_unlocked (port, write_buf);
ptob->write (port, write_buf);
write_buf->cur = write_buf->end = 0;
}
memcpy (write_buf->buf + write_buf->end, ptr, size); memcpy (write_buf->buf + write_buf->end,
write_buf->end += size; SCM_BYTEVECTOR_CONTENTS (src) + start,
count);
write_buf->end += count;
if (write_buf->end == write_buf->size) if (write_buf->end == write_buf->size)
{ scm_i_write_unlocked (port, write_buf);
ptob->write (port, write_buf);
write_buf->cur = write_buf->end = 0;
}
} }
else else
{ {
/* Our write would overflow the buffer. Flush buffered bytes (if /* Our write would overflow the buffer. Flush buffered bytes (if
needed), then write our bytes with just one syscall. */ needed), then write our bytes with just one syscall. */
size_t written;
scm_t_port_buffer ad_hoc_buf =
{ (scm_t_uint8 *) ptr, 0, size, size, 0, NULL };
if (write_buf->cur < write_buf->end) if (write_buf->cur < write_buf->end)
{ scm_i_write_unlocked (port, write_buf);
ptob->write (port, write_buf);
write_buf->cur = write_buf->end = 0;
}
ptob->write (port, &ad_hoc_buf); written = SCM_PORT_DESCRIPTOR (port)->write (port, src, start, count);
assert (written == count);
}
return count;
}
#undef FUNC_NAME
/* Like scm_c_write_bytes, but always writes through the write buffer.
Used when an application wants to write bytes stored in an area not
managed by GC. */
void
scm_c_write_unlocked (SCM port, const void *ptr, size_t size)
#define FUNC_NAME "scm_c_write"
{
scm_t_port *pt;
scm_t_port_buffer *write_buf;
size_t written = 0;
const scm_t_uint8 *src = ptr;
SCM_VALIDATE_OPOUTPORT (1, port);
pt = SCM_PTAB_ENTRY (port);
write_buf = pt->write_buf;
if (pt->rw_random)
{
if (pt->rw_active == SCM_PORT_READ)
scm_end_input_unlocked (port);
pt->rw_active = SCM_PORT_WRITE;
}
while (written < size)
{
size_t to_write = write_buf->size - write_buf->end;
if (to_write > size - written)
to_write = size - written;
memcpy (write_buf->buf + write_buf->end, src, to_write);
write_buf->end += to_write;
written += to_write;
src += to_write;
if (write_buf->end == write_buf->size)
scm_i_write_unlocked (port, write_buf);
} }
} }
#undef FUNC_NAME #undef FUNC_NAME
@ -2606,6 +2762,16 @@ scm_c_write (SCM port, const void *ptr, size_t size)
scm_i_pthread_mutex_unlock (lock); scm_i_pthread_mutex_unlock (lock);
} }
void
scm_c_write_bytes (SCM port, SCM src, size_t start, size_t count)
{
scm_i_pthread_mutex_t *lock;
scm_c_lock_port (port, &lock);
scm_c_write_bytes_unlocked (port, src, start, count);
if (lock)
scm_i_pthread_mutex_unlock (lock);
}
/* scm_lfwrite /* scm_lfwrite
* *
* This function differs from scm_c_write; it updates port line and * This function differs from scm_c_write; it updates port line and
@ -2846,7 +3012,6 @@ SCM_DEFINE (scm_truncate_file, "truncate-file", 1, 1, 0,
else if (SCM_OPOUTPORTP (object)) else if (SCM_OPOUTPORTP (object))
{ {
off_t_or_off64_t c_length = scm_to_off_t_or_off64_t (length); off_t_or_off64_t c_length = scm_to_off_t_or_off64_t (length);
scm_t_port *pt = SCM_PTAB_ENTRY (object);
scm_t_ptob_descriptor *ptob = SCM_PORT_DESCRIPTOR (object); scm_t_ptob_descriptor *ptob = SCM_PORT_DESCRIPTOR (object);
if (!ptob->truncate) if (!ptob->truncate)
@ -3082,14 +3247,16 @@ SCM_DEFINE (scm_flush_all_ports, "flush-all-ports", 0, 0, 0,
scm_t_bits scm_tc16_void_port = 0; scm_t_bits scm_tc16_void_port = 0;
static void static size_t
void_port_read (SCM port, scm_t_port_buffer *buf) void_port_read (SCM port, SCM dst, size_t start, size_t count)
{ {
return 0;
} }
static void static size_t
void_port_write (SCM port, scm_t_port_buffer *buf) void_port_write (SCM port, SCM src, size_t start, size_t count)
{ {
return count;
} }
static SCM static SCM

View file

@ -87,8 +87,8 @@ typedef struct
peek-u8 should still return EOF. */ peek-u8 should still return EOF. */
int has_eof; int has_eof;
/* Heap object that keeps `buf' alive. */ /* Bytevector whose contents are [BUF, BUF + SIZE). */
void *holder; SCM bytevector;
} scm_t_port_buffer; } scm_t_port_buffer;
@ -204,8 +204,8 @@ typedef struct scm_t_ptob_descriptor
char *name; char *name;
int (*print) (SCM exp, SCM port, scm_print_state *pstate); int (*print) (SCM exp, SCM port, scm_print_state *pstate);
void (*read) (SCM port, scm_t_port_buffer *dst); size_t (*read) (SCM port, SCM dst, size_t start, size_t count);
void (*write) (SCM port, scm_t_port_buffer *src); size_t (*write) (SCM port, SCM src, size_t start, size_t count);
scm_t_off (*seek) (SCM port, scm_t_off OFFSET, int WHENCE); scm_t_off (*seek) (SCM port, scm_t_off OFFSET, int WHENCE);
void (*close) (SCM port); void (*close) (SCM port);
@ -230,8 +230,8 @@ SCM_API scm_t_ptob_descriptor* scm_c_port_type_ref (long ptobnum);
SCM_API long scm_c_port_type_add_x (scm_t_ptob_descriptor *desc); SCM_API long scm_c_port_type_add_x (scm_t_ptob_descriptor *desc);
SCM_API scm_t_bits scm_make_port_type SCM_API scm_t_bits scm_make_port_type
(char *name, (char *name,
void (*read) (SCM port, scm_t_port_buffer *dst), size_t (*read) (SCM port, SCM dst, size_t start, size_t count),
void (*write) (SCM port, scm_t_port_buffer *src)); size_t (*write) (SCM port, SCM src, size_t start, size_t count));
SCM_API void scm_set_port_print (scm_t_bits tc, SCM_API void scm_set_port_print (scm_t_bits tc,
int (*print) (SCM exp, int (*print) (SCM exp,
SCM port, SCM port,
@ -323,6 +323,7 @@ SCM_INLINE int scm_peek_byte_or_eof_unlocked (SCM port);
SCM_API int scm_slow_peek_byte_or_eof_unlocked (SCM port); SCM_API int scm_slow_peek_byte_or_eof_unlocked (SCM port);
SCM_API size_t scm_c_read (SCM port, void *buffer, size_t size); SCM_API size_t scm_c_read (SCM port, void *buffer, size_t size);
SCM_API size_t scm_c_read_unlocked (SCM port, void *buffer, size_t size); SCM_API size_t scm_c_read_unlocked (SCM port, void *buffer, size_t size);
SCM_API size_t scm_c_read_bytes (SCM port, SCM dst, size_t start, size_t count);
SCM_API scm_t_wchar scm_getc (SCM port); SCM_API scm_t_wchar scm_getc (SCM port);
SCM_API scm_t_wchar scm_getc_unlocked (SCM port); SCM_API scm_t_wchar scm_getc_unlocked (SCM port);
SCM_API SCM scm_read_char (SCM port); SCM_API SCM scm_read_char (SCM port);
@ -359,6 +360,7 @@ SCM_API void scm_puts (const char *str_data, SCM port);
SCM_INLINE void scm_puts_unlocked (const char *str_data, SCM port); SCM_INLINE void scm_puts_unlocked (const char *str_data, SCM port);
SCM_API void scm_c_write (SCM port, const void *buffer, size_t size); SCM_API void scm_c_write (SCM port, const void *buffer, size_t size);
SCM_API void scm_c_write_unlocked (SCM port, const void *buffer, size_t size); SCM_API void scm_c_write_unlocked (SCM port, const void *buffer, size_t size);
SCM_API void scm_c_write_bytes (SCM port, SCM src, size_t start, size_t count);
SCM_API void scm_lfwrite (const char *ptr, size_t size, SCM port); SCM_API void scm_lfwrite (const char *ptr, size_t size, SCM port);
SCM_API void scm_lfwrite_unlocked (const char *ptr, size_t size, SCM port); SCM_API void scm_lfwrite_unlocked (const char *ptr, size_t size, SCM port);
SCM_INTERNAL void scm_lfwrite_substr (SCM str, size_t start, size_t end, SCM_INTERNAL void scm_lfwrite_substr (SCM str, size_t start, size_t end,

View file

@ -99,25 +99,26 @@ make_bytevector_input_port (SCM bv)
(scm_t_bits) stream); (scm_t_bits) stream);
} }
static void static size_t
bytevector_input_port_read (SCM port, scm_t_port_buffer *buf) bytevector_input_port_read (SCM port, SCM dst, size_t start, size_t count)
{ {
size_t count; size_t remaining;
struct bytevector_input_port *stream = (void *) SCM_STREAM (port); struct bytevector_input_port *stream = (void *) SCM_STREAM (port);
if (stream->pos >= SCM_BYTEVECTOR_LENGTH (stream->bytevector)) if (stream->pos >= SCM_BYTEVECTOR_LENGTH (stream->bytevector))
return; return 0;
count = SCM_BYTEVECTOR_LENGTH (stream->bytevector) - stream->pos; remaining = SCM_BYTEVECTOR_LENGTH (stream->bytevector) - stream->pos;
if (count > buf->size - buf->end) if (remaining < count)
count = buf->size - buf->end; count = remaining;
memcpy (buf->buf + buf->end, memcpy (SCM_BYTEVECTOR_CONTENTS (dst) + start,
SCM_BYTEVECTOR_CONTENTS (stream->bytevector) + stream->pos, SCM_BYTEVECTOR_CONTENTS (stream->bytevector) + stream->pos,
count); count);
buf->end += count;
stream->pos += count; stream->pos += count;
return count;
} }
static scm_t_off static scm_t_off
@ -277,32 +278,21 @@ make_custom_binary_input_port (SCM read_proc, SCM get_position_proc,
(scm_t_bits) stream); (scm_t_bits) stream);
} }
static void static size_t
custom_binary_input_port_read (SCM port, scm_t_port_buffer *buf) custom_binary_input_port_read (SCM port, SCM dst, size_t start, size_t count)
#define FUNC_NAME "custom_binary_input_port_read" #define FUNC_NAME "custom_binary_input_port_read"
{ {
struct custom_binary_port *stream = (void *) SCM_STREAM (port); struct custom_binary_port *stream = (void *) SCM_STREAM (port);
SCM bv, octets; SCM octets;
size_t c_octets; size_t c_octets;
/* FIXME: We need to make sure buf->buf is kept alive. If read_buf is octets = scm_call_3 (stream->read, dst, scm_from_size_t (start),
referenced from PORT, passing PORT as the parent will do it. But, scm_from_size_t (count));
pushback could re-set PORT->read_buf, which would be a fail. But,
probably buf->buf is itself GC-allocated, so we can pack it
directly. But, perhaps it's not, as in scm_c_read(). In that
latter case we're kinda screwed and probably need to prevent
rewinding. But shouldn't we always prevent rewinding? And how can
we avoid allocating the bytevector at all? */
bv = scm_c_take_gc_bytevector ((signed char *) (buf->buf + buf->end),
buf->size - buf->end,
PTR2SCM (buf->buf));
octets = scm_call_3 (stream->read, bv, SCM_INUM0, scm_bytevector_length (bv));
c_octets = scm_to_size_t (octets); c_octets = scm_to_size_t (octets);
if (c_octets > scm_c_bytevector_length (bv)) if (c_octets > count)
scm_out_of_range (FUNC_NAME, octets); scm_out_of_range (FUNC_NAME, octets);
buf->end += c_octets; return c_octets;
} }
#undef FUNC_NAME #undef FUNC_NAME
@ -405,7 +395,6 @@ SCM_DEFINE (scm_get_bytevector_n, "get-bytevector-n", 2, 0, 0,
#define FUNC_NAME s_scm_get_bytevector_n #define FUNC_NAME s_scm_get_bytevector_n
{ {
SCM result; SCM result;
char *c_bv;
unsigned c_count; unsigned c_count;
size_t c_read; size_t c_read;
@ -413,11 +402,10 @@ SCM_DEFINE (scm_get_bytevector_n, "get-bytevector-n", 2, 0, 0,
c_count = scm_to_uint (count); c_count = scm_to_uint (count);
result = scm_c_make_bytevector (c_count); result = scm_c_make_bytevector (c_count);
c_bv = (char *) SCM_BYTEVECTOR_CONTENTS (result);
if (SCM_LIKELY (c_count > 0)) if (SCM_LIKELY (c_count > 0))
/* XXX: `scm_c_read ()' does not update the port position. */ /* XXX: `scm_c_read ()' does not update the port position. */
c_read = scm_c_read_unlocked (port, c_bv, c_count); c_read = scm_c_read_bytes (port, result, 0, c_count);
else else
/* Don't invoke `scm_c_read ()' since it may block. */ /* Don't invoke `scm_c_read ()' since it may block. */
c_read = 0; c_read = 0;
@ -443,7 +431,6 @@ SCM_DEFINE (scm_get_bytevector_n_x, "get-bytevector-n!", 4, 0, 0,
#define FUNC_NAME s_scm_get_bytevector_n_x #define FUNC_NAME s_scm_get_bytevector_n_x
{ {
SCM result; SCM result;
char *c_bv;
unsigned c_start, c_count, c_len; unsigned c_start, c_count, c_len;
size_t c_read; size_t c_read;
@ -452,14 +439,13 @@ SCM_DEFINE (scm_get_bytevector_n_x, "get-bytevector-n!", 4, 0, 0,
c_start = scm_to_uint (start); c_start = scm_to_uint (start);
c_count = scm_to_uint (count); c_count = scm_to_uint (count);
c_bv = (char *) SCM_BYTEVECTOR_CONTENTS (bv);
c_len = SCM_BYTEVECTOR_LENGTH (bv); c_len = SCM_BYTEVECTOR_LENGTH (bv);
if (SCM_UNLIKELY (c_start + c_count > c_len)) if (SCM_UNLIKELY (c_start + c_count > c_len))
scm_out_of_range (FUNC_NAME, count); scm_out_of_range (FUNC_NAME, count);
if (SCM_LIKELY (c_count > 0)) if (SCM_LIKELY (c_count > 0))
c_read = scm_c_read_unlocked (port, c_bv + c_start, c_count); c_read = scm_c_read_bytes (port, bv, c_start, c_count);
else else
/* Don't invoke `scm_c_read ()' since it may block. */ /* Don't invoke `scm_c_read ()' since it may block. */
c_read = 0; c_read = 0;
@ -513,14 +499,13 @@ SCM_DEFINE (scm_get_bytevector_all, "get-bytevector-all", 1, 0, 0,
#define FUNC_NAME s_scm_get_bytevector_all #define FUNC_NAME s_scm_get_bytevector_all
{ {
SCM result; SCM result;
char *c_bv;
unsigned c_len, c_count; unsigned c_len, c_count;
size_t c_read, c_total; size_t c_read, c_total;
SCM_VALIDATE_BINARY_INPUT_PORT (1, port); SCM_VALIDATE_BINARY_INPUT_PORT (1, port);
c_len = c_count = 4096; c_len = c_count = 4096;
c_bv = (char *) scm_gc_malloc_pointerless (c_len, SCM_GC_BYTEVECTOR); result = scm_c_make_bytevector (c_count);
c_total = c_read = 0; c_total = c_read = 0;
do do
@ -528,37 +513,27 @@ SCM_DEFINE (scm_get_bytevector_all, "get-bytevector-all", 1, 0, 0,
if (c_total + c_read > c_len) if (c_total + c_read > c_len)
{ {
/* Grow the bytevector. */ /* Grow the bytevector. */
c_bv = (char *) scm_gc_realloc (c_bv, c_len, c_len * 2, SCM prev = result;
SCM_GC_BYTEVECTOR); result = scm_c_make_bytevector (c_len * 2);
memcpy (SCM_BYTEVECTOR_CONTENTS (result),
SCM_BYTEVECTOR_CONTENTS (prev),
c_total);
c_count = c_len; c_count = c_len;
c_len *= 2; c_len *= 2;
} }
/* `scm_c_read ()' blocks until C_COUNT bytes are available or EOF is /* `scm_c_read ()' blocks until C_COUNT bytes are available or EOF is
reached. */ reached. */
c_read = scm_c_read_unlocked (port, c_bv + c_total, c_count); c_read = scm_c_read_bytes (port, result, c_total, c_count);
c_total += c_read, c_count -= c_read; c_total += c_read, c_count -= c_read;
} }
while (c_count == 0); while (c_count == 0);
if (c_total == 0) if (c_total == 0)
{ return SCM_EOF_VAL;
result = SCM_EOF_VAL;
scm_gc_free (c_bv, c_len, SCM_GC_BYTEVECTOR);
}
else
{
if (c_len > c_total)
{
/* Shrink the bytevector. */
c_bv = (char *) scm_gc_realloc (c_bv, c_len, c_total,
SCM_GC_BYTEVECTOR);
c_len = (unsigned) c_total;
}
result = scm_c_take_gc_bytevector ((signed char *) c_bv, c_len, if (c_len > c_total)
SCM_BOOL_F); return scm_c_shrink_bytevector (result, c_total);
}
return result; return result;
} }
@ -596,14 +571,12 @@ SCM_DEFINE (scm_put_bytevector, "put-bytevector", 2, 2, 0,
"octets.") "octets.")
#define FUNC_NAME s_scm_put_bytevector #define FUNC_NAME s_scm_put_bytevector
{ {
char *c_bv;
unsigned c_start, c_count, c_len; unsigned c_start, c_count, c_len;
SCM_VALIDATE_BINARY_OUTPUT_PORT (1, port); SCM_VALIDATE_BINARY_OUTPUT_PORT (1, port);
SCM_VALIDATE_BYTEVECTOR (2, bv); SCM_VALIDATE_BYTEVECTOR (2, bv);
c_len = SCM_BYTEVECTOR_LENGTH (bv); c_len = SCM_BYTEVECTOR_LENGTH (bv);
c_bv = (char *) SCM_BYTEVECTOR_CONTENTS (bv);
if (!scm_is_eq (start, SCM_UNDEFINED)) if (!scm_is_eq (start, SCM_UNDEFINED))
{ {
@ -626,7 +599,7 @@ SCM_DEFINE (scm_put_bytevector, "put-bytevector", 2, 2, 0,
else else
c_start = 0, c_count = c_len; c_start = 0, c_count = c_len;
scm_c_write_unlocked (port, c_bv + c_start, c_count); scm_c_write_bytes (port, bv, c_start, c_count);
return SCM_UNSPECIFIED; return SCM_UNSPECIFIED;
} }
@ -777,21 +750,22 @@ make_bytevector_output_port (void)
} }
/* Write octets from WRITE_BUF to the backing store. */ /* Write octets from WRITE_BUF to the backing store. */
static void static size_t
bytevector_output_port_write (SCM port, scm_t_port_buffer *write_buf) bytevector_output_port_write (SCM port, SCM src, size_t start, size_t count)
{ {
size_t count;
scm_t_bytevector_output_port_buffer *buf; scm_t_bytevector_output_port_buffer *buf;
buf = SCM_BYTEVECTOR_OUTPUT_PORT_BUFFER (port); buf = SCM_BYTEVECTOR_OUTPUT_PORT_BUFFER (port);
count = write_buf->end - write_buf->cur;
if (buf->pos + count > buf->total_len) if (buf->pos + count > buf->total_len)
bytevector_output_port_buffer_grow (buf, buf->pos + count); bytevector_output_port_buffer_grow (buf, buf->pos + count);
memcpy (buf->buffer + buf->pos, write_buf->buf + write_buf->cur, count); memcpy (buf->buffer + buf->pos, SCM_BYTEVECTOR_CONTENTS (src) + start, count);
buf->pos += count; buf->pos += count;
buf->len = (buf->len > buf->pos) ? buf->len : buf->pos; buf->len = (buf->len > buf->pos) ? buf->len : buf->pos;
return count;
} }
static scm_t_off static scm_t_off
@ -909,41 +883,35 @@ make_custom_binary_output_port (SCM write_proc, SCM get_position_proc,
} }
/* Flush octets from BUF to the backing store. */ /* Flush octets from BUF to the backing store. */
static void static size_t
custom_binary_output_port_write (SCM port, scm_t_port_buffer *buf) custom_binary_output_port_write (SCM port, SCM src, size_t start, size_t count)
#define FUNC_NAME "custom_binary_output_port_write" #define FUNC_NAME "custom_binary_output_port_write"
{ {
size_t size, written; size_t written;
struct custom_binary_port *stream = (void *) SCM_STREAM (port); struct custom_binary_port *stream = (void *) SCM_STREAM (port);
SCM bv;
/* FIXME: If BUF is the same as PORT->write_buf, then the data is
GC-managed and we could avoid allocating a new bytevector backing
store. Otherwise we have to copy, as we do here. */
size = buf->end - buf->cur;
bv = scm_c_make_bytevector (size);
memcpy (SCM_BYTEVECTOR_CONTENTS (bv), buf->buf + buf->cur, size);
/* Since the `write' procedure of Guile's ports has type `void', it must /* Since the `write' procedure of Guile's ports has type `void', it must
try hard to write exactly SIZE bytes, regardless of how many bytes the try hard to write exactly SIZE bytes, regardless of how many bytes the
sink can handle. */ sink can handle. */
written = 0; written = 0;
while (written < size) while (written < count)
{ {
long int c_result; long int c_result;
SCM result; SCM result;
result = scm_call_3 (stream->write, bv, result = scm_call_3 (stream->write, src,
scm_from_size_t (written), scm_from_size_t (start + written),
scm_from_size_t (size - written)); scm_from_size_t (count - written));
c_result = scm_to_long (result); c_result = scm_to_long (result);
if (c_result < 0 || (size_t) c_result > (size - written)) if (c_result < 0 || (size_t) c_result > (count - written))
scm_wrong_type_arg_msg (FUNC_NAME, 0, result, scm_wrong_type_arg_msg (FUNC_NAME, 0, result,
"R6RS custom binary output port `write!' " "R6RS custom binary output port `write!' "
"returned a incorrect integer"); "returned a incorrect integer");
written += c_result; written += c_result;
} }
return written;
} }
#undef FUNC_NAME #undef FUNC_NAME
@ -1008,32 +976,19 @@ make_transcoded_port (SCM binary_port, unsigned long mode)
return port; return port;
} }
static void static size_t
transcoded_port_write (SCM port, scm_t_port_buffer *buf) transcoded_port_write (SCM port, SCM src, size_t start, size_t count)
{ {
SCM bport = SCM_TRANSCODED_PORT_BINARY_PORT (port); SCM bport = SCM_TRANSCODED_PORT_BINARY_PORT (port);
scm_c_write_unlocked (bport, buf->buf + buf->cur, buf->end - buf->cur); scm_c_write_bytes (bport, src, start, count);
return count;
} }
static void static size_t
transcoded_port_read (SCM port, scm_t_port_buffer *buf) transcoded_port_read (SCM port, SCM dst, size_t start, size_t count)
{ {
size_t count; SCM bport = SCM_TRANSCODED_PORT_BINARY_PORT (port);
scm_t_port_buffer *bport_buf; return scm_c_read_bytes (bport, dst, start, count);
/* We can't use `scm_c_read' here, since it blocks until the whole
block has been read or EOF. */
bport_buf = scm_fill_input (SCM_TRANSCODED_PORT_BINARY_PORT (port));
/* Consume EOF from bport. */
bport_buf->has_eof = 0;
count = bport_buf->end - bport_buf->cur;
if (count > buf->size - buf->end)
count = buf->size - buf->end;
memcpy (buf->buf + buf->end, bport_buf->buf + bport_buf->cur, count);
bport_buf->cur += count;
buf->end += count;
} }
static void static void

View file

@ -60,31 +60,29 @@ struct string_port {
size_t len; size_t len;
}; };
static void static size_t
string_port_read (SCM port, scm_t_port_buffer *dst) string_port_read (SCM port, SCM dst, size_t start, size_t count)
{ {
size_t count;
struct string_port *stream = (void *) SCM_STREAM (port); struct string_port *stream = (void *) SCM_STREAM (port);
if (stream->pos >= stream->len) if (stream->pos >= stream->len)
return; return 0;
count = stream->len - stream->pos; if (count > stream->len - stream->pos)
if (count > dst->size - dst->end) count = stream->len - stream->pos;
count = dst->size - dst->end;
memcpy (dst->buf + dst->end, memcpy (SCM_BYTEVECTOR_CONTENTS (dst) + start,
SCM_BYTEVECTOR_CONTENTS (stream->bytevector) + stream->pos, SCM_BYTEVECTOR_CONTENTS (stream->bytevector) + stream->pos,
count); count);
dst->end += count;
stream->pos += count; stream->pos += count;
return count;
} }
static void static size_t
string_port_write (SCM port, scm_t_port_buffer *src) string_port_write (SCM port, SCM src, size_t start, size_t count)
{ {
struct string_port *stream = (void *) SCM_STREAM (port); struct string_port *stream = (void *) SCM_STREAM (port);
size_t count = src->end - src->cur;
if (SCM_BYTEVECTOR_LENGTH (stream->bytevector) < stream->pos + count) if (SCM_BYTEVECTOR_LENGTH (stream->bytevector) < stream->pos + count)
{ {
@ -101,12 +99,13 @@ string_port_write (SCM port, scm_t_port_buffer *src)
} }
memcpy (SCM_BYTEVECTOR_CONTENTS (stream->bytevector) + stream->pos, memcpy (SCM_BYTEVECTOR_CONTENTS (stream->bytevector) + stream->pos,
src->buf + src->cur, SCM_BYTEVECTOR_CONTENTS (src) + start,
count); count);
src->cur += count;
stream->pos += count; stream->pos += count;
if (stream->pos > stream->len) if (stream->pos > stream->len)
stream->len = stream->pos; stream->len = stream->pos;
return count;
} }
static scm_t_off static scm_t_off

View file

@ -72,27 +72,30 @@ soft_port_get_natural_buffer_sizes (SCM port, size_t *read_size,
*write_size = 1; *write_size = 1;
} }
static void static size_t
soft_port_write (SCM port, scm_t_port_buffer *buf) soft_port_write (SCM port, SCM src, size_t start, size_t count)
{ {
struct soft_port *stream = (void *) SCM_STREAM (port); struct soft_port *stream = (void *) SCM_STREAM (port);
scm_t_uint8 * ptr = buf->buf + buf->cur; signed char * ptr = SCM_BYTEVECTOR_CONTENTS (src) + start;
SCM str = scm_from_port_stringn ((char *) ptr, buf->end - buf->cur, port);
buf->end = buf->cur = 0;
scm_call_1 (stream->write_string, str); scm_call_1 (stream->write_string,
scm_from_port_stringn ((char *) ptr, count, port));
/* Backwards compatibility. */ /* Backwards compatibility. */
if (scm_is_true (stream->flush)) if (scm_is_true (stream->flush))
scm_call_0 (stream->flush); scm_call_0 (stream->flush);
return count;
} }
/* places a single char in the input buffer. */ /* places a single char in the input buffer. */
static void static size_t
soft_port_read (SCM port, scm_t_port_buffer *dst) soft_port_read (SCM port, SCM dst, size_t start, size_t count)
{ {
size_t written;
struct soft_port *stream = (void *) SCM_STREAM (port); struct soft_port *stream = (void *) SCM_STREAM (port);
scm_t_port_buffer *encode_buf = stream->encode_buf; scm_t_port_buffer *encode_buf = stream->encode_buf;
signed char *dst_ptr = SCM_BYTEVECTOR_CONTENTS (dst) + start;
/* A character can be more than one byte, but we don't have a /* A character can be more than one byte, but we don't have a
guarantee that there is more than one byte in the read buffer. So, guarantee that there is more than one byte in the read buffer. So,
@ -106,7 +109,7 @@ soft_port_read (SCM port, scm_t_port_buffer *dst)
ans = scm_call_0 (stream->read_char); ans = scm_call_0 (stream->read_char);
if (scm_is_false (ans) || SCM_EOF_OBJECT_P (ans)) if (scm_is_false (ans) || SCM_EOF_OBJECT_P (ans))
return; return 0;
SCM_ASSERT (SCM_CHARP (ans), ans, SCM_ARG1, "soft_port_read"); SCM_ASSERT (SCM_CHARP (ans), ans, SCM_ARG1, "soft_port_read");
/* It's possible to make a fast path here, but it would be fastest /* It's possible to make a fast path here, but it would be fastest
@ -119,8 +122,12 @@ soft_port_read (SCM port, scm_t_port_buffer *dst)
free (str); free (str);
} }
while (dst->end < dst->size && encode_buf->cur < encode_buf->end) for (written = 0;
dst->buf[dst->end++] = encode_buf->buf[encode_buf->cur++]; written < count && encode_buf->cur < encode_buf->end;
written++, encode_buf->cur++)
dst_ptr[written] = encode_buf->buf[encode_buf->cur];
return written;
} }

View file

@ -54,18 +54,20 @@ make_port (scm_t_bits port_type)
return scm_c_make_port (port_type, SCM_RDNG, (scm_t_bits) stream); return scm_c_make_port (port_type, SCM_RDNG, (scm_t_bits) stream);
} }
static void static size_t
custom_port_read (SCM port, scm_t_port_buffer *dst) custom_port_read (SCM port, SCM dst, size_t start, size_t count)
{ {
size_t to_copy = dst->size - dst->end; size_t to_copy = count;
struct custom_port *stream = (void *) SCM_STREAM (port); struct custom_port *stream = (void *) SCM_STREAM (port);
if (stream->pos + to_copy > stream->len) if (stream->pos + to_copy > stream->len)
to_copy = stream->len - stream->pos; to_copy = stream->len - stream->pos;
memcpy (dst->buf + dst->end, stream->buf + stream->pos, to_copy); memcpy (SCM_BYTEVECTOR_CONTENTS (dst) + start,
stream->buf + stream->pos, to_copy);
stream->pos += to_copy; stream->pos += to_copy;
dst->end += to_copy;
return to_copy;
} }
/* Return true (non-zero) if BUF contains only zeros. */ /* Return true (non-zero) if BUF contains only zeros. */