/*
Copyright (C) 2005-2008, The Perl Foundation.
$Id: /mirror/trunk/src/pmc/addrregistry.pmc 34039 2008-12-17T03:44:25.526016Z cotto  $

=head1 NAME

src/pmc/addrregistry.pmc - A DOD Registry PMC

=head1 DESCRIPTION

The AddrRegistry class provides the equivalence of reference counts
mainly for extenders and embedders of Parrot. The hash keys are the
addresses of the key PMC, values are reference counts, i.e. the
difference of (set_pmc_keyed - delete_pmc_keyed). If the reference
goes to zero, the entry is deleted physically.

Please note that you have to anchor an instance of AddrRegistry yourself with
C<Parrot_register_pmc> if it isn't visible to Parrot.

=head2 Functions

=over 4

=cut

*/

#include "parrot/parrot.h"

/* included manually to prevent breaking C++ builds -- see RT #56534 */
#include "parrot/hash.h"
#include "pmc_hash.h"

pmclass AddrRegistry need_ext provides hash {
    ATTR Hash *hash;    /* a PMC Hash */
/*

=item C<void init()>

Initializes the instance.

=item C<void destroy()>

Free hash structure.

=cut

*/

    VTABLE void init() {
        Hash                       *registry;
        Parrot_AddrRegistry_attributes * const addr_registry =
            mem_allocate_zeroed_typed(Parrot_AddrRegistry_attributes);

        PMC_data(SELF)      = addr_registry;
        PObj_custom_mark_destroy_SETALL(SELF);

        parrot_new_pmc_hash_x(INTERP, SELF, enum_type_int, Hash_key_type_PMC,
                              int_compare, key_hash_int);

        registry            = (Hash *)PMC_struct_val(SELF);

        /* this hack can go away when parrot_new_pmc_hash_x behaves */
        addr_registry->hash = registry;
        registry->container = SELF;
    }

    VTABLE void destroy() {
        if (PMC_data(SELF)) {
            Parrot_AddrRegistry_attributes * const registry = PARROT_ADDRREGISTRY(SELF);

            parrot_hash_destroy(INTERP, registry->hash);
            mem_sys_free(registry);
            PMC_data(SELF) = NULL;
        }
    }

/*

=item C<void mark()>

Marks the hash as live.

=cut

*/

    VTABLE void mark() {
        if (PMC_data(SELF))
            parrot_mark_hash(INTERP, PARROT_ADDRREGISTRY(SELF)->hash);
    }

/*

=item C<INTVAL get_integer_keyed(PMC *key)>

Returns the reference count for C<key> or 0 if the key doesn't exist.

=item C<INTVAL elements()>

Returns the number of elements in the hash.

=item C<INTVAL get_bool()>

Returns true if the hash size is not zero.

=cut

*/

    VTABLE INTVAL get_integer_keyed(PMC *key) {
        Hash   *hash  = PARROT_ADDRREGISTRY(SELF)->hash;
        void   *value = parrot_hash_get(INTERP, hash, key);

        if (value)
            return (INTVAL)value;

        return 0;
    }

    VTABLE INTVAL elements() {
        return parrot_hash_size(INTERP, PARROT_ADDRREGISTRY(SELF)->hash);
    }

    VTABLE INTVAL get_bool() {
        return parrot_hash_size(INTERP, PARROT_ADDRREGISTRY(SELF)->hash) != 0;
    }
/*

=item C<void set_pmc_keyed(PMC *key, PMC *value)>

Increment the reference count of C<key>. If the entry doesn't exist
create it. The C<value> is always ignored.

=item C<void set_integer_keyed(PMC *key, INTVAL value)>

Set the given value.

=item C<void delete_keyed(PMC *key)>

Decrement the reference count of C<key>. If the reference count
reaches 0, delete the entry.


=cut

*/

    VTABLE void set_pmc_keyed(PMC *key, PMC *value) {
        Hash       * const hash = PARROT_ADDRREGISTRY(SELF)->hash;
        void       *oldval      = parrot_hash_get(INTERP, hash, key);
        long        newval      = 1;
        UNUSED(value);

        if (oldval)
            newval += (long)oldval;

        parrot_hash_put(INTERP, hash, key, (void *)newval);
    }

    VTABLE void set_integer_keyed(PMC *key, INTVAL value) {
        Hash *hash = PARROT_ADDRREGISTRY(SELF)->hash;
        parrot_hash_put(INTERP, hash, key, (void *)value);
    }

    VTABLE void delete_keyed(PMC *key) {
        Hash       * const hash  = PARROT_ADDRREGISTRY(SELF)->hash;
        void              *value = parrot_hash_get(INTERP, hash, key);

        /* these casts look bad, but they avoid type punning warnings with -O */
        if (value) {
            long val = (long)value;
            if (val == 1L)
                parrot_hash_delete(INTERP, hash, key);
            else {
                value = (void *)(--val);
                parrot_hash_put(INTERP, hash, key, value);
            }
        }
    }

/*

=item C<PMC *get_iter ()>

Return a new iterator for the PMC.

=item C<PMC *get_pmc_keyed(PMC *key)>

If called from iteration, return the key PMC, else PMCNULL is returned.

=cut

*/

    VTABLE PMC *get_iter() {
        return Parrot_Hash_get_iter(INTERP, SELF);
    }

    VTABLE PMC *get_pmc_keyed(PMC *key) {
        Hash * const hash = PARROT_ADDRREGISTRY(SELF)->hash;

        if (KEY_IS_HASH_ITERATOR(key))
            return (PMC *)parrot_hash_get_idx(INTERP, hash, key);

        return PMCNULL;
    }
}

/*

=back

=head1 SEE ALSO

F<src/pmc.c:dod_register_pmc()>

=cut

*/

/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4:
 */
