GeezerVison is showing github.com — util: add fast path for utf8 encoding · nodejs/node@32a3859
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: lib/internal/encoding.js +30-5Lines changed: 30 additions & 5 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 4 | 4 | // https://encoding.spec.whatwg.org | 5 | 5 |
| 6 | 6 | const { | | 7 | + Boolean, | 7 | 8 | ObjectCreate, | 8 | 9 | ObjectDefineProperties, | 9 | 10 | ObjectGetOwnPropertyDescriptors, | @@ -28,6 +29,8 @@ const kFlags = Symbol('flags'); | 28 | 29 | const kEncoding = Symbol('encoding'); | 29 | 30 | const kDecoder = Symbol('decoder'); | 30 | 31 | const kEncoder = Symbol('encoder'); | | 32 | +const kUTF8FastPath = Symbol('kUTF8FastPath'); | | 33 | +const kIgnoreBOM = Symbol('kIgnoreBOM'); | 31 | 34 |
| 32 | 35 | const { | 33 | 36 | getConstructorOf, | @@ -49,7 +52,8 @@ const { | 49 | 52 |
| 50 | 53 | const { | 51 | 54 | encodeInto, | 52 | | - encodeUtf8String | | 55 | + encodeUtf8String, | | 56 | + decodeUTF8, | 53 | 57 | } = internalBinding('buffer'); | 54 | 58 |
| 55 | 59 | let Buffer; | @@ -397,19 +401,40 @@ function makeTextDecoderICU() { | 397 | 401 | flags |= options.ignoreBOM ? CONVERTER_FLAGS_IGNORE_BOM : 0; | 398 | 402 | } | 399 | 403 |
| 400 | | - const handle = getConverter(enc, flags); | 401 | | - if (handle === undefined) | 402 | | - throw new ERR_ENCODING_NOT_SUPPORTED(encoding); | | 404 | + // Only support fast path for UTF-8 without FATAL flag | | 405 | + const fastPathAvailable = enc === 'utf-8' && !(options?.fatal); | 403 | 406 |
| 404 | 407 | this[kDecoder] = true; | 405 | | - this[kHandle] = handle; | 406 | 408 | this[kFlags] = flags; | 407 | 409 | this[kEncoding] = enc; | | 410 | + this[kIgnoreBOM] = Boolean(options?.ignoreBOM); | | 411 | + this[kUTF8FastPath] = fastPathAvailable; | | 412 | + this[kHandle] = undefined; | | 413 | + | | 414 | + if (!fastPathAvailable) { | | 415 | + this.#prepareConverter(); | | 416 | + } | 408 | 417 | } | 409 | 418 |
| | 419 | + #prepareConverter() { | | 420 | + if (this[kHandle] !== undefined) return; | | 421 | + const handle = getConverter(this[kEncoding], this[kFlags]); | | 422 | + if (handle === undefined) | | 423 | + throw new ERR_ENCODING_NOT_SUPPORTED(this[kEncoding]); | | 424 | + this[kHandle] = handle; | | 425 | + } | 410 | 426 |
| 411 | 427 | decode(input = empty, options = kEmptyObject) { | 412 | 428 | validateDecoder(this); | | 429 | + | | 430 | + this[kUTF8FastPath] &&= !(options?.stream); | | 431 | + | | 432 | + if (this[kUTF8FastPath]) { | | 433 | + return decodeUTF8(input, this[kIgnoreBOM]); | | 434 | + } | | 435 | + | | 436 | + this.#prepareConverter(); | | 437 | + | 413 | 438 | validateObject(options, 'options', { | 414 | 439 | nullable: true, | 415 | 440 | allowArray: true, | |
Collapse file Copy file name to clipboardExpand all lines: src/node_buffer.cc +45Lines changed: 45 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 24 | 24 | #include "node_blob.h" | 25 | 25 | #include "node_errors.h" | 26 | 26 | #include "node_external_reference.h" | | 27 | +#include "node_i18n.h" | 27 | 28 | #include "node_internals.h" | 28 | 29 |
| 29 | 30 | #include "env-inl.h" | @@ -565,6 +566,48 @@ void StringSlice(const FunctionCallbackInfo<Value>& args) { | 565 | 566 | args.GetReturnValue().Set(ret); | 566 | 567 | } | 567 | 568 |
| | 569 | +// Convert the input into an encoded string | | 570 | +void DecodeUTF8(const FunctionCallbackInfo<Value>& args) { | | 571 | + Environment* env = Environment::GetCurrent(args); // list, flags | | 572 | + | | 573 | + if (!(args[0]->IsArrayBuffer() || args[0]->IsSharedArrayBuffer() || | | 574 | + args[0]->IsArrayBufferView())) { | | 575 | + return node::THROW_ERR_INVALID_ARG_TYPE( | | 576 | + env->isolate(), | | 577 | + "The \"list\" argument must be an instance of SharedArrayBuffer, " | | 578 | + "ArrayBuffer or ArrayBufferView."); | | 579 | + } | | 580 | + | | 581 | + ArrayBufferViewContents<char> buffer(args[0]); | | 582 | + | | 583 | + CHECK(args[1]->IsBoolean()); | | 584 | + bool ignore_bom = args[1]->IsTrue(); | | 585 | + | | 586 | + const char* data = buffer.data(); | | 587 | + size_t length = buffer.length(); | | 588 | + | | 589 | + if (!ignore_bom && length >= 3) { | | 590 | + if (memcmp(data, "\xEF\xBB\xBF", 3) == 0) { | | 591 | + data += 3; | | 592 | + length -= 3; | | 593 | + } | | 594 | + } | | 595 | + | | 596 | + if (length == 0) return args.GetReturnValue().SetEmptyString(); | | 597 | + | | 598 | + Local<Value> error; | | 599 | + MaybeLocal<Value> maybe_ret = | | 600 | + StringBytes::Encode(env->isolate(), data, length, UTF8, &error); | | 601 | + Local<Value> ret; | | 602 | + | | 603 | + if (!maybe_ret.ToLocal(&ret)) { | | 604 | + CHECK(!error.IsEmpty()); | | 605 | + env->isolate()->ThrowException(error); | | 606 | + return; | | 607 | + } | | 608 | + | | 609 | + args.GetReturnValue().Set(ret); | | 610 | +} | 568 | 611 |
| 569 | 612 | // bytesCopied = copy(buffer, target[, targetStart][, sourceStart][, sourceEnd]) | 570 | 613 | void Copy(const FunctionCallbackInfo<Value> &args) { | @@ -1282,6 +1325,7 @@ void Initialize(Local<Object> target, | 1282 | 1325 |
| 1283 | 1326 | SetMethod(context, target, "setBufferPrototype", SetBufferPrototype); | 1284 | 1327 | SetMethodNoSideEffect(context, target, "createFromString", CreateFromString); | | 1328 | + SetMethodNoSideEffect(context, target, "decodeUTF8", DecodeUTF8); | 1285 | 1329 |
| 1286 | 1330 | SetMethodNoSideEffect(context, target, "byteLengthUtf8", ByteLengthUtf8); | 1287 | 1331 | SetMethod(context, target, "copy", Copy); | @@ -1339,6 +1383,7 @@ void Initialize(Local<Object> target, | 1339 | 1383 | void RegisterExternalReferences(ExternalReferenceRegistry* registry) { | 1340 | 1384 | registry->Register(SetBufferPrototype); | 1341 | 1385 | registry->Register(CreateFromString); | | 1386 | + registry->Register(DecodeUTF8); | 1342 | 1387 |
| 1343 | 1388 | registry->Register(ByteLengthUtf8); | 1344 | 1389 | registry->Register(Copy); | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-whatwg-encoding-custom-textdecoder.js +1-1Lines changed: 1 addition & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -113,7 +113,7 @@ if (common.hasIntl) { | 113 | 113 | ' fatal: false,\n' + | 114 | 114 | ' ignoreBOM: true,\n' + | 115 | 115 | ' [Symbol(flags)]: 4,\n' + | 116 | | - ' [Symbol(handle)]: Converter {}\n' + | | 116 | + ' [Symbol(handle)]: undefined\n' + | 117 | 117 | '}' | 118 | 118 | ); | 119 | 119 | } else { | |
|