Permalink
Switch branches/tags
Find file
Fetching contributors…
Cannot retrieve contributors at this time
1327 lines (899 sloc) 32.6 KB
/*
Copyright (C) 2001-2014, Parrot Foundation.
=head1 NAME
src/pmc/hash.pmc - Hash PMC
=head1 DESCRIPTION
Hash PMC wraps Parrot's _hash to provide a high-level API:
=over 4
=item *
Convert between various types to use as hash keys.
=item *
Convert between various types to use as hash values.
=item *
Handle compound Keys for nested Hash/Array lookups.
=item *
Provide C<HashIterator> to iterate over C<Hash>.
=back
By default Hash uses string keys and PMC values. Methods C<set_key_type> and
C<set_value_type> may be used to switch key and values type. For C<PMC> keys
hash value is calculated using VTABLE C<get_hashvalue> function.
=cut
*/
#include "pmc/pmc_iterator.h"
#include "pmc/pmc_key.h"
#include "pmc/pmc_hashiteratorkey.h"
/* HEADERIZER HFILE: none */
/* HEADERIZER BEGIN: static */
/* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
PARROT_DOES_NOT_RETURN
static void cannot_autovivify_nested(PARROT_INTERP)
__attribute__nonnull__(1);
PARROT_DOES_NOT_RETURN
static void entry_type_must_be_pmc(PARROT_INTERP)
__attribute__nonnull__(1);
PARROT_CANNOT_RETURN_NULL
PARROT_WARN_UNUSED_RESULT
static PMC* get_next_hash(PARROT_INTERP,
ARGIN(const Hash *hash),
ARGIN(const void *key))
__attribute__nonnull__(1)
__attribute__nonnull__(2)
__attribute__nonnull__(3);
#define ASSERT_ARGS_cannot_autovivify_nested __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
PARROT_ASSERT_ARG(interp))
#define ASSERT_ARGS_entry_type_must_be_pmc __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
PARROT_ASSERT_ARG(interp))
#define ASSERT_ARGS_get_next_hash __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
PARROT_ASSERT_ARG(interp) \
, PARROT_ASSERT_ARG(hash) \
, PARROT_ASSERT_ARG(key))
/* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
/* HEADERIZER END: static */
pmclass Hash provides hash auto_attrs {
ATTR Hash *hash;
/*
=head2 Vtable functions
=over 4
=item C<void init()>
Initializes the instance.
=item C<void init_int(INTVAL value_type)>
Initializes the instance with the value_type provided.
=item C<void destroy()>
Free hash structure.
=cut
*/
VTABLE void init() {
Parrot_Hash_attributes * const attr =
(Parrot_Hash_attributes *) PMC_data(SELF);
attr->hash = Parrot_hash_new(INTERP);
PObj_custom_mark_destroy_SETALL(SELF);
}
VTABLE void init_int(INTVAL value_type) {
Parrot_Hash_attributes * const attr =
(Parrot_Hash_attributes *) PMC_data(SELF);
attr->hash = Parrot_hash_create(INTERP,
(PARROT_DATA_TYPE)value_type,
Hash_key_type_STRING);
PObj_custom_mark_destroy_SETALL(SELF);
}
VTABLE void destroy() :no_wb {
Hash * const hash = (Hash *)SELF.get_pointer();
if (hash)
Parrot_hash_destroy(INTERP, hash);
}
/*
=item C<void mark()>
Marks the hash as live.
=cut
*/
VTABLE void mark() :no_wb {
Hash * const hash = (Hash *)SELF.get_pointer();
if (hash && hash->entries)
Parrot_hash_mark(INTERP, hash);
}
/*
=item C<PMC *clone()>
Creates and returns a clone of the hash.
=cut
*/
VTABLE PMC *clone() :manual_wb {
PMC * const dest = Parrot_pmc_new(INTERP, SELF->vtable->base_type);
Parrot_hash_clone(INTERP, (Hash *)SELF.get_pointer(),
(Hash *)VTABLE_get_pointer(INTERP, dest));
/*
* Trigger write barrier.
*
* If GC was triggered during deep clone of hash than C<dest>
* will be moved into Gen1 without making it dirty. So, half
* of keys/values will not be marked during next cycle.
*/
PARROT_GC_WRITE_BARRIER(INTERP, dest);
return dest;
}
/*
=item C<void set_pointer(void *ptr)>
Use C<ptr> as this PMC's Hash*.
=cut
*/
VTABLE void set_pointer(void *ptr) {
Hash * const old_hash = (Hash *)SELF.get_pointer();
Hash * const new_hash = (Hash *)ptr;
PARROT_HASH(SELF)->hash = new_hash;
if (old_hash)
Parrot_hash_destroy(INTERP, old_hash);
}
/*
=item C<void set_integer(INTVAL type)>
=item C<void set_key_type(INTVAL type)>
Reset Hash to use different keys. See enum C<Hash_key_type> for possible
values.
NB: this method will destroy all old data!
=cut
*/
VTABLE void set_integer_native(INTVAL type) {
Hash *new_hash;
Hash * const old_hash = PARROT_HASH(SELF)->hash;
const PARROT_DATA_TYPE entry_type = old_hash
? old_hash->entry_type
: enum_type_PMC;
switch (type) {
case Hash_key_type_int:
case Hash_key_type_STRING:
case Hash_key_type_PMC:
case Hash_key_type_PMC_ptr:
case Hash_key_type_STRING_enc:
new_hash = Parrot_hash_create(INTERP,
entry_type,
(Hash_key_type)type);
break;
default:
/* We probably will not implement other types of keys. They are way
* too dangerous to use from PIR */
Parrot_ex_throw_from_c_noargs(INTERP, EXCEPTION_UNIMPLEMENTED,
"Hash: Unknown key type");
}
PARROT_HASH(SELF)->hash = new_hash;
if (old_hash)
Parrot_hash_destroy(INTERP, old_hash);
}
/*
=item C<void *get_pointer()>
Get a pointer to this PMC's Hash*.
=cut
*/
VTABLE void *get_pointer() :no_wb {
UNUSED(INTERP);
return PARROT_HASH(SELF)->hash;
}
/*
=item C<INTVAL get_integer()>
=item C<FLOATVAL get_number()>
Returns the size of the hash.
=cut
*/
VTABLE INTVAL get_integer() :no_wb {
return Parrot_hash_size(INTERP, (Hash *)SELF.get_pointer());
}
VTABLE FLOATVAL get_number() :no_wb {
return SELF.get_integer();
}
/*
=item C<STRING *get_string()>
Returns a string representation of the hash, showing its class name and
memory address.
=item C<STRING *get_repr()>
Return a representation of the hash contents.
=cut
*/
VTABLE STRING *get_string() :no_wb {
return Parrot_sprintf_c(INTERP, "Hash[0x%lx]", PTR2ULONG(SELF));
}
VTABLE STRING *get_repr() :no_wb {
PMC * const iter = VTABLE_get_iter(INTERP, SELF);
PMC * const sb = Parrot_pmc_new(INTERP, enum_class_StringBuilder);
const INTVAL n = VTABLE_elements(INTERP, SELF);
INTVAL j;
VTABLE_push_string(INTERP, sb, CONST_STRING(INTERP, "{"));
for (j = 0; j < n; ++j) {
STRING * const key = VTABLE_shift_string(INTERP, iter);
const INTVAL len = STRING_length(key);
PMC *val;
const INTVAL idx = STRING_find_not_cclass(interp, enum_cclass_numeric,
key, 0, len);
if (idx >= len) {
/* Only digits */
VTABLE_push_string(INTERP, sb, key);
}
else {
VTABLE_push_string(INTERP, sb, CONST_STRING(INTERP, "'"));
VTABLE_push_string(INTERP, sb, key);
VTABLE_push_string(INTERP, sb, CONST_STRING(INTERP, "'"));
}
VTABLE_push_string(INTERP, sb, CONST_STRING(INTERP, ":"));
val = SELF.get_pmc_keyed_str(key);
VTABLE_push_string(INTERP, sb, VTABLE_get_string(INTERP, val));
if (j < n - 1)
VTABLE_push_string(INTERP, sb, CONST_STRING(INTERP, ", "));
}
return VTABLE_get_string(INTERP, sb);
}
/*
=item C<INTVAL get_integer_keyed_str(STRING *key)>
=item C<INTVAL get_integer_keyed_int(INTVAL key)>
=cut
*/
VTABLE INTVAL get_integer_keyed_str(STRING *key) :no_wb {
const Hash * const hash = (Hash*)SELF.get_pointer();
HashBucket * const b = Parrot_hash_get_bucket(INTERP, hash,
Parrot_hash_key_from_string(INTERP, hash, key));
if (!b)
return 0;
return Parrot_hash_value_to_int(INTERP, hash, b->value);
}
VTABLE INTVAL get_integer_keyed_int(INTVAL key) :no_wb {
const Hash * const hash = (Hash*)SELF.get_pointer();
HashBucket * const b = Parrot_hash_get_bucket(INTERP, hash,
Parrot_hash_key_from_int(INTERP, hash, key));
if (!b)
return 0;
return Parrot_hash_value_to_int(INTERP, hash, b->value);
}
/*
=item C<INTVAL get_integer_keyed(PMC *key)>
Returns the integer value for the element at C<*key>.
=cut
*/
/* Handling Keys */
VTABLE INTVAL get_integer_keyed(PMC *key) :no_wb {
const Hash * const hash = (Hash *)SELF.get_pointer();
const void * const hash_key = Parrot_hash_key_from_pmc(INTERP, hash, key);
HashBucket * const b = Parrot_hash_get_bucket(INTERP, hash, hash_key);
if (!b)
return 0;
key = Parrot_key_next(INTERP, key);
/* Stop recursion. This is last step */
if (!key)
return Parrot_hash_value_to_int(INTERP, hash, b->value);
if (hash->entry_type != enum_type_PMC)
entry_type_must_be_pmc(INTERP);
/* Recursively call to enclosed aggregate */
return VTABLE_get_integer_keyed(INTERP, (PMC *)b->value, key);
}
/*
=item C<void set_integer_keyed(PMC *key, INTVAL value)>
=cut
*/
VTABLE void set_integer_keyed(PMC *key, INTVAL value) {
Hash * const hash = (Hash *)SELF.get_pointer();
void * const hash_key = Parrot_hash_key_from_pmc(INTERP, hash, key);
if (PObj_constant_TEST(SELF)
&& !PObj_constant_TEST((const PObj *)key))
Parrot_ex_throw_from_c_noargs(INTERP,
EXCEPTION_INVALID_OPERATION,
"Used non-constant PMC key in constant hash");
key = Parrot_key_next(INTERP, key);
if (!key) {
Parrot_hash_put(INTERP, hash, hash_key,
Parrot_hash_value_from_int(INTERP, hash, value));
}
else {
PMC * const next_hash = get_next_hash(INTERP, hash, hash_key);
if (PMC_IS_NULL(next_hash))
cannot_autovivify_nested(INTERP);
VTABLE_set_integer_keyed(INTERP, next_hash, key, value);
}
}
VTABLE void set_integer_keyed_int(INTVAL key, INTVAL value) {
Hash * const hash = (Hash *)SELF.get_pointer();
Parrot_hash_put(INTERP, hash, Parrot_hash_key_from_int(INTERP, hash, key),
Parrot_hash_value_from_int(INTERP, hash, value));
}
/*
=item C<void set_integer_keyed_str(STRING *key, INTVAL value)>
=cut
*/
VTABLE void set_integer_keyed_str(STRING *key, INTVAL value) {
Hash * const hash = (Hash *)SELF.get_pointer();
if (PObj_constant_TEST(SELF)
&& !PObj_constant_TEST((PObj *)key))
Parrot_ex_throw_from_c_noargs(INTERP,
EXCEPTION_INVALID_OPERATION,
"Used non-constant key in constant hash");
Parrot_hash_put(INTERP, hash, Parrot_hash_key_from_string(INTERP, hash, key),
Parrot_hash_value_from_int(INTERP, hash, value));
}
/*
=item C<FLOATVAL get_number_keyed_str(STRING *key)>
=item C<FLOATVAL get_number_keyed_int(INTVAL key)>
=cut
*/
VTABLE FLOATVAL get_number_keyed_str(STRING *key) :no_wb {
const Hash * const hash = (Hash *)SELF.get_pointer();
HashBucket * const b = Parrot_hash_get_bucket(INTERP, hash,
Parrot_hash_key_from_string(INTERP, hash, key));
if (!b)
return 0.0;
return Parrot_hash_value_to_number(INTERP, hash, b->value);
}
VTABLE FLOATVAL get_number_keyed_int(INTVAL key) :no_wb {
const Hash * const hash = (Hash *)SELF.get_pointer();
HashBucket * const b = Parrot_hash_get_bucket(INTERP, hash,
Parrot_hash_key_from_int(INTERP, hash, key));
if (!b)
return 0.0;
return Parrot_hash_value_to_number(INTERP, hash, b->value);
}
/*
=item C<FLOATVAL get_number_keyed(PMC *key)>
Returns the floating-point value for the element at C<*key>.
=cut
*/
/* I can't migrate this function right now. Some problem with JITting */
VTABLE FLOATVAL get_number_keyed(PMC *key) :no_wb {
const Hash * const hash = (Hash *)SELF.get_pointer();
void * const hash_key = Parrot_hash_key_from_pmc(INTERP, hash, key);
HashBucket * const b = Parrot_hash_get_bucket(INTERP, hash, hash_key);
if (!b)
return 0.0;
key = Parrot_key_next(INTERP, key);
if (!key)
return Parrot_hash_value_to_number(INTERP, hash, b->value);
if (hash->entry_type != enum_type_PMC)
entry_type_must_be_pmc(INTERP);
return VTABLE_get_number_keyed(INTERP, (PMC *)b->value, key);
}
/*
=item C<STRING *get_string_keyed_str(STRING *key)>
=item C<STRING *get_string_keyed_int(INTVAL key)>
=cut
*/
VTABLE STRING *get_string_keyed_str(STRING *key) :no_wb {
const Hash * const hash = (Hash*)SELF.get_pointer();
HashBucket * const b =
Parrot_hash_get_bucket(INTERP, hash, Parrot_hash_key_from_string(INTERP, hash, key));
/* XXX: shouldn't we return STRINGNULL? */
if (!b)
return CONST_STRING(INTERP, "");
return Parrot_hash_value_to_string(INTERP, hash, b->value);
}
VTABLE STRING *get_string_keyed_int(INTVAL key) :no_wb {
const Hash * const hash = (Hash*)SELF.get_pointer();
HashBucket * const b =
Parrot_hash_get_bucket(INTERP, hash, Parrot_hash_key_from_int(INTERP, hash, key));
if (!b)
return CONST_STRING(INTERP, "");
return Parrot_hash_value_to_string(INTERP, hash, b->value);
}
/*
=item C<STRING *get_string_keyed(PMC *key)>
Returns the string value for the element at C<*key>.
=cut
*/
VTABLE STRING *get_string_keyed(PMC *key) :no_wb {
const Hash * const hash = (Hash *)SELF.get_pointer();
const void * const hash_key = Parrot_hash_key_from_pmc(INTERP, hash, key);
HashBucket * const b = Parrot_hash_get_bucket(INTERP, hash, hash_key);
if (!b)
return CONST_STRING(INTERP, "");
key = Parrot_key_next(INTERP, key);
/* Stop recursion. This is last step */
if (!key)
return Parrot_hash_value_to_string(INTERP, hash, b->value);
if (hash->entry_type != enum_type_PMC)
entry_type_must_be_pmc(INTERP);
/* Recursively call to enclosed aggregate */
return VTABLE_get_string_keyed(INTERP, (PMC *)b->value, key);
}
/*
=item C<void set_string_keyed(PMC *key, STRING *value)>
=cut
*/
VTABLE void set_string_keyed(PMC *key, STRING *value) {
Hash * const hash = (Hash *)SELF.get_pointer();
void * const hash_key = Parrot_hash_key_from_pmc(INTERP, hash, key);
if (PObj_constant_TEST(SELF)){
if (!PObj_constant_TEST((PObj *)key))
Parrot_ex_throw_from_c_noargs(INTERP,
EXCEPTION_INVALID_OPERATION,
"Used non-constant PMC key in constant hash");
if (!PObj_constant_TEST((PObj *)value))
Parrot_ex_throw_from_c_noargs(INTERP,
EXCEPTION_INVALID_OPERATION,
"Used non-constant STRING value in constant hash");
}
key = Parrot_key_next(INTERP, key);
if (!key) {
Parrot_hash_put(INTERP, hash, hash_key,
Parrot_hash_value_from_string(INTERP, hash, value));
}
else {
PMC * const next_hash = get_next_hash(INTERP, hash, hash_key);
if (PMC_IS_NULL(next_hash))
cannot_autovivify_nested(INTERP);
VTABLE_set_string_keyed(INTERP, next_hash, key, value);
}
}
/*
=item C<void set_string_keyed_str(STRING *key, STRING *value)>
=cut
*/
VTABLE void set_string_keyed_str(STRING *key, STRING *value) {
Hash * const hash = (Hash *)SELF.get_pointer();
if (PObj_constant_TEST(SELF)){
if (!PObj_constant_TEST((PObj *)key))
Parrot_ex_throw_from_c_noargs(INTERP,
EXCEPTION_INVALID_OPERATION,
"Used non-constant STRING key in constant hash");
if (!PObj_constant_TEST((PObj *)value))
Parrot_ex_throw_from_c_noargs(INTERP,
EXCEPTION_INVALID_OPERATION,
"Used non-constant STRING value in constant hash");
}
Parrot_hash_put(INTERP, hash, Parrot_hash_key_from_string(INTERP, hash, key),
Parrot_hash_value_from_string(INTERP, hash, value));
}
VTABLE void set_string_keyed_int(INTVAL key, STRING *value) {
Hash * const hash = (Hash *)SELF.get_pointer();
if ((PObj_constant_TEST(SELF))
&& (!PObj_constant_TEST((PObj *)value)))
Parrot_ex_throw_from_c_noargs(INTERP,
EXCEPTION_INVALID_OPERATION,
"Used non-constant STRING value in constant hash");
Parrot_hash_put(INTERP, hash,
Parrot_hash_key_from_int(INTERP, hash, key),
Parrot_hash_value_from_string(INTERP, hash, value));
}
/*
=item C<PMC *get_pmc_keyed(PMC *key)>
=item C<PMC *get_pmc_keyed_str(STRING *key)>
=item C<PMC *get_pmc_keyed_int(INTVAL key)>
Returns the PMC value for the element at C<*key>.
=cut
*/
VTABLE PMC *get_pmc_keyed_str(STRING *key) :no_wb {
const Hash *hash;
HashBucket *b;
GET_ATTR_hash(INTERP, SELF, hash);
if (!hash->entries)
return PMCNULL;
b = Parrot_hash_get_bucket(INTERP, hash, key);
if (!b)
return PMCNULL;
/* special case the most likely value type, for speed */
if (hash->entry_type == enum_type_PMC)
return (PMC *)b->value;
else
return Parrot_hash_value_to_pmc(INTERP, hash, b->value);
}
VTABLE PMC *get_pmc_keyed_int(INTVAL key) :no_wb {
const Hash * const hash = (Hash *)SELF.get_pointer();
HashBucket * const b = Parrot_hash_get_bucket(INTERP, hash,
Parrot_hash_key_from_int(INTERP, hash, key));
if (!b)
return PMCNULL;
return Parrot_hash_value_to_pmc(INTERP, hash, b->value);
}
/* Compound Key */
VTABLE PMC *get_pmc_keyed(PMC *key) :no_wb {
const Hash * const hash = (Hash *)SELF.get_pointer();
const void * const hash_key = Parrot_hash_key_from_pmc(INTERP, hash, key);
HashBucket * const b = Parrot_hash_get_bucket(INTERP, hash, hash_key);
if (!b)
return PMCNULL;
key = Parrot_key_next(INTERP, key);
/* Stop recursion. This is last step */
if (!key)
return Parrot_hash_value_to_pmc(INTERP, hash, b->value);
if (hash->entry_type != enum_type_PMC)
entry_type_must_be_pmc(INTERP);
/* Recursively call to enclosed aggregate */
return VTABLE_get_pmc_keyed(INTERP, (PMC *)b->value, key);
}
/*
=item C<void set_number_keyed(PMC *key, FLOATVAL value)>
=cut
*/
VTABLE void set_number_keyed(PMC *key, FLOATVAL value) {
Hash * const hash = (Hash *)SELF.get_pointer();
void * const hash_key = Parrot_hash_key_from_pmc(INTERP, hash, key);
if (PObj_constant_TEST(SELF)
&& !PObj_constant_TEST((PObj *)key))
Parrot_ex_throw_from_c_noargs(INTERP,
EXCEPTION_INVALID_OPERATION,
"Used non-constant PMC key in constant hash");
key = Parrot_key_next(INTERP, key);
if (!key) {
Parrot_hash_put(INTERP, hash, hash_key,
Parrot_hash_value_from_number(INTERP, hash, value));
}
else {
PMC * const next_hash = get_next_hash(INTERP, hash, hash_key);
if (PMC_IS_NULL(next_hash))
cannot_autovivify_nested(INTERP);
VTABLE_set_number_keyed(INTERP, next_hash, key, value);
}
}
/*
=item C<void set_number_keyed_str(STRING *key, FLOATVAL value)>
Sets C<value> as the value for C<*key>.
=cut
*/
VTABLE void set_number_keyed_str(STRING *key, FLOATVAL value) {
Hash * const hash = (Hash *)SELF.get_pointer();
if (PObj_constant_TEST(SELF)
&& !PObj_constant_TEST((PObj *)key))
Parrot_ex_throw_from_c_noargs(INTERP,
EXCEPTION_INVALID_OPERATION,
"Used non-constant STRING key in constant hash");
Parrot_hash_put(INTERP, hash, Parrot_hash_key_from_string(INTERP, hash, key),
Parrot_hash_value_from_number(INTERP, hash, value));
}
/*
=item C<void set_pmc_keyed(PMC *dest_key, PMC *value)>
=cut
*/
VTABLE void set_pmc_keyed(PMC *key, PMC *value) {
Hash * const hash = (Hash *)SELF.get_pointer();
void * const hash_key = Parrot_hash_key_from_pmc(INTERP, hash, key);
if (PObj_constant_TEST(SELF)) {
if (!PObj_constant_TEST((PObj *)key))
Parrot_ex_throw_from_c_noargs(INTERP,
EXCEPTION_INVALID_OPERATION,
"Used non-constant PMC key in constant hash");
if (!PObj_constant_TEST((PObj *)value))
Parrot_ex_throw_from_c_noargs(INTERP,
EXCEPTION_INVALID_OPERATION,
"Used non-constant PMC value in constant hash");
}
key = Parrot_key_next(INTERP, key);
if (!key) {
Parrot_hash_put(INTERP, hash, hash_key, value);
}
else {
PMC * const next_hash = get_next_hash(INTERP, hash, hash_key);
if (PMC_IS_NULL(next_hash))
cannot_autovivify_nested(INTERP);
VTABLE_set_pmc_keyed(INTERP, next_hash, key, value);
}
}
/*
=item C<void set_pmc_keyed_str(STRING *key, PMC *value)>
Sets C<*value> as the value for C<*key>.
=cut
*/
VTABLE void set_pmc_keyed_str(STRING *key, PMC *value) {
Hash * const hash = (Hash *)SELF.get_pointer();
if (PObj_constant_TEST(SELF)) {
if (!PObj_constant_TEST((PObj *)key))
Parrot_ex_throw_from_c_noargs(INTERP,
EXCEPTION_INVALID_OPERATION,
"Used non-constant STRING key in constant hash");
if (!PObj_constant_TEST((PObj *)value))
Parrot_ex_throw_from_c_noargs(INTERP,
EXCEPTION_INVALID_OPERATION,
"Used non-constant STRING value in constant hash");
}
Parrot_hash_put(INTERP, hash, Parrot_hash_key_from_string(INTERP, hash, key),
Parrot_hash_value_from_pmc(INTERP, hash, value));
}
/*
=item C<void set_pmc_keyed_int(INTVAL key, PMC *value)>
Sets C<*value> as the value for C<*key>.
=cut
*/
VTABLE void set_pmc_keyed_int(INTVAL key, PMC *value) {
Hash * const hash = (Hash *)SELF.get_pointer();
if (PObj_constant_TEST(SELF)
&& (!PObj_constant_TEST((PObj *)value)))
Parrot_ex_throw_from_c_noargs(INTERP,
EXCEPTION_INVALID_OPERATION,
"Used non-constant PMC value in constant hash");
Parrot_hash_put(INTERP, hash,
Parrot_hash_key_from_int(INTERP, hash, key),
Parrot_hash_value_from_pmc(INTERP, hash, value));
}
/*
=item C<INTVAL exists_keyed_str(STRING *key)>
=cut
*/
VTABLE INTVAL exists_keyed_str(STRING *key) :no_wb {
Hash * const hash = (Hash *)SELF.get_pointer();
HashBucket * const b = Parrot_hash_get_bucket(INTERP, hash,
Parrot_hash_key_from_string(INTERP, hash, key));
return b != NULL;
}
/*
=item C<INTVAL exists_keyed(PMC *key)>
Returns whether a key C<*key> exists in the hash.
=cut
*/
VTABLE INTVAL exists_keyed(PMC *key) :no_wb {
Hash * const h = (Hash *)SELF.get_pointer();
void * const sx = Parrot_hash_key_from_pmc(INTERP, h, key);
HashBucket * const b = Parrot_hash_get_bucket(INTERP, h, sx);
/* no such key */
if (!b)
return 0;
key = Parrot_key_next(INTERP, key);
/* lookup stops here */
if (!key)
return 1;
if (h->entry_type != enum_type_PMC)
entry_type_must_be_pmc(INTERP);
return VTABLE_exists_keyed(INTERP, (PMC *)b->value, key);
}
/*
=item C<INTVAL defined_keyed_str(STRING *key)>
=cut
*/
VTABLE INTVAL defined_keyed_str(STRING *key) :no_wb {
const Hash * const hash = (Hash *)SELF.get_pointer();
HashBucket * const b = Parrot_hash_get_bucket(INTERP, hash,
Parrot_hash_key_from_string(INTERP, hash, key));
/* no such key */
if (!b)
return 0;
return VTABLE_defined(INTERP, Parrot_hash_value_to_pmc(INTERP, hash, b->value));
}
/*
=item C<INTVAL defined_keyed(PMC *key)>
Returns whether the value for C<*key> is defined.
=cut
*/
VTABLE INTVAL defined_keyed(PMC *key) :no_wb {
Hash * const h = (Hash *)SELF.get_pointer();
void * const sx = Parrot_hash_key_from_pmc(INTERP, h, key);
HashBucket * const b = Parrot_hash_get_bucket(INTERP, h, sx);
/* no such key */
if (!b)
return 0;
key = Parrot_key_next(INTERP, key);
if (!key)
return VTABLE_defined(INTERP, Parrot_hash_value_to_pmc(INTERP, h, b->value));
if (h->entry_type != enum_type_PMC)
entry_type_must_be_pmc(INTERP);
return VTABLE_defined_keyed(INTERP, (PMC *)b->value, key);
}
/*
=item C<void delete_keyed_str(STRING *key)>
=cut
*/
VTABLE void delete_keyed_str(STRING *key) {
Hash * const hash = (Hash *)SELF.get_pointer();
Parrot_hash_delete(INTERP, hash, Parrot_hash_key_from_string(INTERP, hash, key));
}
/*
=item C<void delete_keyed_int(INTVAL key)>
=cut
*/
VTABLE void delete_keyed_int(INTVAL. key) {
Hash * const hash = (Hash *)SELF.get_pointer();
Parrot_hash_delete(INTERP, hash, Parrot_hash_key_from_int(INTERP, hash, key));
}
/*
=item C<void delete_keyed(PMC *key)>
Deletes the element associated with C<*key>.
=cut
*/
VTABLE void delete_keyed(PMC *key) {
Hash * const h = (Hash *)SELF.get_pointer();
void * const sx = Parrot_hash_key_from_pmc(INTERP, h, key);
HashBucket * const b = Parrot_hash_get_bucket(INTERP, h, sx);
/* no such key */
if (!b)
return;
key = Parrot_key_next(INTERP, key);
if (!key) {
Parrot_hash_delete(INTERP, h, sx);
return;
}
if (h->entry_type != enum_type_PMC)
entry_type_must_be_pmc(INTERP);
VTABLE_delete_keyed(INTERP, (PMC *)b->value, key);
}
/*
=item C<INTVAL get_bool()>
Returns true if the hash size is not zero.
=cut
*/
VTABLE INTVAL get_bool() :no_wb {
return Parrot_hash_size(INTERP, (Hash *)SELF.get_pointer()) != 0;
}
/*
=item C<INTVAL elements()>
Returns the number of elements in the hash.
=cut
*/
VTABLE INTVAL elements() :no_wb {
return Parrot_hash_size(INTERP, (Hash *)SELF.get_pointer());
}
/*
=item C<PMC *get_iter()>
Return a new iterator.
=cut
*/
VTABLE PMC *get_iter() :no_wb {
return Parrot_pmc_new_init(INTERP, enum_class_HashIterator, SELF);
}
/*
=item C<INTVAL is_same(const PMC *other)>
Returns whether the hash is the same as C<*other>.
=cut
*/
VTABLE INTVAL is_same(PMC *other) :no_wb {
return (INTVAL)(other->vtable == SELF->vtable &&
VTABLE_get_pointer(INTERP, other) == SELF.get_pointer());
}
/*
=item C<INTVAL is_equal(PMC *value)>
The C<==> operation.
Check if two hashes hold the same keys and values.
=cut
*/
VTABLE INTVAL is_equal(PMC *value) :no_wb {
PMC * const iter = VTABLE_get_iter(INTERP, SELF);
INTVAL j, n;
if (value->vtable->base_type != SELF->vtable->base_type)
return 0;
n = SELF.elements();
if (VTABLE_elements(INTERP, value) != n)
return 0;
for (j = 0; j < n; ++j) {
STRING * const key = VTABLE_shift_string(INTERP, iter);
PMC *item1, *item2;
if (!VTABLE_exists_keyed_str(INTERP, value, key))
return 0;
item1 = SELF.get_pmc_keyed_str(key);
item2 = VTABLE_get_pmc_keyed_str(INTERP, value, key);
if (item1 == item2)
continue;
if (!VTABLE_is_equal(INTERP, item1, item2))
return 0;
}
return 1;
}
/*
=item C<void freeze(PMC *info)>
Used to archive the hash.
=cut
*/
VTABLE void freeze(PMC *info) :no_wb {
Parrot_hash_freeze(INTERP, (Hash *)SELF.get_pointer(), info);
}
/*
=item C<void thaw(PMC *info)>
Used to unarchive the hash.
=cut
*/
VTABLE void thaw(PMC *info) {
SELF.set_pointer((void *)Parrot_hash_thaw(INTERP, info));
/* Thawed Hash was created from pmc_new_noinit */
PObj_custom_mark_destroy_SETALL(SELF);
}
/*
=back
=head1 Methods
=over 4
=item C<METHOD set_key_type(INTVAL)>
Set the type of key in the hash.
=item C<METHOD get_key_type()>
Return type of keys in Hash.
=item C<METHOD set_value_type(INTVAL type)>
Reset Hash to use different value-type for stored items. If there is no
previous _hash was set defaults to STRING* keys.
NB: this method will destroy all old data!
=item C<METHOD get_value_type()>
Return type of values in Hash.
=item C<METHOD update(Hash *other)>
Copies all entries from the other hash into the hash, overwriting
entries with the same key.
=cut
*/
METHOD set_key_type(INTVAL type) :manual_wb {
SELF.set_integer_native(type);
}
METHOD get_key_type() :no_wb {
const INTVAL ret = ((Hash *)SELF.get_pointer())->key_type;
RETURN(INTVAL ret);
}
METHOD set_value_type(INTVAL type) {
Hash * const old_hash = (Hash *)SELF.get_pointer();
Hash *new_hash;
/*
If someone called Hash.set_pointer with NULL pointer...
It will create STRING* keys hash.
*/
if (old_hash && old_hash->entry_type == (PARROT_DATA_TYPE)type)
return;
switch (type) {
case enum_type_INTVAL:
case enum_type_STRING:
case enum_type_PMC:
new_hash = Parrot_hash_create(INTERP,
(PARROT_DATA_TYPE)type,
old_hash ? old_hash->key_type : Hash_key_type_STRING);
break;
default:
Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_UNIMPLEMENTED,
"Hash: unsupported entry_type %d", type);
}
PARROT_HASH(SELF)->hash = new_hash;
if (old_hash)
Parrot_hash_destroy(INTERP, old_hash);
}
METHOD get_value_type() :no_wb {
const INTVAL ret = ((Hash *)SELF.get_pointer())->entry_type;
RETURN(INTVAL ret);
}
METHOD update(PMC *other) {
if (other->vtable->base_type == SELF->vtable->base_type
|| other->vtable->base_type == enum_class_Hash) {
Parrot_hash_update(INTERP, (Hash *)SELF.get_pointer(),
(Hash *)VTABLE_get_pointer(INTERP, other));
}
else {
Parrot_ex_throw_from_c_noargs(INTERP, EXCEPTION_UNIMPLEMENTED,
"Hash: don't know how to update from a non-Hash type");
}
}
}
/*
=back
=head1 Auxiliar functions
=over 4
=item C<static PMC* get_next_hash(PARROT_INTERP, const Hash *hash, const void
*key)>
Get the next hash for multipart keys. Autovivify a hash if it doesn't exist.
=cut
*/
PARROT_CANNOT_RETURN_NULL
PARROT_WARN_UNUSED_RESULT
static PMC*
get_next_hash(PARROT_INTERP, ARGIN(const Hash *hash), ARGIN(const void *key))
{
ASSERT_ARGS(get_next_hash)
HashBucket *bucket;
if (hash->entry_type != enum_type_PMC)
entry_type_must_be_pmc(interp);
bucket = Parrot_hash_get_bucket(interp, hash, key);
if (bucket)
return (PMC *)bucket->value;
else
return PMCNULL;
}
/*
=item C<static void entry_type_must_be_pmc(PARROT_INTERP)>
=item C<static void cannot_autovivify_nested(PARROT_INTERP)>
Throw the appropriate exception.
=cut
*/
PARROT_DOES_NOT_RETURN
static void
entry_type_must_be_pmc(PARROT_INTERP)
{
ASSERT_ARGS(entry_type_must_be_pmc)
Parrot_ex_throw_from_c_noargs(interp,
EXCEPTION_INVALID_OPERATION,
"Hash entry type must be PMC for multipart keys");
}
PARROT_DOES_NOT_RETURN
static void
cannot_autovivify_nested(PARROT_INTERP)
{
ASSERT_ARGS(cannot_autovivify_nested)
Parrot_ex_throw_from_c_noargs(interp,
EXCEPTION_INVALID_OPERATION,
"Cannot autovivify nested hashes");
}
/*
=back
=head1 SEE ALSO
F<docs/pdds/pdd08_keys.pod>.
=cut
*/
/*
* Local variables:
* c-file-style: "parrot"
* End:
* vim: expandtab shiftwidth=4 cinoptions='\:2=2' :
*/