Tcl Source Code

Changes On Branch bug-3525907
Login

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

Changes In Branch bug-3525907 Excluding Merge-Ins

This is equivalent to a diff from ba6087ec84 to 8971a30409

2012-05-23
13:13
* generic/tclZlib.c (ZlibTransformInput): [Bug 3525907]: Ensure that decompressed input is flushed t... check-in: 0252132d3a user: dkf tags: trunk
2012-05-22
20:28
Use zero-delays instead of finite ones when posting fileevents, because (1) they should be zero in t... Closed-Leaf check-in: 8971a30409 user: ferrieux tags: bug-3525907
2012-05-20
20:46
Add test showing both loss of bytes and empty-fileevent frenzy. check-in: 1897b2023d user: ferrieux tags: bug-3525907
07:58
* generic/tclOOBasic.c (TclOO_Class_Constructor): [Bug 2023112]: Cut the amount of hackiness in cl...
check-in: 5855f07bc4 user: dkf tags: trunk
2012-05-17
21:47
Brought bugfix branch uptodate with head development. check-in: c003b262df user: andreask tags: bug-3525907
17:26
merge trunk check-in: 22b8f68842 user: dkf tags: tip-400-impl
17:20
Cancel the timeout timers! If this isn't done, lingering timers from early tests can trigger false... check-in: ba6087ec84 user: dgp tags: trunk
16:43
* generic/tclCmdMZ.c (Tcl_SwitchObjCmd): [Bug 3106532]: Corrected resulting indexes from -indexvar...
check-in: 0fb04d2cdf user: dkf tags: trunk, potential incompatibility

Changes to generic/tclIORTrans.c.

434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
static const char *msg_dstlost =
    "-code 1 -level 0 -errorcode NONE -errorinfo {} -errorline 1 {Owner lost}";

/*
 * Timer management (flushing out buffered data via artificial events).
 */

/*
 * Number of milliseconds to wait before firing an event to try to flush out
 * information waiting in buffers (fileevent support).
 */

#define FLUSH_DELAY	(5)

/*
 * Helper functions encapsulating some of the thread forwarding to make the
 * control flow in callers easier.
 */

static void		TimerKill(ReflectedTransform *rtPtr);
static void		TimerSetup(ReflectedTransform *rtPtr);







<
<
<
<
<
<
<







434
435
436
437
438
439
440







441
442
443
444
445
446
447
static const char *msg_dstlost =
    "-code 1 -level 0 -errorcode NONE -errorinfo {} -errorline 1 {Owner lost}";

/*
 * Timer management (flushing out buffered data via artificial events).
 */








/*
 * Helper functions encapsulating some of the thread forwarding to make the
 * control flow in callers easier.
 */

static void		TimerKill(ReflectedTransform *rtPtr);
static void		TimerSetup(ReflectedTransform *rtPtr);
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
}

/*
 *----------------------------------------------------------------------
 *
 * ReflectOutput --
 *
 *	This function is invoked when data is writen to the channel.
 *
 * Results:
 *	The number of bytes actually written.
 *
 * Side effects:
 *	Allocates memory. Arbitrary, as it calls upon a script.
 *







|







1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
}

/*
 *----------------------------------------------------------------------
 *
 * ReflectOutput --
 *
 *	This function is invoked when data is written to the channel.
 *
 * Results:
 *	The number of bytes actually written.
 *
 * Side effects:
 *	Allocates memory. Arbitrary, as it calls upon a script.
 *
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
TimerSetup(
    ReflectedTransform *rtPtr)
{
    if (rtPtr->timer != NULL) {
	return;
    }

    rtPtr->timer = Tcl_CreateTimerHandler(FLUSH_DELAY, TimerRun, rtPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * TimerRun --
 *







|







2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
TimerSetup(
    ReflectedTransform *rtPtr)
{
    if (rtPtr->timer != NULL) {
	return;
    }

    rtPtr->timer = Tcl_CreateTimerHandler(0, TimerRun, rtPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * TimerRun --
 *

Changes to generic/tclZlib.c.

13
14
15
16
17
18
19

20
21
22
23
24
25
26
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "tclInt.h"
#ifdef HAVE_ZLIB
#include <zlib.h>


/*
 * Magic flags used with wbits fields to indicate that we're handling the gzip
 * format or automatic detection of format. Putting it here is slightly less
 * gross!
 */








>







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "tclInt.h"
#ifdef HAVE_ZLIB
#include <zlib.h>
#include "tclIO.h"

