From a2110892447b53f535fd7d7aba9786904a82abf8 Mon Sep 17 00:00:00 2001 From: dkf Date: Sat, 13 Dec 2008 17:36:34 +0000 Subject: Improve docs, start working towards implementing [zlib push] --- ChangeLog | 1 + doc/TclZlib.3 | 148 +++++++++++++++++++++++++++++++++++++++++++++++ doc/zlib.n | 40 ++++++++++++- generic/tclZlib.c | 169 +++++++++++++++++++++++++++++++++++------------------- 4 files changed, 296 insertions(+), 62 deletions(-) create mode 100644 doc/TclZlib.3 diff --git a/ChangeLog b/ChangeLog index 7f57ec1..6122e8b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,6 @@ 2008-12-13 Donal K. Fellows + * doc/TclZlib.3: Basic documentation of the C-level API. * doc/zlib.n: Substantially improve documentation of Tcl-level API. * generic/tclZlib.c (ZlibCmd): Flesh out the argument parsing for the command to integrate with channels. diff --git a/doc/TclZlib.3 b/doc/TclZlib.3 new file mode 100644 index 0000000..103f2e8 --- /dev/null +++ b/doc/TclZlib.3 @@ -0,0 +1,148 @@ +'\" +'\" Copyright (c) 2008 Donal K. Fellows +'\" +'\" See the file "license.terms" for information on usage and redistribution +'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES. +'\" +'\" RCS: @(#) $Id: TclZlib.3,v 1.1 2008/12/13 17:36:34 dkf Exp $ +'\" +.so man.macros +.TH TclZlib 3 8.6 Tcl "Tcl Built-In Commands" +.BS +'\" Note: do not modify the .SH NAME line immediately below! +.SH NAME +Tcl_ZlibAdler32, Tcl_ZlibCRC32, Tcl_ZlibDeflate, Tcl_ZlibInflate, Tcl_ZlibStreamAdler32, Tcl_ZlibStreamClose, Tcl_ZlibStreamEof, Tcl_ZlibStreamGet, Tcl_ZlibStreamGetCommandName, Tcl_ZlibStreamInit, Tcl_ZlibStreamPut \- compression and decompression functions +.SH SYNOPSIS +.nf +#include +.sp +Tcl_Obj * +\fBTcl_ZlibDeflate\fR(\fIinterp, format, dataObj, level, dictObj\fR) +.sp +Tcl_Obj * +\fBTcl_ZlibInflate\fR(\fIinterp, format, dataObj, dictObj\fR) +.sp +unsigned int +\fBTcl_ZlibCRC32\fR(\fIinitValue, bytes, length\fR) +.sp +unsigned int +\fBTcl_ZlibAdler32\fR(\fIinitValue, bytes, length\fR) +.sp +int +\fBTcl_ZlibStreamInit\fR(\fIinterp, mode, format, level, dictObj, zshandlePtr\fR) +.sp +Tcl_Obj * +\fBTcl_ZlibStreamGetCommandName\fR(\fIzshandle\fR) +.sp +int +\fBTcl_ZlibStreamEof\fR(\fIzshandle\fR) +.sp +int +\fBTcl_ZlibStreamClose\fR(\fIzshandle\fR) +.sp +int +\fBTcl_ZlibStreamAdler32\fR(\fIzshandle\fR) +.sp +int +\fBTcl_ZlibStreamPut\fR(\fIzshandle, dataObj, flush\fR) +.sp +int +\fBTcl_ZlibStreamGet\fR(\fIzshandle, dataObj, count\fR) +.fi +.SH ARGUMENTS +.AS Tcl_ZlibStream *zshandlePtr out +.AP Tcl_Interp *interp in +The interpreter to store resulting compressed or uncompressed data in. Also +where any error messages are written. +.AP int format in +What format of compressed data to work with. Must be one of +\fBTCL_ZLIB_FORMAT_ZLIB\fR for zlib-format data, \fBTCL_ZLIB_FORMAT_GZIP\fR +for gzip-format data, or \fBTCL_ZLIB_FORMAT_RAW\fR for raw compressed data. In +addition, for decompression only, \fBTCL_ZLIB_FORMAT_AUTO\fR may also be +chosen which can automatically detect whether the compressed data was in zlib +or gzip format. +.AP Tcl_Obj *dataObj in/out +A byte-array object containing the data to be compressed or decompressed, or +which is set to the data extracted from the stream when passed to +\fBTcl_ZlibStreamGet\fR. +.AP int level in +What level of compression to use. Should be a number from 0 to 9 or one of the +following: \fBTCL_ZLIB_COMPRESS_NONE\fR for no compression, +\fBTCL_ZLIB_COMPRESS_FAST\fR for fast but inefficient compression, +\fBTCL_ZLIB_COMPRESS_BEST\fR for slow but maximal compression, or +\fBTCL_ZLIB_COMPRESS_DEFAULT\fR for the level recommended by the zlib library. +.AP Tcl_Obj *dictObj in/out +A dictionary that contains, or which will be updated to contain, a description +of the gzip header associated with the compressed data. Only useful when the +\fIformat\fR is \fBTCL_ZLIB_FORMAT_GZIP\fR or \fBTCL_ZLIB_FORMAT_AUTO\fR. If +a NULL is passed, a default header will be used on compression and the header +will be ignored (apart from integrity checks) on decompression. +.AP "unsigned int" initValue in +The initial value for the checksum algorithm. +.AP "unsigned char" *bytes in +An array of bytes to run the checksum algorithm over, or NULL to get the +recommended initial value for the checksum algorithm. +.AP int length in +The number of bytes in the array. +.AP int mode in +What mode to operate the stream in. Should be either +\fBTCL_ZLIB_STREAM_DEFLATE\fR for a compressing stream or +\fBTCL_ZLIB_STREAM_INFLATE\fR for a decompressing stream. +.AP Tcl_ZlibStream *zshandlePtr out +A pointer to a variable in which to write the abstract token for the stream +upon successful creation. +.AP Tcl_ZlibStream zshandle in +The abstract token for the stream to operate on. +.AP int flush in +Whether and how to flush the stream after writing the data to it. Must be one +of: \fBTCL_ZLIB_NO_FLUSH\fR if no flushing is to be done, \fBTCL_ZLIB_FLUSH\fR +if the currently compressed data must be made available for access using +\fBTcl_ZlibStreamGet\fR, \fBTCL_ZLIB_FULLFLUSH\fR if the stream must be put +into a state where the decompressor can recover from on corruption, or +\fBTCL_ZLIB_FINALIZE\fR to ensure that the stream is finished and that any +trailer demanded by the format is written. +.AP int count in +The maximum number of bytes to get from the stream, or -1 to get all remaining +bytes from the stream's buffers. +.BE +.SH DESCRIPTION +These functions form the interface from the Tcl library to the Zlib +library by Jean-loup Gailly and Mark Adler. +.PP +\fBTcl_ZlibDeflate\fR and \fBTcl_ZlibInflate\fR compress and decompress data +respectively. Upon success, they leave the resulting compressed or +decompressed data in a byte-array object that is the Tcl interpreter's +result. Note that the \fIdictObj\fR parameter is only used when the +\fIformat\fR parameter is \fBTCL_ZLIB_FORMAT_GZIP\fR or +\fBTCL_ZLIB_FORMAT_AUTO\fR. +.PP +\fBTcl_ZlibAdler32\fR and \fBTcl_ZlibCRC32\fR compute checksums on arrays of +bytes. Typical usage is: +.PP +.CS +checksum = Tcl_ZlibCRC32(Tcl_ZlibCRC32(0,NULL,0), data, length); +.CE +.PP +\fBTcl_ZlibStreamInit\fR creates a compressing or decompressing stream that is +linked to a Tcl command, according to its arguments, and provides an abstract +token for the stream; \fBTcl_ZlibStreamGetCommandName\fR returns the name of +that command given the stream token. Once a stream has been constructed, +\fBTcl_ZlibStreamPut\fR is used to add data to the stream and +\fBTcl_ZlibStreamGet\fR is used to retrieve data from the stream after +processing. \fBTcl_ZlibStreamAdler32\fR returns the checksum computed over the +uncompressed data, and \fBTcl_ZlibStreamEof\fR returns whether the end of the +uncompressed data has been reached. Finally, \fBTcl_ZlibStreamClose\fR will +clean up the stream and delete the associated command: using +\fBTcl_DeleteCommand\fR on the stream's command is equivalent. +.SH "PORTABILITY NOTES" +These functions will fail gracefully if Tcl is not linked with the zlib +library. +.SH "SEE ALSO" +Tcl_NewByteArrayObj(3), zlib(n) +'\"Tcl_StackChannel(3) +.SH "KEYWORDS" +compress, decompress, deflate, gzip, inflate +'\" Local Variables: +'\" mode: nroff +'\" fill-column: 78 +'\" End: diff --git a/doc/zlib.n b/doc/zlib.n index 8937e78..b010eb4 100644 --- a/doc/zlib.n +++ b/doc/zlib.n @@ -4,7 +4,7 @@ '\" See the file "license.terms" for information on usage and redistribution '\" of this file, and for a DISCLAIMER OF ALL WARRANTIES. '\" -'\" RCS: @(#) $Id: zlib.n,v 1.3 2008/12/13 09:19:06 dkf Exp $ +'\" RCS: @(#) $Id: zlib.n,v 1.4 2008/12/13 17:36:34 dkf Exp $ '\" .so man.macros .TH zlib n 8.6 Tcl "Tcl Built-In Commands" @@ -130,7 +130,43 @@ is to be used to receive the data. .TP \fBzlib push\fI mode channel\fR ?\fIoptions ...\fR . +Pushes a compressing or decompressing transformation onto the channel +\fIchannel\fR. The \fImode\fR argument determines what type of transformation +is pushed; the following are supported: +.RS +.TP +\fBcompress\fR +. +The transformation will be a compressing transformation that produces +zlib-format data on \fIchannel\fR, which must be writable. +.TP +\fBdecompress\fR +. +The transformation will be a decompressing transformation that reads +zlib-format data from \fIchannel\R, which must be readable. +.TP +\fBdeflate\fR +. +The transformation will be a compressing transformation that produces raw +compressed data on \fIchannel\fR, which must be writable. +.TP +\fBgunzip\fR +. +The transformation will be a decompressing transformation that reads +gzip-format data from \fIchannel\R, which must be readable. +.TP +\fBgzip\fR +. +The transformation will be a compressing transformation that produces +gzip-format data on \fIchannel\fR, which must be writable. +.TP +\fBinflate\fR +. +The transformation will be a decompressing transformation that reads raw +compressed data from \fIchannel\R, which must be readable. +.PP \fITODO: not yet implemented!\fR +.RE .TP \fBzlib stream\fI mode\fR ?\fIlevel\fR? . @@ -302,7 +338,7 @@ set compData [$\fIstrm \fBget\fR] $\fIstrm \fBclose\fR .CE .SH "SEE ALSO" -binary(n), chan(n), encoding(n) +binary(n), chan(n), encoding(n), Tcl_ZlibDeflate(3) .br RFC1950 \- RFC1952 .SH "KEYWORDS" diff --git a/generic/tclZlib.c b/generic/tclZlib.c index 6f40744..00d54e6 100644 --- a/generic/tclZlib.c +++ b/generic/tclZlib.c @@ -13,7 +13,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclZlib.c,v 1.9 2008/12/13 09:19:06 dkf Exp $ + * RCS: @(#) $Id: tclZlib.c,v 1.10 2008/12/13 17:36:34 dkf Exp $ */ #include "tclInt.h" @@ -79,8 +79,6 @@ static int ChanGetOption(ClientData instanceData, static void ChanWatch(ClientData instanceData, int mask); static int ChanGetHandle(ClientData instanceData, int direction, ClientData *handlePtr); -static int ChanClose2(ClientData instanceData, - Tcl_Interp *interp, int flags); static int ChanBlockMode(ClientData instanceData, int mode); static int ChanFlush(ClientData instanceData); static int ChanHandler(ClientData instanceData, @@ -94,10 +92,10 @@ static const Tcl_ChannelType zlibChannelType = { ChanOutput, NULL, /* seekProc */ NULL, /* ChanSetOption, */ - NULL, /* ChanGetOption, */ + ChanGetOption, ChanWatch, ChanGetHandle, - NULL, /* ChanClose2, */ + NULL, /* close2Proc, */ ChanBlockMode, ChanFlush, ChanHandler, @@ -113,12 +111,19 @@ typedef struct { /* Zlib specific channel state */ int inFormat; int outFormat; - z_stream instream; - z_stream outstream; - char *inbuffer; + z_stream inStream; + z_stream outStream; + char *inBuffer; int inAllocated, inUsed, inPos; - char *outbuffer; + char *outBuffer; int outAllocated, outUsed, outPos; + + gz_header inHeader; + char inFilenameBuffer[MAXPATHLEN]; + char inCommentBuffer[256]; + gz_header outHeader; + Tcl_DString outFilenameDString; + Tcl_DString outCommentDString; } ZlibChannelData; /* Flag values */ @@ -225,6 +230,7 @@ GenerateHeader( if (GetValue(interp, dictObj, "comment", &value) != TCL_OK) { return TCL_ERROR; } else if (value != NULL) { + /* TODO: Convert to external */ headerPtr->comment = (Bytef *) Tcl_GetStringFromObj(value, &extra); *extraSizePtr += extra; } @@ -239,6 +245,7 @@ GenerateHeader( if (GetValue(interp, dictObj, "filename", &value) != TCL_OK) { return TCL_ERROR; } else if (value != NULL) { + /* TODO: Convert to external */ headerPtr->name = (Bytef *) Tcl_GetStringFromObj(value, &extra); *extraSizePtr += extra; } @@ -308,11 +315,13 @@ ExtractHeader( Tcl_Obj *dictObj) /* The dictionary to store in. */ { if (headerPtr->comment != Z_NULL) { + /* TODO: Convert from external */ SetValue(dictObj, "comment", Tcl_NewStringObj((char *) headerPtr->comment, -1)); } SetValue(dictObj, "crc", Tcl_NewBooleanObj(headerPtr->hcrc)); if (headerPtr->name != Z_NULL) { + /* TODO: Convert from external */ SetValue(dictObj, "filename", Tcl_NewStringObj((char *) headerPtr->name, -1)); } @@ -799,7 +808,7 @@ Tcl_ZlibStreamPut( obj = Tcl_NewByteArrayObj((unsigned char *) dataTmp, outSize - zsh->stream.avail_out); /* - * Now append the compressed data to the outbuffer. + * Now append the compressed data to the outData list. */ Tcl_ListObjAppendElement(zsh->interp, zsh->outData, obj); @@ -825,14 +834,14 @@ Tcl_ZlibStreamPut( outSize - zsh->stream.avail_out); /* - * Now append the compressed data to the outbuffer. + * Now append the compressed data to the outData list. */ Tcl_ListObjAppendElement(zsh->interp, zsh->outData, obj); } } else { /* - * This is easy. Just append to inbuffer. + * This is easy. Just append to the inData list. */ Tcl_ListObjAppendElement(zsh->interp, zsh->inData, data); @@ -1760,7 +1769,11 @@ ZlibCmd( } } +#if 0 + Tcl_ZlibStackChannel(interp, 0/*inFormat*/,level,0/*outFormat*/,level, chan, headerObj); +#else Tcl_AppendResult(interp, "not yet implemented", NULL); +#endif break; } }; @@ -1968,35 +1981,33 @@ ChanClose( Tcl_Interp *interp) { ZlibChannelData *cd = instanceData; - Tcl_Channel parent; + Tcl_Channel parent = Tcl_GetStackedChannel(cd->channel); int e; - parent = Tcl_GetStackedChannel(cd->channel); - if (cd->inFormat != ZLIB_PASSTHROUGH) { - if (cd->inFormat && ZLIB_INFLATE) { - e = inflateEnd(&cd->instream); + if (cd->inFormat & ZLIB_INFLATE) { + e = inflateEnd(&cd->inStream); } else { - e = deflateEnd(&cd->instream); + e = deflateEnd(&cd->inStream); } } if (cd->outFormat != ZLIB_PASSTHROUGH) { - if (cd->outFormat && ZLIB_INFLATE) { - e = inflateEnd(&cd->outstream); + if (cd->outFormat & ZLIB_INFLATE) { + e = inflateEnd(&cd->outStream); } else { - e = deflateEnd(&cd->outstream); + e = deflateEnd(&cd->outStream); } } - if (cd->inbuffer) { - ckfree(cd->inbuffer); - cd->inbuffer = NULL; + if (cd->inBuffer) { + ckfree(cd->inBuffer); + cd->inBuffer = NULL; } - if (cd->outbuffer) { - ckfree(cd->outbuffer); - cd->outbuffer = NULL; + if (cd->outBuffer) { + ckfree(cd->outBuffer); + cd->outBuffer = NULL; } return TCL_OK; } @@ -2009,7 +2020,16 @@ ChanInput( int *errorCodePtr) { ZlibChannelData *cd = instanceData; + Tcl_Channel parent = Tcl_GetStackedChannel(cd->channel); + Tcl_DriverInputProc *inProc = + Tcl_ChannelInputProc(Tcl_GetChannelType(parent)); + if (!(cd->flags & TCL_READABLE)) { + return inProc(Tcl_GetChannelInstanceData(parent), buf, toRead, + errorCodePtr); + } + + // TODO return TCL_OK; } @@ -2021,7 +2041,16 @@ ChanOutput( int *errorCodePtr) { ZlibChannelData *cd = instanceData; + Tcl_Channel parent = Tcl_GetStackedChannel(cd->channel); + Tcl_DriverOutputProc *outProc = + Tcl_ChannelOutputProc(Tcl_GetChannelType(parent)); + + if (!(cd->flags & TCL_WRITABLE)) { + return outProc(Tcl_GetChannelInstanceData(parent), buf, toWrite, + errorCodePtr); + } + // TODO return TCL_OK; } @@ -2046,12 +2075,42 @@ ChanSetOption( /* not used */ } static int -ChanGetOption( /* not used */ +ChanGetOption( ClientData instanceData, Tcl_Interp *interp, const char *optionName, Tcl_DString *dsPtr) { + ZlibChannelData *cd = instanceData; + Tcl_Channel parent = Tcl_GetStackedChannel(cd->channel); + Tcl_DriverSetOptionProc *getOptionProc = + Tcl_ChannelGetOptionProc(Tcl_GetChannelType(parent)); + + if (strcmp(optionName, "-crc") == 0) { + uLong crc; + char buf[12]; + + if (cd->flags & TCL_WRITABLE) { + crc = cd->outStream.adler; + } else { + crc = cd->inStream.adler; + } + + sprintf(buf, "0x%lx", crc); + Tcl_DStringAppend(dsPtr, buf, -1); + return TCL_OK; + } + + if (getOptionProc && getOptionProc(Tcl_GetChannelInstanceData(parent), + interp, optionName, dsPtr) != TCL_OK) { + return TCL_ERROR; + } else if (optionName != NULL) { + return TCL_ERROR; + } + + if (optionName == NULL) { + Tcl_DStringAppendElement(dsPtr, "-crc"); + } return TCL_OK; } @@ -2069,20 +2128,10 @@ ChanGetHandle( int direction, ClientData *handlePtr) { - /* - * No such thing as an OS handle for Zlib. - */ - - return 0; -} + ZlibChannelData *cd = instanceData; + Tcl_Channel parent = Tcl_GetStackedChannel(cd->channel); -static int -ChanClose2( /* not used */ - ClientData instanceData, - Tcl_Interp *interp, - int flags) -{ - return TCL_OK; + return Tcl_GetChannelHandle(parent, direction, handlePtr); } static int @@ -2165,29 +2214,29 @@ Tcl_ZlibStackChannel( cd->inFormat = inFormat; cd->outFormat = outFormat; - cd->instream.zalloc = 0; - cd->instream.zfree = 0; - cd->instream.opaque = 0; - cd->instream.avail_in = 0; - cd->instream.next_in = NULL; - cd->instream.avail_out = 0; - cd->instream.next_out = NULL; - - cd->outstream.zalloc = 0; - cd->outstream.zfree = 0; - cd->outstream.opaque = 0; - cd->outstream.avail_in = 0; - cd->outstream.next_in = NULL; - cd->outstream.avail_out = 0; - cd->outstream.next_out = NULL; + cd->inStream.zalloc = 0; + cd->inStream.zfree = 0; + cd->inStream.opaque = 0; + cd->inStream.avail_in = 0; + cd->inStream.next_in = NULL; + cd->inStream.avail_out = 0; + cd->inStream.next_out = NULL; + + cd->outStream.zalloc = 0; + cd->outStream.zfree = 0; + cd->outStream.opaque = 0; + cd->outStream.avail_in = 0; + cd->outStream.next_in = NULL; + cd->outStream.avail_out = 0; + cd->outStream.next_out = NULL; if (inFormat != ZLIB_PASSTHROUGH) { if (inFormat & ZLIB_INFLATE) { /* Initialize for Inflate */ - e = inflateInit2(&cd->instream, inwbits); + e = inflateInit2(&cd->inStream, inwbits); } else { /* Initialize for Deflate */ - e = deflateInit2(&cd->instream, inLevel, Z_DEFLATED, inwbits, + e = deflateInit2(&cd->inStream, inLevel, Z_DEFLATED, inwbits, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); } } @@ -2195,10 +2244,10 @@ Tcl_ZlibStackChannel( if (outFormat != ZLIB_PASSTHROUGH) { if (outFormat && ZLIB_INFLATE) { /* Initialize for Inflate */ - e = inflateInit2(&cd->outstream, outwbits); + e = inflateInit2(&cd->outStream, outwbits); } else { /* Initialize for Deflate */ - e = deflateInit2(&cd->outstream, outLevel, Z_DEFLATED, outwbits, + e = deflateInit2(&cd->outStream, outLevel, Z_DEFLATED, outwbits, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); } } -- cgit v0.12