mirror of
https://github.com/nodejs/node-v0.x-archive.git
synced 2026-04-28 03:01:10 -04:00
Including <zlib.h> may lead to false positives when the user specifies a bad path in `./configure --shared-zlib --shared-zlib-includes=/path/to/zlib`. If a zlib.h exists somewhere on the system include path (common on UNIX systems), the compiler will include that instead, possibly leading to header mismatch errors that are hard to debug.
437 lines
12 KiB
C++
437 lines
12 KiB
C++
// 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)
|