Created
November 7, 2018 23:43
-
-
Save jaburns/f15df68937cb91af62f292fde40e7880 to your computer and use it in GitHub Desktop.
Generic variable size array in C
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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