refactor(bitmap): switch to mutating API and return `this` from all setters
The Bitmap API is now fully mutating: - all state-changing methods (`set`, `remove`, `grow`, `clear`, `filter`) return `this` - bitwise operations (`and`, `or`, `xor`, `andNot`) no longer clone internally and now mutate the current bitmap - functional (immutable-style) usage is still possible via explicit `.clone()` - removed implicit cloning from all arithmetic operations This change makes the mutation semantics consistent and predictable, while keeping optional immutability available through manual cloning.
This commit is contained in:
parent
21761e5dac
commit
594ec641a4
136
src/bitmap.ts
136
src/bitmap.ts
|
|
@ -123,27 +123,30 @@ export class Bitmap {
|
|||
|
||||
/**
|
||||
* Set the bit at index `x` to 1.
|
||||
* Automatically grows the internal buffer if necessary.
|
||||
* @param {number} x - Bit index to set (0-based).
|
||||
* @returns {void}
|
||||
* Automatically grows the bitmap if necessary.
|
||||
* **Mutates this bitmap** in-place.
|
||||
* @param {number} x - Bit index (0-based).
|
||||
* @returns {this} The instance itself (for method chaining).
|
||||
*/
|
||||
public set(x: number): void {
|
||||
public set(x: number): this {
|
||||
const idx = getBitPositionIndex(x);
|
||||
if (idx >= this.bits.length) this.grow(x);
|
||||
this.bits[idx] |= getMask(getBitPosition(x));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the bit at index `x` (set to 0).
|
||||
* If `x` is beyond current capacity, the call is a no-op.
|
||||
* @param {number} x - Bit index to clear (0-based).
|
||||
* @returns {void}
|
||||
* No-op if the index is outside current capacity.
|
||||
* **Mutates this bitmap** in-place.
|
||||
* @param {number} x - Bit index (0-based).
|
||||
* @returns {this} The instance itself (for method chaining).
|
||||
*/
|
||||
public remove(x: number): void {
|
||||
public remove(x: number): this {
|
||||
const idx = getBitPositionIndex(x);
|
||||
if (idx >= this.bits.length) return;
|
||||
const mask = getMask(getBitPosition(x));
|
||||
this.bits[idx] &= ~mask;
|
||||
if (idx >= this.bits.length) return this;
|
||||
this.bits[idx] &= ~getMask(getBitPosition(x));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -175,94 +178,85 @@ export class Bitmap {
|
|||
}
|
||||
|
||||
/**
|
||||
* Bitwise AND with another bitmap. Returns a new Bitmap containing the intersection.
|
||||
* The result's capacity is equal to the left-hand bitmap's capacity (cloned from `this`).
|
||||
* Bitwise AND with another bitmap.
|
||||
* **Mutates this bitmap** — performs the operation in-place and clears extra bytes if needed.
|
||||
* @param {Bitmap} bitmap - Other bitmap to AND with.
|
||||
* @returns {Bitmap} New Bitmap representing this & bitmap.
|
||||
* @returns {this} The instance itself (for method chaining).
|
||||
*/
|
||||
public and(bitmap: Bitmap): Bitmap {
|
||||
const otherBits = (bitmap as Bitmap).bits;
|
||||
const result = this.clone() as Bitmap;
|
||||
const minlen = Math.min(this.bits.length, otherBits.length);
|
||||
public and(bitmap: Bitmap): this {
|
||||
const other = bitmap.bits;
|
||||
const minlen = Math.min(this.bits.length, other.length);
|
||||
|
||||
for (let i = 0; i < minlen; i++) {
|
||||
result.bits[i] &= otherBits[i];
|
||||
this.bits[i] &= other[i];
|
||||
}
|
||||
|
||||
for (let i = minlen; i < result.bits.length; i++) {
|
||||
result.bits[i] = 0;
|
||||
for (let i = minlen; i < this.bits.length; i++) {
|
||||
this.bits[i] = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitwise AND NOT (this & ~bitmap). Returns a new Bitmap.
|
||||
* @param {Bitmap} bitmap - Other bitmap to subtract from this.
|
||||
* @returns {Bitmap} New Bitmap representing this & ~bitmap.
|
||||
* Bitwise AND NOT (this = this & ~bitmap).
|
||||
* **Mutates this bitmap** in-place.
|
||||
* @param {Bitmap} bitmap - Bitmap whose set bits will be subtracted.
|
||||
* @returns {this} The instance itself (for method chaining).
|
||||
*/
|
||||
public andNot(bitmap: Bitmap): Bitmap {
|
||||
const otherBits = (bitmap as Bitmap).bits;
|
||||
const result = this.clone() as Bitmap;
|
||||
const minlen = Math.min(this.bits.length, otherBits.length);
|
||||
public andNot(bitmap: Bitmap): this {
|
||||
const other = bitmap.bits;
|
||||
const minlen = Math.min(this.bits.length, other.length);
|
||||
|
||||
for (let i = 0; i < minlen; i++) {
|
||||
result.bits[i] &= ~otherBits[i];
|
||||
this.bits[i] &= ~other[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitwise OR (union) with another bitmap. Returns a new Bitmap.
|
||||
* The resulting bitmap will be large enough to contain bits from both operands.
|
||||
* Bitwise OR (union) with another bitmap.
|
||||
* **Mutates this bitmap** in-place and grows it if necessary.
|
||||
* @param {Bitmap} bitmap - Other bitmap to OR with.
|
||||
* @returns {Bitmap} New Bitmap representing this | bitmap.
|
||||
* @returns {this} The instance itself (for method chaining).
|
||||
*/
|
||||
public or(bitmap: Bitmap): Bitmap {
|
||||
const otherBits = (bitmap as Bitmap).bits;
|
||||
let result = this.clone() as Bitmap;
|
||||
public or(bitmap: Bitmap): this {
|
||||
const other = bitmap.bits;
|
||||
|
||||
if (otherBits.length > result.bits.length) {
|
||||
const extended = new Uint8Array(otherBits.length);
|
||||
extended.set(result.bits);
|
||||
result.bits = extended;
|
||||
if (other.length > this.bits.length) {
|
||||
const newArr = new Uint8Array(other.length);
|
||||
newArr.set(this.bits);
|
||||
this.bits = newArr;
|
||||
}
|
||||
|
||||
const maxlen = Math.max(result.bits.length, otherBits.length);
|
||||
const maxlen = Math.max(this.bits.length, other.length);
|
||||
for (let i = 0; i < maxlen; i++) {
|
||||
const a = result.bits[i] ?? 0;
|
||||
const b = otherBits[i] ?? 0;
|
||||
result.bits[i] = a | b;
|
||||
const a = this.bits[i] ?? 0;
|
||||
const b = other[i] ?? 0;
|
||||
this.bits[i] = a | b;
|
||||
}
|
||||
|
||||
return result;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitwise XOR (symmetric difference) with another bitmap. Returns a new Bitmap.
|
||||
* The resulting bitmap will be large enough to contain bits from both operands.
|
||||
* Bitwise XOR with another bitmap.
|
||||
* **Mutates this bitmap** in-place and grows it if necessary.
|
||||
* @param {Bitmap} bitmap - Other bitmap to XOR with.
|
||||
* @returns {Bitmap} New Bitmap representing this ^ bitmap.
|
||||
* @returns {this} The instance itself (for method chaining).
|
||||
*/
|
||||
public xor(bitmap: Bitmap): Bitmap {
|
||||
const otherBits = (bitmap as Bitmap).bits;
|
||||
let result = this.clone() as Bitmap;
|
||||
public xor(bitmap: Bitmap): this {
|
||||
const other = bitmap.bits;
|
||||
|
||||
if (otherBits.length > result.bits.length) {
|
||||
const extended = new Uint8Array(otherBits.length);
|
||||
extended.set(result.bits);
|
||||
result.bits = extended;
|
||||
if (other.length > this.bits.length) {
|
||||
const newArr = new Uint8Array(other.length);
|
||||
newArr.set(this.bits);
|
||||
this.bits = newArr;
|
||||
}
|
||||
|
||||
const maxlen = Math.max(result.bits.length, otherBits.length);
|
||||
const maxlen = Math.max(this.bits.length, other.length);
|
||||
for (let i = 0; i < maxlen; i++) {
|
||||
const a = result.bits[i] ?? 0;
|
||||
const b = otherBits[i] ?? 0;
|
||||
result.bits[i] = a ^ b;
|
||||
const a = this.bits[i] ?? 0;
|
||||
const b = other[i] ?? 0;
|
||||
this.bits[i] = a ^ b;
|
||||
}
|
||||
|
||||
return result;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -286,8 +280,9 @@ export class Bitmap {
|
|||
}
|
||||
|
||||
/**
|
||||
* Remove bits for which `fn(bitIndex)` returns false. Mutates this bitmap.
|
||||
* @param {(x: number) => boolean} fn - Predicate called for each set bit index; if it returns false the bit is cleared.
|
||||
* Remove bits for which the predicate returns `false`.
|
||||
* **Mutates this bitmap** in-place.
|
||||
* @param {(bitIndex: number) => boolean} fn - Predicate; bit is cleared if `false` is returned.
|
||||
* @returns {void}
|
||||
*/
|
||||
public filter(fn: (x: number) => boolean): void {
|
||||
|
|
@ -302,7 +297,8 @@ export class Bitmap {
|
|||
}
|
||||
|
||||
/**
|
||||
* Clear all bits (set all to zero).
|
||||
* Clear all bits in the bitmap (set everything to 0).
|
||||
* **Mutates this bitmap** in-place.
|
||||
* @returns {void}
|
||||
*/
|
||||
public clear(): void {
|
||||
|
|
|
|||
Loading…
Reference in New Issue