diff --git a/deps/nbytes/nbytes.cpp b/deps/nbytes/nbytes.cpp index d5ad70b08d9e73..565e31646395db 100644 --- a/deps/nbytes/nbytes.cpp +++ b/deps/nbytes/nbytes.cpp @@ -146,6 +146,106 @@ const int8_t unbase64_table[256] = -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; +// ============================================================================ +// Hex + +const int8_t unhex_table[256] = + { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + +size_t HexEncode( + const char* src, + size_t slen, + char* dst, + size_t dlen) { + // We know how much we'll write, just make sure that there's space. + NBYTES_ASSERT_TRUE( + dlen >= MultiplyWithOverflowCheck(slen, 2u) && + "not enough space provided for hex encode"); + + dlen = slen * 2; + for (size_t i = 0, k = 0; k < dlen; i += 1, k += 2) { + static const char hex[] = "0123456789abcdef"; + uint8_t val = static_cast(src[i]); + dst[k + 0] = hex[val >> 4]; + dst[k + 1] = hex[val & 15]; + } + + return dlen; +} + +std::string HexEncode(const char* src, size_t slen) { + size_t dlen = slen * 2; + std::string dst(dlen, '\0'); + HexEncode(src, slen, dst.data(), dlen); + return dst; +} + +// ============================================================================ + +void ForceAsciiSlow(const char* src, char* dst, size_t len) { + for (size_t i = 0; i < len; ++i) { + dst[i] = src[i] & 0x7f; + } +} + +void ForceAscii(const char* src, char* dst, size_t len) { + if (len < 16) { + ForceAsciiSlow(src, dst, len); + return; + } + + const unsigned bytes_per_word = sizeof(uintptr_t); + const unsigned align_mask = bytes_per_word - 1; + const unsigned src_unalign = reinterpret_cast(src) & align_mask; + const unsigned dst_unalign = reinterpret_cast(dst) & align_mask; + + if (src_unalign > 0) { + if (src_unalign == dst_unalign) { + const unsigned unalign = bytes_per_word - src_unalign; + ForceAsciiSlow(src, dst, unalign); + src += unalign; + dst += unalign; + len -= src_unalign; + } else { + ForceAsciiSlow(src, dst, len); + return; + } + } + +#if defined(_WIN64) || defined(_LP64) + const uintptr_t mask = ~0x8080808080808080ll; +#else + const uintptr_t mask = ~0x80808080l; +#endif + const uintptr_t* srcw = reinterpret_cast(src); + uintptr_t* dstw = reinterpret_cast(dst); + + for (size_t i = 0, n = len / bytes_per_word; i < n; ++i) { + dstw[i] = srcw[i] & mask; + } + + const unsigned remainder = len & align_mask; + if (remainder > 0) { + const size_t offset = len - remainder; + ForceAsciiSlow(src + offset, dst + offset, remainder); + } +} } // namespace nbytes diff --git a/deps/nbytes/nbytes.h b/deps/nbytes/nbytes.h index 364549324edadd..acb0f1bedf1549 100644 --- a/deps/nbytes/nbytes.h +++ b/deps/nbytes/nbytes.h @@ -1,12 +1,44 @@ #pragma once -#include -#include -#include +#include #include +#include +#include namespace nbytes { +#if NBYTES_DEVELOPMENT_CHECKS +#define NBYTES_STR(x) #x +#define NBYTES_REQUIRE(EXPR) \ + { \ + if (!(EXPR) { abort(); }) } + +#define NBYTES_FAIL(MESSAGE) \ + do { \ + std::cerr << "FAIL: " << (MESSAGE) << std::endl; \ + abort(); \ + } while (0); +#define NBYTES_ASSERT_EQUAL(LHS, RHS, MESSAGE) \ + do { \ + if (LHS != RHS) { \ + std::cerr << "Mismatch: '" << LHS << "' - '" << RHS << "'" << std::endl; \ + NBYTES_FAIL(MESSAGE); \ + } \ + } while (0); +#define NBYTES_ASSERT_TRUE(COND) \ + do { \ + if (!(COND)) { \ + std::cerr << "Assert at line " << __LINE__ << " of file " << __FILE__ \ + << std::endl; \ + NBYTES_FAIL(NBYTES_STR(COND)); \ + } \ + } while (0); +#else +#define NBYTES_FAIL(MESSAGE) +#define NBYTES_ASSERT_EQUAL(LHS, RHS, MESSAGE) +#define NBYTES_ASSERT_TRUE(COND) +#endif + // The nbytes (short for "node bytes") is a set of utility helpers for // working with bytes that are extracted from Node.js' internals. The // motivation for extracting these into a separate library is to make it @@ -26,6 +58,19 @@ constexpr T* AlignUp(T* ptr, U alignment) { RoundUp(reinterpret_cast(ptr), alignment)); } +template +inline T MultiplyWithOverflowCheck(T a, T b) { + auto ret = a * b; + if (a != 0) { + NBYTES_ASSERT_TRUE(b == ret / a); + } + + return ret; +} + +void ForceAsciiSlow(const char* src, char* dst, size_t len); +void ForceAscii(const char* src, char* dst, size_t len); + // ============================================================================ // Byte Swapping @@ -160,4 +205,34 @@ size_t Base64Decode(char* const dst, const size_t dstlen, #pragma warning(pop) #endif +// ============================================================================ +// Hex (legacy) + +extern const int8_t unhex_table[256]; + +template +static size_t HexDecode(char* buf, + size_t len, + const TypeName* src, + const size_t srcLen) { + size_t i; + for (i = 0; i < len && i * 2 + 1 < srcLen; ++i) { + unsigned a = unhex_table[static_cast(src[i * 2 + 0])]; + unsigned b = unhex_table[static_cast(src[i * 2 + 1])]; + if (!~a || !~b) + return i; + buf[i] = (a << 4) | b; + } + + return i; +} + +size_t HexEncode( + const char* src, + size_t slen, + char* dst, + size_t dlen); + +std::string HexEncode(const char* src, size_t slen); + } // namespace nbytes diff --git a/src/crypto/crypto_common.cc b/src/crypto/crypto_common.cc index 962018583360a1..11e8836fbc450e 100644 --- a/src/crypto/crypto_common.cc +++ b/src/crypto/crypto_common.cc @@ -8,6 +8,7 @@ #include "node_internals.h" #include "string_bytes.h" #include "v8.h" +#include "nbytes.h" #include #include @@ -90,10 +91,10 @@ void LogSecret( } std::string line = name; - line += " " + StringBytes::hex_encode(reinterpret_cast(crandom), - kTlsClientRandomSize); - line += " " + StringBytes::hex_encode( - reinterpret_cast(secret), secretlen); + line += " " + nbytes::HexEncode(reinterpret_cast(crandom), + kTlsClientRandomSize); + line += " " + nbytes::HexEncode(reinterpret_cast(secret), + secretlen); keylog_cb(ssl.get(), line.c_str()); } diff --git a/src/quic/cid.cc b/src/quic/cid.cc index 7c30d0d542aeaf..a017502deeb2ac 100644 --- a/src/quic/cid.cc +++ b/src/quic/cid.cc @@ -5,6 +5,7 @@ #include #include #include "quic/defs.h" +#include "nbytes.h" namespace node { namespace quic { @@ -72,10 +73,10 @@ size_t CID::length() const { std::string CID::ToString() const { char dest[kMaxLength * 2]; size_t written = - StringBytes::hex_encode(reinterpret_cast(ptr_->data), - ptr_->datalen, - dest, - arraysize(dest)); + nbytes::HexEncode(reinterpret_cast(ptr_->data), + ptr_->datalen, + dest, + arraysize(dest)); return std::string(dest, written); } diff --git a/src/quic/tokens.cc b/src/quic/tokens.cc index 9ffdab2575cb42..045c5f9a050d05 100644 --- a/src/quic/tokens.cc +++ b/src/quic/tokens.cc @@ -7,6 +7,7 @@ #include #include #include +#include "nbytes.h" namespace node { namespace quic { @@ -49,7 +50,7 @@ TokenSecret::operator const char*() const { std::string TokenSecret::ToString() const { char dest[QUIC_TOKENSECRET_LEN * 2]; - size_t written = StringBytes::hex_encode( + size_t written = nbytes::HexEncode( *this, QUIC_TOKENSECRET_LEN, dest, arraysize(dest)); DCHECK_EQ(written, arraysize(dest)); return std::string(dest, written); @@ -117,7 +118,7 @@ std::string StatelessResetToken::ToString() const { if (ptr_ == nullptr) return std::string(); char dest[kStatelessTokenLen * 2]; size_t written = - StringBytes::hex_encode(*this, kStatelessTokenLen, dest, arraysize(dest)); + nbytes::HexEncode(*this, kStatelessTokenLen, dest, arraysize(dest)); DCHECK_EQ(written, arraysize(dest)); return std::string(dest, written); } @@ -230,7 +231,7 @@ std::string RetryToken::ToString() const { if (ptr_.base == nullptr) return std::string(); MaybeStackBuffer dest(ptr_.len * 2); size_t written = - StringBytes::hex_encode(*this, ptr_.len, dest.out(), dest.length()); + nbytes::HexEncode(*this, ptr_.len, dest.out(), dest.length()); DCHECK_EQ(written, dest.length()); return std::string(dest.out(), written); } @@ -289,7 +290,7 @@ std::string RegularToken::ToString() const { if (ptr_.base == nullptr) return std::string(); MaybeStackBuffer dest(ptr_.len * 2); size_t written = - StringBytes::hex_encode(*this, ptr_.len, dest.out(), dest.length()); + nbytes::HexEncode(*this, ptr_.len, dest.out(), dest.length()); DCHECK_EQ(written, dest.length()); return std::string(dest.out(), written); } diff --git a/src/string_bytes.cc b/src/string_bytes.cc index d1355de1093692..dda14fe49e7634 100644 --- a/src/string_bytes.cc +++ b/src/string_bytes.cc @@ -200,46 +200,6 @@ MaybeLocal ExternTwoByteString::NewSimpleFromCopy(Isolate* isolate, } // anonymous namespace -static const int8_t unhex_table[256] = - { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, - -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 - }; - -static inline unsigned unhex(uint8_t x) { - return unhex_table[x]; -} - -template -static size_t hex_decode(char* buf, - size_t len, - const TypeName* src, - const size_t srcLen) { - size_t i; - for (i = 0; i < len && i * 2 + 1 < srcLen; ++i) { - unsigned a = unhex(static_cast(src[i * 2 + 0])); - unsigned b = unhex(static_cast(src[i * 2 + 1])); - if (!~a || !~b) - return i; - buf[i] = (a << 4) | b; - } - - return i; -} - size_t StringBytes::WriteUCS2( Isolate* isolate, char* buf, size_t buflen, Local str, int flags) { uint16_t* const dst = reinterpret_cast(buf); @@ -434,10 +394,10 @@ size_t StringBytes::Write(Isolate* isolate, case HEX: if (str->IsExternalOneByte()) { auto ext = str->GetExternalOneByteStringResource(); - nbytes = hex_decode(buf, buflen, ext->data(), ext->length()); + nbytes = nbytes::HexDecode(buf, buflen, ext->data(), ext->length()); } else { String::Value value(isolate, str); - nbytes = hex_decode(buf, buflen, *value, value.length()); + nbytes = nbytes::HexDecode(buf, buflen, *value, value.length()); } break; @@ -547,85 +507,6 @@ Maybe StringBytes::Size(Isolate* isolate, UNREACHABLE(); } -static void force_ascii_slow(const char* src, char* dst, size_t len) { - for (size_t i = 0; i < len; ++i) { - dst[i] = src[i] & 0x7f; - } -} - - -static void force_ascii(const char* src, char* dst, size_t len) { - if (len < 16) { - force_ascii_slow(src, dst, len); - return; - } - - const unsigned bytes_per_word = sizeof(uintptr_t); - const unsigned align_mask = bytes_per_word - 1; - const unsigned src_unalign = reinterpret_cast(src) & align_mask; - const unsigned dst_unalign = reinterpret_cast(dst) & align_mask; - - if (src_unalign > 0) { - if (src_unalign == dst_unalign) { - const unsigned unalign = bytes_per_word - src_unalign; - force_ascii_slow(src, dst, unalign); - src += unalign; - dst += unalign; - len -= src_unalign; - } else { - force_ascii_slow(src, dst, len); - return; - } - } - -#if defined(_WIN64) || defined(_LP64) - const uintptr_t mask = ~0x8080808080808080ll; -#else - const uintptr_t mask = ~0x80808080l; -#endif - - const uintptr_t* srcw = reinterpret_cast(src); - uintptr_t* dstw = reinterpret_cast(dst); - - for (size_t i = 0, n = len / bytes_per_word; i < n; ++i) { - dstw[i] = srcw[i] & mask; - } - - const unsigned remainder = len & align_mask; - if (remainder > 0) { - const size_t offset = len - remainder; - force_ascii_slow(src + offset, dst + offset, remainder); - } -} - - -size_t StringBytes::hex_encode( - const char* src, - size_t slen, - char* dst, - size_t dlen) { - // We know how much we'll write, just make sure that there's space. - CHECK(dlen >= MultiplyWithOverflowCheck(slen, 2u) && - "not enough space provided for hex encode"); - - dlen = slen * 2; - for (size_t i = 0, k = 0; k < dlen; i += 1, k += 2) { - static const char hex[] = "0123456789abcdef"; - uint8_t val = static_cast(src[i]); - dst[k + 0] = hex[val >> 4]; - dst[k + 1] = hex[val & 15]; - } - - return dlen; -} - -std::string StringBytes::hex_encode(const char* src, size_t slen) { - size_t dlen = slen * 2; - std::string dst(dlen, '\0'); - hex_encode(src, slen, dst.data(), dlen); - return dst; -} - #define CHECK_BUFLEN_IN_RANGE(len) \ do { \ if ((len) > Buffer::kMaxLength) { \ @@ -667,7 +548,7 @@ MaybeLocal StringBytes::Encode(Isolate* isolate, *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate); return MaybeLocal(); } - force_ascii(buf, out, buflen); + nbytes::ForceAscii(buf, out, buflen); return ExternOneByteString::New(isolate, out, buflen, error); } else { return ExternOneByteString::NewFromCopy(isolate, buf, buflen, error); @@ -726,7 +607,7 @@ MaybeLocal StringBytes::Encode(Isolate* isolate, *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate); return MaybeLocal(); } - size_t written = hex_encode(buf, buflen, dst, dlen); + size_t written = nbytes::HexEncode(buf, buflen, dst, dlen); CHECK_EQ(written, dlen); return ExternOneByteString::New(isolate, dst, dlen, error); diff --git a/src/string_bytes.h b/src/string_bytes.h index ad1f15b05704c8..fde5070ffb66a7 100644 --- a/src/string_bytes.h +++ b/src/string_bytes.h @@ -98,13 +98,6 @@ class StringBytes { enum encoding encoding, v8::Local* error); - static size_t hex_encode(const char* src, - size_t slen, - char* dst, - size_t dlen); - - static std::string hex_encode(const char* src, size_t slen); - private: static size_t WriteUCS2(v8::Isolate* isolate, char* buf,