@colyseus/schema
Advanced tools
Comparing version
@@ -9,3 +9,3 @@ import "./symbol.shim"; | ||
import type { SetSchema } from "./types/custom/SetSchema"; | ||
export type RawPrimitiveType = "string" | "number" | "boolean" | "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32" | "int64" | "uint64" | "float32" | "float64"; | ||
export type RawPrimitiveType = "string" | "number" | "boolean" | "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32" | "int64" | "uint64" | "float32" | "float64" | "bigint64" | "biguint64"; | ||
export type PrimitiveType = RawPrimitiveType | typeof Schema | object; | ||
@@ -12,0 +12,0 @@ export type DefinitionType<T extends PrimitiveType = PrimitiveType> = T | T[] | { |
@@ -53,12 +53,5 @@ "use strict"; | ||
using Colyseus.Schema; | ||
using Action = System.Action; | ||
${namespace ? `\nnamespace ${namespace} {` : ""} | ||
${indent}public partial class ${klass.name} : ${klass.extends} { | ||
${klass.properties.map((prop) => generateProperty(prop, indent)).join("\n\n")} | ||
${indent}\t/* | ||
${indent}\t * Support for individual property change callbacks below... | ||
${indent}\t */ | ||
${generateAllFieldCallbacks(klass, indent)} | ||
${indent}} | ||
@@ -109,3 +102,3 @@ ${namespace ? "}" : ""} | ||
} | ||
initializer = `new ${langType}()`; | ||
initializer = `null`; | ||
} | ||
@@ -133,38 +126,2 @@ else { | ||
} | ||
function generateAllFieldCallbacks(klass, indent) { | ||
// | ||
// TODO: improve me. It would be great to generate less boilerplate in favor | ||
// of a single implementation on C# Schema class itself. | ||
// | ||
const eventNames = []; | ||
return `${klass.properties | ||
.filter(prop => !prop.deprecated) // generate only for properties that haven't been deprecated. | ||
.map(prop => { | ||
const eventName = `__${prop.name}Change`; | ||
eventNames.push(eventName); | ||
const defaultNull = (prop.childType) | ||
? "null" | ||
: `default(${getType(prop)})`; | ||
return `\t${indent}protected event PropertyChangeHandler<${getType(prop)}> ${eventName}; | ||
\t${indent}public Action On${capitalize(prop.name)}Change(PropertyChangeHandler<${getType(prop)}> __handler, bool __immediate = true) { | ||
\t${indent}\tif (__callbacks == null) { __callbacks = new SchemaCallbacks(); } | ||
\t${indent}\t__callbacks.AddPropertyCallback(nameof(this.${prop.name})); | ||
\t${indent}\t${eventName} += __handler; | ||
\t${indent}\tif (__immediate && this.${prop.name} != ${defaultNull}) { __handler(this.${prop.name}, ${defaultNull}); } | ||
\t${indent}\treturn () => { | ||
\t${indent}\t\t__callbacks.RemovePropertyCallback(nameof(${prop.name})); | ||
\t${indent}\t\t${eventName} -= __handler; | ||
\t${indent}\t}; | ||
\t${indent}}`; | ||
}).join("\n\n")} | ||
\t${indent}protected override void TriggerFieldChange(DataChange change) { | ||
\t${indent}\tswitch (change.Field) { | ||
${klass.properties.filter(prop => !prop.deprecated).map((prop, i) => { | ||
return `\t${indent}\t\tcase nameof(${prop.name}): ${eventNames[i]}?.Invoke((${getType(prop)}) change.Value, (${getType(prop)}) change.PreviousValue); break;`; | ||
}).join("\n")} | ||
\t${indent}\t\tdefault: break; | ||
\t\t${indent}} | ||
\t${indent}}`; | ||
} | ||
function getChildType(prop) { | ||
@@ -171,0 +128,0 @@ return typeMaps[prop.childType]; |
@@ -5,3 +5,3 @@ import { OPERATION } from "../encoding/spec"; | ||
import type { Decoder } from "./Decoder"; | ||
import * as decode from "../encoding/decode"; | ||
import { Iterator } from "../encoding/decode"; | ||
export interface DataChange<T = any, F = string> { | ||
@@ -17,4 +17,4 @@ ref: Ref; | ||
export declare const DEFINITION_MISMATCH = -1; | ||
export type DecodeOperation<T extends Schema = any> = (decoder: Decoder<T>, bytes: Buffer, it: decode.Iterator, ref: Ref, allChanges: DataChange[]) => number | void; | ||
export declare function decodeValue(decoder: Decoder, operation: OPERATION, ref: Ref, index: number, type: any, bytes: Buffer, it: decode.Iterator, allChanges: DataChange[]): { | ||
export type DecodeOperation<T extends Schema = any> = (decoder: Decoder<T>, bytes: Buffer, it: Iterator, ref: Ref, allChanges: DataChange[]) => number | void; | ||
export declare function decodeValue(decoder: Decoder, operation: OPERATION, ref: Ref, index: number, type: any, bytes: Buffer, it: Iterator, allChanges: DataChange[]): { | ||
value: any; | ||
@@ -21,0 +21,0 @@ previousValue: any; |
@@ -7,3 +7,3 @@ "use strict"; | ||
const Schema_1 = require("../Schema"); | ||
const decode = require("../encoding/decode"); | ||
const decode_1 = require("../encoding/decode"); | ||
const symbols_1 = require("../types/symbols"); | ||
@@ -49,3 +49,3 @@ const registry_1 = require("../types/registry"); | ||
else if (Schema_1.Schema.is(type)) { | ||
const refId = decode.number(bytes, it); | ||
const refId = decode_1.decode.number(bytes, it); | ||
value = $root.refs.get(refId); | ||
@@ -75,7 +75,7 @@ if (previousValue) { | ||
// | ||
value = decode[type](bytes, it); | ||
value = decode_1.decode[type](bytes, it); | ||
} | ||
else { | ||
const typeDef = (0, registry_1.getType)(Object.keys(type)[0]); | ||
const refId = decode.number(bytes, it); | ||
const refId = decode_1.decode.number(bytes, it); | ||
const valueRef = ($root.refs.has(refId)) | ||
@@ -160,3 +160,3 @@ ? previousValue || $root.refs.get(refId) | ||
} | ||
const index = decode.number(bytes, it); | ||
const index = decode_1.decode.number(bytes, it); | ||
const type = ref[symbols_1.$childType]; | ||
@@ -166,3 +166,3 @@ let dynamicIndex; | ||
if (typeof (ref['set']) === "function") { | ||
dynamicIndex = decode.string(bytes, it); // MapSchema | ||
dynamicIndex = decode_1.decode.string(bytes, it); // MapSchema | ||
ref['setIndex'](index, dynamicIndex); | ||
@@ -230,3 +230,3 @@ } | ||
// TODO: refactor here, try to follow same flow as below | ||
const refId = decode.number(bytes, it); | ||
const refId = decode_1.decode.number(bytes, it); | ||
const previousValue = decoder.root.refs.get(refId); | ||
@@ -247,3 +247,3 @@ index = ref.findIndex((value) => value === previousValue); | ||
else if (operation === spec_1.OPERATION.ADD_BY_REFID) { | ||
const refId = decode.number(bytes, it); | ||
const refId = decode_1.decode.number(bytes, it); | ||
const itemByRefId = decoder.root.refs.get(refId); | ||
@@ -256,3 +256,3 @@ // use existing index, or push new value | ||
else { | ||
index = decode.number(bytes, it); | ||
index = decode_1.decode.number(bytes, it); | ||
} | ||
@@ -259,0 +259,0 @@ const type = ref[symbols_1.$childType]; |
@@ -6,3 +6,3 @@ "use strict"; | ||
const symbols_1 = require("../types/symbols"); | ||
const decode = require("../encoding/decode"); | ||
const decode_1 = require("../encoding/decode"); | ||
const spec_1 = require("../encoding/spec"); | ||
@@ -38,3 +38,3 @@ const ReferenceTracker_1 = require("./ReferenceTracker"); | ||
it.offset++; | ||
this.currentRefId = decode.number(bytes, it); | ||
this.currentRefId = decode_1.decode.number(bytes, it); | ||
const nextRef = $root.refs.get(this.currentRefId); | ||
@@ -61,5 +61,5 @@ // | ||
while (it.offset < totalBytes) { | ||
if (decode.switchStructureCheck(bytes, it)) { | ||
if (bytes[it.offset] === spec_1.SWITCH_TO_STRUCTURE) { | ||
nextIterator.offset = it.offset + 1; | ||
if ($root.refs.has(decode.number(bytes, nextIterator))) { | ||
if ($root.refs.has(decode_1.decode.number(bytes, nextIterator))) { | ||
break; | ||
@@ -85,3 +85,3 @@ } | ||
it.offset++; | ||
const type_id = decode.number(bytes, it); | ||
const type_id = decode_1.decode.number(bytes, it); | ||
type = this.context.get(type_id); | ||
@@ -88,0 +88,0 @@ } |
@@ -7,6 +7,6 @@ "use strict"; | ||
const symbols_1 = require("../types/symbols"); | ||
const encode = require("../encoding/encode"); | ||
const encode_1 = require("../encoding/encode"); | ||
function encodeValue(encoder, bytes, type, value, operation, it) { | ||
if (typeof (type) === "string") { | ||
encode[type]?.(bytes, value, it); | ||
encode_1.encode[type]?.(bytes, value, it); | ||
} | ||
@@ -18,3 +18,3 @@ else if (type[Symbol.metadata] !== undefined) { | ||
// | ||
encode.number(bytes, value[symbols_1.$changes].refId, it); | ||
encode_1.encode.number(bytes, value[symbols_1.$changes].refId, it); | ||
// Try to encode inherited TYPE_ID if it's an ADD operation. | ||
@@ -30,3 +30,3 @@ if ((operation & spec_1.OPERATION.ADD) === spec_1.OPERATION.ADD) { | ||
// | ||
encode.number(bytes, value[symbols_1.$changes].refId, it); | ||
encode_1.encode.number(bytes, value[symbols_1.$changes].refId, it); | ||
} | ||
@@ -63,3 +63,3 @@ } | ||
// encode index | ||
encode.number(bytes, index, it); | ||
encode_1.encode.number(bytes, index, it); | ||
// Do not encode value for DELETE operations | ||
@@ -79,3 +79,3 @@ if (operation === spec_1.OPERATION.DELETE) { | ||
const dynamicIndex = changeTree.ref['$indexes'].get(index); | ||
encode.string(bytes, dynamicIndex, it); | ||
encode_1.encode.string(bytes, dynamicIndex, it); | ||
} | ||
@@ -129,3 +129,3 @@ } | ||
// encode index | ||
encode.number(bytes, refOrIndex, it); | ||
encode_1.encode.number(bytes, refOrIndex, it); | ||
// Do not encode value for DELETE operations | ||
@@ -132,0 +132,0 @@ if (operation === spec_1.OPERATION.DELETE) { |
@@ -6,3 +6,3 @@ "use strict"; | ||
const symbols_1 = require("../types/symbols"); | ||
const encode = require("../encoding/encode"); | ||
const encode_1 = require("../encoding/encode"); | ||
const spec_1 = require("../encoding/spec"); | ||
@@ -66,3 +66,3 @@ const Root_1 = require("./Root"); | ||
buffer[it.offset++] = spec_1.SWITCH_TO_STRUCTURE & 255; | ||
encode.number(buffer, changeTree.refId, it); | ||
encode_1.encode.number(buffer, changeTree.refId, it); | ||
} | ||
@@ -196,3 +196,3 @@ for (let j = 0, numChanges = operations.operations.length; j < numChanges; j++) { | ||
bytes[it.offset++] = spec_1.SWITCH_TO_STRUCTURE & 255; | ||
encode.number(bytes, changeTree.refId, it); | ||
encode_1.encode.number(bytes, changeTree.refId, it); | ||
const keys = Object.keys(changes); | ||
@@ -263,3 +263,3 @@ for (let i = 0, numChanges = keys.length; i < numChanges; i++) { | ||
bytes[it.offset++] = spec_1.TYPE_ID & 255; | ||
encode.number(bytes, targetTypeId, it); | ||
encode_1.encode.number(bytes, targetTypeId, it); | ||
} | ||
@@ -266,0 +266,0 @@ } |
@@ -1,6 +0,6 @@ | ||
import { Schema } from "../Schema"; | ||
import { CollectionSchema } from "../types/custom/CollectionSchema"; | ||
import { MapSchema } from "../types/custom/MapSchema"; | ||
import { SetSchema } from "../types/custom/SetSchema"; | ||
import { ArraySchema } from "../types/custom/ArraySchema"; | ||
import type { Schema } from "../Schema"; | ||
import type { CollectionSchema } from "../types/custom/CollectionSchema"; | ||
import type { MapSchema } from "../types/custom/MapSchema"; | ||
import type { SetSchema } from "../types/custom/SetSchema"; | ||
import type { ArraySchema } from "../types/custom/ArraySchema"; | ||
import type { Ref } from "../encoder/ChangeTree"; | ||
@@ -7,0 +7,0 @@ export declare class EncodeSchemaError extends Error { |
@@ -29,2 +29,6 @@ "use strict"; | ||
break; | ||
case "bigint64": | ||
case "biguint64": | ||
typeofTarget = "bigint"; | ||
break; | ||
case "string": | ||
@@ -37,2 +41,6 @@ typeofTarget = "string"; | ||
return; | ||
default: | ||
// skip assertion for custom types | ||
// TODO: allow custom types to define their own assertions | ||
return; | ||
} | ||
@@ -39,0 +47,0 @@ if (typeof (value) !== typeofTarget && (!allowNull || (allowNull && value !== null))) { |
@@ -31,21 +31,36 @@ /** | ||
} | ||
export declare function utf8Read(bytes: BufferLike, it: Iterator, length: number): string; | ||
export declare function int8(bytes: BufferLike, it: Iterator): number; | ||
export declare function uint8(bytes: BufferLike, it: Iterator): any; | ||
export declare function int16(bytes: BufferLike, it: Iterator): number; | ||
export declare function uint16(bytes: BufferLike, it: Iterator): number; | ||
export declare function int32(bytes: BufferLike, it: Iterator): number; | ||
export declare function uint32(bytes: BufferLike, it: Iterator): number; | ||
export declare function float32(bytes: BufferLike, it: Iterator): number; | ||
export declare function float64(bytes: BufferLike, it: Iterator): number; | ||
export declare function int64(bytes: BufferLike, it: Iterator): number; | ||
export declare function uint64(bytes: BufferLike, it: Iterator): number; | ||
export declare function readFloat32(bytes: BufferLike, it: Iterator): number; | ||
export declare function readFloat64(bytes: BufferLike, it: Iterator): number; | ||
export declare function boolean(bytes: BufferLike, it: Iterator): boolean; | ||
export declare function string(bytes: BufferLike, it: Iterator): string; | ||
export declare function stringCheck(bytes: BufferLike, it: Iterator): boolean; | ||
export declare function number(bytes: BufferLike, it: Iterator): any; | ||
export declare function numberCheck(bytes: BufferLike, it: Iterator): boolean; | ||
export declare function arrayCheck(bytes: BufferLike, it: Iterator): boolean; | ||
export declare function switchStructureCheck(bytes: BufferLike, it: Iterator): boolean; | ||
declare function utf8Read(bytes: BufferLike, it: Iterator, length: number): string; | ||
declare function int8(bytes: BufferLike, it: Iterator): number; | ||
declare function uint8(bytes: BufferLike, it: Iterator): any; | ||
declare function int16(bytes: BufferLike, it: Iterator): number; | ||
declare function uint16(bytes: BufferLike, it: Iterator): number; | ||
declare function int32(bytes: BufferLike, it: Iterator): number; | ||
declare function uint32(bytes: BufferLike, it: Iterator): number; | ||
declare function float32(bytes: BufferLike, it: Iterator): number; | ||
declare function float64(bytes: BufferLike, it: Iterator): number; | ||
declare function int64(bytes: BufferLike, it: Iterator): number; | ||
declare function uint64(bytes: BufferLike, it: Iterator): number; | ||
declare function bigint64(bytes: BufferLike, it: Iterator): bigint; | ||
declare function biguint64(bytes: BufferLike, it: Iterator): bigint; | ||
declare function boolean(bytes: BufferLike, it: Iterator): boolean; | ||
declare function string(bytes: BufferLike, it: Iterator): string; | ||
declare function number(bytes: BufferLike, it: Iterator): any; | ||
export declare const decode: { | ||
utf8Read: typeof utf8Read; | ||
int8: typeof int8; | ||
uint8: typeof uint8; | ||
int16: typeof int16; | ||
uint16: typeof uint16; | ||
int32: typeof int32; | ||
uint32: typeof uint32; | ||
float32: typeof float32; | ||
float64: typeof float64; | ||
int64: typeof int64; | ||
uint64: typeof uint64; | ||
bigint64: typeof bigint64; | ||
biguint64: typeof biguint64; | ||
boolean: typeof boolean; | ||
string: typeof string; | ||
number: typeof number; | ||
}; | ||
export {}; |
@@ -25,23 +25,11 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.utf8Read = utf8Read; | ||
exports.int8 = int8; | ||
exports.uint8 = uint8; | ||
exports.int16 = int16; | ||
exports.uint16 = uint16; | ||
exports.int32 = int32; | ||
exports.uint32 = uint32; | ||
exports.float32 = float32; | ||
exports.float64 = float64; | ||
exports.int64 = int64; | ||
exports.uint64 = uint64; | ||
exports.readFloat32 = readFloat32; | ||
exports.readFloat64 = readFloat64; | ||
exports.boolean = boolean; | ||
exports.string = string; | ||
exports.stringCheck = stringCheck; | ||
exports.number = number; | ||
exports.numberCheck = numberCheck; | ||
exports.arrayCheck = arrayCheck; | ||
exports.switchStructureCheck = switchStructureCheck; | ||
const spec_1 = require("./spec"); | ||
exports.decode = void 0; | ||
// force little endian to facilitate decoding on multiple implementations | ||
const _isLittleEndian = true; // new Uint16Array(new Uint8Array([1, 0]).buffer)[0] === 1; | ||
const _convoBuffer = new ArrayBuffer(8); | ||
const _int32 = new Int32Array(_convoBuffer); | ||
const _float32 = new Float32Array(_convoBuffer); | ||
const _float64 = new Float64Array(_convoBuffer); | ||
const _uint64 = new BigUint64Array(_convoBuffer); | ||
const _int64 = new BigInt64Array(_convoBuffer); | ||
function utf8Read(bytes, it, length) { | ||
@@ -112,7 +100,12 @@ var string = '', chr = 0; | ||
function float32(bytes, it) { | ||
return readFloat32(bytes, it); | ||
_int32[0] = int32(bytes, it); | ||
return _float32[0]; | ||
} | ||
; | ||
function float64(bytes, it) { | ||
return readFloat64(bytes, it); | ||
_int32[_isLittleEndian ? 0 : 1] = int32(bytes, it); | ||
_int32[_isLittleEndian ? 1 : 0] = int32(bytes, it); | ||
return _float64[0]; | ||
} | ||
; | ||
function int64(bytes, it) { | ||
@@ -130,18 +123,12 @@ const low = uint32(bytes, it); | ||
; | ||
// force little endian to facilitate decoding on multiple implementations | ||
const _isLittleEndian = true; // new Uint16Array(new Uint8Array([1, 0]).buffer)[0] === 1; | ||
const _int32 = new Int32Array(2); | ||
const _float32 = new Float32Array(_int32.buffer); | ||
const _float64 = new Float64Array(_int32.buffer); | ||
function readFloat32(bytes, it) { | ||
function bigint64(bytes, it) { | ||
_int32[0] = int32(bytes, it); | ||
return _float32[0]; | ||
_int32[1] = int32(bytes, it); | ||
return _int64[0]; | ||
} | ||
; | ||
function readFloat64(bytes, it) { | ||
_int32[_isLittleEndian ? 0 : 1] = int32(bytes, it); | ||
_int32[_isLittleEndian ? 1 : 0] = int32(bytes, it); | ||
return _float64[0]; | ||
function biguint64(bytes, it) { | ||
_int32[0] = int32(bytes, it); | ||
_int32[1] = int32(bytes, it); | ||
return _uint64[0]; | ||
} | ||
; | ||
function boolean(bytes, it) { | ||
@@ -169,14 +156,2 @@ return uint8(bytes, it) > 0; | ||
} | ||
function stringCheck(bytes, it) { | ||
const prefix = bytes[it.offset]; | ||
return ( | ||
// fixstr | ||
(prefix < 0xc0 && prefix > 0xa0) || | ||
// str 8 | ||
prefix === 0xd9 || | ||
// str 16 | ||
prefix === 0xda || | ||
// str 32 | ||
prefix === 0xdb); | ||
} | ||
function number(bytes, it) { | ||
@@ -190,7 +165,7 @@ const prefix = bytes[it.offset++]; | ||
// float 32 | ||
return readFloat32(bytes, it); | ||
return float32(bytes, it); | ||
} | ||
else if (prefix === 0xcb) { | ||
// float 64 | ||
return readFloat64(bytes, it); | ||
return float64(bytes, it); | ||
} | ||
@@ -235,38 +210,20 @@ else if (prefix === 0xcc) { | ||
; | ||
function numberCheck(bytes, it) { | ||
const prefix = bytes[it.offset]; | ||
// positive fixint - 0x00 - 0x7f | ||
// float 32 - 0xca | ||
// float 64 - 0xcb | ||
// uint 8 - 0xcc | ||
// uint 16 - 0xcd | ||
// uint 32 - 0xce | ||
// uint 64 - 0xcf | ||
// int 8 - 0xd0 | ||
// int 16 - 0xd1 | ||
// int 32 - 0xd2 | ||
// int 64 - 0xd3 | ||
return (prefix < 0x80 || | ||
(prefix >= 0xca && prefix <= 0xd3)); | ||
} | ||
function arrayCheck(bytes, it) { | ||
return bytes[it.offset] < 0xa0; | ||
// const prefix = bytes[it.offset] ; | ||
// if (prefix < 0xa0) { | ||
// return prefix; | ||
// // array | ||
// } else if (prefix === 0xdc) { | ||
// it.offset += 2; | ||
// } else if (0xdd) { | ||
// it.offset += 4; | ||
// } | ||
// return prefix; | ||
} | ||
function switchStructureCheck(bytes, it) { | ||
return ( | ||
// previous byte should be `SWITCH_TO_STRUCTURE` | ||
bytes[it.offset - 1] === spec_1.SWITCH_TO_STRUCTURE && | ||
// next byte should be a number | ||
(bytes[it.offset] < 0x80 || (bytes[it.offset] >= 0xca && bytes[it.offset] <= 0xd3))); | ||
} | ||
exports.decode = { | ||
utf8Read, | ||
int8, | ||
uint8, | ||
int16, | ||
uint16, | ||
int32, | ||
uint32, | ||
float32, | ||
float64, | ||
int64, | ||
uint64, | ||
bigint64, | ||
biguint64, | ||
boolean, | ||
string, | ||
number, | ||
}; | ||
//# sourceMappingURL=decode.js.map |
@@ -25,18 +25,37 @@ /** | ||
export type BufferLike = number[] | ArrayBufferLike; | ||
export declare const utf8Length: (str: string, _?: any) => number; | ||
export declare function utf8Write(view: BufferLike, str: string, it: Iterator): void; | ||
export declare function int8(bytes: BufferLike, value: number, it: Iterator): void; | ||
export declare function uint8(bytes: BufferLike, value: number, it: Iterator): void; | ||
export declare function int16(bytes: BufferLike, value: number, it: Iterator): void; | ||
export declare function uint16(bytes: BufferLike, value: number, it: Iterator): void; | ||
export declare function int32(bytes: BufferLike, value: number, it: Iterator): void; | ||
export declare function uint32(bytes: BufferLike, value: number, it: Iterator): void; | ||
export declare function int64(bytes: BufferLike, value: number, it: Iterator): void; | ||
export declare function uint64(bytes: BufferLike, value: number, it: Iterator): void; | ||
export declare function float32(bytes: BufferLike, value: number, it: Iterator): void; | ||
export declare function float64(bytes: BufferLike, value: number, it: Iterator): void; | ||
export declare function writeFloat32(bytes: BufferLike, value: number, it: Iterator): void; | ||
export declare function writeFloat64(bytes: BufferLike, value: number, it: Iterator): void; | ||
export declare function boolean(bytes: BufferLike, value: number, it: Iterator): void; | ||
export declare function string(bytes: BufferLike, value: string, it: Iterator): number; | ||
export declare function number(bytes: BufferLike, value: number, it: Iterator): 1 | 2 | 3 | 5 | 9; | ||
declare function utf8Write(view: BufferLike, str: string, it: Iterator): void; | ||
declare function int8(bytes: BufferLike, value: number, it: Iterator): void; | ||
declare function uint8(bytes: BufferLike, value: number, it: Iterator): void; | ||
declare function int16(bytes: BufferLike, value: number, it: Iterator): void; | ||
declare function uint16(bytes: BufferLike, value: number, it: Iterator): void; | ||
declare function int32(bytes: BufferLike, value: number, it: Iterator): void; | ||
declare function uint32(bytes: BufferLike, value: number, it: Iterator): void; | ||
declare function int64(bytes: BufferLike, value: number, it: Iterator): void; | ||
declare function uint64(bytes: BufferLike, value: number, it: Iterator): void; | ||
declare function bigint64(bytes: BufferLike, value: bigint, it: Iterator): void; | ||
declare function biguint64(bytes: BufferLike, value: bigint, it: Iterator): void; | ||
declare function float32(bytes: BufferLike, value: number, it: Iterator): void; | ||
declare function float64(bytes: BufferLike, value: number, it: Iterator): void; | ||
declare function boolean(bytes: BufferLike, value: number, it: Iterator): void; | ||
declare function string(bytes: BufferLike, value: string, it: Iterator): number; | ||
declare function number(bytes: BufferLike, value: number, it: Iterator): 1 | 2 | 3 | 5 | 9; | ||
export declare const encode: { | ||
int8: typeof int8; | ||
uint8: typeof uint8; | ||
int16: typeof int16; | ||
uint16: typeof uint16; | ||
int32: typeof int32; | ||
uint32: typeof uint32; | ||
int64: typeof int64; | ||
uint64: typeof uint64; | ||
bigint64: typeof bigint64; | ||
biguint64: typeof biguint64; | ||
float32: typeof float32; | ||
float64: typeof float64; | ||
boolean: typeof boolean; | ||
string: typeof string; | ||
number: typeof number; | ||
utf8Write: typeof utf8Write; | ||
utf8Length: (str: string, _?: any) => number; | ||
}; | ||
export {}; |
@@ -25,19 +25,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.utf8Length = void 0; | ||
exports.utf8Write = utf8Write; | ||
exports.int8 = int8; | ||
exports.uint8 = uint8; | ||
exports.int16 = int16; | ||
exports.uint16 = uint16; | ||
exports.int32 = int32; | ||
exports.uint32 = uint32; | ||
exports.int64 = int64; | ||
exports.uint64 = uint64; | ||
exports.float32 = float32; | ||
exports.float64 = float64; | ||
exports.writeFloat32 = writeFloat32; | ||
exports.writeFloat64 = writeFloat64; | ||
exports.boolean = boolean; | ||
exports.string = string; | ||
exports.number = number; | ||
exports.encode = void 0; | ||
/** | ||
@@ -53,4 +37,11 @@ * msgpack implementation highly based on notepack.io | ||
catch (e) { } | ||
// force little endian to facilitate decoding on multiple implementations | ||
const _isLittleEndian = true; // new Uint16Array(new Uint8Array([1, 0]).buffer)[0] === 1; | ||
const _convoBuffer = new ArrayBuffer(8); | ||
const _int32 = new Int32Array(_convoBuffer); | ||
const _float32 = new Float32Array(_convoBuffer); | ||
const _float64 = new Float64Array(_convoBuffer); | ||
const _int64 = new BigInt64Array(_convoBuffer); | ||
const hasBufferByteLength = (typeof Buffer !== 'undefined' && Buffer.byteLength); | ||
exports.utf8Length = (hasBufferByteLength) | ||
const utf8Length = (hasBufferByteLength) | ||
? Buffer.byteLength // node | ||
@@ -156,19 +147,17 @@ : function (str, _) { | ||
; | ||
function float32(bytes, value, it) { | ||
writeFloat32(bytes, value, it); | ||
function bigint64(bytes, value, it) { | ||
_int64[0] = BigInt.asIntN(64, value); | ||
int32(bytes, _int32[0], it); | ||
int32(bytes, _int32[1], it); | ||
} | ||
function float64(bytes, value, it) { | ||
writeFloat64(bytes, value, it); | ||
function biguint64(bytes, value, it) { | ||
_int64[0] = BigInt.asIntN(64, value); | ||
int32(bytes, _int32[0], it); | ||
int32(bytes, _int32[1], it); | ||
} | ||
// force little endian to facilitate decoding on multiple implementations | ||
const _isLittleEndian = true; // new Uint16Array(new Uint8Array([1, 0]).buffer)[0] === 1; | ||
const _int32 = new Int32Array(2); | ||
const _float32 = new Float32Array(_int32.buffer); | ||
const _float64 = new Float64Array(_int32.buffer); | ||
function writeFloat32(bytes, value, it) { | ||
function float32(bytes, value, it) { | ||
_float32[0] = value; | ||
int32(bytes, _int32[0], it); | ||
} | ||
; | ||
function writeFloat64(bytes, value, it) { | ||
function float64(bytes, value, it) { | ||
_float64[0] = value; | ||
@@ -178,3 +167,2 @@ int32(bytes, _int32[_isLittleEndian ? 0 : 1], it); | ||
} | ||
; | ||
function boolean(bytes, value, it) { | ||
@@ -189,3 +177,3 @@ bytes[it.offset++] = value ? 1 : 0; // uint8 | ||
} | ||
let length = (0, exports.utf8Length)(value, "utf8"); | ||
let length = utf8Length(value, "utf8"); | ||
let size = 0; | ||
@@ -229,11 +217,14 @@ // fixstr | ||
else if (value !== (value | 0)) { | ||
if (Math.abs(value) <= 3.4028235e+38) { // range check | ||
_float32[0] = value; | ||
if (Math.abs(Math.abs(_float32[0]) - Math.abs(value)) < 1e-4) { // precision check; adjust 1e-n (n = precision) to in-/decrease acceptable precision loss | ||
// now we know value is in range for f32 and has acceptable precision for f32 | ||
bytes[it.offset++] = 0xca; | ||
float32(bytes, value, it); | ||
return 5; | ||
} | ||
} | ||
bytes[it.offset++] = 0xcb; | ||
writeFloat64(bytes, value, it); | ||
float64(bytes, value, it); | ||
return 9; | ||
// TODO: encode float 32? | ||
// is it possible to differentiate between float32 / float64 here? | ||
// // float 32 | ||
// bytes.push(0xca); | ||
// writeFloat32(bytes, value); | ||
// return 5; | ||
} | ||
@@ -299,2 +290,21 @@ if (value >= 0) { | ||
} | ||
exports.encode = { | ||
int8, | ||
uint8, | ||
int16, | ||
uint16, | ||
int32, | ||
uint32, | ||
int64, | ||
uint64, | ||
bigint64, | ||
biguint64, | ||
float32, | ||
float64, | ||
boolean, | ||
string, | ||
number, | ||
utf8Write, | ||
utf8Length, | ||
}; | ||
//# sourceMappingURL=encode.js.map |
@@ -10,4 +10,4 @@ export declare const SWITCH_TO_STRUCTURE = 255; | ||
DELETE = 64,// (01000000) delete field | ||
DELETE_AND_MOVE = 96,// () add new structure/primitive | ||
MOVE_AND_ADD = 160,// () add new structure/primitive | ||
DELETE_AND_MOVE = 96,// () ArraySchema only | ||
MOVE_AND_ADD = 160,// () ArraySchema only | ||
DELETE_AND_ADD = 192,// (11000000) DELETE field, followed by an ADD | ||
@@ -21,4 +21,2 @@ /** | ||
*/ | ||
PUSH = 11, | ||
UNSHIFT = 12, | ||
REVERSE = 15, | ||
@@ -25,0 +23,0 @@ MOVE = 32, |
@@ -24,4 +24,2 @@ "use strict"; | ||
*/ | ||
OPERATION[OPERATION["PUSH"] = 11] = "PUSH"; | ||
OPERATION[OPERATION["UNSHIFT"] = 12] = "UNSHIFT"; | ||
OPERATION[OPERATION["REVERSE"] = 15] = "REVERSE"; | ||
@@ -28,0 +26,0 @@ OPERATION[OPERATION["MOVE"] = 32] = "MOVE"; |
@@ -12,10 +12,8 @@ export { Schema } from "./Schema"; | ||
export { SetSchema }; | ||
import { registerType } from "./types/registry"; | ||
export { registerType }; | ||
import { registerType, defineCustomTypes } from "./types/registry"; | ||
export { registerType, defineCustomTypes }; | ||
export { dumpChanges } from "./utils"; | ||
export { $track, $encoder, $decoder, $filter, $getByIndex, $deleteByIndex, $changes, $childType } from "./types/symbols"; | ||
export type { Iterator } from "./encoding/decode"; | ||
import * as encode from "./encoding/encode"; | ||
import * as decode from "./encoding/decode"; | ||
export { encode, decode }; | ||
export { encode } from "./encoding/encode"; | ||
export { decode, type Iterator } from "./encoding/decode"; | ||
export { Reflection, ReflectionType, ReflectionField, } from "./Reflection"; | ||
@@ -22,0 +20,0 @@ export { Metadata } from "./Metadata"; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.OPERATION = exports.decodeKeyValueOperation = exports.decodeSchemaOperation = exports.Decoder = exports.StateView = exports.ChangeTree = exports.encodeKeyValueOperation = exports.encodeSchemaOperation = exports.Encoder = exports.getRawChangesCallback = exports.getDecoderStateCallbacks = exports.TypeContext = exports.schema = exports.view = exports.defineTypes = exports.deprecated = exports.type = exports.Metadata = exports.ReflectionField = exports.ReflectionType = exports.Reflection = exports.decode = exports.encode = exports.$childType = exports.$changes = exports.$deleteByIndex = exports.$getByIndex = exports.$filter = exports.$decoder = exports.$encoder = exports.$track = exports.dumpChanges = exports.registerType = exports.SetSchema = exports.CollectionSchema = exports.ArraySchema = exports.MapSchema = exports.Schema = void 0; | ||
exports.OPERATION = exports.decodeKeyValueOperation = exports.decodeSchemaOperation = exports.Decoder = exports.StateView = exports.ChangeTree = exports.encodeKeyValueOperation = exports.encodeSchemaOperation = exports.Encoder = exports.getRawChangesCallback = exports.getDecoderStateCallbacks = exports.TypeContext = exports.schema = exports.view = exports.defineTypes = exports.deprecated = exports.type = exports.Metadata = exports.ReflectionField = exports.ReflectionType = exports.Reflection = exports.decode = exports.encode = exports.$childType = exports.$changes = exports.$deleteByIndex = exports.$getByIndex = exports.$filter = exports.$decoder = exports.$encoder = exports.$track = exports.dumpChanges = exports.defineCustomTypes = exports.registerType = exports.SetSchema = exports.CollectionSchema = exports.ArraySchema = exports.MapSchema = exports.Schema = void 0; | ||
var Schema_1 = require("./Schema"); | ||
@@ -16,2 +16,3 @@ Object.defineProperty(exports, "Schema", { enumerable: true, get: function () { return Schema_1.Schema; } }); | ||
Object.defineProperty(exports, "registerType", { enumerable: true, get: function () { return registry_1.registerType; } }); | ||
Object.defineProperty(exports, "defineCustomTypes", { enumerable: true, get: function () { return registry_1.defineCustomTypes; } }); | ||
(0, registry_1.registerType)("map", { constructor: MapSchema_1.MapSchema }); | ||
@@ -34,6 +35,6 @@ (0, registry_1.registerType)("array", { constructor: ArraySchema_1.ArraySchema }); | ||
Object.defineProperty(exports, "$childType", { enumerable: true, get: function () { return symbols_1.$childType; } }); | ||
const encode = require("./encoding/encode"); | ||
exports.encode = encode; | ||
const decode = require("./encoding/decode"); | ||
exports.decode = decode; | ||
var encode_1 = require("./encoding/encode"); | ||
Object.defineProperty(exports, "encode", { enumerable: true, get: function () { return encode_1.encode; } }); | ||
var decode_1 = require("./encoding/decode"); | ||
Object.defineProperty(exports, "decode", { enumerable: true, get: function () { return decode_1.decode; } }); | ||
// Reflection | ||
@@ -40,0 +41,0 @@ var Reflection_1 = require("./Reflection"); |
@@ -0,3 +1,7 @@ | ||
import { BufferLike } from "../encoding/encode"; | ||
import { Iterator } from "../encoding/decode"; | ||
export interface TypeDefinition { | ||
constructor: any; | ||
constructor?: any; | ||
encode?: (bytes: BufferLike, value: any, it: Iterator) => any; | ||
decode?: (bytes: BufferLike, it: Iterator) => any; | ||
} | ||
@@ -7,1 +11,4 @@ export declare function registerType(identifier: string, definition: TypeDefinition): void; | ||
export declare function getType(identifier: string): TypeDefinition; | ||
export declare function defineCustomTypes<T extends { | ||
[key: string]: TypeDefinition; | ||
}>(types: T): (t: keyof T) => PropertyDecorator; |
@@ -6,7 +6,19 @@ "use strict"; | ||
exports.getType = getType; | ||
exports.defineCustomTypes = defineCustomTypes; | ||
const annotations_1 = require("../annotations"); | ||
const encode_1 = require("../encoding/encode"); | ||
const decode_1 = require("../encoding/decode"); | ||
const registeredTypes = {}; | ||
const identifiers = new Map(); | ||
function registerType(identifier, definition) { | ||
identifiers.set(definition.constructor, identifier); | ||
registeredTypes[identifier] = definition; | ||
if (definition.constructor) { | ||
identifiers.set(definition.constructor, identifier); | ||
registeredTypes[identifier] = definition; | ||
} | ||
if (definition.encode) { | ||
encode_1.encode[identifier] = definition.encode; | ||
} | ||
if (definition.decode) { | ||
decode_1.decode[identifier] = definition.decode; | ||
} | ||
} | ||
@@ -19,2 +31,8 @@ function getIdentifier(klass) { | ||
} | ||
function defineCustomTypes(types) { | ||
for (const identifier in types) { | ||
registerType(identifier, types[identifier]); | ||
} | ||
return (t) => (0, annotations_1.type)(t); | ||
} | ||
//# sourceMappingURL=registry.js.map |
{ | ||
"name": "@colyseus/schema", | ||
"version": "3.0.0-alpha.40", | ||
"version": "3.0.0-alpha.41", | ||
"description": "Binary state serializer with delta encoding for games", | ||
@@ -5,0 +5,0 @@ "bin": { |
@@ -166,3 +166,3 @@ <div align="center"> | ||
There are 3 majour features of the `Encoder` class: | ||
There are 3 major features of the `Encoder` class: | ||
@@ -237,2 +237,14 @@ - Encoding the full state | ||
## Decoder | ||
The `Decoder` class is used to decode the binary data received from the server. | ||
```typescript | ||
import { Decoder } from "@colyseus/schema"; | ||
const state = new MyState(); | ||
const decoder = new Decoder(state); | ||
decoder.decode(encodedBytes); | ||
``` | ||
### Backwards/forwards compability | ||
@@ -288,6 +300,10 @@ | ||
## Decoder implementation in other languages | ||
## Decoder implementations | ||
Each Colyseus SDK has its own decoder implementation of the `@colyseus/schema` protocol: | ||
Decoders for each target language are located at [`/decoders/`](decoders). They have no third party dependencies. | ||
- [C#](https://github.com/colyseus/colyseus-unity-sdk) | ||
- [Haxe](https://github.com/colyseus/colyseus-haxe) | ||
- [Lua](https://github.com/colyseus/colyseus-defold) | ||
- [C++](https://github.com/colyseus/colyseus-cocos2d-x) _(Not up-to-date)_ | ||
@@ -294,0 +310,0 @@ ## Why |
@@ -28,3 +28,5 @@ import "./symbol.shim"; | ||
"float32" | | ||
"float64"; | ||
"float64" | | ||
"bigint64" | | ||
"biguint64"; | ||
@@ -31,0 +33,0 @@ export type PrimitiveType = RawPrimitiveType | typeof Schema | object; |
@@ -62,12 +62,5 @@ import { | ||
using Colyseus.Schema; | ||
using Action = System.Action; | ||
${namespace ? `\nnamespace ${namespace} {` : ""} | ||
${indent}public partial class ${klass.name} : ${klass.extends} { | ||
${klass.properties.map((prop) => generateProperty(prop, indent)).join("\n\n")} | ||
${indent}\t/* | ||
${indent}\t * Support for individual property change callbacks below... | ||
${indent}\t */ | ||
${generateAllFieldCallbacks(klass, indent)} | ||
${indent}} | ||
@@ -123,3 +116,3 @@ ${namespace ? "}" : ""} | ||
initializer = `new ${langType}()`; | ||
initializer = `null`; | ||
@@ -152,41 +145,2 @@ } else { | ||
function generateAllFieldCallbacks(klass: Class, indent: string) { | ||
// | ||
// TODO: improve me. It would be great to generate less boilerplate in favor | ||
// of a single implementation on C# Schema class itself. | ||
// | ||
const eventNames: string[] = []; | ||
return `${klass.properties | ||
.filter(prop => !prop.deprecated) // generate only for properties that haven't been deprecated. | ||
.map(prop => { | ||
const eventName = `__${prop.name}Change`; | ||
eventNames.push(eventName); | ||
const defaultNull = (prop.childType) | ||
? "null" | ||
: `default(${getType(prop)})`; | ||
return `\t${indent}protected event PropertyChangeHandler<${getType(prop)}> ${eventName}; | ||
\t${indent}public Action On${capitalize(prop.name)}Change(PropertyChangeHandler<${getType(prop)}> __handler, bool __immediate = true) { | ||
\t${indent}\tif (__callbacks == null) { __callbacks = new SchemaCallbacks(); } | ||
\t${indent}\t__callbacks.AddPropertyCallback(nameof(this.${prop.name})); | ||
\t${indent}\t${eventName} += __handler; | ||
\t${indent}\tif (__immediate && this.${prop.name} != ${defaultNull}) { __handler(this.${prop.name}, ${defaultNull}); } | ||
\t${indent}\treturn () => { | ||
\t${indent}\t\t__callbacks.RemovePropertyCallback(nameof(${prop.name})); | ||
\t${indent}\t\t${eventName} -= __handler; | ||
\t${indent}\t}; | ||
\t${indent}}`; | ||
}).join("\n\n")} | ||
\t${indent}protected override void TriggerFieldChange(DataChange change) { | ||
\t${indent}\tswitch (change.Field) { | ||
${klass.properties.filter(prop => !prop.deprecated).map((prop, i) => { | ||
return `\t${indent}\t\tcase nameof(${prop.name}): ${eventNames[i]}?.Invoke((${getType(prop)}) change.Value, (${getType(prop)}) change.PreviousValue); break;`; | ||
}).join("\n")} | ||
\t${indent}\t\tdefault: break; | ||
\t\t${indent}} | ||
\t${indent}}`; | ||
} | ||
function getChildType(prop: Property) { | ||
@@ -193,0 +147,0 @@ return typeMaps[prop.childType]; |
@@ -6,3 +6,3 @@ import { OPERATION } from "../encoding/spec"; | ||
import type { Decoder } from "./Decoder"; | ||
import * as decode from "../encoding/decode"; | ||
import { Iterator, decode } from "../encoding/decode"; | ||
import { $childType, $deleteByIndex, $getByIndex } from "../types/symbols"; | ||
@@ -32,3 +32,3 @@ | ||
bytes: Buffer, | ||
it: decode.Iterator, | ||
it: Iterator, | ||
ref: Ref, | ||
@@ -45,3 +45,3 @@ allChanges: DataChange[], | ||
bytes: Buffer, | ||
it: decode.Iterator, | ||
it: Iterator, | ||
allChanges: DataChange[], | ||
@@ -182,3 +182,3 @@ ) { | ||
bytes: Buffer, | ||
it: decode.Iterator, | ||
it: Iterator, | ||
ref: Ref, | ||
@@ -232,3 +232,3 @@ allChanges: DataChange[], | ||
bytes: Buffer, | ||
it: decode.Iterator, | ||
it: Iterator, | ||
ref: Ref, | ||
@@ -317,3 +317,3 @@ allChanges: DataChange[] | ||
bytes: Buffer, | ||
it: decode.Iterator, | ||
it: Iterator, | ||
ref: ArraySchema, | ||
@@ -320,0 +320,0 @@ allChanges: DataChange[] |
@@ -5,3 +5,3 @@ import { TypeContext } from "../types/TypeContext"; | ||
import * as decode from "../encoding/decode"; | ||
import { decode } from "../encoding/decode"; | ||
import { OPERATION, SWITCH_TO_STRUCTURE, TYPE_ID } from '../encoding/spec'; | ||
@@ -86,5 +86,5 @@ import type { Ref } from "../encoder/ChangeTree"; | ||
// | ||
const nextIterator: decode.Iterator = { offset: it.offset }; | ||
const nextIterator: Iterator = { offset: it.offset }; | ||
while (it.offset < totalBytes) { | ||
if (decode.switchStructureCheck(bytes, it)) { | ||
if (bytes[it.offset] === SWITCH_TO_STRUCTURE) { | ||
nextIterator.offset = it.offset + 1; | ||
@@ -91,0 +91,0 @@ if ($root.refs.has(decode.number(bytes, nextIterator))) { |
import { OPERATION } from "../encoding/spec"; | ||
import { $changes, $childType, $getByIndex } from "../types/symbols"; | ||
import * as encode from "../encoding/encode"; | ||
import { encode } from "../encoding/encode"; | ||
@@ -6,0 +6,0 @@ import type { ChangeTree, Ref } from "./ChangeTree"; |
@@ -5,3 +5,3 @@ import type { Schema } from "../Schema"; | ||
import * as encode from "../encoding/encode"; | ||
import { encode } from "../encoding/encode"; | ||
import type { Iterator } from "../encoding/decode"; | ||
@@ -8,0 +8,0 @@ |
@@ -1,6 +0,6 @@ | ||
import { Schema } from "../Schema"; | ||
import { CollectionSchema } from "../types/custom/CollectionSchema"; | ||
import { MapSchema } from "../types/custom/MapSchema"; | ||
import { SetSchema } from "../types/custom/SetSchema"; | ||
import { ArraySchema } from "../types/custom/ArraySchema"; | ||
import type { Schema } from "../Schema"; | ||
import type { CollectionSchema } from "../types/custom/CollectionSchema"; | ||
import type { MapSchema } from "../types/custom/MapSchema"; | ||
import type { SetSchema } from "../types/custom/SetSchema"; | ||
import type { ArraySchema } from "../types/custom/ArraySchema"; | ||
import type { Ref } from "../encoder/ChangeTree"; | ||
@@ -31,2 +31,6 @@ | ||
break; | ||
case "bigint64": | ||
case "biguint64": | ||
typeofTarget = "bigint"; | ||
break; | ||
case "string": | ||
@@ -39,2 +43,6 @@ typeofTarget = "string"; | ||
return; | ||
default: | ||
// skip assertion for custom types | ||
// TODO: allow custom types to define their own assertions | ||
return; | ||
} | ||
@@ -41,0 +49,0 @@ |
@@ -24,3 +24,2 @@ /** | ||
import { SWITCH_TO_STRUCTURE } from "./spec"; | ||
import type { BufferLike } from "./encode"; | ||
@@ -35,3 +34,13 @@ | ||
export function utf8Read(bytes: BufferLike, it: Iterator, length: number) { | ||
// force little endian to facilitate decoding on multiple implementations | ||
const _isLittleEndian = true; // new Uint16Array(new Uint8Array([1, 0]).buffer)[0] === 1; | ||
const _convoBuffer = new ArrayBuffer(8); | ||
const _int32 = new Int32Array(_convoBuffer); | ||
const _float32 = new Float32Array(_convoBuffer); | ||
const _float64 = new Float64Array(_convoBuffer); | ||
const _uint64 = new BigUint64Array(_convoBuffer); | ||
const _int64 = new BigInt64Array(_convoBuffer); | ||
function utf8Read(bytes: BufferLike, it: Iterator, length: number) { | ||
var string = '', chr = 0; | ||
@@ -81,35 +90,38 @@ for (var i = it.offset, end = it.offset + length; i < end; i++) { | ||
export function int8 (bytes: BufferLike, it: Iterator) { | ||
function int8 (bytes: BufferLike, it: Iterator) { | ||
return uint8(bytes, it) << 24 >> 24; | ||
}; | ||
export function uint8 (bytes: BufferLike, it: Iterator) { | ||
function uint8 (bytes: BufferLike, it: Iterator) { | ||
return bytes[it.offset++]; | ||
}; | ||
export function int16 (bytes: BufferLike, it: Iterator) { | ||
function int16 (bytes: BufferLike, it: Iterator) { | ||
return uint16(bytes, it) << 16 >> 16; | ||
}; | ||
export function uint16 (bytes: BufferLike, it: Iterator) { | ||
function uint16 (bytes: BufferLike, it: Iterator) { | ||
return bytes[it.offset++] | bytes[it.offset++] << 8; | ||
}; | ||
export function int32 (bytes: BufferLike, it: Iterator) { | ||
function int32 (bytes: BufferLike, it: Iterator) { | ||
return bytes[it.offset++] | bytes[it.offset++] << 8 | bytes[it.offset++] << 16 | bytes[it.offset++] << 24; | ||
}; | ||
export function uint32 (bytes: BufferLike, it: Iterator) { | ||
function uint32 (bytes: BufferLike, it: Iterator) { | ||
return int32(bytes, it) >>> 0; | ||
}; | ||
export function float32(bytes: BufferLike, it: Iterator) { | ||
return readFloat32(bytes, it); | ||
} | ||
function float32 (bytes: BufferLike, it: Iterator) { | ||
_int32[0] = int32(bytes, it); | ||
return _float32[0]; | ||
}; | ||
export function float64(bytes: BufferLike, it: Iterator) { | ||
return readFloat64(bytes, it); | ||
} | ||
function float64 (bytes: BufferLike, it: Iterator) { | ||
_int32[_isLittleEndian ? 0 : 1] = int32(bytes, it); | ||
_int32[_isLittleEndian ? 1 : 0] = int32(bytes, it); | ||
return _float64[0]; | ||
}; | ||
export function int64(bytes: BufferLike, it: Iterator) { | ||
function int64(bytes: BufferLike, it: Iterator) { | ||
const low = uint32(bytes, it); | ||
@@ -120,30 +132,25 @@ const high = int32(bytes, it) * Math.pow(2, 32); | ||
export function uint64(bytes: BufferLike, it: Iterator) { | ||
const low = uint32(bytes, it); | ||
const high = uint32(bytes, it) * Math.pow(2, 32); | ||
return high + low; | ||
function uint64(bytes: BufferLike, it: Iterator) { | ||
const low = uint32(bytes, it); | ||
const high = uint32(bytes, it) * Math.pow(2, 32); | ||
return high + low; | ||
}; | ||
// force little endian to facilitate decoding on multiple implementations | ||
const _isLittleEndian = true; // new Uint16Array(new Uint8Array([1, 0]).buffer)[0] === 1; | ||
const _int32 = new Int32Array(2); | ||
const _float32 = new Float32Array(_int32.buffer); | ||
const _float64 = new Float64Array(_int32.buffer); | ||
function bigint64(bytes: BufferLike, it: Iterator) { | ||
_int32[0] = int32(bytes, it); | ||
_int32[1] = int32(bytes, it); | ||
return _int64[0]; | ||
} | ||
export function readFloat32 (bytes: BufferLike, it: Iterator) { | ||
function biguint64(bytes: BufferLike, it: Iterator) { | ||
_int32[0] = int32(bytes, it); | ||
return _float32[0]; | ||
}; | ||
_int32[1] = int32(bytes, it); | ||
return _uint64[0]; | ||
} | ||
export function readFloat64 (bytes: BufferLike, it: Iterator) { | ||
_int32[_isLittleEndian ? 0 : 1] = int32(bytes, it); | ||
_int32[_isLittleEndian ? 1 : 0] = int32(bytes, it); | ||
return _float64[0]; | ||
}; | ||
export function boolean (bytes: BufferLike, it: Iterator) { | ||
function boolean (bytes: BufferLike, it: Iterator) { | ||
return uint8(bytes, it) > 0; | ||
}; | ||
export function string (bytes: BufferLike, it: Iterator) { | ||
function string (bytes: BufferLike, it: Iterator) { | ||
const prefix = bytes[it.offset++]; | ||
@@ -169,17 +176,3 @@ let length: number; | ||
export function stringCheck(bytes: BufferLike, it: Iterator) { | ||
const prefix = bytes[it.offset]; | ||
return ( | ||
// fixstr | ||
(prefix < 0xc0 && prefix > 0xa0) || | ||
// str 8 | ||
prefix === 0xd9 || | ||
// str 16 | ||
prefix === 0xda || | ||
// str 32 | ||
prefix === 0xdb | ||
); | ||
} | ||
export function number (bytes: BufferLike, it: Iterator) { | ||
function number (bytes: BufferLike, it: Iterator) { | ||
const prefix = bytes[it.offset++]; | ||
@@ -193,7 +186,7 @@ | ||
// float 32 | ||
return readFloat32(bytes, it); | ||
return float32(bytes, it); | ||
} else if (prefix === 0xcb) { | ||
// float 64 | ||
return readFloat64(bytes, it); | ||
return float64(bytes, it); | ||
@@ -238,47 +231,19 @@ } else if (prefix === 0xcc) { | ||
export function numberCheck (bytes: BufferLike, it: Iterator) { | ||
const prefix = bytes[it.offset]; | ||
// positive fixint - 0x00 - 0x7f | ||
// float 32 - 0xca | ||
// float 64 - 0xcb | ||
// uint 8 - 0xcc | ||
// uint 16 - 0xcd | ||
// uint 32 - 0xce | ||
// uint 64 - 0xcf | ||
// int 8 - 0xd0 | ||
// int 16 - 0xd1 | ||
// int 32 - 0xd2 | ||
// int 64 - 0xd3 | ||
return ( | ||
prefix < 0x80 || | ||
(prefix >= 0xca && prefix <= 0xd3) | ||
); | ||
} | ||
export function arrayCheck (bytes: BufferLike, it: Iterator) { | ||
return bytes[it.offset] < 0xa0; | ||
// const prefix = bytes[it.offset] ; | ||
// if (prefix < 0xa0) { | ||
// return prefix; | ||
// // array | ||
// } else if (prefix === 0xdc) { | ||
// it.offset += 2; | ||
// } else if (0xdd) { | ||
// it.offset += 4; | ||
// } | ||
// return prefix; | ||
} | ||
export function switchStructureCheck(bytes: BufferLike, it: Iterator) { | ||
return ( | ||
// previous byte should be `SWITCH_TO_STRUCTURE` | ||
bytes[it.offset - 1] === SWITCH_TO_STRUCTURE && | ||
// next byte should be a number | ||
(bytes[it.offset] < 0x80 || (bytes[it.offset] >= 0xca && bytes[it.offset] <= 0xd3)) | ||
); | ||
export const decode = { | ||
utf8Read, | ||
int8, | ||
uint8, | ||
int16, | ||
uint16, | ||
int32, | ||
uint32, | ||
float32, | ||
float64, | ||
int64, | ||
uint64, | ||
bigint64, | ||
biguint64, | ||
boolean, | ||
string, | ||
number, | ||
} |
@@ -38,5 +38,13 @@ /** | ||
// force little endian to facilitate decoding on multiple implementations | ||
const _isLittleEndian = true; // new Uint16Array(new Uint8Array([1, 0]).buffer)[0] === 1; | ||
const _convoBuffer = new ArrayBuffer(8); | ||
const _int32 = new Int32Array(_convoBuffer); | ||
const _float32 = new Float32Array(_convoBuffer); | ||
const _float64 = new Float64Array(_convoBuffer); | ||
const _int64 = new BigInt64Array(_convoBuffer); | ||
const hasBufferByteLength = (typeof Buffer !== 'undefined' && Buffer.byteLength); | ||
export const utf8Length: (str: string, _?: any) => number = (hasBufferByteLength) | ||
const utf8Length: (str: string, _?: any) => number = (hasBufferByteLength) | ||
? Buffer.byteLength // node | ||
@@ -64,3 +72,3 @@ : function (str: string, _?: any) { | ||
export function utf8Write(view: BufferLike, str: string, it: Iterator) { | ||
function utf8Write(view: BufferLike, str: string, it: Iterator) { | ||
var c = 0; | ||
@@ -95,11 +103,11 @@ for (var i = 0, l = str.length; i < l; i++) { | ||
export function int8(bytes: BufferLike, value: number, it: Iterator) { | ||
function int8(bytes: BufferLike, value: number, it: Iterator) { | ||
bytes[it.offset++] = value & 255; | ||
}; | ||
export function uint8(bytes: BufferLike, value: number, it: Iterator) { | ||
function uint8(bytes: BufferLike, value: number, it: Iterator) { | ||
bytes[it.offset++] = value & 255; | ||
}; | ||
export function int16(bytes: BufferLike, value: number, it: Iterator) { | ||
function int16(bytes: BufferLike, value: number, it: Iterator) { | ||
bytes[it.offset++] = value & 255; | ||
@@ -109,3 +117,3 @@ bytes[it.offset++] = (value >> 8) & 255; | ||
export function uint16(bytes: BufferLike, value: number, it: Iterator) { | ||
function uint16(bytes: BufferLike, value: number, it: Iterator) { | ||
bytes[it.offset++] = value & 255; | ||
@@ -115,3 +123,3 @@ bytes[it.offset++] = (value >> 8) & 255; | ||
export function int32(bytes: BufferLike, value: number, it: Iterator) { | ||
function int32(bytes: BufferLike, value: number, it: Iterator) { | ||
bytes[it.offset++] = value & 255; | ||
@@ -123,3 +131,3 @@ bytes[it.offset++] = (value >> 8) & 255; | ||
export function uint32(bytes: BufferLike, value: number, it: Iterator) { | ||
function uint32(bytes: BufferLike, value: number, it: Iterator) { | ||
const b4 = value >> 24; | ||
@@ -135,3 +143,3 @@ const b3 = value >> 16; | ||
export function int64(bytes: BufferLike, value: number, it: Iterator) { | ||
function int64(bytes: BufferLike, value: number, it: Iterator) { | ||
const high = Math.floor(value / Math.pow(2, 32)); | ||
@@ -143,3 +151,3 @@ const low = value >>> 0; | ||
export function uint64(bytes: BufferLike, value: number, it: Iterator) { | ||
function uint64(bytes: BufferLike, value: number, it: Iterator) { | ||
const high = (value / Math.pow(2, 32)) >> 0; | ||
@@ -151,32 +159,30 @@ const low = value >>> 0; | ||
export function float32(bytes: BufferLike, value: number, it: Iterator) { | ||
writeFloat32(bytes, value, it); | ||
function bigint64(bytes: BufferLike, value: bigint, it: Iterator) { | ||
_int64[0] = BigInt.asIntN(64, value); | ||
int32(bytes, _int32[0], it); | ||
int32(bytes, _int32[1], it); | ||
} | ||
export function float64(bytes: BufferLike, value: number, it: Iterator) { | ||
writeFloat64(bytes, value, it); | ||
function biguint64(bytes: BufferLike, value: bigint, it: Iterator) { | ||
_int64[0] = BigInt.asIntN(64, value); | ||
int32(bytes, _int32[0], it); | ||
int32(bytes, _int32[1], it); | ||
} | ||
// force little endian to facilitate decoding on multiple implementations | ||
const _isLittleEndian = true; // new Uint16Array(new Uint8Array([1, 0]).buffer)[0] === 1; | ||
const _int32 = new Int32Array(2); | ||
const _float32 = new Float32Array(_int32.buffer); | ||
const _float64 = new Float64Array(_int32.buffer); | ||
export function writeFloat32(bytes: BufferLike, value: number, it: Iterator) { | ||
function float32(bytes: BufferLike, value: number, it: Iterator) { | ||
_float32[0] = value; | ||
int32(bytes, _int32[0], it); | ||
}; | ||
} | ||
export function writeFloat64(bytes: BufferLike, value: number, it: Iterator) { | ||
function float64(bytes: BufferLike, value: number, it: Iterator) { | ||
_float64[0] = value; | ||
int32(bytes, _int32[_isLittleEndian ? 0 : 1], it); | ||
int32(bytes, _int32[_isLittleEndian ? 1 : 0], it); | ||
}; | ||
} | ||
export function boolean(bytes: BufferLike, value: number, it: Iterator) { | ||
function boolean(bytes: BufferLike, value: number, it: Iterator) { | ||
bytes[it.offset++] = value ? 1 : 0; // uint8 | ||
}; | ||
export function string(bytes: BufferLike, value: string, it: Iterator) { | ||
function string(bytes: BufferLike, value: string, it: Iterator) { | ||
// encode `null` strings as empty. | ||
@@ -219,3 +225,3 @@ if (!value) { value = ""; } | ||
export function number(bytes: BufferLike, value: number, it: Iterator) { | ||
function number(bytes: BufferLike, value: number, it: Iterator) { | ||
if (isNaN(value)) { | ||
@@ -228,13 +234,15 @@ return number(bytes, 0, it); | ||
} else if (value !== (value|0)) { | ||
if (Math.abs(value) <= 3.4028235e+38) { // range check | ||
_float32[0] = value; | ||
if (Math.abs(Math.abs(_float32[0]) - Math.abs(value)) < 1e-4) { // precision check; adjust 1e-n (n = precision) to in-/decrease acceptable precision loss | ||
// now we know value is in range for f32 and has acceptable precision for f32 | ||
bytes[it.offset++] = 0xca; | ||
float32(bytes, value, it); | ||
return 5; | ||
} | ||
} | ||
bytes[it.offset++] = 0xcb; | ||
writeFloat64(bytes, value, it); | ||
float64(bytes, value, it); | ||
return 9; | ||
// TODO: encode float 32? | ||
// is it possible to differentiate between float32 / float64 here? | ||
// // float 32 | ||
// bytes.push(0xca); | ||
// writeFloat32(bytes, value); | ||
// return 5; | ||
} | ||
@@ -310,1 +318,21 @@ | ||
} | ||
export const encode = { | ||
int8, | ||
uint8, | ||
int16, | ||
uint16, | ||
int32, | ||
uint32, | ||
int64, | ||
uint64, | ||
bigint64, | ||
biguint64, | ||
float32, | ||
float64, | ||
boolean, | ||
string, | ||
number, | ||
utf8Write, | ||
utf8Length, | ||
} |
@@ -11,4 +11,4 @@ export const SWITCH_TO_STRUCTURE = 255; // (decoding collides with DELETE_AND_ADD + fieldIndex = 63) | ||
DELETE = 64, // (01000000) delete field | ||
DELETE_AND_MOVE = 96, // () add new structure/primitive | ||
MOVE_AND_ADD = 160, // () add new structure/primitive | ||
DELETE_AND_MOVE = 96, // () ArraySchema only | ||
MOVE_AND_ADD = 160, // () ArraySchema only | ||
DELETE_AND_ADD = 192, // (11000000) DELETE field, followed by an ADD | ||
@@ -24,4 +24,2 @@ | ||
*/ | ||
PUSH = 11, | ||
UNSHIFT = 12, | ||
REVERSE = 15, | ||
@@ -31,3 +29,2 @@ MOVE = 32, | ||
ADD_BY_REFID = 129, | ||
} |
@@ -17,4 +17,4 @@ export { Schema } from "./Schema"; | ||
import { registerType } from "./types/registry"; | ||
export { registerType }; | ||
import { registerType, defineCustomTypes } from "./types/registry"; | ||
export { registerType, defineCustomTypes }; | ||
@@ -31,6 +31,4 @@ registerType("map", { constructor: MapSchema }); | ||
export { $track, $encoder, $decoder, $filter, $getByIndex, $deleteByIndex, $changes, $childType } from "./types/symbols"; | ||
export type { Iterator } from "./encoding/decode"; | ||
import * as encode from "./encoding/encode"; | ||
import * as decode from "./encoding/decode"; | ||
export { encode, decode }; | ||
export { encode } from "./encoding/encode"; | ||
export { decode, type Iterator } from "./encoding/decode"; | ||
@@ -37,0 +35,0 @@ // Reflection |
@@ -0,3 +1,9 @@ | ||
import { DefinitionType, type } from "../annotations"; | ||
import { BufferLike, encode } from "../encoding/encode"; | ||
import { decode, Iterator } from "../encoding/decode"; | ||
export interface TypeDefinition { | ||
constructor: any, | ||
constructor?: any, | ||
encode?: (bytes: BufferLike, value: any, it: Iterator) => any; | ||
decode?: (bytes: BufferLike, it: Iterator) => any; | ||
} | ||
@@ -9,4 +15,9 @@ | ||
export function registerType(identifier: string, definition: TypeDefinition) { | ||
identifiers.set(definition.constructor, identifier); | ||
registeredTypes[identifier] = definition; | ||
if (definition.constructor) { | ||
identifiers.set(definition.constructor, identifier); | ||
registeredTypes[identifier] = definition; | ||
} | ||
if (definition.encode) { encode[identifier] = definition.encode; } | ||
if (definition.decode) { decode[identifier] = definition.decode; } | ||
} | ||
@@ -21,1 +32,9 @@ | ||
} | ||
export function defineCustomTypes<T extends {[key: string]: TypeDefinition}>(types: T) { | ||
for (const identifier in types) { | ||
registerType(identifier, types[identifier]); | ||
} | ||
return (t: keyof T) => type(t as DefinitionType); | ||
} |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
331
5.08%2836035
-0.07%33747
-0.24%