lib: Fixed signature for poll in PNaCl
[nit.git] / lib / standard / stream.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
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
9 # another product.
10
11 # Input and output streams of characters
12 module stream
13
14 intrude import ropes
15 import error
16
17 in "C" `{
18 #include <unistd.h>
19 #include <string.h>
20 #include <signal.h>
21 `}
22
23 # Any kind of error that could be produced by an operation on Streams
24 class IOError
25 super Error
26 end
27
28 # Abstract stream class
29 abstract class IOS
30 # Error produced by the file stream
31 #
32 # var ifs = new IFStream.open("donotmakethisfile.binx")
33 # ifs.read_all
34 # ifs.close
35 # assert ifs.last_error != null
36 var last_error: nullable IOError = null
37
38 # close the stream
39 fun close is abstract
40 end
41
42 # Abstract input streams
43 abstract class IStream
44 super IOS
45 # Read a character. Return its ASCII value, -1 on EOF or timeout
46 fun read_char: Int is abstract
47
48 # Read at most i bytes
49 fun read(i: Int): String
50 do
51 if last_error != null then return ""
52 var s = new FlatBuffer.with_capacity(i)
53 while i > 0 and not eof do
54 var c = read_char
55 if c >= 0 then
56 s.add(c.ascii)
57 i -= 1
58 end
59 end
60 return s.to_s
61 end
62
63 # Read a string until the end of the line.
64 fun read_line: String
65 do
66 if last_error != null then return ""
67 assert not eof
68 var s = new FlatBuffer
69 append_line_to(s)
70 return s.to_s
71 end
72
73 # Read all the stream until the eof.
74 fun read_all: String
75 do
76 if last_error != null then return ""
77 var s = new FlatBuffer
78 while not eof do
79 var c = read_char
80 if c >= 0 then s.add(c.ascii)
81 end
82 return s.to_s
83 end
84
85 # Read a string until the end of the line and append it to `s`.
86 fun append_line_to(s: Buffer)
87 do
88 if last_error != null then return
89 loop
90 var x = read_char
91 if x == -1 then
92 if eof then return
93 else
94 var c = x.ascii
95 s.chars.push(c)
96 if c == '\n' then return
97 end
98 end
99 end
100
101 # Is there something to read.
102 # This function returns 'false' if there is something to read.
103 fun eof: Bool is abstract
104 end
105
106 # IStream capable of declaring if readable without blocking
107 abstract class PollableIStream
108 super IStream
109
110 # Is there something to read? (without blocking)
111 fun poll_in: Bool is abstract
112
113 end
114
115 # Abstract output stream
116 abstract class OStream
117 super IOS
118 # write a string
119 fun write(s: Text) is abstract
120
121 # Can the stream be used to write
122 fun is_writable: Bool is abstract
123 end
124
125 # Things that can be efficienlty writen to a OStream
126 #
127 # The point of this interface it to allow is instance to be efficenty
128 # writen into a OStream without having to allocate a big String object
129 #
130 # ready-to-save documents usually provide this interface.
131 interface Streamable
132 # Write itself to a `stream`
133 # The specific logic it let to the concrete subclasses
134 fun write_to(stream: OStream) is abstract
135
136 # Like `write_to` but return a new String (may be quite large)
137 #
138 # This funtionnality is anectodical, since the point
139 # of streamable object to to be efficienlty written to a
140 # stream without having to allocate and concatenate strings
141 fun write_to_string: String
142 do
143 var stream = new StringOStream
144 write_to(stream)
145 return stream.to_s
146 end
147 end
148
149 redef class Text
150 super Streamable
151 redef fun write_to(stream) do stream.write(self)
152 end
153
154 # Input streams with a buffer
155 abstract class BufferedIStream
156 super IStream
157 redef fun read_char
158 do
159 if last_error != null then return 0
160 if eof then last_error = new IOError("Stream has reached eof")
161 if _buffer_pos >= _buffer.length then
162 fill_buffer
163 end
164 if _buffer_pos >= _buffer.length then
165 return -1
166 end
167 var c = _buffer.chars[_buffer_pos]
168 _buffer_pos += 1
169 return c.ascii
170 end
171
172 redef fun read(i)
173 do
174 if last_error != null then return ""
175 if _buffer.length == _buffer_pos then
176 if not eof then
177 fill_buffer
178 return read(i)
179 end
180 return ""
181 end
182 if _buffer_pos + i >= _buffer.length then
183 var from = _buffer_pos
184 _buffer_pos = _buffer.length
185 return _buffer.substring_from(from).to_s
186 end
187 _buffer_pos += i
188 return _buffer.substring(_buffer_pos - i, i).to_s
189 end
190
191 redef fun read_all
192 do
193 if last_error != null then return ""
194 var s = new FlatBuffer
195 while not eof do
196 var j = _buffer_pos
197 var k = _buffer.length
198 while j < k do
199 s.add(_buffer[j])
200 j += 1
201 end
202 _buffer_pos = j
203 fill_buffer
204 end
205 return s.to_s
206 end
207
208 redef fun append_line_to(s)
209 do
210 loop
211 # First phase: look for a '\n'
212 var i = _buffer_pos
213 while i < _buffer.length and _buffer.chars[i] != '\n' do i += 1
214
215 # if there is something to append
216 if i > _buffer_pos then
217 # Enlarge the string (if needed)
218 s.enlarge(s.length + i - _buffer_pos)
219
220 # Copy from the buffer to the string
221 var j = _buffer_pos
222 while j < i do
223 s.add(_buffer.chars[j])
224 j += 1
225 end
226 end
227
228 if i < _buffer.length then
229 # so \n is in _buffer[i]
230 _buffer_pos = i + 1 # skip \n
231 return
232 else
233 # so \n is not found
234 _buffer_pos = i
235 if end_reached then
236 return
237 else
238 fill_buffer
239 end
240 end
241 end
242 end
243
244 redef fun eof do return _buffer_pos >= _buffer.length and end_reached
245
246 # The buffer
247 private var buffer: nullable FlatBuffer = null
248
249 # The current position in the buffer
250 private var buffer_pos: Int = 0
251
252 # Fill the buffer
253 protected fun fill_buffer is abstract
254
255 # Is the last fill_buffer reach the end
256 protected fun end_reached: Bool is abstract
257
258 # Allocate a `_buffer` for a given `capacity`.
259 protected fun prepare_buffer(capacity: Int)
260 do
261 _buffer = new FlatBuffer.with_capacity(capacity)
262 _buffer_pos = 0 # need to read
263 end
264 end
265
266 abstract class IOStream
267 super IStream
268 super OStream
269 end
270
271 # Stream to a String.
272 #
273 # Mainly used for compatibility with OStream type and tests.
274 class StringOStream
275 super OStream
276
277 private var content = new Array[String]
278 redef fun to_s do return content.to_s
279 redef fun is_writable do return not closed
280 redef fun write(str)
281 do
282 assert not closed
283 content.add(str.to_s)
284 end
285
286 protected var closed = false
287 redef fun close do closed = true
288 end
289
290 # Stream from a String.
291 #
292 # Mainly used for compatibility with IStream type and tests.
293 class StringIStream
294 super IStream
295
296 # The string to read from.
297 var source: String
298
299 # The current position in the string.
300 private var cursor: Int = 0
301
302 redef fun read_char do
303 if cursor < source.length then
304 var c = source[cursor].ascii
305
306 cursor += 1
307 return c
308 else
309 return -1
310 end
311 end
312
313 redef fun close do
314 source = ""
315 end
316
317 redef fun eof do return cursor >= source.length
318 end