src: replace naive search in Buffer::IndexOf
[platform/upstream/nodejs.git] / src / node_zlib.cc
1 #include "node.h"
2 #include "node_buffer.h"
3
4 #include "async-wrap.h"
5 #include "async-wrap-inl.h"
6 #include "env.h"
7 #include "env-inl.h"
8 #include "util.h"
9 #include "util-inl.h"
10
11 #include "v8.h"
12 #include "zlib.h"
13
14 #include <errno.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <sys/types.h>
18
19 namespace node {
20
21 using v8::Array;
22 using v8::Context;
23 using v8::FunctionCallbackInfo;
24 using v8::FunctionTemplate;
25 using v8::HandleScope;
26 using v8::Integer;
27 using v8::Local;
28 using v8::Number;
29 using v8::Object;
30 using v8::String;
31 using v8::Value;
32
33 enum node_zlib_mode {
34   NONE,
35   DEFLATE,
36   INFLATE,
37   GZIP,
38   GUNZIP,
39   DEFLATERAW,
40   INFLATERAW,
41   UNZIP
42 };
43
44
45 void InitZlib(v8::Local<v8::Object> target);
46
47
48 /**
49  * Deflate/Inflate
50  */
51 class ZCtx : public AsyncWrap {
52  public:
53
54   ZCtx(Environment* env, Local<Object> wrap, node_zlib_mode mode)
55       : AsyncWrap(env, wrap, AsyncWrap::PROVIDER_ZLIB),
56         chunk_size_(0),
57         dictionary_(nullptr),
58         dictionary_len_(0),
59         err_(0),
60         flush_(0),
61         init_done_(false),
62         level_(0),
63         memLevel_(0),
64         mode_(mode),
65         strategy_(0),
66         windowBits_(0),
67         write_in_progress_(false),
68         pending_close_(false),
69         refs_(0) {
70     MakeWeak<ZCtx>(this);
71   }
72
73
74   ~ZCtx() override {
75     CHECK_EQ(false, write_in_progress_ && "write in progress");
76     Close();
77   }
78
79   void Close() {
80     if (write_in_progress_) {
81       pending_close_ = true;
82       return;
83     }
84
85     pending_close_ = false;
86     CHECK(init_done_ && "close before init");
87     CHECK_LE(mode_, UNZIP);
88
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 ||
94                mode_ == UNZIP) {
95       (void)inflateEnd(&strm_);
96       int64_t change_in_bytes = -static_cast<int64_t>(kInflateContextSize);
97       env()->isolate()->AdjustAmountOfExternalAllocatedMemory(change_in_bytes);
98     }
99     mode_ = NONE;
100
101     if (dictionary_ != nullptr) {
102       delete[] dictionary_;
103       dictionary_ = nullptr;
104     }
105   }
106
107
108   static void Close(const FunctionCallbackInfo<Value>& args) {
109     ZCtx* ctx = Unwrap<ZCtx>(args.Holder());
110     ctx->Close();
111   }
112
113
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);
118
119     ZCtx* ctx = Unwrap<ZCtx>(args.Holder());
120     CHECK(ctx->init_done_ && "write before init");
121     CHECK(ctx->mode_ != NONE && "already finalized");
122
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;
126     ctx->Ref();
127
128     CHECK_EQ(false, args[0]->IsUndefined() && "must provide flush value");
129
130     unsigned int flush = args[0]->Uint32Value();
131
132     if (flush != Z_NO_FLUSH &&
133         flush != Z_PARTIAL_FLUSH &&
134         flush != Z_SYNC_FLUSH &&
135         flush != Z_FULL_FLUSH &&
136         flush != Z_FINISH &&
137         flush != Z_BLOCK) {
138       CHECK(0 && "Invalid flush value");
139     }
140
141     Bytef *in;
142     Bytef *out;
143     size_t in_off, in_len, out_off, out_len;
144     Environment* env = ctx->env();
145
146     if (args[1]->IsNull()) {
147       // just a flush
148       Bytef nada[1] = { 0 };
149       in = nada;
150       in_len = 0;
151       in_off = 0;
152     } else {
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();
158
159       CHECK(Buffer::IsWithinBounds(in_off, in_len, Buffer::Length(in_buf)));
160       in = reinterpret_cast<Bytef *>(Buffer::Data(in_buf) + in_off);
161     }
162
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);
169
170     // build up the work request
171     uv_work_t* work_req = &(ctx->work_req_);
172
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;
177     ctx->flush_ = flush;
178
179     // set this so that later on, I can easily tell how much was written.
180     ctx->chunk_size_ = out_len;
181
182     if (!async) {
183       // sync version
184       ctx->env()->PrintSyncTrace();
185       Process(work_req);
186       if (CheckError(ctx))
187         AfterSync(ctx, args);
188       return;
189     }
190
191     // async version
192     uv_queue_work(ctx->env()->event_loop(),
193                   work_req,
194                   ZCtx::Process,
195                   ZCtx::After);
196
197     args.GetReturnValue().Set(ctx->object());
198   }
199
200
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);
207
208     ctx->write_in_progress_ = false;
209
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);
214
215     ctx->Unref();
216   }
217
218
219   // thread pool!
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
222   // been consumed.
223   static void Process(uv_work_t* work_req) {
224     ZCtx *ctx = ContainerOf(&ZCtx::work_req_, work_req);
225
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_) {
230       case DEFLATE:
231       case GZIP:
232       case DEFLATERAW:
233         ctx->err_ = deflate(&ctx->strm_, ctx->flush_);
234         break;
235       case UNZIP:
236       case INFLATE:
237       case GUNZIP:
238       case INFLATERAW:
239         ctx->err_ = inflate(&ctx->strm_, ctx->flush_);
240
241         // If data was encoded with dictionary
242         if (ctx->err_ == Z_NEED_DICT && ctx->dictionary_ != nullptr) {
243           // Load it
244           ctx->err_ = inflateSetDictionary(&ctx->strm_,
245                                            ctx->dictionary_,
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
253             // input.
254             ctx->err_ = Z_NEED_DICT;
255           }
256         }
257         break;
258       default:
259         CHECK(0 && "wtf?");
260     }
261
262     // pass any errors back to the main thread to deal with.
263
264     // now After will emit the output, and
265     // either schedule another call to Process,
266     // or shift the queue and call Process.
267   }
268
269
270   static bool CheckError(ZCtx* ctx) {
271     // Acceptable error states depend on the type of zlib stream.
272     switch (ctx->err_) {
273     case Z_OK:
274     case Z_STREAM_END:
275     case Z_BUF_ERROR:
276       // normal statuses, not fatal
277       break;
278     case Z_NEED_DICT:
279       if (ctx->dictionary_ == nullptr)
280         ZCtx::Error(ctx, "Missing dictionary");
281       else
282         ZCtx::Error(ctx, "Bad dictionary");
283       return false;
284     default:
285       // something else.
286       ZCtx::Error(ctx, "Zlib error");
287       return false;
288     }
289
290     return true;
291   }
292
293
294   // v8 land!
295   static void After(uv_work_t* work_req, int status) {
296     CHECK_EQ(status, 0);
297
298     ZCtx* ctx = ContainerOf(&ZCtx::work_req_, work_req);
299     Environment* env = ctx->env();
300
301     HandleScope handle_scope(env->isolate());
302     Context::Scope context_scope(env->context());
303
304     if (!CheckError(ctx))
305       return;
306
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);
311
312     ctx->write_in_progress_ = false;
313
314     // call the write() cb
315     Local<Value> args[2] = { avail_in, avail_out };
316     ctx->MakeCallback(env->callback_string(), ARRAY_SIZE(args), args);
317
318     ctx->Unref();
319     if (ctx->pending_close_)
320       ctx->Close();
321   }
322
323   static void Error(ZCtx* ctx, const char* message) {
324     Environment* env = ctx->env();
325
326     // If you hit this assertion, you forgot to enter the v8::Context first.
327     CHECK_EQ(env->context(), env->isolate()->GetCurrentContext());
328
329     if (ctx->strm_.msg != nullptr) {
330       message = ctx->strm_.msg;
331     }
332
333     HandleScope scope(env->isolate());
334     Local<Value> args[2] = {
335       OneByteString(env->isolate(), message),
336       Number::New(env->isolate(), ctx->err_)
337     };
338     ctx->MakeCallback(env->onerror_string(), ARRAY_SIZE(args), args);
339
340     // no hope of rescue.
341     if (ctx->write_in_progress_)
342       ctx->Unref();
343     ctx->write_in_progress_ = false;
344     if (ctx->pending_close_)
345       ctx->Close();
346   }
347
348   static void New(const FunctionCallbackInfo<Value>& args) {
349     Environment* env = Environment::GetCurrent(args);
350
351     if (args.Length() < 1 || !args[0]->IsInt32()) {
352       return env->ThrowTypeError("Bad argument");
353     }
354     node_zlib_mode mode = static_cast<node_zlib_mode>(args[0]->Int32Value());
355
356     if (mode < DEFLATE || mode > UNZIP) {
357       return env->ThrowTypeError("Bad argument");
358     }
359
360     new ZCtx(env, args.This(), mode);
361   }
362
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])");
367
368     ZCtx* ctx = Unwrap<ZCtx>(args.Holder());
369
370     int windowBits = args[0]->Uint32Value();
371     CHECK((windowBits >= 8 && windowBits <= 15) && "invalid windowBits");
372
373     int level = args[1]->Int32Value();
374     CHECK((level >= -1 && level <= 9) && "invalid compression level");
375
376     int memLevel = args[2]->Uint32Value();
377     CHECK((memLevel >= 1 && memLevel <= 9) && "invalid memlevel");
378
379     int strategy = args[3]->Uint32Value();
380     CHECK((strategy == Z_FILTERED ||
381             strategy == Z_HUFFMAN_ONLY ||
382             strategy == Z_RLE ||
383             strategy == Z_FIXED ||
384             strategy == Z_DEFAULT_STRATEGY) && "invalid strategy");
385
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());
390
391       dictionary_len = Buffer::Length(dictionary_);
392       dictionary = new char[dictionary_len];
393
394       memcpy(dictionary, Buffer::Data(dictionary_), dictionary_len);
395     }
396
397     Init(ctx, level, windowBits, memLevel, strategy,
398          dictionary, dictionary_len);
399     SetDictionary(ctx);
400   }
401
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());
406   }
407
408   static void Reset(const FunctionCallbackInfo<Value> &args) {
409     ZCtx* ctx = Unwrap<ZCtx>(args.Holder());
410     Reset(ctx);
411     SetDictionary(ctx);
412   }
413
414   static void Init(ZCtx *ctx, int level, int windowBits, int memLevel,
415                    int strategy, char* dictionary, size_t dictionary_len) {
416     ctx->level_ = level;
417     ctx->windowBits_ = windowBits;
418     ctx->memLevel_ = memLevel;
419     ctx->strategy_ = strategy;
420
421     ctx->strm_.zalloc = Z_NULL;
422     ctx->strm_.zfree = Z_NULL;
423     ctx->strm_.opaque = Z_NULL;
424
425     ctx->flush_ = Z_NO_FLUSH;
426
427     ctx->err_ = Z_OK;
428
429     if (ctx->mode_ == GZIP || ctx->mode_ == GUNZIP) {
430       ctx->windowBits_ += 16;
431     }
432
433     if (ctx->mode_ == UNZIP) {
434       ctx->windowBits_ += 32;
435     }
436
437     if (ctx->mode_ == DEFLATERAW || ctx->mode_ == INFLATERAW) {
438       ctx->windowBits_ *= -1;
439     }
440
441     switch (ctx->mode_) {
442       case DEFLATE:
443       case GZIP:
444       case DEFLATERAW:
445         ctx->err_ = deflateInit2(&ctx->strm_,
446                                  ctx->level_,
447                                  Z_DEFLATED,
448                                  ctx->windowBits_,
449                                  ctx->memLevel_,
450                                  ctx->strategy_);
451         ctx->env()->isolate()
452             ->AdjustAmountOfExternalAllocatedMemory(kDeflateContextSize);
453         break;
454       case INFLATE:
455       case GUNZIP:
456       case INFLATERAW:
457       case UNZIP:
458         ctx->err_ = inflateInit2(&ctx->strm_, ctx->windowBits_);
459         ctx->env()->isolate()
460             ->AdjustAmountOfExternalAllocatedMemory(kInflateContextSize);
461         break;
462       default:
463         CHECK(0 && "wtf?");
464     }
465
466     if (ctx->err_ != Z_OK) {
467       ZCtx::Error(ctx, "Init error");
468     }
469
470
471     ctx->dictionary_ = reinterpret_cast<Bytef *>(dictionary);
472     ctx->dictionary_len_ = dictionary_len;
473
474     ctx->write_in_progress_ = false;
475     ctx->init_done_ = true;
476   }
477
478   static void SetDictionary(ZCtx* ctx) {
479     if (ctx->dictionary_ == nullptr)
480       return;
481
482     ctx->err_ = Z_OK;
483
484     switch (ctx->mode_) {
485       case DEFLATE:
486       case DEFLATERAW:
487         ctx->err_ = deflateSetDictionary(&ctx->strm_,
488                                          ctx->dictionary_,
489                                          ctx->dictionary_len_);
490         break;
491       default:
492         break;
493     }
494
495     if (ctx->err_ != Z_OK) {
496       ZCtx::Error(ctx, "Failed to set dictionary");
497     }
498   }
499
500   static void Params(ZCtx* ctx, int level, int strategy) {
501     ctx->err_ = Z_OK;
502
503     switch (ctx->mode_) {
504       case DEFLATE:
505       case DEFLATERAW:
506         ctx->err_ = deflateParams(&ctx->strm_, level, strategy);
507         break;
508       default:
509         break;
510     }
511
512     if (ctx->err_ != Z_OK && ctx->err_ != Z_BUF_ERROR) {
513       ZCtx::Error(ctx, "Failed to set parameters");
514     }
515   }
516
517   static void Reset(ZCtx* ctx) {
518     ctx->err_ = Z_OK;
519
520     switch (ctx->mode_) {
521       case DEFLATE:
522       case DEFLATERAW:
523         ctx->err_ = deflateReset(&ctx->strm_);
524         break;
525       case INFLATE:
526       case INFLATERAW:
527         ctx->err_ = inflateReset(&ctx->strm_);
528         break;
529       default:
530         break;
531     }
532
533     if (ctx->err_ != Z_OK) {
534       ZCtx::Error(ctx, "Failed to reset stream");
535     }
536   }
537
538   size_t self_size() const override { return sizeof(*this); }
539
540  private:
541   void Ref() {
542     if (++refs_ == 1) {
543       ClearWeak();
544     }
545   }
546
547   void Unref() {
548     CHECK_GT(refs_, 0);
549     if (--refs_ == 0) {
550       MakeWeak<ZCtx>(this);
551     }
552   }
553
554   static const int kDeflateContextSize = 16384;  // approximate
555   static const int kInflateContextSize = 10240;  // approximate
556
557   int chunk_size_;
558   Bytef* dictionary_;
559   size_t dictionary_len_;
560   int err_;
561   int flush_;
562   bool init_done_;
563   int level_;
564   int memLevel_;
565   node_zlib_mode mode_;
566   int strategy_;
567   z_stream strm_;
568   int windowBits_;
569   uv_work_t work_req_;
570   bool write_in_progress_;
571   bool pending_close_;
572   unsigned int refs_;
573 };
574
575
576 void InitZlib(Local<Object> target,
577               Local<Value> unused,
578               Local<Context> context,
579               void* priv) {
580   Environment* env = Environment::GetCurrent(context);
581   Local<FunctionTemplate> z = env->NewFunctionTemplate(ZCtx::New);
582
583   z->InstanceTemplate()->SetInternalFieldCount(1);
584
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);
591
592   z->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Zlib"));
593   target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "Zlib"), z->GetFunction());
594
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);
602
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);
613
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);
624
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);
632
633   target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "ZLIB_VERSION"),
634               FIXED_ONE_BYTE_STRING(env->isolate(), ZLIB_VERSION));
635 }
636
637 }  // namespace node
638
639 NODE_MODULE_CONTEXT_AWARE_BUILTIN(zlib, node::InitZlib)