diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a07cffc..398c5d0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/src/nfagraph/ng_violet.cpp b/src/nfagraph/ng_violet.cpp index 7a4de5f5..9ce732c2 100644 --- a/src/nfagraph/ng_violet.cpp +++ b/src/nfagraph/ng_violet.cpp @@ -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 sources; - map> images; + insertion_ordered_map> 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> 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 seen_order; - map> infixes; + insertion_ordered_map> 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> prefixes; bool changed; u32 gen = 0; do { DEBUG_PRINTF("gen %u\n", gen); changed = false; - vector seen_order; - map > 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 stuck; + STAGE_DEBUG_PRINTF("EXTRACT STRONG LITERALS\n"); + + unordered_set stuck; + insertion_ordered_map> edges_by_graph; bool changed; + do { changed = false; - vector seen_order; - map > 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 weak; - vector ordered_weak; + unordered_set 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 > weak_edges; + insertion_ordered_map> 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 > suffixes; - vector ordered_suffixes; + + insertion_ordered_map> 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 > right_edges; - vector ordered_graphs; + insertion_ordered_map> 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> right_edges; bool changed; do { changed = false; - map > right_edges; - vector 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 &ee = right_edges[h]; + for (const auto &m : right_edges) { + const NGHolder *h = m.first; + const vector &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 prev = {getStart(vg)}; + insertion_ordered_set prev({getStart(vg)}); + insertion_ordered_set curr; while (gen < MAX_DESIRED_CLEAN_SPLIT_DEPTH) { - /* collect vertices in edge order for determinism */ - vector curr; - set 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> rightfixes; - vector ordered_graphs; + insertion_ordered_map> 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 > edges_by_graph; - vector> graphs; + insertion_ordered_map, + vector> 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"); diff --git a/src/rose/rose_build_add.cpp b/src/rose/rose_build_add.cpp index b003336a..71f1667d 100644 --- a/src/rose/rose_build_add.cpp +++ b/src/rose/rose_build_add.cpp @@ -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 > graphs; - vector ordered_graphs; // Stored in first-encounter order. + insertion_ordered_map> 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 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 diff --git a/src/rose/rose_build_bytecode.cpp b/src/rose/rose_build_bytecode.cpp index 0ae5bb4f..d3ae52bf 100644 --- a/src/rose/rose_build_bytecode.cpp +++ b/src/rose/rose_build_bytecode.cpp @@ -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 > infixTriggers; - vector order; - unordered_map> succs; + map> infixTriggers; findInfixTriggers(tbi, &infixTriggers); + insertion_ordered_map> 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 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 index 00000000..2067d350 --- /dev/null +++ b/src/util/insertion_ordered.h @@ -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 +#include +#include +#include +#include + +#include + +namespace ue2 { + +namespace insertion_ordered_detail { + +// Iterator facade that wraps an underlying iterator, so that we get our +// own iterator types. +template +class iter_wrapper + : public boost::iterator_facade, 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 friend class iter_wrapper; + + template + iter_wrapper(iter_wrapper other, + typename std::enable_if::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::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 element_store { + std::vector data; + ue2_unordered_map 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::const_iterator, + const Element>; + using iterator = + iter_wrapper::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 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 insertion_ordered_map + : public totally_ordered> { +public: + using key_type = Key; + using mapped_type = Value; + using value_type = std::pair; + +private: + using store_type = insertion_ordered_detail::element_store; + store_type store; + +public: + using const_iterator = typename store_type::const_iterator; + using iterator = typename store_type::iterator; + + insertion_ordered_map() = default; + + template + insertion_ordered_map(Iter it, Iter it_end) { + insert(it, it_end); + } + + explicit insertion_ordered_map(std::initializer_list 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 insert(const std::pair &p) { + return store.insert(p.first, p); + } + + template + 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 insertion_ordered_set + : public totally_ordered> { +public: + using key_type = Key; + using value_type = Key; + +private: + using store_type = insertion_ordered_detail::element_store; + store_type store; + +public: + using const_iterator = typename store_type::const_iterator; + using iterator = typename store_type::iterator; + + insertion_ordered_set() = default; + + template + insertion_ordered_set(Iter it, Iter it_end) { + insert(it, it_end); + } + + explicit insertion_ordered_set(std::initializer_list 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 insert(const Key &key) { + return store.insert(key, key); + } + + template + 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 diff --git a/unit/CMakeLists.txt b/unit/CMakeLists.txt index fad8633d..6f8a8bf4 100644 --- a/unit/CMakeLists.txt +++ b/unit/CMakeLists.txt @@ -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 index 00000000..6026ce1d --- /dev/null +++ b/unit/internal/insertion_ordered.cpp @@ -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 +std::ostream &operator<<(std::ostream &os, + const insertion_ordered_map &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 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> v = {{7, 1}, {1, 2}, {3, 4}, + {10, 5}, {99, 6}, {12, 7}}; + insertion_ordered_map m; + for (const auto &e : v) { + m.insert(e); + } + + EXPECT_FALSE(m.empty()); + EXPECT_EQ(v.size(), m.size()); + vector> v2(m.begin(), m.end()); + EXPECT_EQ(v, v2); +} + +TEST(insertion_ordered_map, insert_iter) { + const vector> v = {{7, 1}, {1, 2}, {3, 4}, + {10, 5}, {99, 6}, {12, 7}}; + insertion_ordered_map m; + m.insert(v.begin(), v.end()); + + EXPECT_FALSE(m.empty()); + EXPECT_EQ(v.size(), m.size()); + vector> v2(m.begin(), m.end()); + EXPECT_EQ(v, v2); +} + +TEST(insertion_ordered_map, find_const) { + const vector> v = {{7, 1}, {1, 2}, {3, 4}, + {10, 5}, {99, 6}, {12, 7}}; + const insertion_ordered_map 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> v = {{7, 1}, {1, 2}, {3, 4}, + {10, 5}, {99, 6}, {12, 7}}; + insertion_ordered_map 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 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 +std::ostream &operator<<(std::ostream &os, const insertion_ordered_set &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 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 v = {7, 1, 3, 10, 99, 12}; + insertion_ordered_set s; + for (const auto &e : v) { + s.insert(e); + } + + EXPECT_FALSE(s.empty()); + EXPECT_EQ(v.size(), s.size()); + vector v2(s.begin(), s.end()); + EXPECT_EQ(v, v2); +} + +TEST(insertion_ordered_set, insert_iter) { + const vector v = {7, 1, 3, 10, 99, 12}; + insertion_ordered_set s; + s.insert(v.begin(), v.end()); + + EXPECT_FALSE(s.empty()); + EXPECT_EQ(v.size(), s.size()); + vector v2(s.begin(), s.end()); + EXPECT_EQ(v, v2); +} + +TEST(insertion_ordered_set, find_const) { + const vector v = {7, 1, 3, 10, 99, 12}; + const insertion_ordered_set s(v.begin(), v.end()); + + for (const auto &e : v) { + auto it = s.find(e); + ASSERT_NE(s.end(), it); + EXPECT_EQ(e, *it); + } +}