|
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 | { |
@@ -552,23 +559,116 @@ class NotInExpression final : public BinaryExpression |
552 | 559 | ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override; |
553 | 560 | }; |
554 | 561 |
|
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 |
556 | 652 | { |
557 | 653 | public: |
558 | 654 | 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 | + } |
561 | 659 |
|
562 | 660 | protected: |
563 | 661 | ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override; |
564 | 662 | }; |
565 | 663 |
|
566 | | -class LogicalOrExpression final : public BinaryExpression |
| 664 | +class LogicalOrExpression final : public AssociativeExpression |
567 | 665 | { |
568 | 666 | public: |
569 | 667 | 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 | + } |
572 | 672 |
|
573 | 673 | protected: |
574 | 674 | ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override; |
|
0 commit comments