2 #include "node_buffer.h"
4 #include "async-wrap.h"
5 #include "async-wrap-inl.h"
17 #include <sys/types.h>
23 using v8::FunctionCallbackInfo;
24 using v8::FunctionTemplate;
25 using v8::HandleScope;
45 void InitZlib(v8::Local<v8::Object> target);
51 class ZCtx : public AsyncWrap {
54 ZCtx(Environment* env, Local<Object> wrap, node_zlib_mode mode)
55 : AsyncWrap(env, wrap, AsyncWrap::PROVIDER_ZLIB),
67 write_in_progress_(false),
68 pending_close_(false),
75 CHECK_EQ(false, write_in_progress_ && "write in progress");
80 if (write_in_progress_) {
81 pending_close_ = true;
85 pending_close_ = false;
86 CHECK(init_done_ && "close before init");
87 CHECK_LE(mode_, UNZIP);
89 if (mode_ == DEFLATE || mode_ == GZIP || mode_ == DEFLATERAW) {
90 (void)deflateEnd(&strm_);
91 int64_t change_in_bytes = -static_cast<int64_t>(kDeflateContextSize);
92 env()->isolate()->AdjustAmountOfExternalAllocatedMemory(change_in_bytes);
93 } else if (mode_ == INFLATE || mode_ == GUNZIP || mode_ == INFLATERAW ||
95 (void)inflateEnd(&strm_);
96 int64_t change_in_bytes = -static_cast<int64_t>(kInflateContextSize);
97 env()->isolate()->AdjustAmountOfExternalAllocatedMemory(change_in_bytes);
101 if (dictionary_ != nullptr) {
102 delete[] dictionary_;
103 dictionary_ = nullptr;
108 static void Close(const FunctionCallbackInfo<Value>& args) {
109 ZCtx* ctx = Unwrap<ZCtx>(args.Holder());
114 // write(flush, in, in_off, in_len, out, out_off, out_len)
115 template <bool async>
116 static void Write(const FunctionCallbackInfo<Value>& args) {
117 CHECK_EQ(args.Length(), 7);
119 ZCtx* ctx = Unwrap<ZCtx>(args.Holder());
120 CHECK(ctx->init_done_ && "write before init");
121 CHECK(ctx->mode_ != NONE && "already finalized");
123 CHECK_EQ(false, ctx->write_in_progress_ && "write already in progress");
124 CHECK_EQ(false, ctx->pending_close_ && "close is pending");
125 ctx->write_in_progress_ = true;
128 CHECK_EQ(false, args[0]->IsUndefined() && "must provide flush value");
130 unsigned int flush = args[0]->Uint32Value();
132 if (flush != Z_NO_FLUSH &&
133 flush != Z_PARTIAL_FLUSH &&
134 flush != Z_SYNC_FLUSH &&
135 flush != Z_FULL_FLUSH &&
138 CHECK(0 && "Invalid flush value");
143 size_t in_off, in_len, out_off, out_len;
144 Environment* env = ctx->env();
146 if (args[1]->IsNull()) {
148 Bytef nada[1] = { 0 };
153 CHECK(Buffer::HasInstance(args[1]));
154 Local<Object> in_buf;
155 in_buf = args[1]->ToObject(env->isolate());
156 in_off = args[2]->Uint32Value();
157 in_len = args[3]->Uint32Value();
159 CHECK(Buffer::IsWithinBounds(in_off, in_len, Buffer::Length(in_buf)));
160 in = reinterpret_cast<Bytef *>(Buffer::Data(in_buf) + in_off);
163 CHECK(Buffer::HasInstance(args[4]));
164 Local<Object> out_buf = args[4]->ToObject(env->isolate());
165 out_off = args[5]->Uint32Value();
166 out_len = args[6]->Uint32Value();
167 CHECK(Buffer::IsWithinBounds(out_off, out_len, Buffer::Length(out_buf)));
168 out = reinterpret_cast<Bytef *>(Buffer::Data(out_buf) + out_off);
170 // build up the work request
171 uv_work_t* work_req = &(ctx->work_req_);
173 ctx->strm_.avail_in = in_len;
174 ctx->strm_.next_in = in;
175 ctx->strm_.avail_out = out_len;
176 ctx->strm_.next_out = out;
179 // set this so that later on, I can easily tell how much was written.
180 ctx->chunk_size_ = out_len;
184 ctx->env()->PrintSyncTrace();
187 AfterSync(ctx, args);
192 uv_queue_work(ctx->env()->event_loop(),
197 args.GetReturnValue().Set(ctx->object());
201 static void AfterSync(ZCtx* ctx, const FunctionCallbackInfo<Value>& args) {
202 Environment* env = ctx->env();
203 Local<Integer> avail_out = Integer::New(env->isolate(),
204 ctx->strm_.avail_out);
205 Local<Integer> avail_in = Integer::New(env->isolate(),
206 ctx->strm_.avail_in);
208 ctx->write_in_progress_ = false;
210 Local<Array> result = Array::New(env->isolate(), 2);
211 result->Set(0, avail_in);
212 result->Set(1, avail_out);
213 args.GetReturnValue().Set(result);
220 // This function may be called multiple times on the uv_work pool
221 // for a single write() call, until all of the input bytes have
223 static void Process(uv_work_t* work_req) {
224 ZCtx *ctx = ContainerOf(&ZCtx::work_req_, work_req);
226 // If the avail_out is left at 0, then it means that it ran out
227 // of room. If there was avail_out left over, then it means
228 // that all of the input was consumed.
229 switch (ctx->mode_) {
233 ctx->err_ = deflate(&ctx->strm_, ctx->flush_);
239 ctx->err_ = inflate(&ctx->strm_, ctx->flush_);
241 // If data was encoded with dictionary
242 if (ctx->err_ == Z_NEED_DICT && ctx->dictionary_ != nullptr) {
244 ctx->err_ = inflateSetDictionary(&ctx->strm_,
246 ctx->dictionary_len_);
247 if (ctx->err_ == Z_OK) {
248 // And try to decode again
249 ctx->err_ = inflate(&ctx->strm_, ctx->flush_);
250 } else if (ctx->err_ == Z_DATA_ERROR) {
251 // Both inflateSetDictionary() and inflate() return Z_DATA_ERROR.
252 // Make it possible for After() to tell a bad dictionary from bad
254 ctx->err_ = Z_NEED_DICT;
262 // pass any errors back to the main thread to deal with.
264 // now After will emit the output, and
265 // either schedule another call to Process,
266 // or shift the queue and call Process.
270 static bool CheckError(ZCtx* ctx) {
271 // Acceptable error states depend on the type of zlib stream.
276 // normal statuses, not fatal
279 if (ctx->dictionary_ == nullptr)
280 ZCtx::Error(ctx, "Missing dictionary");
282 ZCtx::Error(ctx, "Bad dictionary");
286 ZCtx::Error(ctx, "Zlib error");
295 static void After(uv_work_t* work_req, int status) {
298 ZCtx* ctx = ContainerOf(&ZCtx::work_req_, work_req);
299 Environment* env = ctx->env();
301 HandleScope handle_scope(env->isolate());
302 Context::Scope context_scope(env->context());
304 if (!CheckError(ctx))
307 Local<Integer> avail_out = Integer::New(env->isolate(),
308 ctx->strm_.avail_out);
309 Local<Integer> avail_in = Integer::New(env->isolate(),
310 ctx->strm_.avail_in);
312 ctx->write_in_progress_ = false;
314 // call the write() cb
315 Local<Value> args[2] = { avail_in, avail_out };
316 ctx->MakeCallback(env->callback_string(), ARRAY_SIZE(args), args);
319 if (ctx->pending_close_)
323 static void Error(ZCtx* ctx, const char* message) {
324 Environment* env = ctx->env();
326 // If you hit this assertion, you forgot to enter the v8::Context first.
327 CHECK_EQ(env->context(), env->isolate()->GetCurrentContext());
329 if (ctx->strm_.msg != nullptr) {
330 message = ctx->strm_.msg;
333 HandleScope scope(env->isolate());
334 Local<Value> args[2] = {
335 OneByteString(env->isolate(), message),
336 Number::New(env->isolate(), ctx->err_)
338 ctx->MakeCallback(env->onerror_string(), ARRAY_SIZE(args), args);
340 // no hope of rescue.
341 if (ctx->write_in_progress_)
343 ctx->write_in_progress_ = false;
344 if (ctx->pending_close_)
348 static void New(const FunctionCallbackInfo<Value>& args) {
349 Environment* env = Environment::GetCurrent(args);
351 if (args.Length() < 1 || !args[0]->IsInt32()) {
352 return env->ThrowTypeError("Bad argument");
354 node_zlib_mode mode = static_cast<node_zlib_mode>(args[0]->Int32Value());
356 if (mode < DEFLATE || mode > UNZIP) {
357 return env->ThrowTypeError("Bad argument");
360 new ZCtx(env, args.This(), mode);
363 // just pull the ints out of the args and call the other Init
364 static void Init(const FunctionCallbackInfo<Value>& args) {
365 CHECK((args.Length() == 4 || args.Length() == 5) &&
366 "init(windowBits, level, memLevel, strategy, [dictionary])");
368 ZCtx* ctx = Unwrap<ZCtx>(args.Holder());
370 int windowBits = args[0]->Uint32Value();
371 CHECK((windowBits >= 8 && windowBits <= 15) && "invalid windowBits");
373 int level = args[1]->Int32Value();
374 CHECK((level >= -1 && level <= 9) && "invalid compression level");
376 int memLevel = args[2]->Uint32Value();
377 CHECK((memLevel >= 1 && memLevel <= 9) && "invalid memlevel");
379 int strategy = args[3]->Uint32Value();
380 CHECK((strategy == Z_FILTERED ||
381 strategy == Z_HUFFMAN_ONLY ||
383 strategy == Z_FIXED ||
384 strategy == Z_DEFAULT_STRATEGY) && "invalid strategy");
386 char* dictionary = nullptr;
387 size_t dictionary_len = 0;
388 if (args.Length() >= 5 && Buffer::HasInstance(args[4])) {
389 Local<Object> dictionary_ = args[4]->ToObject(args.GetIsolate());
391 dictionary_len = Buffer::Length(dictionary_);
392 dictionary = new char[dictionary_len];
394 memcpy(dictionary, Buffer::Data(dictionary_), dictionary_len);
397 Init(ctx, level, windowBits, memLevel, strategy,
398 dictionary, dictionary_len);
402 static void Params(const FunctionCallbackInfo<Value>& args) {
403 CHECK(args.Length() == 2 && "params(level, strategy)");
404 ZCtx* ctx = Unwrap<ZCtx>(args.Holder());
405 Params(ctx, args[0]->Int32Value(), args[1]->Int32Value());
408 static void Reset(const FunctionCallbackInfo<Value> &args) {
409 ZCtx* ctx = Unwrap<ZCtx>(args.Holder());
414 static void Init(ZCtx *ctx, int level, int windowBits, int memLevel,
415 int strategy, char* dictionary, size_t dictionary_len) {
417 ctx->windowBits_ = windowBits;
418 ctx->memLevel_ = memLevel;
419 ctx->strategy_ = strategy;
421 ctx->strm_.zalloc = Z_NULL;
422 ctx->strm_.zfree = Z_NULL;
423 ctx->strm_.opaque = Z_NULL;
425 ctx->flush_ = Z_NO_FLUSH;
429 if (ctx->mode_ == GZIP || ctx->mode_ == GUNZIP) {
430 ctx->windowBits_ += 16;
433 if (ctx->mode_ == UNZIP) {
434 ctx->windowBits_ += 32;
437 if (ctx->mode_ == DEFLATERAW || ctx->mode_ == INFLATERAW) {
438 ctx->windowBits_ *= -1;
441 switch (ctx->mode_) {
445 ctx->err_ = deflateInit2(&ctx->strm_,
451 ctx->env()->isolate()
452 ->AdjustAmountOfExternalAllocatedMemory(kDeflateContextSize);
458 ctx->err_ = inflateInit2(&ctx->strm_, ctx->windowBits_);
459 ctx->env()->isolate()
460 ->AdjustAmountOfExternalAllocatedMemory(kInflateContextSize);
466 if (ctx->err_ != Z_OK) {
467 ZCtx::Error(ctx, "Init error");
471 ctx->dictionary_ = reinterpret_cast<Bytef *>(dictionary);
472 ctx->dictionary_len_ = dictionary_len;
474 ctx->write_in_progress_ = false;
475 ctx->init_done_ = true;
478 static void SetDictionary(ZCtx* ctx) {
479 if (ctx->dictionary_ == nullptr)
484 switch (ctx->mode_) {
487 ctx->err_ = deflateSetDictionary(&ctx->strm_,
489 ctx->dictionary_len_);
495 if (ctx->err_ != Z_OK) {
496 ZCtx::Error(ctx, "Failed to set dictionary");
500 static void Params(ZCtx* ctx, int level, int strategy) {
503 switch (ctx->mode_) {
506 ctx->err_ = deflateParams(&ctx->strm_, level, strategy);
512 if (ctx->err_ != Z_OK && ctx->err_ != Z_BUF_ERROR) {
513 ZCtx::Error(ctx, "Failed to set parameters");
517 static void Reset(ZCtx* ctx) {
520 switch (ctx->mode_) {
523 ctx->err_ = deflateReset(&ctx->strm_);
527 ctx->err_ = inflateReset(&ctx->strm_);
533 if (ctx->err_ != Z_OK) {
534 ZCtx::Error(ctx, "Failed to reset stream");
538 size_t self_size() const override { return sizeof(*this); }
550 MakeWeak<ZCtx>(this);
554 static const int kDeflateContextSize = 16384; // approximate
555 static const int kInflateContextSize = 10240; // approximate
559 size_t dictionary_len_;
565 node_zlib_mode mode_;
570 bool write_in_progress_;
576 void InitZlib(Local<Object> target,
578 Local<Context> context,
580 Environment* env = Environment::GetCurrent(context);
581 Local<FunctionTemplate> z = env->NewFunctionTemplate(ZCtx::New);
583 z->InstanceTemplate()->SetInternalFieldCount(1);
585 env->SetProtoMethod(z, "write", ZCtx::Write<true>);
586 env->SetProtoMethod(z, "writeSync", ZCtx::Write<false>);
587 env->SetProtoMethod(z, "init", ZCtx::Init);
588 env->SetProtoMethod(z, "close", ZCtx::Close);
589 env->SetProtoMethod(z, "params", ZCtx::Params);
590 env->SetProtoMethod(z, "reset", ZCtx::Reset);
592 z->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Zlib"));
593 target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "Zlib"), z->GetFunction());
595 // valid flush values.
596 NODE_DEFINE_CONSTANT(target, Z_NO_FLUSH);
597 NODE_DEFINE_CONSTANT(target, Z_PARTIAL_FLUSH);
598 NODE_DEFINE_CONSTANT(target, Z_SYNC_FLUSH);
599 NODE_DEFINE_CONSTANT(target, Z_FULL_FLUSH);
600 NODE_DEFINE_CONSTANT(target, Z_FINISH);
601 NODE_DEFINE_CONSTANT(target, Z_BLOCK);
603 // return/error codes
604 NODE_DEFINE_CONSTANT(target, Z_OK);
605 NODE_DEFINE_CONSTANT(target, Z_STREAM_END);
606 NODE_DEFINE_CONSTANT(target, Z_NEED_DICT);
607 NODE_DEFINE_CONSTANT(target, Z_ERRNO);
608 NODE_DEFINE_CONSTANT(target, Z_STREAM_ERROR);
609 NODE_DEFINE_CONSTANT(target, Z_DATA_ERROR);
610 NODE_DEFINE_CONSTANT(target, Z_MEM_ERROR);
611 NODE_DEFINE_CONSTANT(target, Z_BUF_ERROR);
612 NODE_DEFINE_CONSTANT(target, Z_VERSION_ERROR);
614 NODE_DEFINE_CONSTANT(target, Z_NO_COMPRESSION);
615 NODE_DEFINE_CONSTANT(target, Z_BEST_SPEED);
616 NODE_DEFINE_CONSTANT(target, Z_BEST_COMPRESSION);
617 NODE_DEFINE_CONSTANT(target, Z_DEFAULT_COMPRESSION);
618 NODE_DEFINE_CONSTANT(target, Z_FILTERED);
619 NODE_DEFINE_CONSTANT(target, Z_HUFFMAN_ONLY);
620 NODE_DEFINE_CONSTANT(target, Z_RLE);
621 NODE_DEFINE_CONSTANT(target, Z_FIXED);
622 NODE_DEFINE_CONSTANT(target, Z_DEFAULT_STRATEGY);
623 NODE_DEFINE_CONSTANT(target, ZLIB_VERNUM);
625 NODE_DEFINE_CONSTANT(target, DEFLATE);
626 NODE_DEFINE_CONSTANT(target, INFLATE);
627 NODE_DEFINE_CONSTANT(target, GZIP);
628 NODE_DEFINE_CONSTANT(target, GUNZIP);
629 NODE_DEFINE_CONSTANT(target, DEFLATERAW);
630 NODE_DEFINE_CONSTANT(target, INFLATERAW);
631 NODE_DEFINE_CONSTANT(target, UNZIP);
633 target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "ZLIB_VERSION"),
634 FIXED_ONE_BYTE_STRING(env->isolate(), ZLIB_VERSION));
639 NODE_MODULE_CONTEXT_AWARE_BUILTIN(zlib, node::InitZlib)