Merge remote-tracking branch 'ry/v0.10' into master
authorisaacs <i@izs.me>
Fri, 17 May 2013 20:04:02 +0000 (13:04 -0700)
committerisaacs <i@izs.me>
Fri, 17 May 2013 21:04:54 +0000 (14:04 -0700)
Conflicts:
AUTHORS
ChangeLog
deps/uv/ChangeLog
deps/uv/config-unix.mk
deps/uv/src/unix/stream.c
deps/uv/src/version.c
deps/uv/uv.gyp
src/node.cc
src/node_buffer.cc
src/node_crypto.cc
src/node_version.h
src/stream_wrap.cc
src/stream_wrap.h

16 files changed:
1  2 
AUTHORS
ChangeLog
doc/api/fs.markdown
doc/api/stream.markdown
lib/_stream_transform.js
lib/_stream_writable.js
lib/crypto.js
lib/net.js
node.gyp
src/node.cc
src/node_buffer.cc
src/node_crypto.cc
src/stream_wrap.cc
src/stream_wrap.h
src/string_bytes.cc
test/simple/test-buffer.js

diff --cc AUTHORS
+++ b/AUTHORS
@@@ -451,4 -446,4 +451,5 @@@ Sam Roberts <vieuxtech@gmail.com
  Kevin Locke <kevin@kevinlocke.name>
  Daniel Moore <polaris@northhorizon.net>
  Robert Kowalski <rok@kowalski.gd>
+ Benoit Vallée <github@benoitvallee.net>
 +Nick Sullivan <nick@sullivanflock.com>
diff --cc ChangeLog
+++ b/ChangeLog
@@@ -1,65 -1,16 +1,78 @@@
 -2013.05.14, Version 0.10.6 (Stable)
 +2013.05.13, Version 0.11.2 (Unstable)
 +
 +* uv: Upgrade to 0.11.2
 +
 +* V8: Upgrade to 3.19.0
 +
 +* npm: Upgrade to 1.2.21
 +
 +* build: Makefile should respect configure --prefix (Timothy J Fontaine)
 +
 +* cluster: use round-robin load balancing (Ben Noordhuis)
 +
 +* debugger, cluster: each worker has new debug port (Miroslav Bajtoš)
 +
 +* debugger: `restart` with custom debug port (Miroslav Bajtoš)
 +
 +* debugger: breakpoints in scripts not loaded yet (Miroslav Bajtoš)
 +
 +* event: EventEmitter#setMaxListeners() returns this (Sam Roberts)
 +
 +* events: add EventEmitter.defaultMaxListeners (Ben Noordhuis)
 +
 +* install: Support $(PREFIX) install target directory prefix (Olof Johansson)
 +
 +* os: Include netmask in os.networkInterfaces() (Ben Kelly)
 +
 +* path: add path.isAbsolute(path) (Ryan Doenges)
 +
 +* stream: Guarantee ordering of 'finish' event (isaacs)
 +
 +* streams: introduce .cork/.uncork/._writev (Fedor Indutny)
 +
 +* vm: add support for timeout argument (Andrew Paprocki)
 +
 +
 +2013.04.19, Version 0.11.1 (Unstable), 4babd2b46ebf9fbea2c9946af5cfae25a33b2b22
 +
 +* V8: upgrade to 3.18.0
 +
 +* uv: Upgrade to v0.11.1
 +
 +* http: split into multiple separate modules (Timothy J Fontaine)
 +
 +* http: escape unsafe characters in request path (Ben Noordhuis)
 +
 +* url: Escape all unwise characters (isaacs)
 +
 +* build: depend on v8 postmortem-metadata if enabled (Paddy Byers)
 +
 +* etw: update prototypes to match dtrace provider (Timothy J Fontaine)
 +
 +* buffer: change output of Buffer.prototype.toJSON() (David Braun)
 +
 +* dtrace: actually use the _handle.fd value (Timothy J Fontaine)
 +
 +* dtrace: pass more arguments to probes (Dave Pacheco)
 +
 +* build: allow building with dtrace on osx (Dave Pacheco)
 +
 +* zlib: allow passing options to convenience methods (Kyle Robinson Young)
 +
 +
++2013.05.14, Version 0.10.6 (Stable), 5deb1672f2b5794f8be19498a425ea4dc0b0711f
+ * module: Deprecate require.extensions (isaacs)
+ * stream: make Readable.wrap support objectMode, empty streams (Daniel Moore)
+ * child_process: fix handle delivery (Ben Noordhuis)
+ * crypto: Fix performance regression (isaacs)
+ * src: DRY string encoding/decoding (isaacs)
  2013.04.23, Version 0.10.5 (Stable), deeaf8fab978e3cadb364e46fb32dafdebe5f095
  
  * uv: Upgrade to 0.10.5 (isaacs)
Simple merge
Simple merge
Simple merge
Simple merge
diff --cc lib/crypto.js
Simple merge
diff --cc lib/net.js
@@@ -623,31 -619,8 +623,25 @@@ Socket.prototype._writeGeneric = functi
      return false;
    }
  
 -  var enc = Buffer.isBuffer(data) ? 'buffer' : encoding;
 -  var writeReq = createWriteReq(this._handle, data, enc);
 +  var writeReq;
 +  if (writev) {
 +    var chunks = new Array(data.length << 1);
 +    for (var i = 0; i < data.length; i++) {
 +      var entry = data[i];
-       var enc = entry.encoding;
 +      var chunk = entry.chunk;
-       var code = getEncodingId(enc);
-       // Buffer encoding, translate argument to buffer
-       if (code === 0 && !Buffer.isBuffer(chunk))
-         chunk = new Buffer(chunk, enc);
++      var enc = entry.encoding;
 +      chunks[i * 2] = chunk;
-       chunks[i * 2 + 1] = code;
++      chunks[i * 2 + 1] = enc;
 +    }
 +    var writeReq = this._handle.writev(chunks);
 +
 +    // Retain chunks
 +    if (writeReq)
 +      writeReq._chunks = chunks;
 +  } else {
 +    var enc = Buffer.isBuffer(data) ? 'buffer' : encoding;
 +    var writeReq = createWriteReq(this._handle, data, enc);
 +  }
  
    if (!writeReq || typeof writeReq !== 'object')
      return this._destroy(errnoException(process._errno, 'write'), cb);
