|
1 " |
|
2 COPYRIGHT (c) 1988-93 by Claus Gittinger |
|
3 All Rights Reserved |
|
4 |
|
5 This software is furnished under a license and may be used |
|
6 only in accordance with the terms of that license and with the |
|
7 inclusion of the above copyright notice. This software may not |
|
8 be provided or otherwise made available to, or used by, any |
|
9 other person. No title to or ownership of the software is |
|
10 hereby transferred. |
|
11 " |
|
12 |
|
13 PositionableStream subclass:#ReadStream |
|
14 instanceVariableNames:'' |
|
15 classVariableNames:'' |
|
16 poolDictionaries:'' |
|
17 category:'Streams' |
|
18 ! |
|
19 |
|
20 ReadStream comment:' |
|
21 |
|
22 COPYRIGHT (c) 1988-93 by Claus Gittinger |
|
23 All Rights Reserved |
|
24 |
|
25 ReadStream defines protocol for reading streamwise over collections. Code here |
|
26 is specially tuned for streaming over strings. |
|
27 |
|
28 %W% %E% |
|
29 '! |
|
30 |
|
31 !ReadStream methodsFor:'access-reading'! |
|
32 |
|
33 peek |
|
34 "return the next element; do NOT advance read pointer. |
|
35 - reimplemented for speed on String-Streams" |
|
36 |
|
37 %{ /* NOCONTEXT */ |
|
38 |
|
39 REGISTER int pos; |
|
40 unsigned ch; |
|
41 |
|
42 if (_isString(_INST(collection)) |
|
43 && _isSmallInteger(_INST(position)) |
|
44 && _isSmallInteger(_INST(readLimit))) { |
|
45 |
|
46 pos = _intVal(_INST(position)); |
|
47 if (pos > _intVal(_INST(readLimit))) { |
|
48 RETURN ( nil ); |
|
49 } |
|
50 if ((pos > 0) |
|
51 && (pos < _qSize(_INST(collection)) - OHDR_SIZE)) { |
|
52 ch = _stringVal(_INST(collection))[pos-1]; |
|
53 RETURN ( _MKCHARACTER(ch) ); |
|
54 } |
|
55 } |
|
56 %} |
|
57 . |
|
58 (position > readLimit) ifTrue:[^ nil]. |
|
59 ^ collection at:position |
|
60 ! |
|
61 |
|
62 next |
|
63 "return the next element; advance read pointer. |
|
64 - reimplemented for speed on String-Streams" |
|
65 |
|
66 |ret| |
|
67 |
|
68 %{ /* NOCONTEXT */ |
|
69 |
|
70 REGISTER int pos; |
|
71 unsigned ch; |
|
72 |
|
73 if (_isString(_INST(collection)) |
|
74 && _isSmallInteger(_INST(position)) |
|
75 && _isSmallInteger(_INST(readLimit))) { |
|
76 |
|
77 pos = _intVal(_INST(position)); |
|
78 if (pos > _intVal(_INST(readLimit))) { |
|
79 RETURN ( nil ); |
|
80 } |
|
81 if ((pos > 0) |
|
82 && (pos < _qSize(_INST(collection)) - OHDR_SIZE)) { |
|
83 ch = _stringVal(_INST(collection))[pos-1]; |
|
84 _INST(position) = _MKSMALLINT(pos + 1); |
|
85 RETURN ( _MKCHARACTER(ch) ); |
|
86 } |
|
87 } |
|
88 %} |
|
89 . |
|
90 (position > readLimit) ifTrue:[^ nil]. |
|
91 ret := collection at:position. |
|
92 position := position + 1. |
|
93 ^ ret |
|
94 ! |
|
95 |
|
96 nextPeek |
|
97 "advance read pointer return the peek element. |
|
98 this is equivalent to (self next; peek). |
|
99 - reimplemented for speed on String-Streams" |
|
100 |
|
101 %{ /* NOCONTEXT */ |
|
102 |
|
103 if (_isString(_INST(collection)) |
|
104 && _isSmallInteger(_INST(position)) |
|
105 && _isSmallInteger(_INST(readLimit))) { |
|
106 REGISTER int pos; |
|
107 unsigned ch; |
|
108 |
|
109 pos = _intVal(_INST(position)); |
|
110 if (pos > _intVal(_INST(readLimit))) { |
|
111 RETURN ( nil ); |
|
112 } |
|
113 if ((pos > 0) |
|
114 && (pos < _qSize(_INST(collection)) - OHDR_SIZE)) { |
|
115 _INST(position) = _MKSMALLINT(pos + 1); |
|
116 pos = pos + 1; |
|
117 if (pos < _qSize(_INST(collection)) - OHDR_SIZE) { |
|
118 ch = _stringVal(_INST(collection))[pos-1]; |
|
119 RETURN ( _MKCHARACTER(ch) ); |
|
120 } |
|
121 RETURN ( nil ); |
|
122 } |
|
123 } |
|
124 %} |
|
125 . |
|
126 (position > readLimit) ifTrue:[^ nil]. |
|
127 position := position + 1. |
|
128 (position > readLimit) ifTrue:[^ nil]. |
|
129 ^ collection at:position |
|
130 ! |
|
131 |
|
132 nextDecimalInteger |
|
133 "read the next integer in radix 10. dont skip whitespace. |
|
134 - reimplemented for speed on String-Streams" |
|
135 |
|
136 |value| |
|
137 %{ |
|
138 int pos, limit, sz; |
|
139 register unsigned char *cp; |
|
140 register unsigned ch; |
|
141 int val = 0; |
|
142 |
|
143 if (_isString(_INST(collection)) |
|
144 && _isSmallInteger(_INST(position)) |
|
145 && _isSmallInteger(_INST(readLimit))) { |
|
146 pos = _intVal(_INST(position)); |
|
147 limit = _intVal(_INST(readLimit)); |
|
148 sz = _qSize(_INST(collection)) - OHDR_SIZE; |
|
149 if (sz < limit) |
|
150 limit = sz; |
|
151 cp = _stringVal(_INST(collection)) + pos - 1; |
|
152 |
|
153 for (;;) { |
|
154 if (pos > limit) break; |
|
155 ch = *cp; |
|
156 |
|
157 if ((ch < '0') || (ch > '9')) break; |
|
158 val = val * 10 + (ch - '0'); |
|
159 pos++; |
|
160 if (val > (_MAX_INT / 10)) goto oops; |
|
161 cp++; |
|
162 } |
|
163 _INST(position) = _MKSMALLINT(pos); |
|
164 return _MKSMALLINT(val); |
|
165 } |
|
166 oops: |
|
167 value = _MKSMALLINT(val); |
|
168 %} |
|
169 . |
|
170 [self peek notNil and:[self peek isDigitRadix:10]] whileTrue:[ |
|
171 value = (value * 10) + self peek digitValue. |
|
172 self next |
|
173 ]. |
|
174 ^ value |
|
175 ! |
|
176 |
|
177 nextWord |
|
178 "read the next word (i.e. up to non letter-or-digit). |
|
179 return a string containing those characters. |
|
180 - reimplemented for speed on String-Streams" |
|
181 %{ |
|
182 /* speedup, if collection is a string */ |
|
183 |
|
184 int pos, limit, sz; |
|
185 int len; |
|
186 char buffer[1024]; |
|
187 register unsigned char *cp; |
|
188 register unsigned ch; |
|
189 |
|
190 if (_isString(_INST(collection)) |
|
191 && _isSmallInteger(_INST(position)) |
|
192 && _isSmallInteger(_INST(readLimit))) { |
|
193 pos = _intVal(_INST(position)); |
|
194 limit = _intVal(_INST(readLimit)); |
|
195 sz = _qSize(_INST(collection)) - OHDR_SIZE; |
|
196 if (sz < limit) |
|
197 limit = sz; |
|
198 cp = _stringVal(_INST(collection)) + pos - 1; |
|
199 |
|
200 for (;;) { |
|
201 if (pos > limit) break; |
|
202 ch = *cp; |
|
203 |
|
204 if (ch > ' ') break; |
|
205 if ((ch != ' ') && (ch != '\t') && (ch != '\r') |
|
206 && (ch != '\n') && (ch != 0x0b)) break; |
|
207 cp++; |
|
208 pos++; |
|
209 } |
|
210 |
|
211 len = 0; |
|
212 for (;;) { |
|
213 if (pos > limit) break; |
|
214 ch = *cp & 0xFF; |
|
215 |
|
216 if (! (((ch >= 'a') && (ch <= 'z')) || |
|
217 ((ch >= 'A') && (ch <= 'Z')) || |
|
218 ((ch >= '0') && (ch <= '9')))) |
|
219 break; |
|
220 buffer[len++] = ch; |
|
221 if (len >= (sizeof(buffer)-1)) { |
|
222 /* emergency */ |
|
223 break; |
|
224 } |
|
225 pos++; |
|
226 cp++; |
|
227 } |
|
228 |
|
229 _INST(position) = _MKSMALLINT(pos); |
|
230 buffer[len] = '\0'; |
|
231 RETURN ( (len != 0) ? _MKSTRING(buffer COMMA_CON) : nil ); |
|
232 } |
|
233 %} |
|
234 . |
|
235 ^ super nextWord |
|
236 ! |
|
237 |
|
238 nextSymbol |
|
239 "read the next selector-symbol (i.e. up to non letter-or-digit). |
|
240 return a string containing those characters. |
|
241 - reimplemented for speed on String-Streams" |
|
242 %{ |
|
243 int pos, limit, sz; |
|
244 int len; |
|
245 char buffer[1024]; |
|
246 register unsigned char *cp; |
|
247 register unsigned ch; |
|
248 |
|
249 if (_isString(_INST(collection)) |
|
250 && _isSmallInteger(_INST(position)) |
|
251 && _isSmallInteger(_INST(readLimit))) { |
|
252 pos = _intVal(_INST(position)); |
|
253 limit = _intVal(_INST(readLimit)); |
|
254 sz = _qSize(_INST(collection)) - OHDR_SIZE; |
|
255 if (sz < limit) |
|
256 limit = sz; |
|
257 cp = _stringVal(_INST(collection)) + pos - 1; |
|
258 |
|
259 len = 0; |
|
260 for (;;) { |
|
261 if (pos > limit) break; |
|
262 ch = *cp; |
|
263 |
|
264 if (! (((ch >= 'a') && (ch <= 'z')) || |
|
265 ((ch >= 'A') && (ch <= 'Z')) || |
|
266 ((ch >= '0') && (ch <= '9')) || |
|
267 (ch == ':'))) |
|
268 break; |
|
269 buffer[len++] = ch; |
|
270 if (len >= (sizeof(buffer)-1)) { |
|
271 /* emergency */ |
|
272 break; |
|
273 } |
|
274 pos++; |
|
275 cp++; |
|
276 } |
|
277 |
|
278 _INST(position) = _MKSMALLINT(pos); |
|
279 buffer[len] = '\0'; |
|
280 RETURN ( (len != 0) ? _MKSTRING(buffer COMMA_CON) : nil ); |
|
281 } |
|
282 %} |
|
283 . |
|
284 ^ super nextSymbol |
|
285 ! |
|
286 |
|
287 skipFor:anObject |
|
288 "skip all objects up-to and including anObject; |
|
289 return the element after anObject. |
|
290 - reimplemented for speed on String-Streams" |
|
291 |
|
292 %{ /* NOCONTEXT */ |
|
293 |
|
294 if (_isString(_INST(collection)) |
|
295 && _isCharacter(anObject) |
|
296 && _isSmallInteger(_INST(position)) |
|
297 && _isSmallInteger(_INST(readLimit))) { |
|
298 REGISTER unsigned char *chars; |
|
299 REGISTER int pos, limit; |
|
300 unsigned ch; |
|
301 |
|
302 pos = _intVal(_INST(position)); |
|
303 if (pos <= 0) { |
|
304 RETURN ( nil ); |
|
305 } |
|
306 |
|
307 limit = _intVal(_INST(readLimit)); |
|
308 if (limit > (_qSize(_INST(collection)) - OHDR_SIZE)) |
|
309 limit = _qSize(_INST(collection)) - OHDR_SIZE; |
|
310 |
|
311 chars = (unsigned char *)(_stringVal(_INST(collection)) + pos - 1); |
|
312 ch = _intVal(_characterVal(anObject)) & 0xFF; |
|
313 while (pos < limit) { |
|
314 if (*chars == ch) { |
|
315 ch = *++chars; |
|
316 pos++; |
|
317 _INST(position) = _MKSMALLINT(pos); |
|
318 RETURN ( _MKCHARACTER(ch) ); |
|
319 } |
|
320 chars++; |
|
321 pos++; |
|
322 } |
|
323 _INST(position) = _MKSMALLINT(pos); |
|
324 RETURN ( nil ); |
|
325 } |
|
326 %} |
|
327 . |
|
328 ^ super skipFor:anObject |
|
329 ! |
|
330 |
|
331 |
|
332 skipSeparators |
|
333 "skip all whitespace; next will return next non-white-space element. |
|
334 - reimplemented for speed on String-Streams" |
|
335 |
|
336 %{ /* NOCONTEXT */ |
|
337 |
|
338 if (_isString(_INST(collection)) |
|
339 && _isSmallInteger(_INST(position)) |
|
340 && _isSmallInteger(_INST(readLimit))) { |
|
341 REGISTER unsigned char *chars; |
|
342 REGISTER unsigned ch; |
|
343 REGISTER int pos; |
|
344 int limit; |
|
345 |
|
346 pos = _intVal(_INST(position)); |
|
347 if (pos <= 0) { |
|
348 RETURN ( nil ); |
|
349 } |
|
350 |
|
351 limit = _intVal(_INST(readLimit)); |
|
352 if (limit > (_qSize(_INST(collection)) - OHDR_SIZE)) |
|
353 limit = _qSize(_INST(collection)) - OHDR_SIZE; |
|
354 |
|
355 chars = (unsigned char *)(_stringVal(_INST(collection)) + pos - 1); |
|
356 while (pos <= limit) { |
|
357 ch = *chars++; |
|
358 if ((ch != ' ') && (ch != '\t') && (ch != '\r') |
|
359 && (ch != '\n') && (ch != 0x0B)) { |
|
360 _INST(position) = _MKSMALLINT(pos); |
|
361 RETURN ( _MKCHARACTER(ch) ); |
|
362 } |
|
363 pos++; |
|
364 } |
|
365 _INST(position) = _MKSMALLINT(pos); |
|
366 RETURN ( nil ); |
|
367 } |
|
368 %} |
|
369 . |
|
370 ^ super skipSeparators |
|
371 ! |
|
372 |
|
373 skipToAll:aCollection |
|
374 "skip for the sequence given by the argument, aCollection; |
|
375 return nil if not found, self otherwise. On a successful match, next read |
|
376 will return elements of aCollection." |
|
377 |
|
378 |oldPos buffer l first idx| |
|
379 |
|
380 oldPos := self position. |
|
381 l := aCollection size. |
|
382 first := aCollection at:1. |
|
383 [self atEnd] whileFalse:[ |
|
384 buffer := self next:l. |
|
385 buffer = aCollection ifTrue:[ |
|
386 self position:(self position - l). |
|
387 ^ self |
|
388 ]. |
|
389 idx := buffer indexOf:first startingAt:2. |
|
390 idx == 0 ifFalse:[ |
|
391 self position:(self position - l + idx - 1) |
|
392 ] |
|
393 ]. |
|
394 self position:oldPos. |
|
395 ^ nil |
|
396 |
|
397 "|s| |
|
398 s := ReadStream on:'12345678901234567890'. |
|
399 s skipToAll:'901'. |
|
400 s next:4" |
|
401 ! ! |
|
402 |
|
403 !ReadStream methodsFor:'access-writing'! |
|
404 |
|
405 nextPut:anElement |
|
406 ^ self error:'ReadStreams cannot write' |
|
407 ! ! |