NAME
src/list.c - List aka array routines
DESCRIPTION
List is roughly based on concepts of IntList (thanks to Steve), so I don't repeat them here.
Especially the same invariants hold, except an empty list is really empty, meaning, push does first check for space.
The main differences are:
- List can hold items of different size, it's suitable for ints and PMCs ..., calculations are still done in terms of items. The item_size is specified at list creation time with the "type" argument.
If you later store different item types in the list, as stated initially, you'll get probably not what you want - so don't do this.
- List does auto grow. The caller may implement a different behaviour if she likes.
- Error checking for out of bounds access is minimal, caller knows better, what should be done.
- List structure itself is different from List_chunk, implying:
- end of list is not
list->prev
butlist->end
- start of list is list->first
- the list of chunks is not closed, detecting the end is more simple
- no spare is keeped, didn't improve due to size constraints
- the List object itself doesn't move around for shift/unshift
- list chunks don't have ->start
and ->end
fields.
Instead the list has ->start
,
which is start of first chunk,
and ->cap
,
the total usable capacity in the list.
- number of items in chunks are not fixed, but there is a mode using same sized chunks
Grow policy
enum_grow_fixed
All chunks are of enum_grow_growing
Chunk sizes grow from enum_grow_mixed
Mixture of above chunk types and when sparse chunks are present, or after insert and delete.The chunks hold the information, how many chunks are of the same type, beginning from the current, and how many items are included in this range. See
MAX_ITEMS
size,
chosen,
when the first access to the array is indexed and beyond MIN_ITEMS
and below 10 * MAX_ITEMS
If the first access is beyond 10 * MAX_ITEMS
a sparse chunk will be created.To avoid this - and the performance penalty - set the array size before setting elements.
new P0, 'Array' set P0, 100000 # sets fixed sized, no sparseThis is only meaningful, if a lot of the entries are used too.
MIN_ITEMS
to MAX_ITEMS
, this will be selected for pushing data on an empty array.
get_chunk
below for details.Sparse lists
To save memory, List can handle sparse arrays. This code snippet:
new P0, 'IntList' set P0[1000000], 42
generates 3 List_chunks, one at the beginning of the array, a big sparse chunk and a chunk for the actual data.
Setting values inside sparse chunks changes them to real chunks. For poping/shifting inside sparse chunks, s. return value below.
Chunk types
fixed_items
Have allocated space, size is a power of 2, consecutive chunks are same sized.
grow_items
Same, but consecutive chunks are growing.
no_power_2
Have allocated space but any size.
sparse
Only dummy allocation,
chunk->items
holds the items of this sparse hole.Data types
A List can hold various datatypes. See src/datatypes.h for the enumeration of types.
Not all are yet implemented in list_set
/list_item
, see the switch()
.
Arbitrary length data:
Construct initializer with:
In list_assign
the values are copied into the array, list_get
returns a pointer as for all other data types.
See src/list_2.t and list_new_init()
.
Return value
List get functions return a (void*)
pointer to the location of the stored data. The caller has to extract the value from this pointer.
For non existent data beyond the dimensions of the array a NULL
pointer is returned.
For non existing data inside sparse holes, a pointer (void*)-1
is returned.
The caller can decide to assume these data as undef or 0 or whatever is appropriate.
Testing
See t/src/{int, }list.c and t/pmc/{int, }list.t.
Also all array usage depends on list.
Functions
static List_chunk *allocate_chunk
Makes a new chunk, and allocates static void rebuild_chunk_ptrs
Rebuilds static void rebuild_sparse
Combines together adjacent sparse chunks in static void rebuild_other
Combines together adjacent irregular chunks in static void rebuild_fix_ends
Resets some values in static UINTVAL rebuild_chunk_list
Optimises static List_chunk *alloc_next_size
Calculates the size and number of items for the next chunk and allocates it. Adds the number of allocated items to the list's total, but does not directly add the chunk to the static List_chunk *add_chunk
Adds a new chunk to the UINTVAL ld
Calculates log2(x), or a useful approximation thereof. Stolen from src/malloc.c.
static List_chunk *get_chunk
Get the chunk for static void split_chunk
Splits a sparse chunk, so that we have- allocated space at static void list_set
Sets static void *list_item
Get the pointer to the item of type static void list_append
Adds one or more chunks to end of list.
size
bytes for buffer storage from the generic memory pool. The chunk holds items
items. Marks the chunk as being part of list->container
, if it exists, for the purposes of GC. Does not install the chunk into list->container
yet.
list
and updates/optimizes chunk usage. Deletes empty chunks, counts chunks and fixes prev
pointers.
list
.
list
.
list
and the lists's first chunk. Called by rebuild_chunk_list()
.
list
when it's been modified in some way. Combines adjacent chunks if they are both sparse or irregular. Updates the grow policies and computes list statistics.
list
.
list
. If where
is 0, the chunk is added to the front of the list. If 0, it is added to the end of the list.
idx
, also update the idx
to point into the chunk.This routine will be called for every operation on list, so it's optimized to be fast and needs an up-to-date chunk statistic. rebuild_chunk_list
provides the necessary chunk statistics.The scheme of operations is:
if all_chunks_are_MAX_ITEMS chunk = chunk_list[ idx / MAX_ITEMS ] idx = idx % MAX_ITEMS done. chunk = first repeat if (index < chunk->items) done. if (index >= items_in_chunk_block) index -= items_in_chunk_block chunk += chunks_in_chunk_block continue calc chunk and index in this block done.One chunk_block consists of chunks of the same type: fixed, growing or other. So the time to look up a chunk doesn't depend on the array length, but on the complexity of the array.
rebuild_chunk_list
tries to reduce the complexity, but may fail, if you e.g. do a prime sieve by actually list_delet
ing the none prime numbers.The complexity of the array is how many different chunk_blocks
are there. They come from:- initially fixed: 1- initially growing: 2- first unshift: 1 except for initially fixed arrays- insert: 1 - 3- delete: 1 - 2- sparse hole: 3 (could be 2, code assumes access at either end now)There could be some optimizer, that, after detecting almost only indexed access after some time, does reorganize the array to be all MAX_ITEMS
sized, when this would improve performance.
idx
if sparse is big:- MAX_ITEMS
near idx
and if there is still sparse space after the real chunk, this also n*MAX_ITEMS
sized, so that consecutive writing would make MAX_ITEMS
sized real chunks.
item
of type type
in chunk at idx
.
type
in the chunk at idx
.
Public Interface Functions
List *list_new
Returns a new list of type void list_pmc_new
Creates a new list containing PMC* values in List *list_new_init
void list_pmc_new_init
Creates a new list of PMC* values in List *list_clone
Returns a clone of the void list_mark
Marks the list and its contents as live for the memory management system.
void list_visit
This is used by freeze/thaw to visit the contents of the list.INTVAL list_length
Returns the length of the list.
void list_set_length
Sets the length of the list to void list_insert
Makes room for void list_delete
Deletes void list_push
Pushes void list_unshift
Pushes void *list_pop
Removes and returns the last item of type void *list_shift
Removes and returns the first item of type void list_assign
Assigns void *list_get
Returns the item of type void list_splice
Replaces
type
.
PMC_data(container)
.
list_new_init()
uses these initializers:
0 ... size (set initial size of list) 1 ... array dimensions (multiarray) 2 ... type (overriding type parameter) 3 ... item_size for enum_type_sized 4 ... items_per_chunk
PMC_data(container)
.
other
list.TODO - Barely tested. Optimize new array structure, fixed if big.
pinfo
is the visit info, (see include/parrot/pmc_freeze.h>).
len
.
n_items
at idx
.
n_items
at idx
.
item
of type type
on to the end of the list.
item
of type type
on to the start of the list.
type
from the end of the list.
type
from the start of the list.
item
of type type
to index idx
.
type
at index idx
.
count
items starting at offset
with the items in value
.If count
is 0 then the items in value
will be inserted after offset
.