Skip to content

CFBitVector: fix multiple correctness bugs#56

Open
DTW-Thalion wants to merge 1 commit into
gnustep:masterfrom
DTW-Thalion:fix/cfbitvector-correctness
Open

CFBitVector: fix multiple correctness bugs#56
DTW-Thalion wants to merge 1 commit into
gnustep:masterfrom
DTW-Thalion:fix/cfbitvector-correctness

Conversation

@DTW-Thalion

Copy link
Copy Markdown
Contributor

Summary

CFBitVector is substantially broken for mutable vectors and for several
query operations. This fixes the bugs exercised by the new
Tests/CFBitVector/general.m:

  • CFBitVectorBitMask computed 0xFF << n in int arithmetic and
    right-shifted before truncating to a byte, so any mask with mostSig > 0
    was wrong — e.g. CFBitVectorSetBitAtIndex set several bits instead of one.
    Truncate to UInt8 first.
  • CFBitVectorOperation single-byte mask used
    BitMask(startBit, startBit + endBit) instead of BitMask(startBit, endBit).
  • CFBitVectorGetBits had an empty body; implemented (packs the range,
    MSB first, matching CFBitVectorGetBitAtIndex).
  • CFBitVectorGetFirstIndexOfBit looped while idx < range.length
    instead of idx < range.location + range.length.
  • CFBitVectorGetLastIndexOfBit started at range.location + range.length
    with condition idx < range.location, so the loop never ran and it always
    returned kCFNotFound.
  • Mutable _byteCount was never set (left at 0 by the count-0 immutable
    constructor), so CFBitVectorSetAllBits was a no-op and CFBitVectorSetCount
    lost the byte count on every grow. Now tracked in CFBitVectorCreateMutable
    and CFBitVectorSetCount, and newly grown storage is zeroed.

Test

Tests/CFBitVector/general.m covers SetCount/SetAllBits, single-bit
set/get, GetFirstIndexOfBit/GetLastIndexOfBit over sub-ranges, and
GetBits. It fails on every point above before this change and passes after.

/* GetBits packs the requested range, most-significant bit first.
Range (4,8) covers bits 4..11; the set bits 5 and 11 map to range
offsets 1 and 7 -> 0b01000001 = 0x41. */
CFBitVectorGetBits (bv, CFRangeMake(4, 8), out);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What should happen when the range is out of bounds or if any index is out of bounds in the other functions? Is this UB or should we expect an exception?

#include "../CFTesting.h"

int main (void)
{

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's also check some other bit vector sizes that are not a multiple of 8, for example size 13, size 0, size 1.

Range (4,8) covers bits 4..11; the set bits 5 and 11 map to range
offsets 1 and 7 -> 0b01000001 = 0x41. */
CFBitVectorGetBits (bv, CFRangeMake(4, 8), out);
PASS_CF(out[0] == 0x41, "GetBits packs the requested range.");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also test a range larger than one byte, for example range size 11.

CFBitVector was substantially broken; this fixes the bugs that the new
Tests/CFBitVector/general.m exercises:

* CFBitVectorBitMask computed 0xFF << n in int arithmetic and right-
  shifted before truncating to a byte, so any mask with mostSig > 0 was
  wrong (e.g. setting a single bit set several).  Truncate to UInt8
  first.
* CFBitVectorOperation single-byte mask used
  BitMask(startBit, startBit + endBit) instead of
  BitMask(startBit, endBit).
* CFBitVectorGetBits had an empty body; implement it (pack the range,
  most-significant bit first, matching CFBitVectorGetBitAtIndex).
* CFBitVectorGetFirstIndexOfBit looped while idx < range.length instead
  of idx < range.location + range.length.
* CFBitVectorGetLastIndexOfBit started at range.location + range.length
  with condition idx < range.location, so the loop never ran and it
  always returned kCFNotFound.
* Mutable bit vectors never set _byteCount (left at 0 by the count-0
  immutable constructor), so CFBitVectorSetAllBits was a no-op and
  CFBitVectorSetCount lost the byte count on every grow.  Track
  _byteCount in CFBitVectorCreateMutable and CFBitVectorSetCount and
  zero newly grown storage.
@DTW-Thalion DTW-Thalion force-pushed the fix/cfbitvector-correctness branch from 252d15d to 23f8806 Compare July 3, 2026 14:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants