2c82f67097
PiperOrigin-RevId: 508786558
799 lines
30 KiB
C++
799 lines
30 KiB
C++
// Copyright 2019 The MediaPipe Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
// Defines Packet, a container capable of holding an object of any type.
|
|
|
|
#ifndef MEDIAPIPE_FRAMEWORK_PACKET_H_
|
|
#define MEDIAPIPE_FRAMEWORK_PACKET_H_
|
|
|
|
#include <cstddef>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <type_traits>
|
|
|
|
#include "absl/base/macros.h"
|
|
#include "absl/memory/memory.h"
|
|
#include "absl/strings/str_cat.h"
|
|
#include "absl/synchronization/mutex.h"
|
|
#include "mediapipe/framework/deps/no_destructor.h"
|
|
#include "mediapipe/framework/deps/registration.h"
|
|
#include "mediapipe/framework/port.h"
|
|
#include "mediapipe/framework/port/canonical_errors.h"
|
|
#include "mediapipe/framework/port/logging.h"
|
|
#include "mediapipe/framework/port/proto_ns.h"
|
|
#include "mediapipe/framework/port/status.h"
|
|
#include "mediapipe/framework/port/status_builder.h"
|
|
#include "mediapipe/framework/port/status_macros.h"
|
|
#include "mediapipe/framework/port/statusor.h"
|
|
#include "mediapipe/framework/timestamp.h"
|
|
#include "mediapipe/framework/tool/type_util.h"
|
|
#include "mediapipe/framework/type_map.h"
|
|
|
|
namespace mediapipe {
|
|
|
|
class Packet;
|
|
|
|
namespace packet_internal {
|
|
class HolderBase;
|
|
|
|
Packet Create(HolderBase* holder);
|
|
Packet Create(HolderBase* holder, Timestamp timestamp);
|
|
Packet Create(std::shared_ptr<HolderBase> holder, Timestamp timestamp);
|
|
const HolderBase* GetHolder(const Packet& packet);
|
|
const std::shared_ptr<HolderBase>& GetHolderShared(const Packet& packet);
|
|
std::shared_ptr<HolderBase> GetHolderShared(Packet&& packet);
|
|
absl::StatusOr<Packet> PacketFromDynamicProto(const std::string& type_name,
|
|
const std::string& serialized);
|
|
} // namespace packet_internal
|
|
|
|
// A generic container class which can hold data of any type. The type of
|
|
// the data is specified when accessing the data (using Packet::Get<T>()).
|
|
//
|
|
// The Packet is implemented as a reference-counted pointer. This means
|
|
// that copying Packets creates a fast, shallow copy. Packets are
|
|
// copyable, movable, and assignable. Packets can be stored in STL
|
|
// containers. A Packet may optionally contain a timestamp.
|
|
//
|
|
// The preferred method of creating a Packet is with MakePacket<T>().
|
|
// The Packet typically owns the object that it contains, but
|
|
// PointToForeign allows a Packet to be constructed which does not
|
|
// own its data.
|
|
//
|
|
// This class is thread compatible.
|
|
class Packet {
|
|
public:
|
|
// Creates an empty Packet, for which IsEmpty()==true and
|
|
// Timestamp()==Timestamp::Unset(). Calling Get() on this Packet leads
|
|
// to CHECK-failure.
|
|
Packet() = default;
|
|
|
|
// Copy constructor and assignment operator.
|
|
Packet(const Packet&);
|
|
Packet& operator=(const Packet&);
|
|
|
|
// Move constructor and assignment operator that take non-const rvalue
|
|
// reference.
|
|
Packet(Packet&&);
|
|
Packet& operator=(Packet&&);
|
|
|
|
// Returns a Packet that contains the same data as *this, and has the
|
|
// given timestamp. Does not modify *this.
|
|
Packet At(class Timestamp timestamp) const&;
|
|
|
|
// The rvalue reference overload of Packet's member function
|
|
// Packet::At(class Timestamp). Moves *this to a new Packet and returns
|
|
// the new Packet with the given timestamp.
|
|
Packet At(class Timestamp timestamp) &&;
|
|
|
|
// Returns true iff the Packet has been created using the default
|
|
// constructor Packet(), or is a copy of such a Packet.
|
|
bool IsEmpty() const;
|
|
|
|
// Returns the reference to the object of typename T if it contains
|
|
// one, crashes otherwise. It is safe to concurrently call Get()
|
|
// on the same packet from multiple threads.
|
|
template <typename T>
|
|
const T& Get() const;
|
|
|
|
// Transfers the ownership of holder's data to a unique pointer
|
|
// of the object if the packet is the sole owner of a non-foreign
|
|
// holder. Otherwise, returns error when the packet can't be consumed.
|
|
// See ConsumeOrCopy for threading requirements and example usage.
|
|
template <typename T>
|
|
absl::StatusOr<std::unique_ptr<T>> Consume();
|
|
|
|
// Consumes the packet and transfers the ownership of the data to a
|
|
// unique pointer if the packet is the sole owner of a non-foreign
|
|
// holder. Otherwise, the unique pointer holds a copy of the original
|
|
// data. In either case, the original packet is set to empty. The
|
|
// method returns error when the packet can't be consumed or copied. If
|
|
// was_copied is not nullptr, it is set to indicate whether the packet
|
|
// data was copied.
|
|
// Packet is thread-compatible, therefore Packet::ConsumeOrCopy()
|
|
// must be thread-compatible: clients who use this function are
|
|
// responsible for ensuring that no other thread is doing anything
|
|
// with the Packet.
|
|
// Example usage:
|
|
// ASSIGN_OR_RETURN(std::unique_ptr<Detection> detection,
|
|
// p.ConsumeOrCopy<Detection>());
|
|
// // The unique_ptr type can be omitted with auto.
|
|
// ASSIGN_OR_RETURN(auto detection, p.ConsumeOrCopy<Detection>());
|
|
// If you would like to crash on failure (prefer ASSIGN_OR_RETURN):
|
|
// auto detection = p.ConsumeOrCopy<Detection>().value();
|
|
// // In functions which do not return absl::Status use an adaptor
|
|
// // function as the third argument to ASSIGN_OR_RETURN. In tests,
|
|
// // use an adaptor which returns void.
|
|
// ASSIGN_OR_RETURN(auto detection, p.ConsumeOrCopy<Detection>(),
|
|
// _.With([](const absl::Status& status) {
|
|
// MP_EXPECT_OK(status);
|
|
// // Use CHECK_OK to crash and report a usable line
|
|
// // number (which the value() alternative does not).
|
|
// // Include a return statement if the return value is
|
|
// // non-void. For example: return 1;
|
|
// }));
|
|
//
|
|
// Version for non-arrays.
|
|
template <typename T>
|
|
absl::StatusOr<std::unique_ptr<T>> ConsumeOrCopy(
|
|
bool* was_copied = nullptr,
|
|
typename std::enable_if<!std::is_array<T>::value>::type* = nullptr);
|
|
|
|
// Version for bounded array.
|
|
template <typename T>
|
|
absl::StatusOr<std::unique_ptr<T>> ConsumeOrCopy(
|
|
bool* was_copied = nullptr,
|
|
typename std::enable_if<std::is_array<T>::value &&
|
|
std::extent<T>::value != 0>::type* = nullptr);
|
|
|
|
// TODO: Support unbounded array after fixing the bug in holder's
|
|
// delete helper.
|
|
// Version for unbounded array.
|
|
template <typename T>
|
|
absl::StatusOr<std::unique_ptr<T>> ConsumeOrCopy(
|
|
bool* was_copied = nullptr,
|
|
typename std::enable_if<std::is_array<T>::value &&
|
|
std::extent<T>::value == 0>::type* = nullptr);
|
|
|
|
// Returns the reference to type MessageLite data, if the underlying
|
|
// object type is protocol buffer, crashes otherwise.
|
|
const proto_ns::MessageLite& GetProtoMessageLite() const;
|
|
|
|
// Returns a vector of pointers to MessageLite data, if the underlying
|
|
// object type is a vector of MessageLite data, returns an error otherwise.
|
|
// Note: This function is meant to be used internally within the MediaPipe
|
|
// framework only.
|
|
StatusOr<std::vector<const proto_ns::MessageLite*>>
|
|
GetVectorOfProtoMessageLitePtrs() const;
|
|
|
|
// Returns an error if the packet does not contain data of type T.
|
|
template <typename T>
|
|
absl::Status ValidateAsType() const {
|
|
return ValidateAsType(kTypeId<T>);
|
|
}
|
|
|
|
// Returns an error if the packet is not an instance of
|
|
// a protocol buffer message.
|
|
absl::Status ValidateAsProtoMessageLite() const;
|
|
|
|
// Get the type id for the underlying type stored in the Packet.
|
|
// Crashes if IsEmpty() == true.
|
|
TypeId GetTypeId() const;
|
|
|
|
// Returns the timestamp.
|
|
class Timestamp Timestamp() const;
|
|
|
|
std::string DebugString() const;
|
|
friend std::ostream& operator<<(std::ostream& stream, const Packet& p) {
|
|
return stream << p.DebugString();
|
|
}
|
|
|
|
// Returns the type name. If the packet is empty or the type is not
|
|
// registered (with MEDIAPIPE_REGISTER_TYPE or companion macros) then
|
|
// the empty string is returned.
|
|
std::string RegisteredTypeName() const;
|
|
// Returns a string with the best guess at the type name.
|
|
std::string DebugTypeName() const;
|
|
|
|
private:
|
|
friend Packet packet_internal::Create(packet_internal::HolderBase* holder);
|
|
friend Packet packet_internal::Create(packet_internal::HolderBase* holder,
|
|
class Timestamp timestamp);
|
|
friend Packet packet_internal::Create(
|
|
std::shared_ptr<packet_internal::HolderBase> holder,
|
|
class Timestamp timestamp);
|
|
friend const packet_internal::HolderBase* packet_internal::GetHolder(
|
|
const Packet& packet);
|
|
friend const std::shared_ptr<packet_internal::HolderBase>&
|
|
packet_internal::GetHolderShared(const Packet& packet);
|
|
friend std::shared_ptr<packet_internal::HolderBase>
|
|
packet_internal::GetHolderShared(Packet&& packet);
|
|
|
|
friend class PacketType;
|
|
absl::Status ValidateAsType(TypeId type_id) const;
|
|
|
|
std::shared_ptr<packet_internal::HolderBase> holder_;
|
|
class Timestamp timestamp_;
|
|
};
|
|
|
|
// Factory functions for creating Packets. Non-members as opposed to static
|
|
// methods, to prevent users from mistakenly calling them on Packet instances.
|
|
|
|
// Returns a Packet that adopts the object; the Packet assumes the ownership.
|
|
// The timestamp of the returned Packet is Timestamp::Unset(). To set the
|
|
// timestamp, the caller should do Adopt(...).At(...).
|
|
//
|
|
// Generally prefer MakePacket<T>().
|
|
template <typename T>
|
|
Packet Adopt(const T* ptr);
|
|
|
|
// Returns a Packet that does not own its data. The data pointed to by *ptr
|
|
// remains owned by the caller, who must ensure that it outlives not only the
|
|
// returned Packet but also all of its copies. The timestamp of the returned
|
|
// Packet is Timestamp::Unset(). To set the timestamp, the caller should do
|
|
// PointToForeign(...).At(...).
|
|
template <typename T>
|
|
Packet PointToForeign(const T* ptr);
|
|
|
|
// Adopts the data but places it in a std::unique_ptr inside the
|
|
// resulting Packet, leaving the timestamp unset. This allows the
|
|
// adopted data to be mutated, with the mutable data accessible as
|
|
// packet.Get<std::unique_ptr<T>>().get(). GetFromUniquePtr below provides
|
|
// a more syntactically-pleasing way of accomplishing that.
|
|
template <typename T>
|
|
Packet AdoptAsUniquePtr(T* ptr) {
|
|
static_assert(
|
|
!std::is_const<T>::value,
|
|
"AdoptAsUniquePtr should not be called with a pointer-to-const.");
|
|
return Adopt(new std::unique_ptr<T>(ptr));
|
|
}
|
|
|
|
// A SyncedPacket is a packet containing a reference to another packet, and the
|
|
// reference can be updated.
|
|
// SyncedPacket is thread-safe.
|
|
class SyncedPacket {
|
|
public:
|
|
explicit SyncedPacket(const Packet& packet) : packet_(packet) {}
|
|
void UpdatePacket(const Packet& packet) {
|
|
absl::WriterMutexLock writer_lock(&mutex_);
|
|
packet_ = packet;
|
|
}
|
|
Packet Get() {
|
|
absl::ReaderMutexLock reader_lock(&mutex_);
|
|
return packet_;
|
|
}
|
|
|
|
private:
|
|
absl::Mutex mutex_;
|
|
Packet packet_;
|
|
};
|
|
|
|
// Adopt the data as SyncedPacket, so that the content of the packet can be
|
|
// updated in a thread-safe way.
|
|
// Usage:
|
|
// Packet synced_packet = AdoptAsSyncedPacket(new int(100));
|
|
// Packet value_packet =
|
|
// synced_packet.Get<std::unique_ptr<SyncedPacket>>()->Get();
|
|
// EXPECT_EQ(100, value_packet.Get<int>());
|
|
// // update the value.
|
|
// Packet new_value_packet = Adopt(new int(999));
|
|
// synced_packet.Get<std::unique_ptr<SyncedPacket>>()
|
|
// ->UpdatePacket(new_value_packet);
|
|
// Packet packet_new =
|
|
// synced_packet.Get<std::unique_ptr<SyncedPacket>>()->Get();
|
|
// EXPECT_EQ(999, packet_new.Get<int>());
|
|
|
|
template <typename T>
|
|
Packet AdoptAsSyncedPacket(const T* ptr) {
|
|
Packet packet = Adopt(ptr);
|
|
return AdoptAsUniquePtr(new SyncedPacket(packet));
|
|
}
|
|
|
|
// Create a packet containing an object of type T initialized with the
|
|
// provided arguments. Similar to MakeUnique. Especially convenient for arrays,
|
|
// since it ensures the packet gets the right type (see below).
|
|
//
|
|
// Version for scalars.
|
|
template <typename T,
|
|
typename std::enable_if<!std::is_array<T>::value>::type* = nullptr,
|
|
typename... Args>
|
|
Packet MakePacket(Args&&... args) { // NOLINT(build/c++11)
|
|
return Adopt(new T(std::forward<Args>(args)...));
|
|
}
|
|
|
|
// Version for arrays. We have to use reinterpret_cast because new T[N]
|
|
// returns a T* instead of a T(*)[N] (i.e. a pointer to the first element
|
|
// instead of a pointer to the array itself - they have the same value, but
|
|
// different types), which would prevent Adopt from seeing the array's type
|
|
// if we did not have the cast.
|
|
template <typename T,
|
|
typename std::enable_if<std::is_array<T>::value>::type* = nullptr,
|
|
typename... Args>
|
|
Packet MakePacket(Args&&... args) { // NOLINT(build/c++11)
|
|
return Adopt(reinterpret_cast<T*>(
|
|
new T{std::forward<typename std::remove_extent<T>::type>(args)...}));
|
|
}
|
|
|
|
// Returns a mutable pointer to the data in a unique_ptr in a packet. This
|
|
// is useful in combination with AdoptAsUniquePtr. The caller must
|
|
// exercise caution when mutating the retrieved data, since the data
|
|
// may be accessible from other locations.
|
|
template <typename T>
|
|
T* GetFromUniquePtr(const Packet& packet) {
|
|
return packet.Get<std::unique_ptr<T>>().get();
|
|
}
|
|
|
|
// Returns a shared_ptr to the payload of the packet which retains its object
|
|
// through a copy of the packet.
|
|
// Use std::const_pointer_cast if you need a shared_ptr<T>, but remember that
|
|
// you must not change the payload if the packet has other owners. Use Consume
|
|
// if you want to try and modify the payload directly.
|
|
template <typename T>
|
|
std::shared_ptr<const T> SharedPtrWithPacket(Packet packet) {
|
|
// This needs to be a separate statement because the evaluation order of
|
|
// function arguments is unspecified, and if the lambda is created first it
|
|
// moves the packet.
|
|
const T* ptr = &packet.Get<T>();
|
|
return std::shared_ptr<const T>(
|
|
ptr, [packet = std::move(packet)](const T* ptr) mutable { packet = {}; });
|
|
}
|
|
|
|
//// Implementation details.
|
|
namespace packet_internal {
|
|
|
|
template <typename T>
|
|
class Holder;
|
|
template <typename T>
|
|
class ForeignHolder;
|
|
|
|
class HolderBase {
|
|
public:
|
|
HolderBase() {}
|
|
HolderBase(const HolderBase&) = delete;
|
|
HolderBase& operator=(const HolderBase&) = delete;
|
|
virtual ~HolderBase();
|
|
template <typename T>
|
|
bool PayloadIsOfType() const {
|
|
return GetTypeId() == kTypeId<T>;
|
|
}
|
|
// Returns a printable string identifying the type stored in the holder.
|
|
virtual const std::string DebugTypeName() const = 0;
|
|
// Returns the registered type name if it's available, otherwise the
|
|
// empty string.
|
|
virtual const std::string RegisteredTypeName() const = 0;
|
|
// Get the type id of the underlying data type.
|
|
virtual TypeId GetTypeId() const = 0;
|
|
// Downcasts this to Holder<T>. Returns nullptr if deserialization
|
|
// failed or if the requested type is not what is stored.
|
|
template <typename T>
|
|
Holder<T>* As();
|
|
|
|
// Same as non-const As() function.
|
|
template <typename T>
|
|
const Holder<T>* As() const;
|
|
|
|
// Returns the pointer to MessageLite type for the data in holder, if
|
|
// underlying object is protocol buffer type, otherwise, nullptr is returned.
|
|
virtual const proto_ns::MessageLite* GetProtoMessageLite() = 0;
|
|
|
|
// Returns a vector<MessageLite*> for the data in the holder, if the
|
|
// underlying object is a vector of protocol buffer objects, otherwise,
|
|
// returns an error.
|
|
virtual StatusOr<std::vector<const proto_ns::MessageLite*>>
|
|
GetVectorOfProtoMessageLite() const = 0;
|
|
|
|
virtual bool HasForeignOwner() const { return false; }
|
|
};
|
|
|
|
// Two helper functions to get the proto base pointers.
|
|
template <typename T>
|
|
const proto_ns::MessageLite* ConvertToProtoMessageLite(const T* data,
|
|
std::false_type) {
|
|
return nullptr;
|
|
}
|
|
|
|
template <typename T>
|
|
const proto_ns::MessageLite* ConvertToProtoMessageLite(const T* data,
|
|
std::true_type) {
|
|
return data;
|
|
}
|
|
|
|
// Helper structs for determining if a type is an std::vector<Proto>.
|
|
template <typename Type>
|
|
struct is_proto_vector : public std::false_type {};
|
|
|
|
template <typename ItemT, typename Allocator>
|
|
struct is_proto_vector<std::vector<ItemT, Allocator>>
|
|
: public std::is_base_of<proto_ns::MessageLite, ItemT>::type {};
|
|
|
|
// Helper function to create and return a vector of pointers to proto message
|
|
// elements of the vector passed into the function.
|
|
template <typename T>
|
|
StatusOr<std::vector<const proto_ns::MessageLite*>>
|
|
ConvertToVectorOfProtoMessageLitePtrs(const T* data,
|
|
/*is_proto_vector=*/std::false_type) {
|
|
return absl::InvalidArgumentError(absl::StrCat(
|
|
"The Packet stores \"", kTypeId<T>.name(), "\"",
|
|
"which is not convertible to vector<proto_ns::MessageLite*>."));
|
|
}
|
|
|
|
template <typename T>
|
|
StatusOr<std::vector<const proto_ns::MessageLite*>>
|
|
ConvertToVectorOfProtoMessageLitePtrs(const T* data,
|
|
/*is_proto_vector=*/std::true_type) {
|
|
std::vector<const proto_ns::MessageLite*> result;
|
|
for (auto it = data->begin(); it != data->end(); ++it) {
|
|
const proto_ns::MessageLite* element = &(*it);
|
|
result.push_back(element);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// This registry is used to create Holders of the right concrete C++ type given
|
|
// a proto type string (which is used as the registration key).
|
|
class MessageHolderRegistry
|
|
: public GlobalFactoryRegistry<std::unique_ptr<HolderBase>> {};
|
|
|
|
template <typename T>
|
|
struct is_concrete_proto_t
|
|
: public std::integral_constant<
|
|
bool, std::is_base_of<proto_ns::MessageLite, T>{} &&
|
|
!std::is_same<proto_ns::MessageLite, T>{} &&
|
|
!std::is_same<proto_ns::Message, T>{}> {};
|
|
|
|
// Registers a message type. T must be a non-cv-qualified concrete proto type.
|
|
template <typename T>
|
|
struct MessageRegistrationImpl {
|
|
static NoDestructor<mediapipe::RegistrationToken> registration;
|
|
// This could have been a lambda inside registration's initializer below, but
|
|
// MSVC has a bug with lambdas, so we put it here as a workaround.
|
|
static std::unique_ptr<Holder<T>> CreateMessageHolder() {
|
|
return absl::make_unique<Holder<T>>(new T);
|
|
}
|
|
};
|
|
|
|
// Static members of template classes can be defined in the header.
|
|
template <typename T>
|
|
NoDestructor<mediapipe::RegistrationToken>
|
|
MessageRegistrationImpl<T>::registration(MessageHolderRegistry::Register(
|
|
T{}.GetTypeName(), MessageRegistrationImpl<T>::CreateMessageHolder,
|
|
__FILE__, __LINE__));
|
|
|
|
// For non-Message payloads, this does nothing.
|
|
template <typename T, typename Enable = void>
|
|
struct HolderSupport {
|
|
static void EnsureStaticInit() {}
|
|
};
|
|
|
|
// This template ensures that, for each concrete MessageLite subclass that is
|
|
// stored in a Packet, we register a function that allows us to create a
|
|
// Holder with the correct payload type from the proto's type name.
|
|
template <typename T>
|
|
struct HolderSupport<T,
|
|
typename std::enable_if<is_concrete_proto_t<T>{}>::type> {
|
|
// We must use std::remove_cv to ensure we don't try to register Foo twice if
|
|
// there are Holder<Foo> and Holder<const Foo>. TODO: lift this
|
|
// up to Holder?
|
|
using R = MessageRegistrationImpl<typename std::remove_cv<T>::type>;
|
|
// For the registration static member to be instantiated, it needs to be
|
|
// referenced in a context that requires the definition to exist (see ISO/IEC
|
|
// C++ 2003 standard, 14.7.1). Calling this ensures that's the case.
|
|
// We need two different call-sites to cover proto types for which packets
|
|
// are only ever created (i.e. the protos are only produced by calculators)
|
|
// and proto types for which packets are only ever consumed (i.e. the protos
|
|
// are only consumed by calculators).
|
|
static void EnsureStaticInit() { CHECK(R::registration.get() != nullptr); }
|
|
};
|
|
|
|
template <typename T>
|
|
class Holder : public HolderBase {
|
|
public:
|
|
explicit Holder(const T* ptr) : ptr_(ptr) {
|
|
HolderSupport<T>::EnsureStaticInit();
|
|
}
|
|
~Holder() override { delete_helper(); }
|
|
const T& data() const {
|
|
HolderSupport<T>::EnsureStaticInit();
|
|
return *ptr_;
|
|
}
|
|
TypeId GetTypeId() const final { return kTypeId<T>; }
|
|
// Releases the underlying data pointer and transfers the ownership to a
|
|
// unique pointer.
|
|
// This method is dangerous and is only used by Packet::Consume() if the
|
|
// packet is the only owner of the holder.
|
|
template <typename U = T>
|
|
absl::StatusOr<std::unique_ptr<T>> Release(
|
|
typename std::enable_if<!std::is_array<U>::value ||
|
|
std::extent<U>::value != 0>::type* = 0) {
|
|
if (HasForeignOwner()) {
|
|
return InternalError(
|
|
"Foreign holder can't release data ptr without ownership.");
|
|
}
|
|
// Casts away constness to make the data mutable after the release.
|
|
std::unique_ptr<T> data_ptr(const_cast<T*>(ptr_));
|
|
ptr_ = nullptr;
|
|
return std::move(data_ptr);
|
|
}
|
|
// TODO: support unbounded array after fixing the bug in holder's
|
|
// delete helper.
|
|
template <typename U = T>
|
|
absl::StatusOr<std::unique_ptr<T>> Release(
|
|
typename std::enable_if<std::is_array<U>::value &&
|
|
std::extent<U>::value == 0>::type* = 0) {
|
|
return absl::InternalError("Release T[] isn't supported.");
|
|
}
|
|
const std::string DebugTypeName() const final {
|
|
return MediaPipeTypeStringOrDemangled<T>();
|
|
}
|
|
const std::string RegisteredTypeName() const final {
|
|
const std::string* type_string = MediaPipeTypeString<T>();
|
|
if (type_string) {
|
|
return *type_string;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
protected:
|
|
// The pointer that uniquely owns the data. However, the ownership of the
|
|
// Holder itself may be shared by several Packets.
|
|
const T* ptr_;
|
|
|
|
// Returns the MessageLite pointer to the data, if the underlying object type
|
|
// is protocol buffer, otherwise, nullptr is returned.
|
|
const proto_ns::MessageLite* GetProtoMessageLite() override {
|
|
return ConvertToProtoMessageLite(
|
|
ptr_, std::is_base_of<proto_ns::MessageLite, T>());
|
|
}
|
|
|
|
// Returns a vector<MessageLite*> for the data in the holder, if the
|
|
// underlying object is a vector of protocol buffer objects, otherwise,
|
|
// returns an error.
|
|
StatusOr<std::vector<const proto_ns::MessageLite*>>
|
|
GetVectorOfProtoMessageLite() const override {
|
|
return ConvertToVectorOfProtoMessageLitePtrs(ptr_, is_proto_vector<T>());
|
|
}
|
|
|
|
private:
|
|
// Call delete[] if T is an array, delete otherwise.
|
|
template <typename U = T>
|
|
inline void delete_helper(
|
|
typename std::enable_if<!std::is_array<U>::value>::type* = 0) {
|
|
delete ptr_;
|
|
}
|
|
template <typename U = T>
|
|
inline void delete_helper(
|
|
typename std::enable_if<std::is_array<U>::value>::type* = 0) {
|
|
// Casts ptr_ from const Type(*)[] or const Type(*)[N] to const Type*.
|
|
// Deleting a pointer to incomplete type (Type(*)[]) causes compile error.
|
|
delete[] reinterpret_cast<const typename std::remove_extent<U>::type*>(
|
|
ptr_);
|
|
}
|
|
};
|
|
|
|
// Like Holder, but does not own its data.
|
|
template <typename T>
|
|
class ForeignHolder : public Holder<T> {
|
|
public:
|
|
using Holder<T>::Holder;
|
|
~ForeignHolder() override {
|
|
// Null out ptr_ so it doesn't get deleted by ~Holder.
|
|
// Note that ~Holder cannot call HasForeignOwner because the subclass's
|
|
// destructor runs first.
|
|
this->ptr_ = nullptr;
|
|
}
|
|
bool HasForeignOwner() const final { return true; }
|
|
};
|
|
|
|
template <typename T>
|
|
Holder<T>* HolderBase::As() {
|
|
if (PayloadIsOfType<T>()) {
|
|
return static_cast<Holder<T>*>(this);
|
|
}
|
|
// Does not hold a T.
|
|
return nullptr;
|
|
}
|
|
|
|
template <typename T>
|
|
const Holder<T>* HolderBase::As() const {
|
|
if (PayloadIsOfType<T>()) {
|
|
return static_cast<const Holder<T>*>(this);
|
|
}
|
|
// Does not hold a T.
|
|
return nullptr;
|
|
}
|
|
|
|
} // namespace packet_internal
|
|
|
|
inline Packet::Packet(const Packet& packet)
|
|
: holder_(packet.holder_), timestamp_(packet.timestamp_) {
|
|
VLOG(4) << "Using copy constructor of " << packet.DebugString();
|
|
}
|
|
|
|
inline Packet& Packet::operator=(const Packet& packet) {
|
|
VLOG(4) << "Using copy assignment operator of " << packet.DebugString();
|
|
if (this != &packet) {
|
|
holder_ = packet.holder_;
|
|
timestamp_ = packet.timestamp_;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <typename T>
|
|
inline absl::StatusOr<std::unique_ptr<T>> Packet::Consume() {
|
|
// If type validation fails, returns error.
|
|
MP_RETURN_IF_ERROR(ValidateAsType<T>());
|
|
// Clients who use this function are responsible for ensuring that no
|
|
// other thread is doing anything with this Packet.
|
|
if (!holder_->HasForeignOwner() && holder_.unique()) {
|
|
VLOG(2) << "Consuming the data of " << DebugString();
|
|
absl::StatusOr<std::unique_ptr<T>> release_result =
|
|
holder_->As<T>()->Release();
|
|
if (release_result.ok()) {
|
|
VLOG(2) << "Setting " << DebugString() << " to empty.";
|
|
holder_.reset();
|
|
}
|
|
return release_result;
|
|
}
|
|
// If packet isn't the sole owner of the holder, returns kFailedPrecondition
|
|
// error with message.
|
|
return absl::Status(absl::StatusCode::kFailedPrecondition,
|
|
"Packet isn't the sole owner of the holder.");
|
|
}
|
|
|
|
template <typename T>
|
|
inline absl::StatusOr<std::unique_ptr<T>> Packet::ConsumeOrCopy(
|
|
bool* was_copied,
|
|
typename std::enable_if<!std::is_array<T>::value>::type*) {
|
|
MP_RETURN_IF_ERROR(ValidateAsType<T>());
|
|
// If holder is the sole owner of the underlying data, consumes this packet.
|
|
if (!holder_->HasForeignOwner() && holder_.unique()) {
|
|
VLOG(2) << "Consuming the data of " << DebugString();
|
|
absl::StatusOr<std::unique_ptr<T>> release_result =
|
|
holder_->As<T>()->Release();
|
|
if (release_result.ok()) {
|
|
VLOG(2) << "Setting " << DebugString() << " to empty.";
|
|
holder_.reset();
|
|
}
|
|
if (was_copied) {
|
|
*was_copied = false;
|
|
}
|
|
return release_result;
|
|
}
|
|
VLOG(2) << "Copying the data of " << DebugString();
|
|
std::unique_ptr<T> data_ptr = absl::make_unique<T>(Get<T>());
|
|
VLOG(2) << "Setting " << DebugString() << " to empty.";
|
|
holder_.reset();
|
|
if (was_copied) {
|
|
*was_copied = true;
|
|
}
|
|
return std::move(data_ptr);
|
|
}
|
|
|
|
template <typename T>
|
|
inline absl::StatusOr<std::unique_ptr<T>> Packet::ConsumeOrCopy(
|
|
bool* was_copied,
|
|
typename std::enable_if<std::is_array<T>::value &&
|
|
std::extent<T>::value != 0>::type*) {
|
|
MP_RETURN_IF_ERROR(ValidateAsType<T>());
|
|
// If holder is the sole owner of the underlying data, consumes this packet.
|
|
if (!holder_->HasForeignOwner() && holder_.unique()) {
|
|
VLOG(2) << "Consuming the data of " << DebugString();
|
|
absl::StatusOr<std::unique_ptr<T>> release_result =
|
|
holder_->As<T>()->Release();
|
|
if (release_result.ok()) {
|
|
VLOG(2) << "Setting " << DebugString() << " to empty.";
|
|
holder_.reset();
|
|
}
|
|
if (was_copied) {
|
|
*was_copied = false;
|
|
}
|
|
return release_result;
|
|
}
|
|
VLOG(2) << "Copying the data of " << DebugString();
|
|
const auto& original_array = Get<T>();
|
|
// Type T is bounded array type, such as int[N] and float[M].
|
|
// The new operator creates a new bounded array.
|
|
std::unique_ptr<T> data_ptr(reinterpret_cast<T*>(new T));
|
|
// Copies bounded array data into data_ptr.
|
|
std::copy(std::begin(original_array), std::end(original_array),
|
|
std::begin(*data_ptr));
|
|
VLOG(2) << "Setting " << DebugString() << " to empty.";
|
|
holder_.reset();
|
|
if (was_copied) {
|
|
*was_copied = true;
|
|
}
|
|
return std::move(data_ptr);
|
|
}
|
|
|
|
template <typename T>
|
|
inline absl::StatusOr<std::unique_ptr<T>> Packet::ConsumeOrCopy(
|
|
bool* was_copied,
|
|
typename std::enable_if<std::is_array<T>::value &&
|
|
std::extent<T>::value == 0>::type*) {
|
|
return absl::InternalError("Unbounded array isn't supported.");
|
|
}
|
|
|
|
inline Packet::Packet(Packet&& packet) {
|
|
VLOG(4) << "Using move constructor of " << packet.DebugString();
|
|
holder_ = std::move(packet.holder_);
|
|
timestamp_ = packet.timestamp_;
|
|
packet.timestamp_ = Timestamp::Unset();
|
|
}
|
|
|
|
inline Packet& Packet::operator=(Packet&& packet) {
|
|
VLOG(4) << "Using move assignment operator of " << packet.DebugString();
|
|
if (this != &packet) {
|
|
holder_ = std::move(packet.holder_);
|
|
timestamp_ = packet.timestamp_;
|
|
packet.timestamp_ = Timestamp::Unset();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
inline bool Packet::IsEmpty() const { return holder_ == nullptr; }
|
|
|
|
inline TypeId Packet::GetTypeId() const {
|
|
CHECK(holder_);
|
|
return holder_->GetTypeId();
|
|
}
|
|
|
|
template <typename T>
|
|
inline const T& Packet::Get() const {
|
|
packet_internal::Holder<T>* holder = IsEmpty() ? nullptr : holder_->As<T>();
|
|
if (holder == nullptr) {
|
|
// Produce a good error message.
|
|
absl::Status status = ValidateAsType<T>();
|
|
LOG(FATAL) << "Packet::Get() failed: " << status.message();
|
|
}
|
|
return holder->data();
|
|
}
|
|
|
|
inline Timestamp Packet::Timestamp() const { return timestamp_; }
|
|
|
|
template <typename T>
|
|
Packet Adopt(const T* ptr) {
|
|
CHECK(ptr != nullptr);
|
|
return packet_internal::Create(new packet_internal::Holder<T>(ptr));
|
|
}
|
|
|
|
template <typename T>
|
|
Packet PointToForeign(const T* ptr) {
|
|
CHECK(ptr != nullptr);
|
|
return packet_internal::Create(new packet_internal::ForeignHolder<T>(ptr));
|
|
}
|
|
|
|
// Equal Packets refer to the same memory contents, like equal pointers.
|
|
inline bool operator==(const Packet& p1, const Packet& p2) {
|
|
return packet_internal::GetHolder(p1) == packet_internal::GetHolder(p2);
|
|
}
|
|
inline bool operator!=(const Packet& p1, const Packet& p2) {
|
|
return !(p1 == p2);
|
|
}
|
|
|
|
namespace packet_internal {
|
|
|
|
inline const std::shared_ptr<HolderBase>& GetHolderShared(
|
|
const Packet& packet) {
|
|
return packet.holder_;
|
|
}
|
|
|
|
inline std::shared_ptr<HolderBase> GetHolderShared(Packet&& packet) {
|
|
return std::move(packet.holder_);
|
|
}
|
|
|
|
} // namespace packet_internal
|
|
|
|
} // namespace mediapipe
|
|
|
|
#endif // MEDIAPIPE_FRAMEWORK_PACKET_H_
|