/** * \file * A pointer array that doesn't use reallocs. * * Copyright (C) 2016 Xamarin Inc * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License 2.0 as published by the Free Software Foundation; * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License 2.0 along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __MONO_SGEN_ARRAY_LIST_H__ #define __MONO_SGEN_ARRAY_LIST_H__ #include #define SGEN_ARRAY_LIST_BUCKETS (32) #define SGEN_ARRAY_LIST_MIN_BUCKET_BITS (5) #define SGEN_ARRAY_LIST_MIN_BUCKET_SIZE (1 << SGEN_ARRAY_LIST_MIN_BUCKET_BITS) typedef void (*SgenArrayListBucketAllocCallback) (gpointer *bucket, guint32 new_bucket_size, gboolean alloc); typedef gboolean (*SgenArrayListIsSlotSetFunc) (volatile gpointer *slot); typedef gboolean (*SgenArrayListSetSlotFunc) (volatile gpointer *slot, gpointer ptr, int data); /* * 'entries' is an array of pointers to buckets of increasing size. The first * bucket has size 'MIN_BUCKET_SIZE', and each bucket is twice the size of the * previous, i.e.: * * |-------|-- MIN_BUCKET_SIZE * [0] -> xxxxxxxx * [1] -> xxxxxxxxxxxxxxxx * [2] -> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx * ... * * 'slot_hint' denotes the position of the last allocation, so that the * whole array needn't be searched on every allocation. * * The size of the spine, 'SGEN_ARRAY_LIST_BUCKETS', is chosen so * that the maximum number of entries is no less than G_MAXUINT32. */ typedef struct { volatile gpointer *volatile entries [SGEN_ARRAY_LIST_BUCKETS]; volatile guint32 capacity; volatile guint32 slot_hint; volatile guint32 next_slot; SgenArrayListBucketAllocCallback bucket_alloc_callback; SgenArrayListIsSlotSetFunc is_slot_set_func; SgenArrayListSetSlotFunc set_slot_func; int mem_type; /* sgen internal mem type or -1 for malloc allocation */ } SgenArrayList; /* * Computes floor(log2(index + MIN_BUCKET_SIZE)) - 1, giving the index * of the bucket containing a slot. */ static inline guint32 sgen_array_list_index_bucket (guint32 index) { #ifdef __GNUC__ return CHAR_BIT * sizeof (index) - __builtin_clz (index + SGEN_ARRAY_LIST_MIN_BUCKET_SIZE) - 1 - SGEN_ARRAY_LIST_MIN_BUCKET_BITS; #else guint count = 0; index += SGEN_ARRAY_LIST_MIN_BUCKET_SIZE; while (index) { ++count; index >>= 1; } return count - 1 - SGEN_ARRAY_LIST_MIN_BUCKET_BITS; #endif } static inline guint32 sgen_array_list_bucket_size (guint32 index) { return 1 << (index + SGEN_ARRAY_LIST_MIN_BUCKET_BITS); } static inline void sgen_array_list_bucketize (guint32 index, guint32 *bucket, guint32 *offset) { *bucket = sgen_array_list_index_bucket (index); *offset = index - sgen_array_list_bucket_size (*bucket) + SGEN_ARRAY_LIST_MIN_BUCKET_SIZE; } static inline volatile gpointer * sgen_array_list_get_slot (SgenArrayList *array, guint32 index) { guint32 bucket, offset; SGEN_ASSERT (0, index < array->capacity, "Why are we accessing an entry that is not allocated"); sgen_array_list_bucketize (index, &bucket, &offset); return &(array->entries [bucket] [offset]); } #define SGEN_ARRAY_LIST_INIT(bucket_alloc_callback, is_slot_set_func, set_slot_func, mem_type) { { NULL }, 0, 0, 0, (bucket_alloc_callback), (is_slot_set_func), (set_slot_func), (mem_type) } #define SGEN_ARRAY_LIST_FOREACH_SLOT(array, slot) { \ guint32 __bucket, __offset; \ const guint32 __max_bucket = sgen_array_list_index_bucket ((array)->capacity); \ guint32 __index = 0; \ const guint32 __next_slot = (array)->next_slot; \ for (__bucket = 0; __bucket < __max_bucket; ++__bucket) { \ volatile gpointer *__entries = (array)->entries [__bucket]; \ for (__offset = 0; __offset < sgen_array_list_bucket_size (__bucket); ++__offset, ++__index) { \ if (__index >= __next_slot) \ break; \ slot = &__entries [__offset]; #define SGEN_ARRAY_LIST_END_FOREACH_SLOT } } } #define SGEN_ARRAY_LIST_FOREACH_SLOT_RANGE(array, begin, end, slot, index) { \ for (index = (begin); index < (end); index++) { \ guint32 __bucket, __offset; \ volatile gpointer *__entries; \ sgen_array_list_bucketize (index, &__bucket, &__offset); \ __entries = (array)->entries [__bucket]; \ slot = &__entries [__offset]; #define SGEN_ARRAY_LIST_END_FOREACH_SLOT_RANGE } } guint32 sgen_array_list_alloc_block (SgenArrayList *array, guint32 slots_to_add); guint32 sgen_array_list_add (SgenArrayList *array, gpointer ptr, int data, gboolean increase_size_before_set); guint32 sgen_array_list_find (SgenArrayList *array, gpointer ptr); gboolean sgen_array_list_default_cas_setter (volatile gpointer *slot, gpointer ptr, int data); gboolean sgen_array_list_default_is_slot_set (volatile gpointer *slot); void sgen_array_list_remove_nulls (SgenArrayList *array); #endif