@@ -86,9 +86,13 @@ constexpr bool isSurrogatePair(uint16_t lead, uint16_t trail) {
8686 return (lead & 0xfc00 ) == 0xd800 && (trail & 0xfc00 ) == 0xdc00 ;
8787}
8888
89- constexpr size_t simpleUtfEncodingLength (uint16_t c) {
89+ constexpr size_t simpleUtfEncodingLength (uint8_t c) {
90+ return c < 0x80 ? 1 : 2 ;
91+ }
92+
93+ constexpr size_t simpleUtfEncodingLength (char16_t c) {
9094 if (c < 0x80 ) return 1 ;
91- if (c < 0x400 ) return 2 ;
95+ if (c < 0x800 ) return 2 ;
9296 return 3 ;
9397}
9498
@@ -114,6 +118,9 @@ constexpr size_t simpleUtfEncodingLength(uint16_t c) {
114118// multi-byte heavy content.
115119template <typename Char>
116120size_t findBestFit (const Char* data, size_t length, size_t bufferSize) {
121+ // TODO: Replace prefix sizing with a bounded simdutf conversion that returns
122+ // both input code units consumed and output bytes written. The current safe
123+ // conversion APIs only return the latter, while encodeInto() requires both.
117124 size_t pos = 0 ;
118125 size_t utf8Accumulated = 0 ;
119126 constexpr size_t CHUNK = 257 ;
@@ -141,14 +148,15 @@ size_t findBestFit(const Char* data, size_t length, size_t bufferSize) {
141148
142149 size_t chunkUtf8Len;
143150 if constexpr (UTF16 ) {
144- // TODO(anonrig): Use utf8_length_from_utf16_with_replacement when
145- // available For now, validate and use utf8_length_from_utf16
146151 size_t newPos = pos + chunkSize;
147152 if (newPos < length && isSurrogatePair (data[newPos - 1 ], data[newPos]))
148153 chunkSize--;
149- chunkUtf8Len = simdutf::utf8_length_from_utf16 (data + pos, chunkSize);
154+ chunkUtf8Len = simdutf::utf8_length_from_utf16_with_replacement (
155+ data + pos, chunkSize)
156+ .count ;
150157 } else {
151- chunkUtf8Len = simdutf::utf8_length_from_latin1 (data + pos, chunkSize);
158+ chunkUtf8Len = simdutf::utf8_length_from_latin1 (
159+ reinterpret_cast <const char *>(data + pos), chunkSize);
152160 }
153161
154162 if (utf8Accumulated + chunkUtf8Len > bufferSize) {
@@ -162,19 +170,21 @@ size_t findBestFit(const Char* data, size_t length, size_t bufferSize) {
162170 }
163171
164172 while (pos < length && utf8Accumulated < bufferSize) {
165- size_t extra = simpleUtfEncodingLength (data[pos]);
166- if (utf8Accumulated + extra > bufferSize) break ;
167- pos++;
168- utf8Accumulated += extra;
169- }
170-
171- if (UTF16 && pos != 0 && pos != length &&
172- isSurrogatePair (data[pos - 1 ], data[pos])) {
173- if (utf8Accumulated < bufferSize) {
174- pos++;
173+ size_t codeUnits = 1 ;
174+ size_t extra;
175+ if constexpr (UTF16 ) {
176+ if (pos + 1 < length && isSurrogatePair (data[pos], data[pos + 1 ])) {
177+ codeUnits = 2 ;
178+ extra = 4 ;
179+ } else {
180+ extra = simpleUtfEncodingLength (data[pos]);
181+ }
175182 } else {
176- pos-- ;
183+ extra = simpleUtfEncodingLength (data[ pos]) ;
177184 }
185+ if (utf8Accumulated + extra > bufferSize) break ;
186+ pos += codeUnits;
187+ utf8Accumulated += extra;
178188 }
179189 return pos;
180190}
@@ -239,9 +249,9 @@ void BindingData::EncodeInto(const FunctionCallbackInfo<Value>& args) {
239249 std::min (static_cast <size_t >(view.length ()), dest_length);
240250
241251 if (view.is_one_byte ()) {
242- auto data = reinterpret_cast < const char *>( view.data8 () );
243- simdutf::result result =
244- simdutf::validate_ascii_with_errors (data, length_that_fits);
252+ const uint8_t * data = view.data8 ();
253+ simdutf::result result = simdutf::validate_ascii_with_errors (
254+ reinterpret_cast < const char *> (data) , length_that_fits);
245255 written = read = result.count ;
246256 memcpy (write_result, data, read);
247257 write_result += read;
@@ -250,8 +260,9 @@ void BindingData::EncodeInto(const FunctionCallbackInfo<Value>& args) {
250260 dest_length -= read;
251261 if (length_that_fits != 0 && dest_length != 0 ) {
252262 if (size_t rest = findBestFit (data, length_that_fits, dest_length)) {
253- DCHECK_LE (simdutf::utf8_length_from_latin1 (data, rest), dest_length);
254- written += simdutf::convert_latin1_to_utf8 (data, rest, write_result);
263+ const char * latin1 = reinterpret_cast <const char *>(data);
264+ DCHECK_LE (simdutf::utf8_length_from_latin1 (latin1, rest), dest_length);
265+ written += simdutf::convert_latin1_to_utf8 (latin1, rest, write_result);
255266 read += rest;
256267 }
257268 }
@@ -266,34 +277,20 @@ void BindingData::EncodeInto(const FunctionCallbackInfo<Value>& args) {
266277 length_that_fits--;
267278 }
268279
269- // Check if input has unpaired surrogates - if so, convert to well-formed
270- // first
271- simdutf::result validation_result =
272- simdutf::validate_utf16_with_errors (data, length_that_fits);
273-
274- if (validation_result.error == simdutf::SUCCESS ) {
275- // Valid UTF-16 - use the fast path
276- read = findBestFit (data, length_that_fits, dest_length);
277- if (read != 0 ) {
278- DCHECK_LE (simdutf::utf8_length_from_utf16 (data, read), dest_length);
279- written = simdutf::convert_utf16_to_utf8 (data, read, write_result);
280- }
281- } else {
282- // Invalid UTF-16 with unpaired surrogates - convert to well-formed first
283- // TODO(anonrig): Use utf8_length_from_utf16_with_replacement when
284- // available
285- MaybeStackBuffer<char16_t , MAX_SIZE_FOR_STACK_ALLOC > conversion_buffer (
286- length_that_fits);
287- simdutf::to_well_formed_utf16 (
288- data, length_that_fits, conversion_buffer.out ());
289-
290- // Now use findBestFit with the well-formed data
291- read =
292- findBestFit (conversion_buffer.out (), length_that_fits, dest_length);
293- if (read != 0 ) {
294- DCHECK_LE (
295- simdutf::utf8_length_from_utf16 (conversion_buffer.out (), read),
296- dest_length);
280+ read = findBestFit (data, length_that_fits, dest_length);
281+ if (read != 0 ) {
282+ DCHECK_LE (
283+ simdutf::utf8_length_from_utf16_with_replacement (data, read).count ,
284+ dest_length);
285+ simdutf::result conversion_result =
286+ simdutf::convert_utf16_to_utf8_with_errors (data, read, write_result);
287+ if (conversion_result.error == simdutf::SUCCESS ) {
288+ written = conversion_result.count ;
289+ } else {
290+ // Unpaired surrogates are encoded as U+FFFD.
291+ MaybeStackBuffer<char16_t , MAX_SIZE_FOR_STACK_ALLOC > conversion_buffer (
292+ read);
293+ simdutf::to_well_formed_utf16 (data, read, conversion_buffer.out ());
297294 written = simdutf::convert_utf16_to_utf8 (
298295 conversion_buffer.out (), read, write_result);
299296 }
0 commit comments