binarius
Advanced tools
Comparing version 0.0.5 to 0.0.7
460
index.js
@@ -1,241 +0,269 @@ | ||
// fallback to using Number if BigInt is not available | ||
const BigInt = (global || window).BigInt || Number; | ||
/** | ||
* @typedef {Object} FieldDescription | ||
* @property {string} name field name | ||
* @property {number} size size of the field in bits | ||
* Use Number if BigInt is not available. | ||
*/ | ||
const BigInt = (global || window).BigInt || Number; | ||
/** | ||
* @private | ||
* @param {Array<FieldDescription>|Array<string>} fields | ||
* @returns {Array<FieldDescription>} | ||
* The largest safe integer for bitwise operations. | ||
*/ | ||
function normalizeFields(fields) { | ||
return typeof fields[0] === 'object' ? fields : fields.map(field => ({ name: field, size: 1 })); | ||
} | ||
const SIGN_BIT = 2147483647; | ||
/** | ||
* @private | ||
* @param {Array<FieldDescription>} fields | ||
* @param {number} index | ||
* @returns {number} | ||
*/ | ||
function getOffset(fields, index) { | ||
let offset = 0; | ||
for (let i = index + 1; i < fields.length; i++) { | ||
offset += fields[i].size; | ||
class Binarius { | ||
/** | ||
* @param {number|Array<number>} data | ||
* @example | ||
* | ||
* const Person = BinariusFactory([{ name: 'age', size: 7}, { name: 'gender', size: 1}]); | ||
* new Person([20, 1]).value | ||
* //=> 41 | ||
* new Person(41).value | ||
* //=> 41 | ||
*/ | ||
constructor(data) { | ||
const { isInitialized } = this.constructor; | ||
if (!isInitialized) this.constructor.initialize(); | ||
const { isBigInt } = this.constructor; | ||
const value = Array.isArray(data) ? this.constructor.encode(data) : data; | ||
this.value = isBigInt ? BigInt(value) : value; | ||
} | ||
return offset; | ||
} | ||
/** | ||
* @private | ||
* @param {Array<FieldDescription>} fields | ||
* @param {number} base | ||
* @param {number} one | ||
* @returns {Array<Object<string, number>>} | ||
*/ | ||
function getMasks(fields, base, one) { | ||
const masks = {}; | ||
const offsets = {}; | ||
const isBigInt = typeof base === 'bigint'; | ||
for (let i = 0; i < fields.length; i++) { | ||
let { name, size } = fields[i]; | ||
if (isBigInt) { | ||
size = BigInt(size); | ||
fields[i].size = size; | ||
} | ||
masks[name] = (base << size - one) - one; | ||
const offset = getOffset(fields, i); | ||
offsets[name] = isBigInt ? BigInt(offset) : offset; | ||
/** | ||
* Returns the value of a given field. | ||
* | ||
* @param {string} field name of the field | ||
* @returns {number} value value of the field | ||
* @example | ||
* | ||
* const Person = BinariusFactory([{ name: 'age', size: 7}, { name: 'gender', size: 1}]); | ||
* const person = new Person([20, 1]); | ||
* person.get('age'); | ||
* //=> 20 | ||
* person.get('gender'); | ||
* //=> 1 | ||
*/ | ||
get(field) { | ||
const { offsets, masks } = this.constructor; | ||
const value = (this.value >> offsets[field]) & masks[field]; | ||
return Number(value); | ||
} | ||
return [masks, offsets]; | ||
} | ||
/** | ||
* Creates a new class for storing data in numbers according to a given schema. | ||
* | ||
* @param {Array<FieldDescription>|Array<string>} schema the layout of data being stored | ||
* @returns {Binarius} a new class tailored to the specified data schema | ||
* @example | ||
* | ||
* const Person = BinariusFactory([{ name: 'age', size: 7}, { name: 'gender', size: 1}]); | ||
* const SettingsFlags = BinariusFactory(['notify', 'premium', 'moderator']) | ||
*/ | ||
function BinariusFactory(schema) { | ||
const Binarius = class { | ||
/** | ||
* @param {number|Array<number>} data | ||
* @example | ||
* | ||
* const Person = BinariusFactory([{ name: 'age', size: 7}, { name: 'gender', size: 1}]); | ||
* new Person([20, 1]).value | ||
* //=> 41 | ||
* new Person(41).value | ||
* //=> 41 | ||
*/ | ||
constructor(data) { | ||
const { isBigInt } = this.constructor; | ||
const value = Array.isArray(data) ? this.constructor.encode(data) : data; | ||
this.value = isBigInt ? BigInt(value) : value; | ||
} | ||
/** | ||
* Stores a given value in a field. | ||
* | ||
* @param {string} field name of the field | ||
* @param {number} value value of the field | ||
* @returns {Binarius} the instance | ||
* @example | ||
* | ||
* const Person = BinariusFactory([{ name: 'age', size: 7}, { name: 'gender', size: 1}]); | ||
* const person = new Person([20, 1]); | ||
* person.get('age'); | ||
* //=> 20 | ||
* person.set('age', 30).get('age'); | ||
* //=> 30 | ||
*/ | ||
set(field, value = this.constructor.one) { | ||
const { offsets, masks, isBigInt } = this.constructor; | ||
if (isBigInt) value = BigInt(value); | ||
this.value = (this.value & ~(masks[field] << offsets[field])) | (value << offsets[field]); | ||
return this; | ||
} | ||
/** | ||
* Returns the value of a given field. | ||
* | ||
* @param {string} field name of the field | ||
* @returns {number} value value of the field | ||
* @example | ||
* | ||
* const Person = BinariusFactory([{ name: 'age', size: 7}, { name: 'gender', size: 1}]); | ||
* const person = new Person([20, 1]); | ||
* person.get('age'); | ||
* //=> 20 | ||
* person.get('gender'); | ||
* //=> 1 | ||
*/ | ||
get(field) { | ||
const { offsets, masks } = this.constructor; | ||
const value = (this.value >> offsets[field]) & masks[field]; | ||
return Number(value); | ||
/** | ||
* Checks if an instance has all the specified fields set to 1. Useful for bit flags. | ||
* | ||
* @param {...string} fields names of the fields to check | ||
* @returns {boolean} whether all the specified fields are set in the instance | ||
* @example | ||
* | ||
* const SettingsFlags = BinariusFactory(['notify', 'premium', 'moderator']); | ||
* const settings = SettingsFlags([1, 0, 1]); | ||
* settings.has('notify', 'moderator'); | ||
* //=> true | ||
* settings.has('notify', 'premium'); | ||
* //=> false | ||
*/ | ||
has(...fields) { | ||
const { offsets, zero, one } = 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; | ||
} | ||
/** | ||
* Stores a given value in a field. | ||
* | ||
* @param {string} field name of the field | ||
* @param {number} value value of the field | ||
* @returns {Binarius} the instance | ||
* @example | ||
* | ||
* const Person = BinariusFactory([{ name: 'age', size: 7}, { name: 'gender', size: 1}]); | ||
* const person = new Person([20, 1]); | ||
* person.get('age'); | ||
* //=> 20 | ||
* person.set('age', 30).get('age'); | ||
* //=> 30 | ||
*/ | ||
set(field, value = this.constructor.one) { | ||
const { offsets, masks, isBigInt } = this.constructor; | ||
if (isBigInt) value = BigInt(value); | ||
this.value = (this.value & ~(masks[field] << offsets[field])) | (value << offsets[field]); | ||
return this; | ||
} | ||
/** | ||
* Returns the numerical value of an instance. | ||
* | ||
* @private | ||
* @returns {*} the numerical value of the instance | ||
*/ | ||
toValue() { | ||
return this.value; | ||
} | ||
/** | ||
* Checks if an instance has all the specified fields set to 1. Useful for bit flags. | ||
* | ||
* @param {...string} fields names of the fields to check | ||
* @returns {boolean} whether all the specified fields are set in the instance | ||
* @example | ||
* | ||
* const SettingsFlags = BinariusFactory(['notify', 'premium', 'moderator']); | ||
* const settings = SettingsFlags([1, 0, 1]); | ||
* settings.has('notify', 'moderator'); | ||
* //=> true | ||
* settings.has('notify', 'premium'); | ||
* //=> false | ||
*/ | ||
has(...fields) { | ||
const { offsets, zero, one } = 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; | ||
} | ||
/** | ||
* Returns the object representation of the instance, | ||
* with field names as properties with corresponding values. | ||
* @returns {Object<string, number>} the object representation of the instance | ||
* @example | ||
* | ||
* const Person = BinariusFactory([{ name: 'age', size: 7}, { name: 'gender', size: 1}]); | ||
* const person = new Person([20, 1]); | ||
* person.toObject(); | ||
* //=> { age: 20, gender: 1 } | ||
*/ | ||
toObject() { | ||
return this.constructor.decode(this.value); | ||
} | ||
/** | ||
* Returns the numerical value of an instance. | ||
* | ||
* @private | ||
* @returns {*} the numerical value of the instance | ||
*/ | ||
toValue() { | ||
return this.value; | ||
/** | ||
* Encodes a given list of numbers into a single number according to the schema. | ||
* | ||
* @param {Array<number>} data the list of numbers to encode | ||
* @returns {number} encoded number | ||
* @example | ||
* | ||
* const Person = BinariusFactory([{ name: 'age', size: 7}, { name: 'gender', size: 1}]); | ||
* Person.encode([20, 1]) | ||
* //=> 41 | ||
*/ | ||
static encode(data) { | ||
const { zero, fields, isBigInt } = this; | ||
let result = zero; | ||
for (let i = 0; i < data.length; i++) { | ||
const current = data[i]; | ||
result <<= fields[i].size; | ||
result |= (isBigInt ? BigInt(current) : current); | ||
} | ||
return result; | ||
} | ||
/** | ||
* Returns the object representation of the instance, | ||
* with field names as properties with corresponding values. | ||
* @returns {Object<string, number>} the object representation of the instance | ||
* @example | ||
* | ||
* const Person = BinariusFactory([{ name: 'age', size: 7}, { name: 'gender', size: 1}]); | ||
* const person = new Person([20, 1]); | ||
* person.toObject(); | ||
* //=> { age: 20, gender: 1 } | ||
*/ | ||
toObject() { | ||
return this.constructor.decode(this.value); | ||
/** | ||
* Decodes an encoded number into it's object representation according to the schema. | ||
* | ||
* @param {number} data encoded number | ||
* @returns {Object<string, number>} object representation | ||
* @example | ||
* | ||
* const Person = BinariusFactory([{ name: 'age', size: 7}, { name: 'gender', size: 1}]); | ||
* Person.decode(41); | ||
* //=> { age: 20, gender: 1 } | ||
*/ | ||
static decode(data) { | ||
const { fields, masks } = this; | ||
const result = {}; | ||
let value = data; | ||
for (let i = fields.length - 1; i >= 0; i--) { | ||
const { name, size } = fields[i]; | ||
result[name] = Number(value & masks[name]); | ||
value >>= size; | ||
} | ||
return result; | ||
} | ||
/** | ||
* Encodes a given list of numbers into a single number according to the schema. | ||
* | ||
* @param {Array<number>} data the list of numbers to encode | ||
* @returns {number} encoded number | ||
* @example | ||
* | ||
* const Person = BinariusFactory([{ name: 'age', size: 7}, { name: 'gender', size: 1}]); | ||
* Person.encode([20, 1]) | ||
* //=> 41 | ||
*/ | ||
static encode(data) { | ||
const { zero, fields, isBigInt } = this; | ||
let result = zero; | ||
for (let i = 0; i < data.length; i++) { | ||
const current = data[i]; | ||
result <<= fields[i].size; | ||
result |= (isBigInt ? BigInt(current) : current); | ||
} | ||
return result; | ||
/** | ||
* Checks if a given set of values or all given pairs of field name and value | ||
* are valid according to the schema. | ||
* | ||
* @param {Array<number>|Object<string,number>} data pairs of field name and value to check | ||
* @returns {boolean} whether all pairs are valid | ||
* @example | ||
* | ||
* const Person = BinariusFactory([{ name: 'age', size: 7}, { name: 'gender', size: 1}]); | ||
* Person.isValid({age: 100}) | ||
* //=> true | ||
* Person.isValid({age: 100, gender: 3}) | ||
* //=> false | ||
* Person.isValid([100, 1]) | ||
* //=> true | ||
* Person.isValid([100, 3]) | ||
* //=> false | ||
*/ | ||
static isValid(data) { | ||
const { masks, fields } = this; | ||
const selectedFields = Array.isArray(data) | ||
? fields.map((field, i) => [field.name, data[i]]) | ||
: Object.entries(data); | ||
for (let i = 0; i < selectedFields.length; i++) { | ||
const [field, value] = selectedFields[i]; | ||
if (((value & SIGN_BIT) !== value) || value > masks[field]) return false; | ||
} | ||
return true; | ||
} | ||
/** | ||
* Decodes an encoded number into it's object representation according to the schema. | ||
* | ||
* @param {number} data encoded number | ||
* @returns {Object<string, number>} object representation | ||
* @example | ||
* | ||
* const Person = BinariusFactory([{ name: 'age', size: 7}, { name: 'gender', size: 1}]); | ||
* Person.decode(41); | ||
* //=> { age: 20, gender: 1 } | ||
*/ | ||
static decode(data) { | ||
const { fields, masks } = this; | ||
const result = {}; | ||
let value = data; | ||
for (let i = fields.length - 1; i >= 0; i--) { | ||
const { name, size } = fields[i]; | ||
result[name] = Number(value & masks[name]); | ||
value >>= size; | ||
} | ||
return result; | ||
/** | ||
* Prepares the class to handle data according to it's schema provided in `Binarius.fields`. | ||
* The method is called automatically the first time the constructor is used. | ||
* | ||
* @returns {void} | ||
*/ | ||
static initialize() { | ||
// determine total size and if BigInts are required | ||
const totalSize = this.fields.reduce((result, field) => result += (field.size || 1), 0); | ||
const isBigInt = totalSize > 31; | ||
const [zero, one, two] = isBigInt ? [BigInt(0), BigInt(1), BigInt(2)] : [0, 1, 2]; | ||
// normalize fields, set masks and offsets | ||
const fields = []; | ||
const masks = {}; | ||
const offsets = {}; | ||
let lastOffset = zero; | ||
for (let i = this.fields.length - 1; i >= 0; i--) { | ||
const field = this.fields[i]; | ||
const name = field.name || field; | ||
const size = isBigInt ? BigInt(field.size || 1) : field.size || 1; | ||
masks[name] = (two << size - one) - one; | ||
offsets[name] = lastOffset; | ||
lastOffset += size; | ||
fields.unshift({ name, size }); | ||
} | ||
}; | ||
const fields = normalizeFields(schema); | ||
const size = fields[0].size + getOffset(fields, 0); | ||
const isBigInt = size > 31; | ||
const [zero, one, two] = isBigInt ? [BigInt(0), BigInt(1), BigInt(2)] : [0, 1, 2]; | ||
const [masks, offsets] = getMasks(fields, two, one); | ||
Object.assign(Binarius, { | ||
fields, | ||
size, | ||
zero, | ||
one, | ||
two, | ||
masks, | ||
offsets, | ||
isBigInt, | ||
}); | ||
return Binarius; | ||
this.fields = fields; | ||
this.size = totalSize; | ||
this.isBigInt = isBigInt; | ||
this.zero = zero; | ||
this.one = one; | ||
this.two = two; | ||
this.masks = masks; | ||
this.offsets = offsets; | ||
this.isInitialized = true; | ||
} | ||
} | ||
module.exports = BinariusFactory; | ||
/** | ||
* @typedef {Object} FieldDescription | ||
* @property {string} name field name | ||
* @property {number} size size of the field in bits | ||
*/ | ||
/** @type {Array<number>|Array<FieldDescription>} */ | ||
Binarius.fields = new Array(31).fill(1).map((e, i) => i); | ||
/** @type {number} */ | ||
Binarius.size = 31; | ||
/** @type {number|BigInt} */ | ||
Binarius.zero = 0; | ||
/** @type {number|BigInt} */ | ||
Binarius.one = 1; | ||
/** @type {number|BigInt} */ | ||
Binarius.two = 2; | ||
/** @type {Object<string, number>} */ | ||
Binarius.masks = undefined; | ||
/** @type {Object<string, number>} */ | ||
Binarius.offsets = undefined; | ||
/** @type {boolean} */ | ||
Binarius.isBigInt = false; | ||
/** @type {boolean} */ | ||
Binarius.isInitialized = false; | ||
module.exports = Binarius; |
@@ -1,130 +0,144 @@ | ||
const binarius = require('./index'); | ||
const Binarius = require('./index'); | ||
describe('Binarius', () => { | ||
describe('Binarius Factory', () => { | ||
it('creates a Binarius class for a given schema', () => { | ||
const fields = [{ name: 'age', size: 7 }, { name: 'gender', size: 1 }]; | ||
const Binarius = binarius(fields); | ||
expect(Binarius.fields).toBe(fields); | ||
expect(Binarius.size).toBe(8); | ||
expect(Binarius.zero).toBe(0); | ||
expect(Binarius.one).toBe(1); | ||
expect(Binarius.two).toBe(2); | ||
expect(Binarius.masks).toBeDefined(); | ||
expect(Binarius.offsets).toBeDefined(); | ||
class Field extends Binarius {} | ||
Field.fields = [{ name: 'width', size: 16 }, { name: 'height', size: 15 }]; | ||
class PersonFlags extends Binarius {} | ||
PersonFlags.fields = ['human', 'gender', 'tall']; | ||
class LargePerson extends Binarius {} | ||
LargePerson.fields = [ | ||
{ name: 'age', size: 7 }, | ||
{ name: 'gender', size: 1 }, | ||
{ name: 'weight', size: 22 }, | ||
{ name: 'height', size: 3 }, | ||
]; | ||
describe('constructor', () => { | ||
it('initializes class on first invocation', () => { | ||
jest.spyOn(Field, 'initialize'); | ||
new Field(0); | ||
new Field(0); | ||
expect(Field.initialize).toHaveBeenCalled(); | ||
expect(Field.initialize.mock.calls.length).toBe(1); | ||
expect(Field.isInitialized).toBe(true); | ||
expect(Field.size).toBe(31); | ||
expect(Field.zero).toBe(0); | ||
expect(Field.one).toBe(1); | ||
expect(Field.two).toBe(2); | ||
expect(Field.masks).toBeDefined(); | ||
expect(Field.offsets).toBeDefined(); | ||
expect(Field.isBigInt).toBe(false); | ||
Field.initialize.mockRestore(); | ||
}); | ||
it('creates a Binarius class for bitfields', () => { | ||
const fields = ['human', 'gender']; | ||
const Binarius = binarius(fields); | ||
expect(Binarius.fields) | ||
.toEqual([{ name: 'human', size: 1 }, { name: 'gender', size: 1 }]); | ||
expect(Binarius.size).toBe(2); | ||
expect(Binarius.masks).toBeDefined(); | ||
expect(Binarius.offsets).toBeDefined(); | ||
it('initializes class tailored for bit flags', () => { | ||
const person = new PersonFlags(0); | ||
expect(person.value).toBe(0); | ||
expect(PersonFlags.isInitialized).toBe(true); | ||
expect(PersonFlags.fields).toEqual([ | ||
{ name: 'human', size: 1 }, | ||
{ name: 'gender', size: 1 }, | ||
{ name: 'tall', size: 1 }, | ||
]); | ||
expect(PersonFlags.size).toBe(3); | ||
}); | ||
it('creates a Binarius class using BigInts if the total size is above 32', () => { | ||
const fields = [{ name: 'weight', size: 30 }, { name: 'height', size: 3 }]; | ||
const Binarius = binarius(fields); | ||
expect(Binarius.fields).toBe(fields); | ||
expect(Binarius.size).toBe(33); | ||
expect(Binarius.zero).toBe(BigInt(0)); | ||
expect(Binarius.one).toBe(BigInt(1)); | ||
expect(Binarius.two).toBe(BigInt(2)); | ||
expect(Binarius.masks).toBeDefined(); | ||
expect(Binarius.offsets).toBeDefined(); | ||
it('initializes class that uses BigInts if the total size is above 31 bits', () => { | ||
const person = new LargePerson(0); | ||
expect(person.value).toBe(BigInt(0)); | ||
expect(LargePerson.size).toBe(33); | ||
expect(LargePerson.zero).toBe(BigInt(0)); | ||
expect(LargePerson.one).toBe(BigInt(1)); | ||
expect(LargePerson.two).toBe(BigInt(2)); | ||
expect(LargePerson.isBigInt).toBe(true); | ||
}); | ||
}); | ||
describe('Binarius Class', () => { | ||
let BinariusNumber; | ||
let BinariusLargestNumber; | ||
let BinariusBigInt; | ||
let BinariusBits; | ||
it('creates an instance with a given numerical value', () => { | ||
expect(new Field(2147483647).value).toBe(2147483647); | ||
expect(new LargePerson(BigInt(1375759717)).value).toBe(BigInt(1375759717)); | ||
expect(new PersonFlags(5).value).toBe(5); | ||
}); | ||
beforeEach(() => { | ||
BinariusNumber = binarius([{ name: 'age', size: 7 }, { name: 'gender', size: 1 }]); | ||
BinariusLargestNumber = binarius([{ name: 'width', size: 16 }, { name: 'height', size: 15 }]); | ||
BinariusBigInt = binarius([ | ||
{ name: 'age', size: 7 }, | ||
{ name: 'gender', size: 1 }, | ||
{ name: 'weight', size: 22 }, | ||
{ name: 'height', size: 3 }, | ||
]); | ||
BinariusBits = binarius(['human', 'gender', 'tall']); | ||
it('createas an instance from a list of values', () => { | ||
expect(new Field([65535, 32767]).value).toBe(2147483647); | ||
expect(new LargePerson([20, 1, 3500, 5]).value).toBe(BigInt(1375759717)); | ||
expect(new PersonFlags([1, 0, 1]).value).toBe(5); | ||
}); | ||
}); | ||
describe('constructor', () => { | ||
it('creates an instance with a given numerical value', () => { | ||
expect(new BinariusNumber(41).value).toBe(41); | ||
expect(new BinariusLargestNumber([65535, 32767]).value).toBe(2147483647); | ||
expect(new BinariusBigInt(BigInt(1375759717)).value).toBe(BigInt(1375759717)); | ||
expect(new BinariusBits(5).value).toBe(5); | ||
}); | ||
it('createas an instance from a list of values', () => { | ||
expect(new BinariusNumber([20, 1]).value).toBe(41); | ||
expect(new BinariusBigInt([20, 1, 3500, 5]).value).toBe(BigInt(1375759717)); | ||
expect(new BinariusBits([1, 0, 1]).value).toBe(5); | ||
}); | ||
describe('get', () => { | ||
it('returns a value of a given field', () => { | ||
expect(new Field([65535, 32767]).get('width')).toBe(65535); | ||
expect(new Field([65535, 32767]).get('height')).toBe(32767); | ||
expect(new LargePerson([20, 1, 3500, 5]).get('weight')).toBe(3500); | ||
expect(new LargePerson(BigInt(1375759717)).get('weight')).toBe(3500); | ||
expect(new PersonFlags([1, 0, 1]).get('gender')).toBe(0); | ||
expect(new PersonFlags(5).get('tall')).toBe(1); | ||
}); | ||
}); | ||
describe('get', () => { | ||
it('returns a value of a given field', () => { | ||
expect(new BinariusNumber([20, 1]).get('age')).toBe(20); | ||
expect(new BinariusNumber(41).get('age')).toBe(20); | ||
expect(new BinariusLargestNumber([65535, 32767]).get('width')).toBe(65535); | ||
expect(new BinariusLargestNumber([65535, 32767]).get('height')).toBe(32767); | ||
expect(new BinariusBigInt([20, 1, 3500, 5]).get('weight')).toBe(3500); | ||
expect(new BinariusBigInt(BigInt(1375759717)).get('weight')).toBe(3500); | ||
expect(new BinariusBits([1, 0, 1]).get('gender')).toBe(0); | ||
expect(new BinariusBits(5).get('tall')).toBe(1); | ||
}); | ||
describe('set', () => { | ||
it('sets a given value to a given field', () => { | ||
expect(new Field([65535, 32760]).set('height', 32767).get('height')).toBe(32767); | ||
expect(new LargePerson([20, 1, 3500, 5]).set('weight', 3000).get('weight')).toBe(3000); | ||
expect(new LargePerson(BigInt(1375759717)).set('age', 21).get('age')).toBe(21); | ||
expect(new PersonFlags([1, 0, 1]).set('gender', 1).get('gender')).toBe(1); | ||
expect(new PersonFlags([1, 0, 1]).set('gender').get('gender')).toBe(1); | ||
expect(new PersonFlags([1, 0, 1]).set('human', 0).get('human')).toBe(0); | ||
}); | ||
}); | ||
describe('set', () => { | ||
it('sets a given value to a given field', () => { | ||
expect(new BinariusNumber([20, 1]).set('age', 30).get('age')).toBe(30); | ||
expect(new BinariusLargestNumber([65535, 32760]).set('height', 32767).get('height')).toBe(32767); | ||
expect(new BinariusNumber([20, 0]).set('gender').get('gender')).toBe(1); | ||
expect(new BinariusNumber(41).set('gender', 0).get('gender')).toBe(0); | ||
expect(new BinariusBigInt([20, 1, 3500, 5]).set('weight', 3000).get('weight')).toBe(3000); | ||
expect(new BinariusBigInt(BigInt(1375759717)).set('age', 21).get('age')).toBe(21); | ||
expect(new BinariusBits([1, 0, 1]).set('gender', 1).get('gender')).toBe(1); | ||
expect(new BinariusBits([1, 0, 1]).set('human', 0).get('human')).toBe(0); | ||
}); | ||
describe('has', () => { | ||
it('checks if all specified fields are set in a given bitfield instance', () => { | ||
expect(new PersonFlags([1, 0, 1]).has('human', 'tall')).toBe(true); | ||
expect(new PersonFlags([1, 1, 1]).has('human', 'tall')).toBe(true); | ||
expect(new PersonFlags([0, 1, 1]).has('human', 'tall')).toBe(false); | ||
expect(new PersonFlags([1, 1, 0]).has('human', 'tall')).toBe(false); | ||
expect(new PersonFlags([0, 1, 0]).has('human', 'tall')).toBe(false); | ||
expect(new PersonFlags([0, 1, 0]).has('gender')).toBe(true); | ||
}); | ||
}); | ||
describe('has', () => { | ||
it('checks if all specified fields are set in a given bitfield instance', () => { | ||
expect(new BinariusBits([1, 0, 1]).has('human', 'tall')).toBe(true); | ||
expect(new BinariusBits([1, 1, 1]).has('human', 'tall')).toBe(true); | ||
expect(new BinariusBits([0, 1, 1]).has('human', 'tall')).toBe(false); | ||
expect(new BinariusBits([1, 1, 0]).has('human', 'tall')).toBe(false); | ||
expect(new BinariusBits([0, 1, 0]).has('human', 'tall')).toBe(false); | ||
expect(new BinariusBits([0, 1, 0]).has('gender')).toBe(true); | ||
}); | ||
describe('toValue', () => { | ||
it('returns value of an instance', () => { | ||
expect(new Field([20, 1]).toValue()).toBe(655361); | ||
expect(new LargePerson([20, 1, 3500, 5]).toValue()).toBe(BigInt(1375759717)); | ||
expect(new PersonFlags([1, 0, 1]).toValue()).toBe(5); | ||
}); | ||
}); | ||
describe('toValue', () => { | ||
it('returns value of an instance', () => { | ||
expect(new BinariusNumber([20, 1]).toValue()).toBe(41); | ||
expect(new BinariusBigInt([20, 1, 3500, 5]).toValue()).toBe(BigInt(1375759717)); | ||
expect(new BinariusBits([1, 0, 1]).toValue()).toBe(5); | ||
describe('toObject', () => { | ||
it('returns a plain object representation of an instance', () => { | ||
expect(new Field([20, 1]).toObject()).toEqual({ width: 20, height: 1 }); | ||
expect(new LargePerson([20, 1, 3500, 5]).toObject()).toEqual({ | ||
age: 20, | ||
gender: 1, | ||
weight: 3500, | ||
height: 5, | ||
}); | ||
expect(new PersonFlags([1, 0, 1]).toObject()).toEqual({ human: 1, gender: 0, tall: 1 }); | ||
}); | ||
}); | ||
describe('toObject', () => { | ||
it('returns a plain object representation of an instance', () => { | ||
expect(new BinariusNumber([20, 1]).toObject()).toEqual({ age: 20, gender: 1 }); | ||
expect(new BinariusBigInt([20, 1, 3500, 5]).toObject()).toEqual({ | ||
age: 20, | ||
gender: 1, | ||
weight: 3500, | ||
height: 5, | ||
}); | ||
expect(new BinariusBits([1, 0, 1]).toObject()).toEqual({ human: 1, gender: 0, tall: 1 }); | ||
}); | ||
describe('isValid', () => { | ||
class Person extends Binarius {} | ||
Person.fields = [{ name: 'age', size: 7 }, { name: 'gender', size: 1 }]; | ||
Person.initialize(); | ||
it('checks if a given set of values is valued according to the schema', () => { | ||
expect(Person.isValid([])).toBe(false); | ||
expect(Person.isValid([20, 0])).toBe(true); | ||
expect(Person.isValid([200, 1])).toBe(false); | ||
expect(Person.isValid([20])).toBe(false); | ||
expect(Person.isValid(['', ''])).toBe(false); | ||
}); | ||
it('checks if given pairs of Person name and value are valid according to the schema', () => { | ||
expect(Person.isValid({ age: 21 })).toBe(true); | ||
expect(Person.isValid({ age: 200 })).toBe(false); | ||
expect(Person.isValid({ age: -200 })).toBe(false); | ||
expect(Person.isValid({ age: 'asdf' })).toBe(false); | ||
expect(Person.isValid({ age: 21, gender: 1 })).toBe(true); | ||
expect(Person.isValid({ age: 21, gender: 4 })).toBe(false); | ||
}); | ||
}); | ||
}); |
{ | ||
"name": "binarius", | ||
"version": "0.0.5", | ||
"version": "0.0.7", | ||
"description": "Store and operate on data in Numbers and BigInts for memory savings, performance, and fun.", | ||
@@ -19,2 +19,3 @@ "main": "index.js", | ||
"devDependencies": { | ||
"@types/jest": "^23.3.12", | ||
"benchmark": "^2.1.4", | ||
@@ -21,0 +22,0 @@ "bitflags": "^1.0.0", |
@@ -20,42 +20,42 @@ # Binarius | ||
Construct: | ||
binarius (Number) x 51,868,502 ops/sec ±9.35% (73 runs sampled) | ||
tiny-binary-format x 27,811,598 ops/sec ±9.24% (75 runs sampled) | ||
Binarius BigInt x 998,165 ops/sec ±8.40% (77 runs sampled) | ||
binarius (Number) x 55,497,411 ops/sec ±1.43% (92 runs sampled) | ||
tiny-binary-format x 35,752,705 ops/sec ±0.11% (95 runs sampled) | ||
binarius (BigInt) x 1,356,673 ops/sec ±9.49% (74 runs sampled) | ||
Get Field: | ||
binarius (Number) x 10,399,734 ops/sec ±9.35% (73 runs sampled) | ||
tiny-binary-format x 16,948,777 ops/sec ±9.69% (72 runs sampled) | ||
binarius (BigInt) x 969,008 ops/sec ±10.44% (64 runs sampled) | ||
binarius (Number) x 16,288,487 ops/sec ±9.38% (73 runs sampled) | ||
tiny-binary-format x 20,619,945 ops/sec ±9.72% (71 runs sampled) | ||
binarius (BigInt) x 1,091,167 ops/sec ±9.14% (75 runs sampled) | ||
Set Field: | ||
binarius (Number) x 11,905,363 ops/sec ±9.10% (75 runs sampled) | ||
binarius (BigInt) x 577,570 ops/sec ±8.59% (81 runs sampled) | ||
binarius (Number) x 12,349,185 ops/sec ±9.53% (73 runs sampled) | ||
binarius (BigInt) x 833,874 ops/sec ±9.15% (75 runs sampled) | ||
Construct Small Bit Set: | ||
binarius x 12,680,530 ops/sec ±9.31% (70 runs sampled) | ||
parseInt x 11,408,948 ops/sec ±9.60% (61 runs sampled) | ||
fast-bitset x 2,008,394 ops/sec ±10.44% (60 runs sampled) | ||
bitflags x 1,882,259 ops/sec ±9.76% (60 runs sampled) | ||
binarius x 13,487,599 ops/sec ±10.04% (63 runs sampled) | ||
parseInt x 12,194,221 ops/sec ±10.72% (63 runs sampled) | ||
fast-bitset x 2,195,533 ops/sec ±9.05% (66 runs sampled) | ||
bitflags x 2,085,162 ops/sec ±9.10% (67 runs sampled) | ||
Get Small Bit Set: | ||
binarius x 33,838,562 ops/sec ±9.15% (65 runs sampled) | ||
Vanilla x 39,430,233 ops/sec ±9.54% (64 runs sampled) | ||
fast-bitset x 1,600,432 ops/sec ±8.72% (71 runs sampled) | ||
bitflags x 1,492,410 ops/sec ±9.44% (64 runs sampled) | ||
binarius x 26,146,290 ops/sec ±9.87% (65 runs sampled) | ||
vanilla x 72,901,685 ops/sec ±9.76% (75 runs sampled) | ||
fast-bitset x 2,077,717 ops/sec ±9.47% (65 runs sampled) | ||
bitflags x 2,205,870 ops/sec ±8.82% (71 runs sampled) | ||
Set Small Bit Set: | ||
binarius x 16,161,097 ops/sec ±10.18% (62 runs sampled) | ||
Vanilla x 69,220,698 ops/sec ±10.08% (64 runs sampled) | ||
fast-bitset x 1,955,982 ops/sec ±9.59% (59 runs sampled) | ||
bitflags x 1,883,057 ops/sec ±9.72% (60 runs sampled) | ||
binarius x 16,939,736 ops/sec ±10.00% (62 runs sampled) | ||
vanilla x 62,144,478 ops/sec ±9.08% (67 runs sampled) | ||
fast-bitset x 1,726,339 ops/sec ±9.24% (63 runs sampled) | ||
bitflags x 2,133,856 ops/sec ±9.25% (68 runs sampled) | ||
Construct Large Bit Set: | ||
binarius (BigInt) x 15,429,294 ops/sec ±10.49% (63 runs sampled) | ||
parseInt x 6,422,370 ops/sec ±10.01% (66 runs sampled) | ||
fast-bitset x 1,853,205 ops/sec ±8.88% (60 runs sampled) | ||
bitflags x 1,675,325 ops/sec ±9.37% (65 runs sampled) | ||
binarius (BigInt) x 13,367,659 ops/sec ±9.40% (72 runs sampled) | ||
parseInt x 6,472,672 ops/sec ±9.30% (76 runs sampled) | ||
fast-bitset x 1,749,372 ops/sec ±8.86% (66 runs sampled) | ||
bitflags x 2,089,318 ops/sec ±9.59% (70 runs sampled) | ||
Get Large Bit Set: | ||
binarius (BigInt) x 3,164,770 ops/sec ±9.84% (64 runs sampled) | ||
fast-bitset x 2,294,947 ops/sec ±9.02% (68 runs sampled) | ||
bitflags x 1,713,873 ops/sec ±9.45% (61 runs sampled) | ||
binarius (BigInt) x 2,796,970 ops/sec ±9.87% (60 runs sampled) | ||
fast-bitset x 2,194,670 ops/sec ±9.49% (67 runs sampled) | ||
bitflags x 2,057,710 ops/sec ±9.50% (65 runs sampled) | ||
Set Large Bit Set: | ||
binarius (BigInt) x 1,351,004 ops/sec ±10.80% (66 runs sampled) | ||
fast-bitset x 1,849,869 ops/sec ±9.61% (63 runs sampled) | ||
bitflags x 1,900,251 ops/sec ±10.02% (60 runs sampled) | ||
binarius (BigInt) x 1,403,656 ops/sec ±10.19% (64 runs sampled) | ||
fast-bitset x 1,932,073 ops/sec ±9.67% (68 runs sampled) | ||
bitflags x 1,872,754 ops/sec ±9.57% (58 runs sampled) | ||
``` | ||
## License | ||
MIT © [Maga D. Zandaqo](http://maga.name) |
18510
375
11