pessimism
Version:
A fast HAMT Map intended for KV caching and optimistic updates
654 lines (622 loc) • 17.1 kB
JavaScript
'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 */