#ifndef VERSTABLE_H
#define VERSTABLE_H
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#define VT_CAT_( a, b ) a##b
#define VT_CAT( a, b ) VT_CAT_( a, b )
#ifdef __GNUC__
#define VT_LIKELY( expression ) __builtin_expect( (bool)( expression ), true )
#define VT_UNLIKELY( expression ) __builtin_expect( (bool)( expression ), false )
#else
#define VT_LIKELY( expression ) ( expression )
#define VT_UNLIKELY( expression ) ( expression )
#endif
#define VT_EMPTY 0x0000
#define VT_HASH_FRAG_MASK 0xF000
#define VT_IN_HOME_BUCKET_MASK 0x0800
#define VT_DISPLACEMENT_MASK 0x07FF
static inline uint16_t vt_hashfrag( uint64_t hash )
{
return ( hash >> 48 ) & VT_HASH_FRAG_MASK;
}
static inline size_t vt_quadratic( uint16_t displacement )
{
return ( (size_t)displacement * displacement + displacement ) / 2;
}
#define VT_MIN_NONZERO_BUCKET_COUNT 8
#if defined( __GNUC__ ) && ULLONG_MAX == 0xFFFFFFFFFFFFFFFF
static inline int vt_first_nonzero_uint16( uint64_t val )
{
const uint16_t endian_checker = 0x0001;
if( *(const char *)&endian_checker ) return __builtin_ctzll( val ) / 16;
return __builtin_clzll( val ) / 16;
}
#elif defined( _MSC_VER ) && ( defined( _M_X64 ) || defined( _M_ARM64 ) )
#include <intrin.h>
#pragma intrinsic(_BitScanForward64)
#pragma intrinsic(_BitScanReverse64)
static inline int vt_first_nonzero_uint16( uint64_t val )
{
unsigned long result;
const uint16_t endian_checker = 0x0001;
if( *(const char *)&endian_checker )
_BitScanForward64( &result, val );
else
{
_BitScanReverse64( &result, val );
result = 63 - result;
}
return result / 16;
}
#else
static inline int vt_first_nonzero_uint16( uint64_t val )
{
int result = 0;
uint32_t half;
memcpy( &half, &val, sizeof( uint32_t ) );
if( !half )
result += 2;
uint16_t quarter;
memcpy( &quarter, (char *)&val + result * sizeof( uint16_t ), sizeof( uint16_t ) );
if( !quarter )
result += 1;
return result;
}
#endif
static const uint16_t vt_empty_placeholder_metadatum = VT_EMPTY;
static inline uint64_t vt_hash_integer( uint64_t key )
{
key ^= key >> 23;
key *= 0x2127599bf4325c37ull;
key ^= key >> 47;
return key;
}
static inline void vt_wymum( uint64_t *a, uint64_t *b )
{
#if defined( __SIZEOF_INT128__ )
__uint128_t r = *a;
r *= *b;
*a = (uint64_t)r;
*b = (uint64_t)( r >> 64 );
#elif defined( _MSC_VER ) && defined( _M_X64 )
*a = _umul128( *a, *b, b );
#else
uint64_t ha = *a >> 32;
uint64_t hb = *b >> 32;
uint64_t la = (uint32_t)*a;
uint64_t lb = (uint32_t)*b;
uint64_t rh = ha * hb;
uint64_t rm0 = ha * lb;
uint64_t rm1 = hb * la;
uint64_t rl = la * lb;
uint64_t t = rl + ( rm0 << 32 );
uint64_t c = t < rl;
uint64_t lo = t + ( rm1 << 32 );
c += lo < t;
uint64_t hi = rh + ( rm0 >> 32 ) + ( rm1 >> 32 ) + c;
*a = lo;
*b = hi;
#endif
}
static inline uint64_t vt_wymix( uint64_t a, uint64_t b )
{
vt_wymum( &a, &b );
return a ^ b;
}
static inline uint64_t vt_wyr8( const unsigned char *p )
{
uint64_t v;
memcpy( &v, p, 8 );
return v;
}
static inline uint64_t vt_wyr4( const unsigned char *p )
{
uint32_t v;
memcpy( &v, p, 4 );
return v;
}
static inline uint64_t vt_wyr3( const unsigned char *p, size_t k )
{
return ( ( (uint64_t)p[ 0 ] ) << 16 ) | ( ( (uint64_t)p[ k >> 1 ] ) << 8 ) | p[ k - 1 ];
}
static inline size_t vt_wyhash( const void *key, size_t len )
{
const unsigned char *p = (const unsigned char *)key;
uint64_t seed = 0xca813bf4c7abf0a9ull;
uint64_t a;
uint64_t b;
if( VT_LIKELY( len <= 16 ) )
{
if( VT_LIKELY( len >= 4 ) )
{
a = ( vt_wyr4( p ) << 32 ) | vt_wyr4( p + ( ( len >> 3 ) << 2 ) );
b = ( vt_wyr4( p + len - 4 ) << 32 ) | vt_wyr4( p + len - 4 - ( ( len >> 3 ) << 2 ) );
}
else if( VT_LIKELY( len > 0 ) )
{
a = vt_wyr3( p, len );
b = 0;
}
else
{
a = 0;
b = 0;
}
}
else
{
size_t i = len;
if( VT_UNLIKELY( i >= 48 ) )
{
uint64_t see1 = seed;
uint64_t see2 = seed;
do{
seed = vt_wymix( vt_wyr8( p ) ^ 0x8bb84b93962eacc9ull, vt_wyr8( p + 8 ) ^ seed );
see1 = vt_wymix( vt_wyr8( p + 16 ) ^ 0x4b33a62ed433d4a3ull, vt_wyr8( p + 24 ) ^ see1 );
see2 = vt_wymix( vt_wyr8( p + 32 ) ^ 0x4d5a2da51de1aa47ull, vt_wyr8( p + 40 ) ^ see2 );
p += 48;
i -= 48;
}
while( VT_LIKELY( i >= 48 ) );
seed ^= see1 ^ see2;
}
while( VT_UNLIKELY( i > 16 ) )
{
seed = vt_wymix( vt_wyr8( p ) ^ 0x8bb84b93962eacc9ull, vt_wyr8( p + 8 ) ^ seed );
i -= 16;
p += 16;
}
a = vt_wyr8( p + i - 16 );
b = vt_wyr8( p + i - 8 );
}
a ^= 0x8bb84b93962eacc9ull;
b ^= seed;
vt_wymum( &a, &b );
return (size_t)vt_wymix( a ^ 0x2d358dccaa6c78a5ull ^ len, b ^ 0x8bb84b93962eacc9ull );
}
static inline uint64_t vt_hash_string( const char *key )
{
return vt_wyhash( key, strlen( key ) );
}
static inline bool vt_cmpr_integer( uint64_t key_1, uint64_t key_2 )
{
return key_1 == key_2;
}
static inline bool vt_cmpr_string( const char *key_1, const char *key_2 )
{
return strcmp( key_1, key_2 ) == 0;
}
static inline void *vt_malloc( size_t size )
{
return malloc( size );
}
static inline void vt_free( void *ptr, size_t size )
{
(void)size;
free( ptr );
}
static inline void *vt_malloc_with_ctx( size_t size, void *ctx )
{
(void)ctx;
return malloc( size );
}
static inline void vt_free_with_ctx( void *ptr, size_t size, void *ctx )
{
(void)size;
(void)ctx;
free( ptr );
}
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined( VT_NO_C11_GENERIC_API )
#define VT_TEMPLATE_COUNT_D1 0
#define VT_TEMPLATE_COUNT_D2 0
#define VT_TEMPLATE_COUNT_D3 0
#define VT_CAT_4_( a, b, c, d ) a##b##c##d
#define VT_CAT_4( a, b, c, d ) VT_CAT_4_( a, b, c, d )
#define VT_TEMPLATE_COUNT VT_CAT_4( 0, VT_TEMPLATE_COUNT_D3, VT_TEMPLATE_COUNT_D2, VT_TEMPLATE_COUNT_D1 )
#define VT_GENERIC_SLOT( ty, fn, n ) , VT_CAT( ty, n ): VT_CAT( fn, n )
#define VT_R1_0( ty, fn, d3, d2 )
#define VT_R1_1( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 0 ) )
#define VT_R1_2( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 1 ) ) VT_R1_1( ty, fn, d3, d2 )
#define VT_R1_3( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 2 ) ) VT_R1_2( ty, fn, d3, d2 )
#define VT_R1_4( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 3 ) ) VT_R1_3( ty, fn, d3, d2 )
#define VT_R1_5( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 4 ) ) VT_R1_4( ty, fn, d3, d2 )
#define VT_R1_6( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 5 ) ) VT_R1_5( ty, fn, d3, d2 )
#define VT_R1_7( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 6 ) ) VT_R1_6( ty, fn, d3, d2 )
#define VT_R1_8( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 7 ) ) VT_R1_7( ty, fn, d3, d2 )
#define VT_R2_0( ty, fn, d3 )
#define VT_R2_1( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 0 )
#define VT_R2_2( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 1 ) VT_R2_1( ty, fn, d3 )
#define VT_R2_3( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 2 ) VT_R2_2( ty, fn, d3 )
#define VT_R2_4( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 3 ) VT_R2_3( ty, fn, d3 )
#define VT_R2_5( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 4 ) VT_R2_4( ty, fn, d3 )
#define VT_R2_6( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 5 ) VT_R2_5( ty, fn, d3 )
#define VT_R2_7( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 6 ) VT_R2_6( ty, fn, d3 )
#define VT_R2_8( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 7 ) VT_R2_7( ty, fn, d3 )
#define VT_R3_0( ty, fn )
#define VT_R3_1( ty, fn ) VT_R2_8( ty, fn, 0 )
#define VT_R3_2( ty, fn ) VT_R2_8( ty, fn, 1 ) VT_R3_1( ty, fn )
#define VT_R3_3( ty, fn ) VT_R2_8( ty, fn, 2 ) VT_R3_2( ty, fn )
#define VT_R3_4( ty, fn ) VT_R2_8( ty, fn, 3 ) VT_R3_3( ty, fn )
#define VT_R3_5( ty, fn ) VT_R2_8( ty, fn, 4 ) VT_R3_4( ty, fn )
#define VT_R3_6( ty, fn ) VT_R2_8( ty, fn, 5 ) VT_R3_5( ty, fn )
#define VT_R3_7( ty, fn ) VT_R2_8( ty, fn, 6 ) VT_R3_6( ty, fn )
#define VT_GENERIC_SLOTS( ty, fn ) \
VT_CAT( VT_R1_, VT_TEMPLATE_COUNT_D1 )( ty, fn, VT_TEMPLATE_COUNT_D3, VT_TEMPLATE_COUNT_D2 ) \
VT_CAT( VT_R2_, VT_TEMPLATE_COUNT_D2 )( ty, fn, VT_TEMPLATE_COUNT_D3 ) \
VT_CAT( VT_R3_, VT_TEMPLATE_COUNT_D3 )( ty, fn ) \
#define VT_ARG_3( _1, _2, _3, ... ) _3
#define vt_init( ... ) VT_ARG_3( __VA_ARGS__, vt_init_with_ctx, vt_init_without_ctx, )( __VA_ARGS__ )
#define vt_init_without_ctx( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_init_ ) )( table )
#define vt_init_with_ctx( table, ... ) _Generic( *( table ) \
VT_GENERIC_SLOTS( vt_table_, vt_init_ ) \
)( table, __VA_ARGS__ ) \
#define vt_init_clone( table, ... ) _Generic( *( table ) \
VT_GENERIC_SLOTS( vt_table_, vt_init_clone_ ) \
)( table, __VA_ARGS__ ) \
#define vt_size( table )_Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_size_ ) )( table )
#define vt_bucket_count( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_bucket_count_ ) )( table )
#define vt_is_end( itr ) _Generic( itr VT_GENERIC_SLOTS( vt_table_itr_, vt_is_end_ ) )( itr )
#define vt_insert( table, ... ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_insert_ ) )( table, __VA_ARGS__ )
#define vt_get_or_insert( table, ... ) _Generic( *( table ) \
VT_GENERIC_SLOTS( vt_table_, vt_get_or_insert_ ) \
)( table, __VA_ARGS__ ) \
#define vt_get( table, ... ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_get_ ) )( table, __VA_ARGS__ )
#define vt_erase( table, ... ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_erase_ ) )( table, __VA_ARGS__ )
#define vt_next( itr ) _Generic( itr VT_GENERIC_SLOTS( vt_table_itr_, vt_next_ ) )( itr )
#define vt_erase_itr( table, ... ) _Generic( *( table ) \
VT_GENERIC_SLOTS( vt_table_, vt_erase_itr_ ) \
)( table, __VA_ARGS__ ) \
#define vt_reserve( table, ... ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_reserve_ ) )( table, __VA_ARGS__ )
#define vt_shrink( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_shrink_ ) )( table )
#define vt_first( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_first_ ) )( table )
#define vt_clear( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_clear_ ) )( table )
#define vt_cleanup( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_cleanup_ ) )( table )
#endif
#endif
#ifndef IMPLEMENTATION_MODE
typedef struct
{
KEY_TY key;
#ifdef VAL_TY
VAL_TY val;
#endif
} VT_CAT( NAME, _bucket );
typedef struct
{
VT_CAT( NAME, _bucket ) *data;
uint16_t *metadatum;
uint16_t *metadata_end; size_t home_bucket; } VT_CAT( NAME, _itr );
typedef struct
{
size_t key_count;
size_t buckets_mask; VT_CAT( NAME, _bucket ) *buckets;
uint16_t *metadata; #ifdef CTX_TY
CTX_TY ctx;
#endif
} NAME;
#endif
#if defined( HEADER_MODE ) || defined( IMPLEMENTATION_MODE )
#define VT_API_FN_QUALIFIERS
#else
#define VT_API_FN_QUALIFIERS static inline
#endif
#ifndef IMPLEMENTATION_MODE
VT_API_FN_QUALIFIERS void VT_CAT( NAME, _init )(
NAME *
#ifdef CTX_TY
, CTX_TY
#endif
);
VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _init_clone )(
NAME *,
const NAME *
#ifdef CTX_TY
, CTX_TY
#endif
);
VT_API_FN_QUALIFIERS size_t VT_CAT( NAME, _size )( const NAME * );
VT_API_FN_QUALIFIERS size_t VT_CAT( NAME, _bucket_count )( const NAME * );
VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _is_end )( VT_CAT( NAME, _itr ) );
VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _insert )(
NAME *,
KEY_TY
#ifdef VAL_TY
, VAL_TY
#endif
);
VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _get_or_insert )(
NAME *,
KEY_TY
#ifdef VAL_TY
, VAL_TY
#endif
);
VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _get )(
const NAME *table,
KEY_TY key
);
VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _erase )( NAME *, KEY_TY );
VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _next )( VT_CAT( NAME, _itr ) );
VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _reserve )( NAME *, size_t );
VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _shrink )( NAME * );
VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _first )( const NAME * );
VT_API_FN_QUALIFIERS void VT_CAT( NAME, _clear )( NAME * );
VT_API_FN_QUALIFIERS void VT_CAT( NAME, _cleanup )( NAME * );
VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _erase_itr_raw ) ( NAME *, VT_CAT( NAME, _itr ) );
#ifdef __GNUC__
static inline __attribute__((always_inline))
#elif defined( _MSC_VER )
static __forceinline
#else
static inline
#endif
VT_CAT( NAME, _itr ) VT_CAT( NAME, _erase_itr )( NAME *table, VT_CAT( NAME, _itr ) itr )
{
if( VT_CAT( NAME, _erase_itr_raw )( table, itr ) )
return VT_CAT( NAME, _next )( itr );
return itr;
}
#endif
#ifndef HEADER_MODE
#ifndef MAX_LOAD
#define MAX_LOAD 0.9
#endif
#ifndef MALLOC_FN
#ifdef CTX_TY
#define MALLOC_FN vt_malloc_with_ctx
#else
#define MALLOC_FN vt_malloc
#endif
#endif
#ifndef FREE_FN
#ifdef CTX_TY
#define FREE_FN vt_free_with_ctx
#else
#define FREE_FN vt_free
#endif
#endif
#ifndef HASH_FN
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
#ifdef _MSC_VER
#define HASH_FN \
_Pragma( "warning( push )" ) \
_Pragma( "warning( disable: 4189 )" ) \
_Generic( ( KEY_TY ){ 0 }, char *: vt_hash_string, const char*: vt_hash_string, default: vt_hash_integer ) \
_Pragma( "warning( pop )" )
#else
#define HASH_FN _Generic( ( KEY_TY ){ 0 }, \
char *: vt_hash_string, \
const char*: vt_hash_string, \
default: vt_hash_integer \
)
#endif
#else
#error Hash function inference is only available in C11 and later. In C99, you need to define HASH_FN manually to \
vt_hash_integer, vt_hash_string, or your own custom function with the signature uint64_t ( KEY_TY ).
#endif
#endif
#ifndef CMPR_FN
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
#ifdef _MSC_VER
#define CMPR_FN \
_Pragma( "warning( push )" ) \
_Pragma( "warning( disable: 4189 )" ) \
_Generic( ( KEY_TY ){ 0 }, char *: vt_cmpr_string, const char*: vt_cmpr_string, default: vt_cmpr_integer ) \
_Pragma( "warning( pop )" )
#else
#define CMPR_FN _Generic( ( KEY_TY ){ 0 }, \
char *: vt_cmpr_string, \
const char*: vt_cmpr_string, \
default: vt_cmpr_integer \
)
#endif
#else
#error Comparison function inference is only available in C11 and later. In C99, you need to define CMPR_FN manually \
to vt_cmpr_integer, vt_cmpr_string, or your own custom function with the signature bool ( KEY_TY, KEY_TY ).
#endif
#endif
VT_API_FN_QUALIFIERS void VT_CAT( NAME, _init )(
NAME *table
#ifdef CTX_TY
, CTX_TY ctx
#endif
)
{
table->key_count = 0;
table->buckets_mask = 0x0000000000000000ull;
table->buckets = NULL;
table->metadata = (uint16_t *)&vt_empty_placeholder_metadatum;
#ifdef CTX_TY
table->ctx = ctx;
#endif
}
static inline size_t VT_CAT( NAME, _metadata_offset )( NAME *table )
{
return ( ( ( table->buckets_mask + 1 ) * sizeof( VT_CAT( NAME, _bucket ) ) + sizeof( uint16_t ) - 1 ) /
sizeof( uint16_t ) ) * sizeof( uint16_t );
}
static inline size_t VT_CAT( NAME, _total_alloc_size )( NAME *table )
{
return VT_CAT( NAME, _metadata_offset )( table ) + ( table->buckets_mask + 1 + 4 ) * sizeof( uint16_t );
}
VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _init_clone )(
NAME *table,
const NAME *source
#ifdef CTX_TY
, CTX_TY ctx
#endif
)
{
table->key_count = source->key_count;
table->buckets_mask = source->buckets_mask;
#ifdef CTX_TY
table->ctx = ctx;
#endif
if( !source->buckets_mask )
{
table->metadata = (uint16_t *)&vt_empty_placeholder_metadatum;
table->buckets = NULL;
return true;
}
void *allocation = MALLOC_FN(
VT_CAT( NAME, _total_alloc_size )( table )
#ifdef CTX_TY
, &table->ctx
#endif
);
if( VT_UNLIKELY( !allocation ) )
return false;
table->buckets = (VT_CAT( NAME, _bucket ) *)allocation;
table->metadata = (uint16_t *)( (unsigned char *)allocation + VT_CAT( NAME, _metadata_offset )( table ) );
memcpy( allocation, source->buckets, VT_CAT( NAME, _total_alloc_size )( table ) );
return true;
}
VT_API_FN_QUALIFIERS size_t VT_CAT( NAME, _size )( const NAME *table )
{
return table->key_count;
}
VT_API_FN_QUALIFIERS size_t VT_CAT( NAME, _bucket_count )( const NAME *table )
{
return table->buckets_mask + (bool)table->buckets_mask;
}
VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _is_end )( VT_CAT( NAME, _itr ) itr )
{
return itr.metadatum == itr.metadata_end;
}
static inline bool VT_CAT( NAME, _find_first_empty )(
NAME *table,
size_t home_bucket,
size_t *empty,
uint16_t *displacement
)
{
*displacement = 1;
size_t linear_dispacement = 1;
while( true )
{
*empty = ( home_bucket + linear_dispacement ) & table->buckets_mask;
if( table->metadata[ *empty ] == VT_EMPTY )
return true;
if( VT_UNLIKELY( ++*displacement == VT_DISPLACEMENT_MASK ) )
return false;
linear_dispacement += *displacement;
}
}
static inline size_t VT_CAT( NAME, _find_insert_location_in_chain )(
NAME *table,
size_t home_bucket,
uint16_t displacement_to_empty
)
{
size_t candidate = home_bucket;
while( true )
{
uint16_t displacement = table->metadata[ candidate ] & VT_DISPLACEMENT_MASK;
if( displacement > displacement_to_empty )
return candidate;
candidate = ( home_bucket + vt_quadratic( displacement ) ) & table->buckets_mask;
}
}
static inline bool VT_CAT( NAME, _evict )( NAME *table, size_t bucket )
{
size_t home_bucket = HASH_FN( table->buckets[ bucket ].key ) & table->buckets_mask;
size_t prev = home_bucket;
while( true )
{
size_t next = ( home_bucket + vt_quadratic( table->metadata[ prev ] & VT_DISPLACEMENT_MASK ) ) &
table->buckets_mask;
if( next == bucket )
break;
prev = next;
}
table->metadata[ prev ] = ( table->metadata[ prev ] & ~VT_DISPLACEMENT_MASK ) | ( table->metadata[ bucket ] &
VT_DISPLACEMENT_MASK );
size_t empty;
uint16_t displacement;
if( VT_UNLIKELY( !VT_CAT( NAME, _find_first_empty )( table, home_bucket, &empty, &displacement ) ) )
return false;
prev = VT_CAT( NAME, _find_insert_location_in_chain )( table, home_bucket, displacement );
table->buckets[ empty ] = table->buckets[ bucket ];
table->metadata[ empty ] = ( table->metadata[ bucket ] & VT_HASH_FRAG_MASK ) | ( table->metadata[ prev ] &
VT_DISPLACEMENT_MASK );
table->metadata[ prev ] = ( table->metadata[ prev ] & ~VT_DISPLACEMENT_MASK ) | displacement;
return true;
}
static inline VT_CAT( NAME, _itr ) VT_CAT( NAME, _end_itr )( void )
{
VT_CAT( NAME, _itr ) itr = { NULL, NULL, NULL, 0 };
return itr;
}
static inline VT_CAT( NAME, _itr ) VT_CAT( NAME, _insert_raw )(
NAME *table,
KEY_TY key,
#ifdef VAL_TY
VAL_TY *val,
#endif
bool unique,
bool replace
)
{
uint64_t hash = HASH_FN( key );
uint16_t hashfrag = vt_hashfrag( hash );
size_t home_bucket = hash & table->buckets_mask;
if( !( table->metadata[ home_bucket ] & VT_IN_HOME_BUCKET_MASK ) )
{
if(
VT_UNLIKELY( table->key_count + 1 > VT_CAT( NAME, _bucket_count )( table ) * MAX_LOAD ) ||
( table->metadata[ home_bucket ] != VT_EMPTY && VT_UNLIKELY( !VT_CAT( NAME, _evict )( table, home_bucket ) ) )
)
return VT_CAT( NAME, _end_itr )();
table->buckets[ home_bucket ].key = key;
#ifdef VAL_TY
table->buckets[ home_bucket ].val = *val;
#endif
table->metadata[ home_bucket ] = hashfrag | VT_IN_HOME_BUCKET_MASK | VT_DISPLACEMENT_MASK;
++table->key_count;
VT_CAT( NAME, _itr ) itr = {
table->buckets + home_bucket,
table->metadata + home_bucket,
table->metadata + table->buckets_mask + 1, home_bucket
};
return itr;
}
if( !unique )
{
size_t bucket = home_bucket;
while( true )
{
if(
( table->metadata[ bucket ] & VT_HASH_FRAG_MASK ) == hashfrag &&
VT_LIKELY( CMPR_FN( table->buckets[ bucket ].key, key ) )
)
{
if( replace )
{
#ifdef KEY_DTOR_FN
KEY_DTOR_FN( table->buckets[ bucket ].key );
#endif
table->buckets[ bucket ].key = key;
#ifdef VAL_TY
#ifdef VAL_DTOR_FN
VAL_DTOR_FN( table->buckets[ bucket ].val );
#endif
table->buckets[ bucket ].val = *val;
#endif
}
VT_CAT( NAME, _itr ) itr = {
table->buckets + bucket,
table->metadata + bucket,
table->metadata + table->buckets_mask + 1,
home_bucket
};
return itr;
}
uint16_t displacement = table->metadata[ bucket ] & VT_DISPLACEMENT_MASK;
if( displacement == VT_DISPLACEMENT_MASK )
break;
bucket = ( home_bucket + vt_quadratic( displacement ) ) & table->buckets_mask;
}
}
size_t empty;
uint16_t displacement;
if(
VT_UNLIKELY(
table->key_count + 1 > VT_CAT( NAME, _bucket_count )( table ) * MAX_LOAD ||
!VT_CAT( NAME, _find_first_empty )( table, home_bucket, &empty, &displacement )
)
)
return VT_CAT( NAME, _end_itr )();
size_t prev = VT_CAT( NAME, _find_insert_location_in_chain )( table, home_bucket, displacement );
table->buckets[ empty ].key = key;
#ifdef VAL_TY
table->buckets[ empty ].val = *val;
#endif
table->metadata[ empty ] = hashfrag | ( table->metadata[ prev ] & VT_DISPLACEMENT_MASK );
table->metadata[ prev ] = ( table->metadata[ prev ] & ~VT_DISPLACEMENT_MASK ) | displacement;
++table->key_count;
VT_CAT( NAME, _itr ) itr = {
table->buckets + empty,
table->metadata + empty,
table->metadata + table->buckets_mask + 1,
home_bucket
};
return itr;
}
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wattributes"
__attribute__((noinline)) static inline
#elif defined( _MSC_VER )
__declspec(noinline) static inline
#else
static inline
#endif
bool VT_CAT( NAME, _rehash )( NAME *table, size_t bucket_count )
{
while( true )
{
NAME new_table = {
0,
bucket_count - 1,
NULL,
NULL
#ifdef CTX_TY
, table->ctx
#endif
};
void *allocation = MALLOC_FN(
VT_CAT( NAME, _total_alloc_size )( &new_table )
#ifdef CTX_TY
, &new_table.ctx
#endif
);
if( VT_UNLIKELY( !allocation ) )
return false;
new_table.buckets = (VT_CAT( NAME, _bucket ) *)allocation;
new_table.metadata = (uint16_t *)( (unsigned char *)allocation + VT_CAT( NAME, _metadata_offset )( &new_table ) );
memset( new_table.metadata, 0x00, ( bucket_count + 4 ) * sizeof( uint16_t ) );
new_table.metadata[ bucket_count ] = 0x01;
for( size_t bucket = 0; bucket < VT_CAT( NAME, _bucket_count )( table ); ++bucket )
if( table->metadata[ bucket ] != VT_EMPTY )
{
VT_CAT( NAME, _itr ) itr = VT_CAT( NAME, _insert_raw )(
&new_table,
table->buckets[ bucket ].key,
#ifdef VAL_TY
&table->buckets[ bucket ].val,
#endif
true,
false
);
if( VT_UNLIKELY( VT_CAT( NAME, _is_end )( itr ) ) )
break;
}
if( VT_UNLIKELY( new_table.key_count < table->key_count ) )
{
FREE_FN(
new_table.buckets,
VT_CAT( NAME, _total_alloc_size )( &new_table )
#ifdef CTX_TY
, &new_table.ctx
#endif
);
bucket_count *= 2;
continue;
}
if( table->buckets_mask )
FREE_FN(
table->buckets,
VT_CAT( NAME, _total_alloc_size )( table )
#ifdef CTX_TY
, &table->ctx
#endif
);
*table = new_table;
return true;
}
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _insert )(
NAME *table,
KEY_TY key
#ifdef VAL_TY
, VAL_TY val
#endif
)
{
while( true )
{
VT_CAT( NAME, _itr ) itr = VT_CAT( NAME, _insert_raw )(
table,
key,
#ifdef VAL_TY
&val,
#endif
false,
true
);
if(
VT_LIKELY( !VT_CAT( NAME, _is_end )( itr ) ) ||
VT_UNLIKELY(
!VT_CAT( NAME, _rehash )(
table, table->buckets_mask ? VT_CAT( NAME, _bucket_count )( table ) * 2 : VT_MIN_NONZERO_BUCKET_COUNT
)
)
)
return itr;
}
}
VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _get_or_insert )(
NAME *table,
KEY_TY key
#ifdef VAL_TY
, VAL_TY val
#endif
)
{
while( true )
{
VT_CAT( NAME, _itr ) itr = VT_CAT( NAME, _insert_raw )(
table,
key,
#ifdef VAL_TY
&val,
#endif
false,
false
);
if(
VT_LIKELY( !VT_CAT( NAME, _is_end )( itr ) ) ||
VT_UNLIKELY(
!VT_CAT( NAME, _rehash )(
table, table->buckets_mask ? VT_CAT( NAME, _bucket_count )( table ) * 2 : VT_MIN_NONZERO_BUCKET_COUNT
)
)
)
return itr;
}
}
VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _get )( const NAME *table, KEY_TY key )
{
uint64_t hash = HASH_FN( key );
size_t home_bucket = hash & table->buckets_mask;
if( !( table->metadata[ home_bucket ] & VT_IN_HOME_BUCKET_MASK ) )
return VT_CAT( NAME, _end_itr )();
uint16_t hashfrag = vt_hashfrag( hash );
size_t bucket = home_bucket;
while( true )
{
if(
( table->metadata[ bucket ] & VT_HASH_FRAG_MASK ) == hashfrag &&
VT_LIKELY( CMPR_FN( table->buckets[ bucket ].key, key ) )
)
{
VT_CAT( NAME, _itr ) itr = {
table->buckets + bucket,
table->metadata + bucket,
table->metadata + table->buckets_mask + 1,
home_bucket
};
return itr;
}
uint16_t displacement = table->metadata[ bucket ] & VT_DISPLACEMENT_MASK;
if( displacement == VT_DISPLACEMENT_MASK )
return VT_CAT( NAME, _end_itr )();
bucket = ( home_bucket + vt_quadratic( displacement ) ) & table->buckets_mask;
}
}
VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _erase_itr_raw )( NAME *table, VT_CAT( NAME, _itr ) itr )
{
--table->key_count;
size_t itr_bucket = itr.metadatum - table->metadata;
#ifdef VAL_DTOR_FN
VAL_DTOR_FN( table->buckets[ itr_bucket ].val );
#endif
if(
table->metadata[ itr_bucket ] & VT_IN_HOME_BUCKET_MASK &&
( table->metadata[ itr_bucket ] & VT_DISPLACEMENT_MASK ) == VT_DISPLACEMENT_MASK
)
{
#ifdef KEY_DTOR_FN
KEY_DTOR_FN( table->buckets[ itr_bucket ].key );
#endif
table->metadata[ itr_bucket ] = VT_EMPTY;
return true;
}
if( itr.home_bucket == SIZE_MAX )
{
if( table->metadata[ itr_bucket ] & VT_IN_HOME_BUCKET_MASK )
itr.home_bucket = itr_bucket;
else
itr.home_bucket = HASH_FN( table->buckets[ itr_bucket ].key ) & table->buckets_mask;
}
#ifdef KEY_DTOR_FN
KEY_DTOR_FN( table->buckets[ itr_bucket ].key );
#endif
if( ( table->metadata[ itr_bucket ] & VT_DISPLACEMENT_MASK ) == VT_DISPLACEMENT_MASK )
{
size_t bucket = itr.home_bucket;
while( true )
{
uint16_t displacement = table->metadata[ bucket ] & VT_DISPLACEMENT_MASK;
size_t next = ( itr.home_bucket + vt_quadratic( displacement ) ) & table->buckets_mask;
if( next == itr_bucket )
{
table->metadata[ bucket ] |= VT_DISPLACEMENT_MASK;
table->metadata[ itr_bucket ] = VT_EMPTY;
return true;
}
bucket = next;
}
}
size_t bucket = itr_bucket;
while( true )
{
size_t prev = bucket;
bucket = ( itr.home_bucket + vt_quadratic( table->metadata[ bucket ] & VT_DISPLACEMENT_MASK ) ) &
table->buckets_mask;
if( ( table->metadata[ bucket ] & VT_DISPLACEMENT_MASK ) == VT_DISPLACEMENT_MASK )
{
table->buckets[ itr_bucket ] = table->buckets[ bucket ];
table->metadata[ itr_bucket ] = ( table->metadata[ itr_bucket ] & ~VT_HASH_FRAG_MASK ) | (
table->metadata[ bucket ] & VT_HASH_FRAG_MASK );
table->metadata[ prev ] |= VT_DISPLACEMENT_MASK;
table->metadata[ bucket ] = VT_EMPTY;
if( bucket > itr_bucket )
return false;
return true;
}
}
}
VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _erase )( NAME *table, KEY_TY key )
{
VT_CAT( NAME, _itr ) itr = VT_CAT( NAME, _get)( table, key );
if( VT_CAT( NAME, _is_end )( itr ) )
return false;
VT_CAT( NAME, _erase_itr_raw )( table, itr );
return true;
}
static inline void VT_CAT( NAME, _fast_forward )( VT_CAT( NAME, _itr ) *itr )
{
while( true )
{
uint64_t metadata;
memcpy( &metadata, itr->metadatum, sizeof( uint64_t ) );
if( metadata )
{
int offset = vt_first_nonzero_uint16( metadata );
itr->data += offset;
itr->metadatum += offset;
itr->home_bucket = SIZE_MAX;
return;
}
itr->data += 4;
itr->metadatum += 4;
}
}
VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _next )( VT_CAT( NAME, _itr ) itr )
{
++itr.data;
++itr.metadatum;
VT_CAT( NAME, _fast_forward )( &itr );
return itr;
}
static inline size_t VT_CAT( NAME, _min_bucket_count_for_size )( size_t size )
{
if( size == 0 )
return 0;
size_t bucket_count = VT_MIN_NONZERO_BUCKET_COUNT;
while( size > bucket_count * MAX_LOAD )
bucket_count *= 2;
return bucket_count;
}
VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _reserve )( NAME *table, size_t size )
{
size_t bucket_count = VT_CAT( NAME, _min_bucket_count_for_size )( size );
if( bucket_count <= VT_CAT( NAME, _bucket_count )( table ) )
return true;
return VT_CAT( NAME, _rehash )( table, bucket_count );
}
VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _shrink )( NAME *table )
{
size_t bucket_count = VT_CAT( NAME, _min_bucket_count_for_size )( table->key_count );
if( bucket_count == VT_CAT( NAME, _bucket_count )( table ) ) return true;
if( bucket_count == 0 )
{
FREE_FN(
table->buckets,
VT_CAT( NAME, _total_alloc_size )( table )
#ifdef CTX_TY
, &table->ctx
#endif
);
table->buckets_mask = 0x0000000000000000ull;
table->metadata = (uint16_t *)&vt_empty_placeholder_metadatum;
return true;
}
return VT_CAT( NAME, _rehash )( table, bucket_count );
}
VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _first )( const NAME *table )
{
if( !table->key_count )
return VT_CAT( NAME, _end_itr )();
VT_CAT( NAME, _itr ) itr = { table->buckets, table->metadata, table->metadata + table->buckets_mask + 1, SIZE_MAX };
VT_CAT( NAME, _fast_forward )( &itr );
return itr;
}
VT_API_FN_QUALIFIERS void VT_CAT( NAME, _clear )( NAME *table )
{
if( !table->key_count )
return;
for( size_t i = 0; i < VT_CAT( NAME, _bucket_count )( table ); ++i )
{
if( table->metadata[ i ] != VT_EMPTY )
{
#ifdef KEY_DTOR_FN
KEY_DTOR_FN( table->buckets[ i ].key );
#endif
#ifdef VAL_DTOR_FN
VAL_DTOR_FN( table->buckets[ i ].val );
#endif
}
table->metadata[ i ] = VT_EMPTY;
}
table->key_count = 0;
}
VT_API_FN_QUALIFIERS void VT_CAT( NAME, _cleanup )( NAME *table )
{
if( !table->buckets_mask )
return;
#if defined( KEY_DTOR_FN ) || defined( VAL_DTOR_FN )
VT_CAT( NAME, _clear )( table );
#endif
FREE_FN(
table->buckets,
VT_CAT( NAME, _total_alloc_size )( table )
#ifdef CTX_TY
, &table->ctx
#endif
);
VT_CAT( NAME, _init )(
table
#ifdef CTX_TY
, table->ctx
#endif
);
}
#endif
#if defined(__STDC_VERSION__) && \
__STDC_VERSION__ >= 201112L && \
!defined( IMPLEMENTATION_MODE ) && \
!defined( VT_NO_C11_GENERIC_API ) \
typedef NAME VT_CAT( vt_table_, VT_TEMPLATE_COUNT );
typedef VT_CAT( NAME, _itr ) VT_CAT( vt_table_itr_, VT_TEMPLATE_COUNT );
static inline void VT_CAT( vt_init_, VT_TEMPLATE_COUNT )(
NAME *table
#ifdef CTX_TY
, CTX_TY ctx
#endif
)
{
VT_CAT( NAME, _init )(
table
#ifdef CTX_TY
, ctx
#endif
);
}
static inline bool VT_CAT( vt_init_clone_, VT_TEMPLATE_COUNT )(
NAME *table,
const NAME* source
#ifdef CTX_TY
, CTX_TY ctx
#endif
)
{
return VT_CAT( NAME, _init_clone )(
table,
source
#ifdef CTX_TY
, ctx
#endif
);
}
static inline size_t VT_CAT( vt_size_, VT_TEMPLATE_COUNT )( const NAME *table )
{
return VT_CAT( NAME, _size )( table );
}
static inline size_t VT_CAT( vt_bucket_count_, VT_TEMPLATE_COUNT )( const NAME *table )
{
return VT_CAT( NAME, _bucket_count )( table );
}
static inline bool VT_CAT( vt_is_end_, VT_TEMPLATE_COUNT )( VT_CAT( NAME, _itr ) itr )
{
return VT_CAT( NAME, _is_end )( itr );
}
static inline VT_CAT( NAME, _itr ) VT_CAT( vt_insert_, VT_TEMPLATE_COUNT )(
NAME *table,
KEY_TY key
#ifdef VAL_TY
, VAL_TY val
#endif
)
{
return VT_CAT( NAME, _insert )(
table,
key
#ifdef VAL_TY
, val
#endif
);
}
static inline VT_CAT( NAME, _itr ) VT_CAT( vt_get_or_insert_, VT_TEMPLATE_COUNT )(
NAME *table,
KEY_TY key
#ifdef VAL_TY
, VAL_TY val
#endif
)
{
return VT_CAT( NAME, _get_or_insert )(
table,
key
#ifdef VAL_TY
, val
#endif
);
}
static inline VT_CAT( NAME, _itr ) VT_CAT( vt_get_, VT_TEMPLATE_COUNT )( const NAME *table, KEY_TY key )
{
return VT_CAT( NAME, _get )( table, key );
}
static inline bool VT_CAT( vt_erase_, VT_TEMPLATE_COUNT )( NAME *table, KEY_TY key )
{
return VT_CAT( NAME, _erase )( table, key );
}
static inline VT_CAT( NAME, _itr ) VT_CAT( vt_next_, VT_TEMPLATE_COUNT )( VT_CAT( NAME, _itr ) itr )
{
return VT_CAT( NAME, _next )( itr );
}
static inline VT_CAT( NAME, _itr ) VT_CAT( vt_erase_itr_, VT_TEMPLATE_COUNT )( NAME *table, VT_CAT( NAME, _itr ) itr )
{
return VT_CAT( NAME, _erase_itr )( table, itr );
}
static inline bool VT_CAT( vt_reserve_, VT_TEMPLATE_COUNT )( NAME *table, size_t bucket_count )
{
return VT_CAT( NAME, _reserve )( table, bucket_count );
}
static inline bool VT_CAT( vt_shrink_, VT_TEMPLATE_COUNT )( NAME *table )
{
return VT_CAT( NAME, _shrink )( table );
}
static inline VT_CAT( NAME, _itr ) VT_CAT( vt_first_, VT_TEMPLATE_COUNT )( const NAME *table )
{
return VT_CAT( NAME, _first )( table );
}
static inline void VT_CAT( vt_clear_, VT_TEMPLATE_COUNT )( NAME *table )
{
VT_CAT( NAME, _clear )( table );
}
static inline void VT_CAT( vt_cleanup_, VT_TEMPLATE_COUNT )( NAME *table )
{
VT_CAT( NAME, _cleanup )( table );
}
#if VT_TEMPLATE_COUNT_D1 == 0
#undef VT_TEMPLATE_COUNT_D1
#define VT_TEMPLATE_COUNT_D1 1
#elif VT_TEMPLATE_COUNT_D1 == 1
#undef VT_TEMPLATE_COUNT_D1
#define VT_TEMPLATE_COUNT_D1 2
#elif VT_TEMPLATE_COUNT_D1 == 2
#undef VT_TEMPLATE_COUNT_D1
#define VT_TEMPLATE_COUNT_D1 3
#elif VT_TEMPLATE_COUNT_D1 == 3
#undef VT_TEMPLATE_COUNT_D1
#define VT_TEMPLATE_COUNT_D1 4
#elif VT_TEMPLATE_COUNT_D1 == 4
#undef VT_TEMPLATE_COUNT_D1
#define VT_TEMPLATE_COUNT_D1 5
#elif VT_TEMPLATE_COUNT_D1 == 5
#undef VT_TEMPLATE_COUNT_D1
#define VT_TEMPLATE_COUNT_D1 6
#elif VT_TEMPLATE_COUNT_D1 == 6
#undef VT_TEMPLATE_COUNT_D1
#define VT_TEMPLATE_COUNT_D1 7
#elif VT_TEMPLATE_COUNT_D1 == 7
#undef VT_TEMPLATE_COUNT_D1
#define VT_TEMPLATE_COUNT_D1 0
#if VT_TEMPLATE_COUNT_D2 == 0
#undef VT_TEMPLATE_COUNT_D2
#define VT_TEMPLATE_COUNT_D2 1
#elif VT_TEMPLATE_COUNT_D2 == 1
#undef VT_TEMPLATE_COUNT_D2
#define VT_TEMPLATE_COUNT_D2 2
#elif VT_TEMPLATE_COUNT_D2 == 2
#undef VT_TEMPLATE_COUNT_D2
#define VT_TEMPLATE_COUNT_D2 3
#elif VT_TEMPLATE_COUNT_D2 == 3
#undef VT_TEMPLATE_COUNT_D2
#define VT_TEMPLATE_COUNT_D2 4
#elif VT_TEMPLATE_COUNT_D2 == 4
#undef VT_TEMPLATE_COUNT_D2
#define VT_TEMPLATE_COUNT_D2 5
#elif VT_TEMPLATE_COUNT_D2 == 5
#undef VT_TEMPLATE_COUNT_D2
#define VT_TEMPLATE_COUNT_D2 6
#elif VT_TEMPLATE_COUNT_D2 == 6
#undef VT_TEMPLATE_COUNT_D2
#define VT_TEMPLATE_COUNT_D2 7
#elif VT_TEMPLATE_COUNT_D2 == 7
#undef VT_TEMPLATE_COUNT_D2
#define VT_TEMPLATE_COUNT_D2 0
#if VT_TEMPLATE_COUNT_D3 == 0
#undef VT_TEMPLATE_COUNT_D3
#define VT_TEMPLATE_COUNT_D3 1
#elif VT_TEMPLATE_COUNT_D3 == 1
#undef VT_TEMPLATE_COUNT_D3
#define VT_TEMPLATE_COUNT_D3 2
#elif VT_TEMPLATE_COUNT_D3 == 2
#undef VT_TEMPLATE_COUNT_D3
#define VT_TEMPLATE_COUNT_D3 3
#elif VT_TEMPLATE_COUNT_D3 == 3
#undef VT_TEMPLATE_COUNT_D3
#define VT_TEMPLATE_COUNT_D3 4
#elif VT_TEMPLATE_COUNT_D3 == 4
#undef VT_TEMPLATE_COUNT_D3
#define VT_TEMPLATE_COUNT_D3 5
#elif VT_TEMPLATE_COUNT_D3 == 5
#undef VT_TEMPLATE_COUNT_D3
#define VT_TEMPLATE_COUNT_D3 6
#elif VT_TEMPLATE_COUNT_D3 == 6
#undef VT_TEMPLATE_COUNT_D3
#define VT_TEMPLATE_COUNT_D3 7
#elif VT_TEMPLATE_COUNT_D3 == 7
#error Sorry, the number of template instances is limited to 511. Define VT_NO_C11_GENERIC_API globally and use the \
C99 prefixed function API to circumvent this restriction.
#endif
#endif
#endif
#endif
#undef NAME
#undef KEY_TY
#undef VAL_TY
#undef HASH_FN
#undef CMPR_FN
#undef MAX_LOAD
#undef KEY_DTOR_FN
#undef VAL_DTOR_FN
#undef CTX_TY
#undef MALLOC_FN
#undef FREE_FN
#undef HEADER_MODE
#undef IMPLEMENTATION_MODE
#undef VT_API_FN_QUALIFIERS