Skip to content

Commit e9e94ec

Browse files
committed
Auto-rewrite associative DSL expression trees
... if depth > log2(leaves) * 2 refs #7827
1 parent 9c7f63c commit e9e94ec

File tree

2 files changed

+109
-6
lines changed

2 files changed

+109
-6
lines changed

lib/config/expression.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@
1515
#include "base/namespace.hpp"
1616
#include <boost/exception_ptr.hpp>
1717
#include <boost/exception/errinfo_nested_exception.hpp>
18+
#include <cmath>
1819

1920
using namespace icinga;
2021

2122
boost::signals2::signal<void (ScriptFrame&, ScriptError *ex, const DebugInfo&)> Expression::OnBreakpoint;
2223
boost::thread_specific_ptr<bool> l_InBreakpointHandler;
2324

25+
const long double AssociativeExpression::m_Log2 = logl(2);
26+
2427
Expression::~Expression()
2528
{ }
2629

lib/config/expression.hpp

Lines changed: 106 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,17 @@
99
#include "base/dictionary.hpp"
1010
#include "base/function.hpp"
1111
#include "base/exception.hpp"
12+
#include "base/logger.hpp"
1213
#include "base/scriptframe.hpp"
1314
#include "base/shared-object.hpp"
1415
#include "base/convert.hpp"
16+
#include <cmath>
17+
#include <cstdint>
18+
#include <iterator>
1519
#include <map>
20+
#include <memory>
21+
#include <utility>
22+
#include <vector>
1623

1724
namespace icinga
1825
{
@@ -552,23 +559,116 @@ class NotInExpression final : public BinaryExpression
552559
ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
553560
};
554561

555-
class LogicalAndExpression final : public BinaryExpression
562+
class AssociativeExpression : public BinaryExpression
563+
{
564+
public:
565+
using BinaryExpression::BinaryExpression;
566+
567+
protected:
568+
template<class Branch>
569+
void RebalanceLeaves()
570+
{
571+
uintmax_t count = 0, depth = 0, depthBuf = 0;
572+
StatLeaves<Branch>(count, depth, depthBuf);
573+
574+
if (depth > logl(count) / m_Log2 * 2) {
575+
{
576+
Log msg (LogDebug, "config");
577+
578+
msg << "Detected associative expression tree with " << count << " leaves, but "
579+
<< depth << " > log2(" << count << ") * 2 nesting levels. Rebalancing to log2(" << count << ") nesting levels.\n";
580+
581+
ShowCodeLocation(msg, m_DebugInfo);
582+
}
583+
584+
std::vector<std::unique_ptr<Expression>> leaves;
585+
leaves.reserve(count);
586+
HarvestLeaves<Branch>(leaves);
587+
588+
auto split (leaves.begin() + leaves.size() / 2u);
589+
m_Operand1 = AssembleLeaves<Branch>(leaves.begin(), split);
590+
m_Operand2 = AssembleLeaves<Branch>(split, leaves.end());
591+
}
592+
}
593+
594+
private:
595+
static const long double m_Log2;
596+
597+
template<class Branch>
598+
void StatLeaves(uintmax_t& count, uintmax_t& depth, uintmax_t& depthBuf)
599+
{
600+
++depthBuf;
601+
602+
if (depth < depthBuf) {
603+
depth = depthBuf;
604+
}
605+
606+
for (auto op : {m_Operand1.get(), m_Operand2.get()}) {
607+
auto branch (dynamic_cast<Branch*>(op));
608+
609+
if (branch == nullptr) {
610+
count += 1u;
611+
} else {
612+
branch->template StatLeaves<Branch>(count, depth, depthBuf);
613+
}
614+
}
615+
616+
--depthBuf;
617+
}
618+
619+
template<class Branch>
620+
void HarvestLeaves(std::vector<std::unique_ptr<Expression>>& leaves)
621+
{
622+
for (auto op : {&m_Operand1, &m_Operand2}) {
623+
auto branch (dynamic_cast<Branch*>(op->get()));
624+
625+
if (branch == nullptr) {
626+
leaves.emplace_back(std::move(*op));
627+
} else {
628+
branch->template HarvestLeaves<Branch>(leaves);
629+
}
630+
}
631+
}
632+
633+
template<class Branch, class Iter>
634+
std::unique_ptr<Expression> AssembleLeaves(Iter begin, Iter end)
635+
{
636+
auto distance (std::distance(begin, end));
637+
638+
if (distance < 2u) {
639+
return std::move(*begin);
640+
} else {
641+
auto split (begin + distance / 2u);
642+
643+
return std::unique_ptr<Branch>(new Branch(
644+
AssembleLeaves<Branch, Iter>(std::move(begin), split),
645+
AssembleLeaves<Branch, Iter>(split, std::move(end))
646+
));
647+
}
648+
}
649+
};
650+
651+
class LogicalAndExpression final : public AssociativeExpression
556652
{
557653
public:
558654
LogicalAndExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
559-
: BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
560-
{ }
655+
: AssociativeExpression(std::move(operand1), std::move(operand2), debugInfo)
656+
{
657+
RebalanceLeaves<LogicalAndExpression>();
658+
}
561659

562660
protected:
563661
ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
564662
};
565663

566-
class LogicalOrExpression final : public BinaryExpression
664+
class LogicalOrExpression final : public AssociativeExpression
567665
{
568666
public:
569667
LogicalOrExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
570-
: BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
571-
{ }
668+
: AssociativeExpression(std::move(operand1), std::move(operand2), debugInfo)
669+
{
670+
RebalanceLeaves<LogicalOrExpression>();
671+
}
572672

573673
protected:
574674
ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;

0 commit comments

Comments
 (0)