Skip to content

Commit efb9127

Browse files
committed
Fix browserify
1 parent b75ad37 commit efb9127

1 file changed

Lines changed: 193 additions & 21 deletions

File tree

examples/browser/cql4browsers.js

Lines changed: 193 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16728,6 +16728,26 @@ const UnitTables = exports.UnitTables = {
1672816728
}
1672916729
return smi(hashed);
1673016730
}
16731+
// Per-process seed for the secondary collision hash. Never exposed nor
16732+
// serialized, so the public `hash()` stays deterministic. An odd base in
16733+
// [3, 2^20) keeps `base * h` exact as a double (no `Math.imul`).
16734+
var COLLISION_HASH_BASE = ((Math.random() * 0x100000) | 1) % 0x100000 || 0x9e37;
16735+
// Secondary hash to index entries within a `HashCollisionNode`, where every key
16736+
// shares the same primary `hash()`. Using a different, seeded base scatters
16737+
// crafted collision families (e.g. "Aa"/"BB", which only collide under base 31)
16738+
// that an attacker cannot precompute without the seed. It only narrows
16739+
// candidates — `is()` still decides equality — so non-string keys can safely
16740+
// fall back to the (here constant) primary hash and a linear scan.
16741+
function hashCollisionKey(key) {
16742+
if (typeof key !== 'string') {
16743+
return hash(key);
16744+
}
16745+
var hashed = 0;
16746+
for (var ii = 0; ii < key.length; ii++) {
16747+
hashed = (COLLISION_HASH_BASE * hashed + key.charCodeAt(ii)) | 0;
16748+
}
16749+
return hashed;
16750+
}
1673116751
function hashSymbol(sym) {
1673216752
var hashed = symbolMap[sym];
1673316753
if (hashed !== undefined) {
@@ -18660,20 +18680,79 @@ const UnitTables = exports.UnitTables = {
1866018680
return new HashArrayMapNode(ownerID, newCount, newNodes);
1866118681
};
1866218682

18683+
/**
18684+
* Trie leaf gathering entries whose keys all share the same 32-bit `hash()`.
18685+
* The trie routes by hash, so colliding keys cannot be separated and land here
18686+
* in a flat `entries` array, disambiguated by `is()`.
18687+
*
18688+
* To guard against hash-flooding DoS (CWE-407), large buckets build a secondary
18689+
* index keyed by a per-process seeded hash (`hashCollisionKey`). `is()` still
18690+
* decides equality, so the index can only ever narrow candidates, never lose a key.
18691+
*/
1866318692
var HashCollisionNode = function HashCollisionNode(ownerID, keyHash, entries) {
1866418693
this.ownerID = ownerID;
1866518694
this.keyHash = keyHash;
1866618695
this.entries = entries;
18696+
// Lazy `{ [secondaryHash]: number[] }`, built only past
18697+
// MIN_HASH_COLLISION_INDEX_SIZE so small buckets keep their linear path.
18698+
this._index = undefined;
1866718699
};
1866818700

18669-
HashCollisionNode.prototype.get = function get (shift, keyHash, key, notSetValue) {
18701+
// Returns the position of `key` in `this.entries`, or -1. Uses the secondary
18702+
// index when present; builds it only when `buildIndex` is true (reads and
18703+
// transient inserts, where the node is reused so the O(n) build amortizes).
18704+
// Persistent inserts already pay an O(n) copy, so a throwaway index is skipped.
18705+
HashCollisionNode.prototype._positionOf = function _positionOf (key, buildIndex) {
18706+
var entries = this.entries;
18707+
var index = this._index;
18708+
if (
18709+
index === undefined &&
18710+
buildIndex &&
18711+
entries.length >= MIN_HASH_COLLISION_INDEX_SIZE
18712+
) {
18713+
index = this._buildIndex();
18714+
}
18715+
if (index !== undefined) {
18716+
var positions = index[hashCollisionKey(key)];
18717+
if (positions !== undefined) {
18718+
for (var jj = 0; jj < positions.length; jj++) {
18719+
var ii = positions[jj];
18720+
if (is(key, entries[ii][0])) {
18721+
return ii;
18722+
}
18723+
}
18724+
}
18725+
return -1;
18726+
}
18727+
for (var ii$1 = 0, len = entries.length; ii$1 < len; ii$1++) {
18728+
if (is(key, entries[ii$1][0])) {
18729+
return ii$1;
18730+
}
18731+
}
18732+
return -1;
18733+
};
18734+
18735+
// Builds and memoizes the secondary index. A plain object, not `Map` — which
18736+
// in this module resolves to the *Immutable* Map, not the native one.
18737+
HashCollisionNode.prototype._buildIndex = function _buildIndex () {
18738+
var index = Object.create(null);
1867018739
var entries = this.entries;
1867118740
for (var ii = 0, len = entries.length; ii < len; ii++) {
18672-
if (is(key, entries[ii][0])) {
18673-
return entries[ii][1];
18741+
var secondaryHash = hashCollisionKey(entries[ii][0]);
18742+
var positions = index[secondaryHash];
18743+
if (positions !== undefined) {
18744+
positions.push(ii);
18745+
} else {
18746+
index[secondaryHash] = [ii];
1867418747
}
1867518748
}
18676-
return notSetValue;
18749+
this._index = index;
18750+
return index;
18751+
};
18752+
18753+
HashCollisionNode.prototype.get = function get (shift, keyHash, key, notSetValue) {
18754+
var idx = this._positionOf(key, true);
18755+
return idx === -1 ? notSetValue : this.entries[idx][1];
1867718756
};
1867818757

1867918758
HashCollisionNode.prototype.update = function update (ownerID, shift, keyHash, key, value, didChangeSize, didAlter) {
@@ -18693,14 +18772,11 @@ const UnitTables = exports.UnitTables = {
1869318772
}
1869418773

1869518774
var entries = this.entries;
18696-
var idx = 0;
1869718775
var len = entries.length;
18698-
for (; idx < len; idx++) {
18699-
if (is(key, entries[idx][0])) {
18700-
break;
18701-
}
18702-
}
18703-
var exists = idx < len;
18776+
var isEditable = ownerID && ownerID === this.ownerID;
18777+
var foundIdx = this._positionOf(key, isEditable);
18778+
var idx = foundIdx === -1 ? len : foundIdx;
18779+
var exists = foundIdx !== -1;
1870418780

1870518781
if (exists ? entries[idx][1] === value : removed) {
1870618782
return this;
@@ -18714,7 +18790,6 @@ const UnitTables = exports.UnitTables = {
1871418790
return new ValueNode(ownerID, this.keyHash, entries[idx ^ 1]);
1871518791
}
1871618792

18717-
var isEditable = ownerID && ownerID === this.ownerID;
1871818793
var newEntries = isEditable ? entries : arrCopy(entries);
1871918794

1872018795
if (exists) {
@@ -18723,11 +18798,27 @@ const UnitTables = exports.UnitTables = {
1872318798
idx === len - 1
1872418799
? newEntries.pop()
1872518800
: (newEntries[idx] = newEntries.pop());
18801+
// The swap-pop reshuffles positions; drop the stale index (rebuilt lazily).
18802+
if (isEditable) {
18803+
this._index = undefined;
18804+
}
1872618805
} else {
18806+
// Same key, same position: the index stays valid.
1872718807
newEntries[idx] = [key, value];
1872818808
}
1872918809
} else {
1873018810
newEntries.push([key, value]);
18811+
// Keep the index in sync on the transient insert path. Persistent inserts
18812+
// return a fresh node below whose index rebuilds lazily, so skip them.
18813+
if (isEditable && this._index !== undefined) {
18814+
var secondaryHash = hashCollisionKey(key);
18815+
var positions = this._index[secondaryHash];
18816+
if (positions !== undefined) {
18817+
positions.push(len);
18818+
} else {
18819+
this._index[secondaryHash] = [len];
18820+
}
18821+
}
1873118822
}
1873218823

1873318824
if (isEditable) {
@@ -19061,6 +19152,12 @@ const UnitTables = exports.UnitTables = {
1906119152
var MAX_BITMAP_INDEXED_SIZE = SIZE / 2;
1906219153
var MIN_HASH_ARRAY_MAP_SIZE = SIZE / 4;
1906319154

19155+
// Above this many colliding entries, a `HashCollisionNode` builds a seeded
19156+
// secondary index instead of scanning linearly. Kept small so the rare,
19157+
// naturally-occurring collision buckets stay overhead-free, while adversarial
19158+
// hash-flooding (thousands of keys sharing one hash) degrades gracefully.
19159+
var MIN_HASH_COLLISION_INDEX_SIZE = 16;
19160+
1906419161
function coerceKeyPath(keyPath) {
1906519162
if (isArrayLike(keyPath) && typeof keyPath !== 'string') {
1906619163
return keyPath;
@@ -19249,7 +19346,7 @@ const UnitTables = exports.UnitTables = {
1924919346
assertNotInfinite(size);
1925019347
if (size > 0 && size < SIZE) {
1925119348
// eslint-disable-next-line no-constructor-return
19252-
return makeList(0, size, SHIFT, null, new VNode(iter.toArray()));
19349+
return makeList(0, size, SHIFT, undefined, new VNode(iter.toArray()));
1925319350
}
1925419351
// eslint-disable-next-line no-constructor-return
1925519352
return empty.withMutations(function (list) {
@@ -19492,6 +19589,13 @@ const UnitTables = exports.UnitTables = {
1949219589
return obj.asImmutable();
1949319590
};
1949419591

19592+
/**
19593+
* A node in the List's 32-wide trie. At inner levels `array` holds child
19594+
* `VNode`s; at the leaf level it holds the List's values. Missing slots are
19595+
* `undefined` array holes.
19596+
*
19597+
* @template T
19598+
*/
1949519599
var VNode = function VNode(array, ownerID) {
1949619600
this.array = array;
1949719601
this.ownerID = ownerID;
@@ -19628,6 +19732,16 @@ const UnitTables = exports.UnitTables = {
1962819732
}
1962919733
}
1963019734

19735+
/**
19736+
* @param {number} origin
19737+
* @param {number} capacity
19738+
* @param {number} level
19739+
* @param {VNode | undefined} [root] The trie root, or `undefined` when every
19740+
* in-range value lives in the tail (or is a virtual `undefined`).
19741+
* @param {VNode | undefined} [tail]
19742+
* @param {OwnerID} [ownerID]
19743+
* @param {number} [hash]
19744+
*/
1963119745
function makeList(origin, capacity, level, root, tail, ownerID, hash) {
1963219746
var list = Object.create(ListPrototype);
1963319747
list.size = capacity - origin;
@@ -19745,6 +19859,14 @@ const UnitTables = exports.UnitTables = {
1974519859
return new VNode(node ? node.array.slice() : [], ownerID);
1974619860
}
1974719861

19862+
/**
19863+
* Returns the leaf `VNode` holding `rawIndex`, or `undefined` when no node is
19864+
* allocated for it (an all-`undefined` region).
19865+
*
19866+
* @param {List} list
19867+
* @param {number} rawIndex
19868+
* @returns {VNode | undefined}
19869+
*/
1974819870
function listNodeFor(list, rawIndex) {
1974919871
if (rawIndex >= getTailOffset(list._capacity)) {
1975019872
return list._tail;
@@ -19760,7 +19882,39 @@ const UnitTables = exports.UnitTables = {
1976019882
}
1976119883
}
1976219884

19885+
/**
19886+
* Validates requested bounds before int32 coercion in setListBounds().
19887+
* Throws when origin/capacity would exceed the trie's safe range.
19888+
*/
19889+
function validateListBoundsRequest(list, begin, end) {
19890+
var requestedOrigin = list._origin + (begin === undefined ? 0 : begin);
19891+
var requestedCapacity =
19892+
end === undefined
19893+
? list._capacity
19894+
: end < 0
19895+
? list._capacity + end
19896+
: list._origin + end;
19897+
19898+
// Keep origin/capacity within the trie's safe signed 32-bit range.
19899+
if (
19900+
(Number.isFinite(requestedCapacity) && requestedCapacity > MAX_LIST_SIZE) ||
19901+
(Number.isFinite(requestedOrigin) && requestedOrigin < -MAX_LIST_SIZE) ||
19902+
(Number.isFinite(requestedCapacity) &&
19903+
Number.isFinite(requestedOrigin) &&
19904+
requestedCapacity - requestedOrigin > MAX_LIST_SIZE)
19905+
) {
19906+
throw new RangeError(
19907+
'Invalid List size: a List cannot hold more than ' +
19908+
MAX_LIST_SIZE +
19909+
' (2 ** 30) values.'
19910+
);
19911+
}
19912+
}
19913+
1976319914
function setListBounds(list, begin, end) {
19915+
// Validate full-precision bounds before int32 coercion.
19916+
validateListBoundsRequest(list, begin, end);
19917+
1976419918
// Sanitize begin & end using this shorthand for ToInt32(argument)
1976519919
// http://www.ecma-international.org/ecma-262/6.0/#sec-toint32
1976619920
if (begin !== undefined) {
@@ -19799,7 +19953,8 @@ const UnitTables = exports.UnitTables = {
1979919953
owner
1980019954
);
1980119955
newLevel += SHIFT;
19802-
offsetShift += 1 << newLevel;
19956+
// Shift origin into non-negative space as trie height grows.
19957+
offsetShift += levelCapacity(newLevel);
1980319958
}
1980419959
if (offsetShift) {
1980519960
newOrigin += offsetShift;
@@ -19812,7 +19967,7 @@ const UnitTables = exports.UnitTables = {
1981219967
var newTailOffset = getTailOffset(newCapacity);
1981319968

1981419969
// New size might need creating a higher root.
19815-
while (newTailOffset >= 1 << (newLevel + SHIFT)) {
19970+
while (newTailOffset >= levelCapacity(newLevel + SHIFT)) {
1981619971
newRoot = new VNode(
1981719972
newRoot && newRoot.array.length ? [newRoot] : [],
1981819973
owner
@@ -19855,7 +20010,7 @@ const UnitTables = exports.UnitTables = {
1985520010
newOrigin -= newTailOffset;
1985620011
newCapacity -= newTailOffset;
1985720012
newLevel = SHIFT;
19858-
newRoot = null;
20013+
newRoot = undefined;
1985920014
newTail = newTail && newTail.removeBefore(owner, 0, newOrigin);
1986020015

1986120016
// Otherwise, if the root has been trimmed, garbage collect.
@@ -19910,6 +20065,19 @@ const UnitTables = exports.UnitTables = {
1991020065
return size < SIZE ? 0 : ((size - 1) >>> SHIFT) << SHIFT;
1991120066
}
1991220067

20068+
// The largest number of values a List can hold. Above this the 32-bit trie math
20069+
// in setListBounds() stays in the safe signed 32-bit range.
20070+
var MAX_LIST_SIZE = Math.pow( 2, 30 ); // 1073741824
20071+
20072+
/**
20073+
* Computes 2 ** exp for the trie level-raising loops in setListBounds().
20074+
* Use the cheap bitwise operator shift whenever possible, otherwise fall back to exponentiation.
20075+
* This is necessary because bitwise operators in JavaScript only work on 32-bit signed integers, so for exp >= 31, we need to use exponentiation to avoid overflow.
20076+
*/
20077+
function levelCapacity(exp) {
20078+
return exp < 31 ? 1 << exp : Math.pow( 2, exp );
20079+
}
20080+
1991320081
/**
1991420082
* True if `maybeOrderedMap` is an OrderedMap.
1991520083
*/
@@ -20325,6 +20493,9 @@ const UnitTables = exports.UnitTables = {
2032520493
reduction = v;
2032620494
}
2032720495
else {
20496+
// `reduction` has already been seeded here (either with the provided
20497+
// initial value or with the first iterated value), so it is never the
20498+
// `undefined` placeholder — only a `V` or a `R`.
2032820499
reduction = reducer.call(context, reduction, v, k, c);
2032920500
}
2033020501
}, reverse);
@@ -21509,11 +21680,12 @@ const UnitTables = exports.UnitTables = {
2150921680

2151021681
has: function has(index) {
2151121682
index = wrapIndex(this, index);
21683+
2151221684
return (
2151321685
index >= 0 &&
2151421686
(this.size !== undefined
2151521687
? this.size === Infinity || index < this.size
21516-
: this.indexOf(index) !== -1)
21688+
: this.find(function (_, key) { return key === index; }, undefined, NOT_SET) !== NOT_SET)
2151721689
);
2151821690
},
2151921691

@@ -21974,15 +22146,15 @@ const UnitTables = exports.UnitTables = {
2197422146
};
2197522147

2197622148
Repeat.prototype.indexOf = function indexOf (searchValue) {
21977-
if (is(this._value, searchValue)) {
22149+
if (this.size !== 0 && is(this._value, searchValue)) {
2197822150
return 0;
2197922151
}
2198022152
return -1;
2198122153
};
2198222154

2198322155
Repeat.prototype.lastIndexOf = function lastIndexOf (searchValue) {
21984-
if (is(this._value, searchValue)) {
21985-
return this.size;
22156+
if (this.size !== 0 && is(this._value, searchValue)) {
22157+
return this.size - 1;
2198622158
}
2198722159
return -1;
2198822160
};
@@ -22063,7 +22235,7 @@ const UnitTables = exports.UnitTables = {
2206322235
return isIndexed(v) ? v.toList() : isKeyed(v) ? v.toMap() : v.toSet();
2206422236
}
2206522237

22066-
var version = "5.1.6";
22238+
var version = "5.1.9";
2206722239

2206822240
/* eslint-disable import/order */
2206922241

0 commit comments

Comments
 (0)