Skip to content

Commit 8a2ba6f

Browse files
zbjornsonFishrock123
authored andcommitted
src: fix build for older clang
Removes use of builtins that are unavailable for older clang. Per benchmarks, only uses builtins on Windows, where speedup is significant. Also adds test for unaligned ucs2 buffer write. Between #3410 and #7645, bytes were swapped twice on bigendian platforms if buffer was not two-byte aligned. See comment in #7645. PR-URL: #7645 Fixes: #7618 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: James M Snell <[email protected]> Conflicts: src/node_buffer.cc
1 parent bee1955 commit 8a2ba6f

File tree

5 files changed

+119
-115
lines changed

5 files changed

+119
-115
lines changed

β€Žsrc/node_buffer.ccβ€Ž

Lines changed: 3 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
#include <string.h>
1414
#include <limits.h>
15-
#include <utility>
1615

1716
#define BUFFER_ID 0xB0E4
1817

@@ -52,38 +51,6 @@
5251
#define BUFFER_MALLOC(length) \
5352
zero_fill_all_buffers ? node::Calloc(length, 1) : node::Malloc(length)
5453

55-
#if defined(__GNUC__) || defined(__clang__)
56-
#define BSWAP_INTRINSIC_2(x) __builtin_bswap16(x)
57-
#define BSWAP_INTRINSIC_4(x) __builtin_bswap32(x)
58-
#define BSWAP_INTRINSIC_8(x) __builtin_bswap64(x)
59-
#elif defined(__linux__)
60-
#include <byteswap.h>
61-
#define BSWAP_INTRINSIC_2(x) bswap_16(x)
62-
#define BSWAP_INTRINSIC_4(x) bswap_32(x)
63-
#define BSWAP_INTRINSIC_8(x) bswap_64(x)
64-
#elif defined(_MSC_VER)
65-
#include <intrin.h>
66-
#define BSWAP_INTRINSIC_2(x) _byteswap_ushort(x);
67-
#define BSWAP_INTRINSIC_4(x) _byteswap_ulong(x);
68-
#define BSWAP_INTRINSIC_8(x) _byteswap_uint64(x);
69-
#else
70-
#define BSWAP_INTRINSIC_2(x) ((x) << 8) | ((x) >> 8)
71-
#define BSWAP_INTRINSIC_4(x) \
72-
(((x) & 0xFF) << 24) | \
73-
(((x) & 0xFF00) << 8) | \
74-
(((x) >> 8) & 0xFF00) | \
75-
(((x) >> 24) & 0xFF)
76-
#define BSWAP_INTRINSIC_8(x) \
77-
(((x) & 0xFF00000000000000ull) >> 56) | \
78-
(((x) & 0x00FF000000000000ull) >> 40) | \
79-
(((x) & 0x0000FF0000000000ull) >> 24) | \
80-
(((x) & 0x000000FF00000000ull) >> 8) | \
81-
(((x) & 0x00000000FF000000ull) << 8) | \
82-
(((x) & 0x0000000000FF0000ull) << 24) | \
83-
(((x) & 0x000000000000FF00ull) << 40) | \
84-
(((x) & 0x00000000000000FFull) << 56)
85-
#endif
86-
8754
namespace node {
8855

8956
// if true, all Buffer and SlowBuffer instances will automatically zero-fill
@@ -1199,23 +1166,7 @@ void Swap16(const FunctionCallbackInfo<Value>& args) {
11991166
Environment* env = Environment::GetCurrent(args);
12001167
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]);
12011168
SPREAD_ARG(args[0], ts_obj);
1202-
1203-
CHECK_EQ(ts_obj_length % 2, 0);
1204-
1205-
int align = reinterpret_cast<uintptr_t>(ts_obj_data) % sizeof(uint16_t);
1206-
1207-
if (align == 0) {
1208-
uint16_t* data16 = reinterpret_cast<uint16_t*>(ts_obj_data);
1209-
size_t len16 = ts_obj_length / 2;
1210-
for (size_t i = 0; i < len16; i++) {
1211-
data16[i] = BSWAP_INTRINSIC_2(data16[i]);
1212-
}
1213-
} else {
1214-
for (size_t i = 0; i < ts_obj_length; i += 2) {
1215-
std::swap(ts_obj_data[i], ts_obj_data[i + 1]);
1216-
}
1217-
}
1218-
1169+
SwapBytes16(ts_obj_data, ts_obj_length);
12191170
args.GetReturnValue().Set(args[0]);
12201171
}
12211172

