annotate semiconginev2/thirdparty/zippy/zippy.nim @ 1242:e8b3dc80e48e

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