Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 437 lines (346 sloc) 12.318 kb
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
1 // Copyright Joyent, Inc. and other Node contributors.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22
ff4a9d3 @bnoordhuis core: use proper #include directives
bnoordhuis authored
23 #include "v8.h"
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
24 #include <errno.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <sys/types.h>
28 #include <zlib.h>
29
ff4a9d3 @bnoordhuis core: use proper #include directives
bnoordhuis authored
30 #include "node.h"
31 #include "node_buffer.h"
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
32
33
34 namespace node {
35 using namespace v8;
36
37
74a8215 @bnoordhuis Revert support for isolates.
bnoordhuis authored
38 static Persistent<String> callback_sym;
39
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
40 enum node_zlib_mode {
41 DEFLATE = 1,
42 INFLATE,
43 GZIP,
44 GUNZIP,
45 DEFLATERAW,
46 INFLATERAW,
47 UNZIP
48 };
49
50 template <node_zlib_mode mode> class ZCtx;
51
52
53 void InitZlib(v8::Handle<v8::Object> target);
54
55
56 /**
57 * Deflate/Inflate
58 */
59 template <node_zlib_mode mode> class ZCtx : public ObjectWrap {
60 public:
61
62 ZCtx() : ObjectWrap() {
e609195 @indutny [zlib] added dictionary support
indutny authored
63 dictionary_ = NULL;
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
64 }
65
66 ~ZCtx() {
67 if (mode == DEFLATE || mode == GZIP || mode == DEFLATERAW) {
68 (void)deflateEnd(&strm_);
69 } else if (mode == INFLATE || mode == GUNZIP || mode == INFLATERAW) {
70 (void)inflateEnd(&strm_);
71 }
e609195 @indutny [zlib] added dictionary support
indutny authored
72
73 if (dictionary_ != NULL) delete[] dictionary_;
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
74 }
75
76 // write(flush, in, in_off, in_len, out, out_off, out_len)
07701e7 @indutny zlib: C++ style fixes
indutny authored
77 static Handle<Value> Write(const Arguments& args) {
d104bfd @isaacs zlib: Fix test so that it's not trivially passing, then pass it.
isaacs authored
78 HandleScope scope;
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
79 assert(args.Length() == 7);
80
81 ZCtx<mode> *ctx = ObjectWrap::Unwrap< ZCtx<mode> >(args.This());
82 assert(ctx->init_done_ && "write before init");
83
8cca30f @isaacs zlib binding cleanup
isaacs authored
84 assert(!ctx->write_in_progress_ && "write already in progress");
85 ctx->write_in_progress_ = true;
86
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
87 unsigned int flush = args[0]->Uint32Value();
88 Bytef *in;
89 Bytef *out;
90 size_t in_off, in_len, out_off, out_len;
91
92 if (args[1]->IsNull()) {
93 // just a flush
94 Bytef nada[1] = { 0 };
95 in = nada;
96 in_len = 0;
97 in_off = 0;
98 } else {
99 assert(Buffer::HasInstance(args[1]));
100 Local<Object> in_buf;
101 in_buf = args[1]->ToObject();
07701e7 @indutny zlib: C++ style fixes
indutny authored
102 in_off = args[2]->Uint32Value();
103 in_len = args[3]->Uint32Value();
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
104
105 assert(in_off + in_len <= Buffer::Length(in_buf));
106 in = reinterpret_cast<Bytef *>(Buffer::Data(in_buf) + in_off);
107 }
108
109 assert(Buffer::HasInstance(args[4]));
110 Local<Object> out_buf = args[4]->ToObject();
07701e7 @indutny zlib: C++ style fixes
indutny authored
111 out_off = args[5]->Uint32Value();
112 out_len = args[6]->Uint32Value();
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
113 assert(out_off + out_len <= Buffer::Length(out_buf));
114 out = reinterpret_cast<Bytef *>(Buffer::Data(out_buf) + out_off);
115
8cca30f @isaacs zlib binding cleanup
isaacs authored
116 // build up the work request
117 uv_work_t* work_req = &(ctx->work_req_);
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
118
119 ctx->strm_.avail_in = in_len;
07701e7 @indutny zlib: C++ style fixes
indutny authored
120 ctx->strm_.next_in = in;
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
121 ctx->strm_.avail_out = out_len;
122 ctx->strm_.next_out = out;
123 ctx->flush_ = flush;
124
125 // set this so that later on, I can easily tell how much was written.
126 ctx->chunk_size_ = out_len;
127
74a8215 @bnoordhuis Revert support for isolates.
bnoordhuis authored
128 uv_queue_work(uv_default_loop(),
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
129 work_req,
130 ZCtx<mode>::Process,
131 ZCtx<mode>::After);
132
0ad2717 @bnoordhuis Make sure that zlib contexts are not garbage collected when busy
bnoordhuis authored
133 ctx->Ref();
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
134
8cca30f @isaacs zlib binding cleanup
isaacs authored
135 return ctx->handle_;
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
136 }
137
138
139 // thread pool!
140 // This function may be called multiple times on the uv_work pool
141 // for a single write() call, until all of the input bytes have
142 // been consumed.
07701e7 @indutny zlib: C++ style fixes
indutny authored
143 static void Process(uv_work_t* work_req) {
8cca30f @isaacs zlib binding cleanup
isaacs authored
144 ZCtx<mode> *ctx = container_of(work_req, ZCtx<mode>, work_req_);
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
145
146 // If the avail_out is left at 0, then it means that it ran out
147 // of room. If there was avail_out left over, then it means
148 // that all of the input was consumed.
149 int err;
150 switch (mode) {
151 case DEFLATE:
152 case GZIP:
153 case DEFLATERAW:
07701e7 @indutny zlib: C++ style fixes
indutny authored
154 err = deflate(&ctx->strm_, ctx->flush_);
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
155 break;
156 case UNZIP:
157 case INFLATE:
158 case GUNZIP:
159 case INFLATERAW:
07701e7 @indutny zlib: C++ style fixes
indutny authored
160 err = inflate(&ctx->strm_, ctx->flush_);
e609195 @indutny [zlib] added dictionary support
indutny authored
161
162 // If data was encoded with dictionary
163 if (err == Z_NEED_DICT) {
164 assert(ctx->dictionary_ != NULL && "Stream has no dictionary");
165
166 // Load it
89556f5 @indutny zlib: C++ style fixes for dictionary
indutny authored
167 err = inflateSetDictionary(&ctx->strm_,
168 ctx->dictionary_,
169 ctx->dictionary_len_);
e609195 @indutny [zlib] added dictionary support
indutny authored
170 assert(err == Z_OK && "Failed to set dictionary");
171
172 // And try to decode again
89556f5 @indutny zlib: C++ style fixes for dictionary
indutny authored
173 err = inflate(&ctx->strm_, ctx->flush_);
e609195 @indutny [zlib] added dictionary support
indutny authored
174 }
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
175 break;
176 default:
177 assert(0 && "wtf?");
178 }
179 assert(err != Z_STREAM_ERROR);
180
181 // now After will emit the output, and
182 // either schedule another call to Process,
183 // or shift the queue and call Process.
184 }
185
186 // v8 land!
07701e7 @indutny zlib: C++ style fixes
indutny authored
187 static void After(uv_work_t* work_req) {
d104bfd @isaacs zlib: Fix test so that it's not trivially passing, then pass it.
isaacs authored
188 HandleScope scope;
8cca30f @isaacs zlib binding cleanup
isaacs authored
189 ZCtx<mode> *ctx = container_of(work_req, ZCtx<mode>, work_req_);
07701e7 @indutny zlib: C++ style fixes
indutny authored
190
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
191 Local<Integer> avail_out = Integer::New(ctx->strm_.avail_out);
192 Local<Integer> avail_in = Integer::New(ctx->strm_.avail_in);
193
8cca30f @isaacs zlib binding cleanup
isaacs authored
194 ctx->write_in_progress_ = false;
195
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
196 // call the write() cb
8cca30f @isaacs zlib binding cleanup
isaacs authored
197 assert(ctx->handle_->Get(callback_sym)->IsFunction() &&
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
198 "Invalid callback");
199 Local<Value> args[2] = { avail_in, avail_out };
8cca30f @isaacs zlib binding cleanup
isaacs authored
200 MakeCallback(ctx->handle_, "callback", 2, args);
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
201
0ad2717 @bnoordhuis Make sure that zlib contexts are not garbage collected when busy
bnoordhuis authored
202 ctx->Unref();
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
203 }
204
07701e7 @indutny zlib: C++ style fixes
indutny authored
205 static Handle<Value> New(const Arguments& args) {
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
206 HandleScope scope;
207 ZCtx<mode> *ctx = new ZCtx<mode>();
208 ctx->Wrap(args.This());
209 return args.This();
210 }
211
212 // just pull the ints out of the args and call the other Init
07701e7 @indutny zlib: C++ style fixes
indutny authored
213 static Handle<Value> Init(const Arguments& args) {
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
214 HandleScope scope;
215
e609195 @indutny [zlib] added dictionary support
indutny authored
216 assert((args.Length() == 4 || args.Length() == 5) &&
217 "init(windowBits, level, memLevel, strategy, [dictionary])");
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
218
219 ZCtx<mode> *ctx = ObjectWrap::Unwrap< ZCtx<mode> >(args.This());
220
221 int windowBits = args[0]->Uint32Value();
222 assert((windowBits >= 8 && windowBits <= 15) && "invalid windowBits");
223
224 int level = args[1]->Uint32Value();
225 assert((level >= -1 && level <= 9) && "invalid compression level");
226
227 int memLevel = args[2]->Uint32Value();
228 assert((memLevel >= 1 && memLevel <= 9) && "invalid memlevel");
229
230 int strategy = args[3]->Uint32Value();
231 assert((strategy == Z_FILTERED ||
232 strategy == Z_HUFFMAN_ONLY ||
233 strategy == Z_RLE ||
234 strategy == Z_FIXED ||
235 strategy == Z_DEFAULT_STRATEGY) && "invalid strategy");
236
e609195 @indutny [zlib] added dictionary support
indutny authored
237 char* dictionary = NULL;
238 size_t dictionary_len = 0;
239 if (args.Length() >= 5 && Buffer::HasInstance(args[4])) {
240 Local<Object> dictionary_ = args[4]->ToObject();
241
242 dictionary_len = Buffer::Length(dictionary_);
243 dictionary = new char[dictionary_len];
244
245 memcpy(dictionary, Buffer::Data(dictionary_), dictionary_len);
246 }
247
248 Init(ctx, level, windowBits, memLevel, strategy,
249 dictionary, dictionary_len);
71ae175 @indutny zlib: reset() method for deflate/inflate streams
indutny authored
250 SetDictionary(ctx);
251 return Undefined();
252 }
253
254 static Handle<Value> Reset(const Arguments &args) {
255 HandleScope scope;
256
257 ZCtx<mode> *ctx = ObjectWrap::Unwrap< ZCtx<mode> >(args.This());
258
259 Reset(ctx);
260 SetDictionary(ctx);
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
261 return Undefined();
262 }
263
07701e7 @indutny zlib: C++ style fixes
indutny authored
264 static void Init(ZCtx *ctx, int level, int windowBits, int memLevel,
9e6957b @indutny Merge branch 'v0.6'
indutny authored
265 int strategy, char* dictionary, size_t dictionary_len) {
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
266 ctx->level_ = level;
267 ctx->windowBits_ = windowBits;
268 ctx->memLevel_ = memLevel;
269 ctx->strategy_ = strategy;
270
271 ctx->strm_.zalloc = Z_NULL;
272 ctx->strm_.zfree = Z_NULL;
273 ctx->strm_.opaque = Z_NULL;
274
275 ctx->flush_ = Z_NO_FLUSH;
276
277 if (mode == GZIP || mode == GUNZIP) {
278 ctx->windowBits_ += 16;
279 }
280
281 if (mode == UNZIP) {
282 ctx->windowBits_ += 32;
283 }
284
285 if (mode == DEFLATERAW || mode == INFLATERAW) {
286 ctx->windowBits_ *= -1;
287 }
288
289 int err;
290 switch (mode) {
291 case DEFLATE:
292 case GZIP:
293 case DEFLATERAW:
89556f5 @indutny zlib: C++ style fixes for dictionary
indutny authored
294 err = deflateInit2(&ctx->strm_,
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
295 ctx->level_,
296 Z_DEFLATED,
297 ctx->windowBits_,
298 ctx->memLevel_,
299 ctx->strategy_);
300 break;
301 case INFLATE:
302 case GUNZIP:
303 case INFLATERAW:
304 case UNZIP:
89556f5 @indutny zlib: C++ style fixes for dictionary
indutny authored
305 err = inflateInit2(&ctx->strm_, ctx->windowBits_);
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
306 break;
307 default:
308 assert(0 && "wtf?");
309 }
310
311 assert(err == Z_OK);
e609195 @indutny [zlib] added dictionary support
indutny authored
312
313 ctx->dictionary_ = reinterpret_cast<Bytef *>(dictionary);
314 ctx->dictionary_len_ = dictionary_len;
315
71ae175 @indutny zlib: reset() method for deflate/inflate streams
indutny authored
316 ctx->write_in_progress_ = false;
317 ctx->init_done_ = true;
318 }
319
320 static void SetDictionary(ZCtx* ctx) {
321 if (ctx->dictionary_ == NULL) return;
e609195 @indutny [zlib] added dictionary support
indutny authored
322
c80abfa @indutny zlib: fix `Failed to set dictionary` issue
indutny authored
323 int err = Z_OK;
71ae175 @indutny zlib: reset() method for deflate/inflate streams
indutny authored
324
325 switch (mode) {
326 case DEFLATE:
327 case DEFLATERAW:
328 err = deflateSetDictionary(&ctx->strm_,
329 ctx->dictionary_,
330 ctx->dictionary_len_);
331 break;
332 default:
333 break;
e609195 @indutny [zlib] added dictionary support
indutny authored
334 }
335
71ae175 @indutny zlib: reset() method for deflate/inflate streams
indutny authored
336 assert(err == Z_OK && "Failed to set dictionary");
337 }
338
339 static void Reset(ZCtx* ctx) {
c80abfa @indutny zlib: fix `Failed to set dictionary` issue
indutny authored
340 int err = Z_OK;
71ae175 @indutny zlib: reset() method for deflate/inflate streams
indutny authored
341
342 switch (mode) {
343 case DEFLATE:
344 case DEFLATERAW:
345 err = deflateReset(&ctx->strm_);
346 break;
347 case INFLATE:
348 case INFLATERAW:
349 err = inflateReset(&ctx->strm_);
350 break;
351 default:
352 break;
353 }
354
355 assert(err == Z_OK && "Failed to reset stream");
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
356 }
357
358 private:
359
360 bool init_done_;
361
362 z_stream strm_;
363 int level_;
364 int windowBits_;
365 int memLevel_;
366 int strategy_;
367
e609195 @indutny [zlib] added dictionary support
indutny authored
368 Bytef* dictionary_;
369 size_t dictionary_len_;
370
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
371 int flush_;
372
373 int chunk_size_;
8cca30f @isaacs zlib binding cleanup
isaacs authored
374
375 bool write_in_progress_;
376
377 uv_work_t work_req_;
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
378 };
379
380
381 #define NODE_ZLIB_CLASS(mode, name) \
382 { \
383 Local<FunctionTemplate> z = FunctionTemplate::New(ZCtx<mode>::New); \
384 z->InstanceTemplate()->SetInternalFieldCount(1); \
385 NODE_SET_PROTOTYPE_METHOD(z, "write", ZCtx<mode>::Write); \
386 NODE_SET_PROTOTYPE_METHOD(z, "init", ZCtx<mode>::Init); \
71ae175 @indutny zlib: reset() method for deflate/inflate streams
indutny authored
387 NODE_SET_PROTOTYPE_METHOD(z, "reset", ZCtx<mode>::Reset); \
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
388 z->SetClassName(String::NewSymbol(name)); \
389 target->Set(String::NewSymbol(name), z->GetFunction()); \
390 }
391
392 void InitZlib(Handle<Object> target) {
393 HandleScope scope;
394
395 NODE_ZLIB_CLASS(INFLATE, "Inflate")
396 NODE_ZLIB_CLASS(DEFLATE, "Deflate")
397 NODE_ZLIB_CLASS(INFLATERAW, "InflateRaw")
398 NODE_ZLIB_CLASS(DEFLATERAW, "DeflateRaw")
399 NODE_ZLIB_CLASS(GZIP, "Gzip")
400 NODE_ZLIB_CLASS(GUNZIP, "Gunzip")
401 NODE_ZLIB_CLASS(UNZIP, "Unzip")
402
403 callback_sym = NODE_PSYMBOL("callback");
404
405 NODE_DEFINE_CONSTANT(target, Z_NO_FLUSH);
406 NODE_DEFINE_CONSTANT(target, Z_PARTIAL_FLUSH);
407 NODE_DEFINE_CONSTANT(target, Z_SYNC_FLUSH);
408 NODE_DEFINE_CONSTANT(target, Z_FULL_FLUSH);
409 NODE_DEFINE_CONSTANT(target, Z_FINISH);
410 NODE_DEFINE_CONSTANT(target, Z_BLOCK);
411 NODE_DEFINE_CONSTANT(target, Z_OK);
412 NODE_DEFINE_CONSTANT(target, Z_STREAM_END);
413 NODE_DEFINE_CONSTANT(target, Z_NEED_DICT);
414 NODE_DEFINE_CONSTANT(target, Z_ERRNO);
415 NODE_DEFINE_CONSTANT(target, Z_STREAM_ERROR);
416 NODE_DEFINE_CONSTANT(target, Z_DATA_ERROR);
417 NODE_DEFINE_CONSTANT(target, Z_MEM_ERROR);
418 NODE_DEFINE_CONSTANT(target, Z_BUF_ERROR);
419 NODE_DEFINE_CONSTANT(target, Z_VERSION_ERROR);
420 NODE_DEFINE_CONSTANT(target, Z_NO_COMPRESSION);
421 NODE_DEFINE_CONSTANT(target, Z_BEST_SPEED);
422 NODE_DEFINE_CONSTANT(target, Z_BEST_COMPRESSION);
423 NODE_DEFINE_CONSTANT(target, Z_DEFAULT_COMPRESSION);
424 NODE_DEFINE_CONSTANT(target, Z_FILTERED);
425 NODE_DEFINE_CONSTANT(target, Z_HUFFMAN_ONLY);
426 NODE_DEFINE_CONSTANT(target, Z_RLE);
427 NODE_DEFINE_CONSTANT(target, Z_FIXED);
428 NODE_DEFINE_CONSTANT(target, Z_DEFAULT_STRATEGY);
429 NODE_DEFINE_CONSTANT(target, ZLIB_VERNUM);
430
431 target->Set(String::NewSymbol("ZLIB_VERSION"), String::New(ZLIB_VERSION));
432 }
433
434 } // namespace node
435
cdcb111 @bnoordhuis Remove stray NODE_MODULE() semi-colons.
bnoordhuis authored
436 NODE_MODULE(node_zlib, node::InitZlib)
Something went wrong with that request. Please try again.