diff --git a/README.markdown b/README.markdown index 78633d1..a4dd399 100644 --- a/README.markdown +++ b/README.markdown @@ -14,7 +14,7 @@ Usually you want to do this: But if you want the latest patches or want to work on it yourself, you may want to do this: - git clone git://github.com/tyler/bitset.git + git clone git://github.com/ericboesch/bitset.git cd bitset rake build gem install pkg/bitset-.gem @@ -35,53 +35,137 @@ We can also create a bitset based on a string of ones and zeros. >> Bitset.from_s('00010001') => 00010001 +or from an array. Falsey values (false and nil) are converted to +zeroes; all other values, including 0 and "", are converted to ones. + + >> Bitset.new [false, nil, 3, 0] + => 0011 + +To input an array of ones and zeroes: + + >> Bitset.new([0,1,1,0].map(&:positive?)) + => 0110 + Obviously you can also set and clear bits... >> bitset = Bitset.new(8) => 00000000 - + >> bitset[3] = true => 00010000 - + >> bitset[3] = false => 00000000 - + >> bitset.set(1, 3, 5, 7) => 01010101 - + >> bitset.clear(1, 5) => 00010001 +Arrays of Integers can also be passed to #clear and #set (c/o brendon9x). + The point of a bitset is to be, effectively, an array of single bits. It should support basic set and bitwise operations. So, let's look at a few of those. >> a = Bitset.from_s('00001111') => 00001111 - + >> b = Bitset.from_s('01010101') => 01010101 - + >> a & b => 00000101 - + >> a | b => 01011111 - + >> b - a => 01010000 - + >> a ^ b => 01011010 - + >> ~a => 11110000 - + >> a.hamming(b) => 4 - + >> a.cardinality => 4 + >> a.reverse + => 11110000 + + # Tell whether all of the given bit numbers are set + >> a.set? 6 + => true + + # Return a new Bitset composed of bits #1, #3, #5, #4, and #1 + # again. Unlike Array#values_at, this function currently only + # accepts an array of Fixnums as its argument. + >> a.values_at [1,3,5,4,1] + => 00110 + + # Tell whether all of the given bit numbers are clear + >> a.clear? 1,3,5 + => false + + # Tell whether all bits are clear + >> a.empty? + => false + + # Pass all bits to the block + >> b.each { |v| puts v } + => false + true + false + ... + + # Pass the positions of all set bits to the block + >> b.each_set { |bit| puts bit } + => 1 + 3 + 5 + 7 + + # Return an array of the positions of all set bits + >> b.each_set # AKA b.to_a + => [1, 3, 5, 7] + + # b.each_set(index) == b.each_set[index], but faster. + >> b.each_set(-3) # Negative index wraps around. + => 3 + + # b.each_set(index, len) == b.each_set[index, len], but faster. + >> b.each_set(2,2) # Block is also allowed + => [5,7] + + + # The following methods modify a Bitset in place very quickly: + >> a.intersect!(b) # like a &= b + >> a.union!(b) # like a |= b + >> a.difference!(b) # like a -= b + >> a.xor!(b) # like a ^= b + >> a.reset! # Zeroes all bits + + # Above, "like" does not mean "identical to." a |= b creates a new + # Bitset object. a.union!(b) changes an existing object which + # affects all variables that point to the same object. + + # Attempting to apply bitwise binary operators or their in-place + # equivalents between bitsets of different sizes will raise an + # ArgumentError. + + >> b.to_binary_array + => [0, 1, 0, 1, 0, 1, 0, 1] + + # b.dup and b.clone are also available. + + # Marshal.dump and Marshal.load are also supported. If you want to + # save a few bytes and don't need Marshal.load to work, you can + # use #pack and Bitset.unpack instead. Contributing ------------ @@ -98,4 +182,4 @@ License See LICENSE.txt. -### Thanks for using Bitset! \ No newline at end of file +### Thanks for using Bitset! diff --git a/Rakefile b/Rakefile index c172a75..c7b99ca 100644 --- a/Rakefile +++ b/Rakefile @@ -12,17 +12,19 @@ task :default => :spec require 'jeweler' Jeweler::Tasks.new do |gem| gem.name = "bitset" - gem.homepage = "http://github.com/tyler/bitset" + gem.homepage = "http://github.com/ericboesch/bitset" gem.license = "MIT" gem.summary = 'Bitset implementation.' - gem.description = 'A fast C-based Bitset. It supports the standard set operations as well as operations you may expect on bit arrays. (popcount, for instance)' - gem.email = "tbmcmullen@gmail.com" + gem.description = 'A fast C-based Bitset. It supports the standard set operations as well as operations you may expect on bit arrays,such as popcount.' + gem.email = "eric.boesch@nist.gov" gem.authors = ["Tyler McMullen"] + # Other significant contributions from Eric Boesch, Gabriel Formica, and Brendon McLean. + end Jeweler::RubygemsDotOrgTasks.new -require 'rake/rdoctask' -Rake::RDocTask.new do |rdoc| +require 'rdoc/task' +RDoc::Task.new do |rdoc| version = File.exist?('VERSION') ? File.read('VERSION') : "" rdoc.rdoc_dir = 'rdoc' diff --git a/VERSION b/VERSION index 6c6aa7c..26aaba0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0 \ No newline at end of file +1.2.0 diff --git a/bitset.gemspec b/bitset.gemspec index 8827a36..0c7ba2c 100644 --- a/bitset.gemspec +++ b/bitset.gemspec @@ -2,48 +2,40 @@ # DO NOT EDIT THIS FILE DIRECTLY # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' # -*- encoding: utf-8 -*- +# stub: bitset 1.2.0 ruby lib +# stub: ext/bitset/extconf.rb Gem::Specification.new do |s| - s.name = %q{bitset} - s.version = "0.1.0" + s.name = "bitset".freeze + s.version = "1.2.0" - s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= - s.authors = ["Tyler McMullen"] - s.date = %q{2011-03-03} - s.description = %q{A fast C-based Bitset. It supports the standard set operations as well as operations you may expect on bit arrays. (popcount, for instance)} - s.email = %q{tbmcmullen@gmail.com} - s.extensions = ["ext/bitset/extconf.rb"] + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.require_paths = ["lib".freeze] + s.authors = ["Tyler McMullen".freeze] + s.date = "2018-05-01" + s.description = "A fast C-based Bitset. It supports the standard set operations as well as operations you may expect on bit arrays,such as popcount.".freeze + s.email = "eric.boesch@nist.gov".freeze + s.extensions = ["ext/bitset/extconf.rb".freeze] s.extra_rdoc_files = [ "LICENSE.txt", - "README.rdoc" + "README.markdown" ] s.files = [ "LICENSE.txt", - "README.rdoc", + "README.markdown", "Rakefile", "VERSION", "bitset.gemspec", "ext/bitset/bitset.c", + "ext/bitset/builtin.h", + "ext/bitset/exact-int.h", "ext/bitset/extconf.rb", + "lib/bitset.rb", "spec/bitset_spec.rb" ] - s.homepage = %q{http://github.com/tyler/bitset} - s.licenses = ["MIT"] - s.require_paths = ["lib"] - s.rubygems_version = %q{1.3.7} - s.summary = %q{Bitset implementation.} - s.test_files = [ - "spec/bitset_spec.rb" - ] - - if s.respond_to? :specification_version then - current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION - s.specification_version = 3 - - if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then - else - end - else - end + s.homepage = "http://github.com/ericboesch/bitset".freeze + s.licenses = ["MIT".freeze] + s.rubygems_version = "2.6.14".freeze + s.summary = "Bitset implementation.".freeze end diff --git a/ext/bitset/bitset.c b/ext/bitset/bitset.c index ed94a8e..3a5c128 100644 --- a/ext/bitset/bitset.c +++ b/ext/bitset/bitset.c @@ -1,8 +1,11 @@ #include "ruby.h" +#include "builtin.h" #include +#include #include #include +#include VALUE cBitset; @@ -11,16 +14,26 @@ typedef struct { uint64_t * data; } Bitset; -#define BYTES(_bs) (((_bs->len-1) >> 3) + 1) -#define INTS(_bs) (((_bs->len-1) >> 6) + 1) +// Avoid using (len-1), just in case len == 0. +#define BYTES(_bs) (((_bs)->len+7) >> 3) +#define INTS(_bs) (((_bs)->len+63) >> 6) + // 2^6=64 + +#define _bit_no(bit) ((bit) & 0x3f) +#define _bit_segment(bit) ((bit) >> 6) +#define _bit_mask(bit) (((uint64_t) 1) << _bit_no(bit)) +#define _seg_no_to_bit_no(seg_no) ((seg_no) << 6) +#define _get_bit(bs, idx) ((bs)->data[_bit_segment(idx)] & _bit_mask(idx)) +#define _set_bit(bs, idx) ((bs)->data[_bit_segment(idx)] |= _bit_mask(idx)) +#define _clear_bit(bs, idx) ((bs)->data[_bit_segment(idx)] &= ~_bit_mask(idx)) Bitset * bitset_new() { - return (Bitset *) malloc(sizeof(Bitset)); + return (Bitset *) calloc(1, sizeof(Bitset)); } void bitset_setup(Bitset * bs, int len) { bs->len = len; - bs->data = (uint64_t *) calloc((((bs->len-1) >> 6) + 1), sizeof(uint64_t)); // 2^6=64 + bs->data = (uint64_t *) calloc(INTS(bs), sizeof(uint64_t)); } void bitset_free(Bitset * bs) { @@ -30,7 +43,7 @@ void bitset_free(Bitset * bs) { } -Bitset * get_bitset(VALUE obj) { +static Bitset * get_bitset(VALUE obj) { Bitset * bs; Data_Get_Struct(obj, Bitset, bs); return bs; @@ -42,9 +55,23 @@ static VALUE rb_bitset_alloc(VALUE klass) { return obj; } -static VALUE rb_bitset_initialize(VALUE self, VALUE len) { +static VALUE rb_bitset_initialize(VALUE self, VALUE ary) { Bitset * bs = get_bitset(self); - bitset_setup(bs, NUM2INT(len)); + if (RB_TYPE_P(ary, T_ARRAY)) { + int i; + int len = (int) RARRAY_LEN(ary); + bitset_setup(bs, len); + for (i = 0; i < len; ++i) { + // This could be more efficient, but if you're converting + // from a Ruby array of bits, you're not looking + // at blazing speed anyhow. + if (RTEST(rb_ary_entry(ary, i))) { + _set_bit(bs, i); + } + } + } else { + bitset_setup(bs, NUM2INT(ary)); + } return self; } @@ -53,44 +80,35 @@ static VALUE rb_bitset_size(VALUE self, VALUE len) { return INT2NUM(bs->len); } -void raise_index_error() { +static void raise_index_error() { VALUE rb_eIndexError = rb_const_get(rb_cObject, rb_intern("IndexError")); rb_raise(rb_eIndexError, "Index out of bounds"); } -#define _bit_segment(bit) ((bit) >> 6UL) -#define _bit_mask(bit) (((uint64_t) 1) << ((bit) & 0x3f)) - -void validate_index(Bitset * bs, int idx) { +static void validate_index(Bitset * bs, int idx) { if(idx < 0 || idx >= bs->len) raise_index_error(); } -uint64_t get_bit(Bitset * bs, int idx) { - uint64_t segment = bs->data[_bit_segment(idx)]; - return segment & _bit_mask(idx); -} - -void set_bit(Bitset * bs, int idx) { - bs->data[_bit_segment(idx)] |= _bit_mask(idx); -} - -void clear_bit(Bitset * bs, int idx) { - bs->data[_bit_segment(idx)] &= ~_bit_mask(idx); +static void verify_equal_size(Bitset * bs1, Bitset * bs2) { + if (bs1->len != bs2->len) { + VALUE rb_eArgumentError = rb_const_get(rb_cObject, rb_intern("ArgumentError")); + rb_raise(rb_eArgumentError, "Operands size mismatch"); + } } void assign_bit(Bitset * bs, int idx, VALUE value) { if(NIL_P(value) || value == Qfalse) - clear_bit(bs, idx); + _clear_bit(bs, idx); else - set_bit(bs, idx); + _set_bit(bs, idx); } static VALUE rb_bitset_aref(VALUE self, VALUE index) { Bitset * bs = get_bitset(self); int idx = NUM2INT(index); validate_index(bs, idx); - return get_bit(bs, idx) > 0 ? Qtrue : Qfalse; + return _get_bit(bs, idx) > 0 ? Qtrue : Qfalse; } static VALUE rb_bitset_aset(VALUE self, VALUE index, VALUE value) { @@ -104,11 +122,21 @@ static VALUE rb_bitset_aset(VALUE self, VALUE index, VALUE value) { static VALUE rb_bitset_set(int argc, VALUE * argv, VALUE self) { int i; Bitset * bs = get_bitset(self); - for(i = 0; i < argc; i++) { - VALUE index = argv[i]; - int idx = NUM2INT(index); - validate_index(bs, idx); - set_bit(bs, idx); + + if (argc == 1 && rb_obj_is_kind_of(argv[0], rb_const_get(rb_cObject, rb_intern("Array")))) { + for(i = 0; i < RARRAY_LEN(argv[0]); i++) { + VALUE index = RARRAY_PTR(argv[0])[i]; + int idx = NUM2INT(index); + validate_index(bs, idx); + _set_bit(bs, idx); + } + } else { + for(i = 0; i < argc; i++) { + VALUE index = argv[i]; + int idx = NUM2INT(index); + validate_index(bs, idx); + _set_bit(bs, idx); + } } return Qtrue; } @@ -116,11 +144,21 @@ static VALUE rb_bitset_set(int argc, VALUE * argv, VALUE self) { static VALUE rb_bitset_clear(int argc, VALUE * argv, VALUE self) { int i; Bitset * bs = get_bitset(self); - for(i = 0; i < argc; i++) { - VALUE index = argv[i]; - int idx = NUM2INT(index); - validate_index(bs, idx); - clear_bit(bs, idx); + + if (argc == 1 && rb_obj_is_kind_of(argv[0], rb_const_get(rb_cObject, rb_intern("Array")))) { + for(i = 0; i < RARRAY_LEN(argv[0]); i++) { + VALUE index = RARRAY_PTR(argv[0])[i]; + int idx = NUM2INT(index); + validate_index(bs, idx); + _clear_bit(bs, idx); + } + } else { + for(i = 0; i < argc; i++) { + VALUE index = argv[i]; + int idx = NUM2INT(index); + validate_index(bs, idx); + _clear_bit(bs, idx); + } } return Qtrue; } @@ -132,7 +170,7 @@ static VALUE rb_bitset_clear_p(int argc, VALUE * argv, VALUE self) { VALUE index = argv[i]; int idx = NUM2INT(index); validate_index(bs, idx); - if(get_bit(bs, idx) > 0) + if(_get_bit(bs, idx) > 0) return Qfalse; } return Qtrue; @@ -145,35 +183,38 @@ static VALUE rb_bitset_set_p(int argc, VALUE * argv, VALUE self) { VALUE index = argv[i]; int idx = NUM2INT(index); validate_index(bs, idx); - if(get_bit(bs, idx) == 0) + if(_get_bit(bs, idx) == 0) return Qfalse; } return Qtrue; } -static VALUE rb_bitset_cardinality(VALUE self) { - Bitset * bs = get_bitset(self); +static int cardinality(Bitset * bs) { int i; - int max = ((bs->len-1) >> 6) + 1; + int max = INTS(bs); int count = 0; for(i = 0; i < max; i++) { - uint64_t segment = bs->data[i]; - if(i+1 == max) - segment &= ((((uint64_t) 1) << (bs->len & 0x3F)) - 1); - count += __builtin_popcountll(segment); + count += psnip_builtin_popcount64(bs->data[i]); } - return INT2NUM(count); + return count; +} + +static VALUE rb_bitset_cardinality(VALUE self) { + Bitset * bs = get_bitset(self); + return INT2NUM(cardinality(bs)); } static VALUE rb_bitset_intersect(VALUE self, VALUE other) { Bitset * bs = get_bitset(self); Bitset * other_bs = get_bitset(other); + Bitset * new_bs; + int max = INTS(bs); + int i; - Bitset * new_bs = bitset_new(); + verify_equal_size(bs, other_bs); + new_bs = bitset_new(); bitset_setup(new_bs, bs->len); - int max = ((bs->len-1) >> 6) + 1; - int i; for(i = 0; i < max; i++) { uint64_t segment = bs->data[i]; uint64_t other_segment = other_bs->data[i]; @@ -186,12 +227,14 @@ static VALUE rb_bitset_intersect(VALUE self, VALUE other) { static VALUE rb_bitset_union(VALUE self, VALUE other) { Bitset * bs = get_bitset(self); Bitset * other_bs = get_bitset(other); + Bitset * new_bs; + int max = INTS(bs); + int i; - Bitset * new_bs = bitset_new(); + verify_equal_size(bs, other_bs); + new_bs = bitset_new(); bitset_setup(new_bs, bs->len); - int max = ((bs->len-1) >> 6) + 1; - int i; for(i = 0; i < max; i++) { uint64_t segment = bs->data[i]; uint64_t other_segment = other_bs->data[i]; @@ -204,12 +247,14 @@ static VALUE rb_bitset_union(VALUE self, VALUE other) { static VALUE rb_bitset_difference(VALUE self, VALUE other) { Bitset * bs = get_bitset(self); Bitset * other_bs = get_bitset(other); + Bitset * new_bs; + int max = INTS(bs); + int i; - Bitset * new_bs = bitset_new(); + verify_equal_size(bs, other_bs); + new_bs = bitset_new(); bitset_setup(new_bs, bs->len); - int max = ((bs->len-1) >> 6) + 1; - int i; for(i = 0; i < max; i++) { uint64_t segment = bs->data[i]; uint64_t other_segment = other_bs->data[i]; @@ -222,12 +267,14 @@ static VALUE rb_bitset_difference(VALUE self, VALUE other) { static VALUE rb_bitset_xor(VALUE self, VALUE other) { Bitset * bs = get_bitset(self); Bitset * other_bs = get_bitset(other); + Bitset * new_bs; + int max = INTS(bs); + int i; - Bitset * new_bs = bitset_new(); + verify_equal_size(bs, other_bs); + new_bs = bitset_new(); bitset_setup(new_bs, bs->len); - int max = ((bs->len-1) >> 6) + 1; - int i; for(i = 0; i < max; i++) { uint64_t segment = bs->data[i]; uint64_t other_segment = other_bs->data[i]; @@ -239,16 +286,17 @@ static VALUE rb_bitset_xor(VALUE self, VALUE other) { static VALUE rb_bitset_not(VALUE self) { Bitset * bs = get_bitset(self); - Bitset * new_bs = bitset_new(); - bitset_setup(new_bs, bs->len); - - int max = ((bs->len-1) >> 6) + 1; + int max = INTS(bs); int i; + + bitset_setup(new_bs, bs->len); for(i = 0; i < max; i++) { uint64_t segment = bs->data[i]; new_bs->data[i] = ~segment; } + if(_bit_no(bs->len) != 0) + new_bs->data[max-1] &= _bit_mask(bs->len) - 1; return Data_Wrap_Struct(cBitset, 0, bitset_free, new_bs); } @@ -259,7 +307,7 @@ static VALUE rb_bitset_to_s(VALUE self) { int i; char * data = malloc(bs->len + 1); for(i = 0; i < bs->len; i++) { - data[i] = get_bit(bs, i) ? '1' : '0'; + data[i] = _get_bit(bs, i) ? '1' : '0'; } data[bs->len] = 0; @@ -269,14 +317,14 @@ static VALUE rb_bitset_to_s(VALUE self) { static VALUE rb_bitset_from_s(VALUE self, VALUE s) { int length = RSTRING_LEN(s); char* data = StringValuePtr(s); - Bitset * new_bs = bitset_new(); + int i; + bitset_setup(new_bs, length); - int i; for (i = 0; i < length; i++) { if (data[i] == '1') { - set_bit(new_bs, i); + _set_bit(new_bs, i); } } @@ -287,13 +335,13 @@ static VALUE rb_bitset_hamming(VALUE self, VALUE other) { Bitset * bs = get_bitset(self); Bitset * other_bs = get_bitset(other); - int max = ((bs->len-1) >> 6) + 1; + int max = INTS(bs); int count = 0; int i; for(i = 0; i < max; i++) { uint64_t segment = bs->data[i]; uint64_t other_segment = other_bs->data[i]; - count += __builtin_popcountll(segment ^ other_segment); + count += psnip_builtin_popcount64(segment ^ other_segment); } return INT2NUM(count); @@ -304,7 +352,7 @@ static VALUE rb_bitset_each(VALUE self) { int i; for(i = 0; i < bs->len; i++) { - rb_yield(get_bit(bs, i) > 0 ? Qtrue : Qfalse); + rb_yield(_get_bit(bs, i) ? Qtrue : Qfalse); } return self; @@ -313,7 +361,7 @@ static VALUE rb_bitset_each(VALUE self) { static VALUE rb_bitset_marshall_dump(VALUE self) { Bitset * bs = get_bitset(self); VALUE hash = rb_hash_new(); - VALUE data = rb_str_new(bs->data, BYTES(bs)); + VALUE data = rb_str_new((const char *) bs->data, BYTES(bs)); rb_hash_aset(hash, ID2SYM(rb_intern("len")), UINT2NUM(bs->len)); rb_hash_aset(hash, ID2SYM(rb_intern("data")), data); @@ -335,11 +383,225 @@ static VALUE rb_bitset_marshall_load(VALUE self, VALUE hash) { return Qnil; } +static VALUE rb_bitset_to_binary_array(VALUE self) { + Bitset * bs = get_bitset(self); + int i; + + VALUE array = rb_ary_new2(bs->len / 2); + for(i = 0; i < bs->len; i++) { + rb_ary_push(array, INT2NUM(_get_bit(bs, i) > 0 ? 1 : 0)); + } + + return array; +} + +static VALUE rb_bitset_dup(VALUE self) { + Bitset * bs = get_bitset(self); + int max = INTS(bs); + + Bitset * new_bs = bitset_new(); + bitset_setup(new_bs, bs->len); + + memcpy(new_bs->data, bs->data, max * sizeof(bs->data[0])); + return Data_Wrap_Struct(cBitset, 0, bitset_free, new_bs); +} + +/* Yield the bit numbers of each set bit in sequence to a block. If + there is no block, return an array of those numbers. */ +static VALUE rb_bitset_each_set(int argc, VALUE * argv, VALUE self) { + Bitset * bs = get_bitset(self); + int seg_no; + int max = INTS(bs); + uint64_t* seg_ptr = bs->data; + int block_p = rb_block_given_p(); + VALUE ary = Qnil; + int set_bit_no = -1; + + /* If there is one argument, it is an index into the notional + output array, and return an int. If there are two arguments, + return up to arguments where is the second argument. */ + int min_set_bit_no = (argc > 0) ? NUM2INT(argv[0]) : 0; + int max_set_bit_no; + + if (argc > 2) { + VALUE error = rb_const_get(rb_cObject, rb_intern("ArgumentError")); + rb_raise(error, "wrong number of arguments (given %d, expected 0..2)", + argc); + } + + if (min_set_bit_no < 0) { + /* Convert negative numbers into offsets from the end of the array. */ + min_set_bit_no = cardinality(bs) + min_set_bit_no; + } + + max_set_bit_no = (argc == 0) + ? INT_MAX + : (argc == 1) + ? (min_set_bit_no + 1) + : (min_set_bit_no + NUM2INT(argv[1])); + + if (min_set_bit_no < 0 || max_set_bit_no < min_set_bit_no) + return Qnil; + + if (argc != 1 && !block_p) { + ary = rb_ary_new(); + } + if (min_set_bit_no < 0 || max_set_bit_no < min_set_bit_no) + return Qnil; + + for (seg_no = 0; seg_no < max; ++seg_no, ++seg_ptr) { + uint64_t segment = *seg_ptr; + int bit_position = 0; + bool finished = false; + while (segment) { + VALUE v; + + if (!(segment & 1)) { + int shift = psnip_builtin_ctz64(segment); + bit_position += shift; + segment >>= shift; + } + v = INT2NUM(_seg_no_to_bit_no(seg_no) + bit_position); + ++bit_position; + segment >>= 1; + ++set_bit_no; + if (set_bit_no < min_set_bit_no) { + continue; + } + if (set_bit_no >= max_set_bit_no) { + finished = true; + break; + } + if (block_p) { + rb_yield(v); + } else if (argc == 1) { + return v; + } else { + rb_ary_push(ary, v); + } + } + if (finished) { + break; + } + } + + return block_p ? self : ary; +} + +static VALUE rb_bitset_empty_p(VALUE self) { + Bitset * bs = get_bitset(self); + int seg_no; + int max = INTS(bs); + uint64_t* seg_ptr = bs->data; + + for (seg_no = 0; seg_no < max; ++seg_no, ++seg_ptr) { + if (*seg_ptr) { + return Qfalse; + } + } + return Qtrue; +} + +static VALUE rb_bitset_values_at(VALUE self, VALUE index_array) { + int i; + Bitset * bs = get_bitset(self); + int blen = bs->len; + int alen = RARRAY_LEN(index_array); + VALUE *ptr = RARRAY_PTR(index_array); + Bitset * new_bs = bitset_new(); + bitset_setup(new_bs, alen); + for (i = 0; i < alen; ++i) { + int idx = NUM2INT(ptr[i]); + if (idx >= 0 && idx < blen && _get_bit(bs, idx)) { + _set_bit(new_bs, i); + } + } + + return Data_Wrap_Struct(cBitset, 0, bitset_free, new_bs); +} + +/** This could run a bit faster if you worked at it. */ +static VALUE rb_bitset_reverse(VALUE self, VALUE index_array) { + int i; + Bitset * bs = get_bitset(self); + int len = bs->len; + Bitset * new_bs = bitset_new(); + bitset_setup(new_bs, len); + for (i = 0; i < len; ++i) { + if (_get_bit(bs, i)) { + _set_bit(new_bs, len - i - 1); + } + } + + return Data_Wrap_Struct(cBitset, 0, bitset_free, new_bs); +} + +static VALUE rb_bitset_equal(VALUE self, VALUE other) { + int i; + Bitset * bs = get_bitset(self); + Bitset * other_bs = get_bitset(other); + int max = INTS(bs); + + if (bs->len != other_bs->len) + return Qfalse; + for(i = 0; i < max; i++) { + if (bs->data[i] != other_bs->data[i]) { + return Qfalse; + } + } + return Qtrue; +} + +typedef uint64_t (*bitwise_op)(uint64_t, uint64_t); +inline uint64_t and(uint64_t a, uint64_t b) { return a & b; } +inline uint64_t or(uint64_t a, uint64_t b) { return a | b; } +inline uint64_t xor(uint64_t a, uint64_t b) { return a ^ b; } +inline uint64_t difference(uint64_t a, uint64_t b) { return a & ~b; } + +static VALUE mutable(VALUE self, VALUE other, bitwise_op operator) { + Bitset * bs = get_bitset(self); + Bitset * other_bs = get_bitset(other); + int max = INTS(bs); + int i; + verify_equal_size(bs, other_bs); + + for(i = 0; i < max; i++) { + uint64_t segment = bs->data[i]; + uint64_t other_segment = other_bs->data[i]; + bs->data[i] = operator(segment, other_segment); + } + + return self; +} + +static VALUE rb_bitset_union_mutable(VALUE self, VALUE other) { + return mutable(self, other, &or); +} + +static VALUE rb_bitset_intersect_mutable(VALUE self, VALUE other) { + return mutable(self, other, &and); +} + +static VALUE rb_bitset_xor_mutable(VALUE self, VALUE other) { + return mutable(self, other, &xor); +} + +static VALUE rb_bitset_difference_mutable(VALUE self, VALUE other) { + return mutable(self, other, &difference); +} + +static VALUE rb_bitset_reset(VALUE self) { + Bitset * bs = get_bitset(self); + memset(bs->data, 0, (INTS(bs) * sizeof(uint64_t)) ); + return self; +} + void Init_bitset() { cBitset = rb_define_class("Bitset", rb_cObject); rb_include_module(cBitset, rb_mEnumerable); rb_define_alloc_func(cBitset, rb_bitset_alloc); rb_define_method(cBitset, "initialize", rb_bitset_initialize, 1); + rb_define_method(cBitset, "reset!", rb_bitset_reset, 0); rb_define_method(cBitset, "size", rb_bitset_size, 0); rb_define_method(cBitset, "[]", rb_bitset_aref, 1); rb_define_method(cBitset, "[]=", rb_bitset_aset, 2); @@ -350,13 +612,22 @@ void Init_bitset() { rb_define_method(cBitset, "cardinality", rb_bitset_cardinality, 0); rb_define_method(cBitset, "intersect", rb_bitset_intersect, 1); rb_define_alias(cBitset, "&", "intersect"); + rb_define_alias(cBitset, "and", "intersect"); + rb_define_method(cBitset, "intersect!", rb_bitset_intersect_mutable, 1); + rb_define_alias(cBitset, "and!", "intersect!"); rb_define_method(cBitset, "union", rb_bitset_union, 1); rb_define_alias(cBitset, "|", "union"); + rb_define_alias(cBitset, "or", "union"); + rb_define_method(cBitset, "union!", rb_bitset_union_mutable, 1); + rb_define_alias(cBitset, "or!", "union!"); rb_define_method(cBitset, "difference", rb_bitset_difference, 1); rb_define_alias(cBitset, "-", "difference"); + rb_define_method(cBitset, "difference!", rb_bitset_difference_mutable, 1); rb_define_method(cBitset, "xor", rb_bitset_xor, 1); rb_define_alias(cBitset, "^", "xor"); rb_define_alias(cBitset, "symmetric_difference", "xor"); + rb_define_method(cBitset, "xor!", rb_bitset_xor_mutable, 1); + rb_define_alias(cBitset, "symmetric_difference!", "xor!"); rb_define_method(cBitset, "not", rb_bitset_not, 0); rb_define_alias(cBitset, "~", "not"); rb_define_method(cBitset, "hamming", rb_bitset_hamming, 1); @@ -365,4 +636,16 @@ void Init_bitset() { rb_define_singleton_method(cBitset, "from_s", rb_bitset_from_s, 1); rb_define_method(cBitset, "marshal_dump", rb_bitset_marshall_dump, 0); rb_define_method(cBitset, "marshal_load", rb_bitset_marshall_load, 1); + rb_define_method(cBitset, "to_binary_array", rb_bitset_to_binary_array, 0); + rb_define_method(cBitset, "dup", rb_bitset_dup, 0); + rb_define_alias(cBitset, "clone", "dup"); + rb_define_method(cBitset, "each_set", rb_bitset_each_set, -1); + rb_define_alias(cBitset, "to_a", "each_set"); + /* #each_set allows an optional block, and #to_a normally doesn't. + But an alias is simpler than having two different functions. */ + rb_define_method(cBitset, "empty?", rb_bitset_empty_p, 0); + rb_define_method(cBitset, "values_at", rb_bitset_values_at, 1); + rb_define_alias(cBitset, "select_bits", "values_at"); + rb_define_method(cBitset, "reverse", rb_bitset_reverse, 0); + rb_define_method(cBitset, "==", rb_bitset_equal, 1); } diff --git a/ext/bitset/builtin.h b/ext/bitset/builtin.h new file mode 100644 index 0000000..36831a5 --- /dev/null +++ b/ext/bitset/builtin.h @@ -0,0 +1,1390 @@ +/* Builtins and Intrinsics + * Portable Snippets - https://gitub.com/nemequ/portable-snippets + * Created by Evan Nemerson + * + * To the extent possible under law, the authors have waived all + * copyright and related or neighboring rights to this code. For + * details, see the Creative Commons Zero 1.0 Universal license at + * https://creativecommons.org/publicdomain/zero/1.0/ + * + * Some of these implementations are based on code from + * https://graphics.stanford.edu/~seander/bithacks.html which is also + * public domain (and a fantastic web site). + */ + +#if !defined(PSNIP_BUILTIN_H) +#define PSNIP_BUILTIN_H + +#if defined(HEDLEY_GCC_HAS_BUILTIN) +# define PSNIP_BUILTIN_GNU_HAS_BUILTIN(builtin,major,minor) HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,0) +#elif defined(__clang__) && defined(__has_builtin) +# define PSNIP_BUILTIN_GNU_HAS_BUILTIN(builtin,major,minor) __has_builtin(builtin) +#elif defined(__GNUC__) +# define PSNIP_BUILTIN_GNU_HAS_BUILTIN(builtin,major,minor) (__GNUC__ > major || (major == __GNUC__ && __GNUC_MINOR__ >= minor)) +#else +# define PSNIP_BUILTIN_GNU_HAS_BUILTIN(builtin,major,minor) (0) +#endif + +#if defined(HEDLEY_CLANG_HAS_BUILTIN) +# define PSNIP_BUILTIN_CLANG_HAS_BUILTIN(builtin) HEDLEY_CLANG_HAS_BUILTIN(builtin) +#elif defined(__has_builtin) +# define PSNIP_BUILTIN_CLANG_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else +# define PSNIP_BUILTIN_CLANG_HAS_BUILTIN(builtin) (0) +#endif + +#if defined(HEDLEY_MSVC_VERSION_CHECK) +# define PSNIP_BUILTIN_MSVC_HAS_INTRIN(intrin,major,minor) HEDLEY_MSVC_VERSION_CHECK(major,minor,0) +#elif !defined(_MSC_VER) +# define PSNIP_BUILTIN_MSVC_HAS_INTRIN(intrin,major,minor) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) +# define PSNIP_BUILTIN_MSVC_HAS_INTRIN(intrin,major,minor) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) +# define PSNIP_BUILTIN_MSVC_HAS_INTRIN(intrin,major,minor) (_MSC_FULL_VER >= ((major * 100000) + (minor * 1000))) +#else +# define PSNIP_BUILTIN_MSVC_HAS_INTRIN(intrin,major,minor) (_MSC_VER >= ((major * 100) + (minor))) +#endif + +#if defined(_MSC_VER) +# include +#endif +#include +#include + +#if defined(__i386) || defined(_M_IX86) || \ + defined(__amd64) || defined(_M_AMD64) || defined(__x86_64) +# if defined(_MSC_VER) +# define PSNIP_BUILTIN__ENABLE_X86 +# elif defined(__GNUC__) +# define PSNIP_BUILTIN__ENABLE_X86 +# include +# endif +#endif + +#if defined(__amd64) || defined(_M_AMD64) || defined(__x86_64) +# if defined(_MSC_VER) +# define PSNIP_BUILTIN__ENABLE_AMD64 +# elif defined(__GNUC__) +# define PSNIP_BUILTIN__ENABLE_AMD64 +# include +# endif +#endif + +#if \ + !defined(psnip_int64_t) || !defined(psnip_uint64_t) || \ + !defined(psnip_int32_t) || !defined(psnip_uint32_t) || \ + !defined(psnip_int16_t) || !defined(psnip_uint16_t) || \ + !defined(psnip_int8_t) || !defined(psnip_uint8_t) +# include "exact-int.h" +#endif + +#if defined(HEDLEY_LIKELY) && defined(HEDLEY_UNLIKELY) +# define PSNIP_BUILTIN_LIKELY(expr) HEDLEY_LIKELY(expr) +# define PSNIP_BUILTIN_UNLIKELY(expr) HEDLEY_UNLIKELY(expr) +#elif PSNIP_BUILTIN_GNU_HAS_BUILTIN(__builtin_expect,3,0) +# define PSNIP_BUILTIN_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define PSNIP_BUILTIN_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +# define PSNIP_BUILTIN_LIKELY(expr) (!!(expr)) +# define PSNIP_BUILTIN_UNLIKELY(expr) (!!(expr)) +#endif + +#if !defined(PSNIP_BUILTIN_STATIC_INLINE) +# if defined(__GNUC__) +# define PSNIP_BUILTIN__COMPILER_ATTRIBUTES __attribute__((__unused__)) +# else +# define PSNIP_BUILTIN__COMPILER_ATTRIBUTES +# endif + +# if defined(HEDLEY_INLINE) +# define PSNIP_BUILTIN__INLINE HEDLEY_INLINE +# elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +# define PSNIP_BUILTIN__INLINE inline +# elif defined(__GNUC_STDC_INLINE__) +# define PSNIP_BUILTIN__INLINE __inline__ +# elif defined(_MSC_VER) && _MSC_VER >= 1200 +# define PSNIP_BUILTIN__INLINE __inline +# else +# define PSNIP_BUILTIN__INLINE +# endif + +# define PSNIP_BUILTIN__FUNCTION PSNIP_BUILTIN__COMPILER_ATTRIBUTES static PSNIP_BUILTIN__INLINE +#endif + +#define PSNIP_BUILTIN__SUFFIX_B 1 +#define PSNIP_BUILTIN__SUFFIX_S 2 +#define PSNIP_BUILTIN__SUFFIX_ 3 +#define PSNIP_BUILTIN__SUFFIX_L 4 +#define PSNIP_BUILTIN__SUFFIX_LL 5 + +#if !defined(PSNIP_BUILTIN__SIZEOF_CHAR) +# if CHAR_MIN == (-0x7fLL-1) && CHAR_MAX == 0x7fLL +# define PSNIP_BUILTIN__SIZEOF_CHAR 8 +# elif CHAR_MIN == (-0x7fffLL-1) && CHAR_MAX == 0x7fffLL +# define PSNIP_BUILTIN__SIZEOF_CHAR 16 +# elif CHAR_MIN == (-0x7fffffffLL-1) && CHAR_MAX == 0x7fffffffLL +# define PSNIP_BUILTIN__SIZEOF_CHAR 32 +# elif CHAR_MIN == (-0x7fffffffffffffffLL-1) && CHAR_MAX == 0x7fffffffffffffffLL +# define PSNIP_BUILTIN__SIZEOF_CHAR 64 +# endif +#endif + +#if !defined(PSNIP_BUILTIN__SIZEOF_SHRT) +# if SHRT_MIN == (-0x7fLL-1) && SHRT_MAX == 0x7fLL +# define PSNIP_BUILTIN__SIZEOF_SHRT 8 +# elif SHRT_MIN == (-0x7fffLL-1) && SHRT_MAX == 0x7fffLL +# define PSNIP_BUILTIN__SIZEOF_SHRT 16 +# elif SHRT_MIN == (-0x7fffffffLL-1) && SHRT_MAX == 0x7fffffffLL +# define PSNIP_BUILTIN__SIZEOF_SHRT 32 +# elif SHRT_MIN == (-0x7fffffffffffffffLL-1) && SHRT_MAX == 0x7fffffffffffffffLL +# define PSNIP_BUILTIN__SIZEOF_SHRT 64 +# endif +#endif + +#if !defined(PSNIP_BUILTIN__SIZEOF_INT) +# if INT_MIN == (-0x7fLL-1) && INT_MAX == 0x7fLL +# define PSNIP_BUILTIN__SIZEOF_INT 8 +# elif INT_MIN == (-0x7fffLL-1) && INT_MAX == 0x7fffLL +# define PSNIP_BUILTIN__SIZEOF_INT 16 +# elif INT_MIN == (-0x7fffffffLL-1) && INT_MAX == 0x7fffffffLL +# define PSNIP_BUILTIN__SIZEOF_INT 32 +# elif INT_MIN == (-0x7fffffffffffffffLL-1) && INT_MAX == 0x7fffffffffffffffLL +# define PSNIP_BUILTIN__SIZEOF_INT 64 +# endif +#endif + +#if !defined(PSNIP_BUILTIN__SIZEOF_LONG) +# if LONG_MIN == (-0x7fLL-1) && LONG_MAX == 0x7fLL +# define PSNIP_BUILTIN__SIZEOF_LONG 8 +# elif LONG_MIN == (-0x7fffLL-1) && LONG_MAX == 0x7fffLL +# define PSNIP_BUILTIN__SIZEOF_LONG 16 +# elif LONG_MIN == (-0x7fffffffLL-1) && LONG_MAX == 0x7fffffffLL +# define PSNIP_BUILTIN__SIZEOF_LONG 32 +# elif LONG_MIN == (-0x7fffffffffffffffLL-1) && LONG_MAX == 0x7fffffffffffffffLL +# define PSNIP_BUILTIN__SIZEOF_LONG 64 +# endif +#endif + +#if !defined(PSNIP_BUILTIN__SIZEOF_LLONG) +# if LLONG_MIN == (-0x7fLL-1) && LLONG_MAX == 0x7fLL +# define PSNIP_BUILTIN__SIZEOF_LLONG 8 +# elif LLONG_MIN == (-0x7fffLL-1) && LLONG_MAX == 0x7fffLL +# define PSNIP_BUILTIN__SIZEOF_LLONG 16 +# elif LLONG_MIN == (-0x7fffffffLL-1) && LLONG_MAX == 0x7fffffffLL +# define PSNIP_BUILTIN__SIZEOF_LLONG 32 +# elif LLONG_MIN == (-0x7fffffffffffffffLL-1) && LLONG_MAX == 0x7fffffffffffffffLL +# define PSNIP_BUILTIN__SIZEOF_LLONG 64 +# endif +#endif + +#if !defined(PSNIP_BUILTIN_SUFFIX_INT8) +# if PSNIP_BUILTIN__SIZEOF_CHAR == 8 +# define PSNIP_BUILTIN_SUFFIX_INT8 PSNIP_BUILTIN__SUFFIX_B +# elif PSNIP_BUILTIN__SIZEOF_SHRT == 8 +# define PSNIP_BUILTIN_SUFFIX_INT8 PSNIP_BUILTIN__SUFFIX_S +# elif PSNIP_BUILTIN__SIZEOF_INT == 8 +# define PSNIP_BUILTIN_SUFFIX_INT8 PSNIP_BUILTIN__SUFFIX_ +# elif PSNIP_BUILTIN__SIZEOF_LONG == 8 +# define PSNIP_BUILTIN_SUFFIX_INT8 PSNIP_BUILTIN__SUFFIX_L +# elif PSNIP_BUILTIN__SIZEOF_LLONG == 8 +# define PSNIP_BUILTIN_SUFFIX_INT8 PSNIP_BUILTIN__SUFFIX_LL +# endif +#endif + +#if !defined(PSNIP_BUILTIN_SUFFIX_INT16) +# if PSNIP_BUILTIN__SIZEOF_CHAR == 16 +# define PSNIP_BUILTIN_SUFFIX_INT16 PSNIP_BUILTIN__SUFFIX_B +# elif PSNIP_BUILTIN__SIZEOF_SHRT == 16 +# define PSNIP_BUILTIN_SUFFIX_INT16 PSNIP_BUILTIN__SUFFIX_S +# elif PSNIP_BUILTIN__SIZEOF_INT == 16 +# define PSNIP_BUILTIN_SUFFIX_INT16 PSNIP_BUILTIN__SUFFIX_ +# elif PSNIP_BUILTIN__SIZEOF_LONG == 16 +# define PSNIP_BUILTIN_SUFFIX_INT16 PSNIP_BUILTIN__SUFFIX_L +# elif PSNIP_BUILTIN__SIZEOF_LLONG == 16 +# define PSNIP_BUILTIN_SUFFIX_INT16 PSNIP_BUILTIN__SUFFIX_LL +# endif +#endif + +#if !defined(PSNIP_BUILTIN_SUFFIX_INT32) +# if PSNIP_BUILTIN__SIZEOF_CHAR == 32 +# define PSNIP_BUILTIN_SUFFIX_INT32 PSNIP_BUILTIN__SUFFIX_B +# elif PSNIP_BUILTIN__SIZEOF_SHRT == 32 +# define PSNIP_BUILTIN_SUFFIX_INT32 PSNIP_BUILTIN__SUFFIX_S +# elif PSNIP_BUILTIN__SIZEOF_INT == 32 +# define PSNIP_BUILTIN_SUFFIX_INT32 PSNIP_BUILTIN__SUFFIX_ +# elif PSNIP_BUILTIN__SIZEOF_LONG == 32 +# define PSNIP_BUILTIN_SUFFIX_INT32 PSNIP_BUILTIN__SUFFIX_L +# elif PSNIP_BUILTIN__SIZEOF_LLONG == 32 +# define PSNIP_BUILTIN_SUFFIX_INT32 PSNIP_BUILTIN__SUFFIX_LL +# endif +#endif + +#if !defined(PSNIP_BUILTIN_SUFFIX_INT64) +# if defined(__APPLE__) && PSNIP_BUILTIN__SIZEOF_LLONG == 64 +# define PSNIP_BUILTIN_SUFFIX_INT64 PSNIP_BUILTIN__SUFFIX_LL +# elif PSNIP_BUILTIN__SIZEOF_CHAR == 64 +# define PSNIP_BUILTIN_SUFFIX_INT64 PSNIP_BUILTIN__SUFFIX_B +# elif PSNIP_BUILTIN__SIZEOF_SHRT == 64 +# define PSNIP_BUILTIN_SUFFIX_INT64 PSNIP_BUILTIN__SUFFIX_S +# elif PSNIP_BUILTIN__SIZEOF_INT == 64 +# define PSNIP_BUILTIN_SUFFIX_INT64 PSNIP_BUILTIN__SUFFIX_ +# elif PSNIP_BUILTIN__SIZEOF_LONG == 64 +# define PSNIP_BUILTIN_SUFFIX_INT64 PSNIP_BUILTIN__SUFFIX_L +# elif PSNIP_BUILTIN__SIZEOF_LLONG == 64 +# define PSNIP_BUILTIN_SUFFIX_INT64 PSNIP_BUILTIN__SUFFIX_LL +# endif +#endif + +#if defined(PSNIP_BUILTIN_SUFFIX_INT8) +# if PSNIP_BUILTIN_SUFFIX_INT8 == 1 +# define PSNIP_BUILTIN__VARIANT2_INT8(prefix,name) prefix##_builtin_##name##b +# elif PSNIP_BUILTIN_SUFFIX_INT8 == 2 +# define PSNIP_BUILTIN__VARIANT2_INT8(prefix,name) prefix##_builtin_##name##s +# elif PSNIP_BUILTIN_SUFFIX_INT8 == 3 +# define PSNIP_BUILTIN__VARIANT_INT8(prefix,name) prefix##_builtin_##name +# define PSNIP_BUILTIN__VARIANT2_INT8(prefix,name) prefix##_builtin_##name +# elif PSNIP_BUILTIN_SUFFIX_INT8 == 4 +# define PSNIP_BUILTIN__VARIANT_INT8(prefix,name) prefix##_builtin_##name##l +# define PSNIP_BUILTIN__VARIANT2_INT8(prefix,name) prefix##_builtin_##name##l +# elif PSNIP_BUILTIN_SUFFIX_INT8 == 5 +# define PSNIP_BUILTIN__VARIANT_INT8(prefix,name) prefix##_builtin_##name##ll +# define PSNIP_BUILTIN__VARIANT2_INT8(prefix,name) prefix##_builtin_##name##ll +# endif +#endif + +#if defined(PSNIP_BUILTIN_SUFFIX_INT16) +# if PSNIP_BUILTIN_SUFFIX_INT16 == 1 +# define PSNIP_BUILTIN__VARIANT2_INT16(prefix,name) prefix##_builtin_##name##b +# elif PSNIP_BUILTIN_SUFFIX_INT16 == 2 +# define PSNIP_BUILTIN__VARIANT2_INT16(prefix,name) prefix##_builtin_##name##s +# elif PSNIP_BUILTIN_SUFFIX_INT16 == 3 +# define PSNIP_BUILTIN__VARIANT_INT16(prefix,name) prefix##_builtin_##name +# define PSNIP_BUILTIN__VARIANT2_INT16(prefix,name) prefix##_builtin_##name +# elif PSNIP_BUILTIN_SUFFIX_INT16 == 4 +# define PSNIP_BUILTIN__VARIANT_INT16(prefix,name) prefix##_builtin_##name##l +# define PSNIP_BUILTIN__VARIANT2_INT16(prefix,name) prefix##_builtin_##name##l +# elif PSNIP_BUILTIN_SUFFIX_INT16 == 5 +# define PSNIP_BUILTIN__VARIANT_INT16(prefix,name) prefix##_builtin_##name##ll +# define PSNIP_BUILTIN__VARIANT2_INT16(prefix,name) prefix##_builtin_##name##ll +# endif +#endif + +#if defined(PSNIP_BUILTIN_SUFFIX_INT32) +# if PSNIP_BUILTIN_SUFFIX_INT32 == 1 +# define PSNIP_BUILTIN__VARIANT2_INT32(prefix,name) prefix##_builtin_##name##b +# elif PSNIP_BUILTIN_SUFFIX_INT32 == 2 +# define PSNIP_BUILTIN__VARIANT2_INT32(prefix,name) prefix##_builtin_##name##s +# elif PSNIP_BUILTIN_SUFFIX_INT32 == 3 +# define PSNIP_BUILTIN__VARIANT_INT32(prefix,name) prefix##_builtin_##name +# define PSNIP_BUILTIN__VARIANT2_INT32(prefix,name) prefix##_builtin_##name +# elif PSNIP_BUILTIN_SUFFIX_INT32 == 4 +# define PSNIP_BUILTIN__VARIANT_INT32(prefix,name) prefix##_builtin_##name##l +# define PSNIP_BUILTIN__VARIANT2_INT32(prefix,name) prefix##_builtin_##name##l +# elif PSNIP_BUILTIN_SUFFIX_INT32 == 5 +# define PSNIP_BUILTIN__VARIANT_INT32(prefix,name) prefix##_builtin_##name##ll +# define PSNIP_BUILTIN__VARIANT2_INT32(prefix,name) prefix##_builtin_##name##ll +# endif +#endif + +#if defined(PSNIP_BUILTIN_SUFFIX_INT64) +# if PSNIP_BUILTIN_SUFFIX_INT64 == 1 +# define PSNIP_BUILTIN__VARIANT2_INT64(prefix,name) prefix##_builtin_##name##b +# elif PSNIP_BUILTIN_SUFFIX_INT64 == 2 +# define PSNIP_BUILTIN__VARIANT2_INT64(prefix,name) prefix##_builtin_##name##s +# elif PSNIP_BUILTIN_SUFFIX_INT64 == 3 +# define PSNIP_BUILTIN__VARIANT_INT64(prefix,name) prefix##_builtin_##name +# define PSNIP_BUILTIN__VARIANT2_INT64(prefix,name) prefix##_builtin_##name +# elif PSNIP_BUILTIN_SUFFIX_INT64 == 4 +# define PSNIP_BUILTIN__VARIANT_INT64(prefix,name) prefix##_builtin_##name##l +# define PSNIP_BUILTIN__VARIANT2_INT64(prefix,name) prefix##_builtin_##name##l +# elif PSNIP_BUILTIN_SUFFIX_INT64 == 5 +# define PSNIP_BUILTIN__VARIANT_INT64(prefix,name) prefix##_builtin_##name##ll +# define PSNIP_BUILTIN__VARIANT2_INT64(prefix,name) prefix##_builtin_##name##ll +# endif +#endif + +/****** + *** GCC-style built-ins + ******/ + +/*** __builtin_ffs ***/ + +#define PSNIP_BUILTIN__FFS_DEFINE_PORTABLE(f_n, T) \ + PSNIP_BUILTIN__FUNCTION \ + int psnip_builtin_##f_n(T x) { \ + static const char psnip_builtin_ffs_lookup[256] = { \ + 0, 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2, 1, \ + 5, 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2, 1, \ + 6, 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2, 1, \ + 5, 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2, 1, \ + 7, 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2, 1, \ + 5, 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2, 1, \ + 6, 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2, 1, \ + 5, 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2, 1, \ + 8, 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2, 1, \ + 5, 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2, 1, \ + 6, 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2, 1, \ + 5, 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2, 1, \ + 7, 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2, 1, \ + 5, 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2, 1, \ + 6, 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2, 1, \ + 5, 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2, 1 \ + }; \ + \ + unsigned char t; \ + size_t s = 0; \ + \ + while (s < (sizeof(T) * 8)) { \ + t = (unsigned char) ((x >> s) & 0xff); \ + if (t) \ + return psnip_builtin_ffs_lookup[t] + s; \ + \ + s += 8; \ + } \ + \ + return 0; \ + } + +#if PSNIP_BUILTIN_GNU_HAS_BUILTIN(__builtin_ffs, 3, 3) +# define psnip_builtin_ffs(x) __builtin_ffs(x) +# define psnip_builtin_ffsl(x) __builtin_ffsl(x) +# define psnip_builtin_ffsll(x) __builtin_ffsll(x) +# define psnip_builtin_ffs32(x) PSNIP_BUILTIN__VARIANT_INT32(_,ffs)(x) +# define psnip_builtin_ffs64(x) PSNIP_BUILTIN__VARIANT_INT64(_,ffs)(x) +#else +# if PSNIP_BUILTIN_MSVC_HAS_INTRIN(_BitScanForward, 14, 0) +PSNIP_BUILTIN__FUNCTION +int psnip_builtin_ffsll(long long v) { + unsigned long r; +# if defined(_M_AMD64) || defined(_M_ARM) + if (_BitScanForward64(&r, (unsigned long long) v)) { + return (int) (r + 1); + } +# else + if (_BitScanForward(&r, (unsigned long) (v))) { + return (int) (r + 1); + } else if (_BitScanForward(&r, (unsigned long) (v >> 32))) { + return (int) (r + 33); + } +# endif + return 0; +} + +PSNIP_BUILTIN__FUNCTION +int psnip_builtin_ffsl(long v) { + unsigned long r; + if (_BitScanForward(&r, (unsigned long) v)) { + return (int) (r + 1); + } + return 0; +} + +PSNIP_BUILTIN__FUNCTION +int psnip_builtin_ffs(int v) { + return psnip_builtin_ffsl(v); +} +# else +PSNIP_BUILTIN__FFS_DEFINE_PORTABLE(ffs, int) +PSNIP_BUILTIN__FFS_DEFINE_PORTABLE(ffsl, long) +PSNIP_BUILTIN__FFS_DEFINE_PORTABLE(ffsll, long long) +# endif +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define __builtin_ffsll(v) psnip_builtin_ffsll(v) +# define __builtin_ffsl(v) psnip_builtin_ffsl(v) +# define __builtin_ffs(v) psnip_builtin_ffs(v) +# endif +#endif + +#if !defined(psnip_builtin_ffs32) +# define psnip_builtin_ffs32(x) PSNIP_BUILTIN__VARIANT_INT32(psnip,ffs)(x) +#endif + +#if !defined(psnip_builtin_ffs64) +# define psnip_builtin_ffs64(x) PSNIP_BUILTIN__VARIANT_INT64(psnip,ffs)(x) +#endif + +/*** __builtin_clz ***/ + +#define PSNIP_BUILTIN__CLZ_DEFINE_PORTABLE(f_n, T) \ + PSNIP_BUILTIN__FUNCTION \ + int psnip_builtin_##f_n(T x) { \ + static const char psnip_builtin_clz_lookup[256] = { \ + 7, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, \ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, \ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, \ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, \ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 \ + }; \ + size_t s = sizeof(T) * 8; \ + T r; \ + \ + while ((s -= 8) != 0) { \ + r = x >> s; \ + if (r != 0) \ + return psnip_builtin_clz_lookup[r] + \ + (((sizeof(T) - 1) * 8) - s); \ + } \ + \ + if (x == 0) \ + return (int) ((sizeof(T) * 8) - 1); \ + else \ + return psnip_builtin_clz_lookup[x] + \ + ((sizeof(T) - 1) * 8); \ + } + +#if PSNIP_BUILTIN_GNU_HAS_BUILTIN(__builtin_clz, 3, 4) +# define psnip_builtin_clz(x) __builtin_clz(x) +# define psnip_builtin_clzl(x) __builtin_clzl(x) +# define psnip_builtin_clzll(x) __builtin_clzll(x) +# define psnip_builtin_clz32(x) PSNIP_BUILTIN__VARIANT_INT32(_,clz)(x) +# define psnip_builtin_clz64(x) PSNIP_BUILTIN__VARIANT_INT64(_,clz)(x) +#else +# if PSNIP_BUILTIN_MSVC_HAS_INTRIN(_BitScanReverse,14,0) +PSNIP_BUILTIN__FUNCTION +int psnip_builtin_clzll(unsigned long long v) { + unsigned long r = 0; +# if defined(_M_AMD64) || defined(_M_ARM) + if (_BitScanReverse64(&r, v)) { + return 63 - r; + } +# else + if (_BitScanReverse(&r, (unsigned long) (v >> 32))) { + return 31 - r; + } else if (_BitScanReverse(&r, (unsigned long) v)) { + return 63 - r; + } +# endif + return 63; +} + +PSNIP_BUILTIN__FUNCTION +int psnip_builtin_clzl(unsigned long v) { + unsigned long r = 0; + if (_BitScanReverse(&r, v)) { + return 31 - r; + } + return 31; +} + +PSNIP_BUILTIN__FUNCTION +int psnip_builtin_clz(unsigned int v) { + return psnip_builtin_clzl(v); +} +# define psnip_builtin_clz32(x) PSNIP_BUILTIN__VARIANT_INT32(psnip,clz)(x) +# define psnip_builtin_clz64(x) PSNIP_BUILTIN__VARIANT_INT64(psnip,clz)(x) +# else +PSNIP_BUILTIN__FUNCTION +int psnip_builtin_clz32(psnip_uint32_t v) { + static const unsigned char MultiplyDeBruijnBitPosition[] = { + 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, + 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 + }; + + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + + return + ((sizeof(psnip_uint32_t) * CHAR_BIT) - 1) - + MultiplyDeBruijnBitPosition[(psnip_uint32_t)(v * 0x07C4ACDDU) >> 27]; +} + +PSNIP_BUILTIN__FUNCTION +int psnip_builtin_clz64(psnip_uint64_t v) { + static const unsigned char MultiplyDeBruijnBitPosition[] = { + 0, 47, 1, 56, 48, 27, 2, 60, 57, 49, 41, 37, 28, 16, 3, 61, + 54, 58, 35, 52, 50, 42, 21, 44, 38, 32, 29, 23, 17, 11, 4, 62, + 46, 55, 26, 59, 40, 36, 15, 53, 34, 51, 20, 43, 31, 22, 10, 45, + 25, 39, 14, 33, 19, 30, 9, 24, 13, 18, 8, 12, 7, 6, 5, 63 + }; + + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + + return + ((sizeof(psnip_uint64_t) * CHAR_BIT) - 1) - + MultiplyDeBruijnBitPosition[(psnip_uint64_t)(v * 0x03F79D71B4CB0A89ULL) >> 58]; +} + +# if PSNIP_BUILTIN__SIZEOF_INT == 32 + PSNIP_BUILTIN__FUNCTION int psnip_builtin_clz(unsigned int x) { return psnip_builtin_clz32(x); } +# elif PSNIP_BUILTIN__SIZEOF_INT == 64 + PSNIP_BUILTIN__FUNCTION int psnip_builtin_clz(unsigned int x) { return psnip_builtin_clz64(x); } +# else + PSNIP_BUILTIN__CLZ_DEFINE_PORTABLE(clz, unsigned int) +# endif + +# if PSNIP_BUILTIN__SIZEOF_LONG == 32 + PSNIP_BUILTIN__FUNCTION int psnip_builtin_clzl(unsigned long x) { return psnip_builtin_clz32(x); } +# elif PSNIP_BUILTIN__SIZEOF_LONG == 64 + PSNIP_BUILTIN__FUNCTION int psnip_builtin_clzl(unsigned long x) { return psnip_builtin_clz64(x); } +# else + PSNIP_BUILTIN__CLZ_DEFINE_PORTABLE(clzl, unsigned long) +# endif + +# if PSNIP_BUILTIN__SIZEOF_LLONG == 32 + PSNIP_BUILTIN__FUNCTION int psnip_builtin_clzll(unsigned long long x) { return psnip_builtin_clz32(x); } +# elif PSNIP_BUILTIN__SIZEOF_LLONG == 64 + PSNIP_BUILTIN__FUNCTION int psnip_builtin_clzll(unsigned long long x) { return psnip_builtin_clz64(x); } +# else + PSNIP_BUILTIN__CLZ_DEFINE_PORTABLE(clzll, unsigned long long) +# endif + +# endif +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define __builtin_clz(x) psnip_builtin_clz(x) +# define __builtin_clzl(x) psnip_builtin_clzl(x) +# define __builtin_clzll(x) psnip_builtin_clzll(x) +# endif +#endif + +#if !defined(psnip_builtin_clz32) +# define psnip_builtin_clz32(x) PSNIP_BUILTIN__VARIANT_INT32(psnip,clz)(x) +#endif + +#if !defined(psnip_builtin_clz64) +# define psnip_builtin_clz64(x) PSNIP_BUILTIN__VARIANT_INT64(psnip,clz)(x) +#endif + +/*** __builtin_ctz ***/ + +#define PSNIP_BUILTIN__CTZ_DEFINE_PORTABLE(f_n, T) \ + PSNIP_BUILTIN__FUNCTION \ + int psnip_builtin_##f_n(T x) { \ + static const char psnip_builtin_ctz_lookup[256] = { \ + 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, \ + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, \ + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, \ + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, \ + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, \ + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, \ + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, \ + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, \ + 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, \ + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, \ + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, \ + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, \ + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, \ + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, \ + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, \ + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 \ + }; \ + size_t s = 0; \ + T r; \ + \ + do { \ + r = (x >> s) & 0xff; \ + if (r != 0) \ + return psnip_builtin_ctz_lookup[r] + (char) s; \ + } while ((s += 8) < (sizeof(T) * 8)); \ + \ + return (int) sizeof(T) - 1; \ + } + +#if PSNIP_BUILTIN_GNU_HAS_BUILTIN(__builtin_ctz, 3, 4) +# define psnip_builtin_ctz(x) __builtin_ctz(x) +# define psnip_builtin_ctzl(x) __builtin_ctzl(x) +# define psnip_builtin_ctzll(x) __builtin_ctzll(x) +# define psnip_builtin_ctz32(x) PSNIP_BUILTIN__VARIANT_INT32(_,ctz)(x) +# define psnip_builtin_ctz64(x) PSNIP_BUILTIN__VARIANT_INT64(_,ctz)(x) +#else +# if PSNIP_BUILTIN_MSVC_HAS_INTRIN(_BitScanForward, 14, 0) +PSNIP_BUILTIN__FUNCTION +int psnip_builtin_ctzll(unsigned long long v) { + unsigned long r = 0; +# if defined(_M_AMD64) || defined(_M_ARM) + _BitScanForward64(&r, v); + return (int) r; +# else + if (_BitScanForward(&r, (unsigned int) (v))) + return (int) (r); + + _BitScanForward(&r, (unsigned int) (v >> 32)); + return (int) (r + 32); +# endif +} + +PSNIP_BUILTIN__FUNCTION +int psnip_builtin_ctzl(unsigned long v) { + unsigned long r = 0; + _BitScanForward(&r, v); + return (int) r; +} + +PSNIP_BUILTIN__FUNCTION +int psnip_builtin_ctz(unsigned int v) { + return psnip_builtin_ctzl(v); +} +# define psnip_builtin_ctz32(x) PSNIP_BUILTIN__VARIANT_INT32(psnip,ctz)(x) +# define psnip_builtin_ctz64(x) PSNIP_BUILTIN__VARIANT_INT64(psnip,ctz)(x) +# else +PSNIP_BUILTIN__FUNCTION +int psnip_builtin_ctz32(psnip_uint32_t v) { + static const unsigned char MultiplyDeBruijnBitPosition[] = { + 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 + }; + + return + MultiplyDeBruijnBitPosition[((psnip_uint32_t)((v & -v) * 0x077CB531U)) >> 27]; +} + +PSNIP_BUILTIN__FUNCTION +int psnip_builtin_ctz64(psnip_uint64_t v) { + static const unsigned char MultiplyDeBruijnBitPosition[] = { + 0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4, + 62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5, + 63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11, + 54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6 + }; + + return + MultiplyDeBruijnBitPosition[((psnip_uint64_t)((v & -v) * 0x03f79d71b4ca8b09ULL)) >> 58]; +} + +# if PSNIP_BUILTIN__SIZEOF_INT == 32 + PSNIP_BUILTIN__FUNCTION int psnip_builtin_ctz(unsigned int x) { return psnip_builtin_ctz32(x); } +# elif PSNIP_BUILTIN__SIZEOF_INT == 64 + PSNIP_BUILTIN__FUNCTION int psnip_builtin_ctz(unsigned int x) { return psnip_builtin_ctz64(x); } +# else + PSNIP_BUILTIN__CTZ_DEFINE_PORTABLE(ctz, unsigned int) +# endif + +# if PSNIP_BUILTIN__SIZEOF_LONG == 32 + PSNIP_BUILTIN__FUNCTION int psnip_builtin_ctzl(unsigned long x) { return psnip_builtin_ctz32(x); } +# elif PSNIP_BUILTIN__SIZEOF_LONG == 64 + PSNIP_BUILTIN__FUNCTION int psnip_builtin_ctzl(unsigned long x) { return psnip_builtin_ctz64(x); } +# else + PSNIP_BUILTIN__CTZ_DEFINE_PORTABLE(ctzl, unsigned long) +# endif + +# if PSNIP_BUILTIN__SIZEOF_LLONG == 32 + PSNIP_BUILTIN__FUNCTION int psnip_builtin_ctzll(unsigned long long x) { return psnip_builtin_ctz32(x); } +# elif PSNIP_BUILTIN__SIZEOF_LLONG == 64 + PSNIP_BUILTIN__FUNCTION int psnip_builtin_ctzll(unsigned long long x) { return psnip_builtin_ctz64(x); } +# else + PSNIP_BUILTIN__CTZ_DEFINE_PORTABLE(ctzll, unsigned long long) +# endif +# endif +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define __builtin_ctz(x) psnip_builtin_ctz(x) +# define __builtin_ctzl(x) psnip_builtin_ctzl(x) +# define __builtin_ctzll(x) psnip_builtin_ctzll(x) +# endif +#endif + +#if !defined(psnip_builtin_ctz32) +# define psnip_builtin_ctz32(x) PSNIP_BUILTIN__VARIANT_INT32(psnip,ctz)(x) +#endif + +#if !defined(psnip_builtin_ctz64) +# define psnip_builtin_ctz64(x) PSNIP_BUILTIN__VARIANT_INT64(psnip,ctz)(x) +#endif + +/*** __builtin_parity ***/ + +#define PSNIP_BUILTIN__PARITY_DEFINE_PORTABLE(f_n, T) \ + PSNIP_BUILTIN__FUNCTION \ + int psnip_builtin_##f_n(T v) { \ + size_t i; \ + for (i = (sizeof(T) * CHAR_BIT) / 2 ; i > 2 ; i /= 2) \ + v ^= v >> i; \ + v &= 0xf; \ + return (0x6996 >> v) & 1; \ + } + +#if PSNIP_BUILTIN_GNU_HAS_BUILTIN(__builtin_parity, 3, 4) +# define psnip_builtin_parity(x) __builtin_parity(x) +# define psnip_builtin_parityl(x) __builtin_parityl(x) +# define psnip_builtin_parityll(x) __builtin_parityll(x) +# define psnip_builtin_parity32(x) PSNIP_BUILTIN__VARIANT_INT32(_,parity)(x) +# define psnip_builtin_parity64(x) PSNIP_BUILTIN__VARIANT_INT64(_,parity)(x) +#else +PSNIP_BUILTIN__PARITY_DEFINE_PORTABLE(parity, unsigned int) +PSNIP_BUILTIN__PARITY_DEFINE_PORTABLE(parityl, unsigned long) +PSNIP_BUILTIN__PARITY_DEFINE_PORTABLE(parityll, unsigned long long) +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define __builtin_parity(x) psnip_builtin_parity(x) +# define __builtin_parityl(x) psnip_builtin_parityl(x) +# define __builtin_parityll(x) psnip_builtin_parityll(x) +# endif +#endif + +#if !defined(psnip_builtin_parity32) +# define psnip_builtin_parity32(x) PSNIP_BUILTIN__VARIANT_INT32(psnip,parity)(x) +#endif + +#if !defined(psnip_builtin_parity64) +# define psnip_builtin_parity64(x) PSNIP_BUILTIN__VARIANT_INT64(psnip,parity)(x) +#endif + +/*** __builtin_popcount ***/ + +#define PSNIP_BUILTIN__POPCOUNT_DEFINE_PORTABLE(f_n, T) \ + PSNIP_BUILTIN__FUNCTION \ + int psnip_builtin_##f_n(T x) { \ + x = x - ((x >> 1) & (T)~(T)0/3); \ + x = (x & (T)~(T)0/15*3) + ((x >> 2) & (T)~(T)0/15*3); \ + x = (x + (x >> 4)) & (T)~(T)0/255*15; \ + return (T)(x * ((T)~(T)0/255)) >> (sizeof(T) - 1) * 8; \ + } + +#if PSNIP_BUILTIN_GNU_HAS_BUILTIN(__builtin_popcount, 3, 4) +# define psnip_builtin_popcount(x) __builtin_popcount(x) +# define psnip_builtin_popcountl(x) __builtin_popcountl(x) +# define psnip_builtin_popcountll(x) __builtin_popcountll(x) +# define psnip_builtin_popcount32(x) PSNIP_BUILTIN__VARIANT_INT32(_,popcount)(x) +# define psnip_builtin_popcount64(x) PSNIP_BUILTIN__VARIANT_INT64(_,popcount)(x) +#else +PSNIP_BUILTIN__POPCOUNT_DEFINE_PORTABLE(popcount, unsigned int) +PSNIP_BUILTIN__POPCOUNT_DEFINE_PORTABLE(popcountl, unsigned long) +PSNIP_BUILTIN__POPCOUNT_DEFINE_PORTABLE(popcountll, unsigned long long) +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define __builtin_popcount(x) psnip_builtin_popcount(x) +# define __builtin_popcountl(x) psnip_builtin_popcountl(x) +# define __builtin_popcountll(x) psnip_builtin_popcountll(x) +# endif +#endif + +#if !defined(psnip_builtin_popcount32) +# define psnip_builtin_popcount32(x) PSNIP_BUILTIN__VARIANT_INT32(psnip,popcount)(x) +#endif + +#if !defined(psnip_builtin_popcount64) +# define psnip_builtin_popcount64(x) PSNIP_BUILTIN__VARIANT_INT64(psnip,popcount)(x) +#endif + +/*** __builtin_clrsb ***/ + +#define PSNIP_BUILTIN__CLRSB_DEFINE_PORTABLE(f_n, clzfn, T) \ + PSNIP_BUILTIN__FUNCTION \ + int psnip_builtin_##f_n(T x) { \ + return (PSNIP_BUILTIN_UNLIKELY(x == -1) ? \ + ((int) sizeof(x) * 8) : \ + psnip_builtin_##clzfn((x < 0) ? ~x : x)) - 1; \ + } + +#if PSNIP_BUILTIN_GNU_HAS_BUILTIN(__builtin_clrsb, 4, 7) +# define psnip_builtin_clrsb(x) __builtin_clrsb(x) +# if !defined(__INTEL_COMPILER) +# define psnip_builtin_clrsbl(x) __builtin_clrsbl(x) +# else +# if PSNIP_BUILTIN__SIZEOF_LONG == PSNIP_BUILTIN__SIZEOF_INT +# define psnip_builtin_clrsbl(x) ((long) __builtin_clrsb((int) x)) +# elif PSNIP_BUILTIN__SIZEOF_LONG == PSNIP_BUILTIN__SIZEOF_LLONG +# define psnip_builtin_clrsbl(x) ((long) __builtin_clrsbll((long long) x)) +# else + PSNIP_BUILTIN__CLRSB_DEFINE_PORTABLE(clrsbl, clzl, long) +# endif +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define __builtin_clrsbl(x) psnip_builtin_clrsbl(x) +# endif +# endif +# define psnip_builtin_clrsbll(x) __builtin_clrsbll(x) +# define psnip_builtin_clrsb32(x) PSNIP_BUILTIN__VARIANT_INT32(_,clrsb)(x) +# define psnip_builtin_clrsb64(x) PSNIP_BUILTIN__VARIANT_INT64(_,clrsb)(x) +#else +PSNIP_BUILTIN__CLRSB_DEFINE_PORTABLE(clrsb, clz, int) +PSNIP_BUILTIN__CLRSB_DEFINE_PORTABLE(clrsbl, clzl, long) +PSNIP_BUILTIN__CLRSB_DEFINE_PORTABLE(clrsbll, clzll, long long) +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define __builtin_clrsb(x) psnip_builtin_clrsb(x) +# define __builtin_clrsbl(x) psnip_builtin_clrsbl(x) +# define __builtin_clrsbll(x) psnip_builtin_clrsbll(x) +# endif +#endif + +#if !defined(psnip_builtin_clrsb32) +# define psnip_builtin_clrsb32(x) PSNIP_BUILTIN__VARIANT_INT32(psnip,clrsb)(x) +#endif + +#if !defined(psnip_builtin_clrsb64) +# define psnip_builtin_clrsb64(x) PSNIP_BUILTIN__VARIANT_INT64(psnip,clrsb)(x) +#endif + +/*** __builtin_bitreverse ***/ + +#define PSNIP_BUILTIN__BITREVERSE_DEFINE_PORTABLE(f_n, T) \ + PSNIP_BUILTIN__FUNCTION \ + T psnip_builtin_##f_n(T x) { \ + size_t s = sizeof(x) * CHAR_BIT; \ + T mask = (T) 0U; \ + mask = ~mask; \ + while ((s >>= 1) > 0) { \ + mask ^= (mask << s); \ + x = ((x >> s) & mask) | ((x << s) & ~mask); \ + } \ + return x; \ + } + +#if PSNIP_BUILTIN_CLANG_HAS_BUILTIN(__builtin_bitreverse64) && !defined(__EMSCRIPTEN__) +# define psnip_builtin_bitreverse8(x) __builtin_bitreverse8(x) +# define psnip_builtin_bitreverse16(x) __builtin_bitreverse16(x) +# define psnip_builtin_bitreverse32(x) __builtin_bitreverse32(x) +# define psnip_builtin_bitreverse64(x) __builtin_bitreverse64(x) +#else +PSNIP_BUILTIN__FUNCTION +psnip_uint8_t psnip_builtin_bitreverse8(psnip_uint8_t v) { + return (psnip_uint8_t) ((v * 0x0202020202ULL & 0x010884422010ULL) % 1023); +} +PSNIP_BUILTIN__BITREVERSE_DEFINE_PORTABLE(bitreverse16, psnip_uint16_t) +PSNIP_BUILTIN__BITREVERSE_DEFINE_PORTABLE(bitreverse32, psnip_uint32_t) +PSNIP_BUILTIN__BITREVERSE_DEFINE_PORTABLE(bitreverse64, psnip_uint64_t) +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define __builtin_bitreverse8(x) psnip_builtin_bitreverse8(x) +# define __builtin_bitreverse16(x) psnip_builtin_bitreverse16(x) +# define __builtin_bitreverse32(x) psnip_builtin_bitreverse32(x) +# define __builtin_bitreverse64(x) psnip_builtin_bitreverse64(x) +# endif +#endif + +/*** __builtin_addc ***/ + +#define PSNIP_BUILTIN__ADDC_DEFINE_PORTABLE(f_n, T) \ + PSNIP_BUILTIN__FUNCTION \ + T psnip_builtin_##f_n(T x, T y, T ci, T* co) { \ + T max = 0; \ + T r = (T) x + y; \ + max = ~max; \ + *co = (T) (x > (max - y)); \ + if (ci) { \ + if (r == max) \ + *co = 1; \ + r += ci; \ + } \ + return r; \ + } + +#if PSNIP_BUILTIN_CLANG_HAS_BUILTIN(__builtin_addc) +# define psnip_builtin_addcb(x, y, ci, co) __builtin_addcb(x, y, ci, co) +# define psnip_builtin_addcs(x, y, ci, co) __builtin_addcs(x, y, ci, co) +# define psnip_builtin_addc(x, y, ci, co) __builtin_addc(x, y, ci, co) +# define psnip_builtin_addcl(x, y, ci, co) __builtin_addcl(x, y, ci, co) +# define psnip_builtin_addcll(x, y, ci, co) __builtin_addcll(x, y, ci, co) +# define psnip_builtin_addc8(x, y, ci, co) PSNIP_BUILTIN__VARIANT2_INT8(_,addc)(x, y, ci, co) +# define psnip_builtin_addc16(x, y, ci, co) PSNIP_BUILTIN__VARIANT2_INT16(_,addc)(x, y, ci, co) +# define psnip_builtin_addc32(x, y, ci, co) PSNIP_BUILTIN__VARIANT2_INT32(_,addc)(x, y, ci, co) +# define psnip_builtin_addc64(x, y, ci, co) PSNIP_BUILTIN__VARIANT2_INT64(_,addc)(x, y, ci, co) +#else +PSNIP_BUILTIN__ADDC_DEFINE_PORTABLE(addcb, unsigned char) +PSNIP_BUILTIN__ADDC_DEFINE_PORTABLE(addcs, unsigned short) +PSNIP_BUILTIN__ADDC_DEFINE_PORTABLE(addc, unsigned int) +PSNIP_BUILTIN__ADDC_DEFINE_PORTABLE(addcl, unsigned long) +PSNIP_BUILTIN__ADDC_DEFINE_PORTABLE(addcll, unsigned long long) +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define __builtin_addcb(x, y, ci, co) psnip_builtin_addcb(x, y, ci, co) +# define __builtin_addcs(x, y, ci, co) psnip_builtin_addcs(x, y, ci, co) +# define __builtin_addc(x, y, ci, co) psnip_builtin_addc(x, y, ci, co) +# define __builtin_addcl(x, y, ci, co) psnip_builtin_addcl(x, y, ci, co) +# define __builtin_addcll(x, y, ci, co) psnip_builtin_addcll(x, y, ci, co) +# endif +#endif + +#if !defined(psnip_builtin_addc8) +# define psnip_builtin_addc8(x, y, ci, co) PSNIP_BUILTIN__VARIANT2_INT8(psnip,addc)(x, y, ci, co) +#endif + +#if !defined(psnip_builtin_addc16) +# define psnip_builtin_addc16(x, y, ci, co) PSNIP_BUILTIN__VARIANT2_INT16(psnip,addc)(x, y, ci, co) +#endif + +#if !defined(psnip_builtin_addc32) +# define psnip_builtin_addc32(x, y, ci, co) PSNIP_BUILTIN__VARIANT2_INT32(psnip,addc)(x, y, ci, co) +#endif + +#if !defined(psnip_builtin_addc64) +# define psnip_builtin_addc64(x, y, ci, co) PSNIP_BUILTIN__VARIANT2_INT64(psnip,addc)(x, y, ci, co) +#endif + +/*** __builtin_subc ***/ + +#define PSNIP_BUILTIN__SUBC_DEFINE_PORTABLE(f_n, T) \ + PSNIP_BUILTIN__FUNCTION \ + T psnip_builtin_##f_n(T x, T y, T ci, T* co) { \ + T r = x - y; \ + *co = x < y; \ + if (ci) { \ + r--; \ + if (r == 0) \ + *co = 1; \ + } \ + return r; \ + } + +#if PSNIP_BUILTIN_CLANG_HAS_BUILTIN(__builtin_subc) +# define psnip_builtin_subcb(x, y, ci, co) __builtin_subcb(x, y, ci, co) +# define psnip_builtin_subcs(x, y, ci, co) __builtin_subcs(x, y, ci, co) +# define psnip_builtin_subc(x, y, ci, co) __builtin_subc(x, y, ci, co) +# define psnip_builtin_subcl(x, y, ci, co) __builtin_subcl(x, y, ci, co) +# define psnip_builtin_subcll(x, y, ci, co) __builtin_subcll(x, y, ci, co) +# define psnip_builtin_subc8(x, y, ci, co) PSNIP_BUILTIN__VARIANT2_INT8(_,subc)(x, y, ci, co) +# define psnip_builtin_subc16(x, y, ci, co) PSNIP_BUILTIN__VARIANT2_INT16(_,subc)(x, y, ci, co) +# define psnip_builtin_subc32(x, y, ci, co) PSNIP_BUILTIN__VARIANT2_INT32(_,subc)(x, y, ci, co) +# define psnip_builtin_subc64(x, y, ci, co) PSNIP_BUILTIN__VARIANT2_INT64(_,subc)(x, y, ci, co) +#else +PSNIP_BUILTIN__SUBC_DEFINE_PORTABLE(subcb, unsigned char) +PSNIP_BUILTIN__SUBC_DEFINE_PORTABLE(subcs, unsigned short) +PSNIP_BUILTIN__SUBC_DEFINE_PORTABLE(subc, unsigned int) +PSNIP_BUILTIN__SUBC_DEFINE_PORTABLE(subcl, unsigned long) +PSNIP_BUILTIN__SUBC_DEFINE_PORTABLE(subcll, unsigned long long) +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define __builtin_subcb(x, y, ci, co) psnip_builtin_subcb(x, y, ci, co) +# define __builtin_subcs(x, y, ci, co) psnip_builtin_subcs(x, y, ci, co) +# define __builtin_subc(x, y, ci, co) psnip_builtin_subc(x, y, ci, co) +# define __builtin_subcl(x, y, ci, co) psnip_builtin_subcl(x, y, ci, co) +# define __builtin_subcll(x, y, ci, co) psnip_builtin_subcll(x, y, ci, co) +# endif +#endif + +#if !defined(psnip_builtin_subc8) +# define psnip_builtin_subc8(x, y, ci, co) PSNIP_BUILTIN__VARIANT2_INT8(psnip,subc)(x, y, ci, co) +#endif + +#if !defined(psnip_builtin_subc16) +# define psnip_builtin_subc16(x, y, ci, co) PSNIP_BUILTIN__VARIANT2_INT16(psnip,subc)(x, y, ci, co) +#endif + +#if !defined(psnip_builtin_subc32) +# define psnip_builtin_subc32(x, y, ci, co) PSNIP_BUILTIN__VARIANT2_INT32(psnip,subc)(x, y, ci, co) +#endif + +#if !defined(psnip_builtin_subc64) +# define psnip_builtin_subc64(x, y, ci, co) PSNIP_BUILTIN__VARIANT2_INT64(psnip,subc)(x, y, ci, co) +#endif + +/*** __builtin_bswap ***/ + +#if PSNIP_BUILTIN_GNU_HAS_BUILTIN(__builtin_bswap16, 4, 8) +# define psnip_builtin_bswap16(x) __builtin_bswap16(x) +#else +# if PSNIP_BUILTIN_MSVC_HAS_INTRIN(_byteswap_ushort,13,10) +# define psnip_builtin_bswap16(x) _byteswap_ushort(x) +# else +PSNIP_BUILTIN__FUNCTION +psnip_uint16_t +psnip_builtin_bswap16(psnip_uint16_t v) { + return + ((v & (((psnip_uint16_t) 0xff) << 8)) >> 8) | + ((v & (((psnip_uint16_t) 0xff) )) << 8); +} +# endif +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define __builtin_bswap16(x) psnip_builtin_bswap16(x) +# endif +#endif + +#if PSNIP_BUILTIN_GNU_HAS_BUILTIN(__builtin_bswap16, 4, 3) +# define psnip_builtin_bswap32(x) __builtin_bswap32(x) +# define psnip_builtin_bswap64(x) __builtin_bswap64(x) +#else +# if PSNIP_BUILTIN_MSVC_HAS_INTRIN(_byteswap_ushort,13,10) +# define psnip_builtin_bswap32(x) _byteswap_ulong(x) +# define psnip_builtin_bswap64(x) _byteswap_uint64(x) +# else +PSNIP_BUILTIN__FUNCTION +psnip_uint32_t +psnip_builtin_bswap32(psnip_uint32_t v) { + return + ((v & (((psnip_uint32_t) 0xff) << 24)) >> 24) | + ((v & (((psnip_uint32_t) 0xff) << 16)) >> 8) | + ((v & (((psnip_uint32_t) 0xff) << 8)) << 8) | + ((v & (((psnip_uint32_t) 0xff) )) << 24); +} + +PSNIP_BUILTIN__FUNCTION +psnip_uint64_t +psnip_builtin_bswap64(psnip_uint64_t v) { + return + ((v & (((psnip_uint64_t) 0xff) << 56)) >> 56) | + ((v & (((psnip_uint64_t) 0xff) << 48)) >> 40) | + ((v & (((psnip_uint64_t) 0xff) << 40)) >> 24) | + ((v & (((psnip_uint64_t) 0xff) << 32)) >> 8) | + ((v & (((psnip_uint64_t) 0xff) << 24)) << 8) | + ((v & (((psnip_uint64_t) 0xff) << 16)) << 24) | + ((v & (((psnip_uint64_t) 0xff) << 8)) << 40) | + ((v & (((psnip_uint64_t) 0xff) )) << 56); +} +# endif +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define __builtin_bswap32(x) psnip_builtin_bswap32(x) +# define __builtin_bswap64(x) psnip_builtin_bswap64(x) +# endif +#endif + +/****** + *** MSVC-style intrinsics + ******/ + +/*** _rotl ***/ + +#define PSNIP_BUILTIN_ROTL_DEFINE_PORTABLE(f_n, T, ST) \ + PSNIP_BUILTIN__FUNCTION \ + T psnip_intrin_##f_n(T value, ST shift) { \ + return \ + (value >> ((sizeof(T) * 8) - shift)) | \ + (value << shift); \ + } + +#if PSNIP_BUILTIN_MSVC_HAS_INTRIN(_rotl8, 14, 0) +# define psnip_intrin_rotl8(value, shift) _rotl8(value, shift) +# define psnip_intrin_rotl16(value, shift) _rotl16(value, shift) +#else + PSNIP_BUILTIN_ROTL_DEFINE_PORTABLE(rotl8, psnip_uint8_t, unsigned char) + PSNIP_BUILTIN_ROTL_DEFINE_PORTABLE(rotl16, psnip_uint16_t, unsigned char) +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# if !defined(_rotl8) +# define _rotl8(value, shift) psnip_intrin_rotl8(value, shift) +# endif +# if !defined(_rotl16) +# define _rotl16(value, shift) psnip_intrin_rotl16(value, shift) +# endif +# endif +#endif + +#if PSNIP_BUILTIN_MSVC_HAS_INTRIN(_rotl8, 13, 10) +# define psnip_intrin_rotl(value, shift) _rotl(value, shift) +# define psnip_intrin_rotl64(value, shift) _rotl64(value, shift) +#else + PSNIP_BUILTIN_ROTL_DEFINE_PORTABLE(rotl, psnip_uint32_t, int) + PSNIP_BUILTIN_ROTL_DEFINE_PORTABLE(rotl64, psnip_uint64_t, int) + +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# if !defined(_rotl) +# define _rotl(value, shift) psnip_intrin_rotl(value, shift) +# endif +# if !defined(_rotl64) +# define _rotl64(value, shift) psnip_intrin_rotl64(value, shift) +# endif +# endif +#endif + +/*** _rotr ***/ + +#define PSNIP_BUILTIN_ROTR_DEFINE_PORTABLE(f_n, T, ST) \ + PSNIP_BUILTIN__FUNCTION \ + T psnip_intrin_##f_n(T value, ST shift) { \ + return \ + (value << ((sizeof(T) * 8) - shift)) | \ + (value >> shift); \ + } + +PSNIP_BUILTIN_ROTR_DEFINE_PORTABLE(rotr8, psnip_uint8_t, unsigned char) +PSNIP_BUILTIN_ROTR_DEFINE_PORTABLE(rotr16, psnip_uint16_t, unsigned char) + +#if PSNIP_BUILTIN_MSVC_HAS_INTRIN(_rotr8, 14, 0) +# define psnip_intrin_rotr8(value, shift) _rotr8(value, shift) +# define psnip_intrin_rotr16(value, shift) _rotr16(value, shift) +#else +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define _rotr8(value, shift) psnip_intrin_rotr8(value, shift) +# define _rotr16(value, shift) psnip_intrin_rotr16(value, shift) +# endif +#endif + +#if PSNIP_BUILTIN_MSVC_HAS_INTRIN(_rotr8, 13, 10) +# define psnip_intrin_rotr(value, shift) _rotr(value, shift) +# define psnip_intrin_rotr64(value, shift) _rotr64(value, shift) +#else +PSNIP_BUILTIN_ROTR_DEFINE_PORTABLE(rotr, psnip_uint32_t, int) +PSNIP_BUILTIN_ROTR_DEFINE_PORTABLE(rotr64, psnip_uint64_t, int) +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# if !defined(_rotr) +# define _rotr(value, shift) psnip_intrin_rotr(value, shift) +# endif +# if !defined(_rotr64) +# define _rotr64(value, shift) psnip_intrin_rotr64(value, shift) +# endif +# endif +#endif + +/*** _BitScanForward ***/ + +#if PSNIP_BUILTIN_MSVC_HAS_INTRIN(_BitScanForward, 14, 0) +# pragma intrinsic(_BitScanForward) +PSNIP_BUILTIN__FUNCTION +unsigned char psnip_intrin_BitScanForward(unsigned long* Index, psnip_uint32_t Mask) { + const unsigned long M = (unsigned long) Mask; + return _BitScanForward(Index, M); +} +#else +PSNIP_BUILTIN__FUNCTION +unsigned char psnip_intrin_BitScanForward(unsigned long* Index, psnip_uint32_t Mask) { + return PSNIP_BUILTIN_UNLIKELY(Mask == 0) ? 0 : ((*Index = psnip_builtin_ctz32 (Mask)), 1); +} + +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define _BitScanForward(Index, Mask) psnip_intrin_BitScanForward(Index, Mask) +# endif +#endif + +#if PSNIP_BUILTIN_MSVC_HAS_INTRIN(_BitScanForward64, 14, 0) && (defined(_M_AMD64) || defined(_M_ARM)) +# pragma intrinsic(_BitScanForward64) +# define psnip_intrin_BitScanForward64(Index, Mask) _BitScanForward64(Index, Mask) +#else +PSNIP_BUILTIN__FUNCTION +unsigned char psnip_intrin_BitScanForward64(unsigned long* Index, psnip_uint64_t Mask) { + return PSNIP_BUILTIN_UNLIKELY(Mask == 0) ? 0 : ((*Index = psnip_builtin_ctz64 (Mask)), 1); +} + +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define _BitScanForward64(Index, Mask) psnip_intrin_BitScanForward64(Index, Mask) +# endif +#endif + +/*** _BitScanReverse ***/ + +#if PSNIP_BUILTIN_MSVC_HAS_INTRIN(_BitScanReverse, 14, 0) +# pragma intrinsic(_BitScanReverse) +PSNIP_BUILTIN__FUNCTION +unsigned char psnip_intrin_BitScanReverse(unsigned long* Index, psnip_uint32_t Mask) { + const unsigned long M = (unsigned long) Mask; + return _BitScanReverse(Index, M); +} +#else +PSNIP_BUILTIN__FUNCTION +unsigned char psnip_intrin_BitScanReverse(unsigned long* Index, psnip_uint32_t Mask) { + return (PSNIP_BUILTIN_UNLIKELY(Mask == 0)) ? 0 : ((*Index = ((sizeof(Mask) * CHAR_BIT) - 1) - psnip_builtin_clz32 (Mask)), 1); +} + +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define _BitScanReverse(Index, Mask) psnip_intrin_BitScanReverse(Index, Mask) +# endif +#endif + +#if PSNIP_BUILTIN_MSVC_HAS_INTRIN(_BitScanReverse64, 14, 0) && (defined(_M_AMD64) || defined(_M_ARM)) +# pragma intrinsic(_BitScanReverse64) +# define psnip_intrin_BitScanReverse64(Index, Mask) _BitScanReverse64(Index, Mask) +#else +PSNIP_BUILTIN__FUNCTION +unsigned char psnip_intrin_BitScanReverse64(unsigned long* Index, psnip_uint64_t Mask) { + return (PSNIP_BUILTIN_UNLIKELY(Mask == 0)) ? 0 : ((*Index = ((sizeof(Mask) * CHAR_BIT) - 1) - psnip_builtin_clz64 (Mask)), 1); +} + +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define _BitScanReverse64(Index, Mask) psnip_intrin_BitScanReverse64(Index, Mask) +# endif +#endif + +/*** bittest ***/ + +#if PSNIP_BUILTIN_MSVC_HAS_INTRIN(_bittest, 14, 0) +# pragma intrinsic(_bittest) +# define psnip_intrin_bittest(a, b) \ + __pragma(warning(push)) \ + __pragma(warning(disable:4057)) \ + _bittest(a, b) \ + __pragma(warning(pop)) +#else +# define psnip_intrin_bittest(a, b) (((*(a)) >> (b)) & 1) +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define _bittest(a, b) psnip_intrin_bittest(a, b) +# endif +#endif + +#if PSNIP_BUILTIN_MSVC_HAS_INTRIN(_bittest64, 14, 0) && (defined(_M_AMD64) || defined(_M_ARM)) +# pragma intrinsic(_bittest64) +# define psnip_intrin_bittest64(a, b) _bittest64(a, b) +#else +# define psnip_intrin_bittest64(a, b) (((*(a)) >> (b)) & 1) +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define _bittest64(a, b) psnip_intrin_bittest64(a, b) +# endif +#endif + +/*** bittestandcomplement ***/ + +#define PSNIP_BUILTIN__BITTESTANDCOMPLEMENT_DEFINE_PORTABLE(f_n, T, UT) \ + PSNIP_BUILTIN__FUNCTION \ + unsigned char psnip_intrin_##f_n(T* a, T b) { \ + const char r = (*a >> b) & 1; \ + *a ^= ((UT) 1) << b; \ + return r; \ + } + +#if PSNIP_BUILTIN_MSVC_HAS_INTRIN(_bittestandcomplement, 14, 0) +# pragma intrinsic(_bittestandcomplement) +# define psnip_intrin_bittestandcomplement(a, b) \ + __pragma(warning(push)) \ + __pragma(warning(disable:4057)) \ + _bittestandcomplement(a, b) \ + __pragma(warning(pop)) +#else +PSNIP_BUILTIN__BITTESTANDCOMPLEMENT_DEFINE_PORTABLE(bittestandcomplement, psnip_int32_t, psnip_uint32_t) +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define _bittestandcomplement(a, b) psnip_intrin_bittestandcomplement(a, b) +# endif +#endif + +#if PSNIP_BUILTIN_MSVC_HAS_INTRIN(_bittestandcomplement64, 14, 0) && defined(_M_AMD64) +# define psnip_intrin_bittestandcomplement64(a, b) _bittestandcomplement64(a, b) +#else +PSNIP_BUILTIN__BITTESTANDCOMPLEMENT_DEFINE_PORTABLE(bittestandcomplement64, psnip_int64_t, psnip_uint64_t) +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define _bittestandcomplement64(a, b) psnip_intrin_bittestandcomplement64(a, b) +# endif +#endif + +/*** bittestandreset ***/ + +#define PSNIP_BUILTIN__BITTESTANDRESET_DEFINE_PORTABLE(f_n, T, UT) \ + PSNIP_BUILTIN__FUNCTION \ + unsigned char psnip_intrin_##f_n(T* a, T b) { \ + const char r = (*a >> b) & 1; \ + *a &= ~(((UT) 1) << b); \ + return r; \ + } + +#if PSNIP_BUILTIN_MSVC_HAS_INTRIN(_bittestandreset, 14, 0) +# pragma intrinsic(_bittestandreset) +# define psnip_intrin_bittestandreset(a, b) \ + __pragma(warning(push)) \ + __pragma(warning(disable:4057)) \ + _bittestandreset(a, b) \ + __pragma(warning(pop)) +#else +PSNIP_BUILTIN__BITTESTANDRESET_DEFINE_PORTABLE(bittestandreset, psnip_int32_t, psnip_uint32_t) +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define _bittestandreset(a, b) psnip_intrin_bittestandreset(a, b) +# endif +#endif + +#if PSNIP_BUILTIN_MSVC_HAS_INTRIN(_bittestandreset64, 14, 0) && (defined(_M_AMD64) || defined(_M_IA64)) +# pragma intrinsic(_bittestandreset64) +# define psnip_intrin_bittestandreset64(a, b) _bittestandreset64(a, b) +#else +PSNIP_BUILTIN__BITTESTANDRESET_DEFINE_PORTABLE(bittestandreset64, psnip_int64_t, psnip_uint64_t) +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define _bittestandreset64(a, b) psnip_intrin_bittestandreset64(a, b) +# endif +#endif + +/*** bittestandset ***/ + +#define PSNIP_BUILTIN__BITTESTANDSET_DEFINE_PORTABLE(f_n, T, UT) \ + PSNIP_BUILTIN__FUNCTION \ + unsigned char psnip_intrin_##f_n(T* a, T b) { \ + const char r = (*a >> b) & 1; \ + *a |= ((UT) 1) << b; \ + return r; \ + } + +#if PSNIP_BUILTIN_MSVC_HAS_INTRIN(_bittestandset, 14, 0) +# pragma intrinsic(_bittestandset) +# define psnip_intrin_bittestandset(a, b) \ + __pragma(warning(push)) \ + __pragma(warning(disable:4057)) \ + _bittestandset(a, b) \ + __pragma(warning(pop)) +#else +PSNIP_BUILTIN__BITTESTANDSET_DEFINE_PORTABLE(bittestandset, psnip_int32_t, psnip_uint32_t) +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define _bittestandset(a, b) psnip_intrin_bittestandset(a, b) +# endif +#endif + +#if PSNIP_BUILTIN_MSVC_HAS_INTRIN(_bittestandset64, 14, 0) && defined(_M_AMD64) +# pragma intrinsic(_bittestandset64) +# define psnip_intrin_bittestandset64(a, b) _bittestandset64(a, b) +#else +PSNIP_BUILTIN__BITTESTANDSET_DEFINE_PORTABLE(bittestandset64, psnip_int64_t, psnip_uint64_t) +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define _bittestandset64(a, b) psnip_intrin_bittestandset64(a, b) +# endif +#endif + +/*** shiftleft128 ***/ + +#if PSNIP_BUILTIN_MSVC_HAS_INTRIN(__shiftleft128, 14, 0) && defined(_M_AMD64) +# define psnip_intrin_shiftleft128(LowPart, HighPart, Shift) __shiftleft128(LowPart, HighPart, Shift) +#else +# if defined(__SIZEOF_INT128__) +PSNIP_BUILTIN__FUNCTION +psnip_uint64_t psnip_intrin_shiftleft128(psnip_uint64_t LowPart, psnip_uint64_t HighPart, unsigned char Shift) { + unsigned __int128 r = HighPart; + r <<= 64; + r |= LowPart; + r <<= Shift % 64; + return (psnip_uint64_t) (r >> 64); +} +# else +PSNIP_BUILTIN__FUNCTION +psnip_uint64_t psnip_intrin_shiftleft128(psnip_uint64_t LowPart, psnip_uint64_t HighPart, unsigned char Shift) { + Shift %= 64; + return PSNIP_BUILTIN_UNLIKELY(Shift == 0) ? HighPart : ((HighPart << Shift) | (LowPart >> (64 - Shift))); +} +# endif +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define __shiftleft128(LowPart, HighPart, Shift) psnip_intrin_shiftleft128(LowPart, HighPart, Shift) +# endif +#endif + +/*** shiftright128 ***/ + +#if PSNIP_BUILTIN_MSVC_HAS_INTRIN(__shiftright128, 14, 0) && defined(_M_AMD64) +# define psnip_intrin_shiftright128(LowPart, HighPart, Shift) __shiftright128(LowPart, HighPart, Shift) +#else +# if defined(__SIZEOF_INT128__) +PSNIP_BUILTIN__FUNCTION +psnip_uint64_t psnip_intrin_shiftright128(psnip_uint64_t LowPart, psnip_uint64_t HighPart, unsigned char Shift) { + unsigned __int128 r = HighPart; + r <<= 64; + r |= LowPart; + r >>= Shift % 64; + return (psnip_uint64_t) r; +} +# else +PSNIP_BUILTIN__FUNCTION +psnip_uint64_t psnip_intrin_shiftright128(psnip_uint64_t LowPart, psnip_uint64_t HighPart, unsigned char Shift) { + Shift %= 64; + + if (PSNIP_BUILTIN_UNLIKELY(Shift == 0)) + return LowPart; + + return + (HighPart << (64 - Shift)) | + (LowPart >> Shift); +} +# endif +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define __shiftright128(LowPart, HighPart, Shift) psnip_intrin_shiftright128(LowPart, HighPart, Shift) +# endif +#endif + +/*** byteswap ***/ + +#if PSNIP_BUILTIN_MSVC_HAS_INTRIN(_byteswap_ushort,13,10) +# pragma intrinsic(_byteswap_ushort) +# define psnip_intrin_byteswap_ushort(v) _byteswap_ushort(v) +# pragma intrinsic(_byteswap_ulong) +# define psnip_intrin_byteswap_ulong(v) _byteswap_ulong(v) +# pragma intrinsic(_byteswap_uint64) +# define psnip_intrin_byteswap_uint64(v) _byteswap_uint64(v) +#else +# define psnip_intrin_byteswap_ushort(v) psnip_builtin_bswap16(v) +# define psnip_intrin_byteswap_ulong(v) psnip_builtin_bswap32(v) +# define psnip_intrin_byteswap_uint64(v) psnip_builtin_bswap64(v) +# if defined(PSNIP_BUILTIN_EMULATE_NATIVE) +# define _byteswap_ushort(v) psnip_intrin_byteswap_ushort(v) +# define _byteswap_ulong(v) psnip_intrin_byteswap_ulong(v) +# define _byteswap_uint64(v) psnip_intrin_byteswap_uint64(v) +# endif +#endif + +#endif /* defined(PSNIP_BUILTIN_H) */ diff --git a/ext/bitset/exact-int.h b/ext/bitset/exact-int.h new file mode 100644 index 0000000..918b8f7 --- /dev/null +++ b/ext/bitset/exact-int.h @@ -0,0 +1,229 @@ +/* Exact-width integer types + * Portable Snippets - https://gitub.com/nemequ/portable-snippets + * Created by Evan Nemerson + * + * To the extent possible under law, the authors have waived all + * copyright and related or neighboring rights to this code. For + * details, see the Creative Commons Zero 1.0 Universal license at + * https://creativecommons.org/publicdomain/zero/1.0/ + * + * This header tries to define psnip_(u)int(8|16|32|64)_t to + * appropriate types given your system. For most systems this means + * including and adding a few preprocessor definitions. + * + * If you prefer, you can define any necessary types yourself. + * Snippets in this repository which rely on these types will not + * attempt to include this header if you have already defined the + * types it uses. + */ + +#if !defined(PSNIP_EXACT_INT_H) +# define PSNIP_EXACT_INT_H +# if !defined(PSNIP_EXACT_INT_HAVE_STDINT) +# if defined(_STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +# define PSNIP_EXACT_INT_HAVE_STDINT +# elif defined(__has_include) +# if __has_include() +# define PSNIP_EXACT_INT_HAVE_STDINT +# endif +# elif \ + defined(HAVE_STDINT_H) || \ + defined(_STDINT_H_INCLUDED) || \ + defined(_STDINT_H) || \ + defined(_STDINT_H_) +# define PSNIP_EXACT_INT_HAVE_STDINT +# elif \ + (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))) || \ + (defined(_MSC_VER) && (_MSC_VER >= 1600)) || \ + (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x570)) || \ + (defined(__WATCOMC__) && (__WATCOMC__ >= 1250)) +# define PSNIP_EXACT_INT_HAVE_STDINT +# endif +# endif + +# if \ + defined(__INT8_TYPE__) && defined(__INT16_TYPE__) && defined(__INT32_TYPE__) && defined(__INT64_TYPE__) && \ + defined(__UINT8_TYPE__) && defined(__UINT16_TYPE__) && defined(__UINT32_TYPE__) && defined(__UINT64_TYPE__) +# define psnip_int8_t __INT8_TYPE__ +# define psnip_int16_t __INT16_TYPE__ +# define psnip_int32_t __INT32_TYPE__ +# define psnip_int64_t __INT64_TYPE__ +# define psnip_uint8_t __UINT8_TYPE__ +# define psnip_uint16_t __UINT16_TYPE__ +# define psnip_uint32_t __UINT32_TYPE__ +# define psnip_uint64_t __UINT64_TYPE__ +# elif defined(PSNIP_EXACT_INT_HAVE_STDINT) +# include +# if !defined(psnip_int8_t) +# define psnip_int8_t int8_t +# endif +# if !defined(psnip_uint8_t) +# define psnip_uint8_t uint8_t +# endif +# if !defined(psnip_int16_t) +# define psnip_int16_t int16_t +# endif +# if !defined(psnip_uint16_t) +# define psnip_uint16_t uint16_t +# endif +# if !defined(psnip_int32_t) +# define psnip_int32_t int32_t +# endif +# if !defined(psnip_uint32_t) +# define psnip_uint32_t uint32_t +# endif +# if !defined(psnip_int64_t) +# define psnip_int64_t int64_t +# endif +# if !defined(psnip_uint64_t) +# define psnip_uint64_t uint64_t +# endif +# elif defined(_MSC_VER) +# if !defined(psnip_int8_t) +# define psnip_int8_t __int8 +# endif +# if !defined(psnip_uint8_t) +# define psnip_uint8_t unsigned __int8 +# endif +# if !defined(psnip_int16_t) +# define psnip_int16_t __int16 +# endif +# if !defined(psnip_uint16_t) +# define psnip_uint16_t unsigned __int16 +# endif +# if !defined(psnip_int32_t) +# define psnip_int32_t __int32 +# endif +# if !defined(psnip_uint32_t) +# define psnip_uint32_t unsigned __int32 +# endif +# if !defined(psnip_int64_t) +# define psnip_int64_t __int64 +# endif +# if !defined(psnip_uint64_t) +# define psnip_uint64_t unsigned __int64 +# endif +# else +# include +# if !defined(psnip_int8_t) +# if defined(CHAR_MIN) && defined(CHAR_MAX) && (CHAR_MIN == (-127-1)) && (CHAR_MAX == 127) +# define psnip_int8_t char +# elif defined(SHRT_MIN) && defined(SHRT_MAX) && (SHRT_MIN == (-127-1)) && (SHRT_MAX == 127) +# define psnip_int8_t short +# elif defined(INT_MIN) && defined(INT_MAX) && (INT_MIN == (-127-1)) && (INT_MAX == 127) +# define psnip_int8_t int +# elif defined(LONG_MIN) && defined(LONG_MAX) && (LONG_MIN == (-127-1)) && (LONG_MAX == 127) +# define psnip_int8_t long +# elif defined(LLONG_MIN) && defined(LLONG_MAX) && (LLONG_MIN == (-127-1)) && (LLONG_MAX == 127) +# define psnip_int8_t long long +# else +# error Unable to locate 8-bit signed integer type. +# endif +# endif +# if !defined(psnip_uint8_t) +# if defined(UCHAR_MAX) && (UCHAR_MAX == 255) +# define psnip_uint8_t unsigned char +# elif defined(USHRT_MAX) && (USHRT_MAX == 255) +# define psnip_uint8_t unsigned short +# elif defined(UINT_MAX) && (UINT_MAX == 255) +# define psnip_uint8_t unsigned int +# elif defined(ULONG_MAX) && (ULONG_MAX == 255) +# define psnip_uint8_t unsigned long +# elif defined(ULLONG_MAX) && (ULLONG_MAX == 255) +# define psnip_uint8_t unsigned long long +# else +# error Unable to locate 8-bit unsigned integer type. +# endif +# endif +# if !defined(psnip_int16_t) +# if defined(CHAR_MIN) && defined(CHAR_MAX) && (CHAR_MIN == (-32767-1)) && (CHAR_MAX == 32767) +# define psnip_int16_t char +# elif defined(SHRT_MIN) && defined(SHRT_MAX) && (SHRT_MIN == (-32767-1)) && (SHRT_MAX == 32767) +# define psnip_int16_t short +# elif defined(INT_MIN) && defined(INT_MAX) && (INT_MIN == (-32767-1)) && (INT_MAX == 32767) +# define psnip_int16_t int +# elif defined(LONG_MIN) && defined(LONG_MAX) && (LONG_MIN == (-32767-1)) && (LONG_MAX == 32767) +# define psnip_int16_t long +# elif defined(LLONG_MIN) && defined(LLONG_MAX) && (LLONG_MIN == (-32767-1)) && (LLONG_MAX == 32767) +# define psnip_int16_t long long +# else +# error Unable to locate 16-bit signed integer type. +# endif +# endif +# if !defined(psnip_uint16_t) +# if defined(UCHAR_MAX) && (UCHAR_MAX == 65535) +# define psnip_uint16_t unsigned char +# elif defined(USHRT_MAX) && (USHRT_MAX == 65535) +# define psnip_uint16_t unsigned short +# elif defined(UINT_MAX) && (UINT_MAX == 65535) +# define psnip_uint16_t unsigned int +# elif defined(ULONG_MAX) && (ULONG_MAX == 65535) +# define psnip_uint16_t unsigned long +# elif defined(ULLONG_MAX) && (ULLONG_MAX == 65535) +# define psnip_uint16_t unsigned long long +# else +# error Unable to locate 16-bit unsigned integer type. +# endif +# endif +# if !defined(psnip_int32_t) +# if defined(CHAR_MIN) && defined(CHAR_MAX) && (CHAR_MIN == (-2147483647-1)) && (CHAR_MAX == 2147483647) +# define psnip_int32_t char +# elif defined(SHRT_MIN) && defined(SHRT_MAX) && (SHRT_MIN == (-2147483647-1)) && (SHRT_MAX == 2147483647) +# define psnip_int32_t short +# elif defined(INT_MIN) && defined(INT_MAX) && (INT_MIN == (-2147483647-1)) && (INT_MAX == 2147483647) +# define psnip_int32_t int +# elif defined(LONG_MIN) && defined(LONG_MAX) && (LONG_MIN == (-2147483647-1)) && (LONG_MAX == 2147483647) +# define psnip_int32_t long +# elif defined(LLONG_MIN) && defined(LLONG_MAX) && (LLONG_MIN == (-2147483647-1)) && (LLONG_MAX == 2147483647) +# define psnip_int32_t long long +# else +# error Unable to locate 32-bit signed integer type. +# endif +# endif +# if !defined(psnip_uint32_t) +# if defined(UCHAR_MAX) && (UCHAR_MAX == 4294967295) +# define psnip_uint32_t unsigned char +# elif defined(USHRT_MAX) && (USHRT_MAX == 4294967295) +# define psnip_uint32_t unsigned short +# elif defined(UINT_MAX) && (UINT_MAX == 4294967295) +# define psnip_uint32_t unsigned int +# elif defined(ULONG_MAX) && (ULONG_MAX == 4294967295) +# define psnip_uint32_t unsigned long +# elif defined(ULLONG_MAX) && (ULLONG_MAX == 4294967295) +# define psnip_uint32_t unsigned long long +# else +# error Unable to locate 32-bit unsigned integer type. +# endif +# endif +# if !defined(psnip_int64_t) +# if defined(CHAR_MIN) && defined(CHAR_MAX) && (CHAR_MIN == (-9223372036854775807LL-1)) && (CHAR_MAX == 9223372036854775807LL) +# define psnip_int64_t char +# elif defined(SHRT_MIN) && defined(SHRT_MAX) && (SHRT_MIN == (-9223372036854775807LL-1)) && (SHRT_MAX == 9223372036854775807LL) +# define psnip_int64_t short +# elif defined(INT_MIN) && defined(INT_MAX) && (INT_MIN == (-9223372036854775807LL-1)) && (INT_MAX == 9223372036854775807LL) +# define psnip_int64_t int +# elif defined(LONG_MIN) && defined(LONG_MAX) && (LONG_MIN == (-9223372036854775807LL-1)) && (LONG_MAX == 9223372036854775807LL) +# define psnip_int64_t long +# elif defined(LLONG_MIN) && defined(LLONG_MAX) && (LLONG_MIN == (-9223372036854775807LL-1)) && (LLONG_MAX == 9223372036854775807LL) +# define psnip_int64_t long long +# else +# error Unable to locate 64-bit signed integer type. +# endif +# endif +# if !defined(psnip_uint64_t) +# if defined(UCHAR_MAX) && (UCHAR_MAX == 18446744073709551615ULL) +# define psnip_uint64_t unsigned char +# elif defined(USHRT_MAX) && (USHRT_MAX == 18446744073709551615ULL) +# define psnip_uint64_t unsigned short +# elif defined(UINT_MAX) && (UINT_MAX == 18446744073709551615ULL) +# define psnip_uint64_t unsigned int +# elif defined(ULONG_MAX) && (ULONG_MAX == 18446744073709551615ULL) +# define psnip_uint64_t unsigned long +# elif defined(ULLONG_MAX) && (ULLONG_MAX == 18446744073709551615ULL) +# define psnip_uint64_t unsigned long long +# else +# error Unable to locate 64-bit unsigned integer type. +# endif +# endif +# endif +#endif diff --git a/ext/bitset/extconf.rb b/ext/bitset/extconf.rb index 1f9bb2a..bdbed02 100644 --- a/ext/bitset/extconf.rb +++ b/ext/bitset/extconf.rb @@ -1,2 +1,2 @@ require 'mkmf' -create_makefile 'bitset' +create_makefile 'bitset/bitset' diff --git a/lib/bitset.rb b/lib/bitset.rb new file mode 100644 index 0000000..2d28064 --- /dev/null +++ b/lib/bitset.rb @@ -0,0 +1,30 @@ +require "bitset/bitset" + +class Bitset + # @return [String] This bitset packed into bytes. + # + # The first 3 bits represent the number of padding bits in the final + # byte of the string. + # + # This is somewhat redundant with Marshal.dump and Marshal.load, but + # it does save a few bytes. + def pack + # Number of bits of zero padding in this representation. + padding_bits = (size+3) & 7 + padding_bits = (padding_bits == 0) ? 0 : (8 - padding_bits) + [("%03b" % padding_bits) + self.to_s].pack("b*") + end + + def inspect + "#{self.class.name}:#{to_s}" + end + + # @param [String] str Output from {#pack} + # + # @return [Bitset] A duplicate of the input to {#pack} + def self.unpack str + bits = str.unpack("b*").first + padding_bits = bits[0...3].to_i(2) + from_s(bits[3 .. -1 - padding_bits]) + end +end diff --git a/spec/bitset_spec.rb b/spec/bitset_spec.rb index fb7d00c..21fd33f 100644 --- a/spec/bitset_spec.rb +++ b/spec/bitset_spec.rb @@ -1,14 +1,22 @@ require 'bitset' describe Bitset do - it 'can be initialized' do + it 'can be initialized by size' do Bitset.new(64) end + it 'can be initialized by array' do + Bitset.new([false, nil, 3, 0]).to_s == "0011" + end + + it 'raises ArgumentError wihen initialized with no argument' do + expect { Bitset.new }.to raise_error(ArgumentError) + end + describe :size do it 'returns the correct size' do - Bitset.new(64).size.should == 64 - Bitset.new(73).size.should == 73 + expect(Bitset.new(64).size).to eq(64) + expect(Bitset.new(73).size).to eq(73) end end @@ -16,17 +24,18 @@ it 'returns True for set bits' do bs = Bitset.new(8) bs[0] = true - bs[0].should == true + expect(bs[0]).to be true end it 'returns False for unset bits' do bs = Bitset.new(8) - bs[0].should == false + expect(bs[0]).to be false end it 'raises an error when accessing out of bound indexes' do bs = Bitset.new(8) expect { bs[8] }.to raise_error(IndexError) + expect { bs[-1] }.to raise_error(IndexError) end end @@ -35,23 +44,23 @@ bs = Bitset.new(8) bs[0] = true - bs[0].should == true + expect(bs[0]).to be true bs[1] = 123 - bs[1].should == true + expect(bs[1]).to be true bs[2] = "woo" - bs[2].should == true + expect(bs[2]).to be true end it 'sets False for falsey values' do bs = Bitset.new(8) bs[0] = false - bs[0].should == false + expect(bs[0]).to be false bs[1] = nil - bs[1].should == false + expect(bs[1]).to be false end it 'raises an error when setting out of bound indexes' do @@ -65,9 +74,10 @@ bs = Bitset.new(8) bs.set 1,2,3 - bs[1].should == true - bs[2].should == true - bs[3].should == true + expect(bs[1]).to be true + expect(bs[0]).to be false + expect(bs[4]).to be false + expect(bs[3]).to be true end end @@ -77,9 +87,9 @@ bs.set 1,2,3 bs.clear 1,3 - bs[1].should == false - bs[2].should == true - bs[3].should == false + expect(bs[1]).to be false + expect(bs[2]).to be true + expect(bs[3]).to be false end end @@ -87,13 +97,13 @@ it 'returns True if all bits indexed are set' do bs = Bitset.new(8) bs.set 1, 4, 5 - bs.set?(1,4,5).should == true + expect(bs.set?(1,4,5)).to be true end it 'returns False if not all bits indexed are set' do bs = Bitset.new(8) bs.set 1, 4 - bs.set?(1,4,5).should == false + expect(bs.set?(1,4,5)).to be false end end @@ -101,44 +111,48 @@ it 'returns True if all bits indexed are clear' do bs = Bitset.new(8) bs.set 1, 4, 5 - bs.clear?(0,2,3,6).should == true + expect(bs.clear?(0,2,3,6)).to be true end - it 'returns works with the full range of 64 bit values' do + it 'works with the full range of 64 bit values' do bs = Bitset.new(68) bs.set 0, 2, 66 - bs.clear?(32, 33, 34).should == true + expect(bs.clear?(32, 33, 34)).to be true end - it 'returns False if not all bits indexed are clear' do + it 'returns false if not all bits indexed are clear' do bs = Bitset.new(8) bs.set 1, 4 - bs.clear?(1,2,6).should == false + expect(bs.clear?(1,2,6)).to be false end end describe :cardinality do it 'returns the number of bits set' do - bs = Bitset.new(8) - bs.cardinality.should == 0 + bs = Bitset.new(64) + expect(bs.cardinality).to eq(0) bs[0] = true - bs.cardinality.should == 1 + expect(bs.cardinality).to eq(1) bs[1] = true - bs.cardinality.should == 2 + expect(bs.cardinality).to eq(2) bs[2] = true - bs.cardinality.should == 3 + expect(bs.cardinality).to eq(3) + + expect(bs.not.cardinality).to eq(bs.size - bs.cardinality) end it '... even for large numbers of bits' do bs = Bitset.new(10_000) - bs.set(*(0...5000).to_a) - bs.cardinality.should == 5000 + size = 5000 + bs.set((0...size).to_a) + expect(bs.cardinality).to eq(size) bs = Bitset.from_s "01001101000000000000000000000011000010100100000000000000010000101000000000000000100000000100000000000010100100010000000010000100000100000001001000110000000000100010000000010100000000000000110000000000000000000000000100000000100010010000000000000000000001000000000000000000000000000001000000000000000000000000000100000000010010000000000000000000100100000000000000001000000010000001000000000000001000001100010001000000000000001000001000001000000000000001100010000010010001000000010000100000000000110000" - bs.cardinality.should == 63 + expect(bs.cardinality).to eq(63) + expect(bs.not.cardinality).to eq(bs.size - bs.cardinality) end end @@ -151,8 +165,8 @@ bs2.set 1, 2, 4, 6 bs3 = bs1 & bs2 - bs3.set?(1,4).should == true - bs3.clear?(0,2,3,5,6,7).should == true + expect(bs3.set?(1,4)).to be true + expect(bs3.clear?(0,2,3,5,6,7)).to be true end end @@ -165,8 +179,11 @@ bs2.set 1, 2, 4, 6 bs3 = bs1 | bs2 - bs3.set?(1,2,4,6,7).should == true - bs3.clear?(0,3,5).should == true + expect(bs3.set?(1,2,4,6,7)).to be true + expect(bs3.clear?(0,3,5)).to be true + end + it 'throws if size mismatch' do + expect { Bitset.new(3) | Bitset.new(7) }.to raise_error(ArgumentError) end end @@ -179,8 +196,11 @@ bs2.set 1, 2, 4, 6 bs3 = bs1 - bs2 - bs3.set?(7).should == true - bs3.clear?(0,1,2,3,4,5,6).should == true + expect(bs3.set?(7)).to be true + expect(bs3.clear?(0,1,2,3,4,5,6)).to be true + end + it 'throws if size mismatch' do + expect { Bitset.new(3) - Bitset.new(7) }.to raise_error(ArgumentError) end end @@ -193,19 +213,23 @@ bs2.set 1, 2, 4, 6 bs3 = bs1 ^ bs2 - bs3.set?(2,6,7).should == true - bs3.clear?(0,1,3,4,5).should == true + expect(bs3.set?(2,6,7)).to be true + expect(bs3.clear?(0,1,3,4,5)).to be true + end + + it 'throws if size mismatch' do + expect { Bitset.new(3) ^ Bitset.new(7) }.to raise_error(ArgumentError) end end describe :not do - it "returns a new Bitset with is the not of one Bitset" do + it "returns a new Bitset which is the not of one Bitset" do bs1 = Bitset.new(8) bs1.set 1, 4, 7 bs2 = bs1.not - bs2.set?(0, 2, 3, 5, 6).should == true - bs2.clear?(1, 4, 7).should == true + expect(bs2.set?(0, 2, 3, 5, 6)).to be true + expect(bs2.clear?(1, 4, 7)).to be true end end @@ -217,7 +241,7 @@ bs2 = Bitset.new(8) bs2.set 1, 2, 4, 6 - bs1.hamming(bs2).should == 3 + expect(bs1.hamming(bs2)).to eq(3) end end @@ -228,10 +252,57 @@ i = 0 bs.each do |bit| - bit.should == bs[i] + expect(bit).to be bs[i] i += 1 end - i.should == 4 + expect(i).to eq(4) + end + end + + describe :each_set do + it 'iterates over each set bit in the Bitset' do + bs = Bitset.new(4) + sets = [0,3] + bs.set sets + sets2 = [] + bs.each_set { |bit| sets2 << bit } + expect(sets2).to eq(sets) + end + + it 'without a block, it returns an array of set bits' do + bs = Bitset.new(4) + sets = [0,3] + bs.set(*sets) + expect(bs.each_set).to eq(sets) + end + + it 'behaves properly with arguments' do + bs = Bitset.from_s "110110011" + expect { bs.each_set 1, 2, 3 }.to raise_error(ArgumentError) + expect(bs.each_set 2).to eq(3) + expect(bs.each_set -3, 2).to eq([4,7]) + end + end + + describe :empty? do + it 'returns empty only if all zeroes' do + expect(Bitset.new(225).tap { |bs| bs[133] = true }.empty?).to be false + expect(Bitset.new(0).empty?).to be true + expect(Bitset.new(225).empty?).to be true + end + end + + describe :dup do + it "returns a duplicate" do + bs = Bitset.from_s("11011") + expect(bs.dup.tap { |bs| bs.clear 1,3 }.to_s).to eq("10001") + expect(bs.to_s).to eq("11011") + end + end + + describe :clone do + it "works" do + expect(Bitset.new(0).clone.to_s).to eq("") end end @@ -239,18 +310,18 @@ it 'correctly prints out a binary string' do bs = Bitset.new(4) bs.set 0, 2 - bs.to_s.should == "1010" + expect(bs.to_s).to eq("1010") bs = Bitset.new(68) bs.set 0, 2, 66 - bs.to_s.should == "101" + ("0" * 63) + "10" + expect(bs.to_s).to eq("101" + ("0" * 63) + "10") end end describe :from_s do it 'correctly creates a bitmap from a binary string' do bs = Bitset.from_s("10101") - bs.set?(0, 2, 4).should == true + expect(bs.set?(0, 2, 4)).to be true end end @@ -260,8 +331,99 @@ bs.set 1, 65 serialized = Marshal.load(Marshal.dump(bs)) - serialized.set?(1, 65).should == true - serialized.cardinality.should == 2 + expect(serialized.set?(1, 65)).to be true + expect(serialized.cardinality).to eq(2) + end + end + + describe :union! do + it 'acts like |=' do + bs = Bitset.from_s "11011" + bs2 = Bitset.from_s "01111" + bs3 = bs.dup + bs.union!(bs2) + expect(bs).to eq(bs3 | bs2) + end + + it 'throws if size mismatch' do + expect { Bitset.new(3).union!(Bitset.new(7)) }.to raise_error(ArgumentError) + end + end + + describe :intersect! do + it 'acts like &=' do + bs = Bitset.from_s "11011" + bs2 = Bitset.from_s "01111" + bs3 = bs.dup + bs.intersect!(bs2) + expect(bs).to eq(bs3 & bs2) + end + end + + describe :xor! do + it 'acts like ^=' do + bs = Bitset.from_s "11011" + bs2 = Bitset.from_s "01111" + bs3 = bs.dup + bs.xor!(bs2) + expect(bs).to eq(bs3 ^ bs2) + end + + it 'throws if size mismatch' do + expect { Bitset.new(3).xor!(Bitset.new(7)) }.to raise_error(ArgumentError) + end + end + + describe :difference! do + it 'acts like -=' do + bs = Bitset.from_s "11011" + bs2 = Bitset.from_s "01111" + bs3 = bs.dup + bs.difference!(bs2) + expect(bs).to eq(bs3 - bs2) + end + end + + describe :reset! do + it 'causes empty? to become true' do + bs = Bitset.from_s "11011" + bs.reset! + expect(bs.empty?).to be true + end + end + + describe :to_a do + it "can convert to an array of set positions" do + bs = Bitset.new(68) + bs.set 1, 64, 65 + + expect(bs.to_a).to eq([1, 64, 65]) + end + end + + describe :to_binary_array do + it "can convert to an array of 1s and 0s" do + bs = Bitset.new(68) + bs.set 1, 64, 65 + + expect(bs.to_binary_array.values_at(1, 64, 65, 66)).to eq([1, 1, 1, 0]) + end + end + + describe :pack do + it 'round trips with #unpack' do + %w{110 00010 101010011101011100000110011010110011010001}.each do |bits| + bitset = Bitset.from_s bits + packed = bitset.pack + unpacked = Bitset.unpack(packed) + expect(bitset).to eq(unpacked) + end + end + end + + describe :inspect do + it "returns expected output" do + expect(Bitset.from_s("1011").inspect).to eq("Bitset:1011") end end end