1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798 |
- 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
|