1
0
mirror of https://github.com/robonen/tools.git synced 2026-03-20 02:44:45 +00:00

Merge pull request #4 from robonen/stdlib-bitsflags

feat(packages/stdlib): add flagsGenerator and bitwise operation funct…
This commit is contained in:
2024-04-17 05:08:22 +07:00
committed by GitHub
2 changed files with 208 additions and 0 deletions

View File

@@ -0,0 +1,118 @@
import { describe, it, expect } from 'vitest';
import { flagsGenerator, and, or, not, has, is, unset, toggle } from './index';
describe('flagsGenerator', () => {
it('generate unique flags', () => {
const generateFlag = flagsGenerator();
const flag1 = generateFlag();
const flag2 = generateFlag();
const flag3 = generateFlag();
expect(flag1).toBe(1);
expect(flag2).toBe(2);
expect(flag3).toBe(4);
});
it('throw an error if more than 31 flags are created', () => {
const generateFlag = flagsGenerator();
for (let i = 0; i < 31; i++) {
generateFlag();
}
expect(() => generateFlag()).toThrow(RangeError);
});
});
describe('flagsAnd', () => {
it('no effect on zero flags', () => {
const result = and();
expect(result).toBe(-1);
});
it('source flag is returned if no flags are provided', () => {
const result = and(0b1010);
expect(result).toBe(0b1010);
});
it('perform bitwise AND operation on flags', () => {
const result = and(0b1111, 0b1010, 0b1100);
expect(result).toBe(0b1000);
});
});
describe('flagsOr', () => {
it('no effect on zero flags', () => {
const result = or();
expect(result).toBe(0);
});
it('source flag is returned if no flags are provided', () => {
const result = or(0b1010);
expect(result).toBe(0b1010);
});
it('perform bitwise OR operation on flags', () => {
const result = or(0b1111, 0b1010, 0b1100);
expect(result).toBe(0b1111);
});
});
describe('flagsNot', () => {
it('perform bitwise NOT operation on a flag', () => {
const result = not(0b101);
expect(result).toBe(-0b110);
});
});
describe('flagsHas', () => {
it('check if a flag has a specific bit set', () => {
const result = has(0b1010, 0b1000);
expect(result).toBe(true);
});
it('check if a flag has a specific bit unset', () => {
const result = has(0b1010, 0b0100);
expect(result).toBe(false);
});
});
describe('flagsIs', () => {
it('check if a flag is set', () => {
const result = is(0b1010);
expect(result).toBe(true);
});
it('check if a flag is unset', () => {
const result = is(0);
expect(result).toBe(false);
});
});
describe('flagsUnset', () => {
it('unset a flag', () => {
const result = unset(0b1010, 0b1000);
expect(result).toBe(0b0010);
});
});
describe('flagsToggle', () => {
it('toggle a flag', () => {
const result = toggle(0b1010, 0b1000);
expect(result).toBe(0b0010);
});
});

View File

@@ -0,0 +1,90 @@
/**
* Create a function that generates unique flags
*
* @returns {Function} A function that generates unique flags
* @throws {RangeError} If more than 31 flags are created
*/
export function flagsGenerator() {
let lastFlag = 0;
return () => {
// 31 flags is the maximum number of flags that can be created
// (without zero) because of the 32-bit integer limit in bitwise operations
if (lastFlag & 0x40000000)
throw new RangeError('Cannot create more than 31 flags');
return (lastFlag = lastFlag === 0 ? 1 : lastFlag << 1);
};
}
/**
* Function to combine multiple flags using the AND operator
*
* @param {number[]} flags - The flags to combine
* @returns {number} The combined flags
*/
export function and(...flags: number[]) {
return flags.reduce((acc, flag) => acc & flag, -1);
}
/**
* Function to combine multiple flags using the OR operator
*
* @param {number[]} flags - The flags to combine
* @returns {number} The combined flags
*/
export function or(...flags: number[]) {
return flags.reduce((acc, flag) => acc | flag, 0);
}
/**
* Function to apply the NOT operator to a flag
*
* @param {number} flag - The flag to apply the NOT operator to
* @returns {number} The result of the NOT operator
*/
export function not(flag: number) {
return ~flag;
}
/**
* Function to make sure a flag has a specific bit set
*
* @param {number} flag - The flag to check
* @returns {boolean} Whether the flag has the bit set
*/
export function has(flag: number, other: number) {
return (flag & other) === other;
}
/**
* Function to check if a flag is set
*
* @param {number} flag - The flag to check
* @returns {boolean} Whether the flag is set
*/
export function is(flag: number) {
return flag !== 0;
}
/**
* Function to unset a flag
*
* @param {number} flag - Source flag
* @param {number} other - Flag to unset
* @returns {number} The new flag
*/
export function unset(flag: number, other: number) {
return flag & ~other;
}
/**
* Function to toggle (xor) a flag
*
* @param {number} flag - Source flag
* @param {number} other - Flag to toggle
* @returns {number} The new flag
*/
export function toggle(flag: number, other: number) {
return flag ^ other;
}