lib/stream: added more explicit comment for eof method.
[nit.git] / lib / standard / stream.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2004-2008 Jean Privat <jean@pryen.org>
4 #
5 # This file is free software, which comes along with NIT. This software is
6 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
7 # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
8 # PARTICULAR PURPOSE. You can modify it is you want, provided this header
9 # is kept unaltered, and a notification of the changes is added.
10 # You are allowed to redistribute it and sell it, alone or is a part of
11 # another product.
12
13 # Input and output streams of characters
14 module stream
15
16 import string
17
18 # Abstract stream class
19 interface IOS
20 # close the stream
21 fun close is abstract
22 end
23
24 # Abstract input streams
25 interface IStream
26 super IOS
27 # Read a character. Return its ASCII value, -1 on EOF or timeout
28 fun read_char: Int is abstract
29
30 # Read at most i bytes
31 fun read(i: Int): String
32 do
33 var s = new Buffer.with_capacity(i)
34 while i > 0 and not eof do
35 var c = read_char
36 if c >= 0 then
37 s.add(c.ascii)
38 i -= 1
39 end
40 end
41 return s.to_s
42 end
43
44 # Read a string until the end of the line.
45 fun read_line: String
46 do
47 assert not eof
48 var s = new Buffer
49 append_line_to(s)
50 return s.to_s
51 end
52
53 # Read all the stream until the eof.
54 fun read_all: String
55 do
56 var s = new Buffer
57 while not eof do
58 var c = read_char
59 if c >= 0 then s.add(c.ascii)
60 end
61 return s.to_s
62 end
63
64 # Read a string until the end of the line and append it to `s`.
65 fun append_line_to(s: Buffer)
66 do
67 loop
68 var x = read_char
69 if x == -1 then
70 if eof then return
71 else
72 var c = x.ascii
73 s.push(c)
74 if c == '\n' then return
75 end
76 end
77 end
78
79 # Is there something to read.
80 # This function returns 'false' if there is something to read.
81 fun eof: Bool is abstract
82 end
83
84 # Abstract output stream
85 interface OStream
86 super IOS
87 # write a string
88 fun write(s: String) is abstract
89
90 # Can the stream be used to write
91 fun is_writable: Bool is abstract
92 end
93
94 # Input streams with a buffer
95 abstract class BufferedIStream
96 super IStream
97 redef fun read_char
98 do
99 assert not eof
100 if _buffer_pos >= _buffer.length then
101 fill_buffer
102 end
103 if _buffer_pos >= _buffer.length then
104 return -1
105 end
106 var c = _buffer[_buffer_pos]
107 _buffer_pos += 1
108 return c.ascii
109 end
110
111 redef fun read(i)
112 do
113 var s = new Buffer.with_capacity(i)
114 var j = _buffer_pos
115 var k = _buffer.length
116 while i > 0 do
117 if j >= k then
118 fill_buffer
119 if eof then return s.to_s
120 j = _buffer_pos
121 k = _buffer.length
122 end
123 while j < k and i > 0 do
124 s.add(_buffer[j])
125 j += 1
126 i -= 1
127 end
128 end
129 _buffer_pos = j
130 return s.to_s
131 end
132
133 redef fun read_all
134 do
135 var s = new Buffer
136 while not eof do
137 var j = _buffer_pos
138 var k = _buffer.length
139 while j < k do
140 s.add(_buffer[j])
141 j += 1
142 end
143 _buffer_pos = j
144 fill_buffer
145 end
146 return s.to_s
147 end
148
149 redef fun append_line_to(s)
150 do
151 loop
152 # First phase: look for a '\n'
153 var i = _buffer_pos
154 while i < _buffer.length and _buffer[i] != '\n' do i += 1
155
156 # if there is something to append
157 if i > _buffer_pos then
158 # Enlarge the string (if needed)
159 s.enlarge(s.length + i - _buffer_pos)
160
161 # Copy from the buffer to the string
162 var j = _buffer_pos
163 while j < i do
164 s.add(_buffer[j])
165 j += 1
166 end
167 end
168
169 if i < _buffer.length then
170 # so \n is in _buffer[i]
171 _buffer_pos = i + 1 # skip \n
172 return
173 else
174 # so \n is not found
175 _buffer_pos = i
176 if end_reached then
177 return
178 else
179 fill_buffer
180 end
181 end
182 end
183 end
184
185 redef fun eof do return _buffer_pos >= _buffer.length and end_reached
186
187 # The buffer
188 var _buffer: nullable Buffer = null
189
190 # The current position in the buffer
191 var _buffer_pos: Int = 0
192
193 # Fill the buffer
194 protected fun fill_buffer is abstract
195
196 # Is the last fill_buffer reach the end
197 protected fun end_reached: Bool is abstract
198
199 # Allocate a `_buffer` for a given `capacity`.
200 protected fun prepare_buffer(capacity: Int)
201 do
202 _buffer = new Buffer.with_capacity(capacity)
203 _buffer_pos = 0 # need to read
204 end
205 end
206
207 interface IOStream
208 super IStream
209 super OStream
210 end
211
212 ##############################################################"
213
214 abstract class FDStream
215 super IOS
216 # File description
217 var fd: Int
218
219 redef fun close do native_close(fd)
220
221 private fun native_close(i: Int): Int is extern "stream_FDStream_FDStream_native_close_1"
222 private fun native_read_char(i: Int): Int is extern "stream_FDStream_FDStream_native_read_char_1"
223 private fun native_read(i: Int, buf: NativeString, len: Int): Int is extern "stream_FDStream_FDStream_native_read_3"
224 private fun native_write(i: Int, buf: NativeString, len: Int): Int is extern "stream_FDStream_FDStream_native_write_3"
225 private fun native_write_char(i: Int, c: Char): Int is extern "stream_FDStream_FDStream_native_write_char_2"
226
227 init(fd: Int) do self.fd = fd
228 end
229
230 class FDIStream
231 super FDStream
232 super IStream
233 redef var eof: Bool = false
234
235 redef fun read_char
236 do
237 var nb = native_read_char(fd)
238 if nb == -1 then eof = true
239 return nb
240 end
241
242 init(fd: Int) do end
243 end
244
245 class FDOStream
246 super FDStream
247 super OStream
248 redef var is_writable: Bool
249
250 redef fun write(s)
251 do
252 var nb = native_write(fd, s.to_cstring, s.length)
253 if nb < s.length then is_writable = false
254 end
255
256 init(fd: Int)
257 do
258 is_writable = true
259 end
260 end
261
262 class FDIOStream
263 super FDIStream
264 super FDOStream
265 super IOStream
266 init(fd: Int)
267 do
268 self.fd = fd
269 is_writable = true
270 end
271 end
272
273 redef interface Object
274 # returns first available stream to read or write to
275 # return null on interruption (possibly a signal)
276 protected fun poll( streams : Sequence[FDStream] ) : nullable FDStream
277 do
278 var in_fds = new Array[Int]
279 var out_fds = new Array[Int]
280 var fd_to_stream = new HashMap[Int,FDStream]
281 for s in streams do
282 var fd = s.fd
283 if s isa FDIStream then in_fds.add( fd )
284 if s isa FDOStream then out_fds.add( fd )
285
286 fd_to_stream[fd] = s
287 end
288
289 var polled_fd = intern_poll( in_fds, out_fds )
290
291 if polled_fd == null then
292 return null
293 else
294 return fd_to_stream[polled_fd]
295 end
296 end
297
298 private fun intern_poll( in_fds : Array[Int], out_fds : Array[Int] ) : nullable Int is extern import Array::length, Array::[], nullable Object as ( Int ), Int as nullable
299 end