1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-20 11:40:18 +02:00

Add implementation of "transcoded ports"

* libguile/r6rs-ports.c (make_tp, tp_write, tp_fill_input, tp_flush)
  (tp_close, initialize_transcoded_ports, scm_i_make_transcoded_port): New
  functions.
  (scm_init_r6rs_ports): Call `initialize_transcoded_ports'.
* module/rnrs/ports.scm (transcoded-port): Actually implement,
  using `%make-transcoded-port'.
* test-suite/tests/r6rs-ports.test ("8.2.6 Input and output ports"): Added a
  few tests for `transcoded-port'.

Signed-off-by: Ludovic Courtès <ludo@gnu.org>
This commit is contained in:
Andreas Rottmann 2010-11-21 23:17:54 +01:00 committed by Ludovic Courtès
parent a5484153b8
commit 1044537dff
3 changed files with 173 additions and 2 deletions

View file

@ -1075,6 +1075,148 @@ initialize_custom_binary_output_ports (void)
scm_set_port_close (custom_binary_output_port_type, cbp_close);
}
/* Transcoded ports ("tp" for short). */
static scm_t_bits transcoded_port_type = 0;
#define TP_INPUT_BUFFER_SIZE 4096
#define SCM_TP_BINARY_PORT(_port) SCM_PACK (SCM_STREAM (_port))
static inline SCM
make_tp (SCM binary_port, unsigned long mode)
{
SCM port;
scm_t_port *c_port;
const unsigned long mode_bits = SCM_OPN | mode;
scm_i_pthread_mutex_lock (&scm_i_port_table_mutex);
port = scm_new_port_table_entry (transcoded_port_type);
SCM_SETSTREAM (port, SCM_UNPACK (binary_port));
SCM_SET_CELL_TYPE (port, transcoded_port_type | mode_bits);
if (SCM_INPUT_PORT_P (port))
{
c_port = SCM_PTAB_ENTRY (port);
c_port->read_buf = scm_gc_malloc_pointerless (TP_INPUT_BUFFER_SIZE,
"port buffer");
c_port->read_pos = c_port->read_end = c_port->read_buf;
c_port->read_buf_size = TP_INPUT_BUFFER_SIZE;
SCM_SET_CELL_WORD_0 (port, SCM_CELL_WORD_0 (port) & ~SCM_BUF0);
}
scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex);
return port;
}
static void
tp_write (SCM port, const void *data, size_t size)
{
scm_c_write (SCM_TP_BINARY_PORT (port), data, size);
}
static int
tp_fill_input (SCM port)
{
size_t count;
scm_t_port *c_port = SCM_PTAB_ENTRY (port);
SCM bport = SCM_TP_BINARY_PORT (port);
scm_t_port *c_bport = SCM_PTAB_ENTRY (bport);
/* We can't use `scm_c_read' here, since it blocks until the whole
block has been read or EOF. */
if (c_bport->rw_active == SCM_PORT_WRITE)
scm_force_output (bport);
if (c_bport->read_pos >= c_bport->read_end)
scm_fill_input (bport);
count = c_bport->read_end - c_bport->read_pos;
if (count > c_port->read_buf_size)
count = c_port->read_buf_size;
memcpy (c_port->read_buf, c_bport->read_pos, count);
c_bport->read_pos += count;
if (c_bport->rw_random)
c_bport->rw_active = SCM_PORT_READ;
if (count == 0)
return EOF;
else
{
c_port->read_pos = c_port->read_buf;
c_port->read_end = c_port->read_buf + count;
return *c_port->read_buf;
}
}
static void
tp_flush (SCM port)
{
SCM binary_port = SCM_TP_BINARY_PORT (port);
scm_t_port *c_port = SCM_PTAB_ENTRY (port);
size_t count = c_port->write_pos - c_port->write_buf;
scm_c_write (binary_port, c_port->write_buf, count);
c_port->write_pos = c_port->write_buf;
c_port->rw_active = SCM_PORT_NEITHER;
scm_force_output (binary_port);
}
static int
tp_close (SCM port)
{
if (SCM_OUTPUT_PORT_P (port))
tp_flush (port);
return scm_is_true (scm_close_port (SCM_TP_BINARY_PORT (port))) ? 0 : -1;
}
static inline void
initialize_transcoded_ports (void)
{
transcoded_port_type =
scm_make_port_type ("r6rs-transcoded-port", tp_fill_input, tp_write);
scm_set_port_flush (transcoded_port_type, tp_flush);
scm_set_port_close (transcoded_port_type, tp_close);
}
SCM_DEFINE (scm_i_make_transcoded_port,
"%make-transcoded-port", 1, 0, 0,
(SCM port),
"Return a new port which reads and writes to @var{port}")
#define FUNC_NAME s_scm_i_make_transcoded_port
{
SCM result;
unsigned long mode = 0;
SCM_VALIDATE_PORT (SCM_ARG1, port);
if (scm_is_true (scm_output_port_p (port)))
mode |= SCM_WRTNG;
else if (scm_is_true (scm_input_port_p (port)))
mode |= SCM_RDNG;
result = make_tp (port, mode);
/* FIXME: We should actually close `port' "in a special way" here,
according to R6RS. As there is no way to do that in Guile without
rendering the underlying port unusable for our purposes as well, we
just leave it open. */
return result;
}
#undef FUNC_NAME
/* Initialization. */
@ -1096,4 +1238,5 @@ scm_init_r6rs_ports (void)
initialize_custom_binary_input_ports ();
initialize_bytevector_output_ports ();
initialize_custom_binary_output_ports ();
initialize_transcoded_ports ();
}

View file

@ -191,8 +191,13 @@
;; So far, we don't support transcoders other than the binary transcoder.
#t)
(define (transcoded-port port)
(error "port transcoders are not supported" port))
(define (transcoded-port port transcoder)
"Return a new textual port based on @var{port}, using
@var{transcoder} to encode and decode data written to or
read from its underlying binary port @var{port}."
(let ((result (%make-transcoded-port port)))
(set-port-encoding! result (transcoder-codec transcoder))
result))
(define (port-position port)
"Return the offset (an integer) indicating where the next octet will be

View file

@ -497,6 +497,29 @@
(not eof?)
(bytevector=? sink source)))))
(with-test-prefix "8.2.6 Input and output ports"
(pass-if "transcoded-port [output]"
(let ((s "Hello\nÄÖÜ"))
(bytevector=?
(string->utf8 s)
(call-with-bytevector-output-port
(lambda (bv-port)
(call-with-port (transcoded-port bv-port (make-transcoder (utf-8-codec)))
(lambda (utf8-port)
(put-string utf8-port s))))))))
(pass-if "transcoded-port [input]"
(let ((s "Hello\nÄÖÜ"))
(string=?
s
(get-string-all
(transcoded-port (open-bytevector-input-port (string->utf8 s))
(make-transcoder (utf-8-codec)))))))
(pass-if "transcoded-port [input line]"
(string=? "ÄÖÜ"
(get-line (transcoded-port
(open-bytevector-input-port (string->utf8 "ÄÖÜ\nFooBar"))
(make-transcoder (utf-8-codec)))))))
;;; Local Variables:
;;; mode: scheme
;;; End: