zlib: Fix test so that it's not trivially passing, then pass it.
[platform/upstream/nodejs.git] / src / node_zlib.cc
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() {
67   }
68
69   ~ZCtx() {
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_);
74     }
75   }
76
77   // write(flush, in, in_off, in_len, out, out_off, out_len)
78   static Handle<Value>
79   Write(const Arguments& args) {
80     HandleScope scope;
81     assert(args.Length() == 7);
82
83     ZCtx<mode> *ctx = ObjectWrap::Unwrap< ZCtx<mode> >(args.This());
84     assert(ctx->init_done_ && "write before init");
85
86     unsigned int flush = args[0]->Uint32Value();
87     Bytef *in;
88     Bytef *out;
89     size_t in_off, in_len, out_off, out_len;
90
91     if (args[1]->IsNull()) {
92       // just a flush
93       Bytef nada[1] = { 0 };
94       in = nada;
95       in_len = 0;
96       in_off = 0;
97     } else {
98       assert(Buffer::HasInstance(args[1]));
99       Local<Object> in_buf;
100       in_buf = args[1]->ToObject();
101       in_off = (size_t)args[2]->Uint32Value();
102       in_len = (size_t)args[3]->Uint32Value();
103
104       assert(in_off + in_len <= Buffer::Length(in_buf));
105       in = reinterpret_cast<Bytef *>(Buffer::Data(in_buf) + in_off);
106     }
107
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);
114
115     WorkReqWrap *req_wrap = new WorkReqWrap();
116
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;
122     ctx->flush_ = flush;
123
124     // set this so that later on, I can easily tell how much was written.
125     ctx->chunk_size_ = out_len;
126
127     // build up the work request
128     uv_work_t* work_req = new uv_work_t();
129     work_req->data = req_wrap;
130
131     uv_queue_work(uv_default_loop(),
132                   work_req,
133                   ZCtx<mode>::Process,
134                   ZCtx<mode>::After);
135
136     req_wrap->Dispatched();
137
138     return req_wrap->object_;
139   }
140
141
142   // thread pool!
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
145   // been consumed.
146   static void
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_;
150
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.
154     int err;
155     switch (mode) {
156       case DEFLATE:
157       case GZIP:
158       case DEFLATERAW:
159         err = deflate(&(ctx->strm_), ctx->flush_);
160         break;
161       case UNZIP:
162       case INFLATE:
163       case GUNZIP:
164       case INFLATERAW:
165         err = inflate(&(ctx->strm_), ctx->flush_);
166         break;
167       default:
168         assert(0 && "wtf?");
169     }
170     assert(err != Z_STREAM_ERROR);
171
172     // now After will emit the output, and
173     // either schedule another call to Process,
174     // or shift the queue and call Process.
175   }
176
177   // v8 land!
178   static void
179   After(uv_work_t* work_req) {
180     HandleScope scope;
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);
185
186     // call the write() cb
187     assert(req_wrap->object_->Get(callback_sym)->IsFunction() &&
188            "Invalid callback");
189     Local<Value> args[2] = { avail_in, avail_out };
190     MakeCallback(req_wrap->object_, "callback", 2, args);
191
192     // delete the ReqWrap
193     delete req_wrap;
194   }
195
196   static Handle<Value>
197   New(const Arguments& args) {
198     HandleScope scope;
199     ZCtx<mode> *ctx = new ZCtx<mode>();
200     ctx->Wrap(args.This());
201     return args.This();
202   }
203
204   // just pull the ints out of the args and call the other Init
205   static Handle<Value>
206   Init(const Arguments& args) {
207     HandleScope scope;
208
209     assert(args.Length() == 4 &&
210            "init(level, windowBits, memLevel, strategy)");
211
212     ZCtx<mode> *ctx = ObjectWrap::Unwrap< ZCtx<mode> >(args.This());
213
214     int windowBits = args[0]->Uint32Value();
215     assert((windowBits >= 8 && windowBits <= 15) && "invalid windowBits");
216
217     int level = args[1]->Uint32Value();
218     assert((level >= -1 && level <= 9) && "invalid compression level");
219
220     int memLevel = args[2]->Uint32Value();
221     assert((memLevel >= 1 && memLevel <= 9) && "invalid memlevel");
222
223     int strategy = args[3]->Uint32Value();
224     assert((strategy == Z_FILTERED ||
225             strategy == Z_HUFFMAN_ONLY ||
226             strategy == Z_RLE ||
227             strategy == Z_FIXED ||
228             strategy == Z_DEFAULT_STRATEGY) && "invalid strategy");
229
230     Init(ctx, level, windowBits, memLevel, strategy);
231     return Undefined();
232   }
233
234   static void
235   Init(ZCtx *ctx,
236        int level,
237        int windowBits,
238        int memLevel,
239        int strategy) {
240     ctx->level_ = level;
241     ctx->windowBits_ = windowBits;
242     ctx->memLevel_ = memLevel;
243     ctx->strategy_ = strategy;
244
245     ctx->strm_.zalloc = Z_NULL;
246     ctx->strm_.zfree = Z_NULL;
247     ctx->strm_.opaque = Z_NULL;
248
249     ctx->flush_ = Z_NO_FLUSH;
250
251     if (mode == GZIP || mode == GUNZIP) {
252       ctx->windowBits_ += 16;
253     }
254
255     if (mode == UNZIP) {
256       ctx->windowBits_ += 32;
257     }
258
259     if (mode == DEFLATERAW || mode == INFLATERAW) {
260       ctx->windowBits_ *= -1;
261     }
262
263     int err;
264     switch (mode) {
265       case DEFLATE:
266       case GZIP:
267       case DEFLATERAW:
268         err = deflateInit2(&(ctx->strm_),
269                            ctx->level_,
270                            Z_DEFLATED,
271                            ctx->windowBits_,
272                            ctx->memLevel_,
273                            ctx->strategy_);
274         break;
275       case INFLATE:
276       case GUNZIP:
277       case INFLATERAW:
278       case UNZIP:
279         err = inflateInit2(&(ctx->strm_), ctx->windowBits_);
280         break;
281       default:
282         assert(0 && "wtf?");
283     }
284
285     ctx->init_done_ = true;
286     assert(err == Z_OK);
287   }
288
289  private:
290
291   bool init_done_;
292
293   z_stream strm_;
294   int level_;
295   int windowBits_;
296   int memLevel_;
297   int strategy_;
298
299   int flush_;
300
301   int chunk_size_;
302 };
303
304
305 #define NODE_ZLIB_CLASS(mode, name)   \
306   { \
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()); \
313   }
314
315 void InitZlib(Handle<Object> target) {
316   HandleScope scope;
317
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")
325
326   callback_sym = NODE_PSYMBOL("callback");
327
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);
353
354   target->Set(String::NewSymbol("ZLIB_VERSION"), String::New(ZLIB_VERSION));
355 }
356
357 }  // namespace node
358
359 NODE_MODULE(node_zlib, node::InitZlib);