cross-chunk tile wall merging

This commit is contained in:
jacob 2025-05-21 21:17:04 -05:00
parent 4bc6ca6679
commit a47009f16b
6 changed files with 239 additions and 169 deletions

View File

@ -39,7 +39,7 @@
#define SPACE_CELL_BINS_SQRT (64)
#define SPACE_CELL_SIZE (1)
#define SIM_TILES_PER_UNIT_SQRT (2)
#define SIM_TILES_PER_UNIT_SQRT (4)
#define SIM_TILES_PER_CHUNK_SQRT (16)
#define SIM_TICKS_PER_SECOND 50

View File

@ -13,6 +13,15 @@
* Contact
* ========================== */
INTERNAL b32 can_contact(struct sim_ent *e0, struct sim_ent *e1)
{
b32 res = false;
res = e0 != e1 &&
!sim_ent_id_eq(e0->top, e1->top) &&
!(sim_ent_has_prop(e0, SEPROP_WALL) && sim_ent_has_prop(e1, SEPROP_WALL));
return res;
}
void phys_create_and_update_contacts(struct phys_step_ctx *ctx, f32 elapsed_dt, u64 phys_iteration)
{
__prof;
@ -39,10 +48,10 @@ void phys_create_and_update_contacts(struct phys_step_ctx *ctx, f32 elapsed_dt,
struct space_entry *space_entry;
while ((space_entry = space_iter_next(&iter))) {
struct sim_ent *check1 = sim_ent_from_id(ss, space_entry->ent);
if (check1 == check0) continue;
if (!sim_ent_is_valid_and_active(check1)) continue;
if (!(sim_ent_has_prop(check1, SEPROP_SOLID) || sim_ent_has_prop(check1, SEPROP_SENSOR))) continue;
if (check1->local_collider.count <= 0) continue;
if (!can_contact(check0, check1)) continue;
/* Deterministic order based on entity id */
struct sim_ent *e0;
@ -1171,10 +1180,10 @@ f32 phys_determine_earliest_toi(struct phys_step_ctx *ctx, f32 step_dt, f32 tole
struct space_entry *entry;
while ((entry = space_iter_next(&iter))) {
struct sim_ent *e1 = sim_ent_from_id(ss, entry->ent);
if (e1 == e0) continue;
if (!sim_ent_should_simulate(e1)) continue;
if (!(sim_ent_has_prop(e1, SEPROP_SOLID) || sim_ent_has_prop(e1, SEPROP_SENSOR))) continue;
if (e1->local_collider.count <= 0) continue;
if (!can_contact(e0, e1)) continue;
struct collider_shape e1_collider = e1->local_collider;
struct xform e1_xf_t0 = sim_ent_get_xform(e1);

View File

@ -377,21 +377,21 @@ INTERNAL void test_spawn_tile(struct sim_snapshot *world, struct v2 world_pos)
INTERNAL SORT_COMPARE_FUNC_DEF(tile_chunk_sort, arg_a, arg_b, udata)
{
(UNUSED)udata;
struct sim_ent *a = (struct sim_ent *)arg_a;
struct sim_ent *b = (struct sim_ent *)arg_b;
struct sim_ent *a = *(struct sim_ent **)arg_a;
struct sim_ent *b = *(struct sim_ent **)arg_b;
struct v2i32 index_a = a->tile_chunk_index;
struct v2i32 index_b = b->tile_chunk_index;
i32 res = 0;
res = 2 * ((index_a.y > index_b.y) - (index_a.y < index_b.y)) + ((index_a.x > index_b.x) - (index_a.x < index_b.x));
res = 2 * ((index_a.y < index_b.y) - (index_a.y > index_b.y)) + ((index_a.x < index_b.x) - (index_a.x > index_b.x));
return res;
}
INTERNAL void test_generate_walls(struct sim_ent *parent)
INTERNAL void test_generate_walls(struct sim_snapshot *world)
{
#if 1
__prof;
struct temp_arena scratch = scratch_begin_no_conflict();
struct sim_snapshot *world = parent->ss;
struct sim_ent *root = sim_ent_from_id(world, SIM_ENT_ROOT_ID);
/* Release existing walls */
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
@ -403,17 +403,17 @@ INTERNAL void test_generate_walls(struct sim_ent *parent)
}
/* Sort tile chunks */
struct sim_ent **tile_chunks = arena_dry_push(scratch.arena, struct sim_ent *);
u64 tile_chunks_count = 0;
struct sim_ent **sorted_tile_chunks = arena_dry_push(scratch.arena, struct sim_ent *);
u64 sorted_tile_chunks_count = 0;
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
struct sim_ent *ent = &world->ents[ent_index];
if (!ent->valid) continue;
if (sim_ent_has_prop(ent, SEPROP_TILE_CHUNK)) {
*arena_push_no_zero(scratch.arena, struct sim_ent *) = ent;
++tile_chunks_count;
++sorted_tile_chunks_count;
}
}
merge_sort(tile_chunks, tile_chunks_count, sizeof(*tile_chunks), tile_chunk_sort, NULL);
merge_sort(sorted_tile_chunks, sorted_tile_chunks_count, sizeof(*sorted_tile_chunks), tile_chunk_sort, NULL);
struct wall_node {
struct v2i32 start;
@ -421,124 +421,156 @@ INTERNAL void test_generate_walls(struct sim_ent *parent)
struct wall_node *next;
};
/* Dicts containing walls that end on edge of tile chunk, keyed by tile end index.
* Used to expand walls accross tile chunks. */
struct dict horizontal_ends_dict = dict_init(scratch.arena, 1024);
struct dict vertical_ends_dict = dict_init(scratch.arena, 1024);
struct wall_node *first_wall = NULL;
/* Generate horizontal wall nodes */
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
struct sim_ent *chunk = &world->ents[ent_index];
if (!chunk->valid) continue;
if (sim_ent_has_prop(chunk, SEPROP_TILE_CHUNK)) {
struct v2i32 chunk_index = chunk->tile_chunk_index;
/* Generate horizontal wall nodes */
{
struct sim_ent *top_chunk = sim_tile_chunk_from_chunk_index(world, V2I32(chunk_index.x, chunk_index.y - 1));
struct sim_ent *bottom_chunk = sim_tile_chunk_from_chunk_index(world, V2I32(chunk_index.x, chunk_index.y + 1));
/* If there's no chunk below this one, then do an extra iteration (since walls are created at the top of each tile) */
i32 y_iterations = SIM_TILES_PER_CHUNK_SQRT + !bottom_chunk->valid;
i32 x_iterations = SIM_TILES_PER_CHUNK_SQRT + 1;
for (i32 tile_y = 0; tile_y < y_iterations; ++tile_y) {
i32 wall_start = -1;
i32 wall_end = -1;
for (i32 tile_x = 0; tile_x < x_iterations; ++tile_x) {
enum sim_tile_kind tile = SIM_TILE_KIND_NONE;
if (tile_x < SIM_TILES_PER_CHUNK_SQRT && tile_y < SIM_TILES_PER_CHUNK_SQRT) {
tile = sim_get_chunk_tile(chunk, V2I32(tile_x, tile_y));
}
b32 should_have_top_wall = false;
if (tile_x < SIM_TILES_PER_CHUNK_SQRT) {
enum sim_tile_kind top_tile = SIM_TILE_KIND_NONE;
if (tile_y == 0) {
if (top_chunk->valid) {
struct v2i32 top_tile_local_index = V2I32(tile_x, SIM_TILES_PER_CHUNK_SQRT - 1);
top_tile = sim_get_chunk_tile(top_chunk, top_tile_local_index);
}
} else {
top_tile = sim_get_chunk_tile(chunk, V2I32(tile_x, tile_y - 1));
for (u64 sorted_index = 0; sorted_index < sorted_tile_chunks_count; ++sorted_index) {
struct sim_ent *chunk = sorted_tile_chunks[sorted_index];
struct v2i32 chunk_index = chunk->tile_chunk_index;
/* Generate horizontal wall nodes */
{
struct sim_ent *top_chunk = sim_tile_chunk_from_chunk_index(world, V2I32(chunk_index.x, chunk_index.y - 1));
struct sim_ent *bottom_chunk = sim_tile_chunk_from_chunk_index(world, V2I32(chunk_index.x, chunk_index.y + 1));
/* If there's no chunk below this one, then do an extra iteration (since walls are created at the top of each tile) */
i32 y_iterations = SIM_TILES_PER_CHUNK_SQRT + !bottom_chunk->valid;
i32 x_iterations = SIM_TILES_PER_CHUNK_SQRT + 1;
for (i32 tile_y = 0; tile_y < y_iterations; ++tile_y) {
i32 wall_start = -1;
i32 wall_end = -1;
for (i32 tile_x = 0; tile_x < x_iterations; ++tile_x) {
enum sim_tile_kind tile = SIM_TILE_KIND_NONE;
if (tile_x < SIM_TILES_PER_CHUNK_SQRT && tile_y < SIM_TILES_PER_CHUNK_SQRT) {
tile = sim_get_chunk_tile(chunk, V2I32(tile_x, tile_y));
}
b32 should_have_top_wall = false;
if (tile_x < SIM_TILES_PER_CHUNK_SQRT) {
enum sim_tile_kind top_tile = SIM_TILE_KIND_NONE;
if (tile_y == 0) {
if (top_chunk->valid) {
struct v2i32 top_tile_local_index = V2I32(tile_x, SIM_TILES_PER_CHUNK_SQRT - 1);
top_tile = sim_get_chunk_tile(top_chunk, top_tile_local_index);
}
if (tile == SIM_TILE_KIND_WALL) {
/* Process wall tile */
should_have_top_wall = top_tile != SIM_TILE_KIND_WALL;
} else {
/* Process non-wall tile */
should_have_top_wall = top_tile == SIM_TILE_KIND_WALL;
} else {
top_tile = sim_get_chunk_tile(chunk, V2I32(tile_x, tile_y - 1));
}
if (tile == SIM_TILE_KIND_WALL) {
/* Process wall tile */
should_have_top_wall = top_tile != SIM_TILE_KIND_WALL;
} else {
/* Process non-wall tile */
should_have_top_wall = top_tile == SIM_TILE_KIND_WALL;
}
}
if (should_have_top_wall) {
if (wall_start < 0) {
/* Start wall */
wall_start = tile_x;
}
/* Extend wall */
wall_end = tile_x + 1;
} else if (wall_end >= 0) {
/* Stop wall */
struct v2i32 start = sim_world_tile_index_from_local_tile_index(chunk_index, V2I32(wall_start, tile_y));
struct v2i32 end = sim_world_tile_index_from_local_tile_index(chunk_index, V2I32(wall_end, tile_y));
struct wall_node *node = NULL;
if (wall_start == 0) {
u64 start_hash = rand_u64_from_seed(*(u64 *)&start);
struct dict_entry *entry = dict_get_entry(&horizontal_ends_dict, start_hash);
if (entry) {
node = (struct wall_node *)entry->value;
dict_remove_entry(&horizontal_ends_dict, entry);
}
}
if (should_have_top_wall) {
if (wall_start < 0) {
/* Start wall */
wall_start = tile_x;
}
/* Extend wall */
wall_end = tile_x + 1;
} else if (wall_end >= 0) {
/* Stop wall */
struct v2i32 start = sim_world_tile_index_from_local_tile_index(chunk_index, V2I32(wall_start, tile_y));
struct v2i32 end = sim_world_tile_index_from_local_tile_index(chunk_index, V2I32(wall_end, tile_y));
struct wall_node *node = arena_push(scratch.arena, struct wall_node);
if (!node) {
node = arena_push(scratch.arena, struct wall_node);
node->start = start;
node->end = end;
node->next = first_wall;
first_wall = node;
wall_start = -1;
wall_end = -1;
}
node->end = end;
if (wall_end == SIM_TILES_PER_CHUNK_SQRT) {
u64 end_hash = rand_u64_from_seed(*(u64 *)&end);
dict_set(scratch.arena, &horizontal_ends_dict, end_hash, node);
}
wall_start = -1;
wall_end = -1;
}
}
}
/* Generate vertical wall nodes */
{
struct sim_ent *left_chunk = sim_tile_chunk_from_chunk_index(world, V2I32(chunk_index.x - 1, chunk_index.y));
struct sim_ent *right_chunk = sim_tile_chunk_from_chunk_index(world, V2I32(chunk_index.x + 1, chunk_index.y));
/* If there's no chunk to the right of this one, then do an extra iteration (since walls are created on the left of each tile) */
i32 y_iterations = SIM_TILES_PER_CHUNK_SQRT + 1;
i32 x_iterations = SIM_TILES_PER_CHUNK_SQRT + !right_chunk->valid;
for (i32 tile_x = 0; tile_x < x_iterations; ++tile_x) {
i32 wall_start = -1;
i32 wall_end = -1;
for (i32 tile_y = 0; tile_y < y_iterations; ++tile_y) {
enum sim_tile_kind tile = SIM_TILE_KIND_NONE;
if (tile_x < SIM_TILES_PER_CHUNK_SQRT && tile_y < SIM_TILES_PER_CHUNK_SQRT) {
tile = sim_get_chunk_tile(chunk, V2I32(tile_x, tile_y));
}
}
/* Generate vertical wall nodes */
{
struct sim_ent *left_chunk = sim_tile_chunk_from_chunk_index(world, V2I32(chunk_index.x - 1, chunk_index.y));
struct sim_ent *right_chunk = sim_tile_chunk_from_chunk_index(world, V2I32(chunk_index.x + 1, chunk_index.y));
/* If there's no chunk to the right of this one, then do an extra iteration (since walls are created on the left of each tile) */
i32 y_iterations = SIM_TILES_PER_CHUNK_SQRT + 1;
i32 x_iterations = SIM_TILES_PER_CHUNK_SQRT + !right_chunk->valid;
for (i32 tile_x = 0; tile_x < x_iterations; ++tile_x) {
i32 wall_start = -1;
i32 wall_end = -1;
for (i32 tile_y = 0; tile_y < y_iterations; ++tile_y) {
enum sim_tile_kind tile = SIM_TILE_KIND_NONE;
if (tile_x < SIM_TILES_PER_CHUNK_SQRT && tile_y < SIM_TILES_PER_CHUNK_SQRT) {
tile = sim_get_chunk_tile(chunk, V2I32(tile_x, tile_y));
}
b32 should_have_left_wall = false;
if (tile_y < SIM_TILES_PER_CHUNK_SQRT) {
enum sim_tile_kind left_tile = SIM_TILE_KIND_NONE;
if (tile_x == 0) {
if (left_chunk->valid) {
struct v2i32 left_tile_local_index = V2I32(SIM_TILES_PER_CHUNK_SQRT - 1, tile_y);
left_tile = sim_get_chunk_tile(left_chunk, left_tile_local_index);
}
} else {
left_tile = sim_get_chunk_tile(chunk, V2I32(tile_x - 1, tile_y));
b32 should_have_left_wall = false;
if (tile_y < SIM_TILES_PER_CHUNK_SQRT) {
enum sim_tile_kind left_tile = SIM_TILE_KIND_NONE;
if (tile_x == 0) {
if (left_chunk->valid) {
struct v2i32 left_tile_local_index = V2I32(SIM_TILES_PER_CHUNK_SQRT - 1, tile_y);
left_tile = sim_get_chunk_tile(left_chunk, left_tile_local_index);
}
if (tile == SIM_TILE_KIND_WALL) {
/* Process wall tile */
should_have_left_wall = left_tile != SIM_TILE_KIND_WALL;
} else {
/* Process non-wall tile */
should_have_left_wall = left_tile == SIM_TILE_KIND_WALL;
} else {
left_tile = sim_get_chunk_tile(chunk, V2I32(tile_x - 1, tile_y));
}
if (tile == SIM_TILE_KIND_WALL) {
/* Process wall tile */
should_have_left_wall = left_tile != SIM_TILE_KIND_WALL;
} else {
/* Process non-wall tile */
should_have_left_wall = left_tile == SIM_TILE_KIND_WALL;
}
}
if (should_have_left_wall) {
if (wall_start < 0) {
/* Start wall */
wall_start = tile_y;
}
/* Extend wall */
wall_end = tile_y + 1;
} else if (wall_end >= 0) {
/* Stop wall */
struct v2i32 start = sim_world_tile_index_from_local_tile_index(chunk_index, V2I32(tile_x, wall_start));
struct v2i32 end = sim_world_tile_index_from_local_tile_index(chunk_index, V2I32(tile_x, wall_end));
struct wall_node *node = NULL;
if (wall_start == 0) {
u64 start_hash = rand_u64_from_seed(*(u64 *)&start);
struct dict_entry *entry = dict_get_entry(&vertical_ends_dict, start_hash);
if (entry) {
node = (struct wall_node *)entry->value;
dict_remove_entry(&vertical_ends_dict, entry);
}
}
if (should_have_left_wall) {
if (wall_start < 0) {
/* Start wall */
wall_start = tile_y;
}
/* Extend wall */
wall_end = tile_y + 1;
} else if (wall_end >= 0) {
/* Stop wall */
struct v2i32 start = sim_world_tile_index_from_local_tile_index(chunk_index, V2I32(tile_x, wall_start));
struct v2i32 end = sim_world_tile_index_from_local_tile_index(chunk_index, V2I32(tile_x, wall_end));
struct wall_node *node = arena_push(scratch.arena, struct wall_node);
if (!node) {
node = arena_push(scratch.arena, struct wall_node);
node->start = start;
node->end = end;
node->next = first_wall;
first_wall = node;
wall_start = -1;
wall_end = -1;
}
node->end = end;
if (wall_end == SIM_TILES_PER_CHUNK_SQRT) {
u64 end_hash = rand_u64_from_seed(*(u64 *)&end);
dict_set(scratch.arena, &vertical_ends_dict, end_hash, node);
}
wall_start = -1;
wall_end = -1;
}
}
}
@ -547,17 +579,9 @@ INTERNAL void test_generate_walls(struct sim_ent *parent)
/* Create wall entities */
for (struct wall_node *node = first_wall; node; node = node->next) {
struct sim_ent *wall_ent = sim_ent_alloc_sync_src(parent);
struct sim_ent *wall_ent = sim_ent_alloc_sync_src(root);
sim_ent_enable_prop(wall_ent, SEPROP_WALL);
#if 0
struct v2i32 node_start = node->start;
struct v2i32 node_end = node->end;
struct v2 start = V2(node_start.x / SIM_TILES_PER_UNIT_SQRT, node_start.y / SIM_TILES_PER_UNIT_SQRT);
struct v2 end = V2(node_end.x / SIM_TILES_PER_UNIT_SQRT, node_end.y / SIM_TILES_PER_UNIT_SQRT);
#endif
struct v2 start = sim_pos_from_world_tile_index(node->start);
struct v2 end = sim_pos_from_world_tile_index(node->end);
@ -567,12 +591,11 @@ INTERNAL void test_generate_walls(struct sim_ent *parent)
sim_ent_enable_prop(wall_ent, SEPROP_SOLID);
wall_ent->local_collider.count = 2;
wall_ent->local_collider.points[1] = v2_sub(end, start);
sim_ent_activate(wall_ent, world->tick);
}
scratch_end(scratch);
#else
(UNUSED)parent;
#endif
}
@ -774,7 +797,7 @@ void sim_step(struct sim_step_ctx *ctx)
player->player_client_handle = client->handle;
sim_ent_enable_prop(player, SEPROP_PLAYER);
player->predictor = player->id;
sim_ent_enable_prop(player, SEPROP_ACTIVE);
sim_ent_activate(player, world->tick);
client->player_id = player->id;
if (client == user_input_client) {
user_input_client->player_id = player->id;
@ -949,7 +972,7 @@ void sim_step(struct sim_step_ctx *ctx)
}
}
if (flags & SIM_CONTROL_FLAG_WALLS_TEST) {
test_generate_walls(root);
test_generate_walls(world);
}
if (flags & SIM_CONTROL_FLAG_EXPLODE_TEST) {
logf_info("Explosion test");
@ -958,37 +981,9 @@ void sim_step(struct sim_step_ctx *ctx)
}
if (flags & SIM_CONTROL_FLAG_TILE_TEST) {
#if 0
if (is_master) {
struct v2 cursor_pos = player->player_cursor_pos;
/* FIXME: Negative indices */
struct v2i32 tile_pos = V2I32(cursor_pos.x * SIM_TILES_PER_UNIT_SQRT, cursor_pos.y * SIM_TILES_PER_UNIT_SQRT);
struct v2i32 chunk_pos = V2I32(tile_pos.x * SIM_TILES_PER_CHUNK_SQRT, tile_pos.y * SIM_TILES_PER_CHUNK_SQRT);
struct v2i32 tile_pos_in_chunk = V2I32(tile_pos.x % SIM_TILES_PER_CHUNK_SQRT, tile_pos.y % SIM_TILES_PER_CHUNK_SQRT);
struct sim_ent_id chunk_id = sim_ent_chunk_id_from_chunk_pos(player->id, chunk_pos);
struct sim_ent *chunk_ent = sim_ent_from_id(world, chunk_id);
if (!chunk_ent->valid) {
chunk_ent = sim_ent_alloc_sync_src_with_id(root, chunk_id);
sim_ent_enable_prop(chunk_ent, SEPROP_TILE_CHUNK);
}
struct string old_data = sim_ent_get_tile_chunk_data(chunk_ent);
struct string new_data = ZI;
new_data.len = SIM_TILES_PER_CHUNK_SQRT * SIM_TILES_PER_CHUNK_SQRT;
new_data.text = arena_push_array_no_zero(scratch.arena, u8, new_data.len);
if (old_data.len == new_data.len) {
MEMCPY(new_data.text, old_data.text, new_data.len);
} else {
MEMZERO(new_data.text, new_data.len);
}
u64 tile_index = tile_pos_in_chunk.x + (tile_pos_in_chunk.y * SIM_TILES_PER_CHUNK_SQRT);
new_data.text[tile_index] = SIM_TILE_KIND_TEST;
sim_ent_set_tile_chunk_data(chunk_ent, new_data);
}
#else
test_spawn_tile(world, player->player_cursor_pos);
#endif
} else if (old_control.flags & SIM_CONTROL_FLAG_TILE_TEST) {
test_generate_walls(world);
}
}
} break;

View File

@ -162,7 +162,7 @@ struct tar_archive tar_parse(struct arena *arena, struct string data, struct str
return archive;
}
struct tar_entry *tar_get(const struct tar_archive *archive, struct string name)
struct tar_entry *tar_get(struct tar_archive *archive, struct string name)
{
u64 hash = hash_fnv64(HASH_FNV64_BASIS, name);
return dict_get(&archive->lookup, hash);

View File

@ -18,6 +18,6 @@ struct tar_archive {
};
struct tar_archive tar_parse(struct arena *arena, struct string data, struct string prefix);
struct tar_entry *tar_get(const struct tar_archive *archive, struct string name);
struct tar_entry *tar_get(struct tar_archive *archive, struct string name);
#endif

View File

@ -35,9 +35,9 @@ INLINE u64 hash_fnv64(u64 seed, struct string s)
* ========================== */
/* Compare functions should
* return an int < 0 if a < b
* return an int = 0 if a == b
* return an int > 0 if a > b
* return a positive value if a should go before b
* return a negative value if a should go after b
* return 0 otherwise
*/
#define SORT_COMPARE_FUNC_DEF(name, arg_a, arg_b, arg_udata) i32 name(void *arg_a, void *arg_b, void *arg_udata)
typedef SORT_COMPARE_FUNC_DEF(sort_compare_func, a, b, udata);
@ -108,18 +108,23 @@ INLINE void merge_sort(void *items, u64 item_count, u64 item_size, sort_compare_
struct dict_entry {
u64 hash;
u64 value;
struct dict_entry *prev_in_bin;
struct dict_entry *next_in_bin;
struct dict_entry *prev;
struct dict_entry *next;
};
struct dict_bin {
struct dict_entry *first;
struct dict_entry *last;
};
struct dict {
u64 bins_count;
struct dict_bin *bins;
struct dict_entry *first_free;
struct dict_entry *first;
struct dict_entry *last;
};
INLINE struct dict dict_init(struct arena *arena, u64 bins_count)
@ -131,10 +136,9 @@ INLINE struct dict dict_init(struct arena *arena, u64 bins_count)
return dict;
}
INLINE void dict_set(struct arena *arena, struct dict *dict, u64 hash, u64 value)
INLINE struct dict_entry *dict_ensure_entry(struct arena *arena, struct dict *dict, u64 hash)
{
__prof;
struct dict_bin *bin = &dict->bins[hash % dict->bins_count];
struct dict_entry *entry = bin->first;
@ -148,19 +152,41 @@ INLINE void dict_set(struct arena *arena, struct dict *dict, u64 hash, u64 value
/* No match found, create new entry */
if (!entry) {
entry = arena_push(arena, struct dict_entry);
entry->value = value;
if (dict->first_free) {
entry = dict->first_free;
dict->first_free = entry->next;
} else {
entry = arena_push_no_zero(arena, struct dict_entry);
}
MEMZERO_STRUCT(entry);
entry->hash = hash;
entry->next_in_bin = bin->first;
entry->next = dict->first;
dict->first = entry;
bin->first = entry;
if (bin->last) {
bin->last->next_in_bin = entry;
entry->prev_in_bin = bin->last;
} else {
bin->first = entry;
}
bin->last = entry;
if (dict->last) {
dict->last->next = entry;
entry->prev = dict->last;
} else {
dict->first = entry;
}
dict->last = entry;
}
return entry;
}
INLINE void dict_set(struct arena *arena, struct dict *dict, u64 hash, u64 value)
{
__prof;
struct dict_entry *entry = dict_ensure_entry(arena, dict, hash);
entry->value = value;
}
INLINE struct dict_entry *dict_get_entry(const struct dict *dict, u64 hash)
INLINE struct dict_entry *dict_get_entry(struct dict *dict, u64 hash)
{
__prof;
struct dict_entry *result = NULL;
@ -175,13 +201,53 @@ INLINE struct dict_entry *dict_get_entry(const struct dict *dict, u64 hash)
return result;
}
INLINE u64 dict_get(const struct dict *dict, u64 hash)
INLINE u64 dict_get(struct dict *dict, u64 hash)
{
__prof;
struct dict_entry *entry = dict_get_entry(dict, hash);
return entry ? entry->value : 0;
}
INLINE void dict_remove_entry(struct dict *dict, struct dict_entry *entry)
{
/* Remove from bin */
{
struct dict_bin *bin = &dict->bins[entry->hash % dict->bins_count];
struct dict_entry *prev_in_bin = entry->prev_in_bin;
struct dict_entry *next_in_bin = entry->next_in_bin;
if (prev_in_bin) {
prev_in_bin->next_in_bin = next_in_bin;
} else {
bin->first = next_in_bin;
}
if (next_in_bin) {
next_in_bin->prev_in_bin = prev_in_bin;
} else {
bin->last = prev_in_bin;
}
}
/* Remove from list */
{
struct dict_entry *prev = entry->prev;
struct dict_entry *next = entry->next;
if (prev) {
prev->next = next;
} else {
dict->first = next;
}
if (next) {
next->prev = prev;
} else {
dict->last = prev;
}
}
/* Insert into free list */
{
entry->next = dict->first_free;
dict->first_free = entry;
}
}
/* ========================== *
* Sync flag
* ========================== */