stdlib/strings: Moved Buffer to FlatBuffer, Buffer is now abstract.
[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 in "C" `{
19 #include <unistd.h>
20 #include <poll.h>
21 #include <errno.h>
22 #include <string.h>
23 `}
24
25 # Abstract stream class
26 interface IOS
27 # close the stream
28 fun close is abstract
29 end
30
31 # Abstract input streams
32 interface IStream
33 super IOS
34 # Read a character. Return its ASCII value, -1 on EOF or timeout
35 fun read_char: Int is abstract
36
37 # Read at most i bytes
38 fun read(i: Int): String
39 do
40 var s = new FlatBuffer.with_capacity(i)
41 while i > 0 and not eof do
42 var c = read_char
43 if c >= 0 then
44 s.add(c.ascii)
45 i -= 1
46 end
47 end
48 return s.to_s
49 end
50
51 # Read a string until the end of the line.
52 fun read_line: String
53 do
54 assert not eof
55 var s = new FlatBuffer
56 append_line_to(s)
57 return s.to_s
58 end
59
60 # Read all the stream until the eof.
61 fun read_all: String
62 do
63 var s = new FlatBuffer
64 while not eof do
65 var c = read_char
66 if c >= 0 then s.add(c.ascii)
67 end
68 return s.to_s
69 end
70
71 # Read a string until the end of the line and append it to `s`.
72 fun append_line_to(s: Buffer)
73 do
74 loop
75 var x = read_char
76 if x == -1 then
77 if eof then return
78 else
79 var c = x.ascii
80 s.chars.push(c)
81 if c == '\n' then return
82 end
83 end
84 end
85
86 # Is there something to read.
87 # This function returns 'false' if there is something to read.
88 fun eof: Bool is abstract
89 end
90
91 # Abstract output stream
92 interface OStream
93 super IOS
94 # write a string
95 fun write(s: String) is abstract
96
97 # Can the stream be used to write
98 fun is_writable: Bool is abstract
99 end
100
101 # Input streams with a buffer
102 abstract class BufferedIStream
103 super IStream
104 redef fun read_char
105 do
106 assert not eof
107 if _buffer_pos >= _buffer.length then
108 fill_buffer
109 end
110 if _buffer_pos >= _buffer.length then
111 return -1
112 end
113 var c = _buffer.chars[_buffer_pos]
114 _buffer_pos += 1
115 return c.ascii
116 end
117
118 redef fun read(i)
119 do
120 var s = new FlatBuffer.with_capacity(i)
121 var j = _buffer_pos
122 var k = _buffer.length
123 while i > 0 do
124 if j >= k then
125 fill_buffer
126 if eof then return s.to_s
127 j = _buffer_pos
128 k = _buffer.length
129 end
130 while j < k and i > 0 do
131 s.add(_buffer.chars[j])
132 j += 1
133 i -= 1
134 end
135 end
136 _buffer_pos = j
137 return s.to_s
138 end
139
140 redef fun read_all
141 do
142 var s = new FlatBuffer
143 while not eof do
144 var j = _buffer_pos
145 var k = _buffer.length
146 while j < k do
147 s.add(_buffer.chars[j])
148 j += 1
149 end
150 _buffer_pos = j
151 fill_buffer
152 end
153 return s.to_s
154 end
155
156 redef fun append_line_to(s)
157 do
158 loop
159 # First phase: look for a '\n'
160 var i = _buffer_pos
161 while i < _buffer.length and _buffer.chars[i] != '\n' do i += 1
162
163 # if there is something to append
164 if i > _buffer_pos then
165 # Enlarge the string (if needed)
166 s.enlarge(s.length + i - _buffer_pos)
167
168 # Copy from the buffer to the string
169 var j = _buffer_pos
170 while j < i do
171 s.add(_buffer.chars[j])
172 j += 1
173 end
174 end
175
176 if i < _buffer.length then
177 # so \n is in _buffer[i]
178 _buffer_pos = i + 1 # skip \n
179 return
180 else
181 # so \n is not found
182 _buffer_pos = i
183 if end_reached then
184 return
185 else
186 fill_buffer
187 end
188 end
189 end
190 end
191
192 redef fun eof do return _buffer_pos >= _buffer.length and end_reached
193
194 # The buffer
195 var _buffer: nullable FlatBuffer = null
196
197 # The current position in the buffer
198 var _buffer_pos: Int = 0
199
200 # Fill the buffer
201 protected fun fill_buffer is abstract
202
203 # Is the last fill_buffer reach the end
204 protected fun end_reached: Bool is abstract
205
206 # Allocate a `_buffer` for a given `capacity`.
207 protected fun prepare_buffer(capacity: Int)
208 do
209 _buffer = new FlatBuffer.with_capacity(capacity)
210 _buffer_pos = 0 # need to read
211 end
212 end
213
214 interface IOStream
215 super IStream
216 super OStream
217 end
218
219 ##############################################################"
220
221 abstract class FDStream
222 super IOS
223 # File description
224 var fd: Int
225
226 redef fun close do native_close(fd)
227
228 private fun native_close(i: Int): Int is extern "stream_FDStream_FDStream_native_close_1"
229 private fun native_read_char(i: Int): Int is extern "stream_FDStream_FDStream_native_read_char_1"
230 private fun native_read(i: Int, buf: NativeString, len: Int): Int is extern "stream_FDStream_FDStream_native_read_3"
231 private fun native_write(i: Int, buf: NativeString, len: Int): Int is extern "stream_FDStream_FDStream_native_write_3"
232 private fun native_write_char(i: Int, c: Char): Int is extern "stream_FDStream_FDStream_native_write_char_2"
233
234 init(fd: Int) do self.fd = fd
235 end
236
237 class FDIStream
238 super FDStream
239 super IStream
240 redef var eof: Bool = false
241
242 redef fun read_char
243 do
244 var nb = native_read_char(fd)
245 if nb == -1 then eof = true
246 return nb
247 end
248
249 init(fd: Int) do end
250 end
251
252 class FDOStream
253 super FDStream
254 super OStream
255 redef var is_writable: Bool
256
257 redef fun write(s)
258 do
259 var nb = native_write(fd, s.to_cstring, s.length)
260 if nb < s.length then is_writable = false
261 end
262
263 init(fd: Int)
264 do
265 is_writable = true
266 end
267 end
268
269 class FDIOStream
270 super FDIStream
271 super FDOStream
272 super IOStream
273 init(fd: Int)
274 do
275 self.fd = fd
276 is_writable = true
277 end
278 end
279
280 redef interface Object
281 # returns first available stream to read or write to
282 # return null on interruption (possibly a signal)
283 protected fun poll( streams : Sequence[FDStream] ) : nullable FDStream
284 do
285 var in_fds = new Array[Int]
286 var out_fds = new Array[Int]
287 var fd_to_stream = new HashMap[Int,FDStream]
288 for s in streams do
289 var fd = s.fd
290 if s isa FDIStream then in_fds.add( fd )
291 if s isa FDOStream then out_fds.add( fd )
292
293 fd_to_stream[fd] = s
294 end
295
296 var polled_fd = intern_poll( in_fds, out_fds )
297
298 if polled_fd == null then
299 return null
300 else
301 return fd_to_stream[polled_fd]
302 end
303 end
304
305 private fun intern_poll(in_fds: Array[Int], out_fds: Array[Int]) : nullable Int is extern import Array[Int].length, Array[Int].[], Int.as(nullable Int) `{
306 int in_len, out_len, total_len;
307 struct pollfd *c_fds;
308 sigset_t sigmask;
309 int i;
310 int first_polled_fd = -1;
311 int result;
312
313 in_len = Array_of_Int_length( in_fds );
314 out_len = Array_of_Int_length( out_fds );
315 total_len = in_len + out_len;
316 c_fds = malloc( sizeof(struct pollfd) * total_len );
317
318 /* input streams */
319 for ( i=0; i<in_len; i ++ ) {
320 int fd;
321 fd = Array_of_Int__index( in_fds, i );
322
323 c_fds[i].fd = fd;
324 c_fds[i].events = POLLIN;
325 }
326
327 /* output streams */
328 for ( i=0; i<out_len; i ++ ) {
329 int fd;
330 fd = Array_of_Int__index( out_fds, i );
331
332 c_fds[i].fd = fd;
333 c_fds[i].events = POLLOUT;
334 }
335
336 /* poll all fds, unlimited timeout */
337 result = poll( c_fds, total_len, -1 );
338
339 if ( result > 0 ) {
340 /* analyse results */
341 for ( i=0; i<total_len; i++ )
342 if ( c_fds[i].revents & c_fds[i].events || /* awaited event */
343 c_fds[i].revents & POLLHUP ) /* closed */
344 {
345 first_polled_fd = c_fds[i].fd;
346 break;
347 }
348
349 return Int_as_nullable( first_polled_fd );
350 }
351 else if ( result < 0 )
352 fprintf( stderr, "Error in Stream:poll: %s\n", strerror( errno ) );
353
354 return null_Int();
355 `}
356 end
357
358 # Stream to a String. Mainly used for compatibility with OStream type and tests.
359 class StringOStream
360 super OStream
361
362 private var content = new Array[String]
363 redef fun to_s do return content.to_s
364 redef fun is_writable do return true
365 redef fun write(str) do content.add(str)
366 end