Tcl Source Code

Check-in [f5f5df0f00]
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 | trunk
Files: files | file ages | folders
SHA1: f5f5df0f00d4215aea8eb071f3bea8d5d2ff9396
User & Date: dgp 2014-01-30 14:51:44
Context
2014-01-31
09:19
Fix [4b3b7a3082]: tcl8.5.15/generic/tclExecute.c:7713: array index before sanity check ? check-in: 0b5fb73910 user: jan.nijtmans tags: trunk
2014-01-30
16:21
merge trunk check-in: b05a6048ea user: dgp tags: dgp-refactor
14:51
Eliminate the use of a staging buffer in WriteChars(). check-in: f5f5df0f00 user: dgp tags: trunk
14:50
Fix [22c10c8e79]: core-8-5: msvc6 build: "Side by Side" error check-in: 3fb17c9267 user: jan.nijtmans tags: trunk
14:31
Eliminate the use of a staging buffer in WriteChars(). check-in: 4749538620 user: dgp tags: core-8-5-branch
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to generic/tclIO.c.

1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
    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) && GotFlag(statePtr, TCL_WRITABLE)) {
	statePtr->outputStage = ckalloc(statePtr->bufSize + 2);
    }

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

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







<

<
<
<







1577
1578
1579
1580
1581
1582
1583

1584



1585
1586
1587
1588
1589
1590
1591
    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;
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
    if (chanPtr == statePtr->bottomChanPtr) {
	if (statePtr->channelName != NULL) {
	    ckfree(statePtr->channelName);
	    statePtr->channelName = NULL;
	}

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

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








<
<
<
<







2830
2831
2832
2833
2834
2835
2836




2837
2838
2839
2840
2841
2842
2843
    if (chanPtr == statePtr->bottomChanPtr) {
	if (statePtr->channelName != NULL) {
	    ckfree(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.
     */

4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270


4271
4272
4273
4274
4275
4276
4277
4278
4279
4280

4281
4282
4283
4284
4285

4286

4287
4288
4289





4290




4291
















4292


4293


4294
4295
4296
4297


4298
4299
4300
4301
4302
4303
4304
4305

4306
4307


4308

4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339

4340
4341
4342

4343
4344
4345
4346
4347





4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
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 = GotFlag(statePtr, 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 --







<
|
<
|
<
|
<





<
<
<
<
<
<






|
|
|
<
<
<
<
|
<
<
<

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

|
|
<
<
|
|
>
>

|
|
|
|

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

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

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

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

<
|
<
>
|
<
|
>
|
|



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







4151
4152
4153
4154
4155
4156
4157

4158

4159

4160

4161
4162
4163
4164
4165






4166
4167
4168
4169
4170
4171
4172
4173
4174




4175



4176



4177
4178




4179







4180



4181




4182



4183



4184




4185


4186






4187
4188
4189
4190
4191



4192
4193
4194
4195
4196
4197
4198
4199


4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211

4212
4213
4214
4215
4216
4217

4218
4219
4220
4221

4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256



4257
4258
4259






4260
4261
4262
4263
4264
4265
4266
4267
4268

4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283


4284
4285



4286
4287

4288

4289

4290
4291

4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304




4305



4306
4307
4308
4309
4310
4311
4312
4313
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 (GotFlag(statePtr, 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) && (GotFlag(statePtr, CHANNEL_UNBUFFERED) ||
	    (needNlFlush && GotFlag(statePtr, CHANNEL_LINEBUFFERED)))) {
	SetFlag(statePtr, BUFFER_READY);
	if (FlushChannel(NULL, chanPtr, 0) != 0) {
	    return -1;
	}




    }




    return total;
}

/*
 *---------------------------------------------------------------------------
 *
 * TranslateOutputEOL --
7592
7593
7594
7595
7596
7597
7598
7599
7600
7601
7602
7603
7604
7605
7606
7607
7608
7609
7610
7611
7612
7613
	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(statePtr->outputStage);
	statePtr->outputStage = NULL;
    }
    if ((statePtr->encoding != NULL) && GotFlag(statePtr, TCL_WRITABLE)) {
	statePtr->outputStage = ckalloc(statePtr->bufSize + 2);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_GetChannelBufferSize --
 *







<
<
<
<
<
<
<
<







7541
7542
7543
7544
7545
7546
7547








7548
7549
7550
7551
7552
7553
7554
	sz = 1;
    } else if (sz > MAX_CHANNEL_BUFFER_SIZE) {
	sz = MAX_CHANNEL_BUFFER_SIZE;
    }

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








}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_GetChannelBufferSize --
 *
8240
8241
8242
8243
8244
8245
8246
8247
8248
8249
8250
8251
8252
8253
8254
8255
8256
8257
8258
8259
8260
8261
8262
8263
8264
	    && (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) && GotFlag(statePtr, TCL_WRITABLE)) {
	statePtr->outputStage = ckalloc(statePtr->bufSize + 2);
    }
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * CleanupChannelHandlers --







<
<
<
<
<
<
<
<
<
<
<







8181
8182
8183
8184
8185
8186
8187











8188
8189
8190
8191
8192
8193
8194
	    && (statePtr->inQueueHead->nextPtr == NULL)
	    && IsBufferEmpty(statePtr->inQueueHead)) {
	RecycleBuffer(statePtr, statePtr->inQueueHead, 1);
	statePtr->inQueueHead = NULL;
	statePtr->inQueueTail = NULL;
    }












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