Created
January 22, 2017 16:24
-
-
Save jsylvanus/b2e912e57dc9e9ad02e163fdbf782104 to your computer and use it in GitHub Desktop.
I don't know, I thought writing a generic spatial hash in C would be a good idea.
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 <stdint.h> | |
#include <stdlib.h> | |
typedef struct spatial_hash_s { | |
uint32_t width; | |
uint32_t height; | |
uint16_t cellmax; // max # of entries per cell | |
uint64_t entrysize; // size of a single entry | |
} spatial_hash_t; | |
typedef struct spatial_index_s { | |
uint16_t count; // # of entries held in this cell | |
} spatial_index_t; | |
#define SHASH_CELL_AT(h,x,y) \ | |
((spatial_index_t*)(((uint8_t*)((h)+1))+(((y)*(h)->width+(x))*(sizeof(spatial_index_t)+((h)->entrysize)*((h)->cellmax))))) | |
#define shash_new(type, w, h, cellmax) \ | |
(spatial_hash((w),(h),(cellmax),sizeof(type))) | |
spatial_hash_t* spatial_hash(uint32_t w, uint32_t h, uint16_t cellmax, uint16_t entrysize) { | |
uint32_t cells = w * h; | |
uint64_t rowsize = (sizeof(spatial_index_t) + (cellmax * entrysize)); | |
uint64_t total_size = sizeof(spatial_hash_t) + (cells * rowsize); | |
spatial_hash_t *sh = malloc(total_size); | |
memset(sh, 0, total_size); // zero out the memory | |
sh->width = w; | |
sh->height = h; | |
sh->cellmax = cellmax; | |
sh->entrysize = entrysize; | |
return sh; | |
} | |
// shash_get(sh, 0, 0, geometry_entry, &count, &results) | |
#define shash_get(type, hash, x, y, countref) \ | |
((type*)spatial_hash_get((hash),(x),(y),(countref))) | |
void* spatial_hash_get(spatial_hash_t *sh, uint16_t x, uint16_t y, uint16_t *count) { | |
spatial_index_t *row = SHASH_CELL_AT(sh, x, y); | |
*count = row->count; | |
return (void*)(row+1); | |
} | |
#define shash_append spatial_hash_append | |
void spatial_hash_append(spatial_hash_t *sh, uint16_t x, uint16_t y, void* val) { | |
spatial_index_t *cell = SHASH_CELL_AT(sh, x, y); | |
void *free_entry = (void*)(((char*)(cell+1))+(cell->count)*sh->entrysize); | |
memcpy(free_entry, val, sh->entrysize); | |
cell->count++; | |
} | |
#define shash_delete spatial_hash_delete | |
void spatial_hash_delete(spatial_hash_t *sh, uint16_t x, uint16_t y, uint16_t i) { | |
spatial_index_t *cell = SHASH_CELL_AT(sh, x, y); | |
char *base = (char*)(cell+1); | |
while (i < cell->count) { | |
// overwrite with next entry | |
memcpy(base + (i*sh->entrysize), base + ((i+1)*sh->entrysize), sh->entrysize); | |
i++; | |
} | |
cell->count--; | |
} | |
void spatial_hash_reset(spatial_hash_t *sh) { | |
int cells = sh->width * sh->height; | |
uint64_t cellsize = (sizeof(spatial_index_t) + (sh->cellmax * sh->entrysize)); | |
uint64_t total_size = sizeof(spatial_hash_t) + (cells * cellsize); | |
memset((void*)(sh+1), 0, total_size); | |
} | |
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
#include "shash_tests.h" | |
#include "../src/spatial_hash.h" | |
typedef struct hashtest_s { | |
int v; | |
char c; | |
} hashtest_t; | |
spatial_hash_t *hash; | |
void test_creating_hash(void) { | |
hash = shash_new(hashtest_t, 2, 2, 5); // 2x2 with max of 5 entries | |
TEST_ASSERT_EQUAL_INT(hash->width, 2); | |
TEST_ASSERT_EQUAL_INT(hash->height, 2); | |
uint16_t count = 5; | |
hashtest_t *x = shash_get(hashtest_t, hash, 0, 0, &count); | |
TEST_ASSERT_EQUAL_INT(count, 0); | |
x = shash_get(hashtest_t, hash, 1, 0, &count); | |
TEST_ASSERT_EQUAL_INT(count, 0); | |
x = shash_get(hashtest_t, hash, 0, 1, &count); | |
TEST_ASSERT_EQUAL_INT(count, 0); | |
x = shash_get(hashtest_t, hash, 1, 1, &count); | |
TEST_ASSERT_EQUAL_INT(count, 0); | |
free(hash); hash = NULL; | |
} | |
void test_deleting_items(void) { // run test_creating_hash first | |
hash = shash_new(hashtest_t, 2, 2, 5); // 2x2 with max of 5 entries | |
hashtest_t val = { 1234, 'X' }; | |
shash_append(hash, 1, 1, &val); | |
hashtest_t val2 = { 2345, 'Y' }; | |
shash_append(hash, 1, 1, &val2); | |
uint16_t count = 0; | |
hashtest_t *row = shash_get(hashtest_t, hash, 1, 1, &count); | |
TEST_ASSERT_EQUAL_INT(count, 2); | |
TEST_ASSERT_EQUAL_INT((row+1)->v, 2345); | |
TEST_ASSERT_EQUAL((row+1)->c, 'Y'); | |
shash_delete(hash, 1, 1, 0); | |
row = shash_get(hashtest_t, hash, 1, 1, &count); | |
TEST_ASSERT_EQUAL_INT(count, 1); | |
TEST_ASSERT_EQUAL_INT(row->v, 2345); | |
TEST_ASSERT_EQUAL(row->c, 'Y'); | |
shash_delete(hash, 1, 1, 0); | |
row = shash_get(hashtest_t, hash, 1, 1, &count); | |
TEST_ASSERT_EQUAL_INT(count, 0); | |
free(hash); hash = NULL; | |
} | |
void test_adding_and_getting_items(void) { // run test_creating_hash first | |
hash = shash_new(hashtest_t, 2, 2, 5); // 2x2 with max of 5 entries | |
hashtest_t val = { 1234, 'X' }; | |
shash_append(hash, 1, 1, &val); | |
uint16_t count = 0; | |
hashtest_t *row = shash_get(hashtest_t, hash, 1, 1, &count); | |
TEST_ASSERT_EQUAL_INT(count, 1); | |
TEST_ASSERT_EQUAL_INT(row->v, 1234); | |
TEST_ASSERT_EQUAL(row->c, 'X'); | |
free(hash); hash = NULL; | |
} | |
void shash_tests(void) { | |
UnityBegin("test/shash_tests.c"); | |
RUN_TEST(test_creating_hash); | |
RUN_TEST(test_adding_and_getting_items); | |
RUN_TEST(test_deleting_items); | |
UnityEnd(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment