Stripe CTF is a yearly programming contest. This year the problems were distributed programming problems.
Level 3 was a distributed search engine. However, when looking at the problem, I thought I had an easy way to handle all the searches on the main node.
Instead of using a more complex algorithm, I put all the words into a string
and then relied on ruby's .index()
to find matches quickly:
#!/usr/bin/env ruby
port = 9090
exit 0 unless ARGV.member?( '--master' )
ARGV.shift while ARGV.size > 0
require 'sinatra'
require 'json'
Thread.abort_on_exception = true
set :port, port
disable :sessions, :logging, :show_exceptions
get '/healthcheck' do
{ success: true }.to_json
end
indexed = false
require 'find'
master = String.new
words = {}
word_locations = {}
get '/index' do
path = params[:path]
Thread.new do
warn "Indexing #{path}"
Find.find( path ) do |file|
next if File.directory? file
relative_path = file[(path.size+1)..-1]
warn "#{words.size} - Adding #{relative_path}, of size #{File.size(file)}"
data = File.readlines( file )
data.each_with_index do |line,number|
line.split( /\W+/ ).each do |word|
next if word == ""
if !words[word]
words[word] = true
master << "#{word}\n"
end
word_locations[word] ||= []
word_locations[word] << relative_path.to_sym
word_locations[word] << number+1
end
end
end
warn "Master size: #{master.size} bytes"
indexed = true
end
"OK"
end
get '/isIndexed' do
{success: indexed}.to_json
end
get '/' do
query = params[:q]
warn "Searching for #{query.inspect}"
res = []
prev = 0
while pos = master.index( query, prev )
start = master.rindex "\n", pos
finish = master.index "\n", pos
word = master[(start+1)...finish]
word_locations[word].each_slice(2) do |pair|
res << pair.join(":")
end
prev = finish
end
{ success: true, results: res.uniq }.to_json
end