Skip to content

Commit

Permalink
Allow up to 64 elementary features
Browse files Browse the repository at this point in the history
  • Loading branch information
fasttime committed Jul 8, 2024
1 parent 5243dde commit d4ffb7c
Show file tree
Hide file tree
Showing 11 changed files with 201 additions and 91 deletions.
14 changes: 13 additions & 1 deletion packages/~feature-hub/dev/impl.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,17 @@ export async function makeLib()
{
const { doMakeLib } = await importPackageUtils();
const pkgURL = new URL('..', import.meta.url);
await doMakeLib(pkgURL);
await
doMakeLib
(
pkgURL,
[
'feature.d.ts',
'index.d.ts',
'mask.d.ts',
'mask-impl.d.ts',
'mask-impl-64.d.ts',
'mask-index.d.ts',
],
);
}
5 changes: 2 additions & 3 deletions packages/~feature-hub/src/feature.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type util from 'node:util';

import
{ type Mask, maskAreEqual, maskIncludes, maskIntersection, maskNew, maskNext, maskUnion }
from './mask';
import { type Mask, maskAreEqual, maskIncludes, maskIntersection, maskNew, maskNext, maskUnion }
from './mask-impl';

import { MaskSet } from './mask-index';

Expand Down
6 changes: 3 additions & 3 deletions packages/~feature-hub/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from './feature';
export * from './mask';
export * from './mask-index';
export * from './feature';
export { maskAreEqual, maskIncludes, maskIntersection, maskNew, maskUnion } from './mask-impl';
export * from './mask-index';
79 changes: 79 additions & 0 deletions packages/~feature-hub/src/mask-impl-52.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import type { Mask } from './mask';

const BIN_POW_31 = 0x8000_0000;
const BIN_POW_32 = 0x1_0000_0000;
const BIN_POW_51 = 0x8_0000_0000_0000;
const BIT_MASK_31 = 0x7fff_ffff;

const EMPTY_MASK: Mask = 0 as never;

/** The maximum number of disjoint, non-empty masks supported by this implementation. */
export const MASK_MAX_SIZE = 52 as number;

/** Determines whether two specified masks are equal. */
export function maskAreEqual(mask1: Mask, mask2: Mask): boolean
{
return mask1 === mask2;
}

/** Determines whether a specified mask includes another one. */
export function maskIncludes(includingMask: Mask, includedMask: Mask): boolean
{
let includedLoValue: number;
let includedHiValue: number;
const returnValue =
(
(includingMask as never) &
(includedLoValue = (includedMask as never) | 0)
) ===
includedLoValue &&
(
includingMask as never / BIN_POW_32 &
(includedHiValue = includedMask as never / BIN_POW_32 | 0)
) ===
includedHiValue;
return returnValue;
}

/** Returns a new mask that is the intersection of two specified masks. */
export function maskIntersection(mask1: Mask, mask2: Mask): Mask
{
const intersectionMask: Mask =
(
((mask1 as unknown as number) & (mask2 as unknown as number) & BIT_MASK_31) +
((mask1 as never) / BIN_POW_31 & (mask2 as never) / BIN_POW_31) * BIN_POW_31
) as never;
return intersectionMask;
}

/** Returns a new empty mask. */
export function maskNew(): Mask
{
return EMPTY_MASK;
}

/**
* Returns a new non-empty mask that does not intersect the specified mask.
*
* @throws If the specified mask is full, a `RangeError` is thrown.
*/
export function maskNext(mask: Mask): Mask
{
let nextValue = 1;
for (let checkValue: number = mask as never; checkValue & 1; checkValue /= 2)
nextValue *= 2;
if (nextValue > BIN_POW_51)
throw RangeError('Mask full');
return nextValue as never;
}

/** Returns a new mask that is the union of two specified masks. */
export function maskUnion(mask1: Mask, mask2: Mask): Mask
{
const unionMask: Mask =
(
(((mask1 as unknown as number) | (mask2 as unknown as number)) & BIT_MASK_31) +
((mask1 as never) / BIN_POW_31 | (mask2 as never) / BIN_POW_31) * BIN_POW_31
) as never;
return unionMask;
}
79 changes: 79 additions & 0 deletions packages/~feature-hub/src/mask-impl-64.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import type { Mask } from './mask';

