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