"
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.
$Header: /cvs/stx/stx/libbasic/Attic/ReadStr.st,v 1.3 1993-10-13 00:17:13 claus Exp $
'!
!ReadStream methodsFor:'access-reading'!
peek
"return the next element; do NOT advance read pointer.
return nil, if there is no next element.
- reimplemented for speed on String/ByteArray-Streams"
%{ /* NOCONTEXT */
REGISTER int pos;
unsigned ch;
OBJ coll;
extern OBJ ByteArray;
coll = _INST(collection);
if (_isNonNilObject(coll)
&& ((_qClass(coll) == String) || (_qClass(coll) == ByteArray))
&& _isSmallInteger(_INST(position))
&& _isSmallInteger(_INST(readLimit))) {
pos = _intVal(_INST(position));
if (pos > _intVal(_INST(readLimit))) {
RETURN ( nil );
}
if (pos > 0) {
if (_qClass(coll) == String) {
if (pos < (_qSize(coll) - OHDR_SIZE)) {
ch = _stringVal(coll)[pos-1];
RETURN ( _MKCHARACTER(ch) );
}
} else {
if (pos <= (_qSize(coll) - OHDR_SIZE)) {
ch = _ByteArrayInstPtr(coll)->ba_element[pos-1];
RETURN ( _MKSMALLINT(ch) );
}
}
}
}
%}
.
(position > readLimit) ifTrue:[^ nil].
^ collection at:position
!
next
"return the next element; advance read pointer.
return nil, if there is no next element.
- reimplemented for speed on String/ByteArray-Streams"
|ret|
%{ /* NOCONTEXT */
REGISTER int pos;
unsigned ch;
OBJ coll;
extern OBJ ByteArray;
coll = _INST(collection);
if (_isNonNilObject(coll)
&& ((_qClass(coll) == String) || (_qClass(coll) == ByteArray))
&& _isSmallInteger(_INST(position))
&& _isSmallInteger(_INST(readLimit))) {
pos = _intVal(_INST(position));
if (pos > _intVal(_INST(readLimit))) {
RETURN ( nil );
}
if (pos > 0) {
if (_qClass(coll) == String) {
if (pos < (_qSize(coll) - OHDR_SIZE)) {
_INST(position) = _MKSMALLINT(pos + 1);
ch = _stringVal(coll)[pos-1];
RETURN ( _MKCHARACTER(ch) );
}
} else {
if (pos <= (_qSize(coll) - OHDR_SIZE)) {
_INST(position) = _MKSMALLINT(pos + 1);
ch = _ByteArrayInstPtr(coll)->ba_element[pos-1];
RETURN ( _MKSMALLINT(ch) );
}
}
}
}
%}
.
(position > readLimit) ifTrue:[^ nil].
ret := collection at:position.
position := position + 1.
^ ret
!
next:count
"return the next count elements of the stream as a collection.
Redefined, to return a collection of the streamed-upon collections
species."
|coll|
coll := (collection species) new:count.
1 to:count do: [:index |
coll at:index put:(self next)
].
^ coll
!
nextPeek
"advance read pointer return the peek element.
this is equivalent to (self next; peek).
- reimplemented for speed on String-Streams for faster scanning"
%{ /* 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 for faster scanning"
|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 for faster scanning"
%{
/* 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 for faster scanning"
%{
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
!
nextMatchFor:anObject
"skip all objects up-to and including anObject, return anObject on success,
nil if end-of-file is reached before. The next read operation will return
the element after anObject.
- reimplemented for speed on String-Streams for faster scanning"
%{ /* 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 ( anObject );
}
chars++;
pos++;
}
RETURN ( nil );
}
%}
.
^ super nextMatchFor:anObject
!
skipSeparators
"skip all whitespace; next will return next non-white-space element.
- reimplemented for speed on String-Streams for faster scanning"
%{ /* 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'
! !