structurae
Advanced tools
Comparing version 1.1.1 to 1.2.0
@@ -7,5 +7,9 @@ # Changelog | ||
## [1.2.0] - 2019-06-10 | ||
### Added | ||
- Support TypedArray fields in RecordArray | ||
## [1.1.1] - 2019-06-09 | ||
### Added | ||
- RecordArray#fromObject | ||
- RecordArray#fromObject ([b263...](https://github.com/zandaqo/structurae/commit/b263dc7503343a9114233e0a0ce027d6504d97cf)) | ||
@@ -12,0 +16,0 @@ ## [1.1.0] - 2019-06-07 |
@@ -181,2 +181,4 @@ // Type definitions for structurae | ||
littleEndian?: boolean; | ||
start?: number; | ||
end?: number; | ||
} | ||
@@ -190,14 +192,17 @@ | ||
size: number; | ||
byteView: StringView; | ||
private fields: RecordField[]; | ||
private schema: RecordSchema; | ||
private offset: number; | ||
private offsets: object; | ||
private stringView: StringView; | ||
constructor(fields: RecordField[], size: number, buffer?: ArrayBuffer, byteOffset?: number, byteLength?: number); | ||
get(index: number, field: string): any; | ||
getArray(offset: number, size: number, type: string): StringView; | ||
getString(offset: number, size: number): StringView; | ||
set(index: number, field: string, value: any, littleEndian?: boolean): this; | ||
getString(offset: number, littleEndian: boolean, size: number): StringView; | ||
setString(offset: number, value: Collection): void; | ||
setArray(offset: number, value: Collection, size: number, type: string): void; | ||
setString(offset: number, value: Collection, size: number): void; | ||
toObject(index: number): object; | ||
fromObject(index: number, object: object): this; | ||
static getLength(fields: RecordField[], size: number): number; | ||
} | ||
@@ -204,0 +209,0 @@ |
@@ -0,1 +1,3 @@ | ||
const utilities = require('./utilities'); | ||
/** | ||
@@ -20,3 +22,3 @@ * @typedef {('Int8' | 'Uint8' | 'Int16' | 'Uint16' | ||
*/ | ||
const FieldSizes = { | ||
const fieldSizes = { | ||
Int8: 1, | ||
@@ -32,5 +34,34 @@ Uint8: 1, | ||
BigUint64: 8, | ||
String: 0, | ||
Int8Array: 0, | ||
Uint8Array: 0, | ||
Uint8ClampedArray: 0, | ||
Int16Array: 0, | ||
Uint16Array: 0, | ||
Int32Array: 0, | ||
Uint32Array: 0, | ||
Float32Array: 0, | ||
Float64Array: 0, | ||
BigInt64Array: 0, | ||
BigUint64Array: 0, | ||
}; | ||
/** | ||
* @private | ||
*/ | ||
const typedArrays = { | ||
Int8Array, | ||
Uint8Array, | ||
Uint8ClampedArray, | ||
Int16Array, | ||
Uint16Array, | ||
Int32Array, | ||
Uint32Array, | ||
Float32Array, | ||
Float64Array, | ||
BigInt64Array, | ||
BigUint64Array, | ||
}; | ||
/** | ||
* Extends DataView to use ArrayBuffer as an array of records or C-like structs. | ||
@@ -60,30 +91,19 @@ * | ||
constructor(fields, size = 1, buffer, byteOffset, byteLength) { | ||
const offsets = {}; | ||
const lastField = fields[fields.length - 1]; | ||
if (!lastField.end) new.target.initialize(fields); | ||
const lastOffset = lastField.end; | ||
const offset = Math.ceil(Math.log2(lastOffset)); | ||
const data = buffer || new ArrayBuffer(size << offset); | ||
super(data, byteOffset, byteLength); | ||
const schema = {}; | ||
let lastOffset = 0; | ||
let hasString = false; | ||
for (let i = 0; i < fields.length; i++) { | ||
const field = fields[i]; | ||
if (field.type === 'String') { | ||
hasString = true; | ||
} else if (!Reflect.has(FieldSizes, field.type)) { | ||
throw TypeError(`Type "${field.type}" is not a valid type.`); | ||
} | ||
offsets[field.name] = lastOffset; | ||
schema[field.name] = field; | ||
lastOffset += (field.size || FieldSizes[field.type]); | ||
schema[fields[i].name] = fields[i]; | ||
} | ||
const offset = Math.ceil(Math.log2(lastOffset)); | ||
const length = size << offset; | ||
const data = buffer || new ArrayBuffer(length); | ||
super(data, byteOffset, byteLength); | ||
const stringView = hasString ? new StringView(this.buffer) : undefined; | ||
Object.defineProperties(this, { | ||
fields: { value: fields }, | ||
offset: { value: offset }, | ||
offsets: { value: offsets }, | ||
schema: { value: schema }, | ||
stringView: { value: stringView }, | ||
byteView: { value: new StringView(this.buffer) }, | ||
}); | ||
@@ -130,4 +150,6 @@ } | ||
return this.getBigUint64(offset, littleEndian); | ||
case 'String': | ||
return this.getString(offset, size); | ||
default: | ||
return this.getString(offset, size); | ||
return this.getArray(offset, size, type); | ||
} | ||
@@ -137,2 +159,21 @@ } | ||
/** | ||
* @param {number} offset | ||
* @param {number} size | ||
* @param {string} type | ||
* @returns {RecordArray} | ||
*/ | ||
getArray(offset, size, type) { | ||
return new typedArrays[type](this.buffer, offset, size); | ||
} | ||
/** | ||
* @param {number} offset | ||
* @param {number} size | ||
* @returns {StringView} | ||
*/ | ||
getString(offset, size) { | ||
return this.byteView.subarray(offset, offset + size); | ||
} | ||
/** | ||
* Sets a value to a field of a record at a given index. | ||
@@ -188,5 +229,8 @@ * | ||
break; | ||
default: | ||
case 'String': | ||
this.setString(offset, value, size); | ||
break; | ||
default: | ||
this.setArray(offset, value, size, type); | ||
break; | ||
} | ||
@@ -198,7 +242,12 @@ return this; | ||
* @param {number} offset | ||
* @param {ArrayLike} value | ||
* @param {number} size | ||
* @returns {StringView} | ||
* @param {string} type | ||
* @returns {RecordArray} | ||
*/ | ||
getString(offset, size) { | ||
return this.stringView.subarray(offset, offset + size); | ||
setArray(offset, value, size, type) { | ||
const array = new typedArrays[type](this.buffer, offset, size); | ||
if (value.length < array.length) array.fill(0); | ||
array.set(value); | ||
return this; | ||
} | ||
@@ -214,5 +263,5 @@ | ||
if (value.length === size) { | ||
this.stringView.set(value, offset); | ||
this.byteView.set(value, offset); | ||
} else { | ||
this.stringView.subarray(offset, offset + size) | ||
this.byteView.subarray(offset, offset + size) | ||
.fill(0) | ||
@@ -246,3 +295,3 @@ .set(value); | ||
getByteOffset(index, field) { | ||
return (index << this.offset) + this.offsets[field]; | ||
return (index << this.offset) + this.schema[field].start; | ||
} | ||
@@ -306,8 +355,31 @@ | ||
static getLength(fields, size) { | ||
const lastField = fields[fields.length - 1]; | ||
if (!lastField.end) this.initialize(fields); | ||
return size << Math.ceil(Math.log2(lastField.end)); | ||
} | ||
/** | ||
* @private | ||
* @param {Array} fields | ||
* @returns {void} | ||
*/ | ||
static initialize(fields) { | ||
let lastOffset = 0; | ||
for (let i = 0; i < fields.length; i++) { | ||
const field = fields[i]; | ||
lastOffset += (field.size || FieldSizes[field.type]); | ||
const { type } = field; | ||
let fieldSize = field.size; | ||
if (!Reflect.has(fieldSizes, type)) { | ||
throw TypeError(`Type "${type}" is not a valid type.`); | ||
} | ||
const isArray = Reflect.has(typedArrays, type); | ||
if (isArray) { | ||
const bytesPerElement = typedArrays[type].BYTES_PER_ELEMENT; | ||
lastOffset = utilities.getGTEMultiple(lastOffset, bytesPerElement); | ||
fieldSize *= bytesPerElement; | ||
} | ||
field.start = lastOffset; | ||
lastOffset += (fieldSize || fieldSizes[type]); | ||
field.end = lastOffset; | ||
} | ||
return size << Math.ceil(Math.log2(lastOffset)); | ||
} | ||
@@ -314,0 +386,0 @@ } |
@@ -37,2 +37,4 @@ const log2 = { | ||
/** | ||
* Counts set bits in a given number. | ||
* | ||
* @param {number} value | ||
@@ -48,2 +50,4 @@ * @returns {number} | ||
/** | ||
* Returns the index of the Least Significant Bit in a number. | ||
* | ||
* @param {number} value | ||
@@ -57,2 +61,13 @@ * @returns {number} | ||
/** | ||
* Returns a multiple of a base number that is greater or equal to a given value. | ||
* | ||
* @param {number} value | ||
* @param {number} base | ||
* @returns {number} | ||
*/ | ||
function getGTEMultiple(value, base) { | ||
return ((value - 1) | (base - 1)) + 1; | ||
} | ||
module.exports = { | ||
@@ -62,2 +77,3 @@ log2, | ||
getLSBIndex, | ||
getGTEMultiple, | ||
}; |
{ | ||
"name": "structurae", | ||
"version": "1.1.1", | ||
"version": "1.2.0", | ||
"description": "Data structures for performance-sensitive modern JavaScript applications.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -514,4 +514,5 @@ # Structurae | ||
RecordArray extends DataView to use ArrayBuffer as an array of records or C-like structs. | ||
Records can contain fields of any type supported by DataView plus strings. | ||
For a string, the maximum size in bytes should be defined. | ||
Records can contain fields of any type supported by DataView, strings, and TypedArrays. | ||
For a string, the maximum size in bytes should be defined, for typed array fields size property specifies | ||
the length of the array. | ||
@@ -552,2 +553,19 @@ ```javascript | ||
Getting a TypedArray field will return a new TypedArray that uses the same buffer as the RecordArray, | ||
that is, any change made on the new TypedArray will reflect in the RecordArray and vice versa: | ||
```javascript | ||
const people = new RecordArray([ | ||
{ name: 'age', type: 'Uint8' }, | ||
{ name: 'scores', type: 'Float32Array', size: 2 }, | ||
], 10); | ||
const scores = people.get(0, 'scores'); | ||
//=> Float32Array [0, 0]; | ||
scores[1] = 1.2; | ||
people.get(0, 'scores'); | ||
//=> Float32Array [0, 1.2] | ||
people.set(0, 'scores', [2, 3]); | ||
scores[0]; | ||
//=> 2 | ||
``` | ||
### Sorted Structures | ||
@@ -554,0 +572,0 @@ #### BinaryHeap |
161500
4457
754