1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # This file is free software, which comes along with NIT. This software is
4 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
5 # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
6 # PARTICULAR PURPOSE. You can modify it is you want, provided this header
7 # is kept unaltered, and a notification of the changes is added.
8 # You are allowed to redistribute it and sell it, alone or is a part of
11 # Input and output streams of characters
24 # Abstract stream class
30 # Abstract input streams
33 # Read a character. Return its ASCII value, -1 on EOF or timeout
34 fun read_char
: Int is abstract
36 # Read at most i bytes
37 fun read
(i
: Int): String
39 var s
= new FlatBuffer.with_capacity
(i
)
40 while i
> 0 and not eof
do
50 # Read a string until the end of the line.
54 var s
= new FlatBuffer
59 # Read all the stream until the eof.
62 var s
= new FlatBuffer
65 if c
>= 0 then s
.add
(c
.ascii
)
70 # Read a string until the end of the line and append it to `s`.
71 fun append_line_to
(s
: Buffer)
80 if c
== '\n' then return
85 # Is there something to read.
86 # This function returns 'false' if there is something to read.
87 fun eof
: Bool is abstract
90 # IStream capable of declaring if readable without blocking
91 interface PollableIStream
94 # Is there something to read? (without blocking)
95 fun poll_in
: Bool is abstract
99 # Abstract output stream
103 fun write
(s
: Text) is abstract
105 # Can the stream be used to write
106 fun is_writable
: Bool is abstract
109 # Things that can be efficienlty writen to a OStream
111 # The point of this interface it to allow is instance to be efficenty
112 # writen into a OStream without having to allocate a big String object
114 # ready-to-save documents usually provide this interface.
116 # Write itself to a `stream`
117 # The specific logic it let to the concrete subclasses
118 fun write_to
(stream
: OStream) is abstract
120 # Like `write_to` but return a new String (may be quite large)
122 # This funtionnality is anectodical, since the point
123 # of streamable object to to be efficienlty written to a
124 # stream without having to allocate and concatenate strings
125 fun write_to_string
: String
127 var stream
= new StringOStream
135 redef fun write_to
(stream
) do stream
.write
(self)
144 redef fun write_to
(s
) do s
.write
(str
)
149 redef fun write_to
(s
)
151 if left
!= null then left
.write_to
(s
)
152 if right
!= null then right
.write_to
(s
)
156 redef class RopeString
158 redef fun write_to
(s
) do root
.write_to
(s
)
161 # Input streams with a buffer
162 abstract class BufferedIStream
167 if _buffer_pos
>= _buffer
.length
then
170 if _buffer_pos
>= _buffer
.length
then
173 var c
= _buffer
.chars
[_buffer_pos
]
180 if _buffer
.length
== _buffer_pos
then
187 if _buffer_pos
+ i
>= _buffer
.length
then
188 var from
= _buffer_pos
189 _buffer_pos
= _buffer
.length
190 return _buffer
.substring_from
(from
).to_s
193 return _buffer
.substring
(_buffer_pos
- i
, i
).to_s
198 var s
= new FlatBuffer
201 var k
= _buffer
.length
212 redef fun append_line_to
(s
)
215 # First phase: look for a '\n'
217 while i
< _buffer
.length
and _buffer
.chars
[i
] != '\n' do i
+= 1
219 # if there is something to append
220 if i
> _buffer_pos
then
221 # Enlarge the string (if needed)
222 s
.enlarge
(s
.length
+ i
- _buffer_pos
)
224 # Copy from the buffer to the string
227 s
.add
(_buffer
.chars
[j
])
232 if i
< _buffer
.length
then
233 # so \n is in _buffer[i]
234 _buffer_pos
= i
+ 1 # skip \n
248 redef fun eof
do return _buffer_pos
>= _buffer
.length
and end_reached
251 private var buffer
: nullable FlatBuffer = null
253 # The current position in the buffer
254 private var buffer_pos
: Int = 0
257 protected fun fill_buffer
is abstract
259 # Is the last fill_buffer reach the end
260 protected fun end_reached
: Bool is abstract
262 # Allocate a `_buffer` for a given `capacity`.
263 protected fun prepare_buffer
(capacity
: Int)
265 _buffer
= new FlatBuffer.with_capacity
(capacity
)
266 _buffer_pos
= 0 # need to read
275 ##############################################################"
277 abstract class FDStream
282 redef fun close
do native_close
(fd
)
284 private fun native_close
(i
: Int): Int is extern "stream_FDStream_FDStream_native_close_1"
285 private fun native_read_char
(i
: Int): Int is extern "stream_FDStream_FDStream_native_read_char_1"
286 private fun native_read
(i
: Int, buf
: NativeString, len
: Int): Int is extern "stream_FDStream_FDStream_native_read_3"
287 private fun native_write
(i
: Int, buf
: NativeString, len
: Int): Int is extern "stream_FDStream_FDStream_native_write_3"
288 private fun native_write_char
(i
: Int, c
: Char): Int is extern "stream_FDStream_FDStream_native_write_char_2"
294 redef var eof
: Bool = false
298 var nb
= native_read_char
(fd
)
299 if nb
== -1 then eof
= true
307 redef var is_writable
= true
311 var nb
= native_write
(fd
, s
.to_cstring
, s
.length
)
312 if nb
< s
.length
then is_writable
= false
322 redef interface Object
323 # returns first available stream to read or write to
324 # return null on interruption (possibly a signal)
325 protected fun poll
( streams
: Sequence[FDStream] ) : nullable FDStream
327 var in_fds
= new Array[Int]
328 var out_fds
= new Array[Int]
329 var fd_to_stream
= new HashMap[Int,FDStream]
332 if s
isa FDIStream then in_fds
.add
( fd
)
333 if s
isa FDOStream then out_fds
.add
( fd
)
338 var polled_fd
= intern_poll
( in_fds
, out_fds
)
340 if polled_fd
== null then
343 return fd_to_stream
[polled_fd
]
347 private fun intern_poll
(in_fds
: Array[Int], out_fds
: Array[Int]) : nullable Int is extern import Array[Int].length
, Array[Int].[], Int.as(nullable Int) `{
348 int in_len, out_len, total_len;
349 struct pollfd *c_fds;
352 int first_polled_fd = -1;
355 in_len = Array_of_Int_length( in_fds );
356 out_len = Array_of_Int_length( out_fds );
357 total_len = in_len + out_len;
358 c_fds = malloc( sizeof(struct pollfd) * total_len );
361 for ( i=0; i<in_len; i ++ ) {
363 fd = Array_of_Int__index( in_fds, i );
366 c_fds[i].events = POLLIN;
370 for ( i=0; i<out_len; i ++ ) {
372 fd = Array_of_Int__index( out_fds, i );
375 c_fds[i].events = POLLOUT;
378 /* poll all fds, unlimited timeout */
379 result = poll( c_fds, total_len, -1 );
382 /* analyse results */
383 for ( i=0; i<total_len; i++ )
384 if ( c_fds[i].revents & c_fds[i].events || /* awaited event */
385 c_fds[i].revents & POLLHUP ) /* closed */
387 first_polled_fd = c_fds[i].fd;
391 return Int_as_nullable( first_polled_fd );
393 else if ( result < 0 )
394 fprintf( stderr, "Error in Stream:poll: %s\n", strerror( errno ) );
400 # Stream to a String.
402 # Mainly used for compatibility with OStream type and tests.
406 private var content
= new Array[String]
407 redef fun to_s
do return content
.to_s
408 redef fun is_writable
do return not closed
412 content
.add
(str
.to_s
)
415 protected var closed
= false
416 redef fun close
do closed
= true
419 # Stream from a String.
421 # Mainly used for compatibility with IStream type and tests.
425 # The string to read from.
428 # The current position in the string.
429 private var cursor
: Int = 0
431 redef fun read_char
do
432 if cursor
< source
.length
then
433 var c
= source
[cursor
].ascii
446 redef fun eof
do return cursor
>= source
.length