NamedEnum and HashTable

NamedEnum and its relation to HashTable

As the description of the NamedEnum in OpenFOAM in NamedEnum.H:

1
2
Description
Initialise the NamedEnum HashTable from the static list of names.

In the previous code study (the devolatilization model), this class has been used. In this blog, this class will be studied more in details. In phaseProperties.H, there is an public object defined in class phaseProperties as:

1
2
//- Corresponding word representations for phase type enumerations
static const NamedEnum<phaseType, 4> phaseTypeNames;

where the phaseType here is defined as:

1
2
3
4
5
6
7
8
//- Phase type enumeration
enum phaseType
{
GAS,
LIQUID,
SOLID,
UNKNOWN
};

The NamedEnum constructor is defined as:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
template<class Enum, unsigned int nEnum>
Foam::NamedEnum<Enum, nEnum>::NamedEnum()
:
HashTable<unsigned int>(2*nEnum)
{
for (unsigned int enumI = 0; enumI < nEnum; ++enumI)
{
if (!names[enumI] || names[enumI][0] == '\0') // '\0' means a null charactor
{
stringList goodNames(enumI); // initilize with a size of enumI

for (unsigned int i = 0; i < enumI; ++i)
{
goodNames[i] = names[i];
}

FatalErrorInFunction
<< "Illegal enumeration name at position " << enumI << endl
<< "after entries " << goodNames << ".\n"
<< "Possibly your NamedEnum<Enum, nEnum>::names array"
<< " is not of size " << nEnum << endl
<< abort(FatalError);
}
insert(names[enumI], enumI);
}
}

Here, it should be noted that the NamedEnum class is a sub class of HashTable class. In this case the Enum is phaseType and the nEnum is 4. Firstly, the HashTable class is initialized:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
template<class T, class Key, class Hash>
Foam::HashTable<T, Key, Hash>::HashTable(const label size)
:
HashTableCore(),
nElmts_(0),
tableSize_(HashTableCore::canonicalSize(size)),
table_(nullptr)
{
if (tableSize_)
{
table_ = new hashedEntry*[tableSize_];

for (label hashIdx = 0; hashIdx < tableSize_; hashIdx++)
{
table_[hashIdx] = 0;
}
}
}

Here as defined in the HashTable.C, the Key and Hash are:

1
template<class T, class Key=word, class Hash=string::hash>

The stringList is defined in stringList.H as:

1
typedef List<string> stringList;

The names_ is defined as:

1
2
//- List of specie names
List<word> names_;

The goodNames is initialized with the size of the enumI, which is a number indicates where the loop is, due to before this value, the whole list is consisting of good “names”. The insert function is defined in the HashTableI.H:

1
2
3
4
5
6
7
8
9
10
//- Insert a new hashedEntry
template<class T, class Key, class Hash>
inline bool Foam::HashTable<T, Key, Hash>::insert
(
const Key& key,
const T& newEntry
)
{
return this->set(key, newEntry, true);
}

This set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
template<class T, class Key, class Hash>
bool Foam::HashTable<T, Key, Hash>::set
(
const Key& key,
const T& newEntry,
const bool protect
)
{
if (!tableSize_)
{
resize(2);
}

const label hashIdx = hashKeyIndex(key);

hashedEntry* existing = 0;
hashedEntry* prev = 0;

for (hashedEntry* ep = table_[hashIdx]; ep; ep = ep->next_)
{
if (key == ep->key_)
{
existing = ep;
break;
}
prev = ep;
}

// Not found, insert it at the head
if (!existing)
{
table_[hashIdx] = new hashedEntry(key, table_[hashIdx], newEntry);
nElmts_++;

if (double(nElmts_)/tableSize_ > 0.8 && tableSize_ < maxTableSize)
{
#ifdef FULLDEBUG
if (debug)
{
InfoInFunction << "Doubling table size\n";
}
#endif

resize(2*tableSize_);
}
}
else if (protect)
{
// Found - but protected from overwriting
// this corresponds to the STL 'insert' convention
#ifdef FULLDEBUG
if (debug)
{
InfoInFunction
<< "Cannot insert " << key << " already in hash table\n";
}
#endif
return false;
}
else
{
// Found - overwrite existing entry
// this corresponds to the Perl convention
hashedEntry* ep = new hashedEntry(key, existing->next_, newEntry);

// Replace existing element - within list or insert at the head
if (prev)
{
prev->next_ = ep;
}
else
{
table_[hashIdx] = ep;
}

delete existing;
}

return true;
}