bitary 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +6 -4
- data/lib/bitary/bitwarr.rb +187 -0
- data/lib/bitary/decorator/kwargs_validator.rb +174 -0
- data/lib/bitary/decorator/single_method/non_nil_enforcer.rb +15 -0
- data/lib/bitary/decorator/single_method/truthy_enforcer.rb +15 -0
- data/lib/bitary/decorator/single_method.rb +22 -0
- data/lib/bitary/decorator.rb +47 -0
- data/lib/bitary/factory.rb +25 -0
- data/lib/bitary/handler/append.rb +12 -10
- data/lib/bitary/handler/get.rb +18 -12
- data/lib/bitary/handler/set.rb +18 -12
- data/lib/bitary/handler/unset.rb +20 -11
- data/lib/bitary/handler.rb +12 -0
- data/lib/bitary/mapper/int_to_bit.rb +15 -0
- data/lib/bitary/mapper/obj_to_bit.rb +14 -0
- data/lib/bitary/mapper.rb +16 -0
- data/lib/bitary/version.rb +1 -1
- data/lib/bitary.rb +22 -124
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf08e150b62f660fa24f19420e8f9ec2b4c44a0ff88bc8466a1bc13339de1ee0
|
4
|
+
data.tar.gz: 5837fb427a2332f8518b8ed247edd4ea750dd63a651a3e546c8efcf783dd9daa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 564e33ae146b3590d8f41540cde48d3a15dc7536b1dd3592fc0601fc2a85010710a370a1082c0a1134a249f6102526d6b2a859950c179b1b5c59d27ad4111e50
|
7
|
+
data.tar.gz: 6eeeb1fd6d855b0c87cb8330ea383f3b4136701d028d2996130215a8f5f4edea632a3ca6047bac3fba0a0079d9b83818a3cc0e7fb083ab883a6257a46d3e2e83
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
## [0.1.3] - 2024-03-29
|
2
|
+
|
3
|
+
- fix some missing require statements
|
4
|
+
- further refactoring of Bitary class
|
5
|
+
|
6
|
+
## [0.1.2] - 2024-03-29
|
7
|
+
|
8
|
+
- internal refactoring of the Bitary class
|
9
|
+
- addition of integer size enum
|
10
|
+
|
1
11
|
## [0.1.1] - 2024-03-26
|
2
12
|
|
3
13
|
- Change root namespace to a class, instead of a module
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Ruby-based implementation of the bit array data structure.
|
4
4
|
|
5
|
-
It's still under development, but as of now, it implements simple and well-optimized logic allowing you to set, unset and retrieve bits.
|
5
|
+
It's still under development, but as of now, it implements simple and well-optimized logic allowing you to set, unset and retrieve bits (as well as some extra features, see below).
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
@@ -24,10 +24,12 @@ Documentation still needs to be written, but here is a breakdown
|
|
24
24
|
of the main capabilities brought by the current bit array implementation:
|
25
25
|
|
26
26
|
```ruby
|
27
|
+
require 'bitary'
|
28
|
+
|
27
29
|
bit_array_sz = Bitary.new(128) # give an explicit size. Defaults to 64 bits used per item
|
28
30
|
bit_array_ar = Bitary.new(
|
29
31
|
[255, 10, 20],
|
30
|
-
bits_per_item: 8
|
32
|
+
bits_per_item: Bitary::BYTE # 8 bits
|
31
33
|
) # create based on some integer array
|
32
34
|
|
33
35
|
bit_array_sz.bits_per_item # 64
|
@@ -55,13 +57,13 @@ bit_array_ar.to_a # [127, 10, 20]
|
|
55
57
|
bit_array_ar.to_s # "01111111 00001010 00010100"
|
56
58
|
|
57
59
|
# increase/decrease bits used per item
|
58
|
-
bit_array_ar.bits_per_item = 64
|
60
|
+
bit_array_ar.bits_per_item = Bitary::LONG # 64 bits
|
59
61
|
bit_array_ar.to_a # [8_325_652]
|
60
62
|
bit_array_ar.to_s # "0000000000000000000000000000000000000000011111110000101000010100"
|
61
63
|
|
62
64
|
bit_array_sz.bits_per_item # 64
|
63
65
|
bit_array_sz.to_a # [1_099_511_627_776, 0]
|
64
|
-
bit_array_sz.bits_per_item = 32
|
66
|
+
bit_array_sz.bits_per_item = Bitary::INT # 32 bits
|
65
67
|
bit_array_sz.to_a # [256, 0, 0, 0]
|
66
68
|
```
|
67
69
|
|
@@ -0,0 +1,187 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Bitary
|
4
|
+
class Bitwarr
|
5
|
+
attr_reader :bpi
|
6
|
+
|
7
|
+
def initialize(initial_data, bpi: Bitary::LONG)
|
8
|
+
check_initial_data(initial_data)
|
9
|
+
check_bpi(bpi)
|
10
|
+
|
11
|
+
@bpi = bpi
|
12
|
+
@bitsize = init_bitsize(initial_data)
|
13
|
+
@array = init_array(initial_data)
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_missing(method, *, &)
|
17
|
+
@array.respond_to?(method) ? @array.send(method, *, &) : super
|
18
|
+
end
|
19
|
+
|
20
|
+
def respond_to_missing?(method, include_all = false)
|
21
|
+
@array.respond_to?(method, include_all) || super
|
22
|
+
end
|
23
|
+
|
24
|
+
def bitsize(bit_index = nil)
|
25
|
+
if bit_index.nil?
|
26
|
+
@bitsize
|
27
|
+
else
|
28
|
+
check_bit_index(bit_index)
|
29
|
+
|
30
|
+
last_index = @array.length - 1
|
31
|
+
if item_index(bit_index) == last_index
|
32
|
+
@bitsize - (last_index * @bpi)
|
33
|
+
else
|
34
|
+
@bpi
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def [](bit_index)
|
40
|
+
@array[item_index(bit_index)]
|
41
|
+
end
|
42
|
+
|
43
|
+
def []=(bit_index, value)
|
44
|
+
raise ArgumentError unless value.is_a?(Integer)
|
45
|
+
|
46
|
+
@array[item_index(bit_index)] = value
|
47
|
+
end
|
48
|
+
|
49
|
+
def relative_bit_index(bit_index)
|
50
|
+
check_bit_index(bit_index)
|
51
|
+
|
52
|
+
bit_index % @bpi
|
53
|
+
end
|
54
|
+
|
55
|
+
def item_index(bit_index)
|
56
|
+
check_bit_index(bit_index)
|
57
|
+
|
58
|
+
bit_index / @bpi
|
59
|
+
end
|
60
|
+
|
61
|
+
def bit_at(index)
|
62
|
+
operate_bit_at(:get, index)
|
63
|
+
end
|
64
|
+
|
65
|
+
def bit_at!(index)
|
66
|
+
operate_bit_at!(:set, index)
|
67
|
+
end
|
68
|
+
|
69
|
+
def unbit_at!(index)
|
70
|
+
operate_bit_at!(:unset, index)
|
71
|
+
end
|
72
|
+
|
73
|
+
def each_byte(&)
|
74
|
+
decrease_items_size(@array, Bitary::BYTE, @bpi).each(&)
|
75
|
+
end
|
76
|
+
|
77
|
+
def to_s
|
78
|
+
@array.map { |item| format("%0#{@bpi}d", item.to_s(2)) }.join(' ')
|
79
|
+
end
|
80
|
+
|
81
|
+
def bpi=(value)
|
82
|
+
check_bpi(value)
|
83
|
+
|
84
|
+
update_items_size!(value)
|
85
|
+
|
86
|
+
@bpi = value
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def init_bitsize(initial_data)
|
92
|
+
initial_data.is_a?(Array) ? @bpi * initial_data.length : initial_data
|
93
|
+
end
|
94
|
+
|
95
|
+
def init_array(initial_data)
|
96
|
+
if initial_data.is_a?(Array)
|
97
|
+
initial_data.clone
|
98
|
+
else
|
99
|
+
[0] * (@bitsize / @bpi.to_f).ceil
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def check_initial_data(initial_data)
|
104
|
+
raise ArgumentError unless [Array, Integer].include?(initial_data.class)
|
105
|
+
end
|
106
|
+
|
107
|
+
def check_bit_index(bit_index)
|
108
|
+
raise ArgumentError unless bit_index.is_a?(Integer)
|
109
|
+
raise IndexError if bit_index.negative? || bit_index >= @bitsize
|
110
|
+
end
|
111
|
+
|
112
|
+
def check_bpi(bpi)
|
113
|
+
raise ArgumentError unless [
|
114
|
+
Bitary::BYTE,
|
115
|
+
Bitary::SHORT,
|
116
|
+
Bitary::INT,
|
117
|
+
Bitary::LONG
|
118
|
+
].include?(bpi)
|
119
|
+
end
|
120
|
+
|
121
|
+
def operate_bit_at(operation, index)
|
122
|
+
Factory
|
123
|
+
.make("Handler::#{operation.capitalize}", self[index])
|
124
|
+
.execute(
|
125
|
+
index: relative_bit_index(index),
|
126
|
+
size: @bpi
|
127
|
+
)
|
128
|
+
end
|
129
|
+
|
130
|
+
def operate_bit_at!(operation, index)
|
131
|
+
self[index] = operate_bit_at(operation, index)
|
132
|
+
end
|
133
|
+
|
134
|
+
def update_items_size!(value)
|
135
|
+
if value > @bpi
|
136
|
+
increase_items_size!(value)
|
137
|
+
else
|
138
|
+
decrease_items_size!(value)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def increase_items_size(array, new_size, bpi)
|
143
|
+
processed_bits = 0
|
144
|
+
array.each_with_object([0]) do |value, acc|
|
145
|
+
offset = bpi
|
146
|
+
if processed_bits >= new_size
|
147
|
+
offset = 0
|
148
|
+
acc << 0
|
149
|
+
processed_bits = 0
|
150
|
+
end
|
151
|
+
|
152
|
+
acc[-1] = Factory.make('Handler::Append', acc[-1]).execute(
|
153
|
+
offset:,
|
154
|
+
value:
|
155
|
+
)
|
156
|
+
processed_bits += bpi
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def increase_items_size!(value)
|
161
|
+
@array = increase_items_size(@array, value, @bpi)
|
162
|
+
end
|
163
|
+
|
164
|
+
def decrease_items_size(array, new_size, bpi)
|
165
|
+
array.each_with_object([]) do |item, acc|
|
166
|
+
acc.concat(explode_item(item, new_size, bpi))
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def decrease_items_size!(value)
|
171
|
+
@array = decrease_items_size(@array, value, @bpi)
|
172
|
+
end
|
173
|
+
|
174
|
+
def explode_item(item, new_size, bpi)
|
175
|
+
res = []
|
176
|
+
offset = bpi
|
177
|
+
mask = (2**new_size) - 1
|
178
|
+
|
179
|
+
while offset.positive?
|
180
|
+
offset -= new_size
|
181
|
+
res << ((item >> offset) & mask)
|
182
|
+
end
|
183
|
+
|
184
|
+
res
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Bitary
|
4
|
+
class Decorator
|
5
|
+
class KwargsValidator < Bitary::Decorator
|
6
|
+
SPEC_KEYS = %i[required default type predicate].freeze
|
7
|
+
|
8
|
+
def initialize(wrappee, spec)
|
9
|
+
super(wrappee) { |method| spec.key?(method) }
|
10
|
+
@spec = check_spec(@wrappee, spec)
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def precall(method, *, **)
|
16
|
+
super(method, *, **check_kwargs(@spec, method, **))
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def check_spec(wrappee, spec)
|
22
|
+
raise ArgumentError unless spec.is_a?(Hash)
|
23
|
+
|
24
|
+
spec.each do |method, method_spec|
|
25
|
+
check_spec_method(wrappee, method)
|
26
|
+
check_spec_method_value(method_spec)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def check_kwargs(spec, method, **kwargs)
|
31
|
+
check_unexpected_user_kwargs(kwargs, spec[method])
|
32
|
+
check_kwargs_against_spec(kwargs, spec[method])
|
33
|
+
end
|
34
|
+
|
35
|
+
def check_spec_method(wrappee, method)
|
36
|
+
raise ArgumentError unless wrappee.respond_to?(method)
|
37
|
+
end
|
38
|
+
|
39
|
+
def check_spec_method_value(method_spec)
|
40
|
+
raise ArgumentError unless method_spec.is_a?(Hash)
|
41
|
+
|
42
|
+
method_spec.each_value do |arg_spec|
|
43
|
+
raise ArgumentError unless arg_spec.is_a?(Hash)
|
44
|
+
|
45
|
+
check_arg_spec(arg_spec)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def check_arg_spec(arg_spec)
|
50
|
+
raise ArgumentError unless arg_spec.keys.all? do |spec_key|
|
51
|
+
SPEC_KEYS.include?(spec_key)
|
52
|
+
end
|
53
|
+
|
54
|
+
arg_spec.each do |spec_key, spec_value|
|
55
|
+
check_arg_spec_entry(spec_key, spec_value)
|
56
|
+
end
|
57
|
+
|
58
|
+
return unless arg_spec.key?(:required) && arg_spec[:required]
|
59
|
+
raise ArgumentError if arg_spec.key?(:default)
|
60
|
+
end
|
61
|
+
|
62
|
+
def check_arg_spec_entry(key, value)
|
63
|
+
case key
|
64
|
+
when :required then check_required(value)
|
65
|
+
when :default then check_default(value)
|
66
|
+
when :type then check_type(value)
|
67
|
+
when :predicate then check_predicate(value)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def check_required(value)
|
72
|
+
raise ArgumentError unless [true, false].include?(value)
|
73
|
+
end
|
74
|
+
|
75
|
+
def check_default(value)
|
76
|
+
# NOTHING TODO
|
77
|
+
end
|
78
|
+
|
79
|
+
def check_type(value)
|
80
|
+
raise ArgumentError unless value.is_a?(Class)
|
81
|
+
end
|
82
|
+
|
83
|
+
def check_predicate(value)
|
84
|
+
raise ArgumentError unless value.is_a?(Hash)
|
85
|
+
|
86
|
+
check_predicate_keys(value)
|
87
|
+
check_predicate_values(value)
|
88
|
+
end
|
89
|
+
|
90
|
+
def check_predicate_keys(value)
|
91
|
+
available_keys = %i[callback error]
|
92
|
+
|
93
|
+
raise ArgumentError unless value.keys.all? do |key|
|
94
|
+
available_keys.include?(key)
|
95
|
+
end
|
96
|
+
|
97
|
+
raise KeyError unless value.key?(:callback) && value.key?(:error)
|
98
|
+
end
|
99
|
+
|
100
|
+
def check_predicate_values(value)
|
101
|
+
raise ArgumentError unless value[:callback].is_a?(Proc)
|
102
|
+
raise ArgumentError unless value[:error].is_a?(Class)
|
103
|
+
raise ArgumentError unless value[:error] < Exception
|
104
|
+
end
|
105
|
+
|
106
|
+
def check_unexpected_user_kwargs(user_kwargs, method_spec)
|
107
|
+
raise ArgumentError unless user_kwargs.keys.all? do |key|
|
108
|
+
method_spec.include?(key)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def check_kwargs_against_spec(user_kwargs, method_spec)
|
113
|
+
predicates = []
|
114
|
+
|
115
|
+
parsed_kwargs = method_spec.reduce({}) do |acc, entry|
|
116
|
+
kwarg_name, kwarg_spec = entry
|
117
|
+
loaded_spec = load_spec(kwarg_spec)
|
118
|
+
|
119
|
+
validate_required(user_kwargs, loaded_spec, kwarg_name)
|
120
|
+
validate_type(user_kwargs, loaded_spec, kwarg_name)
|
121
|
+
predicates << loaded_spec[:predicate]
|
122
|
+
|
123
|
+
acc.merge(
|
124
|
+
kwarg_name => compute_value(user_kwargs, loaded_spec, kwarg_name)
|
125
|
+
)
|
126
|
+
end
|
127
|
+
|
128
|
+
predicates.each do |predicate|
|
129
|
+
validate_predicate(parsed_kwargs, predicate)
|
130
|
+
end
|
131
|
+
|
132
|
+
parsed_kwargs
|
133
|
+
end
|
134
|
+
|
135
|
+
def load_spec(kwarg_spec)
|
136
|
+
{
|
137
|
+
required: kwarg_spec[:required] || false,
|
138
|
+
default: kwarg_spec[:default],
|
139
|
+
type: kwarg_spec[:type] || Object,
|
140
|
+
predicate: kwarg_spec[:predicate] || {
|
141
|
+
callback: ->(_value) { true },
|
142
|
+
error: ArgumentError
|
143
|
+
}
|
144
|
+
}
|
145
|
+
end
|
146
|
+
|
147
|
+
def validate_required(user_kwargs, spec, expected_key)
|
148
|
+
return unless spec[:required]
|
149
|
+
|
150
|
+
raise KeyError unless user_kwargs.key?(expected_key)
|
151
|
+
end
|
152
|
+
|
153
|
+
def validate_type(user_kwargs, spec, expected_key)
|
154
|
+
return unless user_kwargs.key?(expected_key)
|
155
|
+
|
156
|
+
raise ArgumentError unless user_kwargs[expected_key].is_a?(spec[:type])
|
157
|
+
end
|
158
|
+
|
159
|
+
def validate_predicate(parsed_kwargs, predicate)
|
160
|
+
return if predicate[:callback].call(**parsed_kwargs)
|
161
|
+
|
162
|
+
raise predicate[:error]
|
163
|
+
end
|
164
|
+
|
165
|
+
def compute_value(user_kwargs, spec, expected_key)
|
166
|
+
if user_kwargs.key?(expected_key)
|
167
|
+
user_kwargs[expected_key]
|
168
|
+
else
|
169
|
+
spec[:default]
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Bitary
|
4
|
+
class Decorator
|
5
|
+
class SingleMethod < Bitary::Decorator
|
6
|
+
class NonNilEnforcer < Bitary::Decorator::SingleMethod
|
7
|
+
protected
|
8
|
+
|
9
|
+
def postcall(resp)
|
10
|
+
(resp.nil? and raise TypeError) || resp
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Bitary
|
4
|
+
class Decorator
|
5
|
+
class SingleMethod < Bitary::Decorator
|
6
|
+
class TruthyEnforcer < Bitary::Decorator::SingleMethod
|
7
|
+
protected
|
8
|
+
|
9
|
+
def postcall(resp)
|
10
|
+
resp or raise TypeError
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'single_method/non_nil_enforcer'
|
4
|
+
require_relative 'single_method/truthy_enforcer'
|
5
|
+
|
6
|
+
class Bitary
|
7
|
+
class Decorator
|
8
|
+
class SingleMethod < Bitary::Decorator
|
9
|
+
def initialize(wrappee, method)
|
10
|
+
super(wrappee) { |meth| meth == method }
|
11
|
+
check_method(wrappee, method)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def check_method(wrappee, method)
|
17
|
+
raise ArgumentError unless method.is_a?(Symbol)
|
18
|
+
raise NoMethodError unless wrappee.respond_to?(method)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'decorator/kwargs_validator'
|
4
|
+
require_relative 'decorator/single_method'
|
5
|
+
|
6
|
+
class Bitary
|
7
|
+
class Decorator
|
8
|
+
def initialize(wrappee, &proc)
|
9
|
+
@wrappee = wrappee
|
10
|
+
@predicate = proc || ->(_method) { true }
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(method, *, **, &)
|
14
|
+
if @wrappee.respond_to?(method)
|
15
|
+
if @predicate.call(method)
|
16
|
+
args, kwargs = precall(method, *, **)
|
17
|
+
resp = @wrappee.send(method, *args, **kwargs, &)
|
18
|
+
postcall(resp)
|
19
|
+
else
|
20
|
+
@wrappee.send(method, *, **, &)
|
21
|
+
end
|
22
|
+
else
|
23
|
+
super
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def respond_to_missing?(method, include_all = false)
|
28
|
+
@wrappee.respond_to?(method, include_all) || super
|
29
|
+
end
|
30
|
+
|
31
|
+
def wrappee
|
32
|
+
res = @wrappee
|
33
|
+
res = res.wrappee while res.respond_to?(:wrappee)
|
34
|
+
res
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
def precall(_method, *args, **kwargs)
|
40
|
+
[args, kwargs]
|
41
|
+
end
|
42
|
+
|
43
|
+
def postcall(resp)
|
44
|
+
resp
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Bitary
|
4
|
+
class Factory
|
5
|
+
private_class_method :new
|
6
|
+
@memo = {}
|
7
|
+
|
8
|
+
def self.make(name, *, **)
|
9
|
+
raise ArgumentError unless name.is_a?(String)
|
10
|
+
|
11
|
+
name.split('::').reduce(Bitary) do |cls, str|
|
12
|
+
cls.const_get(str)
|
13
|
+
end.new(*, **)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.make_memo(name, *args, **kwargs)
|
17
|
+
raise ArgumentError unless name.is_a?(String)
|
18
|
+
if @memo.key?(name.to_sym) && !args.empty? && !kwargs.empty?
|
19
|
+
raise ArgumentError
|
20
|
+
end
|
21
|
+
|
22
|
+
@memo[name.to_sym] ||= make(name, *args, **kwargs)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -3,17 +3,19 @@
|
|
3
3
|
class Bitary
|
4
4
|
class Handler
|
5
5
|
class Append < Bitary::Handler
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
6
|
+
SPEC = {
|
7
|
+
offset: {
|
8
|
+
required: true,
|
9
|
+
type: Integer
|
10
|
+
},
|
11
|
+
value: {
|
12
|
+
required: true,
|
13
|
+
type: Integer
|
14
|
+
}
|
15
|
+
}.freeze
|
15
16
|
|
16
|
-
|
17
|
+
def execute(**kwargs)
|
18
|
+
(@value << kwargs[:offset]) | kwargs[:value]
|
17
19
|
end
|
18
20
|
end
|
19
21
|
end
|
data/lib/bitary/handler/get.rb
CHANGED
@@ -3,19 +3,25 @@
|
|
3
3
|
class Bitary
|
4
4
|
class Handler
|
5
5
|
class Get < Bitary::Handler
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
6
|
+
SPEC = {
|
7
|
+
index: {
|
8
|
+
required: true,
|
9
|
+
type: Integer,
|
10
|
+
predicate: {
|
11
|
+
callback: lambda do |**kwargs|
|
12
|
+
kwargs[:index] >= 0 && kwargs[:index] < kwargs[:size]
|
13
|
+
end,
|
14
|
+
error: IndexError
|
15
|
+
}
|
16
|
+
},
|
17
|
+
size: {
|
18
|
+
required: true,
|
19
|
+
type: Integer
|
20
|
+
}
|
21
|
+
}.freeze
|
15
22
|
|
16
|
-
|
17
|
-
|
18
|
-
(@value >> (size - index - 1)) & 0x1
|
23
|
+
def execute(**kwargs)
|
24
|
+
(@value >> (kwargs[:size] - kwargs[:index] - 1)) & 0x1
|
19
25
|
end
|
20
26
|
end
|
21
27
|
end
|
data/lib/bitary/handler/set.rb
CHANGED
@@ -3,19 +3,25 @@
|
|
3
3
|
class Bitary
|
4
4
|
class Handler
|
5
5
|
class Set < Bitary::Handler
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
6
|
+
SPEC = {
|
7
|
+
index: {
|
8
|
+
required: true,
|
9
|
+
type: Integer,
|
10
|
+
predicate: {
|
11
|
+
callback: lambda do |**kwargs|
|
12
|
+
kwargs[:index] >= 0 && kwargs[:index] < kwargs[:size]
|
13
|
+
end,
|
14
|
+
error: IndexError
|
15
|
+
}
|
16
|
+
},
|
17
|
+
size: {
|
18
|
+
required: true,
|
19
|
+
type: Integer
|
20
|
+
}
|
21
|
+
}.freeze
|
15
22
|
|
16
|
-
|
17
|
-
|
18
|
-
@value | (2**(size - index - 1))
|
23
|
+
def execute(**kwargs)
|
24
|
+
@value | (2**(kwargs[:size] - kwargs[:index] - 1))
|
19
25
|
end
|
20
26
|
end
|
21
27
|
end
|
data/lib/bitary/handler/unset.rb
CHANGED
@@ -3,19 +3,28 @@
|
|
3
3
|
class Bitary
|
4
4
|
class Handler
|
5
5
|
class Unset < Bitary::Handler
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
6
|
+
SPEC = {
|
7
|
+
index: {
|
8
|
+
required: true,
|
9
|
+
type: Integer,
|
10
|
+
predicate: {
|
11
|
+
callback: lambda do |**kwargs|
|
12
|
+
kwargs[:index] >= 0 && kwargs[:index] < kwargs[:size]
|
13
|
+
end,
|
14
|
+
error: IndexError
|
15
|
+
}
|
16
|
+
},
|
17
|
+
size: {
|
18
|
+
required: true,
|
19
|
+
type: Integer
|
20
|
+
}
|
21
|
+
}.freeze
|
15
22
|
|
16
|
-
|
23
|
+
def execute(**kwargs)
|
24
|
+
mask =
|
25
|
+
(2**kwargs[:size]) - 1 - (2**(kwargs[:size] - kwargs[:index] - 1))
|
17
26
|
|
18
|
-
@value &
|
27
|
+
@value & mask
|
19
28
|
end
|
20
29
|
end
|
21
30
|
end
|
data/lib/bitary/handler.rb
CHANGED
@@ -1,11 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'handler/set'
|
4
|
+
require_relative 'handler/unset'
|
5
|
+
require_relative 'handler/get'
|
6
|
+
require_relative 'handler/append'
|
4
7
|
|
5
8
|
class Bitary
|
6
9
|
class Handler
|
10
|
+
SPEC = {}.freeze
|
11
|
+
|
7
12
|
attr_reader :value
|
8
13
|
|
14
|
+
def self.new(*arg, **kwargs)
|
15
|
+
Decorator::SingleMethod::KwargsValidator.new(
|
16
|
+
super,
|
17
|
+
{ execute: self::SPEC }
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
9
21
|
def initialize(value)
|
10
22
|
raise ArgumentError unless value.is_a?(Integer)
|
11
23
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'mapper/int_to_bit'
|
4
|
+
require_relative 'mapper/obj_to_bit'
|
5
|
+
|
6
|
+
class Bitary
|
7
|
+
class Mapper
|
8
|
+
def self.new(*arg, **kwargs)
|
9
|
+
Decorator::SingleMethod::NonNilEnforcer.new(super, :map)
|
10
|
+
end
|
11
|
+
|
12
|
+
def map(value)
|
13
|
+
raise NotImplementedError
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/bitary/version.rb
CHANGED
data/lib/bitary.rb
CHANGED
@@ -2,53 +2,28 @@
|
|
2
2
|
|
3
3
|
require_relative 'bitary/size'
|
4
4
|
require_relative 'bitary/version'
|
5
|
+
require_relative 'bitary/handler'
|
6
|
+
require_relative 'bitary/factory'
|
7
|
+
require_relative 'bitary/bitwarr'
|
8
|
+
require_relative 'bitary/decorator'
|
9
|
+
require_relative 'bitary/mapper'
|
5
10
|
|
6
11
|
class Bitary
|
7
12
|
include Size
|
8
13
|
|
9
|
-
|
10
|
-
|
11
|
-
def initialize(initial_data, bits_per_item: LONG)
|
12
|
-
raise ArgumentError unless [BYTE, SHORT, INT, LONG].include?(bits_per_item)
|
13
|
-
|
14
|
-
@size = init_size(initial_data, bits_per_item)
|
15
|
-
@internal_array = init_internal_array(initial_data, @size, bits_per_item)
|
16
|
-
@bits_per_item = bits_per_item
|
14
|
+
def initialize(initial_data, bpi: LONG)
|
15
|
+
@internal_array = Bitwarr.new(initial_data, bpi:)
|
17
16
|
end
|
18
17
|
|
19
18
|
def [](index)
|
20
|
-
|
21
|
-
|
22
|
-
item_index = compute_item_index(index)
|
23
|
-
item_bit_size = compute_item_bit_size(item_index)
|
24
|
-
item = @internal_array[item_index]
|
25
|
-
|
26
|
-
Handler::Get.new(item).execute(
|
27
|
-
index: index % @bits_per_item,
|
28
|
-
size: item_bit_size
|
29
|
-
)
|
19
|
+
@internal_array.bit_at(index)
|
30
20
|
end
|
31
21
|
|
32
22
|
def []=(index, value)
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
item_bit_size = compute_item_bit_size(item_index)
|
38
|
-
item = @internal_array[item_index]
|
39
|
-
|
40
|
-
@internal_array[item_index] =
|
41
|
-
if bit == 1
|
42
|
-
Handler::Set.new(item).execute(
|
43
|
-
index: index % @bits_per_item,
|
44
|
-
size: item_bit_size
|
45
|
-
)
|
46
|
-
else
|
47
|
-
Handler::Unset.new(item).execute(
|
48
|
-
index: index % @bits_per_item,
|
49
|
-
size: item_bit_size
|
50
|
-
)
|
51
|
-
end
|
23
|
+
case Mapper::ObjToBit.new.map(value)
|
24
|
+
when 0 then @internal_array.unbit_at!(index)
|
25
|
+
else @internal_array.bit_at!(index)
|
26
|
+
end
|
52
27
|
end
|
53
28
|
|
54
29
|
def set(index)
|
@@ -59,105 +34,28 @@ class Bitary
|
|
59
34
|
self[index] = 0
|
60
35
|
end
|
61
36
|
|
62
|
-
def each_byte(&
|
63
|
-
|
64
|
-
proc ? res.each { |byte| proc.call(byte) } : res.each
|
37
|
+
def each_byte(&)
|
38
|
+
@internal_array.each_byte(&)
|
65
39
|
end
|
66
40
|
|
67
41
|
def to_a
|
68
|
-
@internal_array.
|
42
|
+
@internal_array.to_a
|
69
43
|
end
|
70
44
|
|
71
45
|
def to_s
|
72
|
-
@internal_array.
|
73
|
-
format("%0#{@bits_per_item}d", item.to_s(2))
|
74
|
-
end.join(' ')
|
75
|
-
end
|
76
|
-
|
77
|
-
def bits_per_item=(value)
|
78
|
-
raise ArgumentError unless [BYTE, SHORT, INT, LONG].include?(value)
|
79
|
-
|
80
|
-
@internal_array =
|
81
|
-
if value > @bits_per_item
|
82
|
-
increase_items_size(@internal_array, value, @bits_per_item)
|
83
|
-
else
|
84
|
-
decrease_items_size(@internal_array, value, @bits_per_item)
|
85
|
-
end
|
86
|
-
|
87
|
-
@bits_per_item = value
|
88
|
-
end
|
89
|
-
|
90
|
-
private
|
91
|
-
|
92
|
-
def init_size(initial_data, bits_per_item)
|
93
|
-
if initial_data.is_a?(Array)
|
94
|
-
bits_per_item * initial_data.length
|
95
|
-
else
|
96
|
-
initial_data
|
97
|
-
end
|
46
|
+
@internal_array.to_s
|
98
47
|
end
|
99
48
|
|
100
|
-
def
|
101
|
-
|
102
|
-
initial_data.is_a?(Array) ? initial_data.clone : res
|
49
|
+
def bpi=(value)
|
50
|
+
@internal_array.bpi = value
|
103
51
|
end
|
104
52
|
|
105
|
-
def
|
106
|
-
|
107
|
-
if value.is_a?(Integer)
|
108
|
-
value.zero? ? 0 : 1
|
109
|
-
else
|
110
|
-
1
|
111
|
-
end
|
112
|
-
else
|
113
|
-
0
|
114
|
-
end
|
53
|
+
def size
|
54
|
+
@internal_array.bitsize
|
115
55
|
end
|
116
56
|
|
117
|
-
def
|
118
|
-
|
119
|
-
size - ((@internal_array.length - 1) * @bits_per_item)
|
120
|
-
else
|
121
|
-
@bits_per_item
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
def compute_item_index(index)
|
126
|
-
index / @bits_per_item
|
127
|
-
end
|
128
|
-
|
129
|
-
def increase_items_size(array, new_size, bpi)
|
130
|
-
processed_bits = 0
|
131
|
-
array.each_with_object([0]) do |value, acc|
|
132
|
-
offset = bpi
|
133
|
-
if processed_bits >= new_size
|
134
|
-
offset = 0
|
135
|
-
acc << 0
|
136
|
-
processed_bits = 0
|
137
|
-
end
|
138
|
-
|
139
|
-
acc[-1] = Handler::Append.new(acc[-1]).execute(offset:, value:)
|
140
|
-
processed_bits += bpi
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def decrease_items_size(array, new_size, bpi)
|
145
|
-
array.each_with_object([]) do |item, acc|
|
146
|
-
acc.concat(explode_item(item, new_size, bpi))
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
def explode_item(item, new_size, bpi)
|
151
|
-
res = []
|
152
|
-
offset = bpi
|
153
|
-
mask = (2**new_size) - 1
|
154
|
-
|
155
|
-
while offset.positive?
|
156
|
-
offset -= new_size
|
157
|
-
res << ((item >> offset) & mask)
|
158
|
-
end
|
159
|
-
|
160
|
-
res
|
57
|
+
def bpi
|
58
|
+
@internal_array.bpi
|
161
59
|
end
|
162
60
|
|
163
61
|
alias at []
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bitary
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Maximilien Ballesteros
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-03-
|
11
|
+
date: 2024-03-31 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Ruby-based implementation of the bit array data structure
|
14
14
|
email:
|
@@ -24,11 +24,21 @@ files:
|
|
24
24
|
- README.md
|
25
25
|
- Rakefile
|
26
26
|
- lib/bitary.rb
|
27
|
+
- lib/bitary/bitwarr.rb
|
28
|
+
- lib/bitary/decorator.rb
|
29
|
+
- lib/bitary/decorator/kwargs_validator.rb
|
30
|
+
- lib/bitary/decorator/single_method.rb
|
31
|
+
- lib/bitary/decorator/single_method/non_nil_enforcer.rb
|
32
|
+
- lib/bitary/decorator/single_method/truthy_enforcer.rb
|
33
|
+
- lib/bitary/factory.rb
|
27
34
|
- lib/bitary/handler.rb
|
28
35
|
- lib/bitary/handler/append.rb
|
29
36
|
- lib/bitary/handler/get.rb
|
30
37
|
- lib/bitary/handler/set.rb
|
31
38
|
- lib/bitary/handler/unset.rb
|
39
|
+
- lib/bitary/mapper.rb
|
40
|
+
- lib/bitary/mapper/int_to_bit.rb
|
41
|
+
- lib/bitary/mapper/obj_to_bit.rb
|
32
42
|
- lib/bitary/size.rb
|
33
43
|
- lib/bitary/version.rb
|
34
44
|
- sig/bitary.rbs
|