Skip to content

Instantly share code, notes, and snippets.

@jaburns
Created November 7, 2018 23:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jaburns/f15df68937cb91af62f292fde40e7880 to your computer and use it in GitHub Desktop.
Save jaburns/f15df68937cb91af62f292fde40e7880 to your computer and use it in GitHub Desktop.
Generic variable size array in C
#pragma once
#include <stddef.h>
#include <stdlib.h>
#define Vec( T ) struct { \
size_t count; \
T *items; \
}
#define VEC_EMPTY { 0, 0 }
#define VEC_SET_EMPTY( vec ) do \
{ \
(vec).count = 0; \
(vec).items = 0; \
} \
while(0)
#define VEC_CLEAR( vec ) do \
{ \
if( (vec).count > 0 ) { \
free( (vec).items ); \
(vec).count = 0; \
(vec).items = 0; \
} \
} \
while(0)
#define VEC_CLEAR_WITH_CALLBACK( vec, ctx, func ) do \
{ \
for( size_t i = 0; i < (vec).count; ++ i ) \
func( ctx, &(vec).items[i] ); \
VEC_CLEAR( (vec) ); \
} \
while(0)
#define VEC_RESIZE( vec, new_size ) do \
{ \
if( (new_size) == 0 ) \
VEC_CLEAR( (vec) ); \
else { \
(vec).count = new_size; \
(void*)(vec).items = realloc( (vec).items, sizeof( *(vec).items ) * (vec).count ); \
} \
} \
while(0)
#define VEC_PUSH( vec, item ) do \
{ \
(vec).count++; \
(void*)(vec).items = realloc( (vec).items, sizeof( *(vec).items ) * (vec).count ); \
(vec).items[(vec).count - 1] = (item); \
} \
while(0)
#define VEC_POP_DECL( T, var_name, vec ) \
T var_name = *( (vec).count > 0 ? &(vec).items[(vec).count - 1] : (T*)0 ); \
VEC_RESIZE( (vec), (vec).count - 1 )
#define VEC_INSERT( vec, index, item ) do \
{ \
if( (index) < (vec).count ) \
{ \
VEC_RESIZE( (vec), (vec).count + 1 ); \
for( size_t i = (vec).count - 1; i > (index); --i ) \
(vec).items[i] = (vec).items[i - 1]; \
(vec).items[index] = (item); \
} \
else \
VEC_PUSH( (vec), (item) ); \
} \
while(0)
#define VEC_REMOVE( vec, index ) do \
{ \
if( (index) >= (vec).count ) break; \
\
for( size_t i = (index); i < (vec).count - 1 ; ++i ) \
(vec).items[i] = (vec).items[i + 1]; \
VEC_RESIZE( (vec), (vec).count - 1 ); \
} \
while(0)
#define VEC_CLONE( vec_from, vec_to ) do \
{ \
VEC_RESIZE( (vec_to), (vec_from).count ); \
for( size_t i = 0; i < (vec_from).count; ++i ) \
(vec_to).items[i] = (vec_from).items[i]; \
} \
while(0)
#define VEC_FIND_INDEX_DECL( T, vec, index_var_name, expr_of_ptr_item ) \
int index_var_name = -1; \
for( int i = 0; i < (vec).count; ++i ) \
{ \
T *item = &(vec).items[i]; \
if( (expr_of_ptr_item) ) \
{ \
index_var_name = i; \
break; \
} \
}
#ifdef RUN_TESTS
#include "testing.h"
extern TestResult vec_test( void );
#endif
#ifdef RUN_TESTS
#include "vec.h"
#include <stdint.h>
static int test_clear_callback_calls;
static uint8_t test_clear_callback_sum;
static void test_clear_callback(void *context, void *item)
{
test_clear_callback_calls++;
test_clear_callback_sum += *((uint8_t*)item);
}
TestResult vec_test(void)
{
TEST_BEGIN("Vec at, push, and pop work correctly");
Vec(float) v = VEC_EMPTY;
VEC_PUSH( v, 4.0f );
VEC_PUSH( v, 4.0f );
VEC_PUSH( v, 8.0f );
TEST_ASSERT( v.count == 3 );
TEST_ASSERT( v.items[0] == 4.0f );
TEST_ASSERT( v.items[1] == 4.0f );
TEST_ASSERT( v.items[2] == 8.0f );
VEC_POP_DECL( float, pop0, v );
VEC_POP_DECL( float, pop1, v );
VEC_POP_DECL( float, pop2, v );
TEST_ASSERT( pop0 == 8.0f );
TEST_ASSERT( pop1 == 4.0f );
TEST_ASSERT( pop2 == 4.0f );
TEST_ASSERT( v.count == 0 );
TEST_ASSERT( v.items == 0 );
TEST_END();
TEST_BEGIN("Vec clear with callback iterates the vec");
Vec(uint8_t) v = VEC_EMPTY;
VEC_PUSH( v, 2 );
VEC_PUSH( v, 4 );
VEC_PUSH( v, 8 );
test_clear_callback_calls = 0;
test_clear_callback_sum = 0;
VEC_CLEAR_WITH_CALLBACK( v, NULL, test_clear_callback );
TEST_ASSERT( test_clear_callback_calls == 3 );
TEST_ASSERT( test_clear_callback_sum == 14 );
TEST_ASSERT( v.count == 0 );
TEST_ASSERT( v.items == 0 );
TEST_END();
TEST_BEGIN("Vec resize larger and smaller works, resize zero clears");
Vec(uint8_t) v = VEC_EMPTY;
VEC_PUSH( v, 2 );
VEC_PUSH( v, 4 );
VEC_RESIZE( v, 4 );
TEST_ASSERT( v.count == 4 );
VEC_RESIZE( v, 1 );
TEST_ASSERT( v.count == 1 );
TEST_ASSERT( v.items[0] == 2 );
VEC_RESIZE( v, 0 );
TEST_ASSERT( v.count == 0 );
TEST_ASSERT( v.items == 0 );
TEST_END();
TEST_BEGIN("Vec insert and remove and clear behave correctly");
Vec(uint8_t) v = VEC_EMPTY;
VEC_PUSH( v, 0 );
VEC_PUSH( v, 1 );
VEC_PUSH( v, 2 );
VEC_PUSH( v, 3 );
VEC_INSERT( v, 1, 10 ); //[0,10,1,2,3]
TEST_ASSERT( v.items[1] == 10 );
TEST_ASSERT( v.items[4] == 3 );
TEST_ASSERT( v.count == 5 );
VEC_INSERT( v, 0, 11 ); //[11,0,10,1,2,3]
TEST_ASSERT( v.items[0] == 11 );
VEC_INSERT( v, 6, 4 ); //[11,0,10,1,2,3,4]
TEST_ASSERT( v.items[6] == 4 );
VEC_REMOVE( v, 1 ); //[11,10,1,2,3,4]
TEST_ASSERT( v.items[1] == 10 );
VEC_REMOVE( v, 5 ); //[11,10,1,2,3]
TEST_ASSERT( v.items[4] == 3 );
VEC_REMOVE( v, 0 ); //[10,1,2,3]
TEST_ASSERT( v.items[0] == 10 );
TEST_ASSERT( v.count == 4 );
VEC_CLEAR( v );
TEST_ASSERT( v.count == 0 );
TEST_ASSERT( v.items == 0 );
TEST_END();
TEST_BEGIN("Vec clone behaves correctly");
Vec(uint8_t) v = VEC_EMPTY;
Vec(uint8_t) u = VEC_EMPTY;
VEC_PUSH( v, 0 );
VEC_PUSH( v, 1 );
VEC_PUSH( v, 2 );
VEC_PUSH( v, 3 );
VEC_CLONE( v, u );
TEST_ASSERT( u.count == v.count );
for( size_t i = 0; i < u.count; ++i )
TEST_ASSERT(v.items[i] == u.items[i]);
VEC_CLEAR( v );
VEC_CLEAR( u );
TEST_END();
TEST_BEGIN("Vec find index finds item or returns -1");
Vec(uint8_t) v = VEC_EMPTY;
VEC_PUSH( v, 0 );
VEC_PUSH( v, 1 );
VEC_PUSH( v, 2 );
VEC_PUSH( v, 3 );
VEC_FIND_INDEX_DECL( uint8_t, v, find0, *item == 1 );
VEC_FIND_INDEX_DECL( uint8_t, v, find1, *item == 42 );
TEST_ASSERT( find0 == 1 );
TEST_ASSERT( find1 < 0 );
VEC_CLEAR( v );
TEST_END();
return 0;
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment