Created
October 13, 2018 13:24
-
-
Save aboisvert/c08e63727d0a3c5de53afa04498e9a90 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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