lib/ascii85.rb in Ascii85-2.0.0 vs lib/ascii85.rb in Ascii85-2.0.1
- old
+ new
@@ -12,10 +12,15 @@
# Author:: Johannes HolzfuĆ (johannes@holzfuss.name)
# License:: Distributed under the MIT License (see LICENSE file)
#
module Ascii85
class << self
+ EMPTY_STRING = ''.dup.force_encoding(Encoding::ASCII_8BIT)
+ START_MARKER = '<~'.dup.force_encoding(Encoding::ASCII_8BIT)
+ ENDING_MARKER = '~>'.dup.force_encoding(Encoding::ASCII_8BIT)
+ LINE_BREAK = "\n".dup.force_encoding(Encoding::ASCII_8BIT)
+
#
# Encodes the bytes of the given String or IO-like object as Ascii85.
#
# @param str_or_io [String, IO] The input to encode
# @param wrap_lines [Integer, false] The line length for wrapping, or +false+ for no wrapping
@@ -53,26 +58,28 @@
str_or_io
else
StringIO.new(str_or_io.to_s, 'rb')
end
- return ''.dup if reader.eof?
+ return EMPTY_STRING.dup if reader.eof?
# Setup buffered Reader and Writers
bufreader = BufferedReader.new(reader, unencoded_chunk_size)
bufwriter = BufferedWriter.new(out || StringIO.new(String.new, 'wb'), encoded_chunk_size)
writer = wrap_lines ? Wrapper.new(bufwriter, wrap_lines) : DummyWrapper.new(bufwriter)
- padding = "\0\0\0\0"
- tuplebuf = '!!!!!'.dup
+ padding = unfrozen_binary_copy("\0\0\0\0")
+ tuplebuf = unfrozen_binary_copy('!!!!!')
+ exclamations = unfrozen_binary_copy('!!!!!')
+ z = unfrozen_binary_copy('z')
bufreader.each_chunk do |chunk|
chunk.unpack('N*').each do |word|
# Encode each big-endian 32-bit word into a 5-character tuple (except
# for 0, which encodes to 'z')
if word.zero?
- writer.write('z')
+ writer.write(z)
else
word, b0 = word.divmod(85)
word, b1 = word.divmod(85)
word, b2 = word.divmod(85)
word, b3 = word.divmod(85)
@@ -96,11 +103,11 @@
trailing = chunk[-(4 - padding_length)..]
word = (trailing + padding[0...padding_length]).unpack1('N')
# Encode the last word and cut off any padding
if word.zero?
- writer.write('!!!!!'[0..(4 - padding_length)])
+ writer.write(exclamations[0..(4 - padding_length)])
else
word, b0 = word.divmod(85)
word, b1 = word.divmod(85)
word, b2 = word.divmod(85)
word, b3 = word.divmod(85)
@@ -117,11 +124,11 @@
end
# If no output IO-object was provided, extract the encoded String from the
# default StringIO writer. We force the encoding to 'ASCII-8BIT' to work
# around a TruffleRuby bug.
- return writer.finish.io.string.force_encoding('ASCII-8BIT') if out.nil?
+ return writer.finish.io.string.force_encoding(Encoding::ASCII_8BIT) if out.nil?
# Otherwise we make sure to flush the output writer, and then return it.
writer.finish.io
end
@@ -149,12 +156,12 @@
opening_delim = '<~'.encode(input.encoding)
closing_delim = '~>'.encode(input.encoding)
# Get the positions of the opening/closing delimiters. If there is no pair
# of opening/closing delimiters, return an unfrozen empty String.
- (start_pos = input.index(opening_delim)) or return ''.dup
- (end_pos = input.index(closing_delim, start_pos + 2)) or return ''.dup
+ (start_pos = input.index(opening_delim)) or return EMPTY_STRING.dup
+ (end_pos = input.index(closing_delim, start_pos + 2)) or return EMPTY_STRING.dup
# Get the String inside the delimiter-pair
input[(start_pos + 2)...end_pos]
end
@@ -224,11 +231,11 @@
else
StringIO.new(str_or_io.to_s, 'rb')
end
# Return an unfrozen String on empty input
- return ''.dup if reader.eof?
+ return EMPTY_STRING.dup if reader.eof?
# Setup buffered Reader and Writers
bufreader = BufferedReader.new(reader, encoded_chunk_size)
bufwriter = BufferedWriter.new(out || StringIO.new(String.new, 'wb'), unencoded_chunk_size)
@@ -236,11 +243,12 @@
lut = (0..4).map { |count| 85**(4 - count) }
# Decode
word = 0
count = 0
- wordbuf = "\0\0\0\0".dup
+ zeroes = unfrozen_binary_copy("\0\0\0\0")
+ wordbuf = zeroes.dup
bufreader.each_chunk do |chunk|
chunk.each_byte do |c|
case c.chr
when ' ', "\t", "\r", "\n", "\f", "\0"
@@ -249,11 +257,11 @@
when 'z'
raise(Ascii85::DecodingError, "Found 'z' inside Ascii85 5-tuple") unless count.zero?
# Expand z to 0-word
- bufwriter.write("\0\0\0\0")
+ bufwriter.write(zeroes)
when '!'..'u'
# Decode 5 characters into a 4-byte word
word += (c - 33) * lut[count]
count += 1
@@ -284,11 +292,11 @@
end
# We're done if all 5-tuples have been consumed
if count.zero?
bufwriter.flush
- return out || bufwriter.io.string.force_encoding('ASCII-8BIT')
+ return out || bufwriter.io.string.force_encoding(Encoding::ASCII_8BIT)
end
raise(Ascii85::DecodingError, 'Last 5-tuple consists of single character') if count == 1
# Finish last, partially decoded 32-bit word
@@ -298,15 +306,21 @@
bufwriter.write((word >> 24).chr) if count >= 1
bufwriter.write(((word >> 16) & 0xff).chr) if count >= 2
bufwriter.write(((word >> 8) & 0xff).chr) if count == 3
bufwriter.flush
- out || bufwriter.io.string.force_encoding('ASCII-8BIT')
+ out || bufwriter.io.string.force_encoding(Encoding::ASCII_8BIT)
end
private
+ # Copies the given String and forces the encoding of the returned copy to
+ # be Encoding::ASCII_8BIT.
+ def unfrozen_binary_copy(str)
+ str.dup.force_encoding(Encoding::ASCII_8BIT)
+ end
+
# Buffers an underlying IO object to increase efficiency. You do not need
# to use this directly.
#
# @private
#
@@ -335,11 +349,11 @@
attr_accessor :io
def initialize(io, buffer_size)
@io = io
@buffer_size = buffer_size
- @buffer = String.new(capacity: buffer_size)
+ @buffer = String.new(capacity: buffer_size, encoding: Encoding::ASCII_8BIT)
end
def write(tuple)
flush if @buffer.bytesize + tuple.bytesize > @buffer_size
@buffer << tuple
@@ -358,19 +372,19 @@
# @private
#
class DummyWrapper
def initialize(out)
@out = out
- @out.write('<~')
+ @out.write(START_MARKER)
end
def write(buffer)
@out.write(buffer)
end
def finish
- @out.write('~>')
+ @out.write(ENDING_MARKER)
@out.flush
@out
end
end
@@ -383,11 +397,11 @@
class Wrapper
def initialize(out, wrap_lines)
@line_length = [2, wrap_lines.to_i].max
@out = out
- @out.write('<~')
+ @out.write(START_MARKER)
@cur_len = 2
end
def write(buffer)
@@ -400,20 +414,20 @@
return
end
remaining = @line_length - @cur_len
@out.write(buffer[0...remaining])
- @out.write("\n")
+ @out.write(LINE_BREAK)
@cur_len = 0
buffer = buffer[remaining..]
return if buffer.empty?
end
end
def finish
# Add the closing delimiter (may need to be pushed to the next line)
- @out.write("\n") if @cur_len + 2 > @line_length
- @out.write('~>')
+ @out.write(LINE_BREAK) if @cur_len + 2 > @line_length
+ @out.write(ENDING_MARKER)
@out.flush
@out
end
end