This is a follow-up question to my previous question
I have modified the code according to the feedback I got from here and my Professor. However, my Professor is still not happy with the final code. More specifically he is not a fan searching once to see if the item exists in the table and searching for the second time to insert the item. I am wondering how can I solve this problem or rewrite the code to be more efficient.
hashcons.h
#ifndef HASHCONS_H
#define HASHCONS_H
#include <stddef.h>
#include <stdbool.h>
typedef int (*Hash_Cons_Hash)(void *);
typedef bool (*Hash_Cons_Equal)(void *, void *);
typedef struct hash_cons_table {
int size;
int capacity;
void **table;
Hash_Cons_Hash hashf;
Hash_Cons_Equal equalf;
} *HASH_CONS_TABLE;
/**
* Get item if there is one otherwise create one
* @param temp_item it is a temporary or perhaps stack allocated creation of item
* @param temp_size how many bytes it is
* @param hashcons table
*/
void *hash_cons_get(void *temp_item, size_t temp_size, HASH_CONS_TABLE table);
#endif
hashcons.c
#include <stdlib.h>
#include <string.h>
#include "prime.h"
#include "hashcons.h"
#define HC_INITIAL_BASE_SIZE 61
#define MAX_DENSITY 0.5
/**
* Initializes a table
* @param hc table
* @param capacity new capacity
*/
void hc_initialize(HASH_CONS_TABLE hc, const int capacity) {
hc->capacity = capacity;
hc->table = calloc(hc->capacity, sizeof(void *));
hc->size = 0;
}
/**
* Finds the candidate index intended to get inserted or searched in table
* @param hc table
* @param item the item looking to be added or removed
* @param insert_mode true indicates insert false indicates search
* @return
*/
static int hc_candidate_index(HASH_CONS_TABLE hc, void *item, bool insert_mode) {
int attempt = 0;
int hash = hc->hashf(item);
int index = hash % hc->capacity;
int step_size = 0;
while (attempt++ < hc->capacity) {
if (insert_mode && hc->table[index] == NULL) {
return index;
} else if (!insert_mode && hc->equalf(hc->table[index], item)) {
return index;
}
if (attempt == 0) {
step_size = hash % (hc->capacity - 2);
}
index = (index + step_size) % hc->capacity;
}
return -1;
}
/**
* Insert an item into table
* @param hc table
* @param item the item intended to get inserted into the table
*/
static void hc_insert(HASH_CONS_TABLE hc, void *item) {
int index = hc_candidate_index(hc, item, true);
hc->table[index] = item;
hc->size++;
}
/**
* Search an item in table
* @param hc table
* @param item the item intended to get searched in the table
* @return the item or null
*/
static void *hc_search(HASH_CONS_TABLE hc, void *item) {
int index = hc_candidate_index(hc, item, false);
return index == -1 ? NULL : hc->table[index];
}
static void hc_resize(HASH_CONS_TABLE hc, const int capacity) {
void **old_table = hc->table;
int old_capacity = hc->capacity;
hc_initialize(hc, capacity);
for (int i = 0; i < old_capacity; i++) {
void *item = old_table[i];
if (item != NULL) {
hc_insert(hc, item);
}
}
free(old_table);
}
/**
* Insert an item into table if item is not already in table or just returns the existing item
* @param item the item
* @param temp_size item size
* @param hc table
* @return item just got inserted into the table or existing item
*/
void *hash_cons_get(void *item, size_t temp_size, HASH_CONS_TABLE hc) {
void *result;
if (hc->table == NULL) {
hc_initialize(hc, HC_INITIAL_BASE_SIZE);
}
if (hc->size > hc->capacity * MAX_DENSITY) {
const int new_capacity = next_twin_prime((hc->capacity << 1) + 1);
hc_resize(hc, new_capacity);
}
if ((result = hc_search(hc, item)) != NULL) {
return result;
} else {
result = malloc(temp_size);
memcpy(result, item, temp_size);
hc_insert(hc, result);
return result;
}
}
prime.h
#ifndef PRIME_H
#define PRIME_H
int next_twin_prime(int x);
#endif
prime.c
#include "prime.h"
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#define INITIAL_TABLE_SIZE 9973
struct PrimesTable {
int size;
bool *table;
};
struct PrimesTable primesTable = {0, 0};
/**
* Create a boolean array "prime[0..n]" and initialize
* all entries it as true. A value in prime[i] will
* finally be false if i is Not a prime, else true.
*/
void initialize_sieve_of_eratosthenes(int n) {
if (primesTable.table == NULL) {
primesTable.size = n;
primesTable.table = malloc(n * sizeof(bool));
memset(primesTable.table, true, primesTable.size);
} else {
int original_size = primesTable.size;
bool *original_table = primesTable.table;
primesTable.size = n;
primesTable.table = malloc(n * sizeof(bool));
memset(primesTable.table, true, primesTable.size);
memcpy(primesTable.table, original_table, original_size * sizeof(bool));
free(original_table);
}
for (int p = 2; p * p < n; p++) {
// If primes[p] is not changed, then it is a prime
if (primesTable.table[p] == true) {
// Update all multiples of p
for (int i = p * 2; i <= n; i += p) primesTable.table[i] = false;
}
}
}
/**
* Return the next prime greater than parameter such that -2 is also a prime
*/
int next_twin_prime(int x) {
if (primesTable.table == 0) {
initialize_sieve_of_eratosthenes(INITIAL_TABLE_SIZE);
}
int i;
for (i = x + 1; i < primesTable.size; i++) {
if (primesTable.table[i] && primesTable.table[i - 2]) return i;
}
initialize_sieve_of_eratosthenes((primesTable.size << 1) + 1);
return next_twin_prime(x);
}
Added by Reviewer
common.h
#ifndef COMMON_H
#define COMMON_H
#define TRUE 1
#define FALSE 0
#endif
test.h
#ifndef TEST_h
#define TEST_h
void test_integer_table();
#endif
test.c
#include "stdlib.h"
#include "stdio.h"
#include "stdbool.h"
#include "hashcons.h"
long hash_integer(void *p) {
return *((int *) p);
}
bool equals_integer(void *p1, void *p2) {
if (p1 == NULL || p2 == NULL) {
return false;
}
int *i1 = (int *) p1;
int *i2 = (int *) p2;
return *i1 == *i2;
}
static struct hash_cons_table integer_table = {
0, 0, 0,
&hash_integer,
&equals_integer
};
int *new_integer(int n) {
return hash_cons_get(&n, sizeof(int), &integer_table);
}
void assertTrue(const char *message, bool b) {
if (!b) {
fprintf(stderr, "Assertion failed: %s\n", message);
exit(1);
}
}
void test_integer_table() {
int *i3 = new_integer(3);
assertTrue("initial i3", *i3 == 3);
int *i8 = new_integer(8);
assertTrue("initial i8", *i8 == 8);
assertTrue("later i3", *i3 == 3);
for (int i = 0; i < 100; ++i) {
char buffer[256];
sprintf(buffer, "integer for %d", i);
assertTrue(buffer, *new_integer(i) == i);
}
}
main.c
#include "common.h"
#include "hashcons.h"
#include <stdio.h>
#include <stdlib.h>
#include "test.h"
typedef struct dummy {
int key;
} *DUMMY;
long hash(void *item) {
return 13 * ((DUMMY) item)->key + 17;
}
int equal(void *item1, void *item2) {
if (item1 == NULL || item2 == NULL) {
return FALSE;
}
return ((DUMMY) item1)->key == ((DUMMY) item2)->key;
}
DUMMY create_dummy(int key) {
DUMMY dummy = malloc(sizeof(dummy));
dummy->key = key;
return dummy;
}
static int test_adding_items(HASH_CONS_TABLE hc, int test_sample)
{
printf("starting to add stuff\n");
int failure_count = 0;
for (int i = 0; i < test_sample; i++) {
void *item = create_dummy(i);
if (!hash_cons_get(item, sizeof(struct dummy), hc))
{
failure_count++;
}
}
printf("finished adding stuff\n");
return failure_count;
}
static int test_getting_times(HASH_CONS_TABLE hc, int test_sample)
{
printf("starting to get stuff\n");
int failure_count = 0;
for (size_t i = 0; i < test_sample; i++) {
void *item = create_dummy(i);
if (hash_cons_get(item, sizeof(struct dummy), hc) == NULL)
{
failure_count++;
printf("Item %d not found\n", i);
}
}
printf("finished getting stuff\n");
return failure_count;
}
int main() {
HASH_CONS_TABLE hc = malloc(sizeof(struct hash_cons_table));
hc->hashf = hash;
hc->equalf = equal;
hc->size = 0;
int count = 300;
printf("starting to add stuff\n");
int i;
for (i = 0; i < count; i++) {
void *item = create_dummy(i);
hash_cons_get(item, sizeof(struct dummy), hc);
}
printf("finished adding stuff\n");
printf("starting to get stuff\n");
for (i = 0; i < count; i++) {
void *item = create_dummy(i);
if (hash_cons_get(item, sizeof(struct dummy), hc) == NULL)
{
printf("Item %d not found\n", i);
}
}
printf("finished getting stuff\n");
printf("Done!");
test_integer_table();
test_adding_items(hc, 100);
test_getting_times(hc, 100);
return 0;
}