mute-structs
Version:
NodeJS module providing an implementation of the LogootSplit CRDT algorithm
222 lines (218 loc) • 10.5 kB
JavaScript
/*
This file is part of MUTE-structs.
Copyright (C) 2017 Matthieu Nicolas, Victorien Elvinger
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
import { isObject } from "./data-validation";
import { Epoch } from "./epoch/epoch";
import { EpochId } from "./epoch/epochid";
import { compareEpochFullIds, EpochStore } from "./epoch/epochstore";
import { IdentifierInterval } from "./identifierinterval";
import { createAtPosition } from "./idfactory";
import { LogootSRopes } from "./logootsropes";
import { LogootSDel } from "./operations/delete/logootsdel";
import { RenamableLogootSDel } from "./operations/delete/renamablelogootsdel";
import { LogootSAdd } from "./operations/insert/logootsadd";
import { RenamableLogootSAdd } from "./operations/insert/renamablelogootsadd";
import { LogootSRename } from "./operations/rename/logootsrename";
import { RenamingMap } from "./renamingmap/renamingmap";
import { RenamingMapStore } from "./renamingmap/renamingmapstore";
import { mkNodeAt } from "./ropesnodes";
function generateInsertOps(idIntervals, str) {
var currentOffset = 0;
return idIntervals
.map(function (idInterval) {
var nextOffset = currentOffset + idInterval.length;
var content = str.slice(currentOffset, nextOffset);
currentOffset = nextOffset;
return new LogootSAdd(idInterval.idBegin, content);
});
}
var RenamableReplicableList = /** @class */ (function () {
function RenamableReplicableList(list, currentEpoch, epochsStore, renamingMapStore) {
this.list = list;
this.currentEpoch = currentEpoch;
this.epochsStore = epochsStore;
this.renamingMapStore = renamingMapStore;
}
RenamableReplicableList.create = function (replicaNumber, clock) {
if (replicaNumber === void 0) { replicaNumber = 0; }
if (clock === void 0) { clock = 0; }
var list = new LogootSRopes(replicaNumber, clock);
var currentEpoch = new Epoch(new EpochId(0, 0));
var epochsStore = new EpochStore(currentEpoch);
var renamingMapStore = new RenamingMapStore();
return new RenamableReplicableList(list, currentEpoch, epochsStore, renamingMapStore);
};
RenamableReplicableList.fromPlain = function (o) {
if (isObject(o)) {
var list = LogootSRopes.fromPlain(o.list);
var epochsStore = EpochStore.fromPlain(o.epochsStore);
var renamingMapStore = RenamingMapStore.fromPlain(o.renamingMapStore);
var currentEpoch = Epoch.fromPlain(o.currentEpoch);
if (list !== null && epochsStore !== null &&
renamingMapStore !== null && currentEpoch !== null) {
return new RenamableReplicableList(list, currentEpoch, epochsStore, renamingMapStore);
}
}
return null;
};
RenamableReplicableList.fromPlainLogootSRopes = function (o) {
var list = LogootSRopes.fromPlain(o);
if (list !== null) {
var currentEpoch = new Epoch(new EpochId(0, 0));
var epochsStore = new EpochStore(currentEpoch);
var renamingMapStore = new RenamingMapStore();
return new RenamableReplicableList(list, currentEpoch, epochsStore, renamingMapStore);
}
return null;
};
Object.defineProperty(RenamableReplicableList.prototype, "replicaNumber", {
get: function () {
return this.list.replicaNumber;
},
enumerable: true,
configurable: true
});
Object.defineProperty(RenamableReplicableList.prototype, "clock", {
get: function () {
return this.list.clock;
},
enumerable: true,
configurable: true
});
Object.defineProperty(RenamableReplicableList.prototype, "currentRenamingMap", {
get: function () {
return this.renamingMapStore.getRenamingMap(this.currentEpoch.id);
},
enumerable: true,
configurable: true
});
RenamableReplicableList.prototype.getList = function () {
return this.list;
};
RenamableReplicableList.prototype.getCurrentEpoch = function () {
return this.currentEpoch;
};
Object.defineProperty(RenamableReplicableList.prototype, "str", {
get: function () {
return this.list.str;
},
enumerable: true,
configurable: true
});
RenamableReplicableList.prototype.insertLocal = function (pos, l) {
return new RenamableLogootSAdd(this.list.insertLocal(pos, l), this.currentEpoch);
};
RenamableReplicableList.prototype.insertRemote = function (epoch, op) {
var _this = this;
if (!epoch.equals(this.currentEpoch)) {
var strat = function (rmap, ids) { return rmap.initRenameIds(ids); };
var newIds = this.renameFromEpochToCurrent(op.insertedIds, epoch, strat);
var newIdIntervals = IdentifierInterval.mergeIdsIntoIntervals(newIds);
var insertOps = generateInsertOps(newIdIntervals, op.content);
return insertOps
.flatMap(function (insertOp) { return insertOp.execute(_this.list); });
}
return op.execute(this.list);
};
RenamableReplicableList.prototype.delLocal = function (begin, end) {
return new RenamableLogootSDel(this.list.delLocal(begin, end), this.currentEpoch);
};
RenamableReplicableList.prototype.delRemote = function (epoch, op) {
if (!epoch.equals(this.currentEpoch)) {
var idsToRename = op.lid
.flatMap(function (idInterval) { return idInterval.toIds(); });
var strat = function (rmap, ids) { return rmap.initRenameIds(ids); };
var newIds = this.renameFromEpochToCurrent(idsToRename, epoch, strat);
var newIdIntervals = IdentifierInterval.mergeIdsIntoIntervals(newIds);
var newOp = new LogootSDel(newIdIntervals, op.author);
return newOp.execute(this.list);
}
return op.execute(this.list);
};
RenamableReplicableList.prototype.renameLocal = function () {
var renamedIdIntervals = this.list.toList();
var clock = this.clock;
var newEpochNumber = this.currentEpoch.id.epochNumber + 1;
var newEpochId = new EpochId(this.replicaNumber, newEpochNumber);
this.currentEpoch = new Epoch(newEpochId, this.currentEpoch.id);
this.epochsStore.addEpoch(this.currentEpoch);
var newRandom = renamedIdIntervals[0].idBegin.tuples[0].random;
var renamingMap = new RenamingMap(this.replicaNumber, clock, renamedIdIntervals);
this.renamingMapStore.add(this.currentEpoch, renamingMap);
var baseId = createAtPosition(this.replicaNumber, clock, newRandom, 0);
var newRoot = mkNodeAt(baseId, this.str.length);
this.list = new LogootSRopes(this.replicaNumber, clock + 1, newRoot, this.str);
return new LogootSRename(this.replicaNumber, clock, this.currentEpoch, renamedIdIntervals);
};
RenamableReplicableList.prototype.renameRemote = function (replicaNumber, clock, newEpoch, renamedIdIntervals) {
var renamingMap = new RenamingMap(replicaNumber, clock, renamedIdIntervals);
this.epochsStore.addEpoch(newEpoch);
this.renamingMapStore.add(newEpoch, renamingMap);
var newEpochFullId = this.epochsStore.getEpochFullId(newEpoch);
var currentEpochFullId = this.epochsStore.getEpochFullId(this.currentEpoch);
if (compareEpochFullIds(currentEpochFullId, newEpochFullId) === -1 /* Less */) {
var previousEpoch = this.currentEpoch;
this.currentEpoch = newEpoch;
var idsToRename = this.list.toList().flatMap(function (idInterval) { return idInterval.toIds(); });
var strat = function (rmap, ids) { return rmap.initRenameSeq(ids); };
var newIds = this.renameFromEpochToCurrent(idsToRename, previousEpoch, strat);
var newIdIntervals = IdentifierInterval.mergeIdsIntoIntervals(newIds);
var newList_1 = new LogootSRopes(this.replicaNumber, this.clock);
var insertOps = generateInsertOps(newIdIntervals, this.str);
insertOps.forEach(function (insertOp) {
insertOp.execute(newList_1);
});
this.list = newList_1;
}
};
RenamableReplicableList.prototype.renameFromEpochToCurrent = function (idsToRename, fromEpoch, strat) {
var _this = this;
var _a = __read(this.epochsStore.getPathBetweenEpochs(fromEpoch, this.currentEpoch), 2), epochsToRevert = _a[0], epochsToApply = _a[1];
var ids = idsToRename;
epochsToRevert.forEach(function (epoch) {
var rmap = _this.renamingMapStore.getRenamingMap(epoch.id);
ids = ids.map(function (id) { return rmap.reverseRenameId(id); });
});
epochsToApply.forEach(function (epoch) {
var rmap = _this.renamingMapStore.getRenamingMap(epoch.id);
ids = strat(rmap, ids);
});
return ids;
};
RenamableReplicableList.prototype.getNbBlocks = function () {
return this.list.toList().length;
};
RenamableReplicableList.prototype.digest = function () {
return this.list.digest();
};
return RenamableReplicableList;
}());
export { RenamableReplicableList };
//# sourceMappingURL=renamablereplicablelist.js.map