xref: /aosp_15_r20/external/cronet/base/state_transitions.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker // Copyright 2020 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker 
5*6777b538SAndroid Build Coastguard Worker #ifndef BASE_STATE_TRANSITIONS_H_
6*6777b538SAndroid Build Coastguard Worker #define BASE_STATE_TRANSITIONS_H_
7*6777b538SAndroid Build Coastguard Worker 
8*6777b538SAndroid Build Coastguard Worker #include <vector>
9*6777b538SAndroid Build Coastguard Worker 
10*6777b538SAndroid Build Coastguard Worker #include "base/check_op.h"
11*6777b538SAndroid Build Coastguard Worker #include "base/containers/contains.h"
12*6777b538SAndroid Build Coastguard Worker #include "base/no_destructor.h"
13*6777b538SAndroid Build Coastguard Worker 
14*6777b538SAndroid Build Coastguard Worker namespace base {
15*6777b538SAndroid Build Coastguard Worker 
16*6777b538SAndroid Build Coastguard Worker // This class represents a set of state transitions where each state is a value
17*6777b538SAndroid Build Coastguard Worker // that supports copy, << and == (e.g. an enum element). It's intended to be
18*6777b538SAndroid Build Coastguard Worker // used in DCHECK-enabled builds to check that only valid transitions occur. Its
19*6777b538SAndroid Build Coastguard Worker // implementation favours convenience and simplicity over performance. To use it
20*6777b538SAndroid Build Coastguard Worker // follow this example:
21*6777b538SAndroid Build Coastguard Worker 
22*6777b538SAndroid Build Coastguard Worker // In foo.h
23*6777b538SAndroid Build Coastguard Worker // ---------
24*6777b538SAndroid Build Coastguard Worker // enum class State {
25*6777b538SAndroid Build Coastguard Worker //   kState1,
26*6777b538SAndroid Build Coastguard Worker //   kState2,
27*6777b538SAndroid Build Coastguard Worker //   kState3,
28*6777b538SAndroid Build Coastguard Worker // };
29*6777b538SAndroid Build Coastguard Worker //
30*6777b538SAndroid Build Coastguard Worker // // This may require exporting the symbol (e.g. CONTENT_EXPORT) if it will be
31*6777b538SAndroid Build Coastguard Worker // // used by any other components: one common way this can happen is if the
32*6777b538SAndroid Build Coastguard Worker // // enum is logged in tests (e.g. via gtest's EXPECT_* macros).
33*6777b538SAndroid Build Coastguard Worker // std::ostream& operator<<(std::ostream& o, const State& s);
34*6777b538SAndroid Build Coastguard Worker // ---------
35*6777b538SAndroid Build Coastguard Worker //
36*6777b538SAndroid Build Coastguard Worker // In foo.cc
37*6777b538SAndroid Build Coastguard Worker // ---------
38*6777b538SAndroid Build Coastguard Worker // #include "base/no_destructor.h"
39*6777b538SAndroid Build Coastguard Worker // #include "base/state_transitions.h"
40*6777b538SAndroid Build Coastguard Worker //
41*6777b538SAndroid Build Coastguard Worker // std::ostream& operator<<(std::ostream& o, const State& s) {
42*6777b538SAndroid Build Coastguard Worker //   return o << static_cast<int>(s);
43*6777b538SAndroid Build Coastguard Worker // }
44*6777b538SAndroid Build Coastguard Worker //
45*6777b538SAndroid Build Coastguard Worker // void DCheckStateTransition(State old_state, State new_state) {
46*6777b538SAndroid Build Coastguard Worker // #if DCHECK_IS_ON()
47*6777b538SAndroid Build Coastguard Worker //   static const base::NoDestructor<StateTransitions<State>> transitions(
48*6777b538SAndroid Build Coastguard Worker //       StateTransitions<State>({
49*6777b538SAndroid Build Coastguard Worker //           {kState1, {kState2, kState3}},
50*6777b538SAndroid Build Coastguard Worker //           {kState2, {kState3}},
51*6777b538SAndroid Build Coastguard Worker //           {kState3, {}},
52*6777b538SAndroid Build Coastguard Worker //       }));
53*6777b538SAndroid Build Coastguard Worker //   DCHECK_STATE_TRANSITION(transitions, old_state, new_state);
54*6777b538SAndroid Build Coastguard Worker // #endif  // DCHECK_IS_ON()
55*6777b538SAndroid Build Coastguard Worker // }
56*6777b538SAndroid Build Coastguard Worker // ---------
57*6777b538SAndroid Build Coastguard Worker 
58*6777b538SAndroid Build Coastguard Worker template <typename State>
59*6777b538SAndroid Build Coastguard Worker struct StateTransitions {
60*6777b538SAndroid Build Coastguard Worker  public:
61*6777b538SAndroid Build Coastguard Worker   // Represents a state and all of the states that are valid transitions from
62*6777b538SAndroid Build Coastguard Worker   // it.
63*6777b538SAndroid Build Coastguard Worker   struct StateTransition {
StateTransitionStateTransitions::StateTransition64*6777b538SAndroid Build Coastguard Worker     StateTransition(State source, std::vector<State> destinations)
65*6777b538SAndroid Build Coastguard Worker         : source(std::move(source)), destinations(std::move(destinations)) {}
66*6777b538SAndroid Build Coastguard Worker 
67*6777b538SAndroid Build Coastguard Worker     const State source;
68*6777b538SAndroid Build Coastguard Worker     const std::vector<State> destinations;
69*6777b538SAndroid Build Coastguard Worker   };
70*6777b538SAndroid Build Coastguard Worker 
StateTransitionsStateTransitions71*6777b538SAndroid Build Coastguard Worker   explicit StateTransitions(std::vector<StateTransition> state_transitions)
72*6777b538SAndroid Build Coastguard Worker       : state_transitions(std::move(state_transitions)) {}
73*6777b538SAndroid Build Coastguard Worker 
74*6777b538SAndroid Build Coastguard Worker   // Returns a list of states that are valid to transition to from |source|.
GetValidTransitionsStateTransitions75*6777b538SAndroid Build Coastguard Worker   const std::vector<State>& GetValidTransitions(const State& source) const {
76*6777b538SAndroid Build Coastguard Worker     for (const StateTransition& state_transition : state_transitions) {
77*6777b538SAndroid Build Coastguard Worker       if (state_transition.source == source)
78*6777b538SAndroid Build Coastguard Worker         return state_transition.destinations;
79*6777b538SAndroid Build Coastguard Worker     }
80*6777b538SAndroid Build Coastguard Worker     static const base::NoDestructor<std::vector<State>> no_transitions;
81*6777b538SAndroid Build Coastguard Worker     return *no_transitions;
82*6777b538SAndroid Build Coastguard Worker   }
83*6777b538SAndroid Build Coastguard Worker 
84*6777b538SAndroid Build Coastguard Worker   // Tests whether transitioning from |source| to |destination| is valid.
IsTransitionValidStateTransitions85*6777b538SAndroid Build Coastguard Worker   bool IsTransitionValid(const State& source, const State& destination) const {
86*6777b538SAndroid Build Coastguard Worker     return base::Contains(GetValidTransitions(source), destination);
87*6777b538SAndroid Build Coastguard Worker   }
88*6777b538SAndroid Build Coastguard Worker 
89*6777b538SAndroid Build Coastguard Worker   const std::vector<StateTransition> state_transitions;
90*6777b538SAndroid Build Coastguard Worker };
91*6777b538SAndroid Build Coastguard Worker 
92*6777b538SAndroid Build Coastguard Worker // DCHECK if transitioning from |old_state| to |new_state| is not valid
93*6777b538SAndroid Build Coastguard Worker // according to |transitions|.
94*6777b538SAndroid Build Coastguard Worker #define DCHECK_STATE_TRANSITION(transitions, old_state, new_state)   \
95*6777b538SAndroid Build Coastguard Worker   DCHECK((transitions)->IsTransitionValid((old_state), (new_state))) \
96*6777b538SAndroid Build Coastguard Worker       << "Invalid transition: " << old_state << " -> " << new_state
97*6777b538SAndroid Build Coastguard Worker 
98*6777b538SAndroid Build Coastguard Worker }  // namespace base
99*6777b538SAndroid Build Coastguard Worker 
100*6777b538SAndroid Build Coastguard Worker #endif  // BASE_STATE_TRANSITIONS_H_
101