@@ -1224,24 +1175,7 @@ void Swap32(const FunctionCallbackInfo<Value>& args) {
12241175
Environment* env = Environment::GetCurrent(args);
12251176
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]);
12261177
SPREAD_ARG(args[0], ts_obj);
1227-
1228-
CHECK_EQ(ts_obj_length % 4, 0);
1229-
1230-
int align = reinterpret_cast<uintptr_t>(ts_obj_data) % sizeof(uint32_t);
1231-
1232-
if (align == 0) {
1233-
uint32_t* data32 = reinterpret_cast<uint32_t*>(ts_obj_data);
1234-
size_t len32 = ts_obj_length / 4;
1235-
for (size_t i = 0; i < len32; i++) {
1236-
data32[i] = BSWAP_INTRINSIC_4(data32[i]);
1237-
}
1238-
} else {
1239-
for (size_t i = 0; i < ts_obj_length; i += 4) {
1240-
std::swap(ts_obj_data[i], ts_obj_data[i + 3]);
1241-
std::swap(ts_obj_data[i + 1], ts_obj_data[i + 2]);
1242-
}
1243-
}
1244-
1178+
SwapBytes32(ts_obj_data, ts_obj_length);
12451179
args.GetReturnValue().Set(args[0]);
12461180
}
12471181

@@ -1250,26 +1184,7 @@ void Swap64(const FunctionCallbackInfo<Value>& args) {
12501184
Environment* env = Environment::GetCurrent(args);
12511185
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]);
12521186
SPREAD_ARG(args[0], ts_obj);
1253-
1254-
CHECK_EQ(ts_obj_length % 8, 0);
1255-
1256-
int align = reinterpret_cast<uintptr_t>(ts_obj_data) % sizeof(uint64_t);
1257-
1258-
if (align == 0) {
1259-
uint64_t* data64 = reinterpret_cast<uint64_t*>(ts_obj_data);
1260-
size_t len32 = ts_obj_length / 8;
1261-
for (size_t i = 0; i < len32; i++) {
1262-
data64[i] = BSWAP_INTRINSIC_8(data64[i]);
1263-
}
1264-
} else {
1265-
for (size_t i = 0; i < ts_obj_length; i += 8) {
1266-
std::swap(ts_obj_data[i], ts_obj_data[i + 7]);
1267-
std::swap(ts_obj_data[i + 1], ts_obj_data[i + 6]);
1268-
std::swap(ts_obj_data[i + 2], ts_obj_data[i + 5]);
1269-
std::swap(ts_obj_data[i + 3], ts_obj_data[i + 4]);
1270-
}
1271-
}
1272-
1187+
SwapBytes64(ts_obj_data, ts_obj_length);
12731188
args.GetReturnValue().Set(args[0]);
12741189
}
12751190

β€Žsrc/string_bytes.ccβ€Ž

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -310,27 +310,13 @@ size_t StringBytes::Write(Isolate* isolate,
310310
if (chars_written != nullptr)
311311
*chars_written = nchars;
312312

313-
if (!IsBigEndian())
314-
break;
315-
316313
// Node's "ucs2" encoding wants LE character data stored in
317314
// the Buffer, so we need to reorder on BE platforms. See
318315
// http://nodejs.org/api/buffer.html regarding Node's "ucs2"
319316
// encoding specification
317+
if (IsBigEndian())
318+
SwapBytes16(buf, nbytes);
320319

321-
const bool is_aligned =
322-
reinterpret_cast<uintptr_t>(buf) % sizeof(uint16_t);
323-
if (is_aligned) {
324-
uint16_t* const dst = reinterpret_cast<uint16_t*>(buf);
325-
SwapBytes(dst, dst, nchars);
326-
}
327-
328-
ASSERT_EQ(sizeof(uint16_t), 2);
329-
for (size_t i = 0; i < nchars; i++) {
330-
char tmp = buf[i * 2];
331-
buf[i * 2] = buf[i * 2 + 1];
332-
buf[i * 2 + 1] = tmp;
333-
}
334320
break;
335321
}
336322

