diff --git a/src/string_bytes.cc b/src/string_bytes.cc index e162fd70e0..416bc5ae3a 100644 --- a/src/string_bytes.cc +++ b/src/string_bytes.cc @@ -36,9 +36,9 @@ namespace node { -using v8::Local; using v8::Handle; using v8::HandleScope; +using v8::Local; using v8::Object; using v8::String; using v8::Value; @@ -227,41 +227,65 @@ static inline size_t hex_decode(char* buf, } +bool GetExternalParts(Handle val, const char** data, size_t* len) { + if (Buffer::HasInstance(val)) { + *data = Buffer::Data(val); + *len = Buffer::Length(val); + return true; + + } + + assert(val->IsString()); + Local str = Local::New(val.As()); + + if (str->IsExternalAscii()) { + const String::ExternalAsciiStringResource* ext; + ext = str->GetExternalAsciiStringResource(); + *data = ext->data(); + *len = ext->length(); + return true; + + } else if (str->IsExternal()) { + const String::ExternalStringResource* ext; + ext = str->GetExternalStringResource(); + *data = reinterpret_cast(ext->data()); + *len = ext->length(); + return true; + } + + return false; +} + + size_t StringBytes::Write(char* buf, size_t buflen, Handle val, enum encoding encoding, int* chars_written) { - HandleScope scope(node_isolate); + const char* data; 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 valObj = Local::New(val.As()); - 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; - } + bool is_extern = GetExternalParts(val, &data, &len); Local str = val->ToString(); + len = len < buflen ? len : buflen; int flags = String::NO_NULL_TERMINATION | String::HINT_MANY_WRITES_EXPECTED; switch (encoding) { case ASCII: - len = str->WriteOneByte(reinterpret_cast(buf), - 0, - buflen, - flags); - if (chars_written != NULL) { + case BINARY: + case BUFFER: + if (is_extern) + memcpy(buf, data, len); + else + len = str->WriteOneByte(reinterpret_cast(buf), + 0, + buflen, + flags); + if (chars_written != NULL) *chars_written = len; - } break; case UTF8: @@ -269,50 +293,38 @@ size_t StringBytes::Write(char* buf, break; case UCS2: - len = str->Write(reinterpret_cast(buf), 0, buflen, flags); - if (chars_written != NULL) { + if (is_extern) + memcpy(buf, data, len * 2); + else + len = str->Write(reinterpret_cast(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(&twobytebuf[i]); - buf[i] = b[0]; + case BASE64: + if (is_extern) { + base64_decode(buf, buflen, data, len); + } else { + String::AsciiValue value(str); + len = base64_decode(buf, buflen, *value, value.length()); } - if (chars_written != NULL) { *chars_written = len; } - - delete[] twobytebuf; break; - } - case HEX: { - String::AsciiValue value(str); - len = hex_decode(buf, buflen, *value, value.length()); + case HEX: + if (is_extern) { + hex_decode(buf, buflen, data, len); + } else { + 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"); diff --git a/test/simple/test-stringbytes-external.js b/test/simple/test-stringbytes-external.js new file mode 100644 index 0000000000..87823cd072 --- /dev/null +++ b/test/simple/test-stringbytes-external.js @@ -0,0 +1,74 @@ +// 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. + +var common = require('../common'); +var assert = require('assert'); +// minimum string size to overflow into external string space +var EXTERN_APEX = 0xFBEE9; + +// manually controlled string for checking binary output +var ucs2_control = 'a\u0000'; +var write_str = 'a'; + + +// first do basic checks +var b = new Buffer(write_str, 'ucs2'); +var c = b.toString('binary'); +assert.equal(b[0], 0x61); +assert.equal(b[1], 0); +assert.equal(ucs2_control, c); + + +// grow the strings to proper length +while (write_str.length <= EXTERN_APEX) { + write_str += write_str; + ucs2_control += ucs2_control; +} +write_str += write_str.substr(0, EXTERN_APEX - write_str.length); +ucs2_control += ucs2_control.substr(0, EXTERN_APEX * 2 - ucs2_control.length); + + +// check resultant buffer and output string +var b = new Buffer(write_str, 'ucs2'); +// check fist Buffer created from write string +for (var i = 0; i < b.length; i += 2) { + assert.equal(b[i], 0x61); + assert.equal(b[i + 1], 0); +} +// create another string to create an external string +var b_bin = b.toString('binary'); +var b_ucs = b.toString('ucs2'); +// check control against external binary string +assert.equal(ucs2_control, b_bin); +// create buffer copy from external +var c_bin = new Buffer(b_bin, 'binary'); +var c_ucs = new Buffer(b_ucs, 'ucs2'); +// make sure they're the same length +assert.equal(c_bin.length, c_ucs.length); +// make sure Buffers from externals are the same +for (var i = 0; i < c_bin.length; i++) { + assert.equal(c_bin[i], c_ucs[i], c_bin[i] + ' == ' + c_ucs[i] + + ' : index ' + i); +} +// check resultant strings +assert.equal(c_bin.toString('ucs2'), c_ucs.toString('ucs2')); +assert.equal(c_bin.toString('binary'), ucs2_control); +assert.equal(c_ucs.toString('binary'), ucs2_control);