1 // Copyright Joyent, Inc. and other Node contributors.
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:
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
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.
27 #include <sys/types.h>
31 #include <node_buffer.h>
39 // write() returns one of these, and then calls the cb() when it's done.
40 typedef ReqWrap<uv_work_t> WorkReqWrap;
42 static Persistent<String> callback_sym;
54 template <node_zlib_mode mode> class ZCtx;
57 void InitZlib(v8::Handle<v8::Object> target);
63 template <node_zlib_mode mode> class ZCtx : public ObjectWrap {
66 ZCtx() : ObjectWrap() {
70 if (mode == DEFLATE || mode == GZIP || mode == DEFLATERAW) {
71 (void)deflateEnd(&strm_);
72 } else if (mode == INFLATE || mode == GUNZIP || mode == INFLATERAW) {
73 (void)inflateEnd(&strm_);
77 // write(flush, in, in_off, in_len, out, out_off, out_len)
79 Write(const Arguments& args) {
81 assert(args.Length() == 7);
83 ZCtx<mode> *ctx = ObjectWrap::Unwrap< ZCtx<mode> >(args.This());
84 assert(ctx->init_done_ && "write before init");
86 unsigned int flush = args[0]->Uint32Value();
89 size_t in_off, in_len, out_off, out_len;
91 if (args[1]->IsNull()) {
93 Bytef nada[1] = { 0 };
98 assert(Buffer::HasInstance(args[1]));
100 in_buf = args[1]->ToObject();
101 in_off = (size_t)args[2]->Uint32Value();
102 in_len = (size_t)args[3]->Uint32Value();
104 assert(in_off + in_len <= Buffer::Length(in_buf));
105 in = reinterpret_cast<Bytef *>(Buffer::Data(in_buf) + in_off);
108 assert(Buffer::HasInstance(args[4]));
109 Local<Object> out_buf = args[4]->ToObject();
110 out_off = (size_t)args[5]->Uint32Value();
111 out_len = (size_t)args[6]->Uint32Value();
112 assert(out_off + out_len <= Buffer::Length(out_buf));
113 out = reinterpret_cast<Bytef *>(Buffer::Data(out_buf) + out_off);
115 WorkReqWrap *req_wrap = new WorkReqWrap();
117 req_wrap->data_ = ctx;
118 ctx->strm_.avail_in = in_len;
119 ctx->strm_.next_in = &(*in);
120 ctx->strm_.avail_out = out_len;
121 ctx->strm_.next_out = out;
124 // set this so that later on, I can easily tell how much was written.
125 ctx->chunk_size_ = out_len;
127 // build up the work request
128 uv_work_t* work_req = new uv_work_t();
129 work_req->data = req_wrap;
131 uv_queue_work(uv_default_loop(),
136 req_wrap->Dispatched();
138 return req_wrap->object_;
143 // This function may be called multiple times on the uv_work pool
144 // for a single write() call, until all of the input bytes have
147 Process(uv_work_t* work_req) {
148 WorkReqWrap *req_wrap = reinterpret_cast<WorkReqWrap *>(work_req->data);
149 ZCtx<mode> *ctx = (ZCtx<mode> *)req_wrap->data_;
151 // If the avail_out is left at 0, then it means that it ran out
152 // of room. If there was avail_out left over, then it means
153 // that all of the input was consumed.
159 err = deflate(&(ctx->strm_), ctx->flush_);
165 err = inflate(&(ctx->strm_), ctx->flush_);
170 assert(err != Z_STREAM_ERROR);
172 // now After will emit the output, and
173 // either schedule another call to Process,
174 // or shift the queue and call Process.
179 After(uv_work_t* work_req) {
181 WorkReqWrap *req_wrap = reinterpret_cast<WorkReqWrap *>(work_req->data);
182 ZCtx<mode> *ctx = (ZCtx<mode> *)req_wrap->data_;
183 Local<Integer> avail_out = Integer::New(ctx->strm_.avail_out);
184 Local<Integer> avail_in = Integer::New(ctx->strm_.avail_in);
186 // call the write() cb
187 assert(req_wrap->object_->Get(callback_sym)->IsFunction() &&
189 Local<Value> args[2] = { avail_in, avail_out };
190 MakeCallback(req_wrap->object_, "callback", 2, args);
192 // delete the ReqWrap
197 New(const Arguments& args) {
199 ZCtx<mode> *ctx = new ZCtx<mode>();
200 ctx->Wrap(args.This());
204 // just pull the ints out of the args and call the other Init
206 Init(const Arguments& args) {
209 assert(args.Length() == 4 &&
210 "init(level, windowBits, memLevel, strategy)");
212 ZCtx<mode> *ctx = ObjectWrap::Unwrap< ZCtx<mode> >(args.This());
214 int windowBits = args[0]->Uint32Value();
215 assert((windowBits >= 8 && windowBits <= 15) && "invalid windowBits");
217 int level = args[1]->Uint32Value();
218 assert((level >= -1 && level <= 9) && "invalid compression level");
220 int memLevel = args[2]->Uint32Value();
221 assert((memLevel >= 1 && memLevel <= 9) && "invalid memlevel");
223 int strategy = args[3]->Uint32Value();
224 assert((strategy == Z_FILTERED ||
225 strategy == Z_HUFFMAN_ONLY ||
227 strategy == Z_FIXED ||
228 strategy == Z_DEFAULT_STRATEGY) && "invalid strategy");
230 Init(ctx, level, windowBits, memLevel, strategy);
241 ctx->windowBits_ = windowBits;
242 ctx->memLevel_ = memLevel;
243 ctx->strategy_ = strategy;
245 ctx->strm_.zalloc = Z_NULL;
246 ctx->strm_.zfree = Z_NULL;
247 ctx->strm_.opaque = Z_NULL;
249 ctx->flush_ = Z_NO_FLUSH;
251 if (mode == GZIP || mode == GUNZIP) {
252 ctx->windowBits_ += 16;
256 ctx->windowBits_ += 32;
259 if (mode == DEFLATERAW || mode == INFLATERAW) {
260 ctx->windowBits_ *= -1;
268 err = deflateInit2(&(ctx->strm_),
279 err = inflateInit2(&(ctx->strm_), ctx->windowBits_);
285 ctx->init_done_ = true;
305 #define NODE_ZLIB_CLASS(mode, name) \
307 Local<FunctionTemplate> z = FunctionTemplate::New(ZCtx<mode>::New); \
308 z->InstanceTemplate()->SetInternalFieldCount(1); \
309 NODE_SET_PROTOTYPE_METHOD(z, "write", ZCtx<mode>::Write); \
310 NODE_SET_PROTOTYPE_METHOD(z, "init", ZCtx<mode>::Init); \
311 z->SetClassName(String::NewSymbol(name)); \
312 target->Set(String::NewSymbol(name), z->GetFunction()); \
315 void InitZlib(Handle<Object> target) {
318 NODE_ZLIB_CLASS(INFLATE, "Inflate")
319 NODE_ZLIB_CLASS(DEFLATE, "Deflate")
320 NODE_ZLIB_CLASS(INFLATERAW, "InflateRaw")
321 NODE_ZLIB_CLASS(DEFLATERAW, "DeflateRaw")
322 NODE_ZLIB_CLASS(GZIP, "Gzip")
323 NODE_ZLIB_CLASS(GUNZIP, "Gunzip")
324 NODE_ZLIB_CLASS(UNZIP, "Unzip")
326 callback_sym = NODE_PSYMBOL("callback");
328 NODE_DEFINE_CONSTANT(target, Z_NO_FLUSH);
329 NODE_DEFINE_CONSTANT(target, Z_PARTIAL_FLUSH);
330 NODE_DEFINE_CONSTANT(target, Z_SYNC_FLUSH);
331 NODE_DEFINE_CONSTANT(target, Z_FULL_FLUSH);
332 NODE_DEFINE_CONSTANT(target, Z_FINISH);
333 NODE_DEFINE_CONSTANT(target, Z_BLOCK);
334 NODE_DEFINE_CONSTANT(target, Z_OK);
335 NODE_DEFINE_CONSTANT(target, Z_STREAM_END);
336 NODE_DEFINE_CONSTANT(target, Z_NEED_DICT);
337 NODE_DEFINE_CONSTANT(target, Z_ERRNO);
338 NODE_DEFINE_CONSTANT(target, Z_STREAM_ERROR);
339 NODE_DEFINE_CONSTANT(target, Z_DATA_ERROR);
340 NODE_DEFINE_CONSTANT(target, Z_MEM_ERROR);
341 NODE_DEFINE_CONSTANT(target, Z_BUF_ERROR);
342 NODE_DEFINE_CONSTANT(target, Z_VERSION_ERROR);
343 NODE_DEFINE_CONSTANT(target, Z_NO_COMPRESSION);
344 NODE_DEFINE_CONSTANT(target, Z_BEST_SPEED);
345 NODE_DEFINE_CONSTANT(target, Z_BEST_COMPRESSION);
346 NODE_DEFINE_CONSTANT(target, Z_DEFAULT_COMPRESSION);
347 NODE_DEFINE_CONSTANT(target, Z_FILTERED);
348 NODE_DEFINE_CONSTANT(target, Z_HUFFMAN_ONLY);
349 NODE_DEFINE_CONSTANT(target, Z_RLE);
350 NODE_DEFINE_CONSTANT(target, Z_FIXED);
351 NODE_DEFINE_CONSTANT(target, Z_DEFAULT_STRATEGY);
352 NODE_DEFINE_CONSTANT(target, ZLIB_VERNUM);
354 target->Set(String::NewSymbol("ZLIB_VERSION"), String::New(ZLIB_VERSION));
359 NODE_MODULE(node_zlib, node::InitZlib);