1 /* port.c -*- mode:c; coding:utf-8; -*-
2 *
3 * Copyright (c) 2010-2021 Takashi Kato <ktakashi@ymail.com>
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
22 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * $Id: $
29 */
30 #include <string.h>
31 #define LIBSAGITTARIUS_BODY
32 #include "sagittarius/private/port.h"
33 #include "sagittarius/private/codec.h"
34 #include "sagittarius/private/core.h"
35 #include "sagittarius/private/clos.h"
36 #include "sagittarius/private/weak.h"
37 #include "sagittarius/private/library.h"
38 #include "sagittarius/private/bytevector.h"
39 #include "sagittarius/private/file.h"
40 #include "sagittarius/private/keyword.h"
41 #include "sagittarius/private/transcoder.h"
42 #include "sagittarius/private/string.h"
43 #include "sagittarius/private/error.h"
44 #include "sagittarius/private/vector.h"
45 #include "sagittarius/private/vm.h"
46 #include "sagittarius/private/pair.h"
47 #include "sagittarius/private/symbol.h"
48 #include "sagittarius/private/writer.h"
49 #include "sagittarius/private/number.h"
50 #include "sagittarius/private/builtin-symbols.h"
51
52 #include "shortnames.incl"
53
54 static SgClass *port_cpl[] = {
55 SG_CLASS_PORT,
56 SG_CLASS_TOP,
57 NULL
58 };
59
60 /* <port> must be an abstract class so that users can't do
61 (make <port>) or something.
62 */
63 static void port_print(SgObject obj, SgPort *port, SgWriteContext *ctx);
64 SG_DEFINE_ABSTRACT_CLASS(Sg_PortClass, port_cpl+1);
65
port_print(SgObject obj,SgPort * port,SgWriteContext * ctx)66 static void port_print(SgObject obj, SgPort *port, SgWriteContext *ctx)
67 {
68 SgPort *p = SG_PORT(obj);
69 SgObject file = SG_FALSE;
70 SgObject transcoder = SG_FALSE;
71
72 SG_PORT_LOCK_WRITE(port);
73 Sg_PutuzUnsafe(port, UC("#<"));
74 if (SG_BINARY_PORTP(p)) {
75 Sg_PutuzUnsafe(port, UC("binary"));
76 } else if (SG_TEXTUAL_PORTP(p)) {
77 Sg_PutuzUnsafe(port, UC("textual"));
78 } else { /* never happen */
79 Sg_PutuzUnsafe(port, UC("-unknown"));
80 }
81 if (SG_BIDIRECTIONAL_PORTP(p)) {
82 /* it's debug purpose anyway */
83 Sg_PutuzUnsafe(port, UC("-bidirectional-port"));
84 } else if (SG_IN_OUT_PORTP(p)) {
85 Sg_PutuzUnsafe(port, UC("-input/output-port"));
86 } else if (SG_INPUT_PORTP(p)) {
87 Sg_PutuzUnsafe(port, UC("-input-port"));
88 } else if (SG_OUTPUT_PORTP(p)) {
89 Sg_PutuzUnsafe(port, UC("-output-port"));
90 }
91 if (SG_CUSTOM_PORTP(p)) {
92 Sg_PutcUnsafe(port, ' ');
93 Sg_Write(SG_CUSTOM_PORT(p)->id, port, SG_WRITE_DISPLAY);
94 }
95 if (SG_BUFFERED_PORTP(p)) {
96 Sg_PutcUnsafe(port, ' ');
97 switch (SG_BUFFERED_PORT(p)->mode) {
98 case SG_BUFFER_MODE_LINE:
99 Sg_PutuzUnsafe(port, UC("line"));
100 break;
101 case SG_BUFFER_MODE_BLOCK:
102 Sg_PutuzUnsafe(port, UC("block"));
103 break;
104 case SG_BUFFER_MODE_NONE:
105 /* never be here but make compiler shut */
106 Sg_PutuzUnsafe(port, UC("none"));
107 break;
108 }
109 }
110
111 file = Sg_FileName(p);
112 if (!SG_FALSEP(file)) {
113 Sg_PutcUnsafe(port, ' ');
114 Sg_Write(file, port, SG_WRITE_DISPLAY);
115 }
116 transcoder = Sg_PortTranscoder(p);
117 if (!SG_FALSEP(transcoder)) {
118 Sg_PutcUnsafe(port, ' ');
119 Sg_PutsUnsafe(port, SG_CODEC_NAME(SG_TRANSCODER_CODEC(transcoder)));
120 }
121 switch (SG_PORT(p)->closed) {
122 case SG_PORT_CLOSED:
123 Sg_PutcUnsafe(port, ' ');
124 Sg_PutuzUnsafe(port, UC("closed"));
125 break;
126 case SG_PORT_PSEUDO:
127 Sg_PutcUnsafe(port, ' ');
128 Sg_PutuzUnsafe(port, UC("pseudo-closed"));
129 break;
130 default: break;
131 }
132 Sg_PutcUnsafe(port, '>');
133 SG_PORT_UNLOCK_WRITE(port);
134 }
135
136 /* file, byte, string and transcoded ports are final */
137 SG_DEFINE_BUILTIN_CLASS(Sg_FilePortClass,
138 port_print, NULL, NULL, NULL,
139 port_cpl);
140
141 SG_DEFINE_BUILTIN_CLASS(Sg_BytePortClass,
142 port_print, NULL, NULL, NULL,
143 port_cpl);
144
145 SG_DEFINE_BUILTIN_CLASS(Sg_StringPortClass,
146 port_print, NULL, NULL, NULL,
147 port_cpl);
148
trans_port_print(SgObject obj,SgPort * port,SgWriteContext * ctx)149 static void trans_port_print(SgObject obj, SgPort *port, SgWriteContext *ctx)
150 {
151 SgTranscodedPort *p = SG_TRANSCODED_PORT(obj);
152 SgObject transcoder = Sg_PortTranscoder(p);
153
154 SG_PORT_LOCK_WRITE(port);
155 Sg_PutuzUnsafe(port, UC("#<transcoded-port"));
156
157 Sg_PutcUnsafe(port, ' ');
158 Sg_PutsUnsafe(port, SG_CODEC_NAME(SG_TRANSCODER_CODEC(transcoder)));
159 Sg_Printf(port, UC(" %A"), p->port);
160
161 switch (SG_PORT(p)->closed) {
162 case SG_PORT_CLOSED:
163 Sg_PutuzUnsafe(port, UC(" closed"));
164 break;
165 case SG_PORT_PSEUDO:
166 Sg_PutuzUnsafe(port, UC(" pseudo-closed"));
167 break;
168 default: break;
169 }
170 Sg_PutcUnsafe(port, '>');
171 SG_PORT_UNLOCK_WRITE(port);
172 }
173 SG_DEFINE_BUILTIN_CLASS(Sg_TranscodedPortClass,
174 trans_port_print, NULL, NULL, NULL,
175 port_cpl);
176
buf_port_print(SgObject obj,SgPort * port,SgWriteContext * ctx)177 static void buf_port_print(SgObject obj, SgPort *port, SgWriteContext *ctx)
178 {
179 SgBufferedPort *p = SG_BUFFERED_PORT(obj);
180 SG_PORT_LOCK_WRITE(port);
181 Sg_PutuzUnsafe(port, UC("#<buffered-port"));
182
183 Sg_Printf(port, UC(" %A"), p->src);
184
185 switch (SG_PORT(p)->closed) {
186 case SG_PORT_CLOSED:
187 Sg_PutuzUnsafe(port, UC(" closed"));
188 break;
189 case SG_PORT_PSEUDO:
190 Sg_PutuzUnsafe(port, UC(" pseudo-closed"));
191 break;
192 default: break;
193 }
194 Sg_PutcUnsafe(port, '>');
195 SG_PORT_UNLOCK_WRITE(port);
196 }
197 SG_DEFINE_BUILTIN_CLASS(Sg_BufferedPortClass,
198 buf_port_print, NULL, NULL, NULL,
199 port_cpl);
200
201 /* custom can be extended */
202 static SgObject custom_port_allocate(SgClass *klass, SgObject initargs);
203 /* we don't need them in C level, so just declare here */
204 SG_CLASS_DECL(Sg_CustomBinaryPortClass);
205 SG_CLASS_DECL(Sg_CustomTextualPortClass);
206 #define SG_CLASS_CUSTOM_BINARY_PORT (&Sg_CustomBinaryPortClass)
207 #define SG_CLASS_CUSTOM_TEXTUAL_PORT (&Sg_CustomTextualPortClass)
208
209 static SgClass *custom_port_cpl[] = {
210 SG_CLASS_CUSTOM_PORT,
211 SG_CLASS_PORT,
212 SG_CLASS_TOP,
213 NULL
214 };
215
216 SG_DEFINE_BASE_CLASS(Sg_CustomPortClass, SgCustomPort,
217 port_print, NULL, NULL, custom_port_allocate,
218 port_cpl);
219 SG_DEFINE_BASE_CLASS(Sg_CustomBinaryPortClass, SgCustomPort,
220 port_print, NULL, NULL, custom_port_allocate,
221 custom_port_cpl);
222 SG_DEFINE_BASE_CLASS(Sg_CustomTextualPortClass, SgCustomPort,
223 port_print, NULL, NULL, custom_port_allocate,
224 custom_port_cpl);
225
226 /* abstract interfaces */
227 SG_CLASS_DECL(Sg_InputPortClass);
228 SG_CLASS_DECL(Sg_OutputPortClass);
229 SG_CLASS_DECL(Sg_BidirectionalPortClass);
230 #define SG_CLASS_INPUT_PORT (&Sg_InputPortClass)
231 #define SG_CLASS_OUTPUT_PORT (&Sg_OutputPortClass)
232 #define SG_CLASS_BIDIRECTIONAL_PORT (&Sg_BidirectionalPortClass)
233
234 SG_DEFINE_ABSTRACT_CLASS(Sg_InputPortClass, port_cpl);
235 SG_DEFINE_ABSTRACT_CLASS(Sg_OutputPortClass, port_cpl);
236 SG_DEFINE_ABSTRACT_CLASS(Sg_BidirectionalPortClass, port_cpl);
237
238 #define PORT_DEFAULT_BUF_SIZE SG_PORT_DEFAULT_BUFFER_SIZE
239
240 /* inteface... sigh */
241 SG_DEFINE_ABSTRACT_CLASS(Sg_ReadOncePortClass, port_cpl);
242
port_cleanup(SgPort * port)243 static void port_cleanup(SgPort *port)
244 {
245 if (port->closed == SG_PORT_CLOSED) return;
246
247 if (SG_PORT_VTABLE(port)->flush) {
248 SG_PORT_VTABLE(port)->flush(port);
249 }
250 /* must always be there */
251 SG_PORT_VTABLE(port)->close(port);
252
253 port->closed = SG_PORT_CLOSED;
254 /* in case */
255 SG_CLEAN_PORT_LOCK(port);
256 }
257
258
port_finalize(SgObject obj,void * data)259 static void port_finalize(SgObject obj, void *data)
260 {
261 port_cleanup(SG_PORT(obj));
262 Sg_UnregisterFinalizer(SG_OBJ(obj));
263 }
264
Sg_AddPortCleanup(SgPort * port)265 int Sg_AddPortCleanup(SgPort *port)
266 {
267 Sg_RegisterFinalizer(SG_OBJ(port), port_finalize, NULL);
268 return TRUE;
269 }
270
make_port_raw(size_t size,SgPortDirection d,SgClass * clazz,SgPortTable * vtbl,SgObject transcoder)271 static SgPort* make_port_raw(size_t size,
272 SgPortDirection d,
273 SgClass *clazz,
274 SgPortTable *vtbl,
275 SgObject transcoder)
276 {
277 SgPort *z = SG_NEW2(SgPort *, size);
278 SG_INIT_PORT(z, clazz, d, vtbl, transcoder);
279 return z;
280 }
281
282 #define make_port(type, d, c, v, t) \
283 make_port_raw(sizeof(type), d, c, v, t)
284
285 /* from Gauche */
286 /* Tracking buffered ports */
287 #define PORT_VECTOR_SIZE 256
288 static struct {
289 int dummy;
290 SgWeakVector *ports;
291 SgInternalMutex lock;
292 } active_buffered_ports = { 1, NULL };
293
294 #define PORT_HASH(port) \
295 (((((uintptr_t)(port)>>3) * 2654435761UL)>>16) % PORT_VECTOR_SIZE)
296
297
register_buffered_port(SgBufferedPort * port)298 static void register_buffered_port(SgBufferedPort *port)
299 {
300 int i, h, c;
301 int tried_gc = FALSE;
302 int need_gc = FALSE;
303
304 retry:
305 h = i = (int)PORT_HASH(port);
306 c = 0;
307 /* make sure h and i are not negative values. */
308 if (h < 0) {
309 h = i = -h;
310 }
311 Sg_LockMutex(&active_buffered_ports.lock);
312 while (!SG_FALSEP(Sg_WeakVectorRef(active_buffered_ports.ports,
313 i, SG_FALSE))) {
314 i -= ++c;
315 while (i < 0) i += PORT_VECTOR_SIZE;
316 if (i == h) {
317 /* Vector entry is full. We run global GC to try to collect
318 unused entry. */
319 need_gc = TRUE;
320 break;
321 }
322 }
323 if (!need_gc) {
324 Sg_WeakVectorSet(active_buffered_ports.ports, i, SG_OBJ(port));
325 }
326 Sg_UnlockMutex(&active_buffered_ports.lock);
327 if (need_gc) {
328 if (tried_gc) {
329 Sg_Panic("active buffered port table overflow.");
330 } else {
331 Sg_GC();
332 tried_gc = TRUE;
333 need_gc = FALSE;
334 goto retry;
335 }
336 }
337 }
338
unregister_buffered_port(SgBufferedPort * port)339 static void unregister_buffered_port(SgBufferedPort *port)
340 {
341 int i, h, c;
342 SgObject p;
343
344 h = i = (int)PORT_HASH(port);
345 c = 0;
346 Sg_LockMutex(&active_buffered_ports.lock);
347 do {
348 p = Sg_WeakVectorRef(active_buffered_ports.ports, i, SG_FALSE);
349 if (!SG_FALSEP(p) && SG_EQ(SG_OBJ(port), p)) {
350 Sg_WeakVectorSet(active_buffered_ports.ports, i, SG_FALSE);
351 break;
352 }
353 i -= ++c; while (i < 0) i += PORT_VECTOR_SIZE;
354 } while (i != h);
355 Sg_UnlockMutex(&active_buffered_ports.lock);
356 }
357
358 /* at some point, we changed port implementation strategy and removed
359 peek function pointer from the structure. now we need to calculate
360 port position with peek buffer, otherwise port-position! returns
361 incorrect position after peek.
362
363 NB: SG_PORT_U8_AHEAD and SG_PORT_CHAR_AHEAD points the same member
364 (at least for now).
365 */
366 #define CONSIDER_PEEK(pos, port) \
367 (int64_t)((pos)-((SG_PORT_U8_AHEAD(port)!=EOF)?1:0))
368
369
370 /* buffered port */
memcpy64(void * s1,const void * s2,uint64_t n)371 static void* memcpy64(void *s1, const void *s2, uint64_t n)
372 {
373 register char *ss1 = s1;
374 register const char *ss2 = s2;
375 if (n != 0) {
376 register const char *t = ss2 + n;
377 do {
378 *ss1++ = *ss2++;
379 } while (ss2 != t);
380 }
381 return s1;
382 }
383
buffered_flush(SgObject self)384 static void buffered_flush(SgObject self)
385 {
386 /* there is no need to flush on input port */
387 if (SG_OUTPUT_PORTP(self)) {
388 SgBufferedPort *bp = SG_BUFFERED_PORT(self);
389 uint8_t *buf = bp->buffer;
390 /* restore position, if possible */
391 if (Sg_HasSetPortPosition(bp->src)) {
392 SG_PORT_VTABLE(bp->src)->setPortPosition(bp->src,
393 bp->src->position, SG_BEGIN);
394 }
395 /* Only if there's something to flush */
396 while (bp->index > 0 && bp->index != bp->bufferSize) {
397 int64_t w = SG_PORT_VTABLE(bp->src)->writeb(bp->src, buf, bp->index);
398 buf += w;
399 bp->index -= w;
400 }
401 bp->index = 0;
402 bp->bufferSize = 0;
403 bp->dirty = FALSE;
404 if (SG_PORT_VTABLE(bp->src)->flush) {
405 SG_PORT_VTABLE(bp->src)->flush(bp->src);
406 }
407 }
408 }
409
buffered_fill_buffer(SgObject self)410 static void buffered_fill_buffer(SgObject self)
411 {
412 int64_t read_size = 0;
413 SgBufferedPort *bp = SG_BUFFERED_PORT(self);
414 SgPort *src = bp->src;
415 const size_t buffer_size = bp->size;
416
417 if (bp->dirty && SG_IN_OUT_PORTP(self)) {
418 buffered_flush(self);
419 }
420 do {
421 int64_t result = SG_PORT_VTABLE(src)->readb(src,
422 bp->buffer + read_size,
423 buffer_size - read_size);
424 if (result < 0) {
425 Sg_IOError(-1, SG_INTERN("fill buffer"),
426 SG_MAKE_STRING("underlying reader returned invalid value"),
427 SG_FALSE, self);
428 }
429 if (result == 0) {
430 break; /* EOF */
431 } else {
432 read_size += result;
433 }
434 /* for socket port we need to allow less reading */
435 } while (FALSE) /* (read_size < (int64_t)buffer_size) */;
436 /* ASSERT(read_size <= PORT_DEFAULT_BUF_SIZE); */
437 bp->bufferSize = read_size;
438 bp->index = 0;
439 }
440
buffered_readb(SgObject self,uint8_t * dest,int64_t req_size)441 static int64_t buffered_readb(SgObject self, uint8_t *dest, int64_t req_size)
442 {
443 int64_t opos = 0LL;
444 int64_t read_size = 0;
445 int need_unwind = FALSE;
446 SgBufferedPort *bp = SG_BUFFERED_PORT(self);
447 SgPort *src = SG_BUFFERED_PORT(self)->src;
448
449 while (read_size < req_size) {
450 int64_t buf_diff = bp->bufferSize - bp->index;
451 int64_t size_diff = req_size - read_size;
452
453 if (buf_diff >= size_diff) {
454 memcpy64(dest + read_size, bp->buffer + bp->index, size_diff);
455 bp->index += size_diff;
456 read_size += size_diff;
457 break;
458 } else {
459 /* position is saved so get it */
460 if (opos == 0LL) opos = src->position;
461 memcpy64(dest + read_size, bp->buffer + bp->index, buf_diff);
462 read_size += buf_diff;
463 buffered_fill_buffer(self);
464 need_unwind = TRUE;
465 if (bp->bufferSize == 0) {
466 /* EOF */
467 break;
468 }
469 }
470 }
471 /* if it's input/output port, then we need to put
472 the position to current position, if possible.
473 so filling buffer won't affect writing position.
474 */
475 if (need_unwind && SG_IN_OUT_PORTP(self)) {
476 if (Sg_HasSetPortPosition(src)) {
477 /* should accept please! */
478 SG_PORT_VTABLE(src)->setPortPosition(src, opos, SG_BEGIN);
479 }
480 /* we don't raise an error in case of socket input/output port */
481 }
482 SG_PORT(self)->position += read_size;
483 return read_size;
484 }
485
buffered_readb_all(SgObject self,uint8_t ** buf)486 static int64_t buffered_readb_all(SgObject self, uint8_t **buf)
487 {
488 SgBufferedPort *bp = SG_BUFFERED_PORT(self);
489 if (bp->index != bp->bufferSize) {
490 SgPort *buffer;
491 SgBytePort byp;
492 uint8_t *tmp;
493 int64_t size = bp->bufferSize - bp->index, tsize;
494
495 buffer = SG_PORT(Sg_InitByteArrayOutputPort(&byp, 1024));
496 Sg_WritebUnsafe(SG_PORT(buffer), bp->buffer, bp->index, size);
497
498 tsize = SG_PORT_VTABLE(bp->src)->readbAll(bp->src, &tmp);
499 Sg_WritebUnsafe(SG_PORT(buffer), tmp, 0, tsize);
500
501 *buf = Sg_GetByteArrayFromBinaryPort(&byp);
502 bp->index = 0;
503 bp->bufferSize = 0;
504 return size+tsize;
505 }
506 bp->index = 0;
507 bp->bufferSize = 0;
508 return SG_PORT_VTABLE(bp->src)->readbAll(bp->src, buf);
509 }
510
buffered_write_to_block_buffer(SgObject self,uint8_t * v,int64_t req_size)511 static int64_t buffered_write_to_block_buffer(SgObject self, uint8_t *v,
512 int64_t req_size)
513 {
514 int64_t write_size = 0;
515 SgBufferedPort *bp = SG_BUFFERED_PORT(self);
516 const size_t buffer_size = bp->size;
517
518 if (!bp->dirty) {
519 bp->dirty = req_size > 0;
520 }
521
522 while (write_size < req_size) {
523 int64_t buf_diff = buffer_size - bp->index;
524 int64_t size_diff = req_size - write_size;
525 if (buf_diff >= size_diff) {
526 memcpy64(bp->buffer + bp->index, v + write_size, size_diff);
527 bp->index += size_diff;
528 write_size += size_diff;
529 } else {
530 memcpy64(bp->buffer + bp->index,v + write_size, buf_diff);
531 bp->index += buf_diff;
532 write_size += buf_diff;
533 buffered_flush(self);
534 }
535 }
536 SG_PORT(self)->position += write_size;
537 return write_size;
538 }
539
buffered_write_to_line_buffer(SgObject self,uint8_t * v,int64_t req_size)540 static int64_t buffered_write_to_line_buffer(SgObject self, uint8_t *v,
541 int64_t req_size)
542 {
543 int64_t write_size = 0;
544 /* int need_unwind = FALSE; */
545 SgBufferedPort *bp = SG_BUFFERED_PORT(self);
546 const size_t buffer_size = bp->size;
547
548 if (!bp->dirty) {
549 bp->dirty = req_size > 0;
550 }
551
552 while (write_size < req_size) {
553 int64_t buf_diff = buffer_size - bp->index;
554 if (buf_diff == 0) {
555 buffered_flush(self);
556 }
557 *(bp->buffer + bp->index) = *(v + write_size);
558 bp->index++;
559 write_size++;
560 if (bp->buffer[bp->index - 1] == '\n') {
561 /* for win utf16, 0x0a will be 0x0a00, so we need to put the next byte.
562 FIXME: this might be too naive.
563 */
564 if (Sg_UTF16ConsolePortP(self)) {
565 *(bp->buffer + bp->index) = *(v + write_size);
566 bp->index++;
567 write_size++;
568 }
569 buffered_flush(self);
570 }
571 }
572 SG_PORT(self)->position += write_size;
573 return write_size;
574 }
575
buffered_close(SgObject self)576 static int buffered_close(SgObject self)
577 {
578 if (SG_PORT(self)->closed != SG_PORT_CLOSED) {
579 SgBufferedPort *bp = SG_BUFFERED_PORT(self);
580 SgPort *src = bp->src;
581 buffered_flush(self);
582 SG_PORT_VTABLE(src)->close(src);
583 SG_PORT(self)->closed = SG_PORT_CLOSED;
584 /* I believe calling GC_REGISTER_FINALIZER_NO_ORDER with
585 non GC pointer is safe. But just in case. */
586 if (Sg_GCBase(self)) {
587 unregister_buffered_port(bp);
588 if (Sg_FinalizerRegisteredP(self)) {
589 Sg_UnregisterFinalizer(self);
590 }
591 }
592 }
593 return TRUE;
594 }
595
buffered_ready(SgObject self)596 static int buffered_ready(SgObject self)
597 {
598 SgBufferedPort *bp = SG_BUFFERED_PORT(self);
599 SgPort *src = bp->src;
600 if (SG_PORT_VTABLE(src)->ready) {
601 return SG_PORT_VTABLE(src)->ready(src);
602 }
603 return TRUE;
604 }
605
buffered_lock(SgObject self,SgPortLockType type)606 static int buffered_lock(SgObject self, SgPortLockType type)
607 {
608 SgBufferedPort *bp = SG_BUFFERED_PORT(self);
609 SgPort *src = bp->src;
610 if (SG_PORT_VTABLE(src)->lockPort) {
611 return SG_PORT_VTABLE(src)->lockPort(src, type);
612 }
613 return TRUE;
614 }
615
buffered_unlock(SgObject self)616 static int buffered_unlock(SgObject self)
617 {
618 SgBufferedPort *bp = SG_BUFFERED_PORT(self);
619 SgPort *src = bp->src;
620 if (SG_PORT_VTABLE(src)->unlockPort) {
621 return SG_PORT_VTABLE(src)->unlockPort(src);
622 }
623 return TRUE;
624 }
625
buffered_position(SgObject self)626 static int64_t buffered_position(SgObject self)
627 {
628 /* underlying port position is changed by filling buffer.
629 so just return this port's position. */
630 return CONSIDER_PEEK(SG_PORT(self)->position, self);
631 }
632
buffered_set_position(SgObject self,int64_t off,SgWhence where)633 static void buffered_set_position(SgObject self, int64_t off, SgWhence where)
634 {
635 SgBufferedPort *bp = SG_BUFFERED_PORT(self);
636 SgPort *src = bp->src;
637 if (Sg_HasSetPortPosition(src)) {
638 /* flush current buffer */
639 buffered_flush(self);
640 bp->index = 0;
641 bp->bufferSize = 0;
642 /* sync source position to buffered position */
643 src->position = SG_PORT(self)->position;
644 SG_PORT_VTABLE(src)->setPortPosition(src, off, where);
645 SG_PORT(self)->position = src->position;
646 return;
647 }
648 Sg_Error(UC("Given port does not support set-port-position!: %S"), self);
649 }
650
651 static SgPortTable line_buffer_table = {
652 buffered_flush,
653 buffered_close,
654 buffered_ready,
655 buffered_lock,
656 buffered_unlock,
657 buffered_position,
658 buffered_set_position,
659 NULL,
660 buffered_readb,
661 buffered_readb_all,
662 buffered_write_to_line_buffer,
663 NULL,
664 NULL
665 };
666
667 static SgPortTable block_buffer_table = {
668 buffered_flush,
669 buffered_close,
670 buffered_ready,
671 buffered_lock,
672 buffered_unlock,
673 buffered_position,
674 buffered_set_position,
675 NULL,
676 buffered_readb,
677 buffered_readb_all,
678 buffered_write_to_block_buffer,
679 NULL,
680 NULL
681 };
682
683
684 /* internal */
685 typedef struct SgBiDirectionalBufferedPortRec
686 {
687 SgBufferedPort in; /* for my convenience */
688 SgBufferedPort out;
689 } SgBiDirectionalBufferedPort;
690 #define BI_PORT(obj) ((SgBiDirectionalBufferedPort*)obj)
691
bi_buffered_close(SgObject self)692 static int bi_buffered_close(SgObject self)
693 {
694 if (SG_PORT(self)->closed != SG_PORT_CLOSED) {
695 /* close out side, this closes source port as well
696 NB: no need to do input side. we don't want to flush it
697 */
698 buffered_close(SG_OBJ(&BI_PORT(self)->out));
699 SG_PORT(self)->closed = SG_PORT_CLOSED;
700 }
701 return TRUE;
702 }
bi_buffered_flush(SgObject self)703 static void bi_buffered_flush(SgObject self)
704 {
705 buffered_flush(SG_OBJ(&BI_PORT(self)->out));
706 }
bi_buffered_write_to_line_buffer(SgObject self,uint8_t * v,int64_t req_size)707 static int64_t bi_buffered_write_to_line_buffer(SgObject self, uint8_t *v,
708 int64_t req_size)
709 {
710 return buffered_write_to_line_buffer(&BI_PORT(self)->out, v, req_size);
711 }
bi_buffered_write_to_block_buffer(SgObject self,uint8_t * v,int64_t req_size)712 static int64_t bi_buffered_write_to_block_buffer(SgObject self, uint8_t *v,
713 int64_t req_size)
714 {
715 return buffered_write_to_block_buffer(&BI_PORT(self)->out, v, req_size);
716 }
717 static SgPortTable bi_line_buffer_table = {
718 bi_buffered_flush,
719 bi_buffered_close,
720 buffered_ready,
721 buffered_lock,
722 buffered_unlock,
723 NULL, /* impossible... */
724 NULL, /* ditto */
725 NULL,
726 buffered_readb,
727 buffered_readb_all,
728 bi_buffered_write_to_line_buffer,
729 NULL,
730 NULL
731 };
732
733 static SgPortTable bi_block_buffer_table = {
734 bi_buffered_flush,
735 bi_buffered_close,
736 buffered_ready,
737 buffered_lock,
738 buffered_unlock,
739 NULL, /* impossible */
740 NULL, /* ditto */
741 NULL,
742 buffered_readb,
743 buffered_readb_all,
744 bi_buffered_write_to_block_buffer,
745 NULL,
746 NULL
747 };
748
init_buffered_port(SgBufferedPort * bp,SgBufferMode mode,SgPort * src,uint8_t * buffer,size_t size,int registerP)749 static SgObject init_buffered_port(SgBufferedPort *bp,
750 SgBufferMode mode, SgPort *src,
751 uint8_t *buffer, size_t size,
752 int registerP)
753 {
754 /* TODO should we close here? */
755 /* Sg_PseudoClosePort(src); */
756 /* for now only binary port */
757 SgPortTable *tbl;
758 tbl = (mode == SG_BUFFER_MODE_LINE)? &line_buffer_table: &block_buffer_table;
759 SG_INIT_PORT(bp, SG_CLASS_BUFFERED_PORT, src->direction, tbl, SG_FALSE);
760 if (buffer != NULL) {
761 bp->buffer = buffer;
762 bp->size = size;
763 } else {
764 bp->buffer = SG_NEW_ATOMIC2(uint8_t *, SG_PORT_DEFAULT_BUFFER_SIZE);
765 bp->size = SG_PORT_DEFAULT_BUFFER_SIZE;
766 }
767 bp->index = 0;
768 bp->bufferSize = 0;
769 bp->dirty = FALSE;
770 bp->src = src;
771 bp->mode = mode;
772 /* If the source port is converted to buffered port after some
773 data is read, we still need to track the position. to do it
774 we need to get the position from source port.
775 */
776 SG_PORT(bp)->position = src->position;
777 /* we don't want to add stack allocated ones */
778 if (registerP && Sg_GCBase(bp)) {
779 register_buffered_port(bp);
780 /*
781 If the source port has finalizer registered, then we
782 first remove it then register this buffered port.
783 This makes GCed buffered port force to flush its
784 content.
785 NB: removing registered finalizer wouldn't be a problem
786 unless C extended port do something other than closing
787 the port. if we ever document how to write C extension
788 then we must specify this fact, such as finalizing
789 port must not do anything other than 'flush' or 'close'.
790 NB2: removing source port finalizer (I believe) saves some
791 memory space on GC. Not totally sure how it works
792 exactly, but it's better to do this for my sanity.
793 */
794 if (Sg_GCBase(src) && Sg_FinalizerRegisteredP(src)) {
795 Sg_UnregisterFinalizer(src);
796 Sg_RegisterFinalizer(bp, port_finalize, NULL);
797 }
798 }
799 return SG_OBJ(bp);
800 }
801
Sg_MakeBufferedPort(SgPort * src,SgBufferMode mode,uint8_t * buffer,size_t size)802 SgObject Sg_MakeBufferedPort(SgPort *src, SgBufferMode mode,
803 uint8_t *buffer, size_t size)
804 {
805 if (SG_BIDIRECTIONAL_PORTP(src)) {
806 SgObject r;
807 SgBiDirectionalBufferedPort *p =SG_NEW(SgBiDirectionalBufferedPort);
808 r = init_buffered_port(&BI_PORT(p)->in, mode, src, buffer, size, FALSE);
809 SG_PORT_VTABLE(r) = (mode == SG_BUFFER_MODE_LINE)
810 ? &bi_line_buffer_table: &bi_block_buffer_table;
811 /* out side is excess so won't be used */
812 init_buffered_port(&BI_PORT(p)->out, mode, src, NULL, 0, TRUE);
813 return r;
814 } else {
815 SgBufferedPort *p = SG_NEW(SgBufferedPort);
816 return Sg_InitBufferedPort(p, mode, src, buffer, size);
817 }
818 }
819
Sg_InitBufferedPort(SgBufferedPort * bp,SgBufferMode mode,SgPort * src,uint8_t * buffer,size_t size)820 SgObject Sg_InitBufferedPort(SgBufferedPort *bp,
821 SgBufferMode mode, SgPort *src,
822 uint8_t *buffer, size_t size)
823 {
824 if (SG_BIDIRECTIONAL_PORTP(src)) {
825 Sg_Error(UC("[Internal] Bidirectional port can't be used"));
826 }
827 return init_buffered_port(bp, mode, src, buffer, size,
828 SG_OUTPUT_PORTP(SG_PORT(src)));
829 }
830
831
832 #define SG_PORT_FILE(p) SG_FILE_PORT(p)->file
833 #define SG_PORT_FILE_VTABLE(p) SG_FILE_VTABLE(SG_PORT_FILE(p))
834
file_open(SgObject self)835 static int file_open(SgObject self)
836 {
837 return SG_PORT_FILE_VTABLE(self)->isOpen(SG_PORT_FILE(self));
838 }
839
file_close(SgObject self)840 static int file_close(SgObject self)
841 {
842 if (SG_PORT(self)->closed != SG_PORT_CLOSED) {
843 if (
844 #ifdef _MSC_VER
845 /* again I have no idea, but this happens... */
846 SG_PORT_FILE(self) &&
847 #endif
848 SG_PORT_FILE_VTABLE(self)->canClose(SG_PORT_FILE(self))) {
849 SG_PORT_FILE_VTABLE(self)->close(SG_PORT_FILE(self));
850 SG_PORT(self)->closed = SG_PORT_CLOSED;
851 Sg_UnregisterFinalizer(self);
852 }
853 }
854 return TRUE;
855 }
856
file_ready(SgObject self)857 static int file_ready(SgObject self)
858 {
859 if (!SG_PORT_FILE(self)) return FALSE;
860 if (SG_PORT_FILE_VTABLE(self)->ready) {
861 return SG_PORT_FILE_VTABLE(self)->ready(SG_PORT_FILE(self));
862 } else {
863 return TRUE;
864 }
865 }
866
file_forward_position(SgObject self,int64_t offset)867 static inline void file_forward_position(SgObject self, int64_t offset)
868 {
869 SG_PORT(self)->position += offset;
870 }
871
872
file_read_u8(SgObject self,uint8_t * buf,int64_t size)873 static int64_t file_read_u8(SgObject self, uint8_t *buf, int64_t size)
874 {
875 int64_t result;
876 int offset = 0;
877 if (size == 0) return 0;
878
879 if (SG_PORT_HAS_U8_AHEAD(self)) {
880 buf[0] = SG_PORT_U8_AHEAD(self);
881 SG_PORT_U8_AHEAD(self) = EOF;
882 offset++;
883 }
884 result = SG_PORT_FILE_VTABLE(self)->read(SG_PORT_FILE(self),
885 buf + offset, size - offset);
886 /* we also need to add offset to forward position. */
887 result += offset;
888 file_forward_position(self, result);
889 return result;
890 }
891
file_look_ahead_u8(SgObject self)892 static int file_look_ahead_u8(SgObject self)
893 {
894 uint8_t buf;
895 int64_t result;
896 if (SG_PORT_HAS_U8_AHEAD(self)) {
897 return SG_PORT_U8_AHEAD(self);
898 } else {
899 result = SG_PORT_FILE_VTABLE(self)->read(SG_PORT_FILE(self), &buf, 1);
900 SG_PORT_U8_AHEAD(self) = (result == 0) ? EOF : buf;
901 }
902 if (result == 0) {
903 return EOF;
904 }
905 return buf;
906 }
file_try_read_all(SgObject self,uint8_t ** buf)907 static int64_t file_try_read_all(SgObject self, uint8_t **buf)
908 {
909 int64_t result = file_look_ahead_u8(self);
910 if (result != EOF) {
911 int count = 0;
912 SgBytePort bp;
913 uint8_t b;
914 Sg_InitByteArrayOutputPort(&bp, 256);
915 while ((result = file_read_u8(self, &b, 1)) != 0) {
916 Sg_PutbUnsafe(SG_PORT(&bp), (uint8_t)b);
917 count++;
918 }
919 *buf = Sg_GetByteArrayFromBinaryPort(&bp);
920 file_forward_position(self, count);
921 return count;
922 } else {
923 return 0;
924 }
925 }
926
file_read_u8_all(SgObject self,uint8_t ** buf)927 static int64_t file_read_u8_all(SgObject self, uint8_t **buf)
928 {
929 int64_t rest_size = 0, result = 0;
930 uint8_t *dest;
931 SgFile *file = SG_PORT_FILE(self);
932 int offset = 0;
933
934 rest_size = SG_FILE_VTABLE(file)->size(file) - SG_PORT(self)->position;
935 if (rest_size < 0) return 0;
936
937 /* if file is pipe or fd, file->size method returns 0, however we know,
938 it can have something, so try to read as bytevector. */
939 if (rest_size == 0) return file_try_read_all(self, buf);
940
941 dest = SG_NEW_ATOMIC2(uint8_t *, (size_t)rest_size);
942 *buf = dest;
943
944 if (SG_PORT_HAS_U8_AHEAD(self)) {
945 dest[offset++] = SG_PORT_U8_AHEAD(self);
946 SG_PORT_U8_AHEAD(self) = EOF;
947 }
948 result = SG_FILE_VTABLE(file)->read(file, dest+offset, rest_size-offset);
949 result += offset;
950
951 file_forward_position(self, result);
952 return result;
953 }
954
file_lock(SgObject self,SgPortLockType type)955 static int file_lock(SgObject self, SgPortLockType type)
956 {
957 SgFile *file = SG_PORT_FILE(self);
958 return Sg_LockFile(file, (enum SgFileLockType)type);
959 }
960
file_unlock(SgObject self)961 static int file_unlock(SgObject self)
962 {
963 SgFile *file = SG_PORT_FILE(self);
964 return Sg_UnlockFile(file);
965 }
966
file_port_position(SgObject self)967 static int64_t file_port_position(SgObject self)
968 {
969 SgFile *file = SG_PORT_FILE(self);
970 if (SG_FILE_VTABLE(file)->tell) {
971 return SG_FILE_VTABLE(file)->tell(file);
972 } else {
973 return CONSIDER_PEEK(SG_PORT(self)->position, self);
974 }
975 }
976
file_set_port_position(SgObject self,int64_t offset,SgWhence whence)977 static void file_set_port_position(SgObject self, int64_t offset,
978 SgWhence whence)
979 {
980 SgFile *file = SG_PORT_FILE(self);
981 /* if flush is there, then flush it */
982 if (SG_OUTPUT_PORTP(self) && SG_PORT_VTABLE(self)->flush) {
983 SG_PORT_VTABLE(self)->flush(self);
984 }
985 if (SG_FILE_VTABLE(file)->seek) {
986 int64_t realoff = offset;
987 /* if the port is buffered port then the actual position is
988 bp->position. now we need to do sort of the same trick as
989 bytevector ports do. */
990 switch (whence) {
991 case SG_CURRENT:
992 realoff += SG_PORT(self)->position;
993 whence = SG_BEGIN;
994 if (realoff < 0) {
995 Sg_Error(UC("offset out of range %d"), (int)realoff);
996 }
997 break;
998 default: break;
999 }
1000 /* how should we handle SG_END with underflow/overflow? */
1001 SG_PORT(self)->position = SG_FILE_VTABLE(file)->seek(file, realoff, whence);
1002 } else {
1003 Sg_Error(UC("given file binary port does not support"
1004 " set-port-position!"));
1005 }
1006 }
1007
file_put_u8_array(SgObject self,uint8_t * v,int64_t size)1008 static int64_t file_put_u8_array(SgObject self, uint8_t *v, int64_t size)
1009 {
1010 int64_t wsize = SG_PORT_FILE_VTABLE(self)->write(SG_PORT_FILE(self), v, size);
1011 file_forward_position(self, wsize);
1012 return wsize;
1013 }
1014
1015 static SgPortTable fb_table = {
1016 NULL, /* file is not buffered so no flush */
1017 file_close,
1018 file_ready,
1019 file_lock,
1020 file_unlock,
1021 file_port_position,
1022 file_set_port_position,
1023 file_open,
1024 file_read_u8,
1025 file_read_u8_all,
1026 file_put_u8_array,
1027 NULL,
1028 NULL
1029 };
1030
1031 /* rather silly... */
1032 static SgPortTable fb_table_no_get_pos = {
1033 NULL,
1034 file_close,
1035 file_ready,
1036 file_lock,
1037 file_unlock,
1038 NULL,
1039 file_set_port_position,
1040 file_open,
1041 file_read_u8,
1042 file_read_u8_all,
1043 file_put_u8_array,
1044 NULL,
1045 NULL
1046 };
1047
1048 static SgPortTable fb_table_no_set_pos = {
1049 NULL,
1050 file_close,
1051 file_ready,
1052 file_lock,
1053 file_unlock,
1054 file_port_position,
1055 NULL,
1056 file_open,
1057 file_read_u8,
1058 file_read_u8_all,
1059 file_put_u8_array,
1060 NULL,
1061 NULL
1062 };
1063
1064 static SgPortTable fb_table_no_pos = {
1065 NULL,
1066 file_close,
1067 file_ready,
1068 file_lock,
1069 file_unlock,
1070 NULL,
1071 NULL,
1072 file_open,
1073 file_read_u8,
1074 file_read_u8_all,
1075 file_put_u8_array,
1076 NULL,
1077 NULL
1078 };
1079
get_file_table(SgFile * file)1080 static SgPortTable* get_file_table(SgFile *file)
1081 {
1082 if (SG_FILE_VTABLE(file)->tell && SG_FILE_VTABLE(file) ->seek) {
1083 return &fb_table;
1084 } else if (SG_FILE_VTABLE(file)->tell) {
1085 return &fb_table_no_set_pos;
1086 } else if (SG_FILE_VTABLE(file)->seek) {
1087 return &fb_table_no_get_pos;
1088 } else {
1089 return &fb_table_no_pos;
1090 }
1091 }
make_file_port(SgFile * file,int bufferMode,SgPortDirection direction)1092 static SgObject make_file_port(SgFile *file, int bufferMode,
1093 SgPortDirection direction)
1094 {
1095 SgPortTable *tbl = get_file_table(file);
1096 SgFilePort *z = (SgFilePort *)make_port(SgFilePort,
1097 direction,
1098 SG_CLASS_FILE_PORT,
1099 tbl,
1100 SG_FALSE);
1101
1102 if (SG_FILE_VTABLE(file)->canClose(file)) {
1103 Sg_RegisterFinalizer(SG_OBJ(z), port_finalize, NULL);
1104 }
1105
1106 z->file = file;
1107 /* set file position */
1108 if (SG_FILE_VTABLE(file)->tell)
1109 SG_PORT(z)->position = SG_FILE_VTABLE(file)->tell(file);
1110
1111 switch (bufferMode) {
1112 case SG_BUFFER_MODE_LINE:
1113 case SG_BUFFER_MODE_BLOCK:
1114 return Sg_MakeBufferedPort(SG_PORT(z), bufferMode, NULL, 0);
1115 default: return SG_OBJ(z);
1116 }
1117 }
1118
Sg_MakeFileBinaryInputPort(SgFile * file,int bufferMode)1119 SgObject Sg_MakeFileBinaryInputPort(SgFile *file, int bufferMode)
1120 {
1121 return make_file_port(file, bufferMode, SG_INPUT_PORT);
1122 }
1123
Sg_MakeFileBinaryOutputPort(SgFile * file,int bufferMode)1124 SgObject Sg_MakeFileBinaryOutputPort(SgFile *file, int bufferMode)
1125 {
1126 return make_file_port(file, bufferMode, SG_OUTPUT_PORT);
1127 }
1128
Sg_MakeFileBinaryInputOutputPort(SgFile * file,int bufferMode)1129 SgObject Sg_MakeFileBinaryInputOutputPort(SgFile *file, int bufferMode)
1130 {
1131 return make_file_port(file, bufferMode, SG_IN_OUT_PORT);
1132 }
1133
1134 /* port must not be null */
Sg_InitFileBinaryPort(SgFilePort * port,SgFile * file,SgPortDirection d,SgBufferedPort * bufferedPort,SgBufferMode mode,uint8_t * buffer,size_t bufferSize)1135 SgObject Sg_InitFileBinaryPort(SgFilePort *port,
1136 SgFile *file,
1137 SgPortDirection d,
1138 SgBufferedPort *bufferedPort,
1139 SgBufferMode mode,
1140 uint8_t *buffer,
1141 size_t bufferSize)
1142 {
1143 SG_INIT_PORT(port, SG_CLASS_FILE_PORT, d, get_file_table(file), SG_FALSE);
1144 port->file = file;
1145 if (bufferedPort) {
1146 return Sg_InitBufferedPort(bufferedPort, mode, SG_PORT(port),
1147 buffer, bufferSize);
1148 } else if (mode != SG_BUFFER_MODE_NONE) {
1149 return Sg_MakeBufferedPort(SG_PORT(port), mode, buffer, bufferSize);
1150 }
1151 return port;
1152 }
1153
1154 /*****
1155 ByteArray port
1156 */
byte_array_close(SgObject self)1157 static int byte_array_close(SgObject self)
1158 {
1159 SG_PORT(self)->closed = SG_PORT_CLOSED;
1160 return TRUE;
1161 }
1162
byte_array_open(SgObject self)1163 static int byte_array_open(SgObject self)
1164 {
1165 return SG_PORT(self)->closed == SG_PORT_OPEN;
1166 }
1167
1168 #define SG_BINARY_PORT_BUFFER(p) (&(SG_BYTE_PORT(p)->buffer))
1169
byte_array_read_u8(SgObject self,uint8_t * buf,int64_t size)1170 static int64_t byte_array_read_u8(SgObject self, uint8_t *buf, int64_t size)
1171 {
1172 size_t index = SG_BINARY_PORT_BUFFER(self)->index;
1173 uint8_t *start = SG_BINARY_PORT_BUFFER(self)->buf;
1174 uint8_t *end = SG_BINARY_PORT_BUFFER(self)->end;
1175 size_t bsize = end - start;
1176 size_t rest = bsize - index;
1177 size_t read_size = (rest >= (size_t)size) ? (size_t)size : rest;
1178 int i;
1179 /* peeked byte must be hanled port APIs */
1180 for (i = 0; i < read_size; i++) {
1181 buf[i] = start[index + i];
1182 }
1183 SG_BINARY_PORT_BUFFER(self)->index += read_size;
1184 SG_PORT(self)->position += read_size;
1185 return read_size;
1186 }
1187
byte_array_read_u8_all(SgObject self,uint8_t ** buf)1188 static int64_t byte_array_read_u8_all(SgObject self, uint8_t **buf)
1189 {
1190 size_t index = SG_BINARY_PORT_BUFFER(self)->index;
1191 uint8_t *start = SG_BINARY_PORT_BUFFER(self)->buf;
1192 uint8_t *end = SG_BINARY_PORT_BUFFER(self)->end;
1193 size_t size = end - start;
1194 size_t rest_size = size - index;
1195
1196 *buf = SG_NEW_ATOMIC2(uint8_t *, rest_size);
1197
1198 return byte_array_read_u8(self, *buf, rest_size);
1199 }
1200
input_byte_array_port_position(SgObject self)1201 static int64_t input_byte_array_port_position(SgObject self)
1202 {
1203 /* todo check whence */
1204 return CONSIDER_PEEK(SG_BINARY_PORT_BUFFER(self)->index, self);
1205 }
1206
input_byte_array_set_port_position(SgObject self,int64_t offset,SgWhence whence)1207 static void input_byte_array_set_port_position(SgObject self, int64_t offset,
1208 SgWhence whence)
1209 {
1210 SgBytePort *bp = SG_BYTE_PORT(self);
1211 int64_t realoff = 0LL;
1212 int64_t size = (int64_t)(bp->buffer.end - bp->buffer.buf);
1213 switch (whence) {
1214 case SG_BEGIN: realoff = offset; break;
1215 case SG_CURRENT: realoff = SG_PORT(self)->position + offset; break;
1216 case SG_END: realoff = size + offset; break;
1217 }
1218 /* don't overflow! */
1219 if (realoff > size) realoff = size;
1220 /* underflow is an error! */
1221 if (realoff < 0) {
1222 Sg_Error(UC("given offset is out of range %d"), (int)offset);
1223 }
1224
1225 bp->buffer.index = (size_t)realoff;
1226 SG_PORT(self)->position = realoff;
1227 }
1228
1229 #define DEFAULT_BUFFER_SIZE 256
1230 #define INCREASE_BUFFER_SIZE 32
1231
obyte_array_close(SgObject self)1232 static int obyte_array_close(SgObject self)
1233 {
1234 SG_PORT(self)->closed = SG_PORT_CLOSED;
1235 /* gc friendliness */
1236 SG_BYTE_PORT(self)->buffer.start = NULL;
1237 SG_BYTE_PORT(self)->buffer.current = NULL;
1238 return TRUE;
1239 }
1240
put_byte_array_u8_array(SgObject self,uint8_t * ba,int64_t size)1241 static int64_t put_byte_array_u8_array(SgObject self, uint8_t *ba,
1242 int64_t size)
1243 {
1244 SgBytePort *bp = SG_BYTE_PORT(self);
1245 int64_t i;
1246 for (i = 0; i < size; i++) {
1247 SG_STREAM_BUFFER_PUTB(bp->buffer.current, bp->buffer.current, ba[i]);
1248 }
1249 SG_PORT(self)->position += size;
1250 return size;
1251 }
1252
output_byte_array_port_position(SgObject self)1253 static int64_t output_byte_array_port_position(SgObject self)
1254 {
1255 /* todo check whence */
1256 /* NB: it's only output, thus no peek operation */
1257 return SG_PORT(self)->position;
1258 }
1259
output_byte_array_set_port_position(SgObject self,int64_t offset,SgWhence whence)1260 static void output_byte_array_set_port_position(SgObject self, int64_t offset,
1261 SgWhence whence)
1262 {
1263 /* todo check whence */
1264 SgBytePort *bp = SG_BYTE_PORT(self);
1265 int64_t realoff = 0LL;
1266 switch (whence) {
1267 case SG_BEGIN: realoff = offset; break;
1268 case SG_CURRENT: realoff = SG_PORT(self)->position + offset; break;
1269 case SG_END:
1270 SG_STREAM_BUFFER_COUNTB(realoff, bp->buffer.start);
1271 realoff += offset;
1272 break;
1273 }
1274 /* underflow is an error! */
1275 if (realoff < 0) {
1276 Sg_Error(UC("given offset is out of range %d"), (int)offset);
1277 }
1278 SG_STREAM_BUFFER_SET_POSITIONB(bp->buffer.start, bp->buffer.current, realoff);
1279 SG_PORT(self)->position = realoff;
1280 }
1281
1282 static SgPortTable bt_inputs = {
1283 NULL, /* flush */
1284 byte_array_close,
1285 NULL, /* ready */
1286 NULL, /* lock */
1287 NULL, /* unlock */
1288 input_byte_array_port_position,
1289 input_byte_array_set_port_position,
1290 byte_array_open,
1291 byte_array_read_u8,
1292 byte_array_read_u8_all,
1293 NULL, /* writeb */
1294 NULL, /* reads */
1295 NULL /* writes */
1296 };
1297
1298 static SgPortTable bt_outputs = {
1299 NULL, /* flush */
1300 obyte_array_close,
1301 NULL, /* ready */
1302 NULL, /* lock */
1303 NULL, /* unlock */
1304 output_byte_array_port_position,
1305 output_byte_array_set_port_position,
1306 byte_array_open,
1307 NULL, /* readb */
1308 NULL, /* readbAll */
1309 put_byte_array_u8_array,
1310 NULL, /* reads */
1311 NULL /* writes */
1312 };
1313
Sg_InitByteArrayInputPort(SgBytePort * port,uint8_t * src,size_t offset,size_t end)1314 SgObject Sg_InitByteArrayInputPort(SgBytePort *port,
1315 uint8_t *src, size_t offset, size_t end)
1316 {
1317 SG_INIT_PORT(port, SG_CLASS_BYTE_PORT, SG_INPUT_PORT, &bt_inputs, SG_FALSE);
1318 /* initialize binary input port */
1319 SG_BINARY_PORT_BUFFER(port)->buf = src;
1320 SG_BINARY_PORT_BUFFER(port)->end = src + end;
1321 SG_BINARY_PORT_BUFFER(port)->index = offset;
1322 return SG_OBJ(port);
1323 }
1324
Sg_MakeByteVectorInputPort(SgByteVector * bv,int64_t start,int64_t end)1325 SgObject Sg_MakeByteVectorInputPort(SgByteVector *bv, int64_t start, int64_t end)
1326 {
1327 int64_t len = SG_BVECTOR_SIZE(bv);
1328 SG_CHECK_START_END(start, end, len);
1329 return Sg_MakeByteArrayInputPort(SG_BVECTOR_ELEMENTS(bv)+start, end-start);
1330 }
1331
Sg_MakeByteArrayInputPort(uint8_t * src,int64_t size)1332 SgObject Sg_MakeByteArrayInputPort(uint8_t *src, int64_t size)
1333 {
1334 SgBytePort *z = SG_NEW(SgBytePort);
1335 return Sg_InitByteArrayInputPort(z, src, 0, size);
1336 }
1337
Sg_MakeByteArrayOutputPort(size_t size)1338 SgObject Sg_MakeByteArrayOutputPort(size_t size)
1339 {
1340 SgBytePort *z = SG_NEW(SgBytePort);
1341 return Sg_InitByteArrayOutputPort(z, size);
1342 }
1343
Sg_InitByteArrayOutputPort(SgBytePort * bp,size_t bufferSize)1344 SgObject Sg_InitByteArrayOutputPort(SgBytePort *bp, size_t bufferSize)
1345 {
1346 SG_INIT_PORT(bp, SG_CLASS_BYTE_PORT, SG_OUTPUT_PORT, &bt_outputs, SG_FALSE);
1347 /* TODO precompute buffer according to the given size */
1348 bp->buffer.start = bp->buffer.current = SG_NEW(byte_buffer);
1349 bp->buffer.start->position = 0;
1350
1351 return SG_OBJ(bp);
1352 }
1353
1354 /*
1355 This function always return new allocated byte array.
1356 */
1357
Sg_GetByteArrayFromBinaryPort(SgBytePort * port)1358 uint8_t* Sg_GetByteArrayFromBinaryPort(SgBytePort *port)
1359 {
1360 uint8_t *r;
1361
1362 if (SG_INPUT_PORTP(port)) {
1363 r = SG_NEW_ATOMIC2(uint8_t*, sizeof(uint8_t) * port->buffer.index);
1364 memcpy(r, port->buffer.buf, port->buffer.index);
1365 return r;
1366 } else {
1367 size_t size;
1368 SG_STREAM_BUFFER_COUNTB(size, port->buffer.start);
1369 r = SG_NEW_ATOMIC2(uint8_t*, sizeof(uint8_t) * size);
1370 SG_STREAM_BUFFER_GET_BUFFERB(r, port->buffer.start);
1371 return r;
1372 }
1373 }
1374
1375
1376 /*****
1377 Transcoded port
1378 */
1379 #define SG_TPORT_PORT SG_TRANSCODED_PORT_PORT
1380 #define SG_TPORT_TRANSCODER(p) SG_PORT(p)->transcoder
1381
trans_get_string(SgObject self,SgChar * buf,int64_t size)1382 static int64_t trans_get_string(SgObject self, SgChar *buf, int64_t size)
1383 {
1384 int64_t readSize;
1385 if (size == 0) return 0; /* short cut */
1386 /* need special treatment when the size is 1 to handle EOL properly */
1387 if (size == 1) {
1388 SgChar c = Sg_TranscoderGetc(SG_TPORT_TRANSCODER(self), self);
1389 if (c == EOF) return 0;
1390 buf[0] = c;
1391 readSize = 1;
1392 } else {
1393 readSize = Sg_TranscoderRead(SG_TPORT_TRANSCODER(self),
1394 self, buf, size);
1395 }
1396 return readSize;
1397 }
1398
trans_close(SgObject self)1399 static int trans_close(SgObject self)
1400 {
1401 SG_PORT(self)->closed = SG_PORT_CLOSED;
1402 return SG_PORT_VTABLE(SG_TPORT_PORT(self))->close(SG_TPORT_PORT(self));
1403 }
1404
trans_ready(SgObject self)1405 static int trans_ready(SgObject self)
1406 {
1407 /* FIXME the implementation of char-ready is sort of broken.
1408 it's because we can't check if those bytes are really correct for
1409 unicode characters. */
1410 SgPort *bp = SG_TPORT_PORT(self);
1411 if (SG_PORT_VTABLE(bp)->ready) {
1412 return SG_PORT_VTABLE(bp)->ready(bp);
1413 } else {
1414 return TRUE;
1415 }
1416 }
1417
trans_lock(SgObject self,SgPortLockType type)1418 static int trans_lock(SgObject self, SgPortLockType type)
1419 {
1420 SgPort *src = SG_TPORT_PORT(self);
1421 return Sg_LockPortResource(src, type);
1422 }
1423
trans_unlock(SgObject self)1424 static int trans_unlock(SgObject self)
1425 {
1426 SgPort *src = SG_TPORT_PORT(self);
1427 return Sg_UnlockPortResouce(src);
1428 }
1429
trans_put_string(SgObject self,SgChar * str,int64_t count)1430 static int64_t trans_put_string(SgObject self, SgChar *str, int64_t count)
1431 {
1432 return Sg_TranscoderWrite(SG_TPORT_TRANSCODER(self), self, str, count);
1433 }
1434
trans_flush(SgObject self)1435 static void trans_flush(SgObject self)
1436 {
1437 if (SG_PORT_VTABLE(SG_TPORT_PORT(self))->flush) {
1438 SG_PORT_VTABLE(SG_TPORT_PORT(self))->flush(SG_TPORT_PORT(self));
1439 }
1440 }
1441
trans_port_position(SgObject self)1442 static int64_t trans_port_position(SgObject self)
1443 {
1444 SgPort *p = SG_TPORT_PORT(self);
1445 int64_t pos = Sg_PortPosition(p);
1446 /* count the peeked char bytes if exists */
1447 if (SG_TRANSCODED_PORT_UNGET(self) != EOF) {
1448 SgBytePort bp;
1449 SgTranscodedPort tp;
1450 SgObject buf;
1451 Sg_InitByteArrayOutputPort(&bp, 10);
1452 Sg_InitTranscodedPort(&tp, SG_PORT(&bp),
1453 SG_TRANSCODED_PORT_TRANSCODER(self),
1454 SG_OUTPUT_PORT);
1455 Sg_TranscoderPutc(SG_TRANSCODED_PORT_TRANSCODER(self), SG_PORT(&tp),
1456 SG_TRANSCODED_PORT_UNGET(self));
1457 buf = Sg_GetByteVectorFromBinaryPort(&bp);
1458 SG_CLEAN_TRANSCODED_PORT(&tp);
1459 SG_CLEAN_BYTE_PORT(&bp);
1460 pos -= SG_BVECTOR_SIZE(buf);
1461 }
1462 return pos;
1463 }
1464
trans_set_port_position(SgObject self,int64_t offset,SgWhence whence)1465 static void trans_set_port_position(SgObject self, int64_t offset,
1466 SgWhence whence)
1467 {
1468 SgPort *p = SG_TPORT_PORT(self);
1469 SG_TRANSCODED_PORT_UNGET(self) = EOF;
1470 /* FIXME */
1471 Sg_SetPortPosition(p, offset, whence);
1472 }
1473
1474
1475 static SgPortTable trans_table = {
1476 trans_flush,
1477 trans_close,
1478 trans_ready,
1479 trans_lock,
1480 trans_unlock,
1481 trans_port_position,
1482 trans_set_port_position,
1483 NULL, /* open */
1484 NULL, /* readb */
1485 NULL, /* readbAll */
1486 NULL, /* writeb */
1487 trans_get_string,
1488 trans_put_string
1489 };
1490
1491 /* silly */
1492 static SgPortTable trans_table_no_get_pos = {
1493 trans_flush,
1494 trans_close,
1495 trans_ready,
1496 trans_lock,
1497 trans_unlock,
1498 NULL,
1499 trans_set_port_position,
1500 NULL, /* open */
1501 NULL, /* readb */
1502 NULL, /* readbAll */
1503 NULL, /* writeb */
1504 trans_get_string,
1505 trans_put_string
1506 };
1507
1508 static SgPortTable trans_table_no_set_pos = {
1509 trans_flush,
1510 trans_close,
1511 trans_ready,
1512 trans_lock,
1513 trans_unlock,
1514 trans_port_position,
1515 NULL,
1516 NULL, /* open */
1517 NULL, /* readb */
1518 NULL, /* readbAll */
1519 NULL, /* writeb */
1520 trans_get_string,
1521 trans_put_string
1522 };
1523
1524 static SgPortTable trans_table_no_pos = {
1525 trans_flush,
1526 trans_close,
1527 trans_ready,
1528 trans_lock,
1529 trans_unlock,
1530 NULL,
1531 NULL,
1532 NULL, /* open */
1533 NULL, /* readb */
1534 NULL, /* readbAll */
1535 NULL, /* writeb */
1536 trans_get_string,
1537 trans_put_string
1538 };
1539
get_transe_table(SgPort * port)1540 static SgPortTable* get_transe_table(SgPort *port)
1541 {
1542 if (Sg_HasPortPosition(port) && Sg_HasSetPortPosition(port)) {
1543 return &trans_table;
1544 } else if (Sg_HasPortPosition(port)) {
1545 return &trans_table_no_set_pos;
1546 } else if (Sg_HasSetPortPosition(port)) {
1547 return &trans_table_no_get_pos;
1548 } else {
1549 return &trans_table_no_pos;
1550 }
1551 }
1552
make_trans_port(SgPort * port,SgTranscoder * transcoder,SgPortDirection d)1553 static SgObject make_trans_port(SgPort *port, SgTranscoder *transcoder,
1554 SgPortDirection d)
1555 {
1556 SgTranscodedPort *z = (SgTranscodedPort *)make_port(SgTranscodedPort,
1557 d,
1558 SG_CLASS_TRANSCODED_PORT,
1559 get_transe_table(port),
1560 transcoder);
1561 z->port = port;
1562 SG_PORT(z)->lineNo = 1;
1563 return SG_OBJ(z);
1564 }
1565
Sg_MakeTranscodedPort(SgPort * port,SgTranscoder * transcoder)1566 SgObject Sg_MakeTranscodedPort(SgPort *port, SgTranscoder *transcoder)
1567 {
1568 return make_trans_port(port, transcoder, port->direction);
1569 }
1570
Sg_InitTranscodedPort(SgTranscodedPort * port,SgPort * src,SgTranscoder * transcoder,SgPortDirection direction)1571 SgObject Sg_InitTranscodedPort(SgTranscodedPort *port,
1572 SgPort *src,
1573 SgTranscoder *transcoder,
1574 SgPortDirection direction)
1575 {
1576 SG_INIT_PORT(port, SG_CLASS_TRANSCODED_PORT, direction,
1577 get_transe_table(src), transcoder);
1578 port->port = src;
1579 SG_PORT(port)->lineNo = 1;
1580 return SG_OBJ(port);
1581 }
1582
1583 /*****
1584 String port
1585 */
string_iport_close(SgObject self)1586 static int string_iport_close(SgObject self)
1587 {
1588 SG_PORT(self)->closed = SG_PORT_CLOSED;
1589 return TRUE;
1590 }
1591
string_oport_close(SgObject self)1592 static int string_oport_close(SgObject self)
1593 {
1594 SG_PORT(self)->closed = SG_PORT_CLOSED;
1595 SG_STRING_PORT(self)->buffer.start = NULL;
1596 SG_STRING_PORT(self)->buffer.current = NULL;
1597 return TRUE;
1598 }
1599
1600
string_oport_put_string(SgObject self,SgChar * str,int64_t count)1601 static int64_t string_oport_put_string(SgObject self, SgChar *str,
1602 int64_t count)
1603 {
1604 SgStringPort *tp = SG_STRING_PORT(self);
1605 int64_t i;
1606 /* TODO: we might want to improve this */
1607 for (i = 0; i < count; i++) {
1608 SG_STREAM_BUFFER_PUTC(tp->buffer.current, tp->buffer.current, str[i]);
1609 }
1610 SG_PORT(self)->position += count;
1611 return i;
1612 }
1613
string_iport_get_string(SgObject self,SgChar * buf,int64_t size)1614 static int64_t string_iport_get_string(SgObject self, SgChar *buf, int64_t size)
1615 {
1616 int64_t i;
1617 SgStringPort *port = SG_STRING_PORT(self);
1618 SgChar *start = SG_STRING_PORT(self)->buffer.buf;
1619 size_t ssize = SG_STRING_PORT(self)->buffer.end - start;
1620 for (i = 0; i < size && port->buffer.index < ssize;
1621 i++, port->buffer.index++) {
1622 buf[i] = start[port->buffer.index];
1623 if (buf[i] == '\n') {
1624 SG_PORT(self)->lineNo++;
1625 }
1626 }
1627 return i;
1628 }
1629
input_string_port_position(SgObject self)1630 static int64_t input_string_port_position(SgObject self)
1631 {
1632 return CONSIDER_PEEK(SG_STRING_PORT(self)->buffer.index, self);
1633 }
1634
input_string_set_port_position(SgObject self,int64_t offset,SgWhence whence)1635 static void input_string_set_port_position(SgObject self, int64_t offset,
1636 SgWhence whence)
1637 {
1638 SgStringPort *tp = SG_STRING_PORT(self);
1639 int64_t realoff = 0LL;
1640 int64_t size = (int64_t)(tp->buffer.end - tp->buffer.buf);
1641 switch (whence) {
1642 case SG_BEGIN: realoff = offset; break;
1643 case SG_CURRENT: realoff = tp->buffer.index + offset; break;
1644 case SG_END: realoff = size + offset; break;
1645 }
1646 /* don't overflow! */
1647 if (realoff > size) realoff = size;
1648 /* underflow is an error! */
1649 if (realoff < 0) {
1650 Sg_Error(UC("given offset is out of range %d"), (int)offset);
1651 }
1652 tp->buffer.index = (size_t)realoff;
1653 }
1654
output_string_port_position(SgObject self)1655 static int64_t output_string_port_position(SgObject self)
1656 {
1657 return SG_PORT(self)->position;
1658 }
1659
output_string_set_port_position(SgObject self,int64_t offset,SgWhence whence)1660 static void output_string_set_port_position(SgObject self, int64_t offset,
1661 SgWhence whence)
1662 {
1663 SgStringPort *tp = SG_STRING_PORT(self);
1664 int64_t realoff = 0LL;
1665 switch (whence) {
1666 case SG_BEGIN: realoff = offset; break;
1667 case SG_CURRENT:
1668 realoff = output_string_port_position(self) + offset;
1669 break;
1670 case SG_END:
1671 SG_STREAM_BUFFER_COUNTC(realoff, tp->buffer.start);
1672 realoff += offset;
1673 break;
1674 }
1675 /* underflow is an error! */
1676 if (realoff < 0) {
1677 Sg_Error(UC("given offset is out of range %d"), (int)offset);
1678 }
1679 SG_STREAM_BUFFER_SET_POSITIONC(tp->buffer.start, tp->buffer.current,
1680 realoff);
1681 SG_PORT(self)->position = realoff;
1682 }
1683
1684 static SgPortTable str_inputs = {
1685 NULL,
1686 string_iport_close,
1687 NULL,
1688 NULL,
1689 NULL,
1690 input_string_port_position,
1691 input_string_set_port_position,
1692 NULL, /* open */
1693 NULL, /* readb */
1694 NULL, /* readbAll */
1695 NULL, /* writeb */
1696 string_iport_get_string,
1697 NULL,
1698 };
1699
1700 static SgPortTable str_outputs = {
1701 NULL,
1702 string_oport_close,
1703 NULL,
1704 NULL,
1705 NULL,
1706 output_string_port_position,
1707 output_string_set_port_position,
1708 NULL, /* open */
1709 NULL, /* readb */
1710 NULL, /* readbAll */
1711 NULL, /* writeb */
1712 NULL,
1713 string_oport_put_string
1714 };
1715
Sg_MakeStringOutputPort(size_t bufferSize)1716 SgObject Sg_MakeStringOutputPort(size_t bufferSize)
1717 {
1718 SgStringPort *z = SG_NEW(SgStringPort);
1719 return Sg_InitStringOutputPort(z, bufferSize);
1720 }
1721
Sg_InitStringOutputPort(SgStringPort * port,size_t bufferSize)1722 SgObject Sg_InitStringOutputPort(SgStringPort *port,
1723 size_t bufferSize)
1724 {
1725 SG_INIT_PORT(port, SG_CLASS_STRING_PORT, SG_OUTPUT_PORT, &str_outputs,
1726 SG_TRUE);
1727
1728 /* TODO compute pre-allocated buffer using buffer size */
1729 port->buffer.start = port->buffer.current = SG_NEW(char_buffer);
1730 port->buffer.start->position = 0;
1731
1732 return SG_OBJ(port);
1733 }
1734
Sg_MakeStringInputPort(SgString * s,int64_t start,int64_t end)1735 SgObject Sg_MakeStringInputPort(SgString *s, int64_t start, int64_t end)
1736 {
1737 SgStringPort *z = SG_NEW(SgStringPort);
1738 return Sg_InitStringInputPort(z, s, start, end);
1739 }
1740
Sg_InitStringInputPort(SgStringPort * port,SgString * s,int64_t start,int64_t end)1741 SgObject Sg_InitStringInputPort(SgStringPort *port, SgString *s,
1742 int64_t start, int64_t end)
1743 {
1744 int64_t len = SG_STRING_SIZE(s);
1745
1746 SG_CHECK_START_END(start, end, len);
1747
1748 SG_INIT_PORT(port, SG_CLASS_STRING_PORT, SG_INPUT_PORT, &str_inputs, SG_TRUE);
1749 port->buffer.buf = SG_STRING_VALUE(s);
1750 port->buffer.end = SG_STRING_VALUE(s) + end;
1751 port->buffer.index = start;
1752 SG_PORT(port)->lineNo = 1;
1753 return SG_OBJ(port);
1754 }
1755
1756 /*
1757 TODO
1758 I need to write optimised char_buffer stuff but for now
1759 I just need to write this simple implementation.
1760 */
Sg_ConvertToStringOutputPort(SgChar * buf,int bufferSize)1761 SgObject Sg_ConvertToStringOutputPort(SgChar *buf, int bufferSize)
1762 {
1763 SgObject o = Sg_MakeStringOutputPort(bufferSize);
1764 Sg_WritesUnsafe(SG_PORT(o), buf, bufferSize);
1765 return o;
1766 }
1767
1768 /* custom ports */
1769 /* because of non-good implementation of SG_PORT_HAS_U8_AHEAD, we need these
1770 for custom binary port.
1771 */
1772 #define SG_CUSTOM_HAS_U8_AHEAD(obj) (SG_PORT(obj)->peek != EOF)
1773 #define SG_CUSTOM_U8_AHEAD(obj) (SG_PORT(obj)->peek)
1774
1775 /*
1776 The same trick as buffered port.
1777 TODO should we expose this?
1778 */
1779 typedef struct {
1780 SgCustomPort in;
1781 SgCustomPort out;
1782 } SgCustomBiPort;
1783
1784 #define BI_CUSTOM_OUT(p) (&((SgCustomBiPort *)p)->out)
1785
1786
1787 /* I'm not sure if we still need this method. */
custom_binary_open(SgObject self)1788 static int custom_binary_open(SgObject self)
1789 {
1790 return SG_PORT(self)->closed == SG_PORT_CLOSED;
1791 }
1792
custom_binary_read_inner(SgObject self,uint8_t * buf,int64_t size,int allP)1793 static int64_t custom_binary_read_inner(SgObject self, uint8_t *buf,
1794 int64_t size, int allP)
1795 {
1796 SgObject bv, result;
1797 long bvsize;
1798 int64_t read = 0;
1799
1800 if (SG_CUSTOM_HAS_U8_AHEAD(self)) {
1801 buf[0] = SG_CUSTOM_U8_AHEAD(self);
1802 SG_CUSTOM_U8_AHEAD(self) = EOF;
1803 if (size == 1) return 1; /* short cut */
1804 size--;
1805 read++;
1806 }
1807
1808 bv = SG_CUSTOM_PORT(self)->binaryBuffer;
1809 bvsize = SG_BVECTOR_SIZE(bv);
1810 /* input/output port is *not* a bidirectional port so we can use the
1811 same buffer as write. so re-use it.*/
1812 do {
1813 long r;
1814 long count = (size < bvsize)? (long)size: bvsize;
1815 result = Sg_Apply3(SG_CUSTOM_PORT(self)->read, bv,
1816 SG_MAKE_INT(0), SG_MAKE_INT(count));
1817
1818 if (!SG_INTP(result)) {
1819 Sg_IOReadError(SG_INTERN("get-bytevector"),
1820 Sg_Sprintf(UC("custom port read! "
1821 "returned invalid value %S"), result),
1822 self,
1823 result);
1824 }
1825 if (result == SG_MAKE_INT(0)) {
1826 break;
1827 }
1828 r = SG_INT_VALUE(result);
1829 memcpy(buf+read, SG_BVECTOR_ELEMENTS(bv), r);
1830 read += r;
1831 /* size -= r; */
1832
1833 /* make things stop */
1834 /* if (allP && size != SG_INT_VALUE(result)) break; */
1835 } while (0);
1836 if (read == 0) return 0; /* short cut */
1837 SG_PORT(self)->position += read;
1838 /* memcpy(buf, SG_BVECTOR_ELEMENTS(bv), read); */
1839 return read;
1840 }
1841
custom_binary_read(SgObject self,uint8_t * buf,int64_t size)1842 static int64_t custom_binary_read(SgObject self, uint8_t *buf, int64_t size)
1843 {
1844 return custom_binary_read_inner(self, buf, size, FALSE);
1845 }
1846
custom_binary_read_all(SgObject self,uint8_t ** buf)1847 static int64_t custom_binary_read_all(SgObject self, uint8_t **buf)
1848 {
1849 SgObject accum = Sg_MakeByteArrayOutputPort(PORT_DEFAULT_BUF_SIZE);
1850 int64_t read_size = 0;
1851 uint8_t rbuf[1024];
1852
1853 for (;;) {
1854 int64_t size = custom_binary_read_inner(self, rbuf, 1024, TRUE);
1855 if (size == 0) break;
1856 read_size += size;
1857 Sg_WritebUnsafe(accum, rbuf, 0, (int)size);
1858 if (size != 1024) break;
1859 }
1860 *buf = Sg_GetByteArrayFromBinaryPort(accum);
1861 return read_size;
1862 }
1863
custom_binary_put_u8_array(SgObject self,uint8_t * v,int64_t size)1864 static int64_t custom_binary_put_u8_array(SgObject self, uint8_t *v,
1865 int64_t size)
1866 {
1867 static const SgObject start = SG_MAKE_INT(0);
1868 SgObject result;
1869 SgByteVector *bv = SG_CUSTOM_PORT(self)->binaryBuffer;
1870 int64_t written = 0, c = size;
1871 long bvsize = SG_BVECTOR_SIZE(bv);
1872 /* to avoid huge allocation, we use pre-allocated buffer to
1873 pass to the Scheme procedure. */
1874 while (written < size) {
1875 long count = (c < bvsize)? (long)c: bvsize;
1876 int64_t t;
1877 memcpy(SG_BVECTOR_ELEMENTS(bv), v+written, count);
1878 result = Sg_Apply3(SG_CUSTOM_PORT(self)->write, bv,
1879 start, SG_MAKE_INT(count));
1880 if (!SG_INTP(result)) {
1881 Sg_IOWriteError(SG_INTERN("put-bytevector"),
1882 Sg_Sprintf(UC("custom port write!"
1883 " returned invalid value, %S"), result),
1884 self,
1885 result);
1886 }
1887 /* how should we tread 0, for now break */
1888 if (SG_EQ(SG_MAKE_INT(0), result)) break;
1889 t = Sg_GetIntegerS64Clamp(result, SG_CLAMP_NONE, NULL);
1890 if (t < 0) {
1891 Sg_IOWriteError(SG_INTERN("put-bytevector"),
1892 Sg_Sprintf(UC("custom port write!"
1893 " exprected non negative integer")),
1894 self,
1895 result);
1896 }
1897 written += t;
1898 c -= t;
1899 }
1900 return written;
1901 }
1902
custom_bi_binary_put_u8_array(SgObject self,uint8_t * v,int64_t size)1903 static int64_t custom_bi_binary_put_u8_array(SgObject self, uint8_t *v,
1904 int64_t size)
1905 {
1906 return custom_binary_put_u8_array(BI_CUSTOM_OUT(self), v, size);
1907 }
1908
custom_flush(SgObject self)1909 static void custom_flush(SgObject self)
1910 {
1911 if (SG_PROCEDUREP(SG_CUSTOM_PORT(self)->flush)) {
1912 Sg_Apply0(SG_CUSTOM_PORT(self)->flush);
1913 }
1914 }
1915
custom_close(SgObject self)1916 static int custom_close(SgObject self)
1917 {
1918 if (SG_PORT(self)->closed != SG_PORT_CLOSED) {
1919 if (SG_PROCEDUREP(SG_CUSTOM_PORT(self)->close)) {
1920 Sg_Apply0(SG_CUSTOM_PORT(self)->close);
1921 }
1922 Sg_UnregisterFinalizer(self);
1923 SG_PORT(self)->closed = SG_PORT_CLOSED;
1924 }
1925 return TRUE;
1926 }
1927
custom_ready(SgObject self)1928 static int custom_ready(SgObject self)
1929 {
1930 if (SG_PROCEDUREP(SG_CUSTOM_PORT(self)->ready)) {
1931 SgObject r = Sg_Apply0(SG_CUSTOM_PORT(self)->ready);
1932 return !SG_FALSEP(r);
1933 }
1934 return TRUE;
1935 }
1936
custom_port_position(SgObject self)1937 static int64_t custom_port_position(SgObject self)
1938 {
1939 SgObject ret;
1940 int64_t pos;
1941 if (!SG_PROCEDUREP(SG_CUSTOM_PORT(self)->getPosition)) {
1942 Sg_WrongTypeOfArgumentViolation(SG_INTERN("port-position"),
1943 SG_MAKE_STRING("positionable port"),
1944 self, SG_NIL);
1945 return -1;
1946 }
1947 ret = Sg_Apply0(SG_CUSTOM_PORT(self)->getPosition);
1948 if (!SG_EXACT_INTP(ret)) {
1949 Sg_AssertionViolation(SG_INTERN("port-position"),
1950 Sg_Sprintf(UC("invalid result %S from %S"),
1951 ret, self),
1952 self);
1953 return -1;
1954 }
1955 pos = Sg_GetIntegerS64Clamp(ret, SG_CLAMP_NONE, NULL);
1956 if (SG_CUSTOM_HAS_U8_AHEAD(self)) {
1957 return pos - 1;
1958 } else {
1959 return pos;
1960 }
1961 }
1962
custom_binary_set_port_position(SgObject port,int64_t offset,SgWhence whence)1963 static void custom_binary_set_port_position(SgObject port, int64_t offset,
1964 SgWhence whence)
1965 {
1966 SgObject sym;
1967 if (!SG_PROCEDUREP(SG_CUSTOM_PORT(port)->setPosition)) {
1968 Sg_WrongTypeOfArgumentViolation(SG_INTERN("port-position"),
1969 SG_MAKE_STRING("positionable port"),
1970 port, SG_NIL);
1971 return;
1972 }
1973 /* reset cache */
1974 SG_CUSTOM_U8_AHEAD(port) = EOF;
1975 sym = SG_FALSE;
1976 switch (whence) {
1977 case SG_BEGIN:
1978 sym = SG_SYMBOL_BEGIN;
1979 SG_PORT(port)->position = offset;
1980 break;
1981 case SG_CURRENT:
1982 sym = SG_INTERN("current");
1983 SG_PORT(port)->position += offset;
1984 break;
1985 case SG_END:
1986 if (offset > 0) {
1987 Sg_Error(UC("end whence requires zero or negative offset %d"),
1988 (int)offset);
1989 }
1990 sym = SG_INTERN("end");
1991 SG_PORT(port)->position += offset;
1992 break;
1993 }
1994 Sg_Apply2(SG_CUSTOM_PORT(port)->setPosition,
1995 Sg_MakeIntegerFromS64(offset), sym);
1996 }
1997
custom_textual_set_port_position(SgObject port,int64_t offset,SgWhence whence)1998 static void custom_textual_set_port_position(SgObject port, int64_t offset,
1999 SgWhence whence)
2000 {
2001 SgObject proc;
2002 SgObject sym = SG_FALSE;
2003 if (!SG_PROCEDUREP(SG_CUSTOM_PORT(port)->setPosition)) {
2004 Sg_WrongTypeOfArgumentViolation(SG_INTERN("port-position"),
2005 SG_MAKE_STRING("positionable port"),
2006 port, SG_NIL);
2007 return;
2008 }
2009 proc = SG_CUSTOM_PORT(port)->setPosition;
2010
2011 switch (whence) {
2012 case SG_BEGIN: sym = SG_SYMBOL_BEGIN; break;
2013 case SG_CURRENT: sym = SG_INTERN("current"); break;
2014 case SG_END:
2015 if (offset > 0) {
2016 Sg_Error(UC("end whence requires zero or negative offset %d"),
2017 (int)offset);
2018 }
2019 sym = SG_INTERN("end");
2020 break;
2021 }
2022 Sg_Apply2(proc, Sg_MakeIntegerFromS64(offset), sym);
2023 }
2024
2025 static SgPortTable custom_binary_table = {
2026 custom_flush,
2027 custom_close,
2028 custom_ready,
2029 NULL,
2030 NULL,
2031 custom_port_position,
2032 custom_binary_set_port_position,
2033 custom_binary_open,
2034 custom_binary_read,
2035 custom_binary_read_all,
2036 custom_binary_put_u8_array,
2037 NULL, /* reads */
2038 NULL /* writes */
2039 };
2040
2041 static SgPortTable custom_bi_binary_table = {
2042 custom_flush,
2043 custom_close,
2044 custom_ready,
2045 NULL,
2046 NULL,
2047 NULL,
2048 NULL,
2049 custom_binary_open,
2050 custom_binary_read,
2051 custom_binary_read_all,
2052 custom_bi_binary_put_u8_array,
2053 NULL, /* reads */
2054 NULL /* writes */
2055 };
2056
2057
2058 /*
2059 For future we may provide non R6RS custom port generator which
2060 requires setPosition procedure accepts whence argument as the second
2061 argument unlike R6RS procedure.
2062 To make my life easier, we create closure to wrap it.
2063 This may consume a bit more memory but better than handling things
2064 complicated way
2065 */
wrapped_custom_set_position(SgObject * args,int argc,void * data)2066 static SgObject wrapped_custom_set_position(SgObject *args, int argc,
2067 void *data)
2068 {
2069 /* a bit pain in the ass but to make this call safe*/
2070 if (SG_FALSEP(SG_PORT(SG_CAR(data))->transcoder)) {
2071 int64_t offset = Sg_GetIntegerS64Clamp(args[0], SG_CLAMP_NONE, NULL);
2072 SG_PORT(SG_CAR(data))->position = offset;
2073 }
2074 return Sg_VMApply1(SG_CDR(SG_OBJ(data)), args[0]);
2075 }
2076
wrap_custom_set_procedure(SgPort * p,SgObject proc)2077 static SgObject wrap_custom_set_procedure(SgPort *p, SgObject proc)
2078 {
2079 if (SG_PROCEDUREP(proc)) {
2080 SgObject data = Sg_Cons(p, proc);
2081 return Sg_MakeSubr(wrapped_custom_set_position, data, 2, 0,
2082 SG_PROCEDURE_NAME(proc));
2083 }
2084 return SG_FALSE;
2085 }
2086
Sg_MakeCustomBinaryPort(SgString * id,int direction,SgObject read,SgObject write,SgObject getPosition,SgObject setPosition,SgObject close,SgObject ready)2087 SgObject Sg_MakeCustomBinaryPort(SgString *id,
2088 int direction,
2089 SgObject read,
2090 SgObject write,
2091 SgObject getPosition,
2092 SgObject setPosition,
2093 SgObject close,
2094 SgObject ready)
2095 {
2096 SgCustomPortSpec spec = {
2097 SG_CUSTOM_PORT_TYPE_BINARY,
2098 direction,
2099 id,
2100 getPosition,
2101 setPosition,
2102 close,
2103 read,
2104 write,
2105 ready,
2106 SG_FALSE,
2107 NULL,
2108 TRUE
2109 };
2110 return Sg_MakeCustomPort(&spec);
2111 }
2112
custom_textual_get_string(SgObject self,SgChar * buf,int64_t size)2113 static int64_t custom_textual_get_string(SgObject self, SgChar *buf,
2114 int64_t size)
2115 {
2116 SgObject s, result;
2117 /* int start; */
2118 int64_t read = 0, i;
2119
2120 if (size == 0) return 0;
2121
2122 s = SG_CUSTOM_PORT(self)->textualBuffer;
2123 do {
2124 long r;
2125 result = Sg_Apply3(SG_CUSTOM_PORT(self)->read, s,
2126 SG_MAKE_INT(0),
2127 SG_MAKE_INT(size));
2128 if (!SG_INTP(result)) {
2129 Sg_IOReadError(SG_INTERN("get-char"),
2130 Sg_Sprintf(UC("custom port read! "
2131 "returned invalid value %S"), result),
2132 self,
2133 result);
2134 }
2135 if (result == SG_MAKE_INT(0)) {
2136 break;
2137 }
2138 r = SG_INT_VALUE(result);
2139 for (i = 0; i < r; i++) {
2140 buf[i] = SG_STRING_VALUE_AT(s, i);
2141 }
2142 read += r;
2143 /* size -= r; */
2144 /* start += r; */
2145 } while (0);
2146
2147 if (read == 0) return 0; /* short cut */
2148 SG_PORT(self)->position += read;
2149 return read;
2150 }
2151
custom_textual_put_string(SgObject self,SgChar * str,int64_t count)2152 static int64_t custom_textual_put_string(SgObject self, SgChar *str,
2153 int64_t count)
2154 {
2155 static const SgObject start = SG_MAKE_INT(0);
2156 SgObject result;
2157 SgString *s = SG_CUSTOM_PORT(self)->textualBuffer;
2158 int64_t written = 0, c = count;
2159 long size = SG_STRING_SIZE(s);
2160
2161 while (written < count) {
2162 long rc = (c < size)? (long)c: size;
2163 int64_t t;
2164 memcpy(SG_STRING_VALUE(s), str+written, rc*sizeof(SgChar));
2165 result = Sg_Apply3(SG_CUSTOM_PORT(self)->write, s, start,
2166 SG_MAKE_INT(rc));
2167 if (!SG_INTP(result)) {
2168 Sg_IOWriteError(SG_INTERN("put-string"),
2169 Sg_Sprintf(UC("custom port write!"
2170 " returned invalid value, %S"), result),
2171 self,
2172 result);
2173 }
2174 if (SG_EQ(SG_MAKE_INT(0), result)) break;
2175 t = Sg_GetIntegerS64Clamp(result, SG_CLAMP_NONE, NULL);
2176 if (t < 0) {
2177 Sg_IOWriteError(SG_INTERN("put-string"),
2178 Sg_Sprintf(UC("custom port write!"
2179 " exprected non negative integer")),
2180 self,
2181 result);
2182 }
2183 written += t;
2184 c -= t;
2185 }
2186 return written;
2187 }
2188
custom_bi_textual_put_string(SgObject self,SgChar * str,int64_t count)2189 static int64_t custom_bi_textual_put_string(SgObject self, SgChar *str,
2190 int64_t count)
2191 {
2192 return custom_textual_put_string(BI_CUSTOM_OUT(self), str, count);
2193 }
2194
2195 static SgPortTable custom_textual_table = {
2196 custom_flush,
2197 custom_close,
2198 custom_ready,
2199 NULL,
2200 NULL,
2201 custom_port_position,
2202 custom_textual_set_port_position,
2203 NULL, /* open */
2204 NULL, /* readb */
2205 NULL, /* readbAll */
2206 NULL, /* writeb */
2207 custom_textual_get_string,
2208 custom_textual_put_string
2209 };
2210
2211 static SgPortTable custom_bi_textual_table = {
2212 custom_flush,
2213 custom_close,
2214 custom_ready,
2215 NULL,
2216 NULL,
2217 NULL, /* no positioning */
2218 NULL, /* no positioning */
2219 NULL, /* open */
2220 NULL, /* readb */
2221 NULL, /* readbAll */
2222 NULL, /* writeb */
2223 custom_textual_get_string,
2224 custom_bi_textual_put_string
2225 };
2226
2227
Sg_MakeCustomTextualPort(SgString * id,int direction,SgObject read,SgObject write,SgObject getPosition,SgObject setPosition,SgObject close,SgObject ready)2228 SgObject Sg_MakeCustomTextualPort(SgString *id,
2229 int direction,
2230 SgObject read,
2231 SgObject write,
2232 SgObject getPosition,
2233 SgObject setPosition,
2234 SgObject close,
2235 SgObject ready)
2236 {
2237 SgCustomPortSpec spec = {
2238 SG_CUSTOM_PORT_TYPE_TEXTUAL,
2239 direction,
2240 id,
2241 getPosition,
2242 setPosition,
2243 close,
2244 read,
2245 write,
2246 ready,
2247 SG_FALSE,
2248 NULL,
2249 TRUE
2250 };
2251 return Sg_MakeCustomPort(&spec);
2252 }
2253
get_custom_table(SgCustomPortSpec * spec)2254 static SgPortTable* get_custom_table(SgCustomPortSpec *spec)
2255 {
2256 int binaryP = SG_CUSTOM_PORT_TYPE_BINARY == spec->type;
2257
2258 if (SG_BIDIRECTIONAL_PORT == spec->direction) {
2259 return binaryP ? &custom_bi_binary_table : &custom_bi_textual_table;
2260 } else {
2261 return binaryP ? &custom_binary_table : &custom_textual_table;
2262 }
2263 }
2264
Sg_MakeCustomPort(SgCustomPortSpec * spec)2265 SgObject Sg_MakeCustomPort(SgCustomPortSpec *spec)
2266 {
2267 SgPortTable *tbl = (spec->table)? spec->table: get_custom_table(spec);
2268 SgObject trans = (spec->type == SG_CUSTOM_PORT_TYPE_BINARY)
2269 ? SG_FALSE: SG_TRUE;
2270 SgCustomPort *port;
2271 SgObject setPosition, getPosition;
2272
2273 if (spec->direction == SG_BIDIRECTIONAL_PORT) {
2274 /* the excess part initialisation. */
2275 port = (SgCustomPort *)make_port(SgCustomBiPort,
2276 spec->direction,
2277 SG_CLASS_CUSTOM_PORT,
2278 tbl,
2279 trans);
2280 if (SG_FALSEP(trans)) {
2281 BI_CUSTOM_OUT(port)->binaryBuffer
2282 = Sg_MakeByteVector(SG_PORT_DEFAULT_BUFFER_SIZE, 0);
2283 } else {
2284 BI_CUSTOM_OUT(port)->textualBuffer
2285 = Sg_ReserveString(SG_PORT_DEFAULT_BUFFER_SIZE, 0);
2286 }
2287 /* we only need this one since writeb or writes is relative
2288 to be bidireational. */
2289 BI_CUSTOM_OUT(port)->write = port->write = spec->write;
2290 } else {
2291 port = (SgCustomPort *)make_port(SgCustomPort,
2292 spec->direction,
2293 SG_CLASS_CUSTOM_PORT,
2294 tbl,
2295 trans);
2296 }
2297
2298 setPosition = (spec->wrap)
2299 ? wrap_custom_set_procedure(SG_PORT(port), spec->setPosition)
2300 : spec->setPosition;
2301 getPosition = spec->getPosition;
2302
2303 port->id = spec->id;
2304 port->getPosition = getPosition;
2305 port->setPosition = setPosition;
2306 port->close = spec->close;
2307 port->read = spec->read;
2308 port->write = spec->write;
2309 port->ready = spec->ready;
2310 port->flush = spec->flush;
2311 SG_PORT(port)->lineNo = 1;
2312 if (SG_FALSEP(trans)) {
2313 port->binaryBuffer = Sg_MakeByteVector(SG_PORT_DEFAULT_BUFFER_SIZE, 0);
2314 } else {
2315 port->textualBuffer = Sg_ReserveString(SG_PORT_DEFAULT_BUFFER_SIZE, 0);
2316 }
2317 Sg_RegisterFinalizer(SG_OBJ(port), port_finalize, NULL);
2318 return SG_OBJ(port);
2319 }
2320
2321 #define MAKE_CUSTOM_SLOT_ACC(name, type, pred) \
2322 static SgObject SG_CPP_CAT3(custom_, name, _get)(SgCustomPort *p) \
2323 { \
2324 return p-> name; \
2325 } \
2326 static void SG_CPP_CAT3(custom_, name, _set)(SgCustomPort *p, SgObject v) \
2327 { \
2328 if (!pred(v)) { \
2329 Sg_WrongTypeOfArgumentViolation(SG_INTERN(#name), \
2330 SG_INTERN(#type), \
2331 v, SG_NIL); \
2332 } \
2333 p-> name = v; \
2334 }
2335
2336 #define PROC_OR_FALSE(o) (SG_FALSEP(o) || SG_PROCEDUREP(o))
2337 MAKE_CUSTOM_SLOT_ACC(id, "string", SG_STRINGP)
2338 MAKE_CUSTOM_SLOT_ACC(getPosition, "procedure or #f", PROC_OR_FALSE)
2339 MAKE_CUSTOM_SLOT_ACC(setPosition, "procedure or #f", PROC_OR_FALSE)
2340 MAKE_CUSTOM_SLOT_ACC(read, "procedure or #f", PROC_OR_FALSE)
2341 MAKE_CUSTOM_SLOT_ACC(write, "procedure or #f", PROC_OR_FALSE)
2342 MAKE_CUSTOM_SLOT_ACC(ready, "procedure or #f", PROC_OR_FALSE)
2343 MAKE_CUSTOM_SLOT_ACC(flush, "procedure or #f", PROC_OR_FALSE)
2344 MAKE_CUSTOM_SLOT_ACC(close, "procedure or #f", PROC_OR_FALSE)
2345
2346 static SgSlotAccessor custom_slots[] = {
2347 SG_CLASS_SLOT_SPEC("id", 0, custom_id_get, custom_id_set),
2348 SG_CLASS_SLOT_SPEC("position", 1, custom_getPosition_get,
2349 custom_getPosition_set),
2350 SG_CLASS_SLOT_SPEC("set-position", 2, custom_setPosition_get,
2351 custom_setPosition_set),
2352 SG_CLASS_SLOT_SPEC("read", 3, custom_read_get, custom_read_set),
2353 SG_CLASS_SLOT_SPEC("write", 4, custom_write_get, custom_write_set),
2354 SG_CLASS_SLOT_SPEC("ready", 5, custom_ready_get, custom_ready_set),
2355 SG_CLASS_SLOT_SPEC("flush", 6, custom_flush_get, custom_flush_set),
2356 SG_CLASS_SLOT_SPEC("close", 7, custom_close_get, custom_close_set),
2357 { { NULL } }
2358 };
2359
2360 static SgObject SG_KEYWORD_ID = SG_FALSE;
2361 static SgObject SG_KEYWORD_POSITION = SG_FALSE;
2362 static SgObject SG_KEYWORD_SET_POSITION = SG_FALSE;
2363 static SgObject SG_KEYWORD_READ = SG_FALSE;
2364 static SgObject SG_KEYWORD_WRITE = SG_FALSE;
2365 static SgObject SG_KEYWORD_READY = SG_FALSE;
2366 static SgObject SG_KEYWORD_FLUSH = SG_FALSE;
2367 static SgObject SG_KEYWORD_CLOSE = SG_FALSE;
2368
custom_port_allocate_rec(int type,SgString * id,int direction,SgObject read,SgObject write,SgObject getPosition,SgObject setPosition,SgObject close,SgObject ready,SgObject flush)2369 static SgCustomPort * custom_port_allocate_rec(int type,
2370 SgString *id,
2371 int direction,
2372 SgObject read,
2373 SgObject write,
2374 SgObject getPosition,
2375 SgObject setPosition,
2376 SgObject close,
2377 SgObject ready,
2378 SgObject flush)
2379 {
2380 SgCustomPortSpec spec = {
2381 type,
2382 direction,
2383 id,
2384 getPosition,
2385 setPosition,
2386 close,
2387 read,
2388 write,
2389 ready,
2390 flush,
2391 NULL,
2392 FALSE,
2393 };
2394 return (SgCustomPort *)Sg_MakeCustomPort(&spec);
2395 }
2396
custom_port_allocate(SgClass * klass,SgObject initargs)2397 static SgObject custom_port_allocate(SgClass *klass, SgObject initargs)
2398 {
2399 int type, flags = 0, i;
2400 SgCustomPort *port;
2401 SgObject *slots;
2402
2403 if (Sg_SubtypeP(klass, SG_CLASS_CUSTOM_TEXTUAL_PORT)) {
2404 type = SG_CUSTOM_PORT_TYPE_TEXTUAL;
2405 } else {
2406 /* if users allocate with <custom-port> then it's binary */
2407 type = SG_CUSTOM_PORT_TYPE_BINARY;
2408 }
2409 if (Sg_SubtypeP(klass, SG_CLASS_BIDIRECTIONAL_PORT)) {
2410 flags = SG_BIDIRECTIONAL_PORT;
2411 } else {
2412 if (Sg_SubtypeP(klass, SG_CLASS_INPUT_PORT)) flags |= SG_INPUT_PORT;
2413 if (Sg_SubtypeP(klass, SG_CLASS_OUTPUT_PORT)) flags |= SG_OUTPUT_PORT;
2414 }
2415
2416 if (!flags) Sg_Error(UC("custom port must inherit input or output port"));
2417
2418 /* initialize slots */
2419 port = custom_port_allocate_rec(
2420 type,
2421 Sg_GetKeyword(SG_KEYWORD_ID, initargs, SG_FALSE),
2422 flags,
2423 Sg_GetKeyword(SG_KEYWORD_READ, initargs, SG_FALSE),
2424 Sg_GetKeyword(SG_KEYWORD_WRITE, initargs, SG_FALSE),
2425 Sg_GetKeyword(SG_KEYWORD_POSITION, initargs, SG_FALSE),
2426 Sg_GetKeyword(SG_KEYWORD_SET_POSITION,initargs, SG_FALSE),
2427 Sg_GetKeyword(SG_KEYWORD_CLOSE, initargs, SG_FALSE),
2428 Sg_GetKeyword(SG_KEYWORD_READY, initargs, SG_FALSE),
2429 Sg_GetKeyword(SG_KEYWORD_FLUSH, initargs, SG_FALSE));
2430 SG_SET_CLASS(port, klass);
2431
2432 /* TODO maybe we shouldn't do it here */
2433 slots = SG_NEW_ARRAY(SgObject, klass->nfields);
2434 for (i = 0; i < klass->nfields; i++) {
2435 slots[i] = SG_UNBOUND;
2436 }
2437 SG_INSTANCE(port)->slots = slots;
2438 if (flags == SG_BIDIRECTIONAL_PORT) {
2439 /* 'out' side of port should have the same slot */
2440 SG_INSTANCE(BI_CUSTOM_OUT(port))->slots = slots;
2441 }
2442
2443 return SG_OBJ(port);
2444 }
2445
2446 /* Port APIs */
2447
Sg_GetByteVectorFromBinaryPort(SgBytePort * port)2448 SgObject Sg_GetByteVectorFromBinaryPort(SgBytePort *port)
2449 {
2450 if (SG_INPUT_PORTP(port)) {
2451 uint8_t *start = SG_BINARY_PORT_BUFFER(port)->buf;
2452 uint8_t *end = SG_BINARY_PORT_BUFFER(port)->end;
2453 size_t index = SG_BINARY_PORT_BUFFER(port)->index;
2454 size_t size = end-start;
2455 /* TODO should we copy? I'm not sure if we are using this pass though. */
2456 return Sg_MakeByteVectorFromU8Array(start+index, (int)(size-index));
2457 } else {
2458 /* recreate */
2459 int size;
2460 SgByteVector *ret;
2461 SG_STREAM_BUFFER_COUNTB(size, port->buffer.start);
2462 ret = Sg_MakeByteVector(size, 0);
2463 SG_STREAM_BUFFER_GET_BUFFERB(SG_BVECTOR_ELEMENTS(ret),
2464 port->buffer.start);
2465 return ret;
2466 }
2467 }
2468
Sg_GetStringFromStringPort(SgStringPort * port)2469 SgObject Sg_GetStringFromStringPort(SgStringPort *port)
2470 {
2471 if (SG_INPUT_PORTP(port)) {
2472 return Sg_MakeString(port->buffer.buf + port->buffer.index,
2473 SG_HEAP_STRING,
2474 (port->buffer.end - port->buffer.end) -
2475 port->buffer.index);
2476 } else {
2477 int size;
2478 SgString *ret;
2479 SG_STREAM_BUFFER_COUNTC(size, port->buffer.start);
2480 ret = Sg_ReserveString(size, ' ');
2481 SG_STREAM_BUFFER_GET_BUFFERC(SG_STRING_VALUE(ret), port->buffer.start);
2482 return ret;
2483 }
2484 }
2485
Sg_ClosePort(SgPort * port)2486 void Sg_ClosePort(SgPort *port)
2487 {
2488 port_cleanup(port);
2489 }
2490
2491 /* this doesn't close port, just pseudo.
2492 on C level we don't check if a port was closed or not.
2493 but on Scheme level we need to do it.
2494 */
Sg_PseudoClosePort(SgPort * port)2495 void Sg_PseudoClosePort(SgPort *port)
2496 {
2497 SG_PORT(port)->closed = SG_PORT_PSEUDO;
2498 }
2499
Sg_PortClosedP(SgPort * port)2500 int Sg_PortClosedP(SgPort *port)
2501 {
2502 return port->closed != SG_PORT_OPEN;
2503 }
2504
Sg_PseudoPortClosedP(SgPort * port)2505 int Sg_PseudoPortClosedP(SgPort *port)
2506 {
2507 return port->closed == SG_PORT_PSEUDO;
2508 }
2509
Sg_FlushPort(SgPort * port)2510 void Sg_FlushPort(SgPort *port)
2511 {
2512 if (SG_PORT_VTABLE(port)->flush) {
2513 SG_PORT_VTABLE(port)->flush(port);
2514 }
2515 }
2516
Sg_FlushAllPort(int exitting)2517 void Sg_FlushAllPort(int exitting)
2518 {
2519 SgWeakVector *ports;
2520 SgObject save[PORT_VECTOR_SIZE];
2521 SgObject p = SG_FALSE;
2522 int i, saved = 0;
2523
2524 /* save = SG_VECTOR(Sg_MakeVector(PORT_VECTOR_SIZE, SG_FALSE)); */
2525 for (i = 0; i < PORT_VECTOR_SIZE; i++) save[i] = SG_FALSE;
2526 ports = active_buffered_ports.ports;
2527
2528 for (i = 0; i < PORT_VECTOR_SIZE;) {
2529 Sg_LockMutex(&active_buffered_ports.lock);
2530 for (; i < PORT_VECTOR_SIZE; i++) {
2531 p = Sg_WeakVectorRef(ports, i, SG_FALSE);
2532 if (SG_PORTP(p)) {
2533 /* Sg_VectorSet(save, i, p); */
2534 save[i] = p;
2535 Sg_WeakVectorSet(ports, i, SG_TRUE);
2536 saved++;
2537 break;
2538 }
2539 }
2540 Sg_UnlockMutex(&active_buffered_ports.lock);
2541 if (SG_PORTP(p)) { /* */
2542 if (SG_PORT_VTABLE(p)->flush) {
2543 SG_PORT_VTABLE(p)->flush(p);
2544 }
2545 }
2546 }
2547 if (!exitting && saved) {
2548 Sg_LockMutex(&active_buffered_ports.lock);
2549 for (i = 0; i < PORT_VECTOR_SIZE; i++) {
2550 /* p = Sg_VectorRef(save, i, SG_FALSE); */
2551 p = save[i];
2552 if (SG_PORTP(p)) Sg_WeakVectorSet(ports, i, p);
2553 }
2554 Sg_UnlockMutex(&active_buffered_ports.lock);
2555 }
2556 }
2557
2558 #define SAFE_READ_CALL(p, call) \
2559 do { \
2560 SG_UNWIND_PROTECT { \
2561 call; \
2562 } SG_WHEN_ERROR { \
2563 SG_PORT_UNLOCK_READ(p); \
2564 SG_NEXT_HANDLER; \
2565 } SG_END_PROTECT; \
2566 } while (0)
2567
2568 #define SAFE_WRITE_CALL(p, call) \
2569 do { \
2570 SG_UNWIND_PROTECT { \
2571 call; \
2572 } SG_WHEN_ERROR { \
2573 SG_PORT_UNLOCK_WRITE(p); \
2574 SG_NEXT_HANDLER; \
2575 } SG_END_PROTECT; \
2576 } while (0)
2577
2578
Sg_Getb(SgPort * port)2579 int Sg_Getb(SgPort *port)
2580 {
2581 int b = -1;
2582 SG_PORT_LOCK_READ(port);
2583 SAFE_READ_CALL(port, b = Sg_GetbUnsafe(port));
2584 SG_PORT_UNLOCK_READ(port);
2585 return b;
2586 }
2587
Sg_Peekb(SgPort * port)2588 int Sg_Peekb(SgPort *port)
2589 {
2590 int b = -1;
2591 SG_PORT_LOCK_READ(port);
2592 SAFE_READ_CALL(port, b = Sg_PeekbUnsafe(port));
2593 SG_PORT_UNLOCK_READ(port);
2594 return b;
2595 }
2596
Sg_Readb(SgPort * port,uint8_t * buf,int64_t size)2597 int64_t Sg_Readb(SgPort *port, uint8_t *buf, int64_t size)
2598 {
2599 int64_t ret = 0;
2600 SG_PORT_LOCK_READ(port);
2601 SAFE_READ_CALL(port, ret = Sg_ReadbUnsafe(port, buf, size));
2602 SG_PORT_UNLOCK_READ(port);
2603 return ret;
2604 }
2605
Sg_ReadbAll(SgPort * port,uint8_t ** buf)2606 int64_t Sg_ReadbAll(SgPort *port, uint8_t **buf)
2607 {
2608 int64_t ret = 0;
2609 SG_PORT_LOCK_READ(port);
2610 SAFE_READ_CALL(port, ret = Sg_ReadbAllUnsafe(port, buf));
2611 SG_PORT_UNLOCK_READ(port);
2612 return ret;
2613 }
2614
Sg_Writeb(SgPort * port,uint8_t * b,int64_t start,int64_t count)2615 void Sg_Writeb(SgPort *port, uint8_t *b, int64_t start, int64_t count)
2616 {
2617 SG_PORT_LOCK_WRITE(port);
2618 SAFE_WRITE_CALL(port, Sg_WritebUnsafe(port, b, start, count));
2619 SG_PORT_UNLOCK_WRITE(port);
2620 }
2621
Sg_Putb(SgPort * port,uint8_t b)2622 void Sg_Putb(SgPort *port, uint8_t b)
2623 {
2624 SG_PORT_LOCK_WRITE(port);
2625 SAFE_WRITE_CALL(port, Sg_PutbUnsafe(port, b));
2626 SG_PORT_UNLOCK_WRITE(port);
2627 }
2628
Sg_Putbv(SgPort * port,SgByteVector * bv)2629 void Sg_Putbv(SgPort *port, SgByteVector *bv)
2630 {
2631 SG_PORT_LOCK_WRITE(port);
2632 SAFE_WRITE_CALL(port, Sg_PutbvUnsafe(port, bv));
2633 SG_PORT_UNLOCK_WRITE(port);
2634 }
2635
Sg_Getc(SgPort * port)2636 SgChar Sg_Getc(SgPort *port)
2637 {
2638 SgChar ch = -1;
2639 SG_PORT_LOCK_READ(port);
2640 SAFE_READ_CALL(port, ch = Sg_GetcUnsafe(port));
2641 SG_PORT_UNLOCK_READ(port);
2642 return ch;
2643 }
2644
Sg_Peekc(SgPort * port)2645 SgChar Sg_Peekc(SgPort *port)
2646 {
2647 SgChar ch = -1;
2648 SG_PORT_LOCK_READ(port);
2649 SAFE_READ_CALL(port, ch = Sg_PeekcUnsafe(port));
2650 SG_PORT_UNLOCK_READ(port);
2651 return ch;
2652 }
2653
Sg_Putc(SgPort * port,SgChar ch)2654 void Sg_Putc(SgPort *port, SgChar ch)
2655 {
2656 SG_PORT_LOCK_WRITE(port);
2657 SAFE_WRITE_CALL(port, Sg_PutcUnsafe(port, ch));
2658 SG_PORT_UNLOCK_WRITE(port);
2659 }
2660
Sg_Putz(SgPort * port,const char * str)2661 void Sg_Putz(SgPort *port, const char *str)
2662 {
2663 SG_PORT_LOCK_WRITE(port);
2664 SAFE_WRITE_CALL(port, Sg_PutzUnsafe(port, str));
2665 SG_PORT_UNLOCK_WRITE(port);
2666 }
2667
Sg_Putuz(SgPort * port,const SgChar * str)2668 void Sg_Putuz(SgPort *port, const SgChar *str)
2669 {
2670 SG_PORT_LOCK_WRITE(port);
2671 SAFE_WRITE_CALL(port, Sg_PutuzUnsafe(port, str));
2672 SG_PORT_UNLOCK_WRITE(port);
2673 }
2674
Sg_Puts(SgPort * port,SgString * str)2675 void Sg_Puts(SgPort *port, SgString *str)
2676 {
2677 SG_PORT_LOCK_WRITE(port);
2678 SAFE_WRITE_CALL(port, Sg_PutsUnsafe(port, str));
2679 SG_PORT_UNLOCK_WRITE(port);
2680 }
2681
Sg_Writes(SgPort * port,SgChar * s,int64_t count)2682 void Sg_Writes(SgPort *port, SgChar *s, int64_t count)
2683 {
2684 SG_PORT_LOCK_WRITE(port);
2685 SAFE_WRITE_CALL(port, Sg_WritesUnsafe(port, s, count));
2686 SG_PORT_UNLOCK_WRITE(port);
2687 }
2688
Sg_WritesUnsafe(SgPort * port,SgChar * s,int64_t count)2689 void Sg_WritesUnsafe(SgPort *port, SgChar *s, int64_t count)
2690 {
2691 if (SG_TEXTUAL_PORTP(port)) {
2692 SG_PORT_VTABLE(port)->writes(port, s, count);
2693 } else {
2694 Sg_Error(UC("textual port required, but got %S"), port);
2695 }
2696 }
2697
Sg_Reads(SgPort * port,SgChar * s,int64_t count)2698 int64_t Sg_Reads(SgPort *port, SgChar *s, int64_t count)
2699 {
2700 int64_t size;
2701 SG_PORT_LOCK_READ(port);
2702 size = Sg_ReadsUnsafe(port, s, count);
2703 SG_PORT_UNLOCK_READ(port);
2704 return size;
2705 }
Sg_ReadsUnsafe(SgPort * port,SgChar * s,int64_t count)2706 int64_t Sg_ReadsUnsafe(SgPort *port, SgChar *s, int64_t count)
2707 {
2708 if (SG_TEXTUAL_PORTP(port)) {
2709 int off = 0;
2710 int64_t r = 0;
2711 if (count == 0) return 0;
2712 if (SG_PORT_HAS_CHAR_AHEAD(port)) {
2713 s[off++] = SG_PORT_CHAR_AHEAD(port);
2714 SG_PORT_CHAR_AHEAD(port) = EOF;
2715 }
2716 if (count == off) return count;
2717 r = SG_PORT_VTABLE(port)->reads(port, s+off, count-off);
2718 return r+off;
2719 } else {
2720 Sg_Error(UC("textual port required, but got %S"), port);
2721 }
2722 return -1; /* dummy */
2723 }
2724
Sg_PutbUnsafe(SgPort * port,uint8_t b)2725 void Sg_PutbUnsafe(SgPort *port, uint8_t b)
2726 {
2727 reckless:
2728 if (SG_BINARY_PORTP(port)) {
2729 SG_PORT_VTABLE(port)->writeb(port, &b, 1);
2730 } else {
2731 /* write byte recklessly */
2732 if (SG_TRANSCODED_PORTP(port)) {
2733 port = SG_TPORT_PORT(port);
2734 goto reckless;
2735 }
2736 Sg_Error(UC("binary port required, but got %S"), port);
2737 }
2738 }
2739
Sg_PutbvUnsafe(SgPort * port,SgByteVector * bv)2740 void Sg_PutbvUnsafe(SgPort *port, SgByteVector *bv)
2741 {
2742 Sg_WritebUnsafe(port, SG_BVECTOR_ELEMENTS(bv), 0, SG_BVECTOR_SIZE(bv));
2743 }
2744
Sg_WritebUnsafe(SgPort * port,uint8_t * b,int64_t start,int64_t count)2745 void Sg_WritebUnsafe(SgPort *port, uint8_t *b, int64_t start, int64_t count)
2746 {
2747 reckless:
2748 if (SG_BINARY_PORTP(port)) {
2749 SG_PORT_VTABLE(port)->writeb(port,b+start,count);
2750 } else {
2751 /* write bytes recklessly */
2752 if (SG_TRANSCODED_PORTP(port)) {
2753 port = SG_TPORT_PORT(port);
2754 goto reckless;
2755 }
2756 Sg_Error(UC("binary port required, but got %S"), port);
2757 }
2758 }
2759
Sg_PutcUnsafe(SgPort * port,SgChar ch)2760 void Sg_PutcUnsafe(SgPort *port, SgChar ch)
2761 {
2762 if (SG_TEXTUAL_PORTP(port)) {
2763 SG_PORT_VTABLE(port)->writes(port, &ch, 1);
2764 } else {
2765 Sg_Error(UC("textual port required, but got %S"), port);
2766 }
2767 }
2768
2769 /* putz and putuz are only used in C */
Sg_PutzUnsafe(SgPort * port,const char * str)2770 void Sg_PutzUnsafe(SgPort *port, const char *str)
2771 {
2772 for (;*str;) Sg_PutcUnsafe(port, ((SgChar)*str++));
2773 }
2774
Sg_PutuzUnsafe(SgPort * port,const SgChar * str)2775 void Sg_PutuzUnsafe(SgPort *port, const SgChar *str)
2776 {
2777 for (;*str;) Sg_PutcUnsafe(port, *str++);
2778 }
2779
Sg_PutsUnsafe(SgPort * port,SgString * str)2780 void Sg_PutsUnsafe(SgPort *port, SgString *str)
2781 {
2782 if (SG_TEXTUAL_PORTP(port)) {
2783 SG_PORT_VTABLE(port)->writes(port, SG_STRING_VALUE(str),
2784 SG_STRING_SIZE(str));
2785 } else {
2786 Sg_Error(UC("textual port required, but got %S"), port);
2787 }
2788 }
2789
Sg_GetbUnsafe(SgPort * port)2790 int Sg_GetbUnsafe(SgPort *port)
2791 {
2792 reckless:
2793 if (SG_BINARY_PORTP(port)) {
2794 uint8_t b;
2795 int64_t count;
2796 if (SG_PORT_HAS_U8_AHEAD(port)) {
2797 b = SG_PORT_U8_AHEAD(port);
2798 SG_PORT_U8_AHEAD(port) = EOF;
2799 return b;
2800 }
2801 count = SG_PORT_VTABLE(port)->readb(port, &b, 1);
2802 if (count == 0) return EOF;
2803 return b;
2804 } else {
2805 /* read from byte recklessly */
2806 if (SG_TRANSCODED_PORTP(port)) {
2807 port = SG_TPORT_PORT(port);
2808 goto reckless;
2809 }
2810 Sg_Error(UC("binary port required, but got %S"), port);
2811 }
2812 return -1; /* dummy */
2813 }
2814
Sg_ReadbUnsafe(SgPort * port,uint8_t * buf,int64_t size)2815 int64_t Sg_ReadbUnsafe(SgPort *port, uint8_t *buf, int64_t size)
2816 {
2817 /* if it's reckless then, we also have chance to get custom port.
2818 see rfc/tls/port.scm
2819 */
2820 reckless:
2821 if (SG_BINARY_PORTP(port)) {
2822 int off = 0;
2823 int64_t count;
2824 if (size == 0) return 0;
2825 if (SG_PORT_HAS_U8_AHEAD(port)) {
2826 buf[off++] = SG_PORT_U8_AHEAD(port);
2827 SG_PORT_U8_AHEAD(port) = EOF;
2828 }
2829 if (size == off) return size;
2830 count = SG_PORT_VTABLE(port)->readb(port, buf+off, size-off);
2831 return count+off;
2832 } else {
2833 /* read from byte recklessly */
2834 if (SG_TRANSCODED_PORTP(port)) {
2835 port = SG_TPORT_PORT(port);
2836 goto reckless;
2837 } else {
2838 Sg_Error(UC("binary port required, but got %S"), port);
2839 }
2840 }
2841 return -1; /* dummy */
2842 }
2843
Sg_ReadbAllUnsafe(SgPort * port,uint8_t ** buf)2844 int64_t Sg_ReadbAllUnsafe(SgPort *port, uint8_t **buf)
2845 {
2846 reckless:
2847 if (SG_BINARY_PORTP(port)) {
2848 return SG_PORT_VTABLE(port)->readbAll(port, buf);
2849 } else {
2850 /* read from byte recklessly */
2851 if (SG_TRANSCODED_PORTP(port)) {
2852 port = SG_TPORT_PORT(port);
2853 goto reckless;
2854 } else {
2855 Sg_Error(UC("binary port required, but got %S"), port);
2856 }
2857 }
2858 return -1; /* dummy */
2859 }
2860
Sg_GetcUnsafe(SgPort * port)2861 SgChar Sg_GetcUnsafe(SgPort *port)
2862 {
2863 if (SG_TEXTUAL_PORTP(port)) {
2864 SgChar ch;
2865 int64_t count;
2866 if (SG_PORT_HAS_CHAR_AHEAD(port)) {
2867 ch = SG_PORT_CHAR_AHEAD(port);
2868 SG_PORT_CHAR_AHEAD(port) = EOF;
2869 return ch;
2870 }
2871 count = SG_PORT_VTABLE(port)->reads(port, &ch, 1);
2872 if (count == 0) return EOF;
2873 return ch;
2874 } else {
2875 Sg_Error(UC("textual port required, but got %S"), port);
2876 }
2877 return -1; /* dummy */
2878 }
2879
Sg_UngetcUnsafe(SgPort * port,SgChar ch)2880 void Sg_UngetcUnsafe(SgPort *port, SgChar ch)
2881 {
2882 if (SG_TEXTUAL_PORTP(port)) {
2883 if (SG_PORT_HAS_CHAR_AHEAD(port)) {
2884 Sg_Error(UC("unget buffer is full %S"), port);
2885 }
2886 SG_PORT_CHAR_AHEAD(port) = ch;
2887 } else {
2888 Sg_Error(UC("textual port required, but got %S"), port);
2889 }
2890 return;
2891 }
2892
Sg_PeekbUnsafe(SgPort * port)2893 int Sg_PeekbUnsafe(SgPort *port)
2894 {
2895 reckless:
2896 if (SG_BINARY_PORTP(port)) {
2897 uint8_t b;
2898 int64_t count;
2899 if (SG_PORT_HAS_U8_AHEAD(port)) {
2900 return SG_PORT_U8_AHEAD(port);
2901 }
2902 count = SG_PORT_VTABLE(port)->readb(port, &b, 1);
2903 if (count == 0) return EOF;
2904 SG_PORT_U8_AHEAD(port) = b;
2905 return b;
2906 } else {
2907 /* read from byte recklessly */
2908 if (SG_TRANSCODED_PORTP(port)) {
2909 port = SG_TPORT_PORT(port);
2910 goto reckless;
2911 }
2912 Sg_Error(UC("binary port required, but got %S"), port);
2913 }
2914 return -1; /* dummy */
2915
2916 }
2917
Sg_PeekcUnsafe(SgPort * port)2918 SgChar Sg_PeekcUnsafe(SgPort *port)
2919 {
2920 if (SG_TEXTUAL_PORTP(port)) {
2921 SgChar ch;
2922 int64_t count;
2923 if (SG_PORT_HAS_CHAR_AHEAD(port)) {
2924 return SG_PORT_CHAR_AHEAD(port);
2925 }
2926 count = SG_PORT_VTABLE(port)->reads(port, &ch, 1);
2927 if (count == 0) return EOF;
2928 SG_PORT_CHAR_AHEAD(port) = ch;
2929 return ch;
2930 } else {
2931 Sg_Error(UC("textual port required, but got %S"), port);
2932 }
2933 return -1; /* dummy */
2934 }
2935
Sg_ReadLine(SgPort * port,SgEolStyle eolStyle)2936 SgObject Sg_ReadLine(SgPort *port, SgEolStyle eolStyle)
2937 {
2938 volatile SgObject r = SG_UNDEF;
2939 if (!SG_TEXTUAL_PORTP(port)) {
2940 Sg_Error(UC("textual port required, but got %S"), port);
2941 }
2942 SG_PORT_LOCK_READ(port);
2943 SG_UNWIND_PROTECT{
2944 SgChar c = Sg_PeekcUnsafe(port);
2945 if (c == EOF) r = SG_EOF;
2946 else {
2947 SgStringPort sout;
2948 SgPort *out;
2949 Sg_InitStringOutputPort(&sout, 512);
2950 out = SG_PORT(&sout);
2951 while (1) {
2952 c = Sg_GetcUnsafe(port);
2953 if (c == EOF) break;
2954 else {
2955 int eol = FALSE;
2956 switch (eolStyle) {
2957 case E_NONE: /* check \n \r and \r\n */
2958 /* todo should we also check NEL, LS and CRNEL? */
2959 if (c == '\n') {
2960 eol = TRUE;
2961 break;
2962 }
2963 if (c == '\r') {
2964 eol = TRUE;
2965 if ('\n' == Sg_PeekcUnsafe(port)) {
2966 Sg_GetcUnsafe(port);
2967 }
2968 break;
2969 }
2970 default:
2971 /* TODO multibyte EOL */
2972 if (c == (SgChar)eolStyle) {
2973 eol = TRUE;
2974 }
2975 }
2976 if (eol) break;
2977 Sg_PutcUnsafe(out, c);
2978 }
2979 }
2980 SG_PORT_UNLOCK_READ(port);
2981 r = Sg_GetStringFromStringPort(&sout);
2982 SG_CLEAN_STRING_PORT(&sout);
2983 }
2984 } SG_WHEN_ERROR {
2985 SG_PORT_UNLOCK_READ(port);
2986 SG_NEXT_HANDLER;
2987 } SG_END_PROTECT;
2988 return r;
2989 }
2990
readb_until(SgPort * port,SgByteVector * eol)2991 static SgObject readb_until(SgPort *port, SgByteVector *eol)
2992 {
2993 SgPort *out;
2994 SgBytePort bp;
2995 SgObject r;
2996 /* use something the same as buffer ports (256) */
2997 uint8_t tmp[DEFAULT_BUFFER_SIZE], *buf;
2998 long size = SG_BVECTOR_SIZE(eol);
2999
3000 /* pre-check */
3001 if (Sg_PeekbUnsafe(port) == EOF) return SG_EOF;
3002
3003 /* setup buffer */
3004 if (SG_BVECTOR_SIZE(eol) > DEFAULT_BUFFER_SIZE) {
3005 buf = SG_NEW_ATOMIC2(uint8_t *, SG_BVECTOR_SIZE(eol));
3006 } else {
3007 buf = tmp;
3008 }
3009 Sg_InitByteArrayOutputPort(&bp, 256);
3010 out = SG_PORT(&bp);
3011 while (1) {
3012 int b = Sg_GetbUnsafe(port);
3013 if (b == EOF) {
3014 break;
3015 } else if ((uint8_t)b == SG_BVECTOR_ELEMENT(eol, 0)) {
3016 /* inner loop */
3017 long i, offset = 0;
3018 buf[0] = (uint8_t)b;
3019 for (i = 1; i < size; i++) {
3020 b = Sg_GetbUnsafe(port);
3021
3022 if (b == EOF) break;
3023
3024 buf[i] = (uint8_t)b;
3025 if (b != SG_BVECTOR_ELEMENT(eol, i)) {
3026 offset = 1;
3027 break;
3028 }
3029 }
3030 if (i == size) break;
3031 Sg_WritebUnsafe(out, buf, 0, i + offset);
3032 } else {
3033 Sg_PutbUnsafe(out, (uint8_t)b);
3034 }
3035 }
3036
3037 r = Sg_GetByteVectorFromBinaryPort(&bp);
3038 SG_CLEAN_BYTE_PORT(&bp);
3039 return r;
3040 }
3041
Sg_ReadbUntil(SgPort * port,SgByteVector * eol)3042 SgObject Sg_ReadbUntil(SgPort *port, SgByteVector *eol)
3043 {
3044 SgObject r = SG_UNDEF;
3045 SG_PORT_LOCK_READ(port);
3046 SAFE_READ_CALL(port, r = readb_until(port, eol));
3047 SG_PORT_UNLOCK_READ(port);
3048 return r;
3049 }
3050
Sg_HasPortPosition(SgPort * port)3051 int Sg_HasPortPosition(SgPort *port)
3052 {
3053 /* a bit awkward solution but this saves me from lots of crap*/
3054 if (SG_CUSTOM_PORTP(port))
3055 return SG_PORT_VTABLE(port)->portPosition != NULL &&
3056 SG_PROCEDUREP(SG_CUSTOM_PORT(port)->getPosition);
3057 return SG_PORT_VTABLE(port)->portPosition != NULL;
3058 }
3059
Sg_HasSetPortPosition(SgPort * port)3060 int Sg_HasSetPortPosition(SgPort *port)
3061 {
3062 /* a bit awkward solution */
3063 if (SG_CUSTOM_PORTP(port))
3064 return SG_PORT_VTABLE(port)->setPortPosition != NULL &&
3065 SG_PROCEDUREP(SG_CUSTOM_PORT(port)->setPosition);
3066 return SG_PORT_VTABLE(port)->setPortPosition != NULL;
3067 }
3068
Sg_PortPosition(SgPort * port)3069 int64_t Sg_PortPosition(SgPort *port)
3070 {
3071 if (!SG_PORT_VTABLE(port)->portPosition) {
3072 Sg_Error(UC("Given port does not support port-position: %S"), port);
3073 }
3074 return SG_PORT_VTABLE(port)->portPosition(port);
3075 }
3076
Sg_SetPortPosition(SgPort * port,int64_t offset,SgWhence whence)3077 void Sg_SetPortPosition(SgPort *port, int64_t offset, SgWhence whence)
3078 {
3079 if (!SG_PORT_VTABLE(port)->setPortPosition) {
3080 Sg_Error(UC("Given port does not support set-port-position! %S"), port);
3081 }
3082 SG_PORT_VTABLE(port)->setPortPosition(port, offset, whence);
3083 /* reset peek buffer */
3084 SG_PORT_CHAR_AHEAD(port) = EOF;
3085 }
3086
Sg_LineNo(SgPort * port)3087 int64_t Sg_LineNo(SgPort *port)
3088 {
3089 if (SG_BUFFERED_PORTP(port)) {
3090 return Sg_LineNo(SG_BUFFERED_PORT(port)->src);
3091 } else {
3092 return port->lineNo;
3093 }
3094 }
3095
Sg_FileName(SgPort * port)3096 SgObject Sg_FileName(SgPort *port)
3097 {
3098 SgFile *file = SG_FILE(Sg_PortFile(port));
3099
3100 if (file != NULL) {
3101 return Sg_String(file->name);
3102 }
3103 return SG_FALSE;
3104 }
3105
Sg_PortFile(SgPort * port)3106 SgObject Sg_PortFile(SgPort *port)
3107 {
3108 SgFile *file = NULL;
3109
3110 if (SG_FILE_PORTP(port)) {
3111 file = SG_FILE_PORT(port)->file;
3112 } else if (SG_TRANSCODED_PORTP(port)) {
3113 return Sg_PortFile(SG_TPORT_PORT(port));
3114 } else if (SG_BUFFERED_PORTP(port)) {
3115 return Sg_PortFile(SG_BUFFERED_PORT(port)->src);
3116 }
3117 return file;
3118 }
3119
Sg_PortTranscoder(SgObject port)3120 SgObject Sg_PortTranscoder(SgObject port)
3121 {
3122 if (SG_TRANSCODERP(SG_PORT(port)->transcoder)) {
3123 return SG_PORT(port)->transcoder;
3124 }
3125 return SG_FALSE;
3126 }
3127
Sg_ReadOncePortP(SgPort * port)3128 int Sg_ReadOncePortP(SgPort *port)
3129 {
3130 if (SG_BUFFERED_PORTP(port)) {
3131 return Sg_ReadOncePortP(SG_BUFFERED_PORT(port)->src);
3132 } else if (SG_TRANSCODED_PORTP(port)) {
3133 return Sg_ReadOncePortP(SG_TPORT_PORT(port));
3134 } else {
3135 return SG_ISA(port, SG_CLASS_READ_ONCE_PORT);
3136 }
3137 }
3138
Sg_LockPortResource(SgPort * port,SgPortLockType lockType)3139 int Sg_LockPortResource(SgPort *port, SgPortLockType lockType)
3140 {
3141 if (SG_PORT_VTABLE(port)->lockPort) {
3142 return SG_PORT_VTABLE(port)->lockPort(port, lockType);
3143 } else {
3144 /* default TRUE */
3145 return TRUE;
3146 }
3147 }
3148
Sg_UnlockPortResouce(SgPort * port)3149 int Sg_UnlockPortResouce(SgPort *port)
3150 {
3151 if (SG_PORT_VTABLE(port)->unlockPort) {
3152 return SG_PORT_VTABLE(port)->unlockPort(port);
3153 } else {
3154 /* default TRUE */
3155 return TRUE;
3156 }
3157 }
3158
Sg_PortReady(SgPort * port)3159 int Sg_PortReady(SgPort *port)
3160 {
3161 /* this prevents transcoded port's underlying port (pseudo closed) */
3162 /* if (Sg_PortClosedP(port)) return FALSE; */
3163 if (SG_PORT_VTABLE(port)->ready) {
3164 return SG_PORT_VTABLE(port)->ready(port);
3165 }
3166 return TRUE;
3167 }
3168
Sg_UTF16ConsolePortP(SgPort * port)3169 int Sg_UTF16ConsolePortP(SgPort *port)
3170 {
3171 if (SG_BUFFERED_PORTP(port)) {
3172 return Sg_UTF16ConsolePortP(SG_BUFFERED_PORT_SRC(port));
3173 } else if (SG_FILE_PORTP(port)) {
3174 return Sg_IsUTF16Console(SG_FILE_PORT(port)->file);
3175 }
3176 return FALSE;
3177 }
3178
3179 /* standard ports */
3180 static SgObject sg_stdin = SG_UNBOUND;
3181 static SgObject sg_stdout = SG_UNBOUND;
3182 static SgObject sg_stderr = SG_UNBOUND;
3183
Sg_StandardOutputPort()3184 SgObject Sg_StandardOutputPort()
3185 {
3186 return SG_OBJ(sg_stdout);
3187 }
3188
Sg_StandardInputPort()3189 SgObject Sg_StandardInputPort()
3190 {
3191 return SG_OBJ(sg_stdin);
3192 }
3193
Sg_StandardErrorPort()3194 SgObject Sg_StandardErrorPort()
3195 {
3196 return SG_OBJ(sg_stderr);
3197 }
3198
Sg_DefaultPortPrinter(SgObject obj,SgPort * port,SgWriteContext * ctx)3199 void Sg_DefaultPortPrinter(SgObject obj, SgPort *port, SgWriteContext *ctx)
3200 {
3201 port_print(obj, port, ctx);
3202 }
3203
Sg__InitPort()3204 void Sg__InitPort()
3205 {
3206 SgVM *vm = Sg_VM();
3207 SgLibrary *clib = Sg_FindLibrary(SG_INTERN("(sagittarius clos)"), TRUE);
3208 Sg_InitMutex(&active_buffered_ports.lock, FALSE);
3209 active_buffered_ports.ports
3210 = SG_WEAK_VECTOR(Sg_MakeWeakVector(PORT_VECTOR_SIZE));
3211
3212 sg_stdin = Sg_MakeFileBinaryInputPort(Sg_StandardIn(),
3213 SG_BUFFER_MODE_NONE);
3214 sg_stdout = Sg_MakeFileBinaryOutputPort(Sg_StandardOut(),
3215 SG_BUFFER_MODE_LINE);
3216 sg_stderr = Sg_MakeFileBinaryOutputPort(Sg_StandardError(),
3217 SG_BUFFER_MODE_NONE);
3218
3219 vm->currentInputPort = Sg_MakeTranscodedPort(sg_stdin,
3220 Sg_IsUTF16Console(Sg_StandardIn())
3221 ? Sg_MakeNativeConsoleTranscoder()
3222 : Sg_MakeNativeTranscoder());
3223 vm->currentOutputPort = Sg_MakeTranscodedPort(sg_stdout,
3224 Sg_IsUTF16Console(Sg_StandardOut())
3225 ? Sg_MakeNativeConsoleTranscoder()
3226 : Sg_MakeNativeTranscoder());
3227 vm->currentErrorPort = Sg_MakeTranscodedPort(sg_stderr,
3228 Sg_IsUTF16Console(Sg_StandardError())
3229 ? Sg_MakeNativeConsoleTranscoder()
3230 : Sg_MakeNativeTranscoder());
3231 vm->logPort = vm->currentErrorPort;
3232 /* CLOS */
3233 #define BINIT(cl, nam, slots) Sg_InitStaticClass(cl, UC(nam), clib, slots, 0)
3234 BINIT(SG_CLASS_PORT, "<port>", NULL);
3235 BINIT(SG_CLASS_FILE_PORT, "<file-port>", NULL);
3236 BINIT(SG_CLASS_BYTE_PORT, "<byte-port>", NULL);
3237 BINIT(SG_CLASS_STRING_PORT, "<string-port>", NULL);
3238 BINIT(SG_CLASS_CUSTOM_PORT, "<custom-port>", custom_slots);
3239 BINIT(SG_CLASS_CUSTOM_BINARY_PORT, "<custom-binary-port>", NULL);
3240 BINIT(SG_CLASS_CUSTOM_TEXTUAL_PORT, "<custom-textual-port>", NULL);
3241 BINIT(SG_CLASS_BUFFERED_PORT, "<buffered-port>", NULL);
3242 BINIT(SG_CLASS_TRANSCODED_PORT, "<transcoded-port>", NULL);
3243 /* dummy but needed */
3244 BINIT(SG_CLASS_INPUT_PORT, "<input-port>", NULL);
3245 BINIT(SG_CLASS_OUTPUT_PORT, "<output-port>", NULL);
3246 BINIT(SG_CLASS_BIDIRECTIONAL_PORT, "<bidirectional-port>", NULL);
3247 BINIT(SG_CLASS_READ_ONCE_PORT, "<read-once-port>", NULL);
3248
3249 #define KEYWORD(k) Sg_MakeKeyword(SG_MAKE_STRING(k))
3250 SG_KEYWORD_ID = KEYWORD("id");
3251 SG_KEYWORD_POSITION = KEYWORD("position");
3252 SG_KEYWORD_SET_POSITION = KEYWORD("set-position");
3253 SG_KEYWORD_READ = KEYWORD("read");
3254 SG_KEYWORD_WRITE = KEYWORD("write");
3255 SG_KEYWORD_READY = KEYWORD("ready");
3256 SG_KEYWORD_FLUSH = KEYWORD("flush");
3257 SG_KEYWORD_CLOSE = KEYWORD("close");
3258
3259 }
3260
3261 /*
3262 end of file
3263 Local Variables:
3264 coding: utf-8-unix
3265 End:
3266 */
3267