1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2004-2008 Jean Privat <jean@pryen.org>
5 # This file is free software, which comes along with NIT. This software is
6 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
7 # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
8 # PARTICULAR PURPOSE. You can modify it is you want, provided this header
9 # is kept unaltered, and a notification of the changes is added.
10 # You are allowed to redistribute it and sell it, alone or is a part of
13 # Input and output streams of characters
25 # Abstract stream class
31 # Abstract input streams
34 # Read a character. Return its ASCII value, -1 on EOF or timeout
35 fun read_char
: Int is abstract
37 # Read at most i bytes
38 fun read
(i
: Int): String
40 var s
= new Buffer.with_capacity
(i
)
41 while i
> 0 and not eof
do
51 # Read a string until the end of the line.
60 # Read all the stream until the eof.
66 if c
>= 0 then s
.add
(c
.ascii
)
71 # Read a string until the end of the line and append it to `s`.
72 fun append_line_to
(s
: Buffer)
81 if c
== '\n' then return
86 # Is there something to read.
87 # This function returns 'false' if there is something to read.
88 fun eof
: Bool is abstract
91 # Abstract output stream
95 fun write
(s
: String) is abstract
97 # Can the stream be used to write
98 fun is_writable
: Bool is abstract
101 # Things that can be efficienlty writen to a OStream
103 # The point of this interface it to allow is instance to be efficenty
104 # writen into a OStream without having to allocate a big String object
106 # Write itself to a `stream`
107 # The specific logic it let to the concrete subclasses
108 fun write_to
(stream
: OStream) is abstract
113 redef fun write_to
(stream
) do stream
.write
(self)
116 # Input streams with a buffer
117 abstract class BufferedIStream
122 if _buffer_pos
>= _buffer
.length
then
125 if _buffer_pos
>= _buffer
.length
then
128 var c
= _buffer
.chars
[_buffer_pos
]
135 var s
= new Buffer.with_capacity
(i
)
137 var k
= _buffer
.length
141 if eof
then return s
.to_s
145 while j
< k
and i
> 0 do
146 s
.add
(_buffer
.chars
[j
])
160 var k
= _buffer
.length
162 s
.add
(_buffer
.chars
[j
])
171 redef fun append_line_to
(s
)
174 # First phase: look for a '\n'
176 while i
< _buffer
.length
and _buffer
.chars
[i
] != '\n' do i
+= 1
178 # if there is something to append
179 if i
> _buffer_pos
then
180 # Enlarge the string (if needed)
181 s
.enlarge
(s
.length
+ i
- _buffer_pos
)
183 # Copy from the buffer to the string
186 s
.add
(_buffer
.chars
[j
])
191 if i
< _buffer
.length
then
192 # so \n is in _buffer[i]
193 _buffer_pos
= i
+ 1 # skip \n
207 redef fun eof
do return _buffer_pos
>= _buffer
.length
and end_reached
210 var _buffer
: nullable Buffer = null
212 # The current position in the buffer
213 var _buffer_pos
: Int = 0
216 protected fun fill_buffer
is abstract
218 # Is the last fill_buffer reach the end
219 protected fun end_reached
: Bool is abstract
221 # Allocate a `_buffer` for a given `capacity`.
222 protected fun prepare_buffer
(capacity
: Int)
224 _buffer
= new Buffer.with_capacity
(capacity
)
225 _buffer_pos
= 0 # need to read
234 ##############################################################"
236 abstract class FDStream
241 redef fun close
do native_close
(fd
)
243 private fun native_close
(i
: Int): Int is extern "stream_FDStream_FDStream_native_close_1"
244 private fun native_read_char
(i
: Int): Int is extern "stream_FDStream_FDStream_native_read_char_1"
245 private fun native_read
(i
: Int, buf
: NativeString, len
: Int): Int is extern "stream_FDStream_FDStream_native_read_3"
246 private fun native_write
(i
: Int, buf
: NativeString, len
: Int): Int is extern "stream_FDStream_FDStream_native_write_3"
247 private fun native_write_char
(i
: Int, c
: Char): Int is extern "stream_FDStream_FDStream_native_write_char_2"
249 init(fd
: Int) do self.fd
= fd
255 redef var eof
: Bool = false
259 var nb
= native_read_char
(fd
)
260 if nb
== -1 then eof
= true
270 redef var is_writable
: Bool
274 var nb
= native_write
(fd
, s
.to_cstring
, s
.length
)
275 if nb
< s
.length
then is_writable
= false
295 redef interface Object
296 # returns first available stream to read or write to
297 # return null on interruption (possibly a signal)
298 protected fun poll
( streams
: Sequence[FDStream] ) : nullable FDStream
300 var in_fds
= new Array[Int]
301 var out_fds
= new Array[Int]
302 var fd_to_stream
= new HashMap[Int,FDStream]
305 if s
isa FDIStream then in_fds
.add
( fd
)
306 if s
isa FDOStream then out_fds
.add
( fd
)
311 var polled_fd
= intern_poll
( in_fds
, out_fds
)
313 if polled_fd
== null then
316 return fd_to_stream
[polled_fd
]
320 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) `{
321 int in_len, out_len, total_len;
322 struct pollfd *c_fds;
325 int first_polled_fd = -1;
328 in_len = Array_of_Int_length( in_fds );
329 out_len = Array_of_Int_length( out_fds );
330 total_len = in_len + out_len;
331 c_fds = malloc( sizeof(struct pollfd) * total_len );
334 for ( i=0; i<in_len; i ++ ) {
336 fd = Array_of_Int__index( in_fds, i );
339 c_fds[i].events = POLLIN;
343 for ( i=0; i<out_len; i ++ ) {
345 fd = Array_of_Int__index( out_fds, i );
348 c_fds[i].events = POLLOUT;
351 /* poll all fds, unlimited timeout */
352 result = poll( c_fds, total_len, -1 );
355 /* analyse results */
356 for ( i=0; i<total_len; i++ )
357 if ( c_fds[i].revents & c_fds[i].events || /* awaited event */
358 c_fds[i].revents & POLLHUP ) /* closed */
360 first_polled_fd = c_fds[i].fd;
364 return Int_as_nullable( first_polled_fd );
366 else if ( result < 0 )
367 fprintf( stderr, "Error in Stream:poll: %s\n", strerror( errno ) );
373 # Stream to a String. Mainly used for compatibility with OStream type and tests.
377 private var content
= new Array[String]
378 redef fun to_s
do return content
.to_s
379 redef fun is_writable
do return true
380 redef fun write
(str
) do content
.add
(str
)