-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathzippy.nim
177 lines (142 loc) · 4.75 KB
/
zippy.nim
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
import zippy/adler32, zippy/common, zippy/crc, zippy/deflate,
zippy/gzip, zippy/inflate, zippy/internal
when (NimMajor, NimMinor, NimPatch) >= (1, 6, 0):
import std/sysrand
else:
import std/random, std/times
export common
proc compress*(
src: pointer,
len: int,
level = DefaultCompression,
dataFormat = dfGzip
): string {.raises: [ZippyError].} =
## Compresses src and returns the compressed data.
let src = cast[ptr UncheckedArray[uint8]](src)
case dataFormat:
of dfGzip:
result.setLen(10)
result[0] = 31.char
result[1] = 139.char
result[2] = 8.char
result[3] = (1.uint8 shl 3).char # Set the fname flag
block: # https://github.com/guzba/zippy/issues/61
let htbLen =
when (NimMajor, NimMinor, NimPatch) >= (1, 6, 0):
var urand: array[1, uint8]
if not urandom(urand):
raise newException(ZippyError, "Failed to generate random number")
(urand[0] mod 26).int
else:
let now = getTime()
var rand = initRand(now.toUnix * 1_000_000_000 + now.nanosecond)
(rand.next() mod 26).int # mod the uint first to ensure a positive int
# Add up to 26 characters as the gzip header file name
for i in 0 ..< htbLen:
result.add (97 + i).char
result.add '\0'
deflate(result, src, len, level)
let
checksum = crc32(src, len)
isize = len
result.add(((checksum shr 0) and 255).char)
result.add(((checksum shr 8) and 255).char)
result.add(((checksum shr 16) and 255).char)
result.add(((checksum shr 24) and 255).char)
result.add(((isize shr 0) and 255).char)
result.add(((isize shr 8) and 255).char)
result.add(((isize shr 16) and 255).char)
result.add(((isize shr 24) and 255).char)
of dfZlib:
const
cm = 8.uint8
cinfo = 7.uint8
cmf = (cinfo shl 4) or cm
fcheck = (31.uint32 - (cmf.uint32 * 256) mod 31).uint8
result.setLen(2)
result[0] = cmf.char
result[1] = fcheck.char
deflate(result, src, len, level)
let checksum = adler32(src, len)
result.add(((checksum shr 24) and 255).char)
result.add(((checksum shr 16) and 255).char)
result.add(((checksum shr 8) and 255).char)
result.add(((checksum shr 0) and 255).char)
of dfDeflate:
deflate(result, src, len, level)
else:
raise newException(ZippyError, "Invalid data format " & $dfDetect)
proc compress*(
src: string,
level = DefaultCompression,
dataFormat = dfGzip
): string {.raises: [ZippyError].} =
compress(src.cstring, src.len, level, dataFormat)
proc compress*(
src: seq[uint8],
level = DefaultCompression,
dataFormat = dfGzip
): seq[uint8] {.raises: [ZippyError].} =
cast[seq[uint8]](compress(cast[string](src).cstring, src.len, level, dataFormat))
proc uncompress*(
src: pointer,
len: int,
dataFormat = dfDetect
): string {.raises: [ZippyError].} =
## Uncompresses src and returns the uncompressed data.
let src = cast[ptr UncheckedArray[uint8]](src)
case dataFormat:
of dfDetect:
if (
len > 18 and
src[0].uint8 == 31 and src[1].uint8 == 139 and src[2].uint8 == 8 and
(src[3].uint8 and 0b11100000) == 0
):
return uncompress(src, len, dfGzip)
if (
len > 6 and
(src[0].uint8 and 0b00001111) == 8 and
(src[0].uint8 shr 4) <= 7 and
((src[0].uint16 * 256) + src[1].uint8) mod 31 == 0
):
return uncompress(src, len, dfZlib)
raise newException(ZippyError, "Unable to detect compressed data format")
of dfGzip:
uncompressGzip(result, src, len)
of dfZlib:
if len < 6:
failUncompress()
let
cmf = src[0].uint8
flg = src[1].uint8
cm = cmf and 0b00001111
cinfo = cmf shr 4
if cm != 8: # DEFLATE
raise newException(ZippyError, "Unsupported compression method")
if cinfo > 7.uint8:
raise newException(ZippyError, "Invalid compression info")
if ((cmf.uint16 * 256) + flg.uint16) mod 31 != 0:
raise newException(ZippyError, "Invalid header")
if (flg and 0b00100000) != 0: # FDICT
raise newException(ZippyError, "Preset dictionary is not yet supported")
inflate(result, src, len, 2)
let checksum = (
src[len - 4].uint32 shl 24 or
src[len - 3].uint32 shl 16 or
src[len - 2].uint32 shl 8 or
src[len - 1].uint32
)
if checksum != adler32(result):
raise newException(ZippyError, "Checksum verification failed")
of dfDeflate:
inflate(result, src, len, 0)
proc uncompress*(
src: string,
dataFormat = dfDetect
): string {.raises: [ZippyError].} =
uncompress(src.cstring, src.len, dataFormat)
proc uncompress*(
src: seq[uint8],
dataFormat = dfDetect
): seq[uint8] {.raises: [ZippyError].} =
cast[seq[uint8]](uncompress(cast[string](src).cstring, src.len, dataFormat))