UNPKG

mute-structs

Version:

NodeJS module providing an implementation of the LogootSplit CRDT algorithm

97 lines (93 loc) 3.91 kB
/* 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/>. */ import { Identifier } from "./identifier"; import { IdentifierTuple } from "./identifiertuple"; import { INT32_BOTTOM, INT32_TOP, isInt32, randomInt32, } from "./int32"; export const INT32_BOTTOM_USER = INT32_BOTTOM + 1; export const INT32_TOP_USER = INT32_TOP - 1; export const MIN_TUPLE = new IdentifierTuple(INT32_BOTTOM, 0, 0, 0); export const MIN_TUPLE_USER = new IdentifierTuple(INT32_BOTTOM_USER, 0, 0, 0); export const MAX_TUPLE_USER = new IdentifierTuple(INT32_TOP_USER, 0, 0, 0); export const MAX_TUPLE = new IdentifierTuple(INT32_TOP, 0, 0, 0); export function createBetweenPosition(id1, id2, replicaNumber, clock) { console.assert(id1 === null || id2 === null || id1.compareTo(id2) === -1 /* Less */, "id1 < id2"); console.assert(isInt32(replicaNumber), "replicaNumber is an int32"); console.assert(isInt32(clock), "clock is an int32"); const seq1 = infiniteSequence(tuplesOf(id1), MIN_TUPLE_USER); const seq2 = infiniteSequence(tuplesOf(id2), MAX_TUPLE_USER); const tuples = []; let tuple1 = seq1.next().value; let tuple2 = seq2.next().value; while (tuple1.compareTo(tuple2) === 0 /* Equal */) { // Cannot insert a new tuple between tuple1 and tuple2 tuples.push(tuple1); tuple1 = seq1.next().value; tuple2 = seq2.next().value; } if (tuple1.random === INT32_BOTTOM && tuple2.random === INT32_BOTTOM_USER) { // Special case to avoid problematic scenarios with renaming mechanism tuples.push(tuple2); while (tuple1.compareTo(MIN_TUPLE_USER) !== 0 /* Equal */) { tuple1 = seq1.next().value; } tuple2 = seq2.next().value; } else if (tuple2.random - tuple1.random <= 1) { tuples.push(tuple1); tuple1 = seq1.next().value; while (tuple2.compareTo(MAX_TUPLE_USER) !== 0 /* Equal */) { tuple2 = seq2.next().value; } } if (tuple1.random + 1 === INT32_TOP_USER) { tuples.push(tuple1); tuple1 = seq1.next().value; } const random = randomInt32(tuple1.random + 1, tuple2.random); // random ∈ ]tuple1.random, tuple2.random[ // tuple1.random exclusion ensures a dense set // tuple2.random exclusion ensures that newTuple < tuple2 // and thus that newId < id2 tuples.push(new IdentifierTuple(random, replicaNumber, clock, 0)); return new Identifier(tuples); } export function createAtPosition(replicaNumber, clock, position, offset) { console.assert([position, replicaNumber, clock, offset].every(isInt32), "each value ∈ int32"); const tuple = new IdentifierTuple(position, replicaNumber, clock, offset); return new Identifier([tuple]); } /** * Generate an infinite sequence of tuples * * @param values * @param defaultValue */ function* infiniteSequence(values, defaultValue) { for (const v of values) { yield v; } while (true) { yield defaultValue; } } /** * @param id * @return Tuples of `a' or an empty array if none. */ function tuplesOf(id) { return (id !== null) ? id.tuples : []; } //# sourceMappingURL=idfactory.js.map