# Converted from Pascal ## Interface to the zlib http://www.zlib.net/ compression library. when defined(windows): const libz = "zlib1.dll" elif defined(macosx): const libz = "libz.dylib" else: const libz = "libz.so.1" type Uint* = int32 Ulong* = int Ulongf* = int Pulongf* = ptr Ulongf ZOffT* = int32 Pbyte* = cstring Pbytef* = cstring TAllocfunc* = proc (p: pointer, items: Uint, size: Uint): pointer{.cdecl.} TFreeFunc* = proc (p: pointer, address: pointer){.cdecl.} TInternalState*{.final, pure.} = object PInternalState* = ptr TInternalState TZStream*{.final, pure.} = object nextIn*: Pbytef availIn*: Uint totalIn*: Ulong nextOut*: Pbytef availOut*: Uint totalOut*: Ulong msg*: Pbytef state*: PInternalState zalloc*: TAllocfunc zfree*: TFreeFunc opaque*: pointer dataType*: int32 adler*: Ulong reserved*: Ulong TZStreamRec* = TZStream PZstream* = ptr TZStream GzFile* = pointer const Z_NO_FLUSH* = 0 Z_PARTIAL_FLUSH* = 1 Z_SYNC_FLUSH* = 2 Z_FULL_FLUSH* = 3 Z_FINISH* = 4 Z_OK* = 0 Z_STREAM_END* = 1 Z_NEED_DICT* = 2 Z_ERRNO* = -1 Z_STREAM_ERROR* = -2 Z_DATA_ERROR* = -3 Z_MEM_ERROR* = -4 Z_BUF_ERROR* = -5 Z_VERSION_ERROR* = -6 Z_NO_COMPRESSION* = 0 Z_BEST_SPEED* = 1 Z_BEST_COMPRESSION* = 9 Z_DEFAULT_COMPRESSION* = -1 Z_FILTERED* = 1 Z_HUFFMAN_ONLY* = 2 Z_DEFAULT_STRATEGY* = 0 Z_BINARY* = 0 Z_ASCII* = 1 Z_UNKNOWN* = 2 Z_DEFLATED* = 8 Z_NULL* = 0 proc zlibVersion*(): cstring{.cdecl, dynlib: libz, importc: "zlibVersion".} proc deflate*(strm: var TZStream, flush: int32): int32{.cdecl, dynlib: libz, importc: "deflate".} proc deflateEnd*(strm: var TZStream): int32{.cdecl, dynlib: libz, importc: "deflateEnd".} proc inflate*(strm: var TZStream, flush: int32): int32{.cdecl, dynlib: libz, importc: "inflate".} proc inflateEnd*(strm: var TZStream): int32{.cdecl, dynlib: libz, importc: "inflateEnd".} proc deflateSetDictionary*(strm: var TZStream, dictionary: Pbytef, dictLength: Uint): int32{.cdecl, dynlib: libz, importc: "deflateSetDictionary".} proc deflateCopy*(dest, source: var TZStream): int32{.cdecl, dynlib: libz, importc: "deflateCopy".} proc deflateReset*(strm: var TZStream): int32{.cdecl, dynlib: libz, importc: "deflateReset".} proc deflateParams*(strm: var TZStream, level: int32, strategy: int32): int32{. cdecl, dynlib: libz, importc: "deflateParams".} proc inflateSetDictionary*(strm: var TZStream, dictionary: Pbytef, dictLength: Uint): int32{.cdecl, dynlib: libz, importc: "inflateSetDictionary".} proc inflateSync*(strm: var TZStream): int32{.cdecl, dynlib: libz, importc: "inflateSync".} proc inflateReset*(strm: var TZStream): int32{.cdecl, dynlib: libz, importc: "inflateReset".} proc compress*(dest: Pbytef, destLen: Pulongf, source: Pbytef, sourceLen: Ulong): cint{. cdecl, dynlib: libz, importc: "compress".} proc compress2*(dest: Pbytef, destLen: Pulongf, source: Pbytef, sourceLen: Ulong, level: cint): cint{.cdecl, dynlib: libz, importc: "compress2".} proc uncompress*(dest: Pbytef, destLen: Pulongf, source: Pbytef, sourceLen: Ulong): cint{.cdecl, dynlib: libz, importc: "uncompress".} proc compressBound*(sourceLen: Ulong): Ulong {.cdecl, dynlib: libz, importc.} proc gzopen*(path: cstring, mode: cstring): GzFile{.cdecl, dynlib: libz, importc: "gzopen".} proc gzdopen*(fd: int32, mode: cstring): GzFile{.cdecl, dynlib: libz, importc: "gzdopen".} proc gzsetparams*(thefile: GzFile, level: int32, strategy: int32): int32{.cdecl, dynlib: libz, importc: "gzsetparams".} proc gzread*(thefile: GzFile, buf: pointer, length: int): int32{.cdecl, dynlib: libz, importc: "gzread".} proc gzwrite*(thefile: GzFile, buf: pointer, length: int): int32{.cdecl, dynlib: libz, importc: "gzwrite".} proc gzprintf*(thefile: GzFile, format: Pbytef): int32{.varargs, cdecl, dynlib: libz, importc: "gzprintf".} proc gzputs*(thefile: GzFile, s: Pbytef): int32{.cdecl, dynlib: libz, importc: "gzputs".} proc gzgets*(thefile: GzFile, buf: Pbytef, length: int32): Pbytef{.cdecl, dynlib: libz, importc: "gzgets".} proc gzputc*(thefile: GzFile, c: char): char{.cdecl, dynlib: libz, importc: "gzputc".} proc gzgetc*(thefile: GzFile): char{.cdecl, dynlib: libz, importc: "gzgetc".} proc gzflush*(thefile: GzFile, flush: int32): int32{.cdecl, dynlib: libz, importc: "gzflush".} proc gzseek*(thefile: GzFile, offset: ZOffT, whence: int32): ZOffT{.cdecl, dynlib: libz, importc: "gzseek".} proc gzrewind*(thefile: GzFile): int32{.cdecl, dynlib: libz, importc: "gzrewind".} proc gztell*(thefile: GzFile): ZOffT{.cdecl, dynlib: libz, importc: "gztell".} proc gzeof*(thefile: GzFile): int {.cdecl, dynlib: libz, importc: "gzeof".} proc gzclose*(thefile: GzFile): int32{.cdecl, dynlib: libz, importc: "gzclose".} proc gzerror*(thefile: GzFile, errnum: var int32): Pbytef{.cdecl, dynlib: libz, importc: "gzerror".} proc adler32*(adler: Ulong, buf: Pbytef, length: Uint): Ulong{.cdecl, dynlib: libz, importc: "adler32".} ## **Warning**: Adler-32 requires at least a few hundred bytes to get rolling. proc crc32*(crc: Ulong, buf: Pbytef, length: Uint): Ulong{.cdecl, dynlib: libz, importc: "crc32".} proc deflateInitu*(strm: var TZStream, level: int32, version: cstring, streamSize: int32): int32{.cdecl, dynlib: libz, importc: "deflateInit_".} proc inflateInitu*(strm: var TZStream, version: cstring, streamSize: int32): int32 {. cdecl, dynlib: libz, importc: "inflateInit_".} proc deflateInit*(strm: var TZStream, level: int32): int32 proc inflateInit*(strm: var TZStream): int32 proc deflateInit2u*(strm: var TZStream, level: int32, `method`: int32, windowBits: int32, memLevel: int32, strategy: int32, version: cstring, streamSize: int32): int32 {.cdecl, dynlib: libz, importc: "deflateInit2_".} proc inflateInit2u*(strm: var TZStream, windowBits: int32, version: cstring, streamSize: int32): int32{.cdecl, dynlib: libz, importc: "inflateInit2_".} proc deflateInit2*(strm: var TZStream, level, `method`, windowBits, memLevel, strategy: int32): int32 proc inflateInit2*(strm: var TZStream, windowBits: int32): int32 proc zError*(err: int32): cstring{.cdecl, dynlib: libz, importc: "zError".} proc inflateSyncPoint*(z: PZstream): int32{.cdecl, dynlib: libz, importc: "inflateSyncPoint".} proc getCrcTable*(): pointer{.cdecl, dynlib: libz, importc: "get_crc_table".} proc deflateInit(strm: var TZStream, level: int32): int32 = result = deflateInitu(strm, level, zlibVersion(), sizeof(TZStream).cint) proc inflateInit(strm: var TZStream): int32 = result = inflateInitu(strm, zlibVersion(), sizeof(TZStream).cint) proc deflateInit2(strm: var TZStream, level, `method`, windowBits, memLevel, strategy: int32): int32 = result = deflateInit2u(strm, level, `method`, windowBits, memLevel, strategy, zlibVersion(), sizeof(TZStream).cint) proc inflateInit2(strm: var TZStream, windowBits: int32): int32 = result = inflateInit2u(strm, windowBits, zlibVersion(), sizeof(TZStream).cint) proc zlibAllocMem*(appData: pointer, items, size: int): pointer {.cdecl.} = result = alloc(items * size) proc zlibFreeMem*(appData, `block`: pointer) {.cdecl.} = dealloc(`block`) proc uncompress*(sourceBuf: cstring, sourceLen: int): string = ## Given a deflated cstring returns its inflated version. ## ## Passing a nil cstring will crash this proc in release mode and assert in ## debug mode. ## ## Returns nil on problems. Failure is a very loose concept, it could be you ## passing a non deflated string, or it could mean not having enough memory ## for the inflated version. ## ## The uncompression algorithm is based on ## http://stackoverflow.com/questions/17820664 but does ignore some of the ## original signed/unsigned checks, so may fail with big chunks of data ## exceeding the positive size of an int32. The algorithm can deal with ## concatenated deflated values properly. assert (not sourceBuf.isNil) var z: TZStream # Initialize input. z.nextIn = sourceBuf # Input left to decompress. var left = zlib.Uint(sourceLen) if left < 1: # Incomplete gzip stream, or overflow? return # Create starting space for output (guess double the input size, will grow if # needed -- in an extreme case, could end up needing more than 1000 times the # input size) var space = zlib.Uint(left shl 1) if space < left: space = left var decompressed = newStringOfCap(space) # Initialize output. z.nextOut = addr(decompressed[0]) # Output generated so far. var have = 0 # Set up for gzip decoding. z.availIn = 0; var status = inflateInit2(z, (15+16)) if status != Z_OK: # Out of memory. return # Make sure memory allocated by inflateInit2() is freed eventually. defer: discard inflateEnd(z) # Decompress all of self. while true: # Allow for concatenated gzip streams (per RFC 1952). if status == Z_STREAM_END: discard inflateReset(z) # Provide input for inflate. if z.availIn == 0: # This only makes sense in the C version using unsigned values. z.availIn = left left -= z.availIn # Decompress the available input. while true: # Allocate more output space if none left. if space == have: # Double space, handle overflow. space = space shl 1 if space < have: # Space was likely already maxed out. discard inflateEnd(z) return # Increase space. decompressed.setLen(space) # Update output pointer (might have moved). z.nextOut = addr(decompressed[have]) # Provide output space for inflate. z.availOut = zlib.Uint(space - have) have += z.availOut; # Inflate and update the decompressed size. status = inflate(z, Z_SYNC_FLUSH); have -= z.availOut; # Bail out if any errors. if status != Z_OK and status != Z_BUF_ERROR and status != Z_STREAM_END: # Invalid gzip stream. discard inflateEnd(z) return # Repeat until all output is generated from provided input (note # that even if z.avail_in is zero, there may still be pending # output -- we're not done until the output buffer isn't filled) if z.availOut != 0: break # Continue until all input consumed. if left == 0 and z.availIn == 0: break # Verify that the input is a valid gzip stream. if status != Z_STREAM_END: # Incomplete gzip stream. return decompressed.setLen(have) swap(result, decompressed) proc inflate*(buffer: var string): bool {.discardable.} = ## Convenience proc which inflates a string containing compressed data. ## ## Passing a nil string will crash this proc in release mode and assert in ## debug mode. It is ok to pass a buffer which doesn't contain deflated data, ## in this case the proc won't modify the buffer. ## ## Returns true if `buffer` was successfully inflated. assert (not buffer.isNil) if buffer.len < 1: return var temp = uncompress(addr(buffer[0]), buffer.len) if not temp.isNil: swap(buffer, temp) result = true