string_bits 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/ext/string_bits/string_bits.c +692 -547
- metadata +1 -1
|
@@ -70,7 +70,7 @@ sb_popcount64(uint64_t x)
|
|
|
70
70
|
/* ctz / clz helpers for set-bit iteration ---------------------------------- */
|
|
71
71
|
|
|
72
72
|
static ID id_bracket;
|
|
73
|
-
static VALUE sym_lsb_first,
|
|
73
|
+
static VALUE sym_lsb_first, sym_invert;
|
|
74
74
|
|
|
75
75
|
enum sb_kw_flag {
|
|
76
76
|
SB_KW_INVERT = 1 << 0,
|
|
@@ -190,27 +190,10 @@ integer_to_bit_idx(VALUE n)
|
|
|
190
190
|
UNREACHABLE_RETURN(0);
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
rb_raise(rb_eTypeError, "bit index must be an integer");
|
|
198
|
-
}
|
|
199
|
-
ssize_t idx = integer_to_bit_idx(n);
|
|
200
|
-
ssize_t size = RSTRING_LEN(self) * 8;
|
|
201
|
-
if (idx < 0 || idx >= size) {
|
|
202
|
-
rb_raise(rb_eIndexError, "bit index out of range");
|
|
203
|
-
}
|
|
204
|
-
if (!lsb_first) idx = (idx & ~7L) | (7 - (idx & 7L));
|
|
205
|
-
return idx;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
static inline ssize_t
|
|
209
|
-
physical_to_count_from(ssize_t physical, int lsb_first)
|
|
210
|
-
{
|
|
211
|
-
return lsb_first ? physical : ((physical & ~7L) | (7 - (physical & 7L)));
|
|
212
|
-
}
|
|
213
|
-
|
|
193
|
+
/* Bit numbering between byte-with-LSB-as-bit-0 and byte-with-MSB-as-bit-0
|
|
194
|
+
* is an involution: swapping in either direction uses the same formula
|
|
195
|
+
* `(x & ~7) | (7 - (x & 7))`. logical_to_physical is therefore symmetric and
|
|
196
|
+
* is reused on the return path (physical -> logical) as well. */
|
|
214
197
|
static inline ssize_t
|
|
215
198
|
logical_to_physical(ssize_t logical, int lsb_first)
|
|
216
199
|
{
|
|
@@ -237,6 +220,20 @@ logical_write_bit(unsigned char *ptr, ssize_t logical_index, int lsb_first, int
|
|
|
237
220
|
physical_write_bit(ptr, logical_to_physical(logical_index, lsb_first), bit);
|
|
238
221
|
}
|
|
239
222
|
|
|
223
|
+
static ssize_t
|
|
224
|
+
check_bit_index(VALUE self, VALUE n, int lsb_first)
|
|
225
|
+
{
|
|
226
|
+
if (!rb_integer_type_p(n)) {
|
|
227
|
+
rb_raise(rb_eTypeError, "bit index must be an integer");
|
|
228
|
+
}
|
|
229
|
+
ssize_t idx = integer_to_bit_idx(n);
|
|
230
|
+
ssize_t size = RSTRING_LEN(self) * 8;
|
|
231
|
+
if (idx < 0 || idx >= size) {
|
|
232
|
+
rb_raise(rb_eIndexError, "bit index out of range");
|
|
233
|
+
}
|
|
234
|
+
return logical_to_physical(idx, lsb_first);
|
|
235
|
+
}
|
|
236
|
+
|
|
240
237
|
/* ssize_t-interface wrapper around rb_range_beg_len.
|
|
241
238
|
*
|
|
242
239
|
* rb_range_beg_len() takes (long *begp, long *lenp, long len), but this
|
|
@@ -266,6 +263,44 @@ sb_range_beg_len_call(VALUE arg)
|
|
|
266
263
|
return rb_range_beg_len(a->range, a->lbegp, a->llenp, a->len, a->err);
|
|
267
264
|
}
|
|
268
265
|
|
|
266
|
+
/* Validate Range endpoints for bit position arguments.
|
|
267
|
+
* Raises ArgumentError for:
|
|
268
|
+
* - any explicit (non-nil) Bignum endpoint: cannot address any real string,
|
|
269
|
+
* consistent with integer_to_bit_idx behavior for scalar indices.
|
|
270
|
+
* - any explicit (non-nil) negative endpoint: count-from-end semantics
|
|
271
|
+
* interact confusingly with lsb_first: true/false.
|
|
272
|
+
* RBIGNUM_NEGATIVE_P is used for the negativity check on Bignums to avoid
|
|
273
|
+
* calling NUM2LL on values that do not fit in long long.
|
|
274
|
+
*
|
|
275
|
+
* Porting to Ruby Core:
|
|
276
|
+
* Replace rb_range_values() with direct struct access:
|
|
277
|
+
* #include "internal/range.h"
|
|
278
|
+
* beg = RANGE_BEG(range);
|
|
279
|
+
* end = RANGE_END(range);
|
|
280
|
+
* excl = RANGE_EXCL(range);
|
|
281
|
+
*/
|
|
282
|
+
static void
|
|
283
|
+
sb_range_validate_endpoints(VALUE range)
|
|
284
|
+
{
|
|
285
|
+
VALUE beg, end;
|
|
286
|
+
int excl;
|
|
287
|
+
rb_range_values(range, &beg, &end, &excl);
|
|
288
|
+
if (!NIL_P(beg) && rb_integer_type_p(beg)) {
|
|
289
|
+
if (!FIXNUM_P(beg))
|
|
290
|
+
rb_raise(rb_eArgError, "bit index out of representable range");
|
|
291
|
+
if (FIX2LONG(beg) < 0)
|
|
292
|
+
rb_raise(rb_eIndexError,
|
|
293
|
+
"negative Range endpoint is not allowed for bit positions");
|
|
294
|
+
}
|
|
295
|
+
if (!NIL_P(end) && rb_integer_type_p(end)) {
|
|
296
|
+
if (!FIXNUM_P(end))
|
|
297
|
+
rb_raise(rb_eArgError, "bit index out of representable range");
|
|
298
|
+
if (FIX2LONG(end) < 0)
|
|
299
|
+
rb_raise(rb_eIndexError,
|
|
300
|
+
"negative Range endpoint is not allowed for bit positions");
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
269
304
|
static inline VALUE
|
|
270
305
|
sb_range_beg_len(VALUE range, ssize_t *begp, ssize_t *lenp, ssize_t len, int err)
|
|
271
306
|
{
|
|
@@ -325,53 +360,42 @@ parse_lsb_first_opt(VALUE opts)
|
|
|
325
360
|
return parse_bool_opt(opts, sym_lsb_first, "lsb_first", 1);
|
|
326
361
|
}
|
|
327
362
|
|
|
328
|
-
|
|
329
|
-
|
|
363
|
+
/* Parse an optional start_offset positional argument (Qnil => 0).
|
|
364
|
+
* Raises ArgumentError for Bignum, IndexError for negative Fixnum. */
|
|
365
|
+
static ssize_t
|
|
366
|
+
parse_start_offset(VALUE v)
|
|
330
367
|
{
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
368
|
+
if (NIL_P(v)) return 0;
|
|
369
|
+
ssize_t start_offset = integer_to_bit_idx(v); /* raises ArgumentError for Bignum */
|
|
370
|
+
if (start_offset < 0)
|
|
371
|
+
rb_raise(rb_eIndexError, "bit_offset must be non-negative");
|
|
372
|
+
return start_offset;
|
|
335
373
|
}
|
|
336
374
|
|
|
337
375
|
/* read -------------------------------------------------------------------- */
|
|
338
376
|
|
|
339
|
-
/*
|
|
340
|
-
*
|
|
341
|
-
* bit_at uses flat/Arrow convention: byte_index = n/8 from start, bit = n%8 from LSB
|
|
342
|
-
* e.g. "\xAA\xCC": bit 0..7 live in byte[0]=0xAA, bit 8..15 live in byte[1]=0xCC
|
|
343
|
-
*
|
|
344
|
-
* str = "\xFF\xAA" # 11111111 10101010
|
|
345
|
-
* str.bit_at(0) # => true (1st bit is set)
|
|
346
|
-
* str.bit_at(7) # => true (8th bit is set)
|
|
347
|
-
* str.bit_at(8) # => false (9th bit is clear)
|
|
348
|
-
* str.bit_at(9) # => true (10th bit is set)
|
|
349
|
-
* str.bit_at(16) # => nil
|
|
350
|
-
*/
|
|
377
|
+
/* Return true/false/nil for the bit at flat position n. */
|
|
351
378
|
static VALUE
|
|
352
379
|
rb_str_bit_at(int argc, VALUE *argv, VALUE self)
|
|
353
380
|
{
|
|
354
|
-
VALUE
|
|
355
|
-
rb_scan_args(argc, argv, "1:", &
|
|
381
|
+
VALUE bit_offset_v, opts;
|
|
382
|
+
rb_scan_args(argc, argv, "1:", &bit_offset_v, &opts);
|
|
356
383
|
validate_option_hash(opts, SB_KW_LSB_FIRST);
|
|
357
384
|
|
|
358
|
-
if (!rb_integer_type_p(
|
|
385
|
+
if (!rb_integer_type_p(bit_offset_v)) {
|
|
359
386
|
rb_raise(rb_eTypeError, "bit index must be an integer");
|
|
360
387
|
}
|
|
361
|
-
ssize_t
|
|
362
|
-
if (
|
|
363
|
-
rb_raise(
|
|
388
|
+
ssize_t bit_offset = integer_to_bit_idx(bit_offset_v);
|
|
389
|
+
if (bit_offset < 0) {
|
|
390
|
+
rb_raise(rb_eIndexError, "bit index out of range");
|
|
364
391
|
}
|
|
365
392
|
ssize_t size = RSTRING_LEN(self) * 8;
|
|
366
|
-
if (size <=
|
|
393
|
+
if (size <= bit_offset) {
|
|
367
394
|
return Qnil;
|
|
368
395
|
}
|
|
369
396
|
|
|
370
397
|
int lsb_first = parse_lsb_first_opt(opts);
|
|
371
|
-
|
|
372
|
-
if (!lsb_first) {
|
|
373
|
-
idx = (idx & ~7L) | (7 - (idx & 7L));
|
|
374
|
-
}
|
|
398
|
+
ssize_t idx = logical_to_physical(bit_offset, lsb_first);
|
|
375
399
|
|
|
376
400
|
if (test_bit(RSTRING_PTR(self), idx)) {
|
|
377
401
|
return Qtrue;
|
|
@@ -380,19 +404,21 @@ rb_str_bit_at(int argc, VALUE *argv, VALUE self)
|
|
|
380
404
|
}
|
|
381
405
|
}
|
|
382
406
|
|
|
383
|
-
|
|
384
|
-
|
|
407
|
+
/* count_set_bits: popcount over a raw byte buffer.
|
|
408
|
+
*
|
|
409
|
+
* Uses a 32-byte (4 x uint64_t) unrolled inner loop, falls back to 8-byte
|
|
410
|
+
* steps, and finally collects the partial trailing bytes into a single
|
|
411
|
+
* uint64_t for one more popcount. memcpy avoids unaligned-load issues on
|
|
412
|
+
* strict-alignment platforms (SPARC, MIPS); modern compilers fold the 8-byte
|
|
413
|
+
* memcpy into a single load on platforms that allow unaligned access. */
|
|
414
|
+
static ssize_t
|
|
415
|
+
count_set_bits(const unsigned char *str, ssize_t len)
|
|
385
416
|
{
|
|
386
417
|
ssize_t count = 0;
|
|
387
|
-
ssize_t len = RSTRING_LEN(self);
|
|
388
|
-
const char *str = RSTRING_PTR(self);
|
|
389
418
|
ssize_t off = 0;
|
|
390
419
|
ssize_t unrolled_end = len & ~31L;
|
|
391
420
|
ssize_t aligned_end = len & ~7L;
|
|
392
421
|
|
|
393
|
-
/* Use memcpy to avoid unaligned loads (SIGBUS on SPARC, MIPS, etc.)
|
|
394
|
-
* and strict-aliasing violations. Modern compilers fold 8-byte memcpy
|
|
395
|
-
* into a single load on platforms that allow unaligned access. */
|
|
396
422
|
for (; off < unrolled_end; off += 32) {
|
|
397
423
|
uint64_t w0, w1, w2, w3;
|
|
398
424
|
memcpy(&w0, str + off, 8);
|
|
@@ -414,208 +440,373 @@ rb_str_bit_count(VALUE self)
|
|
|
414
440
|
ssize_t remainder = len - aligned_end;
|
|
415
441
|
if (remainder > 0) {
|
|
416
442
|
uint64_t last = 0;
|
|
417
|
-
const unsigned char *tail =
|
|
443
|
+
const unsigned char *tail = str + aligned_end;
|
|
418
444
|
for (ssize_t i = 0; i < remainder; i++) {
|
|
419
445
|
last |= (uint64_t)tail[i] << (i * 8);
|
|
420
446
|
}
|
|
421
447
|
count += sb_popcount64(last);
|
|
422
448
|
}
|
|
423
449
|
|
|
424
|
-
return
|
|
450
|
+
return count;
|
|
425
451
|
}
|
|
426
452
|
|
|
427
|
-
/*
|
|
453
|
+
/* count_set_bits_range: popcount over [start, start+length) in LSB-first numbering.
|
|
454
|
+
* Handles non-byte-aligned start and length by masking partial first/last bytes. */
|
|
455
|
+
static ssize_t
|
|
456
|
+
count_set_bits_range(const unsigned char *str, ssize_t total_bytes,
|
|
457
|
+
ssize_t start, ssize_t length)
|
|
458
|
+
{
|
|
459
|
+
if (length <= 0) return 0;
|
|
460
|
+
ssize_t total_bits = total_bytes * 8;
|
|
461
|
+
if (start >= total_bits) return 0;
|
|
462
|
+
if (start + length > total_bits) length = total_bits - start;
|
|
428
463
|
|
|
429
|
-
|
|
430
|
-
|
|
464
|
+
ssize_t byte_start = start >> 3;
|
|
465
|
+
int bit_lo = (int)(start & 7);
|
|
466
|
+
ssize_t end_bit = start + length;
|
|
467
|
+
ssize_t last_byte = (end_bit - 1) >> 3;
|
|
468
|
+
int e_bit = (int)(end_bit & 7); /* bits to use in last byte; 0 means full byte */
|
|
469
|
+
|
|
470
|
+
if (byte_start == last_byte) {
|
|
471
|
+
unsigned int b = (unsigned int)str[byte_start] >> bit_lo;
|
|
472
|
+
b &= (1u << (unsigned)length) - 1u;
|
|
473
|
+
return (ssize_t)sb_popcount64(b);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
ssize_t count = 0;
|
|
477
|
+
if (bit_lo != 0) {
|
|
478
|
+
count += sb_popcount64((unsigned int)str[byte_start] >> bit_lo);
|
|
479
|
+
byte_start++;
|
|
480
|
+
}
|
|
481
|
+
ssize_t full_last = (e_bit == 0) ? last_byte + 1 : last_byte;
|
|
482
|
+
count += count_set_bits(str + byte_start, full_last - byte_start);
|
|
483
|
+
if (e_bit != 0) {
|
|
484
|
+
unsigned int b = (unsigned int)str[last_byte] & ((1u << (unsigned)e_bit) - 1u);
|
|
485
|
+
count += sb_popcount64(b);
|
|
486
|
+
}
|
|
487
|
+
return count;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/* count_set_bits_range_msb: same as count_set_bits_range but for MSB-first numbering.
|
|
491
|
+
* In MSB-first, position 0 within a byte is physical bit 7 (the MSB). */
|
|
492
|
+
static ssize_t
|
|
493
|
+
count_set_bits_range_msb(const unsigned char *str, ssize_t total_bytes,
|
|
494
|
+
ssize_t start, ssize_t length)
|
|
431
495
|
{
|
|
432
|
-
|
|
496
|
+
if (length <= 0) return 0;
|
|
497
|
+
ssize_t total_bits = total_bytes * 8;
|
|
498
|
+
if (start >= total_bits) return 0;
|
|
499
|
+
if (start + length > total_bits) length = total_bits - start;
|
|
433
500
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
501
|
+
ssize_t byte_start = start >> 3;
|
|
502
|
+
int s_bit = (int)(start & 7); /* MSB-first within-byte start index */
|
|
503
|
+
ssize_t end_bit = start + length;
|
|
504
|
+
ssize_t last_byte = (end_bit - 1) >> 3;
|
|
505
|
+
int e_bit = (int)(end_bit & 7); /* bits to use in last byte; 0 means full byte */
|
|
437
506
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
}
|
|
444
|
-
} else {
|
|
445
|
-
for (int j = 7; j >= 0; j--) {
|
|
446
|
-
rb_yield((b >> j) & 1 ? Qtrue : Qfalse);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
507
|
+
if (byte_start == last_byte) {
|
|
508
|
+
/* physical bits (7-s_bit) down to (7-s_bit-length+1) */
|
|
509
|
+
unsigned int b = (unsigned int)str[byte_start] >> (unsigned)(8 - s_bit - (int)length);
|
|
510
|
+
b &= (1u << (unsigned)length) - 1u;
|
|
511
|
+
return (ssize_t)sb_popcount64(b);
|
|
449
512
|
}
|
|
450
513
|
|
|
451
|
-
|
|
514
|
+
ssize_t count = 0;
|
|
515
|
+
/* partial first byte: MSB-first positions s_bit..7 = physical bits 0..(7-s_bit) */
|
|
516
|
+
if (s_bit != 0) {
|
|
517
|
+
unsigned int b = (unsigned int)str[byte_start] & ((1u << (unsigned)(8 - s_bit)) - 1u);
|
|
518
|
+
count += sb_popcount64(b);
|
|
519
|
+
byte_start++;
|
|
520
|
+
}
|
|
521
|
+
ssize_t full_last = (e_bit == 0) ? last_byte + 1 : last_byte;
|
|
522
|
+
count += count_set_bits(str + byte_start, full_last - byte_start);
|
|
523
|
+
/* partial last byte: MSB-first positions 0..(e_bit-1) = physical bits (8-e_bit)..7 */
|
|
524
|
+
if (e_bit != 0) {
|
|
525
|
+
unsigned int b = (unsigned int)str[last_byte] >> (unsigned)(8 - e_bit);
|
|
526
|
+
count += sb_popcount64(b);
|
|
527
|
+
}
|
|
528
|
+
return count;
|
|
452
529
|
}
|
|
453
530
|
|
|
454
531
|
static VALUE
|
|
455
|
-
|
|
532
|
+
rb_str_bit_count(int argc, VALUE *argv, VALUE self)
|
|
456
533
|
{
|
|
457
|
-
int lsb_first = parse_lsb_first(argc, argv);
|
|
458
|
-
ssize_t len = RSTRING_LEN(self);
|
|
459
534
|
const unsigned char *str = (const unsigned char *)RSTRING_PTR(self);
|
|
460
|
-
ssize_t
|
|
461
|
-
int have_block = rb_block_given_p();
|
|
535
|
+
ssize_t src_len = RSTRING_LEN(self);
|
|
462
536
|
|
|
463
|
-
VALUE
|
|
537
|
+
VALUE v0 = Qnil, v1 = Qnil, opts = Qnil;
|
|
538
|
+
rb_scan_args(argc, argv, "02:", &v0, &v1, &opts);
|
|
539
|
+
validate_option_hash(opts, SB_KW_LSB_FIRST);
|
|
464
540
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
541
|
+
/* No positional args: count the whole string; lsb_first: is ignored (order-independent) */
|
|
542
|
+
if (NIL_P(v0))
|
|
543
|
+
return SSIZET2NUM(count_set_bits(str, src_len));
|
|
544
|
+
|
|
545
|
+
int lsb_first = parse_lsb_first_opt(opts);
|
|
546
|
+
ssize_t total_bits = src_len * 8;
|
|
547
|
+
ssize_t bit_offset, bit_length;
|
|
548
|
+
|
|
549
|
+
if (rb_obj_is_kind_of(v0, rb_cRange)) {
|
|
550
|
+
if (!NIL_P(v1))
|
|
551
|
+
rb_raise(rb_eArgError, "wrong number of arguments");
|
|
552
|
+
sb_range_validate_endpoints(v0);
|
|
553
|
+
ssize_t beg, len;
|
|
554
|
+
if (!RTEST(sb_range_beg_len(v0, &beg, &len, total_bits, 0)))
|
|
555
|
+
return INT2FIX(0);
|
|
556
|
+
bit_offset = beg;
|
|
557
|
+
bit_length = len;
|
|
558
|
+
}
|
|
559
|
+
else if (!NIL_P(v1)) {
|
|
560
|
+
if (!rb_integer_type_p(v0))
|
|
561
|
+
rb_raise(rb_eTypeError, "bit_offset must be an integer");
|
|
562
|
+
if (!rb_integer_type_p(v1))
|
|
563
|
+
rb_raise(rb_eTypeError, "bit_length must be an integer");
|
|
564
|
+
bit_offset = integer_to_bit_idx(v0);
|
|
565
|
+
if (bit_offset < 0)
|
|
566
|
+
rb_raise(rb_eIndexError, "bit_offset must be non-negative");
|
|
567
|
+
bit_length = integer_to_bit_idx(v1);
|
|
568
|
+
if (bit_length < 0)
|
|
569
|
+
rb_raise(rb_eArgError, "bit_length must be non-negative");
|
|
570
|
+
}
|
|
571
|
+
else {
|
|
572
|
+
rb_raise(rb_eArgError,
|
|
573
|
+
"wrong number of arguments (given 1, expected 0, 1 Range, or 2)");
|
|
478
574
|
}
|
|
479
575
|
|
|
480
|
-
|
|
576
|
+
if (lsb_first)
|
|
577
|
+
return SSIZET2NUM(count_set_bits_range(str, src_len, bit_offset, bit_length));
|
|
578
|
+
else
|
|
579
|
+
return SSIZET2NUM(count_set_bits_range_msb(str, src_len, bit_offset, bit_length));
|
|
481
580
|
}
|
|
482
581
|
|
|
483
|
-
/* iterate
|
|
582
|
+
/* iterate bits ------------------------------------------------------------ */
|
|
484
583
|
|
|
485
|
-
|
|
486
|
-
|
|
584
|
+
/* Unified emitter for each_bit / bits.
|
|
585
|
+
*
|
|
586
|
+
* Yields (when ary == Qnil) or pushes to a pre-allocated Array. lsb_first is
|
|
587
|
+
* hoisted outside the byte loop so the inner walk direction is straight-line
|
|
588
|
+
* code, removing a per-byte branch.
|
|
589
|
+
*/
|
|
590
|
+
static void
|
|
591
|
+
emit_bits(const unsigned char *str, ssize_t len, int lsb_first, ssize_t start_offset, VALUE ary)
|
|
487
592
|
{
|
|
488
|
-
|
|
593
|
+
if (start_offset >= len * 8) return;
|
|
594
|
+
|
|
595
|
+
#define SB_EMIT(v) \
|
|
596
|
+
do { VALUE _b = (v); \
|
|
597
|
+
if (ary == Qnil) rb_yield(_b); else rb_ary_push(ary, _b); } while (0)
|
|
598
|
+
|
|
599
|
+
ssize_t byte_start = start_offset >> 3;
|
|
600
|
+
int bit_start = (int)(start_offset & 7);
|
|
489
601
|
|
|
490
|
-
int lsb_first = parse_lsb_first(argc, argv);
|
|
491
|
-
ssize_t len = RSTRING_LEN(self);
|
|
492
|
-
const unsigned char *str = (const unsigned char *)RSTRING_PTR(self);
|
|
493
602
|
if (lsb_first) {
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
ssize_t n_words = len >> 3;
|
|
500
|
-
for (ssize_t wi = 0; wi < n_words; wi++) {
|
|
501
|
-
uint64_t w;
|
|
502
|
-
memcpy(&w, str + wi * 8, 8);
|
|
503
|
-
while (w != 0) {
|
|
504
|
-
int bit = sb_ctzll(w);
|
|
505
|
-
rb_yield(SSIZET2NUM(wi * 64 + bit));
|
|
506
|
-
w &= w - 1;
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
for (ssize_t bi = n_words << 3; bi < len; bi++) {
|
|
510
|
-
unsigned int b = str[bi];
|
|
511
|
-
while (b != 0) {
|
|
512
|
-
int bit = sb_ctz8(b);
|
|
513
|
-
rb_yield(SSIZET2NUM(bi * 8 + bit));
|
|
514
|
-
b &= b - 1;
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
#else
|
|
518
|
-
for (ssize_t bi = 0; bi < len; bi++) {
|
|
519
|
-
unsigned int b = str[bi];
|
|
520
|
-
while (b != 0) {
|
|
521
|
-
int bit = sb_ctz8(b);
|
|
522
|
-
rb_yield(SSIZET2NUM(bi * 8 + bit));
|
|
523
|
-
b &= b - 1;
|
|
603
|
+
for (ssize_t i = byte_start; i < len; i++) {
|
|
604
|
+
unsigned char b = str[i];
|
|
605
|
+
int j_start = (i == byte_start) ? bit_start : 0;
|
|
606
|
+
for (int j = j_start; j < 8; j++) {
|
|
607
|
+
SB_EMIT((b >> j) & 1 ? Qtrue : Qfalse);
|
|
524
608
|
}
|
|
525
609
|
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
while (b != 0) {
|
|
533
|
-
int bit = sb_highest_bit8(b);
|
|
534
|
-
ssize_t physical = bi * 8 + bit;
|
|
535
|
-
rb_yield(SSIZET2NUM(physical_to_count_from(physical, 0)));
|
|
536
|
-
b ^= (1u << bit); /* clear highest set bit */
|
|
610
|
+
} else {
|
|
611
|
+
for (ssize_t i = byte_start; i < len; i++) {
|
|
612
|
+
unsigned char b = str[i];
|
|
613
|
+
int j_end = (i == byte_start) ? (7 - bit_start) : 7;
|
|
614
|
+
for (int j = j_end; j >= 0; j--) {
|
|
615
|
+
SB_EMIT((b >> j) & 1 ? Qtrue : Qfalse);
|
|
537
616
|
}
|
|
538
617
|
}
|
|
539
618
|
}
|
|
540
619
|
|
|
620
|
+
#undef SB_EMIT
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
static VALUE
|
|
624
|
+
rb_str_each_bit(int argc, VALUE *argv, VALUE self)
|
|
625
|
+
{
|
|
626
|
+
RETURN_ENUMERATOR(self, argc, argv);
|
|
627
|
+
|
|
628
|
+
VALUE start_offset_v = Qnil, opts = Qnil;
|
|
629
|
+
rb_scan_args(argc, argv, "01:", &start_offset_v, &opts);
|
|
630
|
+
validate_option_hash(opts, SB_KW_LSB_FIRST);
|
|
631
|
+
int lsb_first = parse_lsb_first_opt(opts);
|
|
632
|
+
ssize_t start_offset = parse_start_offset(start_offset_v);
|
|
633
|
+
|
|
634
|
+
emit_bits((const unsigned char *)RSTRING_PTR(self), RSTRING_LEN(self),
|
|
635
|
+
lsb_first, start_offset, Qnil);
|
|
541
636
|
return self;
|
|
542
637
|
}
|
|
543
638
|
|
|
544
639
|
static VALUE
|
|
545
|
-
|
|
640
|
+
rb_str_bits(int argc, VALUE *argv, VALUE self)
|
|
546
641
|
{
|
|
547
|
-
|
|
642
|
+
VALUE start_offset_v = Qnil, opts = Qnil;
|
|
643
|
+
rb_scan_args(argc, argv, "01:", &start_offset_v, &opts);
|
|
644
|
+
validate_option_hash(opts, SB_KW_LSB_FIRST);
|
|
645
|
+
int lsb_first = parse_lsb_first_opt(opts);
|
|
646
|
+
ssize_t start_offset = parse_start_offset(start_offset_v);
|
|
548
647
|
ssize_t len = RSTRING_LEN(self);
|
|
549
648
|
const unsigned char *str = (const unsigned char *)RSTRING_PTR(self);
|
|
550
|
-
int have_block = rb_block_given_p();
|
|
551
649
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
}
|
|
556
|
-
else {
|
|
557
|
-
/* Pre-size the Array with popcount to avoid repeated reallocation.
|
|
558
|
-
* memcpy avoids unaligned-load issues on strict-alignment platforms. */
|
|
559
|
-
ssize_t count = 0;
|
|
560
|
-
ssize_t nw = len >> 3;
|
|
561
|
-
for (ssize_t wi = 0; wi < nw; wi++) {
|
|
562
|
-
uint64_t w;
|
|
563
|
-
memcpy(&w, str + wi * 8, 8);
|
|
564
|
-
count += sb_popcount64(w);
|
|
565
|
-
}
|
|
566
|
-
for (ssize_t bi = nw << 3; bi < len; bi++)
|
|
567
|
-
count += sb_popcount64((uint64_t)(unsigned char)str[bi]);
|
|
568
|
-
ary = rb_ary_new_capa(count);
|
|
650
|
+
if (rb_block_given_p()) {
|
|
651
|
+
emit_bits(str, len, lsb_first, start_offset, Qnil);
|
|
652
|
+
return self;
|
|
569
653
|
}
|
|
570
654
|
|
|
655
|
+
ssize_t nbits = (start_offset >= len * 8) ? 0 : (len * 8 - start_offset);
|
|
656
|
+
VALUE ary = rb_ary_new_capa(nbits);
|
|
657
|
+
emit_bits(str, len, lsb_first, start_offset, ary);
|
|
658
|
+
return ary;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/* iterate bit positions matching `bit` ------------------------------------ */
|
|
662
|
+
|
|
663
|
+
/* parse the required `bit` argument (true/false/1/0) to 0 or 1 */
|
|
664
|
+
static int
|
|
665
|
+
parse_bit_target(VALUE bit_val)
|
|
666
|
+
{
|
|
667
|
+
if (bit_val == Qtrue || bit_val == INT2FIX(1)) return 1;
|
|
668
|
+
if (bit_val == Qfalse || bit_val == INT2FIX(0)) return 0;
|
|
669
|
+
rb_raise(rb_eArgError, "bit must be 0, 1, false, or true");
|
|
670
|
+
UNREACHABLE_RETURN(0);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/* Unified scanner for each_bit_offset / bit_offsets.
|
|
674
|
+
*
|
|
675
|
+
* Emit each bit position equal to `target` either by yielding to the block
|
|
676
|
+
* (when ary == Qnil) or by pushing to the pre-allocated Array. Both call
|
|
677
|
+
* paths share the same hot loops; the only per-emit cost is one branch on
|
|
678
|
+
* (ary == Qnil), which the compiler can lift out of the inner while loop.
|
|
679
|
+
*
|
|
680
|
+
* LSB-first path: on little-endian, an 8-byte memcpy preserves the flat
|
|
681
|
+
* LSB-first bit numbering (word bit 0 == position 0), so we can scan 64 bits
|
|
682
|
+
* per ctzll. For target=0, invert the loaded word/byte; all 8/64 bits of the
|
|
683
|
+
* inverted unit are valid positions since each byte contributes exactly 8.
|
|
684
|
+
*
|
|
685
|
+
* MSB-first path: walk byte-by-byte with sb_highest_bit8, mapping each
|
|
686
|
+
* physical (LSB-first) bit position into the MSB-first count via
|
|
687
|
+
* logical_to_physical (the operation is its own inverse).
|
|
688
|
+
*/
|
|
689
|
+
static void
|
|
690
|
+
emit_bit_offsets(const unsigned char *str, ssize_t len, int target, int lsb_first,
|
|
691
|
+
ssize_t start_offset, VALUE ary)
|
|
692
|
+
{
|
|
693
|
+
if (start_offset >= len * 8) return;
|
|
694
|
+
|
|
695
|
+
#define SB_EMIT(pos_val) \
|
|
696
|
+
do { VALUE _p = (pos_val); \
|
|
697
|
+
if (ary == Qnil) rb_yield(_p); else rb_ary_push(ary, _p); } while (0)
|
|
698
|
+
|
|
699
|
+
ssize_t byte_start = start_offset >> 3;
|
|
700
|
+
int bit_lo = (int)(start_offset & 7);
|
|
701
|
+
|
|
571
702
|
if (lsb_first) {
|
|
703
|
+
/* Handle the partial first byte before aligning to byte boundary */
|
|
704
|
+
if (bit_lo != 0) {
|
|
705
|
+
unsigned int b = str[byte_start];
|
|
706
|
+
if (target == 0) b = (~b) & 0xFF;
|
|
707
|
+
b >>= bit_lo;
|
|
708
|
+
while (b != 0) {
|
|
709
|
+
int bit = sb_ctz8(b);
|
|
710
|
+
SB_EMIT(SSIZET2NUM(byte_start * 8 + bit_lo + bit));
|
|
711
|
+
b &= b - 1;
|
|
712
|
+
}
|
|
713
|
+
byte_start++;
|
|
714
|
+
}
|
|
572
715
|
#if SB_LITTLE_ENDIAN
|
|
573
|
-
ssize_t n_words = len >> 3;
|
|
716
|
+
ssize_t n_words = (len - byte_start) >> 3;
|
|
574
717
|
for (ssize_t wi = 0; wi < n_words; wi++) {
|
|
575
718
|
uint64_t w;
|
|
576
|
-
memcpy(&w, str + wi * 8, 8);
|
|
719
|
+
memcpy(&w, str + byte_start + wi * 8, 8);
|
|
720
|
+
if (target == 0) w = ~w;
|
|
577
721
|
while (w != 0) {
|
|
578
722
|
int bit = sb_ctzll(w);
|
|
579
|
-
|
|
580
|
-
have_block ? rb_yield(pos) : rb_ary_push(ary, pos);
|
|
723
|
+
SB_EMIT(SSIZET2NUM((byte_start + wi * 8) * 8 + bit));
|
|
581
724
|
w &= w - 1;
|
|
582
725
|
}
|
|
583
726
|
}
|
|
584
|
-
for (ssize_t bi = n_words << 3; bi < len; bi++) {
|
|
727
|
+
for (ssize_t bi = byte_start + (n_words << 3); bi < len; bi++) {
|
|
585
728
|
unsigned int b = str[bi];
|
|
729
|
+
if (target == 0) b = (~b) & 0xFF;
|
|
586
730
|
while (b != 0) {
|
|
587
731
|
int bit = sb_ctz8(b);
|
|
588
|
-
|
|
589
|
-
have_block ? rb_yield(pos) : rb_ary_push(ary, pos);
|
|
732
|
+
SB_EMIT(SSIZET2NUM(bi * 8 + bit));
|
|
590
733
|
b &= b - 1;
|
|
591
734
|
}
|
|
592
735
|
}
|
|
593
736
|
#else
|
|
594
|
-
for (ssize_t bi =
|
|
737
|
+
for (ssize_t bi = byte_start; bi < len; bi++) {
|
|
595
738
|
unsigned int b = str[bi];
|
|
739
|
+
if (target == 0) b = (~b) & 0xFF;
|
|
596
740
|
while (b != 0) {
|
|
597
741
|
int bit = sb_ctz8(b);
|
|
598
|
-
|
|
599
|
-
have_block ? rb_yield(pos) : rb_ary_push(ary, pos);
|
|
742
|
+
SB_EMIT(SSIZET2NUM(bi * 8 + bit));
|
|
600
743
|
b &= b - 1;
|
|
601
744
|
}
|
|
602
745
|
}
|
|
603
746
|
#endif
|
|
604
747
|
}
|
|
605
748
|
else {
|
|
606
|
-
|
|
749
|
+
/* lsb_first: false => byte order preserved, bits 7..0 map to logical 0..7.
|
|
750
|
+
* In the first (possibly partial) byte, skip the top bit_lo bits. */
|
|
751
|
+
for (ssize_t bi = byte_start; bi < len; bi++) {
|
|
607
752
|
unsigned int b = str[bi];
|
|
753
|
+
if (target == 0) b = (~b) & 0xFF;
|
|
754
|
+
if (bi == byte_start && bit_lo != 0)
|
|
755
|
+
b &= (1u << (8 - bit_lo)) - 1; /* clear top bit_lo bits */
|
|
608
756
|
while (b != 0) {
|
|
609
757
|
int bit = sb_highest_bit8(b);
|
|
610
758
|
ssize_t physical = bi * 8 + bit;
|
|
611
|
-
|
|
612
|
-
have_block ? rb_yield(pos) : rb_ary_push(ary, pos);
|
|
759
|
+
SB_EMIT(SSIZET2NUM(logical_to_physical(physical, 0)));
|
|
613
760
|
b ^= (1u << bit);
|
|
614
761
|
}
|
|
615
762
|
}
|
|
616
763
|
}
|
|
617
764
|
|
|
618
|
-
|
|
765
|
+
#undef SB_EMIT
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
static VALUE
|
|
769
|
+
rb_str_each_bit_offset(int argc, VALUE *argv, VALUE self)
|
|
770
|
+
{
|
|
771
|
+
RETURN_ENUMERATOR(self, argc, argv);
|
|
772
|
+
|
|
773
|
+
VALUE bit_val, start_offset_v = Qnil, opts = Qnil;
|
|
774
|
+
rb_scan_args(argc, argv, "11:", &bit_val, &start_offset_v, &opts);
|
|
775
|
+
validate_option_hash(opts, SB_KW_LSB_FIRST);
|
|
776
|
+
int lsb_first = parse_lsb_first_opt(opts);
|
|
777
|
+
int target = parse_bit_target(bit_val);
|
|
778
|
+
ssize_t start_offset = parse_start_offset(start_offset_v);
|
|
779
|
+
|
|
780
|
+
emit_bit_offsets((const unsigned char *)RSTRING_PTR(self), RSTRING_LEN(self),
|
|
781
|
+
target, lsb_first, start_offset, Qnil);
|
|
782
|
+
return self;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
static VALUE
|
|
786
|
+
rb_str_bit_offsets(int argc, VALUE *argv, VALUE self)
|
|
787
|
+
{
|
|
788
|
+
VALUE bit_val, start_offset_v = Qnil, opts = Qnil;
|
|
789
|
+
rb_scan_args(argc, argv, "11:", &bit_val, &start_offset_v, &opts);
|
|
790
|
+
validate_option_hash(opts, SB_KW_LSB_FIRST);
|
|
791
|
+
int lsb_first = parse_lsb_first_opt(opts);
|
|
792
|
+
int target = parse_bit_target(bit_val);
|
|
793
|
+
ssize_t start_offset = parse_start_offset(start_offset_v);
|
|
794
|
+
|
|
795
|
+
ssize_t len = RSTRING_LEN(self);
|
|
796
|
+
const unsigned char *str = (const unsigned char *)RSTRING_PTR(self);
|
|
797
|
+
|
|
798
|
+
if (rb_block_given_p()) {
|
|
799
|
+
emit_bit_offsets(str, len, target, lsb_first, start_offset, Qnil);
|
|
800
|
+
return self;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/* Pre-size the Array using popcount to avoid repeated reallocation.
|
|
804
|
+
* For target=0 the expected count is (len * 8 - popcount). */
|
|
805
|
+
ssize_t set_count = count_set_bits(str, len);
|
|
806
|
+
ssize_t count = (target == 1) ? set_count : (len * 8 - set_count);
|
|
807
|
+
VALUE ary = rb_ary_new_capa(count);
|
|
808
|
+
emit_bit_offsets(str, len, target, lsb_first, start_offset, ary);
|
|
809
|
+
return ary;
|
|
619
810
|
}
|
|
620
811
|
|
|
621
812
|
/* multi-bit mutation ------------------------------------------------------ */
|
|
@@ -712,40 +903,36 @@ bit_copy_core(unsigned char *dst, ssize_t dst_bit_off,
|
|
|
712
903
|
if (tmp != stack_tmp) ruby_xfree(tmp);
|
|
713
904
|
}
|
|
714
905
|
|
|
715
|
-
/*
|
|
716
|
-
* String#bit_slice(range) -> String
|
|
717
|
-
*
|
|
718
|
-
* str = "\xFF\x00" # 11111111 00000000
|
|
719
|
-
* str.bit_slice(4, 8) # => "\xF0" (11110000)
|
|
720
|
-
*/
|
|
906
|
+
/* Extract a sub-sequence of bits into a new String. */
|
|
721
907
|
static VALUE
|
|
722
908
|
rb_str_bit_slice(int argc, VALUE *argv, VALUE self)
|
|
723
909
|
{
|
|
724
910
|
ssize_t src_len = RSTRING_LEN(self);
|
|
725
911
|
ssize_t total_bits = src_len * 8;
|
|
726
|
-
ssize_t
|
|
912
|
+
ssize_t bit_offset, bit_length;
|
|
727
913
|
VALUE v0, v1, opts;
|
|
728
914
|
int n_pos = rb_scan_args(argc, argv, "11:", &v0, &v1, &opts);
|
|
729
915
|
validate_option_hash(opts, SB_KW_LSB_FIRST);
|
|
730
916
|
int lsb_first = parse_lsb_first_opt(opts);
|
|
731
917
|
|
|
732
918
|
if (n_pos == 1 && rb_obj_is_kind_of(v0, rb_cRange)) {
|
|
919
|
+
sb_range_validate_endpoints(v0);
|
|
733
920
|
ssize_t beg, len;
|
|
734
921
|
if (!RTEST(sb_range_beg_len(v0, &beg, &len, total_bits, 0))) {
|
|
735
922
|
return Qnil;
|
|
736
923
|
}
|
|
737
|
-
|
|
738
|
-
|
|
924
|
+
bit_offset = beg;
|
|
925
|
+
bit_length = len;
|
|
739
926
|
}
|
|
740
927
|
else if (n_pos == 2) {
|
|
741
928
|
if (!rb_integer_type_p(v0) || !rb_integer_type_p(v1)) {
|
|
742
929
|
return Qnil;
|
|
743
930
|
}
|
|
744
931
|
|
|
745
|
-
|
|
746
|
-
|
|
932
|
+
bit_offset = integer_to_bit_idx(v0);
|
|
933
|
+
bit_length = integer_to_bit_idx(v1);
|
|
747
934
|
|
|
748
|
-
if (
|
|
935
|
+
if (bit_offset < 0 || bit_length < 0) return Qnil;
|
|
749
936
|
}
|
|
750
937
|
else if (n_pos == 1) {
|
|
751
938
|
return Qnil;
|
|
@@ -755,13 +942,13 @@ rb_str_bit_slice(int argc, VALUE *argv, VALUE self)
|
|
|
755
942
|
"wrong number of arguments (given %d, expected 1 or 2)", n_pos);
|
|
756
943
|
}
|
|
757
944
|
|
|
758
|
-
if (
|
|
759
|
-
ssize_t available = total_bits -
|
|
760
|
-
if (
|
|
945
|
+
if (bit_offset > total_bits) return Qnil;
|
|
946
|
+
ssize_t available = total_bits - bit_offset;
|
|
947
|
+
if (bit_length > available) bit_length = available;
|
|
761
948
|
|
|
762
|
-
if (
|
|
949
|
+
if (bit_length == 0) return rb_str_new("", 0);
|
|
763
950
|
|
|
764
|
-
ssize_t out_bytes = (
|
|
951
|
+
ssize_t out_bytes = (bit_length + 7) / 8;
|
|
765
952
|
VALUE result = rb_str_buf_new(out_bytes);
|
|
766
953
|
rb_str_resize(result, out_bytes);
|
|
767
954
|
rb_enc_associate(result, rb_enc_get(self));
|
|
@@ -771,17 +958,17 @@ rb_str_bit_slice(int argc, VALUE *argv, VALUE self)
|
|
|
771
958
|
memset(dst, 0, out_bytes);
|
|
772
959
|
|
|
773
960
|
if (lsb_first) {
|
|
774
|
-
bit_copy_core(dst, 0, src, src_len,
|
|
961
|
+
bit_copy_core(dst, 0, src, src_len, bit_offset, bit_length);
|
|
775
962
|
} else {
|
|
776
963
|
ssize_t dst_bit = 0;
|
|
777
|
-
ssize_t start_byte =
|
|
778
|
-
ssize_t end_byte = (
|
|
964
|
+
ssize_t start_byte = bit_offset >> 3;
|
|
965
|
+
ssize_t end_byte = (bit_offset + bit_length - 1) >> 3;
|
|
779
966
|
|
|
780
967
|
for (ssize_t b = start_byte; b <= end_byte; b++) {
|
|
781
968
|
ssize_t b_start_l = b << 3;
|
|
782
969
|
ssize_t b_end_l = b_start_l + 7;
|
|
783
|
-
ssize_t l_min = (
|
|
784
|
-
ssize_t l_max = ((
|
|
970
|
+
ssize_t l_min = (bit_offset > b_start_l) ? bit_offset : b_start_l;
|
|
971
|
+
ssize_t l_max = ((bit_offset + bit_length - 1) < b_end_l) ? (bit_offset + bit_length - 1) : b_end_l;
|
|
785
972
|
|
|
786
973
|
ssize_t p_min = b_start_l + (7 - (l_max & 7L));
|
|
787
974
|
ssize_t p_max = b_start_l + (7 - (l_min & 7L));
|
|
@@ -805,8 +992,8 @@ enum sb_mutation_op {
|
|
|
805
992
|
static VALUE
|
|
806
993
|
rb_str_mutate_bits(int argc, VALUE *argv, VALUE self, enum sb_mutation_op op)
|
|
807
994
|
{
|
|
808
|
-
VALUE target, opts;
|
|
809
|
-
rb_scan_args(argc, argv, "
|
|
995
|
+
VALUE target, bit_length_v = Qnil, opts = Qnil;
|
|
996
|
+
rb_scan_args(argc, argv, "11:", &target, &bit_length_v, &opts);
|
|
810
997
|
validate_option_hash(opts, SB_KW_LSB_FIRST);
|
|
811
998
|
int lsb_first = parse_lsb_first_opt(opts);
|
|
812
999
|
|
|
@@ -814,17 +1001,47 @@ rb_str_mutate_bits(int argc, VALUE *argv, VALUE self, enum sb_mutation_op op)
|
|
|
814
1001
|
unsigned char *ptr = (unsigned char *)RSTRING_PTR(self);
|
|
815
1002
|
|
|
816
1003
|
if (rb_integer_type_p(target)) {
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
1004
|
+
if (NIL_P(bit_length_v)) {
|
|
1005
|
+
/* Single-bit form: bit_set(n) */
|
|
1006
|
+
ssize_t idx = check_bit_index(self, target, lsb_first);
|
|
1007
|
+
unsigned char mask = (unsigned char)(1u << (idx % 8));
|
|
1008
|
+
switch (op) {
|
|
1009
|
+
case SB_MUT_SET: ptr[idx / 8] |= mask; break;
|
|
1010
|
+
case SB_MUT_CLEAR: ptr[idx / 8] &= (unsigned char)~mask; break;
|
|
1011
|
+
case SB_MUT_FLIP: ptr[idx / 8] ^= mask; break;
|
|
1012
|
+
}
|
|
1013
|
+
return self;
|
|
1014
|
+
}
|
|
1015
|
+
/* 2-arg form: bit_set(bit_offset, bit_length) */
|
|
1016
|
+
if (!rb_integer_type_p(bit_length_v))
|
|
1017
|
+
rb_raise(rb_eTypeError, "bit_length must be an integer");
|
|
1018
|
+
ssize_t bit_offset = integer_to_bit_idx(target);
|
|
1019
|
+
if (bit_offset < 0)
|
|
1020
|
+
rb_raise(rb_eIndexError, "bit_offset must be non-negative");
|
|
1021
|
+
ssize_t bit_length = integer_to_bit_idx(bit_length_v);
|
|
1022
|
+
if (bit_length < 0)
|
|
1023
|
+
rb_raise(rb_eArgError, "bit_length must be non-negative");
|
|
1024
|
+
if (bit_length == 0) return self;
|
|
1025
|
+
ssize_t total_bits = RSTRING_LEN(self) * 8;
|
|
1026
|
+
if (bit_offset >= total_bits || bit_offset + bit_length > total_bits)
|
|
1027
|
+
rb_raise(rb_eIndexError, "bit range out of range");
|
|
1028
|
+
for (ssize_t logical = bit_offset; logical < bit_offset + bit_length; logical++) {
|
|
1029
|
+
ssize_t idx = logical_to_physical(logical, lsb_first);
|
|
1030
|
+
unsigned char mask = (unsigned char)(1u << (idx % 8));
|
|
1031
|
+
switch (op) {
|
|
1032
|
+
case SB_MUT_SET: ptr[idx / 8] |= mask; break;
|
|
1033
|
+
case SB_MUT_CLEAR: ptr[idx / 8] &= (unsigned char)~mask; break;
|
|
1034
|
+
case SB_MUT_FLIP: ptr[idx / 8] ^= mask; break;
|
|
1035
|
+
}
|
|
823
1036
|
}
|
|
824
1037
|
return self;
|
|
825
1038
|
}
|
|
826
1039
|
|
|
1040
|
+
if (!NIL_P(bit_length_v))
|
|
1041
|
+
rb_raise(rb_eArgError, "wrong number of arguments");
|
|
1042
|
+
|
|
827
1043
|
if (rb_obj_is_kind_of(target, rb_cRange)) {
|
|
1044
|
+
sb_range_validate_endpoints(target);
|
|
828
1045
|
ssize_t total_bits = RSTRING_LEN(self) * 8;
|
|
829
1046
|
ssize_t beg, len;
|
|
830
1047
|
|
|
@@ -836,19 +1053,20 @@ rb_str_mutate_bits(int argc, VALUE *argv, VALUE self, enum sb_mutation_op op)
|
|
|
836
1053
|
|
|
837
1054
|
/* err=0 silently clamps end > total. Detect that and raise instead,
|
|
838
1055
|
* to stay consistent with bit_splice and single-bit mutation. */
|
|
839
|
-
VALUE
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
ssize_t
|
|
1056
|
+
VALUE rng_beg_unused, rng_end_v;
|
|
1057
|
+
int excl;
|
|
1058
|
+
rb_range_values(target, &rng_beg_unused, &rng_end_v, &excl);
|
|
1059
|
+
(void)rng_beg_unused;
|
|
1060
|
+
if (!NIL_P(rng_end_v)) {
|
|
1061
|
+
ssize_t end_val = integer_to_bit_idx(rng_end_v);
|
|
1062
|
+
ssize_t end_excl = excl ? end_val : end_val + 1;
|
|
845
1063
|
if (end_excl > total_bits) {
|
|
846
1064
|
rb_raise(rb_eIndexError, "bit range out of range");
|
|
847
1065
|
}
|
|
848
1066
|
}
|
|
849
1067
|
|
|
850
1068
|
for (ssize_t logical = beg; logical < beg + len; logical++) {
|
|
851
|
-
ssize_t idx =
|
|
1069
|
+
ssize_t idx = logical_to_physical(logical, lsb_first);
|
|
852
1070
|
unsigned char mask = (unsigned char)(1u << (idx % 8));
|
|
853
1071
|
switch (op) {
|
|
854
1072
|
case SB_MUT_SET: ptr[idx / 8] |= mask; break;
|
|
@@ -864,19 +1082,19 @@ rb_str_mutate_bits(int argc, VALUE *argv, VALUE self, enum sb_mutation_op op)
|
|
|
864
1082
|
}
|
|
865
1083
|
|
|
866
1084
|
static VALUE
|
|
867
|
-
|
|
1085
|
+
rb_str_bit_set(int argc, VALUE *argv, VALUE self)
|
|
868
1086
|
{
|
|
869
1087
|
return rb_str_mutate_bits(argc, argv, self, SB_MUT_SET);
|
|
870
1088
|
}
|
|
871
1089
|
|
|
872
1090
|
static VALUE
|
|
873
|
-
|
|
1091
|
+
rb_str_bit_clear(int argc, VALUE *argv, VALUE self)
|
|
874
1092
|
{
|
|
875
1093
|
return rb_str_mutate_bits(argc, argv, self, SB_MUT_CLEAR);
|
|
876
1094
|
}
|
|
877
1095
|
|
|
878
1096
|
static VALUE
|
|
879
|
-
|
|
1097
|
+
rb_str_bit_flip(int argc, VALUE *argv, VALUE self)
|
|
880
1098
|
{
|
|
881
1099
|
return rb_str_mutate_bits(argc, argv, self, SB_MUT_FLIP);
|
|
882
1100
|
}
|
|
@@ -902,101 +1120,141 @@ alloc_result(VALUE self)
|
|
|
902
1120
|
return result;
|
|
903
1121
|
}
|
|
904
1122
|
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
{
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1123
|
+
/*
|
|
1124
|
+
* Bitwise op kernels: process 32 bytes (4 x uint64_t) per loop iteration via
|
|
1125
|
+
* memcpy + word-wise op + memcpy, then any 8-byte tail, then byte-by-byte for
|
|
1126
|
+
* the final < 8 bytes. memcpy avoids unaligned-load/store issues on strict-
|
|
1127
|
+
* alignment platforms; modern compilers fold each 8-byte memcpy into a single
|
|
1128
|
+
* load/store. Macro-generated to avoid 8 near-identical functions.
|
|
1129
|
+
*
|
|
1130
|
+
* NOT operands take only `src`; binary AND/OR/XOR take `a` and `b`.
|
|
1131
|
+
*/
|
|
1132
|
+
#define SB_DEFINE_UNARY_KERNEL(name, expr_word, expr_byte) \
|
|
1133
|
+
static void \
|
|
1134
|
+
name(unsigned char *dst, const unsigned char *src, ssize_t len) \
|
|
1135
|
+
{ \
|
|
1136
|
+
ssize_t off = 0; \
|
|
1137
|
+
ssize_t unrolled_end = len & ~31L; \
|
|
1138
|
+
ssize_t aligned_end = len & ~7L; \
|
|
1139
|
+
for (; off < unrolled_end; off += 32) { \
|
|
1140
|
+
uint64_t s0, s1, s2, s3; \
|
|
1141
|
+
memcpy(&s0, src + off, 8); \
|
|
1142
|
+
memcpy(&s1, src + off + 8, 8); \
|
|
1143
|
+
memcpy(&s2, src + off + 16, 8); \
|
|
1144
|
+
memcpy(&s3, src + off + 24, 8); \
|
|
1145
|
+
uint64_t d0 = (expr_word(s0)); \
|
|
1146
|
+
uint64_t d1 = (expr_word(s1)); \
|
|
1147
|
+
uint64_t d2 = (expr_word(s2)); \
|
|
1148
|
+
uint64_t d3 = (expr_word(s3)); \
|
|
1149
|
+
memcpy(dst + off, &d0, 8); \
|
|
1150
|
+
memcpy(dst + off + 8, &d1, 8); \
|
|
1151
|
+
memcpy(dst + off + 16, &d2, 8); \
|
|
1152
|
+
memcpy(dst + off + 24, &d3, 8); \
|
|
1153
|
+
} \
|
|
1154
|
+
for (; off < aligned_end; off += 8) { \
|
|
1155
|
+
uint64_t s; \
|
|
1156
|
+
memcpy(&s, src + off, 8); \
|
|
1157
|
+
uint64_t d = (expr_word(s)); \
|
|
1158
|
+
memcpy(dst + off, &d, 8); \
|
|
1159
|
+
} \
|
|
1160
|
+
for (; off < len; off++) dst[off] = (expr_byte(src[off])); \
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
#define SB_DEFINE_BINARY_KERNEL(name, expr_word, expr_byte) \
|
|
1164
|
+
static void \
|
|
1165
|
+
name(unsigned char *dst, const unsigned char *a, const unsigned char *b, \
|
|
1166
|
+
ssize_t len) \
|
|
1167
|
+
{ \
|
|
1168
|
+
ssize_t off = 0; \
|
|
1169
|
+
ssize_t unrolled_end = len & ~31L; \
|
|
1170
|
+
ssize_t aligned_end = len & ~7L; \
|
|
1171
|
+
for (; off < unrolled_end; off += 32) { \
|
|
1172
|
+
uint64_t a0, a1, a2, a3, b0, b1, b2, b3; \
|
|
1173
|
+
memcpy(&a0, a + off, 8); memcpy(&b0, b + off, 8); \
|
|
1174
|
+
memcpy(&a1, a + off + 8, 8); memcpy(&b1, b + off + 8, 8); \
|
|
1175
|
+
memcpy(&a2, a + off + 16, 8); memcpy(&b2, b + off + 16, 8); \
|
|
1176
|
+
memcpy(&a3, a + off + 24, 8); memcpy(&b3, b + off + 24, 8); \
|
|
1177
|
+
uint64_t d0 = expr_word(a0, b0); \
|
|
1178
|
+
uint64_t d1 = expr_word(a1, b1); \
|
|
1179
|
+
uint64_t d2 = expr_word(a2, b2); \
|
|
1180
|
+
uint64_t d3 = expr_word(a3, b3); \
|
|
1181
|
+
memcpy(dst + off, &d0, 8); \
|
|
1182
|
+
memcpy(dst + off + 8, &d1, 8); \
|
|
1183
|
+
memcpy(dst + off + 16, &d2, 8); \
|
|
1184
|
+
memcpy(dst + off + 24, &d3, 8); \
|
|
1185
|
+
} \
|
|
1186
|
+
for (; off < aligned_end; off += 8) { \
|
|
1187
|
+
uint64_t av, bv; \
|
|
1188
|
+
memcpy(&av, a + off, 8); memcpy(&bv, b + off, 8); \
|
|
1189
|
+
uint64_t d = expr_word(av, bv); \
|
|
1190
|
+
memcpy(dst + off, &d, 8); \
|
|
1191
|
+
} \
|
|
1192
|
+
for (; off < len; off++) dst[off] = expr_byte(a[off], b[off]); \
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
#define SB_NOT_WORD(x) (~(x))
|
|
1196
|
+
#define SB_NOT_BYTE(x) ((unsigned char)~(x))
|
|
1197
|
+
#define SB_AND_WORD(x, y) ((x) & (y))
|
|
1198
|
+
#define SB_AND_BYTE(x, y) ((unsigned char)((x) & (y)))
|
|
1199
|
+
#define SB_OR_WORD(x, y) ((x) | (y))
|
|
1200
|
+
#define SB_OR_BYTE(x, y) ((unsigned char)((x) | (y)))
|
|
1201
|
+
#define SB_XOR_WORD(x, y) ((x) ^ (y))
|
|
1202
|
+
#define SB_XOR_BYTE(x, y) ((unsigned char)((x) ^ (y)))
|
|
1203
|
+
|
|
1204
|
+
SB_DEFINE_UNARY_KERNEL (kern_not, SB_NOT_WORD, SB_NOT_BYTE)
|
|
1205
|
+
SB_DEFINE_BINARY_KERNEL(kern_and, SB_AND_WORD, SB_AND_BYTE)
|
|
1206
|
+
SB_DEFINE_BINARY_KERNEL(kern_or, SB_OR_WORD, SB_OR_BYTE)
|
|
1207
|
+
SB_DEFINE_BINARY_KERNEL(kern_xor, SB_XOR_WORD, SB_XOR_BYTE)
|
|
1208
|
+
|
|
1209
|
+
/* Method wrappers: allocate-and-return form, and the in-place (!) form. */
|
|
1210
|
+
#define SB_DEFINE_UNARY_METHODS(op_name, kernel) \
|
|
1211
|
+
static VALUE \
|
|
1212
|
+
rb_str_bitwise_##op_name(VALUE self) \
|
|
1213
|
+
{ \
|
|
1214
|
+
ssize_t len = RSTRING_LEN(self); \
|
|
1215
|
+
VALUE result = alloc_result(self); \
|
|
1216
|
+
kernel((unsigned char *)RSTRING_PTR(result), \
|
|
1217
|
+
(const unsigned char *)RSTRING_PTR(self), len); \
|
|
1218
|
+
return result; \
|
|
1219
|
+
} \
|
|
1220
|
+
static VALUE \
|
|
1221
|
+
rb_str_bitwise_##op_name##_bang(VALUE self) \
|
|
1222
|
+
{ \
|
|
1223
|
+
rb_str_modify(self); \
|
|
1224
|
+
ssize_t len = RSTRING_LEN(self); \
|
|
1225
|
+
unsigned char *ptr = (unsigned char *)RSTRING_PTR(self); \
|
|
1226
|
+
kernel(ptr, ptr, len); \
|
|
1227
|
+
return self; \
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
#define SB_DEFINE_BINARY_METHODS(op_name, kernel) \
|
|
1231
|
+
static VALUE \
|
|
1232
|
+
rb_str_bitwise_##op_name(VALUE self, VALUE other) \
|
|
1233
|
+
{ \
|
|
1234
|
+
check_binary_op_lengths(self, other); \
|
|
1235
|
+
ssize_t len = RSTRING_LEN(self); \
|
|
1236
|
+
VALUE result = alloc_result(self); \
|
|
1237
|
+
kernel((unsigned char *)RSTRING_PTR(result), \
|
|
1238
|
+
(const unsigned char *)RSTRING_PTR(self), \
|
|
1239
|
+
(const unsigned char *)RSTRING_PTR(other), len); \
|
|
1240
|
+
return result; \
|
|
1241
|
+
} \
|
|
1242
|
+
static VALUE \
|
|
1243
|
+
rb_str_bitwise_##op_name##_bang(VALUE self, VALUE other) \
|
|
1244
|
+
{ \
|
|
1245
|
+
check_binary_op_lengths(self, other); \
|
|
1246
|
+
rb_str_modify(self); \
|
|
1247
|
+
ssize_t len = RSTRING_LEN(self); \
|
|
1248
|
+
unsigned char *a = (unsigned char *)RSTRING_PTR(self); \
|
|
1249
|
+
const unsigned char *b = (const unsigned char *)RSTRING_PTR(other); \
|
|
1250
|
+
kernel(a, a, b, len); \
|
|
1251
|
+
return self; \
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
SB_DEFINE_UNARY_METHODS (not, kern_not)
|
|
1255
|
+
SB_DEFINE_BINARY_METHODS(and, kern_and)
|
|
1256
|
+
SB_DEFINE_BINARY_METHODS(or, kern_or)
|
|
1257
|
+
SB_DEFINE_BINARY_METHODS(xor, kern_xor)
|
|
1000
1258
|
|
|
1001
1259
|
/* packed bit-field iteration ---------------------------------------------- */
|
|
1002
1260
|
/*
|
|
@@ -1051,25 +1309,7 @@ extract_uint64(const unsigned char *src, ssize_t src_len,
|
|
|
1051
1309
|
return val;
|
|
1052
1310
|
}
|
|
1053
1311
|
|
|
1054
|
-
/*
|
|
1055
|
-
* String#each_bit_field(*bitlens, lsb_first: true) -> Enumerator
|
|
1056
|
-
*
|
|
1057
|
-
* Iterates over the string as a sequence of packed bit-field records. Each
|
|
1058
|
-
* positional argument specifies the width (in bits) of one field in the record.
|
|
1059
|
-
* On each iteration, one Integer per field is yielded (LSB-first bit layout).
|
|
1060
|
-
* Each bitlen must be in the range 1..64.
|
|
1061
|
-
*
|
|
1062
|
-
* lsb_first: true (default) -- intra-byte field extraction uses bit 0..7.
|
|
1063
|
-
* lsb_first: false -- intra-byte field extraction uses bit 7..0.
|
|
1064
|
-
*
|
|
1065
|
-
* Incomplete trailing bits (when bytesize*8 is not a multiple of sum(bitlens))
|
|
1066
|
-
* are silently dropped, matching the behavior of Enumerable#each_slice.
|
|
1067
|
-
*
|
|
1068
|
-
* Porting to Ruby Core:
|
|
1069
|
-
* 1. Move extract_uint64 and this function into string.c.
|
|
1070
|
-
* 2. Register with rb_define_method in Init_String().
|
|
1071
|
-
* 3. Replace ALLOCA_N with stack arrays for small field counts and heap otherwise.
|
|
1072
|
-
*/
|
|
1312
|
+
/* Yield each packed bit-field record as one Integer per field. */
|
|
1073
1313
|
static VALUE
|
|
1074
1314
|
rb_str_each_bit_field(int argc, VALUE *argv, VALUE self)
|
|
1075
1315
|
{
|
|
@@ -1125,22 +1365,7 @@ rb_str_each_bit_field(int argc, VALUE *argv, VALUE self)
|
|
|
1125
1365
|
return self;
|
|
1126
1366
|
}
|
|
1127
1367
|
|
|
1128
|
-
/*
|
|
1129
|
-
* String#bit_fields(*bitlens, lsb_first: true) { |*fields| } -> self
|
|
1130
|
-
*
|
|
1131
|
-
* Non-iterator complement of each_bit_field. Without a block, returns an
|
|
1132
|
-
* Array of all extracted records. With a single bitlen the array is flat
|
|
1133
|
-
* (matching each_bit_field(n).to_a); with multiple bitlens each record is
|
|
1134
|
-
* itself an Array (matching each_bit_field(a, b, ...).to_a).
|
|
1135
|
-
*
|
|
1136
|
-
* With a block, behaves identically to each_bit_field without with: ---
|
|
1137
|
-
* yielding one Integer per field and returning self.
|
|
1138
|
-
*
|
|
1139
|
-
* Porting to Ruby Core:
|
|
1140
|
-
* 1. Move alongside each_bit_field in string.c.
|
|
1141
|
-
* 2. Share extract_uint64 and the bitlen validation logic.
|
|
1142
|
-
* 3. Register with rb_define_method in Init_String().
|
|
1143
|
-
*/
|
|
1368
|
+
/* Non-iterator form of each_bit_field; collect bit-field records into an Array. */
|
|
1144
1369
|
static VALUE
|
|
1145
1370
|
rb_str_bit_fields(int argc, VALUE *argv, VALUE self)
|
|
1146
1371
|
{
|
|
@@ -1207,7 +1432,7 @@ rb_str_bit_fields(int argc, VALUE *argv, VALUE self)
|
|
|
1207
1432
|
|
|
1208
1433
|
/*
|
|
1209
1434
|
* count_run_lsb: count consecutive bits equal to `target` starting at flat
|
|
1210
|
-
* position `
|
|
1435
|
+
* position `bit_offset` (LSB-first). Uses ctz / ctzll to skip bits in bulk:
|
|
1211
1436
|
* - partial first byte: ctz on the inverted masked nibble
|
|
1212
1437
|
* - full 64-bit words (LE): ctzll on the inverted word (64 bits per step)
|
|
1213
1438
|
* - remaining bytes: ctz on the inverted byte
|
|
@@ -1217,11 +1442,11 @@ rb_str_bit_fields(int argc, VALUE *argv, VALUE self)
|
|
|
1217
1442
|
* 2. Share sb_ctz8 / sb_ctzll with the existing set-bit helpers.
|
|
1218
1443
|
*/
|
|
1219
1444
|
static ssize_t
|
|
1220
|
-
count_run_lsb(const unsigned char *src, ssize_t src_len, ssize_t
|
|
1445
|
+
count_run_lsb(const unsigned char *src, ssize_t src_len, ssize_t bit_offset, int target)
|
|
1221
1446
|
{
|
|
1222
|
-
ssize_t max_run = src_len * 8 -
|
|
1223
|
-
ssize_t byte_idx =
|
|
1224
|
-
int bit_off =
|
|
1447
|
+
ssize_t max_run = src_len * 8 - bit_offset;
|
|
1448
|
+
ssize_t byte_idx = bit_offset >> 3;
|
|
1449
|
+
int bit_off = bit_offset & 7;
|
|
1225
1450
|
ssize_t count = 0;
|
|
1226
1451
|
|
|
1227
1452
|
/* partial first byte: shift pos to bit 0, mask remaining bits */
|
|
@@ -1272,196 +1497,123 @@ count_run_lsb(const unsigned char *src, ssize_t src_len, ssize_t pos, int target
|
|
|
1272
1497
|
return count < max_run ? count : max_run;
|
|
1273
1498
|
}
|
|
1274
1499
|
|
|
1275
|
-
/*
|
|
1276
|
-
*
|
|
1277
|
-
* Returns the length of the consecutive run of `bit` starting at flat
|
|
1278
|
-
* position `pos`. Returns nil when `pos` is out of range or the bit at `pos`
|
|
1279
|
-
* does not equal `bit`.
|
|
1280
|
-
*
|
|
1281
|
-
* `bit` accepts 0, 1, false, or true (false/true are aliases for 0/1,
|
|
1282
|
-
* matching the values yielded by each_bit_run).
|
|
1283
|
-
*
|
|
1284
|
-
* Counts forward from `pos` toward higher bit indices.
|
|
1285
|
-
*
|
|
1286
|
-
* Inspired by Gauche Scheme's (bitvector-count-run bit bvec i).
|
|
1287
|
-
*
|
|
1288
|
-
* Uses the same flat LSB-first addressing as bit_at: byte[pos/8] bit pos%8.
|
|
1289
|
-
*
|
|
1290
|
-
* Porting to Ruby Core:
|
|
1291
|
-
* 1. Move to string.c; register in Init_String().
|
|
1292
|
-
* 2. Reuse integer_to_bit_idx for consistent Bignum handling.
|
|
1293
|
-
*/
|
|
1500
|
+
/* Return the length of the consecutive run of `bit` starting at pos, or nil. */
|
|
1294
1501
|
static VALUE
|
|
1295
1502
|
rb_str_bit_run_count(int argc, VALUE *argv, VALUE self)
|
|
1296
1503
|
{
|
|
1297
|
-
VALUE
|
|
1298
|
-
rb_scan_args(argc, argv, "20:", &
|
|
1504
|
+
VALUE bit_offset_v, bit_val, opts;
|
|
1505
|
+
rb_scan_args(argc, argv, "20:", &bit_val, &bit_offset_v, &opts);
|
|
1299
1506
|
validate_option_hash(opts, SB_KW_LSB_FIRST);
|
|
1300
1507
|
int lsb_first = parse_lsb_first_opt(opts);
|
|
1301
1508
|
|
|
1302
|
-
if (!rb_integer_type_p(
|
|
1509
|
+
if (!rb_integer_type_p(bit_offset_v)) {
|
|
1303
1510
|
rb_raise(rb_eTypeError, "position must be an integer");
|
|
1304
1511
|
}
|
|
1305
|
-
int target;
|
|
1306
|
-
|
|
1307
|
-
target = 1;
|
|
1308
|
-
} else if (bit_val == Qfalse || bit_val == INT2FIX(0)) {
|
|
1309
|
-
target = 0;
|
|
1310
|
-
} else {
|
|
1311
|
-
rb_raise(rb_eArgError, "bit must be 0, 1, false, or true");
|
|
1312
|
-
}
|
|
1313
|
-
ssize_t pos = integer_to_bit_idx(pos_val);
|
|
1512
|
+
int target = parse_bit_target(bit_val);
|
|
1513
|
+
ssize_t bit_offset = integer_to_bit_idx(bit_offset_v);
|
|
1314
1514
|
ssize_t src_len = RSTRING_LEN(self);
|
|
1315
|
-
if (
|
|
1515
|
+
if (bit_offset < 0 || bit_offset >= src_len * 8) return Qnil;
|
|
1316
1516
|
|
|
1317
1517
|
const unsigned char *src = (const unsigned char *)RSTRING_PTR(self);
|
|
1318
1518
|
if (lsb_first) {
|
|
1319
|
-
if (((src[
|
|
1320
|
-
return SSIZET2NUM(count_run_lsb(src, src_len,
|
|
1519
|
+
if (((src[bit_offset >> 3] >> (bit_offset & 7)) & 1) != target) return Qnil;
|
|
1520
|
+
return SSIZET2NUM(count_run_lsb(src, src_len, bit_offset, target));
|
|
1321
1521
|
}
|
|
1322
1522
|
|
|
1323
|
-
if (logical_get_bit(src,
|
|
1523
|
+
if (logical_get_bit(src, bit_offset, 0) != target) return Qnil;
|
|
1324
1524
|
|
|
1325
1525
|
ssize_t run = 1;
|
|
1326
1526
|
ssize_t total_bits = src_len * 8;
|
|
1327
|
-
while (
|
|
1527
|
+
while (bit_offset + run < total_bits && logical_get_bit(src, bit_offset + run, 0) == target) {
|
|
1328
1528
|
run++;
|
|
1329
1529
|
}
|
|
1330
1530
|
return SSIZET2NUM(run);
|
|
1331
1531
|
}
|
|
1332
1532
|
|
|
1333
|
-
/*
|
|
1334
|
-
|
|
1533
|
+
/* Yield (bit, offset, run_length) triples for each consecutive run of identical bits. */
|
|
1534
|
+
/* Unified emitter for each_bit_run / bit_runs.
|
|
1335
1535
|
*
|
|
1336
|
-
*
|
|
1337
|
-
*
|
|
1338
|
-
*
|
|
1536
|
+
* Walks the bitmap in (bit, run_length) chunks. Yields each pair (when
|
|
1537
|
+
* ary == Qnil) or pushes (bit, run_length) Arrays to the pre-allocated
|
|
1538
|
+
* result. The LSB-first path uses the fast count_run_lsb (word-at-a-time
|
|
1539
|
+
* via ctzll); the MSB-first path scans bit by bit through logical_get_bit.
|
|
1339
1540
|
*
|
|
1340
|
-
*
|
|
1341
|
-
*
|
|
1342
|
-
* ratio is proportional to the average run length.
|
|
1343
|
-
*
|
|
1344
|
-
* lsb_first: true (default) iterates bit 0..7 within each byte.
|
|
1345
|
-
* lsb_first: false iterates bit 7..0 within each byte.
|
|
1346
|
-
*
|
|
1347
|
-
* Porting to Ruby Core:
|
|
1348
|
-
* 1. Move to string.c; register in Init_String().
|
|
1349
|
-
* 2. count_run_lsb / count_run_msb move with it.
|
|
1541
|
+
* self is re-read inside the loop because rb_yield can invoke Ruby code
|
|
1542
|
+
* that mutates the receiver, potentially invalidating RSTRING_PTR.
|
|
1350
1543
|
*/
|
|
1351
|
-
static
|
|
1352
|
-
|
|
1544
|
+
static void
|
|
1545
|
+
emit_bit_runs(VALUE self, int lsb_first, ssize_t start_offset, VALUE ary)
|
|
1353
1546
|
{
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
int lsb_first = parse_lsb_first(argc, argv);
|
|
1357
|
-
ssize_t src_len = RSTRING_LEN(self);
|
|
1358
|
-
if (src_len == 0) return self;
|
|
1359
|
-
|
|
1547
|
+
ssize_t src_len = RSTRING_LEN(self);
|
|
1548
|
+
if (src_len == 0 || start_offset >= src_len * 8) return;
|
|
1360
1549
|
ssize_t total_bits = src_len * 8;
|
|
1550
|
+
ssize_t offset = start_offset;
|
|
1551
|
+
|
|
1552
|
+
#define SB_EMIT_TRIPLE(bval, oval, lval) \
|
|
1553
|
+
do { if (ary == Qnil) rb_yield_values(3, (bval), (oval), (lval)); \
|
|
1554
|
+
else rb_ary_push(ary, rb_ary_new3(3, (bval), (oval), (lval))); } while (0)
|
|
1361
1555
|
|
|
1362
1556
|
if (lsb_first) {
|
|
1363
|
-
|
|
1364
|
-
while (pos < total_bits) {
|
|
1557
|
+
while (offset < total_bits) {
|
|
1365
1558
|
const unsigned char *src = (const unsigned char *)RSTRING_PTR(self);
|
|
1366
|
-
int bit
|
|
1367
|
-
ssize_t run = count_run_lsb(src, src_len,
|
|
1368
|
-
|
|
1369
|
-
|
|
1559
|
+
int bit = (src[offset >> 3] >> (offset & 7)) & 1;
|
|
1560
|
+
ssize_t run = count_run_lsb(src, src_len, offset, bit);
|
|
1561
|
+
SB_EMIT_TRIPLE(bit ? Qtrue : Qfalse, SSIZET2NUM(offset), SSIZET2NUM(run));
|
|
1562
|
+
offset += run;
|
|
1370
1563
|
}
|
|
1371
1564
|
}
|
|
1372
1565
|
else {
|
|
1373
|
-
|
|
1374
|
-
while (pos < total_bits) {
|
|
1566
|
+
while (offset < total_bits) {
|
|
1375
1567
|
const unsigned char *src = (const unsigned char *)RSTRING_PTR(self);
|
|
1376
|
-
int bit = logical_get_bit(src,
|
|
1568
|
+
int bit = logical_get_bit(src, offset, 0);
|
|
1377
1569
|
ssize_t run = 1;
|
|
1378
|
-
while (
|
|
1570
|
+
while (offset + run < total_bits && logical_get_bit(src, offset + run, 0) == bit) {
|
|
1379
1571
|
run++;
|
|
1380
1572
|
}
|
|
1381
|
-
|
|
1382
|
-
|
|
1573
|
+
SB_EMIT_TRIPLE(bit ? Qtrue : Qfalse, SSIZET2NUM(offset), SSIZET2NUM(run));
|
|
1574
|
+
offset += run;
|
|
1383
1575
|
}
|
|
1384
1576
|
}
|
|
1385
1577
|
|
|
1386
|
-
|
|
1578
|
+
#undef SB_EMIT_TRIPLE
|
|
1387
1579
|
}
|
|
1388
1580
|
|
|
1389
|
-
/* String#bit_runs(lsb_first: true) -> Array
|
|
1390
|
-
* String#bit_runs(lsb_first: true) { |bit, len| } -> self
|
|
1391
|
-
*
|
|
1392
|
-
* Non-iterator complement of each_bit_run. Without a block, collects all
|
|
1393
|
-
* (bit, run_length) pairs into an Array and returns it. With a block,
|
|
1394
|
-
* yields each pair and returns self.
|
|
1395
|
-
*
|
|
1396
|
-
* Follows the same pattern as String#bytes vs String#each_byte.
|
|
1397
|
-
*
|
|
1398
|
-
* Porting to Ruby Core:
|
|
1399
|
-
* 1. Move to string.c alongside each_bit_run; register in Init_String().
|
|
1400
|
-
*/
|
|
1401
1581
|
static VALUE
|
|
1402
|
-
|
|
1582
|
+
rb_str_each_bit_run(int argc, VALUE *argv, VALUE self)
|
|
1403
1583
|
{
|
|
1404
|
-
|
|
1405
|
-
ssize_t src_len = RSTRING_LEN(self);
|
|
1406
|
-
int have_block = rb_block_given_p();
|
|
1584
|
+
RETURN_ENUMERATOR(self, argc, argv);
|
|
1407
1585
|
|
|
1408
|
-
|
|
1586
|
+
VALUE start_offset_v = Qnil, opts = Qnil;
|
|
1587
|
+
rb_scan_args(argc, argv, "01:", &start_offset_v, &opts);
|
|
1588
|
+
validate_option_hash(opts, SB_KW_LSB_FIRST);
|
|
1589
|
+
int lsb_first = parse_lsb_first_opt(opts);
|
|
1590
|
+
ssize_t start_offset = parse_start_offset(start_offset_v);
|
|
1409
1591
|
|
|
1410
|
-
|
|
1411
|
-
|
|
1592
|
+
emit_bit_runs(self, lsb_first, start_offset, Qnil);
|
|
1593
|
+
return self;
|
|
1594
|
+
}
|
|
1412
1595
|
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
ssize_t pos = 0;
|
|
1427
|
-
while (pos < total_bits) {
|
|
1428
|
-
const unsigned char *src = (const unsigned char *)RSTRING_PTR(self);
|
|
1429
|
-
int bit = logical_get_bit(src, pos, 0);
|
|
1430
|
-
ssize_t run = 1;
|
|
1431
|
-
while (pos + run < total_bits && logical_get_bit(src, pos + run, 0) == bit) {
|
|
1432
|
-
run++;
|
|
1433
|
-
}
|
|
1434
|
-
VALUE bval = bit ? Qtrue : Qfalse;
|
|
1435
|
-
VALUE lval = SSIZET2NUM(run);
|
|
1436
|
-
have_block ? rb_yield_values(2, bval, lval)
|
|
1437
|
-
: rb_ary_push(result, rb_assoc_new(bval, lval));
|
|
1438
|
-
pos += run;
|
|
1439
|
-
}
|
|
1596
|
+
/* Non-iterator form of each_bit_run; collect run triples into an Array. */
|
|
1597
|
+
static VALUE
|
|
1598
|
+
rb_str_bit_runs(int argc, VALUE *argv, VALUE self)
|
|
1599
|
+
{
|
|
1600
|
+
VALUE start_offset_v = Qnil, opts = Qnil;
|
|
1601
|
+
rb_scan_args(argc, argv, "01:", &start_offset_v, &opts);
|
|
1602
|
+
validate_option_hash(opts, SB_KW_LSB_FIRST);
|
|
1603
|
+
int lsb_first = parse_lsb_first_opt(opts);
|
|
1604
|
+
ssize_t start_offset = parse_start_offset(start_offset_v);
|
|
1605
|
+
|
|
1606
|
+
if (rb_block_given_p()) {
|
|
1607
|
+
emit_bit_runs(self, lsb_first, start_offset, Qnil);
|
|
1608
|
+
return self;
|
|
1440
1609
|
}
|
|
1441
1610
|
|
|
1442
|
-
|
|
1611
|
+
VALUE ary = rb_ary_new();
|
|
1612
|
+
emit_bit_runs(self, lsb_first, start_offset, ary);
|
|
1613
|
+
return ary;
|
|
1443
1614
|
}
|
|
1444
1615
|
|
|
1445
|
-
/*
|
|
1446
|
-
* String#bit_splice(bit_index, bit_length, str, str_bit_index, str_bit_length) -> self
|
|
1447
|
-
* String#bit_splice(range, str) -> self
|
|
1448
|
-
* String#bit_splice(range, str, str_range) -> self
|
|
1449
|
-
*
|
|
1450
|
-
* Writes bits from str into self at bit-level granularity. The inverse of
|
|
1451
|
-
* bit_slice: where bit_slice reads a sub-sequence of bits, bit_splice writes one.
|
|
1452
|
-
*
|
|
1453
|
-
* The destination and source bit lengths must be equal; bit_splice does not
|
|
1454
|
-
* resize self (sub-byte resize is undefined). This mirrors the constraint that
|
|
1455
|
-
* bytesplice imposes when the replacement has the same byte length.
|
|
1456
|
-
*
|
|
1457
|
-
* Negative indices count backward from the end, exactly as in bytesplice.
|
|
1458
|
-
* Returns self.
|
|
1459
|
-
*
|
|
1460
|
-
* Porting to Ruby Core:
|
|
1461
|
-
* 1. Move to string.c; register in Init_String().
|
|
1462
|
-
* 2. Use rb_str_modify_expand if resize support is ever added.
|
|
1463
|
-
* 3. bit_copy_core moves with it; share ebs_extract with bit_slice.
|
|
1464
|
-
*/
|
|
1616
|
+
/* Write bits from str into self at bit-level granularity (inverse of bit_slice). */
|
|
1465
1617
|
static VALUE
|
|
1466
1618
|
rb_str_bit_splice(int argc, VALUE *argv, VALUE self)
|
|
1467
1619
|
{
|
|
@@ -1469,14 +1621,15 @@ rb_str_bit_splice(int argc, VALUE *argv, VALUE self)
|
|
|
1469
1621
|
ssize_t src_bit_off, src_bit_len;
|
|
1470
1622
|
VALUE str;
|
|
1471
1623
|
ssize_t dst_total = RSTRING_LEN(self) * 8;
|
|
1472
|
-
VALUE v0, v1, v2, v3,
|
|
1624
|
+
VALUE v0, v1, v2, v3, opts;
|
|
1473
1625
|
|
|
1474
|
-
int n_pos = rb_scan_args(argc, argv, "
|
|
1626
|
+
int n_pos = rb_scan_args(argc, argv, "22:", &v0, &v1, &v2, &v3, &opts);
|
|
1475
1627
|
validate_option_hash(opts, SB_KW_LSB_FIRST);
|
|
1476
1628
|
int lsb_first = parse_lsb_first_opt(opts);
|
|
1477
1629
|
|
|
1478
1630
|
if (n_pos == 2 && rb_obj_is_kind_of(v0, rb_cRange)) {
|
|
1479
1631
|
/* bit_splice(range, str) */
|
|
1632
|
+
sb_range_validate_endpoints(v0);
|
|
1480
1633
|
ssize_t beg, len;
|
|
1481
1634
|
sb_range_beg_len(v0, &beg, &len, dst_total, 1);
|
|
1482
1635
|
dst_bit_off = beg;
|
|
@@ -1487,20 +1640,21 @@ rb_str_bit_splice(int argc, VALUE *argv, VALUE self)
|
|
|
1487
1640
|
src_bit_len = dst_bit_len;
|
|
1488
1641
|
}
|
|
1489
1642
|
else if (n_pos == 3 && rb_obj_is_kind_of(v0, rb_cRange)) {
|
|
1490
|
-
/* bit_splice(range, str,
|
|
1643
|
+
/* bit_splice(range, str, str_bit_index) */
|
|
1644
|
+
sb_range_validate_endpoints(v0);
|
|
1491
1645
|
ssize_t beg, len;
|
|
1492
1646
|
sb_range_beg_len(v0, &beg, &len, dst_total, 1);
|
|
1493
1647
|
dst_bit_off = beg;
|
|
1494
1648
|
dst_bit_len = len;
|
|
1495
1649
|
str = v1;
|
|
1496
1650
|
Check_Type(str, T_STRING);
|
|
1497
|
-
if (!
|
|
1498
|
-
rb_raise(rb_eTypeError, "third argument must be
|
|
1651
|
+
if (!rb_integer_type_p(v2)) {
|
|
1652
|
+
rb_raise(rb_eTypeError, "third argument must be an Integer");
|
|
1499
1653
|
}
|
|
1500
1654
|
ssize_t src_total = RSTRING_LEN(str) * 8;
|
|
1501
|
-
|
|
1502
|
-
src_bit_off
|
|
1503
|
-
src_bit_len =
|
|
1655
|
+
src_bit_off = integer_to_bit_idx(v2);
|
|
1656
|
+
if (src_bit_off < 0) src_bit_off += src_total;
|
|
1657
|
+
src_bit_len = dst_bit_len;
|
|
1504
1658
|
}
|
|
1505
1659
|
else if (n_pos == 3) {
|
|
1506
1660
|
/* bit_splice(bit_index, bit_length, str) */
|
|
@@ -1526,10 +1680,9 @@ rb_str_bit_splice(int argc, VALUE *argv, VALUE self)
|
|
|
1526
1680
|
src_bit_off = 0;
|
|
1527
1681
|
src_bit_len = dst_bit_len;
|
|
1528
1682
|
}
|
|
1529
|
-
else if (n_pos ==
|
|
1530
|
-
/* bit_splice(bit_index, bit_length, str, str_bit_index
|
|
1531
|
-
if (!rb_integer_type_p(v0) || !rb_integer_type_p(v1) ||
|
|
1532
|
-
!rb_integer_type_p(v3) || !rb_integer_type_p(v4)) {
|
|
1683
|
+
else if (n_pos == 4) {
|
|
1684
|
+
/* bit_splice(bit_index, bit_length, str, str_bit_index) */
|
|
1685
|
+
if (!rb_integer_type_p(v0) || !rb_integer_type_p(v1) || !rb_integer_type_p(v3)) {
|
|
1533
1686
|
rb_raise(rb_eTypeError, "bit indices and lengths must be integers");
|
|
1534
1687
|
}
|
|
1535
1688
|
dst_bit_off = integer_to_bit_idx(v0);
|
|
@@ -1539,12 +1692,12 @@ rb_str_bit_splice(int argc, VALUE *argv, VALUE self)
|
|
|
1539
1692
|
Check_Type(str, T_STRING);
|
|
1540
1693
|
ssize_t src_total = RSTRING_LEN(str) * 8;
|
|
1541
1694
|
src_bit_off = integer_to_bit_idx(v3);
|
|
1542
|
-
src_bit_len = integer_to_bit_idx(v4);
|
|
1543
1695
|
if (src_bit_off < 0) src_bit_off += src_total;
|
|
1696
|
+
src_bit_len = dst_bit_len;
|
|
1544
1697
|
}
|
|
1545
1698
|
else {
|
|
1546
1699
|
rb_raise(rb_eArgError,
|
|
1547
|
-
"wrong number of arguments (given %d, expected 2, 3, or
|
|
1700
|
+
"wrong number of arguments (given %d, expected 2, 3, or 4)", n_pos);
|
|
1548
1701
|
}
|
|
1549
1702
|
|
|
1550
1703
|
if (dst_bit_off < 0 || dst_bit_len < 0 || dst_bit_off + dst_bit_len > dst_total) {
|
|
@@ -1560,12 +1713,6 @@ rb_str_bit_splice(int argc, VALUE *argv, VALUE self)
|
|
|
1560
1713
|
src_bit_off, src_bit_len, src_total_bits);
|
|
1561
1714
|
}
|
|
1562
1715
|
|
|
1563
|
-
if (dst_bit_len != src_bit_len) {
|
|
1564
|
-
rb_raise(rb_eArgError,
|
|
1565
|
-
"bit_splice: destination length (%ld) must equal source length (%ld)",
|
|
1566
|
-
dst_bit_len, src_bit_len);
|
|
1567
|
-
}
|
|
1568
|
-
|
|
1569
1716
|
if (dst_bit_len == 0) return self;
|
|
1570
1717
|
|
|
1571
1718
|
/* Guard against self-aliasing: duplicate src before modifying self */
|
|
@@ -1765,38 +1912,36 @@ rb_ary_mask_bang(int argc, VALUE *argv, VALUE self)
|
|
|
1765
1912
|
void
|
|
1766
1913
|
Init_string_bits(void)
|
|
1767
1914
|
{
|
|
1768
|
-
id_bracket
|
|
1769
|
-
sym_lsb_first
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
rb_define_method(rb_cString, "
|
|
1775
|
-
rb_define_method(rb_cString, "
|
|
1776
|
-
rb_define_method(rb_cString, "
|
|
1777
|
-
rb_define_method(rb_cString, "
|
|
1778
|
-
rb_define_method(rb_cString, "
|
|
1779
|
-
rb_define_method(rb_cString, "
|
|
1780
|
-
rb_define_method(rb_cString, "
|
|
1781
|
-
rb_define_method(rb_cString, "
|
|
1782
|
-
rb_define_method(rb_cString, "
|
|
1783
|
-
rb_define_method(rb_cString, "
|
|
1784
|
-
rb_define_method(rb_cString, "
|
|
1785
|
-
rb_define_method(rb_cString, "
|
|
1786
|
-
rb_define_method(rb_cString, "
|
|
1787
|
-
rb_define_method(rb_cString, "
|
|
1788
|
-
rb_define_method(rb_cString, "
|
|
1789
|
-
rb_define_method(rb_cString, "
|
|
1790
|
-
rb_define_method(rb_cString, "
|
|
1791
|
-
rb_define_method(rb_cString, "
|
|
1792
|
-
rb_define_method(rb_cString, "
|
|
1793
|
-
rb_define_method(rb_cString, "
|
|
1794
|
-
rb_define_method(rb_cString, "bit_xor", rb_str_bit_xor, 1);
|
|
1795
|
-
rb_define_method(rb_cString, "bit_xor!", rb_str_bit_xor_bang, 1);
|
|
1915
|
+
id_bracket = rb_intern("[]");
|
|
1916
|
+
sym_lsb_first = ID2SYM(rb_intern("lsb_first"));
|
|
1917
|
+
sym_invert = ID2SYM(rb_intern("invert"));
|
|
1918
|
+
|
|
1919
|
+
rb_define_method(rb_cString, "bit_at", rb_str_bit_at, -1);
|
|
1920
|
+
rb_define_method(rb_cString, "bit_count", rb_str_bit_count, -1);
|
|
1921
|
+
rb_define_method(rb_cString, "each_bit", rb_str_each_bit, -1);
|
|
1922
|
+
rb_define_method(rb_cString, "bits", rb_str_bits, -1);
|
|
1923
|
+
rb_define_method(rb_cString, "each_bit_offset", rb_str_each_bit_offset, -1);
|
|
1924
|
+
rb_define_method(rb_cString, "bit_offsets", rb_str_bit_offsets, -1);
|
|
1925
|
+
rb_define_method(rb_cString, "bit_slice", rb_str_bit_slice, -1);
|
|
1926
|
+
rb_define_method(rb_cString, "bit_splice", rb_str_bit_splice, -1);
|
|
1927
|
+
rb_define_method(rb_cString, "bit_run_count", rb_str_bit_run_count, -1);
|
|
1928
|
+
rb_define_method(rb_cString, "each_bit_run", rb_str_each_bit_run, -1);
|
|
1929
|
+
rb_define_method(rb_cString, "bit_runs", rb_str_bit_runs, -1);
|
|
1930
|
+
rb_define_method(rb_cString, "bit_set", rb_str_bit_set, -1);
|
|
1931
|
+
rb_define_method(rb_cString, "bit_clear", rb_str_bit_clear, -1);
|
|
1932
|
+
rb_define_method(rb_cString, "bit_flip", rb_str_bit_flip, -1);
|
|
1933
|
+
rb_define_method(rb_cString, "bitwise_not", rb_str_bitwise_not, 0);
|
|
1934
|
+
rb_define_method(rb_cString, "bitwise_not!", rb_str_bitwise_not_bang, 0);
|
|
1935
|
+
rb_define_method(rb_cString, "bitwise_and", rb_str_bitwise_and, 1);
|
|
1936
|
+
rb_define_method(rb_cString, "bitwise_and!", rb_str_bitwise_and_bang, 1);
|
|
1937
|
+
rb_define_method(rb_cString, "bitwise_or", rb_str_bitwise_or, 1);
|
|
1938
|
+
rb_define_method(rb_cString, "bitwise_or!", rb_str_bitwise_or_bang, 1);
|
|
1939
|
+
rb_define_method(rb_cString, "bitwise_xor", rb_str_bitwise_xor, 1);
|
|
1940
|
+
rb_define_method(rb_cString, "bitwise_xor!", rb_str_bitwise_xor_bang, 1);
|
|
1796
1941
|
|
|
1797
1942
|
// These methods are defined here to avoid cluttering this file, but they are not part of the current core proposal (see FUTURE_PROPOSAL_PLAN.md).
|
|
1798
|
-
rb_define_method(rb_cString, "each_bit_field",
|
|
1799
|
-
rb_define_method(rb_cString, "bit_fields",
|
|
1800
|
-
rb_define_method(rb_cArray, "mask",
|
|
1801
|
-
rb_define_method(rb_cArray, "mask!",
|
|
1943
|
+
rb_define_method(rb_cString, "each_bit_field", rb_str_each_bit_field, -1);
|
|
1944
|
+
rb_define_method(rb_cString, "bit_fields", rb_str_bit_fields, -1);
|
|
1945
|
+
rb_define_method(rb_cArray, "mask", rb_ary_mask, -1);
|
|
1946
|
+
rb_define_method(rb_cArray, "mask!", rb_ary_mask_bang, -1);
|
|
1802
1947
|
}
|