base85 0.1.0 → 0.3.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: 328dd90db168fa41b46bfe68fbee1fecf63547fa31cdae25a9405753c58cb8f3
4
- data.tar.gz: 631040e522c72ec2191b709d024334f98a0c1caf1a71067ae3c63563d4eedc6b
3
+ metadata.gz: cbae229dec12dca96d21882da5944e4370e6f4d3096e85dad9ce0183a21c18d6
4
+ data.tar.gz: 8bfb829e5a5b61d5f6169940a55d74cc1534699350a2c563d0fe3feb51373d67
5
5
  SHA512:
6
- metadata.gz: 74fd0e3d3a76477ae6598e6665499150b3c874dc2e052d9797dc884a82a4736724a28647a16f7873cf502d8893f733b22b0cafb544097090de1ece7d81f0c6f2
7
- data.tar.gz: 7d34cdcd2606ad5ed01f95875c4b1b3d183af6855dd0fda9f543fe179c63ac0d556f4ac500765f8b5cfd55bf3eacb762aece0f87bb29991d5721c42273ae220b
6
+ metadata.gz: 894e13e4725732b656aa9b97194c7e81ef0fc9b87d4fa7d255e23762586a88b0396da955004de092677c5423de6384f7faf23fcc6b4c093c6fd5e82a5ada6cdd
7
+ data.tar.gz: f10eb21ea204e722f99b6b2c8f08fc7147bb48737b77b91c64cd9db95b72ad8925cd61f4ca380aedba01aa0390c556a521c61de54786049564fc99cd65da1aab
data/.rubocop.yml CHANGED
@@ -10,9 +10,21 @@ AllCops:
10
10
  Layout/LineLength:
11
11
  Max: 120
12
12
 
13
+ Metrics/AbcSize:
14
+ Max: 18
15
+
16
+ Metrics/ClassLength:
17
+ Enabled: false
18
+
19
+ Metrics/MethodLength:
20
+ Max: 20
21
+
13
22
  RSpec/DescribedClass:
14
23
  Enabled: false
15
24
 
25
+ RSpec/MultipleExpectations:
26
+ Max: 2
27
+
16
28
  Style/RaiseArgs:
17
29
  Enabled: false
18
30
 
data/CHANGELOG.md CHANGED
@@ -1,4 +1,13 @@
1
- ## [Unreleased]
1
+ # Changelog
2
+
3
+ ## [0.3.0] - 2025-06-xx
4
+
5
+ - Add Ascii85 alphabet (Adobe flavor)
6
+ - Add base85 command line tool
7
+
8
+ ## [0.2.0] - 2025-06-25
9
+
10
+ - Add Z85 and RFC1924 alphabets
2
11
 
3
12
  ## [0.1.0] - 2025-06-14
