Preview Ruby 4.1 is a preview. It can change before the final release. Switch to Ruby 4.0 →
String#bit_count
static VALUE
rb_str_bit_count(int argc, VALUE *argv, VALUE str)
{
VALUE v0, v1, opts;
uint64_t beg = 0, len = 0;
/* Count positional arguments so that an explicit nil is not mistaken for an omitted one. */
int nargs = rb_scan_args(argc, argv, "02:", &v0, &v1, &opts);
/*
* A whole-string popcount is independent of bit numbering.
* no-(offset|range)-argument form only validates lsb_first.
*/
bool lsb_first = str_lsb_first_from_opts(opts);
if (nargs == 0) {
return ULL2NUM(str_count_bits((const unsigned char *)RSTRING_PTR(str), RSTRING_LEN(str)));
}
bool is_range = rb_obj_is_kind_of(v0, rb_cRange);
struct str_bit_range range = {0};
if (is_range) {
if (nargs == 2) {
rb_raise(rb_eArgError, "bit length not allowed with a Range");
}
str_bit_range_to_offsets(v0, &range);
}
else if (nargs == 1) {
rb_raise(rb_eArgError, "no bit length given");
}
else {
beg = str_bit_offset_from_index(v0).value;
len = str_bit_length_from_index(v1);
}
const unsigned char *ptr = (const unsigned char *)RSTRING_PTR(str);
uint64_t total_bits = str_bit_size(RSTRING_LEN(str));
if (is_range) {
if (!str_bit_range_resolve(&range, total_bits, &beg, &len)) {
return INT2FIX(0);
}
}
else if (beg >= total_bits) {
return INT2FIX(0);
}
/* Reads clamp: only the part of the region that exists is counted. */
if (len > total_bits - beg) len = total_bits - beg;
if (len == 0) return INT2FIX(0);
return ULL2NUM(str_count_bits_region(ptr, beg, len, lsb_first));
}
Returns the number of set bits in self.
With no arguments, counts over all bytes of self:
"\x00".bit_count # => 0 "\xFF".bit_count # => 8 "\xAA".bit_count # => 4
With offset and length, counts only the length bits beginning at zero-based bit offset; with a Range, counts the bits covered by range:
data = "\xFF\x00\xF0" data.bit_count(0, 8) # => 8 data.bit_count(8, 8) # => 0 data.bit_count(0..7) # => 8 data.bit_count(8...16) # => 0 data.bit_count(16..) # => 4
A region that extends beyond the end of self is clamped to the bits that exist; a region that begins at or beyond the end counts zero:
data.bit_count(16, 100) # => 4 data.bit_count(100, 8) # => 0
By default, bits within each byte are numbered from least-significant to most-significant. If lsb_first is false, byte order is unchanged but bits within each byte are numbered from most-significant to least-significant. The numbering matters only when the region is not byte-aligned; the no-argument form is independent of it:
"\xF0".bit_count(0, 4) # => 0 "\xF0".bit_count(0, 4, lsb_first: false) # => 4
The count is over the bytes of self and is independent of string encoding. Raises IndexError if offset or a Range endpoint is negative. Raises ArgumentError if length is negative. Raises ArgumentError if a bit position is too large to be represented. Raises ArgumentError if lsb_first is neither true nor false.
Ruby version changes
-
4.1
-
Basic bit operations are added.
String#bit_get,String#bit_set?,String#bit_set,String#bit_clear,String#bit_flipandString#bit_counthandle individual bits, andString#bitwise_not,String#bitwise_and,String#bitwise_or,String#bitwise_xor(with their!variants) handle whole strings. [Feature #22118]
-