Skip to content

Commit d3508ca

Browse files
authored
Merge branch 'nodejs:main' into main
2 parents 0ec91b9 + 58f408f commit d3508ca

23 files changed

+571
-57
lines changed

benchmark/dgram/multi-buffer.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,18 @@ const common = require('../common.js');
55
const dgram = require('dgram');
66
const PORT = common.PORT;
77

8-
// `num` is the number of send requests to queue up each time.
8+
// `n` is the number of send requests to queue up each time.
99
// Keep it reasonably high (>10) otherwise you're benchmarking the speed of
1010
// event loop cycles more than anything else.
1111
const bench = common.createBenchmark(main, {
12-
len: [64, 256, 1024],
13-
num: [100],
14-
chunks: [1, 2, 4, 8],
12+
len: [64, 512, 1024],
13+
n: [100],
14+
chunks: [1, 8],
1515
type: ['send', 'recv'],
1616
dur: [5],
1717
});
1818

19-
function main({ dur, len, num, type, chunks }) {
19+
function main({ dur, len, n, type, chunks }) {
2020
const chunk = [];
2121
for (let i = 0; i < chunks; i++) {
2222
chunk.push(Buffer.allocUnsafe(Math.round(len / chunks)));
@@ -26,11 +26,11 @@ function main({ dur, len, num, type, chunks }) {
2626
const socket = dgram.createSocket('udp4');
2727

2828
function onsend() {
29-
if (sent++ % num === 0) {
29+
if (sent++ % n === 0) {
3030
// The setImmediate() is necessary to have event loop progress on OSes
3131
// that only perform synchronous I/O on nonblocking UDP sockets.
3232
setImmediate(() => {
33-
for (let i = 0; i < num; i++) {
33+
for (let i = 0; i < n; i++) {
3434
socket.send(chunk, PORT, '127.0.0.1', onsend);
3535
}
3636
});

benchmark/zlib/crc32.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict';
2+
3+
const common = require('../common.js');
4+
const { crc32 } = require('zlib');
5+
6+
// Benchmark crc32 on Buffer and String inputs across sizes.
7+
// Iteration count is scaled inversely with input length to keep runtime sane.
8+
// Example:
9+
// node benchmark/zlib/crc32.js type=buffer len=4096 n=4000000
10+
// ./out/Release/node benchmark/zlib/crc32.js --test
11+
12+
const bench = common.createBenchmark(main, {
13+
type: ['buffer', 'string'],
14+
len: [32, 256, 4096, 65536],
15+
n: [4e6],
16+
});
17+
18+
function makeBuffer(size) {
19+
const buf = Buffer.allocUnsafe(size);
20+
for (let i = 0; i < size; i++) buf[i] = (i * 1103515245 + 12345) & 0xff;
21+
return buf;
22+
}
23+
24+
function makeAsciiString(size) {
25+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
26+
let out = '';
27+
for (let i = 0, j = 0; i < size; i++, j = (j + 7) % chars.length) out += chars[j];
28+
return out;
29+
}
30+
31+
function main({ type, len, n }) {
32+
// Scale iterations so that total processed bytes roughly constant around n*4096 bytes.
33+
const scale = 4096 / len;
34+
const iters = Math.max(1, Math.floor(n * scale));
35+
36+
const data = type === 'buffer' ? makeBuffer(len) : makeAsciiString(len);
37+
38+
let acc = 0;
39+
for (let i = 0; i < Math.min(iters, 10000); i++) acc ^= crc32(data, 0);
40+
41+
bench.start();
42+
let sum = 0;
43+
for (let i = 0; i < iters; i++) sum ^= crc32(data, 0);
44+
bench.end(iters);
45+
46+
if (sum === acc - 1) process.stderr.write('');
47+
}

doc/api/assert.md

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,11 +229,20 @@ The `Assert` class allows creating independent assertion instances with custom o
229229

230230
### `new assert.Assert([options])`
231231

232+
<!-- YAML
233+
changes:
234+
- version: REPLACEME
235+
pr-url: https://github.com/nodejs/node/pull/59762
236+
description: Added `skipPrototype` option.
237+
-->
238+
232239
* `options` {Object}
233240
* `diff` {string} If set to `'full'`, shows the full diff in assertion errors. Defaults to `'simple'`.
234241
Accepted values: `'simple'`, `'full'`.
235242
* `strict` {boolean} If set to `true`, non-strict methods behave like their
236243
corresponding strict methods. Defaults to `true`.
244+
* `skipPrototype` {boolean} If set to `true`, skips prototype and constructor
245+
comparison in deep equality checks. Defaults to `false`.
237246

238247
Creates a new assertion instance. The `diff` option controls the verbosity of diffs in assertion error messages.
239248

@@ -245,7 +254,8 @@ assertInstance.deepStrictEqual({ a: 1 }, { a: 2 });
245254
```
246255

247256
**Important**: When destructuring assertion methods from an `Assert` instance,
248-
the methods lose their connection to the instance's configuration options (such as `diff` and `strict` settings).
257+
the methods lose their connection to the instance's configuration options (such
258+
as `diff`, `strict`, and `skipPrototype` settings).
249259
The destructured methods will fall back to default behavior instead.
250260

251261
```js
@@ -259,6 +269,33 @@ const { strictEqual } = myAssert;
259269
strictEqual({ a: 1 }, { b: { c: 1 } });
260270
```
261271

272+
The `skipPrototype` option affects all deep equality methods:
273+
274+
```js
275+
class Foo {
276+
constructor(a) {
277+
this.a = a;
278+
}
279+
}
280+
281+
class Bar {
282+
constructor(a) {
283+
this.a = a;
284+
}
285+
}
286+
287+
const foo = new Foo(1);
288+
const bar = new Bar(1);
289+
290+
// Default behavior - fails due to different constructors
291+
const assert1 = new Assert();
292+
assert1.deepStrictEqual(foo, bar); // AssertionError
293+
294+
// Skip prototype comparison - passes if properties are equal
295+
const assert2 = new Assert({ skipPrototype: true });
296+
assert2.deepStrictEqual(foo, bar); // OK
297+
```
298+
262299
When destructured, methods lose access to the instance's `this` context and revert to default assertion behavior
263300
(diff: 'simple', non-strict mode).
264301
To maintain custom options when using destructured methods, avoid

doc/api/http.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2142,7 +2142,7 @@ added: v0.4.0
21422142
-->
21432143

21442144
* `name` {string}
2145-
* Returns: {any}
2145+
* Returns: {number | string | string\[] | undefined}
21462146

21472147
Reads out a header that's already been queued but not sent to the client.
21482148
The name is case-insensitive. The type of the return value depends
@@ -2277,7 +2277,7 @@ added: v0.4.0
22772277
-->
22782278

22792279
* `name` {string}
2280-
* `value` {any}
2280+
* `value` {number | string | string\[]}
22812281
* Returns: {http.ServerResponse}
22822282

22832283
Returns the response object.
@@ -3228,7 +3228,7 @@ added: v0.4.0
32283228
-->
32293229
32303230
* `name` {string} Name of header
3231-
* Returns: {string | undefined}
3231+
* Returns: {number | string | string\[] | undefined}
32323232
32333233
Gets the value of the HTTP header with the given name. If that header is not
32343234
set, the returned value will be `undefined`.
@@ -3330,7 +3330,7 @@ added: v0.4.0
33303330
-->
33313331
33323332
* `name` {string} Header name
3333-
* `value` {any} Header value
3333+
* `value` {number | string | string\[]} Header value
33343334
* Returns: {this}
33353335
33363336
Sets a single header value. If the header already exists in the to-be-sent

doc/api/util.md

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1574,19 +1574,56 @@ inspect.defaultOptions.maxArrayLength = null;
15741574
console.log(arr); // logs the full array
15751575
```
15761576

1577-
## `util.isDeepStrictEqual(val1, val2)`
1577+
## `util.isDeepStrictEqual(val1, val2[, options])`
15781578

15791579
<!-- YAML
15801580
added: v9.0.0
1581+
changes:
1582+
- version: REPLACEME
1583+
pr-url: https://github.com/nodejs/node/pull/59762
1584+
description: Added `options` parameter to allow skipping prototype comparison.
15811585
-->
15821586

15831587
* `val1` {any}
15841588
* `val2` {any}
1589+
* `skipPrototype` {boolean} If `true`, prototype and constructor
1590+
comparison is skipped during deep strict equality check. **Default:** `false`.
15851591
* Returns: {boolean}
15861592

15871593
Returns `true` if there is deep strict equality between `val1` and `val2`.
15881594
Otherwise, returns `false`.
15891595

1596+
By default, deep strict equality includes comparison of object prototypes and
1597+
constructors. When `skipPrototype` is `true`, objects with
1598+
different prototypes or constructors can still be considered equal if their
1599+
enumerable properties are deeply strictly equal.
1600+
1601+
```js
1602+
const util = require('node:util');
1603+
1604+
class Foo {
1605+
constructor(a) {
1606+
this.a = a;
1607+
}
1608+
}
1609+
1610+
class Bar {
1611+
constructor(a) {
1612+
this.a = a;
1613+
}
1614+
}
1615+
1616+
const foo = new Foo(1);
1617+
const bar = new Bar(1);
1618+
1619+
// Different constructors, same properties
1620+
console.log(util.isDeepStrictEqual(foo, bar));
1621+
// false
1622+
1623+
console.log(util.isDeepStrictEqual(foo, bar, true));
1624+
// true
1625+
```
1626+
15901627
See [`assert.deepStrictEqual()`][] for more information about deep strict
15911628
equality.
15921629

doc/api/webcrypto.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,9 +1220,9 @@ changes:
12201220
* `keyUsages` {string\[]} See [Key usages][].
12211221
* Returns: {Promise} Fulfills with a {CryptoKey|CryptoKeyPair} upon success.
12221222
1223-
Using the method and parameters provided in `algorithm`, `subtle.generateKey()`
1224-
attempts to generate new keying material. Depending the method used, the method
1225-
may generate either a single {CryptoKey} or a {CryptoKeyPair}.
1223+
Using the parameters provided in `algorithm`, this method
1224+
attempts to generate new keying material. Depending on the algorithm used
1225+
either a single {CryptoKey} or a {CryptoKeyPair} is generated.
12261226
12271227
The {CryptoKeyPair} (public and private key) generating algorithms supported
12281228
include:
@@ -1297,10 +1297,11 @@ changes:
12971297
* `keyUsages` {string\[]} See [Key usages][].
12981298
* Returns: {Promise} Fulfills with a {CryptoKey} upon success.
12991299
1300-
The [`subtle.importKey()`][] method attempts to interpret the provided `keyData`
1300+
This method attempts to interpret the provided `keyData`
13011301
as the given `format` to create a {CryptoKey} instance using the provided
13021302
`algorithm`, `extractable`, and `keyUsages` arguments. If the import is
1303-
successful, the returned promise will be resolved with the created {CryptoKey}.
1303+
successful, the returned promise will be resolved with a {CryptoKey}
1304+
representation of the key material.
13041305
13051306
If importing KDF algorithm keys, `extractable` must be `false`.
13061307

lib/_http_common.js

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
const {
2525
MathMin,
2626
Symbol,
27+
Uint8Array,
2728
} = primordials;
2829
const { setImmediate } = require('timers');
2930

@@ -205,7 +206,30 @@ function freeParser(parser, req, socket) {
205206
}
206207
}
207208

209+
// Character code ranges for valid HTTP tokens
210+
// Valid chars: ^_`a-zA-Z-0-9!#$%&'*+.|~
211+
// Based on RFC 7230 Section 3.2.6 token definition
212+
// See https://tools.ietf.org/html/rfc7230#section-3.2.6
208213
const tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/;
214+
const validTokenChars = new Uint8Array([
215+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0-15
216+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31
217+
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32-47 (!"#$%&'()*+,-./)
218+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48-63 (0-9:;<=>?)
219+
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64-79 (@A-O)
220+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80-95 (P-Z[\]^_)
221+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96-111 (`a-o)
222+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 112-127 (p-z{|}~)
223+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128-143
224+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144-159
225+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160-175
226+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176-191
227+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192-207
228+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208-223
229+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224-239
230+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 240-255
231+
]);
232+
209233
/**
210234
* Verifies that the given val is a valid HTTP token
211235
* per the rules defined in RFC 7230
@@ -214,7 +238,19 @@ const tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/;
214238
* @returns {boolean}
215239
*/
216240
function checkIsHttpToken(val) {
217-
return tokenRegExp.test(val);
241+
if (val.length >= 10) {
242+
return tokenRegExp.test(val);
243+
}
244+
245+
if (val.length === 0) return false;
246+
247+
// Use lookup table for short strings, regex for longer ones
248+
for (let i = 0; i < val.length; i++) {
249+
if (!validTokenChars[val.charCodeAt(i)]) {
250+
return false;
251+
}
252+
}
253+
return true;
218254
}
219255

220256
const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/;

lib/assert.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ const NO_EXCEPTION_SENTINEL = {};
9393
* @property {'full'|'simple'} [diff='simple'] - If set to 'full', shows the full diff in assertion errors.
9494
* @property {boolean} [strict=true] - If set to true, non-strict methods behave like their corresponding
9595
* strict methods.
96+
* @property {boolean} [skipPrototype=false] - If set to true, skips comparing prototypes
97+
* in deep equality checks.
9698
*/
9799

98100
/**
@@ -105,7 +107,7 @@ function Assert(options) {
105107
throw new ERR_CONSTRUCT_CALL_REQUIRED('Assert');
106108
}
107109

108-
options = ObjectAssign({ __proto__: null, strict: true }, options);
110+
options = ObjectAssign({ __proto__: null, strict: true, skipPrototype: false }, options);
109111

110112
const allowedDiffs = ['simple', 'full'];
111113
if (options.diff !== undefined) {
@@ -311,7 +313,7 @@ Assert.prototype.deepStrictEqual = function deepStrictEqual(actual, expected, me
311313
throw new ERR_MISSING_ARGS('actual', 'expected');
312314
}
313315
if (isDeepEqual === undefined) lazyLoadComparison();
314-
if (!isDeepStrictEqual(actual, expected)) {
316+
if (!isDeepStrictEqual(actual, expected, this?.[kOptions]?.skipPrototype)) {
315317
innerFail({
316318
actual,
317319
expected,
@@ -337,7 +339,7 @@ function notDeepStrictEqual(actual, expected, message) {
337339
throw new ERR_MISSING_ARGS('actual', 'expected');
338340
}
339341
if (isDeepEqual === undefined) lazyLoadComparison();
340-
if (isDeepStrictEqual(actual, expected)) {
342+
if (isDeepStrictEqual(actual, expected, this?.[kOptions]?.skipPrototype)) {
341343
innerFail({
342344
actual,
343345
expected,

lib/internal/readline/interface.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,12 @@ class Interface extends InterfaceConstructor {
658658

659659
[kInsertString](c) {
660660
this[kBeforeEdit](this.line, this.cursor);
661+
if (!this.isCompletionEnabled) {
662+
this.line += c;
663+
this.cursor += c.length;
664+
this[kWriteToOutput](c);
665+
return;
666+
}
661667
if (this.cursor < this.line.length) {
662668
const beg = StringPrototypeSlice(this.line, 0, this.cursor);
663669
const end = StringPrototypeSlice(

0 commit comments

Comments
 (0)