Copy Source | Copy HTML template < class SState , class SEvent , class SFunctor = SEmptyFunctor< SState , SEvent >, class SUnknownEventStrategy = SThrowStrategy< SEvent > > class SFiniteStateMachine { public : typedef SState StateType ; typedef SEvent EventType ; private : StateType _CurrentState; // Current machine state std::vector< StateType > _States; // A list of the registered states std::vector< EventType > _Events; // A list of the registered events std::vector< std::vector< StateType > > _Transitions; // A table of transitions between states SFunctor _Functor; // Transition function SUnknownEventStrategy _UnknownEventStrategy; // Unknown event strategy std::deque< EventType > _EventsQueue; // Internal events queue to support events // that were generated in the transition functions bool _InProcess; // To be sure that we are in the process of events int _CurrentStateIndex; // Index of column in a transition table (0 - based) StateType _InitialState; // Start machine state // ......
Copy Source | Copy HTML template < class SState , class SEvent , class SFunctor = SEmptyFunctor< SState , SEvent >, class SUnknownEventStrategy = SThrowStrategy< SEvent > > class SFiniteStateMachine { public : typedef SState StateType ; typedef SEvent EventType ; private : StateType _CurrentState; // Current machine state std::vector< StateType > _States; // A list of the registered states std::vector< EventType > _Events; // A list of the registered events std::vector< std::vector< StateType > > _Transitions; // A table of transitions between states SFunctor _Functor; // Transition function SUnknownEventStrategy _UnknownEventStrategy; // Unknown event strategy std::deque< EventType > _EventsQueue; // Internal events queue to support events // that were generated in the transition functions bool _InProcess; // To be sure that we are in the process of events int _CurrentStateIndex; // Index of column in a transition table (0 - based) StateType _InitialState; // Start machine state // ......
Copy Source | Copy HTML template < class SState , class SEvent , class SFunctor = SEmptyFunctor< SState , SEvent >, class SUnknownEventStrategy = SThrowStrategy< SEvent > > class SFiniteStateMachine { public : typedef SState StateType ; typedef SEvent EventType ; private : StateType _CurrentState; // Current machine state std::vector< StateType > _States; // A list of the registered states std::vector< EventType > _Events; // A list of the registered events std::vector< std::vector< StateType > > _Transitions; // A table of transitions between states SFunctor _Functor; // Transition function SUnknownEventStrategy _UnknownEventStrategy; // Unknown event strategy std::deque< EventType > _EventsQueue; // Internal events queue to support events // that were generated in the transition functions bool _InProcess; // To be sure that we are in the process of events int _CurrentStateIndex; // Index of column in a transition table (0 - based) StateType _InitialState; // Start machine state // ......
Copy Source | Copy HTML template < class SState , class SEvent , class SFunctor = SEmptyFunctor< SState , SEvent >, class SUnknownEventStrategy = SThrowStrategy< SEvent > > class SFiniteStateMachine { public : typedef SState StateType ; typedef SEvent EventType ; private : StateType _CurrentState; // Current machine state std::vector< StateType > _States; // A list of the registered states std::vector< EventType > _Events; // A list of the registered events std::vector< std::vector< StateType > > _Transitions; // A table of transitions between states SFunctor _Functor; // Transition function SUnknownEventStrategy _UnknownEventStrategy; // Unknown event strategy std::deque< EventType > _EventsQueue; // Internal events queue to support events // that were generated in the transition functions bool _InProcess; // To be sure that we are in the process of events int _CurrentStateIndex; // Index of column in a transition table (0 - based) StateType _InitialState; // Start machine state // ......
Copy Source | Copy HTML template < class SState , class SEvent , class SFunctor = SEmptyFunctor< SState , SEvent >, class SUnknownEventStrategy = SThrowStrategy< SEvent > > class SFiniteStateMachine { public : typedef SState StateType ; typedef SEvent EventType ; private : StateType _CurrentState; // Current machine state std::vector< StateType > _States; // A list of the registered states std::vector< EventType > _Events; // A list of the registered events std::vector< std::vector< StateType > > _Transitions; // A table of transitions between states SFunctor _Functor; // Transition function SUnknownEventStrategy _UnknownEventStrategy; // Unknown event strategy std::deque< EventType > _EventsQueue; // Internal events queue to support events // that were generated in the transition functions bool _InProcess; // To be sure that we are in the process of events int _CurrentStateIndex; // Index of column in a transition table (0 - based) StateType _InitialState; // Start machine state // ......
Copy Source | Copy HTML template < class SState , class SEvent , class SFunctor = SEmptyFunctor< SState , SEvent >, class SUnknownEventStrategy = SThrowStrategy< SEvent > > class SFiniteStateMachine { public : typedef SState StateType ; typedef SEvent EventType ; private : StateType _CurrentState; // Current machine state std::vector< StateType > _States; // A list of the registered states std::vector< EventType > _Events; // A list of the registered events std::vector< std::vector< StateType > > _Transitions; // A table of transitions between states SFunctor _Functor; // Transition function SUnknownEventStrategy _UnknownEventStrategy; // Unknown event strategy std::deque< EventType > _EventsQueue; // Internal events queue to support events // that were generated in the transition functions bool _InProcess; // To be sure that we are in the process of events int _CurrentStateIndex; // Index of column in a transition table (0 - based) StateType _InitialState; // Start machine state // ......
Copy Source | Copy HTML template < class SState , class SEvent , class SFunctor = SEmptyFunctor< SState , SEvent >, class SUnknownEventStrategy = SThrowStrategy< SEvent > > class SFiniteStateMachine { public : typedef SState StateType ; typedef SEvent EventType ; private : StateType _CurrentState; // Current machine state std::vector< StateType > _States; // A list of the registered states std::vector< EventType > _Events; // A list of the registered events std::vector< std::vector< StateType > > _Transitions; // A table of transitions between states SFunctor _Functor; // Transition function SUnknownEventStrategy _UnknownEventStrategy; // Unknown event strategy std::deque< EventType > _EventsQueue; // Internal events queue to support events // that were generated in the transition functions bool _InProcess; // To be sure that we are in the process of events int _CurrentStateIndex; // Index of column in a transition table (0 - based) StateType _InitialState; // Start machine state // ......
Copy Source | Copy HTML template < class SState , class SEvent , class SFunctor = SEmptyFunctor< SState , SEvent >, class SUnknownEventStrategy = SThrowStrategy< SEvent > > class SFiniteStateMachine { public : typedef SState StateType ; typedef SEvent EventType ; private : StateType _CurrentState; // Current machine state std::vector< StateType > _States; // A list of the registered states std::vector< EventType > _Events; // A list of the registered events std::vector< std::vector< StateType > > _Transitions; // A table of transitions between states SFunctor _Functor; // Transition function SUnknownEventStrategy _UnknownEventStrategy; // Unknown event strategy std::deque< EventType > _EventsQueue; // Internal events queue to support events // that were generated in the transition functions bool _InProcess; // To be sure that we are in the process of events int _CurrentStateIndex; // Index of column in a transition table (0 - based) StateType _InitialState; // Start machine state // ......
Copy Source | Copy HTML template < class SState , class SEvent , class SFunctor = SEmptyFunctor< SState , SEvent >, class SUnknownEventStrategy = SThrowStrategy< SEvent > > class SFiniteStateMachine { public : typedef SState StateType ; typedef SEvent EventType ; private : StateType _CurrentState; // Current machine state std::vector< StateType > _States; // A list of the registered states std::vector< EventType > _Events; // A list of the registered events std::vector< std::vector< StateType > > _Transitions; // A table of transitions between states SFunctor _Functor; // Transition function SUnknownEventStrategy _UnknownEventStrategy; // Unknown event strategy std::deque< EventType > _EventsQueue; // Internal events queue to support events // that were generated in the transition functions bool _InProcess; // To be sure that we are in the process of events int _CurrentStateIndex; // Index of column in a transition table (0 - based) StateType _InitialState; // Start machine state // ......
Copy Source | Copy HTML template < class SState , class SEvent , class SFunctor = SEmptyFunctor< SState , SEvent >, class SUnknownEventStrategy = SThrowStrategy< SEvent > > class SFiniteStateMachine { public : typedef SState StateType ; typedef SEvent EventType ; private : StateType _CurrentState; // Current machine state std::vector< StateType > _States; // A list of the registered states std::vector< EventType > _Events; // A list of the registered events std::vector< std::vector< StateType > > _Transitions; // A table of transitions between states SFunctor _Functor; // Transition function SUnknownEventStrategy _UnknownEventStrategy; // Unknown event strategy std::deque< EventType > _EventsQueue; // Internal events queue to support events // that were generated in the transition functions bool _InProcess; // To be sure that we are in the process of events int _CurrentStateIndex; // Index of column in a transition table (0 - based) StateType _InitialState; // Start machine state // ......
Copy Source | Copy HTML template < class SState , class SEvent , class SFunctor = SEmptyFunctor< SState , SEvent >, class SUnknownEventStrategy = SThrowStrategy< SEvent > > class SFiniteStateMachine { public : typedef SState StateType ; typedef SEvent EventType ; private : StateType _CurrentState; // Current machine state std::vector< StateType > _States; // A list of the registered states std::vector< EventType > _Events; // A list of the registered events std::vector< std::vector< StateType > > _Transitions; // A table of transitions between states SFunctor _Functor; // Transition function SUnknownEventStrategy _UnknownEventStrategy; // Unknown event strategy std::deque< EventType > _EventsQueue; // Internal events queue to support events // that were generated in the transition functions bool _InProcess; // To be sure that we are in the process of events int _CurrentStateIndex; // Index of column in a transition table (0 - based) StateType _InitialState; // Start machine state // ......
Copy Source | Copy HTML template < class SState , class SEvent , class SFunctor = SEmptyFunctor< SState , SEvent >, class SUnknownEventStrategy = SThrowStrategy< SEvent > > class SFiniteStateMachine { public : typedef SState StateType ; typedef SEvent EventType ; private : StateType _CurrentState; // Current machine state std::vector< StateType > _States; // A list of the registered states std::vector< EventType > _Events; // A list of the registered events std::vector< std::vector< StateType > > _Transitions; // A table of transitions between states SFunctor _Functor; // Transition function SUnknownEventStrategy _UnknownEventStrategy; // Unknown event strategy std::deque< EventType > _EventsQueue; // Internal events queue to support events // that were generated in the transition functions bool _InProcess; // To be sure that we are in the process of events int _CurrentStateIndex; // Index of column in a transition table (0 - based) StateType _InitialState; // Start machine state // ......
Copy Source | Copy HTML template < class SState , class SEvent , class SFunctor = SEmptyFunctor< SState , SEvent >, class SUnknownEventStrategy = SThrowStrategy< SEvent > > class SFiniteStateMachine { public : typedef SState StateType ; typedef SEvent EventType ; private : StateType _CurrentState; // Current machine state std::vector< StateType > _States; // A list of the registered states std::vector< EventType > _Events; // A list of the registered events std::vector< std::vector< StateType > > _Transitions; // A table of transitions between states SFunctor _Functor; // Transition function SUnknownEventStrategy _UnknownEventStrategy; // Unknown event strategy std::deque< EventType > _EventsQueue; // Internal events queue to support events // that were generated in the transition functions bool _InProcess; // To be sure that we are in the process of events int _CurrentStateIndex; // Index of column in a transition table (0 - based) StateType _InitialState; // Start machine state // ......
Copy Source | Copy HTML template < class SState , class SEvent , class SFunctor = SEmptyFunctor< SState , SEvent >, class SUnknownEventStrategy = SThrowStrategy< SEvent > > class SFiniteStateMachine { public : typedef SState StateType ; typedef SEvent EventType ; private : StateType _CurrentState; // Current machine state std::vector< StateType > _States; // A list of the registered states std::vector< EventType > _Events; // A list of the registered events std::vector< std::vector< StateType > > _Transitions; // A table of transitions between states SFunctor _Functor; // Transition function SUnknownEventStrategy _UnknownEventStrategy; // Unknown event strategy std::deque< EventType > _EventsQueue; // Internal events queue to support events // that were generated in the transition functions bool _InProcess; // To be sure that we are in the process of events int _CurrentStateIndex; // Index of column in a transition table (0 - based) StateType _InitialState; // Start machine state // ......
Copy Source | Copy HTML template < class SState , class SEvent , class SFunctor = SEmptyFunctor< SState , SEvent >, class SUnknownEventStrategy = SThrowStrategy< SEvent > > class SFiniteStateMachine { public : typedef SState StateType ; typedef SEvent EventType ; private : StateType _CurrentState; // Current machine state std::vector< StateType > _States; // A list of the registered states std::vector< EventType > _Events; // A list of the registered events std::vector< std::vector< StateType > > _Transitions; // A table of transitions between states SFunctor _Functor; // Transition function SUnknownEventStrategy _UnknownEventStrategy; // Unknown event strategy std::deque< EventType > _EventsQueue; // Internal events queue to support events // that were generated in the transition functions bool _InProcess; // To be sure that we are in the process of events int _CurrentStateIndex; // Index of column in a transition table (0 - based) StateType _InitialState; // Start machine state // ......
Copy Source | Copy HTML template < class SState , class SEvent , class SFunctor = SEmptyFunctor< SState , SEvent >, class SUnknownEventStrategy = SThrowStrategy< SEvent > > class SFiniteStateMachine { public : typedef SState StateType ; typedef SEvent EventType ; private : StateType _CurrentState; // Current machine state std::vector< StateType > _States; // A list of the registered states std::vector< EventType > _Events; // A list of the registered events std::vector< std::vector< StateType > > _Transitions; // A table of transitions between states SFunctor _Functor; // Transition function SUnknownEventStrategy _UnknownEventStrategy; // Unknown event strategy std::deque< EventType > _EventsQueue; // Internal events queue to support events // that were generated in the transition functions bool _InProcess; // To be sure that we are in the process of events int _CurrentStateIndex; // Index of column in a transition table (0 - based) StateType _InitialState; // Start machine state // ......
Copy Source | Copy HTML template < class SState , class SEvent , class SFunctor = SEmptyFunctor< SState , SEvent >, class SUnknownEventStrategy = SThrowStrategy< SEvent > > class SFiniteStateMachine { public : typedef SState StateType ; typedef SEvent EventType ; private : StateType _CurrentState; // Current machine state std::vector< StateType > _States; // A list of the registered states std::vector< EventType > _Events; // A list of the registered events std::vector< std::vector< StateType > > _Transitions; // A table of transitions between states SFunctor _Functor; // Transition function SUnknownEventStrategy _UnknownEventStrategy; // Unknown event strategy std::deque< EventType > _EventsQueue; // Internal events queue to support events // that were generated in the transition functions bool _InProcess; // To be sure that we are in the process of events int _CurrentStateIndex; // Index of column in a transition table (0 - based) StateType _InitialState; // Start machine state // ......
Copy Source | Copy HTML template < class SState , class SEvent , class SFunctor = SEmptyFunctor< SState , SEvent >, class SUnknownEventStrategy = SThrowStrategy< SEvent > > class SFiniteStateMachine { public : typedef SState StateType ; typedef SEvent EventType ; private : StateType _CurrentState; // Current machine state std::vector< StateType > _States; // A list of the registered states std::vector< EventType > _Events; // A list of the registered events std::vector< std::vector< StateType > > _Transitions; // A table of transitions between states SFunctor _Functor; // Transition function SUnknownEventStrategy _UnknownEventStrategy; // Unknown event strategy std::deque< EventType > _EventsQueue; // Internal events queue to support events // that were generated in the transition functions bool _InProcess; // To be sure that we are in the process of events int _CurrentStateIndex; // Index of column in a transition table (0 - based) StateType _InitialState; // Start machine state // ......
Copy Source | Copy HTML template < class SState , class SEvent , class SFunctor = SEmptyFunctor< SState , SEvent >, class SUnknownEventStrategy = SThrowStrategy< SEvent > > class SFiniteStateMachine { public : typedef SState StateType ; typedef SEvent EventType ; private : StateType _CurrentState; // Current machine state std::vector< StateType > _States; // A list of the registered states std::vector< EventType > _Events; // A list of the registered events std::vector< std::vector< StateType > > _Transitions; // A table of transitions between states SFunctor _Functor; // Transition function SUnknownEventStrategy _UnknownEventStrategy; // Unknown event strategy std::deque< EventType > _EventsQueue; // Internal events queue to support events // that were generated in the transition functions bool _InProcess; // To be sure that we are in the process of events int _CurrentStateIndex; // Index of column in a transition table (0 - based) StateType _InitialState; // Start machine state // ......
Copy Source | Copy HTML template < class SState , class SEvent , class SFunctor = SEmptyFunctor< SState , SEvent >, class SUnknownEventStrategy = SThrowStrategy< SEvent > > class SFiniteStateMachine { public : typedef SState StateType ; typedef SEvent EventType ; private : StateType _CurrentState; // Current machine state std::vector< StateType > _States; // A list of the registered states std::vector< EventType > _Events; // A list of the registered events std::vector< std::vector< StateType > > _Transitions; // A table of transitions between states SFunctor _Functor; // Transition function SUnknownEventStrategy _UnknownEventStrategy; // Unknown event strategy std::deque< EventType > _EventsQueue; // Internal events queue to support events // that were generated in the transition functions bool _InProcess; // To be sure that we are in the process of events int _CurrentStateIndex; // Index of column in a transition table (0 - based) StateType _InitialState; // Start machine state // ......
Copy Source | Copy HTML
- inline void ProcessEvent ( const EventType & tEvent)
- {
- int EventIndex (GetEventIndex (tEvent));
- if (EventIndex == - 1 ) return ;
- StateType OldState (_CurrentState); // Save the old state.
- _CurrentState = (_Transitions [EventIndex]) [_ CurrentStateIndex]; // Define a new state.
- _CurrentStateIndex = GetStateIndex (_CurrentState);
- _Functor (OldState, tEvent, _CurrentState); // Perform the action associated with the new state.
- }
Copy Source | Copy HTML
- class TState ;
- typedef TCharsSetEvent TEvent ;
- typedef TCFDivisionTreeItem < TCFLevelDataType , TUnitType > TCFCustomUnitDivisionTreeItem;
- typedef TCFTreeLevel < TCFLevelDataType , TUnitType > TCFCustomUnitTreeLevel;
- typedef void (* TFuncPtr) (TCFCustomUnitDivisionTreeItem *, const TEvent &);
- typedef TStateMachineDescriptor < TState , TEvent > TParSenWordDescriptor;
- / * Class of used state. * /
- class TState : public TFunctionalState < TEvent , TTextString >
- {
- private :
- TFuncPtr _Function;
- TCFCustomUnitDivisionTreeItem * _TargetTree;
- public :
- void OnEnter ( const TState & tStateFrom, const TEvent & tEvent) {
- _Function (_TargetTree, tEvent);
- };
- TState ( const TTextString & tName, TFuncPtr tFunc, TCFCustomUnitDivisionTreeItem * tTargetTree)
- : TFunctionalState < TEvent , TTextString > (tName, sat_None), _Function (tFunc), _TargetTree (tTargetTree) {};
- };
Copy Source | Copy HTML
- void FEmpty (TCFCustomUnitDivisionTreeItem * tItem, const TEvent & tEvent)
- {
- return ;
- };
- void FOpenParagraph (TCFCustomUnitDivisionTreeItem * tTree, const TEvent & tEvent)
- {
- TCFCustomUnitDivisionTreeItem NewItem ( 0 , TRangeItem (tEvent.Iterator (), tEvent.Iterator ()));
- tTree-> AddItem ( 0 , NewItem);
- };
- void FOpenSentence (TCFCustomUnitDivisionTreeItem * tTree, const TEvent & tEvent)
- {
- TCFCustomUnitDivisionTreeItem NewItem ( 1 , TRangeItem (tEvent.Iterator (), tEvent.Iterator ()));
- tTree-> AddItem ( 1 , NewItem);
- };
- // .......
- void FCloseAll (TCFCustomUnitDivisionTreeItem * tTree, const TEvent & tEvent)
- {
- FCloseWord (tTree, tEvent);
- FCloseSentence (tTree, tEvent);
- FCloseParagraph (tTree, tEvent);
- };
- // ... and some more functions ...
Copy Source | Copy HTML
- TParSenWordDescriptor Descriptor (TCFCustomUnitDivisionTreeItem * tTree)
- {
- // The initial state is q0, the function is FEmpty, the transition table is called "Paragraphs, sentences, words".
- TParSenWordDescriptor D (TState ( "q0" , FEmpty, tTree), "Paragraphs, Sentences, Words" );
- // Set the state, and each has its own name: q0, q1, ... q14.
- // Each state is passed a function (FEmpty, FOpenAll ...), which should be called if the state occurs.
- // Also, we pass a pointer to the tree that we are building - tTree.
- D << TState ( "q0" , FEmpty, tTree)
- << TState ( "q1" , FOpenAll, tTree)
- << TState ( "q2" , FOpenSentenceOpenWord, tTree)
- << TState ( "q3" , FOpenWord, tTree)
- << TState ( "q4" , FEmpty, tTree)
- << TState ( " q5 " , FCloseWord, tTree)
- << TState ( "q6" , FCloseSentence, tTree)
- << TState ( "q7" , FCloseSentenceOpenSentenceOpenWord, tTree)
- << TState ( "q8" , FEmpty, tTree)
- << TState ( "q9" , FEmpty, tTree)
- << TState ( "q10" , FCloseWord, tTree)
- << TState ( "q11" , FCloseAll, tTree)
- << TState ( "q12" , FCloseSentenceCloseParagraph, tTree)
- << TState ( "q13" , FCloseParagraph, tTree)
- << TState ( "q14" , FEmpty, tTree);
- // Set events, each one has its own name ("WinParagraph", "SentenceEnd" ...).
- // cWinParagraph, cSentenceEnd are the character sets described in UConstants.h.
- D << TEvent ( "WinParagraph" , cWinParagraph)
- << "q0" << "q11" << "q11" << "q11" << "q11" << "q12" << "q12" << "q11" << "q12" << "q13" << "q12" << "q0" << "q0" << "q0" << "q12" ;
- D << TEvent ( "SentenceEnd" , cSentenceEnd)
- << "q0" << "q10" << "q10" << "q10" << "q10" << "q14" << "q14" << "q10" << "q14" << "q9" << "q14" << "q0" << "q0" << "q0" << "q14" ;
- D << TEvent ( "Letters" , cLetters + cDigits)
- << "q1" << "q4" << "q4" << "q4" << "q4" << "q3" << "q2" << "q4" << "q3" << "q2" << "q7" << "q1" << "q1" << "q1" << "q7" ;
- D << TEvent ( "Space" , cSpace + cTab)
- << "q0" << " q5" << "q5" << "q5" << "q5" << "q8" << "q9" << "q5" << "q8" << "q9" << "q6" << "q0" << "q0" << "q0" << "q6" ;
- D << TEvent ( "PunctMarks and other symbols" , TCharsSet (cPrintable) >> cWinParagraph >> cSentenceEnd >> cLetters >> cDigits >> cSpace >> cTab)
- << "q0" << " q5" << "q5" << "q5" << "q5" << "q8" << "q9" << "q5" << "q8" << "q9" << "q14" << "q0" << "q0" << "q0" << "q14" ;
- D << TEvent ( "EndSign" , TCharsSet (CTextStringEndSign))
- << "q0" << "q11" << "q11" << "q11" << "q11" << "q12" << "q13" << "q11" << "q12" << "q13" << "q12" << "q0" << "q0" << "q0" << "q12" ;
- return D;
- };
Copy Source | Copy HTML
- template < class EventType , class FiniteStateMachineType , class StateMachineDescriptorType , class SourceType >
- class TFiniteStateMachineManager
- {
- private :
- StateMachineDescriptorType _Descriptor;
- SourceType _Source;
- TUInt _Begin;
- TUInt _End;
- TUInt _Iterator;
- public :
- // <......>
- TFiniteStateMachineManager ( const StateMachineDescriptorType & tDescriptor, const SourceType & tSource)
- :
- _Descriptor (tDescriptor),
- _Source (tSource),
- _ReportStep ( 0 ),
- _ProgressFunc (NULL)
- {};
- // <......>
Copy Source | Copy HTML
- TFiniteStateMachineManager & Process ()
- {
- _Begin = _Source.Begin ();
- _End = _Source.End ();
- FiniteStateMachineType _Machine (_Descriptor.StartState (), _Descriptor.Proxy ());
- for (_Iterator = _Begin; _Iterator <= _ End; _Iterator ++)
- {
- _Machine << EventType (_Source [_Iterator], _Iterator);
- };
- _Machine << EventType (_Source.EndSign (), _End + 1 );
- return * this ;
- };
Copy Source | Copy HTML
- // ......
- SourceType * _Source;
- // ......
- SourceType :: Iterator _Iterator = _Source-> Begin ();
- for (; _Iterator! = _Source-> End (); ++ _ Iterator)
- {
- _Machine << EventType (* _ Iterator);
- };
Source: https://habr.com/ru/post/114187/
All Articles