bitary 0.1.9 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e00e1deaf6796f8875294cb61799bc4ee1ae956c681489cee8243c9cad532224
4
- data.tar.gz: 74088437d85300f562cb47011f17f9242329a0522a95878dfadce0a54803fffe
3
+ metadata.gz: 23912fd76ca478d49ef1760e9d8cdb47344b3493377d00db1d133b4131c11b6e
4
+ data.tar.gz: 49def92fa2d841953139bb16cd81779d92e8f9478b6fe8a395f0400b6344a05b
5
5
  SHA512:
6
- metadata.gz: 71b02ce18e1ad4decb42b8680fbcbb44cf44b9a4edb3637300aa40e1ef90825279b9b4c76e3eef24cf08b8b084cf248129f49e55dd98a191ac2223f185c3e7fb
7
- data.tar.gz: a55ae4d18bd812a9d3a3ff1d6eafa263a5eb362978ed8dd47cfed2c0638927c6491f286f70f767a2cc0eeafea85704583cbee085ad450c8d533d5c086f9d8e1f
6
+ metadata.gz: 5faafe8215df8fc28fe075a0bf8a874b503c2346489c1327ddb94f1388c12e52cc6e0218fb25a876da9d75409e58a80f369cc71bde766c325ff4d01a02353330
7
+ data.tar.gz: 0f4b3584332df720e29be0069b42979af9471152df2e4347e5cde90972902a575bba323d7040ee133a8807b918a817d95a1d65f48032ef8f27d8d8708fed7ddc
data/.rubocop.yml CHANGED
@@ -23,3 +23,11 @@ Layout/LineLength:
23
23
  Style/MixinUsage:
24
24
  Exclude:
25
25
  - 'spec/**/*.rb'
26
+
27
+ Style/ArgumentsForwarding:
28
+ Exclude:
29
+ - /**/*.rb
30
+
31
+ Naming/BlockForwarding:
32
+ Exclude:
33
+ - /**/*.rb
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --markup markdown
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## [0.2.0] - 2024-04-14
2
+
3
+ - `Bitary#to_a` now returns a clone to prevent client code to misuse the data structure (same for `Bitary#each_byte`)
4
+ - Streamline the internal array initialization
5
+ - Enhance constructor implementation
6
+ - `Bitary#set` and `Bitary#set` are now suffixed by `!`
7
+
8
+ ## [0.1.9] - 2024-04-01
9
+
10
+ - boost perf even more by inlining mapping operations
11
+
1
12
  ## [0.1.8] - 2024-04-01
2
13
 
3
14
  - inline bit operations to regain original perf
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 (as well as some extra features, see below).
5
+ 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
 
@@ -20,23 +20,22 @@ $ gem install bitary
20
20
 
21
21
  ## Usage
22
22
 
23
- Documentation still needs to be written, but here is a breakdown
24
- of the main capabilities brought by the current bit array implementation:
23
+ Here is a breakdown of the main capabilities brought by the current implementation:
25
24
 
