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' and '\r\n', if any, is removed in each line.
68 # var txt = "Hello\n\nWorld\n"
69 # var i = new StringIStream(txt)
70 # assert i.read_line == "Hello"
71 # assert i.read_line == ""
72 # assert i.read_line == "World"
76 # Only LINE FEED (`\n`), CARRIAGE RETURN & LINE FEED (`\r\n`), and
77 # the end or file (EOF) is considered to delimit the end of lines.
78 # CARRIAGE RETURN (`\r`) alone is not used for the end of line.
81 # var txt2 = "Hello\r\n\n\rWorld"
82 # var i2 = new StringIStream(txt2)
83 # assert i2.read_line == "Hello"
84 # assert i2.read_line == ""
85 # assert i2.read_line == "\rWorld"
89 # NOTE: Use `append_line_to` if the line terminator needs to be preserved.
92 if last_error
!= null then return ""
94 var s
= new FlatBuffer
99 # Read all the lines until the eof.
101 # The line terminator '\n' and `\r\n` is removed in each line,
104 # var txt = "Hello\n\nWorld\n"
105 # var i = new StringIStream(txt)
106 # assert i.read_lines == ["Hello", "", "World"]
109 # This method is more efficient that splitting
110 # the result of `read_all`.
112 # NOTE: SEE `read_line` for details.
113 fun read_lines
: Array[String]
115 var res
= new Array[String]
122 # Return an iterator that read each line.
124 # The line terminator '\n' and `\r\n` is removed in each line,
125 # The line are read with `read_line`. See this method for details.
128 # var txt = "Hello\n\nWorld\n"
129 # var i = new StringIStream(txt)
130 # assert i.each_line.to_a == ["Hello", "", "World"]
133 # Unlike `read_lines` that read all lines at the call, `each_line` is lazy.
134 # Therefore, the stream should no be closed until the end of the stream.
137 # i = new StringIStream(txt)
138 # var el = i.each_line
140 # assert el.item == "Hello"
142 # assert el.item == ""
147 # assert not el.is_ok
148 # # closed before "world" is read
150 fun each_line
: LineIterator do return new LineIterator(self)
152 # Read all the stream until the eof.
154 # The content of the file is returned verbatim.
157 # var txt = "Hello\n\nWorld\n"
158 # var i = new StringIStream(txt)
159 # assert i.read_all == txt
163 if last_error
!= null then return ""
164 var s
= new FlatBuffer
167 if c
>= 0 then s
.add
(c
.ascii
)
172 # Read a string until the end of the line and append it to `s`.
174 # Unlike `read_line` and other related methods,
175 # the line terminator '\n', if any, is preserved in each line.
176 # Use the method `Text::chomp` to safely remove it.
179 # var txt = "Hello\n\nWorld\n"
180 # var i = new StringIStream(txt)
181 # var b = new FlatBuffer
182 # i.append_line_to(b)
183 # assert b == "Hello\n"
184 # i.append_line_to(b)
185 # assert b == "Hello\n\n"
186 # i.append_line_to(b)
191 # If `\n` is not present at the end of the result, it means that
192 # a non-eol terminated last line was returned.
195 # var i2 = new StringIStream("hello")
197 # var b2 = new FlatBuffer
198 # i2.append_line_to(b2)
199 # assert b2 == "hello"
203 # NOTE: The single character LINE FEED (`\n`) delimits the end of lines.
204 # Therefore CARRIAGE RETURN & LINE FEED (`\r\n`) is also recognized.
205 fun append_line_to
(s
: Buffer)
207 if last_error
!= null then return
215 if c
== '\n' then return
220 # Is there something to read.
221 # This function returns 'false' if there is something to read.
222 fun eof
: Bool is abstract
225 # Iterator returned by `IStream::each_line`.
226 # See the aforementioned method for details.
228 super Iterator[String]
230 # The original stream
235 var res
= not stream
.eof
236 if not res
and close_on_finish
then stream
.close
244 line
= stream
.read_line
250 # The last line read (cache)
251 private var line
: nullable String = null
256 if line
== null then item
261 # Close the stream when the stream is at the EOF.
264 var close_on_finish
= false is writable
268 if close_on_finish
then stream
.close
272 # IStream capable of declaring if readable without blocking
273 abstract class PollableIStream
276 # Is there something to read? (without blocking)
277 fun poll_in
: Bool is abstract
281 # Abstract output stream
282 abstract class OStream
285 fun write
(s
: Text) is abstract
287 # Can the stream be used to write
288 fun is_writable
: Bool is abstract
291 # Things that can be efficienlty writen to a OStream
293 # The point of this interface it to allow is instance to be efficenty
294 # writen into a OStream without having to allocate a big String object
296 # ready-to-save documents usually provide this interface.
298 # Write itself to a `stream`
299 # The specific logic it let to the concrete subclasses
300 fun write_to
(stream
: OStream) is abstract
302 # Like `write_to` but return a new String (may be quite large)
304 # This funtionnality is anectodical, since the point
305 # of streamable object to to be efficienlty written to a
306 # stream without having to allocate and concatenate strings
307 fun write_to_string
: String
309 var stream
= new StringOStream
317 redef fun write_to
(stream
) do stream
.write
(self)
320 # Input streams with a buffer
321 abstract class BufferedIStream
325 if last_error
!= null then return -1
327 last_error
= new IOError("Stream has reached eof")
330 var c
= _buffer
.chars
[_buffer_pos
]
337 if last_error
!= null then return ""
338 if _buffer
.length
== _buffer_pos
then
344 if _buffer_pos
+ i
>= _buffer
.length
then
345 var from
= _buffer_pos
346 _buffer_pos
= _buffer
.length
347 return _buffer
.substring_from
(from
).to_s
350 return _buffer
.substring
(_buffer_pos
- i
, i
).to_s
355 if last_error
!= null then return ""
356 var s
= new FlatBuffer
359 var k
= _buffer
.length
370 redef fun append_line_to
(s
)
373 # First phase: look for a '\n'
375 while i
< _buffer
.length
and _buffer
.chars
[i
] != '\n' do i
+= 1
378 if i
< _buffer
.length
then
379 assert _buffer
.chars
[i
] == '\n'
386 # if there is something to append
387 if i
> _buffer_pos
then
388 # Enlarge the string (if needed)
389 s
.enlarge
(s
.length
+ i
- _buffer_pos
)
391 # Copy from the buffer to the string
394 s
.add
(_buffer
.chars
[j
])
408 if end_reached
then return
416 if _buffer_pos
< _buffer
.length
then return false
417 if end_reached
then return true
419 return _buffer_pos
>= _buffer
.length
and end_reached
423 private var buffer
: nullable FlatBuffer = null
425 # The current position in the buffer
426 private var buffer_pos
: Int = 0
429 protected fun fill_buffer
is abstract
431 # Is the last fill_buffer reach the end
432 protected fun end_reached
: Bool is abstract
434 # Allocate a `_buffer` for a given `capacity`.
435 protected fun prepare_buffer
(capacity
: Int)
437 _buffer
= new FlatBuffer.with_capacity
(capacity
)
438 _buffer_pos
= 0 # need to read
442 # An Input/Output Stream
443 abstract class IOStream
448 # Stream to a String.
450 # Mainly used for compatibility with OStream type and tests.
454 private var content
= new Array[String]
455 redef fun to_s
do return content
.to_s
456 redef fun is_writable
do return not closed
460 content
.add
(str
.to_s
)
463 # Is the stream closed?
464 protected var closed
= false
466 redef fun close
do closed
= true
469 # Stream from a String.
471 # Mainly used for compatibility with IStream type and tests.
475 # The string to read from.
478 # The current position in the string.
479 private var cursor
: Int = 0
481 redef fun read_char
do
482 if cursor
< source
.length
then
483 var c
= source
[cursor
].ascii
496 redef fun read_all
do
498 cursor
= source
.length
499 if c
== 0 then return source
500 return source
.substring_from
(c
)
503 redef fun eof
do return cursor
>= source
.length