@@ -706,17 +692,19 @@ Local<Value> StringBytes::Encode(Isolate* isolate,
706692
Local<Value> StringBytes::Encode(Isolate* isolate,
707693
const uint16_t* buf,
708694
size_t buflen) {
709-
Local<String> val;
695+
// Node's "ucs2" encoding expects LE character data inside a
696+
// Buffer, so we need to reorder on BE platforms. See
697+
// http://nodejs.org/api/buffer.html regarding Node's "ucs2"
698+
// encoding specification
710699
std::vector<uint16_t> dst;
711700
if (IsBigEndian()) {
712-
// Node's "ucs2" encoding expects LE character data inside a
713-
// Buffer, so we need to reorder on BE platforms. See
714-
// http://nodejs.org/api/buffer.html regarding Node's "ucs2"
715-
// encoding specification
716-
dst.resize(buflen);
717-
SwapBytes(&dst[0], buf, buflen);
701+
dst.assign(buf, buf + buflen);
702+
size_t nbytes = buflen * sizeof(dst[0]);
703+
SwapBytes16(reinterpret_cast<char*>(&dst[0]), nbytes);
718704
buf = &dst[0];
719705
}
706+
707+
Local<String> val;
720708
if (buflen < EXTERN_APEX) {
721709
val = String::NewFromTwoByte(isolate,
722710
buf,

β€Žsrc/util-inl.hβ€Ž

Lines changed: 94 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,30 @@
44
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
55

66
#include "util.h"
7+
#include <cstring>
8+
9+
#if defined(_MSC_VER)
10+
#include <intrin.h>
11+
#define BSWAP_2(x) _byteswap_ushort(x)
12+
#define BSWAP_4(x) _byteswap_ulong(x)
13+
#define BSWAP_8(x) _byteswap_uint64(x)
14+
#else
15+
#define BSWAP_2(x) ((x) << 8) | ((x) >> 8)
16+
#define BSWAP_4(x) \
17+
(((x) & 0xFF) << 24) | \
18+
(((x) & 0xFF00) << 8) | \
19+
(((x) >> 8) & 0xFF00) | \
20+
(((x) >> 24) & 0xFF)
21+
#define BSWAP_8(x) \
22+
(((x) & 0xFF00000000000000ull) >> 56) | \
23+
(((x) & 0x00FF000000000000ull) >> 40) | \
24+
(((x) & 0x0000FF0000000000ull) >> 24) | \
25+
(((x) & 0x000000FF00000000ull) >> 8) | \
26+
(((x) & 0x00000000FF000000ull) << 8) | \
27+
(((x) & 0x0000000000FF0000ull) << 24) | \
28+
(((x) & 0x000000000000FF00ull) << 40) | \
29+
(((x) & 0x00000000000000FFull) << 56)
30+
#endif
731

832
namespace node {
933

@@ -200,9 +224,76 @@ TypeName* Unwrap(v8::Local<v8::Object> object) {
200224
return static_cast<TypeName*>(pointer);
201225
}
202226

203-
void SwapBytes(uint16_t* dst, const uint16_t* src, size_t buflen) {
204-
for (size_t i = 0; i < buflen; i += 1)
205-
dst[i] = (src[i] << 8) | (src[i] >> 8);
227+
void SwapBytes16(char* data, size_t nbytes) {
228+
CHECK_EQ(nbytes % 2, 0);
229+
230+
#if defined(_MSC_VER)
231+
int align = reinterpret_cast<uintptr_t>(data) % sizeof(uint16_t);
232+
if (align == 0) {
233+
// MSVC has no strict aliasing, and is able to highly optimize this case.
234+
uint16_t* data16 = reinterpret_cast<uint16_t*>(data);
235+
size_t len16 = nbytes / sizeof(*data16);
236+
for (size_t i = 0; i < len16; i++) {
237+
data16[i] = BSWAP_2(data16[i]);
238+
}
239+
return;
240+
}
241+
#endif
242+
243+
uint16_t temp;
244+
for (size_t i = 0; i < nbytes; i += sizeof(temp)) {
245+
memcpy(&temp, &data[i], sizeof(temp));
246+
temp = BSWAP_2(temp);
247+
memcpy(&data[i], &temp, sizeof(temp));
248+
}
249+
}
250+
251+
void SwapBytes32(char* data, size_t nbytes) {
252+
CHECK_EQ(nbytes % 4, 0);
253+
254+
#if defined(_MSC_VER)
255+
int align = reinterpret_cast<uintptr_t>(data) % sizeof(uint32_t);
256+
// MSVC has no strict aliasing, and is able to highly optimize this case.
257+
if (align == 0) {
258+
uint32_t* data32 = reinterpret_cast<uint32_t*>(data);
259+
size_t len32 = nbytes / sizeof(*data32);
260+
for (size_t i = 0; i < len32; i++) {
261+
data32[i] = BSWAP_4(data32[i]);
262+
}
263+
return;
264+
}
265+
#endif
266+
267+
uint32_t temp;
268+
for (size_t i = 0; i < nbytes; i += sizeof(temp)) {
269+
memcpy(&temp, &data[i], sizeof(temp));
270+
temp = BSWAP_4(temp);
271+
memcpy(&data[i], &temp, sizeof(temp));
272+
}
273+
}
274+
275+
void SwapBytes64(char* data, size_t nbytes) {
276+
CHECK_EQ(nbytes % 8, 0);
277+
278+
#if defined(_MSC_VER)
279+
int align = reinterpret_cast<uintptr_t>(data) % sizeof(uint64_t);
280+
if (align == 0) {
281+
// MSVC has no strict aliasing, and is able to highly optimize this case.
282+
uint64_t* data64 = reinterpret_cast<uint64_t*>(data);
283+
size_t len64 = nbytes / sizeof(*data64);
284+
for (size_t i = 0; i < len64; i++) {
285+
data64[i] = BSWAP_8(data64[i]);
286+
}
287+
return;
288+
}
289+
#endif
290+
291+
uint64_t temp;
292+
for (size_t i = 0; i < nbytes; i += sizeof(temp)) {
293+
memcpy(&temp, &data[i], sizeof(temp));
294+
temp = BSWAP_8(temp);
295+
memcpy(&data[i], &temp, sizeof(temp));
296+
}
206297
}
207298

208299
char ToLower(char c) {

β€Žsrc/util.hβ€Ž

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,11 @@ inline void ClearWrap(v8::Local<v8::Object> object);
244244
template <typename TypeName>
245245
inline TypeName* Unwrap(v8::Local<v8::Object> object);
246246

247-
inline void SwapBytes(uint16_t* dst, const uint16_t* src, size_t buflen);
247+
// Swaps bytes in place. nbytes is the number of bytes to swap and must be a
248+
// multiple of the word size (checked by function).
249+
inline void SwapBytes16(char* data, size_t nbytes);
250+
inline void SwapBytes32(char* data, size_t nbytes);
251+
inline void SwapBytes64(char* data, size_t nbytes);
248252

249253
// tolower() is locale-sensitive. Use ToLower() instead.
250254
inline char ToLower(char c);

β€Žtest/parallel/test-buffer-alloc.jsβ€Ž

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,12 @@ assert.strictEqual('<Buffer 81 a3 66 6f 6f a3 62 61 72>', x.inspect());
585585
assert.strictEqual(b.toString(encoding), 'γ‚γ„γ†γˆγŠ');
586586
});
587587

588+
['ucs2', 'ucs-2', 'utf16le', 'utf-16le'].forEach((encoding) => {
589+
const b = Buffer.allocUnsafe(11);
590+
b.write('γ‚γ„γ†γˆγŠ', 1, encoding);
591+
assert.strictEqual(b.toString(encoding, 1), 'γ‚γ„γ†γˆγŠ');
592+
});
593+
588594
{
589595
// latin1 encoding should write only one byte per character.
590596
const b = Buffer.from([0xde, 0xad, 0xbe, 0xef]);

0 commit comments

Comments
Β (0)