41
41
#include < charconv>
42
42
#include < wtf/text/EscapedFormsForJSON.h>
43
43
#include < wtf/text/StringBuilder.h>
44
+ #include < wtf/text/StringCommon.h>
44
45
45
46
// Turn this on to log information about fastStringify usage, with a focus on why it failed.
46
47
#define FAST_STRINGIFY_LOG_USAGE 0
@@ -1059,6 +1060,102 @@ void FastStringifier<CharType>::append(JSValue value)
1059
1060
recordFailure (" String::tryGetValue" _s);
1060
1061
return ;
1061
1062
}
1063
+
1064
+ auto charactersCopySameType = [&](auto span, auto * cursor) ALWAYS_INLINE_LAMBDA {
1065
+ #if CPU(ARM64) || CPU(X86_64)
1066
+ constexpr size_t stride = 16 / sizeof (CharType);
1067
+ if (span.size () >= stride) {
1068
+ using UnsignedType = std::make_unsigned_t <CharType>;
1069
+ using BulkType = decltype (WTF::loadBulk (static_cast <const UnsignedType*>(nullptr )));
1070
+ constexpr auto quoteMask = WTF::splatBulk (static_cast <UnsignedType>(' "' ));
1071
+ constexpr auto escapeMask = WTF::splatBulk (static_cast <UnsignedType>(' \\ ' ));
1072
+ constexpr auto controlMask = WTF::splatBulk (static_cast <UnsignedType>(' ' ));
1073
+ const auto * ptr = span.data ();
1074
+ const auto * end = ptr + span.size ();
1075
+ auto * cursorEnd = cursor + span.size ();
1076
+ BulkType accumulated { };
1077
+ for (; ptr + (stride - 1 ) < end; ptr += stride, cursor += stride) {
1078
+ auto input = WTF::loadBulk (bitwise_cast<const UnsignedType*>(ptr));
1079
+ WTF::storeBulk (input, bitwise_cast<UnsignedType*>(cursor));
1080
+ auto quotes = WTF::equalBulk (input, quoteMask);
1081
+ auto escapes = WTF::equalBulk (input, escapeMask);
1082
+ auto controls = WTF::lessThanBulk (input, controlMask);
1083
+ accumulated = WTF::mergeBulk (accumulated, WTF::mergeBulk (quotes, WTF::mergeBulk (escapes, controls)));
1084
+ if constexpr (sizeof (CharType) != 1 ) {
1085
+ constexpr auto surrogateMask = WTF::splatBulk (static_cast <UnsignedType>(0xf800 ));
1086
+ constexpr auto surrogateCheckMask = WTF::splatBulk (static_cast <UnsignedType>(0xd800 ));
1087
+ accumulated = WTF::mergeBulk (accumulated, WTF::equalBulk (simde_vandq_u16 (input, surrogateMask), surrogateCheckMask));
1088
+ }
1089
+ }
1090
+ if (ptr < end) {
1091
+ auto input = WTF::loadBulk (bitwise_cast<const UnsignedType*>(end - stride));
1092
+ WTF::storeBulk (input, bitwise_cast<UnsignedType*>(cursorEnd - stride));
1093
+ auto quotes = WTF::equalBulk (input, quoteMask);
1094
+ auto escapes = WTF::equalBulk (input, escapeMask);
1095
+ auto controls = WTF::lessThanBulk (input, controlMask);
1096
+ accumulated = WTF::mergeBulk (accumulated, WTF::mergeBulk (quotes, WTF::mergeBulk (escapes, controls)));
1097
+ if constexpr (sizeof (CharType) != 1 ) {
1098
+ constexpr auto surrogateMask = WTF::splatBulk (static_cast <UnsignedType>(0xf800 ));
1099
+ constexpr auto surrogateCheckMask = WTF::splatBulk (static_cast <UnsignedType>(0xd800 ));
1100
+ accumulated = WTF::mergeBulk (accumulated, WTF::equalBulk (simde_vandq_u16 (input, surrogateMask), surrogateCheckMask));
1101
+ }
1102
+ }
1103
+ return WTF::isNonZeroBulk (accumulated);
1104
+ }
1105
+ #endif
1106
+ for (auto character : span) {
1107
+ if constexpr (sizeof (CharType) != 1 ) {
1108
+ if (UNLIKELY (U16_IS_SURROGATE (character)))
1109
+ return true ;
1110
+ }
1111
+ if (UNLIKELY (character <= 0xff && WTF::escapedFormsForJSON[character]))
1112
+ return true ;
1113
+ *cursor++ = character;
1114
+ }
1115
+ return false ;
1116
+ };
1117
+
1118
+ auto charactersCopyUpconvert = [&](std::span<const LChar> span, UChar* cursor) ALWAYS_INLINE_LAMBDA {
1119
+ #if CPU(ARM64) || CPU(X86_64)
1120
+ constexpr size_t stride = 16 / sizeof (LChar);
1121
+ if (span.size () >= stride) {
1122
+ using UnsignedType = std::make_unsigned_t <LChar>;
1123
+ using BulkType = decltype (WTF::loadBulk (static_cast <const UnsignedType*>(nullptr )));
1124
+ constexpr auto quoteMask = WTF::splatBulk (static_cast <UnsignedType>(' "' ));
1125
+ constexpr auto escapeMask = WTF::splatBulk (static_cast <UnsignedType>(' \\ ' ));
1126
+ constexpr auto controlMask = WTF::splatBulk (static_cast <UnsignedType>(' ' ));
1127
+ constexpr auto zeros = WTF::splatBulk (static_cast <UnsignedType>(0 ));
1128
+ const auto * ptr = span.data ();
1129
+ const auto * end = ptr + span.size ();
1130
+ auto * cursorEnd = cursor + span.size ();
1131
+ BulkType accumulated { };
1132
+ for (; ptr + (stride - 1 ) < end; ptr += stride, cursor += stride) {
1133
+ auto input = WTF::loadBulk (bitwise_cast<const UnsignedType*>(ptr));
1134
+ simde_vst2q_u8 (bitwise_cast<UnsignedType*>(cursor), (simde_uint8x16x2_t { input, zeros }));
1135
+ auto quotes = WTF::equalBulk (input, quoteMask);
1136
+ auto escapes = WTF::equalBulk (input, escapeMask);
1137
+ auto controls = WTF::lessThanBulk (input, controlMask);
1138
+ accumulated = WTF::mergeBulk (accumulated, WTF::mergeBulk (quotes, WTF::mergeBulk (escapes, controls)));
1139
+ }
1140
+ if (ptr < end) {
1141
+ auto input = WTF::loadBulk (bitwise_cast<const UnsignedType*>(end - stride));
1142
+ simde_vst2q_u8 (bitwise_cast<UnsignedType*>(cursorEnd - stride), (simde_uint8x16x2_t { input, zeros }));
1143
+ auto quotes = WTF::equalBulk (input, quoteMask);
1144
+ auto escapes = WTF::equalBulk (input, escapeMask);
1145
+ auto controls = WTF::lessThanBulk (input, controlMask);
1146
+ accumulated = WTF::mergeBulk (accumulated, WTF::mergeBulk (quotes, WTF::mergeBulk (escapes, controls)));
1147
+ }
1148
+ return WTF::isNonZeroBulk (accumulated);
1149
+ }
1150
+ #endif
1151
+ for (auto character : span) {
1152
+ if (UNLIKELY (WTF::escapedFormsForJSON[character]))
1153
+ return true ;
1154
+ *cursor++ = character;
1155
+ }
1156
+ return false ;
1157
+ };
1158
+
1062
1159
if constexpr (sizeof (CharType) == 1 ) {
1063
1160
if (UNLIKELY (!string.is8Bit ())) {
1064
1161
m_retryWith16BitFastStringifier = m_length < (m_capacity / 2 );
@@ -1070,47 +1167,32 @@ void FastStringifier<CharType>::append(JSValue value)
1070
1167
recordBufferFull ();
1071
1168
return ;
1072
1169
}
1073
- auto * cursor = m_buffer + m_length;
1074
- *cursor++ = ' "' ;
1075
- for (auto character : string.span8 ()) {
1076
- if (UNLIKELY (WTF::escapedFormsForJSON[character])) {
1077
- recordFailure (" string character needs escaping" _s);
1078
- return ;
1079
- }
1080
- *cursor++ = character;
1170
+ m_buffer[m_length] = ' "' ;
1171
+ if (UNLIKELY (charactersCopySameType (string.span8 (), m_buffer + m_length + 1 ))) {
1172
+ recordFailure (" string character needs escaping" _s);
1173
+ return ;
1081
1174
}
1082
- *cursor = ' "' ;
1175
+ m_buffer[m_length + 1 + stringLength] = ' "' ;
1083
1176
m_length += 1 + stringLength + 1 ;
1084
1177
} else {
1085
1178
auto stringLength = string.length ();
1086
1179
if (UNLIKELY (!hasRemainingCapacity (1 + stringLength + 1 ))) {
1087
1180
recordBufferFull ();
1088
1181
return ;
1089
1182
}
1090
- auto * cursor = m_buffer + m_length;
1091
- *cursor++ = ' "' ;
1183
+ m_buffer[m_length] = ' "' ;
1092
1184
if (string.is8Bit ()) {
1093
- for (auto character : string.span8 ()) {
1094
- if (UNLIKELY (WTF::escapedFormsForJSON[character])) {
1095
- recordFailure (" string character needs escaping" _s);
1096
- return ;
1097
- }
1098
- *cursor++ = character;
1185
+ if (UNLIKELY (charactersCopyUpconvert (string.span8 (), m_buffer + m_length + 1 ))) {
1186
+ recordFailure (" string character needs escaping" _s);
1187
+ return ;
1099
1188
}
1100
1189
} else {
1101
- for (auto character : string.span16 ()) {
1102
- if (UNLIKELY (U16_IS_SURROGATE (character))) {
1103
- recordFailure (" string character is surrogate" _s);
1104
- return ;
1105
- }
1106
- if (UNLIKELY (character <= 0xff && WTF::escapedFormsForJSON[character])) {
1107
- recordFailure (" string character needs escaping" _s);
1108
- return ;
1109
- }
1110
- *cursor++ = character;
1190
+ if (UNLIKELY (charactersCopySameType (string.span16 (), m_buffer + m_length + 1 ))) {
1191
+ recordFailure (" string character needs escaping or surrogate pair handling" _s);
1192
+ return ;
1111
1193
}
1112
1194
}
1113
- *cursor = ' "' ;
1195
+ m_buffer[m_length + 1 + stringLength] = ' "' ;
1114
1196
m_length += 1 + stringLength + 1 ;
1115
1197
}
1116
1198
return ;
0 commit comments