diff --git a/src/node_buffer.cc b/src/node_buffer.cc index e88dd1e6614e1f..3f654bfaf6546e 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -12,7 +12,6 @@ #include #include -#include #define BUFFER_ID 0xB0E4 @@ -52,38 +51,6 @@ #define BUFFER_MALLOC(length) \ zero_fill_all_buffers ? calloc(length, 1) : malloc(length) -#if defined(__GNUC__) || defined(__clang__) -#define BSWAP_INTRINSIC_2(x) __builtin_bswap16(x) -#define BSWAP_INTRINSIC_4(x) __builtin_bswap32(x) -#define BSWAP_INTRINSIC_8(x) __builtin_bswap64(x) -#elif defined(__linux__) -#include -#define BSWAP_INTRINSIC_2(x) bswap_16(x) -#define BSWAP_INTRINSIC_4(x) bswap_32(x) -#define BSWAP_INTRINSIC_8(x) bswap_64(x) -#elif defined(_MSC_VER) -#include -#define BSWAP_INTRINSIC_2(x) _byteswap_ushort(x); -#define BSWAP_INTRINSIC_4(x) _byteswap_ulong(x); -#define BSWAP_INTRINSIC_8(x) _byteswap_uint64(x); -#else -#define BSWAP_INTRINSIC_2(x) ((x) << 8) | ((x) >> 8) -#define BSWAP_INTRINSIC_4(x) \ - (((x) & 0xFF) << 24) | \ - (((x) & 0xFF00) << 8) | \ - (((x) >> 8) & 0xFF00) | \ - (((x) >> 24) & 0xFF) -#define BSWAP_INTRINSIC_8(x) \ - (((x) & 0xFF00000000000000ull) >> 56) | \ - (((x) & 0x00FF000000000000ull) >> 40) | \ - (((x) & 0x0000FF0000000000ull) >> 24) | \ - (((x) & 0x000000FF00000000ull) >> 8) | \ - (((x) & 0x00000000FF000000ull) << 8) | \ - (((x) & 0x0000000000FF0000ull) << 24) | \ - (((x) & 0x000000000000FF00ull) << 40) | \ - (((x) & 0x00000000000000FFull) << 56) -#endif - namespace node { // if true, all Buffer and SlowBuffer instances will automatically zero-fill @@ -516,8 +483,7 @@ void StringSlice(const FunctionCallbackInfo& args) { // Node's "ucs2" encoding expects LE character data inside a Buffer, so we // need to reorder on BE platforms. See http://nodejs.org/api/buffer.html // regarding Node's "ucs2" encoding specification. - const bool aligned = (reinterpret_cast(data) % sizeof(*buf) == 0); - if (IsLittleEndian() && !aligned) { + if (IsLittleEndian() && !IsAlignedTo(data)) { // Make a copy to avoid unaligned accesses in v8::String::NewFromTwoByte(). // This applies ONLY to little endian platforms, as misalignment will be // handled by a byte-swapping operation in StringBytes::Encode on @@ -1206,23 +1172,8 @@ void Swap16(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); SPREAD_ARG(args[0], ts_obj); - CHECK_EQ(ts_obj_length % 2, 0); - - int align = reinterpret_cast(ts_obj_data) % sizeof(uint16_t); - - if (align == 0) { - uint16_t* data16 = reinterpret_cast(ts_obj_data); - size_t len16 = ts_obj_length / 2; - for (size_t i = 0; i < len16; i++) { - data16[i] = BSWAP_INTRINSIC_2(data16[i]); - } - } else { - for (size_t i = 0; i < ts_obj_length; i += 2) { - std::swap(ts_obj_data[i], ts_obj_data[i + 1]); - } - } - + SwapBytes16(ts_obj_data, ts_obj_data, ts_obj_length); args.GetReturnValue().Set(args[0]); } @@ -1231,24 +1182,8 @@ void Swap32(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); SPREAD_ARG(args[0], ts_obj); - CHECK_EQ(ts_obj_length % 4, 0); - - int align = reinterpret_cast(ts_obj_data) % sizeof(uint32_t); - - if (align == 0) { - uint32_t* data32 = reinterpret_cast(ts_obj_data); - size_t len32 = ts_obj_length / 4; - for (size_t i = 0; i < len32; i++) { - data32[i] = BSWAP_INTRINSIC_4(data32[i]); - } - } else { - for (size_t i = 0; i < ts_obj_length; i += 4) { - std::swap(ts_obj_data[i], ts_obj_data[i + 3]); - std::swap(ts_obj_data[i + 1], ts_obj_data[i + 2]); - } - } - + SwapBytes32(ts_obj_data, ts_obj_data, ts_obj_length); args.GetReturnValue().Set(args[0]); } @@ -1257,26 +1192,8 @@ void Swap64(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); SPREAD_ARG(args[0], ts_obj); - CHECK_EQ(ts_obj_length % 8, 0); - - int align = reinterpret_cast(ts_obj_data) % sizeof(uint64_t); - - if (align == 0) { - uint64_t* data64 = reinterpret_cast(ts_obj_data); - size_t len32 = ts_obj_length / 8; - for (size_t i = 0; i < len32; i++) { - data64[i] = BSWAP_INTRINSIC_8(data64[i]); - } - } else { - for (size_t i = 0; i < ts_obj_length; i += 8) { - std::swap(ts_obj_data[i], ts_obj_data[i + 7]); - std::swap(ts_obj_data[i + 1], ts_obj_data[i + 6]); - std::swap(ts_obj_data[i + 2], ts_obj_data[i + 5]); - std::swap(ts_obj_data[i + 3], ts_obj_data[i + 4]); - } - } - + SwapBytes64(ts_obj_data, ts_obj_data, ts_obj_length); args.GetReturnValue().Set(args[0]); } diff --git a/src/string_bytes.cc b/src/string_bytes.cc index 6f15ad48848ed5..14411b200102d2 100644 --- a/src/string_bytes.cc +++ b/src/string_bytes.cc @@ -299,27 +299,13 @@ size_t StringBytes::Write(Isolate* isolate, 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 + if (IsBigEndian()) + SwapBytes16(buf, buf, nbytes); - const bool is_aligned = - reinterpret_cast(buf) % sizeof(uint16_t); - if (is_aligned) { - uint16_t* const dst = reinterpret_cast(buf); - SwapBytes(dst, dst, nchars); - } - - 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; - } break; } @@ -703,7 +689,9 @@ Local StringBytes::Encode(Isolate* isolate, // http://nodejs.org/api/buffer.html regarding Node's "ucs2" // encoding specification dst.resize(buflen); - SwapBytes(&dst[0], buf, buflen); + SwapBytes16(reinterpret_cast(&dst[0]), + reinterpret_cast(&buf[0]), + buflen * sizeof(buf[0])); buf = &dst[0]; } if (buflen < EXTERN_APEX) { diff --git a/src/util-inl.h b/src/util-inl.h index a167a57c5ab67d..07298c4560482b 100644 --- a/src/util-inl.h +++ b/src/util-inl.h @@ -4,6 +4,7 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include "util.h" +#include // memcpy() namespace node { @@ -200,9 +201,69 @@ TypeName* Unwrap(v8::Local object) { return static_cast(pointer); } -void SwapBytes(uint16_t* dst, const uint16_t* src, size_t buflen) { - for (size_t i = 0; i < buflen; i += 1) - dst[i] = (src[i] << 8) | (src[i] >> 8); +template +bool IsAlignedTo(const U* p) { + return reinterpret_cast(p) % sizeof(T) == 0; +} + +uint16_t ByteSwap(uint16_t v) { + uint16_t a = (v >> 0) & 255; + uint16_t b = (v >> 8) & 255; + return (a << 8) | b; +} + +uint32_t ByteSwap(uint32_t v) { + uint32_t a = (v >> 0) & 255; + uint32_t b = (v >> 8) & 255; + uint32_t c = (v >> 16) & 255; + uint32_t d = (v >> 24) & 255; + return (a << 24) | (b << 16) | (c << 8) | d; +} + +uint64_t ByteSwap(uint64_t v) { + uint64_t a = (v >> 0) & 255; + uint64_t b = (v >> 8) & 255; + uint64_t c = (v >> 16) & 255; + uint64_t d = (v >> 24) & 255; + uint64_t e = (v >> 32) & 255; + uint64_t f = (v >> 40) & 255; + uint64_t g = (v >> 48) & 255; + uint64_t h = (v >> 56) & 255; + return (a << 56) | (b << 48) | (c << 40) | (d << 32) | + (e << 24) | (f << 16) | (g << 8) | h; +} + +template +inline void DoSwapBytes(char* dst, const char* src, size_t size) { + for (size_t i = 0; i < size; i += sizeof(T)) { + T v; + memcpy(&v, &src[i], sizeof(v)); + v = ByteSwap(v); + memcpy(&dst[i], &v, sizeof(v)); + } +} + +template +void SwapBytes(char* dst, const char* src, size_t size) { + if (src == dst && IsAlignedTo(dst)) { + // Hit the compiler over the head with the fact that the source + // and the destination are the same and is properly aligned. + DoSwapBytes(dst, dst, size); + } else { + DoSwapBytes(dst, src, size); + } +} + +void SwapBytes16(char* dst, const char* src, size_t size) { + return SwapBytes(dst, src, size); +} + +void SwapBytes32(char* dst, const char* src, size_t size) { + return SwapBytes(dst, src, size); +} + +void SwapBytes64(char* dst, const char* src, size_t size) { + return SwapBytes(dst, src, size); } char ToLower(char c) { diff --git a/src/util.h b/src/util.h index e57651c3a6981d..42df1d4dd981b8 100644 --- a/src/util.h +++ b/src/util.h @@ -238,7 +238,18 @@ inline void ClearWrap(v8::Local object); template inline TypeName* Unwrap(v8::Local object); -inline void SwapBytes(uint16_t* dst, const uint16_t* src, size_t buflen); +template +inline bool IsAlignedTo(const U* p); + +inline uint16_t ByteSwap(uint16_t v); +inline uint32_t ByteSwap(uint32_t v); +inline uint64_t ByteSwap(uint64_t v); + +// |src| and |dst| are allowed to overlap. |size| is in bytes and must be +// a multiple of the word size. +inline void SwapBytes16(char* dst, const char* src, size_t size); +inline void SwapBytes32(char* dst, const char* src, size_t size); +inline void SwapBytes64(char* dst, const char* src, size_t size); // tolower() is locale-sensitive. Use ToLower() instead. inline char ToLower(char c);