Looking for C++ immutable hashset/hashmap - c++

I work on GPL'ed C++ code with heavy data processing. One particular pattern we often have is to collect some amount (thousands to millions) of keys or key/value pairs (usually int32..int128), insert them into hashset/hashmap and then use it without further modifications.
I named it immutable hashtable, although single-assignment hashtable may be even a better name since we don't use it prior to full construction.
Today we are using STL unordered_map/set, but we are looking for a better (especially faster) library. Can you recommend anything suitable for the situation, with GPL-compatible license?
I think that the most efficient approach would be to radix-sort all keys by the bucket num and provide bucket->range mapping, so we can use the following code to search for a key:
bool contains (set,key) {
h = hash(key);
b = h % BUCKETS;
for (i : range(set.bucket[b], set.bucket[b+1]-1)
if (set.keys[i]==key) return true;
return false;
}
Your comments on this approach? Can you propose a faster way to implement immutable map/set?

I think, for your case is more suitable Double Hashing or Robin Hood Hashing. Among lot of possible algorithms, I prefer to use Double Hashing with 2^n table and odd step. This algorithm very efficient and easy to code. Following is just an example of such container for uint32_t keys:
class uint32_DH {
static const int _TABSZ = 1 << 20; // 1M cells, 2^N size
public:
uint32_DH() { bzero(_data, sizeof(_data)); }
bool search(uint32_t key) { return *lookup(key) == key; }
void insert(uint32_t key) { *lookup(key) = key; }
private:
uint32_t* lookup(uint32_t key) {
uint32_t pos = key + (key >> 32) * 7919;
uint32_t step = (key * 7717 ^ (pos >> 16)) | 1;
uint32_t *rc;
do {
rc = _data + ((pos += step) & (_TABSZ - 1));
} while(*rc != 0 && *rc != key);
return rc;
}
uint32_t _data[_TABSZ];
}

Related

Number of buckets of std::unordered_map grows unexpectedly

I'd like to use std::unordered map as a software cache with a limited capacity. Namely, I set the number of buckets in the constructor (doesn't mind that it might become actually larger) and insert new data (if not already there) if the following way:
If the bucket where the data belong is not empty, I replace its node with the inserted data (by C++17 extraction-insertion pattern).
Otherwise, I simply insert data.
The minimal example that simulates this approach is as follows:
#include <iostream>
#include <unordered_map>
std::unordered_map<int, int> m(2);
void insert(int a) {
auto idx = m.bucket(a);
if (m.bucket_size(idx) > 0) {
const auto& key = m.begin(idx)->first;
auto nh = m.extract(key);
nh.key() = a;
nh.mapped() = a;
m.insert(std::move(nh));
}
else
m.insert({a, a});
}
int main() {
for (int i = 0; i < 1000; i++) {
auto bc1 = m.bucket_count();
insert(i);
auto bc2 = m.bucket_count();
if (bc1 != bc2) std::cerr << bc2 << std::endl;
}
}
The problem is, that with GCC 8.1 (that is available for me in the production environment), the bucket count is not fixed and grows instead; the output reads:
7
17
37
79
167
337
709
1493
Live demo: https://wandbox.org/permlink/c8nnEU52NsWarmuD
Updated info: the bucket count is always increased in the else branch: https://wandbox.org/permlink/p2JaHNP5008LGIpL.
However, when I use GCC 9.1 or Clang 8.0, the bucket count remains fixed (no output is printed in the error stream).
My question is whether this is a bug in the older version of libstdc++, or my approach isn't correct and I cannot use std::unordered_map this way.
Moreover, I found out that the problem disappears when I set the max_load_factor to some very high number, such as
m.max_load_factor(1e20f);
But I don't want to rely on such a "fragile" solution in the production code.
Unfortunately the problem you're having appears to be a bug in older implementations of std::unordered_map. This problem disappears in g++-9, but if you're limited to g++-8, I recommend rolling your own hash-cache.
Rolling our own hash-cache
Thankfully, the type of cache you want to write is actually simpler than writing a full hash-table, mainly because it's fine if values occasionally get dropped from the table. To see how difficult it'd be, I wrote my own version.
So what's it look like?
Let's say you have an expensive function you want to cache. The fibbonacci function, when written using the recursive implementation, is notorious for requiring exponential time in terms of the input because it calls itself.
// Uncached version
long long fib(int n) {
if(n <= 1)
return n;
else
return fib(n - 1) + fib(n - 2);
}
Let's transform it to the cached version, using the Cache class which I'll show you in a moment. We actually only need to add one line of code to the function:
// Cached version; much faster
long long fib(int n) {
static auto fib = Cache(::fib, 1024); // fib now refers to the cache, instead of the enclosing function
if(n <= 1)
return n;
else
return fib(n - 1) + fib(n - 2); // Invokes cache
}
The first argument is the function you want to cache (in this case, fib itself), and the second argument is the capacity. For n == 40, the uncached version takes 487,000 microseconds to run. And the cached version? Just 16 microseconds to initialize the cache, fill it, and return the value! You can see it run here.. After that initial access, retrieving a stored value from the cache takes around 6 nanoseconds.
(If Compiler Explorer shows the assembly instead of the output, click on the tab next to it.)
How would we write this Cache class?
Here's a compact implementation of it. The Cache class stores the following
An array of bools, which keeps track of which buckets have values
An array of keys
An array of values
A bitmask & hash function
A function to calculate values that aren't in the table
In order to calculate a value, we:
Check if the key is stored in the table
If the key is not in the table, calculate and store the value
Return the stored value
Here's the code:
template<class Key, class Value, class Func>
class Cache {
static size_t calc_mask(size_t min_cap) {
size_t actual_cap = 1;
while(actual_cap <= min_cap) {
actual_cap *= 2;
}
return actual_cap - 1;
}
size_t mask = 0;
std::unique_ptr<bool[]> isEmpty;
std::unique_ptr<Key[]> keys;
std::unique_ptr<Value[]> values;
std::hash<Key> hash;
Func func;
public:
Cache(Cache const& c)
: mask(c.mask)
, isEmpty(new bool[mask + 1])
, keys(new Key[mask + 1])
, values(new Value[mask + 1])
, hash(c.hash)
, func(c.func)
{
std::copy_n(c.isEmpty.get(), capacity(), isEmpty.get());
std::copy_n(c.keys.get(), capacity(), keys.get());
std::copy_n(c.values.get(), capacity(), values.get());
}
Cache(Cache&&) = default;
Cache(Func func, size_t cap)
: mask(calc_mask(cap))
, isEmpty(new bool[mask + 1])
, keys(new Key[mask + 1])
, values(new Value[mask + 1])
, hash()
, func(func) {
std::fill_n(isEmpty.get(), capacity(), true);
}
Cache(Func func, size_t cap, std::hash<Key> const& hash)
: mask(calc_mask(cap))
, isEmpty(new bool[mask + 1])
, keys(new Key[mask + 1])
, values(new Value[mask + 1])
, hash(hash)
, func(func) {
std::fill_n(isEmpty.get(), capacity(), true);
}
Value operator()(Key const& key) const {
size_t index = hash(key) & mask;
auto& value = values[index];
auto& old_key = keys[index];
if(isEmpty[index] || old_key != key) {
old_key = key;
value = func(key);
isEmpty[index] = false;
}
return value;
}
size_t capacity() const {
return mask + 1;
}
};
template<class Key, class Value>
Cache(Value(*)(Key), size_t) -> Cache<Key, Value, Value(*)(Key)>;

