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