Ascii85 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Binary file
@@ -0,0 +1,5 @@
1
+ === 0.9.0 / 2009-02-17
2
+
3
+ * 1 major enhancement
4
+
5
+ * Initial release
@@ -0,0 +1,6 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ lib/ascii85.rb
6
+ spec/ascii85_spec.rb
@@ -0,0 +1,59 @@
1
+ = Ascii85
2
+
3
+ * http://ascii85.rubyforge.org
4
+
5
+ == DESCRIPTION:
6
+
7
+ Ascii85 is a simple gem that provides methods for encoding/decoding Adobe's
8
+ binary-to-text encoding of the same name.
9
+
10
+ See http://www.adobe.com/products/postscript/pdfs/PLRM.pdf page 131 and
11
+ http://en.wikipedia.org/wiki/Ascii85 for more information about the format.
12
+
13
+
14
+ == SYNOPSIS:
15
+
16
+ require 'rubygems'
17
+ require 'ascii85'
18
+
19
+ Ascii85::encode("Ruby")
20
+ => "<~;KZGo~>"
21
+
22
+ Ascii85::decode("<~;KZGo~>")
23
+ => "Ruby"
24
+
25
+ In addition, Ascii85::encode can take a second parameter that specifies the
26
+ length of the returned lines. The default is 80; use +false+ for unlimited.
27
+
28
+ Ascii85::decode expects the input to be enclosed in <~ and ~>. It ignores
29
+ everything outside of these.
30
+
31
+
32
+ == INSTALL:
33
+
34
+ * sudo gem install ascii85
35
+
36
+
37
+ == LICENSE:
38
+
39
+ (The MIT License)
40
+
41
+ Copyright (c) 2009 Johannes Holzfuß
42
+
43
+ Permission is hereby granted, free of charge, to any person obtaining a
44
+ copy of this software and associated documentation files (the "Software"),
45
+ to deal in the Software without restriction, including without limitation the
46
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
47
+ sell copies of the Software, and to permit persons to whom the Software is
48
+ furnished to do so, subject to the following conditions:
49
+
50
+ The above copyright notice and this permission notice shall be included in
51
+ all copies or substantial portions of the Software.
52
+
53
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
54
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
55
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
56
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
57
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
58
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
59
+ THE SOFTWARE.
@@ -0,0 +1,43 @@
1
+
2
+ require 'rubygems'
3
+
4
+ require 'hoe'
5
+ require 'rake'
6
+ require 'rake/clean'
7
+ require 'rake/rdoctask'
8
+ require 'spec/rake/spectask'
9
+
10
+ require 'lib/ascii85.rb'
11
+
12
+
13
+ # rspec
14
+
15
+ desc "Run specs"
16
+ Spec::Rake::SpecTask.new do |t|
17
+ t.spec_opts = ["--color"]
18
+ end
19
+
20
+ desc "Show specdoc"
21
+ Spec::Rake::SpecTask.new('specdoc') do |t|
22
+ t.spec_opts = ["--color", "--format=specdoc"]
23
+ end
24
+
25
+
26
+ # Hoe
27
+
28
+ Hoe.new('Ascii85', Ascii85::VERSION) do |p|
29
+ p.author = "Johannes Holzfuß"
30
+ p.email = "Drangon@gmx.de"
31
+ p.summary = "Ascii85 encoder/decoder"
32
+
33
+ p.description = "Ascii85 provides methods to encode/decode Adobe's binary-to-text encoding of the same name."
34
+
35
+ p.remote_rdoc_dir = ''
36
+
37
+ p.testlib = "spec"
38
+ p.test_globs = "spec/ascii85_spec.rb"
39
+ end
40
+
41
+
42
+ # default task is spec
43
+ task :default => :spec
@@ -0,0 +1,205 @@
1
+ #
2
+ # Ascii85 is an implementation of Adobe's binary-to-text encoding of the same
3
+ # name in pure Ruby.
4
+ #
5
+ # See http://www.adobe.com/products/postscript/pdfs/PLRM.pdf page 131 and
6
+ # http://en.wikipedia.org/wiki/Ascii85 for more information about the format.
7
+ #
8
+ # Author:: Johannes Holzfuß (Drangon@gmx.de)
9
+ # License:: Distributed under the MIT License (see README.txt)
10
+ #
11
+
12
+
13
+ module Ascii85
14
+ # The gem version number
15
+ VERSION = '0.9.0' # :nodoc:
16
+
17
+ #
18
+ # Encodes the given String as Ascii85.
19
+ #
20
+ # If +wrap_lines+ evaluates to +false+, the output will be returned as a
21
+ # single long line. Otherwise #encode formats the output into lines of
22
+ # length +wrap_lines+ (minimum is 2).
23
+ #
24
+ # Ascii85::encode("Ruby")
25
+ # => <~;KZGo~>
26
+ #
27
+ # Ascii85::encode("Supercalifragilisticexpialidocious", 15)
28
+ # => <~;g!%jEarNoBkD
29
+ # BoB5)0rF*),+AU&
30
+ # 0.@;KXgDe!L"F`R
31
+ # ~>
32
+ #
33
+ # Ascii85::encode("Supercalifragilisticexpialidocious", false)
34
+ # => <~;g!%jEarNoBkDBoB5)0rF*),+AU&0.@;KXgDe!L"F`R~>
35
+ #
36
+ #
37
+ def self.encode(str, wrap_lines = 80)
38
+
39
+ return '' if str.to_s.empty?
40
+
41
+ # Compute number of \0s to pad the message with (0..3)
42
+ padding_length = (-str.to_s.length) % 4
43
+
44
+ # Extract big-endian integers
45
+ tuples = (str.to_s + ("\0" * padding_length)).unpack('N*')
46
+
47
+ # Encode
48
+ tuples.map! do |tuple|
49
+ if tuple == 0
50
+ 'z'
51
+ else
52
+ tmp = ""
53
+ 5.times do
54
+ tmp += ((tuple % 85) + 33).chr
55
+ tuple /= 85
56
+ end
57
+ tmp.reverse
58
+ end
59
+ end
60
+
61
+ # We can't use the z-abbreviation if we're going to cut off padding
62
+ if (padding_length > 0) and (tuples.last == 'z')
63
+ tuples[-1] = '!!!!!'
64
+ end
65
+
66
+ # Cut off the padding
67
+ tuples[-1] = tuples[-1][0..(4 - padding_length)]
68
+
69
+ # Add start-marker and join into a String
70
+ result = '<~' + tuples.join
71
+
72
+ # If we don't need to wrap the lines to a certain length, add ~> and return
73
+ if (!wrap_lines)
74
+ return result + '~>'
75
+ end
76
+
77
+ # Otherwise we wrap the lines
78
+
79
+ line_length = [2, wrap_lines.to_i].max
80
+
81
+ wrapped = []
82
+ 0.step(result.length, line_length) do |index|
83
+ wrapped << result.slice(index, line_length)
84
+ end
85
+
86
+ # Add end-marker -- on a new line if necessary
87
+ if (wrapped.last.length + 2) > line_length
88
+ wrapped << '~>'
89
+ else
90
+ wrapped[-1] += '~>'
91
+ end
92
+
93
+ return wrapped.join("\n")
94
+ end
95
+
96
+ #
97
+ # Searches through +str+ and decodes the _first_ Ascii85-String found.
98
+ #
99
+ # #decode expects an Ascii85-encoded String enclosed in <~ and ~>. It will
100
+ # ignore all characters outside these markers.
101
+ #
102
+ # Ascii85::decode("<~;KZGo~>")
103
+ # => "Ruby"
104
+ #
105
+ # Ascii85::decode("Foo<~;KZGo~>Bar<~;KZGo~>Baz")
106
+ # => "Ruby"
107
+ #
108
+ # Ascii85::decode("No markers")
109
+ # => ""
110
+ #
111
+ # #decode will raise Ascii85::DecodingError when malformed input is
112
+ # encountered.
113
+ #
114
+ def self.decode(str)
115
+
116
+ # Find the Ascii85 encoded data between <~ and ~>
117
+ input = str.to_s.match(/<~.*?~>/mn)
118
+
119
+ return '' if input.nil?
120
+
121
+ # Remove the delimiters
122
+ input = input.to_s[2..-3]
123
+
124
+ return '' if input.empty?
125
+
126
+ # Decode
127
+ result = []
128
+
129
+ count = 0
130
+ word = 0
131
+
132
+ input.each_byte do |c|
133
+
134
+ case c.chr
135
+ when /[ \t\r\n\f\0]/
136
+ # Ignore whitespace
137
+ next
138
+
139
+ when 'z'
140
+ if count == 0
141
+ # Expand z to 0-word
142
+ result << 0
143
+ else
144
+ raise(Ascii85::DecodingError, "Found 'z' inside Ascii85 5-tuple")
145
+ end
146
+
147
+ when '!'..'u'
148
+ # Decode 5 characters into a 4-byte word
149
+ word += (c - 33) * 85**(4 - count)
150
+ count += 1
151
+
152
+ if count == 5
153
+
154
+ if word >= 2**32
155
+ raise(Ascii85::DecodingError,
156
+ "Invalid Ascii85 5-tuple (#{word} >= 2**32)")
157
+ end
158
+
159
+ result << word
160
+ word = 0
161
+ count = 0
162
+ end
163
+
164
+ else
165
+ raise(Ascii85::DecodingError,
166
+ "Illegal character inside Ascii85: #{c.chr.dump}")
167
+ end
168
+
169
+ end
170
+
171
+ # Convert result into a String
172
+ result = result.pack('N*')
173
+
174
+ if count > 0
175
+ # Finish last, partially decoded 32-bit-word
176
+
177
+ if count == 1
178
+ raise(Ascii85::DecodingError,
179
+ "Last 5-tuple consists of single character")
180
+ end
181
+
182
+ count -= 1
183
+ word += 85**(4 - count)
184
+
185
+ result += ((word >> 24) & 255).chr if count >= 1
186
+ result += ((word >> 16) & 255).chr if count >= 2
187
+ result += ((word >> 8) & 255).chr if count == 3
188
+ end
189
+
190
+ return result
191
+ end
192
+
193
+ #
194
+ # This error is raised when Ascii85::decode encounters one of the following
195
+ # problems in the input:
196
+ #
197
+ # * An invalid character. Valid characters are '!'..'u' and 'z'.
198
+ # * A 'z' character inside a 5-tuple. 'z's are only valid on their own.
199
+ # * An invalid 5-tuple that decodes to >= 2**32
200
+ # * The last tuple consisting of a single character. Valid tuples always have
201
+ # at least two characters.
202
+ #
203
+ class DecodingError < StandardError; end
204
+
205
+ end
@@ -0,0 +1,152 @@
1
+ # encoding: utf-8
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
+
5
+ require 'ascii85'
6
+
7
+ describe Ascii85 do
8
+
9
+ TEST_CASES = {
10
+
11
+ "" => "",
12
+ " " => "<~+9~>",
13
+
14
+ "\0" * 1 => "<~!!~>",
15
+ "\0" * 2 => "<~!!!~>",
16
+ "\0" * 3 => "<~!!!!~>",
17
+ "\0" * 4 => "<~z~>",
18
+ "\0" * 5 => "<~z!!~>",
19
+ "A\0\0\0\0" => "<~5l^lb!!~>", # No z-abbreviation!
20
+
21
+ "A" => "<~5l~>",
22
+ "AB" => "<~5sb~>",
23
+ "ABC" => "<~5sdp~>",
24
+ "ABCD" => "<~5sdq,~>",
25
+ "ABCDE" => "<~5sdq,70~>",
26
+ "ABCDEF" => "<~5sdq,77I~>",
27
+ "ABCDEFG" => "<~5sdq,77Kc~>",
28
+ "ABCDEFGH" => "<~5sdq,77Kd<~>",
29
+ "ABCDEFGHI" => "<~5sdq,77Kd<8H~>",
30
+ "Ascii85" => "<~6$$OMBfIs~>",
31
+
32
+ 'Antidisestablishmentarianism' => '<~6#LdYA8-*rF*(i"Ch[s(D.RU,@<-\'jDJ=0/~>',
33
+
34
+ # Dōmo arigatō, Mr. Roboto (according to Wikipedia)
35
+ 'どうもありがとうミスターロボット' =>
36
+ "<~j+42iJVN3:K&_E6j+<0KJW/W?W8iG`j+EuaK\"9on^Z0sZj+FJoK:LtSKB%T?~>",
37
+
38
+ [Math::PI].pack('G') => "<~5RAV2<(&;T~>",
39
+ [Math::E].pack('G') => "<~5R\"n0M\\K6,~>"
40
+ }
41
+
42
+ it "#decode should be the inverse of #encode" do
43
+
44
+ # Generate a random string
45
+ test_str = ""
46
+ (1 + rand(255)).times do
47
+ test_str += rand(256).chr
48
+ end
49
+
50
+ encoded = Ascii85::encode(test_str)
51
+ decoded = Ascii85::decode(encoded)
52
+
53
+ decoded.should == test_str
54
+ end
55
+
56
+ describe "#encode" do
57
+
58
+ it "should encode all specified test-cases correctly" do
59
+ TEST_CASES.each_pair do |input, encoded|
60
+ Ascii85::encode(input).should == encoded
61
+ end
62
+ end
63
+
64
+ it "should produce output lines no longer than specified" do
65
+ test_str = '0123456789' * 30
66
+
67
+ #
68
+ # No wrap
69
+ #
70
+ Ascii85::encode(test_str, false).count("\n").should == 0
71
+
72
+ #
73
+ # x characters per line, except for the last one
74
+ #
75
+ x = 2 + rand(255) # < test_str.length
76
+ encoded = Ascii85::encode(test_str, x)
77
+
78
+ # Determine the length of all lines
79
+ count_arr = []
80
+ encoded.each_line do |line|
81
+ count_arr << line.chomp.length
82
+ end
83
+
84
+ # The last line is allowed to be shorter than x, so remove it
85
+ count_arr.pop if count_arr.last <= x
86
+
87
+ # If the end-marker is on a line of its own, the next-to-last line is
88
+ # allowed to be shorter than specified by exactly one character
89
+ count_arr.pop if (encoded[-3].chr =~ /[\r\n]/) and (count_arr.last == x-1)
90
+
91
+ # Remove all line-lengths that are of length x from count_arr
92
+ count_arr.delete_if { |len| len == x }
93
+
94
+ # Now count_arr should be empty
95
+ count_arr.should be_empty
96
+ end
97
+
98
+ it "should not split the end-marker to achieve correct line length" do
99
+ Ascii85::encode("\0" * 4, 4).should == "<~z\n~>"
100
+ end
101
+
102
+ end
103
+
104
+ describe "#decode" do
105
+
106
+ it "should decode all specified test-cases correctly" do
107
+ TEST_CASES.each_pair do |decoded, input|
108
+ Ascii85::decode(input).should == decoded
109
+ end
110
+ end
111
+
112
+ it "should ignore everything before/after the delimiter-pairs" do
113
+ Ascii85::decode("Doesn't contain delimiters").should == ''
114
+ Ascii85::decode("FooBar<~z~>BazQux").should == ("\0" * 4)
115
+ end
116
+
117
+ it "should ignore whitespace" do
118
+ decoded = Ascii85::decode("<~6 #LdYA\r\08\n \n\n- *rF*(i\"Ch[s \t(D.RU,@ <-\'jDJ=0\f/~>")
119
+ decoded.should == 'Antidisestablishmentarianism'
120
+ end
121
+
122
+ describe "Error conditions" do
123
+
124
+ it "should raise DecodingError if it encounters a word >= 2**32" do
125
+ lambda {
126
+ Ascii85::decode('<~s8W-#~>')
127
+ }.should raise_error Ascii85::DecodingError
128
+ end
129
+
130
+ it "should raise DecodingError if it encounters an invalid character" do
131
+ lambda {
132
+ Ascii85::decode('<~!!y!!~>')
133
+ }.should raise_error Ascii85::DecodingError
134
+ end
135
+
136
+ it "should raise DecodingError if the last tuple consists of a single character" do
137
+ lambda {
138
+ Ascii85::decode('<~!~>')
139
+ }.should raise_error Ascii85::DecodingError
140
+ end
141
+
142
+ it "should raise DecodingError if a z is found inside a 5-tuple" do
143
+ lambda {
144
+ Ascii85::decode('<~!!z!!~>')
145
+ }.should raise_error Ascii85::DecodingError
146
+ end
147
+
148
+ end
149
+
150
+ end
151
+
152
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: Ascii85
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ platform: ruby
6
+ authors:
7
+ - "Johannes Holzfu\xC3\x9F"
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIDKjCCAhKgAwIBAgIBADANBgkqhkiG9w0BAQUFADA7MRAwDgYDVQQDDAdfcmFu
14
+ Z29uMRMwEQYKCZImiZPyLGQBGRYDZ214MRIwEAYKCZImiZPyLGQBGRYCZGUwHhcN
15
+ MDkwMjE0MTgxNDM5WhcNMTAwMjE0MTgxNDM5WjA7MRAwDgYDVQQDDAdfcmFuZ29u
16
+ MRMwEQYKCZImiZPyLGQBGRYDZ214MRIwEAYKCZImiZPyLGQBGRYCZGUwggEiMA0G
17
+ CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa7HRW1pPBbx8k9GLOzYBwHNLAOvHc
18
+ kWfq5E99plLhxKobF7xSValm2csTPQqF8549JJUADGrmeSyFx4z241JtDATjFdYI
19
+ 5LaBXqvxR/c6BfPqPOIuZqc6o1VEpMEVsgKqcL0wjmOaNXMDL7vWj2+LyizWqMXJ
20
+ Y2Hj6GlR6kYzoUyOoAPO+FG363YT++tFvOaOmba314dL40r/Spc730gr1utS8pHA
21
+ t8Gl+Z0EC5gmcUvfDT2sVAF8k9qQhGGqOuOM2HDP+ZrJ368UJznVBxp9YhUveGc2
22
+ MPXvntkrN2XLfGjV2Tr418v7OBP+0IgnZVBM8O7q+iMJmuv/yt2M53H/AgMBAAGj
23
+ OTA3MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBT83GfQpOoJZm8B
24
+ gY6lweCTOsu0QTANBgkqhkiG9w0BAQUFAAOCAQEAAT33NPbPZVy3tgPsl1TicU9q
25
+ eBen2WkXfrCO8jP1ivuFQqMHl5b+MpG2LIwEB45QLLnoXmcW+C4ihGdkYG078uor
26
+ ZskHRETlHJ791l6PyGw5j1oFSwbeYwYIFBWcRrq0KdcZO4CZb7rln2S06ZJWQIMg
27
+ 930O6/WuJyRQLr1PaTJcUGoHUC9cd4CE7ARuck3V38vNR4azjAznCa01mgSkp9UQ
28
+ xPVAMuP9qOp6+OFiuL7DDCHgGI52vDFlUSU+hMcsSSDbYSFcilSPlZ3WLTlOhM4d
29
+ 5FGhYN16QZS8VKLApBtxxP9XmwFASMyJLNizTN2q6hCCy/MjoTzHWzodPaWm0Q==
30
+ -----END CERTIFICATE-----
31
+
32
+ date: 2009-02-17 00:00:00 +01:00
33
+ default_executable:
34
+ dependencies:
35
+ - !ruby/object:Gem::Dependency
36
+ name: hoe
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 1.8.3
44
+ version:
45
+ description: Ascii85 provides methods to encode/decode Adobe's binary-to-text encoding of the same name.
46
+ email: Drangon@gmx.de
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - History.txt
53
+ - Manifest.txt
54
+ - README.txt
55
+ files:
56
+ - History.txt
57
+ - Manifest.txt
58
+ - README.txt
59
+ - Rakefile
60
+ - lib/ascii85.rb
61
+ - spec/ascii85_spec.rb
62
+ has_rdoc: true
63
+ homepage: http://ascii85.rubyforge.org
64
+ post_install_message:
65
+ rdoc_options:
66
+ - --main
67
+ - README.txt
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: "0"
75
+ version:
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: "0"
81
+ version:
82
+ requirements: []
83
+
84
+ rubyforge_project: ascii85
85
+ rubygems_version: 1.3.1
86
+ signing_key:
87
+ specification_version: 2
88
+ summary: Ascii85 encoder/decoder
89
+ test_files:
90
+ - spec/ascii85_spec.rb
Binary file