[t][cage] Remove PGE-dependence from t/op/inf_nan.t since it is part of 'make coretest'
[parrot.git] / src / list.c
blob18e674e6ea719c9ea86de5ba033b39e71a3d5ee1
1 /*
2 Copyright (C) 2002-2009, Parrot Foundation.
3 License: Artistic 2.0, see README and LICENSE for details
4 $Id$
6 =head1 NAME
8 src/list.c - List aka array routines
10 =head1 DESCRIPTION
12 List is roughly based on concepts of IntList (thanks to Steve),
13 so I don't repeat them here.
15 Especially the same invariants hold, except an empty list is really
16 empty, meaning, push does first check for space.
18 The main differences are:
20 - List can hold items of different size, it's suitable for ints and PMCs
21 ..., calculations are still done in terms of items. The item_size is
22 specified at list creation time with the "type" argument.
24 If you later store different item types in the list, as stated
25 initially, you'll get probably not what you want - so don't do this.
27 - List does auto grow. The caller may implement a different behaviour if
28 she likes.
30 - Error checking for out of bounds access is minimal, caller knows
31 better, what should be done.
33 - List structure itself is different from List_chunk, implying:
35 =over 4
37 =item * end of list is not C<< list->prev >> but C<< list->end >>
39 =item * start of list is list->first
41 =item * the list of chunks is not closed, detecting the end is more simple
43 =item * no spare is keeped, didn't improve due to size constraints
45 =item * the List object itself doesn't move around for shift/unshift
47 =back
49 - list chunks don't have C<< ->start >> and C<< ->end >>
50 fields. Instead the list has C<< ->start >>, which is start of first
51 chunk, and C<< ->cap >>, the total usable capacity in the list.
53 - number of items in chunks are not fixed, but there is a mode
54 using same sized chunks
56 =head2 Grow policy
58 =over 4
60 =item C<enum_grow_fixed>
62 All chunks are of C<MAX_ITEMS> size, chosen, when the first access to
63 the array is indexed and beyond C<MIN_ITEMS> and below 10 *
64 C<MAX_ITEMS>
66 If the first access is beyond 10 * C<MAX_ITEMS> a sparse chunk will
67 be created.
69 To avoid this - and the performance penalty - set the array size
70 before setting elements.
72 new P0, 'Array'
73 set P0, 100000 # sets fixed sized, no sparse
75 This is only meaningful, if a lot of the entries are used too.
77 =item C<enum_grow_growing>
79 Chunk sizes grow from C<MIN_ITEMS> to C<MAX_ITEMS>, this will be selected
80 for pushing data on an empty array.
82 =item C<enum_grow_mixed>
84 Mixture of above chunk types and when sparse chunks are present, or
85 after insert and delete.
87 The chunks hold the information, how many chunks are of the same type,
88 beginning from the current, and how many items are included in this
89 range. See C<get_chunk> below for details.
91 =back
93 =head2 Sparse lists
96 To save memory, List can handle sparse arrays. This code snippet:
98 new P0, 'IntList'
99 set P0[1000000], 42
101 generates 3 List_chunks, one at the beginning of the array, a
102 big sparse chunk and a chunk for the actual data.
104 Setting values inside sparse chunks changes them to real chunks.
105 For poping/shifting inside sparse chunks, s. return value below.
107 =head2 Chunk types
109 =over 4
111 =item C<fixed_items>
113 Have allocated space, size is a power of 2, consecutive chunks are same sized.
115 =item C<grow_items>
117 Same, but consecutive chunks are growing.
119 =item C<no_power_2>
121 Have allocated space but any size.
123 =item C<sparse>
125 Only dummy allocation, C<< chunk->items >> holds the items of this sparse
126 hole.
128 =back
130 =head2 Data types
132 A List can hold various datatypes. See F<src/datatypes.h> for the
133 enumeration of types.
135 Not all are yet implemented in C<Parrot_pmc_array_set>/C<Parrot_pmc_array_item>, see the
136 C<switch()>.
138 Arbitrary length data:
140 Construct initializer with:
142 =over 4
144 =item C<enum_type_sized>
146 =item C<item_size> (in bytes)
148 =item C<items_per_chunk> (rounded up to power of 2, default C<MAX_ITEMS>)
150 =back
152 In C<Parrot_pmc_array_assign> the values are copied into the array, C<Parrot_pmc_array_get>
153 returns a pointer as for all other data types.
155 See F<src/list_2.t> and C<Parrot_pmc_array_new_init()>.
157 =head2 Return value
159 List get functions return a C<(void*)> pointer to the location of the
160 stored data. The caller has to extract the value from this pointer.
162 For non existent data beyond the dimensions of the array a C<NULL>
163 pointer is returned.
165 For non existing data inside sparse holes, a pointer C<(void*)-1> is
166 returned.
168 The caller can decide to assume these data as undef or 0 or whatever is
169 appropriate.
171 =head2 Testing
173 See F<t/src/{int, }list.c> and F<t/pmc/{int, }list.t>.
175 Also all array usage depends on list.
177 =head2 Functions
179 =over 4
181 =cut
185 #include "parrot/parrot.h"
187 /* HEADERIZER HFILE: include/parrot/list.h */
189 /* HEADERIZER BEGIN: static */
190 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
192 PARROT_IGNORABLE_RESULT
193 PARROT_CANNOT_RETURN_NULL
194 static List_chunk * add_chunk_at_end(PARROT_INTERP,
195 ARGMOD(List *list),
196 UINTVAL idx)
197 __attribute__nonnull__(1)
198 __attribute__nonnull__(2)
199 FUNC_MODIFIES(*list);
201 PARROT_IGNORABLE_RESULT
202 PARROT_CANNOT_RETURN_NULL
203 static List_chunk * add_chunk_at_start(PARROT_INTERP,
204 ARGMOD(List *list),
205 UINTVAL idx)
206 __attribute__nonnull__(1)
207 __attribute__nonnull__(2)
208 FUNC_MODIFIES(*list);
210 PARROT_WARN_UNUSED_RESULT
211 PARROT_CANNOT_RETURN_NULL
212 static List_chunk * alloc_next_size(PARROT_INTERP,
213 ARGMOD(List *list),
214 int where,
215 UINTVAL idx)
216 __attribute__nonnull__(1)
217 __attribute__nonnull__(2)
218 FUNC_MODIFIES(*list);
220 PARROT_CANNOT_RETURN_NULL
221 PARROT_WARN_UNUSED_RESULT
222 static List_chunk * allocate_chunk(PARROT_INTERP,
223 ARGIN(List *list),
224 UINTVAL items,
225 UINTVAL size)
226 __attribute__nonnull__(1)
227 __attribute__nonnull__(2);
229 PARROT_WARN_UNUSED_RESULT
230 PARROT_CANNOT_RETURN_NULL
231 static List_chunk * get_chunk(PARROT_INTERP,
232 ARGMOD(List *list),
233 ARGMOD(UINTVAL *idx))
234 __attribute__nonnull__(1)
235 __attribute__nonnull__(2)
236 __attribute__nonnull__(3)
237 FUNC_MODIFIES(*list)
238 FUNC_MODIFIES(*idx);
240 static void Parrot_pmc_array_append(PARROT_INTERP,
241 ARGMOD(List *list),
242 ARGIN_NULLOK(void *item),
243 int type,
244 UINTVAL idx)
245 __attribute__nonnull__(1)
246 __attribute__nonnull__(2)
247 FUNC_MODIFIES(*list);
249 PARROT_WARN_UNUSED_RESULT
250 PARROT_CANNOT_RETURN_NULL
251 static void * Parrot_pmc_array_item(PARROT_INTERP,
252 ARGMOD(List *list),
253 int type,
254 INTVAL idx)
255 __attribute__nonnull__(1)
256 __attribute__nonnull__(2)
257 FUNC_MODIFIES(*list);
259 static void Parrot_pmc_array_set(PARROT_INTERP,
260 ARGMOD(List *list),
261 ARGIN_NULLOK(void *item),
262 INTVAL type,
263 INTVAL idx)
264 __attribute__nonnull__(1)
265 __attribute__nonnull__(2)
266 FUNC_MODIFIES(*list);
268 static void rebuild_chunk_list(PARROT_INTERP, ARGMOD(List *list))
269 __attribute__nonnull__(1)
270 __attribute__nonnull__(2)
271 FUNC_MODIFIES(*list);
273 static void rebuild_chunk_ptrs(ARGMOD(List *list), int cut)
274 __attribute__nonnull__(1)
275 FUNC_MODIFIES(*list);
277 static void rebuild_fix_ends(ARGMOD(List *list))
278 __attribute__nonnull__(1)
279 FUNC_MODIFIES(*list);
281 static void rebuild_other(PARROT_INTERP, ARGMOD(List *list))
282 __attribute__nonnull__(1)
283 __attribute__nonnull__(2)
284 FUNC_MODIFIES(*list);
286 static void rebuild_sparse(ARGMOD(List *list))
287 __attribute__nonnull__(1)
288 FUNC_MODIFIES(*list);
290 static void split_chunk(PARROT_INTERP,
291 ARGMOD(List *list),
292 ARGMOD(List_chunk *chunk),
293 UINTVAL ix)
294 __attribute__nonnull__(1)
295 __attribute__nonnull__(2)
296 __attribute__nonnull__(3)
297 FUNC_MODIFIES(*list)
298 FUNC_MODIFIES(*chunk);
300 #define ASSERT_ARGS_add_chunk_at_end __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
301 PARROT_ASSERT_ARG(interp) \
302 , PARROT_ASSERT_ARG(list))
303 #define ASSERT_ARGS_add_chunk_at_start __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
304 PARROT_ASSERT_ARG(interp) \
305 , PARROT_ASSERT_ARG(list))
306 #define ASSERT_ARGS_alloc_next_size __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
307 PARROT_ASSERT_ARG(interp) \
308 , PARROT_ASSERT_ARG(list))
309 #define ASSERT_ARGS_allocate_chunk __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
310 PARROT_ASSERT_ARG(interp) \
311 , PARROT_ASSERT_ARG(list))
312 #define ASSERT_ARGS_get_chunk __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
313 PARROT_ASSERT_ARG(interp) \
314 , PARROT_ASSERT_ARG(list) \
315 , PARROT_ASSERT_ARG(idx))
316 #define ASSERT_ARGS_Parrot_pmc_array_append __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
317 PARROT_ASSERT_ARG(interp) \
318 , PARROT_ASSERT_ARG(list))
319 #define ASSERT_ARGS_Parrot_pmc_array_item __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
320 PARROT_ASSERT_ARG(interp) \
321 , PARROT_ASSERT_ARG(list))
322 #define ASSERT_ARGS_Parrot_pmc_array_set __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
323 PARROT_ASSERT_ARG(interp) \
324 , PARROT_ASSERT_ARG(list))
325 #define ASSERT_ARGS_rebuild_chunk_list __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
326 PARROT_ASSERT_ARG(interp) \
327 , PARROT_ASSERT_ARG(list))
328 #define ASSERT_ARGS_rebuild_chunk_ptrs __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
329 PARROT_ASSERT_ARG(list))
330 #define ASSERT_ARGS_rebuild_fix_ends __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
331 PARROT_ASSERT_ARG(list))
332 #define ASSERT_ARGS_rebuild_other __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
333 PARROT_ASSERT_ARG(interp) \
334 , PARROT_ASSERT_ARG(list))
335 #define ASSERT_ARGS_rebuild_sparse __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
336 PARROT_ASSERT_ARG(list))
337 #define ASSERT_ARGS_split_chunk __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
338 PARROT_ASSERT_ARG(interp) \
339 , PARROT_ASSERT_ARG(list) \
340 , PARROT_ASSERT_ARG(chunk))
341 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
342 /* HEADERIZER END: static */
344 #define chunk_list_size(list) \
345 (Buffer_buflen(&(list)->chunk_list) / sizeof (List_chunk *))
347 /* hide the ugly cast somehow: */
348 #define chunk_list_ptr(list, idx) \
349 ((List_chunk**) Buffer_bufstart(&(list)->chunk_list))[(idx)]
353 =item C<static List_chunk * allocate_chunk(PARROT_INTERP, List *list, UINTVAL
354 items, UINTVAL size)>
356 Makes a new chunk, and allocates C<size> bytes for buffer storage from the
357 generic memory pool. The chunk holds C<items> items. Marks the chunk as
358 being part of C<< list->container >>, if it exists, for the purposes of GC. Does
359 not install the chunk into C<< list->container >> yet.
361 =cut
365 PARROT_CANNOT_RETURN_NULL
366 PARROT_WARN_UNUSED_RESULT
367 static List_chunk *
368 allocate_chunk(PARROT_INTERP, ARGIN(List *list), UINTVAL items, UINTVAL size)
370 ASSERT_ARGS(allocate_chunk)
371 List_chunk *chunk;
373 Parrot_block_GC_mark(interp);
374 /* Parrot_block_GC_sweep(interp); - why */
376 chunk = (List_chunk *)Parrot_gc_new_bufferlike_header(interp, sizeof (*chunk));
378 chunk->items = items;
379 chunk->n_chunks = 0;
380 chunk->n_items = 0;
381 chunk->next = NULL;
382 chunk->prev = NULL;
383 Parrot_gc_allocate_buffer_storage_aligned(interp, (Buffer *)chunk, size);
384 memset(Buffer_bufstart((Buffer*)chunk), 0, size);
386 Parrot_unblock_GC_mark(interp);
388 /* Parrot_unblock_GC_sweep(interp); */
389 return chunk;
395 =item C<static void rebuild_chunk_ptrs(List *list, int cut)>
397 Rebuilds C<list> and updates/optimizes chunk usage. Deletes empty chunks,
398 counts chunks, and fixes C<prev> pointers.
400 =cut
404 static void
405 rebuild_chunk_ptrs(ARGMOD(List *list), int cut)
407 ASSERT_ARGS(rebuild_chunk_ptrs)
408 List_chunk *chunk, *prev;
409 UINTVAL start = list->start;
410 UINTVAL len = 0;
411 UINTVAL cap = 0;
413 for (prev = NULL, chunk = list->first; chunk; chunk = chunk->next) {
414 /* skip empty chunks, first is empty, when all items get skipped due
415 * to list->start */
416 if (chunk->items == start) {
417 if (prev)
418 prev->next = chunk->next;
419 else
420 list->first = chunk->next;
421 start = 0;
422 continue;
425 len++;
427 start = 0;
428 chunk->prev = prev;
429 prev = chunk;
430 list->last = chunk;
432 if (cut && cap > list->start + list->length && chunk != list->first) {
433 list->last = chunk->prev ? chunk->prev : list->first;
434 len--;
435 break;
438 cap += chunk->items;
440 if (list->last)
441 list->last->next = NULL;
443 list->cap = cap;
445 if (list->first)
446 list->first->prev = NULL;
448 list->n_chunks = len;
454 =item C<static void rebuild_sparse(List *list)>
456 Combines adjacent sparse chunks in C<list>.
458 =cut
462 static void
463 rebuild_sparse(ARGMOD(List *list))
465 ASSERT_ARGS(rebuild_sparse)
467 List_chunk *chunk = list->first;
468 List_chunk *prev = NULL;
469 int changes = 0;
471 for (; chunk; chunk = chunk->next) {
472 if (prev && (prev->flags & sparse) && (chunk->flags & sparse)) {
473 prev->items += chunk->items;
474 chunk->items = 0;
475 changes++;
476 continue;
478 prev = chunk;
481 if (changes)
482 rebuild_chunk_ptrs(list, 0);
488 =item C<static void rebuild_other(PARROT_INTERP, List *list)>
490 Combines adjacent irregular chunks in C<list>.
492 =cut
496 static void
497 rebuild_other(PARROT_INTERP, ARGMOD(List *list))
499 ASSERT_ARGS(rebuild_other)
500 List_chunk *chunk = list->first;
501 List_chunk *prev = NULL;
502 int changes = 0;
504 for (; chunk; chunk = chunk->next) {
505 /* two adjacent irregular chunks */
506 if (prev && (prev->flags & no_power_2) && (chunk->flags & no_power_2)) {
507 /* DONE don't make chunks bigger then MAX_ITEMS, no - make then
508 * but: if bigger, split them in a next pass
509 * TODO test the logic that solves the above problem */
510 if (prev->items + chunk->items > MAX_ITEMS) {
511 Parrot_gc_reallocate_buffer_storage(interp, (Buffer *)prev,
512 MAX_ITEMS * list->item_size);
514 mem_sys_memmove(
515 (char *) Buffer_bufstart(&prev->data) +
516 prev->items * list->item_size,
517 (const char *) Buffer_bufstart(&chunk->data),
518 (MAX_ITEMS - prev->items) * list->item_size);
519 mem_sys_memmove(
520 (char *) Buffer_bufstart(&chunk->data),
521 (const char *) Buffer_bufstart(&chunk->data) +
522 (MAX_ITEMS - prev->items) * list->item_size,
523 (chunk->items - (MAX_ITEMS - prev->items))
524 * list->item_size);
525 chunk->items = chunk->items - (MAX_ITEMS - prev->items);
526 prev->items = MAX_ITEMS;
528 else {
529 Parrot_gc_reallocate_buffer_storage(interp, (Buffer *)prev,
530 (prev->items + chunk->items) * list->item_size);
532 mem_sys_memmove(
533 (char *) Buffer_bufstart(&prev->data) +
534 prev->items * list->item_size,
535 (const char *) Buffer_bufstart(&chunk->data),
536 chunk->items * list->item_size);
537 prev->items += chunk->items;
538 chunk->items = 0;
540 changes++;
541 continue;
543 prev = chunk;
546 if (changes)
547 rebuild_chunk_ptrs(list, 0);
553 =item C<static void rebuild_fix_ends(List *list)>
555 Resets some values in C<list> and the lists's first chunk. Called by
556 C<rebuild_chunk_list()>.
558 =cut
562 static void
563 rebuild_fix_ends(ARGMOD(List *list))
565 ASSERT_ARGS(rebuild_fix_ends)
566 List_chunk * const chunk = list->first;
568 /* first is irregular, next is empty */
569 if (list->n_chunks <= 2 && (chunk->flags & no_power_2)
570 && (!chunk->next
571 || chunk->next->items == 0
572 || list->start + list->length <= chunk->items)) {
574 chunk->flags = 0;
575 list->grow_policy = enum_grow_unknown;
576 list->cap += Buffer_buflen(&chunk->data) / list->item_size - chunk->items;
577 chunk->items = Buffer_buflen(&chunk->data) / list->item_size;
580 /* XXX - still needed? - if last is empty and last->prev not full then
581 * delete last - combine small chunks if list is big */
587 =item C<static void rebuild_chunk_list(PARROT_INTERP, List *list)>
589 Optimizes a modified C<list>: combines adjacent chunks if they are both sparse
590 or irregular and updates the grow policies and computes list statistics.
592 =cut
596 static void
597 rebuild_chunk_list(PARROT_INTERP, ARGMOD(List *list))
599 ASSERT_ARGS(rebuild_chunk_list)
600 List_chunk *chunk, *prev, *first;
601 UINTVAL len;
603 Parrot_block_GC_mark(interp);
604 Parrot_block_GC_sweep(interp);
606 /* count chunks and fix prev pointers */
607 rebuild_chunk_ptrs(list, 0);
609 /* if not regular, check & optimize */
610 if (list->grow_policy == enum_grow_mixed) {
611 rebuild_sparse(list);
612 rebuild_other(interp, list);
613 rebuild_fix_ends(list);
616 /* allocate a new chunk_list buffer, if old one has moved or is too small */
617 len = list->n_chunks;
618 if (list->collect_runs != Parrot_gc_count_collect_runs(interp)
619 || len > chunk_list_size(list)) {
620 /* round up to reasonable size */
621 len = 1 << (ld(len) + 1);
623 if (len < 4)
624 len = 4;
626 Parrot_gc_reallocate_buffer_storage(interp, (Buffer *)list,
627 len * sizeof (List_chunk *));
629 list->collect_runs = Parrot_gc_count_collect_runs(interp);
632 /* reset type, actual state of chunks will show, what we really have */
633 list->grow_policy = enum_grow_unknown;
635 /* fill chunk_list and update statistics */
636 first = chunk = list->first;
637 for (prev = NULL, len = 0; chunk; chunk = chunk->next) {
638 chunk_list_ptr(list, len) = chunk;
639 len++;
641 /* look what type of chunks we have this is always correct: */
642 chunk->n_chunks = 1;
643 chunk->n_items = chunk->items;
645 /* sparse hole or irregular chunk */
646 if (chunk->flags & (sparse | no_power_2)) {
647 List_chunk *next;
649 /* add next sparse or no_power_2 chunks up so that get_chunk will
650 * skip this range of chunks, when the idx is beyond this block. */
651 for (next = chunk->next; next; next = next->next)
652 if (next->flags & (sparse | no_power_2)) {
653 chunk->n_chunks++;
654 chunk->n_items += next->items;
656 else
657 break;
658 first = chunk->next;
659 list->grow_policy = enum_grow_mixed;
660 continue;
663 /* clear flag, next chunks will tell what comes */
664 chunk->flags = enum_grow_unknown;
666 if (first && first != chunk) {
667 /* constant chunk block */
668 if (first->items == chunk->items) {
669 first->n_chunks++;
670 first->n_items += chunk->items;
671 first->flags = fixed_items;
673 /* TODO optimize for fixed but non MAX_ITEMS lists */
674 if (first->items == MAX_ITEMS)
675 list->grow_policy |= enum_grow_fixed;
676 else
677 list->grow_policy |= enum_grow_mixed;
680 /* growing chunk block could optimize small growing blocks, they
681 * are probably not worth the effort. */
682 else if (prev && (prev->items == chunk->items >> 1)) {
683 first->n_chunks++;
684 first->n_items += chunk->items;
685 first->flags = grow_items;
686 list->grow_policy |= enum_grow_growing;
688 /* different growing scheme starts here */
689 else
690 first = chunk;
693 prev = chunk;
696 /* if we have some mixture of grow_policies, then set it to _mixed */
697 if (list->grow_policy && list->grow_policy != enum_grow_growing
698 && list->grow_policy != enum_grow_fixed)
699 list->grow_policy = enum_grow_mixed;
701 Parrot_unblock_GC_mark(interp);
702 Parrot_unblock_GC_sweep(interp);
708 =item C<static List_chunk * alloc_next_size(PARROT_INTERP, List *list, int
709 where, UINTVAL idx)>
711 Calculates the size and number of items for the next chunk and allocates it.
712 Adds the number of allocated items to the list's total, but does not
713 directly add the chunk to the C<list>.
715 =cut
719 PARROT_WARN_UNUSED_RESULT
720 PARROT_CANNOT_RETURN_NULL
721 static List_chunk *
722 alloc_next_size(PARROT_INTERP, ARGMOD(List *list), int where, UINTVAL idx)
724 ASSERT_ARGS(alloc_next_size)
725 List_chunk *new_chunk;
726 UINTVAL items, size;
727 const int much = idx - list->cap >= MIN_ITEMS;
728 int do_sparse = (INTVAL)idx - (INTVAL)list->cap >= 10 * MAX_ITEMS;
730 if (list->item_type == enum_type_sized) {
731 do_sparse = 0;
732 items = list->items_per_chunk;
733 size = items * list->item_size;
735 list->grow_policy = items == MAX_ITEMS ?
736 enum_grow_fixed : enum_grow_mixed;
738 else if (do_sparse) {
739 PARROT_ASSERT(where);
740 /* don't add sparse chunk at start of list */
741 if (!list->n_chunks) {
742 do_sparse = 0;
743 items = MAX_ITEMS;
745 /* if we need more, the next allocation will allocate the rest */
746 size = items * list->item_size;
747 list->grow_policy = enum_grow_fixed;
749 else {
750 /* allocate a dummy chunk holding many items virtually */
751 size = list->item_size;
752 items = idx - list->cap - 1;
754 /* round down this function will then be called again, to add the
755 * final real chunk, with the rest of the needed size */
756 items &= ~(MAX_ITEMS - 1);
757 list->grow_policy = enum_grow_mixed;
760 /* initial size for empty lists grow_policy is not yet known or was
761 * different */
762 else if (!list->cap) {
763 #ifdef ONLY_FIXED_ALLOCATIONS
764 list->grow_policy = enum_grow_fixed;
765 #else
766 list->grow_policy = enum_grow_unknown;
767 #endif
768 /* more then MIN_ITEMS, i.e. indexed access beyond length */
769 if (much) {
770 list->grow_policy = enum_grow_fixed;
771 items = MAX_ITEMS;
773 else {
774 /* TODO make bigger for small items like char */
775 items = MIN_ITEMS;
777 size = items * list->item_size;
779 else {
780 if (list->grow_policy & (enum_grow_fixed | enum_grow_mixed))
781 items = MAX_ITEMS;
782 else {
783 items = where ? list->last->items : list->first->items;
784 /* push: allocate at end, more if possbile */
785 if (where) {
786 if (items < MAX_ITEMS) {
787 items <<= 1;
788 list->grow_policy = enum_grow_growing;
791 /* unshift: if possible, make less items */
792 else {
793 list->grow_policy = enum_grow_growing;
794 if (items > MIN_ITEMS)
795 items >>= 1; /* allocate less */
796 /* if not: second allocation from unshift */
797 else {
798 list->grow_policy = enum_grow_mixed;
799 items = MAX_ITEMS;
803 size = items * list->item_size;
806 new_chunk = allocate_chunk(interp, list, items, size);
807 list->cap += items;
809 if (do_sparse)
810 new_chunk->flags |= sparse;
812 return new_chunk;
818 =item C<static List_chunk * add_chunk_at_start(PARROT_INTERP, List *list,
819 UINTVAL idx)>
821 Adds a new chunk to the start of C<list>.
823 =cut
827 PARROT_IGNORABLE_RESULT
828 PARROT_CANNOT_RETURN_NULL
829 static List_chunk *
830 add_chunk_at_start(PARROT_INTERP, ARGMOD(List *list), UINTVAL idx)
832 ASSERT_ARGS(add_chunk_at_start)
833 List_chunk * const chunk = list->first;
834 List_chunk * const new_chunk = alloc_next_size(interp, list, enum_add_at_start, idx);
836 new_chunk->next = chunk;
837 list->first = new_chunk;
839 if (!list->last)
840 list->last = new_chunk;
842 rebuild_chunk_list(interp, list);
844 return new_chunk;
850 =item C<static List_chunk * add_chunk_at_end(PARROT_INTERP, List *list, UINTVAL
851 idx)>
853 Adds a new chunk to the end of C<list>.
855 =cut
859 PARROT_IGNORABLE_RESULT
860 PARROT_CANNOT_RETURN_NULL
861 static List_chunk *
862 add_chunk_at_end(PARROT_INTERP, ARGMOD(List *list), UINTVAL idx)
864 ASSERT_ARGS(add_chunk_at_end)
865 List_chunk * const chunk = list->last;
866 List_chunk * const new_chunk = alloc_next_size(interp, list, enum_add_at_end, idx);
868 if (chunk)
869 chunk->next = new_chunk;
871 if (!list->first)
872 list->first = new_chunk;
874 list->last = new_chunk;
876 rebuild_chunk_list(interp, list);
878 return new_chunk;
884 =item C<UINTVAL ld(UINTVAL x)>
886 Calculates log2(x), or a useful approximation thereof. Stolen from
887 F<src/malloc.c>.
889 =cut
893 PARROT_EXPORT
894 PARROT_CONST_FUNCTION
895 PARROT_WARN_UNUSED_RESULT
896 UINTVAL
897 ld(UINTVAL x)
899 ASSERT_ARGS(ld)
900 UINTVAL m; /* bit position of highest set bit of m */
902 /* On intel, use BSRL instruction to find highest bit */
903 #if defined(__GNUC__) && defined(i386)
905 __asm__("bsrl %1,%0\n\t":"=r"(m)
906 : "g"(x));
908 #else
911 * Based on branch-free nlz algorithm in chapter 5 of Henry S. Warren
912 * Jr's book "Hacker's Delight". */
914 unsigned int n = ((x - 0x100) >> 16) & 8;
916 x <<= n;
917 m = ((x - 0x1000) >> 16) & 4;
918 n += m;
919 x <<= m;
920 m = ((x - 0x4000) >> 16) & 2;
921 n += m;
922 x = (x << m) >> 14;
923 m = 13 - n + (x & ~(x >> 1));
925 #endif
926 return m;
932 =item C<static List_chunk * get_chunk(PARROT_INTERP, List *list, UINTVAL *idx)>
934 Get the chunk for C<idx>, also update the C<idx> to point into the chunk.
936 This routine will be called for every operation on list, so it's
937 optimized to be fast and needs an up-to-date chunk statistic.
938 C<rebuild_chunk_list> provides the necessary chunk statistics.
940 The scheme of operations is:
942 if all_chunks_are_MAX_ITEMS
943 chunk = chunk_list[ idx / MAX_ITEMS ]
944 idx = idx % MAX_ITEMS
945 done.
947 chunk = first
948 repeat
949 if (index < chunk->items)
950 done.
952 if (index >= items_in_chunk_block)
953 index -= items_in_chunk_block
954 chunk += chunks_in_chunk_block
955 continue
957 calc chunk and index in this block
958 done.
960 One chunk_block consists of chunks of the same type: fixed, growing or
961 other. So the time to look up a chunk doesn't depend on the array
962 length, but on the complexity of the array. C<rebuild_chunk_list> tries
963 to reduce the complexity, but may fail, if you e.g. do a prime sieve by
964 actually C<Parrot_pmc_array_delet>ing the none prime numbers.
966 The complexity of the array is how many different C<chunk_blocks> are
967 there. They come from:
969 - initially fixed: 1
971 - initially growing: 2
973 - first unshift: 1 except for initially fixed arrays
975 - insert: 1 - 3
977 - delete: 1 - 2
979 - sparse hole: 3 (could be 2, code assumes access at either end now)
981 There could be some optimizer that, after detecting almost only indexed access
982 after some time, reorganizes the array to be all C<MAX_ITEMS> sized when this
983 would improve performance.
985 =cut
989 PARROT_WARN_UNUSED_RESULT
990 PARROT_CANNOT_RETURN_NULL
991 static List_chunk *
992 get_chunk(PARROT_INTERP, ARGMOD(List *list), ARGMOD(UINTVAL *idx))
994 ASSERT_ARGS(get_chunk)
995 List_chunk *chunk;
996 UINTVAL i;
998 if (list->collect_runs != Parrot_gc_count_collect_runs(interp))
999 rebuild_chunk_list(interp, list);
1000 #ifdef SLOW_AND_BORING
1001 /* in SLOW_AND_BORING mode, we loop through each chunk, and determine if
1002 idx is in the chunk using basic bounds checking. If the loop completes
1003 without finding idx we panic. "Panic" is probably not the best
1004 reaction, however. */
1005 UNUSED(interp);
1006 for (chunk = list->first; chunk; chunk = chunk->next) {
1007 if (*idx < chunk->items)
1008 return chunk;
1009 *idx -= chunk->items;
1012 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INTERNAL_PANIC,
1013 "Reached end of list %p without finding item index %d\n",
1014 list, *idx);
1015 #endif
1017 /* fixed sized chunks - easy: all MAX_ITEMS sized */
1018 if (list->grow_policy == enum_grow_fixed) {
1019 chunk = chunk_list_ptr(list, *idx >> LD_MAX);
1020 *idx &= MAX_MASK;
1021 return chunk;
1024 /* else look at chunks flags, what grow type follows and adjust chunks and
1025 * idx */
1026 for (i = 0, chunk = list->first; chunk;) {
1027 /* if we have no more items, we have found the chunk */
1028 if (*idx < chunk->items)
1029 return chunk;
1031 /* now look, if we can use the range of items in chunk_block: if idx
1032 * is beyond n_items, skip n_chunks */
1033 if (*idx >= chunk->n_items) {
1034 i += chunk->n_chunks;
1035 *idx -= chunk->n_items;
1036 chunk = chunk_list_ptr(list, i);
1037 continue;
1040 /* we are inside this range of items */
1041 if (chunk->flags & fixed_items) {
1042 /* all chunks are chunk->items big, a power of 2 */
1043 chunk = chunk_list_ptr(list, i + (*idx >> ld(chunk->items)));
1044 *idx &= chunk->items - 1;
1045 return chunk;
1049 * Here is a small table, providing the basics of growing sized
1050 * addressing, for people like me, whose math lessons are +30 years
1051 * in the past ;-)
1052 * assuming MIN_ITEMS=4
1054 * ch# size idx +4 bit ld2(idx) -ld2(4)
1056 * 0 4 0..3 4..7 0000 01xx 2 0
1057 * 1 8 4..11 8..15 0000 1xxx 3 1
1058 * 2 16 12..27 16..31 0001 xxxx 4 2
1059 * ...
1060 * 8 1024 1020.. ...2047 10 8
1063 if (chunk->flags & grow_items) {
1064 /* the next chunks are growing from chunk->items ... last->items */
1065 const UINTVAL ld_first = ld(chunk->items);
1066 const UINTVAL slot = ld(*idx + chunk->items) - ld_first;
1068 /* we are in this growing area, so we are done */
1069 PARROT_ASSERT(slot < chunk->n_chunks);
1070 *idx -= (1 << (ld_first + slot)) - chunk->items;
1071 return chunk_list_ptr(list, i + slot);
1074 if (chunk->flags & (sparse | no_power_2)) {
1075 /* these chunks hold exactly chunk->items */
1076 *idx -= chunk->items;
1077 chunk = chunk->next;
1078 i++;
1079 continue;
1082 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INTERNAL_PANIC,
1083 "Cannot determine how to find location %d in list %p of %d items\n",
1084 *idx, list, list->cap);
1087 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INTERNAL_PANIC,
1088 "Cannot find index %d in list %p of %d items using any method\n",
1089 *idx, list, list->cap);
1095 =item C<static void split_chunk(PARROT_INTERP, List *list, List_chunk *chunk,
1096 UINTVAL ix)>
1098 Splits a sparse chunk, so that we have
1100 - allocated space at C<idx>
1102 if sparse is big:
1104 - C<MAX_ITEMS> near C<idx> and if there is still sparse space after the
1105 real chunk, this also C<n*MAX_ITEMS> sized, so that consecutive writing
1106 would make C<MAX_ITEMS> sized real chunks.
1108 =cut
1112 static void
1113 split_chunk(PARROT_INTERP, ARGMOD(List *list), ARGMOD(List_chunk *chunk), UINTVAL ix)
1115 ASSERT_ARGS(split_chunk)
1116 /* allocate space at idx */
1117 if (chunk->items <= MAX_ITEMS) {
1118 /* it fits, just allocate */
1119 Parrot_gc_reallocate_buffer_storage(interp, (Buffer *)chunk,
1120 chunk->items * list->item_size);
1122 chunk->flags |= no_power_2;
1123 chunk->flags &= ~sparse;
1125 else {
1126 /* split chunk->items: n3 = n*MAX_ITEMS after chunk n2 = MAX_ITEMS
1127 * chunk n1 = rest before */
1128 const INTVAL idx = ix;
1129 const INTVAL n2 = MAX_ITEMS;
1130 const INTVAL n3 = ((chunk->items - idx) / MAX_ITEMS) * MAX_ITEMS;
1131 const INTVAL n1 = chunk->items - n2 - n3;
1133 chunk->items = n2;
1135 Parrot_gc_reallocate_buffer_storage(interp, (Buffer *)chunk,
1136 chunk->items * list->item_size);
1138 chunk->flags &= ~sparse;
1140 if (n3) {
1141 List_chunk * const new_chunk = allocate_chunk(interp, list, n3, list->item_size);
1143 new_chunk->flags |= sparse;
1144 new_chunk->next = chunk->next;
1146 if (chunk->next)
1147 chunk->next = new_chunk;
1148 else
1149 list->last = new_chunk;
1152 /* size before idx */
1153 if (n1 > 0) {
1154 /* insert a new sparse chunk before this one */
1155 List_chunk * const new_chunk = allocate_chunk(interp, list, n1, list->item_size);
1157 new_chunk->flags |= sparse;
1158 new_chunk->next = chunk;
1160 if (chunk->prev)
1161 chunk->prev->next = new_chunk;
1162 else
1163 list->first = new_chunk;
1167 rebuild_chunk_list(interp, list);
1173 =item C<static void Parrot_pmc_array_set(PARROT_INTERP, List *list, void *item,
1174 INTVAL type, INTVAL idx)>
1176 Sets C<item> of type C<type> in chunk at C<idx>.
1178 =cut
1182 static void
1183 Parrot_pmc_array_set(PARROT_INTERP, ARGMOD(List *list), ARGIN_NULLOK(void *item),
1184 INTVAL type, INTVAL idx)
1186 ASSERT_ARGS(Parrot_pmc_array_set)
1187 List_chunk *chunk = get_chunk(interp, list, (UINTVAL *)&idx);
1188 const INTVAL oidx = idx;
1190 PARROT_ASSERT(chunk);
1192 /* if this is a sparse chunk: split in possibly 2 sparse parts before and
1193 * after then make a real chunk, rebuild chunk list and set item */
1194 if (chunk->flags & sparse) {
1195 split_chunk(interp, list, chunk, idx);
1196 /* reget chunk and idx */
1197 idx = oidx;
1198 chunk = get_chunk(interp, list, (UINTVAL *)&idx);
1199 PARROT_ASSERT(chunk);
1200 PARROT_ASSERT(!(chunk->flags & sparse));
1203 switch (type) {
1204 case enum_type_sized:
1205 /* copy data into list */
1206 memcpy(&((char *) Buffer_bufstart(&chunk->data))[idx * list->item_size],
1207 item, list->item_size);
1208 break;
1209 case enum_type_char:
1210 ((char *) Buffer_bufstart(&chunk->data))[idx] = (char)PTR2INTVAL(item);
1211 break;
1212 case enum_type_short:
1213 ((short *) Buffer_bufstart(&chunk->data))[idx] = (short)PTR2INTVAL(item);
1214 break;
1215 case enum_type_int:
1216 ((int *) Buffer_bufstart(&chunk->data))[idx] = (int)PTR2INTVAL(item);
1217 break;
1218 case enum_type_INTVAL:
1219 ((INTVAL *) Buffer_bufstart(&chunk->data))[idx] = PTR2INTVAL(item);
1220 break;
1221 case enum_type_FLOATVAL:
1222 ((FLOATVAL *) Buffer_bufstart(&chunk->data))[idx] = *(FLOATVAL *)item;
1223 break;
1224 case enum_type_PMC:
1225 ((PMC **) Buffer_bufstart(&chunk->data))[idx] = (PMC *)item;
1226 break;
1227 case enum_type_STRING:
1228 ((STRING **) Buffer_bufstart(&chunk->data))[idx] = (STRING *)item;
1229 break;
1230 default:
1231 Parrot_ex_throw_from_c_args(interp, NULL, 1, "Unknown list entry type\n");
1232 break;
1239 =item C<static void * Parrot_pmc_array_item(PARROT_INTERP, List *list, int type,
1240 INTVAL idx)>
1242 Get the pointer to the item of type C<type> in the chunk at C<idx>.
1244 =cut
1248 PARROT_WARN_UNUSED_RESULT
1249 PARROT_CANNOT_RETURN_NULL
1250 static void *
1251 Parrot_pmc_array_item(PARROT_INTERP, ARGMOD(List *list), int type, INTVAL idx)
1253 ASSERT_ARGS(Parrot_pmc_array_item)
1254 List_chunk * const chunk = get_chunk(interp, list, (UINTVAL *)&idx);
1255 /* if this is a sparse chunk return -1, the caller may decide to return 0
1256 * or undef or whatever */
1257 if (chunk->flags & sparse) {
1258 #ifdef INTLIST_EMUL
1259 static int null = 0;
1261 return (void *)&null;
1262 #else
1263 return (void *)-1;
1264 #endif
1267 switch (type) {
1268 case enum_type_sized:
1269 return (void *)&((char *)
1270 Buffer_bufstart(&chunk->data))[idx * list->item_size];
1271 case enum_type_char:
1272 return (void *)&((char *) Buffer_bufstart(&chunk->data))[idx];
1273 case enum_type_short:
1274 return (void *)&((short *) Buffer_bufstart(&chunk->data))[idx];
1275 case enum_type_int:
1276 return (void *)&((int *) Buffer_bufstart(&chunk->data))[idx];
1277 case enum_type_INTVAL:
1278 return (void *)&((INTVAL *) Buffer_bufstart(&chunk->data))[idx];
1279 case enum_type_FLOATVAL:
1280 return (void *)&((FLOATVAL *) Buffer_bufstart(&chunk->data))[idx];
1281 case enum_type_PMC:
1282 return (void *)&((PMC **) Buffer_bufstart(&chunk->data))[idx];
1283 case enum_type_STRING:
1284 return (void *)&((STRING **) Buffer_bufstart(&chunk->data))[idx];
1285 default:
1286 Parrot_ex_throw_from_c_args(interp, NULL, 1, "Unknown list entry type\n");
1293 =item C<static void Parrot_pmc_array_append(PARROT_INTERP, List *list, void
1294 *item, int type, UINTVAL idx)>
1296 Adds one or more chunks to end of list.
1298 =cut
1302 static void
1303 Parrot_pmc_array_append(PARROT_INTERP, ARGMOD(List *list),
1304 ARGIN_NULLOK(void *item), int type, UINTVAL idx)
1306 ASSERT_ARGS(Parrot_pmc_array_append)
1307 /* initially, list may be empty, also used by assign */
1308 while (idx >= list->cap){
1309 add_chunk_at_end(interp, list, idx);
1311 Parrot_pmc_array_set(interp, list, item, type, idx);
1313 /* invariant: prepare for next push */
1314 if (idx >= list->cap - 1)
1315 add_chunk_at_end(interp, list, 0);
1321 =back
1323 =head2 Public Interface Functions
1325 =over 4
1327 =item C<List * Parrot_pmc_array_new(PARROT_INTERP, PARROT_DATA_TYPE type)>
1329 Returns a new list of type C<type>.
1331 =cut
1335 PARROT_EXPORT
1336 PARROT_WARN_UNUSED_RESULT
1337 PARROT_CANNOT_RETURN_NULL
1338 List *
1339 Parrot_pmc_array_new(PARROT_INTERP, PARROT_DATA_TYPE type)
1341 ASSERT_ARGS(Parrot_pmc_array_new)
1342 List * const list = (List *)Parrot_gc_new_bufferlike_header(interp,
1343 sizeof (*list));
1345 list->item_type = type;
1346 switch (type) {
1347 case enum_type_sized: /* gets overridden below */
1348 case enum_type_char:
1349 list->item_size = sizeof (char);
1350 break;
1351 case enum_type_short:
1352 list->item_size = sizeof (short);
1353 break;
1354 case enum_type_int:
1355 list->item_size = sizeof (int);
1356 break;
1357 case enum_type_INTVAL:
1358 list->item_size = sizeof (INTVAL);
1359 break;
1360 case enum_type_FLOATVAL:
1361 list->item_size = sizeof (FLOATVAL);
1362 break;
1363 case enum_type_PMC:
1364 list->item_size = sizeof (PMC *);
1365 break;
1366 case enum_type_STRING:
1367 list->item_size = sizeof (STRING *);
1368 break;
1369 default:
1370 Parrot_ex_throw_from_c_args(interp, NULL, 1, "Unknown list type\n");
1371 break;
1374 return list;
1380 =item C<void Parrot_pmc_array_pmc_new(PARROT_INTERP, PMC *container)>
1382 Creates a new list containing PMC* values in C<PMC_data(container)>.
1384 =cut
1388 PARROT_EXPORT
1389 void
1390 Parrot_pmc_array_pmc_new(PARROT_INTERP, ARGMOD(PMC *container))
1392 ASSERT_ARGS(Parrot_pmc_array_pmc_new)
1394 List * const l = Parrot_pmc_array_new(interp, enum_type_PMC);
1395 l->container = container;
1396 PMC_data(container) = l;
1402 =item C<List * Parrot_pmc_array_new_init(PARROT_INTERP, PARROT_DATA_TYPE type,
1403 PMC *init)>
1405 C<Parrot_pmc_array_new_init()> uses these initializers:
1407 0 ... size (set initial size of list)
1408 1 ... array dimensions (multiarray)
1409 2 ... type (overriding type parameter)
1410 3 ... item_size for enum_type_sized
1411 4 ... items_per_chunk
1413 =cut
1417 PARROT_EXPORT
1418 PARROT_WARN_UNUSED_RESULT
1419 PARROT_CANNOT_RETURN_NULL
1420 List *
1421 Parrot_pmc_array_new_init(PARROT_INTERP, PARROT_DATA_TYPE type, ARGIN(PMC *init))
1423 ASSERT_ARGS(Parrot_pmc_array_new_init)
1424 List *list;
1425 PMC *multi_key = NULL;
1426 INTVAL size = 0;
1427 INTVAL item_size = 0;
1428 INTVAL items_per_chunk = 0;
1430 INTVAL i, len;
1432 if (!init->vtable)
1433 Parrot_ex_throw_from_c_args(interp, NULL, 1,
1434 "Illegal initializer for init\n");
1436 len = VTABLE_elements(interp, init);
1438 if (len & 1)
1439 Parrot_ex_throw_from_c_args(interp, NULL, 1,
1440 "Illegal initializer for init: odd elements\n");
1442 for (i = 0; i < len; i += 2) {
1443 const INTVAL key = VTABLE_get_integer_keyed_int(interp, init, i);
1444 const INTVAL val = i + 1;
1445 switch (key) {
1446 case 0:
1447 size = VTABLE_get_integer_keyed_int(interp, init, val);
1448 break;
1449 case 1:
1450 multi_key = VTABLE_get_pmc_keyed_int(interp, init, val);
1451 break;
1452 case 2:
1454 const INTVAL result =
1455 VTABLE_get_integer_keyed_int(interp, init, val);
1456 type = (PARROT_DATA_TYPE)result;
1458 break;
1459 case 3:
1460 item_size = VTABLE_get_integer_keyed_int(interp, init, val);
1461 break;
1462 case 4:
1463 items_per_chunk = VTABLE_get_integer_keyed_int(
1464 interp, init, val);
1465 break;
1466 default:
1467 Parrot_ex_throw_from_c_args(interp, NULL, 1,
1468 "Invalid initializer for list\n");
1472 list = Parrot_pmc_array_new(interp, type);
1474 if (list->item_type == enum_type_sized) { /* override item_size */
1476 if (!item_size)
1477 Parrot_ex_throw_from_c_args(interp, NULL, 1,
1478 "No item_size for type_sized list\n");
1480 list->item_size = item_size;
1481 list->items_per_chunk =
1482 items_per_chunk
1483 ? (1 << (ld(items_per_chunk) + 1)) /* make power of 2 */
1484 : MAX_ITEMS;
1487 if (size)
1488 Parrot_pmc_array_set_length(interp, list, size);
1490 return list;
1496 =item C<void Parrot_pmc_array_pmc_new_init(PARROT_INTERP, PMC *container, PMC
1497 *init)>
1499 Creates a new list of PMC* values in C<PMC_data(container)>.
1501 =cut
1505 PARROT_EXPORT
1506 void
1507 Parrot_pmc_array_pmc_new_init(PARROT_INTERP, ARGMOD(PMC *container), ARGIN(PMC *init))
1509 ASSERT_ARGS(Parrot_pmc_array_pmc_new_init)
1511 List * const l = Parrot_pmc_array_new_init(interp, enum_type_PMC, init);
1512 l->container = container;
1513 PMC_data(container) = l;
1519 =item C<List * Parrot_pmc_array_clone(PARROT_INTERP, const List *other)>
1521 Returns a clone of the C<other> list.
1523 TODO - Barely tested. Optimize new array structure, fixed if big.
1525 =cut
1529 PARROT_EXPORT
1530 PARROT_WARN_UNUSED_RESULT
1531 PARROT_CANNOT_RETURN_NULL
1532 List *
1533 Parrot_pmc_array_clone(PARROT_INTERP, ARGIN(const List *other))
1535 ASSERT_ARGS(Parrot_pmc_array_clone)
1536 List *l;
1537 List_chunk *chunk, *prev;
1538 UINTVAL i;
1540 Parrot_block_GC_mark(interp);
1541 Parrot_block_GC_sweep(interp);
1543 l = Parrot_pmc_array_new(interp, other->item_type);
1545 STRUCT_COPY(l, other);
1546 Buffer_buflen(&l->chunk_list) = 0;
1547 Buffer_bufstart(&l->chunk_list) = NULL;
1549 for (chunk = other->first, prev = NULL; chunk; chunk = chunk->next) {
1550 List_chunk * const new_chunk = allocate_chunk(interp, l,
1551 chunk->items, Buffer_buflen(&chunk->data));
1553 new_chunk->flags = chunk->flags;
1555 if (!prev)
1556 l->first = new_chunk;
1557 else
1558 prev->next = new_chunk;
1560 prev = new_chunk;
1562 if (!(new_chunk->flags & sparse)) {
1563 switch (l->item_type) {
1564 case enum_type_PMC:
1565 for (i = 0; i < chunk->items; i++) {
1566 PMC * const op = ((PMC **) Buffer_bufstart(&chunk->data))[i];
1568 if (op)
1569 ((PMC **) Buffer_bufstart(&new_chunk->data))[i] =
1570 VTABLE_clone(interp, op);
1572 break;
1573 case enum_type_STRING:
1574 for (i = 0; i < chunk->items; i++) {
1575 STRING *s = ((STRING **) Buffer_bufstart(&chunk->data))[i];
1576 if (s)
1577 ((STRING **) Buffer_bufstart(&new_chunk->data))[i] =
1578 Parrot_str_copy(interp, s);
1580 break;
1581 default:
1582 mem_sys_memcopy(Buffer_bufstart(&new_chunk->data),
1583 Buffer_bufstart(&chunk->data), Buffer_buflen(&chunk->data));
1584 break;
1589 rebuild_chunk_list(interp, l);
1590 Parrot_unblock_GC_mark(interp);
1591 Parrot_unblock_GC_sweep(interp);
1593 return l;
1599 =item C<void Parrot_pmc_array_mark(PARROT_INTERP, List *list)>
1601 Marks the list and its contents as live for the memory management system.
1603 =cut
1607 PARROT_EXPORT
1608 void
1609 Parrot_pmc_array_mark(PARROT_INTERP, ARGMOD(List *list))
1611 ASSERT_ARGS(Parrot_pmc_array_mark)
1612 List_chunk *chunk;
1614 for (chunk = list->first; chunk; chunk = chunk->next) {
1615 Parrot_gc_mark_PObj_alive(interp, (PObj *)chunk);
1617 if (list->item_type == enum_type_PMC
1618 || list->item_type == enum_type_STRING) {
1619 if (!(chunk->flags & sparse)) {
1620 PObj **p = ((PObj **) Buffer_bufstart(&chunk->data));
1621 UINTVAL i;
1623 for (i = 0; i < chunk->items; i++, ++p) {
1624 if (*p)
1625 Parrot_gc_mark_PObj_alive(interp, *p);
1632 Parrot_gc_mark_PObj_alive(interp, (PObj *)list);
1638 =item C<void Parrot_pmc_array_visit(PARROT_INTERP, List *list, void *pinfo)>
1640 This is used by freeze/thaw to visit the contents of the list.
1642 C<pinfo> is the visit info, (see include/parrot/pmc_freeze.h>).
1644 =cut
1648 PARROT_EXPORT
1649 void
1650 Parrot_pmc_array_visit(PARROT_INTERP, ARGIN(List *list), ARGMOD(void *pinfo))
1652 ASSERT_ARGS(Parrot_pmc_array_visit)
1653 List_chunk *chunk;
1654 visit_info * const info = (visit_info*) pinfo;
1655 UINTVAL idx;
1657 const UINTVAL n = Parrot_pmc_array_length(interp, list);
1658 PARROT_ASSERT(list->item_type == enum_type_PMC);
1660 for (idx = 0, chunk = list->first; chunk; chunk = chunk->next) {
1661 /* TODO deleted elements */
1662 if (!(chunk->flags & sparse)) {
1663 UINTVAL i;
1664 for (i = 0; i < chunk->items && idx < n; i++, idx++) {
1665 PMC ** const pos = ((PMC **) Buffer_bufstart(&chunk->data)) + i;
1666 info->thaw_ptr = pos;
1667 (info->visit_pmc_now)(interp, *pos, info);
1671 * TODO handle sparse
1679 =item C<INTVAL Parrot_pmc_array_length(PARROT_INTERP, const List *list)>
1681 Returns the length of the list.
1683 =cut
1687 PARROT_WARN_UNUSED_RESULT
1688 PARROT_PURE_FUNCTION
1689 INTVAL
1690 Parrot_pmc_array_length(SHIM_INTERP, ARGIN(const List *list))
1692 ASSERT_ARGS(Parrot_pmc_array_length)
1693 return list->length;
1699 =item C<void Parrot_pmc_array_set_length(PARROT_INTERP, List *list, INTVAL len)>
1701 Sets the length of the list to C<len>.
1703 =cut
1707 PARROT_EXPORT
1708 void
1709 Parrot_pmc_array_set_length(PARROT_INTERP, ARGMOD(List *list), INTVAL len)
1711 ASSERT_ARGS(Parrot_pmc_array_set_length)
1713 if (len < 0)
1714 len += list->length;
1716 if (len >= 0) {
1717 const UINTVAL idx = list->start + (UINTVAL)len;
1718 list->length = len;
1720 if (idx >= list->cap) {
1721 /* assume user will fill it, so don't generate sparse chunks */
1722 if (!list->cap && idx > MAX_ITEMS) {
1723 while (idx - MAX_ITEMS >= list->cap) {
1724 add_chunk_at_end(interp, list, list->cap + MAX_ITEMS);
1728 Parrot_pmc_array_append(interp, list, NULL, list->item_type, idx);
1730 else
1731 rebuild_chunk_ptrs(list, 1);
1733 rebuild_chunk_list(interp, list);
1740 =item C<void Parrot_pmc_array_insert(PARROT_INTERP, List *list, INTVAL idx,
1741 INTVAL n_items)>
1743 Makes room for C<n_items> at C<idx>.
1745 =cut
1749 PARROT_EXPORT
1750 void
1751 Parrot_pmc_array_insert(PARROT_INTERP, ARGMOD(List *list), INTVAL idx, INTVAL n_items)
1753 ASSERT_ARGS(Parrot_pmc_array_insert)
1754 List_chunk *chunk;
1756 PARROT_ASSERT(idx >= 0);
1757 idx += list->start;
1759 PARROT_ASSERT(n_items >= 0);
1760 if (n_items == 0)
1761 return;
1763 /* empty list */
1764 if (idx >= (INTVAL)list->cap) {
1765 idx += n_items;
1766 list->length = idx;
1768 while (idx >= (INTVAL)list->cap)
1769 add_chunk_at_end(interp, list, idx);
1771 return;
1774 list->length += n_items;
1775 list->cap += n_items;
1776 chunk = get_chunk(interp, list, (UINTVAL *)&idx);
1778 /* the easy case: */
1779 if (chunk->flags & sparse)
1780 chunk->items += n_items;
1781 else {
1782 List_chunk *new_chunk;
1783 INTVAL items;
1785 /* 1. cut this chunk at idx */
1786 list->grow_policy = enum_grow_mixed;
1788 /* allocate a sparse chunk, n_items big */
1789 new_chunk = allocate_chunk(interp, list, n_items, list->item_size);
1790 new_chunk->flags |= sparse;
1791 items = chunk->items - idx;
1793 if (items) {
1794 /* allocate a small chunk, holding the rest of chunk beyond idx */
1795 List_chunk *rest = allocate_chunk(interp, list, items,
1796 items * list->item_size);
1798 chunk->flags = no_power_2;
1799 rest->flags |= no_power_2;
1801 /* hang them together */
1802 rest->next = chunk->next;
1803 chunk->next = new_chunk;
1804 new_chunk->next = rest;
1806 /* copy data over */
1807 mem_sys_memmove(
1808 (char *)Buffer_bufstart(&rest->data),
1809 (char *)Buffer_bufstart(&chunk->data) + idx * list->item_size,
1810 items * list->item_size);
1812 else {
1813 new_chunk->next = chunk->next;
1814 chunk->next = new_chunk;
1817 chunk->items = idx;
1820 rebuild_chunk_list(interp, list);
1826 =item C<void Parrot_pmc_array_delete(PARROT_INTERP, List *list, INTVAL idx,
1827 INTVAL n_items)>
1829 Deletes C<n_items> at C<idx>.
1831 =cut
1835 PARROT_EXPORT
1836 void
1837 Parrot_pmc_array_delete(PARROT_INTERP, ARGMOD(List *list), INTVAL idx, INTVAL n_items)
1839 ASSERT_ARGS(Parrot_pmc_array_delete)
1840 List_chunk *chunk;
1842 PARROT_ASSERT(idx >= 0);
1843 PARROT_ASSERT(n_items >= 0);
1845 if (n_items == 0)
1846 return;
1848 idx += list->start;
1849 chunk = get_chunk(interp, list, (UINTVAL *)&idx);
1851 /* deleting beyond end? */
1852 if (idx + n_items > (INTVAL)list->length)
1853 n_items = list->length - idx;
1855 list->length -= n_items;
1856 list->cap -= n_items;
1857 list->grow_policy = enum_grow_mixed;
1859 while (n_items > 0) {
1860 if (idx + n_items <= (INTVAL)chunk->items) {
1861 /* chunk is bigger then we want to delete */
1862 if (!(chunk->flags & sparse)) {
1863 chunk->flags = no_power_2;
1864 if (idx + n_items <= (INTVAL)chunk->items) {
1865 #ifdef __LCC__
1866 /* LCC has a bug where it can't handle all the temporary
1867 * variables created in this one line. adding an explicit
1868 * one fixes things. No need to force this workaround on
1869 * less brain-damaged compilers though */
1870 size_t tmp_size = (chunk->items - idx - n_items) *
1871 list->item_size;
1873 mem_sys_memmove(
1874 (char *) Buffer_bufstart(&chunk->data) +
1875 idx * list->item_size,
1876 (char *) Buffer_bufstart(&chunk->data) +
1877 (idx + n_items) * list->item_size, tmp_size);
1878 #else
1879 mem_sys_memmove(
1880 (char *) Buffer_bufstart(&chunk->data) +
1881 idx * list->item_size,
1882 (char *) Buffer_bufstart(&chunk->data) +
1883 (idx + n_items) * list->item_size,
1884 (chunk->items - idx - n_items) * list->item_size);
1885 #endif
1888 chunk->items -= n_items;
1889 break;
1892 if (idx == 0 && n_items >= (INTVAL)chunk->items) {
1893 /* delete this chunk */
1894 n_items -= chunk->items;
1896 /* rebuild_chunk_list will kill it because: */
1897 chunk->items = 0;
1899 if (!chunk->prev)
1900 list->first = chunk->next;
1902 else if (idx) {
1903 /* else shrink chunk, it starts at idx then */
1904 if (!(chunk->flags & sparse))
1905 chunk->flags = no_power_2;
1907 n_items -= chunk->items - idx;
1908 chunk->items = idx;
1911 idx = 0;
1912 chunk = chunk->next;
1915 rebuild_chunk_ptrs(list, 1);
1916 rebuild_chunk_list(interp, list);
1922 =item C<void Parrot_pmc_array_push(PARROT_INTERP, List *list, void *item, int
1923 type)>
1925 Pushes C<item> of type C<type> on to the end of the list.
1927 =cut
1931 PARROT_EXPORT
1932 void
1933 Parrot_pmc_array_push(PARROT_INTERP, ARGMOD(List *list), ARGIN_NULLOK(void *item), int type)
1935 ASSERT_ARGS(Parrot_pmc_array_push)
1936 const INTVAL idx = list->start + list->length++;
1938 Parrot_pmc_array_append(interp, list, item, type, idx);
1944 =item C<void Parrot_pmc_array_unshift(PARROT_INTERP, List *list, void *item, int
1945 type)>
1947 Pushes C<item> of type C<type> on to the start of the list.
1949 =cut
1953 PARROT_EXPORT
1954 void
1955 Parrot_pmc_array_unshift(PARROT_INTERP, ARGMOD(List *list), ARGIN(void *item), int type)
1957 ASSERT_ARGS(Parrot_pmc_array_unshift)
1958 List_chunk *chunk;
1960 if (list->start == 0) {
1961 chunk = add_chunk_at_start(interp, list, 0);
1962 list->start = chunk->items;
1965 /* XXX This chunk is unused. Why are we getting it? */
1966 else
1967 chunk = list->first;
1969 Parrot_pmc_array_set(interp, list, item, type, --list->start);
1970 list->length++;
1976 =item C<void * Parrot_pmc_array_pop(PARROT_INTERP, List *list, int type)>
1978 Removes and returns the last item of type C<type> from the end of the list.
1980 =cut
1984 PARROT_EXPORT
1985 PARROT_CAN_RETURN_NULL
1986 void *
1987 Parrot_pmc_array_pop(PARROT_INTERP, ARGMOD(List *list), int type)
1989 ASSERT_ARGS(Parrot_pmc_array_pop)
1990 List_chunk *chunk = list->last;
1991 UINTVAL idx;
1993 if (list->length == 0)
1994 return NULL;
1996 idx = list->start + --list->length;
1998 if (list->length == 0)
1999 list->start = 0;
2001 /* shrink array if necessary */
2002 if (idx < list->cap - chunk->items) {
2003 list->cap -= chunk->items;
2004 chunk = list->last = chunk->prev;
2005 chunk->next = NULL;
2007 if (list->n_chunks <= 2)
2008 list->first = list->last;
2010 rebuild_chunk_list(interp, list);
2013 return Parrot_pmc_array_item(interp, list, type, idx);
2019 =item C<void * Parrot_pmc_array_shift(PARROT_INTERP, List *list, int type)>
2021 Removes and returns the first item of type C<type> from the start of the list.
2023 =cut
2027 PARROT_EXPORT
2028 PARROT_CAN_RETURN_NULL
2029 void *
2030 Parrot_pmc_array_shift(PARROT_INTERP, ARGMOD(List *list), int type)
2032 ASSERT_ARGS(Parrot_pmc_array_shift)
2033 void *ret;
2034 List_chunk *chunk = list->first;
2035 UINTVAL idx;
2037 if (list->length == 0)
2038 return NULL;
2040 idx = list->start;
2042 /* optimize push + shift on empty lists */
2043 if (--list->length == 0)
2044 list->start = 0;
2045 else
2046 list->start++;
2048 ret = Parrot_pmc_array_item(interp, list, type, idx);
2050 if (list->start >= chunk->items) {
2051 list->cap -= chunk->items;
2052 chunk = list->first = chunk->next ? chunk->next : list->last;
2053 list->start = 0;
2055 rebuild_chunk_list(interp, list);
2057 if (list->n_chunks == 1)
2058 list->last = list->first;
2061 return ret;
2067 =item C<void Parrot_pmc_array_assign(PARROT_INTERP, List *list, INTVAL idx, void
2068 *item, int type)>
2070 Assigns C<item> of type C<type> to index C<idx>.
2072 =cut
2076 PARROT_EXPORT
2077 void
2078 Parrot_pmc_array_assign(PARROT_INTERP, ARGMOD(List *list), INTVAL idx,
2079 ARGIN_NULLOK(void *item), int type)
2081 ASSERT_ARGS(Parrot_pmc_array_assign)
2082 const INTVAL length = list->length;
2084 if (idx < -length)
2085 idx = -idx - length - 1;
2086 else if (idx < 0)
2087 idx += length;
2088 if (idx >= length) {
2089 Parrot_pmc_array_append(interp, list, item, type, list->start + idx);
2090 list->length = idx + 1;
2092 else
2093 Parrot_pmc_array_set(interp, list, item, type, list->start + idx);
2099 =item C<void * Parrot_pmc_array_get(PARROT_INTERP, List *list, INTVAL idx, int
2100 type)>
2102 Returns the item of type C<type> at index C<idx>.
2104 =cut
2108 PARROT_EXPORT
2109 PARROT_CAN_RETURN_NULL
2110 PARROT_WARN_UNUSED_RESULT
2111 void *
2112 Parrot_pmc_array_get(PARROT_INTERP, ARGMOD(List *list), INTVAL idx, int type)
2114 ASSERT_ARGS(Parrot_pmc_array_get)
2115 const INTVAL length = list->length;
2117 if (idx >= length || -idx > length)
2118 return NULL;
2120 if (idx < 0)
2121 idx += length;
2123 idx += list->start;
2125 return Parrot_pmc_array_item(interp, list, type, idx);
2131 =item C<void Parrot_pmc_array_splice(PARROT_INTERP, List *list, List
2132 *value_list, INTVAL offset, INTVAL count)>
2134 Replaces C<count> items starting at C<offset> with the items in C<value>.
2136 If C<count> is 0 then the items in C<value> will be inserted after C<offset>.
2138 =cut
2142 PARROT_EXPORT
2143 void
2144 Parrot_pmc_array_splice(PARROT_INTERP, ARGMOD(List *list), ARGMOD_NULLOK(List *value_list),
2145 INTVAL offset, INTVAL count)
2147 ASSERT_ARGS(Parrot_pmc_array_splice)
2148 const INTVAL value_length = value_list ? value_list->length : 0;
2149 const INTVAL length = list->length;
2150 const int type = list->item_type;
2151 INTVAL i, j;
2153 if (value_list && type != value_list->item_type)
2154 Parrot_ex_throw_from_c_args(interp, NULL, 1,
2155 "Item type mismatch in splice\n");
2157 /* start from end */
2158 if (offset < 0) {
2159 offset += length;
2160 if (offset < 0)
2161 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_OUT_OF_BOUNDS,
2162 "illegal splice offset\n");
2165 /* "leave that many elements off the end of the array" */
2166 if (count < 0) {
2167 count += length - offset + 1;
2168 if (count < 0)
2169 count = 0;
2172 /* replace count items at offset with values */
2173 for (i = j = 0; i < count && j < value_length; i++, j++) {
2174 void *val = Parrot_pmc_array_get(interp, value_list, j, type);
2176 /* no clone here, if the HL wants to reuse the values, the HL has to */
2177 /* clone the values */
2179 /* XXX We don't know that val is non-NULL coming back from Parrot_pmc_array_get. */
2180 /* We need to check that we're not dereferencing NULL. */
2181 if (type == enum_type_PMC)
2182 val = *(PMC **)val;
2183 else if (type == enum_type_STRING)
2184 val = *(STRING **)val;
2186 Parrot_pmc_array_assign(interp, list, offset + i, val, type);
2189 /* if we still have values in value_list, insert them */
2190 if (j < value_length) {
2191 /* make room for the remaining values */
2192 Parrot_pmc_array_insert(interp, list, offset + i, value_length - j);
2194 for (; j < value_length; i++, j++) {
2195 void *val = Parrot_pmc_array_get(interp, value_list, j, type);
2197 /* XXX We don't know that val is non-NULL coming back from
2198 * Parrot_pmc_array_get; check that we're not dereferencing NULL. */
2199 if (type == enum_type_PMC)
2200 val = *(PMC **)val;
2201 else if (type == enum_type_STRING)
2202 val = *(STRING **)val;
2204 Parrot_pmc_array_assign(interp, list, offset + i, val, type);
2207 /* else delete the rest */
2208 else
2209 Parrot_pmc_array_delete(interp, list, offset + i, count - i);
2215 =back
2217 =cut
2222 * Local variables:
2223 * c-file-style: "parrot"
2224 * End:
2225 * vim: expandtab shiftwidth=4: