Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 417 lines (337 sloc) 11.818 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
23 #include <v8.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <sys/types.h>
28 #include <zlib.h>
29
30 #include <node.h>
31 #include <node_buffer.h>
32 #include <req_wrap.h>
33
34
35
36 namespace node {
37 using namespace v8;
38
39 // write() returns one of these, and then calls the cb() when it's done.
40 typedef ReqWrap<uv_work_t> WorkReqWrap;
41
42 static Persistent<String> callback_sym;
43
44 enum node_zlib_mode {
45 DEFLATE = 1,
46 INFLATE,
47 GZIP,
48 GUNZIP,
49 DEFLATERAW,
50 INFLATERAW,
51 UNZIP
52 };
53
54 template <node_zlib_mode mode> class ZCtx;
55
56
57 void InitZlib(v8::Handle<v8::Object> target);
58
59
60 /**
61 * Deflate/Inflate
62 */
63 template <node_zlib_mode mode> class ZCtx : public ObjectWrap {
64 public:
65
66 ZCtx() : ObjectWrap() {
e609195 @indutny [zlib] added dictionary support
indutny authored
67 dictionary_ = NULL;
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
68 }
69
70 ~ZCtx() {
71 if (mode == DEFLATE || mode == GZIP || mode == DEFLATERAW) {
72 (void)deflateEnd(&strm_);
73 } else if (mode == INFLATE || mode == GUNZIP || mode == INFLATERAW) {
74 (void)inflateEnd(&strm_);
75 }
e609195 @indutny [zlib] added dictionary support
indutny authored
76
77 if (dictionary_ != NULL) delete[] dictionary_;
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
78 }
79
80 // write(flush, in, in_off, in_len, out, out_off, out_len)
81 static Handle<Value>
82 Write(const Arguments& args) {
d104bfd @isaacs zlib: Fix test so that it's not trivially passing, then pass it.
isaacs authored
83 HandleScope scope;
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
84 assert(args.Length() == 7);
85
86 ZCtx<mode> *ctx = ObjectWrap::Unwrap< ZCtx<mode> >(args.This());
87 assert(ctx->init_done_ && "write before init");
88
89 unsigned int flush = args[0]->Uint32Value();
90 Bytef *in;
91 Bytef *out;
92 size_t in_off, in_len, out_off, out_len;
93
94 if (args[1]->IsNull()) {
95 // just a flush
96 Bytef nada[1] = { 0 };
97 in = nada;
98 in_len = 0;
99 in_off = 0;
100 } else {
101 assert(Buffer::HasInstance(args[1]));
102 Local<Object> in_buf;
103 in_buf = args[1]->ToObject();
104 in_off = (size_t)args[2]->Uint32Value();
105 in_len = (size_t)args[3]->Uint32Value();
106
107 assert(in_off + in_len <= Buffer::Length(in_buf));
108 in = reinterpret_cast<Bytef *>(Buffer::Data(in_buf) + in_off);
109 }
110
111 assert(Buffer::HasInstance(args[4]));
112 Local<Object> out_buf = args[4]->ToObject();
113 out_off = (size_t)args[5]->Uint32Value();
114 out_len = (size_t)args[6]->Uint32Value();
115 assert(out_off + out_len <= Buffer::Length(out_buf));
116 out = reinterpret_cast<Bytef *>(Buffer::Data(out_buf) + out_off);
117
118 WorkReqWrap *req_wrap = new WorkReqWrap();
119
120 req_wrap->data_ = ctx;
121 ctx->strm_.avail_in = in_len;
122 ctx->strm_.next_in = &(*in);
123 ctx->strm_.avail_out = out_len;
124 ctx->strm_.next_out = out;
125 ctx->flush_ = flush;
126
127 // set this so that later on, I can easily tell how much was written.
128 ctx->chunk_size_ = out_len;
129
130 // build up the work request
131 uv_work_t* work_req = new uv_work_t();
132 work_req->data = req_wrap;
133
134 uv_queue_work(uv_default_loop(),
135 work_req,
136 ZCtx<mode>::Process,
137 ZCtx<mode>::After);
138
139 req_wrap->Dispatched();
140
141 return req_wrap->object_;
142 }
143
144
145 // thread pool!
146 // This function may be called multiple times on the uv_work pool
147 // for a single write() call, until all of the input bytes have
148 // been consumed.
149 static void
150 Process(uv_work_t* work_req) {
151 WorkReqWrap *req_wrap = reinterpret_cast<WorkReqWrap *>(work_req->data);
152 ZCtx<mode> *ctx = (ZCtx<mode> *)req_wrap->data_;
153
154 // If the avail_out is left at 0, then it means that it ran out
155 // of room. If there was avail_out left over, then it means
156 // that all of the input was consumed.
157 int err;
158 switch (mode) {
159 case DEFLATE:
160 case GZIP:
161 case DEFLATERAW:
162 err = deflate(&(ctx->strm_), ctx->flush_);
163 break;
164 case UNZIP:
165 case INFLATE:
166 case GUNZIP:
167 case INFLATERAW:
168 err = inflate(&(ctx->strm_), ctx->flush_);
e609195 @indutny [zlib] added dictionary support
indutny authored
169
170 // If data was encoded with dictionary
171 if (err == Z_NEED_DICT) {
172 assert(ctx->dictionary_ != NULL && "Stream has no dictionary");
173
174 // Load it
175 err = inflateSetDictionary(
176 &(ctx->strm_),
177 ctx->dictionary_,
178 ctx->dictionary_len_
179 );
180 assert(err == Z_OK && "Failed to set dictionary");
181
182 // And try to decode again
183 err = inflate(&(ctx->strm_), ctx->flush_);
184 }
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
185 break;
186 default:
187 assert(0 && "wtf?");
188 }
189 assert(err != Z_STREAM_ERROR);
190
191 // now After will emit the output, and
192 // either schedule another call to Process,
193 // or shift the queue and call Process.
194 }
195
196 // v8 land!
197 static void
198 After(uv_work_t* work_req) {
d104bfd @isaacs zlib: Fix test so that it's not trivially passing, then pass it.
isaacs authored
199 HandleScope scope;
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
200 WorkReqWrap *req_wrap = reinterpret_cast<WorkReqWrap *>(work_req->data);
201 ZCtx<mode> *ctx = (ZCtx<mode> *)req_wrap->data_;
202 Local<Integer> avail_out = Integer::New(ctx->strm_.avail_out);
203 Local<Integer> avail_in = Integer::New(ctx->strm_.avail_in);
204
205 // call the write() cb
206 assert(req_wrap->object_->Get(callback_sym)->IsFunction() &&
207 "Invalid callback");
208 Local<Value> args[2] = { avail_in, avail_out };
209 MakeCallback(req_wrap->object_, "callback", 2, args);
210
211 // delete the ReqWrap
212 delete req_wrap;
213 }
214
215 static Handle<Value>
216 New(const Arguments& args) {
217 HandleScope scope;
218 ZCtx<mode> *ctx = new ZCtx<mode>();
219 ctx->Wrap(args.This());
220 return args.This();
221 }
222
223 // just pull the ints out of the args and call the other Init
224 static Handle<Value>
225 Init(const Arguments& args) {
226 HandleScope scope;
227
e609195 @indutny [zlib] added dictionary support
indutny authored
228 assert((args.Length() == 4 || args.Length() == 5) &&
229 "init(windowBits, level, memLevel, strategy, [dictionary])");
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
230
231 ZCtx<mode> *ctx = ObjectWrap::Unwrap< ZCtx<mode> >(args.This());
232
233 int windowBits = args[0]->Uint32Value();
234 assert((windowBits >= 8 && windowBits <= 15) && "invalid windowBits");
235
236 int level = args[1]->Uint32Value();
237 assert((level >= -1 && level <= 9) && "invalid compression level");
238
239 int memLevel = args[2]->Uint32Value();
240 assert((memLevel >= 1 && memLevel <= 9) && "invalid memlevel");
241
242 int strategy = args[3]->Uint32Value();
243 assert((strategy == Z_FILTERED ||
244 strategy == Z_HUFFMAN_ONLY ||
245 strategy == Z_RLE ||
246 strategy == Z_FIXED ||
247 strategy == Z_DEFAULT_STRATEGY) && "invalid strategy");
248
e609195 @indutny [zlib] added dictionary support
indutny authored
249 char* dictionary = NULL;
250 size_t dictionary_len = 0;
251 if (args.Length() >= 5 && Buffer::HasInstance(args[4])) {
252 Local<Object> dictionary_ = args[4]->ToObject();
253
254 dictionary_len = Buffer::Length(dictionary_);
255 dictionary = new char[dictionary_len];
256
257 memcpy(dictionary, Buffer::Data(dictionary_), dictionary_len);
258 }
259
260 Init(ctx, level, windowBits, memLevel, strategy,
261 dictionary, dictionary_len);
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
262 return Undefined();
263 }
264
265 static void
266 Init(ZCtx *ctx,
267 int level,
268 int windowBits,
269 int memLevel,
e609195 @indutny [zlib] added dictionary support
indutny authored
270 int strategy,
271 char* dictionary,
272 size_t dictionary_len) {
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
273 ctx->level_ = level;
274 ctx->windowBits_ = windowBits;
275 ctx->memLevel_ = memLevel;
276 ctx->strategy_ = strategy;
277
278 ctx->strm_.zalloc = Z_NULL;
279 ctx->strm_.zfree = Z_NULL;
280 ctx->strm_.opaque = Z_NULL;
281
282 ctx->flush_ = Z_NO_FLUSH;
283
284 if (mode == GZIP || mode == GUNZIP) {
285 ctx->windowBits_ += 16;
286 }
287
288 if (mode == UNZIP) {
289 ctx->windowBits_ += 32;
290 }
291
292 if (mode == DEFLATERAW || mode == INFLATERAW) {
293 ctx->windowBits_ *= -1;
294 }
295
296 int err;
297 switch (mode) {
298 case DEFLATE:
299 case GZIP:
300 case DEFLATERAW:
301 err = deflateInit2(&(ctx->strm_),
302 ctx->level_,
303 Z_DEFLATED,
304 ctx->windowBits_,
305 ctx->memLevel_,
306 ctx->strategy_);
307 break;
308 case INFLATE:
309 case GUNZIP:
310 case INFLATERAW:
311 case UNZIP:
312 err = inflateInit2(&(ctx->strm_), ctx->windowBits_);
313 break;
314 default:
315 assert(0 && "wtf?");
316 }
317
318 assert(err == Z_OK);
e609195 @indutny [zlib] added dictionary support
indutny authored
319
320 ctx->dictionary_ = reinterpret_cast<Bytef *>(dictionary);
321 ctx->dictionary_len_ = dictionary_len;
322
323 if (dictionary != NULL) {
324 switch (mode) {
325 case DEFLATE:
326 case DEFLATERAW:
327 err = deflateSetDictionary(
328 &(ctx->strm_),
329 ctx->dictionary_,
330 dictionary_len
331 );
332 break;
333 default:
334 break;
335 }
336
337 assert(err == Z_OK && "Failed to set dictionary");
338 }
339
340 ctx->init_done_ = true;
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
341 }
342
343 private:
344
345 bool init_done_;
346
347 z_stream strm_;
348 int level_;
349 int windowBits_;
350 int memLevel_;
351 int strategy_;
352
e609195 @indutny [zlib] added dictionary support
indutny authored
353 Bytef* dictionary_;
354 size_t dictionary_len_;
355
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
356 int flush_;
357
358 int chunk_size_;
359 };
360
361
362 #define NODE_ZLIB_CLASS(mode, name) \
363 { \
364 Local<FunctionTemplate> z = FunctionTemplate::New(ZCtx<mode>::New); \
365 z->InstanceTemplate()->SetInternalFieldCount(1); \
366 NODE_SET_PROTOTYPE_METHOD(z, "write", ZCtx<mode>::Write); \
367 NODE_SET_PROTOTYPE_METHOD(z, "init", ZCtx<mode>::Init); \
368 z->SetClassName(String::NewSymbol(name)); \
369 target->Set(String::NewSymbol(name), z->GetFunction()); \
370 }
371
372 void InitZlib(Handle<Object> target) {
373 HandleScope scope;
374
375 NODE_ZLIB_CLASS(INFLATE, "Inflate")
376 NODE_ZLIB_CLASS(DEFLATE, "Deflate")
377 NODE_ZLIB_CLASS(INFLATERAW, "InflateRaw")
378 NODE_ZLIB_CLASS(DEFLATERAW, "DeflateRaw")
379 NODE_ZLIB_CLASS(GZIP, "Gzip")
380 NODE_ZLIB_CLASS(GUNZIP, "Gunzip")
381 NODE_ZLIB_CLASS(UNZIP, "Unzip")
382
383 callback_sym = NODE_PSYMBOL("callback");
384
385 NODE_DEFINE_CONSTANT(target, Z_NO_FLUSH);
386 NODE_DEFINE_CONSTANT(target, Z_PARTIAL_FLUSH);
387 NODE_DEFINE_CONSTANT(target, Z_SYNC_FLUSH);
388 NODE_DEFINE_CONSTANT(target, Z_FULL_FLUSH);
389 NODE_DEFINE_CONSTANT(target, Z_FINISH);
390 NODE_DEFINE_CONSTANT(target, Z_BLOCK);
391 NODE_DEFINE_CONSTANT(target, Z_OK);
392 NODE_DEFINE_CONSTANT(target, Z_STREAM_END);
393 NODE_DEFINE_CONSTANT(target, Z_NEED_DICT);
394 NODE_DEFINE_CONSTANT(target, Z_ERRNO);
395 NODE_DEFINE_CONSTANT(target, Z_STREAM_ERROR);
396 NODE_DEFINE_CONSTANT(target, Z_DATA_ERROR);
397 NODE_DEFINE_CONSTANT(target, Z_MEM_ERROR);
398 NODE_DEFINE_CONSTANT(target, Z_BUF_ERROR);
399 NODE_DEFINE_CONSTANT(target, Z_VERSION_ERROR);
400 NODE_DEFINE_CONSTANT(target, Z_NO_COMPRESSION);
401 NODE_DEFINE_CONSTANT(target, Z_BEST_SPEED);
402 NODE_DEFINE_CONSTANT(target, Z_BEST_COMPRESSION);
403 NODE_DEFINE_CONSTANT(target, Z_DEFAULT_COMPRESSION);
404 NODE_DEFINE_CONSTANT(target, Z_FILTERED);
405 NODE_DEFINE_CONSTANT(target, Z_HUFFMAN_ONLY);
406 NODE_DEFINE_CONSTANT(target, Z_RLE);
407 NODE_DEFINE_CONSTANT(target, Z_FIXED);
408 NODE_DEFINE_CONSTANT(target, Z_DEFAULT_STRATEGY);
409 NODE_DEFINE_CONSTANT(target, ZLIB_VERNUM);
410
411 target->Set(String::NewSymbol("ZLIB_VERSION"), String::New(ZLIB_VERSION));
412 }
413
414 } // namespace node
415
cdcb111 @bnoordhuis Remove stray NODE_MODULE() semi-colons.
bnoordhuis authored
416 NODE_MODULE(node_zlib, node::InitZlib)
Something went wrong with that request. Please try again.