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.
|
* Set the bit at index `x` to 1.
|
||||||
* Automatically grows the internal buffer if necessary.
|
* Automatically grows the bitmap if necessary.
|
||||||
* @param {number} x - Bit index to set (0-based).
|
* **Mutates this bitmap** in-place.
|
||||||
* @returns {void}
|
* @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);
|
const idx = getBitPositionIndex(x);
|
||||||
if (idx >= this.bits.length) this.grow(x);
|
if (idx >= this.bits.length) this.grow(x);
|
||||||
this.bits[idx] |= getMask(getBitPosition(x));
|
this.bits[idx] |= getMask(getBitPosition(x));
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the bit at index `x` (set to 0).
|
* Clear the bit at index `x` (set to 0).
|
||||||
* If `x` is beyond current capacity, the call is a no-op.
|
* No-op if the index is outside current capacity.
|
||||||
* @param {number} x - Bit index to clear (0-based).
|
* **Mutates this bitmap** in-place.
|
||||||
* @returns {void}
|
* @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);
|
const idx = getBitPositionIndex(x);
|
||||||
if (idx >= this.bits.length) return;
|
if (idx >= this.bits.length) return this;
|
||||||
const mask = getMask(getBitPosition(x));
|
this.bits[idx] &= ~getMask(getBitPosition(x));
|
||||||
this.bits[idx] &= ~mask;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -175,94 +178,85 @@ export class Bitmap {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bitwise AND with another bitmap. Returns a new Bitmap containing the intersection.
|
* Bitwise AND with another bitmap.
|
||||||
* The result's capacity is equal to the left-hand bitmap's capacity (cloned from `this`).
|
* **Mutates this bitmap** — performs the operation in-place and clears extra bytes if needed.
|
||||||
* @param {Bitmap} bitmap - Other bitmap to AND with.
|
* @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 {
|
public and(bitmap: Bitmap): this {
|
||||||
const otherBits = (bitmap as Bitmap).bits;
|
const other = bitmap.bits;
|
||||||
const result = this.clone() as Bitmap;
|
const minlen = Math.min(this.bits.length, other.length);
|
||||||
const minlen = Math.min(this.bits.length, otherBits.length);
|
|
||||||
|
|
||||||
for (let i = 0; i < minlen; i++) {
|
for (let i = 0; i < minlen; i++) {
|
||||||
result.bits[i] &= otherBits[i];
|
this.bits[i] &= other[i];
|
||||||
}
|
}
|
||||||
|
for (let i = minlen; i < this.bits.length; i++) {
|
||||||
for (let i = minlen; i < result.bits.length; i++) {
|
this.bits[i] = 0;
|
||||||
result.bits[i] = 0;
|
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bitwise AND NOT (this & ~bitmap). Returns a new Bitmap.
|
* Bitwise AND NOT (this = this & ~bitmap).
|
||||||
* @param {Bitmap} bitmap - Other bitmap to subtract from this.
|
* **Mutates this bitmap** in-place.
|
||||||
* @returns {Bitmap} New Bitmap representing this & ~bitmap.
|
* @param {Bitmap} bitmap - Bitmap whose set bits will be subtracted.
|
||||||
|
* @returns {this} The instance itself (for method chaining).
|
||||||
*/
|
*/
|
||||||
public andNot(bitmap: Bitmap): Bitmap {
|
public andNot(bitmap: Bitmap): this {
|
||||||
const otherBits = (bitmap as Bitmap).bits;
|
const other = bitmap.bits;
|
||||||
const result = this.clone() as Bitmap;
|
const minlen = Math.min(this.bits.length, other.length);
|
||||||
const minlen = Math.min(this.bits.length, otherBits.length);
|
|
||||||
|
|
||||||
for (let i = 0; i < minlen; i++) {
|
for (let i = 0; i < minlen; i++) {
|
||||||
result.bits[i] &= ~otherBits[i];
|
this.bits[i] &= ~other[i];
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bitwise OR (union) with another bitmap. Returns a new Bitmap.
|
* Bitwise OR (union) with another bitmap.
|
||||||
* The resulting bitmap will be large enough to contain bits from both operands.
|
* **Mutates this bitmap** in-place and grows it if necessary.
|
||||||
* @param {Bitmap} bitmap - Other bitmap to OR with.
|
* @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 {
|
public or(bitmap: Bitmap): this {
|
||||||
const otherBits = (bitmap as Bitmap).bits;
|
const other = bitmap.bits;
|
||||||
let result = this.clone() as Bitmap;
|
|
||||||
|
|
||||||
if (otherBits.length > result.bits.length) {
|
if (other.length > this.bits.length) {
|
||||||
const extended = new Uint8Array(otherBits.length);
|
const newArr = new Uint8Array(other.length);
|
||||||
extended.set(result.bits);
|
newArr.set(this.bits);
|
||||||
result.bits = extended;
|
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++) {
|
for (let i = 0; i < maxlen; i++) {
|
||||||
const a = result.bits[i] ?? 0;
|
const a = this.bits[i] ?? 0;
|
||||||
const b = otherBits[i] ?? 0;
|
const b = other[i] ?? 0;
|
||||||
result.bits[i] = a | b;
|
this.bits[i] = a | b;
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bitwise XOR (symmetric difference) with another bitmap. Returns a new Bitmap.
|
* Bitwise XOR with another bitmap.
|
||||||
* The resulting bitmap will be large enough to contain bits from both operands.
|
* **Mutates this bitmap** in-place and grows it if necessary.
|
||||||
* @param {Bitmap} bitmap - Other bitmap to XOR with.
|
* @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 {
|
public xor(bitmap: Bitmap): this {
|
||||||
const otherBits = (bitmap as Bitmap).bits;
|
const other = bitmap.bits;
|
||||||
let result = this.clone() as Bitmap;
|
|
||||||
|
|
||||||
if (otherBits.length > result.bits.length) {
|
if (other.length > this.bits.length) {
|
||||||
const extended = new Uint8Array(otherBits.length);
|
const newArr = new Uint8Array(other.length);
|
||||||
extended.set(result.bits);
|
newArr.set(this.bits);
|
||||||
result.bits = extended;
|
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++) {
|
for (let i = 0; i < maxlen; i++) {
|
||||||
const a = result.bits[i] ?? 0;
|
const a = this.bits[i] ?? 0;
|
||||||
const b = otherBits[i] ?? 0;
|
const b = other[i] ?? 0;
|
||||||
result.bits[i] = a ^ b;
|
this.bits[i] = a ^ b;
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -286,8 +280,9 @@ export class Bitmap {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove bits for which `fn(bitIndex)` returns false. Mutates this bitmap.
|
* Remove bits for which the predicate returns `false`.
|
||||||
* @param {(x: number) => boolean} fn - Predicate called for each set bit index; if it returns false the bit is cleared.
|
* **Mutates this bitmap** in-place.
|
||||||
|
* @param {(bitIndex: number) => boolean} fn - Predicate; bit is cleared if `false` is returned.
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
public filter(fn: (x: number) => boolean): 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}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
public clear(): void {
|
public clear(): void {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue