Tcl Source Code

Check-in [4749538620]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Eliminate the use of a staging buffer in WriteChars().
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | core-8-5-branch
Files: files | file ages | folders
SHA1: 47495386202f4a6b059d5cad048cb285d240e0f1
User & Date: dgp 2014-01-30 14:31:17
Context
2014-01-30
14:51
Eliminate the use of a staging buffer in WriteChars(). check-in: f5f5df0f00 user: dgp tags: trunk
14:44
Fix [22c10c8e79]: core-8-5: msvc6 build: "Side by Side" error check-in: 8a9e78209d user: jan.nijtmans tags: core-8-5-branch
14:31
Eliminate the use of a staging buffer in WriteChars(). check-in: 4749538620 user: dgp tags: core-8-5-branch
2014-01-28
20:23
The outputStage field is now unused, so never allocate it. check-in: d89e241ea4 user: dgp tags: dgp-optimize-output-stage
2014-01-25
21:41
sync tcl.m4 with Tk version check-in: fc06c2939f user: jan.nijtmans tags: core-8-5-branch
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to generic/tclIO.c.

1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
    statePtr->chPtr		= NULL;
    statePtr->interestMask	= 0;
    statePtr->scriptRecordPtr	= NULL;
    statePtr->bufSize		= CHANNELBUFFER_DEFAULT_SIZE;
    statePtr->timer		= NULL;
    statePtr->csPtrR		= NULL;
    statePtr->csPtrW		= NULL;

    statePtr->outputStage	= NULL;
    if ((statePtr->encoding != NULL) && (statePtr->flags & TCL_WRITABLE)) {
	statePtr->outputStage = (char *)
		ckalloc((unsigned) (statePtr->bufSize + 2));
    }

    /*
     * As we are creating the channel, it is obviously the top for now.
     */

    statePtr->topChanPtr	= chanPtr;
    statePtr->bottomChanPtr	= chanPtr;







<

<
<
<
<







1485
1486
1487
1488
1489
1490
1491

1492




1493
1494
1495
1496
1497
1498
1499
    statePtr->chPtr		= NULL;
    statePtr->interestMask	= 0;
    statePtr->scriptRecordPtr	= NULL;
    statePtr->bufSize		= CHANNELBUFFER_DEFAULT_SIZE;
    statePtr->timer		= NULL;
    statePtr->csPtrR		= NULL;
    statePtr->csPtrW		= NULL;

    statePtr->outputStage	= NULL;





    /*
     * As we are creating the channel, it is obviously the top for now.
     */

    statePtr->topChanPtr	= chanPtr;
    statePtr->bottomChanPtr	= chanPtr;
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
    if (chanPtr == statePtr->bottomChanPtr) {
	if (statePtr->channelName != NULL) {
	    ckfree((char *) statePtr->channelName);
	    statePtr->channelName = NULL;
	}

	Tcl_FreeEncoding(statePtr->encoding);
	if (statePtr->outputStage != NULL) {
	    ckfree((char *) statePtr->outputStage);
	    statePtr->outputStage = NULL;
	}
    }

    /*
     * If we are being called synchronously, report either any latent error on
     * the channel or the current error.
     */








<
<
<
<







2748
2749
2750
2751
2752
2753
2754




2755
2756
2757
2758
2759
2760
2761
    if (chanPtr == statePtr->bottomChanPtr) {
	if (statePtr->channelName != NULL) {
	    ckfree((char *) statePtr->channelName);
	    statePtr->channelName = NULL;
	}

	Tcl_FreeEncoding(statePtr->encoding);




    }

    /*
     * If we are being called synchronously, report either any latent error on
     * the channel or the current error.
     */

3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858


3859
3860
3861
3862
3863
3864
3865
3866
3867
3868

3869
3870
3871
3872
3873

3874

3875
3876
3877





3878




3879
















3880


3881


3882
3883
3884
3885


3886
3887
3888
3889
3890
3891
3892
3893

3894
3895


3896

3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927

3928
3929
3930

3931
3932
3933
3934
3935





3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
WriteChars(
    Channel *chanPtr,		/* The channel to buffer output for. */
    const char *src,		/* UTF-8 string to write. */
    int srcLen)			/* Length of UTF-8 string in bytes. */
{
    ChannelState *statePtr = chanPtr->state;
				/* State info for channel */
    ChannelBuffer *bufPtr;
    char *dst, *stage;
    int saved, savedLF, sawLF, total, dstLen, stageMax, dstWrote;
    int stageLen, toWrite, stageRead, endEncoding, result;
    int consumedSomething, translate;
    Tcl_Encoding encoding;
    char safe[BUFFER_PADDING];

    if (srcLen) {
        WillWrite(chanPtr);
    }

    total = 0;
    sawLF = 0;
    savedLF = 0;
    saved = 0;
    encoding = statePtr->encoding;

    /*
     * Write the terminated escape sequence even if srcLen is 0.
     */

    endEncoding = ((statePtr->outputEncodingFlags & TCL_ENCODING_END) != 0);

    translate = (statePtr->flags & CHANNEL_LINEBUFFERED)
	|| (statePtr->outputTranslation != TCL_TRANSLATE_LF);

    /*
     * Loop over all UTF-8 characters in src, storing them in staging buffer
     * with proper EOL translation.
     */

    consumedSomething = 1;
    while (consumedSomething && (srcLen + savedLF + endEncoding > 0)) {
	consumedSomething = 0;

	if (translate) {
	    stage = statePtr->outputStage;
	    stageMax = statePtr->bufSize;
	    stageLen = stageMax;

	    toWrite = stageLen;
	    if (toWrite > srcLen) {
		toWrite = srcLen;
	    }

	    if (savedLF) {
		/*
		 * A '\n' was left over from last call to TranslateOutputEOL()
		 * and we need to store it in the staging buffer. If the channel
		 * is line-based, we will need to flush the output buffer (after
		 * translating the staging buffer).
		 */

		*stage++ = '\n';
		stageLen--;
		sawLF++;
	    }
	    if (TranslateOutputEOL(statePtr, stage, src, &stageLen, &toWrite)) {
		sawLF++;
	    }

	    stage -= savedLF;
	    stageLen += savedLF;
	    savedLF = 0;

	    if (stageLen > stageMax) {
		savedLF = 1;
		stageLen = stageMax;
	    }
	} else {
	    stage = (char *) src;
	    stageLen = srcLen;
	    toWrite = stageLen;
	}
	src += toWrite;
	srcLen -= toWrite;

	/*
	 * Loop over all UTF-8 characters in staging buffer, converting them
	 * to external encoding, storing them in output buffer.
	 */

	while (stageLen + saved + endEncoding > 0) {
	    bufPtr = statePtr->curOutPtr;
	    if (bufPtr == NULL) {
		bufPtr = AllocChannelBuffer(statePtr->bufSize);
		statePtr->curOutPtr = bufPtr;
	    }
	    dst = InsertPoint(bufPtr);
	    dstLen = SpaceLeft(bufPtr);

	    if (saved != 0) {
		/*
		 * Here's some translated bytes left over from the last buffer
		 * that we need to stick at the beginning of this buffer.
		 */

		memcpy(dst, safe, (size_t) saved);
		bufPtr->nextAdded += saved;
		dst += saved;
		dstLen -= saved;
		saved = 0;
	    }



	    result = Tcl_UtfToExternal(NULL, encoding, stage, stageLen,
		    statePtr->outputEncodingFlags,
		    &statePtr->outputEncodingState, dst,
		    dstLen + BUFFER_PADDING, &stageRead, &dstWrote, NULL);

	    /*
	     * Fix for SF #506297, reported by Martin Forssen
	     * <[email protected]>.
	     *

	     * The encoding chosen in the script exposing the bug writes out
	     * three intro characters when TCL_ENCODING_START is set, but does
	     * not consume any input as TCL_ENCODING_END is cleared. As some
	     * output was generated the enclosing loop calls UtfToExternal
	     * again, again with START set. Three more characters in the out

	     * and still no use of input ... To break this infinite loop we

	     * remove TCL_ENCODING_START from the set of flags after the first
	     * call (no condition is required, the later calls remove an unset
	     * flag, which is a no-op). This causes the subsequent calls to





	     * UtfToExternal to consume and convert the actual input.




	     */



















	    statePtr->outputEncodingFlags &= ~TCL_ENCODING_START;



	    /*
	     * The following code must be executed only when result is not 0.
	     */



	    if ((result != 0) && (stageRead + dstWrote == 0)) {
		/*
		 * We have an incomplete UTF-8 character at the end of the
		 * staging buffer. It will get moved to the beginning of the
		 * staging buffer followed by more bytes from src.
		 */


		src -= stageLen;
		srcLen += stageLen;


		stageLen = 0;

		savedLF = 0;
		break;
	    }
	    bufPtr->nextAdded += dstWrote;
	    if (IsBufferOverflowing(bufPtr)) {
		/*
		 * When translating from UTF-8 to external encoding, we
		 * allowed the translation to produce a character that crossed
		 * the end of the output buffer, so that we would get a
		 * completely full buffer before flushing it. The extra bytes
		 * will be moved to the beginning of the next buffer.
		 */

		saved = -SpaceLeft(bufPtr);
		memcpy(safe, dst + dstLen, (size_t) saved);
		bufPtr->nextAdded = bufPtr->bufLength;
	    }
	    if (CheckFlush(chanPtr, bufPtr, sawLF) != 0) {
		return -1;
	    }

	    total += dstWrote;
	    stage += stageRead;
	    stageLen -= stageRead;
	    sawLF = 0;

	    consumedSomething = 1;

	    /*
	     * If all translated characters are written to the buffer,
	     * endEncoding is set to 0 because the escape sequence may be

	     * output.
	     */


	    if ((stageLen + saved == 0) && (result == 0)) {
		endEncoding = 0;
	    }
	}
    }






    /*
     * If nothing was written and it happened because there was no progress in
     * the UTF conversion, we throw an error.
     */

    if (!consumedSomething && (total == 0)) {
	Tcl_SetErrno(EINVAL);
	return -1;
    }
    return total;
}

/*
 *---------------------------------------------------------------------------
 *
 * TranslateOutputEOL --







<
|
<
|
<
|
<





<
<
<
<
<
<






|
|
|
<
<
<
<
|
<
<
<

<
<
<
|
|
<
<
<
<
|
<
<
<
<
<
<
<
|
<
<
<
|
<
<
<
|
<
<
<
|
<
<
<
|
<
<
<
<
|
<
<
|
<
<
<
<
<
<
|
|
|
|
|
<
<
<
|
|
|
|
|

|
|
<
<
|
|
>
>

|
|
|
|

|
|
<
|
>
|
|
|
|
<
>
|
>
|
<
|
>
>
>
>
>
|
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
|
>
>

<
<
<
>
>
|
<
<
<
<
<
<

>
|
|
>
>
|
>
|
<
|
|
|
|
|
|
|
|
|
|

|
|
|
|
<
<
|
|
<
<
<
|
|
<

<
|
<
>
|
<
|
>
|
|



>
>
>
>
>
|
<
<
<
<
|
<
<
<
|







3739
3740
3741
3742
3743
3744
3745

3746

3747

3748

3749
3750
3751
3752
3753






3754
3755
3756
3757
3758
3759
3760
3761
3762




3763



3764



3765
3766




3767







3768



3769



3770



3771



3772




3773


3774






3775
3776
3777
3778
3779



3780
3781
3782
3783
3784
3785
3786
3787


3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799

3800
3801
3802
3803
3804
3805

3806
3807
3808
3809

3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844



3845
3846
3847






3848
3849
3850
3851
3852
3853
3854
3855
3856

3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871


3872
3873



3874
3875

3876

3877

3878
3879

3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892




3893



3894
3895
3896
3897
3898
3899
3900
3901
WriteChars(
    Channel *chanPtr,		/* The channel to buffer output for. */
    const char *src,		/* UTF-8 string to write. */
    int srcLen)			/* Length of UTF-8 string in bytes. */
{
    ChannelState *statePtr = chanPtr->state;
				/* State info for channel */

    char *nextNewLine = NULL;

    int endEncoding, saved = 0, total = 0, flushed = 0, needNlFlush = 0;

    Tcl_Encoding encoding = statePtr->encoding;


    if (srcLen) {
        WillWrite(chanPtr);
    }







    /*
     * Write the terminated escape sequence even if srcLen is 0.
     */

    endEncoding = ((statePtr->outputEncodingFlags & TCL_ENCODING_END) != 0);

    if ((statePtr->flags & CHANNEL_LINEBUFFERED)
	    || (statePtr->outputTranslation != TCL_TRANSLATE_LF)) {
	nextNewLine = memchr(src, '\n', srcLen);




    }







    while (srcLen + saved + endEncoding > 0) {
	ChannelBuffer *bufPtr;




	char *dst, safe[BUFFER_PADDING];







	int result, srcRead, dstLen, dstWrote, srcLimit = srcLen;







	if (nextNewLine) {



	    srcLimit = nextNewLine - src;



	}




	


	/* Get space to write into */






	bufPtr = statePtr->curOutPtr;
	if (bufPtr == NULL) {
	    bufPtr = AllocChannelBuffer(statePtr->bufSize);
	    statePtr->curOutPtr = bufPtr;
	}



	if (saved) {
	    /*
	     * Here's some translated bytes left over from the last buffer
	     * that we need to stick at the beginning of this buffer.
	     */

	    memcpy(InsertPoint(bufPtr), safe, (size_t) saved);
	    bufPtr->nextAdded += saved;


	    saved = 0;
	}
	dst = InsertPoint(bufPtr);
	dstLen = SpaceLeft(bufPtr);

	result = Tcl_UtfToExternal(NULL, encoding, src, srcLimit,
		statePtr->outputEncodingFlags,
		&statePtr->outputEncodingState, dst,
		dstLen + BUFFER_PADDING, &srcRead, &dstWrote, NULL);

	/* See chan-io-1.[89]. Tcl Bug 506297. */
	statePtr->outputEncodingFlags &= ~TCL_ENCODING_START;

	
	if ((result != TCL_OK) && (srcRead + dstWrote == 0)) {
	    /* We're reading from invalid/incomplete UTF-8 */
	    if (total == 0) {
		Tcl_SetErrno(EINVAL);
		return -1;

	    }
	    break;
	}


	bufPtr->nextAdded += dstWrote;
	src += srcRead;
	srcLen -= srcRead;
	total += dstWrote;
	dst += dstWrote;
	dstLen -= dstWrote;

	if (src == nextNewLine && dstLen > 0) {
	    static char crln[3] = "\r\n";
	    char *nl = NULL;
	    int nlLen = 0;

	    switch (statePtr->outputTranslation) {
	    case TCL_TRANSLATE_LF:
		nl = crln + 1;
		nlLen = 1;
		break;
	    case TCL_TRANSLATE_CR:
		nl = crln;
		nlLen = 1;
		break;
	    case TCL_TRANSLATE_CRLF:
		nl = crln;
		nlLen = 2;
		break;
	    default:
		Tcl_Panic("unknown output translation requested");
		break;
	    }
	
	    result |= Tcl_UtfToExternal(NULL, encoding, nl, nlLen,
		statePtr->outputEncodingFlags,
		&statePtr->outputEncodingState, dst,
		dstLen + BUFFER_PADDING, &srcRead, &dstWrote, NULL);




	    if (srcRead != nlLen) {
		Tcl_Panic("Can This Happen?");
	    }







	    bufPtr->nextAdded += dstWrote;
	    src++;
	    srcLen--;
	    total += dstWrote;
	    dst += dstWrote;
	    dstLen -= dstWrote;
	    nextNewLine = memchr(src, '\n', srcLen);
	    needNlFlush = 1;

	}

	if (IsBufferOverflowing(bufPtr)) {
	    /*
	     * When translating from UTF-8 to external encoding, we
	     * allowed the translation to produce a character that crossed
	     * the end of the output buffer, so that we would get a
	     * completely full buffer before flushing it. The extra bytes
	     * will be moved to the beginning of the next buffer.
	     */

	    saved = -SpaceLeft(bufPtr);
	    memcpy(safe, dst + dstLen, (size_t) saved);
	    bufPtr->nextAdded = bufPtr->bufLength;
	}



	if ((srcLen + saved == 0) && (result == TCL_OK)) {



	    endEncoding = 0;
	}



	if (IsBufferFull(bufPtr)) {

	    if (FlushChannel(NULL, chanPtr, 0) != 0) {
		return -1;

	    }
	    flushed += statePtr->bufSize;
	    if (saved == 0 || src[-1] != '\n') {
		needNlFlush = 0;
	    }
	}
    }
    if ((flushed < total) && (statePtr->flags & CHANNEL_UNBUFFERED ||
	    (needNlFlush && statePtr->flags & CHANNEL_LINEBUFFERED))) {
	SetFlag(statePtr, BUFFER_READY);
	if (FlushChannel(NULL, chanPtr, 0) != 0) {
	    return -1;
	}




    }




    return total;
}

