--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ReadStream.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,407 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+PositionableStream subclass:#ReadStream
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Streams'
+!
+
+ReadStream comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ReadStream defines protocol for reading streamwise over collections. Code here
+is specially tuned for streaming over strings.
+
+%W% %E%
+'!
+
+!ReadStream methodsFor:'access-reading'!
+
+peek
+ "return the next element; do NOT advance read pointer.
+ - reimplemented for speed on String-Streams"
+
+%{ /* NOCONTEXT */
+
+ REGISTER int pos;
+ unsigned ch;
+
+ if (_isString(_INST(collection))
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+
+ pos = _intVal(_INST(position));
+ if (pos > _intVal(_INST(readLimit))) {
+ RETURN ( nil );
+ }
+ if ((pos > 0)
+ && (pos < _qSize(_INST(collection)) - OHDR_SIZE)) {
+ ch = _stringVal(_INST(collection))[pos-1];
+ RETURN ( _MKCHARACTER(ch) );
+ }
+ }
+%}
+.
+ (position > readLimit) ifTrue:[^ nil].
+ ^ collection at:position
+!
+
+next
+ "return the next element; advance read pointer.
+ - reimplemented for speed on String-Streams"
+
+ |ret|
+
+%{ /* NOCONTEXT */
+
+ REGISTER int pos;
+ unsigned ch;
+
+ if (_isString(_INST(collection))
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+
+ pos = _intVal(_INST(position));
+ if (pos > _intVal(_INST(readLimit))) {
+ RETURN ( nil );
+ }
+ if ((pos > 0)
+ && (pos < _qSize(_INST(collection)) - OHDR_SIZE)) {
+ ch = _stringVal(_INST(collection))[pos-1];
+ _INST(position) = _MKSMALLINT(pos + 1);
+ RETURN ( _MKCHARACTER(ch) );
+ }
+ }
+%}
+.
+ (position > readLimit) ifTrue:[^ nil].
+ ret := collection at:position.
+ position := position + 1.
+ ^ ret
+!
+
+nextPeek
+ "advance read pointer return the peek element.
+ this is equivalent to (self next; peek).
+ - reimplemented for speed on String-Streams"
+
+%{ /* NOCONTEXT */
+
+ if (_isString(_INST(collection))
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+ REGISTER int pos;
+ unsigned ch;
+
+ pos = _intVal(_INST(position));
+ if (pos > _intVal(_INST(readLimit))) {
+ RETURN ( nil );
+ }
+ if ((pos > 0)
+ && (pos < _qSize(_INST(collection)) - OHDR_SIZE)) {
+ _INST(position) = _MKSMALLINT(pos + 1);
+ pos = pos + 1;
+ if (pos < _qSize(_INST(collection)) - OHDR_SIZE) {
+ ch = _stringVal(_INST(collection))[pos-1];
+ RETURN ( _MKCHARACTER(ch) );
+ }
+ RETURN ( nil );
+ }
+ }
+%}
+.
+ (position > readLimit) ifTrue:[^ nil].
+ position := position + 1.
+ (position > readLimit) ifTrue:[^ nil].
+ ^ collection at:position
+!
+
+nextDecimalInteger
+ "read the next integer in radix 10. dont skip whitespace.
+ - reimplemented for speed on String-Streams"
+
+ |value|
+%{
+ int pos, limit, sz;
+ register unsigned char *cp;
+ register unsigned ch;
+ int val = 0;
+
+ if (_isString(_INST(collection))
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+ pos = _intVal(_INST(position));
+ limit = _intVal(_INST(readLimit));
+ sz = _qSize(_INST(collection)) - OHDR_SIZE;
+ if (sz < limit)
+ limit = sz;
+ cp = _stringVal(_INST(collection)) + pos - 1;
+
+ for (;;) {
+ if (pos > limit) break;
+ ch = *cp;
+
+ if ((ch < '0') || (ch > '9')) break;
+ val = val * 10 + (ch - '0');
+ pos++;
+ if (val > (_MAX_INT / 10)) goto oops;
+ cp++;
+ }
+ _INST(position) = _MKSMALLINT(pos);
+ return _MKSMALLINT(val);
+ }
+oops:
+ value = _MKSMALLINT(val);
+%}
+.
+ [self peek notNil and:[self peek isDigitRadix:10]] whileTrue:[
+ value = (value * 10) + self peek digitValue.
+ self next
+ ].
+ ^ value
+!
+
+nextWord
+ "read the next word (i.e. up to non letter-or-digit).
+ return a string containing those characters.
+ - reimplemented for speed on String-Streams"
+%{
+ /* speedup, if collection is a string */
+
+ int pos, limit, sz;
+ int len;
+ char buffer[1024];
+ register unsigned char *cp;
+ register unsigned ch;
+
+ if (_isString(_INST(collection))
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+ pos = _intVal(_INST(position));
+ limit = _intVal(_INST(readLimit));
+ sz = _qSize(_INST(collection)) - OHDR_SIZE;
+ if (sz < limit)
+ limit = sz;
+ cp = _stringVal(_INST(collection)) + pos - 1;
+
+ for (;;) {
+ if (pos > limit) break;
+ ch = *cp;
+
+ if (ch > ' ') break;
+ if ((ch != ' ') && (ch != '\t') && (ch != '\r')
+ && (ch != '\n') && (ch != 0x0b)) break;
+ cp++;
+ pos++;
+ }
+
+ len = 0;
+ for (;;) {
+ if (pos > limit) break;
+ ch = *cp & 0xFF;
+
+ if (! (((ch >= 'a') && (ch <= 'z')) ||
+ ((ch >= 'A') && (ch <= 'Z')) ||
+ ((ch >= '0') && (ch <= '9'))))
+ break;
+ buffer[len++] = ch;
+ if (len >= (sizeof(buffer)-1)) {
+ /* emergency */
+ break;
+ }
+ pos++;
+ cp++;
+ }
+
+ _INST(position) = _MKSMALLINT(pos);
+ buffer[len] = '\0';
+ RETURN ( (len != 0) ? _MKSTRING(buffer COMMA_CON) : nil );
+ }
+%}
+.
+ ^ super nextWord
+!
+
+nextSymbol
+ "read the next selector-symbol (i.e. up to non letter-or-digit).
+ return a string containing those characters.
+ - reimplemented for speed on String-Streams"
+%{
+ int pos, limit, sz;
+ int len;
+ char buffer[1024];
+ register unsigned char *cp;
+ register unsigned ch;
+
+ if (_isString(_INST(collection))
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+ pos = _intVal(_INST(position));
+ limit = _intVal(_INST(readLimit));
+ sz = _qSize(_INST(collection)) - OHDR_SIZE;
+ if (sz < limit)
+ limit = sz;
+ cp = _stringVal(_INST(collection)) + pos - 1;
+
+ len = 0;
+ for (;;) {
+ if (pos > limit) break;
+ ch = *cp;
+
+ if (! (((ch >= 'a') && (ch <= 'z')) ||
+ ((ch >= 'A') && (ch <= 'Z')) ||
+ ((ch >= '0') && (ch <= '9')) ||
+ (ch == ':')))
+ break;
+ buffer[len++] = ch;
+ if (len >= (sizeof(buffer)-1)) {
+ /* emergency */
+ break;
+ }
+ pos++;
+ cp++;
+ }
+
+ _INST(position) = _MKSMALLINT(pos);
+ buffer[len] = '\0';
+ RETURN ( (len != 0) ? _MKSTRING(buffer COMMA_CON) : nil );
+ }
+%}
+.
+ ^ super nextSymbol
+!
+
+skipFor:anObject
+ "skip all objects up-to and including anObject;
+ return the element after anObject.
+ - reimplemented for speed on String-Streams"
+
+%{ /* NOCONTEXT */
+
+ if (_isString(_INST(collection))
+ && _isCharacter(anObject)
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+ REGISTER unsigned char *chars;
+ REGISTER int pos, limit;
+ unsigned ch;
+
+ pos = _intVal(_INST(position));
+ if (pos <= 0) {
+ RETURN ( nil );
+ }
+
+ limit = _intVal(_INST(readLimit));
+ if (limit > (_qSize(_INST(collection)) - OHDR_SIZE))
+ limit = _qSize(_INST(collection)) - OHDR_SIZE;
+
+ chars = (unsigned char *)(_stringVal(_INST(collection)) + pos - 1);
+ ch = _intVal(_characterVal(anObject)) & 0xFF;
+ while (pos < limit) {
+ if (*chars == ch) {
+ ch = *++chars;
+ pos++;
+ _INST(position) = _MKSMALLINT(pos);
+ RETURN ( _MKCHARACTER(ch) );
+ }
+ chars++;
+ pos++;
+ }
+ _INST(position) = _MKSMALLINT(pos);
+ RETURN ( nil );
+ }
+%}
+.
+ ^ super skipFor:anObject
+!
+
+
+skipSeparators
+ "skip all whitespace; next will return next non-white-space element.
+ - reimplemented for speed on String-Streams"
+
+%{ /* NOCONTEXT */
+
+ if (_isString(_INST(collection))
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+ REGISTER unsigned char *chars;
+ REGISTER unsigned ch;
+ REGISTER int pos;
+ int limit;
+
+ pos = _intVal(_INST(position));
+ if (pos <= 0) {
+ RETURN ( nil );
+ }
+
+ limit = _intVal(_INST(readLimit));
+ if (limit > (_qSize(_INST(collection)) - OHDR_SIZE))
+ limit = _qSize(_INST(collection)) - OHDR_SIZE;
+
+ chars = (unsigned char *)(_stringVal(_INST(collection)) + pos - 1);
+ while (pos <= limit) {
+ ch = *chars++;
+ if ((ch != ' ') && (ch != '\t') && (ch != '\r')
+ && (ch != '\n') && (ch != 0x0B)) {
+ _INST(position) = _MKSMALLINT(pos);
+ RETURN ( _MKCHARACTER(ch) );
+ }
+ pos++;
+ }
+ _INST(position) = _MKSMALLINT(pos);
+ RETURN ( nil );
+ }
+%}
+.
+ ^ super skipSeparators
+!
+
+skipToAll:aCollection
+ "skip for the sequence given by the argument, aCollection;
+ return nil if not found, self otherwise. On a successful match, next read
+ will return elements of aCollection."
+
+ |oldPos buffer l first idx|
+
+ oldPos := self position.
+ l := aCollection size.
+ first := aCollection at:1.
+ [self atEnd] whileFalse:[
+ buffer := self next:l.
+ buffer = aCollection ifTrue:[
+ self position:(self position - l).
+ ^ self
+ ].
+ idx := buffer indexOf:first startingAt:2.
+ idx == 0 ifFalse:[
+ self position:(self position - l + idx - 1)
+ ]
+ ].
+ self position:oldPos.
+ ^ nil
+
+ "|s|
+ s := ReadStream on:'12345678901234567890'.
+ s skipToAll:'901'.
+ s next:4"
+! !
+
+!ReadStream methodsFor:'access-writing'!
+
+nextPut:anElement
+ ^ self error:'ReadStreams cannot write'
+! !