registration.c
1 // SPDX-FileCopyrightText: 2023-2025 Le'Sec Core collective 2 // 3 // SPDX-License-Identifier: LGPL-3.0-or-later 4 5 #include <time.h> 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <stdarg.h> 9 #include <string.h> 10 #include <lscore/registration.h> 11 #include "hashmap.h" 12 #include "local.h" 13 14 // We use some compiler trickery to generic types, especially for function 15 // signatures, as the exact type of their arguments don't really matter much 16 // here. Types are sufficiently vague in the right places to shut the compiler 17 // up, and sufficiently exact in in the other right places to make the compiler 18 // happy. All we have to do is to ensure that the function pointers are picked 19 // from the correct table, where that matters. 20 LSC_DATA_SET_TYPES(void); 21 LSC_REGISTRATION_CALLBACK_TYPES(void); 22 23 typedef struct lsc_implementation_bag_data_st { 24 LSC_env_t *env; 25 char *class; 26 struct hashmap *table; 27 } lsc_implementation_bag_data_t; 28 29 typedef struct lsc_implementation_data_st { 30 const LSC_void_dispatch_fn *dispatch; 31 const LSC_void_destroy_fn *destroy; 32 const void *dispatch_data; 33 const char *docstring; 34 const LSC_plugin_t *plugin; 35 const lsc_implementation_bag_data_t *parent; 36 // The identity is the actual hashing input 37 const char *id; 38 } lsc_implementation_data_t; 39 40 static uint64_t hash_implementation_data(const void *item, 41 uint64_t seed0, uint64_t seed1) 42 { 43 const lsc_implementation_data_t *data = item; 44 45 return hashmap_xxhash3(data->id, strlen(data->id), seed0, seed1); 46 } 47 48 static int compare_implementation_data(const void *a, const void *b, 49 void *udata) 50 { 51 const lsc_implementation_data_t *adata = a; 52 const lsc_implementation_data_t *bdata = b; 53 54 return strcmp(adata->id, bdata->id); 55 } 56 57 static LE_STATUS 58 lsc_implementation_bag_dispatch(LSC_implementation_bag_t *t, int num, ...) 59 { 60 LE_STATUS status = LE_STS_ERROR; // TODO: Set more precise status 61 va_list ap; 62 63 va_start(ap, num); 64 switch (num) { 65 case LSC_NR_register_implementation: 66 case LSC_NR_deregister_implementation: 67 { 68 const LSC_plugin_t *plugin = va_arg(ap, const LSC_plugin_t *); 69 const char *id = va_arg(ap, const char *); 70 const LSC_void_dispatch_fn *dispatch 71 = va_arg(ap, const LSC_void_dispatch_fn *); 72 const LSC_void_destroy_fn *destroy 73 = va_arg(ap, const LSC_void_destroy_fn *); 74 const void *dispatch_data = va_arg(ap, const void *); 75 const char *docstring = va_arg(ap, const char *); 76 lsc_implementation_data_t item; 77 const lsc_implementation_data_t *pitem; 78 lsc_implementation_bag_data_t *t2 = t->lsc_data; 79 80 if (id == NULL || dispatch == NULL || destroy == NULL) 81 break; // TODO: Set more precise status 82 83 item.id = id; 84 item.plugin = plugin; 85 item.dispatch = dispatch; 86 item.destroy = destroy; 87 item.dispatch_data = dispatch_data; 88 item.docstring = docstring; 89 item.parent = t2; 90 91 switch (num) { 92 case LSC_NR_register_implementation: 93 { 94 if ((pitem = hashmap_get(t2->table, &item)) == NULL) { 95 if (hashmap_set(t2->table, &item) != NULL) 96 // What the hell, there was an item after all? TODO: MEMLEAK 97 break; // TODO: Set more precise status 98 } 99 } 100 status = LE_STS_SUCCESS; 101 break; 102 case LSC_NR_deregister_implementation: 103 hashmap_delete(t2->table, &item); 104 status = LE_STS_SUCCESS; 105 break; 106 } 107 } 108 break; 109 case LSC_NR_find_implementation: 110 { 111 const char *id = va_arg(ap, const char *); 112 const LSC_plugin_t **plugin = va_arg(ap, const LSC_plugin_t **); 113 const LSC_void_dispatch_fn **dispatch 114 = va_arg(ap, const LSC_void_dispatch_fn **); 115 const LSC_void_destroy_fn **destroy 116 = va_arg(ap, const LSC_void_destroy_fn **); 117 const void **dispatch_data = va_arg(ap, const void **); 118 const char **docstring = va_arg(ap, const char **); 119 lsc_implementation_data_t item; 120 const lsc_implementation_data_t *pitem; 121 lsc_implementation_bag_data_t *t2 = t->lsc_data; 122 123 item.id = id; 124 if ((pitem = hashmap_get(t2->table, &item)) != NULL) { 125 *dispatch = pitem->dispatch; 126 *destroy = pitem->destroy; 127 *dispatch_data = pitem->dispatch_data; 128 *docstring = pitem->docstring; 129 if (plugin != NULL) 130 *plugin = pitem->plugin; 131 status = LE_STS_SUCCESS; 132 } 133 } 134 break; 135 case LSC_NR_do_all_implementations: 136 { 137 LSC_do_all_void_implementations_callback_fn *fn 138 = va_arg(ap, LSC_do_all_void_implementations_callback_fn *); 139 void *user_arg = va_arg(ap, void *); 140 const lsc_implementation_data_t *pitem = NULL; 141 lsc_implementation_bag_data_t *t2 = t->lsc_data; 142 size_t cursor; 143 144 for (cursor = 0; hashmap_iter(t2->table, &cursor, (void **)&pitem);) 145 if (!LE_status_is_OK(status = fn(t2->class, pitem->id, 146 pitem->plugin, t2->env, 147 pitem->dispatch, pitem->destroy, 148 pitem->dispatch_data, 149 pitem->docstring, user_arg))) 150 break; 151 } 152 break; 153 } 154 155 va_end(ap); 156 return status; 157 } 158 159 static LE_STATUS 160 lsc_destroy_implementation_bag(LSC_implementation_bag_t *t) 161 { 162 if (t == NULL) 163 return LE_STS_SUCCESS; 164 if (t->lsc_data != NULL) { 165 lsc_implementation_bag_data_t *t2 = t->lsc_data; 166 hashmap_free(t2->table); 167 free(t2->class); 168 } 169 free(t); 170 return LE_STS_SUCCESS; 171 } 172 173 // This is called from the environment itself to get this bad to appear 174 LE_STATUS 175 lsc_get_environment_implementation_bag(LSC_env_t *env, const char *class, 176 LSC_implementation_bag_t **t) 177 { 178 LE_STATUS sts; 179 int seed = time(NULL); 180 181 sts = LSC_find_environment_bag(env, class, (LSC_env_bag_t **)t); 182 if (LE_status_is_warning(sts)) { 183 size_t table_data_size 184 = sizeof(**t) + sizeof(lsc_implementation_bag_data_t); 185 if ((*t = malloc(table_data_size)) == NULL) 186 return LE_STS_ERROR; 187 lsc_implementation_bag_data_t *t2 = (void *)*t + sizeof(**t); 188 t2->table = hashmap_new(sizeof(lsc_implementation_data_t), 0, 189 seed, seed, 190 hash_implementation_data, 191 compare_implementation_data, 192 NULL, NULL); 193 t2->class = strdup(class); 194 t2->env = env; 195 (*t)->lsc_dispatch = lsc_implementation_bag_dispatch; 196 (*t)->lsc_destroy = lsc_destroy_implementation_bag; 197 (*t)->lsc_data = t2; 198 if (t2->table == NULL || t2->class == NULL) 199 sts = LE_STS_ERROR; 200 else 201 sts = LSC_add_environment_bag(env, class, *(LSC_env_bag_t **)t); 202 if (!LE_status_is_OK(sts)) { 203 (*t)->lsc_destroy(*t); 204 *t = NULL; 205 return LE_STS_ERROR; 206 } 207 } 208 return LE_STS_SUCCESS; 209 } 210 211 #ifdef TEST_REGISTRATION 212 213 #undef LSC_NAME 214 #define LSC_NAME(x) TEST_##x 215 LSC_DATA_SET_TYPES(something); 216 LSC_REGISTRATION_FUNCTIONS("TEST::", something); 217 218 const char LSC_NAME(something_docs)[] = "Implementation of something"; 219 220 static LE_STATUS LSC_NAME(something_dispatch)(LSC_NAME(something_t) *data, int num, ...) {} 221 static LE_STATUS LSC_NAME(something_destroy)(LSC_NAME(something_t) *data) {} 222 static LE_STATUS LSC_NAME(something_print) 223 (const char *class, const char *id, 224 const LSC_plugin_t *plugin, const LSC_env_t *env, 225 LSC_NAME(something_dispatch_fn) *dispatch, 226 LSC_NAME(something_destroy_fn) *destroy, 227 const void *dispatch_data, 228 const char *docstring, 229 void *user_arg) 230 { 231 printf("FOUND %s : %s", class, id); 232 printf(" ["); 233 if (plugin) 234 printf("plugin: %p", (void *)plugin); 235 else 236 printf("built in", (void *)plugin); 237 if (env) 238 printf(", env: %p]", (void *)env); 239 else 240 printf(", env-independent]"); 241 printf("]\n"); 242 if (docstring) 243 printf("--BEGIN DOCS--\n%s\n--END DOCS--\n", docstring); 244 return LE_STS_SUCCESS; 245 } 246 #undef LSC_NAME 247 #define LSC_NAME(x) LSC_STD_NAME(x) 248 249 int main() 250 { 251 LSC_env_t *env = NULL; 252 TEST_something_dispatch_fn *dispatch; 253 TEST_something_destroy_fn *destroy; 254 const void *dispatch_data; 255 const char *docstring; 256 LE_STATUS sts; 257 int e = 0; 258 259 // Setup 260 if (!LE_status_is_OK(sts = LSC_new_environment(&env))) 261 e++; 262 263 // Add an implementation 264 sts = TEST_register_something_implementation(env, NULL, "something1", 265 TEST_something_dispatch, 266 TEST_something_destroy, 267 NULL, 268 TEST_something_docs); 269 if (!LE_status_is_OK(sts)) 270 e++; 271 272 // Find the same implementation, and check it 273 sts = TEST_find_something_implementation(env, "something1", NULL, 274 &dispatch, 275 &destroy, 276 &dispatch_data, 277 &docstring); 278 if (!LE_status_is_OK(sts) 279 || dispatch != TEST_something_dispatch 280 || destroy != TEST_something_destroy 281 || docstring != TEST_something_docs) 282 e++; 283 284 // Try printing 285 sts = TEST_do_all_something_implementations(env, TEST_something_print, 286 NULL); 287 if (!LE_status_is_OK(sts)) 288 e++; 289 290 // Cleanup 291 if (!LE_status_is_OK(sts = LSC_free_environment(env))) 292 e++; 293 294 exit(e); 295 } 296 297 #endif