bitary 0.1.9 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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