181 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			181 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|   | // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
 | ||
|  | 
 | ||
|  | #pragma once
 | ||
|  | 
 | ||
|  | /*! \file rx-window.hpp
 | ||
|  | 
 | ||
|  |     \brief Return an observable that emits connected, non-overlapping windows, each containing at most count items from the source observable. | ||
|  |            If the skip parameter is set, return an observable that emits windows every skip items containing at most count items from the source observable. | ||
|  | 
 | ||
|  |     \param count  the maximum size of each window before it should be completed | ||
|  |     \param skip   how many items need to be skipped before starting a new window | ||
|  | 
 | ||
|  |     \return  Observable that emits connected, non-overlapping windows, each containing at most count items from the source observable. | ||
|  |              If the skip parameter is set, return an Observable that emits windows every skip items containing at most count items from the source observable. | ||
|  | 
 | ||
|  |     \sample | ||
|  |     \snippet window.cpp window count+skip sample | ||
|  |     \snippet output.txt window count+skip sample | ||
|  | 
 | ||
|  |     \sample | ||
|  |     \snippet window.cpp window count sample | ||
|  |     \snippet output.txt window count sample | ||
|  | */ | ||
|  | 
 | ||
|  | #if !defined(RXCPP_OPERATORS_RX_WINDOW_HPP)
 | ||
|  | #define RXCPP_OPERATORS_RX_WINDOW_HPP
 | ||
|  | 
 | ||
|  | #include "../rx-includes.hpp"
 | ||
|  | 
 | ||
