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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 80cd57b662219349dc57edd47612599f95f5aeca086dbec30af067adb10fe587
4
- data.tar.gz: dc630c61a13c74a20e3472a90f244dd995f4c7196fb6146ca15973efe02e310d
3
+ metadata.gz: bf08e150b62f660fa24f19420e8f9ec2b4c44a0ff88bc8466a1bc13339de1ee0
4
+ data.tar.gz: 5837fb427a2332f8518b8ed247edd4ea750dd63a651a3e546c8efcf783dd9daa
5
5
  SHA512:
6
- metadata.gz: 9adf435f35e798c71a3a81213f408da0c178b2d3137ecae3619c84e3c95d564e060a4985d24a4746b93a0fb8e2d73fc4a209a38908c7dad25d87dfd728e575da
7
- data.tar.gz: a6f136e8e5f140ad9455c14b13d94232d40bfc37e09e62b6827857888d03dacbc53fb027a9da1901dfa2d2f966144680a3e2d0a5dd57c4af563d4bd09af99326
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
- def execute(**kwargs)
7
- raise ArgumentError unless kwargs.all? do |key, _value|
8
- %i[offset value].include?(key)
9
- end
10
-
11
- offset = kwargs[:offset] or raise KeyError
12
- value = kwargs[:value] or raise KeyError
13
- raise ArgumentError unless offset.is_a?(Integer)
14
- raise ArgumentError unless value.is_a?(Integer)
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
- (@value << offset) | value
17
+ def execute(**kwargs)
18
+ (@value << kwargs[:offset]) | kwargs[:value]
17
19
  end
18
20
  end
19
21
  end
@@ -3,19 +3,25 @@
3
3
  class Bitary
4
4
  class Handler
5
5
  class Get < Bitary::Handler
6
- def execute(**kwargs)
7
- raise ArgumentError unless kwargs.all? do |key, _value|
8
- %i[index size].include?(key)
9
- end
10
-
11
- index = kwargs[:index] or raise KeyError
12
- size = kwargs[:size] or raise KeyError
13
- raise ArgumentError unless index.is_a?(Integer)
14
- raise ArgumentError unless size.is_a?(Integer)
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
- raise IndexError if index.negative? || index >= size
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
@@ -3,19 +3,25 @@
3
3
  class Bitary
4
4
  class Handler
5
5
  class Set < Bitary::Handler
6
- def execute(**kwargs)
7
- raise ArgumentError unless kwargs.all? do |key, _value|
8
- %i[index size].include?(key)
9
- end
10
-
11
- index = kwargs[:index] or raise KeyError
12
- size = kwargs[:size] or raise KeyError
13
- raise ArgumentError unless index.is_a?(Integer)
14
- raise ArgumentError unless size.is_a?(Integer)
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
- raise IndexError if index.negative? || index >= size
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
@@ -3,19 +3,28 @@
3
3
  class Bitary
4
4
  class Handler
5
5
  class Unset < Bitary::Handler
6
- def execute(**kwargs)
7
- raise ArgumentError unless kwargs.all? do |key, _value|
8
- %i[index size].include?(key)
9
- end
10
-
11
- index = kwargs[:index] or raise KeyError
12
- size = kwargs[:size] or raise KeyError
13
- raise ArgumentError unless index.is_a?(Integer)
14
- raise ArgumentError unless size.is_a?(Integer)
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
- raise IndexError if index.negative? || index >= size
23
+ def execute(**kwargs)
24
+ mask =
25
+ (2**kwargs[:size]) - 1 - (2**(kwargs[:size] - kwargs[:index] - 1))
17
26
 
18
- @value & (((2**size) - 1) - (2**(size - index - 1)))
27
+ @value & mask
19
28
  end
20
29
  end
21
30
  end
@@ -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,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Bitary
4
+ class Mapper
5
+ class IntToBit < Bitary::Mapper
6
+ def map(value)
7
+ if value.is_a?(Integer)
8
+ value.zero? ? 0 : 1
9
+ else
10
+ 1
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Bitary
4
+ class Mapper
5
+ class ObjToBit < Bitary::Mapper
6
+ def map(value)
7
+ case !!value
8
+ when true then IntToBit.new.map(value)
9
+ when false then 0
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Bitary
4
- VERSION = '0.1.2'
4
+ VERSION = '0.1.4'
5
5
  end
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
- attr_reader :size, :bits_per_item
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
- raise IndexError if index.negative? || index >= @size
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
- raise IndexError if index.negative? || index >= @size
34
-
35
- bit = map_to_bit(value)
36
- item_index = compute_item_index(index)
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(&proc)
63
- res = decrease_items_size(@internal_array, BYTE, @bits_per_item)
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.clone
42
+ @internal_array.to_a
69
43
  end
70
44
 
71
45
  def to_s
72
- @internal_array.map do |item|
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 init_internal_array(initial_data, size, bits_per_item)
101
- res = [0] * (size / bits_per_item.to_f).ceil
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 map_to_bit(value)
106
- if value
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 compute_item_bit_size(index)
118
- if index == @internal_array.length - 1
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.2
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-29 00:00:00.000000000 Z
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