created Array type that has a good time complexity for both insert and retrieval of objects
This commit is contained in:
109
src/Types/Array.js
Normal file
109
src/Types/Array.js
Normal file
@@ -0,0 +1,109 @@
|
||||
|
||||
|
||||
(function(){
|
||||
|
||||
class YArray {
|
||||
constructor (os, _model, idArray, valArray) {
|
||||
this.os = os;
|
||||
this._model = _model;
|
||||
// Array of all the operation id's
|
||||
this.idArray = idArray;
|
||||
this.valArray = valArray;
|
||||
this.eventHandler = new EventHandler( ops =>{
|
||||
for (var i in ops) {
|
||||
var op = ops[i];
|
||||
var pos;
|
||||
if (op.right === null) {
|
||||
pos = this.idArray.length;
|
||||
} else {
|
||||
var sid = JSON.stringify(op.right);
|
||||
pos = this.idArray.indexOf(sid);
|
||||
}
|
||||
if (pos < 0) {
|
||||
throw new Error("Unexpected operation!");
|
||||
}
|
||||
this.idArray.splice(pos, 0, JSON.stringify(op.id));
|
||||
this.valArray.splice(pos, 0, op.content);
|
||||
}
|
||||
});
|
||||
}
|
||||
get (pos) {
|
||||
if (pos == null || typeof pos !== "number") {
|
||||
throw new Error("pos must be a number!");
|
||||
}
|
||||
return this.valArray[pos];
|
||||
}
|
||||
toArray() {
|
||||
return this.valArray.slice();
|
||||
}
|
||||
insert (pos, contents) {
|
||||
if (typeof pos !== "number") {
|
||||
throw new Error("pos must be a number!");
|
||||
}
|
||||
if (!(contents instanceof Array)) {
|
||||
throw new Error("contents must be an Array of objects!");
|
||||
}
|
||||
if (contents.length === 0) {
|
||||
return;
|
||||
}
|
||||
if (pos > this.idArray.length || pos < 0) {
|
||||
throw new Error("This position exceeds the range of the array!");
|
||||
}
|
||||
var mostLeft = pos === 0 ? null : JSON.parse(this.idArray[pos - 1]);
|
||||
var mostRight = pos === this.idArray.length ? null : JSON.parse(this.idArray[pos]);
|
||||
|
||||
var ops = [];
|
||||
var prevId = mostLeft;
|
||||
for (var i = 0; i < contents.length; i++) {
|
||||
var op = {
|
||||
left: prevId,
|
||||
origin: prevId,
|
||||
right: mostRight,
|
||||
parent: this._model,
|
||||
content: contents[i],
|
||||
struct: "Insert",
|
||||
id: this.os.getNextOpId()
|
||||
};
|
||||
ops.push(op);
|
||||
prevId = op.id;
|
||||
}
|
||||
var eventHandler = this.eventHandler;
|
||||
eventHandler.awaitAndPrematurelyCall(ops);
|
||||
this.os.requestTransaction(function*(){
|
||||
yield* this.applyCreatedOperations(ops);
|
||||
eventHandler.awaitedLastOp(ops.length);
|
||||
});
|
||||
}
|
||||
*delete (pos) {
|
||||
if (typeof pos !== "number") {
|
||||
throw new Error("pos must be a number!");
|
||||
}
|
||||
var t = yield "transaction";
|
||||
var model = yield* t.getOperation(this._model);
|
||||
yield* Y.Struct.Array.delete.call(t, model, pos);
|
||||
}
|
||||
_changed (op) {
|
||||
this.eventHandler.receivedOp(op);
|
||||
}
|
||||
}
|
||||
|
||||
Y.Array = function* _YArray(){
|
||||
var model = {
|
||||
start: null,
|
||||
end: null,
|
||||
struct: "List",
|
||||
type: "Array",
|
||||
id: this.store.getNextOpId()
|
||||
};
|
||||
yield* this.applyCreatedOperations([model]);
|
||||
return yield* this.createType(model);
|
||||
};
|
||||
Y.Array.create = function* YArrayCreate(os, model){
|
||||
var valArray = [];
|
||||
var idArray = yield* Y.Struct.List.map.call(this, model, function(c){
|
||||
valArray.push(c.content);
|
||||
return JSON.stringify(c.id);
|
||||
});
|
||||
return new YArray(os, model.id, idArray, valArray);
|
||||
};
|
||||
})();
|
||||
@@ -1,48 +0,0 @@
|
||||
|
||||
|
||||
(function(){
|
||||
|
||||
class List {
|
||||
constructor (_model) {
|
||||
this._model = _model;
|
||||
}
|
||||
*val (pos) {
|
||||
var t = yield "transaction";
|
||||
var model = yield* t.getOperation(this._model);
|
||||
if (pos != null) {
|
||||
var o = yield* Y.Struct.List.ref.call(t, model, pos);
|
||||
return o ? o.content : null;
|
||||
} else {
|
||||
return yield* Y.Struct.List.map.call(t, model, function(c){return c; });
|
||||
}
|
||||
}
|
||||
*insert (pos, contents) {
|
||||
if (typeof pos !== "number") {
|
||||
throw new Error("pos must be a number!");
|
||||
}
|
||||
if (!(contents instanceof Array)) {
|
||||
throw new Error("contents must be an Array of objects!");
|
||||
}
|
||||
var t = yield "transaction";
|
||||
var model = yield* t.getOperation(this._model);
|
||||
yield* Y.Struct.List.insert.call(t, model, pos, contents);
|
||||
}
|
||||
*delete (pos) {
|
||||
if (typeof pos !== "number") {
|
||||
throw new Error("pos must be a number!");
|
||||
}
|
||||
var t = yield "transaction";
|
||||
var model = yield* t.getOperation(this._model);
|
||||
yield* Y.Struct.List.delete.call(t, model, pos);
|
||||
}
|
||||
_changed () {
|
||||
}
|
||||
}
|
||||
|
||||
Y.List = function* YList(){
|
||||
var t = yield "transaction";
|
||||
var model = yield* Y.Struct.List.create.call(t, {type: "List"});
|
||||
return t.createType(model);
|
||||
};
|
||||
Y.List.Create = List;
|
||||
})();
|
||||
@@ -14,25 +14,29 @@ class EventHandler {
|
||||
this.waiting.push(copyObject(op));
|
||||
}
|
||||
}
|
||||
awaitAndPrematurelyCall (op) {
|
||||
awaitAndPrematurelyCall (ops) {
|
||||
this.awaiting++;
|
||||
this.onevent([op]);
|
||||
this.onevent(ops);
|
||||
}
|
||||
awaitedLastOp () {
|
||||
var op = this.waiting.pop();
|
||||
for (var i = this.waiting.length - 1; i >= 0; i--) {
|
||||
var w = this.waiting[i];
|
||||
if (compareIds(op.left, w.id)) {
|
||||
// include the effect of op in w
|
||||
w.right = op.id;
|
||||
// exclude the effect of w in op
|
||||
op.left = w.left;
|
||||
} else if (compareIds(op.right, w.id)) {
|
||||
// similar..
|
||||
w.left = op.id;
|
||||
op.right = w.right;
|
||||
awaitedLastOp (n) {
|
||||
var ops = this.waiting.splice(this.waiting.length - n);
|
||||
for (var oid = 0; oid < ops.length; oid++) {
|
||||
var op = ops[oid];
|
||||
for (var i = this.waiting.length - 1; i >= 0; i--) {
|
||||
var w = this.waiting[i];
|
||||
if (compareIds(op.left, w.id)) {
|
||||
// include the effect of op in w
|
||||
w.right = op.id;
|
||||
// exclude the effect of w in op
|
||||
op.left = w.left;
|
||||
} else if (compareIds(op.right, w.id)) {
|
||||
// similar..
|
||||
w.left = op.id;
|
||||
op.right = w.right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.awaiting--;
|
||||
if (this.awaiting <= 0) {
|
||||
var events = this.waiting;
|
||||
@@ -87,8 +91,10 @@ class EventHandler {
|
||||
var insert = {
|
||||
left: null,
|
||||
right: right,
|
||||
origin: null,
|
||||
parent: this._model,
|
||||
parentSub: key
|
||||
parentSub: key,
|
||||
struct: "Insert"
|
||||
};
|
||||
var def = Promise.defer();
|
||||
if ( value != null && value.constructor === GeneratorFunction) {
|
||||
@@ -96,17 +102,19 @@ class EventHandler {
|
||||
this.os.requestTransaction(function*(){
|
||||
var type = yield* value.call(this);
|
||||
insert.opContent = type._model;
|
||||
yield* Struct.Insert.create.call(this, insert);
|
||||
insert.id = this.store.getNextOpId();
|
||||
yield* this.applyCreatedOperations([insert]);
|
||||
def.resolve(type);
|
||||
});
|
||||
} else {
|
||||
insert.content = value;
|
||||
insert.id = this.os.getNextOpId();
|
||||
var eventHandler = this.eventHandler;
|
||||
eventHandler.awaitAndPrematurelyCall(insert);
|
||||
eventHandler.awaitAndPrematurelyCall([insert]);
|
||||
|
||||
this.os.requestTransaction(function*(){
|
||||
yield* Struct.Insert.create.call(this, insert);
|
||||
eventHandler.awaitedLastOp();
|
||||
yield* this.applyCreatedOperations([insert]);
|
||||
eventHandler.awaitedLastOp(1);
|
||||
});
|
||||
def.resolve(value);
|
||||
}
|
||||
@@ -124,7 +132,13 @@ class EventHandler {
|
||||
}
|
||||
|
||||
Y.Map = function* YMap(){
|
||||
var model = yield* Y.Struct.Map.create.call(this, {type: "Map"});
|
||||
var model = {
|
||||
map: {},
|
||||
struct: "Map",
|
||||
type: "Map",
|
||||
id: this.store.getNextOpId()
|
||||
};
|
||||
yield* this.applyCreatedOperations([model]);
|
||||
return yield* this.createType(model);
|
||||
};
|
||||
Y.Map.create = function* YMapCreate(os, model){
|
||||
|
||||
Reference in New Issue
Block a user