UNPKG

pessimism

Version:

A fast HAMT Map intended for KV caching and optimistic updates

654 lines (622 loc) 17.1 kB
'use strict'; var Block = require("bs-platform/lib/js/block.js"); var Caml_int32 = require("bs-platform/lib/js/caml_int32.js"); var Caml_option = require("bs-platform/lib/js/caml_option.js"); var Js_undefined = require("bs-platform/lib/js/js_undefined.js"); var anon = /* record */[/* contents : () */0]; function isOwner(a, b) { if (a !== anon) { return a === b; } else { return false; } } function mask(x, pos) { return (1 << ((x >>> Caml_int32.imul(pos, 5)) & 31)); } function hash(x) { var until = x.length - 1 | 0; var h = 5381; for(var i = 0; i <= until; ++i){ h = ((h << 5) + h | 0) + x.charCodeAt(i) | 0; } var x$1 = h; return (x$1 >>> 1) & 1073741824 | x$1 & -1073741825; } function indexBit(x, pos) { var input = x & (pos - 1 | 0); var x$1 = input; x$1 = x$1 - ((x$1 >> 1) & 1431655765) | 0; x$1 = (x$1 & 858993459) + ((x$1 >> 2) & 858993459) | 0; x$1 = x$1 + (x$1 >> 4) & 252645135; x$1 = x$1 + (x$1 >> 8) | 0; x$1 = x$1 + (x$1 >> 16) | 0; return x$1 & 127; } function copyIndex(index, owner) { var match = !isOwner(owner, index[/* owner */2]); if (match) { return /* record */[ /* bitmap */index[/* bitmap */0], /* contents */index[/* contents */1].slice(), /* owner */owner ]; } else { return index; } } function traverseCopy(map, code, owner) { var _index = map; var _depth = 0; while(true) { var depth = _depth; var index = _index; var bitmap = index[/* bitmap */0]; var contents = index[/* contents */1]; var pos = mask(code, depth); if ((bitmap | pos) !== bitmap) { return /* tuple */[ depth, index ]; } else { var i = indexBit(bitmap, pos); var child = contents[i]; if (child.tag) { return /* tuple */[ depth, index ]; } else { var newChildIndex = copyIndex(child[0], owner); contents[i] = /* Index */Block.__(0, [newChildIndex]); _depth = depth + 1 | 0; _index = newChildIndex; continue ; } } }; } function resolveConflict(codeA, codeB, nodeA, nodeB, depth, owner) { var posA = mask(codeA, depth); var posB = mask(codeB, depth); var bitmap = posA | posB; var match = posA === posB; var contents; if (match) { contents = /* array */[resolveConflict(codeA, codeB, nodeA, nodeB, depth + 1 | 0, owner)]; } else { var match$1 = indexBit(bitmap, posA) !== 0; contents = match$1 ? /* array */[ nodeB, nodeA ] : /* array */[ nodeA, nodeB ]; } return /* Index */Block.__(0, [/* record */[ /* bitmap */bitmap, /* contents */contents, /* owner */owner ]]); } function removeFromIndex(index, pos, owner) { var bitmap = index[/* bitmap */0]; var newBitmap = bitmap ^ pos; if (newBitmap !== bitmap) { var index$1 = copyIndex(index, owner); index$1[/* contents */1].splice(indexBit(bitmap, pos), 1); index$1[/* bitmap */0] = newBitmap; return index$1; } else { return index; } } function clearBox(_box, optid) { while(true) { var box = _box; var match = box[/* prev */3]; var id = box[/* id */2]; if (match !== undefined) { var prev = match; if (id === optid) { _box = prev; continue ; } else { return /* record */[ /* key */box[/* key */0], /* value */box[/* value */1], /* id */box[/* id */2], /* prev */clearBox(prev, optid) ]; } } else if (id === optid) { return undefined; } else { return box; } }; } function clearOptimisticNode(node, optid) { switch (node.tag | 0) { case 2 : var box = node[0]; if (box[/* id */2] === 0) { return node; } else { var code = node[1]; var match = clearBox(box, optid); if (match !== undefined) { return /* Values */Block.__(2, [ match, code ]); } else { return /* Empty */Block.__(4, [code]); } } case 3 : var code$1 = node[1]; var bucket = node[0]; var bucketSize = bucket.length; var newBucket = /* array */[]; var i = 0; while(i < bucketSize) { var box$1 = bucket[i]; if (box$1[/* id */2] === 0) { newBucket.push(box$1); } else { var match$1 = clearBox(box$1, optid); if (match$1 !== undefined) { newBucket.push(match$1); } } i = i + 1 | 0; }; var match$2 = newBucket.length > 0; if (match$2) { return /* Collision */Block.__(3, [ newBucket, code$1 ]); } else { return /* Empty */Block.__(4, [code$1]); } default: return node; } } function addToBucket(bucket, box) { var bucket$1 = bucket.slice(); var bucketSize = bucket$1.length; var optimistic = box[/* id */2] !== 0; var i = 0; var hasReplaced = false; while(i < bucketSize && !hasReplaced) { var prev = bucket$1[i]; if (prev[/* key */0] === box[/* key */0]) { if (optimistic) { box[/* prev */3] = prev; } bucket$1[i] = box; hasReplaced = true; } i = i + 1 | 0; }; if (!hasReplaced) { bucket$1.push(box); } return bucket$1; } function removeFromBucket(bucket, key) { var bucketSize = bucket.length; var newBucket = /* array */[]; var i = 0; while(i < bucketSize) { var box = bucket[i]; if (box[/* key */0] !== key) { newBucket.push(box); } i = i + 1 | 0; }; return newBucket; } function rebuildWithStack(_stack, _innerIndex, _depth, code, owner) { while(true) { var depth = _depth; var innerIndex = _innerIndex; var stack = _stack; var pos = mask(code, depth); if (stack) { var rest = stack[1]; var index = stack[0]; if (innerIndex[/* bitmap */0] === 0) { var index$1 = removeFromIndex(index, pos, owner); _depth = depth - 1 | 0; _innerIndex = index$1; _stack = rest; continue ; } else { var index$2 = copyIndex(index, owner); var i = indexBit(index$2[/* bitmap */0], pos); index$2[/* contents */1][i] = /* Index */Block.__(0, [innerIndex]); _depth = depth - 1 | 0; _innerIndex = index$2; _stack = rest; continue ; } } else { return innerIndex; } }; } function make(param) { return /* record */[ /* bitmap */0, /* contents : array */[], /* owner */anon ]; } function asMutable(index) { var match = index[/* owner */2] === anon; if (match) { return /* record */[ /* bitmap */index[/* bitmap */0], /* contents */index[/* contents */1], /* owner : record */[/* contents : () */0] ]; } else { return index; } } function asImmutable(index) { index[/* owner */2] = anon; return index; } function getUndefined(map, key) { var code = hash(key); var _index = map; var _depth = 0; while(true) { var depth = _depth; var index = _index; var bitmap = index[/* bitmap */0]; var contents = index[/* contents */1]; var pos = mask(code, depth); if ((bitmap | pos) !== bitmap) { return undefined; } else { var child = contents[indexBit(bitmap, pos)]; switch (child.tag | 0) { case 0 : _depth = depth + 1 | 0; _index = child[0]; continue ; case 1 : if (child[0] === key) { return child[1]; } else { return undefined; } case 2 : var box = child[0]; if (box[/* key */0] === key) { return box[/* value */1]; } else { return undefined; } case 3 : var bucket = child[0]; var key$1 = key; var bucketSize = bucket.length; var res = undefined; var i = 0; while(Js_undefined.testAny(res) && i < bucketSize) { var box$1 = bucket[i]; if (box$1[/* key */0] === key$1) { res = box$1[/* value */1]; } i = i + 1 | 0; }; return res; case 4 : return undefined; } } }; } function get(map, k) { return Caml_option.undefined_to_opt(getUndefined(map, k)); } function setOptimistic(map, key, value, id) { var owner = map[/* owner */2]; var map$1 = copyIndex(map, owner); var code = hash(key); var match = traverseCopy(map$1, code, owner); var index = match[1]; var depth = match[0]; var optimistic = id !== 0; var pos = mask(code, depth); var newBitmap = index[/* bitmap */0] | pos; var i = indexBit(newBitmap, pos); if (newBitmap !== index[/* bitmap */0]) { index[/* contents */1].splice(i, 0, /* Value */Block.__(1, [ key, value, code ])); index[/* bitmap */0] = newBitmap; } else { var prev = index[/* contents */1][i]; var node; var exit = 0; var prevCode; var prev$1; switch (prev.tag | 0) { case 0 : node = prev; break; case 1 : var c = prev[2]; var v = prev[1]; var k = prev[0]; if (k === key && optimistic) { var prev$2 = /* record */[ /* key */k, /* value */v, /* id */0, /* prev */undefined ]; var next = /* record */[ /* key */key, /* value */value, /* id */id, /* prev */prev$2 ]; node = /* Values */Block.__(2, [ next, code ]); } else if (k === key) { node = /* Value */Block.__(1, [ key, value, code ]); } else if (c === code) { var prev$3 = /* record */[ /* key */k, /* value */v, /* id */0, /* prev */undefined ]; var next$1 = /* record */[ /* key */key, /* value */value, /* id */0, /* prev */undefined ]; node = /* Collision */Block.__(3, [ /* array */[ prev$3, next$1 ], code ]); } else { prevCode = c; prev$1 = prev; exit = 1; } break; case 2 : var box = prev[0]; if (box[/* key */0] === key && optimistic) { node = /* Values */Block.__(2, [ /* record */[ /* key */key, /* value */value, /* id */id, /* prev */box ], code ]); } else if (box[/* key */0] === key) { node = /* Values */Block.__(2, [ /* record */[ /* key */key, /* value */value, /* id */0, /* prev */undefined ], code ]); } else { var c$1 = prev[1]; if (c$1 === code) { var next$2 = /* record */[ /* key */key, /* value */value, /* id */0, /* prev */undefined ]; node = /* Collision */Block.__(3, [ /* array */[ box, next$2 ], code ]); } else { prevCode = c$1; prev$1 = prev; exit = 1; } } break; case 3 : var c$2 = prev[1]; if (c$2 === code) { var box$1 = /* record */[ /* key */key, /* value */value, /* id */id, /* prev */undefined ]; node = /* Collision */Block.__(3, [ addToBucket(prev[0], box$1), code ]); } else { prevCode = c$2; prev$1 = prev; exit = 1; } break; case 4 : node = optimistic ? /* Values */Block.__(2, [ /* record */[ /* key */key, /* value */value, /* id */id, /* prev */undefined ], code ]) : /* Value */Block.__(1, [ key, value, code ]); break; } if (exit === 1) { var next$3 = optimistic ? /* Values */Block.__(2, [ /* record */[ /* key */key, /* value */value, /* id */id, /* prev */undefined ], code ]) : /* Value */Block.__(1, [ key, value, code ]); node = resolveConflict(prevCode, code, prev$1, next$3, depth + 1 | 0, owner); } index[/* contents */1][i] = node; } return map$1; } function set(map, k, v) { return setOptimistic(map, k, v, 0); } function remove(map, key) { var owner = map[/* owner */2]; var code = hash(key); var _stack = /* [] */0; var _index = map; var _depth = 0; while(true) { var depth = _depth; var index = _index; var stack = _stack; var bitmap = index[/* bitmap */0]; var contents = index[/* contents */1]; var pos = mask(code, depth); if ((bitmap | pos) !== bitmap) { return map; } else { var i = indexBit(bitmap, pos); var child = contents[i]; var exit = 0; var k; switch (child.tag | 0) { case 0 : _depth = depth + 1 | 0; _index = child[0]; _stack = /* :: */[ index, stack ]; continue ; case 1 : k = child[0]; exit = 1; break; case 2 : k = child[0][/* key */0]; exit = 1; break; case 3 : if (child[1] === code) { var bucket = removeFromBucket(child[0], key); if (bucket.length === 0) { var index$1 = removeFromIndex(index, pos, owner); return rebuildWithStack(stack, index$1, depth - 1 | 0, code, owner); } else { var index$2 = copyIndex(index, owner); index$2[/* contents */1][i] = /* Collision */Block.__(3, [ bucket, code ]); return rebuildWithStack(stack, index$2, depth - 1 | 0, code, owner); } } else { return map; } case 4 : return map; } if (exit === 1) { if (k === key) { var index$3 = removeFromIndex(index, pos, owner); return rebuildWithStack(stack, index$3, depth - 1 | 0, code, owner); } else { return map; } } } }; } function clearOptimistic(map, optid) { var owner = map[/* owner */2]; var index = copyIndex(map, owner); for(var x = 0; x <= 31; ++x){ var pos = (1 << x); if ((pos & index[/* bitmap */0]) !== 0) { var i = indexBit(index[/* bitmap */0], pos); var node = clearOptimisticNode(index[/* contents */1][i], optid); switch (node.tag | 0) { case 0 : var newInnerIndex = clearOptimistic(node[0], optid); if (newInnerIndex[/* bitmap */0] === 0) { index[/* contents */1].splice(i, 1); index[/* bitmap */0] = index[/* bitmap */0] ^ pos; } else { index[/* contents */1][i] = /* Index */Block.__(0, [newInnerIndex]); } break; case 4 : index[/* contents */1].splice(i, 1); index[/* bitmap */0] = index[/* bitmap */0] ^ pos; break; default: index[/* contents */1][i] = node; } } } return index; } exports.make = make; exports.asMutable = asMutable; exports.asImmutable = asImmutable; exports.get = get; exports.getUndefined = getUndefined; exports.remove = remove; exports.set = set; exports.setOptimistic = setOptimistic; exports.clearOptimistic = clearOptimistic; /* No side effect */