]> git.ipfire.org Git - thirdparty/vectorscan.git/commitdiff
insertion_ordered_{map,set}: add new containers
authorJustin Viiret <justin.viiret@intel.com>
Fri, 4 Aug 2017 03:23:07 +0000 (13:23 +1000)
committerMatthew Barr <matthew.barr@intel.com>
Mon, 21 Aug 2017 01:25:21 +0000 (11:25 +1000)
These are associative map/set structures that are iterable in insertion
order.

CMakeLists.txt
src/nfagraph/ng_violet.cpp
src/rose/rose_build_add.cpp
src/rose/rose_build_bytecode.cpp
src/util/insertion_ordered.h [new file with mode: 0644]
unit/CMakeLists.txt
unit/internal/insertion_ordered.cpp [new file with mode: 0644]

index 4a07cffcaec1b2ab6fe11c681d32216dc03f8b2f..398c5d0c0f71e01a539f6661e528c12094a2108e 100644 (file)
@@ -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
index 7a4de5f5efaeebb7ad8778e6aae042bc1011afd7..9ce732c2ea28c01962bd84cf87bdc247be4d07ef 100644 (file)
@@ -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;
 
+    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");
index b003336a208b0761034182c9eef11b4e3ecfe9c4..71f1667dc372a0ac030282d25dd57525f4c3b7cd 100644 (file)
@@ -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
index 0ae5bb4f0989475a6667d17a79e13a603086a34f..d3ae52bf38e1cca4aaaef77060a632bce1762b91 100644 (file)
@@ -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);
     }
 
diff --git a/src/util/insertion_ordered.h b/src/util/insertion_ordered.h
new file mode 100644 (file)
index 0000000..2067d35
--- /dev/null
@@ -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
index fad8633d2451ece94766766092dec0a64d10a245..6f8a8bf43402664b006e5d1c85a22339f8f3481b 100644 (file)
@@ -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
diff --git a/unit/internal/insertion_ordered.cpp b/unit/internal/insertion_ordered.cpp
new file mode 100644 (file)
index 0000000..6026ce1
--- /dev/null
@@ -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);
+    }
+}