├── public
├── js
│ └── .gitkeep
└── assets
│ ├── img
│ ├── call.png
│ ├── shhh.ico
│ ├── favicon.ico
│ ├── myLipsAreSealed.ico
│ └── conclave-mask-small.ico
│ └── fonts
│ ├── InputSans-Light.ttf
│ └── InputSans-Medium.ttf
├── Procfile
├── performance
├── logs
│ └── .gitkeep
├── utilLinear.js
├── comparisons
│ └── linearArray
│ │ ├── tripleBase
│ │ ├── 2017-11-17 5:41:7.log
│ │ ├── 2017-11-17 5:40:56.log
│ │ └── 2017-11-17 1:44:28.log
│ │ ├── doubleBase
│ │ ├── 2017-11-17 5:39:6.log
│ │ ├── 2017-11-17 5:38:54.log
│ │ └── 2017-11-17 1:29:26.log
│ │ └── constantBase
│ │ ├── 2017-11-17 2:26:13.log
│ │ └── 2017-11-17 5:19:28.log
├── scriptLinear.js
├── script.js
└── util.js
├── _config.yml
├── .dockerignore
├── .babelrc
├── .gitignore
├── Dockerfile
├── views
├── bots.pug
├── idGraph.pug
├── timeGraph.pug
├── arraysGraph.pug
├── about.pug
├── index.pug
└── layout.pug
├── Makefile
├── spec
├── support
│ └── jasmine.json
├── sortedArraySpec.js
├── versionSpec.js
├── identifierSpec.js
├── editorSpec.js
├── charSpec.js
└── versionVectorSpec.js
├── lib
├── sortedArray.js
├── identifier.js
├── hashAlgo.js
├── char.js
├── main.js
├── version.js
├── remoteCursor.js
├── demo.js
├── versionVector.js
├── idGraph.js
├── userBot.js
├── timeGraph.js
├── cursorNames.js
├── arraysGraph.js
├── utilLinear.js
├── crdtLinear.js
├── cssColors.js
├── broadcast.js
├── util.js
└── editor.js
├── webpack.config.js
├── app.js
├── LICENSE.txt
├── package.json
└── README.md
/public/js/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: npm start
2 |
--------------------------------------------------------------------------------
/performance/logs/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "stage-3"],
3 | "plugins": []
4 | }
5 |
--------------------------------------------------------------------------------
/public/assets/img/call.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/conclave-team/conclave/HEAD/public/assets/img/call.png
--------------------------------------------------------------------------------
/public/assets/img/shhh.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/conclave-team/conclave/HEAD/public/assets/img/shhh.ico
--------------------------------------------------------------------------------
/public/assets/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/conclave-team/conclave/HEAD/public/assets/img/favicon.ico
--------------------------------------------------------------------------------
/public/assets/img/myLipsAreSealed.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/conclave-team/conclave/HEAD/public/assets/img/myLipsAreSealed.ico
--------------------------------------------------------------------------------
/public/assets/fonts/InputSans-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/conclave-team/conclave/HEAD/public/assets/fonts/InputSans-Light.ttf
--------------------------------------------------------------------------------
/public/assets/fonts/InputSans-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/conclave-team/conclave/HEAD/public/assets/fonts/InputSans-Medium.ttf
--------------------------------------------------------------------------------
/public/assets/img/conclave-mask-small.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/conclave-team/conclave/HEAD/public/assets/img/conclave-mask-small.ico
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /public/js/*.js
3 | /public/js/*.txt
4 | /build
5 | debug.js
6 | .DS_Store
7 | /performance/logs/*
8 | !/performance/logs/.gitkeep
9 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:14
2 |
3 | WORKDIR /usr/src/app
4 | COPY package*.json ./
5 | RUN npm install
6 |
7 | COPY . .
8 |
9 | RUN npm run build
10 |
11 | EXPOSE 3000
12 |
13 |
--------------------------------------------------------------------------------
/views/bots.pug:
--------------------------------------------------------------------------------
1 | extends layout.pug
2 |
3 | block link
4 | p.disappear Sharing Link:
5 | a.link(id='myLink' target="_blank")
6 |
7 | block scripts
8 | script(src='js/bots.js')
9 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | build-image:
2 | docker build -t conclave .
3 |
4 | run-local: build-image server
5 |
6 | server:
7 | docker run --rm -p 3000:3000 -e DEBUG=express:* conclave npm start
8 |
9 |
--------------------------------------------------------------------------------
/spec/support/jasmine.json:
--------------------------------------------------------------------------------
1 | {
2 | "spec_dir": "spec",
3 | "spec_files": [
4 | "**/*[sS]pec.js"
5 | ],
6 | "helpers": [
7 | "helpers/**/*.js",
8 | "../node_modules/babel-register/lib/node.js"
9 | ],
10 | "stopSpecOnExpectationFailure": false,
11 | "random": false
12 | }
13 |
--------------------------------------------------------------------------------
/views/idGraph.pug:
--------------------------------------------------------------------------------
1 | doctype html
2 | html(lang="en")
3 | head
4 | title='Linear Array CRDT Performance'
5 | script(src="https://cdn.plot.ly/plotly-latest.min.js")
6 | body
7 | div(id='g0')
8 | div(id='g1')
9 | div(id='g2')
10 | div(id='g3')
11 | script(src='js/idGraph.js')
12 |
--------------------------------------------------------------------------------
/views/timeGraph.pug:
--------------------------------------------------------------------------------
1 | doctype html
2 | html(lang="en")
3 | head
4 | title='Linear Array CRDT Performance'
5 | script(src="https://cdn.plot.ly/plotly-latest.min.js")
6 | body
7 | div(id='g0')
8 | div(id='g1')
9 | div(id='g2')
10 | div(id='g3')
11 | script(src='js/timegraph.js')
12 |
--------------------------------------------------------------------------------
/views/arraysGraph.pug:
--------------------------------------------------------------------------------
1 | doctype html
2 | html(lang="en")
3 | head
4 | title='Linear Array versus Array-of-Arrays CRDT Performance'
5 | script(src="https://cdn.plot.ly/plotly-latest.min.js")
6 | body
7 | div(id='g0')
8 | div(id='g1')
9 | div(id='g2')
10 | div(id='g3')
11 | script(src='js/arraysGraph.js')
12 |
--------------------------------------------------------------------------------
/lib/sortedArray.js:
--------------------------------------------------------------------------------
1 | import sorted from 'sorted-cmp-array';
2 |
3 | // Extending SortedArray functionality from 'sorted-cmp-array'.
4 | // Adding a 'get' method for retrieving elements.
5 | class SortedArray extends sorted {
6 | constructor(compareFn) {
7 | super(compareFn);
8 | }
9 |
10 | get(idx) {
11 | return this.arr[idx];
12 | }
13 | }
14 |
15 | export default SortedArray;
16 |
--------------------------------------------------------------------------------
/spec/sortedArraySpec.js:
--------------------------------------------------------------------------------
1 | import SortedArray from '../lib/sortedArray';
2 |
3 | describe('SortedArray', () => {
4 | describe('get', () => {
5 | it('returns the element at the specified index', () => {
6 | const sortedArray = new SortedArray((a, b) => a - b);
7 | sortedArray.insert(2);
8 | sortedArray.insert(1);
9 |
10 | expect(sortedArray.get(0)).toBe(1);
11 | });
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | var glob = require("glob");
3 |
4 | module.exports = {
5 | context: path.resolve(__dirname, 'lib'),
6 | entry: {
7 | main: './main.js',
8 | demo: './demo.js',
9 | bots: './userBot.js',
10 | timegraph: './timeGraph.js',
11 | arraysGraph: './arraysGraph.js',
12 | idGraph: './idGraph.js'
13 | },
14 | output: {
15 | filename: '[name].js',
16 | path: path.resolve(__dirname, 'public/js'),
17 | },
18 | };
--------------------------------------------------------------------------------
/lib/identifier.js:
--------------------------------------------------------------------------------
1 | class Identifier {
2 | constructor(digit, siteId) {
3 | this.digit = digit;
4 | this.siteId = siteId;
5 | }
6 |
7 | // Compare identifiers using their digit value with siteID as the tiebreaker
8 | // If identifers are equal, return 0
9 | compareTo(otherId) {
10 | if (this.digit < otherId.digit) {
11 | return -1;
12 | } else if (this.digit > otherId.digit) {
13 | return 1;
14 | } else {
15 | if (this.siteId < otherId.siteId) {
16 | return -1;
17 | } else if (this.siteId > otherId.siteId) {
18 | return 1;
19 | } else {
20 | return 0;
21 | }
22 | }
23 | }
24 | }
25 |
26 | export default Identifier;
27 |
--------------------------------------------------------------------------------
/lib/hashAlgo.js:
--------------------------------------------------------------------------------
1 | function hashAlgo(input, collection) {
2 | // const alphabet = 'abcdefghijklmnopqrstuvwxyz';
3 | // const filteredInputArray = input.toLowerCase().replace(/[a-z\-]/g, '').split('');
4 | // const sum = filteredInputArray.reduce((acc, num) => acc + Number(num), 0);
5 |
6 | // return Math.floor((sum * 13) / 7) % collection.length;
7 |
8 | const justNums = input.toLowerCase().replace(/[a-z\-]/g, '');
9 | return Math.floor(justNums * 13) % collection.length
10 | }
11 |
12 | function generateItemFromHash(siteId, collection) {
13 | const hashIdx = hashAlgo(siteId, collection);
14 |
15 | return collection[hashIdx];
16 | }
17 |
18 | export {
19 | hashAlgo,
20 | generateItemFromHash
21 | }
22 |
--------------------------------------------------------------------------------
/lib/char.js:
--------------------------------------------------------------------------------
1 | class Char {
2 | constructor(value, counter, siteId, identifiers) {
3 | this.position = identifiers;
4 | this.counter = counter;
5 | this.siteId = siteId;
6 | this.value = value;
7 | }
8 |
9 | compareTo(otherChar) {
10 | let comp, id1, id2;
11 | const pos1 = this.position;
12 | const pos2 = otherChar.position;
13 |
14 | for (let i = 0; i < Math.min(pos1.length, pos2.length); i++) {
15 | id1 = pos1[i];
16 | id2 = pos2[i];
17 | comp = id1.compareTo(id2);
18 |
19 | if (comp !== 0) {
20 | return comp;
21 | }
22 | }
23 |
24 | if (pos1.length < pos2.length) {
25 | return -1;
26 | } else if (pos1.length > pos2.length) {
27 | return 1;
28 | } else {
29 | return 0;
30 | }
31 | }
32 | }
33 |
34 | export default Char;
35 |
--------------------------------------------------------------------------------
/lib/main.js:
--------------------------------------------------------------------------------
1 | import Peer from 'peerjs';
2 | import SimpleMDE from 'simplemde';
3 |
4 | import Controller from './controller';
5 | import Broadcast from './broadcast';
6 | import Editor from './editor';
7 |
8 | if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
9 |
10 | } else {
11 | new Controller(
12 | (location.search.slice(1) || '0'),
13 | location.origin,
14 | new Peer({
15 | debug: 3
16 | }),
17 | new Broadcast(),
18 | new Editor(new SimpleMDE({
19 | placeholder: "Share the link to invite collaborators to your document.",
20 | spellChecker: false,
21 | toolbar: false,
22 | autofocus: false,
23 | indentWithTabs: true,
24 | tabSize: 4,
25 | indentUnit: 4,
26 | lineWrapping: false,
27 | shortCuts: []
28 | }))
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/views/about.pug:
--------------------------------------------------------------------------------
1 | extends layout.pug
2 |
3 | block nav
4 | .start
5 | a.link(href='/') New Document
6 | .case-study
7 | a.link(href='https://conclave-team.github.io/conclave-site/' target="_blank") Case Study
8 | .team
9 | a.link(href='https://conclave-team.github.io/conclave-site/team/' target="_blank") Our Team
10 | .github
11 | a.link(href='https://github.com/conclave-team/conclave' target="_blank") Fork on GitHub
12 | block link
13 | p.sharing-link.disappear
14 | a.link(id='myLink' target="_blank") Sharing Link
15 | span(id="myLinkInput" class="aside disappear")
16 | span(class="copy-container" data-tooltip="Copy to Clipboard")
17 | i(data-feather="copy" class="copy-link" color="rgb(17, 117, 232)")
18 | span(class="copy-status") Copied!
19 |
20 | block scripts
21 | script(src='js/demo.js')
22 |
--------------------------------------------------------------------------------
/views/index.pug:
--------------------------------------------------------------------------------
1 | extends layout.pug
2 |
3 | block nav
4 | .about
5 | a.link(href='/about' target="_blank") What is Conclave?
6 | .case-study
7 | a.link(href='https://conclave-team.github.io/conclave-site/' target="_blank") Case Study
8 | .team
9 | a.link(href='https://conclave-team.github.io/conclave-site/team/' target="_blank") Our Team
10 | .github
11 | a.link(href='https://github.com/conclave-team/conclave' target="_blank") Fork on GitHub
12 |
13 | block link
14 | p.sharing-link
15 | a.link(id='myLink' target="_blank") Sharing Link
16 | span(id="myLinkInput" class="aside disappear")
17 | span(class="copy-container" data-tooltip="Copy to Clipboard")
18 | i(data-feather="copy" class="copy-link" color="rgb(17, 117, 232)")
19 | span(class="copy-status") Copied!
20 |
21 | block scripts
22 | script(src='js/main.js')
23 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const path = require('path');
3 | const app = express();
4 | const port = process.env.PORT || 3000;
5 |
6 | app.use(express.static('public'));
7 | app.set('views', path.join(__dirname, 'views'));
8 | app.set('view engine', 'pug');
9 |
10 | app.get('/', function (req, res) {
11 | res.render('index', {title: 'Conclave'});
12 | });
13 |
14 | app.get('/about', function (req, res) {
15 | res.render('about', {title: 'About'});
16 | });
17 |
18 | app.get('/bots', function(req, res) {
19 | res.render('bots', {title: 'Talk to Bots'});
20 | });
21 |
22 | app.get('/idLength', function (req, res) {
23 | res.render('idGraph');
24 | });
25 |
26 | app.get('/opTime', function (req, res) {
27 | res.render('timeGraph');
28 | })
29 |
30 | app.get('/arraysGraph', function (req, res) {
31 | res.render('arraysGraph');
32 | })
33 |
34 | var srv = app.listen(port, function() {
35 | console.log('Listening on '+port)
36 | })
37 |
38 | app.use('/peerjs', require('peer').ExpressPeerServer(srv, {
39 | debug: true
40 | }))
41 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 Conclave Team
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/lib/version.js:
--------------------------------------------------------------------------------
1 | // Class that wraps the information about each version.
2 | // exceptions are a set of counters for operations that our local CRDT has not
3 | // seen or integrated yet. Waiting for these operations.
4 | class Version {
5 | constructor(siteId) {
6 | this.siteId = siteId;
7 | this.counter = 0;
8 | this.exceptions = [];
9 | }
10 |
11 | // Update a site's version based on the incoming operation that was processed
12 | // If the incomingCounter is less than we had previously processed, we can remove it from the exceptions
13 | // Else if the incomingCounter is the operation immediately after the last one we procesed, we just increment our counter to reflect that
14 | // Else, add an exception for each counter value that we haven't seen yet, and update our counter to match
15 | update(version) {
16 | const incomingCounter = version.counter;
17 |
18 | if (incomingCounter <= this.counter) {
19 | const index = this.exceptions.indexOf(incomingCounter);
20 | this.exceptions.splice(index, 1);
21 | } else if (incomingCounter === this.counter + 1) {
22 | this.counter = this.counter + 1;
23 | } else {
24 | for (let i = this.counter + 1; i < incomingCounter; i++) {
25 | this.exceptions.push(i);
26 | }
27 | this.counter = incomingCounter;
28 | }
29 | }
30 | }
31 |
32 | export default Version;
33 |
--------------------------------------------------------------------------------
/spec/versionSpec.js:
--------------------------------------------------------------------------------
1 | import Version from '../lib/version';
2 |
3 | describe('Version', () => {
4 | let siteId, version;
5 |
6 | beforeEach(() => {
7 | siteId = Math.floor(Math.random() * 1000);
8 | version = new Version(siteId);
9 | });
10 |
11 | describe('constructor', () => {
12 | it('initializes with counter at 0', () => {
13 | expect(version.counter).toBe(0);
14 | });
15 | });
16 |
17 | describe('update', () => {
18 | it('increments counter by one it counter is greater by 1', () => {
19 | version.update({sideId: siteId, counter: 1});
20 |
21 | expect(version.counter).toBe(1);
22 | });
23 |
24 | it('does not increment counter if remote counter is less than current', () => {
25 | version.update({sideId: siteId, counter: -1});
26 |
27 | expect(version.counter).toBe(0);
28 | });
29 |
30 | it('does not increment counter if remote counter is equal to current', () => {
31 | version.update({sideId: siteId, counter: 0});
32 |
33 | expect(version.counter).toBe(0);
34 | });
35 |
36 | it('creates exceptions if remote counter is greater than current by more than 1', () => {
37 | version.update({sideId: siteId, counter: 2});
38 |
39 | expect(version.exceptions.includes(1)).toBe(true);
40 | });
41 |
42 | it('removes exceptions if remote counter is less than current and exists in exceptions', () => {
43 | version.update({sideId: siteId, counter: 2});
44 | expect(version.exceptions.includes(1)).toBe(true);
45 |
46 | version.update({sideId: siteId, counter: 1});
47 | expect(version.exceptions.includes(1)).toBe(false);
48 | });
49 | });
50 | });
51 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "conclave",
3 | "version": "1.0.0",
4 | "description": "Peer-to-peer collaborative text editor",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "jasmine",
8 | "start": "node app.js",
9 | "build": "webpack -c ./webpack.config.js",
10 | "compile": "browserify build/main.js -o public/js/bundle.js && browserify build/demo.js -o public/js/demo-bundle.js && browserify build/idGraph.js -o public/js/idGraph-bundle.js && browserify build/timeGraph.js -o public/js/timeGraph-bundle.js && browserify build/arraysGraph.js -o public/js/arraysGraph-bundle.js",
11 | "local": "npm run build && npm start",
12 | "perform": "babel-node performance/script.js",
13 | "perform-linear": "babel-node performance/scriptLinear.js"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "git+https://github.com/Super-NES/conclave.git"
18 | },
19 | "author": "Super-NES",
20 | "license": "ISC",
21 | "bugs": {
22 | "url": "https://github.com/Super-NES/conclave/issues"
23 | },
24 | "homepage": "https://github.com/Super-NES/conclave#readme",
25 | "dependencies": {
26 | "acorn": "^6.4.2",
27 | "express": "^4.17.1",
28 | "feather-icons": "^4.28.0",
29 | "jquery": "^3.6.0",
30 | "peer": "^0.6.1",
31 | "peerjs": "^1.3.1",
32 | "pug": "^3.0.1",
33 | "rxjs": "^5.5.12",
34 | "simplemde": "^1.11.2",
35 | "sorted-cmp-array": "^2.0.1",
36 | "uuid": "^3.4.0"
37 | },
38 | "devDependencies": {
39 | "babel-cli": "^6.26.0",
40 | "babel-core": "^6.26.3",
41 | "babel-preset-es2015": "^6.24.1",
42 | "babel-preset-stage-3": "^6.24.1",
43 | "babel-register": "^6.26.0",
44 | "jasmine": "^2.99.0",
45 | "browserify": "^16.5.2",
46 | "jsdom": "^11.12.0",
47 | "webpack": "^5.38.1",
48 | "webpack-cli": "^4.7.0"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/spec/identifierSpec.js:
--------------------------------------------------------------------------------
1 | import Identifier from '../lib/identifier';
2 |
3 | describe("Identifier", () => {
4 | describe("compareTo", () => {
5 | it("compares itself to an id with a larger digit", () => {
6 | const siteId1 = 1;
7 | const siteId2 = 20;
8 | const id1 = new Identifier(1, siteId1);
9 | const id2 = new Identifier(2, siteId2);
10 |
11 | const comparator = id1.compareTo(id2);
12 | expect(comparator).toBe(-1);
13 | });
14 |
15 | it("compares itself to an id with a smaller digit", () => {
16 | const siteId1 = 1;
17 | const siteId2 = 20;
18 | const id1 = new Identifier(2, siteId1);
19 | const id2 = new Identifier(1, siteId2);
20 |
21 | const comparator = id1.compareTo(id2);
22 | expect(comparator).toBe(1);
23 | });
24 |
25 | it("compares itself to an id with a larger siteId", () => {
26 | const siteId1 = 2;
27 | const siteId2 = 1;
28 | const id1 = new Identifier(1, siteId1);
29 | const id2 = new Identifier(2, siteId2);
30 |
31 | const comparator = id1.compareTo(id2);
32 | expect(comparator).toBe(-1);
33 | });
34 |
35 | it("compares itself to an id with a smaller siteId", () => {
36 | const siteId1 = 2;
37 | const siteId2 = 1;
38 | const id1 = new Identifier(2, siteId1);
39 | const id2 = new Identifier(1, siteId2);
40 |
41 | const comparator = id1.compareTo(id2);
42 | expect(comparator).toBe(1);
43 | });
44 |
45 | it("compares itself to an id with the same digit and site", () => {
46 | const siteId1 = 1;
47 | const siteId2 = 1;
48 | const id1 = new Identifier(1, siteId1);
49 | const id2 = new Identifier(1, siteId2);
50 |
51 | const comparator = id1.compareTo(id2);
52 | expect(comparator).toBe(0);
53 | });
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # Conclave: Collaborate in private
4 |
5 | # No Longer Maintained
6 |
7 | Conclave was made for fun and educational purposes but it is no longer actively maintained. The creators have since moved onto other projects and work. It will remain open so that developers can ask questions and fork it. The demo at [https://conclave.tech](https://conclave.tech) will also stay up. However, feature requests will **not** be worked on by the creators at this time.
8 |
9 | Thanks again to all the developers who found this project interesting. Feel free ask questions to learn more about how it works and its internals. Hopefully you will venture off and create your own version of Conclave as well.
10 |
11 | ## Summary
12 |
13 | Conclave is an open-source, real-time, collaborative text editor for the browser built from scratch in JavaScript.
14 |
15 | Intrigued by collaboration tools like Google Docs, we set out to build one from scratch. Conclave uses **Conflict-Free Replicated Data Types** (CRDT) to make sure all users stay in-sync and **WebRTC** to allow users to send messages directly to one another. The result is a private and decentralized way to collaborate on documents.
16 |
17 | For more details on how we designed and built Conclave, read our [case study](https://conclave-team.github.io/conclave-site/).
18 |
19 | # How to Run Locally
20 |
21 | You will need node and npm. First download the dependencies.
22 |
23 | ```
24 | npm install
25 | ```
26 |
27 | Next, you will need to build and compile the assets and start the server. You can do that all in an npm command.
28 |
29 | ```
30 | npm run local
31 | ```
32 |
33 | We've added a Makefile and Dockerfile to make this easier. I highly recommend using them.
34 |
35 | Simply run:
36 |
37 | ```
38 | make run-local
39 | ```
40 |
41 | And you will be good to go.
42 |
--------------------------------------------------------------------------------
/lib/remoteCursor.js:
--------------------------------------------------------------------------------
1 | import CSS_COLORS from './cssColors';
2 | import { generateItemFromHash } from './hashAlgo';
3 | import { ANIMALS } from './cursorNames';
4 |
5 | export default class RemoteCursor {
6 | constructor(mde, siteId, position) {
7 | this.mde = mde;
8 |
9 | const color = generateItemFromHash(siteId, CSS_COLORS);
10 | const name = generateItemFromHash(siteId, ANIMALS);
11 |
12 | this.createCursor(color);
13 | this.createFlag(color, name);
14 |
15 | this.cursor.appendChild(this.flag);
16 | this.set(position);
17 | }
18 |
19 | createCursor(color) {
20 | const textHeight = this.mde.codemirror.defaultTextHeight();
21 |
22 | this.cursor = document.createElement('div');
23 | this.cursor.classList.add('remote-cursor');
24 | this.cursor.style.backgroundColor = color;
25 | this.cursor.style.height = textHeight + 'px';
26 | }
27 |
28 | createFlag(color, name) {
29 | const cursorName = document.createTextNode(name);
30 |
31 | this.flag = document.createElement('span');
32 | this.flag.classList.add('flag');
33 | this.flag.style.backgroundColor = color;
34 | this.flag.appendChild(cursorName)
35 | }
36 |
37 | set(position) {
38 | this.detach();
39 |
40 | const coords = this.mde.codemirror.cursorCoords(position, 'local');
41 | this.cursor.style.left = (coords.left >= 0 ? coords.left : 0) + 'px';
42 | this.mde.codemirror.getDoc().setBookmark(position, { widget: this.cursor });
43 | this.lastPosition = position;
44 |
45 | // Add a zero width-space so line wrapping works (on firefox?)
46 | this.cursor.parentElement.appendChild(document.createTextNode("\u200b"));
47 | }
48 |
49 | detach() {
50 | // Used when updating cursor position.
51 | // If cursor exists on the DOM, remove it.
52 | // DO NOT remove cursor's parent. It contains the zero width-space.
53 | if (this.cursor.parentElement)
54 | this.cursor.remove();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/views/layout.pug:
--------------------------------------------------------------------------------
1 | doctype html
2 | html(lang="en")
3 | head
4 | title= title
5 | link(rel="stylesheet" href="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.css")
6 | link(rel='stylesheet' href='css/style.css')
7 | link(rel='shortcut icon' href='assets/img/favicon.ico' type='image/x-icon')
8 | body
9 | .navbar
10 | a(href='/' target="_blank")
11 | h1.logo Conclave
12 | .nav-items
13 | block nav
14 | p#safari Your browser doesn't support WebRTC yet.Please use the Chrome or Firefox desktop browsers.
15 | #conclave.hide
16 | .text-wrapper
17 | .editor
18 | .header
19 | block link
20 | button#download(type='button') Save
21 | label#upload(for='file') Upload
22 | input#file(type='file' accept='.txt, .js, .rb, .md')
23 | textarea(row='10' col='20')
24 | p#peerId Peers:
25 | .video-modal.hide
26 | .video-bar
27 | i(data-feather="minus" class="minimize")
28 | i(data-feather='x' class="exit")
29 | video
30 | .loading
31 | p Loading
32 | .sk-fading-circle
33 | .sk-circle1.sk-circle
34 | .sk-circle2.sk-circle
35 | .sk-circle3.sk-circle
36 | .sk-circle4.sk-circle
37 | .sk-circle5.sk-circle
38 | .sk-circle6.sk-circle
39 | .sk-circle7.sk-circle
40 | .sk-circle8.sk-circle
41 | .sk-circle9.sk-circle
42 | .sk-circle10.sk-circle
43 | .sk-circle11.sk-circle
44 | .sk-circle12.sk-circle
45 | p Conclave currently supports Chrome or Firefox desktop browsers.
46 | p Note: Chrome v63 introduced a bug that causes sporadic issues. If loading takes longer than 10 sec, please upgrade to Chrome v64 or consider using Firefox.
47 | script(src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js")
48 | script.
49 | feather.replace({ 'stroke-width': 3 });
50 | block scripts
51 |
--------------------------------------------------------------------------------
/lib/demo.js:
--------------------------------------------------------------------------------
1 | import Peer from 'peerjs';
2 | import SimpleMDE from 'simplemde';
3 |
4 | import DemoController from './controller';
5 | import Broadcast from './broadcast';
6 | import Editor from './editor';
7 | import UserBot from './userBot';
8 |
9 | const id = Math.floor(Math.random() * 100000);
10 |
11 | const demo = new DemoController(
12 | (location.search.slice(1) || '0'),
13 | location.origin,
14 | new Peer('conclave-demo-'+id, {
15 | debug: 3
16 | }),
17 | new Broadcast(),
18 | new Editor(new SimpleMDE({
19 | placeholder: "Share the link to invite collaborators to your document.",
20 | spellChecker: false,
21 | toolbar: false,
22 | autofocus: true,
23 | indentWithTabs: true,
24 | tabSize: 4,
25 | indentUnit: 4,
26 | lineWrapping: false,
27 | shortCuts: []
28 | }))
29 | );
30 |
31 | const script1 = `Conclave is a private and secure real-time collaborative text editor. Conclave
32 | allows you to create and edit documents with multiple people all at the same time.
33 |
34 | ### How Do I Use It?
35 |
36 | To start editing, click the *New Document* link above, and then click the blue
37 | boxes icon to copy the *Sharing Link* to your clipboard. Share the link however
38 | you'd like with your collaborators.
39 |
40 | ### Doesn't Google Already Do This?
41 |
42 | Kind of, but Conclave is decentralized and therefore private. Google stores your
43 | documents on their servers where they and the government could access them. With
44 | Conclave, your document is stored only on your computer and any changes you make
45 | are sent only to the people collaborating with you. Also Google is pretty big.
46 | We're just three engineers who created Conclave in a month. Click *Our Team* above
47 | to learn more about us.
48 |
49 | ### What Else Can Conclave Do?
50 |
51 | - Upload a document from your computer to continue editing
52 | - Save the document to your computer at any time
53 |
54 | Happy Typing!`;
55 |
56 | new UserBot('conclave-bot'+id, 'conclave-demo-'+id, script1, demo.editor.mde);
57 |
58 |
--------------------------------------------------------------------------------
/lib/versionVector.js:
--------------------------------------------------------------------------------
1 | import SortedArray from './sortedArray';
2 | import Version from './version';
3 |
4 | // vector/list of versions of sites in the distributed system
5 | // keeps track of the latest operation received from each site (i.e. version)
6 | // prevents duplicate operations from being applied to our CRDT
7 | class VersionVector {
8 | // initialize empty vector to be sorted by siteId
9 | // initialize Version/Clock for local site and insert into SortedArray vector object
10 | constructor(siteId) {
11 | // this.versions = new SortedArray(this.siteIdComparator);
12 | this.versions = []
13 | this.localVersion = new Version(siteId);
14 | this.versions.push(this.localVersion);
15 | }
16 |
17 | increment() {
18 | this.localVersion.counter++;
19 | }
20 |
21 | // updates vector with new version received from another site
22 | // if vector doesn't contain version, it's created and added to vector
23 | // create exceptions if need be.
24 | update(incomingVersion) {
25 | const existingVersion = this.versions.find(version => incomingVersion.siteId === version.siteId);
26 |
27 | if (!existingVersion) {
28 | const newVersion = new Version(incomingVersion.siteId);
29 |
30 | newVersion.update(incomingVersion);
31 | this.versions.push(newVersion);
32 | } else {
33 | existingVersion.update(incomingVersion);
34 | }
35 | }
36 |
37 | // check if incoming remote operation has already been applied to our crdt
38 | hasBeenApplied(incomingVersion) {
39 | const localIncomingVersion = this.getVersionFromVector(incomingVersion);
40 | const isIncomingInVersionVector = !!localIncomingVersion;
41 |
42 | if (!isIncomingInVersionVector) return false;
43 |
44 | const isIncomingLower = incomingVersion.counter <= localIncomingVersion.counter;
45 | const isInExceptions = localIncomingVersion.exceptions.includes(incomingVersion.counter);
46 |
47 | return isIncomingLower && !isInExceptions;
48 | }
49 |
50 | getVersionFromVector(incomingVersion) {
51 | return this.versions.find(version => version.siteId === incomingVersion.siteId);
52 | }
53 |
54 | getLocalVersion() {
55 | return {
56 | siteId: this.localVersion.siteId,
57 | counter: this.localVersion.counter
58 | };
59 | }
60 | }
61 |
62 | export default VersionVector;
63 |
--------------------------------------------------------------------------------
/spec/editorSpec.js:
--------------------------------------------------------------------------------
1 | import { JSDOM } from 'jsdom';
2 |
3 | import Editor from '../lib/editor';
4 |
5 | describe("Editor", () => {
6 | const mockMDE = {
7 | codemirror: {
8 | setOption: function() {}
9 | }
10 | };
11 | const editor = new Editor(mockMDE);
12 | editor.controller = {
13 | crdt: { text: '' }
14 | };
15 |
16 | describe("constructor", () => {
17 | it("sets the mde passed in to the.mde", () => {
18 | expect(editor.mde).toEqual(mockMDE);
19 | });
20 | });
21 |
22 | describe("bindChangeEvent", () => {
23 | // it("is triggered by a change in the codemirror", () => {
24 | // spyOn(editor.mde.codemirror, "on");
25 | // editor.mde.codemirror.trigger("change");
26 | // expect(editor.mde.codemirror.on).toHaveBeenCalled();
27 | // });
28 | //
29 | // it("changes the character text to a new line", () => {
30 | //
31 | // });
32 | //
33 | // it("calls controller.handleInsert when change was an insert", () => {
34 | //
35 | // });
36 | //
37 | // it("calls controller.handleDelete when change was a deletion", () => {
38 | //
39 | // });
40 | });
41 |
42 | describe("updateView", () => {
43 | beforeEach(() => {
44 | const dom = new JSDOM(``);
45 | });
46 |
47 | // it("adds text to the view", () => {
48 | // const editor = new Editor(null);
49 | // const newText = "I am here."
50 | // editor.updateView(newText);
51 | //
52 | // expect(editor.mde.value()).toEqual(newText);
53 | // });
54 | //
55 | // it("removes text from the view", () => {
56 | //
57 | // });
58 | //
59 | // it("retains the cursor position", () => {
60 | //
61 | // });
62 | });
63 |
64 | describe("findLinearIdx", () => {
65 | it("returns 0 if lines of text is empty", () => {
66 | editor.controller.crdt.text = "";
67 | expect(editor.findLinearIdx(0, 0)).toEqual(0);
68 | });
69 |
70 | it("calculates linear index from a single line of text", () => {
71 | editor.controller.crdt.text = "abcdefghijklmnop";
72 | expect(editor.findLinearIdx(0, 7)).toEqual(7);
73 | });
74 |
75 | it("calculates linear index from multiple lines of text", () => {
76 | editor.controller.crdt.text = "abc\ndefgh\nijk\nlmnop";
77 | expect(editor.findLinearIdx(1, 2)).toEqual(6);
78 | });
79 |
80 | it("can find the linear index on the last line of text", () => {
81 | editor.controller.crdt.text = "abc\ndefgh\nijk\nlmnop";
82 | expect(editor.findLinearIdx(3, 2)).toEqual(16);
83 | });
84 |
85 | it("can find the linear index at the end of a line of text", () => {
86 | editor.controller.crdt.text = "abc\ndefgh\nijk\nlmnop";
87 | expect(editor.findLinearIdx(1, 5)).toEqual(9);
88 | });
89 | });
90 | });
91 |
--------------------------------------------------------------------------------
/spec/charSpec.js:
--------------------------------------------------------------------------------
1 | import Char from '../lib/char';
2 | import Identifier from "../lib/identifier";
3 |
4 | describe("Char", () => {
5 | describe("compareTo", () => {
6 | let char1;
7 | let id1;
8 | let id2;
9 | let id3;
10 |
11 | beforeEach(() => {
12 | id1 = new Identifier(2, 1);
13 | id2 = new Identifier(5, 1);
14 | id3 = new Identifier(1, 2);
15 | char1 = new Char("a", 0, 2, [id1, id2, id3]);
16 | });
17 |
18 | it("returns -1 if first position is 'lower' than second position", () => {
19 | const id21 = new Identifier(2, 1);
20 | const id22 = new Identifier(5, 1);
21 | const id23 = new Identifier(3, 2);
22 | const char2 = new Char("b", 0, 2, [id21, id22, id23]);
23 | expect(char1.compareTo(char2)).toEqual(-1);
24 | });
25 |
26 | it("returns -1 if first site is 'lower' than second site", () => {
27 | const id21 = new Identifier(2, 1);
28 | const id22 = new Identifier(5, 2);
29 | const id23 = new Identifier(1, 2);
30 | const char2 = new Char("b", 0, 2, [id21, id22, id23]);
31 | expect(char1.compareTo(char2)).toEqual(-1);
32 | });
33 |
34 | it("returns -1 if first position is 'shorter' than second position", () => {
35 | const id21 = new Identifier(2, 1);
36 | const id22 = new Identifier(5, 1);
37 | const id23 = new Identifier(1, 2);
38 | const id24 = new Identifier(8, 2);
39 | const char2 = new Char("b", 0, 2, [id21, id22, id23, id24]);
40 | expect(char1.compareTo(char2)).toEqual(-1);
41 | });
42 |
43 | it("returns 1 if first position is 'higher' than second position", () => {
44 | const id21 = new Identifier(2, 1);
45 | const id22 = new Identifier(3, 1);
46 | const id23 = new Identifier(1, 2);
47 | const char2 = new Char("b", 0, 2, [id21, id22, id23]);
48 | expect(char1.compareTo(char2)).toEqual(1);
49 | });
50 |
51 | it("returns 1 if first site is 'higher' than second site", () => {
52 | const id21 = new Identifier(2, 1);
53 | const id22 = new Identifier(5, 1);
54 | const id23 = new Identifier(1, 1);
55 | const char2 = new Char("b", 0, 1, [id21, id22, id23]);
56 | expect(char1.compareTo(char2)).toEqual(1);
57 | });
58 |
59 | it("returns 1 if first position is 'longer' than second position", () => {
60 | const id21 = new Identifier(2, 1);
61 | const id22 = new Identifier(5, 1);
62 | const char2 = new Char("b", 0, 1, [id21, id22]);
63 | expect(char1.compareTo(char2)).toEqual(1);
64 | });
65 |
66 | it("returns 0 if positions are exactly the same", () => {
67 | const id21 = new Identifier(2, 1);
68 | const id22 = new Identifier(5, 1);
69 | const id23 = new Identifier(1, 2);
70 | const char2 = new Char("b", 0, 2, [id21, id22, id23]);
71 | expect(char1.compareTo(char2)).toEqual(0);
72 | });
73 | });
74 | });
75 |
--------------------------------------------------------------------------------
/lib/idGraph.js:
--------------------------------------------------------------------------------
1 | import CRDT from './crdtLinear';
2 | import * as Util from './utilLinear';
3 | import UUID from 'uuid/v1';
4 |
5 | function mockController() {
6 | return {
7 | siteId: UUID(),
8 | broadcastInsertion: function() {},
9 | broadcastDeletion: function() {},
10 | insertIntoEditor: function() {},
11 | deleteFromEditor: function() {},
12 | vector: {
13 | localVersion: {
14 | counter: 0
15 | },
16 | increment: function() {
17 | this.localVersion.counter++;
18 | }
19 | }
20 | }
21 | }
22 |
23 | let multipliers, bases, boundaries, strategies, crdt, xs, ys, data, name, title;
24 | const ops = [100, 500, 1000, 3000, 5000];
25 | const funcs = [
26 | [Util.insertRandom, 'Inserted Randomly'],
27 | [Util.insertEnd, 'Inserted at End'],
28 | [Util.insertBeginning, 'Inserted at Beginning']
29 | ];
30 |
31 |
32 | // comparing multipliers
33 |
34 | multipliers = [1, 2, 3];
35 | data = [];
36 |
37 | multipliers.forEach(mult => {
38 | funcs.forEach(func => {
39 | xs = [];
40 | ys = [];
41 | crdt = new CRDT(mockController(), 32, 10, 'random', mult);
42 | crdt.insertText = function() {};
43 | crdt.deleteText = function() {};
44 | ops.forEach(op => {
45 | func[0](crdt, op);
46 | xs.push(op);
47 | ys.push(Util.avgIdLength(crdt));
48 | crdt.struct = [];
49 | });
50 | name = `multiplier: ${mult}, ${func[1]}`;
51 | data.push({x: xs, y: ys, type: 'scatter', name: name});
52 | });
53 | });
54 |
55 | title = 'Different Base Multiplications (base = 32, boundary = 10, strategy = random)'
56 | Plotly.newPlot('g0', data, {title: title, height: 600});
57 |
58 |
59 | // comparing base
60 |
61 | bases = [32, 1024, 4096];
62 | data = [];
63 |
64 | bases.forEach(base => {
65 | funcs.forEach(func => {
66 | xs = [];
67 | ys = [];
68 | crdt = new CRDT(mockController(), base, 10, 'random', 2);
69 | crdt.insertText = function() {};
70 | crdt.deleteText = function() {};
71 | ops.forEach(op => {
72 | func[0](crdt, op);
73 | xs.push(op);
74 | ys.push(Util.avgIdLength(crdt));
75 | crdt.struct = [];
76 | });
77 | name = `base: ${base}, ${func[1]}`;
78 | data.push({x: xs, y: ys, type: 'scatter', name: name});
79 | });
80 | });
81 |
82 | title = 'Different Starting Bases (mult = 2, boundary = 10, strategy = random)';
83 | Plotly.newPlot('g1', data, {title: title, height: 600});
84 |
85 |
86 | // comparing boundary
87 |
88 | boundaries = [10, 20, 30];
89 | data = [];
90 |
91 | boundaries.forEach(boundary => {
92 | funcs.forEach(func => {
93 | xs = [];
94 | ys = [];
95 | crdt = new CRDT(mockController(), 32, boundary, 'random', 2);
96 | crdt.insertText = function() {};
97 | crdt.deleteText = function() {};
98 | ops.forEach(op => {
99 | func[0](crdt, op);
100 | xs.push(op);
101 | ys.push(Util.avgIdLength(crdt));
102 | crdt.struct = [];
103 | });
104 | name = `boundary: ${boundary}, ${func[1]}`;
105 | data.push({x: xs, y: ys, type: 'scatter', name: name});
106 | });
107 | });
108 |
109 | title = 'Different Boundaries (mult = 2, base = 32, strategy = random)';
110 | Plotly.newPlot('g2', data, {title: title, height: 600});
111 |
112 |
113 | // comparing strategy
114 |
115 | strategies = ['every2nd', 'every3rd', 'random'];
116 | data = [];
117 |
118 | strategies.forEach(strat => {
119 | funcs.forEach(func => {
120 | xs = [];
121 | ys = [];
122 | crdt = new CRDT(mockController(), 32, 10, strat, 2);
123 | crdt.insertText = function() {};
124 | crdt.deleteText = function() {};
125 | ops.forEach(op => {
126 | func[0](crdt, op);
127 | xs.push(op);
128 | ys.push(Util.avgIdLength(crdt));
129 | crdt.struct = [];
130 | });
131 | name = `strategy: ${strat}, ${func[1]}`;
132 | data.push({x: xs, y: ys, type: 'scatter', name: name});
133 | });
134 | });
135 |
136 | title = 'Different Strategies (mult = 2, base = 32, boundary = 10)';
137 | Plotly.newPlot('g3', data, {title: title, height: 600});
138 |
--------------------------------------------------------------------------------
/spec/versionVectorSpec.js:
--------------------------------------------------------------------------------
1 | import VersionVector from "../lib/VersionVector";
2 |
3 | describe("VersionVector", () => {
4 | describe('constructor', () => {
5 | const vector = new VersionVector(10);
6 |
7 | it('initializes a local property on the object', () => {
8 | expect(vector.localVersion).toBeTruthy();
9 | });
10 |
11 | it('initializes a Versions property', () => {
12 | expect(vector.versions).toBeTruthy();
13 | });
14 |
15 | it('puts local vector in the all vector', () => {
16 | expect(vector.versions[0].siteId).toBe(10);
17 | });
18 | });
19 |
20 | describe('increment', () => {
21 | it('increments the counter in the local property', () => {
22 | const vector = new VersionVector(10);
23 |
24 | expect(vector.localVersion.counter).toBe(0);
25 |
26 | vector.increment();
27 | expect(vector.localVersion.counter).toBe(1);
28 | });
29 |
30 | it('increments the counter of the version in the Versions array', () => {
31 | const vector = new VersionVector(10);
32 | vector.increment();
33 |
34 | expect(vector.versions[0].counter).toBe(1)
35 | });
36 | });
37 |
38 | describe('update', () => {
39 | it('increments the version if the entry exists in the all arr', () => {
40 | const vector = new VersionVector(10);
41 | vector.update({siteId: 10, counter: 1});
42 |
43 | expect(vector.versions[0].counter).toBe(1);
44 | });
45 |
46 | it('creates the entry if it does not exist and then increments', () => {
47 | const vector = new VersionVector(10);
48 |
49 | expect(vector.versions[0].siteId).toBe(10);
50 | vector.update({siteId: 5, counter: 1});
51 |
52 | expect(vector.versions[1].siteId).toBe(5);
53 | expect(vector.versions[1].counter).toBe(1);
54 | });
55 |
56 | it('creates exceptions if version counter is greater by more than 1', () => {
57 | const vector = new VersionVector(10);
58 | vector.update({siteId: 10, counter: 2});
59 |
60 | expect(vector.versions[0].exceptions.includes(1)).toBe(true);
61 | });
62 |
63 | it('does not update version counter if remote version counter is equal to current counter', () => {
64 | const vector = new VersionVector(10);
65 | vector.update({siteId: 10, counter: 0});
66 |
67 | expect(vector.versions[0].counter).toBe(0);
68 | });
69 |
70 | it('does not update version counter if remote version counter is less than current counter', () => {
71 | const vector = new VersionVector(10);
72 | vector.update({siteId: 10, counter: -1});
73 | expect(vector.versions[0].counter).toBe(0);
74 | });
75 |
76 | it('removes exceptions if counter exists in exceptions set', () => {
77 | const vector = new VersionVector(10);
78 | vector.update({siteId: 10, counter: 2});
79 | expect(vector.versions[0].exceptions.includes(1)).toBe(true);
80 |
81 | vector.update({siteId: 10, counter: 1});
82 | expect(vector.versions[0].exceptions.includes(1)).toBe(false);
83 | });
84 | });
85 |
86 | describe('hasBeenApplied', () => {
87 | it('returns true if remote counter is equal to or less than local version counter and no exceptions', () => {
88 | const vector = new VersionVector(10);
89 | vector.update({siteId: 10, counter: 1});
90 | vector.update({siteId: 10, counter: 2});
91 |
92 | expect(vector.hasBeenApplied({siteId: 10, counter: 1})).toBe(true);
93 | expect(vector.hasBeenApplied({siteId: 10, counter: 2})).toBe(true);
94 | });
95 |
96 | it('returns false if version does not exist', () => {
97 | const vector = new VersionVector(10);
98 |
99 | expect(vector.hasBeenApplied({siteId: 5, counter: 1})).toBe(false);
100 | });
101 |
102 | it('returns false if version counter is greater than stored version', () => {
103 | const vector = new VersionVector(10);
104 | vector.update({siteId: 10, counter: 1});
105 |
106 | expect(vector.hasBeenApplied({siteId: 10, counter: 2})).toBe(false);
107 | });
108 |
109 | it('returns false if version counter is in exceptions', () => {
110 | const vector = new VersionVector(10);
111 | vector.update({siteId: 10, counter: 2});
112 |
113 | expect(vector.hasBeenApplied({siteId: 10, counter: 1})).toBe(false);
114 | });
115 | });
116 | });
117 |
--------------------------------------------------------------------------------
/lib/userBot.js:
--------------------------------------------------------------------------------
1 | import CRDT from './crdt';
2 | import VersionVector from './versionVector';
3 | import Peer from 'peerjs';
4 | import Identifier from './identifier';
5 | import Char from './char';
6 |
7 | class UserBot {
8 | constructor(peerId, targetPeerId, script, mde) {
9 | this.siteId = 'bot-1';
10 | this.peer = new Peer(peerId, {
11 | debug: 3
12 | });
13 | this.vector = new VersionVector(this.siteId);
14 | this.crdt = new CRDT(this);
15 | this.buffer = [];
16 | this.mde = mde;
17 | this.script = script;
18 |
19 | this.peer.on('open', () => {
20 | this.connectToUser(targetPeerId);
21 | this.onConnection();
22 | this.runScript(75)
23 | })
24 | }
25 |
26 | connectToUser(targetPeerId) {
27 | this.connection = this.peer.connect(targetPeerId);
28 |
29 | this.connection.on('open', () => {
30 | const message = JSON.stringify({
31 | type: "add to network",
32 | newPeer: this.peer.id,
33 | newSite: this.siteId
34 | });
35 | this.connection.send(message);
36 | });
37 | }
38 |
39 | runScript(interval) {
40 | this.counter = 0;
41 | const self = this;
42 | let line = 0;
43 | let ch = 0;
44 |
45 | self.intervalId = setInterval(function() {
46 | let index = self.counter;
47 | let val = self.script[self.counter++];
48 | let pos = { line: line, ch: ch };
49 | ch++;
50 |
51 | if (!val) {
52 | clearInterval(self.intervalId);
53 | return;
54 | } else if (val === '\n') {
55 | line++;
56 | ch = 0;
57 | }
58 |
59 | self.crdt.handleLocalInsert(val, pos);
60 | }, interval);
61 | }
62 |
63 | onConnection() {
64 | this.peer.on('connection', connection => {
65 | connection.on('data', data => {
66 | const dataObj = JSON.parse(data);
67 |
68 | this.handleRemoteOperation(dataObj);
69 | });
70 | });
71 | }
72 |
73 | processDeletionBuffer() {
74 | let i = 0;
75 | let deleteOperation;
76 |
77 | while (i < this.buffer.length) {
78 | deleteOperation = this.buffer[i];
79 |
80 | if (this.hasInsertionBeenApplied(deleteOperation)) {
81 | this.applyOperation(deleteOperation);
82 | this.buffer.splice(i, 1);
83 | } else {
84 | i++;
85 | }
86 | }
87 | }
88 |
89 | hasInsertionBeenApplied(operation) {
90 | const charVersion = { siteId: operation.char.siteId, counter: operation.char.counter };
91 | return this.vector.hasBeenApplied(charVersion);
92 | }
93 |
94 | handleRemoteOperation(operation) {
95 | if (this.vector.hasBeenApplied(operation.version)) return;
96 |
97 | if (operation.type === 'insert') {
98 | this.applyOperation(operation);
99 | } else if (operation.type === 'delete') {
100 | this.buffer.push(operation);
101 | }
102 |
103 | this.processDeletionBuffer();
104 | }
105 |
106 | applyOperation(operation) {
107 | const char = operation.char;
108 | const identifiers = char.position.map(pos => new Identifier(pos.digit, pos.siteId));
109 | const newChar = new Char(char.value, char.counter, char.siteId, identifiers);
110 |
111 | if (operation.type === 'insert') {
112 | this.crdt.handleRemoteInsert(newChar);
113 | } else if (operation.type === 'delete') {
114 | this.crdt.handleRemoteDelete(newChar, operation.version.siteId);
115 | }
116 |
117 | this.vector.update(operation.version);
118 | }
119 |
120 | broadcastInsertion(char) {
121 | const operation = JSON.stringify({
122 | type: 'insert',
123 | char: char,
124 | version: this.vector.getLocalVersion()
125 | });
126 |
127 | if (this.connection.open) {
128 | this.connection.send(operation);
129 | } else {
130 | this.connection.on('open', () => {
131 | this.connection.send(operation);
132 | });
133 | }
134 | }
135 |
136 | broadcastDeletion(char) {
137 | const operation = JSON.stringify({
138 | type: 'delete',
139 | char: char,
140 | version: this.vector.getLocalVersion()
141 | });
142 |
143 | if (this.connection.open) {
144 | this.connection.send(operation);
145 | } else {
146 | this.connection.on('open', () => {
147 | this.connection.send(operation);
148 | });
149 | }
150 | }
151 |
152 | insertIntoEditor() {}
153 | deleteFromEditor() {}
154 | }
155 |
156 | export default UserBot;
157 |
--------------------------------------------------------------------------------
/lib/timeGraph.js:
--------------------------------------------------------------------------------
1 | import CRDT from './crdtLinear';
2 | import * as Util from './utilLinear';
3 | import UUID from 'uuid/v1';
4 |
5 | function mockController() {
6 | return {
7 | siteId: UUID(),
8 | broadcastInsertion: function() {},
9 | broadcastDeletion: function() {},
10 | insertIntoEditor: function() {},
11 | deleteFromEditor: function() {},
12 | vector: {
13 | localVersion: {
14 | counter: 0
15 | },
16 | increment: function() {
17 | this.localVersion.counter++;
18 | }
19 | }
20 | }
21 | }
22 |
23 | let funcs, crdt, xs, ys, data, name, title;
24 | const bases = [32, 2064];
25 | const boundaries = [10, 100];
26 | const ops = [1000, 5000, 10000, 20000];
27 |
28 |
29 | // local insertions
30 |
31 | funcs = [[Util.insertRandom, 'random'], [Util.insertEnd, 'at end'], [Util.insertBeginning, 'at beginning']];
32 | data = [];
33 |
34 | funcs.forEach(func => {
35 | bases.forEach(base => {
36 | boundaries.forEach(boundary => {
37 | xs = [];
38 | ys = [];
39 | crdt = new CRDT(mockController(), base, boundary, 'random', 2);
40 | crdt.insertText = function() {};
41 | crdt.deleteText = function() {};
42 | ops.forEach(op => {
43 | xs.push(op);
44 | ys.push(func[0](crdt, op));
45 | crdt.struct = [];
46 | });
47 | name = `base: ${base}, boundary: ${boundary}, ${func[1]}`;
48 | data.push({x: xs, y: ys, type: 'scatter', name: name});
49 | });
50 | });
51 | });
52 |
53 | title = 'Local Insertions, Different Bases and Boundaries (mult = 2, strategy = random)';
54 | Plotly.newPlot('g0', data, {title: title, height: 600});
55 |
56 |
57 | // local deletions
58 |
59 | funcs = [[Util.deleteRandom, 'random'], [Util.deleteEnd, 'at end'], [Util.deleteEnd, 'at beginning']];
60 | data = [];
61 |
62 | funcs.forEach(func => {
63 | bases.forEach(base => {
64 | boundaries.forEach(boundary => {
65 | xs = [];
66 | ys = [];
67 | crdt = new CRDT(mockController(), base, boundary, 'random', 2);
68 | crdt.insertText = function() {};
69 | crdt.deleteText = function() {};
70 | ops.forEach(op => {
71 | xs.push(op);
72 | Util.insertRandom(crdt, op);
73 | ys.push(func[0](crdt, op));
74 | crdt.struct = [];
75 | });
76 | name = `base: ${base}, boundary: ${boundary}, ${func[1]}`;
77 | data.push({x: xs, y: ys, type: 'scatter', name: name});
78 | });
79 | });
80 | });
81 |
82 | title = 'Local Deletions, Different Bases and Boundaries (mult = 2, strategy = random)';
83 | Plotly.newPlot('g1', data, {title: title, height: 600});
84 |
85 |
86 | // remote insertions
87 |
88 | funcs = [[Util.remoteInsertRandom, 'random'], [Util.remoteInsertEnd, 'at end'], [Util.remoteInsertBeginning, 'at beginning']];
89 | data = [];
90 |
91 | funcs.forEach(func => {
92 | bases.forEach(base => {
93 | boundaries.forEach(boundary => {
94 | xs = [];
95 | ys = [];
96 | crdt = new CRDT(mockController(), base, boundary, 'random', 2);
97 | crdt.insertText = function() {};
98 | crdt.deleteText = function() {};
99 | ops.forEach(op => {
100 | xs.push(op);
101 | ys.push(func[0](crdt, op));
102 | crdt.struct = [];
103 | });
104 | name = `base: ${base}, boundary: ${boundary}, ${func[1]}`;
105 | data.push({x: xs, y: ys, type: 'scatter', name: name});
106 | });
107 | });
108 | });
109 |
110 | title = 'Remote Insertions, Different Bases and Boundaries (mult = 2, strategy = random)';
111 | Plotly.newPlot('g2', data, {title: title, height: 600});
112 |
113 |
114 | // remote deletions
115 |
116 | funcs = [[Util.remoteDeleteRandom, 'random'], [Util.remoteDeleteEnd, 'at end'], [Util.remoteDeleteBeginning, 'at beginning']];
117 | data = [];
118 |
119 | funcs.forEach(func => {
120 | bases.forEach(base => {
121 | boundaries.forEach(boundary => {
122 | xs = [];
123 | ys = [];
124 | ops.forEach(op => {
125 | crdt = new CRDT(mockController(), base, boundary, 'random', 2);
126 | crdt.insertText = function() {};
127 | crdt.deleteText = function() {};
128 | Util.remoteInsertRandom(crdt, op);
129 | xs.push(op);
130 | ys.push(func[0](crdt, op));
131 | });
132 | name = `base: ${base}, boundary: ${boundary}, ${func[1]}`;
133 | data.push({x: xs, y: ys, type: 'scatter', name: name});
134 | });
135 | });
136 | });
137 |
138 | title = 'Remote Deletions, Different Bases and Boundaries (mult = 2, strategy = random)';
139 | Plotly.newPlot('g3', data, {title: title, height: 600});
140 |
--------------------------------------------------------------------------------
/lib/cursorNames.js:
--------------------------------------------------------------------------------
1 | const ADJECTIVES = [
2 | 'anonymous',
3 | 'secret',
4 | 'mysterious',
5 | 'silent',
6 | 'covert',
7 | 'private',
8 | 'secluded',
9 | 'undercover',
10 | 'unknown',
11 | 'undisclosed',
12 | 'mystic',
13 | 'furtive',
14 | 'esoteric',
15 | 'strange',
16 | 'mystical',
17 | 'occult',
18 | 'dark',
19 | 'classified',
20 | 'confidential',
21 | 'sneaky',
22 | 'stealthy',
23 | 'cryptic',
24 | 'discreet',
25 | 'camouflaged',
26 | 'hidden',
27 | 'concealed',
28 | 'cloaked',
29 | 'disguised'
30 | ];
31 |
32 | const ANIMALS = [
33 | "Aardvark",
34 | "Albatross",
35 | "Alligator",
36 | "Alpaca",
37 | "Ant",
38 | "Anteater",
39 | "Antelope",
40 | "Ape",
41 | "Armadillo",
42 | "Donkey",
43 | "Baboon",
44 | "Badger",
45 | "Barracuda",
46 | "ConclaveBot",
47 | "Bat",
48 | "Bear",
49 | "Beaver",
50 | "Bee",
51 | "Bison",
52 | "Boar",
53 | "Buffalo",
54 | "Butterfly",
55 | "Camel",
56 | "Capybara",
57 | "Caribou",
58 | "Cassowary",
59 | "Cat",
60 | "Caterpillar",
61 | "Cattle",
62 | "Chamois",
63 | "Cheetah",
64 | "Chicken",
65 | "Chimpanzee",
66 | "Chinchilla",
67 | "Chough",
68 | "Clam",
69 | "Cobra",
70 | "Cockroach",
71 | "Cod",
72 | "Cormorant",
73 | "Coyote",
74 | "Crab",
75 | "Crane",
76 | "Crocodile",
77 | "Crow",
78 | "Curlew",
79 | "Deer",
80 | "Dinosaur",
81 | "Dog",
82 | "Dogfish",
83 | "Dolphin",
84 | "Dotterel",
85 | "Dove",
86 | "Dragonfly",
87 | "Duck",
88 | "Dugong",
89 | "Dunlin",
90 | "Eagle",
91 | "Echidna",
92 | "Eel",
93 | "Eland",
94 | "Elephant",
95 | "Elk",
96 | "Emu",
97 | "Falcon",
98 | "Ferret",
99 | "Finch",
100 | "Fish",
101 | "Flamingo",
102 | "Fly",
103 | "Fox",
104 | "Frog",
105 | "Gaur",
106 | "Gazelle",
107 | "Gerbil",
108 | "Giraffe",
109 | "Gnat",
110 | "Gnu",
111 | "Goat",
112 | "Goldfinch",
113 | "Goldfish",
114 | "Goose",
115 | "Gorilla",
116 | "Goshawk",
117 | "Grasshopper",
118 | "Grouse",
119 | "Guanaco",
120 | "Gull",
121 | "Hamster",
122 | "Hare",
123 | "Hawk",
124 | "Hedgehog",
125 | "Heron",
126 | "Herring",
127 | "Hippopotamus",
128 | "Hornet",
129 | "Horse",
130 | "Human",
131 | "Hummingbird",
132 | "Hyena",
133 | "Ibex",
134 | "Ibis",
135 | "Jackal",
136 | "Jaguar",
137 | "Jay",
138 | "Jellyfish",
139 | "Kangaroo",
140 | "Kingfisher",
141 | "Koala",
142 | "Kookabura",
143 | "Kouprey",
144 | "Kudu",
145 | "Lapwing",
146 | "Lark",
147 | "Lemur",
148 | "Leopard",
149 | "Lion",
150 | "Llama",
151 | "Lobster",
152 | "Locust",
153 | "Loris",
154 | "Louse",
155 | "Lyrebird",
156 | "Magpie",
157 | "Mallard",
158 | "Manatee",
159 | "Mandrill",
160 | "Mantis",
161 | "Marten",
162 | "Meerkat",
163 | "Mink",
164 | "Mole",
165 | "Mongoose",
166 | "Monkey",
167 | "Moose",
168 | "Mosquito",
169 | "Mouse",
170 | "Mule",
171 | "Narwhal",
172 | "Newt",
173 | "Nightingale",
174 | "Octopus",
175 | "Okapi",
176 | "Opossum",
177 | "Oryx",
178 | "Ostrich",
179 | "Otter",
180 | "Owl",
181 | "Oyster",
182 | "Panther",
183 | "Parrot",
184 | "Partridge",
185 | "Peafowl",
186 | "Pelican",
187 | "Penguin",
188 | "Pheasant",
189 | "Pig",
190 | "Pigeon",
191 | "Pony",
192 | "Porcupine",
193 | "Porpoise",
194 | "Quail",
195 | "Quelea",
196 | "Quetzal",
197 | "Rabbit",
198 | "Raccoon",
199 | "Rail",
200 | "Ram",
201 | "Rat",
202 | "Raven",
203 | "Reindeer",
204 | "Rhinoceros",
205 | "Rook",
206 | "Salamander",
207 | "Salmon",
208 | "Sandpiper",
209 | "Sardine",
210 | "Scorpion",
211 | "Seahorse",
212 | "Seal",
213 | "Shark",
214 | "Sheep",
215 | "Shrew",
216 | "Skunk",
217 | "Snail",
218 | "Snake",
219 | "Sparrow",
220 | "Spider",
221 | "Spoonbill",
222 | "Squid",
223 | "Squirrel",
224 | "Starling",
225 | "Stingray",
226 | "Stinkbug",
227 | "Stork",
228 | "Swallow",
229 | "Swan",
230 | "Tapir",
231 | "Tarsier",
232 | "Termite",
233 | "Tiger",
234 | "Toad",
235 | "Trout",
236 | "Turkey",
237 | "Turtle",
238 | "Viper",
239 | "Vulture",
240 | "Wallaby",
241 | "Walrus",
242 | "Wasp",
243 | "Weasel",
244 | "Whale",
245 | "Wildcat",
246 | "Wolf",
247 | "Wolverine",
248 | "Wombat",
249 | "Woodcock",
250 | "Woodpecker",
251 | "Worm",
252 | "Wren",
253 | "Yak",
254 | "Zebra"
255 | ];
256 |
257 | export {
258 | ANIMALS,
259 | ADJECTIVES
260 | };
261 |
--------------------------------------------------------------------------------
/lib/arraysGraph.js:
--------------------------------------------------------------------------------
1 | import CRDTLinear from './crdtLinear';
2 | import * as UtilLinear from './utilLinear';
3 | import CRDT from './crdt';
4 | import * as Util from './util';
5 | import UUID from 'uuid/v1';
6 |
7 |
8 | function mockController() {
9 | return {
10 | siteId: UUID(),
11 | broadcastInsertion: function() {},
12 | broadcastDeletion: function() {},
13 | insertIntoEditor: function() {},
14 | deleteFromEditor: function() {},
15 | vector: {
16 | getLocalVersion: () => {},
17 | localVersion: {
18 | counter: 0
19 | },
20 | increment: function() {
21 | this.localVersion.counter++;
22 | }
23 | }
24 | }
25 | }
26 |
27 | let func, base, boundary, mult, boundaryStrategy, crdt, xs, ys, data, name, title;
28 | const ops = [1000, 10000, 20000, 40000, 60000, 80000, 100000];
29 | base = 32;
30 | boundary = 10;
31 | mult = 2;
32 | boundaryStrategy = 'random';
33 |
34 | // Local Insertions
35 | title = 'Local Insertions';
36 | data = [];
37 |
38 | // LINEAR
39 | func = UtilLinear.insertBeginning;
40 | xs = [];
41 | ys = [];
42 |
43 | crdt = new CRDTLinear(mockController(), base, boundary, boundaryStrategy, mult);
44 | crdt.insertText = function() {};
45 | crdt.deleteText = function() {};
46 | ops.forEach(op => {
47 | xs.push(op);
48 | ys.push(func(crdt, op));
49 | crdt.struct = [];
50 | });
51 |
52 | name = 'Linear';
53 | data.push({x: xs, y: ys, type: 'scatter', name: name});
54 |
55 | // array-of-arrays
56 | func = Util.insertBeginning;
57 | xs = [];
58 | ys = [];
59 |
60 | crdt = new CRDT(mockController(), base, boundary, boundaryStrategy, mult);
61 | ops.forEach(op => {
62 | xs.push(op);
63 | ys.push(func(crdt, op));
64 | crdt.struct = [];
65 | });
66 |
67 | name = 'Array-of-Arrays';
68 | data.push({x: xs, y: ys, type: 'scatter', name: name});
69 |
70 | Plotly.newPlot('g0', data, {title: title, height: 600});
71 |
72 | // Local Deletions
73 | title = 'Local Deletions';
74 | data = [];
75 |
76 | // linear
77 | func = UtilLinear.deleteBeginning;
78 | xs = [];
79 | ys = [];
80 |
81 | crdt = new CRDTLinear(mockController(), base, boundary, boundaryStrategy, mult);
82 | crdt.insertText = function() {};
83 | crdt.deleteText = function() {};
84 | ops.forEach(op => {
85 | xs.push(op);
86 | UtilLinear.insertEnd(crdt, op);
87 | ys.push(func(crdt, op));
88 | crdt.struct = [];
89 | });
90 |
91 | name = 'Linear';
92 | data.push({x: xs, y: ys, type: 'scatter', name: name});
93 |
94 | // array-of-arrays
95 | func = Util.deleteBeginning;
96 | xs = [];
97 | ys = [];
98 |
99 | crdt = new CRDT(mockController(), base, boundary, boundaryStrategy, mult);
100 | ops.forEach(op => {
101 | xs.push(op);
102 | Util.insertRandom(crdt, op);
103 | ys.push(func(crdt, op));
104 | crdt.struct = [];
105 | });
106 |
107 | name = 'Array-of-Arrays';
108 | data.push({x: xs, y: ys, type: 'scatter', name: name});
109 |
110 | Plotly.newPlot('g1', data, {title: title, height: 600});
111 |
112 | // Remote Insertions
113 | title = "Remote Insertions";
114 | data = [];
115 |
116 | // linear
117 | func = UtilLinear.remoteInsertBeginning;
118 | xs = [];
119 | ys = [];
120 | crdt = new CRDTLinear(mockController(), base, boundary, boundaryStrategy, mult);
121 | crdt.insertText = function() {};
122 | crdt.deleteText = function() {};
123 | ops.forEach(op => {
124 | xs.push(op);
125 | ys.push(func(crdt, op));
126 | crdt.struct = [];
127 | });
128 |
129 | name = 'Linear';
130 | data.push({x: xs, y: ys, type: 'scatter', name: name});
131 |
132 | // array-of-arrays
133 | func = Util.insertBeginning;
134 | xs = [];
135 | ys = [];
136 |
137 | crdt = new CRDT(mockController(), base, boundary, boundaryStrategy, mult);
138 | ops.forEach(op => {
139 | xs.push(op);
140 | ys.push(func(crdt, op));
141 | crdt.struct = [];
142 | });
143 |
144 | name = 'Array-of-Arrays';
145 | data.push({x: xs, y: ys, type: 'scatter', name: name});
146 |
147 | Plotly.newPlot('g2', data, {title: title, height: 600});
148 |
149 | // Remote Deletions
150 | title = 'Remote Deletions';
151 | data = [];
152 |
153 | // linear
154 | func = UtilLinear.remoteDeleteBeginning;
155 | xs = [];
156 | ys = [];
157 | crdt = new CRDTLinear(mockController(), base, boundary, boundaryStrategy, mult);
158 | crdt.insertText = function() {};
159 | crdt.deleteText = function() {};
160 | ops.forEach(op => {
161 | xs.push(op);
162 | UtilLinear.remoteInsertEnd(crdt, op);
163 | ys.push(func(crdt, op));
164 | crdt.struct = [];
165 | });
166 |
167 | name = 'Linear';
168 | data.push({x: xs, y: ys, type: 'scatter', name: name});
169 |
170 | // array-of-arrays
171 | func = Util.deleteBeginning;
172 | xs = [];
173 | ys = [];
174 |
175 | crdt = new CRDT(mockController(), base, boundary, boundaryStrategy, mult);
176 | ops.forEach(op => {
177 | xs.push(op);
178 | Util.insertRandom(crdt, op);
179 | ys.push(func(crdt, op));
180 | crdt.struct = [];
181 | });
182 |
183 | name = 'Array-of-Arrays';
184 | data.push({x: xs, y: ys, type: 'scatter', name: name});
185 |
186 | Plotly.newPlot('g3', data, {title: title, height: 600});
187 |
--------------------------------------------------------------------------------
/lib/utilLinear.js:
--------------------------------------------------------------------------------
1 | import Char from './char';
2 | import CRDT from './crdtLinear';
3 | import UUID from 'uuid/v1';
4 |
5 | function mockController() {
6 | return {
7 | siteId: UUID(),
8 | broadcastInsertion: function() {},
9 | broadcastDeletion: function() {},
10 | insertIntoEditor: function() {},
11 | deleteFromEditor: function() {},
12 | vector: {
13 | localVersion: {
14 | counter: 0
15 | },
16 | increment: function() {
17 | this.localVersion.counter++;
18 | }
19 | }
20 | }
21 | }
22 |
23 | function insertRandom(crdt, numberOfOperations) {
24 | const start = Date.now();
25 | let index;
26 |
27 | for(let i = 0; i < numberOfOperations; i++) {
28 | index = Math.floor(Math.random() * i);
29 | crdt.handleLocalInsert('a', index);
30 | }
31 |
32 | const end = Date.now();
33 | return end - start;
34 | }
35 |
36 | function remoteInsertRandom(crdt, numberOfOperations) {
37 | const chars = generateChars(numberOfOperations);
38 | const randomChars = shuffle(chars);
39 |
40 | return remoteInsert(crdt, randomChars);
41 | }
42 |
43 | function insertBeginning(crdt, numberOfOperations) {
44 | const start = Date.now();
45 |
46 | for(let i = 0; i < numberOfOperations; i++) {
47 | crdt.handleLocalInsert('a', 0);
48 | }
49 |
50 | const end = Date.now();
51 | return end - start;
52 | }
53 |
54 | function insertEnd(crdt, numberOfOperations) {
55 | const start = Date.now();
56 |
57 | for(let i = 0; i < numberOfOperations; i++) {
58 | crdt.handleLocalInsert('a', i);
59 | }
60 |
61 | const end = Date.now();
62 | return end - start;
63 | }
64 |
65 | function remoteInsertBeginning(crdt, numberOfOperations) {
66 | const chars = generateChars(numberOfOperations);
67 | const descChars = chars.reverse();
68 |
69 | return remoteInsert(crdt, descChars);
70 | }
71 |
72 | function remoteInsertEnd(crdt, numberOfOperations) {
73 | const ascChars = generateChars(numberOfOperations);
74 |
75 | return remoteInsert(crdt, ascChars);
76 | }
77 |
78 | function remoteInsert(crdt, chars) {
79 | const start = Date.now();
80 |
81 | chars.forEach(char => crdt.handleRemoteInsert(char));
82 |
83 | const end = Date.now();
84 | return end - start;
85 | }
86 |
87 | function deleteRandom(crdt) {
88 | const start = Date.now();
89 | let index;
90 |
91 | for(let i = crdt.struct.length - 1; i >= 0; i--) {
92 | index = Math.floor(Math.random() * i);
93 | crdt.handleLocalDelete(index);
94 | }
95 |
96 | const end = Date.now();
97 | return end - start;
98 | }
99 |
100 | function remoteDeleteRandom(crdt) {
101 | let toDel = [];
102 | crdt.struct.forEach(char => toDel.push(char));
103 | const randomToDel = shuffle(toDel);
104 | return remoteDelete(crdt, randomToDel);
105 | }
106 |
107 | function deleteBeginning(crdt) {
108 | const start = Date.now();
109 |
110 | for(let i = crdt.struct.length - 1; i >= 0; i--) {
111 | crdt.handleLocalDelete(0);
112 | }
113 |
114 | const end = Date.now();
115 | return end - start;
116 | }
117 |
118 | function remoteDeleteBeginning(crdt) {
119 | let toDel = [];
120 | crdt.struct.forEach(char => toDel.push(char));
121 | return remoteDelete(crdt, toDel);
122 | }
123 |
124 | function deleteEnd(crdt) {
125 | const start = Date.now();
126 |
127 | for(let i = crdt.struct.length - 1; i >= 0; i--) {
128 | crdt.handleLocalDelete(i);
129 | }
130 |
131 | const end = Date.now();
132 | return end - start;
133 | }
134 |
135 | function remoteDeleteEnd(crdt) {
136 | let toDel = [];
137 | crdt.struct.forEach(char => toDel.push(char));
138 | const reverseToDel = toDel.reverse();
139 | return remoteDelete(crdt, reverseToDel);
140 | }
141 |
142 | function remoteDelete(crdt, chars) {
143 | const start = Date.now();
144 |
145 | chars.forEach(char => crdt.handleRemoteDelete(char));
146 |
147 | const end = Date.now();
148 | return end - start;
149 | }
150 |
151 | function generateChars(numberOfOperations) {
152 | const structs = generateRemoteStructs(numberOfOperations);
153 | const charObjects = [];
154 | for (let i = 0; i < structs[0].length; i++) {
155 | structs.forEach(struct => charObjects.push(struct[i]));
156 | }
157 | return charObjects;
158 | }
159 |
160 | function generateRemoteStructs(numberOfOperations) {
161 | const remoteCRDTs = generateRemoteCRDTs(Math.log10(numberOfOperations));
162 |
163 | const numOfOps = numberOfOperations / remoteCRDTs.length;
164 |
165 | remoteCRDTs.forEach(crdt => insertRandom(crdt, numOfOps));
166 |
167 | return remoteCRDTs.map(crdt => crdt.struct);
168 | }
169 |
170 | function generateRemoteCRDTs(num) {
171 | let CRDTs = [];
172 | let crdt;
173 | for (let i = 0; i < num; i++) {
174 | crdt = new CRDT(mockController());
175 | CRDTs.push(crdt);
176 | }
177 | return CRDTs;
178 | }
179 |
180 | function shuffle(a) {
181 | for (let i = a.length - 1; i > 0; i--) {
182 | const j = Math.floor(Math.random() * (i + 1));
183 | [a[i], a[j]] = [a[j], a[i]];
184 | }
185 | return a;
186 | }
187 |
188 | function avgIdLength(crdt) {
189 | const idArray = crdt.struct.map(char => char.position.map(id => id.digit).join(''));
190 | const digitLengthSum = idArray.reduce((acc, id) => { return acc + id.length }, 0);
191 |
192 | return Math.floor(digitLengthSum / idArray.length);
193 | }
194 |
195 | function avgPosLength(crdt) {
196 | const posArray = crdt.struct.map(char => char.position.length);
197 | const posLengthSum = posArray.reduce((acc, len) => { return acc + len }, 0);
198 |
199 | return Math.floor(posLengthSum / posArray.length);
200 | }
201 |
202 | function average(time, operations) {
203 | return time / operations;
204 | }
205 |
206 | export {
207 | avgIdLength,
208 | average,
209 | insertRandom,
210 | remoteInsertRandom,
211 | insertEnd,
212 | remoteInsertEnd,
213 | insertBeginning,
214 | remoteInsertBeginning,
215 | deleteRandom,
216 | remoteDeleteRandom,
217 | deleteEnd,
218 | remoteDeleteEnd,
219 | deleteBeginning,
220 | remoteDeleteBeginning,
221 | };
222 |
--------------------------------------------------------------------------------
/lib/crdtLinear.js:
--------------------------------------------------------------------------------
1 | import Identifier from './identifier';
2 | import Char from './char';
3 |
4 | class CRDT {
5 | constructor(controller, base=32, boundary=10, strategy='random', mult=2) {
6 | this.controller = controller;
7 | this.vector = controller.vector;
8 | this.struct = [];
9 | this.siteId = controller.siteId;
10 | this.text = "";
11 | this.base = base;
12 | this.boundary = boundary;
13 | this.strategy = strategy;
14 | this.strategyCache = [];
15 | this.mult = mult;
16 | }
17 |
18 | handleLocalInsert(val, index) {
19 | this.vector.increment();
20 |
21 | const char = this.generateChar(val, index);
22 | this.insertChar(index, char);
23 | this.insertText(char.value, index);
24 |
25 | this.controller.broadcastInsertion(char);
26 | }
27 |
28 | handleRemoteInsert(char) {
29 | const index = this.findInsertIndex(char);
30 |
31 | this.insertChar(index, char);
32 | this.insertText(char.value, index);
33 |
34 | this.controller.insertIntoEditor(char.value, index, char.siteId);
35 | }
36 |
37 | insertChar(index, char) {
38 | this.struct.splice(index, 0, char);
39 | }
40 |
41 | handleLocalDelete(idx) {
42 | this.vector.increment();
43 |
44 | const char = this.struct.splice(idx, 1)[0];
45 | this.deleteText(idx);
46 |
47 | this.controller.broadcastDeletion(char);
48 | }
49 |
50 | handleRemoteDelete(char, siteId) {
51 | const index = this.findIndexByPosition(char);
52 | this.struct.splice(index, 1);
53 |
54 | this.controller.deleteFromEditor(char.value, index, siteId);
55 | this.deleteText(index);
56 | }
57 |
58 | findInsertIndex(char) {
59 | let left = 0;
60 | let right = this.struct.length - 1;
61 | let mid, compareNum;
62 |
63 | if (this.struct.length === 0 || char.compareTo(this.struct[left]) < 0) {
64 | return left;
65 | } else if (char.compareTo(this.struct[right]) > 0) {
66 | return this.struct.length;
67 | }
68 |
69 | while (left + 1 < right) {
70 | mid = Math.floor(left + (right - left) / 2);
71 | compareNum = char.compareTo(this.struct[mid]);
72 |
73 | if (compareNum === 0) {
74 | return mid;
75 | } else if (compareNum > 0) {
76 | left = mid;
77 | } else {
78 | right = mid;
79 | }
80 | }
81 |
82 | return char.compareTo(this.struct[left]) === 0 ? left : right;
83 | }
84 |
85 | findIndexByPosition(char) {
86 | let left = 0;
87 | let right = this.struct.length - 1;
88 | let mid, compareNum;
89 |
90 | if (this.struct.length === 0) {
91 | throw new Error("Character does not exist in CRDT.");
92 | }
93 |
94 | while (left + 1 < right) {
95 | mid = Math.floor(left + (right - left) / 2);
96 | compareNum = char.compareTo(this.struct[mid]);
97 |
98 | if (compareNum === 0) {
99 | return mid;
100 | } else if (compareNum > 0) {
101 | left = mid;
102 | } else {
103 | right = mid;
104 | }
105 | }
106 |
107 | if (char.compareTo(this.struct[left]) === 0) {
108 | return left;
109 | } else if (char.compareTo(this.struct[right]) === 0) {
110 | return right;
111 | } else {
112 | throw new Error("Character does not exist in CRDT.");
113 | }
114 | }
115 |
116 | generateChar(val, index) {
117 | const posBefore = (this.struct[index - 1] && this.struct[index - 1].position) || [];
118 | const posAfter = (this.struct[index] && this.struct[index].position) || [];
119 | const newPos = this.generatePosBetween(posBefore, posAfter);
120 | const localCounter = this.vector.localVersion.counter;
121 |
122 | return new Char(val, localCounter, this.siteId, newPos);
123 | }
124 |
125 | retrieveStrategy(level) {
126 | if (this.strategyCache[level]) return this.strategyCache[level];
127 | let strategy;
128 |
129 | switch (this.strategy) {
130 | case 'plus':
131 | strategy = '+';
132 | break;
133 | case 'minus':
134 | strategy = '-';
135 | break;
136 | case 'random':
137 | strategy = Math.round(Math.random()) === 0 ? '+' : '-';
138 | break;
139 | case 'every2nd':
140 | strategy = ((level+1) % 2) === 0 ? '-' : '+';
141 | break;
142 | case 'every3rd':
143 | strategy = ((level+1) % 3) === 0 ? '-' : '+';
144 | break;
145 | default:
146 | strategy = ((level+1) % 2) === 0 ? '-' : '+';
147 | break;
148 | }
149 |
150 | this.strategyCache[level] = strategy;
151 | return strategy;
152 | }
153 |
154 | generatePosBetween(pos1, pos2, newPos=[], level=0) {
155 | let base = Math.pow(this.mult, level) * this.base;
156 | let boundaryStrategy = this.retrieveStrategy(level);
157 |
158 | let id1 = pos1[0] || new Identifier(0, this.siteId);
159 | let id2 = pos2[0] || new Identifier(base, this.siteId);
160 |
161 | if (id2.digit - id1.digit > 1) {
162 |
163 | let newDigit = this.generateIdBetween(id1.digit, id2.digit, boundaryStrategy);
164 | newPos.push(new Identifier(newDigit, this.siteId));
165 | return newPos;
166 |
167 | } else if (id2.digit - id1.digit === 1) {
168 |
169 | newPos.push(id1);
170 | return this.generatePosBetween(pos1.slice(1), [], newPos, level+1);
171 |
172 | } else if (id1.digit === id2.digit) {
173 | if (id1.siteId < id2.siteId) {
174 | newPos.push(id1);
175 | return this.generatePosBetween(pos1.slice(1), [], newPos, level+1);
176 | } else if (id1.siteId === id2.siteId) {
177 | newPos.push(id1);
178 | return this.generatePosBetween(pos1.slice(1), pos2.slice(1), newPos, level+1);
179 | } else {
180 | throw new Error("Fix Position Sorting");
181 | }
182 | }
183 | }
184 | /*
185 | Math.random gives you a range that is inclusive of the min and exclusive of the max
186 | so have to add and subtract ones to get them all into that format
187 |
188 | if max - min <= boundary, the boundary doesn't matter
189 | newDigit > min, newDigit < max
190 | ie (min+1...max)
191 | so, min = min + 1
192 | if max - min > boundary and the boundary is negative
193 | min = max - boundary
194 | newDigit >= min, newDigit < max
195 | ie (min...max)
196 | if max - min > boundary and the boundary is positive
197 | max = min + boundary
198 | newDigit > min, newDigit <= max
199 | ie (min+1...max+1)
200 | so, min = min + 1 and max = max + 1
201 |
202 | now all are (min...max)
203 | */
204 | generateIdBetween(min, max, boundaryStrategy) {
205 | if ((max - min) < this.boundary) {
206 | min = min + 1;
207 | } else {
208 | if (boundaryStrategy === '-') {
209 | min = max - this.boundary;
210 | } else {
211 | min = min + 1;
212 | max = min + this.boundary;
213 | }
214 | }
215 | return Math.floor(Math.random() * (max - min)) + min;
216 | }
217 |
218 | insertText(val, index) {
219 | this.text = this.text.slice(0, index) + val + this.text.slice(index);
220 | }
221 |
222 | deleteText(index) {
223 | this.text = this.text.slice(0, index) + this.text.slice(index + 1);
224 | }
225 |
226 | populateText() {
227 | this.text = this.struct.map(char => char.value).join('');
228 | }
229 | }
230 |
231 | export default CRDT;
232 |
--------------------------------------------------------------------------------
/performance/utilLinear.js:
--------------------------------------------------------------------------------
1 | import Char from '../lib/char';
2 | import CRDT from '../lib/crdtLinear';
3 | import { mockController } from './scriptLinear';
4 |
5 | const CELL_1_SIZE = 17;
6 | const CELL_2_SIZE = 20;
7 | const CELL_3_SIZE = 21;
8 | const CELL_4_SIZE = 16;
9 | const CELL_5_SIZE = 15;
10 |
11 | function insertRandom(crdt, numberOfOperations) {
12 | const start = Date.now();
13 | let index;
14 |
15 | for(let i = 0; i < numberOfOperations; i++) {
16 | index = Math.floor(Math.random() * i);
17 | crdt.handleLocalInsert('a', index);
18 | }
19 |
20 | const end = Date.now();
21 | return end - start;
22 | }
23 |
24 | function remoteInsertRandom(crdt, numberOfOperations) {
25 | const chars = generateChars(numberOfOperations);
26 | const randomChars = shuffle(chars);
27 |
28 | return remoteInsert(crdt, randomChars);
29 | }
30 |
31 | function insertBeginning(crdt, numberOfOperations) {
32 | const start = Date.now();
33 |
34 | for(let i = 0; i < numberOfOperations; i++) {
35 | crdt.handleLocalInsert('a', 0);
36 | }
37 |
38 | const end = Date.now();
39 | return end - start;
40 | }
41 |
42 | function insertEnd(crdt, numberOfOperations) {
43 | const start = Date.now();
44 |
45 | for(let i = 0; i < numberOfOperations; i++) {
46 | crdt.handleLocalInsert('a', i);
47 | }
48 |
49 | const end = Date.now();
50 | return end - start;
51 | }
52 |
53 | function remoteInsertBeginning(crdt, numberOfOperations) {
54 | const chars = generateChars(numberOfOperations);
55 | const descChars = chars.reverse();
56 |
57 | return remoteInsert(crdt, descChars);
58 | }
59 |
60 | function remoteInsertEnd(crdt, numberOfOperations) {
61 | const ascChars = generateChars(numberOfOperations);
62 |
63 | return remoteInsert(crdt, ascChars);
64 | }
65 |
66 | function remoteInsert(crdt, chars) {
67 | const start = Date.now();
68 |
69 | chars.forEach(char => crdt.handleRemoteInsert(char));
70 |
71 | const end = Date.now();
72 | return end - start;
73 | }
74 |
75 | function deleteRandom(crdt) {
76 | const start = Date.now();
77 | let index;
78 |
79 | for(let i = crdt.struct.length - 1; i >= 0; i--) {
80 | index = Math.floor(Math.random() * i);
81 | crdt.handleLocalDelete(index);
82 | }
83 |
84 | const end = Date.now();
85 | return end - start;
86 | }
87 |
88 | function remoteDeleteRandom(crdt) {
89 | let toDel = [];
90 | crdt.struct.forEach(char => toDel.push(char));
91 | const randomToDel = shuffle(toDel);
92 | return remoteDelete(crdt, randomToDel);
93 | }
94 |
95 | function deleteBeginning(crdt) {
96 | const start = Date.now();
97 |
98 | for(let i = crdt.struct.length - 1; i >= 0; i--) {
99 | crdt.handleLocalDelete(0);
100 | }
101 |
102 | const end = Date.now();
103 | return end - start;
104 | }
105 |
106 | function remoteDeleteBeginning(crdt) {
107 | let toDel = [];
108 | crdt.struct.forEach(char => toDel.push(char));
109 | return remoteDelete(crdt, toDel);
110 | }
111 |
112 | function deleteEnd(crdt) {
113 | const start = Date.now();
114 |
115 | for(let i = crdt.struct.length - 1; i >= 0; i--) {
116 | crdt.handleLocalDelete(i);
117 | }
118 |
119 | const end = Date.now();
120 | return end - start;
121 | }
122 |
123 | function remoteDeleteEnd(crdt) {
124 | let toDel = [];
125 | crdt.struct.forEach(char => toDel.push(char));
126 | const reverseToDel = toDel.reverse();
127 | return remoteDelete(crdt, reverseToDel);
128 | }
129 |
130 | function remoteDelete(crdt, chars) {
131 | const start = Date.now();
132 |
133 | chars.forEach(char => crdt.handleRemoteDelete(char));
134 |
135 | const end = Date.now();
136 | return end - start;
137 | }
138 |
139 | function generateChars(numberOfOperations) {
140 | const structs = generateRemoteStructs(numberOfOperations);
141 | const charObjects = [];
142 | for (let i = 0; i < structs[0].length; i++) {
143 | structs.forEach(struct => charObjects.push(struct[i]));
144 | }
145 | return charObjects;
146 | }
147 |
148 | function generateRemoteStructs(numberOfOperations) {
149 | const remoteCRDTs = generateRemoteCRDTs(Math.log10(numberOfOperations));
150 |
151 | const numOfOps = numberOfOperations / remoteCRDTs.length;
152 |
153 | remoteCRDTs.forEach(crdt => insertRandom(crdt, numOfOps));
154 |
155 | return remoteCRDTs.map(crdt => crdt.struct);
156 | }
157 |
158 | function generateRemoteCRDTs(num) {
159 | let CRDTs = [];
160 | let crdt;
161 | for (let i = 0; i < num; i++) {
162 | crdt = new CRDT(mockController());
163 | CRDTs.push(crdt);
164 | }
165 | return CRDTs;
166 | }
167 |
168 | function shuffle(a) {
169 | for (let i = a.length - 1; i > 0; i--) {
170 | const j = Math.floor(Math.random() * (i + 1));
171 | [a[i], a[j]] = [a[j], a[i]];
172 | }
173 | return a;
174 | }
175 |
176 | function avgIdLength(crdt) {
177 | const idArray = crdt.struct.map(char => char.position.map(id => id.digit).join(''));
178 | const digitLengthSum = idArray.reduce((acc, id) => { return acc + id.length }, 0);
179 |
180 | return Math.floor(digitLengthSum / idArray.length);
181 | }
182 |
183 | function avgPosLength(crdt) {
184 | const posArray = crdt.struct.map(char => char.position.length);
185 | const posLengthSum = posArray.reduce((acc, len) => { return acc + len }, 0);
186 |
187 | return Math.floor(posLengthSum / posArray.length);
188 | }
189 |
190 | function average(time, operations) {
191 | return time / operations;
192 | }
193 |
194 | function addPadding(value, cellSize) {
195 | value = String(value);
196 |
197 | if (value.length > cellSize) {
198 | value = value.slice(0, cellSize);
199 | }
200 |
201 | const padding = ((cellSize - value.length) / 2);
202 | return (' ').repeat(Math.floor(padding)) + value + (' ').repeat(Math.ceil(padding));
203 | }
204 |
205 | function addRowWithId(operations, crdt, func) {
206 | const totalTime = func(crdt, operations);
207 | const cell1 = addPadding(operations, CELL_1_SIZE);
208 | const cell2 = addPadding(totalTime, CELL_2_SIZE);
209 | const cell3 = addPadding(average(totalTime, operations), CELL_3_SIZE);
210 | const cell4 = addPadding(avgIdLength(crdt), CELL_4_SIZE);
211 | const cell5 = addPadding(avgPosLength(crdt), CELL_5_SIZE);
212 |
213 | return `|${cell1}|${cell2}|${cell3}|${cell4}|${cell5}|
214 | ${'-'.repeat(95)}`
215 |
216 | }
217 |
218 | function addRow(operations, crdt, func) {
219 | const totalTime = func(crdt, operations);
220 | const cell1 = addPadding(operations, CELL_1_SIZE);
221 | const cell2 = addPadding(totalTime, CELL_2_SIZE);
222 | const cell3 = addPadding(average(totalTime, operations), CELL_3_SIZE);
223 |
224 | return `|${cell1}|${cell2}|${cell3}|
225 | ${'-'.repeat(62)}`
226 | }
227 |
228 | function getTimestamp() {
229 | const now = new Date();
230 | const year = now.getUTCFullYear();
231 | const month = now.getUTCMonth() + 1;
232 | const date = now.getUTCDate();
233 | const hours = now.getUTCHours();
234 | const minutes = now.getUTCMinutes();
235 | const seconds = now.getUTCSeconds();
236 |
237 | return `${year}-${month}-${date} ${hours}:${minutes}:${seconds}`;
238 | }
239 |
240 | export {
241 | addRow,
242 | addRowWithId,
243 | insertRandom,
244 | remoteInsertRandom,
245 | insertEnd,
246 | remoteInsertEnd,
247 | insertBeginning,
248 | remoteInsertBeginning,
249 | deleteRandom,
250 | remoteDeleteRandom,
251 | deleteEnd,
252 | remoteDeleteEnd,
253 | deleteBeginning,
254 | remoteDeleteBeginning,
255 | getTimestamp
256 | };
257 |
--------------------------------------------------------------------------------
/performance/comparisons/linearArray/tripleBase/2017-11-17 5:41:7.log:
--------------------------------------------------------------------------------
1 |
2 | #### PERFORMANCE METRICS
3 | Base: 32 | Boundary: 10 | Strategy: minus
4 | ================================================================================================
5 |
6 | ## RANDOM
7 | ---------
8 |
9 | # LOCAL INSERTIONS
10 | -----------------------------------------------------------------------------------------------
11 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
12 | | | (in milliseconds) | (in milliseconds) | | Length |
13 | -----------------------------------------------------------------------------------------------
14 |
15 | | 10 | 1 | 0.1 | 1 | 1 |
16 | -----------------------------------------------------------------------------------------------
17 | | 100 | 1 | 0.01 | 5 | 2 |
18 | -----------------------------------------------------------------------------------------------
19 |
20 | # LOCAL DELETIONS
21 | --------------------------------------------------------------
22 | | # of Operations | Total Execute Time | Avg. Operation Time |
23 | | | (in milliseconds) | (in milliseconds) |
24 | --------------------------------------------------------------
25 | | 10 | 0 | 0 |
26 | --------------------------------------------------------------
27 | | 100 | 0 | 0 |
28 | --------------------------------------------------------------
29 |
30 | # REMOTE INSERTIONS
31 | --------------------------------------------------------------
32 | | # of Operations | Total Execute Time | Avg. Operation Time |
33 | | | (in milliseconds) | (in milliseconds) |
34 | --------------------------------------------------------------
35 | | 10 | 0 | 0 |
36 | --------------------------------------------------------------
37 | | 100 | 1 | 0.01 |
38 | --------------------------------------------------------------
39 |
40 | # REMOTE DELETIONS
41 | --------------------------------------------------------------
42 | | # of Operations | Total Execute Time | Avg. Operation Time |
43 | | | (in milliseconds) | (in milliseconds) |
44 | --------------------------------------------------------------
45 | | 10 | 0 | 0 |
46 | --------------------------------------------------------------
47 | | 100 | 1 | 0.01 |
48 | --------------------------------------------------------------
49 |
50 |
51 | ## AT THE BEGINNING
52 | -------------------
53 |
54 | # LOCAL INSERTIONS
55 | -----------------------------------------------------------------------------------------------
56 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
57 | | | (in milliseconds) | (in milliseconds) | | Length |
58 | -----------------------------------------------------------------------------------------------
59 | | 10 | 0 | 0 | 2 | 1 |
60 | -----------------------------------------------------------------------------------------------
61 | | 100 | 0 | 0 | 4 | 2 |
62 | -----------------------------------------------------------------------------------------------
63 |
64 | # LOCAL DELETIONS
65 | --------------------------------------------------------------
66 | | # of Operations | Total Execute Time | Avg. Operation Time |
67 | | | (in milliseconds) | (in milliseconds) |
68 | --------------------------------------------------------------
69 | | 10 | 0 | 0 |
70 | --------------------------------------------------------------
71 | | 100 | 0 | 0 |
72 | --------------------------------------------------------------
73 |
74 | # REMOTE INSERTIONS
75 | --------------------------------------------------------------
76 | | # of Operations | Total Execute Time | Avg. Operation Time |
77 | | | (in milliseconds) | (in milliseconds) |
78 | --------------------------------------------------------------
79 | | 10 | 0 | 0 |
80 | --------------------------------------------------------------
81 | | 100 | 1 | 0.01 |
82 | --------------------------------------------------------------
83 |
84 | # REMOTE DELETIONS
85 | --------------------------------------------------------------
86 | | # of Operations | Total Execute Time | Avg. Operation Time |
87 | | | (in milliseconds) | (in milliseconds) |
88 | --------------------------------------------------------------
89 | | 10 | 0 | 0 |
90 | --------------------------------------------------------------
91 | | 100 | 0 | 0 |
92 | --------------------------------------------------------------
93 |
94 |
95 | ## AT THE END
96 | -------------
97 |
98 | # LOCAL INSERTIONS
99 | -----------------------------------------------------------------------------------------------
100 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
101 | | | (in milliseconds) | (in milliseconds) | | Length |
102 | -----------------------------------------------------------------------------------------------
103 | | 10 | 0 | 0 | 4 | 2 |
104 | -----------------------------------------------------------------------------------------------
105 | | 100 | 2 | 0.02 | 142 | 18 |
106 | -----------------------------------------------------------------------------------------------
107 |
108 | # LOCAL DELETIONS
109 | --------------------------------------------------------------
110 | | # of Operations | Total Execute Time | Avg. Operation Time |
111 | | | (in milliseconds) | (in milliseconds) |
112 | --------------------------------------------------------------
113 | | 10 | 0 | 0 |
114 | --------------------------------------------------------------
115 | | 100 | 0 | 0 |
116 | --------------------------------------------------------------
117 |
118 | # REMOTE INSERTIONS
119 | --------------------------------------------------------------
120 | | # of Operations | Total Execute Time | Avg. Operation Time |
121 | | | (in milliseconds) | (in milliseconds) |
122 | --------------------------------------------------------------
123 | | 10 | 0 | 0 |
124 | --------------------------------------------------------------
125 | | 100 | 0 | 0 |
126 | --------------------------------------------------------------
127 |
128 | # REMOTE DELETIONS
129 | --------------------------------------------------------------
130 | | # of Operations | Total Execute Time | Avg. Operation Time |
131 | | | (in milliseconds) | (in milliseconds) |
132 | --------------------------------------------------------------
133 | | 10 | 0 | 0 |
134 | --------------------------------------------------------------
135 | | 100 | 1 | 0.01 |
136 | --------------------------------------------------------------
137 |
--------------------------------------------------------------------------------
/performance/comparisons/linearArray/tripleBase/2017-11-17 5:40:56.log:
--------------------------------------------------------------------------------
1 |
2 | #### PERFORMANCE METRICS
3 | Base: 3200 | Boundary: 3000 | Strategy: minus
4 | ================================================================================================
5 |
6 | ## RANDOM
7 | ---------
8 |
9 | # LOCAL INSERTIONS
10 | -----------------------------------------------------------------------------------------------
11 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
12 | | | (in milliseconds) | (in milliseconds) | | Length |
13 | -----------------------------------------------------------------------------------------------
14 |
15 | | 10 | 1 | 0.1 | 3 | 1 |
16 | -----------------------------------------------------------------------------------------------
17 | | 100 | 1 | 0.01 | 4 | 1 |
18 | -----------------------------------------------------------------------------------------------
19 |
20 | # LOCAL DELETIONS
21 | --------------------------------------------------------------
22 | | # of Operations | Total Execute Time | Avg. Operation Time |
23 | | | (in milliseconds) | (in milliseconds) |
24 | --------------------------------------------------------------
25 | | 10 | 0 | 0 |
26 | --------------------------------------------------------------
27 | | 100 | 0 | 0 |
28 | --------------------------------------------------------------
29 |
30 | # REMOTE INSERTIONS
31 | --------------------------------------------------------------
32 | | # of Operations | Total Execute Time | Avg. Operation Time |
33 | | | (in milliseconds) | (in milliseconds) |
34 | --------------------------------------------------------------
35 | | 10 | 0 | 0 |
36 | --------------------------------------------------------------
37 | | 100 | 0 | 0 |
38 | --------------------------------------------------------------
39 |
40 | # REMOTE DELETIONS
41 | --------------------------------------------------------------
42 | | # of Operations | Total Execute Time | Avg. Operation Time |
43 | | | (in milliseconds) | (in milliseconds) |
44 | --------------------------------------------------------------
45 | | 10 | 0 | 0 |
46 | --------------------------------------------------------------
47 | | 100 | 0 | 0 |
48 | --------------------------------------------------------------
49 |
50 |
51 | ## AT THE BEGINNING
52 | -------------------
53 |
54 | # LOCAL INSERTIONS
55 | -----------------------------------------------------------------------------------------------
56 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
57 | | | (in milliseconds) | (in milliseconds) | | Length |
58 | -----------------------------------------------------------------------------------------------
59 | | 10 | 0 | 0 | 3 | 1 |
60 | -----------------------------------------------------------------------------------------------
61 | | 100 | 3 | 0.03 | 6 | 3 |
62 | -----------------------------------------------------------------------------------------------
63 |
64 | # LOCAL DELETIONS
65 | --------------------------------------------------------------
66 | | # of Operations | Total Execute Time | Avg. Operation Time |
67 | | | (in milliseconds) | (in milliseconds) |
68 | --------------------------------------------------------------
69 | | 10 | 0 | 0 |
70 | --------------------------------------------------------------
71 | | 100 | 0 | 0 |
72 | --------------------------------------------------------------
73 |
74 | # REMOTE INSERTIONS
75 | --------------------------------------------------------------
76 | | # of Operations | Total Execute Time | Avg. Operation Time |
77 | | | (in milliseconds) | (in milliseconds) |
78 | --------------------------------------------------------------
79 | | 10 | 0 | 0 |
80 | --------------------------------------------------------------
81 | | 100 | 0 | 0 |
82 | --------------------------------------------------------------
83 |
84 | # REMOTE DELETIONS
85 | --------------------------------------------------------------
86 | | # of Operations | Total Execute Time | Avg. Operation Time |
87 | | | (in milliseconds) | (in milliseconds) |
88 | --------------------------------------------------------------
89 | | 10 | 0 | 0 |
90 | --------------------------------------------------------------
91 | | 100 | 1 | 0.01 |
92 | --------------------------------------------------------------
93 |
94 |
95 | ## AT THE END
96 | -------------
97 |
98 | # LOCAL INSERTIONS
99 | -----------------------------------------------------------------------------------------------
100 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
101 | | | (in milliseconds) | (in milliseconds) | | Length |
102 | -----------------------------------------------------------------------------------------------
103 | | 10 | 0 | 0 | 5 | 1 |
104 | -----------------------------------------------------------------------------------------------
105 | | 100 | 1 | 0.01 | 36 | 6 |
106 | -----------------------------------------------------------------------------------------------
107 |
108 | # LOCAL DELETIONS
109 | --------------------------------------------------------------
110 | | # of Operations | Total Execute Time | Avg. Operation Time |
111 | | | (in milliseconds) | (in milliseconds) |
112 | --------------------------------------------------------------
113 | | 10 | 0 | 0 |
114 | --------------------------------------------------------------
115 | | 100 | 0 | 0 |
116 | --------------------------------------------------------------
117 |
118 | # REMOTE INSERTIONS
119 | --------------------------------------------------------------
120 | | # of Operations | Total Execute Time | Avg. Operation Time |
121 | | | (in milliseconds) | (in milliseconds) |
122 | --------------------------------------------------------------
123 | | 10 | 0 | 0 |
124 | --------------------------------------------------------------
125 | | 100 | 0 | 0 |
126 | --------------------------------------------------------------
127 |
128 | # REMOTE DELETIONS
129 | --------------------------------------------------------------
130 | | # of Operations | Total Execute Time | Avg. Operation Time |
131 | | | (in milliseconds) | (in milliseconds) |
132 | --------------------------------------------------------------
133 | | 10 | 0 | 0 |
134 | --------------------------------------------------------------
135 | | 100 | 1 | 0.01 |
136 | --------------------------------------------------------------
137 |
--------------------------------------------------------------------------------
/lib/cssColors.js:
--------------------------------------------------------------------------------
1 | // export default [
2 | // 'rgba(255,127,80,0.5)',
3 | // 'rgba(255,140,0,0.5)',
4 | // 'rgba(255,215,0,0.5)',
5 | // 'rgba(255,165,0,0.5)',
6 | // 'rgba(255,69,0,0.5)',
7 | // 'rgba(255,99,71,0.5)',
8 | // 'rgba(255,20,147,0.5)',
9 | // 'rgba(255,105,180,0.5)',
10 | // 'rgba(255,182,193,0.5)',
11 | // 'rgba(199,21,133,0.5)',
12 | // 'rgba(255,192,203,0.5)',
13 | // 'rgba(219,112,147,0.5)',
14 | // 'rgba(255,255,0,0.5)',
15 | // 'rgba(244,164,96,0.5)',
16 | // 'rgba(160,82,45,0.5)',
17 | // 'rgba(210,180,140,0.5)',
18 | // 'rgba(165,42,42,0.5)',
19 | // 'rgba(222,184,135,0.5)',
20 | // 'rgba(210,105,30,0.5)',
21 | // 'rgba(184,134,11,0.5)',
22 | // 'rgba(128,0,0,0.5)',
23 | // 'rgba(188,143,143,0.5)',
24 | // 'rgba(127,255,0,0.5)',
25 | // 'rgba(0,100,0,0.5)',
26 | // 'rgba(85,107,47,0.5)',
27 | // 'rgba(143,188,143,0.5)',
28 | // 'rgba(34,139,34,0.5)',
29 | // 'rgba(0,128,0,0.5)',
30 | // 'rgba(173,255,47,0.5)',
31 | // 'rgba(124,252,0,0.5)',
32 | // 'rgba(144,238,144,0.5)',
33 | // 'rgba(0,255,0,0.5)',
34 | // 'rgba(50,205,50,0.5)',
35 | // 'rgba(60,179,113,0.5)',
36 | // 'rgba(0,250,154,0.5)',
37 | // 'rgba(128,128,0,0.5)',
38 | // 'rgba(107,142,35,0.5)',
39 | // 'rgba(95,158,160,0.5)',
40 | // 'rgba(0,255,255,0.5)',
41 | // 'rgba(0,139,139,0.5)',
42 | // 'rgba(0,0,255,0.5)',
43 | // 'rgba(138,43,226,0.5)',
44 | // 'rgba(75,0,130,0.5)',
45 | // 'rgba(72,61,139,0.5)',
46 | // 'rgba(65,105,225,0.5)'
47 | // ];
48 |
49 | export default [
50 | 'rgba(255, 0, 255, 0.5)',
51 | 'rgba(255, 51, 255, 0.5)',
52 | 'rgba(204, 0, 204, 0.5)',
53 | 'rgba(255, 102, 255, 0.5)',
54 | 'rgba(204, 51, 204, 0.5)',
55 | 'rgba(153, 0, 153, 0.5)',
56 | 'rgba(255, 153, 255, 0.5)',
57 | 'rgba(204, 102, 204, 0.5)',
58 | 'rgba(153, 51, 153, 0.5)',
59 | 'rgba(102, 0, 102, 0.5)',
60 | 'rgba(255, 204, 255, 0.5)',
61 | 'rgba(204, 153, 204, 0.5)',
62 | 'rgba(153, 102, 153, 0.5)',
63 | 'rgba(17, 117, 232, 0.5)',
64 | 'rgba(102, 51, 102, 0.5)',
65 | 'rgba(51, 0, 51, 0.5)',
66 | 'rgba(204, 0, 255, 0.5)',
67 | 'rgba(204, 51, 255, 0.5)',
68 | 'rgba(153, 0, 204, 0.5)',
69 | 'rgba(204, 102, 255, 0.5)',
70 | 'rgba(153, 51, 204, 0.5)',
71 | 'rgba(102, 0, 153, 0.5)',
72 | 'rgba(204, 153, 255, 0.5)',
73 | 'rgba(153, 102, 204, 0.5)',
74 | 'rgba(102, 51, 153, 0.5)',
75 | 'rgba(51, 0, 102, 0.5)',
76 | 'rgba(153, 0, 255, 0.5)',
77 | 'rgba(153, 51, 255, 0.5)',
78 | 'rgba(102, 0, 204, 0.5)',
79 | 'rgba(153, 102, 255, 0.5)',
80 | 'rgba(102, 51, 204, 0.5)',
81 | 'rgba(51, 0, 153, 0.5)',
82 | 'rgba(102, 0, 255, 0.5)',
83 | 'rgba(102, 51, 255, 0.5)',
84 | 'rgba(51, 0, 204, 0.5)',
85 | 'rgba(51, 0, 255, 0.5)',
86 | 'rgba(0, 0, 255, 0.5)',
87 | 'rgba(51, 51, 255, 0.5)',
88 | 'rgba(0, 0, 204, 0.5)',
89 | 'rgba(102, 102, 255, 0.5)',
90 | 'rgba(51, 51, 204, 0.5)',
91 | 'rgba(0, 0, 153, 0.5)',
92 | 'rgba(153, 153, 255, 0.5)',
93 | 'rgba(102, 102, 204, 0.5)',
94 | 'rgba(51, 51, 153, 0.5)',
95 | 'rgba(0, 0, 102, 0.5)',
96 | 'rgba(204, 204, 255, 0.5)',
97 | 'rgba(153, 153, 204, 0.5)',
98 | 'rgba(102, 102, 153, 0.5)',
99 | 'rgba(51, 51, 102, 0.5)',
100 | 'rgba(0, 0, 51, 0.5)',
101 | 'rgba(0, 51, 255, 0.5)',
102 | 'rgba(51, 102, 255, 0.5)',
103 | 'rgba(0, 51, 204, 0.5)',
104 | 'rgba(0, 102, 255, 0.5)',
105 | 'rgba(102, 153, 255, 0.5)',
106 | 'rgba(51, 102, 204, 0.5)',
107 | 'rgba(0, 51, 153, 0.5)',
108 | 'rgba(51, 153, 255, 0.5)',
109 | 'rgba(0, 102, 204, 0.5)',
110 | 'rgba(0, 153, 255, 0.5)',
111 | 'rgba(153, 204, 255, 0.5)',
112 | 'rgba(102, 153, 204, 0.5)',
113 | 'rgba(51, 102, 153, 0.5)',
114 | 'rgba(0, 51, 102, 0.5)',
115 | 'rgba(102, 204, 255, 0.5)',
116 | 'rgba(51, 153, 204, 0.5)',
117 | 'rgba(0, 102, 153, 0.5)',
118 | 'rgba(51, 204, 255, 0.5)',
119 | 'rgba(0, 153, 204, 0.5)',
120 | 'rgba(0, 204, 255, 0.5)',
121 | 'rgba(0, 255, 255, 0.5)',
122 | 'rgba(51, 255, 255, 0.5)',
123 | 'rgba(0, 204, 204, 0.5)',
124 | 'rgba(102, 255, 255, 0.5)',
125 | 'rgba(51, 204, 204, 0.5)',
126 | 'rgba(0, 153, 153, 0.5)',
127 | 'rgba(153, 255, 255, 0.5)',
128 | 'rgba(102, 204, 204, 0.5)',
129 | 'rgba(51, 153, 153, 0.5)',
130 | 'rgba(0, 102, 102, 0.5)',
131 | 'rgba(204, 255, 255, 0.5)',
132 | 'rgba(153, 204, 204, 0.5)',
133 | 'rgba(102, 153, 153, 0.5)',
134 | 'rgba(51, 102, 102, 0.5)',
135 | 'rgba(0, 51, 51, 0.5)',
136 | 'rgba(0, 255, 204, 0.5)',
137 | 'rgba(51, 255, 204, 0.5)',
138 | 'rgba(0, 204, 153, 0.5)',
139 | 'rgba(102, 255, 204, 0.5)',
140 | 'rgba(51, 204, 153, 0.5)',
141 | 'rgba(0, 153, 102, 0.5)',
142 | 'rgba(153, 255, 204, 0.5)',
143 | 'rgba(102, 204, 153, 0.5)',
144 | 'rgba(51, 153, 102, 0.5)',
145 | 'rgba(0, 102, 51, 0.5)',
146 | 'rgba(0, 255, 153, 0.5)',
147 | 'rgba(51, 255, 153, 0.5)',
148 | 'rgba(0, 204, 102, 0.5)',
149 | 'rgba(102, 255, 153, 0.5)',
150 | 'rgba(51, 204, 102, 0.5)',
151 | 'rgba(0, 153, 51, 0.5)',
152 | 'rgba(0, 255, 102, 0.5)',
153 | 'rgba(51, 255, 102, 0.5)',
154 | 'rgba(0, 204, 51, 0.5)',
155 | 'rgba(0, 255, 51, 0.5)',
156 | 'rgba(0, 255, 0, 0.5)',
157 | 'rgba(51, 255, 51, 0.5)',
158 | 'rgba(0, 204, 0, 0.5)',
159 | 'rgba(102, 255, 102, 0.5)',
160 | 'rgba(51, 204, 51, 0.5)',
161 | 'rgba(0, 153, 0, 0.5)',
162 | 'rgba(153, 255, 153, 0.5)',
163 | 'rgba(102, 204, 102, 0.5)',
164 | 'rgba(51, 153, 51, 0.5)',
165 | 'rgba(0, 102, 0, 0.5)',
166 | 'rgba(204, 255, 204, 0.5)',
167 | 'rgba(153, 204, 153, 0.5)',
168 | 'rgba(102, 153, 102, 0.5)',
169 | 'rgba(51, 102, 51, 0.5)',
170 | 'rgba(0, 51, 0, 0.5)',
171 | 'rgba(51, 255, 0, 0.5)',
172 | 'rgba(102, 255, 51, 0.5)',
173 | 'rgba(51, 204, 0, 0.5)',
174 | 'rgba(102, 255, 0, 0.5)',
175 | 'rgba(153, 255, 102, 0.5)',
176 | 'rgba(102, 204, 51, 0.5)',
177 | 'rgba(51, 153, 0, 0.5)',
178 | 'rgba(153, 255, 51, 0.5)',
179 | 'rgba(102, 204, 0, 0.5)',
180 | 'rgba(153, 255, 0, 0.5)',
181 | 'rgba(204, 255, 153, 0.5)',
182 | 'rgba(153, 204, 102, 0.5)',
183 | 'rgba(102, 153, 51, 0.5)',
184 | 'rgba(51, 102, 0, 0.5)',
185 | 'rgba(204, 255, 102, 0.5)',
186 | 'rgba(153, 204, 51, 0.5)',
187 | 'rgba(102, 153, 0, 0.5)',
188 | 'rgba(204, 255, 51, 0.5)',
189 | 'rgba(153, 204, 0, 0.5)',
190 | 'rgba(204, 255, 0, 0.5)',
191 | 'rgba(255, 255, 0, 0.5)',
192 | 'rgba(255, 255, 51, 0.5)',
193 | 'rgba(204, 204, 0, 0.5)',
194 | 'rgba(255, 255, 102, 0.5)',
195 | 'rgba(204, 204, 51, 0.5)',
196 | 'rgba(153, 153, 0, 0.5)',
197 | 'rgba(255, 255, 153, 0.5)',
198 | 'rgba(204, 204, 102, 0.5)',
199 | 'rgba(153, 153, 51, 0.5)',
200 | 'rgba(102, 102, 0, 0.5)',
201 | 'rgba(255, 255, 204, 0.5)',
202 | 'rgba(204, 204, 153, 0.5)',
203 | 'rgba(153, 153, 102, 0.5)',
204 | 'rgba(102, 102, 51, 0.5)',
205 | 'rgba(51, 51, 0, 0.5)',
206 | 'rgba(255, 204, 0, 0.5)',
207 | 'rgba(255, 204, 51, 0.5)',
208 | 'rgba(204, 153, 0, 0.5)',
209 | 'rgba(255, 204, 102, 0.5)',
210 | 'rgba(204, 153, 51, 0.5)',
211 | 'rgba(153, 102, 0, 0.5)',
212 | 'rgba(255, 204, 153, 0.5)',
213 | 'rgba(204, 153, 102, 0.5)',
214 | 'rgba(153, 102, 51, 0.5)',
215 | 'rgba(102, 51, 0, 0.5)',
216 | 'rgba(255, 153, 0, 0.5)',
217 | 'rgba(255, 153, 51, 0.5)',
218 | 'rgba(204, 102, 0, 0.5)',
219 | 'rgba(255, 153, 102, 0.5)',
220 | 'rgba(204, 102, 51, 0.5)',
221 | 'rgba(153, 51, 0, 0.5)',
222 | 'rgba(255, 102, 0, 0.5)',
223 | 'rgba(255, 102, 51, 0.5)',
224 | 'rgba(204, 51, 0, 0.5)',
225 | 'rgba(255, 51, 0, 0.5)',
226 | 'rgba(255, 0, 0, 0.5)',
227 | 'rgba(255, 51, 51, 0.5)',
228 | 'rgba(204, 0, 0, 0.5)',
229 | 'rgba(255, 102, 102, 0.5)',
230 | 'rgba(204, 51, 51, 0.5)',
231 | 'rgba(153, 0, 0, 0.5)',
232 | 'rgba(255, 153, 153, 0.5)',
233 | 'rgba(204, 102, 102, 0.5)',
234 | 'rgba(153, 51, 51, 0.5)',
235 | 'rgba(102, 0, 0, 0.5)',
236 | 'rgba(255, 204, 204, 0.5)',
237 | 'rgba(204, 153, 153, 0.5)',
238 | 'rgba(153, 102, 102, 0.5)',
239 | 'rgba(102, 51, 51, 0.5)',
240 | 'rgba(51, 0, 0, 0.5)',
241 | 'rgba(255, 0, 51, 0.5)',
242 | 'rgba(255, 51, 102, 0.5)',
243 | 'rgba(204, 0, 51, 0.5)',
244 | 'rgba(255, 0, 102, 0.5)',
245 | 'rgba(255, 102, 153, 0.5)',
246 | 'rgba(204, 51, 102, 0.5)',
247 | 'rgba(153, 0, 51, 0.5)',
248 | 'rgba(255, 51, 153, 0.5)',
249 | 'rgba(204, 0, 102, 0.5)',
250 | 'rgba(255, 0, 153, 0.5)',
251 | 'rgba(255, 153, 204, 0.5)',
252 | 'rgba(204, 102, 153, 0.5)',
253 | 'rgba(153, 51, 102, 0.5)',
254 | 'rgba(102, 0, 51, 0.5)',
255 | 'rgba(255, 102, 204, 0.5)',
256 | 'rgba(204, 51, 153, 0.5)',
257 | 'rgba(153, 0, 102, 0.5)',
258 | 'rgba(255, 51, 204, 0.5)',
259 | 'rgba(204, 0, 153, 0.5)',
260 | 'rgba(255, 0, 204, 0.5)',
261 | ];
262 |
--------------------------------------------------------------------------------
/performance/scriptLinear.js:
--------------------------------------------------------------------------------
1 | import CRDT from '../lib/crdtLinear';
2 | import * as Util from './utilLinear';
3 | import fs from 'fs';
4 | import UUID from 'uuid/v1';
5 |
6 | const logPath = 'performance/logs';
7 |
8 | export function mockController() {
9 | return {
10 | siteId: UUID(),
11 | broadcastInsertion: function() {},
12 | broadcastDeletion: function() {},
13 | insertIntoEditor: function() {},
14 | deleteFromEditor: function() {},
15 | vector: {
16 | localVersion: {
17 | counter: 0
18 | },
19 | increment: function() {
20 | this.localVersion.counter++;
21 | }
22 | }
23 | }
24 | }
25 |
26 | const crdt1 = new CRDT(mockController());
27 | const crdt2 = new CRDT(mockController());
28 | const crdt3 = new CRDT(mockController());
29 | const crdt4 = new CRDT(mockController());
30 | const crdt5 = new CRDT(mockController());
31 |
32 | [crdt1, crdt2, crdt3, crdt4, crdt5].forEach(crdt => {
33 | crdt.insertText = function() {};
34 | crdt.deleteText = function() {};
35 | });
36 |
37 | let table = `
38 | #### PERFORMANCE METRICS (Linear)
39 | Base: ${crdt1.base} | Boundary: ${crdt1.boundary} | Strategy: ${crdt1.strategy}
40 | ================================================================================================
41 |
42 | ## RANDOM
43 | ---------
44 |
45 | # LOCAL INSERTIONS
46 | -----------------------------------------------------------------------------------------------
47 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
48 | | | (in milliseconds) | (in milliseconds) | | Length |
49 | -----------------------------------------------------------------------------------------------
50 |
51 | ${Util.addRowWithId(10, crdt1, Util.insertRandom)}
52 | ${Util.addRowWithId(100, crdt2, Util.insertRandom)}
53 | ${Util.addRowWithId(1000, crdt3, Util.insertRandom)}
54 | ${Util.addRowWithId(10000, crdt4, Util.insertRandom)}
55 | ${Util.addRowWithId(100000, crdt5, Util.insertRandom)}
56 |
57 | # LOCAL DELETIONS
58 | --------------------------------------------------------------
59 | | # of Operations | Total Execute Time | Avg. Operation Time |
60 | | | (in milliseconds) | (in milliseconds) |
61 | --------------------------------------------------------------
62 | ${Util.addRow(10, crdt1, Util.deleteRandom)}
63 | ${Util.addRow(100, crdt2, Util.deleteRandom)}
64 | ${Util.addRow(1000, crdt3, Util.deleteRandom)}
65 | ${Util.addRow(10000, crdt4, Util.deleteRandom)}
66 | ${Util.addRow(100000, crdt5, Util.deleteRandom)}
67 |
68 | # REMOTE INSERTIONS
69 | --------------------------------------------------------------
70 | | # of Operations | Total Execute Time | Avg. Operation Time |
71 | | | (in milliseconds) | (in milliseconds) |
72 | --------------------------------------------------------------
73 | ${Util.addRow(10, crdt1, Util.remoteInsertRandom)}
74 | ${Util.addRow(100, crdt2, Util.remoteInsertRandom)}
75 | ${Util.addRow(1000, crdt3, Util.remoteInsertRandom)}
76 | ${Util.addRow(10000, crdt4, Util.remoteInsertRandom)}
77 | ${Util.addRow(100000, crdt5, Util.remoteInsertRandom)}
78 |
79 | # REMOTE DELETIONS
80 | --------------------------------------------------------------
81 | | # of Operations | Total Execute Time | Avg. Operation Time |
82 | | | (in milliseconds) | (in milliseconds) |
83 | --------------------------------------------------------------
84 | ${Util.addRow(10, crdt1, Util.remoteDeleteRandom)}
85 | ${Util.addRow(100, crdt2, Util.remoteDeleteRandom)}
86 | ${Util.addRow(1000, crdt3, Util.remoteDeleteRandom)}
87 | ${Util.addRow(10000, crdt4, Util.remoteDeleteRandom)}
88 | ${Util.addRow(100000, crdt5, Util.remoteDeleteRandom)}
89 |
90 |
91 | ## AT THE BEGINNING
92 | -------------------
93 |
94 | # LOCAL INSERTIONS
95 | -----------------------------------------------------------------------------------------------
96 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
97 | | | (in milliseconds) | (in milliseconds) | | Length |
98 | -----------------------------------------------------------------------------------------------
99 | ${Util.addRowWithId(10, crdt1, Util.insertBeginning)}
100 | ${Util.addRowWithId(100, crdt2, Util.insertBeginning)}
101 | ${Util.addRowWithId(1000, crdt3, Util.insertBeginning)}
102 | ${Util.addRowWithId(10000, crdt4, Util.insertBeginning)}
103 | ${Util.addRowWithId(100000, crdt5, Util.insertBeginning)}
104 |
105 | # LOCAL DELETIONS
106 | --------------------------------------------------------------
107 | | # of Operations | Total Execute Time | Avg. Operation Time |
108 | | | (in milliseconds) | (in milliseconds) |
109 | --------------------------------------------------------------
110 | ${Util.addRow(10, crdt1, Util.deleteBeginning)}
111 | ${Util.addRow(100, crdt2, Util.deleteBeginning)}
112 | ${Util.addRow(1000, crdt3, Util.deleteBeginning)}
113 | ${Util.addRow(10000, crdt4, Util.deleteBeginning)}
114 | ${Util.addRow(100000, crdt5, Util.deleteBeginning)}
115 |
116 | # REMOTE INSERTIONS
117 | --------------------------------------------------------------
118 | | # of Operations | Total Execute Time | Avg. Operation Time |
119 | | | (in milliseconds) | (in milliseconds) |
120 | --------------------------------------------------------------
121 | ${Util.addRow(10, crdt1, Util.remoteInsertBeginning)}
122 | ${Util.addRow(100, crdt2, Util.remoteInsertBeginning)}
123 | ${Util.addRow(1000, crdt3, Util.remoteInsertBeginning)}
124 | ${Util.addRow(10000, crdt4, Util.remoteInsertBeginning)}
125 | ${Util.addRow(100000, crdt5, Util.remoteInsertBeginning)}
126 |
127 | # REMOTE DELETIONS
128 | --------------------------------------------------------------
129 | | # of Operations | Total Execute Time | Avg. Operation Time |
130 | | | (in milliseconds) | (in milliseconds) |
131 | --------------------------------------------------------------
132 | ${Util.addRow(10, crdt1, Util.remoteDeleteBeginning)}
133 | ${Util.addRow(100, crdt2, Util.remoteDeleteBeginning)}
134 | ${Util.addRow(1000, crdt3, Util.remoteDeleteBeginning)}
135 | ${Util.addRow(10000, crdt4, Util.remoteDeleteBeginning)}
136 | ${Util.addRow(100000, crdt5, Util.remoteDeleteBeginning)}
137 |
138 |
139 | ## AT THE END
140 | -------------
141 |
142 | # LOCAL INSERTIONS
143 | -----------------------------------------------------------------------------------------------
144 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
145 | | | (in milliseconds) | (in milliseconds) | | Length |
146 | -----------------------------------------------------------------------------------------------
147 | ${Util.addRowWithId(10, crdt1, Util.insertEnd)}
148 | ${Util.addRowWithId(100, crdt2, Util.insertEnd)}
149 | ${Util.addRowWithId(1000, crdt3, Util.insertEnd)}
150 | ${Util.addRowWithId(10000, crdt4, Util.insertEnd)}
151 | ${Util.addRowWithId(100000, crdt5, Util.insertEnd)}
152 |
153 | # LOCAL DELETIONS
154 | --------------------------------------------------------------
155 | | # of Operations | Total Execute Time | Avg. Operation Time |
156 | | | (in milliseconds) | (in milliseconds) |
157 | --------------------------------------------------------------
158 | ${Util.addRow(10, crdt1, Util.deleteEnd)}
159 | ${Util.addRow(100, crdt2, Util.deleteEnd)}
160 | ${Util.addRow(1000, crdt3, Util.deleteEnd)}
161 | ${Util.addRow(10000, crdt4, Util.deleteEnd)}
162 | ${Util.addRow(100000, crdt5, Util.deleteEnd)}
163 |
164 | # REMOTE INSERTIONS
165 | --------------------------------------------------------------
166 | | # of Operations | Total Execute Time | Avg. Operation Time |
167 | | | (in milliseconds) | (in milliseconds) |
168 | --------------------------------------------------------------
169 | ${Util.addRow(10, crdt1, Util.remoteInsertEnd)}
170 | ${Util.addRow(100, crdt2, Util.remoteInsertEnd)}
171 | ${Util.addRow(1000, crdt3, Util.remoteInsertEnd)}
172 | ${Util.addRow(10000, crdt4, Util.remoteInsertEnd)}
173 | ${Util.addRow(100000, crdt5, Util.remoteInsertEnd)}
174 |
175 | # REMOTE DELETIONS
176 | --------------------------------------------------------------
177 | | # of Operations | Total Execute Time | Avg. Operation Time |
178 | | | (in milliseconds) | (in milliseconds) |
179 | --------------------------------------------------------------
180 | ${Util.addRow(10, crdt1, Util.remoteDeleteEnd)}
181 | ${Util.addRow(100, crdt2, Util.remoteDeleteEnd)}
182 | ${Util.addRow(1000, crdt3, Util.remoteDeleteEnd)}
183 | ${Util.addRow(10000, crdt4, Util.remoteDeleteEnd)}
184 | ${Util.addRow(100000, crdt5, Util.remoteDeleteEnd)}
185 | `;
186 |
187 | fs.writeFile(`${logPath}/${Util.getTimestamp()}.log`, table, function(err) {
188 | if (err) {
189 | return console.log(err);
190 | }
191 |
192 | console.log(`Results saved to ${logPath}`)
193 | })
194 |
--------------------------------------------------------------------------------
/performance/script.js:
--------------------------------------------------------------------------------
1 | import CRDT from '../lib/crdt';
2 | import * as Util from './util';
3 | import fs from 'fs';
4 | import UUID from 'uuid/v1';
5 |
6 | const logPath = 'performance/logs';
7 |
8 | export function mockController() {
9 | return {
10 | siteId: UUID(),
11 | broadcastInsertion: function() {},
12 | broadcastDeletion: function() {},
13 | insertIntoEditor: function() {},
14 | deleteFromEditor: function() {},
15 | vector: {
16 | getLocalVersion: () => {},
17 | localVersion: {
18 | counter: 0
19 | },
20 | increment: function() {
21 | this.localVersion.counter++;
22 | }
23 | }
24 | }
25 | }
26 |
27 | const crdt1 = new CRDT(mockController());
28 | const crdt2 = new CRDT(mockController());
29 | const crdt3 = new CRDT(mockController());
30 | const crdt4 = new CRDT(mockController());
31 | const crdt5 = new CRDT(mockController());
32 | const crdt6 = new CRDT(mockController());
33 |
34 | let table = `
35 | #### PERFORMANCE METRICS (Array of Arrays)
36 | Base: ${crdt1.base} | Boundary: ${crdt1.boundary} | Strategy: ${crdt1.strategy}
37 | ================================================================================================
38 |
39 | ## RANDOM
40 | ---------
41 |
42 | # LOCAL INSERTIONS
43 | -----------------------------------------------------------------------------------------------
44 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
45 | | | (in milliseconds) | (in milliseconds) | | Length |
46 | -----------------------------------------------------------------------------------------------
47 |
48 | ${Util.addRowWithId(10, crdt1, Util.insertRandom)}
49 | ${Util.addRowWithId(100, crdt2, Util.insertRandom)}
50 | ${Util.addRowWithId(1000, crdt3, Util.insertRandom)}
51 | ${Util.addRowWithId(10000, crdt4, Util.insertRandom)}
52 | ${Util.addRowWithId(100000, crdt5, Util.insertRandom)}
53 | ${Util.addRowWithId(1000000, crdt6, Util.insertRandom)}
54 |
55 |
56 | # LOCAL DELETIONS
57 | --------------------------------------------------------------
58 | | # of Operations | Total Execute Time | Avg. Operation Time |
59 | | | (in milliseconds) | (in milliseconds) |
60 | --------------------------------------------------------------
61 | ${Util.addRow(10, crdt1, Util.deleteRandom)}
62 | ${Util.addRow(100, crdt2, Util.deleteRandom)}
63 | ${Util.addRow(1000, crdt3, Util.deleteRandom)}
64 | ${Util.addRow(10000, crdt4, Util.deleteRandom)}
65 | ${Util.addRow(100000, crdt5, Util.deleteRandom)}
66 | ${Util.addRow(1000000, crdt6, Util.deleteRandom)}
67 |
68 |
69 | # REMOTE INSERTIONS
70 | --------------------------------------------------------------
71 | | # of Operations | Total Execute Time | Avg. Operation Time |
72 | | | (in milliseconds) | (in milliseconds) |
73 | --------------------------------------------------------------
74 | ${Util.addRow(10, crdt1, Util.remoteInsertRandom)}
75 | ${Util.addRow(100, crdt2, Util.remoteInsertRandom)}
76 | ${Util.addRow(1000, crdt3, Util.remoteInsertRandom)}
77 | ${Util.addRow(10000, crdt4, Util.remoteInsertRandom)}
78 | ${Util.addRow(100000, crdt5, Util.remoteInsertRandom)}
79 |
80 |
81 | # REMOTE DELETIONS
82 | --------------------------------------------------------------
83 | | # of Operations | Total Execute Time | Avg. Operation Time |
84 | | | (in milliseconds) | (in milliseconds) |
85 | --------------------------------------------------------------
86 | ${Util.addRow(10, crdt1, Util.remoteDeleteRandom)}
87 | ${Util.addRow(100, crdt2, Util.remoteDeleteRandom)}
88 | ${Util.addRow(1000, crdt3, Util.remoteDeleteRandom)}
89 | ${Util.addRow(10000, crdt4, Util.remoteDeleteRandom)}
90 | ${Util.addRow(100000, crdt5, Util.remoteDeleteRandom)}
91 |
92 |
93 |
94 | ## AT THE BEGINNING
95 | -------------------
96 |
97 | # LOCAL INSERTIONS
98 | -----------------------------------------------------------------------------------------------
99 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
100 | | | (in milliseconds) | (in milliseconds) | | Length |
101 | -----------------------------------------------------------------------------------------------
102 | ${Util.addRowWithId(10, crdt1, Util.insertBeginning)}
103 | ${Util.addRowWithId(100, crdt2, Util.insertBeginning)}
104 | ${Util.addRowWithId(1000, crdt3, Util.insertBeginning)}
105 | ${Util.addRowWithId(10000, crdt4, Util.insertBeginning)}
106 | ${Util.addRowWithId(100000, crdt5, Util.insertBeginning)}
107 |
108 |
109 | # LOCAL DELETIONS
110 | --------------------------------------------------------------
111 | | # of Operations | Total Execute Time | Avg. Operation Time |
112 | | | (in milliseconds) | (in milliseconds) |
113 | --------------------------------------------------------------
114 | ${Util.addRow(10, crdt1, Util.deleteBeginning)}
115 | ${Util.addRow(100, crdt2, Util.deleteBeginning)}
116 | ${Util.addRow(1000, crdt3, Util.deleteBeginning)}
117 | ${Util.addRow(10000, crdt4, Util.deleteBeginning)}
118 | ${Util.addRow(100000, crdt5, Util.deleteBeginning)}
119 |
120 |
121 | # REMOTE INSERTIONS
122 | --------------------------------------------------------------
123 | | # of Operations | Total Execute Time | Avg. Operation Time |
124 | | | (in milliseconds) | (in milliseconds) |
125 | --------------------------------------------------------------
126 | ${Util.addRow(10, crdt1, Util.remoteInsertBeginning)}
127 | ${Util.addRow(100, crdt2, Util.remoteInsertBeginning)}
128 | ${Util.addRow(1000, crdt3, Util.remoteInsertBeginning)}
129 | ${Util.addRow(10000, crdt4, Util.remoteInsertBeginning)}
130 | ${Util.addRow(100000, crdt5, Util.remoteInsertBeginning)}
131 |
132 |
133 | # REMOTE DELETIONS
134 | --------------------------------------------------------------
135 | | # of Operations | Total Execute Time | Avg. Operation Time |
136 | | | (in milliseconds) | (in milliseconds) |
137 | --------------------------------------------------------------
138 | ${Util.addRow(10, crdt1, Util.remoteDeleteBeginning)}
139 | ${Util.addRow(100, crdt2, Util.remoteDeleteBeginning)}
140 | ${Util.addRow(1000, crdt3, Util.remoteDeleteBeginning)}
141 | ${Util.addRow(10000, crdt4, Util.remoteDeleteBeginning)}
142 | ${Util.addRow(100000, crdt5, Util.remoteDeleteBeginning)}
143 |
144 |
145 |
146 | ## AT THE END
147 | -------------
148 |
149 | # LOCAL INSERTIONS
150 | -----------------------------------------------------------------------------------------------
151 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
152 | | | (in milliseconds) | (in milliseconds) | | Length |
153 | -----------------------------------------------------------------------------------------------
154 | ${Util.addRowWithId(10, crdt1, Util.insertEnd)}
155 | ${Util.addRowWithId(100, crdt2, Util.insertEnd)}
156 | ${Util.addRowWithId(1000, crdt3, Util.insertEnd)}
157 | ${Util.addRowWithId(10000, crdt4, Util.insertEnd)}
158 | ${Util.addRowWithId(100000, crdt5, Util.insertEnd)}
159 |
160 |
161 | # LOCAL DELETIONS
162 | --------------------------------------------------------------
163 | | # of Operations | Total Execute Time | Avg. Operation Time |
164 | | | (in milliseconds) | (in milliseconds) |
165 | --------------------------------------------------------------
166 | ${Util.addRow(10, crdt1, Util.deleteEnd)}
167 | ${Util.addRow(100, crdt2, Util.deleteEnd)}
168 | ${Util.addRow(1000, crdt3, Util.deleteEnd)}
169 | ${Util.addRow(10000, crdt4, Util.deleteEnd)}
170 | ${Util.addRow(100000, crdt5, Util.deleteEnd)}
171 |
172 |
173 | # REMOTE INSERTIONS
174 | --------------------------------------------------------------
175 | | # of Operations | Total Execute Time | Avg. Operation Time |
176 | | | (in milliseconds) | (in milliseconds) |
177 | --------------------------------------------------------------
178 | ${Util.addRow(10, crdt1, Util.remoteInsertEnd)}
179 | ${Util.addRow(100, crdt2, Util.remoteInsertEnd)}
180 | ${Util.addRow(1000, crdt3, Util.remoteInsertEnd)}
181 | ${Util.addRow(10000, crdt4, Util.remoteInsertEnd)}
182 | ${Util.addRow(100000, crdt5, Util.remoteInsertEnd)}
183 |
184 |
185 | # REMOTE DELETIONS
186 | --------------------------------------------------------------
187 | | # of Operations | Total Execute Time | Avg. Operation Time |
188 | | | (in milliseconds) | (in milliseconds) |
189 | --------------------------------------------------------------
190 | ${Util.addRow(10, crdt1, Util.remoteDeleteEnd)}
191 | ${Util.addRow(100, crdt2, Util.remoteDeleteEnd)}
192 | ${Util.addRow(1000, crdt3, Util.remoteDeleteEnd)}
193 | ${Util.addRow(10000, crdt4, Util.remoteDeleteEnd)}
194 | ${Util.addRow(100000, crdt5, Util.remoteDeleteEnd)}
195 |
196 | `;
197 |
198 | fs.writeFile(`${logPath}/${Util.getTimestamp()}.log`, table, function(err) {
199 | if (err) {
200 | return console.log(err);
201 | }
202 |
203 | console.log(`Results saved to ${logPath}`)
204 | })
205 |
--------------------------------------------------------------------------------
/performance/util.js:
--------------------------------------------------------------------------------
1 | import Char from '../lib/char';
2 | import CRDT from '../lib/crdt';
3 | import { mockController } from './script';
4 |
5 | const CELL_1_SIZE = 17;
6 | const CELL_2_SIZE = 20;
7 | const CELL_3_SIZE = 21;
8 | const CELL_4_SIZE = 16;
9 | const CELL_5_SIZE = 15;
10 |
11 | function insertRandom(crdt, numberOfOperations) {
12 | let counter = 0;
13 | let line = 0;
14 | let ch, pos;
15 | const start = Date.now();
16 |
17 | for (let i = 0; i < numberOfOperations; i++) {
18 | if (counter === 100) {
19 | pos = { line: line, ch: counter}
20 | crdt.handleLocalInsert('\n', pos);
21 |
22 | line++;
23 | counter = 0;
24 | } else {
25 | ch = Math.floor(Math.random() * counter);
26 | pos = { line: line, ch: ch };
27 | crdt.handleLocalInsert('a', pos);
28 |
29 | counter++
30 | }
31 | }
32 |
33 | const end = Date.now();
34 | return end - start;
35 | }
36 |
37 | function remoteInsertRandom(crdt, numberOfOperations) {
38 | const chars = shuffle(generateChars(numberOfOperations));
39 | return remoteInsert(crdt, chars);
40 | }
41 |
42 | function remoteInsert(crdt, chars) {
43 | const start = Date.now();
44 |
45 | chars.forEach(char => crdt.handleRemoteInsert(char));
46 |
47 | const end = Date.now();
48 | return end - start;
49 | }
50 |
51 | function deleteRandom(crdt) {
52 | const totalChars = crdt.totalChars();
53 | const start = Date.now();
54 | let line, ch, startPos, endPos;
55 |
56 | for(let i = totalChars; i > 0; i--) {
57 | line = Math.floor(Math.random() * crdt.struct.length)
58 | ch = Math.floor(Math.random() * crdt.struct[line].length);
59 | startPos = { line: line, ch: ch }
60 | endPos = { line: line, ch: ch + 1 }
61 | crdt.handleLocalDelete(startPos, endPos);
62 | }
63 |
64 | const end = Date.now();
65 | return end - start;
66 | }
67 |
68 | function remoteDeleteRandom(crdt) {
69 | const charsToDelete = [].concat.apply([], crdt.struct);
70 |
71 | return remoteDelete(crdt, shuffle(charsToDelete));
72 | }
73 |
74 | function remoteDelete(crdt, chars) {
75 | const start = Date.now();
76 |
77 | chars.forEach(char => crdt.handleRemoteDelete(char));
78 |
79 | const end = Date.now();
80 | return end - start;
81 | }
82 |
83 | function insertBeginning(crdt, numberOfOperations) {
84 | let counter = 0;
85 | let line = 0;
86 | let ch, pos;
87 | const start = Date.now();
88 |
89 | for (let i = 0; i < numberOfOperations; i++) {
90 | if (counter === 100) {
91 | pos = { line: line, ch: counter };
92 | crdt.handleLocalInsert('\n', pos);
93 |
94 | line++;
95 | counter = 0;
96 | } else {
97 | pos = { line: line, ch: 0 };
98 | crdt.handleLocalInsert('a', pos);
99 |
100 | counter++
101 | }
102 | }
103 |
104 | const end = Date.now();
105 | return end - start;
106 | }
107 |
108 | function deleteBeginning(crdt) {
109 | const totalChars = crdt.totalChars();
110 | const start = Date.now();
111 |
112 | for (let i = totalChars; i > 0; i--) {
113 | let startPos = { line: 0, ch: 0 };
114 | let endPos = { line: 0, ch: 1};
115 |
116 | crdt.handleLocalDelete(startPos, endPos);
117 | }
118 |
119 | const end = Date.now();
120 | return end - start;
121 | }
122 |
123 | function remoteInsertBeginning(crdt, numberOfOperations) {
124 | const chars = generateChars(numberOfOperations);
125 | const descChars = chars.reverse();
126 |
127 | return remoteInsert(crdt, descChars);
128 | }
129 |
130 | function remoteDeleteBeginning(crdt) {
131 | const charsToDelete = [].concat.apply([], crdt.struct);
132 | return remoteDelete(crdt, charsToDelete);
133 | }
134 |
135 | function insertEnd(crdt, numberOfOperations) {
136 | let counter = 0;
137 | let line = 0;
138 | let ch, pos;
139 | const start = Date.now();
140 |
141 | for (let i = 0; i < numberOfOperations; i++) {
142 | pos = { line: line, ch: counter };
143 |
144 | if (counter === 100) {
145 | crdt.handleLocalInsert('\n', pos);
146 |
147 | line++;
148 | counter = 0;
149 | } else {
150 | crdt.handleLocalInsert('a', pos);
151 |
152 | counter++
153 | }
154 | }
155 |
156 | const end = Date.now();
157 | return end - start;
158 | }
159 |
160 | function deleteEnd(crdt) {
161 | const totalChars = crdt.totalChars();
162 | const start = Date.now();
163 | let line;
164 | let ch;
165 | let lineNum;
166 |
167 | for (let i = totalChars; i > 0; i--) {
168 | lineNum = crdt.struct.length - 1;
169 | line = crdt.struct[lineNum];
170 | ch = line[line.length - 1];
171 | let startPos = { line: lineNum, ch: ch };
172 | let endPos = { line: lineNum, ch: ch + 1};
173 |
174 | crdt.handleLocalDelete(startPos, endPos);
175 | }
176 |
177 | const end = Date.now();
178 | return end - start;
179 | }
180 |
181 | function remoteInsertEnd(crdt, numberOfOperations) {
182 | const ascChars = generateChars(numberOfOperations);
183 |
184 | return remoteInsert(crdt, ascChars);
185 | }
186 |
187 | function remoteDeleteEnd(crdt) {
188 | const charsToDelete = [].concat.apply([], crdt.struct);
189 | const reverseToDel = charsToDelete.reverse();
190 | return remoteDelete(crdt, reverseToDel);
191 | }
192 |
193 | function generateChars(numOps) {
194 | let crdts = [];
195 | let crdt;
196 |
197 | // Create crdts based on number of operations requested
198 | for (let i = 0; i < Math.log10(numOps); i++) {
199 | crdt = new CRDT(mockController());
200 | crdts.push(crdt);
201 | }
202 |
203 | // Insert characters randomly in each crdt
204 | const numOpsPerCRDT = numOps / crdts.length;
205 | crdts.forEach(crdt => insertRandom(crdt, numOpsPerCRDT));
206 |
207 | let chars = [];
208 | const structsWithLines = crdts.map(crdt => crdt.struct);
209 | const structs = structsWithLines.map(struct => {
210 | return [].concat.apply([], struct);
211 | });
212 |
213 | for (let i = 0; i < structs[0].length; i++) {
214 | structs.forEach(struct => chars.push(struct[i]));
215 | }
216 |
217 | return chars;
218 | }
219 |
220 | function shuffle(a) {
221 | for (let i = a.length - 1; i > 0; i--) {
222 | const j = Math.floor(Math.random() * (i + 1));
223 | [a[i], a[j]] = [a[j], a[i]];
224 | }
225 | return a;
226 | }
227 |
228 | function avgIdLength(crdt) {
229 | let numChars = 0;
230 |
231 | const idArray = crdt.struct.map(line => line.map(char => char.position.map(id => id.digit).join('')));
232 | const digitLengthSum = idArray.reduce((acc, line) => {
233 | return acc + line.reduce((acc, id) => {
234 | numChars++;
235 | return acc + id.length;
236 | }, 0);
237 | }, 0);
238 |
239 | return Math.floor(digitLengthSum / numChars);
240 | }
241 |
242 | function avgPosLength(crdt) {
243 | let numChars = 0;
244 |
245 | const posArray = crdt.struct.map(line => line.map(char => char.position.length));
246 | const posLengthSum = posArray.reduce((acc, line) => {
247 | return acc + line.reduce((acc, len) => {
248 | numChars++;
249 | return acc + len;
250 | }, 0);
251 | }, 0);
252 |
253 | return Math.floor(posLengthSum / numChars);
254 | }
255 |
256 | function average(time, operations) {
257 | return time / operations;
258 | }
259 |
260 | function addPadding(value, cellSize) {
261 | value = String(value);
262 |
263 | if (value.length > cellSize) {
264 | value = value.slice(0, cellSize);
265 | }
266 |
267 | const padding = ((cellSize - value.length) / 2);
268 | return (' ').repeat(Math.floor(padding)) + value + (' ').repeat(Math.ceil(padding));
269 | }
270 |
271 | function addRowWithId(operations, crdt, func) {
272 | const totalTime = func(crdt, operations);
273 | const cell1 = addPadding(operations, CELL_1_SIZE);
274 | const cell2 = addPadding(totalTime, CELL_2_SIZE);
275 | const cell3 = addPadding(average(totalTime, operations), CELL_3_SIZE);
276 | const cell4 = addPadding(avgIdLength(crdt), CELL_4_SIZE);
277 | const cell5 = addPadding(avgPosLength(crdt), CELL_5_SIZE);
278 |
279 | return `|${cell1}|${cell2}|${cell3}|${cell4}|${cell5}|
280 | ${'-'.repeat(95)}`
281 |
282 | }
283 |
284 | function addRow(operations, crdt, func) {
285 | const totalTime = func(crdt, operations);
286 | const cell1 = addPadding(operations, CELL_1_SIZE);
287 | const cell2 = addPadding(totalTime, CELL_2_SIZE);
288 | const cell3 = addPadding(average(totalTime, operations), CELL_3_SIZE);
289 |
290 | return `|${cell1}|${cell2}|${cell3}|
291 | ${'-'.repeat(62)}`
292 | }
293 |
294 | function getTimestamp() {
295 | const now = new Date();
296 | const year = now.getUTCFullYear();
297 | const month = now.getUTCMonth() + 1;
298 | const date = now.getUTCDate();
299 | const hours = now.getUTCHours();
300 | const minutes = now.getUTCMinutes();
301 | const seconds = now.getUTCSeconds();
302 |
303 | return `${year}-${month}-${date} ${hours}:${minutes}:${seconds}`;
304 | }
305 |
306 | export {
307 | addRow,
308 | addRowWithId,
309 | insertRandom,
310 | remoteInsertRandom,
311 | insertEnd,
312 | remoteInsertEnd,
313 | insertBeginning,
314 | remoteInsertBeginning,
315 | deleteRandom,
316 | remoteDeleteRandom,
317 | deleteEnd,
318 | remoteDeleteEnd,
319 | deleteBeginning,
320 | remoteDeleteBeginning,
321 | getTimestamp
322 | };
323 |
--------------------------------------------------------------------------------
/lib/broadcast.js:
--------------------------------------------------------------------------------
1 | class Broadcast {
2 | constructor() {
3 | this.controller = null;
4 | this.peer = null;
5 | this.outConns = [];
6 | this.inConns = [];
7 | this.outgoingBuffer = [];
8 | this.MAX_BUFFER_SIZE = 40;
9 | this.currentStream = null;
10 | }
11 |
12 | send(operation) {
13 | const operationJSON = JSON.stringify(operation);
14 | if (operation.type === 'insert' || operation.type === 'delete') {
15 | this.addToOutgoingBuffer(operationJSON);
16 | }
17 | this.outConns.forEach(conn => conn.send(operationJSON));
18 | }
19 |
20 | addToOutgoingBuffer(operation) {
21 | if (this.outgoingBuffer.length === this.MAX_BUFFER_SIZE) {
22 | this.outgoingBuffer.shift();
23 | }
24 |
25 | this.outgoingBuffer.push(operation);
26 | }
27 |
28 | processOutgoingBuffer(peerId) {
29 | const connection = this.outConns.find(conn => conn.peer === peerId);
30 | this.outgoingBuffer.forEach(op => {
31 | connection.send(op);
32 | });
33 | }
34 |
35 | bindServerEvents(targetPeerId, peer) {
36 | this.peer = peer;
37 | this.onOpen(targetPeerId);
38 | this.heartbeat = this.startPeerHeartBeat(peer);
39 | }
40 |
41 | startPeerHeartBeat(peer) {
42 | let timeoutId = 0;
43 | const heartbeat = () => {
44 | timeoutId = setTimeout( heartbeat, 20000 );
45 | if ( peer.socket._wsOpen() ) {
46 | peer.socket.send( {type:'HEARTBEAT'} );
47 | }
48 | };
49 |
50 | heartbeat();
51 |
52 | return {
53 | start : function () {
54 | if ( timeoutId === 0 ) { heartbeat(); }
55 | },
56 | stop : function () {
57 | clearTimeout( timeoutId );
58 | timeoutId = 0;
59 | }
60 | };
61 | }
62 |
63 | onOpen(targetPeerId) {
64 | this.peer.on('open', id => {
65 | this.controller.updateShareLink(id);
66 | this.onPeerConnection();
67 | this.onError();
68 | this.onDisconnect();
69 | if (targetPeerId == 0) {
70 | this.controller.addToNetwork(id, this.controller.siteId);
71 | } else {
72 | this.requestConnection(targetPeerId, id, this.controller.siteId)
73 | }
74 | });
75 | }
76 |
77 | onError() {
78 | this.peer.on("error", err => {
79 | const pid = String(err).replace("Error: Could not connect to peer ", "");
80 | this.removeFromConnections(pid);
81 | console.log(err.type);
82 | if (!this.peer.disconnected) {
83 | this.controller.findNewTarget();
84 | }
85 | this.controller.enableEditor();
86 | });
87 | }
88 |
89 | onDisconnect() {
90 | this.peer.on('disconnected', () => {
91 | this.controller.lostConnection();
92 | });
93 | }
94 |
95 | requestConnection(target, peerId, siteId) {
96 | const conn = this.peer.connect(target);
97 | this.addToOutConns(conn);
98 | conn.on('open', () => {
99 | conn.send(JSON.stringify({
100 | type: 'connRequest',
101 | peerId: peerId,
102 | siteId: siteId,
103 | }));
104 | });
105 | }
106 |
107 | evaluateRequest(peerId, siteId) {
108 | if (this.hasReachedMax()) {
109 | this.forwardConnRequest(peerId, siteId);
110 | } else {
111 | this.acceptConnRequest(peerId, siteId);
112 | }
113 | }
114 |
115 | hasReachedMax() {
116 | const halfTheNetwork = Math.ceil(this.controller.network.length / 2);
117 | const tooManyInConns = this.inConns.length > Math.max(halfTheNetwork, 5);
118 | const tooManyOutConns = this.outConns.length > Math.max(halfTheNetwork, 5);
119 |
120 | return tooManyInConns || tooManyOutConns;
121 | }
122 |
123 | forwardConnRequest(peerId, siteId) {
124 | const connected = this.outConns.filter(conn => conn.peer !== peerId);
125 | const randomIdx = Math.floor(Math.random() * connected.length);
126 | connected[randomIdx].send(JSON.stringify({
127 | type: 'connRequest',
128 | peerId: peerId,
129 | siteId: siteId,
130 | }));
131 | }
132 |
133 | addToOutConns(connection) {
134 | if (!!connection && !this.isAlreadyConnectedOut(connection)) {
135 | this.outConns.push(connection);
136 | }
137 | }
138 |
139 | addToInConns(connection) {
140 | if (!!connection && !this.isAlreadyConnectedIn(connection)) {
141 | this.inConns.push(connection);
142 | }
143 | }
144 |
145 | addToNetwork(peerId, siteId) {
146 | this.send({
147 | type: "add to network",
148 | newPeer: peerId,
149 | newSite: siteId
150 | });
151 | }
152 |
153 | removeFromNetwork(peerId) {
154 | this.send({
155 | type: "remove from network",
156 | oldPeer: peerId
157 | });
158 | this.controller.removeFromNetwork(peerId);
159 | }
160 |
161 | removeFromConnections(peer) {
162 | this.inConns = this.inConns.filter(conn => conn.peer !== peer);
163 | this.outConns = this.outConns.filter(conn => conn.peer !== peer);
164 | this.removeFromNetwork(peer);
165 | }
166 |
167 | isAlreadyConnectedOut(connection) {
168 | if (connection.peer) {
169 | return !!this.outConns.find(conn => conn.peer === connection.peer);
170 | } else {
171 | return !!this.outConns.find(conn => conn.peer.id === connection);
172 | }
173 | }
174 |
175 | isAlreadyConnectedIn(connection) {
176 | if (connection.peer) {
177 | return !!this.inConns.find(conn => conn.peer === connection.peer);
178 | } else {
179 | return !!this.inConns.find(conn => conn.peer.id === connection);
180 | }
181 | }
182 |
183 | onPeerConnection() {
184 | this.peer.on('connection', (connection) => {
185 | this.onConnection(connection);
186 | this.onVideoCall(connection);
187 | this.onData(connection);
188 | this.onConnClose(connection);
189 | });
190 | }
191 |
192 | acceptConnRequest(peerId, siteId) {
193 | const connBack = this.peer.connect(peerId);
194 | this.addToOutConns(connBack);
195 | this.controller.addToNetwork(peerId, siteId);
196 |
197 | const initialData = JSON.stringify({
198 | type: 'syncResponse',
199 | siteId: this.controller.siteId,
200 | peerId: this.peer.id,
201 | initialStruct: this.controller.crdt.struct,
202 | initialVersions: this.controller.vector.versions,
203 | network: this.controller.network
204 | });
205 |
206 | if (connBack.open) {
207 | connBack.send(initialData);
208 | } else {
209 | connBack.on('open', () => {
210 | connBack.send(initialData);
211 | });
212 | }
213 | }
214 |
215 | videoCall(id, ms) {
216 | if (!this.currentStream) {
217 | const callObj = this.peer.call(id, ms);
218 | this.onStream(callObj);
219 | }
220 | }
221 |
222 | onConnection(connection) {
223 | this.controller.updateRootUrl(connection.peer);
224 | this.addToInConns(connection);
225 | }
226 |
227 | onVideoCall() {
228 | this.peer.on('call', callObj => {
229 | this.controller.beingCalled(callObj);
230 | });
231 | }
232 |
233 | answerCall(callObj, ms) {
234 | if (!this.currentStream) {
235 | callObj.answer(ms);
236 | this.controller.answerCall(callObj.peer);
237 | this.onStream(callObj);
238 | }
239 | }
240 |
241 | onStream(callObj) {
242 | callObj.on('stream', stream => {
243 | if (this.currentStream) { this.currentStream.close(); }
244 | this.currentStream = callObj;
245 |
246 | this.controller.streamVideo(stream, callObj);
247 |
248 | callObj.on('close', () => this.onStreamClose(callObj.peer))
249 | });
250 | }
251 |
252 | onStreamClose(peerId) {
253 | this.currentStream.localStream.getTracks().forEach(track => track.stop());
254 | this.currentStream = null;
255 |
256 | this.controller.closeVideo(peerId);
257 | }
258 |
259 | onData(connection) {
260 | connection.on('data', data => {
261 | const dataObj = JSON.parse(data);
262 |
263 | switch(dataObj.type) {
264 | case 'connRequest':
265 | this.evaluateRequest(dataObj.peerId, dataObj.siteId);
266 | break;
267 | case 'syncResponse':
268 | this.processOutgoingBuffer(dataObj.peerId);
269 | this.controller.handleSync(dataObj);
270 | break;
271 | case 'syncCompleted':
272 | this.processOutgoingBuffer(dataObj.peerId);
273 | break;
274 | case 'add to network':
275 | this.controller.addToNetwork(dataObj.newPeer, dataObj.newSite);
276 | break;
277 | case 'remove from network':
278 | this.controller.removeFromNetwork(dataObj.oldPeer);
279 | break;
280 | default:
281 | this.controller.handleRemoteOperation(dataObj);
282 | }
283 | });
284 | }
285 |
286 | randomId() {
287 | const possConns = this.inConns.filter(conn => {
288 | return this.peer.id !== conn.peer;
289 | });
290 | const randomIdx = Math.floor(Math.random() * possConns.length);
291 | if (possConns[randomIdx]) {
292 | return possConns[randomIdx].peer;
293 | } else {
294 | return false;
295 | }
296 | }
297 |
298 | onConnClose(connection) {
299 | connection.on('close', () => {
300 | this.removeFromConnections(connection.peer);
301 | if (connection.peer == this.controller.urlId) {
302 | const id = this.randomId();
303 | if (id) { this.controller.updatePageURL(id); }
304 | }
305 | if (!this.hasReachedMax()) {
306 | this.controller.findNewTarget();
307 | }
308 | });
309 | }
310 | }
311 |
312 | export default Broadcast;
313 |
--------------------------------------------------------------------------------
/lib/util.js:
--------------------------------------------------------------------------------
1 | import Char from './char';
2 | import CRDT from './crdt';
3 | import UUID from 'uuid/v1';
4 |
5 | export function mockController() {
6 | return {
7 | siteId: UUID(),
8 | broadcastInsertion: function() {},
9 | broadcastDeletion: function() {},
10 | insertIntoEditor: function() {},
11 | deleteFromEditor: function() {},
12 | vector: {
13 | getLocalVersion: () => {},
14 | localVersion: {
15 | counter: 0
16 | },
17 | increment: function() {
18 | this.localVersion.counter++;
19 | }
20 | }
21 | }
22 | }
23 |
24 | function insertRandom(crdt, numberOfOperations) {
25 | let counter = 0;
26 | let line = 0;
27 | let ch, pos;
28 | const start = Date.now();
29 |
30 | for (let i = 0; i < numberOfOperations; i++) {
31 | if (counter === 100) {
32 | pos = { line: line, ch: counter}
33 | crdt.handleLocalInsert('\n', pos);
34 |
35 | line++;
36 | counter = 0;
37 | } else {
38 | ch = Math.floor(Math.random() * counter);
39 | pos = { line: line, ch: ch };
40 | crdt.handleLocalInsert('a', pos);
41 | ``
42 | counter++
43 | }
44 | }
45 |
46 | const end = Date.now();
47 | return end - start;
48 | }
49 |
50 | function remoteInsertRandom(crdt, numberOfOperations) {
51 | const chars = shuffle(generateChars(numberOfOperations));
52 | return remoteInsert(crdt, chars);
53 | }
54 |
55 | function remoteInsert(crdt, chars) {
56 | const start = Date.now();
57 |
58 | chars.forEach(char => crdt.handleRemoteInsert(char));
59 |
60 | const end = Date.now();
61 | return end - start;
62 | }
63 |
64 | function deleteRandom(crdt) {
65 | const totalChars = crdt.totalChars();
66 | const start = Date.now();
67 | let line, ch, startPos, endPos;
68 |
69 | for(let i = totalChars; i > 0; i--) {
70 | line = Math.floor(Math.random() * crdt.struct.length)
71 | ch = Math.floor(Math.random() * crdt.struct[line].length);
72 | startPos = { line: line, ch: ch }
73 | endPos = { line: line, ch: ch + 1 }
74 | crdt.handleLocalDelete(startPos, endPos);
75 | }
76 |
77 | const end = Date.now();
78 | return end - start;
79 | }
80 |
81 | function remoteDeleteRandom(crdt) {
82 | const charsToDelete = [].concat.apply([], crdt.struct);
83 |
84 | return remoteDelete(crdt, shuffle(charsToDelete));
85 | }
86 |
87 | function remoteDelete(crdt, chars) {
88 | const start = Date.now();
89 |
90 | chars.forEach(char => crdt.handleRemoteDelete(char));
91 |
92 | const end = Date.now();
93 | return end - start;
94 | }
95 |
96 | function insertBeginning(crdt, numberOfOperations) {
97 | let counter = 0;
98 | let line = 0;
99 | let ch, pos;
100 | const start = Date.now();
101 |
102 | for (let i = 0; i < numberOfOperations; i++) {
103 | if (counter === 100) {
104 | pos = { line: line, ch: counter };
105 | crdt.handleLocalInsert('\n', pos);
106 |
107 | line++;
108 | counter = 0;
109 | } else {
110 | pos = { line: line, ch: 0 };
111 | crdt.handleLocalInsert('a', pos);
112 |
113 | counter++
114 | }
115 | }
116 |
117 | const end = Date.now();
118 | return end - start;
119 | }
120 |
121 | function deleteBeginning(crdt) {
122 | const totalChars = crdt.totalChars();
123 | const start = Date.now();
124 |
125 | for (let i = totalChars; i > 0; i--) {
126 | let startPos = { line: 0, ch: 0 };
127 | let endPos = { line: 0, ch: 1};
128 |
129 | crdt.handleLocalDelete(startPos, endPos);
130 | }
131 |
132 | const end = Date.now();
133 | return end - start;
134 | }
135 |
136 | function remoteInsertBeginning(crdt, numberOfOperations) {
137 | const chars = generateChars(numberOfOperations);
138 | const descChars = chars.reverse();
139 |
140 | return remoteInsert(crdt, descChars);
141 | }
142 |
143 | function remoteDeleteBeginning(crdt) {
144 | const charsToDelete = [].concat.apply([], crdt.struct);
145 | return remoteDelete(crdt, charsToDelete);
146 | }
147 |
148 | function insertEnd(crdt, numberOfOperations) {
149 | let counter = 0;
150 | let line = 0;
151 | let ch, pos;
152 | const start = Date.now();
153 |
154 | for (let i = 0; i < numberOfOperations; i++) {
155 | pos = { line: line, ch: counter };
156 |
157 | if (counter === 100) {
158 | crdt.handleLocalInsert('\n', pos);
159 |
160 | line++;
161 | counter = 0;
162 | } else {
163 | crdt.handleLocalInsert('a', pos);
164 |
165 | counter++
166 | }
167 | }
168 |
169 | const end = Date.now();
170 | return end - start;
171 | }
172 |
173 | function deleteEnd(crdt) {
174 | const totalChars = crdt.totalChars();
175 | const start = Date.now();
176 | let line;
177 | let ch;
178 | let lineNum;
179 |
180 | for (let i = totalChars; i > 0; i--) {
181 | lineNum = crdt.struct.length - 1;
182 | line = crdt.struct[lineNum];
183 | ch = line[line.length - 1];
184 | let startPos = { line: lineNum, ch: ch };
185 | let endPos = { line: lineNum, ch: ch + 1};
186 |
187 | crdt.handleLocalDelete(startPos, endPos);
188 | }
189 |
190 | const end = Date.now();
191 | return end - start;
192 | }
193 |
194 | function remoteInsertEnd(crdt, numberOfOperations) {
195 | const ascChars = generateChars(numberOfOperations);
196 |
197 | return remoteInsert(crdt, ascChars);
198 | }
199 |
200 | function remoteDeleteEnd(crdt) {
201 | const charsToDelete = [].concat.apply([], crdt.struct);
202 | const reverseToDel = charsToDelete.reverse();
203 | return remoteDelete(crdt, reverseToDel);
204 | }
205 |
206 | function generateChars(numOps) {
207 | let crdts = [];
208 | let crdt;
209 |
210 | // Create crdts based on number of operations requested
211 | for (let i = 0; i < Math.log10(numOps); i++) {
212 | crdt = new CRDT(mockController());
213 | crdts.push(crdt);
214 | }
215 |
216 | // Insert characters randomly in each crdt
217 | const numOpsPerCRDT = numOps / crdts.length;
218 | crdts.forEach(crdt => insertRandom(crdt, numOpsPerCRDT));
219 |
220 | let chars = [];
221 | const structsWithLines = crdts.map(crdt => crdt.struct);
222 | const structs = structsWithLines.map(struct => {
223 | return [].concat.apply([], struct);
224 | });
225 |
226 | for (let i = 0; i < structs[0].length; i++) {
227 | structs.forEach(struct => chars.push(struct[i]));
228 | }
229 |
230 | return chars;
231 | }
232 |
233 | function shuffle(a) {
234 | for (let i = a.length - 1; i > 0; i--) {
235 | const j = Math.floor(Math.random() * (i + 1));
236 | [a[i], a[j]] = [a[j], a[i]];
237 | }
238 | return a;
239 | }
240 |
241 | function avgIdLength(crdt) {
242 | let numChars = 0;
243 |
244 | const idArray = crdt.struct.map(line => line.map(char => char.position.map(id => id.digit).join('')));
245 | const digitLengthSum = idArray.reduce((acc, line) => {
246 | return acc + line.reduce((acc, id) => {
247 | numChars++;
248 | return acc + id.length;
249 | }, 0);
250 | }, 0);
251 |
252 | return Math.floor(digitLengthSum / numChars);
253 | }
254 |
255 | function avgPosLength(crdt) {
256 | let numChars = 0;
257 |
258 | const posArray = crdt.struct.map(line => line.map(char => char.position.length));
259 | const posLengthSum = posArray.reduce((acc, line) => {
260 | return acc + line.reduce((acc, len) => {
261 | numChars++;
262 | return acc + len;
263 | }, 0);
264 | }, 0);
265 |
266 | return Math.floor(posLengthSum / numChars);
267 | }
268 |
269 | function average(time, operations) {
270 | return time / operations;
271 | }
272 |
273 | function addPadding(value, cellSize) {
274 | value = String(value);
275 |
276 | if (value.length > cellSize) {
277 | value = value.slice(0, cellSize);
278 | }
279 |
280 | const padding = ((cellSize - value.length) / 2);
281 | return (' ').repeat(Math.floor(padding)) + value + (' ').repeat(Math.ceil(padding));
282 | }
283 |
284 | function addRowWithId(operations, crdt, func) {
285 | const totalTime = func(crdt, operations);
286 | const cell1 = addPadding(operations, CELL_1_SIZE);
287 | const cell2 = addPadding(totalTime, CELL_2_SIZE);
288 | const cell3 = addPadding(average(totalTime, operations), CELL_3_SIZE);
289 | const cell4 = addPadding(avgIdLength(crdt), CELL_4_SIZE);
290 | const cell5 = addPadding(avgPosLength(crdt), CELL_5_SIZE);
291 |
292 | return `|${cell1}|${cell2}|${cell3}|${cell4}|${cell5}|
293 | ${'-'.repeat(95)}`
294 |
295 | }
296 |
297 | function addRow(operations, crdt, func) {
298 | const totalTime = func(crdt, operations);
299 | const cell1 = addPadding(operations, CELL_1_SIZE);
300 | const cell2 = addPadding(totalTime, CELL_2_SIZE);
301 | const cell3 = addPadding(average(totalTime, operations), CELL_3_SIZE);
302 |
303 | return `|${cell1}|${cell2}|${cell3}|
304 | ${'-'.repeat(62)}`
305 | }
306 |
307 | function getTimestamp() {
308 | const now = new Date();
309 | const year = now.getUTCFullYear();
310 | const month = now.getUTCMonth() + 1;
311 | const date = now.getUTCDate();
312 | const hours = now.getUTCHours();
313 | const minutes = now.getUTCMinutes();
314 | const seconds = now.getUTCSeconds();
315 |
316 | return `${year}-${month}-${date} ${hours}:${minutes}:${seconds}`;
317 | }
318 |
319 | export {
320 | addRow,
321 | addRowWithId,
322 | insertRandom,
323 | remoteInsertRandom,
324 | insertEnd,
325 | remoteInsertEnd,
326 | insertBeginning,
327 | remoteInsertBeginning,
328 | deleteRandom,
329 | remoteDeleteRandom,
330 | deleteEnd,
331 | remoteDeleteEnd,
332 | deleteBeginning,
333 | remoteDeleteBeginning,
334 | getTimestamp
335 | };
336 |
--------------------------------------------------------------------------------
/performance/comparisons/linearArray/doubleBase/2017-11-17 5:39:6.log:
--------------------------------------------------------------------------------
1 |
2 | #### PERFORMANCE METRICS
3 | Base: 32 | Boundary: 10 | Strategy: minus
4 | ================================================================================================
5 |
6 | ## RANDOM
7 | ---------
8 |
9 | # LOCAL INSERTIONS
10 | -----------------------------------------------------------------------------------------------
11 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
12 | | | (in milliseconds) | (in milliseconds) | | Length |
13 | -----------------------------------------------------------------------------------------------
14 |
15 | | 10 | 0 | 0 | 2 | 1 |
16 | -----------------------------------------------------------------------------------------------
17 | | 100 | 0 | 0 | 5 | 2 |
18 | -----------------------------------------------------------------------------------------------
19 | | 1000 | 5 | 0.005 | 8 | 3 |
20 | -----------------------------------------------------------------------------------------------
21 |
22 | # LOCAL DELETIONS
23 | --------------------------------------------------------------
24 | | # of Operations | Total Execute Time | Avg. Operation Time |
25 | | | (in milliseconds) | (in milliseconds) |
26 | --------------------------------------------------------------
27 | | 10 | 0 | 0 |
28 | --------------------------------------------------------------
29 | | 100 | 0 | 0 |
30 | --------------------------------------------------------------
31 | | 1000 | 1 | 0.001 |
32 | --------------------------------------------------------------
33 |
34 | # REMOTE INSERTIONS
35 | --------------------------------------------------------------
36 | | # of Operations | Total Execute Time | Avg. Operation Time |
37 | | | (in milliseconds) | (in milliseconds) |
38 | --------------------------------------------------------------
39 | | 10 | 1 | 0.1 |
40 | --------------------------------------------------------------
41 | | 100 | 0 | 0 |
42 | --------------------------------------------------------------
43 | | 1000 | 5 | 0.005 |
44 | --------------------------------------------------------------
45 |
46 | # REMOTE DELETIONS
47 | --------------------------------------------------------------
48 | | # of Operations | Total Execute Time | Avg. Operation Time |
49 | | | (in milliseconds) | (in milliseconds) |
50 | --------------------------------------------------------------
51 | | 10 | 0 | 0 |
52 | --------------------------------------------------------------
53 | | 100 | 0 | 0 |
54 | --------------------------------------------------------------
55 | | 1000 | 3 | 0.003 |
56 | --------------------------------------------------------------
57 |
58 |
59 | ## AT THE BEGINNING
60 | -------------------
61 |
62 | # LOCAL INSERTIONS
63 | -----------------------------------------------------------------------------------------------
64 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
65 | | | (in milliseconds) | (in milliseconds) | | Length |
66 | -----------------------------------------------------------------------------------------------
67 | | 10 | 0 | 0 | 2 | 1 |
68 | -----------------------------------------------------------------------------------------------
69 | | 100 | 0 | 0 | 4 | 3 |
70 | -----------------------------------------------------------------------------------------------
71 | | 1000 | 4 | 0.004 | 8 | 6 |
72 | -----------------------------------------------------------------------------------------------
73 |
74 | # LOCAL DELETIONS
75 | --------------------------------------------------------------
76 | | # of Operations | Total Execute Time | Avg. Operation Time |
77 | | | (in milliseconds) | (in milliseconds) |
78 | --------------------------------------------------------------
79 | | 10 | 0 | 0 |
80 | --------------------------------------------------------------
81 | | 100 | 0 | 0 |
82 | --------------------------------------------------------------
83 | | 1000 | 1 | 0.001 |
84 | --------------------------------------------------------------
85 |
86 | # REMOTE INSERTIONS
87 | --------------------------------------------------------------
88 | | # of Operations | Total Execute Time | Avg. Operation Time |
89 | | | (in milliseconds) | (in milliseconds) |
90 | --------------------------------------------------------------
91 | | 10 | 0 | 0 |
92 | --------------------------------------------------------------
93 | | 100 | 0 | 0 |
94 | --------------------------------------------------------------
95 | | 1000 | 1 | 0.001 |
96 | --------------------------------------------------------------
97 |
98 | # REMOTE DELETIONS
99 | --------------------------------------------------------------
100 | | # of Operations | Total Execute Time | Avg. Operation Time |
101 | | | (in milliseconds) | (in milliseconds) |
102 | --------------------------------------------------------------
103 | | 10 | 0 | 0 |
104 | --------------------------------------------------------------
105 | | 100 | 0 | 0 |
106 | --------------------------------------------------------------
107 | | 1000 | 1 | 0.001 |
108 | --------------------------------------------------------------
109 |
110 |
111 | ## AT THE END
112 | -------------
113 |
114 | # LOCAL INSERTIONS
115 | -----------------------------------------------------------------------------------------------
116 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
117 | | | (in milliseconds) | (in milliseconds) | | Length |
118 | -----------------------------------------------------------------------------------------------
119 | | 10 | 0 | 0 | 5 | 2 |
120 | -----------------------------------------------------------------------------------------------
121 | | 100 | 2 | 0.02 | 106 | 18 |
122 | -----------------------------------------------------------------------------------------------
123 | | 1000 | 573 | 0.573 | 8068 | 398 |
124 | -----------------------------------------------------------------------------------------------
125 |
126 | # LOCAL DELETIONS
127 | --------------------------------------------------------------
128 | | # of Operations | Total Execute Time | Avg. Operation Time |
129 | | | (in milliseconds) | (in milliseconds) |
130 | --------------------------------------------------------------
131 | | 10 | 0 | 0 |
132 | --------------------------------------------------------------
133 | | 100 | 0 | 0 |
134 | --------------------------------------------------------------
135 | | 1000 | 1 | 0.001 |
136 | --------------------------------------------------------------
137 |
138 | # REMOTE INSERTIONS
139 | --------------------------------------------------------------
140 | | # of Operations | Total Execute Time | Avg. Operation Time |
141 | | | (in milliseconds) | (in milliseconds) |
142 | --------------------------------------------------------------
143 | | 10 | 0 | 0 |
144 | --------------------------------------------------------------
145 | | 100 | 1 | 0.01 |
146 | --------------------------------------------------------------
147 | | 1000 | 5 | 0.005 |
148 | --------------------------------------------------------------
149 |
150 | # REMOTE DELETIONS
151 | --------------------------------------------------------------
152 | | # of Operations | Total Execute Time | Avg. Operation Time |
153 | | | (in milliseconds) | (in milliseconds) |
154 | --------------------------------------------------------------
155 | | 10 | 0 | 0 |
156 | --------------------------------------------------------------
157 | | 100 | 0 | 0 |
158 | --------------------------------------------------------------
159 | | 1000 | 6 | 0.006 |
160 | --------------------------------------------------------------
161 |
--------------------------------------------------------------------------------
/performance/comparisons/linearArray/doubleBase/2017-11-17 5:38:54.log:
--------------------------------------------------------------------------------
1 |
2 | #### PERFORMANCE METRICS
3 | Base: 3200 | Boundary: 3000 | Strategy: minus
4 | ================================================================================================
5 |
6 | ## RANDOM
7 | ---------
8 |
9 | # LOCAL INSERTIONS
10 | -----------------------------------------------------------------------------------------------
11 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
12 | | | (in milliseconds) | (in milliseconds) | | Length |
13 | -----------------------------------------------------------------------------------------------
14 |
15 | | 10 | 1 | 0.1 | 3 | 1 |
16 | -----------------------------------------------------------------------------------------------
17 | | 100 | 1 | 0.01 | 4 | 1 |
18 | -----------------------------------------------------------------------------------------------
19 | | 1000 | 8 | 0.008 | 6 | 2 |
20 | -----------------------------------------------------------------------------------------------
21 |
22 | # LOCAL DELETIONS
23 | --------------------------------------------------------------
24 | | # of Operations | Total Execute Time | Avg. Operation Time |
25 | | | (in milliseconds) | (in milliseconds) |
26 | --------------------------------------------------------------
27 | | 10 | 0 | 0 |
28 | --------------------------------------------------------------
29 | | 100 | 0 | 0 |
30 | --------------------------------------------------------------
31 | | 1000 | 1 | 0.001 |
32 | --------------------------------------------------------------
33 |
34 | # REMOTE INSERTIONS
35 | --------------------------------------------------------------
36 | | # of Operations | Total Execute Time | Avg. Operation Time |
37 | | | (in milliseconds) | (in milliseconds) |
38 | --------------------------------------------------------------
39 | | 10 | 1 | 0.1 |
40 | --------------------------------------------------------------
41 | | 100 | 0 | 0 |
42 | --------------------------------------------------------------
43 | | 1000 | 6 | 0.006 |
44 | --------------------------------------------------------------
45 |
46 | # REMOTE DELETIONS
47 | --------------------------------------------------------------
48 | | # of Operations | Total Execute Time | Avg. Operation Time |
49 | | | (in milliseconds) | (in milliseconds) |
50 | --------------------------------------------------------------
51 | | 10 | 0 | 0 |
52 | --------------------------------------------------------------
53 | | 100 | 0 | 0 |
54 | --------------------------------------------------------------
55 | | 1000 | 3 | 0.003 |
56 | --------------------------------------------------------------
57 |
58 |
59 | ## AT THE BEGINNING
60 | -------------------
61 |
62 | # LOCAL INSERTIONS
63 | -----------------------------------------------------------------------------------------------
64 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
65 | | | (in milliseconds) | (in milliseconds) | | Length |
66 | -----------------------------------------------------------------------------------------------
67 | | 10 | 0 | 0 | 2 | 1 |
68 | -----------------------------------------------------------------------------------------------
69 | | 100 | 0 | 0 | 6 | 4 |
70 | -----------------------------------------------------------------------------------------------
71 | | 1000 | 4 | 0.004 | 12 | 7 |
72 | -----------------------------------------------------------------------------------------------
73 |
74 | # LOCAL DELETIONS
75 | --------------------------------------------------------------
76 | | # of Operations | Total Execute Time | Avg. Operation Time |
77 | | | (in milliseconds) | (in milliseconds) |
78 | --------------------------------------------------------------
79 | | 10 | 0 | 0 |
80 | --------------------------------------------------------------
81 | | 100 | 0 | 0 |
82 | --------------------------------------------------------------
83 | | 1000 | 1 | 0.001 |
84 | --------------------------------------------------------------
85 |
86 | # REMOTE INSERTIONS
87 | --------------------------------------------------------------
88 | | # of Operations | Total Execute Time | Avg. Operation Time |
89 | | | (in milliseconds) | (in milliseconds) |
90 | --------------------------------------------------------------
91 | | 10 | 0 | 0 |
92 | --------------------------------------------------------------
93 | | 100 | 0 | 0 |
94 | --------------------------------------------------------------
95 | | 1000 | 1 | 0.001 |
96 | --------------------------------------------------------------
97 |
98 | # REMOTE DELETIONS
99 | --------------------------------------------------------------
100 | | # of Operations | Total Execute Time | Avg. Operation Time |
101 | | | (in milliseconds) | (in milliseconds) |
102 | --------------------------------------------------------------
103 | | 10 | 0 | 0 |
104 | --------------------------------------------------------------
105 | | 100 | 1 | 0.01 |
106 | --------------------------------------------------------------
107 | | 1000 | 0 | 0 |
108 | --------------------------------------------------------------
109 |
110 |
111 | ## AT THE END
112 | -------------
113 |
114 | # LOCAL INSERTIONS
115 | -----------------------------------------------------------------------------------------------
116 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
117 | | | (in milliseconds) | (in milliseconds) | | Length |
118 | -----------------------------------------------------------------------------------------------
119 | | 10 | 0 | 0 | 5 | 1 |
120 | -----------------------------------------------------------------------------------------------
121 | | 100 | 0 | 0 | 27 | 5 |
122 | -----------------------------------------------------------------------------------------------
123 | | 1000 | 257 | 0.257 | 4156 | 211 |
124 | -----------------------------------------------------------------------------------------------
125 |
126 | # LOCAL DELETIONS
127 | --------------------------------------------------------------
128 | | # of Operations | Total Execute Time | Avg. Operation Time |
129 | | | (in milliseconds) | (in milliseconds) |
130 | --------------------------------------------------------------
131 | | 10 | 0 | 0 |
132 | --------------------------------------------------------------
133 | | 100 | 0 | 0 |
134 | --------------------------------------------------------------
135 | | 1000 | 0 | 0 |
136 | --------------------------------------------------------------
137 |
138 | # REMOTE INSERTIONS
139 | --------------------------------------------------------------
140 | | # of Operations | Total Execute Time | Avg. Operation Time |
141 | | | (in milliseconds) | (in milliseconds) |
142 | --------------------------------------------------------------
143 | | 10 | 0 | 0 |
144 | --------------------------------------------------------------
145 | | 100 | 1 | 0.01 |
146 | --------------------------------------------------------------
147 | | 1000 | 4 | 0.004 |
148 | --------------------------------------------------------------
149 |
150 | # REMOTE DELETIONS
151 | --------------------------------------------------------------
152 | | # of Operations | Total Execute Time | Avg. Operation Time |
153 | | | (in milliseconds) | (in milliseconds) |
154 | --------------------------------------------------------------
155 | | 10 | 0 | 0 |
156 | --------------------------------------------------------------
157 | | 100 | 0 | 0 |
158 | --------------------------------------------------------------
159 | | 1000 | 5 | 0.005 |
160 | --------------------------------------------------------------
161 |
--------------------------------------------------------------------------------
/lib/editor.js:
--------------------------------------------------------------------------------
1 | import CRDT from './crdt';
2 | import RemoteCursor from './remoteCursor';
3 |
4 | class Editor {
5 | constructor(mde) {
6 | this.controller = null;
7 | this.mde = mde;
8 | this.remoteCursors = {};
9 | this.customTabBehavior();
10 | }
11 |
12 | customTabBehavior() {
13 | this.mde.codemirror.setOption("extraKeys", {
14 | Tab: function(codemirror) {
15 | codemirror.replaceSelection("\t");
16 | }
17 | });
18 | }
19 |
20 | bindButtons() {
21 | if (this.controller.urlId == 0) {
22 | this.bindUploadButton();
23 | } else {
24 | this.hideUploadButton();
25 | }
26 |
27 | this.bindDownloadButton();
28 | }
29 |
30 | bindDownloadButton() {
31 | const dlButton = document.querySelector('#download');
32 |
33 | dlButton.onclick = () => {
34 | const textToSave = this.mde.value();
35 | const textAsBlob = new Blob([textToSave], { type:"text/plain" });
36 | const textAsURL = window.URL.createObjectURL(textAsBlob);
37 | const fileName = "Conclave-"+Date.now();
38 | const downloadLink = document.createElement("a");
39 |
40 | downloadLink.download = fileName;
41 | downloadLink.innerHTML = "Download File";
42 | downloadLink.href = textAsURL;
43 | downloadLink.onclick = this.afterDownload;
44 | downloadLink.style.display = "none";
45 |
46 | document.body.appendChild(downloadLink);
47 | downloadLink.click();
48 | };
49 | }
50 |
51 | afterDownload(e, doc=document) {
52 | doc.body.removeChild(e.target);
53 | }
54 |
55 | hideUploadButton(doc=document) {
56 | const ulButton = doc.querySelector('#upload');
57 | const fileInput = doc.querySelector('#file');
58 | ulButton.style.display = 'none';
59 | fileInput.style.display = 'none';
60 | }
61 |
62 | bindUploadButton(doc=document) {
63 | const fileSelect = doc.querySelector('#file');
64 | fileSelect.onchange = () => {
65 | const file = doc.querySelector("#file").files[0];
66 | const fileReader = new FileReader();
67 | fileReader.onload = (e) => {
68 | const fileText = e.target.result;
69 | this.controller.localInsert(fileText, { line: 0, ch: 0 });
70 | this.replaceText(this.controller.crdt.toText());
71 | this.hideUploadButton();
72 | }
73 | fileReader.readAsText(file, "UTF-8");
74 | }
75 | }
76 |
77 | bindChangeEvent() {
78 | this.mde.codemirror.on("change", (_, changeObj) => {
79 | if (changeObj.origin === "setValue") return;
80 | if (changeObj.origin === "insertText") return;
81 | if (changeObj.origin === "deleteText") return;
82 |
83 | switch(changeObj.origin) {
84 | case 'redo':
85 | case 'undo':
86 | this.processUndoRedo(changeObj);
87 | break;
88 | case '*compose':
89 | case '+input':
90 | // this.processInsert(changeObj); // uncomment this line for palindromes!
91 | case 'paste':
92 | this.processInsert(changeObj);
93 | break;
94 | case '+delete':
95 | case 'cut':
96 | this.processDelete(changeObj);
97 | break;
98 | default:
99 | throw new Error("Unknown operation attempted in editor.");
100 | }
101 | });
102 | }
103 |
104 | processInsert(changeObj) {
105 | this.processDelete(changeObj);
106 | const chars = this.extractChars(changeObj.text);
107 | const startPos = changeObj.from;
108 |
109 | this.updateRemoteCursorsInsert(chars, changeObj.to);
110 | this.controller.localInsert(chars, startPos);
111 | }
112 |
113 | isEmpty(textArr) {
114 | return textArr.length === 1 && textArr[0].length === 0;
115 | }
116 |
117 | processDelete(changeObj) {
118 | if (this.isEmpty(changeObj.removed)) return;
119 | const startPos = changeObj.from;
120 | const endPos = changeObj.to;
121 | const chars = this.extractChars(changeObj.removed);
122 |
123 | this.updateRemoteCursorsDelete(chars, changeObj.to, changeObj.from);
124 | this.controller.localDelete(startPos, endPos);
125 | }
126 |
127 | processUndoRedo(changeObj) {
128 | if (changeObj.removed[0].length > 0) {
129 | this.processDelete(changeObj);
130 | } else {
131 | this.processInsert(changeObj);
132 | }
133 | }
134 |
135 | extractChars(text) {
136 | if (text[0] === '' && text[1] === '' && text.length === 2) {
137 | return '\n';
138 | } else {
139 | return text.join("\n");
140 | }
141 | }
142 |
143 | replaceText(text) {
144 | const cursor = this.mde.codemirror.getCursor();
145 | this.mde.value(text);
146 | this.mde.codemirror.setCursor(cursor);
147 | }
148 |
149 | insertText(value, positions, siteId) {
150 | const localCursor = this.mde.codemirror.getCursor();
151 | const delta = this.generateDeltaFromChars(value);
152 |
153 | this.mde.codemirror.replaceRange(value, positions.from, positions.to, 'insertText');
154 | this.updateRemoteCursorsInsert(positions.to, siteId);
155 | this.updateRemoteCursor(positions.to, siteId, 'insert', value);
156 |
157 | if (localCursor.line > positions.to.line) {
158 | localCursor.line += delta.line
159 | } else if (localCursor.line === positions.to.line && localCursor.ch > positions.to.ch) {
160 | if (delta.line > 0) {
161 | localCursor.line += delta.line
162 | localCursor.ch -= positions.to.ch;
163 | }
164 |
165 | localCursor.ch += delta.ch;
166 | }
167 |
168 | this.mde.codemirror.setCursor(localCursor);
169 | }
170 |
171 | removeCursor(siteId) {
172 | const remoteCursor = this.remoteCursors[siteId];
173 |
174 | if (remoteCursor) {
175 | remoteCursor.detach();
176 |
177 | delete this.remoteCursors[siteId];
178 | }
179 | }
180 |
181 | updateRemoteCursorsInsert(chars, position, siteId) {
182 | const positionDelta = this.generateDeltaFromChars(chars);
183 |
184 | for (const cursorSiteId in this.remoteCursors) {
185 | if (cursorSiteId === siteId) continue;
186 | const remoteCursor = this.remoteCursors[cursorSiteId];
187 | const newPosition = Object.assign({}, remoteCursor.lastPosition);
188 |
189 | if (newPosition.line > position.line) {
190 | newPosition.line += positionDelta.line;
191 | } else if (newPosition.line === position.line && newPosition.ch > position.ch) {
192 | if (positionDelta.line > 0) {
193 | newPosition.line += positionDelta.line;
194 | newPosition.ch -= position.ch;
195 | }
196 |
197 | newPosition.ch += positionDelta.ch;
198 | }
199 |
200 | remoteCursor.set(newPosition)
201 | }
202 | }
203 |
204 | updateRemoteCursorsDelete(chars, to, from, siteId) {
205 | const positionDelta = this.generateDeltaFromChars(chars);
206 |
207 | for (const cursorSiteId in this.remoteCursors) {
208 | if (cursorSiteId === siteId) continue;
209 | const remoteCursor = this.remoteCursors[cursorSiteId];
210 | const newPosition = Object.assign({}, remoteCursor.lastPosition);
211 |
212 | if (newPosition.line > to.line) {
213 | newPosition.line -= positionDelta.line;
214 | } else if (newPosition.line === to.line && newPosition.ch > to.ch) {
215 | if (positionDelta.line > 0) {
216 | newPosition.line -= positionDelta.line;
217 | newPosition.ch += from.ch;
218 | }
219 |
220 | newPosition.ch -= positionDelta.ch;
221 | }
222 |
223 | remoteCursor.set(newPosition)
224 | }
225 | }
226 |
227 | updateRemoteCursor(position, siteId, opType, value) {
228 | const remoteCursor = this.remoteCursors[siteId];
229 | const clonedPosition = Object.assign({}, position);
230 |
231 | if (opType === 'insert') {
232 | if (value === '\n') {
233 | clonedPosition.line++;
234 | clonedPosition.ch = 0
235 | } else {
236 | clonedPosition.ch++;
237 | }
238 | } else {
239 | clonedPosition.ch--;
240 | }
241 |
242 | if (remoteCursor) {
243 | remoteCursor.set(clonedPosition);
244 | } else {
245 | this.remoteCursors[siteId] = new RemoteCursor(this.mde, siteId, clonedPosition);
246 | }
247 | }
248 |
249 | deleteText(value, positions, siteId) {
250 | const localCursor = this.mde.codemirror.getCursor();
251 | const delta = this.generateDeltaFromChars(value);
252 |
253 | this.mde.codemirror.replaceRange("", positions.from, positions.to, 'deleteText');
254 | this.updateRemoteCursorsDelete(positions.to, siteId);
255 | this.updateRemoteCursor(positions.to, siteId, 'delete');
256 |
257 | if (localCursor.line > positions.to.line) {
258 | localCursor.line -= delta.line;
259 | } else if (localCursor.line === positions.to.line && localCursor.ch > positions.to.ch) {
260 | if (delta.line > 0) {
261 | localCursor.line -= delta.line;
262 | localCursor.ch += positions.from.ch;
263 | }
264 |
265 | localCursor.ch -= delta.ch;
266 | }
267 |
268 | this.mde.codemirror.setCursor(localCursor);
269 | }
270 |
271 | findLinearIdx(lineIdx, chIdx) {
272 | const linesOfText = this.controller.crdt.text.split("\n");
273 |
274 | let index = 0
275 | for (let i = 0; i < lineIdx; i++) {
276 | index += linesOfText[i].length + 1;
277 | }
278 |
279 | return index + chIdx;
280 | }
281 |
282 | generateDeltaFromChars(chars) {
283 | const delta = { line: 0, ch: 0 };
284 | let counter = 0;
285 |
286 | while (counter < chars.length) {
287 | if (chars[counter] === '\n') {
288 | delta.line++;
289 | delta.ch = 0;
290 | } else {
291 | delta.ch++;
292 | }
293 |
294 | counter++;
295 | }
296 |
297 | return delta;
298 | }
299 | }
300 |
301 | export default Editor;
302 |
--------------------------------------------------------------------------------
/performance/comparisons/linearArray/doubleBase/2017-11-17 1:29:26.log:
--------------------------------------------------------------------------------
1 |
2 | #### PERFORMANCE METRICS
3 | Base: 32 | Boundary: 10 | Strategy: plus
4 | ================================================================================================
5 |
6 | ## RANDOM
7 | ---------
8 |
9 | # LOCAL INSERTIONS
10 | -----------------------------------------------------------------------------------------------
11 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
12 | | | (in milliseconds) | (in milliseconds) | | Length |
13 | -----------------------------------------------------------------------------------------------
14 |
15 | | 10 | 1 | 0.1 | 1 | 1 |
16 | -----------------------------------------------------------------------------------------------
17 | | 100 | 1 | 0.01 | 3 | 2 |
18 | -----------------------------------------------------------------------------------------------
19 | | 1000 | 5 | 0.005 | 4 | 4 |
20 | -----------------------------------------------------------------------------------------------
21 | | 10000 | 56 | 0.0056 | 6 | 5 |
22 | -----------------------------------------------------------------------------------------------
23 |
24 | # LOCAL DELETIONS
25 | --------------------------------------------------------------
26 | | # of Operations | Total Execute Time | Avg. Operation Time |
27 | | | (in milliseconds) | (in milliseconds) |
28 | --------------------------------------------------------------
29 | | 10 | 0 | 0 |
30 | --------------------------------------------------------------
31 | | 100 | 0 | 0 |
32 | --------------------------------------------------------------
33 | | 1000 | 1 | 0.001 |
34 | --------------------------------------------------------------
35 | | 10000 | 32 | 0.0032 |
36 | --------------------------------------------------------------
37 |
38 | # REMOTE INSERTIONS
39 | --------------------------------------------------------------
40 | | # of Operations | Total Execute Time | Avg. Operation Time |
41 | | | (in milliseconds) | (in milliseconds) |
42 | --------------------------------------------------------------
43 | | 10 | 0 | 0 |
44 | --------------------------------------------------------------
45 | | 100 | 0 | 0 |
46 | --------------------------------------------------------------
47 | | 1000 | 8 | 0.008 |
48 | --------------------------------------------------------------
49 | | 10000 | 36 | 0.0036 |
50 | --------------------------------------------------------------
51 |
52 | # REMOTE DELETIONS
53 | --------------------------------------------------------------
54 | | # of Operations | Total Execute Time | Avg. Operation Time |
55 | | | (in milliseconds) | (in milliseconds) |
56 | --------------------------------------------------------------
57 | | 10 | 0 | 0 |
58 | --------------------------------------------------------------
59 | | 100 | 0 | 0 |
60 | --------------------------------------------------------------
61 | | 1000 | 4 | 0.004 |
62 | --------------------------------------------------------------
63 | | 10000 | 34 | 0.0034 |
64 | --------------------------------------------------------------
65 |
66 |
67 | ## AT THE BEGINNING
68 | -------------------
69 |
70 | # LOCAL INSERTIONS
71 | -----------------------------------------------------------------------------------------------
72 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
73 | | | (in milliseconds) | (in milliseconds) | | Length |
74 | -----------------------------------------------------------------------------------------------
75 | | 10 | 0 | 0 | 3 | 3 |
76 | -----------------------------------------------------------------------------------------------
77 | | 100 | 1 | 0.01 | 17 | 17 |
78 | -----------------------------------------------------------------------------------------------
79 | | 1000 | 201 | 0.201 | 175 | 175 |
80 | -----------------------------------------------------------------------------------------------
81 | | 10000 | 525798 | 52.5798 | 1697 | 1697 |
82 | -----------------------------------------------------------------------------------------------
83 |
84 | # LOCAL DELETIONS
85 | --------------------------------------------------------------
86 | | # of Operations | Total Execute Time | Avg. Operation Time |
87 | | | (in milliseconds) | (in milliseconds) |
88 | --------------------------------------------------------------
89 | | 10 | 0 | 0 |
90 | --------------------------------------------------------------
91 | | 100 | 0 | 0 |
92 | --------------------------------------------------------------
93 | | 1000 | 0 | 0 |
94 | --------------------------------------------------------------
95 | | 10000 | 4 | 0.0004 |
96 | --------------------------------------------------------------
97 |
98 | # REMOTE INSERTIONS
99 | --------------------------------------------------------------
100 | | # of Operations | Total Execute Time | Avg. Operation Time |
101 | | | (in milliseconds) | (in milliseconds) |
102 | --------------------------------------------------------------
103 | | 10 | 1 | 0.1 |
104 | --------------------------------------------------------------
105 | | 100 | 0 | 0 |
106 | --------------------------------------------------------------
107 | | 1000 | 4 | 0.004 |
108 | --------------------------------------------------------------
109 | | 10000 | 25 | 0.0025 |
110 | --------------------------------------------------------------
111 |
112 | # REMOTE DELETIONS
113 | --------------------------------------------------------------
114 | | # of Operations | Total Execute Time | Avg. Operation Time |
115 | | | (in milliseconds) | (in milliseconds) |
116 | --------------------------------------------------------------
117 | | 10 | 0 | 0 |
118 | --------------------------------------------------------------
119 | | 100 | 1 | 0.01 |
120 | --------------------------------------------------------------
121 | | 1000 | 2 | 0.002 |
122 | --------------------------------------------------------------
123 | | 10000 | 27 | 0.0027 |
124 | --------------------------------------------------------------
125 |
126 |
127 | ## AT THE END
128 | -------------
129 |
130 | # LOCAL INSERTIONS
131 | -----------------------------------------------------------------------------------------------
132 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
133 | | | (in milliseconds) | (in milliseconds) | | Length |
134 | -----------------------------------------------------------------------------------------------
135 | | 10 | 0 | 0 | 2 | 1 |
136 | -----------------------------------------------------------------------------------------------
137 | | 100 | 0 | 0 | 7 | 3 |
138 | -----------------------------------------------------------------------------------------------
139 | | 1000 | 2 | 0.002 | 18 | 6 |
140 | -----------------------------------------------------------------------------------------------
141 | | 10000 | 50 | 0.005 | 32 | 9 |
142 | -----------------------------------------------------------------------------------------------
143 |
144 | # LOCAL DELETIONS
145 | --------------------------------------------------------------
146 | | # of Operations | Total Execute Time | Avg. Operation Time |
147 | | | (in milliseconds) | (in milliseconds) |
148 | --------------------------------------------------------------
149 | | 10 | 0 | 0 |
150 | --------------------------------------------------------------
151 | | 100 | 0 | 0 |
152 | --------------------------------------------------------------
153 | | 1000 | 0 | 0 |
154 | --------------------------------------------------------------
155 | | 10000 | 3 | 0.0003 |
156 | --------------------------------------------------------------
157 |
158 | # REMOTE INSERTIONS
159 | --------------------------------------------------------------
160 | | # of Operations | Total Execute Time | Avg. Operation Time |
161 | | | (in milliseconds) | (in milliseconds) |
162 | --------------------------------------------------------------
163 | | 10 | 0 | 0 |
164 | --------------------------------------------------------------
165 | | 100 | 0 | 0 |
166 | --------------------------------------------------------------
167 | | 1000 | 1 | 0.001 |
168 | --------------------------------------------------------------
169 | | 10000 | 18 | 0.0018 |
170 | --------------------------------------------------------------
171 |
172 | # REMOTE DELETIONS
173 | --------------------------------------------------------------
174 | | # of Operations | Total Execute Time | Avg. Operation Time |
175 | | | (in milliseconds) | (in milliseconds) |
176 | --------------------------------------------------------------
177 | | 10 | 0 | 0 |
178 | --------------------------------------------------------------
179 | | 100 | 0 | 0 |
180 | --------------------------------------------------------------
181 | | 1000 | 2 | 0.002 |
182 | --------------------------------------------------------------
183 | | 10000 | 25 | 0.0025 |
184 | --------------------------------------------------------------
185 |
--------------------------------------------------------------------------------
/performance/comparisons/linearArray/tripleBase/2017-11-17 1:44:28.log:
--------------------------------------------------------------------------------
1 |
2 | #### PERFORMANCE METRICS
3 | Base: 32 | Boundary: 10 | Strategy: plus
4 | ================================================================================================
5 |
6 | ## RANDOM
7 | ---------
8 |
9 | # LOCAL INSERTIONS
10 | -----------------------------------------------------------------------------------------------
11 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
12 | | | (in milliseconds) | (in milliseconds) | | Length |
13 | -----------------------------------------------------------------------------------------------
14 |
15 | | 10 | 1 | 0.1 | 2 | 2 |
16 | -----------------------------------------------------------------------------------------------
17 | | 100 | 1 | 0.01 | 2 | 2 |
18 | -----------------------------------------------------------------------------------------------
19 | | 1000 | 7 | 0.007 | 5 | 4 |
20 | -----------------------------------------------------------------------------------------------
21 | | 10000 | 60 | 0.006 | 6 | 4 |
22 | -----------------------------------------------------------------------------------------------
23 |
24 | # LOCAL DELETIONS
25 | --------------------------------------------------------------
26 | | # of Operations | Total Execute Time | Avg. Operation Time |
27 | | | (in milliseconds) | (in milliseconds) |
28 | --------------------------------------------------------------
29 | | 10 | 0 | 0 |
30 | --------------------------------------------------------------
31 | | 100 | 0 | 0 |
32 | --------------------------------------------------------------
33 | | 1000 | 1 | 0.001 |
34 | --------------------------------------------------------------
35 | | 10000 | 13 | 0.0013 |
36 | --------------------------------------------------------------
37 |
38 | # REMOTE INSERTIONS
39 | --------------------------------------------------------------
40 | | # of Operations | Total Execute Time | Avg. Operation Time |
41 | | | (in milliseconds) | (in milliseconds) |
42 | --------------------------------------------------------------
43 | | 10 | 0 | 0 |
44 | --------------------------------------------------------------
45 | | 100 | 0 | 0 |
46 | --------------------------------------------------------------
47 | | 1000 | 8 | 0.008 |
48 | --------------------------------------------------------------
49 | | 10000 | 41 | 0.0041 |
50 | --------------------------------------------------------------
51 |
52 | # REMOTE DELETIONS
53 | --------------------------------------------------------------
54 | | # of Operations | Total Execute Time | Avg. Operation Time |
55 | | | (in milliseconds) | (in milliseconds) |
56 | --------------------------------------------------------------
57 | | 10 | 0 | 0 |
58 | --------------------------------------------------------------
59 | | 100 | 0 | 0 |
60 | --------------------------------------------------------------
61 | | 1000 | 3 | 0.003 |
62 | --------------------------------------------------------------
63 | | 10000 | 35 | 0.0035 |
64 | --------------------------------------------------------------
65 |
66 |
67 | ## AT THE BEGINNING
68 | -------------------
69 |
70 | # LOCAL INSERTIONS
71 | -----------------------------------------------------------------------------------------------
72 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
73 | | | (in milliseconds) | (in milliseconds) | | Length |
74 | -----------------------------------------------------------------------------------------------
75 | | 10 | 0 | 0 | 3 | 3 |
76 | -----------------------------------------------------------------------------------------------
77 | | 100 | 3 | 0.03 | 19 | 18 |
78 | -----------------------------------------------------------------------------------------------
79 | | 1000 | 199 | 0.199 | 175 | 175 |
80 | -----------------------------------------------------------------------------------------------
81 | | 10000 | 523694 | 52.3694 | 1689 | 1689 |
82 | -----------------------------------------------------------------------------------------------
83 |
84 | # LOCAL DELETIONS
85 | --------------------------------------------------------------
86 | | # of Operations | Total Execute Time | Avg. Operation Time |
87 | | | (in milliseconds) | (in milliseconds) |
88 | --------------------------------------------------------------
89 | | 10 | 0 | 0 |
90 | --------------------------------------------------------------
91 | | 100 | 0 | 0 |
92 | --------------------------------------------------------------
93 | | 1000 | 1 | 0.001 |
94 | --------------------------------------------------------------
95 | | 10000 | 4 | 0.0004 |
96 | --------------------------------------------------------------
97 |
98 | # REMOTE INSERTIONS
99 | --------------------------------------------------------------
100 | | # of Operations | Total Execute Time | Avg. Operation Time |
101 | | | (in milliseconds) | (in milliseconds) |
102 | --------------------------------------------------------------
103 | | 10 | 0 | 0 |
104 | --------------------------------------------------------------
105 | | 100 | 1 | 0.01 |
106 | --------------------------------------------------------------
107 | | 1000 | 10 | 0.01 |
108 | --------------------------------------------------------------
109 | | 10000 | 25 | 0.0025 |
110 | --------------------------------------------------------------
111 |
112 | # REMOTE DELETIONS
113 | --------------------------------------------------------------
114 | | # of Operations | Total Execute Time | Avg. Operation Time |
115 | | | (in milliseconds) | (in milliseconds) |
116 | --------------------------------------------------------------
117 | | 10 | 1 | 0.1 |
118 | --------------------------------------------------------------
119 | | 100 | 0 | 0 |
120 | --------------------------------------------------------------
121 | | 1000 | 3 | 0.003 |
122 | --------------------------------------------------------------
123 | | 10000 | 22 | 0.0022 |
124 | --------------------------------------------------------------
125 |
126 |
127 | ## AT THE END
128 | -------------
129 |
130 | # LOCAL INSERTIONS
131 | -----------------------------------------------------------------------------------------------
132 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
133 | | | (in milliseconds) | (in milliseconds) | | Length |
134 | -----------------------------------------------------------------------------------------------
135 | | 10 | 0 | 0 | 2 | 1 |
136 | -----------------------------------------------------------------------------------------------
137 | | 100 | 0 | 0 | 6 | 3 |
138 | -----------------------------------------------------------------------------------------------
139 | | 1000 | 13 | 0.013 | 13 | 4 |
140 | -----------------------------------------------------------------------------------------------
141 | | 10000 | 28 | 0.0028 | 22 | 7 |
142 | -----------------------------------------------------------------------------------------------
143 |
144 | # LOCAL DELETIONS
145 | --------------------------------------------------------------
146 | | # of Operations | Total Execute Time | Avg. Operation Time |
147 | | | (in milliseconds) | (in milliseconds) |
148 | --------------------------------------------------------------
149 | | 10 | 0 | 0 |
150 | --------------------------------------------------------------
151 | | 100 | 0 | 0 |
152 | --------------------------------------------------------------
153 | | 1000 | 1 | 0.001 |
154 | --------------------------------------------------------------
155 | | 10000 | 2 | 0.0002 |
156 | --------------------------------------------------------------
157 |
158 | # REMOTE INSERTIONS
159 | --------------------------------------------------------------
160 | | # of Operations | Total Execute Time | Avg. Operation Time |
161 | | | (in milliseconds) | (in milliseconds) |
162 | --------------------------------------------------------------
163 | | 10 | 0 | 0 |
164 | --------------------------------------------------------------
165 | | 100 | 0 | 0 |
166 | --------------------------------------------------------------
167 | | 1000 | 1 | 0.001 |
168 | --------------------------------------------------------------
169 | | 10000 | 15 | 0.0015 |
170 | --------------------------------------------------------------
171 |
172 | # REMOTE DELETIONS
173 | --------------------------------------------------------------
174 | | # of Operations | Total Execute Time | Avg. Operation Time |
175 | | | (in milliseconds) | (in milliseconds) |
176 | --------------------------------------------------------------
177 | | 10 | 0 | 0 |
178 | --------------------------------------------------------------
179 | | 100 | 0 | 0 |
180 | --------------------------------------------------------------
181 | | 1000 | 1 | 0.001 |
182 | --------------------------------------------------------------
183 | | 10000 | 16 | 0.0016 |
184 | --------------------------------------------------------------
185 |
--------------------------------------------------------------------------------
/performance/comparisons/linearArray/constantBase/2017-11-17 2:26:13.log:
--------------------------------------------------------------------------------
1 |
2 | #### PERFORMANCE METRICS
3 | Base: 32 | Boundary: 10 | Strategy: plus
4 | ================================================================================================
5 |
6 | ## RANDOM
7 | ---------
8 |
9 | # LOCAL INSERTIONS
10 | -----------------------------------------------------------------------------------------------
11 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
12 | | | (in milliseconds) | (in milliseconds) | | Length |
13 | -----------------------------------------------------------------------------------------------
14 |
15 | | 10 | 0 | 0 | 1 | 1 |
16 | -----------------------------------------------------------------------------------------------
17 | | 100 | 0 | 0 | 3 | 2 |
18 | -----------------------------------------------------------------------------------------------
19 | | 1000 | 6 | 0.006 | 4 | 4 |
20 | -----------------------------------------------------------------------------------------------
21 | | 10000 | 58 | 0.0058 | 5 | 4 |
22 | -----------------------------------------------------------------------------------------------
23 |
24 | # LOCAL DELETIONS
25 | --------------------------------------------------------------
26 | | # of Operations | Total Execute Time | Avg. Operation Time |
27 | | | (in milliseconds) | (in milliseconds) |
28 | --------------------------------------------------------------
29 | | 10 | 0 | 0 |
30 | --------------------------------------------------------------
31 | | 100 | 0 | 0 |
32 | --------------------------------------------------------------
33 | | 1000 | 0 | 0 |
34 | --------------------------------------------------------------
35 | | 10000 | 13 | 0.0013 |
36 | --------------------------------------------------------------
37 |
38 | # REMOTE INSERTIONS
39 | --------------------------------------------------------------
40 | | # of Operations | Total Execute Time | Avg. Operation Time |
41 | | | (in milliseconds) | (in milliseconds) |
42 | --------------------------------------------------------------
43 | | 10 | 0 | 0 |
44 | --------------------------------------------------------------
45 | | 100 | 1 | 0.01 |
46 | --------------------------------------------------------------
47 | | 1000 | 6 | 0.006 |
48 | --------------------------------------------------------------
49 | | 10000 | 29 | 0.0029 |
50 | --------------------------------------------------------------
51 |
52 | # REMOTE DELETIONS
53 | --------------------------------------------------------------
54 | | # of Operations | Total Execute Time | Avg. Operation Time |
55 | | | (in milliseconds) | (in milliseconds) |
56 | --------------------------------------------------------------
57 | | 10 | 1 | 0.1 |
58 | --------------------------------------------------------------
59 | | 100 | 0 | 0 |
60 | --------------------------------------------------------------
61 | | 1000 | 3 | 0.003 |
62 | --------------------------------------------------------------
63 | | 10000 | 27 | 0.0027 |
64 | --------------------------------------------------------------
65 |
66 |
67 | ## AT THE BEGINNING
68 | -------------------
69 |
70 | # LOCAL INSERTIONS
71 | -----------------------------------------------------------------------------------------------
72 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
73 | | | (in milliseconds) | (in milliseconds) | | Length |
74 | -----------------------------------------------------------------------------------------------
75 | | 10 | 0 | 0 | 2 | 2 |
76 | -----------------------------------------------------------------------------------------------
77 | | 100 | 1 | 0.01 | 17 | 17 |
78 | -----------------------------------------------------------------------------------------------
79 | | 1000 | 167 | 0.167 | 172 | 172 |
80 | -----------------------------------------------------------------------------------------------
81 | | 10000 | 387842 | 38.7842 | 1703 | 1703 |
82 | -----------------------------------------------------------------------------------------------
83 |
84 | # LOCAL DELETIONS
85 | --------------------------------------------------------------
86 | | # of Operations | Total Execute Time | Avg. Operation Time |
87 | | | (in milliseconds) | (in milliseconds) |
88 | --------------------------------------------------------------
89 | | 10 | 0 | 0 |
90 | --------------------------------------------------------------
91 | | 100 | 0 | 0 |
92 | --------------------------------------------------------------
93 | | 1000 | 0 | 0 |
94 | --------------------------------------------------------------
95 | | 10000 | 9 | 0.0009 |
96 | --------------------------------------------------------------
97 |
98 | # REMOTE INSERTIONS
99 | --------------------------------------------------------------
100 | | # of Operations | Total Execute Time | Avg. Operation Time |
101 | | | (in milliseconds) | (in milliseconds) |
102 | --------------------------------------------------------------
103 | | 10 | 0 | 0 |
104 | --------------------------------------------------------------
105 | | 100 | 0 | 0 |
106 | --------------------------------------------------------------
107 | | 1000 | 0 | 0 |
108 | --------------------------------------------------------------
109 | | 10000 | 27 | 0.0027 |
110 | --------------------------------------------------------------
111 |
112 | # REMOTE DELETIONS
113 | --------------------------------------------------------------
114 | | # of Operations | Total Execute Time | Avg. Operation Time |
115 | | | (in milliseconds) | (in milliseconds) |
116 | --------------------------------------------------------------
117 | | 10 | 0 | 0 |
118 | --------------------------------------------------------------
119 | | 100 | 0 | 0 |
120 | --------------------------------------------------------------
121 | | 1000 | 1 | 0.001 |
122 | --------------------------------------------------------------
123 | | 10000 | 29 | 0.0029 |
124 | --------------------------------------------------------------
125 |
126 |
127 | ## AT THE END
128 | -------------
129 |
130 | # LOCAL INSERTIONS
131 | -----------------------------------------------------------------------------------------------
132 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
133 | | | (in milliseconds) | (in milliseconds) | | Length |
134 | -----------------------------------------------------------------------------------------------
135 | | 10 | 0 | 0 | 2 | 1 |
136 | -----------------------------------------------------------------------------------------------
137 | | 100 | 0 | 0 | 15 | 7 |
138 | -----------------------------------------------------------------------------------------------
139 | | 1000 | 49 | 0.049 | 141 | 70 |
140 | -----------------------------------------------------------------------------------------------
141 | | 10000 | 29091 | 2.9091 | 1455 | 727 |
142 | -----------------------------------------------------------------------------------------------
143 |
144 | # LOCAL DELETIONS
145 | --------------------------------------------------------------
146 | | # of Operations | Total Execute Time | Avg. Operation Time |
147 | | | (in milliseconds) | (in milliseconds) |
148 | --------------------------------------------------------------
149 | | 10 | 0 | 0 |
150 | --------------------------------------------------------------
151 | | 100 | 0 | 0 |
152 | --------------------------------------------------------------
153 | | 1000 | 0 | 0 |
154 | --------------------------------------------------------------
155 | | 10000 | 2 | 0.0002 |
156 | --------------------------------------------------------------
157 |
158 | # REMOTE INSERTIONS
159 | --------------------------------------------------------------
160 | | # of Operations | Total Execute Time | Avg. Operation Time |
161 | | | (in milliseconds) | (in milliseconds) |
162 | --------------------------------------------------------------
163 | | 10 | 0 | 0 |
164 | --------------------------------------------------------------
165 | | 100 | 0 | 0 |
166 | --------------------------------------------------------------
167 | | 1000 | 1 | 0.001 |
168 | --------------------------------------------------------------
169 | | 10000 | 14 | 0.0014 |
170 | --------------------------------------------------------------
171 |
172 | # REMOTE DELETIONS
173 | --------------------------------------------------------------
174 | | # of Operations | Total Execute Time | Avg. Operation Time |
175 | | | (in milliseconds) | (in milliseconds) |
176 | --------------------------------------------------------------
177 | | 10 | 0 | 0 |
178 | --------------------------------------------------------------
179 | | 100 | 0 | 0 |
180 | --------------------------------------------------------------
181 | | 1000 | 1 | 0.001 |
182 | --------------------------------------------------------------
183 | | 10000 | 15 | 0.0015 |
184 | --------------------------------------------------------------
185 |
--------------------------------------------------------------------------------
/performance/comparisons/linearArray/constantBase/2017-11-17 5:19:28.log:
--------------------------------------------------------------------------------
1 |
2 | #### PERFORMANCE METRICS
3 | Base: 32 | Boundary: 10 | Strategy: random
4 | ================================================================================================
5 |
6 | ## RANDOM
7 | ---------
8 |
9 | # LOCAL INSERTIONS
10 | -----------------------------------------------------------------------------------------------
11 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
12 | | | (in milliseconds) | (in milliseconds) | | Length |
13 | -----------------------------------------------------------------------------------------------
14 |
15 | | 10 | 0 | 0 | 1 | 1 |
16 | -----------------------------------------------------------------------------------------------
17 | | 100 | 0 | 0 | 4 | 2 |
18 | -----------------------------------------------------------------------------------------------
19 | | 1000 | 7 | 0.007 | 7 | 4 |
20 | -----------------------------------------------------------------------------------------------
21 | | 10000 | 69 | 0.0069 | 9 | 5 |
22 | -----------------------------------------------------------------------------------------------
23 |
24 | # LOCAL DELETIONS
25 | --------------------------------------------------------------
26 | | # of Operations | Total Execute Time | Avg. Operation Time |
27 | | | (in milliseconds) | (in milliseconds) |
28 | --------------------------------------------------------------
29 | | 10 | 0 | 0 |
30 | --------------------------------------------------------------
31 | | 100 | 0 | 0 |
32 | --------------------------------------------------------------
33 | | 1000 | 0 | 0 |
34 | --------------------------------------------------------------
35 | | 10000 | 13 | 0.0013 |
36 | --------------------------------------------------------------
37 |
38 | # REMOTE INSERTIONS
39 | --------------------------------------------------------------
40 | | # of Operations | Total Execute Time | Avg. Operation Time |
41 | | | (in milliseconds) | (in milliseconds) |
42 | --------------------------------------------------------------
43 | | 10 | 0 | 0 |
44 | --------------------------------------------------------------
45 | | 100 | 0 | 0 |
46 | --------------------------------------------------------------
47 | | 1000 | 7 | 0.007 |
48 | --------------------------------------------------------------
49 | | 10000 | 34 | 0.0034 |
50 | --------------------------------------------------------------
51 |
52 | # REMOTE DELETIONS
53 | --------------------------------------------------------------
54 | | # of Operations | Total Execute Time | Avg. Operation Time |
55 | | | (in milliseconds) | (in milliseconds) |
56 | --------------------------------------------------------------
57 | | 10 | 0 | 0 |
58 | --------------------------------------------------------------
59 | | 100 | 0 | 0 |
60 | --------------------------------------------------------------
61 | | 1000 | 5 | 0.005 |
62 | --------------------------------------------------------------
63 | | 10000 | 31 | 0.0031 |
64 | --------------------------------------------------------------
65 |
66 |
67 | ## AT THE BEGINNING
68 | -------------------
69 |
70 | # LOCAL INSERTIONS
71 | -----------------------------------------------------------------------------------------------
72 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
73 | | | (in milliseconds) | (in milliseconds) | | Length |
74 | -----------------------------------------------------------------------------------------------
75 | | 10 | 0 | 0 | 1 | 1 |
76 | -----------------------------------------------------------------------------------------------
77 | | 100 | 1 | 0.01 | 10 | 10 |
78 | -----------------------------------------------------------------------------------------------
79 | | 1000 | 93 | 0.093 | 106 | 106 |
80 | -----------------------------------------------------------------------------------------------
81 | | 10000 | 57860 | 5.786 | 1027 | 1027 |
82 | -----------------------------------------------------------------------------------------------
83 |
84 | # LOCAL DELETIONS
85 | --------------------------------------------------------------
86 | | # of Operations | Total Execute Time | Avg. Operation Time |
87 | | | (in milliseconds) | (in milliseconds) |
88 | --------------------------------------------------------------
89 | | 10 | 0 | 0 |
90 | --------------------------------------------------------------
91 | | 100 | 0 | 0 |
92 | --------------------------------------------------------------
93 | | 1000 | 0 | 0 |
94 | --------------------------------------------------------------
95 | | 10000 | 4 | 0.0004 |
96 | --------------------------------------------------------------
97 |
98 | # REMOTE INSERTIONS
99 | --------------------------------------------------------------
100 | | # of Operations | Total Execute Time | Avg. Operation Time |
101 | | | (in milliseconds) | (in milliseconds) |
102 | --------------------------------------------------------------
103 | | 10 | 0 | 0 |
104 | --------------------------------------------------------------
105 | | 100 | 0 | 0 |
106 | --------------------------------------------------------------
107 | | 1000 | 0 | 0 |
108 | --------------------------------------------------------------
109 | | 10000 | 19 | 0.0019 |
110 | --------------------------------------------------------------
111 |
112 | # REMOTE DELETIONS
113 | --------------------------------------------------------------
114 | | # of Operations | Total Execute Time | Avg. Operation Time |
115 | | | (in milliseconds) | (in milliseconds) |
116 | --------------------------------------------------------------
117 | | 10 | 0 | 0 |
118 | --------------------------------------------------------------
119 | | 100 | 0 | 0 |
120 | --------------------------------------------------------------
121 | | 1000 | 1 | 0.001 |
122 | --------------------------------------------------------------
123 | | 10000 | 15 | 0.0015 |
124 | --------------------------------------------------------------
125 |
126 |
127 | ## AT THE END
128 | -------------
129 |
130 | # LOCAL INSERTIONS
131 | -----------------------------------------------------------------------------------------------
132 | | # of Operations | Total Execute Time | Avg. Operation Time | Avg. ID Length | Avg. Position |
133 | | | (in milliseconds) | (in milliseconds) | | Length |
134 | -----------------------------------------------------------------------------------------------
135 | | 10 | 0 | 0 | 4 | 2 |
136 | -----------------------------------------------------------------------------------------------
137 | | 100 | 0 | 0 | 20 | 10 |
138 | -----------------------------------------------------------------------------------------------
139 | | 1000 | 58 | 0.058 | 196 | 98 |
140 | -----------------------------------------------------------------------------------------------
141 | | 10000 | 51955 | 5.1955 | 2038 | 1019 |
142 | -----------------------------------------------------------------------------------------------
143 |
144 | # LOCAL DELETIONS
145 | --------------------------------------------------------------
146 | | # of Operations | Total Execute Time | Avg. Operation Time |
147 | | | (in milliseconds) | (in milliseconds) |
148 | --------------------------------------------------------------
149 | | 10 | 0 | 0 |
150 | --------------------------------------------------------------
151 | | 100 | 0 | 0 |
152 | --------------------------------------------------------------
153 | | 1000 | 0 | 0 |
154 | --------------------------------------------------------------
155 | | 10000 | 2 | 0.0002 |
156 | --------------------------------------------------------------
157 |
158 | # REMOTE INSERTIONS
159 | --------------------------------------------------------------
160 | | # of Operations | Total Execute Time | Avg. Operation Time |
161 | | | (in milliseconds) | (in milliseconds) |
162 | --------------------------------------------------------------
163 | | 10 | 0 | 0 |
164 | --------------------------------------------------------------
165 | | 100 | 0 | 0 |
166 | --------------------------------------------------------------
167 | | 1000 | 1 | 0.001 |
168 | --------------------------------------------------------------
169 | | 10000 | 16 | 0.0016 |
170 | --------------------------------------------------------------
171 |
172 | # REMOTE DELETIONS
173 | --------------------------------------------------------------
174 | | # of Operations | Total Execute Time | Avg. Operation Time |
175 | | | (in milliseconds) | (in milliseconds) |
176 | --------------------------------------------------------------
177 | | 10 | 0 | 0 |
178 | --------------------------------------------------------------
179 | | 100 | 0 | 0 |
180 | --------------------------------------------------------------
181 | | 1000 | 1 | 0.001 |
182 | --------------------------------------------------------------
183 | | 10000 | 17 | 0.0017 |
184 | --------------------------------------------------------------
185 |
--------------------------------------------------------------------------------