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