Skip to content

Commit be37929

Browse files
amabluea-maurice
authored andcommitted
Added PruneForest class, which is forest of "prune trees" where a prune tree is
a location that can be pruned with a tree of descendants that must be excluded from the pruning. PiperOrigin-RevId: 280315131
1 parent e633749 commit be37929

File tree

2 files changed

+260
-0
lines changed

2 files changed

+260
-0
lines changed
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// Copyright 2019 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "database/src/desktop/persistence/prune_forest.h"
16+
17+
#include <set>
18+
#include <string>
19+
20+
#include "app/src/assert.h"
21+
#include "app/src/path.h"
22+
#include "database/src/desktop/core/tree.h"
23+
24+
namespace firebase {
25+
namespace database {
26+
namespace internal {
27+
28+
static const Tree<bool> kPruneTree(true); // NOLINT
29+
static const Tree<bool> kKeepTree(false); // NOLINT
30+
31+
static bool KeepPredicate(bool prune) { return !prune; }
32+
static bool PrunePredicate(bool prune) { return prune; }
33+
34+
PruneForestRef::PruneForestRef(PruneForest* prune_forest)
35+
: prune_forest_(prune_forest) {}
36+
37+
bool PruneForestRef::operator==(const PruneForestRef& other) const {
38+
return (prune_forest_ == other.prune_forest_) ||
39+
(prune_forest_ != nullptr && other.prune_forest_ != nullptr &&
40+
*prune_forest_ == *other.prune_forest_);
41+
}
42+
43+
bool PruneForestRef::PrunesAnything() const {
44+
return prune_forest_->ContainsMatchingValue(PrunePredicate);
45+
}
46+
47+
bool PruneForestRef::ShouldPruneUnkeptDescendants(const Path& path) const {
48+
const bool* should_prune = prune_forest_->LeafMostValue(path);
49+
return should_prune != nullptr && *should_prune;
50+
}
51+
52+
bool PruneForestRef::ShouldKeep(const Path& path) const {
53+
const bool* should_prune = prune_forest_->LeafMostValue(path);
54+
return should_prune != nullptr && !*should_prune;
55+
}
56+
57+
bool PruneForestRef::AffectsPath(const Path& path) const {
58+
return prune_forest_->RootMostValue(path) != nullptr ||
59+
(prune_forest_->GetChild(path) &&
60+
!prune_forest_->GetChild(path)->IsEmpty());
61+
}
62+
63+
PruneForestRef PruneForestRef::GetChild(const std::string& key) {
64+
return PruneForestRef(prune_forest_->GetOrMakeSubtree(Path(key)));
65+
}
66+
67+
const PruneForestRef PruneForestRef::GetChild(const std::string& key) const {
68+
return PruneForestRef(prune_forest_->GetOrMakeSubtree(Path(key)));
69+
}
70+
71+
PruneForestRef PruneForestRef::GetChild(const Path& path) {
72+
return PruneForestRef(prune_forest_->GetOrMakeSubtree(path));
73+
}
74+
75+
const PruneForestRef PruneForestRef::GetChild(const Path& path) const {
76+
return PruneForestRef(prune_forest_->GetOrMakeSubtree(path));
77+
}
78+
79+
void PruneForestRef::Prune(const Path& path) {
80+
FIREBASE_DEV_ASSERT_MESSAGE(
81+
prune_forest_->RootMostValueMatching(path, KeepPredicate) == nullptr,
82+
"Can't prune path that was kept previously!")
83+
if (prune_forest_->RootMostValueMatching(path, PrunePredicate) != nullptr) {
84+
// This path will already be pruned
85+
} else {
86+
*prune_forest_->GetOrMakeSubtree(path) = kPruneTree;
87+
}
88+
}
89+
90+
void PruneForestRef::Keep(const Path& path) {
91+
if (prune_forest_->RootMostValueMatching(path, KeepPredicate) != nullptr) {
92+
// This path will already be kept
93+
} else {
94+
*prune_forest_->GetOrMakeSubtree(path) = kKeepTree;
95+
}
96+
}
97+
98+
void PruneForestRef::KeepAll(const Path& path,
99+
const std::set<std::string>& children) {
100+
if (prune_forest_->RootMostValueMatching(path, KeepPredicate) != nullptr) {
101+
// This path will already be kept.
102+
} else {
103+
DoAll(path, children, kKeepTree);
104+
}
105+
}
106+
107+
void PruneForestRef::PruneAll(const Path& path,
108+
const std::set<std::string>& children) {
109+
FIREBASE_DEV_ASSERT_MESSAGE(
110+
prune_forest_->RootMostValueMatching(path, KeepPredicate) == nullptr,
111+
"Can't prune path that was kept previously!");
112+
113+
if (prune_forest_->RootMostValueMatching(path, PrunePredicate) != nullptr) {
114+
// This path will already be pruned.
115+
} else {
116+
DoAll(path, children, kPruneTree);
117+
}
118+
}
119+
120+
void PruneForestRef::DoAll(const Path& path,
121+
const std::set<std::string>& children,
122+
const Tree<bool>& keep_or_prune_tree) {
123+
Tree<bool>* subtree = prune_forest_->GetChild(path);
124+
for (const std::string& directory : children) {
125+
*subtree->GetOrMakeSubtree(Path(directory)) = keep_or_prune_tree;
126+
}
127+
}
128+
129+
} // namespace internal
130+
} // namespace database
131+
} // namespace firebase
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// Copyright 2019 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef FIREBASE_DATABASE_CLIENT_CPP_SRC_DESKTOP_PERSISTENCE_PRUNE_FOREST_H_
16+
#define FIREBASE_DATABASE_CLIENT_CPP_SRC_DESKTOP_PERSISTENCE_PRUNE_FOREST_H_
17+
18+
#include <set>
19+
#include <string>
20+
21+
#include "app/src/path.h"
22+
#include "database/src/desktop/core/tree.h"
23+
24+
namespace firebase {
25+
namespace database {
26+
namespace internal {
27+
28+
// Forest of "prune trees" where a prune tree is a location that can be pruned
29+
// with a tree of descendants that must be excluded from the pruning.
30+
//
31+
// Internally we store this as a single tree of bools with the following
32+
// characteristics:
33+
//
34+
// * 'true' indicates a location that can be pruned, possibly
35+
// with some excluded descendants.
36+
// * 'false' indicates a location that we should keep (i.e. exclude from
37+
// pruning).
38+
// * 'true' (prune) cannot be a descendant of 'false' (keep). This will
39+
// trigger an exception.
40+
// * 'true' cannot be a descendant of 'true' (we'll just keep the more shallow
41+
// 'true').
42+
// * 'false' cannot be a descendant of 'false' (we'll just keep the more
43+
// shallow 'false').
44+
//
45+
typedef Tree<bool> PruneForest;
46+
47+
// A PruneTreeRef is a way to operate on a PruneTree, treating any node as the
48+
// root. It provides fucntions to set or keep various locations, as well as
49+
// GetChild allow you to drill into the children of a location in the tree.
50+
// PruneForestRef is a lightweight object that does not take ownership of the
51+
// PruneForest passed to it.
52+
class PruneForestRef {
53+
public:
54+
explicit PruneForestRef(PruneForest* prune_forest);
55+
56+
// Check if this PruneForestRef is equal to another PruneForestRef. This is
57+
// mostly used for testing.
58+
bool operator==(const PruneForestRef& other) const;
59+
60+
// Check if this PruneForestRef is not equal to another PruneForestRef. This
61+
// is mostly used for testing.
62+
bool operator!=(const PruneForestRef& other) const {
63+
return !(*this == other);
64+
}
65+
66+
// Returns true if this PruneForestRef prunes anything.
67+
bool PrunesAnything() const;
68+
69+
// Indicates that path is marked for pruning, so anything below it that didn't
70+
// have keep() called on it should be pruned.
71+
//
72+
// @param path The path in question
73+
// @return True if we should prune descendants that didn't have keep() called
74+
// on them.
75+
bool ShouldPruneUnkeptDescendants(const Path& path) const;
76+
77+
// Returns true if the given path should be kept.
78+
bool ShouldKeep(const Path& path) const;
79+
80+
// Returns true if the given path should be pruned.
81+
bool AffectsPath(const Path& path) const;
82+
83+
// Get the child of this tree at the given key.
84+
PruneForestRef GetChild(const std::string& key);
85+
const PruneForestRef GetChild(const std::string& key) const;
86+
87+
// Get the child of this tree at the given path.
88+
PruneForestRef GetChild(const Path& path);
89+
const PruneForestRef GetChild(const Path& path) const;
90+
91+
// Mark that the value at the given path should be pruned.
92+
void Prune(const Path& path);
93+
94+
// Mark that the value at the given path should be kept.
95+
void Keep(const Path& path);
96+
97+
// Mark that the given child values at the given path should be kept.
98+
void KeepAll(const Path& path, const std::set<std::string>& children);
99+
100+
// Mark that the given child values at the given path should be pruned.
101+
void PruneAll(const Path& path, const std::set<std::string>& children);
102+
103+
// Accumulate a result given a visitor function to be applied to all nodes
104+
// that are marked as being kept.
105+
template <typename T, typename Func>
106+
T FoldKeptNodes(const T& start_value, const Func& visitor) const {
107+
return prune_forest_->Fold(
108+
start_value,
109+
[visitor](const Path& relative_path, bool prune, const T& accum) -> T {
110+
if (!prune) {
111+
return visitor(relative_path, nullptr, accum);
112+
} else {
113+
return accum;
114+
}
115+
});
116+
}
117+
118+
private:
119+
void DoAll(const Path& path, const std::set<std::string>& children,
120+
const Tree<bool>& keep_or_prune_tree);
121+
122+
PruneForest* prune_forest_;
123+
};
124+
125+
} // namespace internal
126+
} // namespace database
127+
} // namespace firebase
128+
129+
#endif // FIREBASE_DATABASE_CLIENT_CPP_SRC_DESKTOP_PERSISTENCE_PRUNE_FOREST_H_

0 commit comments

Comments
 (0)