implemented binary encoding for all basic structs

This commit is contained in:
Kevin Jahns
2017-07-13 17:42:21 +02:00
parent 6c8876d282
commit 252bec0ad2
6 changed files with 1056 additions and 21 deletions

102
src/Encoding.js Normal file
View File

@@ -0,0 +1,102 @@
import utf8 from 'utf-8'
const bits7 = 0b1111111
export class BinaryLength {
constructor () {
this.length = 0
}
writeUint8 (num) {
this.length++
}
writeVarUint (num) {
while (num >= 0b10000000) {
this.length++
num >>= 7
}
this.length++
}
writeVarString (str) {
let len = utf8.setBytesFromString(str).length
this.writeVarUint(len)
this.length += len
}
writeOpID (id) {
this.writeVarUint(id[0])
this.writeVarUint(id[1])
}
}
export class BinaryEncoder {
constructor (binaryLength) {
this.dataview = new DataView(new ArrayBuffer(binaryLength.length))
this.pos = 0
}
writeUint8 (num) {
this.dataview.setUint8(this.pos++, num)
}
writeVarUint (num) {
while (num >= 0b10000000) {
this.dataview.setUint8(this.pos++, 0b10000000 | (bits7 & num))
num >>= 7
}
this.dataview.setUint8(this.pos++, bits7 & num)
}
writeVarString (str) {
let bytes = utf8.setBytesFromString(str)
let len = bytes.length
this.writeVarUint(len)
for (let i = 0; i < len; i++) {
this.dataview.setUint8(this.pos++, bytes[i])
}
}
writeOpID (id) {
this.writeVarUint(id[0])
this.writeVarUint(id[1])
}
}
export class BinaryDecoder {
constructor (dataview) {
this.dataview = dataview
this.pos = 0
}
skip8 () {
this.pos++
}
skip16 () {
this.pos += 2
}
skip32 () {
this.pos += 4
}
skipVar () {
while (this.dataview.getUint8(this.pos++) >= 1 << 7) { }
}
readUint8 () {
return this.dataview.getUint8(this.pos++)
}
readVarUint () {
let num = 0
let len = 0
while (true) {
let r = this.dataview.getUint8(this.pos++)
num = num | ((r & bits7) << len)
len += 7
if (r < 1 << 7) {
return num
}
}
}
readVarString () {
let len = this.readVarUint()
let bytes = new Array(len)
for (let i = 0; i < len; i++) {
bytes[i] = this.dataview.getUint8(this.pos++)
}
return utf8.getStringFromBytes(bytes)
}
readOpID () {
return [this.readVarUint(), this.readVarUint()]
}
}

View File

@@ -1,5 +1,7 @@
/* @flow */
'use strict'
const CDELETE = 0
const CINSERT = 1
const CLIST = 2
const CMAP = 3
/*
An operation also defines the structure of a type. This is why operation and
@@ -36,6 +38,19 @@ export default function extendStruct (Y) {
struct: 'Delete'
}
},
binaryEncode: function (encoder, op) {
encoder.writeUint8(CDELETE)
encoder.writeOpID(op.target)
encoder.writeVarUint(op.length || 0)
},
binaryDecode: function (decoder) {
decoder.skip8()
return {
target: decoder.readOpID(),
length: decoder.readVarUint(),
struct: 'Delete'
}
},
requiredOps: function (op) {
return [] // [op.target]
},
@@ -77,6 +92,91 @@ export default function extendStruct (Y) {
return e
},
binaryEncode: function (encoder, op) {
encoder.writeUint8(CINSERT)
// compute info property
let contentIsText = op.content != null && op.content.every(c => typeof c === 'string' && c.length === 1)
let originIsLeft = Y.utils.compareIds(op.left, op.origin)
let info =
(op.parentSub != null ? 1 : 0) |
(op.opContent != null ? 2 : 0) |
(contentIsText ? 4 : 0) |
(originIsLeft ? 8 : 0) |
(op.left != null ? 16 : 0) |
(op.right != null ? 32 : 0) |
(op.origin != null ? 64 : 0)
encoder.writeUint8(info)
encoder.writeOpID(op.id)
encoder.writeOpID(op.parent)
if (info & 16) {
encoder.writeOpID(op.left)
}
if (info & 32) {
encoder.writeOpID(op.right)
}
if (!originIsLeft && info & 64) {
encoder.writeOpID(op.origin)
}
if (info & 1) {
// write parentSub
encoder.writeVarString(op.parentSub)
}
if (info & 2) {
// write opContent
encoder.writeOpID(op.opContent)
} else if (info & 4) {
// write text
encoder.writeVarString(op.content.join(''))
} else {
// convert to JSON and write
encoder.writeVarString(JSON.stringify(op.content))
}
},
binaryDecode: function (decoder) {
let op = {
struct: 'Insert'
}
decoder.skip8()
// get info property
let info = decoder.readUint8()
op.id = decoder.readOpID()
op.parent = decoder.readOpID()
if (info & 16) {
op.left = decoder.readOpID()
} else {
op.left = null
}
if (info & 32) {
op.right = decoder.readOpID()
} else {
op.right = null
}
if (info & 8) {
// origin is left
op.origin = op.left
} else if (info & 64) {
op.origin = decoder.readOpID()
} else {
op.origin = null
}
if (info & 1) {
// has parentSub
op.parentSub = decoder.readVarString()
}
if (info & 2) {
// has opContent
op.opContent = decoder.readOpID()
} else if (info & 4) {
// has pure text content
op.content = decoder.readVarString().split('')
} else {
// has mixed content
let s = decoder.readVarString()
op.content = JSON.parse(s)
}
return op
},
requiredOps: function (op) {
var ids = []
if (op.left != null) {
@@ -300,6 +400,8 @@ export default function extendStruct (Y) {
type: op.type
}
if (op.requires != null) {
debugger // TODO: this is used. adapt binarEncode/Decode!!
console.warn('Note to myself: this is used. adapt binarEncode/Decode!!')
e.requires = op.requires
}
if (op.info != null) {
@@ -307,6 +409,26 @@ export default function extendStruct (Y) {
}
return e
},
binaryEncode: function (encoder, op) {
encoder.writeUint8(CLIST)
encoder.writeOpID(op.id)
encoder.writeVarString(op.type)
let info = op.info != null ? JSON.stringify(op.info) : ''
encoder.writeVarString(info)
},
binaryDecode: function (decoder) {
decoder.skip8()
let op = {
id: decoder.readOpID(),
type: decoder.readVarString(),
struct: 'List'
}
let info = decoder.readVarString()
if (info.length > 0) {
op.info = JSON.parse(info)
}
return op
},
requiredOps: function () {
/*
var ids = []
@@ -381,13 +503,36 @@ export default function extendStruct (Y) {
map: {} // overwrite map!!
}
if (op.requires != null) {
e.requires = op.requires
e.requires = op.require
// TODO: !!
console.warn('requires is used! see same note above for List')
}
if (op.info != null) {
e.info = op.info
}
return e
},
binaryEncode: function (encoder, op) {
encoder.writeUint8(CMAP)
encoder.writeOpID(op.id)
encoder.writeVarString(op.type)
let info = op.info != null ? JSON.stringify(op.info) : ''
encoder.writeVarString(info)
},
binaryDecode: function (decoder) {
decoder.skip8()
let op = {
id: decoder.readOpID(),
type: decoder.readVarString(),
struct: 'Map',
map: {}
}
let info = decoder.readVarString()
if (info.length > 0) {
op.info = JSON.parse(info)
}
return op
},
requiredOps: function () {
return []
},