/*
 *---------------------------------------------------------------------------
 *
 * TranslateOutputEOL --
7189
7190
7191
7192
7193
7194
7195
7196
7197
7198
7199
7200
7201
7202
7203
7204
7205
7206
7207
7208
7209
7210
7211
      sz = 1;
    } else if (sz > MAX_CHANNEL_BUFFER_SIZE) {
      sz = MAX_CHANNEL_BUFFER_SIZE;
    }

    statePtr = ((Channel *) chan)->state;
    statePtr->bufSize = sz;

    if (statePtr->outputStage != NULL) {
	ckfree((char *) statePtr->outputStage);
	statePtr->outputStage = NULL;
    }
    if ((statePtr->encoding != NULL) && (statePtr->flags & TCL_WRITABLE)) {
	statePtr->outputStage = (char *)
		ckalloc((unsigned) (statePtr->bufSize + 2));
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_GetChannelBufferSize --
 *







<
<
<
<
<
<
<
<
<







7138
7139
7140
7141
7142
7143
7144









7145
7146
7147
7148
7149
7150
7151
      sz = 1;
    } else if (sz > MAX_CHANNEL_BUFFER_SIZE) {
      sz = MAX_CHANNEL_BUFFER_SIZE;
    }

    statePtr = ((Channel *) chan)->state;
    statePtr->bufSize = sz;









}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_GetChannelBufferSize --
 *
7838
7839
7840
7841
7842
7843
7844
7845
7846
7847
7848
7849
7850
7851
7852
7853
7854
7855
7856
7857
7858
7859
7860
7861
7862
	    && (statePtr->inQueueHead->nextPtr == NULL)
	    && IsBufferEmpty(statePtr->inQueueHead)) {
	RecycleBuffer(statePtr, statePtr->inQueueHead, 1);
	statePtr->inQueueHead = NULL;
	statePtr->inQueueTail = NULL;
    }

    /*
     * If encoding or bufsize changes, need to update output staging buffer.
     */

    if (statePtr->outputStage != NULL) {
	ckfree(statePtr->outputStage);
	statePtr->outputStage = NULL;
    }
    if ((statePtr->encoding != NULL) && (statePtr->flags & TCL_WRITABLE)) {
	statePtr->outputStage = ckalloc((unsigned) (statePtr->bufSize + 2));
    }
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * CleanupChannelHandlers --







<
<
<
<
<
<
<
<
<
<
<







7778
7779
7780
7781
7782
7783
7784











7785
7786
7787
7788
7789
7790
7791
	    && (statePtr->inQueueHead->nextPtr == NULL)
	    && IsBufferEmpty(statePtr->inQueueHead)) {
	RecycleBuffer(statePtr, statePtr->inQueueHead, 1);
	statePtr->inQueueHead = NULL;
	statePtr->inQueueTail = NULL;
    }












    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * CleanupChannelHandlers --