I need to create MultiMap using hash-table but I get time-limit exceeded error (C++)

I'm trying to solve algorithm task: I need to create MultiMap(key,(values)) using hash-table. I can't use Set and Map libraries. I send code to testing system, but I get time-limit exceeded error on test 20. I don't know what exactly this test contains. The code must do following tasks:
put x y - add pair (x,y).If pair exists, do nothing.
delete x y - delete pair(x,y). If pair doesn't exist, do nothing.
deleteall x - delete all pairs with first element x.
get x - print number of pairs with first element x and second elements.
The amount of operations <= 100000
Time limit - 2s
Example:
multimap.in:
put a a
put a b
put a c
get a
delete a b
get a
deleteall a
get a
multimap.out:
3 b c a
2 c a
0
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;
inline long long h1(const string& key) {
long long number = 0;
const int p = 31;
int pow = 1;
for(auto& x : key){
number += (x - 'a' + 1 ) * pow;
pow *= p;
}
return abs(number) % 1000003;
}
inline void Put(vector<vector<pair<string,string>>>& Hash_table,const long long& hash, const string& key, const string& value) {
int checker = 0;
for(int i = 0; i < Hash_table[hash].size();i++) {
if(Hash_table[hash][i].first == key && Hash_table[hash][i].second == value) {
checker = 1;
break;
}
}
if(checker == 0){
pair <string,string> key_value = make_pair(key,value);
Hash_table[hash].push_back(key_value);
}
}
inline void Delete(vector<vector<pair<string,string>>>& Hash_table,const long long& hash, const string& key, const string& value) {
for(int i = 0; i < Hash_table[hash].size();i++) {
if(Hash_table[hash][i].first == key && Hash_table[hash][i].second == value) {
Hash_table[hash].erase(Hash_table[hash].begin() + i);
break;
}
}
}
inline void Delete_All(vector<vector<pair<string,string>>>& Hash_table,const long long& hash,const string& key) {
for(int i = Hash_table[hash].size() - 1;i >= 0;i--){
if(Hash_table[hash][i].first == key){
Hash_table[hash].erase(Hash_table[hash].begin() + i);
}
}
}
inline string Get(const vector<vector<pair<string,string>>>& Hash_table,const long long& hash, const string& key) {
string result="";
int counter = 0;
for(int i = 0; i < Hash_table[hash].size();i++){
if(Hash_table[hash][i].first == key){
counter++;
result += Hash_table[hash][i].second + " ";
}
}
if(counter != 0)
return to_string(counter) + " " + result + "\n";
else
return "0\n";
}
int main() {
vector<vector<pair<string,string>>> Hash_table;
Hash_table.resize(1000003);
ifstream input("multimap.in");
ofstream output("multimap.out");
string command;
string key;
int k = 0;
string value;
while(true) {
input >> command;
if(input.eof())
break;
if(command == "put") {
input >> key;
long long hash = h1(key);
input >> value;
Put(Hash_table,hash,key,value);
}
if(command == "delete") {
input >> key;
input >> value;
long long hash = h1(key);
Delete(Hash_table,hash,key,value);
}
if(command == "get") {
input >> key;
long long hash = h1(key);
output << Get(Hash_table,hash,key);
}
if(command == "deleteall"){
input >> key;
long long hash = h1(key);
Delete_All(Hash_table,hash,key);
}
}
}
How can I do my code work faster?
At very first, a matter of design: Normally, one would pass the key only to the function and calculate the hash within. Your variant allows a user to place elements anywhere within the hash table (using bad hash values), so user could easily break it.
So e. g. put:
using HashTable = std::vector<std::vector<std::pair<std::string, std::string>>>;
void put(HashTable& table, std::string& key, std::string const& value)
{
auto hash = h1(key);
// ...
}
If at all, the hash function could be parametrised, but then you'd write a separate class for (wrapping the vector of vectors) and provide the hash function in constructor so that a user cannot exchange it arbitrarily (and again break the hash table). A class would come with additional benefits, most important: better encapsulation (hiding the vector away, so user could not change it with vector's own interface):
class HashTable
{
public:
// IF you want to provide hash function:
template <typename Hash>
HashTable(Hash hash) : hash(hash) { }
void put(std::string const& key, std::string const& value);
void remove(std::string const& key, std::string const& value); //(delete is keyword!)
// ...
private:
std::vector<std::vector<std::pair<std::string, std::string>>> data;
// if hash function parametrized:
std::function<size_t(std::string)> hash; // #include <functional> for
};
I'm not 100% sure how efficient std::function really is, so for high performance code, you preferrably use your hash function h1 directly (not implenting constructor as illustrated above).
Coming to optimisations:
For the hash key I would prefer unsigned value: Negative indices are meaningless anyway, so why allow them at all? long long (signed or unsigned) might be a bad choice if testing system is a 32 bit system (might be unlikely, but still...). size_t covers both issues at once: it is unsigned and it is selected in size appropriately for given system (if interested in details: actually adjusted to address bus size, but on modern systems, this is equal to register size as well, which is what we need). Select type of pow to be the same.
deleteAll is implemented inefficiently: With each element you erase you move all the subsequent elements one position towards front. If you delete multiple elements, you do this repeatedly, so one single element can get moved multiple times. Better:
auto pos = vector.begin();
for(auto& pair : vector)
{
if(pair.first != keyToDelete)
*pos++ = std::move(s); // move semantics: faster than copying!
}
vector.erase(pos, vector.end());
This will move each element at most once, erasing all surplus elements in one single go. Appart from the final erasing (which you have to do explicitly then), this is more or less what std::remove and std::remove_if from algorithm library do as well. Are you allowed to use it? Then your code might look like this:
auto condition = [&keyToDelete](std::pair<std::string, std::string> const& p)
{ return p.first == keyToDelete; };
vector.erase(std::remove_if(vector.begin(), vector.end(), condition), vector.end());
and you profit from already highly optimised algorithm.
Just a minor performance gain, but still: You can spare variable initialisation, assignment and conditional branch (the latter one can be relatively expensive operation on some systems) within put if you simply return if an element is found:
//int checker = 0;
for(auto& pair : hashTable[hash]) // just a little more comfortable to write...
{
if(pair.first == key && pair.second == value)
return;
}
auto key_value = std::make_pair(key, value);
hashTable[hash].push_back(key_value);
Again, with algorithm library:
auto key_value = std::make_pair(key, value);
// same condition as above!
if(std::find_if(vector.begin(), vector.end(), condition) == vector.end();
{
vector.push_back(key_value);
}
Then less than 100000 operations does not indicate that each operation will require a separate key/value pair. We might expect that keys are added, removed, re-added, ..., so you most likely don't have to cope with 100000 different values. I'd assume your map is much too large (be aware that it requires initialisation of 100000 vectors as well). I'd assume a much smaller one should suffice already (possibly 1009 or 10007? You might possibly have to experiment a little...).
Keeping the inner vectors sorted might give you some performance boost as well:
put: You could use a binary search to find the two elements in between a new one is to be inserted (if one of these two is equal to given one, no insertion, of course)
delete: Use binary search to find the element to delete.
deleteAll: Find upper and lower bounds for elements to be deleted and erase whole range at once.
get: find lower and upper bound as for deleteAll, distance in between (number of elements) is a simple subtraction and you could print out the texts directly (instead of first building a long string). Which of outputting directly or creating a string really is more efficient is to be found out, though, as outputting directly involves multiple system calls, which in the end might cost previously gained performance again...
Considering your input loop:
Checking for eof() (only) is critical! If there is an error in the file, you'll end up in an endless loop, as the fail bit gets set, operator>> actually won't read anything at all any more and you won't ever reach the end of the file. This even might be the reason for your 20th test failing.
Additionally: You have line based input (each command on a separate line), so reading a whole line at once and only afterwards parse it will spare you some system calls. If some argument is missing, you will detect it correctly instead of (illegally) reading next command (e. g. put) as argument, similarly you won't interpret a surplus argument as next command. If a line is invalid for whatever reason (bad number of arguments as above or unknown command), you can then decide indiviually what you want to do (just ignore the line or abort processing entirely). So:
std::string line;
while(std::getline(std::cin, line))
{
// parse the string; if line is invalid, appropriate error handling
// (ignoring the line, exiting from loop, ...)
}
if(!std::cin.eof())
{
// some error occured, print error message!
}

How do I delete the least recently accessed key in an associative array?

I have an associative array limited to x amount of keys and want to remove the least recently accessed key in order to add another. I found HashAA in mintl which would be able to do the job in D1 but I've found nothing for D2. Is there anything that supports this now or would I need to maintain a second array to get the job done?
I don't have a real answer but I thought it would be fun to try and implement it in a few minutes (it's probably very inefficient and maybe buggy):
import std.stdio;
import std.traits;
struct MyHash(AA, size_t Limit)
if (isAssociativeArray!AA)
{
alias KeyType!AA Key;
alias ValueType!AA Value;
void opIndexAssign(Value value, Key key)
{
if (hash.length >= Limit)
{
Key leastUsed = leastUsedKey;
hash.remove(leastUsed);
counts.remove(leastUsed);
}
hash[key] = value;
}
Value opIndex(Key key)
{
counts[key]++;
return hash[key];
}
Value[Key] hash;
alias hash this;
private:
#property Key leastUsedKey()
{
Key result;
size_t maxCount = size_t.max;
foreach (key; hash.byKey)
{
if (auto count = key in counts)
{
if (*count < maxCount)
{
maxCount = *count;
result = key;
}
}
else
{
return key;
}
}
return result;
}
size_t[Key] counts;
}
// just to avoid declaring variables in main()
#property void consume(int key) { }
void main()
{
MyHash!(int[int], 3) hash;
hash[0] = 0;
hash[1] = 0;
hash[2] = 0;
writeln(hash.keys);
hash[2].consume;
hash[5] = 0;
writeln(hash.keys); // 2 stays, 5 added
hash.clear();
hash[0] = 0;
hash[1] = 0;
hash[2] = 0;
hash[0].consume;
hash[1].consume;
hash[1].consume;
hash[2].consume;
hash[2].consume;
hash[2].consume;
hash[5] = 0;
writeln(hash); // (0 removed)
}
The built in AAs will rehash when they need to in order to fit more elements, are not fixed in size, and will not keep track of how you've accessed or added elements, and because the language has AAs built in, alternate hash table implementations are going to be relatively rare (most everyone just uses the built in AAs).
So, I'm pretty darn sure that you would need to do this yourself - probably by creating a wrapper around the built in AA type and having the wrapper keep track of all of the accesses to it so that it knows which key was accessed least recently.
You could always check out dcollections for some alternate container implemenations (and IIRC it does have a hash table in it, but I doubt that it does what you want). Personally, I've never even heard of a hash table that acted the way that you want it to, so I expect that what you're looking for is at least somewhat abnormal. But it should be simple enough to create a wrapper around AAs which acts the way that you want.

