mirror of
https://github.com/VectorCamp/vectorscan.git
synced 2025-06-28 16:41:01 +03:00
insertion_ordered_{map,set}: add new containers
These are associative map/set structures that are iterable in insertion order.
This commit is contained in:
parent
72973ccb47
commit
3ff70d5568
@ -1007,6 +1007,7 @@ SET (hs_compile_SRCS
|
||||
src/util/graph_small_color_map.h
|
||||
src/util/hash.h
|
||||
src/util/hash_dynamic_bitset.h
|
||||
src/util/insertion_ordered.h
|
||||
src/util/math.h
|
||||
src/util/multibit_build.cpp
|
||||
src/util/multibit_build.h
|
||||
|
@ -60,6 +60,7 @@
|
||||
#include "util/flat_containers.h"
|
||||
#include "util/graph.h"
|
||||
#include "util/graph_range.h"
|
||||
#include "util/insertion_ordered.h"
|
||||
#include "util/make_unique.h"
|
||||
#include "util/order_check.h"
|
||||
#include "util/target_info.h"
|
||||
@ -1076,24 +1077,21 @@ bool splitRoseEdge(const NGHolder &base_graph, RoseInGraph &vg,
|
||||
insert(&splitter_reports, base_graph[v].reports);
|
||||
}
|
||||
|
||||
/* find the targets of each source vertex; note the use of vectors to
|
||||
/* find the targets of each source vertex; insertion_ordered_map used to
|
||||
* preserve deterministic ordering */
|
||||
vector<RoseInVertex> sources;
|
||||
map<RoseInVertex, vector<RoseInVertex>> images;
|
||||
insertion_ordered_map<RoseInVertex, vector<RoseInVertex>> images;
|
||||
for (const RoseInEdge &e : ee) {
|
||||
RoseInVertex src = source(e, vg);
|
||||
RoseInVertex dest = target(e, vg);
|
||||
if (!contains(images, src)) {
|
||||
sources.push_back(src);
|
||||
}
|
||||
images[src].push_back(dest);
|
||||
remove_edge(e, vg);
|
||||
}
|
||||
|
||||
map<vector<RoseInVertex>, vector<RoseInVertex>> verts_by_image;
|
||||
|
||||
for (const auto &u : sources) {
|
||||
const auto &image = images[u];
|
||||
for (const auto &m : images) {
|
||||
const auto &u = m.first;
|
||||
const auto &image = m.second;
|
||||
|
||||
if (contains(verts_by_image, image)) {
|
||||
for (RoseInVertex v : verts_by_image[image]) {
|
||||
@ -1743,8 +1741,7 @@ void removeRedundantLiteralsFromInfix(const NGHolder &h, RoseInGraph &ig,
|
||||
static
|
||||
void removeRedundantLiteralsFromInfixes(RoseInGraph &g,
|
||||
const CompileContext &cc) {
|
||||
vector<NGHolder *> seen_order;
|
||||
map<NGHolder *, vector<RoseInEdge>> infixes;
|
||||
insertion_ordered_map<NGHolder *, vector<RoseInEdge>> infixes;
|
||||
|
||||
for (const RoseInEdge &e : edges_range(g)) {
|
||||
RoseInVertex s = source(e, g);
|
||||
@ -1766,14 +1763,13 @@ void removeRedundantLiteralsFromInfixes(RoseInGraph &g,
|
||||
}
|
||||
|
||||
NGHolder *h = g[e].graph.get();
|
||||
if (!contains(infixes, h)) {
|
||||
seen_order.push_back(h);
|
||||
}
|
||||
infixes[h].push_back(e);
|
||||
}
|
||||
|
||||
for (NGHolder *h : seen_order) {
|
||||
removeRedundantLiteralsFromInfix(*h, g, infixes[h], cc);
|
||||
for (const auto &m : infixes) {
|
||||
NGHolder *h = m.first;
|
||||
const auto &edges = m.second;
|
||||
removeRedundantLiteralsFromInfix(*h, g, edges, cc);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2088,13 +2084,13 @@ void findBetterPrefixes(RoseInGraph &vg, const CompileContext &cc) {
|
||||
STAGE_DEBUG_PRINTF("FIND BETTER PREFIXES\n");
|
||||
RoseInVertex start = getStart(vg);
|
||||
|
||||
insertion_ordered_map<NGHolder *, vector<RoseInEdge>> prefixes;
|
||||
bool changed;
|
||||
u32 gen = 0;
|
||||
do {
|
||||
DEBUG_PRINTF("gen %u\n", gen);
|
||||
changed = false;
|
||||
vector<NGHolder *> seen_order;
|
||||
map<NGHolder *, vector<RoseInEdge> > prefixes;
|
||||
prefixes.clear();
|
||||
|
||||
/* find prefixes */
|
||||
for (const RoseInEdge &e : out_edges_range(start, vg)) {
|
||||
@ -2102,9 +2098,6 @@ void findBetterPrefixes(RoseInGraph &vg, const CompileContext &cc) {
|
||||
assert(vg[target(e, vg)].type == RIV_LITERAL);
|
||||
if (vg[e].graph) {
|
||||
NGHolder *h = vg[e].graph.get();
|
||||
if (!contains(prefixes, h)) {
|
||||
seen_order.push_back(h);
|
||||
}
|
||||
prefixes[h].push_back(e);
|
||||
}
|
||||
}
|
||||
@ -2114,14 +2107,16 @@ void findBetterPrefixes(RoseInGraph &vg, const CompileContext &cc) {
|
||||
}
|
||||
|
||||
/* look for bad prefixes and try to split */
|
||||
for (NGHolder *h : seen_order) {
|
||||
for (const auto &m : prefixes) {
|
||||
NGHolder *h = m.first;
|
||||
const auto &edges = m.second;
|
||||
depth max_width = findMaxWidth(*h);
|
||||
if (willBeTransient(max_width, cc)
|
||||
|| willBeAnchoredTable(max_width, cc.grey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
changed = improvePrefix(*h, vg, prefixes[h], cc);
|
||||
changed = improvePrefix(*h, vg, edges, cc);
|
||||
}
|
||||
} while (changed && gen++ < MAX_FIND_BETTER_PREFIX_GEN);
|
||||
}
|
||||
@ -2149,24 +2144,25 @@ void extractStrongLiterals(RoseInGraph &vg, const CompileContext &cc) {
|
||||
if (!cc.grey.violetExtractStrongLiterals) {
|
||||
return;
|
||||
}
|
||||
STAGE_DEBUG_PRINTF("EXTRACT STRONG LITERALS\n");
|
||||
set<NGHolder *> stuck;
|
||||
|
||||
STAGE_DEBUG_PRINTF("EXTRACT STRONG LITERALS\n");
|
||||
|
||||
unordered_set<NGHolder *> stuck;
|
||||
insertion_ordered_map<NGHolder *, vector<RoseInEdge>> edges_by_graph;
|
||||
bool changed;
|
||||
|
||||
do {
|
||||
changed = false;
|
||||
|
||||
vector<NGHolder *> seen_order;
|
||||
map<NGHolder *, vector<RoseInEdge> > edges_by_graph;
|
||||
edges_by_graph.clear();
|
||||
for (const RoseInEdge &ve : edges_range(vg)) {
|
||||
if (vg[source(ve, vg)].type != RIV_LITERAL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (vg[ve].graph) {
|
||||
if (!contains(edges_by_graph, vg[ve].graph.get())) {
|
||||
seen_order.push_back(vg[ve].graph.get());
|
||||
}
|
||||
edges_by_graph[vg[ve].graph.get()].push_back(ve);
|
||||
NGHolder *h = vg[ve].graph.get();
|
||||
edges_by_graph[h].push_back(ve);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2175,12 +2171,14 @@ void extractStrongLiterals(RoseInGraph &vg, const CompileContext &cc) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (NGHolder *g : seen_order) {
|
||||
for (const auto &m : edges_by_graph) {
|
||||
NGHolder *g = m.first;
|
||||
const auto &edges = m.second;
|
||||
if (contains(stuck, g)) {
|
||||
DEBUG_PRINTF("already known to be bad\n");
|
||||
continue;
|
||||
}
|
||||
bool rv = extractStrongLiteral(*g, vg, edges_by_graph[g], cc);
|
||||
bool rv = extractStrongLiteral(*g, vg, edges, cc);
|
||||
if (rv) {
|
||||
changed = true;
|
||||
} else {
|
||||
@ -2228,8 +2226,7 @@ void improveWeakInfixes(RoseInGraph &vg, const CompileContext &cc) {
|
||||
|
||||
RoseInVertex start = getStart(vg);
|
||||
|
||||
set<NGHolder *> weak;
|
||||
vector<NGHolder *> ordered_weak;
|
||||
unordered_set<NGHolder *> weak;
|
||||
|
||||
for (RoseInVertex vv : adjacent_vertices_range(start, vg)) {
|
||||
/* outfixes shouldn't have made it this far */
|
||||
@ -2245,22 +2242,22 @@ void improveWeakInfixes(RoseInGraph &vg, const CompileContext &cc) {
|
||||
|
||||
NGHolder *h = vg[e].graph.get();
|
||||
DEBUG_PRINTF("'%s' guards %p\n", dumpString(vg[vv].s).c_str(), h);
|
||||
if (!contains(weak, h)) {
|
||||
weak.insert(h);
|
||||
ordered_weak.push_back(h);
|
||||
}
|
||||
weak.insert(h);
|
||||
}
|
||||
}
|
||||
|
||||
map<NGHolder *, vector<RoseInEdge> > weak_edges;
|
||||
insertion_ordered_map<NGHolder *, vector<RoseInEdge>> weak_edges;
|
||||
for (const RoseInEdge &ve : edges_range(vg)) {
|
||||
if (contains(weak, vg[ve].graph.get())) {
|
||||
weak_edges[vg[ve].graph.get()].push_back(ve);
|
||||
NGHolder *h = vg[ve].graph.get();
|
||||
if (contains(weak, h)) {
|
||||
weak_edges[h].push_back(ve);
|
||||
}
|
||||
}
|
||||
|
||||
for (NGHolder *h : ordered_weak) {
|
||||
improveInfix(*h, vg, weak_edges[h], cc);
|
||||
for (const auto &m : weak_edges) {
|
||||
NGHolder *h = m.first;
|
||||
const auto &edges = m.second;
|
||||
improveInfix(*h, vg, edges, cc);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2416,8 +2413,8 @@ void avoidSuffixes(RoseInGraph &vg, const CompileContext &cc) {
|
||||
STAGE_DEBUG_PRINTF("AVOID SUFFIXES\n");
|
||||
|
||||
RoseInVertex accept = getPrimaryAccept(vg);
|
||||
map<const NGHolder *, vector<RoseInEdge> > suffixes;
|
||||
vector<const NGHolder *> ordered_suffixes;
|
||||
|
||||
insertion_ordered_map<const NGHolder *, vector<RoseInEdge>> suffixes;
|
||||
|
||||
/* find suffixes */
|
||||
for (const RoseInEdge &e : in_edges_range(accept, vg)) {
|
||||
@ -2426,15 +2423,14 @@ void avoidSuffixes(RoseInGraph &vg, const CompileContext &cc) {
|
||||
assert(vg[e].graph); /* non suffix paths should be wired to other
|
||||
accepts */
|
||||
const NGHolder *h = vg[e].graph.get();
|
||||
if (!contains(suffixes, h)) {
|
||||
ordered_suffixes.push_back(h);
|
||||
}
|
||||
suffixes[h].push_back(e);
|
||||
}
|
||||
|
||||
/* look at suffixes and try to split */
|
||||
for (const NGHolder *h : ordered_suffixes) {
|
||||
replaceSuffixWithInfix(*h, vg, suffixes[h], cc);
|
||||
for (const auto &m : suffixes) {
|
||||
const NGHolder *h = m.first;
|
||||
const auto &edges = m.second;
|
||||
replaceSuffixWithInfix(*h, vg, edges, cc);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2518,20 +2514,18 @@ void lookForDoubleCut(RoseInGraph &vg, const CompileContext &cc) {
|
||||
return;
|
||||
}
|
||||
|
||||
map<const NGHolder *, vector<RoseInEdge> > right_edges;
|
||||
vector<const NGHolder *> ordered_graphs;
|
||||
insertion_ordered_map<const NGHolder *, vector<RoseInEdge>> right_edges;
|
||||
for (const RoseInEdge &ve : edges_range(vg)) {
|
||||
if (vg[ve].graph && vg[source(ve, vg)].type == RIV_LITERAL) {
|
||||
const NGHolder *h = vg[ve].graph.get();
|
||||
if (!contains(right_edges, h)) {
|
||||
ordered_graphs.push_back(h);
|
||||
}
|
||||
right_edges[h].push_back(ve);
|
||||
}
|
||||
}
|
||||
|
||||
for (const NGHolder *h : ordered_graphs) {
|
||||
lookForDoubleCut(*h, right_edges[h], vg, cc.grey);
|
||||
for (const auto &m : right_edges) {
|
||||
const NGHolder *h = m.first;
|
||||
const auto &edges = m.second;
|
||||
lookForDoubleCut(*h, edges, vg, cc.grey);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2656,24 +2650,22 @@ void decomposeLiteralChains(RoseInGraph &vg, const CompileContext &cc) {
|
||||
return;
|
||||
}
|
||||
|
||||
insertion_ordered_map<const NGHolder *, vector<RoseInEdge>> right_edges;
|
||||
bool changed;
|
||||
do {
|
||||
changed = false;
|
||||
|
||||
map<const NGHolder *, vector<RoseInEdge> > right_edges;
|
||||
vector<const NGHolder *> ordered_graphs;
|
||||
right_edges.clear();
|
||||
for (const RoseInEdge &ve : edges_range(vg)) {
|
||||
if (vg[ve].graph && vg[source(ve, vg)].type == RIV_LITERAL) {
|
||||
const NGHolder *h = vg[ve].graph.get();
|
||||
if (!contains(right_edges, h)) {
|
||||
ordered_graphs.push_back(h);
|
||||
}
|
||||
right_edges[h].push_back(ve);
|
||||
}
|
||||
}
|
||||
|
||||
for (const NGHolder *h : ordered_graphs) {
|
||||
const vector<RoseInEdge> &ee = right_edges[h];
|
||||
for (const auto &m : right_edges) {
|
||||
const NGHolder *h = m.first;
|
||||
const vector<RoseInEdge> &ee = m.second;
|
||||
bool rv = lookForDoubleCut(*h, ee, vg, cc.grey);
|
||||
if (!rv && h->kind != NFA_SUFFIX) {
|
||||
rv = lookForTrailingLiteralDotStar(*h, ee, vg, cc.grey);
|
||||
@ -2701,39 +2693,34 @@ static
|
||||
void lookForCleanEarlySplits(RoseInGraph &vg, const CompileContext &cc) {
|
||||
u32 gen = 0;
|
||||
|
||||
vector<RoseInVertex> prev = {getStart(vg)};
|
||||
insertion_ordered_set<RoseInVertex> prev({getStart(vg)});
|
||||
insertion_ordered_set<RoseInVertex> curr;
|
||||
|
||||
while (gen < MAX_DESIRED_CLEAN_SPLIT_DEPTH) {
|
||||
/* collect vertices in edge order for determinism */
|
||||
vector<RoseInVertex> curr;
|
||||
set<RoseInVertex> curr_seen;
|
||||
curr.clear();
|
||||
for (RoseInVertex u : prev) {
|
||||
for (auto v : adjacent_vertices_range(u, vg)) {
|
||||
if (curr_seen.insert(v).second) {
|
||||
curr.push_back(v);
|
||||
}
|
||||
curr.insert(v);
|
||||
}
|
||||
}
|
||||
|
||||
map<const NGHolder *, vector<RoseInEdge>> rightfixes;
|
||||
vector<NGHolder *> ordered_graphs;
|
||||
insertion_ordered_map<const NGHolder *, vector<RoseInEdge>> rightfixes;
|
||||
for (RoseInVertex v : curr) {
|
||||
for (const RoseInEdge &e : out_edges_range(v, vg)) {
|
||||
if (vg[e].graph) {
|
||||
NGHolder *h = vg[e].graph.get();
|
||||
if (!contains(rightfixes, h)) {
|
||||
ordered_graphs.push_back(h);
|
||||
}
|
||||
rightfixes[h].push_back(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const NGHolder *h : ordered_graphs) {
|
||||
lookForCleanSplit(*h, rightfixes[h], vg, cc);
|
||||
for (const auto &m : rightfixes) {
|
||||
const NGHolder *h = m.first;
|
||||
const auto &edges = m.second;
|
||||
lookForCleanSplit(*h, edges, vg, cc);
|
||||
}
|
||||
|
||||
prev = curr;
|
||||
prev = std::move(curr);
|
||||
gen++;
|
||||
}
|
||||
}
|
||||
@ -2907,18 +2894,16 @@ bool ensureImplementable(RoseBuild &rose, RoseInGraph &vg, bool allow_changes,
|
||||
do {
|
||||
changed = false;
|
||||
DEBUG_PRINTF("added %u\n", added_count);
|
||||
map<const NGHolder *, vector<RoseInEdge> > edges_by_graph;
|
||||
vector<shared_ptr<NGHolder>> graphs;
|
||||
insertion_ordered_map<shared_ptr<NGHolder>,
|
||||
vector<RoseInEdge>> edges_by_graph;
|
||||
for (const RoseInEdge &ve : edges_range(vg)) {
|
||||
if (vg[ve].graph && !vg[ve].dfa) {
|
||||
auto &h = vg[ve].graph;
|
||||
if (!contains(edges_by_graph, h.get())) {
|
||||
graphs.push_back(h);
|
||||
}
|
||||
edges_by_graph[h.get()].push_back(ve);
|
||||
edges_by_graph[h].push_back(ve);
|
||||
}
|
||||
}
|
||||
for (auto &h : graphs) {
|
||||
for (auto &m : edges_by_graph) {
|
||||
auto &h = m.first;
|
||||
if (contains(good, h)) {
|
||||
continue;
|
||||
}
|
||||
@ -2928,9 +2913,10 @@ bool ensureImplementable(RoseBuild &rose, RoseInGraph &vg, bool allow_changes,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tryForEarlyDfa(*h, cc)
|
||||
&& doEarlyDfa(rose, vg, *h, edges_by_graph[h.get()],
|
||||
final_chance, rm, cc)) {
|
||||
const auto &edges = m.second;
|
||||
|
||||
if (tryForEarlyDfa(*h, cc) &&
|
||||
doEarlyDfa(rose, vg, *h, edges, final_chance, rm, cc)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -2939,7 +2925,7 @@ bool ensureImplementable(RoseBuild &rose, RoseInGraph &vg, bool allow_changes,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (splitForImplementability(vg, *h, edges_by_graph[h.get()], cc)) {
|
||||
if (splitForImplementability(vg, *h, edges, cc)) {
|
||||
added_count++;
|
||||
if (added_count > MAX_IMPLEMENTABLE_SPLITS) {
|
||||
DEBUG_PRINTF("added_count hit limit\n");
|
||||
|
@ -55,6 +55,7 @@
|
||||
#include "util/container.h"
|
||||
#include "util/dump_charclass.h"
|
||||
#include "util/graph_range.h"
|
||||
#include "util/insertion_ordered.h"
|
||||
#include "util/make_unique.h"
|
||||
#include "util/noncopyable.h"
|
||||
#include "util/order_check.h"
|
||||
@ -1525,8 +1526,7 @@ bool RoseBuildImpl::addRose(const RoseInGraph &ig, bool prefilter) {
|
||||
renumber_vertices(in);
|
||||
assert(validateKinds(in));
|
||||
|
||||
map<NGHolder *, vector<RoseInEdge> > graphs;
|
||||
vector<NGHolder *> ordered_graphs; // Stored in first-encounter order.
|
||||
insertion_ordered_map<NGHolder *, vector<RoseInEdge>> graphs;
|
||||
|
||||
for (const auto &e : edges_range(in)) {
|
||||
if (!in[e].graph) {
|
||||
@ -1544,21 +1544,17 @@ bool RoseBuildImpl::addRose(const RoseInGraph &ig, bool prefilter) {
|
||||
NGHolder *h = in[e].graph.get();
|
||||
|
||||
assert(isCorrectlyTopped(*h));
|
||||
if (!contains(graphs, h)) {
|
||||
ordered_graphs.push_back(h);
|
||||
}
|
||||
graphs[h].push_back(e);
|
||||
}
|
||||
|
||||
assert(ordered_graphs.size() == graphs.size());
|
||||
|
||||
vector<RoseInEdge> graph_edges;
|
||||
|
||||
for (auto h : ordered_graphs) {
|
||||
for (const auto &m : graphs) {
|
||||
NGHolder *h = m.first;
|
||||
if (!canImplementGraph(*h, prefilter, rm, cc)) {
|
||||
return false;
|
||||
}
|
||||
insert(&graph_edges, graph_edges.end(), graphs[h]);
|
||||
insert(&graph_edges, graph_edges.end(), m.second);
|
||||
}
|
||||
|
||||
/* we are now past the point of no return. We can start making irreversible
|
||||
|
@ -86,6 +86,7 @@
|
||||
#include "util/container.h"
|
||||
#include "util/fatbit_build.h"
|
||||
#include "util/graph_range.h"
|
||||
#include "util/insertion_ordered.h"
|
||||
#include "util/make_unique.h"
|
||||
#include "util/multibit_build.h"
|
||||
#include "util/noncopyable.h"
|
||||
@ -1474,11 +1475,11 @@ bool buildLeftfixes(RoseBuildImpl &tbi, build_context &bc,
|
||||
RoseGraph &g = tbi.g;
|
||||
const CompileContext &cc = tbi.cc;
|
||||
|
||||
map<left_id, set<PredTopPair> > infixTriggers;
|
||||
vector<left_id> order;
|
||||
unordered_map<left_id, vector<RoseVertex>> succs;
|
||||
map<left_id, set<PredTopPair>> infixTriggers;
|
||||
findInfixTriggers(tbi, &infixTriggers);
|
||||
|
||||
insertion_ordered_map<left_id, vector<RoseVertex>> succs;
|
||||
|
||||
if (cc.grey.allowTamarama && cc.streaming && !do_prefix) {
|
||||
findExclusiveInfixes(tbi, bc, qif, infixTriggers, no_retrigger_queues);
|
||||
}
|
||||
@ -1517,10 +1518,6 @@ bool buildLeftfixes(RoseBuildImpl &tbi, build_context &bc,
|
||||
}
|
||||
}
|
||||
|
||||
if (!contains(succs, leftfix)) {
|
||||
order.push_back(leftfix);
|
||||
}
|
||||
|
||||
succs[leftfix].push_back(v);
|
||||
}
|
||||
|
||||
@ -1529,8 +1526,9 @@ bool buildLeftfixes(RoseBuildImpl &tbi, build_context &bc,
|
||||
|
||||
map<left_id, eager_info> eager;
|
||||
|
||||
for (const left_id &leftfix : order) {
|
||||
const auto &left_succs = succs[leftfix];
|
||||
for (const auto &m : succs) {
|
||||
const left_id &leftfix = m.first;
|
||||
const auto &left_succs = m.second;
|
||||
|
||||
rose_group squash_mask = tbi.rose_squash_masks.at(leftfix);
|
||||
eager_info ei;
|
||||
@ -1549,9 +1547,11 @@ bool buildLeftfixes(RoseBuildImpl &tbi, build_context &bc,
|
||||
eager.clear();
|
||||
}
|
||||
|
||||
for (const left_id &leftfix : order) {
|
||||
for (const auto &m : succs) {
|
||||
const left_id &leftfix = m.first;
|
||||
const auto &left_succs = m.second;
|
||||
buildLeftfix(tbi, bc, do_prefix, qif.get_queue(), infixTriggers,
|
||||
no_retrigger_queues, eager_queues, eager, succs[leftfix],
|
||||
no_retrigger_queues, eager_queues, eager, left_succs,
|
||||
leftfix);
|
||||
}
|
||||
|
||||
|
368
src/util/insertion_ordered.h
Normal file
368
src/util/insertion_ordered.h
Normal file
@ -0,0 +1,368 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Intel Corporation
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef UTIL_INSERTION_ORDERED_H
|
||||
#define UTIL_INSERTION_ORDERED_H
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \brief Insertion-ordered associative containers (set, map).
|
||||
*/
|
||||
|
||||
#include "util/operators.h"
|
||||
#include "util/unordered.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/iterator/iterator_facade.hpp>
|
||||
|
||||
namespace ue2 {
|
||||
|
||||
namespace insertion_ordered_detail {
|
||||
|
||||
// Iterator facade that wraps an underlying iterator, so that we get our
|
||||
// own iterator types.
|
||||
template<class WrappedIter, class Value>
|
||||
class iter_wrapper
|
||||
: public boost::iterator_facade<iter_wrapper<WrappedIter, Value>, Value,
|
||||
boost::random_access_traversal_tag> {
|
||||
public:
|
||||
iter_wrapper() = default;
|
||||
explicit iter_wrapper(WrappedIter it_in) : it(std::move(it_in)) {}
|
||||
|
||||
// Templated copy-constructor to allow for interoperable iterator and
|
||||
// const_iterator.
|
||||
template<class, class> friend class iter_wrapper;
|
||||
|
||||
template<class OtherIter, class OtherValue>
|
||||
iter_wrapper(iter_wrapper<OtherIter, OtherValue> other,
|
||||
typename std::enable_if<std::is_convertible<
|
||||
OtherIter, WrappedIter>::value>::type * = nullptr)
|
||||
: it(std::move(other.it)) {}
|
||||
|
||||
WrappedIter get() const { return it; }
|
||||
|
||||
private:
|
||||
friend class boost::iterator_core_access;
|
||||
|
||||
WrappedIter it;
|
||||
|
||||
void increment() { ++it; }
|
||||
void decrement() { --it; }
|
||||
void advance(size_t n) { it += n; }
|
||||
typename std::iterator_traits<WrappedIter>::difference_type
|
||||
distance_to(const iter_wrapper &other) const {
|
||||
return other.it - it;
|
||||
}
|
||||
bool equal(const iter_wrapper &other) const { return it == other.it; }
|
||||
Value &dereference() const { return *it; }
|
||||
};
|
||||
|
||||
template<class Key, class Element>
|
||||
class element_store {
|
||||
std::vector<Element> data;
|
||||
ue2_unordered_map<Key, size_t> map;
|
||||
|
||||
public:
|
||||
bool empty() const {
|
||||
return data.empty();
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
assert(data.size() == map.size());
|
||||
return data.size();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
data.clear();
|
||||
map.clear();
|
||||
}
|
||||
|
||||
void reserve(size_t n) {
|
||||
data.reserve(n);
|
||||
map.reserve(n);
|
||||
}
|
||||
|
||||
// Iteration.
|
||||
|
||||
using const_iterator =
|
||||
iter_wrapper<typename std::vector<Element>::const_iterator,
|
||||
const Element>;
|
||||
using iterator =
|
||||
iter_wrapper<typename std::vector<Element>::iterator, Element>;
|
||||
|
||||
const_iterator begin() const {
|
||||
return const_iterator(data.begin());
|
||||
}
|
||||
|
||||
const_iterator end() const {
|
||||
return const_iterator(data.end());
|
||||
}
|
||||
|
||||
iterator begin() {
|
||||
return iterator(data.begin());
|
||||
}
|
||||
|
||||
iterator end() {
|
||||
return iterator(data.end());
|
||||
}
|
||||
|
||||
// Search.
|
||||
|
||||
const_iterator find(const Key &key) const {
|
||||
auto map_it = map.find(key);
|
||||
if (map_it == map.end()) {
|
||||
return end();
|
||||
}
|
||||
auto idx = map_it->second;
|
||||
assert(idx < data.size());
|
||||
return begin() + idx;
|
||||
}
|
||||
|
||||
iterator find(const Key &key) {
|
||||
auto map_it = map.find(key);
|
||||
if (map_it == map.end()) {
|
||||
return end();
|
||||
}
|
||||
auto idx = map_it->second;
|
||||
assert(idx < data.size());
|
||||
return begin() + idx;
|
||||
}
|
||||
|
||||
// Insert.
|
||||
|
||||
std::pair<iterator, bool> insert(const Key &key, const Element &element) {
|
||||
const auto idx = data.size();
|
||||
if (map.emplace(key, idx).second) {
|
||||
data.push_back(element);
|
||||
return {begin() + idx, true};
|
||||
}
|
||||
return {end(), false};
|
||||
}
|
||||
|
||||
bool operator==(const element_store &a) const {
|
||||
return data == a.data;
|
||||
}
|
||||
|
||||
bool operator<(const element_store &a) const {
|
||||
return data < a.data;
|
||||
}
|
||||
|
||||
void swap(element_store &a) {
|
||||
using std::swap;
|
||||
swap(data, a.data);
|
||||
swap(map, a.map);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace insertion_ordered_detail
|
||||
|
||||
template<class Key, class Value>
|
||||
class insertion_ordered_map
|
||||
: public totally_ordered<insertion_ordered_map<Key, Value>> {
|
||||
public:
|
||||
using key_type = Key;
|
||||
using mapped_type = Value;
|
||||
using value_type = std::pair<const Key, Value>;
|
||||
|
||||
private:
|
||||
using store_type = insertion_ordered_detail::element_store<Key, value_type>;
|
||||
store_type store;
|
||||
|
||||
public:
|
||||
using const_iterator = typename store_type::const_iterator;
|
||||
using iterator = typename store_type::iterator;
|
||||
|
||||
insertion_ordered_map() = default;
|
||||
|
||||
template<class Iter>
|
||||
insertion_ordered_map(Iter it, Iter it_end) {
|
||||
insert(it, it_end);
|
||||
}
|
||||
|
||||
explicit insertion_ordered_map(std::initializer_list<value_type> init) {
|
||||
insert(init.begin(), init.end());
|
||||
}
|
||||
|
||||
const_iterator begin() const { return store.begin(); }
|
||||
const_iterator end() const { return store.end(); }
|
||||
iterator begin() { return store.begin(); }
|
||||
iterator end() { return store.end(); }
|
||||
|
||||
const_iterator find(const Key &key) const {
|
||||
return store.find(key);
|
||||
}
|
||||
|
||||
iterator find(const Key &key) {
|
||||
return store.find(key);
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const std::pair<const Key, Value> &p) {
|
||||
return store.insert(p.first, p);
|
||||
}
|
||||
|
||||
template<class Iter>
|
||||
void insert(Iter it, Iter it_end) {
|
||||
for (; it != it_end; ++it) {
|
||||
insert(*it);
|
||||
}
|
||||
}
|
||||
|
||||
Value &operator[](const Key &key) {
|
||||
auto it = find(key);
|
||||
if (it == end()) {
|
||||
it = insert({key, Value{}}).first;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
const Value &at(const Key &key) const {
|
||||
return find(key)->second;
|
||||
}
|
||||
|
||||
Value &at(const Key &key) {
|
||||
return find(key)->second;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return store.empty();
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return store.size();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
store.clear();
|
||||
}
|
||||
|
||||
void reserve(size_t n) {
|
||||
store.reserve(n);
|
||||
}
|
||||
|
||||
bool operator==(const insertion_ordered_map &a) const {
|
||||
return store == a.store;
|
||||
}
|
||||
|
||||
bool operator<(const insertion_ordered_map &a) const {
|
||||
return store < a.store;
|
||||
}
|
||||
|
||||
void swap(insertion_ordered_map &a) {
|
||||
store.swap(a.store);
|
||||
}
|
||||
|
||||
friend void swap(insertion_ordered_map &a, insertion_ordered_map &b) {
|
||||
a.swap(b);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Key>
|
||||
class insertion_ordered_set
|
||||
: public totally_ordered<insertion_ordered_set<Key>> {
|
||||
public:
|
||||
using key_type = Key;
|
||||
using value_type = Key;
|
||||
|
||||
private:
|
||||
using store_type = insertion_ordered_detail::element_store<Key, value_type>;
|
||||
store_type store;
|
||||
|
||||
public:
|
||||
using const_iterator = typename store_type::const_iterator;
|
||||
using iterator = typename store_type::iterator;
|
||||
|
||||
insertion_ordered_set() = default;
|
||||
|
||||
template<class Iter>
|
||||
insertion_ordered_set(Iter it, Iter it_end) {
|
||||
insert(it, it_end);
|
||||
}
|
||||
|
||||
explicit insertion_ordered_set(std::initializer_list<value_type> init) {
|
||||
insert(init.begin(), init.end());
|
||||
}
|
||||
|
||||
const_iterator begin() const { return store.begin(); }
|
||||
const_iterator end() const { return store.end(); }
|
||||
|
||||
const_iterator find(const Key &key) const {
|
||||
return store.find(key);
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const Key &key) {
|
||||
return store.insert(key, key);
|
||||
}
|
||||
|
||||
template<class Iter>
|
||||
void insert(Iter it, Iter it_end) {
|
||||
for (; it != it_end; ++it) {
|
||||
insert(*it);
|
||||
}
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return store.empty();
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return store.size();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
store.clear();
|
||||
}
|
||||
|
||||
void reserve(size_t n) {
|
||||
store.reserve(n);
|
||||
}
|
||||
|
||||
bool operator==(const insertion_ordered_set &a) const {
|
||||
return store == a.store;
|
||||
}
|
||||
|
||||
bool operator<(const insertion_ordered_set &a) const {
|
||||
return store < a.store;
|
||||
}
|
||||
|
||||
void swap(insertion_ordered_set &a) {
|
||||
store.swap(a.store);
|
||||
}
|
||||
|
||||
friend void swap(insertion_ordered_set &a, insertion_ordered_set &b) {
|
||||
a.swap(b);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ue2
|
||||
|
||||
#endif // UTIL_INSERTION_ORDERED_H
|
@ -78,6 +78,7 @@ set(unit_internal_SOURCES
|
||||
internal/flat_set.cpp
|
||||
internal/flat_map.cpp
|
||||
internal/graph.cpp
|
||||
internal/insertion_ordered.cpp
|
||||
internal/lbr.cpp
|
||||
internal/limex_nfa.cpp
|
||||
internal/masked_move.cpp
|
||||
|
209
unit/internal/insertion_ordered.cpp
Normal file
209
unit/internal/insertion_ordered.cpp
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Intel Corporation
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "ue2common.h"
|
||||
#include "util/insertion_ordered.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace ue2;
|
||||
|
||||
template <class K, class V>
|
||||
std::ostream &operator<<(std::ostream &os,
|
||||
const insertion_ordered_map<K, V> &m) {
|
||||
os << "{";
|
||||
for (auto it = begin(m); it != end(m); ++it) {
|
||||
os << "{" << it->first << ", " << it->second << "}";
|
||||
if (it != end(m)) {
|
||||
os << ", ";
|
||||
}
|
||||
}
|
||||
os << "}";
|
||||
return os;
|
||||
}
|
||||
|
||||
TEST(insertion_ordered_map, empty) {
|
||||
insertion_ordered_map<u32, u32> m;
|
||||
EXPECT_TRUE(m.empty());
|
||||
EXPECT_TRUE(m.begin() == m.end());
|
||||
EXPECT_EQ(0, m.size());
|
||||
|
||||
m.insert({10, 10});
|
||||
EXPECT_FALSE(m.empty());
|
||||
EXPECT_EQ(1, m.size());
|
||||
|
||||
m.clear();
|
||||
EXPECT_TRUE(m.empty());
|
||||
EXPECT_TRUE(m.begin() == m.end());
|
||||
EXPECT_EQ(0, m.size());
|
||||
}
|
||||
|
||||
TEST(insertion_ordered_map, insert) {
|
||||
const vector<pair<u32, u32>> v = {{7, 1}, {1, 2}, {3, 4},
|
||||
{10, 5}, {99, 6}, {12, 7}};
|
||||
insertion_ordered_map<u32, u32> m;
|
||||
for (const auto &e : v) {
|
||||
m.insert(e);
|
||||
}
|
||||
|
||||
EXPECT_FALSE(m.empty());
|
||||
EXPECT_EQ(v.size(), m.size());
|
||||
vector<pair<u32, u32>> v2(m.begin(), m.end());
|
||||
EXPECT_EQ(v, v2);
|
||||
}
|
||||
|
||||
TEST(insertion_ordered_map, insert_iter) {
|
||||
const vector<pair<u32, u32>> v = {{7, 1}, {1, 2}, {3, 4},
|
||||
{10, 5}, {99, 6}, {12, 7}};
|
||||
insertion_ordered_map<u32, u32> m;
|
||||
m.insert(v.begin(), v.end());
|
||||
|
||||
EXPECT_FALSE(m.empty());
|
||||
EXPECT_EQ(v.size(), m.size());
|
||||
vector<pair<u32, u32>> v2(m.begin(), m.end());
|
||||
EXPECT_EQ(v, v2);
|
||||
}
|
||||
|
||||
TEST(insertion_ordered_map, find_const) {
|
||||
const vector<pair<u32, u32>> v = {{7, 1}, {1, 2}, {3, 4},
|
||||
{10, 5}, {99, 6}, {12, 7}};
|
||||
const insertion_ordered_map<u32, u32> m(v.begin(), v.end());
|
||||
|
||||
for (const auto &e : v) {
|
||||
auto it = m.find(e.first);
|
||||
ASSERT_NE(m.end(), it);
|
||||
EXPECT_EQ(e.first, it->first);
|
||||
EXPECT_EQ(e.second, it->second);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(insertion_ordered_map, find_mutable) {
|
||||
const vector<pair<u32, u32>> v = {{7, 1}, {1, 2}, {3, 4},
|
||||
{10, 5}, {99, 6}, {12, 7}};
|
||||
insertion_ordered_map<u32, u32> m(v.begin(), v.end());
|
||||
|
||||
for (const auto &e : v) {
|
||||
auto it = m.find(e.first);
|
||||
ASSERT_NE(m.end(), it);
|
||||
EXPECT_EQ(e.first, it->first);
|
||||
EXPECT_EQ(e.second, it->second);
|
||||
auto &mut = it->second;
|
||||
++mut;
|
||||
EXPECT_EQ(e.second + 1, m.at(e.first));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(insertion_ordered_map, operator_brackets) {
|
||||
insertion_ordered_map<u32, u32> m;
|
||||
|
||||
u32 val = 1000;
|
||||
for (u32 i = 10; i > 0; i--) {
|
||||
m[i] = val++;
|
||||
}
|
||||
|
||||
EXPECT_EQ(10, m.size());
|
||||
|
||||
val = 1000;
|
||||
auto it = m.begin();
|
||||
for (u32 i = 10; i > 0; i--) {
|
||||
ASSERT_NE(m.end(), it);
|
||||
EXPECT_EQ(i, it->first);
|
||||
EXPECT_EQ(val, it->second);
|
||||
++val;
|
||||
++it;
|
||||
}
|
||||
|
||||
ASSERT_EQ(m.end(), it);
|
||||
}
|
||||
|
||||
template <class K>
|
||||
std::ostream &operator<<(std::ostream &os, const insertion_ordered_set<K> &s) {
|
||||
os << "{";
|
||||
for (auto it = begin(s); it != end(s); ++it) {
|
||||
os << *it;
|
||||
if (it != end(s)) {
|
||||
os << ", ";
|
||||
}
|
||||
}
|
||||
os << "}";
|
||||
return os;
|
||||
}
|
||||
|
||||
TEST(insertion_ordered_set, empty) {
|
||||
insertion_ordered_set<u32> m;
|
||||
EXPECT_TRUE(m.empty());
|
||||
EXPECT_TRUE(m.begin() == m.end());
|
||||
EXPECT_EQ(0, m.size());
|
||||
|
||||
m.insert(10);
|
||||
EXPECT_FALSE(m.empty());
|
||||
EXPECT_EQ(1, m.size());
|
||||
|
||||
m.clear();
|
||||
EXPECT_TRUE(m.empty());
|
||||
EXPECT_TRUE(m.begin() == m.end());
|
||||
EXPECT_EQ(0, m.size());
|
||||
}
|
||||
|
||||
TEST(insertion_ordered_set, insert) {
|
||||
const vector<u32> v = {7, 1, 3, 10, 99, 12};
|
||||
insertion_ordered_set<u32> s;
|
||||
for (const auto &e : v) {
|
||||
s.insert(e);
|
||||
}
|
||||
|
||||
EXPECT_FALSE(s.empty());
|
||||
EXPECT_EQ(v.size(), s.size());
|
||||
vector<u32> v2(s.begin(), s.end());
|
||||
EXPECT_EQ(v, v2);
|
||||
}
|
||||
|
||||
TEST(insertion_ordered_set, insert_iter) {
|
||||
const vector<u32> v = {7, 1, 3, 10, 99, 12};
|
||||
insertion_ordered_set<u32> s;
|
||||
s.insert(v.begin(), v.end());
|
||||
|
||||
EXPECT_FALSE(s.empty());
|
||||
EXPECT_EQ(v.size(), s.size());
|
||||
vector<u32> v2(s.begin(), s.end());
|
||||
EXPECT_EQ(v, v2);
|
||||
}
|
||||
|
||||
TEST(insertion_ordered_set, find_const) {
|
||||
const vector<u32> v = {7, 1, 3, 10, 99, 12};
|
||||
const insertion_ordered_set<u32> s(v.begin(), v.end());
|
||||
|
||||
for (const auto &e : v) {
|
||||
auto it = s.find(e);
|
||||
ASSERT_NE(s.end(), it);
|
||||
EXPECT_EQ(e, *it);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user