TclZlib(3)

πŸ’– TclZlib(3) πŸ’–

okay bestie, welcome to the Tcl Library Procedures page for TclZlib β€” yes, the compression/decompression one, and it's lowkey iconic. We're doing part 1, so hydrate, sit pretty, and let's get into it. 🫢✨


✨ NAME ✨

Tcl_ZlibAdler32, Tcl_ZlibCRC32, Tcl_ZlibDeflate, Tcl_ZlibInflate, Tcl_ZlibStreamChecksum, Tcl_ZlibStreamClose, Tcl_ZlibStreamEof, Tcl_ZlibStreamGet, Tcl_ZlibStreamGetCommandName, Tcl_ZlibStreamInit, Tcl_ZlibStreamPut β€” compression and decompression functions. πŸ’…


✨ SYNOPSIS ✨

Bestie, the C API said:

#include <tcl.h>

int
Tcl_ZlibDeflate(interp, format, dataObj, level, dictObj)

int
Tcl_ZlibInflate(interp, format, dataObj, bufferSize, dictObj)

unsigned int
Tcl_ZlibCRC32(initValue, bytes, length)

unsigned int
Tcl_ZlibAdler32(initValue, bytes, length)

int
Tcl_ZlibStreamInit(interp, mode, format, level, dictObj, zshandlePtr)

Tcl_Obj *
Tcl_ZlibStreamGetCommandName(zshandle)

int
Tcl_ZlibStreamEof(zshandle)

int
Tcl_ZlibStreamClose(zshandle)

int
Tcl_ZlibStreamReset(zshandle)

int
Tcl_ZlibStreamChecksum(zshandle)

int
Tcl_ZlibStreamPut(zshandle, dataObj, flush)

int
Tcl_ZlibStreamGet(zshandle, dataObj, count)

Tcl_ZlibStreamSetCompressionDictionary(zshandle, compDict)

✨ ARGUMENTS ✨

okay, now the receipts. Every argument, every type, every in/out, no skipping. πŸ’–

  • Tcl_Interp *interp (in) β€” the interpreter where the resulting compressed or uncompressed data gets stored. It's also where any error messages are written. For Tcl_ZlibStreamInit, this can be NULL to create a stream that is not bound to a command. πŸ‘‘
  • int format (in) β€” what format of compressed data you're working with. Must be one of TCL_ZLIB_FORMAT_ZLIB for zlib-format data, TCL_ZLIB_FORMAT_GZIP for gzip-format data, or TCL_ZLIB_FORMAT_RAW for raw compressed data. In addition, for decompression only, TCL_ZLIB_FORMAT_AUTO may also be chosen, which can automatically detect whether the compressed data was in zlib or gzip format. ✨
  • Tcl_Obj *dataObj (in/out) β€” a byte-array value containing the data to be compressed or decompressed, or to which the data extracted from the stream is appended when passed to Tcl_ZlibStreamGet. πŸ¦‹
  • int level (in) β€” what level of compression to use. Should be a number from 0 to 9, or one of these: TCL_ZLIB_COMPRESS_NONE for no compression, TCL_ZLIB_COMPRESS_FAST for fast but inefficient compression, TCL_ZLIB_COMPRESS_BEST for slow but maximal compression, or TCL_ZLIB_COMPRESS_DEFAULT for the level recommended by the zlib library. πŸ’…
  • 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 format is TCL_ZLIB_FORMAT_GZIP or TCL_ZLIB_FORMAT_AUTO. 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. See the section GZIP OPTIONS DICTIONARY for details about the contents of this dictionary. πŸŽ€
  • unsigned int initValue (in) β€” the initial value for the checksum algorithm. ⭐
  • Tcl_Size bufferSize (in) β€” a hint as to what size of buffer is to be used to receive the data. Use 0 to use a geric guess based on the input data. 😭
  • 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. 🧚
  • Tcl_Size length (in) β€” the number of bytes in the array. 🌸
  • int mode (in) β€” what mode to operate the stream in. Should be either TCL_ZLIB_STREAM_DEFLATE for a compressing stream or TCL_ZLIB_STREAM_INFLATE for a decompressing stream. πŸ’œ
  • Tcl_ZlibStream *zshandlePtr (out) β€” a pointer to a variable in which to write the abstract token for the stream upon successful creation. πŸ‘‘
  • Tcl_ZlibStream zshandle (in) β€” the abstract token for the stream to operate on. 🫢
  • int flush (in) β€” whether and how to flush the stream after writing the data to it. Must be one of: TCL_ZLIB_NO_FLUSH if no flushing is to be done, TCL_ZLIB_FLUSH if the currently compressed data must be made available for access using Tcl_ZlibStreamGet, TCL_ZLIB_FULLFLUSH if the stream must be put into a state where the decompressor can recover from on corruption, or TCL_ZLIB_FINALIZE to ensure that the stream is finished and that any trailer demanded by the format is written. πŸ”₯
  • Tcl_Size count (in) β€” the maximum number of bytes to get from the stream, or -1 to get all remaining bytes from the stream's buffers. ✨
  • Tcl_Obj *compDict (in) β€” a byte array value that is the compression dictionary to use with the stream.
