Module: Weak::Map::StrongKeys
- Includes:
- AbstractStrongKeys
- Defined in:
- lib/weak/map/strong_keys.rb
Overview
This Weak::Map strategy targets JRuby >= 9.4.6.0 and TruffleRuby >= 22. Older versions require additional indirections implemented in StrongSecondaryKeys:
The ObjectSpace::WeakMap
on JRuby and TruffleRuby has strong keys and
weak values. Thus, only the value object in an ObjectSpace::WeakMap
can
be garbage collected to remove the entry while the key defines a strong
object reference which prevents the key object from being garbage
collected.
As a workaround, we use the element's object_id as a key. Being an
Integer
, the object_id is generally is not garbage collected anyway but
allows to uniquely identity the object.
As we need to store both a key and value object for each key-value pair in
our Weak::Map
, we use two separate ObjectSpace::WeakMap
objects for
storing those. This allows keys and values to be independently garbage
collected. When accessing a logical key in the Weak::Map, we need to
manually check if we have a valid entry for both the stored key and the
associated value.
The ObjectSpace::WeakMap
does not allow to explicitly delete entries. We
emulate this by setting the garbage-collectible value of a deleted entry
to a simple new object. This value will be garbage collected on the next
GC run which will then remove the entry. When accessing elements, we
delete and filter out these recently deleted entries.
Class Method Summary collapse
-
.usable? ⇒ Bool
Checks if this strategy is usable for the current Ruby version.
Instance Method Summary collapse
-
#[](key) ⇒ Object
The value associated with the given
key
, if found. -
#[]=(key, value) ⇒ Object
Associates the given
value
with the givenkey
; returnsvalue
. -
#clear ⇒ self
Removes all elements and returns
self
. -
#delete(key) {|key| ... } ⇒ Object?
Deletes the key-value pair and returns the value from
self
whose key is equal tokey
. -
#each_key {|key| ... } ⇒ self, Enumerator
Calls the given block once for each live key in
self
, passing the key as a parameter. -
#each_pair {|key, value| ... } ⇒ self, Enumerator
Calls the given block once for each live key in
self
, passing the key and value as parameters. -
#each_value {|value| ... } ⇒ self, Enumerator
Calls the given block once for each live key
self
, passing the live value associated with the key as a parameter. -
#fetch(key, default = UNDEFINED) {|key| ... } ⇒ Object
Returns a value from the hash for the given
key
. -
#include?(key) ⇒ Bool
true
if the given key is included inself
and has an associated live value,false
otherwise. -
#prune ⇒ self
Cleanup data structures from the map to remove data associated with deleted or garbage collected keys and/or values.
Methods included from AbstractStrongKeys
Class Method Details
.usable? ⇒ Bool
Checks if this strategy is usable for the current Ruby version.
51 52 53 54 55 56 57 58 |
# File 'lib/weak/map/strong_keys.rb', line 51 def self.usable? case RUBY_ENGINE when "ruby", "truffleruby" true when "jruby" Gem::Version.new(RUBY_ENGINE_VERSION) >= Gem::Version.new("9.4.6.0") end end |
Instance Method Details
#[](key) ⇒ Object
Set does not test member equality with ==
or eql?
.
Instead, it always checks strict object equality, so that, e.g.,
different strings are not considered equal, even if they may contain
the same string content.
Returns the value associated with the given key
, if found. If
key
is not found, returns the default value, i.e. the value returned
by the default proc (if defined) or the default
value (which is
initially nil
.).
61 62 63 |
# File 'lib/weak/map/strong_keys.rb', line 61 def [](key) _get(key.__id__) { _default(key) } end |
#[]=(key, value) ⇒ Object
Set does not test member equality with ==
or eql?
.
Instead, it always checks strict object equality, so that, e.g.,
different strings are not considered equal, even if they may contain
the same string content.
Associates the given value
with the given key
; returns value
. If
the given key
exists, replaces its value with the given value
.
66 67 68 69 70 71 72 |
# File 'lib/weak/map/strong_keys.rb', line 66 def []=(key, value) id = key.__id__ @keys[id] = key.nil? ? NIL : key @values[id] = value.nil? ? NIL : value value end |
#clear ⇒ self
Removes all elements and returns self
75 76 77 78 79 |
# File 'lib/weak/map/strong_keys.rb', line 75 def clear @keys = ObjectSpace::WeakMap.new @values = ObjectSpace::WeakMap.new self end |
#delete(key) {|key| ... } ⇒ Object?
Set does not test member equality with ==
or eql?
.
Instead, it always checks strict object equality, so that, e.g.,
different strings are not considered equal, even if they may contain
the same string content.
Deletes the key-value pair and returns the value from self
whose key
is equal to key
. If the key is not found, it returns nil
. If the
optional block is given and the key is not found, pass in the key and
return the result of the block.
82 83 84 |
# File 'lib/weak/map/strong_keys.rb', line 82 def delete(key) _delete(key.__id__) { yield(key) if block_given? } end |
#each_key {|key| ... } ⇒ self, Enumerator
Calls the given block once for each live key in self
, passing the key
as a parameter. Returns the weak map itself.
If no block is given, an Enumerator
is returned instead.
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/weak/map/strong_keys.rb', line 87 def each_key return enum_for(__method__) { size } unless block_given? @keys.values.each do |raw_key| next if DeletedEntry === raw_key key = value!(raw_key) id = key.__id__ if missing?(@values[id]) @keys[id] = DeletedEntry.new else yield key end end self end |
#each_pair {|key, value| ... } ⇒ self, Enumerator
Calls the given block once for each live key in self
, passing the key
and value as parameters. Returns the weak map itself.
If no block is given, an Enumerator
is returned instead.
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/weak/map/strong_keys.rb', line 106 def each_pair return enum_for(__method__) { size } unless block_given? @keys.values.each do |raw_key| next if DeletedEntry === raw_key key = value!(raw_key) id = key.__id__ raw_value = @values[id] if missing?(raw_value) @keys[id] = DeletedEntry.new else yield [key, value!(raw_value)] end end self end |
#each_value {|value| ... } ⇒ self, Enumerator
Calls the given block once for each live key self
, passing the live
value associated with the key as a parameter. Returns the weak map
itself.
If no block is given, an Enumerator
is returned instead.
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/weak/map/strong_keys.rb', line 127 def each_value return enum_for(__method__) { size } unless block_given? @keys.values.each do |raw_key| next if DeletedEntry === raw_key key = value!(raw_key) id = key.__id__ raw_value = @values[id] if missing?(raw_value) @keys[id] = DeletedEntry.new else yield value!(raw_value) end end self end |
#fetch(key, default = UNDEFINED) {|key| ... } ⇒ Object
Set does not test member equality with ==
or eql?
.
Instead, it always checks strict object equality, so that, e.g.,
different strings are not considered equal, even if they may contain
the same string content.
Returns a value from the hash for the given key
. If the key can't be
found, there are several options: With no other arguments, it will raise
a KeyError
exception; if default
is given, then that value will be
returned; if the optional code block is specified, then it will be
called and its result returned.
148 149 150 |
# File 'lib/weak/map/strong_keys.rb', line 148 def fetch(key, default = UNDEFINED, &block) _get(key.__id__) { _fetch_default(key, default, &block) } end |
#include?(key) ⇒ Bool
Set does not test member equality with ==
or eql?
.
Instead, it always checks strict object equality, so that, e.g.,
different strings are not considered equal, even if they may contain
the same string content.
Returns true
if the given key is included in self
and has an
associated live value, false
otherwise.
153 154 155 156 |
# File 'lib/weak/map/strong_keys.rb', line 153 def include?(key) _get(key.__id__) { return false } true end |
#prune ⇒ self
Cleanup data structures from the map to remove data associated with deleted or garbage collected keys and/or values. This method may be called automatically for some Weak::Map operations.
159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/weak/map/strong_keys.rb', line 159 def prune value_keys = ::Set.new(@values.keys) @keys.keys.each do |id| next if value_keys.delete?(id) @keys[id] = DeletedEntry.new end value_keys.each do |id| @values[id] = DeletedEntry.new end self end |