@yorozu/utils 0.3.1 → 0.5.1

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.
@@ -1,3 +1,3 @@
1
- export declare const empty: Uint8Array;
1
+ export declare const empty: Uint8Array<ArrayBuffer>;
2
2
  export declare function clone(buf: Uint8Array): Uint8Array;
3
3
  export declare function readNthBit(byte: number, bit: number): number;
@@ -1,9 +1,15 @@
1
1
  import { Deque } from '../structures';
2
2
  import { Deferred } from './deferred';
3
+ type WaitResult<T> = {
4
+ done: true;
5
+ } | {
6
+ done: false;
7
+ value: T;
8
+ };
3
9
  export declare class AsyncQueue<T> {
4
10
  readonly queue: Deque<T>;
5
11
  readonly maxSize: number | undefined;
6
- protected _consumerWaiters: Deque<Deferred<T | undefined>>;
12
+ protected _consumerWaiters: Deque<Deferred<WaitResult<T>>>;
7
13
  protected _producerWaiters: Deque<Deferred<void>>;
8
14
  protected _ended: boolean;
9
15
  constructor(from?: ArrayLike<T> | Deque<T>, maxSize?: number);
@@ -18,5 +24,7 @@ export declare class AsyncQueue<T> {
18
24
  next(): T | undefined;
19
25
  nextOrWait(): Promise<T | undefined>;
20
26
  [Symbol.asyncIterator](): AsyncIterableIterator<T>;
27
+ protected _waitForItem(): Promise<WaitResult<T>>;
21
28
  protected _wakeProducerIfNeeded(): void;
22
29
  }
30
+ export {};
package/async/index.d.ts CHANGED
@@ -7,5 +7,6 @@ export * from './condition-variable';
7
7
  export * from './deferred';
8
8
  export * from './emitter';
9
9
  export * from './pool';
10
+ export * from './priority-work-queue';
10
11
  export * from './sleep';
11
12
  export { timers };
@@ -0,0 +1,25 @@
1
+ export type WorkPri = "visible" | "preload" | "background";
2
+ export type PriorityWorkJob = {
3
+ id: string;
4
+ pri: WorkPri;
5
+ run: (ctx: {
6
+ signal: AbortSignal;
7
+ }) => Promise<void>;
8
+ };
9
+ export type PriorityWorkQueueStats = {
10
+ active: number;
11
+ queued: number;
12
+ maxActive: number;
13
+ };
14
+ export type PriorityWorkQueueOptions = {
15
+ concurrency?: number;
16
+ onError?: (error: Error, id: string) => void;
17
+ };
18
+ export type PriorityWorkQueue = {
19
+ enqueue(job: PriorityWorkJob): boolean;
20
+ cancel(id: string): boolean;
21
+ cancelAll(): void;
22
+ isBusy(id: string): boolean;
23
+ get stats(): PriorityWorkQueueStats;
24
+ };
25
+ export declare function createPriorityWorkQueue(opts?: PriorityWorkQueueOptions): PriorityWorkQueue;
@@ -0,0 +1,10 @@
1
+ export declare const fixedLengthExtraBits: Uint8Array;
2
+ export declare const fixedDistanceExtraBits: Uint8Array;
3
+ export declare const codeLengthOrder: Uint8Array;
4
+ export declare const lengthBase: Uint16Array;
5
+ export declare const lengthReverse: Int32Array;
6
+ export declare const distanceBase: Uint16Array;
7
+ export declare const distanceReverse: Int32Array;
8
+ export declare const reverseBits15: Uint16Array;
9
+ export declare const fixedLiteralLengths: Uint8Array;
10
+ export declare const fixedDistanceLengths: Uint8Array;
package/index.js CHANGED
@@ -44,9 +44,14 @@ function lastIndexOf(haystack, needle, start = haystack.length - 1) {
44
44
  return -1;
45
45
  }
46
46
  function indexOfArray(haystack, needle, start = 0) {
47
+ let length = haystack.length;
48
+ if (start < 0) {
49
+ start += length;
50
+ if (start < 0) start = 0;
51
+ }
47
52
  if (needle.length === 0) return start;
48
53
  if (needle.length === 1) return indexOf(haystack, needle[0], start);
49
- let max = haystack.length - needle.length;
54
+ let max = length - needle.length;
50
55
  for (let i = start; i <= max; i++) if (haystack[i] === needle[0]) {
51
56
  let j = 1;
52
57
  for (; j < needle.length; j++) if (haystack[i + j] !== needle[j]) break;
@@ -55,9 +60,14 @@ function indexOfArray(haystack, needle, start = 0) {
55
60
  return -1;
56
61
  }
57
62
  function lastIndexOfArray(haystack, needle, start = haystack.length - 1) {
63
+ let length = haystack.length;
64
+ if (start < 0) {
65
+ start += length;
66
+ if (start < 0) return -1;
67
+ } else if (start >= length) start = length - 1;
58
68
  if (needle.length === 0) return start;
59
69
  if (needle.length === 1) return lastIndexOf(haystack, needle[0], start);
60
- let maxStart = haystack.length - needle.length;
70
+ let maxStart = length - needle.length;
61
71
  let effectiveStart = start > maxStart ? maxStart : start;
62
72
  for (let i = effectiveStart; i >= 0; i--) {
63
73
  let j = 0;
@@ -1354,16 +1364,16 @@ var LruSet = class {
1354
1364
  var AsyncLock = class {
1355
1365
  _queue = new Deque();
1356
1366
  async acquire() {
1357
- let info;
1358
- while (info = this._queue.peekFront()) await info[0];
1359
1367
  let unlock;
1360
- const promise = new Promise((resolve) => {
1368
+ let promise = new Promise((resolve) => {
1361
1369
  unlock = resolve;
1362
1370
  });
1371
+ let prev = this._queue.peekBack();
1363
1372
  this._queue.pushBack([promise, unlock]);
1373
+ if (prev) await prev[0];
1364
1374
  }
1365
1375
  release() {
1366
- const front = this._queue.popFront();
1376
+ let front = this._queue.popFront();
1367
1377
  if (!front) throw new Error("Nothing to release.", { cause: this._queue });
1368
1378
  front[1]();
1369
1379
  }
@@ -1458,7 +1468,10 @@ var AsyncQueue = class {
1458
1468
  if (this._ended) throw new Error("Cannot enqueue after .end() has been called");
1459
1469
  while (true) {
1460
1470
  if (this._consumerWaiters.length > 0) {
1461
- this._consumerWaiters.popFront().resolve(item);
1471
+ this._consumerWaiters.popFront().resolve({
1472
+ done: false,
1473
+ value: item
1474
+ });
1462
1475
  return;
1463
1476
  }
1464
1477
  if (!this.isFull) {
@@ -1474,7 +1487,10 @@ var AsyncQueue = class {
1474
1487
  tryEnqueue(item) {
1475
1488
  if (this._ended || this.isFull) return false;
1476
1489
  if (this._consumerWaiters.length > 0) {
1477
- this._consumerWaiters.popFront().resolve(item);
1490
+ this._consumerWaiters.popFront().resolve({
1491
+ done: false,
1492
+ value: item
1493
+ });
1478
1494
  return true;
1479
1495
  }
1480
1496
  this.queue.pushBack(item);
@@ -1483,7 +1499,7 @@ var AsyncQueue = class {
1483
1499
  end() {
1484
1500
  if (this._ended) throw new Error(".end() has already been called.");
1485
1501
  this._ended = true;
1486
- for (let waiter of this._consumerWaiters) waiter.resolve(void 0);
1502
+ for (let waiter of this._consumerWaiters) waiter.resolve({ done: true });
1487
1503
  this._consumerWaiters.clear();
1488
1504
  let err = /* @__PURE__ */ new Error("Queue has been ended.");
1489
1505
  for (let waiter of this._producerWaiters) waiter.reject(err);
@@ -1505,27 +1521,40 @@ var AsyncQueue = class {
1505
1521
  return item;
1506
1522
  }
1507
1523
  if (this._ended) return;
1508
- let waiter = new Deferred();
1509
- this._consumerWaiters.pushBack(waiter);
1510
- return waiter.promise;
1524
+ let result = await this._waitForItem();
1525
+ if (result.done) return void 0;
1526
+ return result.value;
1511
1527
  }
1512
1528
  [Symbol.asyncIterator]() {
1513
1529
  let iterator = {
1514
1530
  [Symbol.asyncIterator]: () => iterator,
1515
1531
  next: async () => {
1516
- let item = await this.nextOrWait();
1517
- if (item === void 0) return {
1532
+ if (this.queue.length > 0) return {
1533
+ value: this.next(),
1534
+ done: false
1535
+ };
1536
+ if (this._ended) return {
1537
+ done: true,
1538
+ value: void 0
1539
+ };
1540
+ let result = await this._waitForItem();
1541
+ if (result.done) return {
1518
1542
  done: true,
1519
1543
  value: void 0
1520
1544
  };
1521
1545
  return {
1522
- value: item,
1546
+ value: result.value,
1523
1547
  done: false
1524
1548
  };
1525
1549
  }
1526
1550
  };
1527
1551
  return iterator;
1528
1552
  }
1553
+ _waitForItem() {
1554
+ let waiter = new Deferred();
1555
+ this._consumerWaiters.pushBack(waiter);
1556
+ return waiter.promise;
1557
+ }
1529
1558
  _wakeProducerIfNeeded() {
1530
1559
  if (this._producerWaiters.length > 0 && !this.isFull) this._producerWaiters.popFront().resolve();
1531
1560
  }
@@ -1774,26 +1803,6 @@ async function parallelMap(iterable, executor, options = {}) {
1774
1803
  return result;
1775
1804
  }
1776
1805
  //#endregion
1777
- //#region src/async/sleep.ts
1778
- function sleep(ms, signal) {
1779
- if (ms < 0) throw new RangeError("sleep: ms must be a non-negative number");
1780
- return new Promise((resolve, reject) => {
1781
- if (signal?.aborted) {
1782
- reject(signal.reason ?? new DOMException("The operation was aborted.", "AbortError"));
1783
- return;
1784
- }
1785
- let onAbort = () => {
1786
- clearTimeout(timeoutId);
1787
- reject(signal.reason ?? new DOMException("The operation was aborted.", "AbortError"));
1788
- };
1789
- let timeoutId = setTimeout(() => {
1790
- signal?.removeEventListener("abort", onAbort);
1791
- resolve();
1792
- }, ms);
1793
- signal?.addEventListener("abort", onAbort, { once: true });
1794
- });
1795
- }
1796
- //#endregion
1797
1806
  //#region src/types/error.ts
1798
1807
  function unknownToError(err) {
1799
1808
  if (err instanceof Error) return err;
@@ -1816,6 +1825,144 @@ function throwUnreachable() {
1816
1825
  throw new Error("Unreachable");
1817
1826
  }
1818
1827
  //#endregion
1828
+ //#region src/async/priority-work-queue.ts
1829
+ var PRI_ORDER = [
1830
+ "visible",
1831
+ "preload",
1832
+ "background"
1833
+ ];
1834
+ function isAbortError(err) {
1835
+ return err instanceof Error && err.name === "AbortError";
1836
+ }
1837
+ function emptyLanes() {
1838
+ return {
1839
+ visible: [],
1840
+ preload: [],
1841
+ background: []
1842
+ };
1843
+ }
1844
+ function resolveConcurrency(value) {
1845
+ if (value === void 0) return 3;
1846
+ let n = Math.floor(value);
1847
+ if (!Number.isFinite(n) || n < 1) return 1;
1848
+ return n;
1849
+ }
1850
+ function createPriorityWorkQueue(opts) {
1851
+ let concurrency = resolveConcurrency(opts?.concurrency);
1852
+ let lanes = emptyLanes();
1853
+ let queuedPri = /* @__PURE__ */ new Map();
1854
+ let active = /* @__PURE__ */ new Map();
1855
+ let maxActive = 0;
1856
+ function queuedCount() {
1857
+ return queuedPri.size;
1858
+ }
1859
+ function removeQueued(id) {
1860
+ let pri = queuedPri.get(id);
1861
+ if (pri === void 0) return void 0;
1862
+ let lane = lanes[pri];
1863
+ let idx = lane.findIndex((job) => job.id === id);
1864
+ queuedPri.delete(id);
1865
+ if (idx < 0) return void 0;
1866
+ return lane.splice(idx, 1)[0];
1867
+ }
1868
+ function pickNext() {
1869
+ for (let pri of PRI_ORDER) {
1870
+ let lane = lanes[pri];
1871
+ while (lane.length > 0) {
1872
+ let job = lane.shift();
1873
+ queuedPri.delete(job.id);
1874
+ if (active.has(job.id)) continue;
1875
+ return job;
1876
+ }
1877
+ }
1878
+ }
1879
+ function pump() {
1880
+ while (active.size < concurrency) {
1881
+ let job = pickNext();
1882
+ if (!job) return;
1883
+ startJob(job);
1884
+ }
1885
+ }
1886
+ function startJob(job) {
1887
+ let controller = new AbortController();
1888
+ active.set(job.id, {
1889
+ id: job.id,
1890
+ controller
1891
+ });
1892
+ if (active.size > maxActive) maxActive = active.size;
1893
+ (async () => {
1894
+ let reported;
1895
+ try {
1896
+ await job.run({ signal: controller.signal });
1897
+ } catch (err) {
1898
+ if (!isAbortError(err)) reported = unknownToError(err);
1899
+ } finally {
1900
+ active.delete(job.id);
1901
+ }
1902
+ if (reported) try {
1903
+ opts?.onError?.(reported, job.id);
1904
+ } catch {}
1905
+ pump();
1906
+ })();
1907
+ }
1908
+ return {
1909
+ enqueue(job) {
1910
+ if (active.has(job.id)) return false;
1911
+ removeQueued(job.id);
1912
+ lanes[job.pri].push({
1913
+ id: job.id,
1914
+ pri: job.pri,
1915
+ run: job.run
1916
+ });
1917
+ queuedPri.set(job.id, job.pri);
1918
+ pump();
1919
+ return true;
1920
+ },
1921
+ cancel(id) {
1922
+ if (removeQueued(id)) return true;
1923
+ let running = active.get(id);
1924
+ if (!running) return false;
1925
+ running.controller.abort();
1926
+ return true;
1927
+ },
1928
+ cancelAll() {
1929
+ lanes = emptyLanes();
1930
+ queuedPri.clear();
1931
+ for (let running of active.values()) running.controller.abort();
1932
+ },
1933
+ isBusy(id) {
1934
+ return active.has(id) || queuedPri.has(id);
1935
+ },
1936
+ get stats() {
1937
+ return {
1938
+ active: active.size,
1939
+ queued: queuedCount(),
1940
+ maxActive
1941
+ };
1942
+ }
1943
+ };
1944
+ }
1945
+ //#endregion
1946
+ //#region src/async/sleep.ts
1947
+ function sleep(ms, signal) {
1948
+ if (ms < 0) throw new RangeError("sleep: ms must be a non-negative number");
1949
+ return new Promise((resolve, reject) => {
1950
+ if (signal?.aborted) {
1951
+ reject(signal.reason ?? new DOMException("The operation was aborted.", "AbortError"));
1952
+ return;
1953
+ }
1954
+ let onAbort = () => {
1955
+ clearTimeout(timeoutId);
1956
+ reject(signal?.reason ?? new DOMException("The operation was aborted.", "AbortError"));
1957
+ };
1958
+ let timeoutId = setTimeout(() => {
1959
+ signal?.removeEventListener("abort", onAbort);
1960
+ resolve();
1961
+ }, ms);
1962
+ signal?.addEventListener("abort", onAbort, { once: true });
1963
+ });
1964
+ }
1965
+ //#endregion
1819
1966
  //#region src/compress/checksum.ts
1820
1967
  var crcTable = new Int32Array(256);
1821
1968
  for (let i = 0; i < 256; i++) {
@@ -3151,4 +3298,4 @@ var decompress_exports = /* @__PURE__ */ __exportAll({
3151
3298
  zlib: () => decompress$1
3152
3299
  });
3153
3300
  //#endregion
3154
- export { Adler32, AggregateError, AsyncInterval, AsyncLock, AsyncQueue, AsyncResource, ChecksumMismatchError, ConditionVariable, Crc32, CustomMap, CustomSet, Deferred, DeferredTracked, Deque, Emitter, FlateError, InvalidBlockTypeError, InvalidDistanceError, InvalidHeaderError, InvalidLengthLiteralError, LruMap, LruSet, NotImplementedError, StreamFinishedError, UnexpectedEofError, abs, adler32, asNonNull, assert, assertEndsWith, assertEndsWith as assertsEndsWith, assertHashKey, assertMatches, assertNotNull, assertStartsWith, asyncPool, base64_exports as base64, bitLength, clearUndefinedInPlace, composeMiddlewares, compress_exports as compress, crc32, decompress_exports as decompress, deepMerge, enumerate, euclideanGcd, fromBytes, hex_exports as hex, isBigInt, isBoolean, isFalsy, isFunction, isNotNull, isNotUndefined, isNumber, isObject, isString, isSymbol, isTruthy, max, max2, min, min2, modInv, modPowBinary, noop, objectEntries, objectKeys, parallelMap, sleep, splitOnce, throwNotImplemented, throwUnreachable, timers_exports as timers, toBytes, twoMultiplicity, typed_exports as typed, u8_exports as u8, unknownToError, unsafeCastType, utf8_exports as utf8 };
3301
+ export { Adler32, AggregateError, AsyncInterval, AsyncLock, AsyncQueue, AsyncResource, ChecksumMismatchError, ConditionVariable, Crc32, CustomMap, CustomSet, Deferred, DeferredTracked, Deque, Emitter, FlateError, InvalidBlockTypeError, InvalidDistanceError, InvalidHeaderError, InvalidLengthLiteralError, LruMap, LruSet, NotImplementedError, StreamFinishedError, UnexpectedEofError, abs, adler32, asNonNull, assert, assertEndsWith, assertEndsWith as assertsEndsWith, assertHashKey, assertMatches, assertNotNull, assertStartsWith, asyncPool, base64_exports as base64, bitLength, clearUndefinedInPlace, composeMiddlewares, compress_exports as compress, crc32, createPriorityWorkQueue, decompress_exports as decompress, deepMerge, enumerate, euclideanGcd, fromBytes, hex_exports as hex, isBigInt, isBoolean, isFalsy, isFunction, isNotNull, isNotUndefined, isNumber, isObject, isString, isSymbol, isTruthy, max, max2, min, min2, modInv, modPowBinary, noop, objectEntries, objectKeys, parallelMap, sleep, splitOnce, throwNotImplemented, throwUnreachable, timers_exports as timers, toBytes, twoMultiplicity, typed_exports as typed, u8_exports as u8, unknownToError, unsafeCastType, utf8_exports as utf8 };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@yorozu/utils",
3
3
  "type": "module",
4
- "version": "0.3.1",
4
+ "version": "0.5.1",
5
5
  "description": "quality of life",
6
6
  "license": "MIT",
7
7
  "exports": {