Skip to content

Instantly share code, notes, and snippets.

@onionhammer
Created January 16, 2014 20:41
Show Gist options
  • Save onionhammer/8463022 to your computer and use it in GitHub Desktop.
Save onionhammer/8463022 to your computer and use it in GitHub Desktop.
# Imports
import zlib
import sockets, jester
# Types
type TCompressStream* = ref object of TObject
client: TSocket
when defined(usegzip):
stream: TZStream
buffer: string
len*: int
checksum: int
# Fields
const CHUNK = 16 * 1024 # 16 Kilobytes
const MAX = 4294967296
const LEVEL = 1 # Z_DEFAULT_COMPRESSION
# Build gzip header
var gzipHeader = newString(10)
gzipHeader[0] = char(0x1f)
gzipHeader[1] = char(0x8b)
gzipHeader[2] = char(8)
gzipHeader[8] = char(4)
# Normal behavior
template send(stream: TCompressStream, value: string): expr =
stream.client.send(value)
template send(stream: TCompressStream, value: pointer, size: int): expr =
stream.client.send(value, size)
# Utilities
template put(into: var string, value: int, at = 0) =
into[at] = char((value shr 24) and 0xff)
into[at + 1] = char((value shr 16) and 0xff)
into[at + 2] = char((value shr 8) and 0xff)
into[at + 3] = char(value and 0xff)
template test(condition) =
if condition: continue
break
proc minifier*(client: TSocket): TCompressStream =
## Create a minifier stream object, removing
## whitespace, compressing with GZIP and writing
## the output to the http response stream
result = TCompressStream(client: client)
when defined(usegzip):
result.stream = TZStream(zalloc: nil, zfree: nil, opaque: nil)
result.buffer = newString(CHUNK)
# Initialize GZIP stream
let ret = zlib.deflateInit(result.stream, LEVEL)
assert ret == Z_OK, "Failed to initialize TZStream"
# Write GZIP header to response stream
client.send(gzipHeader)
template do_deflate(flush: expr) =
var ret: int
var have: zlib.uint
while true:
writer.stream.avail_out = CHUNK
writer.stream.next_out = writer.buffer
ret = zlib.deflate(writer.stream, flush)
have = CHUNK - writer.stream.avail_out
assert ret != Z_STREAM_ERROR
# Ensure first 2 bytes are skipped & last 5 bytes are skipped
var src_index =
if writer.len == 0: 2
else: 0
var dest_index =
if ret == Z_STREAM_END: have - 4
else: have - zlib.uint(src_index)
# Write to destination
discard writer.send(addr writer.buffer[src_index], dest_index)
# Until all bytes have been processed
test writer.stream.avail_out == 0
assert writer.stream.avail_in == 0, "All input must be used"
when flush == Z_FINISH:
assert ret == Z_STREAM_END
proc close*(writer: TCompressStream) =
## Flush stream & write GZIP footer
do_deflate(Z_FINISH)
# Write GZIP footer
var footer = newString(8)
footer.put(writer.checksum)
footer.put(writer.len mod MAX, 4)
writer.send(footer)
# Cleanup
discard zlib.deflateEnd(writer.stream)
proc `&=`*(writer: TCompressStream, value: string) =
## Put string into minifier
when defined(usegzip):
# Compress value & send to underlying response
writer.stream.avail_in = zlib.uint(value.len)
writer.stream.next_in = value
# Update source length & checksum
var to_write = writer.stream.avail_in
writer.checksum = zlib.crc32(writer.checksum, writer.buffer, to_write)
# Write to stream
do_deflate(Z_NO_FLUSH)
# Increment amount written
inc(writer.len, to_write)
else:
writer.send(value.minify)
template add*(writer, value) =
## Put string into minifier
writer &= value
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment