require 'rubygems' require 'digest' require 'securerandom' require "hashtree/version" class HashTree attr_reader :index, :branches, :settings def initialize(settings = {}) @settings = { max_branch_size: 16 }.merge(settings) @index = {} @branches = { free: {} } end # Public API def [](key) begin id = @index[key] if id read(id) else nil end rescue nil end end def []=(key, value) begin id = @index[key] if id update(id, value) else create(key, value) end rescue nil end end def delete(key) begin id = @index[key] if id doc = read(id) destroy(id) @index.delete(key) return doc else nil end rescue nil end end def clear @index = {} @branches = {} end def count @index.count end def branch(key) branch_id = get_branch_id(key) @branches[branch_id] if branch_id end private def get_branch_id(key) id = @index[key] if id branch_id = id[0..15] else nil end end # Private API def read(id) branch_id, leaf_id = id[0..15], id[16..32] @branches[branch_id][leaf_id] end def update(id, value) branch_id, leaf_id = id[0..15], id[16..32] @branches[branch_id][leaf_id] = value return value end def create(key, value) free_branch_id = @branches[:free].keys.sample if free_branch_id leaf_id = Digest::MD5.hexdigest(SecureRandom.uuid)[0..15] @index[key] = free_branch_id + leaf_id branch = @branches[free_branch_id] branch[leaf_id] = value @branches[:free].delete(branch_id) if branch.size >= @settings[:max_branch_size] else id = Digest::MD5.hexdigest(SecureRandom.uuid) @index[key] = id branch_id, leaf_id = id[0..15], id[16..32] @branches[branch_id] = {} @branches[branch_id][leaf_id] = value end return value end def destroy(id) branch_id, leaf_id = id[0..15], id[16..32] branch = @branches[branch_id] destroyed_element = branch.delete(leaf_id) if branch.size == 0 @branches.delete(branch_id) @branches[:free].delete(branch_id) elsif branch.size > 0 and branch.size < 16 @branches[:free][branch_id] = nil end end end