/*
 * Magic flags used with wbits fields to indicate that we're handling the gzip
 * format or automatic detection of format. Putting it here is slightly less
 * gross!
 */

86
87
88
89
90
91
92

93
94
95
96
97
98
99
    int inAllocated, outAllocated;
				/* Sizes of working buffers. */
    GzipHeader inHeader;	/* Header read from input stream, when
				 * decompressing a gzip stream. */
    GzipHeader outHeader;	/* Header to write to an output stream, when
				 * compressing a gzip stream. */
    Tcl_TimerToken timer;	/* Timer used for keeping events fresh. */

} ZlibChannelData;

/*
 * Value bits for the flags field. Definitions are:
 *	ASYNC -		Whether this is an asynchronous channel.
 *	IN_HEADER -	Whether the inHeader field has been registered with
 *			the input compressor.







>







87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
    int inAllocated, outAllocated;
				/* Sizes of working buffers. */
    GzipHeader inHeader;	/* Header read from input stream, when
				 * decompressing a gzip stream. */
    GzipHeader outHeader;	/* Header to write to an output stream, when
				 * compressing a gzip stream. */
    Tcl_TimerToken timer;	/* Timer used for keeping events fresh. */
    Tcl_DString    result;      /* Buffer for decompression results */
} ZlibChannelData;

/*
 * Value bits for the flags field. Definitions are:
 *	ASYNC -		Whether this is an asynchronous channel.
 *	IN_HEADER -	Whether the inHeader field has been registered with
 *			the input compressor.
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/*
 * Size of buffers allocated by default. Should be enough...
 */

#define DEFAULT_BUFFER_SIZE	4096

/*
 * Time to wait (in milliseconds) before flushing the channel when reading
 * data through the transform.
 */

#define TRANSFORM_FLUSH_DELAY	5

/*
 * Prototypes for private procedures defined later in this file:
 */

static Tcl_CmdDeleteProc	ZlibStreamCmdDelete;
static Tcl_DriverBlockModeProc	ZlibTransformBlockMode;







|
<


|







110
111
112
113
114
115
116
117

118
119
120
121
122
123
124
125
126
127
/*
 * Size of buffers allocated by default. Should be enough...
 */

#define DEFAULT_BUFFER_SIZE	4096

/*
 * Convenience macro to make some casts easier to use.

 */

#define UCHARP(x)	((unsigned char *) (x))

/*
 * Prototypes for private procedures defined later in this file:
 */

static Tcl_CmdDeleteProc	ZlibStreamCmdDelete;
static Tcl_DriverBlockModeProc	ZlibTransformBlockMode;
143
144
145
146
147
148
149



150
151
152
153
154
155
156
			    int mode, int format, int level,
			    Tcl_Channel channel, Tcl_Obj *gzipHeaderDictPtr);
static void		ZlibStreamCleanup(ZlibStreamHandle *zshPtr);
static void		ZlibTransformTimerKill(ZlibChannelData *cd);
static void		ZlibTransformTimerRun(ClientData clientData);
static void		ZlibTransformTimerSetup(ZlibChannelData *cd);




/*
 * Type of zlib-based compressing and decompressing channels.
 */

static const Tcl_ChannelType zlibChannelType = {
    "zlib",
    TCL_CHANNEL_VERSION_3,







>
>
>







144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
			    int mode, int format, int level,
			    Tcl_Channel channel, Tcl_Obj *gzipHeaderDictPtr);
static void		ZlibStreamCleanup(ZlibStreamHandle *zshPtr);
static void		ZlibTransformTimerKill(ZlibChannelData *cd);
static void		ZlibTransformTimerRun(ClientData clientData);
static void		ZlibTransformTimerSetup(ZlibChannelData *cd);

static int		ResultCopy(Tcl_DString* r, unsigned char *buf, int toRead);
static int		ResultGenerate(ZlibChannelData *cd, int n, int flush, int* errorCodePtr);

/*
 * Type of zlib-based compressing and decompressing channels.
 */

static const Tcl_ChannelType zlibChannelType = {
    "zlib",
    TCL_CHANNEL_VERSION_3,
2316
2317
2318
2319
2320
2321
2322


2323
2324
2325
2326
2327
2328
2329
	e = inflateEnd(&cd->inStream);
    }

    /*
     * Release all memory.
     */



    if (cd->inBuffer) {
	ckfree(cd->inBuffer);
	cd->inBuffer = NULL;
    }
    if (cd->outBuffer) {
	ckfree(cd->outBuffer);
	cd->outBuffer = NULL;







>
>







2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
	e = inflateEnd(&cd->inStream);
    }

    /*
     * Release all memory.
     */

    Tcl_DStringFree (&cd->result);

    if (cd->inBuffer) {
	ckfree(cd->inBuffer);
	cd->inBuffer = NULL;
    }
    if (cd->outBuffer) {
	ckfree(cd->outBuffer);
	cd->outBuffer = NULL;
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367


2368
2369
2370
2371
2372
2373
2374
2375

2376

2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388

2389

2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401

2402
2403
2404
2405

2406














2407


2408
2409









2410




2411



2412








2413



2414




2415





























2416
2417
2418
2419
2420
2421
2422
    char *buf,
    int toRead,
    int *errorCodePtr)
{
    ZlibChannelData *cd = instanceData;
    Tcl_DriverInputProc *inProc =
	    Tcl_ChannelInputProc(Tcl_GetChannelType(cd->parent));
    int e, readBytes, flush = Z_NO_FLUSH;

    if (cd->mode == TCL_ZLIB_STREAM_DEFLATE) {
	return inProc(Tcl_GetChannelInstanceData(cd->parent), buf, toRead,
		errorCodePtr);
    }

    cd->inStream.next_out = (Bytef *) buf;
    cd->inStream.avail_out = toRead;
    if (cd->inStream.next_in == NULL) {
	goto doReadFirst;
    }
    while (1) {
	e = inflate(&cd->inStream, flush);
	if ((e == Z_STREAM_END) || (e==Z_OK && cd->inStream.avail_out==0)) {
	    return toRead - cd->inStream.avail_out;
	}

	/*
	 * Z_BUF_ERROR can be ignored as per http://www.zlib.net/zlib_how.html
	 *
	 * Just indicates that the zlib couldn't consume input/produce output,
	 * and is fixed by supplying more input.


	 */

	if ((e != Z_OK) && (e != Z_BUF_ERROR)) {
	    Tcl_Obj *errObj = Tcl_NewListObj(0, NULL);

	    Tcl_ListObjAppendElement(NULL, errObj,
		    Tcl_NewStringObj(cd->inStream.msg, -1));
	    Tcl_SetChannelError(cd->parent, errObj);

	    *errorCodePtr = EINVAL;

	    return -1;
	}

	/*
	 * Check if the inflate stopped early.
	 */

	if (cd->inStream.avail_in > 0) {
	    continue;
	}

	/*

	 * Emptied the buffer of data from the underlying channel. Get some

	 * more.
	 */

    doReadFirst:
	/*
	 * Hack for Bug 2762041. Disable pre-reading of lots of input, read
	 * only one character. This way the Z_END_OF_STREAM can be read
	 * without triggering an EOF in the base channel. The higher input
	 * loops in DoReadChars() would react to that by stopping, despite the
	 * transform still having data which could be read.
	 *
	 * This is only a hack because other transforms may not be able to

	 * work around the general problem in this way.
	 */

	readBytes = Tcl_ReadRaw(cd->parent, cd->inBuffer, 1);

	if (readBytes < 0) {














	    *errorCodePtr = Tcl_GetErrno();


	    return -1;
	} else if (readBytes == 0) {









	    flush = Z_SYNC_FLUSH;




	}












	cd->inStream.next_in = (Bytef *) cd->inBuffer;



	cd->inStream.avail_in = readBytes;




    }





























}

