mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-04-20 22:35:41 +00:00
hashtable: Redesign the hashtable API.
This was intended to make the API public, so SDL_hashtable.h got an extreme documentation makeover, but for now this remains a private header. This makes several significant interface changes to SDL_HashTable, and improves code that makes use of it in various ways. - The ability to make "stackable" tables is removed. Apparently this still worked with the current implementation, but I could see a future implementation struggle mightily to support this. It'll be better for something external to build on top of the table if it needs it, inserting a linked list of stacked items as the hash values and managing them separately. There was only one place in SDL using this, unnecessarily, and that has also been cleaned up to not need it. - You no longer specify "buckets" when creating a table, but rather an estimated number of items the table is meant to hold. The bucket count was crucial to our classic hashtable implementation, but meant less once we moved to an Open Addressing implementation anyhow, since the bucket count isn't static (and they aren't really "buckets" anymore either). Now you can just report how many items you think the hash will hold and SDL will allocate a reasonable default for you...or 0 to not guess, and SDL will start small and grow as necessary, which is often the correct thing to do. - There's no more SDL_IterateHashTableKey because there's no more "stackable" hash tables. - SDL_IterateHashTable() now uses a callback, which matches other parts of SDL, and also lets us hold the read-lock for the entire iteration and get rid of the goofy iterator state variable. - SDL_InsertIntoHashTable() now lets you specify whether to replace existing keys or fail if the key already exists. - Callbacks now use SDL conventions (userdata as the first param). - Other naming convention fixes. I discovered we use a lot of hash tables in SDL3 internally. :) So the bulk of this work is fixing up that code to use the new interfaces, and simplifying things (like checking for an item to remove it if it already exists before inserting a replacement...just do the insert atomically, it'll do all that for you!).
This commit is contained in:
@@ -282,21 +282,6 @@ static void ClosePhysicalCamera(SDL_Camera *device)
|
||||
device->adjust_timestamp = 0;
|
||||
}
|
||||
|
||||
// this must not be called while `device` is still in a device list, or while a device's camera thread is still running.
|
||||
static void DestroyPhysicalCamera(SDL_Camera *device)
|
||||
{
|
||||
if (device) {
|
||||
// Destroy any logical devices that still exist...
|
||||
ClosePhysicalCamera(device);
|
||||
camera_driver.impl.FreeDeviceHandle(device);
|
||||
SDL_DestroyMutex(device->lock);
|
||||
SDL_free(device->all_specs);
|
||||
SDL_free(device->name);
|
||||
SDL_free(device);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Don't hold the device lock when calling this, as we may destroy the device!
|
||||
void UnrefPhysicalCamera(SDL_Camera *device)
|
||||
{
|
||||
@@ -307,7 +292,6 @@ void UnrefPhysicalCamera(SDL_Camera *device)
|
||||
SDL_AddAtomicInt(&camera_driver.device_count, -1);
|
||||
}
|
||||
SDL_UnlockRWLock(camera_driver.device_hash_lock);
|
||||
DestroyPhysicalCamera(device); // ...and nuke it.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,7 +484,7 @@ SDL_Camera *SDL_AddCamera(const char *name, SDL_CameraPosition position, int num
|
||||
RefPhysicalCamera(device);
|
||||
|
||||
SDL_LockRWLockForWriting(camera_driver.device_hash_lock);
|
||||
if (SDL_InsertIntoHashTable(camera_driver.device_hash, (const void *) (uintptr_t) device->instance_id, device)) {
|
||||
if (SDL_InsertIntoHashTable(camera_driver.device_hash, (const void *) (uintptr_t) device->instance_id, device, false)) {
|
||||
SDL_AddAtomicInt(&camera_driver.device_count, 1);
|
||||
} else {
|
||||
SDL_DestroyMutex(device->lock);
|
||||
@@ -624,7 +608,25 @@ void SDL_CameraPermissionOutcome(SDL_Camera *device, bool approved)
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct FindOnePhysicalCameraByCallbackData
|
||||
{
|
||||
bool (*callback)(SDL_Camera *device, void *userdata);
|
||||
void *userdata;
|
||||
SDL_Camera *device;
|
||||
} FindOnePhysicalCameraByCallbackData;
|
||||
|
||||
static bool SDLCALL FindOnePhysicalCameraByCallback(void *userdata, const SDL_HashTable *table, const void *key, const void *value)
|
||||
{
|
||||
FindOnePhysicalCameraByCallbackData *data = (FindOnePhysicalCameraByCallbackData *) userdata;
|
||||
SDL_Camera *device = (SDL_Camera *) value;
|
||||
if (data->callback(device, data->userdata)) {
|
||||
data->device = device;
|
||||
return false; // stop iterating.
|
||||
}
|
||||
return true; // keep iterating.
|
||||
}
|
||||
|
||||
// !!! FIXME: this doesn't follow SDL convention of `userdata` being the first param of the callback.
|
||||
SDL_Camera *SDL_FindPhysicalCameraByCallback(bool (*callback)(SDL_Camera *device, void *userdata), void *userdata)
|
||||
{
|
||||
if (!SDL_GetCurrentCameraDriver()) {
|
||||
@@ -632,23 +634,17 @@ SDL_Camera *SDL_FindPhysicalCameraByCallback(bool (*callback)(SDL_Camera *device
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const void *key;
|
||||
const void *value;
|
||||
void *iter = NULL;
|
||||
|
||||
FindOnePhysicalCameraByCallbackData data = { callback, userdata, NULL };
|
||||
SDL_LockRWLockForReading(camera_driver.device_hash_lock);
|
||||
while (SDL_IterateHashTable(camera_driver.device_hash, &key, &value, &iter)) {
|
||||
SDL_Camera *device = (SDL_Camera *) value;
|
||||
if (callback(device, userdata)) { // found it?
|
||||
SDL_UnlockRWLock(camera_driver.device_hash_lock);
|
||||
return device;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_IterateHashTable(camera_driver.device_hash, FindOnePhysicalCameraByCallback, &data);
|
||||
SDL_UnlockRWLock(camera_driver.device_hash_lock);
|
||||
|
||||
SDL_SetError("Device not found");
|
||||
return NULL;
|
||||
if (!data.device) {
|
||||
SDL_SetError("Device not found");
|
||||
}
|
||||
|
||||
return data.device;
|
||||
}
|
||||
|
||||
void SDL_CloseCamera(SDL_Camera *camera)
|
||||
@@ -704,6 +700,19 @@ SDL_CameraPosition SDL_GetCameraPosition(SDL_CameraID instance_id)
|
||||
}
|
||||
|
||||
|
||||
typedef struct GetOneCameraData
|
||||
{
|
||||
SDL_CameraID *result;
|
||||
int devs_seen;
|
||||
} GetOneCameraData;
|
||||
|
||||
static bool SDLCALL GetOneCamera(void *userdata, const SDL_HashTable *table, const void *key, const void *value)
|
||||
{
|
||||
GetOneCameraData *data = (GetOneCameraData *) userdata;
|
||||
data->result[data->devs_seen++] = (SDL_CameraID) (uintptr_t) key;
|
||||
return true; // keep iterating.
|
||||
}
|
||||
|
||||
SDL_CameraID *SDL_GetCameras(int *count)
|
||||
{
|
||||
int dummy_count;
|
||||
@@ -725,16 +734,10 @@ SDL_CameraID *SDL_GetCameras(int *count)
|
||||
if (!result) {
|
||||
num_devices = 0;
|
||||
} else {
|
||||
int devs_seen = 0;
|
||||
const void *key;
|
||||
const void *value;
|
||||
void *iter = NULL;
|
||||
while (SDL_IterateHashTable(camera_driver.device_hash, &key, &value, &iter)) {
|
||||
result[devs_seen++] = (SDL_CameraID) (uintptr_t) key;
|
||||
}
|
||||
|
||||
SDL_assert(devs_seen == num_devices);
|
||||
result[devs_seen] = 0; // null-terminated.
|
||||
GetOneCameraData data = { result, 0 };
|
||||
SDL_IterateHashTable(camera_driver.device_hash, GetOneCamera, &data);
|
||||
SDL_assert(data.devs_seen == num_devices);
|
||||
result[num_devices] = 0; // null-terminated.
|
||||
}
|
||||
SDL_UnlockRWLock(camera_driver.device_hash_lock);
|
||||
|
||||
@@ -1381,37 +1384,26 @@ void SDL_QuitCamera(void)
|
||||
SDL_free(i);
|
||||
}
|
||||
|
||||
const void *key;
|
||||
const void *value;
|
||||
void *iter = NULL;
|
||||
while (SDL_IterateHashTable(device_hash, &key, &value, &iter)) {
|
||||
DestroyPhysicalCamera((SDL_Camera *) value);
|
||||
}
|
||||
SDL_DestroyHashTable(device_hash);
|
||||
|
||||
// Free the driver data
|
||||
camera_driver.impl.Deinitialize();
|
||||
|
||||
SDL_DestroyRWLock(camera_driver.device_hash_lock);
|
||||
SDL_DestroyHashTable(device_hash);
|
||||
|
||||
SDL_zero(camera_driver);
|
||||
}
|
||||
|
||||
|
||||
static Uint32 HashCameraID(const void *key, void *data)
|
||||
// Physical camera objects are only destroyed when removed from the device hash.
|
||||
static void SDLCALL DestroyCameraHashItem(void *userdata, const void *key, const void *value)
|
||||
{
|
||||
// The values are unique incrementing integers, starting at 1, so just return minus 1 to start with bucket zero.
|
||||
return ((Uint32) ((uintptr_t) key)) - 1;
|
||||
}
|
||||
|
||||
static bool MatchCameraID(const void *a, const void *b, void *data)
|
||||
{
|
||||
return (a == b); // simple integers, just compare them as pointer values.
|
||||
}
|
||||
|
||||
static void NukeCameraHashItem(const void *key, const void *value, void *data)
|
||||
{
|
||||
// no-op, keys and values in this hashtable are treated as Plain Old Data and don't get freed here.
|
||||
SDL_Camera *device = (SDL_Camera *) userdata;
|
||||
ClosePhysicalCamera(device);
|
||||
camera_driver.impl.FreeDeviceHandle(device);
|
||||
SDL_DestroyMutex(device->lock);
|
||||
SDL_free(device->all_specs);
|
||||
SDL_free(device->name);
|
||||
SDL_free(device);
|
||||
}
|
||||
|
||||
bool SDL_CameraInit(const char *driver_name)
|
||||
@@ -1425,7 +1417,7 @@ bool SDL_CameraInit(const char *driver_name)
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_HashTable *device_hash = SDL_CreateHashTable(NULL, 8, HashCameraID, MatchCameraID, NukeCameraHashItem, false, false);
|
||||
SDL_HashTable *device_hash = SDL_CreateHashTable(0, false, SDL_HashID, SDL_KeyMatchID, DestroyCameraHashItem, NULL);
|
||||
if (!device_hash) {
|
||||
SDL_DestroyRWLock(device_hash_lock);
|
||||
return false;
|
||||
|
||||
@@ -190,7 +190,7 @@ typedef struct SDL_CameraDriver
|
||||
const char *desc; // The description of this camera driver
|
||||
SDL_CameraDriverImpl impl; // the backend's interface
|
||||
|
||||
SDL_RWLock *device_hash_lock; // A rwlock that protects `device_hash`
|
||||
SDL_RWLock *device_hash_lock; // A rwlock that protects `device_hash` // !!! FIXME: device_hash _also_ has a rwlock, see if we still need this one.
|
||||
SDL_HashTable *device_hash; // the collection of currently-available camera devices
|
||||
SDL_PendingCameraEvent pending_events;
|
||||
SDL_PendingCameraEvent *pending_events_tail;
|
||||
|
||||
Reference in New Issue
Block a user