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