How to implement C++ dictionary data structure without using STL

I have a project that I am doing, the main objective is to load a list of words (and lots of them 15k+) into a data structure and then do a search on that structure. I did a little research and as far as I can tell a hash table would be best for this (correct me if I am wrong, I looked into tries as well)
Here's the tricky part: I cannot use any STL's for this project. So as far as I can tell I am going to have to write my own hash table class or find one that pretty much works. I understand how has tables work on a basic level but I am not sure I know enough to write a whole one by myself.
I looked around Google and I could not find any suitable sample code.
My question is does anyone know how to do this in c++ and/or where I can find some code to start off with. I need 3 basic functions for the table: insert, search, remove.
Things to remember while you think about this:
The Number 1 Concern is SPEED! this needs to be lighting fast, no concern for system resources. From the reading that I have done, a hash table can do better than O(log n) Consider mutithreading?
Cannot use STL!
I think, sorted array of strings + binary search should be pretty efficient.
std::unordered_map is not STL
http://www.cs.auckland.ac.nz/software/AlgAnim/hash_tables.html
Not entirely clear on all of the restrictions, but assuming you cannot use anything from std, you could write a simple class like the one below to do the job. We will use an array of buckets to store the data, then use a hash function to turn a string into a number in the range 0...MAX_ELEMENTS. each bucket will hold a linked list of strings, so you can retrieve information again. Typically o(1) insertion and find.
Note that for a more effective solution, you may wish to use a vector rather than a fixed length array as I have gone for. There is also minimal error checking and other improvements, but this should get you started.
NOTE you will need to implement your own string hashing function, you can find plenty of these on the net.
class dictionary
{
struct data
{
char* word = nullptr;
data* next = nullptr;
~data()
{
delete [] word;
}
};
public:
const unsigned int MAX_BUCKETS;
dictionary(unsigned int maxBuckets = 1024)
: MAX_BUCKETS(maxBuckets)
, words(new data*[MAX_BUCKETS])
{
memset(words, 0, sizeof(data*) * MAX_BUCKETS);
}
~dictionary()
{
for (int i = 0; i < MAX_BUCKETS; ++i)
delete words[i];
delete [] words;
}
void insert(const char* word)
{
const auto hash_index = hash(word);
auto& d = words[hash_index];
if (d == nullptr)
{
d = new data;
copy_string(d, word);
}
else
{
while (d->next != nullptr)
{
d = d->next;
}
d->next = new data;
copy_string(d->next, word);
}
}
void copy_string(data* d, const char* word)
{
const auto word_length = strlen(word)+1;
d->word = new char[word_length];
strcpy(d->word, word);
printf("%s\n", d->word);
}
const char* find(const char* word) const
{
const auto hash_index = hash(word);
auto& d = words[hash_index];
if (d == nullptr)
{
return nullptr;
}
while (d != nullptr)
{
printf("checking %s with %s\n", word, d->word);
if (strcmp(d->word, word) == 0)
return d->word;
d = d->next;
}
return nullptr;
}
private:
unsigned int hash(const char* word) const
{
// :TODO: write your own hash function here
const unsigned int num = 0; // :TODO:
return num % MAX_BUCKETS;
}
data** words;
};
http://wikipedia-clustering.speedblue.org/trie.php
Seems the above link is down at the moment.
Alternative link:
https://web.archive.org/web/20160426224744/http://wikipedia-clustering.speedblue.org/trie.php
Source Code: https://web.archive.org/web/20160426224744/http://wikipedia-clustering.speedblue.org/download/libTrie-0.1.tgz

