structurae
Advanced tools
Comparing version 3.3.0 to 4.0.0-pre.1
731
index.d.ts
@@ -1,702 +0,29 @@ | ||
// Type definitions for structurae | ||
// Project: structurae | ||
// Definitions by: Maga D. Zandaqo <denelxan@gmail.com> (http://maga.name) | ||
type TypedArray = | ||
| Int8Array | ||
| Uint8Array | ||
| Uint8ClampedArray | ||
| Int16Array | ||
| Uint16Array | ||
| Int32Array | ||
| Uint32Array | ||
| Float32Array | ||
| Float64Array; | ||
type TypedArrayConstructor = | ||
| Int8ArrayConstructor | ||
| Uint8ArrayConstructor | ||
| Uint8ClampedArrayConstructor | ||
| Int16Array | ||
| Uint16ArrayConstructor | ||
| Int32ArrayConstructor | ||
| Uint32ArrayConstructor | ||
| Float32ArrayConstructor | ||
| Float64ArrayConstructor; | ||
type Collection = any[] | TypedArray; | ||
type CollectionConstructor = ArrayConstructor | TypedArrayConstructor; | ||
interface Constructor<T> { | ||
new (...args): T; | ||
} | ||
interface GridOptions { | ||
rows: number; | ||
columns: number; | ||
pad?: any; | ||
} | ||
interface Coordinates { | ||
row: number; | ||
column: number; | ||
} | ||
declare class Grid { | ||
columns: number; | ||
rows: number; | ||
offset: number; | ||
pad: any; | ||
lastCoordinates: Coordinates; | ||
constructor(options?: GridOptions, ...args: any); | ||
get(row: number, column: number): any; | ||
set(row: number | ArrayLike<number>, column?: number, value?: any): this; | ||
setArray(array: Collection, offset: number): void; | ||
getCoordinates(index: number): Coordinates; | ||
toArrays(withPadding?: boolean): any[][]; | ||
static getOffset(columns: number): number; | ||
static getLength(rows: number, columns: number): number; | ||
static fromArrays(arrays: any[][], pad: any): Grid; | ||
} | ||
export declare function GridMixin<T extends Collection>( | ||
Base?: Constructor<T>, | ||
): Constructor<T & Grid>; | ||
interface BitPosition { | ||
bucket: number; | ||
position: number; | ||
} | ||
type Bit = 0 | 1; | ||
interface BinaryGridOptions { | ||
rows: number; | ||
columns: number; | ||
} | ||
export declare class BinaryGrid extends Uint16Array { | ||
offset: number; | ||
columns: number; | ||
rows: number; | ||
lastPosition: BitPosition; | ||
constructor(options: BinaryGridOptions, ...args: any); | ||
get(row: number, column: number): Bit; | ||
set(row: number | ArrayLike<number>, column?: number, value?: Bit): this; | ||
private getBitPosition(row: number, column: number): BitPosition; | ||
static getLength(rows: number, columns: number): number; | ||
static getOffset(columns: number): number; | ||
} | ||
declare class SymmetricGrid { | ||
rows: number; | ||
columns: number; | ||
pad: any; | ||
lastCoordinates: Coordinates; | ||
constructor(options?: GridOptions, ...args: any); | ||
get(row: number, column: number): any; | ||
set(row: number | ArrayLike<number>, column?: number, value?: any): this; | ||
setArray(array: Collection, offset: number): void; | ||
getCoordinates(index: number): Coordinates; | ||
toArrays(withPadding?: boolean): any[][]; | ||
static getLength(rows: number, columns: number): number; | ||
static fromArrays(arrays: any[][], pad: any): SymmetricGrid; | ||
} | ||
export declare function SymmetricGridMixin<T extends Collection>( | ||
Base?: Constructor<T>, | ||
): Constructor<T & SymmetricGrid>; | ||
type GridStructure = Grid | BinaryGrid | SymmetricGrid; | ||
type Matcher = [number, number]; | ||
interface NumberMap { | ||
[propName: string]: number; | ||
} | ||
export declare class BitField { | ||
value: number; | ||
static schema: NumberMap; | ||
static size: number; | ||
static isInitialized: boolean; | ||
private static fields: string[]; | ||
private static masks: NumberMap; | ||
private static offsets: NumberMap; | ||
private static mask: number; | ||
constructor(data?: number | BitField | number[] | NumberMap); | ||
get(field: string): number; | ||
set(field: string, value: number); | ||
has(...fields: string[]): boolean; | ||
match(matcher: Matcher | NumberMap): boolean; | ||
toJSON(): number; | ||
toObject(): NumberMap; | ||
toString(): string; | ||
valueOf(): number; | ||
static initialize(): void; | ||
static encode(data: number[] | NumberMap): number; | ||
static decode(data: number): NumberMap; | ||
static isValid(data: NumberMap): boolean; | ||
static getMinSize(number: number): number; | ||
static getMatcher(matcher: NumberMap): Matcher; | ||
static match(value: number, matcher: Matcher): boolean; | ||
} | ||
type BigIntMatcher = [number, number]; | ||
interface BigIntMap { | ||
[propName: string]: bigint; | ||
} | ||
export declare class BigBitField { | ||
value: bigint; | ||
static schema: BigIntMap; | ||
static size: bigint; | ||
static isInitialized: boolean; | ||
private static fields: string[]; | ||
private static masks: BigIntMap; | ||
private static offsets: BigIntMap; | ||
private static mask: bigint; | ||
constructor(data?: bigint | BigBitField | number[] | NumberMap); | ||
get(field: string): number; | ||
set(field: string, value: number); | ||
has(...fields: string[]): boolean; | ||
match(matcher: BigIntMatcher | NumberMap): boolean; | ||
toJSON(): bigint; | ||
toObject(): NumberMap; | ||
toString(): string; | ||
valueOf(): bigint; | ||
static initialize(): void; | ||
static encode(data: number[] | NumberMap): number; | ||
static decode(data: number): NumberMap; | ||
static isValid(data: NumberMap): boolean; | ||
static getMinSize(number: number): number; | ||
static getMatcher(matcher: NumberMap): BigIntMatcher; | ||
static match(value: bigint, matcher: BigIntMatcher): boolean; | ||
} | ||
export declare function BitFieldMixin( | ||
schema: string[] | NumberMap, | ||
BitFieldClass?: typeof BitField | typeof BigBitField, | ||
): typeof BitField | typeof BitFieldClass; | ||
type CompareResult = 1 | -1 | 0; | ||
interface Comparator { | ||
(a: any, b: any): CompareResult; | ||
} | ||
declare class SortedCollection { | ||
isSorted(): boolean; | ||
isUnique(): boolean; | ||
range(start: number, end: number, subarray?: boolean): SortedCollection; | ||
rank(element: any): number; | ||
static compare(a: any, b: any): CompareResult; | ||
static getDifference<T extends Collection>( | ||
a: Collection, | ||
b: Collection, | ||
symmetric?: boolean, | ||
comparator?: Comparator, | ||
container?: T, | ||
): T; | ||
static getDifferenceScore( | ||
a: Collection, | ||
b: Collection, | ||
symmetric?: boolean, | ||
comparator?: Comparator, | ||
): number; | ||
static getIndex( | ||
arr: Collection, | ||
target: any, | ||
comparator?: Comparator, | ||
rank?: boolean, | ||
start?: number, | ||
end?: number, | ||
): number; | ||
static getIntersection<T extends Collection>( | ||
a: Collection, | ||
b: Collection, | ||
comparator?: Comparator, | ||
container?: T, | ||
): T; | ||
static getIntersectionScore(a: Collection, b: Collection, comparator?: Comparator): number; | ||
static getRange<T extends Collection>( | ||
arr: T, | ||
start?: number, | ||
end?: number, | ||
comparator?: Comparator, | ||
subarray?: boolean, | ||
): T; | ||
static getUnion<T extends Collection>( | ||
a: Collection, | ||
b: Collection, | ||
unique?: boolean, | ||
comparator?: Comparator, | ||
container?: T, | ||
): T; | ||
static getUnique<T extends Collection>( | ||
arr: Collection, | ||
comparator?: Comparator, | ||
container?: T, | ||
): T; | ||
static isSorted(arr: Collection, comparator?: Comparator): boolean; | ||
static isUnique(arr: Collection, comparator?: Comparator): boolean; | ||
} | ||
export declare function SortedMixin<T extends Collection>( | ||
Base?: Constructor<T>, | ||
): Constructor<T & SortedCollection>; | ||
export class SortedArray extends SortedMixin(Array) { | ||
unique: boolean; | ||
set(arr: Collection): this; | ||
uniquify(): this; | ||
} | ||
type PrimitiveFieldType = | ||
| 'int8' | ||
| 'uint8' | ||
| 'int16' | ||
| 'uint16' | ||
| 'int32' | ||
| 'uint32' | ||
| 'float32' | ||
| 'float64' | ||
| 'bigint64' | ||
| 'biguint64'; | ||
type ViewType = typeof ArrayView | typeof ObjectView | typeof StringView | typeof TypeView; | ||
type View = ObjectView | ArrayView | StringView | TypeView; | ||
export declare class TypeView extends DataView { | ||
static offset: number; | ||
static littleEndian: true; | ||
static viewLength: number; | ||
static Views: Map<string, typeof TypeView>; | ||
static ArrayClass: typeof ArrayView; | ||
get(): number; | ||
set(value: number): this; | ||
toJSON(): number; | ||
static getLength(): number; | ||
static from(value: any, view?: View, start?: number): View; | ||
static toJSON(view: View, start?: number): any; | ||
static of(): TypeView; | ||
} | ||
export declare class BooleanView extends TypeView { | ||
static from(value: number | boolean, view?: View, start?: number): View; | ||
static toJSON(view: View, start?: number): boolean; | ||
} | ||
export declare function TypeViewMixin( | ||
type: PrimitiveFieldType, | ||
littleEndian?: boolean, | ||
TypeViewClass?: typeof TypeView, | ||
): typeof TypeView; | ||
export declare class ArrayView extends DataView { | ||
size: number; | ||
static itemLength: number; | ||
static View: ViewType; | ||
static ArrayClass: typeof ArrayView; | ||
get(index: number): any; | ||
getView(index: number): View; | ||
set(index: number, value: any): this; | ||
setView(index: number, value: View): this; | ||
toJSON(): any[]; | ||
[Symbol.iterator](): IterableIterator<View | number>; | ||
static from(value: ArrayLike<any>, array?: View, start?: number, length?: number): View; | ||
static toJSON(view: View, start: number, length: number): any[]; | ||
static of(size?: number): ArrayView; | ||
static getLength(size: number): number; | ||
static getOffset(size: number): number; | ||
static getSize(length: number): number; | ||
} | ||
export declare class TypedArrayView extends ArrayView { | ||
static View: typeof TypeView; | ||
get(index: number): number; | ||
set(index: number, value: number): this; | ||
toJSON(): Array<number>; | ||
[Symbol.iterator](): IterableIterator<number>; | ||
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; | ||
} | ||
export declare function ArrayViewMixin( | ||
ObjectViewClass: ViewType | PrimitiveFieldType, | ||
itemLength?: number | boolean, | ||
): typeof ArrayView; | ||
interface ViewLayoutField { | ||
View: ViewType; | ||
start?: number; | ||
length?: number; | ||
default?: any; | ||
} | ||
interface ViewLayout { | ||
[propName: string]: ViewLayoutField; | ||
} | ||
interface ViewTypes { | ||
[propName: string]: ViewType; | ||
} | ||
interface ObjectViewTypeDefs { | ||
[propName: string]: (field: ViewLayoutField) => void; | ||
} | ||
export declare class ObjectView extends DataView { | ||
static schema: object; | ||
static layout: ViewLayout; | ||
static fields: string[]; | ||
static Views: ViewTypes; | ||
static ArrayClass: typeof ArrayView; | ||
static types: ObjectViewTypeDefs; | ||
static viewLength: number; | ||
private static defaultBuffer: ArrayBuffer; | ||
get(field: string): any; | ||
getView(field: string): View; | ||
set(field: string, value: any): this; | ||
setView(field: string, value: View): this; | ||
toJSON(): object; | ||
static from(object: object, view?: View, start?: number, length?: number): View; | ||
static toJSON(view: View, start?: number): object; | ||
static getLength(): number; | ||
static initialize(): void; | ||
private static setDefaultBuffer(): void; | ||
static getSchemaOrdering(schema: object): object[]; | ||
static getLayoutFromSchema(schema: object): [ViewLayout, number, string[]]; | ||
static getViewFromSchema(schema: object): ViewType; | ||
} | ||
export declare function ObjectViewMixin( | ||
schema: object, | ||
ObjectViewClass?: typeof ObjectView, | ||
): typeof ObjectView; | ||
export declare class StringView extends Uint8Array { | ||
size: number; | ||
static masks: Int8Array; | ||
static ArrayClass: typeof ArrayView; | ||
characters(): Iterable<string>; | ||
charAt(index?: number): string; | ||
private getCharEnd(index: number): number; | ||
private getCharStart(index: number, startCharIndex?: number, startIndex?: number): number; | ||
replace(pattern: Collection, replacement: Collection): this; | ||
reverse(): this; | ||
search(searchValue: Collection, fromIndex?: number): number; | ||
private searchNaive(searchValue: Collection, fromIndex?: number): number; | ||
private searchShiftOr(searchValue: Collection, fromIndex?: number): number; | ||
substring(indexStart: number, indexEnd?: number): string; | ||
private toChar(index: number): string; | ||
toString(): string; | ||
toJSON(): string; | ||
trim(): StringView; | ||
static decode(bytes: Uint8Array): string; | ||
static encode( | ||
string: string, | ||
view: number[] | Uint8Array | DataView, | ||
start?: number, | ||
length?: number, | ||
): number; | ||
static from(...args: any[]): View; | ||
static toJSON(view: View, start?: number, length?: number): string; | ||
static getLength(string: string): number; | ||
} | ||
export declare class VariableView extends DataView { | ||
static maxLength: number; | ||
static maxView: DataView; | ||
static bufferView: DataView; | ||
} | ||
type AnyView = View | VariableView; | ||
export declare class MapView extends VariableView { | ||
static schema: object; | ||
static layout: ViewLayout; | ||
static optionalFields: string[]; | ||
static requiredFields: string[]; | ||
static optionalOffset: number; | ||
static lengthOffset: number; | ||
static defaultBuffer: Uint8Array; | ||
static ObjectViewClass: typeof ObjectView; | ||
static Views: ViewTypes; | ||
get(field: string): any; | ||
getView(field: string): AnyView; | ||
private getLayout(field: string): [ViewType, number, number]; | ||
set(field: string, value: any): this; | ||
setView(field: string, value: View): this; | ||
toJSON(): object; | ||
static encode(value: object, view: AnyView, start?: number): number; | ||
static from(value: object, view?: AnyView, start?: number): View; | ||
static toJSON(view: AnyView, start?: number): object; | ||
static getLength(value: any): number; | ||
static initialize(): void; | ||
private static getFieldLayout( | ||
field: ViewLayoutField, | ||
start: number, | ||
required: boolean, | ||
): ViewLayoutField; | ||
private static setDefaultBuffer(): void; | ||
} | ||
export declare function MapViewMixin( | ||
schema: object, | ||
MapViewClass?: typeof MapView, | ||
ObjectViewClass?: typeof ObjectView, | ||
): typeof MapView; | ||
export declare class VectorView extends VariableView { | ||
static View: ViewType | typeof VariableView; | ||
static Views: WeakMap<ViewType | typeof VariableView, typeof VectorView>; | ||
get(index: number): any; | ||
getView(index: number): AnyView; | ||
private getLayout(index: number): [number, number]; | ||
set(index: number, value: any): this; | ||
setView(index: number, value: AnyView): this; | ||
toJSON(): any[]; | ||
[Symbol.iterator](): IterableIterator<AnyView>; | ||
static encode(value: ArrayLike<any>, view: AnyView, start?: number): number; | ||
static from(value: ArrayLike<any>, view?: AnyView, start?: number): AnyView; | ||
static toJSON(view: AnyView, start?: number, length?: number): any[]; | ||
static getLength(value: any[]): number; | ||
static getSize(view: AnyView): number; | ||
} | ||
export declare function VectorViewMixin( | ||
ViewClass: ViewType | typeof VariableView, | ||
VectorViewClass?: typeof VectorView, | ||
): typeof VectorView; | ||
interface BinaryProtocolSchema { | ||
[propName: number]: typeof ObjectView; | ||
} | ||
export declare class BinaryProtocol { | ||
Views: BinaryProtocolSchema; | ||
private tagName: string; | ||
private tagType: PrimitiveFieldType; | ||
constructor(views: object, tagName?: string, tagType?: string); | ||
view(buffer: ArrayBuffer, offset?: number): ObjectView; | ||
encode(object: object, arrayBuffer?: ArrayBuffer, offset?: number): ObjectView; | ||
decode(buffer: ArrayBuffer, offset?: number): object; | ||
} | ||
export declare class BitArray extends Uint32Array { | ||
size: number; | ||
private lastPosition: BitPosition; | ||
constructor(size: number | ArrayLike<number> | ArrayBuffer, ...args: any); | ||
getBit(index: number): Bit; | ||
setBit(index: number, value: Bit): this; | ||
protected getBitPosition(index: number): void; | ||
static getLength(size: number): number; | ||
} | ||
export declare class Pool extends BitArray { | ||
get(): number; | ||
free(index: number): void; | ||
static getLength(size: number): number; | ||
} | ||
export declare class RankedBitArray extends BitArray { | ||
setBit(index: number, value: Bit): this; | ||
rank(index: number): number; | ||
select(index: number): number; | ||
static getLength(size: number): number; | ||
} | ||
export declare class BinaryHeap extends Array { | ||
heapify(): this; | ||
isHeap(): boolean; | ||
left(index: number): any; | ||
parent(index: number): any; | ||
replace(item: any): any; | ||
right(index: number): any; | ||
update(index: number): void; | ||
private has(index: number): boolean; | ||
private siftDown(start: number): void; | ||
private siftUp(start: number): void; | ||
static compare(a: any, b: any): boolean; | ||
private static getLeftIndex(index: number): number; | ||
private static getParentIndex(index: number): number; | ||
private static getRightIndex(index: number): number; | ||
static isHeap(heap: Collection): boolean; | ||
} | ||
interface AdjacencyListOptions { | ||
vertices: number; | ||
edges: number; | ||
} | ||
export declare class UnweightedAdjacencyList extends Uint32Array { | ||
vertices: number; | ||
edges: number; | ||
static undirected: boolean; | ||
static weighted: false; | ||
constructor(options?: AdjacencyListOptions, ...args: any); | ||
addEdge(x: number, y: number): this; | ||
removeEdge(x: number, y: number): this; | ||
hasEdge(x: number, y: number): boolean; | ||
getEdge(x: number, y: number): Bit; | ||
private setEdge(x: number, y: number): this; | ||
private unsetEdge(x: number, y: number): this; | ||
outEdges(x: number): IterableIterator<number>; | ||
inEdges(x: number): IterableIterator<number>; | ||
private setOffsets(): void; | ||
isFull(): boolean; | ||
grow(vertices?: number, edges?: number): UnweightedAdjacencyList; | ||
static getLength(vertices: number, edges: number): number; | ||
static getVertexCount(array: Collection): number; | ||
static fromGrid(grid: Grid): UnweightedAdjacencyList; | ||
} | ||
declare class WeightedAdjacencyList { | ||
vertices: number; | ||
edges: number; | ||
static undirected: boolean; | ||
static weighted: true; | ||
constructor(options?: AdjacencyListOptions, ...args: any); | ||
addEdge(x: number, y: number, weight: number): this; | ||
removeEdge(x: number, y: number): this; | ||
hasEdge(x: number, y: number): boolean; | ||
getEdge(x: number, y: number): number; | ||
private setEdge(x: number, y: number, weight: number): this; | ||
private unsetEdge(x: number, y: number): this; | ||
outEdges(x: number): IterableIterator<number>; | ||
inEdges(x: number): IterableIterator<number>; | ||
private setOffsets(): void; | ||
isFull(): boolean; | ||
grow(vertices?: number, edges?: number): UnweightedAdjacencyList; | ||
static getLength(vertices: number, edges: number): number; | ||
static getVertexCount(array: Collection): number; | ||
static fromGrid(grid: Grid): WeightedAdjacencyList; | ||
} | ||
export declare function WeightedAdjacencyListMixin<T extends Collection>( | ||
Base?: Constructor<T>, | ||
): Constructor<T & WeightedAdjacencyList>; | ||
interface UnweightedMatrixOptions { | ||
vertices: number; | ||
} | ||
export declare class UnweightedAdjacencyMatrix extends BinaryGrid { | ||
vertices: number; | ||
static undirected: boolean; | ||
static weighted: false; | ||
constructor(options?: UnweightedMatrixOptions, ...args: any); | ||
addEdge(x: number, y: number): this; | ||
removeEdge(x: number, y: number): this; | ||
hasEdge(x: number, y: number): boolean; | ||
getEdge(x: number, y: number): number; | ||
outEdges(x: number): IterableIterator<number>; | ||
inEdges(x: number): IterableIterator<number>; | ||
static getLength(size: number): number; | ||
static fromList(list: UnweightedAdjacencyList): UnweightedAdjacencyMatrix; | ||
} | ||
interface WeightedMatrixOptions { | ||
vertices: number; | ||
pad?: any; | ||
} | ||
declare class WeightedAdjacencyMatrix { | ||
vertices: number; | ||
static undirected: boolean; | ||
static weighted: true; | ||
constructor(options?: WeightedMatrixOptions, ...args: any); | ||
addEdge(x: number, y: number): this; | ||
removeEdge(x: number, y: number): this; | ||
hasEdge(x: number, y: number): boolean; | ||
getEdge(x: number, y: number): number; | ||
outEdges(x: number): IterableIterator<number>; | ||
inEdges(x: number): IterableIterator<number>; | ||
static getLength(size: number): number; | ||
static fromList(list: WeightedAdjacencyList, pad: number): WeightedAdjacencyMatrix; | ||
} | ||
export declare function WeightedAdjacencyMatrixMixin<T extends GridStructure>( | ||
Base: CollectionConstructor, | ||
undirected?: boolean, | ||
): Constructor<T & WeightedAdjacencyMatrix>; | ||
type AdjacencyStructure = | ||
| UnweightedAdjacencyList | ||
| UnweightedAdjacencyMatrix | ||
| WeightedAdjacencyList | ||
| WeightedAdjacencyMatrix; | ||
interface GraphOptions { | ||
vertices: number; | ||
edges?: number; | ||
pad?: number; | ||
} | ||
declare class Graph { | ||
colors: BinaryGrid; | ||
constructor(options: GraphOptions, ...args: any); | ||
isGray(x: number): boolean; | ||
setGray(x: number): this; | ||
isBlack(x: number): boolean; | ||
setBlack(x: number): this; | ||
traverse( | ||
isDFS?: boolean, | ||
start?: number, | ||
gray?: boolean, | ||
white?: boolean, | ||
black?: boolean, | ||
): IterableIterator<number>; | ||
path(start: number, end: number, isAcyclic?: boolean, isPositive?: boolean): number[]; | ||
tree(start?: number): number[]; | ||
isAcyclic(): boolean; | ||
topologicalSort(): number[]; | ||
private searchUnweighted(start: number, end: number, predecessors: number[]): boolean; | ||
private searchTopological( | ||
start: number, | ||
end: number, | ||
distances: number[], | ||
predecessors: number[], | ||
): boolean; | ||
private searchDijkstra( | ||
start: number, | ||
end: number, | ||
distances: number[], | ||
predecessors: number[], | ||
): boolean; | ||
private searchBellmanFord( | ||
start: number, | ||
end: number, | ||
distances: number[], | ||
predecessors: number[], | ||
): boolean; | ||
} | ||
export declare function GraphMixin<T extends AdjacencyStructure>( | ||
Base: Constructor<T>, | ||
undirected?: boolean, | ||
): Constructor<T & Graph>; | ||
export { AdjacencyListMixin } from "./adjacency-list.ts"; | ||
export { AdjacencyMatrixUnweightedDirected } from "./adjacency-matrix-unweighted-directed.ts"; | ||
export { AdjacencyMatrixUnweightedUndirected } from "./adjacency-matrix-unweighted-undirected.ts"; | ||
export { AdjacencyMatrixWeightedDirectedMixin } from "./adjacency-matrix-weighted-directed.ts"; | ||
export { AdjacencyMatrixWeightedUndirectedMixin } from "./adjacency-matrix-weighted-undirected.ts"; | ||
export { ArrayView } from "./array-view.ts"; | ||
export { BigBitFieldMixin } from "./big-bit-field.ts"; | ||
export { BinaryGrid } from "./binary-grid.ts"; | ||
export { BinaryHeap } from "./binary-heap.ts"; | ||
export { BitArray } from "./bit-array.ts"; | ||
export { BitFieldMixin } from "./bit-field.ts"; | ||
export { BooleanView } from "./boolean-view.ts"; | ||
export { GraphMixin } from "./graph.ts"; | ||
export type { Colors } from "./graph.ts"; | ||
export { GridMixin } from "./grid.ts"; | ||
export { MapView } from "./map-view.ts"; | ||
export { BigInt64View, BigUint64View, Float32View, Float64View, Int16View, Int32View, Int8View, Uint16View, Uint32View, Uint8View, } from "./numeric-view.ts"; | ||
export { ObjectView } from "./object-view.ts"; | ||
export { Pool } from "./pool.ts"; | ||
export { RankedBitArray } from "./ranked-bit-array.ts"; | ||
export { SortedArray } from "./sorted-array.ts"; | ||
export type { Comparator } from "./sorted-array.ts"; | ||
export { StringView } from "./string-view.ts"; | ||
export { SymmetricGridMixin } from "./symmetric-grid.ts"; | ||
export { TypedArrayView } from "./typed-array-view.ts"; | ||
export type { AdjacencyStructure, AdjacencyStructureConstructor, Bit, IndexedCollection, } from "./utility-types.ts"; | ||
export { getBitSize, getLog2, getLSBIndex, popCount32 } from "./utilities.ts"; | ||
export { VectorView } from "./vector-view.ts"; | ||
export { View } from "./view.ts"; |
101
index.js
@@ -1,74 +0,27 @@ | ||
const BitField = require('./lib/bit-field'); | ||
const BigBitField = require('./lib/big-bit-field'); | ||
const BitFieldMixin = require('./lib/bit-field-mixin'); | ||
const GraphMixin = require('./lib/graph'); | ||
const GridMixin = require('./lib/grid'); | ||
const BinaryHeap = require('./lib/binary-heap.js'); | ||
const Pool = require('./lib/pool'); | ||
const RankedBitArray = require('./lib/ranked-bit-array'); | ||
const SortedArray = require('./lib/sorted-array'); | ||
const SortedMixin = require('./lib/sorted-collection'); | ||
const SymmetricGridMixin = require('./lib/symmetric-grid'); | ||
const UnweightedAdjacencyList = require('./lib/unweighted-adjacency-list'); | ||
const UnweightedAdjacencyMatrix = require('./lib/unweighted-adjacency-matrix'); | ||
const WeightedAdjacencyListMixin = require('./lib/weighted-adjacency-list'); | ||
const WeightedAdjacencyMatrixMixin = require('./lib/weighted-adjacency-matrix'); | ||
const ArrayView = require('./lib/array-view'); | ||
const TypedArrayView = require('./lib/typed-array-view'); | ||
const ArrayViewMixin = require('./lib/array-view-mixin'); | ||
const { MapView, MapViewMixin } = require('./lib/map-view'); | ||
const { ObjectView, ObjectViewMixin } = require('./lib/object-view'); | ||
const StringView = require('./lib/string-view'); | ||
const BinaryProtocol = require('./lib/binary-protocol'); | ||
const { TypeView, TypeViewMixin } = require('./lib/type-view'); | ||
const BooleanView = require('./lib/boolean-view'); | ||
const VariableView = require('./lib/variable-view'); | ||
const { VectorView, VectorViewMixin } = require('./lib/vector-view'); | ||
/** | ||
* @external ArrayBuffer | ||
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer|ArrayBuffer} | ||
*/ | ||
/** | ||
* @external DataView | ||
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView|DataView} | ||
*/ | ||
/** | ||
* @external TypedArray | ||
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray|TypedArray} | ||
*/ | ||
module.exports = { | ||
BitField, | ||
BigBitField, | ||
BitFieldMixin, | ||
GraphMixin, | ||
GridMixin, | ||
BinaryHeap, | ||
Pool, | ||
RankedBitArray, | ||
SortedArray, | ||
SortedMixin, | ||
SymmetricGridMixin, | ||
UnweightedAdjacencyList, | ||
UnweightedAdjacencyMatrix, | ||
WeightedAdjacencyListMixin, | ||
WeightedAdjacencyMatrixMixin, | ||
ArrayView, | ||
TypedArrayView, | ||
ArrayViewMixin, | ||
MapView, | ||
MapViewMixin, | ||
ObjectView, | ||
ObjectViewMixin, | ||
StringView, | ||
BinaryProtocol, | ||
TypeView, | ||
TypeViewMixin, | ||
BooleanView, | ||
VariableView, | ||
VectorView, | ||
VectorViewMixin, | ||
}; | ||
export { AdjacencyListMixin } from "./adjacency-list.ts"; | ||
export { AdjacencyMatrixUnweightedDirected } from "./adjacency-matrix-unweighted-directed.ts"; | ||
export { AdjacencyMatrixUnweightedUndirected } from "./adjacency-matrix-unweighted-undirected.ts"; | ||
export { AdjacencyMatrixWeightedDirectedMixin } from "./adjacency-matrix-weighted-directed.ts"; | ||
export { AdjacencyMatrixWeightedUndirectedMixin } from "./adjacency-matrix-weighted-undirected.ts"; | ||
export { ArrayView } from "./array-view.ts"; | ||
export { BigBitFieldMixin } from "./big-bit-field.ts"; | ||
export { BinaryGrid } from "./binary-grid.ts"; | ||
export { BinaryHeap } from "./binary-heap.ts"; | ||
export { BitArray } from "./bit-array.ts"; | ||
export { BitFieldMixin } from "./bit-field.ts"; | ||
export { BooleanView } from "./boolean-view.ts"; | ||
export { GraphMixin } from "./graph.ts"; | ||
export { GridMixin } from "./grid.ts"; | ||
export { MapView } from "./map-view.ts"; | ||
export { BigInt64View, BigUint64View, Float32View, Float64View, Int16View, Int32View, Int8View, Uint16View, Uint32View, Uint8View, } from "./numeric-view.ts"; | ||
export { ObjectView } from "./object-view.ts"; | ||
export { Pool } from "./pool.ts"; | ||
export { RankedBitArray } from "./ranked-bit-array.ts"; | ||
export { SortedArray } from "./sorted-array.ts"; | ||
export { StringView } from "./string-view.ts"; | ||
export { SymmetricGridMixin } from "./symmetric-grid.ts"; | ||
export { TypedArrayView } from "./typed-array-view.ts"; | ||
export { getBitSize, getLog2, getLSBIndex, popCount32 } from "./utilities.ts"; | ||
export { VectorView } from "./vector-view.ts"; | ||
export { View } from "./view.ts"; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "structurae", | ||
"version": "3.3.0", | ||
"version": "4.0.0-pre.1", | ||
"description": "Data structures for performance-sensitive modern JavaScript applications.", | ||
"main": "index.js", | ||
"types": "index.d.ts", | ||
"type": "module", | ||
"keywords": [ | ||
@@ -23,15 +23,8 @@ "optimization", | ||
"scripts": { | ||
"test": "jest", | ||
"lint": "eslint ./lib/*.js ./test/*.js", | ||
"doc:api": "jsdoc2md > doc/API.md", | ||
"benchmark": "node bench/index.js" | ||
"build": "tsc-silent -p tsconfig.json --suppress @" | ||
}, | ||
"directories": { | ||
"lib": "lib", | ||
"doc": "doc", | ||
"test": "test" | ||
}, | ||
"files": [ | ||
"/lib/", | ||
"index.d.ts" | ||
"*.js", | ||
"*.d.ts", | ||
"*.js.map" | ||
], | ||
@@ -48,80 +41,9 @@ "author": "Maga D. Zandaqo <denelxan@gmail.com> (http://maga.name)", | ||
}, | ||
"devDependencies": { | ||
"@types/jest": "^26.0.4", | ||
"benchmark": "^2.1.4", | ||
"eslint": "^7.4.0", | ||
"eslint-config-airbnb-base": "^14.2.0", | ||
"eslint-config-prettier": "^6.11.0", | ||
"eslint-plugin-import": "^2.22.0", | ||
"jest": "^26.1.0", | ||
"jsdoc-to-markdown": "^6.0.1", | ||
"json-schema-faker": "^0.5.0-rcv.24" | ||
}, | ||
"jest": { | ||
"testEnvironment": "node", | ||
"collectCoverage": true, | ||
"collectCoverageFrom": [ | ||
"**/lib/**", | ||
"!**/node_modules/**", | ||
"!**/test/**" | ||
], | ||
"coverageDirectory": "<rootDir>/coverage", | ||
"coverageReporters": [ | ||
"lcov" | ||
], | ||
"coverageThreshold": { | ||
"global": { | ||
"branches": 95, | ||
"functions": 100, | ||
"lines": 100, | ||
"statements": 100 | ||
} | ||
} | ||
}, | ||
"engines": { | ||
"node": ">=11.0.0" | ||
"node": ">=14.0.0" | ||
}, | ||
"prettier": { | ||
"printWidth": 100, | ||
"singleQuote": true, | ||
"trailingComma": "all" | ||
}, | ||
"eslintConfig": { | ||
"extends": [ | ||
"airbnb-base", | ||
"prettier" | ||
], | ||
"env": { | ||
"node": true, | ||
"jest": true | ||
}, | ||
"globals": { | ||
"globalThis": false | ||
}, | ||
"rules": { | ||
"max-classes-per-file": 0, | ||
"no-bitwise": 0, | ||
"no-plusplus": 0, | ||
"no-continue": 0, | ||
"no-restricted-syntax": 0, | ||
"no-nested-ternary": 1, | ||
"no-labels": 1, | ||
"no-param-reassign": [ | ||
2, | ||
{ | ||
"props": false | ||
} | ||
], | ||
"valid-jsdoc": [ | ||
2, | ||
{ | ||
"prefer": { | ||
"return": "returns" | ||
}, | ||
"requireReturnDescription": false, | ||
"requireParamDescription": false | ||
} | ||
] | ||
} | ||
"devDependencies": { | ||
"tsc-silent": "^1.2.1", | ||
"typescript": "^4.3.2" | ||
} | ||
} |
1342
README.md
# Structurae | ||
[![Actions Status](https://github.com/zandaqo/structurae/workflows/ci/badge.svg)](https://github.com/zandaqo/structurae/actions) | ||
[![npm](https://img.shields.io/npm/v/structurae.svg?style=flat-square)](https://www.npmjs.com/package/structurae) | ||
[![Actions Status](https://github.com/zandaqo/structurae/workflows/Build/badge.svg)](https://github.com/zandaqo/structurae/actions) | ||
A collection of data structures for high-performance JavaScript applications that includes: | ||
A collection of data structures for high-performance JavaScript applications | ||
that includes: | ||
- [Binary Structures](https://github.com/zandaqo/structurae#binary-structures): | ||
- [ObjectView](https://github.com/zandaqo/structurae#ObjectView) - extends DataView to implement C-like struct. | ||
- [ArrayView](https://github.com/zandaqo/structurae#ArrayView) - DataView based array of ObjectViews, strings, numbers, etc. | ||
- [MapView](https://github.com/zandaqo/structurae#MapView) - ObjectView with optional fields and fields of varying sizes. | ||
- [VectorView](https://github.com/zandaqo/structurae#VectorView) - ArrayView that supports optional and variable length elements, including MapViews. | ||
- [StringView](https://github.com/zandaqo/structurae#StringView) - extends Uint8Array to handle C-like representation of UTF-8 encoded strings. | ||
- [BinaryProtocol](https://github.com/zandaqo/structurae#BinaryProtocol) - a helper class that simplifies defining and operating on multiple tagged ObjectViews. | ||
- [Binary Protocol](https://github.com/zandaqo/structurae#binary-protocol) - | ||
simple binary protocol based on DataView and defined with JSONSchema | ||
- Bit Structures: | ||
- [BitField & BigBitField](https://github.com/zandaqo/structurae#BitField) - stores and operates on data in Numbers and BigInts treating them as bitfields. | ||
- [BitArray](https://github.com/zandaqo/structurae#BitArray) - an array of bits implemented with Uint32Array. | ||
- [Pool](https://github.com/zandaqo/structurae#Pool) - manages availability of objects in object pools. | ||
- [RankedBitArray](https://github.com/zandaqo/structurae#RankedBitArray) - extends BitArray with O(1) time rank and O(logN) select methods. | ||
- [BitField & BigBitField](https://github.com/zandaqo/structurae#BitField) - | ||
stores and operates on data in Numbers and BigInts treating them as | ||
bitfields. | ||
- [BitArray](https://github.com/zandaqo/structurae#BitArray) - an array of | ||
bits implemented with Uint32Array. | ||
- [Pool](https://github.com/zandaqo/structurae#Pool) - manages availability of | ||
objects in object pools. | ||
- [RankedBitArray](https://github.com/zandaqo/structurae#RankedBitArray) - | ||
extends BitArray with O(1) time rank and O(logN) select methods. | ||
- [Graphs](https://github.com/zandaqo/structurae#Graphs): | ||
- [Graph](https://github.com/zandaqo/structurae#Graph) - extends an adjacency list/matrix structure and provides methods for traversal (BFS, DFS), | ||
pathfinding (Dijkstra, Bellman-Ford), spanning tree construction (BFS, Prim), etc. | ||
- [UnweightedAdjacencyList & WeightedAdjacencyList](https://github.com/zandaqo/structurae#Adjacency-Lists) - implements Adjacency List data structure. | ||
- [UnweightedAdjacencyMatrix & WeightedAdjacencyMatrix](https://github.com/zandaqo/structurae#Adjacency-Matrices) - implements Adjacency Matrix data structure. | ||
- [Adjacency Structures](https://github.com/zandaqo/structurae#Adjacency-Structures) - | ||
implement adjacency list & matrix data structures. | ||
- [Graph](https://github.com/zandaqo/structurae#Graph) - extends an adjacency | ||
list/matrix structure and provides methods for traversal (BFS, DFS), | ||
pathfinding (Dijkstra, Bellman-Ford), spanning tree construction (BFS, | ||
Prim), etc. | ||
- [Grids](https://github.com/zandaqo/structurae#Grids): | ||
- [BinaryGrid](https://github.com/zandaqo/structurae#BinaryGrid) - creates a grid or 2D matrix of bits. | ||
- [Grid](https://github.com/zandaqo/structurae#Grid) - extends built-in indexed collections to handle 2 dimensional data (e.g. nested arrays). | ||
- [SymmetricGrid](https://github.com/zandaqo/structurae#SymmetricGrid) - a grid to handle symmetric or triangular matrices using half the space required for a normal grid. | ||
- [BinaryGrid](https://github.com/zandaqo/structurae#BinaryGrid) - creates a | ||
grid or 2D matrix of bits. | ||
- [Grid](https://github.com/zandaqo/structurae#Grid) - extends built-in | ||
indexed collections to handle 2 dimensional data (e.g. nested arrays). | ||
- [SymmetricGrid](https://github.com/zandaqo/structurae#SymmetricGrid) - a | ||
grid to handle symmetric or triangular matrices using half the space | ||
required for a normal grid. | ||
- [Sorted Structures](https://github.com/zandaqo/structurae#sorted-structures): | ||
- [BinaryHeap](https://github.com/zandaqo/structurae#BinaryHeap) - extends Array to implement the Binary Heap data structure. | ||
- [SortedCollection](https://github.com/zandaqo/structurae#SortedCollection) - extends TypedArrays to handle sorted data. | ||
- [SortedArray](https://github.com/zandaqo/structurae#SortedArray) - extends Array to handle sorted data. | ||
- [BinaryHeap](https://github.com/zandaqo/structurae#BinaryHeap) - extends | ||
Array to implement the Binary Heap data structure. | ||
- [SortedArray](https://github.com/zandaqo/structurae#SortedArray) - extends | ||
Array to handle sorted data. | ||
## Installation | ||
## Usage | ||
Node.js: | ||
``` | ||
npm i structurae | ||
npm i structurae | ||
``` | ||
``` | ||
import {...} from "structurae"; | ||
``` | ||
Deno: | ||
``` | ||
import * as structurae from "https://raw.githubusercontent.com/zandaqo/structurae/4.0.0-rc1/index.ts" | ||
``` | ||
## Documentation | ||
- [API documentation](https://github.com/zandaqo/structurae/blob/master/doc/API.md) | ||
- Articles: | ||
- [Structurae: Data Structures for Heigh-Performance JavaScript](https://blog.usejournal.com/structurae-data-structures-for-high-performance-javascript-9b7da4c73f8) | ||
- [Structurae 1.0: Graphs, Strings, and WebAssembly](https://medium.com/@zandaqo/structurae-1-0-graphs-strings-and-webassembly-25dd964d5a70) | ||
- [Structurae: Data Structures for Heigh-Performance | ||
JavaScript](https://blog.usejournal.com/structurae-data-structures-for-high-performance-javascript-9b7da4c73f8) | ||
- [Structurae 1.0: Graphs, Strings, and | ||
WebAssembly](https://medium.com/@zandaqo/structurae-1-0-graphs-strings-and-webassembly-25dd964d5a70) | ||
## Overview | ||
### Binary Structures | ||
Binary data in JavaScript is represented by ArrayBuffer and accessed through "view" objects--TypedArrays and DataView. | ||
However, both of those interfaces are limited to working with numbers. Structurae offers a set of classes that extend these interfaces to | ||
support using ArrayBuffers for strings, objects, and arrays of objects defined with schema akin to C-like structs. | ||
Useful on their own, when combined, these classes form the basis for a simple binary protocol that is smaller and faster than | ||
schema-less binary formats (e.g. BSON, MessagePack) and supports zero-copy operations. Unlike other schema-based formats | ||
(e.g. Flatbuffers), these interfaces are native to JavaScript, hence, supported in all modern browsers and Node.js, | ||
and do not require compilation. | ||
### Binary Protocol | ||
#### ObjectView | ||
ObjectView extends DataView to store a JavaScript object in an ArrayBuffer akin to C-like struct. | ||
Fields of an ObjectView are defined using a subset of JSON Schema. ObjectView supports all JavaScript values, that is, numbers, strings, | ||
booleans, objects, and arrays. Internally, the data is laid out sequentially with fixed sizes, hence, | ||
variable length arrays and optional fields are not supported (for those check out [MapView](https://github.com/zandaqo/structurae#MapView)). | ||
Binary data in JavaScript is represented by ArrayBuffer and accessed through | ||
"view" interfaces--TypedArrays and DataView. However, both of those interfaces | ||
are limited to working with numbers. Structurae offers a set of classes that | ||
extend the DataView interface to support using ArrayBuffers for strings, | ||
objects, and arrays. These classes ("views") form the basis for a simple binary | ||
protocol ("view protocol") with the following features: | ||
```javascript | ||
const { ObjectViewMixin } = require('structurae'); | ||
- smaller and faster than schema-less binary formats (e.g. BSON, MessagePack); | ||
- supports zero-copy operations, e.g. reading and changing object fields | ||
without; decoding the whole object; | ||
- supports static typing through TypeScript; | ||
- uses JSON Schema for schema definitions; | ||
- does not require compilation unlike most other schema-based formats (e.g. | ||
FlatBuffers). | ||
const Person = ObjectViewMixin({ | ||
$id: 'Person', // each object requires a unique id | ||
type: 'object', | ||
The protocol is operated through the `View` class that handles creation and | ||
caching of necessary structures for a given JSON Schema as well as simplifying | ||
serialization of tagged objects. | ||
```typescript | ||
import { View } from "structurae"; | ||
// define interface for out animal objects | ||
interface Animal { | ||
name: string; | ||
age: number; | ||
} | ||
// create and return a view class (extension of DataView) that handles our Animal objects | ||
const AnimalView = View.create<Animal>({ | ||
$id: "Pet", | ||
type: "object", | ||
properties: { | ||
name: { type: 'string', maxLength: 10 }, // the size of a string field is required and defined by maxLength | ||
name: { type: "string", maxLength: 10 }, | ||
// by default, type `number` is treated as int32, but can be further specified usin `btype` | ||
age: { type: "number", btype: "uint8" }, | ||
}, | ||
}); | ||
// encode our animal object | ||
const animal = AnimalView.from({ name: "Gaspode", age: 10 }); | ||
animal instanceof DataView; | ||
//=> true | ||
animal.byteLength; | ||
//=> 14 | ||
animal.get("name"); | ||
//=> Gaspode | ||
animal.get("age"); | ||
//=> 10 | ||
animal.set("age", 20); | ||
animal.toJSON(); | ||
//=> { name: "Gaspode", age: 20 } | ||
``` | ||
#### Objects and Maps | ||
Objects by default are treated as C-like structs, the data is laid out | ||
sequentially with fixed sizes, all standard JavaScript values are supported, | ||
inluding other objects and arrays of fixed size: | ||
```typescript | ||
interface Friend { | ||
name: string; | ||
} | ||
interface Person { | ||
name: string; | ||
fullName: Array<string>; | ||
bestFriend: Friend; | ||
friends: Array<Friend>; | ||
} | ||
const PersonView = View.create<Person>({ | ||
// each object requires a unique id | ||
$id: "Person", | ||
type: "object", | ||
properties: { | ||
// the size of a string field is required and defined by maxLength | ||
name: { type: "string", maxLength: 10 }, | ||
fullName: { | ||
type: 'array', | ||
maxItems: 2, // the size of an array is required and defined by maxItems | ||
items: { type: 'string', maxLength: 10 }, // all items have to be the same type | ||
type: "array", | ||
// the size of an array is required and defined by maxItems | ||
maxItems: 2, | ||
// all items have to be the same type | ||
items: { type: "string", maxLength: 20 }, | ||
}, | ||
bestFriend: { $ref: '#Friend' }, // objects can be referenced with $ref using their $id | ||
// objects can be referenced with $ref using their $id | ||
bestFriend: { $ref: "#Friend" }, | ||
friends: { | ||
type: 'array', | ||
type: "array", | ||
maxItems: 3, | ||
items: { | ||
$id: 'Friend', | ||
type: 'object', | ||
$id: "Friend", | ||
type: "object", | ||
properties: { | ||
name: { type: 'string', maxLength: 10 }, | ||
name: { type: "string", maxLength: 20 }, | ||
}, | ||
}, | ||
}, | ||
scores: { | ||
type: 'array', | ||
maxItems: 10, | ||
items: { type: 'integer', btype: 'int16' }, | ||
}, | ||
house: { | ||
$id: 'House', | ||
type: 'object', | ||
properties: { | ||
size: { type: 'number', btype: 'float32', default: 100 }, // default values are applied upon creation of a view | ||
}, | ||
}, | ||
pets: { | ||
type: 'array', | ||
maxItems: 3, | ||
items: { | ||
$id: 'Pet', | ||
type: 'object', | ||
properties: { | ||
type: { type: 'string', maxLength: 10 }, | ||
} | ||
}, | ||
}, | ||
}, | ||
}); | ||
const person = Person.from({ | ||
name: 'Zaphod', | ||
fullName: ['Zaphod', 'Beeblebrox'], | ||
scores: [1, 2, 3], | ||
house: { | ||
size: 1, | ||
}, | ||
pets: [ | ||
{ type: 'dog' }, { type: 'cat' } | ||
], | ||
name: "Carrot", | ||
fullName: ["Carrot", "Ironfoundersson"], | ||
bestFriend: { name: "Sam Vimes" }, | ||
friends: [{ name: "Sam Vimes" }], | ||
}); | ||
person.byteLength | ||
//=> 64 | ||
person.get('scores'); | ||
//=> 1 | ||
person.get('name'); | ||
//=> Zaphod | ||
person.getView('name') | ||
person.byteLength; | ||
//=> 130 | ||
person.get("name"); | ||
//=> Carrot | ||
person.getView("name"); | ||
//=> StringView [10] | ||
person.get('scores') | ||
//=> [1, 2, 3, 0, 0, 0, 0, 0, 0, 0,] | ||
person.set('house', { size: 5 }); | ||
person.get('house'); | ||
//=> { size: 5 } | ||
person.toJSON() | ||
//=> { name: 'Zaphod', fullName: ['Zaphod', 'Beeblebrox'], scores: [1, 2, 3, 0, 0, 0, 0, 0, 0, 0,], | ||
// house: { size: 5 }, pets: [{ type: 'dog' }, { type: 'cat' }, { type: '' }] } | ||
person.get("fullName"); | ||
//=> ["Carrot", "Ironfoundersson"] | ||
person.toJSON(); | ||
//=> { | ||
// name: "Carrot", | ||
// fullName: ["Carrot", "Ironfoundersson"], | ||
// bestFriend: { name: "Sam Vimes" }, | ||
// friends: [{ name: "Sam Vimes" }] | ||
// } | ||
``` | ||
There are certain requirements for a JSON Schema used for ObjectViews: | ||
- Each object should have a unique id defined with `$id` field. Upon initialization, the view class is stored in `ObjectView.Views` | ||
and accessed with the id used as the key. References made with `$ref` are also resolved against the id. | ||
- Sizes of strings and arrays should be defined using `maxLength` and `maxItems` properties respectfully. | ||
- `$ref` can be used to reference objects and only objects by their `$id`. The referenced object should be defined either in the | ||
same schema or in a schema of an ObjectView class initialized previously. | ||
- Type `number` by default resolves to `float64` and type `integer` to `int32`, you can use any other type by specifying it in | ||
`btype` property. | ||
Objects that support optional fields and fields of variable size ("maps") should | ||
additionally specify `btype` `map` and list non-optional (fixed sized) fields as | ||
`required`: | ||
ObjectView supports setting default values of fields. Default values are applied upon creation of a view: | ||
```javascript | ||
const House = ObjectViewMixin({ | ||
$id: 'House', | ||
type: 'object', | ||
properties: { | ||
size: { type: 'integer', btype: 'uint32', default: 100 } | ||
}, | ||
}); | ||
const house = House.from({}); | ||
house.get('size'); | ||
//=> 100 | ||
```typescript | ||
interface Town { | ||
name: string; | ||
railstation: boolean; | ||
clacks?: number; | ||
} | ||
const TownView = View.create<Town>({ | ||
$id: "Town", | ||
type: "object", | ||
btype: "map", | ||
properties: { | ||
// notice that maxLength is not required for optional fields in maps | ||
// however, if set, map with truncate longer strings to fit the maxLength | ||
name: { type: "string" }, | ||
railstation: { type: "boolean" }, | ||
// optional, nullable field | ||
clacks: { type: "integer" }, | ||
} | ||
required: ["railstation"], | ||
}); | ||
const lancre = TownView.from({ name: "Lancre", railstation: false }); | ||
lancre.get("name") //=> Lancre | ||
lancre.get("clacks") //=> undefined | ||
lancre.byteLength //=> 19 | ||
const stoLat = TownView.from({ name: "Sto Lat", railstation: true, clacks: 1 }); | ||
stoLat.get("clacks") //=> 1 | ||
stoLat.byteLength //=> 24 | ||
``` | ||
Default values of an ObjectView can be overridden when the view is used as a field inside other views: | ||
```javascript | ||
const Neighborhood = ObjectViewMixin({ | ||
$id: 'Neighborhood', | ||
type: 'object', | ||
properties: { | ||
house: { $ref: '#House' }, | ||
biggerHouse: { $ref: '#House', default: { size: 200 } }, | ||
}, | ||
}); | ||
The size and layout of each map instance is calculated upon creation and stored | ||
within the instance (unlike fixed sized objects, where each instance have the | ||
same size and layout). Maps are useful for densely packing objects and arrays | ||
whose size my vary greatly. There is a limitation, though, since ArrayBuffers | ||
cannot be resized, optional fields that were absent upon creation of a map view | ||
cannot be set later, and those set cannot be resized, that is, assigned a value | ||
that is greater than their current size. | ||
const neighborhood = Neighborhood.from({}); | ||
neighborhood.get('house') | ||
//=> { size: 100 } | ||
neighborhood.get('biggerHouse') | ||
//=> { size: 200 } | ||
``` | ||
For performance sake, all variable size views are encoded using single global | ||
ArrayBuffer that is 8192 bytes long, if you expect to handle bigger views, set | ||
`View.maxLength` to desired max length before instantiating any view. | ||
You can add your own field types to ObjectView, for example an ObjectView that supports Date: | ||
```javascript | ||
const { TypeViewMixin } = require('structurae'); | ||
class DateView extends TypeViewMixin('float64', true) { | ||
static from(value, view, start) { | ||
super.from(+value, view, start); | ||
} | ||
static toJSON(view, start) { | ||
return new Date(super.toJSON(view, start)); | ||
} | ||
} | ||
There are certain requirements for a JSON Schema used for fixed sized objects: | ||
class View extends ObjectView {} | ||
View.types = { | ||
...ObjectView.types, | ||
date(field) { | ||
return DateView; | ||
}, | ||
}; | ||
- Each object should have a unique id defined with `$id` field. Upon | ||
initialization, the view class is stored in `View.Views` and accessed with the | ||
id used as the key. References made with `$ref` are also resolved against the | ||
id. | ||
- For fixed sized objects, sizes of strings and arrays should be defined using | ||
`maxLength` and `maxItems` properties respectfully. | ||
- `$ref` can be used to reference objects by their `$id`. The referenced object | ||
should be defined either in the same schema or in a schema initialized | ||
previously. | ||
- Type `number` by default resolves to `float64` and type `integer` to `int32`, | ||
you can use any other type by specifying it in `btype` property. | ||
const ViewWithDate = ObjectViewMixin({ | ||
$id: 'ViewWithDate', | ||
type: 'object', | ||
Objects and maps support setting default values of required fields. Default | ||
values are applied upon creation of a view: | ||
```typescript | ||
interface House { | ||
size: number; | ||
} | ||
const House = View.create<House>({ | ||
$id: "House", | ||
type: "object", | ||
properties: { | ||
a: { type: 'date' }, | ||
size: { type: "integer", btype: "uint32", default: 100 }, | ||
}, | ||
}, View); | ||
const date = ViewWithDate.from({ a: new Date(0) }); | ||
date.get('a') | ||
//=> Thu Jan 01 1970 00:00:00 GMT+0000 | ||
date.set('a', new Date(1e8)); | ||
date.toJSON(); | ||
//=> { a: Fri Jan 02 1970 03:46:40 GMT+0000 } | ||
}); | ||
const house = House.from({} as House); | ||
house.get("size"); | ||
//=> 100 | ||
``` | ||
#### ArrayView | ||
DataView based array of "views": objects, number, strings, etc: | ||
```javascript | ||
const { ObjectViewMixin, ArrayViewMixin, StringView } = require('structurae'); | ||
Default values of an object can be overridden when it is nested inside another | ||
object: | ||
const Int32ArrayView = ArrayViewMixin('int32', true); // create an ArrayView class for int32 values with little endian encoding | ||
const Int32ArrayViewBE = ArrayViewMixin('int32', false); // big endian int32 values | ||
const StringsView = ArrayViewMixin(StringView, 20); // an ArrayView class for strings with maximum length of 20 bytes each | ||
const Person = ObjectViewMixin({ | ||
$id: 'Person', // each object requires a unique id | ||
type: 'object', | ||
```typescript | ||
interface Neighborhood { | ||
house: House; | ||
biggerHouse: House; | ||
} | ||
const Neighborhood = View.create<Neighborhood>({ | ||
$id: "Neighborhood", | ||
type: "object", | ||
properties: { | ||
id: { type: 'integer', btype: 'uint32' }, | ||
name: { type: 'string', maxLength: 10 }, | ||
house: { $ref: "#House" }, | ||
biggerHouse: { $ref: "#House", default: { size: 200 } }, | ||
}, | ||
}); | ||
// an array class for Person objects | ||
const PeopleArray = ArrayViewMixin(Person); | ||
// create an empty array view of 10 Person objects | ||
const people = PeopleArray.of(10); | ||
// create an array view from a given array | ||
const hitchhikers = PeopleArray.from([ | ||
{ id: 1, name: 'Arthur' }, | ||
{ id: 2, name: 'Ford' }, | ||
]); | ||
// get a view of the first object | ||
hitchhikers.getView(0); | ||
//=> Person [14] | ||
// get the value of the first object | ||
hitchhikers.get(0); | ||
//=> { id: 1, name: 'Arthur' } | ||
// set the first object data | ||
hitchhikers.set(0, { id: 3, name: 'Trillian' }); | ||
hitchhikers.get(0); | ||
//=> { id: 3, name: 'Trillian' } | ||
hitchhikers.toJSON(); | ||
//=> [{ id: 1, name: 'Arthur' }, { id: 2, name: 'Ford' }] | ||
const neighborhood = Neighborhood.from({} as Neighborhood); | ||
neighborhood.get("house"); | ||
//=> { size: 100 } | ||
neighborhood.get("biggerHouse"); | ||
//=> { size: 200 } | ||
``` | ||
TypedArrays in JavaScript have two limitations that make them cumbersome to use in conjunction with DataView. | ||
First, there is no way to specify the endianness of numbers in TypedArrays unlike DataView. | ||
Second, TypedArrays require their offset (byteOffset) to be a multiple of their element size (BYTES_PER_ELEMENT), | ||
which means that they often cannot "view" into existing ArrayBuffer starting from certain positions. | ||
ArrayViews for numbers are essentially TypedArrays that circumvent both issues by using the DataView interface. | ||
You can specify endianness and instantiate them at any position in an existing ArrayBuffer. | ||
#### Arrays and Vectors | ||
```javascript | ||
const { ArrayViewMixin } = require('structurae'); | ||
The protocol supports arrays of non-nullable fixed sized values (numbers, | ||
strings of fixed maximum size, objects) and vectors--arrays with nullable or | ||
variable sized values. The type of items held by both "container" views is | ||
defined in `items` field of the schema. | ||
// create a class for little endian doubles | ||
const Float64View = ArrayViewMixin('float64', true); | ||
const buffer = new ArrayBuffer(11); | ||
const doubles = new Float64View(buffer, 3, 8); | ||
doubles.byteLength | ||
//=> 20 | ||
doubles.byteOffset | ||
//=> 3 | ||
doubles.set(0, 5).set(1, 10); | ||
[...doubles] | ||
//=> [5, 10] | ||
```typescript | ||
const Street = View.create<Array<House>>({ | ||
type: "array", | ||
items: { | ||
type: "object", | ||
// we can also reference previously created class with $ref | ||
$ref: "#House", | ||
}, | ||
}); | ||
const street = Street.from([{ size: 10 }, { size: 20 }]); | ||
street.byteLength; //=> 8 | ||
street.get(0); //=> { size: 10 } | ||
street.getView(0).get("size"); //=> 10 | ||
street.size; //=> 2 | ||
street.set(0, { size: 100 }); | ||
street.get(0); //=> { size: 100 } | ||
``` | ||
For vectors set `btype` to `vector`: | ||
#### MapView | ||
MapView is an ObjectView that supports optional fields and fields of variable size. The size and layout of each MapView instance | ||
is calculated upon creation and stored within the instance (unlike ObjectViews, where each instance have the same size and layout). | ||
MapViews are useful for densely packing objects and arrays whose size my vary greatly. | ||
There is a limitation, though, since ArrayBuffers cannot be resized, optional fields that were absent upon creation of a map view cannot be set later, and those set cannot be resized. | ||
```javascript | ||
const { MapViewMixin } = require('structurae'); | ||
const Person = MapViewMixin({ | ||
$id: 'Person', | ||
type: 'object', | ||
properties: { | ||
id: { type: 'integer', btype: 'uint32', default: 10 }, | ||
// notice that maxLength is not required for optional fields 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: { | ||
$id: 'Pet', | ||
type: 'object', | ||
properties: { | ||
// however both maxLengh and maxItems are required in nested objects and arrays | ||
type: { type: 'string', maxLength: 10 } | ||
}, | ||
}, | ||
}, | ||
names: { | ||
type: 'array', | ||
// uses VectorView for an array of variable length elements | ||
// if btype is set to vector | ||
btype: 'vector', | ||
items: { type: 'string' }, | ||
}, | ||
```typescript | ||
const Names = View.create<Array<string | undefined>>({ | ||
type: "array", | ||
btype: "vector", | ||
items: { | ||
type: "string", | ||
}, | ||
// required fields are always present and can have default values | ||
required: ['id'], | ||
}); | ||
const person0 = Person.from({}); | ||
person.get('id') | ||
//=> 10 | ||
person.get('name') | ||
//=> name | ||
// create a person with one pet | ||
const person1 = Person.from({ id: 1, name: 'Artur', pets: [{ type: 'dog'}] }); | ||
person1.byteLength; | ||
//=> 31 | ||
// create a person with no pets | ||
const person0 = Person.from({ id: 1, name: 'Artur'}); | ||
person0.byteLength; | ||
//=> 18 | ||
person0.get('pets'); | ||
//=> undefined | ||
person0.set('pets', [{ type: 'dog'}]); | ||
person0.get('pets'); | ||
//=> undefined | ||
const person2 = Person.from({ names: ['Arthur', 'Dent', '', 'Arthur Dent']}) | ||
person2.toJSON(); | ||
//=> { id: 10, names: ['Arthur', 'Dent', undefined, 'Arthur Dent']} | ||
const witches = Names.from([ | ||
"Granny Weatherwax", | ||
"Nanny Ogg", | ||
undefined, | ||
"Magrat Garlick", | ||
]); | ||
witches.byteLength; //=> 64 | ||
witches.get(0); //=> "Granny Weatherwax" | ||
witches.get(2); //=> undefined | ||
``` | ||
For performance reasons, MapView uses a single buffer for serialization, thus, limiting the maximum size of a view. | ||
The buffer is inherited from `VariableView` class and the default is 8192 bytes, if you expect bigger views, please set the desired size in `VariableView.maxLength`. | ||
As with maps, the layout of vectors is calculated upon creation and editing is | ||
limited to the items present upon creation. | ||
#### VectorView | ||
VectorView is an ArrayView that supports optional elements (i.e. `undefined`) and elements of variable length, such as MapView or StringView. | ||
VectorView stores offsets inside the view itself resulting in an overhead of 4 * (_n_ + 2) bytes where _n_ is the number of elements in the view. | ||
Like MapView, VectorView has limited editablity: the layout of an instance is calculated once upon creation, | ||
hence, setting absent elements or resizing existing elements is not possible. | ||
```javascript | ||
const { MapViewMixin, VectorViewMixin, TypeViewMixin } = require('structurae'); | ||
const SparseArrayView = VectorViewMixin(TypeViewMixin('uint8')); | ||
SparseArrayView.from([1, , 2, null]).toJSON(); | ||
//=> [1, undefined, 2, undefined] | ||
#### Strings | ||
const MapVector = VectorViewMixin(MapViewMixin({ | ||
$id: 'SomeMap', | ||
btype: 'map', | ||
properties: { | ||
id: { type: 'integer' }, | ||
name: { type: 'string' }, | ||
}, | ||
})); | ||
const mapVector = MapVector.from([{ id: 1 }, null, { name: 'abc'}]); | ||
mapVector.size; | ||
//=> 3 | ||
mapVector.get(0); | ||
//=> { id: 1 }; | ||
mapVector.toJSON(); | ||
//=> [{ id: 1 }, undefined, { name: 'abc'}] | ||
``` | ||
The protocol handles strings through StringView, an extension of DataView that | ||
handles string serialization. It also offers a handful of convenience methods to | ||
operate on encoded strings so that some common operations can be performed | ||
without decoding the string: | ||
Like MapView, VectorView uses for serialization the default buffer inherited from `VariableView`, if you expect your | ||
vectors to exceed the default 8192 bytes in length, please set the desired maximum length in `VariableView.maxLength`. | ||
#### StringView | ||
Encoding API (available both in modern browsers and Node.js) allows us to convert JavaScript strings to | ||
(and from) UTF-8 encoded stream of bytes represented by a Uint8Array. StringView extends Uint8Array with string related methods | ||
and relies on Encoding API internally for conversions. | ||
You can use `StringView.fromString` to create an encoded string, and `StringView#toString` to convert it back to a string: | ||
```javascript | ||
const { StringView } = require('structurae'); | ||
import { StringView } from "structurae"; | ||
const stringView = StringView.from('abc😀a'); | ||
let stringView = StringView.from("abc😀a"); | ||
//=> StringView [ 97, 98, 99, 240, 159, 152, 128, 97 ] | ||
stringView.toString(); | ||
//=> 'abc😀a' | ||
stringView == 'abc😀a'; | ||
//=> "abc😀a" | ||
stringView == "abc😀a"; | ||
//=> true | ||
``` | ||
While the array itself holds code points, StringView provides methods to operate on characters of the underlying string: | ||
```javascript | ||
const stringView = StringView.from('abc😀'); | ||
stringView = StringView.from("abc😀"); | ||
stringView.length; // length of the view in bytes | ||
@@ -416,139 +364,116 @@ //=> 8 | ||
stringView.charAt(0); // get the first character in the string | ||
//=> 'a' | ||
//=> "a" | ||
stringView.charAt(3); // get the fourth character in the string | ||
//=> '😀' | ||
[...stringView.characters()] // iterate over characters | ||
//=> ['a', 'b', 'c', '😀'] | ||
//=> "😀" | ||
[...stringView.characters()]; // iterate over characters | ||
//=> ["a", "b", "c", "😀"] | ||
stringView.substring(0, 4); | ||
//=> 'abc😀' | ||
``` | ||
//=> "abc😀" | ||
StringView also offers methods for searching and in-place changing the underlying string without decoding: | ||
```javascript | ||
const stringView = StringView.from('abc😀a'); | ||
const searchValue = StringView.from('😀'); | ||
stringView = StringView.from("abc😀a"); | ||
const searchValue = StringView.from("😀"); | ||
stringView.search(searchValue); // equivalent of String#indexOf | ||
//=> 3 | ||
const replacement = StringView.from('d'); | ||
const replacement = StringView.from("d"); | ||
stringView.replace(searchValue, replacement).toString(); | ||
//=> 'abcda' | ||
//=> "abcda" | ||
stringView.reverse().toString(); | ||
//=> 'adcba' | ||
//=> "adcba" | ||
``` | ||
#### BinaryProtocol | ||
When transferring our buffers encoded with views we can often rely on meta information to know what kind of ObjectView | ||
to use in order to decode a received buffer, e.g. let's say we have a `HouseView` class to encode/decode all buffers | ||
that go through `/houses` route. However, sometimes we need our ObjectViews to carry within themselves an information | ||
as to what kind of ObjectView was used to encode them. To do that, we can prepend or tag each view with a value indicating its | ||
class, i.e. add a field that defaults to a certain value for each view class. Now upon receiving a buffer we can read that field | ||
using the DataView and convert it into an appropriate view. The `BinaryProtocol` does all that under the hood serving as a helper class | ||
to remove boilerplate, plus it creates the necessary ObjectView classes from schemas for when we are not concerned too much about individual | ||
classes: | ||
#### Tagged Objects | ||
```javascript | ||
const { BinaryProtocol } = require('structurae'); | ||
When transferring our buffers encoded with views we can often rely on meta | ||
information to know what kind of view to use in order to decode a received | ||
buffer. However, sometimes we might want our views to carry that information | ||
within themselves. To do that, we can prepend or tag each view with a value | ||
indicating its class, i.e. add a field that defaults to a certain value for each | ||
view class. Now upon receiving a buffer we can read that field using the | ||
DataView and convert it into an appropriate view. | ||
const protocol = new BinaryProtocol({ | ||
0: { | ||
$id: 'Person', | ||
type: 'object', | ||
properties: { | ||
age: { type: 'integer', btype: 'int8' }, | ||
name: { type: 'string', length: 10 }, | ||
} | ||
The `View` class offers a few convenience methods to simplify this process: | ||
```typescript | ||
import { View } from "structurae"; | ||
interface Dog { | ||
tag: 0; | ||
name: string; | ||
} | ||
interface Cat { | ||
tag: 1; | ||
name: string; | ||
} | ||
const DogView = View.create<Dog>({ | ||
type: "object", | ||
$id: "Dog", | ||
properties: { | ||
// the tag field with default value | ||
tag: { type: "number", btype: "uint8", default: 0 } | ||
name: { type: "string", maxLength: 10 } | ||
}, | ||
1: { | ||
$id: 'Items', | ||
type: 'object', | ||
properties: { | ||
id: { type: 'integer', btype: 'uint32' }, | ||
items: { | ||
type: 'array', | ||
maxItems: 3, | ||
items: { type: 'string', maxLength: 10 }, | ||
}, | ||
}, | ||
}); | ||
const CatView = View.create<Cat>({ | ||
type: "object", | ||
$id: "Cat", | ||
properties: { | ||
// the tag field with default value | ||
tag: { type: "number", btype: "uint8", default: 1 } | ||
name: { type: "string", maxLength: 10 } | ||
}, | ||
}); | ||
const person = protocol.encode({ tag: 0, age: 100, name: 'abc' }); | ||
//=> ObjectView (12) | ||
protocol.decode(person.buffer) | ||
//=> { tag: 0, age: 100, name: 'abc' } | ||
const personView = protocol.view(person.buffer); | ||
personView.get('age'); | ||
//=> 100 | ||
const item = protocol.encode({ tag: 1, id: 10, items: ['a', 'b', 'c'] }); | ||
//=> ObjectView (35) | ||
protocol.decode(item.buffer) | ||
//=> { tag: 1, id: 10, items: ['a', 'b', 'c'] } | ||
// now we can encode tagged objects without specifying views first: | ||
const animal = View.encode({ tag: 0, name: "Gaspode" }); | ||
// and decode them: | ||
View.decode(animal) //=> { tag: 0, name: "Gaspode" } | ||
``` | ||
We can use references to existing ObjectViews, however, those views should have a tag field and appropriate default value specified. | ||
By default, the tag field is named `tag` and has the type of `uint8`. A | ||
different tag name can be specified in `View.tagName`. | ||
```javascript | ||
const View = ObjectViewMixin({ | ||
$id: 'Items', | ||
type: 'object', | ||
properties: { | ||
tag: { type: 'integer', btype: 'uint8', default: 1 }, | ||
id: { type: 'integer', btype: 'uint32' }, | ||
items: { | ||
type: 'array', | ||
maxItems: 3, | ||
items: { type: 'string', maxLength: 10 }, | ||
}, | ||
$id: "Items", | ||
type: "object", | ||
properties: { | ||
tagId: { type: "integer", btype: "uint32", default: 1 }, | ||
id: { type: "integer", btype: "uint32" }, | ||
items: { | ||
type: "array", | ||
maxItems: 3, | ||
items: { type: "string", maxLength: 10 }, | ||
}, | ||
}); | ||
const protocol = new BinaryProtocol({ | ||
0: { | ||
$id: 'Person', | ||
type: 'object', | ||
properties: { | ||
age: { type: 'integer', btype: 'int8' }, | ||
name: { type: 'string', length: 10 }, | ||
} | ||
}, | ||
1: { $ref: '#Items' }, | ||
}); | ||
``` | ||
By default, the tag field is named `tag` and has the type of `uint8`, both can be changed and provided as second and third parameters to protocol constructor. | ||
```javascript | ||
const View = ObjectViewMixin({ | ||
$id: 'Items', | ||
type: 'object', | ||
properties: { | ||
tagId: { type: 'integer', btype: 'uint32', default: 1 }, | ||
id: { type: 'integer', btype: 'uint32' }, | ||
items: { | ||
type: 'array', | ||
maxItems: 3, | ||
items: { type: 'string', maxLength: 10 }, | ||
const protocol = new BinaryProtocol( | ||
{ | ||
0: { | ||
$id: "Person", | ||
type: "object", | ||
properties: { | ||
age: { type: "integer", btype: "int8" }, | ||
name: { type: "string", length: 10 }, | ||
}, | ||
}, | ||
}); | ||
const protocol = new BinaryProtocol({ | ||
0: { | ||
$id: 'Person', | ||
type: 'object', | ||
properties: { | ||
age: { type: 'integer', btype: 'int8' }, | ||
name: { type: 'string', length: 10 }, | ||
} | ||
1: { $ref: "#Items" }, | ||
}, | ||
1: { $ref: '#Items' }, | ||
}, 'tagId', 'uint32'); | ||
"tagId", | ||
"uint32", | ||
); | ||
``` | ||
### Bit Structures | ||
#### BitField & BigBitField | ||
BitField and BigBitField use JavaScript Numbers and BigInts respectively as bitfields to store and operate on data using bitwise operations. | ||
By default, BitField operates on 31 bit long bitfield where bits are indexed from least significant to most: | ||
BitField and BigBitField use JavaScript Numbers and BigInts respectively as | ||
bitfields to store and operate on data using bitwise operations. By default, | ||
BitField operates on 31 bit long bitfield where bits are indexed from least | ||
significant to most: | ||
```javascript | ||
const { BitField } = require('structurae'); | ||
import { BitField } from "structurae"; | ||
@@ -564,12 +489,14 @@ const bitfield = new BitField(29); // 29 === 0b11101 | ||
You can extend BitField or BigBitField directly or use BitFieldMixin with your own schema by specifying field names and their respective sizes in bits: | ||
You can use BitFieldMixin or BigBitFieldMixin with your own schema by specifying | ||
field names and their respective sizes in bits: | ||
```javascript | ||
const Field = BitFieldMixin({ width: 8, height: 8 }); | ||
const field = new Field({ width: 100, height: 200 }); | ||
field.get('width'); | ||
field.get("width"); | ||
//=> 100; | ||
field.get('height'); | ||
field.get("height"); | ||
//=> 200 | ||
field.set('width', 18); | ||
field.get('width'); | ||
field.set("width", 18); | ||
field.get("width"); | ||
//=> 18 | ||
@@ -580,27 +507,19 @@ field.toObject(); | ||
You can forgo specifying sizes if your field size is 1 bit: | ||
```javascript | ||
const Privileges = BitFieldMixin(['user', 'moderator', 'administrator']); | ||
const privileges = new Privileges(0); | ||
privileges.set('user').set('moderator'); | ||
privileges.has('user', 'moderator'); | ||
//=> true | ||
privileges.set('moderator', 0).has('moderator'); | ||
//=> false | ||
``` | ||
If the total size of your fields exceeds 31 bits, use BigBitFieldMixin that | ||
internally uses a BigInt to represent the resulting number, however, you can | ||
still use normal numbers to set each field and get their value as a number as | ||
well: | ||
If the total size of your fields exceeds 31 bits, BitFieldMixin will switch to BigBitField that internally uses | ||
a BigInt to represent the resulting number, however, you can still use normal numbers to set each field | ||
and get their value as a number as well: | ||
```javascript | ||
const LargeField = BitFieldMixin({ width: 20, height: 20 }); | ||
const largeField = new LargeField([1048576, 1048576]); | ||
largeField.value | ||
largeField.value; | ||
//=> 1099512676352n | ||
largeField.set('width', 1000).get('width') | ||
largeField.set("width", 1000).get("width"); | ||
//=> 1000 | ||
``` | ||
If you have to add more fields to your schema later on, you do not have to re-encode your existing values, just add new fields | ||
at the end of your new schema: | ||
If you have to add more fields to your schema later on, you do not have to | ||
re-encode your existing values, just add new fields at the end of your new | ||
schema: | ||
@@ -614,14 +533,16 @@ ```javascript | ||
const newField = new NewField(oldField); | ||
newField.get('width'); | ||
newField.get("width"); | ||
//=> 20 | ||
newField.get('height'); | ||
newField.get("height"); | ||
//=> 1 | ||
newField.set('weight', 100).get('weight'); | ||
newField.set("weight", 100).get("weight"); | ||
//=> 100 | ||
``` | ||
If you only want to encode or decode a set of field values without creating an instance, you can do so by using static methods | ||
`BitField.encode` and `BitField.decode` respectively: | ||
If you only want to encode or decode a set of field values without creating an | ||
instance, you can do so by using static methods `BitField.encode` and | ||
`BitField.decode` respectively: | ||
```javascript | ||
const Field = BitFieldMixin({ width: 7, height: 1 }) | ||
const Field = BitFieldMixin({ width: 7, height: 1 }); | ||
@@ -638,4 +559,5 @@ Field.encode([20, 1]); | ||
If you don't know beforehand how many bits you need for your field, you can call `BitField.getMinSize` with the maximum | ||
possible value of your field to find out: | ||
If you don't know beforehand how many bits you need for your field, you can call | ||
`BitField.getMinSize` with the maximum possible value of your field to find out: | ||
```javascript | ||
@@ -647,4 +569,7 @@ BitField.getMinSize(100); | ||
For performance sake, BitField doesn't check the size of values being set and setting values that exceed the specified | ||
field size will lead to undefined behavior. If you want to check whether values fit their respective fields, you can use `BitField.isValid`: | ||
For performance sake, BitField doesn't check the size of values being set and | ||
setting values that exceed the specified field size will lead to undefined | ||
behavior. If you want to check whether values fit their respective fields, you | ||
can use `BitField.isValid`: | ||
```javascript | ||
@@ -659,3 +584,5 @@ const Field = BitFieldMixin({ width: 7, height: 1 }); | ||
`BitField#match` (and its static variation `BitField.match`) can be used to check values of multiple fields at once: | ||
`BitField#match` (and its static variation `BitField.match`) can be used to | ||
check values of multiple fields at once: | ||
```javascript | ||
@@ -674,4 +601,6 @@ const Field = BitFieldMixin({ width: 7, height: 1 }); | ||
If you have to check multiple BitField instances for the same values, create a special matcher with `BitField.getMatcher` | ||
and use it in the match method, that way each check will require only one bitwise operation and a comparison: | ||
If you have to check multiple BitField instances for the same values, create a | ||
special matcher with `BitField.getMatcher` and use it in the match method, that | ||
way each check will require only one bitwise operation and a comparison: | ||
```javascript | ||
@@ -687,23 +616,32 @@ const Field = BitFieldMixin({ width: 7, height: 1 }); | ||
#### BitArray | ||
BitArray uses Uint32Array as an array or vector of bits. It's a simpler version of BitField that only sets and checks individual bits: | ||
BitArray uses Uint32Array as an array or vector of bits. It's a simpler version | ||
of BitField that only sets and checks individual bits: | ||
```javascript | ||
const array = new BitArray(10); | ||
array.getBit(0) | ||
array.getBit(0); | ||
//=> 0 | ||
array.setBit(0).getBit(0); | ||
//=> 1 | ||
array.size | ||
array.size; | ||
//=> 10 | ||
array.length | ||
array.length; | ||
//=> 1 | ||
``` | ||
BitArray is the base class for [Pool](https://github.com/zandaqo/structurae#Pool) and [RankedBitArray](https://github.com/zandaqo/structurae#RankedBitArray) classes. | ||
It's useful in cases where one needs more bits than can be stored in a number, but doesn't want to use BigInts as it is done by [BitField](https://github.com/zandaqo/structurae#BitField). | ||
BitArray is the base class for | ||
[Pool](https://github.com/zandaqo/structurae#Pool) and | ||
[RankedBitArray](https://github.com/zandaqo/structurae#RankedBitArray) classes. | ||
It's useful in cases where one needs more bits than can be stored in a number, | ||
but doesn't want to use BigInts as it is done by | ||
[BitField](https://github.com/zandaqo/structurae#BitField). | ||
#### Pool | ||
Implements a fast algorithm to manage availability of objects in an object pool using a BitArray. | ||
Implements a fast algorithm to manage availability of objects in an object pool | ||
using a BitArray. | ||
```javascript | ||
const { Pool } = require('structurae'); | ||
const { Pool } = require("structurae"); | ||
@@ -729,6 +667,8 @@ // create a pool of 1600 indexes | ||
#### RankedBitArray | ||
RankedBitArray is an extension of BitArray with methods to efficiently calculate rank and select. | ||
The rank is calculated in constant time where as select has O(logN) time complexity. | ||
This is often used as a basic element in implementing succinct data structures. | ||
RankedBitArray is an extension of BitArray with methods to efficiently calculate | ||
rank and select. The rank is calculated in constant time where as select has | ||
O(logN) time complexity. This is often used as a basic element in implementing | ||
succinct data structures. | ||
```javascript | ||
@@ -746,117 +686,127 @@ const array = new RankedBitArray(10); | ||
### Graphs | ||
Structurae offers classes that implement Adjacency List (`UnweightedAdjacencyList`, `WeightedAdjacencyList`) and Adjacency Matrix (`UnweightedAdjacencyMatrix`, | ||
`WeightedAdjacencyMatrix`) as basic primitives to represent graphs using a TypedArray, and the `Graph` class that extends the adjacency structures to offer methods for traversing | ||
graphs (BFS, DFS), pathfinding (Dijkstra, Bellman-Ford), and spanning tree construction (BFS, Prim). | ||
#### Adjacency Lists | ||
`UnweightedAdjacencyList` and `WeightedAdjacencyList` implement Adjacency List data structure extending a TypedArray class. | ||
The adjacency list requires less storage space: number of vertices + number of edges (for an unweighted list) or number of edges * 2 (for a weighted list). | ||
However, adding and removing edges is much slower since it involves shifting/unshifting values in the underlying typed array. | ||
```javascript | ||
const { UnweightedAdjacencyList, WeightedAdjacencyListMixin } = require('structurae'); | ||
Structurae offers classes that implement adjacency list (`AdjacencyList`) and | ||
adjacency matrix (`AdjacencyMatrixUnweightedDirected`, | ||
`AdjacencyMatrixUnweightedUndirected`, `AdjacencyMatrixWeightedDirected`, | ||
`AdjacencyMatrixWeightedUnirected`) as basic primitives to represent graphs | ||
using TypedArrays, and the `Graph` class that extends the adjacency structures | ||
to offer methods for traversing graphs (BFS, DFS), pathfinding (Dijkstra, | ||
Bellman-Ford), and spanning tree construction (BFS, Prim). | ||
const WeightedAdjacencyList = WeightedAdjacencyListMixin(Int32Array); | ||
#### Adjacency Structures | ||
const unweightedGraph = new UnweightedAdjacencyList({ vertices: 6, edges: 6 }); | ||
const weightedGraph = new WeightedAdjacencyList({ vertices: 6, edges: 6 }); | ||
`AdjacencyList` implements adjacency list data structure extending a TypedArray | ||
class. The adjacency list requires less storage space: number of vertices + | ||
number of edges * 2 (for a weighted list). However, adding and removing edges is | ||
much slower since it involves shifting/unshifting values in the underlying typed | ||
array. | ||
// the length of an unweighted graph is vertices + edges + 1 | ||
unweightedGraph.length; | ||
//=> 13 | ||
```javascript | ||
import { AdjacencyListMixin } from "structurae"; | ||
const List = AdjacencyListMixin(Int32Array); | ||
const graph = List.create(6, 6); | ||
// the length of a weighted graph is vertices + edges * 2 + 1 | ||
weightedGraph.length; | ||
graph.length; | ||
//=> 19 | ||
unweightedGraph.addEdge(0, 1).addEdge(0, 2).addEdge(2, 4).addEdge(2, 5); | ||
unweightedGraph.hasEdge(0, 1); | ||
graph.addEdge(0, 1, 5); | ||
graph.addEdge(0, 2, 1); | ||
graph.addEdge(2, 4, 1); | ||
graph.addEdge(2, 5, 2); | ||
graph.hasEdge(0, 1); | ||
//=> true | ||
unweightedGraph.hasEdge(0, 4); | ||
graph.hasEdge(0, 4); | ||
//=> false | ||
unweightedGraph.outEdges(2); | ||
graph.outEdges(2); | ||
//=> [4, 5] | ||
unweightedGraph.inEdges(2); | ||
graph.inEdges(2); | ||
//=> [0] | ||
weightedGraph.addEdge(0, 1, 5); | ||
weightedGraph.hasEdge(0, 1); | ||
graph.hasEdge(0, 1); | ||
//=> true | ||
weightedGraph.getEdge(0, 1); | ||
graph.getEdge(0, 1); | ||
//=> 5 | ||
``` | ||
Since the maximum amount of egdes is limited to the number specified at creation, adding edges can overflow throwing a RangeError. | ||
If that's a possibility, use `isFull` to check if the limit is reached before adding. If additional edges are required, one can use the | ||
`grow` method specifying the amount of additional vertices and edges required. `grow` creates a copy of the graph with increased limits: | ||
```javascript | ||
graph.length | ||
//=> 13 | ||
const biggerGraph = graph.grow(4, 10); // add 4 vertices and 10 edges | ||
biggerGraph.length | ||
//=> 27 | ||
``` | ||
Since the maximum amount of egdes in AdjacencyList is limited to the number | ||
specified at creation, adding edges can overflow throwing a RangeError. If | ||
that's a possibility, use `AdjacencyList#isFull` method to check if the limit is | ||
reached before adding an edge. | ||
Adjacency lists can be created from an existing adjacency matrices or grids using the `fromGrid` method. | ||
`AdjacencyMatrixUnweightedDirected` and `AdjacencyMatrixUnweightedUndirected` | ||
implement adjacency matrix data structure for unweighted graphs representing | ||
each edge by a single bit in an underlying ArrayBuffer. | ||
#### Adjacency Matrices | ||
`UnweightedAdjacencyMatrix` and `WeightedAdjacencyMatrix` build on Grid classes extending them to implement Adjacency Matrix data structure | ||
using TypedArrays. They offer the same methods to operate on edges as the adjacency list structures described above. | ||
`AdjacencyMatrixWeightedDirected` and `AdjacencyMatrixWeightedUnirected` | ||
implement adjacency matrix for weighted graphs extending a given TypedArray to | ||
store the weights akin to [Grid](https://github.com/zandaqo/structurae#Grid). | ||
`UnweightedAdjacencyMatrix` extends [BinaryGrid](https://github.com/zandaqo/structurae#BinaryGrid) to represent | ||
an unweighted graph in the densest possible way: each edge is represented by a single bit in an underlying ArrayBuffer. | ||
For example, to represent a graph with 80 vertices as an Adjacency Matrix we need 80 * 80 bits or 800 bytes. UnweightedAdjacencyMatrix will | ||
will create an ArrayBuffer of that size, "view" it as Uint16Array (of length 400) and operate on edges using bitwise operations. | ||
```javascript | ||
import { | ||
AdjacencyMatrixUnweightedDirected, | ||
AdjacencyMatrixWeightedDirectedMixin, | ||
} from "structurae"; | ||
`WeightedAdjacencyMatrix` extends [Grid](https://github.com/zandaqo/structurae#Grid) (for directed graphs) | ||
or [SymmetricGrid](https://github.com/zandaqo/structurae#SymmetricGrid) (for undirected) to handle weighted graphs. | ||
```javascript | ||
const { UnweightedAdjacencyMatrix, WeightedAdjacencyMatrixMixin } = require('structurae'); | ||
// creates a class for directed graphs that uses Int32Array for edge weights | ||
const WeightedAdjacencyMatrix = WeightedAdjacencyMatrixMixin(Int32Array, true); | ||
const Matrix = AdjacencyMatrixWeightedDirectedMixin(Int32Array); | ||
const unweightedGraph = new UnweightedAdjacencyMatrix({ vertices: 6 }); | ||
unweightedGraph.addEdge(0, 1).addEdge(0, 2).addEdge(0, 3).addEdge(2, 4).addEdge(2, 5); | ||
unweightedGraph.hasEdge(0, 1); | ||
const unweightedMatrix = new AdjacencyMatrixUnweightedDirected.create(6); | ||
unweightedMatrix.addEdge(0, 1); | ||
unweightedMatrix.addEdge(0, 2); | ||
unweightedMatrix.addEdge(0, 3); | ||
unweightedMatrix.addEdge(2, 4); | ||
unweightedMatrix.addEdge(2, 5); | ||
unweightedMatrix.hasEdge(0, 1); | ||
//=> true | ||
unweightedGraph.hasEdge(0, 4); | ||
unweightedMatrix.hasEdge(0, 4); | ||
//=> false | ||
unweightedGraph.outEdges(2); | ||
unweightedMatrix.outEdges(2); | ||
//=> [4, 5] | ||
unweightedGraph.inEdges(2); | ||
unweightedMatrix.inEdges(2); | ||
//=> [0] | ||
const weightedGraph = new WeightedAdjacencyMatrix({ vertices: 6, pad: -1 }); | ||
weightedGraph.addEdge(0, 1, 3); | ||
weightedGraph.hasEdge(0, 1); | ||
const weightedMatrix = Matrix.create(6); | ||
weightedMatrix.addEdge(0, 1, 3); | ||
weightedMatrix.hasEdge(0, 1); | ||
//=> true | ||
weightedGraph.hasEdge(1, 0); | ||
weightedMatrix.hasEdge(1, 0); | ||
//=> false | ||
weightedGraph.getEdge(1, 0); | ||
weightedMatrix.getEdge(1, 0); | ||
//=> 3 | ||
``` | ||
``` | ||
#### Graph | ||
`Graph` extends a provided adjacency structure with methods for traversing, pathfinding, and spanning tree construction that use various | ||
graph algorithms. | ||
`Graph` extends a provided adjacency structure with methods for traversing, | ||
pathfinding, and spanning tree construction that use various graph algorithms. | ||
```javascript | ||
const { GraphMixin, UnweightedAdjacencyList, WeightedAdjacencyMatrixMixin } = require('structurae'); | ||
import { | ||
AdjacencyMatrixUnweightedDirected, | ||
AdjacencyMatrixWeightedDirectedMixin, | ||
GraphMixin, | ||
} from "structurae"; | ||
// create a graph for directed unweighted graphs that use adjacency list structure | ||
const UnweightedGraph = GraphMixin(UnweightedAdjacencyList); | ||
const UnweightedGraph = GraphMixin(AdjacencyMatrixUnweightedDirected); | ||
// for directed weighted graphs that use adjacency matrix structure | ||
const WeightedGraph = GraphMixin(WeightedAdjacencyMatrixMixin(Int32Array)); | ||
const WeightedGraph = GraphMixin( | ||
AdjacencyMatrixWeightedDirectedMixin(Int32Array), | ||
); | ||
``` | ||
The traversal is done by a generator function `Graph#traverse` that can be configured to use Breadth-First or Depth-First traversal, | ||
as well as returning vertices on various stages of processing, i.e. only return vertices that are fully processed (`black`), or being | ||
processed (`gray`), or just encountered (`white`): | ||
The traversal is done by a generator function `Graph#traverse` that can be | ||
configured to use Breadth-First or Depth-First traversal, as well as returning | ||
vertices on various stages of processing, i.e. only return vertices that are | ||
fully processed (`black`), or being processed (`gray`), or just encountered | ||
(`white`): | ||
```javascript | ||
const graph = new WeightedGraph({ vertices: 6, edges: 12 }); | ||
graph.addEdge(0, 1, 3).addEdge(0, 2, 2).addEdge(0, 3, 1).addEdge(2, 4, 8).addEdge(2, 5, 6); | ||
const graph = WeightedGraph.create(6); | ||
graph.addEdge(0, 1, 3); | ||
graph.addEdge(0, 2, 2); | ||
graph.addEdge(0, 3, 1); | ||
graph.addEdge(2, 4, 8); | ||
graph.addEdge(2, 5, 6); | ||
@@ -871,3 +821,3 @@ // a BFS traversal results | ||
// BFS yeilding only non-encountered ('white') vertices starting from 0 | ||
// BFS yeilding only non-encountered ("white") vertices starting from 0 | ||
[...graph.traverse(false, 0, false, true)]; | ||
@@ -877,5 +827,7 @@ //=> [1, 2, 3, 4, 5] | ||
`Graph#path` returns the list of vertices constituting the shortest path between two given vertices. By default, the class uses | ||
BFS based search for unweighted graphs, and Bellman-Ford algorithm for weighted graphs. However, the method can be configured to use | ||
other algorithms by specifying arguments of the function: | ||
`Graph#path` returns the list of vertices constituting the shortest path between | ||
two given vertices. By default, the class uses BFS based search for unweighted | ||
graphs, and Bellman-Ford algorithm for weighted graphs. However, the method can | ||
be configured to use other algorithms by specifying arguments of the function: | ||
```javascript | ||
@@ -888,58 +840,77 @@ graph.path(0, 5); // uses Bellman-Ford by default | ||
### Grids | ||
#### BinaryGrid | ||
BinaryGrid creates a grid or 2D matrix of bits and provides methods to operate on it: | ||
BinaryGrid creates a grid or 2D matrix of bits and provides methods to operate | ||
on it: | ||
```javascript | ||
const { BinaryGrid } = require('structurae'); | ||
import { BinaryGrid } from "structurae"; | ||
const bitGrid = new BinaryGrid({ rows: 2, columns: 8 }); | ||
bitGrid.set(0, 0).set(0, 2).set(0, 5); | ||
bitGrid.get(0, 0); | ||
// create a grid of 2 rows and 8 columns | ||
const bitGrid = BinaryGrid.create(2, 8); | ||
bitGrid.setValue(0, 0).setValue(0, 2).setValue(0, 5); | ||
bitGrid.getValue(0, 0); | ||
//=> 1 | ||
bitGrid.get(0, 1); | ||
bitGrid.getValue(0, 1); | ||
//=> 0 | ||
bitGrid.get(0, 2); | ||
bitGrid.getValue(0, 2); | ||
//=> 1 | ||
``` | ||
BinaryGrid packs bits into numbers like [BitField](https://github.com/zandaqo/structurae#BitField) | ||
and holds them in an ArrayBuffer, thus occupying the smallest possible space. | ||
BinaryGrid packs bits into numbers like | ||
[BitField](https://github.com/zandaqo/structurae#BitField) and holds them in an | ||
ArrayBuffer, thus occupying the smallest possible space. | ||
#### Grid | ||
Grid extends a provided indexed collection class (Array or TypedArrays) to efficiently handle 2 dimensional data without creating | ||
nested arrays. Grid "unrolls" nested arrays into a single array and pads its "columns" to the nearest power of 2 in order to employ | ||
quick lookups with bitwise operations. | ||
Grid extends a provided Array or TypedArray class to efficiently handle 2 | ||
dimensional data without creating nested arrays. Grid "unrolls" nested arrays | ||
into a single array and pads its "columns" to the nearest power of 2 in order to | ||
employ quick lookups with bitwise operations. | ||
```javascript | ||
const { GridMixin } = require('structurae'); | ||
import { GridMixin } from "structurae"; | ||
const ArrayGrid = GridMixin(Array); | ||
// create a grid of 5 rows and 4 columns filled with 0 | ||
const grid = new ArrayGrid({rows: 5, columns: 4 }); | ||
grid.length | ||
// create a grid of 5 rows and 4 columns | ||
const grid = ArrayGrid.create(5, 4); | ||
grid.length; | ||
//=> 20 | ||
grid[0] | ||
grid[0]; | ||
//=> 0 | ||
// send data as the second parameter to instantiate a grid with data: | ||
const dataGrid = new ArrayGrid({rows: 5, columns: 4 }, [1, 2, 3, 4, 5, 6, 7, 8]); | ||
grid.length | ||
//=> 20 | ||
grid[0] | ||
//=> 0 | ||
// create a grid from existing data: | ||
const dataGrid = new ArrayGrid([ | ||
1, | ||
2, | ||
3, | ||
4, | ||
5, | ||
6, | ||
7, | ||
8, | ||
]); | ||
// set columns number: | ||
dataGrid.columns = 4; | ||
dataGrid.getValue(1, 0); | ||
//=> 5 | ||
// you can change dimensions of the grid by setting columns number at any time: | ||
dataGrid.columns = 2; | ||
dataGrid.getValue(1, 0); | ||
//=> 3 | ||
``` | ||
You can get and set elements using their row and column indexes: | ||
```javascript | ||
grid | ||
//=> ArrayGrid [1, 2, 3, 4, 5, 6, 7, 8] | ||
grid.get(0, 1); | ||
grid.getValue(0, 1); | ||
//=> 2 | ||
grid.set(0, 1, 10); | ||
grid.get(0, 1); | ||
grid.setValue(0, 1, 10); | ||
grid.getValue(0, 1); | ||
//=> 10 | ||
// use `getIndex` to get an array index of an element at given coordinates | ||
@@ -951,12 +922,14 @@ grid.getIndex(0, 1); | ||
grid.getCoordinates(0); | ||
//=> { row: 0, column: 0 } | ||
//=> [0, 0] | ||
grid.getCoordinates(1); | ||
//=> { row: 0, column: 1 } | ||
//=> [0, 1] | ||
``` | ||
A grid can be turned to and from an array of nested arrays using respectively `Grid.fromArrays` and `Grid#toArrays` methods: | ||
A grid can be turned to and from an array of nested arrays using respectively | ||
`Grid.fromArrays` and `Grid#toArrays` methods: | ||
```javascript | ||
const grid = ArrayGrid.fromArrays([[1,2], [3, 4]]); | ||
const grid = ArrayGrid.fromArrays([[1, 2], [3, 4]]); | ||
//=> ArrayGrid [ 1, 2, 3, 4 ] | ||
grid.get(1, 1); | ||
grid.getValue(1, 1); | ||
//=> 4 | ||
@@ -968,3 +941,3 @@ | ||
//=> ArrayGrid [ 1, 2, 0, 0, 3, 4, 5, 0 ] | ||
grid.get(1, 1); | ||
grid.getValue(1, 1); | ||
//=> 4 | ||
@@ -981,19 +954,25 @@ | ||
#### SymmetricGrid | ||
SymmetricGrid is a Grid that offers a more compact way of encoding symmetric or triangular square matrices using half as much space. | ||
SymmetricGrid is a Grid that offers a more compact way of encoding symmetric or | ||
triangular square matrices using half as much space. | ||
```javascript | ||
const { SymmetricGrid } = require('structurae'); | ||
import { SymmetricGrid } from "structurae"; | ||
const grid = new ArrayGrid({rows: 100, columns: 100 }); | ||
const grid = ArrayGrid.create(100, 100); | ||
grid.length; | ||
//=> 12800 | ||
const symmetricGrid = new SymmetricGrid({ rows: 100 }); | ||
const symmetricGrid = SymmetricGrid.create(100); | ||
symmetricGrid.length; | ||
//=> 5050 | ||
``` | ||
Since the grid is symmetric, it returns the same value for a given pair of coordinates regardless of their position: | ||
Since the grid is symmetric, it returns the same value for a given pair of | ||
coordinates regardless of their position: | ||
```javascript | ||
symmetricGrid.set(0, 5, 10); | ||
symmetricGrid.get(0, 5); | ||
symmetricGrid.setValue(0, 5, 10); | ||
symmetricGrid.getValue(0, 5); | ||
//=> 10 | ||
symmetricGrid.get(5, 0); | ||
symmetricGrid.getValue(5, 0); | ||
//=> 10 | ||
@@ -1003,16 +982,23 @@ ``` | ||
### Sorted Structures | ||
#### BinaryHeap | ||
BinaryHeap extends built-in Array to implement the Binary Heap data structure. | ||
All the mutating methods (push, shift, splice, etc.) do so while maintaining the valid heap structure. | ||
By default, BinaryHeap implements min-heap, but it can be changed by providing a different comparator function: | ||
BinaryHeap extends built-in Array to implement the binary heap data structure. | ||
All the mutating methods (push, shift, splice, etc.) do so while maintaining the | ||
valid heap structure. By default, BinaryHeap implements min-heap, but it can be | ||
changed by providing a different comparator function: | ||
```javascript | ||
const { BinaryHeap } = require('structurae'); | ||
import { BinaryHeap } from "structurae"; | ||
class MaxHeap extends BinaryHeap {} | ||
MaxHeap.compare = (a, b) => b - a; | ||
MaxHeap.compare = (a, b) => a > b; | ||
``` | ||
In addition to all array methods, BinaryHeap provides a few methods to traverse or change the heap: | ||
In addition to all array methods, BinaryHeap provides a few methods to traverse | ||
or change the heap: | ||
```javascript | ||
const heap = new BinaryHeap(10, 1, 20, 3, 9, 8); | ||
heap[0] | ||
heap[0]; | ||
//=> 1 | ||
@@ -1026,5 +1012,5 @@ heap.left(0); // the left child of the first (minimal) element of the heap | ||
heap.replace(4) // returns the first element and adds a new element in one operation | ||
heap.replace(4); // returns the first element and adds a new element in one operation | ||
//=> 1 | ||
heap[0] | ||
heap[0]; | ||
//=> 3 | ||
@@ -1035,77 +1021,13 @@ heap[0] = 6; | ||
// BinaryHeap [ 4, 6, 8, 10, 9, 20 ] | ||
``` | ||
#### SortedCollection | ||
SortedCollection extends a given built-in indexed collection with methods to efficiently handle sorted data. | ||
```javascript | ||
const { SortedMixin } = require('structurae'); | ||
const SortedInt32Array = SortedMixin(Int32Array); | ||
``` | ||
To create a sorted collection from unsorted array-like objects or items use `from` and `of` static methods respectively: | ||
```js | ||
SortedInt32Array.from(unsorted); | ||
//=> SortedInt32Array [ 2, 3, 4, 5, 9 ] | ||
SortedInt32Array.of(8, 5, 6); | ||
//=> SortedInt32Array [ 5, 6, 8 ] | ||
``` | ||
#### SortedArray | ||
`new SortedInt32Array` behaves the same way as `new Int32Array` and should be used with already sorted elements: | ||
```js | ||
new SortedInt32Array(...[ 1, 2, 3, 4, 8 ]); | ||
//=> SortedInt32Array [ 1, 2, 3, 4, 8 ]; | ||
new SortedInt32Array(2,3,4); | ||
//=> SortedInt32Array [ 2, 3, 4 ]; | ||
``` | ||
SortedArray extends Array with methods to efficiently handle sorted data. The | ||
methods that change the contents of an array do so while preserving the sorted | ||
order: | ||
A custom comparison function can be specified on the collection instance to be used for sorting: | ||
```js | ||
//=> SortedInt32Array [ 2, 3, 4, 5, 9 ] | ||
sortedInt32Array.compare = (a, b) => (a > b ? -1 : a < b ? 1 : 0); | ||
sortedInt32Array.sort(); | ||
//=> SortedInt32Array [ 9, 5, 4, 3, 2 ] | ||
``` | ||
import { SortedArray } from "structurae"; | ||
SortedCollection supports all the methods of its base class: | ||
```javascript | ||
//=> SortedInt32Array [ 2, 3, 4, 5, 9 ] | ||
sortedInt32Array.slice(0, 2) | ||
//=> SortedInt32Array [ 2, 3 ] | ||
sortedInt32Array.set([0, 0, 1]) | ||
//=> SortedInt32Array [ 0, 0, 1, 5, 9 ] | ||
``` | ||
`indexOf` and `includes` use binary search that increasingly outperforms the built-in methods as the size of the collection grows. | ||
SortedCollection provides `isSorted` method to check if the collection is sorted, | ||
and `range` method to get elements of the collection whose values are between the specified range: | ||
```js | ||
//=> SortedInt32Array [ 2, 3, 4, 5, 9 ] | ||
sortedInt32Array.range(3, 5); | ||
// => SortedInt32Array [ 3, 4, 5 ] | ||
sortedInt32Array.range(undefined, 4); | ||
// => SortedInt32Array [ 2, 3, 4 ] | ||
sortedInt32Array.range(4); | ||
// => SortedInt32Array [ 4, 5, 8 ] | ||
// set `subarray` to `true` to use `TypedArray#subarray` for the return value instead of copying it with slice: | ||
sortedInt32Array.range(3, 5, true).buffer === sortedInt32Array.buffer; | ||
// => true; | ||
``` | ||
SortedCollection also provides a set of functions to perform common set operations | ||
and find statistics of any sorted array-like objects without converting them to sorted collection. | ||
Check [API documentation](https://github.com/zandaqo/structurae/blob/master/doc/API.md) for more information. | ||
#### SortedArray | ||
SortedArray extends SortedCollection using built-in Array. | ||
SortedArray supports all the methods of Array as well as those provided by SortedCollection. | ||
The methods that change the contents of an array do so while preserving the sorted order: | ||
```js | ||
const { SortedArray } = require('structurae'); | ||
const sortedArray = new SortedArray(); | ||
@@ -1121,4 +1043,5 @@ sortedArray.push(1); | ||
`uniquify` can be used to remove duplicating elements from the array: | ||
```js | ||
const a = SortedArray.from([ 1, 1, 2, 2, 3, 4 ]); | ||
const a = SortedArray.from([1, 1, 2, 2, 3, 4]); | ||
a.uniquify(); | ||
@@ -1128,3 +1051,5 @@ //=> SortedArray [ 1, 2, 3, 4 ] | ||
If the instance property `unique` of an array is set to `true`, the array will behave as a set and avoid duplicating elements: | ||
If the instance property `unique` of an array is set to `true`, the array will | ||
behave as a set and avoid duplicating elements: | ||
```js | ||
@@ -1139,7 +1064,46 @@ const a = new SortedArray(); | ||
//=> 2 | ||
a | ||
a; | ||
//=> SortedArray [ 1, 2 ] | ||
``` | ||
To create a sorted collection from unsorted array-like objects or items use | ||
`from` and `of` static methods respectively: | ||
```js | ||
SortedArray.from([3, 2, 9, 5, 4]); | ||
//=> SortedArray [ 2, 3, 4, 5, 9 ] | ||
SortedArray.of(8, 5, 6); | ||
//=> SortedArray [ 5, 6, 8 ] | ||
``` | ||
`new SortedArray` behaves the same way as `new Array` and should be used with | ||
already sorted elements: | ||
```js | ||
new SortedArray(...[1, 2, 3, 4, 8]); | ||
//=> SortedArray [ 1, 2, 3, 4, 8 ]; | ||
``` | ||
`indexOf` and `includes` use binary search that increasingly outperforms the | ||
built-in methods as the size of the array grows. | ||
SortedArray provides `isSorted` method to check if the collection is sorted, and | ||
`range` method to get elements of the collection whose values are between the | ||
specified range: | ||
```js | ||
//=> SortedArray [ 2, 3, 4, 5, 9 ] | ||
sortedArray.range(3, 5); | ||
// => SortedArray [ 3, 4, 5 ] | ||
sortedArray.range(undefined, 4); | ||
// => SortedArray [ 2, 3, 4 ] | ||
sortedArray.range(4); | ||
// => SortedArray [ 4, 5, 8 ] | ||
``` | ||
SortedArray also provides a set of functions to perform common set operations | ||
and find statistics of any sorted array-like objects. | ||
## License | ||
MIT © [Maga D. Zandaqo](http://maga.name) | ||
MIT © [Maga D. Zandaqo](http://maga.name) |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
523141
2
90
Yes
6367
1
1087
2