26
25
  ```ruby
27
26
  require 'bitary'
28
27
 
29
28
  bit_array_sz = Bitary.new(128) # give an explicit size. Defaults to 64 bits used per item
30
29
  bit_array_ar = Bitary.new(
31
- [255, 10, 20],
30
+ bytes: [255, 10, 20],
32
31
  bpi: Bitary::BYTE # 8 bits
33
32
  ) # create based on some integer array
34
33
 
35
34
  bit_array_sz.bpi # 64
36
35
  bit_array_ar.bpi # 8
37
36
 
38
- bit_array_ar.size # 128
39
- bit_array_ar.size # 24
37
+ bit_array_ar.bits # 128
38
+ bit_array_ar.bits # 24
40
39
 
41
40
  # set/unset/get
42
41
  bit_array_sz[23] = 1 # set bit at position 23 (0-indexed)
@@ -58,8 +57,8 @@ bit_array_ar.to_s # "01111111 00001010 00010100"
58
57
 
59
58
  # increase/decrease bits used per item
60
59
  bit_array_ar.bpi = Bitary::LONG # 64 bits
61
- bit_array_ar.to_a # [8_325_652]
62
- bit_array_ar.to_s # "0000000000000000000000000000000000000000011111110000101000010100"
60
+ bit_array_ar.to_a # [9154151182816509952]
61
+ bit_array_ar.to_s # "0111111100001010000101000000000000000000000000000000000000000000"
63
62
 
64
63
  bit_array_sz.bpi # 64
65
64
  bit_array_sz.to_a # [1_099_511_627_776, 0]
@@ -73,6 +72,14 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
73
72
 
74
73
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
75
74
 
75
+ Access the technical documentation in HTML format by issuing:
76
+
77
+ ```bash
78
+ $ bundle exec yardoc # or simply yardoc if bundler is not being used
79
+ ```
80
+
81
+ The YARD documentation will be generated under `/doc`.
82
+
76
83
  ## Contributing
77
84
 
78
85
  Bug reports and pull requests are welcome on GitHub at https://github.com/Patacode/bitary.
@@ -2,11 +2,13 @@
2
2
 
3
3
  class Bitary
4
4
  class Bitwarr
5
- attr_reader :bpi, :bitsize
5
+ attr_reader :bpi, :bits
6
6
 
7
- def initialize(initial_data, bpi: Bitary::LONG)
8
- @bitsize = init_bitsize(initial_data, bpi)
9
- @array = init_array(initial_data, @bitsize, bpi)
7
+ DEFAULT_INIT_CAP = Bitary::Size::LONG * 2
8
+
9
+ def initialize(init_cap, bytes:, bpi:)
10
+ @array = init_array(init_cap, bytes, bpi)
11
+ @bits = init_bits(init_cap, bytes)
10
12
  @bpi = bpi
11
13
  end
12
14
 
@@ -24,11 +26,14 @@ class Bitary
24
26
  end
25
27
 
26
28
  def to_s = @array.map { |item| to_binstr(item) }.join(' ')
29
+ def to_a = @array.clone
27
30
 
28
31
  def each_byte(&proc)
32
+ raise ArgumentError if proc.nil?
33
+
29
34
  @array.each do |item|
30
35
  explode_item(item, Bitary::BYTE, @bpi, &proc)
31
- end
36
+ end.clone
32
37
  end
33
38
 
34
39
  def bpi=(value)
@@ -38,30 +43,61 @@ class Bitary
38
43
  @bpi = value
39
44
  end
40
45
 
41
- def method_missing(method, *, &)
42
- @array.respond_to?(method) ? @array.send(method, *, &) : super
43
- end
46
+ private
44
47
 
45
- def respond_to_missing?(method, include_all = false)
46
- @array.respond_to?(method, include_all) || super
48
+ def init_bits(init_cap, bytes)
49
+ if init_cap.nil?
50
+ if bytes.nil?
51
+ DEFAULT_INIT_CAP
52
+ else
53
+ bytes.length * Bitary::BYTE
54
+ end
55
+ else
56
+ init_cap
57
+ end
47
58
  end
48
59
 
49
- private
50
-
51
- def init_bitsize(initial_data, bpi)
52
- initial_data.is_a?(Array) ? bpi * initial_data.length : initial_data
60
+ def compute_nb_items(init_cap, bpi)
61
+ (init_cap / bpi.to_f).ceil
53
62
  end
54
63
 
55
- def init_array(initial_data, bitsize, bpi)
56
- if initial_data.is_a?(Array)
57
- if bpi == Bitary::BYTE
58
- initial_data.clone
64
+ def init_array(init_cap, bytes, bpi)
65
+ if init_cap.nil?
66
+ if bytes.nil?
67
+ fill_array(0, DEFAULT_INIT_CAP, bpi)
59
68
  else
60
- increase_items_size(initial_data, bpi, Bitary::BYTE)
69
+ adjust_array(bytes, bpi)
61
70
  end
71
+ elsif bytes.nil?
72
+ fill_array(0, init_cap, bpi)
62
73
  else
63
- [0] * (bitsize / bpi.to_f).ceil
74
+ clone = adjust_array(bytes, bpi)
75
+ if init_cap > clone.length * bpi
76
+ adjust_array_to_cap(clone, init_cap, bpi)
77
+ end
78
+ clone
79
+ end
80
+ end
81
+
82
+ def fill_array(value, size, bpi)
83
+ [value] * compute_nb_items(size, bpi)
84
+ end
85
+
86
+ def adjust_array(bytes, bpi)
87
+ if bpi == Bitary::BYTE
88
+ bytes.clone
89
+ else
90
+ increase_items_size(bytes, bpi, Bitary::BYTE)
91
+ end
92
+ end
93
+
94
+ def adjust_array_to_cap(bytes, init_cap, bpi)
95
+ target_size = compute_nb_items(init_cap, bpi)
96
+ while target_size > bytes.length
97
+ bytes << 0
98
+ target_size -= 1
64
99
  end
100
+ bytes
65
101
  end
66
102
 
67
103
  def item_index(bit_index)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Bitary
4
- VERSION = '0.1.9'
4
+ VERSION = '0.2.0'
5
5
  end
data/lib/bitary.rb CHANGED
@@ -4,22 +4,70 @@ require_relative 'bitary/size'
4
4
  require_relative 'bitary/version'
5
5
  require_relative 'bitary/bitwarr'
6
6
 
7
+ # Bit array facade through client code SHOULD interact with.
7
8
  class Bitary
8
9
  include Size
9
10
 
10
- def initialize(initial_data, bpi: LONG)
11
- check_initial_data(initial_data)
11
+ # Creates a new bit array.
12
+ #
13
+ # If the initial bit capacity is not given (or `nil`), it will be deducted
14
+ # from the given byte array. In such case, if no byte array is given, it
15
+ # will default to {Bitwarr::DEFAULT_INIT_CAP}.
16
+ #
17
+ # If no byte array is given (or `nil`), the resulting bit array's items will
18
+ # all be set to 0.
19
+ #
20
+ # Note that, no check is performed on the byte array parameter to ensure
21
+ # that it has `Integer` items only, as it would considerably impact the
22
+ # data structure performance at initialization time. So be careful with
23
+ # it.
24
+ #
25
+ # @param init_cap [Integer?] an optional initial bit capacity
26
+ # @param bytes [Array<Integer>?] an optional byte array used to preset
27
+ # the bit array
28
+ # @param bpi [Integer] the number of bits used per item internally.
29
+ # Defaults to {LONG}
30
+ #
31
+ # @raise [TypeError] if **init_cap** is neither an `Integer` nor `nil`
32
+ # @raise [TypeError] if **bytes** is neither an `Array` nor `nil`
33
+ # @raise [TypeError] if **bpi** is not an `Integer`
34
+ # @raise [ArgumentError] if **init_cap** is <= 0
35
+ # @raise [ArgumentError] if **bpi** is not equal to one of the constants
36
+ # defined in {Size}
37
+ def initialize(init_cap = nil, bytes: nil, bpi: LONG)
38
+ check_init_cap(init_cap)
39
+ check_bytes(bytes)
12
40
  check_bpi(bpi)
13
41
 
14
- @bitwarr = Bitwarr.new(initial_data, bpi:)
42
+ @bitwarr = Bitwarr.new(init_cap, bytes:, bpi:)
15
43
  end
16
44
 
45
+ # Gets the bit at given index.
46
+ #
47
+ # @param index [Integer] the index of the bit to retrieve
48
+ #
49
+ # @raise [TypeError] if **index** is not an `Integer`
50
+ # @raise [IndexError] if **index** is < 0 || >= {#bits}
51
+ #
52
+ # @return [Integer] the bit at given index
17
53
  def [](index)
18
54
  check_bit_index(index)
19
55
 
20
56
  @bitwarr.bit_at(index)
21
57
  end
22
58
 
59
+ # Sets or unsets the bit at given index.
60
+ #
61
+ # The bit at given index will be set to 1 if given value is truthy but 0,
62
+ # or 0 otherwise.
63
+ #
64
+ # @param index [Integer] the index of the bit to set/unset
65
+ # @param value [Object] the object used to set/unset the bit at given index
66
+ #
67
+ # @raise [TypeError] if **index** is not an `Integer`
68
+ # @raise [IndexError] if **index** is < 0 || >= {#bits}
69
+ #
70
+ # @return [Object] the given **value**
23
71
  def []=(index, value)
24
72
  check_bit_index(index)
25
73
 
@@ -29,60 +77,122 @@ class Bitary
29
77
  end
30
78
  end
31
79
 
32
- def set(index)
80
+ # Sets the bit at given index to 1 (set).
81
+ #
82
+ # Specialization method of {#[]=} that is similar to `bitary[index] = 1`.
83
+ #
84
+ # @param index [Integer] the index of the bit to set
85
+ #
86
+ # @raise [TypeError] if **index** is not an `Integer`
87
+ # @raise [IndexError] if **index** is < 0 || >= {#bits}
88
+ #
89
+ # @return [Integer] 1
90
+ def set!(index)
33
91
  self[index] = 1
34
92
  end
35
93
 
36
- def unset(index)
94
+ # Sets the bit at given index to 0 (unset).
95
+ #
96
+ # Specialization method of {#[]=} that is similar to `bitary[index] = 0`.
97
+ #
98
+ # @param index [Integer] the index of the bit to unset
99
+ #
100
+ # @raise [TypeError] if **index** is not an `Integer`
101
+ # @raise [IndexError] if **index** is < 0 || >= {#bits}
102
+ #
103
+ # @return [Integer] 0
104
+ def unset!(index)
37
105
  self[index] = 0
38
106
  end
39
107
 
40
- def each_byte(&)
41
- @bitwarr.each_byte(&)
108
+ # Traverses each byte of this bit array starting with its byte at most
109
+ # significant address.
110
+ #
111
+ # @param block [Proc] the block to execute during byte traversal
112
+ #
113
+ # @yield [Integer] each byte of this bit array
114
+ #
115
+ # @return [Array<Integer>] a clone of the internal backed array (same
116
+ # as {#to_a})
117
+ def each_byte(&block)
118
+ @bitwarr.each_byte(&block)
42
119
  end
43
120
 
121
+ # Returns a clone of the internal backed array used by this bit array.
122
+ #
123
+ # @return [Array<Integer>] a clone of the internal backed array
44
124
  def to_a
45
125
  @bitwarr.to_a
46
126
  end
47
127
 
128
+ # Converts this bit array into an equivalent binary string.
129
+ #
130
+ # @return [String] the binary string representation of this bit array
48
131
  def to_s
49
132
  @bitwarr.to_s
50
133
  end
51
134
 
135
+ # Updates the number of bits used internally by each item.
136
+ #
137
+ # Depending on the use case, increasing/decreasing the number of bits used
138
+ # per item could reduce the memory footprint of this bit array.
139
+ #
140
+ # The idea is just to pay attention to potential internal fragmentation
141
+ # and find the right balance between the total number of bits you want to
142
+ # use and the number of bits that will be used by each item internally.
143
+ #
144
+ # @param value [Integer] the new bpi to be used by this bit array
145
+ #
146
+ # @raise [TypeError] if **value** is not an `Integer`
147
+ # @raise [ArgumentError] if **value** is not equal to one of the constants
148
+ # defined in {Size}
52
149
  def bpi=(value)
53
150
  check_bpi(value)
54
151
 
55
152
  @bitwarr.bpi = value
56
153
  end
57
154
 
58
- def size
59
- @bitwarr.bitsize
155
+ # Returns the total number of bits used by this bit array.
156
+ #
157
+ # @return [Integer] he total number of bits used by this bit array
158
+ def bits
159
+ @bitwarr.bits
60
160
  end
61
161
 
162
+ # Returns the number of bits used per item internally.
163
+ #
164
+ # @return [Integer] the number of bits used per item internally
62
165
  def bpi
63
166
  @bitwarr.bpi
64
167
  end
65
168
 
66
169
  private
67
170
 
68
- def check_initial_data(initial_data)
69
- raise ArgumentError unless [Array, Integer].include?(initial_data.class)
171
+ def check_init_cap(init_cap)
172
+ return if init_cap.nil?
173
+
174
+ raise TypeError unless init_cap.is_a?(Integer)
175
+ raise ArgumentError unless init_cap.positive?
176
+ end
177
+
178
+ def check_bytes(bytes)
179
+ return if bytes.nil?
180
+
181
+ raise TypeError unless bytes.is_a?(Array)
70
182
  end
71
183
 
72
184
  def check_bpi(bpi)
185
+ raise TypeError unless bpi.is_a?(Integer)
73
186
  raise ArgumentError unless [BYTE, SHORT, INT, LONG].include?(bpi)
74
187
  end
75
188
 
76
189
  def check_bit_index(bit_index)
77
- raise ArgumentError unless bit_index.is_a?(Integer)
78
- raise IndexError if bit_index.negative? || bit_index >= @bitwarr.bitsize
190
+ raise TypeError unless bit_index.is_a?(Integer)
191
+ raise IndexError if bit_index.negative? || bit_index >= bits
79
192
  end
80
193
 
81
194
  def obj_to_bit(value)
82
- case !!value
83
- when true then truthy_to_bit(value)
84
- when false then 0
85
- end
195
+ !!value ? truthy_to_bit(value) : 0
86
196
  end
87
197
 
88
198
  def truthy_to_bit(value)
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.9
4
+ version: 0.2.0
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-04-01 00:00:00.000000000 Z
11
+ date: 2024-04-14 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Ruby-based implementation of the bit array data structure
14
14
  email:
@@ -19,6 +19,7 @@ extra_rdoc_files: []
19
19
  files:
20
20
  - ".rspec"
21
21
  - ".rubocop.yml"
22
+ - ".yardopts"
22
23
  - CHANGELOG.md
23
24
  - LICENSE.txt
24
25
  - README.md