copied set implementations from MultiFlag JS
This commit is contained in:
64
node/src/flagsets/array.ts
Normal file
64
node/src/flagsets/array.ts
Normal 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
130
node/src/flagsets/base64.ts
Normal 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')
|
||||
}
|
||||
}
|
||||
54
node/src/flagsets/bigint.ts
Normal file
54
node/src/flagsets/bigint.ts
Normal 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)
|
||||
}
|
||||
}
|
||||
49
node/src/flagsets/collection.ts
Normal file
49
node/src/flagsets/collection.ts
Normal 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.')
|
||||
}
|
||||
}
|
||||
95
node/src/flagsets/index.ts
Normal file
95
node/src/flagsets/index.ts
Normal 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'
|
||||
36
node/src/flagsets/number.ts
Normal file
36
node/src/flagsets/number.ts
Normal 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user