4
13
 
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2025 TODO: Write your name
3
+ Copyright (c) 2025 sd77
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,6 +1,15 @@
1
+ [![Gem Version](https://badge.fury.io/rb/base85.svg)](https://badge.fury.io/rb/base85)
2
+
1
3
  # Base85
2
4
 
3
- Base85 is a pure Ruby gem to encode/decode data using Base85 encoding.
5
+ Base85 is a pure Ruby gem to encode/decode data using Base85 encodings.
6
+
7
+ It handles mulitple alphabets:
8
+
9
+ * standard (original) one,
10
+ * Ascii85 (Adobe version, very similar to standard one but with start and end markers, and a special 'z' character for 4 null bytes),
11
+ * Z85 from [ZeroMQ](https://github.com/zeromq/libzmq),
12
+ * [RFC1924](https://www.rfc-editor.org/rfc/rfc1924).
4
13
 
5
14
  ## Installation
6
15
 
@@ -14,12 +23,50 @@ If bundler is not being used to manage dependencies, install the gem by executin
14
23
 
15
24
  ## Usage
16
25
 
26
+ ### CLI
27
+
28
+ `base85` command line tool may be used to decode/encode base85 dialects on command line.
29
+
30
+ ```bash
31
+ # Encode data from STDIN to STDOUT
32
+ echo "text to encode" | base85
33
+ # Encode using given alphabet (standard (default one), z85, ascii85 or rfc1924)
34
+ echo "text to encode" | base85 -a z85
35
+ # Output data is wrapped to 72 columns by default (as do base64)
36
+ # To change this value, use -w option (0 to deactivate wrapping)
37
+ echo "text to encode" | base85 -a rfc1924 -w 0
38
+
39
+ # Encode file to STDOUT
40
+ base85 -a ascii85 -w 40 myfile
41
+
42
+ # Decode data from STDIN to STDOUT
43
+ echo "aaaaa" | base85 -d
44
+ # Encode using given alphabet (standard (default one), z85, ascii85 or rfc1924)
45
+ echo "aaaaa" | base85 -d -a z85
46
+
47
+ # Decode file to STDOUT
48
+ base85 -d -a rfc1924 myfile
49
+ ```
50
+
51
+ ### Ruby library
52
+
17
53
  To encode a string:
18
54
 
19
55
  ```ruby
20
56
  require "base85"
21
57
 
58
+ # Use standard alphabet
22
59
  Base85.encode("test1") #=> "FCfN80`"
60
+ Base85.encode("test1", alphabet: :standard) #=> "FCfN80`"
61
+
62
+ # Use Ascii85 alphabet
63
+ Base85.encode("test1", alphabet: :ascii85) #=> "<~FCfN80`~>"
64
+
65
+ # Use Z85 alphabet
66
+ Base85.encode("test1", alphabet: :z85) #=> "By/Jnf-"
67
+
68
+ # Use RFC1924 alphabet
69
+ Base85.encode("test1", alphabet: :rfc1924) #=> "bY*jNF#"
23
70
  ```
24
71
 
25
72
  To decode a string:
@@ -27,14 +74,23 @@ To decode a string:
27
74
  ```ruby
28
75
  require "base85"
29
76
 
30
- Base85.decode("!!!!!") #=> "\u0000\u0000\u0000\u0000"
31
- ```
77
+ # Use standard alphabet
78
+ Base85.decode("ASu!rA7]9") #=> "encoded"
79
+ Base85.decode("ASu!rA7]9", alphabet: :standard) #=> "encoded"
32
80
 
33
- ## Development
81
+ # Use Ascii85 alphabet
82
+ Base85.decode("<~ASu!rA7]9~>", alphabet: :ascii85) #=> "encoded"
83
+ # Special z character
84
+ Base85.decode("<~z~>", alphabet: :ascii85) #=> "\u0000\u0000\u0000\u0000"
85
+ # Also accept string without markers
86
+ Base85.decode("ASu!rA7]9", alphabet: :ascii85) #=> "encoded"
34
87
 
35
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake spec` to run the tests. You can also run `bundle exec bin/console` for an interactive prompt that will allow you to experiment.
88
+ # Use Z85 alphabet
89
+ Base85.decode("wO#0@wmYo", alphabet: :z85) #=> "encoded"
36
90
 
37
- 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).
91
+ # Use RFC1924 alphabet
92
+ Base85.decode("Wo~0{WMyO", alphabet: :rfc1924) #=> "encoded"
93
+ ```
38
94
 
39
95
  ## Contributing
40
96
 
data/Rakefile CHANGED
@@ -10,3 +10,13 @@ require "rubocop/rake_task"
10
10
  RuboCop::RakeTask.new
11
11
 
12
12
  task default: %i[spec rubocop]
13
+
14
+ begin
15
+ require "yard"
16
+
17
+ YARD::Rake::YardocTask.new do |t|
18
+ t.files = ["lib/**/*.rb", "-", "README.md", "LICENSE.txt"]
19
+ t.options = %w[--no-private]
20
+ end
21
+ rescue LoadError # rubocop:disable Lint/SuppressedException
22
+ end
data/exe/base85 ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "base85/cli"
5
+
6
+ Base85::CLI.start(ARGV)
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Base85
4
+ # Ascii85 base85 encoding. This encoding uses +!+ to +u+ ASCII characters to encode data. +z+ is used to represent
5
+ # 32-bit 0 value. The encoded string is surrounded by +<~+ and +~>+ markers.
6
+ # @author sd77
7
+ module Ascii85
8
+ extend ModuleMethods
9
+
10
+ # ASCII value for character +!+
11
+ BASE_ASCII_VALUE = "!".ord
12
+ # Ascii85 defines +z+ as a special character
13
+ SPECIAL_Z = true
14
+
15
+ # ASCII85 start marker
16
+ START_MARKER = "<~"
17
+ # ASCII85 end marker
18
+ END_MARKER = "~>"
19
+
20
+ # Encode a value (0..84 integer) to a Ascii85 character
21
+ # @param [Integer] value
22
+ # @return [String]
23
+ def self.encode_value(value)
24
+ (BASE_ASCII_VALUE + value).chr
25
+ end
26
+
27
+ # Encode data using Ascii85 encoding
28
+ # @param [String] data
29
+ # @return [String] encoded data, surrounded by +<~+ and +~>+ markers
30
+ def self.encode(data)
31
+ "#{START_MARKER}#{super}#{END_MARKER}"
32
+ end
33
+
34
+ # Decode a character from Ascii85 and give its index in alphabet
35
+ # @param [String] char a acharacter from Ascii85 alphabet
36
+ # @return [Integer] index of this character in alphabet
37
+ # @raise DecodeError +char+ is not known in alphabet
38
+ def self.decode_char(char)
39
+ case char
40
+ when "!".."u" # rubocop:disable Lint/MixedCaseRange
41
+ char.ord - BASE_ASCII_VALUE
42
+ else
43
+ raise DecodeError, "unknown character '#{char}'"
44
+ end
45
+ end
46
+
47
+ # Decode data using Ascii85 encoding
48
+ # @param [String] data Encoded string, may be prefixed by +<~+ and suffixed by +~>+
49
+ # @return [String] Decoded data
50
+ # @raise [DecodeError] unknown character or invalid tuple
51
+ def self.decode(data)
52
+ start = data.start_with?(START_MARKER) ? 2 : 0
53
+ stop = data.end_with?(END_MARKER) ? -3 : -1
54
+ super(data[start..stop])
55
+ end
56
+ end
57
+ end
data/lib/base85/cli.rb ADDED
@@ -0,0 +1,196 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+ require_relative "../base85"
5
+
6
+ module Base85
7
+ # Base85 comman line interface
8
+ # @author sd77
9
+ class CLI
10
+ # Default wrap column count
11
+ DEFAULT_WRAP = 76
12
+ # @private
13
+ # Max size to read from input at once
14
+ READ_MAX_SIZE = 4096
15
+ # Known alphabet codes for --alphabet option
16
+ ALPHABETS = Base85::SUPPORTED_ALPHABETS.map(&:to_s).freeze
17
+
18
+ # Decode (+true+) or encode (+false+)
19
+ # @return [Boolean]
20
+ attr_accessor :decode
21
+ # Alpahbet to use
22
+ # @param alphabet [:standard, :ascii85 :z85 :rfc1924]
23
+ attr_writer :alphabet
24
+ # column count after which output is wrapped
25
+ # @return [Integer]
26
+ attr_accessor :wrap
27
+
28
+ # Create a CLI object and parse arguments
29
+ # @param [Array[String]] argv
30
+ # @return [CLI]
31
+ def self.start(argv)
32
+ new.start(argv)
33
+ end
34
+
35
+ # @return [self]
36
+ def initialize
37
+ @decode = false
38
+ @alphabet = :standard
39
+ @wrap = DEFAULT_WRAP
40
+ end
41
+
42
+ # Alpahbet to use
43
+ # @return [:standard, :ascii85 :z85 :rfc1924]
44
+ def alphabet
45
+ @alphabet.to_sym
46
+ end
47
+
48
+ # Parse arguments
49
+ # @param [Array[String]] argv
50
+ # @return [self]
51
+ def start(argv)
52
+ parser = create_option_parser
53
+ parser.parse!(argv)
54
+
55
+ abort("---wrap may only be used when encoding") if decode && (wrap != DEFAULT_WRAP)
56
+
57
+ if argv.empty?
58
+ process($stdin)
59
+ elsif argv.size == 1
60
+ File.open(argv.shift) do |file|
61
+ process(file)
62
+ end
63
+ else
64
+ parser.abort("Too much operands")
65
+ end
66
+
67
+ self
68
+ end
69
+
70
+ # Process +input+
71
+ # @param [IO] input
72
+ # @return [void]
73
+ def process(input)
74
+ if decode
75
+ decode_input(input)
76
+ else
77
+ encode_input(input)
78
+ end
79
+ end
80
+
81
+ # Encode +input+ and print it to stdout
82
+ # @param [IO] input
83
+ # @return [void]
84
+ def encode_input(input)
85
+ rest = +""
86
+ current_col = handle_ascii85_start
87
+ until input.eof?
88
+ data = rest << input.read(READ_MAX_SIZE)
89
+ mod = data.size % 4
90
+ rest = mod.zero? ? +"" : data.slice!(-mod..)
91
+ current_col = print_wrapped(encode_data(data), current_col)
92
+ end
93
+
94
+ encoded_rest = handle_ascii85_end(encode_data(rest))
95
+ print_wrapped(encoded_rest, current_col)
96
+ end
97
+
98
+ # @param [String] data
99
+ # @return [String] encoded data
100
+ def encode_data(data)
101
+ encoded = Base85.encode(data, alphabet: alphabet)
102
+ ascii85_remove_markers(encoded)
103
+ end
104
+
105
+ # Decode +input+ and print it to stdout
106
+ # @param [IO] input
107
+ # @return [void]
108
+ def decode_input(input)
109
+ rest = +""
110
+ until input.eof?
111
+ data = rest << input.read(READ_MAX_SIZE)
112
+ ascii85_remove_markers(data)
113
+ mod = data.size % 5
114
+ rest = mod.zero? ? +"" : data.slice!(-mod..)
115
+ print decode_data(data)
116
+ end
117
+ print decode_data(rest)
118
+ end
119
+
120
+ # @param [String] data
121
+ # @return [String] decoded data
122
+ def decode_data(data)
123
+ Base85.decode(data, alphabet: alphabet)
124
+ end
125
+
126
+ private
127
+
128
+ def create_option_parser
129
+ OptionParser.new do |parser|
130
+ prepare_banner(parser)
131
+ parser.on("-d", "--decode", "Decode data.") { self.decode = true }
132
+ parser.on("-a", "--alphabet ALPHABET", ALPHABETS,
133
+ "Alphabet to use: #{ALPHABETS.join(", ")}\n",
134
+ "(default standard).") { |alphabet| self.alphabet = alphabet }
135
+ parser.on("-w", "--wrap COLS", Integer,
136
+ "Wrap encoded lines after WRAP characters (default #{DEFAULT_WRAP}).\n",
137
+ "Use 0 to disable wrapping.") { |cols| self.wrap = cols }
138
+ parser.on("--version", "Output version information and exit.") do
139
+ show_version
140
+ exit
141
+ end
142
+ end
143
+ end
144
+
145
+ def prepare_banner(parser)
146
+ parser.banner = "Usage: #{$PROGRAM_NAME} [options] [FILE]\n" \
147
+ "Encode or decode FILE or standard input to standard output."
148
+ parser.separator ""
149
+ parser.separator "With no FILE, or when FILE is -, read standard input."
150
+ parser.separator ""
151
+ parser.separator "Options:"
152
+ end
153
+
154
+ def show_version
155
+ puts "base85 #{Base85::VERSION}"
156
+ end
157
+
158
+ def ascii85?
159
+ alphabet == :ascii85
160
+ end
161
+
162
+ def ascii85_remove_markers(data)
163
+ return data unless ascii85?
164
+
165
+ data.delete_prefix!("<~")
166
+ data.delete_suffix!("~>")
167
+ data
168
+ end
169
+
170
+ def handle_ascii85_start
171
+ if ascii85?
172
+ print Ascii85::START_MARKER
173
+ Ascii85::START_MARKER.size
174
+ else
175
+ 0
176
+ end
177
+ end
178
+
179
+ def handle_ascii85_end(data)
180
+ data << Ascii85::END_MARKER if ascii85?
181
+ data
182
+ end
183
+
184
+ def print_wrapped(data, current_col)
185
+ if wrap.zero?
186
+ print data
187
+ return 0
188
+ end
189
+
190
+ puts data.slice!(0, wrap - current_col) if (data.size + current_col) > wrap
191
+ puts data.slice!(0, wrap) while data.size > wrap
192
+ print data
193
+ data.size
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,86 @@
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
+ return "z" if const_defined?(:SPECIAL_Z) && word.zero? && (byte_count == 4)
16
+
17
+ encoded = "~" * 5
18
+ 5.times do |i|
19
+ word, r = word.divmod(85)
20
+ encoded[-i - 1] = encode_value(r)
21
+ end
22
+ encoded[..byte_count]
23
+ end
24
+
25
+ # Encode data in base85
26
+ # @param [String] data data to encode
27
+ # @return [String] base85 encoded string
28
+ def encode(data)
29
+ result = +""
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)
39
+ end
40
+
41
+ # Decode data from base85
42
+ # @param [String] data base85 encoded data
43
+ # @return [String] decoded data
44
+ # @raise [DecodeError] unknown character or invalid tuple
45
+ def decode(data)
46
+ result = +""
47
+ word = 0
48
+ count = 0
49
+ data.each_char do |c|
50
+ if c == "z" && const_defined?(:SPECIAL_Z)
51
+ raise DecodeError, "'z' cannot be in the middle of a tuple" if count.positive?
52
+
53
+ result << "\0\0\0\0"
54
+ next
55
+ end
56
+
57
+ idx = decode_char(c)
58
+
59
+ word += idx * LUT[count]
60
+ count += 1
61
+
62
+ if (count == 5) && (word > 0xffffffff)
63
+ raise DecodeError, "invalid tuple"
64
+ elsif count == 5
65
+ wordbuf = " " * 4
66
+ 3.downto(0) do |i|
67
+ wordbuf.setbyte(i, word & 0xff)
68
+ word >>= 8
69
+ end
70
+ result << wordbuf
71
+ count = 0
72
+ word = 0
73
+ end
74
+ end
75
+
76
+ return result if count.zero?
77
+
78
+ count -= 1
79
+ word += LUT[count]
80
+ result << ((word >> 24) & 0xff).chr if count >= 1
81
+ result << ((word >> 16) & 0xff).chr if count >= 2
82
+ result << ((word >> 8) & 0xff).chr if count == 3
83
+ result
84
+ end
85
+ end
86
+ 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
@@ -1,84 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Base85
4
- # Standard base85 encoding. This encoding uses '!' to 'u' ASCII characters to encode data.
4
+ # Standard base85 encoding. This encoding uses +!+ to +u+ ASCII characters to encode data.
5
5
  # @author sd77
6
6
  module Standard
7
- # ASCII value for character '!'
8
- BASE_ASCII_VALUE = "!".ord
9
- # @private
10
- LUT = (0..4).map { |count| 85**(4 - count) }.freeze
7
+ extend ModuleMethods
11
8
 
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
9
+ # ASCII value for character +!+
10
+ BASE_ASCII_VALUE = "!".ord
24
11
 
25
- # Encode data using standard base85 encoding
26
- # @param [String] data
12
+ # Encode a value (0..84 integer) to a standard base85 character
13
+ # @param [Integer] value
27
14
  # @return [String]
28
- def self.encode(data)
29
- result = +""
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 data using standard base85 encoding
42
- # @param [String] data
43
- # @return [String]
44
- # @raise [DecodeError] unknown character or invalid tuple
45
- def self.decode(data)
46
- result = +""
47
- word = 0
48
- count = 0
49
- data.each_char do |c|
50
- case c
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 character 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
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Base85
4
- VERSION = "0.1.0"
4
+ # Base85 gem version
5
+ VERSION = "0.3.0"
5
6
  end
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,11 @@
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"
8
+ require_relative "base85/ascii85"
5
9
 
6
10
  # Provide methods to encode/decode Base85 formatted data
7
11
  # @author sd77
@@ -14,32 +18,54 @@ module Base85
14
18
 
15
19
  # Unknown alphabet exception
16
20
  class UnknownAlphabetError < Error
21
+ # @param [Symbol] name alphabet name
17
22
  def initialize(name)
18
23
  super
19
24
  @name = name
20
25
  end
21
26
 
27
+ # @return [String]
22
28
  def message
23
29
  "Uknown alphabet '#{@name}'"
24
30
  end
25
31
  end
26
32
 
27
33
  # Supported Base85 alphabets
28
- SUPPORTED_ALPHABETS = %i[standard z85 rfc1924].freeze
34
+ SUPPORTED_ALPHABETS = %i[standard ascii85 z85 rfc1924].freeze
29
35
 
36
+ # Encode +data+ using given +alphabet+
37
+ # @param [String] data data to encode
38
+ # @param [:standard,:ascii85,:z85,:rfc1924] alphabet alphabet to use to encode
39
+ # @return [String] encoded string
30
40
  def self.encode(data, alphabet: :standard)
31
41
  case alphabet
32
42
  when :standard
33
43
  Standard.encode(data)
44
+ when :ascii85
45
+ Ascii85.encode(data)
46
+ when :z85
47
+ Z85.encode(data)
48
+ when :rfc1924
49
+ Rfc1924.encode(data)
34
50
  else
35
51
  raise UnknownAlphabetError.new(alphabet)
36
52
  end
37
53
  end
38
54
 
55
+ # Decode +data+ using given +alphabet+
56
+ # @param [String] data data to decode
57
+ # @param [:standard,:ascii85,:z85,:rfc1924] alphabet alphabet to use to decode
58
+ # @return [String] decoded string
39
59
  def self.decode(data, alphabet: :standard)
40
60
  case alphabet
41
61
  when :standard
42
62
  Standard.decode(data)
63
+ when :ascii85
64
+ Ascii85.decode(data)
65
+ when :z85
66
+ Z85.decode(data)
67
+ when :rfc1924
68
+ Rfc1924.decode(data)
43
69
  else
44
70
  raise UnknownAlphabetError.new(alphabet)
45
71
  end
metadata CHANGED
@@ -1,20 +1,22 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: base85
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.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-14 00:00:00.000000000 Z
11
+ date: 2025-06-21 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: Encode and decode base85 data. Multiple alphabets are supported (standard,
14
- Z85 or rfc1924)
13
+ description: |
14
+ Encode and decode base85 data. Multiple alphabets are supported (standard,
15
+ Ascii85, Z85 or RFC 1924).
15
16
  email:
16
17
  - sd@ld77.eu
17
- executables: []
18
+ executables:
19
+ - base85
18
20
  extensions: []
19
21
  extra_rdoc_files: []
20
22
  files:
@@ -24,9 +26,15 @@ files:
24
26
  - LICENSE.txt
25
27
  - README.md
26
28
  - Rakefile
29
+ - exe/base85
27
30
  - lib/base85.rb
31
+ - lib/base85/ascii85.rb
32
+ - lib/base85/cli.rb
33
+ - lib/base85/module_methods.rb
34
+ - lib/base85/rfc1924.rb
28
35
  - lib/base85/standard.rb
29
36
  - lib/base85/version.rb
37
+ - lib/base85/z85.rb
30
38
  - sig/base85.rbs
31
39
  homepage: https://codeberg.org/sd77/base85
32
40
  licenses: