fix codetest failure - ASSERT_ARGS does not have a ; after and
[parrot.git] / src / pmc / hash.pmc
blob3e01bba0807c80c115792a0b74f4a0893a076f80
1 /*
2 Copyright (C) 2001-2010, Parrot Foundation.
3 $Id$
5 =head1 NAME
7 src/pmc/hash.pmc - Hash PMC
9 =head1 DESCRIPTION
11 Hash PMC wraps Parrot's _hash to provide a high-level API:
13 =over 4
15 =item *
17 Convert between various types to use as hash keys.
19 =item *
21 Convert between various types to use as hash values.
23 =item *
25 Handle compound Keys for nested Hash/Array lookups.
27 =item *
29 Provide C<HashIterator> to iterate over C<Hash>.
31 =back
33 By default Hash uses string keys and PMC values. Methods C<set_key_type> and
34 C<set_value_type> may be used to switch key and values type. For C<PMC> keys
35 hash value is calculated using VTABLE C<get_hashvalue> function.
37 These are the vtable functions for the Hash PMC.
39 =head2 Functions
41 =over 4
43 =cut
47 #include "pmc/pmc_iterator.h"
48 #include "pmc/pmc_key.h"
49 #include "pmc/pmc_hashiteratorkey.h"
51 /* HEADERIZER HFILE: none */
52 /* HEADERIZER BEGIN: static */
53 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
55 PARROT_CANNOT_RETURN_NULL
56 PARROT_WARN_UNUSED_RESULT
57 static PMC* get_next_hash(PARROT_INTERP,
58     ARGMOD(Hash *hash),
59     ARGIN(void *key))
60         __attribute__nonnull__(1)
61         __attribute__nonnull__(2)
62         __attribute__nonnull__(3)
63         FUNC_MODIFIES(*hash);
65 #define ASSERT_ARGS_get_next_hash __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
66        PARROT_ASSERT_ARG(interp) \
67     , PARROT_ASSERT_ARG(hash) \
68     , PARROT_ASSERT_ARG(key))
69 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
70 /* HEADERIZER END: static */
74 =item C<static PMC* get_next_hash(PARROT_INTERP, Hash *hash, void *key)>
76 Get the next hash for multipart keys. Autovivify a hash if it doesn't exist.
78 =cut
82 PARROT_CANNOT_RETURN_NULL
83 PARROT_WARN_UNUSED_RESULT
84 static PMC*
85 get_next_hash(PARROT_INTERP, ARGMOD(Hash *hash), ARGIN(void *key))
87     ASSERT_ARGS(get_next_hash)
88     PMC        *next_hash;
89     HashBucket *bucket;
91     if (hash->entry_type != enum_type_PMC)
92         Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
93             "Hash entry type must be PMC for multipart keys.");
95     bucket = parrot_hash_get_bucket(interp, hash, key);
97     if (bucket) {
98         next_hash = (PMC *)bucket->value;
99     }
100     else {
101         /* autovivify a Hash */
102         next_hash = Parrot_pmc_new(interp, enum_class_Hash);
103         parrot_hash_put(interp, hash, key, next_hash);
104     }
106     return next_hash;
109 pmclass Hash provides hash auto_attrs {
110     ATTR Hash *hash;
114 =item C<void init()>
116 Initializes the instance.
118 =item C<void init_int(INTVAL value_type)>
120 Initializes the instance with the value_type provided.
122 =item C<void destroy()>
124 Free hash structure.
126 =cut
130     VTABLE void init() {
131         Parrot_Hash_attributes * const attr =
132             (Parrot_Hash_attributes *) PMC_data(SELF);
134         attr->hash            = parrot_new_hash(INTERP);
135         PObj_custom_mark_destroy_SETALL(SELF);
136     }
138     VTABLE void init_int(INTVAL value_type) {
139         Parrot_Hash_attributes * const attr =
140             (Parrot_Hash_attributes *) PMC_data(SELF);
142         attr->hash = parrot_create_hash(INTERP,
143                 (PARROT_DATA_TYPE)value_type,
144                 Hash_key_type_STRING);
145         PObj_custom_mark_destroy_SETALL(SELF);
146     }
148     VTABLE void destroy() {
149         Hash * const hash = (Hash *)SELF.get_pointer();
150         if (hash)
151             parrot_hash_destroy(INTERP, hash);
152     }
156 =item C<void mark()>
158 Marks the hash as live.
160 =cut
164     VTABLE void mark() {
165         Hash * const hash = (Hash *)SELF.get_pointer();
166         if (hash && hash->entries)
167             parrot_mark_hash(INTERP, hash);
168     }
172 =item C<PMC *clone()>
174 Creates and returns a clone of the hash.
176 =cut
180     VTABLE PMC *clone() {
181         PMC * const dest = Parrot_pmc_new(INTERP, SELF->vtable->base_type);
183         parrot_hash_clone(INTERP, (Hash *)SELF.get_pointer(),
184                    (Hash *)VTABLE_get_pointer(INTERP, dest));
186         return dest;
187     }
191 =item C<void set_pointer(void *ptr)>
193 Use C<ptr> as this PMC's Hash*.
195 =cut
199     VTABLE void set_pointer(void *ptr) {
200         Hash * const old_hash = (Hash *)SELF.get_pointer();
201         Hash * const new_hash = (Hash *)ptr;
203         PARROT_HASH(SELF)->hash = new_hash;
205         if (old_hash)
206             parrot_hash_destroy(INTERP, old_hash);
207     }
212 =item C<void set_integer(INTVAL type)>
214 =item C<void set_key_type(INTVAL type)>
216 Reset Hash to use different keys. See enum C<Hash_key_type> for possible
217 values.
219 NB: this method will destroy all old data!
221 =cut
224     VTABLE void set_integer_native(INTVAL type) {
225         Hash            *new_hash;
226         Hash * const     old_hash   = PARROT_HASH(SELF)->hash;
227         PARROT_DATA_TYPE entry_type = old_hash
228                                     ? old_hash->entry_type
229                                     : enum_type_PMC;
231         if (type == Hash_key_type_STRING)
232             new_hash = parrot_create_hash(INTERP,
233                     entry_type,
234                     Hash_key_type_STRING);
235         else if (type == Hash_key_type_int)
236             /* new_int_hash set BOTH keys and values to INTVAL */
237             new_hash = parrot_create_hash(INTERP,
238                     entry_type,
239                     Hash_key_type_int);
240         else if (type == Hash_key_type_PMC)
241             /* new_int_hash set BOTH keys and values to INTVAL */
242             new_hash = parrot_create_hash(INTERP,
243                     entry_type,
244                     Hash_key_type_PMC);
245         else
246             /* We probably will not implement other types of keys. They are way
247              * too dangerous to use from PIR */
248             Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_UNIMPLEMENTED,
249                 "Hash: Unknown key type");
252         PARROT_HASH(SELF)->hash = new_hash;
254         if (old_hash)
255             parrot_hash_destroy(INTERP, old_hash);
256     }
258     METHOD set_key_type(INTVAL type) {
259         SELF.set_integer_native(type);
260     }
264 =item C<METHOD get_key_type()>
266 Return type of keys in Hash.
268 =cut
271     METHOD get_key_type() {
272         const INTVAL ret = ((Hash *)SELF.get_pointer())->key_type;
273         RETURN(INTVAL ret);
274     }
278 =item C<METHOD set_value_type(INTVAL type)>
280 Reset Hash to use different value-type for stored items. If there is no
281 previous _hash was set defaults to STRING* keys.
283 NB: this method will destroy all old data!
285 =cut
287     METHOD set_value_type(INTVAL type) {
288         Hash *old_hash = (Hash *)SELF.get_pointer();
289         Hash *new_hash;
291         /*
292         If someone called Hash.set_pointer with NULL pointer...
293         It will create STRING* keys hash.
294         */
296         if (old_hash && old_hash->entry_type == type)
297             return;
299         switch (type) {
300           case enum_type_INTVAL:
301           case enum_type_STRING:
302           case enum_type_PMC:
303             new_hash = parrot_create_hash(INTERP,
304                         (PARROT_DATA_TYPE)type,
305                         old_hash ? old_hash->key_type : Hash_key_type_STRING);
306             break;
307           default:
308             Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_UNIMPLEMENTED,
309                         "Hash: unsupported entry_type %d", type);
310         }
312         PARROT_HASH(SELF)->hash = new_hash;
314         if (old_hash)
315             parrot_hash_destroy(INTERP, old_hash);
316     }
318     METHOD get_value_type() {
319         INTVAL ret = ((Hash *)SELF.get_pointer())->entry_type;
320         RETURN(INTVAL ret);
321     }
325 =item C<void *get_pointer()>
327 Get a pointer to this PMC's Hash*.
329 =cut
332     VTABLE void *get_pointer() {
333         return PARROT_HASH(SELF)->hash;
334     }
338 =item C<INTVAL get_integer()>
340 =item C<FLOATVAL get_number()>
342 Returns the size of the hash.
344 =cut
348     VTABLE INTVAL get_integer() {
349         return parrot_hash_size(INTERP, (Hash *)SELF.get_pointer());
350     }
352     VTABLE FLOATVAL get_number() {
353         return SELF.get_integer();
354     }
358 =item C<STRING *get_string()>
360 Returns a string representation of the hash, showing its class name and
361 memory address.
363 =item  C<STRING *get_repr()>
365 Return a representation of the hash contents.
367 =cut
371     VTABLE STRING *get_string() {
372         return Parrot_sprintf_c(INTERP, "Hash[0x%x]", SELF);
373     }
375     VTABLE STRING *get_repr() {
376         /* TT #1231:  Use freeze in get_repr() (for hashes) */
377         PMC * const  iter = VTABLE_get_iter(INTERP, SELF);
378         STRING      *res  = CONST_STRING(INTERP, "{");
379         const INTVAL n    = VTABLE_elements(INTERP, SELF);
380         INTVAL       j;
382         for (j = 0; j < n; ++j) {
383             STRING * const key      = VTABLE_shift_string(INTERP, iter);
384             char *   const key_str  = Parrot_str_to_cstring(INTERP, key);
385             const size_t   str_len  = strlen(key_str);
386             size_t         i;
387             int            all_digit = 1;
388             PMC           *val;
390             for (i = 0; i < str_len; ++i) {
391                 if (!isdigit((unsigned char)key_str[i])) {
392                     all_digit = 0;
393                     break;
394                 }
395             }
397             Parrot_str_free_cstring(key_str);
399             if (all_digit) {
400                 res = Parrot_str_concat(INTERP, res, key);
401             }
402             else {
403                 res = Parrot_str_concat(INTERP, res, CONST_STRING(INTERP, "'"));
404                 res = Parrot_str_concat(INTERP, res, key);
405                 res = Parrot_str_concat(INTERP, res, CONST_STRING(INTERP, "'"));
406             }
408             res = Parrot_str_concat(INTERP, res, CONST_STRING(INTERP, ": "));
409             val = SELF.get_pmc_keyed_str(key);
410             res = Parrot_str_concat(INTERP, res, VTABLE_get_string(INTERP, val));
412             if (j < n - 1)
413                 res = Parrot_str_concat(INTERP, res, CONST_STRING(INTERP, ", "));
414         }
416         res = Parrot_str_concat(INTERP, res, CONST_STRING(INTERP, "}"));
418         return res;
419     }
423 =item C<INTVAL get_integer_keyed_str(STRING *key)>
425 =item C<INTVAL get_integer_keyed_int(INTVAL key)>
427 =cut
431     VTABLE INTVAL get_integer_keyed_str(STRING *key) {
432         const Hash * const hash = (Hash*)SELF.get_pointer();
433         HashBucket * const b = parrot_hash_get_bucket(INTERP, hash,
434                 hash_key_from_string(INTERP, hash, key));
436         if (!b)
437             return 0;
439         return hash_value_to_int(INTERP, hash, b->value);
440     }
442     VTABLE INTVAL get_integer_keyed_int(INTVAL key) {
443         const Hash * const hash = (Hash*)SELF.get_pointer();
444         HashBucket * const b = parrot_hash_get_bucket(INTERP, hash,
445                 hash_key_from_int(INTERP, hash, key));
447         if (!b)
448             return 0;
450         return hash_value_to_int(INTERP, hash, b->value);
451     }
454 =item C<INTVAL get_integer_keyed(PMC *key)>
456 Returns the integer value for the element at C<*key>.
458 =cut
462     /* Handling Keys */
463     VTABLE INTVAL get_integer_keyed(PMC *key) {
464         const Hash * const hash     = (Hash *)SELF.get_pointer();
465         const void * const hash_key = hash_key_from_pmc(INTERP, hash, key);
466         HashBucket * const b        = parrot_hash_get_bucket(INTERP, hash, hash_key);
468         if (!b)
469             return 0;
471         key = key_next(INTERP, key);
473         /* Stop recursion. This is last step */
474         if (!key)
475             return hash_value_to_int(INTERP, hash, b->value);
477         if (hash->entry_type != enum_type_PMC)
478             Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
479                 "Hash entry type must be PMC for multipart keys.");
481         /* Recursively call to enclosed aggregate */
482         return VTABLE_get_integer_keyed(INTERP, (PMC *)b->value, key);
483     }
487 =item C<void set_integer_keyed(PMC *key, INTVAL value)>
489 =cut
493     VTABLE void set_integer_keyed(PMC *key, INTVAL value) {
494         Hash * const hash     = (Hash *)SELF.get_pointer();
495         void * const hash_key = hash_key_from_pmc(INTERP, hash, key);
497         if (PObj_constant_TEST(SELF)
498         && !PObj_constant_TEST((PObj *)key))
499             Parrot_ex_throw_from_c_args(INTERP, NULL,
500                 EXCEPTION_INVALID_OPERATION,
501                 "Used non-constant PMC key in constant hash.");
503         key = key_next(INTERP, key);
505         if (!key) {
506             parrot_hash_put(INTERP, hash, hash_key,
507                     hash_value_from_int(INTERP, hash, value));
508         }
509         else {
510             PMC * const next_hash = get_next_hash(INTERP, hash, hash_key);
511             VTABLE_set_integer_keyed(INTERP, next_hash, key, value);
512         }
513     }
515     VTABLE void set_integer_keyed_int(INTVAL key, INTVAL value) {
516         Hash * const hash = (Hash *)SELF.get_pointer();
517         parrot_hash_put(INTERP, hash, hash_key_from_int(INTERP, hash, key),
518                 hash_value_from_int(INTERP, hash, value));
519     }
523 =item C<void set_integer_keyed_str(STRING *key, INTVAL value)>
525 =cut
529     VTABLE void set_integer_keyed_str(STRING *key, INTVAL value) {
530         Hash * const hash = (Hash *)SELF.get_pointer();
532         if (PObj_constant_TEST(SELF)
533         && !PObj_constant_TEST((PObj *)key))
534             Parrot_ex_throw_from_c_args(INTERP, NULL,
535                 EXCEPTION_INVALID_OPERATION,
536                 "Used non-constant key in constant hash.");
538         parrot_hash_put(INTERP, hash, hash_key_from_string(INTERP, hash, key),
539                 hash_value_from_int(INTERP, hash, value));
540     }
545 =item C<FLOATVAL get_number_keyed_str(STRING *key)>
547 =item C<FLOATVAL get_number_keyed_int(INTVAL key)>
549 =cut
553     VTABLE FLOATVAL get_number_keyed_str(STRING *key) {
554         const Hash * const hash = (Hash *)SELF.get_pointer();
555         HashBucket * const b = parrot_hash_get_bucket(INTERP, hash,
556                 hash_key_from_string(INTERP, hash, key));
558         if (!b)
559             return 0.0;
561         return hash_value_to_number(INTERP, hash, b->value);
562     }
564     VTABLE FLOATVAL get_number_keyed_int(INTVAL key) {
565         const Hash * const hash = (Hash *)SELF.get_pointer();
566         HashBucket * const b = parrot_hash_get_bucket(INTERP, hash,
567                 hash_key_from_int(INTERP, hash, key));
569         if (!b)
570             return 0.0;
572         return hash_value_to_number(INTERP, hash, b->value);
573     }
576 =item C<FLOATVAL get_number_keyed(PMC *key)>
578 Returns the floating-point value for the element at C<*key>.
580 =cut
584     /* I can't migrate this function right now. Some problem with JITting */
585     VTABLE FLOATVAL get_number_keyed(PMC *key) {
586         const Hash * const hash     = (Hash *)SELF.get_pointer();
587         void       * const hash_key = hash_key_from_pmc(INTERP, hash, key);
588         HashBucket * const b        = parrot_hash_get_bucket(INTERP, hash, hash_key);
590         if (!b)
591             return 0.0;
593         key = key_next(INTERP, key);
595         if (!key)
596             return hash_value_to_number(INTERP, hash, b->value);
598         if (hash->entry_type != enum_type_PMC)
599             Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
600                 "Hash entry type must be PMC for multipart keys.");
602         return VTABLE_get_number_keyed(INTERP, (PMC *)b->value, key);
603     }
608 =item C<STRING *get_string_keyed_str(STRING *key)>
610 =item C<STRING *get_string_keyed_int(INTVAL key)>
612 =cut
616     VTABLE STRING *get_string_keyed_str(STRING *key) {
617         const Hash * const hash = (Hash*)SELF.get_pointer();
618         HashBucket * const b =
619             parrot_hash_get_bucket(INTERP, hash, hash_key_from_string(INTERP, hash, key));
621         /* XXX: shouldn't we return STRINGNULL? */
622         if (!b)
623             return CONST_STRING(INTERP, "");
625         return hash_value_to_string(INTERP, hash, b->value);
626     }
628     VTABLE STRING *get_string_keyed_int(INTVAL key) {
629         const Hash * const hash = (Hash*)SELF.get_pointer();
630         HashBucket * const b =
631             parrot_hash_get_bucket(INTERP, hash, hash_key_from_int(INTERP, hash, key));
633         if (!b)
634             return CONST_STRING(INTERP, "");
636         return hash_value_to_string(INTERP, hash, b->value);
637     }
641 =item C<STRING *get_string_keyed(PMC *key)>
643 Returns the string value for the element at C<*key>.
645 =cut
649     VTABLE STRING *get_string_keyed(PMC *key) {
650         const Hash * const hash     = (Hash *)SELF.get_pointer();
651         const void * const hash_key = hash_key_from_pmc(INTERP, hash, key);
652         HashBucket * const b        = parrot_hash_get_bucket(INTERP, hash, hash_key);
654         if (!b)
655             return CONST_STRING(INTERP, "");
657         key = key_next(INTERP, key);
659         /* Stop recursion. This is last step */
660         if (!key)
661             return hash_value_to_string(INTERP, hash, b->value);
663         if (hash->entry_type != enum_type_PMC)
664             Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
665                 "Hash entry type must be PMC for multipart keys.");
667         /* Recursively call to enclosed aggregate */
668         return VTABLE_get_string_keyed(INTERP, (PMC *)b->value, key);
669     }
673 =item C<void set_string_keyed(PMC *key, STRING *value)>
675 =cut
679     VTABLE void set_string_keyed(PMC *key, STRING *value) {
680         Hash * const hash     = (Hash *)SELF.get_pointer();
681         void * const hash_key = hash_key_from_pmc(INTERP, hash, key);
683         if (PObj_constant_TEST(SELF)){
684             if (!PObj_constant_TEST((PObj *)key))
685                 Parrot_ex_throw_from_c_args(INTERP, NULL,
686                     EXCEPTION_INVALID_OPERATION,
687                     "Used non-constant PMC key in constant hash.");
688             if (!PObj_constant_TEST((PObj *)value))
689                 Parrot_ex_throw_from_c_args(INTERP, NULL,
690                     EXCEPTION_INVALID_OPERATION,
691                     "Used non-constant STRING value in constant hash.");
692         }
694         key = key_next(INTERP, key);
696         if (!key) {
697             parrot_hash_put(INTERP, hash, hash_key,
698                     hash_value_from_string(INTERP, hash, value));
699         }
700         else {
701             PMC * const next_hash = get_next_hash(INTERP, hash, hash_key);
702             VTABLE_set_string_keyed(INTERP, next_hash, key, value);
703         }
704     }
708 =item C<void set_string_keyed_str(STRING *key, STRING *value)>
710 =cut
714     VTABLE void set_string_keyed_str(STRING *key, STRING *value) {
715         Hash * const hash = (Hash *)SELF.get_pointer();
717         if (PObj_constant_TEST(SELF)){
718             if (!PObj_constant_TEST((PObj *)key))
719                 Parrot_ex_throw_from_c_args(INTERP, NULL,
720                     EXCEPTION_INVALID_OPERATION,
721                     "Used non-constant STRING key in constant hash.");
722             if (!PObj_constant_TEST((PObj *)value))
723                 Parrot_ex_throw_from_c_args(INTERP, NULL,
724                     EXCEPTION_INVALID_OPERATION,
725                     "Used non-constant STRING value in constant hash.");
726         }
728         parrot_hash_put(INTERP, hash, hash_key_from_string(INTERP, hash, key),
729                 hash_value_from_string(INTERP, hash, value));
730     }
732     VTABLE void set_string_keyed_int(INTVAL key, STRING *value) {
733         Hash * const hash = (Hash *)SELF.get_pointer();
735         if ((PObj_constant_TEST(SELF))
736         && (!PObj_constant_TEST((PObj *)value)))
737             Parrot_ex_throw_from_c_args(INTERP, NULL,
738                 EXCEPTION_INVALID_OPERATION,
739                 "Used non-constant STRING value in constant hash.");
741         parrot_hash_put(INTERP, hash,
742                 hash_key_from_int(INTERP, hash, key),
743                 hash_value_from_string(INTERP, hash, value));
744     }
748 =item C<PMC *get_pmc_keyed(PMC *key)>
750 =item C<PMC *get_pmc_keyed_str(STRING *key)>
752 =item C<PMC *get_pmc_keyed_int(INTVAL key)>
754 Returns the PMC value for the element at C<*key>.
756 =cut
760     VTABLE PMC *get_pmc_keyed_str(STRING *key) {
761         const Hash *hash;
762         HashBucket *b;
764         GET_ATTR_hash(INTERP, SELF, hash);
766         /* special case the most likely key type, for speed */
767         if (hash->key_type != Hash_key_type_STRING)
768             key = (STRING *)hash_key_from_string(INTERP, hash, key);
770         b = parrot_hash_get_bucket(INTERP, hash, key);
772         if (!b)
773             return PMCNULL;
775         /* special case the most likely value type, for speed */
776         if (hash->entry_type == enum_type_PMC)
777             return (PMC *)b->value;
778         else
779             return hash_value_to_pmc(INTERP, hash, b->value);
780     }
782     VTABLE PMC *get_pmc_keyed_int(INTVAL key) {
783         const Hash * const hash = (Hash *)SELF.get_pointer();
784         HashBucket * const b    = parrot_hash_get_bucket(INTERP, hash,
785                 hash_key_from_int(INTERP, hash, key));
787         if (!b)
788             return PMCNULL;
790         return hash_value_to_pmc(INTERP, hash, b->value);
791     }
793     /* Compound Key */
794     VTABLE PMC *get_pmc_keyed(PMC *key) {
795         const Hash * const hash     = (Hash *)SELF.get_pointer();
796         const void * const hash_key = hash_key_from_pmc(INTERP, hash, key);
797         HashBucket * const b        = parrot_hash_get_bucket(INTERP, hash, hash_key);
799         if (!b)
800             return PMCNULL;
802         key = key_next(INTERP, key);
804         /* Stop recursion. This is last step */
805         if (!key)
806             return hash_value_to_pmc(INTERP, hash, b->value);
808         if (hash->entry_type != enum_type_PMC)
809             Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
810                 "Hash entry type must be PMC for multipart keys.");
812         /* Recursively call to enclosed aggregate */
813         return VTABLE_get_pmc_keyed(INTERP, (PMC *)b->value, key);
814     }
818 =item C<void set_number_keyed(PMC *key, FLOATVAL value)>
820 =cut
824     VTABLE void set_number_keyed(PMC *key, FLOATVAL value) {
825         Hash * const hash     = (Hash *)SELF.get_pointer();
826         void * const hash_key = hash_key_from_pmc(INTERP, hash, key);
828         if (PObj_constant_TEST(SELF)
829         && !PObj_constant_TEST((PObj *)key))
830             Parrot_ex_throw_from_c_args(INTERP, NULL,
831                 EXCEPTION_INVALID_OPERATION,
832                 "Used non-constant PMC key in constant hash.");
834         key = key_next(INTERP, key);
836         if (!key) {
837             parrot_hash_put(INTERP, hash, hash_key,
838                     hash_value_from_number(INTERP, hash, value));
839         }
840         else {
841             PMC * const next_hash = get_next_hash(INTERP, hash, hash_key);
842             VTABLE_set_number_keyed(INTERP, next_hash, key, value);
843         }
844     }
848 =item C<void set_number_keyed_str(STRING *key, FLOATVAL value)>
850 Sets C<value> as the value for C<*key>.
852 =cut
856     VTABLE void set_number_keyed_str(STRING *key, FLOATVAL value) {
857         Hash * const hash = (Hash *)SELF.get_pointer();
859         if (PObj_constant_TEST(SELF)
860         && !PObj_constant_TEST((PObj *)key))
861             Parrot_ex_throw_from_c_args(INTERP, NULL,
862                 EXCEPTION_INVALID_OPERATION,
863                 "Used non-constant STRING key in constant hash.");
865         parrot_hash_put(INTERP, hash, hash_key_from_string(INTERP, hash, key),
866                 hash_value_from_number(INTERP, hash, value));
867     }
871 =item C<void set_pmc_keyed(PMC *dest_key, PMC *value)>
873 =cut
877     VTABLE void set_pmc_keyed(PMC *key, PMC *value) {
878         Hash * const hash     = (Hash *)SELF.get_pointer();
879         void * const hash_key = hash_key_from_pmc(INTERP, hash, key);
881         if (PObj_constant_TEST(SELF)) {
882             if (!PObj_constant_TEST((PObj *)key))
883                 Parrot_ex_throw_from_c_args(INTERP, NULL,
884                     EXCEPTION_INVALID_OPERATION,
885                     "Used non-constant PMC key in constant hash.");
887             if (!PObj_constant_TEST((PObj *)value))
888                 Parrot_ex_throw_from_c_args(INTERP, NULL,
889                     EXCEPTION_INVALID_OPERATION,
890                     "Used non-constant PMC value in constant hash.");
891         }
893         key = key_next(INTERP, key);
895         if (!key) {
896             parrot_hash_put(INTERP, hash, hash_key, value);
897         }
898         else {
899             PMC * const next_hash = get_next_hash(INTERP, hash, hash_key);
900             VTABLE_set_pmc_keyed(INTERP, next_hash, key, value);
901         }
902     }
906 =item C<void set_pmc_keyed_str(STRING *key, PMC *value)>
908 Sets C<*value> as the value for C<*key>.
910 =cut
914     VTABLE void set_pmc_keyed_str(STRING *key, PMC *value) {
915         Hash * const hash = (Hash *)SELF.get_pointer();
917         if (PObj_constant_TEST(SELF)) {
918             if (!PObj_constant_TEST((PObj *)key))
919                 Parrot_ex_throw_from_c_args(INTERP, NULL,
920                     EXCEPTION_INVALID_OPERATION,
921                     "Used non-constant STRING key in constant hash.");
923             if (!PObj_constant_TEST((PObj *)value))
924                 Parrot_ex_throw_from_c_args(INTERP, NULL,
925                     EXCEPTION_INVALID_OPERATION,
926                     "Used non-constant STRING value in constant hash.");
927         }
929         parrot_hash_put(INTERP, hash, hash_key_from_string(INTERP, hash, key),
930                 hash_value_from_pmc(INTERP, hash, value));
931     }
935 =item C<INTVAL exists_keyed_str(STRING *key)>
937 =cut
941     VTABLE INTVAL exists_keyed_str(STRING *key) {
942         Hash * const hash = (Hash *)SELF.get_pointer();
943         HashBucket * const b = parrot_hash_get_bucket(INTERP, hash,
944                 hash_key_from_string(INTERP, hash, key));
945         return b != NULL;
946     }
950 =item C<INTVAL exists_keyed(PMC *key)>
952 Returns whether a key C<*key> exists in the hash.
954 =cut
958     VTABLE INTVAL exists_keyed(PMC *key) {
959         Hash       * const h  = (Hash *)SELF.get_pointer();
960         void       * const sx = hash_key_from_pmc(INTERP, h, key);
961         HashBucket * const b  = parrot_hash_get_bucket(INTERP, h, sx);
963         /* no such key */
964         if (!b)
965             return 0;
967         key = key_next(INTERP, key);
969         /* lookup stops here */
970         if (!key)
971             return 1;
973         if (h->entry_type != enum_type_PMC)
974             Parrot_ex_throw_from_c_args(INTERP, NULL,
975                 EXCEPTION_INVALID_OPERATION,
976                 "Hash entry type must be PMC for multipart keys.");
978         return VTABLE_exists_keyed(INTERP, (PMC *)b->value, key);
979     }
983 =item C<INTVAL defined_keyed_str(STRING *key)>
985 =cut
989     VTABLE INTVAL defined_keyed_str(STRING *key) {
990         const Hash * const hash = (Hash *)SELF.get_pointer();
991         HashBucket * const b = parrot_hash_get_bucket(INTERP, hash,
992                 hash_key_from_string(INTERP, hash, key));
994         /* no such key */
995         if (!b)
996             return 0;
998         return VTABLE_defined(INTERP, hash_value_to_pmc(INTERP, hash, b->value));
999     }
1003 =item C<INTVAL defined_keyed(PMC *key)>
1005 Returns whether the value for C<*key> is defined.
1007 =cut
1011     VTABLE INTVAL defined_keyed(PMC *key) {
1012         Hash       * const h  = (Hash *)SELF.get_pointer();
1013         void       * const sx = hash_key_from_pmc(INTERP, h, key);
1014         HashBucket * const b  = parrot_hash_get_bucket(INTERP, h, sx);
1016         /* no such key */
1017         if (!b)
1018             return 0;
1020         key = key_next(INTERP, key);
1022         if (!key)
1023             return VTABLE_defined(INTERP, hash_value_to_pmc(INTERP, h, b->value));
1025         if (h->entry_type != enum_type_PMC)
1026             Parrot_ex_throw_from_c_args(INTERP, NULL,
1027                 EXCEPTION_INVALID_OPERATION,
1028                 "Hash entry type must be PMC for multipart keys.");
1030         return VTABLE_defined_keyed(INTERP, (PMC *)b->value, key);
1031     }
1035 =item C<void delete_keyed_str(STRING *key)>
1037 =cut
1041     VTABLE void delete_keyed_str(STRING *key) {
1042         Hash * const hash = (Hash *)SELF.get_pointer();
1043         parrot_hash_delete(INTERP, hash, hash_key_from_string(INTERP, hash, key));
1044     }
1048 =item C<void delete_keyed(PMC *key)>
1050 Deletes the element associated with C<*key>.
1052 =cut
1056     VTABLE void delete_keyed(PMC *key) {
1057         Hash       * const h  = (Hash *)SELF.get_pointer();
1058         void       * const sx = hash_key_from_pmc(INTERP, h, key);
1059         HashBucket * const b  = parrot_hash_get_bucket(INTERP, h, sx);
1061         /* no such key */
1062         if (!b)
1063             return;
1065         key = key_next(INTERP, key);
1067         if (!key) {
1068             parrot_hash_delete(INTERP, h, sx);
1069             return;
1070         }
1072         if (h->entry_type != enum_type_PMC)
1073             Parrot_ex_throw_from_c_args(INTERP, NULL,
1074                 EXCEPTION_INVALID_OPERATION,
1075                 "Hash entry type must be PMC for multipart keys.");
1077         VTABLE_delete_keyed(INTERP, (PMC *)b->value, key);
1078     }
1082 =item C<INTVAL get_bool()>
1084 Returns true if the hash size is not zero.
1086 =cut
1090     VTABLE INTVAL get_bool() {
1091         return parrot_hash_size(INTERP, (Hash *)SELF.get_pointer()) != 0;
1092     }
1096 =item C<INTVAL elements()>
1098 Returns the number of elements in the hash.
1100 =cut
1104     VTABLE INTVAL elements() {
1105         return parrot_hash_size(INTERP, (Hash *)SELF.get_pointer());
1106     }
1110 =item C<PMC *get_iter()>
1112 Return a new iterator.
1114 =cut
1118     VTABLE PMC *get_iter() {
1119         return Parrot_pmc_new_init(INTERP, enum_class_HashIterator, SELF);
1120     }
1124 =item C<INTVAL is_same(const PMC *other)>
1126 Returns whether the hash is the same as C<*other>.
1128 =cut
1132     VTABLE INTVAL is_same(PMC *other) {
1133         return (INTVAL)(other->vtable == SELF->vtable &&
1134             VTABLE_get_pointer(INTERP, other) == SELF.get_pointer());
1135     }
1139 =item C<INTVAL is_equal(PMC *value)>
1141 The C<==> operation.
1143 Check if two hashes hold the same keys and values.
1145 =cut
1149     VTABLE INTVAL is_equal(PMC *value) {
1150         PMC * const iter = VTABLE_get_iter(INTERP, SELF);
1151         INTVAL      j, n;
1153         if (value->vtable->base_type != SELF->vtable->base_type)
1154             return 0;
1156         n = SELF.elements();
1158         if (VTABLE_elements(INTERP, value) != n)
1159             return 0;
1161         for (j = 0; j < n; ++j) {
1162             STRING * const key = VTABLE_shift_string(INTERP, iter);
1163             PMC           *item1, *item2;
1165             if (!VTABLE_exists_keyed_str(INTERP, value, key))
1166                 return 0;
1168             item1 = SELF.get_pmc_keyed_str(key);
1169             item2 = VTABLE_get_pmc_keyed_str(INTERP, value, key);
1171             if (item1 == item2)
1172                 continue;
1174             if (!VTABLE_is_equal(INTERP, item1, item2))
1175                 return 0;
1176         }
1178         return 1;
1179     }
1183 =item C<void freeze(PMC *info)>
1185 Used to archive the hash.
1187 =cut
1191     VTABLE void freeze(PMC *info) {
1192         SUPER(info);
1193         Parrot_hash_freeze(INTERP, (Hash *)SELF.get_pointer(), info);
1194     }
1198 =item C<void thaw(PMC *info)>
1200 Used to unarchive the hash.
1202 =cut
1206     VTABLE void thaw(PMC *info) {
1207         SUPER(info);
1208         SELF.set_pointer((void *)Parrot_hash_thaw(INTERP, info));
1209     }
1214 =back
1216 =head1 SEE ALSO
1218 F<docs/pdds/pdd08_keys.pod>.
1220 =cut
1225  * Local variables:
1226  *   c-file-style: "parrot"
1227  * End:
1228  * vim: expandtab shiftwidth=4:
1229  */