17ec681f3Smrg/* 27ec681f3Smrg * Copyright © 2019 Intel Corporation 37ec681f3Smrg * 47ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a 57ec681f3Smrg * copy of this software and associated documentation files (the "Software"), 67ec681f3Smrg * to deal in the Software without restriction, including without limitation 77ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 87ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the 97ec681f3Smrg * Software is furnished to do so, subject to the following conditions: 107ec681f3Smrg * 117ec681f3Smrg * The above copyright notice and this permission notice (including the next 127ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the 137ec681f3Smrg * Software. 147ec681f3Smrg * 157ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 167ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 177ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 187ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 197ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 207ec681f3Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 217ec681f3Smrg * IN THE SOFTWARE. 227ec681f3Smrg */ 237ec681f3Smrg 247ec681f3Smrg#ifndef _UTIL_SPARSE_ARRAY_H 257ec681f3Smrg#define _UTIL_SPARSE_ARRAY_H 267ec681f3Smrg 277ec681f3Smrg#include <stdint.h> 287ec681f3Smrg 297ec681f3Smrg#include "c11/threads.h" 307ec681f3Smrg#include "macros.h" 317ec681f3Smrg#include "u_atomic.h" 327ec681f3Smrg#include "u_math.h" 337ec681f3Smrg 347ec681f3Smrg#ifdef __cplusplus 357ec681f3Smrgextern "C" { 367ec681f3Smrg#endif 377ec681f3Smrg 387ec681f3Smrgstruct util_sparse_array_node; 397ec681f3Smrg 407ec681f3Smrg/** A thread-safe automatically growing sparse array data structure 417ec681f3Smrg * 427ec681f3Smrg * This data structure has the following very nice properties: 437ec681f3Smrg * 447ec681f3Smrg * 1. Accessing an element is basically constant time. Technically, it's 457ec681f3Smrg * O(log_b n) where the base b is the node size and n is the maximum 467ec681f3Smrg * index. However, node sizes are expected to be fairly large and the 477ec681f3Smrg * index is a uint64_t so, if your node size is 256, it's O(8). 487ec681f3Smrg * 497ec681f3Smrg * 2. The data stored in the array is never moved in memory. Instead, the 507ec681f3Smrg * data structure only ever grows and new nodes are added as-needed. This 517ec681f3Smrg * means it's safe to store a pointer to something stored in the sparse 527ec681f3Smrg * array without worrying about a realloc invalidating it. 537ec681f3Smrg * 547ec681f3Smrg * 3. The data structure is thread-safe. No guarantees are made about the 557ec681f3Smrg * data stored in the sparse array but it is safe to call 567ec681f3Smrg * util_sparse_array_get(arr, idx) from as many threads as you'd like and 577ec681f3Smrg * we guarantee that two calls to util_sparse_array_get(arr, idx) with the 587ec681f3Smrg * same array and index will always return the same pointer regardless 597ec681f3Smrg * contention between threads. 607ec681f3Smrg * 617ec681f3Smrg * 4. The data structure is lock-free. All manipulations of the tree are 627ec681f3Smrg * done by a careful use of atomics to maintain thread safety and no locks 637ec681f3Smrg * are ever taken other than those taken implicitly by calloc(). If no 647ec681f3Smrg * allocation is required, util_sparse_array_get(arr, idx) does a simple 657ec681f3Smrg * walk over the tree should be efficient even in the case where many 667ec681f3Smrg * threads are accessing the sparse array at once. 677ec681f3Smrg */ 687ec681f3Smrgstruct util_sparse_array { 697ec681f3Smrg size_t elem_size; 707ec681f3Smrg unsigned node_size_log2; 717ec681f3Smrg 727ec681f3Smrg uintptr_t root; 737ec681f3Smrg}; 747ec681f3Smrg 757ec681f3Smrgvoid util_sparse_array_init(struct util_sparse_array *arr, 767ec681f3Smrg size_t elem_size, size_t node_size); 777ec681f3Smrg 787ec681f3Smrgvoid util_sparse_array_finish(struct util_sparse_array *arr); 797ec681f3Smrg 807ec681f3Smrgvoid *util_sparse_array_get(struct util_sparse_array *arr, uint64_t idx); 817ec681f3Smrg 827ec681f3Smrgvoid util_sparse_array_validate(struct util_sparse_array *arr); 837ec681f3Smrg 847ec681f3Smrg/** A thread-safe free list for use with struct util_sparse_array 857ec681f3Smrg * 867ec681f3Smrg * This data structure provides an easy way to manage a singly linked list of 877ec681f3Smrg * "free" elements backed by a util_sparse_array. The list supports only two 887ec681f3Smrg * operations: push and pop both of which are thread-safe and lock-free. T 897ec681f3Smrg */ 907ec681f3Smrgstruct 917ec681f3Smrg#ifdef _MSC_VER 927ec681f3Smrg __declspec(align(8)) 937ec681f3Smrg#else 947ec681f3Smrg __attribute__((aligned(8))) 957ec681f3Smrg#endif 967ec681f3Smrgutil_sparse_array_free_list 977ec681f3Smrg{ 987ec681f3Smrg /** Head of the list 997ec681f3Smrg * 1007ec681f3Smrg * The bottom 64 bits of this value are the index to the next free element 1017ec681f3Smrg * or the sentinel value if the list is empty. 1027ec681f3Smrg * 1037ec681f3Smrg * We want this element to be 8-byte aligned. Otherwise, the performance 1047ec681f3Smrg * of atomic operations on it will be aweful on 32-bit platforms. 1057ec681f3Smrg */ 1067ec681f3Smrg uint64_t head; 1077ec681f3Smrg 1087ec681f3Smrg /** The array backing this free list */ 1097ec681f3Smrg struct util_sparse_array *arr; 1107ec681f3Smrg 1117ec681f3Smrg /** Sentinel value to indicate the end of the list 1127ec681f3Smrg * 1137ec681f3Smrg * This value must never be passed into util_sparse_array_free_list_push. 1147ec681f3Smrg */ 1157ec681f3Smrg uint32_t sentinel; 1167ec681f3Smrg 1177ec681f3Smrg /** Offset into the array element at which to find the "next" value 1187ec681f3Smrg * 1197ec681f3Smrg * The assumption is that there is some uint32_t "next" value embedded in 1207ec681f3Smrg * the array element for use in the free list. This is its offset. 1217ec681f3Smrg */ 1227ec681f3Smrg uint32_t next_offset; 1237ec681f3Smrg}; 1247ec681f3Smrg 1257ec681f3Smrgvoid util_sparse_array_free_list_init(struct util_sparse_array_free_list *fl, 1267ec681f3Smrg struct util_sparse_array *arr, 1277ec681f3Smrg uint32_t sentinel, 1287ec681f3Smrg uint32_t next_offset); 1297ec681f3Smrg 1307ec681f3Smrgvoid util_sparse_array_free_list_push(struct util_sparse_array_free_list *fl, 1317ec681f3Smrg uint32_t *items, unsigned num_items); 1327ec681f3Smrg 1337ec681f3Smrguint32_t util_sparse_array_free_list_pop_idx(struct util_sparse_array_free_list *fl); 1347ec681f3Smrgvoid *util_sparse_array_free_list_pop_elem(struct util_sparse_array_free_list *fl); 1357ec681f3Smrg 1367ec681f3Smrg#ifdef __cplusplus 1377ec681f3Smrg} /* extern C */ 1387ec681f3Smrg#endif 1397ec681f3Smrg 1407ec681f3Smrg#endif /* _UTIL_SPARSE_ARRAY_H */ 141