Array.st
author claus
Fri, 16 Jul 1993 11:39:45 +0200
changeset 1 a27a279701f8
child 2 6526dde5f3ac
permissions -rw-r--r--
Initial revision

"
 COPYRIGHT (c) 1989-92 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.
"

ArrayedCollection variableSubclass:#Array
       instanceVariableNames:''
       classVariableNames:''
       poolDictionaries:''
       category:'Collections-Indexed'
!

Array comment:'

COPYRIGHT (c) 1989-92 by Claus Gittinger
              All Rights Reserved

Arrays store general objects; the size is fixed, so add/remove is not
allowed. Access to the elements is via an Integer index. Since Arrays
are used very often in the system, some methods have been tuned by
reimplementation as primitive.

%W% %E%

written spring 89 by claus
'!

!Array methodsFor:'resizing'!

grow:newSize
    (newSize ~~ self size) ifTrue:[
        self fixedSizeError
    ]
! !

!Array methodsFor:'accessing'!

size
    "return the number of indexed elements in the receiver"

%{  /* NOCONTEXT */

    RETURN ( _MKSMALLINT(_arraySize(self) - _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) ));
%}
!

at:index
    "return the indexed instance variable with index, anInteger
     - added here for speed"

%{  /* NOCONTEXT */

    REGISTER int indx;
    REGISTER int nIndex;

    if (_isSmallInteger(index)) {
        indx = _intVal(index) - 1;
	if (indx >= 0) {
            nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
            indx += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
            if (indx < nIndex) {
                RETURN ( _InstPtr(self)->i_instvars[indx] );
            }
        }
    }
%}
.
    ^ super at:index
!

at:index put:anObject
    "store the 2nd arg, anObject as indexed instvar with index, anInteger.
     - added here for speed"

%{  /* NOCONTEXT */

    REGISTER int indx;
    REGISTER int nIndex;

    if (_isSmallInteger(index)) {
        indx = _intVal(index) - 1;
        if (indx >= 0) {
            nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
            indx += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
            if (indx < nIndex) {
                _InstPtr(self)->i_instvars[indx] = anObject;
                __STORE(self, anObject);
                RETURN ( anObject );
            }
        }
    }
%}
.
    ^ super at:index put:anObject
! !

!Array methodsFor:'copying'!

copyWith:something
    "reimplemented for speed if receiver is an Array"
%{
    OBJ nObj;
    int mySize;
    int i, nIndex;
    OBJ *op;
    extern int newSpace;

    if (_qClass(self) == Array) {
        mySize = _qSize(self);
        _qAlignedNew(nObj, mySize + sizeof(OBJ), __context);
        _InstPtr(nObj)->o_class = Array;

	nIndex = (mySize - OHDR_SIZE) / sizeof(OBJ);
        /* created object is usually in newspace */
        if (_qSpace(nObj) == newSpace) {
            /* dont care for store */
#ifdef bcopy4
	    bcopy4(_ArrayInstPtr(self)->a_element, _ArrayInstPtr(nObj)->a_element, nIndex);
#else
            bcopy(_ArrayInstPtr(self)->a_element, _ArrayInstPtr(nObj)->a_element, mySize - OHDR_SIZE);
#endif
            _ArrayInstPtr(nObj)->a_element[nIndex] = something;
        } else {
            /* must take care of stores ... */
            op = _ArrayInstPtr(self)->a_element;
            for (i=0; i<nIndex; i++) {
                _ArrayInstPtr(nObj)->a_element[i] = *op;
               __STORE(nObj, *op);
               op++;
            }
            _ArrayInstPtr(nObj)->a_element[i] = something;
            __STORE(nObj, something);
	}
        RETURN ( nObj );
    }
%}
.
    ^ super copyWith:something
! !

!Array methodsFor:'filling & replacing'!

from:index1 to:index2 put:anObject
    "reimplemented for speed if receiver is an Array"

%{  /* NOCONTEXT */

    REGISTER int index;
    int nIndex;
    int endIndex;
    REGISTER OBJ *dst;

    if ((_qClass(self) == Array)
     && _isSmallInteger(index1)
     && _isSmallInteger(index2)) {
        index = _intVal(index1) - 1;
        if (index >= 0) {
            nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
            endIndex = _intVal(index2) - 1;
            if (endIndex < nIndex) {
                dst = &(_InstPtr(self)->i_instvars[index]);
#ifdef memset4
                memset4(dst, anObject, (endIndex-index+1));
                __STORE(self, anObject);
#else
                if ((INT)anObject == 0) {
                    memset(dst, 0, (endIndex-index+1) * sizeof(OBJ));
                } else {
                    for (; index <= endIndex; index++) {
                        *dst++ = anObject;
                    }
                    __STORE(self, anObject);
                }
#endif
                RETURN ( self );
            }
        }
    }
%}
.
    ^ super from:index1 to:index2 put:anObject
