fixed YMap

This commit is contained in:
Kevin Jahns 2019-03-29 13:49:13 +01:00
parent ff981a8697
commit c188f813a4
13 changed files with 32 additions and 184 deletions

View File

@ -2,7 +2,6 @@
export { Y } from './utils/Y.js'
export { UndoManager } from './utils/UndoManager.js'
export { Transaction } from './utils/Transaction.js'
export { Delete } from './Delete.js'
export { ItemJSON } from './structs/ItemJSON.js'
export { ItemString } from './structs/ItemString.js'
export { ItemFormat } from './structs/ItemFormat.js'
@ -20,7 +19,5 @@ export { YXmlElement as XmlElement, YXmlFragment as XmlFragment } from './types/
export { getRelativePosition, fromRelativePosition, equal as equalRelativePosition } from './utils/relativePosition.js'
export { ID, createID } from './utils/ID.js'
export { DeleteStore, DSNode } from './utils/DeleteSet.js/index.js'
export { deleteItemRange } from './utils/structManipulation.js'
export { integrateRemoteStructs } from './utils/integrateRemoteStructs.js'
export { isParentOf } from './utils/isParentOf.js'

View File

@ -51,7 +51,7 @@ export const splitItem = (transaction, leftItem, diff) => {
foundOrigins.add(o)
o = o.right
}
const right = leftItem.splitAt(diff)
const right = leftItem.splitAt(transaction, diff)
if (transaction.added.has(leftItem)) {
transaction.added.add(right)
} else if (transaction.deleted.has(leftItem)) {

View File

@ -2,11 +2,10 @@
* @module types
*/
import { AbstractType, typeMapDelete } from './AbstractType.js'
import { ItemJSON } from '../structs/ItemJSON.js'
import { AbstractType, typeMapDelete, typeMapSet, typeMapGet, typeMapHas } from './AbstractType.js'
import { ItemType } from '../structs/ItemType.js' // eslint-disable-line
import { YEvent } from '../utils/YEvent.js'
import { ItemBinary } from '../structs/ItemBinary.js'
import * as decoding from 'lib0/decoding.js' // eslint-disable-line
import { Transaction } from '../utils/Transaction.js' // eslint-disable-line
class YMapIterator {
@ -159,94 +158,39 @@ export class YMap extends AbstractType {
* @param {Object | string | number | AbstractType | ArrayBuffer } value The value of the element to add
*/
set (key, value) {
this._transact(y => {
const old = this._map.get(key) || null
if (old !== null) {
if (
old.constructor === ItemJSON &&
!old._deleted && old._content[0] === value
) {
// Trying to overwrite with same value
// break here
return value
}
if (y !== null) {
old._delete(y)
}
}
let v
if (typeof value === 'function') {
v = new value() // eslint-disable-line new-cap
value = v
} else if (value instanceof Item) {
v = value
} else if (value != null && value.constructor === ArrayBuffer) {
v = new ItemBinary()
v._content = value
} else {
v = new ItemJSON()
v._content = [value]
}
v._right = old
v._right_origin = old
v._parent = this
v._parentSub = key
if (y !== null) {
v._integrate(y)
} else {
this._map.set(key, v)
}
})
if (this._y !== null) {
this._y.transact(transaction => {
typeMapSet(transaction, this, key, value)
})
} else {
// @ts-ignore
this._prelimContent.set(key, value)
}
return value
}
/**
* Returns a specified element from this YMap.
*
* @param {string} key The key of the element to return.
* @param {HistorySnapshot} [snapshot]
* @param {string} key
* @return {Object<string,any>|number|Array<any>|string|ArrayBuffer|AbstractType|undefined}
*/
get (key, snapshot) {
let v = this._map.get(key)
if (v === undefined) {
return undefined
}
if (snapshot !== undefined) {
// iterate until found element that exists
while (!snapshot.sm.has(v._id.user) || v._id.clock >= snapshot.sm.get(v._id.user)) {
v = v._right
}
}
if (isVisible(v, snapshot)) {
if (v instanceof Type) {
return v
} else if (v.constructor === ItemBinary) {
return v._content
} else {
return v._content[v._content.length - 1]
}
}
get (key) {
return typeMapGet(this, key)
}
/**
* Returns a boolean indicating whether the specified key exists or not.
*
* @param {string} key The key to test.
* @param {HistorySnapshot} [snapshot]
* @return {boolean}
*/
has (key, snapshot) {
let v = this._map.get(key)
if (v === undefined) {
return false
}
if (snapshot !== undefined) {
// iterate until found element that exists
while (!snapshot.sm.has(v._id.user) || v._id.clock >= snapshot.sm.get(v._id.user)) {
v = v._right
}
}
return isVisible(v, snapshot)
has (key) {
return typeMapHas(this, key)
}
}
export const readYMap = decoder => new YMap()
/**
* @param {decoding.Decoder} decoder
*/
export const readYMap = decoder => new YMap()

View File

@ -2,7 +2,6 @@
* @module types
*/
import { logItemHelper } from '../structs/AbstractItem.js/index.js'
import { ItemEmbed } from '../structs/ItemEmbed.js'
import { ItemString } from '../structs/ItemString.js'
import { ItemFormat } from '../structs/ItemFormat.js'

View File

@ -3,7 +3,6 @@
*/
import { Transaction } from '../utils/Transaction.js' // eslint-disable-line
import { logItemHelper } from '../structs/AbstractItem.js'
import { YMap } from './YMap.js'
import * as encoding from 'lib0/encoding.js'
import * as decoding from 'lib0/decoding.js'

View File

@ -4,7 +4,7 @@
import { YEvent } from '../utils/YEvent.js'
import { Type } from './AbstractType.js/index.js.js.js.js' // eslint-disable-line
import { Type } from './AbstractType.js' // eslint-disable-line
import { Transaction } from '../utils/Transaction.js' // eslint-disable-line
/**

View File

@ -3,8 +3,7 @@ import * as encoding from 'lib0/encoding.js'
import * as decoding from 'lib0/decoding.js'
import { StructStore, getItemRange } from './StructStore.js' // eslint-disable-line
import { Transaction } from './Transaction.js' // eslint-disable-line
import * as error from 'lib0/error.js'
import { ID } from './ID.js'
import { ID } from './ID.js' // eslint-disable-line
class DeleteItem {
/**

View File

@ -2,11 +2,9 @@
* @module utils
*/
import { getStruct } from '../utils/structReferences.js'
import * as decoding from 'lib0/decoding.js'
import { GC } from '../structs/GC.js/index.js.js'
import { GC } from '../structs/GC.js'
import { Y } from '../utils/Y.js' // eslint-disable-line
import { Item } from '../structs/AbstractItem.js/index.js' // eslint-disable-line
class MissingEntry {
constructor (decoder, missing, struct) {

View File

@ -3,24 +3,23 @@
*/
import { Y } from '../utils/Y.js' // eslint-disable-line
import { Type } from '../types/AbstractType.js/index.js.js.js.js' // eslint-disable-line
import { AbstractType } from '../types/AbstractType.js' // eslint-disable-line
/**
* Check if `parent` is a parent of `child`.
*
* @param {Type | Y} parent
* @param {Type | Y} child
* @param {AbstractType} parent
* @param {AbstractType} child
* @return {Boolean} Whether `parent` is a parent of `child`.
*
* @public
*/
export const isParentOf = (parent, child) => {
child = child._parent
while (child !== null) {
while (child._item !== null) {
if (child === parent) {
return true
}
child = child._parent
child = child._item.parent
}
return false
}

View File

@ -3,7 +3,7 @@
*/
import * as ID from './ID.js'
import { GC } from '../structs/GC.js/index.js.js'
import { GC } from '../structs/GC.js'
// TODO: Implement function to describe ranges

View File

@ -1,7 +1,6 @@
import * as encoding from 'lib0/encoding.js'
import * as decoding from 'lib0/decoding.js'
import { AbstractStruct, AbstractRef } from '../structs/AbstractStruct.js'
import { ID, createID, writeID, writeNullID } from './ID.js'
import * as binary from 'lib0/binary.js'
import { Transaction } from './Transaction.js'
import { findIndex } from './StructStore.js'

View File

@ -1,86 +0,0 @@
import * as prng from 'lib0/prng.js'
import * as t from 'lib0/testing.js'
import { DeleteStore } from '../src/utils/DeleteStore.js'
import * as ID from '../src/utils/ID.js'
/**
* Converts a DS to an array of length 10.
*
* @example
* const ds = new DeleteStore()
* ds.mark(ID.createID(0, 0), 1, false)
* ds.mark(ID.createID(0, 1), 1, true)
* ds.mark(ID.createID(0, 3), 1, false)
* dsToArray(ds) // => [0, 1, undefined, 0]
*
* @return {Array<(null|number)>} Array of numbers indicating if array[i] is deleted (0), garbage collected (1), or undeleted (undefined).
*/
const dsToArray = ds => {
const array = []
let i = 0
ds.iterate(null, null, n => {
// fill with null
while (i < n._id.clock) {
array[i++] = null
}
while (i < n._id.clock + n.len) {
array[i++] = n.gc ? 1 : 0
}
})
return array
}
/**
* @param {t.TestCase} tc
*/
export const testDeleteStore = tc => {
t.describe('Integrity tests')
const ds = new DeleteStore()
ds.mark(ID.createID(0, 1), 1, false)
ds.mark(ID.createID(0, 2), 1, false)
ds.mark(ID.createID(0, 3), 1, false)
t.compareArrays(dsToArray(ds), [null, 0, 0, 0])
ds.mark(ID.createID(0, 2), 1, true)
t.compareArrays(dsToArray(ds), [null, 0, 1, 0])
ds.mark(ID.createID(0, 1), 1, true)
t.compareArrays(dsToArray(ds), [null, 1, 1, 0])
ds.mark(ID.createID(0, 3), 1, true)
t.compareArrays(dsToArray(ds), [null, 1, 1, 1])
ds.mark(ID.createID(0, 5), 1, true)
ds.mark(ID.createID(0, 4), 1, true)
t.compareArrays(dsToArray(ds), [null, 1, 1, 1, 1, 1])
ds.mark(ID.createID(0, 0), 3, false)
t.compareArrays(dsToArray(ds), [0, 0, 0, 1, 1, 1])
}
export const testRepeatDeleteStoreTests = tc => {
const gen = tc.prng
const ds = new DeleteStore()
const dsArray = []
for (let i = 0; i < 200; i++) {
const pos = prng.int31(gen, 0, 10)
const len = prng.int31(gen, 0, 4)
const gc = prng.bool(gen)
ds.mark(ID.createID(0, pos), len, gc)
for (let j = 0; j < len; j++) {
dsArray[pos + j] = gc ? 1 : 0
}
}
// fill empty fields
for (let i = 0; i < dsArray.length; i++) {
if (dsArray[i] !== 0 && dsArray[i] !== 1) {
dsArray[i] = null
}
}
t.compareArrays(dsToArray(ds), dsArray)
let size = 0
let lastEl = null
for (let i = 0; i < dsArray.length; i++) {
let el = dsArray[i]
if (lastEl !== el && el !== null) {
size++
}
lastEl = el
}
t.assert(size === ds.length)
}

View File

@ -58,5 +58,5 @@
"typeRoots": ["./src/utils/typedefs.js"],
// "types": ["./src/utils/typedefs.js"]
},
"files": ["./src/index.js"]
"exclude": ["./dist/**"]
}