multibases 0.1.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 +7 -0
- data/.gitignore +9 -0
- data/.rubocop.yml +7 -0
- data/.travis.yml +7 -0
- data/Gemfile +8 -0
- data/README.md +326 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/lib/multibases.rb +42 -0
- data/lib/multibases/bare.rb +142 -0
- data/lib/multibases/base16.rb +106 -0
- data/lib/multibases/base2.rb +98 -0
- data/lib/multibases/base32.rb +110 -0
- data/lib/multibases/base64.rb +116 -0
- data/lib/multibases/base_x.rb +129 -0
- data/lib/multibases/byte_array.rb +73 -0
- data/lib/multibases/ord_table.rb +109 -0
- data/lib/multibases/registry.rb +53 -0
- data/lib/multibases/version.rb +5 -0
- data/multibases.gemspec +51 -0
- metadata +112 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 57c641bb941bf22ba1e15624ec2f798ccfdbda9c349f8abaa18bc6ef39275008
|
4
|
+
data.tar.gz: 5dd0cbd27ac8ee91362505fa0ab64b021856109ce61ca8d1815242d21a2f347e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4ebd6b9d19633242cf696170912d7897c2ebc40321b92a3f717d918d1adad0d65ab2cbd9c50dca09497917221236c6110ffcf65f2be93fa690b6df9c658018d6
|
7
|
+
data.tar.gz: 59e4ddce0a22b82771c695e7f50e9da0653af855cd09e6be53329539a0115ac85329606ce15ff03ca28c0eb1cf07fbda45c4005975cbaf633c84d4763d729b74
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,326 @@
|
|
1
|
+
# Multibases
|
2
|
+
|
3
|
+
[](https://travis-ci.com/SleeplessByte/ruby-multibase)
|
4
|
+
[](https://badge.fury.io/rb/ruby-multibase)
|
5
|
+
[](http://opensource.org/licenses/MIT)
|
6
|
+
[](https://codeclimate.com/repos/5d094b036bd112014f005f98/maintainability)
|
7
|
+
|
8
|
+
> Multibase is a protocol for disambiguating the encoding of base-encoded
|
9
|
+
> (e.g., base32, base64, base58, etc.) binary appearing in text.
|
10
|
+
|
11
|
+
`Multibases` is the ruby implementation of [multiformats/multibase][spec].
|
12
|
+
|
13
|
+
This gem can be used _both_ for encoding into or decoding from multibase packed
|
14
|
+
strings, as well as serve as a _general purpose_ library to do `BaseX` encoding
|
15
|
+
and decoding _without_ adding the prefix.
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Add this line to your application's Gemfile:
|
20
|
+
|
21
|
+
```Ruby
|
22
|
+
gem 'multibases'
|
23
|
+
```
|
24
|
+
|
25
|
+
or alternatively if you would like to bring your own engines and not load any
|
26
|
+
of the built-in ones:
|
27
|
+
|
28
|
+
```Ruby
|
29
|
+
gem 'multibases', require: 'multibases/bare'
|
30
|
+
```
|
31
|
+
|
32
|
+
And then execute:
|
33
|
+
|
34
|
+
$ bundle
|
35
|
+
|
36
|
+
Or install it yourself as:
|
37
|
+
|
38
|
+
$ gem install multibases
|
39
|
+
|
40
|
+
## Usage
|
41
|
+
|
42
|
+
This is a low-level library, but high level implementations are provided.
|
43
|
+
You can also bring your own encoder/decoder. The most important methods are:
|
44
|
+
|
45
|
+
- `Multibases.encode(encoding, data, engine?)`: encodes the given data with a
|
46
|
+
built-in engine for encoding, or engine if it's given. Returns an `Encoded`
|
47
|
+
PORO that has `pack`.
|
48
|
+
- `Multibases.unpack(packed)`: decodes a multibase packed string into an
|
49
|
+
`Encoded` PORO that has `decode`.
|
50
|
+
- `Multibases::Encoded.pack`: packs the multihash into a single string
|
51
|
+
- `Multibases::Encoded.decode(engine?)`: decodes the PORO's data using a
|
52
|
+
built-in engine, or engine if it's given. Returns a decoded `ByteArray`.
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
encoded = Multibases.encode('base2', 'mb')
|
56
|
+
# => #<struct Multibases::Encoded
|
57
|
+
# code="0", encoding="base2", length=16,
|
58
|
+
# data=[Multibases::EncodedByteArray "0110110101100010"]>
|
59
|
+
|
60
|
+
encoded.pack
|
61
|
+
# => [Multibases::EncodedByteArray "00110110101100010"]
|
62
|
+
|
63
|
+
|
64
|
+
encoded = Multibases.unpack('766542')
|
65
|
+
# => #<struct Multibases::Encoded
|
66
|
+
# code="7", encoding="base8", length=5,
|
67
|
+
# data=[Multibases::EncodedByteArray "66542"]>
|
68
|
+
|
69
|
+
encoded.decode
|
70
|
+
# => [Multibases::DecodedByteArray "mb"]
|
71
|
+
```
|
72
|
+
|
73
|
+
This means that the flow of calls is as follows:
|
74
|
+
|
75
|
+
```text
|
76
|
+
data ➡️ (encode) ➡️ encoded data
|
77
|
+
encoded data ➡️ (pack) ➡️ multibasestr
|
78
|
+
|
79
|
+
multibasestr ➡️ (unpack) ➡️ encoded data
|
80
|
+
encoded data ➡️ (decode) ➡️ data
|
81
|
+
```
|
82
|
+
|
83
|
+
Convenience methods are provided:
|
84
|
+
|
85
|
+
- `Multibases.pack(encoding, data, engine?)`: calls `encode` and then `pack`
|
86
|
+
- `Multibases.decode(packed, engine?)`: calls `unpack` and then `decode`
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
Multibases.pack('base2', 'mb')
|
90
|
+
# => [Multibases::EncodedByteArray "00110110101100010"]
|
91
|
+
```
|
92
|
+
|
93
|
+
### ByteArrays and encoding
|
94
|
+
|
95
|
+
As you can see, the "final" methods output a `ByteArray`. These are simple
|
96
|
+
`DelegateClass` wrappers around the array with bytes, which means that the `hex`
|
97
|
+
encoding of `hello` is not actually stored as `"f68656c6c6f"`:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
packed = Multibases.pack('base16', 'hello')
|
101
|
+
# => [Multibases::EncodedByteArray "f68656c6c6f"]
|
102
|
+
|
103
|
+
packed.to_a # .__getobj__.dup
|
104
|
+
# => [102, 54, 56, 54, 53, 54, 99, 54, 99, 54, 102]
|
105
|
+
```
|
106
|
+
|
107
|
+
They override `inspect` and _force_ the encoding to `UTF-8` (in inspect), but
|
108
|
+
you can use the convenience methods to use the correct encoding:
|
109
|
+
|
110
|
+
> **Note**: If you're using `pry` and have not changed the printer, you
|
111
|
+
> naturally won't see the output as described above, but instead see the inner
|
112
|
+
> Array of bytes, always.
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
data = 'hello'.encode('UTF-16LE')
|
116
|
+
data.encoding
|
117
|
+
# => #<Encoding:UTF-16LE>
|
118
|
+
|
119
|
+
data.bytes
|
120
|
+
# => [104, 0, 101, 0, 108, 0, 108, 0, 111, 0]
|
121
|
+
|
122
|
+
packed = Multibases.pack('base16', data)
|
123
|
+
# => [Multibases::EncodedByteArray "f680065006c006c006f00"]
|
124
|
+
|
125
|
+
decoded = Multibases.decode(packed)
|
126
|
+
# => [Multibases::DecodedByteArray "h e l l o "]
|
127
|
+
|
128
|
+
decoded.to_s('UTF-16LE')
|
129
|
+
# => "hello"
|
130
|
+
```
|
131
|
+
|
132
|
+
### Implementations
|
133
|
+
|
134
|
+
You can find the _current_ multibase table [here][git-multibase-table]. At this
|
135
|
+
moment, built-in engines are provided as follows:
|
136
|
+
|
137
|
+
| encoding | code | description | implementation |
|
138
|
+
|-------------------|------|-----------------------------------|----------------|
|
139
|
+
| identity | 0x00 | 8-bit binary | `bare` |
|
140
|
+
| base1 | 1 | unary (1111) | ❌ |
|
141
|
+
| base2 | 0 | binary (0101) | `base2` 💨 |
|
142
|
+
| base8 | 7 | octal | `base_x` |
|
143
|
+
| base10 | 9 | decimal | `base_x` |
|
144
|
+
| base16 | f | hexadecimal | `base16` 💨 |
|
145
|
+
| base16upper | F | hexadecimal | `base16` 💨 |
|
146
|
+
| base32hex | v | rfc4648 no padding - highest char | `base32` ✨ |
|
147
|
+
| base32hexupper | V | rfc4648 no padding - highest char | `base32` ✨ |
|
148
|
+
| base32hexpad | t | rfc4648 with padding | `base32` ✨ |
|
149
|
+
| base32hexpadupper | T | rfc4648 with padding | `base32` ✨ |
|
150
|
+
| base32 | b | rfc4648 no padding | `base32` ✨ |
|
151
|
+
| base32upper | B | rfc4648 no padding | `base32` ✨ |
|
152
|
+
| base32pad | c | rfc4648 with padding | `base32` ✨ |
|
153
|
+
| base32padupper | C | rfc4648 with padding | `base32` ✨ |
|
154
|
+
| base32z | h | z-base-32 (used by Tahoe-LAFS) | `base32` ✨ |
|
155
|
+
| base58flickr | Z | base58 flicker | `base_x` |
|
156
|
+
| base58btc | z | base58 bitcoin | `base_x` |
|
157
|
+
| base64 | m | rfc4648 no padding | `base64` 💨 |
|
158
|
+
| base64pad | M | rfc4648 with padding - MIME enc | `base64` 💨 |
|
159
|
+
| base64url | u | rfc4648 no padding | `base64` 💨 |
|
160
|
+
| base64urlpad | U | rfc4648 with padding | `base64` 💨 |
|
161
|
+
|
162
|
+
Those with a 💨 are marked because they are backed by a C implementation (using
|
163
|
+
`pack` and `unpack`) and are therefore suposed to be blazingly fast. Those with
|
164
|
+
a ✨ are marked because they have a custom implementation over the generic
|
165
|
+
`base_x` implementation. It should be faster.
|
166
|
+
|
167
|
+
The version of the spec that this repository was last updated for is available
|
168
|
+
via `Multibases.multibase_version`:
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
Multibases.multibase_version
|
172
|
+
# => "1.0.0"
|
173
|
+
```
|
174
|
+
|
175
|
+
### Bring your own engine
|
176
|
+
|
177
|
+
The methods of `multibases` allow you to bring your own engine, and you can safe
|
178
|
+
additional memory by only loading `multibases/bare`.
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
# Note: This is not how multibase was meant to work. It's supposed to only
|
182
|
+
# convert the input from one base to another, and denote what that base
|
183
|
+
# is, stored in the output. However, the system is _so_ flexible that this
|
184
|
+
# works perfectly for any reversible transformation!
|
185
|
+
class EngineKlazz
|
186
|
+
def initialize(*_)
|
187
|
+
end
|
188
|
+
|
189
|
+
def encode(plain)
|
190
|
+
plain = plain.bytes unless plain.is_a?(Array)
|
191
|
+
Multibases::EncodedByteArray.new(plain.reverse)
|
192
|
+
end
|
193
|
+
|
194
|
+
def decode(encoded)
|
195
|
+
encoded = encoded.bytes unless encoded.is_a?(Array)
|
196
|
+
Multibases::DecodedByteArray.new(encoded.reverse)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
Multibases.implement 'reverse', 'r', EngineKlazz, 'alphabet'
|
201
|
+
# => Initializes EngineKlazz with 'alphabet'
|
202
|
+
|
203
|
+
Multibases.pack('reverse', 'md')
|
204
|
+
# => [Multibases::EncodedByteArray "rdm"]
|
205
|
+
|
206
|
+
Multibases.decode('dm')
|
207
|
+
# => [Multibases::DecodedByteArray "md"]
|
208
|
+
|
209
|
+
# Alternatively, you can pass the instantiated engine to the appropriate
|
210
|
+
# function.
|
211
|
+
engine = EngineKlazz.new
|
212
|
+
|
213
|
+
# Mark the encoding as "existing" and attach a code
|
214
|
+
Multibases.implement 'reverse', 'r'
|
215
|
+
|
216
|
+
# Pack, using a custom engine
|
217
|
+
Multibases.pack('reverse', 'md', engine)
|
218
|
+
# => [Multibases::EncodedByteArray "rdm"]
|
219
|
+
|
220
|
+
Multibases.decode('rdm', engine)
|
221
|
+
# => [Multibases::DecodedByteArray "md"]
|
222
|
+
```
|
223
|
+
|
224
|
+
### Using the built-in encoders/decoders
|
225
|
+
|
226
|
+
You can use the built-in encoders and decoders.
|
227
|
+
|
228
|
+
```ruby
|
229
|
+
require 'multibases/base16'
|
230
|
+
|
231
|
+
Multibases::Base16.encode('foobar')
|
232
|
+
# => [Multibases::EncodedByteArray "666f6f626172"]
|
233
|
+
|
234
|
+
Multibases::Base16.decode('666f6f626172')
|
235
|
+
# => [Multibases::DecodedByteArray "foobar"]
|
236
|
+
```
|
237
|
+
|
238
|
+
These don't add the `multibase` prefix to the output and they use the canonical
|
239
|
+
`encode` and `decode` nomenclature.
|
240
|
+
|
241
|
+
The `base_x` / `BaseX` encoder does not have a module function. You _must_
|
242
|
+
instantiate it first. The result is an encoder that uses the base alphabet to
|
243
|
+
determine its base. Currently padding is ❌ not supported for `BaseX`, but
|
244
|
+
might be in a future update using a second argument or key.
|
245
|
+
|
246
|
+
```ruby
|
247
|
+
require 'multibases/base_x'
|
248
|
+
|
249
|
+
Base3 = Multibases::BaseX.new('012')
|
250
|
+
# => [Multibases::Base3 alphabet="012" strict]
|
251
|
+
|
252
|
+
Base3.encode('foobar')
|
253
|
+
# => [Multibases::EncodedByteArray "112202210012121110020020001100"]
|
254
|
+
```
|
255
|
+
|
256
|
+
You can use the same technique to inject a custom alphabet. This can be used on
|
257
|
+
the built-in encoders, even the ones that are not `BaseX`:
|
258
|
+
|
259
|
+
```ruby
|
260
|
+
base = Multibases::Base2.new('.!')
|
261
|
+
# => [Multibases::Base2 alphabet=".!"]
|
262
|
+
|
263
|
+
base.encode('foo')
|
264
|
+
# [Multibases::EncodedByteArray ".!!..!!..!!.!!!!.!!.!!!!"]
|
265
|
+
|
266
|
+
base.decode('.!!...!..!!....!.!!!..!.')
|
267
|
+
# => [Multibases::DecodedByteArray "bar"]
|
268
|
+
```
|
269
|
+
|
270
|
+
All the built-in encoder/decoders take strings, arrays or byte-arrays as input.
|
271
|
+
|
272
|
+
```ruby
|
273
|
+
expected = Multibases::Base16.encode("abc")
|
274
|
+
# => [Multibases::EncodedByteArray "616263"]
|
275
|
+
|
276
|
+
expected == Multibases::Base16.encode([97, 98, 99])
|
277
|
+
# => true
|
278
|
+
|
279
|
+
expected == Multibases::Base16.encode(Multibases::ByteArray.new("abc".bytes))
|
280
|
+
# => true
|
281
|
+
```
|
282
|
+
|
283
|
+
## Related
|
284
|
+
|
285
|
+
- [`multiformats/multibase`][git-multibase]: the spec repository
|
286
|
+
- [`multiformats/ruby-multihash`][git-ruby-multihash]: the ruby implementation of [`multiformats/multihash`][git-multihash]
|
287
|
+
|
288
|
+
## Development
|
289
|
+
|
290
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
291
|
+
`rake test` to run the tests. You can also run `bin/console` for an interactive
|
292
|
+
prompt that will allow you to experiment.
|
293
|
+
|
294
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
295
|
+
To release a new version, update the version number in `version.rb`, and then
|
296
|
+
run `bundle exec rake release`, which will create a git tag for the version,
|
297
|
+
push git commits and tags, and push the `.gem` file to [rubygems.org][web-rubygems].
|
298
|
+
|
299
|
+
## Contributing
|
300
|
+
|
301
|
+
Bug reports and pull requests are welcome on GitHub at [SleeplessByte/commmand][git-self].
|
302
|
+
This project is intended to be a safe, welcoming space for collaboration, and
|
303
|
+
contributors are expected to adhere to the [Contributor Covenant][web-coc] code
|
304
|
+
of conduct.
|
305
|
+
|
306
|
+
## License
|
307
|
+
|
308
|
+
The gem is available as open source under the terms of the [MIT License][web-mit].
|
309
|
+
|
310
|
+
## Code of Conduct
|
311
|
+
|
312
|
+
Everyone interacting in the Shrine::ConfigurableStorage project’s codebases,
|
313
|
+
issue trackers, chat rooms and mailing lists is expected to follow the
|
314
|
+
[code of conduct][git-self-coc].
|
315
|
+
|
316
|
+
[spec]: https://github.com/multiformats/multibase
|
317
|
+
[git-self-coc]: https://github.com/SleeplessByte/ruby-multibase/blob/master/CODE_OF_CONDUCT.md
|
318
|
+
[git-self]: https://github.com/SleeplessByte/ruby-multibase
|
319
|
+
[git-multibase]: https://github.com/multiformats/multibase
|
320
|
+
[git-multibase-table]: https://github.com/multiformats/multibase/blob/master/multibase.csv
|
321
|
+
[git-ruby-multihash]: https://github.com/multiformats/ruby-multihash
|
322
|
+
[git-multihash]: https://github.com/multiformats/multihash
|
323
|
+
[web-coc]: http://contributor-covenant.org
|
324
|
+
[web-mit]: https://opensource.org/licenses/MIT
|
325
|
+
[web-rubygems]: https://rubygems.org
|
326
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'multibases'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require 'irb'
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/lib/multibases.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'multibases/bare'
|
4
|
+
|
5
|
+
require 'multibases/registry'
|
6
|
+
|
7
|
+
require 'multibases/base_x'
|
8
|
+
require 'multibases/base2'
|
9
|
+
require 'multibases/base16'
|
10
|
+
require 'multibases/base32'
|
11
|
+
require 'multibases/base64'
|
12
|
+
|
13
|
+
module Multibases
|
14
|
+
class Error < StandardError; end
|
15
|
+
|
16
|
+
# https://github.com/multiformats/multibase#multibase-table-v100-rc-semver
|
17
|
+
multibase_version '1.0.0'
|
18
|
+
|
19
|
+
# rubocop:disable Metrics/LineLength
|
20
|
+
implement 'base1', '1', nil, '1'
|
21
|
+
implement 'base2', '0', Base2, '01'
|
22
|
+
implement 'base8', '7', BaseX, '01234567'
|
23
|
+
implement 'base10', '9', BaseX, '0123456789'
|
24
|
+
implement 'base16', 'f', Base16, '0123456789abcdef'
|
25
|
+
implement 'base16upper', 'F', Base16, '0123456789ABCDEF'
|
26
|
+
implement 'base32', 'b', Base32, 'abcdefghijklmnopqrstuvwxyz234567'
|
27
|
+
implement 'base32upper', 'B', Base32, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'
|
28
|
+
implement 'base32pad', 'c', Base32, 'abcdefghijklmnopqrstuvwxyz234567='
|
29
|
+
implement 'base32padupper', 'C', Base32, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567='
|
30
|
+
implement 'base32hex', 'v', Base32, '0123456789abcdefghijklmnopqrstuv'
|
31
|
+
implement 'base32hexupper', 'V', Base32, '0123456789ABCDEFGHIJKLMNOPQRSTUV'
|
32
|
+
implement 'base32hexpad', 't', Base32, '0123456789abcdefghijklmnopqrstuv='
|
33
|
+
implement 'base32hexpadupper', 'T', Base32, '0123456789ABCDEFGHIJKLMNOPQRSTUV='
|
34
|
+
implement 'base32z', 'h', Base32, 'ybndrfg8ejkmcpqxot1uwisza345h769'
|
35
|
+
implement 'base58flickr', 'Z', BaseX, '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'
|
36
|
+
implement 'base58btc', 'z', BaseX, '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
37
|
+
implement 'base64', 'm', Base64, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
|
38
|
+
implement 'base64pad', 'M', Base64, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
|
39
|
+
implement 'base64url', 'u', Base64, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'
|
40
|
+
implement 'base64urlpad', 'U', Base64, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_='
|
41
|
+
# rubocop:enable Metrics/LineLength
|
42
|
+
end
|