2 Copyright (C) 2001-2009, Parrot Foundation.
7 src/pmc/orderedhash.pmc - Ordered Hash
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:
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)
32 See F<t/pmc/orderedhash.t>.
42 pmclass OrderedHash extends Hash need_ext provides array provides hash {
48 Marks the OrderedHash as live.
55 Hash * const h = (Hash *)SELF.get_pointer();
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];
68 Parrot_gc_mark_PObj_alive(interp, (PObj *)b->key);
70 Parrot_gc_mark_PObj_alive(interp, (PObj *)b->value);
80 =item C<PMC *get_iter()>
88 VTABLE PMC *get_iter() {
89 return pmc_new_init(INTERP, enum_class_OrderedHashIterator, SELF);
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)>
104 VTABLE PMC *get_pmc_keyed_int(INTVAL idx) {
105 Hash * const h = (Hash *)SELF.get_pointer();
106 const INTVAL n = h->entries;
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!");
119 return (PMC *)b->value;
121 Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_KEY_NOT_FOUND,
122 "OrderedHash: No such key");
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);
133 return VTABLE_get_pmc_keyed(INTERP, item, next);
136 return INTERP->vtables[enum_class_Hash]->get_pmc_keyed(INTERP, SELF, key);
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)>
151 VTABLE STRING *get_string_keyed_int(INTVAL idx) {
152 Hash * const h = (Hash *)SELF.get_pointer();
153 const INTVAL n = h->entries;
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!");
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");
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);
179 return VTABLE_get_string(INTERP, item);
181 return VTABLE_get_string_keyed(INTERP, item, next);
184 return INTERP->vtables[enum_class_Hash]->get_string_keyed(INTERP, SELF, key);
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>.
200 VTABLE INTVAL get_integer_keyed_int(INTVAL idx) {
201 Hash * const h = (Hash *)SELF.get_pointer();
202 const INTVAL n = h->entries;
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!");
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");
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);
228 return VTABLE_get_integer(INTERP, item);
230 return VTABLE_get_integer_keyed(INTERP, item, next);
233 return INTERP->vtables[enum_class_Hash]->get_integer_keyed(INTERP, SELF, key);
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>.
250 VTABLE FLOATVAL get_number_keyed_int(INTVAL idx) {
251 Hash * const h = (Hash *)SELF.get_pointer();
252 const INTVAL n = h->entries;
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!");
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");
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);
277 return VTABLE_get_number(INTERP, item);
279 return VTABLE_get_number_keyed(INTERP, item, next);
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".
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");
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);
318 HashBucket * const b = h->bs + idx;
321 b->key = Parrot_sprintf_s(INTERP, fmt, idx);
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);
334 VTABLE void set_number_keyed_int(INTVAL idx, FLOATVAL value) {
335 PMC * const v = pmc_new(INTERP, Parrot_get_ctx_HLL_type(INTERP,
337 VTABLE_set_number_native(INTERP, v, value);
338 SELF.set_pmc_keyed_int(idx, v);
341 VTABLE void set_string_keyed_int(INTVAL idx, STRING *value) {
342 PMC * const v = pmc_new(INTERP, Parrot_get_ctx_HLL_type(INTERP,
344 VTABLE_set_string_native(INTERP, v, value);
345 SELF.set_pmc_keyed_int(idx, v);
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)>
361 VTABLE void push_pmc(PMC *value) {
362 const INTVAL n = SELF.elements();
363 SELF.set_pmc_keyed_int(n, value);
366 VTABLE void push_float(FLOATVAL value) {
367 const INTVAL n = SELF.elements();
368 SELF.set_number_keyed_int(n, value);
371 VTABLE void push_integer(INTVAL value) {
372 const INTVAL n = SELF.elements();
373 SELF.set_integer_keyed_int(n, value);
376 VTABLE void push_string(STRING *value) {
377 const INTVAL n = SELF.elements();
378 SELF.set_string_keyed_int(n, value);
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)>
393 VTABLE INTVAL exists_keyed_int(INTVAL idx) {
394 Hash * const h = (Hash *)SELF.get_pointer();
395 const INTVAL n = h->entries;
401 if (idx < 0 || idx >= n)
412 VTABLE INTVAL exists_keyed(PMC *key) {
413 if (PObj_get_FLAGS(key) & KEY_integer_FLAG) {
416 Hash * const h = (Hash *)SELF.get_pointer();
417 INTVAL idx = VTABLE_get_integer(INTERP, key);
418 const INTVAL n = h->entries;
423 if (idx < 0 || idx >= n)
431 item = (PMC *)b->value;
432 next = VTABLE_shift_pmc(INTERP, key);
437 return VTABLE_exists_keyed(INTERP, item, next);
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);
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)>
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;
474 /* XXX non-existent is undefined - is this correct */
475 if (idx < 0 || idx >= n)
483 item = (PMC *)b->value;
484 next = VTABLE_shift_pmc(INTERP, key);
487 return VTABLE_defined(INTERP, item);
489 return VTABLE_defined_keyed(INTERP, item, next);
495 VTABLE INTVAL defined_keyed_int(INTVAL idx) {
496 Hash * const h = (Hash *)SELF.get_pointer();
497 const INTVAL n = h->entries;
503 if (idx < 0 || idx >= n)
509 return VTABLE_defined(INTERP, (PMC *)b->value);
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.
528 VTABLE void delete_keyed(PMC *key) {
529 PMC * const next = key_next(INTERP, key);
531 if (PObj_get_FLAGS(key) & KEY_integer_FLAG) {
533 PMC * const item = SELF.get_pmc_keyed_int(VTABLE_get_integer(INTERP, key));
534 VTABLE_delete_keyed(INTERP, item, next);
538 SELF.delete_keyed_int(VTABLE_get_integer(INTERP, key));
542 PMC * const item = SELF.get_pmc_keyed_str(VTABLE_get_string(INTERP, key));
543 VTABLE_delete_keyed(INTERP, item, next);
546 SELF.delete_keyed_str(VTABLE_get_string(INTERP, key));
550 VTABLE void delete_keyed_int(INTVAL idx) {
551 Hash * const h = (Hash *)SELF.get_pointer();
552 const INTVAL n = h->entries;
558 if (idx < 0 || idx >= n)
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);
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.
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);
598 for (i = 0; i <= N_BUCKETS(hash->mask-1); i++) {
599 HashBucket * const b = hash->bs + i;
600 void * const key = b->key;
603 parrot_hash_put(INTERP, h_dest, key,
604 (void *)VTABLE_clone(INTERP, (PMC *)b->value));
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.
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:
639 case VISIT_FREEZE_NORMAL:
640 case VISIT_FREEZE_AT_DESTRUCT:
642 Hash * const hash = (Hash *)SELF.get_pointer();
643 IMAGE_IO * const io = info->image_io;
644 const UINTVAL entries = hash->entries;
647 for (i = 0; i < entries; i++) {
648 HashBucket * const b = hash->bs + i;
651 STRING * const key = (STRING *)b->key;
653 VTABLE_push_string(interp, io, key);
654 (info->visit_pmc_now)(interp, (PMC *)b->value, info);
661 Parrot_ex_throw_from_c_args(interp, NULL,
662 EXCEPTION_INVALID_OPERATION,
663 "unhandled visit action (%d)", info->what);
674 F<docs/pdds/pdd08_keys.pod>.
678 Initial rev by leo 2003-08-21.
686 * c-file-style: "parrot"
688 * vim: expandtab shiftwidth=4: