|
22 | 22 | #include "node_buffer.h" |
23 | 23 | #include "node.h" |
24 | 24 | #include "node_blob.h" |
| 25 | +#include "node_debug.h" |
25 | 26 | #include "node_errors.h" |
26 | 27 | #include "node_external_reference.h" |
27 | 28 | #include "node_i18n.h" |
@@ -1442,6 +1443,79 @@ void CopyArrayBuffer(const FunctionCallbackInfo<Value>& args) { |
1442 | 1443 | memcpy(dest, src, bytes_to_copy); |
1443 | 1444 | } |
1444 | 1445 |
|
| 1446 | +size_t convert_latin1_to_utf8_s(const char* src, |
| 1447 | + size_t src_len, |
| 1448 | + char* dst, |
| 1449 | + size_t dst_len) noexcept { |
| 1450 | + size_t src_pos = 0; |
| 1451 | + size_t dst_pos = 0; |
| 1452 | + |
| 1453 | + const auto safe_len = std::min(src_len, dst_len >> 1); |
| 1454 | + if (safe_len > 16) { |
| 1455 | + // convert_latin1_to_utf8 will never write more than input length * 2. |
| 1456 | + dst_pos += simdutf::convert_latin1_to_utf8(src, safe_len, dst); |
| 1457 | + src_pos += safe_len; |
| 1458 | + } |
| 1459 | + |
| 1460 | + // Based on: |
| 1461 | + // https://github.com/simdutf/simdutf/blob/master/src/scalar/latin1_to_utf8/latin1_to_utf8.h |
| 1462 | + // with an upper limit on the number of bytes to write. |
| 1463 | + |
| 1464 | + const auto src_ptr = reinterpret_cast<const uint8_t*>(src); |
| 1465 | + const auto dst_ptr = reinterpret_cast<uint8_t*>(dst); |
| 1466 | + |
| 1467 | + size_t skip_pos = src_pos; |
| 1468 | + while (src_pos < src_len && dst_pos < dst_len) { |
| 1469 | + if (skip_pos <= src_pos && src_pos + 16 <= src_len && |
| 1470 | + dst_pos + 16 <= dst_len) { |
| 1471 | + uint64_t v1; |
| 1472 | + memcpy(&v1, src_ptr + src_pos + 0, 8); |
| 1473 | + uint64_t v2; |
| 1474 | + memcpy(&v2, src_ptr + src_pos + 8, 8); |
| 1475 | + if (((v1 | v2) & UINT64_C(0x8080808080808080)) == 0) { |
| 1476 | + memcpy(dst_ptr + dst_pos, src_ptr + src_pos, 16); |
| 1477 | + dst_pos += 16; |
| 1478 | + src_pos += 16; |
| 1479 | + } else { |
| 1480 | + skip_pos = src_pos + 16; |
| 1481 | + } |
| 1482 | + } else { |
| 1483 | + const auto byte = src_ptr[src_pos++]; |
| 1484 | + if ((byte & 0x80) == 0) { |
| 1485 | + dst_ptr[dst_pos++] = byte; |
| 1486 | + } else if (dst_pos + 2 <= dst_len) { |
| 1487 | + dst_ptr[dst_pos++] = (byte >> 6) | 0b11000000; |
| 1488 | + dst_ptr[dst_pos++] = (byte & 0b111111) | 0b10000000; |
| 1489 | + } else { |
| 1490 | + break; |
| 1491 | + } |
| 1492 | + } |
| 1493 | + } |
| 1494 | + |
| 1495 | + return dst_pos; |
| 1496 | +} |
| 1497 | + |
| 1498 | +template <encoding encoding> |
| 1499 | +uint32_t WriteOneByteString(const char* src, |
| 1500 | + uint32_t src_len, |
| 1501 | + char* dst, |
| 1502 | + uint32_t dst_len) { |
| 1503 | + if (dst_len == 0) { |
| 1504 | + return 0; |
| 1505 | + } |
| 1506 | + |
| 1507 | + if (encoding == UTF8) { |
| 1508 | + return convert_latin1_to_utf8_s(src, src_len, dst, dst_len); |
| 1509 | + } else if (encoding == LATIN1 || encoding == ASCII) { |
| 1510 | + const auto size = std::min(src_len, dst_len); |
| 1511 | + memcpy(dst, src, size); |
| 1512 | + return size; |
| 1513 | + } else { |
| 1514 | + // TODO(ronag): Add support for more encoding. |
| 1515 | + UNREACHABLE(); |
| 1516 | + } |
| 1517 | +} |
| 1518 | + |
1445 | 1519 | template <encoding encoding> |
1446 | 1520 | void SlowWriteString(const FunctionCallbackInfo<Value>& args) { |
1447 | 1521 | Environment* env = Environment::GetCurrent(args); |
@@ -1464,11 +1538,22 @@ void SlowWriteString(const FunctionCallbackInfo<Value>& args) { |
1464 | 1538 |
|
1465 | 1539 | if (max_length == 0) return args.GetReturnValue().Set(0); |
1466 | 1540 |
|
1467 | | - uint32_t written = StringBytes::Write( |
1468 | | - env->isolate(), ts_obj_data + offset, max_length, str, encoding); |
| 1541 | + uint32_t written = 0; |
| 1542 | + |
| 1543 | + if ((encoding == UTF8 || encoding == LATIN1 || encoding == ASCII) && |
| 1544 | + str->IsExternalOneByte()) { |
| 1545 | + const auto src = str->GetExternalOneByteStringResource(); |
| 1546 | + written = WriteOneByteString<encoding>( |
| 1547 | + src->data(), src->length(), ts_obj_data + offset, max_length); |
| 1548 | + } else { |
| 1549 | + written = StringBytes::Write( |
| 1550 | + env->isolate(), ts_obj_data + offset, max_length, str, encoding); |
| 1551 | + } |
| 1552 | + |
1469 | 1553 | args.GetReturnValue().Set(written); |
1470 | 1554 | } |
1471 | 1555 |
|
| 1556 | +template <encoding encoding> |
1472 | 1557 | uint32_t FastWriteString(Local<Value> receiver, |
1473 | 1558 | const v8::FastApiTypedArray<uint8_t>& dst, |
1474 | 1559 | const v8::FastOneByteString& src, |
@@ -1478,16 +1563,21 @@ uint32_t FastWriteString(Local<Value> receiver, |
1478 | 1563 | CHECK(dst.getStorageIfAligned(&dst_data)); |
1479 | 1564 | CHECK(offset <= dst.length()); |
1480 | 1565 | CHECK(dst.length() - offset <= std::numeric_limits<uint32_t>::max()); |
| 1566 | + TRACK_V8_FAST_API_CALL("buffer.writeString"); |
1481 | 1567 |
|
1482 | | - const auto size = std::min( |
1483 | | - {static_cast<uint32_t>(dst.length() - offset), max_length, src.length}); |
1484 | | - |
1485 | | - memcpy(dst_data + offset, src.data, size); |
1486 | | - |
1487 | | - return size; |
| 1568 | + return WriteOneByteString<encoding>( |
| 1569 | + src.data, |
| 1570 | + src.length, |
| 1571 | + reinterpret_cast<char*>(dst_data + offset), |
| 1572 | + std::min<uint32_t>(dst.length() - offset, max_length)); |
1488 | 1573 | } |
1489 | 1574 |
|
1490 | | -static v8::CFunction fast_write_string(v8::CFunction::Make(FastWriteString)); |
| 1575 | +static v8::CFunction fast_write_string_ascii( |
| 1576 | + v8::CFunction::Make(FastWriteString<ASCII>)); |
| 1577 | +static v8::CFunction fast_write_string_latin1( |
| 1578 | + v8::CFunction::Make(FastWriteString<LATIN1>)); |
| 1579 | +static v8::CFunction fast_write_string_utf8( |
| 1580 | + v8::CFunction::Make(FastWriteString<UTF8>)); |
1491 | 1581 |
|
1492 | 1582 | void Initialize(Local<Object> target, |
1493 | 1583 | Local<Value> unused, |
@@ -1554,9 +1644,21 @@ void Initialize(Local<Object> target, |
1554 | 1644 | SetMethod(context, target, "hexWrite", StringWrite<HEX>); |
1555 | 1645 | SetMethod(context, target, "ucs2Write", StringWrite<UCS2>); |
1556 | 1646 |
|
1557 | | - SetMethod(context, target, "asciiWriteStatic", SlowWriteString<ASCII>); |
1558 | | - SetMethod(context, target, "latin1WriteStatic", SlowWriteString<LATIN1>); |
1559 | | - SetMethod(context, target, "utf8WriteStatic", SlowWriteString<UTF8>); |
| 1647 | + SetFastMethod(context, |
| 1648 | + target, |
| 1649 | + "asciiWriteStatic", |
| 1650 | + SlowWriteString<ASCII>, |
| 1651 | + &fast_write_string_ascii); |
| 1652 | + SetFastMethod(context, |
| 1653 | + target, |
| 1654 | + "latin1WriteStatic", |
| 1655 | + SlowWriteString<LATIN1>, |
| 1656 | + &fast_write_string_latin1); |
| 1657 | + SetFastMethod(context, |
| 1658 | + target, |
| 1659 | + "utf8WriteStatic", |
| 1660 | + SlowWriteString<UTF8>, |
| 1661 | + &fast_write_string_utf8); |
1560 | 1662 |
|
1561 | 1663 | SetMethod(context, target, "getZeroFillToggle", GetZeroFillToggle); |
1562 | 1664 | } |
@@ -1601,8 +1703,12 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) { |
1601 | 1703 | registry->Register(SlowWriteString<ASCII>); |
1602 | 1704 | registry->Register(SlowWriteString<LATIN1>); |
1603 | 1705 | registry->Register(SlowWriteString<UTF8>); |
1604 | | - registry->Register(fast_write_string.GetTypeInfo()); |
1605 | | - registry->Register(FastWriteString); |
| 1706 | + registry->Register(FastWriteString<ASCII>); |
| 1707 | + registry->Register(fast_write_string_ascii.GetTypeInfo()); |
| 1708 | + registry->Register(FastWriteString<LATIN1>); |
| 1709 | + registry->Register(fast_write_string_latin1.GetTypeInfo()); |
| 1710 | + registry->Register(FastWriteString<UTF8>); |
| 1711 | + registry->Register(fast_write_string_utf8.GetTypeInfo()); |
1606 | 1712 | registry->Register(StringWrite<ASCII>); |
1607 | 1713 | registry->Register(StringWrite<BASE64>); |
1608 | 1714 | registry->Register(StringWrite<BASE64URL>); |
|