@mark1russell7/collections 0.9.5 → 0.9.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/build/index.d.ts +10 -4
- package/build/index.d.ts.map +1 -1
- package/build/index.js +34 -18
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/behaviors/synchronized.ts +20 -3
- package/src/utils/defaults.ts +19 -13
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package",
|
|
3
3
|
"name": "@mark1russell7/collections",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.7",
|
|
5
5
|
"description": "Collections with no dependencies: lists, maps, queues, a summary tree, channels, lazy operations and dependency graphs.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"collections",
|
|
@@ -277,8 +277,12 @@ export function readWriteLock<C extends object>(): Middleware<C> {
|
|
|
277
277
|
"forEach",
|
|
278
278
|
"getOrDefault",
|
|
279
279
|
"getOrUndefined",
|
|
280
|
+
"descendingIterator",
|
|
280
281
|
]);
|
|
281
282
|
|
|
283
|
+
// The reads that give an iterator. The lock copies the elements before it releases the read (C32).
|
|
284
|
+
const ITERATOR_READS = new Set(["keys", "values", "entries", "descendingIterator"]);
|
|
285
|
+
|
|
282
286
|
return new Proxy(next, {
|
|
283
287
|
get(target, prop, receiver) {
|
|
284
288
|
const value = Reflect.get(target, prop, receiver);
|
|
@@ -287,15 +291,28 @@ export function readWriteLock<C extends object>(): Middleware<C> {
|
|
|
287
291
|
return value;
|
|
288
292
|
}
|
|
289
293
|
|
|
290
|
-
|
|
294
|
+
// A method with a symbol key, for example Symbol.iterator, stays synchronous, so `for of` works (C32).
|
|
295
|
+
// The loop reads at once with no lock, as a plain method of synchronized().
|
|
296
|
+
if (typeof prop === "symbol") {
|
|
297
|
+
return function (this: unknown, ...args: unknown[]) {
|
|
298
|
+
return (value as Function).apply(target, args);
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const isReadMethod = READ_METHODS.has(prop);
|
|
303
|
+
const isIteratorRead = ITERATOR_READS.has(prop);
|
|
291
304
|
|
|
292
305
|
return async function (this: any, ...args: any[]) {
|
|
293
306
|
if (isReadMethod) {
|
|
294
307
|
// Acquire read lock
|
|
295
308
|
await acquireRead();
|
|
296
309
|
try {
|
|
297
|
-
const
|
|
298
|
-
|
|
310
|
+
const raw = (value as Function).apply(target, args);
|
|
311
|
+
const result = raw instanceof Promise ? await raw : raw;
|
|
312
|
+
// Before C32, the iteration started after the release of the read, so a write could change the elements.
|
|
313
|
+
return isIteratorRead && result != null && typeof result[Symbol.iterator] === "function"
|
|
314
|
+
? Array.from(result as Iterable<unknown>)[Symbol.iterator]()
|
|
315
|
+
: result;
|
|
299
316
|
} finally {
|
|
300
317
|
releaseRead();
|
|
301
318
|
}
|
package/src/utils/defaults.ts
CHANGED
|
@@ -368,25 +368,29 @@ export const hashBy = <T>(accessor: (value: T) => any): Hash<T> => {
|
|
|
368
368
|
// Default Comparison
|
|
369
369
|
// ============================================================================
|
|
370
370
|
|
|
371
|
+
/** This function gives the place of `null` and `undefined` in the order of `defaultCompare`: `null`, then `undefined`, then each other value. */
|
|
372
|
+
const nullishRank = (value: unknown): number => (value === null ? 0 : value === undefined ? 1 : 2);
|
|
373
|
+
|
|
371
374
|
/**
|
|
372
|
-
*
|
|
373
|
-
*
|
|
375
|
+
* This comparator gives the natural order of primitives.
|
|
376
|
+
* - `null` comes first, then `undefined`, then each other value.
|
|
377
|
+
* - Numbers and dates compare as `numberCompare` and `dateCompare` compare them.
|
|
378
|
+
* - Strings compare by `localeCompare`, and `false` comes before `true`.
|
|
379
|
+
* - Two values of different types compare by their text, with `String`.
|
|
374
380
|
*/
|
|
375
381
|
export const defaultCompare: Compare<any> = (a, b) => {
|
|
376
382
|
if (a === b) return 0;
|
|
377
|
-
|
|
378
|
-
if (
|
|
383
|
+
const nullish = nullishRank(a) - nullishRank(b);
|
|
384
|
+
if (nullish !== 0) return nullish;
|
|
379
385
|
|
|
380
386
|
// Handle dates
|
|
381
387
|
if (a instanceof Date && b instanceof Date) {
|
|
382
|
-
return a
|
|
388
|
+
return dateCompare(a, b);
|
|
383
389
|
}
|
|
384
390
|
|
|
385
391
|
// Handle numbers
|
|
386
392
|
if (typeof a === "number" && typeof b === "number") {
|
|
387
|
-
|
|
388
|
-
if (Number.isNaN(b)) return 1;
|
|
389
|
-
return a - b;
|
|
393
|
+
return numberCompare(a, b);
|
|
390
394
|
}
|
|
391
395
|
|
|
392
396
|
// Handle strings
|
|
@@ -404,12 +408,14 @@ export const defaultCompare: Compare<any> = (a, b) => {
|
|
|
404
408
|
};
|
|
405
409
|
|
|
406
410
|
/**
|
|
407
|
-
*
|
|
411
|
+
* This comparator gives the order of numbers by value. It gives -1, 0 or 1.
|
|
412
|
+
* - `NaN` comes first, and two `NaN` values give 0.
|
|
413
|
+
* - Two equal infinities give 0, and 0 equals -0.
|
|
408
414
|
*/
|
|
409
415
|
export const numberCompare: Compare<number> = (a, b) => {
|
|
410
416
|
if (Number.isNaN(a)) return Number.isNaN(b) ? 0 : -1;
|
|
411
417
|
if (Number.isNaN(b)) return 1;
|
|
412
|
-
return a - b;
|
|
418
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
413
419
|
};
|
|
414
420
|
|
|
415
421
|
/**
|
|
@@ -424,10 +430,10 @@ export const stringCompareIgnoreCase: Compare<string> = (a, b) =>
|
|
|
424
430
|
a.toLowerCase().localeCompare(b.toLowerCase());
|
|
425
431
|
|
|
426
432
|
/**
|
|
427
|
-
*
|
|
433
|
+
* This comparator gives the order of dates by time, with `numberCompare`. An invalid date has the time `NaN`, so it comes
|
|
434
|
+
* first, and two invalid dates give 0.
|
|
428
435
|
*/
|
|
429
|
-
export const dateCompare: Compare<Date> = (a, b) =>
|
|
430
|
-
a.getTime() - b.getTime();
|
|
436
|
+
export const dateCompare: Compare<Date> = (a, b) => numberCompare(a.getTime(), b.getTime());
|
|
431
437
|
|
|
432
438
|
/**
|
|
433
439
|
* Boolean comparison (false < true).
|