зеркало из https://github.com/microsoft/IPC.Bond.git
Родитель
03a8c06660
Коммит
cdcd3a6652
|
@ -1,252 +1,45 @@
|
|||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# DNX
|
||||
project.lock.json
|
||||
artifacts/
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
/ipch
|
||||
*.gch
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
# Nuget packages
|
||||
/packages
|
||||
|
||||
# Visual Studio files
|
||||
/.vs
|
||||
*.VC.opendb
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
*.vcxproj.user
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
# Build folders
|
||||
**/x64
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/packages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/packages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/packages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignoreable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
node_modules/
|
||||
orleans.codegen.cs
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
# Generated files
|
||||
UnitTestsManaged/generated
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
[submodule "IPC"]
|
||||
path = IPC
|
||||
url = https://github.com/Microsoft/IPC
|
||||
[submodule "bond"]
|
||||
path = bond
|
||||
url = https://github.com/Microsoft/bond.git
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 1f34d78408cd68e234305682a96f60ec5dd535af
|
|
@ -0,0 +1,52 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25420.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests", "UnitTests\Build\UnitTests.vcxproj", "{2A3B9657-1D10-4A71-AA21-62AD4106CED4}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Native", "Native\Build\Native.vcxproj", "{2030ED0D-4667-4299-87CD-ACE298BDF56D}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Interop", "Interop\Build\Interop.vcxproj", "{A13012C1-76DE-4D1D-A58B-2361D2BE8F65}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Managed", "Managed\Build\Managed.vcxproj", "{705098F7-93DC-4954-8165-FDDCD66231F0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Transport", "Transport\Transport.csproj", "{BDAAAAAD-21A8-446E-840B-68718C49E7D4}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestsManaged", "UnitTestsManaged\UnitTestsManaged.csproj", "{BDFCB3AD-21A8-446E-840B-68718C49E7D4}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{2A3B9657-1D10-4A71-AA21-62AD4106CED4}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{2A3B9657-1D10-4A71-AA21-62AD4106CED4}.Debug|x64.Build.0 = Debug|x64
|
||||
{2A3B9657-1D10-4A71-AA21-62AD4106CED4}.Release|x64.ActiveCfg = Release|x64
|
||||
{2A3B9657-1D10-4A71-AA21-62AD4106CED4}.Release|x64.Build.0 = Release|x64
|
||||
{2030ED0D-4667-4299-87CD-ACE298BDF56D}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{2030ED0D-4667-4299-87CD-ACE298BDF56D}.Debug|x64.Build.0 = Debug|x64
|
||||
{2030ED0D-4667-4299-87CD-ACE298BDF56D}.Release|x64.ActiveCfg = Release|x64
|
||||
{2030ED0D-4667-4299-87CD-ACE298BDF56D}.Release|x64.Build.0 = Release|x64
|
||||
{A13012C1-76DE-4D1D-A58B-2361D2BE8F65}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{A13012C1-76DE-4D1D-A58B-2361D2BE8F65}.Debug|x64.Build.0 = Debug|x64
|
||||
{A13012C1-76DE-4D1D-A58B-2361D2BE8F65}.Release|x64.ActiveCfg = Release|x64
|
||||
{A13012C1-76DE-4D1D-A58B-2361D2BE8F65}.Release|x64.Build.0 = Release|x64
|
||||
{705098F7-93DC-4954-8165-FDDCD66231F0}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{705098F7-93DC-4954-8165-FDDCD66231F0}.Debug|x64.Build.0 = Debug|x64
|
||||
{705098F7-93DC-4954-8165-FDDCD66231F0}.Release|x64.ActiveCfg = Release|x64
|
||||
{705098F7-93DC-4954-8165-FDDCD66231F0}.Release|x64.Build.0 = Release|x64
|
||||
{BDAAAAAD-21A8-446E-840B-68718C49E7D4}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{BDAAAAAD-21A8-446E-840B-68718C49E7D4}.Debug|x64.Build.0 = Debug|x64
|
||||
{BDAAAAAD-21A8-446E-840B-68718C49E7D4}.Release|x64.ActiveCfg = Release|x64
|
||||
{BDAAAAAD-21A8-446E-840B-68718C49E7D4}.Release|x64.Build.0 = Release|x64
|
||||
{BDFCB3AD-21A8-446E-840B-68718C49E7D4}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{BDFCB3AD-21A8-446E-840B-68718C49E7D4}.Debug|x64.Build.0 = Debug|x64
|
||||
{BDFCB3AD-21A8-446E-840B-68718C49E7D4}.Release|x64.ActiveCfg = Release|x64
|
||||
{BDFCB3AD-21A8-446E-840B-68718C49E7D4}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
Двоичный файл не отображается.
|
@ -0,0 +1,75 @@
|
|||
#pragma once
|
||||
|
||||
#include <IPC/detail/Accept.h>
|
||||
#include <IPC/Policies/ErrorHandler.h>
|
||||
#include <IPC/ComponentCollection.h>
|
||||
#include "Acceptor.h"
|
||||
#include "Client.h"
|
||||
#include "Server.h"
|
||||
#include "DefaultTraits.h"
|
||||
#include <bond/core/bond_const_enum.h>
|
||||
#include <memory>
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
template <typename Request, typename Response, typename Traits = DefaultTraits, typename HandlerFactory, typename ErrorHandler = typename Traits::ErrorHandler>
|
||||
auto AcceptServers(
|
||||
const char* name,
|
||||
HandlerFactory&& handlerFactory,
|
||||
bond::ProtocolType protocol = bond::ProtocolType::COMPACT_PROTOCOL,
|
||||
bool marshal = true,
|
||||
ChannelSettings<Traits> channelSettings = {},
|
||||
std::size_t minBlobSize = 0,
|
||||
std::size_t hostInfoMemorySize = 0,
|
||||
ErrorHandler&& errorHandler = {})
|
||||
{
|
||||
return IPC::detail::Accept<ServerAcceptor<Request, Response, Traits>>(
|
||||
name,
|
||||
std::make_shared<ServerCollection<Server<Request, Response, Traits>>>(),
|
||||
[protocol, marshal, minBlobSize, handlerFactory = std::forward<HandlerFactory>(handlerFactory)](auto&& connection, auto&& closeHandler) mutable
|
||||
{
|
||||
return MakeServer<Request, Response, Traits>(
|
||||
std::move(connection), handlerFactory, std::forward<decltype(closeHandler)>(closeHandler), protocol, marshal, minBlobSize);
|
||||
},
|
||||
std::forward<ErrorHandler>(errorHandler),
|
||||
std::move(channelSettings),
|
||||
hostInfoMemorySize);
|
||||
}
|
||||
|
||||
|
||||
template <typename Request, typename Response, typename Traits = DefaultTraits, typename ErrorHandler = typename Traits::ErrorHandler>
|
||||
auto AcceptClients(
|
||||
const char* name,
|
||||
bond::ProtocolType protocol = bond::ProtocolType::COMPACT_PROTOCOL,
|
||||
bool marshal = true,
|
||||
ChannelSettings<Traits> channelSettings = {},
|
||||
std::size_t minBlobSize = 0,
|
||||
std::size_t hostInfoMemorySize = 0,
|
||||
ErrorHandler&& errorHandler = {},
|
||||
typename Traits::TransactionManagerFactory transactionManagerFactory = {})
|
||||
{
|
||||
using Client = Client<Request, Response, Traits>;
|
||||
|
||||
return IPC::detail::Accept<ClientAcceptor<Request, Response, Traits>>(
|
||||
name,
|
||||
std::make_shared<ClientCollection<Client>>(),
|
||||
[protocol, marshal, minBlobSize, transactionManagerFactory = std::move(transactionManagerFactory)](auto&& connection, auto&& closeHandler)
|
||||
{
|
||||
return MakeClient<Request, Response, Traits>(
|
||||
std::move(connection),
|
||||
std::forward<decltype(closeHandler)>(closeHandler),
|
||||
protocol,
|
||||
marshal,
|
||||
minBlobSize,
|
||||
transactionManagerFactory(IPC::detail::Identity<typename Client::TransactionManager>{}));
|
||||
},
|
||||
std::forward<ErrorHandler>(errorHandler),
|
||||
std::move(channelSettings),
|
||||
hostInfoMemorySize);
|
||||
}
|
||||
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include <IPC/Acceptor.h>
|
||||
#include "detail/ComponentBase.h"
|
||||
#include "DefaultTraits.h"
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
template <typename Request, typename Response, typename Traits = DefaultTraits>
|
||||
using ClientAcceptor = detail::BufferComponent<ClientAcceptor, Request, Response, Traits>;
|
||||
|
||||
template <typename Request, typename Response, typename Traits = DefaultTraits>
|
||||
using ServerAcceptor = detail::BufferComponent<ServerAcceptor, Request, Response, Traits>;
|
||||
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include "detail/BlobHolder.h"
|
||||
#include <type_traits>
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include <IPC/SharedMemory.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
template <typename Blob>
|
||||
bond::blob BlobCast(Blob&& from, std::shared_ptr<SharedMemory> memory)
|
||||
{
|
||||
auto data = from.data();
|
||||
auto size = from.size();
|
||||
|
||||
assert(memory && memory->Contains(data) && memory->Contains(data + size - 1));
|
||||
|
||||
return{
|
||||
boost::shared_ptr<const char[]>{ data, detail::BlobHolder<std::decay_t<Blob>>{ std::forward<Blob>(from), std::move(memory) } },
|
||||
static_cast<std::uint32_t>(size) };
|
||||
}
|
||||
|
||||
template <typename Blob>
|
||||
Blob BlobCast(const bond::blob& from)
|
||||
{
|
||||
if (auto deleter = boost::get_deleter<detail::BlobHolder<Blob>>(bond::blob_cast<detail::BlobHook>(from)))
|
||||
{
|
||||
const auto& blob = deleter->m_blob;
|
||||
assert(blob);
|
||||
|
||||
return blob.GetRange(std::distance(blob.data(), from.content()), from.size());
|
||||
}
|
||||
|
||||
return{};
|
||||
}
|
||||
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,368 @@
|
|||
#pragma once
|
||||
|
||||
#include "BufferPoolFwd.h"
|
||||
#include "IPC/SharedMemory.h"
|
||||
#include "IPC/detail/LockFree/Queue.h"
|
||||
#include <boost/interprocess/containers/vector.hpp>
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
template <template <typename> typename QueueT>
|
||||
class BufferPool<QueueT>::Impl
|
||||
{
|
||||
public:
|
||||
using Queue = QueueT<SharedMemory::SharedPtr<Data>>;
|
||||
|
||||
explicit Impl(std::shared_ptr<SharedMemory> memory)
|
||||
: m_memory{ std::move(memory) },
|
||||
m_queue{ m_memory->MakeShared<Queue>(anonymous_instance, m_memory->GetAllocator<char>()) }
|
||||
{}
|
||||
|
||||
SharedMemory::SharedPtr<Data> Take()
|
||||
{
|
||||
if (auto data = m_queue->Pop())
|
||||
{
|
||||
return std::move(*data);
|
||||
}
|
||||
|
||||
return m_memory->MakeShared<Data>(anonymous_instance, m_memory->GetAllocator<char>());
|
||||
}
|
||||
|
||||
const auto& GetQueue() const
|
||||
{
|
||||
return m_queue;
|
||||
}
|
||||
|
||||
const auto& GetMemory() const
|
||||
{
|
||||
return m_memory;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<SharedMemory> m_memory;
|
||||
SharedMemory::SharedPtr<Queue> m_queue;
|
||||
};
|
||||
|
||||
|
||||
template <template <typename> typename QueueT>
|
||||
struct BufferPool<QueueT>::Data
|
||||
{
|
||||
explicit Data(const SharedMemory::Allocator<char>& allocator)
|
||||
: m_blob{ allocator },
|
||||
m_buffer{ allocator }
|
||||
{}
|
||||
|
||||
boost::interprocess::vector<char, SharedMemory::Allocator<char>> m_blob;
|
||||
boost::interprocess::vector<ConstBlob, SharedMemory::Allocator<ConstBlob>> m_buffer;
|
||||
};
|
||||
|
||||
|
||||
template <template <typename> typename QueueT>
|
||||
class BufferPool<QueueT>::ItemBase
|
||||
{
|
||||
public:
|
||||
~ItemBase()
|
||||
{
|
||||
if (m_data.unique())
|
||||
{
|
||||
m_data->m_blob.clear();
|
||||
m_data->m_buffer.clear();
|
||||
m_queue->Push(std::move(m_data));
|
||||
}
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return m_data && m_queue;
|
||||
}
|
||||
|
||||
protected:
|
||||
ItemBase() = default;
|
||||
|
||||
ItemBase(SharedMemory::SharedPtr<Data> data, SharedMemory::SharedPtr<typename Impl::Queue> queue)
|
||||
: m_data{ std::move(data) },
|
||||
m_queue{ std::move(queue) }
|
||||
{}
|
||||
|
||||
const auto& GetBlob() const
|
||||
{
|
||||
return m_data->m_blob;
|
||||
}
|
||||
|
||||
auto& GetBlob()
|
||||
{
|
||||
return m_data->m_blob;
|
||||
}
|
||||
|
||||
const auto& GetBuffer() const
|
||||
{
|
||||
return m_data->m_buffer;
|
||||
}
|
||||
|
||||
auto& GetBuffer()
|
||||
{
|
||||
return m_data->m_buffer;
|
||||
}
|
||||
|
||||
bool operator==(const ItemBase& other) const
|
||||
{
|
||||
return m_data == other.m_data && m_queue == other.m_queue;
|
||||
}
|
||||
|
||||
private:
|
||||
SharedMemory::SharedPtr<Data> m_data;
|
||||
SharedMemory::SharedPtr<typename Impl::Queue> m_queue;
|
||||
};
|
||||
|
||||
|
||||
template <template <typename> typename QueueT>
|
||||
class BufferPool<QueueT>::Blob : public ItemBase
|
||||
{
|
||||
friend BufferPool;
|
||||
|
||||
using ItemBase::ItemBase;
|
||||
|
||||
public:
|
||||
Blob() = default;
|
||||
|
||||
Blob(const Blob& other) = delete;
|
||||
Blob& operator=(const Blob& other) = delete;
|
||||
|
||||
Blob(Blob&& other) = default;
|
||||
Blob& operator=(Blob&& other) = default;
|
||||
|
||||
decltype(auto) operator*() const
|
||||
{
|
||||
return this->GetBlob();
|
||||
}
|
||||
|
||||
decltype(auto) operator*()
|
||||
{
|
||||
return this->GetBlob();
|
||||
}
|
||||
|
||||
auto operator->() const
|
||||
{
|
||||
return &this->GetBlob();
|
||||
}
|
||||
|
||||
auto operator->()
|
||||
{
|
||||
return &this->GetBlob();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <template <typename> typename QueueT>
|
||||
class BufferPool<QueueT>::ConstBlob : public ItemBase
|
||||
{
|
||||
using Iterator = decltype(std::declval<Data>().m_blob.cbegin());
|
||||
|
||||
public:
|
||||
ConstBlob() = default;
|
||||
|
||||
ConstBlob(const Blob& blob)
|
||||
: ConstBlob{ blob, blob->begin(), blob->end() }
|
||||
{}
|
||||
|
||||
auto begin() const
|
||||
{
|
||||
return m_begin;
|
||||
}
|
||||
|
||||
auto end() const
|
||||
{
|
||||
return m_end;
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
return std::distance(m_begin, m_end);
|
||||
}
|
||||
|
||||
const char* data() const
|
||||
{
|
||||
return std::addressof(*m_begin);
|
||||
}
|
||||
|
||||
ConstBlob GetRange(std::size_t offset, std::size_t count) const
|
||||
{
|
||||
if (offset + count > size())
|
||||
{
|
||||
throw std::out_of_range{ "Out of buffer range." };
|
||||
}
|
||||
|
||||
auto begin = std::next(m_begin, offset);
|
||||
|
||||
return{ *this, begin, std::next(begin, count) };
|
||||
}
|
||||
|
||||
private:
|
||||
ConstBlob(ItemBase blob, Iterator begin, Iterator end)
|
||||
: ItemBase{ std::move(blob) },
|
||||
m_begin{ begin },
|
||||
m_end{ end }
|
||||
{}
|
||||
|
||||
Iterator m_begin{};
|
||||
Iterator m_end{};
|
||||
};
|
||||
|
||||
|
||||
template <template <typename> typename QueueT>
|
||||
class BufferPool<QueueT>::Buffer : public ItemBase
|
||||
{
|
||||
friend BufferPool;
|
||||
|
||||
using ItemBase::ItemBase;
|
||||
|
||||
public:
|
||||
Buffer() = default;
|
||||
|
||||
Buffer(const Buffer& other) = delete;
|
||||
Buffer& operator=(const Buffer& other) = delete;
|
||||
|
||||
Buffer(Buffer&& other) = default;
|
||||
Buffer& operator=(Buffer&& other) = default;
|
||||
|
||||
decltype(auto) operator*() const
|
||||
{
|
||||
return this->GetBuffer();
|
||||
}
|
||||
|
||||
decltype(auto) operator*()
|
||||
{
|
||||
return this->GetBuffer();
|
||||
}
|
||||
|
||||
auto operator->() const
|
||||
{
|
||||
return &this->GetBuffer();
|
||||
}
|
||||
|
||||
auto operator->()
|
||||
{
|
||||
return &this->GetBuffer();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <template <typename> typename QueueT>
|
||||
class BufferPool<QueueT>::ConstBuffer : public ItemBase
|
||||
{
|
||||
using BlobIterator = decltype(std::declval<Data>().m_buffer.cbegin());
|
||||
|
||||
public:
|
||||
struct Range
|
||||
{
|
||||
Range() = default;
|
||||
|
||||
Range(ConstBuffer buffer, BlobIterator firstBlob, std::size_t firstOffset, BlobIterator lastBlob, std::size_t lastOffset)
|
||||
: m_firstBlob{ std::move(firstBlob) },
|
||||
m_firstOffset{ firstOffset },
|
||||
m_lastBlob{ std::move(lastBlob) },
|
||||
m_lastOffset{ lastOffset },
|
||||
m_buffer{ std::move(buffer) }
|
||||
{}
|
||||
|
||||
bool IsEmpty() const
|
||||
{
|
||||
return m_firstBlob == m_lastBlob && m_firstOffset == m_lastOffset;
|
||||
}
|
||||
|
||||
BlobIterator m_firstBlob;
|
||||
std::size_t m_firstOffset{ 0 };
|
||||
BlobIterator m_lastBlob;
|
||||
std::size_t m_lastOffset{ 0 };
|
||||
ConstBuffer m_buffer;
|
||||
};
|
||||
|
||||
|
||||
ConstBuffer() = default;
|
||||
|
||||
ConstBuffer(Buffer buffer)
|
||||
: ItemBase{ std::move(buffer) }
|
||||
{}
|
||||
|
||||
auto begin() const
|
||||
{
|
||||
return this->GetBuffer().begin();
|
||||
}
|
||||
|
||||
auto end() const
|
||||
{
|
||||
return this->GetBuffer().end();
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
std::size_t size = 0;
|
||||
|
||||
for (const auto& blob : this->GetBuffer())
|
||||
{
|
||||
size += blob.size();
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
bool operator==(const ConstBuffer& other) const
|
||||
{
|
||||
return ItemBase::operator==(other);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <template <typename> typename QueueT>
|
||||
BufferPool<QueueT>::BufferPool(std::shared_ptr<SharedMemory> memory)
|
||||
: m_impl{ std::make_shared<Impl>(std::move(memory)) }
|
||||
{}
|
||||
|
||||
template <template <typename> typename QueueT>
|
||||
auto BufferPool<QueueT>::TakeBlob() -> Blob
|
||||
{
|
||||
return{ m_impl->Take(), m_impl->GetQueue() };
|
||||
}
|
||||
|
||||
template <template <typename> typename QueueT>
|
||||
auto BufferPool<QueueT>::TakeBuffer() -> Buffer
|
||||
{
|
||||
return{ m_impl->Take(), m_impl->GetQueue() };
|
||||
}
|
||||
|
||||
template <template <typename> typename QueueT>
|
||||
const std::shared_ptr<SharedMemory>& BufferPool<QueueT>::GetMemory() const
|
||||
{
|
||||
return m_impl->GetMemory();
|
||||
}
|
||||
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename T>
|
||||
class DefaultBufferPoolQueue : public IPC::detail::LockFree::Queue<T, SharedMemory::Allocator<char>>
|
||||
{
|
||||
public:
|
||||
using Queue::Queue;
|
||||
};
|
||||
|
||||
template <typename BufferPool>
|
||||
class ConstBuffer : public BufferPool::ConstBuffer
|
||||
{
|
||||
public:
|
||||
using BufferPool::ConstBuffer::ConstBuffer;
|
||||
|
||||
ConstBuffer() = default;
|
||||
|
||||
ConstBuffer(const typename BufferPool::ConstBuffer& buffer)
|
||||
: BufferPool::ConstBuffer{ buffer }
|
||||
{}
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
|
||||
class SharedMemory;
|
||||
|
||||
namespace Bond
|
||||
{
|
||||
template <template <typename> typename QueueT>
|
||||
class BufferPool
|
||||
{
|
||||
public:
|
||||
class Blob;
|
||||
class ConstBlob;
|
||||
|
||||
class Buffer;
|
||||
class ConstBuffer;
|
||||
|
||||
explicit BufferPool(std::shared_ptr<SharedMemory> memory);
|
||||
|
||||
Blob TakeBlob();
|
||||
|
||||
Buffer TakeBuffer();
|
||||
|
||||
const std::shared_ptr<SharedMemory>& GetMemory() const;
|
||||
|
||||
private:
|
||||
struct Data;
|
||||
class ItemBase;
|
||||
|
||||
class Impl;
|
||||
|
||||
std::shared_ptr<Impl> m_impl;
|
||||
};
|
||||
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename T>
|
||||
class DefaultBufferPoolQueue;
|
||||
|
||||
template <typename BufferPool>
|
||||
class ConstBuffer;
|
||||
|
||||
} // detail
|
||||
|
||||
|
||||
using DefaultBufferPool = BufferPool<detail::DefaultBufferPoolQueue>;
|
||||
|
||||
using DefaultConstBuffer = detail::ConstBuffer<DefaultBufferPool>;
|
||||
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,85 @@
|
|||
#pragma once
|
||||
|
||||
#include "detail/ComponentBase.h"
|
||||
#include <IPC/Client.h>
|
||||
#include "DefaultTraits.h"
|
||||
#include <bond/core/bond_const_enum.h>
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
template <typename Request, typename Response, typename Traits = DefaultTraits>
|
||||
class Client : public detail::ComponentBase<IPC::Client, Request, Response, Traits>
|
||||
{
|
||||
using Base = detail::ComponentBase<IPC::Client, Request, Response, Traits>;
|
||||
|
||||
public:
|
||||
template <typename CloseHandler>
|
||||
Client(
|
||||
typename Base::BufferPoolHolder pools,
|
||||
typename Base::Serializer serializer,
|
||||
std::unique_ptr<typename Base::Connection> connection,
|
||||
CloseHandler&& closeHandler,
|
||||
typename Base::TransactionManager transactionManager = {})
|
||||
: Base{ std::move(pools), std::move(serializer), std::move(connection), std::forward<CloseHandler>(closeHandler), std::move(transactionManager) }
|
||||
{}
|
||||
|
||||
template <typename Callback, typename... TransactionArgs, typename U = Response, std::enable_if_t<!std::is_void<U>::value>* = nullptr,
|
||||
decltype(std::declval<Callback>()(std::declval<std::future<U>>()))* = nullptr>
|
||||
void operator()(const Request& request, Callback&& callback, TransactionArgs&&... transactionArgs)
|
||||
{
|
||||
Base::operator()(
|
||||
this->Serialize(request),
|
||||
[serializer = static_cast<typename Base::Serializer&>(*this), callback = std::forward<Callback>(callback)](typename Traits::BufferPool::ConstBuffer&& buffer) mutable
|
||||
{
|
||||
callback(serializer.template Deserialize<Response>(std::move(buffer)));
|
||||
},
|
||||
std::forward<TransactionArgs>(transactionArgs)...);
|
||||
}
|
||||
|
||||
template <typename U = Response, std::enable_if_t<std::is_void<U>::value>* = nullptr>
|
||||
void operator()(const Request& request)
|
||||
{
|
||||
Base::operator()(this->Serialize(request));
|
||||
}
|
||||
|
||||
template <typename... TransactionArgs, typename U = Response, std::enable_if_t<!std::is_void<U>::value>* = nullptr>
|
||||
std::future<Response> operator()(const Request& request, TransactionArgs&&... transactionArgs)
|
||||
{
|
||||
std::packaged_task<Response(typename Traits::BufferPool::ConstBuffer&&)> callback{
|
||||
[serializer = static_cast<typename Base::Serializer&>(*this)](typename Traits::BufferPool::ConstBuffer&& buffer) mutable
|
||||
{
|
||||
Response response;
|
||||
serializer.Deserialize(std::move(buffer), response);
|
||||
return response;
|
||||
} };
|
||||
|
||||
auto result = callback.get_future();
|
||||
|
||||
Base::operator()(this->Serialize(request), std::move(callback), std::forward<TransactionArgs>(transactionArgs)...);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename Request, typename Response, typename Traits = DefaultTraits, typename CloseHandler>
|
||||
auto MakeClient(
|
||||
std::unique_ptr<typename Client<Request, Response, Traits>::Connection> connection,
|
||||
CloseHandler&& closeHandler,
|
||||
bond::ProtocolType protocol = bond::ProtocolType::COMPACT_PROTOCOL,
|
||||
bool marshal = true,
|
||||
std::size_t minBlobSize = 0,
|
||||
typename Client<Request, Response, Traits>::TransactionManager transactionManager = {})
|
||||
{
|
||||
auto pools = detail::MakeBufferPoolHolder<typename Traits::BufferPool>(*connection);
|
||||
typename Traits::Serializer serializer{ protocol, marshal, pools.GetOutputPool(), pools.GetInputPool()->GetMemory(), minBlobSize };
|
||||
|
||||
return std::make_unique<Client<Request, Response, Traits>>(
|
||||
std::move(pools), std::move(serializer), std::move(connection), std::forward<CloseHandler>(closeHandler), std::move(transactionManager));
|
||||
}
|
||||
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,88 @@
|
|||
#pragma once
|
||||
|
||||
#include <IPC/detail/Connect.h>
|
||||
#include <IPC/Policies/TimeoutFactory.h>
|
||||
#include <IPC/Policies/ErrorHandler.h>
|
||||
#include "Client.h"
|
||||
#include "Server.h"
|
||||
#include <bond/core/bond_const_enum.h>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
template <
|
||||
typename PacketConnector,
|
||||
typename TimeoutFactory = typename PacketConnector::Traits::TimeoutFactory,
|
||||
typename ErrorHandler = typename PacketConnector::Traits::ErrorHandler,
|
||||
typename... TransactionArgs>
|
||||
auto ConnectClient(
|
||||
const char* acceptorName,
|
||||
std::shared_ptr<PacketConnector> connector,
|
||||
bool async,
|
||||
bond::ProtocolType protocol = bond::ProtocolType::COMPACT_PROTOCOL,
|
||||
bool marshal = true,
|
||||
std::size_t minBlobSize = 0,
|
||||
TimeoutFactory&& timeoutFactory = { std::chrono::seconds{ 1 } },
|
||||
ErrorHandler&& errorHandler = {},
|
||||
typename PacketConnector::Traits::TransactionManagerFactory transactionManagerFactory = {},
|
||||
TransactionArgs&&... transactionArgs)
|
||||
{
|
||||
return IPC::detail::Connect(
|
||||
acceptorName,
|
||||
std::move(connector),
|
||||
async,
|
||||
std::forward<TimeoutFactory>(timeoutFactory),
|
||||
std::forward<ErrorHandler>(errorHandler),
|
||||
[protocol, marshal, minBlobSize, transactionManagerFactory = std::move(transactionManagerFactory)](auto&& connection, auto&& callback)
|
||||
{
|
||||
using Client = Client<typename PacketConnector::Request, typename PacketConnector::Response, typename PacketConnector::Traits>;
|
||||
|
||||
return MakeClient<typename PacketConnector::Request, typename PacketConnector::Response, typename PacketConnector::Traits>(
|
||||
std::move(connection),
|
||||
std::forward<decltype(callback)>(callback),
|
||||
protocol,
|
||||
marshal,
|
||||
minBlobSize,
|
||||
transactionManagerFactory(IPC::detail::Identity<typename Client::TransactionManager>{}));
|
||||
},
|
||||
std::forward<TransactionArgs>(transactionArgs)...);
|
||||
}
|
||||
|
||||
template <
|
||||
typename PacketConnector,
|
||||
typename HandlerFactory,
|
||||
typename TimeoutFactory = typename PacketConnector::Traits::TimeoutFactory,
|
||||
typename ErrorHandler = typename PacketConnector::Traits::ErrorHandler,
|
||||
typename... TransactionArgs>
|
||||
auto ConnectServer(
|
||||
const char* acceptorName,
|
||||
std::shared_ptr<PacketConnector> connector,
|
||||
HandlerFactory&& handlerFactory,
|
||||
bool async,
|
||||
bond::ProtocolType protocol = bond::ProtocolType::COMPACT_PROTOCOL,
|
||||
bool marshal = true,
|
||||
std::size_t minBlobSize = 0,
|
||||
TimeoutFactory&& timeoutFactory = { std::chrono::seconds{ 1 } },
|
||||
ErrorHandler&& errorHandler = {},
|
||||
TransactionArgs&&... transactionArgs)
|
||||
{
|
||||
return IPC::detail::Connect(
|
||||
acceptorName,
|
||||
std::move(connector),
|
||||
async,
|
||||
std::forward<TimeoutFactory>(timeoutFactory),
|
||||
std::forward<ErrorHandler>(errorHandler),
|
||||
[protocol, marshal, minBlobSize, handlerFactory = std::forward<HandlerFactory>(handlerFactory)](auto&& connection, auto&& callback) mutable
|
||||
{
|
||||
return MakeServer<typename PacketConnector::Request, typename PacketConnector::Response, typename PacketConnector::Traits>(
|
||||
std::move(connection), handlerFactory, std::forward<decltype(callback)>(callback), protocol, marshal, minBlobSize);
|
||||
},
|
||||
std::forward<TransactionArgs>(transactionArgs)...);
|
||||
}
|
||||
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include <IPC/Connector.h>
|
||||
#include "detail/ComponentBase.h"
|
||||
#include "DefaultTraits.h"
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
template <typename Request, typename Response, typename Traits = DefaultTraits>
|
||||
using ClientConnector = detail::BufferComponent<ClientConnector, Request, Response, Traits>;
|
||||
|
||||
template <typename Request, typename Response, typename Traits = DefaultTraits>
|
||||
using ServerConnector = detail::BufferComponent<ServerConnector, Request, Response, Traits>;
|
||||
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <IPC/DefaultTraits.h>
|
||||
#include "BufferPool.h"
|
||||
#include "Serializer.h"
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
struct DefaultTraits : IPC::DefaultTraits
|
||||
{
|
||||
using BufferPool = DefaultBufferPool;
|
||||
|
||||
using Serializer = DefaultSerializer;
|
||||
};
|
||||
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,263 @@
|
|||
#pragma once
|
||||
|
||||
#include "BlobCast.h"
|
||||
#include "BufferPool.h"
|
||||
|
||||
#include <bond/core/traits.h>
|
||||
#include <bond/stream/input_buffer.h>
|
||||
#include <bond/protocol/encoding.h>
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
template <typename ConstBuffer>
|
||||
class InputBuffer
|
||||
{
|
||||
public:
|
||||
InputBuffer() = default;
|
||||
|
||||
InputBuffer(ConstBuffer buffer, std::shared_ptr<SharedMemory> memory)
|
||||
: InputBuffer{ buffer ? InputBuffer{ std::move(memory), buffer, buffer.begin(), buffer.end() } : InputBuffer{} }
|
||||
{}
|
||||
|
||||
InputBuffer(const typename ConstBuffer::Range& range, std::shared_ptr<SharedMemory> memory)
|
||||
: InputBuffer{ std::move(memory), range.m_buffer, range.m_firstBlob, range.m_lastBlob, range.m_firstOffset, range.m_lastOffset }
|
||||
{}
|
||||
|
||||
template <typename T>
|
||||
void Read(T& value)
|
||||
{
|
||||
// Assuming primitives do not span over multiple blobs.
|
||||
ReadSingle(sizeof(T), [&] { std::memcpy(&value, m_ptr, sizeof(T)); });
|
||||
}
|
||||
|
||||
void Read(void* buffer, std::uint32_t size)
|
||||
{
|
||||
ReadMultiple(
|
||||
size,
|
||||
[&](std::size_t available)
|
||||
{
|
||||
std::memcpy(buffer, m_ptr, available);
|
||||
buffer = static_cast<char*>(buffer) + available;
|
||||
});
|
||||
}
|
||||
|
||||
void Read(bond::blob& bondBlob, std::uint32_t size)
|
||||
{
|
||||
// Do not merge if spans over multiple blobs.
|
||||
const auto& blob = *m_blob;
|
||||
ReadSingle(size, [&] { bondBlob.assign(BlobCast(blob, m_memory), static_cast<std::uint32_t>(m_ptr - blob.data()), size); });
|
||||
}
|
||||
|
||||
const void* Allocate(std::uint32_t size)
|
||||
{
|
||||
const void* ptr;
|
||||
ReadSingle(size, [&] { ptr = m_ptr; });
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ReadVariableUnsigned(T& value)
|
||||
{
|
||||
if (m_ptr + sizeof(T) * 8 / 7 < m_ptrEnd)
|
||||
{
|
||||
auto ptr = m_ptr;
|
||||
bond::input_buffer::VariableUnsignedUnchecked<T, 0>::Read(ptr, value);
|
||||
EndRead(ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
bond::GenericReadVariableUnsigned(*this, value);
|
||||
}
|
||||
}
|
||||
|
||||
void Skip(std::uint32_t size)
|
||||
{
|
||||
ReadMultiple(size, [](std::size_t) {});
|
||||
}
|
||||
|
||||
bool IsEof() const
|
||||
{
|
||||
assert(m_isEof == (m_blob == m_blobEnd));
|
||||
return m_isEof;
|
||||
}
|
||||
|
||||
bool operator==(const InputBuffer& other) const
|
||||
{
|
||||
return m_ptr == other.m_ptr && m_blob == other.m_blob && m_blobEnd == other.m_blobEnd && m_lastOffset == other.m_lastOffset;
|
||||
}
|
||||
|
||||
const std::shared_ptr<SharedMemory>& GetMemory() const
|
||||
{
|
||||
return m_memory;
|
||||
}
|
||||
|
||||
private:
|
||||
using BlobIterator = decltype(std::declval<ConstBuffer>().begin());
|
||||
|
||||
InputBuffer(
|
||||
std::shared_ptr<SharedMemory> memory,
|
||||
ConstBuffer buffer,
|
||||
BlobIterator begin,
|
||||
BlobIterator end,
|
||||
std::size_t firstOffset = 0,
|
||||
std::size_t lastOffset = 0)
|
||||
: m_blob{ begin },
|
||||
m_blobEnd{ lastOffset != 0 ? std::next(end) : end },
|
||||
m_lastOffset{ lastOffset },
|
||||
m_memory{ std::move(memory) },
|
||||
m_buffer{ std::move(buffer) }
|
||||
{
|
||||
if (UpdateData())
|
||||
{
|
||||
m_ptr += firstOffset;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
void ReadMultiple(std::size_t size, Function&& func)
|
||||
{
|
||||
auto ptr = m_ptr;
|
||||
auto ptrEnd = m_ptrEnd;
|
||||
auto blob = m_blob;
|
||||
|
||||
try
|
||||
{
|
||||
while (size != 0)
|
||||
{
|
||||
const auto n = std::min<std::size_t>(m_ptrEnd - m_ptr, size);
|
||||
|
||||
ReadSingle(n, [&] { func(n); });
|
||||
|
||||
size -= n;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
m_ptr = ptr;
|
||||
m_ptrEnd = ptrEnd;
|
||||
m_blob = blob;
|
||||
m_isEof = (m_blob == m_blobEnd);
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void BeginRead(const char* ptr) const
|
||||
{
|
||||
if (ptr > m_ptrEnd || IsEof())
|
||||
{
|
||||
throw std::out_of_range{ "Out of buffer range." };
|
||||
}
|
||||
}
|
||||
|
||||
void EndRead(const char* ptr)
|
||||
{
|
||||
if (ptr != m_ptrEnd)
|
||||
{
|
||||
m_ptr = ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveNext();
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(noinline) void MoveNext()
|
||||
{
|
||||
m_isEof = (++m_blob == m_blobEnd);
|
||||
UpdateData();
|
||||
}
|
||||
|
||||
bool SkipEmptyBlobs()
|
||||
{
|
||||
for (; m_blob != m_blobEnd; ++m_blob)
|
||||
{
|
||||
const auto& blob = *m_blob;
|
||||
|
||||
if (blob && blob.size() != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_isEof = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
void ReadSingle(std::size_t size, Function&& func)
|
||||
{
|
||||
const auto ptr = m_ptr + size;
|
||||
|
||||
BeginRead(ptr);
|
||||
|
||||
std::forward<Function>(func)();
|
||||
|
||||
EndRead(ptr);
|
||||
}
|
||||
|
||||
bool UpdateData()
|
||||
{
|
||||
if (!SkipEmptyBlobs())
|
||||
{
|
||||
const auto& blob = *m_blob;
|
||||
|
||||
m_ptr = blob.data();
|
||||
m_ptrEnd = blob.data() + (m_lastOffset != 0 && std::next(m_blob) == m_blobEnd ? m_lastOffset : blob.size());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
m_ptr = m_ptrEnd = nullptr;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
friend auto GetCurrentBuffer(const InputBuffer& buffer)
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
|
||||
friend auto GetBufferRange(const InputBuffer& begin, const InputBuffer& end)
|
||||
{
|
||||
assert(begin.m_buffer == end.m_buffer);
|
||||
assert(end.IsEof() || !begin.IsEof());
|
||||
|
||||
return typename ConstBuffer::Range{
|
||||
begin.m_buffer,
|
||||
begin.m_blob,
|
||||
begin.IsEof() ? 0 : std::size_t(begin.m_ptr - begin.m_blob->data()),
|
||||
end.m_blob,
|
||||
end.IsEof() ? 0 : std::size_t(end.m_ptr - end.m_blob->data()) };
|
||||
}
|
||||
|
||||
friend auto CreateInputBuffer(const InputBuffer& other, const typename ConstBuffer::Range& range)
|
||||
{
|
||||
return InputBuffer{ range, other.m_memory };
|
||||
}
|
||||
|
||||
|
||||
const char* m_ptr{ nullptr };
|
||||
const char* m_ptrEnd{ nullptr };
|
||||
BlobIterator m_blob{};
|
||||
BlobIterator m_blobEnd{};
|
||||
bool m_isEof{ m_blob == m_blobEnd };
|
||||
std::size_t m_lastOffset{ 0 };
|
||||
std::shared_ptr<SharedMemory> m_memory; // Must be declared before m_buffer.
|
||||
ConstBuffer m_buffer{};
|
||||
};
|
||||
|
||||
|
||||
using DefaultInputBuffer = InputBuffer<DefaultBufferPool::ConstBuffer>;
|
||||
|
||||
} // Bond
|
||||
} // IPC
|
||||
|
||||
namespace bond
|
||||
{
|
||||
BOND_DEFINE_BUFFER_MAGIC(IPC::Bond::DefaultInputBuffer, 0x5349 /*SB*/);
|
||||
|
||||
} // namespace bond
|
|
@ -0,0 +1,228 @@
|
|||
#pragma once
|
||||
|
||||
#include "BlobCast.h"
|
||||
#include "BufferPool.h"
|
||||
|
||||
#include <bond/stream/output_buffer.h>
|
||||
#include <bond/protocol/encoding.h>
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
template <typename BufferPool>
|
||||
class OutputBuffer
|
||||
{
|
||||
public:
|
||||
explicit OutputBuffer(std::shared_ptr<BufferPool> pool, std::size_t minBlobSize = 0)
|
||||
: m_buffer{ pool->TakeBuffer() },
|
||||
m_pool{ std::move(pool) },
|
||||
m_minBlobSize{ minBlobSize != 0 ? minBlobSize : 4096 }
|
||||
{
|
||||
TakeBlob();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Write(const T& value)
|
||||
{
|
||||
if (m_ptr + sizeof(T) < m_ptrEnd)
|
||||
{
|
||||
std::memcpy(m_ptr, &value, sizeof(T));
|
||||
m_ptr += sizeof(T);
|
||||
}
|
||||
else
|
||||
{
|
||||
Write(&value, sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
void Write(const void* value, std::uint32_t size)
|
||||
{
|
||||
std::memcpy(Allocate(size), value, size);
|
||||
}
|
||||
|
||||
void* Allocate(std::uint32_t size)
|
||||
{
|
||||
if (m_ptr + size >= m_ptrEnd)
|
||||
{
|
||||
auto& blob = *m_blob;
|
||||
auto offset = m_ptr - blob.data();
|
||||
|
||||
blob.resize(offset + std::max<std::size_t>(size, m_minBlobSize), boost::container::default_init);
|
||||
|
||||
m_ptr = blob.data() + offset;
|
||||
m_ptrEnd = blob.data() + blob.size();
|
||||
}
|
||||
|
||||
auto ptr = m_ptr;
|
||||
m_ptr += size;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void Write(const bond::blob& blob)
|
||||
{
|
||||
Write(BlobCast<typename BufferPool::ConstBlob>(blob), blob);
|
||||
}
|
||||
|
||||
void Write(const typename BufferPool::ConstBuffer& buffer)
|
||||
{
|
||||
if (buffer)
|
||||
{
|
||||
for (const auto& blob : buffer)
|
||||
{
|
||||
Write(blob);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Write(const typename BufferPool::ConstBuffer::Range& range)
|
||||
{
|
||||
if (!range.IsEmpty())
|
||||
{
|
||||
const auto& first = range.m_firstBlob;
|
||||
|
||||
auto length = std::distance(
|
||||
first,
|
||||
range.m_lastOffset != 0 ? std::next(range.m_lastBlob) : range.m_lastBlob);
|
||||
|
||||
assert(length != 0);
|
||||
|
||||
if (length == 1)
|
||||
{
|
||||
Write(first->GetRange(
|
||||
range.m_firstOffset,
|
||||
(range.m_lastOffset != 0 ? range.m_lastOffset : first->size()) - range.m_firstOffset));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto last = std::next(first, length - 1);
|
||||
|
||||
Write(range.m_firstOffset != 0
|
||||
? first->GetRange(range.m_firstOffset, first->size() - range.m_firstOffset)
|
||||
: *first);
|
||||
|
||||
for (auto it = std::next(first); it != last; ++it)
|
||||
{
|
||||
Write(*it);
|
||||
}
|
||||
|
||||
Write(range.m_lastOffset != 0
|
||||
? last->GetRange(0, range.m_lastOffset)
|
||||
: *last);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void WriteVariableUnsigned(T value)
|
||||
{
|
||||
if (m_ptr + sizeof(T) * 8 / 7 < m_ptrEnd)
|
||||
{
|
||||
m_ptr += bond::output_buffer::VariableUnsignedUnchecked<T, 1>::Write(m_ptr, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
bond::GenericWriteVariableUnsigned(*this, value);
|
||||
}
|
||||
}
|
||||
|
||||
void Flush()
|
||||
{
|
||||
if (Merge())
|
||||
{
|
||||
TakeBlob();
|
||||
}
|
||||
}
|
||||
|
||||
const typename BufferPool::Buffer& GetBuffer() const &
|
||||
{
|
||||
if (m_ptr != m_blob->data())
|
||||
{
|
||||
throw std::runtime_error{ "Buffer is not flushed." };
|
||||
}
|
||||
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
const typename BufferPool::Buffer& GetBuffer() &
|
||||
{
|
||||
Flush();
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
typename BufferPool::ConstBuffer GetBuffer() &&
|
||||
{
|
||||
Merge();
|
||||
return std::move(m_buffer);
|
||||
}
|
||||
|
||||
const std::shared_ptr<BufferPool>& GetBufferPool() const
|
||||
{
|
||||
return m_pool;
|
||||
}
|
||||
|
||||
private:
|
||||
void TakeBlob()
|
||||
{
|
||||
m_blob = m_pool->TakeBlob();
|
||||
m_blob->resize(std::max<std::size_t>(m_blob->capacity(), m_minBlobSize), boost::container::default_init);
|
||||
|
||||
m_ptr = m_blob->data();
|
||||
m_ptrEnd = m_blob->data() + m_blob->size();
|
||||
}
|
||||
|
||||
bool Merge()
|
||||
{
|
||||
auto offset = m_ptr - m_blob->data();
|
||||
|
||||
if (offset != 0)
|
||||
{
|
||||
m_blob->resize(offset);
|
||||
m_buffer->push_back(std::move(m_blob));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename OtherBlob>
|
||||
void Write(const typename BufferPool::ConstBlob& blob, const OtherBlob& otherBlob)
|
||||
{
|
||||
if (blob && m_pool->GetMemory()->Contains(blob.data()))
|
||||
{
|
||||
assert(m_pool->GetMemory()->Contains(blob.data() + blob.size() - 1));
|
||||
|
||||
Flush();
|
||||
m_buffer->push_back(blob);
|
||||
}
|
||||
else
|
||||
{
|
||||
Write(otherBlob.data(), static_cast<uint32_t>(otherBlob.size()));
|
||||
}
|
||||
}
|
||||
|
||||
void Write(const typename BufferPool::ConstBlob& blob)
|
||||
{
|
||||
Write(blob, blob);
|
||||
}
|
||||
|
||||
friend auto CreateOutputBuffer(const OutputBuffer& other)
|
||||
{
|
||||
return OutputBuffer{ other.m_pool };
|
||||
}
|
||||
|
||||
char* m_ptr{ nullptr };
|
||||
char* m_ptrEnd{ nullptr };
|
||||
typename BufferPool::Blob m_blob;
|
||||
typename BufferPool::Buffer m_buffer;
|
||||
std::shared_ptr<BufferPool> m_pool;
|
||||
std::size_t m_minBlobSize;
|
||||
};
|
||||
|
||||
|
||||
using DefaultOutputBuffer = OutputBuffer<DefaultBufferPool>;
|
||||
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,157 @@
|
|||
#pragma once
|
||||
|
||||
#include "OutputBuffer.h"
|
||||
#include "InputBuffer.h"
|
||||
#include "BufferPool.h"
|
||||
#include <bond/core/bond.h>
|
||||
#include <memory>
|
||||
#include <future>
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
struct DefaultProtocols
|
||||
: bond::Protocols<
|
||||
bond::CompactBinaryReader<DefaultInputBuffer>,
|
||||
bond::SimpleBinaryReader<DefaultInputBuffer>,
|
||||
bond::FastBinaryReader<DefaultInputBuffer>,
|
||||
bond::SimpleJsonReader<DefaultInputBuffer>> {};
|
||||
|
||||
|
||||
template <template <typename> typename Writer, typename Protocols = DefaultProtocols, typename BufferPool, typename T>
|
||||
typename BufferPool::ConstBuffer Serialize(std::shared_ptr<BufferPool> pool, const T& value, std::size_t minBlobSize = 0)
|
||||
{
|
||||
OutputBuffer<BufferPool> output{ std::move(pool), minBlobSize };
|
||||
Writer<decltype(output)> writer{ output };
|
||||
bond::Serialize<Protocols>(value, writer);
|
||||
return std::move(output).GetBuffer();
|
||||
}
|
||||
|
||||
template <typename Protocols = DefaultProtocols, typename BufferPool, typename T>
|
||||
typename BufferPool::ConstBuffer Serialize(bond::ProtocolType protocol, std::shared_ptr<BufferPool> pool, const T& value, std::size_t minBlobSize = 0)
|
||||
{
|
||||
OutputBuffer<BufferPool> output{ std::move(pool), minBlobSize };
|
||||
bond::Apply<bond::Serializer, Protocols>(value, output, static_cast<std::uint16_t>(protocol));
|
||||
return std::move(output).GetBuffer();
|
||||
}
|
||||
|
||||
template <template <typename> typename Reader, typename Protocols = DefaultProtocols, typename ConstBuffer, typename T>
|
||||
void Deserialize(ConstBuffer&& buffer, T& value, std::shared_ptr<SharedMemory> memory)
|
||||
{
|
||||
InputBuffer<std::decay_t<ConstBuffer>> input{ std::forward<ConstBuffer>(buffer), std::move(memory) };
|
||||
Reader<decltype(input)> reader{ std::move(input) };
|
||||
bond::Deserialize<Protocols>(reader, value);
|
||||
}
|
||||
|
||||
template <typename Protocols = DefaultProtocols, typename ConstBuffer, typename T>
|
||||
void Deserialize(bond::ProtocolType protocol, ConstBuffer&& buffer, T& value, std::shared_ptr<SharedMemory> memory)
|
||||
{
|
||||
InputBuffer<std::decay_t<ConstBuffer>> input{ std::forward<ConstBuffer>(buffer), std::move(memory) };
|
||||
bond::Apply<T, Protocols>(bond::To<T, Protocols>{ value }, input, static_cast<std::uint16_t>(protocol));
|
||||
}
|
||||
|
||||
template <template <typename> typename Writer, typename Protocols = DefaultProtocols, typename BufferPool, typename T>
|
||||
typename BufferPool::ConstBuffer Marshal(std::shared_ptr<BufferPool> pool, const T& value, std::size_t minBlobSize = 0)
|
||||
{
|
||||
OutputBuffer<BufferPool> output{ std::move(pool), minBlobSize };
|
||||
Writer<decltype(output)> writer{ output };
|
||||
bond::Marshal<Protocols>(value, writer);
|
||||
return std::move(output).GetBuffer();
|
||||
}
|
||||
|
||||
template <typename Protocols = DefaultProtocols, typename BufferPool, typename T>
|
||||
typename BufferPool::ConstBuffer Marshal(bond::ProtocolType protocol, std::shared_ptr<BufferPool> pool, const T& value, std::size_t minBlobSize = 0)
|
||||
{
|
||||
OutputBuffer<BufferPool> output{ std::move(pool), minBlobSize };
|
||||
bond::Apply<bond::Marshaler, Protocols>(value, output, static_cast<std::uint16_t>(protocol));
|
||||
return std::move(output).GetBuffer();
|
||||
}
|
||||
|
||||
template <typename Protocols = DefaultProtocols, typename ConstBuffer, typename T>
|
||||
void Unmarshal(ConstBuffer&& buffer, T& value, std::shared_ptr<SharedMemory> memory)
|
||||
{
|
||||
bond::Unmarshal<Protocols>(InputBuffer<std::decay_t<ConstBuffer>>{ std::forward<ConstBuffer>(buffer), std::move(memory) }, value);
|
||||
}
|
||||
|
||||
|
||||
template <typename BufferPool, typename ProtocolsT = DefaultProtocols>
|
||||
class Serializer // TODO: Add compile-time protocol support.
|
||||
{
|
||||
public:
|
||||
using Protocols = ProtocolsT;
|
||||
|
||||
Serializer(bond::ProtocolType protocol, bool marshal, std::shared_ptr<BufferPool> outputPool, std::shared_ptr<SharedMemory> inputMemory, std::size_t minBlobSize = 0)
|
||||
: m_outputPool{ std::move(outputPool) },
|
||||
m_inputMemory{ std::move(inputMemory) },
|
||||
m_protocol{ protocol },
|
||||
m_marshal{ marshal },
|
||||
m_minBlobSize{ minBlobSize }
|
||||
{}
|
||||
|
||||
bond::ProtocolType GetProtocolType() const
|
||||
{
|
||||
return m_protocol;
|
||||
}
|
||||
|
||||
bool IsMarshaled() const
|
||||
{
|
||||
return m_marshal;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename BufferPool::ConstBuffer Serialize(const T& value)
|
||||
{
|
||||
return m_marshal
|
||||
? Bond::Marshal<Protocols>(m_protocol, m_outputPool, value, m_minBlobSize)
|
||||
: Bond::Serialize<Protocols>(m_protocol, m_outputPool, value, m_minBlobSize);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Deserialize(typename BufferPool::ConstBuffer&& buffer, T& value)
|
||||
{
|
||||
m_marshal
|
||||
? Bond::Unmarshal<Protocols>(std::move(buffer), value, m_inputMemory)
|
||||
: Bond::Deserialize<Protocols>(m_protocol, std::move(buffer), value, m_inputMemory);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::future<T> Deserialize(typename BufferPool::ConstBuffer buffer)
|
||||
{
|
||||
std::packaged_task<T()> task{
|
||||
[&]
|
||||
{
|
||||
T value;
|
||||
Deserialize(std::move(buffer), value);
|
||||
return value;
|
||||
} };
|
||||
|
||||
task();
|
||||
|
||||
return task.get_future();
|
||||
}
|
||||
|
||||
const std::shared_ptr<BufferPool>& GetOutputBufferPool() const
|
||||
{
|
||||
return m_outputPool;
|
||||
}
|
||||
|
||||
const std::shared_ptr<SharedMemory>& GetInputMemory() const
|
||||
{
|
||||
return m_inputMemory;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<BufferPool> m_outputPool;
|
||||
std::shared_ptr<SharedMemory> m_inputMemory;
|
||||
bond::ProtocolType m_protocol;
|
||||
bool m_marshal;
|
||||
std::size_t m_minBlobSize;
|
||||
};
|
||||
|
||||
|
||||
using DefaultSerializer = Serializer<DefaultBufferPool>;
|
||||
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,58 @@
|
|||
#pragma once
|
||||
|
||||
#include "detail/ComponentBase.h"
|
||||
#include <IPC/Server.h>
|
||||
#include "DefaultTraits.h"
|
||||
#include <bond/core/bond_const_enum.h>
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
template <typename Request, typename Response, typename Traits = DefaultTraits>
|
||||
class Server : public detail::ComponentBase<IPC::Server, Request, Response, Traits>
|
||||
{
|
||||
using Base = detail::ComponentBase<IPC::Server, Request, Response, Traits>;
|
||||
|
||||
public:
|
||||
template <typename Handler, typename CloseHandler>
|
||||
Server(typename Base::BufferPoolHolder pools, typename Base::Serializer serializer, std::unique_ptr<typename Base::Connection> connection, Handler&& handler, CloseHandler&& closeHandler)
|
||||
: Base{
|
||||
std::move(pools),
|
||||
serializer,
|
||||
std::move(connection),
|
||||
[serializer, handler = std::forward<Handler>(handler)](typename Traits::BufferPool::ConstBuffer&& buffer, auto&& callback) mutable
|
||||
{
|
||||
handler(
|
||||
serializer.template Deserialize<Request>(std::move(buffer)),
|
||||
[serializer, callback = std::forward<decltype(callback)>(callback)](const Response& response) mutable
|
||||
{
|
||||
callback(serializer.Serialize(response));
|
||||
});
|
||||
},
|
||||
std::forward<CloseHandler>(closeHandler) }
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
template <typename Request, typename Response, typename Traits = DefaultTraits, typename HandlerFactory, typename CloseHandler>
|
||||
auto MakeServer(
|
||||
std::unique_ptr<typename Server<Request, Response, Traits>::Connection> connection,
|
||||
HandlerFactory&& handlerFactory,
|
||||
CloseHandler&& closeHandler,
|
||||
bond::ProtocolType protocol = bond::ProtocolType::COMPACT_PROTOCOL,
|
||||
bool marshal = true,
|
||||
std::size_t minBlobSize = 0)
|
||||
{
|
||||
auto pools = detail::MakeBufferPoolHolder<typename Traits::BufferPool>(*connection);
|
||||
typename Traits::Serializer serializer{ protocol, marshal, pools.GetOutputPool(), pools.GetInputPool()->GetMemory(), minBlobSize };
|
||||
|
||||
auto handler = handlerFactory(*connection, pools, serializer);
|
||||
|
||||
return std::make_unique<Server<Request, Response, Traits>>(
|
||||
std::move(pools), std::move(serializer), std::move(connection), std::move(handler), std::forward<CloseHandler>(closeHandler));
|
||||
}
|
||||
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,200 @@
|
|||
#pragma once
|
||||
|
||||
#include "Acceptor.h"
|
||||
#include "Accept.h"
|
||||
#include "Connector.h"
|
||||
#include "Connect.h"
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
struct DefaultTraits;
|
||||
|
||||
|
||||
template <typename Request, typename Response, typename Traits = DefaultTraits>
|
||||
class Transport
|
||||
{
|
||||
public:
|
||||
using Client = Client<Request, Response, Traits>;
|
||||
using Server = Server<Request, Response, Traits>;
|
||||
using ClientConnector = ClientConnector<Request, Response, Traits>;
|
||||
using ServerConnector = ServerConnector<Request, Response, Traits>;
|
||||
using ClientAcceptor = ClientAcceptor<Request, Response, Traits>;
|
||||
using ServerAcceptor = ServerAcceptor<Request, Response, Traits>;
|
||||
|
||||
Transport()
|
||||
: Transport{ bond::ProtocolType::COMPACT_PROTOCOL }
|
||||
{}
|
||||
|
||||
explicit Transport(
|
||||
bond::ProtocolType protocol,
|
||||
bool marshal = true,
|
||||
ChannelSettings<Traits> channelSettings = {},
|
||||
std::size_t minBlobSize = 0,
|
||||
std::size_t hostInfoMemorySize = 0,
|
||||
typename Traits::TimeoutFactory timeoutFactory = {},
|
||||
typename Traits::ErrorHandler errorHandler = {},
|
||||
typename Traits::TransactionManagerFactory transactionManagerFactory = {})
|
||||
: m_protocol{ protocol },
|
||||
m_marshal{ marshal },
|
||||
m_channelSettings{ std::move(channelSettings) },
|
||||
m_minBlobSize{ minBlobSize },
|
||||
m_hostInfoMemorySize{ hostInfoMemorySize },
|
||||
m_timeoutFactory{ std::move(timeoutFactory) },
|
||||
m_errorHandler{ std::move(errorHandler) },
|
||||
m_transactionManagerFactory{ std::move(transactionManagerFactory) }
|
||||
{}
|
||||
|
||||
template <typename CloseHandler>
|
||||
auto MakeClient(std::unique_ptr<typename Client::Connection> connection, CloseHandler&& closeHandler)
|
||||
{
|
||||
return IPC::Bond::MakeClient<Request, Response, Traits>(
|
||||
std::move(connection),
|
||||
std::forward<CloseHandler>(closeHandler),
|
||||
m_protocol,
|
||||
m_marshal,
|
||||
m_minBlobSize,
|
||||
m_transactionManagerFactory(IPC::detail::Identity<typename Client::TransactionManager>{}));
|
||||
}
|
||||
|
||||
template <typename HandlerFactory, typename CloseHandler>
|
||||
auto MakeServer(std::unique_ptr<typename Server::Connection> connection, HandlerFactory&& handlerFactory, CloseHandler&& closeHandler)
|
||||
{
|
||||
return IPC::Bond::MakeServer<Request, Response, Traits>(
|
||||
std::move(connection),
|
||||
std::forward<HandlerFactory>(handlerFactory),
|
||||
std::forward<CloseHandler>(closeHandler),
|
||||
m_protocol,
|
||||
m_marshal,
|
||||
m_minBlobSize);
|
||||
}
|
||||
|
||||
auto MakeClientConnector()
|
||||
{
|
||||
return ClientConnector{ m_channelSettings, m_transactionManagerFactory };
|
||||
}
|
||||
|
||||
auto MakeServerConnector()
|
||||
{
|
||||
return ServerConnector{ m_channelSettings, m_transactionManagerFactory };
|
||||
}
|
||||
|
||||
template <typename Handler>
|
||||
auto MakeServerAcceptor(const char* name, Handler&& handler)
|
||||
{
|
||||
return ServerAcceptor{ name, std::forward<Handler>(handler), m_channelSettings, m_hostInfoMemorySize };
|
||||
}
|
||||
|
||||
template <typename Handler>
|
||||
auto MakeClientAcceptor(const char* name, Handler&& handler)
|
||||
{
|
||||
return ClientAcceptor{ name, std::forward<Handler>(handler), m_channelSettings, m_hostInfoMemorySize };
|
||||
}
|
||||
|
||||
template <typename... TransactionArgs>
|
||||
auto ConnectClient(
|
||||
const char* name,
|
||||
bool async,
|
||||
std::shared_ptr<ClientConnector> connector = {},
|
||||
TransactionArgs&&... transactionArgs)
|
||||
{
|
||||
if (!connector)
|
||||
{
|
||||
std::call_once(
|
||||
m_clientConnectorOnceFlag,
|
||||
[this] { m_clientConnector = std::make_shared<ClientConnector>(MakeClientConnector()); });
|
||||
|
||||
connector = m_clientConnector;
|
||||
}
|
||||
|
||||
return IPC::Bond::ConnectClient(
|
||||
name,
|
||||
connector,
|
||||
async,
|
||||
m_protocol,
|
||||
m_marshal,
|
||||
m_minBlobSize,
|
||||
m_timeoutFactory,
|
||||
m_errorHandler,
|
||||
m_transactionManagerFactory,
|
||||
std::forward<TransactionArgs>(transactionArgs)...);
|
||||
}
|
||||
|
||||
template <typename HandlerFactory, typename... TransactionArgs>
|
||||
auto ConnectServer(
|
||||
const char* name,
|
||||
HandlerFactory&& handlerFactory,
|
||||
bool async,
|
||||
std::shared_ptr<ServerConnector> connector = {},
|
||||
TransactionArgs&&... transactionArgs)
|
||||
{
|
||||
if (!connector)
|
||||
{
|
||||
std::call_once(
|
||||
m_serverConnectorOnceFlag,
|
||||
[this] { m_serverConnector = std::make_shared<ServerConnector>(MakeServerConnector()); });
|
||||
|
||||
connector = m_serverConnector;
|
||||
}
|
||||
|
||||
return IPC::Bond::ConnectServer(
|
||||
name,
|
||||
connector,
|
||||
std::forward<HandlerFactory>(handlerFactory),
|
||||
async,
|
||||
m_protocol,
|
||||
m_marshal,
|
||||
m_minBlobSize,
|
||||
m_timeoutFactory,
|
||||
m_errorHandler,
|
||||
std::forward<TransactionArgs>(transactionArgs)...);
|
||||
}
|
||||
|
||||
template <typename HandlerFactory>
|
||||
auto AcceptServers(const char* name, HandlerFactory&& handlerFactory)
|
||||
{
|
||||
return IPC::Bond::AcceptServers<Request, Response, Traits>(
|
||||
name,
|
||||
std::forward<HandlerFactory>(handlerFactory),
|
||||
m_protocol,
|
||||
m_marshal,
|
||||
m_channelSettings,
|
||||
m_minBlobSize,
|
||||
m_hostInfoMemorySize,
|
||||
m_errorHandler);
|
||||
}
|
||||
|
||||
auto AcceptClients(const char* name)
|
||||
{
|
||||
return IPC::Bond::AcceptClients<Request, Response, Traits>(
|
||||
name,
|
||||
m_protocol,
|
||||
m_marshal,
|
||||
m_channelSettings,
|
||||
m_minBlobSize,
|
||||
m_hostInfoMemorySize,
|
||||
m_errorHandler,
|
||||
m_transactionManagerFactory);
|
||||
}
|
||||
|
||||
private:
|
||||
bond::ProtocolType m_protocol;
|
||||
bool m_marshal;
|
||||
ChannelSettings<Traits> m_channelSettings;
|
||||
std::size_t m_minBlobSize;
|
||||
std::size_t m_hostInfoMemorySize;
|
||||
typename Traits::TimeoutFactory m_timeoutFactory;
|
||||
typename Traits::ErrorHandler m_errorHandler;
|
||||
typename Traits::TransactionManagerFactory m_transactionManagerFactory;
|
||||
std::shared_ptr<ClientConnector> m_clientConnector;
|
||||
std::shared_ptr<ServerConnector> m_serverConnector;
|
||||
std::once_flag m_clientConnectorOnceFlag;
|
||||
std::once_flag m_serverConnectorOnceFlag;
|
||||
};
|
||||
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
|
||||
#include <bond/core/blob.h>
|
||||
#include <cassert>
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
class SharedMemory;
|
||||
|
||||
|
||||
namespace Bond
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template <typename Blob>
|
||||
struct BlobHolder
|
||||
{
|
||||
explicit BlobHolder(Blob blob, std::shared_ptr<SharedMemory> memory = {})
|
||||
: m_memory{ std::move(memory) },
|
||||
m_blob{ std::move(blob) }
|
||||
{}
|
||||
|
||||
void operator()(const char* buffer)
|
||||
{
|
||||
if (buffer != nullptr)
|
||||
{
|
||||
assert(m_blob);
|
||||
assert(buffer == m_blob.data());
|
||||
|
||||
m_blob = {};
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<SharedMemory> m_memory; // Must be declared before m_blob.
|
||||
Blob m_blob;
|
||||
};
|
||||
|
||||
struct BlobHook : boost::shared_ptr<const char[]>
|
||||
{
|
||||
using shared_ptr::shared_ptr;
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // Bond
|
||||
} // IPC
|
||||
|
||||
|
||||
namespace bond
|
||||
{
|
||||
template <>
|
||||
inline IPC::Bond::detail::BlobHook blob_cast(const blob& from)
|
||||
{
|
||||
return from._buffer;
|
||||
}
|
||||
|
||||
} // bond
|
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template <typename BufferPool>
|
||||
class BufferPoolHolder
|
||||
{
|
||||
public:
|
||||
BufferPoolHolder(std::shared_ptr<BufferPool> inPool, std::shared_ptr<BufferPool> outPool)
|
||||
: m_inPool{ std::move(inPool) },
|
||||
m_outPool{ std::move(outPool) }
|
||||
{}
|
||||
|
||||
const std::shared_ptr<BufferPool>& GetInputPool() const
|
||||
{
|
||||
return m_inPool;
|
||||
}
|
||||
|
||||
const std::shared_ptr<BufferPool>& GetOutputPool() const
|
||||
{
|
||||
return m_outPool;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<BufferPool> m_inPool;
|
||||
std::shared_ptr<BufferPool> m_outPool;
|
||||
};
|
||||
|
||||
|
||||
template <typename BufferPool, typename Connection>
|
||||
auto MakeBufferPoolHolder(const Connection& connection)
|
||||
{
|
||||
auto pools = std::make_shared<std::pair<BufferPool, BufferPool>>(
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple(connection.GetInputChannel().GetMemory()),
|
||||
std::forward_as_tuple(connection.GetOutputChannel().GetMemory()));
|
||||
|
||||
auto& pair = *pools;
|
||||
|
||||
std::shared_ptr<BufferPool> in{ pools, &pair.first };
|
||||
std::shared_ptr<BufferPool> out{ std::move(pools), &pair.second };
|
||||
|
||||
return BufferPoolHolder<BufferPool>{ std::move(in), std::move(out) };
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,61 @@
|
|||
#pragma once
|
||||
|
||||
#include "BufferPoolHolder.h"
|
||||
#include "BufferPoolFwd.h"
|
||||
#include <type_traits>
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template <template <typename, typename, typename> typename Component, typename Request, typename Response, typename Traits>
|
||||
using BufferComponentOf = Component<
|
||||
ConstBuffer<typename Traits::BufferPool>,
|
||||
std::conditional_t<std::is_void<Response>::value, void, ConstBuffer<typename Traits::BufferPool>>,
|
||||
Traits>;
|
||||
|
||||
|
||||
template <template <typename, typename, typename> typename Component, typename RequestT, typename ResponseT, typename TraitsT>
|
||||
class BufferComponent : public BufferComponentOf<Component, RequestT, ResponseT, TraitsT>
|
||||
{
|
||||
using Base = BufferComponentOf<Component, RequestT, ResponseT, TraitsT>;
|
||||
|
||||
public:
|
||||
using Request = RequestT;
|
||||
using Response = ResponseT;
|
||||
using Traits = TraitsT;
|
||||
|
||||
template <typename... Args>
|
||||
BufferComponent(Args&&... args) // TODO: Inherit constructors instead when VC14 bugs are fixed.
|
||||
: Base{ std::forward<Args>(args)... }
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
template <template <typename, typename, typename> typename Component, typename Request, typename Response, typename Traits>
|
||||
class ComponentBase
|
||||
: public BufferComponent<Component, Request, Response, Traits>,
|
||||
public BufferPoolHolder<typename Traits::BufferPool>,
|
||||
public Traits::Serializer
|
||||
{
|
||||
using BufferComponent = BufferComponent<Component, Request, Response, Traits>;
|
||||
|
||||
protected:
|
||||
using BufferPoolHolder = BufferPoolHolder<typename Traits::BufferPool>;
|
||||
using Serializer = typename Traits::Serializer;
|
||||
|
||||
public:
|
||||
template <typename... Args>
|
||||
ComponentBase(BufferPoolHolder pools, Serializer serializer, Args&&... args)
|
||||
: BufferComponent{ std::forward<Args>(args)... },
|
||||
BufferPoolHolder{ std::move(pools) },
|
||||
Serializer{ std::move(serializer) }
|
||||
{}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,73 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\Inc\detail\Interop\BufferPool.h" />
|
||||
<ClInclude Include="..\Inc\detail\Interop\InputBuffer.h" />
|
||||
<ClInclude Include="..\Inc\detail\Interop\OutputBuffer.h" />
|
||||
<ClInclude Include="..\Inc\stdafx.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\Src\detail\Interop\BufferPool.cpp" />
|
||||
<ClCompile Include="..\Src\detail\Interop\InputBuffer.cpp" />
|
||||
<ClCompile Include="..\Src\detail\Interop\OutputBuffer.cpp" />
|
||||
<ClCompile Include="..\Src\detail\Interop\Transport.cpp" />
|
||||
<ClCompile Include="..\Src\stdafx.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{A13012C1-76DE-4D1D-A58B-2361D2BE8F65}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<PropertyGroup>
|
||||
<TargetName>IPC.Bond.Interop</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<Lib>
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Lib>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..\..\Inc;..\Inc;..\..\IPC\Inc;..\..\bond\build\target\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4494;4503;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AnySuitable</InlineFunctionExpansion>
|
||||
<FavorSizeOrSpeed Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Speed</FavorSizeOrSpeed>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Import Project="..\..\IPC\Packages\boost.1.63.0.0\build\native\boost.targets" Condition="Exists('..\..\IPC\Packages\boost.1.63.0.0\build\native\boost.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\IPC\Packages\boost.1.63.0.0\build\native\boost.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\IPC\Packages\boost.1.63.0.0\build\native\boost.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="detail">
|
||||
<UniqueIdentifier>{45e3f9ed-bd76-4f45-8e40-1d9c56304c65}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="detail\Interop">
|
||||
<UniqueIdentifier>{647cf641-c404-49b8-ae45-ca5de6ea9686}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\Src\stdafx.cpp">
|
||||
<Filter>detail</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Src\detail\Interop\BufferPool.cpp">
|
||||
<Filter>detail\Interop</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Src\detail\Interop\InputBuffer.cpp">
|
||||
<Filter>detail\Interop</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Src\detail\Interop\OutputBuffer.cpp">
|
||||
<Filter>detail\Interop</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Src\detail\Interop\Transport.cpp">
|
||||
<Filter>detail\Interop</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\Inc\stdafx.h">
|
||||
<Filter>detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Inc\detail\Interop\BufferPool.h">
|
||||
<Filter>detail\Interop</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Inc\detail\Interop\InputBuffer.h">
|
||||
<Filter>detail\Interop</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Inc\detail\Interop\OutputBuffer.h">
|
||||
<Filter>detail\Interop</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="boost" version="1.63.0.0" targetFramework="native" />
|
||||
</packages>
|
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
#pragma managed(push, off)
|
||||
|
||||
#include <IPC/Managed/detail/Interop/SharedMemory.h>
|
||||
#include <IPC/Bond/BufferPoolFwd.h>
|
||||
#include <memory>
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
namespace Managed
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
namespace Interop
|
||||
{
|
||||
class BufferPool : public std::shared_ptr<IPC::Bond::DefaultBufferPool>
|
||||
{
|
||||
public:
|
||||
class ConstBuffer : public std::shared_ptr<IPC::Bond::DefaultConstBuffer>
|
||||
{
|
||||
public:
|
||||
ConstBuffer(const IPC::Bond::DefaultConstBuffer& impl);
|
||||
|
||||
~ConstBuffer();
|
||||
|
||||
std::size_t GetSize() const;
|
||||
|
||||
std::size_t CopyTo(void* buffer, std::size_t size) const;
|
||||
};
|
||||
|
||||
explicit BufferPool(const std::shared_ptr<IPC::SharedMemory>& memory);
|
||||
|
||||
~BufferPool();
|
||||
|
||||
const std::shared_ptr<IPC::SharedMemory>& GetMemory() const;
|
||||
};
|
||||
|
||||
} // Interop
|
||||
} // detail
|
||||
|
||||
} // Managed
|
||||
} // Bond
|
||||
} // IPC
|
||||
|
||||
#pragma managed(pop)
|
|
@ -0,0 +1,66 @@
|
|||
#pragma once
|
||||
#pragma managed(push, off)
|
||||
|
||||
#include "BufferPool.h"
|
||||
#include <memory>
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
namespace Managed
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
namespace Interop
|
||||
{
|
||||
class InputBuffer
|
||||
{
|
||||
public:
|
||||
InputBuffer();
|
||||
|
||||
InputBuffer(const BufferPool::ConstBuffer& buffer, const std::shared_ptr<IPC::SharedMemory>& memory);
|
||||
|
||||
InputBuffer(const InputBuffer& other);
|
||||
|
||||
~InputBuffer();
|
||||
|
||||
void Read(void* buffer, std::uint32_t size);
|
||||
|
||||
const void* Allocate(std::uint32_t size);
|
||||
|
||||
float ReadFloat();
|
||||
|
||||
double ReadDouble();
|
||||
|
||||
std::uint8_t ReadByte();
|
||||
|
||||
std::uint16_t ReadUInt16();
|
||||
|
||||
std::uint32_t ReadUInt32();
|
||||
|
||||
std::uint64_t ReadUInt64();
|
||||
|
||||
std::uint16_t ReadVarUInt16();
|
||||
|
||||
std::uint32_t ReadVarUInt32();
|
||||
|
||||
std::uint64_t ReadVarUInt64();
|
||||
|
||||
void Skip(std::uint32_t size);
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
};
|
||||
|
||||
} // Interop
|
||||
} // detail
|
||||
|
||||
} // Managed
|
||||
} // Bond
|
||||
} // IPC
|
||||
|
||||
#pragma managed(pop)
|
|
@ -0,0 +1,62 @@
|
|||
#pragma once
|
||||
#pragma managed(push, off)
|
||||
|
||||
#include "BufferPool.h"
|
||||
#include <memory>
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
namespace Managed
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
namespace Interop
|
||||
{
|
||||
class OutputBuffer
|
||||
{
|
||||
public:
|
||||
OutputBuffer(const BufferPool& pool, std::size_t minBlobSize);
|
||||
|
||||
~OutputBuffer();
|
||||
|
||||
void Write(const void* value, std::uint32_t size);
|
||||
|
||||
void* Allocate(std::uint32_t size);
|
||||
|
||||
void WriteFloat(float value);
|
||||
|
||||
void WriteDouble(double value);
|
||||
|
||||
void WriteByte(std::uint8_t value);
|
||||
|
||||
void WriteUInt16(std::uint16_t value);
|
||||
|
||||
void WriteUInt32(std::uint32_t value);
|
||||
|
||||
void WriteUInt64(std::uint64_t value);
|
||||
|
||||
void WriteVarUInt16(std::uint16_t value);
|
||||
|
||||
void WriteVarUInt32(std::uint32_t value);
|
||||
|
||||
void WriteVarUInt64(std::uint64_t value);
|
||||
|
||||
BufferPool::ConstBuffer GetBuffer() &&;
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
};
|
||||
|
||||
} // Interop
|
||||
} // detail
|
||||
|
||||
} // Managed
|
||||
} // Bond
|
||||
} // IPC
|
||||
|
||||
#pragma managed(pop)
|
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
|
@ -0,0 +1,72 @@
|
|||
#include "stdafx.h"
|
||||
#include "detail/Interop/BufferPool.h"
|
||||
#include <IPC/Bond/BufferPool.h>
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
namespace Managed
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
namespace Interop
|
||||
{
|
||||
BufferPool::ConstBuffer::ConstBuffer(const IPC::Bond::DefaultConstBuffer& impl)
|
||||
: shared_ptr{ std::make_shared<IPC::Bond::DefaultConstBuffer>(impl) }
|
||||
{}
|
||||
|
||||
BufferPool::ConstBuffer::~ConstBuffer() = default;
|
||||
|
||||
std::size_t BufferPool::ConstBuffer::GetSize() const
|
||||
{
|
||||
std::size_t size = 0;
|
||||
|
||||
for (const auto& blob : **this)
|
||||
{
|
||||
size += blob.size();
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
std::size_t BufferPool::ConstBuffer::CopyTo(void* buffer, std::size_t size) const
|
||||
{
|
||||
auto ptr = static_cast<char*>(buffer);
|
||||
|
||||
for (const auto& blob : **this)
|
||||
{
|
||||
if (auto sz = (std::min)(size, blob.size()))
|
||||
{
|
||||
std::memcpy(ptr, blob.data(), sz);
|
||||
ptr += sz;
|
||||
size -= sz;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ptr - static_cast<char*>(buffer);
|
||||
}
|
||||
|
||||
|
||||
BufferPool::BufferPool(const std::shared_ptr<IPC::SharedMemory>& memory)
|
||||
: shared_ptr{ std::make_shared<IPC::Bond::DefaultBufferPool>(memory) }
|
||||
{}
|
||||
|
||||
BufferPool::~BufferPool() = default;
|
||||
|
||||
const std::shared_ptr<IPC::SharedMemory>& BufferPool::GetMemory() const
|
||||
{
|
||||
return get()->GetMemory();
|
||||
}
|
||||
|
||||
} // Interop
|
||||
} // detail
|
||||
|
||||
} // Managed
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,122 @@
|
|||
#include "stdafx.h"
|
||||
#include "detail/Interop/InputBuffer.h"
|
||||
#include <IPC/Bond/InputBuffer.h>
|
||||
#include <IPC/Managed/detail/Throw.h>
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
namespace Managed
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
using IPC::Managed::detail::InvokeThrow;
|
||||
|
||||
namespace Interop
|
||||
{
|
||||
class InputBuffer::Impl : public IPC::Bond::DefaultInputBuffer
|
||||
{
|
||||
public:
|
||||
using DefaultInputBuffer::DefaultInputBuffer;
|
||||
};
|
||||
|
||||
InputBuffer::InputBuffer()
|
||||
: m_impl{ std::make_unique<Impl>() }
|
||||
{}
|
||||
|
||||
InputBuffer::InputBuffer(const BufferPool::ConstBuffer& buffer, const std::shared_ptr<IPC::SharedMemory>& memory)
|
||||
: m_impl{ std::make_unique<Impl>(*buffer, memory) }
|
||||
{}
|
||||
|
||||
InputBuffer::InputBuffer(const InputBuffer& other)
|
||||
: m_impl{ std::make_unique<Impl>(*other.m_impl) }
|
||||
{}
|
||||
|
||||
InputBuffer::~InputBuffer() = default;
|
||||
|
||||
void InputBuffer::Read(void* buffer, std::uint32_t size)
|
||||
{
|
||||
InvokeThrow([&] { m_impl->Read(buffer, size); });
|
||||
}
|
||||
|
||||
const void* InputBuffer::Allocate(std::uint32_t size)
|
||||
{
|
||||
return InvokeThrow([&] { return m_impl->Allocate(size); });
|
||||
}
|
||||
|
||||
float InputBuffer::ReadFloat()
|
||||
{
|
||||
float value;
|
||||
InvokeThrow([&] { m_impl->Read(value); });
|
||||
return value;
|
||||
}
|
||||
|
||||
double InputBuffer::ReadDouble()
|
||||
{
|
||||
double value;
|
||||
InvokeThrow([&] { m_impl->Read(value); });
|
||||
return value;
|
||||
}
|
||||
|
||||
std::uint8_t InputBuffer::ReadByte()
|
||||
{
|
||||
std::uint8_t value;
|
||||
InvokeThrow([&] { m_impl->Read(value); });
|
||||
return value;
|
||||
}
|
||||
|
||||
std::uint16_t InputBuffer::ReadUInt16()
|
||||
{
|
||||
std::uint16_t value;
|
||||
InvokeThrow([&] { m_impl->Read(value); });
|
||||
return value;
|
||||
}
|
||||
|
||||
std::uint32_t InputBuffer::ReadUInt32()
|
||||
{
|
||||
std::uint32_t value;
|
||||
InvokeThrow([&] { m_impl->Read(value); });
|
||||
return value;
|
||||
}
|
||||
|
||||
std::uint64_t InputBuffer::ReadUInt64()
|
||||
{
|
||||
std::uint64_t value;
|
||||
InvokeThrow([&] { m_impl->Read(value); });
|
||||
return value;
|
||||
}
|
||||
|
||||
std::uint16_t InputBuffer::ReadVarUInt16()
|
||||
{
|
||||
std::uint16_t value;
|
||||
InvokeThrow([&] { m_impl->ReadVariableUnsigned(value); });
|
||||
return value;
|
||||
}
|
||||
|
||||
std::uint32_t InputBuffer::ReadVarUInt32()
|
||||
{
|
||||
std::uint32_t value;
|
||||
InvokeThrow([&] { m_impl->ReadVariableUnsigned(value); });
|
||||
return value;
|
||||
}
|
||||
|
||||
std::uint64_t InputBuffer::ReadVarUInt64()
|
||||
{
|
||||
std::uint64_t value;
|
||||
InvokeThrow([&] { m_impl->ReadVariableUnsigned(value); });
|
||||
return value;
|
||||
}
|
||||
|
||||
void InputBuffer::Skip(std::uint32_t size)
|
||||
{
|
||||
InvokeThrow([&] { m_impl->Skip(size); });
|
||||
}
|
||||
|
||||
} // Interop
|
||||
} // detail
|
||||
|
||||
} // Managed
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,96 @@
|
|||
#include "stdafx.h"
|
||||
#include "detail/Interop/OutputBuffer.h"
|
||||
#include <IPC/Bond/OutputBuffer.h>
|
||||
#include <IPC/Managed/detail/Throw.h>
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
namespace Managed
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
using IPC::Managed::detail::InvokeThrow;
|
||||
|
||||
namespace Interop
|
||||
{
|
||||
class OutputBuffer::Impl : public IPC::Bond::DefaultOutputBuffer
|
||||
{
|
||||
public:
|
||||
using DefaultOutputBuffer::DefaultOutputBuffer;
|
||||
};
|
||||
|
||||
OutputBuffer::OutputBuffer(const BufferPool& pool, std::size_t minBlobSize)
|
||||
: m_impl{ std::make_unique<Impl>(pool, minBlobSize) }
|
||||
{}
|
||||
|
||||
OutputBuffer::~OutputBuffer() = default;
|
||||
|
||||
void OutputBuffer::Write(const void* value, std::uint32_t size)
|
||||
{
|
||||
InvokeThrow([&] { m_impl->Write(value, size); });
|
||||
}
|
||||
|
||||
void* OutputBuffer::Allocate(std::uint32_t size)
|
||||
{
|
||||
return InvokeThrow([&] { return m_impl->Allocate(size); });
|
||||
}
|
||||
|
||||
void OutputBuffer::WriteFloat(float value)
|
||||
{
|
||||
InvokeThrow([&] { m_impl->Write(value); });
|
||||
}
|
||||
|
||||
void OutputBuffer::WriteDouble(double value)
|
||||
{
|
||||
InvokeThrow([&] { m_impl->Write(value); });
|
||||
}
|
||||
|
||||
void OutputBuffer::WriteByte(std::uint8_t value)
|
||||
{
|
||||
InvokeThrow([&] { m_impl->Write(value); });
|
||||
}
|
||||
|
||||
void OutputBuffer::WriteUInt16(std::uint16_t value)
|
||||
{
|
||||
InvokeThrow([&] { m_impl->Write(value); });
|
||||
}
|
||||
|
||||
void OutputBuffer::WriteUInt32(std::uint32_t value)
|
||||
{
|
||||
InvokeThrow([&] { m_impl->Write(value); });
|
||||
}
|
||||
|
||||
void OutputBuffer::WriteUInt64(std::uint64_t value)
|
||||
{
|
||||
InvokeThrow([&] { m_impl->Write(value); });
|
||||
}
|
||||
|
||||
void OutputBuffer::WriteVarUInt16(std::uint16_t value)
|
||||
{
|
||||
InvokeThrow([&] { m_impl->WriteVariableUnsigned(value); });
|
||||
}
|
||||
|
||||
void OutputBuffer::WriteVarUInt32(std::uint32_t value)
|
||||
{
|
||||
InvokeThrow([&] { m_impl->WriteVariableUnsigned(value); });
|
||||
}
|
||||
|
||||
void OutputBuffer::WriteVarUInt64(std::uint64_t value)
|
||||
{
|
||||
InvokeThrow([&] { m_impl->WriteVariableUnsigned(value); });
|
||||
}
|
||||
|
||||
BufferPool::ConstBuffer OutputBuffer::GetBuffer() &&
|
||||
{
|
||||
return InvokeThrow([&] { return std::move(*m_impl).GetBuffer(); });
|
||||
}
|
||||
|
||||
} // Interop
|
||||
} // detail
|
||||
|
||||
} // Managed
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,20 @@
|
|||
#include "stdafx.h"
|
||||
#include "IPC/Managed/detail/Interop/TransportImpl.h"
|
||||
#include <IPC/Bond/BufferPool.h>
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Managed
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
namespace Interop
|
||||
{
|
||||
template Transport<IPC::Bond::DefaultConstBuffer, IPC::Bond::DefaultConstBuffer>;
|
||||
|
||||
} // Interop
|
||||
} // detail
|
||||
|
||||
} // Managed
|
||||
} // IPC
|
|
@ -0,0 +1 @@
|
|||
#include "stdafx.h"
|
10
LICENSE
10
LICENSE
|
@ -1,6 +1,10 @@
|
|||
MIT License
|
||||
IPC.Bond (Inter-Process Communication via Bond)
|
||||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
Copyright (c) Microsoft Corporation
|
||||
|
||||
All rights reserved.
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -12,7 +16,7 @@
|
|||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
|
||||
<ProjectGuid>{705098F7-93DC-4954-8165-FDDCD66231F0}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<CLRSupport>true</CLRSupport>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<PropertyGroup>
|
||||
<TargetName>IPC.Bond.$(ProjectName)</TargetName>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>..\..\IPC.Bond.snk</AssemblyOriginatorKeyFile>
|
||||
<LinkKeyFile>..\..\IPC.Bond.snk</LinkKeyFile>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<Link>
|
||||
<AdditionalDependencies>wininet.lib;..\..\IPC\$(Platform)\$(Configuration)\IPC.lib;..\..\IPC\$(Platform)\$(Configuration)\IPC.Interop.lib;..\..\IPC\$(Platform)\$(Configuration)\IPC.Managed.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalOptions>/ignore:4248 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
<Lib>
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Lib>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..\Inc;..\..\Inc;..\..\Interop\Inc;..\..\IPC\Inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4494;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Bond">
|
||||
<HintPath>..\..\IPC\Packages\Bond.Core.CSharp.6.0.0\lib\net45\Bond.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="IPC.Managed">
|
||||
<HintPath>..\..\IPC\$(Platform)\$(Configuration)\IPC.Managed.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="IPC.Managed.Object">
|
||||
<HintPath>..\..\IPC\$(Platform)\$(Configuration)\IPC.Managed.Object.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="IPC.Managed.Transport">
|
||||
<HintPath>..\..\IPC\$(Platform)\$(Configuration)\IPC.Managed.Transport.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\Src\AssemblyInfo.cpp" />
|
||||
<ClCompile Include="..\Src\BufferPool.cpp" />
|
||||
<ClCompile Include="..\Src\InputStream.cpp" />
|
||||
<ClCompile Include="..\Src\OutputStream.cpp" />
|
||||
<ClCompile Include="..\Src\Transport.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Interop\Build\Interop.vcxproj">
|
||||
<Project>{a13012c1-76de-4d1d-a58b-2361d2be8f65}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\Native\Build\Native.vcxproj">
|
||||
<Project>{2030ED0D-4667-4299-87CD-ACE298BDF56D}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\Inc\BufferPool.h" />
|
||||
<ClInclude Include="..\Inc\InputStream.h" />
|
||||
<ClInclude Include="..\Inc\OutputStream.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Import Project="..\..\IPC\Packages\boost.1.63.0.0\build\native\boost.targets" Condition="Exists('..\..\IPC\Packages\boost.1.63.0.0\build\native\boost.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\IPC\Packages\boost.1.63.0.0\build\native\boost.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\IPC\Packages\boost.1.63.0.0\build\native\boost.targets'))" />
|
||||
<Error Condition="!Exists('..\..\IPC\Packages\boost_date_time-vc140.1.63.0.0\build\native\boost_date_time-vc140.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\IPC\Packages\boost_date_time-vc140.1.63.0.0\build\native\boost_date_time-vc140.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\..\IPC\Packages\boost_date_time-vc140.1.63.0.0\build\native\boost_date_time-vc140.targets" Condition="Exists('..\..\IPC\Packages\boost_date_time-vc140.1.63.0.0\build\native\boost_date_time-vc140.targets')" />
|
||||
</Project>
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\Src\AssemblyInfo.cpp" />
|
||||
<ClCompile Include="..\Src\BufferPool.cpp" />
|
||||
<ClCompile Include="..\Src\InputStream.cpp" />
|
||||
<ClCompile Include="..\Src\OutputStream.cpp" />
|
||||
<ClCompile Include="..\Src\Transport.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\Inc\BufferPool.h" />
|
||||
<ClInclude Include="..\Inc\InputStream.h" />
|
||||
<ClInclude Include="..\Inc\OutputStream.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="boost" version="1.63.0.0" targetFramework="native" />
|
||||
<package id="boost_date_time-vc140" version="1.63.0.0" targetFramework="native" />
|
||||
</packages>
|
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
|
||||
#include "detail/Interop/BufferPool.h"
|
||||
#include "IPC/Managed/detail/NativeObject.h"
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
namespace Managed
|
||||
{
|
||||
public ref class BufferPool
|
||||
{
|
||||
public:
|
||||
ref class ConstBuffer
|
||||
{
|
||||
internal:
|
||||
ConstBuffer(const detail::Interop::BufferPool::ConstBuffer& buffer);
|
||||
|
||||
property const detail::Interop::BufferPool::ConstBuffer& Impl
|
||||
{
|
||||
const detail::Interop::BufferPool::ConstBuffer& get();
|
||||
}
|
||||
|
||||
public:
|
||||
property System::Int32 Length
|
||||
{
|
||||
System::Int32 get();
|
||||
}
|
||||
|
||||
void CopyTo(cli::array<System::Byte>^ buffer, System::Int32 offset);
|
||||
|
||||
private:
|
||||
System::Int32 CalculateLength();
|
||||
|
||||
IPC::Managed::detail::NativeObject<detail::Interop::BufferPool::ConstBuffer> m_impl;
|
||||
System::Lazy<System::Int32> m_length;
|
||||
};
|
||||
|
||||
BufferPool(IPC::Managed::SharedMemory^ memory);
|
||||
|
||||
property IPC::Managed::SharedMemory^ Memory
|
||||
{
|
||||
IPC::Managed::SharedMemory^ get();
|
||||
}
|
||||
|
||||
internal:
|
||||
property detail::Interop::BufferPool& Impl
|
||||
{
|
||||
detail::Interop::BufferPool& get();
|
||||
}
|
||||
|
||||
private:
|
||||
IPC::Managed::detail::NativeObject<detail::Interop::BufferPool> m_impl;
|
||||
};
|
||||
|
||||
} // Managed
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,65 @@
|
|||
#pragma once
|
||||
|
||||
#include "BufferPool.h"
|
||||
#include "detail/Interop/InputBuffer.h"
|
||||
#include "IPC/Managed/detail/NativeObject.h"
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
namespace Managed
|
||||
{
|
||||
public ref class InputStream : public ::Bond::IO::IInputStream, public ::Bond::IO::ICloneable<InputStream^>
|
||||
{
|
||||
public:
|
||||
InputStream(BufferPool::ConstBuffer^ buffer, IPC::Managed::SharedMemory^ inputMemory);
|
||||
|
||||
virtual property System::Int64 Length
|
||||
{
|
||||
System::Int64 get();
|
||||
}
|
||||
|
||||
virtual property System::Int64 Position
|
||||
{
|
||||
System::Int64 get();
|
||||
|
||||
void set(System::Int64 value);
|
||||
}
|
||||
|
||||
virtual System::Single ReadFloat();
|
||||
|
||||
virtual System::Double ReadDouble();
|
||||
|
||||
virtual System::Byte ReadUInt8();
|
||||
|
||||
virtual System::UInt16 ReadUInt16();
|
||||
|
||||
virtual System::UInt32 ReadUInt32();
|
||||
|
||||
virtual System::UInt64 ReadUInt64();
|
||||
|
||||
virtual System::UInt16 ReadVarUInt16();
|
||||
|
||||
virtual System::UInt32 ReadVarUInt32();
|
||||
|
||||
virtual System::UInt64 ReadVarUInt64();
|
||||
|
||||
virtual System::String^ ReadString(System::Text::Encoding^ encoding, System::Int32 count);
|
||||
|
||||
virtual System::ArraySegment<System::Byte> ReadBytes(System::Int32 count);
|
||||
|
||||
virtual void SkipBytes(System::Int32 count);
|
||||
|
||||
virtual InputStream^ Clone();
|
||||
|
||||
private:
|
||||
InputStream(InputStream% other);
|
||||
|
||||
IPC::Managed::detail::NativeObject<detail::Interop::InputBuffer> m_impl;
|
||||
};
|
||||
|
||||
} // Managed
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include "BufferPool.h"
|
||||
#include "detail/Interop/OutputBuffer.h"
|
||||
#include "IPC/Managed/detail/NativeObject.h"
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
namespace Managed
|
||||
{
|
||||
public ref class OutputStream : ::Bond::IO::IOutputStream
|
||||
{
|
||||
public:
|
||||
OutputStream(BufferPool^ pool, [System::Runtime::InteropServices::Optional] System::UInt32 minBlobSize);
|
||||
|
||||
virtual property System::Int64 Position
|
||||
{
|
||||
System::Int64 get();
|
||||
|
||||
void set(System::Int64 value);
|
||||
}
|
||||
|
||||
virtual void WriteFloat(System::Single value);
|
||||
|
||||
virtual void WriteDouble(System::Double value);
|
||||
|
||||
virtual void WriteUInt8(System::Byte value);
|
||||
|
||||
virtual void WriteUInt16(System::UInt16 value);
|
||||
|
||||
virtual void WriteUInt32(System::UInt32 value);
|
||||
|
||||
virtual void WriteUInt64(System::UInt64 value);
|
||||
|
||||
virtual void WriteVarUInt16(System::UInt16 value);
|
||||
|
||||
virtual void WriteVarUInt32(System::UInt32 value);
|
||||
|
||||
virtual void WriteVarUInt64(System::UInt64 value);
|
||||
|
||||
virtual void WriteString(System::Text::Encoding^ encoding, System::String^ value, System::Int32 count);
|
||||
|
||||
virtual void WriteBytes(System::ArraySegment<System::Byte> data);
|
||||
|
||||
BufferPool::ConstBuffer^ GetBuffer();
|
||||
|
||||
private:
|
||||
IPC::Managed::detail::NativeObject<detail::Interop::OutputBuffer> m_impl;
|
||||
};
|
||||
|
||||
} // Managed
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,18 @@
|
|||
using namespace System;
|
||||
using namespace System::Reflection;
|
||||
using namespace System::Runtime::InteropServices;
|
||||
|
||||
[assembly:AssemblyTitleAttribute("IPC.Bond.Managed")];
|
||||
[assembly:AssemblyDescriptionAttribute("")];
|
||||
[assembly:AssemblyConfigurationAttribute("")];
|
||||
[assembly:AssemblyCompanyAttribute("Microsoft")];
|
||||
[assembly:AssemblyProductAttribute("IPC.Bond.Managed")];
|
||||
[assembly:AssemblyCopyrightAttribute("Copyright © Microsoft 2017")];
|
||||
[assembly:AssemblyTrademarkAttribute("")];
|
||||
[assembly:AssemblyCultureAttribute("")];
|
||||
|
||||
[assembly:CLSCompliantAttribute(true)];
|
||||
|
||||
[assembly:ComVisible(false)];
|
||||
|
||||
[assembly:AssemblyVersionAttribute("1.0.*")];
|
|
@ -0,0 +1,69 @@
|
|||
#include "BufferPool.h"
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
namespace Managed
|
||||
{
|
||||
BufferPool::ConstBuffer::ConstBuffer(const detail::Interop::BufferPool::ConstBuffer& buffer)
|
||||
: m_impl{ buffer },
|
||||
m_length{
|
||||
gcnew System::Func<System::Int32>(this, &BufferPool::ConstBuffer::CalculateLength),
|
||||
System::Threading::LazyThreadSafetyMode::PublicationOnly }
|
||||
{}
|
||||
|
||||
const detail::Interop::BufferPool::ConstBuffer& BufferPool::ConstBuffer::Impl::get()
|
||||
{
|
||||
return *m_impl;
|
||||
}
|
||||
|
||||
System::Int32 BufferPool::ConstBuffer::CalculateLength()
|
||||
{
|
||||
return static_cast<int>(m_impl->GetSize());
|
||||
}
|
||||
|
||||
System::Int32 BufferPool::ConstBuffer::Length::get()
|
||||
{
|
||||
return m_length.Value;
|
||||
}
|
||||
|
||||
void BufferPool::ConstBuffer::CopyTo(cli::array<System::Byte>^ buffer, System::Int32 offset)
|
||||
{
|
||||
if (buffer == nullptr)
|
||||
{
|
||||
throw gcnew System::ArgumentNullException{ "buffer" };
|
||||
}
|
||||
|
||||
auto length = Length;
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
if (offset + length > buffer->Length)
|
||||
{
|
||||
throw gcnew System::ArgumentException{ "Insufficient buffer size.", "buffer" };
|
||||
}
|
||||
|
||||
pin_ptr<System::Byte> ptr = &buffer[offset];
|
||||
m_impl->CopyTo(ptr, length);
|
||||
}
|
||||
}
|
||||
|
||||
BufferPool::BufferPool(IPC::Managed::SharedMemory^ memory)
|
||||
: m_impl{ memory->Impl }
|
||||
{}
|
||||
|
||||
IPC::Managed::SharedMemory^ BufferPool::Memory::get()
|
||||
{
|
||||
return gcnew IPC::Managed::SharedMemory{ m_impl->GetMemory() };
|
||||
}
|
||||
|
||||
detail::Interop::BufferPool& BufferPool::Impl::get()
|
||||
{
|
||||
return *m_impl;
|
||||
}
|
||||
|
||||
} // Managed
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,132 @@
|
|||
#include "InputStream.h"
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
namespace Managed
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
using IPC::Managed::detail::ThrowManagedException;
|
||||
|
||||
} // detail
|
||||
|
||||
namespace
|
||||
{
|
||||
[[noreturn]] void ThrowNotImplemented()
|
||||
{
|
||||
throw gcnew System::NotImplementedException{};
|
||||
}
|
||||
|
||||
} // anonymous
|
||||
|
||||
|
||||
InputStream::InputStream(BufferPool::ConstBuffer^ buffer, IPC::Managed::SharedMemory^ inputMemory)
|
||||
try
|
||||
: m_impl{ buffer->Impl, inputMemory->Impl }
|
||||
{}
|
||||
catch (const std::exception& /*e*/)
|
||||
{
|
||||
detail::ThrowManagedException(std::current_exception());
|
||||
}
|
||||
|
||||
InputStream::InputStream(InputStream% other)
|
||||
try
|
||||
: m_impl{ *other.m_impl }
|
||||
{}
|
||||
catch (const std::exception& /*e*/)
|
||||
{
|
||||
detail::ThrowManagedException(std::current_exception());
|
||||
}
|
||||
|
||||
System::Int64 InputStream::Length::get()
|
||||
{
|
||||
ThrowNotImplemented();
|
||||
}
|
||||
|
||||
System::Int64 InputStream::Position::get()
|
||||
{
|
||||
// TODO: Needs to be implemented. (?)
|
||||
ThrowNotImplemented();
|
||||
}
|
||||
|
||||
void InputStream::Position::set(System::Int64 value)
|
||||
{
|
||||
// TODO: Needs to be implemented. (?)
|
||||
ThrowNotImplemented();
|
||||
}
|
||||
|
||||
System::Single InputStream::ReadFloat()
|
||||
{
|
||||
return m_impl->ReadFloat();
|
||||
}
|
||||
|
||||
System::Double InputStream::ReadDouble()
|
||||
{
|
||||
return m_impl->ReadDouble();
|
||||
}
|
||||
|
||||
System::Byte InputStream::ReadUInt8()
|
||||
{
|
||||
return m_impl->ReadByte();
|
||||
}
|
||||
|
||||
System::UInt16 InputStream::ReadUInt16()
|
||||
{
|
||||
return m_impl->ReadUInt16();
|
||||
}
|
||||
|
||||
System::UInt32 InputStream::ReadUInt32()
|
||||
{
|
||||
return m_impl->ReadUInt32();
|
||||
}
|
||||
|
||||
System::UInt64 InputStream::ReadUInt64()
|
||||
{
|
||||
return m_impl->ReadUInt64();
|
||||
}
|
||||
|
||||
System::UInt16 InputStream::ReadVarUInt16()
|
||||
{
|
||||
return m_impl->ReadVarUInt16();
|
||||
}
|
||||
|
||||
System::UInt32 InputStream::ReadVarUInt32()
|
||||
{
|
||||
return m_impl->ReadVarUInt32();
|
||||
}
|
||||
|
||||
System::UInt64 InputStream::ReadVarUInt64()
|
||||
{
|
||||
return m_impl->ReadVarUInt64();
|
||||
}
|
||||
|
||||
System::String^ InputStream::ReadString(System::Text::Encoding^ encoding, System::Int32 count)
|
||||
{
|
||||
auto ptr = m_impl->Allocate(count);
|
||||
return encoding->GetString(static_cast<unsigned char*>(const_cast<void*>(ptr)), count);
|
||||
}
|
||||
|
||||
System::ArraySegment<System::Byte> InputStream::ReadBytes(System::Int32 count)
|
||||
{
|
||||
auto buffer = gcnew cli::array<System::Byte>(count);
|
||||
pin_ptr<System::Byte> ptr = &buffer[0];
|
||||
m_impl->Read(ptr, count);
|
||||
return System::ArraySegment<System::Byte>(buffer);
|
||||
}
|
||||
|
||||
void InputStream::SkipBytes(System::Int32 count)
|
||||
{
|
||||
m_impl->Skip(count);
|
||||
}
|
||||
|
||||
InputStream^ InputStream::Clone()
|
||||
{
|
||||
return gcnew InputStream{ *this };
|
||||
}
|
||||
|
||||
} // Managed
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,120 @@
|
|||
#include "OutputStream.h"
|
||||
#include <vcclr.h>
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Bond
|
||||
{
|
||||
namespace Managed
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
using IPC::Managed::detail::ThrowManagedException;
|
||||
|
||||
} // detail
|
||||
|
||||
namespace
|
||||
{
|
||||
[[noreturn]] void ThrowNotImplemented()
|
||||
{
|
||||
throw gcnew System::NotImplementedException{};
|
||||
}
|
||||
|
||||
} // anonymous
|
||||
|
||||
|
||||
OutputStream::OutputStream(BufferPool^ pool, System::UInt32 minBlobSize)
|
||||
try
|
||||
: m_impl{ pool->Impl, static_cast<std::size_t>(minBlobSize) }
|
||||
{}
|
||||
catch (const std::exception& /*e*/)
|
||||
{
|
||||
detail::ThrowManagedException(std::current_exception());
|
||||
}
|
||||
|
||||
System::Int64 OutputStream::Position::get()
|
||||
{
|
||||
#ifdef NDEBUG
|
||||
ThrowNotImplemented();
|
||||
#else
|
||||
return 0;
|
||||
#endif // NDEBUG
|
||||
}
|
||||
|
||||
void OutputStream::Position::set(System::Int64 /*value*/)
|
||||
{
|
||||
ThrowNotImplemented();
|
||||
}
|
||||
|
||||
void OutputStream::WriteFloat(System::Single value)
|
||||
{
|
||||
m_impl->WriteFloat(value);
|
||||
}
|
||||
|
||||
void OutputStream::WriteDouble(System::Double value)
|
||||
{
|
||||
m_impl->WriteDouble(value);
|
||||
}
|
||||
|
||||
void OutputStream::WriteUInt8(System::Byte value)
|
||||
{
|
||||
m_impl->WriteByte(value);
|
||||
}
|
||||
|
||||
void OutputStream::WriteUInt16(System::UInt16 value)
|
||||
{
|
||||
m_impl->WriteUInt16(value);
|
||||
}
|
||||
|
||||
void OutputStream::WriteUInt32(System::UInt32 value)
|
||||
{
|
||||
m_impl->WriteUInt32(value);
|
||||
}
|
||||
|
||||
void OutputStream::WriteUInt64(System::UInt64 value)
|
||||
{
|
||||
m_impl->WriteUInt64(value);
|
||||
}
|
||||
|
||||
void OutputStream::WriteVarUInt16(System::UInt16 value)
|
||||
{
|
||||
m_impl->WriteVarUInt16(value);
|
||||
}
|
||||
|
||||
void OutputStream::WriteVarUInt32(System::UInt32 value)
|
||||
{
|
||||
m_impl->WriteVarUInt32(value);
|
||||
}
|
||||
|
||||
void OutputStream::WriteVarUInt64(System::UInt64 value)
|
||||
{
|
||||
m_impl->WriteVarUInt64(value);
|
||||
}
|
||||
|
||||
void OutputStream::WriteString(System::Text::Encoding^ encoding, System::String^ value, System::Int32 count)
|
||||
{
|
||||
auto ptr = m_impl->Allocate(count);
|
||||
pin_ptr<const wchar_t> str = PtrToStringChars(value);
|
||||
encoding->GetBytes(const_cast<wchar_t*>(str), value->Length, static_cast<unsigned char*>(ptr), count);
|
||||
}
|
||||
|
||||
void OutputStream::WriteBytes(System::ArraySegment<System::Byte> data)
|
||||
{
|
||||
if (data.Array != nullptr && data.Count > 0)
|
||||
{
|
||||
pin_ptr<System::Byte> ptr = &data.Array[data.Offset];
|
||||
m_impl->Write(ptr, data.Count);
|
||||
}
|
||||
}
|
||||
|
||||
BufferPool::ConstBuffer^ OutputStream::GetBuffer()
|
||||
{
|
||||
auto buffer = gcnew BufferPool::ConstBuffer{ std::move(*m_impl).GetBuffer() };
|
||||
delete this;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
} // Managed
|
||||
} // Bond
|
||||
} // IPC
|
|
@ -0,0 +1,38 @@
|
|||
#include "IPC/Managed/detail/TransportImpl.h"
|
||||
#include "BufferPool.h"
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
namespace Managed
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template <>
|
||||
struct Convert<IPC::Bond::DefaultConstBuffer>
|
||||
{
|
||||
using type = IPC::Bond::Managed::BufferPool::ConstBuffer^;
|
||||
|
||||
static const IPC::Bond::DefaultConstBuffer& From(type% from)
|
||||
{
|
||||
return *from->Impl;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Convert<IPC::Bond::Managed::BufferPool::ConstBuffer^>
|
||||
{
|
||||
using type = IPC::Bond::DefaultConstBuffer;
|
||||
|
||||
static IPC::Bond::Managed::BufferPool::ConstBuffer^ From(const type& from)
|
||||
{
|
||||
return gcnew IPC::Bond::Managed::BufferPool::ConstBuffer{ from };
|
||||
}
|
||||
};
|
||||
|
||||
template Transport<IPC::Bond::Managed::BufferPool::ConstBuffer^, IPC::Bond::Managed::BufferPool::ConstBuffer^>;
|
||||
|
||||
} // detail
|
||||
|
||||
} // Managed
|
||||
} // IPC
|
|
@ -0,0 +1,81 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\Accept.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\Acceptor.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\BlobCast.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\BufferPool.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\BufferPoolFwd.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\Client.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\Connect.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\Connector.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\DefaultTraits.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\detail\BlobHolder.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\detail\BufferPoolHolder.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\detail\ComponentBase.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\InputBuffer.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\OutputBuffer.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\Serializer.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\Server.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\Transport.h" />
|
||||
<ClInclude Include="..\Inc\stdafx.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\Src\stdafx.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{2030ED0D-4667-4299-87CD-ACE298BDF56D}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<PropertyGroup>
|
||||
<TargetName>IPC.Bond</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<Lib>
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Lib>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..\..\Inc;..\Inc;..\..\bond\build\target\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4494;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Import Project="..\..\IPC\Packages\boost.1.63.0.0\build\native\boost.targets" Condition="Exists('..\..\IPC\Packages\boost.1.63.0.0\build\native\boost.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\IPC\Packages\boost.1.63.0.0\build\native\boost.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\IPC\Packages\boost.1.63.0.0\build\native\boost.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="detail">
|
||||
<UniqueIdentifier>{45e3f9ed-bd76-4f45-8e40-1d9c56304c65}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\Src\stdafx.cpp">
|
||||
<Filter>detail</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\Inc\stdafx.h">
|
||||
<Filter>detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\BufferPool.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\InputBuffer.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\OutputBuffer.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\Client.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\Server.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\DefaultTraits.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\Acceptor.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\Connector.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\detail\ComponentBase.h">
|
||||
<Filter>detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\Accept.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\Connect.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\BlobCast.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\detail\BlobHolder.h">
|
||||
<Filter>detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\Serializer.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\detail\BufferPoolHolder.h">
|
||||
<Filter>detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\BufferPoolFwd.h" />
|
||||
<ClInclude Include="..\..\Inc\IPC\Bond\Transport.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="boost" version="1.63.0.0" targetFramework="native" />
|
||||
</packages>
|
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
|
@ -0,0 +1 @@
|
|||
#include "stdafx.h"
|
25
README.md
25
README.md
|
@ -1,3 +1,28 @@
|
|||
# IPC.Bond
|
||||
|
||||
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/Microsoft/IPC/blob/master/LICENSE)
|
||||
|
||||
IPC.Bond is an extension of [IPC](https://github.com/Microsoft/IPC) library that provides inter-process communication using shared memory on Windows with [Bond](https://github.com/Microsoft/bond) serialization.<br/>
|
||||
|
||||
# Build
|
||||
|
||||
The library is developed and maintained with [Visual Studio 2015](https://msdn.microsoft.com/en-us/library/dd831853.aspx).
|
||||
In order to build the library you will need to do the following:
|
||||
1. Restore NuGet packages for [IPC.Bond.sln](https://github.com/Microsoft/IPC.Bond/blob/master/IPC.Bond.sln).
|
||||
2. Build the [Bond](https://github.com/Microsoft/bond) (only core C++) submodule using helper [bond.cmd](https://github.com/Microsoft/IPC.Bond/blob/master/bond.cmd) script.
|
||||
3. Build the [IPC](https://github.com/Microsoft/IPC) submodule using [IPC.sln](https://github.com/Microsoft/IPC/blob/master/IPC.sln).
|
||||
4. Build the [IPC.Bond.sln](https://github.com/Microsoft/IPC.Bond/blob/master/IPC.Bond.sln).
|
||||
|
||||
# Getting Started
|
||||
|
||||
Start with [C++](https://github.com/Microsoft/IPC.Bond/blob/master/UnitTests/TransportTests.cpp) and [C#](https://github.com/Microsoft/IPC.Bond/blob/master/UnitTestsManaged/TransportTests.cs) tests.
|
||||
|
||||
# Contributing
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
# License
|
||||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Licensed under the [MIT](https://github.com/Microsoft/IPC.Bond/blob/master/LICENSE) License.
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
using System;
|
||||
|
||||
namespace IPC.Bond.Managed
|
||||
{
|
||||
using IPC.Managed;
|
||||
|
||||
public abstract class AccessorBase<T, Interface, InterfaceImpl, AccessorImpl> : Disposable<AccessorImpl>, IAccessor<Interface>
|
||||
where Interface : class, IComponent
|
||||
where InterfaceImpl : class, IComponent
|
||||
where T : Interface
|
||||
where AccessorImpl : class, IAccessor<InterfaceImpl>
|
||||
{
|
||||
internal AccessorBase(AccessorImpl impl)
|
||||
: base(impl)
|
||||
{
|
||||
impl.Connected += OnConnected;
|
||||
impl.Disconnected += OnDisconnected;
|
||||
impl.Error += OnError;
|
||||
}
|
||||
|
||||
protected abstract T ConnectComponent(InterfaceImpl impl);
|
||||
|
||||
protected abstract T DisconnectComponent(InterfaceImpl impl);
|
||||
|
||||
public event EventHandler<ComponentEventArgs<T>> Connected;
|
||||
|
||||
private event EventHandler<ComponentEventArgs<Interface>> ConnectedInternal;
|
||||
event EventHandler<ComponentEventArgs<Interface>> IAccessor<Interface>.Connected
|
||||
{
|
||||
add { ConnectedInternal += value; }
|
||||
remove { ConnectedInternal -= value; }
|
||||
}
|
||||
|
||||
public event EventHandler<ComponentEventArgs<T>> Disconnected;
|
||||
|
||||
private event EventHandler<ComponentEventArgs<Interface>> DisconnectedInternal;
|
||||
event EventHandler<ComponentEventArgs<Interface>> IAccessor<Interface>.Disconnected
|
||||
{
|
||||
add { DisconnectedInternal += value; }
|
||||
remove { DisconnectedInternal -= value; }
|
||||
}
|
||||
|
||||
public event EventHandler<ErrorEventArgs> Error;
|
||||
|
||||
private void OnConnected(object sender, ComponentEventArgs<InterfaceImpl> args)
|
||||
{
|
||||
var component = ConnectComponent(args.Component);
|
||||
|
||||
var connected = Connected;
|
||||
connected?.Invoke(this, new ComponentEventArgs<T>(component));
|
||||
|
||||
var connectedInternal = ConnectedInternal;
|
||||
connectedInternal?.Invoke(this, new ComponentEventArgs<Interface>(component));
|
||||
}
|
||||
|
||||
private void OnDisconnected(object sender, ComponentEventArgs<InterfaceImpl> args)
|
||||
{
|
||||
var component = DisconnectComponent(args.Component);
|
||||
|
||||
var disconnected = Disconnected;
|
||||
disconnected?.Invoke(this, new ComponentEventArgs<T>(component));
|
||||
|
||||
var disconnectedInternal = DisconnectedInternal;
|
||||
disconnectedInternal?.Invoke(this, new ComponentEventArgs<Interface>(component));
|
||||
}
|
||||
|
||||
private void OnError(object sender, ErrorEventArgs args)
|
||||
{
|
||||
var error = Error;
|
||||
error?.Invoke(this, args);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("IPC.Bond.Managed.Transport")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Microsoft")]
|
||||
[assembly: AssemblyProduct("IPC.Bond.Managed")]
|
||||
[assembly: AssemblyCopyright("Copyright © Microsoft 2017")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
[assembly: CLSCompliant(false)]
|
||||
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IPC.Bond.Managed
|
||||
{
|
||||
using IPC.Managed;
|
||||
|
||||
public partial class Transport<Request, Response>
|
||||
{
|
||||
public class Client : Component, IClient<Request, Response>
|
||||
{
|
||||
private readonly Serializer _serializer;
|
||||
|
||||
internal Client(IClient<BufferPool.ConstBuffer, BufferPool.ConstBuffer> impl, Serializer serializer)
|
||||
: base(impl)
|
||||
{
|
||||
_serializer = serializer;
|
||||
}
|
||||
|
||||
/// <remarks>The caller is responsible for disposing the <param name="allocator"/></remarks>
|
||||
public async Task<Response> InvokeAsync(Request request, TimeSpan timeout = default(TimeSpan))
|
||||
{
|
||||
Task<BufferPool.ConstBuffer> responseBufferTask;
|
||||
|
||||
using (var requestBuffer = _serializer.Serialize(request))
|
||||
{
|
||||
responseBufferTask = Impl.InvokeAsync(requestBuffer, timeout);
|
||||
}
|
||||
|
||||
using (var responseBuffer = await responseBufferTask)
|
||||
{
|
||||
return _serializer.Deserialize<Response>(responseBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <remarks>The supplied allocator will be reclaimed by GC.</remarks>
|
||||
Task<Response> IClient<Request, Response>.InvokeAsync(Request request, TimeSpan timeout)
|
||||
{
|
||||
return InvokeAsync(request, timeout);
|
||||
}
|
||||
|
||||
private new IClient<BufferPool.ConstBuffer, BufferPool.ConstBuffer> Impl
|
||||
{
|
||||
get { return (IClient<BufferPool.ConstBuffer, BufferPool.ConstBuffer>)base.Impl; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace IPC.Bond.Managed
|
||||
{
|
||||
using IPC.Managed;
|
||||
|
||||
public partial class Transport<Request, Response>
|
||||
{
|
||||
public class ClientAccessor :
|
||||
AccessorBase<
|
||||
Client,
|
||||
IClient<Request, Response>,
|
||||
IClient<BufferPool.ConstBuffer, BufferPool.ConstBuffer>,
|
||||
IClientAccessor<BufferPool.ConstBuffer, BufferPool.ConstBuffer>>,
|
||||
IClientAccessor<Request, Response>
|
||||
{
|
||||
private readonly Func<IComponent, Serializer> _serializerMaker;
|
||||
private Client _client;
|
||||
|
||||
internal ClientAccessor(
|
||||
IClientAccessor<BufferPool.ConstBuffer, BufferPool.ConstBuffer> impl,
|
||||
Func<IComponent, Serializer> serializerMaker)
|
||||
: base(impl)
|
||||
{
|
||||
_serializerMaker = serializerMaker;
|
||||
}
|
||||
|
||||
public Client Client
|
||||
{
|
||||
get
|
||||
{
|
||||
return _client ?? ConnectComponent(Impl.Client);
|
||||
}
|
||||
}
|
||||
|
||||
IClient<Request, Response> IClientAccessor<Request, Response>.Client
|
||||
{
|
||||
get { return Client; }
|
||||
}
|
||||
|
||||
protected override Client ConnectComponent(IClient<BufferPool.ConstBuffer, BufferPool.ConstBuffer> impl)
|
||||
{
|
||||
var client = new Client(impl, _serializerMaker(impl));
|
||||
|
||||
Interlocked.Exchange(ref _client, client);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
protected override Client DisconnectComponent(IClient<BufferPool.ConstBuffer, BufferPool.ConstBuffer> impl)
|
||||
{
|
||||
return Interlocked.Exchange(ref _client, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IPC.Bond.Managed
|
||||
{
|
||||
using IPC.Managed;
|
||||
|
||||
public partial class Transport<Request, Response>
|
||||
{
|
||||
public class ClientConnector :
|
||||
Disposable<IClientConnector<BufferPool.ConstBuffer, BufferPool.ConstBuffer>>,
|
||||
IClientConnector<Request, Response>
|
||||
{
|
||||
private readonly Func<IComponent, Serializer> _serializerMaker;
|
||||
|
||||
internal ClientConnector(
|
||||
IClientConnector<BufferPool.ConstBuffer, BufferPool.ConstBuffer> impl,
|
||||
Func<IComponent, Serializer> serializerMaker)
|
||||
: base(impl)
|
||||
{
|
||||
_serializerMaker = serializerMaker;
|
||||
}
|
||||
|
||||
public async Task<Client> ConnectAsync(string acceptorName, TimeSpan timeout = default(TimeSpan))
|
||||
{
|
||||
var client = await Impl.ConnectAsync(acceptorName, timeout);
|
||||
return new Client(client, _serializerMaker(client));
|
||||
}
|
||||
|
||||
async Task<IClient<Request, Response>> IClientConnector<Request, Response>.ConnectAsync(string acceptorName, TimeSpan timeout)
|
||||
{
|
||||
return await ConnectAsync(acceptorName, timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
using System;
|
||||
|
||||
namespace IPC.Bond.Managed
|
||||
{
|
||||
using IPC.Managed;
|
||||
|
||||
public partial class Transport<Request, Response>
|
||||
{
|
||||
public abstract class Component : Disposable<IComponent>, IComponent
|
||||
{
|
||||
internal Component(IComponent impl)
|
||||
: base(impl)
|
||||
{
|
||||
impl.Closed += OnClosed;
|
||||
}
|
||||
|
||||
public SharedMemory InputMemory
|
||||
{
|
||||
get { return Impl.InputMemory; }
|
||||
}
|
||||
|
||||
public SharedMemory OutputMemory
|
||||
{
|
||||
get { return Impl.OutputMemory; }
|
||||
}
|
||||
|
||||
public bool IsClosed
|
||||
{
|
||||
get { return Impl.IsClosed; }
|
||||
}
|
||||
|
||||
public event EventHandler Closed;
|
||||
|
||||
public void Close()
|
||||
{
|
||||
Impl.Close();
|
||||
}
|
||||
|
||||
private void OnClosed(object sender, EventArgs args)
|
||||
{
|
||||
var closed = Closed;
|
||||
closed?.Invoke(this, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
namespace IPC.Bond.Managed
|
||||
{
|
||||
public class Config : IPC.Managed.Config
|
||||
{
|
||||
public global::Bond.ProtocolType ProtocolType { get; set; } = global::Bond.ProtocolType.COMPACT_PROTOCOL;
|
||||
|
||||
public bool Marshal { get; set; } = true;
|
||||
|
||||
public uint MinBlobSize { get; set; } = 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
|
||||
namespace IPC.Bond.Managed
|
||||
{
|
||||
public class Disposable<T> : IDisposable
|
||||
where T : class, IDisposable
|
||||
{
|
||||
private T _impl;
|
||||
|
||||
internal Disposable(T impl)
|
||||
{
|
||||
_impl = impl;
|
||||
}
|
||||
|
||||
internal T Impl
|
||||
{
|
||||
get { return _impl; }
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_impl != null)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_impl.Dispose();
|
||||
}
|
||||
|
||||
_impl = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
namespace IPC.Bond.Managed
|
||||
{
|
||||
using IPC.Managed;
|
||||
|
||||
public class Serializer
|
||||
{
|
||||
private readonly BufferPool _pool;
|
||||
private readonly SharedMemory _inputMemory;
|
||||
private readonly uint _minBlobSize;
|
||||
private readonly ISerializer<InputStream, OutputStream> _serializer;
|
||||
|
||||
public Serializer(global::Bond.ProtocolType protocol, bool marshal, BufferPool pool, SharedMemory inputMemory, uint minBlobSize = 0)
|
||||
: this(SerializerFactory.Create<InputStream, OutputStream>(protocol, marshal), pool, inputMemory, minBlobSize)
|
||||
{ }
|
||||
|
||||
public Serializer(ISerializer<InputStream, OutputStream> serializer, BufferPool pool, SharedMemory inputMemory, uint minBlobSize = 0)
|
||||
{
|
||||
_pool = pool;
|
||||
_inputMemory = inputMemory;
|
||||
_minBlobSize = minBlobSize;
|
||||
_serializer = serializer;
|
||||
}
|
||||
|
||||
public global::Bond.ProtocolType ProtocolType
|
||||
{
|
||||
get { return _serializer.ProtocolType; }
|
||||
}
|
||||
|
||||
public bool IsMarshaled
|
||||
{
|
||||
get { return _serializer.IsMarshaled; }
|
||||
}
|
||||
|
||||
public BufferPool.ConstBuffer Serialize<T>(T obj)
|
||||
{
|
||||
using (var output = new OutputStream(_pool, _minBlobSize))
|
||||
{
|
||||
_serializer.Serialize(output, obj);
|
||||
return output.GetBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
public T Deserialize<T>(BufferPool.ConstBuffer buffer)
|
||||
{
|
||||
using (var input = new InputStream(buffer, _inputMemory))
|
||||
{
|
||||
return _serializer.Deserialize<T>(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
namespace IPC.Bond.Managed
|
||||
{
|
||||
using IInputStream = global::Bond.IO.IInputStream;
|
||||
using IOutputStream = global::Bond.IO.IOutputStream;
|
||||
|
||||
public interface ISerializer<Input, Output>
|
||||
where Input : IInputStream, global::Bond.IO.ICloneable<Input>
|
||||
where Output : IOutputStream
|
||||
{
|
||||
global::Bond.ProtocolType ProtocolType { get; }
|
||||
|
||||
bool IsMarshaled { get; }
|
||||
|
||||
void Serialize<T>(Output output, T obj);
|
||||
|
||||
T Deserialize<T>(Input input);
|
||||
}
|
||||
|
||||
public struct CompactBinarySerializer<Input, Output> : ISerializer<Input, Output>
|
||||
where Input : IInputStream, global::Bond.IO.ICloneable<Input>
|
||||
where Output : IOutputStream
|
||||
{
|
||||
public global::Bond.ProtocolType ProtocolType
|
||||
{
|
||||
get { return global::Bond.ProtocolType.COMPACT_PROTOCOL; }
|
||||
}
|
||||
|
||||
public bool IsMarshaled
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public void Serialize<T>(Output output, T obj)
|
||||
{
|
||||
global::Bond.Serialize.To(new global::Bond.Protocols.CompactBinaryWriter<Output>(output), obj);
|
||||
}
|
||||
|
||||
public T Deserialize<T>(Input input)
|
||||
{
|
||||
return global::Bond.Deserialize<T>.From(new global::Bond.Protocols.CompactBinaryReader<Input>(input));
|
||||
}
|
||||
}
|
||||
|
||||
public struct FastBinarySerializer<Input, Output> : ISerializer<Input, Output>
|
||||
where Input : IInputStream, global::Bond.IO.ICloneable<Input>
|
||||
where Output : IOutputStream
|
||||
{
|
||||
public global::Bond.ProtocolType ProtocolType
|
||||
{
|
||||
get { return global::Bond.ProtocolType.FAST_PROTOCOL; }
|
||||
}
|
||||
|
||||
public bool IsMarshaled
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public void Serialize<T>(Output output, T obj)
|
||||
{
|
||||
global::Bond.Serialize.To(new global::Bond.Protocols.FastBinaryWriter<Output>(output), obj);
|
||||
}
|
||||
|
||||
public T Deserialize<T>(Input input)
|
||||
{
|
||||
return global::Bond.Deserialize<T>.From(new global::Bond.Protocols.FastBinaryReader<Input>(input));
|
||||
}
|
||||
}
|
||||
|
||||
public struct SimpleBinarySerializer<Input, Output> : ISerializer<Input, Output>
|
||||
where Input : IInputStream, global::Bond.IO.ICloneable<Input>
|
||||
where Output : IOutputStream
|
||||
{
|
||||
public global::Bond.ProtocolType ProtocolType
|
||||
{
|
||||
get { return global::Bond.ProtocolType.SIMPLE_PROTOCOL; }
|
||||
}
|
||||
|
||||
public bool IsMarshaled
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public void Serialize<T>(Output output, T obj)
|
||||
{
|
||||
global::Bond.Serialize.To(new global::Bond.Protocols.SimpleBinaryWriter<Output>(output), obj);
|
||||
}
|
||||
|
||||
public T Deserialize<T>(Input input)
|
||||
{
|
||||
return global::Bond.Deserialize<T>.From(new global::Bond.Protocols.SimpleBinaryReader<Input>(input));
|
||||
}
|
||||
}
|
||||
|
||||
public struct CompactBinaryMarshaler<Input, Output> : ISerializer<Input, Output>
|
||||
where Input : IInputStream, global::Bond.IO.ICloneable<Input>
|
||||
where Output : IOutputStream
|
||||
{
|
||||
public global::Bond.ProtocolType ProtocolType
|
||||
{
|
||||
get { return global::Bond.ProtocolType.COMPACT_PROTOCOL; }
|
||||
}
|
||||
|
||||
public bool IsMarshaled
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public void Serialize<T>(Output output, T obj)
|
||||
{
|
||||
global::Bond.Marshal.To(new global::Bond.Protocols.CompactBinaryWriter<Output>(output), obj);
|
||||
}
|
||||
|
||||
public T Deserialize<T>(Input input)
|
||||
{
|
||||
return global::Bond.Unmarshal<T>.From(input);
|
||||
}
|
||||
}
|
||||
|
||||
public struct FastBinaryMarshaler<Input, Output> : ISerializer<Input, Output>
|
||||
where Input : IInputStream, global::Bond.IO.ICloneable<Input>
|
||||
where Output : IOutputStream
|
||||
{
|
||||
public global::Bond.ProtocolType ProtocolType
|
||||
{
|
||||
get { return global::Bond.ProtocolType.FAST_PROTOCOL; }
|
||||
}
|
||||
|
||||
public bool IsMarshaled
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public void Serialize<T>(Output output, T obj)
|
||||
{
|
||||
global::Bond.Marshal.To(new global::Bond.Protocols.FastBinaryWriter<Output>(output), obj);
|
||||
}
|
||||
|
||||
public T Deserialize<T>(Input input)
|
||||
{
|
||||
return global::Bond.Unmarshal<T>.From(input);
|
||||
}
|
||||
}
|
||||
|
||||
public struct SimpleBinaryMarshaler<Input, Output> : ISerializer<Input, Output>
|
||||
where Input : IInputStream, global::Bond.IO.ICloneable<Input>
|
||||
where Output : IOutputStream
|
||||
{
|
||||
public global::Bond.ProtocolType ProtocolType
|
||||
{
|
||||
get { return global::Bond.ProtocolType.SIMPLE_PROTOCOL; }
|
||||
}
|
||||
|
||||
public bool IsMarshaled
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public void Serialize<T>(Output output, T obj)
|
||||
{
|
||||
global::Bond.Marshal.To(new global::Bond.Protocols.SimpleBinaryWriter<Output>(output), obj);
|
||||
}
|
||||
|
||||
public T Deserialize<T>(Input input)
|
||||
{
|
||||
return global::Bond.Unmarshal<T>.From(input);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SerializerFactory
|
||||
{
|
||||
public static ISerializer<Input, Output> Create<Input, Output>(global::Bond.ProtocolType protocol, bool marshal)
|
||||
where Input : IInputStream, global::Bond.IO.ICloneable<Input>
|
||||
where Output : IOutputStream
|
||||
{
|
||||
switch (protocol)
|
||||
{
|
||||
case global::Bond.ProtocolType.COMPACT_PROTOCOL:
|
||||
return marshal ? (ISerializer<Input, Output>)new CompactBinaryMarshaler<Input, Output>() : new CompactBinarySerializer<Input, Output>();
|
||||
case global::Bond.ProtocolType.FAST_PROTOCOL:
|
||||
return marshal ? (ISerializer<Input, Output>)new FastBinaryMarshaler<Input, Output>() : new FastBinarySerializer<Input, Output>();
|
||||
case global::Bond.ProtocolType.SIMPLE_PROTOCOL:
|
||||
return marshal ? (ISerializer<Input, Output>)new SimpleBinaryMarshaler<Input, Output>() : new SimpleBinarySerializer<Input, Output>();
|
||||
}
|
||||
|
||||
throw new IPC.Managed.Exception("Unknown protocol.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
|
||||
namespace IPC.Bond.Managed
|
||||
{
|
||||
using IPC.Managed;
|
||||
|
||||
public partial class Transport<Request, Response>
|
||||
{
|
||||
public class Server : Component, IServer<Request, Response>
|
||||
{
|
||||
internal Server(IServer<BufferPool.ConstBuffer, BufferPool.ConstBuffer> impl)
|
||||
: base(impl)
|
||||
{
|
||||
impl.Error += OnError;
|
||||
}
|
||||
|
||||
public event EventHandler<ErrorEventArgs> Error;
|
||||
|
||||
private void OnError(object sender, ErrorEventArgs args)
|
||||
{
|
||||
var error = Error;
|
||||
error?.Invoke(this, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
using System;
|
||||
|
||||
namespace IPC.Bond.Managed
|
||||
{
|
||||
using IPC.Managed;
|
||||
|
||||
public partial class Transport<Request, Response>
|
||||
{
|
||||
public class ServerAcceptor :
|
||||
Disposable<IServerAcceptor<BufferPool.ConstBuffer, BufferPool.ConstBuffer>>,
|
||||
IServerAcceptor<Request, Response>
|
||||
{
|
||||
internal ServerAcceptor(IServerAcceptor<BufferPool.ConstBuffer, BufferPool.ConstBuffer> impl)
|
||||
: base(impl)
|
||||
{
|
||||
impl.Accepted += OnAccepted;
|
||||
impl.Error += OnError;
|
||||
}
|
||||
|
||||
public event EventHandler<ComponentEventArgs<Server>> Accepted;
|
||||
|
||||
private event EventHandler<ComponentEventArgs<IServer<Request, Response>>> AcceptedInternal;
|
||||
event EventHandler<ComponentEventArgs<IServer<Request, Response>>> IServerAcceptor<Request, Response>.Accepted
|
||||
{
|
||||
add { AcceptedInternal += value; }
|
||||
remove { AcceptedInternal -= value; }
|
||||
}
|
||||
|
||||
public event EventHandler<ErrorEventArgs> Error;
|
||||
|
||||
private void OnAccepted(object sender, ComponentEventArgs<IServer<BufferPool.ConstBuffer, BufferPool.ConstBuffer>> args)
|
||||
{
|
||||
var server = new Server(args.Component);
|
||||
|
||||
var accepted = Accepted;
|
||||
accepted?.Invoke(this, new ComponentEventArgs<Server>(server));
|
||||
|
||||
var acceptedInternal = AcceptedInternal;
|
||||
acceptedInternal?.Invoke(this, new ComponentEventArgs<IServer<Request, Response>>(server));
|
||||
}
|
||||
|
||||
private void OnError(object sender, ErrorEventArgs args)
|
||||
{
|
||||
var error = Error;
|
||||
error?.Invoke(this, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace IPC.Bond.Managed
|
||||
{
|
||||
using IPC.Managed;
|
||||
|
||||
public partial class Transport<Request, Response>
|
||||
{
|
||||
public class ServersAccessor :
|
||||
AccessorBase<
|
||||
Server,
|
||||
IServer<Request, Response>,
|
||||
IServer<BufferPool.ConstBuffer, BufferPool.ConstBuffer>,
|
||||
IServersAccessor<BufferPool.ConstBuffer, BufferPool.ConstBuffer>>,
|
||||
IServersAccessor<Request, Response>
|
||||
{
|
||||
internal ServersAccessor(IServersAccessor<BufferPool.ConstBuffer, BufferPool.ConstBuffer> impl)
|
||||
: base(impl)
|
||||
{ }
|
||||
|
||||
public IReadOnlyCollection<Server> Servers
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ReadOnlyCollection<Server>(Impl.Servers.Select(server => new Server(server)).ToList());
|
||||
}
|
||||
}
|
||||
|
||||
IReadOnlyCollection<IServer<Request, Response>> IServersAccessor<Request, Response>.Servers
|
||||
{
|
||||
get { return Servers; }
|
||||
}
|
||||
|
||||
protected override Server ConnectComponent(IServer<BufferPool.ConstBuffer, BufferPool.ConstBuffer> impl)
|
||||
{
|
||||
return new Server(impl);
|
||||
}
|
||||
|
||||
protected override Server DisconnectComponent(IServer<BufferPool.ConstBuffer, BufferPool.ConstBuffer> impl)
|
||||
{
|
||||
return new Server(impl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
using System;
|
||||
|
||||
namespace IPC.Bond.Managed
|
||||
{
|
||||
using IPC.Managed;
|
||||
|
||||
public partial class Transport<Request, Response> :
|
||||
Disposable<ITransport<BufferPool.ConstBuffer, BufferPool.ConstBuffer>>,
|
||||
ITransport<Request, Response>
|
||||
where Request : new()
|
||||
where Response : new()
|
||||
{
|
||||
private static readonly TransportFactory _factory;
|
||||
|
||||
private readonly Config _config;
|
||||
|
||||
static Transport()
|
||||
{
|
||||
_factory = new TransportFactory();
|
||||
_factory.Register(typeof(BufferPool).Assembly);
|
||||
}
|
||||
|
||||
public Transport()
|
||||
: this(new Config())
|
||||
{ }
|
||||
|
||||
public Transport(Config config)
|
||||
: base(_factory.Make<BufferPool.ConstBuffer, BufferPool.ConstBuffer>(config))
|
||||
{
|
||||
_config = config;
|
||||
}
|
||||
|
||||
public ClientConnector MakeClientConnector()
|
||||
{
|
||||
return new ClientConnector(Impl.MakeClientConnector(), GetSerializerMaker());
|
||||
}
|
||||
|
||||
IClientConnector<Request, Response> ITransport<Request, Response>.MakeClientConnector()
|
||||
{
|
||||
return MakeClientConnector();
|
||||
}
|
||||
|
||||
public ServerAcceptor MakeServerAcceptor(string name, HandlerFactory<Request, Response> handlerFactory)
|
||||
{
|
||||
return new ServerAcceptor(Impl.MakeServerAcceptor(name, MakeHandlerFactory(handlerFactory)));
|
||||
}
|
||||
|
||||
IServerAcceptor<Request, Response> ITransport<Request, Response>.MakeServerAcceptor(string name, HandlerFactory<Request, Response> handlerFactory)
|
||||
{
|
||||
return MakeServerAcceptor(name, handlerFactory);
|
||||
}
|
||||
|
||||
public ClientAccessor ConnectClient(string name, bool async, TimeSpan timeout = default(TimeSpan), ClientConnector connector = null)
|
||||
{
|
||||
return new ClientAccessor(Impl.ConnectClient(name, async, timeout, connector?.Impl), GetSerializerMaker());
|
||||
}
|
||||
|
||||
IClientAccessor<Request, Response> ITransport<Request, Response>.ConnectClient(
|
||||
string name, bool async, TimeSpan timeout, IClientConnector<Request, Response> connector)
|
||||
{
|
||||
return ConnectClient(name, async, timeout, connector as ClientConnector);
|
||||
}
|
||||
|
||||
public ServersAccessor AcceptServers(string name, HandlerFactory<Request, Response> handlerFactory)
|
||||
{
|
||||
return new ServersAccessor(Impl.AcceptServers(name, MakeHandlerFactory(handlerFactory)));
|
||||
}
|
||||
|
||||
IServersAccessor<Request, Response> ITransport<Request, Response>.AcceptServers(string name, HandlerFactory<Request, Response> handlerFactory)
|
||||
{
|
||||
return AcceptServers(name, handlerFactory);
|
||||
}
|
||||
|
||||
private Func<IComponent, Serializer> GetSerializerMaker()
|
||||
{
|
||||
return component => MakeSerializer(component.InputMemory, component.OutputMemory);
|
||||
}
|
||||
|
||||
private Serializer MakeSerializer(SharedMemory inputMemory, SharedMemory outputMemory)
|
||||
{
|
||||
return new Serializer(_config.ProtocolType, _config.Marshal, new BufferPool(outputMemory), inputMemory, _config.MinBlobSize);
|
||||
}
|
||||
|
||||
private HandlerFactory<BufferPool.ConstBuffer, BufferPool.ConstBuffer> MakeHandlerFactory(HandlerFactory<Request, Response> handlerFactory)
|
||||
{
|
||||
return (inputMemory, outputMemory) =>
|
||||
{
|
||||
var handler = handlerFactory(inputMemory, outputMemory);
|
||||
var serializer = MakeSerializer(inputMemory, outputMemory);
|
||||
|
||||
return async (requestBuffer) =>
|
||||
{
|
||||
Request request;
|
||||
|
||||
using (requestBuffer)
|
||||
{
|
||||
request = serializer.Deserialize<Request>(requestBuffer);
|
||||
}
|
||||
|
||||
return serializer.Serialize(await handler(request));
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{BDAAAAAD-21A8-446E-840B-68718C49E7D4}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>IPC.Bond.Managed</RootNamespace>
|
||||
<AssemblyName>IPC.Bond.Managed.Transport</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
|
||||
<AssemblyClsCompliant>false</AssemblyClsCompliant>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<TargetFrameworkProfile />
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>..\IPC.Bond.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<OutputPath>..\x64\Debug\</OutputPath>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>..\x64\Release\</OutputPath>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Bond">
|
||||
<HintPath>..\IPC\Packages\Bond.Core.CSharp.6.0.0\lib\net45\Bond.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="IPC.Managed">
|
||||
<HintPath>..\IPC\$(Platform)\$(Configuration)\IPC.Managed.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="IPC.Managed.Object">
|
||||
<HintPath>..\IPC\$(Platform)\$(Configuration)\IPC.Managed.Object.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="IPC.Managed.Transport">
|
||||
<HintPath>..\IPC\$(Platform)\$(Configuration)\IPC.Managed.Transport.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Managed\Build\Managed.vcxproj">
|
||||
<Project>{705098f7-93dc-4954-8165-fddcd66231f0}</Project>
|
||||
<Name>Managed</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AccessorBase.cs" />
|
||||
<Compile Include="AssemblyInfo.cs" />
|
||||
<Compile Include="Client.cs" />
|
||||
<Compile Include="ClientAccessor.cs" />
|
||||
<Compile Include="ClientConnector.cs" />
|
||||
<Compile Include="Component.cs" />
|
||||
<Compile Include="Config.cs" />
|
||||
<Compile Include="Disposable.cs" />
|
||||
<Compile Include="Serializer.cs" />
|
||||
<Compile Include="SerializerFactory.cs" />
|
||||
<Compile Include="Server.cs" />
|
||||
<Compile Include="ServerAcceptor.cs" />
|
||||
<Compile Include="ServersAccessor.cs" />
|
||||
<Compile Include="Transport.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Bond.Core.CSharp" version="6.0.0" targetFramework="net46" />
|
||||
</packages>
|
|
@ -0,0 +1,128 @@
|
|||
#include "stdafx.h"
|
||||
#include "IPC/Bond/BlobCast.h"
|
||||
#include "IPC/SharedMemory.h"
|
||||
#include "IPC/detail/RandomString.h"
|
||||
#include <memory>
|
||||
|
||||
using namespace IPC::Bond;
|
||||
using IPC::detail::GenerateRandomString;
|
||||
using IPC::SharedMemory;
|
||||
using IPC::create_only;
|
||||
using IPC::anonymous_instance;
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(BlobCastTests)
|
||||
|
||||
struct BlobMock : public std::shared_ptr<std::pair<const char*, std::size_t>>
|
||||
{
|
||||
BlobMock() = default;
|
||||
|
||||
BlobMock(const char* data, std::size_t size)
|
||||
: shared_ptr{ std::make_shared<std::pair<const char*, std::size_t>>(std::make_pair(data, size)) }
|
||||
{}
|
||||
|
||||
const char* data() const
|
||||
{
|
||||
return get()->first;
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
return get()->second;
|
||||
}
|
||||
|
||||
BlobMock GetRange(std::size_t offset, std::size_t count) const
|
||||
{
|
||||
if (offset + count > size())
|
||||
{
|
||||
throw std::out_of_range{ "Count is out of range." };
|
||||
}
|
||||
|
||||
return{ data() + offset, count };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CastToBondBlobTest)
|
||||
{
|
||||
auto memory = std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024);
|
||||
|
||||
auto& data = memory->Construct<char[5]>(anonymous_instance);
|
||||
std::strcpy(data, "Data");
|
||||
|
||||
BlobMock mock{ data, sizeof(data) };
|
||||
BOOST_TEST(mock.unique());
|
||||
|
||||
bond::blob blob = BlobCast(mock, memory);
|
||||
|
||||
BOOST_TEST(blob.data() == data);
|
||||
BOOST_TEST(blob.size() == sizeof(data));
|
||||
|
||||
BOOST_TEST(mock.use_count() == 2);
|
||||
blob.clear();
|
||||
BOOST_TEST(mock.unique());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CastToBondBlobMemoryOwnershipTest)
|
||||
{
|
||||
auto memory = std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024);
|
||||
|
||||
auto& data = memory->Construct<char[5]>(anonymous_instance);
|
||||
std::strcpy(data, "Data");
|
||||
|
||||
bond::blob blob;
|
||||
{
|
||||
BlobMock mock{ data, sizeof(data) };
|
||||
BOOST_TEST(mock.unique());
|
||||
BOOST_TEST(memory.unique());
|
||||
|
||||
blob = BlobCast(mock, memory);
|
||||
}
|
||||
|
||||
BOOST_TEST(blob.data() == data);
|
||||
BOOST_TEST(blob.size() == sizeof(data));
|
||||
BOOST_TEST(memory.use_count() == 2);
|
||||
|
||||
memory.reset();
|
||||
|
||||
BOOST_TEST(std::strcmp(data, "Data") == 0);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CastFromCorrectBondBlobTest)
|
||||
{
|
||||
auto memory = std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024);
|
||||
|
||||
auto& data = memory->Construct<char[5]>(anonymous_instance);
|
||||
std::strcpy(data, "Data");
|
||||
|
||||
BlobMock mock = BlobCast<BlobMock>(BlobCast(BlobMock{ data, sizeof(data) }, memory));
|
||||
|
||||
BOOST_TEST(mock.data() == data);
|
||||
BOOST_TEST(mock.size() == sizeof(data));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CastFromCorrectBondBlobWithOffsetTest)
|
||||
{
|
||||
auto memory = std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024);
|
||||
|
||||
auto& data = memory->Construct<char[6]>(anonymous_instance);
|
||||
std::strcpy(data, "Data!");
|
||||
|
||||
constexpr std::size_t offset = 2;
|
||||
|
||||
BlobMock mock = BlobCast<BlobMock>(BlobCast(BlobMock{ data, sizeof(data) }, memory).range(offset));
|
||||
|
||||
BOOST_TEST(mock.data() == data + offset);
|
||||
BOOST_TEST(mock.size() == sizeof(data) - offset);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CastFromIncorrectBondBlobTest)
|
||||
{
|
||||
const char data[] = "Data";
|
||||
|
||||
BlobMock mock = BlobCast<BlobMock>(bond::blob{ data, sizeof(data) });
|
||||
|
||||
BOOST_TEST(!mock);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -0,0 +1,214 @@
|
|||
#include "stdafx.h"
|
||||
#include "IPC/Bond/BufferPool.h"
|
||||
#include "IPC/detail/RandomString.h"
|
||||
|
||||
using namespace IPC::Bond;
|
||||
using IPC::detail::GenerateRandomString;
|
||||
using IPC::SharedMemory;
|
||||
using IPC::create_only;
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(BufferPoolTests)
|
||||
|
||||
static_assert(std::is_copy_constructible<DefaultBufferPool>::value, "BufferPool should be copy constructible.");
|
||||
static_assert(std::is_copy_assignable<DefaultBufferPool>::value, "BufferPool should be copy assignable.");
|
||||
|
||||
static_assert(std::is_default_constructible<DefaultBufferPool::Blob>::value, "BufferPool::Blob should be default constructible.");
|
||||
static_assert(!std::is_copy_constructible<DefaultBufferPool::Blob>::value, "BufferPool::Blob should not be copy constructible.");
|
||||
static_assert(!std::is_copy_assignable<DefaultBufferPool::Blob>::value, "BufferPool::Blob should not be copy assignable.");
|
||||
static_assert(std::is_move_constructible<DefaultBufferPool::Blob>::value, "BufferPool::Blob should be move constructible.");
|
||||
static_assert(std::is_move_assignable<DefaultBufferPool::Blob>::value, "BufferPool::Blob should be move assignable.");
|
||||
|
||||
static_assert(std::is_default_constructible<DefaultBufferPool::ConstBlob>::value, "BufferPool::ConstBlob should be default constructible.");
|
||||
static_assert(std::is_copy_constructible<DefaultBufferPool::ConstBlob>::value, "BufferPool::ConstBlob should be copy constructible.");
|
||||
static_assert(std::is_copy_assignable<DefaultBufferPool::ConstBlob>::value, "BufferPool::ConstBlob should be copy assignable.");
|
||||
|
||||
static_assert(std::is_default_constructible<DefaultBufferPool::Buffer>::value, "BufferPool::Buffer should be default constructible.");
|
||||
static_assert(!std::is_copy_constructible<DefaultBufferPool::Buffer>::value, "BufferPool::Buffer should not be copy constructible.");
|
||||
static_assert(!std::is_copy_assignable<DefaultBufferPool::Buffer>::value, "BufferPool::Buffer should not be copy assignable.");
|
||||
static_assert(std::is_move_constructible<DefaultBufferPool::Buffer>::value, "BufferPool::Buffer should be move constructible.");
|
||||
static_assert(std::is_move_assignable<DefaultBufferPool::Buffer>::value, "BufferPool::Buffer should be move assignable.");
|
||||
|
||||
static_assert(std::is_default_constructible<DefaultBufferPool::ConstBuffer>::value, "BufferPool::ConstBuffer should be default constructible.");
|
||||
static_assert(std::is_copy_constructible<DefaultBufferPool::ConstBuffer>::value, "BufferPool::ConstBuffer should be copy constructible.");
|
||||
static_assert(std::is_copy_assignable<DefaultBufferPool::ConstBuffer>::value, "BufferPool::ConstBuffer should be copy assignable.");
|
||||
|
||||
static_assert(std::is_default_constructible<DefaultBufferPool::ConstBuffer::Range>::value, "BufferPool::ConstBuffer::Range should be default constructible.");
|
||||
static_assert(std::is_copy_constructible<DefaultBufferPool::ConstBuffer::Range>::value, "BufferPool::ConstBuffer::Range should be copy constructible.");
|
||||
static_assert(std::is_copy_assignable<DefaultBufferPool::ConstBuffer::Range>::value, "BufferPool::ConstBuffer::Range should be copy assignable.");
|
||||
|
||||
BOOST_AUTO_TEST_CASE(BlobTest)
|
||||
{
|
||||
auto name = GenerateRandomString();
|
||||
auto memory = std::make_shared<SharedMemory>(create_only, name.c_str(), 1024 * 1024);
|
||||
auto pool = std::make_unique<DefaultBufferPool>(memory);
|
||||
|
||||
auto blob = pool->TakeBlob();
|
||||
BOOST_TEST(!!blob);
|
||||
BOOST_TEST(blob->empty());
|
||||
|
||||
BOOST_TEST(&*blob == blob.operator->());
|
||||
|
||||
BOOST_CHECK_THROW((blob->resize(memory->GetFreeSize() + 1)), std::exception);
|
||||
|
||||
blob->resize(1024, boost::container::default_init);
|
||||
BOOST_TEST(blob->size() == 1024);
|
||||
|
||||
BOOST_TEST(memory->Contains(blob->data()));
|
||||
BOOST_TEST(memory->Contains(&*blob));
|
||||
|
||||
auto ptr = &*blob;
|
||||
blob = {};
|
||||
BOOST_TEST(!blob);
|
||||
|
||||
blob = pool->TakeBlob();
|
||||
BOOST_TEST(!!blob);
|
||||
BOOST_TEST(blob->empty());
|
||||
|
||||
BOOST_TEST(ptr == &*blob);
|
||||
|
||||
pool.reset();
|
||||
BOOST_CHECK_NO_THROW(blob = {});
|
||||
BOOST_TEST(!blob);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ConstBlobTest)
|
||||
{
|
||||
auto name = GenerateRandomString();
|
||||
auto memory = std::make_shared<SharedMemory>(create_only, name.c_str(), 1024 * 1024);
|
||||
auto pool = std::make_unique<DefaultBufferPool>(memory);
|
||||
|
||||
BOOST_TEST(!DefaultBufferPool::ConstBlob{});
|
||||
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(1024, boost::container::default_init);
|
||||
|
||||
auto data = blob->data();
|
||||
auto size = blob->size();
|
||||
|
||||
DefaultBufferPool::ConstBlob constBlob{ std::move(blob) };
|
||||
|
||||
BOOST_TEST(!!constBlob);
|
||||
BOOST_TEST(constBlob.data() == data);
|
||||
BOOST_TEST(constBlob.size() == size);
|
||||
BOOST_TEST(&*constBlob.begin() == data);
|
||||
BOOST_TEST(&*constBlob.end() == data + size);
|
||||
|
||||
BOOST_CHECK_THROW(constBlob.GetRange(1, size), std::exception);
|
||||
BOOST_CHECK_THROW(constBlob.GetRange(size, 1), std::exception);
|
||||
|
||||
constexpr std::size_t offset = 2;
|
||||
constexpr std::size_t count = 2;
|
||||
|
||||
auto range = constBlob.GetRange(offset, count);
|
||||
BOOST_TEST(!!range);
|
||||
BOOST_TEST(range.data() == data + offset);
|
||||
BOOST_TEST(range.size() == count);
|
||||
BOOST_TEST(&*range.begin() == data + offset);
|
||||
BOOST_TEST(&*range.end() == data + offset + count);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(BufferTest)
|
||||
{
|
||||
auto name = GenerateRandomString();
|
||||
auto memory = std::make_shared<SharedMemory>(create_only, name.c_str(), 1024 * 1024);
|
||||
auto pool = std::make_unique<DefaultBufferPool>(memory);
|
||||
|
||||
auto buffer = pool->TakeBuffer();
|
||||
BOOST_TEST(!!buffer);
|
||||
BOOST_TEST(buffer->empty());
|
||||
|
||||
BOOST_TEST(&*buffer == buffer.operator->());
|
||||
|
||||
BOOST_CHECK_THROW((buffer->resize(memory->GetFreeSize() + 1)), std::exception);
|
||||
|
||||
DefaultBufferPool::ConstBlob b1{ pool->TakeBlob() }, b2{ pool->TakeBlob() };
|
||||
|
||||
buffer->push_back(b1);
|
||||
buffer->push_back(b2);
|
||||
BOOST_TEST(buffer->size() == 2);
|
||||
|
||||
BOOST_TEST(memory->Contains(buffer->data()));
|
||||
BOOST_TEST(memory->Contains(&*buffer));
|
||||
|
||||
auto ptr = &*buffer;
|
||||
buffer = {};
|
||||
BOOST_TEST(!buffer);
|
||||
|
||||
buffer = pool->TakeBuffer();
|
||||
BOOST_TEST(!!buffer);
|
||||
BOOST_TEST(buffer->empty());
|
||||
|
||||
BOOST_TEST(ptr == &*buffer);
|
||||
|
||||
pool.reset();
|
||||
BOOST_CHECK_NO_THROW(buffer = {});
|
||||
BOOST_TEST(!buffer);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ConstBufferTest)
|
||||
{
|
||||
auto name = GenerateRandomString();
|
||||
auto memory = std::make_shared<SharedMemory>(create_only, name.c_str(), 1024 * 1024);
|
||||
auto pool = std::make_unique<DefaultBufferPool>(memory);
|
||||
|
||||
BOOST_TEST(!DefaultBufferPool::ConstBuffer{});
|
||||
|
||||
auto buffer = pool->TakeBuffer();
|
||||
{
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(100, boost::container::default_init);
|
||||
buffer->push_back(std::move(blob));
|
||||
}
|
||||
{
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(200, boost::container::default_init);
|
||||
buffer->push_back(std::move(blob));
|
||||
}
|
||||
|
||||
auto data = buffer->data();
|
||||
auto size = buffer->size();
|
||||
|
||||
DefaultBufferPool::ConstBuffer constBuffer{ std::move(buffer) };
|
||||
|
||||
BOOST_TEST(!!constBuffer);
|
||||
BOOST_TEST(&*constBuffer.begin() == data);
|
||||
BOOST_TEST(&*constBuffer.end() == data + size);
|
||||
|
||||
BOOST_TEST(constBuffer.size() == 300);
|
||||
|
||||
BOOST_CHECK_NO_THROW((DefaultBufferPool::ConstBuffer::Range{ constBuffer, constBuffer.begin(), 0, constBuffer.end(), 0 }));
|
||||
|
||||
BOOST_TEST((constBuffer == constBuffer));
|
||||
BOOST_TEST((DefaultBufferPool::ConstBuffer{} == DefaultBufferPool::ConstBuffer{}));
|
||||
BOOST_TEST(!(constBuffer == DefaultBufferPool::ConstBuffer{}));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ConstBufferRangeTest)
|
||||
{
|
||||
auto name = GenerateRandomString();
|
||||
auto memory = std::make_shared<SharedMemory>(create_only, name.c_str(), 1024 * 1024);
|
||||
auto pool = std::make_unique<DefaultBufferPool>(memory);
|
||||
|
||||
BOOST_TEST(DefaultBufferPool::ConstBuffer::Range{}.IsEmpty());
|
||||
|
||||
{
|
||||
auto buffer = pool->TakeBuffer();
|
||||
DefaultBufferPool::ConstBuffer constBuffer{ std::move(buffer) };
|
||||
|
||||
BOOST_TEST((DefaultBufferPool::ConstBuffer::Range{ constBuffer, constBuffer.begin(), 0, constBuffer.end(), 0 }.IsEmpty()));
|
||||
BOOST_TEST((DefaultBufferPool::ConstBuffer::Range{ constBuffer, constBuffer.begin(), 10, constBuffer.end(), 10 }.IsEmpty()));
|
||||
}
|
||||
{
|
||||
auto buffer = pool->TakeBuffer();
|
||||
buffer->push_back(pool->TakeBlob());
|
||||
DefaultBufferPool::ConstBuffer constBuffer{ std::move(buffer) };
|
||||
|
||||
BOOST_TEST(!(DefaultBufferPool::ConstBuffer::Range{ constBuffer, constBuffer.begin(), 0, constBuffer.end(), 0 }.IsEmpty()));
|
||||
BOOST_TEST(!(DefaultBufferPool::ConstBuffer::Range{ constBuffer, constBuffer.begin(), 10, constBuffer.end(), 10 }.IsEmpty()));
|
||||
BOOST_TEST((DefaultBufferPool::ConstBuffer::Range{ constBuffer, constBuffer.begin(), 0, constBuffer.begin(), 0 }.IsEmpty()));
|
||||
BOOST_TEST((DefaultBufferPool::ConstBuffer::Range{ constBuffer, constBuffer.begin(), 10, constBuffer.begin(), 10 }.IsEmpty()));
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -0,0 +1,93 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{2A3B9657-1D10-4A71-AA21-62AD4106CED4}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<PropertyGroup>
|
||||
<TargetName>IPC.Bond.$(ProjectName)</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Ws2_32.lib;Synchronization.lib;..\..\IPC\$(Platform)\$(Configuration)\IPC.lib;..\..\bond\build\target\lib\bond\bond.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
<Lib>
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Lib>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..\..\Inc;..\..\IPC\Inc;..\..\bond\build\target\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;BOND_COMPACT_BINARY_PROTOCOL;BOND_SIMPLE_BINARY_PROTOCOL;BOND_FAST_BINARY_PROTOCOL;BOND_SIMPLE_JSON_PROTOCOL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4494;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\BlobCastTests.cpp" />
|
||||
<ClCompile Include="..\BufferPoolTests.cpp" />
|
||||
<ClCompile Include="..\ClientServerTests.cpp" />
|
||||
<ClCompile Include="..\ConnectAcceptTests.cpp" />
|
||||
<ClCompile Include="..\InputBufferTests.cpp" />
|
||||
<ClCompile Include="..\OutputBufferTests.cpp" />
|
||||
<ClCompile Include="..\SerializerTests.cpp" />
|
||||
<ClCompile Include="..\stdafx.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\TransportTests.cpp" />
|
||||
<ClCompile Include="..\UsageTests.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\stdafx.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Native\Build\Native.vcxproj">
|
||||
<Project>{2030ED0D-AAAA-4299-87CD-ACE298BDF56D}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="..\..\IPC\Packages\boost.1.63.0.0\build\native\boost.targets" Condition="Exists('..\..\IPC\Packages\boost.1.63.0.0\build\native\boost.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\IPC\Packages\boost.1.63.0.0\build\native\boost.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\IPC\Packages\boost.1.63.0.0\build\native\boost.targets'))" />
|
||||
<Error Condition="!Exists('..\..\IPC\Packages\boost_unit_test_framework-vc140.1.63.0.0\build\native\boost_unit_test_framework-vc140.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\IPC\Packages\boost_unit_test_framework-vc140.1.63.0.0\build\native\boost_unit_test_framework-vc140.targets'))" />
|
||||
<Error Condition="!Exists('..\..\IPC\Packages\boost_date_time-vc140.1.63.0.0\build\native\boost_date_time-vc140.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\IPC\Packages\boost_date_time-vc140.1.63.0.0\build\native\boost_date_time-vc140.targets'))" />
|
||||
<Error Condition="!Exists('..\..\IPC\Packages\boost_locale-vc140.1.63.0.0\build\native\boost_locale-vc140.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\IPC\Packages\boost_locale-vc140.1.63.0.0\build\native\boost_locale-vc140.targets'))" />
|
||||
<Error Condition="!Exists('..\..\IPC\Packages\boost_thread-vc140.1.63.0.0\build\native\boost_thread-vc140.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\IPC\Packages\boost_thread-vc140.1.63.0.0\build\native\boost_thread-vc140.targets'))" />
|
||||
<Error Condition="!Exists('..\..\IPC\Packages\boost_system-vc140.1.63.0.0\build\native\boost_system-vc140.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\IPC\Packages\boost_system-vc140.1.63.0.0\build\native\boost_system-vc140.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\..\IPC\Packages\boost_unit_test_framework-vc140.1.63.0.0\build\native\boost_unit_test_framework-vc140.targets" Condition="Exists('..\..\IPC\Packages\boost_unit_test_framework-vc140.1.63.0.0\build\native\boost_unit_test_framework-vc140.targets')" />
|
||||
<Import Project="..\..\IPC\Packages\boost_date_time-vc140.1.63.0.0\build\native\boost_date_time-vc140.targets" Condition="Exists('..\..\IPC\Packages\boost_date_time-vc140.1.63.0.0\build\native\boost_date_time-vc140.targets')" />
|
||||
<Import Project="..\..\IPC\Packages\boost_locale-vc140.1.63.0.0\build\native\boost_locale-vc140.targets" Condition="Exists('..\..\IPC\Packages\boost_locale-vc140.1.63.0.0\build\native\boost_locale-vc140.targets')" />
|
||||
<Import Project="..\..\IPC\Packages\boost_thread-vc140.1.63.0.0\build\native\boost_thread-vc140.targets" Condition="Exists('..\..\IPC\Packages\boost_thread-vc140.1.63.0.0\build\native\boost_thread-vc140.targets')" />
|
||||
<Import Project="..\..\IPC\Packages\boost_system-vc140.1.63.0.0\build\native\boost_system-vc140.targets" Condition="Exists('..\..\IPC\Packages\boost_system-vc140.1.63.0.0\build\native\boost_system-vc140.targets')" />
|
||||
</Project>
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\stdafx.cpp" />
|
||||
<ClCompile Include="..\BlobCastTests.cpp" />
|
||||
<ClCompile Include="..\BufferPoolTests.cpp" />
|
||||
<ClCompile Include="..\OutputBufferTests.cpp" />
|
||||
<ClCompile Include="..\InputBufferTests.cpp" />
|
||||
<ClCompile Include="..\SerializerTests.cpp" />
|
||||
<ClCompile Include="..\ClientServerTests.cpp" />
|
||||
<ClCompile Include="..\ConnectAcceptTests.cpp" />
|
||||
<ClCompile Include="..\UsageTests.cpp" />
|
||||
<ClCompile Include="..\TransportTests.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\stdafx.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="boost" version="1.63.0.0" targetFramework="native" />
|
||||
<package id="boost_date_time-vc140" version="1.63.0.0" targetFramework="native" />
|
||||
<package id="boost_locale-vc140" version="1.63.0.0" targetFramework="native" />
|
||||
<package id="boost_system-vc140" version="1.63.0.0" targetFramework="native" />
|
||||
<package id="boost_thread-vc140" version="1.63.0.0" targetFramework="native" />
|
||||
<package id="boost_unit_test_framework-vc140" version="1.63.0.0" targetFramework="native" />
|
||||
</packages>
|
|
@ -0,0 +1,204 @@
|
|||
#include "stdafx.h"
|
||||
#include "IPC/Bond/Server.h"
|
||||
#include "IPC/Bond/Client.h"
|
||||
#include "IPC/Bond/Acceptor.h"
|
||||
#include "IPC/Bond/Connector.h"
|
||||
#include "IPC/detail/RandomString.h"
|
||||
#include <bond/core/tuple.h>
|
||||
|
||||
using namespace IPC::Bond;
|
||||
using IPC::detail::GenerateRandomString;
|
||||
using IPC::SharedMemory;
|
||||
using IPC::create_only;
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(ClientServerTests)
|
||||
|
||||
using Request = std::tuple<int>;
|
||||
using Response = std::tuple<int, int>;
|
||||
|
||||
class SerializerMock
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
DefaultBufferPool::ConstBuffer Serialize(const T& value);
|
||||
|
||||
template <>
|
||||
DefaultBufferPool::ConstBuffer Serialize(const Request& /*value*/)
|
||||
{
|
||||
++m_counters->m_requestSerialized;
|
||||
return{};
|
||||
}
|
||||
|
||||
template <>
|
||||
DefaultBufferPool::ConstBuffer Serialize(const Response& /*value*/)
|
||||
{
|
||||
++m_counters->m_responseSerialized;
|
||||
return{};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Deserialize(DefaultBufferPool::ConstBuffer&& buffer, T& value);
|
||||
|
||||
template <>
|
||||
void Deserialize(DefaultBufferPool::ConstBuffer&& /*buffer*/, Response& /*value*/)
|
||||
{
|
||||
++m_counters->m_responseDeserialized;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::future<T> Deserialize(DefaultBufferPool::ConstBuffer buffer);
|
||||
|
||||
template <>
|
||||
std::future<Request> Deserialize(DefaultBufferPool::ConstBuffer /*buffer*/)
|
||||
{
|
||||
++m_counters->m_requestDeserialized;
|
||||
return Deserialize<Request>();
|
||||
}
|
||||
|
||||
template <>
|
||||
std::future<Response> Deserialize(DefaultBufferPool::ConstBuffer /*buffer*/)
|
||||
{
|
||||
++m_counters->m_responseDeserialized;
|
||||
return Deserialize<Response>();
|
||||
}
|
||||
|
||||
bool CheckClientUsage() const
|
||||
{
|
||||
return m_counters->m_requestSerialized == 1
|
||||
&& m_counters->m_requestDeserialized == 0
|
||||
&& m_counters->m_responseSerialized == 0
|
||||
&& m_counters->m_responseDeserialized == 1;
|
||||
}
|
||||
|
||||
bool CheckServerUsage() const
|
||||
{
|
||||
return m_counters->m_requestSerialized == 0
|
||||
&& m_counters->m_requestDeserialized == 1
|
||||
&& m_counters->m_responseSerialized == 1
|
||||
&& m_counters->m_responseDeserialized == 0;
|
||||
}
|
||||
|
||||
void ResetCounters()
|
||||
{
|
||||
m_counters->Reset();
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
std::future<T> Deserialize()
|
||||
{
|
||||
std::promise<T> promise;
|
||||
promise.set_value({});
|
||||
return promise.get_future();
|
||||
}
|
||||
|
||||
struct Counters
|
||||
{
|
||||
void Reset()
|
||||
{
|
||||
*this = {};
|
||||
}
|
||||
|
||||
std::size_t m_requestSerialized{ 0 };
|
||||
std::size_t m_requestDeserialized{ 0 };
|
||||
std::size_t m_responseSerialized{ 0 };
|
||||
std::size_t m_responseDeserialized{ 0 };
|
||||
};
|
||||
|
||||
std::shared_ptr<Counters> m_counters{ std::make_shared<Counters>() };
|
||||
};
|
||||
|
||||
struct MockTraits : DefaultTraits
|
||||
{
|
||||
using Serializer = SerializerMock;
|
||||
};
|
||||
|
||||
using Server = Server<Request, Response, MockTraits>;
|
||||
using Client = Client<Request, Response, MockTraits>;
|
||||
using Acceptor = ServerAcceptor<Request, Response, MockTraits>;
|
||||
using Connector = ClientConnector<Request, Response, MockTraits>;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(SerializerTest)
|
||||
{
|
||||
auto name = GenerateRandomString();
|
||||
|
||||
std::unique_ptr<Server> server;
|
||||
|
||||
Acceptor acceptor{
|
||||
name.c_str(),
|
||||
[&](auto&& futureConnection)
|
||||
{
|
||||
server = std::make_unique<Server>(
|
||||
detail::BufferPoolHolder<DefaultBufferPool>{ nullptr, nullptr },
|
||||
SerializerMock{},
|
||||
futureConnection.get(),
|
||||
[](auto futureRequest, auto&& callback)
|
||||
{
|
||||
auto request = futureRequest.get();
|
||||
callback(std::tuple_cat(request, request));
|
||||
},
|
||||
[] {});
|
||||
} };
|
||||
|
||||
Client client{
|
||||
detail::BufferPoolHolder<DefaultBufferPool>{ nullptr, nullptr },
|
||||
SerializerMock{},
|
||||
Connector{}.Connect(name.c_str()).get(),
|
||||
[] {},
|
||||
{} };
|
||||
|
||||
std::promise<Response> result;
|
||||
client(Request{}, [&](std::future<Response>&& response) { result.set_value(response.get()); });
|
||||
BOOST_TEST((result.get_future().get() == Response{}));
|
||||
BOOST_TEST(client.CheckClientUsage());
|
||||
BOOST_TEST(server->CheckServerUsage());
|
||||
|
||||
client.ResetCounters();
|
||||
server->ResetCounters();
|
||||
BOOST_TEST((client(Request{}).get() == Response{}));
|
||||
BOOST_TEST(client.CheckClientUsage());
|
||||
BOOST_TEST(server->CheckServerUsage());
|
||||
|
||||
client.ResetCounters();
|
||||
server->ResetCounters();
|
||||
BOOST_TEST((client(Request{}, std::chrono::milliseconds{ 1 }).get() == Response{}));
|
||||
BOOST_TEST(client.CheckClientUsage());
|
||||
BOOST_TEST(server->CheckServerUsage());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(BufferPoolTest)
|
||||
{
|
||||
auto memory = std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024);
|
||||
auto inPool = std::make_shared<DefaultBufferPool>(memory);
|
||||
auto outPool = std::make_shared<DefaultBufferPool>(memory);
|
||||
|
||||
auto name = GenerateRandomString();
|
||||
|
||||
std::unique_ptr<Server> server;
|
||||
|
||||
Acceptor acceptor{
|
||||
name.c_str(),
|
||||
[&](auto&& futureConnection)
|
||||
{
|
||||
server = std::make_unique<Server>(
|
||||
detail::BufferPoolHolder<DefaultBufferPool>{ inPool, outPool },
|
||||
SerializerMock{},
|
||||
futureConnection.get(),
|
||||
[](auto&&...) {},
|
||||
[] {});
|
||||
} };
|
||||
|
||||
Client client{
|
||||
detail::BufferPoolHolder<DefaultBufferPool>{ inPool, outPool },
|
||||
SerializerMock{},
|
||||
Connector{}.Connect(name.c_str()).get(),
|
||||
[] {} };
|
||||
|
||||
BOOST_TEST(client.GetInputPool() == inPool);
|
||||
BOOST_TEST(client.GetOutputPool() == outPool);
|
||||
BOOST_TEST(server->GetInputPool() == inPool);
|
||||
BOOST_TEST(server->GetOutputPool() == outPool);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -0,0 +1,183 @@
|
|||
#include "stdafx.h"
|
||||
#include "IPC/Bond/Accept.h"
|
||||
#include "IPC/Bond/Connect.h"
|
||||
#include "IPC/Bond/Connector.h"
|
||||
#include "IPC/detail/RandomString.h"
|
||||
#include <bond/core/bond.h>
|
||||
#include <bond/protocol/simple_json_writer.h>
|
||||
#include <bond/core/tuple.h>
|
||||
|
||||
using namespace IPC::Bond;
|
||||
using IPC::detail::GenerateRandomString;
|
||||
using IPC::SharedMemory;
|
||||
using IPC::create_only;
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(ConnectAcceptTests)
|
||||
|
||||
using Request = std::tuple<int>;
|
||||
using Response = std::tuple<int, int>;
|
||||
|
||||
class SerializerMock : public DefaultSerializer
|
||||
{
|
||||
public:
|
||||
SerializerMock(
|
||||
bond::ProtocolType protocol,
|
||||
bool marshal,
|
||||
std::shared_ptr<DefaultBufferPool> outputPool,
|
||||
std::shared_ptr<SharedMemory> inputMemory = {},
|
||||
std::size_t minBlobSize = 0)
|
||||
: DefaultSerializer{ protocol, marshal, outputPool, std::move(inputMemory), minBlobSize },
|
||||
m_outputPool{ std::move(outputPool) }
|
||||
{}
|
||||
|
||||
const auto& GetSerializerPool() const
|
||||
{
|
||||
return m_outputPool;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<DefaultBufferPool> m_outputPool;
|
||||
};
|
||||
|
||||
struct MockTraits : DefaultTraits
|
||||
{
|
||||
using Serializer = SerializerMock;
|
||||
};
|
||||
|
||||
using ClientConnector = ClientConnector<Request, Response, MockTraits>;
|
||||
using ServerConnector = ServerConnector<Request, Response, MockTraits>;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(SerializerTest)
|
||||
{
|
||||
auto name = GenerateRandomString();
|
||||
|
||||
auto serversAccessor = AcceptServers<Request, Response, MockTraits>(
|
||||
name.c_str(),
|
||||
[](auto&&...) { return [](auto&&...) {}; });
|
||||
|
||||
auto clientAccessor = ConnectClient(name.c_str(), std::make_shared<ClientConnector>(), false);
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto servers = serversAccessor();
|
||||
if (servers->empty())
|
||||
{
|
||||
std::this_thread::yield();
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& server = servers->front();
|
||||
|
||||
BOOST_TEST(server->GetSerializerPool() == server->GetOutputPool());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
auto client = clientAccessor();
|
||||
|
||||
BOOST_TEST(client->GetSerializerPool() == client->GetOutputPool());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ReverseConnectionSerializerTest)
|
||||
{
|
||||
auto name = GenerateRandomString();
|
||||
|
||||
auto clientsAccessor = AcceptClients<Request, Response, MockTraits>(name.c_str());
|
||||
|
||||
auto serverAccessor = ConnectServer(
|
||||
name.c_str(),
|
||||
std::make_shared<ServerConnector>(),
|
||||
[](auto&&...) { return [](auto&&...) {}; },
|
||||
false);
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto clients = clientsAccessor();
|
||||
if (clients->empty())
|
||||
{
|
||||
std::this_thread::yield();
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& client = clients->front();
|
||||
|
||||
BOOST_TEST(client->GetSerializerPool() == client->GetOutputPool());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
auto server = serverAccessor();
|
||||
|
||||
BOOST_TEST(server->GetSerializerPool() == server->GetOutputPool());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(BufferPoolTest)
|
||||
{
|
||||
auto name = GenerateRandomString();
|
||||
|
||||
auto serversAccessor = AcceptServers<Request, Response, MockTraits>(
|
||||
name.c_str(),
|
||||
[](auto&&...) { return [](auto&&...) {}; });
|
||||
|
||||
auto clientAccessor = ConnectClient(name.c_str(), std::make_shared<ClientConnector>(), false);
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto servers = serversAccessor();
|
||||
if (servers->empty())
|
||||
{
|
||||
std::this_thread::yield();
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& server = servers->front();
|
||||
|
||||
BOOST_TEST(server->GetConnection().GetInputChannel().GetMemory() == server->GetInputPool()->GetMemory());
|
||||
BOOST_TEST(server->GetConnection().GetOutputChannel().GetMemory() == server->GetOutputPool()->GetMemory());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
auto client = clientAccessor();
|
||||
|
||||
BOOST_TEST(client->GetConnection().GetInputChannel().GetMemory() == client->GetInputPool()->GetMemory());
|
||||
BOOST_TEST(client->GetConnection().GetOutputChannel().GetMemory() == client->GetOutputPool()->GetMemory());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ReverseConnectionBufferPoolTest)
|
||||
{
|
||||
auto name = GenerateRandomString();
|
||||
|
||||
auto clientsAccessor = AcceptClients<Request, Response, MockTraits>(name.c_str());
|
||||
|
||||
auto serverAccessor = ConnectServer(
|
||||
name.c_str(),
|
||||
std::make_shared<ServerConnector>(),
|
||||
[](auto&&...) { return [](auto&&...) {}; },
|
||||
false);
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto clients = clientsAccessor();
|
||||
if (clients->empty())
|
||||
{
|
||||
std::this_thread::yield();
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& client = clients->front();
|
||||
|
||||
BOOST_TEST(client->GetConnection().GetInputChannel().GetMemory() == client->GetInputPool()->GetMemory());
|
||||
BOOST_TEST(client->GetConnection().GetOutputChannel().GetMemory() == client->GetOutputPool()->GetMemory());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
auto server = serverAccessor();
|
||||
|
||||
BOOST_TEST(server->GetConnection().GetInputChannel().GetMemory() == server->GetInputPool()->GetMemory());
|
||||
BOOST_TEST(server->GetConnection().GetOutputChannel().GetMemory() == server->GetOutputPool()->GetMemory());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -0,0 +1,460 @@
|
|||
#include "stdafx.h"
|
||||
#include "IPC/Bond/InputBuffer.h"
|
||||
#include "IPC/detail/RandomString.h"
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
using namespace IPC::Bond;
|
||||
using IPC::detail::GenerateRandomString;
|
||||
using IPC::SharedMemory;
|
||||
using IPC::create_only;
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(InputBufferTests)
|
||||
|
||||
static_assert(std::is_default_constructible<DefaultInputBuffer>::value, "InputBuffer should be default constructible.");
|
||||
static_assert(std::is_copy_constructible<DefaultInputBuffer>::value, "InputBuffer should be copy constructible.");
|
||||
static_assert(std::is_copy_assignable<DefaultInputBuffer>::value, "InputBuffer should be copy assignable.");
|
||||
|
||||
template <typename Function, std::size_t... I>
|
||||
void ForEach(Function&& func, std::index_sequence<I...>)
|
||||
{
|
||||
auto dummy = { (func(std::integral_constant<std::size_t, I>{}), 0)... };
|
||||
(void)dummy;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ReadTrivialTypesTest)
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
auto buffer = pool->TakeBuffer();
|
||||
|
||||
auto values = std::make_tuple(
|
||||
static_cast<std::int8_t>(1),
|
||||
static_cast<std::int16_t>(2),
|
||||
static_cast<std::int32_t>(3),
|
||||
static_cast<std::int64_t>(4),
|
||||
static_cast<std::uint8_t>(5),
|
||||
static_cast<std::uint16_t>(6),
|
||||
static_cast<std::uint32_t>(7),
|
||||
static_cast<std::uint64_t>(8),
|
||||
static_cast<float>(9.10),
|
||||
static_cast<double>(11.12));
|
||||
|
||||
ForEach(
|
||||
[&](auto index)
|
||||
{
|
||||
auto& value = std::get<index.value>(values);
|
||||
(void)index;
|
||||
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(sizeof(decltype(value)), boost::container::default_init);
|
||||
std::memcpy(blob->data(), &value, sizeof(value));
|
||||
|
||||
buffer->push_back(std::move(blob));
|
||||
},
|
||||
std::make_index_sequence<std::tuple_size<decltype(values)>::value>{});
|
||||
|
||||
DefaultInputBuffer input{ std::move(buffer), pool->GetMemory() };
|
||||
decltype(values) results;
|
||||
|
||||
BOOST_TEST(!input.IsEof());
|
||||
|
||||
ForEach(
|
||||
[&](auto index)
|
||||
{
|
||||
input.Read(std::get<index.value>(results));
|
||||
(void)index;
|
||||
},
|
||||
std::make_index_sequence<std::tuple_size<decltype(values)>::value>{});
|
||||
|
||||
BOOST_TEST(input.IsEof());
|
||||
BOOST_TEST((values == results));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ReadRawBytesTest)
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
|
||||
auto buffer = pool->TakeBuffer();
|
||||
{
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(10, 1);
|
||||
buffer->push_back(std::move(blob));
|
||||
}
|
||||
{
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(20, 2);
|
||||
buffer->push_back(std::move(blob));
|
||||
}
|
||||
{
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(30, 3);
|
||||
buffer->push_back(std::move(blob));
|
||||
}
|
||||
|
||||
DefaultInputBuffer input{ std::move(buffer), pool->GetMemory() };
|
||||
char result[10 + 20 + 30];
|
||||
auto ptr = result;
|
||||
|
||||
input.Read(ptr, 5);
|
||||
input.Read(ptr += 5, 10);
|
||||
input.Read(ptr += 10, 5);
|
||||
BOOST_CHECK_THROW(input.Read(ptr += 5, 41), std::exception);
|
||||
input.Read(ptr, 15);
|
||||
input.Read(ptr += 15, 5);
|
||||
input.Read(ptr += 5, 20);
|
||||
|
||||
BOOST_TEST(input.IsEof());
|
||||
BOOST_TEST(std::all_of(result, result + 10, [](const char& c) { return c == 1; }));
|
||||
BOOST_TEST(std::all_of(result + 10, result + 10 + 20, [](const char& c) { return c == 2; }));
|
||||
BOOST_TEST(std::all_of(result + 10 + 20, result + 10 + 20 + 30, [](const char& c) { return c == 3; }));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ReadBondBlobTest)
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
|
||||
auto buffer = pool->TakeBuffer();
|
||||
{
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(10, 1);
|
||||
buffer->push_back(std::move(blob));
|
||||
}
|
||||
{
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(10, 2);
|
||||
buffer->push_back(std::move(blob));
|
||||
}
|
||||
|
||||
DefaultBufferPool::ConstBuffer constBuffer{ std::move(buffer) };
|
||||
|
||||
DefaultInputBuffer input{ constBuffer, pool->GetMemory() };
|
||||
|
||||
bond::blob b1, b2, b3;
|
||||
|
||||
BOOST_CHECK_THROW(input.Read(b1, 20), std::exception);
|
||||
|
||||
input.Read(b1, 5);
|
||||
input.Read(b2, 5);
|
||||
input.Read(b3, 10);
|
||||
|
||||
BOOST_TEST(input.IsEof());
|
||||
BOOST_TEST(b1.size() == 5U);
|
||||
BOOST_TEST(std::all_of(b1.begin(), b1.end(), [](const char& c) { return c == 1; }));
|
||||
BOOST_TEST(b2.size() == 5U);
|
||||
BOOST_TEST(std::all_of(b2.begin(), b2.end(), [](const char& c) { return c == 1; }));
|
||||
BOOST_TEST(b3.size() == 10U);
|
||||
BOOST_TEST(std::all_of(b3.begin(), b3.end(), [](const char& c) { return c == 2; }));
|
||||
|
||||
BOOST_TEST(BlobCast<DefaultBufferPool::ConstBlob>(b1).data() == constBuffer.begin()->data());
|
||||
BOOST_TEST(BlobCast<DefaultBufferPool::ConstBlob>(b2).data() == constBuffer.begin()->data() + 5);
|
||||
BOOST_TEST(BlobCast<DefaultBufferPool::ConstBlob>(b3).data() == std::next(constBuffer.begin())->data());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(SkipTest)
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
|
||||
auto buffer = pool->TakeBuffer();
|
||||
{
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(10, 1);
|
||||
buffer->push_back(std::move(blob));
|
||||
}
|
||||
{
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(10, 2);
|
||||
buffer->push_back(std::move(blob));
|
||||
}
|
||||
|
||||
DefaultInputBuffer input{ std::move(buffer), pool->GetMemory() };
|
||||
char result[10];
|
||||
|
||||
input.Read(result, 5);
|
||||
BOOST_CHECK_THROW(input.Skip(16), std::exception);
|
||||
input.Skip(10);
|
||||
input.Read(result + 5, 5);
|
||||
|
||||
BOOST_TEST(input.IsEof());
|
||||
BOOST_TEST(std::all_of(result, result + 5, [](const char& c) { return c == 1; }));
|
||||
BOOST_TEST(std::all_of(result + 5, result + 10, [](const char& c) { return c == 2; }));
|
||||
|
||||
BOOST_CHECK_NO_THROW(input.Skip(0));
|
||||
BOOST_CHECK_THROW(input.Skip(1), std::exception);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ReadEmptyDataTest)
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
|
||||
auto buffer = pool->TakeBuffer();
|
||||
buffer->push_back(DefaultBufferPool::ConstBlob{});
|
||||
buffer->push_back(pool->TakeBlob());
|
||||
|
||||
DefaultBufferPool::ConstBuffer constBuffer{ std::move(buffer) };
|
||||
|
||||
BOOST_TEST((DefaultInputBuffer{ DefaultBufferPool::ConstBuffer{}, nullptr }.IsEof()));
|
||||
|
||||
BOOST_TEST((DefaultInputBuffer{ constBuffer, pool->GetMemory() }.IsEof()));
|
||||
|
||||
BOOST_TEST((DefaultInputBuffer{ DefaultBufferPool::ConstBuffer::Range{ constBuffer, constBuffer.begin(), 0, constBuffer.end(), 0 }, pool->GetMemory() }.IsEof()));
|
||||
|
||||
BOOST_TEST((DefaultInputBuffer{ DefaultBufferPool::ConstBuffer::Range{}, nullptr }.IsEof()));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ReadFromRangeTest)
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
|
||||
auto buffer = pool->TakeBuffer();
|
||||
{
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(10, 1);
|
||||
buffer->push_back(std::move(blob));
|
||||
}
|
||||
{
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(20, 2);
|
||||
buffer->push_back(std::move(blob));
|
||||
}
|
||||
{
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(30, 3);
|
||||
buffer->push_back(std::move(blob));
|
||||
}
|
||||
|
||||
DefaultBufferPool::ConstBuffer constBuffer{ std::move(buffer) };
|
||||
|
||||
{
|
||||
DefaultInputBuffer input{ DefaultBufferPool::ConstBuffer::Range{ constBuffer, constBuffer.begin(), 0, constBuffer.end(), 0 }, pool->GetMemory() };
|
||||
char result[10 + 20 + 30];
|
||||
|
||||
BOOST_TEST(!input.IsEof());
|
||||
input.Read(result, sizeof(result));
|
||||
BOOST_TEST(input.IsEof());
|
||||
BOOST_TEST(std::all_of(result, result + 10, [](const char& c) { return c == 1; }));
|
||||
BOOST_TEST(std::all_of(result + 10, result + 10 + 20, [](const char& c) { return c == 2; }));
|
||||
BOOST_TEST(std::all_of(result + 10 + 20, result + 10 + 20 + 30, [](const char& c) { return c == 3; }));
|
||||
}
|
||||
{
|
||||
DefaultInputBuffer input{ DefaultBufferPool::ConstBuffer::Range{ constBuffer, constBuffer.begin(), 3, constBuffer.begin(), 8 }, pool->GetMemory() };
|
||||
char result[5];
|
||||
|
||||
BOOST_TEST(!input.IsEof());
|
||||
input.Read(result, sizeof(result));
|
||||
BOOST_TEST(input.IsEof());
|
||||
BOOST_TEST(std::all_of(result, result + 5, [](const char& c) { return c == 1; }));
|
||||
}
|
||||
{
|
||||
DefaultInputBuffer input{ DefaultBufferPool::ConstBuffer::Range{ constBuffer, constBuffer.begin(), 5, std::next(constBuffer.begin(), 2), 15 }, pool->GetMemory() };
|
||||
char result[5 + 20 + 15];
|
||||
|
||||
BOOST_TEST(!input.IsEof());
|
||||
input.Read(result, sizeof(result));
|
||||
BOOST_TEST(input.IsEof());
|
||||
BOOST_TEST(std::all_of(result, result + 5, [](const char& c) { return c == 1; }));
|
||||
BOOST_TEST(std::all_of(result + 5, result + 5 + 20, [](const char& c) { return c == 2; }));
|
||||
BOOST_TEST(std::all_of(result + 5 + 20, result + 5 + 20 + 15, [](const char& c) { return c == 3; }));
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(IsEofTest)
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
|
||||
auto buffer = pool->TakeBuffer();
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(10);
|
||||
buffer->push_back(std::move(blob));
|
||||
|
||||
DefaultInputBuffer input{ std::move(buffer), pool->GetMemory() };
|
||||
|
||||
BOOST_TEST(DefaultInputBuffer{}.IsEof());
|
||||
|
||||
BOOST_TEST(!input.IsEof());
|
||||
input.Skip(5);
|
||||
BOOST_TEST(!input.IsEof());
|
||||
BOOST_CHECK_THROW(input.Skip(10), std::exception);
|
||||
BOOST_TEST(!input.IsEof());
|
||||
input.Skip(5);
|
||||
BOOST_TEST(input.IsEof());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(EqualityTest)
|
||||
{
|
||||
BOOST_TEST((DefaultInputBuffer{ DefaultBufferPool::ConstBuffer{}, nullptr } == DefaultInputBuffer{ DefaultBufferPool::ConstBuffer{}, nullptr }));
|
||||
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
|
||||
auto buffer1 = pool->TakeBuffer();
|
||||
{
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(10);
|
||||
buffer1->push_back(std::move(blob));
|
||||
}
|
||||
auto buffer2 = pool->TakeBuffer();
|
||||
{
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(10);
|
||||
buffer2->push_back(std::move(blob));
|
||||
}
|
||||
|
||||
assert(buffer1->size() == 1);
|
||||
assert(buffer2->size() == 1);
|
||||
assert(std::equal(buffer1->front().begin(), buffer1->front().end(), buffer2->front().begin(), buffer2->front().end()));
|
||||
|
||||
DefaultInputBuffer input1{ std::move(buffer1), pool->GetMemory() };
|
||||
auto input1Copy = input1;
|
||||
DefaultInputBuffer input2{ std::move(buffer2), pool->GetMemory() };
|
||||
auto input2Copy = input2;
|
||||
|
||||
BOOST_TEST(!(input1 == input2));
|
||||
BOOST_TEST(!(input1Copy == input2Copy));
|
||||
BOOST_TEST((input1 == input1Copy));
|
||||
BOOST_TEST((input2 == input2Copy));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(GetCurrentBufferTest)
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
|
||||
auto buffer = pool->TakeBuffer();
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(10);
|
||||
buffer->push_back(std::move(blob));
|
||||
|
||||
DefaultInputBuffer input{ std::move(buffer), pool->GetMemory() };
|
||||
|
||||
BOOST_TEST((GetCurrentBuffer(input) == input));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(GetBufferRangeTest)
|
||||
{
|
||||
BOOST_TEST(GetBufferRange(
|
||||
DefaultInputBuffer{ DefaultBufferPool::ConstBuffer{}, nullptr },
|
||||
DefaultInputBuffer{ DefaultBufferPool::ConstBuffer{}, nullptr }).IsEmpty());
|
||||
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
|
||||
auto buffer = pool->TakeBuffer();
|
||||
{
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(10, 1);
|
||||
buffer->push_back(std::move(blob));
|
||||
}
|
||||
{
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(20, 2);
|
||||
buffer->push_back(std::move(blob));
|
||||
}
|
||||
|
||||
DefaultBufferPool::ConstBuffer constBuffer{ std::move(buffer) };
|
||||
|
||||
DefaultInputBuffer input1{ constBuffer, pool->GetMemory() };
|
||||
DefaultInputBuffer input2{ constBuffer, pool->GetMemory() };
|
||||
|
||||
input1.Skip(5);
|
||||
input2.Skip(20);
|
||||
{
|
||||
DefaultInputBuffer input3{ GetBufferRange(input1, input2), pool->GetMemory() };
|
||||
char result[15];
|
||||
|
||||
BOOST_TEST(!input3.IsEof());
|
||||
input3.Read(result, sizeof(result));
|
||||
BOOST_TEST(input3.IsEof());
|
||||
BOOST_TEST(std::all_of(result, result + 5, [](const char& c) { return c == 1; }));
|
||||
BOOST_TEST(std::all_of(result + 5, result + 15, [](const char& c) { return c == 2; }));
|
||||
}
|
||||
input1.Skip(10);
|
||||
input2.Skip(5);
|
||||
{
|
||||
DefaultInputBuffer input3{ GetBufferRange(input1, input2), pool->GetMemory() };
|
||||
char result[10];
|
||||
|
||||
BOOST_TEST(!input3.IsEof());
|
||||
input3.Read(result, sizeof(result));
|
||||
BOOST_TEST(input3.IsEof());
|
||||
BOOST_TEST(std::all_of(result, result + 10, [](const char& c) { return c == 2; }));
|
||||
}
|
||||
input2.Skip(5);
|
||||
assert(input2.IsEof());
|
||||
{
|
||||
DefaultInputBuffer input3{ GetBufferRange(input1, input2), pool->GetMemory() };
|
||||
char result[15];
|
||||
|
||||
BOOST_TEST(!input3.IsEof());
|
||||
input3.Read(result, sizeof(result));
|
||||
BOOST_TEST(input3.IsEof());
|
||||
BOOST_TEST(std::all_of(result, result + 15, [](const char& c) { return c == 2; }));
|
||||
}
|
||||
input1.Skip(15);
|
||||
assert(input1.IsEof());
|
||||
{
|
||||
DefaultInputBuffer input3{ GetBufferRange(input1, input2), pool->GetMemory() };
|
||||
BOOST_TEST(input3.IsEof());
|
||||
BOOST_CHECK_THROW(input3.Skip(1), std::exception);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CreateInputBufferTest)
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
|
||||
auto buffer = pool->TakeBuffer();
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(10, 1);
|
||||
buffer->push_back(std::move(blob));
|
||||
|
||||
DefaultBufferPool::ConstBuffer constBuffer{ std::move(buffer) };
|
||||
|
||||
auto input = CreateInputBuffer(
|
||||
DefaultInputBuffer{ constBuffer, pool->GetMemory() },
|
||||
DefaultBufferPool::ConstBuffer::Range{ constBuffer, constBuffer.begin(), 3, constBuffer.begin(), 8 });
|
||||
|
||||
char result[5];
|
||||
|
||||
BOOST_TEST(!input.IsEof());
|
||||
input.Read(result, sizeof(result));
|
||||
BOOST_TEST(input.IsEof());
|
||||
BOOST_TEST(std::all_of(result, result + 5, [](const char& c) { return c == 1; }));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(MemoryOwnershipTest)
|
||||
{
|
||||
auto memory = std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024);
|
||||
|
||||
bond::blob bondBlob;
|
||||
const char* ptr;
|
||||
|
||||
BOOST_TEST(memory.unique());
|
||||
{
|
||||
DefaultBufferPool::ConstBuffer constBuffer;
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(memory);
|
||||
|
||||
auto buffer = pool->TakeBuffer();
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(10, 1);
|
||||
buffer->push_back(std::move(blob));
|
||||
|
||||
constBuffer = std::move(buffer);
|
||||
}
|
||||
|
||||
DefaultInputBuffer input{ constBuffer, memory };
|
||||
BOOST_TEST(memory.use_count() == 2);
|
||||
|
||||
input.Read(bondBlob, 10);
|
||||
BOOST_TEST(memory.use_count() == 3);
|
||||
|
||||
ptr = constBuffer.begin()->data();
|
||||
}
|
||||
|
||||
BOOST_TEST(bondBlob.data() == ptr);
|
||||
BOOST_TEST(memory.use_count() == 2);
|
||||
|
||||
memory.reset();
|
||||
|
||||
BOOST_TEST(std::all_of(ptr, ptr + 10, [](const char& c) { return c == 1; }));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -0,0 +1,460 @@
|
|||
#include "stdafx.h"
|
||||
#include "IPC/Bond/OutputBuffer.h"
|
||||
#include "IPC/detail/RandomString.h"
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
using namespace IPC::Bond;
|
||||
using IPC::detail::GenerateRandomString;
|
||||
using IPC::SharedMemory;
|
||||
using IPC::create_only;
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(OutputBufferTests)
|
||||
|
||||
static_assert(!std::is_copy_constructible<DefaultOutputBuffer>::value, "OutputBuffer should not be copy constructible.");
|
||||
static_assert(!std::is_copy_assignable<DefaultOutputBuffer>::value, "OutputBuffer should not be copy assignable.");
|
||||
static_assert(std::is_move_constructible<DefaultOutputBuffer>::value, "OutputBuffer should be move constructible.");
|
||||
static_assert(std::is_move_assignable<DefaultOutputBuffer>::value, "OutputBuffer should be move assignable.");
|
||||
|
||||
template <typename Function, std::size_t... I>
|
||||
void ForEach(Function&& func, std::index_sequence<I...>)
|
||||
{
|
||||
auto dummy = { (func(std::integral_constant<std::size_t, I>{}), 0)... };
|
||||
(void)dummy;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(WriteArithmeticTypesTest)
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
DefaultOutputBuffer output{ pool, 3 };
|
||||
|
||||
auto values = std::make_tuple(
|
||||
static_cast<std::int8_t>(1),
|
||||
static_cast<std::int16_t>(2),
|
||||
static_cast<std::int32_t>(3),
|
||||
static_cast<std::int64_t>(4),
|
||||
static_cast<std::uint8_t>(5),
|
||||
static_cast<std::uint16_t>(6),
|
||||
static_cast<std::uint32_t>(7),
|
||||
static_cast<std::uint64_t>(8),
|
||||
static_cast<float>(9.10),
|
||||
static_cast<double>(11.12));
|
||||
|
||||
std::size_t size = 0;
|
||||
|
||||
ForEach(
|
||||
[&](auto index)
|
||||
{
|
||||
const auto& value = std::get<index.value>(values);
|
||||
(void)index;
|
||||
|
||||
output.Write(value);
|
||||
size += sizeof(decltype(value));
|
||||
},
|
||||
std::make_index_sequence<std::tuple_size<decltype(values)>::value>{});
|
||||
|
||||
auto buffer = std::move(output).GetBuffer();
|
||||
|
||||
BOOST_TEST(buffer.size() == size);
|
||||
BOOST_TEST(std::distance(buffer.begin(), buffer.end()) == 1);
|
||||
|
||||
auto ptr = buffer.begin()->data();
|
||||
|
||||
ForEach(
|
||||
[&](auto index)
|
||||
{
|
||||
const auto& value = std::get<index.value>(values);
|
||||
(void)index;
|
||||
|
||||
BOOST_TEST(std::memcmp(&value, ptr, sizeof(value)) == 0);
|
||||
ptr += sizeof(decltype(value));
|
||||
},
|
||||
std::make_index_sequence<std::tuple_size<decltype(values)>::value>{});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(WriteRawBytesTest)
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
DefaultOutputBuffer output{ pool, 2 };
|
||||
|
||||
const char data[] = "Data";
|
||||
|
||||
output.Write(data, sizeof(data));
|
||||
output.Write(data, sizeof(data));
|
||||
|
||||
auto buffer = std::move(output).GetBuffer();
|
||||
|
||||
BOOST_TEST(buffer.size() == 2 * sizeof(data));
|
||||
BOOST_TEST(std::distance(buffer.begin(), buffer.end()) == 1);
|
||||
|
||||
auto ptr = buffer.begin()->data();
|
||||
|
||||
BOOST_TEST(std::memcmp(ptr, data, sizeof(data)) == 0);
|
||||
BOOST_TEST(std::memcmp(ptr + sizeof(data), data, sizeof(data)) == 0);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(WriteBufferTest)
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
DefaultOutputBuffer output{ pool, 3 };
|
||||
|
||||
const char data[] = "Data";
|
||||
|
||||
output.Write(data, sizeof(data));
|
||||
|
||||
DefaultBufferPool::ConstBlob b1;
|
||||
{
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(10);
|
||||
b1 = std::move(blob);
|
||||
}
|
||||
|
||||
DefaultBufferPool::ConstBlob b2;
|
||||
{
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(20);
|
||||
b2 = std::move(blob);
|
||||
}
|
||||
|
||||
{
|
||||
auto buffer = pool->TakeBuffer();
|
||||
buffer->push_back(b1);
|
||||
buffer->push_back(DefaultBufferPool::ConstBlob{ pool->TakeBlob() });
|
||||
buffer->push_back(b2);
|
||||
buffer->push_back(DefaultBufferPool::ConstBlob{});
|
||||
|
||||
output.Write(DefaultBufferPool::ConstBuffer{ std::move(buffer) });
|
||||
}
|
||||
|
||||
auto otherPool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
{
|
||||
auto buffer = otherPool->TakeBuffer();
|
||||
auto blob = otherPool->TakeBlob();
|
||||
blob->resize(sizeof(data), boost::container::default_init);
|
||||
std::strcpy(blob->data(), data);
|
||||
buffer->push_back(std::move(blob));
|
||||
|
||||
output.Write(DefaultBufferPool::ConstBuffer{ std::move(buffer) });
|
||||
}
|
||||
|
||||
output.Write(data, sizeof(data));
|
||||
|
||||
auto buffer = std::move(output).GetBuffer();
|
||||
|
||||
BOOST_TEST(buffer.size() == sizeof(data) + 10 + 20 + 2 * sizeof(data));
|
||||
BOOST_TEST(std::distance(buffer.begin(), buffer.end()) == 4);
|
||||
|
||||
auto blob = buffer.begin();
|
||||
BOOST_TEST(blob->size() == sizeof(data));
|
||||
BOOST_TEST(std::memcmp(blob->data(), data, sizeof(data)) == 0);
|
||||
|
||||
++blob;
|
||||
BOOST_TEST(blob->size() == b1.size());
|
||||
BOOST_TEST(blob->data() == b1.data());
|
||||
|
||||
++blob;
|
||||
BOOST_TEST(blob->size() == b2.size());
|
||||
BOOST_TEST(blob->data() == b2.data());
|
||||
|
||||
++blob;
|
||||
BOOST_TEST(blob->size() == 2 * sizeof(data));
|
||||
BOOST_TEST(std::memcmp(blob->data(), data, sizeof(data)) == 0);
|
||||
BOOST_TEST(std::memcmp(blob->data() + sizeof(data), data, sizeof(data)) == 0);
|
||||
|
||||
BOOST_TEST((++blob == buffer.end()));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(WriteBufferRangeTest)
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
|
||||
DefaultBufferPool::ConstBlob constBlob;
|
||||
DefaultBufferPool::ConstBuffer constBuffer;
|
||||
|
||||
const char data[] = "Data!";
|
||||
{
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(sizeof(data), boost::container::default_init);
|
||||
std::strcpy(blob->data(), data);
|
||||
|
||||
constBlob = std::move(blob);
|
||||
|
||||
auto buffer = pool->TakeBuffer();
|
||||
buffer->push_back(constBlob);
|
||||
buffer->push_back(constBlob);
|
||||
buffer->push_back(constBlob);
|
||||
|
||||
constBuffer = std::move(buffer);
|
||||
}
|
||||
|
||||
constexpr std::size_t leftOffset = 2, rightOffset = 4;
|
||||
|
||||
{
|
||||
DefaultOutputBuffer output{ pool, 4 };
|
||||
output.Write(DefaultBufferPool::ConstBuffer::Range{ constBuffer, constBuffer.begin(), leftOffset, std::next(constBuffer.begin()), 0 });
|
||||
|
||||
auto buffer = std::move(output).GetBuffer();
|
||||
|
||||
BOOST_TEST(buffer.size() == constBlob.size() - leftOffset);
|
||||
BOOST_TEST(std::distance(buffer.begin(), buffer.end()) == 1);
|
||||
|
||||
auto blob = buffer.begin();
|
||||
BOOST_TEST(blob->size() == constBlob.size() - leftOffset);
|
||||
BOOST_TEST(blob->data() == constBlob.data() + leftOffset);
|
||||
|
||||
BOOST_TEST((++blob == buffer.end()));
|
||||
}
|
||||
{
|
||||
DefaultOutputBuffer output{ pool, 3 };
|
||||
output.Write(DefaultBufferPool::ConstBuffer::Range{ constBuffer, constBuffer.begin(), leftOffset, constBuffer.begin(), rightOffset });
|
||||
|
||||
auto buffer = std::move(output).GetBuffer();
|
||||
|
||||
BOOST_TEST(buffer.size() == rightOffset - leftOffset);
|
||||
BOOST_TEST(std::distance(buffer.begin(), buffer.end()) == 1);
|
||||
|
||||
auto blob = buffer.begin();
|
||||
BOOST_TEST(blob->size() == rightOffset - leftOffset);
|
||||
BOOST_TEST(blob->data() == constBlob.data() + leftOffset);
|
||||
|
||||
BOOST_TEST((++blob == buffer.end()));
|
||||
}
|
||||
{
|
||||
DefaultOutputBuffer output{ pool, 2 };
|
||||
output.Write(DefaultBufferPool::ConstBuffer::Range{ constBuffer, constBuffer.begin(), leftOffset, std::next(constBuffer.begin()), rightOffset });
|
||||
|
||||
auto buffer = std::move(output).GetBuffer();
|
||||
|
||||
BOOST_TEST(buffer.size() == constBlob.size() - leftOffset + rightOffset);
|
||||
BOOST_TEST(std::distance(buffer.begin(), buffer.end()) == 2);
|
||||
|
||||
auto blob = buffer.begin();
|
||||
BOOST_TEST(blob->size() == constBlob.size() - leftOffset);
|
||||
BOOST_TEST(blob->data() == constBlob.data() + leftOffset);
|
||||
|
||||
++blob;
|
||||
BOOST_TEST(blob->size() == rightOffset);
|
||||
BOOST_TEST(blob->data() == constBlob.data());
|
||||
|
||||
BOOST_TEST((++blob == buffer.end()));
|
||||
}
|
||||
{
|
||||
DefaultOutputBuffer output{ pool, 2 };
|
||||
output.Write(DefaultBufferPool::ConstBuffer::Range{ constBuffer, constBuffer.begin(), leftOffset, constBuffer.end(), 0 });
|
||||
|
||||
auto buffer = std::move(output).GetBuffer();
|
||||
|
||||
BOOST_TEST(buffer.size() == 3 * constBlob.size() - leftOffset);
|
||||
BOOST_TEST(std::distance(buffer.begin(), buffer.end()) == 3);
|
||||
|
||||
auto blob = buffer.begin();
|
||||
BOOST_TEST(blob->size() == constBlob.size() - leftOffset);
|
||||
BOOST_TEST(blob->data() == constBlob.data() + leftOffset);
|
||||
|
||||
++blob;
|
||||
BOOST_TEST(blob->size() == constBlob.size());
|
||||
BOOST_TEST(blob->data() == constBlob.data());
|
||||
|
||||
++blob;
|
||||
BOOST_TEST(blob->size() == constBlob.size());
|
||||
BOOST_TEST(blob->data() == constBlob.data());
|
||||
|
||||
BOOST_TEST((++blob == buffer.end()));
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(WriteBondBlobWithPrivateMemoryTest)
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
DefaultOutputBuffer output{ pool, 1 };
|
||||
|
||||
const char data[] = "Data";
|
||||
|
||||
output.Write(static_cast<short>(1));
|
||||
output.Write(bond::blob{ data, sizeof(data) });
|
||||
output.Write(static_cast<int>(2));
|
||||
|
||||
auto buffer = std::move(output).GetBuffer();
|
||||
|
||||
BOOST_TEST(buffer.size() == sizeof(data) + sizeof(short) + sizeof(int));
|
||||
BOOST_TEST(std::distance(buffer.begin(), buffer.end()) == 1);
|
||||
|
||||
BOOST_TEST(buffer.size() == buffer.begin()->size());
|
||||
|
||||
auto ptr = buffer.begin()->data();
|
||||
BOOST_TEST(*reinterpret_cast<const short*>(ptr) == 1);
|
||||
|
||||
ptr += sizeof(short);
|
||||
BOOST_TEST(std::memcmp(ptr, data, sizeof(data)) == 0);
|
||||
|
||||
ptr += sizeof(data);
|
||||
BOOST_TEST(*reinterpret_cast<const int*>(ptr) == 2);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(WriteBondBlobWithSharedMemoryTest)
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
DefaultOutputBuffer output{ pool, 2 };
|
||||
|
||||
output.Write(static_cast<short>(1));
|
||||
|
||||
const char data[] = "Data";
|
||||
{
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(sizeof(data), boost::container::default_init);
|
||||
std::strcpy(blob->data(), data);
|
||||
|
||||
output.Write(BlobCast(DefaultBufferPool::ConstBlob{ std::move(blob) }, pool->GetMemory()));
|
||||
}
|
||||
|
||||
output.Write(static_cast<int>(2));
|
||||
|
||||
auto buffer = std::move(output).GetBuffer();
|
||||
|
||||
BOOST_TEST(buffer.size() == sizeof(data) + sizeof(short) + sizeof(int));
|
||||
BOOST_TEST(std::distance(buffer.begin(), buffer.end()) == 3);
|
||||
|
||||
auto blob = buffer.begin();
|
||||
BOOST_TEST(blob->size() == sizeof(short));
|
||||
BOOST_TEST(*reinterpret_cast<const short*>(blob->data()) == 1);
|
||||
|
||||
++blob;
|
||||
BOOST_TEST(blob->size() == sizeof(data));
|
||||
BOOST_TEST(std::memcmp(blob->data(), data, sizeof(data)) == 0);
|
||||
|
||||
++blob;
|
||||
BOOST_TEST(blob->size() == sizeof(int));
|
||||
BOOST_TEST(*reinterpret_cast<const int*>(blob->data()) == 2);
|
||||
|
||||
BOOST_TEST((++blob == buffer.end()));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(WriteEmptyDataTest)
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
|
||||
{
|
||||
DefaultOutputBuffer output{ pool };
|
||||
|
||||
auto buffer = std::move(output).GetBuffer();
|
||||
|
||||
BOOST_TEST(!!buffer);
|
||||
BOOST_TEST(buffer.size() == 0);
|
||||
BOOST_TEST(std::distance(buffer.begin(), buffer.end()) == 0);
|
||||
}
|
||||
|
||||
DefaultOutputBuffer output{ pool };
|
||||
|
||||
output.Write(bond::blob{});
|
||||
output.Write(DefaultBufferPool::ConstBuffer{});
|
||||
output.Write(DefaultBufferPool::ConstBuffer::Range{});
|
||||
|
||||
{
|
||||
auto buffer = pool->TakeBuffer();
|
||||
DefaultBufferPool::ConstBuffer constBuffer{ std::move(buffer) };
|
||||
|
||||
output.Write(constBuffer);
|
||||
output.Write(DefaultBufferPool::ConstBuffer::Range{ constBuffer, constBuffer.begin(), 0, constBuffer.end(), 0 });
|
||||
}
|
||||
{
|
||||
auto buffer = pool->TakeBuffer();
|
||||
buffer->push_back(DefaultBufferPool::ConstBlob{});
|
||||
DefaultBufferPool::ConstBuffer constBuffer{ std::move(buffer) };
|
||||
|
||||
output.Write(constBuffer);
|
||||
output.Write(DefaultBufferPool::ConstBuffer::Range{ constBuffer, constBuffer.begin(), 0, constBuffer.end(), 0 });
|
||||
}
|
||||
|
||||
auto buffer = std::move(output).GetBuffer();
|
||||
|
||||
BOOST_TEST(!!buffer);
|
||||
BOOST_TEST(buffer.size() == 0);
|
||||
BOOST_TEST(std::distance(buffer.begin(), buffer.end()) == 0);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(FlushTest)
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
DefaultOutputBuffer output{ pool };
|
||||
|
||||
const char data[] = "Data";
|
||||
const auto& buffer = output.GetBuffer();
|
||||
|
||||
output.Write(data, sizeof(data));
|
||||
BOOST_TEST(buffer->empty());
|
||||
|
||||
output.Flush();
|
||||
BOOST_TEST(buffer->size() == 1);
|
||||
BOOST_TEST(std::memcmp(buffer->front().data(), data, sizeof(data)) == 0);
|
||||
|
||||
output.Write(data, sizeof(data));
|
||||
BOOST_TEST(buffer->size() == 1);
|
||||
|
||||
output.Flush();
|
||||
BOOST_TEST(buffer->size() == 2);
|
||||
BOOST_TEST(std::memcmp(buffer->back().data(), data, sizeof(data)) == 0);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(GetBufferTest)
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
DefaultOutputBuffer output{ pool };
|
||||
const auto& constOutput = output;
|
||||
|
||||
BOOST_TEST(output.GetBufferPool() == pool);
|
||||
|
||||
const char data[] = "Data";
|
||||
|
||||
BOOST_CHECK_NO_THROW(constOutput.GetBuffer());
|
||||
BOOST_CHECK_NO_THROW(output.GetBuffer());
|
||||
BOOST_TEST(&output.GetBuffer() == &constOutput.GetBuffer());
|
||||
BOOST_TEST(output.GetBuffer()->empty());
|
||||
|
||||
output.Write(data, sizeof(data));
|
||||
|
||||
BOOST_CHECK_THROW(constOutput.GetBuffer(), std::exception);
|
||||
BOOST_CHECK_NO_THROW(output.GetBuffer());
|
||||
BOOST_CHECK_NO_THROW(constOutput.GetBuffer());
|
||||
|
||||
output.Write(data, sizeof(data));
|
||||
|
||||
BOOST_CHECK_THROW(constOutput.GetBuffer(), std::exception);
|
||||
|
||||
auto buffer = std::move(output).GetBuffer();
|
||||
|
||||
BOOST_TEST(buffer.size() == 2 * sizeof(data));
|
||||
BOOST_TEST(std::distance(buffer.begin(), buffer.end()) == 2);
|
||||
|
||||
auto blob = buffer.begin();
|
||||
BOOST_TEST(std::memcmp(blob->data(), data, sizeof(data)) == 0);
|
||||
|
||||
++blob;
|
||||
BOOST_TEST(std::memcmp(blob->data(), data, sizeof(data)) == 0);
|
||||
|
||||
BOOST_TEST((++blob == buffer.end()));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CreateOutputBufferTest)
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
DefaultOutputBuffer output{ pool };
|
||||
|
||||
output.Write(1);
|
||||
|
||||
{
|
||||
const auto& buffer = output.GetBuffer();
|
||||
BOOST_TEST(!!buffer);
|
||||
BOOST_TEST(buffer->size() == 1);
|
||||
}
|
||||
|
||||
auto output2 = CreateOutputBuffer(output);
|
||||
|
||||
{
|
||||
const auto& buffer = output2.GetBuffer();
|
||||
BOOST_TEST(!!buffer);
|
||||
BOOST_TEST(buffer->empty());
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -0,0 +1,342 @@
|
|||
#include "stdafx.h"
|
||||
#include "IPC/Bond/Serializer.h"
|
||||
#include "IPC/detail/RandomString.h"
|
||||
#include <bond/core/bond.h>
|
||||
#include <bond/core/bond_types.h>
|
||||
#include <bond/core/tuple.h>
|
||||
#include <bond/protocol/simple_binary.h>
|
||||
#include <bond/protocol/compact_binary.h>
|
||||
#include <bond/protocol/fast_binary.h>
|
||||
#include <bond/protocol/simple_json_reader.h>
|
||||
#include <bond/protocol/simple_json_writer.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
|
||||
using ValueStruct = std::tuple<
|
||||
std::int8_t,
|
||||
std::int16_t,
|
||||
std::int32_t,
|
||||
std::int64_t,
|
||||
std::uint8_t,
|
||||
std::uint16_t,
|
||||
std::uint32_t,
|
||||
std::uint64_t,
|
||||
bond::blob,
|
||||
bond::ProtocolType,
|
||||
bond::blob,
|
||||
std::string>;
|
||||
|
||||
using Struct = std::tuple<
|
||||
ValueStruct,
|
||||
std::vector<ValueStruct>,
|
||||
std::map<bond::ProtocolType, ValueStruct>>;
|
||||
|
||||
using BondedStruct = bond::Box<bond::bonded<Struct>>;
|
||||
|
||||
namespace bond
|
||||
{
|
||||
// TODO: Fix bond to properly work with std::tuple.
|
||||
|
||||
inline ValueStruct make_element(std::vector<ValueStruct>& container)
|
||||
{
|
||||
return{};
|
||||
}
|
||||
|
||||
inline ValueStruct make_value(std::map<ProtocolType, ValueStruct>& map)
|
||||
{
|
||||
return{};
|
||||
}
|
||||
|
||||
} // namespace bond
|
||||
|
||||
|
||||
using namespace IPC::Bond;
|
||||
using IPC::detail::GenerateRandomString;
|
||||
using IPC::SharedMemory;
|
||||
using IPC::create_only;
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(SerializerTests)
|
||||
|
||||
static_assert(std::is_copy_constructible<DefaultSerializer>::value, "Serializer should be copy constructible.");
|
||||
static_assert(std::is_copy_assignable<DefaultSerializer>::value, "Serializer should be copy assignable.");
|
||||
|
||||
|
||||
template <typename Function, typename Protocol, typename MakeBondedFunc>
|
||||
void RunProtocolTest(Function&& func, Protocol protocol, MakeBondedFunc&& makeBondedFunc)
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
|
||||
auto obj = MakeStruct(*pool);
|
||||
BondedStruct bondedObj;
|
||||
bondedObj.value = makeBondedFunc(pool, obj, protocol);
|
||||
|
||||
BondedStruct bondedResult;
|
||||
|
||||
std::forward<Function>(func)(pool, bondedObj, bondedResult, protocol);
|
||||
|
||||
Struct result;
|
||||
bondedResult.value.Deserialize<DefaultProtocols>(result);
|
||||
|
||||
BOOST_TEST((obj == result));
|
||||
}
|
||||
|
||||
template <typename Function, typename Protocol>
|
||||
void RunProtocolTest(Function&& func, Protocol protocol, std::nullptr_t)
|
||||
{
|
||||
RunProtocolTest(
|
||||
std::forward<Function>(func),
|
||||
protocol,
|
||||
[](auto&& /*pool*/, const Struct& obj, auto /*protocol*/)
|
||||
{
|
||||
return bond::bonded<Struct>{ obj };
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
void ForEachRuntimeProtocol(Function&& func, std::initializer_list<bond::ProtocolType> protocols)
|
||||
{
|
||||
for (const auto& protocol : protocols)
|
||||
{
|
||||
RunProtocolTest(func, protocol, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
void ForEachRuntimeProtocol(Function&& func)
|
||||
{
|
||||
ForEachRuntimeProtocol(
|
||||
std::forward<Function>(func),
|
||||
{
|
||||
bond::ProtocolType::COMPACT_PROTOCOL,
|
||||
bond::ProtocolType::FAST_PROTOCOL,
|
||||
bond::ProtocolType::SIMPLE_PROTOCOL,
|
||||
bond::ProtocolType::SIMPLE_JSON_PROTOCOL
|
||||
});
|
||||
}
|
||||
|
||||
template <template <typename...> typename R>
|
||||
struct ProtocolInfo
|
||||
{
|
||||
template <typename T>
|
||||
using Reader = R<T>;
|
||||
|
||||
template <typename T>
|
||||
using Writer = typename bond::get_protocol_writer<R<T>, T>::type;
|
||||
};
|
||||
|
||||
template <template <typename...> typename... Readers, typename Function>
|
||||
void ForEachReader(Function&& func)
|
||||
{
|
||||
std::initializer_list<int>{ ((void)func(ProtocolInfo<Readers>{}), 0)... };
|
||||
}
|
||||
|
||||
template <template <typename...> typename Reader, template <typename...> typename... Readers, typename Function, typename MakeBondedFunc = std::nullptr_t>
|
||||
void ForEachCompiletimeProtocol(Function&& func, MakeBondedFunc&& makeBondedFunc = nullptr)
|
||||
{
|
||||
ForEachReader<Reader, Readers...>(
|
||||
[func = std::forward<Function>(func), makeBondedFunc = std::forward<MakeBondedFunc>(makeBondedFunc)](auto protocol)
|
||||
{
|
||||
RunProtocolTest(func, protocol, makeBondedFunc);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Function, typename MakeBondedFunc = std::nullptr_t>
|
||||
void ForEachCompiletimeProtocol(Function&& func, MakeBondedFunc&& makeBondedFunc = nullptr)
|
||||
{
|
||||
ForEachCompiletimeProtocol<
|
||||
bond::CompactBinaryReader,
|
||||
bond::SimpleBinaryReader,
|
||||
bond::FastBinaryReader,
|
||||
bond::SimpleJsonReader>(std::forward<Function>(func), std::forward<MakeBondedFunc>(makeBondedFunc));
|
||||
}
|
||||
|
||||
Struct MakeStruct(DefaultBufferPool& pool)
|
||||
{
|
||||
auto data = "Random data for blob.";
|
||||
|
||||
auto blob = pool.TakeBlob();
|
||||
blob->resize(std::strlen(data) + 1, boost::container::default_init);
|
||||
std::strcpy(blob->data(), data);
|
||||
|
||||
bond::blob bondBlob = BlobCast(DefaultBufferPool::ConstBlob{ std::move(blob) }, pool.GetMemory());
|
||||
|
||||
ValueStruct value{
|
||||
1, 2, 3, 4, 5, 6, 7, 8,
|
||||
bondBlob,
|
||||
bond::ProtocolType::COMPACT_PROTOCOL,
|
||||
{ bondBlob.data(), bondBlob.size() },
|
||||
data };
|
||||
|
||||
return Struct(
|
||||
value,
|
||||
{ 10, value },
|
||||
{
|
||||
{ bond::ProtocolType::COMPACT_PROTOCOL, value },
|
||||
{ bond::ProtocolType::SIMPLE_PROTOCOL, value },
|
||||
{ bond::ProtocolType::SIMPLE_JSON_PROTOCOL, value }
|
||||
});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(SerializationUsingRuntimeProtocolTest)
|
||||
{
|
||||
ForEachRuntimeProtocol(
|
||||
[&](auto&& pool, BondedStruct& bondedObj, BondedStruct& bondedResult, bond::ProtocolType protocol)
|
||||
{
|
||||
Deserialize(protocol, Serialize(protocol, pool, bondedObj), bondedResult, pool->GetMemory());
|
||||
});
|
||||
|
||||
ForEachRuntimeProtocol(
|
||||
[&](auto&& pool, BondedStruct& bondedObj, BondedStruct& bondedResult, bond::ProtocolType protocol)
|
||||
{
|
||||
DefaultSerializer serializer{ protocol, false, pool, pool->GetMemory() };
|
||||
BOOST_TEST(!serializer.IsMarshaled());
|
||||
BOOST_TEST(serializer.GetProtocolType() == protocol);
|
||||
|
||||
serializer.Deserialize(serializer.Serialize(bondedObj), bondedResult);
|
||||
});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(SerializationUsingCompiletimeProtocolTest)
|
||||
{
|
||||
ForEachCompiletimeProtocol(
|
||||
[&](auto&& pool, BondedStruct& bondedObj, BondedStruct& bondedResult, auto protocol)
|
||||
{
|
||||
using Protocol = decltype(protocol);
|
||||
(void)protocol;
|
||||
|
||||
Deserialize<typename Protocol::template Reader>(
|
||||
Serialize<typename Protocol::template Writer>(pool, bondedObj), bondedResult, pool->GetMemory());
|
||||
});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(MarshalingUsingRuntimeProtocolTest)
|
||||
{
|
||||
auto protocols =
|
||||
{
|
||||
bond::ProtocolType::COMPACT_PROTOCOL,
|
||||
bond::ProtocolType::FAST_PROTOCOL,
|
||||
bond::ProtocolType::SIMPLE_PROTOCOL
|
||||
};
|
||||
|
||||
ForEachRuntimeProtocol(
|
||||
[&](auto&& pool, BondedStruct& bondedObj, BondedStruct& bondedResult, bond::ProtocolType protocol)
|
||||
{
|
||||
Unmarshal(Marshal(protocol, pool, bondedObj), bondedResult, pool->GetMemory());
|
||||
},
|
||||
protocols);
|
||||
|
||||
ForEachRuntimeProtocol(
|
||||
[&](auto&& pool, BondedStruct& bondedObj, BondedStruct& bondedResult, bond::ProtocolType protocol)
|
||||
{
|
||||
DefaultSerializer serializer{ protocol, true, pool, pool->GetMemory() };
|
||||
BOOST_TEST(serializer.IsMarshaled());
|
||||
BOOST_TEST(serializer.GetProtocolType() == protocol);
|
||||
|
||||
serializer.Deserialize(serializer.Serialize(bondedObj), bondedResult);
|
||||
},
|
||||
protocols);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(MarshalingUsingCompiletimeProtocolTest)
|
||||
{
|
||||
ForEachCompiletimeProtocol<
|
||||
bond::CompactBinaryReader,
|
||||
bond::SimpleBinaryReader,
|
||||
bond::FastBinaryReader>(
|
||||
[&](auto&& pool, BondedStruct& bondedObj, BondedStruct& bondedResult, auto protocol)
|
||||
{
|
||||
using Protocol = decltype(protocol);
|
||||
(void)protocol;
|
||||
|
||||
Unmarshal(Marshal<typename Protocol::template Writer>(pool, bondedObj), bondedResult, pool->GetMemory());
|
||||
});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(TranscodingTest)
|
||||
{
|
||||
ForEachCompiletimeProtocol<
|
||||
bond::CompactBinaryReader,
|
||||
bond::SimpleBinaryReader,
|
||||
bond::FastBinaryReader>(
|
||||
[&](auto&& pool, BondedStruct& bondedObj, BondedStruct& bondedResult, auto protocol)
|
||||
{
|
||||
using Protocol = decltype(protocol);
|
||||
(void)protocol;
|
||||
|
||||
Deserialize<typename Protocol::template Reader>(
|
||||
Serialize<typename Protocol::template Writer>(pool, bondedObj), bondedResult, pool->GetMemory());
|
||||
},
|
||||
[](auto&& pool, const Struct& obj, auto protocol)
|
||||
{
|
||||
using Protocol = decltype(protocol);
|
||||
(void)protocol;
|
||||
|
||||
return bond::bonded<Struct>{
|
||||
typename Protocol::template Reader<DefaultInputBuffer>{
|
||||
DefaultInputBuffer{ Serialize<typename Protocol::template Writer>(pool, obj), pool->GetMemory() } } };
|
||||
});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(FailedDeserializationTest)
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024));
|
||||
|
||||
auto obj = MakeStruct(*pool);
|
||||
|
||||
DefaultSerializer serializer{ bond::ProtocolType::COMPACT_PROTOCOL, false, pool, pool->GetMemory() };
|
||||
|
||||
auto buffer = serializer.Serialize(obj);
|
||||
|
||||
BOOST_TEST((serializer.Deserialize<Struct>(buffer).get() == obj));
|
||||
|
||||
DefaultSerializer wrongSerializer{ serializer.GetProtocolType(), !serializer.IsMarshaled(), pool, pool->GetMemory() };
|
||||
|
||||
BOOST_CHECK_THROW(wrongSerializer.Deserialize<Struct>(buffer).get(), std::exception);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(MemoryOwnershipTest)
|
||||
{
|
||||
auto memory = std::make_shared<SharedMemory>(create_only, GenerateRandomString().c_str(), 1024 * 1024);
|
||||
|
||||
auto data = "Blob content";
|
||||
bond::Box<bond::blob> obj;
|
||||
obj.value = { data, static_cast<std::uint32_t>(std::strlen(data)) + 1 };
|
||||
|
||||
BOOST_TEST(memory.unique());
|
||||
|
||||
decltype(obj) serializedObj, marshaledObj;
|
||||
{
|
||||
auto pool = std::make_shared<DefaultBufferPool>(memory);
|
||||
{
|
||||
DefaultSerializer serializer{ bond::ProtocolType::COMPACT_PROTOCOL, false, pool, memory };
|
||||
BOOST_TEST(serializer.GetOutputBufferPool() == pool);
|
||||
BOOST_TEST(serializer.GetInputMemory() == memory);
|
||||
serializer.Deserialize(serializer.Serialize(obj), serializedObj);
|
||||
}
|
||||
{
|
||||
DefaultSerializer serializer{ bond::ProtocolType::COMPACT_PROTOCOL, true, pool, memory };
|
||||
BOOST_TEST(serializer.GetOutputBufferPool() == pool);
|
||||
BOOST_TEST(serializer.GetInputMemory() == memory);
|
||||
serializer.Deserialize(serializer.Serialize(obj), marshaledObj);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_TEST(memory.use_count() == 3);
|
||||
|
||||
BOOST_TEST((obj == serializedObj));
|
||||
BOOST_TEST(memory->Contains(serializedObj.value.content()));
|
||||
BOOST_TEST(memory->Contains(serializedObj.value.content() + serializedObj.value.size() - 1));
|
||||
serializedObj.value = {};
|
||||
BOOST_TEST(memory.use_count() == 2);
|
||||
|
||||
BOOST_TEST((obj == marshaledObj));
|
||||
BOOST_TEST(memory->Contains(marshaledObj.value.content()));
|
||||
BOOST_TEST(memory->Contains(marshaledObj.value.content() + marshaledObj.value.size() - 1));
|
||||
marshaledObj.value = {};
|
||||
BOOST_TEST(memory.unique());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -0,0 +1,397 @@
|
|||
#include "stdafx.h"
|
||||
#include "IPC/Bond/Transport.h"
|
||||
#include "IPC/detail/RandomString.h"
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <future>
|
||||
|
||||
#include <bond/core/bond_types.h>
|
||||
#include <bond/protocol/simple_json_writer.h>
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(TransportTests)
|
||||
|
||||
struct Traits : IPC::Bond::DefaultTraits
|
||||
{
|
||||
using TimeoutFactory = IPC::Policies::NullTimeoutFactory; // Using no-timeout to make these tests reliable.
|
||||
|
||||
template <typename Context>
|
||||
using TransactionManager = IPC::Policies::TransactionManager<Context, TimeoutFactory>;
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_CASE(AcceptorConnectorTest)
|
||||
{
|
||||
auto serverHandler = [](std::future<bond::Box<int>> x, auto&& callback)
|
||||
{
|
||||
try
|
||||
{
|
||||
bond::Box<double> y;
|
||||
y.value = std::sqrt(x.get().value);
|
||||
|
||||
callback(y);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to send response: " << e.what() << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
auto closeHandler = []
|
||||
{
|
||||
std::clog << "Connection is closed." << std::endl;
|
||||
};
|
||||
|
||||
using Transport = IPC::Bond::Transport<bond::Box<int>, bond::Box<double>, Traits>;
|
||||
|
||||
Transport transport;
|
||||
|
||||
auto name = IPC::detail::GenerateRandomString();
|
||||
|
||||
std::mutex lock;
|
||||
std::condition_variable serverInserted;
|
||||
std::vector<std::unique_ptr<Transport::Server>> servers;
|
||||
|
||||
auto acceptor = transport.MakeServerAcceptor(
|
||||
name.c_str(),
|
||||
[&](auto futureConnection)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::lock_guard<std::mutex> guard{ lock };
|
||||
servers.push_back(transport.MakeServer(futureConnection.get(), [&](auto&&...) { return serverHandler; }, closeHandler));
|
||||
serverInserted.notify_one();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to accept a server: " << e.what() << std::endl;
|
||||
}
|
||||
});
|
||||
|
||||
std::unique_ptr<Transport::Client> client;
|
||||
|
||||
auto connector = transport.MakeClientConnector();
|
||||
|
||||
try
|
||||
{
|
||||
client = transport.MakeClient(connector.Connect(name.c_str()).get(), closeHandler);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to connect a client: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
BOOST_TEST_REQUIRE(!!client);
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> guard{ lock };
|
||||
serverInserted.wait(guard, [&] { return !servers.empty(); });
|
||||
}
|
||||
|
||||
BOOST_TEST(servers.size() == 1);
|
||||
|
||||
bond::Box<int> x;
|
||||
x.value = 111;
|
||||
|
||||
bond::Box<double> y;
|
||||
|
||||
try
|
||||
{
|
||||
y = (*client)(x).get();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to invoke a client: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
BOOST_TEST(y.value == std::sqrt(x.value), boost::test_tools::tolerance(0.0001));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ReverseConnectionAcceptorConnectorTest)
|
||||
{
|
||||
auto serverHandler = [](std::future<bond::Box<int>> x, auto&& callback)
|
||||
{
|
||||
try
|
||||
{
|
||||
bond::Box<double> y;
|
||||
y.value = std::sqrt(x.get().value);
|
||||
|
||||
callback(std::move(y));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to send response: " << e.what() << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
auto closeHandler = []
|
||||
{
|
||||
std::clog << "Connection is closed." << std::endl;
|
||||
};
|
||||
|
||||
using Transport = IPC::Bond::Transport<bond::Box<int>, bond::Box<double>, Traits>;
|
||||
|
||||
Transport transport;
|
||||
|
||||
auto name = IPC::detail::GenerateRandomString();
|
||||
|
||||
std::mutex lock;
|
||||
std::condition_variable clientInserted;
|
||||
std::vector<std::unique_ptr<Transport::Client>> clients;
|
||||
|
||||
auto acceptor = transport.MakeClientAcceptor(
|
||||
name.c_str(),
|
||||
[&](auto futureConnection)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::lock_guard<std::mutex> guard{ lock };
|
||||
clients.push_back(transport.MakeClient(futureConnection.get(), closeHandler));
|
||||
clientInserted.notify_one();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to accept a client: " << e.what() << std::endl;
|
||||
}
|
||||
});
|
||||
|
||||
std::unique_ptr<Transport::Server> server;
|
||||
|
||||
auto connector = transport.MakeServerConnector();
|
||||
|
||||
try
|
||||
{
|
||||
server = transport.MakeServer(connector.Connect(name.c_str()).get(), [&](auto&&...) { return serverHandler; }, closeHandler);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to connect a server: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
BOOST_TEST_REQUIRE(!!server);
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> guard{ lock };
|
||||
clientInserted.wait(guard, [&] { return !clients.empty(); });
|
||||
}
|
||||
|
||||
BOOST_TEST(clients.size() == 1);
|
||||
|
||||
bond::Box<int> x;
|
||||
x.value = 222;
|
||||
|
||||
bond::Box<double> y;
|
||||
|
||||
try
|
||||
{
|
||||
y = (*clients.front())(x).get();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to invoke a client: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
BOOST_TEST(y.value == std::sqrt(x.value), boost::test_tools::tolerance(0.0001));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(AcceptConnectTest)
|
||||
{
|
||||
auto serverHandler = [](std::future<bond::Box<int>> x, auto&& callback)
|
||||
{
|
||||
try
|
||||
{
|
||||
bond::Box<double> y;
|
||||
y.value = std::sqrt(x.get().value);
|
||||
|
||||
callback(std::move(y));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to send response: " << e.what() << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
using Transport = IPC::Bond::Transport<bond::Box<int>, bond::Box<double>, Traits>;
|
||||
|
||||
Transport transport;
|
||||
|
||||
auto name = IPC::detail::GenerateRandomString();
|
||||
|
||||
auto serversAccessor = transport.AcceptServers(name.c_str(), [&](auto&&...) { return serverHandler; });
|
||||
|
||||
auto clientAccessor = transport.ConnectClient(name.c_str(), false);
|
||||
|
||||
std::shared_ptr<Transport::Client> client;
|
||||
|
||||
try
|
||||
{
|
||||
client = clientAccessor();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Client is not available: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
BOOST_TEST_REQUIRE(!!client);
|
||||
|
||||
bond::Box<int> x;
|
||||
x.value = 333;
|
||||
|
||||
bond::Box<double> y;
|
||||
|
||||
try
|
||||
{
|
||||
y = (*client)(x).get();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to invoke a client: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
BOOST_TEST(y.value == std::sqrt(x.value), boost::test_tools::tolerance(0.0001));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ReverseConnectionAcceptConnectTest)
|
||||
{
|
||||
auto serverHandler = [](std::future<bond::Box<int>> x, auto&& callback)
|
||||
{
|
||||
try
|
||||
{
|
||||
bond::Box<double> y;
|
||||
y.value = std::sqrt(x.get().value);
|
||||
|
||||
callback(std::move(y));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to send response: " << e.what() << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
using Transport = IPC::Bond::Transport<bond::Box<int>, bond::Box<double>, Traits>;
|
||||
|
||||
Transport transport;
|
||||
|
||||
auto name = IPC::detail::GenerateRandomString();
|
||||
|
||||
auto clientsAccessor = transport.AcceptClients(name.c_str());
|
||||
|
||||
auto serverAccessor = transport.ConnectServer(name.c_str(), [&](auto&&...) { return serverHandler; }, false);
|
||||
|
||||
std::shared_ptr<Transport::Server> server;
|
||||
|
||||
try
|
||||
{
|
||||
server = serverAccessor();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Server is not available: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
BOOST_TEST_REQUIRE(!!server);
|
||||
|
||||
bond::Box<int> x;
|
||||
x.value = 444;
|
||||
|
||||
bond::Box<double> y;
|
||||
|
||||
try
|
||||
{
|
||||
y = (*clientsAccessor()->front())(x).get();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to invoke a client: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
BOOST_TEST(y.value == std::sqrt(x.value), boost::test_tools::tolerance(0.0001));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(DynamicDataTest)
|
||||
{
|
||||
using Struct = bond::Box<bond::blob>;
|
||||
|
||||
auto serverHandler = [](IPC::Bond::DefaultBufferPool& pool, std::future<Struct> futureRequest, auto&& callback)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto request = futureRequest.get();
|
||||
|
||||
auto blob = pool.TakeBlob();
|
||||
blob->resize(2 * request.value.size(), boost::container::default_init);
|
||||
std::memcpy(blob->data(), request.value.data(), request.value.size());
|
||||
std::memcpy(blob->data() + request.value.size(), request.value.data(), request.value.size());
|
||||
|
||||
Struct response;
|
||||
response.value = IPC::Bond::BlobCast(IPC::Bond::DefaultBufferPool::ConstBlob{ std::move(blob) }, pool.GetMemory());
|
||||
|
||||
callback(std::move(response));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to send response: " << e.what() << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
using Transport = IPC::Bond::Transport<Struct, Struct, Traits>;
|
||||
|
||||
Transport transport;
|
||||
|
||||
auto name = IPC::detail::GenerateRandomString();
|
||||
|
||||
auto serversAccessor = transport.AcceptServers(
|
||||
name.c_str(),
|
||||
[&](const auto& /*connection*/, const auto& pools, const auto& /*serializer*/)
|
||||
{
|
||||
return [&serverHandler, pool = pools.GetOutputPool()](std::future<Struct> request, auto&& callback)
|
||||
{
|
||||
return serverHandler(*pool, std::move(request), std::forward<decltype(callback)>(callback));
|
||||
};
|
||||
});
|
||||
|
||||
auto clientAccessor = transport.ConnectClient(name.c_str(), false);
|
||||
|
||||
std::shared_ptr<Transport::Client> client;
|
||||
|
||||
try
|
||||
{
|
||||
client = clientAccessor();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Client is not available: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
BOOST_TEST_REQUIRE(!!client);
|
||||
|
||||
const char str[] = "Hello World";
|
||||
Struct result;
|
||||
|
||||
try
|
||||
{
|
||||
auto pool = client->GetOutputPool();
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(sizeof(str), boost::container::default_init);
|
||||
std::strcpy(blob->data(), str);
|
||||
|
||||
Struct request;
|
||||
request.value = IPC::Bond::BlobCast(IPC::Bond::DefaultBufferPool::ConstBlob{ std::move(blob) }, pool->GetMemory());
|
||||
|
||||
result = (*client)(std::move(request)).get();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to invoke a client: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
BOOST_TEST(result.value.size() == 2 * sizeof(str));
|
||||
BOOST_TEST(std::strcmp(result.value.content(), str) == 0);
|
||||
BOOST_TEST(std::strcmp(result.value.content() + sizeof(str), str) == 0);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -0,0 +1,403 @@
|
|||
#include "stdafx.h"
|
||||
#include "IPC/Bond/Accept.h"
|
||||
#include "IPC/Bond/Acceptor.h"
|
||||
#include "IPC/Bond/Connect.h"
|
||||
#include "IPC/Bond/Connector.h"
|
||||
#include "IPC/detail/RandomString.h"
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <future>
|
||||
|
||||
#include <bond/core/bond_types.h>
|
||||
#include <bond/protocol/simple_json_writer.h>
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(UsageTests)
|
||||
|
||||
struct Traits : IPC::Bond::DefaultTraits
|
||||
{
|
||||
using TimeoutFactory = IPC::Policies::NullTimeoutFactory; // Using no-timeout to make these tests reliable.
|
||||
|
||||
template <typename Context>
|
||||
using TransactionManager = IPC::Policies::TransactionManager<Context, TimeoutFactory>;
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_CASE(AcceptorConnectorTest)
|
||||
{
|
||||
constexpr auto protocol = bond::ProtocolType::COMPACT_PROTOCOL;
|
||||
constexpr auto marshal = false;
|
||||
|
||||
auto serverHandler = [](std::future<bond::Box<int>> x, auto&& callback)
|
||||
{
|
||||
try
|
||||
{
|
||||
bond::Box<double> y;
|
||||
y.value = std::sqrt(x.get().value);
|
||||
|
||||
callback(y);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to send response: " << e.what() << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
auto closeHandler = []
|
||||
{
|
||||
std::clog << "Connection is closed." << std::endl;
|
||||
};
|
||||
|
||||
auto name = IPC::detail::GenerateRandomString();
|
||||
|
||||
std::mutex lock;
|
||||
std::condition_variable serverInserted;
|
||||
std::vector<std::unique_ptr<IPC::Bond::Server<bond::Box<int>, bond::Box<double>, Traits>>> servers;
|
||||
|
||||
IPC::Bond::ServerAcceptor<bond::Box<int>, bond::Box<double>, Traits> acceptor{
|
||||
name.c_str(),
|
||||
[&](auto futureConnection)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::lock_guard<std::mutex> guard{ lock };
|
||||
servers.push_back(IPC::Bond::MakeServer<bond::Box<int>, bond::Box<double>, Traits>(
|
||||
futureConnection.get(),
|
||||
[&](auto&&...) { return serverHandler; },
|
||||
closeHandler,
|
||||
protocol,
|
||||
marshal));
|
||||
serverInserted.notify_one();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to accept a server: " << e.what() << std::endl;
|
||||
}
|
||||
} };
|
||||
|
||||
std::unique_ptr<IPC::Bond::Client<bond::Box<int>, bond::Box<double>, Traits>> client;
|
||||
|
||||
IPC::Bond::ClientConnector<bond::Box<int>, bond::Box<double>, Traits> connector;
|
||||
|
||||
try
|
||||
{
|
||||
client = IPC::Bond::MakeClient<bond::Box<int>, bond::Box<double>, Traits>(
|
||||
connector.Connect(name.c_str()).get(), closeHandler, protocol, marshal);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to connect a client: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> guard{ lock };
|
||||
serverInserted.wait(guard, [&] { return !servers.empty(); });
|
||||
}
|
||||
|
||||
BOOST_TEST(servers.size() == 1);
|
||||
|
||||
bond::Box<int> x;
|
||||
x.value = 111;
|
||||
|
||||
bond::Box<double> y;
|
||||
|
||||
try
|
||||
{
|
||||
y = (*client)(x).get();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to invoke a client: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
BOOST_TEST(y.value == std::sqrt(x.value), boost::test_tools::tolerance(0.0001));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ReverseConnectionAcceptorConnectorTest)
|
||||
{
|
||||
constexpr auto protocol = bond::ProtocolType::COMPACT_PROTOCOL;
|
||||
constexpr auto marshal = false;
|
||||
|
||||
auto serverHandler = [](std::future<bond::Box<int>> x, auto&& callback)
|
||||
{
|
||||
try
|
||||
{
|
||||
bond::Box<double> y;
|
||||
y.value = std::sqrt(x.get().value);
|
||||
|
||||
callback(std::move(y));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to send response: " << e.what() << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
auto closeHandler = []
|
||||
{
|
||||
std::clog << "Connection is closed." << std::endl;
|
||||
};
|
||||
|
||||
auto name = IPC::detail::GenerateRandomString();
|
||||
|
||||
std::mutex lock;
|
||||
std::condition_variable clientInserted;
|
||||
std::vector<std::unique_ptr<IPC::Bond::Client<bond::Box<int>, bond::Box<double>, Traits>>> clients;
|
||||
|
||||
IPC::Bond::ClientAcceptor<bond::Box<int>, bond::Box<double>, Traits> acceptor{
|
||||
name.c_str(),
|
||||
[&](auto futureConnection)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::lock_guard<std::mutex> guard{ lock };
|
||||
clients.push_back(IPC::Bond::MakeClient<bond::Box<int>, bond::Box<double>, Traits>(
|
||||
futureConnection.get(), closeHandler, protocol, marshal));
|
||||
clientInserted.notify_one();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to accept a client: " << e.what() << std::endl;
|
||||
}
|
||||
} };
|
||||
|
||||
std::unique_ptr<IPC::Bond::Server<bond::Box<int>, bond::Box<double>, Traits>> server;
|
||||
|
||||
IPC::Bond::ServerConnector<bond::Box<int>, bond::Box<double>, Traits> connector;
|
||||
|
||||
try
|
||||
{
|
||||
server = IPC::Bond::MakeServer<bond::Box<int>, bond::Box<double>, Traits>(
|
||||
connector.Connect(name.c_str()).get(),
|
||||
[&](auto&&...) { return serverHandler; },
|
||||
closeHandler,
|
||||
protocol,
|
||||
marshal);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to connect a server: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> guard{ lock };
|
||||
clientInserted.wait(guard, [&] { return !clients.empty(); });
|
||||
}
|
||||
|
||||
BOOST_TEST(clients.size() == 1);
|
||||
|
||||
bond::Box<int> x;
|
||||
x.value = 222;
|
||||
|
||||
bond::Box<double> y;
|
||||
|
||||
try
|
||||
{
|
||||
y = (*clients.front())(x).get();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to invoke a client: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
BOOST_TEST(y.value == std::sqrt(x.value), boost::test_tools::tolerance(0.0001));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(AcceptConnectTest)
|
||||
{
|
||||
auto serverHandler = [](std::future<bond::Box<int>> x, auto&& callback)
|
||||
{
|
||||
try
|
||||
{
|
||||
bond::Box<double> y;
|
||||
y.value = std::sqrt(x.get().value);
|
||||
|
||||
callback(std::move(y));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to send response: " << e.what() << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
auto name = IPC::detail::GenerateRandomString();
|
||||
|
||||
auto serversAccessor = IPC::Bond::AcceptServers<bond::Box<int>, bond::Box<double>, Traits>(
|
||||
name.c_str(), [&](auto&&...) { return serverHandler; });
|
||||
|
||||
auto clientAccessor = IPC::Bond::ConnectClient(
|
||||
name.c_str(), std::make_shared<IPC::Bond::ClientConnector<bond::Box<int>, bond::Box<double>, Traits>>(), false);
|
||||
|
||||
std::shared_ptr<IPC::Bond::Client<bond::Box<int>, bond::Box<double>, Traits>> client;
|
||||
|
||||
try
|
||||
{
|
||||
client = clientAccessor();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Client is not available: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
assert(client);
|
||||
|
||||
bond::Box<int> x;
|
||||
x.value = 333;
|
||||
|
||||
bond::Box<double> y;
|
||||
|
||||
try
|
||||
{
|
||||
y = (*client)(x).get();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to invoke a client: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
BOOST_TEST(y.value == std::sqrt(x.value), boost::test_tools::tolerance(0.0001));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ReverseConnectionAcceptConnectTest)
|
||||
{
|
||||
auto serverHandler = [](std::future<bond::Box<int>> x, auto&& callback)
|
||||
{
|
||||
try
|
||||
{
|
||||
bond::Box<double> y;
|
||||
y.value = std::sqrt(x.get().value);
|
||||
|
||||
callback(std::move(y));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to send response: " << e.what() << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
auto name = IPC::detail::GenerateRandomString();
|
||||
|
||||
auto clientsAccessor = IPC::Bond::AcceptClients<bond::Box<int>, bond::Box<double>, Traits>(name.c_str());
|
||||
|
||||
auto serverAccessor = IPC::Bond::ConnectServer(
|
||||
name.c_str(),
|
||||
std::make_shared<IPC::Bond::ServerConnector<bond::Box<int>, bond::Box<double>, Traits>>(),
|
||||
[&](auto&&...) { return serverHandler; },
|
||||
false);
|
||||
|
||||
std::shared_ptr<IPC::Bond::Server<bond::Box<int>, bond::Box<double>, Traits>> server;
|
||||
|
||||
try
|
||||
{
|
||||
server = serverAccessor();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Server is not available: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
assert(server);
|
||||
|
||||
bond::Box<int> x;
|
||||
x.value = 444;
|
||||
|
||||
bond::Box<double> y;
|
||||
|
||||
try
|
||||
{
|
||||
y = (*clientsAccessor()->front())(x).get();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to invoke a client: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
BOOST_TEST(y.value == std::sqrt(x.value), boost::test_tools::tolerance(0.0001));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(DynamicDataTest)
|
||||
{
|
||||
using Struct = bond::Box<bond::blob>;
|
||||
|
||||
auto serverHandler = [](IPC::Bond::DefaultBufferPool& pool, std::future<Struct> futureRequest, auto&& callback)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto request = futureRequest.get();
|
||||
|
||||
auto blob = pool.TakeBlob();
|
||||
blob->resize(2 * request.value.size(), boost::container::default_init);
|
||||
std::memcpy(blob->data(), request.value.data(), request.value.size());
|
||||
std::memcpy(blob->data() + request.value.size(), request.value.data(), request.value.size());
|
||||
|
||||
Struct response;
|
||||
response.value = IPC::Bond::BlobCast(IPC::Bond::DefaultBufferPool::ConstBlob{ std::move(blob) }, pool.GetMemory());
|
||||
|
||||
callback(std::move(response));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to send response: " << e.what() << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
auto name = IPC::detail::GenerateRandomString();
|
||||
|
||||
auto serversAccessor = IPC::Bond::AcceptServers<Struct, Struct, Traits>(
|
||||
name.c_str(),
|
||||
[&](const auto& /*connection*/, const auto& pools, const auto& /*serializer*/)
|
||||
{
|
||||
return [&serverHandler, pool = pools.GetOutputPool()](std::future<Struct> request, auto&& callback)
|
||||
{
|
||||
return serverHandler(*pool, std::move(request), std::forward<decltype(callback)>(callback));
|
||||
};
|
||||
});
|
||||
|
||||
auto clientAccessor = IPC::Bond::ConnectClient(
|
||||
name.c_str(),
|
||||
std::make_shared<IPC::Bond::ClientConnector<Struct, Struct, Traits>>(),
|
||||
false);
|
||||
|
||||
std::shared_ptr<IPC::Bond::Client<Struct, Struct, Traits>> client;
|
||||
|
||||
try
|
||||
{
|
||||
client = clientAccessor();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Client is not available: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
assert(client);
|
||||
|
||||
const char str[] = "Hello World";
|
||||
Struct result;
|
||||
|
||||
try
|
||||
{
|
||||
auto pool = client->GetOutputPool();
|
||||
auto blob = pool->TakeBlob();
|
||||
blob->resize(sizeof(str), boost::container::default_init);
|
||||
std::strcpy(blob->data(), str);
|
||||
|
||||
Struct request;
|
||||
request.value = IPC::Bond::BlobCast(IPC::Bond::DefaultBufferPool::ConstBlob{ std::move(blob) }, pool->GetMemory());
|
||||
|
||||
result = (*client)(std::move(request)).get();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::clog << "Failed to invoke a client: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
BOOST_TEST(result.value.size() == 2 * sizeof(str));
|
||||
BOOST_TEST(std::strcmp(result.value.content(), str) == 0);
|
||||
BOOST_TEST(std::strcmp(result.value.content() + sizeof(str), str) == 0);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -0,0 +1 @@
|
|||
#include "stdafx.h"
|
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#define BOOST_TEST_MODULE IPC_Bond_UnitTests
|
||||
#include <boost/test/unit_test.hpp>
|
|
@ -0,0 +1,111 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Internal;
|
||||
|
||||
namespace IPC.Bond.Managed.UnitTests
|
||||
{
|
||||
using SharedMemory = IPC.Managed.SharedMemory;
|
||||
|
||||
[TestFixture(Category = "ManagedTests")]
|
||||
public class SerializationTests
|
||||
{
|
||||
private readonly Randomizer _rand = new Randomizer();
|
||||
|
||||
private byte[] MakeRandomBytes()
|
||||
{
|
||||
var buffer = new byte[_rand.Next(1, 1024)];
|
||||
_rand.NextBytes(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private ValueStruct MakeRandomValueStruct()
|
||||
{
|
||||
return new ValueStruct
|
||||
{
|
||||
FieldBool = _rand.Next() % 2 == 0,
|
||||
FieldInt8 = (sbyte)_rand.Next(sbyte.MinValue, sbyte.MaxValue),
|
||||
FieldUInt8 = (byte)_rand.Next(byte.MinValue, byte.MaxValue),
|
||||
FieldInt16 = (short)_rand.Next(short.MinValue, short.MaxValue),
|
||||
FieldUInt16 = (ushort)_rand.Next(ushort.MinValue, ushort.MaxValue),
|
||||
FieldInt32 = _rand.Next(),
|
||||
FieldUInt32 = (uint)_rand.NextDouble(),
|
||||
FieldInt64 = (long)_rand.NextDouble(),
|
||||
FieldUInt64 = (ulong)_rand.NextDouble(),
|
||||
FieldFloat = (float)_rand.NextDouble(),
|
||||
FieldDouble = _rand.NextDouble(),
|
||||
FieldEnum = (TestEnum)Enum.GetValues(typeof(TestEnum)).GetValue(_rand.Next() % Enum.GetValues(typeof(TestEnum)).Length),
|
||||
FieldString = System.Text.Encoding.ASCII.GetString(MakeRandomBytes()),
|
||||
FieldWString = System.Text.Encoding.Unicode.GetString(MakeRandomBytes()),
|
||||
FieldBlob = new ArraySegment<byte>(MakeRandomBytes())
|
||||
};
|
||||
}
|
||||
|
||||
private IEnumerable<ValueStruct> MakeRandomValueStructs()
|
||||
{
|
||||
var count = _rand.Next(0, _rand.Next(0, 100));
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
yield return MakeRandomValueStruct();
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<string, ValueStruct> MakeRandomValueStructMap()
|
||||
{
|
||||
var map = new Dictionary<string, ValueStruct>();
|
||||
var count = _rand.Next(0, _rand.Next(0, 100));
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
map[Guid.NewGuid().ToString()] = MakeRandomValueStruct();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private TestStruct MakeRandomStruct()
|
||||
{
|
||||
return new TestStruct
|
||||
{
|
||||
FieldList = new LinkedList<ValueStruct>(MakeRandomValueStructs()),
|
||||
FieldVector = new List<ValueStruct>(MakeRandomValueStructs()),
|
||||
FieldSet = new HashSet<int>(Enumerable.Repeat(_rand.Next(), 100)),
|
||||
FieldMap = new Dictionary<string, ValueStruct>(MakeRandomValueStructMap()),
|
||||
FieldNullable = _rand.Next() % 2 == 0 ? MakeRandomValueStruct() : null,
|
||||
FieldBonded = new global::Bond.Bonded<ValueStruct>(MakeRandomValueStruct())
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SerializerTest()
|
||||
{
|
||||
using (var memory = SharedMemory.Create(Guid.NewGuid().ToString(), 4 * 1024 * 1024))
|
||||
using (var pool = new BufferPool(memory))
|
||||
{
|
||||
foreach (var protocol in new global::Bond.ProtocolType[] { global::Bond.ProtocolType.COMPACT_PROTOCOL, global::Bond.ProtocolType.FAST_PROTOCOL, global::Bond.ProtocolType.SIMPLE_PROTOCOL })
|
||||
{
|
||||
foreach (var marshal in new bool[] { true, false })
|
||||
{
|
||||
var serializer = new Serializer(protocol, marshal, pool, memory);
|
||||
|
||||
Assert.AreEqual(protocol, serializer.ProtocolType);
|
||||
Assert.AreEqual(marshal, serializer.IsMarshaled);
|
||||
|
||||
var s1 = MakeRandomStruct();
|
||||
TestStruct s2;
|
||||
|
||||
using (var buffer = serializer.Serialize(s1))
|
||||
{
|
||||
s2 = serializer.Deserialize<TestStruct>(buffer);
|
||||
}
|
||||
|
||||
// Bond.Comparer.Equal does not properly work with Bonded<T> fields.
|
||||
Assert.IsTrue(global::Bond.Comparer.Equal(s1.FieldBonded.Deserialize(), s2.FieldBonded.Deserialize()));
|
||||
s1.FieldBonded = s2.FieldBonded = global::Bond.Bonded<ValueStruct>.Empty;
|
||||
|
||||
Assert.IsTrue(global::Bond.Comparer.Equal(s1, s2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
namespace IPC.Bond.Managed.UnitTests
|
||||
|
||||
enum TestEnum
|
||||
{
|
||||
Value1 = -1,
|
||||
Value2 = 0,
|
||||
Value3 = 1
|
||||
}
|
||||
|
||||
struct ValueStruct
|
||||
{
|
||||
01: bool FieldBool;
|
||||
02: int8 FieldInt8;
|
||||
03: uint8 FieldUInt8;
|
||||
04: int16 FieldInt16;
|
||||
05: uint16 FieldUInt16;
|
||||
06: int32 FieldInt32;
|
||||
07: uint32 FieldUInt32;
|
||||
08: int64 FieldInt64;
|
||||
09: uint64 FieldUInt64;
|
||||
10: float FieldFloat;
|
||||
11: double FieldDouble;
|
||||
12: string FieldString;
|
||||
13: wstring FieldWString;
|
||||
14: TestEnum FieldEnum = Value1;
|
||||
15: blob FieldBlob;
|
||||
};
|
||||
|
||||
struct TestStruct
|
||||
{
|
||||
1: list<ValueStruct> FieldList;
|
||||
2: vector<ValueStruct> FieldVector;
|
||||
3: set<int32> FieldSet;
|
||||
4: map<string, ValueStruct> FieldMap;
|
||||
5: nullable<ValueStruct> FieldNullable;
|
||||
6: bonded<ValueStruct> FieldBonded;
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace IPC.Bond.Managed.UnitTests
|
||||
{
|
||||
[TestFixture(Category = "ManagedTests")]
|
||||
public class TransportTests
|
||||
{
|
||||
[Test]
|
||||
public async Task AcceptorConnectorTest()
|
||||
{
|
||||
var address = Guid.NewGuid().ToString();
|
||||
bool isClientClosed = false;
|
||||
bool isServerClosed = false;
|
||||
|
||||
var config = new Config { DefaultRequestTimeout = System.Threading.Timeout.InfiniteTimeSpan };
|
||||
|
||||
using (var transport = new Transport<global::Bond.Box<int>, global::Bond.Box<int>>(config))
|
||||
using (var acceptor = transport.MakeServerAcceptor(address, (inMemory, outMemory) => x => Task.FromResult(x)))
|
||||
using (var connector = transport.MakeClientConnector())
|
||||
{
|
||||
var servers = new List<Transport<global::Bond.Box<int>, global::Bond.Box<int>>.Server>();
|
||||
var newServerEvent = new System.Threading.ManualResetEventSlim(false);
|
||||
|
||||
acceptor.Accepted += (sender, args) =>
|
||||
{
|
||||
lock (servers)
|
||||
{
|
||||
servers.Add(args.Component);
|
||||
}
|
||||
|
||||
newServerEvent.Set();
|
||||
};
|
||||
|
||||
Transport<global::Bond.Box<int>, global::Bond.Box<int>>.Server server;
|
||||
|
||||
using (var client = await connector.ConnectAsync(address))
|
||||
{
|
||||
newServerEvent.Wait();
|
||||
|
||||
lock (servers)
|
||||
{
|
||||
Assert.AreEqual(1, servers.Count);
|
||||
server = servers.First();
|
||||
}
|
||||
|
||||
Assert.IsFalse(server.IsClosed);
|
||||
server.Closed += (sender, args) => { isServerClosed = true; };
|
||||
|
||||
Assert.IsFalse(client.IsClosed);
|
||||
client.Closed += (sender, args) => { isClientClosed = true; };
|
||||
|
||||
var request = new global::Bond.Box<int> { value = 100 };
|
||||
var response = await client.InvokeAsync(request);
|
||||
|
||||
Assert.IsTrue(global::Bond.Comparer.Equal(request, response));
|
||||
}
|
||||
|
||||
server.Dispose();
|
||||
}
|
||||
|
||||
Assert.IsTrue(isClientClosed);
|
||||
Assert.IsTrue(isServerClosed);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task AcceptConnectTest()
|
||||
{
|
||||
var address = Guid.NewGuid().ToString();
|
||||
|
||||
var config = new Config { DefaultRequestTimeout = System.Threading.Timeout.InfiniteTimeSpan };
|
||||
|
||||
using (var transport = new Transport<global::Bond.Box<int>, global::Bond.Box<int>>(config))
|
||||
using (var serversAccessor = transport.AcceptServers(address, (inMemory, outMemory) => x => Task.FromResult(x)))
|
||||
using (var clientAccessor = transport.ConnectClient(address, false))
|
||||
{
|
||||
var request = new global::Bond.Box<int> { value = 200 };
|
||||
var response = await clientAccessor.Client.InvokeAsync(request);
|
||||
|
||||
Assert.IsTrue(global::Bond.Comparer.Equal(request, response));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{BDFCB3AD-21A8-446E-840B-68718C49E7D4}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>IPC.Bond.Managed.UnitTests</RootNamespace>
|
||||
<AssemblyName>IPC.Bond.Managed.UnitTests</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
|
||||
<AssemblyClsCompliant>false</AssemblyClsCompliant>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<OutputPath>..\x64\Debug\</OutputPath>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>..\x64\Release\</OutputPath>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Bond">
|
||||
<HintPath>..\IPC\Packages\Bond.Core.CSharp.6.0.0\lib\net45\Bond.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Bond.Attributes">
|
||||
<HintPath>..\IPC\Packages\Bond.Core.CSharp.6.0.0\lib\net45\Bond.Attributes.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework">
|
||||
<HintPath>..\IPC\Packages\NUnit.3.6.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="IPC.Managed">
|
||||
<HintPath>..\IPC\$(Platform)\$(Configuration)\IPC.Managed.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="IPC.Managed.Transport">
|
||||
<HintPath>..\IPC\$(Platform)\$(Configuration)\IPC.Managed.Transport.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Managed\Build\Managed.vcxproj">
|
||||
<Project>{705098F7-AAAA-4954-8165-FDDCD66231F0}</Project>
|
||||
<Name>Managed</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Transport\Transport.csproj">
|
||||
<Project>{BDAAAAAD-21a8-446e-840b-68718c49e7d4}</Project>
|
||||
<Name>Transport</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="SerializationTests.cs" />
|
||||
<Compile Include="TransportTests.cs" />
|
||||
<Compile Include="generated\Test_types.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<None Include="Test.bond" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>$(SolutionDir)\IPC\packages\Bond.Compiler.6.0.0\tools\gbc.exe c# -o=$(MSBuildProjectDirectory)\generated $(MSBuildProjectDirectory)\Test.bond</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
</Project>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Bond.Compiler" version="6.0.0" targetFramework="net46" />
|
||||
<package id="Bond.Core.CSharp" version="6.0.0" targetFramework="net46" />
|
||||
<package id="NUnit" version="3.6.1" targetFramework="net46" />
|
||||
</packages>
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 0236301fe809802ecd754d5d047288ef397f9c79
|
|
@ -0,0 +1,18 @@
|
|||
@setlocal
|
||||
|
||||
@set PreferredToolArchitecture=x64
|
||||
@set BOOST_ROOT=%CD%\IPC\packages\boost.1.63.0.0\lib\native\include
|
||||
@set BOOST_LIBRARYDIR=%CD%\IPC\packages\boost.1.63.0.0
|
||||
@set BOND_GBC_PATH=%CD%\IPC\packages\Bond.Compiler.6.0.0\tools
|
||||
|
||||
@mkdir bond\build
|
||||
@mkdir bond\build\target
|
||||
|
||||
@pushd bond\build
|
||||
|
||||
@cmake -G "Visual Studio 14 2015 Win64" -DBOND_LIBRARIES_ONLY=ON -DBOND_ENABLE_COMM=FALSE -DBOND_ENABLE_GRPC=FALSE -DCMAKE_INSTALL_PREFIX=%CD%\target ..
|
||||
@cmake --build . --target
|
||||
@cmake --build . --target INSTALL
|
||||
|
||||
@popd
|
||||
@endlocal
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<config>
|
||||
<add key="repositoryPath" value="IPC\Packages" />
|
||||
</config>
|
||||
</configuration>
|
Загрузка…
Ссылка в новой задаче