[TT #871] Add rand as a dynop, with tests
[parrot.git] / src / pmc / hashiterator.pmc
blob4159317f3515324c9ff29a701ce495e09abf825c
1 /*
2 Copyright (C) 2001-2009, Parrot Foundation.
3 $Id$
5 =head1 NAME
7 src/pmc/hashiterator.pmc - Implementation of Iterator for Hashes.
9 =head1 DESCRIPTION
11 Generic iterator for traversing Hash.
13 =head1 SYNOPSIS
15 =head2 default usage
17     .local pmc iterator, hash, key, entry
18     iterator = iter hash
19   iter_loop:
20     unless iterator, iter_end  # while (more values)
21     key   = shift iterator     # get the key. Some key
22     entry = hash[key]
23     ...
24     goto iter_loop
25   iter_end:
27 =head2 C++-style usage
29     .local pmc iterator, hash, iter_key, key, entry
30     iterator = iter hash
31   iter_loop:
32     unless iterator, iter_end  # while (more values)
33     iter_key = shift iterator     # get the key
34     key = iter_key.'key'()        # get an original key used to put value
35     key = iter_key.'value'()      # get an entry
36     ...
37     goto iter_loop
38   iter_end:
41 =head1 Methods
43 =over 4
45 =cut
49 #include "pmc_hash.h"
50 #include "pmc_hashiteratorkey.h"
54 Advance to next position. Return found (if any) HashBucket.
57 static HashBucket*
58 advance_to_next(PARROT_INTERP, PMC *self) {
59     Parrot_HashIterator_attributes * const attrs  = PARROT_HASHITERATOR(self);
60     HashBucket                            *bucket = attrs->bucket;
62     /* Try to advance current bucket */
63     if (bucket)
64         bucket = bucket->next;
66     while (!bucket) {
67         /* If there is no more buckets */
68         if (attrs->pos == attrs->total_buckets)
69             break;
71         bucket = attrs->parrot_hash->bi[attrs->pos++];
72     }
73     attrs->bucket = bucket;
74     attrs->elements--;
75     return bucket;
78 pmclass HashIterator extends Iterator no_ro {
79     ATTR PMC        *pmc_hash;      /* the Hash which this Iterator iterates */
80     ATTR Hash       *parrot_hash;   /* Underlying implementation of hash */
81     ATTR HashBucket *bucket;        /* Current bucket */
82     ATTR INTVAL      total_buckets; /* Total buckets in index */
83     ATTR INTVAL      pos;           /* Current position in index */
84     ATTR INTVAL      elements;      /* How many elements left to iterate over */
88 =item C<void init_pmc(PMC *initializer)>
90 Initializes the iterator with an aggregate PMC.
91 Defaults iteration mode to iterate from start.
93 =cut
97     VTABLE void init_pmc(PMC *hash) {
98         Parrot_HashIterator_attributes * const attrs =
99             mem_allocate_zeroed_typed(Parrot_HashIterator_attributes);
101         attrs->pmc_hash         = hash;
102         attrs->parrot_hash      = (Hash*)VTABLE_get_pointer(INTERP, hash);
103         attrs->total_buckets    = attrs->parrot_hash->mask + 1;
104         attrs->bucket           = 0;
105         attrs->pos              = 0;
106         /* Will be decreased on initial advance_to_next */
107         /* XXX Do we really need to support this use-case ? */
108         attrs->elements         = attrs->parrot_hash->entries + 1;
109         PMC_data(SELF)          = attrs;
111         PObj_custom_mark_destroy_SETALL(SELF);
113         /* Initial state of iterator is "before start" */
114         /* So, advance to first element */
115         advance_to_next(INTERP, SELF);
116     }
120 =item C<void destroy()>
122 destroys this PMC
124 =cut
128     VTABLE void destroy() {
129         mem_sys_free(PMC_data(SELF));
130         PMC_data(SELF) = NULL;
131     }
135 =item C<void mark()>
137 Marks the hash as live.
139 =cut
143     VTABLE void mark() {
144         PMC *hash = PARROT_HASHITERATOR(SELF)->pmc_hash;
145         if (hash)
146              Parrot_gc_mark_PObj_alive(INTERP, (PObj *)hash);
147         /* We don't mark underlying parrot_hash. Hash PMC will mark it */
148     }
152 =item C<PMC *clone()>
154 =cut
157     VTABLE PMC* clone() {
158         return PMCNULL;
159     }
163 =item C<void set_integer_native()>
165 =cut
168     VTABLE void set_integer_native(INTVAL value) {
169         Parrot_HashIterator_attributes * const attrs =
170                 PARROT_HASHITERATOR(SELF);
172         if (value == 0) {
173             /* Restart iterator */
174             attrs->bucket           = 0;
175             attrs->pos              = 0;
176             advance_to_next(INTERP, SELF);
177             return;
178         }
180         Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
181                 "HashIterator: unknown iterator type");
182     };
186 =item C<PMC *get_pmc()>
188 Returns this Iterator's Hash.
190 =cut
192     VTABLE PMC* get_pmc() {
193         return PARROT_HASHITERATOR(SELF)->pmc_hash;
194     }
198 =item C<INTVAL get_bool()>
200 Returns true if there is more elements to iterate over.
202 =cut
206     VTABLE INTVAL get_bool() {
207         return PARROT_HASHITERATOR(SELF)->bucket != 0;
208     }
212 =item C<INTVAL elements()>
214 Returns the number of remaining elements in the Hash.
216 =cut
220     VTABLE INTVAL elements() {
221         return PARROT_HASHITERATOR(SELF)->elements;
222     }
224     VTABLE INTVAL get_integer() {
225         return SELF.elements();
226     }
230 =item C<PMC *shift_pmc()>
232 Returns the HashIteratorKey for the current position and advance
233 the next one.
235 =cut
239     VTABLE PMC *shift_pmc() {
240         Parrot_HashIterator_attributes * const attrs =
241                 PARROT_HASHITERATOR(SELF);
243         PMC        *ret;
245         if (!attrs->bucket)
246             Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
247                 "StopIteration");
249         ret = pmc_new(INTERP, enum_class_HashIteratorKey);
250         /* Poke directly into HIK. We don't want to create any kind of public API for this */
251         PARROT_HASHITERATORKEY(ret)->parrot_hash = attrs->parrot_hash;
252         PARROT_HASHITERATORKEY(ret)->bucket      = attrs->bucket;
254         /* Move to next bucket */
255         advance_to_next(INTERP, SELF);
257         return ret;
258     }
264     VTABLE STRING* shift_string() {
265         PMC * const key = SELF.shift_pmc();
266         return VTABLE_get_string(INTERP, key);
267     }
273 =back
275 =cut
280  * Local variables:
281  *   c-file-style: "parrot"
282  * End:
283  * vim: expandtab shiftwidth=4:
284  */