mirror of https://github.com/lukechilds/node.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
436 lines
12 KiB
436 lines
12 KiB
// 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 <v8.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <zlib.h>
|
|
|
|
#include <node.h>
|
|
#include <node_buffer.h>
|
|
|
|
|
|
namespace node {
|
|
using namespace v8;
|
|
|
|
|
|
static Persistent<String> callback_sym;
|
|
|
|
enum node_zlib_mode {
|
|
DEFLATE = 1,
|
|
INFLATE,
|
|
GZIP,
|
|
GUNZIP,
|
|
DEFLATERAW,
|
|
INFLATERAW,
|
|
UNZIP
|
|
};
|
|
|
|
template <node_zlib_mode mode> class ZCtx;
|
|
|
|
|
|
void InitZlib(v8::Handle<v8::Object> target);
|
|
|
|
|
|
/**
|
|
* Deflate/Inflate
|
|
*/
|
|
template <node_zlib_mode mode> class ZCtx : public ObjectWrap {
|
|
public:
|
|
|
|
ZCtx() : ObjectWrap() {
|
|
dictionary_ = NULL;
|
|
}
|
|
|
|
~ZCtx() {
|
|
if (mode == DEFLATE || mode == GZIP || mode == DEFLATERAW) {
|
|
(void)deflateEnd(&strm_);
|
|
} else if (mode == INFLATE || mode == GUNZIP || mode == INFLATERAW) {
|
|
(void)inflateEnd(&strm_);
|
|
}
|
|
|
|
if (dictionary_ != NULL) delete[] dictionary_;
|
|
}
|
|
|
|
// write(flush, in, in_off, in_len, out, out_off, out_len)
|
|
static Handle<Value> Write(const Arguments& args) {
|
|
HandleScope scope;
|
|
assert(args.Length() == 7);
|
|
|
|
ZCtx<mode> *ctx = ObjectWrap::Unwrap< ZCtx<mode> >(args.This());
|
|
assert(ctx->init_done_ && "write before init");
|
|
|
|
assert(!ctx->write_in_progress_ && "write already in progress");
|
|
ctx->write_in_progress_ = true;
|
|
|
|
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<Object> in_buf;
|
|
in_buf = args[1]->ToObject();
|
|
in_off = args[2]->Uint32Value();
|
|
in_len = args[3]->Uint32Value();
|
|
|
|
assert(in_off + in_len <= Buffer::Length(in_buf));
|
|
in = reinterpret_cast<Bytef *>(Buffer::Data(in_buf) + in_off);
|
|
}
|
|
|
|
assert(Buffer::HasInstance(args[4]));
|
|
Local<Object> out_buf = args[4]->ToObject();
|
|
out_off = args[5]->Uint32Value();
|
|
out_len = args[6]->Uint32Value();
|
|
assert(out_off + out_len <= Buffer::Length(out_buf));
|
|
out = reinterpret_cast<Bytef *>(Buffer::Data(out_buf) + out_off);
|
|
|
|
// build up the work request
|
|
uv_work_t* work_req = &(ctx->work_req_);
|
|
|
|
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;
|
|
|
|
uv_queue_work(uv_default_loop(),
|
|
work_req,
|
|
ZCtx<mode>::Process,
|
|
ZCtx<mode>::After);
|
|
|
|
ctx->Ref();
|
|
|
|
return ctx->handle_;
|
|
}
|
|
|
|
|
|
// 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) {
|
|
ZCtx<mode> *ctx = container_of(work_req, ZCtx<mode>, work_req_);
|
|
|
|
// 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_);
|
|
|
|
// If data was encoded with dictionary
|
|
if (err == Z_NEED_DICT) {
|
|
assert(ctx->dictionary_ != NULL && "Stream has no dictionary");
|
|
|
|
// Load it
|
|
err = inflateSetDictionary(&ctx->strm_,
|
|
ctx->dictionary_,
|
|
ctx->dictionary_len_);
|
|
assert(err == Z_OK && "Failed to set dictionary");
|
|
|
|
// And try to decode again
|
|
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;
|
|
ZCtx<mode> *ctx = container_of(work_req, ZCtx<mode>, work_req_);
|
|
|
|
Local<Integer> avail_out = Integer::New(ctx->strm_.avail_out);
|
|
Local<Integer> avail_in = Integer::New(ctx->strm_.avail_in);
|
|
|
|
ctx->write_in_progress_ = false;
|
|
|
|
// call the write() cb
|
|
assert(ctx->handle_->Get(callback_sym)->IsFunction() &&
|
|
"Invalid callback");
|
|
Local<Value> args[2] = { avail_in, avail_out };
|
|
MakeCallback(ctx->handle_, "callback", 2, args);
|
|
|
|
ctx->Unref();
|
|
}
|
|
|
|
static Handle<Value> New(const Arguments& args) {
|
|
HandleScope scope;
|
|
ZCtx<mode> *ctx = new ZCtx<mode>();
|
|
ctx->Wrap(args.This());
|
|
return args.This();
|
|
}
|
|
|
|
// just pull the ints out of the args and call the other Init
|
|
static Handle<Value> Init(const Arguments& args) {
|
|
HandleScope scope;
|
|
|
|
assert((args.Length() == 4 || args.Length() == 5) &&
|
|
"init(windowBits, level, memLevel, strategy, [dictionary])");
|
|
|
|
ZCtx<mode> *ctx = ObjectWrap::Unwrap< ZCtx<mode> >(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");
|
|
|
|
char* dictionary = NULL;
|
|
size_t dictionary_len = 0;
|
|
if (args.Length() >= 5 && Buffer::HasInstance(args[4])) {
|
|
Local<Object> dictionary_ = args[4]->ToObject();
|
|
|
|
dictionary_len = Buffer::Length(dictionary_);
|
|
dictionary = new char[dictionary_len];
|
|
|
|
memcpy(dictionary, Buffer::Data(dictionary_), dictionary_len);
|
|
}
|
|
|
|
Init(ctx, level, windowBits, memLevel, strategy,
|
|
dictionary, dictionary_len);
|
|
SetDictionary(ctx);
|
|
return Undefined();
|
|
}
|
|
|
|
static Handle<Value> Reset(const Arguments &args) {
|
|
HandleScope scope;
|
|
|
|
ZCtx<mode> *ctx = ObjectWrap::Unwrap< ZCtx<mode> >(args.This());
|
|
|
|
Reset(ctx);
|
|
SetDictionary(ctx);
|
|
return Undefined();
|
|
}
|
|
|
|
static void Init(ZCtx *ctx, int level, int windowBits, int memLevel,
|
|
int strategy, char* dictionary, size_t dictionary_len) {
|
|
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?");
|
|
}
|
|
|
|
assert(err == Z_OK);
|
|
|
|
ctx->dictionary_ = reinterpret_cast<Bytef *>(dictionary);
|
|
ctx->dictionary_len_ = dictionary_len;
|
|
|
|
ctx->write_in_progress_ = false;
|
|
ctx->init_done_ = true;
|
|
}
|
|
|
|
static void SetDictionary(ZCtx* ctx) {
|
|
if (ctx->dictionary_ == NULL) return;
|
|
|
|
int err = Z_OK;
|
|
|
|
switch (mode) {
|
|
case DEFLATE:
|
|
case DEFLATERAW:
|
|
err = deflateSetDictionary(&ctx->strm_,
|
|
ctx->dictionary_,
|
|
ctx->dictionary_len_);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
assert(err == Z_OK && "Failed to set dictionary");
|
|
}
|
|
|
|
static void Reset(ZCtx* ctx) {
|
|
int err = Z_OK;
|
|
|
|
switch (mode) {
|
|
case DEFLATE:
|
|
case DEFLATERAW:
|
|
err = deflateReset(&ctx->strm_);
|
|
break;
|
|
case INFLATE:
|
|
case INFLATERAW:
|
|
err = inflateReset(&ctx->strm_);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
assert(err == Z_OK && "Failed to reset stream");
|
|
}
|
|
|
|
private:
|
|
|
|
bool init_done_;
|
|
|
|
z_stream strm_;
|
|
int level_;
|
|
int windowBits_;
|
|
int memLevel_;
|
|
int strategy_;
|
|
|
|
Bytef* dictionary_;
|
|
size_t dictionary_len_;
|
|
|
|
int flush_;
|
|
|
|
int chunk_size_;
|
|
|
|
bool write_in_progress_;
|
|
|
|
uv_work_t work_req_;
|
|
};
|
|
|
|
|
|
#define NODE_ZLIB_CLASS(mode, name) \
|
|
{ \
|
|
Local<FunctionTemplate> z = FunctionTemplate::New(ZCtx<mode>::New); \
|
|
z->InstanceTemplate()->SetInternalFieldCount(1); \
|
|
NODE_SET_PROTOTYPE_METHOD(z, "write", ZCtx<mode>::Write); \
|
|
NODE_SET_PROTOTYPE_METHOD(z, "init", ZCtx<mode>::Init); \
|
|
NODE_SET_PROTOTYPE_METHOD(z, "reset", ZCtx<mode>::Reset); \
|
|
z->SetClassName(String::NewSymbol(name)); \
|
|
target->Set(String::NewSymbol(name), z->GetFunction()); \
|
|
}
|
|
|
|
void InitZlib(Handle<Object> 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)
|
|
|