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.
52 # The line terminator '\n' and '\r\n', if any, is removed in each line.
55 # var txt = "Hello\n\nWorld\n"
56 # var i = new StringIStream(txt)
57 # assert i.read_line == "Hello"
58 # assert i.read_line == ""
59 # assert i.read_line == "World"
63 # Only LINE FEED (`\n`), CARRIAGE RETURN & LINE FEED (`\r\n`), and
64 # the end or file (EOF) is considered to delimit the end of lines.
65 # CARRIAGE RETURN (`\r`) alone is not used for the end of line.
68 # var txt2 = "Hello\r\n\n\rWorld"
69 # var i2 = new StringIStream(txt2)
70 # assert i2.read_line == "Hello"
71 # assert i2.read_line == ""
72 # assert i2.read_line == "\rWorld"
76 # NOTE: Use `append_line_to` if the line terminator needs to be preserved.
80 var s
= new FlatBuffer
85 # Read all the lines until the eof.
87 # The line terminator '\n' and `\r\n` is removed in each line,
90 # var txt = "Hello\n\nWorld\n"
91 # var i = new StringIStream(txt)
92 # assert i.read_lines == ["Hello", "", "World"]
95 # This method is more efficient that splitting
96 # the result of `read_all`.
98 # NOTE: SEE `read_line` for details.
99 fun read_lines
: Array[String]
101 var res
= new Array[String]
108 # Read all the stream until the eof.
110 # The content of the file is returned verbatim.
113 # var txt = "Hello\n\nWorld\n"
114 # var i = new StringIStream(txt)
115 # assert i.read_all == txt
119 var s
= new FlatBuffer
122 if c
>= 0 then s
.add
(c
.ascii
)
127 # Read a string until the end of the line and append it to `s`.
129 # Unlike `read_line` and other related methods,
130 # the line terminator '\n', if any, is preserved in each line.
131 # Use the method `Text::chomp` to safely remove it.
134 # var txt = "Hello\n\nWorld\n"
135 # var i = new StringIStream(txt)
136 # var b = new FlatBuffer
137 # i.append_line_to(b)
138 # assert b == "Hello\n"
139 # i.append_line_to(b)
140 # assert b == "Hello\n\n"
141 # i.append_line_to(b)
146 # If `\n` is not present at the end of the result, it means that
147 # a non-eol terminated last line was returned.
150 # var i2 = new StringIStream("hello")
152 # var b2 = new FlatBuffer
153 # i2.append_line_to(b2)
154 # assert b2 == "hello"
158 # NOTE: The single character LINE FEED (`\n`) delimits the end of lines.
159 # Therefore CARRIAGE RETURN & LINE FEED (`\r\n`) is also recognized.
160 fun append_line_to
(s
: Buffer)
169 if c
== '\n' then return
174 # Is there something to read.
175 # This function returns 'false' if there is something to read.
176 fun eof
: Bool is abstract
179 # IStream capable of declaring if readable without blocking
180 interface PollableIStream
183 # Is there something to read? (without blocking)
184 fun poll_in
: Bool is abstract
188 # Abstract output stream
192 fun write
(s
: Text) is abstract
194 # Can the stream be used to write
195 fun is_writable
: Bool is abstract
198 # Things that can be efficienlty writen to a OStream
200 # The point of this interface it to allow is instance to be efficenty
201 # writen into a OStream without having to allocate a big String object
203 # ready-to-save documents usually provide this interface.
205 # Write itself to a `stream`
206 # The specific logic it let to the concrete subclasses
207 fun write_to
(stream
: OStream) is abstract
209 # Like `write_to` but return a new String (may be quite large)
211 # This funtionnality is anectodical, since the point
212 # of streamable object to to be efficienlty written to a
213 # stream without having to allocate and concatenate strings
214 fun write_to_string
: String
216 var stream
= new StringOStream
224 redef fun write_to
(stream
) do stream
.write
(self)
227 # Input streams with a buffer
228 abstract class BufferedIStream
232 if _buffer_pos
>= _buffer
.length
then
235 if _buffer_pos
>= _buffer
.length
then
238 var c
= _buffer
.chars
[_buffer_pos
]
245 if _buffer
.length
== _buffer_pos
then
251 if _buffer_pos
+ i
>= _buffer
.length
then
252 var from
= _buffer_pos
253 _buffer_pos
= _buffer
.length
254 return _buffer
.substring_from
(from
).to_s
257 return _buffer
.substring
(_buffer_pos
- i
, i
).to_s
262 var s
= new FlatBuffer
265 var k
= _buffer
.length
276 redef fun append_line_to
(s
)
279 # First phase: look for a '\n'
281 while i
< _buffer
.length
and _buffer
.chars
[i
] != '\n' do i
+= 1
284 if i
< _buffer
.length
then
285 assert _buffer
.chars
[i
] == '\n'
292 # if there is something to append
293 if i
> _buffer_pos
then
294 # Enlarge the string (if needed)
295 s
.enlarge
(s
.length
+ i
- _buffer_pos
)
297 # Copy from the buffer to the string
300 s
.add
(_buffer
.chars
[j
])
314 if end_reached
then return
322 if _buffer_pos
< _buffer
.length
then return false
323 if end_reached
then return true
325 return _buffer_pos
>= _buffer
.length
and end_reached
329 private var buffer
: nullable FlatBuffer = null
331 # The current position in the buffer
332 private var buffer_pos
: Int = 0
335 protected fun fill_buffer
is abstract
337 # Is the last fill_buffer reach the end
338 protected fun end_reached
: Bool is abstract
340 # Allocate a `_buffer` for a given `capacity`.
341 protected fun prepare_buffer
(capacity
: Int)
343 _buffer
= new FlatBuffer.with_capacity
(capacity
)
344 _buffer_pos
= 0 # need to read
348 # An Input/Output Stream
354 ##############################################################"
356 # A File Descriptor Stream.
357 abstract class FDStream
362 redef fun close
do native_close
(fd
)
364 private fun native_close
(i
: Int): Int is extern "stream_FDStream_FDStream_native_close_1"
365 private fun native_read_char
(i
: Int): Int is extern "stream_FDStream_FDStream_native_read_char_1"
366 private fun native_read
(i
: Int, buf
: NativeString, len
: Int): Int is extern "stream_FDStream_FDStream_native_read_3"
367 private fun native_write
(i
: Int, buf
: NativeString, len
: Int): Int is extern "stream_FDStream_FDStream_native_write_3"
368 private fun native_write_char
(i
: Int, c
: Char): Int is extern "stream_FDStream_FDStream_native_write_char_2"
371 # An Input File Descriptor Stream.
375 redef var eof
: Bool = false
379 var nb
= native_read_char
(fd
)
380 if nb
== -1 then eof
= true
385 # An Output File Descriptor Stream.
389 redef var is_writable
= true
393 var nb
= native_write
(fd
, s
.to_cstring
, s
.length
)
394 if nb
< s
.length
then is_writable
= false
398 # An Input/Output File Descriptor Stream.
405 redef interface Object
406 # returns first available stream to read or write to
407 # return null on interruption (possibly a signal)
408 protected fun poll
( streams
: Sequence[FDStream] ) : nullable FDStream
410 var in_fds
= new Array[Int]
411 var out_fds
= new Array[Int]
412 var fd_to_stream
= new HashMap[Int,FDStream]
415 if s
isa FDIStream then in_fds
.add
( fd
)
416 if s
isa FDOStream then out_fds
.add
( fd
)
421 var polled_fd
= intern_poll
( in_fds
, out_fds
)
423 if polled_fd
== null then
426 return fd_to_stream
[polled_fd
]
430 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) `{
431 int in_len, out_len, total_len;
432 struct pollfd *c_fds;
435 int first_polled_fd = -1;
438 in_len = Array_of_Int_length( in_fds );
439 out_len = Array_of_Int_length( out_fds );
440 total_len = in_len + out_len;
441 c_fds = malloc( sizeof(struct pollfd) * total_len );
444 for ( i=0; i<in_len; i ++ ) {
446 fd = Array_of_Int__index( in_fds, i );
449 c_fds[i].events = POLLIN;
453 for ( i=0; i<out_len; i ++ ) {
455 fd = Array_of_Int__index( out_fds, i );
458 c_fds[i].events = POLLOUT;
461 /* poll all fds, unlimited timeout */
462 result = poll( c_fds, total_len, -1 );
465 /* analyse results */
466 for ( i=0; i<total_len; i++ )
467 if ( c_fds[i].revents & c_fds[i].events || /* awaited event */
468 c_fds[i].revents & POLLHUP ) /* closed */
470 first_polled_fd = c_fds[i].fd;
474 return Int_as_nullable( first_polled_fd );
476 else if ( result < 0 )
477 fprintf( stderr, "Error in Stream:poll: %s\n", strerror( errno ) );
483 # Stream to a String.
485 # Mainly used for compatibility with OStream type and tests.
489 private var content
= new Array[String]
490 redef fun to_s
do return content
.to_s
491 redef fun is_writable
do return not closed
495 content
.add
(str
.to_s
)
498 # Is the stream closed?
499 protected var closed
= false
501 redef fun close
do closed
= true
504 # Stream from a String.
506 # Mainly used for compatibility with IStream type and tests.
510 # The string to read from.
513 # The current position in the string.
514 private var cursor
: Int = 0
516 redef fun read_char
do
517 if cursor
< source
.length
then
518 var c
= source
[cursor
].ascii
531 redef fun eof
do return cursor
>= source
.length