diff options
author | Fedor Indutny <fedor@indutny.com> | 2015-08-21 02:57:14 +0300 |
---|---|---|
committer | Fedor Indutny <fedor@indutny.com> | 2015-08-21 22:26:03 +0300 |
commit | 68e53ddcbad4ce4a2f44362411b6960c716b7e3e (patch) | |
tree | 9714b7be99d84c5231a5ea379f76f9b08d6a0d5e /src/string_bytes.cc | |
parent | 1c20b87d1979e741f1f1698f8120169d32a779ae (diff) |
string_bytes: fix unaligned write in UCS2
Support unaligned output buffer when writing out UCS2 in
`StringBytes::Write`.
Fix: https://github.com/nodejs/node/issues/2457
PR-URL: https://github.com/nodejs/node/pull/2480
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
Diffstat (limited to 'src/string_bytes.cc')
-rw-r--r-- | src/string_bytes.cc | 78 |
1 files changed, 66 insertions, 12 deletions
diff --git a/src/string_bytes.cc b/src/string_bytes.cc index 0abdbf85bbc..3d568c6ca13 100644 --- a/src/string_bytes.cc +++ b/src/string_bytes.cc @@ -293,6 +293,46 @@ bool StringBytes::GetExternalParts(Isolate* isolate, } +size_t StringBytes::WriteUCS2(char* buf, + size_t buflen, + size_t nbytes, + const char* data, + Local<String> str, + int flags, + size_t* chars_written) { + uint16_t* const dst = reinterpret_cast<uint16_t*>(buf); + + size_t max_chars = (buflen / sizeof(*dst)); + size_t nchars; + size_t alignment = reinterpret_cast<uintptr_t>(dst) % sizeof(*dst); + if (alignment == 0) { + nchars = str->Write(dst, 0, max_chars, flags); + *chars_written = nchars; + return nchars * sizeof(*dst); + } + + uint16_t* aligned_dst = + reinterpret_cast<uint16_t*>(buf + sizeof(*dst) - alignment); + ASSERT_EQ(reinterpret_cast<uintptr_t>(aligned_dst) % sizeof(*dst), 0); + + // Write all but the last char + nchars = str->Write(aligned_dst, 0, max_chars - 1, flags); + + // Shift everything to unaligned-left + memmove(dst, aligned_dst, nchars * sizeof(*dst)); + + // One more char to be written + uint16_t last; + if (nchars == max_chars - 1 && str->Write(&last, nchars, 1, flags) != 0) { + memcpy(buf + nchars * sizeof(*dst), &last, sizeof(last)); + nchars++; + } + + *chars_written = nchars; + return nchars * sizeof(*dst); +} + + size_t StringBytes::Write(Isolate* isolate, char* buf, size_t buflen, @@ -334,26 +374,40 @@ size_t StringBytes::Write(Isolate* isolate, break; case UCS2: { - uint16_t* const dst = reinterpret_cast<uint16_t*>(buf); size_t nchars; + if (is_extern && !str->IsOneByte()) { memcpy(buf, data, nbytes); - nchars = nbytes / sizeof(*dst); + nchars = nbytes / sizeof(uint16_t); } else { - nchars = buflen / sizeof(*dst); - nchars = str->Write(dst, 0, nchars, flags); - nbytes = nchars * sizeof(*dst); + nbytes = WriteUCS2(buf, buflen, nbytes, data, str, flags, &nchars); } - if (IsBigEndian()) { - // Node's "ucs2" encoding wants LE character data stored in - // the Buffer, so we need to reorder on BE platforms. See - // http://nodejs.org/api/buffer.html regarding Node's "ucs2" - // encoding specification + if (chars_written != nullptr) + *chars_written = nchars; + + if (!IsBigEndian()) + break; + + // Node's "ucs2" encoding wants LE character data stored in + // the Buffer, so we need to reorder on BE platforms. See + // http://nodejs.org/api/buffer.html regarding Node's "ucs2" + // encoding specification + + const bool is_aligned = + reinterpret_cast<uintptr_t>(buf) % sizeof(uint16_t); + if (is_aligned) { + uint16_t* const dst = reinterpret_cast<uint16_t*>(buf); for (size_t i = 0; i < nchars; i++) dst[i] = dst[i] << 8 | dst[i] >> 8; + break; + } + + ASSERT_EQ(sizeof(uint16_t), 2); + for (size_t i = 0; i < nchars; i++) { + char tmp = buf[i * 2]; + buf[i * 2] = buf[i * 2 + 1]; + buf[i * 2 + 1] = tmp; } - if (chars_written != nullptr) - *chars_written = nchars; break; } |