restructer and move to esdoc
This commit is contained in:
151
src/Util/Binary/Decoder.js
Normal file
151
src/Util/Binary/Decoder.js
Normal file
@@ -0,0 +1,151 @@
|
||||
import ID from '../Util/ID/ID.js.js'
|
||||
import { default as RootID, RootFakeUserID } from '../Util/ID/RootID.js.js'
|
||||
|
||||
/**
|
||||
* A BinaryDecoder handles the decoding of an ArrayBuffer.
|
||||
*/
|
||||
export default class BinaryDecoder {
|
||||
/**
|
||||
* @param {Uint8Array|Buffer} buffer The binary data that this instance
|
||||
* decodes.
|
||||
*/
|
||||
constructor (buffer) {
|
||||
if (buffer instanceof ArrayBuffer) {
|
||||
this.uint8arr = new Uint8Array(buffer)
|
||||
} else if (
|
||||
buffer instanceof Uint8Array ||
|
||||
(
|
||||
typeof Buffer !== 'undefined' && buffer instanceof Buffer
|
||||
)
|
||||
) {
|
||||
this.uint8arr = buffer
|
||||
} else {
|
||||
throw new Error('Expected an ArrayBuffer or Uint8Array!')
|
||||
}
|
||||
this.pos = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone this decoder instance.
|
||||
* Optionally set a new position parameter.
|
||||
*/
|
||||
clone (newPos = this.pos) {
|
||||
let decoder = new BinaryDecoder(this.uint8arr)
|
||||
decoder.pos = newPos
|
||||
return decoder
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of bytes.
|
||||
*/
|
||||
get length () {
|
||||
return this.uint8arr.length
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip one byte, jump to the next position.
|
||||
*/
|
||||
skip8 () {
|
||||
this.pos++
|
||||
}
|
||||
|
||||
/**
|
||||
* Read one byte as unsigned integer.
|
||||
*/
|
||||
readUint8 () {
|
||||
return this.uint8arr[this.pos++]
|
||||
}
|
||||
|
||||
/**
|
||||
* Read 4 bytes as unsigned integer.
|
||||
*
|
||||
* @return {number} An unsigned integer.
|
||||
*/
|
||||
readUint32 () {
|
||||
let uint =
|
||||
this.uint8arr[this.pos] +
|
||||
(this.uint8arr[this.pos + 1] << 8) +
|
||||
(this.uint8arr[this.pos + 2] << 16) +
|
||||
(this.uint8arr[this.pos + 3] << 24)
|
||||
this.pos += 4
|
||||
return uint
|
||||
}
|
||||
|
||||
/**
|
||||
* Look ahead without incrementing position.
|
||||
* to the next byte and read it as unsigned integer.
|
||||
*
|
||||
* @return {number} An unsigned integer.
|
||||
*/
|
||||
peekUint8 () {
|
||||
return this.uint8arr[this.pos]
|
||||
}
|
||||
|
||||
/**
|
||||
* Read unsigned integer (32bit) with variable length.
|
||||
* 1/8th of the storage is used as encoding overhead.
|
||||
* * numbers < 2^7 is stored in one byte.
|
||||
* * numbers < 2^14 is stored in two bytes.
|
||||
*
|
||||
* @return {number} An unsigned integer.
|
||||
*/
|
||||
readVarUint () {
|
||||
let num = 0
|
||||
let len = 0
|
||||
while (true) {
|
||||
let r = this.uint8arr[this.pos++]
|
||||
num = num | ((r & 0b1111111) << len)
|
||||
len += 7
|
||||
if (r < 1 << 7) {
|
||||
return num >>> 0 // return unsigned number!
|
||||
}
|
||||
if (len > 35) {
|
||||
throw new Error('Integer out of range!')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read string of variable length
|
||||
* * varUint is used to store the length of the string
|
||||
*
|
||||
* @return {String} The read String.
|
||||
*/
|
||||
readVarString () {
|
||||
let len = this.readVarUint()
|
||||
let bytes = new Array(len)
|
||||
for (let i = 0; i < len; i++) {
|
||||
bytes[i] = this.uint8arr[this.pos++]
|
||||
}
|
||||
let encodedString = bytes.map(b => String.fromCodePoint(b)).join('')
|
||||
return decodeURIComponent(escape(encodedString))
|
||||
}
|
||||
|
||||
/**
|
||||
* Look ahead and read varString without incrementing position
|
||||
*/
|
||||
peekVarString () {
|
||||
let pos = this.pos
|
||||
let s = this.readVarString()
|
||||
this.pos = pos
|
||||
return s
|
||||
}
|
||||
|
||||
/**
|
||||
* Read ID.
|
||||
* * If first varUint read is 0xFFFFFF a RootID is returned.
|
||||
* * Otherwise an ID is returned.
|
||||
*
|
||||
* @return ID
|
||||
*/
|
||||
readID () {
|
||||
let user = this.readVarUint()
|
||||
if (user === RootFakeUserID) {
|
||||
// read property name and type id
|
||||
const rid = new RootID(this.readVarString(), null)
|
||||
rid.type = this.readVarUint()
|
||||
return rid
|
||||
}
|
||||
return new ID(user, this.readVarUint())
|
||||
}
|
||||
}
|
||||
144
src/Util/Binary/Encoder.js
Normal file
144
src/Util/Binary/Encoder.js
Normal file
@@ -0,0 +1,144 @@
|
||||
import { RootFakeUserID } from '../Util/ID/RootID.js.js'
|
||||
|
||||
const bits7 = 0b1111111
|
||||
const bits8 = 0b11111111
|
||||
|
||||
/**
|
||||
* A BinaryEncoder handles the encoding to an ArrayBuffer.
|
||||
*/
|
||||
export default class BinaryEncoder {
|
||||
constructor () {
|
||||
// TODO: implement chained Uint8Array buffers instead of Array buffer
|
||||
this.data = []
|
||||
}
|
||||
|
||||
/**
|
||||
* The current length of the encoded data.
|
||||
*/
|
||||
get length () {
|
||||
return this.data.length
|
||||
}
|
||||
|
||||
/**
|
||||
* The current write pointer (the same as {@link length}).
|
||||
*/
|
||||
get pos () {
|
||||
return this.data.length
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an ArrayBuffer.
|
||||
*
|
||||
* @return {Uint8Array} A Uint8Array that represents the written data.
|
||||
*/
|
||||
createBuffer () {
|
||||
return Uint8Array.from(this.data).buffer
|
||||
}
|
||||
|
||||
/**
|
||||
* Write one byte as an unsigned integer.
|
||||
*
|
||||
* @param {number} num The number that is to be encoded.
|
||||
*/
|
||||
writeUint8 (num) {
|
||||
this.data.push(num & bits8)
|
||||
}
|
||||
|
||||
/**
|
||||
* Write one byte as an unsigned Integer at a specific location.
|
||||
*
|
||||
* @param {number} pos The location where the data will be written.
|
||||
* @param {number} num The number that is to be encoded.
|
||||
*/
|
||||
setUint8 (pos, num) {
|
||||
this.data[pos] = num & bits8
|
||||
}
|
||||
|
||||
/**
|
||||
* Write two bytes as an unsigned integer.
|
||||
*
|
||||
* @param {number} num The number that is to be encoded.
|
||||
*/
|
||||
writeUint16 (num) {
|
||||
this.data.push(num & bits8, (num >>> 8) & bits8)
|
||||
}
|
||||
/**
|
||||
* Write two bytes as an unsigned integer at a specific location.
|
||||
*
|
||||
* @param {number} pos The location where the data will be written.
|
||||
* @param {number} num The number that is to be encoded.
|
||||
*/
|
||||
setUint16 (pos, num) {
|
||||
this.data[pos] = num & bits8
|
||||
this.data[pos + 1] = (num >>> 8) & bits8
|
||||
}
|
||||
|
||||
/**
|
||||
* Write two bytes as an unsigned integer
|
||||
*
|
||||
* @param {number} num The number that is to be encoded.
|
||||
*/
|
||||
writeUint32 (num) {
|
||||
for (let i = 0; i < 4; i++) {
|
||||
this.data.push(num & bits8)
|
||||
num >>>= 8
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write two bytes as an unsigned integer at a specific location.
|
||||
*
|
||||
* @param {number} pos The location where the data will be written.
|
||||
* @param {number} num The number that is to be encoded.
|
||||
*/
|
||||
setUint32 (pos, num) {
|
||||
for (let i = 0; i < 4; i++) {
|
||||
this.data[pos + i] = num & bits8
|
||||
num >>>= 8
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a variable length unsigned integer.
|
||||
*
|
||||
* @param {number} num The number that is to be encoded.
|
||||
*/
|
||||
writeVarUint (num) {
|
||||
while (num >= 0b10000000) {
|
||||
this.data.push(0b10000000 | (bits7 & num))
|
||||
num >>>= 7
|
||||
}
|
||||
this.data.push(bits7 & num)
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a variable length string.
|
||||
*
|
||||
* @param {String} str The string that is to be encoded.
|
||||
*/
|
||||
writeVarString (str) {
|
||||
let encodedString = unescape(encodeURIComponent(str))
|
||||
let bytes = encodedString.split('').map(c => c.codePointAt())
|
||||
let len = bytes.length
|
||||
this.writeVarUint(len)
|
||||
for (let i = 0; i < len; i++) {
|
||||
this.data.push(bytes[i])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an ID at the current position.
|
||||
*
|
||||
* @param {ID} id The ID that is to be written.
|
||||
*/
|
||||
writeID (id) {
|
||||
const user = id.user
|
||||
this.writeVarUint(user)
|
||||
if (user !== RootFakeUserID) {
|
||||
this.writeVarUint(id.clock)
|
||||
} else {
|
||||
this.writeVarString(id.name)
|
||||
this.writeVarUint(id.type)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
export default class ID {
|
||||
constructor (user, clock) {
|
||||
this.user = user
|
||||
this.user = user // TODO: rename to client
|
||||
this.clock = clock
|
||||
}
|
||||
clone () {
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
import ID from '../Util/ID.js'
|
||||
import ID from '../Util/ID/ID.js.js'
|
||||
import ItemJSON from '../Struct/ItemJSON.js'
|
||||
import ItemString from '../Struct/ItemString.js'
|
||||
|
||||
|
||||
@@ -1,4 +1,32 @@
|
||||
|
||||
/**
|
||||
* A SimpleDiff describes a change on a String.
|
||||
*
|
||||
* @example
|
||||
* console.log(a) // the old value
|
||||
* console.log(b) // the updated value
|
||||
* // Apply changes of diff (pseudocode)
|
||||
* a.remove(diff.pos, diff.remove) // Remove `diff.remove` characters
|
||||
* a.insert(diff.pos, diff.insert) // Insert `diff.insert`
|
||||
* a === b // values match
|
||||
*
|
||||
* @typedef {Object} SimpleDiff
|
||||
* @property {NaturalNumber} pos The index where changes were applied
|
||||
* @property {NaturalNumber} delete The number of characters to delete starting
|
||||
* at `index`.
|
||||
* @property {String} insert The new text to insert at `index` after applying
|
||||
* `delete`
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a diff between two strings. This diff implementation is intentionally
|
||||
* not very smart.
|
||||
*
|
||||
* @public
|
||||
* @param {String} a The old version of the string
|
||||
* @param {String} b The updated version of the string
|
||||
* @return {SimpleDiff} The diff description.
|
||||
*/
|
||||
export default function simpleDiff (a, b) {
|
||||
let left = 0 // number of same characters counting from left
|
||||
let right = 0 // number of same characters counting from right
|
||||
@@ -12,7 +40,7 @@ export default function simpleDiff (a, b) {
|
||||
}
|
||||
}
|
||||
return {
|
||||
pos: left,
|
||||
pos: left, // TODO: rename to index (also in type above)
|
||||
remove: a.length - left - right,
|
||||
insert: b.slice(left, b.length - right)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import YArray from '../Type/YArray.js'
|
||||
import YMap from '../Type/YMap.js'
|
||||
import YText from '../Type/YText.js'
|
||||
import { YXmlFragment, YXmlElement, YXmlText, YXmlHook } from '../Type/y-xml/y-xml.js'
|
||||
import YArray from '../Types/YArray/YArray.js'
|
||||
import YMap from '../Types/YMap/YMap.js'
|
||||
import YText from '../Types/YText/YText.js'
|
||||
import { YXmlFragment, YXmlElement, YXmlText, YXmlHook } from '../Types/YXml/YXml.js'
|
||||
|
||||
import Delete from '../Struct/Delete.js'
|
||||
import ItemJSON from '../Struct/ItemJSON.js'
|
||||
@@ -12,7 +12,7 @@ import ItemEmbed from '../Struct/ItemEmbed.js'
|
||||
const structs = new Map()
|
||||
const references = new Map()
|
||||
|
||||
export function addStruct (reference, structConstructor) {
|
||||
export function registerStruct (reference, structConstructor) {
|
||||
structs.set(reference, structConstructor)
|
||||
references.set(structConstructor, reference)
|
||||
}
|
||||
@@ -21,21 +21,21 @@ export function getStruct (reference) {
|
||||
return structs.get(reference)
|
||||
}
|
||||
|
||||
export function getReference (typeConstructor) {
|
||||
export function getStructReference (typeConstructor) {
|
||||
return references.get(typeConstructor)
|
||||
}
|
||||
|
||||
// TODO: reorder (Item* should have low numbers)
|
||||
addStruct(0, ItemJSON)
|
||||
addStruct(1, ItemString)
|
||||
addStruct(10, ItemFormat)
|
||||
addStruct(11, ItemEmbed)
|
||||
addStruct(2, Delete)
|
||||
registerStruct(0, ItemJSON)
|
||||
registerStruct(1, ItemString)
|
||||
registerStruct(10, ItemFormat)
|
||||
registerStruct(11, ItemEmbed)
|
||||
registerStruct(2, Delete)
|
||||
|
||||
addStruct(3, YArray)
|
||||
addStruct(4, YMap)
|
||||
addStruct(5, YText)
|
||||
addStruct(6, YXmlFragment)
|
||||
addStruct(7, YXmlElement)
|
||||
addStruct(8, YXmlText)
|
||||
addStruct(9, YXmlHook)
|
||||
registerStruct(3, YArray)
|
||||
registerStruct(4, YMap)
|
||||
registerStruct(5, YText)
|
||||
registerStruct(6, YXmlFragment)
|
||||
registerStruct(7, YXmlElement)
|
||||
registerStruct(8, YXmlText)
|
||||
registerStruct(9, YXmlHook)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
import YMap from '../Type/YMap'
|
||||
import YArray from '../Type/YArray'
|
||||
import YMap from '../Types/YMap'
|
||||
import YArray from '../Types/YArray'
|
||||
|
||||
export function writeObjectToYMap (object, type) {
|
||||
for (var key in object) {
|
||||
|
||||
Reference in New Issue
Block a user