diff --git a/libcob/ChangeLog b/libcob/ChangeLog index 880927535..dac0a0c8e 100644 --- a/libcob/ChangeLog +++ b/libcob/ChangeLog @@ -1,3 +1,7 @@ +2024-08-08 Remi Bertrand-Hardy + + * added Object.c Object.h hash_table.c and hash_table.h to + provide necessary functions to implement objects in GnuCOBOL. 2024-07-29 Chuck Haatvedt diff --git a/libcob/Object.c b/libcob/Object.c new file mode 100755 index 000000000..f84a5ece5 --- /dev/null +++ b/libcob/Object.c @@ -0,0 +1,113 @@ + +#include +#include +#include +#include +#include +#include "Object.h" +#include "hash_table.h" + +/* Un type pour toutes les fonctions qui définissent des méthodes d'un objet. + Ici, c'est un pointeur directement vers le code de la fonction, mais + on pourrait imaginer passer par un "struct" qui contiendrait plus + d'informations */ + +/* Field structure */ + +/*Un type pour définir les variables cobol, éventuellement à étendre pour fournir plus d'informations*/ + +/* Interface de création de la classe */ + +struct cobol_class *cb_class_new(const char *name) +{ + struct cobol_class *new_class; + new_class = malloc(sizeof *new_class * 1); + new_class->tmv = ht_new(); + /* alloue et initialise une classe vide */ +} + +static ht_item HT_DELETED_ITEM = {NULL, NULL}; + +void cb_class_inherit(struct cobol_class *myclass, + struct cobol_class *ancester) +{ + for (int i = 0; i < ancester->tmv->size; i++) + { + ht_item *item = ancester->tmv->items[i]; + if (item != NULL && item != &HT_DELETED_ITEM) + { + ht_insert(myclass->tmv, item->key, item->value); + } + } + /* */ /* ajoute la class [ancester] comme un ancêtre de la + classe [myclass] */ +} + +void cb_class_new_method(struct cobol_class *myclass, + const char *method_name, + cobol_method *method_code) +{ + ht_insert(myclass->tmv, method_name, method_code); + /* */ /* ajoute la méthode [method_name] à la classe [myclass] + pointant sur le code [method_code]. On peut convenir + que si [method_name] est NULL, c'est le constructeur. Ici, + je suppose qu'il n'y a pas d'overloading en fonction + des arguments ? S'il est possible de faire des méthodes + virtuelles, alors [method_code] peut être NULL dans ce cas. */ +} + +void cb_class_new_field(struct cobol_class *myclass, + const char *field_name, + int size) +{ + myclass->ft_size++; + /* */ /* ajoute une variable d'instance [field_name] + dans la class [myclass] avec une taille [size]. + A noter qu'on peut remplacer [int size] par une information + de type plus détaillée, comme celle utilisée par GnuCOBOL. + Dans ce cas, la fonction [cb_object_field] plus loin + pourrait retourner une structure plutôt que juste un + pointeur vers le champ. + */ +} + +int cb_interface_conform(cob_interface *my_interface, cob_class *my_class) +{ + for (int i = 0; i < my_interface->size; i++){ + if(ht_search(my_class->tmv,my_interface->method_list[i]) != NULL){ + return 1; + } + } + return 0; +} + +/* Interface de création des objets et accès */ + +struct cobol_object *cb_object_new(struct cobol_class *myclass, + cob_field *args[]) + +{ + struct cobol_object *new_object = (struct cobol_object *) + malloc(sizeof(struct cobol_object) + myclass->ft_size * sizeof(struct _cob_field)); + new_object->my_class = myclass; + /* allocation d'un objet de la class [myclass] */ +} + +cobol_method cb_object_method(struct cobol_object *self, + const char *method_name) +{ + meth_table *tmv = self->my_class->tmv; + return ht_search(tmv, method_name); + + /* retourne le pointeur sur la méthode [method_name] dans + l'objet [self]. Si le pointeur retourné n'est pas NULL, + il peut être utilisé pour appeler la méthode. */ +} + +char *cb_object_field(struct cobol_object *self, + const char *field_name) + +{ + /*les champs étant propre à chaque classe, l'accès + à ces champs se fera directement à la compilation*/ +} diff --git a/libcob/Object.h b/libcob/Object.h new file mode 100755 index 000000000..b2b13ca7e --- /dev/null +++ b/libcob/Object.h @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include "hash_table.h" +#ifndef OOMODULE +#define OOMODULE + +typedef struct _cob_field { + char* data; + /*A completer avec les éléments nécessaires pour caractériser une variable COBOL*/ +} cob_field; + +typedef int (*cobol_method)( + struct cobol_object *self, + char *args[]); + +typedef struct _interface{ + const char name; + char **method_list; + int size; +}cob_interface; + + +typedef struct _meth_table +{ + struct _meth_table *next; + const char *name; + cobol_method call_pointer; +} meth_table; + +typedef struct _field_table +{ + struct _field_table *next; + const char *name; + int index; +} field_table; + +typedef struct cobol_object +{ + //struct cobol_class *my_class; + struct cobol_class *my_class; + cob_field (*data)[]; + /* la taille est 1 dans la définition de type, mais + on fera un malloc avec la vraie taille à runtime */ +}cob_object; + + + + + +typedef struct cobol_class +{ + ht_hash_table *tmv; + struct cobol_classv **super_class; + int ft_size; +}cob_class; + +struct cobol_class *cb_class_new(const char *name); + + +struct cobol_object *cb_object_new(struct cobol_class *myclass, + cob_field *args[]); + + +cobol_method cb_object_method(struct cobol_object *self, + const char *method_name); + +void cb_class_inherit(struct cobol_class *myclass, + struct cobol_class *ancester); + +void cb_class_new_method(struct cobol_class *myclass, + const char *method_name, + cobol_method *method_code); + +void cb_class_new_field(struct cobol_class *myclass, + const char *field_name, + int size); + +#endif + + + + + + diff --git a/libcob/hash_table.c b/libcob/hash_table.c new file mode 100755 index 000000000..f7d24f95e --- /dev/null +++ b/libcob/hash_table.c @@ -0,0 +1,225 @@ +// hash_table.c +#include +#include +#include +#include "hash_table.h" +#include "Object.h" + + + + +static ht_item *ht_new_item(const char *k, cobol_method *v) +{ + ht_item *i = malloc(sizeof(ht_item)); + i->key = strdup(k); + i->value = strdup(v); + return i; +} + +// hash_table.c +static ht_hash_table* ht_new_sized(const int base_size) { + ht_hash_table* ht = malloc(sizeof(ht_hash_table)); + ht->base_size = base_size; + + ht->size = next_prime(ht->base_size); + + ht->count = 0; + ht->items = calloc((size_t)ht->size, sizeof(ht_item*)); + return ht; +} + + +ht_hash_table* ht_new() { + return ht_new_sized(HT_INITIAL_BASE_SIZE); +} + +static void ht_del_item(ht_item *i) +{ + free(i->key); + free(i->value); + free(i); +} + +void ht_del_hash_table(ht_hash_table *ht) +{ + for (int i = 0; i < ht->size; i++) + { + ht_item *item = ht->items[i]; + if (item != NULL) + { + ht_del_item(item); + } + } + free(ht->items); + free(ht); +} + +static int ht_hash(const char *s, const int a, const int m) +{ + long hash = 0; + const int len_s = strlen(s); + for (int i = 0; i < len_s; i++) + { + hash += (long)pow(a, len_s - (i + 1)) * s[i]; + hash = hash % m; + } + return (int)hash; +} + +static int ht_get_hash( + const char *s, const int num_buckets, const int attempt) +{ + const int hash_a = ht_hash(s, HT_PRIME_1, num_buckets); + const int hash_b = ht_hash(s, HT_PRIME_2, num_buckets); + return (hash_a + (attempt * (hash_b + 1))) % num_buckets; +} + +void ht_insert(ht_hash_table *ht, const char *key, cobol_method *value) +{ + const int load = ht->count * 100 / ht->size; + if (load > 70) { + ht_resize_up(ht); + } + ht_item *item = ht_new_item(key, value); + int index = ht_get_hash(item->key, ht->size, 0); + ht_item *cur_item = ht->items[index]; + int i = 1; + while (cur_item != NULL && cur_item != &HT_DELETED_ITEM) + { + if (strcmp(cur_item->key, key) == 0) + { + ht_del_item(cur_item); + ht->items[index] = item; + return; + } + index = ht_get_hash(item->key, ht->size, i); + cur_item = ht->items[index]; + i++; + } + ht->items[index] = item; + ht->count++; +} + +cobol_method *ht_search(ht_hash_table *ht, const char *key) +{ + int index = ht_get_hash(key, ht->size, 0); + ht_item *item = ht->items[index]; + int i = 1; + while (item != NULL) + { + if (item != &HT_DELETED_ITEM) + { + if (strcmp(item->key, key) == 0) + { + return item->value; + } + } + index = ht_get_hash(key, ht->size, i); + item = ht->items[index]; + i++; + } + return NULL; +} + +static ht_item HT_DELETED_ITEM = {NULL, NULL}; + +void ht_delete(ht_hash_table *ht, const char *key) +{ + const int load = ht->count * 100 / ht->size; + if (load < 10) { + ht_resize_down(ht); + } + int index = ht_get_hash(key, ht->size, 0); + ht_item *item = ht->items[index]; + int i = 1; + while (item != NULL) + { + if (item != &HT_DELETED_ITEM) + { + if (strcmp(item->key, key) == 0) + { + ht_del_item(item); + ht->items[index] = &HT_DELETED_ITEM; + } + } + index = ht_get_hash(key, ht->size, i); + item = ht->items[index]; + i++; + } + ht->count--; +} + + + +static void ht_resize(ht_hash_table* ht, const int base_size) { + if (base_size < HT_INITIAL_BASE_SIZE) { + return; + } + ht_hash_table* new_ht = ht_new_sized(base_size); + for (int i = 0; i < ht->size; i++) { + ht_item* item = ht->items[i]; + if (item != NULL && item != &HT_DELETED_ITEM) { + ht_insert(new_ht, item->key, item->value); + } + } + + ht->base_size = new_ht->base_size; + ht->count = new_ht->count; + + // To delete new_ht, we give it ht's size and items + const int tmp_size = ht->size; + ht->size = new_ht->size; + new_ht->size = tmp_size; + + ht_item** tmp_items = ht->items; + ht->items = new_ht->items; + new_ht->items = tmp_items; + + ht_del_hash_table(new_ht); +} + +static void ht_resize_up(ht_hash_table* ht) { + const int new_size = ht->base_size * 2; + ht_resize(ht, new_size); +} + + +static void ht_resize_down(ht_hash_table* ht) { + const int new_size = ht->base_size / 2; + ht_resize(ht, new_size); +} + + + + + +/* + * Return whether x is prime or not + * + * Returns: + * 1 - prime + * 0 - not prime + * -1 - undefined (i.e. x < 2) + */ +int is_prime(const int x) { + if (x < 2) { return -1; } + if (x < 4) { return 1; } + if ((x % 2) == 0) { return 0; } + for (int i = 3; i <= floor(sqrt((double) x)); i += 2) { + if ((x % i) == 0) { + return 0; + } + } + return 1; +} + + +/* + * Return the next prime after x, or x if x is prime + */ +int next_prime(int x) { + while (is_prime(x) != 1) { + x++; + } + return x; +} \ No newline at end of file diff --git a/libcob/hash_table.h b/libcob/hash_table.h new file mode 100755 index 000000000..c77f2f216 --- /dev/null +++ b/libcob/hash_table.h @@ -0,0 +1,33 @@ +#ifndef HTABLE +#define HTABLEht_item + +#define HT_PRIME_1 151 +#define HT_PRIME_2 193 +#define HT_INITIAL_BASE_SIZE 47 + + + +typedef struct { + char* key; + char* value; +} ht_item; + + + +typedef struct { + int size; + int count; + int base_size; + ht_item** items; +} ht_hash_table; + + +ht_hash_table* ht_new(); + +void ht_insert(ht_hash_table* ht, const char* key, cobol_method *value); +cobol_method* ht_search(ht_hash_table* ht, const char* key); +void ht_delete(ht_hash_table* h, const char* key); + +#endif + +