How do I make make my hash table with linear probing more efficient?

I'm trying to implement an efficient hash table where collisions are solved using linear probing with step. This function has to be as efficient as possible. No needless = or == operations. My code is working, but not efficient. This efficiency is evaluated by an internal company system. It needs to be better.
There are two classes representing a key/value pair: CKey and CValue. These classes each have a standard constructor, copy constructor, and overridden operators = and ==. Both of them contain a getValue() method returning value of internal private variable. There is also the method getHashLPS() inside CKey, which return hashed position in hash table.
int getHashLPS(int tableSize,int step, int collision) const
{
return ((value + (i*step)) % tableSize);
}
Hash table.
class CTable
{
struct CItem {
CKey key;
CValue value;
};
CItem **table;
int valueCounter;
}
Methods
// return collisions count
int insert(const CKey& key, const CValue& val)
{
int position, collision = 0;
while(true)
{
position = key.getHashLPS(tableSize, step, collision); // get position
if(table[position] == NULL) // free space
{
table[position] = new CItem; // save item
table[position]->key = CKey(key);
table[position]->value = CValue(val);
valueCounter++;
break;
}
if(table[position]->key == key) // same keys => overwrite value
{
table[position]->value = val;
break;
}
collision++; // current positions is full, try another
if(collision >= tableSize) // full table
return -1;
}
return collision;
}
// return collisions count
int remove(const CKey& key)
{
int position, collision = 0;
while(true)
{
position = key.getHashLPS(tableSize, step, collision);
if(table[position] == NULL) // free position - key isn't in table or is unreachable bacause of wrong rehashing
return -1;
if(table[position]->key == key) // found
{
table[position] = NULL; // remove it
valueCounter--;
int newPosition, collisionRehash = 0;
for(int i = 0; i < tableSize; i++, collisionRehash = 0) // rehash table
{
if(table[i] != NULL) // if there is a item, rehash it
{
while(true)
{
newPosition = table[i]->key.getHashLPS(tableSize, step, collisionRehash++);
if(newPosition == i) // same position like before
break;
if(table[newPosition] == NULL) // new position and there is a free space
{
table[newPosition] = table[i]; // copy from old, insert to new
table[i] = NULL; // remove from old
break;
}
}
}
}
break;
}
collision++; // there is some item on newPosition, let's count another
if(collision >= valueCounter) // item isn't in table
return -1;
}
return collision;
}
Both functions return collisions count (for my own purpose) and they return -1 when the searched CKey isn't in the table or the table is full.
Tombstones are forbidden. Rehashing after removing is a must.
The biggest change for improvement I see is in the removal function. You shouldn't need to rehash the entire table. You only need to rehash starting from the removal point until you reach an empty bucket. Also, when re-hashing, remove and store all of the items that need to be re-hashed before doing the re-hashing so that they don't get in the way when placing them back in.
Another thing. With all hashes, the quickest way to increase efficiency to to decrease the loadFactor (the ratio of elements to backing-array size). This reduces the number of collisions, which means less iterating looking for an open spot, and less rehashing on removal. In the limit, as the loadFactor approaches 0, collision probability approaches 0, and it becomes more and more like an array. Though of course memory use goes up.
Update
You only need to rehash starting from the removal point and moving forward by your step size until you reach a null. The reason for this is that those are the only objects that could possibly change their location due to the removal. All other objects would wind up hasing to the exact same place, since they don't belong to the same "collision run".
A possible improvement would be to pre-allocate an array of CItems, that would avoid the malloc()s / news and free() deletes; and you would need the array to be changed to "CItem *table;"
But again: what you want is basically a smooth ride in a car with square wheels.