string_bits 0.2.0 → 0.3.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 +6 -2
- data/ext/string_bits/string_bits.c +467 -298
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c21407d901f599aa0d33fe0bad85d39f4729da08c656cab3556fc087198394f2
|
|
4
|
+
data.tar.gz: e4c8912125bfd9f4a6fcfce0be0d78f528f0bb87369064cfc1c96b4d3f5b7ac9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8d73669323a7e9cee148d96775e8d9269b2c3c990906ad62e03d20466f93f3f2b7cafb911778fed607f7bdc561eaae57ac35770910de97f893addceb68e122bb
|
|
7
|
+
data.tar.gz: 359fd91c4eb04d4338850de1e38e2162c0d308a2697f86d2ef8ddbf200656384f62e81aa5eabac2c44c5afc79bde0383bc5b672a4a16ad50ab44fa0bcb195323
|
data/README.md
CHANGED
|
@@ -4,7 +4,9 @@ Ruby's `String` is already a byte sequence. This gem extends it one level lower:
|
|
|
4
4
|
|
|
5
5
|
The methods are designed for real workloads: Apache Arrow validity bitmaps, bitmap font glyph data, and any protocol buffer where bits carry meaning. Many methods read or modify the existing string bytes directly; methods such as `bit_slice`, `bitwise_not`, `bitwise_and`, `bitwise_or`, `bitwise_xor`, `bits`, `bit_offsets`, `bit_runs`, and `Array#mask` allocate a derived result object. Because the API relies solely on `String`, the same methods would benefit memory-constrained implementations such as mruby and PicoRuby once adopted into CRuby.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Every method reads the receiver as a raw byte sequence, so the `String`s that `bit_slice` and the non-destructive `bitwise_*` methods return are always `Encoding::BINARY`. The destructive methods leave the receiver's encoding untouched, as `String#setbyte` does.
|
|
8
|
+
|
|
9
|
+
A single keyword, `lsb_first:`, controls bit ordering across the API: it selects intra-byte numbering wherever positions are exchanged with the caller, and intra-byte scan direction wherever the API walks the sequence. Across-byte order is always `byte[0]` first, and result Strings from `bit_slice` are always packed LSB-first regardless, so `data.each_bit_offset(true, lsb_first: false).all? { |n| data.bit_set?(n, lsb_first: false) }` stays true.
|
|
8
10
|
|
|
9
11
|
## Usage
|
|
10
12
|
|
|
@@ -14,7 +16,9 @@ gem install string_bits
|
|
|
14
16
|
|
|
15
17
|
```ruby
|
|
16
18
|
require "string_bits"
|
|
17
|
-
puts "\xAA\xAA\xAA\xAA".
|
|
19
|
+
puts "\xAA\xAA\xAA\xAA".bit_get(10)
|
|
20
|
+
#=> 0
|
|
21
|
+
puts "\xAA\xAA\xAA\xAA".bit_set?(10)
|
|
18
22
|
#=> false
|
|
19
23
|
|
|
20
24
|
puts "\xAA".each_bit(lsb_first: false).to_a.inspect
|
|
@@ -2,10 +2,40 @@
|
|
|
2
2
|
#include "ruby/encoding.h"
|
|
3
3
|
|
|
4
4
|
#include <limits.h> /* CHAR_BIT */
|
|
5
|
-
#include <stdint.h> /* uint64_t, UINT64_MAX */
|
|
5
|
+
#include <stdint.h> /* uint64_t, UINT64_MAX, int64_t, intptr_t */
|
|
6
|
+
#include <inttypes.h> /* PRIdPTR (ssize_t via intptr_t), PRId64 */
|
|
6
7
|
#include <string.h> /* memcpy */
|
|
7
8
|
#include <sys/types.h> /* ssize_t (Ruby typedefs it on Windows) */
|
|
8
9
|
|
|
10
|
+
/* Whole-string bit length, computed in 64 bits.
|
|
11
|
+
*
|
|
12
|
+
* RSTRING_LEN returns a pointer-width signed length, so `RSTRING_LEN(s) * 8`
|
|
13
|
+
* overflows a signed 32-bit ssize_t once a string reaches 2**28 bytes (256 MiB)
|
|
14
|
+
* on an ILP32 build, corrupting every bounds check that compares a bit offset
|
|
15
|
+
* against it. Valid bit indices are confined to the Fixnum range and always fit
|
|
16
|
+
* ssize_t, so only this whole-string bit length needs the wider type: computing
|
|
17
|
+
* it in int64_t keeps the bounds checks correct on 32-bit without changing the
|
|
18
|
+
* public pointer-width bit-index contract (see Discussion.md, "Error behavior
|
|
19
|
+
* for out-of-range bit indices").
|
|
20
|
+
*
|
|
21
|
+
* Porting to Ruby Core:
|
|
22
|
+
* 1. Core String lengths are `long` (RSTRING_LEN), which is pointer-width,
|
|
23
|
+
* so `RSTRING_LEN(str) * 8` overflows on ILP32 for strings >= 256 MiB
|
|
24
|
+
* exactly as it does for ssize_t here. Keep the whole-string bit length
|
|
25
|
+
* in a 64-bit intermediate at every bounds check; do not hold it in a
|
|
26
|
+
* `long`. Reuse this macro (or an equivalent inline) rather than open-
|
|
27
|
+
* coding `len * 8`.
|
|
28
|
+
* 2. Keep the public bit-index type pointer-width and keep rejecting
|
|
29
|
+
* out-of-range positions with ArgumentError (see the cross-reference
|
|
30
|
+
* above). Only this internal length is widened, so the contract that
|
|
31
|
+
* core inherits is unchanged.
|
|
32
|
+
* 3. The error-message format specifiers below (<inttypes.h>: (intptr_t)
|
|
33
|
+
* with PRIdPTR for bit offsets, PRId64 for this widened length) exist
|
|
34
|
+
* only because this length is wider than the offsets. In core, follow
|
|
35
|
+
* the local convention for formatting `long` offsets and pick a 64-bit
|
|
36
|
+
* specifier for the widened length accordingly. */
|
|
37
|
+
#define SB_BIT_LEN(byte_len) ((int64_t)(byte_len) * 8)
|
|
38
|
+
|
|
9
39
|
/* popcount ----------------------------------------------------------------- */
|
|
10
40
|
/*
|
|
11
41
|
* Porting to Ruby Core:
|
|
@@ -172,22 +202,179 @@ test_bit(const char *ptr, ssize_t bit_index)
|
|
|
172
202
|
return rb_str_get_bit(ptr, bit_index, 1);
|
|
173
203
|
}
|
|
174
204
|
|
|
175
|
-
/*
|
|
205
|
+
/* Bit position arguments -------------------------------------------------- */
|
|
206
|
+
/*
|
|
207
|
+
* Every bit offset in this API -- the single-bit offset of bit_get and friends,
|
|
208
|
+
* the offset of an offset/length pair, and each endpoint of a bit Range --
|
|
209
|
+
* follows one contract, the one accepted into Ruby core:
|
|
210
|
+
*
|
|
211
|
+
* - the argument goes through rb_to_int, so any object responding to #to_int
|
|
212
|
+
* is accepted, exactly like the index of String#setbyte;
|
|
213
|
+
* - a negative offset raises IndexError. Negative positions are not read as
|
|
214
|
+
* count-from-end, because combining that normalization with the
|
|
215
|
+
* lsb_first: true/false numbering would make the addressing rule much
|
|
216
|
+
* harder to state (see Discussion.md);
|
|
217
|
+
* - any non-negative offset below 2**64 is a well-formed position. One past
|
|
218
|
+
* the end of the receiver is not an error in itself: it takes the ordinary
|
|
219
|
+
* out-of-range path of whichever method received it (nil, zero, an empty
|
|
220
|
+
* iteration, or IndexError for a mutation);
|
|
221
|
+
* - only an offset that does not fit in uint64_t raises ArgumentError, since
|
|
222
|
+
* no byte buffer on any platform can be addressed by it.
|
|
223
|
+
*
|
|
224
|
+
* The return type is therefore uint64_t. Callers bounds-check it against
|
|
225
|
+
* SB_BIT_LEN(), and any offset that survives that check is below the bit
|
|
226
|
+
* length of a real string, hence always representable as ssize_t.
|
|
176
227
|
*
|
|
177
|
-
* Raises ArgumentError for Bignums on all platforms: a Bignum cannot be a
|
|
178
|
-
* valid bit index for any real string, and raising explicitly is clearer than
|
|
179
|
-
* silently mapping to a sentinel value that later triggers a different error.
|
|
180
228
|
* NUM2SSIZET is width-aware (uses FIX2LL on LLP64, FIX2LONG on LP64) so the
|
|
181
229
|
* FIXNUM extraction does not truncate large FIXNUMs on Windows.
|
|
230
|
+
* RBIGNUM_NEGATIVE_P and rb_absint_size are available via ruby.h.
|
|
182
231
|
*
|
|
183
|
-
*
|
|
184
|
-
|
|
185
|
-
|
|
232
|
+
* Porting to Ruby Core:
|
|
233
|
+
* Core keeps a `long` fast path alongside the uint64_t value so that the
|
|
234
|
+
* common Fixnum case never leaves the pointer-width arithmetic. That split
|
|
235
|
+
* is a pure optimization; the observable contract is the one above.
|
|
236
|
+
*/
|
|
237
|
+
|
|
238
|
+
/* True when a positive Integer does not fit uint64_t. rb_absint_size returns
|
|
239
|
+
* the byte width of the absolute value, so the bound is exactly 8 bytes. */
|
|
240
|
+
static inline int
|
|
241
|
+
sb_exceeds_uint64(VALUE integer)
|
|
242
|
+
{
|
|
243
|
+
return rb_absint_size(integer, NULL) > sizeof(uint64_t);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
static uint64_t
|
|
247
|
+
sb_bit_offset(VALUE n)
|
|
186
248
|
{
|
|
187
|
-
|
|
249
|
+
VALUE integer = rb_to_int(n);
|
|
250
|
+
|
|
251
|
+
if (FIXNUM_P(integer)) {
|
|
252
|
+
ssize_t value = NUM2SSIZET(integer);
|
|
253
|
+
if (value < 0) {
|
|
254
|
+
rb_raise(rb_eIndexError, "bit index out of range");
|
|
255
|
+
}
|
|
256
|
+
return (uint64_t)value;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
RUBY_ASSERT(RB_TYPE_P(integer, T_BIGNUM));
|
|
260
|
+
if (RBIGNUM_NEGATIVE_P(integer)) {
|
|
261
|
+
rb_raise(rb_eIndexError, "bit index out of range");
|
|
262
|
+
}
|
|
263
|
+
if (sb_exceeds_uint64(integer)) {
|
|
264
|
+
rb_raise(rb_eArgError, "bit index out of representable range");
|
|
265
|
+
}
|
|
266
|
+
return (uint64_t)NUM2ULL(integer);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/* Bit lengths share the offset's representable range, but a negative length is
|
|
270
|
+
* an ArgumentError rather than an IndexError: it is a malformed size, not a
|
|
271
|
+
* position outside the string. A length that runs past the end of the receiver
|
|
272
|
+
* is clamped by the caller, matching byteslice. */
|
|
273
|
+
static uint64_t
|
|
274
|
+
sb_bit_length(VALUE n)
|
|
275
|
+
{
|
|
276
|
+
VALUE integer = rb_to_int(n);
|
|
277
|
+
|
|
278
|
+
if (FIXNUM_P(integer)) {
|
|
279
|
+
ssize_t value = NUM2SSIZET(integer);
|
|
280
|
+
if (value < 0) {
|
|
281
|
+
rb_raise(rb_eArgError, "bit_length must be non-negative");
|
|
282
|
+
}
|
|
283
|
+
return (uint64_t)value;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
RUBY_ASSERT(RB_TYPE_P(integer, T_BIGNUM));
|
|
287
|
+
if (RBIGNUM_NEGATIVE_P(integer)) {
|
|
288
|
+
rb_raise(rb_eArgError, "bit_length must be non-negative");
|
|
289
|
+
}
|
|
290
|
+
if (sb_exceeds_uint64(integer)) {
|
|
291
|
+
rb_raise(rb_eArgError, "bit_length out of representable range");
|
|
292
|
+
}
|
|
293
|
+
return (uint64_t)NUM2ULL(integer);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/* Soft variants for bit_slice, which answers nil for an argument it cannot
|
|
297
|
+
* use instead of raising -- the same leniency String#byteslice has. Only the
|
|
298
|
+
* unrepresentable case still raises, so the boundary between "a position past
|
|
299
|
+
* the end" and "not a position at all" stays where the rest of the API puts
|
|
300
|
+
* it. Returns 0 on success, or -1 for a non-Integer or a negative value. */
|
|
301
|
+
static int
|
|
302
|
+
sb_bit_position_soft(VALUE n, uint64_t *out)
|
|
303
|
+
{
|
|
304
|
+
if (!rb_integer_type_p(n)) return -1;
|
|
305
|
+
|
|
306
|
+
if (FIXNUM_P(n)) {
|
|
307
|
+
ssize_t value = NUM2SSIZET(n);
|
|
308
|
+
if (value < 0) return -1;
|
|
309
|
+
*out = (uint64_t)value;
|
|
310
|
+
return 0;
|
|
311
|
+
}
|
|
312
|
+
|
|
188
313
|
RUBY_ASSERT(RB_TYPE_P(n, T_BIGNUM));
|
|
189
|
-
|
|
190
|
-
|
|
314
|
+
if (RBIGNUM_NEGATIVE_P(n)) return -1;
|
|
315
|
+
if (sb_exceeds_uint64(n)) {
|
|
316
|
+
rb_raise(rb_eArgError, "bit index out of representable range");
|
|
317
|
+
}
|
|
318
|
+
*out = (uint64_t)NUM2ULL(n);
|
|
319
|
+
return 0;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/* Narrowing a parsed position to the internal index ----------------------- */
|
|
323
|
+
/*
|
|
324
|
+
* Positions are parsed in 64 bits but held in a pointer-width signed integer
|
|
325
|
+
* once they address memory, and on an ILP32 build those two widths differ:
|
|
326
|
+
* SB_BIT_LEN can reach 2**34 while ssize_t stops at 2**31-1, so a string of
|
|
327
|
+
* 256 MiB or more has bits that exist but cannot be indexed internally. Such a
|
|
328
|
+
* position gets the same answer as one that does not fit 64 bits at all --
|
|
329
|
+
* ArgumentError -- rather than being silently truncated into a valid-looking
|
|
330
|
+
* index. On LP64 the gap is empty and none of this is reachable: no String is
|
|
331
|
+
* large enough for its bit length to exceed SSIZE_MAX.
|
|
332
|
+
*/
|
|
333
|
+
#ifndef SSIZE_MAX
|
|
334
|
+
# define SSIZE_MAX ((ssize_t)(((size_t)-1) >> 1))
|
|
335
|
+
#endif
|
|
336
|
+
#define SB_MAX_BIT_POS ((uint64_t)SSIZE_MAX)
|
|
337
|
+
|
|
338
|
+
/* The receiver's bit length as an internal index, saturated at ssize_t. */
|
|
339
|
+
static inline ssize_t
|
|
340
|
+
sb_addressable_bits(int64_t total)
|
|
341
|
+
{
|
|
342
|
+
return (ssize_t)(((uint64_t)total > SB_MAX_BIT_POS) ? SB_MAX_BIT_POS : (uint64_t)total);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/* Narrow a parsed position for the methods whose answer at or past the end is
|
|
346
|
+
* "there is nothing here" (an empty iteration, a count of zero, an empty
|
|
347
|
+
* slice): those saturate at the end of the string rather than failing. */
|
|
348
|
+
static ssize_t
|
|
349
|
+
sb_narrow_bit_pos(uint64_t pos, int64_t total)
|
|
350
|
+
{
|
|
351
|
+
if (pos >= (uint64_t)total) return sb_addressable_bits(total);
|
|
352
|
+
if (pos > SB_MAX_BIT_POS) {
|
|
353
|
+
rb_raise(rb_eArgError, "bit index out of representable range");
|
|
354
|
+
}
|
|
355
|
+
return (ssize_t)pos;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/* Clamp a parsed length to the bits still addressable after `beg`. */
|
|
359
|
+
static ssize_t
|
|
360
|
+
sb_clamp_bit_length(uint64_t len, ssize_t beg, int64_t total)
|
|
361
|
+
{
|
|
362
|
+
uint64_t available = (uint64_t)sb_addressable_bits(total) - (uint64_t)beg;
|
|
363
|
+
return (ssize_t)(len > available ? available : len);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/* Resolve a single-bit offset against the receiver's bit length.
|
|
367
|
+
* Returns -1 when the offset is past the end of the string. */
|
|
368
|
+
static ssize_t
|
|
369
|
+
sb_resolve_bit_offset(VALUE self, VALUE n)
|
|
370
|
+
{
|
|
371
|
+
int64_t total = SB_BIT_LEN(RSTRING_LEN(self));
|
|
372
|
+
uint64_t offset = sb_bit_offset(n);
|
|
373
|
+
if ((uint64_t)total <= offset) return -1;
|
|
374
|
+
if (offset > SB_MAX_BIT_POS) {
|
|
375
|
+
rb_raise(rb_eArgError, "bit index out of representable range");
|
|
376
|
+
}
|
|
377
|
+
return (ssize_t)offset;
|
|
191
378
|
}
|
|
192
379
|
|
|
193
380
|
/* Bit numbering between byte-with-LSB-as-bit-0 and byte-with-MSB-as-bit-0
|
|
@@ -223,54 +410,32 @@ logical_write_bit(unsigned char *ptr, ssize_t logical_index, int lsb_first, int
|
|
|
223
410
|
static ssize_t
|
|
224
411
|
check_bit_index(VALUE self, VALUE n, int lsb_first)
|
|
225
412
|
{
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
229
|
-
ssize_t idx = integer_to_bit_idx(n);
|
|
230
|
-
ssize_t size = RSTRING_LEN(self) * 8;
|
|
231
|
-
if (idx < 0 || idx >= size) {
|
|
413
|
+
ssize_t idx = sb_resolve_bit_offset(self, n);
|
|
414
|
+
if (idx < 0) {
|
|
232
415
|
rb_raise(rb_eIndexError, "bit index out of range");
|
|
233
416
|
}
|
|
234
417
|
return logical_to_physical(idx, lsb_first);
|
|
235
418
|
}
|
|
236
419
|
|
|
237
|
-
/*
|
|
420
|
+
/* Resolve a bit Range against the receiver's bit length.
|
|
238
421
|
*
|
|
239
|
-
* rb_range_beg_len()
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
422
|
+
* This replaces rb_range_beg_len() rather than wrapping it, for two reasons.
|
|
423
|
+
* rb_range_beg_len() takes `long *` endpoints, so on Windows (LLP64), where
|
|
424
|
+
* long is 32-bit while ssize_t is 64-bit, its interface does not match the
|
|
425
|
+
* pointer-width bit index used here. More importantly, it raises RangeError
|
|
426
|
+
* for any endpoint that does not fit `long`, whereas a bit Range endpoint is
|
|
427
|
+
* allowed to be any position up to UINT64_MAX (see sb_bit_offset); such an
|
|
428
|
+
* endpoint simply lies past the end of the string.
|
|
246
429
|
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
int err;
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
static VALUE
|
|
260
|
-
sb_range_beg_len_call(VALUE arg)
|
|
261
|
-
{
|
|
262
|
-
struct sb_range_args *a = (struct sb_range_args *)arg;
|
|
263
|
-
return rb_range_beg_len(a->range, a->lbegp, a->llenp, a->len, a->err);
|
|
264
|
-
}
|
|
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.
|
|
430
|
+
* The resolution rules match rb_range_beg_len() with err=0, minus the
|
|
431
|
+
* count-from-end normalization that this API does not have:
|
|
432
|
+
* - a nil begin is 0, a nil end runs to the end of the string;
|
|
433
|
+
* - an end past the end of the string is clamped to it;
|
|
434
|
+
* - a begin past the end of the string reports SB_RANGE_OUT, and each caller
|
|
435
|
+
* decides what that means (0 bits counted, a nil slice, an IndexError).
|
|
436
|
+
* Like rb_range_beg_len(), the returned length may exceed the bits actually
|
|
437
|
+
* available when the end was clamped, so callers that write must re-check
|
|
438
|
+
* beg + len against the total (see rb_str_mutate_bits and rb_str_bit_splice).
|
|
274
439
|
*
|
|
275
440
|
* Porting to Ruby Core:
|
|
276
441
|
* Replace rb_range_values() with direct struct access:
|
|
@@ -279,47 +444,39 @@ sb_range_beg_len_call(VALUE arg)
|
|
|
279
444
|
* end = RANGE_END(range);
|
|
280
445
|
* excl = RANGE_EXCL(range);
|
|
281
446
|
*/
|
|
282
|
-
|
|
283
|
-
|
|
447
|
+
enum sb_range_result {
|
|
448
|
+
SB_RANGE_OUT = 0, /* the range begins past the end of the string */
|
|
449
|
+
SB_RANGE_OK = 1
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
static enum sb_range_result
|
|
453
|
+
sb_bit_range_beg_len(VALUE range, ssize_t *begp, ssize_t *lenp, int64_t total)
|
|
284
454
|
{
|
|
285
|
-
VALUE
|
|
455
|
+
VALUE beg_v, end_v;
|
|
286
456
|
int excl;
|
|
287
|
-
rb_range_values(range, &
|
|
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
|
-
}
|
|
457
|
+
rb_range_values(range, &beg_v, &end_v, &excl);
|
|
303
458
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
VALUE result = rb_protect(sb_range_beg_len_call, (VALUE)&args, &state);
|
|
312
|
-
if (state) {
|
|
313
|
-
VALUE exc = rb_errinfo();
|
|
314
|
-
rb_set_errinfo(Qnil);
|
|
315
|
-
if (rb_obj_is_kind_of(exc, rb_eRangeError)) {
|
|
316
|
-
rb_raise(rb_eIndexError, "bit range out of range");
|
|
317
|
-
}
|
|
318
|
-
rb_exc_raise(exc);
|
|
459
|
+
uint64_t total_u = (uint64_t)total;
|
|
460
|
+
uint64_t beg = NIL_P(beg_v) ? 0 : sb_bit_offset(beg_v);
|
|
461
|
+
if (beg > total_u) return SB_RANGE_OUT;
|
|
462
|
+
|
|
463
|
+
uint64_t end_exclusive;
|
|
464
|
+
if (NIL_P(end_v)) {
|
|
465
|
+
end_exclusive = total_u;
|
|
319
466
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
467
|
+
else {
|
|
468
|
+
uint64_t end = sb_bit_offset(end_v);
|
|
469
|
+
if (end > total_u) end = total_u;
|
|
470
|
+
end_exclusive = excl ? end : end + 1;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/* The length is deliberately left unclamped: when the end was clamped it
|
|
474
|
+
* can exceed the bits available, which is exactly what lets a writing
|
|
475
|
+
* caller detect the overrun. Reading callers clamp it themselves. */
|
|
476
|
+
uint64_t len = (end_exclusive > beg) ? end_exclusive - beg : 0;
|
|
477
|
+
*begp = sb_narrow_bit_pos(beg, total);
|
|
478
|
+
*lenp = (ssize_t)(len > SB_MAX_BIT_POS ? SB_MAX_BIT_POS : len);
|
|
479
|
+
return SB_RANGE_OK;
|
|
323
480
|
}
|
|
324
481
|
|
|
325
482
|
static void
|
|
@@ -342,16 +499,20 @@ validate_option_hash(VALUE opts, unsigned allowed)
|
|
|
342
499
|
}
|
|
343
500
|
}
|
|
344
501
|
|
|
502
|
+
/* An absent keyword falls back to the default, but an explicitly passed value
|
|
503
|
+
* must be true or false: `lsb_first: nil` is a caller mistake, not a request
|
|
504
|
+
* for the default, and silently treating it as LSB-first would flip the bit
|
|
505
|
+
* numbering of a getter and its matching setter apart. */
|
|
345
506
|
static int
|
|
346
507
|
parse_bool_opt(VALUE opts, VALUE key, const char *name, int default_value)
|
|
347
508
|
{
|
|
348
509
|
if (NIL_P(opts)) return default_value;
|
|
349
|
-
VALUE value =
|
|
350
|
-
if (
|
|
510
|
+
VALUE value = rb_hash_lookup2(opts, key, Qundef);
|
|
511
|
+
if (value == Qundef) return default_value;
|
|
351
512
|
if (value == Qtrue) return 1;
|
|
352
513
|
if (value == Qfalse) return 0;
|
|
353
514
|
rb_raise(rb_eArgError, "%s must be true or false", name);
|
|
354
|
-
|
|
515
|
+
UNREACHABLE_RETURN(default_value);
|
|
355
516
|
}
|
|
356
517
|
|
|
357
518
|
static int
|
|
@@ -360,48 +521,49 @@ parse_lsb_first_opt(VALUE opts)
|
|
|
360
521
|
return parse_bool_opt(opts, sym_lsb_first, "lsb_first", 1);
|
|
361
522
|
}
|
|
362
523
|
|
|
363
|
-
/* Parse
|
|
364
|
-
*
|
|
524
|
+
/* Parse the optional start_offset positional argument of the iterators
|
|
525
|
+
* (Qnil => 0). A start past the end of the receiver is clamped to its bit
|
|
526
|
+
* length, where every emitter already stops immediately. */
|
|
365
527
|
static ssize_t
|
|
366
|
-
parse_start_offset(VALUE v)
|
|
528
|
+
parse_start_offset(VALUE self, VALUE v)
|
|
367
529
|
{
|
|
368
530
|
if (NIL_P(v)) return 0;
|
|
369
|
-
|
|
370
|
-
if (start_offset < 0)
|
|
371
|
-
rb_raise(rb_eIndexError, "bit_offset must be non-negative");
|
|
372
|
-
return start_offset;
|
|
531
|
+
return sb_narrow_bit_pos(sb_bit_offset(v), SB_BIT_LEN(RSTRING_LEN(self)));
|
|
373
532
|
}
|
|
374
533
|
|
|
375
534
|
/* read -------------------------------------------------------------------- */
|
|
376
535
|
|
|
377
|
-
/*
|
|
378
|
-
|
|
379
|
-
|
|
536
|
+
/* Shared body of bit_get and bit_set?.
|
|
537
|
+
* Returns the bit value (0 or 1), or -1 when the offset is out of range. */
|
|
538
|
+
static int
|
|
539
|
+
str_bit_read(int argc, VALUE *argv, VALUE self)
|
|
380
540
|
{
|
|
381
541
|
VALUE bit_offset_v, opts;
|
|
382
542
|
rb_scan_args(argc, argv, "1:", &bit_offset_v, &opts);
|
|
383
543
|
validate_option_hash(opts, SB_KW_LSB_FIRST);
|
|
544
|
+
int lsb_first = parse_lsb_first_opt(opts);
|
|
384
545
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
}
|
|
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");
|
|
391
|
-
}
|
|
392
|
-
ssize_t size = RSTRING_LEN(self) * 8;
|
|
393
|
-
if (size <= bit_offset) {
|
|
394
|
-
return Qnil;
|
|
395
|
-
}
|
|
546
|
+
ssize_t bit_offset = sb_resolve_bit_offset(self, bit_offset_v);
|
|
547
|
+
if (bit_offset < 0) return -1;
|
|
396
548
|
|
|
397
|
-
|
|
398
|
-
|
|
549
|
+
return test_bit(RSTRING_PTR(self), logical_to_physical(bit_offset, lsb_first));
|
|
550
|
+
}
|
|
399
551
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
552
|
+
/* Return 1/0/nil for the bit at flat position n. */
|
|
553
|
+
static VALUE
|
|
554
|
+
rb_str_bit_get(int argc, VALUE *argv, VALUE self)
|
|
555
|
+
{
|
|
556
|
+
int bit = str_bit_read(argc, argv, self);
|
|
557
|
+
return bit < 0 ? Qnil : INT2FIX(bit);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/* Return true/false/nil for the bit at flat position n. */
|
|
561
|
+
static VALUE
|
|
562
|
+
rb_str_bit_set_p(int argc, VALUE *argv, VALUE self)
|
|
563
|
+
{
|
|
564
|
+
int bit = str_bit_read(argc, argv, self);
|
|
565
|
+
if (bit < 0) return Qnil;
|
|
566
|
+
return bit ? Qtrue : Qfalse;
|
|
405
567
|
}
|
|
406
568
|
|
|
407
569
|
/* count_set_bits: popcount over a raw byte buffer.
|
|
@@ -457,9 +619,9 @@ count_set_bits_range(const unsigned char *str, ssize_t total_bytes,
|
|
|
457
619
|
ssize_t start, ssize_t length)
|
|
458
620
|
{
|
|
459
621
|
if (length <= 0) return 0;
|
|
460
|
-
|
|
622
|
+
int64_t total_bits = SB_BIT_LEN(total_bytes);
|
|
461
623
|
if (start >= total_bits) return 0;
|
|
462
|
-
if (start + length > total_bits) length = total_bits - start;
|
|
624
|
+
if (start + length > total_bits) length = (ssize_t)(total_bits - start);
|
|
463
625
|
|
|
464
626
|
ssize_t byte_start = start >> 3;
|
|
465
627
|
int bit_lo = (int)(start & 7);
|
|
@@ -494,9 +656,9 @@ count_set_bits_range_msb(const unsigned char *str, ssize_t total_bytes,
|
|
|
494
656
|
ssize_t start, ssize_t length)
|
|
495
657
|
{
|
|
496
658
|
if (length <= 0) return 0;
|
|
497
|
-
|
|
659
|
+
int64_t total_bits = SB_BIT_LEN(total_bytes);
|
|
498
660
|
if (start >= total_bits) return 0;
|
|
499
|
-
if (start + length > total_bits) length = total_bits - start;
|
|
661
|
+
if (start + length > total_bits) length = (ssize_t)(total_bits - start);
|
|
500
662
|
|
|
501
663
|
ssize_t byte_start = start >> 3;
|
|
502
664
|
int s_bit = (int)(start & 7); /* MSB-first within-byte start index */
|
|
@@ -543,30 +705,26 @@ rb_str_bit_count(int argc, VALUE *argv, VALUE self)
|
|
|
543
705
|
return SSIZET2NUM(count_set_bits(str, src_len));
|
|
544
706
|
|
|
545
707
|
int lsb_first = parse_lsb_first_opt(opts);
|
|
546
|
-
|
|
708
|
+
int64_t total_bits = SB_BIT_LEN(src_len);
|
|
547
709
|
ssize_t bit_offset, bit_length;
|
|
548
710
|
|
|
549
711
|
if (rb_obj_is_kind_of(v0, rb_cRange)) {
|
|
550
712
|
if (!NIL_P(v1))
|
|
551
713
|
rb_raise(rb_eArgError, "wrong number of arguments");
|
|
552
|
-
sb_range_validate_endpoints(v0);
|
|
553
714
|
ssize_t beg, len;
|
|
554
|
-
if (
|
|
715
|
+
if (sb_bit_range_beg_len(v0, &beg, &len, total_bits) == SB_RANGE_OUT)
|
|
555
716
|
return INT2FIX(0);
|
|
556
717
|
bit_offset = beg;
|
|
557
718
|
bit_length = len;
|
|
558
719
|
}
|
|
559
720
|
else if (!NIL_P(v1)) {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
bit_length = integer_to_bit_idx(v1);
|
|
568
|
-
if (bit_length < 0)
|
|
569
|
-
rb_raise(rb_eArgError, "bit_length must be non-negative");
|
|
721
|
+
uint64_t off = sb_bit_offset(v0);
|
|
722
|
+
uint64_t len = sb_bit_length(v1);
|
|
723
|
+
/* A region starting at or past the end holds no bits, and one that
|
|
724
|
+
* runs off the end contributes only the part that exists. */
|
|
725
|
+
if (off >= (uint64_t)total_bits) return INT2FIX(0);
|
|
726
|
+
bit_offset = sb_narrow_bit_pos(off, total_bits);
|
|
727
|
+
bit_length = sb_clamp_bit_length(len, bit_offset, total_bits);
|
|
570
728
|
}
|
|
571
729
|
else {
|
|
572
730
|
rb_raise(rb_eArgError,
|
|
@@ -590,7 +748,7 @@ rb_str_bit_count(int argc, VALUE *argv, VALUE self)
|
|
|
590
748
|
static void
|
|
591
749
|
emit_bits(const unsigned char *str, ssize_t len, int lsb_first, ssize_t start_offset, VALUE ary)
|
|
592
750
|
{
|
|
593
|
-
if (start_offset >= len
|
|
751
|
+
if (start_offset >= SB_BIT_LEN(len)) return;
|
|
594
752
|
|
|
595
753
|
#define SB_EMIT(v) \
|
|
596
754
|
do { VALUE _b = (v); \
|
|
@@ -629,7 +787,7 @@ rb_str_each_bit(int argc, VALUE *argv, VALUE self)
|
|
|
629
787
|
rb_scan_args(argc, argv, "01:", &start_offset_v, &opts);
|
|
630
788
|
validate_option_hash(opts, SB_KW_LSB_FIRST);
|
|
631
789
|
int lsb_first = parse_lsb_first_opt(opts);
|
|
632
|
-
ssize_t start_offset = parse_start_offset(start_offset_v);
|
|
790
|
+
ssize_t start_offset = parse_start_offset(self, start_offset_v);
|
|
633
791
|
|
|
634
792
|
emit_bits((const unsigned char *)RSTRING_PTR(self), RSTRING_LEN(self),
|
|
635
793
|
lsb_first, start_offset, Qnil);
|
|
@@ -643,7 +801,7 @@ rb_str_bits(int argc, VALUE *argv, VALUE self)
|
|
|
643
801
|
rb_scan_args(argc, argv, "01:", &start_offset_v, &opts);
|
|
644
802
|
validate_option_hash(opts, SB_KW_LSB_FIRST);
|
|
645
803
|
int lsb_first = parse_lsb_first_opt(opts);
|
|
646
|
-
ssize_t start_offset = parse_start_offset(start_offset_v);
|
|
804
|
+
ssize_t start_offset = parse_start_offset(self, start_offset_v);
|
|
647
805
|
ssize_t len = RSTRING_LEN(self);
|
|
648
806
|
const unsigned char *str = (const unsigned char *)RSTRING_PTR(self);
|
|
649
807
|
|
|
@@ -652,7 +810,8 @@ rb_str_bits(int argc, VALUE *argv, VALUE self)
|
|
|
652
810
|
return self;
|
|
653
811
|
}
|
|
654
812
|
|
|
655
|
-
|
|
813
|
+
int64_t total_bits = SB_BIT_LEN(len);
|
|
814
|
+
ssize_t nbits = (start_offset >= total_bits) ? 0 : (ssize_t)(total_bits - start_offset);
|
|
656
815
|
VALUE ary = rb_ary_new_capa(nbits);
|
|
657
816
|
emit_bits(str, len, lsb_first, start_offset, ary);
|
|
658
817
|
return ary;
|
|
@@ -690,7 +849,7 @@ static void
|
|
|
690
849
|
emit_bit_offsets(const unsigned char *str, ssize_t len, int target, int lsb_first,
|
|
691
850
|
ssize_t start_offset, VALUE ary)
|
|
692
851
|
{
|
|
693
|
-
if (start_offset >= len
|
|
852
|
+
if (start_offset >= SB_BIT_LEN(len)) return;
|
|
694
853
|
|
|
695
854
|
#define SB_EMIT(pos_val) \
|
|
696
855
|
do { VALUE _p = (pos_val); \
|
|
@@ -775,7 +934,7 @@ rb_str_each_bit_offset(int argc, VALUE *argv, VALUE self)
|
|
|
775
934
|
validate_option_hash(opts, SB_KW_LSB_FIRST);
|
|
776
935
|
int lsb_first = parse_lsb_first_opt(opts);
|
|
777
936
|
int target = parse_bit_target(bit_val);
|
|
778
|
-
ssize_t start_offset = parse_start_offset(start_offset_v);
|
|
937
|
+
ssize_t start_offset = parse_start_offset(self, start_offset_v);
|
|
779
938
|
|
|
780
939
|
emit_bit_offsets((const unsigned char *)RSTRING_PTR(self), RSTRING_LEN(self),
|
|
781
940
|
target, lsb_first, start_offset, Qnil);
|
|
@@ -790,7 +949,7 @@ rb_str_bit_offsets(int argc, VALUE *argv, VALUE self)
|
|
|
790
949
|
validate_option_hash(opts, SB_KW_LSB_FIRST);
|
|
791
950
|
int lsb_first = parse_lsb_first_opt(opts);
|
|
792
951
|
int target = parse_bit_target(bit_val);
|
|
793
|
-
ssize_t start_offset = parse_start_offset(start_offset_v);
|
|
952
|
+
ssize_t start_offset = parse_start_offset(self, start_offset_v);
|
|
794
953
|
|
|
795
954
|
ssize_t len = RSTRING_LEN(self);
|
|
796
955
|
const unsigned char *str = (const unsigned char *)RSTRING_PTR(self);
|
|
@@ -803,7 +962,7 @@ rb_str_bit_offsets(int argc, VALUE *argv, VALUE self)
|
|
|
803
962
|
/* Pre-size the Array using popcount to avoid repeated reallocation.
|
|
804
963
|
* For target=0 the expected count is (len * 8 - popcount). */
|
|
805
964
|
ssize_t set_count = count_set_bits(str, len);
|
|
806
|
-
ssize_t count = (target == 1) ? set_count : (len
|
|
965
|
+
ssize_t count = (target == 1) ? set_count : (ssize_t)(SB_BIT_LEN(len) - set_count);
|
|
807
966
|
VALUE ary = rb_ary_new_capa(count);
|
|
808
967
|
emit_bit_offsets(str, len, target, lsb_first, start_offset, ary);
|
|
809
968
|
return ary;
|
|
@@ -908,7 +1067,7 @@ static VALUE
|
|
|
908
1067
|
rb_str_bit_slice(int argc, VALUE *argv, VALUE self)
|
|
909
1068
|
{
|
|
910
1069
|
ssize_t src_len = RSTRING_LEN(self);
|
|
911
|
-
|
|
1070
|
+
int64_t total_bits = SB_BIT_LEN(src_len);
|
|
912
1071
|
ssize_t bit_offset, bit_length;
|
|
913
1072
|
VALUE v0, v1, opts;
|
|
914
1073
|
int n_pos = rb_scan_args(argc, argv, "11:", &v0, &v1, &opts);
|
|
@@ -916,23 +1075,21 @@ rb_str_bit_slice(int argc, VALUE *argv, VALUE self)
|
|
|
916
1075
|
int lsb_first = parse_lsb_first_opt(opts);
|
|
917
1076
|
|
|
918
1077
|
if (n_pos == 1 && rb_obj_is_kind_of(v0, rb_cRange)) {
|
|
919
|
-
sb_range_validate_endpoints(v0);
|
|
920
1078
|
ssize_t beg, len;
|
|
921
|
-
if (
|
|
1079
|
+
if (sb_bit_range_beg_len(v0, &beg, &len, total_bits) == SB_RANGE_OUT) {
|
|
922
1080
|
return Qnil;
|
|
923
1081
|
}
|
|
924
1082
|
bit_offset = beg;
|
|
925
1083
|
bit_length = len;
|
|
926
1084
|
}
|
|
927
1085
|
else if (n_pos == 2) {
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
1086
|
+
uint64_t off, len;
|
|
1087
|
+
if (sb_bit_position_soft(v0, &off) < 0) return Qnil;
|
|
1088
|
+
if (sb_bit_position_soft(v1, &len) < 0) return Qnil;
|
|
1089
|
+
if (off > (uint64_t)total_bits) return Qnil;
|
|
931
1090
|
|
|
932
|
-
bit_offset =
|
|
933
|
-
bit_length =
|
|
934
|
-
|
|
935
|
-
if (bit_offset < 0 || bit_length < 0) return Qnil;
|
|
1091
|
+
bit_offset = sb_narrow_bit_pos(off, total_bits);
|
|
1092
|
+
bit_length = sb_clamp_bit_length(len, bit_offset, total_bits);
|
|
936
1093
|
}
|
|
937
1094
|
else if (n_pos == 1) {
|
|
938
1095
|
return Qnil;
|
|
@@ -943,15 +1100,18 @@ rb_str_bit_slice(int argc, VALUE *argv, VALUE self)
|
|
|
943
1100
|
}
|
|
944
1101
|
|
|
945
1102
|
if (bit_offset > total_bits) return Qnil;
|
|
946
|
-
|
|
947
|
-
if (bit_length > available) bit_length = available;
|
|
1103
|
+
int64_t available = total_bits - bit_offset;
|
|
1104
|
+
if (bit_length > available) bit_length = (ssize_t)available;
|
|
948
1105
|
|
|
949
1106
|
if (bit_length == 0) return rb_str_new("", 0);
|
|
950
1107
|
|
|
951
1108
|
ssize_t out_bytes = (bit_length + 7) / 8;
|
|
952
1109
|
VALUE result = rb_str_buf_new(out_bytes);
|
|
953
1110
|
rb_str_resize(result, out_bytes);
|
|
954
|
-
|
|
1111
|
+
/* A bit-aligned slice is a repacked byte sequence, not a substring, so the
|
|
1112
|
+
* result carries BINARY like every other String returned by this API. */
|
|
1113
|
+
rb_enc_associate(result, rb_ascii8bit_encoding());
|
|
1114
|
+
ENC_CODERANGE_CLEAR(result);
|
|
955
1115
|
unsigned char *dst = (unsigned char *)RSTRING_PTR(result);
|
|
956
1116
|
const unsigned char *src = (const unsigned char *)RSTRING_PTR(self);
|
|
957
1117
|
|
|
@@ -997,88 +1157,68 @@ rb_str_mutate_bits(int argc, VALUE *argv, VALUE self, enum sb_mutation_op op)
|
|
|
997
1157
|
validate_option_hash(opts, SB_KW_LSB_FIRST);
|
|
998
1158
|
int lsb_first = parse_lsb_first_opt(opts);
|
|
999
1159
|
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
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
|
-
}
|
|
1160
|
+
unsigned char *ptr;
|
|
1161
|
+
|
|
1162
|
+
/* Single-bit form: bit_set(n). The offset is validated before
|
|
1163
|
+
* rb_str_modify, so an out-of-range offset raises IndexError even on a
|
|
1164
|
+
* frozen receiver, matching the order of the core implementation. */
|
|
1165
|
+
if (NIL_P(bit_length_v) && !rb_obj_is_kind_of(target, rb_cRange)) {
|
|
1166
|
+
ssize_t idx = check_bit_index(self, target, lsb_first);
|
|
1167
|
+
rb_str_modify(self);
|
|
1168
|
+
ptr = (unsigned char *)RSTRING_PTR(self);
|
|
1169
|
+
unsigned char mask = (unsigned char)(1u << (idx % 8));
|
|
1170
|
+
switch (op) {
|
|
1171
|
+
case SB_MUT_SET: ptr[idx / 8] |= mask; break;
|
|
1172
|
+
case SB_MUT_CLEAR: ptr[idx / 8] &= (unsigned char)~mask; break;
|
|
1173
|
+
case SB_MUT_FLIP: ptr[idx / 8] ^= mask; break;
|
|
1036
1174
|
}
|
|
1037
1175
|
return self;
|
|
1038
1176
|
}
|
|
1039
1177
|
|
|
1040
|
-
|
|
1041
|
-
|
|
1178
|
+
int64_t total_bits = SB_BIT_LEN(RSTRING_LEN(self));
|
|
1179
|
+
ssize_t bit_offset, bit_length;
|
|
1042
1180
|
|
|
1043
1181
|
if (rb_obj_is_kind_of(target, rb_cRange)) {
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
ssize_t beg, len;
|
|
1182
|
+
if (!NIL_P(bit_length_v))
|
|
1183
|
+
rb_raise(rb_eArgError, "wrong number of arguments");
|
|
1047
1184
|
|
|
1048
|
-
/*
|
|
1049
|
-
*
|
|
1050
|
-
if (
|
|
1185
|
+
/* A range that begins past the end has no bits to write, which is an
|
|
1186
|
+
* error for a mutation, not a silent no-op. */
|
|
1187
|
+
if (sb_bit_range_beg_len(target, &bit_offset, &bit_length, total_bits) == SB_RANGE_OUT)
|
|
1051
1188
|
rb_raise(rb_eIndexError, "bit range out of range");
|
|
1052
|
-
}
|
|
1053
1189
|
|
|
1054
|
-
/*
|
|
1055
|
-
*
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1190
|
+
/* sb_bit_range_beg_len clamps the end, so a range that overruns the
|
|
1191
|
+
* string comes back longer than the bits available. Writing is not
|
|
1192
|
+
* allowed to silently shrink, so report the overrun instead. */
|
|
1193
|
+
if ((int64_t)bit_offset + (int64_t)bit_length > total_bits)
|
|
1194
|
+
rb_raise(rb_eIndexError, "bit range out of range");
|
|
1195
|
+
}
|
|
1196
|
+
else {
|
|
1197
|
+
/* 2-arg form: bit_set(bit_offset, bit_length) */
|
|
1198
|
+
uint64_t off = sb_bit_offset(target);
|
|
1199
|
+
uint64_t len = sb_bit_length(bit_length_v);
|
|
1200
|
+
if (len == 0) return self;
|
|
1201
|
+
if (off >= (uint64_t)total_bits || len > (uint64_t)total_bits - off)
|
|
1202
|
+
rb_raise(rb_eIndexError, "bit range out of range");
|
|
1203
|
+
bit_offset = sb_narrow_bit_pos(off, total_bits);
|
|
1204
|
+
if (len > (uint64_t)(sb_addressable_bits(total_bits) - bit_offset))
|
|
1205
|
+
rb_raise(rb_eArgError, "bit index out of representable range");
|
|
1206
|
+
bit_length = (ssize_t)len;
|
|
1207
|
+
}
|
|
1067
1208
|
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1209
|
+
rb_str_modify(self);
|
|
1210
|
+
ptr = (unsigned char *)RSTRING_PTR(self);
|
|
1211
|
+
|
|
1212
|
+
for (ssize_t logical = bit_offset; logical < bit_offset + bit_length; logical++) {
|
|
1213
|
+
ssize_t idx = logical_to_physical(logical, lsb_first);
|
|
1214
|
+
unsigned char mask = (unsigned char)(1u << (idx % 8));
|
|
1215
|
+
switch (op) {
|
|
1216
|
+
case SB_MUT_SET: ptr[idx / 8] |= mask; break;
|
|
1217
|
+
case SB_MUT_CLEAR: ptr[idx / 8] &= (unsigned char)~mask; break;
|
|
1218
|
+
case SB_MUT_FLIP: ptr[idx / 8] ^= mask; break;
|
|
1076
1219
|
}
|
|
1077
|
-
return self;
|
|
1078
1220
|
}
|
|
1079
|
-
|
|
1080
|
-
rb_raise(rb_eTypeError, "bit index must be an integer or Range");
|
|
1081
|
-
UNREACHABLE_RETURN(Qnil);
|
|
1221
|
+
return self;
|
|
1082
1222
|
}
|
|
1083
1223
|
|
|
1084
1224
|
static VALUE
|
|
@@ -1110,13 +1250,20 @@ check_binary_op_lengths(VALUE self, VALUE other)
|
|
|
1110
1250
|
}
|
|
1111
1251
|
}
|
|
1112
1252
|
|
|
1253
|
+
/* Allocate the destination buffer of a non-destructive bitwise operation.
|
|
1254
|
+
*
|
|
1255
|
+
* Bit operations read the receiver as a raw byte sequence, so the bytes they
|
|
1256
|
+
* produce need not be valid in the receiver's encoding; the result is always
|
|
1257
|
+
* BINARY. The destructive (`!`) variants leave the receiver's encoding alone
|
|
1258
|
+
* instead, which is what String#setbyte does for the same reason. */
|
|
1113
1259
|
static VALUE
|
|
1114
1260
|
alloc_result(VALUE self)
|
|
1115
1261
|
{
|
|
1116
1262
|
ssize_t len = RSTRING_LEN(self);
|
|
1117
1263
|
VALUE result = rb_str_buf_new(len);
|
|
1118
1264
|
rb_str_resize(result, len);
|
|
1119
|
-
rb_enc_associate(result,
|
|
1265
|
+
rb_enc_associate(result, rb_ascii8bit_encoding());
|
|
1266
|
+
ENC_CODERANGE_CLEAR(result);
|
|
1120
1267
|
return result;
|
|
1121
1268
|
}
|
|
1122
1269
|
|
|
@@ -1231,6 +1378,7 @@ SB_DEFINE_BINARY_KERNEL(kern_xor, SB_XOR_WORD, SB_XOR_BYTE)
|
|
|
1231
1378
|
static VALUE \
|
|
1232
1379
|
rb_str_bitwise_##op_name(VALUE self, VALUE other) \
|
|
1233
1380
|
{ \
|
|
1381
|
+
StringValue(other); \
|
|
1234
1382
|
check_binary_op_lengths(self, other); \
|
|
1235
1383
|
ssize_t len = RSTRING_LEN(self); \
|
|
1236
1384
|
VALUE result = alloc_result(self); \
|
|
@@ -1242,6 +1390,7 @@ SB_DEFINE_BINARY_KERNEL(kern_xor, SB_XOR_WORD, SB_XOR_BYTE)
|
|
|
1242
1390
|
static VALUE \
|
|
1243
1391
|
rb_str_bitwise_##op_name##_bang(VALUE self, VALUE other) \
|
|
1244
1392
|
{ \
|
|
1393
|
+
StringValue(other); \
|
|
1245
1394
|
check_binary_op_lengths(self, other); \
|
|
1246
1395
|
rb_str_modify(self); \
|
|
1247
1396
|
ssize_t len = RSTRING_LEN(self); \
|
|
@@ -1336,7 +1485,7 @@ rb_str_each_bit_field(int argc, VALUE *argv, VALUE self)
|
|
|
1336
1485
|
rb_raise(rb_eArgError, "bitlen must be positive");
|
|
1337
1486
|
}
|
|
1338
1487
|
if (bl > 64) {
|
|
1339
|
-
rb_raise(rb_eArgError, "bitlen must be <= 64 (got %
|
|
1488
|
+
rb_raise(rb_eArgError, "bitlen must be <= 64 (got %" PRIdPTR ")", (intptr_t)bl);
|
|
1340
1489
|
}
|
|
1341
1490
|
bitlens[f] = bl;
|
|
1342
1491
|
step += bl;
|
|
@@ -1345,8 +1494,8 @@ rb_str_each_bit_field(int argc, VALUE *argv, VALUE self)
|
|
|
1345
1494
|
int lsb_first = parse_lsb_first_opt(opts);
|
|
1346
1495
|
|
|
1347
1496
|
ssize_t src_len = RSTRING_LEN(self);
|
|
1348
|
-
|
|
1349
|
-
ssize_t iterations = total_bits / step;
|
|
1497
|
+
int64_t total_bits = SB_BIT_LEN(src_len);
|
|
1498
|
+
ssize_t iterations = (ssize_t)(total_bits / step);
|
|
1350
1499
|
|
|
1351
1500
|
VALUE *field_vals = ALLOCA_N(VALUE, num_fields);
|
|
1352
1501
|
|
|
@@ -1390,7 +1539,7 @@ rb_str_bit_fields(int argc, VALUE *argv, VALUE self)
|
|
|
1390
1539
|
rb_raise(rb_eArgError, "bitlen must be positive");
|
|
1391
1540
|
}
|
|
1392
1541
|
if (bl > 64) {
|
|
1393
|
-
rb_raise(rb_eArgError, "bitlen must be <= 64 (got %
|
|
1542
|
+
rb_raise(rb_eArgError, "bitlen must be <= 64 (got %" PRIdPTR ")", (intptr_t)bl);
|
|
1394
1543
|
}
|
|
1395
1544
|
bitlens[f] = bl;
|
|
1396
1545
|
step += bl;
|
|
@@ -1399,8 +1548,8 @@ rb_str_bit_fields(int argc, VALUE *argv, VALUE self)
|
|
|
1399
1548
|
int lsb_first = parse_lsb_first_opt(opts);
|
|
1400
1549
|
|
|
1401
1550
|
ssize_t src_len = RSTRING_LEN(self);
|
|
1402
|
-
|
|
1403
|
-
ssize_t iterations = total_bits / step;
|
|
1551
|
+
int64_t total_bits = SB_BIT_LEN(src_len);
|
|
1552
|
+
ssize_t iterations = (ssize_t)(total_bits / step);
|
|
1404
1553
|
|
|
1405
1554
|
int have_block = rb_block_given_p();
|
|
1406
1555
|
VALUE result = have_block ? Qnil : rb_ary_new_capa(iterations);
|
|
@@ -1438,13 +1587,13 @@ rb_str_bit_fields(int argc, VALUE *argv, VALUE self)
|
|
|
1438
1587
|
* - remaining bytes: ctz on the inverted byte
|
|
1439
1588
|
*
|
|
1440
1589
|
* Porting to Ruby Core:
|
|
1441
|
-
* 1. Move to string.c alongside
|
|
1590
|
+
* 1. Move to string.c alongside bit_get and each_bit.
|
|
1442
1591
|
* 2. Share sb_ctz8 / sb_ctzll with the existing set-bit helpers.
|
|
1443
1592
|
*/
|
|
1444
1593
|
static ssize_t
|
|
1445
1594
|
count_run_lsb(const unsigned char *src, ssize_t src_len, ssize_t bit_offset, int target)
|
|
1446
1595
|
{
|
|
1447
|
-
|
|
1596
|
+
int64_t max_run = SB_BIT_LEN(src_len) - bit_offset;
|
|
1448
1597
|
ssize_t byte_idx = bit_offset >> 3;
|
|
1449
1598
|
int bit_off = bit_offset & 7;
|
|
1450
1599
|
ssize_t count = 0;
|
|
@@ -1461,7 +1610,7 @@ count_run_lsb(const unsigned char *src, ssize_t src_len, ssize_t bit_offset, int
|
|
|
1461
1610
|
count += run;
|
|
1462
1611
|
byte_idx++;
|
|
1463
1612
|
if (run < remaining)
|
|
1464
|
-
return count < max_run ? count : max_run;
|
|
1613
|
+
return (ssize_t)(count < max_run ? count : max_run);
|
|
1465
1614
|
}
|
|
1466
1615
|
|
|
1467
1616
|
#if SB_LITTLE_ENDIAN
|
|
@@ -1475,7 +1624,7 @@ count_run_lsb(const unsigned char *src, ssize_t src_len, ssize_t bit_offset, int
|
|
|
1475
1624
|
byte_idx += 8;
|
|
1476
1625
|
} else {
|
|
1477
1626
|
count += sb_ctzll(~word);
|
|
1478
|
-
return count < max_run ? count : max_run;
|
|
1627
|
+
return (ssize_t)(count < max_run ? count : max_run);
|
|
1479
1628
|
}
|
|
1480
1629
|
}
|
|
1481
1630
|
#endif
|
|
@@ -1490,11 +1639,11 @@ count_run_lsb(const unsigned char *src, ssize_t src_len, ssize_t bit_offset, int
|
|
|
1490
1639
|
byte_idx++;
|
|
1491
1640
|
} else {
|
|
1492
1641
|
count += sb_ctz8(~b);
|
|
1493
|
-
return count < max_run ? count : max_run;
|
|
1642
|
+
return (ssize_t)(count < max_run ? count : max_run);
|
|
1494
1643
|
}
|
|
1495
1644
|
}
|
|
1496
1645
|
|
|
1497
|
-
return count < max_run ? count : max_run;
|
|
1646
|
+
return (ssize_t)(count < max_run ? count : max_run);
|
|
1498
1647
|
}
|
|
1499
1648
|
|
|
1500
1649
|
/* Return the length of the consecutive run of `bit` starting at pos, or nil. */
|
|
@@ -1510,9 +1659,15 @@ rb_str_bit_run_count(int argc, VALUE *argv, VALUE self)
|
|
|
1510
1659
|
rb_raise(rb_eTypeError, "position must be an integer");
|
|
1511
1660
|
}
|
|
1512
1661
|
int target = parse_bit_target(bit_val);
|
|
1513
|
-
ssize_t bit_offset = integer_to_bit_idx(bit_offset_v);
|
|
1514
1662
|
ssize_t src_len = RSTRING_LEN(self);
|
|
1515
|
-
|
|
1663
|
+
/* A position with no run starting at it and a position outside the string
|
|
1664
|
+
* are the same answer here -- nil -- so a negative or past-the-end offset
|
|
1665
|
+
* is not an error (see ProposedMethods.md, "Why bit_run_count never
|
|
1666
|
+
* returns 0"). */
|
|
1667
|
+
uint64_t off;
|
|
1668
|
+
if (sb_bit_position_soft(bit_offset_v, &off) < 0) return Qnil;
|
|
1669
|
+
if (off >= (uint64_t)SB_BIT_LEN(src_len)) return Qnil;
|
|
1670
|
+
ssize_t bit_offset = (ssize_t)off;
|
|
1516
1671
|
|
|
1517
1672
|
const unsigned char *src = (const unsigned char *)RSTRING_PTR(self);
|
|
1518
1673
|
if (lsb_first) {
|
|
@@ -1523,7 +1678,7 @@ rb_str_bit_run_count(int argc, VALUE *argv, VALUE self)
|
|
|
1523
1678
|
if (logical_get_bit(src, bit_offset, 0) != target) return Qnil;
|
|
1524
1679
|
|
|
1525
1680
|
ssize_t run = 1;
|
|
1526
|
-
|
|
1681
|
+
int64_t total_bits = SB_BIT_LEN(src_len);
|
|
1527
1682
|
while (bit_offset + run < total_bits && logical_get_bit(src, bit_offset + run, 0) == target) {
|
|
1528
1683
|
run++;
|
|
1529
1684
|
}
|
|
@@ -1545,8 +1700,8 @@ static void
|
|
|
1545
1700
|
emit_bit_runs(VALUE self, int lsb_first, ssize_t start_offset, VALUE ary)
|
|
1546
1701
|
{
|
|
1547
1702
|
ssize_t src_len = RSTRING_LEN(self);
|
|
1548
|
-
|
|
1549
|
-
|
|
1703
|
+
int64_t total_bits = SB_BIT_LEN(src_len);
|
|
1704
|
+
if (src_len == 0 || start_offset >= total_bits) return;
|
|
1550
1705
|
ssize_t offset = start_offset;
|
|
1551
1706
|
|
|
1552
1707
|
#define SB_EMIT_TRIPLE(bval, oval, lval) \
|
|
@@ -1587,7 +1742,7 @@ rb_str_each_bit_run(int argc, VALUE *argv, VALUE self)
|
|
|
1587
1742
|
rb_scan_args(argc, argv, "01:", &start_offset_v, &opts);
|
|
1588
1743
|
validate_option_hash(opts, SB_KW_LSB_FIRST);
|
|
1589
1744
|
int lsb_first = parse_lsb_first_opt(opts);
|
|
1590
|
-
ssize_t start_offset = parse_start_offset(start_offset_v);
|
|
1745
|
+
ssize_t start_offset = parse_start_offset(self, start_offset_v);
|
|
1591
1746
|
|
|
1592
1747
|
emit_bit_runs(self, lsb_first, start_offset, Qnil);
|
|
1593
1748
|
return self;
|
|
@@ -1601,7 +1756,7 @@ rb_str_bit_runs(int argc, VALUE *argv, VALUE self)
|
|
|
1601
1756
|
rb_scan_args(argc, argv, "01:", &start_offset_v, &opts);
|
|
1602
1757
|
validate_option_hash(opts, SB_KW_LSB_FIRST);
|
|
1603
1758
|
int lsb_first = parse_lsb_first_opt(opts);
|
|
1604
|
-
ssize_t start_offset = parse_start_offset(start_offset_v);
|
|
1759
|
+
ssize_t start_offset = parse_start_offset(self, start_offset_v);
|
|
1605
1760
|
|
|
1606
1761
|
if (rb_block_given_p()) {
|
|
1607
1762
|
emit_bit_runs(self, lsb_first, start_offset, Qnil);
|
|
@@ -1618,52 +1773,47 @@ static VALUE
|
|
|
1618
1773
|
rb_str_bit_splice(int argc, VALUE *argv, VALUE self)
|
|
1619
1774
|
{
|
|
1620
1775
|
ssize_t dst_bit_off, dst_bit_len;
|
|
1621
|
-
ssize_t src_bit_off
|
|
1776
|
+
ssize_t src_bit_off;
|
|
1622
1777
|
VALUE str;
|
|
1623
|
-
|
|
1778
|
+
int64_t dst_total = SB_BIT_LEN(RSTRING_LEN(self));
|
|
1624
1779
|
VALUE v0, v1, v2, v3, opts;
|
|
1625
1780
|
|
|
1626
1781
|
int n_pos = rb_scan_args(argc, argv, "22:", &v0, &v1, &v2, &v3, &opts);
|
|
1627
1782
|
validate_option_hash(opts, SB_KW_LSB_FIRST);
|
|
1628
1783
|
int lsb_first = parse_lsb_first_opt(opts);
|
|
1629
1784
|
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
sb_range_beg_len(v0, &beg, &len, dst_total, 1);
|
|
1635
|
-
dst_bit_off = beg;
|
|
1636
|
-
dst_bit_len = len;
|
|
1637
|
-
str = v1;
|
|
1638
|
-
Check_Type(str, T_STRING);
|
|
1639
|
-
src_bit_off = 0;
|
|
1640
|
-
src_bit_len = dst_bit_len;
|
|
1641
|
-
}
|
|
1642
|
-
else if (n_pos == 3 && rb_obj_is_kind_of(v0, rb_cRange)) {
|
|
1643
|
-
/* bit_splice(range, str, str_bit_index) */
|
|
1644
|
-
sb_range_validate_endpoints(v0);
|
|
1785
|
+
uint64_t dst_off_u, dst_len_u, src_off_u, src_len_u;
|
|
1786
|
+
|
|
1787
|
+
if (n_pos <= 3 && rb_obj_is_kind_of(v0, rb_cRange)) {
|
|
1788
|
+
/* bit_splice(range, str) and bit_splice(range, str, str_bit_index) */
|
|
1645
1789
|
ssize_t beg, len;
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1790
|
+
if (sb_bit_range_beg_len(v0, &beg, &len, dst_total) == SB_RANGE_OUT) {
|
|
1791
|
+
rb_raise(rb_eIndexError,
|
|
1792
|
+
"bit_splice: destination range starts past the end "
|
|
1793
|
+
"(total %" PRId64 " bits)", (int64_t)dst_total);
|
|
1794
|
+
}
|
|
1795
|
+
dst_off_u = (uint64_t)beg;
|
|
1796
|
+
dst_len_u = (uint64_t)len;
|
|
1649
1797
|
str = v1;
|
|
1650
1798
|
Check_Type(str, T_STRING);
|
|
1651
|
-
if (
|
|
1652
|
-
|
|
1799
|
+
if (n_pos == 3) {
|
|
1800
|
+
if (!rb_integer_type_p(v2)) {
|
|
1801
|
+
rb_raise(rb_eTypeError, "third argument must be an Integer");
|
|
1802
|
+
}
|
|
1803
|
+
src_off_u = sb_bit_offset(v2);
|
|
1653
1804
|
}
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1805
|
+
else {
|
|
1806
|
+
src_off_u = 0;
|
|
1807
|
+
}
|
|
1808
|
+
src_len_u = dst_len_u;
|
|
1658
1809
|
}
|
|
1659
1810
|
else if (n_pos == 3) {
|
|
1660
1811
|
/* bit_splice(bit_index, bit_length, str) */
|
|
1661
1812
|
if (!rb_integer_type_p(v0) || !rb_integer_type_p(v1)) {
|
|
1662
1813
|
rb_raise(rb_eTypeError, "bit index and length must be integers");
|
|
1663
1814
|
}
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
if (dst_bit_off < 0) dst_bit_off += dst_total;
|
|
1815
|
+
dst_off_u = sb_bit_offset(v0);
|
|
1816
|
+
dst_len_u = sb_bit_length(v1);
|
|
1667
1817
|
|
|
1668
1818
|
/*
|
|
1669
1819
|
* Integer source support was prototyped here, but it is intentionally
|
|
@@ -1677,40 +1827,56 @@ rb_str_bit_splice(int argc, VALUE *argv, VALUE self)
|
|
|
1677
1827
|
|
|
1678
1828
|
str = v2;
|
|
1679
1829
|
Check_Type(str, T_STRING);
|
|
1680
|
-
|
|
1681
|
-
|
|
1830
|
+
src_off_u = 0;
|
|
1831
|
+
src_len_u = dst_len_u;
|
|
1682
1832
|
}
|
|
1683
1833
|
else if (n_pos == 4) {
|
|
1684
1834
|
/* bit_splice(bit_index, bit_length, str, str_bit_index) */
|
|
1685
1835
|
if (!rb_integer_type_p(v0) || !rb_integer_type_p(v1) || !rb_integer_type_p(v3)) {
|
|
1686
1836
|
rb_raise(rb_eTypeError, "bit indices and lengths must be integers");
|
|
1687
1837
|
}
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
if (dst_bit_off < 0) dst_bit_off += dst_total;
|
|
1838
|
+
dst_off_u = sb_bit_offset(v0);
|
|
1839
|
+
dst_len_u = sb_bit_length(v1);
|
|
1691
1840
|
str = v2;
|
|
1692
1841
|
Check_Type(str, T_STRING);
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
if (src_bit_off < 0) src_bit_off += src_total;
|
|
1696
|
-
src_bit_len = dst_bit_len;
|
|
1842
|
+
src_off_u = sb_bit_offset(v3);
|
|
1843
|
+
src_len_u = dst_len_u;
|
|
1697
1844
|
}
|
|
1698
1845
|
else {
|
|
1699
1846
|
rb_raise(rb_eArgError,
|
|
1700
1847
|
"wrong number of arguments (given %d, expected 2, 3, or 4)", n_pos);
|
|
1701
1848
|
}
|
|
1702
1849
|
|
|
1703
|
-
|
|
1850
|
+
/* Both ranges are checked in 64 bits: a position past the end of the
|
|
1851
|
+
* string is well-formed input here, so it must reach this bounds check
|
|
1852
|
+
* rather than being rejected while it is parsed. */
|
|
1853
|
+
if (dst_off_u > (uint64_t)dst_total || dst_len_u > (uint64_t)dst_total - dst_off_u) {
|
|
1704
1854
|
rb_raise(rb_eIndexError,
|
|
1705
|
-
"bit_splice: destination range [%
|
|
1706
|
-
|
|
1855
|
+
"bit_splice: destination range [%" PRIu64 ", %" PRIu64
|
|
1856
|
+
"] out of bounds (total %" PRId64 " bits)",
|
|
1857
|
+
dst_off_u, dst_len_u, (int64_t)dst_total);
|
|
1707
1858
|
}
|
|
1708
1859
|
|
|
1709
|
-
|
|
1710
|
-
if (
|
|
1860
|
+
int64_t src_total_bits = SB_BIT_LEN(RSTRING_LEN(str));
|
|
1861
|
+
if (src_off_u > (uint64_t)src_total_bits ||
|
|
1862
|
+
src_len_u > (uint64_t)src_total_bits - src_off_u) {
|
|
1711
1863
|
rb_raise(rb_eIndexError,
|
|
1712
|
-
"bit_splice: source range [%
|
|
1713
|
-
|
|
1864
|
+
"bit_splice: source range [%" PRIu64 ", %" PRIu64
|
|
1865
|
+
"] out of bounds (total %" PRId64 " bits)",
|
|
1866
|
+
src_off_u, src_len_u, (int64_t)src_total_bits);
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
/* Both ranges are inside their strings now, so narrowing only fails on an
|
|
1870
|
+
* ILP32 build whose bit length exceeds the internal index (see
|
|
1871
|
+
* sb_narrow_bit_pos). */
|
|
1872
|
+
dst_bit_off = sb_narrow_bit_pos(dst_off_u, dst_total);
|
|
1873
|
+
dst_bit_len = (ssize_t)dst_len_u;
|
|
1874
|
+
if (dst_len_u > (uint64_t)(sb_addressable_bits(dst_total) - dst_bit_off)) {
|
|
1875
|
+
rb_raise(rb_eArgError, "bit index out of representable range");
|
|
1876
|
+
}
|
|
1877
|
+
src_bit_off = sb_narrow_bit_pos(src_off_u, src_total_bits);
|
|
1878
|
+
if (src_len_u > (uint64_t)(sb_addressable_bits(src_total_bits) - src_bit_off)) {
|
|
1879
|
+
rb_raise(rb_eArgError, "bit index out of representable range");
|
|
1714
1880
|
}
|
|
1715
1881
|
|
|
1716
1882
|
if (dst_bit_len == 0) return self;
|
|
@@ -1840,8 +2006,9 @@ rb_ary_mask(int argc, VALUE *argv, VALUE self)
|
|
|
1840
2006
|
ssize_t needed = (ary_len + 7) >> 3;
|
|
1841
2007
|
if (needed > bmp_len)
|
|
1842
2008
|
rb_raise(rb_eArgError,
|
|
1843
|
-
"bitmap too short: need %
|
|
1844
|
-
|
|
2009
|
+
"bitmap too short: need %" PRIdPTR " bytes for %" PRIdPTR
|
|
2010
|
+
" elements, got %" PRIdPTR,
|
|
2011
|
+
(intptr_t)needed, (intptr_t)ary_len, (intptr_t)bmp_len);
|
|
1845
2012
|
|
|
1846
2013
|
if (!lsb_first) {
|
|
1847
2014
|
for (ssize_t i = 0; i < ary_len; i++) {
|
|
@@ -1885,8 +2052,9 @@ rb_ary_mask_bang(int argc, VALUE *argv, VALUE self)
|
|
|
1885
2052
|
ssize_t needed = (ary_len + 7) >> 3;
|
|
1886
2053
|
if (needed > bmp_len)
|
|
1887
2054
|
rb_raise(rb_eArgError,
|
|
1888
|
-
"bitmap too short: need %
|
|
1889
|
-
|
|
2055
|
+
"bitmap too short: need %" PRIdPTR " bytes for %" PRIdPTR
|
|
2056
|
+
" elements, got %" PRIdPTR,
|
|
2057
|
+
(intptr_t)needed, (intptr_t)ary_len, (intptr_t)bmp_len);
|
|
1890
2058
|
|
|
1891
2059
|
if (!lsb_first) {
|
|
1892
2060
|
for (ssize_t i = 0; i < ary_len; i++) {
|
|
@@ -1916,7 +2084,8 @@ Init_string_bits(void)
|
|
|
1916
2084
|
sym_lsb_first = ID2SYM(rb_intern("lsb_first"));
|
|
1917
2085
|
sym_invert = ID2SYM(rb_intern("invert"));
|
|
1918
2086
|
|
|
1919
|
-
rb_define_method(rb_cString, "
|
|
2087
|
+
rb_define_method(rb_cString, "bit_get", rb_str_bit_get, -1);
|
|
2088
|
+
rb_define_method(rb_cString, "bit_set?", rb_str_bit_set_p, -1);
|
|
1920
2089
|
rb_define_method(rb_cString, "bit_count", rb_str_bit_count, -1);
|
|
1921
2090
|
rb_define_method(rb_cString, "each_bit", rb_str_each_bit, -1);
|
|
1922
2091
|
rb_define_method(rb_cString, "bits", rb_str_bits, -1);
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: string_bits
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- HASUMI Hitoshi
|
|
@@ -91,6 +91,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
91
91
|
- - ">="
|
|
92
92
|
- !ruby/object:Gem::Version
|
|
93
93
|
version: '3.0'
|
|
94
|
+
- - "<"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: 4.1.dev
|
|
94
97
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
98
|
requirements:
|
|
96
99
|
- - ">="
|