Reorganize `io-ports.test' following the R6RS sectioning.
[guile-r6rs-libs.git] / src / ports.c
blob28d73c6725c1d17c1bcc6a4c744a27fdf42164fc
1 /* Guile-R6RS-Libs --- Implementation of R6RS standard libraries.
2 Copyright (C) 2007, 2008, 2009 Ludovic Courtès <ludo@gnu.org>
4 Guile-R6RS-Libs is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
9 Guile-R6RS-Libs is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with Guile-R6RS-Libs; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
22 #include "compat.h"
24 #include <libguile.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <assert.h>
29 #include "ports.h"
30 #include "bytevector.h"
31 #include "utils.h"
34 /* Unimplemented features. */
37 /* Transoders are currently not implemented since Guile 1.8 is not
38 Unicode-capable. Thus, most of the code here assumes the use of the
39 binary transcoder. */
40 static inline void
41 transcoders_not_implemented (void)
43 fprintf (stderr, "%s: warning: transcoders not implemented\n",
44 PACKAGE_NAME);
48 /* End-of-file object. */
50 SCM_DEFINE (scm_r6rs_eof_object, "eof-object", 0, 0, 0,
51 (void),
52 "Return the end-of-file object.")
53 #define FUNC_NAME s_scm_r6rs_eof_object
55 return (SCM_EOF_VAL);
57 #undef FUNC_NAME
60 /* Input ports. */
62 #ifndef MIN
63 # define MIN(a,b) ((a) < (b) ? (a) : (b))
64 #endif
66 /* Bytevector input ports or "bip" for short. */
67 static scm_t_bits bytevector_input_port_type = 0;
69 static inline SCM
70 make_bip (SCM bv)
72 SCM port;
73 char *c_bv;
74 unsigned c_len;
75 scm_t_port *c_port;
76 const unsigned long mode_bits = SCM_OPN | SCM_RDNG;
78 port = scm_new_port_table_entry (bytevector_input_port_type);
80 /* Prevent BV from being GC'd. */
81 SCM_SETSTREAM (port, SCM_UNPACK (bv));
83 /* Have the port directly access the bytevector. */
84 c_bv = (char *) SCM_R6RS_BYTEVECTOR_CONTENTS (bv);
85 c_len = SCM_R6RS_BYTEVECTOR_LENGTH (bv);
87 c_port = SCM_PTAB_ENTRY (port);
88 c_port->read_pos = c_port->read_buf = (unsigned char *) c_bv;
89 c_port->read_end = (unsigned char *) c_bv + c_len;
90 c_port->read_buf_size = c_len;
92 /* Mark PORT as open, readable and unbuffered (hmm, how elegant...). */
93 SCM_SET_CELL_TYPE (port, bytevector_input_port_type | mode_bits);
95 return port;
98 static SCM
99 bip_mark (SCM port)
101 /* Mark the underlying bytevector. */
102 return (SCM_PACK (SCM_STREAM (port)));
105 #ifndef HAVE_SCM_SET_PORT_READ
107 static int
108 bip_fill_input (SCM port)
110 int result;
111 scm_t_port *c_port = SCM_PTAB_ENTRY (port);
113 if (c_port->read_pos >= c_port->read_end)
114 result = EOF;
115 else
116 result = (int) *c_port->read_pos;
118 return result;
121 #else /* HAVE_SCM_SET_PORT_READ */
123 static size_t
124 bip_read (SCM port, void *buffer, size_t size)
126 size_t remaining, count;
127 scm_t_port *c_port = SCM_PTAB_ENTRY (port);
129 remaining = c_port->read_end - c_port->read_pos;
130 count = MIN (remaining, size);
132 memcpy (buffer, c_port->read_pos, count);
133 c_port->read_pos += count;
135 return count;
138 #endif /* HAVE_SCM_SET_PORT_READ */
140 static off_t
141 bip_seek (SCM port, off_t offset, int whence)
142 #define FUNC_NAME "bip_seek"
144 off_t c_result = 0;
145 scm_t_port *c_port = SCM_PTAB_ENTRY (port);
147 switch (whence)
149 case SEEK_CUR:
150 offset += c_port->read_pos - c_port->read_buf;
151 /* Fall through. */
153 case SEEK_SET:
154 if (c_port->read_buf + offset < c_port->read_end)
156 c_port->read_pos = c_port->read_buf + offset;
157 c_result = offset;
159 else
160 scm_out_of_range (FUNC_NAME, scm_from_int (offset));
161 break;
163 case SEEK_END:
164 if (c_port->read_end - offset >= c_port->read_buf)
166 c_port->read_pos = c_port->read_end - offset;
167 c_result = c_port->read_pos - c_port->read_buf;
169 else
170 scm_out_of_range (FUNC_NAME, scm_from_int (offset));
171 break;
173 default:
174 scm_wrong_type_arg_msg (FUNC_NAME, 0, port,
175 "invalid `seek' parameter");
178 return c_result;
180 #undef FUNC_NAME
183 /* Instantiate the bytevector input port type. */
184 static inline void
185 initialize_bytevector_input_ports (void)
187 bytevector_input_port_type =
188 scm_make_port_type ("r6rs-bytevector-input-port",
189 PORT_FILL_INPUT_METHOD (bip_fill_input),
190 NULL);
192 scm_set_port_read (bytevector_input_port_type, bip_read);
193 scm_set_port_mark (bytevector_input_port_type, bip_mark);
194 scm_set_port_seek (bytevector_input_port_type, bip_seek);
198 SCM_DEFINE (scm_r6rs_open_bytevector_input_port,
199 "open-bytevector-input-port", 1, 1, 0,
200 (SCM bv, SCM transcoder),
201 "Return an input port whose contents are drawn from "
202 "bytevector @var{bv}.")
203 #define FUNC_NAME s_scm_r6rs_open_bytevector_input_port
205 SCM_VALIDATE_R6RS_BYTEVECTOR (1, bv);
206 if (transcoder != SCM_UNDEFINED)
207 transcoders_not_implemented ();
209 return (make_bip (bv));
211 #undef FUNC_NAME
215 /* Custom binary input port ("cbip" for short). */
217 static scm_t_bits custom_binary_input_port_type = 0;
219 /* Size of the buffer embedded in custom binary input ports. */
220 #define CBIP_BUFFER_SIZE 4096
222 /* Return the bytevector associated with PORT. */
223 #define SCM_R6RS_CBIP_BYTEVECTOR(_port) \
224 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 0)
226 /* Return the various procedures of PORT. */
227 #define SCM_R6RS_CBIP_READ_PROC(_port) \
228 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 1)
229 #define SCM_R6RS_CBIP_GET_POSITION_PROC(_port) \
230 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 2)
231 #define SCM_R6RS_CBIP_SET_POSITION_PROC(_port) \
232 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 3)
233 #define SCM_R6RS_CBIP_CLOSE_PROC(_port) \
234 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 4)
237 static inline SCM
238 make_cbip (SCM read_proc, SCM get_position_proc,
239 SCM set_position_proc, SCM close_proc)
241 SCM port, bv, method_vector;
242 char *c_bv;
243 unsigned c_len;
244 scm_t_port *c_port;
245 const unsigned long mode_bits = SCM_OPN | SCM_RDNG;
247 /* Use a bytevector as the underlying buffer. */
248 c_len = CBIP_BUFFER_SIZE;
249 bv = scm_r6rs_c_make_bytevector (c_len);
250 c_bv = (char *) SCM_R6RS_BYTEVECTOR_CONTENTS (bv);
252 /* Store the various methods and bytevector in a vector. */
253 method_vector = scm_c_make_vector (5, SCM_BOOL_F);
254 SCM_SIMPLE_VECTOR_SET (method_vector, 0, bv);
255 SCM_SIMPLE_VECTOR_SET (method_vector, 1, read_proc);
256 SCM_SIMPLE_VECTOR_SET (method_vector, 2, get_position_proc);
257 SCM_SIMPLE_VECTOR_SET (method_vector, 3, set_position_proc);
258 SCM_SIMPLE_VECTOR_SET (method_vector, 4, close_proc);
260 port = scm_new_port_table_entry (custom_binary_input_port_type);
262 /* Attach it the method vector. */
263 SCM_SETSTREAM (port, SCM_UNPACK (method_vector));
265 /* Have the port directly access the buffer (bytevector). */
266 c_port = SCM_PTAB_ENTRY (port);
267 c_port->read_pos = c_port->read_buf = (unsigned char *) c_bv;
268 c_port->read_end = (unsigned char *) c_bv;
269 c_port->read_buf_size = c_len;
271 /* Mark PORT as open, readable and unbuffered (hmm, how elegant...). */
272 SCM_SET_CELL_TYPE (port, custom_binary_input_port_type | mode_bits);
274 return port;
277 static SCM
278 cbip_mark (SCM port)
280 /* Mark the underlying bytevector and methods. */
281 return (SCM_PACK (SCM_STREAM (port)));
284 static int
285 cbip_fill_input (SCM port)
286 #define FUNC_NAME "cbip_fill_input"
288 int result;
289 scm_t_port *c_port = SCM_PTAB_ENTRY (port);
291 again:
292 if (c_port->read_pos >= c_port->read_end)
294 /* Invoke the user's `read!' procedure. */
295 unsigned c_octets;
296 SCM bv, read_proc, octets;
298 /* Use the bytevector associated with PORT as the buffer passed to the
299 `read!' procedure, thereby avoiding additional allocations. */
300 bv = SCM_R6RS_CBIP_BYTEVECTOR (port);
301 read_proc = SCM_R6RS_CBIP_READ_PROC (port);
303 /* The assumption here is that C_PORT's internal buffer wasn't changed
304 behind our back. */
305 assert (c_port->read_buf ==
306 (unsigned char *) SCM_R6RS_BYTEVECTOR_CONTENTS (bv));
307 assert (c_port->read_buf_size == SCM_R6RS_BYTEVECTOR_LENGTH (bv));
309 octets = scm_call_3 (read_proc, bv, SCM_INUM0,
310 SCM_I_MAKINUM (CBIP_BUFFER_SIZE));
311 c_octets = scm_to_uint (octets);
313 c_port->read_pos = (unsigned char *) SCM_R6RS_BYTEVECTOR_CONTENTS (bv);
314 c_port->read_end = (unsigned char *) c_port->read_pos + c_octets;
316 if (c_octets > 0)
317 goto again;
318 else
319 result = EOF;
321 else
322 result = (int) *c_port->read_pos;
324 return result;
326 #undef FUNC_NAME
328 static off_t
329 cbip_seek (SCM port, off_t offset, int whence)
330 #define FUNC_NAME "cbip_seek"
332 SCM result;
333 off_t c_result = 0;
335 switch (whence)
337 case SEEK_CUR:
339 SCM get_position_proc;
341 get_position_proc = SCM_R6RS_CBIP_GET_POSITION_PROC (port);
342 if (SCM_LIKELY (scm_is_true (get_position_proc)))
343 result = scm_call_0 (get_position_proc);
344 else
345 scm_wrong_type_arg_msg (FUNC_NAME, 0, port,
346 "R6RS custom binary input port does not "
347 "support `port-position'");
349 offset += scm_to_int (result);
350 /* Fall through. */
353 case SEEK_SET:
355 SCM set_position_proc;
357 set_position_proc = SCM_R6RS_CBIP_SET_POSITION_PROC (port);
358 if (SCM_LIKELY (scm_is_true (set_position_proc)))
359 result = scm_call_1 (set_position_proc, scm_from_int (offset));
360 else
361 scm_wrong_type_arg_msg (FUNC_NAME, 0, port,
362 "R6RS custom binary input port does not "
363 "support `set-port-position!'");
365 /* Assuming setting the position succeeded. */
366 c_result = offset;
367 break;
370 default:
371 /* `SEEK_END' cannot be supported. */
372 scm_wrong_type_arg_msg (FUNC_NAME, 0, port,
373 "R6RS custom binary input ports do not "
374 "support `SEEK_END'");
377 return c_result;
379 #undef FUNC_NAME
381 static int
382 cbip_close (SCM port)
384 SCM close_proc;
386 close_proc = SCM_R6RS_CBIP_CLOSE_PROC (port);
387 if (scm_is_true (close_proc))
388 /* Invoke the `close' thunk. */
389 scm_call_0 (close_proc);
391 return 1;
395 SCM_DEFINE (scm_r6rs_make_custom_binary_input_port,
396 "make-custom-binary-input-port", 5, 0, 0,
397 (SCM id, SCM read_proc, SCM get_position_proc,
398 SCM set_position_proc, SCM close_proc),
399 "Return a new custom binary input port whose input is drained "
400 "by invoking @var{read_proc} and passing it a bytevector, an "
401 "index where octets should be written, and an octet count.")
402 #define FUNC_NAME s_scm_r6rs_make_custom_binary_input_port
404 SCM_VALIDATE_STRING (1, id);
405 SCM_VALIDATE_PROC (2, read_proc);
407 if (!scm_is_false (get_position_proc))
408 SCM_VALIDATE_PROC (3, get_position_proc);
410 if (!scm_is_false (set_position_proc))
411 SCM_VALIDATE_PROC (4, set_position_proc);
413 if (!scm_is_false (close_proc))
414 SCM_VALIDATE_PROC (5, close_proc);
416 return (make_cbip (read_proc, get_position_proc, set_position_proc,
417 close_proc));
419 #undef FUNC_NAME
422 /* Instantiate the custom binary input port type. */
423 static inline void
424 initialize_custom_binary_input_ports (void)
426 custom_binary_input_port_type =
427 scm_make_port_type ("r6rs-custom-binary-input-port",
428 cbip_fill_input, NULL);
430 scm_set_port_mark (custom_binary_input_port_type, cbip_mark);
431 scm_set_port_seek (custom_binary_input_port_type, cbip_seek);
432 scm_set_port_close (custom_binary_input_port_type, cbip_close);
437 /* Binary input. */
439 /* We currently don't support specific binary input ports. */
440 #define SCM_VALIDATE_R6RS_BINARY_INPUT_PORT SCM_VALIDATE_OPINPORT
442 SCM_DEFINE (scm_r6rs_get_u8, "get-u8", 1, 0, 0,
443 (SCM port),
444 "Read an octet from @var{port}, a binary input port, "
445 "blocking as necessary.")
446 #define FUNC_NAME s_scm_r6rs_get_u8
448 SCM result;
449 int c_result;
451 SCM_VALIDATE_R6RS_BINARY_INPUT_PORT (1, port);
453 c_result = scm_getc (port);
454 if (c_result == EOF)
455 result = SCM_EOF_VAL;
456 else
457 result = SCM_I_MAKINUM ((unsigned char) c_result);
459 return result;
461 #undef FUNC_NAME
463 SCM_DEFINE (scm_r6rs_lookahead_u8, "lookahead-u8", 1, 0, 0,
464 (SCM port),
465 "Like @code{get-u8} but does not update @var{port} to "
466 "point past the octet.")
467 #define FUNC_NAME s_scm_r6rs_lookahead_u8
469 SCM result;
471 SCM_VALIDATE_R6RS_BINARY_INPUT_PORT (1, port);
473 result = scm_peek_char (port);
474 if (SCM_CHARP (result))
475 result = SCM_I_MAKINUM ((signed char) SCM_CHAR (result));
476 else
477 result = SCM_EOF_VAL;
479 return result;
481 #undef FUNC_NAME
483 SCM_DEFINE (scm_r6rs_get_bytevector_n, "get-bytevector-n", 2, 0, 0,
484 (SCM port, SCM count),
485 "Read @var{count} octets from @var{port}, blocking as "
486 "necessary and return a bytevector containing the octets "
487 "read. If fewer bytes are available, a bytevector smaller "
488 "than @var{count} is returned.")
489 #define FUNC_NAME s_scm_r6rs_get_bytevector_n
491 SCM result;
492 char *c_bv;
493 unsigned c_count;
494 size_t c_read;
496 SCM_VALIDATE_R6RS_BINARY_INPUT_PORT (1, port);
497 c_count = scm_to_uint (count);
499 result = scm_r6rs_c_make_bytevector (c_count);
500 c_bv = (char *) SCM_R6RS_BYTEVECTOR_CONTENTS (result);
502 if (SCM_LIKELY (c_count > 0))
503 /* XXX: `scm_c_read ()' does not update the port position. */
504 c_read = scm_c_read (port, c_bv, c_count);
505 else
506 /* Don't invoke `scm_c_read ()' since it may block. */
507 c_read = 0;
509 if ((c_read == 0) && (c_count > 0))
511 if (SCM_EOF_OBJECT_P (scm_peek_char (port)))
512 result = SCM_EOF_VAL;
513 else
514 result = scm_r6rs_null_bytevector;
516 else
518 if (c_read < c_count)
519 result = scm_r6rs_c_shrink_bytevector (result, c_read);
522 return result;
524 #undef FUNC_NAME
526 SCM_DEFINE (scm_r6rs_get_bytevector_n_x, "get-bytevector-n!", 4, 0, 0,
527 (SCM port, SCM bv, SCM start, SCM count),
528 "Read @var{count} bytes from @var{port} and store them "
529 "in @var{bv} starting at index @var{start}. Return either "
530 "the number of bytes actually read or the end-of-file "
531 "object.")
532 #define FUNC_NAME s_scm_r6rs_get_bytevector_n_x
534 SCM result;
535 char *c_bv;
536 unsigned c_start, c_count, c_len;
537 size_t c_read;
539 SCM_VALIDATE_R6RS_BINARY_INPUT_PORT (1, port);
540 SCM_VALIDATE_R6RS_BYTEVECTOR (2, bv);
541 c_start = scm_to_uint (start);
542 c_count = scm_to_uint (count);
544 c_bv = (char *) SCM_R6RS_BYTEVECTOR_CONTENTS (bv);
545 c_len = SCM_R6RS_BYTEVECTOR_LENGTH (bv);
547 if (SCM_UNLIKELY (c_start + c_count > c_len))
548 scm_out_of_range (FUNC_NAME, count);
550 if (SCM_LIKELY (c_count > 0))
551 c_read = scm_c_read (port, c_bv + c_start, c_count);
552 else
553 /* Don't invoke `scm_c_read ()' since it may block. */
554 c_read = 0;
556 if ((c_read == 0) && (c_count > 0))
558 if (SCM_EOF_OBJECT_P (scm_peek_char (port)))
559 result = SCM_EOF_VAL;
560 else
561 result = SCM_I_MAKINUM (0);
563 else
564 result = scm_from_size_t (c_read);
566 return result;
568 #undef FUNC_NAME
571 SCM_DEFINE (scm_r6rs_get_bytevector_some, "get-bytevector-some", 1, 0, 0,
572 (SCM port),
573 "Read from @var{port}, blocking as necessary, until data "
574 "are available or and end-of-file is reached. Return either "
575 "a new bytevector containing the data read or the "
576 "end-of-file object.")
577 #define FUNC_NAME s_scm_r6rs_get_bytevector_some
579 /* Read at least one byte, unless the end-of-file is already reached, and
580 read while characters are available (buffered). */
582 SCM result;
583 char *c_bv;
584 unsigned c_len;
585 size_t c_total;
587 SCM_VALIDATE_R6RS_BINARY_INPUT_PORT (1, port);
589 c_len = 4096;
590 c_bv = (char *) scm_gc_malloc (c_len, SCM_GC_BYTEVECTOR);
591 c_total = 0;
595 int c_chr;
597 if (c_total + 1 > c_len)
599 /* Grow the bytevector. */
600 c_bv = (char *) scm_gc_realloc (c_bv, c_len, c_len * 2,
601 SCM_GC_BYTEVECTOR);
602 c_len *= 2;
605 /* We can't use `scm_c_read ()' since it blocks. */
606 c_chr = scm_getc (port);
607 if (c_chr != EOF)
609 c_bv[c_total] = (char) c_chr;
610 c_total++;
613 while ((scm_is_true (scm_char_ready_p (port)))
614 && (!SCM_EOF_OBJECT_P (scm_peek_char (port))));
616 if (c_total == 0)
618 result = SCM_EOF_VAL;
619 scm_gc_free (c_bv, c_len, SCM_GC_BYTEVECTOR);
621 else
623 if (c_len > c_total)
625 /* Shrink the bytevector. */
626 c_bv = (char *) scm_gc_realloc (c_bv, c_len, c_total,
627 SCM_GC_BYTEVECTOR);
628 c_len = (unsigned) c_total;
631 result = scm_r6rs_c_take_bytevector ((signed char *) c_bv, c_len);
634 return result;
636 #undef FUNC_NAME
638 SCM_DEFINE (scm_r6rs_get_bytevector_all, "get-bytevector-all", 1, 0, 0,
639 (SCM port),
640 "Read from @var{port}, blocking as necessary, until "
641 "the end-of-file is reached. Return either "
642 "a new bytevector containing the data read or the "
643 "end-of-file object (if no data were available).")
644 #define FUNC_NAME s_scm_r6rs_get_bytevector_all
646 SCM result;
647 char *c_bv;
648 unsigned c_len, c_count;
649 size_t c_read, c_total;
651 SCM_VALIDATE_R6RS_BINARY_INPUT_PORT (1, port);
653 c_len = c_count = 4096;
654 c_bv = (char *) scm_gc_malloc (c_len, SCM_GC_BYTEVECTOR);
655 c_total = c_read = 0;
659 if (c_total + c_read > c_len)
661 /* Grow the bytevector. */
662 c_bv = (char *) scm_gc_realloc (c_bv, c_len, c_len * 2,
663 SCM_GC_BYTEVECTOR);
664 c_count = c_len;
665 c_len *= 2;
668 /* `scm_c_read ()' blocks until C_COUNT bytes are available or EOF is
669 reached. */
670 c_read = scm_c_read (port, c_bv + c_total, c_count);
671 c_total += c_read, c_count -= c_read;
673 while (!SCM_EOF_OBJECT_P (scm_peek_char (port)));
675 if (c_total == 0)
677 result = SCM_EOF_VAL;
678 scm_gc_free (c_bv, c_len, SCM_GC_BYTEVECTOR);
680 else
682 if (c_len > c_total)
684 /* Shrink the bytevector. */
685 c_bv = (char *) scm_gc_realloc (c_bv, c_len, c_total,
686 SCM_GC_BYTEVECTOR);
687 c_len = (unsigned) c_total;
690 result = scm_r6rs_c_take_bytevector ((signed char *) c_bv, c_len);
693 return result;
695 #undef FUNC_NAME
699 /* Binary output. */
701 /* We currently don't support specific binary input ports. */
702 #define SCM_VALIDATE_R6RS_BINARY_OUTPUT_PORT SCM_VALIDATE_OPOUTPORT
705 SCM_DEFINE (scm_r6rs_put_u8, "put-u8", 2, 0, 0,
706 (SCM port, SCM octet),
707 "Write @var{octet} to binary port @var{port}.")
708 #define FUNC_NAME s_scm_r6rs_put_u8
710 scm_t_uint8 c_octet;
712 SCM_VALIDATE_R6RS_BINARY_OUTPUT_PORT (1, port);
713 c_octet = scm_to_uint8 (octet);
715 scm_putc ((char) c_octet, port);
717 return SCM_UNSPECIFIED;
719 #undef FUNC_NAME
721 SCM_DEFINE (scm_r6rs_put_bytevector, "put-bytevector", 2, 2, 0,
722 (SCM port, SCM bv, SCM start, SCM count),
723 "Write the contents of @var{bv} to @var{port}, optionally "
724 "starting at index @var{start} and limiting to @var{count} "
725 "octets.")
726 #define FUNC_NAME s_scm_r6rs_put_bytevector
728 char *c_bv;
729 unsigned c_start, c_count, c_len;
731 SCM_VALIDATE_R6RS_BINARY_OUTPUT_PORT (1, port);
732 SCM_VALIDATE_R6RS_BYTEVECTOR (2, bv);
734 c_len = SCM_R6RS_BYTEVECTOR_LENGTH (bv);
735 c_bv = (char *) SCM_R6RS_BYTEVECTOR_CONTENTS (bv);
737 if (start != SCM_UNDEFINED)
739 c_start = scm_to_uint (start);
741 if (count != SCM_UNDEFINED)
743 c_count = scm_to_uint (count);
744 if (SCM_UNLIKELY (c_start + c_count > c_len))
745 scm_out_of_range (FUNC_NAME, count);
747 else
749 if (SCM_UNLIKELY (c_start >= c_len))
750 scm_out_of_range (FUNC_NAME, start);
751 else
752 c_count = c_len - c_start;
755 else
756 c_start = 0, c_count = c_len;
758 scm_c_write (port, c_bv + c_start, c_count);
760 return SCM_UNSPECIFIED;
762 #undef FUNC_NAME
766 /* Bytevector output port ("bop" for short). */
768 /* Implementation of "bops".
770 Each bop has an internal buffer, of type `scm_t_bop_buffer', attached to
771 it. The procedure returned along with the output port is actually an
772 applicable SMOB. The SMOB holds a reference to the port. When applied,
773 the SMOB swallows the port's internal buffer, turning it into a
774 bytevector, and resets it.
776 XXX: Access to a bop's internal buffer is not thread-safe. */
778 static scm_t_bits bytevector_output_port_type = 0;
780 SCM_SMOB (bytevector_output_port_procedure,
781 "r6rs-bytevector-output-port-procedure",
784 #define SCM_GC_BOP "r6rs-bytevector-output-port"
785 #define SCM_BOP_BUFFER_INITIAL_SIZE 4096
787 /* Representation of a bop's internal buffer. */
788 typedef struct
790 size_t total_len;
791 size_t len;
792 size_t pos;
793 char *buffer;
794 } scm_t_bop_buffer;
797 /* Accessing a bop's buffer. */
798 #define SCM_R6RS_BOP_BUFFER(_port) \
799 ((scm_t_bop_buffer *) SCM_STREAM (_port))
800 #define SCM_R6RS_SET_BOP_BUFFER(_port, _buf) \
801 (SCM_SETSTREAM ((_port), (scm_t_bits) (_buf)))
804 static inline void
805 bop_buffer_init (scm_t_bop_buffer *buf)
807 buf->total_len = buf->len = buf->pos = 0;
808 buf->buffer = NULL;
811 static inline void
812 bop_buffer_grow (scm_t_bop_buffer *buf, size_t min_size)
814 char *new_buf;
815 size_t new_size;
817 for (new_size = buf->total_len
818 ? buf->total_len : SCM_BOP_BUFFER_INITIAL_SIZE;
819 new_size < min_size;
820 new_size *= 2);
822 if (buf->buffer)
823 new_buf = scm_gc_realloc ((void *) buf->buffer, buf->total_len,
824 new_size, SCM_GC_BOP);
825 else
826 new_buf = scm_gc_malloc (new_size, SCM_GC_BOP);
828 buf->buffer = new_buf;
829 buf->total_len = new_size;
832 static inline SCM
833 make_bop (void)
835 SCM port, bop_proc;
836 scm_t_port *c_port;
837 scm_t_bop_buffer *buf;
838 const unsigned long mode_bits = SCM_OPN | SCM_WRTNG;
840 port = scm_new_port_table_entry (bytevector_output_port_type);
842 buf = (scm_t_bop_buffer *) scm_gc_malloc (sizeof (* buf), SCM_GC_BOP);
843 bop_buffer_init (buf);
845 c_port = SCM_PTAB_ENTRY (port);
846 c_port->write_buf = c_port->write_pos = c_port->write_end = NULL;
847 c_port->write_buf_size = 0;
849 SCM_R6RS_SET_BOP_BUFFER (port, buf);
851 /* Mark PORT as open and writable. */
852 SCM_SET_CELL_TYPE (port, bytevector_output_port_type | mode_bits);
854 /* Make the bop procedure. */
855 SCM_NEWSMOB (bop_proc, bytevector_output_port_procedure,
856 SCM_PACK (port));
858 return (scm_values (scm_list_2 (port, bop_proc)));
861 static size_t
862 bop_free (SCM port)
864 /* The port itself is necessarily freed _after_ the bop proc, since the bop
865 proc holds a reference to it. Thus we can safely free the internal
866 buffer when the bop becomes unreferenced. */
867 scm_t_bop_buffer *buf;
869 buf = SCM_R6RS_BOP_BUFFER (port);
870 if (buf->buffer)
871 scm_gc_free (buf->buffer, buf->total_len, SCM_GC_BOP);
873 scm_gc_free (buf, sizeof (* buf), SCM_GC_BOP);
875 return 0;
878 /* Write SIZE octets from DATA to PORT. */
879 static void
880 bop_write (SCM port, const void *data, size_t size)
882 scm_t_bop_buffer *buf;
884 buf = SCM_R6RS_BOP_BUFFER (port);
886 if (buf->pos + size > buf->total_len)
887 bop_buffer_grow (buf, buf->pos + size);
889 memcpy (buf->buffer + buf->pos, data, size);
890 buf->pos += size;
891 buf->len = (buf->len > buf->pos) ? buf->len : buf->pos;
894 static off_t
895 bop_seek (SCM port, off_t offset, int whence)
896 #define FUNC_NAME "bop_seek"
898 scm_t_bop_buffer *buf;
900 buf = SCM_R6RS_BOP_BUFFER (port);
901 switch (whence)
903 case SEEK_CUR:
904 offset += (off_t) buf->pos;
905 /* Fall through. */
907 case SEEK_SET:
908 if (offset > buf->len)
909 scm_out_of_range (FUNC_NAME, scm_from_int (offset));
910 else
911 buf->pos = offset;
912 break;
914 case SEEK_END:
915 if (offset >= buf->len)
916 scm_out_of_range (FUNC_NAME, scm_from_int (offset));
917 else
918 buf->pos = buf->len - offset;
919 break;
921 default:
922 scm_wrong_type_arg_msg (FUNC_NAME, 0, port,
923 "invalid `seek' parameter");
926 return buf->pos;
928 #undef FUNC_NAME
930 /* Fetch data from a bop. */
931 SCM_SMOB_APPLY (bytevector_output_port_procedure,
932 bop_proc_apply, 0, 0, 0, (SCM bop_proc))
934 SCM port, bv;
935 scm_t_bop_buffer *buf, result_buf;
937 port = SCM_PACK (SCM_SMOB_DATA (bop_proc));
938 buf = SCM_R6RS_BOP_BUFFER (port);
940 result_buf = *buf;
941 bop_buffer_init (buf);
943 if (result_buf.len == 0)
944 bv = scm_r6rs_c_take_bytevector (NULL, 0);
945 else
947 if (result_buf.total_len > result_buf.len)
948 /* Shrink the buffer. */
949 result_buf.buffer = scm_gc_realloc ((void *) result_buf.buffer,
950 result_buf.total_len,
951 result_buf.len,
952 SCM_GC_BOP);
954 bv = scm_r6rs_c_take_bytevector ((signed char *) result_buf.buffer,
955 result_buf.len);
958 return bv;
961 SCM_SMOB_MARK (bytevector_output_port_procedure, bop_proc_mark,
962 bop_proc)
964 /* Mark the port associated with BOP_PROC. */
965 return (SCM_PACK (SCM_SMOB_DATA (bop_proc)));
969 SCM_DEFINE (scm_r6rs_open_bytevector_output_port,
970 "open-bytevector-output-port", 0, 1, 0,
971 (SCM transcoder),
972 "Return two values: an output port and a procedure. The latter "
973 "should be called with zero arguments to obtain a bytevector "
974 "containing the data accumulated by the port.")
975 #define FUNC_NAME scm_r6rs_open_bytevector_output_port
977 if (transcoder != SCM_UNDEFINED)
978 transcoders_not_implemented ();
980 return (make_bop ());
982 #undef FUNC_NAME
984 static inline void
985 initialize_bytevector_output_ports (void)
987 bytevector_output_port_type =
988 scm_make_port_type ("r6rs-bytevector-output-port",
989 NULL, bop_write);
991 scm_set_port_seek (bytevector_output_port_type, bop_seek);
992 scm_set_port_free (bytevector_output_port_type, bop_free);
997 /* Initialization. */
999 void
1000 scm_init_r6rs_ports (void)
1002 #include "ports.x"
1004 initialize_bytevector_input_ports ();
1005 initialize_custom_binary_input_ports ();
1006 initialize_bytevector_output_ports ();
1009 /* arch-tag: c041ffd6-8871-40e8-a25a-d0f8768a71dc