Add DaydreamVR native libraries and services
Upstreaming the main VR system components from master-dreamos-dev
into goog/master.
Bug: None
Test: `m -j32` succeeds. Sailfish boots and basic_vr sample app works
Change-Id: I853015872afc443aecee10411ef2d6b79184d051
diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp
new file mode 100644
index 0000000..69300d6
--- /dev/null
+++ b/libs/vr/libpdx/Android.bp
@@ -0,0 +1,59 @@
+cc_library_static {
+ name: "libpdx",
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ export_include_dirs: ["private"],
+ local_include_dirs: ["private"],
+ srcs: [
+ "client.cpp",
+ "service.cpp",
+ "status.cpp",
+ ],
+}
+
+cc_test {
+ name: "pdx_tests",
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ srcs: [
+ "client_tests.cpp",
+ "mock_tests.cpp",
+ "serialization_tests.cpp",
+ "service_tests.cpp",
+ "status_tests.cpp",
+ "thread_local_buffer_tests.cpp",
+ "variant_tests.cpp",
+ ],
+ static_libs: [
+ "libgmock",
+ "libpdx",
+ "liblog",
+ "libutils",
+ ],
+}
+
+// Code analysis target.
+cc_test {
+ name: "pdx_encoder_performance_test",
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-O2",
+ ],
+ srcs: [
+ "encoder_performance_test.cpp",
+ ],
+ static_libs: [
+ "libpdx",
+ ],
+}
diff --git a/libs/vr/libpdx/client.cpp b/libs/vr/libpdx/client.cpp
new file mode 100644
index 0000000..c318628
--- /dev/null
+++ b/libs/vr/libpdx/client.cpp
@@ -0,0 +1,290 @@
+#include "pdx/client.h"
+
+#define LOG_TAG "ServiceFramework"
+#include <log/log.h>
+
+#include <pdx/trace.h>
+#include "errno_guard.h"
+
+namespace android {
+namespace pdx {
+
+void Client::EnableAutoReconnect(int64_t reconnect_timeout_ms) {
+ if (channel_factory_) {
+ reconnect_timeout_ms_ = reconnect_timeout_ms;
+ auto_reconnect_enabled_ = true;
+ }
+}
+
+void Client::DisableAutoReconnect() { auto_reconnect_enabled_ = false; }
+
+bool Client::IsConnected() const { return channel_.get() != nullptr; }
+
+Status<void> Client::CheckReconnect() {
+ Status<void> ret;
+ bool was_disconnected = !IsConnected();
+ if (auto_reconnect_enabled_ && was_disconnected && channel_factory_) {
+ auto status = channel_factory_->Connect(reconnect_timeout_ms_);
+ if (!status) {
+ error_ = -status.error();
+ ret.SetError(status.error());
+ return ret;
+ }
+ channel_ = status.take();
+ }
+
+ if (!IsConnected()) {
+ ret.SetError(ESHUTDOWN);
+ } else {
+ // Call the subclass OnConnect handler. The subclass may choose to close the
+ // connection in the handler, in which case error_ will be non-zero.
+ if (was_disconnected)
+ OnConnect();
+ if (!IsConnected())
+ ret.SetError(-error_);
+ else
+ ret.SetValue();
+ }
+
+ return ret;
+}
+
+bool Client::NeedToDisconnectChannel(int error) const {
+ return error == ESHUTDOWN && auto_reconnect_enabled_;
+}
+
+void Client::CheckDisconnect(int error) {
+ if (NeedToDisconnectChannel(error))
+ Close(error);
+}
+
+Client::Client(std::unique_ptr<ClientChannel> channel)
+ : channel_{std::move(channel)} {}
+
+Client::Client(std::unique_ptr<ClientChannelFactory> channel_factory,
+ int64_t timeout_ms)
+ : channel_factory_{std::move(channel_factory)} {
+ auto status = channel_factory_->Connect(timeout_ms);
+ if (!status) {
+ ALOGE("Client::Client: Failed to connect to service because: %s",
+ status.GetErrorMessage().c_str());
+ error_ = -status.error();
+ } else {
+ channel_ = status.take();
+ }
+}
+
+bool Client::IsInitialized() const {
+ return IsConnected() || (channel_factory_ && auto_reconnect_enabled_);
+}
+
+void Client::OnConnect() {}
+
+int Client::error() const { return error_; }
+
+Status<void> Client::SendImpulse(int opcode) {
+ PDX_TRACE_NAME("Client::SendImpulse");
+ ErrnoGuard errno_guard;
+
+ auto status = CheckReconnect();
+ if (!status)
+ return status;
+
+ status = channel_->SendImpulse(opcode, nullptr, 0);
+ CheckDisconnect(status);
+ return status;
+}
+
+Status<void> Client::SendImpulse(int opcode, const void* buffer,
+ size_t length) {
+ PDX_TRACE_NAME("Client::SendImpulse");
+ ErrnoGuard errno_guard;
+
+ auto status = CheckReconnect();
+ if (!status)
+ return status;
+
+ status = channel_->SendImpulse(opcode, buffer, length);
+ CheckDisconnect(status);
+ return status;
+}
+
+void Client::Close(int error) {
+ ErrnoGuard errno_guard;
+ channel_.reset();
+ // Normalize error codes to negative integer space.
+ error_ = error <= 0 ? error : -error;
+}
+
+int Client::event_fd() const {
+ return IsConnected() ? channel_->event_fd() : -1;
+}
+
+LocalChannelHandle& Client::GetChannelHandle() {
+ return channel_->GetChannelHandle();
+}
+
+///////////////////////////// Transaction implementation //////////////////////
+
+Transaction::Transaction(Client& client) : client_{client} {}
+
+Transaction::~Transaction() {
+ if (state_allocated_ && client_.GetChannel())
+ client_.GetChannel()->FreeTransactionState(state_);
+}
+
+bool Transaction::EnsureStateAllocated() {
+ if (!state_allocated_ && client_.GetChannel()) {
+ state_ = client_.GetChannel()->AllocateTransactionState();
+ state_allocated_ = true;
+ }
+ return state_allocated_;
+}
+
+void Transaction::SendTransaction(int opcode, Status<void>* ret,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) {
+ *ret = client_.CheckReconnect();
+ if (!*ret)
+ return;
+
+ if (!EnsureStateAllocated()) {
+ ret->SetError(ESHUTDOWN);
+ return;
+ }
+
+ auto status = client_.GetChannel()->SendWithInt(
+ state_, opcode, send_vector, send_count, receive_vector, receive_count);
+
+ if (status) {
+ ret->SetValue();
+ } else {
+ ret->SetError(status.error());
+ }
+ CheckDisconnect(status);
+}
+
+void Transaction::SendTransaction(int opcode, Status<int>* ret,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) {
+ auto status = client_.CheckReconnect();
+ if (!status) {
+ ret->SetError(status.error());
+ return;
+ }
+
+ if (!EnsureStateAllocated()) {
+ ret->SetError(ESHUTDOWN);
+ return;
+ }
+
+ *ret = client_.GetChannel()->SendWithInt(
+ state_, opcode, send_vector, send_count, receive_vector, receive_count);
+
+ CheckDisconnect(*ret);
+}
+
+void Transaction::SendTransaction(int opcode, Status<LocalHandle>* ret,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) {
+ auto status = client_.CheckReconnect();
+ if (!status) {
+ ret->SetError(status.error());
+ return;
+ }
+
+ if (!EnsureStateAllocated()) {
+ ret->SetError(ESHUTDOWN);
+ return;
+ }
+
+ *ret = client_.GetChannel()->SendWithFileHandle(
+ state_, opcode, send_vector, send_count, receive_vector, receive_count);
+
+ CheckDisconnect(*ret);
+}
+
+void Transaction::SendTransaction(int opcode, Status<LocalChannelHandle>* ret,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) {
+ auto status = client_.CheckReconnect();
+ if (!status) {
+ ret->SetError(status.error());
+ return;
+ }
+
+ if (!EnsureStateAllocated()) {
+ ret->SetError(ESHUTDOWN);
+ return;
+ }
+
+ *ret = client_.GetChannel()->SendWithChannelHandle(
+ state_, opcode, send_vector, send_count, receive_vector, receive_count);
+
+ CheckDisconnect(*ret);
+}
+
+FileReference Transaction::PushFileHandle(const LocalHandle& handle) {
+ return client_.CheckReconnect() && EnsureStateAllocated()
+ ? client_.GetChannel()->PushFileHandle(state_, handle)
+ : -1;
+}
+
+FileReference Transaction::PushFileHandle(const BorrowedHandle& handle) {
+ return client_.CheckReconnect() && EnsureStateAllocated()
+ ? client_.GetChannel()->PushFileHandle(state_, handle)
+ : -1;
+}
+
+FileReference Transaction::PushFileHandle(const RemoteHandle& handle) {
+ return handle.Get();
+}
+
+ChannelReference Transaction::PushChannelHandle(
+ const LocalChannelHandle& handle) {
+ return client_.CheckReconnect() && EnsureStateAllocated()
+ ? client_.GetChannel()->PushChannelHandle(state_, handle)
+ : -1;
+}
+
+ChannelReference Transaction::PushChannelHandle(
+ const BorrowedChannelHandle& handle) {
+ return client_.CheckReconnect() && EnsureStateAllocated()
+ ? client_.GetChannel()->PushChannelHandle(state_, handle)
+ : -1;
+}
+
+ChannelReference Transaction::PushChannelHandle(
+ const RemoteChannelHandle& handle) {
+ return handle.value();
+}
+
+bool Transaction::GetFileHandle(FileReference ref, LocalHandle* handle) {
+ return client_.CheckReconnect() && EnsureStateAllocated() &&
+ client_.GetChannel()->GetFileHandle(state_, ref, handle);
+}
+
+bool Transaction::GetChannelHandle(ChannelReference ref,
+ LocalChannelHandle* handle) {
+ return client_.CheckReconnect() && EnsureStateAllocated() &&
+ client_.GetChannel()->GetChannelHandle(state_, ref, handle);
+}
+
+void Transaction::CheckDisconnect(int error) {
+ if (client_.NeedToDisconnectChannel(error)) {
+ if (state_allocated_) {
+ if (client_.GetChannel())
+ client_.GetChannel()->FreeTransactionState(state_);
+ state_ = nullptr;
+ state_allocated_ = false;
+ }
+ client_.Close(error);
+ }
+}
+
+} // namespace pdx
+} // namespace android
diff --git a/libs/vr/libpdx/client_tests.cpp b/libs/vr/libpdx/client_tests.cpp
new file mode 100644
index 0000000..f1fb6d1
--- /dev/null
+++ b/libs/vr/libpdx/client_tests.cpp
@@ -0,0 +1,566 @@
+#include <pdx/client.h>
+
+#include <gmock/gmock.h>
+#include <sys/eventfd.h>
+
+#include <pdx/mock_client_channel.h>
+#include <pdx/mock_client_channel_factory.h>
+#include <pdx/rpc/remote_method.h>
+
+using android::pdx::BorrowedChannelHandle;
+using android::pdx::BorrowedHandle;
+using android::pdx::ClientBase;
+using android::pdx::ClientChannel;
+using android::pdx::ClientChannelFactory;
+using android::pdx::ErrorStatus;
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::MockClientChannel;
+using android::pdx::MockClientChannelFactory;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::RemoteHandle;
+using android::pdx::Status;
+using android::pdx::Transaction;
+using android::pdx::rpc::Void;
+
+using testing::A;
+using testing::AnyNumber;
+using testing::ByMove;
+using testing::Invoke;
+using testing::Ne;
+using testing::Return;
+using testing::_;
+
+namespace {
+
+inline void* IntToPtr(intptr_t addr) { return reinterpret_cast<void*>(addr); }
+inline const void* IntToConstPtr(intptr_t addr) {
+ return reinterpret_cast<const void*>(addr);
+}
+
+struct TestInterface final {
+ // Op codes.
+ enum {
+ kOpAdd = 0,
+ kOpSendFile,
+ kOpGetFile,
+ kOpPushChannel,
+ };
+
+ // Methods.
+ PDX_REMOTE_METHOD(Add, kOpAdd, int(int, int));
+ PDX_REMOTE_METHOD(SendFile, kOpSendFile, void(const LocalHandle& fd));
+ PDX_REMOTE_METHOD(GetFile, kOpGetFile, LocalHandle(const std::string&, int));
+ PDX_REMOTE_METHOD(PushChannel, kOpPushChannel, LocalChannelHandle(Void));
+
+ PDX_REMOTE_API(API, Add, SendFile, GetFile, PushChannel);
+};
+
+class SimpleClient : public ClientBase<SimpleClient> {
+ public:
+ explicit SimpleClient(std::unique_ptr<ClientChannel> channel)
+ : BASE{std::move(channel)} {}
+ SimpleClient(std::unique_ptr<ClientChannelFactory> channel_factory,
+ int64_t timeout_ms)
+ : BASE{std::move(channel_factory), timeout_ms} {
+ EnableAutoReconnect(timeout_ms);
+ }
+
+ using BASE::SendImpulse;
+ using BASE::InvokeRemoteMethod;
+ using BASE::InvokeRemoteMethodInPlace;
+ using BASE::Close;
+ using BASE::IsConnected;
+ using BASE::EnableAutoReconnect;
+ using BASE::DisableAutoReconnect;
+ using BASE::event_fd;
+ using BASE::GetChannel;
+
+ MOCK_METHOD0(OnConnect, void());
+};
+
+class FailingClient : public ClientBase<FailingClient> {
+ public:
+ explicit FailingClient(std::unique_ptr<ClientChannel> channel, int error_code)
+ : BASE{std::move(channel)} {
+ Close(error_code);
+ }
+};
+
+class ClientChannelTest : public testing::Test {
+ public:
+ ClientChannelTest()
+ : client_{SimpleClient::Create(
+ std::make_unique<testing::StrictMock<MockClientChannel>>())} {}
+
+ MockClientChannel* mock_channel() {
+ return static_cast<MockClientChannel*>(client_->GetChannel());
+ }
+
+ std::unique_ptr<SimpleClient> client_;
+};
+
+class ClientChannelFactoryTest : public testing::Test {
+ public:
+ ClientChannelFactoryTest() {
+ auto factory =
+ std::make_unique<testing::NiceMock<MockClientChannelFactory>>();
+ ON_CALL(*factory, Connect(kTimeout))
+ .WillByDefault(Invoke(this, &ClientChannelFactoryTest::OnConnect));
+ client_ = SimpleClient::Create(std::move(factory), kTimeout);
+ }
+
+ MockClientChannel* mock_channel() {
+ return static_cast<MockClientChannel*>(client_->GetChannel());
+ }
+
+ Status<std::unique_ptr<ClientChannel>> OnConnect(int64_t /*timeout_ms*/) {
+ if (on_connect_error_)
+ return ErrorStatus(on_connect_error_);
+ std::unique_ptr<MockClientChannel> channel =
+ std::make_unique<testing::StrictMock<MockClientChannel>>();
+ if (on_connect_callback_)
+ on_connect_callback_(channel.get());
+ return Status<std::unique_ptr<ClientChannel>>{std::move(channel)};
+ }
+
+ void OnConnectCallback(std::function<void(MockClientChannel*)> callback) {
+ on_connect_callback_ = callback;
+ }
+ void SetOnConnectError(int error) { on_connect_error_ = error; }
+ void ResetOnConnectError() { on_connect_error_ = 0; }
+
+ constexpr static int64_t kTimeout = 123;
+ std::unique_ptr<SimpleClient> client_;
+ std::function<void(MockClientChannel*)> on_connect_callback_;
+ int on_connect_error_{0};
+};
+
+constexpr int64_t ClientChannelFactoryTest::kTimeout;
+
+class ClientTransactionTest : public ClientChannelTest {
+ public:
+ ClientTransactionTest() : transaction_{*client_} {}
+
+ Transaction transaction_;
+};
+
+} // anonymous namespace
+
+TEST_F(ClientChannelTest, IsInitialized) {
+ ASSERT_NE(client_.get(), nullptr);
+ EXPECT_TRUE(client_->IsInitialized());
+ EXPECT_TRUE(client_->IsConnected());
+}
+
+TEST_F(ClientChannelTest, CloseOnConstruction) {
+ FailingClient failed_client1{std::make_unique<MockClientChannel>(), EACCES};
+ ASSERT_FALSE(failed_client1.IsInitialized());
+ EXPECT_EQ(-EACCES, failed_client1.error());
+
+ FailingClient failed_client2{std::make_unique<MockClientChannel>(), -EACCES};
+ ASSERT_FALSE(failed_client2.IsInitialized());
+ EXPECT_EQ(-EACCES, failed_client2.error());
+
+ auto failed_client3 =
+ FailingClient::Create(std::make_unique<MockClientChannel>(), EIO);
+ ASSERT_EQ(failed_client3.get(), nullptr);
+}
+
+TEST_F(ClientChannelTest, IsConnected) {
+ EXPECT_TRUE(client_->IsConnected());
+ EXPECT_EQ(0, client_->error());
+ client_->Close(-EINVAL);
+ EXPECT_FALSE(client_->IsConnected());
+ EXPECT_EQ(-EINVAL, client_->error());
+}
+
+TEST_F(ClientChannelTest, event_fd) {
+ EXPECT_CALL(*mock_channel(), event_fd()).WillOnce(Return(12));
+ EXPECT_EQ(12, client_->event_fd());
+}
+
+TEST_F(ClientChannelTest, SendImpulse) {
+ EXPECT_CALL(*mock_channel(), SendImpulse(123, nullptr, 0))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_TRUE(client_->SendImpulse(123));
+
+ EXPECT_CALL(*mock_channel(), SendImpulse(17, nullptr, 0))
+ .WillOnce(Return(ErrorStatus{EIO}));
+ auto status = client_->SendImpulse(17);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EIO, status.error());
+
+ const void* const kTestPtr = IntToConstPtr(1234);
+ EXPECT_CALL(*mock_channel(), SendImpulse(1, kTestPtr, 17))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_TRUE(client_->SendImpulse(1, kTestPtr, 17));
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodNullTransactionState) {
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(nullptr));
+ EXPECT_CALL(*mock_channel(),
+ SendWithInt(nullptr, TestInterface::kOpAdd, _, _, nullptr, 0))
+ .WillOnce(Return(9));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr));
+ EXPECT_TRUE(client_->InvokeRemoteMethod<TestInterface::Add>(4, 5));
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodAddSuccess) {
+ void* const kTransactionState = IntToPtr(123);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(
+ *mock_channel(),
+ SendWithInt(kTransactionState, TestInterface::kOpAdd, _, _, nullptr, 0))
+ .WillOnce(Return(3));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ Status<int> status = client_->InvokeRemoteMethod<TestInterface::Add>(1, 2);
+ ASSERT_TRUE(status);
+ EXPECT_EQ(3, status.get());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodAddFailure) {
+ void* const kTransactionState = IntToPtr(123);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(
+ *mock_channel(),
+ SendWithInt(kTransactionState, TestInterface::kOpAdd, _, _, nullptr, 0))
+ .WillOnce(Return(ErrorStatus{EIO}));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ Status<int> status = client_->InvokeRemoteMethod<TestInterface::Add>(1, 2);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EIO, status.error());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodGetFileSuccess) {
+ void* const kTransactionState = IntToPtr(123);
+ int fd = eventfd(0, 0);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(*mock_channel(),
+ SendWithFileHandle(kTransactionState, TestInterface::kOpGetFile,
+ _, _, nullptr, 0))
+ .WillOnce(Return(ByMove(LocalHandle{fd})));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ Status<LocalHandle> status =
+ client_->InvokeRemoteMethod<TestInterface::GetFile>();
+ ASSERT_TRUE(status);
+ EXPECT_EQ(fd, status.get().Get());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodGetFileFailure) {
+ void* const kTransactionState = IntToPtr(123);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(*mock_channel(),
+ SendWithFileHandle(kTransactionState, TestInterface::kOpGetFile,
+ _, _, nullptr, 0))
+ .WillOnce(Return(ByMove(ErrorStatus{EACCES})));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ Status<LocalHandle> status =
+ client_->InvokeRemoteMethod<TestInterface::GetFile>("file", 0);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EACCES, status.error());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodPushChannelSuccess) {
+ void* const kTransactionState = IntToPtr(123);
+ const int32_t kHandleValue = 17;
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(
+ *mock_channel(),
+ SendWithChannelHandle(kTransactionState, TestInterface::kOpPushChannel, _,
+ _, nullptr, 0))
+ .WillOnce(Return(ByMove(LocalChannelHandle{nullptr, kHandleValue})));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ Status<LocalChannelHandle> status =
+ client_->InvokeRemoteMethod<TestInterface::PushChannel>();
+ ASSERT_TRUE(status);
+ EXPECT_EQ(kHandleValue, status.get().value());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodPushChannelFailure) {
+ void* const kTransactionState = IntToPtr(123);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(
+ *mock_channel(),
+ SendWithChannelHandle(kTransactionState, TestInterface::kOpPushChannel, _,
+ _, nullptr, 0))
+ .WillOnce(Return(ByMove(ErrorStatus{EACCES})));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ Status<LocalChannelHandle> status =
+ client_->InvokeRemoteMethod<TestInterface::PushChannel>();
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EACCES, status.error());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodSendFileSuccess) {
+ void* const kTransactionState = IntToPtr(123);
+ LocalHandle fd{eventfd(0, 0)};
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(*mock_channel(),
+ PushFileHandle(kTransactionState, A<const LocalHandle&>()))
+ .WillOnce(Return(1));
+ EXPECT_CALL(*mock_channel(),
+ SendWithInt(kTransactionState, TestInterface::kOpSendFile, _, _,
+ nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ EXPECT_TRUE(client_->InvokeRemoteMethod<TestInterface::SendFile>(fd));
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodSendFileFailure) {
+ void* const kTransactionState = IntToPtr(123);
+ LocalHandle fd{eventfd(0, 0)};
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(*mock_channel(),
+ PushFileHandle(kTransactionState, A<const LocalHandle&>()))
+ .WillOnce(Return(1));
+ EXPECT_CALL(*mock_channel(),
+ SendWithInt(kTransactionState, TestInterface::kOpSendFile, _, _,
+ nullptr, 0))
+ .WillOnce(Return(ErrorStatus{EACCES}));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ EXPECT_FALSE(client_->InvokeRemoteMethod<TestInterface::SendFile>(fd));
+}
+
+TEST_F(ClientChannelFactoryTest, IsInitialized) {
+ ASSERT_NE(client_.get(), nullptr);
+ EXPECT_TRUE(client_->IsInitialized());
+ EXPECT_TRUE(client_->IsConnected());
+}
+
+TEST_F(ClientChannelFactoryTest, NotConnectedButInitialized) {
+ auto factory =
+ std::make_unique<testing::NiceMock<MockClientChannelFactory>>();
+ EXPECT_CALL(*factory, Connect(kTimeout))
+ .WillOnce(Return(ByMove(ErrorStatus(ESHUTDOWN))))
+ .WillOnce(Invoke(this, &ClientChannelFactoryTest::OnConnect));
+ client_ = SimpleClient::Create(std::move(factory), kTimeout);
+ ASSERT_NE(client_.get(), nullptr);
+ EXPECT_TRUE(client_->IsInitialized());
+ EXPECT_FALSE(client_->IsConnected());
+ client_->DisableAutoReconnect();
+ ASSERT_FALSE(client_->SendImpulse(17));
+ EXPECT_FALSE(client_->IsConnected());
+ client_->EnableAutoReconnect(kTimeout);
+ EXPECT_CALL(*client_, OnConnect());
+ OnConnectCallback([](auto* mock) {
+ EXPECT_CALL(*mock, SendImpulse(17, nullptr, 0))
+ .WillOnce(Return(Status<void>{}));
+ });
+ ASSERT_TRUE(client_->SendImpulse(17));
+ EXPECT_TRUE(client_->IsConnected());
+}
+
+TEST_F(ClientChannelFactoryTest, CheckDisconnect) {
+ EXPECT_CALL(*mock_channel(), SendImpulse(17, nullptr, 0))
+ .WillOnce(Return(ErrorStatus{ESHUTDOWN}));
+ ASSERT_FALSE(client_->SendImpulse(17));
+ EXPECT_FALSE(client_->IsConnected());
+ EXPECT_EQ(-ESHUTDOWN, client_->error());
+}
+
+TEST_F(ClientChannelFactoryTest, CheckReconnect) {
+ client_->Close(ESHUTDOWN);
+ ASSERT_FALSE(client_->IsConnected());
+
+ EXPECT_CALL(*client_, OnConnect());
+ OnConnectCallback([](auto* mock) {
+ EXPECT_CALL(*mock, SendImpulse(17, nullptr, 0))
+ .WillOnce(Return(Status<void>{}));
+ });
+ ASSERT_TRUE(client_->SendImpulse(17));
+ EXPECT_TRUE(client_->IsConnected());
+}
+
+TEST_F(ClientChannelFactoryTest, CloseOnConnect) {
+ client_->Close(ESHUTDOWN);
+
+ EXPECT_CALL(*client_, OnConnect()).WillOnce(Invoke([this] {
+ client_->Close(EIO);
+ }));
+ auto status = client_->SendImpulse(17);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EIO, status.error());
+ EXPECT_FALSE(client_->IsConnected());
+ EXPECT_EQ(-EIO, client_->error());
+}
+
+TEST_F(ClientChannelFactoryTest, DisableAutoReconnect) {
+ client_->Close(EIO);
+ ASSERT_FALSE(client_->IsConnected());
+ client_->DisableAutoReconnect();
+ auto status = client_->SendImpulse(17);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(ESHUTDOWN, status.error());
+ EXPECT_FALSE(client_->IsConnected());
+ client_->EnableAutoReconnect(kTimeout);
+ EXPECT_CALL(*client_, OnConnect());
+ OnConnectCallback([](auto* mock) {
+ EXPECT_CALL(*mock, SendImpulse(17, nullptr, 0))
+ .WillOnce(Return(Status<void>{}));
+ });
+ ASSERT_TRUE(client_->SendImpulse(17));
+ EXPECT_TRUE(client_->IsConnected());
+}
+
+TEST_F(ClientTransactionTest, SendNoData) {
+ void* const kTransactionState = IntToPtr(123);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ EXPECT_CALL(*mock_channel(),
+ SendWithInt(kTransactionState, 1, nullptr, 0, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.Send<void>(1));
+ EXPECT_CALL(*mock_channel(),
+ SendWithFileHandle(kTransactionState, 2, nullptr, 0, nullptr, 0))
+ .WillOnce(Return(ByMove(LocalHandle{-1})));
+ EXPECT_TRUE(transaction_.Send<LocalHandle>(2));
+ EXPECT_CALL(*mock_channel(), SendWithChannelHandle(kTransactionState, 3,
+ nullptr, 0, nullptr, 0))
+ .WillOnce(Return(ByMove(LocalChannelHandle{nullptr, 1})));
+ EXPECT_TRUE(transaction_.Send<LocalChannelHandle>(3));
+}
+
+TEST_F(ClientTransactionTest, SendNoState) {
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(nullptr));
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 1, nullptr, 0, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr));
+ EXPECT_TRUE(transaction_.Send<void>(1));
+}
+
+TEST_F(ClientTransactionTest, SendBuffers) {
+ const void* const kSendBuffer = IntToConstPtr(123);
+ const size_t kSendSize = 12;
+ void* const kReceiveBuffer = IntToPtr(456);
+ const size_t kReceiveSize = 34;
+
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(nullptr));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 1, nullptr, 0, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.Send<void>(1, nullptr, 0, nullptr, 0));
+
+ EXPECT_CALL(*mock_channel(),
+ SendWithInt(nullptr, 2, Ne(nullptr), 1, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.Send<void>(2, kSendBuffer, kSendSize, nullptr, 0));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 3, nullptr, 0, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.Send<void>(3, kSendBuffer, 0, nullptr, 0));
+
+ EXPECT_CALL(*mock_channel(),
+ SendWithInt(nullptr, 4, nullptr, 0, Ne(nullptr), 1))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(
+ transaction_.Send<void>(4, nullptr, 0, kReceiveBuffer, kReceiveSize));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 5, nullptr, 0, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.Send<void>(5, nullptr, 0, kReceiveBuffer, 0));
+
+ EXPECT_CALL(*mock_channel(),
+ SendWithInt(nullptr, 5, Ne(nullptr), 1, Ne(nullptr), 1))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.Send<void>(5, kSendBuffer, kSendSize, kReceiveBuffer,
+ kReceiveSize));
+}
+
+TEST_F(ClientTransactionTest, SendVector) {
+ iovec send[3] = {};
+ iovec recv[4] = {};
+
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(nullptr));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 1, nullptr, 0, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.SendVector<void>(1, nullptr, 0, nullptr, 0));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 2, send, 3, recv, 4))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.SendVector<void>(2, send, 3, recv, 4));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 3, send, 3, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.SendVector<void>(3, send, nullptr));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 4, nullptr, 0, recv, 4))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.SendVector<void>(4, nullptr, recv));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 5, send, 3, recv, 4))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.SendVector<void>(5, send, recv));
+}
+
+TEST_F(ClientTransactionTest, PushHandle) {
+ void* const kTransactionState = IntToPtr(123);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+
+ EXPECT_CALL(*mock_channel(),
+ PushFileHandle(kTransactionState, A<const LocalHandle&>()))
+ .WillOnce(Return(1));
+ EXPECT_EQ(1, transaction_.PushFileHandle(LocalHandle{-1}));
+
+ EXPECT_CALL(*mock_channel(),
+ PushFileHandle(kTransactionState, A<const BorrowedHandle&>()))
+ .WillOnce(Return(2));
+ EXPECT_EQ(2, transaction_.PushFileHandle(BorrowedHandle{-1}));
+
+ EXPECT_EQ(3, transaction_.PushFileHandle(RemoteHandle{3}));
+
+ EXPECT_CALL(
+ *mock_channel(),
+ PushChannelHandle(kTransactionState, A<const LocalChannelHandle&>()))
+ .WillOnce(Return(11));
+ EXPECT_EQ(11, transaction_.PushChannelHandle(LocalChannelHandle{nullptr, 1}));
+
+ EXPECT_CALL(
+ *mock_channel(),
+ PushChannelHandle(kTransactionState, A<const BorrowedChannelHandle&>()))
+ .WillOnce(Return(12));
+ EXPECT_EQ(12, transaction_.PushChannelHandle(BorrowedChannelHandle{2}));
+
+ EXPECT_EQ(13, transaction_.PushChannelHandle(RemoteChannelHandle{13}));
+}
+
+TEST_F(ClientTransactionTest, GetHandle) {
+ void* const kTransactionState = IntToPtr(123);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+
+ EXPECT_CALL(*mock_channel(), GetFileHandle(kTransactionState, 1, _))
+ .WillOnce(Return(false))
+ .WillOnce(Return(true));
+
+ LocalHandle file_handle;
+ EXPECT_FALSE(transaction_.GetFileHandle(1, &file_handle));
+ EXPECT_TRUE(transaction_.GetFileHandle(1, &file_handle));
+
+ EXPECT_CALL(*mock_channel(), GetChannelHandle(kTransactionState, 2, _))
+ .WillOnce(Return(false))
+ .WillOnce(Return(true));
+
+ LocalChannelHandle channel_handle;
+ EXPECT_FALSE(transaction_.GetChannelHandle(2, &channel_handle));
+ EXPECT_TRUE(transaction_.GetChannelHandle(2, &channel_handle));
+}
diff --git a/libs/vr/libpdx/encoder_performance_test.cpp b/libs/vr/libpdx/encoder_performance_test.cpp
new file mode 100644
index 0000000..b7d94b3
--- /dev/null
+++ b/libs/vr/libpdx/encoder_performance_test.cpp
@@ -0,0 +1,515 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <iomanip>
+#include <iostream>
+#include <vector>
+
+#include <pdx/rpc/argument_encoder.h>
+#include <pdx/rpc/message_buffer.h>
+#include <pdx/rpc/payload.h>
+#include <pdx/utility.h>
+
+using namespace android::pdx::rpc;
+using namespace android::pdx;
+using std::placeholders::_1;
+using std::placeholders::_2;
+using std::placeholders::_3;
+using std::placeholders::_4;
+using std::placeholders::_5;
+using std::placeholders::_6;
+
+namespace {
+
+constexpr size_t kMaxStaticBufferSize = 20480;
+
+// Provide numpunct facet that formats numbers with ',' as thousands separators.
+class CommaNumPunct : public std::numpunct<char> {
+ protected:
+ char do_thousands_sep() const override { return ','; }
+ std::string do_grouping() const override { return "\03"; }
+};
+
+class TestPayload : public MessagePayload<SendBuffer>,
+ public MessageWriter,
+ public MessageReader,
+ public NoOpResourceMapper {
+ public:
+ // MessageWriter
+ void* GetNextWriteBufferSection(size_t size) override {
+ const size_t section_offset = Size();
+ Extend(size);
+ return Data() + section_offset;
+ }
+
+ OutputResourceMapper* GetOutputResourceMapper() override { return this; }
+
+ // MessageReader
+ BufferSection GetNextReadBufferSection() override {
+ return {&*ConstCursor(), &*ConstEnd()};
+ }
+
+ void ConsumeReadBufferSectionData(const void* new_start) override {
+ std::advance(ConstCursor(), PointerDistance(new_start, &*ConstCursor()));
+ }
+
+ InputResourceMapper* GetInputResourceMapper() override { return this; }
+};
+
+class StaticBuffer : public MessageWriter,
+ public MessageReader,
+ public NoOpResourceMapper {
+ public:
+ void Clear() {
+ read_ptr_ = buffer_;
+ write_ptr_ = 0;
+ }
+ void Rewind() { read_ptr_ = buffer_; }
+
+ // MessageWriter
+ void* GetNextWriteBufferSection(size_t size) override {
+ void* ptr = buffer_ + write_ptr_;
+ write_ptr_ += size;
+ return ptr;
+ }
+
+ OutputResourceMapper* GetOutputResourceMapper() override { return this; }
+
+ // MessageReader
+ BufferSection GetNextReadBufferSection() override {
+ return {read_ptr_, std::end(buffer_)};
+ }
+
+ void ConsumeReadBufferSectionData(const void* new_start) override {
+ read_ptr_ = static_cast<const uint8_t*>(new_start);
+ }
+
+ InputResourceMapper* GetInputResourceMapper() override { return this; }
+
+ private:
+ uint8_t buffer_[kMaxStaticBufferSize];
+ const uint8_t* read_ptr_{buffer_};
+ size_t write_ptr_{0};
+};
+
+// Simple callback function to clear/reset the input/output buffers for
+// serialization. Using raw function pointer here instead of std::function to
+// minimize the overhead of invocation in the tight test loop over millions of
+// iterations.
+using ResetFunc = void(void*);
+
+// Serialization test function signature, used by the TestRunner.
+using SerializeTestSignature = std::chrono::nanoseconds(MessageWriter* writer,
+ size_t iterations,
+ ResetFunc* write_reset,
+ void* reset_data);
+
+// Deserialization test function signature, used by the TestRunner.
+using DeserializeTestSignature = std::chrono::nanoseconds(
+ MessageReader* reader, MessageWriter* writer, size_t iterations,
+ ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data);
+
+// Generic serialization test runner method. Takes the |value| of type T and
+// serializes it into the output buffer represented by |writer|.
+template <typename T>
+std::chrono::nanoseconds SerializeTestRunner(MessageWriter* writer,
+ size_t iterations,
+ ResetFunc* write_reset,
+ void* reset_data, const T& value) {
+ auto start = std::chrono::high_resolution_clock::now();
+ for (size_t i = 0; i < iterations; i++) {
+ write_reset(reset_data);
+ Serialize(value, writer);
+ }
+ auto stop = std::chrono::high_resolution_clock::now();
+ return stop - start;
+}
+
+// Generic deserialization test runner method. Takes the |value| of type T and
+// temporarily serializes it into the output buffer, then repeatedly
+// deserializes the data back from that buffer.
+template <typename T>
+std::chrono::nanoseconds DeserializeTestRunner(
+ MessageReader* reader, MessageWriter* writer, size_t iterations,
+ ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data,
+ const T& value) {
+ write_reset(reset_data);
+ Serialize(value, writer);
+ T output_data;
+ auto start = std::chrono::high_resolution_clock::now();
+ for (size_t i = 0; i < iterations; i++) {
+ read_reset(reset_data);
+ Deserialize(&output_data, reader);
+ }
+ auto stop = std::chrono::high_resolution_clock::now();
+ if (output_data != value)
+ return start - stop; // Return negative value to indicate error.
+ return stop - start;
+}
+
+// Special version of SerializeTestRunner that doesn't perform any serialization
+// but does all the same setup steps and moves data of size |data_size| into
+// the output buffer. Useful to determine the baseline to calculate time used
+// just for serialization layer.
+std::chrono::nanoseconds SerializeBaseTest(MessageWriter* writer,
+ size_t iterations,
+ ResetFunc* write_reset,
+ void* reset_data, size_t data_size) {
+ std::vector<uint8_t> dummy_data(data_size);
+ auto start = std::chrono::high_resolution_clock::now();
+ for (size_t i = 0; i < iterations; i++) {
+ write_reset(reset_data);
+ memcpy(writer->GetNextWriteBufferSection(dummy_data.size()),
+ dummy_data.data(), dummy_data.size());
+ }
+ auto stop = std::chrono::high_resolution_clock::now();
+ return stop - start;
+}
+
+// Special version of DeserializeTestRunner that doesn't perform any
+// deserialization but invokes Rewind on the input buffer repeatedly.
+// Useful to determine the baseline to calculate time used just for
+// deserialization layer.
+std::chrono::nanoseconds DeserializeBaseTest(
+ MessageReader* reader, MessageWriter* writer, size_t iterations,
+ ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data,
+ size_t data_size) {
+ std::vector<uint8_t> dummy_data(data_size);
+ write_reset(reset_data);
+ memcpy(writer->GetNextWriteBufferSection(dummy_data.size()),
+ dummy_data.data(), dummy_data.size());
+ auto start = std::chrono::high_resolution_clock::now();
+ for (size_t i = 0; i < iterations; i++) {
+ read_reset(reset_data);
+ auto section = reader->GetNextReadBufferSection();
+ memcpy(dummy_data.data(), section.first, dummy_data.size());
+ reader->ConsumeReadBufferSectionData(
+ AdvancePointer(section.first, dummy_data.size()));
+ }
+ auto stop = std::chrono::high_resolution_clock::now();
+ return stop - start;
+}
+
+// The main class that accumulates individual tests to be executed.
+class TestRunner {
+ public:
+ struct BufferInfo {
+ BufferInfo(const std::string& buffer_name, MessageReader* reader,
+ MessageWriter* writer, ResetFunc* read_reset_func,
+ ResetFunc* write_reset_func, void* reset_data)
+ : name{buffer_name},
+ reader{reader},
+ writer{writer},
+ read_reset_func{read_reset_func},
+ write_reset_func{write_reset_func},
+ reset_data{reset_data} {}
+ std::string name;
+ MessageReader* reader;
+ MessageWriter* writer;
+ ResetFunc* read_reset_func;
+ ResetFunc* write_reset_func;
+ void* reset_data;
+ };
+
+ void AddTestFunc(const std::string& name,
+ std::function<SerializeTestSignature> serialize_test,
+ std::function<DeserializeTestSignature> deserialize_test,
+ size_t data_size) {
+ tests_.emplace_back(name, std::move(serialize_test),
+ std::move(deserialize_test), data_size);
+ }
+
+ template <typename T>
+ void AddSerializationTest(const std::string& name, T&& value) {
+ const size_t data_size = GetSerializedSize(value);
+ auto serialize_test =
+ std::bind(static_cast<std::chrono::nanoseconds (*)(
+ MessageWriter*, size_t, ResetFunc*, void*, const T&)>(
+ &SerializeTestRunner),
+ _1, _2, _3, _4, std::forward<T>(value));
+ tests_.emplace_back(name, std::move(serialize_test),
+ std::function<DeserializeTestSignature>{}, data_size);
+ }
+
+ template <typename T>
+ void AddDeserializationTest(const std::string& name, T&& value) {
+ const size_t data_size = GetSerializedSize(value);
+ auto deserialize_test =
+ std::bind(static_cast<std::chrono::nanoseconds (*)(
+ MessageReader*, MessageWriter*, size_t, ResetFunc*,
+ ResetFunc*, void*, const T&)>(&DeserializeTestRunner),
+ _1, _2, _3, _4, _5, _6, std::forward<T>(value));
+ tests_.emplace_back(name, std::function<SerializeTestSignature>{},
+ std::move(deserialize_test), data_size);
+ }
+
+ template <typename T>
+ void AddTest(const std::string& name, T&& value) {
+ const size_t data_size = GetSerializedSize(value);
+ if (data_size > kMaxStaticBufferSize) {
+ std::cerr << "Test '" << name << "' requires " << data_size
+ << " bytes in the serialization buffer but only "
+ << kMaxStaticBufferSize << " are available." << std::endl;
+ exit(1);
+ }
+ auto serialize_test =
+ std::bind(static_cast<std::chrono::nanoseconds (*)(
+ MessageWriter*, size_t, ResetFunc*, void*, const T&)>(
+ &SerializeTestRunner),
+ _1, _2, _3, _4, value);
+ auto deserialize_test =
+ std::bind(static_cast<std::chrono::nanoseconds (*)(
+ MessageReader*, MessageWriter*, size_t, ResetFunc*,
+ ResetFunc*, void*, const T&)>(&DeserializeTestRunner),
+ _1, _2, _3, _4, _5, _6, std::forward<T>(value));
+ tests_.emplace_back(name, std::move(serialize_test),
+ std::move(deserialize_test), data_size);
+ }
+
+ std::string CenterString(std::string text, size_t column_width) {
+ if (text.size() < column_width) {
+ text = std::string((column_width - text.size()) / 2, ' ') + text;
+ }
+ return text;
+ }
+
+ void RunTests(size_t iteration_count,
+ const std::vector<BufferInfo>& buffers) {
+ using float_seconds = std::chrono::duration<double>;
+ const std::string name_column_separator = " : ";
+ const std::string buffer_column_separator = " || ";
+ const std::string buffer_timing_column_separator = " | ";
+ const size_t data_size_column_width = 6;
+ const size_t time_column_width = 9;
+ const size_t qps_column_width = 18;
+ const size_t buffer_column_width = time_column_width +
+ buffer_timing_column_separator.size() +
+ qps_column_width;
+
+ auto compare_name_length = [](const TestEntry& t1, const TestEntry& t2) {
+ return t1.name.size() < t2.name.size();
+ };
+ auto test_with_longest_name =
+ std::max_element(tests_.begin(), tests_.end(), compare_name_length);
+ size_t name_column_width = test_with_longest_name->name.size();
+
+ size_t total_width =
+ name_column_width + name_column_separator.size() +
+ data_size_column_width + buffer_column_separator.size() +
+ buffers.size() * (buffer_column_width + buffer_column_separator.size());
+
+ const std::string dbl_separator(total_width, '=');
+ const std::string separator(total_width, '-');
+
+ auto print_header = [&](const std::string& header) {
+ std::cout << dbl_separator << std::endl;
+ std::stringstream ss;
+ ss.imbue(std::locale(ss.getloc(), new CommaNumPunct));
+ ss << header << " (" << iteration_count << " iterations)";
+ std::cout << CenterString(ss.str(), total_width) << std::endl;
+ std::cout << dbl_separator << std::endl;
+ std::cout << std::setw(name_column_width) << "Test Name" << std::left
+ << name_column_separator << std::setw(data_size_column_width)
+ << CenterString("Size", data_size_column_width)
+ << buffer_column_separator;
+ for (const auto& buffer_info : buffers) {
+ std::cout << std::setw(buffer_column_width)
+ << CenterString(buffer_info.name, buffer_column_width)
+ << buffer_column_separator;
+ }
+ std::cout << std::endl;
+ std::cout << std::setw(name_column_width) << "" << name_column_separator
+ << std::setw(data_size_column_width)
+ << CenterString("bytes", data_size_column_width)
+ << buffer_column_separator << std::left;
+ for (size_t i = 0; i < buffers.size(); i++) {
+ std::cout << std::setw(time_column_width)
+ << CenterString("Time, s", time_column_width)
+ << buffer_timing_column_separator
+ << std::setw(qps_column_width)
+ << CenterString("QPS", qps_column_width)
+ << buffer_column_separator;
+ }
+ std::cout << std::right << std::endl;
+ std::cout << separator << std::endl;
+ };
+
+ print_header("Serialization benchmarks");
+ for (const auto& test : tests_) {
+ if (test.serialize_test) {
+ std::cout << std::setw(name_column_width) << test.name << " : "
+ << std::setw(data_size_column_width) << test.data_size
+ << buffer_column_separator;
+ for (const auto& buffer_info : buffers) {
+ auto seconds =
+ std::chrono::duration_cast<float_seconds>(test.serialize_test(
+ buffer_info.writer, iteration_count,
+ buffer_info.write_reset_func, buffer_info.reset_data));
+ double qps = iteration_count / seconds.count();
+ std::cout << std::fixed << std::setprecision(3)
+ << std::setw(time_column_width) << seconds.count()
+ << buffer_timing_column_separator
+ << std::setw(qps_column_width) << qps
+ << buffer_column_separator;
+ }
+ std::cout << std::endl;
+ }
+ }
+
+ print_header("Deserialization benchmarks");
+ for (const auto& test : tests_) {
+ if (test.deserialize_test) {
+ std::cout << std::setw(name_column_width) << test.name << " : "
+ << std::setw(data_size_column_width) << test.data_size
+ << buffer_column_separator;
+ for (const auto& buffer_info : buffers) {
+ auto seconds =
+ std::chrono::duration_cast<float_seconds>(test.deserialize_test(
+ buffer_info.reader, buffer_info.writer, iteration_count,
+ buffer_info.read_reset_func, buffer_info.write_reset_func,
+ buffer_info.reset_data));
+ double qps = iteration_count / seconds.count();
+ std::cout << std::fixed << std::setprecision(3)
+ << std::setw(time_column_width) << seconds.count()
+ << buffer_timing_column_separator
+ << std::setw(qps_column_width) << qps
+ << buffer_column_separator;
+ }
+ std::cout << std::endl;
+ }
+ }
+ std::cout << dbl_separator << std::endl;
+ }
+
+ private:
+ struct TestEntry {
+ TestEntry(const std::string& test_name,
+ std::function<SerializeTestSignature> serialize_test,
+ std::function<DeserializeTestSignature> deserialize_test,
+ size_t data_size)
+ : name{test_name},
+ serialize_test{std::move(serialize_test)},
+ deserialize_test{std::move(deserialize_test)},
+ data_size{data_size} {}
+ std::string name;
+ std::function<SerializeTestSignature> serialize_test;
+ std::function<DeserializeTestSignature> deserialize_test;
+ size_t data_size;
+ };
+
+ std::vector<TestEntry> tests_;
+};
+
+std::string GenerateContainerName(const std::string& type, size_t count) {
+ std::stringstream ss;
+ ss << type << "(" << count << ")";
+ return ss.str();
+}
+
+} // anonymous namespace
+
+int main(int /*argc*/, char** /*argv*/) {
+ const size_t iteration_count = 10000000; // 10M iterations.
+ TestRunner test_runner;
+ std::cout.imbue(std::locale(std::cout.getloc(), new CommaNumPunct));
+
+ // Baseline tests to figure out the overhead of buffer resizing and data
+ // transfers.
+ for (size_t len : {0, 1, 9, 66, 259}) {
+ auto serialize_base_test =
+ std::bind(&SerializeBaseTest, _1, _2, _3, _4, len);
+ auto deserialize_base_test =
+ std::bind(&DeserializeBaseTest, _1, _2, _3, _4, _5, _6, len);
+ test_runner.AddTestFunc("--Baseline--", std::move(serialize_base_test),
+ std::move(deserialize_base_test), len);
+ }
+
+ // Individual serialization/deserialization tests.
+ test_runner.AddTest("bool", true);
+ test_runner.AddTest("int32_t", 12);
+
+ for (size_t len : {0, 1, 8, 64, 256}) {
+ test_runner.AddTest(GenerateContainerName("string", len),
+ std::string(len, '*'));
+ }
+ // Serialization is too slow to handle such large strings, add this test for
+ // deserialization only.
+ test_runner.AddDeserializationTest(GenerateContainerName("string", 10240),
+ std::string(10240, '*'));
+
+ for (size_t len : {0, 1, 8, 64, 256}) {
+ std::vector<int32_t> int_vector(len);
+ std::iota(int_vector.begin(), int_vector.end(), 0);
+ test_runner.AddTest(GenerateContainerName("vector<int32_t>", len),
+ std::move(int_vector));
+ }
+
+ std::vector<std::string> vector_of_strings = {
+ "012345678901234567890123456789", "012345678901234567890123456789",
+ "012345678901234567890123456789", "012345678901234567890123456789",
+ "012345678901234567890123456789",
+ };
+ test_runner.AddTest(
+ GenerateContainerName("vector<string>", vector_of_strings.size()),
+ std::move(vector_of_strings));
+
+ test_runner.AddTest("tuple<int, bool, string, double>",
+ std::make_tuple(123, true, std::string{"foobar"}, 1.1));
+
+ for (size_t len : {0, 1, 8, 64}) {
+ std::map<int, std::string> test_map;
+ for (size_t i = 0; i < len; i++)
+ test_map.emplace(i, std::to_string(i));
+ test_runner.AddTest(GenerateContainerName("map<int, string>", len),
+ std::move(test_map));
+ }
+
+ for (size_t len : {0, 1, 8, 64}) {
+ std::unordered_map<int, std::string> test_map;
+ for (size_t i = 0; i < len; i++)
+ test_map.emplace(i, std::to_string(i));
+ test_runner.AddTest(
+ GenerateContainerName("unordered_map<int, string>", len),
+ std::move(test_map));
+ }
+
+ // BufferWrapper can't be used with deserialization tests right now because
+ // it requires external buffer to be filled in, which is not available.
+ std::vector<std::vector<uint8_t>> data_buffers;
+ for (size_t len : {0, 1, 8, 64, 256}) {
+ data_buffers.emplace_back(len);
+ test_runner.AddSerializationTest(
+ GenerateContainerName("BufferWrapper<uint8_t*>", len),
+ BufferWrapper<uint8_t*>(data_buffers.back().data(),
+ data_buffers.back().size()));
+ }
+
+ // Various backing buffers to run the tests on.
+ std::vector<TestRunner::BufferInfo> buffers;
+
+ Payload buffer;
+ buffers.emplace_back("Non-TLS Buffer", &buffer, &buffer,
+ [](void* ptr) { static_cast<Payload*>(ptr)->Rewind(); },
+ [](void* ptr) { static_cast<Payload*>(ptr)->Clear(); },
+ &buffer);
+
+ TestPayload tls_buffer;
+ buffers.emplace_back(
+ "TLS Buffer", &tls_buffer, &tls_buffer,
+ [](void* ptr) { static_cast<TestPayload*>(ptr)->Rewind(); },
+ [](void* ptr) { static_cast<TestPayload*>(ptr)->Clear(); }, &tls_buffer);
+
+ StaticBuffer static_buffer;
+ buffers.emplace_back(
+ "Static Buffer", &static_buffer, &static_buffer,
+ [](void* ptr) { static_cast<StaticBuffer*>(ptr)->Rewind(); },
+ [](void* ptr) { static_cast<StaticBuffer*>(ptr)->Clear(); },
+ &static_buffer);
+
+ // Finally, run all the tests.
+ test_runner.RunTests(iteration_count, buffers);
+ return 0;
+}
diff --git a/libs/vr/libpdx/errno_guard.h b/libs/vr/libpdx/errno_guard.h
new file mode 100644
index 0000000..fc7dfdf
--- /dev/null
+++ b/libs/vr/libpdx/errno_guard.h
@@ -0,0 +1,34 @@
+#ifndef ANDROID_PDX_ERRNO_GUARD_H_
+#define ANDROID_PDX_ERRNO_GUARD_H_
+
+#include <errno.h>
+
+namespace android {
+namespace pdx {
+
+// Automatically saves and restores the system errno for API implementations to
+// prevent internal use errno from affecting API callers.
+class ErrnoGuard {
+ public:
+ ErrnoGuard() : saved_errno_(errno) {}
+ ~ErrnoGuard() { errno = saved_errno_; }
+
+ int saved_errno() const { return saved_errno_; }
+
+ private:
+ int saved_errno_;
+
+ ErrnoGuard(const ErrnoGuard&) = delete;
+ void operator=(const ErrnoGuard&) = delete;
+};
+
+// Checks |return_code| and returns either it or the negated system errno based
+// on the return code value.
+inline int ReturnCodeOrError(int return_code) {
+ return return_code < 0 ? -errno : return_code;
+}
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_ERRNO_GUARD_H_
diff --git a/libs/vr/libpdx/mock_tests.cpp b/libs/vr/libpdx/mock_tests.cpp
new file mode 100644
index 0000000..76fd154
--- /dev/null
+++ b/libs/vr/libpdx/mock_tests.cpp
@@ -0,0 +1,20 @@
+#include <gtest/gtest.h>
+#include <pdx/mock_client_channel.h>
+#include <pdx/mock_client_channel_factory.h>
+#include <pdx/mock_message_reader.h>
+#include <pdx/mock_message_writer.h>
+#include <pdx/mock_service_dispatcher.h>
+#include <pdx/mock_service_endpoint.h>
+
+TEST(MockTypes, Instantiation) {
+ // Make sure all our interfaces are mocked out properly and mock instances
+ // can be created.
+ android::pdx::MockClientChannel client_channel;
+ android::pdx::MockClientChannelFactory client_channel_factory;
+ android::pdx::MockInputResourceMapper input_resource_mapper;
+ android::pdx::MockMessageReader message_reader;
+ android::pdx::MockOutputResourceMapper output_resource_mapper;
+ android::pdx::MockMessageWriter message_writer;
+ android::pdx::MockServiceDispatcher service_dispatcher;
+ android::pdx::MockEndpoint endpoint;
+}
diff --git a/libs/vr/libpdx/private/pdx/channel_handle.h b/libs/vr/libpdx/private/pdx/channel_handle.h
new file mode 100644
index 0000000..1e62d25
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/channel_handle.h
@@ -0,0 +1,123 @@
+#ifndef ANDROID_PDX_CHANNEL_HANDLE_H_
+#define ANDROID_PDX_CHANNEL_HANDLE_H_
+
+#include <cstdint>
+#include <type_traits>
+
+namespace android {
+namespace pdx {
+
+enum class ChannelHandleMode {
+ Local,
+ Borrowed,
+ Remote,
+};
+
+class ChannelManagerInterface {
+ public:
+ virtual void CloseHandle(std::int32_t handle) = 0;
+
+ protected:
+ // Nobody should be allowed to delete the instance of channel manager
+ // through this interface.
+ virtual ~ChannelManagerInterface() = default;
+};
+
+class ChannelHandleBase {
+ public:
+ ChannelHandleBase() = default;
+ ChannelHandleBase(const int32_t& value) : value_{value} {}
+
+ ChannelHandleBase(const ChannelHandleBase&) = delete;
+ ChannelHandleBase& operator=(const ChannelHandleBase&) = delete;
+
+ std::int32_t value() const { return value_; }
+ bool valid() const { return value_ >= 0; }
+ explicit operator bool() const { return valid(); }
+
+ void Close() { value_ = kEmptyHandle; }
+
+ protected:
+ // Must not be used by itself. Must be derived from.
+ ~ChannelHandleBase() = default;
+ enum : std::int32_t { kEmptyHandle = -1 };
+
+ std::int32_t value_{kEmptyHandle};
+};
+
+template <ChannelHandleMode Mode>
+class ChannelHandle : public ChannelHandleBase {
+ public:
+ ChannelHandle() = default;
+ using ChannelHandleBase::ChannelHandleBase;
+ ChannelHandle(ChannelHandle&& other) : ChannelHandleBase{other.value_} {
+ other.value_ = kEmptyHandle;
+ }
+ ~ChannelHandle() = default;
+
+ ChannelHandle Duplicate() const { return ChannelHandle{value_}; }
+
+ ChannelHandle& operator=(ChannelHandle&& other) {
+ value_ = other.value_;
+ other.value_ = kEmptyHandle;
+ return *this;
+ }
+};
+
+template <>
+class ChannelHandle<ChannelHandleMode::Local> : public ChannelHandleBase {
+ public:
+ ChannelHandle() = default;
+ ChannelHandle(ChannelManagerInterface* manager, int32_t value)
+ : ChannelHandleBase{value}, manager_{manager} {}
+
+ ChannelHandle(const ChannelHandle&) = delete;
+ ChannelHandle& operator=(const ChannelHandle&) = delete;
+
+ ChannelHandle(ChannelHandle&& other)
+ : ChannelHandleBase{other.value_}, manager_{other.manager_} {
+ other.manager_ = nullptr;
+ other.value_ = kEmptyHandle;
+ }
+
+ ChannelHandle& operator=(ChannelHandle&& other) {
+ value_ = other.value_;
+ manager_ = other.manager_;
+ other.value_ = kEmptyHandle;
+ other.manager_ = nullptr;
+ return *this;
+ }
+
+ ~ChannelHandle() {
+ if (manager_)
+ manager_->CloseHandle(value_);
+ }
+
+ ChannelHandle<ChannelHandleMode::Borrowed> Borrow() const {
+ return ChannelHandle<ChannelHandleMode::Borrowed>{value_};
+ }
+
+ void Close() {
+ if (manager_)
+ manager_->CloseHandle(value_);
+ manager_ = nullptr;
+ value_ = kEmptyHandle;
+ }
+
+ private:
+ ChannelManagerInterface* manager_{nullptr};
+};
+
+using LocalChannelHandle = ChannelHandle<ChannelHandleMode::Local>;
+using BorrowedChannelHandle = ChannelHandle<ChannelHandleMode::Borrowed>;
+using RemoteChannelHandle = ChannelHandle<ChannelHandleMode::Remote>;
+
+// ChannelReference is a 32 bit integer used in IPC serialization to be
+// transferred across processes. You can convert this value to a local channel
+// handle by calling Transaction.GetChannelHandle().
+using ChannelReference = int32_t;
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_CHANNEL_HANDLE_H_
diff --git a/libs/vr/libpdx/private/pdx/client.h b/libs/vr/libpdx/private/pdx/client.h
new file mode 100644
index 0000000..4eafe76
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/client.h
@@ -0,0 +1,286 @@
+#ifndef ANDROID_PDX_CLIENT_H_
+#define ANDROID_PDX_CLIENT_H_
+
+#include <errno.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <type_traits>
+
+#include <pdx/channel_handle.h>
+#include <pdx/client_channel.h>
+#include <pdx/client_channel_factory.h>
+#include <pdx/file_handle.h>
+#include <pdx/message_reader.h>
+#include <pdx/message_writer.h>
+#include <pdx/rpc/remote_method_type.h>
+#include <pdx/status.h>
+
+namespace android {
+namespace pdx {
+
+class Transaction;
+
+/*
+ * Base class of client-side service API classes.
+ */
+class Client {
+ public:
+ static const int64_t kInfiniteTimeout = -1;
+
+ virtual ~Client() = default;
+
+ /*
+ * Returns true if the Client instance successfully initialized, false
+ * otherwise. Subclasses that can fail to initialize must override this and
+ * AND their initialization result with this base class method's result.
+ *
+ * This method is not intended to perform initialization, only to report
+ * the status of the initialization.
+ */
+ virtual bool IsInitialized() const;
+
+ /*
+ * Returns the error code describing the Client initialization failure, or 0
+ * if there was no failure.
+ */
+ int error() const;
+
+ // Returns a reference to IPC channel handle.
+ LocalChannelHandle& GetChannelHandle();
+
+ protected:
+ friend Transaction;
+ explicit Client(std::unique_ptr<ClientChannel> channel);
+ explicit Client(std::unique_ptr<ClientChannelFactory> channel_factory,
+ int64_t timeout_ms = kInfiniteTimeout);
+
+ /*
+ * Called by Client::Connect() after successfully connecting to the service
+ * endpoint. Subclasses may override this method to perform additional setup,
+ * including sending messages to complete the connection process.
+ *
+ * Subclasses may call Client::Close() within this method to terminate the
+ * connection; Client::Connect() returns the negated error passed to
+ * Client::Close() when this happens.
+ */
+ virtual void OnConnect();
+
+ enum : size_t { MAX_IMPULSE_LENGTH = sizeof(uint64_t) * 4 };
+
+ Status<void> SendImpulse(int opcode);
+ Status<void> SendImpulse(int opcode, const void* buffer, size_t length);
+
+ /*
+ * Remote method call API using pdx::rpc serialization.
+ * Include pdx/rpc/remote_method.h to use these methods.
+ */
+ template <typename RemoteMethodType, typename... Args>
+ Status<typename RemoteMethodType::Return> InvokeRemoteMethod(Args&&... args);
+
+ template <typename RemoteMethodType, typename ReturnType, typename... Args>
+ Status<void> InvokeRemoteMethodInPlace(ReturnType* return_value,
+ Args&&... args);
+
+ /*
+ * Close the endpoint file descriptor and optionally indicate an error, which
+ * may be retrieved through error(). Subclasses may use this in their
+ * constructor to signal failure during initialization or at other times
+ * during operation.
+ */
+ void Close(int error);
+
+ /*
+ * Returns true if the client is connected to the service, false otherwise.
+ */
+ bool IsConnected() const;
+
+ /*
+ * Enables auto-reconnect with the given timeout. Use kInfiniteTimeout (-1)
+ * for no timeout. Auto-reconnect can only be enabled if the Client class
+ * was constructed with a ClientChannelFactory.
+ */
+ void EnableAutoReconnect(int64_t reconnect_timeout_ms);
+
+ /*
+ * Disables auto-reconnect.
+ */
+ void DisableAutoReconnect();
+
+ int event_fd() const;
+ ClientChannel* GetChannel() const { return channel_.get(); }
+
+ private:
+ Client(const Client&) = delete;
+ void operator=(const Client&) = delete;
+
+ Status<void> CheckReconnect();
+ bool NeedToDisconnectChannel(int error) const;
+ void CheckDisconnect(int error);
+
+ template <typename T>
+ inline void CheckDisconnect(const Status<T>& status) {
+ if (!status)
+ CheckDisconnect(status.error());
+ }
+
+ std::unique_ptr<ClientChannel> channel_;
+ int error_{0};
+
+ // Reconnection state.
+ std::unique_ptr<ClientChannelFactory> channel_factory_;
+ int64_t reconnect_timeout_ms_{0};
+ bool auto_reconnect_enabled_{false};
+};
+
+/*
+ * Utility template base class for client-side service API classes. Handles
+ * initialization checks during allocation and automatically cleans up on
+ * failure.
+ *
+ * @tparam T Type of the class extending this one.
+ * @tparam C Client class to wrap. Defaults to the Client class.
+ */
+template <typename T, typename ParentClient = Client>
+class ClientBase : public ParentClient {
+ public:
+ // Type of the client this class wraps.
+ using ClientType = ParentClient;
+
+ static_assert(std::is_base_of<Client, ParentClient>::value,
+ "The provided parent client is not a Client subclass.");
+
+ /*
+ * Allocates a new instance of the superclass and checks for successful
+ * initialization.
+ *
+ * The variadic arguments must expand to match one of type T's constructors
+ * and are passed through unchanged. If a timeout is desired, subclasses are
+ * responsible for passing this through to the appropriate ClientBase
+ * constructor.
+ *
+ * Returns a unique_ptr to the new instance on success, or an empty unique_ptr
+ * otherwise.
+ */
+ template <typename... Args>
+ static inline std::unique_ptr<T> Create(Args&&... args) {
+ std::unique_ptr<T> client(new T(std::forward<Args>(args)...));
+ if (client->IsInitialized())
+ return client;
+ else
+ return nullptr;
+ }
+
+ protected:
+ /*
+ * Type of the base class. Useful for referencing the base class type and
+ * constructor in subclasses. Subclasses with non-public constructors
+ * must declare BASE a friend.
+ */
+ using BASE = ClientBase<T, ParentClient>;
+
+ /*
+ * Type of the unique_ptr deleter. Useful for friend declarations.
+ */
+ using deleter_type = typename std::unique_ptr<T>::deleter_type;
+
+ using ParentClient::ParentClient;
+};
+
+class Transaction final : public OutputResourceMapper,
+ public InputResourceMapper {
+ public:
+ Transaction(Client& client);
+ ~Transaction();
+
+ template <typename T>
+ Status<T> Send(int opcode) {
+ return SendVector<T>(opcode, nullptr, 0, nullptr, 0);
+ }
+
+ template <typename T>
+ Status<T> Send(int opcode, const void* send_buffer, size_t send_length,
+ void* receive_buffer, size_t receive_length) {
+ const bool send = (send_buffer && send_length);
+ const bool receive = (receive_buffer && receive_length);
+ const iovec send_vector = {const_cast<void*>(send_buffer), send_length};
+ const iovec receive_vector = {receive_buffer, receive_length};
+ return SendVector<T>(opcode, send ? &send_vector : nullptr, send ? 1 : 0,
+ receive ? &receive_vector : nullptr, receive ? 1 : 0);
+ }
+
+ template <typename T>
+ Status<T> SendVector(int opcode, const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector, size_t receive_count) {
+ Status<T> ret;
+ SendTransaction(opcode, &ret, send_vector, send_count, receive_vector,
+ receive_count);
+ return ret;
+ }
+
+ template <typename T, size_t send_count, size_t receive_count>
+ Status<T> SendVector(int opcode, const iovec (&send_vector)[send_count],
+ const iovec (&receive_vector)[receive_count]) {
+ return SendVector<T>(opcode, send_vector, send_count, receive_vector,
+ receive_count);
+ }
+
+ template <typename T, size_t send_count>
+ Status<T> SendVector(int opcode, const iovec (&send_vector)[send_count],
+ std::nullptr_t) {
+ return SendVector<T>(opcode, send_vector, send_count, nullptr, 0);
+ }
+
+ template <typename T, size_t receive_count>
+ Status<T> SendVector(int opcode, std::nullptr_t,
+ const iovec (&receive_vector)[receive_count]) {
+ return SendVector<T>(opcode, nullptr, 0, receive_vector, receive_count);
+ }
+
+ // OutputResourceMapper
+ FileReference PushFileHandle(const LocalHandle& handle) override;
+ FileReference PushFileHandle(const BorrowedHandle& handle) override;
+ FileReference PushFileHandle(const RemoteHandle& handle) override;
+ ChannelReference PushChannelHandle(const LocalChannelHandle& handle) override;
+ ChannelReference PushChannelHandle(
+ const BorrowedChannelHandle& handle) override;
+ ChannelReference PushChannelHandle(
+ const RemoteChannelHandle& handle) override;
+
+ // InputResourceMapper
+ bool GetFileHandle(FileReference ref, LocalHandle* handle) override;
+ bool GetChannelHandle(ChannelReference ref,
+ LocalChannelHandle* handle) override;
+
+ private:
+ bool EnsureStateAllocated();
+ void SendTransaction(int opcode, Status<void>* ret, const iovec* send_vector,
+ size_t send_count, const iovec* receive_vector,
+ size_t receive_count);
+ void SendTransaction(int opcode, Status<int>* ret, const iovec* send_vector,
+ size_t send_count, const iovec* receive_vector,
+ size_t receive_count);
+ void SendTransaction(int opcode, Status<LocalHandle>* ret,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector, size_t receive_count);
+ void SendTransaction(int opcode, Status<LocalChannelHandle>* ret,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector, size_t receive_count);
+ void CheckDisconnect(int error);
+
+ template <typename T>
+ inline void CheckDisconnect(const Status<T>& status) {
+ if (!status)
+ CheckDisconnect(status.error());
+ }
+
+ Client& client_;
+ void* state_{nullptr};
+ bool state_allocated_{false};
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_CLIENT_H_
diff --git a/libs/vr/libpdx/private/pdx/client_channel.h b/libs/vr/libpdx/private/pdx/client_channel.h
new file mode 100644
index 0000000..e7ea475
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/client_channel.h
@@ -0,0 +1,56 @@
+#ifndef ANDROID_PDX_CLIENT_CHANNEL_H_
+#define ANDROID_PDX_CLIENT_CHANNEL_H_
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/status.h>
+
+struct iovec;
+
+namespace android {
+namespace pdx {
+
+class ClientChannel {
+ public:
+ virtual ~ClientChannel() = default;
+
+ // Returns a tag that uniquely identifies a specific underlying IPC transport.
+ virtual uint32_t GetIpcTag() const = 0;
+
+ virtual int event_fd() const = 0;
+ virtual LocalChannelHandle& GetChannelHandle() = 0;
+ virtual void* AllocateTransactionState() = 0;
+ virtual void FreeTransactionState(void* state) = 0;
+
+ virtual Status<void> SendImpulse(int opcode, const void* buffer,
+ size_t length) = 0;
+
+ virtual Status<int> SendWithInt(void* transaction_state, int opcode,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) = 0;
+ virtual Status<LocalHandle> SendWithFileHandle(
+ void* transaction_state, int opcode, const iovec* send_vector,
+ size_t send_count, const iovec* receive_vector, size_t receive_count) = 0;
+ virtual Status<LocalChannelHandle> SendWithChannelHandle(
+ void* transaction_state, int opcode, const iovec* send_vector,
+ size_t send_count, const iovec* receive_vector, size_t receive_count) = 0;
+
+ virtual FileReference PushFileHandle(void* transaction_state,
+ const LocalHandle& handle) = 0;
+ virtual FileReference PushFileHandle(void* transaction_state,
+ const BorrowedHandle& handle) = 0;
+ virtual ChannelReference PushChannelHandle(
+ void* transaction_state, const LocalChannelHandle& handle) = 0;
+ virtual ChannelReference PushChannelHandle(
+ void* transaction_state, const BorrowedChannelHandle& handle) = 0;
+ virtual bool GetFileHandle(void* transaction_state, FileReference ref,
+ LocalHandle* handle) const = 0;
+ virtual bool GetChannelHandle(void* transaction_state, ChannelReference ref,
+ LocalChannelHandle* handle) const = 0;
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_CLIENT_CHANNEL_H_
diff --git a/libs/vr/libpdx/private/pdx/client_channel_factory.h b/libs/vr/libpdx/private/pdx/client_channel_factory.h
new file mode 100644
index 0000000..a82ab70
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/client_channel_factory.h
@@ -0,0 +1,21 @@
+#ifndef ANDROID_PDX_CLIENT_CHANNEL_FACTORY_H_
+#define ANDROID_PDX_CLIENT_CHANNEL_FACTORY_H_
+
+#include <pdx/client_channel.h>
+#include <pdx/status.h>
+
+namespace android {
+namespace pdx {
+
+class ClientChannelFactory {
+ public:
+ virtual ~ClientChannelFactory() = default;
+
+ virtual Status<std::unique_ptr<ClientChannel>> Connect(
+ int64_t timeout_ms) const = 0;
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_CLIENT_CHANNEL_FACTORY_H_
diff --git a/libs/vr/libpdx/private/pdx/file_handle.h b/libs/vr/libpdx/private/pdx/file_handle.h
new file mode 100644
index 0000000..b3c3ad7
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/file_handle.h
@@ -0,0 +1,141 @@
+#ifndef ANDROID_PDX_FILE_HANDLE_H_
+#define ANDROID_PDX_FILE_HANDLE_H_
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <string>
+
+namespace android {
+namespace pdx {
+
+enum class FileHandleMode {
+ Local,
+ Remote,
+ Borrowed,
+};
+
+// Manages ownership, sharing, and lifetime of file descriptors.
+template <FileHandleMode Mode>
+class FileHandle {
+ public:
+ static constexpr int kEmptyFileHandle = -1;
+
+ // Constructs an empty FileHandle object.
+ FileHandle() : fd_(kEmptyFileHandle) {}
+
+ // Constructs a FileHandle from an integer file descriptor and takes
+ // ownership.
+ explicit FileHandle(int fd) : fd_(fd) {}
+
+ // Constructs a FileHandle by opening |path|. The arguments follow the
+ // semantics of open().
+ FileHandle(const std::string& path, int flags, mode_t mode = 0) {
+ fd_ = open(path.c_str(), flags, mode);
+ }
+
+ // Constructs a FileHandle by opening |path| relative to |dir_fd|, following
+ // the semantics of openat().
+ FileHandle(const int directory_fd, const std::string& path, int flags,
+ mode_t mode = 0) {
+ fd_ = openat(directory_fd, path.c_str(), flags, mode);
+ }
+
+ // Move constructor that assumes ownership of the file descriptor, leaving the
+ // other FileHandle object empty.
+ FileHandle(FileHandle&& other) {
+ fd_ = other.fd_;
+ other.fd_ = kEmptyFileHandle;
+ }
+
+ // Returns a FileHandle as a duplicate handle of |fd|. Borrowed handles and
+ // handles in remote handle space are not duplicated.
+ static FileHandle AsDuplicate(const int fd) {
+ if (Mode == FileHandleMode::Local)
+ return FileHandle(dup(fd));
+ else
+ return FileHandle(fd);
+ }
+
+ // Destructor closes the file descriptor when non-empty.
+ ~FileHandle() { Close(); }
+
+ // Move assignment operator that assumes ownership of the underlying file
+ // descriptor, leaving the other FileHandle object empty.
+ FileHandle& operator=(FileHandle&& other) {
+ if (this != &other) {
+ Reset(other.fd_);
+ other.fd_ = kEmptyFileHandle;
+ }
+ return *this;
+ }
+
+ // Resets the underlying file handle to |fd|.
+ void Reset(int fd) {
+ Close();
+ fd_ = fd;
+ }
+
+ // Closes the underlying file descriptor when non-empty.
+ void Close() {
+ if (IsValid() && Mode == FileHandleMode::Local)
+ close(fd_);
+ fd_ = kEmptyFileHandle;
+ }
+
+ // Return the internal fd, passing ownership to the caller.
+ int Release() {
+ int release_fd = fd_;
+ fd_ = kEmptyFileHandle;
+ return release_fd;
+ }
+
+ // Duplicates the underlying file descriptor and returns a FileHandle that
+ // owns the new file descriptor. File descriptors are not duplicated when Mode
+ // is Remote or Borrowed.
+ FileHandle Duplicate() const {
+ return FileHandle(Mode == FileHandleMode::Local ? dup(fd_) : fd_);
+ }
+
+ FileHandle<FileHandleMode::Borrowed> Borrow() const {
+ return FileHandle<FileHandleMode::Borrowed>(Get());
+ }
+
+ // Gets the underlying file descriptor value.
+ int Get() const { return fd_; }
+ bool IsValid() const { return fd_ >= 0; }
+ explicit operator bool() const { return IsValid(); }
+
+ private:
+ int fd_;
+
+ FileHandle(const FileHandle&) = delete;
+ void operator=(const FileHandle&) = delete;
+};
+
+// Alias for a file handle in the local process' handle space.
+using LocalHandle = FileHandle<FileHandleMode::Local>;
+
+// Alias for a file handle in another process' handle space. Handles returned
+// from pushing a file object or channel must be stored in this type of handle
+// class, which doesn't close the underlying file descriptor. The underlying
+// file descriptor in this wrapper should not be passed to close() because doing
+// so would close an unrelated file descriptor in the local handle space.
+using RemoteHandle = FileHandle<FileHandleMode::Remote>;
+
+// Alias for borrowed handles in the local process' handle space. A borrowed
+// file handle is not close() because this wrapper does not own the underlying
+// file descriptor. Care must be take to ensure that a borrowed file handle
+// remains valid during use.
+using BorrowedHandle = FileHandle<FileHandleMode::Borrowed>;
+
+// FileReference is a 16 bit integer used in IPC serialization to be
+// transferred across processes. You can convert this value to a local file
+// handle by calling Transaction.GetFileHandle() on client side and
+// Message.GetFileHandle() on service side.
+using FileReference = int16_t;
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_FILE_HANDLE_H_
diff --git a/libs/vr/libpdx/private/pdx/message_reader.h b/libs/vr/libpdx/private/pdx/message_reader.h
new file mode 100644
index 0000000..bc280cf
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/message_reader.h
@@ -0,0 +1,38 @@
+#ifndef ANDROID_PDX_MESSAGE_READER_H_
+#define ANDROID_PDX_MESSAGE_READER_H_
+
+#include <memory>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+
+namespace android {
+namespace pdx {
+
+class InputResourceMapper {
+ public:
+ virtual bool GetFileHandle(FileReference ref, LocalHandle* handle) = 0;
+ virtual bool GetChannelHandle(ChannelReference ref,
+ LocalChannelHandle* handle) = 0;
+
+ protected:
+ virtual ~InputResourceMapper() = default;
+};
+
+class MessageReader {
+ public:
+ // Pointers to start/end of the region in the read buffer.
+ using BufferSection = std::pair<const void*, const void*>;
+
+ virtual BufferSection GetNextReadBufferSection() = 0;
+ virtual void ConsumeReadBufferSectionData(const void* new_start) = 0;
+ virtual InputResourceMapper* GetInputResourceMapper() = 0;
+
+ protected:
+ virtual ~MessageReader() = default;
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MESSAGE_READER_H_
diff --git a/libs/vr/libpdx/private/pdx/message_writer.h b/libs/vr/libpdx/private/pdx/message_writer.h
new file mode 100644
index 0000000..0cb6e40
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/message_writer.h
@@ -0,0 +1,38 @@
+#ifndef ANDROID_PDX_MESSAGE_WRITER_H_
+#define ANDROID_PDX_MESSAGE_WRITER_H_
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+
+namespace android {
+namespace pdx {
+
+class OutputResourceMapper {
+ public:
+ virtual FileReference PushFileHandle(const LocalHandle& handle) = 0;
+ virtual FileReference PushFileHandle(const BorrowedHandle& handle) = 0;
+ virtual FileReference PushFileHandle(const RemoteHandle& handle) = 0;
+ virtual ChannelReference PushChannelHandle(
+ const LocalChannelHandle& handle) = 0;
+ virtual ChannelReference PushChannelHandle(
+ const BorrowedChannelHandle& handle) = 0;
+ virtual ChannelReference PushChannelHandle(
+ const RemoteChannelHandle& handle) = 0;
+
+ protected:
+ virtual ~OutputResourceMapper() = default;
+};
+
+class MessageWriter {
+ public:
+ virtual void* GetNextWriteBufferSection(size_t size) = 0;
+ virtual OutputResourceMapper* GetOutputResourceMapper() = 0;
+
+ protected:
+ virtual ~MessageWriter() = default;
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MESSAGE_WRITER_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_client_channel.h b/libs/vr/libpdx/private/pdx/mock_client_channel.h
new file mode 100644
index 0000000..7780ee3
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_client_channel.h
@@ -0,0 +1,55 @@
+#ifndef ANDROID_PDX_MOCK_CLIENT_CHANNEL_H_
+#define ANDROID_PDX_MOCK_CLIENT_CHANNEL_H_
+
+#include <gmock/gmock.h>
+#include <pdx/client_channel.h>
+
+namespace android {
+namespace pdx {
+
+class MockClientChannel : public ClientChannel {
+ public:
+ MOCK_CONST_METHOD0(GetIpcTag, uint32_t());
+ MOCK_CONST_METHOD0(event_fd, int());
+ MOCK_METHOD0(GetChannelHandle, LocalChannelHandle&());
+ MOCK_METHOD0(AllocateTransactionState, void*());
+ MOCK_METHOD1(FreeTransactionState, void(void* state));
+ MOCK_METHOD3(SendImpulse,
+ Status<void>(int opcode, const void* buffer, size_t length));
+ MOCK_METHOD6(SendWithInt,
+ Status<int>(void* transaction_state, int opcode,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector, size_t receive_count));
+ MOCK_METHOD6(SendWithFileHandle,
+ Status<LocalHandle>(void* transaction_state, int opcode,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count));
+ MOCK_METHOD6(SendWithChannelHandle,
+ Status<LocalChannelHandle>(void* transaction_state, int opcode,
+ const iovec* send_vector,
+ size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count));
+ MOCK_METHOD2(PushFileHandle, FileReference(void* transaction_state,
+ const LocalHandle& handle));
+ MOCK_METHOD2(PushFileHandle, FileReference(void* transaction_state,
+ const BorrowedHandle& handle));
+ MOCK_METHOD2(PushChannelHandle,
+ ChannelReference(void* transaction_state,
+ const LocalChannelHandle& handle));
+ MOCK_METHOD2(PushChannelHandle,
+ ChannelReference(void* transaction_state,
+ const BorrowedChannelHandle& handle));
+ MOCK_CONST_METHOD3(GetFileHandle,
+ bool(void* transaction_state, FileReference ref,
+ LocalHandle* handle));
+ MOCK_CONST_METHOD3(GetChannelHandle,
+ bool(void* transaction_state, ChannelReference ref,
+ LocalChannelHandle* handle));
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MOCK_CLIENT_CHANNEL_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_client_channel_factory.h b/libs/vr/libpdx/private/pdx/mock_client_channel_factory.h
new file mode 100644
index 0000000..0190f5e
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_client_channel_factory.h
@@ -0,0 +1,19 @@
+#ifndef ANDROID_PDX_MOCK_CLIENT_CHANNEL_FACTORY_H_
+#define ANDROID_PDX_MOCK_CLIENT_CHANNEL_FACTORY_H_
+
+#include <gmock/gmock.h>
+#include <pdx/client_channel_factory.h>
+
+namespace android {
+namespace pdx {
+
+class MockClientChannelFactory : public ClientChannelFactory {
+ public:
+ MOCK_CONST_METHOD1(
+ Connect, Status<std::unique_ptr<ClientChannel>>(int64_t timeout_ms));
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MOCK_CLIENT_CHANNEL_FACTORY_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_message_reader.h b/libs/vr/libpdx/private/pdx/mock_message_reader.h
new file mode 100644
index 0000000..85e96ef
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_message_reader.h
@@ -0,0 +1,27 @@
+#ifndef ANDROID_PDX_MOCK_MESSAGE_READER_H_
+#define ANDROID_PDX_MOCK_MESSAGE_READER_H_
+
+#include <gmock/gmock.h>
+#include <pdx/message_reader.h>
+
+namespace android {
+namespace pdx {
+
+class MockInputResourceMapper : public InputResourceMapper {
+ public:
+ MOCK_METHOD2(GetFileHandle, bool(FileReference ref, LocalHandle* handle));
+ MOCK_METHOD2(GetChannelHandle,
+ bool(ChannelReference ref, LocalChannelHandle* handle));
+};
+
+class MockMessageReader : public MessageReader {
+ public:
+ MOCK_METHOD0(GetNextReadBufferSection, BufferSection());
+ MOCK_METHOD1(ConsumeReadBufferSectionData, void(const void* new_start));
+ MOCK_METHOD0(GetInputResourceMapper, InputResourceMapper*());
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MOCK_MESSAGE_READER_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_message_writer.h b/libs/vr/libpdx/private/pdx/mock_message_writer.h
new file mode 100644
index 0000000..3c513d7
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_message_writer.h
@@ -0,0 +1,32 @@
+#ifndef ANDROID_PDX_MOCK_MESSAGE_WRITER_H_
+#define ANDROID_PDX_MOCK_MESSAGE_WRITER_H_
+
+#include <gmock/gmock.h>
+#include <pdx/message_writer.h>
+
+namespace android {
+namespace pdx {
+
+class MockOutputResourceMapper : public OutputResourceMapper {
+ public:
+ MOCK_METHOD1(PushFileHandle, FileReference(const LocalHandle& handle));
+ MOCK_METHOD1(PushFileHandle, FileReference(const BorrowedHandle& handle));
+ MOCK_METHOD1(PushFileHandle, FileReference(const RemoteHandle& handle));
+ MOCK_METHOD1(PushChannelHandle,
+ ChannelReference(const LocalChannelHandle& handle));
+ MOCK_METHOD1(PushChannelHandle,
+ ChannelReference(const BorrowedChannelHandle& handle));
+ MOCK_METHOD1(PushChannelHandle,
+ ChannelReference(const RemoteChannelHandle& handle));
+};
+
+class MockMessageWriter : public MessageWriter {
+ public:
+ MOCK_METHOD1(GetNextWriteBufferSection, void*(size_t size));
+ MOCK_METHOD0(GetOutputResourceMapper, OutputResourceMapper*());
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MOCK_MESSAGE_WRITER_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_service_dispatcher.h b/libs/vr/libpdx/private/pdx/mock_service_dispatcher.h
new file mode 100644
index 0000000..9b51d30
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_service_dispatcher.h
@@ -0,0 +1,24 @@
+#ifndef ANDROID_PDX_MOCK_SERVICE_DISPATCHER_H_
+#define ANDROID_PDX_MOCK_SERVICE_DISPATCHER_H_
+
+#include <gmock/gmock.h>
+#include <pdx/service_dispatcher.h>
+
+namespace android {
+namespace pdx {
+
+class MockServiceDispatcher : public ServiceDispatcher {
+ public:
+ MOCK_METHOD1(AddService, int(const std::shared_ptr<Service>& service));
+ MOCK_METHOD1(RemoveService, int(const std::shared_ptr<Service>& service));
+ MOCK_METHOD0(ReceiveAndDispatch, int());
+ MOCK_METHOD1(ReceiveAndDispatch, int(int timeout));
+ MOCK_METHOD0(EnterDispatchLoop, int());
+ MOCK_METHOD1(SetCanceled, void(bool cancel));
+ MOCK_CONST_METHOD0(IsCanceled, bool());
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MOCK_SERVICE_DISPATCHER_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_service_endpoint.h b/libs/vr/libpdx/private/pdx/mock_service_endpoint.h
new file mode 100644
index 0000000..ead74d5
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_service_endpoint.h
@@ -0,0 +1,66 @@
+#ifndef ANDROID_PDX_MOCK_ENDPOINT_H_
+#define ANDROID_PDX_MOCK_ENDPOINT_H_
+
+#include <gmock/gmock.h>
+#include <pdx/service_endpoint.h>
+
+namespace android {
+namespace pdx {
+
+class MockEndpoint : public Endpoint {
+ public:
+ MOCK_CONST_METHOD0(GetIpcTag, uint32_t());
+ MOCK_METHOD1(SetService, int(Service* service));
+ MOCK_METHOD2(SetChannel, int(int channel_id, Channel* channel));
+ MOCK_METHOD1(CloseChannel, int(int channel_id));
+ MOCK_METHOD3(ModifyChannelEvents,
+ int(int channel_id, int clear_mask, int set_mask));
+ MOCK_METHOD4(PushChannel,
+ Status<RemoteChannelHandle>(Message* message, int flags,
+ Channel* channel, int* channel_id));
+ MOCK_METHOD3(CheckChannel,
+ Status<int>(const Message* message, ChannelReference ref,
+ Channel** channel));
+ MOCK_METHOD1(DefaultHandleMessage, int(const MessageInfo& info));
+ MOCK_METHOD1(MessageReceive, int(Message* message));
+ MOCK_METHOD2(MessageReply, int(Message* message, int return_code));
+ MOCK_METHOD2(MessageReplyFd, int(Message* message, unsigned int push_fd));
+ MOCK_METHOD2(MessageReplyChannelHandle,
+ int(Message* message, const LocalChannelHandle& handle));
+ MOCK_METHOD2(MessageReplyChannelHandle,
+ int(Message* message, const BorrowedChannelHandle& handle));
+ MOCK_METHOD2(MessageReplyChannelHandle,
+ int(Message* message, const RemoteChannelHandle& handle));
+ MOCK_METHOD3(ReadMessageData, ssize_t(Message* message, const iovec* vector,
+ size_t vector_length));
+ MOCK_METHOD3(WriteMessageData, ssize_t(Message* message, const iovec* vector,
+ size_t vector_length));
+ MOCK_METHOD2(PushFileHandle,
+ FileReference(Message* message, const LocalHandle& handle));
+ MOCK_METHOD2(PushFileHandle,
+ FileReference(Message* message, const BorrowedHandle& handle));
+ MOCK_METHOD2(PushFileHandle,
+ FileReference(Message* message, const RemoteHandle& handle));
+ MOCK_METHOD2(PushChannelHandle,
+ ChannelReference(Message* message,
+ const LocalChannelHandle& handle));
+ MOCK_METHOD2(PushChannelHandle,
+ ChannelReference(Message* message,
+ const BorrowedChannelHandle& handle));
+ MOCK_METHOD2(PushChannelHandle,
+ ChannelReference(Message* message,
+ const RemoteChannelHandle& handle));
+ MOCK_CONST_METHOD2(GetFileHandle,
+ LocalHandle(Message* message, FileReference ref));
+ MOCK_CONST_METHOD2(GetChannelHandle,
+ LocalChannelHandle(Message* message,
+ ChannelReference ref));
+ MOCK_METHOD0(AllocateMessageState, void*());
+ MOCK_METHOD1(FreeMessageState, void(void* state));
+ MOCK_METHOD0(Cancel, int());
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MOCK_ENDPOINT_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/argument_encoder.h b/libs/vr/libpdx/private/pdx/rpc/argument_encoder.h
new file mode 100644
index 0000000..e006284
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/argument_encoder.h
@@ -0,0 +1,184 @@
+#ifndef ANDROID_PDX_RPC_ARGUMENT_ENCODER_H_
+#define ANDROID_PDX_RPC_ARGUMENT_ENCODER_H_
+
+#include <cstdint>
+#include <tuple>
+#include <type_traits>
+
+#include <pdx/rpc/serialization.h>
+#include <pdx/service.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Provides automatic serialization of argument lists and return
+// values by analyzing the supplied function signature types.
+// Examples:
+// ArgumentEncoder<int(int, float)> encoder(writer);
+// encoder.EncodeArguments(1, 1.0);
+
+template <typename T>
+class ArgumentEncoder;
+
+// Specialization of ArgumentEncoder for void return types.
+template <typename... Args>
+class ArgumentEncoder<void(Args...)> {
+ public:
+ explicit ArgumentEncoder(MessageWriter* writer) : writer_{writer} {}
+
+ // Serializes the arguments as a tuple.
+ void EncodeArguments(Args... args) {
+ Serialize(std::forward_as_tuple(args...), writer_);
+ }
+
+ private:
+ MessageWriter* writer_;
+};
+
+// Specialization of ArgumentEncoder for non-void return types.
+template <typename Return, typename... Args>
+class ArgumentEncoder<Return(Args...)> {
+ public:
+ // Simplified types with reference and cv removed.
+ using ReturnType = typename std::decay<Return>::type;
+
+ explicit ArgumentEncoder(MessageWriter* writer) : writer_{writer} {}
+
+ // Serializes the arguments as a tuple.
+ void EncodeArguments(Args... args) {
+ Serialize(std::forward_as_tuple(args...), writer_);
+ }
+
+ // Serializes the return value for rvalue references.
+ void EncodeReturn(const ReturnType& return_value) {
+ Serialize(return_value, writer_);
+ }
+
+ private:
+ MessageWriter* writer_;
+};
+
+// Utility to build an ArgumentEncoder from a function pointer and a message
+// writer.
+template <typename Return, typename... Args>
+inline ArgumentEncoder<Return(Args...)> MakeArgumentEncoder(
+ Return (*)(Args...), MessageWriter* writer) {
+ return ArgumentEncoder<Return(Args...)>(writer);
+}
+
+// Utility to build an ArgumentEncoder from a method pointer and a message
+// writer.
+template <typename Class, typename Return, typename... Args>
+inline ArgumentEncoder<Return(Args...)> MakeArgumentEncoder(
+ Return (Class::*)(Args...), MessageWriter* writer) {
+ return ArgumentEncoder<Return(Args...)>(writer);
+}
+
+// Utility to build an ArgumentEncoder from a const method pointer and a
+// message writer.
+template <typename Class, typename Return, typename... Args>
+inline ArgumentEncoder<Return(Args...)> MakeArgumentEncoder(
+ Return (Class::*)(Args...) const, MessageWriter* writer) {
+ return ArgumentEncoder<Return(Args...)>(writer);
+}
+
+// Utility to build an ArgumentEncoder from a function type and a message
+// writer.
+template <typename Signature>
+inline ArgumentEncoder<Signature> MakeArgumentEncoder(MessageWriter* writer) {
+ return ArgumentEncoder<Signature>(writer);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Provides automatic deserialization of argument lists and return
+// values by analyzing the supplied function signature types.
+// Examples:
+// auto decoder = MakeArgumentDecoder<std::string(void)>(reader);
+// ErrorType error = decoder.DecodeReturn(&return_value);
+
+template <typename T>
+class ArgumentDecoder;
+
+// Specialization of ArgumentDecoder for void return types.
+template <typename... Args>
+class ArgumentDecoder<void(Args...)> {
+ public:
+ // Simplified types with reference and cv removed.
+ using ArgsTupleType = std::tuple<typename std::decay<Args>::type...>;
+
+ explicit ArgumentDecoder(MessageReader* reader) : reader_{reader} {}
+
+ // Deserializes arguments into a tuple.
+ ArgsTupleType DecodeArguments(ErrorType* error) {
+ ArgsTupleType value;
+ *error = Deserialize(&value, reader_);
+ return value;
+ }
+
+ private:
+ MessageReader* reader_;
+};
+
+// Specialization of ArgumentDecoder for non-void return types.
+template <typename Return, typename... Args>
+class ArgumentDecoder<Return(Args...)> {
+ public:
+ // Simplified types with reference and cv removed.
+ using ArgsTupleType = std::tuple<typename std::decay<Args>::type...>;
+ using ReturnType = typename std::decay<Return>::type;
+
+ explicit ArgumentDecoder(MessageReader* reader) : reader_{reader} {}
+
+ // Deserializes arguments into a tuple.
+ ArgsTupleType DecodeArguments(ErrorType* error) {
+ ArgsTupleType value;
+ *error = Deserialize(&value, reader_);
+ return value;
+ }
+
+ // Deserializes the return value.
+ ErrorType DecodeReturn(ReturnType* value) {
+ return Deserialize(value, reader_);
+ }
+
+ private:
+ MessageReader* reader_;
+};
+
+// Utility to build an ArgumentDecoder from a function pointer and a message
+// reader.
+template <typename Return, typename... Args>
+inline ArgumentDecoder<Return(Args...)> MakeArgumentDecoder(
+ Return (*)(Args...), MessageReader* reader) {
+ return ArgumentDecoder<Return(Args...)>(reader);
+}
+
+// Utility to build an ArgumentDecoder from a method pointer and a message
+// reader.
+template <typename Class, typename Return, typename... Args>
+inline ArgumentDecoder<Return(Args...)> MakeArgumentDecoder(
+ Return (Class::*)(Args...), MessageReader* reader) {
+ return ArgumentDecoder<Return(Args...)>(reader);
+}
+
+// Utility to build an ArgumentDecoder from a const method pointer and a
+// message reader.
+template <typename Class, typename Return, typename... Args>
+inline ArgumentDecoder<Return(Args...)> MakeArgumentDecoder(
+ Return (Class::*)(Args...) const, MessageReader* reader) {
+ return ArgumentDecoder<Return(Args...)>(reader);
+}
+
+// Utility to build an ArgumentDecoder from a function type and a message
+// reader.
+template <typename Signature>
+inline ArgumentDecoder<Signature> MakeArgumentDecoder(MessageReader* reader) {
+ return ArgumentDecoder<Signature>(reader);
+}
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_ARGUMENT_ENCODER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/array_wrapper.h b/libs/vr/libpdx/private/pdx/rpc/array_wrapper.h
new file mode 100644
index 0000000..93d87f3
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/array_wrapper.h
@@ -0,0 +1,111 @@
+#ifndef ANDROID_PDX_RPC_ARRAY_WRAPPER_H_
+#define ANDROID_PDX_RPC_ARRAY_WRAPPER_H_
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <vector>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Wrapper class for C array buffers, providing an interface suitable for
+// SerializeObject and DeserializeObject. This class serializes to the same
+// format as std::vector, and may be substituted for std::vector during
+// serialization and deserialization. This substitution makes handling of C
+// arrays more efficient by avoiding unnecessary copies when remote method
+// signatures specify std::vector arguments or return values.
+template <typename T>
+class ArrayWrapper {
+ public:
+ // Define types in the style of STL containers to support STL operators.
+ typedef T value_type;
+ typedef std::size_t size_type;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+
+ ArrayWrapper() : buffer_(nullptr), capacity_(0), end_(0) {}
+
+ ArrayWrapper(pointer buffer, size_type capacity, size_type size)
+ : buffer_(&buffer[0]),
+ capacity_(capacity),
+ end_(capacity < size ? capacity : size) {}
+
+ ArrayWrapper(pointer buffer, size_type size)
+ : ArrayWrapper(buffer, size, size) {}
+
+ ArrayWrapper(const ArrayWrapper& other) { *this = other; }
+
+ ArrayWrapper(ArrayWrapper&& other) { *this = std::move(other); }
+
+ ArrayWrapper& operator=(const ArrayWrapper& other) {
+ if (&other == this) {
+ return *this;
+ } else {
+ buffer_ = other.buffer_;
+ capacity_ = other.capacity_;
+ end_ = other.end_;
+ }
+
+ return *this;
+ }
+
+ ArrayWrapper& operator=(ArrayWrapper&& other) {
+ if (&other == this) {
+ return *this;
+ } else {
+ buffer_ = other.buffer_;
+ capacity_ = other.capacity_;
+ end_ = other.end_;
+ other.buffer_ = nullptr;
+ other.capacity_ = 0;
+ other.end_ = 0;
+ }
+
+ return *this;
+ }
+
+ pointer data() { return buffer_; }
+ const_pointer data() const { return buffer_; }
+
+ pointer begin() { return &buffer_[0]; }
+ pointer end() { return &buffer_[end_]; }
+ const_pointer begin() const { return &buffer_[0]; }
+ const_pointer end() const { return &buffer_[end_]; }
+
+ size_type size() const { return end_; }
+ size_type max_size() const { return capacity_; }
+ size_type capacity() const { return capacity_; }
+
+ // Moves the end marker to |size|, clamping the end marker to the max capacity
+ // of the underlying array. This method does not change the size of the
+ // underlying array.
+ void resize(size_type size) {
+ if (size <= capacity_)
+ end_ = size;
+ else
+ end_ = capacity_;
+ }
+
+ reference operator[](size_type pos) { return buffer_[pos]; }
+ const_reference operator[](size_type pos) const { return buffer_[pos]; }
+
+ private:
+ pointer buffer_;
+ size_type capacity_;
+ size_type end_;
+};
+
+template <typename T, typename SizeType = std::size_t>
+ArrayWrapper<T> WrapArray(T* buffer, SizeType size) {
+ return ArrayWrapper<T>(buffer, size);
+}
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_ARRAY_WRAPPER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/buffer_wrapper.h b/libs/vr/libpdx/private/pdx/rpc/buffer_wrapper.h
new file mode 100644
index 0000000..aa86531
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/buffer_wrapper.h
@@ -0,0 +1,177 @@
+#ifndef ANDROID_PDX_RPC_BUFFER_WRAPPER_H_
+#define ANDROID_PDX_RPC_BUFFER_WRAPPER_H_
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <vector>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Wrapper class for buffers, providing an interface suitable for
+// SerializeObject and DeserializeObject. This class supports serialization of
+// buffers as raw bytes.
+template <typename T>
+class BufferWrapper;
+
+template <typename T>
+class BufferWrapper<T*> {
+ public:
+ // Define types in the style of STL containers to support STL operators.
+ typedef T value_type;
+ typedef std::size_t size_type;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+
+ BufferWrapper() : buffer_(nullptr), capacity_(0), end_(0) {}
+
+ BufferWrapper(pointer buffer, size_type capacity, size_type size)
+ : buffer_(&buffer[0]),
+ capacity_(capacity),
+ end_(capacity < size ? capacity : size) {}
+
+ BufferWrapper(pointer buffer, size_type size)
+ : BufferWrapper(buffer, size, size) {}
+
+ BufferWrapper(const BufferWrapper& other) { *this = other; }
+
+ BufferWrapper(BufferWrapper&& other) { *this = std::move(other); }
+
+ BufferWrapper& operator=(const BufferWrapper& other) {
+ if (&other == this) {
+ return *this;
+ } else {
+ buffer_ = other.buffer_;
+ capacity_ = other.capacity_;
+ end_ = other.end_;
+ }
+
+ return *this;
+ }
+
+ BufferWrapper& operator=(BufferWrapper&& other) {
+ if (&other == this) {
+ return *this;
+ } else {
+ buffer_ = other.buffer_;
+ capacity_ = other.capacity_;
+ end_ = other.end_;
+ other.buffer_ = nullptr;
+ other.capacity_ = 0;
+ other.end_ = 0;
+ }
+
+ return *this;
+ }
+
+ pointer data() { return buffer_; }
+ const_pointer data() const { return buffer_; }
+
+ pointer begin() { return &buffer_[0]; }
+ pointer end() { return &buffer_[end_]; }
+ const_pointer begin() const { return &buffer_[0]; }
+ const_pointer end() const { return &buffer_[end_]; }
+
+ size_type size() const { return end_; }
+ size_type max_size() const { return capacity_; }
+ size_type capacity() const { return capacity_; }
+
+ void resize(size_type size) {
+ if (size <= capacity_)
+ end_ = size;
+ else
+ end_ = capacity_;
+ }
+
+ reference operator[](size_type pos) { return buffer_[pos]; }
+ const_reference operator[](size_type pos) const { return buffer_[pos]; }
+
+ private:
+ pointer buffer_;
+ size_type capacity_;
+ size_type end_;
+};
+
+template <typename T, typename Allocator>
+class BufferWrapper<std::vector<T, Allocator>> {
+ public:
+ using BufferType = typename std::vector<T, Allocator>;
+ using value_type = typename BufferType::value_type;
+ using size_type = typename BufferType::size_type;
+ using reference = typename BufferType::reference;
+ using const_reference = typename BufferType::const_reference;
+ using pointer = typename BufferType::pointer;
+ using const_pointer = typename BufferType::const_pointer;
+ using iterator = typename BufferType::iterator;
+ using const_iterator = typename BufferType::const_iterator;
+
+ BufferWrapper() {}
+ BufferWrapper(const BufferType& buffer) : buffer_(buffer) {}
+ BufferWrapper(const BufferType& buffer, const Allocator& allocator)
+ : buffer_(buffer, allocator) {}
+ BufferWrapper(BufferType&& buffer) : buffer_(std::move(buffer)) {}
+ BufferWrapper(BufferType&& buffer, const Allocator& allocator)
+ : buffer_(std::move(buffer), allocator) {}
+ BufferWrapper(const BufferWrapper&) = default;
+ BufferWrapper(BufferWrapper&&) = default;
+ BufferWrapper& operator=(const BufferWrapper&) = default;
+ BufferWrapper& operator=(BufferWrapper&&) = default;
+
+ pointer data() { return buffer_.data(); }
+ const_pointer data() const { return buffer_.data(); }
+
+ iterator begin() { return buffer_.begin(); }
+ iterator end() { return buffer_.end(); }
+ const_iterator begin() const { return buffer_.begin(); }
+ const_iterator end() const { return buffer_.end(); }
+
+ size_type size() const { return buffer_.size(); }
+ size_type max_size() const { return buffer_.capacity(); }
+ size_type capacity() const { return buffer_.capacity(); }
+
+ void resize(size_type size) { buffer_.resize(size); }
+ void reserve(size_type size) { buffer_.reserve(size); }
+
+ reference operator[](size_type pos) { return buffer_[pos]; }
+ const_reference operator[](size_type pos) const { return buffer_[pos]; }
+
+ BufferType& buffer() { return buffer_; }
+ const BufferType& buffer() const { return buffer_; }
+
+ private:
+ BufferType buffer_;
+};
+
+template <typename T, typename SizeType = std::size_t>
+BufferWrapper<T*> WrapBuffer(T* buffer, SizeType size) {
+ return BufferWrapper<T*>(buffer, size);
+}
+
+template <typename SizeType = std::size_t>
+BufferWrapper<std::uint8_t*> WrapBuffer(void* buffer, SizeType size) {
+ return BufferWrapper<std::uint8_t*>(static_cast<std::uint8_t*>(buffer), size);
+}
+
+template <typename SizeType = std::size_t>
+BufferWrapper<const std::uint8_t*> WrapBuffer(const void* buffer,
+ SizeType size) {
+ return BufferWrapper<const std::uint8_t*>(
+ static_cast<const std::uint8_t*>(buffer), size);
+}
+
+template <typename T, typename Allocator = std::allocator<T>>
+BufferWrapper<std::vector<T, Allocator>> WrapBuffer(
+ std::vector<T, Allocator>&& buffer) {
+ return BufferWrapper<std::vector<T, Allocator>>(
+ std::forward<std::vector<T, Allocator>>(buffer));
+}
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_BUFFER_WRAPPER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/copy_cv_reference.h b/libs/vr/libpdx/private/pdx/rpc/copy_cv_reference.h
new file mode 100644
index 0000000..5ce34f8
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/copy_cv_reference.h
@@ -0,0 +1,43 @@
+#ifndef ANDROID_PDX_RPC_COPY_CV_REFERENCE_H_
+#define ANDROID_PDX_RPC_COPY_CV_REFERENCE_H_
+
+#include <type_traits>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Copies const, void, and reference qualifiers from type T to type U, such that
+// the new type U' carries the same cv-reference qualifiers as T, with the same
+// underlying type as U.
+template <typename T, typename U>
+class CopyCVReference {
+ private:
+ using R = typename std::remove_reference<T>::type;
+ using U1 =
+ typename std::conditional<std::is_const<R>::value,
+ typename std::add_const<U>::type, U>::type;
+ using U2 =
+ typename std::conditional<std::is_volatile<R>::value,
+ typename std::add_volatile<U1>::type, U1>::type;
+ using U3 =
+ typename std::conditional<std::is_lvalue_reference<T>::value,
+ typename std::add_lvalue_reference<U2>::type,
+ U2>::type;
+ using U4 =
+ typename std::conditional<std::is_rvalue_reference<T>::value,
+ typename std::add_rvalue_reference<U3>::type,
+ U3>::type;
+
+ public:
+ using Type = U4;
+};
+
+template <typename T, typename U>
+using CopyCVReferenceType = typename CopyCVReference<T, U>::Type;
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_COPY_CV_REFERENCE_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/default_initialization_allocator.h b/libs/vr/libpdx/private/pdx/rpc/default_initialization_allocator.h
new file mode 100644
index 0000000..b6e2980
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/default_initialization_allocator.h
@@ -0,0 +1,47 @@
+#ifndef ANDROID_PDX_RPC_DEFAULT_INITIALIZATION_ALLOCATOR_H_
+#define ANDROID_PDX_RPC_DEFAULT_INITIALIZATION_ALLOCATOR_H_
+
+#include <memory>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Allocator adaptor that interposes construct() calls to convert value
+// initialization into default initialization. All standard containers
+// value-initialize their elements when constructed with a single size_type
+// argument or when grown by a call to resize. This allocator avoids potentially
+// costly value-initialization in these situations for value types that are
+// default constructible. As a consequence, elements of non-class types are left
+// uninitialized; this is desirable when using std::vector as a resizable
+// buffer, for example.
+template <typename T, typename Allocator = std::allocator<T>>
+class DefaultInitializationAllocator : public Allocator {
+ typedef std::allocator_traits<Allocator> AllocatorTraits;
+
+ public:
+ template <typename U>
+ struct rebind {
+ using other = DefaultInitializationAllocator<
+ U, typename AllocatorTraits::template rebind_alloc<U>>;
+ };
+
+ using Allocator::Allocator;
+
+ template <typename U>
+ void construct(U* pointer) noexcept(
+ std::is_nothrow_default_constructible<U>::value) {
+ ::new (static_cast<void*>(pointer)) U;
+ }
+ template <typename U, typename... Args>
+ void construct(U* pointer, Args&&... args) {
+ AllocatorTraits::construct(static_cast<Allocator&>(*this), pointer,
+ std::forward<Args>(args)...);
+ }
+};
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_DEFAULT_INITIALIZATION_ALLOCATOR_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/encoding.h b/libs/vr/libpdx/private/pdx/rpc/encoding.h
new file mode 100644
index 0000000..f51d807
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/encoding.h
@@ -0,0 +1,616 @@
+#ifndef ANDROID_PDX_RPC_ENCODING_H_
+#define ANDROID_PDX_RPC_ENCODING_H_
+
+#include <array>
+#include <cstdint>
+#include <cstring>
+#include <map>
+#include <numeric>
+#include <string>
+#include <tuple>
+#include <unordered_map>
+#include <vector>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+
+#include "array_wrapper.h"
+#include "buffer_wrapper.h"
+#include "string_wrapper.h"
+#include "variant.h"
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// This library uses a subset, or profile, of MessagePack (http://msgpack.org)
+// to encode supported data types during serialization and to verify the
+// expected data types during deserialization. One notable deviation from the
+// MessagePack specification is that little-endian byte order is used for
+// multi-byte numeric types to avoid unnecessary conversion on nearly all
+// relevant architectures.
+//
+// Some data types, integers for example, support multiple encoding strategies.
+// This library attempts to optimize for space based on the value of such types.
+// However, during decode all valid encodings for a given type are accepted.
+
+// Prefix byte for type encodings. This is the complete list of prefix bytes
+// from the MessagePack specification, even though not all types are used by
+// this library.
+enum EncodingPrefix {
+ ENCODING_TYPE_POSITIVE_FIXINT = 0x00,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN = 0x00,
+ ENCODING_TYPE_POSITIVE_FIXINT_MAX = 0x7f,
+ ENCODING_TYPE_POSITIVE_FIXINT_MASK = 0x7f,
+ ENCODING_TYPE_FIXMAP = 0x80,
+ ENCODING_TYPE_FIXMAP_MIN = 0x80,
+ ENCODING_TYPE_FIXMAP_MAX = 0x8f,
+ ENCODING_TYPE_FIXMAP_MASK = 0x0f,
+ ENCODING_TYPE_FIXARRAY = 0x90,
+ ENCODING_TYPE_FIXARRAY_MIN = 0x90,
+ ENCODING_TYPE_FIXARRAY_MAX = 0x9f,
+ ENCODING_TYPE_FIXARRAY_MASK = 0x0f,
+ ENCODING_TYPE_FIXSTR = 0xa0,
+ ENCODING_TYPE_FIXSTR_MIN = 0xa0,
+ ENCODING_TYPE_FIXSTR_MAX = 0xbf,
+ ENCODING_TYPE_FIXSTR_MASK = 0x1f,
+ ENCODING_TYPE_NIL = 0xc0,
+ ENCODING_TYPE_RESERVED = 0xc1,
+ ENCODING_TYPE_FALSE = 0xc2,
+ ENCODING_TYPE_TRUE = 0xc3,
+ ENCODING_TYPE_BIN8 = 0xc4,
+ ENCODING_TYPE_BIN16 = 0xc5,
+ ENCODING_TYPE_BIN32 = 0xc6,
+ ENCODING_TYPE_EXT8 = 0xc7,
+ ENCODING_TYPE_EXT16 = 0xc8,
+ ENCODING_TYPE_EXT32 = 0xc9,
+ ENCODING_TYPE_FLOAT32 = 0xca,
+ ENCODING_TYPE_FLOAT64 = 0xcb,
+ ENCODING_TYPE_UINT8 = 0xcc,
+ ENCODING_TYPE_UINT16 = 0xcd,
+ ENCODING_TYPE_UINT32 = 0xce,
+ ENCODING_TYPE_UINT64 = 0xcf,
+ ENCODING_TYPE_INT8 = 0xd0,
+ ENCODING_TYPE_INT16 = 0xd1,
+ ENCODING_TYPE_INT32 = 0xd2,
+ ENCODING_TYPE_INT64 = 0xd3,
+ ENCODING_TYPE_FIXEXT1 = 0xd4,
+ ENCODING_TYPE_FIXEXT2 = 0xd5,
+ ENCODING_TYPE_FIXEXT4 = 0xd6,
+ ENCODING_TYPE_FIXEXT8 = 0xd7,
+ ENCODING_TYPE_FIXEXT16 = 0xd8,
+ ENCODING_TYPE_STR8 = 0xd9,
+ ENCODING_TYPE_STR16 = 0xda,
+ ENCODING_TYPE_STR32 = 0xdb,
+ ENCODING_TYPE_ARRAY16 = 0xdc,
+ ENCODING_TYPE_ARRAY32 = 0xdd,
+ ENCODING_TYPE_MAP16 = 0xde,
+ ENCODING_TYPE_MAP32 = 0xdf,
+ ENCODING_TYPE_NEGATIVE_FIXINT = 0xe0,
+ ENCODING_TYPE_NEGATIVE_FIXINT_MIN = 0xe0,
+ ENCODING_TYPE_NEGATIVE_FIXINT_MAX = 0xff,
+};
+
+// Base encoding classes grouping multi-strategy encodings.
+enum EncodingClass {
+ ENCODING_CLASS_BOOL,
+ ENCODING_CLASS_NIL,
+ ENCODING_CLASS_INT,
+ ENCODING_CLASS_UINT,
+ ENCODING_CLASS_FLOAT,
+ ENCODING_CLASS_ARRAY,
+ ENCODING_CLASS_MAP,
+ ENCODING_CLASS_STRING,
+ ENCODING_CLASS_BINARY,
+ ENCODING_CLASS_EXTENSION,
+};
+
+// Encoding prefixes are unsigned bytes.
+typedef std::uint8_t EncodingType;
+
+// Extension encoding types defined by this library.
+enum EncodingExtType : int8_t {
+ ENCODING_EXT_TYPE_FILE_DESCRIPTOR,
+ ENCODING_EXT_TYPE_CHANNEL_HANDLE,
+};
+
+// Encoding predicates. Determines whether the given encoding is of a specific
+// type.
+inline constexpr bool IsFixintEncoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsUnsignedFixintEncoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsInt8Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+ case ENCODING_TYPE_INT8:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsUInt8Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_UINT8:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsInt16Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+ case ENCODING_TYPE_INT8:
+ case ENCODING_TYPE_INT16:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsUInt16Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_UINT8:
+ case ENCODING_TYPE_UINT16:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsInt32Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+ case ENCODING_TYPE_INT8:
+ case ENCODING_TYPE_INT16:
+ case ENCODING_TYPE_INT32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsUInt32Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_UINT8:
+ case ENCODING_TYPE_UINT16:
+ case ENCODING_TYPE_UINT32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsInt64Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+ case ENCODING_TYPE_INT8:
+ case ENCODING_TYPE_INT16:
+ case ENCODING_TYPE_INT32:
+ case ENCODING_TYPE_INT64:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsUInt64Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_UINT8:
+ case ENCODING_TYPE_UINT16:
+ case ENCODING_TYPE_UINT32:
+ case ENCODING_TYPE_UINT64:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsFixmapEncoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FIXMAP_MIN ... ENCODING_TYPE_FIXMAP_MAX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsFixarrayEncoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FIXARRAY_MIN ... ENCODING_TYPE_FIXARRAY_MAX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsFixstrEncoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FIXSTR_MIN ... ENCODING_TYPE_FIXSTR_MAX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsFixextEncoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FIXEXT1:
+ case ENCODING_TYPE_FIXEXT2:
+ case ENCODING_TYPE_FIXEXT4:
+ case ENCODING_TYPE_FIXEXT8:
+ case ENCODING_TYPE_FIXEXT16:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsFloat32Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FLOAT32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsFloat64Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FLOAT32:
+ case ENCODING_TYPE_FLOAT64:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsBoolEncoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FALSE:
+ case ENCODING_TYPE_TRUE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr std::size_t GetFixstrSize(EncodingType encoding) {
+ return encoding & ENCODING_TYPE_FIXSTR_MASK;
+}
+
+inline constexpr std::size_t GetFixarraySize(EncodingType encoding) {
+ return encoding & ENCODING_TYPE_FIXARRAY_MASK;
+}
+
+inline constexpr std::size_t GetFixmapSize(EncodingType encoding) {
+ return encoding & ENCODING_TYPE_FIXMAP_MASK;
+}
+
+inline constexpr std::size_t GetFixextSize(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FIXEXT1:
+ return 1;
+ case ENCODING_TYPE_FIXEXT2:
+ return 2;
+ case ENCODING_TYPE_FIXEXT4:
+ return 4;
+ case ENCODING_TYPE_FIXEXT8:
+ return 8;
+ case ENCODING_TYPE_FIXEXT16:
+ return 16;
+ default:
+ return 0; // Invalid fixext size.
+ }
+}
+
+// Gets the size of the encoding in bytes, not including external payload data.
+inline constexpr std::size_t GetEncodingSize(EncodingType encoding) {
+ switch (encoding) {
+ // Encoding is fully contained within the type value.
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+ case ENCODING_TYPE_FIXMAP_MIN ... ENCODING_TYPE_FIXMAP_MAX:
+ case ENCODING_TYPE_FIXARRAY_MIN ... ENCODING_TYPE_FIXARRAY_MAX:
+ case ENCODING_TYPE_FIXSTR_MIN ... ENCODING_TYPE_FIXSTR_MAX:
+ case ENCODING_TYPE_NIL:
+ case ENCODING_TYPE_RESERVED:
+ case ENCODING_TYPE_FALSE:
+ case ENCODING_TYPE_TRUE:
+ return 1;
+
+ // Encoding type followed by one-byte size or immediate value.
+ case ENCODING_TYPE_BIN8:
+ case ENCODING_TYPE_EXT8:
+ case ENCODING_TYPE_UINT8:
+ case ENCODING_TYPE_INT8:
+ case ENCODING_TYPE_STR8:
+ // Encoding type followed by one-byte extension type.
+ case ENCODING_TYPE_FIXEXT1:
+ case ENCODING_TYPE_FIXEXT2:
+ case ENCODING_TYPE_FIXEXT4:
+ case ENCODING_TYPE_FIXEXT8:
+ case ENCODING_TYPE_FIXEXT16:
+ return 2;
+
+ // Encoding type followed by two-byte size or immediate value.
+ case ENCODING_TYPE_BIN16:
+ case ENCODING_TYPE_EXT16:
+ case ENCODING_TYPE_UINT16:
+ case ENCODING_TYPE_INT16:
+ case ENCODING_TYPE_STR16:
+ case ENCODING_TYPE_ARRAY16:
+ case ENCODING_TYPE_MAP16:
+ return 3;
+
+ // Encoding type followed by four-byte size or immediate value.
+ case ENCODING_TYPE_BIN32:
+ case ENCODING_TYPE_EXT32:
+ case ENCODING_TYPE_FLOAT32:
+ case ENCODING_TYPE_UINT32:
+ case ENCODING_TYPE_INT32:
+ case ENCODING_TYPE_STR32:
+ case ENCODING_TYPE_ARRAY32:
+ case ENCODING_TYPE_MAP32:
+ return 5;
+
+ // Encoding type followed by eight-byte immediate value.
+ case ENCODING_TYPE_FLOAT64:
+ case ENCODING_TYPE_UINT64:
+ case ENCODING_TYPE_INT64:
+ return 9;
+
+ default:
+ return 0;
+ }
+}
+
+// Encoding for standard types. Each supported data type has an associated
+// encoding or set of encodings. These functions determine the MessagePack
+// encoding based on the data type, value, and size of their arguments.
+
+inline constexpr EncodingType EncodeArrayType(std::size_t size) {
+ if (size < (1U << 4))
+ return ENCODING_TYPE_FIXARRAY | (size & ENCODING_TYPE_FIXARRAY_MASK);
+ else if (size < (1U << 16))
+ return ENCODING_TYPE_ARRAY16;
+ else
+ return ENCODING_TYPE_ARRAY32;
+}
+
+inline constexpr EncodingType EncodeMapType(std::size_t size) {
+ if (size < (1U << 4))
+ return ENCODING_TYPE_FIXMAP | (size & ENCODING_TYPE_FIXMAP_MASK);
+ else if (size < (1U << 16))
+ return ENCODING_TYPE_MAP16;
+ else
+ return ENCODING_TYPE_MAP32;
+}
+
+inline constexpr EncodingType EncodeStringType(std::size_t size) {
+ if (size < (1U << 5))
+ return ENCODING_TYPE_FIXSTR | (size & ENCODING_TYPE_FIXSTR_MASK);
+ else if (size < (1U << 8))
+ return ENCODING_TYPE_STR8;
+ else if (size < (1U << 16))
+ return ENCODING_TYPE_STR16;
+ else
+ return ENCODING_TYPE_STR32;
+}
+
+inline constexpr EncodingType EncodeBinType(std::size_t size) {
+ if (size < (1U << 8))
+ return ENCODING_TYPE_BIN8;
+ else if (size < (1U << 16))
+ return ENCODING_TYPE_BIN16;
+ else
+ return ENCODING_TYPE_BIN32;
+}
+
+inline EncodingType EncodeType(const EmptyVariant& /*empty*/) {
+ return ENCODING_TYPE_NIL;
+}
+
+// Variant is encoded as a single-element map, with the type index as the key.
+template <typename... Types>
+inline EncodingType EncodeType(const Variant<Types...>& /*variant*/) {
+ return EncodeMapType(1);
+}
+
+template <typename T>
+inline constexpr EncodingType EncodeType(const StringWrapper<T>& value) {
+ return EncodeStringType(value.length());
+}
+
+inline constexpr EncodingType EncodeType(const std::string& value) {
+ return EncodeStringType(value.length());
+}
+
+template <typename T, std::size_t Size>
+inline constexpr EncodingType EncodeType(const std::array<T, Size>& /*value*/) {
+ return EncodeArrayType(Size);
+}
+
+template <typename T>
+inline constexpr EncodingType EncodeType(const ArrayWrapper<T>& value) {
+ return EncodeArrayType(value.size());
+}
+
+template <typename T, typename Allocator>
+inline constexpr EncodingType EncodeType(
+ const std::vector<T, Allocator>& value) {
+ return EncodeArrayType(value.size());
+}
+
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline constexpr EncodingType EncodeType(
+ const std::map<Key, T, Compare, Allocator>& value) {
+ return EncodeMapType(value.size());
+}
+
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline constexpr EncodingType EncodeType(
+ const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& value) {
+ return EncodeMapType(value.size());
+}
+
+template <typename T>
+inline constexpr EncodingType EncodeType(const BufferWrapper<T>& value) {
+ // BIN size is in bytes.
+ return EncodeBinType(value.size() *
+ sizeof(typename BufferWrapper<T>::value_type));
+}
+
+template <typename T, typename U>
+inline constexpr EncodingType EncodeType(const std::pair<T, U>& /*value*/) {
+ return EncodeArrayType(2);
+}
+
+template <typename... T>
+inline constexpr EncodingType EncodeType(const std::tuple<T...>& /*value*/) {
+ return EncodeArrayType(sizeof...(T));
+}
+
+// FileHandle is encoded as a FIXEXT2 with a type code for "FileDescriptor"
+// and a signed 16-bit index into the pushed fd array. Empty file descriptor
+// have an array index of -1.
+template <FileHandleMode Mode>
+inline constexpr EncodingType EncodeType(const FileHandle<Mode>& /*fd*/) {
+ return ENCODING_TYPE_FIXEXT2;
+}
+
+// ChannelHandle is encoded as a FIXEXT4 with a type of
+// ENCODING_EXT_TYPE_CHANNEL_HANDLE and a signed 32-bit value representing
+// a client channel in a remote process. Empty handle has a value of -1.
+template <ChannelHandleMode Mode>
+inline constexpr EncodingType EncodeType(
+ const ChannelHandle<Mode>& /*handle*/) {
+ return ENCODING_TYPE_FIXEXT4;
+}
+
+inline constexpr EncodingType EncodeType(const bool& value) {
+ return value ? ENCODING_TYPE_TRUE : ENCODING_TYPE_FALSE;
+}
+
+// Type 'char' is a little bit special in that it is distinct from 'signed char'
+// and 'unsigned char'. Treating it as an unsigned 8-bit value is safe for
+// encoding purposes and nicely handles 7-bit ASCII encodings as FIXINT.
+inline constexpr EncodingType EncodeType(const char& value) {
+ if (value < static_cast<char>(1 << 7))
+ return value;
+ else
+ return ENCODING_TYPE_UINT8;
+}
+
+inline constexpr EncodingType EncodeType(const uint8_t& value) {
+ if (value < (1U << 7))
+ return value;
+ else
+ return ENCODING_TYPE_UINT8;
+}
+inline constexpr EncodingType EncodeType(const int8_t& value) {
+ if (value >= -32)
+ return value;
+ else
+ return ENCODING_TYPE_INT8;
+}
+inline constexpr EncodingType EncodeType(const uint16_t& value) {
+ if (value < (1U << 7))
+ return static_cast<EncodingType>(value);
+ else if (value < (1U << 8))
+ return ENCODING_TYPE_UINT8;
+ else
+ return ENCODING_TYPE_UINT16;
+}
+inline constexpr EncodingType EncodeType(const int16_t& value) {
+ if (value >= -32 && value <= 127)
+ return static_cast<EncodingType>(value);
+ else if (value >= -128 && value <= 127)
+ return ENCODING_TYPE_INT8;
+ else
+ return ENCODING_TYPE_INT16;
+}
+inline constexpr EncodingType EncodeType(const uint32_t& value) {
+ if (value < (1U << 7))
+ return static_cast<EncodingType>(value);
+ else if (value < (1U << 8))
+ return ENCODING_TYPE_UINT8;
+ else if (value < (1U << 16))
+ return ENCODING_TYPE_UINT16;
+ else
+ return ENCODING_TYPE_UINT32;
+}
+inline constexpr EncodingType EncodeType(const int32_t& value) {
+ if (value >= -32 && value <= 127)
+ return static_cast<EncodingType>(value);
+ else if (value >= -128 && value <= 127)
+ return ENCODING_TYPE_INT8;
+ else if (value >= -32768 && value <= 32767)
+ return ENCODING_TYPE_INT16;
+ else
+ return ENCODING_TYPE_INT32;
+}
+inline constexpr EncodingType EncodeType(const uint64_t& value) {
+ if (value < (1ULL << 7))
+ return static_cast<EncodingType>(value);
+ else if (value < (1ULL << 8))
+ return ENCODING_TYPE_UINT8;
+ else if (value < (1ULL << 16))
+ return ENCODING_TYPE_UINT16;
+ else if (value < (1ULL << 32))
+ return ENCODING_TYPE_UINT32;
+ else
+ return ENCODING_TYPE_UINT64;
+}
+inline constexpr EncodingType EncodeType(const int64_t& value) {
+ if (value >= -32 && value <= 127)
+ return static_cast<EncodingType>(value);
+ else if (value >= -128 && value <= 127) // Effectively [-128, -32).
+ return ENCODING_TYPE_INT8;
+ else if (value >= -32768 && value <= 32767)
+ return ENCODING_TYPE_INT16;
+ else if (value >= -2147483648 && value <= 2147483647)
+ return ENCODING_TYPE_INT32;
+ else
+ return ENCODING_TYPE_INT64;
+}
+
+inline constexpr EncodingType EncodeType(const float& /*value*/) {
+ return ENCODING_TYPE_FLOAT32;
+}
+
+inline constexpr EncodingType EncodeType(const double& /*value*/) {
+ return ENCODING_TYPE_FLOAT64;
+}
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_ENCODING_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/enumeration.h b/libs/vr/libpdx/private/pdx/rpc/enumeration.h
new file mode 100644
index 0000000..7a35d31
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/enumeration.h
@@ -0,0 +1,65 @@
+#ifndef ANDROID_PDX_RPC_ENUMERATION_H_
+#define ANDROID_PDX_RPC_ENUMERATION_H_
+
+#include <pdx/rpc/sequence.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility for manipulating lists of types. Provides operations to lookup an
+// element by type or index.
+
+namespace detail {
+
+// Helper type that captures type and index for each element of a type
+// enumeration.
+template <std::size_t I, typename T>
+struct IndexedElement {
+ using Type = T;
+ static constexpr std::size_t Index = I;
+};
+
+// Helper type that captures an IndexSequence and corresponding list of types.
+template <typename Is, typename... Ts>
+struct ElementIndexer;
+
+// Partial specialization that generates an instantiation of IndexElement<I, T>
+// for each element of a type enumeration using inheritance. Once a type
+// enumeration is instantiated this way the compiler is able to deduce either I
+// or T from the other using the method below.
+template <std::size_t... Is, typename... Ts>
+struct ElementIndexer<IndexSequence<Is...>, Ts...> : IndexedElement<Is, Ts>... {
+};
+
+// Helper function that causes the compiler to deduce an IndexedElement<I, T>
+// given T.
+template <typename T, std::size_t I>
+static IndexedElement<I, T> SelectElementByType(IndexedElement<I, T>);
+
+// Helper function that causes the compiler to deduce an IndexedElement<I, T>
+// given I.
+template <std::size_t I, typename T>
+static IndexedElement<I, T> SelectElementByIndex(IndexedElement<I, T>);
+
+} // namespace detail
+
+// Deduces the IndexedElement<I, T> given T and a type sequence Ts. This may be
+// used to determine the index of T within Ts at compile time.
+template <typename T, typename... Ts>
+using ElementForType = decltype(detail::SelectElementByType<T>(
+ detail::ElementIndexer<typename IndexSequenceFor<Ts...>::type, Ts...>{}));
+
+// Deduces the IndexedElement<I, T> given I and a type sequence Ts. This may be
+// used to determine the type of the element at index I within Ts at compile
+// time. Tuple operations may also be used to accomplish the same task, however
+// this implementation is provided here for symmetry.
+template <std::size_t I, typename... Ts>
+using ElementForIndex = decltype(detail::SelectElementByIndex<I>(
+ detail::ElementIndexer<typename IndexSequenceFor<Ts...>::type, Ts...>{}));
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_ENUMERATION_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/find_replace.h b/libs/vr/libpdx/private/pdx/rpc/find_replace.h
new file mode 100644
index 0000000..b4b086b
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/find_replace.h
@@ -0,0 +1,45 @@
+#ifndef ANDROID_PDX_RPC_FIND_REPLACE_H_
+#define ANDROID_PDX_RPC_FIND_REPLACE_H_
+
+#include <type_traits>
+
+#include <pdx/rpc/copy_cv_reference.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility class to capture types to find and replace.
+template <typename Find, typename Replace>
+struct FindReplace;
+
+template <typename T, typename U>
+using IsSameBaseType = typename std::is_same<typename std::decay<T>::type,
+ typename std::decay<U>::type>;
+
+// Replaces the type Subject with type Replace if type Subject is the same type
+// as type Find, excluding cv-reference qualifiers in the match.
+template <typename Find, typename Replace, typename Subject>
+using ReplaceType =
+ typename std::conditional<IsSameBaseType<Find, Subject>::value,
+ CopyCVReferenceType<Subject, Replace>,
+ Subject>::type;
+
+// Determines whether the type Find (excluding cv-reference qualifiers) is in
+// the given parameter pack.
+template <typename Find, typename... Types>
+struct ContainsType : std::true_type {};
+
+template <typename Find, typename First, typename... Rest>
+struct ContainsType<Find, First, Rest...>
+ : std::conditional<IsSameBaseType<Find, First>::value, std::true_type,
+ ContainsType<Find, Rest...>>::type {};
+
+template <typename Find>
+struct ContainsType<Find> : std::false_type {};
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_FIND_REPLACE_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/function_traits.h b/libs/vr/libpdx/private/pdx/rpc/function_traits.h
new file mode 100644
index 0000000..5fdad72
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/function_traits.h
@@ -0,0 +1,55 @@
+#ifndef ANDROID_PDX_RPC_FUNCTION_TRAITS_H_
+#define ANDROID_PDX_RPC_FUNCTION_TRAITS_H_
+
+#include <type_traits>
+
+#include <pdx/rpc/type_operators.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility type to capture return and argument types of a function signature.
+// Examples:
+// typedef SignatureType<int(int)> SignatureType;
+// using SignatureType = SignatureType<int(int)>;
+template <typename T>
+using SignatureType = T;
+
+// Utility class to extract return and argument types from function types.
+// Provides nested types for return value, arguments, and full signature. Also
+// provides accessor types for individual arguments, argument-arity, and type
+// substitution.
+template <typename T>
+struct FunctionTraits;
+
+template <typename Return_, typename... Args_>
+struct FunctionTraits<Return_(Args_...)> {
+ using Return = Return_;
+ using Args = std::tuple<Args_...>;
+ using Signature = SignatureType<Return_(Args_...)>;
+
+ enum : std::size_t { Arity = sizeof...(Args_) };
+
+ template <std::size_t Index>
+ using Arg = typename std::tuple_element<Index, Args>::type;
+
+ template <typename... Params>
+ using RewriteArgs =
+ SignatureType<Return_(ConditionalRewrite<Args_, Params>...)>;
+
+ template <typename ReturnType, typename... Params>
+ using RewriteSignature =
+ SignatureType<ConditionalRewrite<Return_, ReturnType>(
+ ConditionalRewrite<Args_, Params>...)>;
+
+ template <typename ReturnType>
+ using RewriteReturn =
+ SignatureType<ConditionalRewrite<Return_, ReturnType>(Args_...)>;
+};
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_FUNCTION_TRAITS_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/macros.h b/libs/vr/libpdx/private/pdx/rpc/macros.h
new file mode 100644
index 0000000..aeae9d3
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/macros.h
@@ -0,0 +1,148 @@
+#ifndef ANDROID_PDX_RPC_MACROS_H_
+#define ANDROID_PDX_RPC_MACROS_H_
+
+// Macros to apply other macros over all elements in a list.
+//
+// For example, for a macro A(x) and B(x, y):
+// - FOR_EACH(A, 1, 2, 3) -> A(1) A(2) A(3).
+// - FOR_EACH_BINARY(B, z, 1, 2, 3) -> B(z, 1) B(z, 2) B(z, 3)
+// - FOR_EACH_LIST(A, 1, 2, 3) -> A(1), B(2), C(3)
+// - FOR_EACH_BINARY_LIST(B, z, 1, 2, 3) -> B(z, 1), B(z, 2), B(z, 3)
+//
+// Empty lists are supported and will produce no output.
+
+// Recursive expansion macros.
+#define _PDX_EXPAND0(...) __VA_ARGS__
+#define _PDX_EXPAND1(...) _PDX_EXPAND0(_PDX_EXPAND0(_PDX_EXPAND0(__VA_ARGS__)))
+#define _PDX_EXPAND2(...) _PDX_EXPAND1(_PDX_EXPAND1(_PDX_EXPAND1(__VA_ARGS__)))
+#define _PDX_EXPAND3(...) _PDX_EXPAND2(_PDX_EXPAND2(_PDX_EXPAND2(__VA_ARGS__)))
+#define _PDX_EXPAND4(...) _PDX_EXPAND3(_PDX_EXPAND3(_PDX_EXPAND3(__VA_ARGS__)))
+#define _PDX_EXPAND(...) _PDX_EXPAND4(_PDX_EXPAND4(_PDX_EXPAND4(__VA_ARGS__)))
+
+// Required to workaround a bug in the VC++ preprocessor.
+#define _PDX_INDIRECT_EXPAND(macro, args) macro args
+
+// Defines a step separation for macro expansion.
+#define _PDX_SEPARATOR
+
+// Clears any remaining contents wrapped in parentheses.
+#define _PDX_CLEAR(...)
+
+// Introduces a first dummy argument and _PDX_CLEAR as second argument.
+#define _PDX_CLEAR_IF_LAST() _, _PDX_CLEAR
+
+// Returns the first argument of a list.
+#define _PDX_FIRST_ARG(first, ...) first
+
+// Returns the second argument of a list.
+#define _PDX_SECOND_ARG(_, second, ...) second
+
+// Expands the arguments and introduces a separator.
+#define _PDX_EXPAND_NEXT_FUNC(_, next_func, ...) \
+ _PDX_INDIRECT_EXPAND(_PDX_SECOND_ARG, (_, next_func)) \
+ _PDX_SEPARATOR
+
+// Returns next_func if the next element is not (), or _PDX_CLEAR
+// otherwise.
+//
+// _PDX_CLEAR_IF_LAST inserts an extra first dummy argument if peek is ().
+#define _PDX_NEXT_FUNC(next_element, next_func) \
+ _PDX_EXPAND_NEXT_FUNC(_PDX_CLEAR_IF_LAST next_element, next_func)
+
+// Macros for the unary version of PDX_FOR_EACH.
+
+// Applies the unary macro. Duplicated for macro recursive expansion.
+#define _PDX_APPLY_1(macro, head, next, ...) \
+ macro(head) _PDX_NEXT_FUNC(next, _PDX_APPLY_2)(macro, next, __VA_ARGS__)
+
+// Applies the unary macro. Duplicated for macro recursive expansion.
+#define _PDX_APPLY_2(macro, head, next, ...) \
+ macro(head) _PDX_NEXT_FUNC(next, _PDX_APPLY_1)(macro, next, __VA_ARGS__)
+
+// Stops expansion if __VA_ARGS__ is empty, calling _PDX_APPLY_1
+// otherwise.
+#define _PDX_HANDLE_EMPTY_ARGS(macro, ...) \
+ _PDX_NEXT_FUNC(_PDX_FIRST_ARG(__VA_ARGS__()), _PDX_APPLY_1) \
+ (macro, __VA_ARGS__, ())
+
+// Applies a unary macro over all the elements in a list.
+#define PDX_FOR_EACH(macro, ...) \
+ _PDX_EXPAND(_PDX_HANDLE_EMPTY_ARGS(macro, __VA_ARGS__))
+
+// Applies the unary macro at the end of a list. Duplicated for macro recursive
+// expansion.
+#define _PDX_APPLY_LIST_1(macro, head, next, ...) \
+ , macro(head) \
+ _PDX_NEXT_FUNC(next, _PDX_APPLY_LIST_2)(macro, next, __VA_ARGS__)
+
+// Applies the unary macro at the end of a list. Duplicated for macro recursive
+// expansion.
+#define _PDX_APPLY_LIST_2(macro, head, next, ...) \
+ , macro(head) \
+ _PDX_NEXT_FUNC(next, _PDX_APPLY_LIST_1)(macro, next, __VA_ARGS__)
+
+// Applies the unary macro at the start of a list.
+#define _PDX_APPLY_LIST_0(macro, head, next, ...) \
+ macro(head) _PDX_NEXT_FUNC(next, _PDX_APPLY_LIST_1)(macro, next, __VA_ARGS__)
+
+// Stops expansion if __VA_ARGS__ is empty, calling _PDX_APPLY_LIST_0
+// otherwise.
+#define _PDX_HANDLE_EMPTY_LIST(macro, ...) \
+ _PDX_NEXT_FUNC(_PDX_FIRST_ARG(__VA_ARGS__()), _PDX_APPLY_LIST_0) \
+ (macro, __VA_ARGS__, ())
+
+// Applies a unary macro over all the elements in a list.
+#define PDX_FOR_EACH_LIST(macro, ...) \
+ _PDX_EXPAND(_PDX_HANDLE_EMPTY_LIST(macro, __VA_ARGS__))
+
+// Macros for the binary version of PDX_FOR_EACH.
+
+// Applies the binary macro. Duplicated for macro recursive expansion.
+#define _PDX_APPLY_BINARY_1(macro, arg, head, next, ...) \
+ macro(arg, head) \
+ _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_2)(macro, arg, next, __VA_ARGS__)
+
+// Applies the binary macro. Duplicated for macro recursive expansion.
+#define _PDX_APPLY_BINARY_2(macro, arg, head, next, ...) \
+ macro(arg, head) \
+ _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_1)(macro, arg, next, __VA_ARGS__)
+
+// Version of _PDX_HANDLE_EMPTY_ARGS that takes 1 fixed argument for a
+// binary macro.
+#define _PDX_HANDLE_EMPTY_ARGS_BINARY(macro, arg, ...) \
+ _PDX_NEXT_FUNC(_PDX_FIRST_ARG(__VA_ARGS__()), _PDX_APPLY_BINARY_1) \
+ (macro, arg, __VA_ARGS__, ())
+
+// Applies a binary macro over all the elements in a list and a given argument.
+#define PDX_FOR_EACH_BINARY(macro, arg, ...) \
+ _PDX_EXPAND(_PDX_HANDLE_EMPTY_ARGS_BINARY(macro, arg, __VA_ARGS__))
+
+// Applies the binary macro at the end of a list. Duplicated for macro recursive
+// expansion.
+#define _PDX_APPLY_BINARY_LIST_1(macro, arg, head, next, ...) \
+ , macro(arg, head) _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_LIST_2)( \
+ macro, arg, next, __VA_ARGS__)
+
+// Applies the binary macro at the end of a list. Duplicated for macro recursive
+// expansion.
+#define _PDX_APPLY_BINARY_LIST_2(macro, arg, head, next, ...) \
+ , macro(arg, head) _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_LIST_1)( \
+ macro, arg, next, __VA_ARGS__)
+
+// Applies the binary macro at the start of a list. Duplicated for macro
+// recursive expansion.
+#define _PDX_APPLY_BINARY_LIST_0(macro, arg, head, next, ...) \
+ macro(arg, head) _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_LIST_1)( \
+ macro, arg, next, __VA_ARGS__)
+
+// Version of _PDX_HANDLE_EMPTY_LIST that takes 1 fixed argument for a
+// binary macro.
+#define _PDX_HANDLE_EMPTY_LIST_BINARY(macro, arg, ...) \
+ _PDX_NEXT_FUNC(_PDX_FIRST_ARG(__VA_ARGS__()), _PDX_APPLY_BINARY_LIST_0) \
+ (macro, arg, __VA_ARGS__, ())
+
+// Applies a binary macro over all the elements in a list and a given argument.
+#define PDX_FOR_EACH_BINARY_LIST(macro, arg, ...) \
+ _PDX_EXPAND(_PDX_HANDLE_EMPTY_LIST_BINARY(macro, arg, __VA_ARGS__))
+
+#endif // ANDROID_PDX_RPC_MACROS_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/message_buffer.h b/libs/vr/libpdx/private/pdx/rpc/message_buffer.h
new file mode 100644
index 0000000..ba4e86e
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/message_buffer.h
@@ -0,0 +1,22 @@
+#ifndef ANDROID_PDX_RPC_MESSAGE_BUFFER_H_
+#define ANDROID_PDX_RPC_MESSAGE_BUFFER_H_
+
+#include <pdx/rpc/thread_local_buffer.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility type for thread-local buffers, providing suitable defaults for most
+// situations. Independent thread-local buffers may be created by using
+// different types for Slot -- ThreadLocalSlot, ThreadLocalTypedSlot and
+// ThreadLocalIndexedSlot provide utilities for building these types.
+template <typename Slot, std::size_t Capacity = 4096, typename T = std::uint8_t,
+ typename Allocator = DefaultInitializationAllocator<T>>
+using MessageBuffer = ThreadLocalBuffer<T, Allocator, Capacity, Slot>;
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_MESSAGE_BUFFER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/payload.h b/libs/vr/libpdx/private/pdx/rpc/payload.h
new file mode 100644
index 0000000..a48a64c
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/payload.h
@@ -0,0 +1,157 @@
+#ifndef ANDROID_PDX_RPC_PAYLOAD_H_
+#define ANDROID_PDX_RPC_PAYLOAD_H_
+
+#include <iterator>
+
+#include <pdx/client.h>
+#include <pdx/rpc/message_buffer.h>
+#include <pdx/service.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Implements the payload interface, required by Serialize/Deserialize, on top
+// of a thread-local MessageBuffer.
+template <typename Slot>
+class MessagePayload {
+ public:
+ using BufferType = typename MessageBuffer<Slot>::BufferType;
+ using ValueType = typename MessageBuffer<Slot>::ValueType;
+
+ // Constructs a MessagePayload with an empty TLS buffer.
+ MessagePayload()
+ : buffer_(MessageBuffer<Slot>::GetEmptyBuffer()),
+ cursor_(buffer_.begin()),
+ const_cursor_(buffer_.cbegin()) {}
+
+ // Returns a reference to the cursor iterator to be used during serialization
+ // into the underlying MessageBuffer.
+ typename BufferType::iterator& Cursor() { return cursor_; }
+
+ // Returns a reference to the const cursor iterator at the beginning of the
+ // underlying MessageBuffer.
+ typename BufferType::const_iterator& ConstCursor() { return const_cursor_; }
+
+ // Returns a const iterator marking the end of the underlying MessageBuffer.
+ typename BufferType::const_iterator ConstEnd() { return buffer_.cend(); }
+
+ // Resizes the underlying MessageBuffer and sets the cursor to the beginning.
+ void Resize(std::size_t size) {
+ buffer_.resize(size);
+ cursor_ = buffer_.begin();
+ const_cursor_ = buffer_.cbegin();
+ }
+
+ // Resets the read cursor so that data can be read from the buffer again.
+ void Rewind() { const_cursor_ = buffer_.cbegin(); }
+
+ // Adds |size| bytes to the size of the underlying MessageBuffer and positions
+ // the cursor at the beginning of the extended region.
+ void Extend(std::size_t size) {
+ const std::size_t offset = buffer_.size();
+ buffer_.resize(offset + size);
+ cursor_ = buffer_.begin() + offset;
+ const_cursor_ = buffer_.cbegin() + offset;
+ }
+
+ // Clears the underlying MessageBuffer and sets the cursor to the beginning.
+ void Clear() {
+ buffer_.clear();
+ cursor_ = buffer_.begin();
+ const_cursor_ = buffer_.cbegin();
+ }
+
+ ValueType* Data() { return buffer_.data(); }
+ const ValueType* Data() const { return buffer_.data(); }
+ std::size_t Size() const { return buffer_.size(); }
+ std::size_t Capacity() const { return buffer_.capacity(); }
+
+ private:
+ BufferType& buffer_;
+ typename BufferType::iterator cursor_;
+ typename BufferType::const_iterator const_cursor_;
+
+ MessagePayload(const MessagePayload<Slot>&) = delete;
+ void operator=(const MessagePayload<Slot>&) = delete;
+};
+
+// Implements the payload interface for service-side RPCs. Handles translating
+// between remote and local handle spaces automatically.
+template <typename Slot>
+class ServicePayload : public MessagePayload<Slot>,
+ public MessageWriter,
+ public MessageReader {
+ public:
+ ServicePayload(Message& message) : message_(message) {}
+
+ // MessageWriter
+ void* GetNextWriteBufferSection(size_t size) override {
+ this->Extend(size);
+ return &*this->Cursor();
+ }
+
+ OutputResourceMapper* GetOutputResourceMapper() override { return &message_; }
+
+ // MessageReader
+ BufferSection GetNextReadBufferSection() override {
+ return {&*this->ConstCursor(), &*this->ConstEnd()};
+ }
+
+ void ConsumeReadBufferSectionData(const void* new_start) override {
+ std::advance(this->ConstCursor(),
+ PointerDistance(new_start, &*this->ConstCursor()));
+ }
+
+ InputResourceMapper* GetInputResourceMapper() override { return &message_; }
+
+ private:
+ Message& message_;
+};
+
+// Implements the payload interface for client-side RPCs. Handles gathering file
+// handles to be sent over IPC automatically.
+template <typename Slot>
+class ClientPayload : public MessagePayload<Slot>,
+ public MessageWriter,
+ public MessageReader {
+ public:
+ using ContainerType =
+ MessageBuffer<ThreadLocalTypeSlot<ClientPayload<Slot>>, 1024u, int>;
+ using BufferType = typename ContainerType::BufferType;
+
+ ClientPayload(Transaction& transaction) : transaction_{transaction} {}
+
+ // MessageWriter
+ void* GetNextWriteBufferSection(size_t size) override {
+ this->Extend(size);
+ return &*this->Cursor();
+ }
+
+ OutputResourceMapper* GetOutputResourceMapper() override {
+ return &transaction_;
+ }
+
+ // MessageReader
+ BufferSection GetNextReadBufferSection() override {
+ return {&*this->ConstCursor(), &*this->ConstEnd()};
+ }
+
+ void ConsumeReadBufferSectionData(const void* new_start) override {
+ std::advance(this->ConstCursor(),
+ PointerDistance(new_start, &*this->ConstCursor()));
+ }
+
+ InputResourceMapper* GetInputResourceMapper() override {
+ return &transaction_;
+ }
+
+ private:
+ Transaction& transaction_;
+};
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_PAYLOAD_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/pointer_wrapper.h b/libs/vr/libpdx/private/pdx/rpc/pointer_wrapper.h
new file mode 100644
index 0000000..d496719
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/pointer_wrapper.h
@@ -0,0 +1,38 @@
+#ifndef ANDROID_PDX_RPC_POINTER_WRAPPER_H_
+#define ANDROID_PDX_RPC_POINTER_WRAPPER_H_
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Wrapper class for pointers to any serializable type. This class is used by
+// serialization/deserialization to handle pointers to objects that are to be
+// serialized or deserialized.
+template <typename T>
+class PointerWrapper {
+ public:
+ using BaseType = T;
+
+ PointerWrapper(T* pointer) : pointer_(pointer) {}
+ PointerWrapper(const PointerWrapper&) = default;
+ PointerWrapper(PointerWrapper&&) = default;
+ PointerWrapper& operator=(const PointerWrapper&) = default;
+ PointerWrapper& operator=(PointerWrapper&&) = default;
+
+ T& Dereference() { return *pointer_; }
+ const T& Dereference() const { return *pointer_; }
+
+ private:
+ T* pointer_;
+};
+
+template <typename T>
+PointerWrapper<T> WrapPointer(T* pointer) {
+ return PointerWrapper<T>(pointer);
+}
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_POINTER_WRAPPER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/remote_method.h b/libs/vr/libpdx/private/pdx/rpc/remote_method.h
new file mode 100644
index 0000000..49bee40
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/remote_method.h
@@ -0,0 +1,550 @@
+#ifndef ANDROID_PDX_RPC_REMOTE_METHOD_H_
+#define ANDROID_PDX_RPC_REMOTE_METHOD_H_
+
+#include <tuple>
+#include <type_traits>
+
+#include <pdx/client.h>
+#include <pdx/rpc/argument_encoder.h>
+#include <pdx/rpc/message_buffer.h>
+#include <pdx/rpc/payload.h>
+#include <pdx/rpc/remote_method_type.h>
+#include <pdx/service.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+#ifdef __clang__
+// Stand-in type to avoid Clang compiler bug. Clang currently has a bug where
+// performing parameter pack expansion for arguments with empty packs causes a
+// compiler crash. Provide a substitute void type and specializations/overloads
+// of CheckArgumentTypes and DispatchRemoteMethod to work around this problem.
+struct Void {};
+
+// Evaluates to true if the method type is <any>(Void), false otherwise.
+template <typename RemoteMethodType>
+using IsVoidMethod = typename std::integral_constant<
+ bool,
+ RemoteMethodType::Traits::Arity == 1 &&
+ std::is_same<typename RemoteMethodType::Traits::template Arg<0>,
+ Void>::value>;
+
+// Utility to determine if a method is of type <any>(Void).
+template <typename RemoteMethodType>
+using EnableIfVoidMethod =
+ typename std::enable_if<IsVoidMethod<RemoteMethodType>::value>::type;
+
+// Utility to determine if a method is not of type <any>(Void).
+template <typename RemoteMethodType>
+using EnableIfNotVoidMethod =
+ typename std::enable_if<!IsVoidMethod<RemoteMethodType>::value>::type;
+
+#else
+// GCC works fine with void argument types, always enable the regular
+// implementation of DispatchRemoteMethod.
+using Void = void;
+template <typename RemoteMethodType>
+using EnableIfVoidMethod = void;
+template <typename RemoteMethodType>
+using EnableIfNotVoidMethod = void;
+#endif
+
+// Helper type trait to specialize InvokeRemoteMethods for return types that
+// can be obtained directly from Transaction::Send<T>() without deserializing
+// reply payload.
+template <typename T>
+struct IsDirectReturn : std::false_type {};
+
+template <>
+struct IsDirectReturn<void> : std::true_type {};
+
+template <>
+struct IsDirectReturn<int> : std::true_type {};
+
+template <>
+struct IsDirectReturn<LocalHandle> : std::true_type {};
+
+template <>
+struct IsDirectReturn<LocalChannelHandle> : std::true_type {};
+
+template <typename Return, typename Type = void>
+using EnableIfDirectReturn =
+ typename std::enable_if<IsDirectReturn<Return>::value, Type>::type;
+
+template <typename Return, typename Type = void>
+using EnableIfNotDirectReturn =
+ typename std::enable_if<!IsDirectReturn<Return>::value, Type>::type;
+
+// Utility class to invoke a method with arguments packed in a tuple.
+template <typename Class, typename T>
+class UnpackArguments;
+
+// Utility class to invoke a method with arguments packed in a tuple.
+template <typename Class, typename Return, typename... Args>
+class UnpackArguments<Class, Return(Args...)> {
+ public:
+ using ArgsTupleType = std::tuple<typename std::decay<Args>::type...>;
+ using MethodType = Return (Class::*)(Message&, Args...);
+
+ UnpackArguments(Class& instance, MethodType method, Message& message,
+ ArgsTupleType& parameters)
+ : instance_(instance),
+ method_(method),
+ message_(message),
+ parameters_(parameters) {}
+
+ // Invokes method_ on intance_ with the packed arguments from parameters_.
+ Return Invoke() {
+ constexpr auto Arity = sizeof...(Args);
+ return static_cast<Return>(InvokeHelper(MakeIndexSequence<Arity>{}));
+ }
+
+ private:
+ Class& instance_;
+ MethodType method_;
+ Message& message_;
+ ArgsTupleType& parameters_;
+
+ template <std::size_t... Index>
+ Return InvokeHelper(IndexSequence<Index...>) {
+ return static_cast<Return>((instance_.*method_)(
+ message_,
+ std::get<Index>(std::forward<ArgsTupleType>(parameters_))...));
+ }
+
+ UnpackArguments(const UnpackArguments&) = delete;
+ void operator=(const UnpackArguments&) = delete;
+};
+
+// Returns an error code from a remote method to the client. May be called
+// either during dispatch of the remote method handler or at a later time if the
+// message is moved for delayed response.
+inline void RemoteMethodError(Message& message, int error_code) {
+ const int ret = message.ReplyError(error_code);
+ ALOGE_IF(ret < 0, "RemoteMethodError: Failed to reply to message: %s",
+ strerror(-ret));
+}
+
+// Returns a value from a remote method to the client. The usual method to
+// return a value during dispatch of a remote method is to simply use a return
+// statement in the handler. If the message is moved however, these methods may
+// be used to return a value at a later time, outside of initial dispatch.
+
+// Overload for direct return types.
+template <typename RemoteMethodType, typename Return>
+EnableIfDirectReturn<typename RemoteMethodType::Return> RemoteMethodReturn(
+ Message& message, const Return& return_value) {
+ const int ret = message.Reply(return_value);
+ ALOGE_IF(ret < 0, "RemoteMethodReturn: Failed to reply to message: %s",
+ strerror(-ret));
+}
+
+// Overload for non-direct return types.
+template <typename RemoteMethodType, typename Return>
+EnableIfNotDirectReturn<typename RemoteMethodType::Return> RemoteMethodReturn(
+ Message& message, const Return& return_value) {
+ using Signature = typename RemoteMethodType::template RewriteReturn<Return>;
+ rpc::ServicePayload<ReplyBuffer> payload(message);
+ MakeArgumentEncoder<Signature>(&payload).EncodeReturn(return_value);
+
+ int ret;
+ auto size = message.Write(payload.Data(), payload.Size());
+ if (size < static_cast<decltype(size)>(payload.Size()))
+ ret = message.ReplyError(EIO);
+ else
+ ret = message.Reply(0);
+ ALOGE_IF(ret < 0, "RemoteMethodReturn: Failed to reply to message: %s",
+ strerror(-ret));
+}
+
+// Dispatches a method by deserializing arguments from the given Message, with
+// compile-time interface check. Overload for void return types.
+template <typename RemoteMethodType, typename Class, typename... Args,
+ typename = EnableIfNotVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+ void (Class::*method)(Message&, Args...),
+ Message& message,
+ std::size_t max_capacity = InitialBufferCapacity) {
+ using Signature = typename RemoteMethodType::template RewriteArgs<Args...>;
+ rpc::ServicePayload<ReceiveBuffer> payload(message);
+ payload.Resize(max_capacity);
+
+ auto size = message.Read(payload.Data(), payload.Size());
+ if (size < 0) {
+ RemoteMethodError(message, -size);
+ return;
+ }
+
+ payload.Resize(size);
+
+ ErrorType error;
+ auto decoder = MakeArgumentDecoder<Signature>(&payload);
+ auto arguments = decoder.DecodeArguments(&error);
+ if (error) {
+ RemoteMethodError(message, EIO);
+ return;
+ }
+
+ UnpackArguments<Class, Signature>(instance, method, message, arguments)
+ .Invoke();
+ // Return to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, 0);
+}
+
+// Dispatches a method by deserializing arguments from the given Message, with
+// compile-time interface check. Overload for int return types.
+template <typename RemoteMethodType, typename Class, typename... Args,
+ typename = EnableIfNotVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+ int (Class::*method)(Message&, Args...),
+ Message& message,
+ std::size_t max_capacity = InitialBufferCapacity) {
+ using Signature = typename RemoteMethodType::template RewriteArgs<Args...>;
+ rpc::ServicePayload<ReceiveBuffer> payload(message);
+ payload.Resize(max_capacity);
+
+ auto size = message.Read(payload.Data(), payload.Size());
+ if (size < 0) {
+ RemoteMethodError(message, -size);
+ return;
+ }
+
+ payload.Resize(size);
+
+ ErrorType error;
+ auto decoder = MakeArgumentDecoder<Signature>(&payload);
+ auto arguments = decoder.DecodeArguments(&error);
+ if (error) {
+ RemoteMethodError(message, EIO);
+ return;
+ }
+
+ auto return_value =
+ UnpackArguments<Class, Signature>(instance, method, message, arguments)
+ .Invoke();
+ // Return the value to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+
+// Dispatches a method by deserializing arguments from the given Message, with
+// compile-time interface check. Overload for FileHandle return types.
+template <typename RemoteMethodType, FileHandleMode Mode, typename Class,
+ typename... Args, typename = EnableIfNotVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+ FileHandle<Mode> (Class::*method)(Message&, Args...),
+ Message& message,
+ std::size_t max_capacity = InitialBufferCapacity) {
+ using Signature =
+ typename RemoteMethodType::template RewriteSignature<FileHandle<Mode>,
+ Args...>;
+ rpc::ServicePayload<ReceiveBuffer> payload(message);
+ payload.Resize(max_capacity);
+
+ auto size = message.Read(payload.Data(), payload.Size());
+ if (size < 0) {
+ RemoteMethodError(message, -size);
+ return;
+ }
+
+ payload.Resize(size);
+
+ ErrorType error;
+ auto decoder = MakeArgumentDecoder<Signature>(&payload);
+ auto arguments = decoder.DecodeArguments(&error);
+ if (error) {
+ RemoteMethodError(message, EIO);
+ return;
+ }
+
+ auto return_value =
+ UnpackArguments<Class, Signature>(instance, method, message, arguments)
+ .Invoke();
+ // Return the value to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+
+// Dispatches a method by deserializing arguments from the given Message, with
+// compile-time interface check. Overload for ChannelHandle return types.
+template <typename RemoteMethodType, ChannelHandleMode Mode, typename Class,
+ typename... Args, typename = EnableIfNotVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(
+ Class& instance, ChannelHandle<Mode> (Class::*method)(Message&, Args...),
+ Message& message, std::size_t max_capacity = InitialBufferCapacity) {
+ using Signature = typename RemoteMethodType::template RewriteArgs<Args...>;
+ rpc::ServicePayload<ReceiveBuffer> payload(message);
+ payload.Resize(max_capacity);
+
+ auto size = message.Read(payload.Data(), payload.Size());
+ if (size < 0) {
+ RemoteMethodError(message, -size);
+ return;
+ }
+
+ payload.Resize(size);
+
+ ErrorType error;
+ auto decoder = MakeArgumentDecoder<Signature>(&payload);
+ auto arguments = decoder.DecodeArguments(&error);
+ if (error) {
+ RemoteMethodError(message, EIO);
+ return;
+ }
+
+ auto return_value =
+ UnpackArguments<Class, Signature>(instance, method, message, arguments)
+ .Invoke();
+ // Return the value to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+
+// Dispatches a method by deserializing arguments from the given Message, with
+// compile-time interface signature check. Overload for generic return types.
+template <typename RemoteMethodType, typename Class, typename Return,
+ typename... Args, typename = EnableIfNotVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+ Return (Class::*method)(Message&, Args...),
+ Message& message,
+ std::size_t max_capacity = InitialBufferCapacity) {
+ using Signature =
+ typename RemoteMethodType::template RewriteSignature<Return, Args...>;
+ rpc::ServicePayload<ReceiveBuffer> payload(message);
+ payload.Resize(max_capacity);
+
+ auto size = message.Read(payload.Data(), payload.Size());
+ if (size < 0) {
+ RemoteMethodError(message, -size);
+ return;
+ }
+
+ payload.Resize(size);
+
+ ErrorType error;
+ auto decoder = MakeArgumentDecoder<Signature>(&payload);
+ auto arguments = decoder.DecodeArguments(&error);
+ if (error) {
+ RemoteMethodError(message, EIO);
+ return;
+ }
+
+ auto return_value =
+ UnpackArguments<Class, Signature>(instance, method, message, arguments)
+ .Invoke();
+ // Return the value to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+
+#ifdef __clang__
+// Overloads to handle Void argument type without exploding clang.
+
+// Overload for void return type.
+template <typename RemoteMethodType, typename Class,
+ typename = EnableIfVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance, void (Class::*method)(Message&),
+ Message& message) {
+ (instance.*method)(message);
+ // Return to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, 0);
+}
+
+// Overload for int return type.
+template <typename RemoteMethodType, typename Class,
+ typename = EnableIfVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance, int (Class::*method)(Message&),
+ Message& message) {
+ const int return_value = (instance.*method)(message);
+ // Return the value to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+
+// Overload for FileHandle return type.
+template <typename RemoteMethodType, typename Class, FileHandleMode Mode,
+ typename = EnableIfVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+ FileHandle<Mode> (Class::*method)(Message&),
+ Message& message) {
+ FileHandle<Mode> return_value = (instance.*method)(message);
+ // Return the value to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+
+// Overload for ChannelHandle return types.
+template <typename RemoteMethodType, typename Class, ChannelHandleMode Mode,
+ typename = EnableIfVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+ ChannelHandle<Mode> (Class::*method)(Message&),
+ Message& message) {
+ ChannelHandle<Mode> return_value = (instance.*method)(message);
+ // Return the value to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+
+// Overload for generic return type.
+template <typename RemoteMethodType, typename Class, typename Return,
+ typename = EnableIfVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance, Return (Class::*method)(Message&),
+ Message& message) {
+ auto return_value = (instance.*method)(message);
+ // Return the value to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+#endif
+
+} // namespace rpc
+
+// Definitions for template methods declared in pdx/client.h.
+
+template <int Opcode, typename T>
+struct CheckArgumentTypes;
+
+template <int Opcode, typename Return, typename... Args>
+struct CheckArgumentTypes<Opcode, Return(Args...)> {
+ template <typename R>
+ static typename rpc::EnableIfDirectReturn<R, Status<R>> Invoke(Client& client,
+ Args... args) {
+ Transaction trans{client};
+ rpc::ClientPayload<rpc::SendBuffer> payload{trans};
+ rpc::MakeArgumentEncoder<Return(Args...)>(&payload).EncodeArguments(
+ std::forward<Args>(args)...);
+ return trans.Send<R>(Opcode, payload.Data(), payload.Size(), nullptr, 0);
+ }
+
+ template <typename R>
+ static typename rpc::EnableIfNotDirectReturn<R, Status<R>> Invoke(
+ Client& client, Args... args) {
+ Transaction trans{client};
+
+ rpc::ClientPayload<rpc::SendBuffer> send_payload{trans};
+ rpc::MakeArgumentEncoder<Return(Args...)>(&send_payload)
+ .EncodeArguments(std::forward<Args>(args)...);
+
+ rpc::ClientPayload<rpc::ReplyBuffer> reply_payload{trans};
+ reply_payload.Resize(reply_payload.Capacity());
+
+ Status<R> result;
+ auto status =
+ trans.Send<void>(Opcode, send_payload.Data(), send_payload.Size(),
+ reply_payload.Data(), reply_payload.Size());
+ if (!status) {
+ result.SetError(status.error());
+ } else {
+ R return_value;
+ rpc::ErrorType error =
+ rpc::MakeArgumentDecoder<Return(Args...)>(&reply_payload)
+ .DecodeReturn(&return_value);
+
+ switch (error.error_code()) {
+ case rpc::ErrorCode::NO_ERROR:
+ result.SetValue(std::move(return_value));
+ break;
+
+ // This error is returned when ArrayWrapper/StringWrapper is too
+ // small to receive the payload.
+ case rpc::ErrorCode::INSUFFICIENT_DESTINATION_SIZE:
+ result.SetError(ENOBUFS);
+ break;
+
+ default:
+ result.SetError(EIO);
+ break;
+ }
+ }
+ return result;
+ }
+
+ template <typename R>
+ static typename rpc::EnableIfDirectReturn<R, Status<void>> InvokeInPlace(
+ Client& client, R* return_value, Args... args) {
+ Transaction trans{client};
+
+ rpc::ClientPayload<rpc::SendBuffer> send_payload{trans};
+ rpc::MakeArgumentEncoder<Return(Args...)>(&send_payload)
+ .EncodeArguments(std::forward<Args>(args)...);
+
+ Status<void> result;
+ auto status = trans.Send<R>(Opcode, send_payload.Data(),
+ send_payload.Size(), nullptr, 0);
+ if (status) {
+ *return_value = status.take();
+ result.SetValue();
+ } else {
+ result.SetError(status.error());
+ }
+ return result;
+ }
+
+ template <typename R>
+ static typename rpc::EnableIfNotDirectReturn<R, Status<void>> InvokeInPlace(
+ Client& client, R* return_value, Args... args) {
+ Transaction trans{client};
+
+ rpc::ClientPayload<rpc::SendBuffer> send_payload{trans};
+ rpc::MakeArgumentEncoder<Return(Args...)>(&send_payload)
+ .EncodeArguments(std::forward<Args>(args)...);
+
+ rpc::ClientPayload<rpc::ReplyBuffer> reply_payload{trans};
+ reply_payload.Resize(reply_payload.Capacity());
+
+ auto result =
+ trans.Send<void>(Opcode, send_payload.Data(), send_payload.Size(),
+ reply_payload.Data(), reply_payload.Size());
+ if (result) {
+ rpc::ErrorType error =
+ rpc::MakeArgumentDecoder<Return(Args...)>(&reply_payload)
+ .DecodeReturn(return_value);
+
+ switch (error.error_code()) {
+ case rpc::ErrorCode::NO_ERROR:
+ result.SetValue();
+ break;
+
+ // This error is returned when ArrayWrapper/StringWrapper is too
+ // small to receive the payload.
+ case rpc::ErrorCode::INSUFFICIENT_DESTINATION_SIZE:
+ result.SetError(ENOBUFS);
+ break;
+
+ default:
+ result.SetError(EIO);
+ break;
+ }
+ }
+ return result;
+ }
+};
+
+// Invokes the remote method with opcode and signature described by
+// |RemoteMethodType|.
+template <typename RemoteMethodType, typename... Args>
+Status<typename RemoteMethodType::Return> Client::InvokeRemoteMethod(
+ Args&&... args) {
+ return CheckArgumentTypes<
+ RemoteMethodType::Opcode,
+ typename RemoteMethodType::template RewriteArgs<Args...>>::
+ template Invoke<typename RemoteMethodType::Return>(
+ *this, std::forward<Args>(args)...);
+}
+
+template <typename RemoteMethodType, typename Return, typename... Args>
+Status<void> Client::InvokeRemoteMethodInPlace(Return* return_value,
+ Args&&... args) {
+ return CheckArgumentTypes<
+ RemoteMethodType::Opcode,
+ typename RemoteMethodType::template RewriteSignature<Return, Args...>>::
+ template InvokeInPlace(*this, return_value, std::forward<Args>(args)...);
+}
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_REMOTE_METHOD_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/remote_method_type.h b/libs/vr/libpdx/private/pdx/rpc/remote_method_type.h
new file mode 100644
index 0000000..de9a3cc
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/remote_method_type.h
@@ -0,0 +1,67 @@
+#ifndef ANDROID_PDX_RPC_REMOTE_METHOD_TYPE_H_
+#define ANDROID_PDX_RPC_REMOTE_METHOD_TYPE_H_
+
+#include <cstddef>
+#include <tuple>
+#include <type_traits>
+
+#include <pdx/rpc/enumeration.h>
+#include <pdx/rpc/function_traits.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility class binding a remote method opcode to its function signature.
+// Describes the interface between RPC clients and services for a single method.
+template <int Opcode_, typename Signature_>
+struct RemoteMethodType {
+ typedef FunctionTraits<Signature_> Traits;
+
+ enum : int { Opcode = Opcode_ };
+
+ typedef typename Traits::Signature Signature;
+ typedef typename Traits::Return Return;
+ typedef typename Traits::Args Args;
+
+ template <typename... Params>
+ using RewriteArgs = typename Traits::template RewriteArgs<Params...>;
+
+ template <typename ReturnType, typename... Params>
+ using RewriteSignature =
+ typename Traits::template RewriteSignature<ReturnType, Params...>;
+
+ template <typename ReturnType>
+ using RewriteReturn = typename Traits::template RewriteReturn<ReturnType>;
+};
+
+// Utility class representing a set of related RemoteMethodTypes. Describes the
+// interface between RPC clients and services as a set of methods.
+template <typename... MethodTypes>
+struct RemoteAPI {
+ typedef std::tuple<MethodTypes...> Methods;
+ enum : std::size_t { Length = sizeof...(MethodTypes) };
+
+ template <std::size_t Index>
+ using Method = typename std::tuple_element<Index, Methods>::type;
+
+ template <typename MethodType>
+ static constexpr std::size_t MethodIndex() {
+ return ElementForType<MethodType, MethodTypes...>::Index;
+ }
+};
+
+// Macro to simplify defining remote method signatures. Remote method signatures
+// are specified by defining a RemoteMethodType for each remote method.
+#define PDX_REMOTE_METHOD(name, opcode, ... /*signature*/) \
+ using name = ::android::pdx::rpc::RemoteMethodType<opcode, __VA_ARGS__>
+
+// Macro to simplify defining a set of remote method signatures.
+#define PDX_REMOTE_API(name, ... /*methods*/) \
+ using name = ::android::pdx::rpc::RemoteAPI<__VA_ARGS__>
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_REMOTE_METHOD_TYPE_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/sequence.h b/libs/vr/libpdx/private/pdx/rpc/sequence.h
new file mode 100644
index 0000000..5fd898a
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/sequence.h
@@ -0,0 +1,56 @@
+#ifndef ANDROID_PDX_RPC_SEQUENCE_H_
+#define ANDROID_PDX_RPC_SEQUENCE_H_
+
+#include <cstdint>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Provides a C++11 implementation of C++14 index_sequence and
+// make_index_sequence for compatibility with common compilers. This
+// implementation may be conditionally replaced with compiler-provided versions
+// when C++14 support is available.
+
+// Utility to capture a sequence of unsigned indices.
+template <std::size_t... I>
+struct IndexSequence {
+ using type = IndexSequence;
+ using value_type = std::size_t;
+ static constexpr std::size_t size() { return sizeof...(I); }
+};
+
+namespace detail {
+
+// Helper class to merge and renumber sequence parts in log N instantiations.
+template <typename S1, typename S2>
+struct MergeSequencesAndRenumber;
+
+template <std::size_t... I1, std::size_t... I2>
+struct MergeSequencesAndRenumber<IndexSequence<I1...>, IndexSequence<I2...>>
+ : IndexSequence<I1..., (sizeof...(I1) + I2)...> {};
+
+} // namespace detail
+
+// Utility to build an IndexSequence with N indices.
+template <std::size_t N>
+struct MakeIndexSequence : detail::MergeSequencesAndRenumber<
+ typename MakeIndexSequence<N / 2>::type,
+ typename MakeIndexSequence<N - N / 2>::type> {};
+
+// Identity sequences.
+template <>
+struct MakeIndexSequence<0> : IndexSequence<> {};
+template <>
+struct MakeIndexSequence<1> : IndexSequence<0> {};
+
+// Utility to build an IndexSequence with indices for each element of a
+// parameter pack.
+template <typename... T>
+using IndexSequenceFor = MakeIndexSequence<sizeof...(T)>;
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_SEQUENCE_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/serializable.h b/libs/vr/libpdx/private/pdx/rpc/serializable.h
new file mode 100644
index 0000000..04a4352
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/serializable.h
@@ -0,0 +1,150 @@
+#ifndef ANDROID_PDX_RPC_SERIALIZABLE_H_
+#define ANDROID_PDX_RPC_SERIALIZABLE_H_
+
+#include <cstddef>
+#include <string>
+#include <tuple>
+
+#include <pdx/message_reader.h>
+#include <pdx/message_writer.h>
+
+#include "macros.h"
+#include "serialization.h"
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// This file provides utilities to define serializable types for communication
+// between clients and services. Supporting efficient, typed communication
+// protocols is the primary goal, NOT providing a general-purpose solution for
+// all your C++ serialization needs. Features that are not aligned to the goals
+// are not supported, such as static/const member serialization and serializable
+// types with virtual methods (requiring a virtual destructor).
+
+// Captures the type and value of a pointer to member. Pointer to members are
+// essentially compile-time constant offsets that can be stored in the type
+// system without adding to the size of the structures they describe. This
+// library uses this property to implement a limited form of reflection for
+// serialization/deserialization functions.
+template <typename T, T>
+struct MemberPointer;
+
+template <typename Type, typename Class, Type Class::*Pointer>
+struct MemberPointer<Type Class::*, Pointer> {
+ // Type of the member pointer this type represents.
+ using PointerType = Type Class::*;
+
+ // Resolves a pointer to member with the given instance, yielding a
+ // reference to the member in that instance.
+ static Type& Resolve(Class& instance) { return (instance.*Pointer); }
+ static const Type& Resolve(const Class& instance) {
+ return (instance.*Pointer);
+ }
+};
+
+// Describes a set of members to be serialized/deserialized by this library. The
+// parameter pack MemberPointers takes a list of MemberPointer types that
+// describe each member to participate in serialization/deserialization.
+template <typename T, typename... MemberPointers>
+struct SerializableMembersType {
+ using Type = T;
+
+ // The number of member pointers described by this type.
+ enum : std::size_t { MemberCount = sizeof...(MemberPointers) };
+
+ // The member pointers described by this type.
+ using Members = std::tuple<MemberPointers...>;
+
+ // Accessor for individual member pointer types.
+ template <std::size_t Index>
+ using At = typename std::tuple_element<Index, Members>::type;
+};
+
+// Classes must do the following to correctly define a serializable type:
+// 1. Define a type called "SerializableMembers" as a template instantiation
+// of SerializableMembersType, describing the members of the class to
+// participate in serialization (presumably all of them). Use the macro
+// PDX_SERIALIZABLE_MEMBERS(...) below to aid the correct type
+// definition. This type should be private to prevent leaking member
+// access information.
+// 2. Make SerializableTraits and HasSerilizableMembers types a friend of
+// the class. The macro PDX_SERIALIZABLE_MEMEBRS(...) takes care of
+// this automatically.
+// 3. Define a public default constructor, if necessary. Deserialization
+// requires instances to be default-constructible.
+//
+// Example usage:
+// class MySerializableType : public AnotherBaseType {
+// public:
+// MySerializableType();
+// ...
+// private:
+// int a;
+// string b;
+// PDX_SERIALIZABLE_MEMBERS(MySerializableType, a, b);
+// };
+//
+// Note that const and static member serialization is not supported.
+
+template <typename T>
+class SerializableTraits {
+ public:
+ // Gets the serialized size of type T.
+ static std::size_t GetSerializedSize(const T& value) {
+ return GetEncodingSize(EncodeArrayType(SerializableMembers::MemberCount)) +
+ GetMembersSize<SerializableMembers>(value);
+ }
+
+ // Serializes type T.
+ static void SerializeObject(const T& value, MessageWriter* writer,
+ void*& buffer) {
+ SerializeArrayEncoding(EncodeArrayType(SerializableMembers::MemberCount),
+ SerializableMembers::MemberCount, buffer);
+ SerializeMembers<SerializableMembers>(value, writer, buffer);
+ }
+
+ // Deserializes type T.
+ static ErrorType DeserializeObject(T* value, MessageReader* reader,
+ const void*& start, const void* end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeArrayType(&encoding, &size, reader, start, end)) {
+ return error;
+ } else if (size != SerializableMembers::MemberCount) {
+ return ErrorCode::UNEXPECTED_TYPE_SIZE;
+ } else {
+ return DeserializeMembers<SerializableMembers>(value, reader, start, end);
+ }
+ }
+
+ private:
+ using SerializableMembers = typename T::SerializableMembers;
+};
+
+// Utility macro to define a MemberPointer type for a member name.
+#define PDX_MEMBER_POINTER(type, member) \
+ ::android::pdx::rpc::MemberPointer<decltype(&type::member), &type::member>
+
+// Defines a list of MemberPointer types given a list of member names.
+#define PDX_MEMBERS(type, ... /*members*/) \
+ PDX_FOR_EACH_BINARY_LIST(PDX_MEMBER_POINTER, type, __VA_ARGS__)
+
+// Defines the serializable members of a type given a list of member names and
+// befriends SerializableTraits and HasSerializableMembers for the class. This
+// macro handles requirements #1 and #2 above.
+#define PDX_SERIALIZABLE_MEMBERS(type, ... /*members*/) \
+ template <typename T> \
+ friend class ::android::pdx::rpc::SerializableTraits; \
+ template <typename, typename> \
+ friend struct ::android::pdx::rpc::HasSerializableMembers; \
+ using SerializableMembers = ::android::pdx::rpc::SerializableMembersType< \
+ type, PDX_MEMBERS(type, __VA_ARGS__)>
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_SERIALIZABLE_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/serialization.h b/libs/vr/libpdx/private/pdx/rpc/serialization.h
new file mode 100644
index 0000000..fccd028
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/serialization.h
@@ -0,0 +1,1996 @@
+#ifndef ANDROID_PDX_RPC_SERIALIZATION_H_
+#define ANDROID_PDX_RPC_SERIALIZATION_H_
+
+#include <cstdint>
+#include <cstring>
+#include <iterator>
+#include <map>
+#include <numeric>
+#include <sstream>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/message_reader.h>
+#include <pdx/message_writer.h>
+#include <pdx/trace.h>
+#include <pdx/utility.h>
+
+#include "array_wrapper.h"
+#include "default_initialization_allocator.h"
+#include "encoding.h"
+#include "pointer_wrapper.h"
+#include "string_wrapper.h"
+#include "variant.h"
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Automatic serialization/deserialization library based on MessagePack
+// (http://msgpack.org). This library provides top level Serialize() and
+// Deserialize() functions to encode/decode a variety of data types.
+//
+// The following data types are supported:
+// * Standard signed integer types: int8_t, int16_t, int32_t, and int64_t.
+// * Regular signed integer types equivalent to the standard types:
+// signed char, short, int, long, and long long.
+// * Standard unsigned integer types: uint8_t, uint16_t, uint32_t, and
+// uint64_t.
+// * Regular unsigned integer types equivalent to the standard types:
+// unsigned char, unsigned short, unsigned int, unsigned long,
+// and unsigned long long.
+// * char without signed/unsigned qualifiers.
+// * bool.
+// * std::vector with value type of any supported type, including nesting.
+// * std::string.
+// * std::tuple with elements of any supported type, including nesting.
+// * std::pair with elements of any supported type, including nesting.
+// * std::map with keys and values of any supported type, including nesting.
+// * std::unordered_map with keys and values of any supported type, including
+// nesting.
+// * std::array with values of any supported type, including nesting.
+// * ArrayWrapper of any supported basic type.
+// * BufferWrapper of any POD type.
+// * StringWrapper of any supported char type.
+// * User types with correctly defined SerializableMembers member type.
+//
+// Planned support for:
+// * std::basic_string with all supported char types.
+
+// Counting template for managing template recursion.
+template <std::size_t N>
+struct Index {};
+
+// Forward declaration of traits type to access types with a SerializedMembers
+// member type.
+template <typename T>
+class SerializableTraits;
+
+template <typename T, typename... MemberPointers>
+struct SerializableMembersType;
+
+// Utility to deduce the template type from a derived type.
+template <template <typename...> class TT, typename... Ts>
+std::true_type DeduceTemplateType(const TT<Ts...>*);
+template <template <typename...> class TT>
+std::false_type DeduceTemplateType(...);
+
+// Utility determining whether template type TT<...> is a base of type T.
+template <template <typename...> class TT, typename T>
+using IsTemplateBaseOf = decltype(DeduceTemplateType<TT>(std::declval<T*>()));
+
+// Utility type for SFINAE in HasHasSerializableMembers.
+template <typename... Ts>
+using TrySerializableMembersType = void;
+
+// Determines whether type T has a member type named SerializableMembers of
+// template type SerializableMembersType.
+template <typename, typename = void>
+struct HasSerializableMembers : std::false_type {};
+template <typename T>
+struct HasSerializableMembers<
+ T, TrySerializableMembersType<typename T::SerializableMembers>>
+ : std::integral_constant<
+ bool, IsTemplateBaseOf<SerializableMembersType,
+ typename T::SerializableMembers>::value> {};
+
+// Utility to simplify overload enable expressions for types with correctly
+// defined SerializableMembers.
+template <typename T>
+using EnableIfHasSerializableMembers =
+ typename std::enable_if<HasSerializableMembers<T>::value>::type;
+
+// Utility to simplify overload enable expressions for enum types.
+template <typename T, typename ReturnType = void>
+using EnableIfEnum =
+ typename std::enable_if<std::is_enum<T>::value, ReturnType>::type;
+
+///////////////////////////////////////////////////////////////////////////////
+// Error Reporting //
+///////////////////////////////////////////////////////////////////////////////
+
+// Error codes returned by the deserialization code.
+enum class ErrorCode {
+ NO_ERROR = 0,
+ UNEXPECTED_ENCODING,
+ UNEXPECTED_TYPE_SIZE,
+ INSUFFICIENT_BUFFER,
+ INSUFFICIENT_DESTINATION_SIZE,
+ GET_FILE_DESCRIPTOR_FAILED,
+ GET_CHANNEL_HANDLE_FAILED,
+ INVALID_VARIANT_ELEMENT,
+};
+
+// Type for errors returned by the deserialization code.
+class ErrorType {
+ public:
+ ErrorType() : error_code_(ErrorCode::NO_ERROR) {}
+
+ // ErrorType constructor for generic error codes. Explicitly not explicit,
+ // implicit conversion from ErrorCode to ErrorType is desirable behavior.
+ // NOLINTNEXTLINE(runtime/explicit)
+ ErrorType(ErrorCode error_code) : error_code_(error_code) {}
+
+ // ErrorType constructor for encoding type errors.
+ ErrorType(ErrorCode error_code, EncodingClass encoding_class,
+ EncodingType encoding_type)
+ : error_code_(error_code) {
+ unexpected_encoding_.encoding_class = encoding_class;
+ unexpected_encoding_.encoding_type = encoding_type;
+ }
+
+ // Evaluates to true if the ErrorType represents an error.
+ explicit operator bool() const { return error_code_ != ErrorCode::NO_ERROR; }
+
+ operator ErrorCode() const { return error_code_; }
+ ErrorCode error_code() const { return error_code_; }
+
+ // Accessors for extra info about unexpected encoding errors.
+ EncodingClass encoding_class() const {
+ return unexpected_encoding_.encoding_class;
+ }
+ EncodingType encoding_type() const {
+ return unexpected_encoding_.encoding_type;
+ }
+
+ operator std::string() const {
+ std::ostringstream stream;
+
+ switch (error_code_) {
+ case ErrorCode::NO_ERROR:
+ return "NO_ERROR";
+ case ErrorCode::UNEXPECTED_ENCODING:
+ stream << "UNEXPECTED_ENCODING: " << static_cast<int>(encoding_class())
+ << ", " << static_cast<int>(encoding_type());
+ return stream.str();
+ case ErrorCode::UNEXPECTED_TYPE_SIZE:
+ return "UNEXPECTED_TYPE_SIZE";
+ case ErrorCode::INSUFFICIENT_BUFFER:
+ return "INSUFFICIENT_BUFFER";
+ case ErrorCode::INSUFFICIENT_DESTINATION_SIZE:
+ return "INSUFFICIENT_DESTINATION_SIZE";
+ default:
+ return "[Unknown Error]";
+ }
+ }
+
+ private:
+ ErrorCode error_code_;
+
+ // Union of extra information for different error code types.
+ union {
+ // UNEXPECTED_ENCODING.
+ struct {
+ EncodingClass encoding_class;
+ EncodingType encoding_type;
+ } unexpected_encoding_;
+ };
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Object Size //
+///////////////////////////////////////////////////////////////////////////////
+
+inline constexpr std::size_t GetSerializedSize(const bool& b) {
+ return GetEncodingSize(EncodeType(b));
+}
+
+// Overloads of GetSerializedSize() for standard integer types.
+inline constexpr std::size_t GetSerializedSize(const char& c) {
+ return GetEncodingSize(EncodeType(c));
+}
+inline constexpr std::size_t GetSerializedSize(const std::uint8_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::int8_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::uint16_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::int16_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::uint32_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::int32_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::uint64_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::int64_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+
+inline constexpr std::size_t GetSerializedSize(const float& f) {
+ return GetEncodingSize(EncodeType(f));
+}
+inline constexpr std::size_t GetSerializedSize(const double& d) {
+ return GetEncodingSize(EncodeType(d));
+}
+
+// Overload for enum types.
+template <typename T>
+inline EnableIfEnum<T, std::size_t> GetSerializedSize(T v) {
+ return GetSerializedSize(static_cast<std::underlying_type_t<T>>(v));
+}
+
+// Forward declaration for nested definitions.
+inline std::size_t GetSerializedSize(const EmptyVariant&);
+template <typename... Types>
+inline std::size_t GetSerializedSize(const Variant<Types...>&);
+template <typename T, typename Enabled>
+inline constexpr std::size_t GetSerializedSize(const T&);
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const PointerWrapper<T>&);
+inline constexpr std::size_t GetSerializedSize(const std::string&);
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const StringWrapper<T>&);
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const BufferWrapper<T>&);
+template <FileHandleMode Mode>
+inline constexpr std::size_t GetSerializedSize(const FileHandle<Mode>&);
+template <ChannelHandleMode Mode>
+inline constexpr std::size_t GetSerializedSize(const ChannelHandle<Mode>&);
+template <typename T, typename Allocator>
+inline std::size_t GetSerializedSize(const std::vector<T, Allocator>& v);
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline std::size_t GetSerializedSize(
+ const std::map<Key, T, Compare, Allocator>& m);
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline std::size_t GetSerializedSize(
+ const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>&);
+template <typename T>
+inline std::size_t GetSerializedSize(const ArrayWrapper<T>&);
+template <typename T, std::size_t Size>
+inline std::size_t GetSerializedSize(const std::array<T, Size>& v);
+template <typename T, typename U>
+inline std::size_t GetSerializedSize(const std::pair<T, U>& p);
+template <typename... T>
+inline std::size_t GetSerializedSize(const std::tuple<T...>& tuple);
+
+// Overload for empty variant type.
+inline std::size_t GetSerializedSize(const EmptyVariant& empty) {
+ return GetEncodingSize(EncodeType(empty));
+}
+
+// Overload for Variant types.
+template <typename... Types>
+inline std::size_t GetSerializedSize(const Variant<Types...>& variant) {
+ return GetEncodingSize(EncodeType(variant)) +
+ GetSerializedSize(variant.index()) +
+ variant.Visit(
+ [](const auto& value) { return GetSerializedSize(value); });
+}
+
+// Overload for structs/classes with SerializableMembers defined.
+template <typename T, typename Enabled = EnableIfHasSerializableMembers<T>>
+inline constexpr std::size_t GetSerializedSize(const T& value) {
+ return SerializableTraits<T>::GetSerializedSize(value);
+}
+
+// Overload for PointerWrapper.
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const PointerWrapper<T>& p) {
+ return GetSerializedSize(p.Dereference());
+}
+
+// Overload for std::string.
+inline constexpr std::size_t GetSerializedSize(const std::string& s) {
+ return GetEncodingSize(EncodeType(s)) +
+ s.length() * sizeof(std::string::value_type);
+}
+
+// Overload for StringWrapper.
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const StringWrapper<T>& s) {
+ return GetEncodingSize(EncodeType(s)) +
+ s.length() * sizeof(typename StringWrapper<T>::value_type);
+}
+
+// Overload for BufferWrapper types.
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const BufferWrapper<T>& b) {
+ return GetEncodingSize(EncodeType(b)) +
+ b.size() * sizeof(typename BufferWrapper<T>::value_type);
+}
+
+// Overload for FileHandle. FileHandle is encoded as a FIXEXT2, with a type code
+// of "FileHandle" and a signed 16-bit offset into the pushed fd array. Empty
+// FileHandles are encoded with an array index of -1.
+template <FileHandleMode Mode>
+inline constexpr std::size_t GetSerializedSize(const FileHandle<Mode>& fd) {
+ return GetEncodingSize(EncodeType(fd)) + sizeof(std::int16_t);
+}
+
+// Overload for ChannelHandle. ChannelHandle is encoded as a FIXEXT4, with a
+// type code of "ChannelHandle" and a signed 32-bit offset into the pushed
+// channel array. Empty ChannelHandles are encoded with an array index of -1.
+template <ChannelHandleMode Mode>
+inline constexpr std::size_t GetSerializedSize(
+ const ChannelHandle<Mode>& channel_handle) {
+ return GetEncodingSize(EncodeType(channel_handle)) + sizeof(std::int32_t);
+}
+
+// Overload for standard vector types.
+template <typename T, typename Allocator>
+inline std::size_t GetSerializedSize(const std::vector<T, Allocator>& v) {
+ return std::accumulate(v.begin(), v.end(), GetEncodingSize(EncodeType(v)),
+ [](const std::size_t& sum, const T& object) {
+ return sum + GetSerializedSize(object);
+ });
+}
+
+// Overload for standard map types.
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline std::size_t GetSerializedSize(
+ const std::map<Key, T, Compare, Allocator>& v) {
+ return std::accumulate(
+ v.begin(), v.end(), GetEncodingSize(EncodeType(v)),
+ [](const std::size_t& sum, const std::pair<Key, T>& object) {
+ return sum + GetSerializedSize(object.first) +
+ GetSerializedSize(object.second);
+ });
+}
+
+// Overload for standard unordered_map types.
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline std::size_t GetSerializedSize(
+ const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& v) {
+ return std::accumulate(
+ v.begin(), v.end(), GetEncodingSize(EncodeType(v)),
+ [](const std::size_t& sum, const std::pair<Key, T>& object) {
+ return sum + GetSerializedSize(object.first) +
+ GetSerializedSize(object.second);
+ });
+}
+
+// Overload for ArrayWrapper types.
+template <typename T>
+inline std::size_t GetSerializedSize(const ArrayWrapper<T>& v) {
+ return std::accumulate(v.begin(), v.end(), GetEncodingSize(EncodeType(v)),
+ [](const std::size_t& sum, const T& object) {
+ return sum + GetSerializedSize(object);
+ });
+}
+
+// Overload for std::array types.
+template <typename T, std::size_t Size>
+inline std::size_t GetSerializedSize(const std::array<T, Size>& v) {
+ return std::accumulate(v.begin(), v.end(), GetEncodingSize(EncodeType(v)),
+ [](const std::size_t& sum, const T& object) {
+ return sum + GetSerializedSize(object);
+ });
+}
+
+// Overload for std::pair.
+template <typename T, typename U>
+inline std::size_t GetSerializedSize(const std::pair<T, U>& p) {
+ return GetEncodingSize(EncodeType(p)) + GetSerializedSize(p.first) +
+ GetSerializedSize(p.second);
+}
+
+// Stops template recursion when the last tuple element is reached.
+template <typename... T>
+inline std::size_t GetTupleSize(const std::tuple<T...>&, Index<0>) {
+ return 0;
+}
+
+// Gets the size of each element in a tuple recursively.
+template <typename... T, std::size_t index>
+inline std::size_t GetTupleSize(const std::tuple<T...>& tuple, Index<index>) {
+ return GetTupleSize(tuple, Index<index - 1>()) +
+ GetSerializedSize(std::get<index - 1>(tuple));
+}
+
+// Overload for tuple types. Gets the size of the tuple, recursing
+// through the elements.
+template <typename... T>
+inline std::size_t GetSerializedSize(const std::tuple<T...>& tuple) {
+ return GetEncodingSize(EncodeType(tuple)) +
+ GetTupleSize(tuple, Index<sizeof...(T)>());
+}
+
+// Stops template recursion when the last member of a Serializable
+// type is reached.
+template <typename Members, typename T>
+inline std::size_t GetMemberSize(const T&, Index<0>) {
+ return 0;
+}
+
+// Gets the size of each member of a Serializable type recursively.
+template <typename Members, typename T, std::size_t index>
+inline std::size_t GetMemberSize(const T& object, Index<index>) {
+ return GetMemberSize<Members>(object, Index<index - 1>()) +
+ GetSerializedSize(Members::template At<index - 1>::Resolve(object));
+}
+
+// Gets the size of a type using the given SerializableMembersType
+// type.
+template <typename Members, typename T>
+inline std::size_t GetMembersSize(const T& object) {
+ return GetMemberSize<Members>(object, Index<Members::MemberCount>());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Object Serialization //
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// SerializeRaw() converts a primitive array or type into a raw byte string.
+// These functions are named differently from SerializeObject() expressly to
+// avoid catch-all specialization of that template, which can be difficult to
+// detect otherwise.
+//
+
+inline void WriteRawData(void*& dest, const void* src, size_t size) {
+ memcpy(dest, src, size);
+ dest = static_cast<uint8_t*>(dest) + size;
+}
+
+// Serializes a primitive array into a raw byte string.
+template <typename T,
+ typename = typename std::enable_if<std::is_pod<T>::value>::type>
+inline void SerializeRaw(const T& value, void*& buffer) {
+ WriteRawData(buffer, &value, sizeof(value));
+}
+
+inline void SerializeEncoding(EncodingType encoding, void*& buffer) {
+ SerializeRaw(encoding, buffer);
+}
+
+inline void SerializeType(const bool& value, void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+}
+
+// Serializes the type code, extended type code, and size for
+// extension types.
+inline void SerializeExtEncoding(EncodingType encoding,
+ EncodingExtType ext_type, std::size_t size,
+ void*& buffer) {
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_EXT8) {
+ std::uint8_t length = size;
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_EXT16) {
+ std::uint16_t length = size;
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_EXT32) {
+ std::uint32_t length = size;
+ SerializeRaw(length, buffer);
+ } else /* if (IsFixextEncoding(encoding) */ {
+ // Encoding byte contains the fixext length, nothing else to do.
+ }
+ SerializeRaw(ext_type, buffer);
+}
+
+// Serializes the type code for file descriptor types.
+template <FileHandleMode Mode>
+inline void SerializeType(const FileHandle<Mode>& value, void*& buffer) {
+ SerializeExtEncoding(EncodeType(value), ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 2,
+ buffer);
+}
+
+// Serializes the type code for channel handle types.
+template <ChannelHandleMode Mode>
+inline void SerializeType(const ChannelHandle<Mode>& handle, void*& buffer) {
+ SerializeExtEncoding(EncodeType(handle), ENCODING_EXT_TYPE_CHANNEL_HANDLE, 4,
+ buffer);
+}
+
+// Serializes type code for variant types.
+template <typename... Types>
+inline void SerializeType(const Variant<Types...>& value, void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+}
+
+// Serializes the type code for string types.
+template <typename StringType>
+inline void SerializeStringType(const StringType& value, void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_STR8) {
+ std::uint8_t length = value.length();
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_STR16) {
+ std::uint16_t length = value.length();
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_STR32) {
+ std::uint32_t length = value.length();
+ SerializeRaw(length, buffer);
+ } else /* if (IsFixstrEncoding(encoding) */ {
+ // Encoding byte contains the fixstr length, nothing else to do.
+ }
+}
+
+// Serializes the type code for std::string and StringWrapper. These types are
+// interchangeable and must serialize to the same format.
+inline void SerializeType(const std::string& value, void*& buffer) {
+ SerializeStringType(value, buffer);
+}
+template <typename T>
+inline void SerializeType(const StringWrapper<T>& value, void*& buffer) {
+ SerializeStringType(value, buffer);
+}
+
+// Serializes the type code for bin types.
+inline void SerializeBinEncoding(EncodingType encoding, std::size_t size,
+ void*& buffer) {
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_BIN8) {
+ std::uint8_t length = size;
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_BIN16) {
+ std::uint16_t length = size;
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_BIN32) {
+ std::uint32_t length = size;
+ SerializeRaw(length, buffer);
+ } else {
+ // Invalid encoding for BIN type.
+ }
+}
+
+// Serializes the type code for BufferWrapper types.
+template <typename T>
+inline void SerializeType(const BufferWrapper<T>& value, void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeBinEncoding(
+ encoding, value.size() * sizeof(typename BufferWrapper<T>::value_type),
+ buffer);
+}
+
+// Serializes the array encoding type and length.
+inline void SerializeArrayEncoding(EncodingType encoding, std::size_t size,
+ void*& buffer) {
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_ARRAY16) {
+ std::uint16_t length = size;
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_ARRAY32) {
+ std::uint32_t length = size;
+ SerializeRaw(length, buffer);
+ } else /* if (IsFixarrayEncoding(encoding) */ {
+ // Encoding byte contains the fixarray length, nothing else to do.
+ }
+}
+
+// Serializes the map encoding type and length.
+inline void SerializeMapEncoding(EncodingType encoding, std::size_t size,
+ void*& buffer) {
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_MAP16) {
+ std::uint16_t length = size;
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_MAP32) {
+ std::uint32_t length = size;
+ SerializeRaw(length, buffer);
+ } else /* if (IsFixmapEncoding(encoding) */ {
+ // Encoding byte contains the fixmap length, nothing else to do.
+ }
+}
+
+// Serializes the type code for array types.
+template <typename ArrayType>
+inline void SerializeArrayType(const ArrayType& value, std::size_t size,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeArrayEncoding(encoding, size, buffer);
+}
+
+// Serializes the type code for map types.
+template <typename MapType>
+inline void SerializeMapType(const MapType& value, std::size_t size,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeMapEncoding(encoding, size, buffer);
+}
+
+// Serializes the type code for std::vector and ArrayWrapper. These types are
+// interchangeable and must serialize to the same format.
+template <typename T, typename Allocator>
+inline void SerializeType(const std::vector<T, Allocator>& value,
+ void*& buffer) {
+ SerializeArrayType(value, value.size(), buffer);
+}
+template <typename T>
+inline void SerializeType(const ArrayWrapper<T>& value, void*& buffer) {
+ SerializeArrayType(value, value.size(), buffer);
+}
+
+// Serializes the type code for std::array. This type serializes to the same
+// format as std::vector and ArrayWrapper and is interchangeable in certain
+// situations.
+template <typename T, std::size_t Size>
+inline void SerializeType(const std::array<T, Size>& value, void*& buffer) {
+ SerializeArrayType(value, Size, buffer);
+}
+
+// Serializes the type code for std::map types.
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline void SerializeType(const std::map<Key, T, Compare, Allocator>& value,
+ void*& buffer) {
+ SerializeMapType(value, value.size(), buffer);
+}
+
+// Serializes the type code for std::unordered_map types.
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline void SerializeType(
+ const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& value,
+ void*& buffer) {
+ SerializeMapType(value, value.size(), buffer);
+}
+
+// Serializes the type code for std::pair types.
+template <typename T, typename U>
+inline void SerializeType(const std::pair<T, U>& value, void*& buffer) {
+ SerializeArrayType(value, 2, buffer);
+}
+
+// Serializes the type code for std::tuple types.
+template <typename... T>
+inline void SerializeType(const std::tuple<T...>& value, void*& buffer) {
+ SerializeArrayType(value, sizeof...(T), buffer);
+}
+
+// Specialization of SerializeObject for boolean type.
+inline void SerializeObject(const bool& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ SerializeType(value, buffer);
+ // Encoding contains the boolean value, nothing else to do.
+}
+
+// Overloads of SerializeObject for float and double types.
+inline void SerializeObject(const float& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ SerializeRaw(value, buffer);
+}
+
+inline void SerializeObject(const double& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ SerializeRaw(value, buffer);
+}
+
+// Overloads of SerializeObject() for standard integer types.
+inline void SerializeObject(const char& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_UINT8) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsUnsignedFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const int8_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_INT8) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const uint8_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_UINT8) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsUnsignedFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const int16_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_INT8) {
+ const int8_t byte = value;
+ SerializeRaw(byte, buffer);
+ } else if (encoding == ENCODING_TYPE_INT16) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const uint16_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_UINT8) {
+ const uint8_t byte = value;
+ SerializeRaw(byte, buffer);
+ } else if (encoding == ENCODING_TYPE_UINT16) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsUnsignedFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const int32_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_INT8) {
+ const int8_t byte = value;
+ SerializeRaw(byte, buffer);
+ } else if (encoding == ENCODING_TYPE_INT16) {
+ const int16_t half = value;
+ SerializeRaw(half, buffer);
+ } else if (encoding == ENCODING_TYPE_INT32) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const uint32_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_UINT8) {
+ const uint8_t byte = value;
+ SerializeRaw(byte, buffer);
+ } else if (encoding == ENCODING_TYPE_UINT16) {
+ const uint16_t half = value;
+ SerializeRaw(half, buffer);
+ } else if (encoding == ENCODING_TYPE_UINT32) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsUnsignedFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const int64_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_INT8) {
+ const int8_t byte = value;
+ SerializeRaw(byte, buffer);
+ } else if (encoding == ENCODING_TYPE_INT16) {
+ const int16_t half = value;
+ SerializeRaw(half, buffer);
+ } else if (encoding == ENCODING_TYPE_INT32) {
+ const int32_t word = value;
+ SerializeRaw(word, buffer);
+ } else if (encoding == ENCODING_TYPE_INT64) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const uint64_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_UINT8) {
+ const uint8_t byte = value;
+ SerializeRaw(byte, buffer);
+ } else if (encoding == ENCODING_TYPE_UINT16) {
+ const uint16_t half = value;
+ SerializeRaw(half, buffer);
+ } else if (encoding == ENCODING_TYPE_UINT32) {
+ const uint32_t word = value;
+ SerializeRaw(word, buffer);
+ } else if (encoding == ENCODING_TYPE_UINT64) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsUnsignedFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+// Serialize enum types.
+template <typename T>
+inline EnableIfEnum<T> SerializeObject(const T& value, MessageWriter* writer,
+ void*& buffer) {
+ SerializeObject(static_cast<std::underlying_type_t<T>>(value), writer,
+ buffer);
+}
+
+// Forward declaration for nested definitions.
+inline void SerializeObject(const EmptyVariant&, MessageWriter*, void*&);
+template <typename... Types>
+inline void SerializeObject(const Variant<Types...>&, MessageWriter*, void*&);
+template <typename T, typename Enabled>
+inline void SerializeObject(const T&, MessageWriter*, void*&);
+template <typename T>
+inline void SerializeObject(const PointerWrapper<T>&, MessageWriter*, void*&);
+template <FileHandleMode Mode>
+inline void SerializeObject(const FileHandle<Mode>&, MessageWriter*, void*&);
+template <ChannelHandleMode Mode>
+inline void SerializeObject(const ChannelHandle<Mode>&, MessageWriter*, void*&);
+template <typename T, typename Allocator>
+inline void SerializeObject(const BufferWrapper<std::vector<T, Allocator>>&, MessageWriter*, void*&);
+template <typename T>
+inline void SerializeObject(const BufferWrapper<T*>&, MessageWriter*, void*&);
+inline void SerializeObject(const std::string&, MessageWriter*, void*&);
+template <typename T>
+inline void SerializeObject(const StringWrapper<T>&, MessageWriter*, void*&);
+template <typename T, typename Allocator>
+inline void SerializeObject(const std::vector<T, Allocator>&, MessageWriter*, void*&);
+template <typename T>
+inline void SerializeObject(const ArrayWrapper<T>&, MessageWriter*, void*&);
+template <typename T, std::size_t Size>
+inline void SerializeObject(const std::array<T, Size>&, MessageWriter*, void*&);
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline void SerializeObject(const std::map<Key, T, Compare, Allocator>&, MessageWriter*, void*&);
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline void SerializeObject(
+ const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>&, MessageWriter*, void*&);
+template <typename T, typename U>
+inline void SerializeObject(const std::pair<T, U>&, MessageWriter*, void*&);
+template <typename... T>
+inline void SerializeObject(const std::tuple<T...>&, MessageWriter*, void*&);
+
+// Overload for empty variant type.
+inline void SerializeObject(const EmptyVariant& empty,
+ MessageWriter* /*writer*/, void*& buffer) {
+ const EncodingType encoding = EncodeType(empty);
+ SerializeEncoding(encoding, buffer);
+}
+
+// Overload for Variant types.
+template <typename... Types>
+inline void SerializeObject(const Variant<Types...>& variant,
+ MessageWriter* writer, void*& buffer) {
+ SerializeType(variant, buffer);
+ SerializeObject(variant.index(), writer, buffer);
+ return variant.Visit([writer, &buffer](const auto& value) {
+ return SerializeObject(value, writer, buffer);
+ });
+}
+
+// Overload for serializable structure/class types.
+template <typename T, typename Enabled = EnableIfHasSerializableMembers<T>>
+inline void SerializeObject(const T& value, MessageWriter* writer,
+ void*& buffer) {
+ SerializableTraits<T>::SerializeObject(value, writer, buffer);
+}
+
+// Serializes the payload of a PointerWrapper.
+template <typename T>
+inline void SerializeObject(const PointerWrapper<T>& pointer,
+ MessageWriter* writer, void*& buffer) {
+ SerializeObject(pointer.Dereference(), writer, buffer);
+}
+
+// Serializes the payload of file descriptor types.
+template <FileHandleMode Mode>
+inline void SerializeObject(const FileHandle<Mode>& fd, MessageWriter* writer,
+ void*& buffer) {
+ SerializeType(fd, buffer);
+ const FileReference value =
+ writer->GetOutputResourceMapper()->PushFileHandle(fd);
+ SerializeRaw(value, buffer);
+}
+
+// Serializes the payload of channel handle types.
+template <ChannelHandleMode Mode>
+inline void SerializeObject(const ChannelHandle<Mode>& handle,
+ MessageWriter* writer, void*& buffer) {
+ SerializeType(handle, buffer);
+ const ChannelReference value =
+ writer->GetOutputResourceMapper()->PushChannelHandle(handle);
+ SerializeRaw(value, buffer);
+}
+
+// Serializes the payload of BufferWrapper types.
+template <typename T, typename Allocator>
+inline void SerializeObject(const BufferWrapper<std::vector<T, Allocator>>& b,
+ MessageWriter* /*writer*/, void*& buffer) {
+ const auto value_type_size =
+ sizeof(typename BufferWrapper<std::vector<T, Allocator>>::value_type);
+ SerializeType(b, buffer);
+ WriteRawData(buffer, b.data(), b.size() * value_type_size);
+}
+template <typename T>
+inline void SerializeObject(const BufferWrapper<T*>& b,
+ MessageWriter* /*writer*/, void*& buffer) {
+ const auto value_type_size = sizeof(typename BufferWrapper<T*>::value_type);
+ SerializeType(b, buffer);
+ WriteRawData(buffer, b.data(), b.size() * value_type_size);
+}
+
+// Serializes the payload of string types.
+template <typename StringType>
+inline void SerializeString(const StringType& s, void*& buffer) {
+ const auto value_type_size = sizeof(typename StringType::value_type);
+ SerializeType(s, buffer);
+ WriteRawData(buffer, s.data(), s.length() * value_type_size);
+}
+
+// Overload of SerializeObject() for std::string and StringWrapper. These types
+// are interchangeable and must serialize to the same format.
+inline void SerializeObject(const std::string& s, MessageWriter* /*writer*/,
+ void*& buffer) {
+ SerializeString(s, buffer);
+}
+template <typename T>
+inline void SerializeObject(const StringWrapper<T>& s,
+ MessageWriter* /*writer*/, void*& buffer) {
+ SerializeString(s, buffer);
+}
+
+// Serializes the payload of array types.
+template <typename ArrayType>
+inline void SerializeArray(const ArrayType& v, MessageWriter* writer,
+ void*& buffer) {
+ SerializeType(v, buffer);
+ for (const auto& element : v)
+ SerializeObject(element, writer, buffer);
+}
+
+// Serializes the payload for map types.
+template <typename MapType>
+inline void SerializeMap(const MapType& v, MessageWriter* writer,
+ void*& buffer) {
+ SerializeType(v, buffer);
+ for (const auto& element : v) {
+ SerializeObject(element.first, writer, buffer);
+ SerializeObject(element.second, writer, buffer);
+ }
+}
+
+// Overload of SerializeObject() for std::vector and ArrayWrapper types. These
+// types are interchangeable and must serialize to the same format.
+template <typename T, typename Allocator>
+inline void SerializeObject(const std::vector<T, Allocator>& v,
+ MessageWriter* writer, void*& buffer) {
+ SerializeArray(v, writer, buffer);
+}
+template <typename T>
+inline void SerializeObject(const ArrayWrapper<T>& v, MessageWriter* writer,
+ void*& buffer) {
+ SerializeArray(v, writer, buffer);
+}
+
+// Overload of SerializeObject() for std::array types. These types serialize to
+// the same format at std::vector and ArrayWrapper and are interchangeable in
+// certain situations.
+template <typename T, std::size_t Size>
+inline void SerializeObject(const std::array<T, Size>& v, MessageWriter* writer,
+ void*& buffer) {
+ SerializeArray(v, writer, buffer);
+}
+
+// Overload of SerializeObject() for std::map types.
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline void SerializeObject(const std::map<Key, T, Compare, Allocator>& v,
+ MessageWriter* writer, void*& buffer) {
+ SerializeMap(v, writer, buffer);
+}
+
+// Overload of SerializeObject() for std::unordered_map types.
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline void SerializeObject(
+ const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& v,
+ MessageWriter* writer, void*& buffer) {
+ SerializeMap(v, writer, buffer);
+}
+
+// Overload of SerializeObject() for std:pair types.
+template <typename T, typename U>
+inline void SerializeObject(const std::pair<T, U>& pair, MessageWriter* writer,
+ void*& buffer) {
+ SerializeType(pair, buffer);
+ SerializeObject(pair.first, writer, buffer);
+ SerializeObject(pair.second, writer, buffer);
+}
+
+// Stops template recursion when the last tuple element is reached.
+template <typename... T>
+inline void SerializeTuple(const std::tuple<T...>&, MessageWriter*, void*&,
+ Index<0>) {}
+
+// Serializes each element of a tuple recursively.
+template <typename... T, std::size_t index>
+inline void SerializeTuple(const std::tuple<T...>& tuple, MessageWriter* writer,
+ void*& buffer, Index<index>) {
+ SerializeTuple(tuple, writer, buffer, Index<index - 1>());
+ SerializeObject(std::get<index - 1>(tuple), writer, buffer);
+}
+
+// Overload of SerializeObject() for tuple types.
+template <typename... T>
+inline void SerializeObject(const std::tuple<T...>& tuple,
+ MessageWriter* writer, void*& buffer) {
+ SerializeType(tuple, buffer);
+ SerializeTuple(tuple, writer, buffer, Index<sizeof...(T)>());
+}
+
+// Stops template recursion when the last member pointer is reached.
+template <typename Members, typename T>
+inline void SerializeMember(const T&, MessageWriter*, void*&, Index<0>) {}
+
+// Serializes each member pointer recursively.
+template <typename Members, typename T, std::size_t index>
+inline void SerializeMember(const T& object, MessageWriter* writer,
+ void*& buffer, Index<index>) {
+ SerializeMember<Members>(object, writer, buffer, Index<index - 1>());
+ SerializeObject(Members::template At<index - 1>::Resolve(object), writer,
+ buffer);
+}
+
+// Serializes the members of a type using the given SerializableMembersType
+// type.
+template <typename Members, typename T>
+inline void SerializeMembers(const T& object, MessageWriter* writer,
+ void*& buffer) {
+ SerializeMember<Members>(object, writer, buffer,
+ Index<Members::MemberCount>());
+}
+
+// Top level serialization function that replaces the buffer's contents.
+template <typename T>
+inline void Serialize(const T& object, MessageWriter* writer) {
+ PDX_TRACE_NAME("Serialize");
+ const std::size_t size = GetSerializedSize(object);
+
+ // Reserve the space needed for the object(s).
+ void* buffer = writer->GetNextWriteBufferSection(size);
+ SerializeObject(object, writer, buffer);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Object Deserialization //
+///////////////////////////////////////////////////////////////////////////////
+
+inline ErrorType ReadRawDataFromNextSection(void* dest, MessageReader* reader,
+ const void*& start,
+ const void*& end, size_t size) {
+ while (AdvancePointer(start, size) > end) {
+ auto remaining_size = PointerDistance(end, start);
+ if (remaining_size > 0) {
+ memcpy(dest, start, remaining_size);
+ dest = AdvancePointer(dest, remaining_size);
+ size -= remaining_size;
+ }
+ reader->ConsumeReadBufferSectionData(AdvancePointer(start, remaining_size));
+ std::tie(start, end) = reader->GetNextReadBufferSection();
+ if (start == end)
+ return ErrorCode::INSUFFICIENT_BUFFER;
+ }
+ memcpy(dest, start, size);
+ start = AdvancePointer(start, size);
+ return ErrorCode::NO_ERROR;
+}
+
+inline ErrorType ReadRawData(void* dest, MessageReader* /*reader*/,
+ const void*& start, const void*& end,
+ size_t size) {
+ if (PDX_UNLIKELY(AdvancePointer(start, size) > end)) {
+ // TODO(avakulenko): Enabling reading from next sections of input buffer
+ // (using ReadRawDataFromNextSection) screws up clang compiler optimizations
+ // (probably inefficient inlining) making the whole deserialization
+ // code path about twice as slow. Investigate and enable more generic
+ // deserialization code, but right now we don't really need/support this
+ // scenario, so I keep this commented out for the time being...
+
+ // return ReadRawDataFromNextSection(dest, reader, start, end, size);
+ return ErrorCode::INSUFFICIENT_BUFFER;
+ }
+ memcpy(dest, start, size);
+ start = AdvancePointer(start, size);
+ return ErrorCode::NO_ERROR;
+}
+
+// Deserializes a primitive object from raw bytes.
+template <typename T,
+ typename = typename std::enable_if<std::is_pod<T>::value>::type>
+inline ErrorType DeserializeRaw(T* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ return ReadRawData(value, reader, start, end, sizeof(T));
+}
+
+// Utility to deserialize POD types when the serialized type is different
+// (smaller) than the target real type. This happens when values are serialized
+// into more compact encodings.
+template <typename SerializedType, typename RealType>
+ErrorType DeserializeValue(RealType* real_value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ SerializedType serialized_value;
+ if (const auto error =
+ DeserializeRaw(&serialized_value, reader, start, end)) {
+ return error;
+ } else {
+ *real_value = serialized_value;
+ return ErrorCode::NO_ERROR;
+ }
+}
+
+inline ErrorType DeserializeEncoding(EncodingType* encoding,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ return DeserializeRaw(encoding, reader, start, end);
+}
+
+// Overload to deserialize bool type.
+inline ErrorType DeserializeObject(bool* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsBoolEncoding(encoding)) {
+ *value = (encoding == ENCODING_TYPE_TRUE);
+ return ErrorCode::NO_ERROR;
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_BOOL,
+ encoding);
+ }
+}
+
+// Specializations to deserialize float and double types.
+inline ErrorType DeserializeObject(float* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsFloat32Encoding(encoding)) {
+ return DeserializeValue<float>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_FLOAT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(double* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsFloat32Encoding(encoding)) {
+ return DeserializeValue<float>(value, reader, start, end);
+ } else if (IsFloat64Encoding(encoding)) {
+ return DeserializeValue<double>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_FLOAT,
+ encoding);
+ }
+}
+
+// Specializations to deserialize standard integer types.
+inline ErrorType DeserializeObject(char* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsUnsignedFixintEncoding(encoding)) {
+ *value = static_cast<char>(encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (IsUInt8Encoding(encoding)) {
+ return DeserializeValue<char>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::int8_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixintEncoding(encoding)) {
+ *value = static_cast<std::int8_t>(encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (IsInt8Encoding(encoding)) {
+ return DeserializeValue<std::int8_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_INT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::uint8_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsUnsignedFixintEncoding(encoding)) {
+ *value = encoding;
+ return ErrorCode::NO_ERROR;
+ } else if (IsUInt8Encoding(encoding)) {
+ return DeserializeValue<std::uint8_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::int16_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixintEncoding(encoding)) {
+ *value = static_cast<std::int8_t>(encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (IsInt8Encoding(encoding)) {
+ return DeserializeValue<std::int8_t>(value, reader, start, end);
+ } else if (IsInt16Encoding(encoding)) {
+ return DeserializeValue<std::int16_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_INT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::uint16_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsUnsignedFixintEncoding(encoding)) {
+ *value = encoding;
+ return ErrorCode::NO_ERROR;
+ } else if (IsUInt8Encoding(encoding)) {
+ return DeserializeValue<std::uint8_t>(value, reader, start, end);
+ } else if (IsUInt16Encoding(encoding)) {
+ return DeserializeValue<std::uint16_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::int32_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixintEncoding(encoding)) {
+ *value = static_cast<std::int8_t>(encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (IsInt8Encoding(encoding)) {
+ return DeserializeValue<std::int8_t>(value, reader, start, end);
+ } else if (IsInt16Encoding(encoding)) {
+ return DeserializeValue<std::int16_t>(value, reader, start, end);
+ } else if (IsInt32Encoding(encoding)) {
+ return DeserializeValue<std::int32_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_INT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::uint32_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsUnsignedFixintEncoding(encoding)) {
+ *value = encoding;
+ return ErrorCode::NO_ERROR;
+ } else if (IsUInt8Encoding(encoding)) {
+ return DeserializeValue<std::uint8_t>(value, reader, start, end);
+ } else if (IsUInt16Encoding(encoding)) {
+ return DeserializeValue<std::uint16_t>(value, reader, start, end);
+ } else if (IsUInt32Encoding(encoding)) {
+ return DeserializeValue<std::uint32_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::int64_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixintEncoding(encoding)) {
+ *value = static_cast<std::int8_t>(encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (IsInt8Encoding(encoding)) {
+ return DeserializeValue<std::int8_t>(value, reader, start, end);
+ } else if (IsInt16Encoding(encoding)) {
+ return DeserializeValue<std::int16_t>(value, reader, start, end);
+ } else if (IsInt32Encoding(encoding)) {
+ return DeserializeValue<std::int32_t>(value, reader, start, end);
+ } else if (IsInt64Encoding(encoding)) {
+ return DeserializeValue<std::int64_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_INT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::uint64_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsUnsignedFixintEncoding(encoding)) {
+ *value = encoding;
+ return ErrorCode::NO_ERROR;
+ } else if (IsUInt8Encoding(encoding)) {
+ return DeserializeValue<std::uint8_t>(value, reader, start, end);
+ } else if (IsUInt16Encoding(encoding)) {
+ return DeserializeValue<std::uint16_t>(value, reader, start, end);
+ } else if (IsUInt32Encoding(encoding)) {
+ return DeserializeValue<std::uint32_t>(value, reader, start, end);
+ } else if (IsUInt64Encoding(encoding)) {
+ return DeserializeValue<std::uint64_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT,
+ encoding);
+ }
+}
+
+template <typename T>
+inline EnableIfEnum<T, ErrorType> DeserializeObject(T* value,
+ MessageReader* reader,
+ const void*& start,
+ const void*& end) {
+ std::underlying_type_t<T> enum_value;
+ ErrorType error = DeserializeObject(&enum_value, reader, start, end);
+ if (!error)
+ *value = static_cast<T>(enum_value);
+ return error;
+}
+
+// Forward declarations for nested definitions.
+template <typename T, typename Enabled>
+inline ErrorType DeserializeObject(T*, MessageReader*, const void*&,
+ const void*&);
+template <typename T>
+inline ErrorType DeserializeObject(PointerWrapper<T>*, MessageReader*,
+ const void*&, const void*&);
+inline ErrorType DeserializeObject(LocalHandle*, MessageReader*, const void*&,
+ const void*&);
+inline ErrorType DeserializeObject(LocalChannelHandle*, MessageReader*,
+ const void*&, const void*&);
+template <typename T, typename Allocator>
+inline ErrorType DeserializeObject(BufferWrapper<std::vector<T, Allocator>>*,
+ MessageReader*, const void*&, const void*&);
+template <typename T>
+inline ErrorType DeserializeObject(BufferWrapper<T*>*, MessageReader*,
+ const void*&, const void*&);
+inline ErrorType DeserializeObject(std::string*, MessageReader*, const void*&,
+ const void*&);
+template <typename T>
+inline ErrorType DeserializeObject(StringWrapper<T>*, MessageReader*,
+ const void*&, const void*&);
+template <typename T, typename U>
+inline ErrorType DeserializeObject(std::pair<T, U>*, MessageReader*,
+ const void*&, const void*&);
+template <typename... T>
+inline ErrorType DeserializeObject(std::tuple<T...>*, MessageReader*,
+ const void*&, const void*&);
+template <typename T, typename Allocator>
+inline ErrorType DeserializeObject(std::vector<T, Allocator>*, MessageReader*,
+ const void*&, const void*&);
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline ErrorType DeserializeObject(std::map<Key, T, Compare, Allocator>*,
+ MessageReader*, const void*&, const void*&);
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline ErrorType DeserializeObject(
+ std::unordered_map<Key, T, Hash, KeyEqual, Allocator>*, MessageReader*,
+ const void*&, const void*&);
+template <typename T>
+inline ErrorType DeserializeObject(ArrayWrapper<T>*, MessageReader*,
+ const void*&, const void*&);
+template <typename T, std::size_t Size>
+inline ErrorType DeserializeObject(std::array<T, Size>*, MessageReader*,
+ const void*&, const void*&);
+template <typename T, typename U>
+inline ErrorType DeserializeObject(std::pair<T, U>*, MessageReader*,
+ const void*&, const void*&);
+template <typename... T>
+inline ErrorType DeserializeObject(std::tuple<T...>*, MessageReader*,
+ const void*&, const void*&);
+inline ErrorType DeserializeObject(EmptyVariant*,
+ MessageReader*, const void*&,
+ const void*&);
+template <typename... Types>
+inline ErrorType DeserializeObject(Variant<Types...>*,
+ MessageReader*, const void*&,
+ const void*&);
+
+// Deserializes a Serializable type.
+template <typename T, typename Enable = EnableIfHasSerializableMembers<T>>
+inline ErrorType DeserializeObject(T* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ return SerializableTraits<T>::DeserializeObject(value, reader, start, end);
+}
+
+// Deserializes a PointerWrapper.
+template <typename T>
+inline ErrorType DeserializeObject(PointerWrapper<T>* pointer,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ return DeserializeObject(&pointer->Dereference(), reader, start, end);
+}
+
+// Deserializes the type code and size for extension types.
+inline ErrorType DeserializeExtType(EncodingType* encoding,
+ EncodingExtType* type, std::size_t* size,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ if (const auto error = DeserializeEncoding(encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixextEncoding(*encoding)) {
+ *size = GetFixextSize(*encoding);
+ } else if (*encoding == ENCODING_TYPE_EXT8) {
+ if (const auto error =
+ DeserializeValue<std::uint8_t>(size, reader, start, end))
+ return error;
+ } else if (*encoding == ENCODING_TYPE_EXT16) {
+ if (const auto error =
+ DeserializeValue<std::uint16_t>(size, reader, start, end))
+ return error;
+ } else if (*encoding == ENCODING_TYPE_EXT32) {
+ if (const auto error =
+ DeserializeValue<std::uint32_t>(size, reader, start, end))
+ return error;
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_EXTENSION,
+ *encoding);
+ }
+
+ // The extension type code follows the encoding and size.
+ return DeserializeRaw(type, reader, start, end);
+}
+
+// Deserializes a file handle and performs handle space translation, if
+// required.
+inline ErrorType DeserializeObject(LocalHandle* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ EncodingExtType type;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeExtType(&encoding, &type, &size, reader, start, end)) {
+ return error;
+ } else if (size != 2) {
+ return ErrorType(ErrorCode::UNEXPECTED_TYPE_SIZE, ENCODING_CLASS_EXTENSION,
+ encoding);
+ } else if (type == ENCODING_EXT_TYPE_FILE_DESCRIPTOR) {
+ // Read the encoded file descriptor value.
+ FileReference ref;
+ if (const auto error = DeserializeRaw(&ref, reader, start, end)) {
+ return error;
+ }
+
+ return reader->GetInputResourceMapper()->GetFileHandle(ref, value)
+ ? ErrorCode::NO_ERROR
+ : ErrorCode::GET_FILE_DESCRIPTOR_FAILED;
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_EXTENSION,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(LocalChannelHandle* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+ EncodingExtType type;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeExtType(&encoding, &type, &size, reader, start, end)) {
+ return error;
+ } else if (size != 4) {
+ return ErrorType(ErrorCode::UNEXPECTED_TYPE_SIZE, ENCODING_CLASS_EXTENSION,
+ encoding);
+ } else if (type == ENCODING_EXT_TYPE_CHANNEL_HANDLE) {
+ // Read the encoded channel handle value.
+ ChannelReference ref;
+ if (const auto error = DeserializeRaw(&ref, reader, start, end)) {
+ return error;
+ }
+ return reader->GetInputResourceMapper()->GetChannelHandle(ref, value)
+ ? ErrorCode::NO_ERROR
+ : ErrorCode::GET_CHANNEL_HANDLE_FAILED;
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_EXTENSION,
+ encoding);
+ }
+}
+
+// Deserializes the type code and size for bin types.
+inline ErrorType DeserializeBinType(EncodingType* encoding, std::size_t* size,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ if (const auto error = DeserializeEncoding(encoding, reader, start, end)) {
+ return error;
+ } else if (*encoding == ENCODING_TYPE_BIN8) {
+ return DeserializeValue<std::uint8_t>(size, reader, start, end);
+ } else if (*encoding == ENCODING_TYPE_BIN16) {
+ return DeserializeValue<std::uint16_t>(size, reader, start, end);
+ } else if (*encoding == ENCODING_TYPE_BIN32) {
+ return DeserializeValue<std::uint32_t>(size, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_BINARY,
+ *encoding);
+ }
+}
+
+// Overload of DeserializeObject() for BufferWrapper types.
+template <typename T, typename Allocator>
+inline ErrorType DeserializeObject(
+ BufferWrapper<std::vector<T, Allocator>>* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ const auto value_type_size =
+ sizeof(typename BufferWrapper<std::vector<T, Allocator>>::value_type);
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeBinType(&encoding, &size, reader, start, end))
+ return error;
+
+ // Try to resize the BufferWrapper to the size of the payload.
+ value->resize(size / value_type_size);
+
+ if (size > value->size() * value_type_size || size % value_type_size != 0) {
+ return ErrorCode::INSUFFICIENT_DESTINATION_SIZE;
+ } else if (size == 0U) {
+ return ErrorCode::NO_ERROR;
+ } else {
+ return ReadRawData(value->data(), reader, start, end, size);
+ }
+}
+template <typename T>
+inline ErrorType DeserializeObject(BufferWrapper<T*>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ const auto value_type_size = sizeof(typename BufferWrapper<T*>::value_type);
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeBinType(&encoding, &size, reader, start, end))
+ return error;
+
+ // Try to resize the BufferWrapper to the size of the payload.
+ value->resize(size / value_type_size);
+
+ if (size > value->size() * value_type_size || size % value_type_size != 0) {
+ return ErrorCode::INSUFFICIENT_DESTINATION_SIZE;
+ } else if (size == 0U) {
+ return ErrorCode::NO_ERROR;
+ } else {
+ return ReadRawData(value->data(), reader, start, end, size);
+ }
+}
+
+// Deserializes the type code and size for string types.
+inline ErrorType DeserializeStringType(EncodingType* encoding,
+ std::size_t* size, MessageReader* reader,
+ const void*& start, const void*& end) {
+ if (const auto error = DeserializeEncoding(encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixstrEncoding(*encoding)) {
+ *size = GetFixstrSize(*encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (*encoding == ENCODING_TYPE_STR8) {
+ return DeserializeValue<std::uint8_t>(size, reader, start, end);
+ } else if (*encoding == ENCODING_TYPE_STR16) {
+ return DeserializeValue<std::uint16_t>(size, reader, start, end);
+ } else if (*encoding == ENCODING_TYPE_STR32) {
+ return DeserializeValue<std::uint32_t>(size, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_STRING,
+ *encoding);
+ }
+}
+
+// Overload of DeserializeObject() for std::string types.
+inline ErrorType DeserializeObject(std::string* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeStringType(&encoding, &size, reader, start, end)) {
+ return error;
+ } else if (size == 0U) {
+ value->clear();
+ return ErrorCode::NO_ERROR;
+ } else {
+ value->resize(size);
+ return ReadRawData(&(*value)[0], reader, start, end, size);
+ }
+}
+
+// Overload of DeserializeObject() for StringWrapper types.
+template <typename T>
+inline ErrorType DeserializeObject(StringWrapper<T>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ const auto value_type_size = sizeof(typename StringWrapper<T>::value_type);
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeStringType(&encoding, &size, reader, start, end))
+ return error;
+
+ // Try to resize the StringWrapper to the size of the payload
+ // string.
+ value->resize(size / value_type_size);
+
+ if (size > value->length() * value_type_size || size % value_type_size != 0) {
+ return ErrorCode::INSUFFICIENT_DESTINATION_SIZE;
+ } else if (size == 0U) {
+ return ErrorCode::NO_ERROR;
+ } else {
+ return ReadRawData(value->data(), reader, start, end, size);
+ }
+}
+
+// Deserializes the type code and size of array types.
+inline ErrorType DeserializeArrayType(EncodingType* encoding, std::size_t* size,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ if (const auto error = DeserializeEncoding(encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixarrayEncoding(*encoding)) {
+ *size = GetFixarraySize(*encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (*encoding == ENCODING_TYPE_ARRAY16) {
+ return DeserializeValue<std::uint16_t>(size, reader, start, end);
+ } else if (*encoding == ENCODING_TYPE_ARRAY32) {
+ return DeserializeValue<std::uint32_t>(size, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_ARRAY,
+ *encoding);
+ }
+}
+
+// Deserializes the type code and size of map types.
+inline ErrorType DeserializeMapType(EncodingType* encoding, std::size_t* size,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ if (const auto error = DeserializeEncoding(encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixmapEncoding(*encoding)) {
+ *size = GetFixmapSize(*encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (*encoding == ENCODING_TYPE_MAP16) {
+ return DeserializeValue<std::uint16_t>(size, reader, start, end);
+ } else if (*encoding == ENCODING_TYPE_MAP32) {
+ return DeserializeValue<std::uint32_t>(size, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_MAP,
+ *encoding);
+ }
+}
+
+// Overload for std::vector types.
+template <typename T, typename Allocator>
+inline ErrorType DeserializeObject(std::vector<T, Allocator>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeArrayType(&encoding, &size, reader, start, end))
+ return error;
+
+ std::vector<T, Allocator> result(size);
+ for (std::size_t i = 0; i < size; i++) {
+ if (const auto error = DeserializeObject(&result[i], reader, start, end))
+ return error;
+ }
+
+ *value = std::move(result);
+ return ErrorCode::NO_ERROR;
+
+// TODO(eieio): Consider the benefits and trade offs of this alternative.
+#if 0
+ value->resize(size);
+ for (std::size_t i = 0; i < size; i++) {
+ if (const auto error = DeserializeObject(&(*value)[i], reader, start, end))
+ return error;
+ }
+ return ErrorCode::NO_ERROR;
+#endif
+}
+
+// Deserializes an EmptyVariant value.
+inline ErrorType DeserializeObject(EmptyVariant* /*empty*/,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (encoding != ENCODING_TYPE_NIL) {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_MAP,
+ encoding);
+ } else {
+ return ErrorCode::NO_ERROR;
+ }
+}
+
+// Deserializes a Variant type.
+template <typename... Types>
+inline ErrorType DeserializeObject(Variant<Types...>* variant,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeMapType(&encoding, &size, reader, start, end)) {
+ return error;
+ }
+
+ if (size != 1)
+ return ErrorType(ErrorCode::UNEXPECTED_TYPE_SIZE, ENCODING_CLASS_MAP,
+ encoding);
+
+ std::int32_t type;
+ if (const auto error = DeserializeObject(&type, reader, start, end)) {
+ return error;
+ } else if (type < Variant<Types...>::kEmptyIndex ||
+ type >= static_cast<std::int32_t>(sizeof...(Types))) {
+ return ErrorCode::INVALID_VARIANT_ELEMENT;
+ } else {
+ variant->Become(type);
+ return variant->Visit([reader, &start, &end](auto&& value) {
+ return DeserializeObject(&value, reader, start, end);
+ });
+ }
+}
+
+// Deserializes map types.
+template <typename MapType>
+inline ErrorType DeserializeMap(MapType* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeMapType(&encoding, &size, reader, start, end))
+ return error;
+
+ MapType result;
+ for (std::size_t i = 0; i < size; i++) {
+ std::pair<typename MapType::key_type, typename MapType::mapped_type>
+ element;
+ if (const auto error =
+ DeserializeObject(&element.first, reader, start, end))
+ return error;
+ if (const auto error =
+ DeserializeObject(&element.second, reader, start, end))
+ return error;
+ result.emplace(std::move(element));
+ }
+
+ *value = std::move(result);
+ return ErrorCode::NO_ERROR;
+}
+
+// Overload for std::map types.
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline ErrorType DeserializeObject(std::map<Key, T, Compare, Allocator>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ return DeserializeMap(value, reader, start, end);
+}
+
+// Overload for std::unordered_map types.
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline ErrorType DeserializeObject(
+ std::unordered_map<Key, T, Hash, KeyEqual, Allocator>* value,
+ MessageReader* reader, const void*& start, const void*& end) {
+ return DeserializeMap(value, reader, start, end);
+}
+
+// Overload for ArrayWrapper types.
+template <typename T>
+inline ErrorType DeserializeObject(ArrayWrapper<T>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeArrayType(&encoding, &size, reader, start, end)) {
+ return error;
+ }
+
+ // Try to resize the wrapper.
+ value->resize(size);
+
+ // Make sure there is enough space in the ArrayWrapper for the
+ // payload.
+ if (size > value->capacity())
+ return ErrorCode::INSUFFICIENT_DESTINATION_SIZE;
+
+ for (std::size_t i = 0; i < size; i++) {
+ if (const auto error = DeserializeObject(&(*value)[i], reader, start, end))
+ return error;
+ }
+
+ return ErrorCode::NO_ERROR;
+}
+
+// Overload for std::array types.
+template <typename T, std::size_t Size>
+inline ErrorType DeserializeObject(std::array<T, Size>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeArrayType(&encoding, &size, reader, start, end)) {
+ return error;
+ }
+
+ if (size != Size)
+ return ErrorCode::INSUFFICIENT_DESTINATION_SIZE;
+
+ for (std::size_t i = 0; i < size; i++) {
+ if (const auto error = DeserializeObject(&(*value)[i], reader, start, end))
+ return error;
+ }
+
+ return ErrorCode::NO_ERROR;
+}
+
+// Deserializes std::pair types.
+template <typename T, typename U>
+inline ErrorType DeserializeObject(std::pair<T, U>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeArrayType(&encoding, &size, reader, start, end)) {
+ return error;
+ } else if (size != 2) {
+ return ErrorCode::UNEXPECTED_TYPE_SIZE;
+ } else if (const auto error =
+ DeserializeObject(&value->first, reader, start, end)) {
+ return error;
+ } else if (const auto error =
+ DeserializeObject(&value->second, reader, start, end)) {
+ return error;
+ } else {
+ return ErrorCode::NO_ERROR;
+ }
+}
+
+// Stops template recursion when the last tuple element is reached.
+template <typename... T>
+inline ErrorType DeserializeTuple(std::tuple<T...>*, MessageReader*,
+ const void*&, const void*, Index<0>) {
+ return ErrorCode::NO_ERROR;
+}
+
+// Deserializes each element of a tuple recursively.
+template <typename... T, std::size_t index>
+inline ErrorType DeserializeTuple(std::tuple<T...>* tuple,
+ MessageReader* reader, const void*& start,
+ const void*& end, Index<index>) {
+ if (const auto error =
+ DeserializeTuple(tuple, reader, start, end, Index<index - 1>()))
+ return error;
+ else
+ return DeserializeObject(&std::get<index - 1>(*tuple), reader, start, end);
+}
+
+// Overload for standard tuple types.
+template <typename... T>
+inline ErrorType DeserializeObject(std::tuple<T...>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeArrayType(&encoding, &size, reader, start, end)) {
+ return error;
+ } else if (size != sizeof...(T)) {
+ return ErrorCode::UNEXPECTED_TYPE_SIZE;
+ } else {
+ return DeserializeTuple(value, reader, start, end, Index<sizeof...(T)>());
+ }
+}
+
+// Stops template recursion when the last member of a Serializable type is
+// reached.
+template <typename Members, typename T>
+inline ErrorType DeserializeMember(T*, MessageReader*, const void*&,
+ const void*, Index<0>) {
+ return ErrorCode::NO_ERROR;
+}
+
+// Deserializes each member of a Serializable type recursively.
+template <typename Members, typename T, std::size_t index>
+inline ErrorType DeserializeMember(T* value, MessageReader* reader,
+ const void*& start, const void*& end,
+ Index<index>) {
+ if (const auto error = DeserializeMember<Members>(value, reader, start, end,
+ Index<index - 1>()))
+ return error;
+ else
+ return DeserializeObject(&Members::template At<index - 1>::Resolve(*value),
+ reader, start, end);
+}
+
+// Deserializes the members of a Serializable type using the given
+// SerializableMembersType type.
+template <typename Members, typename T>
+inline ErrorType DeserializeMembers(T* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ return DeserializeMember<Members>(value, reader, start, end,
+ Index<Members::MemberCount>());
+}
+
+// Top level deserialization function.
+template <typename T>
+inline ErrorType Deserialize(T* value, MessageReader* reader) {
+ PDX_TRACE_NAME("Deserialize");
+ MessageReader::BufferSection section = reader->GetNextReadBufferSection();
+ if (section.first == section.second)
+ return ErrorCode::INSUFFICIENT_BUFFER;
+ ErrorType error =
+ DeserializeObject(value, reader, section.first, section.second);
+ reader->ConsumeReadBufferSectionData(section.first);
+ return error;
+}
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_SERIALIZATION_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/string_wrapper.h b/libs/vr/libpdx/private/pdx/rpc/string_wrapper.h
new file mode 100644
index 0000000..19fc4c1
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/string_wrapper.h
@@ -0,0 +1,129 @@
+#ifndef ANDROID_PDX_RPC_STRING_WRAPPER_H_
+#define ANDROID_PDX_RPC_STRING_WRAPPER_H_
+
+#include <cstddef>
+#include <cstring>
+#include <string>
+#include <type_traits>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Wrapper class for C string buffers, providing an interface suitable for
+// SerializeObject and DeserializeObject. This class serializes to the same
+// format as std::basic_string, and may be substituted for std::basic_string
+// during serialization and deserialization. This substitution makes handling of
+// C strings more efficient by avoiding unnecessary copies when remote method
+// signatures specify std::basic_string arguments or return values.
+template <typename CharT = std::string::value_type,
+ typename Traits = std::char_traits<CharT>>
+class StringWrapper {
+ public:
+ // Define types in the style of STL strings to support STL operators.
+ typedef Traits traits_type;
+ typedef typename Traits::char_type value_type;
+ typedef std::size_t size_type;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+
+ StringWrapper() : buffer_(nullptr), capacity_(0), end_(0) {}
+
+ StringWrapper(pointer buffer, size_type capacity, size_type size)
+ : buffer_(&buffer[0]),
+ capacity_(capacity),
+ end_(capacity < size ? capacity : size) {}
+
+ StringWrapper(pointer buffer, size_type size)
+ : StringWrapper(buffer, size, size) {}
+
+ explicit StringWrapper(pointer buffer)
+ : StringWrapper(buffer, std::strlen(buffer)) {}
+
+ StringWrapper(const StringWrapper& other) { *this = other; }
+
+ StringWrapper(StringWrapper&& other) { *this = std::move(other); }
+
+ StringWrapper& operator=(const StringWrapper& other) {
+ if (&other == this) {
+ return *this;
+ } else {
+ buffer_ = other.buffer_;
+ capacity_ = other.capacity_;
+ end_ = other.end_;
+ }
+
+ return *this;
+ }
+
+ StringWrapper& operator=(StringWrapper&& other) {
+ if (&other == this) {
+ return *this;
+ } else {
+ buffer_ = other.buffer_;
+ capacity_ = other.capacity_;
+ end_ = other.end_;
+ other.buffer_ = nullptr;
+ other.capacity_ = 0;
+ other.end_ = 0;
+ }
+
+ return *this;
+ }
+
+ pointer data() { return buffer_; }
+ const_pointer data() const { return buffer_; }
+
+ pointer begin() { return &buffer_[0]; }
+ pointer end() { return &buffer_[end_]; }
+ const_pointer begin() const { return &buffer_[0]; }
+ const_pointer end() const { return &buffer_[end_]; }
+
+ size_type size() const { return end_; }
+ size_type length() const { return end_; }
+ size_type max_size() const { return capacity_; }
+ size_type capacity() const { return capacity_; }
+
+ void resize(size_type size) {
+ if (size <= capacity_)
+ end_ = size;
+ else
+ end_ = capacity_;
+ }
+
+ reference operator[](size_type pos) { return buffer_[pos]; }
+ const_reference operator[](size_type pos) const { return buffer_[pos]; }
+
+ private:
+ pointer buffer_;
+ size_type capacity_;
+ size_type end_;
+};
+
+// Utility functions that infer the underlying type of the string, simplifying
+// the wrapper interface.
+
+// TODO(eieio): Wrapping std::basic_string is here for completeness, but is it
+// useful?
+template <typename T, typename... Any>
+StringWrapper<const T> WrapString(const std::basic_string<T, Any...>& s) {
+ return StringWrapper<const T>(s.c_str(), s.length());
+}
+
+template <typename T, typename SizeType = std::size_t>
+StringWrapper<T> WrapString(T* s, SizeType size) {
+ return StringWrapper<T>(s, size);
+}
+
+template <typename T>
+StringWrapper<T> WrapString(T* s) {
+ return StringWrapper<T>(s);
+}
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_STRING_WRAPPER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/thread_local_buffer.h b/libs/vr/libpdx/private/pdx/rpc/thread_local_buffer.h
new file mode 100644
index 0000000..e5ef2aa
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/thread_local_buffer.h
@@ -0,0 +1,134 @@
+#ifndef ANDROID_PDX_RPC_THREAD_LOCAL_BUFFER_H_
+#define ANDROID_PDX_RPC_THREAD_LOCAL_BUFFER_H_
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+#include <pdx/rpc/default_initialization_allocator.h>
+#include <pdx/trace.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility class to distinguish between different thread local entries or
+// "slots" in the thread local variable table. Each slot is uniquely identified
+// by (T,Index) and is independent of any other slot.
+template <typename T, std::size_t Index>
+struct ThreadLocalSlot;
+
+// Utility class to specify thread local slots using only a type.
+template <typename T>
+struct ThreadLocalTypeSlot;
+
+// Utility class to specify thread local slots using only an index.
+template <std::size_t Index>
+struct ThreadLocalIndexSlot;
+
+// Initial capacity of thread local buffer, unless otherwise specified.
+constexpr std::size_t InitialBufferCapacity = 4096;
+
+// Thread local slots for buffers used by this library to send, receive, and
+// reply to messages.
+using SendBuffer = ThreadLocalIndexSlot<0>;
+using ReceiveBuffer = ThreadLocalIndexSlot<1>;
+using ReplyBuffer = ThreadLocalIndexSlot<2>;
+
+// Provides a simple interface to thread local buffers for large IPC messages.
+// Slot provides multiple thread local slots for a given T, Allocator, Capacity
+// combination.
+template <typename T, typename Allocator = DefaultInitializationAllocator<T>,
+ std::size_t Capacity = InitialBufferCapacity,
+ typename Slot = ThreadLocalSlot<void, 0>>
+class ThreadLocalBuffer {
+ public:
+ using BufferType = std::vector<T, Allocator>;
+ using ValueType = T;
+
+ // Reserves |capacity| number of elements of capacity in the underlying
+ // buffer. Call this during startup to avoid allocation during use.
+ static void Reserve(std::size_t capacity) {
+ PDX_TRACE_NAME("ThreadLocalBuffer::Reserve");
+ InitializeBuffer(capacity);
+ buffer_->reserve(capacity);
+ }
+
+ // Resizes the buffer to |size| elements.
+ static void Resize(std::size_t size) {
+ PDX_TRACE_NAME("ThreadLocalBuffer::Resize");
+ InitializeBuffer(size);
+ buffer_->resize(size);
+ }
+
+ // Gets a reference to the underlying buffer after reserving |capacity|
+ // elements. The current size of the buffer is left intact. The returned
+ // reference is valid until FreeBuffer() is called.
+ static BufferType& GetBuffer(std::size_t capacity = Capacity) {
+ PDX_TRACE_NAME("ThreadLocalBuffer::GetBuffer");
+ Reserve(capacity);
+ return *buffer_;
+ }
+
+ // Gets a reference to the underlying buffer after reserving |Capacity|
+ // elements. The current size of the buffer is set to zero. The returned
+ // reference is valid until FreeBuffer() is called.
+ static BufferType& GetEmptyBuffer() {
+ PDX_TRACE_NAME("ThreadLocalBuffer::GetEmptyBuffer");
+ Reserve(Capacity);
+ buffer_->clear();
+ return *buffer_;
+ }
+
+ // Gets a reference to the underlying buffer after resizing it to |size|
+ // elements. The returned reference is valid until FreeBuffer() is called.
+ static BufferType& GetSizedBuffer(std::size_t size = Capacity) {
+ PDX_TRACE_NAME("ThreadLocalBuffer::GetSizedBuffer");
+ Resize(size);
+ return *buffer_;
+ }
+
+ // Frees the underlying buffer. The buffer will be reallocated if any of the
+ // methods above are called.
+ static void FreeBuffer() {
+ if (buffer_) {
+ GetBufferGuard().reset(buffer_ = nullptr);
+ }
+ }
+
+ private:
+ friend class ThreadLocalBufferTest;
+
+ static void InitializeBuffer(std::size_t capacity) {
+ if (!buffer_) {
+ GetBufferGuard().reset(buffer_ = new BufferType(capacity));
+ }
+ }
+
+ // Work around performance issues with thread-local dynamic initialization
+ // semantics by using a normal pointer in parallel with a std::unique_ptr. The
+ // std::unique_ptr is never dereferenced, only assigned, to avoid the high
+ // cost of dynamic initialization checks, while still providing automatic
+ // cleanup. The normal pointer provides fast access to the buffer object.
+ // Never dereference buffer_guard or performance could be severely impacted
+ // by slow implementations of TLS dynamic initialization.
+ static thread_local BufferType* buffer_;
+
+ static std::unique_ptr<BufferType>& GetBufferGuard() {
+ PDX_TRACE_NAME("ThreadLocalBuffer::GetBufferGuard");
+ static thread_local std::unique_ptr<BufferType> buffer_guard;
+ return buffer_guard;
+ }
+};
+
+// Instantiation of the static ThreadLocalBuffer::buffer_ member.
+template <typename T, typename Allocator, std::size_t Capacity, typename Slot>
+thread_local
+ typename ThreadLocalBuffer<T, Allocator, Capacity, Slot>::BufferType*
+ ThreadLocalBuffer<T, Allocator, Capacity, Slot>::buffer_;
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_THREAD_LOCAL_BUFFER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/type_operators.h b/libs/vr/libpdx/private/pdx/rpc/type_operators.h
new file mode 100644
index 0000000..811bd87
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/type_operators.h
@@ -0,0 +1,195 @@
+#ifndef ANDROID_PDX_RPC_TYPE_OPERATORS_H_
+#define ANDROID_PDX_RPC_TYPE_OPERATORS_H_
+
+#include <array>
+#include <map>
+#include <type_traits>
+#include <unordered_map>
+#include <vector>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/rpc/array_wrapper.h>
+#include <pdx/rpc/buffer_wrapper.h>
+#include <pdx/rpc/copy_cv_reference.h>
+#include <pdx/rpc/pointer_wrapper.h>
+#include <pdx/rpc/string_wrapper.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Simplifies type expressions.
+template <typename T>
+using Decay = typename std::decay<T>::type;
+
+// Compares the underlying type of A and B.
+template <typename A, typename B>
+using IsEquivalent = typename std::is_same<Decay<A>, Decay<B>>::type;
+
+// Logical AND over template parameter pack.
+template <typename... T>
+struct And : std::false_type {};
+template <typename A, typename B>
+struct And<A, B> : std::integral_constant<bool, A::value && B::value> {};
+template <typename A, typename B, typename... Rest>
+struct And<A, B, Rest...> : And<A, And<B, Rest...>> {};
+
+// Determines whether A is convertible to B (serializes to the same format)
+// using these rules:
+// 1. std:vector<T, Any...> is convertible to ArrayWrapper<T>.
+// 2. ArrayWrapper<T> is convertible to std:vector<T, Any...>.
+// 3. std::basic_string<T, Any...> is convertible to StringWrapper<T>.
+// 4. StringWrapper<T> is convertible to std::basic_string<T, Any...>.
+// 5. BufferWrapper<T*> is convertible to BufferWrapper<std::vector<T,
+// Any...>>.
+// 6. BufferWrapper<std::vector<T, ...>> is convertible to BufferWrapper<T*>.
+// 7. The value type T of A and B must match.
+
+// Compares A and B for convertibility. This base type determines convertibility
+// by equivalence of the underlying types of A and B. Specializations of this
+// type handle the rules for which complex types are convertible.
+template <typename A, typename B>
+struct IsConvertible : IsEquivalent<A, B> {};
+
+// Compares TT<A, ...> and TT<B, ...>; these are convertible if A and B are
+// convertible.
+template <template <typename, typename...> class TT, typename A, typename B,
+ typename... AnyA, typename... AnyB>
+struct IsConvertible<TT<A, AnyA...>, TT<B, AnyB...>>
+ : IsConvertible<Decay<A>, Decay<B>> {};
+
+// Compares TT<KeyA, ValueA, ...> and TT<KeyB, ValueB, ...>; these are
+// convertible if KeyA and KeyB are
+// convertible and ValueA and ValueB are convertible.
+template <template <typename, typename, typename...> class TT, typename KeyA,
+ typename ValueA, typename KeyB, typename ValueB, typename... AnyA,
+ typename... AnyB>
+struct IsConvertible<TT<KeyA, ValueA, AnyA...>, TT<KeyB, ValueB, AnyB...>>
+ : And<IsConvertible<Decay<KeyA>, Decay<KeyB>>,
+ IsConvertible<Decay<ValueA>, Decay<ValueB>>> {};
+
+// Compares two std::pairs to see if the corresponding elements are convertible.
+template <typename A, typename B, typename C, typename D>
+struct IsConvertible<std::pair<A, B>, std::pair<C, D>>
+ : And<IsConvertible<Decay<A>, Decay<C>>,
+ IsConvertible<Decay<B>, Decay<D>>> {};
+
+// Compares std::pair with a two-element std::tuple to see if the corresponding
+// elements are convertible.
+template <typename A, typename B, typename C, typename D>
+struct IsConvertible<std::pair<A, B>, std::tuple<C, D>>
+ : And<IsConvertible<Decay<A>, Decay<C>>,
+ IsConvertible<Decay<B>, Decay<D>>> {};
+template <typename A, typename B, typename C, typename D>
+struct IsConvertible<std::tuple<A, B>, std::pair<C, D>>
+ : And<IsConvertible<Decay<A>, Decay<C>>,
+ IsConvertible<Decay<B>, Decay<D>>> {};
+
+// Compares two std::tuples to see if the corresponding elements are
+// convertible.
+template <typename... A, typename... B>
+struct IsConvertible<std::tuple<A...>, std::tuple<B...>>
+ : And<IsConvertible<Decay<A>, Decay<B>>...> {};
+
+// Compares std::vector, std::array, and ArrayWrapper; these are convertible if
+// the value types are convertible.
+template <typename A, typename B, typename... Any>
+struct IsConvertible<std::vector<A, Any...>, ArrayWrapper<B>>
+ : IsConvertible<Decay<A>, Decay<B>> {};
+template <typename A, typename B, typename... Any>
+struct IsConvertible<ArrayWrapper<A>, std::vector<B, Any...>>
+ : IsConvertible<Decay<A>, Decay<B>> {};
+template <typename A, typename B, typename... Any, std::size_t Size>
+struct IsConvertible<std::vector<A, Any...>, std::array<B, Size>>
+ : IsConvertible<Decay<A>, Decay<B>> {};
+template <typename A, typename B, typename... Any, std::size_t Size>
+struct IsConvertible<std::array<A, Size>, std::vector<B, Any...>>
+ : IsConvertible<Decay<A>, Decay<B>> {};
+template <typename A, typename B, std::size_t Size>
+struct IsConvertible<ArrayWrapper<A>, std::array<B, Size>>
+ : IsConvertible<Decay<A>, Decay<B>> {};
+template <typename A, typename B, std::size_t Size>
+struct IsConvertible<std::array<A, Size>, ArrayWrapper<B>>
+ : IsConvertible<Decay<A>, Decay<B>> {};
+
+// Compares std::map and std::unordered_map; these are convertible if the keys
+// are convertible and the values are convertible.
+template <typename KeyA, typename ValueA, typename KeyB, typename ValueB,
+ typename... AnyA, typename... AnyB>
+struct IsConvertible<std::map<KeyA, ValueA, AnyA...>,
+ std::unordered_map<KeyB, ValueB, AnyB...>>
+ : And<IsConvertible<Decay<KeyA>, Decay<KeyB>>,
+ IsConvertible<Decay<ValueA>, Decay<ValueB>>> {};
+template <typename KeyA, typename ValueA, typename KeyB, typename ValueB,
+ typename... AnyA, typename... AnyB>
+struct IsConvertible<std::unordered_map<KeyA, ValueA, AnyA...>,
+ std::map<KeyB, ValueB, AnyB...>>
+ : And<IsConvertible<Decay<KeyA>, Decay<KeyB>>,
+ IsConvertible<Decay<ValueA>, Decay<ValueB>>> {};
+
+// Compares BufferWrapper<A*> and BufferWrapper<std::vector<B>>; these are
+// convertible if A and B are equivalent. Allocator types are not relevant to
+// convertibility.
+template <typename A, typename B, typename Allocator>
+struct IsConvertible<BufferWrapper<A*>,
+ BufferWrapper<std::vector<B, Allocator>>>
+ : IsEquivalent<A, B> {};
+template <typename A, typename B, typename Allocator>
+struct IsConvertible<BufferWrapper<std::vector<A, Allocator>>,
+ BufferWrapper<B*>> : IsEquivalent<A, B> {};
+template <typename A, typename B, typename AllocatorA, typename AllocatorB>
+struct IsConvertible<BufferWrapper<std::vector<A, AllocatorA>>,
+ BufferWrapper<std::vector<B, AllocatorB>>>
+ : IsEquivalent<A, B> {};
+template <typename A, typename B>
+struct IsConvertible<BufferWrapper<A*>, BufferWrapper<B*>>
+ : IsEquivalent<A, B> {};
+
+// Compares std::basic_string<A, ...> and StringWrapper<B>; these are
+// convertible if A and B are equivalent.
+template <typename A, typename B, typename... Any>
+struct IsConvertible<std::basic_string<A, Any...>, StringWrapper<B>>
+ : IsEquivalent<A, B> {};
+template <typename A, typename B, typename... Any>
+struct IsConvertible<StringWrapper<A>, std::basic_string<B, Any...>>
+ : IsEquivalent<A, B> {};
+
+// Compares PointerWrapper<A> and B; these are convertible if A and B are
+// convertible.
+template <typename A, typename B>
+struct IsConvertible<PointerWrapper<A>, B> : IsConvertible<Decay<A>, Decay<B>> {
+};
+template <typename A, typename B>
+struct IsConvertible<A, PointerWrapper<B>> : IsConvertible<Decay<A>, Decay<B>> {
+};
+
+// LocalHandle is convertible to RemoteHandle on the service side. This means
+// that a RemoteHandle may be supplied by a service when the protocol calls for
+// a LocalHandle return value. The other way around is not safe and can leak
+// file descriptors. The ServicePayload class enforces this policy by only
+// supporting RemoteHandle for pushed handles.
+template <>
+struct IsConvertible<LocalHandle, RemoteHandle> : std::true_type {};
+template <>
+struct IsConvertible<LocalHandle, BorrowedHandle> : std::true_type {};
+
+template <>
+struct IsConvertible<LocalChannelHandle, RemoteChannelHandle> : std::true_type {
+};
+template <>
+struct IsConvertible<LocalChannelHandle, BorrowedChannelHandle>
+ : std::true_type {};
+
+// Conditionally "rewrites" type A as type B, including cv-reference qualifiers,
+// iff A is convertible to B.
+template <typename A, typename B>
+using ConditionalRewrite =
+ typename std::conditional<IsConvertible<Decay<A>, Decay<B>>::value,
+ CopyCVReferenceType<A, B>, A>::type;
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_TYPE_OPERATORS_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/variant.h b/libs/vr/libpdx/private/pdx/rpc/variant.h
new file mode 100644
index 0000000..09789e5
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/variant.h
@@ -0,0 +1,693 @@
+#ifndef ANDROID_PDX_RPC_VARIANT_H_
+#define ANDROID_PDX_RPC_VARIANT_H_
+
+#include <cstdint>
+#include <tuple>
+#include <type_traits>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Type tag denoting an empty variant.
+struct EmptyVariant {};
+
+namespace detail {
+
+// Type for matching tagged overloads.
+template <typename T>
+struct TypeTag {};
+
+// Determines the type of the I-th element of Types....
+template <std::size_t I, typename... Types>
+using TypeForIndex = std::tuple_element_t<I, std::tuple<Types...>>;
+
+// Determines the type tag for the I-th element of Types....
+template <std::size_t I, typename... Types>
+using TypeTagForIndex = TypeTag<TypeForIndex<I, Types...>>;
+
+// Enable if T(Args...) is well formed.
+template <typename R, typename T, typename... Args>
+using EnableIfConstructible =
+ typename std::enable_if<std::is_constructible<T, Args...>::value, R>::type;
+// Enable if T(Args...) is not well formed.
+template <typename R, typename T, typename... Args>
+using EnableIfNotConstructible =
+ typename std::enable_if<!std::is_constructible<T, Args...>::value, R>::type;
+
+// Determines whether T is an element of Types...;
+template <typename... Types>
+struct HasType : std::false_type {};
+template <typename T, typename U>
+struct HasType<T, U> : std::is_same<T, U> {};
+template <typename T, typename First, typename... Rest>
+struct HasType<T, First, Rest...>
+ : std::integral_constant<
+ bool, std::is_same<T, First>::value || HasType<T, Rest...>::value> {};
+
+template <typename T, typename... Types>
+using HasTypeIgnoreRef =
+ HasType<typename std::remove_reference<T>::type, Types...>;
+
+// Defines set operations on a set of Types...
+template <typename... Types>
+struct Set {
+ // Default specialization catches the empty set, which is always a subset.
+ template <typename...>
+ struct IsSubset : std::true_type {};
+ template <typename T>
+ struct IsSubset<T> : HasType<T, Types...> {};
+ template <typename First, typename... Rest>
+ struct IsSubset<First, Rest...>
+ : std::integral_constant<
+ bool, IsSubset<First>::value && IsSubset<Rest...>::value> {};
+};
+
+// Determines the number of elements of Types... that are constructible from
+// From.
+template <typename... Types>
+struct ConstructibleCount;
+template <typename From, typename To>
+struct ConstructibleCount<From, To>
+ : std::integral_constant<std::size_t,
+ std::is_constructible<To, From>::value> {};
+template <typename From, typename First, typename... Rest>
+struct ConstructibleCount<From, First, Rest...>
+ : std::integral_constant<std::size_t,
+ std::is_constructible<First, From>::value +
+ ConstructibleCount<From, Rest...>::value> {};
+
+// Enable if T is an element of Types...
+template <typename R, typename T, typename... Types>
+using EnableIfElement =
+ typename std::enable_if<HasTypeIgnoreRef<T, Types...>::value, R>::type;
+// Enable if T is not an element of Types...
+template <typename R, typename T, typename... Types>
+using EnableIfNotElement =
+ typename std::enable_if<!HasTypeIgnoreRef<T, Types...>::value, R>::type;
+
+// Enable if T is convertible to an element of Types... T is considered
+// convertible IIF a single element of Types... is assignable from T and T is
+// not a direct element of Types...
+template <typename R, typename T, typename... Types>
+using EnableIfConvertible =
+ typename std::enable_if<!HasTypeIgnoreRef<T, Types...>::value &&
+ ConstructibleCount<T, Types...>::value == 1,
+ R>::type;
+
+// Enable if T is assignable to an element of Types... T is considered
+// assignable IFF a single element of Types... is constructible from T or T is a
+// direct element of Types.... Note that T is REQUIRED to be an element of
+// Types... when multiple elements are constructible from T to prevent ambiguity
+// in conversion.
+template <typename R, typename T, typename... Types>
+using EnableIfAssignable =
+ typename std::enable_if<HasTypeIgnoreRef<T, Types...>::value ||
+ ConstructibleCount<T, Types...>::value == 1,
+ R>::type;
+
+// Selects a type for SFINAE constructor selection.
+template <bool CondA, typename SelectA, typename SelectB>
+using Select = std::conditional_t<CondA, SelectA, SelectB>;
+
+// Recursive union type.
+template <typename... Types>
+union Union;
+
+// Specialization handling a singular type, terminating template recursion.
+template <typename Type>
+union Union<Type> {
+ Union() {}
+ ~Union() {}
+
+ template <typename T>
+ Union(std::int32_t index, std::int32_t* index_out, TypeTag<Type>, T&& value)
+ : first_(std::forward<T>(value)) {
+ *index_out = index;
+ }
+ template <typename T, typename = EnableIfAssignable<void, T, Type>>
+ Union(std::int32_t index, std::int32_t* index_out, T&& value)
+ : first_(std::forward<T>(value)) {
+ *index_out = index;
+ }
+
+ Type& get(TypeTag<Type>) { return first_; }
+ const Type& get(TypeTag<Type>) const { return first_; }
+ EmptyVariant get(TypeTag<EmptyVariant>) const { return {}; }
+ constexpr std::int32_t index(TypeTag<Type>) const { return 0; }
+
+ template <typename... Args>
+ std::int32_t Construct(TypeTag<Type>, Args&&... args) {
+ new (&first_) Type(std::forward<Args>(args)...);
+ return 0;
+ }
+ template <typename... Args>
+ EnableIfConstructible<std::int32_t, Type, Args...> Construct(Args&&... args) {
+ new (&first_) Type(std::forward<Args>(args)...);
+ return 0;
+ }
+
+ void Destruct(std::int32_t target_index) {
+ if (target_index == index(TypeTag<Type>{})) {
+ (&get(TypeTag<Type>{}))->~Type();
+ }
+ }
+
+ template <typename T>
+ bool Assign(TypeTag<Type>, std::int32_t target_index, T&& value) {
+ if (target_index == 0) {
+ first_ = std::forward<T>(value);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ template <typename T>
+ EnableIfConstructible<bool, Type, T> Assign(std::int32_t target_index,
+ T&& value) {
+ if (target_index == 0) {
+ first_ = std::forward<T>(value);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ template <typename T>
+ EnableIfNotConstructible<bool, Type, T> Assign(std::int32_t /*target_index*/,
+ T&& /*value*/) {
+ return false;
+ }
+
+ template <typename Op>
+ decltype(auto) Visit(std::int32_t target_index, Op&& op) {
+ if (target_index == index(TypeTag<Type>{}))
+ return std::forward<Op>(op)(get(TypeTag<Type>{}));
+ else
+ return std::forward<Op>(op)(get(TypeTag<EmptyVariant>{}));
+ }
+ template <typename Op>
+ decltype(auto) Visit(std::int32_t target_index, Op&& op) const {
+ if (target_index == index(TypeTag<Type>{}))
+ return std::forward<Op>(op)(get(TypeTag<Type>{}));
+ else
+ return std::forward<Op>(op)(get(TypeTag<EmptyVariant>{}));
+ }
+
+ template <typename... Args>
+ bool Become(std::int32_t target_index, Args&&... args) {
+ if (target_index == index(TypeTag<Type>{})) {
+ Construct(TypeTag<Type>{}, std::forward<Args>(args)...);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private:
+ Type first_;
+};
+
+// Specialization that recursively unions types from the paramater pack.
+template <typename First, typename... Rest>
+union Union<First, Rest...> {
+ Union() {}
+ ~Union() {}
+
+ template <typename T>
+ Union(std::int32_t index, std::int32_t* index_out, TypeTag<First>, T&& value)
+ : first_(std::forward<T>(value)) {
+ *index_out = index;
+ }
+ template <typename T, typename U>
+ Union(std::int32_t index, std::int32_t* index_out, TypeTag<T>, U&& value)
+ : rest_(index + 1, index_out, TypeTag<T>{}, std::forward<U>(value)) {}
+
+ struct FirstType {};
+ struct RestType {};
+ template <typename T>
+ using SelectConstructor =
+ Select<ConstructibleCount<T, First>::value == 1, FirstType, RestType>;
+
+ template <typename T>
+ Union(std::int32_t index, std::int32_t* index_out, T&& value)
+ : Union(index, index_out, std::forward<T>(value),
+ SelectConstructor<T>{}) {}
+
+ template <typename T>
+ Union(std::int32_t index, std::int32_t* index_out, T&& value, FirstType)
+ : first_(std::forward<T>(value)) {
+ *index_out = index;
+ }
+ template <typename T>
+ Union(std::int32_t index, std::int32_t* index_out, T&& value, RestType)
+ : rest_(index + 1, index_out, std::forward<T>(value)) {}
+
+ First& get(TypeTag<First>) { return first_; }
+ const First& get(TypeTag<First>) const { return first_; }
+ constexpr std::int32_t index(TypeTag<First>) const { return 0; }
+
+ template <typename T>
+ T& get(TypeTag<T>) {
+ return rest_.template get(TypeTag<T>{});
+ }
+ template <typename T>
+ const T& get(TypeTag<T>) const {
+ return rest_.template get(TypeTag<T>{});
+ }
+ template <typename T>
+ constexpr std::int32_t index(TypeTag<T>) const {
+ return 1 + rest_.template index(TypeTag<T>{});
+ }
+
+ template <typename... Args>
+ std::int32_t Construct(TypeTag<First>, Args&&... args) {
+ new (&first_) First(std::forward<Args>(args)...);
+ return 0;
+ }
+ template <typename T, typename... Args>
+ std::int32_t Construct(TypeTag<T>, Args&&... args) {
+ return 1 +
+ rest_.template Construct(TypeTag<T>{}, std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ EnableIfConstructible<std::int32_t, First, Args...> Construct(
+ Args&&... args) {
+ new (&first_) First(std::forward<Args>(args)...);
+ return 0;
+ }
+ template <typename... Args>
+ EnableIfNotConstructible<std::int32_t, First, Args...> Construct(
+ Args&&... args) {
+ return 1 + rest_.template Construct(std::forward<Args>(args)...);
+ }
+
+ void Destruct(std::int32_t target_index) {
+ if (target_index == index(TypeTag<First>{})) {
+ (get(TypeTag<First>{})).~First();
+ } else {
+ rest_.Destruct(target_index - 1);
+ }
+ }
+
+ template <typename T>
+ bool Assign(TypeTag<First>, std::int32_t target_index, T&& value) {
+ if (target_index == 0) {
+ first_ = std::forward<T>(value);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ template <typename T, typename U>
+ bool Assign(TypeTag<T>, std::int32_t target_index, U&& value) {
+ return rest_.Assign(TypeTag<T>{}, target_index - 1, std::forward<U>(value));
+ }
+ template <typename T>
+ EnableIfConstructible<bool, First, T> Assign(std::int32_t target_index,
+ T&& value) {
+ if (target_index == 0) {
+ first_ = std::forward<T>(value);
+ return true;
+ } else {
+ return rest_.Assign(target_index - 1, std::forward<T>(value));
+ }
+ }
+ template <typename T>
+ EnableIfNotConstructible<bool, First, T> Assign(std::int32_t target_index,
+ T&& value) {
+ return rest_.Assign(target_index - 1, std::forward<T>(value));
+ }
+
+ // Recursively traverses the union and calls Op on the active value when the
+ // active type is found. If the union is empty Op is called on EmptyVariant.
+ // TODO(eieio): This could be refactored into an array or jump table. It's
+ // unclear whether this would be more efficient for practical variant arity.
+ template <typename Op>
+ decltype(auto) Visit(std::int32_t target_index, Op&& op) {
+ if (target_index == index(TypeTag<First>{}))
+ return std::forward<Op>(op)(get(TypeTag<First>{}));
+ else
+ return rest_.Visit(target_index - 1, std::forward<Op>(op));
+ }
+ template <typename Op>
+ decltype(auto) Visit(std::int32_t target_index, Op&& op) const {
+ if (target_index == index(TypeTag<First>{}))
+ return std::forward<Op>(op)(get(TypeTag<First>{}));
+ else
+ return rest_.Visit(target_index - 1, std::forward<Op>(op));
+ }
+
+ template <typename... Args>
+ bool Become(std::int32_t target_index, Args&&... args) {
+ if (target_index == index(TypeTag<First>{})) {
+ Construct(TypeTag<First>{}, std::forward<Args>(args)...);
+ return true;
+ } else {
+ return rest_.Become(target_index - 1, std::forward<Args>(args)...);
+ }
+ }
+
+ private:
+ First first_;
+ Union<Rest...> rest_;
+};
+
+} // namespace detail
+
+template <typename... Types>
+class Variant {
+ private:
+ // Convenience types.
+ template <typename T>
+ using TypeTag = detail::TypeTag<T>;
+ template <typename T>
+ using TypeTagIgnoreRef = TypeTag<typename std::remove_reference<T>::type>;
+ template <std::size_t I>
+ using TypeForIndex = detail::TypeForIndex<I, Types...>;
+ template <std::size_t I>
+ using TypeTagForIndex = detail::TypeTagForIndex<I, Types...>;
+ template <typename T>
+ using HasType = detail::HasType<T, Types...>;
+ template <typename T>
+ using HasTypeIgnoreRef = detail::HasTypeIgnoreRef<T, Types...>;
+ template <typename R, typename T>
+ using EnableIfElement = detail::EnableIfElement<R, T, Types...>;
+ template <typename R, typename T>
+ using EnableIfConvertible = detail::EnableIfConvertible<R, T, Types...>;
+ template <typename R, typename T>
+ using EnableIfAssignable = detail::EnableIfAssignable<R, T, Types...>;
+
+ struct Direct {};
+ struct Convert {};
+ template <typename T>
+ using SelectConstructor =
+ detail::Select<HasTypeIgnoreRef<T>::value, Direct, Convert>;
+
+ // Constructs by type tag when T is an direct element of Types...
+ template <typename T>
+ explicit Variant(T&& value, Direct)
+ : value_(0, &index_, TypeTagIgnoreRef<T>{}, std::forward<T>(value)) {}
+ // Conversion constructor when T is not a direct element of Types...
+ template <typename T>
+ explicit Variant(T&& value, Convert)
+ : value_(0, &index_, std::forward<T>(value)) {}
+
+ public:
+ // Variants are default construcible, regardless of whether the elements are
+ // default constructible. Default consruction yields an empty Variant.
+ Variant() {}
+ explicit Variant(EmptyVariant) {}
+ ~Variant() { Destruct(); }
+
+ // Copy and move construction from Variant types. Each element of OtherTypes
+ // must be convertible to an element of Types.
+ template <typename... OtherTypes>
+ explicit Variant(const Variant<OtherTypes...>& other) {
+ other.Visit([this](const auto& value) { Construct(value); });
+ }
+ template <typename... OtherTypes>
+ explicit Variant(Variant<OtherTypes...>&& other) {
+ other.Visit([this](auto&& value) { Construct(std::move(value)); });
+ }
+
+ // Construction from non-Variant types.
+ template <typename T, typename = EnableIfAssignable<void, T>>
+ explicit Variant(T&& value)
+ : Variant(std::forward<T>(value), SelectConstructor<T>{}) {}
+
+ // Performs assignment from type T belonging to Types. This overload takes
+ // priority to prevent implicit conversion in cases where T is implicitly
+ // convertible to multiple elements of Types.
+ template <typename T>
+ EnableIfElement<Variant&, T> operator=(T&& value) {
+ Assign(TypeTagIgnoreRef<T>{}, std::forward<T>(value));
+ return *this;
+ }
+
+ // Performs assignment from type T not belonging to Types. This overload
+ // matches in cases where conversion is the only viable option.
+ template <typename T>
+ EnableIfConvertible<Variant&, T> operator=(T&& value) {
+ Assign(std::forward<T>(value));
+ return *this;
+ }
+
+ // Handles assignment from the empty type. This overload supports assignment
+ // in visitors using generic lambdas.
+ Variant& operator=(EmptyVariant) {
+ Assign(EmptyVariant{});
+ return *this;
+ }
+
+ // Assignment from Variant types. Each element of OtherTypes must be
+ // convertible to an element of Types. Forwards through non-Variant assignment
+ // operators to apply conversion checks.
+ template <typename... OtherTypes>
+ Variant& operator=(const Variant<OtherTypes...>& other) {
+ other.Visit([this](const auto& value) { *this = value; });
+ return *this;
+ }
+ template <typename... OtherTypes>
+ Variant& operator=(Variant<OtherTypes...>&& other) {
+ other.Visit([this](auto&& value) { *this = std::move(value); });
+ return *this;
+ }
+
+ // Becomes the target type, constructing a new element from the given
+ // arguments if necessary. No action is taken if the active element is already
+ // the target type. Otherwise the active element is destroyed and replaced by
+ // constructing an element of the new type using |Args|. An invalid target
+ // type index results in an empty Variant.
+ template <typename... Args>
+ void Become(std::int32_t target_index, Args&&... args) {
+ if (target_index != index()) {
+ Destruct();
+ index_ = value_.Become(target_index, std::forward<Args>(args)...)
+ ? target_index
+ : kEmptyIndex;
+ }
+ }
+
+ // Invokes |Op| on the active element. If the Variant is empty |Op| is invoked
+ // on EmptyVariant.
+ template <typename Op>
+ decltype(auto) Visit(Op&& op) {
+ return value_.Visit(index_, std::forward<Op>(op));
+ }
+ template <typename Op>
+ decltype(auto) Visit(Op&& op) const {
+ return value_.Visit(index_, std::forward<Op>(op));
+ }
+
+ // Index returned when the Variant is empty.
+ enum : std::int32_t { kEmptyIndex = -1 };
+
+ // Returns the index of the given type.
+ template <typename T>
+ constexpr std::int32_t index_of() const {
+ static_assert(HasType<T>::value, "T is not an element type of Variant.");
+ return value_.template index(TypeTag<T>{});
+ }
+
+ // Returns the index of the active type. If the Variant is empty -1 is
+ // returned.
+ std::int32_t index() const { return index_; }
+
+ // Returns true if the given type is active, false otherwise.
+ template <typename T>
+ bool is() const {
+ static_assert(HasType<T>::value, "T is not an element type of Variant.");
+ return index() == index_of<T>();
+ }
+
+ // Returns true if the Variant is empty, false otherwise.
+ bool empty() const { return index() == kEmptyIndex; }
+
+ // Element accessors. Returns a pointer to the active value if the given
+ // type/index is active, otherwise nullptr is returned.
+ template <typename T>
+ T* get() {
+ if (is<T>())
+ return &value_.template get(TypeTag<T>{});
+ else
+ return nullptr;
+ }
+ template <typename T>
+ const T* get() const {
+ if (is<T>())
+ return &value_.template get(TypeTag<T>{});
+ else
+ return nullptr;
+ }
+ template <std::size_t I>
+ TypeForIndex<I>* get() {
+ if (is<TypeForIndex<I>>())
+ return &value_.template get(TypeTagForIndex<I>{});
+ else
+ return nullptr;
+ }
+ template <std::size_t I>
+ const TypeForIndex<I>* get() const {
+ if (is<TypeForIndex<I>>())
+ return &value_.template get(TypeTagForIndex<I>{});
+ else
+ return nullptr;
+ }
+
+ private:
+ std::int32_t index_ = kEmptyIndex;
+ detail::Union<Types...> value_;
+
+ // Constructs an element from the given arguments and sets the Variant to the
+ // resulting type.
+ template <typename... Args>
+ void Construct(Args&&... args) {
+ index_ = value_.template Construct(std::forward<Args>(args)...);
+ }
+ void Construct(EmptyVariant) {}
+
+ // Destroys the active element of the Variant.
+ void Destruct() { value_.Destruct(index_); }
+
+ // Assigns the Variant when non-empty and the current type matches the target
+ // type, otherwise destroys the current value and constructs a element of the
+ // new type. Tagged assignment is used when T is an element of the Variant to
+ // prevent implicit conversion in cases where T is implicitly convertible to
+ // multiple element types.
+ template <typename T, typename U>
+ void Assign(TypeTag<T>, U&& value) {
+ if (!value_.template Assign(TypeTag<T>{}, index_, std::forward<U>(value))) {
+ Destruct();
+ Construct(TypeTag<T>{}, std::forward<U>(value));
+ }
+ }
+ template <typename T>
+ void Assign(T&& value) {
+ if (!value_.template Assign(index_, std::forward<T>(value))) {
+ Destruct();
+ Construct(std::forward<T>(value));
+ }
+ }
+ // Handles assignment from an empty Variant.
+ void Assign(EmptyVariant) { Destruct(); }
+};
+
+// Utility type to extract/convert values from a variant. This class simplifies
+// conditional logic to get/move/swap/action values from a variant when one or
+// more elements are compatible with the destination type.
+//
+// Example:
+// Variant<int, bool, std::string> v(10);
+// bool bool_value;
+// if (IfAnyOf<int, bool>::Get(v, &bool_value)) {
+// DoSomething(bool_value);
+// } else {
+// HandleInvalidType();
+// }
+// IfAnyOf<int>::Call(v, [](const auto& value) { DoSomething(value); });
+//
+template <typename... ValidTypes>
+struct IfAnyOf {
+ // Calls Op on the underlying value of the variant and returns true when the
+ // variant is a valid type, otherwise does nothing and returns false.
+ template <typename Op, typename... Types>
+ static bool Call(Variant<Types...>* variant, Op&& op) {
+ static_assert(
+ detail::Set<Types...>::template IsSubset<ValidTypes...>::value,
+ "ValidTypes may only contain element types from the Variant.");
+ return variant->Visit(CallOp<Op>{std::forward<Op>(op)});
+ }
+ template <typename Op, typename... Types>
+ static bool Call(const Variant<Types...>* variant, Op&& op) {
+ static_assert(
+ detail::Set<Types...>::template IsSubset<ValidTypes...>::value,
+ "ValidTypes may only contain element types from the Variant.");
+ return variant->Visit(CallOp<Op>{std::forward<Op>(op)});
+ }
+
+ // Gets/converts the underlying value of the variant to type T and returns
+ // true when the variant is a valid type, otherwise does nothing and returns
+ // false.
+ template <typename T, typename... Types>
+ static bool Get(const Variant<Types...>* variant, T* value_out) {
+ return Call(variant,
+ [value_out](const auto& value) { *value_out = value; });
+ }
+
+ // Moves the underlying value of the variant and returns true when the variant
+ // is a valid type, otherwise does nothing and returns false.
+ template <typename T, typename... Types>
+ static bool Take(Variant<Types...>* variant, T* value_out) {
+ return Call(variant,
+ [value_out](auto&& value) { *value_out = std::move(value); });
+ }
+
+ // Swaps the underlying value of the variant with |*value_out| and returns
+ // true when the variant is a valid type, otherwise does nothing and returns
+ // false.
+ template <typename T, typename... Types>
+ static bool Swap(Variant<Types...>* variant, T* value_out) {
+ return Call(variant,
+ [value_out](auto&& value) { std::swap(*value_out, value); });
+ }
+
+ private:
+ template <typename Op>
+ struct CallOp {
+ Op&& op;
+ template <typename U>
+ detail::EnableIfNotElement<bool, U, ValidTypes...> operator()(U&&) {
+ return false;
+ }
+ template <typename U>
+ detail::EnableIfElement<bool, U, ValidTypes...> operator()(const U& value) {
+ std::forward<Op>(op)(value);
+ return true;
+ }
+ template <typename U>
+ detail::EnableIfElement<bool, U, ValidTypes...> operator()(U&& value) {
+ std::forward<Op>(op)(std::forward<U>(value));
+ return true;
+ }
+ };
+};
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+// Overloads of std::get<T> and std::get<I> for android::pdx::rpc::Variant.
+namespace std {
+
+template <typename T, typename... Types>
+inline T& get(::android::pdx::rpc::Variant<Types...>& v) {
+ return *v.template get<T>();
+}
+template <typename T, typename... Types>
+inline T&& get(::android::pdx::rpc::Variant<Types...>&& v) {
+ return std::move(*v.template get<T>());
+}
+template <typename T, typename... Types>
+inline const T& get(const ::android::pdx::rpc::Variant<Types...>& v) {
+ return *v.template get<T>();
+}
+template <std::size_t I, typename... Types>
+inline ::android::pdx::rpc::detail::TypeForIndex<I, Types...>& get(
+ ::android::pdx::rpc::Variant<Types...>& v) {
+ return *v.template get<I>();
+}
+template <std::size_t I, typename... Types>
+inline ::android::pdx::rpc::detail::TypeForIndex<I, Types...>&& get(
+ ::android::pdx::rpc::Variant<Types...>&& v) {
+ return std::move(*v.template get<I>());
+}
+template <std::size_t I, typename... Types>
+inline const ::android::pdx::rpc::detail::TypeForIndex<I, Types...>& get(
+ const ::android::pdx::rpc::Variant<Types...>& v) {
+ return *v.template get<I>();
+}
+
+} // namespace std
+
+#endif // ANDROID_PDX_RPC_VARIANT_H_
diff --git a/libs/vr/libpdx/private/pdx/service.h b/libs/vr/libpdx/private/pdx/service.h
new file mode 100644
index 0000000..029e6bf
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/service.h
@@ -0,0 +1,703 @@
+#ifndef ANDROID_PDX_SERVICE_H_
+#define ANDROID_PDX_SERVICE_H_
+
+#include <cutils/log.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "pdx/channel_handle.h"
+#include "pdx/file_handle.h"
+#include "pdx/message_reader.h"
+#include "pdx/message_writer.h"
+#include "pdx/service_endpoint.h"
+
+namespace android {
+namespace pdx {
+
+class Service;
+
+namespace opcodes {
+
+/*
+ * Reserved message opcodes used by libpdx. The reserved opcodes start at the
+ * max positive signed integer for the system and go down.
+ * In contrast, service opcodes start at zero and go up. This scheme leaves
+ * most of the positive integer space for services, a tiny fraction of the
+ * positive integer space for the framework, and the entire negative integer
+ * space for the kernel.
+ */
+#define PDX_OPCODE(name, n) name = ((-1U >> 1) - (n)) // 0x7fff..ffff - n
+
+enum {
+ // System message sent when a new client channel is open.
+ CHANNEL_OPEN = -1,
+ // System message sent when a channel is closed.
+ CHANNEL_CLOSE = -2,
+ // Request the service to reload system properties.
+ PDX_OPCODE(REPORT_SYSPROP_CHANGE, 0),
+ // Request the service to dump state and return it in a text buffer.
+ PDX_OPCODE(DUMP_STATE, 1),
+};
+
+} // namespace opcodes
+
+/*
+ * Base class of service-side per-channel context classes.
+ */
+class Channel : public std::enable_shared_from_this<Channel> {
+ public:
+ Channel() {}
+ virtual ~Channel() {}
+
+ /*
+ * Utility to get a shared_ptr reference from the channel context pointer.
+ */
+ static std::shared_ptr<Channel> GetFromMessageInfo(const MessageInfo& info);
+};
+
+/*
+ * Message class represents an RPC message, and implicitly the blocked sender
+ * waiting for a response. Every message should get a reply, at some point
+ * (unless the endpoint is closed), to prevent clients from blocking
+ * indefinitely. In order to enforce this and prevent leaking message ids,
+ * Message automatically replies with an error to the client on destruction,
+ * unless one of two things happens:
+ *
+ * 1. The service calls one of the reply methods before the Message is
+ * destroyed.
+ * 2. The responsibility for the message is moved to another instance of
+ * Message, using either move construction or move assignment.
+ *
+ * The second case is useful for services that need to delay responding to a
+ * sender until a later time. In this situation the service can move the
+ * Message to another instance in a suitable data structure for later use. The
+ * moved-to Message then takes on the same behavior and responsibilities
+ * described above.
+ */
+class Message : public OutputResourceMapper, public InputResourceMapper {
+ public:
+ Message();
+ Message(const MessageInfo& info);
+ ~Message();
+
+ /*
+ * Message objects support move construction and assignment.
+ */
+ Message(Message&& other);
+ Message& operator=(Message&& other);
+
+ /*
+ * Read/write payload, in either single buffer or iovec form.
+ */
+ ssize_t ReadVector(const iovec* vector, size_t vector_length);
+ ssize_t Read(void* buffer, size_t length);
+ ssize_t WriteVector(const iovec* vector, size_t vector_length);
+ ssize_t Write(const void* buffer, size_t length);
+
+ template <size_t N>
+ inline ssize_t ReadVector(const iovec (&vector)[N]) {
+ return ReadVector(vector, N);
+ }
+
+ template <size_t N>
+ inline ssize_t WriteVector(const iovec (&vector)[N]) {
+ return WriteVector(vector, N);
+ }
+
+ // OutputResourceMapper
+ FileReference PushFileHandle(const LocalHandle& handle) override;
+ FileReference PushFileHandle(const BorrowedHandle& handle) override;
+ FileReference PushFileHandle(const RemoteHandle& handle) override;
+ ChannelReference PushChannelHandle(const LocalChannelHandle& handle) override;
+ ChannelReference PushChannelHandle(
+ const BorrowedChannelHandle& handle) override;
+ ChannelReference PushChannelHandle(
+ const RemoteChannelHandle& handle) override;
+
+ // InputResourceMapper
+ bool GetFileHandle(FileReference ref, LocalHandle* handle) override;
+ bool GetChannelHandle(ChannelReference ref,
+ LocalChannelHandle* handle) override;
+
+ /*
+ * Various ways to reply to a message.
+ */
+ int Reply(int return_code);
+ int ReplyError(unsigned error);
+ int ReplyFileDescriptor(unsigned int fd);
+ int Reply(const LocalHandle& handle);
+ int Reply(const BorrowedHandle& handle);
+ int Reply(const RemoteHandle& handle);
+ int Reply(const LocalChannelHandle& handle);
+ int Reply(const BorrowedChannelHandle& handle);
+ int Reply(const RemoteChannelHandle& handle);
+
+ template <typename T>
+ inline int Reply(const Status<T>& status) {
+ return status ? Reply(status.get()) : ReplyError(status.error());
+ }
+
+ /*
+ * Update the channel event bits with the given clear and set masks.
+ */
+ int ModifyChannelEvents(int clear_mask, int set_mask);
+
+ /*
+ * Create a new channel and push it as a file descriptor to the client. See
+ * Service::PushChannel() for a detail description of this method's operation.
+ */
+ Status<RemoteChannelHandle> PushChannel(
+ int flags, const std::shared_ptr<Channel>& channel, int* channel_id);
+
+ /*
+ * Create a new channel and push it as a file descriptor to the client. See
+ * Service::PushChannel() for a detail description of this method's operation.
+ */
+ Status<RemoteChannelHandle> PushChannel(
+ Service* service, int flags, const std::shared_ptr<Channel>& channel,
+ int* channel_id);
+
+ /*
+ * Check whether the |ref| is a reference to channel to this service.
+ * If the channel reference in question is valid, the Channel object is
+ * returned in |channel| when non-nullptr.
+ *
+ * Return values:
+ * channel_id - id of the channel if the |ref| is a valid reference to
+ * this service's channel.
+ * Errors:
+ * EOPNOTSUPP - the file descriptor is not a channel or is a channel to
+ * another service.
+ * EBADF - the file descriptor is invalid.
+ * FAULT - |channel_id| or |channel| are non-nullptr and point to invalid
+ * memory addresses.
+ * EINVAL - the value of |ref| is invalid or the message id for this
+ * message is no longer valid.
+ */
+ Status<int> CheckChannel(ChannelReference ref,
+ std::shared_ptr<Channel>* channel) const;
+
+ /*
+ * Overload of CheckChannel() that checks whether the channel reference is for
+ * a channel to the service |service|.
+ */
+ Status<int> CheckChannel(const Service* service, ChannelReference ref,
+ std::shared_ptr<Channel>* channel) const;
+
+ /*
+ * Overload of CheckChannel() that automatically converts to shared pointers
+ * to types derived from Channel.
+ */
+ template <class C>
+ Status<int> CheckChannel(ChannelReference ref,
+ std::shared_ptr<C>* channel) const {
+ std::shared_ptr<Channel> base_pointer;
+ const Status<int> ret =
+ CheckChannel(ref, channel ? &base_pointer : nullptr);
+ if (channel)
+ *channel = std::static_pointer_cast<C>(base_pointer);
+ return ret;
+ }
+
+ template <class C>
+ Status<int> CheckChannel(const Service* service, ChannelReference ref,
+ std::shared_ptr<C>* channel) const {
+ std::shared_ptr<Channel> base_pointer;
+ const Status<int> ret =
+ CheckChannel(service, ref, channel ? &base_pointer : nullptr);
+ if (channel)
+ *channel = std::static_pointer_cast<C>(base_pointer);
+ return ret;
+ }
+
+ /*
+ * MessageInfo accessors.
+ */
+ pid_t GetProcessId() const;
+ pid_t GetThreadId() const;
+ uid_t GetEffectiveUserId() const;
+ gid_t GetEffectiveGroupId() const;
+ int GetChannelId() const;
+ int GetMessageId() const;
+ int GetOp() const;
+ int GetFlags() const;
+ size_t GetSendLength() const;
+ size_t GetReceiveLength() const;
+ size_t GetFileDescriptorCount() const;
+
+ /*
+ * Impulses are asynchronous and cannot be replied to. All impulses have this
+ * invalid message id.
+ */
+ enum { IMPULSE_MESSAGE_ID = -1 };
+
+ /*
+ * Returns true if this Message describes an asynchronous "impulse" message.
+ */
+ bool IsImpulse() const { return GetMessageId() == IMPULSE_MESSAGE_ID; }
+
+ /*
+ * Returns a pointer to the impulse payload. Impulses are a maximum of 32
+ * bytes in size and the start of the impulse payload is guaranteed to be
+ * 8-byte aligned. Use GetSendLength() to determine the payload size.
+ */
+ const std::uint8_t* ImpulseBegin() const;
+
+ /*
+ * Returns one byte past the end of the impulse payload, as conventional for
+ * STL iterators.
+ */
+ const std::uint8_t* ImpulseEnd() const;
+
+ /*
+ * Get/set the Channel object for the channel associated
+ * with this message. It is up to the caller to synchronize
+ * these in multi-threaded services.
+ */
+ std::shared_ptr<Channel> GetChannel() const;
+ void SetChannel(const std::shared_ptr<Channel>& channnel);
+
+ /*
+ * Get the Channel object for the channel associated with this message,
+ * automatically converted to the desired subclass of Channel.
+ */
+ template <class C>
+ std::shared_ptr<C> GetChannel() const {
+ return std::static_pointer_cast<C>(GetChannel());
+ }
+
+ /*
+ * Gets the service this message was received on. Returns nullptr if the
+ * service was destroyed.
+ */
+ std::shared_ptr<Service> GetService() const;
+
+ /*
+ * Raw access to the MessageInfo for this message.
+ */
+ const MessageInfo& GetInfo() const;
+
+ bool replied() const { return replied_; }
+ bool IsChannelExpired() const { return channel_.expired(); }
+ bool IsServiceExpired() const { return service_.expired(); }
+
+ /*
+ * Returns true if the message is non-empty; that is the message can be
+ * replied to using this instance.
+ */
+ explicit operator bool() const { return !replied_; }
+
+ const void* GetState() const { return state_; }
+ void* GetState() { return state_; }
+
+ private:
+ friend class Service;
+
+ Message(const Message&) = delete;
+ void operator=(const Message&) = delete;
+ void Destroy();
+
+ std::weak_ptr<Service> service_;
+ std::weak_ptr<Channel> channel_;
+ MessageInfo info_;
+ void* state_{nullptr};
+ bool replied_;
+};
+
+// Base class for RPC services.
+class Service : public std::enable_shared_from_this<Service> {
+ public:
+ Service(const std::string& name, std::unique_ptr<Endpoint> endpoint);
+ virtual ~Service();
+
+ /*
+ * Utility to get a shared_ptr reference from the service context pointer.
+ */
+ static std::shared_ptr<Service> GetFromMessageInfo(const MessageInfo& info);
+
+ /*
+ * Returns whether initialization was successful. Subclasses that override
+ * this must call this base method and AND the results with their own. This
+ * method is not intended to do any initialization work itself, only to
+ * signal success or failure.
+ */
+ virtual bool IsInitialized() const;
+
+ /*
+ * Called by defaultHandleMessage in response to a CHANNEL_OPEN message.
+ * This gives subclasses of Service a convenient hook to create per-channel
+ * context in the form of a Channel subclass.
+ *
+ * The Channel instance returned by this is used to set the channel context
+ * pointer for the channel that was just opened.
+ */
+ virtual std::shared_ptr<Channel> OnChannelOpen(Message& message);
+
+ /*
+ * Called by defaultHandleMessage in response to a CHANNEL_CLOSE message.
+ * This give subclasses of Service a convenient hook to clean up per-channel
+ * context.
+ */
+ virtual void OnChannelClose(Message& message,
+ const std::shared_ptr<Channel>& channel);
+
+ /*
+ * Set the channel context for the given channel. This keeps a reference to
+ * the Channel object until the channel is closed or another call replaces
+ * the current value.
+ */
+ int SetChannel(int channel_id, const std::shared_ptr<Channel>& channel);
+
+ /*
+ * Get the channel context for the given channel id. This method should be
+ * used sparingly because of the performance characteristics of the underlying
+ * map; it is intended for limited, non-critical path access from outside of
+ * message dispatch. In most cases lookup by id should be unnecessary in a
+ * properly designed service; Message::GetChannel() should be used instead
+ * whenever an operation is in the context of a message.
+ *
+ * Again, if you lookup a channel context object for a service by id in a
+ * message handling path for the same service, you're probably doing something
+ * wrong.
+ */
+ std::shared_ptr<Channel> GetChannel(int channel_id) const;
+
+ /*
+ * Get a snapshot of the active channels for this service. This is the
+ * preferred way to access the set of channels because it avoids potential
+ * deadlocks and race conditions that may occur when operating on the channel
+ * map directly. However, it is more expensive than direct iteration because
+ * of dynamic memory allocation and shared pointer reference costs.
+ *
+ * Automatically converts returned items to shared pointers of the type
+ * std::shared_ptr<C>, where C is the subclass of Channel used by the service.
+ */
+ template <class C>
+ std::vector<std::shared_ptr<C>> GetChannels() const {
+ std::lock_guard<std::mutex> autolock(channels_mutex_);
+ std::vector<std::shared_ptr<C>> items;
+ items.reserve(channels_.size());
+
+ for (const auto& pair : channels_) {
+ items.push_back(std::static_pointer_cast<C>(pair.second));
+ }
+
+ return items;
+ }
+
+ /*
+ * Close a channel, signaling the client file object and freeing the channel
+ * id. Once closed, the client side of the channel always returns the error
+ * ESHUTDOWN and signals the poll/epoll events POLLHUP and POLLFREE.
+ *
+ * The internal reference to the Channel instance associated with the channel
+ * is removed, which may result in the Channel object being freed.
+ *
+ * OnChannelClosed is not called in response to this method call.
+ */
+ int CloseChannel(int channel_id);
+
+ /*
+ * Update the event bits for the given channel (given by id), using the
+ * given clear and set masks.
+ *
+ * This is useful for asynchronously signaling events that clients may be
+ * waiting for using select/poll/epoll.
+ */
+ int ModifyChannelEvents(int channel_id, int clear_mask, int set_mask);
+
+ /*
+ * Create a new channel and push it as a file descriptor to the process
+ * sending the |message|. |flags| may be set to O_NONBLOCK and/or
+ * O_CLOEXEC to control the initial behavior of the new file descriptor (the
+ * sending process may change these later using fcntl()). The internal Channel
+ * instance associated with this channel is set to |channel|, which may be
+ * nullptr. The new channel id allocated for this channel is returned in
+ * |channel_id|, which may also be nullptr if not needed.
+ *
+ * On success, returns the remote channel handle for the new channel in the
+ * sending process' handle space. This MUST be returned to the sender via
+ * Message::Reply(), Message::Write(), or Message::WriteVector().
+ *
+ * On error, returns an errno code describing the cause of the error.
+ *
+ * Service::OnChannelCreate() is not called in response to the creation of the
+ * new channel.
+ */
+ Status<RemoteChannelHandle> PushChannel(
+ Message* message, int flags, const std::shared_ptr<Channel>& channel,
+ int* channel_id);
+
+ /*
+ * Check whether the |ref| is a reference to a channel to this service.
+ * If the channel reference in question is valid, the Channel object is
+ * returned in |channel| when non-nullptr.
+ *
+ * Return values:
+ * channel_id - id of the channel if the channel reference.
+ * Errors:
+ * EOPNOTSUPP - the file descriptor is not a channel or is a channel to
+ * another service.
+ * EBADF - the file descriptor is invalid.
+ * FAULT - |channel_id| or |channel| are non-nullptr and point to invalid
+ * memory addresses.
+ * EINVAL - the value of |ref| is invalid or the message id for this
+ * message is no longer valid.
+ */
+ Status<int> CheckChannel(const Message* message, ChannelReference ref,
+ std::shared_ptr<Channel>* channel) const;
+
+ /*
+ * Overload of CheckChannel() that automatically converts to shared pointers
+ * of types derived from Channel.
+ */
+ template <class C>
+ Status<int> CheckChannel(const Message* message, ChannelReference ref,
+ std::shared_ptr<C>* channel) const {
+ std::shared_ptr<Channel> base_pointer;
+ const Status<int> ret =
+ CheckChannel(message, ref, channel ? &base_pointer : nullptr);
+ if (channel)
+ *channel = std::static_pointer_cast<C>(base_pointer);
+ return ret;
+ }
+
+ /*
+ * Handle a message. Subclasses override this to receive messages and decide
+ * how to dispatch them.
+ *
+ * The default implementation simply calls defaultHandleMessage().
+ * Subclasses should call the same for any unrecognized message opcodes.
+ */
+ virtual int HandleMessage(Message& message);
+
+ /*
+ * Handle an asynchronous message. Subclasses override this to receive
+ * asynchronous "impulse" messages. Impulses have a limited-size payload that
+ * is transferred upfront with the message description.
+ */
+ virtual void HandleImpulse(Message& impulse);
+
+ /*
+ * The default message handler. It is important that all messages
+ * (eventually) get a reply. This method should be called by subclasses for
+ * any unrecognized opcodes or otherwise unhandled messages to prevent
+ * erroneous requests from blocking indefinitely.
+ *
+ * Provides default handling of CHANNEL_OPEN and CHANNEL_CLOSE, calling
+ * OnChannelOpen() and OnChannelClose(), respectively.
+ *
+ * For all other message opcodes, this method replies with -ENOTSUP.
+ */
+ int DefaultHandleMessage(Message& message);
+
+ /*
+ * Called when system properties have changed. Subclasses should implement
+ * this method if they need to handle when system properties change.
+ */
+ virtual void OnSysPropChange();
+
+ /*
+ * Get the endpoint for the service.
+ */
+ Endpoint* endpoint() const { return endpoint_.get(); }
+
+ /*
+ * Cancels the endpoint, unblocking any receiver threads waiting in
+ * ReceiveAndDispatch().
+ */
+ int Cancel();
+
+ /*
+ * Iterator type for Channel map iterators.
+ */
+ using ChannelIterator =
+ std::unordered_map<int, std::shared_ptr<Channel>>::iterator;
+
+ /*
+ * Iterates over the Channel map and performs the action given by |action| on
+ * each channel map item (const ChannelIterator::value_type).
+ * |channels_mutex_| is not held; it is the responsibility of the caller to
+ * ensure serialization between threads that modify or iterate over the
+ * Channel map.
+ */
+ template <class A>
+ void ForEachChannelUnlocked(A action) const {
+ std::for_each(channels_.begin(), channels_.end(), action);
+ }
+
+ /*
+ * Iterates over the Channel map and performs the action given by |action| on
+ * each channel map item (const ChannelIterator::value_type).
+ * |channels_mutex_| is held to serialize access to the map; care must be
+ * taken to avoid recursively acquiring the mutex, for example, by calling
+ * Service::{GetChannel,SetChannel,CloseChannel,PushChannel}() or
+ * Message::SetChannel() in the action.
+ */
+ template <class A>
+ void ForEachChannel(A action) const {
+ std::lock_guard<std::mutex> autolock(channels_mutex_);
+ ForEachChannelUnlocked(action);
+ }
+
+ /*
+ * Subclasses of Service may override this method to provide a text string
+ * describing the state of the service. This method is called by
+ * HandleSystemMessage in response to the standard
+ * DUMP_STATE message. The string returned to the dump state client is
+ * truncated to |max_length| and reflects the maximum size the client can
+ * handle.
+ */
+ virtual std::string DumpState(size_t max_length);
+
+ /*
+ * Receives a message on this Service instance's endpoint and dispatches it.
+ * If the endpoint is in blocking mode this call blocks until a message is
+ * received, a signal is delivered to this thread, or the service is canceled.
+ * If the endpoint is in non-blocking mode and a message is not pending this
+ * call returns immediately with -ETIMEDOUT.
+ */
+ int ReceiveAndDispatch();
+
+ private:
+ friend class Message;
+
+ bool HandleSystemMessage(Message& message);
+
+ Service(const Service&);
+ void operator=(const Service&) = delete;
+
+ const std::string name_;
+ std::unique_ptr<Endpoint> endpoint_;
+
+ /*
+ * Maintains references to active channels.
+ */
+ mutable std::mutex channels_mutex_;
+ std::unordered_map<int, std::shared_ptr<Channel>> channels_;
+};
+
+/*
+ * Utility base class for services. This template handles allocation and
+ * initialization checks, reducing boiler plate code.
+ */
+template <typename TYPE>
+class ServiceBase : public Service {
+ public:
+ /*
+ * Static service allocation method that check for initialization errors.
+ * If errors are encountered these automatically clean up and return
+ * nullptr.
+ */
+ template <typename... Args>
+ static inline std::shared_ptr<TYPE> Create(Args&&... args) {
+ std::shared_ptr<TYPE> service(new TYPE(std::forward<Args>(args)...));
+ if (service->IsInitialized())
+ return service;
+ else
+ return nullptr;
+ }
+
+ protected:
+ /*
+ * Shorthand for subclasses to refer to this base, particularly
+ * to call the base class constructor.
+ */
+ typedef ServiceBase<TYPE> BASE;
+
+ ServiceBase(const std::string& name, std::unique_ptr<Endpoint> endpoint)
+ : Service(name, std::move(endpoint)) {}
+};
+
+#ifndef STRINGIFY
+#define STRINGIFY2(s) #s
+#define STRINGIFY(s) STRINGIFY2(s)
+#endif
+
+#define PDX_ERROR_PREFIX "[" __FILE__ ":" STRINGIFY(__LINE__) "]"
+
+/*
+ * Macros for replying to messages. Error handling can be tedious;
+ * these macros make things a little cleaner.
+ */
+#define CHECK_ERROR(cond, error, fmt, ...) \
+ do { \
+ if ((cond)) { \
+ ALOGE(fmt, ##__VA_ARGS__); \
+ goto error; \
+ } \
+ } while (0)
+
+#define REPLY_ERROR(message, error, error_label) \
+ do { \
+ int __ret = message.ReplyError(error); \
+ CHECK_ERROR(__ret < 0, error_label, \
+ PDX_ERROR_PREFIX " Failed to reply to message because: %s\n", \
+ strerror(-__ret)); \
+ goto error_label; \
+ } while (0)
+
+#define REPLY_ERROR_RETURN(message, error, ...) \
+ do { \
+ int __ret = message.ReplyError(error); \
+ ALOGE_IF(__ret < 0, \
+ PDX_ERROR_PREFIX " Failed to reply to message because: %s", \
+ strerror(-__ret)); \
+ return __VA_ARGS__; \
+ } while (0)
+
+#define REPLY_MESSAGE(message, message_return_code, error_label) \
+ do { \
+ int __ret = message.Reply(message_return_code); \
+ CHECK_ERROR(__ret < 0, error_label, \
+ PDX_ERROR_PREFIX " Failed to reply to message because: %s\n", \
+ strerror(-__ret)); \
+ goto error_label; \
+ } while (0)
+
+#define REPLY_SUCCESS(message, message_return_code, error_label) \
+ REPLY_MESSAGE(message, message_return_code, error_label)
+
+#define REPLY_MESSAGE_RETURN(message, message_return_code, ...) \
+ do { \
+ int __ret = message.Reply(message_return_code); \
+ ALOGE_IF(__ret < 0, \
+ PDX_ERROR_PREFIX " Failed to reply to message because: %s", \
+ strerror(-__ret)); \
+ return __VA_ARGS__; \
+ } while (0)
+
+#define REPLY_SUCCESS_RETURN(message, message_return_code, ...) \
+ REPLY_MESSAGE_RETURN(message, message_return_code, __VA_ARGS__)
+
+#define REPLY_FD(message, push_fd, error_label) \
+ do { \
+ int __ret = message.ReplyFileDescriptor(push_fd); \
+ CHECK_ERROR(__ret < 0, error_label, \
+ PDX_ERROR_PREFIX " Failed to reply to message because: %s\n", \
+ strerror(-__ret)); \
+ goto error_label; \
+ } while (0)
+
+#define REPLY_FD_RETURN(message, push_fd, ...) \
+ do { \
+ int __ret = message.ReplyFileDescriptor(push_fd); \
+ ALOGE_IF(__ret < 0, \
+ PDX_ERROR_PREFIX " Failed to reply to message because: %s", \
+ strerror(-__ret)); \
+ return __VA_ARGS__; \
+ } while (0)
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_SERVICE_H_
diff --git a/libs/vr/libpdx/private/pdx/service_dispatcher.h b/libs/vr/libpdx/private/pdx/service_dispatcher.h
new file mode 100644
index 0000000..c5e342a
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/service_dispatcher.h
@@ -0,0 +1,79 @@
+#ifndef ANDROID_PDX_SERVICE_DISPATCHER_H_
+#define ANDROID_PDX_SERVICE_DISPATCHER_H_
+
+#include <memory>
+
+namespace android {
+namespace pdx {
+
+class Service;
+
+/*
+ * ServiceDispatcher manages a list of Service instances and handles message
+ * reception and dispatch to the services. This makes repetitive dispatch tasks
+ * easier to implement.
+ */
+class ServiceDispatcher {
+ public:
+ virtual ~ServiceDispatcher() = default;
+
+ /*
+ * Adds a service to the list of services handled by this dispatcher. This
+ * will fail if any threads are blocked waiting for messages in this
+ * dispatcher.
+ *
+ * Returns 0 on success; -EEXIST if the service was already added.
+ */
+ virtual int AddService(const std::shared_ptr<Service>& service) = 0;
+
+ /*
+ * Removes a service from this dispatcher. This will fail if any threads are
+ * blocked waiting for messages in this dispatcher.
+ *
+ * Returns 0 on success; -ENOENT if the service was not previously added;
+ * -EBUSY if there are threads in the dispatcher.
+ */
+ virtual int RemoveService(const std::shared_ptr<Service>& service) = 0;
+
+ /*
+ * Receive and dispatch one set of messages. Multiple threads may enter this
+ * method to create an implicit thread pool, as described for
+ * enterDispatchLoop() below, however this method exits after one dispatch
+ * cycle, requiring an external loop. This is useful when other work needs
+ * to be done in the service dispatch loop.
+ */
+ virtual int ReceiveAndDispatch() = 0;
+
+ /*
+ * Same as above with timeout in milliseconds. A negative value means
+ * infinite timeout, while a value of 0 means return immediately if no
+ * messages are available to receive.
+ */
+ virtual int ReceiveAndDispatch(int timeout) = 0;
+
+ /*
+ * Receive and dispatch messages until canceled. When more than one thread
+ * enters this method it creates an implicit thread pool to dispatch messages.
+ * Explicit thread pools may be created by using a single dispatch thread that
+ * hands Message instances (via move assignment) over to a queue of threads
+ * (or perhaps one of several) to handle.
+ */
+ virtual int EnterDispatchLoop() = 0;
+
+ /*
+ * Sets the canceled state of the dispatcher. When canceled is true, any
+ * threads blocked waiting for messages will return. This method waits until
+ * all dispatch threads have exited the dispatcher.
+ */
+ virtual void SetCanceled(bool cancel) = 0;
+
+ /*
+ * Gets the canceled state of the dispatcher.
+ */
+ virtual bool IsCanceled() const = 0;
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_SERVICE_DISPATCHER_H_
diff --git a/libs/vr/libpdx/private/pdx/service_endpoint.h b/libs/vr/libpdx/private/pdx/service_endpoint.h
new file mode 100644
index 0000000..613be7c
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/service_endpoint.h
@@ -0,0 +1,149 @@
+#ifndef ANDROID_PDX_ENDPOINT_H_
+#define ANDROID_PDX_ENDPOINT_H_
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/status.h>
+
+struct iovec;
+
+namespace android {
+namespace pdx {
+
+class Service;
+class Channel;
+class Message;
+
+struct MessageInfo {
+ int pid{0};
+ int tid{0};
+ int cid{0};
+ int mid{0};
+ int euid{0};
+ int egid{0};
+ int32_t op{0};
+ uint32_t flags{0};
+ Service* service{nullptr};
+ Channel* channel{nullptr};
+ size_t send_len{0};
+ size_t recv_len{0};
+ size_t fd_count{0};
+ uint64_t impulse[4] = {};
+};
+
+// Wrapper around transport endpoint. Abstracts the underlying transport APIs in
+// a way, that the underlying IPC can be substituted for another technology
+// without changing the Service, Client and Message classes of this library.
+class Endpoint {
+ public:
+ virtual ~Endpoint() = default;
+
+ // Returns a tag that uniquely identifies a specific underlying IPC transport.
+ virtual uint32_t GetIpcTag() const = 0;
+
+ // Associates a Service instance with an endpoint by setting the service
+ // context pointer to the address of the Service. Only one Service may be
+ // associated with a given endpoint.
+ virtual int SetService(Service* service) = 0;
+
+ // Set the channel context for the given channel.
+ virtual int SetChannel(int channel_id, Channel* channel) = 0;
+
+ // Close a channel, signaling the client file object and freeing the channel
+ // id. Once closed, the client side of the channel always returns the error
+ // ESHUTDOWN and signals the poll/epoll events POLLHUP and POLLFREE.
+ virtual int CloseChannel(int channel_id) = 0;
+
+ // Update the event bits for the given channel (given by id), using the
+ // given clear and set masks.
+ virtual int ModifyChannelEvents(int channel_id, int clear_mask,
+ int set_mask) = 0;
+
+ // Create a new channel and push it as a file descriptor to the process
+ // sending the |message|. |flags| may be set to O_NONBLOCK and/or
+ // O_CLOEXEC to control the initial behavior of the new file descriptor (the
+ // sending process may change these later using fcntl()). The internal Channel
+ // instance associated with this channel is set to |channel|, which may be
+ // nullptr. The new channel id allocated for this channel is returned in
+ // |channel_id|, which may also be nullptr if not needed.
+ virtual Status<RemoteChannelHandle> PushChannel(Message* message, int flags,
+ Channel* channel,
+ int* channel_id) = 0;
+
+ // Check whether the |ref| is a reference to a channel to the service
+ // represented by the |endpoint|. If the channel reference in question is
+ // valid, the Channel object is returned in |channel| when non-nullptr and
+ // the channel ID is returned through the Status object.
+ virtual Status<int> CheckChannel(const Message* message, ChannelReference ref,
+ Channel** channel) = 0;
+
+ // The default message handler. It is important that all messages
+ // (eventually) get a reply. This method should be called for any unrecognized
+ // opcodes or otherwise unhandled messages to prevent erroneous requests from
+ // blocking indefinitely.
+ virtual int DefaultHandleMessage(const MessageInfo& info) = 0;
+
+ // Receives a message on the given endpoint file descriptor.
+ virtual int MessageReceive(Message* message) = 0;
+
+ // Replies to the message with a return code.
+ virtual int MessageReply(Message* message, int return_code) = 0;
+
+ // Replies to the message with a file descriptor.
+ virtual int MessageReplyFd(Message* message, unsigned int push_fd) = 0;
+
+ // Replies to the message with a local channel handle.
+ virtual int MessageReplyChannelHandle(Message* message,
+ const LocalChannelHandle& handle) = 0;
+
+ // Replies to the message with a borrowed local channel handle.
+ virtual int MessageReplyChannelHandle(
+ Message* message, const BorrowedChannelHandle& handle) = 0;
+
+ // Replies to the message with a remote channel handle.
+ virtual int MessageReplyChannelHandle(Message* message,
+ const RemoteChannelHandle& handle) = 0;
+
+ // Reads message data into an array of memory buffers.
+ virtual ssize_t ReadMessageData(Message* message, const iovec* vector,
+ size_t vector_length) = 0;
+
+ // Sends reply data for message.
+ virtual ssize_t WriteMessageData(Message* message, const iovec* vector,
+ size_t vector_length) = 0;
+
+ // Records a file descriptor into the message buffer and returns the remapped
+ // reference to be sent to the remote process.
+ virtual FileReference PushFileHandle(Message* message,
+ const LocalHandle& handle) = 0;
+ virtual FileReference PushFileHandle(Message* message,
+ const BorrowedHandle& handle) = 0;
+ virtual FileReference PushFileHandle(Message* message,
+ const RemoteHandle& handle) = 0;
+ virtual ChannelReference PushChannelHandle(
+ Message* message, const LocalChannelHandle& handle) = 0;
+ virtual ChannelReference PushChannelHandle(
+ Message* message, const BorrowedChannelHandle& handle) = 0;
+ virtual ChannelReference PushChannelHandle(
+ Message* message, const RemoteChannelHandle& handle) = 0;
+
+ // Obtains a file descriptor/channel handle from a message for the given
+ // reference.
+ virtual LocalHandle GetFileHandle(Message* message,
+ FileReference ref) const = 0;
+ virtual LocalChannelHandle GetChannelHandle(Message* message,
+ ChannelReference ref) const = 0;
+
+ // Transport-specific message state management.
+ virtual void* AllocateMessageState() = 0;
+ virtual void FreeMessageState(void* state) = 0;
+
+ // Cancels the endpoint, unblocking any receiver threads waiting for a
+ // message.
+ virtual int Cancel() = 0;
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_ENDPOINT_H_
diff --git a/libs/vr/libpdx/private/pdx/status.h b/libs/vr/libpdx/private/pdx/status.h
new file mode 100644
index 0000000..ca2832c
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/status.h
@@ -0,0 +1,168 @@
+#ifndef ANDROID_PDX_STATUS_H_
+#define ANDROID_PDX_STATUS_H_
+
+#include <algorithm>
+#include <memory>
+#include <string>
+
+namespace android {
+namespace pdx {
+
+// This is a helper class for constructing Status<T> with an error code.
+struct ErrorStatus {
+ public:
+ ErrorStatus(int error) : error_{error} {}
+ int error() const { return error_; }
+
+ static std::string ErrorToString(int error_code);
+
+ private:
+ int error_;
+};
+
+// Status<T> is a container class that can be used to return a value of type T
+// or error code to the caller.
+template <typename T>
+class Status {
+ public:
+ // Default constructor so an empty Status object can be created.
+ Status() : error_{-1} {}
+
+ // Value copy/move constructors. These are intentionally not marked as
+ // explicit to allow direct value returns from functions without having
+ // to explicitly wrap them into Status<T>().
+ Status(const T& value) : value_{value} {} // NOLINT(runtime/explicit)
+ Status(T&& value) : value_{std::move(value)} {} // NOLINT(runtime/explicit)
+
+ // Constructor for storing an error code inside the Status object.
+ Status(const ErrorStatus& error_status) // NOLINT(runtime/explicit)
+ : error_{error_status.error()} {}
+
+ // Copy/move constructors. Move constructor leaves |other| object in empty
+ // state.
+ Status(const Status& other) = default;
+ Status(Status&& other)
+ : value_{std::move(other.value_)}, error_{other.error_} {
+ other.error_ = -1;
+ }
+
+ // Assignment operators.
+ Status& operator=(const Status& other) = default;
+ Status& operator=(Status&& other) {
+ error_ = other.error_;
+ value_ = std::move(other.value_);
+ other.error_ = -1;
+ T empty;
+ std::swap(other.value_, empty);
+ return *this;
+ }
+
+ // Change the value/error code of the status object directly.
+ void SetValue(T value) {
+ error_ = 0;
+ value_ = std::move(value);
+ }
+ void SetError(int error) {
+ error_ = error;
+ T empty;
+ std::swap(value_, empty);
+ }
+
+ // If |other| is in error state, copy the error code to this object.
+ // Returns true if error was propagated
+ template<typename U>
+ bool PropagateError(const Status<U>& other) {
+ if (!other.ok() && !other.empty()) {
+ SetError(other.error());
+ return true;
+ }
+ return false;
+ }
+
+ // Returns true if the status object contains valid value for type T.
+ // This means, the object is not empty and does not contain an error code.
+ bool ok() const { return error_ == 0; }
+
+ // Checks if the object is empty (doesn't contain a valid value nor an error).
+ bool empty() const { return error_ < 0; }
+
+ // Explicit bool conversion, equivalent to invoking ok().
+ explicit operator bool() const { return ok(); }
+
+ // Accessors for the value stored in Status. Calling when ok() is false leads
+ // to undefined behavior.
+ const T& get() const { return value_; }
+ T&& take() {
+ error_ = -1;
+ return std::move(value_);
+ }
+
+ // Returns the error code stored in the object. These codes are positive
+ // non-zero values.
+ // Can be called only when an error is actually stored (that is, the object
+ // is not empty nor containing a valid value).
+ int error() const { return std::max(error_, 0); }
+
+ // Returns the error message associated with error code stored in the object.
+ // The message is the same as the string returned by strerror(status.error()).
+ // Can be called only when an error is actually stored (that is, the object
+ // is not empty nor containing a valid value).
+ std::string GetErrorMessage() const {
+ std::string message;
+ if (error_ > 0)
+ message = ErrorStatus::ErrorToString(error_);
+ return message;
+ }
+
+ private:
+ T value_{};
+ int error_{0};
+};
+
+// Specialization for status containing no other value but the error code.
+template <>
+class Status<void> {
+ public:
+ Status() = default;
+ Status(const ErrorStatus& error_status) // NOLINT(runtime/explicit)
+ : error_{error_status.error()} {}
+ void SetValue() { error_ = 0; }
+ void SetError(int error) { error_ = error; }
+
+ template<typename U>
+ bool PropagateError(const Status<U>& other) {
+ if (!other.ok() && !other.empty()) {
+ SetError(other.error());
+ return true;
+ }
+ return false;
+ }
+
+ bool ok() const { return error_ == 0; }
+ bool empty() const { return false; }
+ explicit operator bool() const { return ok(); }
+ int error() const { return std::max(error_, 0); }
+ std::string GetErrorMessage() const {
+ std::string message;
+ if (error_ > 0)
+ message = ErrorStatus::ErrorToString(error_);
+ return message;
+ }
+
+ private:
+ int error_{0};
+};
+
+// TODO(avakulenko): Remove these function once all callers of it are gone.
+inline int ReturnStatusOrError(const Status<void>& status) {
+ return status ? 0 : -status.error();
+}
+
+inline int ReturnStatusOrError(const Status<int>& status) {
+ return status ? status.get() : -status.error();
+}
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_STATUS_H_
diff --git a/libs/vr/libpdx/private/pdx/trace.h b/libs/vr/libpdx/private/pdx/trace.h
new file mode 100644
index 0000000..ebe8491
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/trace.h
@@ -0,0 +1,35 @@
+#ifndef ANDROID_PDX_TRACE_H_
+#define ANDROID_PDX_TRACE_H_
+
+// Tracing utilities for libpdx. Tracing in the service framework is enabled
+// under these conditions:
+// 1. ATRACE_TAG is defined, AND
+// 2. ATRACE_TAG does not equal ATRACE_TAG_NEVER, AND
+// 3. PDX_TRACE_ENABLED is defined, AND
+// 4. PDX_TRACE_ENABLED is equal to logical true.
+//
+// If any of these conditions are not met tracing is completely removed from the
+// library and headers.
+
+// If ATRACE_TAG is not defined, default to never.
+#ifndef ATRACE_TAG
+#define ATRACE_TAG ATRACE_TAG_NEVER
+#endif
+
+// Include tracing functions after the trace tag is defined.
+#include <utils/Trace.h>
+
+// If PDX_TRACE_ENABLED is not defined, default to off.
+#ifndef PDX_TRACE_ENABLED
+#define PDX_TRACE_ENABLED 0
+#endif
+
+#if (ATRACE_TAG) != (ATRACE_TAG_NEVER) && (PDX_TRACE_ENABLED)
+#define PDX_TRACE_NAME ATRACE_NAME
+#else
+#define PDX_TRACE_NAME(name) \
+ do { \
+ } while (0)
+#endif
+
+#endif // ANDROID_PDX_TRACE_H_
diff --git a/libs/vr/libpdx/private/pdx/utility.h b/libs/vr/libpdx/private/pdx/utility.h
new file mode 100644
index 0000000..c8c717c
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/utility.h
@@ -0,0 +1,367 @@
+#ifndef ANDROID_PDX_UTILITY_H_
+#define ANDROID_PDX_UTILITY_H_
+
+#include <cstdint>
+#include <iterator>
+
+#include <pdx/rpc/sequence.h>
+
+// Utilities for testing object serialization.
+
+namespace android {
+namespace pdx {
+
+class ByteBuffer {
+ public:
+ using iterator = uint8_t*;
+ using const_iterator = const uint8_t*;
+ using size_type = size_t;
+
+ ByteBuffer() = default;
+ ByteBuffer(const ByteBuffer& other) {
+ resize(other.size());
+ if (other.size())
+ memcpy(data_, other.data(), other.size());
+ }
+
+ ByteBuffer& operator=(const ByteBuffer& other) {
+ resize(other.size());
+ if (other.size())
+ memcpy(data_, other.data(), other.size());
+ return *this;
+ }
+
+ ByteBuffer& operator=(ByteBuffer&& other) {
+ std::swap(data_, other.data_);
+ std::swap(size_, other.size_);
+ std::swap(capacity_, other.capacity_);
+ other.clear();
+ return *this;
+ }
+
+ inline const uint8_t* data() const { return data_; }
+ inline uint8_t* data() { return data_; }
+ inline size_t size() const { return size_; }
+ inline size_t capacity() const { return capacity_; }
+
+ iterator begin() { return data_; }
+ const_iterator begin() const { return data_; }
+ iterator end() { return data_ + size_; }
+ const_iterator end() const { return data_ + size_; }
+
+ inline bool operator==(const ByteBuffer& other) const {
+ return size_ == other.size_ &&
+ (size_ == 0 || memcmp(data_, other.data_, size_) == 0);
+ }
+
+ inline bool operator!=(const ByteBuffer& other) const {
+ return !operator==(other);
+ }
+
+ inline void reserve(size_t size) {
+ if (size <= capacity_)
+ return;
+ // Find next power of 2 (assuming the size is 32 bits for now).
+ size--;
+ size |= size >> 1;
+ size |= size >> 2;
+ size |= size >> 4;
+ size |= size >> 8;
+ size |= size >> 16;
+ size++;
+ void* new_data = data_ ? realloc(data_, size) : malloc(size);
+ // TODO(avakulenko): Check for allocation failures.
+ data_ = static_cast<uint8_t*>(new_data);
+ capacity_ = size;
+ }
+
+ inline void resize(size_t size) {
+ reserve(size);
+ size_ = size;
+ }
+
+ inline uint8_t* grow_by(size_t size_delta) {
+ size_t old_size = size_;
+ resize(old_size + size_delta);
+ return data_ + old_size;
+ }
+
+ inline void clear() { size_ = 0; }
+
+ private:
+ uint8_t* data_{nullptr};
+ size_t size_{0};
+ size_t capacity_{0};
+};
+
+// Utility functions to increment/decrement void pointers to data buffers.
+template <typename OFFSET_T>
+inline const void* AdvancePointer(const void* ptr, OFFSET_T offset) {
+ return static_cast<const uint8_t*>(ptr) + offset;
+}
+
+template <typename OFFSET_T>
+inline void* AdvancePointer(void* ptr, OFFSET_T offset) {
+ return static_cast<uint8_t*>(ptr) + offset;
+}
+
+inline ptrdiff_t PointerDistance(const void* end, const void* begin) {
+ return static_cast<const uint8_t*>(end) - static_cast<const uint8_t*>(begin);
+}
+
+// Utility to build sequences of types.
+template <typename, typename>
+struct AppendTypeSequence;
+
+template <typename T, typename... S, template <typename...> class TT>
+struct AppendTypeSequence<T, TT<S...>> {
+ using type = TT<S..., T>;
+};
+
+// Utility to generate repeated types.
+template <typename T, std::size_t N, template <typename...> class TT>
+struct RepeatedType {
+ using type = typename AppendTypeSequence<
+ T, typename RepeatedType<T, N - 1, TT>::type>::type;
+};
+
+template <typename T, template <typename...> class TT>
+struct RepeatedType<T, 0, TT> {
+ using type = TT<>;
+};
+
+template <typename V, typename S>
+inline V ReturnValueHelper(V value, S /*ignore*/) {
+ return value;
+}
+
+template <typename R, typename V, size_t... S>
+inline R GetNTupleHelper(V value, rpc::IndexSequence<S...>) {
+ return std::make_tuple(ReturnValueHelper(value, S)...);
+}
+
+// Returns an N-tuple of type std::tuple<T,...T> containing |value| in each
+// element.
+template <size_t N, typename T,
+ typename R = typename RepeatedType<T, N, std::tuple>::type>
+inline R GetNTuple(T value) {
+ return GetNTupleHelper<R>(value, rpc::MakeIndexSequence<N>{});
+}
+
+class NoOpOutputResourceMapper : public OutputResourceMapper {
+ public:
+ FileReference PushFileHandle(const LocalHandle& handle) override {
+ return handle.Get();
+ }
+
+ FileReference PushFileHandle(const BorrowedHandle& handle) override {
+ return handle.Get();
+ }
+
+ FileReference PushFileHandle(const RemoteHandle& handle) override {
+ return handle.Get();
+ }
+
+ ChannelReference PushChannelHandle(
+ const LocalChannelHandle& handle) override {
+ return handle.value();
+ }
+
+ ChannelReference PushChannelHandle(
+ const BorrowedChannelHandle& handle) override {
+ return handle.value();
+ }
+
+ ChannelReference PushChannelHandle(
+ const RemoteChannelHandle& handle) override {
+ return handle.value();
+ }
+};
+
+class NoOpInputResourceMapper : public InputResourceMapper {
+ public:
+ bool GetFileHandle(FileReference ref, LocalHandle* handle) override {
+ *handle = LocalHandle{ref};
+ return true;
+ }
+
+ bool GetChannelHandle(ChannelReference ref,
+ LocalChannelHandle* handle) override {
+ *handle = LocalChannelHandle{nullptr, ref};
+ return true;
+ }
+};
+
+class NoOpResourceMapper : public NoOpOutputResourceMapper,
+ public NoOpInputResourceMapper {};
+
+// Simple implementation of the payload interface, required by
+// Serialize/Deserialize. This is intended for test cases, where compatibility
+// with std::vector is helpful.
+class Payload : public MessageWriter,
+ public MessageReader,
+ public OutputResourceMapper {
+ public:
+ using BaseType = ByteBuffer;
+ using iterator = typename BaseType::iterator;
+ using const_iterator = typename BaseType::const_iterator;
+ using size_type = typename BaseType::size_type;
+
+ Payload() = default;
+ explicit Payload(size_type count, uint8_t value = 0) { Append(count, value); }
+ Payload(const Payload& other) : buffer_(other.buffer_) {}
+ Payload(const std::initializer_list<uint8_t>& initializer) {
+ buffer_.resize(initializer.size());
+ std::copy(initializer.begin(), initializer.end(), buffer_.begin());
+ }
+
+ Payload& operator=(const Payload& other) {
+ buffer_ = other.buffer_;
+ read_pos_ = 0;
+ return *this;
+ }
+ Payload& operator=(const std::initializer_list<uint8_t>& initializer) {
+ buffer_.resize(initializer.size());
+ std::copy(initializer.begin(), initializer.end(), buffer_.begin());
+ read_pos_ = 0;
+ return *this;
+ }
+
+ // Compares Payload with Payload.
+ bool operator==(const Payload& other) const {
+ return buffer_ == other.buffer_;
+ }
+ bool operator!=(const Payload& other) const {
+ return buffer_ != other.buffer_;
+ }
+
+ // Compares Payload with std::vector.
+ template <typename Type, typename AllocatorType>
+ typename std::enable_if<sizeof(Type) == sizeof(uint8_t), bool>::type
+ operator==(const std::vector<Type, AllocatorType>& other) const {
+ return buffer_.size() == other.size() &&
+ memcmp(buffer_.data(), other.data(), other.size()) == 0;
+ }
+ template <typename Type, typename AllocatorType>
+ typename std::enable_if<sizeof(Type) == sizeof(uint8_t), bool>::type
+ operator!=(const std::vector<Type, AllocatorType>& other) const {
+ return !operator!=(other);
+ }
+
+ iterator begin() { return buffer_.begin(); }
+ const_iterator begin() const { return buffer_.begin(); }
+ iterator end() { return buffer_.end(); }
+ const_iterator end() const { return buffer_.end(); }
+
+ void Append(size_type count, uint8_t value) {
+ auto* data = buffer_.grow_by(count);
+ std::fill(data, data + count, value);
+ }
+
+ void Clear() {
+ buffer_.clear();
+ file_handles_.clear();
+ read_pos_ = 0;
+ }
+
+ void Rewind() { read_pos_ = 0; }
+
+ uint8_t* Data() { return buffer_.data(); }
+ const uint8_t* Data() const { return buffer_.data(); }
+ size_type Size() const { return buffer_.size(); }
+
+ // MessageWriter
+ void* GetNextWriteBufferSection(size_t size) override {
+ return buffer_.grow_by(size);
+ }
+
+ OutputResourceMapper* GetOutputResourceMapper() override { return this; }
+
+ // OutputResourceMapper
+ FileReference PushFileHandle(const LocalHandle& handle) override {
+ if (handle) {
+ const int ref = file_handles_.size();
+ file_handles_.push_back(handle.Get());
+ return ref;
+ } else {
+ return handle.Get();
+ }
+ }
+
+ FileReference PushFileHandle(const BorrowedHandle& handle) override {
+ if (handle) {
+ const int ref = file_handles_.size();
+ file_handles_.push_back(handle.Get());
+ return ref;
+ } else {
+ return handle.Get();
+ }
+ }
+
+ FileReference PushFileHandle(const RemoteHandle& handle) override {
+ return handle.Get();
+ }
+
+ ChannelReference PushChannelHandle(
+ const LocalChannelHandle& handle) override {
+ if (handle) {
+ const int ref = file_handles_.size();
+ file_handles_.push_back(handle.value());
+ return ref;
+ } else {
+ return handle.value();
+ }
+ }
+
+ ChannelReference PushChannelHandle(
+ const BorrowedChannelHandle& handle) override {
+ if (handle) {
+ const int ref = file_handles_.size();
+ file_handles_.push_back(handle.value());
+ return ref;
+ } else {
+ return handle.value();
+ }
+ }
+
+ ChannelReference PushChannelHandle(
+ const RemoteChannelHandle& handle) override {
+ return handle.value();
+ }
+
+ // MessageReader
+ BufferSection GetNextReadBufferSection() override {
+ return {buffer_.data() + read_pos_, &*buffer_.end()};
+ }
+
+ void ConsumeReadBufferSectionData(const void* new_start) override {
+ read_pos_ = PointerDistance(new_start, buffer_.data());
+ }
+
+ InputResourceMapper* GetInputResourceMapper() override {
+ return &input_resource_mapper_;
+ }
+
+ const int* FdArray() const { return file_handles_.data(); }
+ std::size_t FdCount() const { return file_handles_.size(); }
+
+ private:
+ NoOpInputResourceMapper input_resource_mapper_;
+ ByteBuffer buffer_;
+ std::vector<int> file_handles_;
+ size_t read_pos_{0};
+};
+
+} // namespace pdx
+} // namespace android
+
+// Helper macros for branch prediction hinting.
+#ifdef __GNUC__
+#define PDX_LIKELY(x) __builtin_expect(!!(x), true)
+#define PDX_UNLIKELY(x) __builtin_expect(!!(x), false)
+#else
+#define PDX_LIKELY(x) (x)
+#define PDX_UNLIKELY(x) (x)
+#endif
+
+#endif // ANDROID_PDX_UTILITY_H_
diff --git a/libs/vr/libpdx/serialization_tests.cpp b/libs/vr/libpdx/serialization_tests.cpp
new file mode 100644
index 0000000..5ad1047
--- /dev/null
+++ b/libs/vr/libpdx/serialization_tests.cpp
@@ -0,0 +1,2505 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <thread>
+#include <utility>
+
+#include <gtest/gtest.h>
+#include <pdx/rpc/argument_encoder.h>
+#include <pdx/rpc/array_wrapper.h>
+#include <pdx/rpc/default_initialization_allocator.h>
+#include <pdx/rpc/payload.h>
+#include <pdx/rpc/serializable.h>
+#include <pdx/rpc/serialization.h>
+#include <pdx/rpc/string_wrapper.h>
+#include <pdx/utility.h>
+
+using namespace android::pdx;
+using namespace android::pdx::rpc;
+
+// Tests the serialization/deserialization of all supported types, verifying all
+// reasonable boundary conditions for types with multiple encodings.
+//
+// NOTE: Sometimes this file uses the construct "var = decltype(var)({...})"
+// instead of the equivalent "var = {...}" to construct vectors. This is to
+// prevent clang-format from producing annoyingly vertical code from long
+// initializers.
+
+// TODO(eieio): Automatically generate some of these tests?
+
+namespace {
+
+// Test data for serialization/deserialization of floats.
+const float kZeroFloat = 0.0f;
+const float kOneFloat = 1.0f;
+const auto kZeroFloatBytes = reinterpret_cast<const std::uint8_t*>(&kZeroFloat);
+const auto kOneFloatBytes = reinterpret_cast<const std::uint8_t*>(&kOneFloat);
+const double kZeroDouble = 0.0;
+const double kOneDouble = 1.0;
+const auto kZeroDoubleBytes =
+ reinterpret_cast<const std::uint8_t*>(&kZeroDouble);
+const auto kOneDoubleBytes = reinterpret_cast<const std::uint8_t*>(&kOneDouble);
+
+struct TestType {
+ enum class Foo { kFoo, kBar, kBaz };
+
+ int a;
+ float b;
+ std::string c;
+ Foo d;
+
+ TestType() {}
+ TestType(int a, float b, const std::string& c, Foo d)
+ : a(a), b(b), c(c), d(d) {}
+
+ // Make gtest expressions simpler by defining equality operator. This is not
+ // needed for serialization.
+ bool operator==(const TestType& other) const {
+ return a == other.a && b == other.b && c == other.c && d == other.d;
+ }
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(TestType, a, b, c, d);
+};
+
+template <typename FileHandleType>
+struct TestTemplateType {
+ FileHandleType fd;
+
+ TestTemplateType() {}
+ TestTemplateType(FileHandleType fd) : fd(std::move(fd)) {}
+
+ bool operator==(const TestTemplateType& other) const {
+ return fd.Get() == other.fd.Get();
+ }
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(TestTemplateType<FileHandleType>, fd);
+};
+
+// Utilities to generate test maps and payloads.
+template <typename MapType>
+MapType MakeMap(std::size_t size) {
+ MapType result;
+ for (std::size_t i = 0; i < size; i++) {
+ result.emplace(i, i);
+ }
+ return result;
+}
+
+template <typename MapType>
+void InsertKeyValue(MessageWriter* writer, std::size_t size) {
+ MapType map;
+ for (std::size_t i = 0; i < size; i++) {
+ map.emplace(i, i);
+ }
+ for (const auto& element : map) {
+ Serialize(element.first, writer);
+ Serialize(element.second, writer);
+ }
+}
+
+} // anonymous namespace
+
+TEST(SerializableTypes, Constructor) {
+ TestType tt(1, 2.0, "three", TestType::Foo::kBar);
+ EXPECT_EQ(1, tt.a);
+ EXPECT_EQ(2.0, tt.b);
+ EXPECT_EQ("three", tt.c);
+ EXPECT_EQ(TestType::Foo::kBar, tt.d);
+}
+
+TEST(SerializationTest, bool) {
+ Payload result;
+ Payload expected;
+ bool value;
+
+ // True.
+ value = true;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_TRUE};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // False.
+ value = false;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FALSE};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, uint8_t) {
+ Payload result;
+ Payload expected;
+ uint8_t value;
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = (1 << 7) - 1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT8.
+ value = (1 << 7);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, (1 << 7)};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT8.
+ value = 0xff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, uint16_t) {
+ Payload result;
+ Payload expected;
+ uint16_t value;
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = (1 << 7) - 1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT8.
+ value = (1 << 7);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, (1 << 7)};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT8.
+ value = 0xff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT16.
+ value = (1 << 8);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT16, 0, 1};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT16.
+ value = 0xffff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, uint32_t) {
+ Payload result;
+ Payload expected;
+ uint32_t value;
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = (1 << 7) - 1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT8.
+ value = (1 << 7);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, (1 << 7)};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT8.
+ value = 0xff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT16.
+ value = (1 << 8);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT16, 0, 1};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT16.
+ value = 0xffff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT32.
+ value = (1 << 16);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT32, 0, 0, 1, 0};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT32.
+ value = 0xffffffff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT32, 0xff, 0xff, 0xff, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, uint64_t) {
+ Payload result;
+ Payload expected;
+ uint64_t value;
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = (1 << 7) - 1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT8.
+ value = (1 << 7);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, (1 << 7)};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT8.
+ value = 0xff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT16.
+ value = (1 << 8);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT16, 0, 1};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT16.
+ value = 0xffff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT32.
+ value = (1 << 16);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT32, 0, 0, 1, 0};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT32.
+ value = 0xffffffff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT32, 0xff, 0xff, 0xff, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT64.
+ value = (1ULL << 32);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT64, 0, 0, 0, 0, 1, 0, 0, 0};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT64.
+ value = 0xffffffffffffffffULL;
+ Serialize(value, &result);
+ expected = {
+ ENCODING_TYPE_UINT64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, int8_t) {
+ Payload result;
+ Payload expected;
+ int8_t value;
+
+ // Min NEGATIVE FIXINT.
+ value = -32;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max NEGATIVE FIXINT.
+ value = -1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = 127;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT8.
+ value = -128;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT8.
+ value = -33;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0xdf};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, int16_t) {
+ Payload result;
+ Payload expected;
+ int16_t value;
+
+ // Min NEGATIVE FIXINT.
+ value = -32;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max NEGATIVE FIXINT.
+ value = -1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = 127;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT8.
+ value = -128;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT8.
+ value = -33;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0xdf};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT16.
+ value = -32768;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT16, 0x00, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT16.
+ value = 32767;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, int32_t) {
+ Payload result;
+ Payload expected;
+ int32_t value;
+
+ // Min NEGATIVE FIXINT.
+ value = -32;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max NEGATIVE FIXINT.
+ value = -1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = 127;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT8.
+ value = -128;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT8.
+ value = -33;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0xdf};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT16.
+ value = -32768;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT16, 0x00, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT16.
+ value = 32767;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT32.
+ value = -2147483648;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT32, 0x00, 0x00, 0x00, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT32.
+ value = 2147483647;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT32, 0xff, 0xff, 0xff, 0x7f};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, int64_t) {
+ Payload result;
+ Payload expected;
+ int64_t value;
+
+ // Min NEGATIVE FIXINT.
+ value = -32;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max NEGATIVE FIXINT.
+ value = -1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = 127;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT8.
+ value = -128;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT8.
+ value = -33;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0xdf};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT16.
+ value = -32768;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT16, 0x00, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT16.
+ value = 32767;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT32.
+ value = -2147483648;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT32, 0x00, 0x00, 0x00, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT32.
+ value = 2147483647;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT32, 0xff, 0xff, 0xff, 0x7f};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT64.
+ value = -9223372036854775808ULL;
+ Serialize(value, &result);
+ expected = {
+ ENCODING_TYPE_INT64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT64.
+ value = 9223372036854775807ULL;
+ Serialize(value, &result);
+ expected = {
+ ENCODING_TYPE_INT64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, float) {
+ Payload result;
+ Payload expected;
+ float value;
+
+ value = 0.0f;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FLOAT32, kZeroFloatBytes[0], kZeroFloatBytes[1],
+ kZeroFloatBytes[2], kZeroFloatBytes[3]};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ value = 1.0f;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FLOAT32, kOneFloatBytes[0], kOneFloatBytes[1],
+ kOneFloatBytes[2], kOneFloatBytes[3]};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, double) {
+ Payload result;
+ Payload expected;
+ double value;
+
+ value = 0.0f;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FLOAT64, kZeroDoubleBytes[0], kZeroDoubleBytes[1],
+ kZeroDoubleBytes[2], kZeroDoubleBytes[3], kZeroDoubleBytes[4],
+ kZeroDoubleBytes[5], kZeroDoubleBytes[6], kZeroDoubleBytes[7]};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ value = 1.0f;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FLOAT64, kOneDoubleBytes[0], kOneDoubleBytes[1],
+ kOneDoubleBytes[2], kOneDoubleBytes[3], kOneDoubleBytes[4],
+ kOneDoubleBytes[5], kOneDoubleBytes[6], kOneDoubleBytes[7]};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, Enum) {
+ Payload result;
+ Payload expected;
+
+ enum Foo { kFoo, kBar, kBaz };
+ Foo value = kBar;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, EnumClass) {
+ Payload result;
+ Payload expected;
+
+ enum class Foo { kFoo, kBar, kBaz };
+ Foo value = Foo::kBaz;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, LocalHandle) {
+ Payload result;
+ Payload expected;
+ LocalHandle fd1;
+ LocalHandle fd2;
+
+ fd1 = LocalHandle(100);
+ Serialize(fd1, &result);
+ expected = {ENCODING_TYPE_FIXEXT2, ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0, 0};
+ EXPECT_EQ(expected, result);
+ EXPECT_EQ(1u, result.FdCount());
+ EXPECT_EQ(100, result.FdArray()[0]);
+ result.Clear();
+
+ fd2 = LocalHandle(200);
+ Serialize(std::forward_as_tuple(fd1, fd2), &result);
+ expected = decltype(expected)(
+ {ENCODING_TYPE_FIXARRAY_MIN + 2, ENCODING_TYPE_FIXEXT2,
+ ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0, 0, ENCODING_TYPE_FIXEXT2,
+ ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 1, 0});
+ EXPECT_EQ(expected, result);
+ EXPECT_EQ(2u, result.FdCount());
+ EXPECT_EQ(100, result.FdArray()[0]);
+ EXPECT_EQ(200, result.FdArray()[1]);
+ result.Clear();
+
+ fd1.Release(); // Don't try to close fd 100.
+ fd2.Release(); // Don't try to close fd 200.
+
+ fd1 = LocalHandle(-2);
+ Serialize(fd1, &result);
+ expected = {ENCODING_TYPE_FIXEXT2, ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0xfe,
+ 0xff};
+ EXPECT_EQ(expected, result);
+ EXPECT_EQ(0u, result.FdCount());
+ result.Clear();
+}
+
+TEST(SerializationTest, string) {
+ Payload result;
+ Payload expected;
+ std::string value;
+
+ // Min FIXSTR.
+ value = "";
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXSTR_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXSTR.
+ value = std::string((1 << 5) - 1, 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXSTR_MAX};
+ expected.Append((1 << 5) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min STR8.
+ value = std::string((1 << 5), 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_STR8, (1 << 5)};
+ expected.Append((1 << 5), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max STR8.
+ value = std::string((1 << 8) - 1, 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_STR8, (1 << 8) - 1};
+ expected.Append((1 << 8) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min STR16.
+ value = std::string((1 << 8), 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_STR16, 0x00, 0x01};
+ expected.Append((1 << 8), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max STR16.
+ value = std::string((1 << 16) - 1, 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_STR16, 0xff, 0xff};
+ expected.Append((1 << 16) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min STR32.
+ value = std::string((1 << 16), 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_STR32, 0x00, 0x00, 0x01, 0x00};
+ expected.Append((1 << 16), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, StringWrapper) {
+ Payload result;
+ Payload expected;
+ std::string value;
+
+ // Min FIXSTR.
+ value = "";
+ Serialize(WrapString(value), &result);
+ expected = {ENCODING_TYPE_FIXSTR_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXSTR.
+ value = std::string((1 << 5) - 1, 'x');
+ Serialize(WrapString(value), &result);
+ expected = {ENCODING_TYPE_FIXSTR_MAX};
+ expected.Append((1 << 5) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min STR8.
+ value = std::string((1 << 5), 'x');
+ Serialize(WrapString(value), &result);
+ expected = {ENCODING_TYPE_STR8, (1 << 5)};
+ expected.Append((1 << 5), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max STR8.
+ value = std::string((1 << 8) - 1, 'x');
+ Serialize(WrapString(value), &result);
+ expected = {ENCODING_TYPE_STR8, (1 << 8) - 1};
+ expected.Append((1 << 8) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min STR16.
+ value = std::string((1 << 8), 'x');
+ Serialize(WrapString(value), &result);
+ expected = {ENCODING_TYPE_STR16, 0x00, 0x01};
+ expected.Append((1 << 8), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max STR16.
+ value = std::string((1 << 16) - 1, 'x');
+ Serialize(WrapString(value), &result);
+ expected = {ENCODING_TYPE_STR16, 0xff, 0xff};
+ expected.Append((1 << 16) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min STR32.
+ value = std::string((1 << 16), 'x');
+ Serialize(WrapString(value), &result);
+ expected = {ENCODING_TYPE_STR32, 0x00, 0x00, 0x01, 0x00};
+ expected.Append((1 << 16), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, vector) {
+ Payload result;
+ Payload expected;
+ std::vector<uint8_t> value;
+
+ // Min FIXARRAY.
+ value = {};
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXARRAY.
+ value = decltype(value)((1 << 4) - 1, 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MAX};
+ expected.Append((1 << 4) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY16.
+ value = decltype(value)((1 << 4), 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0x10, 0x00};
+ expected.Append((1 << 4), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max ARRAY16.
+ value = decltype(value)((1 << 16) - 1, 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+ expected.Append((1 << 16) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY32.
+ value = decltype(value)((1 << 16), 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+ expected.Append((1 << 16), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, map) {
+ Payload result;
+ Payload expected;
+ std::map<std::uint32_t, std::uint32_t> value;
+
+ // Min FIXMAP.
+ value = {};
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXMAP.
+ value = MakeMap<decltype(value)>((1 << 4) - 1);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MAX};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 4) - 1);
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min MAP16.
+ value = MakeMap<decltype(value)>((1 << 4));
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_MAP16, 0x10, 0x00};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 4));
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max MAP16.
+ value = MakeMap<decltype(value)>((1 << 16) - 1);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_MAP16, 0xff, 0xff};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 16) - 1);
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min MAP32.
+ value = MakeMap<decltype(value)>((1 << 16));
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x01, 0x00};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 16));
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, unordered_map) {
+ Payload result;
+ Payload expected;
+ std::unordered_map<std::uint32_t, std::uint32_t> value;
+
+ // Min FIXMAP.
+ value = {};
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXMAP.
+ value = MakeMap<decltype(value)>((1 << 4) - 1);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MAX};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 4) - 1);
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min MAP16.
+ value = MakeMap<decltype(value)>((1 << 4));
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_MAP16, 0x10, 0x00};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 4));
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max MAP16.
+ value = MakeMap<decltype(value)>((1 << 16) - 1);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_MAP16, 0xff, 0xff};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 16) - 1);
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min MAP32.
+ value = MakeMap<decltype(value)>((1 << 16));
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x01, 0x00};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 16));
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, array) {
+ Payload result;
+ Payload expected;
+
+ // Min FIXARRAY.
+ std::array<std::uint8_t, 0> a0;
+ Serialize(a0, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXARRAY.
+ std::array<std::uint8_t, (1 << 4) - 1> a1;
+ for (auto& element : a1)
+ element = 'x';
+ Serialize(a1, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MAX};
+ expected.Append((1 << 4) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY16.
+ std::array<std::uint8_t, (1 << 4)> a2;
+ for (auto& element : a2)
+ element = 'x';
+ Serialize(a2, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0x10, 0x00};
+ expected.Append((1 << 4), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max ARRAY16.
+ std::array<std::uint8_t, (1 << 16) - 1> a3;
+ for (auto& element : a3)
+ element = 'x';
+ Serialize(a3, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+ expected.Append((1 << 16) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY32.
+ std::array<std::uint8_t, (1 << 16)> a4;
+ for (auto& element : a4)
+ element = 'x';
+ Serialize(a4, &result);
+ expected = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+ expected.Append((1 << 16), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, ArrayWrapper) {
+ Payload result;
+ Payload expected;
+ std::vector<std::uint8_t, DefaultInitializationAllocator<std::uint8_t>> value;
+ ArrayWrapper<std::uint8_t> wrapper;
+
+ // Min FIXARRAY.
+ value = {};
+ Serialize(wrapper, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXARRAY.
+ value = decltype(value)((1 << 4) - 1, 'x');
+ wrapper = decltype(wrapper)(value.data(), value.capacity(), value.size());
+ Serialize(wrapper, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MAX};
+ expected.Append((1 << 4) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY16.
+ value = decltype(value)((1 << 4), 'x');
+ wrapper = decltype(wrapper)(value.data(), value.capacity(), value.size());
+ Serialize(wrapper, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0x10, 0x00};
+ expected.Append((1 << 4), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max ARRAY16.
+ value = decltype(value)((1 << 16) - 1, 'x');
+ wrapper = decltype(wrapper)(value.data(), value.capacity(), value.size());
+ Serialize(wrapper, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+ expected.Append((1 << 16) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY32.
+ value = decltype(value)((1 << 16), 'x');
+ wrapper = decltype(wrapper)(value.data(), value.capacity(), value.size());
+ Serialize(wrapper, &result);
+ expected = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+ expected.Append((1 << 16), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, pair) {
+ Payload result;
+ Payload expected;
+
+ auto p1 = std::make_pair(1, 2);
+ Serialize(p1, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MIN + 2, 1, 2};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ auto p2 = std::make_pair('x', std::string("12345"));
+ Serialize(p2, &result);
+ expected = decltype(expected)({ENCODING_TYPE_FIXARRAY_MIN + 2, 'x',
+ ENCODING_TYPE_FIXSTR_MIN + 5, '1', '2', '3',
+ '4', '5'});
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, tuple) {
+ Payload result;
+ Payload expected;
+
+ // Min FIXARRAY.
+ auto t1 = std::make_tuple();
+ Serialize(t1, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXARRAY.
+ auto t2 = GetNTuple<15>('x');
+ Serialize(t2, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MAX};
+ expected.Append((1 << 4) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY16.
+ auto t3 = GetNTuple<(1 << 4)>('x');
+ Serialize(t3, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0x10, 0x00};
+ expected.Append((1 << 4), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+// Template instantiation depth is an issue for these tests. They are commented
+// out to document the expected behavior, even though tuples of this order are
+// not expected in practice.
+#if 0
+ // Max ARRAY16.
+ auto t4 = GetNTuple<(1 << 16)-1>('x');
+ Serialize(t4, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+ expected.Append((1 << 16)-1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY32.
+ auto t5 = GetNTuple<(1 << 16)>('x');
+ Serialize(t5, &result);
+ expected = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+ expected.Append((1 << 16), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+#endif
+}
+
+// TODO(eieio): More exhaustive testing of type nesting.
+TEST(SerializationTest, NestedTuple) {
+ Payload result;
+ Payload expected;
+
+ auto t1 = std::make_tuple('x', std::make_tuple<int, int>(1, 2));
+ Serialize(t1, &result);
+ expected = decltype(expected)({ENCODING_TYPE_FIXARRAY_MIN + 2, 'x',
+ ENCODING_TYPE_FIXARRAY_MIN + 2, 1, 2});
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ auto t2 = std::make_tuple('x', std::make_tuple<int, int>(1, 2),
+ std::string("0123456789"));
+ Serialize(t2, &result);
+ expected = decltype(expected)({ENCODING_TYPE_FIXARRAY_MIN + 3, 'x',
+ ENCODING_TYPE_FIXARRAY_MIN + 2, 1, 2,
+ ENCODING_TYPE_FIXSTR | 10, '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9'});
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ auto t3 = std::make_tuple(0.0f, std::uint64_t(10ULL),
+ std::vector<char>{'a', 'b', 'c'});
+ Serialize(t3, &result);
+ expected = decltype(expected)(
+ {ENCODING_TYPE_FIXARRAY_MIN + 3, ENCODING_TYPE_FLOAT32,
+ kZeroFloatBytes[0], kZeroFloatBytes[1], kZeroFloatBytes[2],
+ kZeroFloatBytes[3], ENCODING_TYPE_POSITIVE_FIXINT_MIN + 10,
+ ENCODING_TYPE_FIXARRAY_MIN + 3, 'a', 'b', 'c'});
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, NestedMap) {
+ Payload result;
+ Payload expected;
+
+ std::map<int, std::pair<std::string, int>> m1 = {{0, {"a", 2}},
+ {1, {"b", 10}}};
+ Serialize(m1, &result);
+ expected = decltype(expected)(
+ {ENCODING_TYPE_FIXMAP_MIN + 2, 0, ENCODING_TYPE_FIXARRAY_MIN + 2,
+ ENCODING_TYPE_FIXSTR_MIN + 1, 'a', 2, 1, ENCODING_TYPE_FIXARRAY_MIN + 2,
+ ENCODING_TYPE_FIXSTR_MIN + 1, 'b', 10});
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, Serializable) {
+ Payload result;
+ Payload expected;
+
+ TestType t1{10, 0.0, "12345", TestType::Foo::kBaz};
+ Serialize(t1, &result);
+ expected = decltype(expected)(
+ {ENCODING_TYPE_FIXARRAY_MIN + 4, 10, ENCODING_TYPE_FLOAT32,
+ kZeroFloatBytes[0], kZeroFloatBytes[1], kZeroFloatBytes[2],
+ kZeroFloatBytes[3], ENCODING_TYPE_FIXSTR_MIN + 5, '1', '2', '3', '4',
+ '5', ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2});
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ TestTemplateType<LocalHandle> tt{LocalHandle(-1)};
+ Serialize(tt, &result);
+ expected =
+ decltype(expected)({ENCODING_TYPE_FIXARRAY_MIN + 1, ENCODING_TYPE_FIXEXT2,
+ ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0xff, 0xff});
+ EXPECT_EQ(expected, result);
+}
+
+TEST(SerializationTest, Variant) {
+ Payload result;
+ Payload expected;
+
+ Variant<int, bool, float> v;
+
+ // Empty variant.
+ Serialize(v, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_NEGATIVE_FIXINT_MAX,
+ ENCODING_TYPE_NIL};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ v = 10;
+ Serialize(v, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MIN + 1,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN + 0,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN + 10};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ v = true;
+ Serialize(v, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MIN + 1,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1, ENCODING_TYPE_TRUE};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ v = false;
+ Serialize(v, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MIN + 1,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1, ENCODING_TYPE_FALSE};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ v = 1.0f;
+ Serialize(v, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MIN + 1,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2,
+ ENCODING_TYPE_FLOAT32,
+ kOneFloatBytes[0],
+ kOneFloatBytes[1],
+ kOneFloatBytes[2],
+ kOneFloatBytes[3]};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // TODO(eieio): Add more serialization tests for Variant.
+}
+
+TEST(DeserializationTest, bool) {
+ Payload buffer;
+ bool result = false;
+ ErrorType error;
+
+ // True.
+ buffer = {ENCODING_TYPE_TRUE};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(1, result); // Gtest generates warning from bool literals.
+
+ // False.
+ buffer = {ENCODING_TYPE_FALSE};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0, result); // Gtest generates warning from bool literals.
+}
+
+TEST(DeserializationTest, uint8_t) {
+ Payload buffer;
+ std::uint8_t result = 0;
+ ErrorType error;
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127U, result);
+
+ // Min UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffU, result);
+
+ // UINT16 out of range.
+ buffer = {ENCODING_TYPE_UINT16};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_UINT16, error.encoding_type());
+
+ // UINT32 out of range.
+ buffer = {ENCODING_TYPE_UINT32};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_UINT32, error.encoding_type());
+
+ // UINT64 out of range.
+ buffer = {ENCODING_TYPE_UINT64};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_UINT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, uint16_t) {
+ Payload buffer;
+ std::uint16_t result = 0;
+ ErrorType error;
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127U, result);
+
+ // Min UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffU, result);
+
+ // Min UINT16.
+ buffer = {ENCODING_TYPE_UINT16, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT16.
+ buffer = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffffU, result);
+
+ // UINT32 out of range.
+ buffer = {ENCODING_TYPE_UINT32};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_UINT32, error.encoding_type());
+
+ // UINT64 out of range.
+ buffer = {ENCODING_TYPE_UINT64};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_UINT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, uint32_t) {
+ Payload buffer;
+ std::uint32_t result = 0;
+ ErrorType error;
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127U, result);
+
+ // Min UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffU, result);
+
+ // Min UINT16.
+ buffer = {ENCODING_TYPE_UINT16, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT16.
+ buffer = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffffU, result);
+
+ // Min UINT32.
+ buffer = {ENCODING_TYPE_UINT32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT32.
+ buffer = {ENCODING_TYPE_UINT32, 0xff, 0xff, 0xff, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffffffffU, result);
+
+ // UINT64 out of range.
+ buffer = {ENCODING_TYPE_UINT64};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_UINT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, uint64_t) {
+ Payload buffer;
+ std::uint64_t result = 0;
+ ErrorType error;
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127U, result);
+
+ // Min UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffU, result);
+
+ // Min UINT16.
+ buffer = {ENCODING_TYPE_UINT16, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT16.
+ buffer = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffffU, result);
+
+ // Min UINT32.
+ buffer = {ENCODING_TYPE_UINT32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT32.
+ buffer = {ENCODING_TYPE_UINT32, 0xff, 0xff, 0xff, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffffffffU, result);
+
+ // Min UINT64.
+ buffer = {
+ ENCODING_TYPE_UINT64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT64.
+ buffer = {
+ ENCODING_TYPE_UINT64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffffffffffffffffUL, result);
+}
+
+TEST(DeserializationTest, int8_t) {
+ Payload buffer;
+ std::int8_t result = 0;
+ ErrorType error;
+
+ // Min NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-32, result);
+
+ // Max NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-1, result);
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // Min INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-128, result);
+
+ // Max INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // INT16 out of range.
+ buffer = {ENCODING_TYPE_INT16};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_INT16, error.encoding_type());
+
+ // INT32 out of range.
+ buffer = {ENCODING_TYPE_INT32};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_INT32, error.encoding_type());
+
+ // INT64 out of range.
+ buffer = {ENCODING_TYPE_INT64};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_INT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, int16_t) {
+ Payload buffer;
+ std::int16_t result = 0;
+ ErrorType error;
+
+ // Min NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-32, result);
+
+ // Max NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-1, result);
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // Min INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-128, result);
+
+ // Max INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // Min INT16.
+ buffer = {ENCODING_TYPE_INT16, 0x00, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-32768, result);
+
+ // Max INT16.
+ buffer = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(32767, result);
+
+ // INT32 out of range.
+ buffer = {ENCODING_TYPE_INT32};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_INT32, error.encoding_type());
+
+ // INT64 out of range.
+ buffer = {ENCODING_TYPE_INT64};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_INT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, int32_t) {
+ Payload buffer;
+ std::int32_t result = 0;
+ ErrorType error;
+
+ // Min NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-32, result);
+
+ // Max NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-1, result);
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // Min INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-128, result);
+
+ // Max INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // Min INT16.
+ buffer = {ENCODING_TYPE_INT16, 0x00, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-32768, result);
+
+ // Max INT16.
+ buffer = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(32767, result);
+
+ // Min INT32.
+ buffer = {ENCODING_TYPE_INT32, 0x00, 0x00, 0x00, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-2147483648, result);
+
+ // Max INT32.
+ buffer = {ENCODING_TYPE_INT32, 0xff, 0xff, 0xff, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(2147483647, result);
+
+ // INT64 out of range.
+ buffer = {ENCODING_TYPE_INT64};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_INT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, int64_t) {
+ Payload buffer;
+ std::int64_t result = 0;
+ ErrorType error;
+
+ // Min NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-32, result);
+
+ // Max NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-1, result);
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // Min INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-128, result);
+
+ // Max INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // Min INT16.
+ buffer = {ENCODING_TYPE_INT16, 0x00, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-32768, result);
+
+ // Max INT16.
+ buffer = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(32767, result);
+
+ // Min INT32.
+ buffer = {ENCODING_TYPE_INT32, 0x00, 0x00, 0x00, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-2147483648, result);
+
+ // Max INT32.
+ buffer = {ENCODING_TYPE_INT32, 0xff, 0xff, 0xff, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(2147483647, result);
+
+ // Min INT64.
+ buffer = {
+ ENCODING_TYPE_INT64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ // Believe it or not, this is actually the correct way to specify the most
+ // negative signed long long.
+ EXPECT_EQ(-9223372036854775807LL - 1, result);
+
+ // Max INT64.
+ buffer = {
+ ENCODING_TYPE_INT64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(9223372036854775807LL, result);
+}
+
+TEST(DeserializationTest, float) {
+ Payload buffer;
+ float result;
+ ErrorType error;
+
+ // FLOAT32.
+ buffer = {ENCODING_TYPE_FLOAT32, kZeroFloatBytes[0], kZeroFloatBytes[1],
+ kZeroFloatBytes[2], kZeroFloatBytes[3]};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(kZeroFloat, result);
+
+ // FLOAT32.
+ buffer = {ENCODING_TYPE_FLOAT32, kOneFloatBytes[0], kOneFloatBytes[1],
+ kOneFloatBytes[2], kOneFloatBytes[3]};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(kOneFloat, result);
+}
+
+TEST(DeserializationTest, double) {
+ Payload buffer;
+ double result;
+ ErrorType error;
+
+ // FLOAT32.
+ buffer = {ENCODING_TYPE_FLOAT32, kZeroFloatBytes[0], kZeroFloatBytes[1],
+ kZeroFloatBytes[2], kZeroFloatBytes[3]};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(kZeroDouble, result);
+
+ // FLOAT64.
+ buffer = {ENCODING_TYPE_FLOAT64, kZeroDoubleBytes[0], kZeroDoubleBytes[1],
+ kZeroDoubleBytes[2], kZeroDoubleBytes[3], kZeroDoubleBytes[4],
+ kZeroDoubleBytes[5], kZeroDoubleBytes[6], kZeroDoubleBytes[7]};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(kZeroDouble, result);
+
+ // FLOAT32.
+ buffer = {ENCODING_TYPE_FLOAT32, kOneFloatBytes[0], kOneFloatBytes[1],
+ kOneFloatBytes[2], kOneFloatBytes[3]};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(kOneDouble, result);
+
+ // FLOAT64.
+ buffer = {ENCODING_TYPE_FLOAT64, kOneDoubleBytes[0], kOneDoubleBytes[1],
+ kOneDoubleBytes[2], kOneDoubleBytes[3], kOneDoubleBytes[4],
+ kOneDoubleBytes[5], kOneDoubleBytes[6], kOneDoubleBytes[7]};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(kOneDouble, result);
+}
+
+TEST(DeserializationTest, Enum) {
+ Payload buffer;
+ enum Foo { kFoo, kBar, kBaz } result;
+ ErrorType error;
+
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(kBar, result);
+}
+
+TEST(DeserializationTest, EnumClass) {
+ Payload buffer;
+ enum Foo { kFoo, kBar, kBaz } result;
+ ErrorType error;
+
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(Foo::kBaz, result);
+}
+
+TEST(DeserializationTest, LocalHandle) {
+ Payload buffer;
+ LocalHandle result1;
+ LocalHandle result2;
+ ErrorType error;
+
+ buffer = {ENCODING_TYPE_FIXEXT2, ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0, 0};
+ error = Deserialize(&result1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0, result1.Get());
+ result1.Release(); // Don't close fd 0.
+
+ std::tuple<LocalHandle&, LocalHandle&> t1(result1, result2);
+ buffer = decltype(buffer)(
+ {ENCODING_TYPE_FIXARRAY_MIN + 2, ENCODING_TYPE_FIXEXT2,
+ ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0, 0, ENCODING_TYPE_FIXEXT2,
+ ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 1, 0});
+ error = Deserialize(&t1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0, result1.Get());
+ EXPECT_EQ(1, result2.Get());
+ result1.Release(); // Don't close fd 0.
+ result2.Release(); // Don't close fd 1.
+
+ buffer = {ENCODING_TYPE_FIXEXT2, ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0xfe,
+ 0xff};
+ error = Deserialize(&result1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-2, result1.Get());
+}
+
+TEST(DeserializationTest, string) {
+ Payload buffer;
+ std::string result = "";
+ ErrorType error;
+
+ // Min FIXSTR.
+ buffer = {ENCODING_TYPE_FIXSTR_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ("", result);
+
+ // Max FIXSTR.
+ buffer = {ENCODING_TYPE_FIXSTR_MAX};
+ buffer.Append((1 << 5) - 1, 'x');
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::string((1 << 5) - 1, 'x'), result);
+
+ // Min STR8.
+ buffer = {ENCODING_TYPE_STR8, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ("", result);
+
+ // Max STR8.
+ buffer = {ENCODING_TYPE_STR8, 0xff};
+ buffer.Append(0xff, 'x');
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::string(0xff, 'x'), result);
+
+ // Min STR16.
+ buffer = {ENCODING_TYPE_STR16, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ("", result);
+
+ // Max STR16.
+ buffer = {ENCODING_TYPE_STR16, 0xff, 0xff};
+ buffer.Append(0xffff, 'x');
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::string(0xffff, 'x'), result);
+
+ // Min STR32.
+ buffer = {ENCODING_TYPE_STR32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ("", result);
+
+ // Test STR32 with max STR16 + 1 bytes. It's not practical to test max
+ // STR32.
+ buffer = {ENCODING_TYPE_STR32, 0x00, 0x00, 0x01, 0x00};
+ buffer.Append(0x10000, 'x');
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::string(0x10000, 'x'), result);
+}
+
+TEST(DeserializationTest, vector) {
+ Payload buffer;
+ std::vector<std::uint8_t, DefaultInitializationAllocator<std::uint8_t>>
+ result;
+ Payload expected;
+ ErrorType error;
+
+ // Min FIXARRAY.
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Max FIXARRAY.
+ buffer = {ENCODING_TYPE_FIXARRAY_MAX};
+ buffer.Append((1 << 4) - 1, 1);
+ error = Deserialize(&result, &buffer);
+ expected = decltype(expected)((1 << 4) - 1, 1);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Min ARRAY16.
+ buffer = {ENCODING_TYPE_ARRAY16, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Max ARRAY16.
+ buffer = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+ buffer.Append(0xffff, 1);
+ error = Deserialize(&result, &buffer);
+ expected = decltype(expected)(0xffff, 1);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Min ARRAY32.
+ buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // ARRAY32 with max ARRAY16 + 1. It's not practical to test max ARRAY32.
+ buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+ buffer.Append(0x10000, 1);
+ error = Deserialize(&result, &buffer);
+ expected = decltype(expected)(0x10000, 1);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+}
+
+TEST(DeserializationTest, map) {
+ Payload buffer;
+ std::map<std::uint32_t, std::uint32_t> result;
+ std::map<std::uint32_t, std::uint32_t> expected;
+ ErrorType error;
+
+ // Min FIXMAP.
+ buffer = {ENCODING_TYPE_FIXMAP_MIN};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Size mismatch.
+ buffer = {ENCODING_TYPE_FIXMAP_MIN + 1};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::INSUFFICIENT_BUFFER, error);
+
+ // Max FIXMAP.
+ buffer = {ENCODING_TYPE_FIXMAP_MAX};
+ InsertKeyValue<decltype(result)>(&buffer, (1 << 4) - 1);
+ error = Deserialize(&result, &buffer);
+ expected = MakeMap<decltype(expected)>((1 << 4) - 1);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error) << std::string(error);
+ EXPECT_EQ(expected, result);
+
+ // Min MAP16.
+ buffer = {ENCODING_TYPE_MAP16, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Max MAP16.
+ buffer = {ENCODING_TYPE_MAP16, 0xff, 0xff};
+ InsertKeyValue<decltype(result)>(&buffer, (1 << 16) - 1);
+ error = Deserialize(&result, &buffer);
+ expected = MakeMap<decltype(expected)>((1 << 16) - 1);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Min MAP32.
+ buffer = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // MAP32 with max MAP16 + 1. It's not practical to test max MAP32.
+ buffer = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x01, 0x00};
+ InsertKeyValue<decltype(result)>(&buffer, (1 << 16));
+ error = Deserialize(&result, &buffer);
+ expected = MakeMap<decltype(expected)>((1 << 16));
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+}
+
+TEST(DeserializationTest, unordered_map) {
+ Payload buffer;
+ std::unordered_map<std::uint32_t, std::uint32_t> result;
+ std::unordered_map<std::uint32_t, std::uint32_t> expected;
+ ErrorType error;
+
+ // Min FIXMAP.
+ buffer = {ENCODING_TYPE_FIXMAP_MIN};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Size mismatch.
+ buffer = {ENCODING_TYPE_FIXMAP_MIN + 1};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::INSUFFICIENT_BUFFER, error);
+
+ // Max FIXMAP.
+ buffer = {ENCODING_TYPE_FIXMAP_MAX};
+ InsertKeyValue<decltype(result)>(&buffer, (1 << 4) - 1);
+ error = Deserialize(&result, &buffer);
+ expected = MakeMap<decltype(expected)>((1 << 4) - 1);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Min MAP16.
+ buffer = {ENCODING_TYPE_MAP16, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Max MAP16.
+ buffer = {ENCODING_TYPE_MAP16, 0xff, 0xff};
+ InsertKeyValue<decltype(result)>(&buffer, (1 << 16) - 1);
+ error = Deserialize(&result, &buffer);
+ expected = MakeMap<decltype(expected)>((1 << 16) - 1);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Min MAP32.
+ buffer = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // MAP32 with max MAP16 + 1. It's not practical to test max MAP32.
+ buffer = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x01, 0x00};
+ InsertKeyValue<decltype(result)>(&buffer, (1 << 16));
+ error = Deserialize(&result, &buffer);
+ expected = MakeMap<decltype(expected)>((1 << 16));
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+}
+
+TEST(DeserializationTest, array) {
+ Payload buffer;
+ ErrorType error;
+
+ // Min FIXARRAY.
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN};
+ std::array<std::uint8_t, 0> a0;
+ error = Deserialize(&a0, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+
+ // Size mismatch.
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN + 1};
+ error = Deserialize(&a0, &buffer);
+ EXPECT_EQ(ErrorCode::INSUFFICIENT_DESTINATION_SIZE, error);
+
+ // Max FIXARRAY.
+ buffer = {ENCODING_TYPE_FIXARRAY_MAX};
+ buffer.Append((1 << 4) - 1, 'x');
+ std::array<std::uint8_t, (1 << 4) - 1> a1, expected1;
+ for (auto& element : expected1)
+ element = 'x';
+ error = Deserialize(&a1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected1, a1);
+
+ // Min ARRAY16.
+ buffer = {ENCODING_TYPE_ARRAY16, 0x00, 0x00};
+ error = Deserialize(&a0, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+
+ // Max ARRAY16.
+ buffer = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+ buffer.Append((1 << 16) - 1, 'x');
+ std::array<std::uint8_t, (1 << 16) - 1> a3, expected3;
+ for (auto& element : expected3)
+ element = 'x';
+ error = Deserialize(&a3, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected3, a3);
+
+ // Min ARRAY32.
+ buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&a0, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+
+ // ARRAY32 with max ARRAY16 + 1. It's not practical to test max ARRAY32.
+ buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+ buffer.Append((1 << 16), 'x');
+ std::array<std::uint8_t, (1 << 16)> a4, expected4;
+ for (auto& element : expected4)
+ element = 'x';
+ error = Deserialize(&a4, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected4, a4);
+}
+
+TEST(DeserializationTest, ArrayWrapper) {
+ Payload buffer;
+ std::vector<std::uint8_t, DefaultInitializationAllocator<std::uint8_t>>
+ result;
+ std::vector<std::uint8_t, DefaultInitializationAllocator<std::uint8_t>>
+ expected;
+ ErrorType error;
+
+ result.reserve(0x10000);
+ ArrayWrapper<std::uint8_t> wrapper(result.data(), result.capacity());
+
+ // Min FIXARRAY.
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN};
+ error = Deserialize(&wrapper, &buffer);
+ expected = {};
+ result.resize(wrapper.size());
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Max FIXARRAY.
+ buffer = {ENCODING_TYPE_FIXARRAY_MAX};
+ buffer.Append((1 << 4) - 1, 1);
+ error = Deserialize(&wrapper, &buffer);
+ expected = decltype(expected)((1 << 4) - 1, 1);
+ result.resize(wrapper.size());
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Min ARRAY16.
+ buffer = {ENCODING_TYPE_ARRAY16, 0x00, 0x00};
+ error = Deserialize(&wrapper, &buffer);
+ expected = {};
+ result.resize(wrapper.size());
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Max ARRAY16.
+ buffer = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+ buffer.Append(0xffff, 1);
+ error = Deserialize(&wrapper, &buffer);
+ expected = decltype(expected)(0xffff, 1);
+ result.resize(wrapper.size());
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Min ARRAY32.
+ buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&wrapper, &buffer);
+ expected = {};
+ result.resize(wrapper.size());
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // ARRAY32 with max ARRAY16 + 1. It's not practical to test max ARRAY32.
+ buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+ buffer.Append(0x10000, 1);
+ error = Deserialize(&wrapper, &buffer);
+ expected = decltype(expected)(0x10000, 1);
+ result.resize(wrapper.size());
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+}
+
+TEST(DeserializationTest, pair) {
+ Payload buffer;
+ ErrorType error;
+
+ std::pair<int, int> p1;
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN + 2, 1, 2};
+ error = Deserialize(&p1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::make_pair(1, 2), p1);
+}
+
+TEST(DeserializationTest, tuple) {
+ Payload buffer;
+ ErrorType error;
+
+ // Min FIXARRAY.
+ std::tuple<> t1;
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN};
+ error = Deserialize(&t1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::make_tuple(), t1); // Superfluous.
+
+ // Max FIXARRAY.
+ auto t2 = GetNTuple<15, int>(0);
+ buffer = {ENCODING_TYPE_FIXARRAY_MAX};
+ buffer.Append((1 << 4) - 1, 1);
+ error = Deserialize(&t2, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ((GetNTuple<15, int>(1)), t2);
+
+ // Min ARRAY16.
+ // Using t1 above.
+ buffer = {ENCODING_TYPE_ARRAY16, 0x00, 0x00};
+ error = Deserialize(&t1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::make_tuple(), t1);
+
+ // ARRAY16 at Max FIXARRAY + 1
+ auto t3 = GetNTuple<(1 << 4), int>(0);
+ buffer = {ENCODING_TYPE_ARRAY16, 0x10, 0x00};
+ buffer.Append((1 << 4), 1);
+ error = Deserialize(&t3, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ((GetNTuple<(1 << 4), int>(1)), t3);
+
+ // Min ARRAY32.
+ // Using t1 from above.
+ buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&t1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::make_tuple(), t1);
+
+ // ARRAY32 at Max FIXARRAY + 1
+ auto t4 = GetNTuple<(1 << 4), int>(0);
+ buffer = {ENCODING_TYPE_ARRAY32, 0x10, 0x00, 0x00, 0x00};
+ buffer.Append((1 << 4), 1);
+ error = Deserialize(&t4, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ((GetNTuple<(1 << 4), int>(1)), t4);
+
+ // Template instantiation depth is an issue for tuples with large numbers of
+ // elements. As these are not expected in practice, the limits of ARRAY16
+ // and ARRAY32 are not tested.
+}
+
+TEST(DeserializationTest, Serializable) {
+ Payload buffer;
+ ErrorType error;
+
+ buffer = decltype(buffer)(
+ {ENCODING_TYPE_FIXARRAY_MIN + 4, 10, ENCODING_TYPE_FLOAT32,
+ kZeroFloatBytes[0], kZeroFloatBytes[1], kZeroFloatBytes[2],
+ kZeroFloatBytes[3], ENCODING_TYPE_FIXSTR_MIN + 5, '1', '2', '3', '4',
+ '5', ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1});
+ TestType t1;
+ error = Deserialize(&t1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(TestType(10, 0.f, "12345", TestType::Foo::kBar), t1);
+
+ buffer =
+ decltype(buffer)({ENCODING_TYPE_FIXARRAY_MIN + 1, ENCODING_TYPE_FIXEXT2,
+ ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0xff, 0xff});
+ TestTemplateType<LocalHandle> tt;
+ error = Deserialize(&tt, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(TestTemplateType<LocalHandle>(LocalHandle(-1)), tt);
+}
+
+TEST(DeserializationTest, Variant) {
+ Payload buffer;
+ ErrorType error;
+
+ Variant<int, bool, float> v;
+
+ buffer = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_NEGATIVE_FIXINT_MAX,
+ ENCODING_TYPE_NIL};
+ error = Deserialize(&v, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_TRUE(v.empty());
+
+ buffer = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_POSITIVE_FIXINT_MIN + 0,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN + 10};
+ error = Deserialize(&v, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ ASSERT_TRUE(v.is<int>());
+ EXPECT_EQ(10, std::get<int>(v));
+
+ buffer = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1,
+ ENCODING_TYPE_TRUE};
+ error = Deserialize(&v, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ ASSERT_TRUE(v.is<bool>());
+ EXPECT_EQ(true, std::get<bool>(v));
+
+ buffer = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1,
+ ENCODING_TYPE_FALSE};
+ error = Deserialize(&v, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ ASSERT_TRUE(v.is<bool>());
+ EXPECT_EQ(false, std::get<bool>(v));
+
+ buffer = {ENCODING_TYPE_FIXMAP_MIN + 1,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2,
+ ENCODING_TYPE_FLOAT32,
+ kOneFloatBytes[0],
+ kOneFloatBytes[1],
+ kOneFloatBytes[2],
+ kOneFloatBytes[3]};
+ error = Deserialize(&v, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ ASSERT_TRUE(v.is<float>());
+ EXPECT_FLOAT_EQ(1.0, std::get<float>(v));
+
+ // TODO(eieio): Add more deserialization tests for Variant.
+}
+
+TEST(DeserializationTest, ErrorType) {
+ Payload buffer;
+ ErrorType error;
+
+ std::uint8_t u8;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&u8, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::uint16_t u16;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&u16, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::uint32_t u32;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&u32, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::uint64_t u64;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&u64, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::int8_t i8;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&i8, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::int16_t i16;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&i16, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::int32_t i32;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&i32, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::int64_t i64;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&i64, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::string s;
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT};
+ error = Deserialize(&s, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_STRING, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_POSITIVE_FIXINT, error.encoding_type());
+
+ std::vector<std::uint8_t> v;
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT};
+ error = Deserialize(&v, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_ARRAY, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_POSITIVE_FIXINT, error.encoding_type());
+
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN + 1, ENCODING_TYPE_STR8};
+ error = Deserialize(&v, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN + 2, 0, 1};
+ std::tuple<int> t;
+ error = Deserialize(&t, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_TYPE_SIZE, error);
+
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN + 3, 0, 1, 2};
+ std::pair<int, int> p;
+ error = Deserialize(&p, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_TYPE_SIZE, error);
+}
diff --git a/libs/vr/libpdx/service.cpp b/libs/vr/libpdx/service.cpp
new file mode 100644
index 0000000..0053af8
--- /dev/null
+++ b/libs/vr/libpdx/service.cpp
@@ -0,0 +1,680 @@
+#define LOG_TAG "ServiceFramework"
+#include "pdx/service.h"
+
+#include <cutils/log.h>
+#include <fcntl.h>
+#include <utils/misc.h>
+
+#include <algorithm>
+#include <cstdint>
+
+#include <pdx/trace.h>
+#include "errno_guard.h"
+
+#define TRACE 0
+
+namespace android {
+namespace pdx {
+
+std::shared_ptr<Channel> Channel::GetFromMessageInfo(const MessageInfo& info) {
+ return info.channel ? info.channel->shared_from_this()
+ : std::shared_ptr<Channel>();
+}
+
+Message::Message() : replied_(true) {}
+
+Message::Message(const MessageInfo& info)
+ : service_{Service::GetFromMessageInfo(info)},
+ channel_{Channel::GetFromMessageInfo(info)},
+ info_{info},
+ replied_{IsImpulse()} {
+ auto svc = service_.lock();
+ if (svc)
+ state_ = svc->endpoint()->AllocateMessageState();
+}
+
+// C++11 specifies the move semantics for shared_ptr but not weak_ptr. This
+// means we have to manually implement the desired move semantics for Message.
+Message::Message(Message&& other) { *this = std::move(other); }
+
+Message& Message::operator=(Message&& other) {
+ Destroy();
+ auto base = reinterpret_cast<std::uint8_t*>(&info_);
+ std::fill(&base[0], &base[sizeof(info_)], 0);
+ replied_ = true;
+ std::swap(service_, other.service_);
+ std::swap(channel_, other.channel_);
+ std::swap(info_, other.info_);
+ std::swap(state_, other.state_);
+ std::swap(replied_, other.replied_);
+ return *this;
+}
+
+Message::~Message() { Destroy(); }
+
+void Message::Destroy() {
+ auto svc = service_.lock();
+ if (svc) {
+ if (!replied_) {
+ ALOGE(
+ "ERROR: Service \"%s\" failed to reply to message: op=%d pid=%d "
+ "cid=%d\n",
+ svc->name_.c_str(), info_.op, info_.pid, info_.cid);
+ svc->endpoint()->DefaultHandleMessage(info_);
+ }
+ svc->endpoint()->FreeMessageState(state_);
+ }
+ state_ = nullptr;
+ service_.reset();
+ channel_.reset();
+}
+
+const std::uint8_t* Message::ImpulseBegin() const {
+ return reinterpret_cast<const std::uint8_t*>(info_.impulse);
+}
+
+const std::uint8_t* Message::ImpulseEnd() const {
+ return ImpulseBegin() + (IsImpulse() ? GetSendLength() : 0);
+}
+
+ssize_t Message::ReadVector(const struct iovec* vector, size_t vector_length) {
+ PDX_TRACE_NAME("Message::ReadVector");
+ if (auto svc = service_.lock()) {
+ ErrnoGuard errno_guard;
+ const ssize_t ret =
+ svc->endpoint()->ReadMessageData(this, vector, vector_length);
+ return ReturnCodeOrError(ret);
+ } else {
+ return -ESHUTDOWN;
+ }
+}
+
+ssize_t Message::Read(void* buffer, size_t length) {
+ PDX_TRACE_NAME("Message::Read");
+ if (auto svc = service_.lock()) {
+ ErrnoGuard errno_guard;
+ const struct iovec vector = {buffer, length};
+ const ssize_t ret = svc->endpoint()->ReadMessageData(this, &vector, 1);
+ return ReturnCodeOrError(ret);
+ } else {
+ return -ESHUTDOWN;
+ }
+}
+
+ssize_t Message::WriteVector(const struct iovec* vector, size_t vector_length) {
+ PDX_TRACE_NAME("Message::WriteVector");
+ if (auto svc = service_.lock()) {
+ ErrnoGuard errno_guard;
+ const ssize_t ret =
+ svc->endpoint()->WriteMessageData(this, vector, vector_length);
+ return ReturnCodeOrError(ret);
+ } else {
+ return -ESHUTDOWN;
+ }
+}
+
+ssize_t Message::Write(const void* buffer, size_t length) {
+ PDX_TRACE_NAME("Message::Write");
+ if (auto svc = service_.lock()) {
+ ErrnoGuard errno_guard;
+ const struct iovec vector = {const_cast<void*>(buffer), length};
+ const ssize_t ret = svc->endpoint()->WriteMessageData(this, &vector, 1);
+ return ReturnCodeOrError(ret);
+ } else {
+ return -ESHUTDOWN;
+ }
+}
+
+FileReference Message::PushFileHandle(const LocalHandle& handle) {
+ PDX_TRACE_NAME("Message::PushFileHandle");
+ if (auto svc = service_.lock()) {
+ ErrnoGuard errno_guard;
+ return ReturnCodeOrError(svc->endpoint()->PushFileHandle(this, handle));
+ } else {
+ return -ESHUTDOWN;
+ }
+}
+
+FileReference Message::PushFileHandle(const BorrowedHandle& handle) {
+ PDX_TRACE_NAME("Message::PushFileHandle");
+ if (auto svc = service_.lock()) {
+ ErrnoGuard errno_guard;
+ return ReturnCodeOrError(svc->endpoint()->PushFileHandle(this, handle));
+ } else {
+ return -ESHUTDOWN;
+ }
+}
+
+FileReference Message::PushFileHandle(const RemoteHandle& handle) {
+ PDX_TRACE_NAME("Message::PushFileHandle");
+ if (auto svc = service_.lock()) {
+ ErrnoGuard errno_guard;
+ return ReturnCodeOrError(svc->endpoint()->PushFileHandle(this, handle));
+ } else {
+ return -ESHUTDOWN;
+ }
+}
+
+ChannelReference Message::PushChannelHandle(const LocalChannelHandle& handle) {
+ PDX_TRACE_NAME("Message::PushChannelHandle");
+ if (auto svc = service_.lock()) {
+ ErrnoGuard errno_guard;
+ return ReturnCodeOrError(svc->endpoint()->PushChannelHandle(this, handle));
+ } else {
+ return -ESHUTDOWN;
+ }
+}
+
+ChannelReference Message::PushChannelHandle(
+ const BorrowedChannelHandle& handle) {
+ PDX_TRACE_NAME("Message::PushChannelHandle");
+ if (auto svc = service_.lock()) {
+ ErrnoGuard errno_guard;
+ return ReturnCodeOrError(svc->endpoint()->PushChannelHandle(this, handle));
+ } else {
+ return -ESHUTDOWN;
+ }
+}
+
+ChannelReference Message::PushChannelHandle(const RemoteChannelHandle& handle) {
+ PDX_TRACE_NAME("Message::PushChannelHandle");
+ if (auto svc = service_.lock()) {
+ ErrnoGuard errno_guard;
+ return ReturnCodeOrError(svc->endpoint()->PushChannelHandle(this, handle));
+ } else {
+ return -ESHUTDOWN;
+ }
+}
+
+bool Message::GetFileHandle(FileReference ref, LocalHandle* handle) {
+ PDX_TRACE_NAME("Message::GetFileHandle");
+ auto svc = service_.lock();
+ if (!svc)
+ return false;
+
+ if (ref >= 0) {
+ ErrnoGuard errno_guard;
+ *handle = svc->endpoint()->GetFileHandle(this, ref);
+ if (!handle->IsValid())
+ return false;
+ } else {
+ *handle = LocalHandle{ref};
+ }
+ return true;
+}
+
+bool Message::GetChannelHandle(ChannelReference ref,
+ LocalChannelHandle* handle) {
+ PDX_TRACE_NAME("Message::GetChannelHandle");
+ auto svc = service_.lock();
+ if (!svc)
+ return false;
+
+ if (ref >= 0) {
+ ErrnoGuard errno_guard;
+ *handle = svc->endpoint()->GetChannelHandle(this, ref);
+ if (!handle->valid())
+ return false;
+ } else {
+ *handle = LocalChannelHandle{nullptr, ref};
+ }
+ return true;
+}
+
+int Message::Reply(int return_code) {
+ PDX_TRACE_NAME("Message::Reply");
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ ErrnoGuard errno_guard;
+ const int ret = svc->endpoint()->MessageReply(this, return_code);
+ replied_ = ret == 0;
+ return ReturnCodeOrError(ret);
+ } else {
+ return -EINVAL;
+ }
+}
+
+int Message::ReplyFileDescriptor(unsigned int fd) {
+ PDX_TRACE_NAME("Message::ReplyFileDescriptor");
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ ErrnoGuard errno_guard;
+ const int ret = svc->endpoint()->MessageReplyFd(this, fd);
+ replied_ = ret == 0;
+ return ReturnCodeOrError(ret);
+ } else {
+ return -EINVAL;
+ }
+}
+
+int Message::ReplyError(unsigned error) {
+ PDX_TRACE_NAME("Message::ReplyError");
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ ErrnoGuard errno_guard;
+ const int ret = svc->endpoint()->MessageReply(this, -error);
+ replied_ = ret == 0;
+ return ReturnCodeOrError(ret);
+ } else {
+ return -EINVAL;
+ }
+}
+
+int Message::Reply(const LocalHandle& handle) {
+ PDX_TRACE_NAME("Message::ReplyFileHandle");
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ ErrnoGuard errno_guard;
+ int ret;
+
+ if (handle)
+ ret = svc->endpoint()->MessageReplyFd(this, handle.Get());
+ else
+ ret = svc->endpoint()->MessageReply(this, handle.Get());
+
+ replied_ = ret == 0;
+ return ReturnCodeOrError(ret);
+ } else {
+ return -EINVAL;
+ }
+}
+
+int Message::Reply(const BorrowedHandle& handle) {
+ PDX_TRACE_NAME("Message::ReplyFileHandle");
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ ErrnoGuard errno_guard;
+ int ret;
+
+ if (handle)
+ ret = svc->endpoint()->MessageReplyFd(this, handle.Get());
+ else
+ ret = svc->endpoint()->MessageReply(this, handle.Get());
+
+ replied_ = ret == 0;
+ return ReturnCodeOrError(ret);
+ } else {
+ return -EINVAL;
+ }
+}
+
+int Message::Reply(const RemoteHandle& handle) {
+ PDX_TRACE_NAME("Message::ReplyFileHandle");
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ ErrnoGuard errno_guard;
+ const int ret = svc->endpoint()->MessageReply(this, handle.Get());
+ replied_ = ret == 0;
+ return ReturnCodeOrError(ret);
+ } else {
+ return -EINVAL;
+ }
+}
+
+int Message::Reply(const LocalChannelHandle& handle) {
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ ErrnoGuard errno_guard;
+ const int ret = svc->endpoint()->MessageReplyChannelHandle(this, handle);
+ replied_ = ret == 0;
+ return ReturnCodeOrError(ret);
+ } else {
+ return -EINVAL;
+ }
+}
+
+int Message::Reply(const BorrowedChannelHandle& handle) {
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ ErrnoGuard errno_guard;
+ const int ret = svc->endpoint()->MessageReplyChannelHandle(this, handle);
+ replied_ = ret == 0;
+ return ReturnCodeOrError(ret);
+ } else {
+ return -EINVAL;
+ }
+}
+
+int Message::Reply(const RemoteChannelHandle& handle) {
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ ErrnoGuard errno_guard;
+ const int ret = svc->endpoint()->MessageReplyChannelHandle(this, handle);
+ replied_ = ret == 0;
+ return ReturnCodeOrError(ret);
+ } else {
+ return -EINVAL;
+ }
+}
+
+int Message::ModifyChannelEvents(int clear_mask, int set_mask) {
+ PDX_TRACE_NAME("Message::ModifyChannelEvents");
+ if (auto svc = service_.lock()) {
+ ErrnoGuard errno_guard;
+ const int ret =
+ svc->endpoint()->ModifyChannelEvents(info_.cid, clear_mask, set_mask);
+ return ReturnCodeOrError(ret);
+ } else {
+ return -ESHUTDOWN;
+ }
+}
+
+Status<RemoteChannelHandle> Message::PushChannel(
+ int flags, const std::shared_ptr<Channel>& channel, int* channel_id) {
+ PDX_TRACE_NAME("Message::PushChannel");
+ if (auto svc = service_.lock()) {
+ return svc->PushChannel(this, flags, channel, channel_id);
+ } else {
+ return ErrorStatus(ESHUTDOWN);
+ }
+}
+
+Status<RemoteChannelHandle> Message::PushChannel(
+ Service* service, int flags, const std::shared_ptr<Channel>& channel,
+ int* channel_id) {
+ PDX_TRACE_NAME("Message::PushChannel");
+ return service->PushChannel(this, flags, channel, channel_id);
+}
+
+Status<int> Message::CheckChannel(ChannelReference ref,
+ std::shared_ptr<Channel>* channel) const {
+ PDX_TRACE_NAME("Message::CheckChannel");
+ if (auto svc = service_.lock()) {
+ return svc->CheckChannel(this, ref, channel);
+ } else {
+ return ErrorStatus(ESHUTDOWN);
+ }
+}
+
+Status<int> Message::CheckChannel(const Service* service, ChannelReference ref,
+ std::shared_ptr<Channel>* channel) const {
+ PDX_TRACE_NAME("Message::CheckChannel");
+ return service->CheckChannel(this, ref, channel);
+}
+
+pid_t Message::GetProcessId() const { return info_.pid; }
+
+pid_t Message::GetThreadId() const { return info_.tid; }
+
+uid_t Message::GetEffectiveUserId() const { return info_.euid; }
+
+gid_t Message::GetEffectiveGroupId() const { return info_.egid; }
+
+int Message::GetChannelId() const { return info_.cid; }
+
+int Message::GetMessageId() const { return info_.mid; }
+
+int Message::GetOp() const { return info_.op; }
+
+int Message::GetFlags() const { return info_.flags; }
+
+size_t Message::GetSendLength() const { return info_.send_len; }
+
+size_t Message::GetReceiveLength() const { return info_.recv_len; }
+
+size_t Message::GetFileDescriptorCount() const { return info_.fd_count; }
+
+std::shared_ptr<Channel> Message::GetChannel() const { return channel_.lock(); }
+
+void Message::SetChannel(const std::shared_ptr<Channel>& chan) {
+ channel_ = chan;
+
+ if (auto svc = service_.lock())
+ svc->SetChannel(info_.cid, chan);
+}
+
+std::shared_ptr<Service> Message::GetService() const { return service_.lock(); }
+
+const MessageInfo& Message::GetInfo() const { return info_; }
+
+Service::Service(const std::string& name, std::unique_ptr<Endpoint> endpoint)
+ : name_(name), endpoint_{std::move(endpoint)} {
+ if (!endpoint_)
+ return;
+
+ const int ret = endpoint_->SetService(this);
+ ALOGE_IF(ret < 0, "Failed to set service context because: %s",
+ strerror(-ret));
+}
+
+Service::~Service() {
+ if (endpoint_) {
+ const int ret = endpoint_->SetService(nullptr);
+ ALOGE_IF(ret < 0, "Failed to clear service context because: %s",
+ strerror(-ret));
+ }
+}
+
+std::shared_ptr<Service> Service::GetFromMessageInfo(const MessageInfo& info) {
+ return info.service ? info.service->shared_from_this()
+ : std::shared_ptr<Service>();
+}
+
+bool Service::IsInitialized() const { return endpoint_.get() != nullptr; }
+
+std::shared_ptr<Channel> Service::OnChannelOpen(Message& /*message*/) {
+ return nullptr;
+}
+
+void Service::OnChannelClose(Message& /*message*/,
+ const std::shared_ptr<Channel>& /*channel*/) {}
+
+int Service::SetChannel(int channel_id,
+ const std::shared_ptr<Channel>& channel) {
+ PDX_TRACE_NAME("Service::SetChannel");
+ ErrnoGuard errno_guard;
+ std::lock_guard<std::mutex> autolock(channels_mutex_);
+
+ const int ret = endpoint_->SetChannel(channel_id, channel.get());
+ if (ret == -1) {
+ ALOGE("%s::SetChannel: Failed to set channel context: %s\n", name_.c_str(),
+ strerror(errno));
+
+ // It's possible someone mucked with things behind our back by calling the C
+ // API directly. Since we know the channel id isn't valid, make sure we
+ // don't have it in the channels map.
+ if (errno == ENOENT)
+ channels_.erase(channel_id);
+
+ return ReturnCodeOrError(ret);
+ }
+
+ if (channel != nullptr)
+ channels_[channel_id] = channel;
+ else
+ channels_.erase(channel_id);
+
+ return ret;
+}
+
+std::shared_ptr<Channel> Service::GetChannel(int channel_id) const {
+ PDX_TRACE_NAME("Service::GetChannel");
+ std::lock_guard<std::mutex> autolock(channels_mutex_);
+
+ auto search = channels_.find(channel_id);
+ if (search != channels_.end())
+ return search->second;
+ else
+ return nullptr;
+}
+
+int Service::CloseChannel(int channel_id) {
+ PDX_TRACE_NAME("Service::CloseChannel");
+ ErrnoGuard errno_guard;
+ std::lock_guard<std::mutex> autolock(channels_mutex_);
+
+ const int ret = endpoint_->CloseChannel(channel_id);
+
+ // Always erase the map entry, in case someone mucked with things behind our
+ // back using the C API directly.
+ channels_.erase(channel_id);
+
+ return ReturnCodeOrError(ret);
+}
+
+int Service::ModifyChannelEvents(int channel_id, int clear_mask, int set_mask) {
+ PDX_TRACE_NAME("Service::ModifyChannelEvents");
+ return endpoint_->ModifyChannelEvents(channel_id, clear_mask, set_mask);
+}
+
+Status<RemoteChannelHandle> Service::PushChannel(
+ Message* message, int flags, const std::shared_ptr<Channel>& channel,
+ int* channel_id) {
+ PDX_TRACE_NAME("Service::PushChannel");
+ ErrnoGuard errno_guard;
+
+ std::lock_guard<std::mutex> autolock(channels_mutex_);
+
+ int channel_id_temp = -1;
+ Status<RemoteChannelHandle> ret =
+ endpoint_->PushChannel(message, flags, channel.get(), &channel_id_temp);
+ ALOGE_IF(!ret.ok(), "%s::PushChannel: Failed to push channel: %s",
+ name_.c_str(), strerror(ret.error()));
+
+ if (channel && channel_id_temp != -1)
+ channels_[channel_id_temp] = channel;
+ if (channel_id)
+ *channel_id = channel_id_temp;
+
+ return ret;
+}
+
+Status<int> Service::CheckChannel(const Message* message, ChannelReference ref,
+ std::shared_ptr<Channel>* channel) const {
+ PDX_TRACE_NAME("Service::CheckChannel");
+ ErrnoGuard errno_guard;
+
+ // Synchronization to maintain consistency between the kernel's channel
+ // context pointer and the userspace channels_ map. Other threads may attempt
+ // to modify the map at the same time, which could cause the channel context
+ // pointer returned by the kernel to be invalid.
+ std::lock_guard<std::mutex> autolock(channels_mutex_);
+
+ Channel* channel_context = nullptr;
+ Status<int> ret = endpoint_->CheckChannel(
+ message, ref, channel ? &channel_context : nullptr);
+ if (ret && channel) {
+ if (channel_context)
+ *channel = channel_context->shared_from_this();
+ else
+ *channel = nullptr;
+ }
+
+ return ret;
+}
+
+std::string Service::DumpState(size_t /*max_length*/) { return ""; }
+
+int Service::HandleMessage(Message& message) {
+ return DefaultHandleMessage(message);
+}
+
+void Service::HandleImpulse(Message& /*impulse*/) {}
+
+bool Service::HandleSystemMessage(Message& message) {
+ const MessageInfo& info = message.GetInfo();
+
+ switch (info.op) {
+ case opcodes::CHANNEL_OPEN: {
+ ALOGD("%s::OnChannelOpen: pid=%d cid=%d\n", name_.c_str(), info.pid,
+ info.cid);
+ message.SetChannel(OnChannelOpen(message));
+ message.Reply(0);
+ return true;
+ }
+
+ case opcodes::CHANNEL_CLOSE: {
+ ALOGD("%s::OnChannelClose: pid=%d cid=%d\n", name_.c_str(), info.pid,
+ info.cid);
+ OnChannelClose(message, Channel::GetFromMessageInfo(info));
+ message.SetChannel(nullptr);
+ message.Reply(0);
+ return true;
+ }
+
+ case opcodes::REPORT_SYSPROP_CHANGE:
+ ALOGD("%s:REPORT_SYSPROP_CHANGE: pid=%d cid=%d\n", name_.c_str(),
+ info.pid, info.cid);
+ OnSysPropChange();
+ android::report_sysprop_change();
+ message.Reply(0);
+ return true;
+
+ case opcodes::DUMP_STATE: {
+ ALOGD("%s:DUMP_STATE: pid=%d cid=%d\n", name_.c_str(), info.pid,
+ info.cid);
+ auto response = DumpState(message.GetReceiveLength());
+ const size_t response_size = response.size() < message.GetReceiveLength()
+ ? response.size()
+ : message.GetReceiveLength();
+ const ssize_t bytes_written =
+ message.Write(response.data(), response_size);
+ if (bytes_written < static_cast<ssize_t>(response_size))
+ message.ReplyError(EIO);
+ else
+ message.Reply(bytes_written);
+ return true;
+ }
+
+ default:
+ return false;
+ }
+}
+
+int Service::DefaultHandleMessage(Message& message) {
+ const MessageInfo& info = message.GetInfo();
+
+ ALOGD_IF(TRACE, "Service::DefaultHandleMessage: pid=%d cid=%d op=%d\n",
+ info.pid, info.cid, info.op);
+
+ switch (info.op) {
+ case opcodes::CHANNEL_OPEN:
+ case opcodes::CHANNEL_CLOSE:
+ case opcodes::REPORT_SYSPROP_CHANGE:
+ case opcodes::DUMP_STATE:
+ HandleSystemMessage(message);
+ return 0;
+
+ default:
+ return message.ReplyError(ENOTSUP);
+ }
+}
+
+void Service::OnSysPropChange() {}
+
+int Service::ReceiveAndDispatch() {
+ ErrnoGuard errno_guard;
+ Message message;
+ const int ret = endpoint_->MessageReceive(&message);
+ if (ret < 0) {
+ ALOGE("Failed to receive message: %s\n", strerror(errno));
+ return ReturnCodeOrError(ret);
+ }
+
+ std::shared_ptr<Service> service = message.GetService();
+
+ if (!service) {
+ ALOGE("Service::ReceiveAndDispatch: service context is NULL!!!\n");
+ // Don't block the sender indefinitely in this error case.
+ endpoint_->MessageReply(&message, -EINVAL);
+ return -EINVAL;
+ }
+
+ if (message.IsImpulse()) {
+ service->HandleImpulse(message);
+ return 0;
+ } else if (service->HandleSystemMessage(message)) {
+ return 0;
+ } else {
+ return service->HandleMessage(message);
+ }
+}
+
+int Service::Cancel() {
+ ErrnoGuard errno_guard;
+ const int ret = endpoint_->Cancel();
+ return ReturnCodeOrError(ret);
+}
+
+} // namespace pdx
+} // namespace android
diff --git a/libs/vr/libpdx/service_tests.cpp b/libs/vr/libpdx/service_tests.cpp
new file mode 100644
index 0000000..fc0c8db
--- /dev/null
+++ b/libs/vr/libpdx/service_tests.cpp
@@ -0,0 +1,796 @@
+#include <pdx/service.h>
+
+#include <memory>
+#include <string>
+
+#include <gmock/gmock.h>
+#include <pdx/mock_service_endpoint.h>
+
+using android::pdx::BorrowedChannelHandle;
+using android::pdx::BorrowedHandle;
+using android::pdx::Channel;
+using android::pdx::ChannelReference;
+using android::pdx::ErrorStatus;
+using android::pdx::FileReference;
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Message;
+using android::pdx::MessageInfo;
+using android::pdx::MockEndpoint;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::RemoteHandle;
+using android::pdx::Service;
+using android::pdx::Status;
+
+using testing::A;
+using testing::ByMove;
+using testing::DoAll;
+using testing::Invoke;
+using testing::Matcher;
+using testing::Ref;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::SetErrnoAndReturn;
+using testing::WithArg;
+using testing::WithoutArgs;
+using testing::_;
+
+namespace {
+
+// Helper functions to construct fake void pointers for tests.
+inline void* IntToPtr(intptr_t addr) { return reinterpret_cast<void*>(addr); }
+
+// Helper matchers for working with iovec structures in tests.
+// Simple matcher for one element iovec array:
+// EXPECT_CALL(mock, method(IoVecMatcher(ptr, size)));
+MATCHER_P2(IoVecMatcher, ptr, size, "") {
+ return arg->iov_base == ptr && arg->iov_len == size;
+}
+
+// Matcher for an array of iovecs:
+// EXPECT_CALL(mock,
+// method(IoVecMatcher(IoVecArray{{ptr1, size1}, {ptr2, size2}})));
+using IoVecArray = std::vector<iovec>;
+MATCHER_P(IoVecMatcher, iovec_array, "") {
+ for (const iovec& item : iovec_array) {
+ if (arg->iov_base != item.iov_base || arg->iov_len != item.iov_len)
+ return false;
+ arg++;
+ }
+ return true;
+}
+
+using IoVecData = std::vector<std::string>;
+MATCHER_P(IoVecDataMatcher, iovec_data, "") {
+ for (const std::string& item : iovec_data) {
+ std::string data{reinterpret_cast<const char*>(arg->iov_base),
+ arg->iov_len};
+ if (data != item)
+ return false;
+ arg++;
+ }
+ return true;
+}
+
+MATCHER_P(FileHandleMatcher, value, "") { return arg.Get() == value; }
+MATCHER_P(ChannelHandleMatcher, value, "") { return arg.value() == value; }
+
+enum : int {
+ kTestPid = 1,
+ kTestTid,
+ kTestCid,
+ kTestMid,
+ kTestEuid,
+ kTestEgid,
+ kTestOp,
+};
+
+class MockService : public Service {
+ public:
+ using Service::Service;
+ MOCK_METHOD1(OnChannelOpen, std::shared_ptr<Channel>(Message& message));
+ MOCK_METHOD2(OnChannelClose,
+ void(Message& message, const std::shared_ptr<Channel>& channel));
+ MOCK_METHOD1(HandleMessage, int(Message& message));
+ MOCK_METHOD1(HandleImpulse, void(Message& impulse));
+ MOCK_METHOD0(OnSysPropChange, void());
+ MOCK_METHOD1(DumpState, std::string(size_t max_length));
+};
+
+class ServiceTest : public testing::Test {
+ public:
+ ServiceTest() {
+ auto endpoint = std::make_unique<testing::StrictMock<MockEndpoint>>();
+ EXPECT_CALL(*endpoint, SetService(_)).Times(2).WillRepeatedly(Return(0));
+ service_ = std::make_shared<MockService>("MockSvc", std::move(endpoint));
+ }
+
+ MockEndpoint* endpoint() {
+ return static_cast<MockEndpoint*>(service_->endpoint());
+ }
+
+ void SetupMessageInfo(MessageInfo* info, int32_t op, bool impulse = false) {
+ info->pid = kTestPid;
+ info->tid = kTestTid;
+ info->cid = kTestCid;
+ info->mid = impulse ? Message::IMPULSE_MESSAGE_ID : kTestMid;
+ info->euid = kTestEuid;
+ info->egid = kTestEgid;
+ info->op = op;
+ info->flags = 0;
+ info->service = service_.get();
+ info->channel = nullptr;
+ info->send_len = 0;
+ info->recv_len = 0;
+ info->fd_count = 0;
+ memset(info->impulse, 0, sizeof(info->impulse));
+ }
+
+ void SetupMessageInfoAndDefaultExpectations(MessageInfo* info, int32_t op,
+ bool impulse = false) {
+ SetupMessageInfo(info, op, impulse);
+ EXPECT_CALL(*endpoint(), AllocateMessageState()).WillOnce(Return(kState));
+ EXPECT_CALL(*endpoint(), FreeMessageState(kState));
+ }
+
+ void ExpectDefaultHandleMessage() {
+ EXPECT_CALL(*endpoint(), DefaultHandleMessage(_));
+ }
+
+ std::shared_ptr<MockService> service_;
+ void* kState = IntToPtr(123456);
+};
+
+class ServiceMessageTest : public ServiceTest {
+ public:
+ ServiceMessageTest() {
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info, kTestOp);
+ message_ = std::make_unique<Message>(info);
+ }
+
+ std::unique_ptr<Message> message_;
+};
+
+} // anonymous namespace
+
+///////////////////////////////////////////////////////////////////////////////
+// Service class tests
+///////////////////////////////////////////////////////////////////////////////
+
+TEST_F(ServiceTest, IsInitialized) {
+ EXPECT_TRUE(service_->IsInitialized());
+ service_ = std::make_shared<MockService>("MockSvc2", nullptr);
+ EXPECT_FALSE(service_->IsInitialized());
+}
+
+TEST_F(ServiceTest, ConstructMessage) {
+ MessageInfo info;
+ SetupMessageInfo(&info, kTestOp);
+ auto test_channel = std::make_shared<Channel>();
+ info.channel = test_channel.get();
+ EXPECT_CALL(*endpoint(), AllocateMessageState()).WillOnce(Return(kState));
+
+ Message message{info};
+
+ EXPECT_FALSE(message.IsImpulse());
+ EXPECT_EQ(kTestPid, message.GetProcessId());
+ EXPECT_EQ(kTestTid, message.GetThreadId());
+ EXPECT_EQ(kTestCid, message.GetChannelId());
+ EXPECT_EQ(kTestMid, message.GetMessageId());
+ EXPECT_EQ(kTestEuid, message.GetEffectiveUserId());
+ EXPECT_EQ(kTestEgid, message.GetEffectiveGroupId());
+ EXPECT_EQ(kTestOp, message.GetOp());
+ EXPECT_EQ(service_, message.GetService());
+ EXPECT_EQ(test_channel, message.GetChannel());
+ EXPECT_FALSE(message.replied());
+ EXPECT_FALSE(message.IsChannelExpired());
+ EXPECT_FALSE(message.IsServiceExpired());
+ EXPECT_EQ(kState, message.GetState());
+
+ ExpectDefaultHandleMessage();
+ EXPECT_CALL(*endpoint(), FreeMessageState(kState));
+}
+
+TEST_F(ServiceTest, ConstructImpulseMessage) {
+ MessageInfo info;
+ SetupMessageInfo(&info, kTestOp, true);
+ auto test_channel = std::make_shared<Channel>();
+ info.channel = test_channel.get();
+ EXPECT_CALL(*endpoint(), AllocateMessageState()).WillOnce(Return(nullptr));
+
+ Message message{info};
+
+ EXPECT_TRUE(message.IsImpulse());
+ EXPECT_EQ(kTestOp, message.GetOp());
+ EXPECT_EQ(service_, message.GetService());
+ EXPECT_EQ(test_channel, message.GetChannel());
+ EXPECT_TRUE(message.replied());
+ EXPECT_FALSE(message.IsChannelExpired());
+ EXPECT_FALSE(message.IsServiceExpired());
+
+ // DefaultHandleMessage should not be called here.
+ EXPECT_CALL(*endpoint(), FreeMessageState(nullptr));
+}
+
+TEST_F(ServiceTest, HandleMessageChannelOpen) {
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info,
+ android::pdx::opcodes::CHANNEL_OPEN);
+ Message message{info};
+
+ auto channel = std::make_shared<Channel>();
+ EXPECT_CALL(*service_, OnChannelOpen(Ref(message))).WillOnce(Return(channel));
+ EXPECT_CALL(*endpoint(), SetChannel(kTestCid, channel.get()))
+ .WillOnce(Return(0));
+ EXPECT_CALL(*endpoint(), MessageReply(&message, 0)).WillOnce(Return(0));
+
+ EXPECT_EQ(0, service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageChannelClose) {
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info,
+ android::pdx::opcodes::CHANNEL_CLOSE);
+ auto channel = std::make_shared<Channel>();
+ info.channel = channel.get();
+ Message message{info};
+
+ EXPECT_CALL(*service_, OnChannelClose(Ref(message), channel));
+ EXPECT_CALL(*endpoint(), SetChannel(kTestCid, nullptr)).WillOnce(Return(0));
+ EXPECT_CALL(*endpoint(), MessageReply(&message, 0)).WillOnce(Return(0));
+
+ EXPECT_EQ(0, service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageOnSysPropChange) {
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(
+ &info, android::pdx::opcodes::REPORT_SYSPROP_CHANGE);
+ Message message{info};
+
+ EXPECT_CALL(*service_, OnSysPropChange());
+ EXPECT_CALL(*endpoint(), MessageReply(&message, 0)).WillOnce(Return(0));
+
+ EXPECT_EQ(0, service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageOnDumpState) {
+ const size_t kRecvBufSize = 1000;
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info,
+ android::pdx::opcodes::DUMP_STATE);
+ info.recv_len = kRecvBufSize;
+ Message message{info};
+
+ const std::string kReply = "foo";
+ EXPECT_CALL(*service_, DumpState(kRecvBufSize)).WillOnce(Return(kReply));
+ EXPECT_CALL(
+ *endpoint(),
+ WriteMessageData(&message, IoVecDataMatcher(IoVecData{kReply}), 1))
+ .WillOnce(Return(kReply.size()));
+ EXPECT_CALL(*endpoint(), MessageReply(&message, kReply.size()))
+ .WillOnce(Return(0));
+
+ EXPECT_EQ(0, service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageOnDumpStateTooLarge) {
+ const size_t kRecvBufSize = 3;
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info,
+ android::pdx::opcodes::DUMP_STATE);
+ info.recv_len = kRecvBufSize;
+ Message message{info};
+
+ const std::string kReply = "0123456789";
+ const std::string kActualReply = kReply.substr(0, kRecvBufSize);
+ EXPECT_CALL(*service_, DumpState(kRecvBufSize)).WillOnce(Return(kReply));
+ EXPECT_CALL(
+ *endpoint(),
+ WriteMessageData(&message, IoVecDataMatcher(IoVecData{kActualReply}), 1))
+ .WillOnce(Return(kActualReply.size()));
+ EXPECT_CALL(*endpoint(), MessageReply(&message, kActualReply.size()))
+ .WillOnce(Return(0));
+
+ EXPECT_EQ(0, service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageOnDumpStateFail) {
+ const size_t kRecvBufSize = 1000;
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info,
+ android::pdx::opcodes::DUMP_STATE);
+ info.recv_len = kRecvBufSize;
+ Message message{info};
+
+ const std::string kReply = "foo";
+ EXPECT_CALL(*service_, DumpState(kRecvBufSize)).WillOnce(Return(kReply));
+ EXPECT_CALL(
+ *endpoint(),
+ WriteMessageData(&message, IoVecDataMatcher(IoVecData{kReply}), 1))
+ .WillOnce(Return(1));
+ EXPECT_CALL(*endpoint(), MessageReply(&message, -EIO)).WillOnce(Return(0));
+
+ EXPECT_EQ(0, service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageCustom) {
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info, kTestOp);
+ Message message{info};
+
+ EXPECT_CALL(*endpoint(), MessageReply(&message, -ENOTSUP))
+ .WillOnce(Return(0));
+
+ EXPECT_EQ(0, service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, ReplyMessageWithoutService) {
+ MessageInfo info;
+ SetupMessageInfo(&info, kTestOp);
+ EXPECT_CALL(*endpoint(), AllocateMessageState()).WillOnce(Return(nullptr));
+
+ Message message{info};
+
+ EXPECT_FALSE(message.IsServiceExpired());
+ service_.reset();
+ EXPECT_TRUE(message.IsServiceExpired());
+
+ EXPECT_EQ(-EINVAL, message.Reply(12));
+}
+
+TEST_F(ServiceTest, ReceiveAndDispatchMessage) {
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info, kTestOp);
+ ExpectDefaultHandleMessage();
+
+ auto on_receive = [&info](Message* message) {
+ *message = Message{info};
+ return 0;
+ };
+ EXPECT_CALL(*endpoint(), MessageReceive(_)).WillOnce(Invoke(on_receive));
+ EXPECT_CALL(*service_, HandleMessage(_)).WillOnce(Return(0));
+
+ EXPECT_EQ(0, service_->ReceiveAndDispatch());
+}
+
+TEST_F(ServiceTest, ReceiveAndDispatchImpulse) {
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info, kTestOp, true);
+
+ auto on_receive = [&info](Message* message) {
+ *message = Message{info};
+ return 0;
+ };
+ EXPECT_CALL(*endpoint(), MessageReceive(_)).WillOnce(Invoke(on_receive));
+ EXPECT_CALL(*service_, HandleImpulse(_));
+
+ EXPECT_EQ(0, service_->ReceiveAndDispatch());
+}
+
+TEST_F(ServiceTest, Cancel) {
+ EXPECT_CALL(*endpoint(), Cancel()).WillOnce(Return(0));
+ EXPECT_EQ(0, service_->Cancel());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Message class tests
+///////////////////////////////////////////////////////////////////////////////
+
+TEST_F(ServiceMessageTest, Reply) {
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), 12))
+ .WillOnce(Return(0));
+ EXPECT_FALSE(message_->replied());
+ EXPECT_EQ(0, message_->Reply(12));
+ EXPECT_TRUE(message_->replied());
+
+ EXPECT_EQ(-EINVAL, message_->Reply(12)); // Already replied.
+}
+
+TEST_F(ServiceMessageTest, ReplyFail) {
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), 12))
+ .WillOnce(SetErrnoAndReturn(EIO, -1));
+ EXPECT_EQ(-EIO, message_->Reply(12));
+
+ ExpectDefaultHandleMessage();
+}
+
+TEST_F(ServiceMessageTest, ReplyError) {
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -12))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->ReplyError(12));
+}
+
+TEST_F(ServiceMessageTest, ReplyFileDescriptor) {
+ EXPECT_CALL(*endpoint(), MessageReplyFd(message_.get(), 5))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->ReplyFileDescriptor(5));
+}
+
+TEST_F(ServiceMessageTest, ReplyLocalFileHandle) {
+ const int kFakeFd = 12345;
+ LocalHandle handle{kFakeFd};
+ EXPECT_CALL(*endpoint(), MessageReplyFd(message_.get(), kFakeFd))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->Reply(handle));
+ handle.Release(); // Make sure we do not close the fake file descriptor.
+}
+
+TEST_F(ServiceMessageTest, ReplyLocalFileHandleError) {
+ LocalHandle handle{-EINVAL};
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -EINVAL))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyBorrowedFileHandle) {
+ const int kFakeFd = 12345;
+ BorrowedHandle handle{kFakeFd};
+ EXPECT_CALL(*endpoint(), MessageReplyFd(message_.get(), kFakeFd))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyBorrowedFileHandleError) {
+ BorrowedHandle handle{-EACCES};
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -EACCES))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyRemoteFileHandle) {
+ RemoteHandle handle{123};
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), handle.Get()))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyRemoteFileHandleError) {
+ RemoteHandle handle{-EIO};
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -EIO))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyLocalChannelHandle) {
+ LocalChannelHandle handle{nullptr, 12345};
+ EXPECT_CALL(*endpoint(), MessageReplyChannelHandle(
+ message_.get(), A<const LocalChannelHandle&>()))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyBorrowedChannelHandle) {
+ BorrowedChannelHandle handle{12345};
+ EXPECT_CALL(*endpoint(),
+ MessageReplyChannelHandle(message_.get(),
+ A<const BorrowedChannelHandle&>()))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyRemoteChannelHandle) {
+ RemoteChannelHandle handle{12345};
+ EXPECT_CALL(*endpoint(), MessageReplyChannelHandle(
+ message_.get(), A<const RemoteChannelHandle&>()))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyStatusInt) {
+ Status<int> status{123};
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), status.get()))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->Reply(status));
+}
+
+TEST_F(ServiceMessageTest, ReplyStatusError) {
+ Status<int> status{ErrorStatus{EIO}};
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -status.error()))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->Reply(status));
+}
+
+TEST_F(ServiceMessageTest, Read) {
+ ExpectDefaultHandleMessage();
+ void* const kDataBuffer = IntToPtr(12345);
+ const size_t kDataSize = 100;
+ EXPECT_CALL(
+ *endpoint(),
+ ReadMessageData(message_.get(), IoVecMatcher(kDataBuffer, kDataSize), 1))
+ .WillOnce(Return(50))
+ .WillOnce(SetErrnoAndReturn(EACCES, -1));
+ EXPECT_EQ(50, message_->Read(kDataBuffer, kDataSize));
+ EXPECT_EQ(-EACCES, message_->Read(kDataBuffer, kDataSize));
+}
+
+TEST_F(ServiceMessageTest, ReadVector) {
+ ExpectDefaultHandleMessage();
+ char buffer1[10];
+ char buffer2[20];
+ iovec vec[] = {{buffer1, sizeof(buffer1)}, {buffer2, sizeof(buffer2)}};
+ EXPECT_CALL(*endpoint(),
+ ReadMessageData(
+ message_.get(),
+ IoVecMatcher(IoVecArray{std::begin(vec), std::end(vec)}), 2))
+ .WillOnce(Return(30))
+ .WillOnce(Return(15))
+ .WillOnce(SetErrnoAndReturn(EBADF, -1));
+ EXPECT_EQ(30, message_->ReadVector(vec, 2));
+ EXPECT_EQ(15, message_->ReadVector(vec));
+ EXPECT_EQ(-EBADF, message_->ReadVector(vec));
+}
+
+TEST_F(ServiceMessageTest, Write) {
+ ExpectDefaultHandleMessage();
+ void* const kDataBuffer = IntToPtr(12345);
+ const size_t kDataSize = 100;
+ EXPECT_CALL(
+ *endpoint(),
+ WriteMessageData(message_.get(), IoVecMatcher(kDataBuffer, kDataSize), 1))
+ .WillOnce(Return(50))
+ .WillOnce(SetErrnoAndReturn(EBADMSG, -1));
+ EXPECT_EQ(50, message_->Write(kDataBuffer, kDataSize));
+ EXPECT_EQ(-EBADMSG, message_->Write(kDataBuffer, kDataSize));
+}
+
+TEST_F(ServiceMessageTest, WriteVector) {
+ ExpectDefaultHandleMessage();
+ char buffer1[10];
+ char buffer2[20];
+ iovec vec[] = {{buffer1, sizeof(buffer1)}, {buffer2, sizeof(buffer2)}};
+ EXPECT_CALL(*endpoint(),
+ WriteMessageData(
+ message_.get(),
+ IoVecMatcher(IoVecArray{std::begin(vec), std::end(vec)}), 2))
+ .WillOnce(Return(30))
+ .WillOnce(Return(15))
+ .WillOnce(SetErrnoAndReturn(EIO, -1));
+ EXPECT_EQ(30, message_->WriteVector(vec, 2));
+ EXPECT_EQ(15, message_->WriteVector(vec));
+ EXPECT_EQ(-EIO, message_->WriteVector(vec, 2));
+}
+
+TEST_F(ServiceMessageTest, PushLocalFileHandle) {
+ ExpectDefaultHandleMessage();
+ const int kFakeFd = 12345;
+ LocalHandle handle{kFakeFd};
+ EXPECT_CALL(*endpoint(),
+ PushFileHandle(message_.get(), Matcher<const LocalHandle&>(
+ FileHandleMatcher(kFakeFd))))
+ .WillOnce(Return(12))
+ .WillOnce(SetErrnoAndReturn(EIO, -1));
+ EXPECT_EQ(12, message_->PushFileHandle(handle));
+ EXPECT_EQ(-EIO, message_->PushFileHandle(handle));
+ handle.Release(); // Make sure we do not close the fake file descriptor.
+}
+
+TEST_F(ServiceMessageTest, PushBorrowedFileHandle) {
+ ExpectDefaultHandleMessage();
+ const int kFakeFd = 12345;
+ BorrowedHandle handle{kFakeFd};
+ EXPECT_CALL(*endpoint(),
+ PushFileHandle(message_.get(), Matcher<const BorrowedHandle&>(
+ FileHandleMatcher(kFakeFd))))
+ .WillOnce(Return(13))
+ .WillOnce(SetErrnoAndReturn(EACCES, -1));
+ EXPECT_EQ(13, message_->PushFileHandle(handle));
+ EXPECT_EQ(-EACCES, message_->PushFileHandle(handle));
+}
+
+TEST_F(ServiceMessageTest, PushRemoteFileHandle) {
+ ExpectDefaultHandleMessage();
+ const int kFakeFd = 12345;
+ RemoteHandle handle{kFakeFd};
+ EXPECT_CALL(*endpoint(),
+ PushFileHandle(message_.get(), Matcher<const RemoteHandle&>(
+ FileHandleMatcher(kFakeFd))))
+ .WillOnce(Return(kFakeFd))
+ .WillOnce(SetErrnoAndReturn(EIO, -1));
+ EXPECT_EQ(kFakeFd, message_->PushFileHandle(handle));
+ EXPECT_EQ(-EIO, message_->PushFileHandle(handle));
+}
+
+TEST_F(ServiceMessageTest, PushLocalChannelHandle) {
+ ExpectDefaultHandleMessage();
+ int32_t kValue = 12345;
+ LocalChannelHandle handle{nullptr, kValue};
+ EXPECT_CALL(*endpoint(), PushChannelHandle(message_.get(),
+ Matcher<const LocalChannelHandle&>(
+ ChannelHandleMatcher(kValue))))
+ .WillOnce(Return(7))
+ .WillOnce(SetErrnoAndReturn(EIO, -1));
+ EXPECT_EQ(7, message_->PushChannelHandle(handle));
+ EXPECT_EQ(-EIO, message_->PushChannelHandle(handle));
+}
+
+TEST_F(ServiceMessageTest, PushBorrowedChannelHandle) {
+ ExpectDefaultHandleMessage();
+ int32_t kValue = 12345;
+ BorrowedChannelHandle handle{kValue};
+ EXPECT_CALL(
+ *endpoint(),
+ PushChannelHandle(message_.get(), Matcher<const BorrowedChannelHandle&>(
+ ChannelHandleMatcher(kValue))))
+ .WillOnce(Return(8))
+ .WillOnce(SetErrnoAndReturn(EIO, -1));
+ EXPECT_EQ(8, message_->PushChannelHandle(handle));
+ EXPECT_EQ(-EIO, message_->PushChannelHandle(handle));
+}
+
+TEST_F(ServiceMessageTest, PushRemoteChannelHandle) {
+ ExpectDefaultHandleMessage();
+ int32_t kValue = 12345;
+ RemoteChannelHandle handle{kValue};
+ EXPECT_CALL(
+ *endpoint(),
+ PushChannelHandle(message_.get(), Matcher<const RemoteChannelHandle&>(
+ ChannelHandleMatcher(kValue))))
+ .WillOnce(Return(kValue))
+ .WillOnce(SetErrnoAndReturn(EIO, -1));
+ EXPECT_EQ(kValue, message_->PushChannelHandle(handle));
+ EXPECT_EQ(-EIO, message_->PushChannelHandle(handle));
+}
+
+TEST_F(ServiceMessageTest, GetFileHandle) {
+ ExpectDefaultHandleMessage();
+ auto make_file_handle = [](FileReference ref) { return LocalHandle{ref}; };
+ EXPECT_CALL(*endpoint(), GetFileHandle(message_.get(), _))
+ .WillOnce(WithArg<1>(Invoke(make_file_handle)));
+ LocalHandle handle;
+ FileReference kRef = 12345;
+ EXPECT_TRUE(message_->GetFileHandle(kRef, &handle));
+ EXPECT_EQ(kRef, handle.Get());
+ handle.Release(); // Make sure we do not close the fake file descriptor.
+}
+
+TEST_F(ServiceMessageTest, GetFileHandleInvalid) {
+ ExpectDefaultHandleMessage();
+ LocalHandle handle;
+ FileReference kRef = -12;
+ EXPECT_TRUE(message_->GetFileHandle(kRef, &handle));
+ EXPECT_EQ(kRef, handle.Get());
+}
+
+TEST_F(ServiceMessageTest, GetFileHandleError) {
+ ExpectDefaultHandleMessage();
+ EXPECT_CALL(*endpoint(), GetFileHandle(message_.get(), _))
+ .WillOnce(WithoutArgs(Invoke([] { return LocalHandle{-EIO}; })));
+ LocalHandle handle;
+ FileReference kRef = 12345;
+ EXPECT_FALSE(message_->GetFileHandle(kRef, &handle));
+ EXPECT_EQ(-EIO, handle.Get());
+}
+
+TEST_F(ServiceMessageTest, GetChannelHandle) {
+ ExpectDefaultHandleMessage();
+ auto make_channel_handle = [](ChannelReference ref) {
+ return LocalChannelHandle{nullptr, ref};
+ };
+ EXPECT_CALL(*endpoint(), GetChannelHandle(message_.get(), _))
+ .WillOnce(WithArg<1>(Invoke(make_channel_handle)));
+ LocalChannelHandle handle;
+ ChannelReference kRef = 12345;
+ EXPECT_TRUE(message_->GetChannelHandle(kRef, &handle));
+ EXPECT_EQ(kRef, handle.value());
+}
+
+TEST_F(ServiceMessageTest, GetChannelHandleInvalid) {
+ ExpectDefaultHandleMessage();
+ LocalChannelHandle handle;
+ ChannelReference kRef = -12;
+ EXPECT_TRUE(message_->GetChannelHandle(kRef, &handle));
+ EXPECT_EQ(-12, handle.value());
+}
+
+TEST_F(ServiceMessageTest, GetChannelHandleError) {
+ ExpectDefaultHandleMessage();
+ EXPECT_CALL(*endpoint(), GetChannelHandle(message_.get(), _))
+ .WillOnce(WithoutArgs(Invoke([] {
+ return LocalChannelHandle{nullptr, -EIO};
+ })));
+ LocalChannelHandle handle;
+ ChannelReference kRef = 12345;
+ EXPECT_FALSE(message_->GetChannelHandle(kRef, &handle));
+ EXPECT_EQ(-EIO, handle.value());
+}
+
+TEST_F(ServiceMessageTest, ModifyChannelEvents) {
+ ExpectDefaultHandleMessage();
+ int kClearMask = 1;
+ int kSetMask = 2;
+ EXPECT_CALL(*endpoint(), ModifyChannelEvents(kTestCid, kClearMask, kSetMask))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->ModifyChannelEvents(kClearMask, kSetMask));
+}
+
+TEST_F(ServiceMessageTest, PushChannelSameService) {
+ ExpectDefaultHandleMessage();
+ int kFlags = 123;
+ int32_t kValue = 12;
+ EXPECT_CALL(*endpoint(), PushChannel(message_.get(), kFlags, nullptr, _))
+ .WillOnce(DoAll(SetArgPointee<3>(kTestCid),
+ Return(ByMove(RemoteChannelHandle{kValue}))));
+ int channel_id = -1;
+ auto status = message_->PushChannel(kFlags, nullptr, &channel_id);
+ ASSERT_TRUE(status);
+ EXPECT_EQ(kValue, status.get().value());
+ EXPECT_EQ(kTestCid, channel_id);
+}
+
+TEST_F(ServiceMessageTest, PushChannelFailure) {
+ ExpectDefaultHandleMessage();
+ int kFlags = 123;
+ EXPECT_CALL(*endpoint(), PushChannel(message_.get(), kFlags, nullptr, _))
+ .WillOnce(Return(ByMove(ErrorStatus{EIO})));
+ int channel_id = -1;
+ auto status = message_->PushChannel(kFlags, nullptr, &channel_id);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EIO, status.error());
+}
+
+TEST_F(ServiceMessageTest, PushChannelDifferentService) {
+ ExpectDefaultHandleMessage();
+ auto endpoint2 = std::make_unique<testing::StrictMock<MockEndpoint>>();
+ EXPECT_CALL(*endpoint2, SetService(_)).Times(2).WillRepeatedly(Return(0));
+ auto service2 =
+ std::make_shared<MockService>("MockSvc2", std::move(endpoint2));
+
+ int kFlags = 123;
+ int32_t kValue = 12;
+ EXPECT_CALL(*static_cast<MockEndpoint*>(service2->endpoint()),
+ PushChannel(message_.get(), kFlags, nullptr, _))
+ .WillOnce(DoAll(SetArgPointee<3>(kTestCid),
+ Return(ByMove(RemoteChannelHandle{kValue}))));
+ int channel_id = -1;
+ auto status =
+ message_->PushChannel(service2.get(), kFlags, nullptr, &channel_id);
+ ASSERT_TRUE(status);
+ EXPECT_EQ(kValue, status.get().value());
+ EXPECT_EQ(kTestCid, channel_id);
+}
+
+TEST_F(ServiceMessageTest, CheckChannelSameService) {
+ ExpectDefaultHandleMessage();
+
+ auto test_channel = std::make_shared<Channel>();
+ ChannelReference kRef = 123;
+ EXPECT_CALL(*endpoint(), CheckChannel(message_.get(), kRef, _))
+ .WillOnce(DoAll(SetArgPointee<2>(test_channel.get()), Return(kTestCid)));
+ std::shared_ptr<Channel> channel;
+ auto status = message_->CheckChannel(kRef, &channel);
+ ASSERT_TRUE(status);
+ EXPECT_EQ(kTestCid, status.get());
+ EXPECT_EQ(test_channel, channel);
+}
+
+TEST_F(ServiceMessageTest, CheckChannelFailure) {
+ ExpectDefaultHandleMessage();
+ ChannelReference kRef = 123;
+ EXPECT_CALL(*endpoint(), CheckChannel(message_.get(), kRef, _))
+ .WillOnce(Return(ByMove(ErrorStatus{EOPNOTSUPP})));
+ std::shared_ptr<Channel> channel;
+ auto status = message_->CheckChannel(kRef, &channel);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EOPNOTSUPP, status.error());
+}
+
+TEST_F(ServiceMessageTest, CheckChannelDifferentService) {
+ ExpectDefaultHandleMessage();
+ auto endpoint2 = std::make_unique<testing::StrictMock<MockEndpoint>>();
+ EXPECT_CALL(*endpoint2, SetService(_)).Times(2).WillRepeatedly(Return(0));
+ auto service2 =
+ std::make_shared<MockService>("MockSvc2", std::move(endpoint2));
+
+ auto test_channel = std::make_shared<Channel>();
+ ChannelReference kRef = 123;
+ EXPECT_CALL(*static_cast<MockEndpoint*>(service2->endpoint()),
+ CheckChannel(message_.get(), kRef, _))
+ .WillOnce(DoAll(SetArgPointee<2>(test_channel.get()), Return(kTestCid)));
+ std::shared_ptr<Channel> channel;
+ auto status = message_->CheckChannel(service2.get(), kRef, &channel);
+ ASSERT_TRUE(status);
+ EXPECT_EQ(kTestCid, status.get());
+ EXPECT_EQ(test_channel, channel);
+}
diff --git a/libs/vr/libpdx/status.cpp b/libs/vr/libpdx/status.cpp
new file mode 100644
index 0000000..c275daf
--- /dev/null
+++ b/libs/vr/libpdx/status.cpp
@@ -0,0 +1,15 @@
+#include "pdx/status.h"
+
+#include <pdx/rpc/serialization.h>
+#include <string.h>
+
+namespace android {
+namespace pdx {
+
+std::string ErrorStatus::ErrorToString(int error_code) {
+ char message[1024] = {};
+ return strerror_r(error_code, message, sizeof(message));
+}
+
+} // namespace pdx
+} // namespace android
diff --git a/libs/vr/libpdx/status_tests.cpp b/libs/vr/libpdx/status_tests.cpp
new file mode 100644
index 0000000..d4e697c
--- /dev/null
+++ b/libs/vr/libpdx/status_tests.cpp
@@ -0,0 +1,125 @@
+#include <pdx/status.h>
+
+#include <gtest/gtest.h>
+
+using android::pdx::ErrorStatus;
+using android::pdx::Status;
+
+TEST(Status, DefaultInit) {
+ Status<int> status;
+ EXPECT_FALSE(status.ok());
+ EXPECT_TRUE(status.empty());
+ EXPECT_EQ(0, status.get());
+ EXPECT_EQ(0, status.error());
+}
+
+TEST(Status, InitalizeSuccess) {
+ Status<int> status_int{0};
+ EXPECT_FALSE(status_int.empty());
+ EXPECT_TRUE(status_int.ok());
+ EXPECT_EQ(0, status_int.get());
+ status_int = Status<int>(3);
+ EXPECT_FALSE(status_int.empty());
+ EXPECT_TRUE(status_int.ok());
+ EXPECT_EQ(3, status_int.get());
+ status_int = Status<int>(-3);
+ EXPECT_FALSE(status_int.empty());
+ EXPECT_TRUE(status_int.ok());
+ EXPECT_EQ(-3, status_int.get());
+
+ Status<std::string> status_str{"foo"};
+ EXPECT_FALSE(status_str.empty());
+ EXPECT_TRUE(status_str.ok());
+ EXPECT_EQ("foo", status_str.get());
+}
+
+TEST(Status, InitalizeError) {
+ Status<int> status_int = ErrorStatus(12);
+ EXPECT_FALSE(status_int.empty());
+ EXPECT_FALSE(status_int.ok());
+ EXPECT_EQ(0, status_int.get());
+ EXPECT_EQ(12, status_int.error());
+
+ Status<std::string> status_str = ErrorStatus(EIO);
+ EXPECT_FALSE(status_str.empty());
+ EXPECT_FALSE(status_str.ok());
+ EXPECT_EQ(EIO, status_str.error());
+}
+
+TEST(Status, ErrorMessage) {
+ Status<int> status = ErrorStatus(EIO);
+ EXPECT_EQ(status.GetErrorMessage(), strerror(EIO));
+
+ status = ErrorStatus(EINVAL);
+ EXPECT_EQ(status.GetErrorMessage(), strerror(EINVAL));
+}
+
+TEST(Status, Copy) {
+ Status<int> status1;
+ Status<int> status2;
+
+ status1 = Status<int>{12};
+ status2 = ErrorStatus(13);
+ EXPECT_FALSE(status1.empty());
+ EXPECT_FALSE(status2.empty());
+ EXPECT_TRUE(status1.ok());
+ EXPECT_FALSE(status2.ok());
+ EXPECT_EQ(12, status1.get());
+ EXPECT_EQ(0, status1.error());
+ EXPECT_EQ(0, status2.get());
+ EXPECT_EQ(13, status2.error());
+
+ status1 = status2;
+ EXPECT_FALSE(status1.empty());
+ EXPECT_FALSE(status2.empty());
+ EXPECT_FALSE(status1.ok());
+ EXPECT_FALSE(status2.ok());
+ EXPECT_EQ(0, status1.get());
+ EXPECT_EQ(13, status1.error());
+ EXPECT_EQ(0, status2.get());
+ EXPECT_EQ(13, status2.error());
+}
+
+TEST(Status, Move) {
+ Status<std::unique_ptr<int>> status1;
+ Status<std::unique_ptr<int>> status2;
+
+ status1 = Status<std::unique_ptr<int>>{std::unique_ptr<int>{new int{11}}};
+ status2 = Status<std::unique_ptr<int>>{std::unique_ptr<int>{new int{12}}};
+ EXPECT_FALSE(status1.empty());
+ EXPECT_FALSE(status2.empty());
+ EXPECT_TRUE(status1.ok());
+ EXPECT_TRUE(status2.ok());
+ EXPECT_EQ(11, *status1.get());
+ EXPECT_EQ(12, *status2.get());
+
+ Status<std::unique_ptr<int>> status3 = std::move(status2);
+ EXPECT_FALSE(status1.empty());
+ EXPECT_TRUE(status2.empty());
+ EXPECT_FALSE(status3.empty());
+ EXPECT_TRUE(status1.ok());
+ EXPECT_FALSE(status2.ok());
+ EXPECT_TRUE(status3.ok());
+ EXPECT_EQ(11, *status1.get());
+ EXPECT_EQ(nullptr, status2.get());
+ EXPECT_EQ(12, *status3.get());
+
+ std::swap(status1, status3);
+ EXPECT_EQ(12, *status1.get());
+ EXPECT_EQ(11, *status3.get());
+
+ status3 = std::move(status1);
+ EXPECT_TRUE(status1.empty());
+ EXPECT_EQ(12, *status3.get());
+}
+
+TEST(Status, Take) {
+ Status<std::unique_ptr<int>> status{std::unique_ptr<int>{new int{123}}};
+ EXPECT_FALSE(status.empty());
+ EXPECT_NE(nullptr, status.get());
+
+ auto data = status.take();
+ EXPECT_TRUE(status.empty());
+ EXPECT_EQ(nullptr, status.get());
+ EXPECT_EQ(123, *data);
+}
diff --git a/libs/vr/libpdx/thread_local_buffer_tests.cpp b/libs/vr/libpdx/thread_local_buffer_tests.cpp
new file mode 100644
index 0000000..c6a7b0b
--- /dev/null
+++ b/libs/vr/libpdx/thread_local_buffer_tests.cpp
@@ -0,0 +1,116 @@
+#include <memory>
+#include <string>
+#include <thread>
+#include <utility>
+
+#include <gtest/gtest.h>
+#include <pdx/rpc/message_buffer.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+class ThreadLocalBufferTest {
+ public:
+ // Returns the unique address of the thread-local buffer. Used to test the
+ // correct behavior of the type-based thread local storage slot mapping
+ // mechanism.
+ template <typename Slot>
+ static std::uintptr_t GetSlotAddress() {
+ return reinterpret_cast<std::uintptr_t>(&MessageBuffer<Slot>::buffer_);
+ }
+
+ // Returns the raw value of the thread local buffer. Used to test the behavior
+ // of backing buffer initialization.
+ template <typename Slot>
+ static std::uintptr_t GetSlotValue() {
+ return reinterpret_cast<std::uintptr_t>(MessageBuffer<Slot>::buffer_);
+ }
+};
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+using namespace android::pdx::rpc;
+
+namespace {
+
+struct TypeTagA;
+struct TypeTagB;
+
+constexpr std::size_t kSendBufferIndex = 0;
+constexpr std::size_t kReceiveBufferIndex = 1;
+
+using SendSlotA = ThreadLocalSlot<TypeTagA, kSendBufferIndex>;
+using SendSlotB = ThreadLocalSlot<TypeTagB, kSendBufferIndex>;
+using ReceiveSlotA = ThreadLocalSlot<TypeTagA, kReceiveBufferIndex>;
+using ReceiveSlotB = ThreadLocalSlot<TypeTagB, kReceiveBufferIndex>;
+
+} // anonymous namespace
+
+// Tests that index and type-based thread-local slot addressing works by
+// checking that the slot address is the same when the same index/type
+// combination is used and different when different combinations are used.
+TEST(ThreadLocalBufferTest, TypeSlots) {
+ auto id1 = ThreadLocalBufferTest::GetSlotAddress<SendSlotA>();
+ auto id2 = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotA>();
+ auto id3 = ThreadLocalBufferTest::GetSlotAddress<SendSlotB>();
+ auto id4 = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotB>();
+
+ EXPECT_NE(id1, id2);
+ EXPECT_NE(id3, id4);
+ EXPECT_NE(id1, id3);
+ EXPECT_NE(id2, id4);
+
+ auto id1_alias = ThreadLocalBufferTest::GetSlotAddress<SendSlotA>();
+ auto id2_alias = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotA>();
+ auto id3_alias = ThreadLocalBufferTest::GetSlotAddress<SendSlotB>();
+ auto id4_alias = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotB>();
+
+ EXPECT_EQ(id1, id1_alias);
+ EXPECT_EQ(id2, id2_alias);
+ EXPECT_EQ(id3, id3_alias);
+ EXPECT_EQ(id4, id4_alias);
+}
+
+// Tests that different threads get different buffers for the same slot address.
+TEST(ThreadLocalBufferTest, ThreadSlots) {
+ auto id1 = ThreadLocalBufferTest::GetSlotAddress<SendBuffer>();
+ std::uintptr_t id2 = 0U;
+
+ std::thread thread([&id2]() mutable {
+ id2 = ThreadLocalBufferTest::GetSlotAddress<SendBuffer>();
+ });
+ thread.join();
+
+ EXPECT_NE(0U, id1);
+ EXPECT_NE(0U, id2);
+ EXPECT_NE(id1, id2);
+}
+
+// Tests that thread-local buffers are allocated at the first buffer request.
+TEST(ThreadLocalBufferTest, InitialValue) {
+ struct TypeTagX;
+ using SendSlotX = ThreadLocalSlot<TypeTagX, kSendBufferIndex>;
+
+ auto value1 = ThreadLocalBufferTest::GetSlotValue<SendSlotX>();
+ auto value2 = ThreadLocalBufferTest::GetSlotValue<SendSlotX>();
+
+ EXPECT_EQ(0U, value1);
+ EXPECT_NE(0U, value2);
+}
+
+// Tests that the underlying buffers are the same for a given index/type pair
+// and different across index/type combinations.
+TEST(ThreadLocalBufferTest, BackingBuffer) {
+ auto& buffer1 = MessageBuffer<SendSlotA>::GetBuffer();
+ auto& buffer2 = MessageBuffer<SendSlotA>::GetBuffer();
+ auto& buffer3 = MessageBuffer<SendSlotB>::GetBuffer();
+ auto& buffer4 = MessageBuffer<SendSlotB>::GetBuffer();
+
+ EXPECT_EQ(buffer1.data(), buffer2.data());
+ EXPECT_EQ(buffer3.data(), buffer4.data());
+ EXPECT_NE(buffer1.data(), buffer3.data());
+ EXPECT_NE(buffer2.data(), buffer4.data());
+}
diff --git a/libs/vr/libpdx/variant_tests.cpp b/libs/vr/libpdx/variant_tests.cpp
new file mode 100644
index 0000000..c30c055
--- /dev/null
+++ b/libs/vr/libpdx/variant_tests.cpp
@@ -0,0 +1,1087 @@
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <string>
+#include <type_traits>
+
+#include <gtest/gtest.h>
+#include <pdx/rpc/variant.h>
+
+using namespace android::pdx;
+using namespace android::pdx::rpc;
+
+namespace {
+
+struct BaseType {
+ BaseType(int value) : value(value) {}
+ int value;
+};
+
+struct DerivedType : BaseType {
+ DerivedType(int value) : BaseType{value} {};
+};
+
+template <typename T>
+class TestType {
+ public:
+ TestType(const T& value) : value_(value) {}
+ TestType(T&& value) : value_(std::move(value)) {}
+ TestType(const TestType&) = default;
+ TestType(TestType&&) = default;
+
+ TestType& operator=(const TestType&) = default;
+ TestType& operator=(TestType&&) = default;
+
+ const T& get() const { return value_; }
+ T&& take() { return std::move(value_); }
+
+ private:
+ T value_;
+};
+
+template <typename T>
+class InstrumentType {
+ public:
+ InstrumentType(const T& value) : value_(value) { constructor_count_++; }
+ InstrumentType(T&& value) : value_(std::move(value)) { constructor_count_++; }
+ InstrumentType(const InstrumentType& other) : value_(other.value_) {
+ constructor_count_++;
+ }
+ InstrumentType(InstrumentType&& other) : value_(std::move(other.value_)) {
+ constructor_count_++;
+ }
+ InstrumentType(const TestType<T>& other) : value_(other.get()) {
+ constructor_count_++;
+ }
+ InstrumentType(TestType<T>&& other) : value_(other.take()) {
+ constructor_count_++;
+ }
+ ~InstrumentType() { destructor_count_++; }
+
+ InstrumentType& operator=(const InstrumentType& other) {
+ copy_assignment_count_++;
+ value_ = other.value_;
+ return *this;
+ }
+ InstrumentType& operator=(InstrumentType&& other) {
+ move_assignment_count_++;
+ value_ = std::move(other.value_);
+ return *this;
+ }
+
+ InstrumentType& operator=(const TestType<T>& other) {
+ copy_assignment_count_++;
+ value_ = other.get();
+ return *this;
+ }
+ InstrumentType& operator=(TestType<T>&& other) {
+ move_assignment_count_++;
+ value_ = other.take();
+ return *this;
+ }
+
+ static std::size_t constructor_count() { return constructor_count_; }
+ static std::size_t destructor_count() { return destructor_count_; }
+ static std::size_t move_assignment_count() { return move_assignment_count_; }
+ static std::size_t copy_assignment_count() { return copy_assignment_count_; }
+
+ const T& get() const { return value_; }
+ T&& take() { return std::move(value_); }
+
+ static void clear() {
+ constructor_count_ = 0;
+ destructor_count_ = 0;
+ move_assignment_count_ = 0;
+ copy_assignment_count_ = 0;
+ }
+
+ private:
+ T value_;
+
+ static std::size_t constructor_count_;
+ static std::size_t destructor_count_;
+ static std::size_t move_assignment_count_;
+ static std::size_t copy_assignment_count_;
+};
+
+template <typename T>
+std::size_t InstrumentType<T>::constructor_count_ = 0;
+template <typename T>
+std::size_t InstrumentType<T>::destructor_count_ = 0;
+template <typename T>
+std::size_t InstrumentType<T>::move_assignment_count_ = 0;
+template <typename T>
+std::size_t InstrumentType<T>::copy_assignment_count_ = 0;
+
+} // anonymous namespace
+
+TEST(Variant, Assignment) {
+ // Assert basic type properties.
+ {
+ Variant<int, bool, float> v;
+ ASSERT_EQ(-1, v.index());
+ ASSERT_FALSE(v.is<int>());
+ ASSERT_FALSE(v.is<bool>());
+ ASSERT_FALSE(v.is<float>());
+ }
+
+ {
+ Variant<int, bool, float> v;
+ v = 10;
+ ASSERT_EQ(0, v.index());
+ ASSERT_TRUE(v.is<int>());
+ ASSERT_FALSE(v.is<bool>());
+ ASSERT_FALSE(v.is<float>());
+ EXPECT_EQ(10, std::get<int>(v));
+ }
+
+ {
+ Variant<int, bool, float> v;
+ v = false;
+ ASSERT_EQ(1, v.index());
+ ASSERT_FALSE(v.is<int>());
+ ASSERT_TRUE(v.is<bool>());
+ ASSERT_FALSE(v.is<float>());
+ EXPECT_EQ(false, std::get<bool>(v));
+ }
+
+ {
+ Variant<int, bool, float> v;
+ v = 1.0f;
+ ASSERT_EQ(2, v.index());
+ ASSERT_FALSE(v.is<int>());
+ ASSERT_FALSE(v.is<bool>());
+ ASSERT_TRUE(v.is<float>());
+ EXPECT_FLOAT_EQ(1.0f, std::get<float>(v));
+ }
+
+ {
+ Variant<int, bool, float> v;
+ // ERROR: More than one type is implicitly convertible from double.
+ // v = 1.0;
+ v = static_cast<float>(1.0);
+ }
+
+ {
+ Variant<int, bool, float> v;
+
+ double x = 1.1;
+ v = static_cast<float>(x);
+ ASSERT_EQ(2, v.index());
+ ASSERT_FALSE(v.is<int>());
+ ASSERT_FALSE(v.is<bool>());
+ ASSERT_TRUE(v.is<float>());
+ EXPECT_FLOAT_EQ(1.1, std::get<float>(v));
+ }
+
+ {
+ Variant<int, std::string> v;
+ ASSERT_EQ(-1, v.index());
+ ASSERT_FALSE(v.is<int>());
+ ASSERT_FALSE(v.is<std::string>());
+ }
+
+ {
+ Variant<int, std::string> v;
+ v = 20;
+ ASSERT_EQ(0, v.index());
+ ASSERT_TRUE(v.is<int>());
+ ASSERT_FALSE(v.is<std::string>());
+ EXPECT_EQ(20, std::get<int>(v));
+ }
+
+ {
+ Variant<int, std::string> v;
+ v = std::string("test");
+ ASSERT_EQ(1, v.index());
+ ASSERT_FALSE(v.is<int>());
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_EQ("test", std::get<std::string>(v));
+ }
+
+ {
+ Variant<int, std::string> v;
+ v = "test";
+ ASSERT_EQ(1, v.index());
+ ASSERT_FALSE(v.is<int>());
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_EQ("test", std::get<std::string>(v));
+ }
+
+ {
+ Variant<const char*> v1;
+ Variant<std::string> v2;
+
+ v1 = "test";
+ ASSERT_TRUE(v1.is<const char*>());
+ v2 = v1;
+ ASSERT_TRUE(v2.is<std::string>());
+ EXPECT_EQ("test", std::get<std::string>(v2));
+ }
+
+ {
+ Variant<int> a(1);
+ Variant<int> b;
+ ASSERT_TRUE(!a.empty());
+ ASSERT_TRUE(b.empty());
+
+ a = b;
+ ASSERT_TRUE(a.empty());
+ ASSERT_TRUE(b.empty());
+ }
+
+ {
+ Variant<int*, char*> v;
+
+ // ERROR: More than one type is implicitly convertible from nullptr.
+ // v = nullptr;
+
+ v = static_cast<int*>(nullptr);
+ EXPECT_TRUE(v.is<int*>());
+
+ v = static_cast<char*>(nullptr);
+ EXPECT_TRUE(v.is<char*>());
+ }
+
+ {
+ Variant<int*, char*> v;
+ int a = 10;
+ char b = 20;
+
+ v = &b;
+ ASSERT_TRUE(v.is<char*>());
+ EXPECT_EQ(&b, std::get<char*>(v));
+ EXPECT_EQ(b, *std::get<char*>(v));
+
+ v = &a;
+ ASSERT_TRUE(v.is<int*>());
+ EXPECT_EQ(&a, std::get<int*>(v));
+ EXPECT_EQ(a, *std::get<int*>(v));
+ }
+
+ {
+ using IntRef = std::reference_wrapper<int>;
+ Variant<IntRef> v;
+ int a = 10;
+
+ v = a;
+ ASSERT_TRUE(v.is<IntRef>());
+ EXPECT_EQ(a, std::get<IntRef>(v));
+
+ a = 20;
+ EXPECT_EQ(a, std::get<IntRef>(v));
+ }
+}
+
+TEST(Variant, MoveAssignment) {
+ {
+ Variant<std::string> v;
+ std::string s = "test";
+ v = std::move(s);
+
+ EXPECT_TRUE(s.empty());
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_EQ("test", std::get<std::string>(v));
+ }
+
+ {
+ Variant<std::string> v("test");
+ std::string s = "fizz";
+ s = std::move(std::get<std::string>(v));
+
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_TRUE(std::get<std::string>(v).empty());
+ EXPECT_EQ("test", s);
+ }
+
+ {
+ Variant<std::string> a("test");
+ Variant<std::string> b;
+
+ b = std::move(a);
+ ASSERT_TRUE(a.is<std::string>());
+ ASSERT_TRUE(b.is<std::string>());
+ EXPECT_TRUE(std::get<std::string>(a).empty());
+ EXPECT_EQ("test", std::get<std::string>(b));
+ }
+
+ {
+ Variant<std::string> a("test");
+ Variant<std::string> b("fizz");
+
+ b = std::move(a);
+ ASSERT_TRUE(a.is<std::string>());
+ ASSERT_TRUE(b.is<std::string>());
+ EXPECT_TRUE(std::get<std::string>(a).empty());
+ EXPECT_EQ("test", std::get<std::string>(b));
+ }
+
+ {
+ Variant<int, std::string> a("test");
+ Variant<int, std::string> b(10);
+
+ b = std::move(a);
+ ASSERT_TRUE(a.is<std::string>());
+ ASSERT_TRUE(b.is<std::string>());
+ EXPECT_TRUE(std::get<std::string>(a).empty());
+ EXPECT_EQ("test", std::get<std::string>(b));
+ }
+
+ {
+ Variant<int, std::string> a(10);
+ Variant<int, std::string> b("test");
+
+ b = std::move(a);
+ ASSERT_TRUE(a.is<int>());
+ ASSERT_TRUE(b.is<int>());
+ EXPECT_EQ(10, std::get<int>(a));
+ EXPECT_EQ(10, std::get<int>(b));
+ }
+}
+
+TEST(Variant, Constructor) {
+ {
+ Variant<int, bool, float> v(true);
+ EXPECT_TRUE(v.is<bool>());
+ }
+
+ {
+ Variant<int, bool, float> v(10);
+ EXPECT_TRUE(v.is<int>());
+ }
+
+ {
+ Variant<int, bool, float> v(10.1f);
+ EXPECT_TRUE(v.is<float>());
+ }
+
+ {
+ Variant<float, std::string> v(10.);
+ EXPECT_TRUE(v.is<float>());
+ }
+
+ {
+ TestType<int> i(1);
+ Variant<int, bool, float> v(i.take());
+ ASSERT_TRUE(v.is<int>());
+ EXPECT_EQ(1, std::get<int>(v));
+ }
+
+ {
+ TestType<bool> b(true);
+ Variant<int, bool, float> v(b.take());
+ ASSERT_TRUE(v.is<bool>());
+ EXPECT_EQ(true, std::get<bool>(v));
+ }
+
+ {
+ Variant<const char*> c("test");
+ Variant<std::string> s(c);
+ ASSERT_TRUE(s.is<std::string>());
+ EXPECT_EQ("test", std::get<std::string>(s));
+ }
+
+ {
+ Variant<int, bool, float> a(true);
+ Variant<int, bool, float> b(a);
+
+ ASSERT_TRUE(b.is<bool>());
+ }
+
+ {
+ using IntRef = std::reference_wrapper<int>;
+ int a = 10;
+ Variant<IntRef> v(a);
+ TestType<IntRef> t(a);
+
+ ASSERT_TRUE(v.is<IntRef>());
+ EXPECT_EQ(a, std::get<IntRef>(v));
+ EXPECT_EQ(a, t.get());
+
+ a = 20;
+ EXPECT_EQ(a, std::get<IntRef>(v));
+ EXPECT_EQ(a, t.get());
+ }
+}
+
+// Verify correct ctor/dtor and assignment behavior used an instrumented type.
+TEST(Variant, CopyMoveConstructAssign) {
+ {
+ InstrumentType<int>::clear();
+
+ // Default construct to empty, no InstrumentType activity.
+ Variant<int, InstrumentType<int>> v;
+ ASSERT_EQ(0u, InstrumentType<int>::constructor_count());
+ ASSERT_EQ(0u, InstrumentType<int>::destructor_count());
+ ASSERT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ ASSERT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from int type, no InstrumentType activity.
+ Variant<int, InstrumentType<int>> v;
+ v = 10;
+ EXPECT_EQ(0u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from int type, no InstrumentType activity.
+ Variant<int, InstrumentType<int>> v(10);
+ EXPECT_EQ(0u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from temporary, temporary ctor/dtor.
+ Variant<int, InstrumentType<int>> v;
+ v = InstrumentType<int>(25);
+ EXPECT_EQ(2u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(1u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from temporary, temporary ctor/dtor.
+ Variant<int, InstrumentType<int>> v(InstrumentType<int>(25));
+ EXPECT_EQ(2u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(1u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from temporary, temporary ctor/dtor.
+ Variant<int, InstrumentType<int>> v(InstrumentType<int>(25));
+
+ // Assign from temporary, temporary ctor/dtor.
+ v = InstrumentType<int>(35);
+ EXPECT_EQ(3u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(2u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(1u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from temporary, temporary ctor/dtor.
+ Variant<int, InstrumentType<int>> v(InstrumentType<int>(25));
+
+ // dtor.
+ v = 10;
+ EXPECT_EQ(2u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(2u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from temporary, temporary ctor/dtor.
+ Variant<int, InstrumentType<int>> v(InstrumentType<int>(25));
+
+ EXPECT_EQ(2u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(1u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+ EXPECT_EQ(2u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(2u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from other temporary.
+ Variant<int, InstrumentType<int>> v(TestType<int>(10));
+ EXPECT_EQ(1u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from other temporary.
+ Variant<int, InstrumentType<int>> v(TestType<int>(10));
+ // Assign from other temporary.
+ v = TestType<int>(11);
+ EXPECT_EQ(1u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(1u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from other temporary.
+ Variant<int, InstrumentType<int>> v(TestType<int>(10));
+ // Assign from empty Variant.
+ v = Variant<int, InstrumentType<int>>();
+ EXPECT_EQ(1u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(1u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ TestType<int> other(10);
+ // Construct from other.
+ Variant<int, InstrumentType<int>> v(other);
+
+ EXPECT_EQ(1u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from other temporary.
+ Variant<int, InstrumentType<int>> v(TestType<int>(0));
+ TestType<int> other(10);
+ // Assign from other.
+ v = other;
+ EXPECT_EQ(1u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(1u, InstrumentType<int>::copy_assignment_count());
+ }
+}
+
+TEST(Variant, MoveConstructor) {
+ {
+ std::unique_ptr<int> pointer = std::make_unique<int>(10);
+ Variant<std::unique_ptr<int>> v(std::move(pointer));
+ ASSERT_TRUE(v.is<std::unique_ptr<int>>());
+ EXPECT_TRUE(std::get<std::unique_ptr<int>>(v) != nullptr);
+ EXPECT_TRUE(pointer == nullptr);
+ }
+
+ {
+ Variant<std::unique_ptr<int>> a(std::make_unique<int>(10));
+ Variant<std::unique_ptr<int>> b(std::move(a));
+
+ ASSERT_TRUE(a.is<std::unique_ptr<int>>());
+ ASSERT_TRUE(b.is<std::unique_ptr<int>>());
+ EXPECT_TRUE(std::get<std::unique_ptr<int>>(a) == nullptr);
+ EXPECT_TRUE(std::get<std::unique_ptr<int>>(b) != nullptr);
+ }
+}
+
+TEST(Variant, IndexOf) {
+ Variant<int, bool, float> v1;
+
+ EXPECT_EQ(0, v1.index_of<int>());
+ EXPECT_EQ(1, v1.index_of<bool>());
+ EXPECT_EQ(2, v1.index_of<float>());
+
+ Variant<int, bool, float, int> v2;
+
+ EXPECT_EQ(0, v2.index_of<int>());
+ EXPECT_EQ(1, v2.index_of<bool>());
+ EXPECT_EQ(2, v2.index_of<float>());
+}
+
+struct Visitor {
+ int int_value = 0;
+ bool bool_value = false;
+ float float_value = 0.0;
+ bool empty_value = false;
+
+ void Visit(int value) { int_value = value; }
+ void Visit(bool value) { bool_value = value; }
+ void Visit(float value) { float_value = value; }
+ void Visit(EmptyVariant) { empty_value = true; }
+};
+
+TEST(Variant, Visit) {
+ {
+ Variant<int, bool, float> v(10);
+ EXPECT_TRUE(v.is<int>());
+
+ Visitor visitor;
+ v.Visit([&visitor](const auto& value) { visitor.Visit(value); });
+ EXPECT_EQ(10, visitor.int_value);
+
+ visitor = {};
+ v = true;
+ v.Visit([&visitor](const auto& value) { visitor.Visit(value); });
+ EXPECT_EQ(true, visitor.bool_value);
+ }
+
+ {
+ Variant<int, bool, float> v;
+ EXPECT_EQ(-1, v.index());
+
+ Visitor visitor;
+ v.Visit([&visitor](const auto& value) { visitor.Visit(value); });
+ EXPECT_TRUE(visitor.empty_value);
+ }
+
+ {
+ Variant<std::string> v("test");
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_FALSE(std::get<std::string>(v).empty());
+
+ v.Visit([](auto&& value) {
+ std::remove_reference_t<decltype(value)> empty;
+ std::swap(empty, value);
+ });
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_TRUE(std::get<std::string>(v).empty());
+ }
+}
+
+TEST(Variant, Become) {
+ {
+ Variant<int, bool, float> v;
+
+ v.Become(0);
+ EXPECT_TRUE(v.is<int>());
+
+ v.Become(1);
+ EXPECT_TRUE(v.is<bool>());
+
+ v.Become(2);
+ EXPECT_TRUE(v.is<float>());
+
+ v.Become(3);
+ EXPECT_TRUE(v.empty());
+
+ v.Become(-1);
+ EXPECT_TRUE(v.empty());
+
+ v.Become(-2);
+ EXPECT_TRUE(v.empty());
+ }
+
+ {
+ Variant<int, bool, float> v;
+
+ v.Become(0, 10);
+ ASSERT_TRUE(v.is<int>());
+ EXPECT_EQ(10, std::get<int>(v));
+
+ v.Become(1, true);
+ ASSERT_TRUE(v.is<bool>());
+ EXPECT_EQ(true, std::get<bool>(v));
+
+ v.Become(2, 2.0f);
+ ASSERT_TRUE(v.is<float>());
+ EXPECT_FLOAT_EQ(2.0f, std::get<float>(v));
+
+ v.Become(3, 10);
+ EXPECT_TRUE(v.empty());
+
+ v.Become(-1, 10);
+ EXPECT_TRUE(v.empty());
+
+ v.Become(-2, 20);
+ EXPECT_TRUE(v.empty());
+ }
+
+ {
+ Variant<std::string> v;
+
+ v.Become(0);
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_TRUE(std::get<std::string>(v).empty());
+ }
+
+ {
+ Variant<std::string> v;
+
+ v.Become(0, "test");
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_EQ("test", std::get<std::string>(v));
+ }
+
+ {
+ Variant<std::string> v("foo");
+
+ v.Become(0, "bar");
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_EQ("foo", std::get<std::string>(v));
+ }
+}
+
+TEST(Variant, Swap) {
+ {
+ Variant<std::string> a;
+ Variant<std::string> b;
+
+ std::swap(a, b);
+ EXPECT_TRUE(a.empty());
+ EXPECT_TRUE(b.empty());
+ }
+
+ {
+ Variant<std::string> a("1");
+ Variant<std::string> b;
+
+ std::swap(a, b);
+ EXPECT_TRUE(!a.empty());
+ EXPECT_TRUE(!b.empty());
+ ASSERT_TRUE(b.is<std::string>());
+ EXPECT_EQ("1", std::get<std::string>(b));
+ }
+
+ {
+ Variant<std::string> a;
+ Variant<std::string> b("1");
+
+ std::swap(a, b);
+ EXPECT_TRUE(!a.empty());
+ EXPECT_TRUE(!b.empty());
+ ASSERT_TRUE(a.is<std::string>());
+ EXPECT_EQ("1", std::get<std::string>(a));
+ }
+
+ {
+ Variant<std::string> a("1");
+ Variant<std::string> b("2");
+
+ std::swap(a, b);
+ ASSERT_TRUE(a.is<std::string>());
+ ASSERT_TRUE(b.is<std::string>());
+ EXPECT_EQ("2", std::get<std::string>(a));
+ EXPECT_EQ("1", std::get<std::string>(b));
+ }
+
+ {
+ Variant<int, std::string> a(10);
+ Variant<int, std::string> b("1");
+
+ std::swap(a, b);
+ ASSERT_TRUE(a.is<std::string>());
+ ASSERT_TRUE(b.is<int>());
+ EXPECT_EQ("1", std::get<std::string>(a));
+ EXPECT_EQ(10, std::get<int>(b));
+ }
+
+ {
+ Variant<int, std::string> a("1");
+ Variant<int, std::string> b(10);
+
+ std::swap(a, b);
+ ASSERT_TRUE(a.is<int>());
+ ASSERT_TRUE(b.is<std::string>());
+ EXPECT_EQ(10, std::get<int>(a));
+ EXPECT_EQ("1", std::get<std::string>(b));
+ }
+}
+
+TEST(Variant, Get) {
+ {
+ Variant<int, bool, float, int> v;
+
+ EXPECT_EQ(nullptr, &std::get<int>(v));
+ EXPECT_EQ(nullptr, &std::get<bool>(v));
+ EXPECT_EQ(nullptr, &std::get<float>(v));
+ EXPECT_EQ(nullptr, &std::get<0>(v));
+ EXPECT_EQ(nullptr, &std::get<1>(v));
+ EXPECT_EQ(nullptr, &std::get<2>(v));
+ EXPECT_EQ(nullptr, &std::get<3>(v));
+ }
+
+ {
+ Variant<int, bool, float, int> v;
+ v = 9;
+ ASSERT_TRUE(v.is<int>())
+ << "Expected type " << v.index_of<int>() << " got type " << v.index();
+ EXPECT_EQ(9, std::get<int>(v));
+ EXPECT_EQ(9, std::get<0>(v));
+
+ std::get<int>(v) = 10;
+ EXPECT_EQ(10, std::get<int>(v));
+ EXPECT_EQ(10, std::get<0>(v));
+
+ std::get<0>(v) = 11;
+ EXPECT_EQ(11, std::get<int>(v));
+ EXPECT_EQ(11, std::get<0>(v));
+
+ std::get<3>(v) = 12;
+ EXPECT_EQ(12, std::get<int>(v));
+ EXPECT_EQ(12, std::get<3>(v));
+ }
+
+ {
+ Variant<int, bool, float, int> v;
+ v = false;
+ ASSERT_TRUE(v.is<bool>())
+ << "Expected type " << v.index_of<bool>() << " got type " << v.index();
+ EXPECT_EQ(false, std::get<bool>(v));
+ EXPECT_EQ(false, std::get<1>(v));
+
+ std::get<bool>(v) = true;
+ EXPECT_EQ(true, std::get<bool>(v));
+ EXPECT_EQ(true, std::get<1>(v));
+
+ std::get<bool>(v) = false;
+ EXPECT_EQ(false, std::get<bool>(v));
+ EXPECT_EQ(false, std::get<1>(v));
+
+ std::get<1>(v) = true;
+ EXPECT_EQ(true, std::get<bool>(v));
+ EXPECT_EQ(true, std::get<1>(v));
+
+ std::get<1>(v) = false;
+ EXPECT_EQ(false, std::get<bool>(v));
+ EXPECT_EQ(false, std::get<1>(v));
+ }
+
+ {
+ Variant<int, bool, float, int> v;
+ v = 1.0f;
+ ASSERT_TRUE(v.is<float>())
+ << "Expected type " << v.index_of<float>() << " got type " << v.index();
+ EXPECT_EQ(2, v.index());
+ EXPECT_FLOAT_EQ(1.0, std::get<float>(v));
+ EXPECT_FLOAT_EQ(1.0, std::get<2>(v));
+
+ std::get<float>(v) = 1.1;
+ EXPECT_FLOAT_EQ(1.1, std::get<float>(v));
+ EXPECT_FLOAT_EQ(1.1, std::get<2>(v));
+
+ std::get<float>(v) = -3.0;
+ EXPECT_FLOAT_EQ(-3.0, std::get<float>(v));
+ EXPECT_FLOAT_EQ(-3.0, std::get<2>(v));
+
+ std::get<2>(v) = 1.1;
+ EXPECT_FLOAT_EQ(1.1, std::get<float>(v));
+ EXPECT_FLOAT_EQ(1.1, std::get<2>(v));
+
+ std::get<2>(v) = -3.0;
+ EXPECT_FLOAT_EQ(-3.0, std::get<float>(v));
+ EXPECT_FLOAT_EQ(-3.0, std::get<2>(v));
+ }
+
+ {
+ Variant<std::unique_ptr<int>> v(std::make_unique<int>(10));
+ std::unique_ptr<int> pointer = std::move(std::get<std::unique_ptr<int>>(v));
+ ASSERT_FALSE(v.empty());
+ EXPECT_TRUE(pointer != nullptr);
+ EXPECT_TRUE(std::get<std::unique_ptr<int>>(v) == nullptr);
+ }
+
+ {
+ Variant<std::string> v("test");
+ std::string s = std::get<std::string>(std::move(v));
+ EXPECT_EQ("test", s);
+ }
+}
+
+TEST(Variant, IfAnyOf) {
+ {
+ Variant<int, float> v(10);
+ ASSERT_TRUE(v.is<int>());
+
+ bool b = false;
+ EXPECT_TRUE(IfAnyOf<int>::Get(&v, &b));
+ EXPECT_TRUE(b);
+
+ float f = 0.0f;
+ EXPECT_TRUE((IfAnyOf<int, float>::Get(&v, &f)));
+ EXPECT_FLOAT_EQ(10.f, f);
+ }
+
+ {
+ const Variant<int, float> v(10);
+ ASSERT_TRUE(v.is<int>());
+
+ bool b = false;
+ EXPECT_TRUE(IfAnyOf<int>::Get(&v, &b));
+ EXPECT_TRUE(b);
+
+ float f = 0.0f;
+ EXPECT_TRUE((IfAnyOf<int, float>::Get(&v, &f)));
+ EXPECT_FLOAT_EQ(10.f, f);
+ }
+
+ {
+ Variant<int, float> v(10);
+ ASSERT_TRUE(v.is<int>());
+
+ bool b = false;
+ EXPECT_TRUE(IfAnyOf<int>::Call(&v, [&b](const auto& value) { b = value; }));
+ EXPECT_TRUE(b);
+
+ float f = 0.0f;
+ EXPECT_TRUE((
+ IfAnyOf<int, float>::Call(&v, [&f](const auto& value) { f = value; })));
+ EXPECT_FLOAT_EQ(10.f, f);
+ }
+
+ {
+ Variant<std::unique_ptr<int>, int> v(std::make_unique<int>(10));
+ ASSERT_TRUE(v.is<std::unique_ptr<int>>());
+ const int* original_v = std::get<std::unique_ptr<int>>(v).get();
+
+ std::unique_ptr<int> u(std::make_unique<int>(20));
+
+ EXPECT_TRUE(IfAnyOf<std::unique_ptr<int>>::Take(&v, &u));
+ ASSERT_TRUE(v.is<std::unique_ptr<int>>());
+ EXPECT_TRUE(std::get<std::unique_ptr<int>>(v) == nullptr);
+ EXPECT_EQ(u.get(), original_v);
+ }
+
+ {
+ Variant<std::unique_ptr<DerivedType>, int> v(
+ std::make_unique<DerivedType>(10));
+ ASSERT_TRUE(v.is<std::unique_ptr<DerivedType>>());
+ const DerivedType* original_v =
+ std::get<std::unique_ptr<DerivedType>>(v).get();
+
+ std::unique_ptr<BaseType> u(std::make_unique<BaseType>(20));
+
+ EXPECT_TRUE(IfAnyOf<std::unique_ptr<DerivedType>>::Take(&v, &u));
+ ASSERT_TRUE(v.is<std::unique_ptr<DerivedType>>());
+ EXPECT_TRUE(std::get<std::unique_ptr<DerivedType>>(v) == nullptr);
+ EXPECT_EQ(u.get(), original_v);
+ }
+
+ {
+ Variant<std::unique_ptr<int>, int> v(std::make_unique<int>(10));
+ ASSERT_TRUE(v.is<std::unique_ptr<int>>());
+ const int* original_v = std::get<std::unique_ptr<int>>(v).get();
+
+ std::unique_ptr<int> u(std::make_unique<int>(20));
+
+ EXPECT_TRUE(IfAnyOf<std::unique_ptr<int>>::Call(
+ &v, [&u](auto&& value) { u = std::move(value); }));
+ ASSERT_TRUE(v.is<std::unique_ptr<int>>());
+ EXPECT_TRUE(std::get<std::unique_ptr<int>>(v) == nullptr);
+ EXPECT_EQ(u.get(), original_v);
+ }
+
+ {
+ Variant<int, bool, float> v(true);
+ ASSERT_TRUE(v.is<bool>());
+
+ float f = 0.f;
+ EXPECT_FALSE((IfAnyOf<int, float>::Get(&v, &f)));
+ EXPECT_FLOAT_EQ(0.f, f);
+ }
+
+ {
+ Variant<std::string, int> v("foo");
+ ASSERT_TRUE(v.is<std::string>());
+
+ std::string s = "bar";
+ EXPECT_TRUE(IfAnyOf<std::string>::Swap(&v, &s));
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_EQ("bar", std::get<std::string>(v));
+ EXPECT_EQ("foo", s);
+ }
+
+ {
+ Variant<std::string, const char*> v(static_cast<const char*>("foo"));
+ ASSERT_TRUE(v.is<const char*>());
+
+ std::string s = "bar";
+ EXPECT_TRUE((IfAnyOf<std::string, const char*>::Take(&v, &s)));
+ ASSERT_TRUE(v.is<const char*>());
+ EXPECT_EQ("foo", std::get<const char*>(v));
+ EXPECT_EQ("foo", s);
+
+ v = std::string("bar");
+ ASSERT_TRUE(v.is<std::string>());
+
+ EXPECT_TRUE((IfAnyOf<std::string, const char*>::Take(&v, &s)));
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_EQ("bar", s);
+ }
+
+ {
+ Variant<std::string, const char*> v;
+ ASSERT_TRUE(v.empty());
+
+ std::string s = "bar";
+ EXPECT_FALSE((IfAnyOf<std::string, const char*>::Take(&v, &s)));
+ EXPECT_EQ("bar", s);
+ }
+
+ {
+ Variant<std::string, const char*> v(static_cast<const char*>("test"));
+ ASSERT_TRUE(v.is<const char*>());
+
+ std::string s;
+ EXPECT_FALSE(IfAnyOf<>::Take(&v, &s));
+ EXPECT_TRUE(s.empty());
+ }
+}
+
+TEST(Variant, ConstVolatile) {
+ {
+ Variant<const int> v(10);
+ ASSERT_TRUE(v.is<const int>());
+ EXPECT_EQ(10, std::get<const int>(v));
+ }
+
+ {
+ Variant<const std::string> v("test");
+ ASSERT_TRUE(v.is<const std::string>());
+ EXPECT_EQ("test", std::get<const std::string>(v));
+ }
+
+ {
+ Variant<volatile int, std::string> v(10);
+ ASSERT_TRUE(v.is<volatile int>());
+ EXPECT_EQ(10, std::get<volatile int>(v));
+ }
+}
+
+TEST(Variant, HasType) {
+ EXPECT_TRUE((detail::HasType<int, int, float, bool>::value));
+ EXPECT_FALSE((detail::HasType<char, int, float, bool>::value));
+ EXPECT_FALSE(detail::HasType<>::value);
+
+ EXPECT_TRUE((detail::HasTypeIgnoreRef<int&, int, float, bool>::value));
+ EXPECT_FALSE((detail::HasTypeIgnoreRef<char&, int, float, bool>::value));
+}
+
+TEST(Variant, Set) {
+ EXPECT_TRUE((detail::Set<int, bool, float>::template IsSubset<int, bool,
+ float>::value));
+ EXPECT_TRUE(
+ (detail::Set<int, bool, float>::template IsSubset<bool, float>::value));
+ EXPECT_TRUE((detail::Set<int, bool, float>::template IsSubset<float>::value));
+ EXPECT_TRUE((detail::Set<int, bool, float>::template IsSubset<>::value));
+
+ EXPECT_FALSE(
+ (detail::Set<int, bool, float>::template IsSubset<int, bool, float,
+ char>::value));
+ EXPECT_FALSE((detail::Set<int, bool, float>::template IsSubset<bool, float,
+ char>::value));
+ EXPECT_FALSE(
+ (detail::Set<int, bool, float>::template IsSubset<float, char>::value));
+ EXPECT_FALSE((detail::Set<int, bool, float>::template IsSubset<char>::value));
+
+ EXPECT_TRUE(detail::Set<>::template IsSubset<>::value);
+ EXPECT_FALSE(detail::Set<>::template IsSubset<int>::value);
+ EXPECT_FALSE((detail::Set<>::template IsSubset<int, float>::value));
+}