[TT #871] Add rand as a dynop, with tests
[parrot.git] / src / pmc / orderedhash.pmc
blobf38748adf53d9a25eb008583dbaf6320ff55675b
1 /*
2 Copyright (C) 2001-2009, Parrot Foundation.
3 $Id$
5 =head1 NAME
7 src/pmc/orderedhash.pmc - Ordered Hash
9 =head1 DESCRIPTION
11 C<OrderedHash> extends C<Hash> to provide the interfaces of C<array> and
12 C<hash>. To achieve ordering, hash there are a few restrictions:
13 C<delete_keyed> never removes items; they are just nulled.
15 Please note that if values are set via integer idx, these indices have to be in
16 strict order. Using C<push_xx> simplifies this task.  This creates a key
17 "\1idx" for C<idx> and is therefore not fully transparent.
19 There are two iterator interfaces:
21 =over 4
23 =item * retrieve values (in creation order)
25 Please note that after a C<delete_keyed> operation, iterating over
26 values doesn't work any more.  You'll get an error 'No such key'.
28 =item * retrieve keys (in creation order)
30 =back
32 See F<t/pmc/orderedhash.t>.
34 =head2 Methods
36 =over 4
38 =cut
42 pmclass OrderedHash extends Hash need_ext provides array provides hash {
46 =item C<void mark()>
48 Marks the OrderedHash as live.
50 =cut
54     VTABLE void mark() {
55         Hash * const h = (Hash *)SELF.get_pointer();
56         INTVAL  i;
58         if (!h)
59             return;
61         /* RT #53890 - keys can be NULL on purpose; move to src/hash.c ? */
62         for (i = h->mask; i >= 0; --i) {
63             HashBucket *b = h->bi[i];
65             while (b) {
67                 if (b->key) {
68                     Parrot_gc_mark_PObj_alive(interp, (PObj *)b->key);
69                     if (b->value)
70                         Parrot_gc_mark_PObj_alive(interp, (PObj *)b->value);
71                 }
73                 b = b->next;
74             }
75         }
76     }
80 =item C<PMC *get_iter()>
82 Return a new iterator
84 =cut
88     VTABLE PMC *get_iter() {
89         return pmc_new_init(INTERP, enum_class_OrderedHashIterator, SELF);
90     }
94 =item C<PMC *get_pmc_keyed(PMC *key)>
96 =item C<PMC *get_pmc_keyed_int(INTVAL key)>
98 =item C<PMC *get_pmc_keyed_str(STRING *key)>
100 =cut
104     VTABLE PMC *get_pmc_keyed_int(INTVAL idx) {
105         Hash * const h = (Hash *)SELF.get_pointer();
106         const INTVAL n = h->entries;
107         HashBucket *b;
109         if (idx < 0)
110             idx += n;
112         if (idx < 0 || idx >= n)
113             Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_KEY_NOT_FOUND,
114                 "OrderedHash: index out of bounds!");
116         b = h->bs + idx;
118         if (b->key)
119             return (PMC *)b->value;
121         Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_KEY_NOT_FOUND,
122             "OrderedHash: No such key");
123     }
125     VTABLE PMC *get_pmc_keyed(PMC *key) {
126         if ((PObj_get_FLAGS(key) & KEY_type_FLAGS) == KEY_integer_FLAG) {
127             PMC * const item = SELF.get_pmc_keyed_int(VTABLE_get_integer(INTERP, key));
128             PMC * const next = VTABLE_shift_pmc(INTERP, key);
130             if (!next)
131                 return item;
133             return VTABLE_get_pmc_keyed(INTERP, item, next);
134         }
136         return INTERP->vtables[enum_class_Hash]->get_pmc_keyed(INTERP, SELF, key);
137     }
141 =item C<STRING *get_string_keyed(PMC *key)>
143 =item C<STRING *get_string_keyed_int(INTVAL key)>
145 =item C<STRING *get_string_keyed_str(STRING *key)>
147 =cut
151     VTABLE STRING *get_string_keyed_int(INTVAL idx) {
152         Hash * const h = (Hash *)SELF.get_pointer();
153         const INTVAL n = h->entries;
155         HashBucket *b;
157         if (idx < 0)
158             idx += n;
160         if (idx < 0 || idx >= n)
161             Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_KEY_NOT_FOUND,
162                 "OrderedHash: index out of bounds!");
164         b = h->bs + idx;
166         if (b->key)
167             return VTABLE_get_string(INTERP, (PMC *)b->value);
169         Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_KEY_NOT_FOUND,
170             "OrderedHash: No such key");
171     }
173     VTABLE STRING *get_string_keyed(PMC *key) {
174         if ((PObj_get_FLAGS(key) & KEY_type_FLAGS) == KEY_integer_FLAG) {
175             PMC * const item = SELF.get_pmc_keyed_int(VTABLE_get_integer(INTERP, key));
176             PMC * const next = VTABLE_shift_pmc(INTERP, key);
178             if (!next)
179                 return VTABLE_get_string(INTERP, item);
181             return VTABLE_get_string_keyed(INTERP, item, next);
182         }
184         return INTERP->vtables[enum_class_Hash]->get_string_keyed(INTERP, SELF, key);
185     }
188 =item C<INTVAL get_integer_keyed(PMC *key)>
190 =item C<INTVAL get_integer_keyed_str(STRING *key)>
192 =item C<INTVAL get_integer_keyed_int(INTVAL key)>
194 Returns the integer value associated with C<key>.
196 =cut
200     VTABLE INTVAL get_integer_keyed_int(INTVAL idx) {
201         Hash * const h = (Hash *)SELF.get_pointer();
202         const INTVAL n = h->entries;
203         HashBucket *b;
205         if (idx < 0)
206             idx += n;
208         if (idx < 0 || idx >= n)
209             Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
210                 "OrderedHash: index out of bounds!");
212         b = h->bs + idx;
214         if (b->key)
215             return VTABLE_get_integer(INTERP, (PMC *)b->value);
217         Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_KEY_NOT_FOUND,
218             "OrderedHash: No such key");
219     }
221     VTABLE INTVAL get_integer_keyed(PMC *key) {
223         if ((PObj_get_FLAGS(key) & KEY_type_FLAGS) == KEY_integer_FLAG) {
224             PMC * const item = SELF.get_pmc_keyed_int(VTABLE_get_integer(INTERP, key));
225             PMC * const next = VTABLE_shift_pmc(INTERP, key);
227             if (!next)
228                 return VTABLE_get_integer(INTERP, item);
230             return VTABLE_get_integer_keyed(INTERP, item, next);
231         }
233         return INTERP->vtables[enum_class_Hash]->get_integer_keyed(INTERP, SELF, key);
234     }
238 =item C<FLOATVAL get_number_keyed(PMC *key)>
240 =item C<FLOATVAL get_number_keyed_int(INTVAL key)>
242 =item C<FLOATVAL get_number_keyed_str(STRING *key)>
244 Returns the floating-point value for the element at C<key>.
246 =cut
250     VTABLE FLOATVAL get_number_keyed_int(INTVAL idx) {
251         Hash * const h = (Hash *)SELF.get_pointer();
252         const INTVAL n = h->entries;
253         HashBucket *b;
255         if (idx < 0)
256             idx += n;
258         if (idx < 0 || idx >= n)
259             Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
260                 "OrderedHash: index out of bounds!");
262         b = h->bs + idx;
264         if (b->key)
265             return VTABLE_get_number(INTERP, (PMC *)b->value);
267         Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_KEY_NOT_FOUND,
268             "OrderedHash: No such key");
269     }
271     VTABLE FLOATVAL get_number_keyed(PMC *key) {
272         if ((PObj_get_FLAGS(key) & KEY_type_FLAGS) == KEY_integer_FLAG) {
273             PMC * const item = SELF.get_pmc_keyed_int(VTABLE_get_integer(INTERP, key));
274             PMC * const next = VTABLE_shift_pmc(INTERP, key);
276             if (!next)
277                 return VTABLE_get_number(INTERP, item);
279             return VTABLE_get_number_keyed(INTERP, item, next);
280         }
282         return SUPER(key);
283     }
287 =item C<void set_pmc_keyed_int(INTVAL idx, PMC *val)>
289 =item C<void set_integer_keyed_int(INTVAL key, INTVAL value)>
291 =item C<void set_number_keyed_int(INTVAL key, FLOATVAL value)>
293 =item C<void set_string_keyed_int(INTVAL key, STRING *value)>
295 Sets the PMC value of the element at index C<key> to C<val>.
296 The created key = "\1idx".
298 =cut
302     VTABLE void set_pmc_keyed_int(INTVAL idx, PMC *val) {
303         Hash * const     h = (Hash *)SELF.get_pointer();
304         const INTVAL     n = h->entries;
305         STRING * const fmt = CONST_STRING(INTERP, "\1%d");
307         if (idx < -n)
308             idx = -idx - n - 1;
309         else if (idx < 0)
310             idx += n;
312         if (idx >= n) {
313             /* TODO warn or fill if there are holes */
314             STRING * const key = Parrot_sprintf_s(INTERP, fmt, idx);
315             SELF.set_pmc_keyed_str(key, val);
316         }
317         else {
318             HashBucket * const b = h->bs + idx;
320             if (!b->key)
321                 b->key = Parrot_sprintf_s(INTERP, fmt, idx);
323             b->value     = val;
324         }
325     }
327     VTABLE void set_integer_keyed_int(INTVAL idx, INTVAL value) {
328         PMC * const v = pmc_new(INTERP, Parrot_get_ctx_HLL_type(INTERP,
329                     enum_class_Integer));
330         VTABLE_set_integer_native(INTERP, v, value);
331         SELF.set_pmc_keyed_int(idx, v);
332     }
334     VTABLE void set_number_keyed_int(INTVAL idx, FLOATVAL value) {
335         PMC * const v = pmc_new(INTERP, Parrot_get_ctx_HLL_type(INTERP,
336                     enum_class_Float));
337         VTABLE_set_number_native(INTERP, v, value);
338         SELF.set_pmc_keyed_int(idx, v);
339     }
341     VTABLE void set_string_keyed_int(INTVAL idx, STRING *value) {
342         PMC * const v = pmc_new(INTERP, Parrot_get_ctx_HLL_type(INTERP,
343                     enum_class_String));
344         VTABLE_set_string_native(INTERP, v, value);
345         SELF.set_pmc_keyed_int(idx, v);
346     }
349 =item C<void push_float(FLOATVAL value)>
351 =item C<void push_integer(INTVAL value)>
353 =item C<void push_pmc(PMC *value)>
355 =item C<void push_string(STRING *value)>
357 =cut
361     VTABLE void push_pmc(PMC *value) {
362         const INTVAL n = SELF.elements();
363         SELF.set_pmc_keyed_int(n, value);
364     }
366     VTABLE void push_float(FLOATVAL value) {
367         const INTVAL n = SELF.elements();
368         SELF.set_number_keyed_int(n, value);
369     }
371     VTABLE void push_integer(INTVAL value) {
372         const INTVAL n = SELF.elements();
373         SELF.set_integer_keyed_int(n, value);
374     }
376     VTABLE void push_string(STRING *value) {
377         const INTVAL n = SELF.elements();
378         SELF.set_string_keyed_int(n, value);
379     }
383 =item C<INTVAL exists_keyed(PMC *key)>
385 =item C<INTVAL exists_keyed_str(STRING *key)>
387 =item C<INTVAL exists_keyed_int(INTVAL key)>
389 =cut
393     VTABLE INTVAL exists_keyed_int(INTVAL idx) {
394         Hash * const h = (Hash *)SELF.get_pointer();
395         const INTVAL n = h->entries;
396         HashBucket  *b;
398         if (idx < 0)
399             idx += n;
401         if (idx < 0 || idx >= n)
402             return 0;
404         b = h->bs + idx;
406         if (b->key)
407             return 1;
409         return 0;
410     }
412     VTABLE INTVAL exists_keyed(PMC *key) {
413         if (PObj_get_FLAGS(key) & KEY_integer_FLAG) {
414             PMC        *item, *next;
415             HashBucket *b;
416             Hash       * const h   = (Hash *)SELF.get_pointer();
417             INTVAL             idx = VTABLE_get_integer(INTERP, key);
418             const INTVAL       n   = h->entries;
420             if (idx < 0)
421                 idx += n;
423             if (idx < 0 || idx >= n)
424                 return 0;
426             b = h->bs + idx;
428             if (!b->key)
429                 return 0;
431             item = (PMC *)b->value;
432             next = VTABLE_shift_pmc(INTERP, key);
434             if (!next)
435                 return 1;
437             return VTABLE_exists_keyed(INTERP, item, next);
438         }
440         return SUPER(key);
441     }
443     VTABLE INTVAL exists_keyed_str(STRING *key) {
444         const Hash       * const h = (Hash *)SELF.get_pointer();
445         const HashBucket * const b = parrot_hash_get_bucket(INTERP, h, key);
447         return (b && b->key);
448     }
452 =item C<INTVAL defined_keyed(PMC *key)>
454 =item C<INTVAL defined_keyed_str(STRING *key)>
456 =item C<INTVAL defined_keyed_int(INTVAL key)>
458 =cut
462     VTABLE INTVAL defined_keyed(PMC *key) {
463         if (PObj_get_FLAGS(key) & KEY_integer_FLAG) {
464             Hash * const h   = (Hash *)SELF.get_pointer();
465             INTVAL       idx = VTABLE_get_integer(INTERP, key);
466             const INTVAL n   = h->entries;
468             HashBucket *b;
469             PMC        *item, *next;
471             if (idx < 0)
472                 idx += n;
474             /* XXX non-existent is undefined - is this correct */
475             if (idx < 0 || idx >= n)
476                 return 0;
478             b = h->bs + idx;
480             if (!b->key)
481                 return 0;
483             item = (PMC *)b->value;
484             next = VTABLE_shift_pmc(INTERP, key);
486             if (!next)
487                 return VTABLE_defined(INTERP, item);
489             return VTABLE_defined_keyed(INTERP, item, next);
490         }
492         return SUPER(key);
493     }
495     VTABLE INTVAL defined_keyed_int(INTVAL idx) {
496         Hash * const h = (Hash *)SELF.get_pointer();
497         const INTVAL n = h->entries;
498         HashBucket  *b;
500         if (idx < 0)
501             idx += n;
503         if (idx < 0 || idx >= n)
504             return 0;
506         b = h->bs + idx;
508         if (b->key)
509             return VTABLE_defined(INTERP, (PMC *)b->value);
511         return 0;
512     }
516 =item C<void delete_keyed(PMC *key)>
518 =item C<void delete_keyed_str(STRING *key)>
520 =item C<void delete_keyed_int(INTVAL key)>
522 Deletes the key C<*key> from the hash.
524 =cut
528     VTABLE void delete_keyed(PMC *key) {
529         PMC * const next = key_next(INTERP, key);
531         if (PObj_get_FLAGS(key) & KEY_integer_FLAG) {
532             if (next) {
533                 PMC * const item = SELF.get_pmc_keyed_int(VTABLE_get_integer(INTERP, key));
534                 VTABLE_delete_keyed(INTERP, item, next);
535                 return;
536             }
538             SELF.delete_keyed_int(VTABLE_get_integer(INTERP, key));
539         }
540         else {
541             if (next) {
542                 PMC * const item = SELF.get_pmc_keyed_str(VTABLE_get_string(INTERP, key));
543                 VTABLE_delete_keyed(INTERP, item, next);
544                 return;
545             }
546             SELF.delete_keyed_str(VTABLE_get_string(INTERP, key));
547         }
548     }
550     VTABLE void delete_keyed_int(INTVAL idx) {
551         Hash * const h = (Hash *)SELF.get_pointer();
552         const INTVAL n = h->entries;
553         HashBucket  *b;
555         if (idx < 0)
556             idx += n;
558         if (idx < 0 || idx >= n)
559             return;
561         b = h->bs + idx;
563         if (!b)
564             return;
566         b->key       = NULL;
567         b->value     = NULL;
568     }
570     VTABLE void delete_keyed_str(STRING *key) {
571         const Hash * const h = (Hash *)SELF.get_pointer();
572         HashBucket * const b = parrot_hash_get_bucket(INTERP, h, key);
574         if (!b)
575             return;
577         b->key       = NULL;
578         b->value     = NULL;
579     }
583 =item C<PMC *clone()>
585 Create a clone of the OrderedHash. Non-existent keys are compacted.  Accessing
586 the clone via integers has different indices, if items were deleted.
588 =cut
592     VTABLE PMC *clone() {
593         PMC  * const dest   = pmc_new(INTERP, SELF->vtable->base_type);
594         Hash * const hash   = (Hash *)SELF.get_pointer();
595         Hash * const h_dest = (Hash *)VTABLE_get_pointer(INTERP, dest);
596         UINTVAL     i;
598         for (i = 0; i <= N_BUCKETS(hash->mask-1); i++) {
599             HashBucket * const b      = hash->bs + i;
600             void       * const key    = b->key;
602             if (key)
603                 parrot_hash_put(INTERP, h_dest, key,
604                     (void *)VTABLE_clone(INTERP, (PMC *)b->value));
605         }
607         return dest;
608     }
611 =item C<void visit(visit_info *info)>
613 Used during archiving to visit the elements in the hash.
615 =item C<void freeze(visit_info *info)>
617 Used to archive the hash.
619 =item C<void thaw(visit_info *info)>
621 Used to unarchive the hash.
623 Freeze/thaw are inherited from hash.  Only thaw.visit is special, as we have to
624 preserve key/value order.
626 =cut
630     VTABLE void visit(visit_info *info) {
631         info->container  = SELF;
633         switch (info->what) {
634             case VISIT_THAW_NORMAL:
635             case VISIT_THAW_CONSTANTS:
636                 SUPER(info);
637                 break;
639             case VISIT_FREEZE_NORMAL:
640             case VISIT_FREEZE_AT_DESTRUCT:
641                 {
642                     Hash     * const hash = (Hash *)SELF.get_pointer();
643                     IMAGE_IO * const io   = info->image_io;
644                     const UINTVAL entries = hash->entries;
645                     UINTVAL i;
647                     for (i = 0; i < entries; i++) {
648                         HashBucket * const b   = hash->bs + i;
650                         if (b) {
651                             STRING * const key = (STRING *)b->key;
652                             if (key) {
653                                 VTABLE_push_string(interp, io, key);
654                                 (info->visit_pmc_now)(interp, (PMC *)b->value, info);
655                             }
656                         }
657                     }
658                 }
659                 break;
660             default:
661                 Parrot_ex_throw_from_c_args(interp, NULL,
662                     EXCEPTION_INVALID_OPERATION,
663                     "unhandled visit action (%d)", info->what);
664         }
665     }
670 =back
672 =head1 SEE ALSO
674 F<docs/pdds/pdd08_keys.pod>.
676 =head1 HISTORY
678 Initial rev by leo 2003-08-21.
680 =cut
685  * Local variables:
686  *   c-file-style: "parrot"
687  * End:
688  * vim: expandtab shiftwidth=4:
689  */