|  | namespace rxcpp { | ||
|  | 
 | ||
|  | namespace operators { | ||
|  | 
 | ||
|  | namespace detail { | ||
|  | 
 | ||
|  | template<class... AN> | ||
|  | struct window_invalid_arguments {}; | ||
|  | 
 | ||
|  | template<class... AN> | ||
|  | struct window_invalid : public rxo::operator_base<window_invalid_arguments<AN...>> { | ||
|  |     using type = observable<window_invalid_arguments<AN...>, window_invalid<AN...>>; | ||
|  | }; | ||
|  | template<class... AN> | ||
|  | using window_invalid_t = typename window_invalid<AN...>::type; | ||
|  | 
 | ||
|  | template<class T> | ||
|  | struct window | ||
|  | { | ||
|  |     typedef rxu::decay_t<T> source_value_type; | ||
|  |     typedef observable<source_value_type> value_type; | ||
|  | 
 | ||
|  |     struct window_values | ||
|  |     { | ||
|  |         window_values(int c, int s) | ||
|  |             : count(c) | ||
|  |             , skip(s) | ||
|  |         { | ||
|  |         } | ||
|  |         int count; | ||
|  |         int skip; | ||
|  |     }; | ||
|  | 
 | ||
|  |     window_values initial; | ||
|  | 
 | ||
|  |     window(int count, int skip) | ||
|  |         : initial(count, skip) | ||
|  |     { | ||
|  |     } | ||
|  | 
 | ||
|  |     template<class Subscriber> | ||
|  |     struct window_observer : public window_values | ||
|  |     { | ||
|  |         typedef window_observer<Subscriber> this_type; | ||
|  |         typedef rxu::decay_t<T> value_type; | ||
|  |         typedef rxu::decay_t<Subscriber> dest_type; | ||
|  |         typedef observer<T, this_type> observer_type; | ||
|  |         dest_type dest; | ||
|  |         mutable int cursor; | ||
|  |         mutable std::deque<rxcpp::subjects::subject<T>> subj; | ||
|  | 
 | ||
|  |         window_observer(dest_type d, window_values v) | ||
|  |             : window_values(v) | ||
|  |             , dest(std::move(d)) | ||
|  |             , cursor(0) | ||
|  |         { | ||
|  |             subj.push_back(rxcpp::subjects::subject<T>()); | ||
|  |             dest.on_next(subj[0].get_observable().as_dynamic()); | ||
|  |         } | ||
|  |         void on_next(T v) const { | ||
|  |             for (auto s : subj) { | ||
|  |                 s.get_subscriber().on_next(v); | ||
|  |             } | ||
|  | 
 | ||
|  |             int c = cursor - this->count + 1; | ||
|  |             if (c >= 0 && c % this->skip == 0) { | ||
|  |                 subj[0].get_subscriber().on_completed(); | ||
|  |                 subj.pop_front(); | ||
|  |             } | ||
|  | 
 | ||
|  |             if (++cursor % this->skip == 0) { | ||
|  |                 subj.push_back(rxcpp::subjects::subject<T>()); | ||
|  |                 dest.on_next(subj[subj.size() - 1].get_observable().as_dynamic()); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         void on_error(rxu::error_ptr e) const { | ||
|  |             for (auto s : subj) { | ||
|  |                 s.get_subscriber().on_error(e); | ||
|  |             } | ||
|  |             dest.on_error(e); | ||
|  |         } | ||
|  | 
 | ||
|  |         void on_completed() const { | ||
|  |             for (auto s : subj) { | ||
|  |                 s.get_subscriber().on_completed(); | ||
|  |             } | ||
|  |             dest.on_completed(); | ||
|  |         } | ||
|  | 
 | ||
|  |         static subscriber<T, observer_type> make(dest_type d, window_values v) { | ||
|  |             auto cs = d.get_subscription(); | ||
|  |             return make_subscriber<T>(std::move(cs), observer_type(this_type(std::move(d), std::move(v)))); | ||
|  |         } | ||
|  |     }; | ||
|  | 
 | ||
|  |     template<class Subscriber> | ||
|  |     auto operator()(Subscriber dest) const | ||
|  |         -> decltype(window_observer<Subscriber>::make(std::move(dest), initial)) { | ||
|  |         return      window_observer<Subscriber>::make(std::move(dest), initial); | ||
|  |     } | ||
|  | }; | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | /*! @copydoc rx-window.hpp
 | ||
|  | */ | ||
|  | template<class... AN> | ||
|  | auto window(AN&&... an) | ||
|  |     ->      operator_factory<window_tag, AN...> { | ||
|  |      return operator_factory<window_tag, AN...>(std::make_tuple(std::forward<AN>(an)...)); | ||
|  | } | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | template<> | ||
|  | struct member_overload<window_tag> | ||
|  | { | ||
|  |     template<class Observable, | ||
|  |         class Enabled = rxu::enable_if_all_true_type_t< | ||
|  |             is_observable<Observable>>, | ||
|  |         class SourceValue = rxu::value_type_t<Observable>, | ||
|  |         class Window = rxo::detail::window<SourceValue>, | ||
|  |         class Value = rxu::value_type_t<Window>> | ||
|  |     static auto member(Observable&& o, int count, int skip) | ||
|  |         -> decltype(o.template lift<Value>(Window(count, skip))) { | ||
|  |         return      o.template lift<Value>(Window(count, skip)); | ||
|  |     } | ||
|  | 
 | ||
|  |      template<class Observable, | ||
|  |         class Enabled = rxu::enable_if_all_true_type_t< | ||
|  |             is_observable<Observable>>, | ||
|  |         class SourceValue = rxu::value_type_t<Observable>, | ||
|  |         class Window = rxo::detail::window<SourceValue>, | ||
|  |         class Value = rxu::value_type_t<Window>> | ||
|  |     static auto member(Observable&& o, int count) | ||
|  |         -> decltype(o.template lift<Value>(Window(count, count))) { | ||
|  |         return      o.template lift<Value>(Window(count, count)); | ||
|  |     } | ||
|  | 
 | ||
|  |     template<class... AN> | ||
|  |     static operators::detail::window_invalid_t<AN...> member(AN...) { | ||
|  |         std::terminate(); | ||
|  |         return {}; | ||
|  |         static_assert(sizeof...(AN) == 10000, "window takes (Count, optional Skip)"); | ||
|  |     } | ||
|  | }; | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | #endif
 |