Remove unneeded SMOB mark/free procedures.
[bpt/guile.git] / libguile / r6rs-ports.c
CommitLineData
1ee2c72e
LC
1/* Copyright (C) 2009 Free Software Foundation, Inc.
2 *
3 * This library is free software; you can redistribute it and/or
53befeb7
NJ
4 * modify it under the terms of the GNU Lesser General Public License
5 * as published by the Free Software Foundation; either version 3 of
6 * the License, or (at your option) any later version.
1ee2c72e 7 *
53befeb7
NJ
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
1ee2c72e
LC
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
53befeb7
NJ
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 * 02110-1301 USA
1ee2c72e
LC
17 */
18
19#ifdef HAVE_CONFIG_H
20# include <config.h>
21#endif
22
23#ifdef HAVE_UNISTD_H
24# include <unistd.h>
25#endif
26
27#include <string.h>
28#include <stdio.h>
29#include <assert.h>
30
31#include "libguile/_scm.h"
32#include "libguile/bytevectors.h"
33#include "libguile/chars.h"
34#include "libguile/eval.h"
35#include "libguile/r6rs-ports.h"
36#include "libguile/strings.h"
37#include "libguile/validate.h"
38#include "libguile/values.h"
39#include "libguile/vectors.h"
40
41
42\f
43/* Unimplemented features. */
44
45
46/* Transoders are currently not implemented since Guile 1.8 is not
47 Unicode-capable. Thus, most of the code here assumes the use of the
48 binary transcoder. */
49static inline void
50transcoders_not_implemented (void)
51{
52 fprintf (stderr, "%s: warning: transcoders not implemented\n",
53 PACKAGE_NAME);
54}
55
56\f
57/* End-of-file object. */
58
59SCM_DEFINE (scm_eof_object, "eof-object", 0, 0, 0,
60 (void),
61 "Return the end-of-file object.")
62#define FUNC_NAME s_scm_eof_object
63{
64 return (SCM_EOF_VAL);
65}
66#undef FUNC_NAME
67
68\f
69/* Input ports. */
70
71#ifndef MIN
72# define MIN(a,b) ((a) < (b) ? (a) : (b))
73#endif
74
75/* Bytevector input ports or "bip" for short. */
76static scm_t_bits bytevector_input_port_type = 0;
77
78static inline SCM
79make_bip (SCM bv)
80{
81 SCM port;
82 char *c_bv;
83 unsigned c_len;
84 scm_t_port *c_port;
85 const unsigned long mode_bits = SCM_OPN | SCM_RDNG;
86
87 port = scm_new_port_table_entry (bytevector_input_port_type);
88
89 /* Prevent BV from being GC'd. */
90 SCM_SETSTREAM (port, SCM_UNPACK (bv));
91
92 /* Have the port directly access the bytevector. */
93 c_bv = (char *) SCM_BYTEVECTOR_CONTENTS (bv);
94 c_len = SCM_BYTEVECTOR_LENGTH (bv);
95
96 c_port = SCM_PTAB_ENTRY (port);
97 c_port->read_pos = c_port->read_buf = (unsigned char *) c_bv;
98 c_port->read_end = (unsigned char *) c_bv + c_len;
99 c_port->read_buf_size = c_len;
100
101 /* Mark PORT as open, readable and unbuffered (hmm, how elegant...). */
102 SCM_SET_CELL_TYPE (port, bytevector_input_port_type | mode_bits);
103
104 return port;
105}
106
1ee2c72e
LC
107static int
108bip_fill_input (SCM port)
109{
110 int result;
111 scm_t_port *c_port = SCM_PTAB_ENTRY (port);
112
113 if (c_port->read_pos >= c_port->read_end)
114 result = EOF;
115 else
116 result = (int) *c_port->read_pos;
117
118 return result;
119}
120
f1ce9199
LC
121static scm_t_off
122bip_seek (SCM port, scm_t_off offset, int whence)
1ee2c72e
LC
123#define FUNC_NAME "bip_seek"
124{
f1ce9199 125 scm_t_off c_result = 0;
1ee2c72e
LC
126 scm_t_port *c_port = SCM_PTAB_ENTRY (port);
127
128 switch (whence)
129 {
130 case SEEK_CUR:
131 offset += c_port->read_pos - c_port->read_buf;
132 /* Fall through. */
133
134 case SEEK_SET:
135 if (c_port->read_buf + offset < c_port->read_end)
136 {
137 c_port->read_pos = c_port->read_buf + offset;
138 c_result = offset;
139 }
140 else
141 scm_out_of_range (FUNC_NAME, scm_from_int (offset));
142 break;
143
144 case SEEK_END:
145 if (c_port->read_end - offset >= c_port->read_buf)
146 {
147 c_port->read_pos = c_port->read_end - offset;
148 c_result = c_port->read_pos - c_port->read_buf;
149 }
150 else
151 scm_out_of_range (FUNC_NAME, scm_from_int (offset));
152 break;
153
154 default:
155 scm_wrong_type_arg_msg (FUNC_NAME, 0, port,
156 "invalid `seek' parameter");
157 }
158
159 return c_result;
160}
161#undef FUNC_NAME
162
163
164/* Instantiate the bytevector input port type. */
165static inline void
166initialize_bytevector_input_ports (void)
167{
168 bytevector_input_port_type =
169 scm_make_port_type ("r6rs-bytevector-input-port", bip_fill_input,
170 NULL);
171
1ee2c72e
LC
172 scm_set_port_seek (bytevector_input_port_type, bip_seek);
173}
174
175
176SCM_DEFINE (scm_open_bytevector_input_port,
177 "open-bytevector-input-port", 1, 1, 0,
178 (SCM bv, SCM transcoder),
179 "Return an input port whose contents are drawn from "
180 "bytevector @var{bv}.")
181#define FUNC_NAME s_scm_open_bytevector_input_port
182{
183 SCM_VALIDATE_BYTEVECTOR (1, bv);
184 if (!SCM_UNBNDP (transcoder) && !scm_is_false (transcoder))
185 transcoders_not_implemented ();
186
187 return (make_bip (bv));
188}
189#undef FUNC_NAME
190
191\f
192/* Custom binary ports. The following routines are shared by input and
193 output custom binary ports. */
194
195#define SCM_CBP_GET_POSITION_PROC(_port) \
196 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 1)
197#define SCM_CBP_SET_POSITION_PROC(_port) \
198 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 2)
199#define SCM_CBP_CLOSE_PROC(_port) \
200 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 3)
201
f1ce9199
LC
202static scm_t_off
203cbp_seek (SCM port, scm_t_off offset, int whence)
1ee2c72e
LC
204#define FUNC_NAME "cbp_seek"
205{
206 SCM result;
f1ce9199 207 scm_t_off c_result = 0;
1ee2c72e
LC
208
209 switch (whence)
210 {
211 case SEEK_CUR:
212 {
213 SCM get_position_proc;
214
215 get_position_proc = SCM_CBP_GET_POSITION_PROC (port);
216 if (SCM_LIKELY (scm_is_true (get_position_proc)))
217 result = scm_call_0 (get_position_proc);
218 else
219 scm_wrong_type_arg_msg (FUNC_NAME, 0, port,
220 "R6RS custom binary port does not "
221 "support `port-position'");
222
223 offset += scm_to_int (result);
224 /* Fall through. */
225 }
226
227 case SEEK_SET:
228 {
229 SCM set_position_proc;
230
231 set_position_proc = SCM_CBP_SET_POSITION_PROC (port);
232 if (SCM_LIKELY (scm_is_true (set_position_proc)))
233 result = scm_call_1 (set_position_proc, scm_from_int (offset));
234 else
235 scm_wrong_type_arg_msg (FUNC_NAME, 0, port,
236 "R6RS custom binary port does not "
237 "support `set-port-position!'");
238
239 /* Assuming setting the position succeeded. */
240 c_result = offset;
241 break;
242 }
243
244 default:
245 /* `SEEK_END' cannot be supported. */
246 scm_wrong_type_arg_msg (FUNC_NAME, 0, port,
247 "R6RS custom binary ports do not "
248 "support `SEEK_END'");
249 }
250
251 return c_result;
252}
253#undef FUNC_NAME
254
255static int
256cbp_close (SCM port)
257{
258 SCM close_proc;
259
260 close_proc = SCM_CBP_CLOSE_PROC (port);
261 if (scm_is_true (close_proc))
262 /* Invoke the `close' thunk. */
263 scm_call_0 (close_proc);
264
265 return 1;
266}
267
268\f
269/* Custom binary input port ("cbip" for short). */
270
271static scm_t_bits custom_binary_input_port_type = 0;
272
273/* Size of the buffer embedded in custom binary input ports. */
274#define CBIP_BUFFER_SIZE 4096
275
276/* Return the bytevector associated with PORT. */
277#define SCM_CBIP_BYTEVECTOR(_port) \
278 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 4)
279
280/* Return the various procedures of PORT. */
281#define SCM_CBIP_READ_PROC(_port) \
282 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 0)
283
284
285static inline SCM
286make_cbip (SCM read_proc, SCM get_position_proc,
287 SCM set_position_proc, SCM close_proc)
288{
289 SCM port, bv, method_vector;
290 char *c_bv;
291 unsigned c_len;
292 scm_t_port *c_port;
293 const unsigned long mode_bits = SCM_OPN | SCM_RDNG;
294
295 /* Use a bytevector as the underlying buffer. */
296 c_len = CBIP_BUFFER_SIZE;
297 bv = scm_c_make_bytevector (c_len);
298 c_bv = (char *) SCM_BYTEVECTOR_CONTENTS (bv);
299
300 /* Store the various methods and bytevector in a vector. */
301 method_vector = scm_c_make_vector (5, SCM_BOOL_F);
302 SCM_SIMPLE_VECTOR_SET (method_vector, 4, bv);
303 SCM_SIMPLE_VECTOR_SET (method_vector, 0, read_proc);
304 SCM_SIMPLE_VECTOR_SET (method_vector, 1, get_position_proc);
305 SCM_SIMPLE_VECTOR_SET (method_vector, 2, set_position_proc);
306 SCM_SIMPLE_VECTOR_SET (method_vector, 3, close_proc);
307
308 port = scm_new_port_table_entry (custom_binary_input_port_type);
309
310 /* Attach it the method vector. */
311 SCM_SETSTREAM (port, SCM_UNPACK (method_vector));
312
313 /* Have the port directly access the buffer (bytevector). */
314 c_port = SCM_PTAB_ENTRY (port);
315 c_port->read_pos = c_port->read_buf = (unsigned char *) c_bv;
316 c_port->read_end = (unsigned char *) c_bv;
317 c_port->read_buf_size = c_len;
318
319 /* Mark PORT as open, readable and unbuffered (hmm, how elegant...). */
320 SCM_SET_CELL_TYPE (port, custom_binary_input_port_type | mode_bits);
321
322 return port;
323}
324
325static int
326cbip_fill_input (SCM port)
327#define FUNC_NAME "cbip_fill_input"
328{
329 int result;
330 scm_t_port *c_port = SCM_PTAB_ENTRY (port);
331
332 again:
333 if (c_port->read_pos >= c_port->read_end)
334 {
335 /* Invoke the user's `read!' procedure. */
336 unsigned c_octets;
337 SCM bv, read_proc, octets;
338
339 /* Use the bytevector associated with PORT as the buffer passed to the
340 `read!' procedure, thereby avoiding additional allocations. */
341 bv = SCM_CBIP_BYTEVECTOR (port);
342 read_proc = SCM_CBIP_READ_PROC (port);
343
344 /* The assumption here is that C_PORT's internal buffer wasn't changed
345 behind our back. */
346 assert (c_port->read_buf ==
347 (unsigned char *) SCM_BYTEVECTOR_CONTENTS (bv));
348 assert ((unsigned) c_port->read_buf_size
349 == SCM_BYTEVECTOR_LENGTH (bv));
350
351 octets = scm_call_3 (read_proc, bv, SCM_INUM0,
352 SCM_I_MAKINUM (CBIP_BUFFER_SIZE));
353 c_octets = scm_to_uint (octets);
354
355 c_port->read_pos = (unsigned char *) SCM_BYTEVECTOR_CONTENTS (bv);
356 c_port->read_end = (unsigned char *) c_port->read_pos + c_octets;
357
358 if (c_octets > 0)
359 goto again;
360 else
361 result = EOF;
362 }
363 else
364 result = (int) *c_port->read_pos;
365
366 return result;
367}
368#undef FUNC_NAME
369
370
371SCM_DEFINE (scm_make_custom_binary_input_port,
372 "make-custom-binary-input-port", 5, 0, 0,
373 (SCM id, SCM read_proc, SCM get_position_proc,
374 SCM set_position_proc, SCM close_proc),
375 "Return a new custom binary input port whose input is drained "
376 "by invoking @var{read_proc} and passing it a bytevector, an "
377 "index where octets should be written, and an octet count.")
378#define FUNC_NAME s_scm_make_custom_binary_input_port
379{
380 SCM_VALIDATE_STRING (1, id);
381 SCM_VALIDATE_PROC (2, read_proc);
382
383 if (!scm_is_false (get_position_proc))
384 SCM_VALIDATE_PROC (3, get_position_proc);
385
386 if (!scm_is_false (set_position_proc))
387 SCM_VALIDATE_PROC (4, set_position_proc);
388
389 if (!scm_is_false (close_proc))
390 SCM_VALIDATE_PROC (5, close_proc);
391
392 return (make_cbip (read_proc, get_position_proc, set_position_proc,
393 close_proc));
394}
395#undef FUNC_NAME
396
397
398/* Instantiate the custom binary input port type. */
399static inline void
400initialize_custom_binary_input_ports (void)
401{
402 custom_binary_input_port_type =
403 scm_make_port_type ("r6rs-custom-binary-input-port",
404 cbip_fill_input, NULL);
405
1ee2c72e
LC
406 scm_set_port_seek (custom_binary_input_port_type, cbp_seek);
407 scm_set_port_close (custom_binary_input_port_type, cbp_close);
408}
409
410
411\f
412/* Binary input. */
413
414/* We currently don't support specific binary input ports. */
415#define SCM_VALIDATE_BINARY_INPUT_PORT SCM_VALIDATE_OPINPORT
416
417SCM_DEFINE (scm_get_u8, "get-u8", 1, 0, 0,
418 (SCM port),
419 "Read an octet from @var{port}, a binary input port, "
420 "blocking as necessary.")
421#define FUNC_NAME s_scm_get_u8
422{
423 SCM result;
424 int c_result;
425
426 SCM_VALIDATE_BINARY_INPUT_PORT (1, port);
427
428 c_result = scm_getc (port);
429 if (c_result == EOF)
430 result = SCM_EOF_VAL;
431 else
432 result = SCM_I_MAKINUM ((unsigned char) c_result);
433
434 return result;
435}
436#undef FUNC_NAME
437
438SCM_DEFINE (scm_lookahead_u8, "lookahead-u8", 1, 0, 0,
439 (SCM port),
440 "Like @code{get-u8} but does not update @var{port} to "
441 "point past the octet.")
442#define FUNC_NAME s_scm_lookahead_u8
443{
444 SCM result;
445
446 SCM_VALIDATE_BINARY_INPUT_PORT (1, port);
447
448 result = scm_peek_char (port);
449 if (SCM_CHARP (result))
450 result = SCM_I_MAKINUM ((signed char) SCM_CHAR (result));
451 else
452 result = SCM_EOF_VAL;
453
454 return result;
455}
456#undef FUNC_NAME
457
458SCM_DEFINE (scm_get_bytevector_n, "get-bytevector-n", 2, 0, 0,
459 (SCM port, SCM count),
460 "Read @var{count} octets from @var{port}, blocking as "
461 "necessary and return a bytevector containing the octets "
462 "read. If fewer bytes are available, a bytevector smaller "
463 "than @var{count} is returned.")
464#define FUNC_NAME s_scm_get_bytevector_n
465{
466 SCM result;
467 char *c_bv;
468 unsigned c_count;
469 size_t c_read;
470
471 SCM_VALIDATE_BINARY_INPUT_PORT (1, port);
472 c_count = scm_to_uint (count);
473
474 result = scm_c_make_bytevector (c_count);
475 c_bv = (char *) SCM_BYTEVECTOR_CONTENTS (result);
476
477 if (SCM_LIKELY (c_count > 0))
478 /* XXX: `scm_c_read ()' does not update the port position. */
479 c_read = scm_c_read (port, c_bv, c_count);
480 else
481 /* Don't invoke `scm_c_read ()' since it may block. */
482 c_read = 0;
483
484 if ((c_read == 0) && (c_count > 0))
485 {
486 if (SCM_EOF_OBJECT_P (scm_peek_char (port)))
487 result = SCM_EOF_VAL;
488 else
489 result = scm_null_bytevector;
490 }
491 else
492 {
493 if (c_read < c_count)
494 result = scm_c_shrink_bytevector (result, c_read);
495 }
496
497 return result;
498}
499#undef FUNC_NAME
500
501SCM_DEFINE (scm_get_bytevector_n_x, "get-bytevector-n!", 4, 0, 0,
502 (SCM port, SCM bv, SCM start, SCM count),
503 "Read @var{count} bytes from @var{port} and store them "
504 "in @var{bv} starting at index @var{start}. Return either "
505 "the number of bytes actually read or the end-of-file "
506 "object.")
507#define FUNC_NAME s_scm_get_bytevector_n_x
508{
509 SCM result;
510 char *c_bv;
511 unsigned c_start, c_count, c_len;
512 size_t c_read;
513
514 SCM_VALIDATE_BINARY_INPUT_PORT (1, port);
515 SCM_VALIDATE_BYTEVECTOR (2, bv);
516 c_start = scm_to_uint (start);
517 c_count = scm_to_uint (count);
518
519 c_bv = (char *) SCM_BYTEVECTOR_CONTENTS (bv);
520 c_len = SCM_BYTEVECTOR_LENGTH (bv);
521
522 if (SCM_UNLIKELY (c_start + c_count > c_len))
523 scm_out_of_range (FUNC_NAME, count);
524
525 if (SCM_LIKELY (c_count > 0))
526 c_read = scm_c_read (port, c_bv + c_start, c_count);
527 else
528 /* Don't invoke `scm_c_read ()' since it may block. */
529 c_read = 0;
530
531 if ((c_read == 0) && (c_count > 0))
532 {
533 if (SCM_EOF_OBJECT_P (scm_peek_char (port)))
534 result = SCM_EOF_VAL;
535 else
536 result = SCM_I_MAKINUM (0);
537 }
538 else
539 result = scm_from_size_t (c_read);
540
541 return result;
542}
543#undef FUNC_NAME
544
545
546SCM_DEFINE (scm_get_bytevector_some, "get-bytevector-some", 1, 0, 0,
547 (SCM port),
548 "Read from @var{port}, blocking as necessary, until data "
549 "are available or and end-of-file is reached. Return either "
550 "a new bytevector containing the data read or the "
551 "end-of-file object.")
552#define FUNC_NAME s_scm_get_bytevector_some
553{
554 /* Read at least one byte, unless the end-of-file is already reached, and
555 read while characters are available (buffered). */
556
557 SCM result;
558 char *c_bv;
559 unsigned c_len;
560 size_t c_total;
561
562 SCM_VALIDATE_BINARY_INPUT_PORT (1, port);
563
564 c_len = 4096;
565 c_bv = (char *) scm_gc_malloc (c_len, SCM_GC_BYTEVECTOR);
566 c_total = 0;
567
568 do
569 {
570 int c_chr;
571
572 if (c_total + 1 > c_len)
573 {
574 /* Grow the bytevector. */
575 c_bv = (char *) scm_gc_realloc (c_bv, c_len, c_len * 2,
576 SCM_GC_BYTEVECTOR);
577 c_len *= 2;
578 }
579
580 /* We can't use `scm_c_read ()' since it blocks. */
581 c_chr = scm_getc (port);
582 if (c_chr != EOF)
583 {
584 c_bv[c_total] = (char) c_chr;
585 c_total++;
586 }
587 }
588 while ((scm_is_true (scm_char_ready_p (port)))
589 && (!SCM_EOF_OBJECT_P (scm_peek_char (port))));
590
591 if (c_total == 0)
592 {
593 result = SCM_EOF_VAL;
594 scm_gc_free (c_bv, c_len, SCM_GC_BYTEVECTOR);
595 }
596 else
597 {
598 if (c_len > c_total)
599 {
600 /* Shrink the bytevector. */
601 c_bv = (char *) scm_gc_realloc (c_bv, c_len, c_total,
602 SCM_GC_BYTEVECTOR);
603 c_len = (unsigned) c_total;
604 }
605
606 result = scm_c_take_bytevector ((signed char *) c_bv, c_len);
607 }
608
609 return result;
610}
611#undef FUNC_NAME
612
613SCM_DEFINE (scm_get_bytevector_all, "get-bytevector-all", 1, 0, 0,
614 (SCM port),
615 "Read from @var{port}, blocking as necessary, until "
616 "the end-of-file is reached. Return either "
617 "a new bytevector containing the data read or the "
618 "end-of-file object (if no data were available).")
619#define FUNC_NAME s_scm_get_bytevector_all
620{
621 SCM result;
622 char *c_bv;
623 unsigned c_len, c_count;
624 size_t c_read, c_total;
625
626 SCM_VALIDATE_BINARY_INPUT_PORT (1, port);
627
628 c_len = c_count = 4096;
629 c_bv = (char *) scm_gc_malloc (c_len, SCM_GC_BYTEVECTOR);
630 c_total = c_read = 0;
631
632 do
633 {
634 if (c_total + c_read > c_len)
635 {
636 /* Grow the bytevector. */
637 c_bv = (char *) scm_gc_realloc (c_bv, c_len, c_len * 2,
638 SCM_GC_BYTEVECTOR);
639 c_count = c_len;
640 c_len *= 2;
641 }
642
643 /* `scm_c_read ()' blocks until C_COUNT bytes are available or EOF is
644 reached. */
645 c_read = scm_c_read (port, c_bv + c_total, c_count);
646 c_total += c_read, c_count -= c_read;
647 }
648 while (!SCM_EOF_OBJECT_P (scm_peek_char (port)));
649
650 if (c_total == 0)
651 {
652 result = SCM_EOF_VAL;
653 scm_gc_free (c_bv, c_len, SCM_GC_BYTEVECTOR);
654 }
655 else
656 {
657 if (c_len > c_total)
658 {
659 /* Shrink the bytevector. */
660 c_bv = (char *) scm_gc_realloc (c_bv, c_len, c_total,
661 SCM_GC_BYTEVECTOR);
662 c_len = (unsigned) c_total;
663 }
664
665 result = scm_c_take_bytevector ((signed char *) c_bv, c_len);
666 }
667
668 return result;
669}
670#undef FUNC_NAME
671
672
673\f
674/* Binary output. */
675
676/* We currently don't support specific binary input ports. */
677#define SCM_VALIDATE_BINARY_OUTPUT_PORT SCM_VALIDATE_OPOUTPORT
678
679
680SCM_DEFINE (scm_put_u8, "put-u8", 2, 0, 0,
681 (SCM port, SCM octet),
682 "Write @var{octet} to binary port @var{port}.")
683#define FUNC_NAME s_scm_put_u8
684{
685 scm_t_uint8 c_octet;
686
687 SCM_VALIDATE_BINARY_OUTPUT_PORT (1, port);
688 c_octet = scm_to_uint8 (octet);
689
690 scm_putc ((char) c_octet, port);
691
692 return SCM_UNSPECIFIED;
693}
694#undef FUNC_NAME
695
696SCM_DEFINE (scm_put_bytevector, "put-bytevector", 2, 2, 0,
697 (SCM port, SCM bv, SCM start, SCM count),
698 "Write the contents of @var{bv} to @var{port}, optionally "
699 "starting at index @var{start} and limiting to @var{count} "
700 "octets.")
701#define FUNC_NAME s_scm_put_bytevector
702{
703 char *c_bv;
704 unsigned c_start, c_count, c_len;
705
706 SCM_VALIDATE_BINARY_OUTPUT_PORT (1, port);
707 SCM_VALIDATE_BYTEVECTOR (2, bv);
708
709 c_len = SCM_BYTEVECTOR_LENGTH (bv);
710 c_bv = (char *) SCM_BYTEVECTOR_CONTENTS (bv);
711
712 if (start != SCM_UNDEFINED)
713 {
714 c_start = scm_to_uint (start);
715
716 if (count != SCM_UNDEFINED)
717 {
718 c_count = scm_to_uint (count);
719 if (SCM_UNLIKELY (c_start + c_count > c_len))
720 scm_out_of_range (FUNC_NAME, count);
721 }
722 else
723 {
724 if (SCM_UNLIKELY (c_start >= c_len))
725 scm_out_of_range (FUNC_NAME, start);
726 else
727 c_count = c_len - c_start;
728 }
729 }
730 else
731 c_start = 0, c_count = c_len;
732
733 scm_c_write (port, c_bv + c_start, c_count);
734
735 return SCM_UNSPECIFIED;
736}
737#undef FUNC_NAME
738
739
740\f
741/* Bytevector output port ("bop" for short). */
742
743/* Implementation of "bops".
744
745 Each bop has an internal buffer, of type `scm_t_bop_buffer', attached to
746 it. The procedure returned along with the output port is actually an
747 applicable SMOB. The SMOB holds a reference to the port. When applied,
748 the SMOB swallows the port's internal buffer, turning it into a
749 bytevector, and resets it.
750
751 XXX: Access to a bop's internal buffer is not thread-safe. */
752
753static scm_t_bits bytevector_output_port_type = 0;
754
755SCM_SMOB (bytevector_output_port_procedure,
756 "r6rs-bytevector-output-port-procedure",
757 0);
758
759#define SCM_GC_BOP "r6rs-bytevector-output-port"
760#define SCM_BOP_BUFFER_INITIAL_SIZE 4096
761
762/* Representation of a bop's internal buffer. */
763typedef struct
764{
765 size_t total_len;
766 size_t len;
767 size_t pos;
768 char *buffer;
769} scm_t_bop_buffer;
770
771
772/* Accessing a bop's buffer. */
773#define SCM_BOP_BUFFER(_port) \
774 ((scm_t_bop_buffer *) SCM_STREAM (_port))
775#define SCM_SET_BOP_BUFFER(_port, _buf) \
776 (SCM_SETSTREAM ((_port), (scm_t_bits) (_buf)))
777
778
779static inline void
780bop_buffer_init (scm_t_bop_buffer *buf)
781{
782 buf->total_len = buf->len = buf->pos = 0;
783 buf->buffer = NULL;
784}
785
786static inline void
787bop_buffer_grow (scm_t_bop_buffer *buf, size_t min_size)
788{
789 char *new_buf;
790 size_t new_size;
791
792 for (new_size = buf->total_len
793 ? buf->total_len : SCM_BOP_BUFFER_INITIAL_SIZE;
794 new_size < min_size;
795 new_size *= 2);
796
797 if (buf->buffer)
798 new_buf = scm_gc_realloc ((void *) buf->buffer, buf->total_len,
799 new_size, SCM_GC_BOP);
800 else
801 new_buf = scm_gc_malloc (new_size, SCM_GC_BOP);
802
803 buf->buffer = new_buf;
804 buf->total_len = new_size;
805}
806
807static inline SCM
808make_bop (void)
809{
810 SCM port, bop_proc;
811 scm_t_port *c_port;
812 scm_t_bop_buffer *buf;
813 const unsigned long mode_bits = SCM_OPN | SCM_WRTNG;
814
815 port = scm_new_port_table_entry (bytevector_output_port_type);
816
817 buf = (scm_t_bop_buffer *) scm_gc_malloc (sizeof (* buf), SCM_GC_BOP);
818 bop_buffer_init (buf);
819
820 c_port = SCM_PTAB_ENTRY (port);
821 c_port->write_buf = c_port->write_pos = c_port->write_end = NULL;
822 c_port->write_buf_size = 0;
823
824 SCM_SET_BOP_BUFFER (port, buf);
825
826 /* Mark PORT as open and writable. */
827 SCM_SET_CELL_TYPE (port, bytevector_output_port_type | mode_bits);
828
829 /* Make the bop procedure. */
830 SCM_NEWSMOB (bop_proc, bytevector_output_port_procedure,
831 SCM_PACK (port));
832
833 return (scm_values (scm_list_2 (port, bop_proc)));
834}
835
1ee2c72e
LC
836/* Write SIZE octets from DATA to PORT. */
837static void
838bop_write (SCM port, const void *data, size_t size)
839{
840 scm_t_bop_buffer *buf;
841
842 buf = SCM_BOP_BUFFER (port);
843
844 if (buf->pos + size > buf->total_len)
845 bop_buffer_grow (buf, buf->pos + size);
846
847 memcpy (buf->buffer + buf->pos, data, size);
848 buf->pos += size;
849 buf->len = (buf->len > buf->pos) ? buf->len : buf->pos;
850}
851
f1ce9199
LC
852static scm_t_off
853bop_seek (SCM port, scm_t_off offset, int whence)
1ee2c72e
LC
854#define FUNC_NAME "bop_seek"
855{
856 scm_t_bop_buffer *buf;
857
858 buf = SCM_BOP_BUFFER (port);
859 switch (whence)
860 {
861 case SEEK_CUR:
f1ce9199 862 offset += (scm_t_off) buf->pos;
1ee2c72e
LC
863 /* Fall through. */
864
865 case SEEK_SET:
866 if (offset < 0 || (unsigned) offset > buf->len)
867 scm_out_of_range (FUNC_NAME, scm_from_int (offset));
868 else
869 buf->pos = offset;
870 break;
871
872 case SEEK_END:
873 if (offset < 0 || (unsigned) offset >= buf->len)
874 scm_out_of_range (FUNC_NAME, scm_from_int (offset));
875 else
876 buf->pos = buf->len - (offset + 1);
877 break;
878
879 default:
880 scm_wrong_type_arg_msg (FUNC_NAME, 0, port,
881 "invalid `seek' parameter");
882 }
883
884 return buf->pos;
885}
886#undef FUNC_NAME
887
888/* Fetch data from a bop. */
889SCM_SMOB_APPLY (bytevector_output_port_procedure,
890 bop_proc_apply, 0, 0, 0, (SCM bop_proc))
891{
892 SCM port, bv;
893 scm_t_bop_buffer *buf, result_buf;
894
895 port = SCM_PACK (SCM_SMOB_DATA (bop_proc));
896 buf = SCM_BOP_BUFFER (port);
897
898 result_buf = *buf;
899 bop_buffer_init (buf);
900
901 if (result_buf.len == 0)
902 bv = scm_c_take_bytevector (NULL, 0);
903 else
904 {
905 if (result_buf.total_len > result_buf.len)
906 /* Shrink the buffer. */
907 result_buf.buffer = scm_gc_realloc ((void *) result_buf.buffer,
908 result_buf.total_len,
909 result_buf.len,
910 SCM_GC_BOP);
911
912 bv = scm_c_take_bytevector ((signed char *) result_buf.buffer,
913 result_buf.len);
914 }
915
916 return bv;
917}
918
1ee2c72e
LC
919SCM_DEFINE (scm_open_bytevector_output_port,
920 "open-bytevector-output-port", 0, 1, 0,
921 (SCM transcoder),
922 "Return two values: an output port and a procedure. The latter "
923 "should be called with zero arguments to obtain a bytevector "
924 "containing the data accumulated by the port.")
925#define FUNC_NAME s_scm_open_bytevector_output_port
926{
927 if (!SCM_UNBNDP (transcoder) && !scm_is_false (transcoder))
928 transcoders_not_implemented ();
929
930 return (make_bop ());
931}
932#undef FUNC_NAME
933
934static inline void
935initialize_bytevector_output_ports (void)
936{
937 bytevector_output_port_type =
938 scm_make_port_type ("r6rs-bytevector-output-port",
939 NULL, bop_write);
940
941 scm_set_port_seek (bytevector_output_port_type, bop_seek);
1ee2c72e
LC
942}
943
944\f
945/* Custom binary output port ("cbop" for short). */
946
947static scm_t_bits custom_binary_output_port_type;
948
949/* Return the various procedures of PORT. */
950#define SCM_CBOP_WRITE_PROC(_port) \
951 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 0)
952
953
954static inline SCM
955make_cbop (SCM write_proc, SCM get_position_proc,
956 SCM set_position_proc, SCM close_proc)
957{
958 SCM port, method_vector;
959 scm_t_port *c_port;
960 const unsigned long mode_bits = SCM_OPN | SCM_WRTNG;
961
962 /* Store the various methods and bytevector in a vector. */
963 method_vector = scm_c_make_vector (4, SCM_BOOL_F);
964 SCM_SIMPLE_VECTOR_SET (method_vector, 0, write_proc);
965 SCM_SIMPLE_VECTOR_SET (method_vector, 1, get_position_proc);
966 SCM_SIMPLE_VECTOR_SET (method_vector, 2, set_position_proc);
967 SCM_SIMPLE_VECTOR_SET (method_vector, 3, close_proc);
968
969 port = scm_new_port_table_entry (custom_binary_output_port_type);
970
971 /* Attach it the method vector. */
972 SCM_SETSTREAM (port, SCM_UNPACK (method_vector));
973
974 /* Have the port directly access the buffer (bytevector). */
975 c_port = SCM_PTAB_ENTRY (port);
976 c_port->write_buf = c_port->write_pos = c_port->write_end = NULL;
977 c_port->write_buf_size = c_port->read_buf_size = 0;
978
979 /* Mark PORT as open, writable and unbuffered. */
980 SCM_SET_CELL_TYPE (port, custom_binary_output_port_type | mode_bits);
981
982 return port;
983}
984
985/* Write SIZE octets from DATA to PORT. */
986static void
987cbop_write (SCM port, const void *data, size_t size)
988#define FUNC_NAME "cbop_write"
989{
990 long int c_result;
991 size_t c_written;
992 SCM bv, write_proc, result;
993
994 /* XXX: Allocating a new bytevector at each `write' call is inefficient,
995 but necessary since (1) we don't control the lifetime of the buffer
996 pointed to by DATA, and (2) the `write!' procedure could capture the
997 bytevector it is passed. */
998 bv = scm_c_make_bytevector (size);
999 memcpy (SCM_BYTEVECTOR_CONTENTS (bv), data, size);
1000
1001 write_proc = SCM_CBOP_WRITE_PROC (port);
1002
1003 /* Since the `write' procedure of Guile's ports has type `void', it must
1004 try hard to write exactly SIZE bytes, regardless of how many bytes the
1005 sink can handle. */
1006 for (c_written = 0;
1007 c_written < size;
1008 c_written += c_result)
1009 {
1010 result = scm_call_3 (write_proc, bv,
1011 scm_from_size_t (c_written),
1012 scm_from_size_t (size - c_written));
1013
1014 c_result = scm_to_long (result);
1015 if (SCM_UNLIKELY (c_result < 0
1016 || (size_t) c_result > (size - c_written)))
1017 scm_wrong_type_arg_msg (FUNC_NAME, 0, result,
1018 "R6RS custom binary output port `write!' "
1019 "returned a incorrect integer");
1020 }
1021}
1022#undef FUNC_NAME
1023
1024
1025SCM_DEFINE (scm_make_custom_binary_output_port,
1026 "make-custom-binary-output-port", 5, 0, 0,
1027 (SCM id, SCM write_proc, SCM get_position_proc,
1028 SCM set_position_proc, SCM close_proc),
1029 "Return a new custom binary output port whose output is drained "
1030 "by invoking @var{write_proc} and passing it a bytevector, an "
1031 "index where octets should be written, and an octet count.")
1032#define FUNC_NAME s_scm_make_custom_binary_output_port
1033{
1034 SCM_VALIDATE_STRING (1, id);
1035 SCM_VALIDATE_PROC (2, write_proc);
1036
1037 if (!scm_is_false (get_position_proc))
1038 SCM_VALIDATE_PROC (3, get_position_proc);
1039
1040 if (!scm_is_false (set_position_proc))
1041 SCM_VALIDATE_PROC (4, set_position_proc);
1042
1043 if (!scm_is_false (close_proc))
1044 SCM_VALIDATE_PROC (5, close_proc);
1045
1046 return (make_cbop (write_proc, get_position_proc, set_position_proc,
1047 close_proc));
1048}
1049#undef FUNC_NAME
1050
1051
1052/* Instantiate the custom binary output port type. */
1053static inline void
1054initialize_custom_binary_output_ports (void)
1055{
1056 custom_binary_output_port_type =
1057 scm_make_port_type ("r6rs-custom-binary-output-port",
1058 NULL, cbop_write);
1059
1ee2c72e
LC
1060 scm_set_port_seek (custom_binary_output_port_type, cbp_seek);
1061 scm_set_port_close (custom_binary_output_port_type, cbp_close);
1062}
1063
1064\f
1065/* Initialization. */
1066
1067void
1068scm_init_r6rs_ports (void)
1069{
62e9a9b7 1070#include "libguile/r6rs-ports.x"
1ee2c72e
LC
1071
1072 initialize_bytevector_input_ports ();
1073 initialize_custom_binary_input_ports ();
1074 initialize_bytevector_output_ports ();
1075 initialize_custom_binary_output_ports ();
1076}