Tcl Source Code

Check-in [f5f5df0f00]
Login
Bounty program for improvements to Tcl and certain Tcl packages.
Tcl 2018 Conference, Houston/TX, US, Oct 15-19
Send your abstracts to tclconference@googlegroups.com
or submit via the online form by Aug 20.

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 Side-by-Side Diffs Ignore Whitespace Patch

Changes to generic/tclIO.c.

  1577   1577       statePtr->chPtr		= NULL;
  1578   1578       statePtr->interestMask	= 0;
  1579   1579       statePtr->scriptRecordPtr	= NULL;
  1580   1580       statePtr->bufSize		= CHANNELBUFFER_DEFAULT_SIZE;
  1581   1581       statePtr->timer		= NULL;
  1582   1582       statePtr->csPtrR		= NULL;
  1583   1583       statePtr->csPtrW		= NULL;
  1584         -
  1585   1584       statePtr->outputStage	= NULL;
  1586         -    if ((statePtr->encoding != NULL) && GotFlag(statePtr, TCL_WRITABLE)) {
  1587         -	statePtr->outputStage = ckalloc(statePtr->bufSize + 2);
  1588         -    }
  1589   1585   
  1590   1586       /*
  1591   1587        * As we are creating the channel, it is obviously the top for now.
  1592   1588        */
  1593   1589   
  1594   1590       statePtr->topChanPtr	= chanPtr;
  1595   1591       statePtr->bottomChanPtr	= chanPtr;
................................................................................
  2834   2830       if (chanPtr == statePtr->bottomChanPtr) {
  2835   2831   	if (statePtr->channelName != NULL) {
  2836   2832   	    ckfree(statePtr->channelName);
  2837   2833   	    statePtr->channelName = NULL;
  2838   2834   	}
  2839   2835   
  2840   2836   	Tcl_FreeEncoding(statePtr->encoding);
  2841         -	if (statePtr->outputStage != NULL) {
  2842         -	    ckfree(statePtr->outputStage);
  2843         -	    statePtr->outputStage = NULL;
  2844         -	}
  2845   2837       }
  2846   2838   
  2847   2839       /*
  2848   2840        * If we are being called synchronously, report either any latent error on
  2849   2841        * the channel or the current error.
  2850   2842        */
  2851   2843   
