stdlib/strings: Cleaned the old way of defining private attributes (readable var...
[nit.git] / lib / standard / stream.nit
index 7036697..745064a 100644 (file)
 # You  are  allowed  to  redistribute it and sell it, alone or is a part of
 # another product.
 
-# This module handle abstract input and output streams
-package stream
+# Input and output streams of characters
+module stream
 
 import string
 
+in "C" `{
+       #include <unistd.h>
+       #include <poll.h>
+       #include <errno.h>
+       #include <string.h>
+`}
+
 # Abstract stream class
-class IOS
+interface IOS
        # close the stream
        fun close is abstract
 end
 
 # Abstract input streams
-class IStream
+interface IStream
        super IOS
        # Read a character. Return its ASCII value, -1 on EOF or timeout
        fun read_char: Int is abstract
@@ -61,7 +68,7 @@ class IStream
                return s.to_s
        end
 
-       # Read a string until the end of the line and append it to `s'.
+       # Read a string until the end of the line and append it to `s`.
        fun append_line_to(s: Buffer)
        do
                loop
@@ -70,18 +77,19 @@ class IStream
                                if eof then return
                        else
                                var c = x.ascii
-                               s.push(c)
+                               s.chars.push(c)
                                if c == '\n' then return
                        end
                end
        end
 
        # Is there something to read.
+       # This function returns 'false' if there is something to read.
        fun eof: Bool is abstract
 end
 
 # Abstract output stream
-class OStream
+interface OStream
        super IOS
        # write a string
        fun write(s: String) is abstract
@@ -91,7 +99,7 @@ class OStream
 end
 
 # Input streams with a buffer
-class BufferedIStream
+abstract class BufferedIStream
        super IStream
        redef fun read_char
        do
@@ -102,7 +110,7 @@ class BufferedIStream
                if _buffer_pos >= _buffer.length then
                        return -1
                end
-               var c = _buffer[_buffer_pos]
+               var c = _buffer.chars[_buffer_pos]
                _buffer_pos += 1
                return c.ascii
        end
@@ -120,7 +128,7 @@ class BufferedIStream
                                k = _buffer.length
                        end
                        while j < k and i > 0 do
-                               s.add(_buffer[j])
+                               s.add(_buffer.chars[j])
                                j +=  1
                                i -= 1
                        end
@@ -136,7 +144,7 @@ class BufferedIStream
                        var j = _buffer_pos
                        var k = _buffer.length
                        while j < k do
-                               s.add(_buffer[j])
+                               s.add(_buffer.chars[j])
                                j += 1
                        end
                        _buffer_pos = j
@@ -150,7 +158,7 @@ class BufferedIStream
                loop
                        # First phase: look for a '\n'
                        var i = _buffer_pos
-                       while i < _buffer.length and _buffer[i] != '\n' do i += 1
+                       while i < _buffer.length and _buffer.chars[i] != '\n' do i += 1
 
                        # if there is something to append
                        if i > _buffer_pos then
@@ -160,7 +168,7 @@ class BufferedIStream
                                # Copy from the buffer to the string
                                var j = _buffer_pos
                                while j < i do
-                                       s.add(_buffer[j])
+                                       s.add(_buffer.chars[j])
                                        j += 1
                                end
                        end
@@ -195,7 +203,7 @@ class BufferedIStream
        # Is the last fill_buffer reach the end 
        protected fun end_reached: Bool is abstract
 
-       # Allocate a `_buffer' for a given `capacity'.
+       # Allocate a `_buffer` for a given `capacity`.
        protected fun prepare_buffer(capacity: Int)
        do
                _buffer = new Buffer.with_capacity(capacity)
@@ -203,14 +211,14 @@ class BufferedIStream
        end
 end
 
-class IOStream
+interface IOStream
        super IStream
        super OStream
 end
 
 ##############################################################"
 
-class FDStream
+abstract class FDStream
        super IOS
        # File description
        var fd: Int
@@ -221,6 +229,7 @@ class FDStream
        private fun native_read_char(i: Int): Int is extern "stream_FDStream_FDStream_native_read_char_1"
        private fun native_read(i: Int, buf: NativeString, len: Int): Int is extern "stream_FDStream_FDStream_native_read_3"
        private fun native_write(i: Int, buf: NativeString, len: Int): Int is extern "stream_FDStream_FDStream_native_write_3"
+       private fun native_write_char(i: Int, c: Char): Int is extern "stream_FDStream_FDStream_native_write_char_2"
 
        init(fd: Int) do self.fd = fd
 end
@@ -267,3 +276,91 @@ class FDIOStream
                is_writable = true
        end
 end
+
+redef interface Object
+       # returns first available stream to read or write to
+       # return null on interruption (possibly a signal)
+       protected fun poll( streams : Sequence[FDStream] ) : nullable FDStream
+       do
+               var in_fds = new Array[Int]
+               var out_fds = new Array[Int]
+               var fd_to_stream = new HashMap[Int,FDStream]
+               for s in streams do
+                       var fd = s.fd
+                       if s isa FDIStream then in_fds.add( fd )
+                       if s isa FDOStream then out_fds.add( fd )
+
+                       fd_to_stream[fd] = s
+               end
+
+               var polled_fd = intern_poll( in_fds, out_fds )
+
+               if polled_fd == null then
+                       return null
+               else
+                       return fd_to_stream[polled_fd]
+               end
+       end
+
+       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) `{
+               int in_len, out_len, total_len;
+               struct pollfd *c_fds;
+               sigset_t sigmask;
+               int i;
+               int first_polled_fd = -1;
+               int result;
+
+               in_len = Array_of_Int_length( in_fds );
+               out_len = Array_of_Int_length( out_fds );
+               total_len = in_len + out_len;
+               c_fds = malloc( sizeof(struct pollfd) * total_len );
+
+               /* input streams */
+               for ( i=0; i<in_len; i ++ ) {
+                       int fd;
+                       fd = Array_of_Int__index( in_fds, i );
+
+                       c_fds[i].fd = fd;
+                       c_fds[i].events = POLLIN;
+               }
+
+               /* output streams */
+               for ( i=0; i<out_len; i ++ ) {
+                       int fd;
+                       fd = Array_of_Int__index( out_fds, i );
+
+                       c_fds[i].fd = fd;
+                       c_fds[i].events = POLLOUT;
+               }
+
+               /* poll all fds, unlimited timeout */
+               result = poll( c_fds, total_len, -1 );
+
+               if ( result > 0 ) {
+                       /* analyse results */
+                       for ( i=0; i<total_len; i++ )
+                               if ( c_fds[i].revents & c_fds[i].events || /* awaited event */
+                                        c_fds[i].revents & POLLHUP ) /* closed */
+                               {
+                                       first_polled_fd = c_fds[i].fd;
+                                       break;
+                               }
+
+                       return Int_as_nullable( first_polled_fd );
+               }
+               else if ( result < 0 )
+                       fprintf( stderr, "Error in Stream:poll: %s\n", strerror( errno ) );
+
+               return null_Int();
+       `}
+end
+
+# Stream to a String. Mainly used for compatibility with OStream type and tests.
+class StringOStream
+       super OStream
+
+       private var content = new Array[String]
+       redef fun to_s do return content.to_s
+       redef fun is_writable do return true
+       redef fun write(str) do content.add(str)
+end