Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 351 lines (280 sloc) 10.008 kb
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
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
33
34 namespace node {
35 using namespace v8;
36
37
38 static Persistent<String> callback_sym;
39
40 enum node_zlib_mode {
41 DEFLATE = 1,
42 INFLATE,
43 GZIP,
44 GUNZIP,
45 DEFLATERAW,
46 INFLATERAW,
47 UNZIP
48 };
49
50 template <node_zlib_mode mode> class ZCtx;
51
52
53 void InitZlib(v8::Handle<v8::Object> target);
54
55
56 /**
57 * Deflate/Inflate
58 */
59 template <node_zlib_mode mode> class ZCtx : public ObjectWrap {
60 public:
61
62 ZCtx() : ObjectWrap() {
63 }
64
65 ~ZCtx() {
66 if (mode == DEFLATE || mode == GZIP || mode == DEFLATERAW) {
67 (void)deflateEnd(&strm_);
68 } else if (mode == INFLATE || mode == GUNZIP || mode == INFLATERAW) {
69 (void)inflateEnd(&strm_);
70 }
71 }
72
73 // write(flush, in, in_off, in_len, out, out_off, out_len)
07701e7 @indutny zlib: C++ style fixes
indutny authored
74 static Handle<Value> Write(const Arguments& args) {
d104bfd @isaacs zlib: Fix test so that it's not trivially passing, then pass it.
isaacs authored
75 HandleScope scope;
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
76 assert(args.Length() == 7);
77
78 ZCtx<mode> *ctx = ObjectWrap::Unwrap< ZCtx<mode> >(args.This());
79 assert(ctx->init_done_ && "write before init");
80
8cca30f @isaacs zlib binding cleanup
isaacs authored
81 assert(!ctx->write_in_progress_ && "write already in progress");
82 ctx->write_in_progress_ = true;
83
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
84 unsigned int flush = args[0]->Uint32Value();
85 Bytef *in;
86 Bytef *out;
87 size_t in_off, in_len, out_off, out_len;
88
89 if (args[1]->IsNull()) {
90 // just a flush
91 Bytef nada[1] = { 0 };
92 in = nada;
93 in_len = 0;
94 in_off = 0;
95 } else {
96 assert(Buffer::HasInstance(args[1]));
97 Local<Object> in_buf;
98 in_buf = args[1]->ToObject();
07701e7 @indutny zlib: C++ style fixes
indutny authored
99 in_off = args[2]->Uint32Value();
100 in_len = args[3]->Uint32Value();
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
101
102 assert(in_off + in_len <= Buffer::Length(in_buf));
103 in = reinterpret_cast<Bytef *>(Buffer::Data(in_buf) + in_off);
104 }
105
106 assert(Buffer::HasInstance(args[4]));
107 Local<Object> out_buf = args[4]->ToObject();
07701e7 @indutny zlib: C++ style fixes
indutny authored
108 out_off = args[5]->Uint32Value();
109 out_len = args[6]->Uint32Value();
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
110 assert(out_off + out_len <= Buffer::Length(out_buf));
111 out = reinterpret_cast<Bytef *>(Buffer::Data(out_buf) + out_off);
112
8cca30f @isaacs zlib binding cleanup
isaacs authored
113 // build up the work request
114 uv_work_t* work_req = &(ctx->work_req_);
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
115
116 ctx->strm_.avail_in = in_len;
07701e7 @indutny zlib: C++ style fixes
indutny authored
117 ctx->strm_.next_in = in;
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
118 ctx->strm_.avail_out = out_len;
119 ctx->strm_.next_out = out;
120 ctx->flush_ = flush;
121
122 // set this so that later on, I can easily tell how much was written.
123 ctx->chunk_size_ = out_len;
124
125 uv_queue_work(uv_default_loop(),
126 work_req,
127 ZCtx<mode>::Process,
128 ZCtx<mode>::After);
129
0ad2717 @bnoordhuis Make sure that zlib contexts are not garbage collected when busy
bnoordhuis authored
130 ctx->Ref();
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
131
8cca30f @isaacs zlib binding cleanup
isaacs authored
132 return ctx->handle_;
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
133 }
134
135
136 // thread pool!
137 // This function may be called multiple times on the uv_work pool
138 // for a single write() call, until all of the input bytes have
139 // been consumed.
07701e7 @indutny zlib: C++ style fixes
indutny authored
140 static void Process(uv_work_t* work_req) {
8cca30f @isaacs zlib binding cleanup
isaacs authored
141 ZCtx<mode> *ctx = container_of(work_req, ZCtx<mode>, work_req_);
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
142
143 // If the avail_out is left at 0, then it means that it ran out
144 // of room. If there was avail_out left over, then it means
145 // that all of the input was consumed.
146 int err;
147 switch (mode) {
148 case DEFLATE:
149 case GZIP:
150 case DEFLATERAW:
07701e7 @indutny zlib: C++ style fixes
indutny authored
151 err = deflate(&ctx->strm_, ctx->flush_);
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
152 break;
153 case UNZIP:
154 case INFLATE:
155 case GUNZIP:
156 case INFLATERAW:
07701e7 @indutny zlib: C++ style fixes
indutny authored
157 err = inflate(&ctx->strm_, ctx->flush_);
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
158 break;
159 default:
160 assert(0 && "wtf?");
161 }
162 assert(err != Z_STREAM_ERROR);
163
164 // now After will emit the output, and
165 // either schedule another call to Process,
166 // or shift the queue and call Process.
167 }
168
169 // v8 land!
07701e7 @indutny zlib: C++ style fixes
indutny authored
170 static void After(uv_work_t* work_req) {
d104bfd @isaacs zlib: Fix test so that it's not trivially passing, then pass it.
isaacs authored
171 HandleScope scope;
8cca30f @isaacs zlib binding cleanup
isaacs authored
172 ZCtx<mode> *ctx = container_of(work_req, ZCtx<mode>, work_req_);
07701e7 @indutny zlib: C++ style fixes
indutny authored
173
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
174 Local<Integer> avail_out = Integer::New(ctx->strm_.avail_out);
175 Local<Integer> avail_in = Integer::New(ctx->strm_.avail_in);
176
8cca30f @isaacs zlib binding cleanup
isaacs authored
177 ctx->write_in_progress_ = false;
178
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
179 // call the write() cb
8cca30f @isaacs zlib binding cleanup
isaacs authored
180 assert(ctx->handle_->Get(callback_sym)->IsFunction() &&
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
181 "Invalid callback");
182 Local<Value> args[2] = { avail_in, avail_out };
8cca30f @isaacs zlib binding cleanup
isaacs authored
183 MakeCallback(ctx->handle_, "callback", 2, args);
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
184
0ad2717 @bnoordhuis Make sure that zlib contexts are not garbage collected when busy
bnoordhuis authored
185 ctx->Unref();
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
186 }
187
07701e7 @indutny zlib: C++ style fixes
indutny authored
188 static Handle<Value> New(const Arguments& args) {
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
189 HandleScope scope;
190 ZCtx<mode> *ctx = new ZCtx<mode>();
191 ctx->Wrap(args.This());
192 return args.This();
193 }
194
195 // just pull the ints out of the args and call the other Init
07701e7 @indutny zlib: C++ style fixes
indutny authored
196 static Handle<Value> Init(const Arguments& args) {
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
197 HandleScope scope;
198
199 assert(args.Length() == 4 &&
f1ca485 @bnoordhuis zlib: fix assert message
bnoordhuis authored
200 "init(windowBits, level, memLevel, strategy)");
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
201
202 ZCtx<mode> *ctx = ObjectWrap::Unwrap< ZCtx<mode> >(args.This());
203
204 int windowBits = args[0]->Uint32Value();
205 assert((windowBits >= 8 && windowBits <= 15) && "invalid windowBits");
206
207 int level = args[1]->Uint32Value();
208 assert((level >= -1 && level <= 9) && "invalid compression level");
209
210 int memLevel = args[2]->Uint32Value();
211 assert((memLevel >= 1 && memLevel <= 9) && "invalid memlevel");
212
213 int strategy = args[3]->Uint32Value();
214 assert((strategy == Z_FILTERED ||
215 strategy == Z_HUFFMAN_ONLY ||
216 strategy == Z_RLE ||
217 strategy == Z_FIXED ||
218 strategy == Z_DEFAULT_STRATEGY) && "invalid strategy");
219
220 Init(ctx, level, windowBits, memLevel, strategy);
221 return Undefined();
222 }
223
07701e7 @indutny zlib: C++ style fixes
indutny authored
224 static void Init(ZCtx *ctx, int level, int windowBits, int memLevel,
225 int strategy) {
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
226 ctx->level_ = level;
227 ctx->windowBits_ = windowBits;
228 ctx->memLevel_ = memLevel;
229 ctx->strategy_ = strategy;
230
231 ctx->strm_.zalloc = Z_NULL;
232 ctx->strm_.zfree = Z_NULL;
233 ctx->strm_.opaque = Z_NULL;
234
235 ctx->flush_ = Z_NO_FLUSH;
236
237 if (mode == GZIP || mode == GUNZIP) {
238 ctx->windowBits_ += 16;
239 }
240
241 if (mode == UNZIP) {
242 ctx->windowBits_ += 32;
243 }
244
245 if (mode == DEFLATERAW || mode == INFLATERAW) {
246 ctx->windowBits_ *= -1;
247 }
248
249 int err;
250 switch (mode) {
251 case DEFLATE:
252 case GZIP:
253 case DEFLATERAW:
254 err = deflateInit2(&(ctx->strm_),
255 ctx->level_,
256 Z_DEFLATED,
257 ctx->windowBits_,
258 ctx->memLevel_,
259 ctx->strategy_);
260 break;
261 case INFLATE:
262 case GUNZIP:
263 case INFLATERAW:
264 case UNZIP:
265 err = inflateInit2(&(ctx->strm_), ctx->windowBits_);
266 break;
267 default:
268 assert(0 && "wtf?");
269 }
270
8cca30f @isaacs zlib binding cleanup
isaacs authored
271 ctx->write_in_progress_ = false;
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
272 ctx->init_done_ = true;
273 assert(err == Z_OK);
274 }
275
276 private:
277
278 bool init_done_;
279
280 z_stream strm_;
281 int level_;
282 int windowBits_;
283 int memLevel_;
284 int strategy_;
285
286 int flush_;
287
288 int chunk_size_;
8cca30f @isaacs zlib binding cleanup
isaacs authored
289
290 bool write_in_progress_;
291
292 uv_work_t work_req_;
5b8e1da @isaacs Initial pass at zlib bindings
isaacs authored
293 };
294
295
296 #define NODE_ZLIB_CLASS(mode, name) \
297 { \
298 Local<FunctionTemplate> z = FunctionTemplate::New(ZCtx<mode>::New); \
299 z->InstanceTemplate()->SetInternalFieldCount(1); \
300 NODE_SET_PROTOTYPE_METHOD(z, "write", ZCtx<mode>::Write); \
301 NODE_SET_PROTOTYPE_METHOD(z, "init", ZCtx<mode>::Init); \
302 z->SetClassName(String::NewSymbol(name)); \
303 target->Set(String::NewSymbol(name), z->GetFunction()); \
304 }
305
306 void InitZlib(Handle<Object> target) {
307 HandleScope scope;
308
309 NODE_ZLIB_CLASS(INFLATE, "Inflate")
310 NODE_ZLIB_CLASS(DEFLATE, "Deflate")
311 NODE_ZLIB_CLASS(INFLATERAW, "InflateRaw")
312 NODE_ZLIB_CLASS(DEFLATERAW, "DeflateRaw")
313 NODE_ZLIB_CLASS(GZIP, "Gzip")
314 NODE_ZLIB_CLASS(GUNZIP, "Gunzip")
315 NODE_ZLIB_CLASS(UNZIP, "Unzip")
316
317 callback_sym = NODE_PSYMBOL("callback");
318
319 NODE_DEFINE_CONSTANT(target, Z_NO_FLUSH);
320 NODE_DEFINE_CONSTANT(target, Z_PARTIAL_FLUSH);
321 NODE_DEFINE_CONSTANT(target, Z_SYNC_FLUSH);
322 NODE_DEFINE_CONSTANT(target, Z_FULL_FLUSH);
323 NODE_DEFINE_CONSTANT(target, Z_FINISH);
324 NODE_DEFINE_CONSTANT(target, Z_BLOCK);
325 NODE_DEFINE_CONSTANT(target, Z_OK);
326 NODE_DEFINE_CONSTANT(target, Z_STREAM_END);
327 NODE_DEFINE_CONSTANT(target, Z_NEED_DICT);
328 NODE_DEFINE_CONSTANT(target, Z_ERRNO);
329 NODE_DEFINE_CONSTANT(target, Z_STREAM_ERROR);
330 NODE_DEFINE_CONSTANT(target, Z_DATA_ERROR);
331 NODE_DEFINE_CONSTANT(target, Z_MEM_ERROR);
332 NODE_DEFINE_CONSTANT(target, Z_BUF_ERROR);
333 NODE_DEFINE_CONSTANT(target, Z_VERSION_ERROR);
334 NODE_DEFINE_CONSTANT(target, Z_NO_COMPRESSION);
335 NODE_DEFINE_CONSTANT(target, Z_BEST_SPEED);
336 NODE_DEFINE_CONSTANT(target, Z_BEST_COMPRESSION);
337 NODE_DEFINE_CONSTANT(target, Z_DEFAULT_COMPRESSION);
338 NODE_DEFINE_CONSTANT(target, Z_FILTERED);
339 NODE_DEFINE_CONSTANT(target, Z_HUFFMAN_ONLY);
340 NODE_DEFINE_CONSTANT(target, Z_RLE);
341 NODE_DEFINE_CONSTANT(target, Z_FIXED);
342 NODE_DEFINE_CONSTANT(target, Z_DEFAULT_STRATEGY);
343 NODE_DEFINE_CONSTANT(target, ZLIB_VERNUM);
344
345 target->Set(String::NewSymbol("ZLIB_VERSION"), String::New(ZLIB_VERSION));
346 }
347
348 } // namespace node
349
cdcb111 @bnoordhuis Remove stray NODE_MODULE() semi-colons.
bnoordhuis authored
350 NODE_MODULE(node_zlib, node::InitZlib)
Something went wrong with that request. Please try again.