зеркало из https://github.com/microsoft/IPC.Bond.git
Родитель
03a8c06660
Коммит
cdcd3a6652
|
@ -1,252 +1,45 @@
|
||||||
## Ignore Visual Studio temporary files, build results, and
|
# Compiled Object files
|
||||||
## files generated by popular Visual Studio add-ons.
|
*.slo
|
||||||
|
*.lo
|
||||||
# User-specific files
|
*.o
|
||||||
*.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
|
|
||||||
*.obj
|
*.obj
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
/ipch
|
||||||
|
*.gch
|
||||||
*.pch
|
*.pch
|
||||||
*.pdb
|
|
||||||
*.pgc
|
|
||||||
*.pgd
|
|
||||||
*.rsp
|
|
||||||
*.sbr
|
|
||||||
*.tlb
|
|
||||||
*.tli
|
|
||||||
*.tlh
|
|
||||||
*.tmp
|
|
||||||
*.tmp_proj
|
|
||||||
*.log
|
|
||||||
*.vspscc
|
|
||||||
*.vssscc
|
|
||||||
.builds
|
|
||||||
*.pidb
|
|
||||||
*.svclog
|
|
||||||
*.scc
|
|
||||||
|
|
||||||
# Chutzpah Test files
|
# Compiled Dynamic libraries
|
||||||
_Chutzpah*
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
|
||||||
# Visual C++ cache files
|
# Fortran module files
|
||||||
ipch/
|
*.mod
|
||||||
*.aps
|
*.smod
|
||||||
*.ncb
|
|
||||||
*.opendb
|
# Compiled Static libraries
|
||||||
*.opensdf
|
*.lai
|
||||||
*.sdf
|
*.la
|
||||||
*.cachefile
|
*.a
|
||||||
|
*.lib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
|
||||||
|
# Nuget packages
|
||||||
|
/packages
|
||||||
|
|
||||||
|
# Visual Studio files
|
||||||
|
/.vs
|
||||||
|
*.VC.opendb
|
||||||
*.VC.db
|
*.VC.db
|
||||||
*.VC.VC.opendb
|
*.vcxproj.user
|
||||||
|
|
||||||
# Visual Studio profiler
|
# Build folders
|
||||||
*.psess
|
**/x64
|
||||||
*.vsp
|
|
||||||
*.vspx
|
|
||||||
*.sap
|
|
||||||
|
|
||||||
# TFS 2012 Local Workspace
|
# Generated files
|
||||||
$tf/
|
UnitTestsManaged/generated
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
|
@ -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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
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
|
The above copyright notice and this permission notice shall be included in all
|
||||||
copies or substantial portions of the Software.
|
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,
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
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
|
# 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.
|
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>
|
Загрузка…
Ссылка в новой задаче