static int
ZlibTransformOutput(
    ClientData instanceData,
    const char *buf,
    int toWrite,







|






<
<
<
|
<
|
<
<
<
<
<

<
<
<
<
>
>


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



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

|
>
|



>

>
>
>
>
>
>
>
>
>
>
>
>
>
>

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







2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357



2358

2359





2360




2361
2362
2363
2364


2365



2366
2367
2368

2369
2370



2371


2372
2373
2374
2375
2376
2377
2378

2379
2380






2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
    char *buf,
    int toRead,
    int *errorCodePtr)
{
    ZlibChannelData *cd = instanceData;
    Tcl_DriverInputProc *inProc =
	    Tcl_ChannelInputProc(Tcl_GetChannelType(cd->parent));
    int readBytes, gotBytes, copied;

    if (cd->mode == TCL_ZLIB_STREAM_DEFLATE) {
	return inProc(Tcl_GetChannelInstanceData(cd->parent), buf, toRead,
		errorCodePtr);
    }




    gotBytes = 0;

    while (toRead > 0) {





	/*




	 * Loop until the request is satisfied (or no data available from
	 * below, possibly EOF).
	 */



	copied = ResultCopy(&cd->result, UCHARP(buf), toRead);



	toRead -= copied;
	buf += copied;
	gotBytes += copied;


	if (toRead == 0) {



	    goto stop;


	}

	/*
	 * The buffer is exhausted, but the caller wants even more. We now
	 * have to go to the underlying channel, get more bytes and then
	 * transform them for delivery. We may not get what we want (full EOF
	 * or temporarily out of data).

	 *
	 * Length (cd->result) == 0, toRead > 0 here.






	 *
	 * The zlib transform allows us to read at most one character from the
	 * underlying channel to properly identify Z_STREAM_END without
	 * reading over the border.
	 */

	readBytes = Tcl_ReadRaw(cd->parent, cd->inBuffer, 1);

	if (readBytes < 0) {
	    /*
	     * Report errors to caller. The state of the seek system is
	     * unchanged!
	     */

	    if ((Tcl_GetErrno() == EAGAIN) && (gotBytes > 0)) {
		/*
		 * EAGAIN is a special situation. If we had some data before
		 * we report that instead of the request to re-try.
		 */

		goto stop;
	    }

	    *errorCodePtr = Tcl_GetErrno();
	    goto error;
	}

	if (readBytes == 0) {
	    /*
	     * Check wether we hit on EOF in 'parent' or not. If not
	     * differentiate between blocking and non-blocking modes. In
	     * non-blocking mode we ran temporarily out of data. Signal this
	     * to the caller via EWOULDBLOCK and error return (-1). In the
	     * other cases we simply return what we got and let the caller
	     * wait for more. On the other hand, if we got an EOF we have to
	     * convert and flush all waiting partial data.
	     */

	    if (!Tcl_Eof(cd->parent)) {
		/*
		 * The state of the seek system is unchanged!
		 */

		if ((gotBytes == 0) && (cd->flags & ASYNC)) {
		    *errorCodePtr = EWOULDBLOCK;
		    goto error;
		}
		goto stop;
	    } else {
		/*
		 * (Semi-)Eof in parent.
		 *
		 * Now this is a bit different. The partial data waiting is
		 * converted and returned.
		 */

		if (ResultGenerate (cd, 0, Z_SYNC_FLUSH, errorCodePtr) < 0) {
		    goto error;
		}

		if (Tcl_DStringLength(&cd->result) == 0) {
		    /*
		     * The drain delivered nothing.
		     */

		    goto stop;
		}

		/*
		 * Reset eof, force caller to drain result buffer.
		 */

		((Channel *) cd->parent)->state->flags &= ~CHANNEL_EOF;
		continue; /* at: while (toRead > 0) */
	    }
	} /* readBytes == 0 */

	/*
	 * Transform the read chunk, which was not empty. Anything we get back
	 * is a transformation result to be put into our buffers, and the next
	 * iteration will put it into the result.
	 */

	if (ResultGenerate (cd, readBytes, Z_NO_FLUSH, errorCodePtr) < 0) {
	    goto error;
	}
    } /* while toRead > 0 */

 stop:
    return gotBytes;

 error:
    gotBytes = -1;
    goto stop;
}

static int
ZlibTransformOutput(
    ClientData instanceData,
    const char *buf,
    int toWrite,
2517
2518
2519
2520
2521
2522
2523





2524
2525
2526
2527
2528
2529
2530
	return TCL_OK;
    }

    if (setOptionProc == NULL) {
	return Tcl_BadChannelOption(interp, optionName, chanOptions);
    }






    return setOptionProc(Tcl_GetChannelInstanceData(cd->parent), interp,
	    optionName, value);
}

static int
ZlibTransformGetOption(
    ClientData instanceData,







>
>
>
>
>







2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
	return TCL_OK;
    }

    if (setOptionProc == NULL) {
	return Tcl_BadChannelOption(interp, optionName, chanOptions);
    }

    /*
     * Pass all unknown options down, to deeper transforms and/or the base
     * channel.
     */

    return setOptionProc(Tcl_GetChannelInstanceData(cd->parent), interp,
	    optionName, value);
}

