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 +4 -4
- data/.rubocop.yml +8 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +11 -0
- data/README.md +15 -8
- data/lib/bitary/bitwarr.rb +56 -20
- data/lib/bitary/version.rb +1 -1
- data/lib/bitary.rb +127 -17
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 23912fd76ca478d49ef1760e9d8cdb47344b3493377d00db1d133b4131c11b6e
|
4
|
+
data.tar.gz: 49def92fa2d841953139bb16cd81779d92e8f9478b6fe8a395f0400b6344a05b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5faafe8215df8fc28fe075a0bf8a874b503c2346489c1327ddb94f1388c12e52cc6e0218fb25a876da9d75409e58a80f369cc71bde766c325ff4d01a02353330
|
7
|
+
data.tar.gz: 0f4b3584332df720e29be0069b42979af9471152df2e4347e5cde90972902a575bba323d7040ee133a8807b918a817d95a1d65f48032ef8f27d8d8708fed7ddc
|
data/.rubocop.yml
CHANGED
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
|
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
|
-
|
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.
|
39
|
-
bit_array_ar.
|
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 # [
|
62
|
-
bit_array_ar.to_s # "
|
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.
|
data/lib/bitary/bitwarr.rb
CHANGED
@@ -2,11 +2,13 @@
|
|
2
2
|
|
3
3
|
class Bitary
|
4
4
|
class Bitwarr
|
5
|
-
attr_reader :bpi, :
|
5
|
+
attr_reader :bpi, :bits
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
42
|
-
@array.respond_to?(method) ? @array.send(method, *, &) : super
|
43
|
-
end
|
46
|
+
private
|
44
47
|
|
45
|
-
def
|
46
|
-
|
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
|
-
|
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(
|
56
|
-
if
|
57
|
-
if
|
58
|
-
|
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
|
-
|
69
|
+
adjust_array(bytes, bpi)
|
61
70
|
end
|
71
|
+
elsif bytes.nil?
|
72
|
+
fill_array(0, init_cap, bpi)
|
62
73
|
else
|
63
|
-
|
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)
|
data/lib/bitary/version.rb
CHANGED
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
|
-
|
11
|
-
|
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(
|
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
|
-
|
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
|
-
|
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
|
-
|
41
|
-
|
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
|
-
|
59
|
-
|
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
|
69
|
-
|
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
|
78
|
-
raise IndexError if bit_index.negative? || bit_index >=
|
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
|
-
|
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.
|
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-
|
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
|