97 lines
3.5 KiB
Nim
97 lines
3.5 KiB
Nim
import os
|
|
import zlib
|
|
import streams
|
|
export streams
|
|
|
|
## This module implements a gzipfile stream for reading, writing, appending.
|
|
|
|
type
|
|
GzFileStream* = ref object of Stream
|
|
mode: FileMode
|
|
f: GzFile
|
|
|
|
const SEEK_SET = 0.int32 # Seek from beginning of file.
|
|
|
|
proc fsClose(s: Stream) =
|
|
if not GzFileStream(s).f.isNil:
|
|
discard gzclose(GzFileStream(s).f)
|
|
GzFileStream(s).f = nil
|
|
|
|
proc fsFlush(s: Stream) =
|
|
# compiler flushFile also discard c_fflush
|
|
discard gzflush(GzFileStream(s).f, Z_FINISH)
|
|
|
|
proc fsAtEnd(s: Stream): bool =
|
|
result = gzeof(GzFileStream(s).f) == 1
|
|
|
|
proc fsSetPosition(s: Stream, pos: int) =
|
|
if gzseek(GzFileStream(s).f, pos.ZOffT, SEEK_SET) == -1:
|
|
if GzFileStream(s).mode in {fmWrite, fmAppend}:
|
|
raise newException(IOError, "error in gzip stream while seeking! (file is in write/append mode!")
|
|
else:
|
|
raise newException(IOError, "error in gzip stream while seeking!")
|
|
|
|
proc fsGetPosition(s: Stream): int =
|
|
result = gztell(GzFileStream(s).f).int
|
|
|
|
proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int =
|
|
result = gzread(GzFileStream(s).f, buffer, bufLen).int
|
|
if result == -1:
|
|
if GzFileStream(s).mode in {fmWrite, fmAppend}:
|
|
raise newException(IOError, "cannot read data from write-only gzip stream!")
|
|
else:
|
|
raise newException(IOError, "cannot read from stream!")
|
|
|
|
proc fsPeekData(s: Stream, buffer: pointer, bufLen: int): int =
|
|
let gz = GzFileStream(s)
|
|
if gz.mode in {fmWrite, fmAppend}:
|
|
raise newException(IOError, "cannot peek data from write-only gzip stream!")
|
|
let pos = int(gztell(gz.f))
|
|
result = fsReadData(s, buffer, bufLen)
|
|
fsSetPosition(s, pos)
|
|
|
|
proc fsWriteData(s: Stream, buffer: pointer, bufLen: int) =
|
|
if gzwrite(GzFileStream(s).f, buffer, bufLen).int != bufLen:
|
|
if GzFileStream(s).mode in {fmWrite, fmAppend}:
|
|
raise newException(IOError, "cannot write data to gzip stream!")
|
|
else:
|
|
raise newException(IOError, "cannot write data to read-only gzip stream!")
|
|
|
|
|
|
proc newGzFileStream*(filename: string; mode=fmRead; level=Z_DEFAULT_COMPRESSION): GzFileStream =
|
|
## Opens a Gzipfile as a file stream. `mode` can be
|
|
## ``fmRead``, ``fmWrite`` or ``fmAppend``.
|
|
##
|
|
## Compression level can be set with ``level`` argument. Currently
|
|
## ``Z_DEFAULT_COMPRESSION`` is 6.
|
|
##
|
|
## Note: ``level`` is ignored if ``mode`` is `fmRead`
|
|
##
|
|
## Note: There is only partial support for file seeking
|
|
## - in fmRead mode, seeking randomly inside the gzip
|
|
## file will lead to poor performance.
|
|
## - in fmWrite, fmAppend mode, only forward seeking
|
|
## is supported.
|
|
new(result)
|
|
case mode
|
|
of fmRead: result.f = gzopen(filename, "rb")
|
|
of fmWrite: result.f = gzopen(filename, "wb")
|
|
of fmAppend: result.f = gzopen(filename, "ab")
|
|
else: raise newException(IOError, "unsupported file mode '" & $mode &
|
|
"' for GzFileStream!")
|
|
if result.f.isNil:
|
|
let err = osLastError()
|
|
if err != OSErrorCode(0'i32):
|
|
raiseOSError(err)
|
|
if mode in {fmWrite, fmAppend}:
|
|
discard gzsetparams(result.f, level.int32, Z_DEFAULT_STRATEGY.int32)
|
|
|
|
result.mode = mode
|
|
result.closeImpl = fsClose
|
|
result.atEndImpl = fsAtEnd
|
|
result.setPositionImpl = fsSetPosition
|
|
result.getPositionImpl = fsGetPosition
|
|
result.readDataImpl = fsReadData
|
|
result.peekDataImpl = fsPeekData
|
|
result.writeDataImpl = fsWriteData
|
|
result.flushImpl = fsFlush
|