2 #include "internal/gc.h"
3 #include "internal/hash.h"
4 #include "internal/proc.h"
5 #include "internal/sanitizers.h"
10 * WeakMap contains one ST table which contains a pointer to the object as the
11 * key and a pointer to the object as the value. This means that the key and
12 * value of the table are both of the type `VALUE *`.
14 * The objects are not directly stored as keys and values in the table because
15 * `rb_gc_mark_weak` requires a pointer to the memory location to overwrite
16 * when the object is reclaimed. Using a pointer into the ST table entry is not
17 * safe because the pointer can change when the ST table is resized.
19 * WeakMap hashes and compares using the pointer address of the object.
21 * For performance and memory efficiency reasons, the key and value
22 * are allocated at the same time and adjacent to each other.
24 * During GC and while iterating, reclaimed entries (i.e. either the key or
25 * value points to `Qundef`) are removed from the ST table.
32 struct weakmap_entry
{
38 wmap_live_p(VALUE obj
)
43 struct wmap_foreach_data
{
44 int (*func
)(struct weakmap_entry
*, st_data_t
);
47 struct weakmap_entry
*dead_entry
;
51 wmap_foreach_i(st_data_t key
, st_data_t val
, st_data_t arg
)
53 struct wmap_foreach_data
*data
= (struct wmap_foreach_data
*)arg
;
55 if (data
->dead_entry
!= NULL
) {
56 ruby_sized_xfree(data
->dead_entry
, sizeof(struct weakmap_entry
));
57 data
->dead_entry
= NULL
;
60 struct weakmap_entry
*entry
= (struct weakmap_entry
*)key
;
61 RUBY_ASSERT(&entry
->val
== (VALUE
*)val
);
63 if (wmap_live_p(entry
->key
) && wmap_live_p(entry
->val
)) {
67 int ret
= data
->func(entry
, data
->arg
);
75 /* We cannot free the weakmap_entry here because the ST_DELETE could
76 * hash the key which would read the weakmap_entry and would cause a
77 * use-after-free. Instead, we store this entry and free it on the next
79 data
->dead_entry
= entry
;
86 wmap_foreach(struct weakmap
*w
, int (*func
)(struct weakmap_entry
*, st_data_t
), st_data_t arg
)
88 struct wmap_foreach_data foreach_data
= {
94 st_foreach(w
->table
, wmap_foreach_i
, (st_data_t
)&foreach_data
);
96 ruby_sized_xfree(foreach_data
.dead_entry
, sizeof(struct weakmap_entry
));
100 wmap_mark_weak_table_i(struct weakmap_entry
*entry
, st_data_t _
)
102 rb_gc_mark_weak(&entry
->key
);
103 rb_gc_mark_weak(&entry
->val
);
111 struct weakmap
*w
= ptr
;
113 wmap_foreach(w
, wmap_mark_weak_table_i
, (st_data_t
)0);
118 wmap_free_table_i(st_data_t key
, st_data_t val
, st_data_t arg
)
120 struct weakmap_entry
*entry
= (struct weakmap_entry
*)key
;
121 RUBY_ASSERT(&entry
->val
== (VALUE
*)val
);
122 ruby_sized_xfree(entry
, sizeof(struct weakmap_entry
));
130 struct weakmap
*w
= ptr
;
132 st_foreach(w
->table
, wmap_free_table_i
, 0);
133 st_free_table(w
->table
);
137 wmap_memsize(const void *ptr
)
139 const struct weakmap
*w
= ptr
;
142 size
+= st_memsize(w
->table
);
143 /* The key and value of the table each take sizeof(VALUE) in size. */
144 size
+= st_table_size(w
->table
) * (2 * sizeof(VALUE
));
149 struct wmap_compact_table_data
{
151 struct weakmap_entry
*dead_entry
;
155 wmap_compact_table_i(st_data_t key
, st_data_t val
, st_data_t d
)
157 struct wmap_compact_table_data
*data
= (struct wmap_compact_table_data
*)d
;
158 if (data
->dead_entry
!= NULL
) {
159 ruby_sized_xfree(data
->dead_entry
, sizeof(struct weakmap_entry
));
160 data
->dead_entry
= NULL
;
163 struct weakmap_entry
*entry
= (struct weakmap_entry
*)key
;
165 entry
->val
= rb_gc_location(entry
->val
);
167 VALUE new_key
= rb_gc_location(entry
->key
);
169 /* If the key object moves, then we must reinsert because the hash is
170 * based on the pointer rather than the object itself. */
171 if (entry
->key
!= new_key
) {
172 DURING_GC_COULD_MALLOC_REGION_START();
174 struct weakmap_entry
*new_entry
= xmalloc(sizeof(struct weakmap_entry
));
175 new_entry
->key
= new_key
;
176 new_entry
->val
= entry
->val
;
177 st_insert(data
->table
, (st_data_t
)&new_entry
->key
, (st_data_t
)&new_entry
->val
);
179 DURING_GC_COULD_MALLOC_REGION_END();
181 /* We cannot free the weakmap_entry here because the ST_DELETE could
182 * hash the key which would read the weakmap_entry and would cause a
183 * use-after-free. Instead, we store this entry and free it on the next
185 data
->dead_entry
= entry
;
194 wmap_compact(void *ptr
)
196 struct weakmap
*w
= ptr
;
199 struct wmap_compact_table_data compact_data
= {
204 st_foreach(w
->table
, wmap_compact_table_i
, (st_data_t
)&compact_data
);
206 ruby_sized_xfree(compact_data
.dead_entry
, sizeof(struct weakmap_entry
));
210 static const rb_data_type_t weakmap_type
= {
218 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
| RUBY_TYPED_WB_PROTECTED
| RUBY_TYPED_EMBEDDABLE
222 wmap_cmp(st_data_t x
, st_data_t y
)
224 VALUE x_obj
= *(VALUE
*)x
;
225 VALUE y_obj
= *(VALUE
*)y
;
227 if (!wmap_live_p(x_obj
) && !wmap_live_p(y_obj
)) {
231 return x_obj
!= y_obj
;
236 wmap_hash(st_data_t n
)
238 return st_numhash(*(VALUE
*)n
);
241 static const struct st_hash_type wmap_hash_type
= {
247 wmap_allocate(VALUE klass
)
250 VALUE obj
= TypedData_Make_Struct(klass
, struct weakmap
, &weakmap_type
, w
);
251 w
->table
= st_init_table(&wmap_hash_type
);
256 wmap_inspect_append(VALUE str
, VALUE obj
)
258 if (SPECIAL_CONST_P(obj
)) {
259 return rb_str_append(str
, rb_inspect(obj
));
262 return rb_str_append(str
, rb_any_to_s(obj
));
267 wmap_inspect_i(struct weakmap_entry
*entry
, st_data_t data
)
269 VALUE str
= (VALUE
)data
;
271 if (RSTRING_PTR(str
)[0] == '#') {
272 rb_str_cat2(str
, ", ");
275 rb_str_cat2(str
, ": ");
276 RSTRING_PTR(str
)[0] = '#';
279 wmap_inspect_append(str
, entry
->key
);
280 rb_str_cat2(str
, " => ");
281 wmap_inspect_append(str
, entry
->val
);
287 wmap_inspect(VALUE self
)
289 VALUE c
= rb_class_name(CLASS_OF(self
));
291 TypedData_Get_Struct(self
, struct weakmap
, &weakmap_type
, w
);
293 VALUE str
= rb_sprintf("-<%"PRIsVALUE
":%p", c
, (void *)self
);
295 wmap_foreach(w
, wmap_inspect_i
, (st_data_t
)str
);
297 RSTRING_PTR(str
)[0] = '#';
298 rb_str_cat2(str
, ">");
304 wmap_each_i(struct weakmap_entry
*entry
, st_data_t _
)
306 rb_yield_values(2, entry
->key
, entry
->val
);
313 * map.each {|key, val| ... } -> self
315 * Iterates over keys and values. Note that unlike other collections,
316 * +each+ without block isn't supported.
320 wmap_each(VALUE self
)
323 TypedData_Get_Struct(self
, struct weakmap
, &weakmap_type
, w
);
325 wmap_foreach(w
, wmap_each_i
, (st_data_t
)0);
331 wmap_each_key_i(struct weakmap_entry
*entry
, st_data_t _data
)
333 rb_yield(entry
->key
);
340 * map.each_key {|key| ... } -> self
342 * Iterates over keys. Note that unlike other collections,
343 * +each_key+ without block isn't supported.
347 wmap_each_key(VALUE self
)
350 TypedData_Get_Struct(self
, struct weakmap
, &weakmap_type
, w
);
352 wmap_foreach(w
, wmap_each_key_i
, (st_data_t
)0);
358 wmap_each_value_i(struct weakmap_entry
*entry
, st_data_t _data
)
360 rb_yield(entry
->val
);
367 * map.each_value {|val| ... } -> self
369 * Iterates over values. Note that unlike other collections,
370 * +each_value+ without block isn't supported.
374 wmap_each_value(VALUE self
)
377 TypedData_Get_Struct(self
, struct weakmap
, &weakmap_type
, w
);
379 wmap_foreach(w
, wmap_each_value_i
, (st_data_t
)0);
385 wmap_keys_i(struct weakmap_entry
*entry
, st_data_t arg
)
387 VALUE ary
= (VALUE
)arg
;
389 rb_ary_push(ary
, entry
->key
);
396 * map.keys -> new_array
398 * Returns a new Array containing all keys in the map.
402 wmap_keys(VALUE self
)
405 TypedData_Get_Struct(self
, struct weakmap
, &weakmap_type
, w
);
407 VALUE ary
= rb_ary_new();
408 wmap_foreach(w
, wmap_keys_i
, (st_data_t
)ary
);
414 wmap_values_i(struct weakmap_entry
*entry
, st_data_t arg
)
416 VALUE ary
= (VALUE
)arg
;
418 rb_ary_push(ary
, entry
->val
);
425 * map.values -> new_array
427 * Returns a new Array containing all values in the map.
431 wmap_values(VALUE self
)
434 TypedData_Get_Struct(self
, struct weakmap
, &weakmap_type
, w
);
436 VALUE ary
= rb_ary_new();
437 wmap_foreach(w
, wmap_values_i
, (st_data_t
)ary
);
443 wmap_aset_replace(st_data_t
*key
, st_data_t
*val
, st_data_t new_key_ptr
, int existing
)
445 VALUE new_key
= *(VALUE
*)new_key_ptr
;
446 VALUE new_val
= *(((VALUE
*)new_key_ptr
) + 1);
449 RUBY_ASSERT(*(VALUE
*)*key
== new_key
);
452 struct weakmap_entry
*entry
= xmalloc(sizeof(struct weakmap_entry
));
454 *key
= (st_data_t
)&entry
->key
;
455 *val
= (st_data_t
)&entry
->val
;
458 *(VALUE
*)*key
= new_key
;
459 *(VALUE
*)*val
= new_val
;
466 * map[key] = value -> value
468 * Associates the given +value+ with the given +key+.
470 * If the given +key+ exists, replaces its value with the given +value+;
471 * the ordering is not affected.
474 wmap_aset(VALUE self
, VALUE key
, VALUE val
)
477 TypedData_Get_Struct(self
, struct weakmap
, &weakmap_type
, w
);
479 VALUE pair
[2] = { key
, val
};
481 st_update(w
->table
, (st_data_t
)pair
, wmap_aset_replace
, (st_data_t
)pair
);
483 RB_OBJ_WRITTEN(self
, Qundef
, key
);
484 RB_OBJ_WRITTEN(self
, Qundef
, val
);
489 /* Retrieves a weakly referenced object with the given key */
491 wmap_lookup(VALUE self
, VALUE key
)
493 RUBY_ASSERT(wmap_live_p(key
));
496 TypedData_Get_Struct(self
, struct weakmap
, &weakmap_type
, w
);
499 if (!st_lookup(w
->table
, (st_data_t
)&key
, &data
)) return Qundef
;
501 if (!wmap_live_p(*(VALUE
*)data
)) return Qundef
;
503 return *(VALUE
*)data
;
510 * Returns the value associated with the given +key+ if found.
512 * If +key+ is not found, returns +nil+.
515 wmap_aref(VALUE self
, VALUE key
)
517 VALUE obj
= wmap_lookup(self
, key
);
518 return !UNDEF_P(obj
) ? obj
: Qnil
;
523 * map.delete(key) -> value or nil
524 * map.delete(key) {|key| ... } -> object
526 * Deletes the entry for the given +key+ and returns its associated value.
528 * If no block is given and +key+ is found, deletes the entry and returns the associated value:
529 * m = ObjectSpace::WeakMap.new
532 * m.delete(key) # => 1
535 * If no block is given and +key+ is not found, returns +nil+.
537 * If a block is given and +key+ is found, ignores the block,
538 * deletes the entry, and returns the associated value:
539 * m = ObjectSpace::WeakMap.new
542 * m.delete(key) { |key| raise 'Will never happen'} # => 2
544 * If a block is given and +key+ is not found,
545 * yields the +key+ to the block and returns the block's return value:
546 * m = ObjectSpace::WeakMap.new
547 * m.delete("nosuch") { |key| "Key #{key} not found" } # => "Key nosuch not found"
550 wmap_delete(VALUE self
, VALUE key
)
553 TypedData_Get_Struct(self
, struct weakmap
, &weakmap_type
, w
);
555 VALUE orig_key
= key
;
556 st_data_t orig_key_data
= (st_data_t
)&orig_key
;
557 st_data_t orig_val_data
;
558 if (st_delete(w
->table
, &orig_key_data
, &orig_val_data
)) {
559 VALUE orig_val
= *(VALUE
*)orig_val_data
;
561 rb_gc_remove_weak(self
, (VALUE
*)orig_key_data
);
562 rb_gc_remove_weak(self
, (VALUE
*)orig_val_data
);
564 struct weakmap_entry
*entry
= (struct weakmap_entry
*)orig_key_data
;
565 ruby_sized_xfree(entry
, sizeof(struct weakmap_entry
));
567 if (wmap_live_p(orig_val
)) {
572 if (rb_block_given_p()) {
573 return rb_yield(key
);
582 * map.key?(key) -> true or false
584 * Returns +true+ if +key+ is a key in +self+, otherwise +false+.
587 wmap_has_key(VALUE self
, VALUE key
)
589 return RBOOL(!UNDEF_P(wmap_lookup(self
, key
)));
596 * Returns the number of referenced objects
599 wmap_size(VALUE self
)
602 TypedData_Get_Struct(self
, struct weakmap
, &weakmap_type
, w
);
604 st_index_t n
= st_table_size(w
->table
);
606 #if SIZEOF_ST_INDEX_T <= SIZEOF_LONG
613 /* ===== WeakKeyMap =====
615 * WeakKeyMap contains one ST table which contains a pointer to the object as
616 * the key and the object as the value. This means that the key is of the type
617 * `VALUE *` while the value is of the type `VALUE`.
619 * The object is not directly stored as keys in the table because
620 * `rb_gc_mark_weak` requires a pointer to the memory location to overwrite
621 * when the object is reclaimed. Using a pointer into the ST table entry is not
622 * safe because the pointer can change when the ST table is resized.
624 * WeakKeyMap hashes and compares using the `#hash` and `#==` methods of the
625 * object, respectively.
627 * During GC and while iterating, reclaimed entries (i.e. the key points to
628 * `Qundef`) are removed from the ST table.
636 wkmap_mark_table_i(st_data_t key
, st_data_t val_obj
, st_data_t data
)
638 VALUE
**dead_entry
= (VALUE
**)data
;
639 if (dead_entry
!= NULL
) {
640 ruby_sized_xfree(*dead_entry
, sizeof(VALUE
));
644 VALUE
*key_ptr
= (VALUE
*)key
;
646 if (wmap_live_p(*key_ptr
)) {
647 rb_gc_mark_weak(key_ptr
);
648 rb_gc_mark_movable((VALUE
)val_obj
);
653 *dead_entry
= key_ptr
;
660 wkmap_mark(void *ptr
)
662 struct weakkeymap
*w
= ptr
;
664 VALUE
*dead_entry
= NULL
;
665 st_foreach(w
->table
, wkmap_mark_table_i
, (st_data_t
)&dead_entry
);
666 if (dead_entry
!= NULL
) {
667 ruby_sized_xfree(dead_entry
, sizeof(VALUE
));
673 wkmap_free_table_i(st_data_t key
, st_data_t _val
, st_data_t _arg
)
675 ruby_sized_xfree((VALUE
*)key
, sizeof(VALUE
));
680 wkmap_free(void *ptr
)
682 struct weakkeymap
*w
= ptr
;
684 st_foreach(w
->table
, wkmap_free_table_i
, 0);
685 st_free_table(w
->table
);
689 wkmap_memsize(const void *ptr
)
691 const struct weakkeymap
*w
= ptr
;
694 size
+= st_memsize(w
->table
);
695 /* Each key of the table takes sizeof(VALUE) in size. */
696 size
+= st_table_size(w
->table
) * sizeof(VALUE
);
702 wkmap_compact_table_i(st_data_t key
, st_data_t val_obj
, st_data_t data
, int _error
)
704 VALUE
**dead_entry
= (VALUE
**)data
;
705 if (dead_entry
!= NULL
) {
706 ruby_sized_xfree(*dead_entry
, sizeof(VALUE
));
710 VALUE
*key_ptr
= (VALUE
*)key
;
712 if (wmap_live_p(*key_ptr
)) {
713 if (*key_ptr
!= rb_gc_location(*key_ptr
) || val_obj
!= rb_gc_location(val_obj
)) {
720 *dead_entry
= key_ptr
;
727 wkmap_compact_table_replace(st_data_t
*key_ptr
, st_data_t
*val_ptr
, st_data_t _data
, int existing
)
729 RUBY_ASSERT(existing
);
731 *(VALUE
*)*key_ptr
= rb_gc_location(*(VALUE
*)*key_ptr
);
732 *val_ptr
= (st_data_t
)rb_gc_location((VALUE
)*val_ptr
);
738 wkmap_compact(void *ptr
)
740 struct weakkeymap
*w
= ptr
;
743 VALUE
*dead_entry
= NULL
;
744 st_foreach_with_replace(w
->table
, wkmap_compact_table_i
, wkmap_compact_table_replace
, (st_data_t
)&dead_entry
);
745 if (dead_entry
!= NULL
) {
746 ruby_sized_xfree(dead_entry
, sizeof(VALUE
));
751 static const rb_data_type_t weakkeymap_type
= {
759 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
| RUBY_TYPED_WB_PROTECTED
| RUBY_TYPED_EMBEDDABLE
763 wkmap_cmp(st_data_t x
, st_data_t y
)
765 VALUE x_obj
= *(VALUE
*)x
;
766 VALUE y_obj
= *(VALUE
*)y
;
768 if (wmap_live_p(x_obj
) && wmap_live_p(y_obj
)) {
769 return rb_any_cmp(x_obj
, y_obj
);
772 /* If one of the objects is dead, then they cannot be the same. */
778 wkmap_hash(st_data_t n
)
780 VALUE obj
= *(VALUE
*)n
;
781 RUBY_ASSERT(wmap_live_p(obj
));
783 return rb_any_hash(obj
);
786 static const struct st_hash_type wkmap_hash_type
= {
792 wkmap_allocate(VALUE klass
)
794 struct weakkeymap
*w
;
795 VALUE obj
= TypedData_Make_Struct(klass
, struct weakkeymap
, &weakkeymap_type
, w
);
796 w
->table
= st_init_table(&wkmap_hash_type
);
801 wkmap_lookup(VALUE self
, VALUE key
)
803 struct weakkeymap
*w
;
804 TypedData_Get_Struct(self
, struct weakkeymap
, &weakkeymap_type
, w
);
807 if (!st_lookup(w
->table
, (st_data_t
)&key
, &data
)) return Qundef
;
816 * Returns the value associated with the given +key+ if found.
818 * If +key+ is not found, returns +nil+.
821 wkmap_aref(VALUE self
, VALUE key
)
823 VALUE obj
= wkmap_lookup(self
, key
);
824 return !UNDEF_P(obj
) ? obj
: Qnil
;
827 struct wkmap_aset_args
{
833 wkmap_aset_replace(st_data_t
*key
, st_data_t
*val
, st_data_t data_args
, int existing
)
835 struct wkmap_aset_args
*args
= (struct wkmap_aset_args
*)data_args
;
838 *key
= (st_data_t
)xmalloc(sizeof(VALUE
));
841 *(VALUE
*)*key
= args
->new_key
;
842 *val
= (st_data_t
)args
->new_val
;
849 * map[key] = value -> value
851 * Associates the given +value+ with the given +key+
853 * The reference to +key+ is weak, so when there is no other reference
854 * to +key+ it may be garbage collected.
856 * If the given +key+ exists, replaces its value with the given +value+;
857 * the ordering is not affected
860 wkmap_aset(VALUE self
, VALUE key
, VALUE val
)
862 struct weakkeymap
*w
;
863 TypedData_Get_Struct(self
, struct weakkeymap
, &weakkeymap_type
, w
);
865 if (!FL_ABLE(key
) || SYMBOL_P(key
) || RB_BIGNUM_TYPE_P(key
) || RB_TYPE_P(key
, T_FLOAT
)) {
866 rb_raise(rb_eArgError
, "WeakKeyMap must be garbage collectable");
867 UNREACHABLE_RETURN(Qnil
);
870 struct wkmap_aset_args args
= {
875 st_update(w
->table
, (st_data_t
)&key
, wkmap_aset_replace
, (st_data_t
)&args
);
877 RB_OBJ_WRITTEN(self
, Qundef
, key
);
878 RB_OBJ_WRITTEN(self
, Qundef
, val
);
885 * map.delete(key) -> value or nil
886 * map.delete(key) {|key| ... } -> object
888 * Deletes the entry for the given +key+ and returns its associated value.
890 * If no block is given and +key+ is found, deletes the entry and returns the associated value:
891 * m = ObjectSpace::WeakKeyMap.new
892 * key = "foo" # to hold reference to the key
894 * m.delete("foo") # => 1
897 * If no block given and +key+ is not found, returns +nil+.
899 * If a block is given and +key+ is found, ignores the block,
900 * deletes the entry, and returns the associated value:
901 * m = ObjectSpace::WeakKeyMap.new
902 * key = "foo" # to hold reference to the key
904 * m.delete("foo") { |key| raise 'Will never happen'} # => 2
906 * If a block is given and +key+ is not found,
907 * yields the +key+ to the block and returns the block's return value:
908 * m = ObjectSpace::WeakKeyMap.new
909 * m.delete("nosuch") { |key| "Key #{key} not found" } # => "Key nosuch not found"
913 wkmap_delete(VALUE self
, VALUE key
)
915 struct weakkeymap
*w
;
916 TypedData_Get_Struct(self
, struct weakkeymap
, &weakkeymap_type
, w
);
918 VALUE orig_key
= key
;
919 st_data_t orig_key_data
= (st_data_t
)&orig_key
;
920 st_data_t orig_val_data
;
921 if (st_delete(w
->table
, &orig_key_data
, &orig_val_data
)) {
922 VALUE orig_val
= (VALUE
)orig_val_data
;
924 rb_gc_remove_weak(self
, (VALUE
*)orig_key_data
);
926 ruby_sized_xfree((VALUE
*)orig_key_data
, sizeof(VALUE
));
931 if (rb_block_given_p()) {
932 return rb_yield(key
);
941 * map.getkey(key) -> existing_key or nil
943 * Returns the existing equal key if it exists, otherwise returns +nil+.
945 * This might be useful for implementing caches, so that only one copy of
946 * some object would be used everywhere in the program:
948 * value = {amount: 1, currency: 'USD'}
950 * # Now if we put this object in a cache:
951 * cache = ObjectSpace::WeakKeyMap.new
952 * cache[value] = true
954 * # ...we can always extract from there and use the same object:
955 * copy = cache.getkey({amount: 1, currency: 'USD'})
956 * copy.object_id == value.object_id #=> true
959 wkmap_getkey(VALUE self
, VALUE key
)
961 struct weakkeymap
*w
;
962 TypedData_Get_Struct(self
, struct weakkeymap
, &weakkeymap_type
, w
);
965 if (!st_get_key(w
->table
, (st_data_t
)&key
, &orig_key
)) return Qnil
;
967 return *(VALUE
*)orig_key
;
972 * map.key?(key) -> true or false
974 * Returns +true+ if +key+ is a key in +self+, otherwise +false+.
977 wkmap_has_key(VALUE self
, VALUE key
)
979 return RBOOL(!UNDEF_P(wkmap_lookup(self
, key
)));
983 wkmap_clear_i(st_data_t key
, st_data_t val
, st_data_t data
)
985 VALUE self
= (VALUE
)data
;
987 /* This WeakKeyMap may have already been marked, so we need to remove the
988 * keys to prevent a use-after-free. */
989 rb_gc_remove_weak(self
, (VALUE
*)key
);
990 return wkmap_free_table_i(key
, val
, 0);
997 * Removes all map entries; returns +self+.
1000 wkmap_clear(VALUE self
)
1002 struct weakkeymap
*w
;
1003 TypedData_Get_Struct(self
, struct weakkeymap
, &weakkeymap_type
, w
);
1005 st_foreach(w
->table
, wkmap_clear_i
, (st_data_t
)self
);
1013 * map.inspect -> new_string
1015 * Returns a new String containing informations about the map:
1017 * m = ObjectSpace::WeakKeyMap.new
1019 * m.inspect # => "#<ObjectSpace::WeakKeyMap:0x00000001028dcba8 size=1>"
1023 wkmap_inspect(VALUE self
)
1025 struct weakkeymap
*w
;
1026 TypedData_Get_Struct(self
, struct weakkeymap
, &weakkeymap_type
, w
);
1028 st_index_t n
= st_table_size(w
->table
);
1030 #if SIZEOF_ST_INDEX_T <= SIZEOF_LONG
1031 const char * format
= "#<%"PRIsVALUE
":%p size=%lu>";
1033 const char * format
= "#<%"PRIsVALUE
":%p size=%llu>";
1036 VALUE str
= rb_sprintf(format
, rb_class_name(CLASS_OF(self
)), (void *)self
, n
);
1041 * Document-class: ObjectSpace::WeakMap
1043 * An ObjectSpace::WeakMap is a key-value map that holds weak references
1044 * to its keys and values, so they can be garbage-collected when there are
1045 * no more references left.
1047 * Keys in the map are compared by identity.
1049 * m = ObjectSpace::WeakMap.new
1058 * m[key1] #=> #<Object:0x0...>
1059 * m[key2] #=> #<Object:0x0...>
1061 * val1 = nil # remove the other reference to value
1065 * m.keys #=> ["bar"]
1067 * key2 = nil # remove the other reference to key
1073 * (Note that GC.start is used here only for demonstrational purposes and might
1074 * not always lead to demonstrated results.)
1077 * See also ObjectSpace::WeakKeyMap map class, which compares keys by value,
1078 * and holds weak references only to the keys.
1082 * Document-class: ObjectSpace::WeakKeyMap
1084 * An ObjectSpace::WeakKeyMap is a key-value map that holds weak references
1085 * to its keys, so they can be garbage collected when there is no more references.
1087 * Unlike ObjectSpace::WeakMap:
1089 * * references to values are _strong_, so they aren't garbage collected while
1090 * they are in the map;
1091 * * keys are compared by value (using Object#eql?), not by identity;
1092 * * only garbage-collectable objects can be used as keys.
1094 * map = ObjectSpace::WeakKeyMap.new
1095 * val = Time.new(2023, 12, 7)
1099 * # Value is fetched by equality: the instance of string "name" is
1100 * # different here, but it is equal to the key
1101 * map["name"] #=> 2023-12-07 00:00:00 +0200
1105 * # There are no more references to `val`, yet the pair isn't
1106 * # garbage-collected.
1107 * map["name"] #=> 2023-12-07 00:00:00 +0200
1111 * # There are no more references to `key`, key and value are
1112 * # garbage-collected.
1113 * map["name"] #=> nil
1115 * (Note that GC.start is used here only for demonstrational purposes and might
1116 * not always lead to demonstrated results.)
1118 * The collection is especially useful for implementing caches of lightweight value
1119 * objects, so that only one copy of each value representation would be stored in
1120 * memory, but the copies that aren't used would be garbage-collected.
1122 * CACHE = ObjectSpace::WeakKeyMap
1124 * def make_value(**)
1125 * val = ValueObject.new(**)
1126 * if (existing = @cache.getkey(val))
1127 * # if the object with this value exists, we return it
1130 * # otherwise, put it in the cache
1131 * @cache[val] = true
1136 * This will result in +make_value+ returning the same object for same set of attributes
1137 * always, but the values that aren't needed anymore wouldn't be sitting in the cache forever.
1143 VALUE rb_mObjectSpace
= rb_define_module("ObjectSpace");
1145 VALUE rb_cWeakMap
= rb_define_class_under(rb_mObjectSpace
, "WeakMap", rb_cObject
);
1146 rb_define_alloc_func(rb_cWeakMap
, wmap_allocate
);
1147 rb_define_method(rb_cWeakMap
, "[]=", wmap_aset
, 2);
1148 rb_define_method(rb_cWeakMap
, "[]", wmap_aref
, 1);
1149 rb_define_method(rb_cWeakMap
, "delete", wmap_delete
, 1);
1150 rb_define_method(rb_cWeakMap
, "include?", wmap_has_key
, 1);
1151 rb_define_method(rb_cWeakMap
, "member?", wmap_has_key
, 1);
1152 rb_define_method(rb_cWeakMap
, "key?", wmap_has_key
, 1);
1153 rb_define_method(rb_cWeakMap
, "inspect", wmap_inspect
, 0);
1154 rb_define_method(rb_cWeakMap
, "each", wmap_each
, 0);
1155 rb_define_method(rb_cWeakMap
, "each_pair", wmap_each
, 0);
1156 rb_define_method(rb_cWeakMap
, "each_key", wmap_each_key
, 0);
1157 rb_define_method(rb_cWeakMap
, "each_value", wmap_each_value
, 0);
1158 rb_define_method(rb_cWeakMap
, "keys", wmap_keys
, 0);
1159 rb_define_method(rb_cWeakMap
, "values", wmap_values
, 0);
1160 rb_define_method(rb_cWeakMap
, "size", wmap_size
, 0);
1161 rb_define_method(rb_cWeakMap
, "length", wmap_size
, 0);
1162 rb_include_module(rb_cWeakMap
, rb_mEnumerable
);
1164 VALUE rb_cWeakKeyMap
= rb_define_class_under(rb_mObjectSpace
, "WeakKeyMap", rb_cObject
);
1165 rb_define_alloc_func(rb_cWeakKeyMap
, wkmap_allocate
);
1166 rb_define_method(rb_cWeakKeyMap
, "[]=", wkmap_aset
, 2);
1167 rb_define_method(rb_cWeakKeyMap
, "[]", wkmap_aref
, 1);
1168 rb_define_method(rb_cWeakKeyMap
, "delete", wkmap_delete
, 1);
1169 rb_define_method(rb_cWeakKeyMap
, "getkey", wkmap_getkey
, 1);
1170 rb_define_method(rb_cWeakKeyMap
, "key?", wkmap_has_key
, 1);
1171 rb_define_method(rb_cWeakKeyMap
, "clear", wkmap_clear
, 0);
1172 rb_define_method(rb_cWeakKeyMap
, "inspect", wkmap_inspect
, 0);