!

replaceFrom:start to:stop with:aCollection startingAt:repStart
    "reimplemented for speed if both receiver and aCollection are Arrays"

%{  /* NOCONTEXT */

    int nIndex, repNIndex;
    int startIndex, stopIndex;
    REGISTER OBJ *src;
    REGISTER OBJ *dst;
    int repStopIndex;
    REGISTER int repStartIndex;
    REGISTER OBJ t;
    REGISTER int count;
    extern int newSpace;

    if ((_qClass(self) == Array)
     && (_Class(aCollection) == Array)
     && _isSmallInteger(start)
     && _isSmallInteger(stop)
     && _isSmallInteger(repStart)) {
        startIndex = _intVal(start) - 1;
        if (startIndex >= 0) {
            nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
            stopIndex = _intVal(stop) - 1;
            count = stopIndex - startIndex + 1;
            if ((count > 0) && (stopIndex < nIndex)) {
                repStartIndex = _intVal(repStart) - 1;
                if (repStartIndex >= 0) {
                    repNIndex = (_qSize(aCollection) - OHDR_SIZE) / sizeof(OBJ);
                    repStopIndex = repStartIndex + (stopIndex - startIndex);
                    if (repStopIndex < repNIndex) {
                        src = &(_InstPtr(aCollection)->i_instvars[repStartIndex]);
                        dst = &(_InstPtr(self)->i_instvars[startIndex]);
                        if (aCollection == self) {
                            /* no need to check stores */
                            /* take care of overlapping copy */
                            if (src < dst) {
                                /* must do a reverse copy */
                                src += count;
                                dst += count;
                                while (count-- > 0) {
                                    *--dst = *--src;
                                }
                                RETURN ( self );
                            }
#ifdef bcopy4
			    bcopy4(src, dst, count);
#else
# ifdef FAST_MEMCPY
                            bcopy(src, dst, count*sizeof(OBJ));
# else
			    while (count >= 4) {
				MOVE4LONGS(src, dst);
				count -= 4;
			    }
                            while (count--) {
                                *dst++ = *src++;
                            }
# endif
#endif
                        } else {
                            /*
			     * no need for store-check, if dst is in newspace
                             */
                            if (_qSpace(self) == newSpace) {
#ifdef bcopy4
				bcopy4(src, dst, count);
#else
# ifdef FAST_MEMCPY
                                bcopy(src, dst, count*sizeof(OBJ));
# else
			        while (count >= 4) {
				    MOVE4LONGS(src, dst);
				    count -= 4;
			        }
                                while (count--) {
                                    *dst++ = *src++;
                                }
# endif
#endif
                            } else {
                                while (count-- > 0) {
                                    t = *src++;
                                    *dst++ = t;
                                    __STORE(self, t);
                                }
                            }
                        }
                        RETURN ( self );
                    }
                }
            }
        }
    }
%}
.
    ^ super replaceFrom:start to:stop with:aCollection startingAt:repStart
! !

!Array methodsFor:'testing'!

includes:anObject
    "return true, if the argument, anObject is contained in the array
     - reimplemented for speed"

    |element|

%{  /* NOCONTEXT */

    REGISTER int index;
    REGISTER OBJ o;
    int nIndex;

    nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
    index = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);

    /* quick check using == */
    o = anObject;
    while (index < nIndex) {
        if (_InstPtr(self)->i_instvars[index++] == o) {
            RETURN ( true );
	}
    }
    if (o == nil) {
	RETURN ( false );
    }
%}
.
%{
    REGISTER int index;
    int nIndex;
    extern OBJ __eq;
    static struct inlineCache eq = _ILC1;

    nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
    index = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);

    /* slow check using = */

    while (index < nIndex) {
        element = _InstPtr(self)->i_instvars[index++];
	if (element != nil) {
#ifdef PASS_ARG_REF
            if ((*eq.ilc_func)(anObject,__eq, CON_COMMA nil,&eq,&element)==true)
#else
            if ((*eq.ilc_func)(anObject,__eq, CON_COMMA nil,&eq,element)==true)
#endif
	    {
                RETURN ( true );
	    }
	}
    }
