45c9f3253def806d60a4e81d3b880968e24dec0c
[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 # This module handle abstract input and output streams
14 package stream
15
16 import string
17
18 # Abstract stream class
19 class IOS
20 # close the stream
21 meth close is abstract
22 end
23
24 # Abstract input streams
25 class IStream
26 special IOS
27 # Read a character. Return its ASCII value, -1 on EOF or timeout
28 meth read_char: Int is abstract
29
30 # Read at most i bytes
31 meth 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 meth 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 meth 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 meth append_line_to(s: Buffer)
66 do
67 while true do
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 meth eof: Bool is abstract
81 end
82
83 # Abstract output stream
84 class OStream
85 special IOS
86 # write a string
87 meth write(s: String) is abstract
88
89 # Can the stream be used to write
90 meth is_writable: Bool is abstract
91 end
92
93 # Input streams with a buffer
94 class BufferedIStream
95 special IStream
96 redef meth read_char
97 do
98 assert not eof
99 if _buffer_pos >= _buffer.length then
100 fill_buffer
101 end
102 if _buffer_pos >= _buffer.length then
103 return -1
104 end
105 var c = _buffer[_buffer_pos]
106 _buffer_pos += 1
107 return c.ascii
108 end
109
110 redef meth read(i)
111 do
112 var s = new Buffer.with_capacity(i)
113 var j = _buffer_pos
114 var k = _buffer.length
115 while i > 0 do
116 if j >= k then
117 fill_buffer
118 if eof then return s.to_s
119 j = _buffer_pos
120 k = _buffer.length
121 end
122 while j < k and i > 0 do
123 s.add(_buffer[j])
124 j += 1
125 i -= 1
126 end
127 end
128 _buffer_pos = j
129 return s.to_s
130 end
131
132 redef meth read_all
133 do
134 var s = new Buffer
135 while not eof do
136 var j = _buffer_pos
137 var k = _buffer.length
138 while j < k do
139 s.add(_buffer[j])
140 j += 1
141 end
142 _buffer_pos = j
143 fill_buffer
144 end
145 return s.to_s
146 end
147
148 redef meth append_line_to(s)
149 do
150 while true do
151 # First phase: look for a '\n'
152 var i = _buffer_pos
153 while i < _buffer.length and _buffer[i] != '\n' do i += 1
154
155 # if there is something to append
156 if i > _buffer_pos then
157 # Enlarge the string (if needed)
158 s.enlarge(s.length + i - _buffer_pos)
159
160 # Copy from the buffer to the string
161 var j = _buffer_pos
162 while j < i do
163 s.add(_buffer[j])
164 j += 1
165 end
166 end
167
168 if i < _buffer.length then
169 # so \n is in _buffer[i]
170 _buffer_pos = i + 1 # skip \n
171 return
172 else
173 # so \n is not found
174 _buffer_pos = i
175 if end_reached then
176 return
177 else
178 fill_buffer
179 end
180 end
181 end
182 end
183
184 redef meth eof do return _buffer_pos >= _buffer.length and end_reached
185
186 # The buffer
187 attr _buffer: nullable Buffer = null
188
189 # The current position in the buffer
190 attr _buffer_pos: Int = 0
191
192 # Fill the buffer
193 protected meth fill_buffer is abstract
194
195 # Is the last fill_buffer reach the end
196 protected meth end_reached: Bool is abstract
197
198 # Allocate a `_buffer' for a given `capacity'.
199 protected meth prepare_buffer(capacity: Int)
200 do
201 _buffer = new Buffer.with_capacity(capacity)
202 _buffer_pos = 0 # need to read
203 end
204 end
205
206 class IOStream
207 special IStream
208 special OStream
209 end
210
211 ##############################################################"
212
213 class FDStream
214 special IOS
215 # File description
216 attr _fd: Int
217
218 redef meth close do native_close(_fd)
219
220 private meth native_close(i: Int): Int is extern "stream_FDStream_FDStream_native_close_1"
221 private meth native_read_char(i: Int): Int is extern "stream_FDStream_FDStream_native_read_char_1"
222 private meth native_read(i: Int, buf: NativeString, len: Int): Int is extern "stream_FDStream_FDStream_native_read_3"
223 private meth native_write(i: Int, buf: NativeString, len: Int): Int is extern "stream_FDStream_FDStream_native_write_3"
224
225 init(fd: Int) do _fd = fd
226 end
227
228 class FDIStream
229 special FDStream
230 special IStream
231 redef readable attr _eof: Bool = false
232
233 redef meth read_char
234 do
235 var nb = native_read_char(_fd)
236 if nb == -1 then _eof = true
237 return nb
238 end
239
240 init(fd: Int) do end
241 end
242
243 class FDOStream
244 special FDStream
245 special OStream
246 redef readable attr _is_writable: Bool
247
248 redef meth write(s)
249 do
250 var nb = native_write(_fd, s.to_cstring, s.length)
251 if nb < s.length then _is_writable = false
252 end
253
254 init(fd: Int)
255 do
256 _is_writable = true
257 end
258 end
259
260 class FDIOStream
261 special FDIStream
262 special FDOStream
263 special IOStream
264 init(fd: Int)
265 do
266 _fd = fd
267 _is_writable = true
268 end
269 end