lib: implement `close` in StringOStream
[nit.git] / lib / standard / stream.nit
index 745064a..a94601c 100644 (file)
 # Input and output streams of characters
 module stream
 
-import string
+intrude import ropes
 
 in "C" `{
        #include <unistd.h>
        #include <poll.h>
        #include <errno.h>
        #include <string.h>
+       #include <signal.h>
 `}
 
 # Abstract stream class
@@ -37,7 +38,7 @@ interface IStream
        # Read at most i bytes
        fun read(i: Int): String
        do
-               var s = new Buffer.with_capacity(i)
+               var s = new FlatBuffer.with_capacity(i)
                while i > 0 and not eof do
                        var c = read_char
                        if c >= 0 then
@@ -52,7 +53,7 @@ interface IStream
        fun read_line: String
        do
                assert not eof
-               var s = new Buffer
+               var s = new FlatBuffer
                append_line_to(s)
                return s.to_s
        end
@@ -60,7 +61,7 @@ interface IStream
        # Read all the stream until the eof.
        fun read_all: String
        do
-               var s = new Buffer
+               var s = new FlatBuffer
                while not eof do
                        var c = read_char
                        if c >= 0 then s.add(c.ascii)
@@ -88,16 +89,77 @@ interface IStream
        fun eof: Bool is abstract
 end
 
+# IStream capable of declaring if readable without blocking
+interface PollableIStream
+       super IStream
+
+       # Is there something to read? (without blocking)
+       fun poll_in: Bool is abstract
+
+end
+
 # Abstract output stream
 interface OStream
        super IOS
        # write a string
-       fun write(s: String) is abstract
+       fun write(s: Text) is abstract
 
        # Can the stream be used to write
        fun is_writable: Bool is abstract
 end
 
+# Things that can be efficienlty writen to a OStream
+#
+# The point of this interface it to allow is instance to be efficenty
+# writen into a OStream without having to allocate a big String object
+#
+# ready-to-save documents usually provide this interface.
+interface Streamable
+       # Write itself to a `stream`
+       # The specific logic it let to the concrete subclasses
+       fun write_to(stream: OStream) is abstract
+
+       # Like `write_to` but return a new String (may be quite large)
+       #
+       # This funtionnality is anectodical, since the point
+       # of streamable object to to be efficienlty written to a
+       # stream without having to allocate and concatenate strings
+       fun write_to_string: String
+       do
+               var stream = new StringOStream
+               write_to(stream)
+               return stream.to_s
+       end
+end
+
+redef class Text
+       super Streamable
+       redef fun write_to(stream) do stream.write(self)
+end
+
+redef class RopeNode
+       super Streamable
+end
+
+redef class Leaf
+
+       redef fun write_to(s) do s.write(str)
+end
+
+redef class Concat
+
+       redef fun write_to(s)
+       do
+               if left != null then left.write_to(s)
+               if right != null then right.write_to(s)
+       end
+end
+
+redef class RopeString
+
+       redef fun write_to(s) do root.write_to(s)
+end
+
 # Input streams with a buffer
 abstract class BufferedIStream
        super IStream
@@ -117,29 +179,25 @@ abstract class BufferedIStream
 
        redef fun read(i)
        do
-               var s = new Buffer.with_capacity(i)
-               var j = _buffer_pos
-               var k = _buffer.length
-               while i > 0 do
-                       if j >= k then
+               if _buffer.length == _buffer_pos then
+                       if not eof then
                                fill_buffer
-                               if eof then return s.to_s
-                               j = _buffer_pos
-                               k = _buffer.length
-                       end
-                       while j < k and i > 0 do
-                               s.add(_buffer.chars[j])
-                               j +=  1
-                               i -= 1
+                               return read(i)
                        end
+                       return ""
                end
-               _buffer_pos = j
-               return s.to_s
+               if _buffer_pos + i >= _buffer.length then
+                       var from = _buffer_pos
+                       _buffer_pos = _buffer.length
+                       return _buffer.substring_from(from).to_s
+               end
+               _buffer_pos += i
+               return _buffer.substring(_buffer_pos - i, i).to_s
        end
 
        redef fun read_all
        do
-               var s = new Buffer
+               var s = new FlatBuffer
                while not eof do
                        var j = _buffer_pos
                        var k = _buffer.length
@@ -192,7 +250,7 @@ abstract class BufferedIStream
        redef fun eof do return _buffer_pos >= _buffer.length and end_reached
 
        # The buffer
-       var _buffer: nullable Buffer = null
+       var _buffer: nullable FlatBuffer = null
 
        # The current position in the buffer
        var _buffer_pos: Int = 0
@@ -206,7 +264,7 @@ abstract class BufferedIStream
        # Allocate a `_buffer` for a given `capacity`.
        protected fun prepare_buffer(capacity: Int)
        do
-               _buffer = new Buffer.with_capacity(capacity)
+               _buffer = new FlatBuffer.with_capacity(capacity)
                _buffer_pos = 0 # need to read
        end
 end
@@ -361,6 +419,13 @@ class StringOStream
 
        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)
+       redef fun is_writable do return not closed
+       redef fun write(str)
+       do
+               assert not closed
+               content.add(str.to_s)
+       end
+
+       protected var closed = false
+       redef fun close do closed = true
 end