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

peek-byte in Scheme

* libguile/ports.c (trampoline_to_c_read, trampoline_to_c_write): Since
  C might assume that the indices are within bounds of the bytevector,
  verify them more here.
  (scm_port_random_access_p, scm_port_read_buffering)
  (scm_set_port_read_buffer, scm_port_read, scm_port_write): New helpers
  exposed to (ice-9 ports).
  (scm_port_read_buffer, scm_port_write_buffer): Don't flush or validate
  port mode; we do that in Scheme.
* module/ice-9/ports.scm: Implement enough of port machinery to
  implement peek-byte in Scheme.  Not yet exported.
This commit is contained in:
Andy Wingo 2016-04-30 14:46:45 +02:00
parent 300c85b0f0
commit 6a752bcf2a
3 changed files with 169 additions and 30 deletions

View file

@ -258,11 +258,20 @@ scm_make_port_type (char *name,
static SCM
trampoline_to_c_read (SCM port, SCM dst, SCM start, SCM count)
#define FUNC_NAME "port-read"
{
size_t c_start, c_count;
SCM_VALIDATE_OPPORT (1, port);
c_start = scm_to_size_t (start);
c_count = scm_to_size_t (count);
SCM_ASSERT_RANGE (2, start, start <= count);
SCM_ASSERT_RANGE (3, count, c_start+c_count <= scm_c_bytevector_length (dst));
return scm_from_size_t
(SCM_PORT_DESCRIPTOR (port)->c_read
(port, dst, scm_to_size_t (start), scm_to_size_t (count)));
(SCM_PORT_DESCRIPTOR (port)->c_read (port, dst, c_start, c_count));
}
#undef FUNC_NAME
static size_t
trampoline_to_scm_read (SCM port, SCM dst, size_t start, size_t count)
@ -274,11 +283,20 @@ trampoline_to_scm_read (SCM port, SCM dst, size_t start, size_t count)
static SCM
trampoline_to_c_write (SCM port, SCM src, SCM start, SCM count)
#define FUNC_NAME "port-write"
{
size_t c_start, c_count;
SCM_VALIDATE_OPPORT (1, port);
c_start = scm_to_size_t (start);
c_count = scm_to_size_t (count);
SCM_ASSERT_RANGE (2, start, c_start <= c_count);
SCM_ASSERT_RANGE (3, count, c_start+c_count <= scm_c_bytevector_length (src));
return scm_from_size_t
(SCM_PORT_DESCRIPTOR (port)->c_write
(port, src, scm_to_size_t (start), scm_to_size_t (count)));
(SCM_PORT_DESCRIPTOR (port)->c_write (port, src, c_start, c_count));
}
#undef FUNC_NAME
static size_t
trampoline_to_scm_write (SCM port, SCM src, size_t start, size_t count)
@ -2457,43 +2475,75 @@ scm_fill_input (SCM port)
return read_buf;
}
SCM_DEFINE (scm_port_random_access_p, "port-random-access?", 1, 0, 0,
(SCM port),
"Return true if the port is random-access, or false otherwise.")
#define FUNC_NAME s_scm_port_random_access_p
{
SCM_VALIDATE_OPPORT (1, port);
return scm_from_bool (SCM_PTAB_ENTRY (port)->rw_random);
}
#undef FUNC_NAME
SCM_DEFINE (scm_port_read_buffering, "port-read-buffering", 1, 0, 0,
(SCM port),
"Return the amount of read buffering on a port, in bytes.")
#define FUNC_NAME s_scm_port_read_buffering
{
SCM_VALIDATE_OPINPORT (1, port);
return scm_from_size_t (SCM_PTAB_ENTRY (port)->read_buffering);
}
#undef FUNC_NAME
SCM_DEFINE (scm_set_port_read_buffer_x, "set-port-read-buffer!", 2, 0, 0,
(SCM port, SCM buf),
"Reset the read buffer on an input port.")
#define FUNC_NAME s_scm_set_port_read_buffer_x
{
SCM_VALIDATE_OPINPORT (1, port);
SCM_ASSERT_TYPE (scm_is_vector (buf) && scm_c_vector_length (buf) >= 4,
buf, 2, FUNC_NAME, "port buffer");
SCM_PTAB_ENTRY (port)->read_buf = buf;
return SCM_UNSPECIFIED;
}
#undef FUNC_NAME
SCM_DEFINE (scm_port_read, "port-read", 1, 0, 0, (SCM port),
"Return the read function for an input port.")
#define FUNC_NAME s_scm_port_read
{
SCM_VALIDATE_OPINPORT (1, port);
return SCM_PORT_DESCRIPTOR (port)->scm_read;
}
#undef FUNC_NAME
SCM_DEFINE (scm_port_write, "port-write", 1, 0, 0,
(SCM port),
"Return the write function for an output port.")
#define FUNC_NAME s_scm_port_write
{
SCM_VALIDATE_OPOUTPORT (1, port);
return SCM_PORT_DESCRIPTOR (port)->scm_write;
}
#undef FUNC_NAME
SCM_DEFINE (scm_port_read_buffer, "port-read-buffer", 1, 0, 0,
(SCM port),
"Return the read buffer for a port. If the port is\n"
"random-access, its write buffer, if any, will be flushed\n"
"if needed.")
"Return the read buffer for a port.")
#define FUNC_NAME s_scm_port_read_buffer
{
scm_t_port *pt;
SCM_VALIDATE_OPINPORT (1, port);
pt = SCM_PTAB_ENTRY (port);
if (pt->rw_random)
scm_flush (pt->port);
return pt->read_buf;
SCM_VALIDATE_OPPORT (1, port);
return SCM_PTAB_ENTRY (port)->read_buf;
}
#undef FUNC_NAME
SCM_DEFINE (scm_port_write_buffer, "port-write-buffer", 1, 0, 0,
(SCM port),
"Return the write buffer for a port. If the port is\n"
"random-access, its read buffer, if any, will be discarded\n"
"if needed.")
"Return the write buffer for a port.")
#define FUNC_NAME s_scm_port_write_buffer
{
scm_t_port *pt;
SCM_VALIDATE_OPOUTPORT (1, port);
pt = SCM_PTAB_ENTRY (port);
if (pt->rw_random)
scm_end_input (pt->port);
return pt->write_buf;
SCM_VALIDATE_OPPORT (1, port);
return SCM_PTAB_ENTRY (port)->write_buf;
}
#undef FUNC_NAME

