remove deprecation notice for TT #449
[parrot.git] / src / hash.c
blob3527b00591754300d92e94d6990fe7cfb0231594
1 /*
2 Copyright (C) 2001-2010, Parrot Foundation.
3 $Id$
5 =head1 NAME
7 src/hash.c - Hash table
9 =head1 DESCRIPTION
11 A hashtable contains an array of bucket indexes. Buckets are nodes in a
12 linked list, each containing a C<void *> key and value. During hash
13 creation, the types of key and value as well as appropriate compare and
14 hashing functions can be set.
16 This hash implementation uses just one piece of malloced memory. The
17 C<< hash->bs >> bucket store points to this region.
19 This hash doesn't move during GC, therefore a lot of the old caveats
20 don't apply.
22 =head2 Functions
24 =over 4
26 =cut
30 #include "parrot/parrot.h"
31 #include "pmc/pmc_key.h"
33 /* the number of entries above which it's faster to hash the hashval instead of
34 * looping over the used HashBuckets directly */
35 #define SMALL_HASH_SIZE 4
36 #define INITIAL_BUCKETS 4
38 /* HEADERIZER HFILE: include/parrot/hash.h */
40 /* HEADERIZER BEGIN: static */
41 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
43 PARROT_WARN_UNUSED_RESULT
44 PARROT_PURE_FUNCTION
45 static int cstring_compare(SHIM_INTERP,
46 ARGIN(const char *a),
47 ARGIN(const char *b))
48 __attribute__nonnull__(2)
49 __attribute__nonnull__(3);
51 static void expand_hash(PARROT_INTERP, ARGMOD(Hash *hash))
52 __attribute__nonnull__(1)
53 __attribute__nonnull__(2)
54 FUNC_MODIFIES(*hash);
56 static void hash_freeze(PARROT_INTERP,
57 ARGIN(const Hash *hash),
58 ARGMOD(PMC *info))
59 __attribute__nonnull__(1)
60 __attribute__nonnull__(2)
61 __attribute__nonnull__(3)
62 FUNC_MODIFIES(*info);
64 static void hash_thaw(PARROT_INTERP, ARGMOD(Hash *hash), ARGMOD(PMC *info))
65 __attribute__nonnull__(1)
66 __attribute__nonnull__(2)
67 __attribute__nonnull__(3)
68 FUNC_MODIFIES(*hash)
69 FUNC_MODIFIES(*info);
71 PARROT_WARN_UNUSED_RESULT
72 PARROT_PURE_FUNCTION
73 static size_t key_hash_cstring(SHIM_INTERP,
74 ARGIN(const void *value),
75 size_t seed)
76 __attribute__nonnull__(2);
78 PARROT_WARN_UNUSED_RESULT
79 PARROT_CONST_FUNCTION
80 static size_t key_hash_pointer(SHIM_INTERP,
81 ARGIN(const void *value),
82 size_t seed)
83 __attribute__nonnull__(2);
85 static void parrot_mark_hash_both(PARROT_INTERP, ARGIN(Hash *hash))
86 __attribute__nonnull__(1)
87 __attribute__nonnull__(2);
89 static void parrot_mark_hash_keys(PARROT_INTERP, ARGIN(Hash *hash))
90 __attribute__nonnull__(1)
91 __attribute__nonnull__(2);
93 static void parrot_mark_hash_values(PARROT_INTERP, ARGIN(Hash *hash))
94 __attribute__nonnull__(1)
95 __attribute__nonnull__(2);
97 PARROT_WARN_UNUSED_RESULT
98 PARROT_CONST_FUNCTION
99 static int pointer_compare(SHIM_INTERP,
100 ARGIN_NULLOK(const void *a),
101 ARGIN_NULLOK(const void *b));
103 #define ASSERT_ARGS_cstring_compare __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
104 PARROT_ASSERT_ARG(a) \
105 , PARROT_ASSERT_ARG(b))
106 #define ASSERT_ARGS_expand_hash __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
107 PARROT_ASSERT_ARG(interp) \
108 , PARROT_ASSERT_ARG(hash))
109 #define ASSERT_ARGS_hash_freeze __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
110 PARROT_ASSERT_ARG(interp) \
111 , PARROT_ASSERT_ARG(hash) \
112 , PARROT_ASSERT_ARG(info))
113 #define ASSERT_ARGS_hash_thaw __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
114 PARROT_ASSERT_ARG(interp) \
115 , PARROT_ASSERT_ARG(hash) \
116 , PARROT_ASSERT_ARG(info))
117 #define ASSERT_ARGS_key_hash_cstring __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
118 PARROT_ASSERT_ARG(value))
119 #define ASSERT_ARGS_key_hash_pointer __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
120 PARROT_ASSERT_ARG(value))
121 #define ASSERT_ARGS_parrot_mark_hash_both __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
122 PARROT_ASSERT_ARG(interp) \
123 , PARROT_ASSERT_ARG(hash))
124 #define ASSERT_ARGS_parrot_mark_hash_keys __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
125 PARROT_ASSERT_ARG(interp) \
126 , PARROT_ASSERT_ARG(hash))
127 #define ASSERT_ARGS_parrot_mark_hash_values __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
128 PARROT_ASSERT_ARG(interp) \
129 , PARROT_ASSERT_ARG(hash))
130 #define ASSERT_ARGS_pointer_compare __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
131 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
132 /* HEADERIZER END: static */
138 =item C<size_t key_hash_STRING(PARROT_INTERP, STRING *s, size_t seed)>
140 Returns the hashed value of the key C<value>. See also string.c.
142 =cut
147 PARROT_WARN_UNUSED_RESULT
148 size_t
149 key_hash_STRING(PARROT_INTERP, ARGMOD(STRING *s), SHIM(size_t seed))
151 ASSERT_ARGS(key_hash_STRING)
153 if (s->hashval)
154 return s->hashval;
156 return Parrot_str_to_hashval(interp, s);
162 =item C<int STRING_compare(PARROT_INTERP, const void *search_key, const void
163 *bucket_key)>
165 Compares the two strings, returning 0 if they are identical.
167 =cut
171 PARROT_WARN_UNUSED_RESULT
173 STRING_compare(PARROT_INTERP, ARGIN(const void *search_key), ARGIN_NULLOK(const void *bucket_key))
175 ASSERT_ARGS(STRING_compare)
176 STRING const *s1 = (STRING const *)search_key;
177 STRING const *s2 = (STRING const *)bucket_key;
179 if (!s2)
180 return 1;
182 if (s1->hashval != s2->hashval)
183 return 1;
185 /* COWed strings */
186 if (Buffer_bufstart(s1) == Buffer_bufstart(s2)
187 && s1->bufused == s2->bufused)
188 return 0;
190 return CHARSET_COMPARE(interp, s1, s2);
196 =item C<int STRING_compare_distinct_cs_enc(PARROT_INTERP, const void
197 *search_key, const void *bucket_key)>
199 Compare two strings. Returns 0 if they are identical. Considers differing
200 charset or encoding to be distinct.
204 PARROT_WARN_UNUSED_RESULT
206 STRING_compare_distinct_cs_enc(PARROT_INTERP, ARGIN(const void *search_key),
207 ARGIN(const void *bucket_key))
209 ASSERT_ARGS(STRING_compare_distinct_cs_enc)
210 STRING const *s1 = (STRING const *)search_key;
211 STRING const *s2 = (STRING const *)bucket_key;
213 if (s1 && s2 && (
214 s1->charset != s2->charset ||
215 s1->encoding != s2->encoding)) {
216 return 1;
219 return STRING_compare(interp, search_key, bucket_key);
225 =item C<static int pointer_compare(PARROT_INTERP, const void *a, const void *b)>
227 Compares the two pointers, returning 0 if they are identical
229 =cut
233 PARROT_WARN_UNUSED_RESULT
234 PARROT_CONST_FUNCTION
235 static int
236 pointer_compare(SHIM_INTERP, ARGIN_NULLOK(const void *a), ARGIN_NULLOK(const void *b))
238 ASSERT_ARGS(pointer_compare)
239 return a != b;
245 =item C<static size_t key_hash_pointer(PARROT_INTERP, const void *value, size_t
246 seed)>
248 Returns a hashvalue for a pointer.
250 =cut
254 PARROT_WARN_UNUSED_RESULT
255 PARROT_CONST_FUNCTION
256 static size_t
257 key_hash_pointer(SHIM_INTERP, ARGIN(const void *value), size_t seed)
259 ASSERT_ARGS(key_hash_pointer)
260 return ((size_t) value) ^ seed;
266 =item C<static size_t key_hash_cstring(PARROT_INTERP, const void *value, size_t
267 seed)>
269 Creates and returns a hash value from a string.
271 Takes an interpreter, a pointer to a string, and a seed value.
272 Returns the hash value.
274 Used by parrot_new_cstring_hash.
276 =cut
280 PARROT_WARN_UNUSED_RESULT
281 PARROT_PURE_FUNCTION
282 static size_t
283 key_hash_cstring(SHIM_INTERP, ARGIN(const void *value), size_t seed)
285 ASSERT_ARGS(key_hash_cstring)
286 const unsigned char * p = (const unsigned char *) value;
287 register size_t h = seed;
289 while (*p) {
290 h += h << 5;
291 h += *p++;
294 return h;
300 =item C<static int cstring_compare(PARROT_INTERP, const char *a, const char *b)>
302 Compares two C strings for equality, returning -1, 0, and 1 if the first string
303 is less than, equal to, or greater than the second, respectively.
305 =cut
309 PARROT_WARN_UNUSED_RESULT
310 PARROT_PURE_FUNCTION
311 static int
312 cstring_compare(SHIM_INTERP, ARGIN(const char *a), ARGIN(const char *b))
314 ASSERT_ARGS(cstring_compare)
315 return strcmp(a, b);
321 =item C<size_t key_hash_PMC(PARROT_INTERP, PMC *value, size_t seed)>
323 Returns a hashed value for an PMC key (passed as a void pointer, sadly).
325 =cut
329 PARROT_WARN_UNUSED_RESULT
330 PARROT_PURE_FUNCTION
331 size_t
332 key_hash_PMC(PARROT_INTERP, ARGIN(PMC *value), SHIM(size_t seed))
334 ASSERT_ARGS(key_hash_PMC)
335 return VTABLE_hashvalue(interp, value);
340 =item C<int PMC_compare(PARROT_INTERP, PMC *a, PMC *b)>
342 Compares two PMC for equality, returning 0 if the first is equal to second.
343 Uses void pointers to store the PMC, sadly.
345 =cut
349 PARROT_WARN_UNUSED_RESULT
350 PARROT_PURE_FUNCTION
352 PMC_compare(PARROT_INTERP, ARGIN(PMC *a), ARGIN(PMC *b))
354 ASSERT_ARGS(PMC_compare)
356 /* If pointers are same - PMCs are same */
357 if (a == b)
358 return 0;
360 /* PMCs of different types are differ */
361 if (a->vtable->base_type != b->vtable->base_type)
362 return 1;
364 return !VTABLE_is_equal(interp, a, b);
369 =item C<size_t key_hash_int(PARROT_INTERP, const void *value, size_t seed)>
371 Returns a hashed value for an integer key (passed as a void pointer, sadly).
373 =cut
377 PARROT_WARN_UNUSED_RESULT
378 PARROT_CONST_FUNCTION
379 size_t
380 key_hash_int(SHIM_INTERP, ARGIN_NULLOK(const void *value), size_t seed)
382 ASSERT_ARGS(key_hash_int)
383 return (size_t)value ^ seed;
388 =item C<int int_compare(PARROT_INTERP, const void *a, const void *b)>
390 Compares two integers for equality, returning -1, 0, and 1 if the first is less
391 than, equal to, or greater than the second, respectively. Uses void pointers
392 to store the integers, sadly.
394 =cut
398 PARROT_WARN_UNUSED_RESULT
399 PARROT_CONST_FUNCTION
401 int_compare(SHIM_INTERP, ARGIN_NULLOK(const void *a), ARGIN_NULLOK(const void *b))
403 ASSERT_ARGS(int_compare)
404 return a != b;
409 =item C<void parrot_dump_hash(PARROT_INTERP, const Hash *hash)>
411 Prints out the hash in human-readable form, at least once someone implements
412 this.
414 =cut
418 PARROT_EXPORT
419 void
420 parrot_dump_hash(SHIM_INTERP, SHIM(const Hash *hash))
422 ASSERT_ARGS(parrot_dump_hash)
428 =item C<void parrot_mark_hash(PARROT_INTERP, Hash *hash)>
430 Marks the hash and its contents as live. Assumes that key and value are non
431 null in all buckets.
433 =cut
437 PARROT_EXPORT
438 void
439 parrot_mark_hash(PARROT_INTERP, ARGMOD(Hash *hash))
441 ASSERT_ARGS(parrot_mark_hash)
442 int mark_key = 0;
443 int mark_value = 0;
445 if (hash->entry_type == (PARROT_DATA_TYPE) enum_hash_string
446 || hash->entry_type == (PARROT_DATA_TYPE) enum_hash_pmc)
447 mark_value = 1;
449 if (hash->key_type == Hash_key_type_STRING
450 || hash->key_type == Hash_key_type_PMC)
451 mark_key = 1;
453 if (mark_key) {
454 if (mark_value)
455 parrot_mark_hash_both(interp, hash);
456 else
457 parrot_mark_hash_keys(interp, hash);
459 else {
460 if (mark_value)
461 parrot_mark_hash_values(interp, hash);
468 =item C<static void parrot_mark_hash_keys(PARROT_INTERP, Hash *hash)>
470 Marks the hash and only its keys as live.
472 =cut
476 static void
477 parrot_mark_hash_keys(PARROT_INTERP, ARGIN(Hash *hash))
479 ASSERT_ARGS(parrot_mark_hash_keys)
480 UINTVAL entries = hash->entries;
481 UINTVAL found = 0;
482 INTVAL i;
484 for (i = hash->mask; i >= 0; --i) {
485 HashBucket *bucket = hash->bi[i];
487 while (bucket) {
488 if (++found > entries)
489 Parrot_ex_throw_from_c_args(interp, NULL, 1,
490 "Detected hash corruption at hash %p entries %d",
491 hash, (int)entries);
493 PARROT_ASSERT(bucket->key);
494 Parrot_gc_mark_PObj_alive(interp, (PObj *)bucket->key);
496 bucket = bucket->next;
504 =item C<static void parrot_mark_hash_values(PARROT_INTERP, Hash *hash)>
506 Marks the hash and only its values as live.
508 =cut
512 static void
513 parrot_mark_hash_values(PARROT_INTERP, ARGIN(Hash *hash))
515 ASSERT_ARGS(parrot_mark_hash_values)
516 const UINTVAL entries = hash->entries;
517 UINTVAL found = 0;
518 INTVAL i;
520 for (i = hash->mask; i >= 0; --i) {
521 HashBucket *bucket = hash->bi[i];
523 while (bucket) {
524 if (++found > entries)
525 Parrot_ex_throw_from_c_args(interp, NULL, 1,
526 "Detected hash corruption at hash %p entries %d",
527 hash, (int)entries);
529 PARROT_ASSERT(bucket->value);
530 Parrot_gc_mark_PObj_alive(interp, (PObj *)bucket->value);
532 bucket = bucket->next;
540 =item C<static void parrot_mark_hash_both(PARROT_INTERP, Hash *hash)>
542 Marks the hash and both its keys and values as live.
544 =cut
548 static void
549 parrot_mark_hash_both(PARROT_INTERP, ARGIN(Hash *hash))
551 ASSERT_ARGS(parrot_mark_hash_both)
552 const UINTVAL entries = hash->entries;
553 UINTVAL found = 0;
554 INTVAL i;
556 for (i = hash->mask; i >= 0; --i) {
557 HashBucket *bucket = hash->bi[i];
559 while (bucket) {
560 if (++found > entries)
561 Parrot_ex_throw_from_c_args(interp, NULL, 1,
562 "Detected hash corruption at hash %p entries %d",
563 hash, (int)entries);
565 PARROT_ASSERT(bucket->key);
566 Parrot_gc_mark_PObj_alive(interp, (PObj *)bucket->key);
568 PARROT_ASSERT(bucket->value);
569 Parrot_gc_mark_PObj_alive(interp, (PObj *)bucket->value);
571 bucket = bucket->next;
579 =item C<static void hash_thaw(PARROT_INTERP, Hash *hash, PMC *info)>
581 Visits the contents of a hash during freeze/thaw.
583 C<pinfo> is the visit info, (see include/parrot/pmc_freeze.h>).
585 =cut
589 static void
590 hash_thaw(PARROT_INTERP, ARGMOD(Hash *hash), ARGMOD(PMC *info))
592 ASSERT_ARGS(hash_thaw)
594 /* during thaw, info->extra is the key/value count */
595 const size_t num_entries = (size_t) hash->entries;
596 size_t entry_index;
598 hash->entries = 0;
600 for (entry_index = 0; entry_index < num_entries; ++entry_index) {
601 HashBucket *b;
603 switch (hash->key_type) {
604 case Hash_key_type_int:
606 const INTVAL i_key = VTABLE_shift_integer(interp, info);
607 b = parrot_hash_put(interp, hash, (void*)i_key, NULL);
609 break;
610 case Hash_key_type_STRING:
612 STRING * const s_key = VTABLE_shift_string(interp, info);
613 b = parrot_hash_put(interp, hash, s_key, NULL);
615 break;
616 case Hash_key_type_PMC:
618 PMC * const p_key = VTABLE_shift_pmc(interp, info);
619 b = parrot_hash_put(interp, hash, p_key, NULL);
620 break;
622 default:
623 Parrot_ex_throw_from_c_args(interp, NULL, 1,
624 "unimplemented key type");
625 break;
628 switch (hash->entry_type) {
629 case enum_hash_int:
631 const INTVAL i = VTABLE_shift_integer(interp, info);
632 b->value = (void *)i;
633 break;
635 case enum_hash_string:
637 STRING * const s = VTABLE_shift_string(interp, info);
638 b->value = (void *)s;
639 break;
641 case enum_hash_pmc:
643 PMC * const p = VTABLE_shift_pmc(interp, info);
644 b->value = (void *)p;
645 break;
647 default:
648 Parrot_ex_throw_from_c_args(interp, NULL, 1,
649 "unimplemented value type");
650 break;
658 =item C<static void hash_freeze(PARROT_INTERP, const Hash *hash, PMC *info)>
660 Freezes hash into a string.
662 Takes an interpreter, a pointer to the hash, and a pointer to the structure
663 containing the string start location.
665 Use by parrot_hash_visit.
667 =cut
671 static void
672 hash_freeze(PARROT_INTERP, ARGIN(const Hash *hash), ARGMOD(PMC *info))
674 ASSERT_ARGS(hash_freeze)
675 size_t i;
677 for (i = 0; i < hash->entries; ++i) {
678 HashBucket * const b = hash->bs+i;
680 switch (hash->key_type) {
681 case Hash_key_type_int:
682 VTABLE_push_integer(interp, info, (INTVAL)b->key);
683 break;
684 case Hash_key_type_STRING:
685 VTABLE_push_string(interp, info, (STRING *)b->key);
686 break;
687 case Hash_key_type_PMC:
688 VTABLE_push_pmc(interp, info, (PMC *)b->key);
689 break;
690 default:
691 Parrot_ex_throw_from_c_args(interp, NULL, 1,
692 "unimplemented key type");
693 break;
696 switch (hash->entry_type) {
697 case enum_hash_int:
698 VTABLE_push_integer(interp, info, (INTVAL)b->value);
699 break;
700 case enum_hash_string:
701 VTABLE_push_string(interp, info, (STRING *)b->value);
702 break;
703 case enum_hash_pmc:
704 VTABLE_push_pmc(interp, info, (PMC *)b->value);
705 break;
706 default:
707 Parrot_ex_throw_from_c_args(interp, NULL, 1,
708 "unimplemented value type");
709 break;
717 =item C<void parrot_hash_visit(PARROT_INTERP, Hash *hash, void *pinfo)>
719 Freezes or thaws a hash as specified. Takes an interpreter, a pointer to the
720 hash, and a pointer to the structure identifying what to do and the location of
721 the string.
723 =cut
727 PARROT_EXPORT
728 void
729 parrot_hash_visit(PARROT_INTERP, ARGMOD(Hash *hash), ARGMOD(void *pinfo))
731 ASSERT_ARGS(parrot_hash_visit)
732 PMC* const info = (PMC*) pinfo;
734 switch (VTABLE_get_integer(interp, info)) {
735 case VISIT_THAW_NORMAL:
736 hash_thaw(interp, hash, info);
737 break;
738 case VISIT_FREEZE_NORMAL:
739 hash_freeze(interp, hash, info);
740 break;
741 default:
742 Parrot_ex_throw_from_c_args(interp, NULL, 1,
743 "unimplemented visit mode");
750 =item C<static void expand_hash(PARROT_INTERP, Hash *hash)>
752 Expands a hash when necessary.
754 For a hashtable of size N, we use C<MAXFULL_PERCENT> % of N as the
755 number of buckets. This way, as soon as we run out of buckets on the
756 free list, we know that it's time to resize the hashtable.
758 Algorithm for expansion: We exactly double the size of the hashtable.
759 Keys are assigned to buckets with the formula
761 bucket_index = hash(key) % parrot_hash_size
763 When doubling the size of the hashtable, we know that every key is either
764 already in the correct bucket, or belongs in the current bucket plus
765 C<parrot_hash_size> (the old C<parrot_hash_size>). In fact, because the
766 hashtable is always a power of two in size, it depends only on the next bit
767 in the hash value, after the ones previously used.
769 We scan through all the buckets in order, moving the buckets that need to be
770 moved. No bucket will be scanned twice, and the cache should be reasonably
771 happy because the hashtable accesses will be two parallel sequential scans.
772 (Of course, this also mucks with the C<< ->next >> pointers, and they'll be
773 all over memory.)
775 =cut
779 static void
780 expand_hash(PARROT_INTERP, ARGMOD(Hash *hash))
782 ASSERT_ARGS(expand_hash)
783 HashBucket **old_bi, **new_bi;
784 HashBucket *bs, *b, *new_mem;
785 HashBucket * const old_offset = (HashBucket *)((char *)hash + sizeof (Hash));
787 void * const old_mem = hash->bs;
788 const UINTVAL old_size = hash->mask + 1;
789 const UINTVAL new_size = old_size << 1;
790 const UINTVAL old_nb = N_BUCKETS(old_size);
791 size_t offset, i;
794 allocate some less buckets
795 e.g. 3 buckets, 4 pointers:
797 +---+---+---+-+-+-+-+
798 | --> bs | -> bi |
799 +---+---+---+-+-+-+-+
801 | old_mem | hash->bi
804 /* resize mem */
805 if (old_offset != old_mem) {
806 /* This buffer has been reallocated at least once before. */
807 new_mem = (HashBucket *)Parrot_gc_reallocate_memory_chunk_with_interior_pointers(
808 interp, old_mem, HASH_ALLOC_SIZE(new_size), HASH_ALLOC_SIZE(old_size));
810 else {
811 /* Allocate a new buffer. */
812 new_mem = (HashBucket *)Parrot_gc_allocate_memory_chunk_with_interior_pointers(
813 interp, HASH_ALLOC_SIZE(new_size));
814 memcpy(new_mem, old_mem, HASH_ALLOC_SIZE(old_size));
818 +---+---+---+---+---+---+-+-+-+-+-+-+-+-+
819 | bs | old_bi | new_bi |
820 +---+---+---+---+---+---+-+-+-+-+-+-+-+-+
822 | new_mem | hash->bi
824 bs = new_mem;
825 old_bi = (HashBucket **)(bs + old_nb);
826 new_bi = (HashBucket **)(bs + N_BUCKETS(new_size));
828 /* things can have moved by this offset */
829 offset = (char *)new_mem - (char *)old_mem;
831 /* relocate the bucket index */
832 mem_sys_memmove(new_bi, old_bi, old_size * sizeof (HashBucket *));
834 /* update hash data */
835 hash->bi = new_bi;
836 hash->bs = bs;
837 hash->mask = new_size - 1;
839 /* clear freshly allocated bucket index */
840 memset(new_bi + old_size, 0, sizeof (HashBucket *) * old_size);
843 * reloc pointers - this part would be also needed, if we
844 * allocate hash memory from GC movable memory, and then
845 * also the free_list needs updating (this is empty now,
846 * as expand_hash is only called for that case).
848 if (offset) {
849 size_t j;
850 for (j = 0; j < old_size; ++j) {
851 HashBucket **next_p = new_bi + j;
852 while (*next_p) {
853 *next_p = (HashBucket *)((char *)*next_p + offset);
854 b = *next_p;
855 next_p = &b->next;
860 /* recalc bucket index */
861 for (i = 0; i < old_size; ++i) {
862 HashBucket **next_p = new_bi + i;
864 while ((b = *next_p) != NULL) {
865 /* rehash the bucket */
866 const size_t new_loc =
867 (hash->hash_val)(interp, b->key, hash->seed) & (new_size - 1);
869 if (i != new_loc) {
870 *next_p = b->next;
871 b->next = new_bi[new_loc];
872 new_bi[new_loc] = b;
874 else
875 next_p = &b->next;
879 /* add new buckets to free_list in reverse order
880 * lowest bucket is top on free list and will be used first */
881 for (i = 0, b = (HashBucket *)new_bi - 1; i < old_nb; ++i, --b) {
882 b->next = hash->free_list;
883 b->key = b->value = NULL;
884 hash->free_list = b;
891 =item C<Hash* parrot_new_hash(PARROT_INTERP)>
893 Creates a new Parrot STRING hash.
895 =cut
899 PARROT_EXPORT
900 PARROT_CANNOT_RETURN_NULL
901 Hash*
902 parrot_new_hash(PARROT_INTERP)
904 ASSERT_ARGS(parrot_new_hash)
905 return parrot_create_hash(interp,
906 enum_type_PMC,
907 Hash_key_type_STRING,
908 STRING_compare,
909 (hash_hash_key_fn)key_hash_STRING);
915 =item C<Hash* parrot_new_cstring_hash(PARROT_INTERP)>
917 Creates a new C string hash in C<hptr>.
919 =cut
923 PARROT_EXPORT
924 PARROT_CANNOT_RETURN_NULL
925 Hash*
926 parrot_new_cstring_hash(PARROT_INTERP)
928 ASSERT_ARGS(parrot_new_cstring_hash)
929 return parrot_create_hash(interp,
930 enum_type_PMC,
931 Hash_key_type_cstring,
932 (hash_comp_fn)cstring_compare,
933 (hash_hash_key_fn)key_hash_cstring);
939 =item C<Hash * parrot_new_pointer_hash(PARROT_INTERP)>
941 Create and return a new hash with void * keys and values.
943 =cut
947 PARROT_EXPORT
948 PARROT_CANNOT_RETURN_NULL
949 Hash *
950 parrot_new_pointer_hash(PARROT_INTERP)
952 ASSERT_ARGS(parrot_new_pointer_hash)
953 return parrot_create_hash(interp,
954 enum_type_ptr,
955 Hash_key_type_ptr,
956 pointer_compare,
957 key_hash_pointer);
963 =item C<Hash* parrot_new_intval_hash(PARROT_INTERP)>
965 Creates and returns new Hash PMC with INTVAL keys and values. C<flags> can be
966 C<PObj_constant_FLAG> or 0.
968 =cut
973 PARROT_EXPORT
974 PARROT_WARN_UNUSED_RESULT
975 PARROT_CANNOT_RETURN_NULL
976 Hash*
977 parrot_new_intval_hash(PARROT_INTERP)
979 ASSERT_ARGS(parrot_new_intval_hash)
980 return parrot_create_hash(interp,
981 enum_type_INTVAL,
982 Hash_key_type_int,
983 int_compare,
984 key_hash_int);
989 =item C<Hash * parrot_create_hash(PARROT_INTERP, PARROT_DATA_TYPE val_type,
990 Hash_key_type hkey_type, hash_comp_fn compare, hash_hash_key_fn keyhash)>
992 Creates and initializes a hash. Function pointers determine its behaviors.
993 The container passed in is the address of the hash PMC that is using it. The
994 hash and the PMC point to each other.
996 Memory from this function must be freed.
998 =cut
1002 PARROT_CANNOT_RETURN_NULL
1003 PARROT_WARN_UNUSED_RESULT
1004 PARROT_MALLOC
1005 Hash *
1006 parrot_create_hash(PARROT_INTERP, PARROT_DATA_TYPE val_type, Hash_key_type hkey_type,
1007 NOTNULL(hash_comp_fn compare), NOTNULL(hash_hash_key_fn keyhash))
1009 ASSERT_ARGS(parrot_create_hash)
1010 HashBucket *bp;
1011 void *alloc = Parrot_gc_allocate_memory_chunk_with_interior_pointers(
1012 interp, sizeof (Hash) + HASH_ALLOC_SIZE(INITIAL_BUCKETS));
1013 Hash * const hash = (Hash*)alloc;
1014 size_t i;
1016 PARROT_ASSERT(INITIAL_BUCKETS % 4 == 0);
1018 hash->compare = compare;
1019 hash->hash_val = keyhash;
1020 hash->entry_type = val_type;
1021 hash->key_type = hkey_type;
1022 hash->seed = interp->hash_seed;
1023 hash->mask = INITIAL_BUCKETS - 1;
1024 hash->entries = 0;
1025 hash->container = PMCNULL;
1028 * TODO if we have a significant amount of small hashes:
1029 * - allocate a bigger hash structure e.g. 128 byte
1030 * - use the bucket store and bi inside this structure
1031 * - when reallocate copy this part
1033 bp = (HashBucket *)((char *)alloc + sizeof (Hash));
1034 hash->free_list = NULL;
1036 /* fill free_list from hi addresses so that we can use
1037 * buckets[i] directly in an OrderedHash, *if* nothing
1038 * was deleted */
1040 hash->bs = bp;
1041 bp += N_BUCKETS(INITIAL_BUCKETS);
1042 hash->bi = (HashBucket **)bp;
1044 for (i = 0, --bp; i < N_BUCKETS(INITIAL_BUCKETS); ++i, --bp) {
1045 bp->next = hash->free_list;
1046 hash->free_list = bp;
1049 return hash;
1055 =item C<void parrot_hash_destroy(PARROT_INTERP, Hash *hash)>
1057 Frees the memory allocated to the specified hash and its bucket store. Used by
1058 parrot_chash_destroy.
1060 Unlike the C library function free(), the hash function must not be NULL.
1062 =cut
1066 PARROT_EXPORT
1067 void
1068 parrot_hash_destroy(PARROT_INTERP, ARGFREE_NOTNULL(Hash *hash))
1070 ASSERT_ARGS(parrot_hash_destroy)
1071 HashBucket * const bp = (HashBucket*)((char*)hash + sizeof (Hash));
1072 if (bp != hash->bs)
1073 mem_gc_free(interp, hash->bs);
1074 mem_gc_free(interp, hash);
1080 =item C<void parrot_chash_destroy(PARROT_INTERP, Hash *hash)>
1082 Deletes the specified hash by freeing the memory allocated to all the key-value
1083 pairs, and finally the hash itself.
1085 =cut
1089 void
1090 parrot_chash_destroy(PARROT_INTERP, ARGMOD(Hash *hash))
1092 ASSERT_ARGS(parrot_chash_destroy)
1093 UINTVAL i;
1095 for (i = 0; i <= hash->mask; ++i) {
1096 HashBucket *bucket = hash->bi[i];
1097 while (bucket) {
1098 mem_gc_free(interp, bucket->key);
1099 mem_gc_free(interp, bucket->value);
1100 bucket = bucket->next;
1104 parrot_hash_destroy(interp, hash);
1110 =item C<void parrot_chash_destroy_values(PARROT_INTERP, Hash *hash, value_free
1111 func)>
1113 Deletes the specified hash by freeing the memory allocated to all the key-value
1114 pairs, calling the provided callback to free the values, and finally the hash
1115 itself.
1117 The callback returns C<void> and takes a C<void *>.
1119 =cut
1123 void
1124 parrot_chash_destroy_values(PARROT_INTERP, ARGMOD(Hash *hash), NOTNULL(value_free func))
1126 ASSERT_ARGS(parrot_chash_destroy_values)
1127 UINTVAL i;
1129 for (i = 0; i <= hash->mask; ++i) {
1130 HashBucket *bucket = hash->bi[i];
1131 while (bucket) {
1132 mem_gc_free(interp, bucket->key);
1133 func(bucket->value);
1134 bucket = bucket->next;
1138 parrot_hash_destroy(interp, hash);
1144 =item C<INTVAL parrot_hash_size(PARROT_INTERP, const Hash *hash)>
1146 Returns the number of used entries in the hash.
1148 =cut
1152 PARROT_EXPORT
1153 PARROT_WARN_UNUSED_RESULT
1154 PARROT_PURE_FUNCTION
1155 INTVAL
1156 parrot_hash_size(SHIM_INTERP, ARGIN(const Hash *hash))
1158 ASSERT_ARGS(parrot_hash_size)
1160 return hash->entries;
1166 =item C<void * parrot_hash_get_idx(PARROT_INTERP, const Hash *hash, PMC *key)>
1168 Finds the next index into the hash's internal storage for the given Key. Used
1169 by iterators. Ugly.
1171 =cut
1175 PARROT_EXPORT
1176 PARROT_WARN_UNUSED_RESULT
1177 PARROT_CAN_RETURN_NULL
1178 void *
1179 parrot_hash_get_idx(PARROT_INTERP, ARGIN(const Hash *hash), ARGMOD(PMC *key))
1181 ASSERT_ARGS(parrot_hash_get_idx)
1182 HashBucket *b;
1183 void *res;
1184 INTVAL i = VTABLE_get_integer(interp, key);
1185 PMC *fake_bi;
1186 BucketIndex bi;
1188 /* idx directly in the bucket store, which is at negative
1189 * address from the data pointer */
1190 /* locate initial */
1191 const INTVAL size = (INTVAL)N_BUCKETS(hash->mask + 1);
1193 GETATTR_Key_next_key(interp, key, fake_bi);
1194 bi = (BucketIndex)fake_bi;
1196 if (bi == INITBucketIndex) {
1197 i = 0;
1198 SETATTR_Key_next_key(interp, key, NULL);
1200 else if (i >= size || i < 0) {
1201 /* NOTE: These instances of SETATTR_Key_int_key can't be VTABLE
1202 * functions because of the "special" way hash iterators work. */
1203 SETATTR_Key_int_key(interp, key, -1);
1204 return NULL;
1207 res = NULL;
1209 for (b = hash->bs + i; i < size ; ++i, ++b) {
1210 /* XXX int keys may be zero - use different iterator */
1211 if (b->key) {
1212 if (!res)
1213 res = b->key;
1215 /* found next key - FIXME hash iter does auto next */
1216 else
1217 break;
1221 if (i >= size)
1222 i = -1;
1224 SETATTR_Key_int_key(interp, key, i);
1226 return res;
1232 =item C<HashBucket * parrot_hash_get_bucket(PARROT_INTERP, const Hash *hash,
1233 const void *key)>
1235 Returns the bucket for C<key>, if found, and NULL otherwise.
1237 =cut
1241 PARROT_EXPORT
1242 PARROT_WARN_UNUSED_RESULT
1243 PARROT_CAN_RETURN_NULL
1244 HashBucket *
1245 parrot_hash_get_bucket(PARROT_INTERP, ARGIN(const Hash *hash), ARGIN_NULLOK(const void *key))
1247 ASSERT_ARGS(parrot_hash_get_bucket)
1249 if (hash->entries <= 0)
1250 return NULL;
1252 /* a very fast search for very small hashes */
1253 if (hash->entries <= SMALL_HASH_SIZE) {
1254 const UINTVAL entries = hash->entries;
1255 UINTVAL i;
1257 for (i = 0; i < entries; ++i) {
1258 HashBucket * const bucket = hash->bs + i;
1260 /* the hash->compare cost is too high for this fast path */
1261 if (bucket->key == key)
1262 return bucket;
1266 /* if the fast search didn't work, try the normal hashing search */
1268 const UINTVAL hashval = (hash->hash_val)(interp, key, hash->seed);
1269 HashBucket *bucket = hash->bi[hashval & hash->mask];
1271 while (bucket) {
1272 /* key equality is always a match, so it's worth checking */
1273 if (bucket->key == key
1275 /* ... but the slower comparison is more accurate */
1276 || ((hash->compare)(interp, key, bucket->key) == 0))
1277 return bucket;
1278 bucket = bucket->next;
1282 return NULL;
1288 =item C<void * parrot_hash_get(PARROT_INTERP, const Hash *hash, const void
1289 *key)>
1291 Returns the value keyed by C<key>, or C<NULL> if no bucket is found.
1293 =cut
1297 PARROT_EXPORT
1298 PARROT_WARN_UNUSED_RESULT
1299 PARROT_CAN_RETURN_NULL
1300 void *
1301 parrot_hash_get(PARROT_INTERP, ARGIN(const Hash *hash), ARGIN(const void *key))
1303 ASSERT_ARGS(parrot_hash_get)
1304 const HashBucket * const bucket = parrot_hash_get_bucket(interp, hash, key);
1305 return bucket ? bucket->value : NULL;
1311 =item C<INTVAL parrot_hash_exists(PARROT_INTERP, const Hash *hash, void *key)>
1313 Returns whether the key exists in the hash.
1315 =cut
1319 PARROT_EXPORT
1320 PARROT_WARN_UNUSED_RESULT
1321 INTVAL
1322 parrot_hash_exists(PARROT_INTERP, ARGIN(const Hash *hash), ARGIN(void *key))
1324 ASSERT_ARGS(parrot_hash_exists)
1325 const HashBucket * const bucket = parrot_hash_get_bucket(interp, hash, key);
1326 return bucket ? 1 : 0;
1332 =item C<HashBucket* parrot_hash_put(PARROT_INTERP, Hash *hash, void *key, void
1333 *value)>
1335 Puts the key and value into the hash. Note that C<key> is B<not> copied.
1337 =cut
1341 PARROT_EXPORT
1342 PARROT_IGNORABLE_RESULT
1343 PARROT_CANNOT_RETURN_NULL
1344 HashBucket*
1345 parrot_hash_put(PARROT_INTERP, ARGMOD(Hash *hash),
1346 ARGIN_NULLOK(void *key), ARGIN_NULLOK(void *value))
1348 ASSERT_ARGS(parrot_hash_put)
1349 const UINTVAL hashval = (hash->hash_val)(interp, key, hash->seed);
1350 HashBucket *bucket = hash->bi[hashval & hash->mask];
1352 /* When the hash is constant, check that the key and value are also
1353 * constant. */
1354 if (!PMC_IS_NULL(hash->container)
1355 && PObj_constant_TEST(hash->container)) {
1356 if (hash->key_type == Hash_key_type_STRING
1357 && !PObj_constant_TEST((PObj *)key))
1358 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
1359 "Used non-constant key in constant hash.");
1360 if (((hash->entry_type == enum_type_PMC)
1361 || (hash->entry_type == enum_type_STRING))
1362 && !PObj_constant_TEST((PObj *)value))
1363 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
1364 "Used non-constant value in constant hash.");
1367 while (bucket) {
1368 /* store hash_val or not */
1369 if ((hash->compare)(interp, key, bucket->key) == 0)
1370 break;
1371 bucket = bucket->next;
1374 if (bucket)
1375 bucket->value = value;
1376 else {
1377 bucket = hash->free_list;
1379 if (!bucket) {
1380 expand_hash(interp, hash);
1381 bucket = hash->free_list;
1384 ++hash->entries;
1385 hash->free_list = bucket->next;
1386 bucket->key = key;
1387 bucket->value = value;
1388 bucket->next = hash->bi[hashval & hash->mask];
1389 hash->bi[hashval & hash->mask] = bucket;
1392 return bucket;
1398 =item C<void parrot_hash_delete(PARROT_INTERP, Hash *hash, void *key)>
1400 Deletes the key from the hash.
1402 =cut
1406 PARROT_EXPORT
1407 void
1408 parrot_hash_delete(PARROT_INTERP, ARGMOD(Hash *hash), ARGIN(void *key))
1410 ASSERT_ARGS(parrot_hash_delete)
1411 HashBucket *bucket;
1412 HashBucket *prev = NULL;
1413 const UINTVAL hashval = (hash->hash_val)(interp, key, hash->seed) & hash->mask;
1415 for (bucket = hash->bi[hashval]; bucket; bucket = bucket->next) {
1416 if ((hash->compare)(interp, key, bucket->key) == 0) {
1418 if (prev)
1419 prev->next = bucket->next;
1420 else
1421 hash->bi[hashval] = bucket->next;
1423 --hash->entries;
1424 bucket->next = hash->free_list;
1425 bucket->key = NULL;
1426 hash->free_list = bucket;
1428 return;
1431 prev = bucket;
1438 =item C<void parrot_hash_clone(PARROT_INTERP, const Hash *hash, Hash *dest)>
1440 Clones C<hash> to C<dest>.
1442 =cut
1446 PARROT_EXPORT
1447 void
1448 parrot_hash_clone(PARROT_INTERP, ARGIN(const Hash *hash), ARGOUT(Hash *dest))
1450 ASSERT_ARGS(parrot_hash_clone)
1451 UINTVAL entries = hash->entries;
1452 UINTVAL i;
1454 for (i = 0; i < entries; ++i) {
1455 void *valtmp;
1456 HashBucket *b = hash->bs+i;
1457 void * const key = b->key;
1459 switch (hash->entry_type) {
1460 case enum_type_undef:
1461 case enum_type_ptr:
1462 case enum_type_INTVAL:
1463 valtmp = (void *)b->value;
1464 break;
1466 case enum_type_STRING:
1467 valtmp = b->value;
1468 break;
1470 case enum_type_PMC:
1471 if (PMC_IS_NULL((PMC *)b->value))
1472 valtmp = (void *)PMCNULL;
1473 else
1474 valtmp = (void *)VTABLE_clone(interp, (PMC*)b->value);
1475 break;
1477 default:
1478 valtmp = NULL; /* avoid warning */
1479 Parrot_ex_throw_from_c_args(interp, NULL, -1,
1480 "hash corruption: type = %d\n", hash->entry_type);
1483 if (key)
1484 parrot_hash_put(interp, dest, key, valtmp);
1490 =item C<PMC* get_integer_pmc(PARROT_INTERP, INTVAL value)>
1492 Lookup the PMC type which is used for storing native integers.
1494 =cut
1498 PARROT_CANNOT_RETURN_NULL
1499 PMC*
1500 get_integer_pmc(PARROT_INTERP, INTVAL value)
1502 ASSERT_ARGS(get_integer_pmc)
1503 PMC * const ret = Parrot_pmc_new(interp, Parrot_get_ctx_HLL_type(interp, enum_class_Integer));
1504 VTABLE_set_integer_native(interp, ret, value);
1505 return ret;
1511 =item C<PMC* get_number_pmc(PARROT_INTERP, FLOATVAL value)>
1513 Lookup the PMC type which is used for floating point numbers.
1515 =cut
1519 PARROT_CANNOT_RETURN_NULL
1520 PMC*
1521 get_number_pmc(PARROT_INTERP, FLOATVAL value)
1523 ASSERT_ARGS(get_number_pmc)
1524 PMC * const ret = Parrot_pmc_new(interp, Parrot_get_ctx_HLL_type(interp, enum_class_Float));
1525 VTABLE_set_number_native(interp, ret, value);
1526 return ret;
1531 =item C<PMC * get_string_pmc(PARROT_INTERP, STRING *value)>
1533 Lookup the PMC type which is used for storing strings.
1535 =cut
1539 PARROT_CANNOT_RETURN_NULL
1540 PMC *
1541 get_string_pmc(PARROT_INTERP, ARGIN(STRING *value))
1543 ASSERT_ARGS(get_string_pmc)
1544 PMC * const ret = Parrot_pmc_new(interp, Parrot_get_ctx_HLL_type(interp, enum_class_String));
1545 VTABLE_set_string_native(interp, ret, value);
1546 return ret;
1552 Poor-man polymorphic functions to convert something to something.
1554 There is bunch of functions to convert from passed value to stored keys type and to/from
1555 stored values type.
1557 void *hash_key_from_TYPE convert to keys type.
1558 TYPE hash_key_to_TYPE convert from keys type.
1559 void *hash_value_from_TYPE convert to values type.
1560 TYPE hash_value_to_TYPE convert from values type.
1566 =item C<void* hash_key_from_int(PARROT_INTERP, const Hash *hash, INTVAL key)>
1568 Cast INTVAL to hash key.
1570 =cut
1574 PARROT_CAN_RETURN_NULL
1575 void*
1576 hash_key_from_int(PARROT_INTERP, ARGIN(const Hash *hash), INTVAL key)
1578 ASSERT_ARGS(hash_key_from_int)
1579 void *ret;
1580 switch (hash->key_type) {
1581 case Hash_key_type_int:
1582 ret = (void *)key;
1583 break;
1584 /* Currently PMCs are stringified */
1585 case Hash_key_type_PMC:
1586 ret = (void *)get_integer_pmc(interp, key);
1587 break;
1588 case Hash_key_type_STRING:
1589 ret = (void *)Parrot_str_from_int(interp, key);
1590 break;
1591 default:
1592 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNIMPLEMENTED,
1593 "Hash: unsupported key_type");
1595 return ret;
1600 =item C<void* hash_key_from_string(PARROT_INTERP, const Hash *hash, STRING
1601 *key)>
1603 Cast STRING to hash key.
1605 =cut
1609 PARROT_CAN_RETURN_NULL
1610 void*
1611 hash_key_from_string(PARROT_INTERP, ARGIN(const Hash *hash), ARGIN(STRING *key))
1613 ASSERT_ARGS(hash_key_from_string)
1614 void *ret;
1615 switch (hash->key_type) {
1616 case Hash_key_type_int:
1618 /* Pacify compiler about casting INVTAL to void */
1619 const INTVAL int_key = Parrot_str_to_int(interp, key);
1620 ret = INTVAL2PTR(void *, int_key);
1621 break;
1624 case Hash_key_type_PMC:
1625 ret = get_string_pmc(interp, key);
1626 break;
1628 case Hash_key_type_STRING:
1629 ret = key;
1630 break;
1632 default:
1633 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNIMPLEMENTED,
1634 "Hash: unsupported key_type");
1636 return ret;
1641 =item C<void* hash_key_from_pmc(PARROT_INTERP, const Hash *hash, PMC *key)>
1643 Cast PMC* to hash key.
1645 =cut
1649 PARROT_CAN_RETURN_NULL
1650 void*
1651 hash_key_from_pmc(PARROT_INTERP, ARGIN(const Hash *hash), ARGIN(PMC *key))
1653 ASSERT_ARGS(hash_key_from_pmc)
1654 void *ret;
1655 switch (hash->key_type) {
1656 case Hash_key_type_int:
1658 const INTVAL int_key = VTABLE_get_integer(interp, key);
1659 ret = INTVAL2PTR(void *, int_key);
1660 break;
1662 case Hash_key_type_PMC:
1664 /* Extract real value from Key (and box it if nessary) */
1665 if (key->vtable->base_type == enum_class_Key)
1666 switch (key_type(interp, key)) {
1667 case KEY_integer_FLAG:
1668 key = get_integer_pmc(interp, key_integer(interp, key));
1669 break;
1670 case KEY_string_FLAG:
1671 key = get_string_pmc(interp, key_string(interp, key));
1672 break;
1673 case KEY_number_FLAG:
1674 key = get_number_pmc(interp, key_number(interp, key));
1675 break;
1676 case KEY_pmc_FLAG:
1677 key = key_pmc(interp, key);
1678 break;
1679 default:
1680 /* It's impossible if Keys are same (and they are not) */
1681 /* So throw exception */
1682 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
1683 "hash: unexpected type of Key");
1684 break;
1687 ret = key;
1688 break;
1690 case Hash_key_type_STRING:
1692 STRING * const tmp = VTABLE_get_string(interp, key);
1693 if (STRING_IS_NULL(tmp))
1694 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNEXPECTED_NULL,
1695 "hash: can't use null as key");
1696 ret = (void *)tmp;
1698 break;
1699 default:
1700 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNIMPLEMENTED,
1701 "Hash: unsupported key_type");
1703 return ret;
1708 =item C<INTVAL hash_key_to_int(PARROT_INTERP, const Hash *hash, void *key)>
1710 Cast hash key to INTVAL.
1712 =cut
1716 INTVAL
1717 hash_key_to_int(PARROT_INTERP, ARGIN(const Hash *hash), ARGIN_NULLOK(void *key))
1719 ASSERT_ARGS(hash_key_to_int)
1720 INTVAL ret;
1721 switch (hash->key_type) {
1722 case Hash_key_type_int:
1723 ret = (INTVAL)key;
1724 break;
1725 case Hash_key_type_PMC:
1726 ret = VTABLE_get_integer(interp, (PMC *)key);
1727 break;
1728 case Hash_key_type_STRING:
1729 ret = Parrot_str_to_int(interp, (STRING *)key);
1730 break;
1731 default:
1732 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNIMPLEMENTED,
1733 "Hash: unsupported key_type");
1735 return ret;
1740 =item C<STRING* hash_key_to_string(PARROT_INTERP, const Hash *hash, void *key)>
1742 Cast hash key to STRING.
1744 =cut
1748 PARROT_CANNOT_RETURN_NULL
1749 STRING*
1750 hash_key_to_string(PARROT_INTERP, ARGIN(const Hash *hash), ARGIN_NULLOK(void *key))
1752 ASSERT_ARGS(hash_key_to_string)
1753 STRING *ret;
1754 switch (hash->key_type) {
1755 case Hash_key_type_int:
1756 ret = Parrot_str_from_int(interp, (INTVAL)key);
1757 break;
1759 case Hash_key_type_PMC:
1760 ret = VTABLE_get_string(interp, (PMC *)key);
1761 break;
1763 case Hash_key_type_STRING:
1764 ret = (STRING *)key;
1765 break;
1767 default:
1768 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNIMPLEMENTED,
1769 "Hash: unsupported key_type");
1771 return ret;
1776 =item C<PMC* hash_key_to_pmc(PARROT_INTERP, const Hash * const hash, void *key)>
1778 Cast hash key to PMC*.
1780 =cut
1784 PARROT_CANNOT_RETURN_NULL
1785 PMC*
1786 hash_key_to_pmc(PARROT_INTERP, ARGIN(const Hash * const hash), ARGIN(void *key))
1788 ASSERT_ARGS(hash_key_to_pmc)
1789 PMC *ret;
1790 switch (hash->key_type) {
1791 case Hash_key_type_int:
1792 ret = get_integer_pmc(interp, (INTVAL)key);
1793 break;
1794 case Hash_key_type_PMC:
1795 ret = (PMC*)key;
1796 break;
1797 case Hash_key_type_STRING:
1798 ret = get_string_pmc(interp, (STRING*)key);
1799 break;
1800 default:
1801 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNIMPLEMENTED,
1802 "Hash: unsupported key_type");
1804 return ret;
1807 /* Second part - convert from stored void* to real type */
1808 /* TODO: FLOATVALs converted into Float PMC for now */
1812 =item C<void* hash_value_from_int(PARROT_INTERP, const Hash *hash, INTVAL
1813 value)>
1815 Cast INTVAL to hash value.
1817 =cut
1821 PARROT_CAN_RETURN_NULL
1822 void*
1823 hash_value_from_int(PARROT_INTERP, ARGIN(const Hash *hash), INTVAL value)
1825 ASSERT_ARGS(hash_value_from_int)
1826 void *ret;
1827 switch (hash->entry_type) {
1828 case enum_type_INTVAL:
1829 ret = INTVAL2PTR(void *, value);
1830 break;
1831 case enum_type_PMC:
1833 PMC * const tmp = get_integer_pmc(interp, value);
1834 ret = (void *)tmp;
1836 break;
1837 case enum_type_STRING:
1838 ret = (void *)Parrot_str_from_int(interp, value);
1839 break;
1840 default:
1841 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNIMPLEMENTED,
1842 "Hash: unsupported entry_type");
1844 return ret;
1849 =item C<void* hash_value_from_string(PARROT_INTERP, const Hash *hash, STRING
1850 *value)>
1852 Cast STRING to hash value.
1854 =cut
1858 PARROT_CAN_RETURN_NULL
1859 void*
1860 hash_value_from_string(PARROT_INTERP, ARGIN(const Hash *hash),
1861 ARGIN_NULLOK(STRING *value))
1863 ASSERT_ARGS(hash_value_from_string)
1864 void *ret;
1865 switch (hash->entry_type) {
1866 case enum_type_INTVAL:
1868 const INTVAL int_val = STRING_IS_NULL(value) ?
1869 (INTVAL) 0 : Parrot_str_to_int(interp, value);
1870 ret = INTVAL2PTR(void *, int_val);
1872 break;
1873 case enum_type_STRING:
1874 ret = (void *)value;
1875 break;
1876 case enum_type_PMC:
1878 PMC * const s = STRING_IS_NULL(value) ?
1879 PMCNULL : get_string_pmc(interp, value);
1880 ret = (void *)s;
1882 break;
1883 default:
1884 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNIMPLEMENTED,
1885 "Hash: unsupported entry_type");
1887 return ret;
1892 =item C<void* hash_value_from_pmc(PARROT_INTERP, const Hash *hash, PMC *value)>
1894 Cast PMC to hash value.
1896 =cut
1900 PARROT_CAN_RETURN_NULL
1901 void*
1902 hash_value_from_pmc(PARROT_INTERP, ARGIN(const Hash *hash),
1903 ARGIN_NULLOK(PMC *value))
1905 ASSERT_ARGS(hash_value_from_pmc)
1906 void *ret;
1907 switch (hash->entry_type) {
1908 case enum_type_INTVAL:
1910 const INTVAL int_val = PMC_IS_NULL(value) ?
1911 (INTVAL) 0 : VTABLE_get_integer(interp, value);
1912 ret = INTVAL2PTR(void *, int_val);
1914 break;
1915 case enum_type_STRING:
1916 ret = PMC_IS_NULL(value) ?
1917 PMCNULL : (void *)VTABLE_get_string(interp, value);
1918 break;
1919 case enum_type_PMC:
1920 ret = (void *)value;
1921 break;
1922 default:
1923 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNIMPLEMENTED,
1924 "Hash: unsupported entry_type");
1926 return ret;
1931 =item C<void* hash_value_from_number(PARROT_INTERP, const Hash *hash, FLOATVAL
1932 value)>
1934 Cast FLOATVAL to hash value.
1936 =cut
1940 PARROT_CAN_RETURN_NULL
1941 void*
1942 hash_value_from_number(PARROT_INTERP, ARGIN(const Hash *hash), FLOATVAL value)
1944 ASSERT_ARGS(hash_value_from_number)
1945 void *ret;
1946 switch (hash->entry_type) {
1947 case enum_type_INTVAL:
1949 const INTVAL tmp = value;
1950 ret = (void*)tmp;
1952 break;
1953 case enum_type_STRING:
1954 ret = (void *)Parrot_str_from_num(interp, value);
1955 break;
1956 case enum_type_PMC:
1958 PMC * const tmp = get_number_pmc(interp, value);
1959 ret = (void *)tmp;
1961 break;
1962 default:
1963 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNIMPLEMENTED,
1964 "Hash: unsupported entry_type");
1966 return ret;
1971 =item C<INTVAL hash_value_to_int(PARROT_INTERP, const Hash *hash, void *value)>
1973 Cast hash value to INTVAL.
1975 =cut
1979 INTVAL
1980 hash_value_to_int(PARROT_INTERP, ARGIN(const Hash *hash), ARGIN_NULLOK(void *value))
1982 ASSERT_ARGS(hash_value_to_int)
1983 INTVAL ret;
1984 switch (hash->entry_type) {
1985 case enum_type_ptr:
1986 case enum_type_INTVAL:
1987 ret = (INTVAL)value;
1988 break;
1989 case enum_type_STRING:
1990 ret = Parrot_str_to_int(interp, (STRING*)value);
1991 break;
1992 case enum_type_PMC:
1993 ret = VTABLE_get_integer(interp, (PMC*)value);
1994 break;
1995 default:
1996 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNIMPLEMENTED,
1997 "Hash: unsupported entry_type");
1999 return ret;
2004 =item C<STRING* hash_value_to_string(PARROT_INTERP, const Hash *hash, void
2005 *value)>
2007 Cast hash value to STRING.
2009 =cut
2013 PARROT_CANNOT_RETURN_NULL
2014 STRING*
2015 hash_value_to_string(PARROT_INTERP, ARGIN(const Hash *hash), ARGIN_NULLOK(void *value))
2017 ASSERT_ARGS(hash_value_to_string)
2018 STRING *ret;
2019 switch (hash->entry_type) {
2020 case enum_type_INTVAL:
2021 ret = Parrot_str_from_int(interp, (INTVAL)value);
2022 break;
2023 case enum_type_STRING:
2024 ret = (STRING *)value;
2025 break;
2026 case enum_type_PMC:
2027 ret = VTABLE_get_string(interp, (PMC *)value);
2028 break;
2029 default:
2030 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNIMPLEMENTED,
2031 "Hash: unsupported entry_type");
2033 return ret;
2038 =item C<PMC* hash_value_to_pmc(PARROT_INTERP, const Hash *hash, void *value)>
2040 Cast hash value to PMC.
2042 =cut
2046 PARROT_CANNOT_RETURN_NULL
2047 PMC*
2048 hash_value_to_pmc(PARROT_INTERP, ARGIN(const Hash *hash), ARGIN_NULLOK(void *value))
2050 ASSERT_ARGS(hash_value_to_pmc)
2051 PMC *ret;
2052 switch (hash->entry_type) {
2053 case enum_type_INTVAL:
2054 ret = get_integer_pmc(interp, (INTVAL)value);
2055 break;
2056 case enum_type_STRING:
2057 ret = get_string_pmc(interp, (STRING*)value);
2058 break;
2059 case enum_type_PMC:
2060 ret = (PMC *)value;
2061 break;
2062 default:
2063 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNIMPLEMENTED,
2064 "Hash: unsupported entry_type");
2066 return ret;
2071 =item C<FLOATVAL hash_value_to_number(PARROT_INTERP, const Hash *hash, void
2072 *value)>
2074 Cast hash value to FLOATVAL.
2076 =cut
2080 FLOATVAL
2081 hash_value_to_number(PARROT_INTERP, ARGIN(const Hash *hash), ARGIN_NULLOK(void *value))
2083 ASSERT_ARGS(hash_value_to_number)
2084 FLOATVAL ret;
2085 switch (hash->entry_type) {
2086 case enum_type_INTVAL:
2088 /* Pacify compiler about casting */
2089 const INTVAL tmp = (INTVAL)value;
2090 ret = tmp;
2092 break;
2093 case enum_type_STRING:
2094 ret = Parrot_str_to_num(interp, (STRING*)value);
2095 break;
2096 case enum_type_PMC:
2097 ret = VTABLE_get_number(interp, (PMC*)value);
2098 break;
2099 default:
2100 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNIMPLEMENTED,
2101 "Hash: unsupported entry_type");
2103 return ret;
2108 =back
2110 =head1 SEE ALSO
2112 F<docs/pdds/pdd08_keys.pod>.
2114 =head1 TODO
2116 Future optimizations:
2118 =over 4
2120 =item * Stop reallocating the bucket pool, and instead add chunks on.
2121 (Saves pointer fixups and copying during C<realloc>.)
2123 =item * Hash contraction (don't if it's worth it)
2125 =back
2127 =cut
2133 * Local variables:
2134 * c-file-style: "parrot"
2135 * End:
2136 * vim: expandtab shiftwidth=4: