This is unused yet. Signed-off-by: Chia-I Wu <olvaffe@gmail.com> Reviewed-by: Yiwei Zhang <zzyiwei@chromium.org> Reviewed-by: Ryan Neph <ryanneph@google.com> Acked-by: Gert Wollny <gert.wollny@collabora.com>macos/master
parent
50f448ee49
commit
e7cf972d8b
@ -0,0 +1,89 @@ |
|||||||
|
/*
|
||||||
|
* Mesa 3-D graphics library |
||||||
|
* |
||||||
|
* Copyright (C) 1999-2008 Brian Paul All Rights Reserved. |
||||||
|
* Copyright (C) 2009 VMware, Inc. All Rights Reserved. |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
* copy of this software and associated documentation files (the "Software"), |
||||||
|
* to deal in the Software without restriction, including without limitation |
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
* Software is furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice shall be included |
||||||
|
* in all copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR |
||||||
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
||||||
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
||||||
|
* OTHER DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file compiler.h |
||||||
|
* Compiler-related stuff. |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
#ifndef COMPILER_H |
||||||
|
#define COMPILER_H |
||||||
|
|
||||||
|
|
||||||
|
#include <assert.h> |
||||||
|
|
||||||
|
#include "util/macros.h" |
||||||
|
|
||||||
|
#include "c99_compat.h" /* inline, __func__, etc. */ |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Either define MESA_BIG_ENDIAN or MESA_LITTLE_ENDIAN, and CPU_TO_LE32. |
||||||
|
* Do not use these unless absolutely necessary! |
||||||
|
* Try to use a runtime test instead. |
||||||
|
* For now, only used by some DRI hardware drivers for color/texel packing. |
||||||
|
*/ |
||||||
|
#if defined(BYTE_ORDER) && defined(BIG_ENDIAN) && BYTE_ORDER == BIG_ENDIAN |
||||||
|
#if defined(__linux__) |
||||||
|
#include <byteswap.h> |
||||||
|
#define CPU_TO_LE32( x ) bswap_32( x ) |
||||||
|
#elif defined(__APPLE__) |
||||||
|
#include <CoreFoundation/CFByteOrder.h> |
||||||
|
#define CPU_TO_LE32( x ) CFSwapInt32HostToLittle( x ) |
||||||
|
#elif defined(__OpenBSD__) |
||||||
|
#include <sys/types.h> |
||||||
|
#define CPU_TO_LE32( x ) htole32( x ) |
||||||
|
#else /*__linux__ */ |
||||||
|
#include <sys/endian.h> |
||||||
|
#define CPU_TO_LE32( x ) bswap32( x ) |
||||||
|
#endif /*__linux__*/ |
||||||
|
#define MESA_BIG_ENDIAN 1 |
||||||
|
#else |
||||||
|
#define CPU_TO_LE32( x ) ( x ) |
||||||
|
#define MESA_LITTLE_ENDIAN 1 |
||||||
|
#endif |
||||||
|
#define LE32_TO_CPU( x ) CPU_TO_LE32( x ) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define IEEE_ONE 0x3f800000 |
||||||
|
|
||||||
|
#ifndef __has_attribute |
||||||
|
# define __has_attribute(x) 0 |
||||||
|
#endif |
||||||
|
|
||||||
|
#if __cplusplus >= 201703L || __STDC_VERSION__ > 201710L |
||||||
|
/* Standard C++17/C23 attribute */ |
||||||
|
#define FALLTHROUGH [[fallthrough]] |
||||||
|
#elif __has_attribute(fallthrough) |
||||||
|
/* Non-standard but supported by at least gcc and clang */ |
||||||
|
#define FALLTHROUGH __attribute__((fallthrough)) |
||||||
|
#else |
||||||
|
#define FALLTHROUGH do { } while(0) |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif /* COMPILER_H */ |
@ -0,0 +1,74 @@ |
|||||||
|
/*
|
||||||
|
* Copyright © 2010 Valve Software |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
* copy of this software and associated documentation files (the "Software"), |
||||||
|
* to deal in the Software without restriction, including without limitation |
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
* Software is furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice (including the next |
||||||
|
* paragraph) shall be included in all copies or substantial portions of the |
||||||
|
* Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
||||||
|
* IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
/*
|
||||||
|
* Code for fast 32-bit unsigned remainder, based off of "Faster Remainder by |
||||||
|
* Direct Computation: Applications to Compilers and Software Libraries," |
||||||
|
* available at https://arxiv.org/pdf/1902.01961.pdf.
|
||||||
|
* |
||||||
|
* util_fast_urem32(n, d, REMAINDER_MAGIC(d)) returns the same thing as |
||||||
|
* n % d for any unsigned n and d, however it compiles down to only a few |
||||||
|
* multiplications, so it should be faster than plain uint32_t modulo if the |
||||||
|
* same divisor is used many times. |
||||||
|
*/ |
||||||
|
|
||||||
|
#define REMAINDER_MAGIC(divisor) \ |
||||||
|
((uint64_t) ~0ull / (divisor) + 1) |
||||||
|
|
||||||
|
/*
|
||||||
|
* Get bits 64-96 of a 32x64-bit multiply. If __int128_t is available, we use |
||||||
|
* it, which usually compiles down to one instruction on 64-bit architectures. |
||||||
|
* Otherwise on 32-bit architectures we usually get four instructions (one |
||||||
|
* 32x32->64 multiply, one 32x32->32 multiply, and one 64-bit add). |
||||||
|
*/ |
||||||
|
|
||||||
|
static inline uint32_t |
||||||
|
_mul32by64_hi(uint32_t a, uint64_t b) |
||||||
|
{ |
||||||
|
#ifdef HAVE_UINT128 |
||||||
|
return ((__uint128_t) b * a) >> 64; |
||||||
|
#else |
||||||
|
/*
|
||||||
|
* Let b = b0 + 2^32 * b1. Then a * b = a * b0 + 2^32 * a * b1. We would |
||||||
|
* have to do a 96-bit addition to get the full result, except that only |
||||||
|
* one term has non-zero lower 32 bits, which means that to get the high 32 |
||||||
|
* bits, we only have to add the high 64 bits of each term. Unfortunately, |
||||||
|
* we have to do the 64-bit addition in case the low 32 bits overflow. |
||||||
|
*/ |
||||||
|
uint32_t b0 = (uint32_t) b; |
||||||
|
uint32_t b1 = b >> 32; |
||||||
|
return ((((uint64_t) a * b0) >> 32) + (uint64_t) a * b1) >> 32; |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
static inline uint32_t |
||||||
|
util_fast_urem32(uint32_t n, uint32_t d, uint64_t magic) |
||||||
|
{ |
||||||
|
uint64_t lowbits = magic * n; |
||||||
|
uint32_t result = _mul32by64_hi(d, lowbits); |
||||||
|
assert(result == n % d); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
@ -0,0 +1,906 @@ |
|||||||
|
/*
|
||||||
|
* Copyright © 2009,2012 Intel Corporation |
||||||
|
* Copyright © 1988-2004 Keith Packard and Bart Massey. |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
* copy of this software and associated documentation files (the "Software"), |
||||||
|
* to deal in the Software without restriction, including without limitation |
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
* Software is furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice (including the next |
||||||
|
* paragraph) shall be included in all copies or substantial portions of the |
||||||
|
* Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
||||||
|
* IN THE SOFTWARE. |
||||||
|
* |
||||||
|
* Except as contained in this notice, the names of the authors |
||||||
|
* or their institutions shall not be used in advertising or |
||||||
|
* otherwise to promote the sale, use or other dealings in this |
||||||
|
* Software without prior written authorization from the |
||||||
|
* authors. |
||||||
|
* |
||||||
|
* Authors: |
||||||
|
* Eric Anholt <eric@anholt.net> |
||||||
|
* Keith Packard <keithp@keithp.com> |
||||||
|
*/ |
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements an open-addressing, linear-reprobing hash table. |
||||||
|
* |
||||||
|
* For more information, see: |
||||||
|
* |
||||||
|
* http://cgit.freedesktop.org/~anholt/hash_table/tree/README
|
||||||
|
*/ |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include <assert.h> |
||||||
|
|
||||||
|
#include "hash_table.h" |
||||||
|
#include "ralloc.h" |
||||||
|
#include "macros.h" |
||||||
|
#include "u_memory.h" |
||||||
|
#include "fast_urem_by_const.h" |
||||||
|
#include "util/u_memory.h" |
||||||
|
|
||||||
|
#define XXH_INLINE_ALL |
||||||
|
#include "xxhash.h" |
||||||
|
|
||||||
|
/**
|
||||||
|
* Magic number that gets stored outside of the struct hash_table. |
||||||
|
* |
||||||
|
* The hash table needs a particular pointer to be the marker for a key that |
||||||
|
* was deleted from the table, along with NULL for the "never allocated in the |
||||||
|
* table" marker. Legacy GL allows any GLuint to be used as a GL object name, |
||||||
|
* and we use a 1:1 mapping from GLuints to key pointers, so we need to be |
||||||
|
* able to track a GLuint that happens to match the deleted key outside of |
||||||
|
* struct hash_table. We tell the hash table to use "1" as the deleted key |
||||||
|
* value, so that we test the deleted-key-in-the-table path as best we can. |
||||||
|
*/ |
||||||
|
#define DELETED_KEY_VALUE 1 |
||||||
|
|
||||||
|
static inline void * |
||||||
|
uint_key(unsigned id) |
||||||
|
{ |
||||||
|
return (void *)(uintptr_t) id; |
||||||
|
} |
||||||
|
|
||||||
|
static const uint32_t deleted_key_value; |
||||||
|
|
||||||
|
/**
|
||||||
|
* From Knuth -- a good choice for hash/rehash values is p, p-2 where |
||||||
|
* p and p-2 are both prime. These tables are sized to have an extra 10% |
||||||
|
* free to avoid exponential performance degradation as the hash table fills |
||||||
|
*/ |
||||||
|
static const struct { |
||||||
|
uint32_t max_entries, size, rehash; |
||||||
|
uint64_t size_magic, rehash_magic; |
||||||
|
} hash_sizes[] = { |
||||||
|
#define ENTRY(max_entries, size, rehash) \ |
||||||
|
{ max_entries, size, rehash, \
|
||||||
|
REMAINDER_MAGIC(size), REMAINDER_MAGIC(rehash) } |
||||||
|
|
||||||
|
ENTRY(2, 5, 3 ), |
||||||
|
ENTRY(4, 7, 5 ), |
||||||
|
ENTRY(8, 13, 11 ), |
||||||
|
ENTRY(16, 19, 17 ), |
||||||
|
ENTRY(32, 43, 41 ), |
||||||
|
ENTRY(64, 73, 71 ), |
||||||
|
ENTRY(128, 151, 149 ), |
||||||
|
ENTRY(256, 283, 281 ), |
||||||
|
ENTRY(512, 571, 569 ), |
||||||
|
ENTRY(1024, 1153, 1151 ), |
||||||
|
ENTRY(2048, 2269, 2267 ), |
||||||
|
ENTRY(4096, 4519, 4517 ), |
||||||
|
ENTRY(8192, 9013, 9011 ), |
||||||
|
ENTRY(16384, 18043, 18041 ), |
||||||
|
ENTRY(32768, 36109, 36107 ), |
||||||
|
ENTRY(65536, 72091, 72089 ), |
||||||
|
ENTRY(131072, 144409, 144407 ), |
||||||
|
ENTRY(262144, 288361, 288359 ), |
||||||
|
ENTRY(524288, 576883, 576881 ), |
||||||
|
ENTRY(1048576, 1153459, 1153457 ), |
||||||
|
ENTRY(2097152, 2307163, 2307161 ), |
||||||
|
ENTRY(4194304, 4613893, 4613891 ), |
||||||
|
ENTRY(8388608, 9227641, 9227639 ), |
||||||
|
ENTRY(16777216, 18455029, 18455027 ), |
||||||
|
ENTRY(33554432, 36911011, 36911009 ), |
||||||
|
ENTRY(67108864, 73819861, 73819859 ), |
||||||
|
ENTRY(134217728, 147639589, 147639587 ), |
||||||
|
ENTRY(268435456, 295279081, 295279079 ), |
||||||
|
ENTRY(536870912, 590559793, 590559791 ), |
||||||
|
ENTRY(1073741824, 1181116273, 1181116271 ), |
||||||
|
ENTRY(2147483648ul, 2362232233ul, 2362232231ul ) |
||||||
|
}; |
||||||
|
|
||||||
|
ASSERTED static inline bool |
||||||
|
key_pointer_is_reserved(const struct hash_table *ht, const void *key) |
||||||
|
{ |
||||||
|
return key == NULL || key == ht->deleted_key; |
||||||
|
} |
||||||
|
|
||||||
|
static int |
||||||
|
entry_is_free(const struct hash_entry *entry) |
||||||
|
{ |
||||||
|
return entry->key == NULL; |
||||||
|
} |
||||||
|
|
||||||
|
static int |
||||||
|
entry_is_deleted(const struct hash_table *ht, struct hash_entry *entry) |
||||||
|
{ |
||||||
|
return entry->key == ht->deleted_key; |
||||||
|
} |
||||||
|
|
||||||
|
static int |
||||||
|
entry_is_present(const struct hash_table *ht, struct hash_entry *entry) |
||||||
|
{ |
||||||
|
return entry->key != NULL && entry->key != ht->deleted_key; |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
_mesa_hash_table_init(struct hash_table *ht, |
||||||
|
void *mem_ctx, |
||||||
|
uint32_t (*key_hash_function)(const void *key), |
||||||
|
bool (*key_equals_function)(const void *a, |
||||||
|
const void *b)) |
||||||
|
{ |
||||||
|
ht->size_index = 0; |
||||||
|
ht->size = hash_sizes[ht->size_index].size; |
||||||
|
ht->rehash = hash_sizes[ht->size_index].rehash; |
||||||
|
ht->size_magic = hash_sizes[ht->size_index].size_magic; |
||||||
|
ht->rehash_magic = hash_sizes[ht->size_index].rehash_magic; |
||||||
|
ht->max_entries = hash_sizes[ht->size_index].max_entries; |
||||||
|
ht->key_hash_function = key_hash_function; |
||||||
|
ht->key_equals_function = key_equals_function; |
||||||
|
ht->table = rzalloc_array(mem_ctx, struct hash_entry, ht->size); |
||||||
|
ht->entries = 0; |
||||||
|
ht->deleted_entries = 0; |
||||||
|
ht->deleted_key = &deleted_key_value; |
||||||
|
|
||||||
|
return ht->table != NULL; |
||||||
|
} |
||||||
|
|
||||||
|
struct hash_table * |
||||||
|
_mesa_hash_table_create(void *mem_ctx, |
||||||
|
uint32_t (*key_hash_function)(const void *key), |
||||||
|
bool (*key_equals_function)(const void *a, |
||||||
|
const void *b)) |
||||||
|
{ |
||||||
|
struct hash_table *ht; |
||||||
|
|
||||||
|
/* mem_ctx is used to allocate the hash table, but the hash table is used
|
||||||
|
* to allocate all of the suballocations. |
||||||
|
*/ |
||||||
|
ht = ralloc(mem_ctx, struct hash_table); |
||||||
|
if (ht == NULL) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
if (!_mesa_hash_table_init(ht, ht, key_hash_function, key_equals_function)) { |
||||||
|
ralloc_free(ht); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
return ht; |
||||||
|
} |
||||||
|
|
||||||
|
static uint32_t |
||||||
|
key_u32_hash(const void *key) |
||||||
|
{ |
||||||
|
uint32_t u = (uint32_t)(uintptr_t)key; |
||||||
|
return _mesa_hash_uint(&u); |
||||||
|
} |
||||||
|
|
||||||
|
static bool |
||||||
|
key_u32_equals(const void *a, const void *b) |
||||||
|
{ |
||||||
|
return (uint32_t)(uintptr_t)a == (uint32_t)(uintptr_t)b; |
||||||
|
} |
||||||
|
|
||||||
|
/* key == 0 and key == deleted_key are not allowed */ |
||||||
|
struct hash_table * |
||||||
|
_mesa_hash_table_create_u32_keys(void *mem_ctx) |
||||||
|
{ |
||||||
|
return _mesa_hash_table_create(mem_ctx, key_u32_hash, key_u32_equals); |
||||||
|
} |
||||||
|
|
||||||
|
struct hash_table * |
||||||
|
_mesa_hash_table_clone(struct hash_table *src, void *dst_mem_ctx) |
||||||
|
{ |
||||||
|
struct hash_table *ht; |
||||||
|
|
||||||
|
ht = ralloc(dst_mem_ctx, struct hash_table); |
||||||
|
if (ht == NULL) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
memcpy(ht, src, sizeof(struct hash_table)); |
||||||
|
|
||||||
|
ht->table = ralloc_array(ht, struct hash_entry, ht->size); |
||||||
|
if (ht->table == NULL) { |
||||||
|
ralloc_free(ht); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
memcpy(ht->table, src->table, ht->size * sizeof(struct hash_entry)); |
||||||
|
|
||||||
|
return ht; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the given hash table. |
||||||
|
* |
||||||
|
* If delete_function is passed, it gets called on each entry present before |
||||||
|
* freeing. |
||||||
|
*/ |
||||||
|
void |
||||||
|
_mesa_hash_table_destroy(struct hash_table *ht, |
||||||
|
void (*delete_function)(struct hash_entry *entry)) |
||||||
|
{ |
||||||
|
if (!ht) |
||||||
|
return; |
||||||
|
|
||||||
|
if (delete_function) { |
||||||
|
hash_table_foreach(ht, entry) { |
||||||
|
delete_function(entry); |
||||||
|
} |
||||||
|
} |
||||||
|
ralloc_free(ht); |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
hash_table_clear_fast(struct hash_table *ht) |
||||||
|
{ |
||||||
|
memset(ht->table, 0, sizeof(struct hash_entry) * hash_sizes[ht->size_index].size); |
||||||
|
ht->entries = ht->deleted_entries = 0; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all entries of the given hash table without deleting the table |
||||||
|
* itself or changing its structure. |
||||||
|
* |
||||||
|
* If delete_function is passed, it gets called on each entry present. |
||||||
|
*/ |
||||||
|
void |
||||||
|
_mesa_hash_table_clear(struct hash_table *ht, |
||||||
|
void (*delete_function)(struct hash_entry *entry)) |
||||||
|
{ |
||||||
|
if (!ht) |
||||||
|
return; |
||||||
|
|
||||||
|
struct hash_entry *entry; |
||||||
|
|
||||||
|
if (delete_function) { |
||||||
|
for (entry = ht->table; entry != ht->table + ht->size; entry++) { |
||||||
|
if (entry_is_present(ht, entry)) |
||||||
|
delete_function(entry); |
||||||
|
|
||||||
|
entry->key = NULL; |
||||||
|
} |
||||||
|
ht->entries = 0; |
||||||
|
ht->deleted_entries = 0; |
||||||
|
} else |
||||||
|
hash_table_clear_fast(ht); |
||||||
|
} |
||||||
|
|
||||||
|
/** Sets the value of the key pointer used for deleted entries in the table.
|
||||||
|
* |
||||||
|
* The assumption is that usually keys are actual pointers, so we use a |
||||||
|
* default value of a pointer to an arbitrary piece of storage in the library. |
||||||
|
* But in some cases a consumer wants to store some other sort of value in the |
||||||
|
* table, like a uint32_t, in which case that pointer may conflict with one of |
||||||
|
* their valid keys. This lets that user select a safe value. |
||||||
|
* |
||||||
|
* This must be called before any keys are actually deleted from the table. |
||||||
|
*/ |
||||||
|
void |
||||||
|
_mesa_hash_table_set_deleted_key(struct hash_table *ht, const void *deleted_key) |
||||||
|
{ |
||||||
|
ht->deleted_key = deleted_key; |
||||||
|
} |
||||||
|
|
||||||
|
static struct hash_entry * |
||||||
|
hash_table_search(struct hash_table *ht, uint32_t hash, const void *key) |
||||||
|
{ |
||||||
|
assert(!key_pointer_is_reserved(ht, key)); |
||||||
|
|
||||||
|
uint32_t size = ht->size; |
||||||
|
uint32_t start_hash_address = util_fast_urem32(hash, size, ht->size_magic); |
||||||
|
uint32_t double_hash = 1 + util_fast_urem32(hash, ht->rehash, |
||||||
|
ht->rehash_magic); |
||||||
|
uint32_t hash_address = start_hash_address; |
||||||
|
|
||||||
|
do { |
||||||
|
struct hash_entry *entry = ht->table + hash_address; |
||||||
|
|
||||||
|
if (entry_is_free(entry)) { |
||||||
|
return NULL; |
||||||
|
} else if (entry_is_present(ht, entry) && entry->hash == hash) { |
||||||
|
if (ht->key_equals_function(key, entry->key)) { |
||||||
|
return entry; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
hash_address += double_hash; |
||||||
|
if (hash_address >= size) |
||||||
|
hash_address -= size; |
||||||
|
} while (hash_address != start_hash_address); |
||||||
|
|
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a hash table entry with the given key and hash of that key. |
||||||
|
* |
||||||
|
* Returns NULL if no entry is found. Note that the data pointer may be |
||||||
|
* modified by the user. |
||||||
|
*/ |
||||||
|
struct hash_entry * |
||||||
|
_mesa_hash_table_search(struct hash_table *ht, const void *key) |
||||||
|
{ |
||||||
|
assert(ht->key_hash_function); |
||||||
|
return hash_table_search(ht, ht->key_hash_function(key), key); |
||||||
|
} |
||||||
|
|
||||||
|
struct hash_entry * |
||||||
|
_mesa_hash_table_search_pre_hashed(struct hash_table *ht, uint32_t hash, |
||||||
|
const void *key) |
||||||
|
{ |
||||||
|
assert(ht->key_hash_function == NULL || hash == ht->key_hash_function(key)); |
||||||
|
return hash_table_search(ht, hash, key); |
||||||
|
} |
||||||
|
|
||||||
|
static struct hash_entry * |
||||||
|
hash_table_insert(struct hash_table *ht, uint32_t hash, |
||||||
|
const void *key, void *data); |
||||||
|
|
||||||
|
static void |
||||||
|
hash_table_insert_rehash(struct hash_table *ht, uint32_t hash, |
||||||
|
const void *key, void *data) |
||||||
|
{ |
||||||
|
uint32_t size = ht->size; |
||||||
|
uint32_t start_hash_address = util_fast_urem32(hash, size, ht->size_magic); |
||||||
|
uint32_t double_hash = 1 + util_fast_urem32(hash, ht->rehash, |
||||||
|
ht->rehash_magic); |
||||||
|
uint32_t hash_address = start_hash_address; |
||||||
|
do { |
||||||
|
struct hash_entry *entry = ht->table + hash_address; |
||||||
|
|
||||||
|
if (likely(entry->key == NULL)) { |
||||||
|
entry->hash = hash; |
||||||
|
entry->key = key; |
||||||
|
entry->data = data; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
hash_address += double_hash; |
||||||
|
if (hash_address >= size) |
||||||
|
hash_address -= size; |
||||||
|
} while (true); |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
_mesa_hash_table_rehash(struct hash_table *ht, unsigned new_size_index) |
||||||
|
{ |
||||||
|
struct hash_table old_ht; |
||||||
|
struct hash_entry *table; |
||||||
|
|
||||||
|
if (ht->size_index == new_size_index && ht->deleted_entries == ht->max_entries) { |
||||||
|
hash_table_clear_fast(ht); |
||||||
|
assert(!ht->entries); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (new_size_index >= ARRAY_SIZE(hash_sizes)) |
||||||
|
return; |
||||||
|
|
||||||
|
table = rzalloc_array(ralloc_parent(ht->table), struct hash_entry, |
||||||
|
hash_sizes[new_size_index].size); |
||||||
|
if (table == NULL) |
||||||
|
return; |
||||||
|
|
||||||
|
old_ht = *ht; |
||||||
|
|
||||||
|
ht->table = table; |
||||||
|
ht->size_index = new_size_index; |
||||||
|
ht->size = hash_sizes[ht->size_index].size; |
||||||
|
ht->rehash = hash_sizes[ht->size_index].rehash; |
||||||
|
ht->size_magic = hash_sizes[ht->size_index].size_magic; |
||||||
|
ht->rehash_magic = hash_sizes[ht->size_index].rehash_magic; |
||||||
|
ht->max_entries = hash_sizes[ht->size_index].max_entries; |
||||||
|
ht->entries = 0; |
||||||
|
ht->deleted_entries = 0; |
||||||
|
|
||||||
|
hash_table_foreach(&old_ht, entry) { |
||||||
|
hash_table_insert_rehash(ht, entry->hash, entry->key, entry->data); |
||||||
|
} |
||||||
|
|
||||||
|
ht->entries = old_ht.entries; |
||||||
|
|
||||||
|
ralloc_free(old_ht.table); |
||||||
|
} |
||||||
|
|
||||||
|
static struct hash_entry * |
||||||
|
hash_table_insert(struct hash_table *ht, uint32_t hash, |
||||||
|
const void *key, void *data) |
||||||
|
{ |
||||||
|
struct hash_entry *available_entry = NULL; |
||||||
|
|
||||||
|
assert(!key_pointer_is_reserved(ht, key)); |
||||||
|
|
||||||
|
if (ht->entries >= ht->max_entries) { |
||||||
|
_mesa_hash_table_rehash(ht, ht->size_index + 1); |
||||||
|
} else if (ht->deleted_entries + ht->entries >= ht->max_entries) { |
||||||
|
_mesa_hash_table_rehash(ht, ht->size_index); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t size = ht->size; |
||||||
|
uint32_t start_hash_address = util_fast_urem32(hash, size, ht->size_magic); |
||||||
|
uint32_t double_hash = 1 + util_fast_urem32(hash, ht->rehash, |
||||||
|
ht->rehash_magic); |
||||||
|
uint32_t hash_address = start_hash_address; |
||||||
|
do { |
||||||
|
struct hash_entry *entry = ht->table + hash_address; |
||||||
|
|
||||||
|
if (!entry_is_present(ht, entry)) { |
||||||
|
/* Stash the first available entry we find */ |
||||||
|
if (available_entry == NULL) |
||||||
|
available_entry = entry; |
||||||
|
if (entry_is_free(entry)) |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
/* Implement replacement when another insert happens
|
||||||
|
* with a matching key. This is a relatively common |
||||||
|
* feature of hash tables, with the alternative |
||||||
|
* generally being "insert the new value as well, and |
||||||
|
* return it first when the key is searched for". |
||||||
|
* |
||||||
|
* Note that the hash table doesn't have a delete |
||||||
|
* callback. If freeing of old data pointers is |
||||||
|
* required to avoid memory leaks, perform a search |
||||||
|
* before inserting. |
||||||
|
*/ |
||||||
|
if (!entry_is_deleted(ht, entry) && |
||||||
|
entry->hash == hash && |
||||||
|
ht->key_equals_function(key, entry->key)) { |
||||||
|
entry->key = key; |
||||||
|
entry->data = data; |
||||||
|
return entry; |
||||||
|
} |
||||||
|
|
||||||
|
hash_address += double_hash; |
||||||
|
if (hash_address >= size) |
||||||
|
hash_address -= size; |
||||||
|
} while (hash_address != start_hash_address); |
||||||
|
|
||||||
|
if (available_entry) { |
||||||
|
if (entry_is_deleted(ht, available_entry)) |
||||||
|
ht->deleted_entries--; |
||||||
|
available_entry->hash = hash; |
||||||
|
available_entry->key = key; |
||||||
|
available_entry->data = data; |
||||||
|
ht->entries++; |
||||||
|
return available_entry; |
||||||
|
} |
||||||
|
|
||||||
|
/* We could hit here if a required resize failed. An unchecked-malloc
|
||||||
|
* application could ignore this result. |
||||||
|
*/ |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts the key with the given hash into the table. |
||||||
|
* |
||||||
|
* Note that insertion may rearrange the table on a resize or rehash, |
||||||
|
* so previously found hash_entries are no longer valid after this function. |
||||||
|
*/ |
||||||
|
struct hash_entry * |
||||||
|
_mesa_hash_table_insert(struct hash_table *ht, const void *key, void *data) |
||||||
|
{ |
||||||
|
assert(ht->key_hash_function); |
||||||
|
return hash_table_insert(ht, ht->key_hash_function(key), key, data); |
||||||
|
} |
||||||
|
|
||||||
|
struct hash_entry * |
||||||
|
_mesa_hash_table_insert_pre_hashed(struct hash_table *ht, uint32_t hash, |
||||||
|
const void *key, void *data) |
||||||
|
{ |
||||||
|
assert(ht->key_hash_function == NULL || hash == ht->key_hash_function(key)); |
||||||
|
return hash_table_insert(ht, hash, key, data); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* This function deletes the given hash table entry. |
||||||
|
* |
||||||
|
* Note that deletion doesn't otherwise modify the table, so an iteration over |
||||||
|
* the table deleting entries is safe. |
||||||
|
*/ |
||||||
|
void |
||||||
|
_mesa_hash_table_remove(struct hash_table *ht, |
||||||
|
struct hash_entry *entry) |
||||||
|
{ |
||||||
|
if (!entry) |
||||||
|
return; |
||||||
|
|
||||||
|
entry->key = ht->deleted_key; |
||||||
|
ht->entries--; |
||||||
|
ht->deleted_entries++; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the entry with the corresponding key, if exists. |
||||||
|
*/ |
||||||
|
void _mesa_hash_table_remove_key(struct hash_table *ht, |
||||||
|
const void *key) |
||||||
|
{ |
||||||
|
_mesa_hash_table_remove(ht, _mesa_hash_table_search(ht, key)); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is an iterator over the hash_table when no deleted entries are present. |
||||||
|
* |
||||||
|
* Pass in NULL for the first entry, as in the start of a for loop. |
||||||
|
*/ |
||||||
|
struct hash_entry * |
||||||
|
_mesa_hash_table_next_entry_unsafe(const struct hash_table *ht, struct hash_entry *entry) |
||||||
|
{ |
||||||
|
assert(!ht->deleted_entries); |
||||||
|
if (!ht->entries) |
||||||
|
return NULL; |
||||||
|
if (entry == NULL) |
||||||
|
entry = ht->table; |
||||||
|
else |
||||||
|
entry = entry + 1; |
||||||
|
if (entry != ht->table + ht->size) |
||||||
|
return entry->key ? entry : _mesa_hash_table_next_entry_unsafe(ht, entry); |
||||||
|
|
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is an iterator over the hash table. |
||||||
|
* |
||||||
|
* Pass in NULL for the first entry, as in the start of a for loop. Note that |
||||||
|
* an iteration over the table is O(table_size) not O(entries). |
||||||
|
*/ |
||||||
|
struct hash_entry * |
||||||
|
_mesa_hash_table_next_entry(struct hash_table *ht, |
||||||
|
struct hash_entry *entry) |
||||||
|
{ |
||||||
|
if (entry == NULL) |
||||||
|
entry = ht->table; |
||||||
|
else |
||||||
|
entry = entry + 1; |
||||||
|
|
||||||
|
for (; entry != ht->table + ht->size; entry++) { |
||||||
|
if (entry_is_present(ht, entry)) { |
||||||
|
return entry; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a random entry from the hash table. |
||||||
|
* |
||||||
|
* This may be useful in implementing random replacement (as opposed |
||||||
|
* to just removing everything) in caches based on this hash table |
||||||
|
* implementation. @predicate may be used to filter entries, or may |
||||||
|
* be set to NULL for no filtering. |
||||||
|
*/ |
||||||
|
struct hash_entry * |
||||||
|
_mesa_hash_table_random_entry(struct hash_table *ht, |
||||||
|
bool (*predicate)(struct hash_entry *entry)) |
||||||
|
{ |
||||||
|
struct hash_entry *entry; |
||||||
|
uint32_t i = rand() % ht->size; |
||||||
|
|
||||||
|
if (ht->entries == 0) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
for (entry = ht->table + i; entry != ht->table + ht->size; entry++) { |
||||||
|
if (entry_is_present(ht, entry) && |
||||||
|
(!predicate || predicate(entry))) { |
||||||
|
return entry; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for (entry = ht->table; entry != ht->table + i; entry++) { |
||||||
|
if (entry_is_present(ht, entry) && |
||||||
|
(!predicate || predicate(entry))) { |
||||||
|
return entry; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
uint32_t |
||||||
|
_mesa_hash_data(const void *data, size_t size) |
||||||
|
{ |
||||||
|
return XXH32(data, size, 0); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t |
||||||
|
_mesa_hash_data_with_seed(const void *data, size_t size, uint32_t seed) |
||||||
|
{ |
||||||
|
return XXH32(data, size, seed); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t |
||||||
|
_mesa_hash_int(const void *key) |
||||||
|
{ |
||||||
|
return XXH32(key, sizeof(int), 0); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t |
||||||
|
_mesa_hash_uint(const void *key) |
||||||
|
{ |
||||||
|
return XXH32(key, sizeof(unsigned), 0); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t |
||||||
|
_mesa_hash_u32(const void *key) |
||||||
|
{ |
||||||
|
return XXH32(key, 4, 0); |
||||||
|
} |
||||||
|
|
||||||
|
/** FNV-1a string hash implementation */ |
||||||
|
uint32_t |
||||||
|
_mesa_hash_string(const void *_key) |
||||||
|
{ |
||||||
|
uint32_t hash = 0; |
||||||
|
const char *key = _key; |
||||||
|
size_t len = strlen(key); |
||||||
|
#if defined(_WIN64) || defined(__x86_64__) |
||||||
|
hash = (uint32_t)XXH64(key, len, hash); |
||||||
|
#else |
||||||
|
hash = XXH32(key, len, hash); |
||||||
|
#endif |
||||||
|
return hash; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t |
||||||
|
_mesa_hash_pointer(const void *pointer) |
||||||
|
{ |
||||||
|
uintptr_t num = (uintptr_t) pointer; |
||||||
|
return (uint32_t) ((num >> 2) ^ (num >> 6) ^ (num >> 10) ^ (num >> 14)); |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
_mesa_key_int_equal(const void *a, const void *b) |
||||||
|
{ |
||||||
|
return *((const int *)a) == *((const int *)b); |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
_mesa_key_uint_equal(const void *a, const void *b) |
||||||
|
{ |
||||||
|
|
||||||
|
return *((const unsigned *)a) == *((const unsigned *)b); |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
_mesa_key_u32_equal(const void *a, const void *b) |
||||||
|
{ |
||||||
|
return *((const uint32_t *)a) == *((const uint32_t *)b); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* String compare function for use as the comparison callback in |
||||||
|
* _mesa_hash_table_create(). |
||||||
|
*/ |
||||||
|
bool |
||||||
|
_mesa_key_string_equal(const void *a, const void *b) |
||||||
|
{ |
||||||
|
return strcmp(a, b) == 0; |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
_mesa_key_pointer_equal(const void *a, const void *b) |
||||||
|
{ |
||||||
|
return a == b; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to create a hash table with pointer keys. |
||||||
|
*/ |
||||||
|
struct hash_table * |
||||||
|
_mesa_pointer_hash_table_create(void *mem_ctx) |
||||||
|
{ |
||||||
|
return _mesa_hash_table_create(mem_ctx, _mesa_hash_pointer, |
||||||
|
_mesa_key_pointer_equal); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
bool |
||||||
|
_mesa_hash_table_reserve(struct hash_table *ht, unsigned size) |
||||||
|
{ |
||||||
|
if (size < ht->max_entries) |
||||||
|
return true; |
||||||
|
for (unsigned i = ht->size_index + 1; i < ARRAY_SIZE(hash_sizes); i++) { |
||||||
|
if (hash_sizes[i].max_entries >= size) { |
||||||
|
_mesa_hash_table_rehash(ht, i); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return ht->max_entries >= size; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash table wrapper which supports 64-bit keys. |
||||||
|
* |
||||||
|
* TODO: unify all hash table implementations. |
||||||
|
*/ |
||||||
|
|
||||||
|
struct hash_key_u64 { |
||||||
|
uint64_t value; |
||||||
|
}; |
||||||
|
|
||||||
|
static uint32_t |
||||||
|
key_u64_hash(const void *key) |
||||||
|
{ |
||||||
|
return _mesa_hash_data(key, sizeof(struct hash_key_u64)); |
||||||
|
} |
||||||
|
|
||||||
|
static bool |
||||||
|
key_u64_equals(const void *a, const void *b) |
||||||
|
{ |
||||||
|
const struct hash_key_u64 *aa = a; |
||||||
|
const struct hash_key_u64 *bb = b; |
||||||
|
|
||||||
|
return aa->value == bb->value; |
||||||
|
} |
||||||
|
|
||||||
|
#define FREED_KEY_VALUE 0 |
||||||
|
|
||||||
|
struct hash_table_u64 * |
||||||
|
_mesa_hash_table_u64_create(void *mem_ctx) |
||||||
|
{ |
||||||
|
STATIC_ASSERT(FREED_KEY_VALUE != DELETED_KEY_VALUE); |
||||||
|
struct hash_table_u64 *ht; |
||||||
|
|
||||||
|
ht = CALLOC_STRUCT(hash_table_u64); |
||||||
|
if (!ht) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
if (sizeof(void *) == 8) { |
||||||
|
ht->table = _mesa_hash_table_create(mem_ctx, _mesa_hash_pointer, |
||||||
|
_mesa_key_pointer_equal); |
||||||
|
} else { |
||||||
|
ht->table = _mesa_hash_table_create(mem_ctx, key_u64_hash, |
||||||
|
key_u64_equals); |
||||||
|
} |
||||||
|
|
||||||
|
if (ht->table) |
||||||
|
_mesa_hash_table_set_deleted_key(ht->table, uint_key(DELETED_KEY_VALUE)); |
||||||
|
|
||||||
|
return ht; |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
_mesa_hash_table_u64_delete_key(struct hash_entry *entry) |
||||||
|
{ |
||||||
|
if (sizeof(void *) == 8) |
||||||
|
return; |
||||||
|
|
||||||
|
struct hash_key_u64 *_key = (struct hash_key_u64 *)entry->key; |
||||||
|
|
||||||
|
if (_key) |
||||||
|
free(_key); |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
_mesa_hash_table_u64_clear(struct hash_table_u64 *ht) |
||||||
|
{ |
||||||
|
if (!ht) |
||||||
|
return; |
||||||
|
|
||||||
|
_mesa_hash_table_clear(ht->table, _mesa_hash_table_u64_delete_key); |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
_mesa_hash_table_u64_destroy(struct hash_table_u64 *ht) |
||||||
|
{ |
||||||
|
if (!ht) |
||||||
|
return; |
||||||
|
|
||||||
|
_mesa_hash_table_u64_clear(ht); |
||||||
|
_mesa_hash_table_destroy(ht->table, NULL); |
||||||
|
free(ht); |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
_mesa_hash_table_u64_insert(struct hash_table_u64 *ht, uint64_t key, |
||||||
|
void *data) |
||||||
|
{ |
||||||
|
if (key == FREED_KEY_VALUE) { |
||||||
|
ht->freed_key_data = data; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (key == DELETED_KEY_VALUE) { |
||||||
|
ht->deleted_key_data = data; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (sizeof(void *) == 8) { |
||||||
|
_mesa_hash_table_insert(ht->table, (void *)(uintptr_t)key, data); |
||||||
|
} else { |
||||||
|
struct hash_key_u64 *_key = CALLOC_STRUCT(hash_key_u64); |
||||||
|
|
||||||
|
if (!_key) |
||||||
|
return; |
||||||
|
_key->value = key; |
||||||
|
|
||||||
|
_mesa_hash_table_insert(ht->table, _key, data); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static struct hash_entry * |
||||||
|
hash_table_u64_search(struct hash_table_u64 *ht, uint64_t key) |
||||||
|
{ |
||||||
|
if (sizeof(void *) == 8) { |
||||||
|
return _mesa_hash_table_search(ht->table, (void *)(uintptr_t)key); |
||||||
|
} else { |
||||||
|
struct hash_key_u64 _key = { .value = key }; |
||||||
|
return _mesa_hash_table_search(ht->table, &_key); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void * |
||||||
|
_mesa_hash_table_u64_search(struct hash_table_u64 *ht, uint64_t key) |
||||||
|
{ |
||||||
|
struct hash_entry *entry; |
||||||
|
|
||||||
|
if (key == FREED_KEY_VALUE) |
||||||
|
return ht->freed_key_data; |
||||||
|
|
||||||
|
if (key == DELETED_KEY_VALUE) |
||||||
|
return ht->deleted_key_data; |
||||||
|
|
||||||
|
entry = hash_table_u64_search(ht, key); |
||||||
|
if (!entry) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
return entry->data; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
_mesa_hash_table_u64_remove(struct hash_table_u64 *ht, uint64_t key) |
||||||
|
{ |
||||||
|
struct hash_entry *entry; |
||||||
|
|
||||||
|
if (key == FREED_KEY_VALUE) { |
||||||
|
ht->freed_key_data = NULL; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (key == DELETED_KEY_VALUE) { |
||||||
|
ht->deleted_key_data = NULL; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
entry = hash_table_u64_search(ht, key); |
||||||
|
if (!entry) |
||||||
|
return; |
||||||
|
|
||||||
|
if (sizeof(void *) == 8) { |
||||||
|
_mesa_hash_table_remove(ht->table, entry); |
||||||
|
} else { |
||||||
|
struct hash_key *_key = (struct hash_key *)entry->key; |
||||||
|
|
||||||
|
_mesa_hash_table_remove(ht->table, entry); |
||||||
|
free(_key); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,936 @@ |
|||||||
|
/*
|
||||||
|
* Copyright © 2010 Intel Corporation |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
* copy of this software and associated documentation files (the "Software"), |
||||||
|
* to deal in the Software without restriction, including without limitation |
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
* Software is furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice (including the next |
||||||
|
* paragraph) shall be included in all copies or substantial portions of the |
||||||
|
* Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
* DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <assert.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <stdarg.h> |
||||||
|
#include <stdio.h> |
||||||
|
#include <string.h> |
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
#include "util/macros.h" |
||||||
|
#include "util/u_math.h" |
||||||
|
|
||||||
|
/* Some versions of MinGW are missing _vscprintf's declaration, although they
|
||||||
|
* still provide the symbol in the import library. */ |
||||||
|
#ifdef __MINGW32__ |
||||||
|
_CRTIMP int _vscprintf(const char *format, va_list argptr); |
||||||
|
#endif |
||||||
|
|
||||||
|
#include "ralloc.h" |
||||||
|
|
||||||
|
#ifndef va_copy |
||||||
|
#ifdef __va_copy |
||||||
|
#define va_copy(dest, src) __va_copy((dest), (src)) |
||||||
|
#else |
||||||
|
#define va_copy(dest, src) (dest) = (src) |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
|
||||||
|
#define CANARY 0x5A1106 |
||||||
|
|
||||||
|
/* Align the header's size so that ralloc() allocations will return with the
|
||||||
|
* same alignment as a libc malloc would have (8 on 32-bit GLIBC, 16 on |
||||||
|
* 64-bit), avoiding performance penalities on x86 and alignment faults on |
||||||
|
* ARM. |
||||||
|
*/ |
||||||
|
struct |
||||||
|
#ifdef _MSC_VER |
||||||
|
#if _WIN64 |
||||||
|
__declspec(align(16)) |
||||||
|
#else |
||||||
|
__declspec(align(8)) |
||||||
|
#endif |
||||||
|
#elif defined(__LP64__) |
||||||
|
__attribute__((aligned(16))) |
||||||
|
#else |
||||||
|
__attribute__((aligned(8))) |
||||||
|
#endif |
||||||
|
ralloc_header |
||||||
|
{ |
||||||
|
#ifndef NDEBUG |
||||||
|
/* A canary value used to determine whether a pointer is ralloc'd. */ |
||||||
|
unsigned canary; |
||||||
|
#endif |
||||||
|
|
||||||
|
struct ralloc_header *parent; |
||||||
|
|
||||||
|
/* The first child (head of a linked list) */ |
||||||
|
struct ralloc_header *child; |
||||||
|
|
||||||
|
/* Linked list of siblings */ |
||||||
|
struct ralloc_header *prev; |
||||||
|
struct ralloc_header *next; |
||||||
|
|
||||||
|
void (*destructor)(void *); |
||||||
|
}; |
||||||
|
|
||||||
|
typedef struct ralloc_header ralloc_header; |
||||||
|
|
||||||
|
static void unlink_block(ralloc_header *info); |
||||||
|
static void unsafe_free(ralloc_header *info); |
||||||
|
|
||||||
|
static ralloc_header * |
||||||
|
get_header(const void *ptr) |
||||||
|
{ |
||||||
|
ralloc_header *info = (ralloc_header *) (((char *) ptr) - |
||||||
|
sizeof(ralloc_header)); |
||||||
|
assert(info->canary == CANARY); |
||||||
|
return info; |
||||||
|
} |
||||||
|
|
||||||
|
#define PTR_FROM_HEADER(info) (((char *) info) + sizeof(ralloc_header)) |
||||||
|
|
||||||
|
static void |
||||||
|
add_child(ralloc_header *parent, ralloc_header *info) |
||||||
|
{ |
||||||
|
if (parent != NULL) { |
||||||
|
info->parent = parent; |
||||||
|
info->next = parent->child; |
||||||
|
parent->child = info; |
||||||
|
|
||||||
|
if (info->next != NULL) |
||||||
|
info->next->prev = info; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void * |
||||||
|
ralloc_context(const void *ctx) |
||||||
|
{ |
||||||
|
return ralloc_size(ctx, 0); |
||||||
|
} |
||||||
|
|
||||||
|
void * |
||||||
|
ralloc_size(const void *ctx, size_t size) |
||||||
|
{ |
||||||
|
/* Some malloc allocation doesn't always align to 16 bytes even on 64 bits
|
||||||
|
* system, from Android bionic/tests/malloc_test.cpp: |
||||||
|
* - Allocations of a size that rounds up to a multiple of 16 bytes |
||||||
|
* must have at least 16 byte alignment. |
||||||
|
* - Allocations of a size that rounds up to a multiple of 8 bytes and |
||||||
|
* not 16 bytes, are only required to have at least 8 byte alignment. |
||||||
|
*/ |
||||||
|
void *block = malloc(align64(size + sizeof(ralloc_header), |
||||||
|
alignof(ralloc_header))); |
||||||
|
ralloc_header *info; |
||||||
|
ralloc_header *parent; |
||||||
|
|
||||||
|
if (unlikely(block == NULL)) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
info = (ralloc_header *) block; |
||||||
|
/* measurements have shown that calloc is slower (because of
|
||||||
|
* the multiplication overflow checking?), so clear things |
||||||
|
* manually |
||||||
|
*/ |
||||||
|
info->parent = NULL; |
||||||
|
info->child = NULL; |
||||||
|
info->prev = NULL; |
||||||
|
info->next = NULL; |
||||||
|
info->destructor = NULL; |
||||||
|
|
||||||
|
parent = ctx != NULL ? get_header(ctx) : NULL; |
||||||
|
|
||||||
|
add_child(parent, info); |
||||||
|
|
||||||
|
#ifndef NDEBUG |
||||||
|
info->canary = CANARY; |
||||||
|
#endif |
||||||
|
|
||||||
|
return PTR_FROM_HEADER(info); |
||||||
|
} |
||||||
|
|
||||||
|
void * |
||||||
|
rzalloc_size(const void *ctx, size_t size) |
||||||
|
{ |
||||||
|
void *ptr = ralloc_size(ctx, size); |
||||||
|
|
||||||
|
if (likely(ptr)) |
||||||
|
memset(ptr, 0, size); |
||||||
|
|
||||||
|
return ptr; |
||||||
|
} |
||||||
|
|
||||||
|
/* helper function - assumes ptr != NULL */ |
||||||
|
static void * |
||||||
|
resize(void *ptr, size_t size) |
||||||
|
{ |
||||||
|
ralloc_header *child, *old, *info; |
||||||
|
|
||||||
|
old = get_header(ptr); |
||||||
|
info = realloc(old, align64(size + sizeof(ralloc_header), |
||||||
|
alignof(ralloc_header))); |
||||||
|
|
||||||
|
if (info == NULL) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
/* Update parent and sibling's links to the reallocated node. */ |
||||||
|
if (info != old && info->parent != NULL) { |
||||||
|
if (info->parent->child == old) |
||||||
|
info->parent->child = info; |
||||||
|
|
||||||
|
if (info->prev != NULL) |
||||||
|
info->prev->next = info; |
||||||
|
|
||||||
|
if (info->next != NULL) |
||||||
|
info->next->prev = info; |
||||||
|
} |
||||||
|
|
||||||
|
/* Update child->parent links for all children */ |
||||||
|
for (child = info->child; child != NULL; child = child->next) |
||||||
|
child->parent = info; |
||||||
|
|
||||||
|
return PTR_FROM_HEADER(info); |
||||||
|
} |
||||||
|
|
||||||
|
void * |
||||||
|
reralloc_size(const void *ctx, void *ptr, size_t size) |
||||||
|
{ |
||||||
|
if (unlikely(ptr == NULL)) |
||||||
|
return ralloc_size(ctx, size); |
||||||
|
|
||||||
|
assert(ralloc_parent(ptr) == ctx); |
||||||
|
return resize(ptr, size); |
||||||
|
} |
||||||
|
|
||||||
|
void * |
||||||
|
rerzalloc_size(const void *ctx, void *ptr, size_t old_size, size_t new_size) |
||||||
|
{ |
||||||
|
if (unlikely(ptr == NULL)) |
||||||
|
return rzalloc_size(ctx, new_size); |
||||||
|
|
||||||
|
assert(ralloc_parent(ptr) == ctx); |
||||||
|
ptr = resize(ptr, new_size); |
||||||
|
|
||||||
|
if (new_size > old_size) |
||||||
|
memset((char *)ptr + old_size, 0, new_size - old_size); |
||||||
|
|
||||||
|
return ptr; |
||||||
|
} |
||||||
|
|
||||||
|
void * |
||||||
|
ralloc_array_size(const void *ctx, size_t size, unsigned count) |
||||||
|
{ |
||||||
|
if (count > SIZE_MAX/size) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
return ralloc_size(ctx, size * count); |
||||||
|
} |
||||||
|
|
||||||
|
void * |
||||||
|
rzalloc_array_size(const void *ctx, size_t size, unsigned count) |
||||||
|
{ |
||||||
|
if (count > SIZE_MAX/size) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
return rzalloc_size(ctx, size * count); |
||||||
|
} |
||||||
|
|
||||||
|
void * |
||||||
|
reralloc_array_size(const void *ctx, void *ptr, size_t size, unsigned count) |
||||||
|
{ |
||||||
|
if (count > SIZE_MAX/size) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
return reralloc_size(ctx, ptr, size * count); |
||||||
|
} |
||||||
|
|
||||||
|
void * |
||||||
|
rerzalloc_array_size(const void *ctx, void *ptr, size_t size, |
||||||
|
unsigned old_count, unsigned new_count) |
||||||
|
{ |
||||||
|
if (new_count > SIZE_MAX/size) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
return rerzalloc_size(ctx, ptr, size * old_count, size * new_count); |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
ralloc_free(void *ptr) |
||||||
|
{ |
||||||
|
ralloc_header *info; |
||||||
|
|
||||||
|
if (ptr == NULL) |
||||||
|
return; |
||||||
|
|
||||||
|
info = get_header(ptr); |
||||||
|
unlink_block(info); |
||||||
|
unsafe_free(info); |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
unlink_block(ralloc_header *info) |
||||||
|
{ |
||||||
|
/* Unlink from parent & siblings */ |
||||||
|
if (info->parent != NULL) { |
||||||
|
if (info->parent->child == info) |
||||||
|
info->parent->child = info->next; |
||||||
|
|
||||||
|
if (info->prev != NULL) |
||||||
|
info->prev->next = info->next; |
||||||
|
|
||||||
|
if (info->next != NULL) |
||||||
|
info->next->prev = info->prev; |
||||||
|
} |
||||||
|
info->parent = NULL; |
||||||
|
info->prev = NULL; |
||||||
|
info->next = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
unsafe_free(ralloc_header *info) |
||||||
|
{ |
||||||
|
/* Recursively free any children...don't waste time unlinking them. */ |
||||||
|
ralloc_header *temp; |
||||||
|
while (info->child != NULL) { |
||||||
|
temp = info->child; |
||||||
|
info->child = temp->next; |
||||||
|
unsafe_free(temp); |
||||||
|
} |
||||||
|
|
||||||
|
/* Free the block itself. Call the destructor first, if any. */ |
||||||
|
if (info->destructor != NULL) |
||||||
|
info->destructor(PTR_FROM_HEADER(info)); |
||||||
|
|
||||||
|
free(info); |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
ralloc_steal(const void *new_ctx, void *ptr) |
||||||
|
{ |
||||||
|
ralloc_header *info, *parent; |
||||||
|
|
||||||
|
if (unlikely(ptr == NULL)) |
||||||
|
return; |
||||||
|
|
||||||
|
info = get_header(ptr); |
||||||
|
parent = new_ctx ? get_header(new_ctx) : NULL; |
||||||
|
|
||||||
|
unlink_block(info); |
||||||
|
|
||||||
|
add_child(parent, info); |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
ralloc_adopt(const void *new_ctx, void *old_ctx) |
||||||
|
{ |
||||||
|
ralloc_header *new_info, *old_info, *child; |
||||||
|
|
||||||
|
if (unlikely(old_ctx == NULL)) |
||||||
|
return; |
||||||
|
|
||||||
|
old_info = get_header(old_ctx); |
||||||
|
new_info = get_header(new_ctx); |
||||||
|
|
||||||
|
/* If there are no children, bail. */ |
||||||
|
if (unlikely(old_info->child == NULL)) |
||||||
|
return; |
||||||
|
|
||||||
|
/* Set all the children's parent to new_ctx; get a pointer to the last child. */ |
||||||
|
for (child = old_info->child; child->next != NULL; child = child->next) { |
||||||
|
child->parent = new_info; |
||||||
|
} |
||||||
|
child->parent = new_info; |
||||||
|
|
||||||
|
/* Connect the two lists together; parent them to new_ctx; make old_ctx empty. */ |
||||||
|
child->next = new_info->child; |
||||||
|
if (child->next) |
||||||
|
child->next->prev = child; |
||||||
|
new_info->child = old_info->child; |
||||||
|
old_info->child = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
void * |
||||||
|
ralloc_parent(const void *ptr) |
||||||
|
{ |
||||||
|
ralloc_header *info; |
||||||
|
|
||||||
|
if (unlikely(ptr == NULL)) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
info = get_header(ptr); |
||||||
|
return info->parent ? PTR_FROM_HEADER(info->parent) : NULL; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
ralloc_set_destructor(const void *ptr, void(*destructor)(void *)) |
||||||
|
{ |
||||||
|
ralloc_header *info = get_header(ptr); |
||||||
|
info->destructor = destructor; |
||||||
|
} |
||||||
|
|
||||||
|
char * |
||||||
|
ralloc_strdup(const void *ctx, const char *str) |
||||||
|
{ |
||||||
|
size_t n; |
||||||
|
char *ptr; |
||||||
|
|
||||||
|
if (unlikely(str == NULL)) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
n = strlen(str); |
||||||
|
ptr = ralloc_array(ctx, char, n + 1); |
||||||
|
memcpy(ptr, str, n); |
||||||
|
ptr[n] = '\0'; |
||||||
|
return ptr; |
||||||
|
} |
||||||
|
|
||||||
|
char * |
||||||
|
ralloc_strndup(const void *ctx, const char *str, size_t max) |
||||||
|
{ |
||||||
|
size_t n; |
||||||
|
char *ptr; |
||||||
|
|
||||||
|
if (unlikely(str == NULL)) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
n = strnlen(str, max); |
||||||
|
ptr = ralloc_array(ctx, char, n + 1); |
||||||
|
memcpy(ptr, str, n); |
||||||
|
ptr[n] = '\0'; |
||||||
|
return ptr; |
||||||
|
} |
||||||
|
|
||||||
|
/* helper routine for strcat/strncat - n is the exact amount to copy */ |
||||||
|
static bool |
||||||
|
cat(char **dest, const char *str, size_t n) |
||||||
|
{ |
||||||
|
char *both; |
||||||
|
size_t existing_length; |
||||||
|
assert(dest != NULL && *dest != NULL); |
||||||
|
|
||||||
|
existing_length = strlen(*dest); |
||||||
|
both = resize(*dest, existing_length + n + 1); |
||||||
|
if (unlikely(both == NULL)) |
||||||
|
return false; |
||||||
|
|
||||||
|
memcpy(both + existing_length, str, n); |
||||||
|
both[existing_length + n] = '\0'; |
||||||
|
|
||||||
|
*dest = both; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
bool |
||||||
|
ralloc_strcat(char **dest, const char *str) |
||||||
|
{ |
||||||
|
return cat(dest, str, strlen(str)); |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
ralloc_strncat(char **dest, const char *str, size_t n) |
||||||
|
{ |
||||||
|
return cat(dest, str, strnlen(str, n)); |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
ralloc_str_append(char **dest, const char *str, |
||||||
|
size_t existing_length, size_t str_size) |
||||||
|
{ |
||||||
|
char *both; |
||||||
|
assert(dest != NULL && *dest != NULL); |
||||||
|
|
||||||
|
both = resize(*dest, existing_length + str_size + 1); |
||||||
|
if (unlikely(both == NULL)) |
||||||
|
return false; |
||||||
|
|
||||||
|
memcpy(both + existing_length, str, str_size); |
||||||
|
both[existing_length + str_size] = '\0'; |
||||||
|
|
||||||
|
*dest = both; |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
char * |
||||||
|
ralloc_asprintf(const void *ctx, const char *fmt, ...) |
||||||
|
{ |
||||||
|
char *ptr; |
||||||
|
va_list args; |
||||||
|
va_start(args, fmt); |
||||||
|
ptr = ralloc_vasprintf(ctx, fmt, args); |
||||||
|
va_end(args); |
||||||
|
return ptr; |
||||||
|
} |
||||||
|
|
||||||
|
/* Return the length of the string that would be generated by a printf-style
|
||||||
|
* format and argument list, not including the \0 byte. |
||||||
|
*/ |
||||||
|
static size_t |
||||||
|
printf_length(const char *fmt, va_list untouched_args) |
||||||
|
{ |
||||||
|
int size; |
||||||
|
char junk; |
||||||
|
|
||||||
|
/* Make a copy of the va_list so the original caller can still use it */ |
||||||
|
va_list args; |
||||||
|
va_copy(args, untouched_args); |
||||||
|
|
||||||
|
#ifdef _WIN32 |
||||||
|
/* We need to use _vcsprintf to calculate the size as vsnprintf returns -1
|
||||||
|
* if the number of characters to write is greater than count. |
||||||
|
*/ |
||||||
|
size = _vscprintf(fmt, args); |
||||||
|
(void)junk; |
||||||
|
#else |
||||||
|
size = vsnprintf(&junk, 1, fmt, args); |
||||||
|
#endif |
||||||
|
assert(size >= 0); |
||||||
|
|
||||||
|
va_end(args); |
||||||
|
|
||||||
|
return size; |
||||||
|
} |
||||||
|
|
||||||
|
char * |
||||||
|
ralloc_vasprintf(const void *ctx, const char *fmt, va_list args) |
||||||
|
{ |
||||||
|
size_t size = printf_length(fmt, args) + 1; |
||||||
|
|
||||||
|
char *ptr = ralloc_size(ctx, size); |
||||||
|
if (ptr != NULL) |
||||||
|
vsnprintf(ptr, size, fmt, args); |
||||||
|
|
||||||
|
return ptr; |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
ralloc_asprintf_append(char **str, const char *fmt, ...) |
||||||
|
{ |
||||||
|
bool success; |
||||||
|
va_list args; |
||||||
|
va_start(args, fmt); |
||||||
|
success = ralloc_vasprintf_append(str, fmt, args); |
||||||
|
va_end(args); |
||||||
|
return success; |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
ralloc_vasprintf_append(char **str, const char *fmt, va_list args) |
||||||
|
{ |
||||||
|
size_t existing_length; |
||||||
|
assert(str != NULL); |
||||||
|
existing_length = *str ? strlen(*str) : 0; |
||||||
|
return ralloc_vasprintf_rewrite_tail(str, &existing_length, fmt, args); |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
ralloc_asprintf_rewrite_tail(char **str, size_t *start, const char *fmt, ...) |
||||||
|
{ |
||||||
|
bool success; |
||||||
|
va_list args; |
||||||
|
va_start(args, fmt); |
||||||
|
success = ralloc_vasprintf_rewrite_tail(str, start, fmt, args); |
||||||
|
va_end(args); |
||||||
|
return success; |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
ralloc_vasprintf_rewrite_tail(char **str, size_t *start, const char *fmt, |
||||||
|
va_list args) |
||||||
|
{ |
||||||
|
size_t new_length; |
||||||
|
char *ptr; |
||||||
|
|
||||||
|
assert(str != NULL); |
||||||
|
|
||||||
|
if (unlikely(*str == NULL)) { |
||||||
|
// Assuming a NULL context is probably bad, but it's expected behavior.
|
||||||
|
*str = ralloc_vasprintf(NULL, fmt, args); |
||||||
|
*start = strlen(*str); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
new_length = printf_length(fmt, args); |
||||||
|
|
||||||
|
ptr = resize(*str, *start + new_length + 1); |
||||||
|
if (unlikely(ptr == NULL)) |
||||||
|
return false; |
||||||
|
|
||||||
|
vsnprintf(ptr + *start, new_length + 1, fmt, args); |
||||||
|
*str = ptr; |
||||||
|
*start += new_length; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* Linear allocator for short-lived allocations. |
||||||
|
*************************************************************************** |
||||||
|
* |
||||||
|
* The allocator consists of a parent node (2K buffer), which requires |
||||||
|
* a ralloc parent, and child nodes (allocations). Child nodes can't be freed |
||||||
|
* directly, because the parent doesn't track them. You have to release |
||||||
|
* the parent node in order to release all its children. |
||||||
|
* |
||||||
|
* The allocator uses a fixed-sized buffer with a monotonically increasing |
||||||
|
* offset after each allocation. If the buffer is all used, another buffer |
||||||
|
* is allocated, sharing the same ralloc parent, so all buffers are at |
||||||
|
* the same level in the ralloc hierarchy. |
||||||
|
* |
||||||
|
* The linear parent node is always the first buffer and keeps track of all |
||||||
|
* other buffers. |
||||||
|
*/ |
||||||
|
|
||||||
|
#define MIN_LINEAR_BUFSIZE 2048 |
||||||
|
#define SUBALLOC_ALIGNMENT 8 |
||||||
|
#define LMAGIC 0x87b9c7d3 |
||||||
|
|
||||||
|
struct |
||||||
|
#ifdef _MSC_VER |
||||||
|
__declspec(align(8)) |
||||||
|
#elif defined(__LP64__) |
||||||
|
__attribute__((aligned(16))) |
||||||
|
#else |
||||||
|
__attribute__((aligned(8))) |
||||||
|
#endif |
||||||
|
linear_header { |
||||||
|
#ifndef NDEBUG |
||||||
|
unsigned magic; /* for debugging */ |
||||||
|
#endif |
||||||
|
unsigned offset; /* points to the first unused byte in the buffer */ |
||||||
|
unsigned size; /* size of the buffer */ |
||||||
|
void *ralloc_parent; /* new buffers will use this */ |
||||||
|
struct linear_header *next; /* next buffer if we have more */ |
||||||
|
struct linear_header *latest; /* the only buffer that has free space */ |
||||||
|
|
||||||
|
/* After this structure, the buffer begins.
|
||||||
|
* Each suballocation consists of linear_size_chunk as its header followed |
||||||
|
* by the suballocation, so it goes: |
||||||
|
* |
||||||
|
* - linear_size_chunk |
||||||
|
* - allocated space |
||||||
|
* - linear_size_chunk |
||||||
|
* - allocated space |
||||||
|
* etc. |
||||||
|
* |
||||||
|
* linear_size_chunk is only needed by linear_realloc. |
||||||
|
*/ |
||||||
|
}; |
||||||
|
|
||||||
|
struct linear_size_chunk { |
||||||
|
unsigned size; /* for realloc */ |
||||||
|
unsigned _padding; |
||||||
|
}; |
||||||
|
|
||||||
|
typedef struct linear_header linear_header; |
||||||
|
typedef struct linear_size_chunk linear_size_chunk; |
||||||
|
|
||||||
|
#define LINEAR_PARENT_TO_HEADER(parent) \ |
||||||
|
(linear_header*) \
|
||||||
|
((char*)(parent) - sizeof(linear_size_chunk) - sizeof(linear_header)) |
||||||
|
|
||||||
|
/* Allocate the linear buffer with its header. */ |
||||||
|
static linear_header * |
||||||
|
create_linear_node(void *ralloc_ctx, unsigned min_size) |
||||||
|
{ |
||||||
|
linear_header *node; |
||||||
|
|
||||||
|
min_size += sizeof(linear_size_chunk); |
||||||
|
|
||||||
|
if (likely(min_size < MIN_LINEAR_BUFSIZE)) |
||||||
|
min_size = MIN_LINEAR_BUFSIZE; |
||||||
|
|
||||||
|
node = ralloc_size(ralloc_ctx, sizeof(linear_header) + min_size); |
||||||
|
if (unlikely(!node)) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
#ifndef NDEBUG |
||||||
|
node->magic = LMAGIC; |
||||||
|
#endif |
||||||
|
node->offset = 0; |
||||||
|
node->size = min_size; |
||||||
|
node->ralloc_parent = ralloc_ctx; |
||||||
|
node->next = NULL; |
||||||
|
node->latest = node; |
||||||
|
return node; |
||||||
|
} |
||||||
|
|
||||||
|
void * |
||||||
|
linear_alloc_child(void *parent, unsigned size) |
||||||
|
{ |
||||||
|
linear_header *first = LINEAR_PARENT_TO_HEADER(parent); |
||||||
|
linear_header *latest = first->latest; |
||||||
|
linear_header *new_node; |
||||||
|
linear_size_chunk *ptr; |
||||||
|
unsigned full_size; |
||||||
|
|
||||||
|
assert(first->magic == LMAGIC); |
||||||
|
assert(!latest->next); |
||||||
|
|
||||||
|
size = ALIGN_POT(size, SUBALLOC_ALIGNMENT); |
||||||
|
full_size = sizeof(linear_size_chunk) + size; |
||||||
|
|
||||||
|
if (unlikely(latest->offset + full_size > latest->size)) { |
||||||
|
/* allocate a new node */ |
||||||
|
new_node = create_linear_node(latest->ralloc_parent, size); |
||||||
|
if (unlikely(!new_node)) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
first->latest = new_node; |
||||||
|
latest->latest = new_node; |
||||||
|
latest->next = new_node; |
||||||
|
latest = new_node; |
||||||
|
} |
||||||
|
|
||||||
|
ptr = (linear_size_chunk *)((char*)&latest[1] + latest->offset); |
||||||
|
ptr->size = size; |
||||||
|
latest->offset += full_size; |
||||||
|
|
||||||
|
assert((uintptr_t)&ptr[1] % SUBALLOC_ALIGNMENT == 0); |
||||||
|
return &ptr[1]; |
||||||
|
} |
||||||
|
|
||||||
|
void * |
||||||
|
linear_alloc_parent(void *ralloc_ctx, unsigned size) |
||||||
|
{ |
||||||
|
linear_header *node; |
||||||
|
|
||||||
|
if (unlikely(!ralloc_ctx)) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
size = ALIGN_POT(size, SUBALLOC_ALIGNMENT); |
||||||
|
|
||||||
|
node = create_linear_node(ralloc_ctx, size); |
||||||
|
if (unlikely(!node)) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
return linear_alloc_child((char*)node + |
||||||
|
sizeof(linear_header) + |
||||||
|
sizeof(linear_size_chunk), size); |
||||||
|
} |
||||||
|
|
||||||
|
void * |
||||||
|
linear_zalloc_child(void *parent, unsigned size) |
||||||
|
{ |
||||||
|
void *ptr = linear_alloc_child(parent, size); |
||||||
|
|
||||||
|
if (likely(ptr)) |
||||||
|
memset(ptr, 0, size); |
||||||
|
return ptr; |
||||||
|
} |
||||||
|
|
||||||
|
void * |
||||||
|
linear_zalloc_parent(void *parent, unsigned size) |
||||||
|
{ |
||||||
|
void *ptr = linear_alloc_parent(parent, size); |
||||||
|
|
||||||
|
if (likely(ptr)) |
||||||
|
memset(ptr, 0, size); |
||||||
|
return ptr; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
linear_free_parent(void *ptr) |
||||||
|
{ |
||||||
|
linear_header *node; |
||||||
|
|
||||||
|
if (unlikely(!ptr)) |
||||||
|
return; |
||||||
|
|
||||||
|
node = LINEAR_PARENT_TO_HEADER(ptr); |
||||||
|
assert(node->magic == LMAGIC); |
||||||
|
|
||||||
|
while (node) { |
||||||
|
void *ptr = node; |
||||||
|
|
||||||
|
node = node->next; |
||||||
|
ralloc_free(ptr); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
ralloc_steal_linear_parent(void *new_ralloc_ctx, void *ptr) |
||||||
|
{ |
||||||
|
linear_header *node; |
||||||
|
|
||||||
|
if (unlikely(!ptr)) |
||||||
|
return; |
||||||
|
|
||||||
|
node = LINEAR_PARENT_TO_HEADER(ptr); |
||||||
|
assert(node->magic == LMAGIC); |
||||||
|
|
||||||
|
while (node) { |
||||||
|
ralloc_steal(new_ralloc_ctx, node); |
||||||
|
node->ralloc_parent = new_ralloc_ctx; |
||||||
|
node = node->next; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void * |
||||||
|
ralloc_parent_of_linear_parent(void *ptr) |
||||||
|
{ |
||||||
|
linear_header *node = LINEAR_PARENT_TO_HEADER(ptr); |
||||||
|
assert(node->magic == LMAGIC); |
||||||
|
return node->ralloc_parent; |
||||||
|
} |
||||||
|
|
||||||
|
void * |
||||||
|
linear_realloc(void *parent, void *old, unsigned new_size) |
||||||
|
{ |
||||||
|
unsigned old_size = 0; |
||||||
|
ralloc_header *new_ptr; |
||||||
|
|
||||||
|
new_ptr = linear_alloc_child(parent, new_size); |
||||||
|
|
||||||
|
if (unlikely(!old)) |
||||||
|
return new_ptr; |
||||||
|
|
||||||
|
old_size = ((linear_size_chunk*)old)[-1].size; |
||||||
|
|
||||||
|
if (likely(new_ptr && old_size)) |
||||||
|
memcpy(new_ptr, old, MIN2(old_size, new_size)); |
||||||
|
|
||||||
|
return new_ptr; |
||||||
|
} |
||||||
|
|
||||||
|
/* All code below is pretty much copied from ralloc and only the alloc
|
||||||
|
* calls are different. |
||||||
|
*/ |
||||||
|
|
||||||
|
char * |
||||||
|
linear_strdup(void *parent, const char *str) |
||||||
|
{ |
||||||
|
unsigned n; |
||||||
|
char *ptr; |
||||||
|
|
||||||
|
if (unlikely(!str)) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
n = strlen(str); |
||||||
|
ptr = linear_alloc_child(parent, n + 1); |
||||||
|
if (unlikely(!ptr)) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
memcpy(ptr, str, n); |
||||||
|
ptr[n] = '\0'; |
||||||
|
return ptr; |
||||||
|
} |
||||||
|
|
||||||
|
char * |
||||||
|
linear_asprintf(void *parent, const char *fmt, ...) |
||||||
|
{ |
||||||
|
char *ptr; |
||||||
|
va_list args; |
||||||
|
va_start(args, fmt); |
||||||
|
ptr = linear_vasprintf(parent, fmt, args); |
||||||
|
va_end(args); |
||||||
|
return ptr; |
||||||
|
} |
||||||
|
|
||||||
|
char * |
||||||
|
linear_vasprintf(void *parent, const char *fmt, va_list args) |
||||||
|
{ |
||||||
|
unsigned size = printf_length(fmt, args) + 1; |
||||||
|
|
||||||
|
char *ptr = linear_alloc_child(parent, size); |
||||||
|
if (ptr != NULL) |
||||||
|
vsnprintf(ptr, size, fmt, args); |
||||||
|
|
||||||
|
return ptr; |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
linear_asprintf_append(void *parent, char **str, const char *fmt, ...) |
||||||
|
{ |
||||||
|
bool success; |
||||||
|
va_list args; |
||||||
|
va_start(args, fmt); |
||||||
|
success = linear_vasprintf_append(parent, str, fmt, args); |
||||||
|
va_end(args); |
||||||
|
return success; |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
linear_vasprintf_append(void *parent, char **str, const char *fmt, va_list args) |
||||||
|
{ |
||||||
|
size_t existing_length; |
||||||
|
assert(str != NULL); |
||||||
|
existing_length = *str ? strlen(*str) : 0; |
||||||
|
return linear_vasprintf_rewrite_tail(parent, str, &existing_length, fmt, args); |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
linear_asprintf_rewrite_tail(void *parent, char **str, size_t *start, |
||||||
|
const char *fmt, ...) |
||||||
|
{ |
||||||
|
bool success; |
||||||
|
va_list args; |
||||||
|
va_start(args, fmt); |
||||||
|
success = linear_vasprintf_rewrite_tail(parent, str, start, fmt, args); |
||||||
|
va_end(args); |
||||||
|
return success; |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
linear_vasprintf_rewrite_tail(void *parent, char **str, size_t *start, |
||||||
|
const char *fmt, va_list args) |
||||||
|
{ |
||||||
|
size_t new_length; |
||||||
|
char *ptr; |
||||||
|
|
||||||
|
assert(str != NULL); |
||||||
|
|
||||||
|
if (unlikely(*str == NULL)) { |
||||||
|
*str = linear_vasprintf(parent, fmt, args); |
||||||
|
*start = strlen(*str); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
new_length = printf_length(fmt, args); |
||||||
|
|
||||||
|
ptr = linear_realloc(parent, *str, *start + new_length + 1); |
||||||
|
if (unlikely(ptr == NULL)) |
||||||
|
return false; |
||||||
|
|
||||||
|
vsnprintf(ptr + *start, new_length + 1, fmt, args); |
||||||
|
*str = ptr; |
||||||
|
*start += new_length; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/* helper routine for strcat/strncat - n is the exact amount to copy */ |
||||||
|
static bool |
||||||
|
linear_cat(void *parent, char **dest, const char *str, unsigned n) |
||||||
|
{ |
||||||
|
char *both; |
||||||
|
unsigned existing_length; |
||||||
|
assert(dest != NULL && *dest != NULL); |
||||||
|
|
||||||
|
existing_length = strlen(*dest); |
||||||
|
both = linear_realloc(parent, *dest, existing_length + n + 1); |
||||||
|
if (unlikely(both == NULL)) |
||||||
|
return false; |
||||||
|
|
||||||
|
memcpy(both + existing_length, str, n); |
||||||
|
both[existing_length + n] = '\0'; |
||||||
|
|
||||||
|
*dest = both; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
linear_strcat(void *parent, char **dest, const char *str) |
||||||
|
{ |
||||||
|
return linear_cat(parent, dest, str, strlen(str)); |
||||||
|
} |
@ -0,0 +1,604 @@ |
|||||||
|
/*
|
||||||
|
* Copyright © 2010 Intel Corporation |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
* copy of this software and associated documentation files (the "Software"), |
||||||
|
* to deal in the Software without restriction, including without limitation |
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
* Software is furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice (including the next |
||||||
|
* paragraph) shall be included in all copies or substantial portions of the |
||||||
|
* Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
* DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
/**
|
||||||
|
* \file ralloc.h |
||||||
|
* |
||||||
|
* ralloc: a recursive memory allocator |
||||||
|
* |
||||||
|
* The ralloc memory allocator creates a hierarchy of allocated |
||||||
|
* objects. Every allocation is in reference to some parent, and |
||||||
|
* every allocated object can in turn be used as the parent of a |
||||||
|
* subsequent allocation. This allows for extremely convenient |
||||||
|
* discarding of an entire tree/sub-tree of allocations by calling |
||||||
|
* ralloc_free on any particular object to free it and all of its |
||||||
|
* children. |
||||||
|
* |
||||||
|
* The conceptual working of ralloc was directly inspired by Andrew |
||||||
|
* Tridgell's talloc, but ralloc is an independent implementation |
||||||
|
* released under the MIT license and tuned for Mesa. |
||||||
|
* |
||||||
|
* talloc is more sophisticated than ralloc in that it includes reference |
||||||
|
* counting and useful debugging features. However, it is released under |
||||||
|
* a non-permissive open source license. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef RALLOC_H |
||||||
|
#define RALLOC_H |
||||||
|
|
||||||
|
#include <stddef.h> |
||||||
|
#include <stdarg.h> |
||||||
|
#include <stdbool.h> |
||||||
|
|
||||||
|
#include "macros.h" |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
/**
|
||||||
|
* \def ralloc(ctx, type) |
||||||
|
* Allocate a new object chained off of the given context. |
||||||
|
* |
||||||
|
* This is equivalent to: |
||||||
|
* \code |
||||||
|
* ((type *) ralloc_size(ctx, sizeof(type)) |
||||||
|
* \endcode |
||||||
|
*/ |
||||||
|
#define ralloc(ctx, type) ((type *) ralloc_size(ctx, sizeof(type))) |
||||||
|
|
||||||
|
/**
|
||||||
|
* \def rzalloc(ctx, type) |
||||||
|
* Allocate a new object out of the given context and initialize it to zero. |
||||||
|
* |
||||||
|
* This is equivalent to: |
||||||
|
* \code |
||||||
|
* ((type *) rzalloc_size(ctx, sizeof(type)) |
||||||
|
* \endcode |
||||||
|
*/ |
||||||
|
#define rzalloc(ctx, type) ((type *) rzalloc_size(ctx, sizeof(type))) |
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate a new ralloc context. |
||||||
|
* |
||||||
|
* While any ralloc'd pointer can be used as a context, sometimes it is useful |
||||||
|
* to simply allocate a context with no associated memory. |
||||||
|
* |
||||||
|
* It is equivalent to: |
||||||
|
* \code |
||||||
|
* ((type *) ralloc_size(ctx, 0) |
||||||
|
* \endcode |
||||||
|
*/ |
||||||
|
void *ralloc_context(const void *ctx); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate memory chained off of the given context. |
||||||
|
* |
||||||
|
* This is the core allocation routine which is used by all others. It |
||||||
|
* simply allocates storage for \p size bytes and returns the pointer, |
||||||
|
* similar to \c malloc. |
||||||
|
*/ |
||||||
|
void *ralloc_size(const void *ctx, size_t size) MALLOCLIKE; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate zero-initialized memory chained off of the given context. |
||||||
|
* |
||||||
|
* This is similar to \c calloc with a size of 1. |
||||||
|
*/ |
||||||
|
void *rzalloc_size(const void *ctx, size_t size) MALLOCLIKE; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize a piece of ralloc-managed memory, preserving data. |
||||||
|
* |
||||||
|
* Similar to \c realloc. Unlike C89, passing 0 for \p size does not free the |
||||||
|
* memory. Instead, it resizes it to a 0-byte ralloc context, just like |
||||||
|
* calling ralloc_size(ctx, 0). This is different from talloc. |
||||||
|
* |
||||||
|
* \param ctx The context to use for new allocation. If \p ptr != NULL, |
||||||
|
* it must be the same as ralloc_parent(\p ptr). |
||||||
|
* \param ptr Pointer to the memory to be resized. May be NULL. |
||||||
|
* \param size The amount of memory to allocate, in bytes. |
||||||
|
*/ |
||||||
|
void *reralloc_size(const void *ctx, void *ptr, size_t size); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize a ralloc-managed array, preserving data and initializing any newly |
||||||
|
* allocated data to zero. |
||||||
|
* |
||||||
|
* Similar to \c realloc. Unlike C89, passing 0 for \p size does not free the |
||||||
|
* memory. Instead, it resizes it to a 0-byte ralloc context, just like |
||||||
|
* calling ralloc_size(ctx, 0). This is different from talloc. |
||||||
|
* |
||||||
|
* \param ctx The context to use for new allocation. If \p ptr != NULL, |
||||||
|
* it must be the same as ralloc_parent(\p ptr). |
||||||
|
* \param ptr Pointer to the memory to be resized. May be NULL. |
||||||
|
* \param old_size The amount of memory in the previous allocation, in bytes. |
||||||
|
* \param new_size The amount of memory to allocate, in bytes. |
||||||
|
*/ |
||||||
|
void *rerzalloc_size(const void *ctx, void *ptr, |
||||||
|
size_t old_size, size_t new_size); |
||||||
|
|
||||||
|
/// \defgroup array Array Allocators @{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \def ralloc_array(ctx, type, count) |
||||||
|
* Allocate an array of objects chained off the given context. |
||||||
|
* |
||||||
|
* Similar to \c calloc, but does not initialize the memory to zero. |
||||||
|
* |
||||||
|
* More than a convenience function, this also checks for integer overflow when |
||||||
|
* multiplying \c sizeof(type) and \p count. This is necessary for security. |
||||||
|
* |
||||||
|
* This is equivalent to: |
||||||
|
* \code |
||||||
|
* ((type *) ralloc_array_size(ctx, sizeof(type), count) |
||||||
|
* \endcode |
||||||
|
*/ |
||||||
|
#define ralloc_array(ctx, type, count) \ |
||||||
|
((type *) ralloc_array_size(ctx, sizeof(type), count)) |
||||||
|
|
||||||
|
/**
|
||||||
|
* \def rzalloc_array(ctx, type, count) |
||||||
|
* Allocate a zero-initialized array chained off the given context. |
||||||
|
* |
||||||
|
* Similar to \c calloc. |
||||||
|
* |
||||||
|
* More than a convenience function, this also checks for integer overflow when |
||||||
|
* multiplying \c sizeof(type) and \p count. This is necessary for security. |
||||||
|
* |
||||||
|
* This is equivalent to: |
||||||
|
* \code |
||||||
|
* ((type *) rzalloc_array_size(ctx, sizeof(type), count) |
||||||
|
* \endcode |
||||||
|
*/ |
||||||
|
#define rzalloc_array(ctx, type, count) \ |
||||||
|
((type *) rzalloc_array_size(ctx, sizeof(type), count)) |
||||||
|
|
||||||
|
/**
|
||||||
|
* \def reralloc(ctx, ptr, type, count) |
||||||
|
* Resize a ralloc-managed array, preserving data. |
||||||
|
* |
||||||
|
* Similar to \c realloc. Unlike C89, passing 0 for \p size does not free the |
||||||
|
* memory. Instead, it resizes it to a 0-byte ralloc context, just like |
||||||
|
* calling ralloc_size(ctx, 0). This is different from talloc. |
||||||
|
* |
||||||
|
* More than a convenience function, this also checks for integer overflow when |
||||||
|
* multiplying \c sizeof(type) and \p count. This is necessary for security. |
||||||
|
* |
||||||
|
* \param ctx The context to use for new allocation. If \p ptr != NULL, |
||||||
|
* it must be the same as ralloc_parent(\p ptr). |
||||||
|
* \param ptr Pointer to the array to be resized. May be NULL. |
||||||
|
* \param type The element type. |
||||||
|
* \param count The number of elements to allocate. |
||||||
|
*/ |
||||||
|
#define reralloc(ctx, ptr, type, count) \ |
||||||
|
((type *) reralloc_array_size(ctx, ptr, sizeof(type), count)) |
||||||
|
|
||||||
|
/**
|
||||||
|
* \def rerzalloc(ctx, ptr, type, count) |
||||||
|
* Resize a ralloc-managed array, preserving data and initializing any newly |
||||||
|
* allocated data to zero. |
||||||
|
* |
||||||
|
* Similar to \c realloc. Unlike C89, passing 0 for \p size does not free the |
||||||
|
* memory. Instead, it resizes it to a 0-byte ralloc context, just like |
||||||
|
* calling ralloc_size(ctx, 0). This is different from talloc. |
||||||
|
* |
||||||
|
* More than a convenience function, this also checks for integer overflow when |
||||||
|
* multiplying \c sizeof(type) and \p count. This is necessary for security. |
||||||
|
* |
||||||
|
* \param ctx The context to use for new allocation. If \p ptr != NULL, |
||||||
|
* it must be the same as ralloc_parent(\p ptr). |
||||||
|
* \param ptr Pointer to the array to be resized. May be NULL. |
||||||
|
* \param type The element type. |
||||||
|
* \param old_count The number of elements in the previous allocation. |
||||||
|
* \param new_count The number of elements to allocate. |
||||||
|
*/ |
||||||
|
#define rerzalloc(ctx, ptr, type, old_count, new_count) \ |
||||||
|
((type *) rerzalloc_array_size(ctx, ptr, sizeof(type), old_count, new_count)) |
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate memory for an array chained off the given context. |
||||||
|
* |
||||||
|
* Similar to \c calloc, but does not initialize the memory to zero. |
||||||
|
* |
||||||
|
* More than a convenience function, this also checks for integer overflow when |
||||||
|
* multiplying \p size and \p count. This is necessary for security. |
||||||
|
*/ |
||||||
|
void *ralloc_array_size(const void *ctx, size_t size, unsigned count) MALLOCLIKE; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate a zero-initialized array chained off the given context. |
||||||
|
* |
||||||
|
* Similar to \c calloc. |
||||||
|
* |
||||||
|
* More than a convenience function, this also checks for integer overflow when |
||||||
|
* multiplying \p size and \p count. This is necessary for security. |
||||||
|
*/ |
||||||
|
void *rzalloc_array_size(const void *ctx, size_t size, unsigned count) MALLOCLIKE; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize a ralloc-managed array, preserving data. |
||||||
|
* |
||||||
|
* Similar to \c realloc. Unlike C89, passing 0 for \p size does not free the |
||||||
|
* memory. Instead, it resizes it to a 0-byte ralloc context, just like |
||||||
|
* calling ralloc_size(ctx, 0). This is different from talloc. |
||||||
|
* |
||||||
|
* More than a convenience function, this also checks for integer overflow when |
||||||
|
* multiplying \c sizeof(type) and \p count. This is necessary for security. |
||||||
|
* |
||||||
|
* \param ctx The context to use for new allocation. If \p ptr != NULL, |
||||||
|
* it must be the same as ralloc_parent(\p ptr). |
||||||
|
* \param ptr Pointer to the array to be resized. May be NULL. |
||||||
|
* \param size The size of an individual element. |
||||||
|
* \param count The number of elements to allocate. |
||||||
|
* |
||||||
|
* \return True unless allocation failed. |
||||||
|
*/ |
||||||
|
void *reralloc_array_size(const void *ctx, void *ptr, size_t size, |
||||||
|
unsigned count); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize a ralloc-managed array, preserving data and initializing any newly |
||||||
|
* allocated data to zero. |
||||||
|
* |
||||||
|
* Similar to \c realloc. Unlike C89, passing 0 for \p size does not free the |
||||||
|
* memory. Instead, it resizes it to a 0-byte ralloc context, just like |
||||||
|
* calling ralloc_size(ctx, 0). This is different from talloc. |
||||||
|
* |
||||||
|
* More than a convenience function, this also checks for integer overflow when |
||||||
|
* multiplying \c sizeof(type) and \p count. This is necessary for security. |
||||||
|
* |
||||||
|
* \param ctx The context to use for new allocation. If \p ptr != NULL, |
||||||
|
* it must be the same as ralloc_parent(\p ptr). |
||||||
|
* \param ptr Pointer to the array to be resized. May be NULL. |
||||||
|
* \param size The size of an individual element. |
||||||
|
* \param old_count The number of elements in the previous allocation. |
||||||
|
* \param new_count The number of elements to allocate. |
||||||
|
* |
||||||
|
* \return True unless allocation failed. |
||||||
|
*/ |
||||||
|
void *rerzalloc_array_size(const void *ctx, void *ptr, size_t size, |
||||||
|
unsigned old_count, unsigned new_count); |
||||||
|
/// @}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free a piece of ralloc-managed memory. |
||||||
|
* |
||||||
|
* This will also free the memory of any children allocated this context. |
||||||
|
*/ |
||||||
|
void ralloc_free(void *ptr); |
||||||
|
|
||||||
|
/**
|
||||||
|
* "Steal" memory from one context, changing it to another. |
||||||
|
* |
||||||
|
* This changes \p ptr's context to \p new_ctx. This is quite useful if |
||||||
|
* memory is allocated out of a temporary context. |
||||||
|
*/ |
||||||
|
void ralloc_steal(const void *new_ctx, void *ptr); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Reparent all children from one context to another. |
||||||
|
* |
||||||
|
* This effectively calls ralloc_steal(new_ctx, child) for all children of \p old_ctx. |
||||||
|
*/ |
||||||
|
void ralloc_adopt(const void *new_ctx, void *old_ctx); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the given pointer's ralloc context. |
||||||
|
*/ |
||||||
|
void *ralloc_parent(const void *ptr); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a callback to occur just before an object is freed. |
||||||
|
*/ |
||||||
|
void ralloc_set_destructor(const void *ptr, void(*destructor)(void *)); |
||||||
|
|
||||||
|
/// \defgroup array String Functions @{
|
||||||
|
/**
|
||||||
|
* Duplicate a string, allocating the memory from the given context. |
||||||
|
*/ |
||||||
|
char *ralloc_strdup(const void *ctx, const char *str) MALLOCLIKE; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Duplicate a string, allocating the memory from the given context. |
||||||
|
* |
||||||
|
* Like \c strndup, at most \p n characters are copied. If \p str is longer |
||||||
|
* than \p n characters, \p n are copied, and a termining \c '\0' byte is added. |
||||||
|
*/ |
||||||
|
char *ralloc_strndup(const void *ctx, const char *str, size_t n) MALLOCLIKE; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Concatenate two strings, allocating the necessary space. |
||||||
|
* |
||||||
|
* This appends \p str to \p *dest, similar to \c strcat, using ralloc_resize |
||||||
|
* to expand \p *dest to the appropriate size. \p dest will be updated to the |
||||||
|
* new pointer unless allocation fails. |
||||||
|
* |
||||||
|
* The result will always be null-terminated. |
||||||
|
* |
||||||
|
* \return True unless allocation failed. |
||||||
|
*/ |
||||||
|
bool ralloc_strcat(char **dest, const char *str); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Concatenate two strings, allocating the necessary space. |
||||||
|
* |
||||||
|
* This appends at most \p n bytes of \p str to \p *dest, using ralloc_resize |
||||||
|
* to expand \p *dest to the appropriate size. \p dest will be updated to the |
||||||
|
* new pointer unless allocation fails. |
||||||
|
* |
||||||
|
* The result will always be null-terminated; \p str does not need to be null |
||||||
|
* terminated if it is longer than \p n. |
||||||
|
* |
||||||
|
* \return True unless allocation failed. |
||||||
|
*/ |
||||||
|
bool ralloc_strncat(char **dest, const char *str, size_t n); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Concatenate two strings, allocating the necessary space. |
||||||
|
* |
||||||
|
* This appends \p n bytes of \p str to \p *dest, using ralloc_resize |
||||||
|
* to expand \p *dest to the appropriate size. \p dest will be updated to the |
||||||
|
* new pointer unless allocation fails. |
||||||
|
* |
||||||
|
* The result will always be null-terminated. |
||||||
|
* |
||||||
|
* This function differs from ralloc_strcat() and ralloc_strncat() in that it |
||||||
|
* does not do any strlen() calls which can become costly on large strings. |
||||||
|
* |
||||||
|
* \return True unless allocation failed. |
||||||
|
*/ |
||||||
|
bool |
||||||
|
ralloc_str_append(char **dest, const char *str, |
||||||
|
size_t existing_length, size_t str_size); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Print to a string. |
||||||
|
* |
||||||
|
* This is analogous to \c sprintf, but allocates enough space (using \p ctx |
||||||
|
* as the context) for the resulting string. |
||||||
|
* |
||||||
|
* \return The newly allocated string. |
||||||
|
*/ |
||||||
|
char *ralloc_asprintf (const void *ctx, const char *fmt, ...) PRINTFLIKE(2, 3) MALLOCLIKE; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Print to a string, given a va_list. |
||||||
|
* |
||||||
|
* This is analogous to \c vsprintf, but allocates enough space (using \p ctx |
||||||
|
* as the context) for the resulting string. |
||||||
|
* |
||||||
|
* \return The newly allocated string. |
||||||
|
*/ |
||||||
|
char *ralloc_vasprintf(const void *ctx, const char *fmt, va_list args) MALLOCLIKE; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Rewrite the tail of an existing string, starting at a given index. |
||||||
|
* |
||||||
|
* Overwrites the contents of *str starting at \p start with newly formatted |
||||||
|
* text, including a new null-terminator. Allocates more memory as necessary. |
||||||
|
* |
||||||
|
* This can be used to append formatted text when the length of the existing |
||||||
|
* string is already known, saving a strlen() call. |
||||||
|
* |
||||||
|
* \sa ralloc_asprintf_append |
||||||
|
* |
||||||
|
* \param str The string to be updated. |
||||||
|
* \param start The index to start appending new data at. |
||||||
|
* \param fmt A printf-style formatting string |
||||||
|
* |
||||||
|
* \p str will be updated to the new pointer unless allocation fails. |
||||||
|
* \p start will be increased by the length of the newly formatted text. |
||||||
|
* |
||||||
|
* \return True unless allocation failed. |
||||||
|
*/ |
||||||
|
bool ralloc_asprintf_rewrite_tail(char **str, size_t *start, |
||||||
|
const char *fmt, ...) |
||||||
|
PRINTFLIKE(3, 4); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Rewrite the tail of an existing string, starting at a given index. |
||||||
|
* |
||||||
|
* Overwrites the contents of *str starting at \p start with newly formatted |
||||||
|
* text, including a new null-terminator. Allocates more memory as necessary. |
||||||
|
* |
||||||
|
* This can be used to append formatted text when the length of the existing |
||||||
|
* string is already known, saving a strlen() call. |
||||||
|
* |
||||||
|
* \sa ralloc_vasprintf_append |
||||||
|
* |
||||||
|
* \param str The string to be updated. |
||||||
|
* \param start The index to start appending new data at. |
||||||
|
* \param fmt A printf-style formatting string |
||||||
|
* \param args A va_list containing the data to be formatted |
||||||
|
* |
||||||
|
* \p str will be updated to the new pointer unless allocation fails. |
||||||
|
* \p start will be increased by the length of the newly formatted text. |
||||||
|
* |
||||||
|
* \return True unless allocation failed. |
||||||
|
*/ |
||||||
|
bool ralloc_vasprintf_rewrite_tail(char **str, size_t *start, const char *fmt, |
||||||
|
va_list args); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Append formatted text to the supplied string. |
||||||
|
* |
||||||
|
* This is equivalent to |
||||||
|
* \code |
||||||
|
* ralloc_asprintf_rewrite_tail(str, strlen(*str), fmt, ...) |
||||||
|
* \endcode |
||||||
|
* |
||||||
|
* \sa ralloc_asprintf |
||||||
|
* \sa ralloc_asprintf_rewrite_tail |
||||||
|
* \sa ralloc_strcat |
||||||
|
* |
||||||
|
* \p str will be updated to the new pointer unless allocation fails. |
||||||
|
* |
||||||
|
* \return True unless allocation failed. |
||||||
|
*/ |
||||||
|
bool ralloc_asprintf_append (char **str, const char *fmt, ...) |
||||||
|
PRINTFLIKE(2, 3); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Append formatted text to the supplied string, given a va_list. |
||||||
|
* |
||||||
|
* This is equivalent to |
||||||
|
* \code |
||||||
|
* ralloc_vasprintf_rewrite_tail(str, strlen(*str), fmt, args) |
||||||
|
* \endcode |
||||||
|
* |
||||||
|
* \sa ralloc_vasprintf |
||||||
|
* \sa ralloc_vasprintf_rewrite_tail |
||||||
|
* \sa ralloc_strcat |
||||||
|
* |
||||||
|
* \p str will be updated to the new pointer unless allocation fails. |
||||||
|
* |
||||||
|
* \return True unless allocation failed. |
||||||
|
*/ |
||||||
|
bool ralloc_vasprintf_append(char **str, const char *fmt, va_list args); |
||||||
|
/// @}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declare C++ new and delete operators which use ralloc. |
||||||
|
* |
||||||
|
* Placing this macro in the body of a class makes it possible to do: |
||||||
|
* |
||||||
|
* TYPE *var = new(mem_ctx) TYPE(...); |
||||||
|
* delete var; |
||||||
|
* |
||||||
|
* which is more idiomatic in C++ than calling ralloc. |
||||||
|
*/ |
||||||
|
#define DECLARE_ALLOC_CXX_OPERATORS_TEMPLATE(TYPE, ALLOC_FUNC) \ |
||||||
|
private: \
|
||||||
|
static void _ralloc_destructor(void *p) \
|
||||||
|
{ \
|
||||||
|
reinterpret_cast<TYPE *>(p)->TYPE::~TYPE(); \
|
||||||
|
} \
|
||||||
|
public: \
|
||||||
|
static void* operator new(size_t size, void *mem_ctx) \
|
||||||
|
{ \
|
||||||
|
void *p = ALLOC_FUNC(mem_ctx, size); \
|
||||||
|
assert(p != NULL); \
|
||||||
|
if (!HAS_TRIVIAL_DESTRUCTOR(TYPE)) \
|
||||||
|
ralloc_set_destructor(p, _ralloc_destructor); \
|
||||||
|
return p; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static void operator delete(void *p) \
|
||||||
|
{ \
|
||||||
|
/* The object's destructor is guaranteed to have already been \
|
||||||
|
* called by the delete operator at this point -- Make sure it's \
|
||||||
|
* not called again. \
|
||||||
|
*/ \
|
||||||
|
if (!HAS_TRIVIAL_DESTRUCTOR(TYPE)) \
|
||||||
|
ralloc_set_destructor(p, NULL); \
|
||||||
|
ralloc_free(p); \
|
||||||
|
} |
||||||
|
|
||||||
|
#define DECLARE_RALLOC_CXX_OPERATORS(type) \ |
||||||
|
DECLARE_ALLOC_CXX_OPERATORS_TEMPLATE(type, ralloc_size) |
||||||
|
|
||||||
|
#define DECLARE_RZALLOC_CXX_OPERATORS(type) \ |
||||||
|
DECLARE_ALLOC_CXX_OPERATORS_TEMPLATE(type, rzalloc_size) |
||||||
|
|
||||||
|
#define DECLARE_LINEAR_ALLOC_CXX_OPERATORS(type) \ |
||||||
|
DECLARE_ALLOC_CXX_OPERATORS_TEMPLATE(type, linear_alloc_child) |
||||||
|
|
||||||
|
#define DECLARE_LINEAR_ZALLOC_CXX_OPERATORS(type) \ |
||||||
|
DECLARE_ALLOC_CXX_OPERATORS_TEMPLATE(type, linear_zalloc_child) |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do a fast allocation from the linear buffer, also known as the child node |
||||||
|
* from the allocator's point of view. It can't be freed directly. You have |
||||||
|
* to free the parent or the ralloc parent. |
||||||
|
* |
||||||
|
* \param parent parent node of the linear allocator |
||||||
|
* \param size size to allocate (max 32 bits) |
||||||
|
*/ |
||||||
|
void *linear_alloc_child(void *parent, unsigned size); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate a parent node that will hold linear buffers. The returned |
||||||
|
* allocation is actually the first child node, but it's also the handle |
||||||
|
* of the parent node. Use it for all child node allocations. |
||||||
|
* |
||||||
|
* \param ralloc_ctx ralloc context, must not be NULL |
||||||
|
* \param size size to allocate (max 32 bits) |
||||||
|
*/ |
||||||
|
void *linear_alloc_parent(void *ralloc_ctx, unsigned size); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as linear_alloc_child, but also clears memory. |
||||||
|
*/ |
||||||
|
void *linear_zalloc_child(void *parent, unsigned size); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as linear_alloc_parent, but also clears memory. |
||||||
|
*/ |
||||||
|
void *linear_zalloc_parent(void *ralloc_ctx, unsigned size); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Free the linear parent node. This will free all child nodes too. |
||||||
|
* Freeing the ralloc parent will also free this. |
||||||
|
*/ |
||||||
|
void linear_free_parent(void *ptr); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as ralloc_steal, but steals the linear parent node. |
||||||
|
*/ |
||||||
|
void ralloc_steal_linear_parent(void *new_ralloc_ctx, void *ptr); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the ralloc parent of the linear parent node. |
||||||
|
*/ |
||||||
|
void *ralloc_parent_of_linear_parent(void *ptr); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as realloc except that the linear allocator doesn't free child nodes, |
||||||
|
* so it's reduced to memory duplication. It's used in places where |
||||||
|
* reallocation is required. Don't use it often. It's much slower than |
||||||
|
* realloc. |
||||||
|
*/ |
||||||
|
void *linear_realloc(void *parent, void *old, unsigned new_size); |
||||||
|
|
||||||
|
/* The functions below have the same semantics as their ralloc counterparts,
|
||||||
|
* except that they always allocate a linear child node. |
||||||
|
*/ |
||||||
|
char *linear_strdup(void *parent, const char *str); |
||||||
|
char *linear_asprintf(void *parent, const char *fmt, ...); |
||||||
|
char *linear_vasprintf(void *parent, const char *fmt, va_list args); |
||||||
|
bool linear_asprintf_append(void *parent, char **str, const char *fmt, ...); |
||||||
|
bool linear_vasprintf_append(void *parent, char **str, const char *fmt, |
||||||
|
va_list args); |
||||||
|
bool linear_asprintf_rewrite_tail(void *parent, char **str, size_t *start, |
||||||
|
const char *fmt, ...); |
||||||
|
bool linear_vasprintf_rewrite_tail(void *parent, char **str, size_t *start, |
||||||
|
const char *fmt, va_list args); |
||||||
|
bool linear_strcat(void *parent, char **dest, const char *str); |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} /* end of extern "C" */ |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue