Don't unconditionally define _GNU_SOURCE
[jimtcl.git] / jim-zlib.c
blobea8b6dc19779271051f70d9526632c2abc85d5f6
1 /*
2 * Jim - zlib bindings
4 * Copyright 2015, 2016 Dima Krasner <dima@dimakrasner.com>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials
15 * provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
28 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 * The views and conclusions contained in the software and documentation
31 * are those of the authors and should not be interpreted as representing
32 * official policies, either expressed or implied, of the Jim Tcl Project.
35 #include <zlib.h>
37 #include <jim.h>
38 #include <jim-subcmd.h>
40 #define _PASTE(x) # x
41 #define PASTE(x) _PASTE(x)
43 #define WBITS_GZIP (MAX_WBITS | 16)
44 /* use small 64K chunks if no size was specified during decompression, to reduce memory consumption */
45 #define DEF_DECOMPRESS_BUFSIZ (64 * 1024)
47 static int Jim_Crc32(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
49 long init;
50 const char *in;
51 int len;
53 if (argc == 1) {
54 init = crc32(0L, Z_NULL, 0);
55 } else {
56 if (Jim_GetLong(interp, argv[1], &init) != JIM_OK) {
57 return JIM_ERR;
61 in = Jim_GetString(argv[0], &len);
62 Jim_SetResultInt(interp, crc32((uLong)init, (const Bytef *)in, (uInt)len) & 0xFFFFFFFF);
64 return JIM_OK;
67 static int Jim_Compress(Jim_Interp *interp, const char *in, int len, long level, int wbits)
69 z_stream strm = {0};
70 Bytef *buf;
72 if ((level != Z_DEFAULT_COMPRESSION) && ((level < Z_NO_COMPRESSION) || (level > Z_BEST_COMPRESSION))) {
73 Jim_SetResultString(interp, "level must be 0 to 9", -1);
74 return JIM_ERR;
77 if (deflateInit2(&strm, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK) {
78 return JIM_ERR;
81 strm.avail_out = deflateBound(&strm, (uLong)len);
82 if (strm.avail_out > INT_MAX) {
83 deflateEnd(&strm);
84 return JIM_ERR;
86 buf = (Bytef *)Jim_Alloc((int)strm.avail_out);
87 strm.next_out = buf;
88 strm.next_in = (Bytef *)in;
89 strm.avail_in = (uInt)len;
91 /* always compress in one pass - the return value holds the entire
92 * decompressed data anyway, so there's no reason to do chunked
93 * decompression */
94 if (deflate(&strm, Z_FINISH) != Z_STREAM_END) {
95 Jim_Free(strm.next_out);
96 deflateEnd(&strm);
97 return JIM_ERR;
100 deflateEnd(&strm);
102 if (strm.total_out > INT_MAX) {
103 Jim_Free(strm.next_out);
104 return JIM_ERR;
107 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, (char *)buf, (int)strm.total_out));
108 return JIM_OK;
111 static int Jim_Deflate(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
113 long level = Z_DEFAULT_COMPRESSION;
114 const char *in;
115 int len;
117 if (argc != 1) {
118 if (Jim_GetLong(interp, argv[1], &level) != JIM_OK) {
119 return JIM_ERR;
123 in = Jim_GetString(argv[0], &len);
124 return Jim_Compress(interp, in, len, level, -MAX_WBITS);
127 static int Jim_Gzip(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
129 long level = Z_DEFAULT_COMPRESSION;
130 const char *in;
131 int len;
133 if (argc == 3) {
134 if (!Jim_CompareStringImmediate(interp, argv[1], "-level")) {
135 Jim_WrongNumArgs(interp, 0, argv, "data ?-level level?");
136 return JIM_ERR;
139 if (Jim_GetLong(interp, argv[2], &level) != JIM_OK) {
140 Jim_WrongNumArgs(interp, 0, argv, "data ?-level level?");
141 return JIM_ERR;
145 else if (argc != 1) {
146 Jim_WrongNumArgs(interp, 0, argv, "data ?-level level?");
147 return JIM_ERR;
150 in = Jim_GetString(argv[0], &len);
151 return Jim_Compress(interp, in, len, level, WBITS_GZIP);
154 static int Jim_Decompress(Jim_Interp *interp, const char *in, int len, long bufsiz, int wbits)
156 z_stream strm = {0};
157 void *buf;
158 Jim_Obj *out;
159 int ret;
161 if ((bufsiz <= 0) || (bufsiz > INT_MAX)) {
162 Jim_SetResultString(interp, "buffer size must be 0 to "PASTE(INT_MAX), -1);
163 return JIM_ERR;
166 if (inflateInit2(&strm, wbits) != Z_OK) {
167 return JIM_ERR;
170 /* allocate a buffer - decompression is done in chunks, into this buffer;
171 * when the decompressed data size is given, decompression is faster because
172 * it's done in one pass, with less memcpy() overhead */
173 buf = Jim_Alloc((int)bufsiz);
175 out = Jim_NewEmptyStringObj(interp);
176 Jim_IncrRefCount(out);
178 strm.next_in = (Bytef*)in;
179 strm.avail_in = (uInt)len;
180 do {
181 do {
182 strm.next_out = buf;
183 strm.avail_out = (uInt)bufsiz;
185 ret = inflate(&strm, Z_NO_FLUSH);
186 switch (ret) {
187 case Z_OK:
188 case Z_STREAM_END:
189 /* append each chunk to the output object */
190 Jim_AppendString(interp, out, buf, (int)(bufsiz - (long)strm.avail_out));
191 break;
193 default:
194 Jim_DecrRefCount(interp, out);
195 Jim_Free(buf);
196 inflateEnd(&strm);
197 if (strm.msg != NULL)
198 Jim_SetResultString(interp, strm.msg, -1);
199 return JIM_ERR;
201 } while (strm.avail_out == 0);
202 } while (ret != Z_STREAM_END);
204 /* free memory used for decompression before we assign the return value */
205 Jim_Free(buf);
206 inflateEnd(&strm);
208 Jim_SetResult(interp, out);
209 Jim_DecrRefCount(interp, out);
211 return JIM_OK;
214 static int Jim_Inflate(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
216 long bufsiz = DEF_DECOMPRESS_BUFSIZ;
217 const char *in;
218 int len;
220 if (argc != 1) {
221 if (Jim_GetLong(interp, argv[1], &bufsiz) != JIM_OK) {
222 return JIM_ERR;
225 if ((bufsiz <= 0) || (bufsiz > INT_MAX)) {
226 Jim_SetResultString(interp, "buffer size must be 0 to "PASTE(INT_MAX), -1);
227 return JIM_ERR;
231 in = Jim_GetString(argv[0], &len);
232 return Jim_Decompress(interp, in, len, bufsiz, -MAX_WBITS);
235 static int Jim_Gunzip(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
237 long bufsiz = DEF_DECOMPRESS_BUFSIZ;
238 const char *in;
239 int len;
241 if (argc == 3) {
242 if (!Jim_CompareStringImmediate(interp, argv[1], "-buffersize")) {
243 Jim_WrongNumArgs(interp, 0, argv, "data ?-buffersize size?");
244 return JIM_ERR;
247 if (Jim_GetLong(interp, argv[2], &bufsiz) != JIM_OK) {
248 Jim_WrongNumArgs(interp, 0, argv, "data ?-buffersize size?");
249 return JIM_ERR;
253 else if (argc != 1) {
254 Jim_WrongNumArgs(interp, 0, argv, "data ?-buffersize size?");
255 return JIM_ERR;
258 in = Jim_GetString(argv[0], &len);
259 return Jim_Decompress(interp, in, len, bufsiz, WBITS_GZIP);
262 static const jim_subcmd_type zlib_command_table[] = {
263 { "crc32",
264 "data ?startValue?",
265 Jim_Crc32,
268 /* Description: Calculates the CRC32 checksum of a string */
270 { "deflate",
271 "string ?level?",
272 Jim_Deflate,
275 /* Description: Compresses a string and outputs a raw, zlib-compressed stream */
277 { "gzip",
278 "data ?-level level?",
279 Jim_Gzip,
282 /* Description: Compresses a string and outputs a gzip-compressed stream */
284 { "inflate",
285 "data ?bufferSize?",
286 Jim_Inflate,
289 /* Description: Decompresses a raw, zlib-compressed stream */
291 { "gunzip",
292 "data ?-buffersize size?",
293 Jim_Gunzip,
296 /* Description: Decompresses a gzip-compressed stream */
298 { NULL }
301 static int JimZlibCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
303 return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, zlib_command_table, argc, argv), argc, argv);
306 int Jim_zlibInit(Jim_Interp *interp)
308 if (Jim_PackageProvide(interp, "zlib", "1.0", JIM_ERRMSG)) {
309 return JIM_ERR;
312 Jim_CreateCommand(interp, "zlib", JimZlibCmd, 0, 0);
314 return JIM_OK;