const LO_INDEX = 0;
const HI_INDEX = 1;

type LoHi = readonly [number, number];

const freezeMask = (_LoHi: LoHi): Mask => Object.freeze(_LoHi) as unknown as Mask;

/** The maximum number of disjoint, non-empty masks supported by this implementation. */
export const MASK_MAX_SIZE = 64 as number;

/** Determines whether two specified masks are equal. */
export function maskAreEqual(mask1: Mask, mask2: Mask): boolean
{
const _LoHi1 = mask1 as unknown as LoHi;
const _LoHi2 = mask2 as unknown as LoHi;
return _LoHi1[LO_INDEX] === _LoHi2[LO_INDEX] && _LoHi1[HI_INDEX] === _LoHi2[HI_INDEX];
}

/** Determines whether a specified mask includes another one. */
export function maskIncludes(includingMask: Mask, includedMask: Mask): boolean
{
const includingLoHi = includingMask as unknown as LoHi;
const includedLoHi = includedMask as unknown as LoHi;
let includedLoValue: number;
let includedHiValue: number;
const returnValue =
(includedLoValue = includedLoHi[LO_INDEX]) === (includedLoValue & includingLoHi[LO_INDEX]) &&
(includedHiValue = includedLoHi[HI_INDEX]) === (includedHiValue & includingLoHi[HI_INDEX]);
return returnValue;
}

/** Returns a new mask that is the intersection of two specified masks. */
export function maskIntersection(mask1: Mask, mask2: Mask): Mask
{
const _LoHi1 = mask1 as unknown as LoHi;
const _LoHi2 = mask2 as unknown as LoHi;
const intersectionLo = _LoHi1[LO_INDEX] & _LoHi2[LO_INDEX];
const intersectionHi = _LoHi1[HI_INDEX] & _LoHi2[HI_INDEX];
const intersectionLoHi: LoHi = [intersectionLo, intersectionHi];
return freezeMask(intersectionLoHi);
}

/** Returns a new empty mask. */
export function maskNew(): Mask
{
return freezeMask([0, 0]);
}

/**
* Returns a new non-empty mask that does not intersect the specified mask.
*
* @throws If the specified mask is full, a `RangeError` is thrown.
*/
export function maskNext(mask: Mask): Mask
{
const _LoHi = mask as unknown as LoHi;
let bitIndex = 0;
for (; _LoHi[bitIndex >> 5] & 1 << (bitIndex & 0x1F); bitIndex++)
{
if (bitIndex === 63)
throw RangeError('Mask full');
}
const nextLoHi: [number, number] = [0, 0];
nextLoHi[bitIndex >> 5] = 1 << (bitIndex & 0x1F);
return freezeMask(nextLoHi);
}

/** Returns a new mask that is the union of two specified masks. */
export function maskUnion(mask1: Mask, mask2: Mask): Mask
{
const _LoHi1 = mask1 as unknown as LoHi;
const _LoHi2 = mask2 as unknown as LoHi;
const unionLo = _LoHi1[LO_INDEX] | _LoHi2[LO_INDEX];
const unionHi = _LoHi1[HI_INDEX] | _LoHi2[HI_INDEX];
const unionLoHi: LoHi = [unionLo, unionHi];
return freezeMask(unionLoHi);
}
2 changes: 2 additions & 0 deletions packages/~feature-hub/src/mask-impl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export type { Mask } from './mask';
export * from './mask-impl-64';
75 changes: 0 additions & 75 deletions packages/~feature-hub/src/mask.ts
Original file line number Diff line number Diff line change
@@ -1,82 +1,7 @@
const BIN_POW_31 = 0x8000_0000;
const BIN_POW_32 = 0x1_0000_0000;
const BIN_POW_51 = 0x8_0000_0000_0000;
const BIT_MASK_31 = 0x7fff_ffff;

const EMPTY_MASK: Mask = 0 as never;

/** A vector of boolean elements, intended for efficient bulk operations. */
export interface Mask
{
[MaskSymbol]: never;
}

declare const MaskSymbol: unique symbol;

/** Determines whether two specified masks are equal. */
export function maskAreEqual(mask1: Mask, mask2: Mask): boolean
{
return mask1 === mask2;
}

