summaryrefslogtreecommitdiff
path: root/chromium/content/common/state_transitions.h
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/content/common/state_transitions.h')
-rw-r--r--chromium/content/common/state_transitions.h100
1 files changed, 100 insertions, 0 deletions
diff --git a/chromium/content/common/state_transitions.h b/chromium/content/common/state_transitions.h
new file mode 100644
index 00000000000..31178988e26
--- /dev/null
+++ b/chromium/content/common/state_transitions.h
@@ -0,0 +1,100 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_COMMON_STATE_TRANSITIONS_H_
+#define CONTENT_COMMON_STATE_TRANSITIONS_H_
+
+#include <vector>
+
+#include "base/check_op.h"
+#include "base/no_destructor.h"
+#include "base/stl_util.h"
+
+namespace content {
+
+// This class represents a set of state transitions where each state is a value
+// that supports copy, << and == (e.g. an enum element). It's intended to be
+// used in DCHECK-enabled builds to check that only valid transitions occur. Its
+// implementation favours convenience and simplicity over performance. To use it
+// follow this example:
+
+// In foo.h
+// ---------
+// enum class State {
+// kState1,
+// kState2,
+// kState3,
+// };
+//
+// // This may require exporting the symbol (e.g. CONTENT_EXPORT) if it will be
+// // used by any other components: one common way this can happen is if the
+// // enum is logged in tests (e.g. via gtest's EXPECT_* macros).
+// std::ostream& operator<<(std::ostream& o, const State& s);
+// ---------
+//
+// In foo.cc
+// ---------
+// #include "base/no_destructor.h"
+// #include "content/common/state_transitions.h"
+//
+// std::ostream& operator<<(std::ostream& o, const State& s) {
+// return o << static_cast<int>(s);
+// }
+//
+// void DCheckStateTransition(State old_state, State new_state) {
+// #if DCHECK_IS_ON()
+// static const base::NoDestructor<StateTransitions<State>> transitions(
+// StateTransitions<State>({
+// {kState1, {kState2, kState3}},
+// {kState2, {kState3}},
+// {kState3, {}},
+// }));
+// DCHECK_STATE_TRANSITION(transitions, old_state, new_state);
+// #endif // DCHECK_IS_ON()
+// }
+// ---------
+
+template <typename State>
+struct StateTransitions {
+ public:
+ // Represents a state and all of the states that are valid transitions from
+ // it.
+ struct StateTransition {
+ StateTransition(State source, std::vector<State> destinations)
+ : source(std::move(source)), destinations(std::move(destinations)) {}
+
+ const State source;
+ const std::vector<State> destinations;
+ };
+
+ explicit StateTransitions(std::vector<StateTransition> state_transitions)
+ : state_transitions(std::move(state_transitions)) {}
+
+ // Returns a list of states that are valid to transition to from |source|.
+ const std::vector<State>& GetValidTransitions(const State& source) const {
+ for (const StateTransition& state_transition : state_transitions) {
+ if (state_transition.source == source)
+ return state_transition.destinations;
+ }
+ static const base::NoDestructor<std::vector<State>> no_transitions;
+ return *no_transitions;
+ }
+
+ // Tests whether transitioning from |source| to |destination| is valid.
+ bool IsTransitionValid(const State& source, const State& destination) const {
+ return base::Contains(GetValidTransitions(source), destination);
+ }
+
+ const std::vector<StateTransition> state_transitions;
+};
+
+// DCHECK if transitioning from |old_state| to |new_state| is not valid
+// according to |transitions|.
+#define DCHECK_STATE_TRANSITION(transitions, old_state, new_state) \
+ DCHECK((transitions)->IsTransitionValid((old_state), (new_state))) \
+ << "Invalid transition: " << old_state << " -> " << new_state
+
+} // namespace content
+
+#endif // CONTENT_COMMON_STATE_TRANSITIONS_H_