|
9 | 9 | #include "base/dictionary.hpp"
|
10 | 10 | #include "base/function.hpp"
|
11 | 11 | #include "base/exception.hpp"
|
| 12 | +#include "base/logger.hpp" |
12 | 13 | #include "base/scriptframe.hpp"
|
13 | 14 | #include "base/shared-object.hpp"
|
14 | 15 | #include "base/convert.hpp"
|
| 16 | +#include <cmath> |
| 17 | +#include <cstdint> |
| 18 | +#include <iterator> |
15 | 19 | #include <map>
|
| 20 | +#include <memory> |
| 21 | +#include <utility> |
| 22 | +#include <vector> |
16 | 23 |
|
17 | 24 | namespace icinga
|
18 | 25 | {
|
@@ -562,23 +569,116 @@ class NotInExpression final : public BinaryExpression
|
562 | 569 | ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
|
563 | 570 | };
|
564 | 571 |
|
565 |
| -class LogicalAndExpression final : public BinaryExpression |
| 572 | +class AssociativeExpression : public BinaryExpression |
| 573 | +{ |
| 574 | +public: |
| 575 | + using BinaryExpression::BinaryExpression; |
| 576 | + |
| 577 | +protected: |
| 578 | + template<class Branch> |
| 579 | + void RebalanceLeaves() |
| 580 | + { |
| 581 | + uintmax_t count = 0, depth = 0, depthBuf = 0; |
| 582 | + StatLeaves<Branch>(count, depth, depthBuf); |
| 583 | + |
| 584 | + if (depth > logl(count) / m_Log2 * 2) { |
| 585 | + { |
| 586 | + Log msg (LogDebug, "config"); |
| 587 | + |
| 588 | + msg << "Detected associative expression tree with " << count << " leaves, but " |
| 589 | + << depth << " > log2(" << count << ") * 2 nesting levels. Rebalancing to log2(" << count << ") nesting levels.\n"; |
| 590 | + |
| 591 | + ShowCodeLocation(msg, m_DebugInfo); |
| 592 | + } |
| 593 | + |
| 594 | + std::vector<std::unique_ptr<Expression>> leaves; |
| 595 | + leaves.reserve(count); |
| 596 | + HarvestLeaves<Branch>(leaves); |
| 597 | + |
| 598 | + auto split (leaves.begin() + leaves.size() / 2u); |
| 599 | + m_Operand1 = AssembleLeaves<Branch>(leaves.begin(), split); |
| 600 | + m_Operand2 = AssembleLeaves<Branch>(split, leaves.end()); |
| 601 | + } |
| 602 | + } |
| 603 | + |
| 604 | +private: |
| 605 | + static const long double m_Log2; |
| 606 | + |
| 607 | + template<class Branch> |
| 608 | + void StatLeaves(uintmax_t& count, uintmax_t& depth, uintmax_t& depthBuf) |
| 609 | + { |
| 610 | + ++depthBuf; |
| 611 | + |
| 612 | + if (depth < depthBuf) { |
| 613 | + depth = depthBuf; |
| 614 | + } |
| 615 | + |
| 616 | + for (auto op : {m_Operand1.get(), m_Operand2.get()}) { |
| 617 | + auto branch (dynamic_cast<Branch*>(op)); |
| 618 | + |
| 619 | + if (branch == nullptr) { |
| 620 | + count += 1u; |
| 621 | + } else { |
| 622 | + branch->template StatLeaves<Branch>(count, depth, depthBuf); |
| 623 | + } |
| 624 | + } |
| 625 | + |
| 626 | + --depthBuf; |
| 627 | + } |
| 628 | + |
| 629 | + template<class Branch> |
| 630 | + void HarvestLeaves(std::vector<std::unique_ptr<Expression>>& leaves) |
| 631 | + { |
| 632 | + for (auto op : {&m_Operand1, &m_Operand2}) { |
| 633 | + auto branch (dynamic_cast<Branch*>(op->get())); |
| 634 | + |
| 635 | + if (branch == nullptr) { |
| 636 | + leaves.emplace_back(std::move(*op)); |
| 637 | + } else { |
| 638 | + branch->template HarvestLeaves<Branch>(leaves); |
| 639 | + } |
| 640 | + } |
| 641 | + } |
| 642 | + |
| 643 | + template<class Branch, class Iter> |
| 644 | + std::unique_ptr<Expression> AssembleLeaves(Iter begin, Iter end) |
| 645 | + { |
| 646 | + auto distance (std::distance(begin, end)); |
| 647 | + |
| 648 | + if (distance < 2u) { |
| 649 | + return std::move(*begin); |
| 650 | + } else { |
| 651 | + auto split (begin + distance / 2u); |
| 652 | + |
| 653 | + return std::unique_ptr<Branch>(new Branch( |
| 654 | + AssembleLeaves<Branch, Iter>(std::move(begin), split), |
| 655 | + AssembleLeaves<Branch, Iter>(split, std::move(end)) |
| 656 | + )); |
| 657 | + } |
| 658 | + } |
| 659 | +}; |
| 660 | + |
| 661 | +class LogicalAndExpression final : public AssociativeExpression |
566 | 662 | {
|
567 | 663 | public:
|
568 | 664 | LogicalAndExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
|
569 |
| - : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo) |
570 |
| - { } |
| 665 | + : AssociativeExpression(std::move(operand1), std::move(operand2), debugInfo) |
| 666 | + { |
| 667 | + RebalanceLeaves<LogicalAndExpression>(); |
| 668 | + } |
571 | 669 |
|
572 | 670 | protected:
|
573 | 671 | ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
|
574 | 672 | };
|
575 | 673 |
|
576 |
| -class LogicalOrExpression final : public BinaryExpression |
| 674 | +class LogicalOrExpression final : public AssociativeExpression |
577 | 675 | {
|
578 | 676 | public:
|
579 | 677 | LogicalOrExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
|
580 |
| - : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo) |
581 |
| - { } |
| 678 | + : AssociativeExpression(std::move(operand1), std::move(operand2), debugInfo) |
| 679 | + { |
| 680 | + RebalanceLeaves<LogicalOrExpression>(); |
| 681 | + } |
582 | 682 |
|
583 | 683 | protected:
|
584 | 684 | ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
|
|
0 commit comments