class Sumac::IDAllocator

Public Class Methods

new() click to toggle source
# File lib/sumac/id_allocator.rb, line 8
def initialize
  @allocated_ranges = []
  @mutex = Mutex.new
end
valid?(id) click to toggle source
# File lib/sumac/id_allocator.rb, line 4
def self.valid?(id)
  id.is_a?(Integer) && id >= 0
end

Public Instance Methods

allocate() { |id| ... } click to toggle source
# File lib/sumac/id_allocator.rb, line 17
def allocate
  @mutex.lock
  if @allocated_ranges.empty?
    id = 0
  elsif @allocated_ranges.first.first == 0
    id = @allocated_ranges.first.last.succ
  else
    id = 0
  end
  
  preceding_range = @allocated_ranges.take_while{ |range| range.last < id }.last
  preceding_range_index = @allocated_ranges.find_index(preceding_range) if preceding_range
  
  following_range_index = @allocated_ranges.find_index { |range| range.first > id }
  following_range = @allocated_ranges[following_range_index] if following_range_index
  
  immediately_preceding_range = preceding_range if preceding_range && preceding_range.last.succ == id
  immediately_following_range = following_range if following_range && following_range.first.pred == id
  
  if immediately_preceding_range && immediately_following_range
    @allocated_ranges[preceding_range_index] = (preceding_range.first..following_range.last)
    @allocated_ranges.delete(following_range)
  elsif immediately_preceding_range
    @allocated_ranges[preceding_range_index] = (preceding_range.first..id)
  elsif immediately_following_range
    @allocated_ranges[following_range_index] = (id..following_range.last)
  else
    new_index = preceding_range ? preceding_range_index.succ : 0
    @allocated_ranges.insert(new_index, (id..id))
  end
  
  @mutex.unlock
  
  if block_given?
    begin
      yield(id)
    ensure
      free(id)
    end
  else
    id
  end
end
allocated?(id) click to toggle source
# File lib/sumac/id_allocator.rb, line 83
def allocated?(id)
  enclosing_range(id)
end
free(id) click to toggle source
# File lib/sumac/id_allocator.rb, line 61
def free(id)
  @mutex.lock
  raise unless valid?(id) && allocated?(id)
  
  enclosing_range = enclosing_range(id)
  enclosing_range_index = @allocated_ranges.find_index(enclosing_range)
  
  if enclosing_range.size == 1
    @allocated_ranges.delete(enclosing_range)
  elsif enclosing_range.first == id
    @allocated_ranges[enclosing_range_index] = (enclosing_range.first.succ..enclosing_range.last)
  elsif enclosing_range.last == id
    @allocated_ranges[enclosing_range_index] = (enclosing_range.first..enclosing_range.last.pred)
  else
    @allocated_ranges[enclosing_range_index] = (enclosing_range.first..id.pred)
    @allocated_ranges.insert(enclosing_range_index.succ, (id.succ..enclosing_range.last))
  end
  
  @mutex.unlock
  nil
end
valid?(id) click to toggle source
# File lib/sumac/id_allocator.rb, line 13
def valid?(id)
  self.class.valid?(id)
end

Private Instance Methods

enclosing_range(id) click to toggle source
# File lib/sumac/id_allocator.rb, line 93
def enclosing_range(id)
  possible_range = @allocated_ranges.find{ |range| range.last >= id }
  return possible_range if possible_range && possible_range.first <= id
end
free?(id) click to toggle source
# File lib/sumac/id_allocator.rb, line 89
def free?(id)
  !allocated?(id)
end