Providing single-linked list implementation with fast size to address DRC merge performance issue on CentOS

This commit is contained in:
Matthias Koefferlein 2022-12-05 02:07:25 +01:00
parent 43ff59750a
commit 035232c978
7 changed files with 816 additions and 192 deletions

View File

@ -1264,7 +1264,7 @@ connected_clusters<T>::join_cluster_with (typename local_cluster<T>::id_type id,
}
connections_type &target = m_connections [id];
target.splice (target.end (), to_join, to_join.begin (), to_join.end ());
target.splice (to_join);
m_connections.erase (tc);

View File

@ -211,6 +211,273 @@ private:
edge_connectivity_type m_ec;
};
/**
* @brief The instance information for a cluster
*/
class DB_PUBLIC ClusterInstElement
{
public:
ClusterInstElement (const db::InstElement &ie)
{
if (ie.array_inst.at_end ()) {
m_inst_cell_index = std::numeric_limits<db::cell_index_type>::max ();
m_inst_trans = db::ICplxTrans ();
m_inst_prop_id = 0;
} else {
m_inst_cell_index = ie.inst_ptr.cell_index ();
m_inst_trans = ie.complex_trans ();
m_inst_prop_id = ie.inst_ptr.prop_id ();
}
}
ClusterInstElement (db::cell_index_type inst_cell_index, const db::ICplxTrans &inst_trans, db::properties_id_type inst_prop_id)
: m_inst_cell_index (inst_cell_index), m_inst_trans (inst_trans), m_inst_prop_id (inst_prop_id)
{
// .. nothing yet ..
}
ClusterInstElement ()
: m_inst_cell_index (std::numeric_limits<db::cell_index_type>::max ()), m_inst_trans (), m_inst_prop_id (0)
{
// .. nothing yet ..
}
/**
* @brief Returns true, if the cluster does not have an instance
*/
bool has_instance () const
{
return m_inst_cell_index != std::numeric_limits<db::cell_index_type>::max ();
}
/**
* @brief Gets the cell index of the cell which is instantiated
*/
db::cell_index_type inst_cell_index () const
{
return m_inst_cell_index;
}
/**
* @brief Gets the instance transformation
*/
const db::ICplxTrans &inst_trans () const
{
return m_inst_trans;
}
/**
* @brief Gets the instance properties id
*/
db::properties_id_type inst_prop_id () const
{
return m_inst_prop_id;
}
/**
* @brief Sets the instance properties id
*/
void set_inst_prop_id (db::properties_id_type pid)
{
m_inst_prop_id = pid;
}
/**
* @brief Transform with the given transformation
*/
void transform (const db::ICplxTrans &tr)
{
m_inst_trans = tr * m_inst_trans;
}
/**
* @brief Equality
*/
bool operator== (const ClusterInstElement &other) const
{
return m_inst_cell_index == other.m_inst_cell_index && m_inst_trans.equal (other.m_inst_trans) && m_inst_prop_id == other.m_inst_prop_id;
}
/**
* @brief Inequality
*/
bool operator!= (const ClusterInstElement &other) const
{
return ! operator== (other);
}
/**
* @brief Less operator
*/
bool operator< (const ClusterInstElement &other) const
{
if (m_inst_cell_index != other.m_inst_cell_index) {
return m_inst_cell_index < other.m_inst_cell_index;
}
if (! m_inst_trans.equal (other.m_inst_trans)) {
return m_inst_trans.less (other.m_inst_trans);
}
return m_inst_prop_id < other.m_inst_prop_id;
}
private:
db::cell_index_type m_inst_cell_index;
db::ICplxTrans m_inst_trans;
db::properties_id_type m_inst_prop_id;
};
/**
* @brief A connection to a cluster in a child instance
*/
class DB_PUBLIC ClusterInstance
: public ClusterInstElement
{
public:
ClusterInstance (size_t id, db::cell_index_type inst_cell_index, const db::ICplxTrans &inst_trans, db::properties_id_type inst_prop_id)
: ClusterInstElement (inst_cell_index, inst_trans, inst_prop_id), m_id (id)
{
// .. nothing yet ..
}
ClusterInstance (size_t id, const db::InstElement &inst_element)
: ClusterInstElement (inst_element), m_id (id)
{
// .. nothing yet ..
}
ClusterInstance (size_t id)
: ClusterInstElement (), m_id (id)
{
// .. nothing yet ..
}
ClusterInstance ()
: ClusterInstElement (), m_id (0)
{
// .. nothing yet ..
}
/**
* @brief Gets the cluster ID
*/
size_t id () const
{
return m_id;
}
/**
* @brief Equality
*/
bool operator== (const ClusterInstance &other) const
{
return m_id == other.m_id && ClusterInstElement::operator== (other);
}
/**
* @brief Inequality
*/
bool operator!= (const ClusterInstance &other) const
{
return ! operator== (other);
}
/**
* @brief Less operator
*/
bool operator< (const ClusterInstance &other) const
{
if (m_id != other.m_id) {
return m_id < other.m_id;
}
return ClusterInstElement::operator< (other);
}
private:
size_t m_id;
};
/**
* @brief A class representing a list of ClusterInstance objects
*
* It is using a std::list internally, but adds fast size support for STLs which do not have that.
*/
class DB_PUBLIC ClusterInstanceList
{
public:
typedef std::list<ClusterInstance> inner_list;
typedef inner_list::iterator iterator;
typedef inner_list::const_iterator const_iterator;
ClusterInstanceList ()
: m_list (), m_size (0)
{
// .. nothing yet ..
}
ClusterInstanceList (const ClusterInstanceList &other)
: m_list (other.m_list), m_size (other.m_size)
{
// .. nothing yet ..
}
ClusterInstanceList (ClusterInstanceList &&other)
: m_list (other.m_list), m_size (other.m_size)
{
// .. nothing yet ..
}
ClusterInstanceList &operator= (const ClusterInstanceList &other)
{
if (this != &other) {
m_list = other.m_list;
m_size = other.m_size;
}
return *this;
}
iterator begin () { return m_list.begin (); }
iterator end () { return m_list.end (); }
const_iterator begin () const { return m_list.begin (); }
const_iterator end () const { return m_list.end (); }
bool empty () const
{
return m_list.empty ();
}
void push_back (const ClusterInstance &inst)
{
m_list.push_back (inst);
++m_size;
}
void push_back (ClusterInstance &&inst)
{
m_list.push_back (inst);
++m_size;
}
void splice (ClusterInstanceList &other)
{
m_size += other.m_size;
m_list.splice (m_list.end (), other.m_list, other.m_list.begin (), other.m_list.end ());
other.m_size = 0;
}
size_t size () const
{
return m_size;
}
private:
inner_list m_list;
size_t m_size;
};
/**
* @brief Represents a cluster of shapes
*
@ -596,195 +863,6 @@ inline void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int
x.mem_stat (stat, purpose, cat, no_self, parent);
}
/**
* @brief The instance information for a cluster
*/
class DB_PUBLIC ClusterInstElement
{
public:
ClusterInstElement (const db::InstElement &ie)
{
if (ie.array_inst.at_end ()) {
m_inst_cell_index = std::numeric_limits<db::cell_index_type>::max ();
m_inst_trans = db::ICplxTrans ();
m_inst_prop_id = 0;
} else {
m_inst_cell_index = ie.inst_ptr.cell_index ();
m_inst_trans = ie.complex_trans ();
m_inst_prop_id = ie.inst_ptr.prop_id ();
}
}
ClusterInstElement (db::cell_index_type inst_cell_index, const db::ICplxTrans &inst_trans, db::properties_id_type inst_prop_id)
: m_inst_cell_index (inst_cell_index), m_inst_trans (inst_trans), m_inst_prop_id (inst_prop_id)
{
// .. nothing yet ..
}
ClusterInstElement ()
: m_inst_cell_index (std::numeric_limits<db::cell_index_type>::max ()), m_inst_trans (), m_inst_prop_id (0)
{
// .. nothing yet ..
}
/**
* @brief Returns true, if the cluster does not have an instance
*/
bool has_instance () const
{
return m_inst_cell_index != std::numeric_limits<db::cell_index_type>::max ();
}
/**
* @brief Gets the cell index of the cell which is instantiated
*/
db::cell_index_type inst_cell_index () const
{
return m_inst_cell_index;
}
/**
* @brief Gets the instance transformation
*/
const db::ICplxTrans &inst_trans () const
{
return m_inst_trans;
}
/**
* @brief Gets the instance properties id
*/
db::properties_id_type inst_prop_id () const
{
return m_inst_prop_id;
}
/**
* @brief Sets the instance properties id
*/
void set_inst_prop_id (db::properties_id_type pid)
{
m_inst_prop_id = pid;
}
/**
* @brief Transform with the given transformation
*/
void transform (const db::ICplxTrans &tr)
{
m_inst_trans = tr * m_inst_trans;
}
/**
* @brief Equality
*/
bool operator== (const ClusterInstElement &other) const
{
return m_inst_cell_index == other.m_inst_cell_index && m_inst_trans.equal (other.m_inst_trans) && m_inst_prop_id == other.m_inst_prop_id;
}
/**
* @brief Inequality
*/
bool operator!= (const ClusterInstElement &other) const
{
return ! operator== (other);
}
/**
* @brief Less operator
*/
bool operator< (const ClusterInstElement &other) const
{
if (m_inst_cell_index != other.m_inst_cell_index) {
return m_inst_cell_index < other.m_inst_cell_index;
}
if (! m_inst_trans.equal (other.m_inst_trans)) {
return m_inst_trans.less (other.m_inst_trans);
}
return m_inst_prop_id < other.m_inst_prop_id;
}
private:
db::cell_index_type m_inst_cell_index;
db::ICplxTrans m_inst_trans;
db::properties_id_type m_inst_prop_id;
};
/**
* @brief A connection to a cluster in a child instance
*/
class DB_PUBLIC ClusterInstance
: public ClusterInstElement
{
public:
ClusterInstance (size_t id, db::cell_index_type inst_cell_index, const db::ICplxTrans &inst_trans, db::properties_id_type inst_prop_id)
: ClusterInstElement (inst_cell_index, inst_trans, inst_prop_id), m_id (id)
{
// .. nothing yet ..
}
ClusterInstance (size_t id, const db::InstElement &inst_element)
: ClusterInstElement (inst_element), m_id (id)
{
// .. nothing yet ..
}
ClusterInstance (size_t id)
: ClusterInstElement (), m_id (id)
{
// .. nothing yet ..
}
ClusterInstance ()
: ClusterInstElement (), m_id (0)
{
// .. nothing yet ..
}
/**
* @brief Gets the cluster ID
*/
size_t id () const
{
return m_id;
}
/**
* @brief Equality
*/
bool operator== (const ClusterInstance &other) const
{
return m_id == other.m_id && ClusterInstElement::operator== (other);
}
/**
* @brief Inequality
*/
bool operator!= (const ClusterInstance &other) const
{
return ! operator== (other);
}
/**
* @brief Less operator
*/
bool operator< (const ClusterInstance &other) const
{
if (m_id != other.m_id) {
return m_id < other.m_id;
}
return ClusterInstElement::operator< (other);
}
private:
size_t m_id;
};
typedef std::list<std::pair<ClusterInstance, ClusterInstance> > cluster_instance_pair_list_type;
inline bool equal_array_delegates (const db::ArrayBase *a, const db::ArrayBase *b)
@ -1040,7 +1118,7 @@ public:
private:
typename local_clusters<T>::const_iterator m_lc_iter;
typedef std::list<ClusterInstance> connections_type;
typedef ClusterInstanceList connections_type;
typename std::map<typename local_cluster<T>::id_type, connections_type>::const_iterator m_x_iter, m_x_iter_end;
};
@ -1060,7 +1138,7 @@ class DB_PUBLIC_TEMPLATE connected_clusters
{
public:
typedef typename local_clusters<T>::id_type id_type;
typedef std::list<ClusterInstance> connections_type;
typedef ClusterInstanceList connections_type;
typedef typename local_clusters<T>::box_type box_type;
typedef connected_clusters_iterator<T> all_iterator;
typedef typename std::map<typename local_cluster<T>::id_type, connections_type>::const_iterator connections_iterator;

View File

@ -51,6 +51,7 @@ SOURCES = \
tlLongInt.cc \
tlUniqueId.cc \
tlList.cc \
tlSList.cc \
tlEquivalenceClusters.cc \
tlUniqueName.cc \
tlRecipe.cc \
@ -115,6 +116,7 @@ HEADERS = \
tlLongInt.h \
tlUniqueId.h \
tlList.h \
tlSList.h \
tlEquivalenceClusters.h \
tlUniqueName.h \
tlRecipe.h \

31
src/tl/tl/tlSList.cc Normal file
View File

@ -0,0 +1,31 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2022 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "tlSList.h"
namespace tl
{
// .. nothing yet ..
}

320
src/tl/tl/tlSList.h Normal file
View File

@ -0,0 +1,320 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2022 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_tlSList
#define HDR_tlSList
#include "tlCommon.h"
#include <algorithm>
#include <iterator>
namespace tl
{
/**
* @brief A simple single-linked list implementation
*
* This implementation supports:
* - fast size
* - push_back, push_front
* - forward iterator, const_iterator
* - splice
* - pop_front
* - clear
* - empty
*/
template <class T>
class slist
{
private:
struct node_type
{
node_type (const T &_t) : next (0), t (_t) { }
node_type (T &&_t) : next (0), t (_t) { }
node_type *next;
T t;
};
public:
class iterator
{
public:
typedef std::forward_iterator_tag category;
typedef T value_type;
typedef T &reference;
typedef T *pointer;
iterator (node_type *p = 0) : mp_p (p) { }
iterator operator++ () { mp_p = mp_p->next; return *this; }
T *operator-> () const
{
return &mp_p->t;
}
T &operator* () const
{
return mp_p->t;
}
bool operator== (iterator other) const { return mp_p == other.mp_p; }
bool operator!= (iterator other) const { return mp_p != other.mp_p; }
private:
node_type *mp_p;
};
class const_iterator
{
public:
typedef std::forward_iterator_tag category;
typedef const T value_type;
typedef const T &reference;
typedef const T *pointer;
const_iterator (const node_type *p = 0) : mp_p (p) { }
const_iterator operator++ () { mp_p = mp_p->next; return *this; }
const T *operator-> () const
{
return &mp_p->t;
}
const T &operator* () const
{
return mp_p->t;
}
bool operator== (const_iterator other) const { return mp_p == other.mp_p; }
bool operator!= (const_iterator other) const { return mp_p != other.mp_p; }
private:
const node_type *mp_p;
};
slist ()
: mp_first (0), mp_last (0)
{
// .. nothing yet ..
}
template <class Iter>
slist (Iter from, Iter to)
: mp_first (0), mp_last (0), m_size (0)
{
for (Iter i = from; i != to; ++i) {
push_back (*i);
}
}
slist (const slist<T> &other)
: mp_first (0), mp_last (0), m_size (0)
{
for (auto i = other.begin (); i != other.end (); ++i) {
push_back (*i);
}
}
slist (slist<T> &&other)
: mp_first (0), mp_last (0), m_size (0)
{
std::swap (mp_first, other.mp_first);
std::swap (mp_last, other.mp_last);
std::swap (m_size, other.m_size);
}
slist<T> &operator= (const slist<T> &other)
{
if (this != &other) {
clear ();
for (const_iterator i = other.begin (); i != other.end (); ++i) {
push_back (*i);
}
}
return *this;
}
slist<T> &operator= (slist<T> &&other)
{
clear ();
std::swap (mp_first, other.mp_first);
std::swap (mp_last, other.mp_last);
std::swap (m_size, other.m_size);
return *this;
}
~slist ()
{
clear ();
}
iterator begin ()
{
return iterator (mp_first);
}
iterator end ()
{
return iterator (0);
}
const_iterator begin () const
{
return const_iterator (mp_first);
}
const_iterator end () const
{
return const_iterator (0);
}
size_t size () const
{
return m_size;
}
bool empty () const
{
return mp_first == 0;
}
void clear ()
{
while (! empty ()) {
pop_front ();
}
}
void swap (slist<T> &other)
{
std::swap (mp_first, other.mp_first);
std::swap (mp_last, other.mp_last);
std::swap (m_size, other.m_size);
}
void pop_front ()
{
if (mp_first) {
node_type *n = mp_first;
if (n == mp_last) {
mp_first = mp_last = 0;
} else {
mp_first = mp_first->next;
}
delete n;
--m_size;
}
}
T &front ()
{
return mp_first->t;
}
const T &front () const
{
return mp_first->t;
}
T &back ()
{
return mp_last->t;
}
const T &back () const
{
return mp_last->t;
}
void push_front (const T &t)
{
push_front_impl (new node_type (t));
}
void push_front (T &&t)
{
push_front_impl (new node_type (t));
}
void push_back (const T &t)
{
push_back_impl (new node_type (t));
}
void push_back (T &&t)
{
push_back_impl (new node_type (t));
}
void splice (slist<T> &other)
{
if (! other.mp_first) {
return;
}
if (! mp_first) {
mp_first = other.mp_first;
} else {
mp_last->next = other.mp_first;
}
mp_last = other.mp_last;
m_size += other.m_size;
other.mp_first = other.mp_last = 0;
other.m_size = 0;
}
private:
node_type *mp_first, *mp_last;
size_t m_size;
void push_front_impl (node_type *n)
{
if (mp_first) {
n->next = mp_first;
mp_first = n;
} else {
mp_first = mp_last = n;
}
++m_size;
}
void push_back_impl (node_type *n)
{
if (mp_last) {
mp_last->next = n;
mp_last = n;
} else {
mp_first = mp_last = n;
}
++m_size;
}
};
}
#endif

View File

@ -0,0 +1,192 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2022 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "tlSList.h"
#include "tlUnitTest.h"
#include "tlString.h"
namespace
{
static size_t obj_count = 0;
struct MyClass1
{
MyClass1 (int _n) : n (_n) { ++obj_count; }
MyClass1 (const MyClass1 &other) : n (other.n) { ++obj_count; }
MyClass1 &operator= (const MyClass1 &other) { n = other.n; return *this; }
~MyClass1 () { --obj_count; }
int n;
bool operator== (const MyClass1 &other) const { return n == other.n; }
bool operator< (const MyClass1 &other) const { return n < other.n; }
};
}
template <class C>
static std::string l2s (const tl::slist<C> &l)
{
std::string x;
for (typename tl::slist<C>::const_iterator i = l.begin (); i != l.end (); ++i) {
if (!x.empty ()) {
x += ",";
}
x += tl::to_string (i->n);
}
return x;
}
template <class C>
static std::string l2s_nc (tl::slist<C> &l)
{
std::string x;
for (typename tl::slist<C>::iterator i = l.begin (); i != l.end (); ++i) {
if (!x.empty ()) {
x += ",";
}
x += tl::to_string (i->n);
}
return x;
}
TEST(1_Basic)
{
obj_count = 0;
{
MyClass1 c1 (-1);
tl::slist<MyClass1> l1, l2;
EXPECT_EQ (l1.empty (), true);
EXPECT_EQ (l1.size (), size_t (0));
EXPECT_EQ (l2s (l1), "");
l1.push_back (MyClass1 (17));
EXPECT_EQ (l1.empty (), false);
EXPECT_EQ (l1.size (), size_t (1));
EXPECT_EQ (l2s (l1), "17");
l1.push_back (MyClass1 (42));
l2 = l1;
tl::slist<MyClass1> l3 (l2);
EXPECT_EQ (l1.empty (), false);
EXPECT_EQ (l1.size (), size_t (2));
EXPECT_EQ (l2s (l1), "17,42");
l1.pop_front ();
EXPECT_EQ (l1.empty (), false);
EXPECT_EQ (l1.size (), size_t (1));
EXPECT_EQ (l2s (l1), "42");
l1.clear ();
EXPECT_EQ (l1.empty (), true);
EXPECT_EQ (l1.size (), size_t (0));
EXPECT_EQ (l2s (l1), "");
EXPECT_EQ (l2s (l2), "17,42");
l2.pop_front ();
EXPECT_EQ (l2s (l2), "42");
l3.push_back (MyClass1 (2));
l3.push_front (MyClass1 (1));
EXPECT_EQ (l2s (l3), "1,17,42,2");
EXPECT_EQ (l2s_nc (l3), "1,17,42,2");
EXPECT_EQ (l3.size (), size_t (4));
l3.pop_front ();
EXPECT_EQ (l2s (l3), "17,42,2");
EXPECT_EQ (l3.size (), size_t (3));
c1 = l3.front ();
EXPECT_EQ (c1.n, 17);
c1 = l3.back ();
EXPECT_EQ (c1.n, 2);
l3.pop_front ();
EXPECT_EQ (l2s (l3), "42,2");
EXPECT_EQ (l3.size (), size_t (2));
l3.push_back (MyClass1 (1));
EXPECT_EQ (l2s (l3), "42,2,1");
EXPECT_EQ (l3.size (), size_t (3));
l3.swap (l2);
EXPECT_EQ (l2s (l2), "42,2,1");
EXPECT_EQ (l2s (l3), "42");
l1.clear ();
l2.swap (l1);
EXPECT_EQ (l2s (l1), "42,2,1");
EXPECT_EQ (l2s (l2), "");
l1.clear ();
l3.clear ();
l2.swap (l1);
EXPECT_EQ (l2s (l1), "");
EXPECT_EQ (l2s (l2), "");
}
EXPECT_EQ (obj_count, size_t (0));
}
TEST(2_SpliceAndMove)
{
obj_count = 0;
{
tl::slist<MyClass1> l1, l2;
l1.splice (l2);
EXPECT_EQ (l2s (l1), "");
l1.push_back (MyClass1 (17));
l1.push_back (MyClass1 (42));
l1.splice (l2);
EXPECT_EQ (l2s (l1), "17,42");
EXPECT_EQ (l2s (l2), "");
l2.splice (l1);
EXPECT_EQ (l2s (l2), "17,42");
EXPECT_EQ (l2s (l1), "");
l1.swap (l2);
l2.push_back (MyClass1 (2));
l2.push_back (MyClass1 (1));
l1.splice (l2);
EXPECT_EQ (l2s (l1), "17,42,2,1");
EXPECT_EQ (l2s (l2), "");
l2 = std::move (l1);
EXPECT_EQ (l2s (l2), "17,42,2,1");
EXPECT_EQ (l2s (l1), "");
l1 = tl::slist<MyClass1> (std::move (l2));
EXPECT_EQ (l2s (l1), "17,42,2,1");
EXPECT_EQ (l2s (l2), "");
}
EXPECT_EQ (obj_count, size_t (0));
}

View File

@ -39,6 +39,7 @@ SOURCES = \
tlThreadsTests.cc \
tlUniqueIdTests.cc \
tlListTests.cc \
tlSListTests.cc \
tlEquivalenceClustersTests.cc \
tlUniqueNameTests.cc \
tlGlobPatternTests.cc \