From a47009f16b5256edf28c063b1bb720bd034212a7 Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 21 May 2025 21:17:04 -0500 Subject: [PATCH] cross-chunk tile wall merging --- src/config.h | 2 +- src/phys.c | 13 ++- src/sim_step.c | 297 ++++++++++++++++++++++++------------------------- src/tar.c | 2 +- src/tar.h | 2 +- src/util.h | 92 ++++++++++++--- 6 files changed, 239 insertions(+), 169 deletions(-) diff --git a/src/config.h b/src/config.h index 56d5c1f0..e3bee223 100644 --- a/src/config.h +++ b/src/config.h @@ -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 diff --git a/src/phys.c b/src/phys.c index 8c2d9fee..37bdb0e2 100644 --- a/src/phys.c +++ b/src/phys.c @@ -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); diff --git a/src/sim_step.c b/src/sim_step.c index 58a85264..2e6a241b 100644 --- a/src/sim_step.c +++ b/src/sim_step.c @@ -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; diff --git a/src/tar.c b/src/tar.c index 22eda27c..8b296d80 100644 --- a/src/tar.c +++ b/src/tar.c @@ -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); diff --git a/src/tar.h b/src/tar.h index 2ee9d1a4..b630d3ad 100644 --- a/src/tar.h +++ b/src/tar.h @@ -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 diff --git a/src/util.h b/src/util.h index fce556dc..7a96b6ae 100644 --- a/src/util.h +++ b/src/util.h @@ -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 * ========================== */