CharacterWriteStream.st
author Jan Vrany <jan.vrany@fit.cvut.cz>
Wed, 01 Apr 2015 10:20:10 +0100
branchjv
changeset 18120 e3a375d5f6a8
parent 18066 89d51443ba6f
parent 17622 553ee52df434
child 18274 042d13555f1f
permissions -rw-r--r--
Merged cb7a12afe736 and 86dd565344f0 (branch default - CVS HEAD)

"{ Encoding: utf8 }"

"
 COPYRIGHT (c) 2005 by eXept Software AG
              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.
"
"{ Package: 'stx:libbasic' }"

"{ NameSpace: Smalltalk }"

WriteStream subclass:#CharacterWriteStream
	instanceVariableNames:'currentCharacterSize'
	classVariableNames:''
	poolDictionaries:''
	category:'Streams'
!

!CharacterWriteStream class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 2005 by eXept Software AG
              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.
"
!

documentation
"
    This is a WriteStream, which automagically changes the underlying collection,
    if a character does fit into the current collection.

    String -> Unicode16String -> Unicode32Sting

    [author:]
        Stefan Vogel (stefan@zwerg)

    [instance variables:]

    [class variables:]

    [see also:]
        String Unicode16String Unicode32Sting
"
!

examples
"

                                                                [exBegin]
    |stream|

    stream := CharacterWriteStream on:(String new:32).
    stream nextPutAll:'abc'.
    stream nextPut:(Character value:16r2c00).
    stream contents inspect
                                                                [exEnd]

"
! !

!CharacterWriteStream methodsFor:'accessing'!

reset
    "reset the stream; write anew.
     See the comment in WriteStream>>contents"

    collection := String new:collection size.
    currentCharacterSize := collection bitsPerCharacter.
    super resetPosition.
! !

!CharacterWriteStream methodsFor:'private'!

characterSizeChanged:aCharacterOrString size:additionalSize
    "change aCollection to fit the size of aCharacter"

    |sz newSz bitsPerCharacter|

    bitsPerCharacter := aCharacterOrString bitsPerCharacter. 
    currentCharacterSize < bitsPerCharacter ifTrue:[
        sz := collection size.
        position + additionalSize >= sz ifTrue:[
            newSz := sz + additionalSize.
        ] ifFalse:[
            newSz := sz.
        ].
        collection := (aCharacterOrString stringSpecies new:newSz) 
                        replaceFrom:1 to:sz with:collection startingAt:1.
        currentCharacterSize := bitsPerCharacter.
    ].
! !

!CharacterWriteStream methodsFor:'private-accessing'!

on:aCollection

    currentCharacterSize := aCollection bitsPerCharacter.
    ^ super on:aCollection.
!

on:aCollection from:start to:stop

    currentCharacterSize := aCollection bitsPerCharacter.
    ^ super on:aCollection from:start to:stop.
!

with:aCollection

    currentCharacterSize := aCollection bitsPerCharacter.
    ^ super with:aCollection.
! !

!CharacterWriteStream methodsFor:'writing'!

next:count put:aCharacter
    "append anObject count times to the receiver.
     Redefined to avoid count grows of the underlying collection -
     instead a single grow on the final size is performed."

    aCharacter bitsPerCharacter > currentCharacterSize ifTrue:[
        self characterSizeChanged:aCharacter size:count.
    ].
    super next:count put:aCharacter
!

nextPut:aCharacter
    "append the argument, aCharacter to the stream.
     Specially tuned for appending to String, ByteArray and Array streams."

%{  /* NOCONTEXT */

#ifndef NO_PRIM_STREAM
    REGISTER int pos;
    unsigned ch;
    OBJ coll;
    OBJ p, wL, rL;
    int __readLimit = -1;

    coll = __INST(collection);
    p = __INST(position);

    if (__isNonNilObject(coll) && __isSmallInteger(p) && __isCharacter(aCharacter)) {
        pos = __intVal(p);
        /* make 1-based */
        pos = pos + 1;
        wL = __INST(writeLimit);

        if ((wL == nil)
         || (__isSmallInteger(wL) && (pos <= __intVal(wL)))) {
            OBJ cls;

            cls = __qClass(coll);
            ch = __intVal(__characterVal(aCharacter));

            rL = __INST(readLimit);
            if (__isSmallInteger(rL)) {
                __readLimit = __intVal(rL);
            }

            if (cls == @global(String)) {
                if (ch > 0xFF) {
                    goto resize;
                }
                if (pos <= __stringSize(coll)) {
                    __StringInstPtr(coll)->s_element[pos-1] = ch;
                    if ((__readLimit >= 0) && (pos >= __readLimit)) {
                        __INST(readLimit) = __mkSmallInteger(pos);
                    }
                    __INST(position) = __mkSmallInteger(__intVal(__INST(position)) + 1);
                    RETURN ( aCharacter );
                }
            } else if (cls == @global(Unicode16String)) {
                if (ch > 0xFFFF) {
                    goto resize;
                }
                if (pos <= __unicode16StringSize(coll)) {
                     __Unicode16StringInstPtr(coll)->s_element[pos-1] = ch;
                    if ((__readLimit >= 0) && (pos >= __readLimit)) {
                        __INST(readLimit) = __mkSmallInteger(pos);
                    }
                    __INST(position) = __mkSmallInteger(__intVal(__INST(position)) + 1);
                    RETURN ( aCharacter );
                }
            } else if (cls == @global(Unicode32String)) {
                if ((pos <= __unicode32StringSize(coll))) {
                     __Unicode32StringInstPtr(coll)->s_element[pos-1] = ch;
                    if ((__readLimit >= 0) && (pos >= __readLimit)) {
                        __INST(readLimit) = __mkSmallInteger(pos);
                    }
                    __INST(position) = __mkSmallInteger(__intVal(__INST(position)) + 1);
                    RETURN ( aCharacter );
                }
            }
        }
    }
    resize:;
#endif
%}.


    (writeLimit isNil
     or:[(position + 1) <= writeLimit]) ifTrue:[
        currentCharacterSize < aCharacter bitsPerCharacter ifTrue:[
            self characterSizeChanged:aCharacter size:1.
        ].
        (position >= collection size) ifTrue:[self growCollection].
        collection at:(position + 1) put:aCharacter.
        (position >= readLimit) ifTrue:[readLimit := (position + 1)].
        position := position + 1.
    ] ifFalse:[
        WriteError raiseErrorString:'write beyond writeLimit'
    ].
    ^ aCharacter
!

nextPutAll:aCollection
    "append aCollection to the receiver.
     Redefined to convert to a string of the needed charcter size."

    aCollection bitsPerCharacter > currentCharacterSize ifTrue:[
        self characterSizeChanged:aCollection size:aCollection size.
    ].
    super nextPutAll:aCollection
!

nextPutAll:aCollection startingAt:start to:stop
    aCollection bitsPerCharacter > currentCharacterSize ifTrue:[
        self characterSizeChanged:aCollection size:stop-start+1.
    ].
    ^ super nextPutAll:aCollection startingAt:start to:stop
!

nextPutAllUnicode:aCollection
    ^ self nextPutAll:aCollection
!

nextPutUnicode:aCharacter
    ^ self nextPut:aCharacter
! !

!CharacterWriteStream class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libbasic/CharacterWriteStream.st,v 1.13 2015-03-14 21:31:57 stefan Exp $'
!

version_CVS
    ^ '$Header: /cvs/stx/stx/libbasic/CharacterWriteStream.st,v 1.13 2015-03-14 21:31:57 stefan Exp $'
! !