Buffer: adjust buffer size so the base64-decoded input fits snugly.
authorBen Noordhuis <info@bnoordhuis.nl>
Wed, 28 Jul 2010 12:20:23 +0000 (14:20 +0200)
committerRyan Dahl <ry@tinyclouds.org>
Wed, 28 Jul 2010 18:37:23 +0000 (11:37 -0700)
Stops Valgrind from complaining about uninitialized memory access.

src/node.cc
src/node.h
src/node_buffer.cc
test/simple/test-buffer.js

index ada473d..81c8fb8 100644 (file)
@@ -783,6 +783,8 @@ enum encoding ParseEncoding(Handle<Value> 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) {
index d879e4b..e20c801 100644 (file)
@@ -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<v8::Value> encoding_v,
                             enum encoding _default = BINARY);
 void FatalException(v8::TryCatch &try_catch);
index 226baf7..ad5ee45 100644 (file)
@@ -149,6 +149,29 @@ Handle<Value> Buffer::New(const Arguments &args) {
     Local<String> 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<Value> 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")));
index 3fb5b2d..0b1a02c 100644 (file)
@@ -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(), '********************');