Skip to content

Commit

Permalink
Support IO-like objects efficiently
Browse files Browse the repository at this point in the history
Previously if we passed in an IO object which doesn't respond to #seek,
MimeMagic would read the whole IO into memory. And this is exactly the
type of objects that the Shrine file upload library deals with.

However, we can also "seek" by blank-reading that many bytes, and always
rewinding to the beginning, which is what we've implemented in this
change.

This change also doesn't require IOs to respond to #binmode.
  • Loading branch information
janko committed Jul 25, 2016
1 parent 7cd73b3 commit 41af06e
Show file tree
Hide file tree
Showing 2 changed files with 10 additions and 27 deletions.
21 changes: 10 additions & 11 deletions lib/mimemagic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,28 +109,27 @@ def self.child?(child, parent)
end

def self.magic_match(io, method)
if io.respond_to?(:seek) && io.respond_to?(:read)
io.binmode
io.set_encoding(Encoding::BINARY) if io.respond_to?(:set_encoding)
buffer = "".force_encoding(Encoding::BINARY)
MAGIC.send(method) { |type, matches| magic_match_io(io, matches, buffer) }
else
str = io.respond_to?(:read) ? io.read : io.to_s
magic_match(StringIO.new(str), method)
end
return magic_match(StringIO.new(io.to_s), method) unless io.respond_to?(:read)

io.binmode if io.respond_to?(:binmode)
io.set_encoding(Encoding::BINARY) if io.respond_to?(:set_encoding)
buffer = "".force_encoding(Encoding::BINARY)

MAGIC.send(method) { |type, matches| magic_match_io(io, matches, buffer) }
end

def self.magic_match_io(io, matches, buffer)
matches.any? do |offset, value, children|
match =
if Range === offset
io.seek(offset.begin)
io.read(offset.begin, buffer)
x = io.read(offset.end - offset.begin + value.bytesize, buffer)
x && x.include?(value)
else
io.seek(offset)
io.read(offset, buffer)
io.read(value.bytesize, buffer) == value
end
io.rewind
match && (!children || magic_match_io(io, children, buffer))
end
end
Expand Down
16 changes: 0 additions & 16 deletions test/mimemagic_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -113,20 +113,4 @@
MimeMagic.by_magic(StringIO.new 'Y MAGICTEST').should.equal 'application/mimemagic-test'
MimeMagic.by_magic(StringIO.new 'Z MAGICTEST').should.equal nil
end

it 'should handle different file objects' do
MimeMagic.add('application/mimemagic-test', magic: [[0, 'MAGICTEST']])
class ReadableObj
def read
'MAGICTEST'
end
end
MimeMagic.by_magic(ReadableObj.new).should.equal 'application/mimemagic-test'
class StringableObject
def to_s
'MAGICTEST'
end
end
MimeMagic.by_magic(StringableObject.new).should.equal 'application/mimemagic-test'
end
end

0 comments on commit 41af06e

Please # to comment.