diff options
author | gatecat <gatecat@ds0.me> | 2021-06-01 16:51:18 +0100 |
---|---|---|
committer | gatecat <gatecat@ds0.me> | 2021-06-02 14:27:56 +0100 |
commit | 579b98c5963c2b86d191d481a2147a663a8196dd (patch) | |
tree | a37baaeac305fbb9d3f7db98ccda8a1708ac234c /common | |
parent | ff72454f8391ab4785fa8314f3efbbea96c30422 (diff) | |
download | nextpnr-579b98c5963c2b86d191d481a2147a663a8196dd.tar.gz nextpnr-579b98c5963c2b86d191d481a2147a663a8196dd.tar.bz2 nextpnr-579b98c5963c2b86d191d481a2147a663a8196dd.zip |
Use hashlib for core netlist structures
Signed-off-by: gatecat <gatecat@ds0.me>
Diffstat (limited to 'common')
-rw-r--r-- | common/basectx.h | 26 | ||||
-rw-r--r-- | common/chain_utils.h | 4 | ||||
-rw-r--r-- | common/context.cc | 4 | ||||
-rw-r--r-- | common/hashlib.h | 4 | ||||
-rw-r--r-- | common/nextpnr_types.h | 23 | ||||
-rw-r--r-- | common/place_common.cc | 14 | ||||
-rw-r--r-- | common/placer1.cc | 30 | ||||
-rw-r--r-- | common/placer_heap.cc | 38 | ||||
-rw-r--r-- | common/pybindings.cc | 14 | ||||
-rw-r--r-- | common/router2.cc | 8 | ||||
-rw-r--r-- | common/sdf.cc | 8 | ||||
-rw-r--r-- | common/timing.cc | 14 | ||||
-rw-r--r-- | common/timing_opt.cc | 6 | ||||
-rw-r--r-- | common/util.h | 5 |
14 files changed, 101 insertions, 97 deletions
diff --git a/common/basectx.h b/common/basectx.h index fccd12af..dd48c33c 100644 --- a/common/basectx.h +++ b/common/basectx.h @@ -59,29 +59,29 @@ struct BaseCtx mutable StrRingBuffer log_strs; // Project settings and config switches - std::unordered_map<IdString, Property> settings; + dict<IdString, Property> settings; // Placed nets and cells. - std::unordered_map<IdString, std::unique_ptr<NetInfo>> nets; - std::unordered_map<IdString, std::unique_ptr<CellInfo>> cells; + dict<IdString, std::unique_ptr<NetInfo>> nets; + dict<IdString, std::unique_ptr<CellInfo>> cells; // Hierarchical (non-leaf) cells by full path - std::unordered_map<IdString, HierarchicalCell> hierarchy; + dict<IdString, HierarchicalCell> hierarchy; // This is the root of the above structure IdString top_module; // Aliases for nets, which may have more than one name due to assignments and hierarchy - std::unordered_map<IdString, IdString> net_aliases; + dict<IdString, IdString> net_aliases; // Top-level ports - std::unordered_map<IdString, PortInfo> ports; - std::unordered_map<IdString, CellInfo *> port_cells; + dict<IdString, PortInfo> ports; + dict<IdString, CellInfo *> port_cells; // Floorplanning regions - std::unordered_map<IdString, std::unique_ptr<Region>> region; + dict<IdString, std::unique_ptr<Region>> region; // Context meta data - std::unordered_map<IdString, Property> attrs; + dict<IdString, Property> attrs; Context *as_ctx = nullptr; @@ -186,10 +186,10 @@ struct BaseCtx bool allUiReload = true; bool frameUiReload = false; - std::unordered_set<BelId> belUiReload; - std::unordered_set<WireId> wireUiReload; - std::unordered_set<PipId> pipUiReload; - std::unordered_set<GroupId> groupUiReload; + pool<BelId> belUiReload; + pool<WireId> wireUiReload; + pool<PipId> pipUiReload; + pool<GroupId> groupUiReload; void refreshUi() { allUiReload = true; } diff --git a/common/chain_utils.h b/common/chain_utils.h index 300d96a1..1bd95c9e 100644 --- a/common/chain_utils.h +++ b/common/chain_utils.h @@ -37,10 +37,10 @@ std::vector<CellChain> find_chains(const Context *ctx, F1 cell_type_predicate, F { std::set<IdString> chained; std::vector<CellChain> chains; - for (auto cell : sorted(ctx->cells)) { + for (auto &cell : ctx->cells) { if (chained.find(cell.first) != chained.end()) continue; - CellInfo *ci = cell.second; + CellInfo *ci = cell.second.get(); if (cell_type_predicate(ctx, ci)) { CellInfo *start = ci; CellInfo *prev_start = ci; diff --git a/common/context.cc b/common/context.cc index 05c1e094..115b333a 100644 --- a/common/context.cc +++ b/common/context.cc @@ -389,8 +389,8 @@ struct FixupHierarchyWorker // Update hierarchy structure for nets and cells that have hiercell set void rebuild_hierarchy() { - for (auto cell : sorted(ctx->cells)) { - CellInfo *ci = cell.second; + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); if (ci->hierpath == IdString()) ci->hierpath = ctx->top_module; auto &hc = ctx->hierarchy.at(ci->hierpath); diff --git a/common/hashlib.h b/common/hashlib.h index 48db024f..30fefc65 100644 --- a/common/hashlib.h +++ b/common/hashlib.h @@ -339,6 +339,10 @@ template <typename K, typename T, typename OPS> class dict } public: + using key_type = K; + using mapped_type = T; + using value_type = std::pair<K, T>; + class const_iterator : public std::iterator<std::forward_iterator_tag, std::pair<K, T>> { friend class dict; diff --git a/common/nextpnr_types.h b/common/nextpnr_types.h index 67e60c50..4770f8ae 100644 --- a/common/nextpnr_types.h +++ b/common/nextpnr_types.h @@ -28,6 +28,7 @@ #include <unordered_set> #include "archdefs.h" +#include "hashlib.h" #include "nextpnr_base_types.h" #include "nextpnr_namespaces.h" #include "property.h" @@ -56,9 +57,9 @@ struct Region bool constr_wires = false; bool constr_pips = false; - std::unordered_set<BelId> bels; - std::unordered_set<WireId> wires; - std::unordered_set<Loc> piplocs; + pool<BelId> bels; + pool<WireId> wires; + pool<Loc> piplocs; }; struct PipMap @@ -128,10 +129,10 @@ struct NetInfo : ArchNetInfo PortRef driver; std::vector<PortRef> users; - std::unordered_map<IdString, Property> attrs; + dict<IdString, Property> attrs; // wire -> uphill_pip - std::unordered_map<WireId, PipMap> wires; + dict<WireId, PipMap> wires; std::vector<IdString> aliases; // entries in net_aliases that point to this net @@ -159,8 +160,8 @@ struct CellInfo : ArchCellInfo IdString name, type, hierpath; int32_t udata; - std::unordered_map<IdString, PortInfo> ports; - std::unordered_map<IdString, Property> attrs, params; + dict<IdString, PortInfo> ports; + dict<IdString, Property> attrs, params; BelId bel; PlaceStrength belStrength = STRENGTH_NONE; @@ -232,13 +233,13 @@ struct HierarchicalCell { IdString name, type, parent, fullpath; // Name inside cell instance -> global name - std::unordered_map<IdString, IdString> leaf_cells, nets; + dict<IdString, IdString> leaf_cells, nets; // Global name -> name inside cell instance - std::unordered_map<IdString, IdString> leaf_cells_by_gname, nets_by_gname; + dict<IdString, IdString> leaf_cells_by_gname, nets_by_gname; // Cell port to net - std::unordered_map<IdString, HierarchicalPort> ports; + dict<IdString, HierarchicalPort> ports; // Name inside cell instance -> global name - std::unordered_map<IdString, IdString> hier_cells; + dict<IdString, IdString> hier_cells; }; NEXTPNR_NAMESPACE_END diff --git a/common/place_common.cc b/common/place_common.cc index 7cbeca65..ece47b5a 100644 --- a/common/place_common.cc +++ b/common/place_common.cc @@ -377,9 +377,9 @@ class ConstraintLegaliseWorker public: ConstraintLegaliseWorker(Context *ctx) : ctx(ctx) { - for (auto cell : sorted(ctx->cells)) { + for (auto &cell : ctx->cells) { if (cell.second->cluster != ClusterId()) - cluster2cells[cell.second->cluster].push_back(cell.second); + cluster2cells[cell.second->cluster].push_back(cell.second.get()); } }; @@ -414,11 +414,11 @@ class ConstraintLegaliseWorker int legalise_constraints() { log_info("Legalising relative constraints...\n"); - for (auto cell : sorted(ctx->cells)) { + for (auto &cell : ctx->cells) { oldLocations[cell.first] = ctx->getBelLocation(cell.second->bel); } - for (auto cell : sorted(ctx->cells)) { - bool res = legalise_cell(cell.second); + for (auto &cell : ctx->cells) { + bool res = legalise_cell(cell.second.get()); if (!res) { log_error("failed to place chain starting at cell '%s'\n", cell.first.c_str(ctx)); return -1; @@ -434,8 +434,8 @@ class ConstraintLegaliseWorker } } auto score = print_stats("replacing ripped up cells"); - for (auto cell : sorted(ctx->cells)) - if (get_constraints_distance(ctx, cell.second) != 0) + for (auto &cell : ctx->cells) + if (get_constraints_distance(ctx, cell.second.get()) != 0) log_error("constraint satisfaction check failed for cell '%s' at Bel '%s'\n", cell.first.c_str(ctx), ctx->nameOfBel(cell.second->bel)); return score; diff --git a/common/placer1.cc b/common/placer1.cc index a3e7a696..f9cef92f 100644 --- a/common/placer1.cc +++ b/common/placer1.cc @@ -88,7 +88,7 @@ class SAPlacer diameter = std::max(max_x, max_y) + 1; std::unordered_set<IdString> cell_types_in_use; - for (auto cell : sorted(ctx->cells)) { + for (auto &cell : ctx->cells) { IdString cell_type = cell.second->type; cell_types_in_use.insert(cell_type); } @@ -108,8 +108,8 @@ class SAPlacer net.second->udata = n++; net_by_udata.push_back(net.second.get()); } - for (auto ®ion : sorted(ctx->region)) { - Region *r = region.second; + for (auto ®ion : ctx->region) { + Region *r = region.second.get(); BoundingBox bb; if (r->constr_bels) { bb.x0 = std::numeric_limits<int>::max(); @@ -360,12 +360,12 @@ class SAPlacer // Only increase temperature if something was moved autoplaced.clear(); chain_basis.clear(); - for (auto cell : sorted(ctx->cells)) { + for (auto &cell : ctx->cells) { if (cell.second->belStrength <= STRENGTH_STRONG && cell.second->cluster != ClusterId() && - ctx->getClusterRootCell(cell.second->cluster) == cell.second) - chain_basis.push_back(cell.second); + ctx->getClusterRootCell(cell.second->cluster) == cell.second.get()) + chain_basis.push_back(cell.second.get()); else if (cell.second->belStrength < STRENGTH_STRONG) - autoplaced.push_back(cell.second); + autoplaced.push_back(cell.second.get()); } // temp = post_legalise_temp; // diameter = std::min<int>(M, diameter * post_legalise_dia_scale); @@ -421,8 +421,8 @@ class SAPlacer } } } - for (auto cell : sorted(ctx->cells)) - if (get_constraints_distance(ctx, cell.second) != 0) + for (auto &cell : ctx->cells) + if (get_constraints_distance(ctx, cell.second.get()) != 0) log_error("constraint satisfaction check failed for cell '%s' at Bel '%s'\n", cell.first.c_str(ctx), ctx->nameOfBel(cell.second->bel)); timing_analysis(ctx); @@ -831,8 +831,8 @@ class SAPlacer // Set up the cost maps void setup_costs() { - for (auto net : sorted(ctx->nets)) { - NetInfo *ni = net.second; + for (auto &net : ctx->nets) { + NetInfo *ni = net.second.get(); if (ignore_net(ni)) continue; net_bounds[ni->udata] = get_net_bounds(ni); @@ -1118,8 +1118,8 @@ class SAPlacer // Build the cell port -> user index void build_port_index() { - for (auto net : sorted(ctx->nets)) { - NetInfo *ni = net.second; + for (auto &net : ctx->nets) { + NetInfo *ni = net.second.get(); for (size_t i = 0; i < ni->users.size(); i++) { auto &usr = ni->users.at(i); fast_port_to_user[&(usr.cell->ports.at(usr.port))] = i; @@ -1135,8 +1135,8 @@ class SAPlacer { total_net_share = 0; nets_by_tile.resize(max_x + 1, std::vector<std::unordered_map<IdString, int>>(max_y + 1)); - for (auto cell : sorted(ctx->cells)) { - CellInfo *ci = cell.second; + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); if (int(ci->ports.size()) > large_cell_thresh) continue; Loc loc = ctx->getBelLocation(ci->bel); diff --git a/common/placer_heap.cc b/common/placer_heap.cc index 2f7c7ccb..c26e1556 100644 --- a/common/placer_heap.cc +++ b/common/placer_heap.cc @@ -146,9 +146,9 @@ class HeAPPlacer tmg.setup_only = true; tmg.setup(); - for (auto cell : sorted(ctx->cells)) + for (auto &cell : ctx->cells) if (cell.second->cluster != ClusterId()) - cluster2cells[cell.second->cluster].push_back(cell.second); + cluster2cells[cell.second->cluster].push_back(cell.second.get()); } bool place() @@ -283,8 +283,8 @@ class HeAPPlacer stalled = 0; // Save solution solution.clear(); - for (auto cell : sorted(ctx->cells)) { - solution.emplace_back(cell.second, cell.second->bel, cell.second->belStrength); + for (auto &cell : ctx->cells) { + solution.emplace_back(cell.second.get(), cell.second->bel, cell.second->belStrength); } } else { ++stalled; @@ -311,10 +311,10 @@ class HeAPPlacer ctx->bindBel(bel, cell, strength); } - for (auto cell : sorted(ctx->cells)) { + for (auto &cell : ctx->cells) { if (cell.second->bel == BelId()) log_error("Found unbound cell %s\n", cell.first.c_str(ctx)); - if (ctx->getBoundBelCell(cell.second->bel) != cell.second) + if (ctx->getBoundBelCell(cell.second->bel) != cell.second.get()) log_error("Found cell %s with mismatched binding\n", cell.first.c_str(ctx)); if (ctx->debug) log_info("AP soln: %s -> %s\n", cell.first.c_str(ctx), ctx->nameOfBel(cell.second->bel)); @@ -450,7 +450,7 @@ class HeAPPlacer std::unordered_set<IdString> cell_types_in_use; std::unordered_set<BelBucketId> buckets_in_use; - for (auto cell : sorted(ctx->cells)) { + for (auto &cell : ctx->cells) { IdString cell_type = cell.second->type; cell_types_in_use.insert(cell_type); BelBucketId bucket = ctx->getBelBucketForCellType(cell_type); @@ -465,8 +465,8 @@ class HeAPPlacer } // Determine bounding boxes of region constraints - for (auto ®ion : sorted(ctx->region)) { - Region *r = region.second; + for (auto ®ion : ctx->region) { + Region *r = region.second.get(); BoundingBox bb; if (r->constr_bels) { bb.x0 = std::numeric_limits<int>::max(); @@ -539,8 +539,8 @@ class HeAPPlacer ctx->shuffle(t.second.begin(), t.second.end()); } - for (auto cell : sorted(ctx->cells)) { - CellInfo *ci = cell.second; + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); if (ci->bel != BelId()) { Loc loc = ctx->getBelLocation(ci->bel); cell_locs[cell.first].x = loc.x; @@ -591,7 +591,7 @@ class HeAPPlacer cell_locs[cell.first].global = ctx->getBelGlobalBuf(bel); // FIXME - if (has_connectivity(cell.second) && !cfg.ioBufTypes.count(ci->type)) { + if (has_connectivity(cell.second.get()) && !cfg.ioBufTypes.count(ci->type)) { bels_used.insert(bel); place_cells.push_back(ci); placed = true; @@ -617,7 +617,7 @@ class HeAPPlacer int row = 0; solve_cells.clear(); // First clear the udata of all cells - for (auto cell : sorted(ctx->cells)) + for (auto &cell : ctx->cells) cell.second->udata = dont_solve; // Then update cells to be placed, which excludes cell children for (auto cell : place_cells) { @@ -671,8 +671,8 @@ class HeAPPlacer es.reset(); - for (auto net : sorted(ctx->nets)) { - NetInfo *ni = net.second; + for (auto &net : ctx->nets) { + NetInfo *ni = net.second.get(); if (ni->driver.cell == nullptr) continue; if (ni->users.empty()) @@ -783,8 +783,8 @@ class HeAPPlacer wirelen_t total_hpwl() { wirelen_t hpwl = 0; - for (auto net : sorted(ctx->nets)) { - NetInfo *ni = net.second; + for (auto &net : ctx->nets) { + NetInfo *ni = net.second.get(); if (ni->driver.cell == nullptr) continue; CellLocation &drvloc = cell_locs.at(ni->driver.cell->name); @@ -809,8 +809,8 @@ class HeAPPlacer auto startt = std::chrono::high_resolution_clock::now(); // Unbind all cells placed in this solution - for (auto cell : sorted(ctx->cells)) { - CellInfo *ci = cell.second; + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); if (ci->bel != BelId() && (ci->udata != dont_solve || (ci->cluster != ClusterId() && ctx->getClusterRootCell(ci->cluster)->udata != dont_solve))) diff --git a/common/pybindings.cc b/common/pybindings.cc index 504074e1..00ebe66e 100644 --- a/common/pybindings.cc +++ b/common/pybindings.cc @@ -164,10 +164,10 @@ PYBIND11_EMBEDDED_MODULE(MODULE_NAME, m) .def("maxFallDelay", &DelayQuad::maxFallDelay) .def("delayPair", &DelayQuad::delayPair); - typedef std::unordered_map<IdString, Property> AttrMap; - typedef std::unordered_map<IdString, PortInfo> PortMap; - typedef std::unordered_map<IdString, IdString> IdIdMap; - typedef std::unordered_map<IdString, std::unique_ptr<Region>> RegionMap; + typedef dict<IdString, Property> AttrMap; + typedef dict<IdString, PortInfo> PortMap; + typedef dict<IdString, IdString> IdIdMap; + typedef dict<IdString, std::unique_ptr<Region>> RegionMap; py::class_<BaseCtx>(m, "BaseCtx"); @@ -218,9 +218,9 @@ PYBIND11_EMBEDDED_MODULE(MODULE_NAME, m) pass_through<PortType>>::def_wrap(pi_cls, "type"); typedef std::vector<PortRef> PortRefVector; - typedef std::unordered_map<WireId, PipMap> WireMap; - typedef std::unordered_set<BelId> BelSet; - typedef std::unordered_set<WireId> WireSet; + typedef dict<WireId, PipMap> WireMap; + typedef pool<BelId> BelSet; + typedef pool<WireId> WireSet; auto ni_cls = py::class_<ContextualWrapper<NetInfo &>>(m, "NetInfo"); readwrite_wrapper<NetInfo &, decltype(&NetInfo::name), &NetInfo::name, conv_to_str<IdString>, diff --git a/common/router2.cc b/common/router2.cc index 2156ce28..b0f53ce1 100644 --- a/common/router2.cc +++ b/common/router2.cc @@ -131,8 +131,8 @@ struct Router2 nets.resize(ctx->nets.size()); nets_by_udata.resize(ctx->nets.size()); size_t i = 0; - for (auto net : sorted(ctx->nets)) { - NetInfo *ni = net.second; + for (auto &net : ctx->nets) { + NetInfo *ni = net.second.get(); ni->udata = i; nets_by_udata.at(i) = ni; nets.at(i).arcs.resize(ni->users.size()); @@ -231,8 +231,8 @@ struct Router2 flat_wires.push_back(pwd); } - for (auto net_pair : sorted(ctx->nets)) { - auto *net = net_pair.second; + for (auto &net_pair : ctx->nets) { + auto *net = net_pair.second.get(); auto &nd = nets.at(net->udata); for (size_t usr = 0; usr < net->users.size(); usr++) { auto &ad = nd.arcs.at(usr); diff --git a/common/sdf.cc b/common/sdf.cc index 5c3d0a5a..814bf09a 100644 --- a/common/sdf.cc +++ b/common/sdf.cc @@ -254,9 +254,9 @@ void Context::writeSDF(std::ostream &out, bool cvc_mode) const return rf; }; - for (auto cell : sorted(cells)) { + for (const auto &cell : cells) { Cell sc; - const CellInfo *ci = cell.second; + const CellInfo *ci = cell.second.get(); sc.instance = ci->name.str(this); sc.celltype = ci->type.str(this); for (auto port : ci->ports) { @@ -313,8 +313,8 @@ void Context::writeSDF(std::ostream &out, bool cvc_mode) const wr.cells.push_back(sc); } - for (auto net : sorted(nets)) { - NetInfo *ni = net.second; + for (auto &net : nets) { + NetInfo *ni = net.second.get(); if (ni->driver.cell == nullptr) continue; for (auto &usr : ni->users) { diff --git a/common/timing.cc b/common/timing.cc index ef5977de..b68ca35c 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -52,17 +52,17 @@ void TimingAnalyser::run() void TimingAnalyser::init_ports() { // Per cell port structures - for (auto cell : sorted(ctx->cells)) { - CellInfo *ci = cell.second; - for (auto port : sorted_ref(ci->ports)) { + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + for (auto &port : ci->ports) { auto &data = ports[CellPortKey(ci->name, port.first)]; data.type = port.second.type; data.cell_port = CellPortKey(ci->name, port.first); } } // Cell port to net port mapping - for (auto net : sorted(ctx->nets)) { - NetInfo *ni = net.second; + for (auto &net : ctx->nets) { + NetInfo *ni = net.second.get(); if (ni->driver.cell != nullptr) ports[CellPortKey(ni->driver)].net_port = NetPortKey(ni->name); for (size_t i = 0; i < ni->users.size(); i++) @@ -138,8 +138,8 @@ void TimingAnalyser::get_cell_delays() void TimingAnalyser::get_route_delays() { - for (auto net : sorted(ctx->nets)) { - NetInfo *ni = net.second; + for (auto &net : ctx->nets) { + NetInfo *ni = net.second.get(); if (ni->driver.cell == nullptr || ni->driver.cell->bel == BelId()) continue; for (auto &usr : ni->users) { diff --git a/common/timing_opt.cc b/common/timing_opt.cc index 854cbc5b..2659f04e 100644 --- a/common/timing_opt.cc +++ b/common/timing_opt.cc @@ -68,8 +68,8 @@ class TimingOptimiser void setup_delay_limits() { max_net_delay.clear(); - for (auto net : sorted(ctx->nets)) { - NetInfo *ni = net.second; + for (auto &net : ctx->nets) { + NetInfo *ni = net.second.get(); if (ni->driver.cell == nullptr) continue; for (auto usr : ni->users) { @@ -239,7 +239,7 @@ class TimingOptimiser std::vector<std::pair<NetInfo *, int>> crit_nets; std::vector<IdString> netnames; std::transform(ctx->nets.begin(), ctx->nets.end(), std::back_inserter(netnames), - [](const std::pair<const IdString, std::unique_ptr<NetInfo>> &kv) { return kv.first; }); + [](const std::pair<IdString, std::unique_ptr<NetInfo>> &kv) { return kv.first; }); ctx->sorted_shuffle(netnames); for (auto net : netnames) { if (crit_nets.size() >= max_count) diff --git a/common/util.h b/common/util.h index 540646c7..b3e8cbf0 100644 --- a/common/util.h +++ b/common/util.h @@ -55,7 +55,7 @@ std::string str_or_default(const Container &ct, const KeyType &key, std::string }; template <typename KeyType> -std::string str_or_default(const std::unordered_map<KeyType, Property> &ct, const KeyType &key, std::string def = "") +std::string str_or_default(const dict<KeyType, Property> &ct, const KeyType &key, std::string def = "") { auto found = ct.find(key); if (found == ct.end()) @@ -78,8 +78,7 @@ template <typename Container, typename KeyType> int int_or_default(const Contain return std::stoi(found->second); }; -template <typename KeyType> -int int_or_default(const std::unordered_map<KeyType, Property> &ct, const KeyType &key, int def = 0) +template <typename KeyType> int int_or_default(const dict<KeyType, Property> &ct, const KeyType &key, int def = 0) { auto found = ct.find(key); if (found == ct.end()) |