base_x 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.ruby_version +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +8 -0
- data/Guardfile +14 -0
- data/LICENSE.txt +3 -0
- data/README.md +197 -0
- data/Rakefile +10 -0
- data/base_x.gemspec +23 -0
- data/lib/base_x.rb +201 -0
- data/lib/base_x/version.rb +3 -0
- data/test/test_base_x.rb +280 -0
- metadata +87 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 230c8a8b317282e89b30d188a1801e81201e31f8
|
4
|
+
data.tar.gz: ae6bc2a2bcd543b4f8be6e8116d5db2520d7aecc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1bf9a6ad14849a82fa486d205a6e25d4d0c8b80cf4741d8f5cee3435c29daba82ab0095278443e38aa47bccb2c9f0ec8a1f5a058f64bfcd08d1427c9ae0b3ff6
|
7
|
+
data.tar.gz: 61fb7c1933f3b53bf55df57fb4e9dcaff89ec0445ef6a16ef772099017e40b202adc0bd2404a0e625bb701c4772e874fccb12ca9a0476ca8d1312e14a5cdbe2e
|
data/.gitignore
ADDED
data/.ruby_version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.1.1
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard :minitest do
|
5
|
+
# with Minitest::Unit
|
6
|
+
watch(%r{^test/(.*)\/?test_(.*)\.rb$})
|
7
|
+
watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
|
8
|
+
watch(%r{^test/test_helper\.rb$}) { 'test' }
|
9
|
+
|
10
|
+
# with Minitest::Spec
|
11
|
+
# watch(%r{^spec/(.*)_spec\.rb$})
|
12
|
+
# watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
13
|
+
# watch(%r{^spec/spec_helper\.rb$}) { 'spec' }
|
14
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,3 @@
|
|
1
|
+
BaseX is dedicated to the public domain by its author, Brian Hempel. No rights are reserved. No restrictions are placed on the use of BaseX. That freedom also means, of course, that no warrenty of fitness is claimed; use BaseX at your own risk.
|
2
|
+
|
3
|
+
Public domain dedication is explained by the CC0 1.0 summary (and only the summary) at https://creativecommons.org/publicdomain/zero/1.0/
|
data/README.md
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
# BaseX
|
2
|
+
|
3
|
+
BaseX is a arbitrary base conversion library that, in addition to converting to/from integers, also supports encoding and decoding arbitrary binary data into and out of any base.
|
4
|
+
|
5
|
+
Many known bases are included, such as [Bitcoin Base58](https://en.bitcoin.it/wiki/Base58Check_encoding).
|
6
|
+
|
7
|
+
BaseX is useful for generating human-friendly cryptographic tokens and ID's, and could even be usde for encoding, transmitting, and decoding binary data over binary-unsafe mediums.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'base_x'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
```
|
20
|
+
$ bundle
|
21
|
+
```
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
```
|
26
|
+
$ gem install base_x
|
27
|
+
```
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
Let's convert some numbers.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
# Emulate hex, just for fun.
|
35
|
+
BaseX.integer_to_string(241, numerals: "0123456789abcdef")
|
36
|
+
# => "f1"
|
37
|
+
BaseX.string_to_integer("f1", numerals: "0123456789abcdef")
|
38
|
+
# => 241
|
39
|
+
|
40
|
+
# Bitcoin Base58 numerals. No O, 0, I, or l
|
41
|
+
BaseX.integer_to_string(456724510, numerals: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
|
42
|
+
# => "hMqHX"
|
43
|
+
BaseX.string_to_integer("hMqHX", numerals: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
|
44
|
+
# => 456724510
|
45
|
+
|
46
|
+
# That was rather clumsy, so let's use the provided Base58 object
|
47
|
+
BaseX::Base58.integer_to_string(456724510)
|
48
|
+
# => "hMqHX"
|
49
|
+
```
|
50
|
+
|
51
|
+
If you use a base more than once, make it a constant.
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
Base24Greek = BaseX.new("αβγδεζηθικλμνξοπρστυφχψω")
|
55
|
+
|
56
|
+
Base24Greek.integer_to_string(456724510)
|
57
|
+
# => "ιψια"
|
58
|
+
Base24Greek.string_to_integer("ιψια")
|
59
|
+
# => 456724510
|
60
|
+
```
|
61
|
+
|
62
|
+
## Encoding
|
63
|
+
|
64
|
+
Use the `encode` and `decode` methods to convert arbitary binary data to and from the desired base.
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
BaseX::Base30L.encode("Hello World!")
|
68
|
+
# => "3xtzc85paptyrbdjgd27"
|
69
|
+
BaseX::Base30L.decode("3xtzc85paptyrbdjgd27")
|
70
|
+
# => "Hello World!"
|
71
|
+
|
72
|
+
BaseX.encode("SOS", numerals: "01")
|
73
|
+
# => "010100110100111101010011"
|
74
|
+
BaseX.decode("010100110100111101010011", numerals: "01")
|
75
|
+
# => "SOS"
|
76
|
+
```
|
77
|
+
|
78
|
+
The encoding scheme is simple: BaseX treats the whole binary blob as one large big-endian binary number and then converts that number into the desired base. If the original binary had leading 0 bytes, leading "0"'s are added to the converted number.
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
Base10 = BaseX.new("0123456789")
|
82
|
+
|
83
|
+
Base10.encode("Hi") # => "18537"
|
84
|
+
Base10.encode("\x00Hi") # => "00018537"
|
85
|
+
Base10.encode("\x00\x00Hi") # => "0000018537"
|
86
|
+
```
|
87
|
+
|
88
|
+
## Token Generation
|
89
|
+
|
90
|
+
BaseX is great for generating tokens for identification or cryptographic purposes.
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
require 'securerandom'
|
94
|
+
|
95
|
+
bytes = SecureRandom.random_bytes(16) # 128 bits
|
96
|
+
|
97
|
+
BaseX::Base30L.encode(bytes)
|
98
|
+
# => "347wbrazxkvj59atq5zh2fdk55e"
|
99
|
+
BaseX::Base58.encode(bytes)
|
100
|
+
# => "SLrA71VABcvExTht6KLr89"
|
101
|
+
```
|
102
|
+
|
103
|
+
## Provided Bases
|
104
|
+
|
105
|
+
BaseX provides some bases you can use right away.
|
106
|
+
|
107
|
+
| Constant(s) | Example (64-bit token) | Numerals and Explanation |
|
108
|
+
|--------------------------------------------------------------------|--------------------------|-----------------------------------------------------------------------------------|
|
109
|
+
| `BaseX::Binary` | `"1111110010001110001…"` | `0-1`<br>Binary! |
|
110
|
+
| `BaseX::Base16` `BaseX::Base16L` `BaseX::Hex` `BaseX::Hexadecimal` | `"fc8e3c917d58368b"` | `0-9a-f`<br>Lowercase [hexadecimal](http://en.wikipedia.org/wiki/Hexadecimal). |
|
111
|
+
| `BaseX::Base16U` | `"FC8E3C917D58368B"` | `0-9A-F`<br>Uppercase [hexadecimal](http://en.wikipedia.org/wiki/Hexadecimal). |
|
112
|
+
| `BaseX::Base30L` | `"369be5e68tfqth"` | `2-9a-hj-km-np-tv-z`<br>(`0 1 i l o u` omitted.)<br>A BaseX special. All characters that could be confused in uppercase or lowercase are removed, so it is suitable for case-insensitive tokens. See `BaseX::CrockfordBase32` for why "u" is removed. |
|
113
|
+
| `BaseX::Base30U` | `"369BE5E68TFQTH"` | `2-9A-HJ-KM-NP-TV-Z`<br>(`0 1 I L O U` omitted.)<br>Uppercase version of above. I think lowercase is easier to read. |
|
114
|
+
| `BaseX::Base31L` | `"s59ez2tk5bep7"` | `0-9a-hj-km-np-z`<br>(`0 1 i l o` omitted.)<br>A BaseX special. All characters that could be confused in uppercase or lowercase are removed, so it is suitable for case-insensitive tokens. The "u" is retained. |
|
115
|
+
| `BaseX::Base31U` | `"S59EZ2TK5BEP7"` | `0-9A-HJ-KM-NP-Z`<br>(`0 1 I L O` omitted.)<br>Uppercase version of above. I think lowercase is easier to read. |
|
116
|
+
| `BaseX::RFC4648Base32` | `"PZDR4SF6VQNUL"` | `A-Z2-7`<br>(`0 1 8 9` omitted.)<br>The base 32 numerals of [RFC 4648](http://tools.ietf.org/html/rfc4648#page-8). |
|
117
|
+
| `BaseX::CrockfordBase32` | `"FS3HWJ5YNGDMB"` | `0-9A-HJ-KM-NP-TV-Z`<br>(`I L O U` omitted.)<br>The numerals of Douglas Crockford's [Base32 proposal](http://www.crockford.com/wrmg/base32.html). Crockford fears that allowing "u" may result in "accidental obscenity". What's the actually probability of a problem? I calculate that, with "u" as a possible numeral, a random 3-numeral string has about a 1/8000 chance of phonetically dropping the f-bomb on a viewer; a 128-bit token encoded in base32 has 26 numerals and thus 24 3-numeral strings, consequently about 1 in every 340 such tokens would be vulgar. |
|
118
|
+
| `BaseX::Base58` `BaseX::BitcoinBase58` | `"jF78uMwAKKg"` | `1-9A-HJ-NP-Za-km-z`<br>(`0 I O l` omitted.)<br>The numeral scheme used in [Bitcoin addresses](https://en.bitcoin.it/wiki/Base58Check_encoding). |
|
119
|
+
| `BaseX::FlickrBase58` | `"Jf78UmWajjF"` | `1-9a-km-zA-HJ-NP-Z`<br>(`0 I O l` omitted.)<br>The numeral scheme in [Flickr short URLs](https://www.flickr.com/groups/api/discuss/72157616713786392/). Same as Bitcoin, but lowercase preceeds uppercase. |
|
120
|
+
| `BaseX::GMPBase58` | `"gE67qKs9IId"` | `0-9A-Za-v`<br>(`w x y z` omitted.)<br>The numeral scheme used for [base conversions in the GMP arbitary-precision math library](https://gmplib.org/manual/Converting-Integers.html). |
|
121
|
+
| `BaseX::NewBase60` | `"W5pTz7hmhxF"` | `0-9A-HJ-NP-Z_a-km-z`<br>(`I O l` omitted, `_` added.)<br>A [scheme by Tantek Çelik](http://tantek.pbworks.com/w/page/19402946/NewBase60), originally for use in a URL shortener. The "_" still allows the whole text to be selected when double-clicking. |
|
122
|
+
| `BaseX::Base62` `BaseX::Base62DUL` | `"LgLY9aNSIf5"` | `0-9A-Za-z`<br>All alphanumeric characters: digits then uppercase then lowercase. |
|
123
|
+
| `BaseX::Base62DLU` | `"lGly9AnsiF5"` | `0-9a-zA-Z`<br>All alphanumeric characters: digits then lowercase then uppercase. |
|
124
|
+
| `BaseX::Base62LDU` | `"vGv8jAx2sFf"` | `a-z0-9A-Z`<br>All alphanumeric characters: lowercase then digits then uppercase. |
|
125
|
+
| `BaseX::Base62LUD` | `"vQvIjKxCsPf"` | `a-zA-Z0-9`<br>All alphanumeric characters: lowercase then uppercase then digits. |
|
126
|
+
| `BaseX::Base62UDL` | `"VgV8JaX2SfF"` | `A-Z0-9a-z`<br>All alphanumeric characters: uppercase then digits then lowercase. |
|
127
|
+
| `BaseX::Base62ULD` | `"VqViJkXcSpF"` | `A-Za-z0-9`<br>All alphanumeric characters: uppercase then lowercase then digits. |
|
128
|
+
| `BaseX::URLBase64` | `"PyOPJF9WDaL"` | `A-Za-z0-9-_`<br>(`- _` added.)<br>Alphanumerics plus `-` and `_`. Intended as a base 64 that can be used in URLs; part of [RFC 4648](http://tools.ietf.org/html/rfc4648#page-7). I find URL base 64 annoying because double-clicking won't select through the `-`. |
|
129
|
+
| `BaseX::Z85` | `"]MO]Dt%j>*"` | `0-9a-zA-Z.-:+=^!/*?&<>()[]{}@%$#`<br>(`. - : + = ^ ! / * ? & < > ( ) [ ] { } @ % $ #` added.)<br>The base 85 numerals used for [ZeroMQ Z85](http://rfc.zeromq.org/spec:32), an encoding standard optimized for 4-byte words and for pasting into single-quoted strings. |
|
130
|
+
| `BaseX::Base256` | `"\xFC\x8E<\x91}X6\x8B"` | `"\x00"-"\xFF"`<br>Byte 0 through byte 255; useful for convert binary strings into a number and back. Used internally by BaseX. |
|
131
|
+
|
132
|
+
Note that although the number schemes from various standards are represented here, BaseX is a number converter only: it does not do padding or other standard-specific details. BaseX is not, for example, a Z85 compliant encoder/decoder. You could, however, easily build one with BaseX.
|
133
|
+
|
134
|
+
In Ruby, you can uses `BaseX.bases` and `BaseX.print_bases` to get information similar to the above table.
|
135
|
+
|
136
|
+
```
|
137
|
+
> BaseX.print_bases
|
138
|
+
Binary 2 111111001000111000111… 01
|
139
|
+
Base16 16 fc8e3c917d58368b 0123456789abcdef
|
140
|
+
Base16L 16 fc8e3c917d58368b 0123456789abcdef
|
141
|
+
Base16U 16 FC8E3C917D58368B 0123456789ABCDEF
|
142
|
+
Hex 16 fc8e3c917d58368b 0123456789abcdef
|
143
|
+
Hexadecimal 16 fc8e3c917d58368b 0123456789abcdef
|
144
|
+
Base30L 30 369be5e68tfqth 23456789abcdefghjkmnpqrstvwxyz
|
145
|
+
...
|
146
|
+
```
|
147
|
+
|
148
|
+
### BaseX.base(n)
|
149
|
+
|
150
|
+
In addition to the named constants above, you can also quickly generate any alphanumeric base between 2 and 62. This could be handy becuase Ruby's `to_i` and `to_s` methods only support bases 2 to 36.
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
BaseX.base(49).integer_to_string(456724510)
|
154
|
+
# => "1UB4UI"
|
155
|
+
BaseX.base(49).string_to_integer("1UB4UI")
|
156
|
+
# => 456724510
|
157
|
+
|
158
|
+
# The order of numerals is digits then uppercase then lowercase
|
159
|
+
BaseX.base(49).numerals
|
160
|
+
# => "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklm"
|
161
|
+
```
|
162
|
+
|
163
|
+
## Use Base30L
|
164
|
+
|
165
|
+
If you want your text to be transferred by any means other than copy-paste, I recommend `BaseX::Base30L`. Tokens have a bad habit of landing in places where they cannot be copied and pasted. Anecdotal evidence from my recent past:
|
166
|
+
|
167
|
+
- There was an infographic that had cited URLs baked into the image. I wanted to check the sources but could not copy the URLs, so I had to retype them. Also, some of the URLs had "O"'s in them.
|
168
|
+
- I recently transferred a shared secret from one computer to another "off the wire". I had my mother read the secret out loud from the other computer. Happily, the secret did not have mixed case: saying "capital L, lowercase u" would have doubled the tedium.
|
169
|
+
|
170
|
+
You can't predict what people are going to do with your encoded text, so it makes sense to be prepared.
|
171
|
+
|
172
|
+
## Edge Cases
|
173
|
+
|
174
|
+
Even though `"".to_i` is `0` in Ruby, BaseX's `string_to_integer` will reject empty strings with a `BaseX::EmptyString` error. An empty string here is probably a bug in your code. (In contrast, the `encode` and `decode` functions do accept empty strings.)
|
175
|
+
|
176
|
+
If you try to convert a string that uses a character not in the base, a `BaseX::InvalidNumeral` error will be raised.
|
177
|
+
|
178
|
+
## Limitations
|
179
|
+
|
180
|
+
If you try to encode/decode in a numeral system larger than base 256 (why would you do this?!), leading 0 bytes may not be properly preserved. Integer conversion will still work as expected.
|
181
|
+
|
182
|
+
## License
|
183
|
+
|
184
|
+
Public Domain; no rights reserved.
|
185
|
+
|
186
|
+
No restrictions are placed on the use of BaseX. That freedom also means, of course, that no warrenty of fitness is claimed; use BaseX at your own risk.
|
187
|
+
|
188
|
+
Public domain dedication is explained by the CC0 1.0 summary (and only the summary) at https://creativecommons.org/publicdomain/zero/1.0/
|
189
|
+
|
190
|
+
## Contributing
|
191
|
+
|
192
|
+
1. Fork it ( http://github.com/brianhempel/base_x/fork )
|
193
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
194
|
+
3. Run the tests with either `guard` or `minitest`
|
195
|
+
4. Commit your changes (`git commit -am 'Add some feature'`)
|
196
|
+
5. Push to the branch (`git push -u origin my-new-feature`)
|
197
|
+
6. Create a new Pull Request
|
data/Rakefile
ADDED
data/base_x.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'base_x/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "base_x"
|
8
|
+
spec.version = BaseX::VERSION
|
9
|
+
spec.authors = ["Brian Hempel"]
|
10
|
+
spec.email = ["plasticchicken@gmail.com"]
|
11
|
+
spec.summary = %q{Convert numbers into and out of any base, also allows encoding and decoding binary data. Many bases included.}
|
12
|
+
spec.description = spec.summary
|
13
|
+
spec.homepage = "https://github.com/brianhempel/base_x"
|
14
|
+
spec.license = "Public Domain"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
end
|
data/lib/base_x.rb
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
require "base_x/version"
|
2
|
+
|
3
|
+
class BaseX
|
4
|
+
class InvalidNumeral < RuntimeError
|
5
|
+
def initialize(invalid_char)
|
6
|
+
super "Can't convert to integer, '#{invalid_char}' is not in the numeral list for this base"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class EmptyString < RuntimeError
|
11
|
+
def initialize
|
12
|
+
super "Can't convert empty string into integer"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
EXAMPLE_TOKEN = "\xFC\x8E\x3C\x91\x7D\x58\x36\x8B" # Random 64-bit token
|
17
|
+
|
18
|
+
def self.string_to_integer(string, opts)
|
19
|
+
opts[:numerals].respond_to?(:index) or
|
20
|
+
raise ArgumentError.new("A string of numerals must be provided, e.g. BaseX.string_to_integer(\"bcde\", numerals: \"abcdefg\")")
|
21
|
+
new(opts[:numerals]).string_to_integer(string)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.integer_to_string(int, opts)
|
25
|
+
opts[:numerals].respond_to?(:index) or
|
26
|
+
raise ArgumentError.new("A string of numerals must be provided, e.g. BaseX.integer_to_string(123, numerals: \"abcdefg\")")
|
27
|
+
new(opts[:numerals]).integer_to_string(int)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.encode(string, opts)
|
31
|
+
opts[:numerals].respond_to?(:index) or
|
32
|
+
raise ArgumentError.new("A string of numerals must be provided, e.g. BaseX.encode(\"Hello World\", numerals: \"abcdefg\")")
|
33
|
+
new(opts[:numerals]).encode(string)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.decode(encoded, opts)
|
37
|
+
opts[:numerals].respond_to?(:index) or
|
38
|
+
raise ArgumentError.new("A string of numerals must be provided, e.g. BaseX.encode(\"bcaffag\", numerals: \"abcdefg\")")
|
39
|
+
new(opts[:numerals]).decode(encoded)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.base(n)
|
43
|
+
n.between?(2, 62) or raise ArgumentError.new("Base #{n} is not valid; base must be at least 2 and at most 62")
|
44
|
+
digits_uppercase_lowercase = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
45
|
+
new(digits_uppercase_lowercase[0...n])
|
46
|
+
end
|
47
|
+
|
48
|
+
# Outputs an array of [base name, base size, example, and numerals] for all built-in bases
|
49
|
+
def self.bases
|
50
|
+
constants
|
51
|
+
.map { |const_name| [const_name, const_get(const_name)] }
|
52
|
+
.select { |const_name, base| base.is_a?(BaseX) }
|
53
|
+
.sort_by { |const_name, base| [base.base, const_name.to_s] }
|
54
|
+
.map do |const_name, base|
|
55
|
+
[const_name.to_s, base.base, base.encode(EXAMPLE_TOKEN), base.numerals]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.bases_table
|
60
|
+
bases.map do |name, base_size, example, numerals|
|
61
|
+
example = example[0...21] + "…" if example.size > 22
|
62
|
+
[
|
63
|
+
name,
|
64
|
+
base_size,
|
65
|
+
example.inspect.include?("\\") ? example.inspect : example,
|
66
|
+
numerals.inspect.include?("\\") ? numerals.inspect : numerals,
|
67
|
+
]
|
68
|
+
end.map do |array|
|
69
|
+
"%-15s %-3s %-22s %s" % array
|
70
|
+
end.join("\n")
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.print_bases
|
74
|
+
puts bases_table
|
75
|
+
end
|
76
|
+
|
77
|
+
attr_reader :numerals
|
78
|
+
attr_reader :base
|
79
|
+
|
80
|
+
def initialize(numerals)
|
81
|
+
numerals.chars.size > 1 or
|
82
|
+
raise ArgumentError.new("Need at least two numerals to express numbers! Numeral given: #{numerals.inspect}")
|
83
|
+
numerals.chars.size == numerals.chars.uniq.size or
|
84
|
+
raise ArgumentError.new("Duplicate characters found in numerals definition: #{numerals.inspect}")
|
85
|
+
@numerals = numerals
|
86
|
+
@base = numerals.size
|
87
|
+
end
|
88
|
+
|
89
|
+
def string_to_integer(string)
|
90
|
+
raise EmptyString.new unless string.size > 0
|
91
|
+
integer = 0
|
92
|
+
string.each_char do |char|
|
93
|
+
integer *= base
|
94
|
+
numeral_value = @numerals.index(char) or raise InvalidNumeral.new(char)
|
95
|
+
integer += numeral_value
|
96
|
+
end
|
97
|
+
integer
|
98
|
+
end
|
99
|
+
|
100
|
+
def integer_to_string(int)
|
101
|
+
return @numerals[0] if int == 0
|
102
|
+
string = ""
|
103
|
+
while int > 0
|
104
|
+
char = @numerals[int % base]
|
105
|
+
string << char
|
106
|
+
int /= base
|
107
|
+
end
|
108
|
+
string.reverse
|
109
|
+
end
|
110
|
+
|
111
|
+
def encode(string)
|
112
|
+
return "" if string.size == 0
|
113
|
+
int = string.each_byte.reduce(0) { |int, byte| int *= 256; int + byte }
|
114
|
+
encoded = integer_to_string(int)
|
115
|
+
string_number_size = 256**(string.size)
|
116
|
+
encoded_number_size = base**(encoded.size)
|
117
|
+
|
118
|
+
while encoded_number_size < string_number_size
|
119
|
+
encoded = @numerals[0] + encoded
|
120
|
+
encoded_number_size *= base
|
121
|
+
end
|
122
|
+
|
123
|
+
encoded
|
124
|
+
end
|
125
|
+
|
126
|
+
def decode(encoded)
|
127
|
+
return "" if encoded.size == 0
|
128
|
+
int = string_to_integer(encoded)
|
129
|
+
decoded = Base256.integer_to_string(int)
|
130
|
+
decoded_number_size = 256**(decoded.size)
|
131
|
+
encoded_number_size = base**(encoded.size)
|
132
|
+
|
133
|
+
# encoded_number_size / base < decoded_number_size <= encoded_number_size
|
134
|
+
while decoded_number_size <= encoded_number_size / base
|
135
|
+
decoded = "\x00" + decoded
|
136
|
+
decoded_number_size *= 256
|
137
|
+
end
|
138
|
+
|
139
|
+
decoded
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
digits = ("0".."9").to_a
|
144
|
+
uppercase = ("A".."Z").to_a
|
145
|
+
lowercase = ("a".."z").to_a
|
146
|
+
|
147
|
+
# Binary
|
148
|
+
BaseX::Binary = BaseX.new("01")
|
149
|
+
|
150
|
+
# Base 16
|
151
|
+
BaseX::Base16L = BaseX.new((digits + %w[a b c d e f]).join)
|
152
|
+
BaseX::Base16U = BaseX.new((digits + %w[A B C D E F]).join)
|
153
|
+
BaseX::Base16 = BaseX::Base16L
|
154
|
+
BaseX::Hex = BaseX::Base16
|
155
|
+
BaseX::Hexadecimal = BaseX::Base16
|
156
|
+
|
157
|
+
# Base 30, in case the number could change case
|
158
|
+
# no "u" following Crockford's probabilistic fear of accidental obscenity
|
159
|
+
# (I caluclate the probability of obscenity, with "u", is about 1 in 2^13
|
160
|
+
# for any given random 3-letter string (about 1 in 8000); when encoding 128bit
|
161
|
+
# tokens, after generating about 225 random tokens you have a 50% chance of
|
162
|
+
# having produced an "obscene token".)
|
163
|
+
BaseX::Base30L = BaseX.new((digits + lowercase - %w[0 1 i l o u]).join)
|
164
|
+
BaseX::Base30U = BaseX.new((digits + uppercase - %w[0 1 I L O U]).join)
|
165
|
+
|
166
|
+
# Base 31, in case the number could change case
|
167
|
+
BaseX::Base31L = BaseX.new((digits + lowercase - %w[0 1 i l o]).join)
|
168
|
+
BaseX::Base31U = BaseX.new((digits + uppercase - %w[0 1 I L O]).join)
|
169
|
+
|
170
|
+
# Base 32 schemes
|
171
|
+
BaseX::RFC4648Base32 = BaseX.new((uppercase + digits - %w[0 1 8 9]).join)
|
172
|
+
BaseX::CrockfordBase32 = BaseX.new((digits + uppercase - %w[I L O U]).join)
|
173
|
+
|
174
|
+
# Base58 schemes
|
175
|
+
BaseX::BitcoinBase58 = BaseX.new((digits + uppercase + lowercase - %w[0 O I l]).join)
|
176
|
+
BaseX::FlickrBase58 = BaseX.new((digits + lowercase + uppercase - %w[0 O I l]).join)
|
177
|
+
BaseX::GMPBase58 = BaseX.new((digits + uppercase + lowercase - %w[w x y z]).join)
|
178
|
+
BaseX::Base58 = BaseX::BitcoinBase58
|
179
|
+
|
180
|
+
# NewBase60, has an underscore as per http://tantek.pbworks.com/w/page/19402946/NewBase60
|
181
|
+
BaseX::NewBase60 = BaseX.new((digits + uppercase + %w[_] + lowercase - %w[O I l]).join)
|
182
|
+
|
183
|
+
# Base62; digits, upper, lower
|
184
|
+
BaseX::Base62DUL = BaseX.new((digits + uppercase + lowercase).join)
|
185
|
+
BaseX::Base62DLU = BaseX.new((digits + lowercase + uppercase).join)
|
186
|
+
BaseX::Base62LDU = BaseX.new((lowercase + digits + uppercase).join)
|
187
|
+
BaseX::Base62LUD = BaseX.new((lowercase + uppercase + digits).join)
|
188
|
+
BaseX::Base62UDL = BaseX.new((uppercase + digits + lowercase).join)
|
189
|
+
BaseX::Base62ULD = BaseX.new((uppercase + lowercase + digits).join)
|
190
|
+
BaseX::Base62 = BaseX::Base62DUL
|
191
|
+
|
192
|
+
# URL Base 64
|
193
|
+
BaseX::URLBase64 = BaseX.new((uppercase + lowercase + digits + %w[- _]).join)
|
194
|
+
|
195
|
+
# ZeroMQ Base 85
|
196
|
+
# Process your data back and forth between 4-byte and 5-byte chunks and you'll be compatible with the Z85 standard
|
197
|
+
BaseX::Z85 = BaseX.new((digits + lowercase + uppercase + %w|. - : + = ^ ! / * ? & < > ( ) [ ] { } @ % $ #|).join)
|
198
|
+
|
199
|
+
# Binary string encoding; turn binary strings into Bignums and back
|
200
|
+
BaseX::Base256 = BaseX.new((0..255).map(&:chr).join)
|
201
|
+
|
data/test/test_base_x.rb
ADDED
@@ -0,0 +1,280 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
Bundler.require(:test)
|
3
|
+
|
4
|
+
require 'base_x'
|
5
|
+
require 'minitest/autorun'
|
6
|
+
require 'minitest/reporters'
|
7
|
+
MiniTest::Reporters.use! Minitest::Reporters::DefaultReporter.new
|
8
|
+
|
9
|
+
class TestBaseX < Minitest::Test
|
10
|
+
def setup
|
11
|
+
@base_x = BaseX.new("012")
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :base_x
|
15
|
+
|
16
|
+
|
17
|
+
### Instance Methods ###
|
18
|
+
|
19
|
+
|
20
|
+
def test_complains_if_numerals_has_duplicate_chars
|
21
|
+
BaseX.new("∫©¶çΩ≈") # no error
|
22
|
+
assert_raises(ArgumentError) { BaseX.new("∫©¶çΩ≈Ω") }
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_complains_if_only_one_numeral
|
26
|
+
BaseX.new("∫©") # no error
|
27
|
+
assert_raises(ArgumentError) { BaseX.new("") }
|
28
|
+
assert_raises(ArgumentError) { BaseX.new("∫") }
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_string_to_integer
|
32
|
+
assert_equal 48, base_x.string_to_integer("01210")
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_string_to_integer_errors_with_invalid_chars
|
36
|
+
assert_raises(BaseX::InvalidNumeral) do
|
37
|
+
base_x.string_to_integer("asdf")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_string_to_integer_errors_with_empty_string
|
42
|
+
assert_raises(BaseX::EmptyString) do
|
43
|
+
base_x.string_to_integer("")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_integer_to_string
|
48
|
+
assert_equal "1210", base_x.integer_to_string(48)
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_0_to_string
|
52
|
+
assert_equal "0", base_x.integer_to_string(0)
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_encode_decode_empty_string
|
56
|
+
assert_equal "", base_x.decode(base_x.encode(""))
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_encode_decode_zeros
|
60
|
+
assert_equal "\x00\x00\x00\x00", base_x.decode(base_x.encode("\x00\x00\x00\x00"))
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_encode_decode_stuff
|
64
|
+
assert_equal "stuff", base_x.decode(base_x.encode("stuff"))
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_a_bunch_of_zero_strings_in_a_bunch_of_bases
|
68
|
+
(2..10).each do |base|
|
69
|
+
base_x = BaseX.new(('a'..'z').take(base).join)
|
70
|
+
(1..257).to_a.sample(20).each do |size|
|
71
|
+
assert_equal "\x00"*size, base_x.decode(base_x.encode("\x00"*size)), "problem with #{size} length zero string in base #{base}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_a_bunch_of_random_strings_in_a_bunch_of_bases
|
77
|
+
(2..10).each do |base|
|
78
|
+
base_x = BaseX.new(('a'..'z').take(base).join)
|
79
|
+
(1..257).to_a.sample(20).each do |size|
|
80
|
+
string = SecureRandom.random_bytes(size)
|
81
|
+
assert_equal string.dup, base_x.decode(base_x.encode(string)), "problem encoding/decoding #{string.inspect} in base #{base}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
### Class Methods ###
|
88
|
+
|
89
|
+
|
90
|
+
def test_integer_to_string_class_method
|
91
|
+
assert_equal "1210", BaseX.integer_to_string(48, numerals: "012")
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_integer_to_string_claass_methods_complains_if_no_numerals_provided
|
95
|
+
assert_raises(ArgumentError) { BaseX.integer_to_string(48, numerals: Object.new) }
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_string_to_integer_class_method
|
99
|
+
assert_equal 48, BaseX.string_to_integer("01210", numerals: "012")
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_string_to_integer_claass_methods_complains_if_no_numerals_provided
|
103
|
+
assert_raises(ArgumentError) { BaseX.string_to_integer("01210", numerals: Object.new) }
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_encode_decode_class_methods
|
107
|
+
assert_equal "stuff", BaseX.decode(BaseX.encode("stuff", numerals: "012"), numerals: "012")
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_encode_class_method_complains_if_no_numerals_provided
|
111
|
+
assert_raises(ArgumentError) { BaseX.encode("stuff", numerals: Object.new) }
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_decode_class_method_complains_if_no_numerals_provided
|
115
|
+
assert_raises(ArgumentError) { BaseX.decode("stuff", numerals: Object.new) }
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_base_class_method
|
119
|
+
assert_equal "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZab", BaseX.base(38).numerals
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_base_class_method_errors
|
123
|
+
BaseX.base(2) # no error
|
124
|
+
BaseX.base(62) # no error
|
125
|
+
assert_raises(ArgumentError) { BaseX.base(1) }
|
126
|
+
assert_raises(ArgumentError) { BaseX.base(63) }
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_bases_class_method
|
130
|
+
assert_equal ["Binary", 2, "1111110010001110001111001001000101111101010110000011011010001011", "01"], BaseX.bases.first
|
131
|
+
expected_order = %w[
|
132
|
+
Binary
|
133
|
+
Base16
|
134
|
+
Base16L
|
135
|
+
Base16U
|
136
|
+
Hex
|
137
|
+
Hexadecimal
|
138
|
+
Base30L
|
139
|
+
Base30U
|
140
|
+
Base31L
|
141
|
+
Base31U
|
142
|
+
CrockfordBase32
|
143
|
+
RFC4648Base32
|
144
|
+
Base58
|
145
|
+
BitcoinBase58
|
146
|
+
FlickrBase58
|
147
|
+
GMPBase58
|
148
|
+
NewBase60
|
149
|
+
Base62
|
150
|
+
Base62DLU
|
151
|
+
Base62DUL
|
152
|
+
Base62LDU
|
153
|
+
Base62LUD
|
154
|
+
Base62UDL
|
155
|
+
Base62ULD
|
156
|
+
URLBase64
|
157
|
+
Z85
|
158
|
+
Base256
|
159
|
+
]
|
160
|
+
assert_equal expected_order, BaseX.bases.map(&:first)
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_bases_table_class_method
|
164
|
+
assert_equal String, BaseX.bases_table.class # it doesn't blow up
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
### Base Constants ###
|
169
|
+
|
170
|
+
|
171
|
+
def test_binary
|
172
|
+
assert_equal "111010110111100110100010101", BaseX::Binary.integer_to_string(123456789)
|
173
|
+
assert_equal "111010110111100110100010110001", BaseX::Binary.integer_to_string(987654321)
|
174
|
+
end
|
175
|
+
|
176
|
+
def test_base16L
|
177
|
+
assert_equal "75bcd15", BaseX::Base16L.integer_to_string(123456789)
|
178
|
+
assert_equal "3ade68b1", BaseX::Base16L.integer_to_string(987654321)
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_base16U
|
182
|
+
assert_equal "75BCD15", BaseX::Base16U.integer_to_string(123456789)
|
183
|
+
assert_equal "3ADE68B1", BaseX::Base16U.integer_to_string(987654321)
|
184
|
+
end
|
185
|
+
|
186
|
+
def test_base30L
|
187
|
+
assert_equal "74eg8b", BaseX::Base30L.integer_to_string(123456789)
|
188
|
+
assert_equal "3cnbspq", BaseX::Base30L.integer_to_string(987654321)
|
189
|
+
end
|
190
|
+
|
191
|
+
def test_base30U
|
192
|
+
assert_equal "74EG8B", BaseX::Base30U.integer_to_string(123456789)
|
193
|
+
assert_equal "3CNBSPQ", BaseX::Base30U.integer_to_string(987654321)
|
194
|
+
end
|
195
|
+
|
196
|
+
def test_base31L
|
197
|
+
assert_equal "6bq524", BaseX::Base31L.integer_to_string(123456789)
|
198
|
+
assert_equal "35hft2u", BaseX::Base31L.integer_to_string(987654321)
|
199
|
+
end
|
200
|
+
|
201
|
+
def test_base31U
|
202
|
+
assert_equal "6BQ524", BaseX::Base31U.integer_to_string(123456789)
|
203
|
+
assert_equal "35HFT2U", BaseX::Base31U.integer_to_string(987654321)
|
204
|
+
end
|
205
|
+
|
206
|
+
def test_rfc4648_base32
|
207
|
+
assert_equal "DVXTIV", BaseX::RFC4648Base32.integer_to_string(123456789)
|
208
|
+
assert_equal "5N42FR", BaseX::RFC4648Base32.integer_to_string(987654321)
|
209
|
+
end
|
210
|
+
|
211
|
+
def test_crockford_base32
|
212
|
+
assert_equal "3NQK8N", BaseX::CrockfordBase32.integer_to_string(123456789)
|
213
|
+
assert_equal "XDWT5H", BaseX::CrockfordBase32.integer_to_string(987654321)
|
214
|
+
end
|
215
|
+
|
216
|
+
def test_bitcoin_base58
|
217
|
+
assert_equal "BukQL", BaseX::BitcoinBase58.integer_to_string(123456789)
|
218
|
+
assert_equal "2WGzDn", BaseX::BitcoinBase58.integer_to_string(987654321)
|
219
|
+
end
|
220
|
+
|
221
|
+
def test_flickr_base58
|
222
|
+
assert_equal "bUKpk", BaseX::FlickrBase58.integer_to_string(123456789)
|
223
|
+
assert_equal "2vgZdM", BaseX::FlickrBase58.integer_to_string(987654321)
|
224
|
+
end
|
225
|
+
|
226
|
+
def test_gmp_base58
|
227
|
+
assert_equal "AqhNJ", BaseX::GMPBase58.integer_to_string(123456789)
|
228
|
+
assert_equal "1TFvCj", BaseX::GMPBase58.integer_to_string(987654321)
|
229
|
+
end
|
230
|
+
|
231
|
+
def test_new_base60
|
232
|
+
assert_equal "9XZZ9", BaseX::NewBase60.integer_to_string(123456789)
|
233
|
+
assert_equal "1GCURM", BaseX::NewBase60.integer_to_string(987654321)
|
234
|
+
end
|
235
|
+
|
236
|
+
def test_base62_dul
|
237
|
+
assert_equal "8M0kX", BaseX::Base62DUL.integer_to_string(123456789)
|
238
|
+
assert_equal "14q60P", BaseX::Base62DUL.integer_to_string(987654321)
|
239
|
+
end
|
240
|
+
|
241
|
+
def test_base62_dlu
|
242
|
+
assert_equal "8m0Kx", BaseX::Base62DLU.integer_to_string(123456789)
|
243
|
+
assert_equal "14Q60p", BaseX::Base62DLU.integer_to_string(987654321)
|
244
|
+
end
|
245
|
+
|
246
|
+
def test_base62_ldu
|
247
|
+
assert_equal "iwaK7", BaseX::Base62LDU.integer_to_string(123456789)
|
248
|
+
assert_equal "beQgaz", BaseX::Base62LDU.integer_to_string(987654321)
|
249
|
+
end
|
250
|
+
|
251
|
+
def test_base62_lud
|
252
|
+
assert_equal "iwaUH", BaseX::Base62LUD.integer_to_string(123456789)
|
253
|
+
assert_equal "be0gaz", BaseX::Base62LUD.integer_to_string(987654321)
|
254
|
+
end
|
255
|
+
|
256
|
+
def test_base62_udl
|
257
|
+
assert_equal "IWAk7", BaseX::Base62UDL.integer_to_string(123456789)
|
258
|
+
assert_equal "BEqGAZ", BaseX::Base62UDL.integer_to_string(987654321)
|
259
|
+
end
|
260
|
+
|
261
|
+
def test_base62_uld
|
262
|
+
assert_equal "IWAuh", BaseX::Base62ULD.integer_to_string(123456789)
|
263
|
+
assert_equal "BE0GAZ", BaseX::Base62ULD.integer_to_string(987654321)
|
264
|
+
end
|
265
|
+
|
266
|
+
def test_url_base64
|
267
|
+
assert_equal "HW80V", BaseX::URLBase64.integer_to_string(123456789)
|
268
|
+
assert_equal "63mix", BaseX::URLBase64.integer_to_string(987654321)
|
269
|
+
end
|
270
|
+
|
271
|
+
def test_z85
|
272
|
+
assert_equal "2v2B/", BaseX::Z85.integer_to_string(123456789)
|
273
|
+
assert_equal "i]jLP", BaseX::Z85.integer_to_string(987654321)
|
274
|
+
end
|
275
|
+
|
276
|
+
def test_base256
|
277
|
+
assert_equal "\x07\x5B\xCD\x15".force_encoding("ASCII-8BIT"), BaseX::Base256.integer_to_string(123456789)
|
278
|
+
assert_equal "\x3A\xDE\x68\xB1".force_encoding("ASCII-8BIT"), BaseX::Base256.integer_to_string(987654321)
|
279
|
+
end
|
280
|
+
end
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: base_x
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.8.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brian Hempel
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-05-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.5'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Convert numbers into and out of any base, also allows encoding and decoding
|
42
|
+
binary data. Many bases included.
|
43
|
+
email:
|
44
|
+
- plasticchicken@gmail.com
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- ".gitignore"
|
50
|
+
- ".ruby_version"
|
51
|
+
- ".travis.yml"
|
52
|
+
- Gemfile
|
53
|
+
- Guardfile
|
54
|
+
- LICENSE.txt
|
55
|
+
- README.md
|
56
|
+
- Rakefile
|
57
|
+
- base_x.gemspec
|
58
|
+
- lib/base_x.rb
|
59
|
+
- lib/base_x/version.rb
|
60
|
+
- test/test_base_x.rb
|
61
|
+
homepage: https://github.com/brianhempel/base_x
|
62
|
+
licenses:
|
63
|
+
- Public Domain
|
64
|
+
metadata: {}
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
requirements: []
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 2.2.2
|
82
|
+
signing_key:
|
83
|
+
specification_version: 4
|
84
|
+
summary: Convert numbers into and out of any base, also allows encoding and decoding
|
85
|
+
binary data. Many bases included.
|
86
|
+
test_files:
|
87
|
+
- test/test_base_x.rb
|