Check-in [f5f5df0f00]
Not logged in
Tcl 2014 Conference, Portland/OR, US, Nov 10-14
Browse the schedule online.

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

Overview
SHA1 Hash:f5f5df0f00d4215aea8eb071f3bea8d5d2ff9396
Date: 2014-01-30 14:51:44
User: dgp
Comment:Eliminate the use of a staging buffer in WriteChars().
Tags And Properties
Changes

Changes to generic/tclIO.c

1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
....
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
....
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
....
7592
7593
7594
7595
7596
7597
7598
7599
7600
7601
7602
7603
7604
7605
7606
7607
7608
7609
7610
7611
7612
7613
....
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->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;
................................................................................
    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.
     */

................................................................................
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
	     * <ruric@users.sourceforge.net>.


	     *
	     * 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 --
................................................................................
	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 --
 *
................................................................................
	    && (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 --







<

<
<
<







 







<
<
<
<







 







|
|
<
<
<
|
<





<
<
<
<
<
<






|
|
>
|
<
<
<
<

<
<
<
>
>
>
>

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

|
|
<
<
|
|
>
>

|
|
|
|

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

<
<
<
<
<
<
>
>
>
>
>
>

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

>
>
>
>
>
>
>
>
>
>
>

>
>
>
>
>
>
>
>
>
>

>
>
>
>
>
>
>
>
>
|
|
|
|
|
|
|
|

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

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



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







 







<
<
<
<
<
<
<
<







 







<
<
<
<
<
<
<
<
<
<
<







1577
1578
1579
1580
1581
1582
1583

1584



1585
1586
1587
1588
1589
1590
1591
....
2830
2831
2832
2833
2834
2835
2836




2837
2838
2839
2840
2841
2842
2843
....
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
....
7541
7542
7543
7544
7545
7546
7547








7548
7549
7550
7551
7552
7553
7554
....
8181
8182
8183
8184
8185
8186
8187











8188
8189
8190
8191
8192
8193
8194
    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;
................................................................................
    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.
     */

................................................................................
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 --
................................................................................
	sz = 1;
    } else if (sz > MAX_CHANNEL_BUFFER_SIZE) {
	sz = MAX_CHANNEL_BUFFER_SIZE;
    }

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








}
 
/*
 *----------------------------------------------------------------------
 *
 * Tcl_GetChannelBufferSize --
 *
................................................................................
	    && (statePtr->inQueueHead->nextPtr == NULL)
	    && IsBufferEmpty(statePtr->inQueueHead)) {
	RecycleBuffer(statePtr, statePtr->inQueueHead, 1);
	statePtr->inQueueHead = NULL;
	statePtr->inQueueTail = NULL;
    }












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