%}
.
    ^ false
!

indexOf:anElement startingAt:start
    "search the array for anElement; return index if found, 0 otherwise
     - reimplemented for speed"

    |element|
%{
    REGISTER int index;
    int nIndex, nInsts;
    extern OBJ __eq;
    static struct inlineCache eq = _ILC1;

    if (_isSmallInteger(start)) {
        index = _intVal(start) - 1;
        if (index >= 0) {
            nInsts = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
            index += nInsts;
            nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
	    if (anElement != nil) {
                while (index < nIndex) {
                    element = _InstPtr(self)->i_instvars[index++];
		    if (element != nil) {
                        if ((element == anElement) 
#ifdef PASS_ARG_REF
                         || ((*eq.ilc_func)(anElement,__eq, CON_COMMA nil,&eq,&element)
#else
                         || ((*eq.ilc_func)(anElement,__eq, CON_COMMA nil,&eq,element)
#endif
                                                                            == true)) {
                            RETURN ( _MKSMALLINT(index - nInsts) );
                        }
                    }
                }
	    } else {
		/* search for nil */
                while (index < nIndex) {
                    if (_InstPtr(self)->i_instvars[index++] == nil) {
                        RETURN ( _MKSMALLINT(index - nInsts) );
                    }
                }
            }
        }
    }
%}
.
    ^ 0
!

identityIndexOf:anElement startingAt:start
    "search the array for anElement; return index if found, 0 otherwise
     - reimplemented for speed"

%{  /* NOCONTEXT */

    REGISTER int index;
    REGISTER OBJ el;
    REGISTER OBJ *op;
    REGISTER int nIndex;
    int nInsts;

    if (_isSmallInteger(start)) {
        index = _intVal(start) - 1;
        if (index >= 0) {
            nInsts = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
            index += nInsts;
            nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
            el = anElement;
            op = & (_InstPtr(self)->i_instvars[index]);
            while (index++ < nIndex) {
                if (*op++ == el) {
                    RETURN ( _MKSMALLINT(index - nInsts) );
                }
            }
	    RETURN ( _MKSMALLINT(0) );
        }
    }
%}
.
    ^ super identityIndexOf:anElement startingAt:start
! !

!Array methodsFor:'enumeration'!

do:aBlock
    "evaluate the argument, aBlock for each element in the collection.
     - reimplemented for speed"

    |home element|
%{
    REGISTER OBJFUNC codeVal;
    REGISTER int index;
    int nIndex;
    extern OBJ _value_, Block;
    static struct inlineCache val = _ILC1;

    index = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
    nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
    if (_isBlock(aBlock)
     && ((codeVal = _BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil)
     && (_BlockInstPtr(aBlock)->b_nargs == _MKSMALLINT(1))) {
        home = _BlockInstPtr(aBlock)->b_home;
        for (; index < nIndex; index++) {
	    if (InterruptPending != nil) interruptL(__LINE__ COMMA_CON);

            element = _InstPtr(self)->i_instvars[index];
#ifdef PASS_ARG_REF
            (*codeVal)(home, CON_COMMA  &element);
#else
            (*codeVal)(home, CON_COMMA  element);
#endif
        } 
    } else {
        for (; index < nIndex; index++) {
	    if (InterruptPending != nil) interruptL(__LINE__ COMMA_CON);

            element = _InstPtr(self)->i_instvars[index];
#ifdef PASS_ARG_REF
            (*val.ilc_func)(aBlock, _value_, CON_COMMA  nil, &val, &element);
#else
            (*val.ilc_func)(aBlock, _value_, CON_COMMA  nil, &val, element);
#endif
        } 
    }
%}
.
    ^ self
!

reverseDo:aBlock
    "evaluate the argument, aBlock for each element in the collection in reverse order.
     - reimplemented for speed"

    |home element|
%{
    REGISTER OBJFUNC codeVal;
    REGISTER int index;
    int nIndex, endIndex;
    extern OBJ _value_, Block;
    static struct inlineCache val = _ILC1;

    endIndex = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
    nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
    if (_isBlock(aBlock)
     && ((codeVal = _BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil)
     && (_BlockInstPtr(aBlock)->b_nargs == _MKSMALLINT(1))) {
        home = _BlockInstPtr(aBlock)->b_home;
        for (index=nIndex-1; index >= endIndex; index--) {
	    if (InterruptPending != nil) interruptL(__LINE__ COMMA_CON);

            element = _InstPtr(self)->i_instvars[index];
#ifdef PASS_ARG_REF
            (*codeVal)(home, CON_COMMA  &element);
#else
            (*codeVal)(home, CON_COMMA  element);
#endif
        } 
    } else {
        for (index=nIndex=1; index >= endIndex; index--) {
	    if (InterruptPending != nil) interruptL(__LINE__ COMMA_CON);

            element = _InstPtr(self)->i_instvars[index];
#ifdef PASS_ARG_REF
            (*val.ilc_func)(aBlock, _value_, CON_COMMA  nil, &val, &element);
#else
            (*val.ilc_func)(aBlock, _value_, CON_COMMA  nil, &val, element);
#endif
        } 
    }
%}
.
    ^ self
!

from:start to:stop do:aBlock
    "evaluate the argument, aBlock for the elements starting at index start
     up to (and including) stop in the collection.
     - reimplemented for speed"

    |home element|
%{
    REGISTER OBJFUNC codeVal;
    REGISTER int index;
    int nIndex, nInsts;
    extern OBJ _value_, Block;
    static struct inlineCache val = _ILC1;
    int indexLow, indexHigh;

    if (_isSmallInteger(start) && _isSmallInteger(stop)) {
        indexLow = _intVal(start);
        if (indexLow > 0) {
            indexHigh = _intVal(stop);
            nInsts = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
            indexLow += nInsts;
            indexHigh += nInsts;
            nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
            if (indexHigh <= nIndex) {
                indexLow--;
                indexHigh--;
                if (_isBlock(aBlock)
                 && ((codeVal = _BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil)
                 && (_BlockInstPtr(aBlock)->b_nargs == _MKSMALLINT(1))) {
                    home = _BlockInstPtr(aBlock)->b_home;
                    for (index=indexLow; index <= indexHigh; index++) {
	    	        if (InterruptPending != nil) interruptL(__LINE__ COMMA_CON);

#ifdef PASS_ARG_REF
                        element = _InstPtr(self)->i_instvars[index];
                        (*codeVal)(home, CON_COMMA  &element);
#else
                        (*codeVal)(home, CON_COMMA  _InstPtr(self)->i_instvars[index]);
#endif
                    } 
                } else {
                    for (index=indexLow; index <= indexHigh; index++) {
	    	        if (InterruptPending != nil) interruptL(__LINE__ COMMA_CON);

                        element = _InstPtr(self)->i_instvars[index];
#ifdef PASS_ARG_REF
                        (*val.ilc_func)
                            (aBlock, _value_, CON_COMMA  nil, &val, &element);
#else
                        (*val.ilc_func)
                            (aBlock, _value_, CON_COMMA  nil, &val, element);
#endif
                    } 
                }
                RETURN ( self );
            }
        }
    }
%}
.
    ^ super from:start to:stop do:aBlock
!

nonNilElementsDo:aBlock
    "evaluate the argument, aBlock for each non-nil element"

    |home element|
%{
    REGISTER OBJFUNC codeVal;
    REGISTER int index;
    int nIndex;
    extern OBJ _value_, Block;
    static struct inlineCache val = _ILC1;

    index = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
    nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
    if (_isBlock(aBlock)
     && ((codeVal = _BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil)
     && (_BlockInstPtr(aBlock)->b_nargs == _MKSMALLINT(1))) {
        home = _BlockInstPtr(aBlock)->b_home;
        for (; index < nIndex; index++) {
	    if (InterruptPending != nil) interrupt(CONARG);

            element = _InstPtr(self)->i_instvars[index];
	    if (element != nil)
#ifdef PASS_ARG_REF
                (*codeVal)(home, CON_COMMA  &element);
#else
                (*codeVal)(home, CON_COMMA  element);
#endif
        } 
    } else {
        for (; index < nIndex; index++) {
	    if (InterruptPending != nil) interrupt(CONARG);

            element = _InstPtr(self)->i_instvars[index];
	    if (element != nil)
#ifdef PASS_ARG_REF
                (*val.ilc_func)(aBlock, _value_, CON_COMMA  nil, &val, &element);
#else
                (*val.ilc_func)(aBlock, _value_, CON_COMMA  nil, &val, element);
#endif
        } 
    }
%}
.
    ^ self
! !