View file

@ -316,6 +316,12 @@ SCM_API SCM scm_drain_input (SCM port);
SCM_API void scm_end_input (SCM port);
SCM_API SCM scm_force_output (SCM port);
SCM_API void scm_flush (SCM port);
SCM_INTERNAL SCM scm_port_random_access_p (SCM port);
SCM_INTERNAL SCM scm_port_read_buffering (SCM port);
SCM_INTERNAL SCM scm_set_port_read_buffer_x (SCM port, SCM buf);
SCM_INTERNAL SCM scm_port_read (SCM port);
SCM_INTERNAL SCM scm_port_write (SCM port);
SCM_INTERNAL SCM scm_port_read_buffer (SCM port);
SCM_INTERNAL SCM scm_port_write_buffer (SCM port);

View file

@ -26,6 +26,7 @@
(define-module (ice-9 ports)
#:use-module (rnrs bytevectors)
#:export (;; Definitions from ports.c.
%port-property
%set-port-property!
@ -153,6 +154,88 @@
(define-syntax-rule (port-buffer-bytevector buf) (vector-ref buf 0))
(define-syntax-rule (port-buffer-cur buf) (vector-ref buf 1))
(define-syntax-rule (port-buffer-end buf) (vector-ref buf 2))
(define-syntax-rule (port-buffer-has-eof? buf) (vector-ref buf 3))
(define-syntax-rule (set-port-buffer-cur! buf cur)
(vector-set! buf 1 cur))
(define-syntax-rule (set-port-buffer-end! buf end)
(vector-set! buf 2 end))
(define-syntax-rule (set-port-buffer-has-eof?! buf has-eof?)
(vector-set! buf 3 has-eof?))
(define (make-port-buffer size)
(vector (make-bytevector size 0) 0 0 #f))
(define (write-bytes port src start count)
(let ((written ((port-write port) port src start count)))
(unless (<= 0 written count)
(error "bad return from port write function" written))
(when (< written count)
(write-bytes port src (+ start written) (- count written)))))
(define (flush-output port)
(let* ((buf (port-write-buffer port))
(cur (port-buffer-cur buf))
(end (port-buffer-end buf)))
(when (< cur end)
;; 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
;; or force-output won't signal *another* error.
(set-port-buffer-cur! buf 0)
(set-port-buffer-end! buf 0)
(write-bytes port (port-buffer-bytevector buf) cur (- end cur)))))
(define (read-bytes port dst start count)
(let ((read ((port-read port) port dst start count)))
(unless (<= 0 read count)
(error "bad return from port read function" read))
read))
(define (fill-input port)
(let ((buf (port-read-buffer port)))
(cond
((or (< (port-buffer-cur buf) (port-buffer-end buf))
(port-buffer-has-eof? buf))
buf)
(else
(unless (input-port? port)
(error "not an input port" port))
(when (port-random-access? port)
(flush-output port))
(let* ((read-buffering (port-read-buffering port))
(buf (if (= (bytevector-length (port-buffer-bytevector buf))
read-buffering)
buf
(let ((buf (make-port-buffer read-buffering)))
(set-port-read-buffer! port buf)
buf)))
(bv (port-buffer-bytevector buf))
(start (port-buffer-end buf))
(count (- (bytevector-length bv) start))
(read (read-bytes port bv start count)))
(set-port-buffer-end! buf (+ start read))
(set-port-buffer-has-eof?! buf (zero? count))
buf)))))
(define (peek-byte port)
(let* ((buf (port-read-buffer port))
(cur (port-buffer-cur buf)))
(if (< cur (port-buffer-end buf))
(bytevector-u8-ref (port-buffer-bytevector buf) cur)
(let* ((buf (fill-input port))
(cur (port-buffer-cur buf)))
(if (< cur (port-buffer-end buf))
(bytevector-u8-ref (port-buffer-bytevector buf) cur)
the-eof-object)))))
;;; Current ports as parameters.
;;;