unspoken
Version:
Pack network messages more efficiently. Compress keyed objects into keyless arrays. Decompress arrays back into usable objects.
65 lines (51 loc) • 2.32 kB
text/typescript
import { getIndexingMetas, IndexAtMeta, Newable } from './Meta'
export type PackedArray = (any[] | undefined)
function _packArray<T extends object>(rootCtor:Newable<T>, arr:T[], parentMeta?:IndexAtMeta,):any[] {
if (!arr.length) return []
const cachedMetas = getIndexingMetas(arr[0])
return arr.map(function(item:any) {
return _packObject(rootCtor, item, cachedMetas, parentMeta)
})
}
function _packProperty<T extends object>(rootCtor:Newable<T>, indexMeta:IndexAtMeta, value:(T | T[])):any {
if (value === undefined) {
return undefined
} else if (Array.isArray(value)) {
if (indexMeta.isArray && indexMeta.typeHint !== undefined) {
return _packArray(indexMeta.typeHint, value, indexMeta)
} else if (indexMeta.isArray && indexMeta.typeHint === undefined) {
return value
} else {
throw new Error(`Unspoken: In '${rootCtor.name}', property value for '${indexMeta.propertyKey}' is an array, but indexAt typeHint was not specified as an array '(indexAt(0, [MyClass]))'.`)
}
} else if (indexMeta.typeHint) {
return _packObject(rootCtor, value, undefined, indexMeta)
} else {
return value
}
}
function _packObject<T>(rootCtor:Newable<T>, obj:(T | number[] | string[] | boolean[]), cachedMetas?:IndexAtMeta[], parentPropMeta?:IndexAtMeta):PackedArray {
if (obj === undefined) return undefined
const presentObj = obj as unknown as object
const indexMetas = cachedMetas ?? getIndexingMetas(obj)
if (!indexMetas.length) {
if (presentObj.constructor.name === 'Object') {
throw new Error(`Unspoken: In '${rootCtor.name}', tried to pack an object that has no indexable information. Expected: ${parentPropMeta?.typeHint ?? rootCtor.name}, Got: ${JSON.stringify(obj)}`)
} else {
return []
}
}
const arr = new Array<any>(indexMetas.length)
for (let i = 0; i < indexMetas.length; i++) {
const indexMeta = indexMetas[i]
const value = Reflect.get(presentObj, indexMeta.propertyKey)
const packedValue = _packProperty(rootCtor, indexMeta, value)
arr[i] = packedValue
}
return arr
}
export function pack<T>(ctor:Newable<T>, obj:T):PackedArray {
if (obj === undefined) return undefined
const indexMetas = getIndexingMetas(ctor.prototype)
return _packObject(ctor, obj as unknown as T, indexMetas)
}