123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- # 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
- Allocfunc* = proc (p: pointer, items: Uint, size: Uint): pointer{.cdecl.}
- FreeFunc* = proc (p: pointer, address: pointer){.cdecl.}
- InternalState*{.final, pure.} = object
- PInternalState* = ptr InternalState
- ZStream*{.final, pure.} = object
- nextIn*: Pbytef
- availIn*: Uint
- totalIn*: Ulong
- nextOut*: Pbytef
- availOut*: Uint
- totalOut*: Ulong
- msg*: Pbytef
- state*: PInternalState
- zalloc*: Allocfunc
- zfree*: FreeFunc
- opaque*: pointer
- dataType*: int32
- adler*: Ulong
- reserved*: Ulong
- ZStreamRec* = ZStream
- PZstream* = ptr ZStream
- GzFile* = pointer
- ZStreamHeader* = enum
- DETECT_STREAM,
- RAW_DEFLATE,
- ZLIB_STREAM,
- GZIP_STREAM
- ZlibStreamError* = object of Exception
- {.deprecated: [TInternalState: InternalState, TAllocfunc: Allocfunc,
- TFreeFunc: FreeFunc, TZStream: ZStream, TZStreamRec: ZStreamRec].}
- 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
- Z_MEM_LEVEL* = 8
- MAX_WBITS* = 15
- proc zlibVersion*(): cstring{.cdecl, dynlib: libz, importc: "zlibVersion".}
- proc deflate*(strm: var ZStream, flush: int32): int32{.cdecl, dynlib: libz,
- importc: "deflate".}
- proc deflateEnd*(strm: var ZStream): int32{.cdecl, dynlib: libz,
- importc: "deflateEnd".}
- proc inflate*(strm: var ZStream, flush: int32): int32{.cdecl, dynlib: libz,
- importc: "inflate".}
- proc inflateEnd*(strm: var ZStream): int32{.cdecl, dynlib: libz,
- importc: "inflateEnd".}
- proc deflateSetDictionary*(strm: var ZStream, dictionary: Pbytef,
- dictLength: Uint): int32{.cdecl, dynlib: libz,
- importc: "deflateSetDictionary".}
- proc deflateCopy*(dest, source: var ZStream): int32{.cdecl, dynlib: libz,
- importc: "deflateCopy".}
- proc deflateReset*(strm: var ZStream): int32{.cdecl, dynlib: libz,
- importc: "deflateReset".}
- proc deflateParams*(strm: var ZStream, level: int32, strategy: int32): int32{.
- cdecl, dynlib: libz, importc: "deflateParams".}
- proc inflateSetDictionary*(strm: var ZStream, dictionary: Pbytef,
- dictLength: Uint): int32{.cdecl, dynlib: libz,
- importc: "inflateSetDictionary".}
- proc inflateSync*(strm: var ZStream): int32{.cdecl, dynlib: libz,
- importc: "inflateSync".}
- proc inflateReset*(strm: var ZStream): 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 ZStream, level: int32, version: cstring,
- streamSize: int32): int32{.cdecl, dynlib: libz,
- importc: "deflateInit_".}
- proc inflateInitu*(strm: var ZStream, version: cstring,
- streamSize: int32): int32 {.
- cdecl, dynlib: libz, importc: "inflateInit_".}
- proc deflateInit*(strm: var ZStream, level: int32): int32
- proc inflateInit*(strm: var ZStream): int32
- proc deflateInit2u*(strm: var ZStream, level: int32, `method`: int32,
- windowBits: int32, memLevel: int32, strategy: int32,
- version: cstring, streamSize: int32): int32 {.cdecl,
- dynlib: libz, importc: "deflateInit2_".}
- proc inflateInit2u*(strm: var ZStream, windowBits: int32, version: cstring,
- streamSize: int32): int32{.cdecl, dynlib: libz,
- importc: "inflateInit2_".}
- proc deflateInit2*(strm: var ZStream,
- level, `method`, windowBits, memLevel,
- strategy: int32): int32
- proc inflateInit2*(strm: var ZStream, 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 deflateBound*(strm: var ZStream, sourceLen: ULong): ULong {.cdecl,
- dynlib: libz, importc: "deflateBound".}
- proc deflateInit(strm: var ZStream, level: int32): int32 =
- result = deflateInitu(strm, level, zlibVersion(), sizeof(ZStream).cint)
- proc inflateInit(strm: var ZStream): int32 =
- result = inflateInitu(strm, zlibVersion(), sizeof(ZStream).cint)
- proc deflateInit2(strm: var ZStream,
- level, `method`, windowBits, memLevel,
- strategy: int32): int32 =
- result = deflateInit2u(strm, level, `method`, windowBits, memLevel,
- strategy, zlibVersion(), sizeof(ZStream).cint)
- proc inflateInit2(strm: var ZStream, windowBits: int32): int32 =
- result = inflateInit2u(strm, windowBits, zlibVersion(),
- sizeof(ZStream).cint)
- proc zlibAllocMem*(appData: pointer, items, size: int): pointer {.cdecl.} =
- result = alloc(items * size)
- proc zlibFreeMem*(appData, `block`: pointer) {.cdecl.} =
- dealloc(`block`)
- proc compress*(sourceBuf: cstring; sourceLen: int; level=Z_DEFAULT_COMPRESSION; stream=GZIP_STREAM): string =
- ## Given a cstring, returns its deflated version with an optional header.
- ##
- ## Valid argument for ``stream`` are
- ## - ``ZLIB_STREAM`` - add a zlib header and footer.
- ## - ``GZIP_STREAM`` - add a basic gzip header and footer.
- ## - ``RAW_DEFLATE`` - no header is generated.
- ##
- ## Passing a nil cstring will crash this proc in release mode and assert in
- ## debug mode.
- ##
- ## Compression level can be set with ``level`` argument. Currently
- ## ``Z_DEFAULT_COMPRESSION`` is 6.
- ##
- ## Returns "" on failure.
- assert(not sourceBuf.isNil)
- assert(sourceLen >= 0)
- var z: ZStream
- var windowBits = MAX_WBITS
- case (stream)
- of RAW_DEFLATE: windowBits = -MAX_WBITS
- of GZIP_STREAM: windowBits = MAX_WBITS + 16
- of ZLIB_STREAM, DETECT_STREAM:
- discard # DETECT_STREAM defaults to ZLIB_STREAM
- var status = deflateInit2(z, level.int32, Z_DEFLATED.int32,
- windowBits.int32, Z_MEM_LEVEL.int32,
- Z_DEFAULT_STRATEGY.int32)
- case status
- of Z_OK: discard
- of Z_MEM_ERROR: raise newException(OutOfMemError, "")
- of Z_STREAM_ERROR: raise newException(ZlibStreamError, "invalid zlib stream parameter!")
- of Z_VERSION_ERROR: raise newException(ZlibStreamError, "zlib version mismatch!")
- else: raise newException(ZlibStreamError, "Unkown error(" & $status & ") : " & $z.msg)
- let space = deflateBound(z, sourceLen)
- var compressed = newStringOfCap(space)
- z.next_in = sourceBuf
- z.avail_in = sourceLen.Uint
- z.next_out = addr(compressed[0])
- z.avail_out = space.Uint
- status = deflate(z, Z_FINISH)
- if status != Z_STREAM_END:
- discard deflateEnd(z) # cleanup allocated ressources
- raise newException(ZlibStreamError, "Invalid stream state(" & $status & ") : " & $z.msg)
- status = deflateEnd(z)
- if status != Z_OK: # cleanup allocated ressources
- raise newException(ZlibStreamError, "Invalid stream state(" & $status & ") : " & $z.msg)
- compressed.setLen(z.total_out)
- swap(result, compressed)
- proc compress*(input: string; level=Z_DEFAULT_COMPRESSION; stream=GZIP_STREAM): string =
- ## Given a string, returns its deflated version with an optional header.
- ##
- ## Valid arguments for ``stream`` are
- ## - ``ZLIB_STREAM`` - add a zlib header and footer.
- ## - ``GZIP_STREAM`` - add a basic gzip header and footer.
- ## - ``RAW_DEFLATE`` - no header is generated.
- ##
- ## Compression level can be set with ``level`` argument. Currently
- ## ``Z_DEFAULT_COMPRESSION`` is 6.
- ##
- ## Returns "" on failure.
- result = compress(input, input.len, level, stream)
- proc uncompress*(sourceBuf: cstring, sourceLen: Natural; stream=DETECT_STREAM): string =
- ## Given a deflated buffer returns its inflated content as a string.
- ##
- ## Valid arguments for ``stream`` are
- ## - ``DETECT_STREAM`` - detect if zlib or gzip header is present
- ## and decompress stream. Fail on raw deflate stream.
- ## - ``ZLIB_STREAM`` - decompress a zlib stream.
- ## - ``GZIP_STREAM`` - decompress a gzip stream.
- ## - ``RAW_DEFLATE`` - decompress a raw deflate stream.
- ##
- ## Passing a nil cstring will crash this proc in release mode and assert in
- ## debug mode.
- ##
- ## Returns "" 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://zlib.net/zpipe.c.
- assert(not sourceBuf.isNil)
- assert(sourceLen >= 0)
- var z: ZStream
- var decompressed: string = ""
- var sbytes = 0
- var wbytes = 0
- ## allocate inflate state
- z.availIn = 0
- var wbits = case (stream)
- of RAW_DEFLATE: -MAX_WBITS
- of ZLIB_STREAM: MAX_WBITS
- of GZIP_STREAM: MAX_WBITS + 16
- of DETECT_STREAM: MAX_WBITS + 32
- var status = inflateInit2(z, wbits.int32)
- case status
- of Z_OK: discard
- of Z_MEM_ERROR: raise newException(OutOfMemError, "")
- of Z_STREAM_ERROR: raise newException(ZlibStreamError, "invalid zlib stream parameter!")
- of Z_VERSION_ERROR: raise newException(ZlibStreamError, "zlib version mismatch!")
- else: raise newException(ZlibStreamError, "Unkown error(" & $status & ") : " & $z.msg)
- # run loop until all input is consumed.
- # handle concatenated deflated stream with header.
- while true:
- z.availIn = (sourceLen - sbytes).int32
- # no more input available
- if (sourceLen - sbytes) <= 0: break
- z.nextIn = sourceBuf[sbytes].unsafeaddr
- # run inflate() on available input until output buffer is full
- while true:
- # if written bytes >= output size : resize output
- if wbytes >= decompressed.len:
- let cur_outlen = decompressed.len
- let new_outlen = if decompressed.len == 0: sourceLen*2 else: decompressed.len*2
- if new_outlen < cur_outlen: # unsigned integer overflow, buffer too large
- discard inflateEnd(z);
- raise newException(OverflowError, "zlib stream decompressed size is too large! (size > " & $int.high & ")")
- decompressed.setLen(new_outlen)
- # available space for decompression
- let space = decompressed.len - wbytes
- z.availOut = space.Uint
- z.nextOut = decompressed[wbytes].addr
- status = inflate(z, Z_NO_FLUSH)
- if status.int8 notin {Z_OK.int8, Z_STREAM_END.int8, Z_BUF_ERROR.int8}:
- discard inflateEnd(z)
- case status
- of Z_MEM_ERROR: raise newException(OutOfMemError, "")
- of Z_DATA_ERROR: raise newException(ZlibStreamError, "invalid zlib stream parameter!")
- else: raise newException(ZlibStreamError, "Unkown error(" & $status & ") : " & $z.msg)
- # add written bytes, if any.
- wbytes += space - z.availOut.int
- # may need more input
- if not (z.availOut == 0): break
- # inflate() says stream is done
- if (status == Z_STREAM_END):
- # may have another stream concatenated
- if z.availIn != 0:
- sbytes = sourceLen - z.availIn # add consumed bytes
- if inflateReset(z) != Z_OK: # reset zlib struct and try again
- raise newException(ZlibStreamError, "Invalid stream state(" & $status & ") : " & $z.msg)
- else:
- break # end of decompression
- # clean up and don't care about any error
- discard inflateEnd(z)
- if status != Z_STREAM_END:
- raise newException(ZlibStreamError, "Invalid stream state(" & $status & ") : " & $z.msg)
- decompressed.setLen(wbytes)
- swap(result, decompressed)
- proc uncompress*(sourceBuf: string; stream=DETECT_STREAM): string =
- ## Given a GZIP-ed string return its inflated content.
- ##
- ## Valid arguments for ``stream`` are
- ## - ``DETECT_STREAM`` - detect if zlib or gzip header is present
- ## and decompress stream. Fail on raw deflate stream.
- ## - ``ZLIB_STREAM`` - decompress a zlib stream.
- ## - ``GZIP_STREAM`` - decompress a gzip stream.
- ## - ``RAW_DEFLATE`` - decompress a raw deflate stream.
- ##
- ## Returns "" on failure.
- result = uncompress(sourceBuf, sourceBuf.len, stream)
- proc deflate*(buffer: var string; level=Z_DEFAULT_COMPRESSION; stream=GZIP_STREAM): bool {.discardable.} =
- ## Convenience proc which deflates a string and insert an optional header/footer.
- ##
- ## Valid arguments for ``stream`` are
- ## - ``ZLIB_STREAM`` - add a zlib header and footer.
- ## - ``GZIP_STREAM`` - add a basic gzip header and footer.
- ## - ``RAW_DEFLATE`` - no header is generated.
- ##
- ## Compression level can be set with ``level`` argument. Currently
- ## ``Z_DEFAULT_COMPRESSION`` is 6.
- ##
- ## Returns true if `buffer` was successfully deflated otherwise the buffer is untouched.
- var temp = compress(addr(buffer[0]), buffer.len, level, stream)
- if temp.len != 0:
- swap(buffer, temp)
- result = true
- proc inflate*(buffer: var string; stream=DETECT_STREAM): bool {.discardable.} =
- ## Convenience proc which inflates a string containing compressed data
- ## with an optional header.
- ##
- ## Valid argument for ``stream`` are:
- ## - ``DETECT_STREAM`` - detect if zlib or gzip header is present
- ## and decompress stream. Fail on raw deflate stream.
- ## - ``ZLIB_STREAM`` - decompress a zlib stream.
- ## - ``GZIP_STREAM`` - decompress a gzip stream.
- ## - ``RAW_DEFLATE`` - decompress a raw deflate stream.
- ##
- ## 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.
-
- var temp = uncompress(addr(buffer[0]), buffer.len, stream)
- if temp.len != 0:
- swap(buffer, temp)
- result = true
|