structurae
Advanced tools
Comparing version 2.2.0 to 2.3.0
@@ -7,2 +7,10 @@ # Changelog | ||
## [2.3.0] - 2020-03-10 | ||
### Added | ||
- Add static `toJSON` methods to View classes | ||
### Changed | ||
- 2x speed up View encoding/decoding by avoiding extra DataView instantiations. | ||
- (potentially breaking) `TypeView.get` & `TypeView.set` are renames into `TypeView.toJSON` and `TypeView.from` respectively. | ||
## [2.2.0] - 2020-02-26 | ||
@@ -9,0 +17,0 @@ ### Added |
@@ -214,8 +214,12 @@ // Type definitions for structurae | ||
static getLength(): number; | ||
static from(value: number, view?: TypeView): TypeView; | ||
static from(value: number, view?: View, start?: number): View; | ||
static toJSON(view: View, start: number): number; | ||
static of(): TypeView; | ||
static get(position: number, view: View): number; | ||
static set(position: number, value: number, view: View): void; | ||
} | ||
export declare class BooleanView extends TypeView { | ||
static from(value: boolean, view?: View, start?: number): View; | ||
static toJSON(view: View, start: number): boolean; | ||
} | ||
export declare function TypeViewMixin(type: PrimitiveFieldType, littleEndian?: boolean): typeof TypeView; | ||
@@ -234,3 +238,4 @@ | ||
[Symbol.iterator](): IterableIterator<ObjectView>; | ||
static from(value: ArrayLike<object>, array?: ArrayView): ArrayView; | ||
static from(value: ArrayLike<object>, array?: View, start?: number, length?: number): View; | ||
static toJSON(view: View, start: number, length: number): any[]; | ||
static of(size: number): ArrayView; | ||
@@ -276,12 +281,9 @@ static getLength(size: number): number; | ||
get(field: string): number | View; | ||
private getPrimitive(position: number, View: ViewType): object; | ||
private getObject(position: number, View: ViewType, length: number): object; | ||
getValue(field: string): any; | ||
getView(field: string): View; | ||
set(field: string, value: any): this; | ||
private setPrimitive(position: number, value: number, View: ViewType): void; | ||
private setObject(position: number, value: object, View: ViewType, length: number): void; | ||
setView(field: string, value: View): this; | ||
toJSON(): object; | ||
static from(object: object, objectView?: ObjectView): ObjectView; | ||
static from(object: object, view?: View, start?: number, length?: number): View; | ||
static toJSON(view: View, start: number): any[]; | ||
static getLength(): number; | ||
@@ -313,3 +315,4 @@ static initialize(): void; | ||
trim(): StringView; | ||
static from(arrayLike: ArrayLike<number>|string, mapFn?: Function | StringView): StringView; | ||
static from(arrayLike: ArrayLike<number>|string, mapFn?: Function | View, thisArg?: any, length?: number): View; | ||
static toJSON(view: View, start?: number, length?: number): string; | ||
static getByteSize(string: string): number; | ||
@@ -328,3 +331,4 @@ } | ||
static getLength(size: number): number; | ||
static from(value: ArrayLike<number>, array?: TypedArrayView): TypedArrayView; | ||
static from(value: ArrayLike<number>, array?: View, start?: number, length?: number): View; | ||
static toJSON(view: View, start: number, length: number): number[]; | ||
static of(size: number): TypedArrayView; | ||
@@ -331,0 +335,0 @@ } |
@@ -28,3 +28,4 @@ /** | ||
getValue(index) { | ||
return this.get(index).toJSON(); | ||
const { itemLength, View } = this.constructor; | ||
return View.toJSON(this, index * itemLength); | ||
} | ||
@@ -40,3 +41,4 @@ | ||
set(index, value) { | ||
this.constructor.View.from(value, this.get(index)); | ||
const { itemLength, View } = this.constructor; | ||
View.from(value, this, this.byteOffset + index * itemLength, itemLength); | ||
return this; | ||
@@ -87,8 +89,3 @@ } | ||
toJSON() { | ||
const { size } = this; | ||
const json = new Array(size); | ||
for (let i = 0; i < size; i++) { | ||
json[i] = this.getValue(i); | ||
} | ||
return json; | ||
return this.constructor.toJSON(this, 0, this.byteLength); | ||
} | ||
@@ -100,14 +97,16 @@ | ||
* @param {ArrayLike<Object>} value | ||
* @param {ArrayView} [array] | ||
* @param {View} [array] | ||
* @param {number} [start=0] | ||
* @param {number} [length] | ||
* @returns {ArrayView} | ||
*/ | ||
static from(value, array) { | ||
static from(value, array, start = 0, length = this.getLength(value.length)) { | ||
const view = array || this.of(value.length); | ||
if (array) { | ||
new Uint8Array(view.buffer, view.byteOffset, view.byteLength).fill(0); | ||
} | ||
const { size } = view; | ||
new Uint8Array(view.buffer, view.byteOffset + start, length).fill(0); | ||
const { View, itemLength } = this; | ||
const size = length / itemLength; | ||
const max = (size < value.length ? size : value.length); | ||
for (let i = 0; i < max; i++) { | ||
view.set(i, value[i]); | ||
const startOffset = start + i * itemLength; | ||
View.from(value[i], view, startOffset, itemLength); | ||
} | ||
@@ -118,2 +117,21 @@ return view; | ||
/** | ||
* Returns an array representation of a given array view. | ||
* | ||
* @param {View} view | ||
* @param {number} [start=0] | ||
* @param {length} [length] | ||
* @returns {Object} | ||
*/ | ||
static toJSON(view, start, length) { | ||
const { View, itemLength } = this; | ||
const size = length / itemLength; | ||
const array = new Array(size); | ||
for (let i = 0; i < size; i++) { | ||
const startOffset = start + i * itemLength; | ||
array[i] = View.toJSON(view, startOffset, itemLength); | ||
} | ||
return array; | ||
} | ||
/** | ||
* Returns the byte length of an array view to hold a given amount of objects. | ||
@@ -120,0 +138,0 @@ * |
@@ -79,3 +79,3 @@ const { ObjectView, ObjectViewMixin } = require('./object-view'); | ||
const data = new DataView(buffer, offset); | ||
const tag = data[this.tagGetter](0); | ||
const tag = this.tagGetter.call(data, 0); | ||
const View = this.Views[tag]; | ||
@@ -82,0 +82,0 @@ if (!View) throw TypeError('No tag information is found.'); |
const TypeViewMixin = require('./type-view'); | ||
class BooleanView extends TypeViewMixin('uint8') { | ||
static get(position, view) { | ||
return !!super.get(position, view); | ||
/** | ||
* Creates a view with a given value. | ||
* | ||
* @param {number|boolean} value | ||
* @param {View} view | ||
* @param {number} [start=0] | ||
* @returns {View} | ||
*/ | ||
static from(value, view, start) { | ||
return super.from(+value, view, start); | ||
} | ||
static set(position, value, view) { | ||
super.set(position, +value, view); | ||
/** | ||
* Returns the boolean value of a given view. | ||
* | ||
* @param {View} view | ||
* @param {number} [start=0] | ||
* @returns {boolean} | ||
* | ||
*/ | ||
static toJSON(view, start) { | ||
return !!super.toJSON(view, start); | ||
} | ||
@@ -11,0 +27,0 @@ } |
@@ -32,3 +32,3 @@ /** | ||
const view = this.get(index); | ||
if (view) View.from(value, view); | ||
if (view) View.from(value, view, 0, view.byteLength); | ||
return this; | ||
@@ -35,0 +35,0 @@ } |
@@ -12,3 +12,3 @@ const StringView = require('./string-view'); | ||
/** | ||
* @typedef {ArrayView|ObjectView|TypedArrayView|StringView} View | ||
* @typedef {ArrayView|ObjectView|TypedArrayView|StringView|TypeView} View | ||
*/ | ||
@@ -58,3 +58,3 @@ | ||
const { start, View, length } = this.constructor.schema[field]; | ||
return View.isPrimitive ? this.getPrimitive(start, View) | ||
return View.isPrimitive ? View.toJSON(this, start) | ||
: new View(this.buffer, this.byteOffset + start, length); | ||
@@ -64,23 +64,2 @@ } | ||
/** | ||
* @private | ||
* @param {number} position | ||
* @param {ViewType} View | ||
* @returns {number} | ||
*/ | ||
getPrimitive(position, View) { | ||
return View.get(position, this); | ||
} | ||
/** | ||
* @private | ||
* @param {number} position | ||
* @param {ViewType} View | ||
* @param {number} length | ||
* @returns {Object} | ||
*/ | ||
getObject(position, View, length) { | ||
return new View(this.buffer, this.byteOffset + position, length).toJSON(); | ||
} | ||
/** | ||
* Returns the JavaScript value of a given field. | ||
@@ -93,3 +72,3 @@ * | ||
const { start, View, length } = this.constructor.schema[field]; | ||
return View.isPrimitive ? this.getPrimitive(start, View) : this.getObject(start, View, length); | ||
return View.toJSON(this, start, length); | ||
} | ||
@@ -117,7 +96,3 @@ | ||
const { start, View, length } = this.constructor.schema[field]; | ||
if (View.isPrimitive) { | ||
this.setPrimitive(start, value, View); | ||
} else { | ||
this.setObject(start, value, View, length); | ||
} | ||
View.from(value, this, start, length); | ||
return this; | ||
@@ -127,26 +102,2 @@ } | ||
/** | ||
* @private | ||
* @param {number} position | ||
* @param {number} value | ||
* @param {ViewType} View | ||
* @returns {void} | ||
*/ | ||
setPrimitive(position, value, View) { | ||
View.set(position, value, this); | ||
} | ||
/** | ||
* @private | ||
* @param {number} position | ||
* @param {Object} value | ||
* @param {ViewType} View | ||
* @param {number} length | ||
* @returns {void} | ||
*/ | ||
setObject(position, value, View, length) { | ||
const view = new View(this.buffer, this.byteOffset + position, length); | ||
View.from(value, view); | ||
} | ||
/** | ||
* Sets an View to a field. | ||
@@ -174,9 +125,3 @@ * | ||
toJSON() { | ||
const { fields } = this.constructor; | ||
const result = {}; | ||
for (let i = 0; i < fields.length; i++) { | ||
const name = fields[i]; | ||
result[name] = this.getValue(name); | ||
} | ||
return result; | ||
return this.constructor.toJSON(this, 0); | ||
} | ||
@@ -188,16 +133,17 @@ | ||
* @param {Object} object the object to take data from | ||
* @param {ObjectView} [view] the object view to assign fields to | ||
* @returns {ObjectView} | ||
* @param {View} [view] the object view to assign fields to | ||
* @param {number} [start=0] | ||
* @param {number} [length] | ||
* @returns {View} | ||
*/ | ||
static from(object, view) { | ||
static from(object, view, start = 0, length = this.objectLength) { | ||
const objectView = view || new this(this.defaultBuffer.slice()); | ||
if (view) { | ||
// zero out existing view | ||
new Uint8Array(objectView.buffer, objectView.byteOffset, objectView.byteLength) | ||
.fill(0); | ||
} | ||
const { fields } = objectView.constructor; | ||
if (view) new Uint8Array(view.buffer, view.byteOffset + start, length).fill(0); | ||
const { fields, schema } = this; | ||
for (let i = 0; i < fields.length; i++) { | ||
const name = fields[i]; | ||
if (Reflect.has(object, name)) objectView.set(name, object[name]); | ||
if (Reflect.has(object, name)) { | ||
const { View, start: fieldStart, length: fieldLength } = schema[name]; | ||
View.from(object[name], objectView, start + fieldStart, fieldLength); | ||
} | ||
} | ||
@@ -208,2 +154,20 @@ return objectView; | ||
/** | ||
* Returns an Object corresponding to a given view. | ||
* | ||
* @param {View} view | ||
* @param {number} [start=0] | ||
* @returns {Object} | ||
*/ | ||
static toJSON(view, start = 0) { | ||
const { fields, schema } = this; | ||
const result = {}; | ||
for (let i = 0; i < fields.length; i++) { | ||
const name = fields[i]; | ||
const { View, start: fieldStart, length: fieldLength } = schema[name]; | ||
result[name] = View.toJSON(view, start + fieldStart, fieldLength); | ||
} | ||
return result; | ||
} | ||
/** | ||
* @private | ||
@@ -210,0 +174,0 @@ * @returns {void} |
@@ -315,12 +315,17 @@ /** | ||
* @param {ArrayLike<number>|string} arrayLike | ||
* @param {Function|Uint8Array} [mapFn] | ||
* @param {Object} [thisArg] | ||
* @param {Function|Uint8Array|View} [mapFn] | ||
* @param {Object|number} [thisArg] | ||
* @param {number} [length] | ||
* @returns {Uint8Array|StringView} | ||
*/ | ||
static from(arrayLike, mapFn, thisArg) { | ||
static from(arrayLike, mapFn, thisArg, length) { | ||
// pass to builtin from if invoked on an array instead of string | ||
if (arrayLike && typeof arrayLike !== 'string') return super.from(arrayLike, mapFn, thisArg); | ||
// no view is supplied | ||
if (!mapFn) return new this(this.encoder.encode(arrayLike).buffer); | ||
mapFn.fill(0); | ||
const array = new Uint8Array(mapFn.buffer, | ||
mapFn.byteOffset + thisArg, length || mapFn.byteLength); | ||
array.fill(0); | ||
// fix for Node.js 12.11, todo remove when encodeInto fixed in Node.js | ||
if (arrayLike) this.encoder.encodeInto(arrayLike, mapFn); | ||
if (arrayLike) this.encoder.encodeInto(arrayLike, array); | ||
return mapFn; | ||
@@ -330,2 +335,14 @@ } | ||
/** | ||
* Returns a string representation of a given view. | ||
* | ||
* @param {View} view | ||
* @param {number} [start=0] | ||
* @param {length} [length] | ||
* @returns {Array<number>} | ||
*/ | ||
static toJSON(view, start = 0, length) { | ||
return new this(view.buffer, view.byteOffset + start, length).toString(); | ||
} | ||
/** | ||
* Returns the size in bytes of a given string without encoding it. | ||
@@ -332,0 +349,0 @@ * |
@@ -29,3 +29,3 @@ const { typeGetters, typeSetters, typeOffsets } = require('./utilities'); | ||
get() { | ||
return this.constructor.get(0, this); | ||
return this.constructor.toJSON(this); | ||
} | ||
@@ -37,6 +37,6 @@ | ||
* @param {number} value | ||
* @returns {number} | ||
* @returns {TypeView} | ||
*/ | ||
set(value) { | ||
this.constructor.set(0, value, this); | ||
this.constructor.from(value, this); | ||
return this; | ||
@@ -51,3 +51,3 @@ } | ||
toJSON() { | ||
return this.get(); | ||
return this.constructor.toJSON(this); | ||
} | ||
@@ -68,8 +68,9 @@ | ||
* @param {number} value | ||
* @param {TypeView} view | ||
* @returns {TypeView} | ||
* @param {View} view | ||
* @param {number} [start=0] | ||
* @returns {View} | ||
*/ | ||
static from(value, view) { | ||
static from(value, view, start = 0) { | ||
const typeView = view || this.of(); | ||
typeView.set(value); | ||
setter.call(typeView, start, value, this.littleEndian); | ||
return typeView; | ||
@@ -90,20 +91,9 @@ } | ||
* | ||
* @param {number} position | ||
* @param {TypeView} view | ||
* @param {View} view | ||
* @param {number} [start=0] | ||
* @returns {number} | ||
*/ | ||
static get(position, view) { | ||
return view[getter](position, this.littleEndian); | ||
} | ||
/** | ||
* Sets the numerical value to a given view. | ||
* | ||
* @param {number} position | ||
* @param {number} value | ||
* @param {TypeView} view | ||
* @returns {void} | ||
*/ | ||
static set(position, value, view) { | ||
view[setter](position, value, this.littleEndian); | ||
static toJSON(view, start = 0) { | ||
return getter.call(view, start, this.littleEndian); | ||
} | ||
@@ -110,0 +100,0 @@ } |
@@ -15,3 +15,3 @@ /** | ||
const { View } = this.constructor; | ||
return View.get(index << View.offset, this); | ||
return View.toJSON(this, index << View.offset); | ||
} | ||
@@ -28,3 +28,3 @@ | ||
const { View } = this.constructor; | ||
View.set(index << View.offset, value, this); | ||
View.from(value, this, index << View.offset); | ||
return this; | ||
@@ -61,3 +61,3 @@ } | ||
toJSON() { | ||
return [...this]; | ||
return this.constructor.toJSON(this, 0, this.byteLength); | ||
} | ||
@@ -79,14 +79,14 @@ | ||
* @param {ArrayLike<number>} value | ||
* @param {TypedArrayView} [array] | ||
* @param {View} [array] | ||
* @param {number} [start=0] | ||
* @param {number} [length] | ||
* @returns {TypedArrayView} | ||
*/ | ||
static from(value, array) { | ||
static from(value, array, start = 0, length = this.getLength(value.length)) { | ||
const view = array || this.of(value.length); | ||
if (array) { | ||
new Uint8Array(array.buffer, array.byteOffset, array.byteLength) | ||
.fill(0); | ||
} | ||
const { size } = view; | ||
new Uint8Array(view.buffer, view.byteOffset + start, length).fill(0); | ||
const { View } = this; | ||
const size = length >> View.offset; | ||
for (let i = 0; i < size; i++) { | ||
view.set(i, value[i]); | ||
View.from(value[i], view, start + (i << View.offset)); | ||
} | ||
@@ -97,2 +97,20 @@ return view; | ||
/** | ||
* Returns an array representation of a given view. | ||
* | ||
* @param {View} view | ||
* @param {number} [start=0] | ||
* @param {length} [length] | ||
* @returns {Array<number>} | ||
*/ | ||
static toJSON(view, start = 0, length) { | ||
const { View } = this; | ||
const size = length >> View.offset; | ||
const array = new Array(size); | ||
for (let i = 0; i < size; i++) { | ||
array[i] = View.toJSON(view, start + (i << View.offset)); | ||
} | ||
return array; | ||
} | ||
/** | ||
* Creates an empty array view of specified size. | ||
@@ -99,0 +117,0 @@ * |
@@ -87,25 +87,25 @@ const BigInt = (global || window).BigInt || Number; | ||
const typeGetters = { | ||
int8: 'getInt8', | ||
uint8: 'getUint8', | ||
int16: 'getInt16', | ||
uint16: 'getUint16', | ||
int32: 'getInt32', | ||
uint32: 'getUint32', | ||
float32: 'getFloat32', | ||
float64: 'getFloat64', | ||
bigint64: 'getBigInt64', | ||
biguint64: 'getBigUint64', | ||
int8: DataView.prototype.getInt8, | ||
uint8: DataView.prototype.getUint8, | ||
int16: DataView.prototype.getInt16, | ||
uint16: DataView.prototype.getUint16, | ||
int32: DataView.prototype.getInt32, | ||
uint32: DataView.prototype.getUint32, | ||
float32: DataView.prototype.getFloat32, | ||
float64: DataView.prototype.getFloat64, | ||
bigint64: DataView.prototype.getBigInt64, | ||
biguint64: DataView.prototype.getBigUint64, | ||
}; | ||
const typeSetters = { | ||
int8: 'setInt8', | ||
uint8: 'setUint8', | ||
int16: 'setInt16', | ||
uint16: 'setUint16', | ||
int32: 'setInt32', | ||
uint32: 'setUint32', | ||
float32: 'setFloat32', | ||
float64: 'setFloat64', | ||
bigint64: 'setBigInt64', | ||
biguint64: 'setBigUint64', | ||
int8: DataView.prototype.setInt8, | ||
uint8: DataView.prototype.setUint8, | ||
int16: DataView.prototype.setInt16, | ||
uint16: DataView.prototype.setUint16, | ||
int32: DataView.prototype.setInt32, | ||
uint32: DataView.prototype.setUint32, | ||
float32: DataView.prototype.setFloat32, | ||
float64: DataView.prototype.setFloat64, | ||
bigint64: DataView.prototype.setBigInt64, | ||
biguint64: DataView.prototype.setBigUint64, | ||
}; | ||
@@ -112,0 +112,0 @@ |
{ | ||
"name": "structurae", | ||
"version": "2.2.0", | ||
"version": "2.3.0", | ||
"description": "Data structures for performance-sensitive modern JavaScript applications.", | ||
@@ -47,3 +47,3 @@ "main": "index.js", | ||
"devDependencies": { | ||
"@types/jest": "^25.1.2", | ||
"@types/jest": "^25.1.4", | ||
"benchmark": "^2.1.4", | ||
@@ -50,0 +50,0 @@ "eslint": "^6.8.0", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
207520
5653