** Documentation ** * http://www.tcl.tk/man/tcl/TclCmd/zlib.htm#M43 : '''zlib stream''' ''mode'' ?''level''? This command, part of [zlib], creates a streaming compression or decompression command, allowing greater control over the compression/decompression process. It returns the name of the stream instance command. The ''mode'' must be one of '''compress''', '''decompress''', '''deflate''', '''inflate''', '''gzip''', '''gunzip'''. The optional ''level'', which is only valid for compressing streams, gives the compression level from 0 (none) to 9 (max). The returned ''streamInst'' command will support the following subcommands: : ''streamInst'' '''add''' ?''option''? ''data'' ** Shortcut for a '''put''' followed by a '''get'''. : ''streamInst'' '''checksum''' ** Returns the current checksum of the uncompressed data, calculated using the appropriate algorithm for the stream's ''mode''. : ''streamInst'' '''close''' ** Disposes of the ''streamInst'' command. Deleting with [rename] works the same. : ''streamInst'' '''eof''' ** Returns whether the end of the input data has been reached. : ''streamInst'' '''finalize''' ** Shortcut for “''streamInst'' '''put -finalize''' `{}`”. : ''streamInst'' '''flush''' ** Shortcut for “''streamInst'' '''put -flush''' `{}`”. : ''streamInst'' '''fullflush''' ** Shortcut for “''streamInst'' '''put -fullflush''' `{}`”. : ''streamInst'' '''get''' ?''count''? ** Return up to ''count'' bytes from the stream's internal buffers. If ''count'' is unspecified, return as much as is available (without flushing). : ''streamInst'' '''put''' ?''option''? ''data'' ** Appends the bytes ''data'' to the stream, compressing or decompressing as necessary. The ''option'' controls the type of flush done: '''-flush''' means to ensure that all data appended to the stream has been processed and made ready for '''get''' at some compression performance penalty, '''-fullflush''' also makes sure that the compression engine can restart from the point after the flush (at more penalty), and '''-finalize''' states that no more data will be written to the stream, causing any trailing bytes required by the format to be written. : ''streamInst'' '''reset''' ** Recreates the stream, ready to start afresh. Discards whatever is in the stream's buffers. ** Example - streaming over sockets ** For simple zlib streaming over sockets like in HTTP, [zlib push] is sufficient. This breaks down more interactive protocols, as it gives you no way to control when a block is flushed to the receiver. If you want to flush each line, for example, you will need something like the following. This code simply forces a flush each time `$zchan write` is called. If that proves insufficient, simply remove the `flush` flag in `method write` and call the object's `method flush` directly. This code was inspired by https://github.com/lehenbauer/compressed_socket_speriments%|%an experiment by karll%|% See http://www.zlib.net/manual.html%|%zlib manual%|% and http://www.bolet.org/~pornin/deflate-flush.html for more detail on Zlib's flushing modes. === # it appears that [$transchan flush] doesn't get called any time interesting. # So each [$transchan write] needs to flush by itself. # # Flushing an already flushed stream is a harmless error {TCL ZLIB BUF}, so we catch it # oo::class create zchan { variable Stream variable Chan variable Mode constructor {mode} { set Stream [zlib stream $mode] # oo::objdefine [self] forward stream $Stream } method initialize {chan mode} { set Chan $chan set Mode $mode if {$mode eq "write"} { return {initialize finalize write flush} } elseif {$mode eq "read"} { return {initialize finalize read drain} } } method finalize {chan} { my destroy } method write {chan data} { try { $Stream add -flush $data # equivalent to: # $Stream put $data # $Stream flush # $Stream get } trap {TCL ZLIB BUF} {} { return "" } } method flush {chan} { try { $Stream add -flush {} # equivalent to: # $Stream flush # $Stream get } trap {TCL ZLIB BUF} {} { return "" } } method read {chan data} { $Stream add $data } method drain {chan} { $Stream add -finalize {} } } if 0 { lassign [chan pipe] r w chan configure $w -translation binary -buffering none chan configure $r -translation binary -blocking 0 lassign {gzip gunzip} out in puts $w "Frumious bandersnatch!" puts "read: [gets $r]" chan push $w [zchan create gw $out] chan push $r [zchan create gr $in] puts $w "Vorpal snacks!" puts "read: [gets $r]" puts $w "And bric-a-brac!" puts "read: [gets $r]" chan pop $w chan pop $r puts $w "Galumphing back" puts "read: [gets $r]" } === <> Command | Compression