base85 0.1.0 → 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 +3 -0
- data/CHANGELOG.md +5 -1
- data/README.md +23 -1
- data/lib/base85/module_methods.rb +78 -0
- data/lib/base85/rfc1924.rb +38 -0
- data/lib/base85/standard.rb +16 -68
- data/lib/base85/version.rb +1 -1
- data/lib/base85/z85.rb +37 -0
- data/lib/base85.rb +11 -0
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4f8a6088b3843d55dae49087d58126a5527c36804fbcee912db569db9950ab40
|
|
4
|
+
data.tar.gz: 5da6748b35556b5723052547d4c10cc0e55a5c1f85ab88f29df32760e164c4ce
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6e63afeb1c585b407dc2bb154ecc1a363e28919b7a354a9ae1c355293c5ed7b3e91c51a98f4adc8bd88c84391f47b4ccf825a545017a96fbb263d0863e5935c9
|
|
7
|
+
data.tar.gz: d0e243fb0f19e05733836879cb47577b0127439e5145f5e11939eb3fab34837ebcba486681061a3f2a53cb8c5d3a161bac73e11b4184a64e937a3c46b749e738
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
Base85 is a pure Ruby gem to encode/decode data using Base85 encoding.
|
|
4
4
|
|
|
5
|
+
It handles mulitple alphabets:
|
|
6
|
+
|
|
7
|
+
* standard (original) one,
|
|
8
|
+
* Z85 from [ZeroMQ](https://github.com/zeromq/libzmq),
|
|
9
|
+
* [RFC1924](https://www.rfc-editor.org/rfc/rfc1924).
|
|
10
|
+
|
|
5
11
|
## Installation
|
|
6
12
|
|
|
7
13
|
Install the gem and add to the application's Gemfile by executing:
|
|
@@ -19,7 +25,15 @@ To encode a string:
|
|
|
19
25
|
```ruby
|
|
20
26
|
require "base85"
|
|
21
27
|
|
|
28
|
+
# Use standard alphabet
|
|
22
29
|
Base85.encode("test1") #=> "FCfN80`"
|
|
30
|
+
Base85.encode("test1", alphabet: :standard) #=> "FCfN80`"
|
|
31
|
+
|
|
32
|
+
# Use Z85 alphabet
|
|
33
|
+
Base85.encode("test1", alphabet: :z85) #=> "By/Jnf-"
|
|
34
|
+
|
|
35
|
+
# Use RFC1924 alphabet
|
|
36
|
+
Base85.encode("test1", alphabet: :rfc1924) #=> "bY*jNF#"
|
|
23
37
|
```
|
|
24
38
|
|
|
25
39
|
To decode a string:
|
|
@@ -27,7 +41,15 @@ To decode a string:
|
|
|
27
41
|
```ruby
|
|
28
42
|
require "base85"
|
|
29
43
|
|
|
30
|
-
|
|
44
|
+
# Use standard alphabet
|
|
45
|
+
Base85.decode("ASu!rA7]9") #=> "encoded"
|
|
46
|
+
Base85.decode("ASu!rA7]9", alphabet: :standard) #=> "encoded"
|
|
47
|
+
|
|
48
|
+
# Use Z85 alphabet
|
|
49
|
+
Base85.decode("wO#0@wmYo", alphabet: :z85) #=> "encoded"
|
|
50
|
+
|
|
51
|
+
# Use RFC1924 alphabet
|
|
52
|
+
Base85.decode("Wo~0{WMyO", alphabet: :rfc1924) #=> "encoded"
|
|
31
53
|
```
|
|
32
54
|
|
|
33
55
|
## Development
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Base85
|
|
4
|
+
# Module handling common module methods to all encodings
|
|
5
|
+
# @author sd77
|
|
6
|
+
module ModuleMethods
|
|
7
|
+
# @private
|
|
8
|
+
LUT = (0..4).map { |count| 85**(4 - count) }.freeze
|
|
9
|
+
|
|
10
|
+
# @private
|
|
11
|
+
# Encode a single word
|
|
12
|
+
# @param [Integer] word
|
|
13
|
+
# @param [Integer] byte_count byte count of word. Must be in range 1..4
|
|
14
|
+
def encode_word(word, byte_count: 4)
|
|
15
|
+
encoded = "~" * 5
|
|
16
|
+
5.times do |i|
|
|
17
|
+
word, r = word.divmod(85)
|
|
18
|
+
encoded[-i - 1] = encode_value(r)
|
|
19
|
+
end
|
|
20
|
+
encoded[..byte_count]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Encode data using standard base85 encoding
|
|
24
|
+
# @param [String] data
|
|
25
|
+
# @return [String]
|
|
26
|
+
def encode(data)
|
|
27
|
+
result = +""
|
|
28
|
+
data.unpack("N*").each do |word|
|
|
29
|
+
result << encode_word(word)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
last_count = data.length % 4
|
|
33
|
+
return result if last_count.zero?
|
|
34
|
+
|
|
35
|
+
last_chars = data[-last_count..] << ("\x00" * (4 - last_count))
|
|
36
|
+
result << encode_word(last_chars.unpack1("N"), byte_count: last_count)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Decode data using standard base85 encoding
|
|
40
|
+
# @param [String] data
|
|
41
|
+
# @return [String]
|
|
42
|
+
# @raise [DecodeError] unknown character or invalid tuple
|
|
43
|
+
def decode(data)
|
|
44
|
+
result = +""
|
|
45
|
+
word = 0
|
|
46
|
+
count = 0
|
|
47
|
+
data.each_char do |c|
|
|
48
|
+
idx = decode_char(c)
|
|
49
|
+
p c if idx.nil?
|
|
50
|
+
|
|
51
|
+
word += idx * LUT[count]
|
|
52
|
+
count += 1
|
|
53
|
+
|
|
54
|
+
if (count == 5) && (word > 0xffffffff)
|
|
55
|
+
raise DecodeError, "invalid tuple"
|
|
56
|
+
elsif count == 5
|
|
57
|
+
wordbuf = " " * 4
|
|
58
|
+
3.downto(0) do |i|
|
|
59
|
+
wordbuf.setbyte(i, word & 0xff)
|
|
60
|
+
word >>= 8
|
|
61
|
+
end
|
|
62
|
+
result << wordbuf
|
|
63
|
+
count = 0
|
|
64
|
+
word = 0
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
return result if count.zero?
|
|
69
|
+
|
|
70
|
+
count -= 1
|
|
71
|
+
word += LUT[count]
|
|
72
|
+
result << ((word >> 24) & 0xff).chr if count >= 1
|
|
73
|
+
result << ((word >> 16) & 0xff).chr if count >= 2
|
|
74
|
+
result << ((word >> 8) & 0xff).chr if count == 3
|
|
75
|
+
result
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Base85
|
|
4
|
+
# RFC 1924 base85 encoding
|
|
5
|
+
# # @author sd77
|
|
6
|
+
module Rfc1924
|
|
7
|
+
extend ModuleMethods
|
|
8
|
+
|
|
9
|
+
# RFC1924 alphabet
|
|
10
|
+
ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~"
|
|
11
|
+
|
|
12
|
+
# Encode a value (0..84 integer) to a RFC 1924 character
|
|
13
|
+
# @param [Integer] value
|
|
14
|
+
# @return [String]
|
|
15
|
+
def self.encode_value(value)
|
|
16
|
+
ALPHABET[value]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Decode a character from RFC 1924 alphabet and give its index in alphabet
|
|
20
|
+
# @param [String] char a acharacter from base85 alphabet
|
|
21
|
+
# @return [Integer] index of this character in alphabet
|
|
22
|
+
# @raise DecodeError +char+ is not known in alphabet
|
|
23
|
+
def self.decode_char(char)
|
|
24
|
+
case char
|
|
25
|
+
when "0".."9"
|
|
26
|
+
char.ord - 48
|
|
27
|
+
when "A".."Z"
|
|
28
|
+
char.ord - 55 # - 65 + 10
|
|
29
|
+
when "a".."z"
|
|
30
|
+
char.ord - 61 # - 97 + 36
|
|
31
|
+
when "!", "#".."&", "(".."+", "-", ";".."@", "^".."`", "{".."~"
|
|
32
|
+
ALPHABET.index(char)
|
|
33
|
+
else
|
|
34
|
+
raise DecodeError, "unknown character '#{char}'"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
data/lib/base85/standard.rb
CHANGED
|
@@ -4,81 +4,29 @@ module Base85
|
|
|
4
4
|
# Standard base85 encoding. This encoding uses '!' to 'u' ASCII characters to encode data.
|
|
5
5
|
# @author sd77
|
|
6
6
|
module Standard
|
|
7
|
+
extend ModuleMethods
|
|
8
|
+
|
|
7
9
|
# ASCII value for character '!'
|
|
8
10
|
BASE_ASCII_VALUE = "!".ord
|
|
9
|
-
# @private
|
|
10
|
-
LUT = (0..4).map { |count| 85**(4 - count) }.freeze
|
|
11
|
-
|
|
12
|
-
# @private
|
|
13
|
-
# Encode a single word
|
|
14
|
-
# @param [Integer] word
|
|
15
|
-
# @param [Integer] byte_count byte count of word. Must be in range 1..4
|
|
16
|
-
def self.encode_word(word, byte_count: 4)
|
|
17
|
-
encoded = "~" * 5
|
|
18
|
-
5.times do |i|
|
|
19
|
-
word, r = word.divmod(85)
|
|
20
|
-
encoded[-i - 1] = (BASE_ASCII_VALUE + r).chr
|
|
21
|
-
end
|
|
22
|
-
encoded[..byte_count]
|
|
23
|
-
end
|
|
24
11
|
|
|
25
|
-
# Encode
|
|
26
|
-
# @param [
|
|
12
|
+
# Encode a value (0..84 integer) to a standard base85 character
|
|
13
|
+
# @param [Integer] value
|
|
27
14
|
# @return [String]
|
|
28
|
-
def self.
|
|
29
|
-
|
|
30
|
-
data.unpack("N*").each do |word|
|
|
31
|
-
result << encode_word(word)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
last_count = data.length % 4
|
|
35
|
-
return result if last_count.zero?
|
|
36
|
-
|
|
37
|
-
last_chars = data[-last_count..] << ("\x00" * (4 - last_count))
|
|
38
|
-
result << encode_word(last_chars.unpack1("N"), byte_count: last_count)
|
|
15
|
+
def self.encode_value(value)
|
|
16
|
+
(BASE_ASCII_VALUE + value).chr
|
|
39
17
|
end
|
|
40
18
|
|
|
41
|
-
# Decode
|
|
42
|
-
# @param [String]
|
|
43
|
-
# @return [
|
|
44
|
-
# @raise
|
|
45
|
-
def self.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
when "!".."u" # rubocop:disable Lint/MixedCaseRange
|
|
52
|
-
idx = c.ord - BASE_ASCII_VALUE
|
|
53
|
-
else
|
|
54
|
-
raise DecodeError, "unknown character '#{c}'"
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
word += idx * LUT[count]
|
|
58
|
-
count += 1
|
|
59
|
-
|
|
60
|
-
if (count == 5) && (word > 0xffffffff)
|
|
61
|
-
raise DecodeError, "invalid tuple"
|
|
62
|
-
elsif count == 5
|
|
63
|
-
wordbuf = " " * 4
|
|
64
|
-
3.downto(0) do |i|
|
|
65
|
-
wordbuf.setbyte(i, word & 0xff)
|
|
66
|
-
word >>= 8
|
|
67
|
-
end
|
|
68
|
-
result << wordbuf
|
|
69
|
-
count = 0
|
|
70
|
-
word = 0
|
|
71
|
-
end
|
|
19
|
+
# Decode a character from standard base85 and give its index in alphabet
|
|
20
|
+
# @param [String] char a acharacter from base85 alphabet
|
|
21
|
+
# @return [Integer] index of this character in alphabet
|
|
22
|
+
# @raise DecodeError +char+ is not known in alphabet
|
|
23
|
+
def self.decode_char(char)
|
|
24
|
+
case char
|
|
25
|
+
when "!".."u" # rubocop:disable Lint/MixedCaseRange
|
|
26
|
+
char.ord - BASE_ASCII_VALUE
|
|
27
|
+
else
|
|
28
|
+
raise DecodeError, "unknown character '#{char}'"
|
|
72
29
|
end
|
|
73
|
-
|
|
74
|
-
return result if count.zero?
|
|
75
|
-
|
|
76
|
-
count -= 1
|
|
77
|
-
word += LUT[count]
|
|
78
|
-
result << ((word >> 24) & 0xff).chr if count >= 1
|
|
79
|
-
result << ((word >> 16) & 0xff).chr if count >= 2
|
|
80
|
-
result << ((word >> 8) & 0xff).chr if count == 3
|
|
81
|
-
result
|
|
82
30
|
end
|
|
83
31
|
end
|
|
84
32
|
end
|
data/lib/base85/version.rb
CHANGED
data/lib/base85/z85.rb
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Base85
|
|
4
|
+
# ZeroMQ (Z85) encoding
|
|
5
|
+
module Z85
|
|
6
|
+
extend ModuleMethods
|
|
7
|
+
|
|
8
|
+
# Z85 alphabet
|
|
9
|
+
ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#"
|
|
10
|
+
|
|
11
|
+
# Encode a value (0..84 integer) to a Z85 character
|
|
12
|
+
# @param [Integer] value
|
|
13
|
+
# @return [String]
|
|
14
|
+
def self.encode_value(value)
|
|
15
|
+
ALPHABET[value]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Decode a character from Z85 and give its index in alphabet
|
|
19
|
+
# @param [String] char a acharacter from base85 alphabet
|
|
20
|
+
# @return [Integer] index of this character in alphabet
|
|
21
|
+
# @raise DecodeError +char+ is not known in alphabet
|
|
22
|
+
def self.decode_char(char)
|
|
23
|
+
case char
|
|
24
|
+
when "0".."9"
|
|
25
|
+
char.ord - 48
|
|
26
|
+
when "a".."z"
|
|
27
|
+
char.ord - 87 # - 97 + 10
|
|
28
|
+
when "A".."Z"
|
|
29
|
+
char.ord - 29 # - 65 + 26
|
|
30
|
+
when "!", "#".."&", "(".."+", "-".."/", ":", "<".."@", "[", "]", "^", "{".."}"
|
|
31
|
+
ALPHABET.index(char)
|
|
32
|
+
else
|
|
33
|
+
raise DecodeError, "unknown character '#{char}'"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
data/lib/base85.rb
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "base85/version"
|
|
4
|
+
require_relative "base85/module_methods"
|
|
4
5
|
require_relative "base85/standard"
|
|
6
|
+
require_relative "base85/z85"
|
|
7
|
+
require_relative "base85/rfc1924"
|
|
5
8
|
|
|
6
9
|
# Provide methods to encode/decode Base85 formatted data
|
|
7
10
|
# @author sd77
|
|
@@ -31,6 +34,10 @@ module Base85
|
|
|
31
34
|
case alphabet
|
|
32
35
|
when :standard
|
|
33
36
|
Standard.encode(data)
|
|
37
|
+
when :z85
|
|
38
|
+
Z85.encode(data)
|
|
39
|
+
when :rfc1924
|
|
40
|
+
Rfc1924.encode(data)
|
|
34
41
|
else
|
|
35
42
|
raise UnknownAlphabetError.new(alphabet)
|
|
36
43
|
end
|
|
@@ -40,6 +47,10 @@ module Base85
|
|
|
40
47
|
case alphabet
|
|
41
48
|
when :standard
|
|
42
49
|
Standard.decode(data)
|
|
50
|
+
when :z85
|
|
51
|
+
Z85.decode(data)
|
|
52
|
+
when :rfc1924
|
|
53
|
+
Rfc1924.decode(data)
|
|
43
54
|
else
|
|
44
55
|
raise UnknownAlphabetError.new(alphabet)
|
|
45
56
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: base85
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- sd77
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-06-
|
|
11
|
+
date: 2025-06-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Encode and decode base85 data. Multiple alphabets are supported (standard,
|
|
14
14
|
Z85 or rfc1924)
|
|
@@ -25,8 +25,11 @@ files:
|
|
|
25
25
|
- README.md
|
|
26
26
|
- Rakefile
|
|
27
27
|
- lib/base85.rb
|
|
28
|
+
- lib/base85/module_methods.rb
|
|
29
|
+
- lib/base85/rfc1924.rb
|
|
28
30
|
- lib/base85/standard.rb
|
|
29
31
|
- lib/base85/version.rb
|
|
32
|
+
- lib/base85/z85.rb
|
|
30
33
|
- sig/base85.rbs
|
|
31
34
|
homepage: https://codeberg.org/sd77/base85
|
|
32
35
|
licenses:
|