diff --git a/src/CollectionHelperMethods.ts b/src/CollectionHelperMethods.ts new file mode 100644 index 000000000..32bacd975 --- /dev/null +++ b/src/CollectionHelperMethods.ts @@ -0,0 +1,51 @@ +import type { Collection } from '../type-definitions/immutable'; +import assertNotInfinite from './utils/assertNotInfinite'; + +export function reduce( + collection: Collection, + reducer: (...args: unknown[]) => unknown, + reduction: unknown, + context: unknown, + useFirst: boolean, + reverse: boolean +) { + // @ts-expect-error Migrate to CollectionImpl in v6 + assertNotInfinite(collection.size); + // @ts-expect-error Migrate to CollectionImpl in v6 + collection.__iterate((v, k, c) => { + if (useFirst) { + useFirst = false; + reduction = v; + } else { + reduction = reducer.call(context, reduction, v, k, c); + } + }, reverse); + return reduction; +} + +export function keyMapper(v: V, k: K): K { + return k; +} + +export function entryMapper(v: V, k: K): [K, V] { + return [k, v]; +} + +export function not(predicate: (...args: unknown[]) => boolean) { + return function (this: unknown, ...args: unknown[]): boolean { + return !predicate.apply(this, args); + }; +} + +export function neg(predicate: (...args: unknown[]) => number) { + return function (this: unknown, ...args: unknown[]): number { + return -predicate.apply(this, args); + }; +} + +export function defaultNegComparator( + a: number | string, + b: number | string +): number { + return a < b ? 1 : a > b ? -1 : 0; +} diff --git a/src/CollectionImpl.js b/src/CollectionImpl.js index c20cad926..9976a4991 100644 --- a/src/CollectionImpl.js +++ b/src/CollectionImpl.js @@ -4,17 +4,23 @@ import { KeyedCollection, SetCollection, } from './Collection'; -import { hash } from './Hash'; +import { + defaultNegComparator, + entryMapper, + keyMapper, + neg, + not, + reduce, +} from './CollectionHelperMethods'; import { ITERATE_ENTRIES, ITERATE_KEYS, ITERATE_VALUES, - ITERATOR_SYMBOL, Iterator, + ITERATOR_SYMBOL, } from './Iterator'; import { List } from './List'; import { Map } from './Map'; -import { imul, smi } from './Math'; import { FromEntriesSequence, ToIndexedSequence, @@ -59,11 +65,12 @@ import { toObject } from './methods/toObject'; import { IS_COLLECTION_SYMBOL } from './predicates/isCollection'; import { isIndexed, IS_INDEXED_SYMBOL } from './predicates/isIndexed'; import { isKeyed, IS_KEYED_SYMBOL } from './predicates/isKeyed'; -import { isOrdered, IS_ORDERED_SYMBOL } from './predicates/isOrdered'; +import { IS_ORDERED_SYMBOL } from './predicates/isOrdered'; import { toJS } from './toJS'; import arrCopy from './utils/arrCopy'; import assertNotInfinite from './utils/assertNotInfinite'; import deepEqual from './utils/deepEqual'; +import { hashCollection } from './utils/hasCollection'; import mixin from './utils/mixin'; import quoteString from './utils/quoteString'; @@ -701,87 +708,6 @@ mixin(SetSeq, SetCollectionPrototype); // #pragma Helper functions -function reduce(collection, reducer, reduction, context, useFirst, reverse) { - assertNotInfinite(collection.size); - collection.__iterate((v, k, c) => { - if (useFirst) { - useFirst = false; - reduction = v; - } else { - reduction = reducer.call(context, reduction, v, k, c); - } - }, reverse); - return reduction; -} - -function keyMapper(v, k) { - return k; -} - -function entryMapper(v, k) { - return [k, v]; -} - -function not(predicate) { - return function () { - return !predicate.apply(this, arguments); - }; -} - -function neg(predicate) { - return function () { - return -predicate.apply(this, arguments); - }; -} - function defaultZipper() { return arrCopy(arguments); } - -function defaultNegComparator(a, b) { - return a < b ? 1 : a > b ? -1 : 0; -} - -function hashCollection(collection) { - if (collection.size === Infinity) { - return 0; - } - const ordered = isOrdered(collection); - const keyed = isKeyed(collection); - let h = ordered ? 1 : 0; - - collection.__iterate( - keyed - ? ordered - ? (v, k) => { - h = (31 * h + hashMerge(hash(v), hash(k))) | 0; - } - : (v, k) => { - h = (h + hashMerge(hash(v), hash(k))) | 0; - } - : ordered - ? (v) => { - h = (31 * h + hash(v)) | 0; - } - : (v) => { - h = (h + hash(v)) | 0; - } - ); - - return murmurHashOfSize(collection.size, h); -} - -function murmurHashOfSize(size, h) { - h = imul(h, 0xcc9e2d51); - h = imul((h << 15) | (h >>> -15), 0x1b873593); - h = imul((h << 13) | (h >>> -13), 5); - h = ((h + 0xe6546b64) | 0) ^ size; - h = imul(h ^ (h >>> 16), 0x85ebca6b); - h = imul(h ^ (h >>> 13), 0xc2b2ae35); - h = smi(h ^ (h >>> 16)); - return h; -} - -function hashMerge(a, b) { - return (a ^ (b + 0x9e3779b9 + (a << 6) + (a >> 2))) | 0; // int -} diff --git a/src/utils/hasCollection.ts b/src/utils/hasCollection.ts new file mode 100644 index 000000000..1d544aa58 --- /dev/null +++ b/src/utils/hasCollection.ts @@ -0,0 +1,52 @@ +import type { Collection } from '../../type-definitions/immutable'; +import { hash } from '../Hash'; +import { imul, smi } from '../Math'; +import { isKeyed } from '../predicates/isKeyed'; +import { isOrdered } from '../predicates/isOrdered'; + +export function hashCollection(collection: Collection): number { + // @ts-expect-error Migrate to CollectionImpl in v6 + if (collection.size === Infinity) { + return 0; + } + const ordered = isOrdered(collection); + const keyed = isKeyed(collection); + let h: number = ordered ? 1 : 0; + + // @ts-expect-error Migrate to CollectionImpl in v6 + collection.__iterate( + keyed + ? ordered + ? (v: V, k: K): void => { + h = (31 * h + hashMerge(hash(v), hash(k))) | 0; + } + : (v: V, k: K): void => { + h = (h + hashMerge(hash(v), hash(k))) | 0; + } + : ordered + ? (v: V): void => { + h = (31 * h + hash(v)) | 0; + } + : (v: V): void => { + h = (h + hash(v)) | 0; + } + ); + + // @ts-expect-error Migrate to CollectionImpl in v6 + return murmurHashOfSize(collection.size, h); +} + +function murmurHashOfSize(size: number, h: number): number { + h = imul(h, 0xcc9e2d51); + h = imul((h << 15) | (h >>> -15), 0x1b873593); + h = imul((h << 13) | (h >>> -13), 5); + h = ((h + 0xe6546b64) | 0) ^ size; + h = imul(h ^ (h >>> 16), 0x85ebca6b); + h = imul(h ^ (h >>> 13), 0xc2b2ae35); + h = smi(h ^ (h >>> 16)); + return h; +} + +function hashMerge(a: number, b: number): number { + return (a ^ (b + 0x9e3779b9 + (a << 6) + (a >> 2))) | 0; // int +}