diff --git a/src/node.cc b/src/node.cc index ada473d074..81c8fb86ca 100644 --- a/src/node.cc +++ b/src/node.cc @@ -783,6 +783,8 @@ enum encoding ParseEncoding(Handle encoding_v, enum encoding _default) { return UTF8; } else if (strcasecmp(*encoding, "ascii") == 0) { return ASCII; + } else if (strcasecmp(*encoding, "base64") == 0) { + return BASE64; } else if (strcasecmp(*encoding, "binary") == 0) { return BINARY; } else if (strcasecmp(*encoding, "raw") == 0) { diff --git a/src/node.h b/src/node.h index d879e4b236..e20c801fd2 100644 --- a/src/node.h +++ b/src/node.h @@ -41,7 +41,7 @@ do { \ __callback##_TEM); \ } while (0) -enum encoding {ASCII, UTF8, BINARY}; +enum encoding {ASCII, UTF8, BASE64, BINARY}; enum encoding ParseEncoding(v8::Handle encoding_v, enum encoding _default = BINARY); void FatalException(v8::TryCatch &try_catch); diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 226baf7aa8..ad5ee45f95 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -149,6 +149,29 @@ Handle Buffer::New(const Arguments &args) { Local s = args[0]->ToString(); enum encoding e = ParseEncoding(args[1], UTF8); int length = e == UTF8 ? s->Utf8Length() : s->Length(); + + // input gets base64-decoded, adjust buffer size + if (e == BASE64 && length > 0) { + const int remainder = length % 4; + + length = (length / 4) * 3; + if (remainder) { + if (length == 0 && remainder == 1) { + // special case: 1-byte input cannot be decoded, return empty buffer + length = 0; + } else { + // non-padded input, add 1 or 2 extra bytes + length += 1 + (remainder == 3); + } + } else { + // check for trailing padding (1 or 2 bytes) + const String::AsciiValue data(s); + const char *const end = *data + data.length(); + if (end[-1] == '=') length--; + if (end[-2] == '=') length--; + } + } + buffer = new Buffer(length); } else if (Buffer::HasInstance(args[0]) && args.Length() > 2) { // var slice = new Buffer(buffer, 123, 130); @@ -516,6 +539,11 @@ Handle Buffer::Base64Write(const Arguments &args) { String::AsciiValue s(args[0]->ToString()); size_t offset = args[1]->Int32Value(); + // handle zero-length buffers graciously + if (offset == 0 && buffer->length_ == 0) { + return scope.Close(Integer::New(0)); + } + if (offset >= buffer->length_) { return ThrowException(Exception::TypeError(String::New( "Offset is out of bounds"))); diff --git a/test/simple/test-buffer.js b/test/simple/test-buffer.js index 3fb5b2d75d..0b1a02caec 100644 --- a/test/simple/test-buffer.js +++ b/test/simple/test-buffer.js @@ -258,5 +258,43 @@ bytesWritten = b.write(expected, 0, 'base64'); assert.equal(quote, b.toString('ascii', 0, quote.length)); assert.equal(quote.length+1, bytesWritten); // writes a \0 too - - +assert.equal(new Buffer('', 'base64').toString(), ''); +assert.equal(new Buffer('K', 'base64').toString(), ''); + +// multiple-of-4 with padding +assert.equal(new Buffer('Kg==', 'base64').toString(), '*'); +assert.equal(new Buffer('Kio=', 'base64').toString(), '**'); +assert.equal(new Buffer('Kioq', 'base64').toString(), '***'); +assert.equal(new Buffer('KioqKg==', 'base64').toString(), '****'); +assert.equal(new Buffer('KioqKio=', 'base64').toString(), '*****'); +assert.equal(new Buffer('KioqKioq', 'base64').toString(), '******'); +assert.equal(new Buffer('KioqKioqKg==', 'base64').toString(), '*******'); +assert.equal(new Buffer('KioqKioqKio=', 'base64').toString(), '********'); +assert.equal(new Buffer('KioqKioqKioq', 'base64').toString(), '*********'); +assert.equal(new Buffer('KioqKioqKioqKg==', 'base64').toString(), '**********'); +assert.equal(new Buffer('KioqKioqKioqKio=', 'base64').toString(), '***********'); +assert.equal(new Buffer('KioqKioqKioqKioq', 'base64').toString(), '************'); +assert.equal(new Buffer('KioqKioqKioqKioqKg==', 'base64').toString(), '*************'); +assert.equal(new Buffer('KioqKioqKioqKioqKio=', 'base64').toString(), '**************'); +assert.equal(new Buffer('KioqKioqKioqKioqKioq', 'base64').toString(), '***************'); +assert.equal(new Buffer('KioqKioqKioqKioqKioqKg==', 'base64').toString(), '****************'); +assert.equal(new Buffer('KioqKioqKioqKioqKioqKio=', 'base64').toString(), '*****************'); +assert.equal(new Buffer('KioqKioqKioqKioqKioqKioq', 'base64').toString(), '******************'); +assert.equal(new Buffer('KioqKioqKioqKioqKioqKioqKg==', 'base64').toString(), '*******************'); +assert.equal(new Buffer('KioqKioqKioqKioqKioqKioqKio=', 'base64').toString(), '********************'); + +// no padding, not a multiple of 4 +assert.equal(new Buffer('Kg', 'base64').toString(), '*'); +assert.equal(new Buffer('Kio', 'base64').toString(), '**'); +assert.equal(new Buffer('KioqKg', 'base64').toString(), '****'); +assert.equal(new Buffer('KioqKio', 'base64').toString(), '*****'); +assert.equal(new Buffer('KioqKioqKg', 'base64').toString(), '*******'); +assert.equal(new Buffer('KioqKioqKio', 'base64').toString(), '********'); +assert.equal(new Buffer('KioqKioqKioqKg', 'base64').toString(), '**********'); +assert.equal(new Buffer('KioqKioqKioqKio', 'base64').toString(), '***********'); +assert.equal(new Buffer('KioqKioqKioqKioqKg', 'base64').toString(), '*************'); +assert.equal(new Buffer('KioqKioqKioqKioqKio', 'base64').toString(), '**************'); +assert.equal(new Buffer('KioqKioqKioqKioqKioqKg', 'base64').toString(), '****************'); +assert.equal(new Buffer('KioqKioqKioqKioqKioqKio', 'base64').toString(), '*****************'); +assert.equal(new Buffer('KioqKioqKioqKioqKioqKioqKg', 'base64').toString(), '*******************'); +assert.equal(new Buffer('KioqKioqKioqKioqKioqKioqKio', 'base64').toString(), '********************');