class Arachni::Support::Database::Hash

Flat-file Hash implementation

Behaves pretty much like a Ruby Hash however it transparently serializes and saves its values to the file-system under the OS’s temp directory.

It’s not interchangeable with Ruby’s Hash as it lacks a lot of the stdlib methods.

@author Tasos “Zapotek” Laskos <tasos.laskos@arachni-scanner.com> @version 0.1

Public Class Methods

new( *args ) click to toggle source

@see Arachni::Database::Base#initialize

Calls superclass method Arachni::Support::Database::Base::new
# File lib/arachni/support/database/hash.rb, line 27
def initialize( *args )
    super( *args )

    # holds the internal representation of the Hash
    # same keys as self but the values are actually pointing to filepaths
    # where the real values are being stores
    @h = ::Hash.new

    # holds a key-value pair of self with digests as values
    # in order to allow comparisons without requiring to load
    # the actual values from their files.
    @eql_h = ::Hash.new
end

Public Instance Methods

==( h ) click to toggle source

@note If the given hash is not of the same type as self it will be coerced

to a Ruby Hash by calling 'to_hash' on it.

@return [Bool]

`true` if self and the given hash contain the same key-pair values.
# File lib/arachni/support/database/hash.rb, line 272
def ==( h )
    if !h.is_a?( self.class )
        eql = {}
        h.to_hash.each { |k, v| eql[k] = eql_hash( serialize( v ) ) }
        @eql_h == eql
    else
        @eql_h == h._eql_h
    end
end
Also aliased as: eql?
[]( k ) click to toggle source

@param [Obj] k key

@return [Object]

Object corresponding to the key object, `nil` otherwise.
# File lib/arachni/support/database/hash.rb, line 61
def []( k )
    load( @h[k] ) if @h[k]
end
[]=( k, v ) click to toggle source

Associates the given value with the given key.

@param [Object] k

Key.

@param [Object] v

Value.

@return [Object]

`v`.
# File lib/arachni/support/database/hash.rb, line 50
def []=( k, v )
    @h[k] = dump( v ) do |serialized|
        @eql_h[k] = eql_hash( serialized )
    end
end
Also aliased as: store
_eql_h() click to toggle source
# File lib/arachni/support/database/hash.rb, line 295
def _eql_h
    @eql_h.dup
end
_internal() click to toggle source

It will return a Ruby Hash with the same values as self but with filepaths as values (pointing to the files that store them).

This is used for efficient merging, i.e. without requiring to load the actual values when merging 2 objects.

@return [Hash]

Internal representation of `self`.
# File lib/arachni/support/database/hash.rb, line 291
def _internal
    @h.dup
end
assoc( k ) click to toggle source

@param [Object] k

Key.

@return [Array]

Array containing the given key and its value.
# File lib/arachni/support/database/hash.rb, line 70
def assoc( k )
    return if !@h[k]
    [ k, self[k] ]
end
clear() click to toggle source

Removes all objects.

# File lib/arachni/support/database/hash.rb, line 262
def clear
    @h.values.each { |filepath| delete_file( filepath ) }
    @h.clear
end
delete( k, &block ) click to toggle source

Removes an entry by key and returns its value.

If the key doesn’t exist and a block has been provided it’s passed the key and the method returns the result of that block.

@param [Object] k

Key.

@return [Object]

# File lib/arachni/support/database/hash.rb, line 94
def delete( k, &block )
    if @h[k]
        obj = load_and_delete_file( @h[k] )
        @h.delete( k )
        @eql_h.delete( k )
        return obj
    else
        block.call( k ) if block_given?
    end
end
each( &block ) click to toggle source

Calls block with each key-value pair.

If a block has been given it retuns self. If no block has been given it returns an enumerator.

@param [Proc] block

# File lib/arachni/support/database/hash.rb, line 119
def each( &block )
    if block_given?
        @h.each { |k, v| block.call( [ k, self[k] ] ) }
        self
    else
        enum_for( :each )
    end
end
Also aliased as: each_pair
each_key( &block ) click to toggle source

Calls block with each key.

If a block has been given it returns self. If no block has been given it returns an enumerator.

@param [Proc] block

# File lib/arachni/support/database/hash.rb, line 135
def each_key( &block )
    if block_given?
        @h.each_key( &block )
        self
    else
        enum_for( :each_key )
    end
end
each_pair( &block )
Alias for: each
each_value( &block ) click to toggle source

Calls block with each value.

If a block has been given it returns ‘self`. If no block has been given it returns an enumerator.

@param [Proc] block

# File lib/arachni/support/database/hash.rb, line 150
def each_value( &block )
    if block_given?
        @h.keys.each { |k| block.call( self[k] ) }
        self
    else
        enum_for( :each_value )
    end
end
empty?() click to toggle source

@return [Bool]

`true` if the Hash if empty, `false` otherwise.
# File lib/arachni/support/database/hash.rb, line 257
def empty?
    @h.empty?
end
eql?( h )
Alias for: ==
has_key?( k )
Alias for: include?
include?( k ) click to toggle source

@return [Bool]

`true` if the given key exists in the hash, `false` otherwise.
# File lib/arachni/support/database/hash.rb, line 183
def include?( k )
    @h.include?( k )
end
Also aliased as: member?, key?, has_key?
key( val ) click to toggle source

@param [Object] val

@return [Object] key

key for the given value.
# File lib/arachni/support/database/hash.rb, line 169
def key( val )
    return if !value?( val )
    each { |k, v| return k if val == self[k] }
    nil
end
key?( k )
Alias for: include?
keys() click to toggle source

@return [Array]

Keys.
# File lib/arachni/support/database/hash.rb, line 161
def keys
    @h.keys
end
length()
Alias for: size
member?( k )
Alias for: include?
merge( h ) click to toggle source

Merges the contents of self with the contents of the given hash and returns them in a new object.

@param [Hash] h

@return [Arachni::Database::Hash]

# File lib/arachni/support/database/hash.rb, line 203
def merge( h )
    self.class.new( serializer ).merge!( self ).merge!( h )
end
merge!( h ) click to toggle source

Merges self with the contents of the given hash and returns self.

If the given Hash is of the same type as self then the values will not be loaded during the merge in order to keep memory usage down.

If the given Hash is any other kind of object it will be coerced to a Hash by calling ‘to_hash’ on it and the merging it with self.

@param [Hash] h

# File lib/arachni/support/database/hash.rb, line 216
def merge!( h )
    if !h.is_a?( self.class )
        h.to_hash.each do |k, v|
            delete( k ) if @h.include?( k )
            self[k] = v
        end
    else
        h._internal.each do |k, v|
            delete( k ) if @h.include?( k )
            @h[k] = v
        end
        @eql_h.merge!( h._eql_h )
    end
    self
end
Also aliased as: update
rassoc( v ) click to toggle source

@param [Object] v

Value.

@return [Array]

Array containing the key for the given value and that value.
# File lib/arachni/support/database/hash.rb, line 80
def rassoc( v )
    return if !value?( v )
    [ key( v ), v ]
end
shift() click to toggle source

Removes the first key-value pair from the hash and returns it as a array,

@return [Array]

# File lib/arachni/support/database/hash.rb, line 108
def shift
    k, v = @h.first
    [ k, delete( k ) ]
end
size() click to toggle source

@return [Integer]

Number of objects.
# File lib/arachni/support/database/hash.rb, line 250
def size
    @h.size
end
Also aliased as: length
store( k, v )
Alias for: []=
to_a() click to toggle source

@return [Array]

`self` as a Ruby Array.
# File lib/arachni/support/database/hash.rb, line 244
def to_a
    to_hash.to_a
end
to_h()
Alias for: to_hash
to_hash() click to toggle source

@return [Hash]

`self` as Ruby Hash
# File lib/arachni/support/database/hash.rb, line 235
def to_hash
    h = {}
    each { |k, v| h[k] = v }
    h
end
Also aliased as: to_h
update( h )
Alias for: merge!
value?( v ) click to toggle source

@return [Bool]

`true` if the given value exists in the hash, `false` otherwise.
# File lib/arachni/support/database/hash.rb, line 192
def value?( v )
    each_value { |val| return true if val == v }
    false
end
values() click to toggle source

@return [Array]

Values.
# File lib/arachni/support/database/hash.rb, line 177
def values
    each_value.to_a
end

Private Instance Methods

eql_hash( str ) click to toggle source
# File lib/arachni/support/database/hash.rb, line 301
def eql_hash( str )
    Digest::SHA1.hexdigest( str )
end