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:
parent
55fb8f4e7e
commit
f7027a8b88
7 changed files with 372 additions and 240 deletions
351
libguile/ports.c
351
libguile/ports.c
|
|
@ -229,8 +229,10 @@ static const size_t default_buffer_size = 1024;
|
|||
|
||||
scm_t_bits
|
||||
scm_make_port_type (char *name,
|
||||
void (*read) (SCM port, scm_t_port_buffer *buf),
|
||||
void (*write) (SCM port, scm_t_port_buffer *buf))
|
||||
size_t (*read) (SCM port, SCM dst, size_t start,
|
||||
size_t count),
|
||||
size_t (*write) (SCM port, SCM src, size_t start,
|
||||
size_t count))
|
||||
{
|
||||
scm_t_ptob_descriptor *desc;
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -1432,33 +1435,51 @@ scm_peek_byte_or_eof (SCM port)
|
|||
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
|
||||
buffer. If the number of available bytes in the buffer does not
|
||||
increase after a call to scm_i_read_unlocked, that indicates EOF. */
|
||||
static void
|
||||
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);
|
||||
|
||||
SCM_PORT_DESCRIPTOR (port)->read (port, buf);
|
||||
buf->has_eof = buf->end == prev_end;
|
||||
count = scm_i_read_bytes_unlocked (port, buf->bytevector, buf->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 only
|
||||
* returns less than SIZE bytes if at end-of-file.
|
||||
*
|
||||
* Warning: Doesn't update port line and column counts! */
|
||||
size_t
|
||||
scm_c_read_unlocked (SCM port, void *buffer, size_t size)
|
||||
#define FUNC_NAME "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
|
||||
returns less than SIZE bytes if at end-of-file.
|
||||
|
||||
Warning: Doesn't update port line and column counts! */
|
||||
static size_t
|
||||
scm_c_read_bytes_unlocked (SCM port, SCM dst, size_t start, size_t count)
|
||||
#define FUNC_NAME "scm_c_read_bytes"
|
||||
{
|
||||
size_t to_read = count;
|
||||
scm_t_port *pt;
|
||||
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);
|
||||
|
||||
|
|
@ -1475,46 +1496,115 @@ scm_c_read_unlocked (SCM port, void *buffer, size_t size)
|
|||
/* Take bytes first from the port's read buffer. */
|
||||
if (read_buf->cur < read_buf->end)
|
||||
{
|
||||
size_t to_read = dst_buf.size - dst_buf.end;
|
||||
to_read = min (to_read, read_buf->end - read_buf->cur);
|
||||
memcpy (dst_buf.buf + dst_buf.end, read_buf->buf + read_buf->cur,
|
||||
to_read);
|
||||
dst_buf.end += to_read;
|
||||
read_buf->cur += to_read;
|
||||
size_t to_copy = count;
|
||||
to_copy = min (to_copy, read_buf->end - read_buf->cur);
|
||||
memcpy (dst_ptr, read_buf->buf + read_buf->cur, to_copy);
|
||||
dst_ptr += to_copy;
|
||||
to_read -= to_copy;
|
||||
read_buf->cur += to_copy;
|
||||
}
|
||||
|
||||
while (dst_buf.end < dst_buf.size)
|
||||
/* If the read buffering is larger than our read size, buffer the
|
||||
read. Otherwise read into our buffer directly. */
|
||||
if (dst_buf.size - dst_buf.end < pt->read_buffering)
|
||||
{
|
||||
size_t to_read = dst_buf.size - dst_buf.end;
|
||||
read_buf = scm_fill_input_unlocked (port);
|
||||
if (to_read > read_buf->end - read_buf->cur)
|
||||
to_read = read_buf->end - read_buf->cur;
|
||||
if (to_read == 0)
|
||||
{
|
||||
/* Consider that we've read off this EOF. */
|
||||
read_buf->has_eof = 0;
|
||||
break;
|
||||
}
|
||||
memcpy (dst_buf.buf + dst_buf.end,
|
||||
read_buf->buf + read_buf->cur,
|
||||
to_read);
|
||||
read_buf->cur += to_read;
|
||||
dst_buf.end += to_read;
|
||||
}
|
||||
else
|
||||
{
|
||||
scm_i_read_unlocked (port, &dst_buf);
|
||||
if (dst_buf.has_eof)
|
||||
break;
|
||||
}
|
||||
while (to_read)
|
||||
{
|
||||
/* If the read is smaller than the buffering on the read side of
|
||||
this port, then go through the buffer. Otherwise fill our
|
||||
buffer directly. */
|
||||
if (to_read < pt->read_buffering)
|
||||
{
|
||||
size_t to_copy = to_read;
|
||||
read_buf = scm_fill_input_unlocked (port);
|
||||
to_copy = min (to_copy, read_buf->end - read_buf->cur);
|
||||
memcpy (dst_ptr, read_buf->buf + read_buf->cur, to_copy);
|
||||
if (to_copy == 0)
|
||||
{
|
||||
/* Consider that we've read off this EOF. */
|
||||
read_buf->has_eof = 0;
|
||||
break;
|
||||
}
|
||||
dst_ptr += to_copy;
|
||||
to_read -= to_copy;
|
||||
read_buf->cur += to_copy;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t filled;
|
||||
|
||||
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
|
||||
|
||||
/* 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
|
||||
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
|
||||
|
||||
static void scm_i_write_unlocked (SCM port, scm_t_port_buffer *src);
|
||||
|
||||
void
|
||||
scm_flush_unlocked (SCM port)
|
||||
{
|
||||
scm_t_port_buffer *buf = SCM_PTAB_ENTRY (port)->write_buf;
|
||||
if (buf->cur < buf->end)
|
||||
SCM_PORT_DESCRIPTOR (port)->write (port, buf);
|
||||
buf->cur = buf->end = 0;
|
||||
scm_i_write_unlocked (port, buf);
|
||||
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_c_write
|
||||
*
|
||||
* 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 and has no
|
||||
* return value.
|
||||
*
|
||||
* Warning: Doesn't update port line and column counts!
|
||||
*/
|
||||
void
|
||||
scm_c_write_unlocked (SCM port, const void *ptr, size_t size)
|
||||
#define FUNC_NAME "scm_c_write"
|
||||
static void
|
||||
scm_i_write_bytes_unlocked (SCM port, SCM src, size_t start, size_t count)
|
||||
{
|
||||
size_t written;
|
||||
scm_t_ptob_descriptor *ptob = SCM_PORT_DESCRIPTOR (port);
|
||||
|
||||
assert (count <= SCM_BYTEVECTOR_LENGTH (src));
|
||||
assert (start + count <= SCM_BYTEVECTOR_LENGTH (src));
|
||||
|
||||
written = ptob->write (port, src, start, count);
|
||||
|
||||
/* 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_buffer *write_buf;
|
||||
scm_t_ptob_descriptor *ptob;
|
||||
|
||||
SCM_VALIDATE_OPOUTPORT (1, port);
|
||||
|
||||
pt = SCM_PTAB_ENTRY (port);
|
||||
ptob = SCM_PORT_DESCRIPTOR (port);
|
||||
write_buf = pt->write_buf;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (size < write_buf->size)
|
||||
if (count < write_buf->size)
|
||||
{
|
||||
/* Make it so that write_buf->end is only nonzero if there are
|
||||
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
|
||||
full after filling it with the new data; if that's the case, we
|
||||
flush then instead. */
|
||||
if (write_buf->end + size > write_buf->size)
|
||||
{
|
||||
ptob->write (port, write_buf);
|
||||
write_buf->cur = write_buf->end = 0;
|
||||
}
|
||||
if (write_buf->end + count > write_buf->size)
|
||||
scm_i_write_unlocked (port, write_buf);
|
||||
|
||||
memcpy (write_buf->buf + write_buf->end, ptr, size);
|
||||
write_buf->end += size;
|
||||
memcpy (write_buf->buf + write_buf->end,
|
||||
SCM_BYTEVECTOR_CONTENTS (src) + start,
|
||||
count);
|
||||
write_buf->end += count;
|
||||
|
||||
if (write_buf->end == write_buf->size)
|
||||
{
|
||||
ptob->write (port, write_buf);
|
||||
write_buf->cur = write_buf->end = 0;
|
||||
}
|
||||
scm_i_write_unlocked (port, write_buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Our write would overflow the buffer. Flush buffered bytes (if
|
||||
needed), then write our bytes with just one syscall. */
|
||||
|
||||
scm_t_port_buffer ad_hoc_buf =
|
||||
{ (scm_t_uint8 *) ptr, 0, size, size, 0, NULL };
|
||||
size_t written;
|
||||
|
||||
if (write_buf->cur < write_buf->end)
|
||||
{
|
||||
ptob->write (port, write_buf);
|
||||
write_buf->cur = write_buf->end = 0;
|
||||
}
|
||||
scm_i_write_unlocked (port, write_buf);
|
||||
|
||||
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
|
||||
|
|
@ -2606,6 +2762,16 @@ scm_c_write (SCM port, const void *ptr, size_t size)
|
|||
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
|
||||
*
|
||||
* 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))
|
||||
{
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
static void
|
||||
void_port_read (SCM port, scm_t_port_buffer *buf)
|
||||
static size_t
|
||||
void_port_read (SCM port, SCM dst, size_t start, size_t count)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
void_port_write (SCM port, scm_t_port_buffer *buf)
|
||||
static size_t
|
||||
void_port_write (SCM port, SCM src, size_t start, size_t count)
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
static SCM
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue