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 */
30 #include "bytevector.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
41 transcoders_not_implemented (void)
43 fprintf (stderr
, "%s: warning: transcoders not implemented\n",
48 /* End-of-file object. */
50 SCM_DEFINE (scm_r6rs_eof_object
, "eof-object", 0, 0, 0,
52 "Return the end-of-file object.")
53 #define FUNC_NAME s_scm_r6rs_eof_object
63 # define MIN(a,b) ((a) < (b) ? (a) : (b))
66 /* Bytevector input ports or "bip" for short. */
67 static scm_t_bits bytevector_input_port_type
= 0;
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
);
101 /* Mark the underlying bytevector. */
102 return (SCM_PACK (SCM_STREAM (port
)));
105 #ifndef HAVE_SCM_SET_PORT_READ
108 bip_fill_input (SCM port
)
111 scm_t_port
*c_port
= SCM_PTAB_ENTRY (port
);
113 if (c_port
->read_pos
>= c_port
->read_end
)
116 result
= (int) *c_port
->read_pos
;
121 #else /* HAVE_SCM_SET_PORT_READ */
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
;
138 #endif /* HAVE_SCM_SET_PORT_READ */
141 bip_seek (SCM port
, off_t offset
, int whence
)
142 #define FUNC_NAME "bip_seek"
145 scm_t_port
*c_port
= SCM_PTAB_ENTRY (port
);
150 offset
+= c_port
->read_pos
- c_port
->read_buf
;
154 if (c_port
->read_buf
+ offset
< c_port
->read_end
)
156 c_port
->read_pos
= c_port
->read_buf
+ offset
;
160 scm_out_of_range (FUNC_NAME
, scm_from_int (offset
));
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
;
170 scm_out_of_range (FUNC_NAME
, scm_from_int (offset
));
174 scm_wrong_type_arg_msg (FUNC_NAME
, 0, port
,
175 "invalid `seek' parameter");
183 /* Instantiate the bytevector input port type. */
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
),
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 (!SCM_UNBNDP (transcoder
) && !scm_is_false (transcoder
))
207 transcoders_not_implemented ();
209 return (make_bip (bv
));
214 /* Custom binary ports. The following routines are shared by input and
215 output custom binary ports. */
217 #define SCM_R6RS_CBP_GET_POSITION_PROC(_port) \
218 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 1)
219 #define SCM_R6RS_CBP_SET_POSITION_PROC(_port) \
220 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 2)
221 #define SCM_R6RS_CBP_CLOSE_PROC(_port) \
222 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 3)
227 /* Mark the underlying method and object vector. */
228 return (SCM_PACK (SCM_STREAM (port
)));
232 cbp_seek (SCM port
, off_t offset
, int whence
)
233 #define FUNC_NAME "cbp_seek"
242 SCM get_position_proc
;
244 get_position_proc
= SCM_R6RS_CBP_GET_POSITION_PROC (port
);
245 if (SCM_LIKELY (scm_is_true (get_position_proc
)))
246 result
= scm_call_0 (get_position_proc
);
248 scm_wrong_type_arg_msg (FUNC_NAME
, 0, port
,
249 "R6RS custom binary port does not "
250 "support `port-position'");
252 offset
+= scm_to_int (result
);
258 SCM set_position_proc
;
260 set_position_proc
= SCM_R6RS_CBP_SET_POSITION_PROC (port
);
261 if (SCM_LIKELY (scm_is_true (set_position_proc
)))
262 result
= scm_call_1 (set_position_proc
, scm_from_int (offset
));
264 scm_wrong_type_arg_msg (FUNC_NAME
, 0, port
,
265 "R6RS custom binary port does not "
266 "support `set-port-position!'");
268 /* Assuming setting the position succeeded. */
274 /* `SEEK_END' cannot be supported. */
275 scm_wrong_type_arg_msg (FUNC_NAME
, 0, port
,
276 "R6RS custom binary ports do not "
277 "support `SEEK_END'");
289 close_proc
= SCM_R6RS_CBP_CLOSE_PROC (port
);
290 if (scm_is_true (close_proc
))
291 /* Invoke the `close' thunk. */
292 scm_call_0 (close_proc
);
298 /* Custom binary input port ("cbip" for short). */
300 static scm_t_bits custom_binary_input_port_type
= 0;
302 /* Size of the buffer embedded in custom binary input ports. */
303 #define CBIP_BUFFER_SIZE 4096
305 /* Return the bytevector associated with PORT. */
306 #define SCM_R6RS_CBIP_BYTEVECTOR(_port) \
307 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 4)
309 /* Return the various procedures of PORT. */
310 #define SCM_R6RS_CBIP_READ_PROC(_port) \
311 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 0)
315 make_cbip (SCM read_proc
, SCM get_position_proc
,
316 SCM set_position_proc
, SCM close_proc
)
318 SCM port
, bv
, method_vector
;
322 const unsigned long mode_bits
= SCM_OPN
| SCM_RDNG
;
324 /* Use a bytevector as the underlying buffer. */
325 c_len
= CBIP_BUFFER_SIZE
;
326 bv
= scm_r6rs_c_make_bytevector (c_len
);
327 c_bv
= (char *) SCM_R6RS_BYTEVECTOR_CONTENTS (bv
);
329 /* Store the various methods and bytevector in a vector. */
330 method_vector
= scm_c_make_vector (5, SCM_BOOL_F
);
331 SCM_SIMPLE_VECTOR_SET (method_vector
, 4, bv
);
332 SCM_SIMPLE_VECTOR_SET (method_vector
, 0, read_proc
);
333 SCM_SIMPLE_VECTOR_SET (method_vector
, 1, get_position_proc
);
334 SCM_SIMPLE_VECTOR_SET (method_vector
, 2, set_position_proc
);
335 SCM_SIMPLE_VECTOR_SET (method_vector
, 3, close_proc
);
337 port
= scm_new_port_table_entry (custom_binary_input_port_type
);
339 /* Attach it the method vector. */
340 SCM_SETSTREAM (port
, SCM_UNPACK (method_vector
));
342 /* Have the port directly access the buffer (bytevector). */
343 c_port
= SCM_PTAB_ENTRY (port
);
344 c_port
->read_pos
= c_port
->read_buf
= (unsigned char *) c_bv
;
345 c_port
->read_end
= (unsigned char *) c_bv
;
346 c_port
->read_buf_size
= c_len
;
348 /* Mark PORT as open, readable and unbuffered (hmm, how elegant...). */
349 SCM_SET_CELL_TYPE (port
, custom_binary_input_port_type
| mode_bits
);
355 cbip_fill_input (SCM port
)
356 #define FUNC_NAME "cbip_fill_input"
359 scm_t_port
*c_port
= SCM_PTAB_ENTRY (port
);
362 if (c_port
->read_pos
>= c_port
->read_end
)
364 /* Invoke the user's `read!' procedure. */
366 SCM bv
, read_proc
, octets
;
368 /* Use the bytevector associated with PORT as the buffer passed to the
369 `read!' procedure, thereby avoiding additional allocations. */
370 bv
= SCM_R6RS_CBIP_BYTEVECTOR (port
);
371 read_proc
= SCM_R6RS_CBIP_READ_PROC (port
);
373 /* The assumption here is that C_PORT's internal buffer wasn't changed
375 assert (c_port
->read_buf
==
376 (unsigned char *) SCM_R6RS_BYTEVECTOR_CONTENTS (bv
));
377 assert ((unsigned) c_port
->read_buf_size
378 == SCM_R6RS_BYTEVECTOR_LENGTH (bv
));
380 octets
= scm_call_3 (read_proc
, bv
, SCM_INUM0
,
381 SCM_I_MAKINUM (CBIP_BUFFER_SIZE
));
382 c_octets
= scm_to_uint (octets
);
384 c_port
->read_pos
= (unsigned char *) SCM_R6RS_BYTEVECTOR_CONTENTS (bv
);
385 c_port
->read_end
= (unsigned char *) c_port
->read_pos
+ c_octets
;
393 result
= (int) *c_port
->read_pos
;
400 SCM_DEFINE (scm_r6rs_make_custom_binary_input_port
,
401 "make-custom-binary-input-port", 5, 0, 0,
402 (SCM id
, SCM read_proc
, SCM get_position_proc
,
403 SCM set_position_proc
, SCM close_proc
),
404 "Return a new custom binary input port whose input is drained "
405 "by invoking @var{read_proc} and passing it a bytevector, an "
406 "index where octets should be written, and an octet count.")
407 #define FUNC_NAME s_scm_r6rs_make_custom_binary_input_port
409 SCM_VALIDATE_STRING (1, id
);
410 SCM_VALIDATE_PROC (2, read_proc
);
412 if (!scm_is_false (get_position_proc
))
413 SCM_VALIDATE_PROC (3, get_position_proc
);
415 if (!scm_is_false (set_position_proc
))
416 SCM_VALIDATE_PROC (4, set_position_proc
);
418 if (!scm_is_false (close_proc
))
419 SCM_VALIDATE_PROC (5, close_proc
);
421 return (make_cbip (read_proc
, get_position_proc
, set_position_proc
,
427 /* Instantiate the custom binary input port type. */
429 initialize_custom_binary_input_ports (void)
431 custom_binary_input_port_type
=
432 scm_make_port_type ("r6rs-custom-binary-input-port",
433 cbip_fill_input
, NULL
);
435 scm_set_port_mark (custom_binary_input_port_type
, cbp_mark
);
436 scm_set_port_seek (custom_binary_input_port_type
, cbp_seek
);
437 scm_set_port_close (custom_binary_input_port_type
, cbp_close
);
444 /* We currently don't support specific binary input ports. */
445 #define SCM_VALIDATE_R6RS_BINARY_INPUT_PORT SCM_VALIDATE_OPINPORT
447 SCM_DEFINE (scm_r6rs_get_u8
, "get-u8", 1, 0, 0,
449 "Read an octet from @var{port}, a binary input port, "
450 "blocking as necessary.")
451 #define FUNC_NAME s_scm_r6rs_get_u8
456 SCM_VALIDATE_R6RS_BINARY_INPUT_PORT (1, port
);
458 c_result
= scm_getc (port
);
460 result
= SCM_EOF_VAL
;
462 result
= SCM_I_MAKINUM ((unsigned char) c_result
);
468 SCM_DEFINE (scm_r6rs_lookahead_u8
, "lookahead-u8", 1, 0, 0,
470 "Like @code{get-u8} but does not update @var{port} to "
471 "point past the octet.")
472 #define FUNC_NAME s_scm_r6rs_lookahead_u8
476 SCM_VALIDATE_R6RS_BINARY_INPUT_PORT (1, port
);
478 result
= scm_peek_char (port
);
479 if (SCM_CHARP (result
))
480 result
= SCM_I_MAKINUM ((signed char) SCM_CHAR (result
));
482 result
= SCM_EOF_VAL
;
488 SCM_DEFINE (scm_r6rs_get_bytevector_n
, "get-bytevector-n", 2, 0, 0,
489 (SCM port
, SCM count
),
490 "Read @var{count} octets from @var{port}, blocking as "
491 "necessary and return a bytevector containing the octets "
492 "read. If fewer bytes are available, a bytevector smaller "
493 "than @var{count} is returned.")
494 #define FUNC_NAME s_scm_r6rs_get_bytevector_n
501 SCM_VALIDATE_R6RS_BINARY_INPUT_PORT (1, port
);
502 c_count
= scm_to_uint (count
);
504 result
= scm_r6rs_c_make_bytevector (c_count
);
505 c_bv
= (char *) SCM_R6RS_BYTEVECTOR_CONTENTS (result
);
507 if (SCM_LIKELY (c_count
> 0))
508 /* XXX: `scm_c_read ()' does not update the port position. */
509 c_read
= scm_c_read (port
, c_bv
, c_count
);
511 /* Don't invoke `scm_c_read ()' since it may block. */
514 if ((c_read
== 0) && (c_count
> 0))
516 if (SCM_EOF_OBJECT_P (scm_peek_char (port
)))
517 result
= SCM_EOF_VAL
;
519 result
= scm_r6rs_null_bytevector
;
523 if (c_read
< c_count
)
524 result
= scm_r6rs_c_shrink_bytevector (result
, c_read
);
531 SCM_DEFINE (scm_r6rs_get_bytevector_n_x
, "get-bytevector-n!", 4, 0, 0,
532 (SCM port
, SCM bv
, SCM start
, SCM count
),
533 "Read @var{count} bytes from @var{port} and store them "
534 "in @var{bv} starting at index @var{start}. Return either "
535 "the number of bytes actually read or the end-of-file "
537 #define FUNC_NAME s_scm_r6rs_get_bytevector_n_x
541 unsigned c_start
, c_count
, c_len
;
544 SCM_VALIDATE_R6RS_BINARY_INPUT_PORT (1, port
);
545 SCM_VALIDATE_R6RS_BYTEVECTOR (2, bv
);
546 c_start
= scm_to_uint (start
);
547 c_count
= scm_to_uint (count
);
549 c_bv
= (char *) SCM_R6RS_BYTEVECTOR_CONTENTS (bv
);
550 c_len
= SCM_R6RS_BYTEVECTOR_LENGTH (bv
);
552 if (SCM_UNLIKELY (c_start
+ c_count
> c_len
))
553 scm_out_of_range (FUNC_NAME
, count
);
555 if (SCM_LIKELY (c_count
> 0))
556 c_read
= scm_c_read (port
, c_bv
+ c_start
, c_count
);
558 /* Don't invoke `scm_c_read ()' since it may block. */
561 if ((c_read
== 0) && (c_count
> 0))
563 if (SCM_EOF_OBJECT_P (scm_peek_char (port
)))
564 result
= SCM_EOF_VAL
;
566 result
= SCM_I_MAKINUM (0);
569 result
= scm_from_size_t (c_read
);
576 SCM_DEFINE (scm_r6rs_get_bytevector_some
, "get-bytevector-some", 1, 0, 0,
578 "Read from @var{port}, blocking as necessary, until data "
579 "are available or and end-of-file is reached. Return either "
580 "a new bytevector containing the data read or the "
581 "end-of-file object.")
582 #define FUNC_NAME s_scm_r6rs_get_bytevector_some
584 /* Read at least one byte, unless the end-of-file is already reached, and
585 read while characters are available (buffered). */
592 SCM_VALIDATE_R6RS_BINARY_INPUT_PORT (1, port
);
595 c_bv
= (char *) scm_gc_malloc (c_len
, SCM_GC_BYTEVECTOR
);
602 if (c_total
+ 1 > c_len
)
604 /* Grow the bytevector. */
605 c_bv
= (char *) scm_gc_realloc (c_bv
, c_len
, c_len
* 2,
610 /* We can't use `scm_c_read ()' since it blocks. */
611 c_chr
= scm_getc (port
);
614 c_bv
[c_total
] = (char) c_chr
;
618 while ((scm_is_true (scm_char_ready_p (port
)))
619 && (!SCM_EOF_OBJECT_P (scm_peek_char (port
))));
623 result
= SCM_EOF_VAL
;
624 scm_gc_free (c_bv
, c_len
, SCM_GC_BYTEVECTOR
);
630 /* Shrink the bytevector. */
631 c_bv
= (char *) scm_gc_realloc (c_bv
, c_len
, c_total
,
633 c_len
= (unsigned) c_total
;
636 result
= scm_r6rs_c_take_bytevector ((signed char *) c_bv
, c_len
);
643 SCM_DEFINE (scm_r6rs_get_bytevector_all
, "get-bytevector-all", 1, 0, 0,
645 "Read from @var{port}, blocking as necessary, until "
646 "the end-of-file is reached. Return either "
647 "a new bytevector containing the data read or the "
648 "end-of-file object (if no data were available).")
649 #define FUNC_NAME s_scm_r6rs_get_bytevector_all
653 unsigned c_len
, c_count
;
654 size_t c_read
, c_total
;
656 SCM_VALIDATE_R6RS_BINARY_INPUT_PORT (1, port
);
658 c_len
= c_count
= 4096;
659 c_bv
= (char *) scm_gc_malloc (c_len
, SCM_GC_BYTEVECTOR
);
660 c_total
= c_read
= 0;
664 if (c_total
+ c_read
> c_len
)
666 /* Grow the bytevector. */
667 c_bv
= (char *) scm_gc_realloc (c_bv
, c_len
, c_len
* 2,
673 /* `scm_c_read ()' blocks until C_COUNT bytes are available or EOF is
675 c_read
= scm_c_read (port
, c_bv
+ c_total
, c_count
);
676 c_total
+= c_read
, c_count
-= c_read
;
678 while (!SCM_EOF_OBJECT_P (scm_peek_char (port
)));
682 result
= SCM_EOF_VAL
;
683 scm_gc_free (c_bv
, c_len
, SCM_GC_BYTEVECTOR
);
689 /* Shrink the bytevector. */
690 c_bv
= (char *) scm_gc_realloc (c_bv
, c_len
, c_total
,
692 c_len
= (unsigned) c_total
;
695 result
= scm_r6rs_c_take_bytevector ((signed char *) c_bv
, c_len
);
706 /* We currently don't support specific binary input ports. */
707 #define SCM_VALIDATE_R6RS_BINARY_OUTPUT_PORT SCM_VALIDATE_OPOUTPORT
710 SCM_DEFINE (scm_r6rs_put_u8
, "put-u8", 2, 0, 0,
711 (SCM port
, SCM octet
),
712 "Write @var{octet} to binary port @var{port}.")
713 #define FUNC_NAME s_scm_r6rs_put_u8
717 SCM_VALIDATE_R6RS_BINARY_OUTPUT_PORT (1, port
);
718 c_octet
= scm_to_uint8 (octet
);
720 scm_putc ((char) c_octet
, port
);
722 return SCM_UNSPECIFIED
;
726 SCM_DEFINE (scm_r6rs_put_bytevector
, "put-bytevector", 2, 2, 0,
727 (SCM port
, SCM bv
, SCM start
, SCM count
),
728 "Write the contents of @var{bv} to @var{port}, optionally "
729 "starting at index @var{start} and limiting to @var{count} "
731 #define FUNC_NAME s_scm_r6rs_put_bytevector
734 unsigned c_start
, c_count
, c_len
;
736 SCM_VALIDATE_R6RS_BINARY_OUTPUT_PORT (1, port
);
737 SCM_VALIDATE_R6RS_BYTEVECTOR (2, bv
);
739 c_len
= SCM_R6RS_BYTEVECTOR_LENGTH (bv
);
740 c_bv
= (char *) SCM_R6RS_BYTEVECTOR_CONTENTS (bv
);
742 if (start
!= SCM_UNDEFINED
)
744 c_start
= scm_to_uint (start
);
746 if (count
!= SCM_UNDEFINED
)
748 c_count
= scm_to_uint (count
);
749 if (SCM_UNLIKELY (c_start
+ c_count
> c_len
))
750 scm_out_of_range (FUNC_NAME
, count
);
754 if (SCM_UNLIKELY (c_start
>= c_len
))
755 scm_out_of_range (FUNC_NAME
, start
);
757 c_count
= c_len
- c_start
;
761 c_start
= 0, c_count
= c_len
;
763 scm_c_write (port
, c_bv
+ c_start
, c_count
);
765 return SCM_UNSPECIFIED
;
771 /* Bytevector output port ("bop" for short). */
773 /* Implementation of "bops".
775 Each bop has an internal buffer, of type `scm_t_bop_buffer', attached to
776 it. The procedure returned along with the output port is actually an
777 applicable SMOB. The SMOB holds a reference to the port. When applied,
778 the SMOB swallows the port's internal buffer, turning it into a
779 bytevector, and resets it.
781 XXX: Access to a bop's internal buffer is not thread-safe. */
783 static scm_t_bits bytevector_output_port_type
= 0;
785 SCM_SMOB (bytevector_output_port_procedure
,
786 "r6rs-bytevector-output-port-procedure",
789 #define SCM_GC_BOP "r6rs-bytevector-output-port"
790 #define SCM_BOP_BUFFER_INITIAL_SIZE 4096
792 /* Representation of a bop's internal buffer. */
802 /* Accessing a bop's buffer. */
803 #define SCM_R6RS_BOP_BUFFER(_port) \
804 ((scm_t_bop_buffer *) SCM_STREAM (_port))
805 #define SCM_R6RS_SET_BOP_BUFFER(_port, _buf) \
806 (SCM_SETSTREAM ((_port), (scm_t_bits) (_buf)))
810 bop_buffer_init (scm_t_bop_buffer
*buf
)
812 buf
->total_len
= buf
->len
= buf
->pos
= 0;
817 bop_buffer_grow (scm_t_bop_buffer
*buf
, size_t min_size
)
822 for (new_size
= buf
->total_len
823 ? buf
->total_len
: SCM_BOP_BUFFER_INITIAL_SIZE
;
828 new_buf
= scm_gc_realloc ((void *) buf
->buffer
, buf
->total_len
,
829 new_size
, SCM_GC_BOP
);
831 new_buf
= scm_gc_malloc (new_size
, SCM_GC_BOP
);
833 buf
->buffer
= new_buf
;
834 buf
->total_len
= new_size
;
842 scm_t_bop_buffer
*buf
;
843 const unsigned long mode_bits
= SCM_OPN
| SCM_WRTNG
;
845 port
= scm_new_port_table_entry (bytevector_output_port_type
);
847 buf
= (scm_t_bop_buffer
*) scm_gc_malloc (sizeof (* buf
), SCM_GC_BOP
);
848 bop_buffer_init (buf
);
850 c_port
= SCM_PTAB_ENTRY (port
);
851 c_port
->write_buf
= c_port
->write_pos
= c_port
->write_end
= NULL
;
852 c_port
->write_buf_size
= 0;
854 SCM_R6RS_SET_BOP_BUFFER (port
, buf
);
856 /* Mark PORT as open and writable. */
857 SCM_SET_CELL_TYPE (port
, bytevector_output_port_type
| mode_bits
);
859 /* Make the bop procedure. */
860 SCM_NEWSMOB (bop_proc
, bytevector_output_port_procedure
,
863 return (scm_values (scm_list_2 (port
, bop_proc
)));
869 /* The port itself is necessarily freed _after_ the bop proc, since the bop
870 proc holds a reference to it. Thus we can safely free the internal
871 buffer when the bop becomes unreferenced. */
872 scm_t_bop_buffer
*buf
;
874 buf
= SCM_R6RS_BOP_BUFFER (port
);
876 scm_gc_free (buf
->buffer
, buf
->total_len
, SCM_GC_BOP
);
878 scm_gc_free (buf
, sizeof (* buf
), SCM_GC_BOP
);
883 /* Write SIZE octets from DATA to PORT. */
885 bop_write (SCM port
, const void *data
, size_t size
)
887 scm_t_bop_buffer
*buf
;
889 buf
= SCM_R6RS_BOP_BUFFER (port
);
891 if (buf
->pos
+ size
> buf
->total_len
)
892 bop_buffer_grow (buf
, buf
->pos
+ size
);
894 memcpy (buf
->buffer
+ buf
->pos
, data
, size
);
896 buf
->len
= (buf
->len
> buf
->pos
) ? buf
->len
: buf
->pos
;
900 bop_seek (SCM port
, off_t offset
, int whence
)
901 #define FUNC_NAME "bop_seek"
903 scm_t_bop_buffer
*buf
;
905 buf
= SCM_R6RS_BOP_BUFFER (port
);
909 offset
+= (off_t
) buf
->pos
;
913 if (offset
< 0 || (unsigned) offset
> buf
->len
)
914 scm_out_of_range (FUNC_NAME
, scm_from_int (offset
));
920 if (offset
< 0 || (unsigned) offset
>= buf
->len
)
921 scm_out_of_range (FUNC_NAME
, scm_from_int (offset
));
923 buf
->pos
= buf
->len
- (offset
+ 1);
927 scm_wrong_type_arg_msg (FUNC_NAME
, 0, port
,
928 "invalid `seek' parameter");
935 /* Fetch data from a bop. */
936 SCM_SMOB_APPLY (bytevector_output_port_procedure
,
937 bop_proc_apply
, 0, 0, 0, (SCM bop_proc
))
940 scm_t_bop_buffer
*buf
, result_buf
;
942 port
= SCM_PACK (SCM_SMOB_DATA (bop_proc
));
943 buf
= SCM_R6RS_BOP_BUFFER (port
);
946 bop_buffer_init (buf
);
948 if (result_buf
.len
== 0)
949 bv
= scm_r6rs_c_take_bytevector (NULL
, 0);
952 if (result_buf
.total_len
> result_buf
.len
)
953 /* Shrink the buffer. */
954 result_buf
.buffer
= scm_gc_realloc ((void *) result_buf
.buffer
,
955 result_buf
.total_len
,
959 bv
= scm_r6rs_c_take_bytevector ((signed char *) result_buf
.buffer
,
966 SCM_SMOB_MARK (bytevector_output_port_procedure
, bop_proc_mark
,
969 /* Mark the port associated with BOP_PROC. */
970 return (SCM_PACK (SCM_SMOB_DATA (bop_proc
)));
974 SCM_DEFINE (scm_r6rs_open_bytevector_output_port
,
975 "open-bytevector-output-port", 0, 1, 0,
977 "Return two values: an output port and a procedure. The latter "
978 "should be called with zero arguments to obtain a bytevector "
979 "containing the data accumulated by the port.")
980 #define FUNC_NAME scm_r6rs_open_bytevector_output_port
982 if (!SCM_UNBNDP (transcoder
) && !scm_is_false (transcoder
))
983 transcoders_not_implemented ();
985 return (make_bop ());
990 initialize_bytevector_output_ports (void)
992 bytevector_output_port_type
=
993 scm_make_port_type ("r6rs-bytevector-output-port",
996 scm_set_port_seek (bytevector_output_port_type
, bop_seek
);
997 scm_set_port_free (bytevector_output_port_type
, bop_free
);
1001 /* Custom binary output port ("cbop" for short). */
1003 static scm_t_bits custom_binary_output_port_type
;
1005 /* Return the various procedures of PORT. */
1006 #define SCM_R6RS_CBOP_WRITE_PROC(_port) \
1007 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 0)
1011 make_cbop (SCM write_proc
, SCM get_position_proc
,
1012 SCM set_position_proc
, SCM close_proc
)
1014 SCM port
, method_vector
;
1016 const unsigned long mode_bits
= SCM_OPN
| SCM_WRTNG
;
1018 /* Store the various methods and bytevector in a vector. */
1019 method_vector
= scm_c_make_vector (4, SCM_BOOL_F
);
1020 SCM_SIMPLE_VECTOR_SET (method_vector
, 0, write_proc
);
1021 SCM_SIMPLE_VECTOR_SET (method_vector
, 1, get_position_proc
);
1022 SCM_SIMPLE_VECTOR_SET (method_vector
, 2, set_position_proc
);
1023 SCM_SIMPLE_VECTOR_SET (method_vector
, 3, close_proc
);
1025 port
= scm_new_port_table_entry (custom_binary_output_port_type
);
1027 /* Attach it the method vector. */
1028 SCM_SETSTREAM (port
, SCM_UNPACK (method_vector
));
1030 /* Have the port directly access the buffer (bytevector). */
1031 c_port
= SCM_PTAB_ENTRY (port
);
1032 c_port
->write_buf
= c_port
->write_pos
= c_port
->write_end
= NULL
;
1033 c_port
->write_buf_size
= c_port
->read_buf_size
= 0;
1035 /* Mark PORT as open, writable and unbuffered. */
1036 SCM_SET_CELL_TYPE (port
, custom_binary_output_port_type
| mode_bits
);
1041 /* Write SIZE octets from DATA to PORT. */
1043 cbop_write (SCM port
, const void *data
, size_t size
)
1044 #define FUNC_NAME "cbop_write"
1048 SCM bv
, write_proc
, result
;
1050 /* XXX: Allocating a new bytevector at each `write' call is inefficient,
1051 but necessary since (1) we don't control the lifetime of the buffer
1052 pointed to by DATA, and (2) the `write!' procedure could capture the
1053 bytevector it is passed. */
1054 bv
= scm_r6rs_c_make_bytevector (size
);
1055 memcpy (SCM_R6RS_BYTEVECTOR_CONTENTS (bv
), data
, size
);
1057 write_proc
= SCM_R6RS_CBOP_WRITE_PROC (port
);
1059 /* Since the `write' procedure of Guile's ports has type `void', it must
1060 try hard to write exactly SIZE bytes, regardless of how many bytes the
1064 c_written
+= c_result
)
1066 result
= scm_call_3 (write_proc
, bv
,
1067 scm_from_size_t (c_written
),
1068 scm_from_size_t (size
- c_written
));
1070 c_result
= scm_to_long (result
);
1071 if (SCM_UNLIKELY (c_result
< 0
1072 || (size_t) c_result
> (size
- c_written
)))
1073 scm_wrong_type_arg_msg (FUNC_NAME
, 0, result
,
1074 "R6RS custom binary output port `write!' "
1075 "returned a incorrect integer");
1081 SCM_DEFINE (scm_r6rs_make_custom_binary_output_port
,
1082 "make-custom-binary-output-port", 5, 0, 0,
1083 (SCM id
, SCM write_proc
, SCM get_position_proc
,
1084 SCM set_position_proc
, SCM close_proc
),
1085 "Return a new custom binary output port whose output is drained "
1086 "by invoking @var{write_proc} and passing it a bytevector, an "
1087 "index where octets should be written, and an octet count.")
1088 #define FUNC_NAME s_scm_r6rs_make_custom_binary_output_port
1090 SCM_VALIDATE_STRING (1, id
);
1091 SCM_VALIDATE_PROC (2, write_proc
);
1093 if (!scm_is_false (get_position_proc
))
1094 SCM_VALIDATE_PROC (3, get_position_proc
);
1096 if (!scm_is_false (set_position_proc
))
1097 SCM_VALIDATE_PROC (4, set_position_proc
);
1099 if (!scm_is_false (close_proc
))
1100 SCM_VALIDATE_PROC (5, close_proc
);
1102 return (make_cbop (write_proc
, get_position_proc
, set_position_proc
,
1108 /* Instantiate the custom binary output port type. */
1110 initialize_custom_binary_output_ports (void)
1112 custom_binary_output_port_type
=
1113 scm_make_port_type ("r6rs-custom-binary-output-port",
1116 scm_set_port_mark (custom_binary_output_port_type
, cbp_mark
);
1117 scm_set_port_seek (custom_binary_output_port_type
, cbp_seek
);
1118 scm_set_port_close (custom_binary_output_port_type
, cbp_close
);
1122 /* Initialization. */
1125 scm_init_r6rs_ports (void)
1129 initialize_bytevector_input_ports ();
1130 initialize_custom_binary_input_ports ();
1131 initialize_bytevector_output_ports ();
1132 initialize_custom_binary_output_ports ();