copied set implementations from MultiFlag JS

This commit is contained in:
2026-02-09 22:11:23 +01:00
parent 80c17ac3ac
commit 140896bd7c
56 changed files with 8055 additions and 24 deletions

View File

@@ -0,0 +1,64 @@
import type { FlagSet } from '.'
import { EnumerateFlags } from '../enumeration'
export class ArrayFlagSet<T> implements FlagSet<T, T[]> {
public none(): T[] {
return []
}
public union(first: T[], second: T[]): T[] {
const unionArray: T[] = []
for (const item of first) {
if (!unionArray.includes(item)) {
unionArray.push(item)
}
}
for (const item of second) {
if (!unionArray.includes(item)) {
unionArray.push(item)
}
}
return unionArray
}
public intersection(first: T[], second: T[]): T[] {
const intersectionArray: T[] = []
for (const item of first) {
if (!intersectionArray.includes(item) && second.includes(item)) {
intersectionArray.push(item)
}
}
return intersectionArray
}
public difference(first: T[], second: T[]): T[] {
const differenceArray: T[] = []
for (const item of first) {
if (!differenceArray.includes(item) && !second.includes(item)) {
differenceArray.push(item)
}
}
return differenceArray
}
public isSuperset(first: T[], second: T[]): boolean {
for (const item of second) {
if (!first.includes(item)) {
return false
}
}
return true
}
public enumerate(flags: T[]): EnumerateFlags<T> {
return flags
}
maximum(flags: T[]): T[] {
throw new Error('Method not implemented.')
}
minimum(flags: T[]): T[] {
throw new Error('Method not implemented.')
}
}

130
node/src/flagsets/base64.ts Normal file
View File

@@ -0,0 +1,130 @@
import type { FlagSet } from '.'
import {
decodeB64Byte,
encodeB64Byte,
normaliseB64String,
ZERO_STRING,
} from '../base64'
import {
Base64BitflagIterator,
EnumerateFlags,
useIterator,
} from '../enumeration'
/**
* Provides flags that are stored in strings using a little-endian base 64
* representation.
*
* This format is compact, easily serializable and allows for an unlimited
* number of flags, but it is specific to Tatsuki. Use {@link CollectionFlagSet}
* instead if you need the data to be easily understandable by other systems.
*/
export class Base64BitFlagSet implements FlagSet<number, string> {
/*protected wrapValue(value: number): string {
if (value < 1) {
throw new RangeError(
'Indices should be greater than or equal to 1.'
)
}
const indexFromZero = value - 1
const leadingBytes = ZERO_STRING.repeat(indexFromZero / 6)
const bigEnd = encodeByte(1 << indexFromZero % 6)
return leadingBytes + bigEnd
}*/
public none(): string {
return ''
}
public union(first: string, second: string): string {
let result = ''
let shorter, longer
if (first.length < second.length) {
shorter = first
longer = second
} else {
shorter = second
longer = first
}
let i = 0
// OR the bytes one by one
for (; i < shorter.length; i++) {
const value = decodeB64Byte(shorter[i]) | decodeB64Byte(longer[i])
result += encodeB64Byte(value)
}
// if one string is longer than the other, append the remaining bytes (x | 0 = x)
for (; i < longer.length; i++) {
result += longer[i]
}
return normaliseB64String(result)
}
public intersection(first: string, second: string): string {
let result = ''
const shorterLength = Math.min(first.length, second.length)
let i = 0
// AND the bytes one by one
for (; i < shorterLength; i++) {
const value = decodeB64Byte(first[i]) & decodeB64Byte(second[i])
result += encodeB64Byte(value)
}
// if one string is longer than the other, don't add anything else (x & 0 = 0)
return normaliseB64String(result)
}
public difference(first: string, second: string): string {
let result = ''
const shorterLength = Math.min(first.length, second.length)
let i = 0
// AND the bytes one by one
for (; i < shorterLength; i++) {
const value = decodeB64Byte(first[i]) & ~decodeB64Byte(second[i])
result += encodeB64Byte(value)
}
// if the first string is longer than the other, append its remaining bytes (x & ~0 = x)
// if the second string is longer, don't add anything (0 & ~y = 0)
for (; i < first.length; i++) {
result += first[i]
}
return normaliseB64String(result)
}
public isSuperset(first: string, second: string): boolean {
let result = true
const shorterLength = Math.min(first.length, second.length)
let i = 0
// AND the bytes one by one and check
// if one is false we don't need to decode further
for (; i < shorterLength && result; i++) {
const secondValue = decodeB64Byte(second[i])
result = (decodeB64Byte(first[i]) & secondValue) == secondValue
}
// if there are more characters in the second string, they must all be zeros
// (0 & x is only equal to x when x is also 0)
for (; i < second.length && result; i++) {
result = second[i] == ZERO_STRING
}
return result
}
public enumerate(flags: string): EnumerateFlags<number> {
return useIterator(flags, Base64BitflagIterator)
}
maximum(flags: string): string {
throw new Error('not implemented')
}
minimum(flags: string): string {
throw new Error('not implemented')
}
}

View File

