1 // Copyright Joyent, Inc. and other Node contributors.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 #include "string_bytes.h"
23
24 #include "base64-inl.h"
25 #include "env-inl.h"
26 #include "node_buffer.h"
27 #include "node_errors.h"
28 #include "simdutf.h"
29 #include "util.h"
30
31 #include <climits>
32 #include <cstring> // memcpy
33
34 #include <algorithm>
35
36 // When creating strings >= this length v8's gc spins up and consumes
37 // most of the execution time. For these cases it's more performant to
38 // use external string resources.
39 #define EXTERN_APEX 0xFBEE9
40
41 namespace node {
42
43 using v8::HandleScope;
44 using v8::Isolate;
45 using v8::Just;
46 using v8::Local;
47 using v8::Maybe;
48 using v8::MaybeLocal;
49 using v8::Nothing;
50 using v8::String;
51 using v8::Value;
52
53 namespace {
54
55 template <typename ResourceType, typename TypeName>
56 class ExternString: public ResourceType {
57 public:
58 ~ExternString() override {
59 free(const_cast<TypeName*>(data_));
60 isolate()->AdjustAmountOfExternalAllocatedMemory(-byte_length());
61 }
62
63 const TypeName* data() const override {
64 return data_;
65 }
66
67 size_t length() const override {
68 return length_;
69 }
70
byte_length() const71 int64_t byte_length() const {
72 return length() * sizeof(*data());
73 }
74
NewFromCopy(Isolate* isolate, const TypeName* data, size_t length, Local<Value>* error)75 static MaybeLocal<Value> NewFromCopy(Isolate* isolate,
76 const TypeName* data,
77 size_t length,
78 Local<Value>* error) {
79 if (length == 0)
80 return String::Empty(isolate);
81
82 if (length < EXTERN_APEX)
83 return NewSimpleFromCopy(isolate, data, length, error);
84
85 TypeName* new_data = node::UncheckedMalloc<TypeName>(length);
86 if (new_data == nullptr) {
87 *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate);
88 return MaybeLocal<Value>();
89 }
90 memcpy(new_data, data, length * sizeof(*new_data));
91
92 return ExternString<ResourceType, TypeName>::New(isolate,
93 new_data,
94 length,
95 error);
96 }
97
98 // uses "data" for external resource, and will be free'd on gc
New(Isolate* isolate, TypeName* data, size_t length, Local<Value>* error)99 static MaybeLocal<Value> New(Isolate* isolate,
100 TypeName* data,
101 size_t length,
102 Local<Value>* error) {
103 if (length == 0)
104 return String::Empty(isolate);
105
106 if (length < EXTERN_APEX) {
107 MaybeLocal<Value> str = NewSimpleFromCopy(isolate, data, length, error);
108 free(data);
109 return str;
110 }
111
112 ExternString* h_str = new ExternString<ResourceType, TypeName>(isolate,
113 data,
114 length);
115 MaybeLocal<Value> str = NewExternal(isolate, h_str);
116 isolate->AdjustAmountOfExternalAllocatedMemory(h_str->byte_length());
117
118 if (str.IsEmpty()) {
119 delete h_str;
120 *error = node::ERR_STRING_TOO_LONG(isolate);
121 return MaybeLocal<Value>();
122 }
123
124 return str.ToLocalChecked();
125 }
126
isolate() const127 inline Isolate* isolate() const { return isolate_; }
128
129 private:
ExternString(Isolate* isolate, const TypeName* data, size_t length)130 ExternString(Isolate* isolate, const TypeName* data, size_t length)
131 : isolate_(isolate), data_(data), length_(length) { }
132 static MaybeLocal<Value> NewExternal(Isolate* isolate,
133 ExternString* h_str);
134
135 // This method does not actually create ExternString instances.
136 static MaybeLocal<Value> NewSimpleFromCopy(Isolate* isolate,
137 const TypeName* data,
138 size_t length,
139 Local<Value>* error);
140
141 Isolate* isolate_;
142 const TypeName* data_;
143 size_t length_;
144 };
145
146
147 typedef ExternString<String::ExternalOneByteStringResource,
148 char> ExternOneByteString;
149 typedef ExternString<String::ExternalStringResource,
150 uint16_t> ExternTwoByteString;
151
152
153 template <>
NewExternal( Isolate* isolate, ExternOneByteString* h_str)154 MaybeLocal<Value> ExternOneByteString::NewExternal(
155 Isolate* isolate, ExternOneByteString* h_str) {
156 return String::NewExternalOneByte(isolate, h_str).FromMaybe(Local<Value>());
157 }
158
159
160 template <>
NewExternal( Isolate* isolate, ExternTwoByteString* h_str)161 MaybeLocal<Value> ExternTwoByteString::NewExternal(
162 Isolate* isolate, ExternTwoByteString* h_str) {
163 return String::NewExternalTwoByte(isolate, h_str).FromMaybe(Local<Value>());
164 }
165
166 template <>
NewSimpleFromCopy(Isolate* isolate, const char* data, size_t length, Local<Value>* error)167 MaybeLocal<Value> ExternOneByteString::NewSimpleFromCopy(Isolate* isolate,
168 const char* data,
169 size_t length,
170 Local<Value>* error) {
171 MaybeLocal<String> str =
172 String::NewFromOneByte(isolate,
173 reinterpret_cast<const uint8_t*>(data),
174 v8::NewStringType::kNormal,
175 length);
176 if (str.IsEmpty()) {
177 *error = node::ERR_STRING_TOO_LONG(isolate);
178 return MaybeLocal<Value>();
179 }
180 return str.ToLocalChecked();
181 }
182
183
184 template <>
NewSimpleFromCopy(Isolate* isolate, const uint16_t* data, size_t length, Local<Value>* error)185 MaybeLocal<Value> ExternTwoByteString::NewSimpleFromCopy(Isolate* isolate,
186 const uint16_t* data,
187 size_t length,
188 Local<Value>* error) {
189 MaybeLocal<String> str =
190 String::NewFromTwoByte(isolate,
191 data,
192 v8::NewStringType::kNormal,
193 length);
194 if (str.IsEmpty()) {
195 *error = node::ERR_STRING_TOO_LONG(isolate);
196 return MaybeLocal<Value>();
197 }
198 return str.ToLocalChecked();
199 }
200
201 } // anonymous namespace
202
203 // supports regular and URL-safe base64
204 const int8_t unbase64_table[256] =
205 { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -1, -1, -2, -1, -1,
206 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
207 -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63,
208 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
209 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
210 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,
211 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
212 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
213 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
214 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
215 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
216 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
217 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
218 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
219 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
220 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
221 };
222
223
224 static const int8_t unhex_table[256] =
225 { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
226 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
227 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
228 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
229 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
230 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
231 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
232 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
233 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
234 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
235 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
236 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
237 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
238 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
239 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
240 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
241 };
242
unhex(uint8_t x)243 static inline unsigned unhex(uint8_t x) {
244 return unhex_table[x];
245 }
246
247 template <typename TypeName>
hex_decode(char* buf, size_t len, const TypeName* src, const size_t srcLen)248 static size_t hex_decode(char* buf,
249 size_t len,
250 const TypeName* src,
251 const size_t srcLen) {
252 size_t i;
253 for (i = 0; i < len && i * 2 + 1 < srcLen; ++i) {
254 unsigned a = unhex(static_cast<uint8_t>(src[i * 2 + 0]));
255 unsigned b = unhex(static_cast<uint8_t>(src[i * 2 + 1]));
256 if (!~a || !~b)
257 return i;
258 buf[i] = (a << 4) | b;
259 }
260
261 return i;
262 }
263
WriteUCS2( Isolate* isolate, char* buf, size_t buflen, Local<String> str, int flags)264 size_t StringBytes::WriteUCS2(
265 Isolate* isolate, char* buf, size_t buflen, Local<String> str, int flags) {
266 uint16_t* const dst = reinterpret_cast<uint16_t*>(buf);
267
268 size_t max_chars = buflen / sizeof(*dst);
269 if (max_chars == 0) {
270 return 0;
271 }
272
273 uint16_t* const aligned_dst = AlignUp(dst, sizeof(*dst));
274 size_t nchars;
275 if (aligned_dst == dst) {
276 nchars = str->Write(isolate, dst, 0, max_chars, flags);
277 return nchars * sizeof(*dst);
278 }
279
280 CHECK_EQ(reinterpret_cast<uintptr_t>(aligned_dst) % sizeof(*dst), 0);
281
282 // Write all but the last char
283 max_chars = std::min(max_chars, static_cast<size_t>(str->Length()));
284 if (max_chars == 0) {
285 return 0;
286 }
287 nchars = str->Write(isolate, aligned_dst, 0, max_chars - 1, flags);
288 CHECK_EQ(nchars, max_chars - 1);
289
290 // Shift everything to unaligned-left
291 memmove(dst, aligned_dst, nchars * sizeof(*dst));
292
293 // One more char to be written
294 uint16_t last;
295 CHECK_EQ(str->Write(isolate, &last, nchars, 1, flags), 1);
296 memcpy(buf + nchars * sizeof(*dst), &last, sizeof(last));
297 nchars++;
298
299 return nchars * sizeof(*dst);
300 }
301
Write(Isolate* isolate, char* buf, size_t buflen, Local<Value> val, enum encoding encoding)302 size_t StringBytes::Write(Isolate* isolate,
303 char* buf,
304 size_t buflen,
305 Local<Value> val,
306 enum encoding encoding) {
307 HandleScope scope(isolate);
308 size_t nbytes;
309
310 CHECK(val->IsString() == true);
311 Local<String> str = val.As<String>();
312
313 int flags = String::HINT_MANY_WRITES_EXPECTED |
314 String::NO_NULL_TERMINATION |
315 String::REPLACE_INVALID_UTF8;
316
317 switch (encoding) {
318 case ASCII:
319 case LATIN1:
320 if (str->IsExternalOneByte()) {
321 auto ext = str->GetExternalOneByteStringResource();
322 nbytes = std::min(buflen, ext->length());
323 memcpy(buf, ext->data(), nbytes);
324 } else {
325 uint8_t* const dst = reinterpret_cast<uint8_t*>(buf);
326 nbytes = str->WriteOneByte(isolate, dst, 0, buflen, flags);
327 }
328 break;
329
330 case BUFFER:
331 case UTF8:
332 nbytes = str->WriteUtf8(isolate, buf, buflen, nullptr, flags);
333 break;
334
335 case UCS2: {
336 nbytes = WriteUCS2(isolate, buf, buflen, str, flags);
337
338 // Node's "ucs2" encoding wants LE character data stored in
339 // the Buffer, so we need to reorder on BE platforms. See
340 // https://nodejs.org/api/buffer.html regarding Node's "ucs2"
341 // encoding specification
342 if (IsBigEndian())
343 SwapBytes16(buf, nbytes);
344
345 break;
346 }
347
348 case BASE64URL:
349 // Fall through
350 case BASE64:
351 if (str->IsExternalOneByte()) {
352 auto ext = str->GetExternalOneByteStringResource();
353 nbytes = base64_decode(buf, buflen, ext->data(), ext->length());
354 } else {
355 String::Value value(isolate, str);
356 nbytes = base64_decode(buf, buflen, *value, value.length());
357 }
358 break;
359
360 case HEX:
361 if (str->IsExternalOneByte()) {
362 auto ext = str->GetExternalOneByteStringResource();
363 nbytes = hex_decode(buf, buflen, ext->data(), ext->length());
364 } else {
365 String::Value value(isolate, str);
366 nbytes = hex_decode(buf, buflen, *value, value.length());
367 }
368 break;
369
370 default:
371 UNREACHABLE("unknown encoding");
372 }
373
374 return nbytes;
375 }
376
377 // Quick and dirty size calculation
378 // Will always be at least big enough, but may have some extra
379 // UTF8 can be as much as 3x the size, Base64 can have 1-2 extra bytes
StorageSize(Isolate* isolate, Local<Value> val, enum encoding encoding)380 Maybe<size_t> StringBytes::StorageSize(Isolate* isolate,
381 Local<Value> val,
382 enum encoding encoding) {
383 HandleScope scope(isolate);
384 size_t data_size = 0;
385 bool is_buffer = Buffer::HasInstance(val);
386
387 if (is_buffer && (encoding == BUFFER || encoding == LATIN1)) {
388 return Just(Buffer::Length(val));
389 }
390
391 Local<String> str;
392 if (!val->ToString(isolate->GetCurrentContext()).ToLocal(&str))
393 return Nothing<size_t>();
394
395 switch (encoding) {
396 case ASCII:
397 case LATIN1:
398 data_size = str->Length();
399 break;
400
401 case BUFFER:
402 case UTF8:
403 // A single UCS2 codepoint never takes up more than 3 utf8 bytes.
404 // It is an exercise for the caller to decide when a string is
405 // long enough to justify calling Size() instead of StorageSize()
406 data_size = 3 * str->Length();
407 break;
408
409 case UCS2:
410 data_size = str->Length() * sizeof(uint16_t);
411 break;
412
413 case BASE64URL:
414 // Fall through
415 case BASE64:
416 data_size = base64_decoded_size_fast(str->Length());
417 break;
418
419 case HEX:
420 CHECK(str->Length() % 2 == 0 && "invalid hex string length");
421 data_size = str->Length() / 2;
422 break;
423
424 default:
425 UNREACHABLE("unknown encoding");
426 }
427
428 return Just(data_size);
429 }
430
Size(Isolate* isolate, Local<Value> val, enum encoding encoding)431 Maybe<size_t> StringBytes::Size(Isolate* isolate,
432 Local<Value> val,
433 enum encoding encoding) {
434 HandleScope scope(isolate);
435
436 if (Buffer::HasInstance(val) && (encoding == BUFFER || encoding == LATIN1))
437 return Just(Buffer::Length(val));
438
439 Local<String> str;
440 if (!val->ToString(isolate->GetCurrentContext()).ToLocal(&str))
441 return Nothing<size_t>();
442
443 switch (encoding) {
444 case ASCII:
445 case LATIN1:
446 return Just<size_t>(str->Length());
447
448 case BUFFER:
449 case UTF8:
450 return Just<size_t>(str->Utf8Length(isolate));
451
452 case UCS2:
453 return Just(str->Length() * sizeof(uint16_t));
454
455 case BASE64URL:
456 // Fall through
457 case BASE64: {
458 String::Value value(isolate, str);
459 return Just(base64_decoded_size(*value, value.length()));
460 }
461
462 case HEX:
463 return Just<size_t>(str->Length() / 2);
464 }
465
466 UNREACHABLE();
467 }
468
force_ascii_slow(const char* src, char* dst, size_t len)469 static void force_ascii_slow(const char* src, char* dst, size_t len) {
470 for (size_t i = 0; i < len; ++i) {
471 dst[i] = src[i] & 0x7f;
472 }
473 }
474
475
force_ascii(const char* src, char* dst, size_t len)476 static void force_ascii(const char* src, char* dst, size_t len) {
477 if (len < 16) {
478 force_ascii_slow(src, dst, len);
479 return;
480 }
481
482 const unsigned bytes_per_word = sizeof(uintptr_t);
483 const unsigned align_mask = bytes_per_word - 1;
484 const unsigned src_unalign = reinterpret_cast<uintptr_t>(src) & align_mask;
485 const unsigned dst_unalign = reinterpret_cast<uintptr_t>(dst) & align_mask;
486
487 if (src_unalign > 0) {
488 if (src_unalign == dst_unalign) {
489 const unsigned unalign = bytes_per_word - src_unalign;
490 force_ascii_slow(src, dst, unalign);
491 src += unalign;
492 dst += unalign;
493 len -= src_unalign;
494 } else {
495 force_ascii_slow(src, dst, len);
496 return;
497 }
498 }
499
500 #if defined(_WIN64) || defined(_LP64)
501 const uintptr_t mask = ~0x8080808080808080ll;
502 #else
503 const uintptr_t mask = ~0x80808080l;
504 #endif
505
506 const uintptr_t* srcw = reinterpret_cast<const uintptr_t*>(src);
507 uintptr_t* dstw = reinterpret_cast<uintptr_t*>(dst);
508
509 for (size_t i = 0, n = len / bytes_per_word; i < n; ++i) {
510 dstw[i] = srcw[i] & mask;
511 }
512
513 const unsigned remainder = len & align_mask;
514 if (remainder > 0) {
515 const size_t offset = len - remainder;
516 force_ascii_slow(src + offset, dst + offset, remainder);
517 }
518 }
519
520
hex_encode( const char* src, size_t slen, char* dst, size_t dlen)521 size_t StringBytes::hex_encode(
522 const char* src,
523 size_t slen,
524 char* dst,
525 size_t dlen) {
526 // We know how much we'll write, just make sure that there's space.
527 CHECK(dlen >= slen * 2 &&
528 "not enough space provided for hex encode");
529
530 dlen = slen * 2;
531 for (uint32_t i = 0, k = 0; k < dlen; i += 1, k += 2) {
532 static const char hex[] = "0123456789abcdef";
533 uint8_t val = static_cast<uint8_t>(src[i]);
534 dst[k + 0] = hex[val >> 4];
535 dst[k + 1] = hex[val & 15];
536 }
537
538 return dlen;
539 }
540
hex_encode(const char* src, size_t slen)541 std::string StringBytes::hex_encode(const char* src, size_t slen) {
542 size_t dlen = slen * 2;
543 std::string dst(dlen, '\0');
544 hex_encode(src, slen, dst.data(), dlen);
545 return dst;
546 }
547
548 #define CHECK_BUFLEN_IN_RANGE(len) \
549 do { \
550 if ((len) > Buffer::kMaxLength) { \
551 *error = node::ERR_BUFFER_TOO_LARGE(isolate); \
552 return MaybeLocal<Value>(); \
553 } \
554 } while (0)
555
556
Encode(Isolate* isolate, const char* buf, size_t buflen, enum encoding encoding, Local<Value>* error)557 MaybeLocal<Value> StringBytes::Encode(Isolate* isolate,
558 const char* buf,
559 size_t buflen,
560 enum encoding encoding,
561 Local<Value>* error) {
562 CHECK_BUFLEN_IN_RANGE(buflen);
563
564 if (!buflen && encoding != BUFFER) {
565 return String::Empty(isolate);
566 }
567
568 MaybeLocal<String> val;
569
570 switch (encoding) {
571 case BUFFER:
572 {
573 auto maybe_buf = Buffer::Copy(isolate, buf, buflen);
574 Local<v8::Object> buf;
575 if (!maybe_buf.ToLocal(&buf)) {
576 *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate);
577 }
578 return buf;
579 }
580
581 case ASCII:
582 if (simdutf::validate_ascii_with_errors(buf, buflen).error) {
583 // The input contains non-ASCII bytes.
584 char* out = node::UncheckedMalloc(buflen);
585 if (out == nullptr) {
586 *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate);
587 return MaybeLocal<Value>();
588 }
589 force_ascii(buf, out, buflen);
590 return ExternOneByteString::New(isolate, out, buflen, error);
591 } else {
592 return ExternOneByteString::NewFromCopy(isolate, buf, buflen, error);
593 }
594
595 case UTF8:
596 {
597 val = String::NewFromUtf8(isolate,
598 buf,
599 v8::NewStringType::kNormal,
600 buflen);
601 Local<String> str;
602 if (!val.ToLocal(&str)) {
603 *error = node::ERR_STRING_TOO_LONG(isolate);
604 }
605 return str;
606 }
607
608 case LATIN1:
609 return ExternOneByteString::NewFromCopy(isolate, buf, buflen, error);
610
611 case BASE64: {
612 size_t dlen = base64_encoded_size(buflen);
613 char* dst = node::UncheckedMalloc(dlen);
614 if (dst == nullptr) {
615 *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate);
616 return MaybeLocal<Value>();
617 }
618
619 size_t written = base64_encode(buf, buflen, dst, dlen);
620 CHECK_EQ(written, dlen);
621
622 return ExternOneByteString::New(isolate, dst, dlen, error);
623 }
624
625 case BASE64URL: {
626 size_t dlen = base64_encoded_size(buflen, Base64Mode::URL);
627 char* dst = node::UncheckedMalloc(dlen);
628 if (dst == nullptr) {
629 *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate);
630 return MaybeLocal<Value>();
631 }
632
633 size_t written = base64_encode(buf, buflen, dst, dlen, Base64Mode::URL);
634 CHECK_EQ(written, dlen);
635
636 return ExternOneByteString::New(isolate, dst, dlen, error);
637 }
638
639 case HEX: {
640 size_t dlen = buflen * 2;
641 char* dst = node::UncheckedMalloc(dlen);
642 if (dst == nullptr) {
643 *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate);
644 return MaybeLocal<Value>();
645 }
646 size_t written = hex_encode(buf, buflen, dst, dlen);
647 CHECK_EQ(written, dlen);
648
649 return ExternOneByteString::New(isolate, dst, dlen, error);
650 }
651
652 case UCS2: {
653 size_t str_len = buflen / 2;
654 if (IsBigEndian()) {
655 uint16_t* dst = node::UncheckedMalloc<uint16_t>(str_len);
656 if (str_len != 0 && dst == nullptr) {
657 *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate);
658 return MaybeLocal<Value>();
659 }
660 for (size_t i = 0, k = 0; k < str_len; i += 2, k += 1) {
661 // The input is in *little endian*, because that's what Node.js
662 // expects, so the high byte comes after the low byte.
663 const uint8_t hi = static_cast<uint8_t>(buf[i + 1]);
664 const uint8_t lo = static_cast<uint8_t>(buf[i + 0]);
665 dst[k] = static_cast<uint16_t>(hi) << 8 | lo;
666 }
667 return ExternTwoByteString::New(isolate, dst, str_len, error);
668 }
669 if (reinterpret_cast<uintptr_t>(buf) % 2 != 0) {
670 // Unaligned data still means we can't directly pass it to V8.
671 char* dst = node::UncheckedMalloc(buflen);
672 if (dst == nullptr) {
673 *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate);
674 return MaybeLocal<Value>();
675 }
676 memcpy(dst, buf, buflen);
677 return ExternTwoByteString::New(
678 isolate, reinterpret_cast<uint16_t*>(dst), str_len, error);
679 }
680 return ExternTwoByteString::NewFromCopy(
681 isolate, reinterpret_cast<const uint16_t*>(buf), str_len, error);
682 }
683
684 default:
685 UNREACHABLE("unknown encoding");
686 }
687 }
688
689
Encode(Isolate* isolate, const uint16_t* buf, size_t buflen, Local<Value>* error)690 MaybeLocal<Value> StringBytes::Encode(Isolate* isolate,
691 const uint16_t* buf,
692 size_t buflen,
693 Local<Value>* error) {
694 if (buflen == 0) return String::Empty(isolate);
695 CHECK_BUFLEN_IN_RANGE(buflen);
696
697 // Node's "ucs2" encoding expects LE character data inside a
698 // Buffer, so we need to reorder on BE platforms. See
699 // https://nodejs.org/api/buffer.html regarding Node's "ucs2"
700 // encoding specification
701 if (IsBigEndian()) {
702 uint16_t* dst = node::UncheckedMalloc<uint16_t>(buflen);
703 if (dst == nullptr) {
704 *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate);
705 return MaybeLocal<Value>();
706 }
707 size_t nbytes = buflen * sizeof(uint16_t);
708 memcpy(dst, buf, nbytes);
709 SwapBytes16(reinterpret_cast<char*>(dst), nbytes);
710 return ExternTwoByteString::New(isolate, dst, buflen, error);
711 } else {
712 return ExternTwoByteString::NewFromCopy(isolate, buf, buflen, error);
713 }
714 }
715
Encode(Isolate* isolate, const char* buf, enum encoding encoding, Local<Value>* error)716 MaybeLocal<Value> StringBytes::Encode(Isolate* isolate,
717 const char* buf,
718 enum encoding encoding,
719 Local<Value>* error) {
720 const size_t len = strlen(buf);
721 return Encode(isolate, buf, len, encoding, error);
722 }
723
724 } // namespace node
725