#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; } }