Line data Source code
1 : /* 2 : * 3 : * Copyright (c) 2023 Project CHIP Authors 4 : * All rights reserved. 5 : * 6 : * Licensed under the Apache License, Version 2.0 (the "License"); 7 : * you may not use this file except in compliance with the License. 8 : * You may obtain a copy of the License at 9 : * 10 : * http://www.apache.org/licenses/LICENSE-2.0 11 : * 12 : * Unless required by applicable law or agreed to in writing, software 13 : * distributed under the License is distributed on an "AS IS" BASIS, 14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 : * See the License for the specific language governing permissions and 16 : * limitations under the License. 17 : */ 18 : #pragma once 19 : 20 : #include <lib/format/FlatTree.h> 21 : #include <lib/support/Span.h> 22 : 23 : namespace chip { 24 : namespace FlatTree { 25 : 26 : /// Represents a position inside a given tree, allowing for descending 27 : /// and ascending. 28 : /// 29 : /// A possition in the tree may be undefined if descending to a non-existing leaf, 30 : /// however the position still allows moving back again. 31 : /// 32 : /// DESCEND_DEPTH is the maximum remembered depth for going back up. 33 : /// 34 : /// General usage: 35 : /// 36 : /// Position<DataType, 10> position(tree, tree_size); 37 : /// 38 : /// position.Enter(ByName("foo")); 39 : /// position.Enter(ByName("bar")); 40 : /// position.Enter(ByName("baz")); 41 : /// 42 : /// position.Get() /// content of foo::bar::baz if it exists 43 : /// 44 : /// position.Exit(); 45 : /// position.Exit(); 46 : /// 47 : /// position.Get() /// content of foo if it exists 48 : /// 49 : /// position.Enter(ById(1234)); 50 : /// 51 : /// position.Get() /// content of foo::1234 52 : /// 53 : template <typename CONTENT, size_t DESCEND_DEPTH> 54 : class Position 55 : { 56 : public: 57 56 : Position(const Node<CONTENT> * tree, size_t treeSize) : mTree(tree), mTreeSize(treeSize) {} 58 : 59 : template <size_t N> 60 : Position(const std::array<const Node<CONTENT>, N> & tree) : mTree(tree.data()), mTreeSize(N) 61 : {} 62 : 63 : // Move back to the top 64 153 : void ResetToTop() 65 : { 66 153 : mDescendDepth = 0; 67 153 : mUnknownDescendDepth = 0; 68 153 : } 69 : 70 : /// Attempt to find a child of the current position that matches 71 : /// the given matcher 72 : template <typename MATCHER> 73 : void Enter(MATCHER matcher); 74 : 75 : /// Move up the tree, undoes an 'Enter' operation. 76 : void Exit(); 77 : 78 : /// Fetch the value where the node is positioned on or nullptr if that 79 : /// value is not available; 80 : const CONTENT * Get() const; 81 : 82 : /// Returns the entries visited so far 83 : /// 84 : /// WILL RETURN EMPTY if the descend depth has been 85 : /// exceeded. Callers MUST handle empty return. 86 : /// 87 : /// Span valid until one of Enter/Exit functions are called 88 : /// and as long as the Position is valid (points inside the object). 89 : chip::Span<const Entry<CONTENT> *> CurrentPath(); 90 : 91 24 : bool HasValidTree() const { return mTree != nullptr; } 92 : 93 11 : size_t DescendDepth() const { return mDescendDepth + mUnknownDescendDepth; } 94 : 95 : private: 96 : // actual tree that we visit 97 : const Node<CONTENT> * mTree = nullptr; 98 : const size_t mTreeSize = 0; 99 : 100 : // Keeping track of descending into the tree, to be able to move back 101 : // last element in the array is the "current" item 102 : const Entry<CONTENT> * mPositions[DESCEND_DEPTH] = { nullptr }; 103 : size_t mDescendDepth = 0; // filled amount of mDescendPositions 104 : 105 : // Descend past remembering memory or in not-found entries. 106 : size_t mUnknownDescendDepth = 0; // depth in invalid positions 107 : }; 108 : 109 : template <typename CONTENT, size_t DESCEND_DEPTH> 110 920 : const CONTENT * Position<CONTENT, DESCEND_DEPTH>::Get() const 111 : { 112 920 : if (mUnknownDescendDepth > 0) 113 : { 114 137 : return nullptr; 115 : } 116 : 117 783 : if (mDescendDepth == 0) 118 : { 119 0 : return nullptr; 120 : } 121 : 122 783 : return &mPositions[mDescendDepth - 1]->data; 123 : } 124 : 125 : template <typename CONTENT, size_t DESCEND_DEPTH> 126 : template <typename MATCHER> 127 602 : void Position<CONTENT, DESCEND_DEPTH>::Enter(MATCHER matcher) 128 : { 129 602 : if (mUnknownDescendDepth > 0) 130 : { 131 : // keep descending into the unknown 132 66 : mUnknownDescendDepth++; 133 66 : return; 134 : } 135 : 136 : // To be able to descend, we have to be able to remember 137 : // the current position 138 536 : if (mDescendDepth == DESCEND_DEPTH) 139 : { 140 0 : mUnknownDescendDepth = 1; 141 0 : return; 142 : } 143 : 144 536 : size_t nodeIdx = 0; // assume root node 145 536 : if (mDescendDepth > 0) 146 : { 147 411 : nodeIdx = mPositions[mDescendDepth - 1]->node_index; 148 : } 149 : 150 536 : const Entry<CONTENT> * child = FindEntry(mTree, mTreeSize, nodeIdx, matcher); 151 : 152 536 : if (child == nullptr) 153 : { 154 43 : mUnknownDescendDepth = 1; 155 43 : return; 156 : } 157 : 158 493 : mPositions[mDescendDepth++] = child; 159 : } 160 : 161 : template <typename CONTENT, size_t DESCEND_DEPTH> 162 420 : void Position<CONTENT, DESCEND_DEPTH>::Exit() 163 : { 164 420 : if (mUnknownDescendDepth > 0) 165 : { 166 50 : mUnknownDescendDepth--; 167 50 : return; 168 : } 169 : 170 370 : if (mDescendDepth > 0) 171 : { 172 370 : mDescendDepth--; 173 : } 174 : } 175 : 176 : template <typename CONTENT, size_t DESCEND_DEPTH> 177 : chip::Span<const Entry<CONTENT> *> Position<CONTENT, DESCEND_DEPTH>::CurrentPath() 178 : { 179 : if (mUnknownDescendDepth > 0) 180 : { 181 : return chip::Span<const Entry<CONTENT> *>(); 182 : } 183 : 184 : // const chip::FlatTree::Entry<{anonymous}::NamedTag>* const* to 185 : // const chip::FlatTree::Entry<{anonymous}::NamedTag>** 186 : typename chip::Span<const Entry<CONTENT> *>::pointer p = mPositions; 187 : 188 : // return chip::Span<const Entry<CONTENT> *>(mPositions, mDescendDepth); 189 : return chip::Span<const Entry<CONTENT> *>(p, mDescendDepth); 190 : } 191 : 192 : } // namespace FlatTree 193 : } // namespace chip