#include "client.h" #include "host.h" #include "arena.h" #include "util.h" #define CHANNEL_LOOKUP_BUCKETS 4096 /* Offset in bytes from start of store struct to start of clients array (assume adjacently allocated) */ /* FIXME: Incorrect since buckets are also allocated */ #define STORE_CLIENTS_OFFSET (sizeof(struct client_store) + (sizeof(struct client_store) % alignof(struct client))) /* Accessed via client_nil() */ READONLY struct client _g_client_nil = { .valid = false }; READONLY struct client_store _g_client_store_nil = { .valid = false }; /* ========================== * * Store * ========================== */ struct client_store *client_store_alloc(void) { struct arena arena = arena_alloc(GIGABYTE(64)); struct client_store *store = arena_push_zero(&arena, struct client_store); store->arena = arena; store->num_channel_lookup_buckets = CHANNEL_LOOKUP_BUCKETS; store->channel_lookup_buckets = arena_push_array_zero(&arena, struct channel_lookup_bucket, store->num_channel_lookup_buckets); store->clients = arena_dry_push(&arena, struct client); return store; } void client_store_release(struct client_store *store) { arena_release(&store->arena); } struct client_store *client_store_from_client(struct client *client) { if (client->valid) { u64 first_client_addr = (u64)(client - client->handle.idx); struct client_store *client_store = (struct client_store *)(first_client_addr - STORE_CLIENTS_OFFSET); ASSERT(client_store->clients == (struct 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 client *client_from_handle(struct client_store *store, struct client_handle handle) { if (handle.gen != 0 && handle.idx < store->clients_reserved) { struct client *client = &store->clients[handle.idx]; if (client->handle.gen == handle.gen) { return client; } } return client_nil(); } struct client *client_from_channel_id(struct client_store *store, struct host_channel_id channel_id) { struct client *res = client_nil(); u64 channel_hash = hash_from_channel_id(channel_id); u64 bucket_index = channel_hash % store->num_channel_lookup_buckets; struct channel_lookup_bucket *bucket = &store->channel_lookup_buckets[bucket_index]; for (struct client *client = bucket->first; client; client = client->next_hash) { if (client->channel_hash == channel_hash) { res = client; break; } } return res; } struct client *client_alloc(struct client_store *store, struct host_channel_id channel_id) { struct client *client = NULL; struct 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 client); handle.gen = 1; handle.idx = store->clients_reserved; ++store->clients_reserved; } *client = _g_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 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 client_release(struct client *client) { struct 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; /* Remove from channel lookup */ u64 bucket_index = client->channel_hash % store->num_channel_lookup_buckets; struct channel_lookup_bucket *bucket = &store->channel_lookup_buckets[bucket_index]; struct client *prev = client->prev_hash; struct 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; } }