Skip to content

Instantly share code, notes, and snippets.

@s-shin
Last active May 10, 2017 07:43
Show Gist options
  • Save s-shin/aaab0740c7d761054dfab188c96e28b6 to your computer and use it in GitHub Desktop.
Save s-shin/aaab0740c7d761054dfab188c96e28b6 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
require 'optparse'
# @param bytes [String] Byte sequence as needle.
# @param io [IO] IO object as haystack.
# @param max [Integer, nil] Stop search when `max` number of `bytes` are found.
# @return [Array<Integer>]
def search(bytes, io, max = nil)
buf_size = bytes.size * 10
buf = ''
buf_tail = ''
results = []
loop do
pos = io.pos
buf = io.read(buf_size)
break if buf.nil? || buf.size < bytes.size
buf = buf_tail + buf
pos -= buf_tail.size
idx = 0
loop do
idx = buf.index(bytes, idx + 1)
break if idx.nil?
results << pos + idx
return results if results.size == max
end
buf_tail = buf.byteslice(-bytes.size, bytes.size)
end
results
end
def optparse!(argv)
r = {
data_io: nil,
key: nil,
max_num: nil
}
key_as_file = false
opt = OptionParser.new
opt.banner = "Usage: #{opt.program_name} [options] <search-key> [<file>]"
opt.on('-f', '--key-as-file') { key_as_file = true }
opt.on('-n', '--max-num=0') { |v| r[:max_num] = v.to_i > 0 ? v.to_i : nil }
opt.parse!(argv)
if argv.size < 1
$stderr.puts 'ERROR: Invalid length of arguments.'
$stderr.puts
$stderr.puts opt.help
exit 1
end
r[:key] = key_as_file ? IO.binread(argv[0]) : argv[0].dup.force_encoding('ASCII-8BIT')
r[:data_io] = argv.size == 2 ? open(argv[1], 'rb') : $stdin
r
end
def main
opts = optparse! ARGV.dup
search(opts[:key], opts[:data_io], opts[:max_num]).each { |idx| puts idx }
end
main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment