class ObjectSpace::WeakKeyMap

An ObjectSpace::WeakKeyMap object holds references to any objects, but objects uses as keys can be garbage collected.

Objects used as values can’t be garbage collected until the key is.

Public Instance Methods

map[key] → value click to toggle source

Returns the value associated with the given key if found.

If key is not found, returns nil.

static VALUE
wkmap_aref(VALUE self, VALUE key)
{
    VALUE obj = wkmap_lookup(self, key);
    return obj != Qundef ? obj : Qnil;
}
map[key] = value → value click to toggle source

Associates the given value with the given key; returns value.

The reference to key is weak, so when there is no other reference to key it may be garbage collected.

If the given key exists, replaces its value with the given value; the ordering is not affected

static VALUE
wkmap_aset(VALUE self, VALUE key, VALUE value)
{
    struct weakkeymap *w;
    TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);

    if (!FL_ABLE(key) || SYMBOL_P(key) || RB_BIGNUM_TYPE_P(key) || RB_TYPE_P(key, T_FLOAT)) {
        rb_raise(rb_eArgError, "WeakKeyMap must be garbage collectable");
        UNREACHABLE_RETURN(Qnil);
    }

    st_index_t hash = wkmap_lookup_hash(w, key);
    weakkeymap_entry_t *key_entry = wkmap_lookup_entry(w, key, hash);

    if (!key_entry) {
        key_entry = ALLOC(weakkeymap_entry_t);
        key_entry->obj = key;
        key_entry->hash = hash;
    }

    if (!st_insert(w->map, (st_data_t)key_entry, (st_data_t)value)) {
        st_insert(w->obj2hash, (st_data_t)key, (st_data_t)hash);
        rb_define_finalizer_no_check(key, w->final);
    }

    RB_OBJ_WRITTEN(self, Qundef, value);

    return value;
}
clear → self click to toggle source

Removes all map entries; returns self.

static VALUE
wkmap_clear(VALUE self)
{
    struct weakkeymap *w;
    TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
    if (w->map) {
        st_clear(w->map);
    }
    if (w->obj2hash) {
        st_clear(w->obj2hash);
    }
    return self;
}
delete(key) → value or nil click to toggle source
delete(key) {|key| ... } → object

Deletes the entry for the given key and returns its associated value.

If no block is given and key is found, deletes the entry and returns the associated value:

m = ObjectSpace::WeakKeyMap.new
m["foo"] = 1
m.delete("foo") # => 1
m["foo"] # => nil

If no block given and key is not found, returns nil.

If a block is given and key is found, ignores the block, deletes the entry, and returns the associated value:

m = ObjectSpace::WeakKeyMap.new
m["foo"] = 2
h.delete("foo") { |key| raise 'Will never happen'} # => 2

If a block is given and key is not found, calls the block and returns the block’s return value:

m = ObjectSpace::WeakKeyMap.new
h.delete("nosuch") { |key| "Key #{key} not found" } # => "Key nosuch not found"
static VALUE
wkmap_delete(VALUE self, VALUE key)
{
    struct weakkeymap *w;
    TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);

    st_index_t hash = rb_any_hash(key);
    weakkeymap_entry_t lookup_entry = {key, hash};
    weakkeymap_entry_t *deleted_entry = NULL;
    if (st_get_key(w->map, (st_data_t)&lookup_entry, (st_data_t *)&deleted_entry)) {
        st_data_t deleted_value;
        if (st_delete(w->map, (st_data_t *)&deleted_entry, &deleted_value)) {
            xfree(deleted_entry);
            st_delete(w->obj2hash, (st_data_t *)key, &hash);
            return (VALUE)deleted_value;
        }
        else {
            rb_bug("WeakKeyMap: miss on delete, corrupted memory?");
        }
    }
    else if (rb_block_given_p()) {
        return rb_yield(key);
    }
    else {
        return Qnil;
    }
}
getkey(key) → existing_key or nil click to toggle source

Returns the existing equal key if it exists, otherwise returns nil.

static VALUE
wkmap_getkey(VALUE self, VALUE key)
{
    struct weakkeymap *w;
    TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);

    st_index_t hash = rb_any_hash(key);
    weakkeymap_entry_t lookup_entry = {key, hash};

    weakkeymap_entry_t *key_entry = NULL;
    if (st_get_key(w->map, (st_data_t)&lookup_entry, (st_data_t *)&key_entry)) {
        assert(key_entry != NULL);

        VALUE obj = key_entry->obj;
        if (wmap_live_p(obj)) {
            return obj;
        }
    }
    return Qnil;
}
inspect → new_string click to toggle source

Returns a new String containing informations about the map:

m = ObjectSpace::WeakKeyMap.new
m[key] = value
m.inspect # => "#<ObjectSpace::WeakKeyMap:0x00000001028dcba8 size=1>"
static VALUE
wkmap_inspect(VALUE self)
{
    struct weakkeymap *w;
    TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);

    st_index_t n = 0;
    if (w->map) {
        n = w->map->num_entries;
    }

#if SIZEOF_ST_INDEX_T <= SIZEOF_LONG
    const char * format = "#<%"PRIsVALUE":%p size=%lu>";
#else
    const char * format = "#<%"PRIsVALUE":%p size=%llu>";
#endif

    VALUE str = rb_sprintf(format, rb_class_name(CLASS_OF(self)), (void *)self, n);
    return str;
}
key?(key) → true or false click to toggle source

Returns true if key is a key in self, otherwise false.

static VALUE
wkmap_has_key(VALUE self, VALUE key)
{
    return RBOOL(wkmap_lookup(self, key) != Qundef);
}