@@ -0,0 +1,54 @@
import type { FlagSet } from '.'
import { UnavailableFeatureError } from '../errors'
import { ENV_BI } from '../env'
import {
BigBitFlagsIterator,
EnumerateFlags,
useIterator,
} from '../enumeration'
export class BigBitFlagSet implements FlagSet<bigint, bigint> {
/**
* Creates a new empty flag set.
*
* @throws UnavailableFeatureError When this constructor is called in an
* environment that does not natively support {@link BigInt}s.
*/
public constructor() {
if (!ENV_BI.AVAILABLE) {
throw new UnavailableFeatureError('BigInts')
}
}
minimum(flags: bigint): bigint {
throw new Error('Method not implemented.')
}
maximum(flags: bigint): bigint {
throw new Error('Method not implemented.')
}
public none(): bigint {
return ENV_BI.ZERO
}
public union(first: bigint, second: bigint): bigint {
return first | second
}
public intersection(first: bigint, second: bigint): bigint {
return first & second
}
public difference(first: bigint, second: bigint): bigint {
return first & ~second
}
public isSuperset(first: bigint, second: bigint): boolean {
return (first & second) == second
}
public enumerate(flags: bigint): EnumerateFlags<bigint> {
return useIterator(flags, BigBitFlagsIterator)
}
}

View File

@@ -0,0 +1,49 @@
import type { FlagSet } from '.'
import { EnumerateFlags, useIterator } from '../enumeration'
import { ENV_SET } from '../env'
import { UnavailableFeatureError } from '../errors'
export class CollectionFlagSet<T> implements FlagSet<T, Set<T>> {
/**
* Creates a new empty flag set.
*
* @throws UnavailableFeatureError When this constructor is called in an
* environment that does not natively support {@link Set}s.
*/
public constructor() {
if (!ENV_SET.AVAILABLE) {
throw new UnavailableFeatureError('Sets')
}
}
public none(): Set<T> {
return new Set()
}
public union(first: Set<T>, second: Set<T>): Set<T> {
return ENV_SET.union.call(first, second) as Set<T>
}
public intersection(first: Set<T>, second: Set<T>): Set<T> {
return ENV_SET.intersection.call(first, second) as Set<T>
}
public difference(first: Set<T>, second: Set<T>): Set<T> {
return ENV_SET.difference.call(first, second) as Set<T>
}
public isSuperset(first: Set<T>, second: Set<T>): boolean {
return ENV_SET.isSupersetOf.call(first, second)
}
public enumerate(flags: Set<T>): EnumerateFlags<T> {
return flags
}
minimum(flags: Set<T>): Set<T> {
throw new Error('Method not implemented.')
}
maximum(flags: Set<T>): Set<T> {
throw new Error('Method not implemented.')
}
}

View File

@@ -0,0 +1,95 @@
import { EnumerateFlags } from '../enumeration'
/**
* Represents a group of flags of type `F` and the relationships between
* them. It also provides methods to use `S` as a set of those flags.
*
* @typeParam F The type of values in the set.
* @typeParam S The type to be used as a set.
*/
export interface FlagSet<F, S> {
/**
* Creates an empty set of flags.
*/
none(): S
/**
* Computes the union of two sets of flags.
*
* @param first - The first set of flags.
* @param second - The second set of flags.
*
* @returns A new set that contains the flags of both sets.
*/
union(first: S, second: S): S
/**
* Computes the intersection of two set of flags.
*
* @param first - The first set of flags.
* @param second - The second set of flags.
*
* @returns A new set that contains the flags that appear both in the first
* set and the second set.
*/
intersection(first: S, second: S): S
/**
* Computes the difference of two set of flags.
*
* @param first - The first set of flags.
* @param second - The second set of flags (that will be subtracted from the
* first).
*
* @returns A new set that contains the flags of the first set that do not
* appear in the second.
*/
difference(first: S, second: S): S
/**
* Checks whether the first set of flags is a superset of the second.
*
* @param first - The first set of flags.
* @param second - The second set of flags.
*/
isSuperset(first: S, second: S): boolean
/**
* Returns an iterable over the individual flags in a set.
*
* @param flags - A set of flags.
*/
enumerate(flags: S): EnumerateFlags<F>
/**
* Filters a flag set so that it only contains the flags that were declared
* with the {@link flag} method. If a flags is missing some of its parents,
* it will not be included in the result.
*
* @param flags The set of flags to filter.
*
* @returns A new set of flags.
*
* @see maximum
*/
minimum(flags: S): S
/**
* Creates a copy of a flag set that will contain all the flags that were
* declared with the {@link flag} method. If a flags is missing some of its
* parents in the original set, they will be added to the result.
*
* @param flags The set of flags to filter.
*
* @returns A new set of flags.
*
* @see minimum
*/
maximum(flags: S): S
}
export { ArrayFlagSet } from './array'
export { Base64BitFlagSet } from './base64'
export { BigBitFlagSet } from './bigint'
export { CollectionFlagSet } from './collection'
export { BitFlagSet } from './number'

View File

@@ -0,0 +1,36 @@
import type { FlagSet } from '.'
import { BitFlagsIterator, EnumerateFlags, useIterator } from '../enumeration'
export class BitFlagSet implements FlagSet<number, number> {
public none(): number {
return 0
}
public union(first: number, second: number): number {
return first | second
}
public intersection(first: number, second: number): number {
return first & second
}
public difference(first: number, second: number): number {
return first & ~second
}
public isSuperset(first: number, second: number): boolean {
return (first & second) == second
}
public enumerate(flags: number): EnumerateFlags<number> {
return useIterator(flags, BitFlagsIterator)
}
public maximum(flags: number): number {
return 0
}
public minimum(flags: number): number {
return 0
}
}