Files
Nim/lib/wrappers/zip/zlib.nim
2015-02-17 02:22:30 +01:00

311 lines
11 KiB
Nim

# 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