structurae
Advanced tools
Comparing version 4.0.0-pre.7 to 4.0.0-pre.8
@@ -0,1 +1,2 @@ | ||
import type { BitFieldConstructor } from "./bit-field-types"; | ||
/** | ||
@@ -18,169 +19,2 @@ * Creates a BigBitField class with a given schema. BigBitField uses bigints as bitfields | ||
*/ | ||
export declare function BigBitFieldMixin<T extends Record<string, number>, K extends keyof T>(schema: T): { | ||
new (data?: bigint | number[] | T | { | ||
value: bigint; | ||
/** | ||
* Returns the value of a given field. | ||
* | ||
* @param field name of the field | ||
* @return value of the field | ||
*/ | ||
get(field: K): number; | ||
/** | ||
* Checks whether an instance has all the specified fields set to 1. Useful for bit flags. | ||
* | ||
* @param fields names of the fields to check | ||
* @return whether all the specified fields are set in the instance | ||
*/ | ||
has(...fields: Array<K>): boolean; | ||
/** | ||
* Checks if the instance contains all the key-value pairs listed in matcher. | ||
* Use `BigBitField.getMatcher` to get an array of precomputed values | ||
* that you can use to efficiently compare multiple instances | ||
* to the same key-value pairs as shown in the examples below. | ||
* | ||
* @param matcher an object with key-value pairs, or an array of precomputed matcher values | ||
* @return whether the instance matches with the provided fields | ||
*/ | ||
match(matcher: Partial<T> | [bigint, bigint]): boolean; | ||
/** | ||
* Stores a given value in a field. | ||
* | ||
* @param field name of the field | ||
* @param value value of the field | ||
* @return the instance | ||
*/ | ||
set(field: K, value?: number): any; | ||
/** | ||
* Returns the bigint value of an instance. | ||
*/ | ||
toJSON(): bigint; | ||
/** | ||
* Returns the object representation of the instance, | ||
* with field names as properties with corresponding values. | ||
*/ | ||
toObject(): Record<K, number>; | ||
/** | ||
* Returns a string representing the value of the instance. | ||
*/ | ||
toString(): string; | ||
/** | ||
* Returns the bigint value of an instance. | ||
*/ | ||
valueOf(): bigint; | ||
/** | ||
* Iterates over numbers stored in the instance. | ||
*/ | ||
[Symbol.iterator](): Generator<number, void, unknown>; | ||
}): { | ||
value: bigint; | ||
/** | ||
* Returns the value of a given field. | ||
* | ||
* @param field name of the field | ||
* @return value of the field | ||
*/ | ||
get(field: K): number; | ||
/** | ||
* Checks whether an instance has all the specified fields set to 1. Useful for bit flags. | ||
* | ||
* @param fields names of the fields to check | ||
* @return whether all the specified fields are set in the instance | ||
*/ | ||
has(...fields: Array<K>): boolean; | ||
/** | ||
* Checks if the instance contains all the key-value pairs listed in matcher. | ||
* Use `BigBitField.getMatcher` to get an array of precomputed values | ||
* that you can use to efficiently compare multiple instances | ||
* to the same key-value pairs as shown in the examples below. | ||
* | ||
* @param matcher an object with key-value pairs, or an array of precomputed matcher values | ||
* @return whether the instance matches with the provided fields | ||
*/ | ||
match(matcher: Partial<T> | [bigint, bigint]): boolean; | ||
/** | ||
* Stores a given value in a field. | ||
* | ||
* @param field name of the field | ||
* @param value value of the field | ||
* @return the instance | ||
*/ | ||
set(field: K, value?: number): any; | ||
/** | ||
* Returns the bigint value of an instance. | ||
*/ | ||
toJSON(): bigint; | ||
/** | ||
* Returns the object representation of the instance, | ||
* with field names as properties with corresponding values. | ||
*/ | ||
toObject(): Record<K, number>; | ||
/** | ||
* Returns a string representing the value of the instance. | ||
*/ | ||
toString(): string; | ||
/** | ||
* Returns the bigint value of an instance. | ||
*/ | ||
valueOf(): bigint; | ||
/** | ||
* Iterates over numbers stored in the instance. | ||
*/ | ||
[Symbol.iterator](): Generator<number, void, unknown>; | ||
}; | ||
schema: Record<K, bigint>; | ||
fields: Array<K>; | ||
masks: Record<K, bigint>; | ||
offsets: Record<K, bigint>; | ||
mask: bigint; | ||
size: bigint; | ||
/** | ||
* Decodes an encoded number into its object representation according to the schema. | ||
* | ||
* @param data encoded number | ||
* @return object representation | ||
*/ | ||
decode(data: bigint): Record<K, number>; | ||
/** | ||
* Encodes a given list of numbers or map of fields and their respective values | ||
* into a single number according to the schema. | ||
* | ||
* @param data the list of numbers to encode | ||
* @return encoded number | ||
*/ | ||
encode(data: Array<number> | T): bigint; | ||
/** | ||
* Creates an array of values to be used as a matcher | ||
* to efficiently match against multiple instances. | ||
* | ||
* @param matcher an object containing field names and their values | ||
* @return an array of precomputed values | ||
*/ | ||
getMatcher(matcher: Partial<T>): [bigint, bigint]; | ||
/** | ||
* Returns the minimum amount of bits necessary to hold a given number. | ||
* | ||
* @param number the number | ||
* @return the amount of bits | ||
*/ | ||
getMinSize(number: number): number; | ||
/** | ||
* Prepares the class to handle data according to its schema provided in `BigBitField.schema`. | ||
*/ | ||
initialize(schema: Record<K, number>): void; | ||
/** | ||
* Checks if a given set of values or all given pairs of field name and value | ||
* are valid according to the schema. | ||
* | ||
* @param data pairs of field name and value to check | ||
* @return whether all pairs are valid | ||
*/ | ||
isValid(data: Partial<T>): boolean; | ||
/** | ||
* The static version of `BigBitField#match`, matches a given value against a precomputed matcher. | ||
* | ||
* @param value a value to check | ||
* @param matcher a precomputed set of values | ||
*/ | ||
match(value: bigint, matcher: [bigint, bigint]): boolean; | ||
}; | ||
export declare function BigBitFieldMixin<T extends Record<K, number>, K extends keyof T>(schema: T): BitFieldConstructor<K, bigint>; |
@@ -6,2 +6,121 @@ import { getBitSize } from "./utilities.js"; | ||
const TWO = BigInt(2); | ||
class BigBitField { | ||
constructor(data = ZERO) { | ||
Object.defineProperty(this, "value", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: ZERO | ||
}); | ||
this.value = data instanceof BigBitField | ||
? data.valueOf() | ||
: typeof data === "object" | ||
? this.constructor.encode(data) | ||
: BigInt(data); | ||
} | ||
static decode(data) { | ||
const schema = this.schema; | ||
const fields = this.fields; | ||
const masks = this.masks; | ||
const result = {}; | ||
let value = data; | ||
for (let i = 0; i < fields.length; i++) { | ||
const field = fields[i]; | ||
result[field] = Number(value & masks[field]); | ||
value >>= schema[field]; | ||
} | ||
return result; | ||
} | ||
static encode(data) { | ||
const schema = this.schema; | ||
const fields = this.fields; | ||
const array = Array.isArray(data) | ||
? data | ||
: fields.map((name) => data[name] || 0); | ||
let result = ZERO; | ||
for (let i = fields.length - 1; i >= 0; i--) { | ||
const field = fields[i]; | ||
const current = array[i]; | ||
result <<= schema[field]; | ||
result |= BigInt(current); | ||
} | ||
return result; | ||
} | ||
static getMatcher(matcher) { | ||
const { masks, offsets } = this; | ||
const fields = Object.keys(matcher); | ||
let mask = ZERO; | ||
let value = ZERO; | ||
for (let i = 0; i < fields.length; i++) { | ||
const fieldName = fields[i]; | ||
const offset = offsets[fieldName]; | ||
const fieldMask = masks[fieldName] << offset; | ||
const fieldValue = BigInt(matcher[fieldName]); | ||
value = (value & ~fieldMask) | (fieldValue << offsets[fieldName]); | ||
mask |= fieldMask; | ||
} | ||
return [value, this.mask ^ mask]; | ||
} | ||
static getMinSize(number) { | ||
return getBitSize(number); | ||
} | ||
static isValid(data) { | ||
const { masks } = this; | ||
const fields = Object.keys(data); | ||
for (let i = 0; i < fields.length; i++) { | ||
const field = fields[i]; | ||
const value = BigInt(data[field]); | ||
if ((value & SIGN_BIT) !== value || value > masks[field]) | ||
return false; | ||
} | ||
return true; | ||
} | ||
static match(value, matcher) { | ||
return (value & matcher[1]) === matcher[0]; | ||
} | ||
*[Symbol.iterator]() { | ||
const { fields } = this.constructor; | ||
for (let i = 0; i < fields.length; i++) { | ||
yield this.get(fields[i]); | ||
} | ||
} | ||
get(field) { | ||
const { offsets, masks } = this.constructor; | ||
const value = (this.value >> offsets[field]) & masks[field]; | ||
return Number(value); | ||
} | ||
has(...fields) { | ||
const { offsets } = this.constructor; | ||
let mask = ZERO; | ||
for (let i = 0; i < fields.length; i++) { | ||
mask |= ONE << offsets[fields[i]]; | ||
} | ||
mask |= this.value; | ||
return this.value === mask; | ||
} | ||
match(matcher) { | ||
return this.constructor.match(this.value, Array.isArray(matcher) | ||
? matcher | ||
: this.constructor.getMatcher(matcher)); | ||
} | ||
set(field, value = 1) { | ||
const { offsets, masks } = this.constructor; | ||
const offset = offsets[field]; | ||
this.value = (this.value & ~(masks[field] << offset)) | | ||
(BigInt(value) << offsets[field]); | ||
return this; | ||
} | ||
toJSON() { | ||
return this.value; | ||
} | ||
toObject() { | ||
return this.constructor.decode(this.value); | ||
} | ||
toString() { | ||
return this.value.toString(); | ||
} | ||
valueOf() { | ||
return this.value; | ||
} | ||
} | ||
/** | ||
@@ -25,233 +144,57 @@ * Creates a BigBitField class with a given schema. BigBitField uses bigints as bitfields | ||
export function BigBitFieldMixin(schema) { | ||
const Class = class BigBitFieldClass { | ||
/** | ||
* @param data a single number value of the field, | ||
* a bit field, or a map of field names with their respective values | ||
*/ | ||
constructor(data = ZERO) { | ||
Object.defineProperty(this, "value", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: ZERO | ||
}); | ||
this.value = data instanceof BigBitFieldClass | ||
? data.valueOf() | ||
: typeof data === "object" | ||
? this.constructor.encode(data) | ||
: BigInt(data); | ||
} | ||
/** | ||
* Decodes an encoded number into its object representation according to the schema. | ||
* | ||
* @param data encoded number | ||
* @return object representation | ||
*/ | ||
static decode(data) { | ||
const { fields, masks, schema } = this; | ||
const result = {}; | ||
let value = data; | ||
for (let i = 0; i < fields.length; i++) { | ||
const field = fields[i]; | ||
result[field] = Number(value & masks[field]); | ||
value >>= schema[field]; | ||
} | ||
return result; | ||
} | ||
/** | ||
* Encodes a given list of numbers or map of fields and their respective values | ||
* into a single number according to the schema. | ||
* | ||
* @param data the list of numbers to encode | ||
* @return encoded number | ||
*/ | ||
static encode(data) { | ||
const { fields, schema } = this; | ||
const array = Array.isArray(data) | ||
? data | ||
: fields.map((name) => data[name] || 0); | ||
let result = ZERO; | ||
for (let i = fields.length - 1; i >= 0; i--) { | ||
const field = fields[i]; | ||
const current = array[i]; | ||
result <<= schema[field]; | ||
result |= BigInt(current); | ||
} | ||
return result; | ||
} | ||
/** | ||
* Creates an array of values to be used as a matcher | ||
* to efficiently match against multiple instances. | ||
* | ||
* @param matcher an object containing field names and their values | ||
* @return an array of precomputed values | ||
*/ | ||
static getMatcher(matcher) { | ||
const { masks, offsets } = this; | ||
const fields = Object.keys(matcher); | ||
let mask = ZERO; | ||
let value = ZERO; | ||
for (let i = 0; i < fields.length; i++) { | ||
const fieldName = fields[i]; | ||
const offset = offsets[fieldName]; | ||
const fieldMask = masks[fieldName] << offset; | ||
const fieldValue = BigInt(matcher[fieldName]); | ||
value = (value & ~fieldMask) | (fieldValue << offsets[fieldName]); | ||
mask |= fieldMask; | ||
} | ||
return [value, this.mask ^ mask]; | ||
} | ||
/** | ||
* Returns the minimum amount of bits necessary to hold a given number. | ||
* | ||
* @param number the number | ||
* @return the amount of bits | ||
*/ | ||
static getMinSize(number) { | ||
return getBitSize(number); | ||
} | ||
/** | ||
* Prepares the class to handle data according to its schema provided in `BigBitField.schema`. | ||
*/ | ||
static initialize(schema) { | ||
const fields = Object.keys(schema); | ||
const _schema = {}; | ||
const masks = {}; | ||
const offsets = {}; | ||
let lastOffset = ZERO; | ||
for (let i = 0; i < fields.length; i++) { | ||
const field = fields[i]; | ||
const size = BigInt(schema[field]); | ||
_schema[field] = size; | ||
masks[field] = (TWO << (size - ONE)) - ONE; | ||
offsets[field] = lastOffset; | ||
lastOffset += size; | ||
} | ||
this.schema = _schema; | ||
this.fields = fields; | ||
this.size = lastOffset; | ||
this.mask = TWO << (BigInt(lastOffset) - ONE); | ||
this.masks = masks; | ||
this.offsets = offsets; | ||
} | ||
/** | ||
* Checks if a given set of values or all given pairs of field name and value | ||
* are valid according to the schema. | ||
* | ||
* @param data pairs of field name and value to check | ||
* @return whether all pairs are valid | ||
*/ | ||
static isValid(data) { | ||
const { masks } = this; | ||
const fields = Object.keys(data); | ||
for (let i = 0; i < fields.length; i++) { | ||
const field = fields[i]; | ||
const value = BigInt(data[field]); | ||
if ((value & SIGN_BIT) !== value || value > masks[field]) | ||
return false; | ||
} | ||
return true; | ||
} | ||
/** | ||
* The static version of `BigBitField#match`, matches a given value against a precomputed matcher. | ||
* | ||
* @param value a value to check | ||
* @param matcher a precomputed set of values | ||
*/ | ||
static match(value, matcher) { | ||
return (value & matcher[1]) === matcher[0]; | ||
} | ||
/** | ||
* Iterates over numbers stored in the instance. | ||
*/ | ||
*[Symbol.iterator]() { | ||
const { fields } = this.constructor; | ||
for (let i = 0; i < fields.length; i++) { | ||
yield this.get(fields[i]); | ||
} | ||
} | ||
/** | ||
* Returns the value of a given field. | ||
* | ||
* @param field name of the field | ||
* @return value of the field | ||
*/ | ||
get(field) { | ||
const { offsets, masks } = this.constructor; | ||
const value = (this.value >> offsets[field]) & masks[field]; | ||
return Number(value); | ||
} | ||
/** | ||
* Checks whether an instance has all the specified fields set to 1. Useful for bit flags. | ||
* | ||
* @param fields names of the fields to check | ||
* @return whether all the specified fields are set in the instance | ||
*/ | ||
has(...fields) { | ||
const { offsets } = this.constructor; | ||
let mask = ZERO; | ||
for (let i = 0; i < fields.length; i++) { | ||
mask |= ONE << offsets[fields[i]]; | ||
} | ||
mask |= this.value; | ||
return this.value === mask; | ||
} | ||
/** | ||
* Checks if the instance contains all the key-value pairs listed in matcher. | ||
* Use `BigBitField.getMatcher` to get an array of precomputed values | ||
* that you can use to efficiently compare multiple instances | ||
* to the same key-value pairs as shown in the examples below. | ||
* | ||
* @param matcher an object with key-value pairs, or an array of precomputed matcher values | ||
* @return whether the instance matches with the provided fields | ||
*/ | ||
match(matcher) { | ||
return this.constructor.match(this.value, Array.isArray(matcher) | ||
? matcher | ||
: this.constructor.getMatcher(matcher)); | ||
} | ||
/** | ||
* Stores a given value in a field. | ||
* | ||
* @param field name of the field | ||
* @param value value of the field | ||
* @return the instance | ||
*/ | ||
set(field, value = 1) { | ||
const { offsets, masks } = this.constructor; | ||
const offset = offsets[field]; | ||
this.value = (this.value & ~(masks[field] << offset)) | | ||
(BigInt(value) << offsets[field]); | ||
return this; | ||
} | ||
/** | ||
* Returns the bigint value of an instance. | ||
*/ | ||
toJSON() { | ||
return this.value; | ||
} | ||
/** | ||
* Returns the object representation of the instance, | ||
* with field names as properties with corresponding values. | ||
*/ | ||
toObject() { | ||
return this.constructor.decode(this.value); | ||
} | ||
/** | ||
* Returns a string representing the value of the instance. | ||
*/ | ||
toString() { | ||
return this.value.toString(); | ||
} | ||
/** | ||
* Returns the bigint value of an instance. | ||
*/ | ||
valueOf() { | ||
return this.value; | ||
} | ||
}; | ||
Class.initialize(schema); | ||
return Class; | ||
var _a; | ||
const fields = Object.keys(schema); | ||
const _schema = {}; | ||
const masks = {}; | ||
const offsets = {}; | ||
let lastOffset = ZERO; | ||
for (let i = 0; i < fields.length; i++) { | ||
const field = fields[i]; | ||
const size = BigInt(schema[field]); | ||
_schema[field] = size; | ||
masks[field] = (TWO << (size - ONE)) - ONE; | ||
offsets[field] = lastOffset; | ||
lastOffset += size; | ||
} | ||
return _a = class extends BigBitField { | ||
}, | ||
Object.defineProperty(_a, "schema", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: _schema | ||
}), | ||
Object.defineProperty(_a, "fields", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: fields | ||
}), | ||
Object.defineProperty(_a, "masks", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: masks | ||
}), | ||
Object.defineProperty(_a, "offsets", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: offsets | ||
}), | ||
Object.defineProperty(_a, "size", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: lastOffset | ||
}), | ||
Object.defineProperty(_a, "mask", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: TWO << (BigInt(lastOffset) - ONE) | ||
}), | ||
_a; | ||
} | ||
//# sourceMappingURL=big-bit-field.js.map |
@@ -0,1 +1,2 @@ | ||
import type { BitFieldConstructor } from "./bit-field-types"; | ||
/** | ||
@@ -22,337 +23,4 @@ * Creates a BitField class from with a given schema. BitField uses numbers as bitfields | ||
*/ | ||
export declare function BitFieldMixin<T extends Record<K, number>, K extends keyof T>(schema: T): { | ||
new (data?: number | number[] | T | { | ||
value: number; | ||
/** | ||
* Returns the value of a given field. | ||
* | ||
* @param field name of the field | ||
* @return value of the field | ||
*/ | ||
get(field: K): number; | ||
/** | ||
* Checks whether an instance has all the specified fields set to 1. Useful for bit flags. | ||
* | ||
* @param fields names of the fields to check | ||
* @return whether all the specified fields are set in the instance | ||
*/ | ||
has(...fields: Array<K>): boolean; | ||
/** | ||
* Checks if the instance contains all the key-value pairs listed in matcher. | ||
* Use `BitField.getMatcher` to get an array of precomputed values | ||
* that you can use to efficiently compare multiple instances | ||
* to the same key-value pairs as shown in the examples below. | ||
* | ||
* @param matcher an object with key-value pairs, or an array of precomputed matcher values | ||
* @return whether the instance matches with the provided fields | ||
*/ | ||
match(matcher: Partial<T> | [number, number]): boolean; | ||
/** | ||
* Stores a given value in a field. | ||
* | ||
* @param field name of the field | ||
* @param value value of the field | ||
* @return the instance | ||
*/ | ||
set(field: K, value?: number): this; | ||
/** | ||
* Returns the numerical value of an instance. | ||
*/ | ||
toJSON(): number; | ||
/** | ||
* Returns the object representation of the instance, | ||
* with field names as properties with corresponding values. | ||
*/ | ||
toObject(): Record<K, number>; | ||
/** | ||
* Returns a string representing the value of the instance. | ||
*/ | ||
toString(): string; | ||
/** | ||
* Returns the numerical value of an instance. | ||
*/ | ||
valueOf(): number; | ||
/** | ||
* Iterates over numbers stored in the instance. | ||
*/ | ||
[Symbol.iterator](): Generator<number, void, unknown>; | ||
}): { | ||
value: number; | ||
/** | ||
* Returns the value of a given field. | ||
* | ||
* @param field name of the field | ||
* @return value of the field | ||
*/ | ||
get(field: K): number; | ||
/** | ||
* Checks whether an instance has all the specified fields set to 1. Useful for bit flags. | ||
* | ||
* @param fields names of the fields to check | ||
* @return whether all the specified fields are set in the instance | ||
*/ | ||
has(...fields: Array<K>): boolean; | ||
/** | ||
* Checks if the instance contains all the key-value pairs listed in matcher. | ||
* Use `BitField.getMatcher` to get an array of precomputed values | ||
* that you can use to efficiently compare multiple instances | ||
* to the same key-value pairs as shown in the examples below. | ||
* | ||
* @param matcher an object with key-value pairs, or an array of precomputed matcher values | ||
* @return whether the instance matches with the provided fields | ||
*/ | ||
match(matcher: Partial<T> | [number, number]): boolean; | ||
/** | ||
* Stores a given value in a field. | ||
* | ||
* @param field name of the field | ||
* @param value value of the field | ||
* @return the instance | ||
*/ | ||
set(field: K, value?: number): this; | ||
/** | ||
* Returns the numerical value of an instance. | ||
*/ | ||
toJSON(): number; | ||
/** | ||
* Returns the object representation of the instance, | ||
* with field names as properties with corresponding values. | ||
*/ | ||
toObject(): Record<K, number>; | ||
/** | ||
* Returns a string representing the value of the instance. | ||
*/ | ||
toString(): string; | ||
/** | ||
* Returns the numerical value of an instance. | ||
*/ | ||
valueOf(): number; | ||
/** | ||
* Iterates over numbers stored in the instance. | ||
*/ | ||
[Symbol.iterator](): Generator<number, void, unknown>; | ||
}; | ||
schema: T; | ||
fields: Array<K>; | ||
masks: Record<K, number>; | ||
offsets: Record<K, number>; | ||
mask: number; | ||
size: number; | ||
/** | ||
* Decodes an encoded number into its object representation according to the schema. | ||
* | ||
* @param data encoded number | ||
* @return object representation | ||
*/ | ||
decode(data: number): Record<K, number>; | ||
/** | ||
* Encodes a given list of numbers or map of fields and their respective values | ||
* into a single number according to the schema. | ||
* | ||
* @param data the list of numbers to encode | ||
* @return encoded number | ||
*/ | ||
encode(data: Array<number> | T): number; | ||
/** | ||
* Creates an array of values to be used as a matcher | ||
* to efficiently match against multiple instances. | ||
* | ||
* @param matcher an object containing field names and their values | ||
* @return an array of precomputed values | ||
*/ | ||
getMatcher(matcher: Partial<T>): [number, number]; | ||
/** | ||
* Returns the minimum amount of bits necessary to hold a given number. | ||
* | ||
* @param number the number | ||
* @return the amount of bits | ||
*/ | ||
getMinSize(number: number): number; | ||
/** | ||
* Prepares the class to handle data according to its schema provided in `BitField.schema`. | ||
*/ | ||
initialize(): void; | ||
/** | ||
* Checks if a given set of values or all given pairs of field name and value | ||
* are valid according to the schema. | ||
* | ||
* @param data pairs of field name and value to check | ||
* @return whether all pairs are valid | ||
*/ | ||
isValid(data: T): boolean; | ||
/** | ||
* The static version of `BitField#match`, matches a given value against a precomputed matcher. | ||
* | ||
* @param value a value to check | ||
* @param matcher a precomputed set of values | ||
*/ | ||
match(value: number, matcher: [number, number]): boolean; | ||
}; | ||
export declare const BitField: { | ||
new (data?: number | number[] | Record<number, number> | { | ||
value: number; | ||
/** | ||
* Returns the value of a given field. | ||
* | ||
* @param field name of the field | ||
* @return value of the field | ||
*/ | ||
get(field: number): number; | ||
/** | ||
* Checks whether an instance has all the specified fields set to 1. Useful for bit flags. | ||
* | ||
* @param fields names of the fields to check | ||
* @return whether all the specified fields are set in the instance | ||
*/ | ||
has(...fields: number[]): boolean; | ||
/** | ||
* Checks if the instance contains all the key-value pairs listed in matcher. | ||
* Use `BitField.getMatcher` to get an array of precomputed values | ||
* that you can use to efficiently compare multiple instances | ||
* to the same key-value pairs as shown in the examples below. | ||
* | ||
* @param matcher an object with key-value pairs, or an array of precomputed matcher values | ||
* @return whether the instance matches with the provided fields | ||
*/ | ||
match(matcher: [number, number] | Partial<Record<number, number>>): boolean; | ||
/** | ||
* Stores a given value in a field. | ||
* | ||
* @param field name of the field | ||
* @param value value of the field | ||
* @return the instance | ||
*/ | ||
set(field: number, value?: number): this; | ||
/** | ||
* Returns the numerical value of an instance. | ||
*/ | ||
toJSON(): number; | ||
/** | ||
* Returns the object representation of the instance, | ||
* with field names as properties with corresponding values. | ||
*/ | ||
toObject(): Record<number, number>; | ||
/** | ||
* Returns a string representing the value of the instance. | ||
*/ | ||
toString(): string; | ||
/** | ||
* Returns the numerical value of an instance. | ||
*/ | ||
valueOf(): number; | ||
/** | ||
* Iterates over numbers stored in the instance. | ||
*/ | ||
[Symbol.iterator](): Generator<number, void, unknown>; | ||
}): { | ||
value: number; | ||
/** | ||
* Returns the value of a given field. | ||
* | ||
* @param field name of the field | ||
* @return value of the field | ||
*/ | ||
get(field: number): number; | ||
/** | ||
* Checks whether an instance has all the specified fields set to 1. Useful for bit flags. | ||
* | ||
* @param fields names of the fields to check | ||
* @return whether all the specified fields are set in the instance | ||
*/ | ||
has(...fields: number[]): boolean; | ||
/** | ||
* Checks if the instance contains all the key-value pairs listed in matcher. | ||
* Use `BitField.getMatcher` to get an array of precomputed values | ||
* that you can use to efficiently compare multiple instances | ||
* to the same key-value pairs as shown in the examples below. | ||
* | ||
* @param matcher an object with key-value pairs, or an array of precomputed matcher values | ||
* @return whether the instance matches with the provided fields | ||
*/ | ||
match(matcher: [number, number] | Partial<Record<number, number>>): boolean; | ||
/** | ||
* Stores a given value in a field. | ||
* | ||
* @param field name of the field | ||
* @param value value of the field | ||
* @return the instance | ||
*/ | ||
set(field: number, value?: number): this; | ||
/** | ||
* Returns the numerical value of an instance. | ||
*/ | ||
toJSON(): number; | ||
/** | ||
* Returns the object representation of the instance, | ||
* with field names as properties with corresponding values. | ||
*/ | ||
toObject(): Record<number, number>; | ||
/** | ||
* Returns a string representing the value of the instance. | ||
*/ | ||
toString(): string; | ||
/** | ||
* Returns the numerical value of an instance. | ||
*/ | ||
valueOf(): number; | ||
/** | ||
* Iterates over numbers stored in the instance. | ||
*/ | ||
[Symbol.iterator](): Generator<number, void, unknown>; | ||
}; | ||
schema: Record<number, number>; | ||
fields: number[]; | ||
masks: Record<number, number>; | ||
offsets: Record<number, number>; | ||
mask: number; | ||
size: number; | ||
/** | ||
* Decodes an encoded number into its object representation according to the schema. | ||
* | ||
* @param data encoded number | ||
* @return object representation | ||
*/ | ||
decode(data: number): Record<number, number>; | ||
/** | ||
* Encodes a given list of numbers or map of fields and their respective values | ||
* into a single number according to the schema. | ||
* | ||
* @param data the list of numbers to encode | ||
* @return encoded number | ||
*/ | ||
encode(data: number[] | Record<number, number>): number; | ||
/** | ||
* Creates an array of values to be used as a matcher | ||
* to efficiently match against multiple instances. | ||
* | ||
* @param matcher an object containing field names and their values | ||
* @return an array of precomputed values | ||
*/ | ||
getMatcher(matcher: Partial<Record<number, number>>): [number, number]; | ||
/** | ||
* Returns the minimum amount of bits necessary to hold a given number. | ||
* | ||
* @param number the number | ||
* @return the amount of bits | ||
*/ | ||
getMinSize(number: number): number; | ||
/** | ||
* Prepares the class to handle data according to its schema provided in `BitField.schema`. | ||
*/ | ||
initialize(): void; | ||
/** | ||
* Checks if a given set of values or all given pairs of field name and value | ||
* are valid according to the schema. | ||
* | ||
* @param data pairs of field name and value to check | ||
* @return whether all pairs are valid | ||
*/ | ||
isValid(data: Record<number, number>): boolean; | ||
/** | ||
* The static version of `BitField#match`, matches a given value against a precomputed matcher. | ||
* | ||
* @param value a value to check | ||
* @param matcher a precomputed set of values | ||
*/ | ||
match(value: number, matcher: [number, number]): boolean; | ||
}; | ||
export declare function BitFieldMixin<T extends Record<K, number>, K extends keyof T>(schema: T): BitFieldConstructor<K>; | ||
declare const _BitField: BitFieldConstructor<0 | 1 | 2 | 4 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30, number>; | ||
export { _BitField as BitField }; |
442
bit-field.js
import { getBitSize } from "./utilities.js"; | ||
/** | ||
* The largest safe integer for bitwise operations. | ||
*/ | ||
const SIGN_BIT = 2147483647; | ||
/** | ||
* The maximum safe size for bitwise operations on standard numbers. | ||
*/ | ||
const MAX_BITWISE_SIZE = 31; | ||
class BitField { | ||
constructor(data = 0) { | ||
Object.defineProperty(this, "value", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: 0 | ||
}); | ||
this.value = typeof data === "number" | ||
? data | ||
: data instanceof BitField | ||
? data.valueOf() | ||
: this.constructor.encode(data); | ||
} | ||
static decode(data) { | ||
const schema = this.schema; | ||
const fields = this.fields; | ||
const masks = this.masks; | ||
const result = {}; | ||
let value = data; | ||
for (let i = 0; i < fields.length; i++) { | ||
const field = fields[i]; | ||
const size = schema[field]; | ||
result[field] = value & masks[field]; | ||
value >>= size; | ||
} | ||
return result; | ||
} | ||
static encode(data) { | ||
const schema = this.schema; | ||
const fields = this.fields; | ||
const array = Array.isArray(data) | ||
? data | ||
: fields.map((name) => data[name] || 0); | ||
let result = 0; | ||
for (let i = fields.length - 1; i >= 0; i--) { | ||
const field = fields[i]; | ||
const current = array[i]; | ||
result <<= schema[field]; | ||
result |= current; | ||
} | ||
return result; | ||
} | ||
static getMatcher(matcher) { | ||
const masks = this.masks; | ||
const offsets = this.offsets; | ||
const fields = Object.keys(matcher); | ||
let mask = 0; | ||
let value = 0; | ||
for (let i = 0; i < fields.length; i++) { | ||
const fieldName = fields[i]; | ||
const fieldMask = masks[fieldName] << offsets[fieldName]; | ||
const fieldValue = matcher[fieldName]; | ||
value = (value & ~fieldMask) | (fieldValue << offsets[fieldName]); | ||
mask |= fieldMask; | ||
} | ||
return [value, this.mask ^ mask]; | ||
} | ||
static getMinSize(number) { | ||
return getBitSize(number); | ||
} | ||
static isValid(data) { | ||
const masks = this.masks; | ||
const fields = Object.keys(data); | ||
for (let i = 0; i < fields.length; i++) { | ||
const field = fields[i]; | ||
const value = data[field]; | ||
if ((value & SIGN_BIT) !== value || value > masks[field]) | ||
return false; | ||
} | ||
return true; | ||
} | ||
static match(value, matcher) { | ||
return (value & matcher[1]) === matcher[0]; | ||
} | ||
*[Symbol.iterator]() { | ||
const fields = this.constructor.fields; | ||
for (let i = 0; i < fields.length; i++) { | ||
yield this.get(fields[i]); | ||
} | ||
} | ||
get(field) { | ||
const { offsets, masks } = this.constructor; | ||
return (this.value >> offsets[field]) & masks[field]; | ||
} | ||
has(...fields) { | ||
const { offsets } = this.constructor; | ||
let mask = 0; | ||
for (let i = 0; i < fields.length; i++) { | ||
mask |= 1 << offsets[fields[i]]; | ||
} | ||
mask |= this.value; | ||
return this.value === mask; | ||
} | ||
match(matcher) { | ||
return this.constructor.match(this.value, Array.isArray(matcher) | ||
? matcher | ||
: this.constructor.getMatcher(matcher)); | ||
} | ||
set(field, value = 1) { | ||
const { offsets, masks } = this.constructor; | ||
this.value = (this.value & ~(masks[field] << offsets[field])) | | ||
(value << offsets[field]); | ||
return this; | ||
} | ||
toJSON() { | ||
return this.value; | ||
} | ||
toObject() { | ||
return this.constructor.decode(this.value); | ||
} | ||
toString() { | ||
return this.value.toString(); | ||
} | ||
valueOf() { | ||
return this.value; | ||
} | ||
} | ||
Object.defineProperty(BitField, "mask", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: 2 << 30 | ||
}); | ||
Object.defineProperty(BitField, "size", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: 31 | ||
}); | ||
/** | ||
@@ -33,228 +157,32 @@ * Creates a BitField class from with a given schema. BitField uses numbers as bitfields | ||
var _a; | ||
const Class = (_a = class BitFieldClass { | ||
/** | ||
* @param data a single number value of the field, | ||
* a bitfield, or a map of field names with their respective values | ||
*/ | ||
constructor(data = 0) { | ||
Object.defineProperty(this, "value", { | ||
const fields = Object.keys(schema); | ||
const masks = {}; | ||
const offsets = {}; | ||
let lastOffset = 0; | ||
for (let i = 0; i < fields.length; i++) { | ||
const field = fields[i]; | ||
const size = schema[field]; | ||
masks[field] = (2 << (size - 1)) - 1; | ||
offsets[field] = lastOffset; | ||
lastOffset += size; | ||
} | ||
if (lastOffset > MAX_BITWISE_SIZE) { | ||
throw TypeError("The total size of the bitfield exceeds 31 bits."); | ||
} | ||
return _a = class extends BitField { | ||
constructor() { | ||
super(...arguments); | ||
Object.defineProperty(this, "size", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: 0 | ||
value: lastOffset | ||
}); | ||
this.value = typeof data === "number" | ||
? data | ||
: data instanceof BitFieldClass | ||
? data.valueOf() | ||
: this.constructor.encode(data); | ||
Object.defineProperty(this, "mask", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: 2 << (lastOffset - 1) | ||
}); | ||
} | ||
/** | ||
* Decodes an encoded number into its object representation according to the schema. | ||
* | ||
* @param data encoded number | ||
* @return object representation | ||
*/ | ||
static decode(data) { | ||
const { fields, masks, schema } = this; | ||
const result = {}; | ||
let value = data; | ||
for (let i = 0; i < fields.length; i++) { | ||
const field = fields[i]; | ||
const size = schema[field]; | ||
result[field] = value & masks[field]; | ||
value >>= size; | ||
} | ||
return result; | ||
} | ||
/** | ||
* Encodes a given list of numbers or map of fields and their respective values | ||
* into a single number according to the schema. | ||
* | ||
* @param data the list of numbers to encode | ||
* @return encoded number | ||
*/ | ||
static encode(data) { | ||
const { fields, schema } = this; | ||
const array = Array.isArray(data) | ||
? data | ||
: fields.map((name) => data[name] || 0); | ||
let result = 0; | ||
for (let i = fields.length - 1; i >= 0; i--) { | ||
const field = fields[i]; | ||
const current = array[i]; | ||
result <<= schema[field]; | ||
result |= current; | ||
} | ||
return result; | ||
} | ||
/** | ||
* Creates an array of values to be used as a matcher | ||
* to efficiently match against multiple instances. | ||
* | ||
* @param matcher an object containing field names and their values | ||
* @return an array of precomputed values | ||
*/ | ||
static getMatcher(matcher) { | ||
const { masks, offsets } = this; | ||
const fields = Object.keys(matcher); | ||
let mask = 0; | ||
let value = 0; | ||
for (let i = 0; i < fields.length; i++) { | ||
const fieldName = fields[i]; | ||
const fieldMask = masks[fieldName] << offsets[fieldName]; | ||
const fieldValue = matcher[fieldName]; | ||
value = (value & ~fieldMask) | (fieldValue << offsets[fieldName]); | ||
mask |= fieldMask; | ||
} | ||
return [value, this.mask ^ mask]; | ||
} | ||
/** | ||
* Returns the minimum amount of bits necessary to hold a given number. | ||
* | ||
* @param number the number | ||
* @return the amount of bits | ||
*/ | ||
static getMinSize(number) { | ||
return getBitSize(number); | ||
} | ||
/** | ||
* Prepares the class to handle data according to its schema provided in `BitField.schema`. | ||
*/ | ||
static initialize() { | ||
const { schema } = this; | ||
const fields = Object.keys(schema); | ||
const masks = {}; | ||
const offsets = {}; | ||
let lastOffset = 0; | ||
for (let i = 0; i < fields.length; i++) { | ||
const field = fields[i]; | ||
const size = schema[field]; | ||
masks[field] = (2 << (size - 1)) - 1; | ||
offsets[field] = lastOffset; | ||
lastOffset += size; | ||
} | ||
if (lastOffset > MAX_BITWISE_SIZE) { | ||
throw TypeError("The total size of the bitfield exceeds 31 bits."); | ||
} | ||
this.schema = schema; | ||
this.fields = fields; | ||
this.size = lastOffset; | ||
this.mask = 2 << (lastOffset - 1); | ||
this.masks = masks; | ||
this.offsets = offsets; | ||
} | ||
/** | ||
* Checks if a given set of values or all given pairs of field name and value | ||
* are valid according to the schema. | ||
* | ||
* @param data pairs of field name and value to check | ||
* @return whether all pairs are valid | ||
*/ | ||
static isValid(data) { | ||
const { masks } = this; | ||
const fields = Object.keys(data); | ||
for (let i = 0; i < fields.length; i++) { | ||
const field = fields[i]; | ||
const value = data[field]; | ||
if ((value & SIGN_BIT) !== value || value > masks[field]) | ||
return false; | ||
} | ||
return true; | ||
} | ||
/** | ||
* The static version of `BitField#match`, matches a given value against a precomputed matcher. | ||
* | ||
* @param value a value to check | ||
* @param matcher a precomputed set of values | ||
*/ | ||
static match(value, matcher) { | ||
return (value & matcher[1]) === matcher[0]; | ||
} | ||
/** | ||
* Iterates over numbers stored in the instance. | ||
*/ | ||
*[Symbol.iterator]() { | ||
const { fields } = this.constructor; | ||
for (let i = 0; i < fields.length; i++) { | ||
yield this.get(fields[i]); | ||
} | ||
} | ||
/** | ||
* Returns the value of a given field. | ||
* | ||
* @param field name of the field | ||
* @return value of the field | ||
*/ | ||
get(field) { | ||
const { offsets, masks } = this.constructor; | ||
return (this.value >> offsets[field]) & masks[field]; | ||
} | ||
/** | ||
* Checks whether an instance has all the specified fields set to 1. Useful for bit flags. | ||
* | ||
* @param fields names of the fields to check | ||
* @return whether all the specified fields are set in the instance | ||
*/ | ||
has(...fields) { | ||
const { offsets } = this.constructor; | ||
let mask = 0; | ||
for (let i = 0; i < fields.length; i++) { | ||
mask |= 1 << offsets[fields[i]]; | ||
} | ||
mask |= this.value; | ||
return this.value === mask; | ||
} | ||
/** | ||
* Checks if the instance contains all the key-value pairs listed in matcher. | ||
* Use `BitField.getMatcher` to get an array of precomputed values | ||
* that you can use to efficiently compare multiple instances | ||
* to the same key-value pairs as shown in the examples below. | ||
* | ||
* @param matcher an object with key-value pairs, or an array of precomputed matcher values | ||
* @return whether the instance matches with the provided fields | ||
*/ | ||
match(matcher) { | ||
return this.constructor.match(this.value, Array.isArray(matcher) | ||
? matcher | ||
: this.constructor.getMatcher(matcher)); | ||
} | ||
/** | ||
* Stores a given value in a field. | ||
* | ||
* @param field name of the field | ||
* @param value value of the field | ||
* @return the instance | ||
*/ | ||
set(field, value = 1) { | ||
const { offsets, masks } = this.constructor; | ||
this.value = (this.value & ~(masks[field] << offsets[field])) | | ||
(value << offsets[field]); | ||
return this; | ||
} | ||
/** | ||
* Returns the numerical value of an instance. | ||
*/ | ||
toJSON() { | ||
return this.value; | ||
} | ||
/** | ||
* Returns the object representation of the instance, | ||
* with field names as properties with corresponding values. | ||
*/ | ||
toObject() { | ||
return this.constructor.decode(this.value); | ||
} | ||
/** | ||
* Returns a string representing the value of the instance. | ||
*/ | ||
toString() { | ||
return this.value.toString(); | ||
} | ||
/** | ||
* Returns the numerical value of an instance. | ||
*/ | ||
valueOf() { | ||
return this.value; | ||
} | ||
}, | ||
@@ -267,11 +195,57 @@ Object.defineProperty(_a, "schema", { | ||
}), | ||
_a); | ||
Class.initialize(); | ||
return Class; | ||
Object.defineProperty(_a, "fields", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: fields | ||
}), | ||
Object.defineProperty(_a, "masks", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: masks | ||
}), | ||
Object.defineProperty(_a, "offsets", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: offsets | ||
}), | ||
_a; | ||
} | ||
export const BitField = BitFieldMixin(Array.from({ length: MAX_BITWISE_SIZE }, (_, i) => i).reduce((a, i) => { | ||
a[i] = 1; | ||
return a; | ||
}, {})); | ||
const _BitField = BitFieldMixin({ | ||
0: 1, | ||
1: 1, | ||
2: 1, | ||
3: 1, | ||
4: 1, | ||
5: 1, | ||
6: 1, | ||
7: 1, | ||
8: 1, | ||
9: 1, | ||
10: 1, | ||
11: 1, | ||
12: 1, | ||
13: 1, | ||
14: 1, | ||
15: 1, | ||
16: 1, | ||
17: 1, | ||
18: 1, | ||
19: 1, | ||
20: 1, | ||
21: 1, | ||
22: 1, | ||
23: 1, | ||
24: 1, | ||
25: 1, | ||
26: 1, | ||
27: 1, | ||
28: 1, | ||
29: 1, | ||
30: 1, | ||
}); | ||
export { _BitField as BitField }; | ||
//# sourceMappingURL=bit-field.js.map |
@@ -32,4 +32,2 @@ import type { AdjacencyStructureConstructor, TypedArrayConstructors } from "./utility-types"; | ||
* Resets all coloring of vertices done during traversals. | ||
* | ||
* @private | ||
*/ | ||
@@ -127,4 +125,2 @@ resetColors(): void; | ||
* Resets all coloring of vertices done during traversals. | ||
* | ||
* @private | ||
*/ | ||
@@ -131,0 +127,0 @@ resetColors(): void; |
@@ -86,4 +86,2 @@ import { BinaryHeap } from "./binary-heap.js"; | ||
* Resets all coloring of vertices done during traversals. | ||
* | ||
* @private | ||
*/ | ||
@@ -90,0 +88,0 @@ resetColors() { |
@@ -11,3 +11,4 @@ export { AdjacencyListMixin } from "./adjacency-list"; | ||
export { BitArray } from "./bit-array"; | ||
export { BitFieldMixin } from "./bit-field"; | ||
export { BitField, BitFieldMixin } from "./bit-field"; | ||
export type { BitFieldConstructor, BitFieldStructure, } from "./bit-field-types"; | ||
export { BooleanView } from "./boolean-view"; | ||
@@ -14,0 +15,0 @@ export { GraphMixin } from "./graph"; |
@@ -11,3 +11,3 @@ export { AdjacencyListMixin } from "./adjacency-list.js"; | ||
export { BitArray } from "./bit-array.js"; | ||
export { BitFieldMixin } from "./bit-field.js"; | ||
export { BitField, BitFieldMixin } from "./bit-field.js"; | ||
export { BooleanView } from "./boolean-view.js"; | ||
@@ -14,0 +14,0 @@ export { GraphMixin } from "./graph.js"; |
{ | ||
"name": "structurae", | ||
"version": "4.0.0-pre.7", | ||
"version": "4.0.0-pre.8", | ||
"description": "Data structures for performance-sensitive modern JavaScript applications.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -67,2 +67,3 @@ # Structurae | ||
WebAssembly](https://medium.com/@zandaqo/structurae-1-0-graphs-strings-and-webassembly-25dd964d5a70) | ||
- [Binary Protocol for JavaScript](https://itnext.io/binary-protocol-for-javascript-cc409e144a3c) | ||
@@ -69,0 +70,0 @@ ## Overview |
@@ -194,3 +194,2 @@ import type { IndexedCollection } from "./utility-types"; | ||
* | ||
* @private | ||
* @param elements the elements of which to create the array | ||
@@ -203,3 +202,2 @@ * @return the new SortedArray | ||
* | ||
* @private | ||
* @param arrays sorted array(s) to merge | ||
@@ -218,3 +216,2 @@ * @return a new SortedArray | ||
* | ||
* @private | ||
* @param element the element to look for | ||
@@ -252,3 +249,2 @@ * @return the element's index in the array or -1 | ||
* | ||
* @private | ||
* @param elements the elements to add to the array | ||
@@ -305,3 +301,2 @@ * @return the new length of the array | ||
* | ||
* @private | ||
* @param compareFunction the function to use for comparison | ||
@@ -314,3 +309,2 @@ * | ||
* | ||
* @private | ||
* @param start the index at which to start changing the array | ||
@@ -336,3 +330,2 @@ * @param deleteCount the amount of old elements to delete | ||
* | ||
* @private | ||
* @param elements the elements to add to the array | ||
@@ -339,0 +332,0 @@ * @return the new length of the array |
@@ -359,3 +359,2 @@ /** | ||
* | ||
* @private | ||
* @param elements the elements of which to create the array | ||
@@ -372,3 +371,2 @@ * @return the new SortedArray | ||
* | ||
* @private | ||
* @param arrays sorted array(s) to merge | ||
@@ -397,3 +395,2 @@ * @return a new SortedArray | ||
* | ||
* @private | ||
* @param element the element to look for | ||
@@ -437,3 +434,2 @@ * @return the element's index in the array or -1 | ||
* | ||
* @private | ||
* @param elements the elements to add to the array | ||
@@ -526,3 +522,2 @@ * @return the new length of the array | ||
* | ||
* @private | ||
* @param compareFunction the function to use for comparison | ||
@@ -538,3 +533,2 @@ * | ||
* | ||
* @private | ||
* @param start the index at which to start changing the array | ||
@@ -567,3 +561,2 @@ * @param deleteCount the amount of old elements to delete | ||
* | ||
* @private | ||
* @param elements the elements to add to the array | ||
@@ -570,0 +563,0 @@ * @return the new length of the array |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
93
983
511206
6334