[Ecls-list] ongoing testing

Brian Spilsbury brian.spilsbury at gmail.com
Tue May 30 15:28:57 UTC 2006


It looks like an improvement, but I'd like to suggest something
slightly more radical:

Here are the CL operators that we need to support, in three conceptual groups.

*introspection*
input-stream-p, output-stream-p, interactive-stream-p, open-stream-p, streamp
stream-element-type, stream-external-format

*cl io*
read-byte, write-byte
peek-char, read-char, read-char-no-hang, unread-char, write-char,
terpri, fresh-line
read-line, write-string, write-line
y-or-n-p, yes-or-no-p
read-sequence, write-sequence
file-string-length
clear-input, clear-output
finish-output, force-output,

*device*
file-length, file-position
open, close
listen

We can bridge the gap between device and i/o by adding octet based io.

read-octets, write-octets

Now the cl file stream i/o operations can be defined in terms of octet
sequences, and this conversion forms the non-portable part of the
stream implementation.

The tricky part here is that we don't always know how many octets we
want to read until we've had a look at them -- this is particularly
true for character conversion.

So, I suggest an interface like so...

int ecl_read_octets(ecl_device *device,  unsigned char **buffer, size_t *count);
int ecl_write_octets(ecl_device *device, unsigned char **buffer, size_t *count);

These just give us access to a unsigned char * and the count that
we're allowed to consume -- we change the unsigned char * and the
count in order to indicate how much we've buffer space we've consumed.
The return value indicates error, eof, etc.

Actual writing io happens upon finish/force output, and we add an
fill-input interface for the other direction.

So, the whole device level api might look something like:
(but maybe as a bunch of function pointers in a struct, etc)

int close (ecl_device *device);
int read_octets (ecl_device *device, unsigned char **buffer, size_t *count);
int write_octets (ecl_device *device, unsigned char **buffer, size_t *count);
size_t flush (ecl_device *device, int wait_p);
size_t fill (ecl_device *device, int wait_p);
int file_length (ecl_device *device, size_t *length);
int get_file_position (ecl_device *device, ecl_device_position *position);
int set_file_position (ecl_device *device, ecl_device_position *position);

/* open is handled by various constructor functions on a per
device-kind basis; open-file, open-pipe, open-socket, ... */

/* clear-input is handled by read_octets and some higher level logic */

/* clear-output is handled by weird high level streams which buffer
user interaction in some intelligible manner, since we can't just
throw away random octets */

/* we replace finish-output and force-output with flush, since these
require higher level semantics to implement in any more meaningful
fashion */

/* listen becomes a call to read_octets and possibly fill_input */

*notes*
Translating characters to and from octets becomes the stream's
problem, but that's not a big deal. Most other buffering issues
(except for unread-char, clear-input and clear-output) are now handled
by the device level.

The device waits by delegating the problem to the lisp system's
scheduler (which might just be a blocking read/write call).
get/set_file_position just provides the current position in the
underlying file, or returns error -- the stream needs to compensate
for what it has or has not read so far, and this is quite complicated
in the case of character conversion.

Sockets can be implemented with the above devices, but extended with
bind, listen, connect, accept, etc.

Sockets add additional semantics with respect to associating streams
with addresses, and these might be folded back in, to meaningfully
associate file devices with file-names, but I haven't really thought
it through, so ymmv.

Regards,
Brian.




More information about the ecl-devel mailing list