diff --cc node.gyp
Simple merge
diff --cc src/node.cc
@@@ -1197,71 -1166,15 +1167,7 @@@ ssize_t DecodeWrite(char *buf
                      size_t buflen,
                      v8::Handle<v8::Value> val,
                      enum encoding encoding) {
-   HandleScope scope(node_isolate);
-   // XXX
-   // A lot of improvement can be made here. See:
-   // http://code.google.com/p/v8/issues/detail?id=270
-   // http://groups.google.com/group/v8-dev/browse_thread/thread/dba28a81d9215291/ece2b50a3b4022c
-   // http://groups.google.com/group/v8-users/browse_thread/thread/1f83b0ba1f0a611
 -  HandleScope scope;
--
--  if (val->IsArray()) {
-     fprintf(stderr, "'raw' encoding (array of integers) has been removed. "
-                     "Use 'binary'.\n");
 -    fprintf(stderr, "'raw' encoding (array of integers) has been removed.\n");
--    assert(0);
--    return -1;
-   }
-   bool is_buffer = Buffer::HasInstance(val);
-   if (is_buffer && (encoding == BINARY || encoding == BUFFER)) {
-     // fast path, copy buffer data
-     const char* data = Buffer::Data(val.As<Object>());
-     size_t size = Buffer::Length(val.As<Object>());
-     size_t len = size < buflen ? size : buflen;
-     memcpy(buf, data, len);
-     return len;
-   }
-   Local<String> str;
-   if (is_buffer) { // slow path, convert to binary string
-     Local<Value> arg = String::New("binary");
-     str = MakeCallback(val.As<Object>(), "toString", 1, &arg)->ToString();
-   }
-   else {
-     str = val->ToString();
-   }
-   if (encoding == UTF8) {
-     str->WriteUtf8(buf, buflen, NULL, String::HINT_MANY_WRITES_EXPECTED);
-     return buflen;
-   }
-   if (encoding == ASCII) {
-     str->WriteOneByte(reinterpret_cast<uint8_t*>(buf),
-                       0,
-                       buflen,
-                       String::HINT_MANY_WRITES_EXPECTED);
-     return buflen;
-   }
-   // THIS IS AWFUL!!! FIXME
-   assert(encoding == BINARY);
-   uint16_t * twobytebuf = new uint16_t[buflen];
-   str->Write(twobytebuf, 0, buflen, String::HINT_MANY_WRITES_EXPECTED);
-   for (size_t i = 0; i < buflen; i++) {
-     unsigned char *b = reinterpret_cast<unsigned char*>(&twobytebuf[i]);
-     buf[i] = b[0];
--  }
-   delete [] twobytebuf;
--
-   return buflen;
+   return StringBytes::Write(buf, buflen, val, encoding, NULL);
  }
  
  void DisplayExceptionLine (TryCatch &try_catch) {
@@@ -65,51 -68,8 +67,8 @@@ static Persistent<Function> fast_buffer
  Persistent<FunctionTemplate> Buffer::constructor_template;
  
  
- static inline size_t base64_decoded_size(const char *src, size_t size) {
-   const char *const end = src + size;
-   const int remainder = size % 4;
-   size = (size / 4) * 3;
-   if (remainder) {
-     if (size == 0 && remainder == 1) {
-       // special case: 1-byte input cannot be decoded
-       size = 0;
-     } else {
-       // non-padded input, add 1 or 2 extra bytes
-       size += 1 + (remainder == 3);
-     }
-   }
-   // check for trailing padding (1 or 2 bytes)
-   if (size > 0) {
-     if (end[-1] == '=') size--;
-     if (end[-2] == '=') size--;
-   }
-   return size;
- }
- static size_t ByteLength (Handle<String> string, enum encoding enc) {
-   HandleScope scope(node_isolate);
-   if (enc == UTF8) {
-     return string->Utf8Length();
-   } else if (enc == BASE64) {
-     String::Utf8Value v(string);
-     return base64_decoded_size(*v, v.length());
-   } else if (enc == UCS2) {
-     return string->Length() * 2;
-   } else if (enc == HEX) {
-     return string->Length() / 2;
-   } else {
-     return string->Length();
-   }
- }
  Handle<Object> Buffer::New(Handle<String> string) {
 -  HandleScope scope;
 +  HandleScope scope(node_isolate);
  
    // get Buffer from global scope.
    Local<Object> global = v8::Context::GetCurrent()->Global();
@@@ -230,12 -189,12 +189,13 @@@ void Buffer::Replace(char *data, size_
    handle_->SetIndexedPropertiesToExternalArrayData(data_,
                                                     kExternalUnsignedByteArray,
                                                     length_);
 -  handle_->Set(length_symbol, Integer::NewFromUnsigned(length_));
 +  handle_->Set(length_symbol, Integer::NewFromUnsigned(length_, node_isolate));
  }
  
- Handle<Value> Buffer::BinarySlice(const Arguments &args) {
 +
 -  HandleScope scope;
+ template <encoding encoding>
+ Handle<Value> Buffer::StringSlice(const Arguments& args) {
 +  HandleScope scope(node_isolate);
    Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());
    SLICE_ARGS(args[0], args[1])
  
@@@ -410,93 -230,9 +231,8 @@@ Handle<Value> Buffer::HexSlice(const Ar
  }
  
  
- // supports regular and URL-safe base64
- static const int unbase64_table[] =
-   {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-2,-1,-1
-   ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-   ,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,62,-1,63
-   ,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1
-   ,-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14
-   ,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,63
-   ,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40
-   ,41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1
-   ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-   ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-   ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-   ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-   ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-   ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-   ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-   ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-   };
- #define unbase64(x) unbase64_table[(uint8_t)(x)]
- Handle<Value> Buffer::Base64Slice(const Arguments &args) {
-   HandleScope scope(node_isolate);
-   Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());
-   SLICE_ARGS(args[0], args[1])
-   unsigned slen = end - start;
-   const char* src = parent->data_ + start;
-   unsigned dlen = (slen + 2 - ((slen + 2) % 3)) / 3 * 4;
-   char* dst = new char[dlen];
-   unsigned a;
-   unsigned b;
-   unsigned c;
-   unsigned i;
-   unsigned k;
-   unsigned n;
-   static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-                               "abcdefghijklmnopqrstuvwxyz"
-                               "0123456789+/";
-   i = 0;
-   k = 0;
-   n = slen / 3 * 3;
-   while (i < n) {
-     a = src[i + 0] & 0xff;
-     b = src[i + 1] & 0xff;
-     c = src[i + 2] & 0xff;
-     dst[k + 0] = table[a >> 2];
-     dst[k + 1] = table[((a & 3) << 4) | (b >> 4)];
-     dst[k + 2] = table[((b & 0x0f) << 2) | (c >> 6)];
-     dst[k + 3] = table[c & 0x3f];
-     i += 3;
-     k += 4;
-   }
-   if (n != slen) {
-     switch (slen - n) {
-     case 1:
-       a = src[i + 0] & 0xff;
-       dst[k + 0] = table[a >> 2];
-       dst[k + 1] = table[(a & 3) << 4];
-       dst[k + 2] = '=';
-       dst[k + 3] = '=';
-       break;
-     case 2:
-       a = src[i + 0] & 0xff;
-       b = src[i + 1] & 0xff;
-       dst[k + 0] = table[a >> 2];
-       dst[k + 1] = table[((a & 3) << 4) | (b >> 4)];
-       dst[k + 2] = table[(b & 0x0f) << 2];
-       dst[k + 3] = '=';
-       break;
-     }
-   }
-   Local<String> string = String::New(dst, dlen);
-   delete [] dst;
--
-   return scope.Close(string);
+ Handle<Value> Buffer::Base64Slice(const Arguments& args) {
+   return Buffer::StringSlice<BASE64>(args);
  }
  
  
@@@ -573,266 -309,78 +309,74 @@@ Handle<Value> Buffer::Copy(const Argume
  }
  
  
- // var charsWritten = buffer.utf8Write(string, offset, [maxLength]);
- Handle<Value> Buffer::Utf8Write(const Arguments &args) {
-   HandleScope scope(node_isolate);
-   Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());
-   if (!args[0]->IsString()) {
-     return ThrowException(Exception::TypeError(String::New(
-             "Argument must be a string")));
-   }
-   Local<String> s = args[0]->ToString();
-   size_t offset = args[1]->Uint32Value();
-   int length = s->Length();
-   if (length == 0) {
-     return scope.Close(Integer::New(0, node_isolate));
-   }
-   if (length > 0 && offset >= buffer->length_) {
-     return ThrowTypeError("Offset is out of bounds");
-   }
-   size_t max_length = args[2]->IsUndefined() ? buffer->length_ - offset
-                                              : args[2]->Uint32Value();
-   max_length = MIN(buffer->length_ - offset, max_length);
-   char* p = buffer->data_ + offset;
-   int char_written;
-   int written = s->WriteUtf8(p,
-                              max_length,
-                              &char_written,
-                              (String::HINT_MANY_WRITES_EXPECTED |
-                               String::NO_NULL_TERMINATION));
-   return scope.Close(Integer::New(written, node_isolate));
+ Handle<Value> Buffer::Base64Write(const Arguments& args) {
+   return Buffer::StringWrite<BASE64>(args);
  }
  
- // var charsWritten = buffer.ucs2Write(string, offset, [maxLength]);
- Handle<Value> Buffer::Ucs2Write(const Arguments &args) {
-   HandleScope scope(node_isolate);
-   Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());
-   if (!args[0]->IsString()) {
-     return ThrowTypeError("Argument must be a string");
-   }
-   Local<String> s = args[0]->ToString();
-   size_t offset = args[1]->Uint32Value();
-   if (s->Length() > 0 && offset >= buffer->length_) {
-     return ThrowException(Exception::TypeError(String::New(
-             "Offset is out of bounds")));
-   }
-   size_t max_length = args[2]->IsUndefined() ? buffer->length_ - offset
-                                              : args[2]->Uint32Value();
-   max_length = MIN(buffer->length_ - offset, max_length) / 2;
-   uint16_t* p = (uint16_t*)(buffer->data_ + offset);
-   int written = s->Write(p,
-                          0,
-                          max_length,
-                          (String::HINT_MANY_WRITES_EXPECTED |
-                           String::NO_NULL_TERMINATION));
-   return scope.Close(Integer::New(written * 2, node_isolate));
+ Handle<Value> Buffer::BinaryWrite(const Arguments& args) {
+   return Buffer::StringWrite<BINARY>(args);
  }
  
- inline unsigned hex2bin(char c) {
-   if (c >= '0' && c <= '9') return c - '0';
-   if (c >= 'A' && c <= 'F') return 10 + (c - 'A');
-   if (c >= 'a' && c <= 'f') return 10 + (c - 'a');
-   return static_cast<unsigned>(-1);
+ Handle<Value> Buffer::Utf8Write(const Arguments& args) {
+   return Buffer::StringWrite<UTF8>(args);
  }
  
+ Handle<Value> Buffer::Ucs2Write(const Arguments& args) {
+   return Buffer::StringWrite<UCS2>(args);
+ }
  
  Handle<Value> Buffer::HexWrite(const Arguments& args) {
-   HandleScope scope(node_isolate);
-   Buffer* parent = ObjectWrap::Unwrap<Buffer>(args.This());
-   if (args[0]->IsString() == false) {
-     return ThrowTypeError("Argument must be a string");
-   }
-   Local<String> s = args[0].As<String>();
-   if (s->Length() % 2 != 0) {
-     return ThrowTypeError("Invalid hex string");
-   }
-   uint32_t start = args[1]->Uint32Value();
-   uint32_t size = args[2]->Uint32Value();
-   uint32_t end = start + size;
-   if (start >= parent->length_) {
-     Local<Integer> val = Integer::New(0, node_isolate);
-     return scope.Close(val);
-   }
-   if (end < start || end > parent->length_) {  // Overflow + bounds check.
-     end = parent->length_;
-     size = parent->length_ - start;
-   }
-   if (size == 0) {
-     Local<Integer> val = Integer::New(0, node_isolate);
-     return scope.Close(val);
-   }
-   char* dst = parent->data_ + start;
-   String::AsciiValue string(s);
-   const char* src = *string;
-   uint32_t max = string.length() / 2;
-   if (max > size) {
-     max = size;
-   }
-   for (uint32_t i = 0; i < max; ++i) {
-     unsigned a = hex2bin(src[i * 2 + 0]);
-     unsigned b = hex2bin(src[i * 2 + 1]);
-     if (!~a || !~b) return ThrowTypeError("Invalid hex string");
-     dst[i] = a * 16 + b;
-   }
-   return scope.Close(Integer::New(max, node_isolate));
+   return Buffer::StringWrite<HEX>(args);
  }
  
+ Handle<Value> Buffer::AsciiWrite(const Arguments& args) {
+   return Buffer::StringWrite<ASCII>(args);
+ }
  
- // var charsWritten = buffer.asciiWrite(string, offset);
- Handle<Value> Buffer::AsciiWrite(const Arguments &args) {
+ template <encoding encoding>
+ Handle<Value> Buffer::StringWrite(const Arguments& args) {
 -  HandleScope scope;
 +  HandleScope scope(node_isolate);
  
-   Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());
+   Bufferbuffer = ObjectWrap::Unwrap<Buffer>(args.This());
  
    if (!args[0]->IsString()) {
      return ThrowTypeError("Argument must be a string");
    }
  
-   Local<String> s = args[0]->ToString();
-   size_t length = s->Length();
-   size_t offset = args[1]->Int32Value();
+   Local<String> str = args[0].As<String>();
  
-   if (length > 0 && offset >= buffer->length_) {
-     return ThrowTypeError("Offset is out of bounds");
-   }
-   size_t max_length = args[2]->IsUndefined() ? buffer->length_ - offset
-                                              : args[2]->Uint32Value();
-   max_length = MIN(length, MIN(buffer->length_ - offset, max_length));
-   char *p = buffer->data_ + offset;
-   int written = s->WriteOneByte(reinterpret_cast<uint8_t*>(p),
-                                 0,
-                                 max_length,
-                                 (String::HINT_MANY_WRITES_EXPECTED |
-                                  String::NO_NULL_TERMINATION));
-   return scope.Close(Integer::New(written, node_isolate));
- }
- // var bytesWritten = buffer.base64Write(string, offset, [maxLength]);
- Handle<Value> Buffer::Base64Write(const Arguments &args) {
-   HandleScope scope(node_isolate);
-   Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());
+   if (encoding == HEX && str->Length() % 2 != 0)
+     return ThrowTypeError("Invalid hex string");
  
-   if (!args[0]->IsString()) {
-     return ThrowTypeError("Argument must be a string");
-   }
 +
-   String::AsciiValue s(args[0]);
-   size_t length = s.length();
    size_t offset = args[1]->Int32Value();
    size_t max_length = args[2]->IsUndefined() ? buffer->length_ - offset
                                               : args[2]->Uint32Value();
-   max_length = MIN(length, MIN(buffer->length_ - offset, max_length));
-   if (max_length && offset >= buffer->length_) {
-     return ThrowTypeError("Offset is out of bounds");
-   }
-   char a, b, c, d;
-   char* start = buffer->data_ + offset;
-   char* dst = start;
-   char* const dstEnd = dst + max_length;
-   const char* src = *s;
-   const char* const srcEnd = src + s.length();
-   while (src < srcEnd && dst < dstEnd) {
-     int remaining = srcEnd - src;
-     while (unbase64(*src) < 0 && src < srcEnd) src++, remaining--;
-     if (remaining == 0 || *src == '=') break;
-     a = unbase64(*src++);
-     while (unbase64(*src) < 0 && src < srcEnd) src++, remaining--;
-     if (remaining <= 1 || *src == '=') break;
-     b = unbase64(*src++);
-     *dst++ = (a << 2) | ((b & 0x30) >> 4);
-     if (dst == dstEnd) break;
-     while (unbase64(*src) < 0 && src < srcEnd) src++, remaining--;
-     if (remaining <= 2 || *src == '=') break;
-     c = unbase64(*src++);
-     *dst++ = ((b & 0x0F) << 4) | ((c & 0x3C) >> 2);
-     if (dst == dstEnd) break;
-     while (unbase64(*src) < 0 && src < srcEnd) src++, remaining--;
-     if (remaining <= 3 || *src == '=') break;
-     d = unbase64(*src++);
-     *dst++ = ((c & 0x03) << 6) | (d & 0x3F);
-   }
-   return scope.Close(Integer::New(dst - start, node_isolate));
- }
- Handle<Value> Buffer::BinaryWrite(const Arguments &args) {
-   HandleScope scope(node_isolate);
-   Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());
+   max_length = MIN(buffer->length_ - offset, max_length);
  
-   if (!args[0]->IsString()) {
-     return ThrowTypeError("Argument must be a string");
+   if (max_length == 0) {
+     // shortcut: nothing to write anyway
+     Local<Integer> val = Integer::New(0);
 -    constructor_template->GetFunction()->Set(chars_written_sym, val);
+     return scope.Close(val);
    }
  
-   Local<String> s = args[0]->ToString();
-   size_t length = s->Length();
-   size_t offset = args[1]->Int32Value();
+   if (encoding == UCS2)
+     max_length = max_length / 2;
  
-   if (s->Length() > 0 && offset >= buffer->length_) {
+   if (offset >= buffer->length_) {
      return ThrowTypeError("Offset is out of bounds");
    }
  
-   char *p = (char*)buffer->data_ + offset;
-   size_t max_length = args[2]->IsUndefined() ? buffer->length_ - offset
-                                              : args[2]->Uint32Value();
-   max_length = MIN(length, MIN(buffer->length_ - offset, max_length));
-   int written = DecodeWrite(p, max_length, s, BINARY);
+   char* start = buffer->data_ + offset;
 -  int chars_written;
+   size_t written = StringBytes::Write(start,
+                                       max_length,
+                                       str,
+                                       encoding,
 -                                      &chars_written);
 -
 -  constructor_template->GetFunction()->Set(chars_written_sym,
 -                                           Integer::New(chars_written));
++                                      NULL);
  
 -  return scope.Close(Integer::New(written));
 +  return scope.Close(Integer::New(written, node_isolate));
  }
  
  static bool is_big_endian() {
    const union { uint8_t u8[2]; uint16_t u16; } u = {{0, 1}};
    return u.u16 == 1 ? true : false;
@@@ -961,7 -509,7 +505,7 @@@ Handle<Value> Buffer::ByteLength(const 
    Local<String> s = args[0]->ToString();
    enum encoding e = ParseEncoding(args[1], UTF8);
  
-   return scope.Close(Integer::New(node::ByteLength(s, e), node_isolate));
 -  return scope.Close(Integer::New(StringBytes::Size(s, e)));
++  return scope.Close(Integer::New(StringBytes::Size(s, e), node_isolate));
  }
  
  
@@@ -1079,23 -626,13 +623,12 @@@ RetainedObjectInfo* WrapperInfo(uint16_
  
  
  void Buffer::Initialize(Handle<Object> target) {
 -  HandleScope scope;
 +  HandleScope scope(node_isolate);
  
-   // sanity checks
-   assert(unbase64('/') == 63);
-   assert(unbase64('+') == 62);
-   assert(unbase64('T') == 19);
-   assert(unbase64('Z') == 25);
-   assert(unbase64('t') == 45);
-   assert(unbase64('z') == 51);
-   assert(unbase64(' ') == -2);
-   assert(unbase64('\n') == -2);
-   assert(unbase64('\r') == -2);
    length_symbol = NODE_PSYMBOL("length");
 -  chars_written_sym = NODE_PSYMBOL("_charsWritten");
  
    Local<FunctionTemplate> t = FunctionTemplate::New(Buffer::New);
 -  constructor_template = Persistent<FunctionTemplate>::New(t);
 +  constructor_template = Persistent<FunctionTemplate>::New(node_isolate, t);
    constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
    constructor_template->SetClassName(String::NewSymbol("SlowBuffer"));
  
@@@ -2048,1204 -2031,1716 +2055,1285 @@@ Handle<Value> Connection::SetSNICallbac
  #endif
  
  
 -class Cipher : public ObjectWrap {
 - public:
 -  static void Initialize (v8::Handle<v8::Object> target) {
 -    HandleScope scope;
 -
 -    Local<FunctionTemplate> t = FunctionTemplate::New(New);
 +void CipherBase::Initialize(Handle<Object> target) {
 +  HandleScope scope(node_isolate);
  
 -    t->InstanceTemplate()->SetInternalFieldCount(1);
 +  Local<FunctionTemplate> t = FunctionTemplate::New(New);
  
 -    NODE_SET_PROTOTYPE_METHOD(t, "init", CipherInit);
 -    NODE_SET_PROTOTYPE_METHOD(t, "initiv", CipherInitIv);
 -    NODE_SET_PROTOTYPE_METHOD(t, "update", CipherUpdate);
 -    NODE_SET_PROTOTYPE_METHOD(t, "setAutoPadding", SetAutoPadding);
 -    NODE_SET_PROTOTYPE_METHOD(t, "final", CipherFinal);
 -
 -    target->Set(String::NewSymbol("Cipher"), t->GetFunction());
 -  }
 +  t->InstanceTemplate()->SetInternalFieldCount(1);
  
 +  NODE_SET_PROTOTYPE_METHOD(t, "init", Init);
 +  NODE_SET_PROTOTYPE_METHOD(t, "initiv", InitIv);
 +  NODE_SET_PROTOTYPE_METHOD(t, "update", Update);
 +  NODE_SET_PROTOTYPE_METHOD(t, "final", Final);
 +  NODE_SET_PROTOTYPE_METHOD(t, "setAutoPadding", SetAutoPadding);
  
 -  bool CipherInit(char* cipherType, char* key_buf, int key_buf_len) {
 -    cipher = EVP_get_cipherbyname(cipherType);
 -    if(!cipher) {
 -      fprintf(stderr, "node-crypto : Unknown cipher %s\n", cipherType);
 -      return false;
 -    }
 +  target->Set(String::NewSymbol("CipherBase"), t->GetFunction());
 +}
  
 -    unsigned char key[EVP_MAX_KEY_LENGTH],iv[EVP_MAX_IV_LENGTH];
 -    int key_len = EVP_BytesToKey(cipher, EVP_md5(), NULL,
 -      (unsigned char*) key_buf, key_buf_len, 1, key, iv);
  
 -    EVP_CIPHER_CTX_init(&ctx);
 -    EVP_CipherInit_ex(&ctx, cipher, NULL, NULL, NULL, true);
 -    if (!EVP_CIPHER_CTX_set_key_length(&ctx, key_len)) {
 -      fprintf(stderr, "node-crypto : Invalid key length %d\n", key_len);
 -      EVP_CIPHER_CTX_cleanup(&ctx);
 -      return false;
 -    }
 -    EVP_CipherInit_ex(&ctx, NULL, NULL,
 -      (unsigned char*)key,
 -      (unsigned char*)iv, true);
 -    initialised_ = true;
 -    return true;
 -  }
 -
 -
 -  bool CipherInitIv(char* cipherType,
 -                    char* key,
 -                    int key_len,
 -                    char* iv,
 -                    int iv_len) {
 -    cipher = EVP_get_cipherbyname(cipherType);
 -    if(!cipher) {
 -      fprintf(stderr, "node-crypto : Unknown cipher %s\n", cipherType);
 -      return false;
 -    }
 -    /* OpenSSL versions up to 0.9.8l failed to return the correct
 -       iv_length (0) for ECB ciphers */
 -    if (EVP_CIPHER_iv_length(cipher) != iv_len &&
 -      !(EVP_CIPHER_mode(cipher) == EVP_CIPH_ECB_MODE && iv_len == 0)) {
 -      fprintf(stderr, "node-crypto : Invalid IV length %d\n", iv_len);
 -      return false;
 -    }
 -    EVP_CIPHER_CTX_init(&ctx);
 -    EVP_CipherInit_ex(&ctx, cipher, NULL, NULL, NULL, true);
 -    if (!EVP_CIPHER_CTX_set_key_length(&ctx, key_len)) {
 -      fprintf(stderr, "node-crypto : Invalid key length %d\n", key_len);
 -      EVP_CIPHER_CTX_cleanup(&ctx);
 -      return false;
 -    }
 -    EVP_CipherInit_ex(&ctx, NULL, NULL,
 -      (unsigned char*)key,
 -      (unsigned char*)iv, true);
 -    initialised_ = true;
 -    return true;
 -  }
 +Handle<Value> CipherBase::New(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -  int CipherUpdate(char* data, int len, unsigned char** out, int* out_len) {
 -    if (!initialised_) return 0;
 -    *out_len = len+EVP_CIPHER_CTX_block_size(&ctx);
 -    *out = new unsigned char[*out_len];
 -    return EVP_CipherUpdate(&ctx, *out, out_len, (unsigned char*)data, len);
 -  }
 +  CipherBase* cipher = new CipherBase(args[0]->IsTrue() ? kCipher : kDecipher);
 +  cipher->Wrap(args.This());
 +  return args.This();
 +}
  
 -  int SetAutoPadding(bool auto_padding) {
 -    if (!initialised_) return 0;
 -    return EVP_CIPHER_CTX_set_padding(&ctx, auto_padding ? 1 : 0);
 -  }
 +Handle<Value> CipherBase::Init(char* cipher_type,
 +                               char* key_buf,
 +                               int key_buf_len) {
 +  HandleScope scope(node_isolate);
  
 -  int CipherFinal(unsigned char** out, int *out_len) {
 -    if (!initialised_) return 0;
 -    *out = new unsigned char[EVP_CIPHER_CTX_block_size(&ctx)];
 -    int r = EVP_CipherFinal_ex(&ctx,*out, out_len);
 -    EVP_CIPHER_CTX_cleanup(&ctx);
 -    initialised_ = false;
 -    return r;
 +  assert(cipher_ == NULL);
 +  cipher_ = EVP_get_cipherbyname(cipher_type);
 +  if (cipher_ == NULL) {
 +    return ThrowError("Unknown cipher");
    }
  
 +  unsigned char key[EVP_MAX_KEY_LENGTH];
 +  unsigned char iv[EVP_MAX_IV_LENGTH];
  
 - protected:
 -
 -  static Handle<Value> New(const Arguments& args) {
 -    HandleScope scope;
 +  int key_len = EVP_BytesToKey(cipher_,
 +                               EVP_md5(),
 +                               NULL,
 +                               reinterpret_cast<unsigned char*>(key_buf),
 +                               key_buf_len,
 +                               1,
 +                               key,
 +                               iv);
  
 -    Cipher *cipher = new Cipher();
 -    cipher->Wrap(args.This());
 -    return args.This();
 +  EVP_CIPHER_CTX_init(&ctx_);
 +  EVP_CipherInit_ex(&ctx_, cipher_, NULL, NULL, NULL, kind_ == kCipher);
 +  if (!EVP_CIPHER_CTX_set_key_length(&ctx_, key_len)) {
 +    EVP_CIPHER_CTX_cleanup(&ctx_);
 +    return ThrowError("Invalid key length");
    }
  
 -  static Handle<Value> CipherInit(const Arguments& args) {
 -    HandleScope scope;
 -
 -    Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
 -
 -    if (args.Length() <= 1
 -        || !args[0]->IsString()
 -        || !(args[1]->IsString() || Buffer::HasInstance(args[1])))
 -    {
 -      return ThrowException(Exception::Error(String::New(
 -        "Must give cipher-type, key")));
 -    }
 +  EVP_CipherInit_ex(&ctx_,
 +                    NULL,
 +                    NULL,
 +                    reinterpret_cast<unsigned char*>(key),
 +                    reinterpret_cast<unsigned char*>(iv),
 +                    kind_ == kCipher);
 +  initialised_ = true;
 +  return Null(node_isolate);
 +}
  
 -    ASSERT_IS_BUFFER(args[1]);
 -    ssize_t key_buf_len = Buffer::Length(args[1]);
  
 -    if (key_buf_len < 0) {
 -      Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
 -      return ThrowException(exception);
 -    }
 +Handle<Value> CipherBase::Init(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -    char* key_buf = new char[key_buf_len];
 -    ssize_t key_written = DecodeWrite(key_buf, key_buf_len, args[1], BINARY);
 -    assert(key_written == key_buf_len);
 +  CipherBase* cipher = ObjectWrap::Unwrap<CipherBase>(args.This());
  
 -    String::Utf8Value cipherType(args[0]);
 -
 -    bool r = cipher->CipherInit(*cipherType, key_buf, key_buf_len);
 +  if (args.Length() < 2 ||
 +      !(args[0]->IsString() && Buffer::HasInstance(args[1]))) {
 +    return ThrowError("Must give cipher-type, key");
 +  }
  
 -    delete [] key_buf;
 +  String::Utf8Value cipher_type(args[0]);
 +  char* key_buf = Buffer::Data(args[1]);
 +  ssize_t key_buf_len = Buffer::Length(args[1]);
  
 -    if (!r) return ThrowCryptoError(ERR_get_error());
 +  Handle<Value> ret = cipher->Init(*cipher_type, key_buf, key_buf_len);
  
 +  if (ret->IsNull()) {
      return args.This();
 +  } else {
 +    // Exception
 +    return scope.Close(ret);
    }
 +}
  
  
 -  static Handle<Value> CipherInitIv(const Arguments& args) {
 -    Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
 -
 -    HandleScope scope;
 -
 +Handle<Value> CipherBase::InitIv(char* cipher_type,
 +                                 char* key,
 +                                 int key_len,
 +                                 char* iv,
 +                                 int iv_len) {
 +  HandleScope scope(node_isolate);
  
 -    if (args.Length() <= 2
 -        || !args[0]->IsString()
 -        || !(args[1]->IsString() || Buffer::HasInstance(args[1]))
 -        || !(args[2]->IsString() || Buffer::HasInstance(args[2])))
 -    {
 -      return ThrowException(Exception::Error(String::New(
 -        "Must give cipher-type, key, and iv as argument")));
 -    }
 +  cipher_ = EVP_get_cipherbyname(cipher_type);
 +  if (cipher_ == NULL) {
 +    return ThrowError("Unknown cipher");
 +  }
  
 -    ASSERT_IS_BUFFER(args[1]);
 -    ssize_t key_len = Buffer::Length(args[1]);
 +  /* OpenSSL versions up to 0.9.8l failed to return the correct
 +     iv_length (0) for ECB ciphers */
 +  if (EVP_CIPHER_iv_length(cipher_) != iv_len &&
 +      !(EVP_CIPHER_mode(cipher_) == EVP_CIPH_ECB_MODE && iv_len == 0)) {
 +    return ThrowError("Invalid IV length");
 +  }
 +  EVP_CIPHER_CTX_init(&ctx_);
 +  EVP_CipherInit_ex(&ctx_, cipher_, NULL, NULL, NULL, kind_ == kCipher);
 +  if (!EVP_CIPHER_CTX_set_key_length(&ctx_, key_len)) {
 +    EVP_CIPHER_CTX_cleanup(&ctx_);
 +    return ThrowError("Invalid key length");
 +  }
  
 -    if (key_len < 0) {
 -      Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
 -      return ThrowException(exception);
 -    }
 +  EVP_CipherInit_ex(&ctx_,
 +                    NULL,
 +                    NULL,
 +                    reinterpret_cast<unsigned char*>(key),
 +                    reinterpret_cast<unsigned char*>(iv),
 +                    kind_ == kCipher);
 +  initialised_ = true;
 +  return Null(node_isolate);
 +}
  
 -    ASSERT_IS_BUFFER(args[2]);
 -    ssize_t iv_len = Buffer::Length(args[2]);
  
 -    if (iv_len < 0) {
 -      Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
 -      return ThrowException(exception);
 -    }
 +Handle<Value> CipherBase::InitIv(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -    char* key_buf = new char[key_len];
 -    ssize_t key_written = DecodeWrite(key_buf, key_len, args[1], BINARY);
 -    assert(key_written == key_len);
 +  CipherBase* cipher = ObjectWrap::Unwrap<CipherBase>(args.This());
  
 -    char* iv_buf = new char[iv_len];
 -    ssize_t iv_written = DecodeWrite(iv_buf, iv_len, args[2], BINARY);
 -    assert(iv_written == iv_len);
 -
 -    String::Utf8Value cipherType(args[0]);
 +  if (args.Length() < 3 || !args[0]->IsString()) {
 +    return ThrowError("Must give cipher-type, key, and iv as argument");
 +  }
  
 -    bool r = cipher->CipherInitIv(*cipherType, key_buf,key_len,iv_buf,iv_len);
 +  ASSERT_IS_BUFFER(args[1]);
 +  ASSERT_IS_BUFFER(args[2]);
  
 -    delete [] key_buf;
 -    delete [] iv_buf;
 +  String::Utf8Value cipher_type(args[0]);
 +  ssize_t key_len = Buffer::Length(args[1]);
 +  char* key_buf = Buffer::Data(args[1]);
 +  ssize_t iv_len = Buffer::Length(args[2]);
 +  char* iv_buf = Buffer::Data(args[2]);
  
 -    if (!r) return ThrowCryptoError(ERR_get_error());
 +  Handle<Value> ret = cipher->InitIv(*cipher_type,
 +                                     key_buf,
 +                                     key_len,
 +                                     iv_buf,
 +                                     iv_len);
  
 +  if (ret->IsNull()) {
      return args.This();
 +  } else {
 +    // Exception
 +    return scope.Close(ret);
    }
 +}
  
 -  static Handle<Value> CipherUpdate(const Arguments& args) {
 -    Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
  
 -    HandleScope scope;
 +bool CipherBase::Update(char* data,
 +                        int len,
 +                        unsigned char** out,
 +                        int* out_len) {
 +  if (!initialised_) return 0;
 +  *out_len = len + EVP_CIPHER_CTX_block_size(&ctx_);
 +  *out = new unsigned char[*out_len];
 +  return EVP_CipherUpdate(&ctx_,
 +                          *out,
 +                          out_len,
 +                          reinterpret_cast<unsigned char*>(data),
 +                          len);
 +}
  
 -    ASSERT_IS_STRING_OR_BUFFER(args[0]);
  
 -    // Only copy the data if we have to, because it's a string
 -    unsigned char* out = 0;
 -    int out_len = 0, r;
 -    if (args[0]->IsString()) {
 -      enum encoding encoding = ParseEncoding(args[1], BINARY);
 -      size_t buflen = StringBytes::StorageSize(args[0], encoding);
 -      char* buf = new char[buflen];
 -      size_t written = StringBytes::Write(buf, buflen, args[0], encoding);
 -      r = cipher->CipherUpdate(buf, written, &out, &out_len);
 -      delete[] buf;
 -    } else {
 -      char* buf = Buffer::Data(args[0]);
 -      size_t buflen = Buffer::Length(args[0]);
 -      r = cipher->CipherUpdate(buf, buflen, &out, &out_len);
 -    }
 +Handle<Value> CipherBase::Update(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -    if (r == 0) {
 -      delete[] out;
 -      return ThrowCryptoTypeError(ERR_get_error());
 -    }
 +  CipherBase* cipher = ObjectWrap::Unwrap<CipherBase>(args.This());
  
-   ASSERT_IS_BUFFER(args[0]);
 -    Local<Value> outString;
 -    outString = Encode(out, out_len, BUFFER);
++  ASSERT_IS_STRING_OR_BUFFER(args[0]);
  
 -    if (out) delete[] out;
 +  unsigned char* out = NULL;
 +  bool r;
 +  int out_len = 0;
-   char* buffer_data = Buffer::Data(args[0]);
-   size_t buffer_length = Buffer::Length(args[0]);
  
-   r = cipher->Update(buffer_data, buffer_length, &out, &out_len);
 -    return scope.Close(outString);
++  // Only copy the data if we have to, because it's a string
++  if (args[0]->IsString()) {
++    enum encoding encoding = ParseEncoding(args[1], BINARY);
++    size_t buflen = StringBytes::StorageSize(args[0], encoding);
++    char* buf = new char[buflen];
++    size_t written = StringBytes::Write(buf, buflen, args[0], encoding);
++    r = cipher->Update(buf, written, &out, &out_len);
++    delete[] buf;
++  } else {
++    char* buf = Buffer::Data(args[0]);
++    size_t buflen = Buffer::Length(args[0]);
++    r = cipher->Update(buf, buflen, &out, &out_len);
+   }
  
 -  static Handle<Value> SetAutoPadding(const Arguments& args) {
 -    HandleScope scope;
 -    Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
 -
 -    cipher->SetAutoPadding(args.Length() < 1 || args[0]->BooleanValue());
 -
 -    return Undefined();
 +  if (!r) {
 +    delete[] out;
 +    return ThrowCryptoTypeError(ERR_get_error());
    }
  
 -  static Handle<Value> CipherFinal(const Arguments& args) {
 -    Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
 -
 -    HandleScope scope;
 +  Buffer* buf = Buffer::New(reinterpret_cast<char*>(out), out_len);
  
 -    unsigned char* out_value = NULL;
 -    int out_len = -1;
 -    Local<Value> outString ;
++  if (out) delete[] out;
 -    int r = cipher->CipherFinal(&out_value, &out_len);
 +  return scope.Close(buf->handle_);
 +}
  
 -    if (out_len <= 0 || r == 0) {
 -      delete[] out_value;
 -      out_value = NULL;
 -      if (r == 0) return ThrowCryptoTypeError(ERR_get_error());
 -    }
  
 -    outString = Encode(out_value, out_len, BUFFER);
 +bool CipherBase::SetAutoPadding(bool auto_padding) {
 +  if (!initialised_) return false;
 +  return EVP_CIPHER_CTX_set_padding(&ctx_, auto_padding);
 +}
  
 -    delete [] out_value;
 -    return scope.Close(outString);
 -  }
  
 -  Cipher () : ObjectWrap ()
 -  {
 -    initialised_ = false;
 -  }
 +Handle<Value> CipherBase::SetAutoPadding(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -  ~Cipher () {
 -    if (initialised_) {
 -      EVP_CIPHER_CTX_cleanup(&ctx);
 -    }
 -  }
 +  CipherBase* cipher = ObjectWrap::Unwrap<CipherBase>(args.This());
  
 - private:
 +  cipher->SetAutoPadding(args.Length() < 1 || args[0]->BooleanValue());
  
 -  EVP_CIPHER_CTX ctx; /* coverity[member_decl] */
 -  const EVP_CIPHER *cipher; /* coverity[member_decl] */
 -  bool initialised_;
 -};
 +  return Undefined(node_isolate);
 +}
  
  
 +bool CipherBase::Final(unsigned char** out, int *out_len) {
 +  if (!initialised_) return false;
  
 -class Decipher : public ObjectWrap {
 - public:
 -  static void
 -  Initialize (v8::Handle<v8::Object> target)
 -  {
 -    HandleScope scope;
 +  *out = new unsigned char[EVP_CIPHER_CTX_block_size(&ctx_)];
 +  bool r = EVP_CipherFinal_ex(&ctx_, *out, out_len);
 +  EVP_CIPHER_CTX_cleanup(&ctx_);
 +  initialised_ = false;
  
 -    Local<FunctionTemplate> t = FunctionTemplate::New(New);
 +  return r;
 +}
  
 -    t->InstanceTemplate()->SetInternalFieldCount(1);
  
 -    NODE_SET_PROTOTYPE_METHOD(t, "init", DecipherInit);
 -    NODE_SET_PROTOTYPE_METHOD(t, "initiv", DecipherInitIv);
 -    NODE_SET_PROTOTYPE_METHOD(t, "update", DecipherUpdate);
 -    NODE_SET_PROTOTYPE_METHOD(t, "final", DecipherFinal);
 -    NODE_SET_PROTOTYPE_METHOD(t, "finaltol", DecipherFinal); // remove someday
 -    NODE_SET_PROTOTYPE_METHOD(t, "setAutoPadding", SetAutoPadding);
 +Handle<Value> CipherBase::Final(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -    target->Set(String::NewSymbol("Decipher"), t->GetFunction());
 -  }
 +  CipherBase* cipher = ObjectWrap::Unwrap<CipherBase>(args.This());
  
 -  bool DecipherInit(char* cipherType, char* key_buf, int key_buf_len) {
 -    cipher_ = EVP_get_cipherbyname(cipherType);
 +  unsigned char* out_value = NULL;
 +  int out_len = -1;
 +  Local<Value> outString;
  
 -    if(!cipher_) {
 -      fprintf(stderr, "node-crypto : Unknown cipher %s\n", cipherType);
 -      return false;
 -    }
 +  bool r = cipher->Final(&out_value, &out_len);
  
 -    unsigned char key[EVP_MAX_KEY_LENGTH],iv[EVP_MAX_IV_LENGTH];
 -    int key_len = EVP_BytesToKey(cipher_,
 -                                 EVP_md5(),
 -                                 NULL,
 -                                 (unsigned char*)(key_buf),
 -                                 key_buf_len,
 -                                 1,
 -                                 key,
 -                                 iv);
 -
 -    EVP_CIPHER_CTX_init(&ctx);
 -    EVP_CipherInit_ex(&ctx, cipher_, NULL, NULL, NULL, false);
 -    if (!EVP_CIPHER_CTX_set_key_length(&ctx, key_len)) {
 -      fprintf(stderr, "node-crypto : Invalid key length %d\n", key_len);
 -      EVP_CIPHER_CTX_cleanup(&ctx);
 -      return false;
 -    }
 -    EVP_CipherInit_ex(&ctx, NULL, NULL,
 -      (unsigned char*)key,
 -      (unsigned char*)iv, false);
 -    initialised_ = true;
 -    return true;
 -  }
 -
 -
 -  bool DecipherInitIv(char* cipherType,
 -                      char* key,
 -                      int key_len,
 -                      char* iv,
 -                      int iv_len) {
 -    cipher_ = EVP_get_cipherbyname(cipherType);
 -    if(!cipher_) {
 -      fprintf(stderr, "node-crypto : Unknown cipher %s\n", cipherType);
 -      return false;
 -    }
 -    /* OpenSSL versions up to 0.9.8l failed to return the correct
 -      iv_length (0) for ECB ciphers */
 -    if (EVP_CIPHER_iv_length(cipher_) != iv_len &&
 -      !(EVP_CIPHER_mode(cipher_) == EVP_CIPH_ECB_MODE && iv_len == 0)) {
 -      fprintf(stderr, "node-crypto : Invalid IV length %d\n", iv_len);
 -      return false;
 -    }
 -    EVP_CIPHER_CTX_init(&ctx);
 -    EVP_CipherInit_ex(&ctx, cipher_, NULL, NULL, NULL, false);
 -    if (!EVP_CIPHER_CTX_set_key_length(&ctx, key_len)) {
 -      fprintf(stderr, "node-crypto : Invalid key length %d\n", key_len);
 -      EVP_CIPHER_CTX_cleanup(&ctx);
 -      return false;
 -    }
 -    EVP_CipherInit_ex(&ctx, NULL, NULL,
 -      (unsigned char*)key,
 -      (unsigned char*)iv, false);
 -    initialised_ = true;
 -    return true;
 -  }
 -
 -  int DecipherUpdate(char* data, int len, unsigned char** out, int* out_len) {
 -    if (!initialised_) {
 -      *out_len = 0;
 -      *out = NULL;
 -      return 0;
 -    }
 -
 -    *out_len = len+EVP_CIPHER_CTX_block_size(&ctx);
 -    *out = new unsigned char[*out_len];
 -
 -    return EVP_CipherUpdate(&ctx, *out, out_len, (unsigned char*)data, len);
 +  if (out_len <= 0 || !r) {
 +    delete[] out_value;
 +    out_value = NULL;
 +    out_len = 0;
 +    if (!r) return ThrowCryptoTypeError(ERR_get_error());
    }
  
 -  int SetAutoPadding(bool auto_padding) {
 -    if (!initialised_) return 0;
 -    return EVP_CIPHER_CTX_set_padding(&ctx, auto_padding ? 1 : 0);
 -  }
 +  Buffer* buf = Buffer::New(reinterpret_cast<char*>(out_value), out_len);
  
 -  // coverity[alloc_arg]
 -  int DecipherFinal(unsigned char** out, int *out_len) {
 -    int r;
 +  return scope.Close(buf->handle_);
 +}
  
 -    if (!initialised_) {
 -      *out_len = 0;
 -      *out = NULL;
 -      return 0;
 -    }
  
 -    *out = new unsigned char[EVP_CIPHER_CTX_block_size(&ctx)];
 -    r = EVP_CipherFinal_ex(&ctx,*out,out_len);
 -    EVP_CIPHER_CTX_cleanup(&ctx);
 -    initialised_ = false;
 -    return r;
 -  }
 +void Hmac::Initialize(v8::Handle<v8::Object> target) {
 +  HandleScope scope(node_isolate);
  
 +  Local<FunctionTemplate> t = FunctionTemplate::New(New);
  
 - protected:
 +  t->InstanceTemplate()->SetInternalFieldCount(1);
  
 -  static Handle<Value> New (const Arguments& args) {
 -    HandleScope scope;
 +  NODE_SET_PROTOTYPE_METHOD(t, "init", HmacInit);
 +  NODE_SET_PROTOTYPE_METHOD(t, "update", HmacUpdate);
 +  NODE_SET_PROTOTYPE_METHOD(t, "digest", HmacDigest);
  
 -    Decipher *cipher = new Decipher();
 -    cipher->Wrap(args.This());
 -    return args.This();
 -  }
 +  target->Set(String::NewSymbol("Hmac"), t->GetFunction());
 +}
  
 -  static Handle<Value> DecipherInit(const Arguments& args) {
 -    Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
  
 -    HandleScope scope;
 +Handle<Value> Hmac::New(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -    if (args.Length() <= 1
 -        || !args[0]->IsString()
 -        || !(args[1]->IsString() || Buffer::HasInstance(args[1])))
 -    {
 -      return ThrowException(Exception::Error(String::New(
 -        "Must give cipher-type, key as argument")));
 -    }
 +  Hmac* hmac = new Hmac();
 +  hmac->Wrap(args.This());
 +  return args.This();
 +}
  
 -    ASSERT_IS_BUFFER(args[1]);
 -    ssize_t key_len = Buffer::Length(args[1]);
  
 -    if (key_len < 0) {
 -      Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
 -      return ThrowException(exception);
 -    }
 +Handle<Value> Hmac::HmacInit(char* hashType, char* key, int key_len) {
 +  HandleScope scope(node_isolate);
  
 -    char* key_buf = new char[key_len];
 -    ssize_t key_written = DecodeWrite(key_buf, key_len, args[1], BINARY);
 -    assert(key_written == key_len);
 +  assert(md_ == NULL);
 +  md_ = EVP_get_digestbyname(hashType);
 +  if (md_ == NULL) {
 +    return ThrowError("Unknown message digest");
 +  }
 +  HMAC_CTX_init(&ctx_);
 +  if (key_len == 0) {
 +    HMAC_Init(&ctx_, "", 0, md_);
 +  } else {
 +    HMAC_Init(&ctx_, key, key_len, md_);
 +  }
 +  initialised_ = true;
  
 -    String::Utf8Value cipherType(args[0]);
 +  return Null(node_isolate);
 +}
  
 -    bool r = cipher->DecipherInit(*cipherType, key_buf,key_len);
  
 -    delete [] key_buf;
 +Handle<Value> Hmac::HmacInit(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -    if (!r) {
 -      return ThrowException(Exception::Error(String::New("DecipherInit error")));
 -    }
 +  Hmac* hmac = ObjectWrap::Unwrap<Hmac>(args.This());
  
 -    return args.This();
 +  if (args.Length() < 2 || !args[0]->IsString()) {
 +    return ThrowError("Must give hashtype string, key as arguments");
    }
  
 -  static Handle<Value> DecipherInitIv(const Arguments& args) {
 -    Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
 -    HandleScope scope;
 +  ASSERT_IS_BUFFER(args[1]);
  
 -    if (args.Length() <= 2
 -        || !args[0]->IsString()
 -        || !(args[1]->IsString() || Buffer::HasInstance(args[1]))
 -        || !(args[2]->IsString() || Buffer::HasInstance(args[2])))
 -    {
 -      return ThrowException(Exception::Error(String::New(
 -        "Must give cipher-type, key, and iv as argument")));
 -    }
 +  String::Utf8Value hashType(args[0]);
  
 -    ASSERT_IS_BUFFER(args[1]);
 -    ssize_t key_len = Buffer::Length(args[1]);
 +  char* buffer_data = Buffer::Data(args[1]);
 +  size_t buffer_length = Buffer::Length(args[1]);
  
 -    if (key_len < 0) {
 -      Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
 -      return ThrowException(exception);
 -    }
 +  Handle<Value> ret = hmac->HmacInit(*hashType, buffer_data, buffer_length);
  
 -    ASSERT_IS_BUFFER(args[2]);
 -    ssize_t iv_len = Buffer::Length(args[2]);
 +  if (ret->IsNull()) {
 +    return args.This();
 +  } else {
 +    // Exception
 +    return ret;
 +  }
 +}
  
 -    if (iv_len < 0) {
 -      Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
 -      return ThrowException(exception);
 -    }
  
 -    char* key_buf = new char[key_len];
 -    ssize_t key_written = DecodeWrite(key_buf, key_len, args[1], BINARY);
 -    assert(key_written == key_len);
 +bool Hmac::HmacUpdate(char* data, int len) {
 +  if (!initialised_) return false;
 +  HMAC_Update(&ctx_, reinterpret_cast<unsigned char*>(data), len);
 +  return true;
 +}
  
 -    char* iv_buf = new char[iv_len];
 -    ssize_t iv_written = DecodeWrite(iv_buf, iv_len, args[2], BINARY);
 -    assert(iv_written == iv_len);
  
 -    String::Utf8Value cipherType(args[0]);
 +Handle<Value> Hmac::HmacUpdate(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -    bool r = cipher->DecipherInitIv(*cipherType, key_buf,key_len,iv_buf,iv_len);
 +  Hmac* hmac = ObjectWrap::Unwrap<Hmac>(args.This());
  
-   ASSERT_IS_BUFFER(args[0]);
 -    delete [] key_buf;
 -    delete [] iv_buf;
++  ASSERT_IS_STRING_OR_BUFFER(args[0]);
  
 -    if (!r) {
 -      return ThrowException(Exception::Error(String::New("DecipherInitIv error")));
 -    }
++  // Only copy the data if we have to, because it's a string
 +  bool r;
-   char* buffer_data = Buffer::Data(args[0]);
-   size_t buffer_length = Buffer::Length(args[0]);
-   r = hmac->HmacUpdate(buffer_data, buffer_length);
++  if (args[0]->IsString()) {
++    enum encoding encoding = ParseEncoding(args[1], BINARY);
++    size_t buflen = StringBytes::StorageSize(args[0], encoding);
++    char* buf = new char[buflen];
++    size_t written = StringBytes::Write(buf, buflen, args[0], encoding);
++    r = hmac->HmacUpdate(buf, written);
++    delete[] buf;
++  } else {
++    char* buf = Buffer::Data(args[0]);
++    size_t buflen = Buffer::Length(args[0]);
++    r = hmac->HmacUpdate(buf, buflen);
++  }
  
 -    return args.This();
 +  if (!r) {
 +    return ThrowTypeError("HmacUpdate fail");
    }
  
 -  static Handle<Value> DecipherUpdate(const Arguments& args) {
 -    HandleScope scope;
 +  return args.This();
 +}
  
 -    Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
  
 -    ASSERT_IS_STRING_OR_BUFFER(args[0]);
 +bool Hmac::HmacDigest(unsigned char** md_value, unsigned int* md_len) {
 +  if (!initialised_) return false;
 +  *md_value = new unsigned char[EVP_MAX_MD_SIZE];
 +  HMAC_Final(&ctx_, *md_value, md_len);
 +  HMAC_CTX_cleanup(&ctx_);
 +  initialised_ = false;
 +  return true;
 +}
  
 -    // Only copy the data if we have to, because it's a string
 -    unsigned char* out = 0;
 -    int out_len = 0, r;
 -    if (args[0]->IsString()) {
 -      enum encoding encoding = ParseEncoding(args[1], BINARY);
 -      size_t buflen = StringBytes::StorageSize(args[0], encoding);
 -      char* buf = new char[buflen];
 -      size_t written = StringBytes::Write(buf, buflen, args[0], encoding);
 -      r = cipher->DecipherUpdate(buf, written, &out, &out_len);
 -      delete[] buf;
 -    } else {
 -      char* buf = Buffer::Data(args[0]);
 -      size_t buflen = Buffer::Length(args[0]);
 -      r = cipher->DecipherUpdate(buf, buflen, &out, &out_len);
 -    }
  
 -    if (r == 0) {
 -      delete[] out;
 -      return ThrowCryptoTypeError(ERR_get_error());
 -    }
 +Handle<Value> Hmac::HmacDigest(const Arguments& args) {
 +  HandleScope scope(node_isolate);
 +
 +  Hmac* hmac = ObjectWrap::Unwrap<Hmac>(args.This());
  
 -    Local<Value> outString;
 -    outString = Encode(out, out_len, BUFFER);
++  enum encoding encoding = BUFFER;
++  if (args.Length() >= 1) {
++    encoding = ParseEncoding(args[0]->ToString(), BUFFER);
++  }
 -    if (out) delete [] out;
 +  unsigned char* md_value = NULL;
 +  unsigned int md_len = 0;
 +  Local<Value> outString;
  
 -    return scope.Close(outString);
 +  bool r = hmac->HmacDigest(&md_value, &md_len);
 +  if (!r) {
 +    md_value = NULL;
 +    md_len = 0;
    }
  
-   Buffer* buf = Buffer::New(reinterpret_cast<char*>(md_value), md_len);
 -  static Handle<Value> SetAutoPadding(const Arguments& args) {
 -    HandleScope scope;
 -    Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
++  outString = StringBytes::Encode(
++        reinterpret_cast<const char*>(md_value), md_len, encoding);
  
-   return scope.Close(buf->handle_);
 -    cipher->SetAutoPadding(args.Length() < 1 || args[0]->BooleanValue());
++  delete[] md_value;
++  return scope.Close(outString);
 +}
  
 -    return Undefined();
 -  }
  
 -  static Handle<Value> DecipherFinal(const Arguments& args) {
 -    HandleScope scope;
 +void Hash::Initialize(v8::Handle<v8::Object> target) {
 +  HandleScope scope(node_isolate);
  
 -    Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
 +  Local<FunctionTemplate> t = FunctionTemplate::New(New);
  
 -    unsigned char* out_value = NULL;
 -    int out_len = -1;
 -    Local<Value> outString;
 +  t->InstanceTemplate()->SetInternalFieldCount(1);
  
 -    int r = cipher->DecipherFinal(&out_value, &out_len);
 +  NODE_SET_PROTOTYPE_METHOD(t, "update", HashUpdate);
 +  NODE_SET_PROTOTYPE_METHOD(t, "digest", HashDigest);
  
 -    if (out_len <= 0 || r == 0) {
 -      delete [] out_value; // allocated even if out_len == 0
 -      out_value = NULL;
 -      if (r == 0) return ThrowCryptoTypeError(ERR_get_error());
 -    }
 +  target->Set(String::NewSymbol("Hash"), t->GetFunction());
 +}
  
 -    outString = Encode(out_value, out_len, BUFFER);
 -    delete [] out_value;
 -    return scope.Close(outString);
 -  }
  
 -  Decipher () : ObjectWrap () {
 -    initialised_ = false;
 -  }
 +Handle<Value> Hash::New(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -  ~Decipher () {
 -    if (initialised_) {
 -      EVP_CIPHER_CTX_cleanup(&ctx);
 -    }
 +  if (args.Length() == 0 || !args[0]->IsString()) {
 +    return ThrowError("Must give hashtype string as argument");
    }
  
 - private:
 +  String::Utf8Value hashType(args[0]);
  
 -  EVP_CIPHER_CTX ctx;
 -  const EVP_CIPHER *cipher_;
 -  bool initialised_;
 -};
 +  Hash* hash = new Hash();
 +  if (!hash->HashInit(*hashType)) {
 +    delete hash;
 +    return ThrowError("Digest method not supported");
 +  }
  
 +  hash->Wrap(args.This());
 +  return args.This();
 +}
  
  
 +bool Hash::HashInit(const char* hashType) {
 +  assert(md_ == NULL);
 +  md_ = EVP_get_digestbyname(hashType);
 +  if (md_ == NULL) return false;
 +  EVP_MD_CTX_init(&mdctx_);
 +  EVP_DigestInit_ex(&mdctx_, md_, NULL);
 +  initialised_ = true;
 +  return true;
 +}
  
 -class Hmac : public ObjectWrap {
 - public:
 -  static void Initialize (v8::Handle<v8::Object> target) {
 -    HandleScope scope;
  
 -    Local<FunctionTemplate> t = FunctionTemplate::New(New);
 +bool Hash::HashUpdate(char* data, int len) {
 +  if (!initialised_) return false;
 +  EVP_DigestUpdate(&mdctx_, data, len);
 +  return true;
 +}
  
 -    t->InstanceTemplate()->SetInternalFieldCount(1);
  
 -    NODE_SET_PROTOTYPE_METHOD(t, "init", HmacInit);
 -    NODE_SET_PROTOTYPE_METHOD(t, "update", HmacUpdate);
 -    NODE_SET_PROTOTYPE_METHOD(t, "digest", HmacDigest);
 +Handle<Value> Hash::HashUpdate(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -    target->Set(String::NewSymbol("Hmac"), t->GetFunction());
 -  }
 +  Hash* hash = ObjectWrap::Unwrap<Hash>(args.This());
  
-   ASSERT_IS_BUFFER(args[0]);
 -  bool HmacInit(char* hashType, char* key, int key_len) {
 -    md = EVP_get_digestbyname(hashType);
 -    if(!md) {
 -      fprintf(stderr, "node-crypto : Unknown message digest %s\n", hashType);
 -      return false;
 -    }
 -    HMAC_CTX_init(&ctx);
 -    if (key_len == 0) {
 -      HMAC_Init(&ctx, "", 0, md);
 -    } else {
 -      HMAC_Init(&ctx, key, key_len, md);
 -    }
 -    initialised_ = true;
 -    return true;
++  ASSERT_IS_STRING_OR_BUFFER(args[0]);
  
-   char* buffer_data = Buffer::Data(args[0]);
-   size_t buffer_length = Buffer::Length(args[0]);
-   r = hash->HashUpdate(buffer_data, buffer_length);
++  // Only copy the data if we have to, because it's a string
 +  bool r;
++  if (args[0]->IsString()) {
++    enum encoding encoding = ParseEncoding(args[1], BINARY);
++    size_t buflen = StringBytes::StorageSize(args[0], encoding);
++    char* buf = new char[buflen];
++    size_t written = StringBytes::Write(buf, buflen, args[0], encoding);
++    r = hash->HashUpdate(buf, written);
++    delete[] buf;
++  } else {
++    char* buf = Buffer::Data(args[0]);
++    size_t buflen = Buffer::Length(args[0]);
++    r = hash->HashUpdate(buf, buflen);
+   }
  
 -  int HmacUpdate(char* data, int len) {
 -    if (!initialised_) return 0;
 -    HMAC_Update(&ctx, (unsigned char*)data, len);
 -    return 1;
 +  if (!r) {
 +    return ThrowTypeError("HashUpdate fail");
    }
  
 -  int HmacDigest(unsigned char** md_value, unsigned int *md_len) {
 -    if (!initialised_) return 0;
 -    *md_value = new unsigned char[EVP_MAX_MD_SIZE];
 -    HMAC_Final(&ctx, *md_value, md_len);
 -    HMAC_CTX_cleanup(&ctx);
 -    initialised_ = false;
 -    return 1;
 -  }
 +  return args.This();
 +}
  
  
 - protected:
 +Handle<Value> Hash::HashDigest(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -  static Handle<Value> New (const Arguments& args) {
 -    HandleScope scope;
 +  Hash* hash = ObjectWrap::Unwrap<Hash>(args.This());
  
 -    Hmac *hmac = new Hmac();
 -    hmac->Wrap(args.This());
 -    return args.This();
 +  if (!hash->initialised_) {
 +    return ThrowError("Not initialized");
    }
  
 -  static Handle<Value> HmacInit(const Arguments& args) {
 -    Hmac *hmac = ObjectWrap::Unwrap<Hmac>(args.This());
 -
 -    HandleScope scope;
++  enum encoding encoding = BUFFER;
++  if (args.Length() >= 1) {
++    encoding = ParseEncoding(args[0]->ToString(), BUFFER);
++  }
 -    if (args.Length() == 0 || !args[0]->IsString()) {
 -      return ThrowException(Exception::Error(String::New(
 -        "Must give hashtype string as argument")));
 -    }
 +  unsigned char md_value[EVP_MAX_MD_SIZE];
 +  unsigned int md_len;
  
 -    ASSERT_IS_BUFFER(args[1]);
 -    ssize_t len = Buffer::Length(args[1]);
 +  EVP_DigestFinal_ex(&hash->mdctx_, md_value, &md_len);
 +  EVP_MD_CTX_cleanup(&hash->mdctx_);
 +  hash->initialised_ = false;
  
-   Buffer* buf = Buffer::New(reinterpret_cast<char*>(md_value), md_len);
 -    if (len < 0) {
 -      Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
 -      return ThrowException(exception);
 -    }
--
-   return scope.Close(buf->handle_);
 -    String::Utf8Value hashType(args[0]);
++  return scope.Close(StringBytes::Encode(
++        reinterpret_cast<const char*>(md_value), md_len, encoding));
 +}
  
 -    bool r;
  
 -    if( Buffer::HasInstance(args[1])) {
 -      char* buffer_data = Buffer::Data(args[1]);
 -      size_t buffer_length = Buffer::Length(args[1]);
 +void Sign::Initialize(v8::Handle<v8::Object> target) {
 +  HandleScope scope(node_isolate);
  
 -      r = hmac->HmacInit(*hashType, buffer_data, buffer_length);
 -    } else {
 -      char* buf = new char[len];
 -      ssize_t written = DecodeWrite(buf, len, args[1], BINARY);
 -      assert(written == len);
 +  Local<FunctionTemplate> t = FunctionTemplate::New(New);
  
 -      r = hmac->HmacInit(*hashType, buf, len);
 +  t->InstanceTemplate()->SetInternalFieldCount(1);
  
 -      delete [] buf;
 -    }
 +  NODE_SET_PROTOTYPE_METHOD(t, "init", SignInit);
 +  NODE_SET_PROTOTYPE_METHOD(t, "update", SignUpdate);
 +  NODE_SET_PROTOTYPE_METHOD(t, "sign", SignFinal);
  
 -    if (!r) {
 -      return ThrowException(Exception::Error(String::New("hmac error")));
 -    }
 -
 -    return args.This();
 -  }
 +  target->Set(String::NewSymbol("Sign"), t->GetFunction());
 +}
  
 -  static Handle<Value> HmacUpdate(const Arguments& args) {
 -    Hmac *hmac = ObjectWrap::Unwrap<Hmac>(args.This());
  
 -    HandleScope scope;
 +Handle<Value> Sign::New(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -    ASSERT_IS_STRING_OR_BUFFER(args[0]);
 +  Sign* sign = new Sign();
 +  sign->Wrap(args.This());
  
 -    // Only copy the data if we have to, because it's a string
 -    int r;
 -    if (args[0]->IsString()) {
 -      enum encoding encoding = ParseEncoding(args[1], BINARY);
 -      size_t buflen = StringBytes::StorageSize(args[0], encoding);
 -      char* buf = new char[buflen];
 -      size_t written = StringBytes::Write(buf, buflen, args[0], encoding);
 -      r = hmac->HmacUpdate(buf, written);
 -      delete[] buf;
 -    } else {
 -      char* buf = Buffer::Data(args[0]);
 -      size_t buflen = Buffer::Length(args[0]);
 -      r = hmac->HmacUpdate(buf, buflen);
 -    }
 +  return args.This();
 +}
  
 -    if (!r) {
 -      Local<Value> exception = Exception::TypeError(String::New("HmacUpdate fail"));
 -      return ThrowException(exception);
 -    }
 +Handle<Value> Sign::SignInit(const char* sign_type) {
 +  HandleScope scope(node_isolate);
  
 -    return args.This();
 +  assert(md_ == NULL);
 +  md_ = EVP_get_digestbyname(sign_type);
 +  if (!md_) {
 +    return ThrowError("Uknown message digest");
    }
 +  EVP_MD_CTX_init(&mdctx_);
 +  EVP_SignInit_ex(&mdctx_, md_, NULL);
 +  initialised_ = true;
 +  return Null(node_isolate);
 +}
  
 -  static Handle<Value> HmacDigest(const Arguments& args) {
 -    Hmac *hmac = ObjectWrap::Unwrap<Hmac>(args.This());
 -
 -    HandleScope scope;
 -
 -    enum encoding encoding = BUFFER;
 -    if (args.Length() >= 1) {
 -      encoding = ParseEncoding(args[0]->ToString(), BUFFER);
 -    }
 -
 -    unsigned char* md_value = NULL;
 -    unsigned int md_len = 0;
 -    Local<Value> outString;
  
 -    int r = hmac->HmacDigest(&md_value, &md_len);
 -    if (r == 0) {
 -      md_value = NULL;
 -      md_len = 0;
 -    }
 +Handle<Value> Sign::SignInit(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -    outString = StringBytes::Encode(
 -          reinterpret_cast<const char*>(md_value), md_len, encoding);
 +  Sign* sign = ObjectWrap::Unwrap<Sign>(args.This());
  
 -    delete[] md_value;
 -    return scope.Close(outString);
 +  if (args.Length() == 0 || !args[0]->IsString()) {
 +    return ThrowError("Must give signtype string as argument");
    }
  
 -  Hmac () : ObjectWrap () {
 -    initialised_ = false;
 -  }
 +  String::Utf8Value sign_type(args[0]);
  
 -  ~Hmac () {
 -    if (initialised_) {
 -      HMAC_CTX_cleanup(&ctx);
 -    }
 -  }
 +  Handle<Value> ret = sign->SignInit(*sign_type);
  
 - private:
 +  if (ret->IsNull()) {
 +    return args.This();
 +  } else {
 +    // Exception
 +    return scope.Close(ret);
 +  }
 +}
  
 -  HMAC_CTX ctx; /* coverity[member_decl] */
 -  const EVP_MD *md; /* coverity[member_decl] */
 -  bool initialised_;
 -};
  
 +bool Sign::SignUpdate(char* data, int len) {
 +  if (!initialised_) return false;
 +  EVP_SignUpdate(&mdctx_, data, len);
 +  return true;
 +}
  
 -class Hash : public ObjectWrap {
 - public:
 -  static void Initialize (v8::Handle<v8::Object> target) {
 -    HandleScope scope;
  
 -    Local<FunctionTemplate> t = FunctionTemplate::New(New);
 +Handle<Value> Sign::SignUpdate(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -    t->InstanceTemplate()->SetInternalFieldCount(1);
 +  Sign* sign = ObjectWrap::Unwrap<Sign>(args.This());
  
-   ASSERT_IS_BUFFER(args[0]);
-   bool r;
-   char* buffer_data = Buffer::Data(args[0]);
-   size_t buffer_length = Buffer::Length(args[0]);
 -    NODE_SET_PROTOTYPE_METHOD(t, "update", HashUpdate);
 -    NODE_SET_PROTOTYPE_METHOD(t, "digest", HashDigest);
++  ASSERT_IS_STRING_OR_BUFFER(args[0]);
  
-   r = sign->SignUpdate(buffer_data, buffer_length);
 -    target->Set(String::NewSymbol("Hash"), t->GetFunction());
++  // Only copy the data if we have to, because it's a string
++  int r;
++  if (args[0]->IsString()) {
++    enum encoding encoding = ParseEncoding(args[1], BINARY);
++    size_t buflen = StringBytes::StorageSize(args[0], encoding);
++    char* buf = new char[buflen];
++    size_t written = StringBytes::Write(buf, buflen, args[0], encoding);
++    r = sign->SignUpdate(buf, written);
++    delete[] buf;
++  } else {
++    char* buf = Buffer::Data(args[0]);
++    size_t buflen = Buffer::Length(args[0]);
++    r = sign->SignUpdate(buf, buflen);
+   }
  
 -  bool HashInit (const char* hashType) {
 -    md = EVP_get_digestbyname(hashType);
 -    if(!md) return false;
 -    EVP_MD_CTX_init(&mdctx);
 -    EVP_DigestInit_ex(&mdctx, md, NULL);
 -    initialised_ = true;
 -    return true;
 +  if (!r) {
 +    return ThrowTypeError("SignUpdate fail");
    }
  
 -  int HashUpdate(char* data, int len) {
 -    if (!initialised_) return 0;
 -    EVP_DigestUpdate(&mdctx, data, len);
 -    return 1;
 -  }
 +  return args.This();
 +}
  
  
 - protected:
 +bool Sign::SignFinal(unsigned char** md_value,
 +                     unsigned int *md_len,
 +                     char* key_pem,
 +                     int key_pem_len) {
 +  if (!initialised_) return false;
  
 -  static Handle<Value> New (const Arguments& args) {
 -    HandleScope scope;
 +  BIO* bp = NULL;
 +  EVP_PKEY* pkey = NULL;
 +  bp = BIO_new(BIO_s_mem());
 +  if (!BIO_write(bp, key_pem, key_pem_len)) return false;
  
 -    if (args.Length() == 0 || !args[0]->IsString()) {
 -      return ThrowException(Exception::Error(String::New(
 -        "Must give hashtype string as argument")));
 -    }
 +  pkey = PEM_read_bio_PrivateKey(bp, NULL, NULL, NULL);
 +  if (pkey == NULL) return 0;
  
 -    String::Utf8Value hashType(args[0]);
 +  EVP_SignFinal(&mdctx_, *md_value, md_len, pkey);
 +  EVP_MD_CTX_cleanup(&mdctx_);
 +  initialised_ = false;
 +  EVP_PKEY_free(pkey);
 +  BIO_free_all(bp);
 +  return true;
 +}
  
 -    Hash *hash = new Hash();
 -    if (!hash->HashInit(*hashType)) {
 -      delete hash;
 -      return ThrowException(Exception::Error(String::New(
 -        "Digest method not supported")));
 -    }
  
 -    hash->Wrap(args.This());
 -    return args.This();
 -  }
 +Handle<Value> Sign::SignFinal(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -  static Handle<Value> HashUpdate(const Arguments& args) {
 -    HandleScope scope;
 +  Sign* sign = ObjectWrap::Unwrap<Sign>(args.This());
  
 -    Hash *hash = ObjectWrap::Unwrap<Hash>(args.This());
 +  unsigned char* md_value;
 +  unsigned int md_len;
 +  Local<Value> outString;
  
 -    ASSERT_IS_STRING_OR_BUFFER(args[0]);
++  enum encoding encoding = BUFFER;
++  if (args.Length() >= 2) {
++    encoding = ParseEncoding(args[1]->ToString(), BUFFER);
++  }
 -    // Only copy the data if we have to, because it's a string
 -    int r;
 -    if (args[0]->IsString()) {
 -      enum encoding encoding = ParseEncoding(args[1], BINARY);
 -      size_t buflen = StringBytes::StorageSize(args[0], encoding);
 -      char* buf = new char[buflen];
 -      size_t written = StringBytes::Write(buf, buflen, args[0], encoding);
 -      r = hash->HashUpdate(buf, written);
 -      delete[] buf;
 -    } else {
 -      char* buf = Buffer::Data(args[0]);
 -      size_t buflen = Buffer::Length(args[0]);
 -      r = hash->HashUpdate(buf, buflen);
 -    }
 +  ASSERT_IS_BUFFER(args[0]);
 +  ssize_t len = Buffer::Length(args[0]);
 +  char* buf = Buffer::Data(args[0]);
  
 -    if (!r) {
 -      Local<Value> exception = Exception::TypeError(String::New("HashUpdate fail"));
 -      return ThrowException(exception);
 -    }
 +  md_len = 8192; // Maximum key size is 8192 bits
 +  md_value = new unsigned char[md_len];
  
 -    return args.This();
 +  bool r = sign->SignFinal(&md_value, &md_len, buf, len);
 +  if (!r) {
 +    delete[] md_value;
 +    md_value = NULL;
 +    md_len = 0;
    }
  
-   Buffer* ret = Buffer::New(reinterpret_cast<char*>(md_value), md_len);
-   delete[] md_value;
 -  static Handle<Value> HashDigest(const Arguments& args) {
 -    HandleScope scope;
++  outString = StringBytes::Encode(
++      reinterpret_cast<const char*>(md_value), md_len, encoding);
  
-   return scope.Close(ret->handle_);
 -    Hash *hash = ObjectWrap::Unwrap<Hash>(args.This());
 -
 -    if (!hash->initialised_) {
 -      return ThrowException(Exception::Error(String::New("Not initialized")));
 -    }
++  delete[] md_value;
++  return scope.Close(outString);
 +}
  
 -    enum encoding encoding = BUFFER;
 -    if (args.Length() >= 1) {
 -      encoding = ParseEncoding(args[0]->ToString(), BUFFER);
 -    }
  
 -    unsigned char md_value[EVP_MAX_MD_SIZE];
 -    unsigned int md_len;
 +void Verify::Initialize(v8::Handle<v8::Object> target) {
 +  HandleScope scope(node_isolate);
  
 -    EVP_DigestFinal_ex(&hash->mdctx, md_value, &md_len);
 -    EVP_MD_CTX_cleanup(&hash->mdctx);
 -    hash->initialised_ = false;
 +  Local<FunctionTemplate> t = FunctionTemplate::New(New);
  
 -    return scope.Close(StringBytes::Encode(
 -          reinterpret_cast<const char*>(md_value), md_len, encoding));
 -  }
 +  t->InstanceTemplate()->SetInternalFieldCount(1);
  
 -  Hash () : ObjectWrap () {
 -    initialised_ = false;
 -  }
 +  NODE_SET_PROTOTYPE_METHOD(t, "init", VerifyInit);
 +  NODE_SET_PROTOTYPE_METHOD(t, "update", VerifyUpdate);
 +  NODE_SET_PROTOTYPE_METHOD(t, "verify", VerifyFinal);
  
 -  ~Hash () {
 -    if (initialised_) {
 -      EVP_MD_CTX_cleanup(&mdctx);
 -    }
 -  }
 +  target->Set(String::NewSymbol("Verify"), t->GetFunction());
 +}
  
 - private:
  
 -  EVP_MD_CTX mdctx; /* coverity[member_decl] */
 -  const EVP_MD *md; /* coverity[member_decl] */
 -  bool initialised_;
 -};
 +Handle<Value> Verify::New(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -class Sign : public ObjectWrap {
 - public:
 -  static void
 -  Initialize (v8::Handle<v8::Object> target) {
 -    HandleScope scope;
 +  Verify* verify = new Verify();
 +  verify->Wrap(args.This());
  
 -    Local<FunctionTemplate> t = FunctionTemplate::New(New);
 +  return args.This();
 +}
  
 -    t->InstanceTemplate()->SetInternalFieldCount(1);
  
 -    NODE_SET_PROTOTYPE_METHOD(t, "init", SignInit);
 -    NODE_SET_PROTOTYPE_METHOD(t, "update", SignUpdate);
 -    NODE_SET_PROTOTYPE_METHOD(t, "sign", SignFinal);
 +Handle<Value> Verify::VerifyInit(const char* verify_type) {
 +  HandleScope scope(node_isolate);
  
 -    target->Set(String::NewSymbol("Sign"), t->GetFunction());
 +  assert(md_ == NULL);
 +  md_ = EVP_get_digestbyname(verify_type);
 +  if (md_ == NULL) {
 +    return ThrowError("Unknown message digest");
    }
  
 -  bool SignInit (const char* signType) {
 -    md = EVP_get_digestbyname(signType);
 -    if(!md) {
 -      printf("Unknown message digest %s\n", signType);
 -      return false;
 -    }
 -    EVP_MD_CTX_init(&mdctx);
 -    EVP_SignInit_ex(&mdctx, md, NULL);
 -    initialised_ = true;
 -    return true;
 -
 -  }
 +  EVP_MD_CTX_init(&mdctx_);
 +  EVP_VerifyInit_ex(&mdctx_, md_, NULL);
 +  initialised_ = true;
  
 -  int SignUpdate(char* data, int len) {
 -    if (!initialised_) return 0;
 -    EVP_SignUpdate(&mdctx, data, len);
 -    return 1;
 -  }
 +  return Null(node_isolate);
 +}
  
 -  int SignFinal(unsigned char** md_value,
 -                unsigned int *md_len,
 -                char* key_pem,
 -                int key_pemLen) {
 -    if (!initialised_) return 0;
  
 -    BIO *bp = NULL;
 -    EVP_PKEY* pkey;
 -    bp = BIO_new(BIO_s_mem());
 -    if(!BIO_write(bp, key_pem, key_pemLen)) return 0;
 +Handle<Value> Verify::VerifyInit(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -    pkey = PEM_read_bio_PrivateKey( bp, NULL, NULL, NULL );
 -    if (pkey == NULL) return 0;
 +  Verify* verify = ObjectWrap::Unwrap<Verify>(args.This());
  
 -    EVP_SignFinal(&mdctx, *md_value, md_len, pkey);
 -    EVP_MD_CTX_cleanup(&mdctx);
 -    initialised_ = false;
 -    EVP_PKEY_free(pkey);
 -    BIO_free(bp);
 -    return 1;
 +  if (args.Length() == 0 || !args[0]->IsString()) {
 +    return ThrowError("Must give verifytype string as argument");
    }
  
 +  String::Utf8Value verify_type(args[0]);
  
 - protected:
 -
 -  static Handle<Value> New (const Arguments& args) {
 -    HandleScope scope;
 -
 -    Sign *sign = new Sign();
 -    sign->Wrap(args.This());
 +  Handle<Value> ret = verify->VerifyInit(*verify_type);
  
 +  if (ret->IsNull()) {
      return args.This();
 +  } else {
 +    // Exception
 +    return scope.Close(ret);
    }
 +}
  
 -  static Handle<Value> SignInit(const Arguments& args) {
 -    HandleScope scope;
  
 -    Sign *sign = ObjectWrap::Unwrap<Sign>(args.This());
 +bool Verify::VerifyUpdate(char* data, int len) {
 +  if (!initialised_) return false;
 +  EVP_VerifyUpdate(&mdctx_, data, len);
 +  return true;
 +}
  
 -    if (args.Length() == 0 || !args[0]->IsString()) {
 -      return ThrowException(Exception::Error(String::New(
 -        "Must give signtype string as argument")));
 -    }
  
 -    String::Utf8Value signType(args[0]);
 +Handle<Value> Verify::VerifyUpdate(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -    bool r = sign->SignInit(*signType);
 +  Verify* verify = ObjectWrap::Unwrap<Verify>(args.This());
  
-   ASSERT_IS_BUFFER(args[0]);
 -    if (!r) {
 -      return ThrowException(Exception::Error(String::New("SignInit error")));
 -    }
++  ASSERT_IS_STRING_OR_BUFFER(args[0]);
  
 -    return args.This();
++  // Only copy the data if we have to, because it's a string
 +  bool r;
-   char* buffer_data = Buffer::Data(args[0]);
-   size_t buffer_length = Buffer::Length(args[0]);
-   r = verify->VerifyUpdate(buffer_data, buffer_length);
++  if (args[0]->IsString()) {
++    enum encoding encoding = ParseEncoding(args[1], BINARY);
++    size_t buflen = StringBytes::StorageSize(args[0], encoding);
++    char* buf = new char[buflen];
++    size_t written = StringBytes::Write(buf, buflen, args[0], encoding);
++    r = verify->VerifyUpdate(buf, written);
++    delete[] buf;
++  } else {
++    char* buf = Buffer::Data(args[0]);
++    size_t buflen = Buffer::Length(args[0]);
++    r = verify->VerifyUpdate(buf, buflen);
+   }
  
 -  static Handle<Value> SignUpdate(const Arguments& args) {
 -    Sign *sign = ObjectWrap::Unwrap<Sign>(args.This());
 -
 -    HandleScope scope;
 -
 -    ASSERT_IS_STRING_OR_BUFFER(args[0]);
 -
 -    // Only copy the data if we have to, because it's a string
 -    int r;
 -    if (args[0]->IsString()) {
 -      enum encoding encoding = ParseEncoding(args[1], BINARY);
 -      size_t buflen = StringBytes::StorageSize(args[0], encoding);
 -      char* buf = new char[buflen];
 -      size_t written = StringBytes::Write(buf, buflen, args[0], encoding);
 -      r = sign->SignUpdate(buf, written);
 -      delete[] buf;
 -    } else {
 -      char* buf = Buffer::Data(args[0]);
 -      size_t buflen = Buffer::Length(args[0]);
 -      r = sign->SignUpdate(buf, buflen);
 -    }
 -
 -    if (!r) {
 -      Local<Value> exception = Exception::TypeError(String::New("SignUpdate fail"));
 -      return ThrowException(exception);
 -    }
 -
 -    return args.This();
 +  if (!r) {
 +    return ThrowTypeError("VerifyUpdate fail");
    }
  
 -  static Handle<Value> SignFinal(const Arguments& args) {
 -    Sign *sign = ObjectWrap::Unwrap<Sign>(args.This());
 -
 -    HandleScope scope;
 -
 -    unsigned char* md_value;
 -    unsigned int md_len;
 -    Local<Value> outString;
 +  return args.This();
 +}
  
 -    ASSERT_IS_BUFFER(args[0]);
 -    ssize_t len = Buffer::Length(args[0]);
  
 -    enum encoding encoding = BUFFER;
 -    if (args.Length() >= 2) {
 -      encoding = ParseEncoding(args[1]->ToString(), BUFFER);
 -    }
 +Handle<Value> Verify::VerifyFinal(char* key_pem,
 +                                  int key_pem_len,
 +                                  unsigned char* sig,
 +                                  int siglen) {
 +  HandleScope scope(node_isolate);
  
 -    char* buf = new char[len];
 -    ssize_t written = DecodeWrite(buf, len, args[0], BUFFER);
 -    assert(written == len);
 +  if (!initialised_) {
 +    return ThrowError("Verify not initalised");
 +  }
  
 -    md_len = 8192; // Maximum key size is 8192 bits
 -    md_value = new unsigned char[md_len];
 +  EVP_PKEY* pkey = NULL;
 +  BIO* bp = NULL;
 +  X509* x509 = NULL;
 +  bool fatal = true;
 +  int r;
  
 -    int r = sign->SignFinal(&md_value, &md_len, buf, len);
 -    if (r == 0) {
 -      md_value = NULL;
 -      md_len = r;
 +  bp = BIO_new(BIO_s_mem());
 +  if (bp == NULL)
 +    goto exit;
 +
 +  if (!BIO_write(bp, key_pem, key_pem_len))
 +    goto exit;
 +
 +  // Check if this is a PKCS#8 or RSA public key before trying as X.509.
 +  // Split this out into a separate function once we have more than one
 +  // consumer of public keys.
 +  if (strncmp(key_pem, PUBLIC_KEY_PFX, PUBLIC_KEY_PFX_LEN) == 0) {
 +    pkey = PEM_read_bio_PUBKEY(bp, NULL, NULL, NULL);
 +    if (pkey == NULL)
 +      goto exit;
 +  } else if (strncmp(key_pem, PUBRSA_KEY_PFX, PUBRSA_KEY_PFX_LEN) == 0) {
 +    RSA* rsa = PEM_read_bio_RSAPublicKey(bp, NULL, NULL, NULL);
 +    if (rsa) {
 +      pkey = EVP_PKEY_new();
 +      if (pkey) EVP_PKEY_set1_RSA(pkey, rsa);
 +      RSA_free(rsa);
      }
 +    if (pkey == NULL)
 +      goto exit;
 +  } else {
 +    // X.509 fallback
 +    x509 = PEM_read_bio_X509(bp, NULL, NULL, NULL);
 +    if (x509 == NULL)
 +      goto exit;
  
 -    delete [] buf;
 +    pkey = X509_get_pubkey(x509);
 +    if (pkey == NULL)
 +      goto exit;
 +  }
  
 -    outString = StringBytes::Encode(
 -        reinterpret_cast<const char*>(md_value), md_len, encoding);
 +  fatal = false;
 +  r = EVP_VerifyFinal(&mdctx_, sig, siglen, pkey);
  
 -    delete [] md_value;
 -    return scope.Close(outString);
 -  }
 +exit:
 +  if (pkey != NULL)
 +    EVP_PKEY_free(pkey);
 +  if (bp != NULL)
 +    BIO_free_all(bp);
 +  if (x509 != NULL)
 +    X509_free(x509);
  
 -  Sign () : ObjectWrap () {
 -    initialised_ = false;
 -  }
 +  EVP_MD_CTX_cleanup(&mdctx_);
 +  initialised_ = false;
  
 -  ~Sign () {
 -    if (initialised_) {
 -      EVP_MD_CTX_cleanup(&mdctx);
 -    }
 +  if (fatal) {
 +    unsigned long err = ERR_get_error();
 +    return ThrowCryptoError(err);
    }
  
 - private:
 -
 -  EVP_MD_CTX mdctx; /* coverity[member_decl] */
 -  const EVP_MD *md; /* coverity[member_decl] */
 -  bool initialised_;
 -};
 +  return scope.Close(r ? True(node_isolate) : False(node_isolate));
 +}
  
 -class Verify : public ObjectWrap {
 - public:
 -  static void Initialize (v8::Handle<v8::Object> target) {
 -    HandleScope scope;
  
 -    Local<FunctionTemplate> t = FunctionTemplate::New(New);
 +Handle<Value> Verify::VerifyFinal(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -    t->InstanceTemplate()->SetInternalFieldCount(1);
 +  Verify* verify = ObjectWrap::Unwrap<Verify>(args.This());
  
 -    NODE_SET_PROTOTYPE_METHOD(t, "init", VerifyInit);
 -    NODE_SET_PROTOTYPE_METHOD(t, "update", VerifyUpdate);
 -    NODE_SET_PROTOTYPE_METHOD(t, "verify", VerifyFinal);
 +  ASSERT_IS_BUFFER(args[0]);
 +  char* kbuf = Buffer::Data(args[0]);
 +  ssize_t klen = Buffer::Length(args[0]);
  
-   ASSERT_IS_BUFFER(args[1]);
-   unsigned char* hbuf = reinterpret_cast<unsigned char*>(Buffer::Data(args[1]));
-   ssize_t hlen = Buffer::Length(args[1]);
 -    target->Set(String::NewSymbol("Verify"), t->GetFunction());
++  ASSERT_IS_STRING_OR_BUFFER(args[1]);
++  // BINARY works for both buffers and binary strings.
++  enum encoding encoding = BINARY;
++  if (args.Length() >= 3) {
++    encoding = ParseEncoding(args[2]->ToString(), BINARY);
+   }
  
-   return scope.Close(verify->VerifyFinal(kbuf, klen, hbuf, hlen));
++  ssize_t hlen = StringBytes::Size(args[1], encoding);
 -  bool VerifyInit (const char* verifyType) {
 -    md = EVP_get_digestbyname(verifyType);
 -    if(!md) {
 -      fprintf(stderr, "node-crypto : Unknown message digest %s\n", verifyType);
 -      return false;
 -    }
 -    EVP_MD_CTX_init(&mdctx);
 -    EVP_VerifyInit_ex(&mdctx, md, NULL);
 -    initialised_ = true;
 -    return true;
++  // only copy if we need to, because it's a string.
++  unsigned char* hbuf;
++  if (args[1]->IsString()) {
++    hbuf = new unsigned char[hlen];
++    ssize_t hwritten = StringBytes::Write(
++        reinterpret_cast<char*>(hbuf), hlen, args[1], encoding);
++    assert(hwritten == hlen);
++  } else {
++    hbuf = reinterpret_cast<unsigned char*>(Buffer::Data(args[1]));
+   }
 -
 -  int VerifyUpdate(char* data, int len) {
 -    if (!initialised_) return 0;
 -    EVP_VerifyUpdate(&mdctx, data, len);
 -    return 1;
++  Local<Value> retval = Local<Value>::New(verify->VerifyFinal(kbuf, klen, hbuf, hlen));
++  if (args[1]->IsString()) {
++    delete[] hbuf;
+   }
++  return scope.Close(retval);
 +}
  
  
 -  int VerifyFinal(char* key_pem, int key_pemLen, unsigned char* sig, int siglen) {
 -    if (!initialised_) return 0;
 -
 -    EVP_PKEY* pkey = NULL;
 -    BIO *bp = NULL;
 -    X509 *x509 = NULL;
 -    int r = 0;
 -
 -    bp = BIO_new(BIO_s_mem());
 -    if (bp == NULL) {
 -      ERR_print_errors_fp(stderr);
 -      return 0;
 -    }
 -    if(!BIO_write(bp, key_pem, key_pemLen)) {
 -      ERR_print_errors_fp(stderr);
 -      return 0;
 -    }
 -
 -    // Check if this is a PKCS#8 or RSA public key before trying as X.509.
 -    // Split this out into a separate function once we have more than one
 -    // consumer of public keys.
 -    if (strncmp(key_pem, PUBLIC_KEY_PFX, PUBLIC_KEY_PFX_LEN) == 0) {
 -      pkey = PEM_read_bio_PUBKEY(bp, NULL, NULL, NULL);
 -      if (pkey == NULL) {
 -        ERR_print_errors_fp(stderr);
 -        return 0;
 -      }
 -    } else if (strncmp(key_pem, PUBRSA_KEY_PFX, PUBRSA_KEY_PFX_LEN) == 0) {
 -      RSA* rsa = PEM_read_bio_RSAPublicKey(bp, NULL, NULL, NULL);
 -      if (rsa) {
 -        pkey = EVP_PKEY_new();
 -        if (pkey) EVP_PKEY_set1_RSA(pkey, rsa);
 -        RSA_free(rsa);
 -      }
 -      if (pkey == NULL) {
 -        ERR_print_errors_fp(stderr);
 -        return 0;
 -      }
 -    } else {
 -      // X.509 fallback
 -      x509 = PEM_read_bio_X509(bp, NULL, NULL, NULL);
 -      if (x509 == NULL) {
 -        ERR_print_errors_fp(stderr);
 -        return 0;
 -      }
 +void DiffieHellman::Initialize(v8::Handle<v8::Object> target) {
 +  HandleScope scope(node_isolate);
  
 -      pkey = X509_get_pubkey(x509);
 -      if (pkey == NULL) {
 -        ERR_print_errors_fp(stderr);
 -        return 0;
 -      }
 -    }
 +  Local<FunctionTemplate> t = FunctionTemplate::New(New);
  
 -    r = EVP_VerifyFinal(&mdctx, sig, siglen, pkey);
 +  t->InstanceTemplate()->SetInternalFieldCount(1);
  
 -    if(pkey != NULL)
 -      EVP_PKEY_free (pkey);
 -    if (x509 != NULL)
 -      X509_free(x509);
 -    if (bp != NULL)
 -      BIO_free(bp);
 -    EVP_MD_CTX_cleanup(&mdctx);
 -    initialised_ = false;
 +  NODE_SET_PROTOTYPE_METHOD(t, "generateKeys", GenerateKeys);
 +  NODE_SET_PROTOTYPE_METHOD(t, "computeSecret", ComputeSecret);
 +  NODE_SET_PROTOTYPE_METHOD(t, "getPrime", GetPrime);
 +  NODE_SET_PROTOTYPE_METHOD(t, "getGenerator", GetGenerator);
 +  NODE_SET_PROTOTYPE_METHOD(t, "getPublicKey", GetPublicKey);
 +  NODE_SET_PROTOTYPE_METHOD(t, "getPrivateKey", GetPrivateKey);
 +  NODE_SET_PROTOTYPE_METHOD(t, "setPublicKey", SetPublicKey);
 +  NODE_SET_PROTOTYPE_METHOD(t, "setPrivateKey", SetPrivateKey);
  
 -    return r;
 -  }
 +  target->Set(String::NewSymbol("DiffieHellman"), t->GetFunction());
  
 +  Local<FunctionTemplate> t2 = FunctionTemplate::New(DiffieHellmanGroup);
 +  t2->InstanceTemplate()->SetInternalFieldCount(1);
  
 - protected:
 +  NODE_SET_PROTOTYPE_METHOD(t2, "generateKeys", GenerateKeys);
 +  NODE_SET_PROTOTYPE_METHOD(t2, "computeSecret", ComputeSecret);
 +  NODE_SET_PROTOTYPE_METHOD(t2, "getPrime", GetPrime);
 +  NODE_SET_PROTOTYPE_METHOD(t2, "getGenerator", GetGenerator);
 +  NODE_SET_PROTOTYPE_METHOD(t2, "getPublicKey", GetPublicKey);
 +  NODE_SET_PROTOTYPE_METHOD(t2, "getPrivateKey", GetPrivateKey);
  
 -  static Handle<Value> New (const Arguments& args) {
 -    HandleScope scope;
 +  target->Set(String::NewSymbol("DiffieHellmanGroup"), t2->GetFunction());
 +}
  
 -    Verify *verify = new Verify();
 -    verify->Wrap(args.This());
  
 -    return args.This();
 -  }
 +bool DiffieHellman::Init(int primeLength) {
 +  dh = DH_new();
 +  DH_generate_parameters_ex(dh, primeLength, DH_GENERATOR_2, 0);
 +  bool result = VerifyContext();
 +  if (!result) return false;
 +  initialised_ = true;
 +  return true;
 +}
  
  
 -  static Handle<Value> VerifyInit(const Arguments& args) {
 -    Verify *verify = ObjectWrap::Unwrap<Verify>(args.This());
 +bool DiffieHellman::Init(unsigned char* p, int p_len) {
 +  dh = DH_new();
 +  dh->p = BN_bin2bn(p, p_len, 0);
 +  dh->g = BN_new();
 +  if (!BN_set_word(dh->g, 2)) return false;
 +  bool result = VerifyContext();
 +  if (!result) return false;
 +  initialised_ = true;
 +  return true;
 +}
  
 -    HandleScope scope;
  
 -    if (args.Length() == 0 || !args[0]->IsString()) {
 -      return ThrowException(Exception::Error(String::New(
 -        "Must give verifytype string as argument")));
 -    }
 +bool DiffieHellman::Init(unsigned char* p,
 +                         int p_len,
 +                         unsigned char* g,
 +                         int g_len) {
 +  dh = DH_new();
 +  dh->p = BN_bin2bn(p, p_len, 0);
 +  dh->g = BN_bin2bn(g, g_len, 0);
 +  initialised_ = true;
 +  return true;
 +}
  
 -    String::Utf8Value verifyType(args[0]);
  
 -    bool r = verify->VerifyInit(*verifyType);
 +Handle<Value> DiffieHellman::DiffieHellmanGroup(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -    if (!r) {
 -      return ThrowException(Exception::Error(String::New("VerifyInit error")));
 -    }
 +  DiffieHellman* diffieHellman = new DiffieHellman();
  
 -    return args.This();
 +  if (args.Length() != 1 || !args[0]->IsString()) {
 +    return ThrowError("No group name given");
    }
  
 +  String::Utf8Value group_name(args[0]);
  
 -  static Handle<Value> VerifyUpdate(const Arguments& args) {
 -    HandleScope scope;
 -
 -    Verify *verify = ObjectWrap::Unwrap<Verify>(args.This());
 -
 -    ASSERT_IS_STRING_OR_BUFFER(args[0]);
 +  modp_group* it = modp_groups;
  
 -    // Only copy the data if we have to, because it's a string
 -    int r;
 -    if (args[0]->IsString()) {
 -      enum encoding encoding = ParseEncoding(args[1], BINARY);
 -      size_t buflen = StringBytes::StorageSize(args[0], encoding);
 -      char* buf = new char[buflen];
 -      size_t written = StringBytes::Write(buf, buflen, args[0], encoding);
 -      r = verify->VerifyUpdate(buf, written);
 -      delete[] buf;
 -    } else {
 -      char* buf = Buffer::Data(args[0]);
 -      size_t buflen = Buffer::Length(args[0]);
 -      r = verify->VerifyUpdate(buf, buflen);
 -    }
 -
 -    if (!r) {
 -      Local<Value> exception = Exception::TypeError(String::New("VerifyUpdate fail"));
 -      return ThrowException(exception);
 -    }
 -
 -    return args.This();
 +  while(it->name != NULL) {
 +    if (!strcasecmp(*group_name, it->name))
 +      break;
 +    it++;
    }
  
 +  if (it->name != NULL) {
 +    diffieHellman->Init(it->prime,
 +                        it->prime_size,
 +                        it->gen,
 +                        it->gen_size);
 +  } else {
 +    return ThrowError("Unknown group");
 +  }
  
 -  static Handle<Value> VerifyFinal(const Arguments& args) {
 -    HandleScope scope;
 -
 -    Verify *verify = ObjectWrap::Unwrap<Verify>(args.This());
 +  diffieHellman->Wrap(args.This());
  
 -    ASSERT_IS_BUFFER(args[0]);
 -    ssize_t klen = Buffer::Length(args[0]);
 +  return args.This();
 +}
  
 -    if (klen < 0) {
 -      Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
 -      return ThrowException(exception);
 -    }
  
 -    char* kbuf = new char[klen];
 -    ssize_t kwritten = DecodeWrite(kbuf, klen, args[0], BINARY);
 -    assert(kwritten == klen);
 +Handle<Value> DiffieHellman::New(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -    ASSERT_IS_STRING_OR_BUFFER(args[1]);
 +  DiffieHellman* diffieHellman = new DiffieHellman();
 +  bool initialized = false;
  
 -    // BINARY works for both buffers and binary strings.
 -    enum encoding encoding = BINARY;
 -    if (args.Length() >= 3) {
 -      encoding = ParseEncoding(args[2]->ToString(), BINARY);
 +  if (args.Length() > 0) {
 +    if (args[0]->IsInt32()) {
 +      initialized = diffieHellman->Init(args[0]->Int32Value());
 +    } else {
 +      initialized = diffieHellman->Init(
 +              reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
 +              Buffer::Length(args[0]));
      }
 +  }
  
 -    ssize_t hlen = StringBytes::Size(args[1], encoding);
 +  if (!initialized) {
 +    return ThrowError("Initialization failed");
 +  }
  
 -    if (hlen < 0) {
 -      delete[] kbuf;
 -      Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
 -      return ThrowException(exception);
 -    }
 +  diffieHellman->Wrap(args.This());
  
 -    unsigned char* hbuf = new unsigned char[hlen];
 -    ssize_t hwritten = StringBytes::Write(
 -        reinterpret_cast<char*>(hbuf), hlen, args[1], BINARY);
 -    assert(hwritten == hlen);
 +  return args.This();
 +}
  
 -    int r;
 -    r = verify->VerifyFinal(kbuf, klen, hbuf, hlen);
  
 -    delete[] kbuf;
 -    delete[] hbuf;
 +Handle<Value> DiffieHellman::GenerateKeys(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -    return Boolean::New(r && r != -1);
 -  }
 +  DiffieHellman* diffieHellman =
 +      ObjectWrap::Unwrap<DiffieHellman>(args.This());
  
 -  Verify () : ObjectWrap () {
 -    initialised_ = false;
 +  if (!diffieHellman->initialised_) {
 +    return ThrowError("Not initialized");
    }
  
 -  ~Verify () {
 -    if (initialised_) {
 -      EVP_MD_CTX_cleanup(&mdctx);
 -    }
 +  if (!DH_generate_key(diffieHellman->dh)) {
 +    return ThrowError("Key generation failed");
    }
  
 - private:
 +  Local<Value> outString;
  
 -  EVP_MD_CTX mdctx; /* coverity[member_decl] */
 -  const EVP_MD *md; /* coverity[member_decl] */
 -  bool initialised_;
 +  int dataSize = BN_num_bytes(diffieHellman->dh->pub_key);
 +  char* data = new char[dataSize];
 +  BN_bn2bin(diffieHellman->dh->pub_key,
 +            reinterpret_cast<unsigned char*>(data));
  
 -};
 -
 -class DiffieHellman : public ObjectWrap {
 - public:
 -  static void Initialize(v8::Handle<v8::Object> target) {
 -    HandleScope scope;
 -
 -    Local<FunctionTemplate> t = FunctionTemplate::New(New);
 -
 -    t->InstanceTemplate()->SetInternalFieldCount(1);
 +  outString = Encode(data, dataSize, BUFFER);
 +  delete[] data;
  
 -    NODE_SET_PROTOTYPE_METHOD(t, "generateKeys", GenerateKeys);
 -    NODE_SET_PROTOTYPE_METHOD(t, "computeSecret", ComputeSecret);
 -    NODE_SET_PROTOTYPE_METHOD(t, "getPrime", GetPrime);
 -    NODE_SET_PROTOTYPE_METHOD(t, "getGenerator", GetGenerator);
 -    NODE_SET_PROTOTYPE_METHOD(t, "getPublicKey", GetPublicKey);
 -    NODE_SET_PROTOTYPE_METHOD(t, "getPrivateKey", GetPrivateKey);
 -    NODE_SET_PROTOTYPE_METHOD(t, "setPublicKey", SetPublicKey);
 -    NODE_SET_PROTOTYPE_METHOD(t, "setPrivateKey", SetPrivateKey);
 +  return scope.Close(outString);
 +}
  
 -    target->Set(String::NewSymbol("DiffieHellman"), t->GetFunction());
  
 -    Local<FunctionTemplate> t2 = FunctionTemplate::New(DiffieHellmanGroup);
 -    t2->InstanceTemplate()->SetInternalFieldCount(1);
 +Handle<Value> DiffieHellman::GetPrime(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -    NODE_SET_PROTOTYPE_METHOD(t2, "generateKeys", GenerateKeys);
 -    NODE_SET_PROTOTYPE_METHOD(t2, "computeSecret", ComputeSecret);
 -    NODE_SET_PROTOTYPE_METHOD(t2, "getPrime", GetPrime);
 -    NODE_SET_PROTOTYPE_METHOD(t2, "getGenerator", GetGenerator);
 -    NODE_SET_PROTOTYPE_METHOD(t2, "getPublicKey", GetPublicKey);
 -    NODE_SET_PROTOTYPE_METHOD(t2, "getPrivateKey", GetPrivateKey);
 +  DiffieHellman* diffieHellman =
 +      ObjectWrap::Unwrap<DiffieHellman>(args.This());
  
 -    target->Set(String::NewSymbol("DiffieHellmanGroup"), t2->GetFunction());
 +  if (!diffieHellman->initialised_) {
 +    return ThrowError("Not initialized");
    }
  
 -  bool Init(int primeLength) {
 -    dh = DH_new();
 -    DH_generate_parameters_ex(dh, primeLength, DH_GENERATOR_2, 0);
 -    bool result = VerifyContext();
 -    if (!result) return false;
 -    initialised_ = true;
 -    return true;
 -  }
 +  int dataSize = BN_num_bytes(diffieHellman->dh->p);
 +  char* data = new char[dataSize];
 +  BN_bn2bin(diffieHellman->dh->p, reinterpret_cast<unsigned char*>(data));
  
 -  bool Init(unsigned char* p, int p_len) {
 -    dh = DH_new();
 -    dh->p = BN_bin2bn(p, p_len, 0);
 -    dh->g = BN_new();
 -    if (!BN_set_word(dh->g, 2)) return false;
 -    bool result = VerifyContext();
 -    if (!result) return false;
 -    initialised_ = true;
 -    return true;
 -  }
 +  Local<Value> outString;
  
 -  bool Init(unsigned char* p, int p_len, unsigned char* g, int g_len) {
 -    dh = DH_new();
 -    dh->p = BN_bin2bn(p, p_len, 0);
 -    dh->g = BN_bin2bn(g, g_len, 0);
 -    initialised_ = true;
 -    return true;
 -  }
 +  outString = Encode(data, dataSize, BUFFER);
  
 - protected:
 -  static Handle<Value> DiffieHellmanGroup(const Arguments& args) {
 -    HandleScope scope;
 +  delete[] data;
  
 -    DiffieHellman* diffieHellman = new DiffieHellman();
 +  return scope.Close(outString);
 +}
  
 -    if (args.Length() != 1 || !args[0]->IsString()) {
 -      return ThrowException(Exception::Error(
 -          String::New("No group name given")));
 -    }
  
 -    String::Utf8Value group_name(args[0]);
 +Handle<Value> DiffieHellman::GetGenerator(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -    modp_group* it = modp_groups;
 +  DiffieHellman* diffieHellman =
 +      ObjectWrap::Unwrap<DiffieHellman>(args.This());
  
 -    while(it->name != NULL) {
 -      if (!strcasecmp(*group_name, it->name))
 -          break;
 -      it++;
 -    }
 +  if (!diffieHellman->initialised_) {
 +    return ThrowError("Not initialized");
 +  }
  
 -    if (it->name != NULL) {
 -      diffieHellman->Init(it->prime, it->prime_size,
 -              it->gen, it->gen_size);
 -    } else {
 -      return ThrowException(Exception::Error(
 -          String::New("Unknown group")));
 -    }
 +  int dataSize = BN_num_bytes(diffieHellman->dh->g);
 +  char* data = new char[dataSize];
 +  BN_bn2bin(diffieHellman->dh->g, reinterpret_cast<unsigned char*>(data));
  
 -    diffieHellman->Wrap(args.This());
 +  Local<Value> outString;
  
 -    return args.This();
 -  }
 +  outString = Encode(data, dataSize, BUFFER);
  
 -  static Handle<Value> New(const Arguments& args) {
 -    HandleScope scope;
 +  delete[] data;
  
 -    DiffieHellman* diffieHellman = new DiffieHellman();
 -    bool initialized = false;
 +  return scope.Close(outString);
 +}
  
 -    if (args.Length() > 0) {
 -      if (args[0]->IsInt32()) {
 -        initialized = diffieHellman->Init(args[0]->Int32Value());
 -      } else {
 -        initialized = diffieHellman->Init(
 -                reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
 -                Buffer::Length(args[0]));
 -      }
 -    }
  
 -    if (!initialized) {
 -      return ThrowException(Exception::Error(
 -            String::New("Initialization failed")));
 -    }
 +Handle<Value> DiffieHellman::GetPublicKey(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -    diffieHellman->Wrap(args.This());
 +  DiffieHellman* diffieHellman =
 +      ObjectWrap::Unwrap<DiffieHellman>(args.This());
  
 -    return args.This();
 +  if (!diffieHellman->initialised_) {
 +    return ThrowError("Not initialized");
    }
  
 -  static Handle<Value> GenerateKeys(const Arguments& args) {
 -    DiffieHellman* diffieHellman =
 -      ObjectWrap::Unwrap<DiffieHellman>(args.This());
 +  if (diffieHellman->dh->pub_key == NULL) {
 +    return ThrowError("No public key - did you forget to generate one?");
 +  }
  
 -    HandleScope scope;
 +  int dataSize = BN_num_bytes(diffieHellman->dh->pub_key);
 +  char* data = new char[dataSize];
 +  BN_bn2bin(diffieHellman->dh->pub_key,
 +            reinterpret_cast<unsigned char*>(data));
  
 -    if (!diffieHellman->initialised_) {
 -      return ThrowException(Exception::Error(
 -            String::New("Not initialized")));
 -    }
 +  Local<Value> outString;
  
 -    if (!DH_generate_key(diffieHellman->dh)) {
 -      return ThrowException(Exception::Error(
 -            String::New("Key generation failed")));
 -    }
 +  outString = Encode(data, dataSize, BUFFER);
  
 -    Local<Value> outString;
 +  delete[] data;
  
 -    int dataSize = BN_num_bytes(diffieHellman->dh->pub_key);
 -    char* data = new char[dataSize];
 -    BN_bn2bin(diffieHellman->dh->pub_key,
 -        reinterpret_cast<unsigned char*>(data));
 +  return scope.Close(outString);
 +}
  
 -    outString = Encode(data, dataSize, BUFFER);
 -    delete[] data;
  
 -    return scope.Close(outString);
 -  }
 +Handle<Value> DiffieHellman::GetPrivateKey(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -  static Handle<Value> GetPrime(const Arguments& args) {
 -    DiffieHellman* diffieHellman =
 +  DiffieHellman* diffieHellman =
        ObjectWrap::Unwrap<DiffieHellman>(args.This());
  
 -    HandleScope scope;
 -
 -    if (!diffieHellman->initialised_) {
 -      return ThrowException(Exception::Error(String::New("Not initialized")));
 -    }
 -
 -    int dataSize = BN_num_bytes(diffieHellman->dh->p);
 -    char* data = new char[dataSize];
 -    BN_bn2bin(diffieHellman->dh->p, reinterpret_cast<unsigned char*>(data));
 -
 -    Local<Value> outString;
 -
 -    outString = Encode(data, dataSize, BUFFER);
 -
 -    delete[] data;
 -
 -    return scope.Close(outString);
 +  if (!diffieHellman->initialised_) {
 +    return ThrowError("Not initialized");
    }
  
 -  static Handle<Value> GetGenerator(const Arguments& args) {
 -    DiffieHellman* diffieHellman =
 -      ObjectWrap::Unwrap<DiffieHellman>(args.This());
 +  if (diffieHellman->dh->priv_key == NULL) {
 +    return ThrowError("No private key - did you forget to generate one?");
 +  }
  
 -    HandleScope scope;
 +  int dataSize = BN_num_bytes(diffieHellman->dh->priv_key);
 +  char* data = new char[dataSize];
 +  BN_bn2bin(diffieHellman->dh->priv_key,
 +            reinterpret_cast<unsigned char*>(data));
  
 -    if (!diffieHellman->initialised_) {
 -      return ThrowException(Exception::Error(String::New("Not initialized")));
 -    }
 +  Local<Value> outString;
  
 -    int dataSize = BN_num_bytes(diffieHellman->dh->g);
 -    char* data = new char[dataSize];
 -    BN_bn2bin(diffieHellman->dh->g, reinterpret_cast<unsigned char*>(data));
 +  outString = Encode(data, dataSize, BUFFER);
  
 -    Local<Value> outString;
 +  delete[] data;
  
 -    outString = Encode(data, dataSize, BUFFER);
 +  return scope.Close(outString);
 +}
  
 -    delete[] data;
  
 -    return scope.Close(outString);
 -  }
 +Handle<Value> DiffieHellman::ComputeSecret(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -  static Handle<Value> GetPublicKey(const Arguments& args) {
 -    DiffieHellman* diffieHellman =
 +  DiffieHellman* diffieHellman =
        ObjectWrap::Unwrap<DiffieHellman>(args.This());
  
 -    HandleScope scope;
 +  if (!diffieHellman->initialised_) {
 +    return ThrowError("Not initialized");
 +  }
  
 -    if (!diffieHellman->initialised_) {
 -      return ThrowException(Exception::Error(String::New("Not initialized")));
 -    }
 +  BIGNUM* key = NULL;
  
 -    if (diffieHellman->dh->pub_key == NULL) {
 -      return ThrowException(Exception::Error(
 -            String::New("No public key - did you forget to generate one?")));
 -    }
 +  if (args.Length() == 0) {
 +    return ThrowError("First argument must be other party's public key");
 +  } else {
 +    ASSERT_IS_BUFFER(args[0]);
 +    key = BN_bin2bn(
 +        reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
 +        Buffer::Length(args[0]),
 +        0);
 +  }
  
 -    int dataSize = BN_num_bytes(diffieHellman->dh->pub_key);
 -    char* data = new char[dataSize];
 -    BN_bn2bin(diffieHellman->dh->pub_key,
 -        reinterpret_cast<unsigned char*>(data));
 +  int dataSize = DH_size(diffieHellman->dh);
 +  char* data = new char[dataSize];
  
 -    Local<Value> outString;
 +  int size = DH_compute_key(reinterpret_cast<unsigned char*>(data),
 +                            key,
 +                            diffieHellman->dh);
  
 -    outString = Encode(data, dataSize, BUFFER);
 +  if (size == -1) {
 +    int checkResult;
 +    int checked;
  
 +    checked = DH_check_pub_key(diffieHellman->dh, key, &checkResult);
 +    BN_free(key);
      delete[] data;
  
 -    return scope.Close(outString);
 -  }
 -
 -  static Handle<Value> GetPrivateKey(const Arguments& args) {
 -    DiffieHellman* diffieHellman =
 -      ObjectWrap::Unwrap<DiffieHellman>(args.This());
 -
 -    HandleScope scope;
 -
 -    if (!diffieHellman->initialised_) {
 -      return ThrowException(Exception::Error(String::New("Not initialized")));
 +    if (!checked) {
 +      return ThrowError("Invalid key");
 +    } else if (checkResult) {
 +      if (checkResult & DH_CHECK_PUBKEY_TOO_SMALL) {
 +        return ThrowError("Supplied key is too small");
 +      } else if (checkResult & DH_CHECK_PUBKEY_TOO_LARGE) {
 +        return ThrowError("Supplied key is too large");
 +      } else {
 +        return ThrowError("Invalid key");
 +      }
 +    } else {
 +      return ThrowError("Invalid key");
      }
 +  }
  
 -    if (diffieHellman->dh->priv_key == NULL) {
 -      return ThrowException(Exception::Error(
 -            String::New("No private key - did you forget to generate one?")));
 -    }
 +  BN_free(key);
 +  assert(size >= 0);
  
 -    int dataSize = BN_num_bytes(diffieHellman->dh->priv_key);
 -    char* data = new char[dataSize];
 -    BN_bn2bin(diffieHellman->dh->priv_key,
 -        reinterpret_cast<unsigned char*>(data));
 +  // DH_size returns number of bytes in a prime number
 +  // DH_compute_key returns number of bytes in a remainder of exponent, which
 +  // may have less bytes than a prime number. Therefore add 0-padding to the
 +  // allocated buffer.
 +  if (size != dataSize) {
 +    assert(dataSize > size);
 +    memmove(data + dataSize - size, data, size);
 +    memset(data, 0, dataSize - size);
 +  }
  
 -    Local<Value> outString;
 +  Local<Value> outString;
  
 -    outString = Encode(data, dataSize, BUFFER);
 +  outString = Encode(data, dataSize, BUFFER);
  
 -    delete[] data;
 +  delete[] data;
 +  return scope.Close(outString);
 +}
  
 -    return scope.Close(outString);
 -  }
  
 -  static Handle<Value> ComputeSecret(const Arguments& args) {
 -    HandleScope scope;
 +Handle<Value> DiffieHellman::SetPublicKey(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -    DiffieHellman* diffieHellman =
 +  DiffieHellman* diffieHellman =
        ObjectWrap::Unwrap<DiffieHellman>(args.This());
  
 -    if (!diffieHellman->initialised_) {
 -      return ThrowException(Exception::Error(String::New("Not initialized")));
 -    }
 -
 -    BIGNUM* key = 0;
 +  if (!diffieHellman->initialised_) {
 +    return ThrowError("Not initialized");
 +  }
  
 -    if (args.Length() == 0) {
 -      return ThrowException(Exception::Error(
 -            String::New("First argument must be other party's public key")));
 -    } else {
 -      ASSERT_IS_BUFFER(args[0]);
 -      key = BN_bin2bn(
 +  if (args.Length() == 0) {
 +    return ThrowError("First argument must be public key");
 +  } else {
 +    ASSERT_IS_BUFFER(args[0]);
 +    diffieHellman->dh->pub_key = BN_bin2bn(
          reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
          Buffer::Length(args[0]), 0);
 -    }
 -
 -    int dataSize = DH_size(diffieHellman->dh);
 -    char* data = new char[dataSize];
 -
 -    int size = DH_compute_key(reinterpret_cast<unsigned char*>(data),
 -      key, diffieHellman->dh);
 -
 -    if (size == -1) {
 -      int checkResult;
 -      int checked;
 -
 -      checked = DH_check_pub_key(diffieHellman->dh, key, &checkResult);
 -      BN_free(key);
 -      delete[] data;
 -
 -      if (!checked) {
 -        return ThrowException(Exception::Error(String::New("Invalid key")));
 -      } else if (checkResult) {
 -        if (checkResult & DH_CHECK_PUBKEY_TOO_SMALL) {
 -          return ThrowException(Exception::Error(
 -                String::New("Supplied key is too small")));
 -        } else if (checkResult & DH_CHECK_PUBKEY_TOO_LARGE) {
 -          return ThrowException(Exception::Error(
 -                String::New("Supplied key is too large")));
 -        } else {
 -          return ThrowException(Exception::Error(String::New("Invalid key")));
 -        }
 -      } else {
 -        return ThrowException(Exception::Error(String::New("Invalid key")));
 -      }
 -    }
 -
 -    BN_free(key);
 -    assert(size >= 0);
 -
 -    // DH_size returns number of bytes in a prime number
 -    // DH_compute_key returns number of bytes in a remainder of exponent, which
 -    // may have less bytes than a prime number. Therefore add 0-padding to the
 -    // allocated buffer.
 -    if (size != dataSize) {
 -      assert(dataSize > size);
 -      memmove(data + dataSize - size, data, size);
 -      memset(data, 0, dataSize - size);
 -    }
 -
 -    Local<Value> outString;
 -
 -    outString = Encode(data, dataSize, BUFFER);
 -
 -    delete[] data;
 -    return scope.Close(outString);
    }
  
 -  static Handle<Value> SetPublicKey(const Arguments& args) {
 -    HandleScope scope;
 -
 -    DiffieHellman* diffieHellman =
 -      ObjectWrap::Unwrap<DiffieHellman>(args.This());
 -
 -    if (!diffieHellman->initialised_) {
 -      return ThrowException(Exception::Error(String::New("Not initialized")));
 -    }
 -
 -    if (args.Length() == 0) {
 -      return ThrowException(Exception::Error(
 -            String::New("First argument must be public key")));
 -    } else {
 -      ASSERT_IS_BUFFER(args[0]);
 -      diffieHellman->dh->pub_key =
 -        BN_bin2bn(
 -          reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
 -          Buffer::Length(args[0]), 0);
 -    }
 +  return args.This();
 +}
  
 -    return args.This();
 -  }
  
 -  static Handle<Value> SetPrivateKey(const Arguments& args) {
 -    HandleScope scope;
 +Handle<Value> DiffieHellman::SetPrivateKey(const Arguments& args) {
 +  HandleScope scope(node_isolate);
  
 -    DiffieHellman* diffieHellman =
 +  DiffieHellman* diffieHellman =
        ObjectWrap::Unwrap<DiffieHellman>(args.This());
  
 -    if (!diffieHellman->initialised_) {
 -      return ThrowException(Exception::Error(
 -            String::New("Not initialized")));
 -    }
 -
 -    if (args.Length() == 0) {
 -      return ThrowException(Exception::Error(
 -            String::New("First argument must be private key")));
 -    } else {
 -      ASSERT_IS_BUFFER(args[0]);
 -      diffieHellman->dh->priv_key =
 -        BN_bin2bn(
 -          reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
 -          Buffer::Length(args[0]), 0);
 -    }
 -
 -    return args.This();
 +  if (!diffieHellman->initialised_) {
 +    return ThrowError("Not initialized");
    }
  
 -  DiffieHellman() : ObjectWrap() {
 -    initialised_ = false;
 -    dh = NULL;
 +  if (args.Length() == 0) {
 +    return ThrowError("First argument must be private key");
 +  } else {
 +    ASSERT_IS_BUFFER(args[0]);
 +    diffieHellman->dh->priv_key = BN_bin2bn(
 +        reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
 +        Buffer::Length(args[0]),
 +        0);
    }
  
 -  ~DiffieHellman() {
 -    if (dh != NULL) {
 -      DH_free(dh);
 -    }
 -  }
 +  return args.This();
 +}
  
 - private:
 -  bool VerifyContext() {
 -    int codes;
 -    if (!DH_check(dh, &codes)) return false;
 -    if (codes & DH_CHECK_P_NOT_SAFE_PRIME) return false;
 -    if (codes & DH_CHECK_P_NOT_PRIME) return false;
 -    if (codes & DH_UNABLE_TO_CHECK_GENERATOR) return false;
 -    if (codes & DH_NOT_SUITABLE_GENERATOR) return false;
 -    return true;
 -  }
  
 -  bool initialised_;
 -  DH* dh;
 -};
 +bool DiffieHellman::VerifyContext() {
 +  int codes;
 +  if (!DH_check(dh, &codes)) return false;
 +  if (codes & DH_CHECK_P_NOT_SAFE_PRIME) return false;
 +  if (codes & DH_CHECK_P_NOT_PRIME) return false;
 +  if (codes & DH_UNABLE_TO_CHECK_GENERATOR) return false;
 +  if (codes & DH_NOT_SUITABLE_GENERATOR) return false;
 +  return true;
 +}
  
  
  struct pbkdf2_req {
@@@ -284,105 -283,8 +284,19 @@@ void StreamWrap::OnRead2(uv_pipe_t* han
  }
  
  
- template <WriteEncoding encoding>
- size_t StreamWrap::WriteStringImpl(char* storage,
-                                    size_t storage_size,
-                                    Handle<Value> val,
-                                    uv_buf_t* buf) {
-   assert(val->IsString());
-   Handle<String> string = val.As<String>();
-   size_t data_size;
-   switch (encoding) {
-    case kAscii:
-     data_size = string->WriteOneByte(
-         reinterpret_cast<uint8_t*>(storage),
-         0,
-         -1,
-         String::NO_NULL_TERMINATION | String::HINT_MANY_WRITES_EXPECTED);
-     break;
-    case kUtf8:
-     data_size = string->WriteUtf8(
-         storage,
-         -1,
-         NULL,
-         String::NO_NULL_TERMINATION | String::HINT_MANY_WRITES_EXPECTED);
-     break;
-    case kUcs2: {
-     int chars_copied = string->Write(
-         reinterpret_cast<uint16_t*>(storage),
-         0,
-         -1,
-         String::NO_NULL_TERMINATION | String::HINT_MANY_WRITES_EXPECTED);
-     data_size = chars_copied * sizeof(uint16_t);
-     break;
-    }
-    default:
-     // Unreachable
-     assert(0);
-   }
-   assert(data_size <= storage_size);
-   buf->base = storage;
-   buf->len = data_size;
-   return data_size;
- }
- template <WriteEncoding encoding>
- size_t StreamWrap::GetStringSizeImpl(Handle<Value> val) {
-   assert(val->IsString());
-   Handle<String> string = val.As<String>();
-   switch (encoding) {
-     case kAscii:
-       return string->Length();
-       break;
-     case kUtf8:
-       if (string->Length() < 65536) {
-         // A single UCS2 codepoint never takes up more than 3 utf8 bytes.
-         // Unless the string is really long we just allocate so much space that
-         // we're certain the string fits in there entirely.
-         // TODO: maybe check handle->write_queue_size instead of string length?
-         return 3 * string->Length();
-       } else {
-         // The string is really long. Compute the allocation size that we
-         // actually need.
-         return string->Utf8Length();
-       }
-       break;
-     case kUcs2:
-       return string->Length() * sizeof(uint16_t);
-       break;
-     default:
-       // Unreachable.
-       assert(0);
-   }
-   return 0;
- }
 +size_t StreamWrap::WriteBuffer(Handle<Value> val, uv_buf_t* buf) {
 +  assert(Buffer::HasInstance(val));
 +
 +  // Simple non-writev case
 +  buf->base = Buffer::Data(val);
 +  buf->len = Buffer::Length(val);
 +
 +  return buf->len;
 +}
 +
 +
  Handle<Value> StreamWrap::WriteBuffer(const Arguments& args) {
 -  HandleScope scope;
 +  HandleScope scope(node_isolate);
  
    UNWRAP(StreamWrap)
  
  }
  
  
- template <WriteEncoding encoding>
+ template <enum encoding encoding>
  Handle<Value> StreamWrap::WriteStringImpl(const Arguments& args) {
 -  HandleScope scope;
 +  HandleScope scope(node_isolate);
    int r;
  
    UNWRAP(StreamWrap)
    char* data = reinterpret_cast<char*>(ROUND_UP(
        reinterpret_cast<uintptr_t>(storage) + sizeof(WriteWrap), 16));
  
+   size_t data_size;
+   data_size = StringBytes::Write(data, storage_size, string, encoding);
+   assert(data_size <= storage_size);
    uv_buf_t buf;
-   size_t data_size =
-       WriteStringImpl<encoding>(data, storage_size, string, &buf);
++
+   buf.base = data;
+   buf.len = data_size;
  
    bool ipc_pipe = wrap->stream_->type == UV_NAMED_PIPE &&
                    ((uv_pipe_t*)wrap->stream_)->ipc;
  }
  
  
-     Handle<Value> string = chunk->ToString();
-     switch (static_cast<WriteEncoding>(chunks->Get(i * 2 + 1)->Int32Value())) {
-      case kAscii:
-       storage_size += GetStringSizeImpl<kAscii>(string);
-       break;
-      case kUtf8:
-       storage_size += GetStringSizeImpl<kUtf8>(string);
-       break;
-      case kUcs2:
-       storage_size += GetStringSizeImpl<kUcs2>(string);
-       break;
-      default:
-       assert(0); // Unreachable
-     }
-     storage_size += 15;
 +Handle<Value> StreamWrap::Writev(const Arguments& args) {
 +  HandleScope scope;
 +
 +  UNWRAP(StreamWrap)
 +
 +  if (args.Length() < 1)
 +    return ThrowTypeError("Not enough arguments");
 +
 +  if (!args[0]->IsArray())
 +    return ThrowTypeError("Argument should be array");
 +
 +  Handle<Array> chunks = args[0].As<Array>();
 +  size_t count = chunks->Length() >> 1;
 +
 +  uv_buf_t bufs_[16];
 +  uv_buf_t* bufs = bufs_;
 +
 +  // Determine storage size first
 +  size_t storage_size = 0;
 +  for (size_t i = 0; i < count; i++) {
 +    Handle<Value> chunk = chunks->Get(i * 2);
 +
 +    if (Buffer::HasInstance(chunk))
 +      continue;
 +      // Buffer chunk, no additional storage required
 +
 +    // String chunk
-       bytes += WriteBuffer(chunk, &bufs[i]);
++    Handle<String> string = chunk->ToString();
++    enum encoding encoding = ParseEncoding(chunks->Get(i * 2 + 1));
++    size_t chunk_size;
++    if (encoding == UTF8 && string->Length() > 65535)
++      chunk_size = StringBytes::Size(string, encoding);
++    else
++      chunk_size = StringBytes::StorageSize(string, encoding);
++
++    storage_size += chunk_size + 15;
 +  }
 +
 +  if (storage_size > INT_MAX) {
 +    uv_err_t err;
 +    err.code = UV_ENOBUFS;
 +    SetErrno(err);
 +    return scope.Close(v8::Null(node_isolate));
 +  }
 +
 +  if (ARRAY_SIZE(bufs_) < count)
 +    bufs = new uv_buf_t[count];
 +
 +  storage_size += sizeof(WriteWrap);
 +  char* storage = new char[storage_size];
 +  WriteWrap* req_wrap = new (storage) WriteWrap();
 +
 +  uint32_t bytes = 0;
 +  size_t offset = sizeof(WriteWrap);
 +  for (size_t i = 0; i < count; i++) {
 +    Handle<Value> chunk = chunks->Get(i * 2);
 +
 +    // Write buffer
 +    if (Buffer::HasInstance(chunk)) {
-     switch (static_cast<WriteEncoding>(chunks->Get(i * 2 + 1)->Int32Value())) {
-      case kAscii:
-       str_size =  WriteStringImpl<kAscii>(str_storage,
-                                           str_size,
-                                           string,
-                                           &bufs[i]);
-       break;
-      case kUtf8:
-       str_size =  WriteStringImpl<kUtf8>(str_storage,
-                                          str_size,
-                                          string,
-                                          &bufs[i]);
-       break;
-      case kUcs2:
-       str_size =  WriteStringImpl<kUcs2>(str_storage,
-                                          str_size,
-                                          string,
-                                          &bufs[i]);
-       break;
-      default:
-       assert(0);
-     }
++      bufs[i].base = Buffer::Data(chunk);
++      bufs[i].len = Buffer::Length(chunk);
++      bytes += bufs[i].len;
 +      continue;
 +    }
 +
 +    // Write string
 +    offset = ROUND_UP(offset, 16);
 +    assert(offset < storage_size);
 +    char* str_storage = storage + offset;
 +    size_t str_size = storage_size - offset;
 +
 +    Handle<String> string = chunk->ToString();
++    enum encoding encoding = ParseEncoding(chunks->Get(i * 2 + 1));
++    str_size = StringBytes::Write(str_storage, str_size, string, encoding);
++    bufs[i].base = str_storage;
++    bufs[i].len = str_size;
 +    offset += str_size;
 +    bytes += str_size;
 +  }
 +
 +  int r = uv_write(&req_wrap->req_,
 +                   wrap->stream_,
 +                   bufs,
 +                   count,
 +                   StreamWrap::AfterWrite);
 +
 +  // Deallocate space
 +  if (bufs != bufs_)
 +    delete[] bufs;
 +
 +  req_wrap->Dispatched();
 +  req_wrap->object_->Set(bytes_sym, Number::New(bytes));
 +
 +  wrap->UpdateWriteQueueSize();
 +
 +  if (r) {
 +    SetErrno(uv_last_error(uv_default_loop()));
 +    req_wrap->~WriteWrap();
 +    delete[] storage;
 +    return scope.Close(v8::Null(node_isolate));
 +  } else {
 +    if (wrap->stream_->type == UV_TCP) {
 +      NODE_COUNT_NET_BYTES_SENT(bytes);
 +    } else if (wrap->stream_->type == UV_NAMED_PIPE) {
 +      NODE_COUNT_PIPE_BYTES_SENT(bytes);
 +    }
 +
 +    return scope.Close(req_wrap->object_);
 +  }
 +}
 +
 +
  Handle<Value> StreamWrap::WriteAsciiString(const Arguments& args) {
-   return WriteStringImpl<kAscii>(args);
+   return WriteStringImpl<ASCII>(args);
  }
  
  
  
  namespace node {
  
 +// Forward declaration
 +class WriteWrap;
 +
 +
- // Important: this should have the same values as in lib/net.js
- enum WriteEncoding {
-   kUtf8 = 0x1,
-   kAscii = 0x2,
-   kUcs2 = 0x3
- };
  class StreamWrap : public HandleWrap {
   public:
    uv_stream_t* GetStream() { return stream_; }
    static v8::Handle<v8::Value> WriteUcs2String(const v8::Arguments& args);
  
   protected:
-   template <enum WriteEncoding encoding>
-   static size_t WriteStringImpl(char* storage,
-                                 size_t storage_size,
-                                 v8::Handle<v8::Value> val,
-                                 uv_buf_t* buf);
-   template <enum WriteEncoding encoding>
-   static size_t GetStringSizeImpl(v8::Handle<v8::Value> val);
 +  static size_t WriteBuffer(v8::Handle<v8::Value> val, uv_buf_t* buf);
 +
    StreamWrap(v8::Handle<v8::Object> object, uv_stream_t* stream);
    virtual void SetHandle(uv_handle_t* h);
    void StateChange() { }
index 0000000,90050df..ab8882d
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,627 +1,622 @@@
 -      len = str->WriteAscii(buf, 0, buflen, flags);
+ // 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 "string_bytes.h"
+ #include <assert.h>
+ #include <string.h>  // memcpy
+ #include <limits.h>
+ #include "node.h"
+ #include "node_buffer.h"
+ #include "v8.h"
+ namespace node {
+ using v8::Local;
+ using v8::Handle;
+ using v8::HandleScope;
+ using v8::Object;
+ using v8::String;
+ using v8::Value;
+ //// Base 64 ////
+ #define base64_encoded_size(size) ((size + 2 - ((size + 2) % 3)) / 3 * 4)
+ // Doesn't check for padding at the end.  Can be 1-2 bytes over.
+ static inline size_t base64_decoded_size_fast(size_t size) {
+   size_t remainder = size % 4;
+   size = (size / 4) * 3;
+   if (remainder) {
+     if (size == 0 && remainder == 1) {
+       // special case: 1-byte input cannot be decoded
+       size = 0;
+     } else {
+       // non-padded input, add 1 or 2 extra bytes
+       size += 1 + (remainder == 3);
+     }
+   }
+   return size;
+ }
+ static inline size_t base64_decoded_size(const char* src, size_t size) {
+   size = base64_decoded_size_fast(size);
+   const char* end = src + size;
+   // check for trailing padding (1 or 2 bytes)
+   if (size > 0) {
+     if (end[-1] == '=') size--;
+     if (size > 0 && end[-2] == '=') size--;
+   }
+   return size;
+ }
+ // supports regular and URL-safe base64
+ static const int unbase64_table[] =
+   { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -1, -1, -2, -1, -1,
+     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+     -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63,
+     52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
+     -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+     15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,
+     -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+     41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+   };
+ #define unbase64(x) unbase64_table[(uint8_t)(x)]
+ static inline size_t base64_decode(char *buf,
+                                    size_t len,
+                                    const char *src,
+                                    const size_t srcLen) {
+   char a, b, c, d;
+   char* dst = buf;
+   char* dstEnd = buf + len;
+   const char* srcEnd = src + srcLen;
+   while (src < srcEnd && dst < dstEnd) {
+     int remaining = srcEnd - src;
+     while (unbase64(*src) < 0 && src < srcEnd) src++, remaining--;
+     if (remaining == 0 || *src == '=') break;
+     a = unbase64(*src++);
+     while (unbase64(*src) < 0 && src < srcEnd) src++, remaining--;
+     if (remaining <= 1 || *src == '=') break;
+     b = unbase64(*src++);
+     *dst++ = (a << 2) | ((b & 0x30) >> 4);
+     if (dst == dstEnd) break;
+     while (unbase64(*src) < 0 && src < srcEnd) src++, remaining--;
+     if (remaining <= 2 || *src == '=') break;
+     c = unbase64(*src++);
+     *dst++ = ((b & 0x0F) << 4) | ((c & 0x3C) >> 2);
+     if (dst == dstEnd) break;
+     while (unbase64(*src) < 0 && src < srcEnd) src++, remaining--;
+     if (remaining <= 3 || *src == '=') break;
+     d = unbase64(*src++);
+     *dst++ = ((c & 0x03) << 6) | (d & 0x3F);
+   }
+   return dst - buf;
+ }
+ //// HEX ////
+ static inline unsigned hex2bin(char c) {
+   if (c >= '0' && c <= '9') return c - '0';
+   if (c >= 'A' && c <= 'F') return 10 + (c - 'A');
+   if (c >= 'a' && c <= 'f') return 10 + (c - 'a');
+   return static_cast<unsigned>(-1);
+ }
+ static inline size_t hex_decode(char *buf,
+                                 size_t len,
+                                 const char *src,
+                                 const size_t srcLen) {
+   size_t i;
+   for (i = 0; i < len && i * 2 + 1 < srcLen; ++i) {
+     unsigned a = hex2bin(src[i * 2 + 0]);
+     unsigned b = hex2bin(src[i * 2 + 1]);
+     if (!~a || !~b) return i;
+     buf[i] = a * 16 + b;
+   }
+   return i;
+ }
+ size_t StringBytes::Write(char* buf,
+                           size_t buflen,
+                           Handle<Value> val,
+                           enum encoding encoding,
+                           int* chars_written) {
+   HandleScope scope;
+   size_t len = 0;
+   bool is_buffer = Buffer::HasInstance(val);
+   // sometimes we use 'binary' when we mean 'buffer'
+   if (is_buffer && (encoding == BINARY || encoding == BUFFER)) {
+     // fast path, copy buffer data
+     Local<Object> valObj = Local<Object>::New(val.As<Object>());
+     const char* data = Buffer::Data(valObj);
+     size_t size = Buffer::Length(valObj);
+     size_t len = size < buflen ? size : buflen;
+     memcpy(buf, data, len);
+     return len;
+   }
+   Local<String> str = val->ToString();
+   int flags = String::NO_NULL_TERMINATION |
+               String::HINT_MANY_WRITES_EXPECTED;
+   switch (encoding) {
+     case ASCII:
 -      // TODO(isaacs): MayContainNonAscii is deprecated in V8 3.19, remove
 -      if (!str->MayContainNonAscii())
 -        data_size = str->Length();
 -      else
 -        data_size = 3 * str->Length();
++      len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf),
++                              0,
++                              buflen,
++                              flags);
+       if (chars_written != NULL) {
+         *chars_written = len;
+       }
+       break;
+     case UTF8:
+       len = str->WriteUtf8(buf, buflen, chars_written, flags);
+       break;
+     case UCS2:
+       len = str->Write(reinterpret_cast<uint16_t*>(buf), 0, buflen, flags);
+       if (chars_written != NULL) {
+         *chars_written = len;
+       }
+       len = len * sizeof(uint16_t);
+       break;
+     case BASE64: {
+       String::AsciiValue value(str);
+       len = base64_decode(buf, buflen, *value, value.length());
+       if (chars_written != NULL) {
+         *chars_written = len;
+       }
+       break;
+     }
+     case BINARY:
+     case BUFFER: {
+       // TODO(isaacs): THIS IS AWFUL!!!
+       uint16_t* twobytebuf = new uint16_t[buflen];
+       len = str->Write(twobytebuf, 0, buflen, flags);
+       for (size_t i = 0; i < buflen && i < len; i++) {
+         unsigned char *b = reinterpret_cast<unsigned char*>(&twobytebuf[i]);
+         buf[i] = b[0];
+       }
+       if (chars_written != NULL) {
+         *chars_written = len;
+       }
+       delete[] twobytebuf;
+       break;
+     }
+     case HEX: {
+       String::AsciiValue value(str);
+       len = hex_decode(buf, buflen, *value, value.length());
+       if (chars_written != NULL) {
+         *chars_written = len * 2;
+       }
+       break;
+     }
+     default:
+       assert(0 && "unknown encoding");
+       break;
+   }
+   return len;
+ }
+ // Quick and dirty size calculation
+ // Will always be at least big enough, but may have some extra
+ // UTF8 can be as much as 3x the size, Base64 can have 1-2 extra bytes
+ size_t StringBytes::StorageSize(Handle<Value> val, enum encoding encoding) {
+   HandleScope scope;
+   size_t data_size = 0;
+   bool is_buffer = Buffer::HasInstance(val);
+   if (is_buffer && (encoding == BUFFER || encoding == BINARY)) {
+     return Buffer::Length(val);
+   }
+   Local<String> str = val->ToString();
+   switch (encoding) {
+     case BINARY:
+     case BUFFER:
+     case ASCII:
+       data_size = str->Length();
+       break;
+     case UTF8:
+       // A single UCS2 codepoint never takes up more than 3 utf8 bytes.
+       // It is an exercise for the caller to decide when a string is
+       // long enough to justify calling Size() instead of StorageSize()
 -      // TODO(isaacs): MayContainNonAscii is deprecated in V8 3.19, remove
 -      if (!str->MayContainNonAscii())
 -        data_size = str->Length();
 -      else
 -        data_size = str->Utf8Length();
++      data_size = 3 * str->Length();
+       break;
+     case UCS2:
+       data_size = str->Length() * sizeof(uint16_t);
+       break;
+     case BASE64:
+       data_size = base64_decoded_size_fast(str->Length());
+       break;
+     case HEX:
+       assert(str->Length() % 2 == 0 && "invalid hex string length");
+       data_size = str->Length() / 2;
+       break;
+     default:
+       assert(0 && "unknown encoding");
+       break;
+   }
+   return data_size;
+ }
+ size_t StringBytes::Size(Handle<Value> val, enum encoding encoding) {
+   HandleScope scope;
+   size_t data_size = 0;
+   bool is_buffer = Buffer::HasInstance(val);
+   if (is_buffer && (encoding == BUFFER || encoding == BINARY)) {
+     return Buffer::Length(val);
+   }
+   Local<String> str = val->ToString();
+   switch (encoding) {
+     case BINARY:
+     case BUFFER:
+     case ASCII:
+       data_size = str->Length();
+       break;
+     case UTF8:
++      data_size = str->Utf8Length();
+       break;
+     case UCS2:
+       data_size = str->Length() * sizeof(uint16_t);
+       break;
+     case BASE64: {
+       String::AsciiValue value(str);
+       data_size = base64_decoded_size(*value, value.length());
+       break;
+     }
+     case HEX:
+       data_size = str->Length() / 2;
+       break;
+     default:
+       assert(0 && "unknown encoding");
+       break;
+   }
+   return data_size;
+ }
+ static bool contains_non_ascii_slow(const char* buf, size_t len) {
+   for (size_t i = 0; i < len; ++i) {
+     if (buf[i] & 0x80) return true;
+   }
+   return false;
+ }
+ static bool contains_non_ascii(const char* src, size_t len) {
+   if (len < 16) {
+     return contains_non_ascii_slow(src, len);
+   }
+   const unsigned bytes_per_word = sizeof(void*);
+   const unsigned align_mask = bytes_per_word - 1;
+   const unsigned unaligned = reinterpret_cast<uintptr_t>(src) & align_mask;
+   if (unaligned > 0) {
+     const unsigned n = bytes_per_word - unaligned;
+     if (contains_non_ascii_slow(src, n)) return true;
+     src += n;
+     len -= n;
+   }
+ #if BITS_PER_LONG == 64
+   const uintptr_t mask = 0x8080808080808080ll;
+ #else
+   const uintptr_t mask = 0x80808080l;
+ #endif
+   const uintptr_t* srcw = reinterpret_cast<const uintptr_t*>(src);
+   for (size_t i = 0, n = len / bytes_per_word; i < n; ++i) {
+     if (srcw[i] & mask) return true;
+   }
+   const unsigned remainder = len & align_mask;
+   if (remainder > 0) {
+     const size_t offset = len - remainder;
+     if (contains_non_ascii_slow(src + offset, remainder)) return true;
+   }
+   return false;
+ }
+ static void force_ascii_slow(const char* src, char* dst, size_t len) {
+   for (size_t i = 0; i < len; ++i) {
+     dst[i] = src[i] & 0x7f;
+   }
+ }
+ static void force_ascii(const char* src, char* dst, size_t len) {
+   if (len < 16) {
+     force_ascii_slow(src, dst, len);
+     return;
+   }
+   const unsigned bytes_per_word = sizeof(void*);
+   const unsigned align_mask = bytes_per_word - 1;
+   const unsigned src_unalign = reinterpret_cast<uintptr_t>(src) & align_mask;
+   const unsigned dst_unalign = reinterpret_cast<uintptr_t>(dst) & align_mask;
+   if (src_unalign > 0) {
+     if (src_unalign == dst_unalign) {
+       const unsigned unalign = bytes_per_word - src_unalign;
+       force_ascii_slow(src, dst, unalign);
+       src += unalign;
+       dst += unalign;
+       len -= src_unalign;
+     } else {
+       force_ascii_slow(src, dst, len);
+       return;
+     }
+   }
+ #if BITS_PER_LONG == 64
+   const uintptr_t mask = ~0x8080808080808080ll;
+ #else
+   const uintptr_t mask = ~0x80808080l;
+ #endif
+   const uintptr_t* srcw = reinterpret_cast<const uintptr_t*>(src);
+   uintptr_t* dstw = reinterpret_cast<uintptr_t*>(dst);
+   for (size_t i = 0, n = len / bytes_per_word; i < n; ++i) {
+     dstw[i] = srcw[i] & mask;
+   }
+   const unsigned remainder = len & align_mask;
+   if (remainder > 0) {
+     const size_t offset = len - remainder;
+     force_ascii_slow(src + offset, dst + offset, remainder);
+   }
+ }
+ static size_t base64_encode(const char* src,
+                             size_t slen,
+                             char* dst,
+                             size_t dlen) {
+   // We know how much we'll write, just make sure that there's space.
+   assert(dlen >= base64_encoded_size(slen) &&
+       "not enough space provided for base64 encode");
+   dlen = base64_encoded_size(slen);
+   unsigned a;
+   unsigned b;
+   unsigned c;
+   unsigned i;
+   unsigned k;
+   unsigned n;
+   static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                               "abcdefghijklmnopqrstuvwxyz"
+                               "0123456789+/";
+   i = 0;
+   k = 0;
+   n = slen / 3 * 3;
+   while (i < n) {
+     a = src[i + 0] & 0xff;
+     b = src[i + 1] & 0xff;
+     c = src[i + 2] & 0xff;
+     dst[k + 0] = table[a >> 2];
+     dst[k + 1] = table[((a & 3) << 4) | (b >> 4)];
+     dst[k + 2] = table[((b & 0x0f) << 2) | (c >> 6)];
+     dst[k + 3] = table[c & 0x3f];
+     i += 3;
+     k += 4;
+   }
+   if (n != slen) {
+     switch (slen - n) {
+       case 1:
+         a = src[i + 0] & 0xff;
+         dst[k + 0] = table[a >> 2];
+         dst[k + 1] = table[(a & 3) << 4];
+         dst[k + 2] = '=';
+         dst[k + 3] = '=';
+         break;
+       case 2:
+         a = src[i + 0] & 0xff;
+         b = src[i + 1] & 0xff;
+         dst[k + 0] = table[a >> 2];
+         dst[k + 1] = table[((a & 3) << 4) | (b >> 4)];
+         dst[k + 2] = table[(b & 0x0f) << 2];
+         dst[k + 3] = '=';
+         break;
+     }
+   }
+   return dlen;
+ }
+ static size_t hex_encode(const char* src, size_t slen, char* dst, size_t dlen) {
+   // We know how much we'll write, just make sure that there's space.
+   assert(dlen >= slen * 2 &&
+       "not enough space provided for hex encode");
+   dlen = slen * 2;
+   for (uint32_t i = 0, k = 0; k < dlen; i += 1, k += 2) {
+     static const char hex[] = "0123456789abcdef";
+     uint8_t val = static_cast<uint8_t>(src[i]);
+     dst[k + 0] = hex[val >> 4];
+     dst[k + 1] = hex[val & 15];
+   }
+   return dlen;
+ }
+ Local<Value> StringBytes::Encode(const char* buf,
+                                  size_t buflen,
+                                  enum encoding encoding) {
+   HandleScope scope;
+   assert(buflen <= Buffer::kMaxLength);
+   if (!buflen && encoding != BUFFER)
+     return scope.Close(String::Empty());
+   Local<String> val;
+   switch (encoding) {
+     case BUFFER:
+       return scope.Close(
+           Buffer::New(static_cast<const char*>(buf), buflen)->handle_);
+     case ASCII:
+       if (contains_non_ascii(buf, buflen)) {
+         char* out = new char[buflen];
+         force_ascii(buf, out, buflen);
+         val = String::New(out, buflen);
+         delete[] out;
+       } else {
+         val = String::New(buf, buflen);
+       }
+       break;
+     case UTF8:
+       val = String::New(buf, buflen);
+       break;
+     case BINARY: {
+       // TODO(isaacs) use ExternalTwoByteString?
+       const unsigned char *cbuf = reinterpret_cast<const unsigned char*>(buf);
+       uint16_t * twobytebuf = new uint16_t[buflen];
+       for (size_t i = 0; i < buflen; i++) {
+         // XXX is the following line platform independent?
+         twobytebuf[i] = cbuf[i];
+       }
+       val = String::New(twobytebuf, buflen);
+       delete[] twobytebuf;
+       break;
+     }
+     case BASE64: {
+       size_t dlen = base64_encoded_size(buflen);
+       char* dst = new char[dlen];
+       size_t written = base64_encode(buf, buflen, dst, dlen);
+       assert(written == dlen);
+       val = String::New(dst, dlen);
+       delete[] dst;
+       break;
+     }
+     case UCS2: {
+       const uint16_t* data = reinterpret_cast<const uint16_t*>(buf);
+       val = String::New(data, buflen / 2);
+       break;
+     }
+     case HEX: {
+       size_t dlen = buflen * 2;
+       char* dst = new char[dlen];
+       size_t written = hex_encode(buf, buflen, dst, dlen);
+       assert(written == dlen);
+       val = String::New(dst, dlen);
+       delete[] dst;
+       break;
+     }
+     default:
+       assert(0 && "unknown encoding");
+       break;
+   }
+   return scope.Close(val);
+ }
+ }  // namespace node
Simple merge