/** Determines whether a specified mask includes another one. */
export function maskIncludes(includingMask: Mask, includedMask: Mask): boolean
{
let includedLoValue: number;
let includedHiValue: number;
const returnValue =
(
(includingMask as never) &
(includedLoValue = (includedMask as never) | 0)
) ===
includedLoValue &&
(
includingMask as never / BIN_POW_32 &
(includedHiValue = includedMask as never / BIN_POW_32 | 0)
) ===
includedHiValue;
return returnValue;
}

/** Returns a new mask that is the intersection of two specified masks. */
export function maskIntersection(mask1: Mask, mask2: Mask): Mask
{
const intersectionMask: Mask =
(
((mask1 as unknown as number) & (mask2 as unknown as number) & BIT_MASK_31) +
((mask1 as never) / BIN_POW_31 & (mask2 as never) / BIN_POW_31) * BIN_POW_31
) as never;
return intersectionMask;
}

/** Returns a new empty mask. */
export function maskNew(): Mask
{
return EMPTY_MASK;
}

/**
* Returns a new non-empty mask that does not intersect the specified mask.
*
* @throws If the specified mask is full, a `RangeError` is thrown.
*/
export function maskNext(mask: Mask): Mask
{
let nextValue = 1;
for (let checkValue: number = mask as never; checkValue & 1; checkValue /= 2)
nextValue *= 2;
if (nextValue > BIN_POW_51)
throw RangeError('Mask full');
return nextValue as never;
}

/** Returns a new mask that is the union of two specified masks. */
export function maskUnion(mask1: Mask, mask2: Mask): Mask
{
const unionMask: Mask =
(
(((mask1 as unknown as number) | (mask2 as unknown as number)) & BIT_MASK_31) +
((mask1 as never) / BIN_POW_31 | (mask2 as never) / BIN_POW_31) * BIN_POW_31
) as never;
return unionMask;
}
5 changes: 2 additions & 3 deletions packages/~feature-hub/test/spec/feature.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ import
}
from '../../src/feature';

import
{ type Mask, maskAreEqual, maskIntersection, maskNew, maskNext, maskUnion }
from '../../src/mask';
import { type Mask, maskAreEqual, maskIntersection, maskNew, maskNext, maskUnion }
from '../../src/mask-impl';

import assert from 'assert';
import type util from 'util';
Expand Down
2 changes: 1 addition & 1 deletion packages/~feature-hub/test/spec/mask-index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { maskNew } from '../../src/mask';
import { maskNew } from '../../src/mask-impl';
import { MaskMap, MaskSet } from '../../src/mask-index';
import assert from 'assert';

Expand Down
15 changes: 12 additions & 3 deletions packages/~feature-hub/test/spec/mask.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import
{ type Mask, maskAreEqual, maskIncludes, maskIntersection, maskNew, maskNext, maskUnion }
from '../../src/mask';
{
MASK_MAX_SIZE,
type Mask,
maskAreEqual,
maskIncludes,
maskIntersection,
maskNew,
maskNext,
maskUnion,
}
from '../../src/mask-impl';

import assert, { AssertionError } from 'assert';

Expand Down Expand Up @@ -73,7 +82,7 @@ it
(): void =>
{
let prevMask = maskNew();
for (let count = 0; count < 52; ++count)
for (let count = 0; count < MASK_MAX_SIZE; ++count)
{
const nextMask = maskNext(prevMask);
assertMaskNotEmpty(nextMask);
Expand Down
10 changes: 8 additions & 2 deletions src/lib/debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,19 @@ if (typeof NO_DEBUG === 'undefined')
if (inputEntries)
{
if (_Array_isArray(inputEntries))
outputEntries = inputEntries.map(clone);
outputEntries = inputEntries.map(cloneEntry);
else
outputEntries = [createEntryClone(inputEntries, EMPTY_MASK)];
}
return outputEntries;
}

function cloneEntry(inputEntry)
{
var entry = createEntryClone(inputEntry.definition, inputEntry.mask);
return entry;
}

function createEncoder(features)
{
var mask = getFeatureMask(features);
Expand Down Expand Up @@ -123,7 +129,7 @@ if (typeof NO_DEBUG === 'undefined')

function getComplexEntry(complex)
{
var entry = clone(COMPLEX[complex]);
var entry = cloneEntry(COMPLEX[complex]);
return entry;
}

Expand Down

0 comments on commit d4ffb7c

Please sign in to comment.