Skip to content

Instantly share code, notes, and snippets.

@aboisvert
Created October 13, 2018 13:24
Show Gist options
  • Save aboisvert/c08e63727d0a3c5de53afa04498e9a90 to your computer and use it in GitHub Desktop.
Save aboisvert/c08e63727d0a3c5de53afa04498e9a90 to your computer and use it in GitHub Desktop.
import zip/zlib
import streams
const BufferSize = 16384*2
type
GZipInputStream* = ref GZipInputStreamObj
GZipInputStreamObj* = object of Stream
source: Stream
zstream: ZStream
buffer: array[BufferSize, char]
proc fsClose(s: Stream) =
let s = GZipInputStream(s)
s.close()
discard inflateEnd(s.zstream)
proc fsFlush(s: Stream) =
raise newException(ZlibStreamError, "Flush operation not supported")
proc fsAtEnd(s: Stream): bool =
GZipInputStream(s).atEnd()
proc fsSetPosition(s: Stream, pos: int) =
raise newException(ZlibStreamError, "Set position operation not supported")
proc fsGetPosition(s: Stream): int =
raise newException(ZlibStreamError, "Get position operation not supported")
proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int =
let s = GZipInputStream(s)
s.zstream.avail_out = bufLen.Uint
s.zstream.next_out = cast[ptr char](buffer)
while true:
if s.zstream.avail_in == 0:
let bytesRead = s.source.readData(s.buffer.addr, BufferSize).Uint
if bytesRead < 0:
raise newException(ZlibStreamError, "Error reading input stream")
if bytesRead == 0: # EoF
return 0
s.zstream.avail_in = bytesRead
s.zstream.next_in = s.buffer.addr
let status = inflate(s.zstream, Z_NO_FLUSH)
assert(status != Z_STREAM_ERROR) # state not clobbered
case (status):
of Z_NEED_DICT, Z_DATA_ERROR, Z_MEM_ERROR:
raise newException(ZlibStreamError, "Unkown error(" & $status & ") : " & $s.zstream.msg)
else: discard
let bytesRead = bufLen - s.zstream.avail_out
if bytesRead >= bufLen or s.zstream.avail_out != 0:
return bytesRead
proc fsPeekData(s: Stream, buffer: pointer, bufLen: int): int =
raise newException(ZlibStreamError, "Peek operation not supported")
proc fsWriteData(s: Stream, buffer: pointer, bufLen: int) =
raise newException(ZlibStreamError, "Write operation not supported")
proc newGZipInputStream*(source: Stream, stream=DETECT_STREAM): GZipInputStream =
new(result)
result.source = source
result.closeImpl = fsClose
result.atEndImpl = fsAtEnd
result.setPositionImpl = fsSetPosition
result.getPositionImpl = fsGetPosition
result.readDataImpl = fsReadData
result.peekDataImpl = fsPeekData
result.writeDataImpl = fsWriteData
result.flushImpl = fsFlush
var windowbits = case (stream)
of RAW_DEFLATE: -MAX_WBITS
of ZLIB_STREAM: MAX_WBITS
of GZIP_STREAM: MAX_WBITS + 16
of DETECT_STREAM: MAX_WBITS + 32
# allocate inflate state
result.zstream.availIn = 0;
let status = inflateInit2(result.zstream, windowbits.int32)
if (status != Z_OK):
discard inflateEnd(result.zstream)
raise newException(ZlibStreamError, "Unkown error(" & $status & ") : " & $result.zstream.msg)
result.zstream.next_in = result.buffer.addr
# When compiled with -d:testing, can be used as a gzcat substitute,
# printing the contents of a .gz file to stdout.
when isMainModule and defined(testing):
import os
if paramCount() < 1:
stderr.writeLine("Missing filename argument")
quit(1)
let filename = paramStr(1)
let f = newFileStream(filename, fmRead)
if f == nil:
stderr.writeLine("Unable to open: " & filename)
quit(1)
let gzip = newGZipInputStream(f)
for s in gzip.lines:
echo s
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment