Ascii85-1.1.0/0000755000004100000410000000000013765473530012763 5ustar www-datawww-dataAscii85-1.1.0/.travis.yml0000644000004100000410000000112413765473530015072 0ustar www-datawww-datalanguage: ruby rvm: - 1.9 - 2.0 - 2.1 - 2.2 - 2.3 - 2.4 - 2.5 - 2.6 - 2.7 - ruby-head - jruby - truffleruby env: - RUBYOPT="--enable-frozen-string-literal" - RUBYOPT="" matrix: exclude: - rvm: 1.9 env: RUBYOPT="--enable-frozen-string-literal" - rvm: 2.0 env: RUBYOPT="--enable-frozen-string-literal" - rvm: 2.1 env: RUBYOPT="--enable-frozen-string-literal" - rvm: 2.2 env: RUBYOPT="--enable-frozen-string-literal" # bundler did not support this back then - rvm: 2.3 env: RUBYOPT="--enable-frozen-string-literal" Ascii85-1.1.0/README.md0000644000004100000410000000366713765473530014256 0ustar www-datawww-data**Status**: This project is in maintenance mode. I will not develop new features, but I will address Issues and Pull Requests. # Ascii85 [![Build Status](https://travis-ci.org/DataWraith/ascii85gem.svg?branch=master)](https://travis-ci.org/DataWraith/ascii85gem) ## Description Ascii85 is a simple gem that provides methods for encoding/decoding Adobe's binary-to-text encoding of the same name. See http://www.adobe.com/products/postscript/pdfs/PLRM.pdf page 131 and http://en.wikipedia.org/wiki/Ascii85 for more information about the format. ## Installation `sudo gem install Ascii85` ## Usage ``` require 'rubygems' require 'ascii85' Ascii85.encode("Ruby") => "<~;KZGo~>" Ascii85.decode("<~;KZGo~>") => "Ruby" ``` In addition, Ascii85.encode can take a second parameter that specifies the length of the returned lines. The default is 80; use `false` for unlimited. Ascii85.decode expects the input to be enclosed in <~ and ~> — it ignores everything outside of these. The output of Ascii85.decode will have the ASCII-8BIT encoding, so in Ruby 1.9 you may have to use String#force_encoding to correct the encoding. ## Command-line utility This gem includes `ascii85`, a command-line utility modeled after `base64` from the GNU coreutils. It can be used to encode/decode Ascii85 directly from the command-line: ``` Usage: ascii85 [OPTIONS] [FILE] Encodes or decodes FILE or STDIN using Ascii85 and writes to STDOUT. -w, --wrap COLUMN Wrap lines at COLUMN. Default is 80, use 0 for no wrapping -d, --decode Decode the input -h, --help Display this help and exit --version Output version information ``` ## Contributors Thank you for your contribution! * [@aliismayilov](https://github.com/aliismayilov) contributed frozen String handling ## License Ascii85 is distributed under the MIT License. See the accompanying LICENSE file for details. Ascii85-1.1.0/bin/0000755000004100000410000000000013765473530013533 5ustar www-datawww-dataAscii85-1.1.0/bin/ascii850000755000004100000410000000341213765473530014726 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 # # A simple command-line tool to de- and encode Ascii85, modeled after `base64` # from the GNU Coreutils. # require "optparse" require File.join(File.dirname(__FILE__), '..', 'lib', 'ascii85') @options = { :wrap => 80, :decode => false } ARGV.options do |opts| opts.banner = "Usage: #{File.basename($PROGRAM_NAME)} [OPTIONS] [FILE]\n" + "Encodes or decodes FILE or STDIN using Ascii85 and writes to STDOUT." opts.on( "-w", "--wrap COLUMN", Integer, "Wrap lines at COLUMN. Default is 80, use 0 for no wrapping") do |opt| @options[:wrap] = opt.abs @options[:wrap] = false if opt.zero? end opts.on( "-d", "--decode", "Decode the input") do @options[:decode] = true end opts.on( "-h", "--help", "Display this help and exit") do puts opts exit end opts.on( "--version", "Output version information") do |opt| puts "Ascii85 v#{Ascii85::VERSION},\nwritten by Johannes Holzfuß" exit end remaining_args = opts.parse! case remaining_args.size when 0 @options[:file] = '-' when 1 @options[:file] = remaining_args.first else abort "Superfluous operand(s): \"#{remaining_args.join('", "')}\"" end end if @options[:file] == '-' @input = $stdin.binmode.read else unless File.exists?(@options[:file]) abort "File not found: \"#{@options[:file]}\"" end unless File.readable_real?(@options[:file]) abort "File is not readable: \"#{@options[:file]}\"" end File.open(@options[:file], 'rb') do |f| @input = f.read end end if @options[:decode] begin print Ascii85.decode(@input) rescue Ascii85::DecodingError => error abort "Decoding Error: #{error.message.to_s}" end else print Ascii85.encode(@input, @options[:wrap]) end Ascii85-1.1.0/spec/0000755000004100000410000000000013765473530013715 5ustar www-datawww-dataAscii85-1.1.0/spec/lib/0000755000004100000410000000000013765473530014463 5ustar www-datawww-dataAscii85-1.1.0/spec/lib/ascii85_spec.rb0000644000004100000410000001434413765473530017275 0ustar www-datawww-data# encoding: utf-8 require 'rubygems' require 'minitest/autorun' # Require implementation require File.expand_path('../../../lib/ascii85', __FILE__) describe Ascii85 do UNSUPPORTED_MSG = "This version of Ruby does not support encodings" TEST_CASES = { "" => "", " " => "<~+9~>", "\0" * 1 => "<~!!~>", "\0" * 2 => "<~!!!~>", "\0" * 3 => "<~!!!!~>", "\0" * 4 => "<~z~>", "\0" * 5 => "<~z!!~>", "A\0\0\0\0" => "<~5l^lb!!~>", # No z-abbreviation! "A" => "<~5l~>", "AB" => "<~5sb~>", "ABC" => "<~5sdp~>", "ABCD" => "<~5sdq,~>", "ABCDE" => "<~5sdq,70~>", "ABCDEF" => "<~5sdq,77I~>", "ABCDEFG" => "<~5sdq,77Kc~>", "ABCDEFGH" => "<~5sdq,77Kd<~>", "ABCDEFGHI" => "<~5sdq,77Kd<8H~>", "Ascii85" => "<~6$$OMBfIs~>", 'Antidisestablishmentarianism' => '<~6#LdYA8-*rF*(i"Ch[s(D.RU,@<-\'jDJ=0/~>', # Dōmo arigatō, Mr. Roboto (according to Wikipedia) 'どうもありがとうミスターロボット' => "<~j+42iJVN3:K&_E6j+<0KJW/W?W8iG`j+EuaK\"9on^Z0sZj+FJoK:LtSKB%T?~>", [Math::PI].pack('G') => "<~5RAV2<(&;T~>", [Math::E].pack('G') => "<~5R\"n0M\\K6,~>" } it "#decode should be the inverse of #encode" do # Generate a random string test_str = String.new (1 + rand(255)).times do test_str << rand(256).chr end encoded = Ascii85.encode(test_str) decoded = Ascii85.decode(encoded) assert_equal decoded, test_str end describe "#encode" do it "should encode all specified test-cases correctly" do TEST_CASES.each_pair do |input, encoded| assert_equal Ascii85.encode(input), encoded end end it "should encode Strings in different encodings correctly" do unless String.new.respond_to?(:encoding) skip(UNSUPPORTED_MSG) end input_EUC_JP = 'どうもありがとうミスターロボット'.encode('EUC-JP') input_binary = input_EUC_JP.force_encoding('ASCII-8BIT') assert_equal Ascii85.encode(input_EUC_JP), Ascii85.encode(input_binary) end it "should produce output lines no longer than specified" do test_str = '0123456789' * 30 # # No wrap # assert_equal Ascii85.encode(test_str, false).count("\n"), 0 # # x characters per line, except for the last one # x = 2 + rand(255) # < test_str.length encoded = Ascii85.encode(test_str, x) # Determine the length of all lines count_arr = [] encoded.each_line do |line| count_arr << line.chomp.length end # The last line is allowed to be shorter than x, so remove it count_arr.pop if count_arr.last <= x # If the end-marker is on a line of its own, the next-to-last line is # allowed to be shorter than specified by exactly one character count_arr.pop if (encoded[-3].chr =~ /[\r\n]/) and (count_arr.last == x-1) # Remove all line-lengths that are of length x from count_arr count_arr.delete_if { |len| len == x } # Now count_arr should be empty assert_empty count_arr end it "should not split the end-marker to achieve correct line length" do assert_equal Ascii85.encode("\0" * 4, 4), "<~z\n~>" end end describe "#decode" do it "should decode all specified test-cases correctly" do TEST_CASES.each_pair do |decoded, input| if String.new.respond_to?(:encoding) assert_equal Ascii85.decode(input), decoded.dup.force_encoding('ASCII-8BIT') else assert_equal Ascii85.decode(input), decoded end end end it "should accept valid input in encodings other than the default" do unless String.new.respond_to?(:encoding) skip(UNSUPPORTED_MSG) end input = "Ragnarök τέχνη русский язык I ♥ Ruby" input_ascii85 = Ascii85.encode(input) # Try to encode input_ascii85 in all possible encodings and see if we # do the right thing in #decode. Encoding.list.each do |encoding| next if encoding.dummy? next unless encoding.ascii_compatible? # CP949 is a Microsoft Codepage for Korean, which apparently does not # include a backslash, even though #ascii_compatible? returns true. This # leads to an Ascii85::DecodingError, so we simply skip the encoding. next if encoding.name == "CP949" begin to_test = input_ascii85.encode(encoding) assert_equal Ascii85.decode(to_test).force_encoding('UTF-8'), input rescue Encoding::ConverterNotFoundError # Ignore this encoding end end end it "should only process data within delimiters" do assert_empty Ascii85.decode("<~~>") assert_empty Ascii85.decode("Doesn't contain delimiters") assert_empty Ascii85.decode("Mismatched ~> delimiters 1") assert_empty Ascii85.decode("Mismatched <~ delimiters 2") assert_empty Ascii85.decode("Mismatched ~><~ delimiters 3") assert_equal Ascii85.decode("<~;KZGo~><~z~>"), "Ruby" assert_equal Ascii85.decode("FooBar<~z~>BazQux"), "\0\0\0\0" end it "should ignore whitespace" do decoded = Ascii85.decode("<~6 #LdYA\r\08\n \n\n- *rF*(i\"Ch[s \t(D.RU,@ <-\'jDJ=0\f/~>") assert_equal decoded, 'Antidisestablishmentarianism' end it "should return ASCII-8BIT encoded strings" do unless String.new.respond_to?(:encoding) skip(UNSUPPORTED_MSG) end assert_equal Ascii85.decode("<~;KZGo~>").encoding.name, "ASCII-8BIT" end describe "Error conditions" do it "should raise DecodingError if it encounters a word >= 2**32" do assert_raises(Ascii85::DecodingError) { Ascii85.decode('<~s8W-#~>') } end it "should raise DecodingError if it encounters an invalid character" do assert_raises(Ascii85::DecodingError) { Ascii85.decode('<~!!y!!~>') } end it "should raise DecodingError if the last tuple consists of a single character" do assert_raises(Ascii85::DecodingError) { Ascii85.decode('<~!~>') } end it "should raise DecodingError if a z is found inside a 5-tuple" do assert_raises(Ascii85::DecodingError) { Ascii85.decode('<~!!z!!~>') } end end end end Ascii85-1.1.0/LICENSE0000644000004100000410000000204513765473530013771 0ustar www-datawww-dataCopyright (c) 2009 Johannes Holzfuß Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Ascii85-1.1.0/History.txt0000644000004100000410000000113713765473530015167 0ustar www-datawww-data=== 1.1.0 / 2020-11-11 * Make use of frozen_string_literal * Update tests to use newer minitest syntax === 1.0.3 / 2018-01-25 * Updated the gem's metadata === 1.0.2 / 2012-09-16 * Changed test runner from RSpec to MiniSpec * Support for rubygems-test * Minor changes to make packaging easier === 1.0.1 / 2011-05-05 * Removed hoe dependency in favor of bundler * Minor corrections in the documentation === 1.0.0 / 2009-12-25 * 2 major enhancements * Ruby 1.9 compatibility * Added command-line en- and decoder === 0.9.0 / 2009-02-17 * 1 major enhancement * Initial release Ascii85-1.1.0/Rakefile0000644000004100000410000000034013765473530014425 0ustar www-datawww-datarequire 'bundler' Bundler::GemHelper.install_tasks require 'rake/testtask' Rake::TestTask.new do |t| t.test_files = FileList['spec/**/*_spec.rb'] end task :specs => :test task :tests => :test task :default => :test Ascii85-1.1.0/lib/0000755000004100000410000000000013765473530013531 5ustar www-datawww-dataAscii85-1.1.0/lib/ascii85.rb0000644000004100000410000001346013765473530015327 0ustar www-datawww-data# encoding: utf-8 # frozen_string_literal: true # # Ascii85 is an implementation of Adobe's binary-to-text encoding of the # same name in pure Ruby. # # See http://www.adobe.com/products/postscript/pdfs/PLRM.pdf page 131 # and http://en.wikipedia.org/wiki/Ascii85 for more information about # the format. # # Author:: Johannes Holzfuß (johannes@holzfuss.name) # License:: Distributed under the MIT License (see LICENSE file) # module Ascii85 # # Encodes the bytes of the given String as Ascii85. # # If +wrap_lines+ evaluates to +false+, the output will be returned as # a single long line. Otherwise #encode formats the output into lines # of length +wrap_lines+ (minimum is 2). # # Ascii85.encode("Ruby") # => <~;KZGo~> # # Ascii85.encode("Supercalifragilisticexpialidocious", 15) # => <~;g!%jEarNoBkD # BoB5)0rF*),+AU& # 0.@;KXgDe!L"F`R # ~> # # Ascii85.encode("Supercalifragilisticexpialidocious", false) # => <~;g!%jEarNoBkDBoB5)0rF*),+AU&0.@;KXgDe!L"F`R~> # # def self.encode(str, wrap_lines = 80) to_encode = str.to_s return '' if to_encode.empty? # Deal with multi-byte encodings if to_encode.respond_to?(:bytesize) input_size = to_encode.bytesize else input_size = to_encode.size end # Compute number of \0s to pad the message with (0..3) padding_length = (-input_size) % 4 # Extract big-endian integers tuples = (to_encode + ("\0" * padding_length)).unpack('N*') # Encode tuples.map! do |tuple| if tuple == 0 'z' else tmp = String.new 5.times do tmp << ((tuple % 85) + 33).chr tuple /= 85 end tmp.reverse end end # We can't use the z-abbreviation if we're going to cut off padding if (padding_length > 0) and (tuples.last == 'z') tuples[-1] = '!!!!!' end # Cut off the padding tuples[-1] = tuples[-1][0..(4 - padding_length)] # If we don't need to wrap the lines, add delimiters and return if (!wrap_lines) return '<~' + tuples.join + '~>' end # Otherwise we wrap the lines line_length = [2, wrap_lines.to_i].max wrapped = [] to_wrap = '<~' + tuples.join 0.step(to_wrap.length, line_length) do |index| wrapped << to_wrap.slice(index, line_length) end # Add end-marker – on a new line if necessary if (wrapped.last.length + 2) > line_length wrapped << '~>' else wrapped[-1] << '~>' end return wrapped.join("\n") end # # Searches through +str+ and decodes the _first_ Ascii85-String found. # # #decode expects an Ascii85-encoded String enclosed in <~ and ~> — it will # ignore all characters outside these markers. The returned strings are always # encoded as ASCII-8BIT. # # Ascii85.decode("<~;KZGo~>") # => "Ruby" # # Ascii85.decode("Foo<~;KZGo~>Bar<~;KZGo~>Baz") # => "Ruby" # # Ascii85.decode("No markers") # => "" # # #decode will raise Ascii85::DecodingError when malformed input is # encountered. # def self.decode(str) input = str.to_s opening_delim = '<~' closing_delim = '~>' # Make sure the delimiter strings have the correct encoding. # # Although I don't think it likely, this may raise encoding # errors if an especially exotic input encoding is introduced. # As of Ruby 1.9.2 all non-dummy encodings work fine though. # if opening_delim.respond_to?(:encode) opening_delim = opening_delim.encode(input.encoding) closing_delim = closing_delim.encode(input.encoding) end # Get the positions of the opening/closing delimiters. If there is # no pair of opening/closing delimiters, return the empty string. (start_pos = input.index(opening_delim)) or return '' (end_pos = input.index(closing_delim, start_pos + 2)) or return '' # Get the string inside the delimiter-pair input = input[(start_pos + 2)...end_pos] # Decode word = 0 count = 0 result = [] input.each_byte do |c| case c.chr when " ", "\t", "\r", "\n", "\f", "\0" # Ignore whitespace next when 'z' if count == 0 # Expand z to 0-word result << 0 else raise(Ascii85::DecodingError, "Found 'z' inside Ascii85 5-tuple") end when '!'..'u' # Decode 5 characters into a 4-byte word word += (c - 33) * 85**(4 - count) count += 1 if count == 5 if word > 0xffffffff raise(Ascii85::DecodingError, "Invalid Ascii85 5-tuple (#{word} >= 2**32)") end result << word word = 0 count = 0 end else raise(Ascii85::DecodingError, "Illegal character inside Ascii85: #{c.chr.dump}") end end # Convert result into a String result = result.pack('N*') if count > 0 # Finish last, partially decoded 32-bit-word if count == 1 raise(Ascii85::DecodingError, "Last 5-tuple consists of single character") end count -= 1 word += 85**(4 - count) result << ((word >> 24) & 255).chr if count >= 1 result << ((word >> 16) & 255).chr if count >= 2 result << ((word >> 8) & 255).chr if count == 3 end return result end # # This error is raised when Ascii85.decode encounters one of the following # problems in the input: # # * An invalid character. Valid characters are '!'..'u' and 'z'. # * A 'z' character inside a 5-tuple. 'z's are only valid on their own. # * An invalid 5-tuple that decodes to >= 2**32 # * The last tuple consisting of a single character. Valid tuples always have # at least two characters. # class DecodingError < StandardError; end end Ascii85-1.1.0/lib/Ascii85/0000755000004100000410000000000013765473530014736 5ustar www-datawww-dataAscii85-1.1.0/lib/Ascii85/version.rb0000644000004100000410000000010613765473530016745 0ustar www-datawww-data# frozen_string_literal: true module Ascii85 VERSION = "1.1.0" end Ascii85-1.1.0/Gemfile0000644000004100000410000000013313765473530014253 0ustar www-datawww-datasource "http://rubygems.org" # Specify your gem's dependencies in Ascii85.gemspec gemspec Ascii85-1.1.0/Ascii85.gemspec0000644000004100000410000000176613765473530015547 0ustar www-datawww-data# -*- encoding: utf-8 -*- $:.push File.expand_path("../lib", __FILE__) require "Ascii85/version" Gem::Specification.new do |s| s.name = "Ascii85" s.version = Ascii85::VERSION s.platform = Gem::Platform::RUBY s.author = "Johannes Holzfuß" s.email = "johannes@holzfuss.name" s.license = 'MIT' s.homepage = "https://github.com/DataWraith/ascii85gem/" s.summary = %q{Ascii85 encoder/decoder} s.description = %q{Ascii85 provides methods to encode/decode Adobe's binary-to-text encoding of the same name.} s.add_development_dependency "bundler", ">= 1.0.0" s.add_development_dependency "minitest",">= 2.6.0" s.add_development_dependency "rake", ">= 0.9.2" s.files = `git ls-files`.split("\n") - ['.gitignore'] s.test_files = `git ls-files -- spec/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] s.extra_rdoc_files = ['README.md', 'LICENSE'] end