ReadStr.st
author claus
Wed, 13 Oct 1993 01:19:00 +0100
changeset 3 24d81bf47225
parent 2 6526dde5f3ac
child 5 67342904af11
permissions -rw-r--r--
*** empty log message ***

"
 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'
! !