#include <cerrno>
#include <fcntl.h>
#include <map>
+#include <string>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
void releaseMMap();
void releaseFD();
- typedef std::pair<const ItemSend *, unsigned> ItemWithScore; // a pair: Item, index of Item
- typedef std::vector<ItemWithScore> ItemsWithScore; // items
- typedef std::pair<unsigned, ItemsWithScore> HighestScoreWithItems; // value for index map: highest index for items, items
- typedef std::map<boost::string_ref, HighestScoreWithItems> MapIndex;
- typedef std::vector<ItemWithScore> SendPrefixIndex;
-
- MapIndex sendIndex; // context default
- SendPrefixIndex sendPrefixIndex; // context default prefix rules
- std::map<uid_t, MapIndex> userSendIndex;
- std::map<uid_t, SendPrefixIndex> userSendPrefixIndex;
-
public:
bool init(const char *filename, bool verify);
bool init(const FB::File *f);
bool initFromXML(const char *config_name);
void release();
- void createSendIndex();
- ldp_xml_parser::DecisionItem getDecisionFromSendIndex(const MatchItemSend &item, const MapIndex &index, const SendPrefixIndex &prefixIndex);
- ldp_xml_parser::DecisionItem getDecisionFromSendIndex(const MatchItemSend &item);
- ldp_xml_parser::DecisionItem getDecisionFromSendIndex(const MatchItemSend &item, uid_t uid);
+ ldp_xml_parser::DecisionItem getDecisionFromSendIndex(const MatchItemSend &item, const FB::PolicySend *policy);
void printContent(const bool xml_format = false) const;
};
ldp_xml_parser::DecisionItem StorageBackendSerialized::StorageBackendSerializedImpl::getDecisionFromSendIndex(const MatchItemSend &item,
- const MapIndex &index, const SendPrefixIndex &prefixIndex) {
- ldp_xml_parser::DecisionItem decision(ldp_xml_parser::Decision::ANY);
+ const FB::PolicySend *policy) {
+
+ const auto *index = policy->index();
+
+ if (!index || index->size() == 0) // this indicates version earlier than LDP2
+ return getDecisionItem(item, policy); // make it old way for old databases
+
+ std::pair<const ItemSend*, uint32_t> currentBest(nullptr, 0);
+
+ auto updateCurrentBest = [¤tBest, &item, &policy](const flatbuffers::Vector<uint32_t> *vec) {
+ // look from the back, the rule is the same as for the full database
+ // we now only check among less elements, because the database is indexed to small lists
+ // item_scores are in increasing order in the index, and they serve also as ids of the policy rules
+ for (auto item_score_it = vec->rbegin(); item_score_it != vec->rend(); item_score_it++) {
+ const auto *fb_item = policy->items()->Get(*item_score_it - 1); // rules are indexed/scored from 1
+ if (*item_score_it > currentBest.second && match(item, fb_item)) {
+ currentBest.first = fb_item;
+ currentBest.second = *item_score_it;
+ return;
+ } else if (*item_score_it <= currentBest.second) {
+ // there is no need to look further as we can't improve the score anymore
+ return;
+ }
+ }
+ };
- ItemWithScore currentBest(nullptr, 0);
+ auto searchAndUpdateCurrentBest = [¤tBest, &index, &updateCurrentBest](boost::string_ref name_ref) {
+ // we need to create C-string for flatbuffers lookups
+ // boost::string_ref gives us correct start, but possibly NUL-terminated in a wrong place, as it does not modify
+ // input string and keeps only the length
+ std::string name(name_ref.data(), name_ref.size());
- auto updateCurrentBest = [¤tBest, &item, &index](boost::string_ref name) {
// check if there are any rules for the name
- auto fit = index.find(name);
- if (fit == index.end())
+ const auto *fit = index->LookupByKey(name.c_str());
+ if (!fit)
return;
// check if there's any chance to get better score
- // fit->second.first is the highest score from vector fit->second.second
- if (fit->second.first <= currentBest.second)
+ if (fit->best_score() <= currentBest.second)
return;
// look for better score
- for (const auto &it: fit->second.second) {
- if (it.second > currentBest.second && match(item, it.first))
- currentBest = it;
- }
+ updateCurrentBest(fit->item_refs());
};
+ const auto *prefixIndex = policy->prefix_index();
+ assert(prefixIndex);
+
// iterate over names
for (const auto &name: item.getNames()) {
// find and check the no-prefix rules
- updateCurrentBest(name);
+ searchAndUpdateCurrentBest(name);
// check the prefix rules
- for (const auto &prefixItem: prefixIndex) {
- if (prefixItem.second > currentBest.second && match(item, prefixItem.first))
- currentBest = prefixItem;
- }
+ updateCurrentBest(prefixIndex);
}
// check no-name rules
- updateCurrentBest("");
+ searchAndUpdateCurrentBest("");
+ // if a matching rule was found, return the decision
if (currentBest.first)
- decision = makeDecisionItem(currentBest.first->decision());
-
- return decision;
-}
+ return makeDecisionItem(currentBest.first->decision());
-ldp_xml_parser::DecisionItem StorageBackendSerialized::StorageBackendSerializedImpl::getDecisionFromSendIndex(const MatchItemSend &item) {
- return getDecisionFromSendIndex(item, sendIndex, sendPrefixIndex);
-}
-
-ldp_xml_parser::DecisionItem StorageBackendSerialized::StorageBackendSerializedImpl::getDecisionFromSendIndex(const MatchItemSend &item, uid_t uid) {
- return getDecisionFromSendIndex(item, userSendIndex[uid], userSendPrefixIndex[uid]);
-}
-
-void StorageBackendSerialized::StorageBackendSerializedImpl::createSendIndex() {
- // helper for adding items to indexes
- auto add = [](MapIndex &index, SendPrefixIndex &prefixIndex, boost::string_ref key, ItemWithScore value) {
- if (value.first->is_name_prefix()) {
- prefixIndex.push_back(value);
- } else {
- index[key].second.push_back(value);
- // we insert items in increasing score order, so we just update the highest score on each add
- index[key].first = value.second;
- }
- };
-
- // context default index
- const auto *policy = file->m_send_set()->context_default();
- unsigned cnt = 1;
-
- const auto *v = policy->items();
- for (auto it = v->begin(); it != v->end(); ++it)
- add(sendIndex, sendPrefixIndex,
- boost::string_ref(it->name()->c_str(), it->name()->size()),
- ItemWithScore(*it, cnt++));
-
- // users index
- for (const auto &uidPolicy: *file->m_send_set()->user()) {
- cnt = 1;
- for (const auto it: *uidPolicy->policy()->items()) {
- add(userSendIndex[uidPolicy->id()],
- userSendPrefixIndex[uidPolicy->id()],
- boost::string_ref(it->name()->c_str(), it->name()->size()),
- ItemWithScore(it, cnt++));
- }
- }
+ // or if no matching rule was not found, return the default decision
+ return ldp_xml_parser::DecisionItem(ldp_xml_parser::Decision::ANY);
}
void StorageBackendSerialized::StorageBackendSerializedImpl::releaseMMap() {
mmapGuard.dismiss();
file = GetFile(mem);
- bool res = file != nullptr;
- if (res)
- createSendIndex();
- return res;
+ return file != nullptr;
}
bool StorageBackendSerialized::StorageBackendSerializedImpl::init(const FB::File *f) {
assert(nullptr == file);
file = f;
- createSendIndex();
return true;
}
}
template <>
+ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextMandatory(const MatchItemSend &item) const {
+ return pimpl->getDecisionFromSendIndex(item, pimpl->getPolicySet<MatchItemSend>()->context_mandatory());
+}
+
+template <>
ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextDefault(const MatchItemSend &item) const {
- return pimpl->getDecisionFromSendIndex(item);
+ return pimpl->getDecisionFromSendIndex(item, pimpl->getPolicySet<MatchItemSend>()->context_default());
}
template <>
auto *policyPair = pimpl->getPolicySet<MatchItemSend>()->user()->LookupByKey(uid);
if (nullptr == policyPair)
return ldp_xml_parser::Decision::ANY;
- return pimpl->getDecisionFromSendIndex(item, uid);
+ return pimpl->getDecisionFromSendIndex(item, policyPair->policy());
}
template <typename T>