static int
ZlibTransformGetOption(
    ClientData instanceData,
2611
2612
2613
2614
2615
2616
2617

2618
2619

2620
2621
2622
2623
2624
2625
2626

    /*
     * This code is based on the code in tclIORTrans.c
     */

    watchProc = Tcl_ChannelWatchProc(Tcl_GetChannelType(cd->parent));
    watchProc(Tcl_GetChannelInstanceData(cd->parent), mask);

    if (!(mask & TCL_READABLE)
	    || (cd->inStream.avail_in == (uInt) cd->inAllocated)) {

	ZlibTransformTimerKill(cd);
    } else {
	ZlibTransformTimerSetup(cd);
    }
}

static int







>
|
<
>







2675
2676
2677
2678
2679
2680
2681
2682
2683

2684
2685
2686
2687
2688
2689
2690
2691

    /*
     * This code is based on the code in tclIORTrans.c
     */

    watchProc = Tcl_ChannelWatchProc(Tcl_GetChannelType(cd->parent));
    watchProc(Tcl_GetChannelInstanceData(cd->parent), mask);

    if (!(mask & TCL_READABLE) ||

	(Tcl_DStringLength(&cd->result) == 0)) {
	ZlibTransformTimerKill(cd);
    } else {
	ZlibTransformTimerSetup(cd);
    }
}

static int
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
}

static void
ZlibTransformTimerSetup(
    ZlibChannelData *cd)
{
    if (cd->timer == NULL) {
	cd->timer = Tcl_CreateTimerHandler(TRANSFORM_FLUSH_DELAY,
		ZlibTransformTimerRun, cd);
    }
}

static void
ZlibTransformTimerKill(
    ZlibChannelData *cd)







|







2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
}

static void
ZlibTransformTimerSetup(
    ZlibChannelData *cd)
{
    if (cd->timer == NULL) {
	cd->timer = Tcl_CreateTimerHandler(0,
		ZlibTransformTimerRun, cd);
    }
}

static void
ZlibTransformTimerKill(
    ZlibChannelData *cd)
2799
2800
2801
2802
2803
2804
2805


2806
2807
2808
2809
2810
2811
2812
	if (cd->flags & OUT_HEADER) {
	    e = deflateSetHeader(&cd->outStream, &cd->outHeader.header);
	    if (e != Z_OK) {
		goto error;
	    }
	}
    }



    chan = Tcl_StackChannel(interp, &zlibChannelType, cd,
	    Tcl_GetChannelMode(channel), channel);
    if (chan == NULL) {
	goto error;
    }
    cd->chan = chan;







>
>







2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
	if (cd->flags & OUT_HEADER) {
	    e = deflateSetHeader(&cd->outStream, &cd->outHeader.header);
	    if (e != Z_OK) {
		goto error;
	    }
	}
    }

    Tcl_DStringInit(&cd->result);

    chan = Tcl_StackChannel(interp, &zlibChannelType, cd,
	    Tcl_GetChannelMode(channel), channel);
    if (chan == NULL) {
	goto error;
    }
    cd->chan = chan;
2822
2823
2824
2825
2826
2827
2828




































































































































2829
2830
2831
2832
2833
2834
2835
    if (cd->outBuffer) {
	ckfree(cd->outBuffer);
	deflateEnd(&cd->outStream);
    }
    ckfree(cd);
    return NULL;
}





































































































































/*
 *----------------------------------------------------------------------
 *	Finally, the TclZlibInit function. Used to install the zlib API.
 *----------------------------------------------------------------------
 */








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
    if (cd->outBuffer) {
	ckfree(cd->outBuffer);
	deflateEnd(&cd->outStream);
    }
    ckfree(cd);
    return NULL;
}

/*
 *----------------------------------------------------------------------
 *
 * ResultCopy --
 *
 *	Copies the requested number of bytes from the buffer into the
 *	specified array and removes them from the buffer afterward. Copies
 *	less if there is not enough data in the buffer.
 *
 * Side effects:
 *	See above.
 *
 * Result:
 *	The number of actually copied bytes, possibly less than 'toRead'.
 *
 *----------------------------------------------------------------------
 */

static int
ResultCopy(
    Tcl_DString* ds,		/* The buffer to read from */
    unsigned char *buf,		/* The buffer to copy into */
    int toRead)			/* Number of requested bytes */
{
    int copied;
    int have = Tcl_DStringLength (ds);

    if (have == 0) {
	/*
	 * Nothing to copy in the case of an empty buffer.
	 */

	copied = 0;
    } else if (have > toRead) {
	/*
	 * The internal buffer contains more than requested. Copy the
	 * requested subset to the caller, shift the remaining bytes down, and
	 * truncate.
	 */

	char* src = Tcl_DStringValue (ds);

	memcpy(buf, src, toRead);
	memmove(src, src + toRead, have - toRead);

	Tcl_DStringSetLength (ds, have - toRead);
	copied = toRead;
    } else /* have <= toRead */ {
	/*
	 * There is just or not enough in the buffer to fully satisfy the
	 * caller, so take everything as best effort.
	 */

	memcpy(buf, Tcl_DStringValue (ds), have);
	Tcl_DStringSetLength (ds, 0);
	copied = have;
    }

    /* -- common postwork code ------- */

    return copied;
}

