structurae
Advanced tools
Comparing version
@@ -7,2 +7,6 @@ # Changelog | ||
## [3.1.0] - 2020-05-28 | ||
### Added | ||
- Support setting maximum size for strings and arrays in MapView | ||
## [3.0.6] - 2020-04-28 | ||
@@ -9,0 +13,0 @@ ### Changed |
@@ -411,8 +411,3 @@ // Type definitions for structurae | ||
trim(): StringView; | ||
static from( | ||
arrayLike: ArrayLike<number> | string, | ||
mapFn?: Function | View, | ||
thisArg?: any, | ||
length?: number, | ||
): View; | ||
static from(...args: any[]): View; | ||
static toJSON(view: View, start?: number, length?: number): string; | ||
@@ -428,2 +423,4 @@ static getByteSize(string: string): number; | ||
static Views: ViewTypes; | ||
static maxLength: number; | ||
static maxView: DataView; | ||
@@ -438,3 +435,3 @@ get(field: string): any; | ||
static toJSON(view: View, start?: number): object; | ||
static getLength(value: any, getOffsets?: boolean): number | [number, number[]]; | ||
static getLength(value: any): number; | ||
static initialize(): void; | ||
@@ -441,0 +438,0 @@ } |
@@ -98,15 +98,21 @@ const { ObjectView, ObjectViewMixin } = require('./object-view'); | ||
const fieldCount = fields.length; | ||
const [length, offsets] = this.getLength(value, true); | ||
const view = new this(new ArrayBuffer(length)); | ||
let offset = (fieldCount + 1) << 2; | ||
const view = this.bufferView; | ||
view.setUint32(0, offset, true); | ||
for (let i = 0; i < fieldCount; i++) { | ||
view.setUint32(i << 2, offsets[i], true); | ||
const start = offsets[i]; | ||
const end = offsets[i + 1]; | ||
if (start === end) continue; | ||
const field = fields[i]; | ||
const { View } = layout[field]; | ||
View.from(value[field], view, start, end - start); | ||
const fieldValue = value[field]; | ||
if (fieldValue != null) { | ||
const { View, length } = layout[field]; | ||
const start = offset; | ||
const valueLength = | ||
typeof fieldValue === 'string' | ||
? StringView.getByteSize(fieldValue) | ||
: View.getLength(fieldValue.length || 1); | ||
offset += Math.min(valueLength, length); | ||
View.from(fieldValue, view, start, offset - start); | ||
} | ||
view.setUint32((i + 1) << 2, offset, true); | ||
} | ||
view.setUint32(fieldCount << 2, offsets[fieldCount], true); | ||
return view; | ||
return new this(view.buffer.slice(0, offset)); | ||
} | ||
@@ -118,13 +124,10 @@ | ||
* @param {Object} value | ||
* @param {boolean} [getOffsets] | ||
* @returns {number} | ||
*/ | ||
static getLength(value, getOffsets) { | ||
static getLength(value) { | ||
const { layout, fields } = this; | ||
const fieldCount = fields.length; | ||
let length = (fieldCount + 1) << 2; | ||
const offsets = new Array(fieldCount + 1).fill(0); | ||
for (let i = 0; i < fieldCount; i++) { | ||
const field = fields[i]; | ||
offsets[i] = length; | ||
if (value[field] == null) continue; | ||
@@ -138,4 +141,3 @@ const { View } = layout[field]; | ||
} | ||
offsets[fieldCount] = length; | ||
return !getOffsets ? length : [length, offsets]; | ||
return length; | ||
} | ||
@@ -184,4 +186,6 @@ | ||
let View; | ||
let length = Infinity; | ||
if (field.type !== 'array') { | ||
View = ObjectViewClass.getViewFromSchema(field); | ||
length = field.type === 'string' ? field.maxLength : View.getLength(); | ||
} else { | ||
@@ -199,4 +203,5 @@ const sizes = []; | ||
} | ||
length = itemLength; | ||
} | ||
layout[property] = { View, start: i }; | ||
layout[property] = { View, start: i, length: length || Infinity }; | ||
} | ||
@@ -206,2 +211,7 @@ this.layout = layout; | ||
} | ||
static get bufferView() { | ||
if (!this.maxView) this.maxView = new DataView(new ArrayBuffer(this.maxLength)); | ||
return this.maxView; | ||
} | ||
} | ||
@@ -235,2 +245,13 @@ | ||
/** | ||
* @type {number} Maximum possible size of a map. | ||
*/ | ||
MapView.maxLength = 8192; | ||
/** | ||
* @protected | ||
* @type {DataView} | ||
*/ | ||
MapView.maxView = undefined; | ||
/** | ||
* Creates a MapView class with a given schema. | ||
@@ -237,0 +258,0 @@ * |
@@ -137,9 +137,7 @@ /** | ||
* @private | ||
* @param {*} arrayLike an array-like object to convert to a SortedCollection | ||
* @param {Function} mapFn a map function to call on every element of the array | ||
* @param {Object} thisArg the value to use as `this` when invoking the `mapFn` | ||
* @param {...*} args | ||
* @returns {SortedCollection} a new SortedCollection | ||
*/ | ||
static from(arrayLike, mapFn, thisArg) { | ||
const result = super.from(arrayLike, mapFn, thisArg); | ||
static from(...args) { | ||
const result = super.from(...args); | ||
result.sort(); | ||
@@ -146,0 +144,0 @@ return result; |
@@ -325,23 +325,16 @@ const ArrayView = require('./array-view'); | ||
* Creates a StringView from a string or an array like object. | ||
* | ||
* @param {ArrayLike<number>|string} arrayLike | ||
* @param {Function|Uint8Array|View} [mapFn] | ||
* @param {Object|number} [thisArg] | ||
* @param {number} [length] | ||
* @param {...*} args | ||
* @returns {Uint8Array|StringView} | ||
*/ | ||
static from(arrayLike, mapFn, thisArg, length) { | ||
static from(...args) { | ||
// pass to builtin from if invoked on an array instead of string | ||
if (arrayLike && typeof arrayLike !== 'string') return super.from(arrayLike, mapFn, thisArg); | ||
if (args[0] && typeof args[0] !== 'string') return super.from(...args); | ||
const [value, view, start, length] = args; | ||
// no view is supplied | ||
if (!mapFn) return new this(stringToUTF8(arrayLike)); | ||
const array = new Uint8Array( | ||
mapFn.buffer, | ||
mapFn.byteOffset + thisArg, | ||
length || mapFn.byteLength, | ||
); | ||
if (!view) return new this(stringToUTF8(value)); | ||
const array = new Uint8Array(view.buffer, view.byteOffset + start, length || view.byteLength); | ||
array.fill(0); | ||
// fix for Node.js 12.11, todo remove when encodeInto fixed in Node.js | ||
if (arrayLike) stringToUTF8(arrayLike, array); | ||
return mapFn; | ||
stringToUTF8(value, array); | ||
return view; | ||
} | ||
@@ -348,0 +341,0 @@ |
{ | ||
"name": "structurae", | ||
"version": "3.0.6", | ||
"version": "3.1.0", | ||
"description": "Data structures for performance-sensitive modern JavaScript applications.", | ||
@@ -48,9 +48,9 @@ "main": "index.js", | ||
"devDependencies": { | ||
"@types/jest": "^25.2.1", | ||
"@types/jest": "^25.2.3", | ||
"benchmark": "^2.1.4", | ||
"eslint": "^6.8.0", | ||
"eslint-config-airbnb-base": "^14.1.0", | ||
"eslint-config-prettier": "^6.10.1", | ||
"eslint-config-prettier": "^6.11.0", | ||
"eslint-plugin-import": "^2.20.2", | ||
"jest": "^25.2.7", | ||
"jest": "^26.0.1", | ||
"jsdoc-to-markdown": "^5.0.3", | ||
@@ -101,7 +101,7 @@ "json-schema-faker": "^0.5.0-rcv.24" | ||
"rules": { | ||
"max-classes-per-file": 1, | ||
"max-classes-per-file": 0, | ||
"no-bitwise": 0, | ||
"no-plusplus": 0, | ||
"no-continue": 0, | ||
"no-restricted-syntax": 1, | ||
"no-restricted-syntax": 0, | ||
"no-nested-ternary": 1, | ||
@@ -108,0 +108,0 @@ "no-labels": 1, |
@@ -302,6 +302,9 @@ # Structurae | ||
id: { type: 'integer', btype: 'uint32' }, | ||
name: { type: 'string' }, // notice that maxLength is not required in MapView | ||
// notice that maxLength is not required in MapView | ||
// however, if set, MapView with truncate longer strings to fit the maxLength | ||
name: { type: 'string' }, | ||
pets: { | ||
type: 'array', | ||
// maxItems is also not required for MapView | ||
// if set, MapView truncate arrays exceeding the specified maximum | ||
items: { | ||
@@ -335,2 +338,5 @@ $id: 'Pet', | ||
For performance reasons, MapView uses a single buffer for serialization, thus, limiting the maximum size of a view. | ||
By default the size is 8192 bytes, if you expect bigger views, please set the desired size in `MapView.maxLength`. | ||
#### StringView | ||
@@ -337,0 +343,0 @@ Encoding API (available both in modern browsers and Node.js) allows us to convert JavaScript strings to |
220028
0.16%6045
0.08%1070
0.56%