insertion_ordered_{map,set}: add new containers

These are associative map/set structures that are iterable in insertion
order.
This commit is contained in:
Justin Viiret 2017-08-04 13:23:07 +10:00 committed by Matthew Barr
parent 72973ccb47
commit 3ff70d5568
7 changed files with 671 additions and 110 deletions

View 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

View 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;
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");

View 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

View 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);
}

View 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

View 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

View 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);
}
}