⚠️ note that this is not a Tcl dictionary, and it is recommended that this only ever be used with streams that were created with their format set to TCL_ZLIB_FORMAT_ZLIB because the other formats have no mechanism to indicate whether a compression dictionary was present other than to fail on decompression. πŸ’€

πŸ’– DESCRIPTION πŸ’–

Okay bestie, so these functions? They're literally the whole interface from the Tcl library over to the Zlib library, which was written by Jean-loup Gailly and Mark Adler. Legends, honestly. πŸ‘‘

Tcl_ZlibDeflate and Tcl_ZlibInflate respectively compress and decompress the data sitting inside the dataObj argument β€” following whatever the format argument says, and for compression, the level argument too. The dictionary in the dictObj parameter is the little messenger that carries extra header info about the compressed data whenever the compression format actually supports that; and rn, that dictionary is only used when format is TCL_ZLIB_FORMAT_GZIP or TCL_ZLIB_FORMAT_AUTO. Wanna know what goes inside that dictionary? Scroll down to the GZIP OPTIONS DICTIONARY section, babes. πŸ“œ

On success, both of those functions leave the resulting compressed-or-decompressed data in a byte-array value that is the Tcl interpreter's result, and the thing they return is a standard Tcl result code. Simple as that. ✨

Tcl_ZlibAdler32 and Tcl_ZlibCRC32 compute checksums over arrays of bytes and hand you back the computed checksum. Now here's the cute part β€” checksums get computed incrementally, so you can process your data one block at a time if you want. The catch? You have to hold onto the current checksum yourself and pass it in as the initValue parameter. And how do you get that starting value? Just pass NULL for the bytes parameter instead of a pointer to the array of bytes you're checksumming. So the typical single-data-block usage looks exactly like this:

checksum = Tcl_ZlibCRC32(Tcl_ZlibCRC32(0,NULL,0), data, length);
Real talk though: the Adler-32 algorithm is not a real checksum β€” it's actually a related type of hash, and it works best on longer data. So don't come for it when it acts different. πŸ’…

🌊 ZLIB STREAMS 🌊

Tcl_ZlibStreamInit creates a compressing or decompressing stream that's linked to a Tcl command, according to its arguments, and it gives you back an abstract token for the stream plus a normal Tcl result code. Then Tcl_ZlibStreamGetCommandName gives you the name of that command when you hand it the stream token β€” or NULL if that stream has no command at all. πŸ™ˆ

Important, no cap: streams are not designed to be thread-safe. Each stream should only ever be used from the thread that created it. Periodt. 🧡🚫

When you're working with gzip streams, you can pass in a dictionary via the dictObj parameter (fields are all listed in the GZIP OPTIONS DICTIONARY section below) β€” and that dictionary lets you control the generated headers on compression, and lets you discover the existing headers on decompression. Heads up: on decompression the dictionary gets written to once enough data has been read to have a complete header. Which means in that case the dictionary must be an unshared value β€” a blank value created with Tcl_NewObj is the suggested move. πŸ“

Once a stream has been constructed, Tcl_ZlibStreamPut is your girl for adding data to the stream, and Tcl_ZlibStreamGet is your girl for retrieving data from the stream after processing. Both return normal Tcl result codes, and in the error case they leave an error message in the result of the interpreter that the stream is registered with (if such a registration has been performed). With Tcl_ZlibStreamPut, the data buffer value you pass in should not be modified afterwards. With Tcl_ZlibStreamGet, the data buffer value you pass in will have the data bytes appended to it. And internally? The stream keeps data compressed so the cost of buffer space stays low. Efficiency slay. πŸ’…βœ¨

Tcl_ZlibStreamChecksum returns the checksum computed over the uncompressed data, according to the format. And Tcl_ZlibStreamEof returns a boolean value telling you whether the end of the uncompressed data has been reached. Yes or no, bestie, that's it. πŸ¦‹

Tcl_ZlibStreamSetCompressionDictionary is how you control the compression dictionary used with the stream. And what even is a compression dictionary? It's an array of bytes (like, the kind you'd create with Tcl_NewByteArrayObj) that gets used to initialize the compression engine β€” instead of letting the engine build one on the fly from the data being compressed. Setting a compression dictionary gets you more efficient compression when the start of your data is highly regular, but it does require the compressor and the decompressor to agree on the value. 🀝

