Skip to content

Instantly share code, notes, and snippets.

@jsylvanus
Created January 22, 2017 16:24
Show Gist options
  • Save jsylvanus/b2e912e57dc9e9ad02e163fdbf782104 to your computer and use it in GitHub Desktop.
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.
#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);
}
#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