On Mon, Mar 20, 2006 at 05:41:56PM +0900, Ross Bamford wrote:
>     # This WeakHash implementation is by Mauricio Fernandez. I
>     # just added support for a default, and full key reclaimation
>     # to suit our need to have the default block run as and when 
>     # (but only when) necessary.
>     # 
>     # See: http://eigenclass.org/hiki.rb?weakhash+and+weakref 

I found your message indirectly via my httpd logs :)

>     class WeakHash #:nodoc: all
[...]
>       def []( key )
>         value_id = @cache[key.hash]
                            ========
This will fail when you have hash collisions; I think it should be 'key'
as in the original code (same goes for []=).

Forcing collisions is not as difficult as one could believe:


#####
  class WeakHash #:nodoc: all
    attr_reader :cache
    def initialize( cache = Hash.new, &initializer )
      @cache = cache
      @initializer = initializer
      @key_map = {}
      @rev_cache = Hash.new{|h,k| h[k] = {}}
      @reclaim_value = lambda do |value_id|
	if @rev_cache.has_key? value_id
	  @rev_cache[value_id].each_key{|key| @cache.delete key}
	  @rev_cache.delete value_id
	end
      end
      @reclaim_key = lambda do |key_id|
	if @key_map.has_key? key_id
	  @cache.delete @key_map[key_id]
	  @key_map.delete key_id
	end
      end
    end

    def []( key )
      value_id = @cache[key.hash]

      if value_id.nil? && @initializer
	self[key] = @initializer.call(self, key)
	value_id = @cache[key.hash]
      end
      
      return ObjectSpace._id2ref(value_id) unless value_id.nil?
      nil
    end

    def []=( key, value )
      case key
      when Fixnum, Symbol, true, false
	key2 = key
      else
	key2 = key.hash
      end
      @rev_cache[value.object_id][key2] = true
      @cache[key2] = value.object_id
      @key_map[key.object_id] = key2

      ObjectSpace.define_finalizer(value, @reclaim_value)
      ObjectSpace.define_finalizer(key, @reclaim_key)
    end
  end

RUBY_VERSION                                       # => "1.8.4"
hash = WeakHash.new
hash[["a"]] = "some value"
hash[["a"]]                                        # => "some value"
hash["c"]                                          # => "some value"


-- 
Mauricio Fernandez  -   http://eigenclass.org   -  singular Ruby