power_play/src/sim_client.c
2025-02-09 15:22:43 -06:00

156 lines
5.2 KiB
C

#include "sim_client.h"
#include "host.h"
#include "arena.h"
#include "util.h"
#include "arena.h"
#define CHANNEL_LOOKUP_BUCKETS 4096
/* Offset in bytes from start of store struct to start of clients array (assume adjacently allocated) */
#define STORE_CLIENTS_OFFSET (sizeof(struct sim_client_store) + (sizeof(struct sim_client_store) % alignof(struct sim_client)))
/* Accessed via client_nil() */
READONLY struct sim_client _g_sim_client_nil = { .valid = false };
READONLY struct sim_client_store _g_sim_client_store_nil = { .valid = false };
struct sim_client_channel_lookup_bucket {
struct sim_client *first;
struct sim_client *last;
};
/* ========================== *
* Store
* ========================== */
struct sim_client_store *sim_client_store_alloc(u64 arena_reserve)
{
struct arena arena = arena_alloc(arena_reserve);
u64 num_channel_lookup_buckets = CHANNEL_LOOKUP_BUCKETS;
struct sim_client_channel_lookup_bucket *channel_lookup_buckets = arena_push_array_zero(&arena, struct sim_client_channel_lookup_bucket, num_channel_lookup_buckets);
struct sim_client_store *store = arena_push_zero(&arena, struct sim_client_store);
store->clients = arena_dry_push(&arena, struct sim_client);
ASSERT((u8 *)store->clients - (u8 *)store == STORE_CLIENTS_OFFSET); /* Offset must be correct */
store->arena = arena;
store->num_channel_lookup_buckets = num_channel_lookup_buckets;
store->channel_lookup_buckets = channel_lookup_buckets;
return store;
}
void sim_client_store_release(struct sim_client_store *store)
{
arena_release(&store->arena);
}
struct sim_client_store *client_store_from_client(struct sim_client *client)
{
if (client->valid) {
u64 first_client_addr = (u64)(client - client->handle.idx);
struct sim_client_store *client_store = (struct sim_client_store *)(first_client_addr - STORE_CLIENTS_OFFSET);
ASSERT(client_store->clients == (struct sim_client *)first_client_addr);
return client_store;
} else {
return client_store_nil();
}
}
/* ========================== *
* Client
* ========================== */
INTERNAL u64 hash_from_channel_id(struct host_channel_id channel_id)
{
return hash_fnv64(HASH_FNV64_BASIS, STRING_FROM_STRUCT(&channel_id));
}
struct sim_client *sim_client_from_handle(struct sim_client_store *store, struct sim_client_handle handle)
{
if (handle.gen != 0 && handle.idx < store->num_reserved) {
struct sim_client *client = &store->clients[handle.idx];
if (client->handle.gen == handle.gen) {
return client;
}
}
return client_nil();
}
struct sim_client *sim_client_from_channel_id(struct sim_client_store *store, struct host_channel_id channel_id)
{
struct sim_client *res = client_nil();
u64 channel_hash = hash_from_channel_id(channel_id);
u64 bucket_index = channel_hash % store->num_channel_lookup_buckets;
struct sim_client_channel_lookup_bucket *bucket = &store->channel_lookup_buckets[bucket_index];
for (struct sim_client *client = bucket->first; client; client = client->next_hash) {
if (client->channel_hash == channel_hash) {
res = client;
break;
}
}
return res;
}
struct sim_client *sim_client_alloc(struct sim_client_store *store, struct host_channel_id channel_id)
{
struct sim_client *client = NULL;
struct sim_client_handle handle = ZI;
if (store->first_free_client) {
client = store->first_free_client;
store->first_free_client = client->next_free;
handle = client->handle;
++handle.gen;
} else {
client = arena_push(&store->arena, struct sim_client);
handle.gen = 1;
handle.idx = store->num_reserved;
++store->num_reserved;
}
++store->num_allocated;
*client = _g_sim_client_nil;
client->valid = true;
client->handle = handle;
u64 channel_hash = hash_from_channel_id(channel_id);
client->channel_id = channel_id;
client->channel_hash = channel_hash;
/* Insert into channel lookup */
u64 bucket_index = channel_hash % store->num_channel_lookup_buckets;
struct sim_client_channel_lookup_bucket *bucket = &store->channel_lookup_buckets[bucket_index];
if (bucket->last) {
bucket->last->next_hash = client;
client->prev_hash = bucket->last;
} else {
bucket->first = client;
}
bucket->last = client;
return client;
}
void sim_client_release(struct sim_client *client)
{
struct sim_client_store *store = client_store_from_client(client);
client->valid = false;
++client->handle.gen;
client->next_free = store->first_free_client;
store->first_free_client = client;
--store->num_allocated;
/* Remove from channel lookup */
u64 bucket_index = client->channel_hash % store->num_channel_lookup_buckets;
struct sim_client_channel_lookup_bucket *bucket = &store->channel_lookup_buckets[bucket_index];
struct sim_client *prev = client->prev_hash;
struct sim_client *next = client->next_hash;
if (prev) {
prev->next_hash = next;
} else {
bucket->first = next;
}
if (next) {
next->prev_hash = prev;
} else {
bucket->last = prev;
}
}