1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
|
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
|