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
23 # Any kind of error that could be produced by an operation on Streams
28 # Abstract stream class
30 # Error produced by the file stream
32 # var ifs = new IFStream.open("donotmakethisfile.binx")
35 # assert ifs.last_error != null
36 var last_error
: nullable IOError = null
42 # Abstract input streams
43 abstract class IStream
45 # Read a character. Return its ASCII value, -1 on EOF or timeout
46 fun read_char
: Int is abstract
48 # Read at most i bytes
49 fun read
(i
: Int): String
51 if last_error
!= null then return ""
52 var s
= new FlatBuffer.with_capacity
(i
)
53 while i
> 0 and not eof
do
63 # Read a string until the end of the line.
65 # The line terminator '\n', if any, is preserved in each line.
66 # Use the method `Text::chomp` to safely remove it.
69 # var txt = "Hello\n\nWorld\n"
70 # var i = new StringIStream(txt)
71 # assert i.read_line == "Hello\n"
72 # assert i.read_line == "\n"
73 # assert i.read_line == "World\n"
77 # If `\n` is not present at the end of the result, it means that
78 # a non-eol terminated last line was returned.
81 # var i2 = new StringIStream("hello")
83 # assert i2.read_line == "hello"
87 # NOTE: Only LINE FEED (`\n`) is considered to delimit the end of lines.
90 if last_error
!= null then return ""
92 var s
= new FlatBuffer
97 # Read all the lines until the eof.
99 # The line terminator '\n' is removed in each line,
102 # var txt = "Hello\n\nWorld\n"
103 # var i = new StringIStream(txt)
104 # assert i.read_lines == ["Hello", "", "World"]
107 # This method is more efficient that splitting
108 # the result of `read_all`.
110 # NOTE: Only LINE FEED (`\n`) is considered to delimit the end of lines.
111 fun read_lines
: Array[String]
113 var res
= new Array[String]
115 res
.add read_line
.chomp
120 # Read all the stream until the eof.
123 if last_error
!= null then return ""
124 var s
= new FlatBuffer
127 if c
>= 0 then s
.add
(c
.ascii
)
132 # Read a string until the end of the line and append it to `s`.
134 # SEE: `read_line` for details.
135 fun append_line_to
(s
: Buffer)
137 if last_error
!= null then return
145 if c
== '\n' then return
150 # Is there something to read.
151 # This function returns 'false' if there is something to read.
152 fun eof
: Bool is abstract
155 # IStream capable of declaring if readable without blocking
156 abstract class PollableIStream
159 # Is there something to read? (without blocking)
160 fun poll_in
: Bool is abstract
164 # Abstract output stream
165 abstract class OStream
168 fun write
(s
: Text) is abstract
170 # Can the stream be used to write
171 fun is_writable
: Bool is abstract
174 # Things that can be efficienlty writen to a OStream
176 # The point of this interface it to allow is instance to be efficenty
177 # writen into a OStream without having to allocate a big String object
179 # ready-to-save documents usually provide this interface.
181 # Write itself to a `stream`
182 # The specific logic it let to the concrete subclasses
183 fun write_to
(stream
: OStream) is abstract
185 # Like `write_to` but return a new String (may be quite large)
187 # This funtionnality is anectodical, since the point
188 # of streamable object to to be efficienlty written to a
189 # stream without having to allocate and concatenate strings
190 fun write_to_string
: String
192 var stream
= new StringOStream
200 redef fun write_to
(stream
) do stream
.write
(self)
203 # Input streams with a buffer
204 abstract class BufferedIStream
208 if last_error
!= null then return 0
209 if eof
then last_error
= new IOError("Stream has reached eof")
210 if _buffer_pos
>= _buffer
.length
then
213 if _buffer_pos
>= _buffer
.length
then
216 var c
= _buffer
.chars
[_buffer_pos
]
223 if last_error
!= null then return ""
224 if _buffer
.length
== _buffer_pos
then
231 if _buffer_pos
+ i
>= _buffer
.length
then
232 var from
= _buffer_pos
233 _buffer_pos
= _buffer
.length
234 return _buffer
.substring_from
(from
).to_s
237 return _buffer
.substring
(_buffer_pos
- i
, i
).to_s
242 if last_error
!= null then return ""
243 var s
= new FlatBuffer
246 var k
= _buffer
.length
257 redef fun append_line_to
(s
)
260 # First phase: look for a '\n'
262 while i
< _buffer
.length
and _buffer
.chars
[i
] != '\n' do i
+= 1
264 # if there is something to append
265 if i
> _buffer_pos
then
266 # Enlarge the string (if needed)
267 s
.enlarge
(s
.length
+ i
- _buffer_pos
)
269 # Copy from the buffer to the string
272 s
.add
(_buffer
.chars
[j
])
277 if i
< _buffer
.length
then
278 # so \n is in _buffer[i]
279 _buffer_pos
= i
+ 1 # skip \n
293 redef fun eof
do return _buffer_pos
>= _buffer
.length
and end_reached
296 private var buffer
: nullable FlatBuffer = null
298 # The current position in the buffer
299 private var buffer_pos
: Int = 0
302 protected fun fill_buffer
is abstract
304 # Is the last fill_buffer reach the end
305 protected fun end_reached
: Bool is abstract
307 # Allocate a `_buffer` for a given `capacity`.
308 protected fun prepare_buffer
(capacity
: Int)
310 _buffer
= new FlatBuffer.with_capacity
(capacity
)
311 _buffer_pos
= 0 # need to read
315 # An Input/Output Stream
316 abstract class IOStream
321 # Stream to a String.
323 # Mainly used for compatibility with OStream type and tests.
327 private var content
= new Array[String]
328 redef fun to_s
do return content
.to_s
329 redef fun is_writable
do return not closed
333 content
.add
(str
.to_s
)
336 # Is the stream closed?
337 protected var closed
= false
339 redef fun close
do closed
= true
342 # Stream from a String.
344 # Mainly used for compatibility with IStream type and tests.
348 # The string to read from.
351 # The current position in the string.
352 private var cursor
: Int = 0
354 redef fun read_char
do
355 if cursor
< source
.length
then
356 var c
= source
[cursor
].ascii
369 redef fun eof
do return cursor
>= source
.length