mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-29 19:30:36 +02:00
* libguile/bitvectors.c: * libguile/bitvectors.h: * libguile/bytevectors.c: * libguile/bytevectors.h: * libguile/chars.c: * libguile/continuations.c: * libguile/control.c: * libguile/conv-integer.i.c: * libguile/conv-uinteger.i.c: * libguile/dynstack.c: * libguile/dynstack.h: * libguile/foreign.c: * libguile/frames.c: * libguile/frames.h: * libguile/gc-inline.h: * libguile/gc.h: * libguile/gsubr.c: * libguile/gsubr.h: * libguile/hash.c: * libguile/i18n.c: * libguile/instructions.c: * libguile/intrinsics.c: * libguile/intrinsics.h: * libguile/loader.c: * libguile/loader.h: * libguile/numbers.c: * libguile/numbers.h: * libguile/pairs.c: * libguile/ports-internal.h: * libguile/ports.c: * libguile/ports.h: * libguile/posix.c: * libguile/print.c: * libguile/print.h: * libguile/programs.c: * libguile/programs.h: * libguile/r6rs-ports.c: * libguile/random.c: * libguile/random.h: * libguile/scm.h: * libguile/socket.c: * libguile/srfi-4.c: * libguile/srfi-4.h: * libguile/stacks.c: * libguile/stime.c: * libguile/strings.c: * libguile/struct.c: * libguile/struct.h: * libguile/symbols.c: * libguile/threads.c: * libguile/threads.h: * libguile/uniform.c: * libguile/vm-engine.c: * libguile/vm.c: * libguile/vm.h: * libguile/vports.c: * test-suite/standalone/test-conversion.c: * test-suite/standalone/test-ffi-lib.c: * test-suite/standalone/test-scm-take-u8vector.c: * test-suite/standalone/test-srfi-4.c: Replace e.g. scm_t_uint8 with uint8_t.
378 lines
11 KiB
C
378 lines
11 KiB
C
/* ports-internal.h - internal-only declarations for ports.
|
|
|
|
Copyright 2013,2018
|
|
Free Software Foundation, Inc.
|
|
|
|
This file is part of Guile.
|
|
|
|
Guile is free software: you can redistribute it and/or modify it
|
|
under the terms of the GNU Lesser General Public License as published
|
|
by the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Guile is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with Guile. If not, see
|
|
<https://www.gnu.org/licenses/>. */
|
|
|
|
#ifndef SCM_PORTS_INTERNAL
|
|
#define SCM_PORTS_INTERNAL
|
|
|
|
#include <assert.h>
|
|
#include <iconv.h>
|
|
#include <string.h>
|
|
|
|
#include "libguile/bytevectors.h"
|
|
#include "libguile/list.h"
|
|
#include "libguile/pairs.h"
|
|
#include "libguile/ports.h"
|
|
#include "libguile/vectors.h"
|
|
|
|
typedef enum scm_t_port_type_flags {
|
|
/* Indicates that the port should be closed if it is garbage collected
|
|
while it is open. */
|
|
SCM_PORT_TYPE_NEEDS_CLOSE_ON_GC = 1 << 0
|
|
} scm_t_port_type_flags;
|
|
|
|
/* port-type description. */
|
|
struct scm_t_port_type
|
|
{
|
|
char *name;
|
|
int (*print) (SCM exp, SCM port, scm_print_state *pstate);
|
|
|
|
size_t (*c_read) (SCM port, SCM dst, size_t start, size_t count);
|
|
size_t (*c_write) (SCM port, SCM src, size_t start, size_t count);
|
|
SCM scm_read;
|
|
SCM scm_write;
|
|
|
|
int (*read_wait_fd) (SCM port);
|
|
int (*write_wait_fd) (SCM port);
|
|
|
|
scm_t_off (*seek) (SCM port, scm_t_off OFFSET, int WHENCE);
|
|
void (*close) (SCM port);
|
|
|
|
void (*get_natural_buffer_sizes) (SCM port, size_t *read_size,
|
|
size_t *write_size);
|
|
int (*random_access_p) (SCM port);
|
|
|
|
int (*input_waiting) (SCM port);
|
|
|
|
void (*truncate) (SCM port, scm_t_off length);
|
|
|
|
unsigned flags;
|
|
|
|
/* GOOPS tomfoolery. */
|
|
SCM input_class, output_class, input_output_class;
|
|
};
|
|
|
|
/* Port buffers.
|
|
|
|
It's important to avoid calling into the kernel too many times. For
|
|
that reason we buffer the input and output, using "port buffer"
|
|
objects. Port buffers are represented as vectors containing the
|
|
buffer, two cursors, and a flag. The bytes in a read buffer are laid
|
|
out like this:
|
|
|
|
|already read | not yet | invalid
|
|
| data | read | data
|
|
readbuf: #vu8(|r r r r r r r|u u u u u|x x x x x|)
|
|
^buf ^cur ^end ^size(buf)
|
|
|
|
Similarly for a write buffer:
|
|
|
|
|already written | not yet | invalid
|
|
| data | written | data
|
|
writebuf: #vu8(|w w w w w w w w |u u u u u|x x x x x|)
|
|
^buf ^cur ^end ^size(buf)
|
|
|
|
We use the same port buffer data structure for both purposes. Port
|
|
buffers are implemented as their own object so that they can be
|
|
atomically swapped in or out of ports, and as Scheme vectors so they
|
|
can be manipulated from Scheme. */
|
|
|
|
enum scm_port_buffer_field {
|
|
SCM_PORT_BUFFER_FIELD_BYTEVECTOR,
|
|
SCM_PORT_BUFFER_FIELD_CUR,
|
|
SCM_PORT_BUFFER_FIELD_END,
|
|
SCM_PORT_BUFFER_FIELD_HAS_EOF_P,
|
|
SCM_PORT_BUFFER_FIELD_POSITION,
|
|
SCM_PORT_BUFFER_FIELD_COUNT
|
|
};
|
|
|
|
/* The port buffers are exposed to Scheme, which can mutate their
|
|
fields. We have to do dynamic checks to ensure that
|
|
potentially-malicious Scheme doesn't invalidate our invariants.
|
|
However these dynamic checks are slow, so we need to avoid them where
|
|
they are unnecessary. An unnecessary check is a check which has
|
|
already been performed, or one which would already be performed by
|
|
the time that memory is accessed. Given that the "can_take",
|
|
"can_put", or "can_putback" functions are eventually called before
|
|
any access to the buffer, we hoist the necessary type checks the
|
|
can_foo and size functions, and otherwise assume that the cur and end
|
|
values are inums within the right ranges. */
|
|
|
|
static inline SCM
|
|
scm_port_buffer_bytevector (SCM buf)
|
|
{
|
|
return SCM_SIMPLE_VECTOR_REF (buf, SCM_PORT_BUFFER_FIELD_BYTEVECTOR);
|
|
}
|
|
|
|
static inline SCM
|
|
scm_port_buffer_cur (SCM buf)
|
|
{
|
|
return SCM_SIMPLE_VECTOR_REF (buf, SCM_PORT_BUFFER_FIELD_CUR);
|
|
}
|
|
|
|
static inline void
|
|
scm_port_buffer_set_cur (SCM buf, SCM cur)
|
|
{
|
|
SCM_SIMPLE_VECTOR_SET (buf, SCM_PORT_BUFFER_FIELD_CUR, cur);
|
|
}
|
|
|
|
static inline SCM
|
|
scm_port_buffer_end (SCM buf)
|
|
{
|
|
return SCM_SIMPLE_VECTOR_REF (buf, SCM_PORT_BUFFER_FIELD_END);
|
|
}
|
|
|
|
static inline void
|
|
scm_port_buffer_set_end (SCM buf, SCM end)
|
|
{
|
|
SCM_SIMPLE_VECTOR_SET (buf, SCM_PORT_BUFFER_FIELD_END, end);
|
|
}
|
|
|
|
static inline SCM
|
|
scm_port_buffer_has_eof_p (SCM buf)
|
|
{
|
|
return SCM_SIMPLE_VECTOR_REF (buf, SCM_PORT_BUFFER_FIELD_HAS_EOF_P);
|
|
}
|
|
|
|
static inline void
|
|
scm_port_buffer_set_has_eof_p (SCM buf, SCM has_eof_p)
|
|
{
|
|
SCM_SIMPLE_VECTOR_SET (buf, SCM_PORT_BUFFER_FIELD_HAS_EOF_P,
|
|
has_eof_p);
|
|
}
|
|
|
|
/* The port position object is a pair that is referenced by the port.
|
|
To make things easier for Scheme port code, it is also referenced by
|
|
port buffers. */
|
|
static inline SCM
|
|
scm_port_buffer_position (SCM buf)
|
|
{
|
|
return SCM_SIMPLE_VECTOR_REF (buf, SCM_PORT_BUFFER_FIELD_POSITION);
|
|
}
|
|
|
|
static inline SCM
|
|
scm_port_position_line (SCM position)
|
|
{
|
|
return scm_car (position);
|
|
}
|
|
|
|
static inline void
|
|
scm_port_position_set_line (SCM position, SCM line)
|
|
{
|
|
scm_set_car_x (position, line);
|
|
}
|
|
|
|
static inline SCM
|
|
scm_port_position_column (SCM position)
|
|
{
|
|
return scm_cdr (position);
|
|
}
|
|
|
|
static inline void
|
|
scm_port_position_set_column (SCM position, SCM column)
|
|
{
|
|
scm_set_cdr_x (position, column);
|
|
}
|
|
|
|
static inline size_t
|
|
scm_port_buffer_size (SCM buf)
|
|
{
|
|
SCM bv = scm_port_buffer_bytevector (buf);
|
|
if (SCM_LIKELY (SCM_BYTEVECTOR_P (bv)))
|
|
return SCM_BYTEVECTOR_LENGTH (bv);
|
|
scm_misc_error (NULL, "invalid port buffer ~a", scm_list_1 (bv));
|
|
return -1;
|
|
}
|
|
|
|
static inline void
|
|
scm_port_buffer_reset (SCM buf)
|
|
{
|
|
scm_port_buffer_set_cur (buf, SCM_INUM0);
|
|
scm_port_buffer_set_end (buf, SCM_INUM0);
|
|
}
|
|
|
|
static inline void
|
|
scm_port_buffer_reset_end (SCM buf)
|
|
{
|
|
scm_port_buffer_set_cur (buf, scm_from_size_t (scm_port_buffer_size (buf)));
|
|
scm_port_buffer_set_end (buf, scm_from_size_t (scm_port_buffer_size (buf)));
|
|
}
|
|
|
|
static inline size_t
|
|
scm_port_buffer_can_take (SCM buf, size_t *cur_out)
|
|
{
|
|
size_t cur, end;
|
|
cur = scm_to_size_t (scm_port_buffer_cur (buf));
|
|
end = scm_to_size_t (scm_port_buffer_end (buf));
|
|
if (end > scm_port_buffer_size (buf))
|
|
scm_misc_error (NULL, "invalid port buffer ~a", scm_list_1 (buf));
|
|
/* If something races and we end up with end < cur, signal the caller
|
|
to do a fill_input and centralize there. */
|
|
*cur_out = cur;
|
|
return end < cur ? 0 : end - cur;
|
|
}
|
|
|
|
static inline size_t
|
|
scm_port_buffer_can_put (SCM buf, size_t *end_out)
|
|
{
|
|
size_t end = scm_to_size_t (scm_port_buffer_end (buf));
|
|
if (end > scm_port_buffer_size (buf))
|
|
scm_misc_error (NULL, "invalid port buffer ~a", scm_list_1 (buf));
|
|
*end_out = end;
|
|
return scm_port_buffer_size (buf) - end;
|
|
}
|
|
|
|
static inline size_t
|
|
scm_port_buffer_can_putback (SCM buf)
|
|
{
|
|
size_t cur = scm_to_size_t (scm_port_buffer_cur (buf));
|
|
if (cur > scm_port_buffer_size (buf))
|
|
scm_misc_error (NULL, "invalid port buffer ~a", scm_list_1 (buf));
|
|
return cur;
|
|
}
|
|
|
|
static inline void
|
|
scm_port_buffer_did_take (SCM buf, size_t prev_cur, size_t count)
|
|
{
|
|
scm_port_buffer_set_cur (buf, SCM_I_MAKINUM (prev_cur + count));
|
|
}
|
|
|
|
static inline void
|
|
scm_port_buffer_did_put (SCM buf, size_t prev_end, size_t count)
|
|
{
|
|
scm_port_buffer_set_end (buf, SCM_I_MAKINUM (prev_end + count));
|
|
}
|
|
|
|
static inline const uint8_t *
|
|
scm_port_buffer_take_pointer (SCM buf, size_t cur)
|
|
{
|
|
signed char *ret = SCM_BYTEVECTOR_CONTENTS (scm_port_buffer_bytevector (buf));
|
|
return ((uint8_t *) ret) + cur;
|
|
}
|
|
|
|
static inline uint8_t *
|
|
scm_port_buffer_put_pointer (SCM buf, size_t end)
|
|
{
|
|
signed char *ret = SCM_BYTEVECTOR_CONTENTS (scm_port_buffer_bytevector (buf));
|
|
return ((uint8_t *) ret) + end;
|
|
}
|
|
|
|
static inline size_t
|
|
scm_port_buffer_take (SCM buf, uint8_t *dst, size_t count,
|
|
size_t cur, size_t avail)
|
|
{
|
|
if (avail < count)
|
|
count = avail;
|
|
if (dst)
|
|
memcpy (dst, scm_port_buffer_take_pointer (buf, cur), count);
|
|
scm_port_buffer_did_take (buf, cur, count);
|
|
return count;
|
|
}
|
|
|
|
static inline size_t
|
|
scm_port_buffer_put (SCM buf, const uint8_t *src, size_t count,
|
|
size_t end, size_t avail)
|
|
{
|
|
if (avail < count)
|
|
count = avail;
|
|
if (src)
|
|
memcpy (scm_port_buffer_put_pointer (buf, end), src, count);
|
|
scm_port_buffer_did_put (buf, end, count);
|
|
return count;
|
|
}
|
|
|
|
static inline void
|
|
scm_port_buffer_putback (SCM buf, const uint8_t *src, size_t count,
|
|
size_t cur)
|
|
{
|
|
assert (count <= cur);
|
|
|
|
/* Sometimes used to move around data within a buffer, so we must use
|
|
memmove. */
|
|
cur -= count;
|
|
scm_port_buffer_set_cur (buf, scm_from_size_t (cur));
|
|
memmove (SCM_BYTEVECTOR_CONTENTS (scm_port_buffer_bytevector (buf)) + cur,
|
|
src, count);
|
|
}
|
|
|
|
struct scm_t_port
|
|
{
|
|
/* Source location information. */
|
|
SCM file_name;
|
|
SCM position;
|
|
|
|
/* Port buffers. */
|
|
SCM read_buf;
|
|
SCM write_buf;
|
|
SCM write_buf_aux;
|
|
|
|
/* All ports have read and write buffers; an unbuffered port simply
|
|
has a one-byte buffer. However unreading bytes can expand the read
|
|
buffer, but that doesn't mean that we want to increase the input
|
|
buffering. For that reason `read_buffering' is a separate
|
|
indication of how many characters to buffer on the read side.
|
|
There isn't a write_buf_size because there isn't an
|
|
`unwrite-byte'. */
|
|
size_t read_buffering;
|
|
|
|
/* Reads and writes can proceed concurrently, but we don't want to
|
|
start any read or write after close() has been called. So we have
|
|
a refcount which is positive if close has not yet been called.
|
|
Reading, writing, and the like temporarily increments this
|
|
refcount, provided it was nonzero to start with. */
|
|
uint32_t refcount;
|
|
|
|
/* True if the port is random access. Implies that the buffers must
|
|
be flushed before switching between reading and writing, seeking,
|
|
and so on. */
|
|
uint32_t rw_random : 1;
|
|
uint32_t at_stream_start_for_bom_read : 1;
|
|
uint32_t at_stream_start_for_bom_write : 1;
|
|
|
|
/* Character encoding support. */
|
|
SCM encoding; /* A symbol of upper-case ASCII. */
|
|
SCM conversion_strategy; /* A symbol; either substitute, error, or escape. */
|
|
|
|
/* This is the same as pt->encoding, except if `encoding' is UTF-16 or
|
|
UTF-32, in which case this is UTF-16LE or a similar
|
|
byte-order-specialed version of UTF-16 or UTF-32. This is a
|
|
separate field from `encoding' because being just plain UTF-16 or
|
|
UTF-32 has an additional meaning, being that we should consume and
|
|
produce byte order marker codepoints as appropriate. Set to #f
|
|
before the iconv descriptors have been opened. */
|
|
SCM precise_encoding; /* with iconv_lock */
|
|
iconv_t input_cd; /* with iconv_lock */
|
|
iconv_t output_cd; /* with iconv_lock */
|
|
|
|
/* Port properties. */
|
|
SCM alist;
|
|
};
|
|
|
|
#define SCM_UNICODE_BOM 0xFEFFUL /* Unicode byte-order mark */
|
|
|
|
#define SCM_FILENAME(x) (SCM_PORT (x)->file_name)
|
|
#define SCM_SET_FILENAME(x, n) (SCM_PORT (x)->file_name = (n))
|
|
|
|
SCM_INTERNAL void scm_port_acquire_iconv_descriptors (SCM port,
|
|
iconv_t *input_cd,
|
|
iconv_t *output_cd);
|
|
SCM_INTERNAL void scm_port_release_iconv_descriptors (SCM port);
|
|
|
|
#endif
|