1123
|
1 import zippy/adler32, zippy/common, zippy/crc, zippy/deflate,
|
|
2 zippy/gzip, zippy/inflate, zippy/internal
|
|
3
|
|
4 when (NimMajor, NimMinor, NimPatch) >= (1, 6, 0):
|
|
5 import std/sysrand
|
|
6 else:
|
|
7 import std/random, std/times
|
|
8
|
|
9 export common
|
|
10
|
|
11 proc compress*(
|
|
12 src: pointer,
|
|
13 len: int,
|
|
14 level = DefaultCompression,
|
|
15 dataFormat = dfGzip
|
|
16 ): string {.raises: [ZippyError].} =
|
|
17 ## Compresses src and returns the compressed data.
|
|
18 let src = cast[ptr UncheckedArray[uint8]](src)
|
|
19
|
|
20 case dataFormat:
|
|
21 of dfGzip:
|
|
22 result.setLen(10)
|
|
23 result[0] = 31.char
|
|
24 result[1] = 139.char
|
|
25 result[2] = 8.char
|
|
26 result[3] = (1.uint8 shl 3).char # Set the fname flag
|
|
27
|
|
28 block: # https://github.com/guzba/zippy/issues/61
|
|
29 let htbLen =
|
|
30 when (NimMajor, NimMinor, NimPatch) >= (1, 6, 0):
|
|
31 var urand: array[1, uint8]
|
|
32 if not urandom(urand):
|
|
33 raise newException(ZippyError, "Failed to generate random number")
|
|
34 (urand[0] mod 26).int
|
|
35 else:
|
|
36 let now = getTime()
|
|
37 var rand = initRand(now.toUnix * 1_000_000_000 + now.nanosecond)
|
|
38 (rand.next() mod 26).int # mod the uint first to ensure a positive int
|
|
39 # Add up to 26 characters as the gzip header file name
|
|
40 for i in 0 ..< htbLen:
|
|
41 result.add (97 + i).char
|
|
42 result.add '\0'
|
|
43
|
|
44 deflate(result, src, len, level)
|
|
45
|
|
46 let
|
|
47 checksum = crc32(src, len)
|
|
48 isize = len
|
|
49
|
|
50 result.add(((checksum shr 0) and 255).char)
|
|
51 result.add(((checksum shr 8) and 255).char)
|
|
52 result.add(((checksum shr 16) and 255).char)
|
|
53 result.add(((checksum shr 24) and 255).char)
|
|
54
|
|
55 result.add(((isize shr 0) and 255).char)
|
|
56 result.add(((isize shr 8) and 255).char)
|
|
57 result.add(((isize shr 16) and 255).char)
|
|
58 result.add(((isize shr 24) and 255).char)
|
|
59
|
|
60 of dfZlib:
|
|
61 const
|
|
62 cm = 8.uint8
|
|
63 cinfo = 7.uint8
|
|
64 cmf = (cinfo shl 4) or cm
|
|
65 fcheck = (31.uint32 - (cmf.uint32 * 256) mod 31).uint8
|
|
66
|
|
67 result.setLen(2)
|
|
68 result[0] = cmf.char
|
|
69 result[1] = fcheck.char
|
|
70
|
|
71 deflate(result, src, len, level)
|
|
72
|
|
73 let checksum = adler32(src, len)
|
|
74
|
|
75 result.add(((checksum shr 24) and 255).char)
|
|
76 result.add(((checksum shr 16) and 255).char)
|
|
77 result.add(((checksum shr 8) and 255).char)
|
|
78 result.add(((checksum shr 0) and 255).char)
|
|
79
|
|
80 of dfDeflate:
|
|
81 deflate(result, src, len, level)
|
|
82
|
|
83 else:
|
|
84 raise newException(ZippyError, "Invalid data format " & $dfDetect)
|
|
85
|
|
86 proc compress*(
|
|
87 src: string,
|
|
88 level = DefaultCompression,
|
|
89 dataFormat = dfGzip
|
|
90 ): string {.raises: [ZippyError].} =
|
|
91 compress(src.cstring, src.len, level, dataFormat)
|
|
92
|
|
93 proc compress*(
|
|
94 src: seq[uint8],
|
|
95 level = DefaultCompression,
|
|
96 dataFormat = dfGzip
|
|
97 ): seq[uint8] {.raises: [ZippyError].} =
|
|
98 cast[seq[uint8]](compress(cast[string](src).cstring, src.len, level, dataFormat))
|
|
99
|
|
100 proc uncompress*(
|
|
101 src: pointer,
|
|
102 len: int,
|
|
103 dataFormat = dfDetect
|
|
104 ): string {.raises: [ZippyError].} =
|
|
105 ## Uncompresses src and returns the uncompressed data.
|
|
106 let src = cast[ptr UncheckedArray[uint8]](src)
|
|
107
|
|
108 case dataFormat:
|
|
109 of dfDetect:
|
|
110 if (
|
|
111 len > 18 and
|
|
112 src[0].uint8 == 31 and src[1].uint8 == 139 and src[2].uint8 == 8 and
|
|
113 (src[3].uint8 and 0b11100000) == 0
|
|
114 ):
|
|
115 return uncompress(src, len, dfGzip)
|
|
116
|
|
117 if (
|
|
118 len > 6 and
|
|
119 (src[0].uint8 and 0b00001111) == 8 and
|
|
120 (src[0].uint8 shr 4) <= 7 and
|
|
121 ((src[0].uint16 * 256) + src[1].uint8) mod 31 == 0
|
|
122 ):
|
|
123 return uncompress(src, len, dfZlib)
|
|
124
|
|
125 raise newException(ZippyError, "Unable to detect compressed data format")
|
|
126
|
|
127 of dfGzip:
|
|
128 uncompressGzip(result, src, len)
|
|
129
|
|
130 of dfZlib:
|
|
131 if len < 6:
|
|
132 failUncompress()
|
|
133
|
|
134 let
|
|
135 cmf = src[0].uint8
|
|
136 flg = src[1].uint8
|
|
137 cm = cmf and 0b00001111
|
|
138 cinfo = cmf shr 4
|
|
139
|
|
140 if cm != 8: # DEFLATE
|
|
141 raise newException(ZippyError, "Unsupported compression method")
|
|
142
|
|
143 if cinfo > 7.uint8:
|
|
144 raise newException(ZippyError, "Invalid compression info")
|
|
145
|
|
146 if ((cmf.uint16 * 256) + flg.uint16) mod 31 != 0:
|
|
147 raise newException(ZippyError, "Invalid header")
|
|
148
|
|
149 if (flg and 0b00100000) != 0: # FDICT
|
|
150 raise newException(ZippyError, "Preset dictionary is not yet supported")
|
|
151
|
|
152 inflate(result, src, len, 2)
|
|
153
|
|
154 let checksum = (
|
|
155 src[len - 4].uint32 shl 24 or
|
|
156 src[len - 3].uint32 shl 16 or
|
|
157 src[len - 2].uint32 shl 8 or
|
|
158 src[len - 1].uint32
|
|
159 )
|
|
160
|
|
161 if checksum != adler32(result):
|
|
162 raise newException(ZippyError, "Checksum verification failed")
|
|
163
|
|
164 of dfDeflate:
|
|
165 inflate(result, src, len, 0)
|
|
166
|
|
167 proc uncompress*(
|
|
168 src: string,
|
|
169 dataFormat = dfDetect
|
|
170 ): string {.raises: [ZippyError].} =
|
|
171 uncompress(src.cstring, src.len, dataFormat)
|
|
172
|
|
173 proc uncompress*(
|
|
174 src: seq[uint8],
|
|
175 dataFormat = dfDetect
|
|
176 ): seq[uint8] {.raises: [ZippyError].} =
|
|
177 cast[seq[uint8]](uncompress(cast[string](src).cstring, src.len, dataFormat))
|