Update README on using libraries in non-standard locations
[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
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but 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 02110-1301 USA
16 */
17
18#ifdef HAVE_CONFIG_H
19# include <config.h>
20#endif
21
22#ifdef HAVE_UNISTD_H
23# include <unistd.h>
24#endif
25
26#include <string.h>
27#include <stdio.h>
28#include <assert.h>
29
30#include "libguile/_scm.h"
31#include "libguile/bytevectors.h"
32#include "libguile/chars.h"
33#include "libguile/eval.h"
34#include "libguile/r6rs-ports.h"
35#include "libguile/strings.h"
36#include "libguile/validate.h"
37#include "libguile/values.h"
38#include "libguile/vectors.h"
39
40
41\f
42/* Unimplemented features. */
43
44
45/* Transoders are currently not implemented since Guile 1.8 is not
46 Unicode-capable. Thus, most of the code here assumes the use of the
47 binary transcoder. */
48static inline void
49transcoders_not_implemented (void)
50{
51 fprintf (stderr, "%s: warning: transcoders not implemented\n",
52 PACKAGE_NAME);
53}
54
55\f
56/* End-of-file object. */
57
58SCM_DEFINE (scm_eof_object, "eof-object", 0, 0, 0,
59 (void),
60 "Return the end-of-file object.")
61#define FUNC_NAME s_scm_eof_object
62{
63 return (SCM_EOF_VAL);
64}
65#undef FUNC_NAME
66
67\f
68/* Input ports. */
69
70#ifndef MIN
71# define MIN(a,b) ((a) < (b) ? (a) : (b))
72#endif
73
74/* Bytevector input ports or "bip" for short. */
75static scm_t_bits bytevector_input_port_type = 0;
76
77static inline SCM
78make_bip (SCM bv)
79{
80 SCM port;
81 char *c_bv;
82 unsigned c_len;
83 scm_t_port *c_port;
84 const unsigned long mode_bits = SCM_OPN | SCM_RDNG;
85
86 port = scm_new_port_table_entry (bytevector_input_port_type);
87
88 /* Prevent BV from being GC'd. */
89 SCM_SETSTREAM (port, SCM_UNPACK (bv));
90
91 /* Have the port directly access the bytevector. */
92 c_bv = (char *) SCM_BYTEVECTOR_CONTENTS (bv);
93 c_len = SCM_BYTEVECTOR_LENGTH (bv);
94
95 c_port = SCM_PTAB_ENTRY (port);
96 c_port->read_pos = c_port->read_buf = (unsigned char *) c_bv;
97 c_port->read_end = (unsigned char *) c_bv + c_len;
98 c_port->read_buf_size = c_len;
99
100 /* Mark PORT as open, readable and unbuffered (hmm, how elegant...). */
101 SCM_SET_CELL_TYPE (port, bytevector_input_port_type | mode_bits);
102
103 return port;
104}
105
106static SCM
107bip_mark (SCM port)
108{
109 /* Mark the underlying bytevector. */
110 return (SCM_PACK (SCM_STREAM (port)));
111}
112
113static int
114bip_fill_input (SCM port)
115{
116 int result;
117 scm_t_port *c_port = SCM_PTAB_ENTRY (port);
118
119 if (c_port->read_pos >= c_port->read_end)
120 result = EOF;
121 else
122 result = (int) *c_port->read_pos;
123
124 return result;
125}
126
127static off_t
128bip_seek (SCM port, off_t offset, int whence)
129#define FUNC_NAME "bip_seek"
130{
131 off_t c_result = 0;
132 scm_t_port *c_port = SCM_PTAB_ENTRY (port);
133
134 switch (whence)
135 {
136 case SEEK_CUR:
137 offset += c_port->read_pos - c_port->read_buf;
138 /* Fall through. */
139
140 case SEEK_SET:
141 if (c_port->read_buf + offset < c_port->read_end)
142 {
143 c_port->read_pos = c_port->read_buf + offset;
144 c_result = offset;
145 }
146 else
147 scm_out_of_range (FUNC_NAME, scm_from_int (offset));
148 break;
149
150 case SEEK_END:
151 if (c_port->read_end - offset >= c_port->read_buf)
152 {
153 c_port->read_pos = c_port->read_end - offset;
154 c_result = c_port->read_pos - c_port->read_buf;
155 }
156 else
157 scm_out_of_range (FUNC_NAME, scm_from_int (offset));
158 break;
159
160 default:
161 scm_wrong_type_arg_msg (FUNC_NAME, 0, port,
162 "invalid `seek' parameter");
163 }
164
165 return c_result;
166}
167#undef FUNC_NAME
168
169
170/* Instantiate the bytevector input port type. */
171static inline void
172initialize_bytevector_input_ports (void)
173{
174 bytevector_input_port_type =
175 scm_make_port_type ("r6rs-bytevector-input-port", bip_fill_input,
176 NULL);
177
178 scm_set_port_mark (bytevector_input_port_type, bip_mark);
179 scm_set_port_seek (bytevector_input_port_type, bip_seek);
180}
181
182
183SCM_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
209static SCM
210cbp_mark (SCM port)
211{
212 /* Mark the underlying method and object vector. */
213 return (SCM_PACK (SCM_STREAM (port)));
214}
215
216static off_t
217cbp_seek (SCM port, off_t offset, int whence)
218#define FUNC_NAME "cbp_seek"
219{
220 SCM result;
221 off_t c_result = 0;
222
223 switch (whence)
224 {
225 case SEEK_CUR:
226 {
227 SCM get_position_proc;
228
229 get_position_proc = SCM_CBP_GET_POSITION_PROC (port);
230 if (SCM_LIKELY (scm_is_true (get_position_proc)))
231 result = scm_call_0 (get_position_proc);
232 else
233 scm_wrong_type_arg_msg (FUNC_NAME, 0, port,
234 "R6RS custom binary port does not "
235 "support `port-position'");
236
237 offset += scm_to_int (result);
238 /* Fall through. */
239 }
240
241 case SEEK_SET:
242 {
243 SCM set_position_proc;
244
245 set_position_proc = SCM_CBP_SET_POSITION_PROC (port);
246 if (SCM_LIKELY (scm_is_true (set_position_proc)))
247 result = scm_call_1 (set_position_proc, scm_from_int (offset));
248 else
249 scm_wrong_type_arg_msg (FUNC_NAME, 0, port,
250 "R6RS custom binary port does not "
251 "support `set-port-position!'");
252
253 /* Assuming setting the position succeeded. */
254 c_result = offset;
255 break;
256 }
257
258 default:
259 /* `SEEK_END' cannot be supported. */
260 scm_wrong_type_arg_msg (FUNC_NAME, 0, port,
261 "R6RS custom binary ports do not "
262 "support `SEEK_END'");
263 }
264
265 return c_result;
266}
267#undef FUNC_NAME
268
269static int
270cbp_close (SCM port)
271{
272 SCM close_proc;
273
274 close_proc = SCM_CBP_CLOSE_PROC (port);
275 if (scm_is_true (close_proc))
276 /* Invoke the `close' thunk. */
277 scm_call_0 (close_proc);
278
279 return 1;
280}
281
282\f
283/* Custom binary input port ("cbip" for short). */
284
285static scm_t_bits custom_binary_input_port_type = 0;
286
287/* Size of the buffer embedded in custom binary input ports. */
288#define CBIP_BUFFER_SIZE 4096
289
290/* Return the bytevector associated with PORT. */
291#define SCM_CBIP_BYTEVECTOR(_port) \
292 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 4)
293
294/* Return the various procedures of PORT. */
295#define SCM_CBIP_READ_PROC(_port) \
296 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 0)
297
298
299static inline SCM
300make_cbip (SCM read_proc, SCM get_position_proc,
301 SCM set_position_proc, SCM close_proc)
302{
303 SCM port, bv, method_vector;
304 char *c_bv;
305 unsigned c_len;
306 scm_t_port *c_port;
307 const unsigned long mode_bits = SCM_OPN | SCM_RDNG;
308
309 /* Use a bytevector as the underlying buffer. */
310 c_len = CBIP_BUFFER_SIZE;
311 bv = scm_c_make_bytevector (c_len);
312 c_bv = (char *) SCM_BYTEVECTOR_CONTENTS (bv);
313
314 /* Store the various methods and bytevector in a vector. */
315 method_vector = scm_c_make_vector (5, SCM_BOOL_F);
316 SCM_SIMPLE_VECTOR_SET (method_vector, 4, bv);
317 SCM_SIMPLE_VECTOR_SET (method_vector, 0, read_proc);
318 SCM_SIMPLE_VECTOR_SET (method_vector, 1, get_position_proc);
319 SCM_SIMPLE_VECTOR_SET (method_vector, 2, set_position_proc);
320 SCM_SIMPLE_VECTOR_SET (method_vector, 3, close_proc);
321
322 port = scm_new_port_table_entry (custom_binary_input_port_type);
323
324 /* Attach it the method vector. */
325 SCM_SETSTREAM (port, SCM_UNPACK (method_vector));
326
327 /* Have the port directly access the buffer (bytevector). */
328 c_port = SCM_PTAB_ENTRY (port);
329 c_port->read_pos = c_port->read_buf = (unsigned char *) c_bv;
330 c_port->read_end = (unsigned char *) c_bv;
331 c_port->read_buf_size = c_len;
332
333 /* Mark PORT as open, readable and unbuffered (hmm, how elegant...). */
334 SCM_SET_CELL_TYPE (port, custom_binary_input_port_type | mode_bits);
335
336 return port;
337}
338
339static int
340cbip_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
385SCM_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. */
413static inline void
414initialize_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_mark (custom_binary_input_port_type, cbp_mark);
421 scm_set_port_seek (custom_binary_input_port_type, cbp_seek);
422 scm_set_port_close (custom_binary_input_port_type, cbp_close);
423}
424
425
426\f
427/* Binary input. */
428
429/* We currently don't support specific binary input ports. */
430#define SCM_VALIDATE_BINARY_INPUT_PORT SCM_VALIDATE_OPINPORT
431
432SCM_DEFINE (scm_get_u8, "get-u8", 1, 0, 0,
433 (SCM port),
434 "Read an octet from @var{port}, a binary input port, "
435 "blocking as necessary.")
436#define FUNC_NAME s_scm_get_u8
437{
438 SCM result;
439 int c_result;
440
441 SCM_VALIDATE_BINARY_INPUT_PORT (1, port);
442
443 c_result = scm_getc (port);
444 if (c_result == EOF)
445 result = SCM_EOF_VAL;
446 else
447 result = SCM_I_MAKINUM ((unsigned char) c_result);
448
449 return result;
450}
451#undef FUNC_NAME
452
453SCM_DEFINE (scm_lookahead_u8, "lookahead-u8", 1, 0, 0,
454 (SCM port),
455 "Like @code{get-u8} but does not update @var{port} to "
456 "point past the octet.")
457#define FUNC_NAME s_scm_lookahead_u8
458{
459 SCM result;
460
461 SCM_VALIDATE_BINARY_INPUT_PORT (1, port);
462
463 result = scm_peek_char (port);
464 if (SCM_CHARP (result))
465 result = SCM_I_MAKINUM ((signed char) SCM_CHAR (result));
466 else
467 result = SCM_EOF_VAL;
468
469 return result;
470}
471#undef FUNC_NAME
472
473SCM_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_EOF_OBJECT_P (scm_peek_char (port)))
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
516SCM_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_EOF_OBJECT_P (scm_peek_char (port)))
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
561SCM_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 (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_getc (port);
597 if (c_chr != EOF)
598 {
599 c_bv[c_total] = (char) c_chr;
600 c_total++;
601 }
602 }
603 while ((scm_is_true (scm_char_ready_p (port)))
604 && (!SCM_EOF_OBJECT_P (scm_peek_char (port))));
605
606 if (c_total == 0)
607 {
608 result = SCM_EOF_VAL;
609 scm_gc_free (c_bv, c_len, SCM_GC_BYTEVECTOR);
610 }
611 else
612 {
613 if (c_len > c_total)
614 {
615 /* Shrink the bytevector. */
616 c_bv = (char *) scm_gc_realloc (c_bv, c_len, c_total,
617 SCM_GC_BYTEVECTOR);
618 c_len = (unsigned) c_total;
619 }
620
621 result = scm_c_take_bytevector ((signed char *) c_bv, c_len);
622 }
623
624 return result;
625}
626#undef FUNC_NAME
627
628SCM_DEFINE (scm_get_bytevector_all, "get-bytevector-all", 1, 0, 0,
629 (SCM port),
630 "Read from @var{port}, blocking as necessary, until "
631 "the end-of-file is reached. Return either "
632 "a new bytevector containing the data read or the "
633 "end-of-file object (if no data were available).")
634#define FUNC_NAME s_scm_get_bytevector_all
635{
636 SCM result;
637 char *c_bv;
638 unsigned c_len, c_count;
639 size_t c_read, c_total;
640
641 SCM_VALIDATE_BINARY_INPUT_PORT (1, port);
642
643 c_len = c_count = 4096;
644 c_bv = (char *) scm_gc_malloc (c_len, SCM_GC_BYTEVECTOR);
645 c_total = c_read = 0;
646
647 do
648 {
649 if (c_total + c_read > c_len)
650 {
651 /* Grow the bytevector. */
652 c_bv = (char *) scm_gc_realloc (c_bv, c_len, c_len * 2,
653 SCM_GC_BYTEVECTOR);
654 c_count = c_len;
655 c_len *= 2;
656 }
657
658 /* `scm_c_read ()' blocks until C_COUNT bytes are available or EOF is
659 reached. */
660 c_read = scm_c_read (port, c_bv + c_total, c_count);
661 c_total += c_read, c_count -= c_read;
662 }
663 while (!SCM_EOF_OBJECT_P (scm_peek_char (port)));
664
665 if (c_total == 0)
666 {
667 result = SCM_EOF_VAL;
668 scm_gc_free (c_bv, c_len, SCM_GC_BYTEVECTOR);
669 }
670 else
671 {
672 if (c_len > c_total)
673 {
674 /* Shrink the bytevector. */
675 c_bv = (char *) scm_gc_realloc (c_bv, c_len, c_total,
676 SCM_GC_BYTEVECTOR);
677 c_len = (unsigned) c_total;
678 }
679
680 result = scm_c_take_bytevector ((signed char *) c_bv, c_len);
681 }
682
683 return result;
684}
685#undef FUNC_NAME
686
687
688\f
689/* Binary output. */
690
691/* We currently don't support specific binary input ports. */
692#define SCM_VALIDATE_BINARY_OUTPUT_PORT SCM_VALIDATE_OPOUTPORT
693
694
695SCM_DEFINE (scm_put_u8, "put-u8", 2, 0, 0,
696 (SCM port, SCM octet),
697 "Write @var{octet} to binary port @var{port}.")
698#define FUNC_NAME s_scm_put_u8
699{
700 scm_t_uint8 c_octet;
701
702 SCM_VALIDATE_BINARY_OUTPUT_PORT (1, port);
703 c_octet = scm_to_uint8 (octet);
704
705 scm_putc ((char) c_octet, port);
706
707 return SCM_UNSPECIFIED;
708}
709#undef FUNC_NAME
710
711SCM_DEFINE (scm_put_bytevector, "put-bytevector", 2, 2, 0,
712 (SCM port, SCM bv, SCM start, SCM count),
713 "Write the contents of @var{bv} to @var{port}, optionally "
714 "starting at index @var{start} and limiting to @var{count} "
715 "octets.")
716#define FUNC_NAME s_scm_put_bytevector
717{
718 char *c_bv;
719 unsigned c_start, c_count, c_len;
720
721 SCM_VALIDATE_BINARY_OUTPUT_PORT (1, port);
722 SCM_VALIDATE_BYTEVECTOR (2, bv);
723
724 c_len = SCM_BYTEVECTOR_LENGTH (bv);
725 c_bv = (char *) SCM_BYTEVECTOR_CONTENTS (bv);
726
727 if (start != SCM_UNDEFINED)
728 {
729 c_start = scm_to_uint (start);
730
731 if (count != SCM_UNDEFINED)
732 {
733 c_count = scm_to_uint (count);
734 if (SCM_UNLIKELY (c_start + c_count > c_len))
735 scm_out_of_range (FUNC_NAME, count);
736 }
737 else
738 {
739 if (SCM_UNLIKELY (c_start >= c_len))
740 scm_out_of_range (FUNC_NAME, start);
741 else
742 c_count = c_len - c_start;
743 }
744 }
745 else
746 c_start = 0, c_count = c_len;
747
748 scm_c_write (port, c_bv + c_start, c_count);
749
750 return SCM_UNSPECIFIED;
751}
752#undef FUNC_NAME
753
754
755\f
756/* Bytevector output port ("bop" for short). */
757
758/* Implementation of "bops".
759
760 Each bop has an internal buffer, of type `scm_t_bop_buffer', attached to
761 it. The procedure returned along with the output port is actually an
762 applicable SMOB. The SMOB holds a reference to the port. When applied,
763 the SMOB swallows the port's internal buffer, turning it into a
764 bytevector, and resets it.
765
766 XXX: Access to a bop's internal buffer is not thread-safe. */
767
768static scm_t_bits bytevector_output_port_type = 0;
769
770SCM_SMOB (bytevector_output_port_procedure,
771 "r6rs-bytevector-output-port-procedure",
772 0);
773
774#define SCM_GC_BOP "r6rs-bytevector-output-port"
775#define SCM_BOP_BUFFER_INITIAL_SIZE 4096
776
777/* Representation of a bop's internal buffer. */
778typedef struct
779{
780 size_t total_len;
781 size_t len;
782 size_t pos;
783 char *buffer;
784} scm_t_bop_buffer;
785
786
787/* Accessing a bop's buffer. */
788#define SCM_BOP_BUFFER(_port) \
789 ((scm_t_bop_buffer *) SCM_STREAM (_port))
790#define SCM_SET_BOP_BUFFER(_port, _buf) \
791 (SCM_SETSTREAM ((_port), (scm_t_bits) (_buf)))
792
793
794static inline void
795bop_buffer_init (scm_t_bop_buffer *buf)
796{
797 buf->total_len = buf->len = buf->pos = 0;
798 buf->buffer = NULL;
799}
800
801static inline void
802bop_buffer_grow (scm_t_bop_buffer *buf, size_t min_size)
803{
804 char *new_buf;
805 size_t new_size;
806
807 for (new_size = buf->total_len
808 ? buf->total_len : SCM_BOP_BUFFER_INITIAL_SIZE;
809 new_size < min_size;
810 new_size *= 2);
811
812 if (buf->buffer)
813 new_buf = scm_gc_realloc ((void *) buf->buffer, buf->total_len,
814 new_size, SCM_GC_BOP);
815 else
816 new_buf = scm_gc_malloc (new_size, SCM_GC_BOP);
817
818 buf->buffer = new_buf;
819 buf->total_len = new_size;
820}
821
822static inline SCM
823make_bop (void)
824{
825 SCM port, bop_proc;
826 scm_t_port *c_port;
827 scm_t_bop_buffer *buf;
828 const unsigned long mode_bits = SCM_OPN | SCM_WRTNG;
829
830 port = scm_new_port_table_entry (bytevector_output_port_type);
831
832 buf = (scm_t_bop_buffer *) scm_gc_malloc (sizeof (* buf), SCM_GC_BOP);
833 bop_buffer_init (buf);
834
835 c_port = SCM_PTAB_ENTRY (port);
836 c_port->write_buf = c_port->write_pos = c_port->write_end = NULL;
837 c_port->write_buf_size = 0;
838
839 SCM_SET_BOP_BUFFER (port, buf);
840
841 /* Mark PORT as open and writable. */
842 SCM_SET_CELL_TYPE (port, bytevector_output_port_type | mode_bits);
843
844 /* Make the bop procedure. */
845 SCM_NEWSMOB (bop_proc, bytevector_output_port_procedure,
846 SCM_PACK (port));
847
848 return (scm_values (scm_list_2 (port, bop_proc)));
849}
850
851static size_t
852bop_free (SCM port)
853{
854 /* The port itself is necessarily freed _after_ the bop proc, since the bop
855 proc holds a reference to it. Thus we can safely free the internal
856 buffer when the bop becomes unreferenced. */
857 scm_t_bop_buffer *buf;
858
859 buf = SCM_BOP_BUFFER (port);
860 if (buf->buffer)
861 scm_gc_free (buf->buffer, buf->total_len, SCM_GC_BOP);
862
863 scm_gc_free (buf, sizeof (* buf), SCM_GC_BOP);
864
865 return 0;
866}
867
868/* Write SIZE octets from DATA to PORT. */
869static void
870bop_write (SCM port, const void *data, size_t size)
871{
872 scm_t_bop_buffer *buf;
873
874 buf = SCM_BOP_BUFFER (port);
875
876 if (buf->pos + size > buf->total_len)
877 bop_buffer_grow (buf, buf->pos + size);
878
879 memcpy (buf->buffer + buf->pos, data, size);
880 buf->pos += size;
881 buf->len = (buf->len > buf->pos) ? buf->len : buf->pos;
882}
883
884static off_t
885bop_seek (SCM port, off_t offset, int whence)
886#define FUNC_NAME "bop_seek"
887{
888 scm_t_bop_buffer *buf;
889
890 buf = SCM_BOP_BUFFER (port);
891 switch (whence)
892 {
893 case SEEK_CUR:
894 offset += (off_t) buf->pos;
895 /* Fall through. */
896
897 case SEEK_SET:
898 if (offset < 0 || (unsigned) offset > buf->len)
899 scm_out_of_range (FUNC_NAME, scm_from_int (offset));
900 else
901 buf->pos = offset;
902 break;
903
904 case SEEK_END:
905 if (offset < 0 || (unsigned) offset >= buf->len)
906 scm_out_of_range (FUNC_NAME, scm_from_int (offset));
907 else
908 buf->pos = buf->len - (offset + 1);
909 break;
910
911 default:
912 scm_wrong_type_arg_msg (FUNC_NAME, 0, port,
913 "invalid `seek' parameter");
914 }
915
916 return buf->pos;
917}
918#undef FUNC_NAME
919
920/* Fetch data from a bop. */
921SCM_SMOB_APPLY (bytevector_output_port_procedure,
922 bop_proc_apply, 0, 0, 0, (SCM bop_proc))
923{
924 SCM port, bv;
925 scm_t_bop_buffer *buf, result_buf;
926
927 port = SCM_PACK (SCM_SMOB_DATA (bop_proc));
928 buf = SCM_BOP_BUFFER (port);
929
930 result_buf = *buf;
931 bop_buffer_init (buf);
932
933 if (result_buf.len == 0)
934 bv = scm_c_take_bytevector (NULL, 0);
935 else
936 {
937 if (result_buf.total_len > result_buf.len)
938 /* Shrink the buffer. */
939 result_buf.buffer = scm_gc_realloc ((void *) result_buf.buffer,
940 result_buf.total_len,
941 result_buf.len,
942 SCM_GC_BOP);
943
944 bv = scm_c_take_bytevector ((signed char *) result_buf.buffer,
945 result_buf.len);
946 }
947
948 return bv;
949}
950
951SCM_SMOB_MARK (bytevector_output_port_procedure, bop_proc_mark,
952 bop_proc)
953{
954 /* Mark the port associated with BOP_PROC. */
955 return (SCM_PACK (SCM_SMOB_DATA (bop_proc)));
956}
957
958
959SCM_DEFINE (scm_open_bytevector_output_port,
960 "open-bytevector-output-port", 0, 1, 0,
961 (SCM transcoder),
962 "Return two values: an output port and a procedure. The latter "
963 "should be called with zero arguments to obtain a bytevector "
964 "containing the data accumulated by the port.")
965#define FUNC_NAME s_scm_open_bytevector_output_port
966{
967 if (!SCM_UNBNDP (transcoder) && !scm_is_false (transcoder))
968 transcoders_not_implemented ();
969
970 return (make_bop ());
971}
972#undef FUNC_NAME
973
974static inline void
975initialize_bytevector_output_ports (void)
976{
977 bytevector_output_port_type =
978 scm_make_port_type ("r6rs-bytevector-output-port",
979 NULL, bop_write);
980
981 scm_set_port_seek (bytevector_output_port_type, bop_seek);
982 scm_set_port_free (bytevector_output_port_type, bop_free);
983}
984
985\f
986/* Custom binary output port ("cbop" for short). */
987
988static scm_t_bits custom_binary_output_port_type;
989
990/* Return the various procedures of PORT. */
991#define SCM_CBOP_WRITE_PROC(_port) \
992 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 0)
993
994
995static inline SCM
996make_cbop (SCM write_proc, SCM get_position_proc,
997 SCM set_position_proc, SCM close_proc)
998{
999 SCM port, method_vector;
1000 scm_t_port *c_port;
1001 const unsigned long mode_bits = SCM_OPN | SCM_WRTNG;
1002
1003 /* Store the various methods and bytevector in a vector. */
1004 method_vector = scm_c_make_vector (4, SCM_BOOL_F);
1005 SCM_SIMPLE_VECTOR_SET (method_vector, 0, write_proc);
1006 SCM_SIMPLE_VECTOR_SET (method_vector, 1, get_position_proc);
1007 SCM_SIMPLE_VECTOR_SET (method_vector, 2, set_position_proc);
1008 SCM_SIMPLE_VECTOR_SET (method_vector, 3, close_proc);
1009
1010 port = scm_new_port_table_entry (custom_binary_output_port_type);
1011
1012 /* Attach it the method vector. */
1013 SCM_SETSTREAM (port, SCM_UNPACK (method_vector));
1014
1015 /* Have the port directly access the buffer (bytevector). */
1016 c_port = SCM_PTAB_ENTRY (port);
1017 c_port->write_buf = c_port->write_pos = c_port->write_end = NULL;
1018 c_port->write_buf_size = c_port->read_buf_size = 0;
1019
1020 /* Mark PORT as open, writable and unbuffered. */
1021 SCM_SET_CELL_TYPE (port, custom_binary_output_port_type | mode_bits);
1022
1023 return port;
1024}
1025
1026/* Write SIZE octets from DATA to PORT. */
1027static void
1028cbop_write (SCM port, const void *data, size_t size)
1029#define FUNC_NAME "cbop_write"
1030{
1031 long int c_result;
1032 size_t c_written;
1033 SCM bv, write_proc, result;
1034
1035 /* XXX: Allocating a new bytevector at each `write' call is inefficient,
1036 but necessary since (1) we don't control the lifetime of the buffer
1037 pointed to by DATA, and (2) the `write!' procedure could capture the
1038 bytevector it is passed. */
1039 bv = scm_c_make_bytevector (size);
1040 memcpy (SCM_BYTEVECTOR_CONTENTS (bv), data, size);
1041
1042 write_proc = SCM_CBOP_WRITE_PROC (port);
1043
1044 /* Since the `write' procedure of Guile's ports has type `void', it must
1045 try hard to write exactly SIZE bytes, regardless of how many bytes the
1046 sink can handle. */
1047 for (c_written = 0;
1048 c_written < size;
1049 c_written += c_result)
1050 {
1051 result = scm_call_3 (write_proc, bv,
1052 scm_from_size_t (c_written),
1053 scm_from_size_t (size - c_written));
1054
1055 c_result = scm_to_long (result);
1056 if (SCM_UNLIKELY (c_result < 0
1057 || (size_t) c_result > (size - c_written)))
1058 scm_wrong_type_arg_msg (FUNC_NAME, 0, result,
1059 "R6RS custom binary output port `write!' "
1060 "returned a incorrect integer");
1061 }
1062}
1063#undef FUNC_NAME
1064
1065
1066SCM_DEFINE (scm_make_custom_binary_output_port,
1067 "make-custom-binary-output-port", 5, 0, 0,
1068 (SCM id, SCM write_proc, SCM get_position_proc,
1069 SCM set_position_proc, SCM close_proc),
1070 "Return a new custom binary output port whose output is drained "
1071 "by invoking @var{write_proc} and passing it a bytevector, an "
1072 "index where octets should be written, and an octet count.")
1073#define FUNC_NAME s_scm_make_custom_binary_output_port
1074{
1075 SCM_VALIDATE_STRING (1, id);
1076 SCM_VALIDATE_PROC (2, write_proc);
1077
1078 if (!scm_is_false (get_position_proc))
1079 SCM_VALIDATE_PROC (3, get_position_proc);
1080
1081 if (!scm_is_false (set_position_proc))
1082 SCM_VALIDATE_PROC (4, set_position_proc);
1083
1084 if (!scm_is_false (close_proc))
1085 SCM_VALIDATE_PROC (5, close_proc);
1086
1087 return (make_cbop (write_proc, get_position_proc, set_position_proc,
1088 close_proc));
1089}
1090#undef FUNC_NAME
1091
1092
1093/* Instantiate the custom binary output port type. */
1094static inline void
1095initialize_custom_binary_output_ports (void)
1096{
1097 custom_binary_output_port_type =
1098 scm_make_port_type ("r6rs-custom-binary-output-port",
1099 NULL, cbop_write);
1100
1101 scm_set_port_mark (custom_binary_output_port_type, cbp_mark);
1102 scm_set_port_seek (custom_binary_output_port_type, cbp_seek);
1103 scm_set_port_close (custom_binary_output_port_type, cbp_close);
1104}
1105
1106\f
1107/* Initialization. */
1108
1109void
1110scm_init_r6rs_ports (void)
1111{
62e9a9b7 1112#include "libguile/r6rs-ports.x"
1ee2c72e
LC
1113
1114 initialize_bytevector_input_ports ();
1115 initialize_custom_binary_input_ports ();
1116 initialize_bytevector_output_ports ();
1117 initialize_custom_binary_output_ports ();
1118}