Skip to content

Commit

Permalink
string_bytes: fix unaligned write in UCS2
Browse files Browse the repository at this point in the history
Support unaligned output buffer when writing out UCS2 in
`StringBytes::Write`.

Fix: #2457
  • Loading branch information
indutny committed Aug 21, 2015
1 parent ec6e5c7 commit d150144
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 12 deletions.
78 changes: 66 additions & 12 deletions src/string_bytes.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}

Expand Down
9 changes: 9 additions & 0 deletions src/string_bytes.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,15 @@ class StringBytes {
enum encoding encoding) {
return Encode(v8::Isolate::GetCurrent(), buf, buflen, encoding);
})

private:
static size_t WriteUCS2(char* buf,
size_t buflen,
size_t nbytes,
const char* data,
v8::Local<v8::String> str,
int flags,
size_t* chars_written);
};

} // namespace node
Expand Down

0 comments on commit d150144

Please sign in to comment.