diff --git a/benchmark/napi/string/.gitignore b/benchmark/napi/string/.gitignore new file mode 100644 index 00000000000000..567609b1234a9b --- /dev/null +++ b/benchmark/napi/string/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/benchmark/napi/string/binding.c b/benchmark/napi/string/binding.c new file mode 100644 index 00000000000000..9ac7de5875ef75 --- /dev/null +++ b/benchmark/napi/string/binding.c @@ -0,0 +1,56 @@ +#include +#define NAPI_EXPERIMENTAL +#include + +#define NAPI_CALL(call) \ + do { \ + napi_status status = call; \ + assert(status == napi_ok && #call " failed"); \ + } while (0); + +#define EXPORT_FUNC(env, exports, name, func) \ + do { \ + napi_value js_func; \ + NAPI_CALL(napi_create_function( \ + (env), (name), NAPI_AUTO_LENGTH, (func), NULL, &js_func)); \ + NAPI_CALL(napi_set_named_property((env), (exports), (name), js_func)); \ + } while (0); + +const char* one_byte_string = "The Quick Brown Fox Jumped Over The Lazy Dog."; +const char16_t* two_byte_string = + u"The Quick Brown Fox Jumped Over The Lazy Dog."; + +#define DECLARE_BINDING(CapName, lowercase_name, var_name) \ + static napi_value CreateString##CapName(napi_env env, \ + napi_callback_info info) { \ + size_t argc = 4; \ + napi_value argv[4]; \ + uint32_t n; \ + uint32_t index; \ + napi_handle_scope scope; \ + napi_value js_string; \ + \ + NAPI_CALL(napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); \ + NAPI_CALL(napi_get_value_uint32(env, argv[0], &n)); \ + NAPI_CALL(napi_open_handle_scope(env, &scope)); \ + NAPI_CALL(napi_call_function(env, argv[1], argv[2], 0, NULL, NULL)); \ + for (index = 0; index < n; index++) { \ + NAPI_CALL(napi_create_string_##lowercase_name( \ + env, (var_name), NAPI_AUTO_LENGTH, &js_string)); \ + } \ + NAPI_CALL(napi_call_function(env, argv[1], argv[3], 1, &argv[0], NULL)); \ + NAPI_CALL(napi_close_handle_scope(env, scope)); \ + \ + return NULL; \ + } + +DECLARE_BINDING(Latin1, latin1, one_byte_string) +DECLARE_BINDING(Utf8, utf8, one_byte_string) +DECLARE_BINDING(Utf16, utf16, two_byte_string) + +NAPI_MODULE_INIT() { + EXPORT_FUNC(env, exports, "createStringLatin1", CreateStringLatin1); + EXPORT_FUNC(env, exports, "createStringUtf8", CreateStringUtf8); + EXPORT_FUNC(env, exports, "createStringUtf16", CreateStringUtf16); + return exports; +} diff --git a/benchmark/napi/string/binding.gyp b/benchmark/napi/string/binding.gyp new file mode 100644 index 00000000000000..413621ade335a1 --- /dev/null +++ b/benchmark/napi/string/binding.gyp @@ -0,0 +1,8 @@ +{ + 'targets': [ + { + 'target_name': 'binding', + 'sources': [ 'binding.c' ] + } + ] +} diff --git a/benchmark/napi/string/index.js b/benchmark/napi/string/index.js new file mode 100644 index 00000000000000..2d2f82b39bdcaf --- /dev/null +++ b/benchmark/napi/string/index.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../../common.js'); + +let binding; +try { + binding = require(`./build/${common.buildType}/binding`); +} catch { + console.error(`${__filename}: Binding failed to load`); + process.exit(0); +} + +const bench = common.createBenchmark(main, { + n: [1e5, 1e6, 1e7], + stringType: ['Latin1', 'Utf8', 'Utf16'], +}); + +function main({ n, stringType }) { + binding[`createString${stringType}`](n, bench, bench.start, bench.end); +} diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 817dbcb2d52357..a4267b0d217fc7 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -801,7 +801,7 @@ napiVersion: 1 Function pointer type for add-on provided functions that allow the user to be notified when externally-owned data is ready to be cleaned up because the -object with which it was associated with, has been garbage-collected. The user +object with which it was associated with has been garbage-collected. The user must provide a function satisfying the following signature which would get called upon the object's collection. Currently, `napi_finalize` can be used for finding out when objects that have external data are collected. @@ -819,6 +819,11 @@ Since these functions may be called while the JavaScript engine is in a state where it cannot execute JavaScript code, some Node-API calls may return `napi_pending_exception` even when there is no exception pending. +In the case of [`node_api_create_external_string_latin1`][] and +[`node_api_create_external_string_utf16`][] the `env` parameter may be null, +because external strings can be collected during the latter part of environment +shutdown. + Change History: * experimental (`NAPI_EXPERIMENTAL` is defined): @@ -2886,6 +2891,56 @@ string. The native string is copied. The JavaScript `string` type is described in [Section 6.1.4][] of the ECMAScript Language Specification. +#### `node_api_create_external_string_latin1` + + + +> Stability: 1 - Experimental + +```c +napi_status +node_api_create_external_string_latin1(napi_env env, + char* str, + size_t length, + napi_finalize finalize_callback, + void* finalize_hint, + napi_value* result, + bool* copied); +``` + +* `[in] env`: The environment that the API is invoked under. +* `[in] str`: Character buffer representing an ISO-8859-1-encoded string. +* `[in] length`: The length of the string in bytes, or `NAPI_AUTO_LENGTH` if it + is null-terminated. +* `[in] finalize_callback`: The function to call when the string is being + collected. The function will be called with the following parameters: + * `[in] env`: The environment in which the add-on is running. This value + may be null if the string is being collected as part of the termination + of the worker or the main Node.js instance. + * `[in] data`: This is the value `str` as a `void*` pointer. + * `[in] finalize_hint`: This is the value `finalize_hint` that was given + to the API. + [`napi_finalize`][] provides more details. + This parameter is optional. Passing a null value means that the add-on + doesn't need to be notified when the corresponding JavaScript string is + collected. +* `[in] finalize_hint`: Optional hint to pass to the finalize callback during + collection. +* `[out] result`: A `napi_value` representing a JavaScript `string`. +* `[out] copied`: Whether the string was copied. If it was, the finalizer will + already have been invoked to destroy `str`. + +Returns `napi_ok` if the API succeeded. + +This API creates a JavaScript `string` value from an ISO-8859-1-encoded C +string. The native string may not be copied and must thus exist for the entire +life cycle of the JavaScript value. + +The JavaScript `string` type is described in +[Section 6.1.4][] of the ECMAScript Language Specification. + #### `napi_create_string_utf16` + +> Stability: 1 - Experimental + +```c +napi_status +node_api_create_external_string_utf16(napi_env env, + char16_t* str, + size_t length, + napi_finalize finalize_callback, + void* finalize_hint, + napi_value* result, + bool* copied); +``` + +* `[in] env`: The environment that the API is invoked under. +* `[in] str`: Character buffer representing a UTF16-LE-encoded string. +* `[in] length`: The length of the string in two-byte code units, or + `NAPI_AUTO_LENGTH` if it is null-terminated. +* `[in] finalize_callback`: The function to call when the string is being + collected. The function will be called with the following parameters: + * `[in] env`: The environment in which the add-on is running. This value + may be null if the string is being collected as part of the termination + of the worker or the main Node.js instance. + * `[in] data`: This is the value `str` as a `void*` pointer. + * `[in] finalize_hint`: This is the value `finalize_hint` that was given + to the API. + [`napi_finalize`][] provides more details. + This parameter is optional. Passing a null value means that the add-on + doesn't need to be notified when the corresponding JavaScript string is + collected. +* `[in] finalize_hint`: Optional hint to pass to the finalize callback during + collection. +* `[out] result`: A `napi_value` representing a JavaScript `string`. +* `[out] copied`: Whether the string was copied. If it was, the finalizer will + already have been invoked to destroy `str`. + +Returns `napi_ok` if the API succeeded. + +This API creates a JavaScript `string` value from a UTF16-LE-encoded C string. +The native string may not be copied and must thus exist for the entire life +cycle of the JavaScript value. + +The JavaScript `string` type is described in +[Section 6.1.4][] of the ECMAScript Language Specification. + #### `napi_create_string_utf8`