/ src / core / environment.c
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