................................................................................
  4159   4151   WriteChars(
  4160   4152       Channel *chanPtr,		/* The channel to buffer output for. */
  4161   4153       const char *src,		/* UTF-8 string to write. */
  4162   4154       int srcLen)			/* Length of UTF-8 string in bytes. */
  4163   4155   {
  4164   4156       ChannelState *statePtr = chanPtr->state;
  4165   4157   				/* State info for channel */
  4166         -    ChannelBuffer *bufPtr;
  4167         -    char *dst, *stage;
  4168         -    int saved, savedLF, sawLF, total, dstLen, stageMax, dstWrote;
  4169         -    int stageLen, toWrite, stageRead, endEncoding, result;
  4170         -    int consumedSomething, translate;
  4171         -    Tcl_Encoding encoding;
  4172         -    char safe[BUFFER_PADDING];
         4158  +    char *nextNewLine = NULL;
         4159  +    int endEncoding, saved = 0, total = 0, flushed = 0, needNlFlush = 0;
         4160  +    Tcl_Encoding encoding = statePtr->encoding;
  4173   4161   
  4174   4162       if (srcLen) {
  4175   4163           WillWrite(chanPtr);
  4176   4164       }
  4177   4165   
  4178         -    total = 0;
  4179         -    sawLF = 0;
  4180         -    savedLF = 0;
  4181         -    saved = 0;
  4182         -    encoding = statePtr->encoding;
  4183         -
  4184   4166       /*
  4185   4167        * Write the terminated escape sequence even if srcLen is 0.
  4186   4168        */
  4187   4169   
  4188   4170       endEncoding = ((statePtr->outputEncodingFlags & TCL_ENCODING_END) != 0);
  4189   4171   
  4190         -    translate = GotFlag(statePtr, CHANNEL_LINEBUFFERED)
  4191         -	    || (statePtr->outputTranslation != TCL_TRANSLATE_LF);
  4192         -
  4193         -    /*
  4194         -     * Loop over all UTF-8 characters in src, storing them in staging buffer
  4195         -     * with proper EOL translation.
  4196         -     */
  4197         -
  4198         -    consumedSomething = 1;
  4199         -    while (consumedSomething && (srcLen + savedLF + endEncoding > 0)) {
  4200         -	consumedSomething = 0;
  4201         -
  4202         -	if (translate) {
  4203         -	    stage = statePtr->outputStage;
  4204         -	    stageMax = statePtr->bufSize;
  4205         -	    stageLen = stageMax;
  4206         -
  4207         -	    toWrite = stageLen;
  4208         -	    if (toWrite > srcLen) {
  4209         -		toWrite = srcLen;
  4210         -	    }
  4211         -
  4212         -	    if (savedLF) {
  4213         -		/*
  4214         -		 * A '\n' was left over from last call to TranslateOutputEOL()
  4215         -		 * and we need to store it in the staging buffer. If the
  4216         -		 * channel is line-based, we will need to flush the output
  4217         -		 * buffer (after translating the staging buffer).
  4218         -		 */
  4219         -
  4220         -		*stage++ = '\n';
  4221         -		stageLen--;
  4222         -		sawLF++;
  4223         -	    }
  4224         -	    if (TranslateOutputEOL(statePtr, stage, src, &stageLen,
  4225         -		    &toWrite)) {
  4226         -		sawLF++;
  4227         -	    }
  4228         -
  4229         -	    stage -= savedLF;
  4230         -	    stageLen += savedLF;
  4231         -	    savedLF = 0;
  4232         -
  4233         -	    if (stageLen > stageMax) {
  4234         -		savedLF = 1;
  4235         -		stageLen = stageMax;
  4236         -	    }
  4237         -	} else {
  4238         -	    stage = (char *) src;
  4239         -	    stageLen = srcLen;
  4240         -	    toWrite = stageLen;
  4241         -	}
  4242         -	src += toWrite;
  4243         -	srcLen -= toWrite;
  4244         -
  4245         -	/*
  4246         -	 * Loop over all UTF-8 characters in staging buffer, converting them
  4247         -	 * to external encoding, storing them in output buffer.
  4248         -	 */
  4249         -
  4250         -	while (stageLen + saved + endEncoding > 0) {
  4251         -	    bufPtr = statePtr->curOutPtr;
  4252         -	    if (bufPtr == NULL) {
  4253         -		bufPtr = AllocChannelBuffer(statePtr->bufSize);
  4254         -		statePtr->curOutPtr = bufPtr;
  4255         -	    }
  4256         -	    dst = InsertPoint(bufPtr);
  4257         -	    dstLen = SpaceLeft(bufPtr);
  4258         -
  4259         -	    if (saved != 0) {
  4260         -		/*
  4261         -		 * Here's some translated bytes left over from the last buffer
  4262         -		 * that we need to stick at the beginning of this buffer.
  4263         -		 */
  4264         -
  4265         -		memcpy(dst, safe, (size_t) saved);
  4266         -		bufPtr->nextAdded += saved;
  4267         -		dst += saved;
  4268         -		dstLen -= saved;
  4269         -		saved = 0;
  4270         -	    }
  4271         -
  4272         -	    result = Tcl_UtfToExternal(NULL, encoding, stage, stageLen,
  4273         -		    statePtr->outputEncodingFlags,
  4274         -		    &statePtr->outputEncodingState, dst,
  4275         -		    dstLen + BUFFER_PADDING, &stageRead, &dstWrote, NULL);
  4276         -
  4277         -	    /*
  4278         -	     * Fix for SF #506297, reported by Martin Forssen
  4279         -	     * <ruric@users.sourceforge.net>.
  4280         -	     *
  4281         -	     * The encoding chosen in the script exposing the bug writes out
  4282         -	     * three intro characters when TCL_ENCODING_START is set, but does
  4283         -	     * not consume any input as TCL_ENCODING_END is cleared. As some
  4284         -	     * output was generated the enclosing loop calls UtfToExternal
  4285         -	     * again, again with START set. Three more characters in the out
  4286         -	     * and still no use of input ... To break this infinite loop we
  4287         -	     * remove TCL_ENCODING_START from the set of flags after the first
  4288         -	     * call (no condition is required, the later calls remove an unset
  4289         -	     * flag, which is a no-op). This causes the subsequent calls to
  4290         -	     * UtfToExternal to consume and convert the actual input.
  4291         -	     */
  4292         -
  4293         -	    statePtr->outputEncodingFlags &= ~TCL_ENCODING_START;
  4294         -
  4295         -	    /*
  4296         -	     * The following code must be executed only when result is not 0.
  4297         -	     */
  4298         -
  4299         -	    if ((result != 0) && (stageRead + dstWrote == 0)) {
  4300         -		/*
  4301         -		 * We have an incomplete UTF-8 character at the end of the
  4302         -		 * staging buffer. It will get moved to the beginning of the
  4303         -		 * staging buffer followed by more bytes from src.
  4304         -		 */
  4305         -
  4306         -		src -= stageLen;
  4307         -		srcLen += stageLen;
  4308         -		stageLen = 0;
  4309         -		savedLF = 0;
         4172  +    if (GotFlag(statePtr, CHANNEL_LINEBUFFERED)
         4173  +	    || (statePtr->outputTranslation != TCL_TRANSLATE_LF)) {
         4174  +	nextNewLine = memchr(src, '\n', srcLen);
         4175  +    }
         4176  +
         4177  +    while (srcLen + saved + endEncoding > 0) {
         4178  +	ChannelBuffer *bufPtr;
         4179  +	char *dst, safe[BUFFER_PADDING];
         4180  +	int result, srcRead, dstLen, dstWrote, srcLimit = srcLen;
         4181  +
         4182  +	if (nextNewLine) {
         4183  +	    srcLimit = nextNewLine - src;
         4184  +	}
         4185  +	
         4186  +	/* Get space to write into */
         4187  +	bufPtr = statePtr->curOutPtr;
         4188  +	if (bufPtr == NULL) {
         4189  +	    bufPtr = AllocChannelBuffer(statePtr->bufSize);
         4190  +	    statePtr->curOutPtr = bufPtr;
         4191  +	}
         4192  +	if (saved) {
         4193  +	    /*
         4194  +	     * Here's some translated bytes left over from the last buffer
         4195  +	     * that we need to stick at the beginning of this buffer.
         4196  +	     */
         4197  +
         4198  +	    memcpy(InsertPoint(bufPtr), safe, (size_t) saved);
         4199  +	    bufPtr->nextAdded += saved;
         4200  +	    saved = 0;
         4201  +	}
         4202  +	dst = InsertPoint(bufPtr);
         4203  +	dstLen = SpaceLeft(bufPtr);
         4204  +
         4205  +	result = Tcl_UtfToExternal(NULL, encoding, src, srcLimit,
         4206  +		statePtr->outputEncodingFlags,
         4207  +		&statePtr->outputEncodingState, dst,
         4208  +		dstLen + BUFFER_PADDING, &srcRead, &dstWrote, NULL);
         4209  +
         4210  +	/* See chan-io-1.[89]. Tcl Bug 506297. */
         4211  +	statePtr->outputEncodingFlags &= ~TCL_ENCODING_START;
         4212  +	
         4213  +	if ((result != TCL_OK) && (srcRead + dstWrote == 0)) {
         4214  +	    /* We're reading from invalid/incomplete UTF-8 */
         4215  +	    if (total == 0) {
         4216  +		Tcl_SetErrno(EINVAL);
         4217  +		return -1;
         4218  +	    }
         4219  +	    break;
         4220  +	}
         4221  +
         4222  +	bufPtr->nextAdded += dstWrote;
         4223  +	src += srcRead;
         4224  +	srcLen -= srcRead;
         4225  +	total += dstWrote;
         4226  +	dst += dstWrote;
         4227  +	dstLen -= dstWrote;
         4228  +
         4229  +	if (src == nextNewLine && dstLen > 0) {
         4230  +	    static char crln[3] = "\r\n";
         4231  +	    char *nl = NULL;
         4232  +	    int nlLen = 0;
         4233  +
         4234  +	    switch (statePtr->outputTranslation) {
         4235  +	    case TCL_TRANSLATE_LF:
         4236  +		nl = crln + 1;
         4237  +		nlLen = 1;
         4238  +		break;
         4239  +	    case TCL_TRANSLATE_CR:
         4240  +		nl = crln;
         4241  +		nlLen = 1;
         4242  +		break;
         4243  +	    case TCL_TRANSLATE_CRLF:
         4244  +		nl = crln;
         4245  +		nlLen = 2;
         4246  +		break;
         4247  +	    default:
         4248  +		Tcl_Panic("unknown output translation requested");
  4310   4249   		break;
  4311   4250   	    }
         4251  +	
         4252  +	    result |= Tcl_UtfToExternal(NULL, encoding, nl, nlLen,
         4253  +		statePtr->outputEncodingFlags,
         4254  +		&statePtr->outputEncodingState, dst,
         4255  +		dstLen + BUFFER_PADDING, &srcRead, &dstWrote, NULL);
         4256  +
         4257  +	    if (srcRead != nlLen) {
         4258  +		Tcl_Panic("Can This Happen?");
         4259  +	    }
         4260  +
  4312   4261   	    bufPtr->nextAdded += dstWrote;
  4313         -	    if (IsBufferOverflowing(bufPtr)) {
  4314         -		/*
  4315         -		 * When translating from UTF-8 to external encoding, we
  4316         -		 * allowed the translation to produce a character that crossed
  4317         -		 * the end of the output buffer, so that we would get a
  4318         -		 * completely full buffer before flushing it. The extra bytes
  4319         -		 * will be moved to the beginning of the next buffer.
  4320         -		 */
  4321         -
  4322         -		saved = -SpaceLeft(bufPtr);
  4323         -		memcpy(safe, dst + dstLen, (size_t) saved);
  4324         -		bufPtr->nextAdded = bufPtr->bufLength;
  4325         -	    }
  4326         -	    if (CheckFlush(chanPtr, bufPtr, sawLF) != 0) {
         4262  +	    src++;
         4263  +	    srcLen--;
         4264  +	    total += dstWrote;
         4265  +	    dst += dstWrote;
         4266  +	    dstLen -= dstWrote;
         4267  +	    nextNewLine = memchr(src, '\n', srcLen);
         4268  +	    needNlFlush = 1;
         4269  +	}
         4270  +
         4271  +	if (IsBufferOverflowing(bufPtr)) {
         4272  +	    /*
         4273  +	     * When translating from UTF-8 to external encoding, we
         4274  +	     * allowed the translation to produce a character that crossed
         4275  +	     * the end of the output buffer, so that we would get a
         4276  +	     * completely full buffer before flushing it. The extra bytes
         4277  +	     * will be moved to the beginning of the next buffer.
         4278  +	     */
         4279  +
         4280  +	    saved = -SpaceLeft(bufPtr);
         4281  +	    memcpy(safe, dst + dstLen, (size_t) saved);
         4282  +	    bufPtr->nextAdded = bufPtr->bufLength;
         4283  +	}
         4284  +
         4285  +	if ((srcLen + saved == 0) && (result == TCL_OK)) {
         4286  +	    endEncoding = 0;
         4287  +	}
         4288  +
         4289  +	if (IsBufferFull(bufPtr)) {
         4290  +	    if (FlushChannel(NULL, chanPtr, 0) != 0) {
  4327   4291   		return -1;
  4328   4292   	    }
  4329         -
  4330         -	    total += dstWrote;
  4331         -	    stage += stageRead;
  4332         -	    stageLen -= stageRead;
  4333         -	    sawLF = 0;
  4334         -
  4335         -	    consumedSomething = 1;
  4336         -
  4337         -	    /*
  4338         -	     * If all translated characters are written to the buffer,
  4339         -	     * endEncoding is set to 0 because the escape sequence may be
  4340         -	     * output.
  4341         -	     */
  4342         -
  4343         -	    if ((stageLen + saved == 0) && (result == 0)) {
  4344         -		endEncoding = 0;
  4345         -	    }
  4346         -	}
  4347         -    }
  4348         -
  4349         -    /*
  4350         -     * If nothing was written and it happened because there was no progress in
  4351         -     * the UTF conversion, we throw an error.
  4352         -     */
  4353         -
  4354         -    if (!consumedSomething && (total == 0)) {
  4355         -	Tcl_SetErrno(EINVAL);
  4356         -	return -1;
  4357         -    }
         4293  +	    flushed += statePtr->bufSize;
         4294  +	    if (saved == 0 || src[-1] != '\n') {
         4295  +		needNlFlush = 0;
         4296  +	    }
         4297  +	}
         4298  +    }
         4299  +    if ((flushed < total) && (GotFlag(statePtr, CHANNEL_UNBUFFERED) ||
         4300  +	    (needNlFlush && GotFlag(statePtr, CHANNEL_LINEBUFFERED)))) {
         4301  +	SetFlag(statePtr, BUFFER_READY);
         4302  +	if (FlushChannel(NULL, chanPtr, 0) != 0) {
         4303  +	    return -1;
         4304  +	}
         4305  +    }
         4306  +
  4358   4307       return total;
  4359   4308   }
  4360   4309   
  4361   4310   /*
  4362   4311    *---------------------------------------------------------------------------
  4363   4312    *
  4364   4313    * TranslateOutputEOL --
................................................................................
  7592   7541   	sz = 1;
  7593   7542       } else if (sz > MAX_CHANNEL_BUFFER_SIZE) {
  7594   7543   	sz = MAX_CHANNEL_BUFFER_SIZE;
  7595   7544       }
  7596   7545   
  7597   7546       statePtr = ((Channel *) chan)->state;
  7598   7547       statePtr->bufSize = sz;
  7599         -
  7600         -    if (statePtr->outputStage != NULL) {
  7601         -	ckfree(statePtr->outputStage);
  7602         -	statePtr->outputStage = NULL;
  7603         -    }
  7604         -    if ((statePtr->encoding != NULL) && GotFlag(statePtr, TCL_WRITABLE)) {
  7605         -	statePtr->outputStage = ckalloc(statePtr->bufSize + 2);
  7606         -    }
  7607   7548   }
  7608   7549   
  7609   7550   /*
  7610   7551    *----------------------------------------------------------------------
  7611   7552    *
  7612   7553    * Tcl_GetChannelBufferSize --
  7613   7554    *
................................................................................
  8240   8181   	    && (statePtr->inQueueHead->nextPtr == NULL)
  8241   8182   	    && IsBufferEmpty(statePtr->inQueueHead)) {
  8242   8183   	RecycleBuffer(statePtr, statePtr->inQueueHead, 1);
  8243   8184   	statePtr->inQueueHead = NULL;
  8244   8185   	statePtr->inQueueTail = NULL;
  8245   8186       }
  8246   8187   
  8247         -    /*
  8248         -     * If encoding or bufsize changes, need to update output staging buffer.
  8249         -     */
  8250         -
  8251         -    if (statePtr->outputStage != NULL) {
  8252         -	ckfree(statePtr->outputStage);
  8253         -	statePtr->outputStage = NULL;
  8254         -    }
  8255         -    if ((statePtr->encoding != NULL) && GotFlag(statePtr, TCL_WRITABLE)) {
  8256         -	statePtr->outputStage = ckalloc(statePtr->bufSize + 2);
  8257         -    }
  8258   8188       return TCL_OK;
  8259   8189   }
  8260   8190   
  8261   8191   /*
  8262   8192    *----------------------------------------------------------------------
  8263   8193    *
  8264   8194    * CleanupChannelHandlers --