fboss/agent/state/NodeMapDelta.h (147 lines of code) (raw):

/* * Copyright (c) 2004-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * */ #pragma once #include <cstddef> #include <functional> #include <memory> #include <type_traits> #include <folly/functional/ApplyTuple.h> namespace facebook::fboss { template <typename NODE> class DeltaValue; template <typename MAP> class MapPointerTraits { public: using RawConstPointerType = const MAP*; // Embed const in MapPointerType for raw pointers. // Since we want to use both raw and unique_ptr, // we don't want to add const to the Map pointer type // in function declarations, where we will want to move // from unique_ptr (moving from const unique_ptr is not // permitted since its a modifying operation). using MapPointerType = const MAP*; static RawConstPointerType getRawPointer(MapPointerType map) { return map; } }; template <typename MAP> class MapUniquePointerTraits { public: using RawConstPointerType = const MAP*; // Non const MapPointer type, const unique_ptr is // severly restrictive when received as a function // argument, since it can't be moved from. using MapPointerType = std::unique_ptr<MAP>; static RawConstPointerType getRawPointer(const MapPointerType& map) { return map.get(); } }; /* * NodeMapDelta contains code for examining the differences between two NodeMap * objects. * * The main function of this class is the Iterator that it provides. This * allows caller to walk over the changed, added, and removed nodes. */ template < typename MAP, typename VALUE = DeltaValue<typename MAP::Node>, typename MAPPOINTERTRAITS = MapPointerTraits<MAP>> class NodeMapDelta { public: using MapPointerType = typename MAPPOINTERTRAITS::MapPointerType; using RawConstPointerType = typename MAPPOINTERTRAITS::RawConstPointerType; using MapType = MAP; using Node = typename MAP::Node; class Iterator; NodeMapDelta(MapPointerType&& oldMap, MapPointerType&& newMap) : old_(std::move(oldMap)), new_(std::move(newMap)) {} RawConstPointerType getOld() const { return MAPPOINTERTRAITS::getRawPointer(old_); } RawConstPointerType getNew() const { return MAPPOINTERTRAITS::getRawPointer(new_); } /* * Return an iterator pointing to the first change. */ Iterator begin() const; /* * Return an iterator pointing just past the last change. */ Iterator end() const; private: /* * NodeMapDelta is used by StateDelta. StateDelta holds a shared_ptr to * the old and new SwitchState objects, which in turn holds * shared_ptrs to NodeMaps, hence in the common case use raw pointers here * and don't bother with ownership. However there are cases where collections * are not easily mapped to a NodeMap structure, but we still want to box * them into a NodeMap while computing delta. In this case NodeMap is created * on the fly and we pass ownership to NodeMapDelta object via a unique_ptr. * See MapPointerTraits and MapUniquePointerTraits classes for details. */ MapPointerType old_; MapPointerType new_; }; template <typename NODE> class DeltaValue { public: using Node = NODE; DeltaValue(const std::shared_ptr<Node>& o, const std::shared_ptr<Node>& n) : old_(o), new_(n) {} void reset(const std::shared_ptr<Node>& o, const std::shared_ptr<Node>& n) { old_ = o; new_ = n; } const std::shared_ptr<Node>& getOld() const { return old_; } const std::shared_ptr<Node>& getNew() const { return new_; } private: // TODO: We should probably change this to store raw pointers. // Storing shared_ptrs means a lot of unnecessary reference count increments // and decrements as we iterate through the changes. // // It should be sufficient for users to receive raw pointers rather than // shared_ptrs. Callers should always maintain a shared_ptr to the top-level // SwitchState object, so they should never need the shared_ptr reference // count on individual nodes. std::shared_ptr<Node> old_; std::shared_ptr<Node> new_; }; /* * An iterator for walking over the Nodes that changed between the two * NodeMaps. */ template <typename MAP, typename VALUE, typename MAPPOINTERTRAITS> class NodeMapDelta<MAP, VALUE, MAPPOINTERTRAITS>::Iterator { public: using MapType = MAP; using Node = typename MAP::Node; // Iterator properties using iterator_category = std::forward_iterator_tag; using value_type = VALUE; using difference_type = ptrdiff_t; using pointer = VALUE*; using reference = VALUE&; Iterator( const MapType* oldMap, typename MapType::Iterator oldIt, const MapType* newMap, typename MapType::Iterator newIt); Iterator(); const value_type& operator*() const { return value_; } const value_type* operator->() const { return &value_; } Iterator& operator++() { advance(); return *this; } Iterator operator++(int) { Iterator tmp(*this); advance(); return tmp; } bool operator==(const Iterator& other) const { return oldIt_ == other.oldIt_ && newIt_ == other.newIt_; } bool operator!=(const Iterator& other) const { return !operator==(other); } private: using InnerIter = typename MapType::Iterator; using Traits = typename MapType::Traits; void advance(); void updateValue(); InnerIter oldIt_{nullptr}; InnerIter newIt_{nullptr}; const MapType* oldMap_{nullptr}; const MapType* newMap_{nullptr}; VALUE value_; static std::shared_ptr<Node> nullNode_; }; template <typename MAP, typename VALUE, typename MAPPOINTERTRAITS> typename NodeMapDelta<MAP, VALUE, MAPPOINTERTRAITS>::Iterator NodeMapDelta<MAP, VALUE, MAPPOINTERTRAITS>::begin() const { if (old_ == new_) { return end(); } // To support deltas where the old node is null (to represent newly created // nodes), point the old side of the iterator at the new node, but start it // at the end of the map. if (!old_) { return Iterator(getNew(), new_->end(), getNew(), new_->begin()); } // And vice-versa for the new node being null (to represent removed nodes). if (!new_) { return Iterator(getOld(), old_->begin(), getOld(), old_->end()); } return Iterator(getOld(), old_->begin(), getNew(), new_->begin()); } template <typename MAP, typename VALUE, typename MAPPOINTERTRAITS> typename NodeMapDelta<MAP, VALUE, MAPPOINTERTRAITS>::Iterator NodeMapDelta<MAP, VALUE, MAPPOINTERTRAITS>::end() const { if (!old_) { return Iterator(getNew(), new_->end(), getNew(), new_->end()); } if (!new_) { return Iterator(getOld(), old_->end(), getOld(), old_->end()); } return Iterator(getOld(), old_->end(), getNew(), new_->end()); } } // namespace facebook::fboss