diff --git a/runtime/Cpp/runtime/src/atn/ATNConfigSet.h b/runtime/Cpp/runtime/src/atn/ATNConfigSet.h index 602e818f0e3..f55a2a21941 100755 --- a/runtime/Cpp/runtime/src/atn/ATNConfigSet.h +++ b/runtime/Cpp/runtime/src/atn/ATNConfigSet.h @@ -46,7 +46,7 @@ namespace atn { ATNConfigSet(const ATNConfigSet &other); - ATNConfigSet(ATNConfigSet &&) = delete; + ATNConfigSet(ATNConfigSet&&) = delete; explicit ATNConfigSet(bool fullCtx); @@ -68,7 +68,7 @@ namespace atn { bool addAll(const ATNConfigSet &other); - std::vector getStates() const; + std::vector getStates() const; /** * Gets the complete set of represented alternatives for the configuration diff --git a/runtime/Cpp/runtime/src/atn/ATNSimulator.cpp b/runtime/Cpp/runtime/src/atn/ATNSimulator.cpp index 25c0c2f3eb4..3abe6882ff9 100755 --- a/runtime/Cpp/runtime/src/atn/ATNSimulator.cpp +++ b/runtime/Cpp/runtime/src/atn/ATNSimulator.cpp @@ -23,12 +23,11 @@ void ATNSimulator::clearDFA() { throw UnsupportedOperationException("This ATN simulator does not support clearing the DFA."); } -PredictionContextCache& ATNSimulator::getSharedContextCache() { +PredictionContextCache& ATNSimulator::getSharedContextCache() const { return _sharedContextCache; } -Ref ATNSimulator::getCachedContext(Ref const& context) { +Ref ATNSimulator::getCachedContext(const Ref &context) { // This function must only be called with an active state lock, as we are going to change a shared structure. - std::map, Ref> visited; - return PredictionContext::getCachedContext(context, _sharedContextCache, visited); + return PredictionContext::getCachedContext(context, getSharedContextCache()); } diff --git a/runtime/Cpp/runtime/src/atn/ATNSimulator.h b/runtime/Cpp/runtime/src/atn/ATNSimulator.h index 2dfb4bd953f..2c842c03d42 100755 --- a/runtime/Cpp/runtime/src/atn/ATNSimulator.h +++ b/runtime/Cpp/runtime/src/atn/ATNSimulator.h @@ -37,8 +37,9 @@ namespace atn { * @since 4.3 */ virtual void clearDFA(); - virtual PredictionContextCache& getSharedContextCache(); - virtual Ref getCachedContext(Ref const& context); + + PredictionContextCache& getSharedContextCache() const; + Ref getCachedContext(const Ref &context); protected: /// diff --git a/runtime/Cpp/runtime/src/atn/ArrayPredictionContext.cpp b/runtime/Cpp/runtime/src/atn/ArrayPredictionContext.cpp index b5b12612502..2bd2139fe49 100755 --- a/runtime/Cpp/runtime/src/atn/ArrayPredictionContext.cpp +++ b/runtime/Cpp/runtime/src/atn/ArrayPredictionContext.cpp @@ -4,11 +4,14 @@ */ #include "support/Arrays.h" +#include "support/Casts.h" #include "atn/SingletonPredictionContext.h" +#include "misc/MurmurHash.h" #include "atn/ArrayPredictionContext.h" using namespace antlr4::atn; +using namespace antlrcpp; ArrayPredictionContext::ArrayPredictionContext(Ref const& a) : ArrayPredictionContext({ a->parent }, { a->returnState }) { @@ -16,9 +19,10 @@ ArrayPredictionContext::ArrayPredictionContext(Ref> parents, std::vector returnStates) - : PredictionContext(calculateHashCode(parents, returnStates)), parents(std::move(parents)), returnStates(std::move(returnStates)) { + : parents(std::move(parents)), returnStates(std::move(returnStates)) { assert(this->parents.size() > 0); assert(this->returnStates.size() > 0); + assert(this->parents.size() == this->returnStates.size()); } PredictionContextType ArrayPredictionContext::getContextType() const { @@ -34,7 +38,7 @@ size_t ArrayPredictionContext::size() const { return returnStates.size(); } -Ref ArrayPredictionContext::getParent(size_t index) const { +const Ref& ArrayPredictionContext::getParent(size_t index) const { return parents[index]; } @@ -42,21 +46,28 @@ size_t ArrayPredictionContext::getReturnState(size_t index) const { return returnStates[index]; } -bool ArrayPredictionContext::operator == (PredictionContext const& o) const { - if (this == &o) { - return true; +size_t ArrayPredictionContext::hashCodeImpl() const { + size_t hash = misc::MurmurHash::initialize(); + hash = misc::MurmurHash::update(hash, static_cast(getContextType())); + for (const auto &parent : parents) { + hash = misc::MurmurHash::update(hash, parent); } - if (o.getContextType() != PredictionContextType::ARRAY) { - return false; + for (const auto &returnState : returnStates) { + hash = misc::MurmurHash::update(hash, returnState); } + return misc::MurmurHash::finish(hash, 1 + parents.size() + returnStates.size()); +} - const ArrayPredictionContext *other = static_cast(&o); - if (hashCode() != other->hashCode()) { - return false; // can't be same if hash is different +bool ArrayPredictionContext::equals(const PredictionContext &other) const { + if (this == &other) { + return true; } - - return antlrcpp::Arrays::equals(returnStates, other->returnStates) && - antlrcpp::Arrays::equals(parents, other->parents); + if (getContextType() != other.getContextType()) { + return false; + } + const ArrayPredictionContext &array = downCast(other); + return Arrays::equals(returnStates, array.returnStates) && + Arrays::equals(parents, array.parents); } std::string ArrayPredictionContext::toString() const { diff --git a/runtime/Cpp/runtime/src/atn/ArrayPredictionContext.h b/runtime/Cpp/runtime/src/atn/ArrayPredictionContext.h index da405668d0c..377167d3fcc 100755 --- a/runtime/Cpp/runtime/src/atn/ArrayPredictionContext.h +++ b/runtime/Cpp/runtime/src/atn/ArrayPredictionContext.h @@ -20,24 +20,27 @@ namespace atn { /// returnState == EMPTY_RETURN_STATE. // Also here: we use a strong reference to our parents to avoid having them freed prematurely. // See also SinglePredictionContext. - const std::vector> parents; + std::vector> parents; /// Sorted for merge, no duplicates; if present, EMPTY_RETURN_STATE is always last. - const std::vector returnStates; + std::vector returnStates; explicit ArrayPredictionContext(Ref const &a); ArrayPredictionContext(std::vector> parents, std::vector returnStates); - PredictionContextType getContextType() const override; - - virtual bool isEmpty() const override; - virtual size_t size() const override; - virtual Ref getParent(size_t index) const override; - virtual size_t getReturnState(size_t index) const override; - bool operator == (const PredictionContext &o) const override; + ArrayPredictionContext(ArrayPredictionContext&&) = default; - virtual std::string toString() const override; + PredictionContextType getContextType() const override; + bool isEmpty() const override; + size_t size() const override; + const Ref& getParent(size_t index) const override; + size_t getReturnState(size_t index) const override; + bool equals(const PredictionContext &other) const override; + std::string toString() const override; + + protected: + size_t hashCodeImpl() const override; }; } // namespace atn diff --git a/runtime/Cpp/runtime/src/atn/PredictionContext.cpp b/runtime/Cpp/runtime/src/atn/PredictionContext.cpp index 5e1a18f01c9..b3d39611aaf 100755 --- a/runtime/Cpp/runtime/src/atn/PredictionContext.cpp +++ b/runtime/Cpp/runtime/src/atn/PredictionContext.cpp @@ -18,16 +18,120 @@ using namespace antlr4; using namespace antlr4::misc; using namespace antlr4::atn; - using namespace antlrcpp; -std::atomic PredictionContext::globalNodeCount(0); +namespace { + + bool combineCommonParents(std::vector> &parents) { + std::unordered_set> uniqueParents; + bool changed = false; + for (const auto &parent : parents) { + uniqueParents.insert(parent); + } + for (auto &parent : parents) { + auto iterator = uniqueParents.find(parent); + if (iterator->get() != parent.get()) { + changed = true; + parent = *iterator; + } + } + return changed; + } + + Ref getCachedContextImpl(const Ref &context, + PredictionContextCache &contextCache, + std::unordered_map, Ref> &visited) { + if (context->isEmpty()) { + return context; + } + + { + auto iterator = visited.find(context); + if (iterator != visited.end()) { + return iterator->second; // Not necessarly the same as context. + } + } + + auto cached = contextCache.get(context); + if (cached) { + visited[context] = cached; + return cached; + } + + bool changed = false; + + std::vector> parents(context->size()); + for (size_t i = 0; i < parents.size(); i++) { + auto parent = getCachedContextImpl(context->getParent(i), contextCache, visited); + if (changed || parent != context->getParent(i)) { + if (!changed) { + parents.clear(); + for (size_t j = 0; j < context->size(); j++) { + parents.push_back(context->getParent(j)); + } + + changed = true; + } + + parents[i] = std::move(parent); + } + } + + if (!changed) { + visited[context] = context; + contextCache.put(context); + return context; + } + + Ref updated; + if (parents.empty()) { + updated = PredictionContext::EMPTY; + } else if (parents.size() == 1) { + updated = SingletonPredictionContext::create(std::move(parents[0]), context->getReturnState(0)); + contextCache.put(updated); + } else { + updated = std::make_shared(std::move(parents), downCast(context.get())->returnStates); + contextCache.put(updated); + } + + visited[updated] = updated; + visited[context] = updated; + + return updated; + } + + void getAllContextNodesImpl(const Ref &context, std::vector> &nodes, + std::unordered_set &visited) { + + if (visited.find(context.get()) != visited.end()) { + return; // Already done. + } + + visited.insert(context.get()); + nodes.push_back(context); + + for (size_t i = 0; i < context->size(); i++) { + getAllContextNodesImpl(context->getParent(i), nodes, visited); + } + } + + size_t insertOrAssignNodeId(std::unordered_map &nodeIds, size_t &nodeId, const PredictionContext *node) { + auto existing = nodeIds.find(node); + if (existing != nodeIds.end()) { + return existing->second; + } + return nodeIds.insert({node, nodeId++}).first->second; + } + +} + const Ref PredictionContext::EMPTY = std::make_shared(nullptr, PredictionContext::EMPTY_RETURN_STATE); //----------------- PredictionContext ---------------------------------------------------------------------------------- -PredictionContext::PredictionContext(size_t cachedHashCode) : id(globalNodeCount.fetch_add(1, std::memory_order_relaxed)), cachedHashCode(cachedHashCode) { -} +PredictionContext::PredictionContext() : _hashCode(0) {} + +PredictionContext::PredictionContext(PredictionContext&& other) : _hashCode(other._hashCode.exchange(0, std::memory_order_relaxed)) {} Ref PredictionContext::fromRuleContext(const ATN &atn, RuleContext *outerContext) { if (outerContext == nullptr) { @@ -41,15 +145,14 @@ Ref PredictionContext::fromRuleContext(const ATN &atn, } // If we have a parent, convert it to a PredictionContext graph - Ref parent = PredictionContext::fromRuleContext(atn, dynamic_cast(outerContext->parent)); + auto parent = PredictionContext::fromRuleContext(atn, + outerContext->parent != nullptr && outerContext->parent->getTreeType() == antlr4::tree::ParseTreeType::RULE + ? downCast(outerContext->parent) + : nullptr); ATNState *state = atn.states.at(outerContext->invokingState); const RuleTransition *transition = downCast(state->transitions[0].get()); - return SingletonPredictionContext::create(parent, transition->followState->stateNumber); -} - -bool PredictionContext::isEmpty() const { - return this == EMPTY.get(); + return SingletonPredictionContext::create(std::move(parent), transition->followState->stateNumber); } bool PredictionContext::hasEmptyPath() const { @@ -58,40 +161,19 @@ bool PredictionContext::hasEmptyPath() const { } size_t PredictionContext::hashCode() const { - return cachedHashCode; -} - -size_t PredictionContext::calculateEmptyHashCode() { - size_t hash = MurmurHash::initialize(INITIAL_HASH); - hash = MurmurHash::finish(hash, 0); - return hash; -} - -size_t PredictionContext::calculateHashCode(Ref parent, size_t returnState) { - size_t hash = MurmurHash::initialize(INITIAL_HASH); - hash = MurmurHash::update(hash, parent); - hash = MurmurHash::update(hash, returnState); - hash = MurmurHash::finish(hash, 2); - return hash; -} - -size_t PredictionContext::calculateHashCode(const std::vector> &parents, - const std::vector &returnStates) { - size_t hash = MurmurHash::initialize(INITIAL_HASH); - - for (const auto &parent : parents) { - hash = MurmurHash::update(hash, parent); - } - - for (const auto &returnState : returnStates) { - hash = MurmurHash::update(hash, returnState); + auto hash = _hashCode.load(std::memory_order_relaxed); + if (hash == 0) { + hash = hashCodeImpl(); + if (hash == 0) { + hash = std::numeric_limits::max(); + } + _hashCode.store(hash, std::memory_order_relaxed); } - - return MurmurHash::finish(hash, parents.size() + returnStates.size()); + return hash; } -Ref PredictionContext::merge(const Ref &a, - const Ref &b, bool rootIsWildcard, PredictionContextMergeCache *mergeCache) { +Ref PredictionContext::merge(Ref a, Ref b, + bool rootIsWildcard, PredictionContextMergeCache *mergeCache) { assert(a && b); // share same graph if both same @@ -103,8 +185,8 @@ Ref PredictionContext::merge(const RefgetContextType(); if (aType == PredictionContextType::SINGLETON && bType == PredictionContextType::SINGLETON) { - return mergeSingletons(std::static_pointer_cast(a), - std::static_pointer_cast(b), rootIsWildcard, mergeCache); + return mergeSingletons(std::static_pointer_cast(std::move(a)), + std::static_pointer_cast(std::move(b)), rootIsWildcard, mergeCache); } // At least one of a or b is array. @@ -121,23 +203,23 @@ Ref PredictionContext::merge(const Ref left; if (aType == PredictionContextType::SINGLETON) { - left = std::make_shared(std::static_pointer_cast(a)); + left = std::make_shared(std::static_pointer_cast(std::move(a))); } else { - left = std::static_pointer_cast(a); + left = std::static_pointer_cast(std::move(a)); } Ref right; if (bType == PredictionContextType::SINGLETON) { - right = std::make_shared(std::static_pointer_cast(b)); + right = std::make_shared(std::static_pointer_cast(std::move(b))); } else { - right = std::static_pointer_cast(b); + right = std::static_pointer_cast(std::move(b)); } - return mergeArrays(left, right, rootIsWildcard, mergeCache); + return mergeArrays(std::move(left), std::move(right), rootIsWildcard, mergeCache); } -Ref PredictionContext::mergeSingletons(const Ref &a, - const Ref &b, bool rootIsWildcard, PredictionContextMergeCache *mergeCache) { +Ref PredictionContext::mergeSingletons(Ref a, Ref b, + bool rootIsWildcard, PredictionContextMergeCache *mergeCache) { - if (mergeCache != nullptr) { // Can be null if not given to the ATNState from which this call originates. + if (mergeCache) { auto existing = mergeCache->get(a, b); if (existing) { return existing; @@ -148,18 +230,18 @@ Ref PredictionContext::mergeSingletons(const Ref rootMerge = mergeRoot(a, b, rootIsWildcard); + auto rootMerge = mergeRoot(a, b, rootIsWildcard); if (rootMerge) { - if (mergeCache != nullptr) { - mergeCache->put(a, b, rootMerge); + if (mergeCache) { + return mergeCache->put(a, b, rootMerge); } return rootMerge; } - Ref parentA = a->parent; - Ref parentB = b->parent; + auto parentA = a->parent; + auto parentB = b->parent; if (a->returnState == b->returnState) { // a == b - Ref parent = merge(parentA, parentB, rootIsWildcard, mergeCache); + auto parent = merge(parentA, parentB, rootIsWildcard, mergeCache); // If parent is same as existing a or b parent or reduced to a parent, return it. if (parent == parentA) { // ax + bx = ax, if a=b @@ -173,55 +255,55 @@ Ref PredictionContext::mergeSingletons(const Ref a_ = SingletonPredictionContext::create(parent, a->returnState); - if (mergeCache != nullptr) { - mergeCache->put(a, b, a_); - } - return a_; - } else { - // a != b payloads differ - // see if we can collapse parents due to $+x parents if local ctx - Ref singleParent; - if (a == b || (*parentA == *parentB)) { // ax + bx = [a,b]x - singleParent = parentA; - } - if (singleParent) { // parents are same, sort payloads and use same parent - std::vector payloads = { a->returnState, b->returnState }; - if (a->returnState > b->returnState) { - payloads[0] = b->returnState; - payloads[1] = a->returnState; - } - std::vector> parents = { singleParent, singleParent }; - Ref a_ = std::make_shared(std::move(parents), std::move(payloads)); - if (mergeCache != nullptr) { - mergeCache->put(a, b, a_); - } - return a_; - } - - // parents differ and can't merge them. Just pack together - // into array; can't merge. - // ax + by = [ax,by] - Ref a_; - if (a->returnState > b->returnState) { // sort by payload - std::vector payloads = { b->returnState, a->returnState }; - std::vector> parents = { b->parent, a->parent }; - a_ = std::make_shared(std::move(parents), std::move(payloads)); - } else { - std::vector payloads = {a->returnState, b->returnState}; - std::vector> parents = { a->parent, b->parent }; - a_ = std::make_shared(std::move(parents), std::move(payloads)); - } - - if (mergeCache != nullptr) { - mergeCache->put(a, b, a_); - } - return a_; - } + auto c = SingletonPredictionContext::create(std::move(parent), a->returnState); + if (mergeCache) { + return mergeCache->put(a, b, std::move(c)); + } + return c; + } + // a != b payloads differ + // see if we can collapse parents due to $+x parents if local ctx + Ref singleParent; + if (a == b || (*parentA == *parentB)) { // ax + bx = [a,b]x + singleParent = parentA; + } + if (singleParent) { // parents are same, sort payloads and use same parent + std::vector payloads = { a->returnState, b->returnState }; + if (a->returnState > b->returnState) { + payloads[0] = b->returnState; + payloads[1] = a->returnState; + } + std::vector> parents = { singleParent, singleParent }; + auto c = std::make_shared(std::move(parents), std::move(payloads)); + if (mergeCache) { + return mergeCache->put(a, b, std::move(c)); + } + return c; + } + + // parents differ and can't merge them. Just pack together + // into array; can't merge. + // ax + by = [ax,by] + if (a->returnState > b->returnState) { // sort by payload + std::vector payloads = { b->returnState, a->returnState }; + std::vector> parents = { b->parent, a->parent }; + auto c = std::make_shared(std::move(parents), std::move(payloads)); + if (mergeCache) { + return mergeCache->put(a, b, std::move(c)); + } + return c; + } + std::vector payloads = {a->returnState, b->returnState}; + std::vector> parents = { a->parent, b->parent }; + auto c = std::make_shared(std::move(parents), std::move(payloads)); + if (mergeCache) { + return mergeCache->put(a, b, std::move(c)); + } + return c; } -Ref PredictionContext::mergeRoot(const Ref &a, - const Ref &b, bool rootIsWildcard) { +Ref PredictionContext::mergeRoot(Ref a, Ref b, + bool rootIsWildcard) { if (rootIsWildcard) { if (a == EMPTY) { // * + b = * return EMPTY; @@ -236,23 +318,21 @@ Ref PredictionContext::mergeRoot(const Ref payloads = { b->returnState, EMPTY_RETURN_STATE }; std::vector> parents = { b->parent, nullptr }; - Ref joined = std::make_shared(std::move(parents), std::move(payloads)); - return joined; + return std::make_shared(std::move(parents), std::move(payloads)); } if (b == EMPTY) { // x + $ = [$,x] ($ is always first if present) std::vector payloads = { a->returnState, EMPTY_RETURN_STATE }; std::vector> parents = { a->parent, nullptr }; - Ref joined = std::make_shared(std::move(parents), std::move(payloads)); - return joined; + return std::make_shared(std::move(parents), std::move(payloads)); } } return nullptr; } -Ref PredictionContext::mergeArrays(const Ref &a, - const Ref &b, bool rootIsWildcard, PredictionContextMergeCache *mergeCache) { +Ref PredictionContext::mergeArrays(Ref a, Ref b, + bool rootIsWildcard, PredictionContextMergeCache *mergeCache) { - if (mergeCache != nullptr) { + if (mergeCache) { auto existing = mergeCache->get(a, b); if (existing) { return existing; @@ -273,32 +353,29 @@ Ref PredictionContext::mergeArrays(const RefreturnStates.size() && j < b->returnStates.size()) { - Ref a_parent = a->parents[i]; - Ref b_parent = b->parents[j]; + auto parentA = a->parents[i]; + auto parentB = b->parents[j]; if (a->returnStates[i] == b->returnStates[j]) { // same payload (stack tops are equal), must yield merged singleton size_t payload = a->returnStates[i]; // $+$ = $ - bool both$ = payload == EMPTY_RETURN_STATE && !a_parent && !b_parent; - bool ax_ax = (a_parent && b_parent) && *a_parent == *b_parent; // ax+ax -> ax + bool both$ = payload == EMPTY_RETURN_STATE && !parentA && !parentB; + bool ax_ax = (parentA && parentB) && *parentA == *parentB; // ax+ax -> ax if (both$ || ax_ax) { - mergedParents[k] = a_parent; // choose left + mergedParents[k] = std::move(parentA); // choose left mergedReturnStates[k] = payload; - } - else { // ax+ay -> a'[x,y] - Ref mergedParent = merge(a_parent, b_parent, rootIsWildcard, mergeCache); - mergedParents[k] = mergedParent; + } else { // ax+ay -> a'[x,y] + mergedParents[k] = merge(std::move(parentA), std::move(parentB), rootIsWildcard, mergeCache); mergedReturnStates[k] = payload; } i++; // hop over left one as usual j++; // but also skip one in right side since we merge } else if (a->returnStates[i] < b->returnStates[j]) { // copy a[i] to M - mergedParents[k] = a_parent; + mergedParents[k] = std::move(parentA); mergedReturnStates[k] = a->returnStates[i]; i++; - } - else { // b > a, copy b[j] to M - mergedParents[k] = b_parent; + } else { // b > a, copy b[j] to M + mergedParents[k] = std::move(parentB); mergedReturnStates[k] = b->returnStates[j]; j++; } @@ -307,13 +384,13 @@ Ref PredictionContext::mergeArrays(const RefreturnStates.size()) { - for (std::vector::size_type p = i; p < a->returnStates.size(); p++) { + for (auto p = i; p < a->returnStates.size(); p++) { mergedParents[k] = a->parents[p]; mergedReturnStates[k] = a->returnStates[p]; k++; } } else { - for (std::vector::size_type p = j; p < b->returnStates.size(); p++) { + for (auto p = j; p < b->returnStates.size(); p++) { mergedParents[k] = b->parents[p]; mergedReturnStates[k] = b->returnStates[p]; k++; @@ -323,60 +400,41 @@ Ref PredictionContext::mergeArrays(const Ref a_ = SingletonPredictionContext::create(mergedParents[0], mergedReturnStates[0]); - if (mergeCache != nullptr) { - mergeCache->put(a, b, a_); + auto c = SingletonPredictionContext::create(std::move(mergedParents[0]), mergedReturnStates[0]); + if (mergeCache) { + return mergeCache->put(a, b, std::move(c)); } - return a_; + return c; } mergedParents.resize(k); mergedReturnStates.resize(k); } - Ref M = std::make_shared(mergedParents, mergedReturnStates); + ArrayPredictionContext m(std::move(mergedParents), std::move(mergedReturnStates)); // if we created same array as a or b, return that instead // TODO: track whether this is possible above during merge sort for speed - if (*M == *a) { - if (mergeCache != nullptr) { - mergeCache->put(a, b, a); + if (m == *a) { + if (mergeCache) { + return mergeCache->put(a, b, a); } return a; } - if (*M == *b) { - if (mergeCache != nullptr) { - mergeCache->put(a, b, b); + if (m == *b) { + if (mergeCache) { + return mergeCache->put(a, b, b); } return b; } - // ml: this part differs from Java code. We have to recreate the context as the parents array is copied on creation. - if (combineCommonParents(mergedParents)) { - mergedReturnStates.resize(mergedParents.size()); - M = std::make_shared(mergedParents, mergedReturnStates); + if (combineCommonParents(m.parents)) { + m.returnStates.resize(m.parents.size()); } - - if (mergeCache != nullptr) { - mergeCache->put(a, b, M); + auto c = std::make_shared(std::move(m)); + if (mergeCache) { + return mergeCache->put(a, b, std::move(c)); } - return M; -} - -bool PredictionContext::combineCommonParents(std::vector> &parents) { - - std::set> uniqueParents; - for (size_t p = 0; p < parents.size(); ++p) { - Ref parent = parents[p]; - if (uniqueParents.find(parent) == uniqueParents.end()) { // don't replace - uniqueParents.insert(parent); - } - } - - for (size_t p = 0; p < parents.size(); ++p) { - parents[p] = *uniqueParents.find(parents[p]); - } - - return true; + return c; } std::string PredictionContext::toDOTString(const Ref &context) { @@ -388,13 +446,12 @@ std::string PredictionContext::toDOTString(const Ref &c ss << "digraph G {\n" << "rankdir=LR;\n"; std::vector> nodes = getAllContextNodes(context); - std::sort(nodes.begin(), nodes.end(), [](const Ref &o1, const Ref &o2) { - return o1->id - o2->id; - }); + std::unordered_map nodeIds; + size_t nodeId = 0; for (const auto ¤t : nodes) { if (current->getContextType() == PredictionContextType::SINGLETON) { - std::string s = std::to_string(current->id); + std::string s = std::to_string(insertOrAssignNodeId(nodeIds, nodeId, current.get())); ss << " s" << s; std::string returnState = std::to_string(current->getReturnState(0)); if (current == PredictionContext::EMPTY) { @@ -404,7 +461,7 @@ std::string PredictionContext::toDOTString(const Ref &c continue; } Ref arr = std::static_pointer_cast(current); - ss << " s" << arr->id << " [shape=box, label=\"" << "["; + ss << " s" << insertOrAssignNodeId(nodeIds, nodeId, arr.get()) << " [shape=box, label=\"" << "["; bool first = true; for (auto inv : arr->returnStates) { if (!first) { @@ -429,7 +486,7 @@ std::string PredictionContext::toDOTString(const Ref &c if (!current->getParent(i)) { continue; } - ss << " s" << current->id << "->" << "s" << current->getParent(i)->id; + ss << " s" << insertOrAssignNodeId(nodeIds, nodeId, current.get()) << "->" << "s" << insertOrAssignNodeId(nodeIds, nodeId, current->getParent(i).get()); if (current->size() > 1) { ss << " [label=\"parent[" << i << "]\"];\n"; } else { @@ -444,104 +501,23 @@ std::string PredictionContext::toDOTString(const Ref &c // The "visited" map is just a temporary structure to control the retrieval process (which is recursive). Ref PredictionContext::getCachedContext(const Ref &context, - PredictionContextCache &contextCache, std::map, Ref> &visited) { - if (context->isEmpty()) { - return context; - } - - { - auto iterator = visited.find(context); - if (iterator != visited.end()) - return iterator->second; // Not necessarly the same as context. - } - - auto iterator = contextCache.find(context); - if (iterator != contextCache.end()) { - visited[context] = *iterator; - - return *iterator; - } - - bool changed = false; - - std::vector> parents(context->size()); - for (size_t i = 0; i < parents.size(); i++) { - Ref parent = getCachedContext(context->getParent(i), contextCache, visited); - if (changed || parent != context->getParent(i)) { - if (!changed) { - parents.clear(); - for (size_t j = 0; j < context->size(); j++) { - parents.push_back(context->getParent(j)); - } - - changed = true; - } - - parents[i] = parent; - } - } - - if (!changed) { - contextCache.insert(context); - visited[context] = context; - - return context; - } - - Ref updated; - if (parents.empty()) { - updated = EMPTY; - } else if (parents.size() == 1) { - updated = SingletonPredictionContext::create(parents[0], context->getReturnState(0)); - contextCache.insert(updated); - } else { - updated = std::make_shared(parents, std::dynamic_pointer_cast(context)->returnStates); - contextCache.insert(updated); - } - - visited[updated] = updated; - visited[context] = updated; - - return updated; + PredictionContextCache &contextCache) { + std::unordered_map, Ref> visited; + return getCachedContextImpl(context, contextCache, visited); } std::vector> PredictionContext::getAllContextNodes(const Ref &context) { std::vector> nodes; - std::set visited; - getAllContextNodes_(context, nodes, visited); + std::unordered_set visited; + getAllContextNodesImpl(context, nodes, visited); return nodes; } - -void PredictionContext::getAllContextNodes_(const Ref &context, std::vector> &nodes, - std::set &visited) { - - if (visited.find(context.get()) != visited.end()) { - return; // Already done. - } - - visited.insert(context.get()); - nodes.push_back(context); - - for (size_t i = 0; i < context->size(); i++) { - getAllContextNodes_(context->getParent(i), nodes, visited); - } -} - -std::string PredictionContext::toString() const { - - return antlrcpp::toString(this); -} - -std::string PredictionContext::toString(Recognizer * /*recog*/) const { - return toString(); -} - -std::vector PredictionContext::toStrings(Recognizer *recognizer, int currentState) { +std::vector PredictionContext::toStrings(Recognizer *recognizer, int currentState) const { return toStrings(recognizer, EMPTY, currentState); } -std::vector PredictionContext::toStrings(Recognizer *recognizer, const Ref &stop, int currentState) { +std::vector PredictionContext::toStrings(Recognizer *recognizer, const Ref &stop, int currentState) const { std::vector result; @@ -610,33 +586,39 @@ std::vector PredictionContext::toStrings(Recognizer *recognizer, co return result; } -//----------------- PredictionContextMergeCache ------------------------------------------------------------------------ +//----------------- PredictionContextCache ------------------------------------------------------------------------ -Ref PredictionContextMergeCache::put(Ref const& key1, Ref const& key2, - Ref const& value) { - Ref previous; +void PredictionContextCache::put(const Ref &value) { + _data.insert(value); +} - auto iterator = _data.find(key1); - if (iterator == _data.end()) - _data[key1][key2] = value; - else { - auto iterator2 = iterator->second.find(key2); - if (iterator2 != iterator->second.end()) - previous = iterator2->second; - iterator->second[key2] = value; +Ref PredictionContextCache::get(const Ref &value) const { + auto iterator = _data.find(value); + if (iterator == _data.end()) { + return nullptr; } + return *iterator; +} - return previous; +//----------------- PredictionContextMergeCache ------------------------------------------------------------------------ + +Ref PredictionContextMergeCache::put(const Ref &key1, + const Ref &key2, + Ref value) { + return _data.try_emplace(key1).first->second.insert_or_assign(key2, std::move(value)).first->second; } -Ref PredictionContextMergeCache::get(Ref const& key1, Ref const& key2) { - auto iterator = _data.find(key1); - if (iterator == _data.end()) +Ref PredictionContextMergeCache::get(const Ref &key1, + const Ref &key2) const { + auto iterator1 = _data.find(key1); + if (iterator1 == _data.end()) { return nullptr; + } - auto iterator2 = iterator->second.find(key2); - if (iterator2 == iterator->second.end()) + auto iterator2 = iterator1->second.find(key2); + if (iterator2 == iterator1->second.end()) { return nullptr; + } return iterator2->second; } @@ -644,20 +626,3 @@ Ref PredictionContextMergeCache::get(ReftoString() + "\n"; - - return result; -} - -size_t PredictionContextMergeCache::count() const { - size_t result = 0; - for (const auto &entry : _data) - result += entry.second.size(); - return result; -} - diff --git a/runtime/Cpp/runtime/src/atn/PredictionContext.h b/runtime/Cpp/runtime/src/atn/PredictionContext.h index 94bba89b56c..5c7f379e6a2 100755 --- a/runtime/Cpp/runtime/src/atn/PredictionContext.h +++ b/runtime/Cpp/runtime/src/atn/PredictionContext.h @@ -15,12 +15,9 @@ namespace antlr4 { namespace atn { - struct PredictionContextHasher; - struct PredictionContextComparer; + class PredictionContextCache; class PredictionContextMergeCache; - typedef std::unordered_set, PredictionContextHasher, PredictionContextComparer> PredictionContextCache; - class ANTLR4CPP_PUBLIC PredictionContext { public: /// Represents $ in local context prediction, which means wildcard. @@ -35,69 +32,12 @@ namespace atn { // conflict with real return states. static constexpr size_t EMPTY_RETURN_STATE = std::numeric_limits::max() - 9; - private: - static constexpr size_t INITIAL_HASH = 1; - - public: - static std::atomic globalNodeCount; - const size_t id; - - /// - /// Stores the computed hash code of this . The hash - /// code is computed in parts to match the following reference algorithm. - /// - ///
-    ///  private int referenceHashCode() {
-    ///      int hash = ();
-    ///
-    ///      for (int i = 0; i < ; i++) {
-    ///          hash = (hash, (i));
-    ///      }
-    ///
-    ///      for (int i = 0; i < ; i++) {
-    ///          hash = (hash, (i));
-    ///      }
-    ///
-    ///      hash = (hash, 2 * );
-    ///      return hash;
-    ///  }
-    /// 
- ///
- const size_t cachedHashCode; - - protected: - explicit PredictionContext(size_t cachedHashCode); - - public: - virtual ~PredictionContext() = default; - - virtual PredictionContextType getContextType() const = 0; - /// Convert a RuleContext tree to a PredictionContext graph. /// Return EMPTY if outerContext is empty. static Ref fromRuleContext(const ATN &atn, RuleContext *outerContext); - - virtual size_t size() const = 0; - virtual Ref getParent(size_t index) const = 0; - virtual size_t getReturnState(size_t index) const = 0; - - virtual bool operator == (const PredictionContext &o) const = 0; - - /// This means only the EMPTY (wildcard? not sure) context is in set. - virtual bool isEmpty() const; - virtual bool hasEmptyPath() const; - virtual size_t hashCode() const; - - protected: - static size_t calculateEmptyHashCode(); - static size_t calculateHashCode(Ref parent, size_t returnState); - static size_t calculateHashCode(const std::vector> &parents, - const std::vector &returnStates); - - public: // dispatch - static Ref merge(const Ref &a, const Ref &b, - bool rootIsWildcard, PredictionContextMergeCache *mergeCache); + static Ref merge(Ref a, Ref b, + bool rootIsWildcard, PredictionContextMergeCache *mergeCache); /// /// Merge two instances. @@ -133,8 +73,8 @@ namespace atn { /// {@code true} if this is a local-context merge, /// otherwise false to indicate a full-context merge /// - static Ref mergeSingletons(const Ref &a, - const Ref &b, bool rootIsWildcard, PredictionContextMergeCache *mergeCache); + static Ref mergeSingletons(Ref a, Ref b, + bool rootIsWildcard, PredictionContextMergeCache *mergeCache); /** * Handle case where at least one of {@code a} or {@code b} is @@ -174,8 +114,8 @@ namespace atn { * @param rootIsWildcard {@code true} if this is a local-context merge, * otherwise false to indicate a full-context merge */ - static Ref mergeRoot(const Ref &a, - const Ref &b, bool rootIsWildcard); + static Ref mergeRoot(Ref a, Ref b, + bool rootIsWildcard); /** * Merge two {@link ArrayPredictionContext} instances. @@ -196,65 +136,120 @@ namespace atn { * {@link SingletonPredictionContext}.
*

*/ - static Ref mergeArrays(const Ref &a, - const Ref &b, bool rootIsWildcard, PredictionContextMergeCache *mergeCache); - - protected: - /// Make pass over all M parents; merge any equal() ones. - /// @returns true if the list has been changed (i.e. duplicates where found). - static bool combineCommonParents(std::vector> &parents); + static Ref mergeArrays(Ref a, Ref b, + bool rootIsWildcard, PredictionContextMergeCache *mergeCache); - public: static std::string toDOTString(const Ref &context); static Ref getCachedContext(const Ref &context, - PredictionContextCache &contextCache, - std::map, Ref> &visited); + PredictionContextCache &contextCache); // ter's recursive version of Sam's getAllNodes() static std::vector> getAllContextNodes(const Ref &context); - static void getAllContextNodes_(const Ref &context, - std::vector> &nodes, std::set &visited); - virtual std::string toString() const; - virtual std::string toString(Recognizer *recog) const; + virtual ~PredictionContext() = default; - std::vector toStrings(Recognizer *recognizer, int currentState); - std::vector toStrings(Recognizer *recognizer, const Ref &stop, int currentState); - }; + virtual PredictionContextType getContextType() const = 0; - struct PredictionContextHasher { - size_t operator () (const Ref &k) const { - return k->hashCode(); - } + virtual size_t size() const = 0; + virtual const Ref& getParent(size_t index) const = 0; + virtual size_t getReturnState(size_t index) const = 0; + + /// This means only the EMPTY (wildcard? not sure) context is in set. + virtual bool isEmpty() const = 0; + bool hasEmptyPath() const; + + size_t hashCode() const; + + virtual bool equals(const PredictionContext &other) const = 0; + + virtual std::string toString() const = 0; + + std::vector toStrings(Recognizer *recognizer, int currentState) const; + std::vector toStrings(Recognizer *recognizer, const Ref &stop, int currentState) const; + + protected: + PredictionContext(); + + PredictionContext(PredictionContext&& other); + + virtual size_t hashCodeImpl() const = 0; + + private: + mutable std::atomic _hashCode; }; - struct PredictionContextComparer { - bool operator () (const Ref &lhs, const Ref &rhs) const - { - if (lhs == rhs) // Object identity. - return true; - return (lhs->hashCode() == rhs->hashCode()) && (*lhs == *rhs); - } + inline bool operator==(const PredictionContext &lhs, const PredictionContext &rhs) { + return lhs.equals(rhs); + } + + inline bool operator!=(const PredictionContext &lhs, const PredictionContext &rhs) { + return !operator==(lhs, rhs); + } + + class ANTLR4CPP_PUBLIC PredictionContextCache final { + public: + void put(const Ref &value); + + Ref get(const Ref &value) const; + + private: + struct PredictionContextHasher final { + size_t operator()(const Ref &predictionContext) const { + return predictionContext->hashCode(); + } + }; + + struct PredictionContextComparer final { + bool operator()(const Ref &lhs, const Ref &rhs) const { + return *lhs == *rhs; + } + }; + + std::unordered_set, PredictionContextHasher, PredictionContextComparer> _data; }; - class PredictionContextMergeCache { + class ANTLR4CPP_PUBLIC PredictionContextMergeCache final { public: - Ref put(Ref const& key1, Ref const& key2, - Ref const& value); - Ref get(Ref const& key1, Ref const& key2); + Ref put(const Ref &key1, + const Ref &key2, + Ref value); + + Ref get(const Ref &key1, + const Ref &key2) const; void clear(); - std::string toString() const; - size_t count() const; private: + struct PredictionContextHasher final { + size_t operator()(const Ref &predictionContext) const { + return predictionContext->hashCode(); + } + }; + + struct PredictionContextComparer final { + bool operator()(const Ref &lhs, const Ref &rhs) const { + return *lhs == *rhs; + } + }; + std::unordered_map, - std::unordered_map, Ref, PredictionContextHasher, PredictionContextComparer>, + std::unordered_map, Ref, + PredictionContextHasher, PredictionContextComparer>, PredictionContextHasher, PredictionContextComparer> _data; }; -} // namespace atn -} // namespace antlr4 +} // namespace atn +} // namespace antlr4 + +namespace std { + + template <> + struct hash<::antlr4::atn::PredictionContext> { + size_t operator()(const ::antlr4::atn::PredictionContext &predictionContext) const { + return predictionContext.hashCode(); + } + }; +} // namespace std diff --git a/runtime/Cpp/runtime/src/atn/PredictionMode.cpp b/runtime/Cpp/runtime/src/atn/PredictionMode.cpp index 755eb454429..f36d42f1625 100755 --- a/runtime/Cpp/runtime/src/atn/PredictionMode.cpp +++ b/runtime/Cpp/runtime/src/atn/PredictionMode.cpp @@ -73,8 +73,8 @@ bool PredictionModeClass::hasSLLConflictTerminatingPrediction(PredictionMode mod } bool PredictionModeClass::hasConfigInRuleStopState(ATNConfigSet *configs) { - for (auto &c : configs->configs) { - if (is(c->state)) { + for (const auto &config : configs->configs) { + if (config->state != nullptr && config->state->getStateType() == ATNStateType::RULE_STOP) { return true; } } @@ -84,7 +84,7 @@ bool PredictionModeClass::hasConfigInRuleStopState(ATNConfigSet *configs) { bool PredictionModeClass::allConfigsInRuleStopStates(ATNConfigSet *configs) { for (auto &config : configs->configs) { - if (!is(config->state)) { + if (config->state == nullptr || config->state->getStateType() != ATNStateType::RULE_STOP) { return false; } } @@ -142,7 +142,7 @@ size_t PredictionModeClass::getUniqueAlt(const std::vector& al antlrcpp::BitSet PredictionModeClass::getAlts(const std::vector& altsets) { antlrcpp::BitSet all; - for (antlrcpp::BitSet alts : altsets) { + for (const auto &alts : altsets) { all |= alts; } @@ -151,43 +151,44 @@ antlrcpp::BitSet PredictionModeClass::getAlts(const std::vectorconfigs) { + for (const auto &config : configs->configs) { alts.set(config->alt); } return alts; } std::vector PredictionModeClass::getConflictingAltSubsets(ATNConfigSet *configs) { - std::unordered_map configToAlts; + std::unordered_map configToAlts; for (auto &config : configs->configs) { configToAlts[config.get()].set(config->alt); } std::vector values; - for (auto it : configToAlts) { - values.push_back(it.second); + values.reserve(configToAlts.size()); + for (const auto &pair : configToAlts) { + values.push_back(pair.second); } return values; } -std::map PredictionModeClass::getStateToAltMap(ATNConfigSet *configs) { - std::map m; - for (auto &c : configs->configs) { +std::unordered_map PredictionModeClass::getStateToAltMap(ATNConfigSet *configs) { + std::unordered_map m; + for (const auto &c : configs->configs) { m[c->state].set(c->alt); } return m; } bool PredictionModeClass::hasStateAssociatedWithOneAlt(ATNConfigSet *configs) { - std::map x = getStateToAltMap(configs); - for (std::map::iterator it = x.begin(); it != x.end(); it++){ - if (it->second.count() == 1) return true; + auto x = getStateToAltMap(configs); + for (const auto &pair : x){ + if (pair.second.count() == 1) return true; } return false; } size_t PredictionModeClass::getSingleViableAlt(const std::vector& altsets) { antlrcpp::BitSet viableAlts; - for (antlrcpp::BitSet alts : altsets) { + for (const auto &alts : altsets) { size_t minAlt = alts.nextSetBit(0); viableAlts.set(minAlt); diff --git a/runtime/Cpp/runtime/src/atn/PredictionMode.h b/runtime/Cpp/runtime/src/atn/PredictionMode.h index 726f4cf40f5..11d1bbe034b 100755 --- a/runtime/Cpp/runtime/src/atn/PredictionMode.h +++ b/runtime/Cpp/runtime/src/atn/PredictionMode.h @@ -425,7 +425,7 @@ namespace atn { /// cref="ATNConfig#alt alt"/> /// ///
- static std::map getStateToAltMap(ATNConfigSet *configs); + static std::unordered_map getStateToAltMap(ATNConfigSet *configs); static bool hasStateAssociatedWithOneAlt(ATNConfigSet *configs); diff --git a/runtime/Cpp/runtime/src/atn/SemanticContext.h b/runtime/Cpp/runtime/src/atn/SemanticContext.h index b7193332f78..22df35241e8 100755 --- a/runtime/Cpp/runtime/src/atn/SemanticContext.h +++ b/runtime/Cpp/runtime/src/atn/SemanticContext.h @@ -193,8 +193,6 @@ namespace atn { } // namespace atn } // namespace antlr4 -// Hash function for SemanticContext, used in the MurmurHash::update function - namespace std { template <> diff --git a/runtime/Cpp/runtime/src/atn/SingletonPredictionContext.cpp b/runtime/Cpp/runtime/src/atn/SingletonPredictionContext.cpp index 5fe4f8aacd2..c65fc6bd783 100755 --- a/runtime/Cpp/runtime/src/atn/SingletonPredictionContext.cpp +++ b/runtime/Cpp/runtime/src/atn/SingletonPredictionContext.cpp @@ -5,16 +5,18 @@ #include "atn/SingletonPredictionContext.h" +#include "support/Casts.h" +#include "misc/MurmurHash.h" + using namespace antlr4::atn; +using namespace antlrcpp; SingletonPredictionContext::SingletonPredictionContext(Ref parent, size_t returnState) - : PredictionContext(parent ? calculateHashCode(parent, returnState) : calculateEmptyHashCode()), - parent(std::move(parent)), returnState(returnState) { + : parent(std::move(parent)), returnState(returnState) { assert(returnState != ATNState::INVALID_STATE_NUMBER); } Ref SingletonPredictionContext::create(Ref parent, size_t returnState) { - if (returnState == EMPTY_RETURN_STATE && parent) { // someone can pass in the bits of an array ctx that mean $ return std::dynamic_pointer_cast(EMPTY); @@ -34,7 +36,7 @@ size_t SingletonPredictionContext::size() const { return 1; } -Ref SingletonPredictionContext::getParent(size_t index) const { +const Ref& SingletonPredictionContext::getParent(size_t index) const { assert(index == 0); ((void)(index)); // Make Release build happy. return parent; @@ -46,29 +48,32 @@ size_t SingletonPredictionContext::getReturnState(size_t index) const { return returnState; } -bool SingletonPredictionContext::operator == (const PredictionContext &o) const { - if (this == &o) { +size_t SingletonPredictionContext::hashCodeImpl() const { + size_t hash = misc::MurmurHash::initialize(); + hash = misc::MurmurHash::update(hash, static_cast(getContextType())); + hash = misc::MurmurHash::update(hash, parent); + hash = misc::MurmurHash::update(hash, returnState); + return misc::MurmurHash::finish(hash, 3); +} + +bool SingletonPredictionContext::equals(const PredictionContext &other) const { + if (this == &other) { return true; } - - if (o.getContextType() != PredictionContextType::SINGLETON) { + if (getContextType() != other.getContextType()) { return false; } - - const SingletonPredictionContext *other = static_cast(&o); - if (this->hashCode() != other->hashCode()) { - return false; // can't be same if hash is different - } - - if (returnState != other->returnState) + const auto &singleton = downCast(other); + if (returnState != singleton.returnState) { return false; - - if (!parent && !other->parent) + } + if (!parent && !singleton.parent) { return true; - if (!parent || !other->parent) + } + if (!parent || !singleton.parent) { return false; - - return parent == other->parent || *parent == *other->parent; + } + return parent == singleton.parent || *parent == *singleton.parent; } std::string SingletonPredictionContext::toString() const { diff --git a/runtime/Cpp/runtime/src/atn/SingletonPredictionContext.h b/runtime/Cpp/runtime/src/atn/SingletonPredictionContext.h index 44af6d6df7d..46beb3b5754 100755 --- a/runtime/Cpp/runtime/src/atn/SingletonPredictionContext.h +++ b/runtime/Cpp/runtime/src/atn/SingletonPredictionContext.h @@ -12,6 +12,8 @@ namespace atn { class ANTLR4CPP_PUBLIC SingletonPredictionContext final : public PredictionContext { public: + static Ref create(Ref parent, size_t returnState); + // Usually a parent is linked via a weak ptr. Not so here as we have kinda reverse reference chain. // There are no child contexts stored here and often the parent context is left dangling when it's // owning ATNState is released. In order to avoid having this context released as well (leaving all other contexts @@ -21,18 +23,17 @@ namespace atn { const size_t returnState; SingletonPredictionContext(Ref parent, size_t returnState); - virtual ~SingletonPredictionContext() = default; - - static Ref create(Ref parent, size_t returnState); PredictionContextType getContextType() const override; - - virtual bool isEmpty() const override; - virtual size_t size() const override; - virtual Ref getParent(size_t index) const override; - virtual size_t getReturnState(size_t index) const override; - virtual bool operator == (const PredictionContext &o) const override; - virtual std::string toString() const override; + bool isEmpty() const override; + size_t size() const override; + const Ref& getParent(size_t index) const override; + size_t getReturnState(size_t index) const override; + bool equals(const PredictionContext &other) const override; + std::string toString() const override; + + protected: + size_t hashCodeImpl() const override; }; } // namespace atn diff --git a/runtime/Cpp/runtime/src/misc/IntervalSet.cpp b/runtime/Cpp/runtime/src/misc/IntervalSet.cpp index 31f26b77616..ea910afe6e9 100755 --- a/runtime/Cpp/runtime/src/misc/IntervalSet.cpp +++ b/runtime/Cpp/runtime/src/misc/IntervalSet.cpp @@ -265,18 +265,13 @@ bool IntervalSet::contains(size_t el) const { } bool IntervalSet::contains(ssize_t el) const { - if (_intervals.empty()) + if (_intervals.empty() || el < _intervals.front().a || el > _intervals.back().b) { return false; - - if (el < _intervals[0].a) // list is sorted and el is before first interval; not here - return false; - - for (const auto &interval : _intervals) { - if (el >= interval.a && el <= interval.b) { - return true; // found in this interval - } } - return false; + + return std::binary_search(_intervals.begin(), _intervals.end(), Interval(el, el), [](const Interval &lhs, const Interval &rhs) { + return lhs.b < rhs.a; + }); } bool IntervalSet::isEmpty() const { @@ -306,7 +301,7 @@ ssize_t IntervalSet::getMinElement() const { return Token::INVALID_TYPE; } - return _intervals[0].a; + return _intervals.front().a; } std::vector const& IntervalSet::getIntervals() const {