environment.c
1 // SPDX-FileCopyrightText: 2023-2024 Le'Sec Core collective 2 // 3 // SPDX-License-Identifier: LGPL-3.0-or-later 4 5 #include <time.h> 6 #include <stdlib.h> 7 #include <stdarg.h> 8 #include <string.h> 9 #include <lscore/environment.h> 10 #include <lscore/index.h> 11 #include <lscore/types.h> 12 #include "hashmap.h" 13 #include "local.h" 14 15 struct lsc_env_hash_item_t { 16 char *tag; 17 LSC_env_bag_t *bag; 18 }; 19 20 static uint64_t hash_env_item(const void *item, uint64_t seed0, uint64_t seed1) 21 { 22 const char *tag = ((const struct lsc_env_hash_item_t *)item)->tag; 23 24 return hashmap_xxhash3(tag, strlen(tag), seed0, seed1); 25 } 26 27 static int compare_env_item(const void *a, const void *b, void *udata) 28 { 29 const char *atag = ((const struct lsc_env_hash_item_t *)a)->tag; 30 const char *btag = ((const struct lsc_env_hash_item_t *)b)->tag; 31 32 return strcmp(atag, btag); 33 } 34 35 static void free_env_item(void *item) 36 { 37 LSC_env_bag_t *bag = ((struct lsc_env_hash_item_t *)item)->bag; 38 39 if (bag->lsc_destroy != NULL) 40 bag->lsc_destroy(bag); 41 } 42 43 static LE_STATUS lsc_env_dispatch(LSC_env_t *env, int num, ...) 44 { 45 LE_STATUS status = LE_STS_ERROR; // TODO: Set more precise status 46 va_list ap; 47 48 va_start(ap, num); 49 switch (num) { 50 case LSC_NR_add_environment_bag: 51 { 52 const char *tag = va_arg(ap, const char *); 53 LSC_env_bag_t *bag = va_arg(ap, LSC_env_bag_t *); 54 struct lsc_env_hash_item_t item; 55 56 if (tag == NULL || bag == NULL) 57 break; // TODO: Set more precise status 58 59 item.tag = strdup(tag); 60 item.bag = bag; 61 if (hashmap_get(env->lsc_data, &item) != NULL) { 62 status = LE_STS_WARNING; 63 break; // TODO: Set more precise status 64 } 65 if (hashmap_set(env->lsc_data, &item) != NULL) 66 // What the hell, there was an item after all? TODO: MEMLEAK 67 break; // TODO: Set more precise status 68 } 69 status = LE_STS_SUCCESS; 70 break; 71 case LSC_NR_find_environment_bag: 72 case LSC_NR_remove_environment_bag: 73 { 74 const char *tag = va_arg(ap, const char *); 75 LSC_env_bag_t **bag = va_arg(ap, LSC_env_bag_t **); // Result 76 struct lsc_env_hash_item_t item; 77 const struct lsc_env_hash_item_t *pitem; 78 79 va_end(ap); 80 81 if (tag == NULL || bag == NULL) 82 break; // TODO: Set more precise status 83 84 // Here, we use the tag as a constant 85 item.tag = (char *)tag; 86 switch (num) { 87 case LSC_NR_find_environment_bag: 88 pitem = hashmap_get(env->lsc_data, &item); 89 break; 90 case LSC_NR_remove_environment_bag: 91 pitem = hashmap_delete(env->lsc_data, &item); 92 if (pitem != NULL) 93 free(pitem->tag); 94 break; 95 } 96 if (pitem == NULL) { 97 status = LE_STS_WARNING; 98 break; // TODO: Set more precise status 99 } 100 101 *bag = pitem->bag; 102 } 103 status = LE_STS_SUCCESS; 104 break; 105 case LSC_NR_get_index_bag: 106 { 107 LSC_index_bag_t **t = va_arg(ap, LSC_index_bag_t **); 108 109 status = lsc_get_environment_index_bag(env, t); 110 } 111 break; 112 case LSC_NR_get_implementation_bag: 113 { 114 const char *class = va_arg(ap, const char *); 115 LSC_implementation_bag_t **t 116 = va_arg(ap, LSC_implementation_bag_t **); 117 118 status = lsc_get_environment_implementation_bag(env, class, t); 119 } 120 break; 121 } 122 123 va_end(ap); 124 return status; 125 } 126 127 static LE_STATUS lsc_destroy_env(LSC_env_t *env) 128 { 129 if (env == NULL) 130 return LE_STS_SUCCESS; 131 if (env->lsc_data != NULL) 132 hashmap_free(env->lsc_data); 133 free(env); 134 return LE_STS_SUCCESS; 135 } 136 137 LE_STATUS LSC_new_environment(LSC_env_t **env) 138 { 139 int seed = time(NULL); 140 141 if ((*env = malloc(sizeof(**env))) == NULL) 142 return LE_STS_ERROR; 143 (*env)->lsc_dispatch = lsc_env_dispatch; 144 (*env)->lsc_destroy = lsc_destroy_env; 145 (*env)->lsc_data = hashmap_new(sizeof(struct lsc_env_hash_item_t), 0, 146 seed, seed, 147 hash_env_item, compare_env_item, free_env_item, 148 NULL); 149 LE_STATUS sts = LE_STS_ERROR; 150 if ((*env)->lsc_data == NULL) { 151 (*env)->lsc_destroy(*env); 152 *env = NULL; 153 return sts; 154 } 155 return LE_STS_SUCCESS; 156 } 157 158 #ifdef TEST_ENVIRONMENT 159 // There is hell to pay if you compete with the data_set numbers 160 # define TEST_NR_foo (0 + LSC_NR__data_set_class_start) 161 # define TEST_NR_bar (1 + LSC_NR__data_set_class_start) 162 163 static LE_STATUS test_dispatch(LSC_env_bag_t *b, int num, ...) 164 { 165 LE_STATUS status = LE_STS_FATAL_ERROR; 166 va_list ap; 167 168 va_start(ap, num); 169 switch (num) { 170 case TEST_NR_foo: 171 { 172 const char **result = va_arg(ap, const char **); 173 174 b->lsc_data = "foo"; 175 *result = "cookie"; 176 } 177 status = LE_STS_SUCCESS; 178 break; 179 case TEST_NR_bar: 180 b->lsc_data = "bar"; 181 status = LE_STS_ERROR; 182 break; 183 } 184 va_end(ap); 185 186 return status; 187 } 188 189 static LE_STATUS test_destroy(LSC_env_bag_t *b) 190 { 191 b->lsc_data = "EXTERMINATE"; 192 return LE_STS_SUCCESS; 193 } 194 195 int main(int argc, char *argv[]) 196 { 197 LSC_env_t *env = NULL; 198 LE_STATUS sts; 199 LSC_env_bag_t bag1, bag2, bag3; 200 LSC_env_bag_t *bag; 201 int e = 0; 202 const char *s; 203 204 bag1.lsc_dispatch = bag2.lsc_dispatch = bag3.lsc_dispatch = test_dispatch; 205 bag1.lsc_destroy = bag2.lsc_destroy = bag3.lsc_destroy = test_destroy; 206 bag1.lsc_dispatch_data = bag2.lsc_dispatch_data = bag3.lsc_dispatch_data = NULL; 207 bag1.lsc_data = bag2.lsc_data = bag3.lsc_data = NULL; 208 209 // Setup 210 if (!LE_status_is_OK(sts = LSC_new_environment(&env)) 211 || !LE_status_is_warning(sts = LSC_find_environment_bag(env, "1", &bag)) 212 || !LE_status_is_warning(sts = LSC_find_environment_bag(env, "2", &bag)) 213 || !LE_status_is_OK(sts = LSC_add_environment_bag(env, "1", &bag1)) 214 || !LE_status_is_OK(sts = LSC_add_environment_bag(env, "2", &bag2)) 215 || !LE_status_is_warning(sts = LSC_add_environment_bag(env, "1", &bag1)) 216 || !LE_status_is_error(sts = LSC_add_environment_bag(env, "3", NULL)) 217 || !LE_status_is_error(sts = LSC_add_environment_bag(env, NULL, &bag3)) 218 || !LE_status_is_error(sts = LSC_add_environment_bag(env, NULL, NULL))) 219 e++; 220 221 // Find the first bag and play with it 222 if (!LE_status_is_OK(sts = LSC_find_environment_bag(env, "1", &bag)) 223 || bag != &bag1 224 // First dispatch command returns LE_STS_SUCCESS 225 || !LE_status_is_OK(sts = bag->lsc_dispatch(bag, TEST_NR_foo, &s)) 226 || s == NULL 227 || strcmp(s, "cookie") != 0 228 || bag1.lsc_data == NULL 229 || strcmp(bag1.lsc_data, "foo") != 0 230 // Second dispatch command returns LE_STS_ERROR 231 || !LE_status_is_error(sts = bag->lsc_dispatch(bag, TEST_NR_bar, &s)) 232 || s == NULL // From the first dispatch above, should remain untouched 233 || bag1.lsc_data == NULL 234 || strcmp(bag1.lsc_data, "bar") != 0 235 // Unknown dispatch command returns LR_STS_FATAL_ERROR 236 || !LE_status_is_fatal(sts = bag->lsc_dispatch(bag, 666, &s)) 237 // Check that the second bag remained untouched 238 || bag2.lsc_data != NULL) 239 e++; 240 241 // Cleanup 242 if (!LE_status_is_OK(sts = LSC_remove_environment_bag(env, "2", &bag)) 243 || !LE_status_is_warning(sts = LSC_remove_environment_bag(env, "2", &bag)) 244 || !LE_status_is_OK(sts = LSC_free_environment(env)) 245 // Check that the first bag got properly destroyed 246 || bag1.lsc_data == NULL 247 || strcmp(bag1.lsc_data, "EXTERMINATE") != 0 248 // Check that the second and third bag remained untouched 249 || bag2.lsc_data != NULL 250 || bag3.lsc_data != NULL) 251 e++; 252 253 exit(e); 254 } 255 #endif