static int
ResultGenerate(ZlibChannelData *cd, int n, int flush, int* errorCodePtr)
{
#define MAXBUF 1024
    unsigned char buf [MAXBUF];
    int e, written;

    cd->inStream.next_in = (Bytef *) cd->inBuffer;
    cd->inStream.avail_in = n;

    while (1) {
	cd->inStream.next_out = (Bytef *) buf;
	cd->inStream.avail_out = MAXBUF;

	e = inflate(&cd->inStream, flush);

	/*
	 * avail_out is now the left over space in the output.
	 * Therefore "MAXBUF - avail_out" is the amount of bytes
	 * generated.
	 */

	written = MAXBUF - cd->inStream.avail_out;

	if (written) {
	    Tcl_DStringAppend (&cd->result, (char*) buf, written);
	}

	if (((flush == Z_SYNC_FLUSH) && (e == Z_BUF_ERROR)) ||
	    (e == Z_STREAM_END) ||
	    (e==Z_OK && cd->inStream.avail_out==0)) {
	    break;
	}

	/*
	 * Z_BUF_ERROR can be ignored as per http://www.zlib.net/zlib_how.html
	 *
	 * Just indicates that the zlib couldn't consume input/produce output,
	 * and is fixed by supplying more input.
	 */

	if ((e != Z_OK) && (e != Z_BUF_ERROR)) {
	    Tcl_Obj *errObj = Tcl_NewListObj(0, NULL);

	    Tcl_ListObjAppendElement(NULL, errObj,
				     Tcl_NewStringObj(cd->inStream.msg, -1));
	    Tcl_SetChannelError(cd->parent, errObj);
	    *errorCodePtr = EINVAL;
	    return -1;
	}

	/*
	 * Check if the inflate stopped early.
	 */

	if (cd->inStream.avail_in > 0) {
	    continue;
	}

	if (flush == Z_SYNC_FLUSH) {
	    continue;
	}

	break;
    }

    return 0;
}

/*
 *----------------------------------------------------------------------
 *	Finally, the TclZlibInit function. Used to install the zlib API.
 *----------------------------------------------------------------------
 */

Changes to tests/zlib.test.

165
166
167
168
169
170
171


















172
173
174
175
176
177
178
    puts $fd "qwertyuiop"
    fconfigure $fd -flush sync
    puts $fd "qwertyuiop"
} -cleanup {
    catch {close $fd}
    removeFile $file
} -result {}



















test zlib-9.1 "check fcopy with push" -constraints zlib -setup {
    set sfile [makeFile {} testsrc.gz]
    set file [makeFile {} test.gz]
    set f [open $sfile wb]
    puts -nonewline $f [zlib gzip [string repeat a 81920]]
    close $f







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
    puts $fd "qwertyuiop"
    fconfigure $fd -flush sync
    puts $fd "qwertyuiop"
} -cleanup {
    catch {close $fd}
    removeFile $file
} -result {}
test zlib-8.5 {transformation and flushing and fileevents: Bug 3525907} -setup {
    foreach {r w} [chan pipe] break
} -constraints zlib -body {
    set ::res {}
    fconfigure $w -buffering none
    zlib push compress $w
    puts -nonewline $w qwertyuiop
    chan configure $w -flush sync
    after 500 {puts -nonewline $w asdfghjkl;close $w}
    fconfigure $r -blocking 0 -buffering none
    zlib push decompress $r
    fileevent $r readable {set msg [read $r];lappend ::res $msg;if {[eof $r]} {set ::done 1}}
    after 250 {lappend ::res MIDDLE}
    vwait ::done
    set ::res
} -cleanup {
    catch {close $r}
} -result {qwertyuiop MIDDLE asdfghjkl}

test zlib-9.1 "check fcopy with push" -constraints zlib -setup {
    set sfile [makeFile {} testsrc.gz]
    set file [makeFile {} test.gz]
    set f [open $sfile wb]
    puts -nonewline $f [zlib gzip [string repeat a 81920]]
    close $f