// Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include #include #include #include #include #include #include namespace node { using namespace v8; // write() returns one of these, and then calls the cb() when it's done. typedef ReqWrap WorkReqWrap; static Persistent callback_sym; enum node_zlib_mode { DEFLATE = 1, INFLATE, GZIP, GUNZIP, DEFLATERAW, INFLATERAW, UNZIP }; template class ZCtx; void InitZlib(v8::Handle target); /** * Deflate/Inflate */ template class ZCtx : public ObjectWrap { public: ZCtx() : ObjectWrap() { } ~ZCtx() { if (mode == DEFLATE || mode == GZIP || mode == DEFLATERAW) { (void)deflateEnd(&strm_); } else if (mode == INFLATE || mode == GUNZIP || mode == INFLATERAW) { (void)inflateEnd(&strm_); } } // write(flush, in, in_off, in_len, out, out_off, out_len) static Handle Write(const Arguments& args) { HandleScope scope; assert(args.Length() == 7); ZCtx *ctx = ObjectWrap::Unwrap< ZCtx >(args.This()); assert(ctx->init_done_ && "write before init"); unsigned int flush = args[0]->Uint32Value(); Bytef *in; Bytef *out; size_t in_off, in_len, out_off, out_len; if (args[1]->IsNull()) { // just a flush Bytef nada[1] = { 0 }; in = nada; in_len = 0; in_off = 0; } else { assert(Buffer::HasInstance(args[1])); Local in_buf; in_buf = args[1]->ToObject(); in_off = (size_t)args[2]->Uint32Value(); in_len = (size_t)args[3]->Uint32Value(); assert(in_off + in_len <= Buffer::Length(in_buf)); in = reinterpret_cast(Buffer::Data(in_buf) + in_off); } assert(Buffer::HasInstance(args[4])); Local out_buf = args[4]->ToObject(); out_off = (size_t)args[5]->Uint32Value(); out_len = (size_t)args[6]->Uint32Value(); assert(out_off + out_len <= Buffer::Length(out_buf)); out = reinterpret_cast(Buffer::Data(out_buf) + out_off); WorkReqWrap *req_wrap = new WorkReqWrap(); req_wrap->data_ = ctx; ctx->strm_.avail_in = in_len; ctx->strm_.next_in = &(*in); ctx->strm_.avail_out = out_len; ctx->strm_.next_out = out; ctx->flush_ = flush; // set this so that later on, I can easily tell how much was written. ctx->chunk_size_ = out_len; // build up the work request uv_work_t* work_req = new uv_work_t(); work_req->data = req_wrap; uv_queue_work(uv_default_loop(), work_req, ZCtx::Process, ZCtx::After); req_wrap->Dispatched(); return req_wrap->object_; } // thread pool! // This function may be called multiple times on the uv_work pool // for a single write() call, until all of the input bytes have // been consumed. static void Process(uv_work_t* work_req) { WorkReqWrap *req_wrap = reinterpret_cast(work_req->data); ZCtx *ctx = (ZCtx *)req_wrap->data_; // If the avail_out is left at 0, then it means that it ran out // of room. If there was avail_out left over, then it means // that all of the input was consumed. int err; switch (mode) { case DEFLATE: case GZIP: case DEFLATERAW: err = deflate(&(ctx->strm_), ctx->flush_); break; case UNZIP: case INFLATE: case GUNZIP: case INFLATERAW: err = inflate(&(ctx->strm_), ctx->flush_); break; default: assert(0 && "wtf?"); } assert(err != Z_STREAM_ERROR); // now After will emit the output, and // either schedule another call to Process, // or shift the queue and call Process. } // v8 land! static void After(uv_work_t* work_req) { HandleScope scope; WorkReqWrap *req_wrap = reinterpret_cast(work_req->data); ZCtx *ctx = (ZCtx *)req_wrap->data_; Local avail_out = Integer::New(ctx->strm_.avail_out); Local avail_in = Integer::New(ctx->strm_.avail_in); // call the write() cb assert(req_wrap->object_->Get(callback_sym)->IsFunction() && "Invalid callback"); Local args[2] = { avail_in, avail_out }; MakeCallback(req_wrap->object_, "callback", 2, args); // delete the ReqWrap delete req_wrap; } static Handle New(const Arguments& args) { HandleScope scope; ZCtx *ctx = new ZCtx(); ctx->Wrap(args.This()); return args.This(); } // just pull the ints out of the args and call the other Init static Handle Init(const Arguments& args) { HandleScope scope; assert(args.Length() == 4 && "init(windowBits, level, memLevel, strategy)"); ZCtx *ctx = ObjectWrap::Unwrap< ZCtx >(args.This()); int windowBits = args[0]->Uint32Value(); assert((windowBits >= 8 && windowBits <= 15) && "invalid windowBits"); int level = args[1]->Uint32Value(); assert((level >= -1 && level <= 9) && "invalid compression level"); int memLevel = args[2]->Uint32Value(); assert((memLevel >= 1 && memLevel <= 9) && "invalid memlevel"); int strategy = args[3]->Uint32Value(); assert((strategy == Z_FILTERED || strategy == Z_HUFFMAN_ONLY || strategy == Z_RLE || strategy == Z_FIXED || strategy == Z_DEFAULT_STRATEGY) && "invalid strategy"); Init(ctx, level, windowBits, memLevel, strategy); return Undefined(); } static void Init(ZCtx *ctx, int level, int windowBits, int memLevel, int strategy) { ctx->level_ = level; ctx->windowBits_ = windowBits; ctx->memLevel_ = memLevel; ctx->strategy_ = strategy; ctx->strm_.zalloc = Z_NULL; ctx->strm_.zfree = Z_NULL; ctx->strm_.opaque = Z_NULL; ctx->flush_ = Z_NO_FLUSH; if (mode == GZIP || mode == GUNZIP) { ctx->windowBits_ += 16; } if (mode == UNZIP) { ctx->windowBits_ += 32; } if (mode == DEFLATERAW || mode == INFLATERAW) { ctx->windowBits_ *= -1; } int err; switch (mode) { case DEFLATE: case GZIP: case DEFLATERAW: err = deflateInit2(&(ctx->strm_), ctx->level_, Z_DEFLATED, ctx->windowBits_, ctx->memLevel_, ctx->strategy_); break; case INFLATE: case GUNZIP: case INFLATERAW: case UNZIP: err = inflateInit2(&(ctx->strm_), ctx->windowBits_); break; default: assert(0 && "wtf?"); } ctx->init_done_ = true; assert(err == Z_OK); } private: bool init_done_; z_stream strm_; int level_; int windowBits_; int memLevel_; int strategy_; int flush_; int chunk_size_; }; #define NODE_ZLIB_CLASS(mode, name) \ { \ Local z = FunctionTemplate::New(ZCtx::New); \ z->InstanceTemplate()->SetInternalFieldCount(1); \ NODE_SET_PROTOTYPE_METHOD(z, "write", ZCtx::Write); \ NODE_SET_PROTOTYPE_METHOD(z, "init", ZCtx::Init); \ z->SetClassName(String::NewSymbol(name)); \ target->Set(String::NewSymbol(name), z->GetFunction()); \ } void InitZlib(Handle target) { HandleScope scope; NODE_ZLIB_CLASS(INFLATE, "Inflate") NODE_ZLIB_CLASS(DEFLATE, "Deflate") NODE_ZLIB_CLASS(INFLATERAW, "InflateRaw") NODE_ZLIB_CLASS(DEFLATERAW, "DeflateRaw") NODE_ZLIB_CLASS(GZIP, "Gzip") NODE_ZLIB_CLASS(GUNZIP, "Gunzip") NODE_ZLIB_CLASS(UNZIP, "Unzip") callback_sym = NODE_PSYMBOL("callback"); NODE_DEFINE_CONSTANT(target, Z_NO_FLUSH); NODE_DEFINE_CONSTANT(target, Z_PARTIAL_FLUSH); NODE_DEFINE_CONSTANT(target, Z_SYNC_FLUSH); NODE_DEFINE_CONSTANT(target, Z_FULL_FLUSH); NODE_DEFINE_CONSTANT(target, Z_FINISH); NODE_DEFINE_CONSTANT(target, Z_BLOCK); NODE_DEFINE_CONSTANT(target, Z_OK); NODE_DEFINE_CONSTANT(target, Z_STREAM_END); NODE_DEFINE_CONSTANT(target, Z_NEED_DICT); NODE_DEFINE_CONSTANT(target, Z_ERRNO); NODE_DEFINE_CONSTANT(target, Z_STREAM_ERROR); NODE_DEFINE_CONSTANT(target, Z_DATA_ERROR); NODE_DEFINE_CONSTANT(target, Z_MEM_ERROR); NODE_DEFINE_CONSTANT(target, Z_BUF_ERROR); NODE_DEFINE_CONSTANT(target, Z_VERSION_ERROR); NODE_DEFINE_CONSTANT(target, Z_NO_COMPRESSION); NODE_DEFINE_CONSTANT(target, Z_BEST_SPEED); NODE_DEFINE_CONSTANT(target, Z_BEST_COMPRESSION); NODE_DEFINE_CONSTANT(target, Z_DEFAULT_COMPRESSION); NODE_DEFINE_CONSTANT(target, Z_FILTERED); NODE_DEFINE_CONSTANT(target, Z_HUFFMAN_ONLY); NODE_DEFINE_CONSTANT(target, Z_RLE); NODE_DEFINE_CONSTANT(target, Z_FIXED); NODE_DEFINE_CONSTANT(target, Z_DEFAULT_STRATEGY); NODE_DEFINE_CONSTANT(target, ZLIB_VERNUM); target->Set(String::NewSymbol("ZLIB_VERSION"), String::New(ZLIB_VERSION)); } } // namespace node NODE_MODULE(node_zlib, node::InitZlib)