gzipfiles.nim 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import os
  2. import zlib
  3. import streams
  4. export streams
  5. ## This module implements a gzipfile stream for reading, writing, appending.
  6. type
  7. GzFileStream* = ref object of Stream
  8. mode: FileMode
  9. f: GzFile
  10. const SEEK_SET = 0.int32 # Seek from beginning of file.
  11. proc fsClose(s: Stream) =
  12. if not GzFileStream(s).f.isNil:
  13. discard gzclose(GzFileStream(s).f)
  14. GzFileStream(s).f = nil
  15. proc fsFlush(s: Stream) =
  16. # compiler flushFile also discard c_fflush
  17. discard gzflush(GzFileStream(s).f, Z_FINISH)
  18. proc fsAtEnd(s: Stream): bool =
  19. result = gzeof(GzFileStream(s).f) == 1
  20. proc fsSetPosition(s: Stream, pos: int) =
  21. if gzseek(GzFileStream(s).f, pos.ZOffT, SEEK_SET) == -1:
  22. if GzFileStream(s).mode in {fmWrite, fmAppend}:
  23. raise newException(IOError, "error in gzip stream while seeking! (file is in write/append mode!")
  24. else:
  25. raise newException(IOError, "error in gzip stream while seeking!")
  26. proc fsGetPosition(s: Stream): int =
  27. result = gztell(GzFileStream(s).f).int
  28. proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int =
  29. result = gzread(GzFileStream(s).f, buffer, bufLen).int
  30. if result == -1:
  31. if GzFileStream(s).mode in {fmWrite, fmAppend}:
  32. raise newException(IOError, "cannot read data from write-only gzip stream!")
  33. else:
  34. raise newException(IOError, "cannot read from stream!")
  35. proc fsPeekData(s: Stream, buffer: pointer, bufLen: int): int =
  36. let gz = GzFileStream(s)
  37. if gz.mode in {fmWrite, fmAppend}:
  38. raise newException(IOError, "cannot peek data from write-only gzip stream!")
  39. let pos = int(gztell(gz.f))
  40. result = fsReadData(s, buffer, bufLen)
  41. fsSetPosition(s, pos)
  42. proc fsWriteData(s: Stream, buffer: pointer, bufLen: int) =
  43. if gzwrite(GzFileStream(s).f, buffer, bufLen).int != bufLen:
  44. if GzFileStream(s).mode in {fmWrite, fmAppend}:
  45. raise newException(IOError, "cannot write data to gzip stream!")
  46. else:
  47. raise newException(IOError, "cannot write data to read-only gzip stream!")
  48. proc newGzFileStream*(filename: string; mode=fmRead; level=Z_DEFAULT_COMPRESSION): GzFileStream =
  49. ## Opens a Gzipfile as a file stream. `mode` can be
  50. ## ``fmRead``, ``fmWrite`` or ``fmAppend``.
  51. ##
  52. ## Compression level can be set with ``level`` argument. Currently
  53. ## ``Z_DEFAULT_COMPRESSION`` is 6.
  54. ##
  55. ## Note: ``level`` is ignored if ``mode`` is `fmRead`
  56. ##
  57. ## Note: There is only partial support for file seeking
  58. ## - in fmRead mode, seeking randomly inside the gzip
  59. ## file will lead to poor performance.
  60. ## - in fmWrite, fmAppend mode, only forward seeking
  61. ## is supported.
  62. new(result)
  63. case mode
  64. of fmRead: result.f = gzopen(filename, "rb")
  65. of fmWrite: result.f = gzopen(filename, "wb")
  66. of fmAppend: result.f = gzopen(filename, "ab")
  67. else: raise newException(IOError, "unsupported file mode '" & $mode &
  68. "' for GzFileStream!")
  69. if result.f.isNil:
  70. let err = osLastError()
  71. if err != OSErrorCode(0'i32):
  72. raiseOSError(err)
  73. if mode in {fmWrite, fmAppend}:
  74. discard gzsetparams(result.f, level.int32, Z_DEFAULT_STRATEGY.int32)
  75. result.mode = mode
  76. result.closeImpl = fsClose
  77. result.atEndImpl = fsAtEnd
  78. result.setPositionImpl = fsSetPosition
  79. result.getPositionImpl = fsGetPosition
  80. result.readDataImpl = fsReadData
  81. result.peekDataImpl = fsPeekData
  82. result.writeDataImpl = fsWriteData
  83. result.flushImpl = fsFlush