Compression dictionaries are only fully supported for zlib-format data. So:

  • On compression, you must set them before any data is sent in with Tcl_ZlibStreamPut.
  • On decompression, you should set them when Tcl_ZlibStreamGet produces an error with its -errorcode set to ZLIB NEED_DICT code β€” and that code will be the Adler-32 checksum (see Tcl_ZlibAdler32) of the compression dictionary being sought.
Little note: that whole errorcode situation is only true for zlib-format streams. gzip streams just ignore compression dictionaries, because the format specification doesn't permit them β€” and raw streams will straight up produce a data error if the compression dictionary is missing or wrong. πŸ’€

Wanna clear a stream and reuse it for a brand new compression or decompression action? Tcl_ZlibStreamReset does exactly that and returns a normal Tcl result code to tell you if it worked. And if the stream is registered with an interpreter, an error message gets left in the interpreter result whenever this function returns TCL_ERROR.

And finally β€” the goodbye kiss β€” Tcl_ZlibStreamClose cleans up the stream and deletes the associated command. Fun fact: using Tcl_DeleteCommand on the stream's command is equivalent to doing that (when such a command exists). 🌈


πŸŽ€ GZIP OPTIONS DICTIONARY πŸŽ€

The dictObj parameter to Tcl_ZlibDeflate, Tcl_ZlibInflate and Tcl_ZlibStreamInit is how you pass a dictionary of options that describes the gzip header inside the compressed data. When you're creating compressed data, the dictionary gets read. When you're unpacking compressed data, the dictionary gets written β€” and in that case the dictObj parameter must refer to an unshared dictionary value, okay? πŸ‘€

Here are the fields in the dictionary value that are understood. Everything else? Ignored, boo. And when you're creating a gzip-format stream, no field is required. πŸ“

comment

This one holds the comment field of the header, if present. If it's absent, no comment was supplied (on decompression) or will be created (on compression). πŸ’¬

crc

A boolean value describing whether a CRC of the header is computed. Fun little tea: the gzip program does not use or allow a CRC on the header. πŸ™ˆ

filename

The name of the file that held the uncompressed data. This should not contain any directory separators, and should be sanitized before use on decompression with file tail. Safety first, babe. πŸ”’

os

The operating system type code field from the header (if it's not the β€œunknown” value). See RFC 1952 for the meaning of these codes. On compression, if this is absent then the field will be set to the β€œunknown” value. 🌍

size

The size of the uncompressed data. This gets ignored on compression β€” because the size of the data compressed depends on how much data is supplied to the compression engine. 🀷

time

The time field from the header, if non-zero, expected to be the time that the file named by the filename field was modified. Suitable for use with clock format. On creation, the right value to use is the one from clock seconds or file mtime. ⏰

type

The type of the uncompressed data β€” either binary or text β€” if known. 🧚


πŸ”₯ REFERENCE COUNT MANAGEMENT πŸ”₯

Okay this part is the responsible-adult section, but it's still important, bestie. Stay with me.

  • Tcl_ZlibDeflate and Tcl_ZlibInflate take a value with arbitrary reference count for their dataObj and dictObj arguments (the latter often being NULL instead), and they set the interpreter result with their output value (or an error). The existing interpreter result should not be passed as any argument value unless an additional reference is held. 🫢
  • Tcl_ZlibStreamInit takes a value with arbitrary reference count for its dictObj argument β€” and it only reads from it. The existing interpreter result should not be passed unless an additional reference is held.
  • Tcl_ZlibStreamGetCommandName returns a zero reference count value, much like Tcl_NewObj.
  • The dataObj argument to Tcl_ZlibStreamPut is a value with arbitrary reference count; it is only ever read from.
  • The dataObj argument to Tcl_ZlibStreamGet is an unshared value (see Tcl_IsShared) that will be updated by the function.
  • The compDict argument to Tcl_ZlibStreamSetCompressionDictionary, if non-NULL, may be duplicated or may have its reference count incremented. Using a zero reference count value is not recommended. πŸ’€

🌈 PORTABILITY NOTES 🌈

These functions will fail gracefully if Tcl is not linked with the zlib library. No drama, no crash. She's polite like that. πŸ’…


πŸ‘€ SEE ALSO πŸ‘€


⭐ KEYWORDS ⭐

compress, decompress, deflate, gzip, inflate


And that's a wrap on this page, bestie!! πŸŽ€ This one came from the Tcl manual, version 8.6, and it's dated TclZlib(3) β€” so yeah, you've officially read the whole thing. Go compress something iconic. Mwah. πŸ’–βœ¨πŸ¦‹

πŸ“¦ where this page comes from