zlib: Don't use PASTE for INTMAX error messages
[jimtcl.git] / jim-zlib.c
blob4bf88a4d61332940d92c17c0fcfb0fa3cfc4dd0e
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 WBITS_GZIP (MAX_WBITS | 16)
41 /* use small 64K chunks if no size was specified during decompression, to reduce memory consumption */
42 #define DEF_DECOMPRESS_BUFSIZ (64 * 1024)
44 static int JimZlibCheckBufSize(Jim_Interp *interp, jim_wide bufsiz)
46 if ((bufsiz <= 0) || (bufsiz > INT_MAX)) {
47 Jim_SetResultString(interp, "buffer size must be 0 to ", -1);
48 Jim_AppendObj(interp, Jim_GetResult(interp), Jim_NewIntObj(interp, INT_MAX));
49 return JIM_ERR;
51 return JIM_OK;
54 static int Jim_Crc32(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
56 long init;
57 const char *in;
58 int len;
60 if (argc == 1) {
61 init = crc32(0L, Z_NULL, 0);
62 } else {
63 if (Jim_GetLong(interp, argv[1], &init) != JIM_OK) {
64 return JIM_ERR;
68 in = Jim_GetString(argv[0], &len);
69 Jim_SetResultInt(interp, crc32((uLong)init, (const Bytef *)in, (uInt)len) & 0xFFFFFFFF);
71 return JIM_OK;
74 static int Jim_Compress(Jim_Interp *interp, const char *in, int len, long level, int wbits)
76 z_stream strm = {0};
77 Bytef *buf;
79 if ((level != Z_DEFAULT_COMPRESSION) && ((level < Z_NO_COMPRESSION) || (level > Z_BEST_COMPRESSION))) {
80 Jim_SetResultString(interp, "level must be 0 to 9", -1);
81 return JIM_ERR;
84 if (deflateInit2(&strm, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK) {
85 return JIM_ERR;
88 strm.avail_out = deflateBound(&strm, (uLong)len);
89 if (strm.avail_out > INT_MAX) {
90 deflateEnd(&strm);
91 return JIM_ERR;
93 buf = (Bytef *)Jim_Alloc((int)strm.avail_out);
94 strm.next_out = buf;
95 strm.next_in = (Bytef *)in;
96 strm.avail_in = (uInt)len;
98 /* always compress in one pass - the return value holds the entire
99 * decompressed data anyway, so there's no reason to do chunked
100 * decompression */
101 if (deflate(&strm, Z_FINISH) != Z_STREAM_END) {
102 Jim_Free(strm.next_out);
103 deflateEnd(&strm);
104 return JIM_ERR;
107 deflateEnd(&strm);
109 if (strm.total_out > INT_MAX) {
110 Jim_Free(strm.next_out);
111 return JIM_ERR;
114 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, (char *)buf, (int)strm.total_out));
115 return JIM_OK;
118 static int Jim_Deflate(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
120 long level = Z_DEFAULT_COMPRESSION;
121 const char *in;
122 int len;
124 if (argc != 1) {
125 if (Jim_GetLong(interp, argv[1], &level) != JIM_OK) {
126 return JIM_ERR;
130 in = Jim_GetString(argv[0], &len);
131 return Jim_Compress(interp, in, len, level, -MAX_WBITS);
134 static int Jim_Gzip(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
136 long level = Z_DEFAULT_COMPRESSION;
137 const char *in;
138 int len;
140 if (argc == 3) {
141 if (!Jim_CompareStringImmediate(interp, argv[1], "-level")) {
142 return -1;
145 if (Jim_GetLong(interp, argv[2], &level) != JIM_OK) {
146 return -1;
150 else if (argc != 1) {
151 return -1;
154 in = Jim_GetString(argv[0], &len);
155 return Jim_Compress(interp, in, len, level, WBITS_GZIP);
158 static int Jim_Decompress(Jim_Interp *interp, const char *in, int len, long bufsiz, int wbits)
160 z_stream strm = {0};
161 void *buf;
162 Jim_Obj *out;
163 int ret;
165 if (JimZlibCheckBufSize(interp, bufsiz)) {
166 return JIM_ERR;
169 if (inflateInit2(&strm, wbits) != Z_OK) {
170 return JIM_ERR;
173 /* allocate a buffer - decompression is done in chunks, into this buffer;
174 * when the decompressed data size is given, decompression is faster because
175 * it's done in one pass, with less memcpy() overhead */
176 buf = Jim_Alloc((int)bufsiz);
178 out = Jim_NewEmptyStringObj(interp);
179 Jim_IncrRefCount(out);
181 strm.next_in = (Bytef*)in;
182 strm.avail_in = (uInt)len;
183 do {
184 do {
185 strm.next_out = buf;
186 strm.avail_out = (uInt)bufsiz;
188 ret = inflate(&strm, Z_NO_FLUSH);
189 switch (ret) {
190 case Z_OK:
191 case Z_STREAM_END:
192 /* append each chunk to the output object */
193 Jim_AppendString(interp, out, buf, (int)(bufsiz - (long)strm.avail_out));
194 break;
196 default:
197 Jim_DecrRefCount(interp, out);
198 Jim_Free(buf);
199 inflateEnd(&strm);
200 if (strm.msg != NULL)
201 Jim_SetResultString(interp, strm.msg, -1);
202 return JIM_ERR;
204 } while (strm.avail_out == 0);
205 } while (ret != Z_STREAM_END);
207 /* free memory used for decompression before we assign the return value */
208 Jim_Free(buf);
209 inflateEnd(&strm);
211 Jim_SetResult(interp, out);
212 Jim_DecrRefCount(interp, out);
214 return JIM_OK;
217 static int Jim_Inflate(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
219 long bufsiz = DEF_DECOMPRESS_BUFSIZ;
220 const char *in;
221 int len;
223 if (argc != 1) {
224 if (Jim_GetLong(interp, argv[1], &bufsiz) != JIM_OK) {
225 return JIM_ERR;
228 if (JimZlibCheckBufSize(interp, bufsiz)) {
229 return JIM_ERR;
233 in = Jim_GetString(argv[0], &len);
234 return Jim_Decompress(interp, in, len, bufsiz, -MAX_WBITS);
237 static int Jim_Gunzip(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
239 long bufsiz = DEF_DECOMPRESS_BUFSIZ;
240 const char *in;
241 int len;
243 if (argc == 3) {
244 if (!Jim_CompareStringImmediate(interp, argv[1], "-buffersize")) {
245 return -1;
248 if (Jim_GetLong(interp, argv[2], &bufsiz) != JIM_OK) {
249 return -1;
253 else if (argc != 1) {
254 return -1;
257 in = Jim_GetString(argv[0], &len);
258 return Jim_Decompress(interp, in, len, bufsiz, WBITS_GZIP);
261 static const jim_subcmd_type zlib_command_table[] = {
262 { "crc32",
263 "data ?startValue?",
264 Jim_Crc32,
267 /* Description: Calculates the CRC32 checksum of a string */
269 { "deflate",
270 "string ?level?",
271 Jim_Deflate,
274 /* Description: Compresses a string and outputs a raw, zlib-compressed stream */
276 { "gzip",
277 "data ?-level level?",
278 Jim_Gzip,
281 /* Description: Compresses a string and outputs a gzip-compressed stream */
283 { "inflate",
284 "data ?bufferSize?",
285 Jim_Inflate,
288 /* Description: Decompresses a raw, zlib-compressed stream */
290 { "gunzip",
291 "data ?-buffersize size?",
292 Jim_Gunzip,
295 /* Description: Decompresses a gzip-compressed stream */
297 { NULL }
300 static int JimZlibCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
302 return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, zlib_command_table, argc, argv), argc, argv);
305 int Jim_zlibInit(Jim_Interp *interp)
307 if (Jim_PackageProvide(interp, "zlib", "1.0", JIM_ERRMSG)) {
308 return JIM_ERR;
311 Jim_CreateCommand(interp, "zlib", JimZlibCmd, 0, 0);
313 return JIM_OK;