Use improved, header only ByteView/ByteVector

dependabot/npm_and_yarn/Src/WebController/UI/websocket-extensions-0.1.4
Pawel Kurowski 2019-11-21 11:33:30 +01:00
parent 08c3087aa7
commit 97a0a76dfa
13 changed files with 755 additions and 906 deletions

View File

@ -22,10 +22,7 @@
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Peripherals\Beacon.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Peripherals\Beacon.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\Interface.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\Interface.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\InterfaceFactory.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\InterfaceFactory.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteVector.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteView.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\CppTools\Encryption.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)MWR\CppTools\Encryption.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\CppTools\Utils.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Crypto\Sodium.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)MWR\Crypto\Sodium.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Slack\SlackApi.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)MWR\Slack\SlackApi.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Sockets\AddrInfo.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)MWR\Sockets\AddrInfo.cpp" />
@ -64,6 +61,7 @@
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\PrecompiledHeader.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\PrecompiledHeader.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Sdk.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Sdk.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteArray.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteArray.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteConverter.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteVector.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteVector.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteView.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteView.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\Encryption.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\Encryption.h" />
@ -72,8 +70,8 @@
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\PrecompiledHeader.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\PrecompiledHeader.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\SafeSmartPointerContainer.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\SafeSmartPointerContainer.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ScopeGuard.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ScopeGuard.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\SecureString.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\Utils.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\Utils.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\SecureString.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\XError.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\XError.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\Base32.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\Base32.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\Base64.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\Base64.h" />

View File

@ -8,10 +8,7 @@
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Peripherals\Beacon.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Peripherals\Beacon.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\Interface.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\Interface.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\InterfaceFactory.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\InterfaceFactory.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteVector.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteView.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\CppTools\Encryption.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)MWR\CppTools\Encryption.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\CppTools\Utils.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Crypto\Sodium.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)MWR\Crypto\Sodium.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Sockets\AddrInfo.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)MWR\Sockets\AddrInfo.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Sockets\InitializeSockets.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)MWR\Sockets\InitializeSockets.cpp" />
@ -42,15 +39,11 @@
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\InterfaceFactory.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\InterfaceFactory.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\PrecompiledHeader.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\PrecompiledHeader.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Sdk.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Sdk.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteArray.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteVector.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteView.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\Encryption.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\Encryption.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\Hash.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\Hash.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\Payload.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\Payload.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\PrecompiledHeader.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\PrecompiledHeader.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ScopeGuard.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ScopeGuard.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\Utils.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\XError.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\XError.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\Base32.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\Base32.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\Base64.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\Base64.h" />
@ -88,5 +81,10 @@
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\StructuredExceptionHandling.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\StructuredExceptionHandling.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)C3_BUILD_VERSION_HASH_PART.hxx" /> <ClInclude Include="$(MSBuildThisFileDirectory)C3_BUILD_VERSION_HASH_PART.hxx" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\InjectionBuffer.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\InjectionBuffer.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteArray.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteConverter.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteVector.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\ByteView.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\Utils.h" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,337 @@
#pragma once
#include "ByteView.h"
/// Example code of specializing ByteConverter for custom type A.
//struct A
//{
// uint16_t m_a, m_b;
//
// A(uint16_t a, uint16_t b) : m_a(a), m_b(b) {}
//};
//
//namespace MWR
//{
// template <>
// struct ByteConverter<A>
// {
// static ByteVector To(A const& a)
// {
// return ByteVector::Create(a.m_a, a.m_b);
// }
//
// static size_t Size(A const& a)
// {
// return 2 * sizeof(uint16_t);
// }
//
// static A From(ByteView& bv)
// {
// auto [a, b] = bv.Read<uint16_t, uint16_t>();
// return A(a, b);
// }
// };
//}
// specializations for ByteConverter for common types.
namespace MWR
{
// ByteConverter specialization for enums.
template <typename T>
struct ByteConverter<T, std::enable_if_t<std::is_enum_v<T>>>
{
static ByteVector To(T obj)
{
return ByteVector::Create(static_cast<std::underlying_type_t<T>>(obj));
}
constexpr static size_t Size()
{
return sizeof(std::underlying_type_t<T>);
}
static T From(ByteView& bv)
{
return static_cast<T>(bv.Read<std::underlying_type_t<T>>());
}
};
// ByteConverter specialization for std::pair<>.
template <typename T1, typename T2>
struct ByteConverter<std::pair<T1, T2>>
{
static ByteVector To(std::pair<T1, T2> const& obj)
{
return ByteVector::Create(obj.first, obj.second);
}
static size_t Size(std::pair<T1, T2> const& obj)
{
return ByteVector::Size(obj.first) + ByteVector::Size(obj.second);
}
static std::pair<T1, T2> From(ByteView& bv)
{
auto [t1, t2] = bv.Read<T1, T2>();
return { std::move(t1), std::move(t2) };
}
};
// ByteConverter specialization for std::filesystem::path.
template <>
struct ByteConverter<std::filesystem::path>
{
static ByteVector To(std::filesystem::path const& obj)
{
return ByteVector::Create(obj.wstring());
}
static size_t Size(std::filesystem::path const& obj)
{
return ByteVector::Size(obj.wstring());
}
static std::filesystem::path From(ByteView& bv)
{
return { bv.Read<std::wstring>() };
}
};
// ByteConverter specialization for std::byte.
template <>
struct ByteConverter<std::byte>
{
static ByteVector To(std::byte obj)
{
return ByteVector::Create(static_cast<unsigned char>(obj));
}
constexpr static size_t Size()
{
return sizeof(unsigned char);
}
static std::byte From(ByteView& bv)
{
return static_cast<std::byte>(bv.Read<unsigned char>());
}
};
/// Class allowing reading N bytes without coping data like in ByeView::Read<ByteArray<N>> or ByteView::Reed(size_t).
/// Using Bytes class in read will return ByteView with requested size using simple syntax:
/// someByteViewObject.Read<int, int, Bytes<7>, std::string>
template<size_t N>
class Bytes
{
/// This class should never be instantiated.
Bytes() = delete;
};
// ByteConverter specialization for MWR::Bytes.
template <size_t N>
struct ByteConverter<MWR::Bytes<N>>
{
static ByteView From(ByteView& bv)
{
if (N > bv.size())
throw std::out_of_range{ OBF(": Cannot read data from ByteView") };
auto retVal = bv.SubString(0, N);
bv.remove_prefix(N);
return retVal;
}
};
// ByteConverter specialization for vector types.
template <typename T>
struct ByteConverter<std::vector<T>>
{
static ByteVector To(std::vector<T> const& obj)
{
ByteVector ret;
ret.reserve(ByteVector::Size(obj));
ret.Write(static_cast<uint32_t>(obj.size()));
for (auto const& e : obj)
ret.Concat(ByteVector::Create(e));
return ret;
}
static size_t Size(std::vector<T> const& obj)
{
size_t size = sizeof(uint32_t); //four bytes for vector size.
for (auto const& e : obj)
size += ByteVector::Size(e);
return size;
}
static std::vector<T> From(ByteView& bv)
{
std::vector<T> ret;
auto size = bv.Read<uint32_t>();
ret.reserve(size);
for (auto i = 0u; i < size; ++i)
ret.push_back(bv.Read<T>());
return ret;
}
};
// ByteConverter specialization for map types.
template <typename T1, typename T2>
struct ByteConverter<std::map<T1, T2>>
{
static ByteVector To(std::map<T1, T2> const& obj)
{
ByteVector ret;
ret.reserve(ByteVector::Size(obj));
ret.Write(static_cast<uint32_t>(obj.size()));
for (auto const& [key, val] : obj)
ret.Concat(ByteVector::Create(key, val));
return ret;
}
static size_t Size(std::map<T1, T2> const& obj)
{
size_t size = sizeof(uint32_t); //four bytes for map size.
for (auto const& [key, val] : obj)
size += ByteVector::Size(key) + ByteVector::Size(val);
return size;
}
static std::map<T1, T2> From(ByteView& bv)
{
std::map<T1, T2> ret;
auto size = bv.Read<uint32_t>();
for (auto i = 0u; i < size; ++i)
{
auto [key, val] = bv.Read<T1, T2>();
ret.emplace(std::move(key), std::move(val));
}
return ret;
}
};
// ByteConverter specialization for std:array.
template <typename T, size_t N>
struct ByteConverter<std::array<T, N>>
{
static ByteVector To(std::array<T, N> const& obj)
{
ByteVector ret;
ret.reserve(ByteVector::Size(obj));
for (auto const& e : obj)
ret.Write(e);
return ret;
}
static size_t Size(std::array<T, N> const& obj)
{
auto ret = size_t{ 0 };
if constexpr (std::is_arithmetic_v<T>)
{
static_cast<void>(obj);
ret = sizeof(T) * N; // avoid extra calls when size of array is known.
}
else
{
for (auto const& e : obj)
ret += ByteVector::Size(e);
}
return ret;
}
static std::array<T, N> From(ByteView& bv)
{
// T might not be default constructible, array must be filled like aggregator,
// order of reading from ByteView would depend on calling convention.
std::vector<T> temp;
temp.reserve(N);
for (auto i = 0u; i < N; ++i)
temp.push_back(bv.Read<T>());
return MakeArray(std::move(temp), std::make_index_sequence<N>());
}
private:
template<size_t...Is>
static std::array<T, N> MakeArray(std::vector<T>&& vec, std::index_sequence<Is...>)
{
return { std::move(vec[Is])... };
}
};
// ByteConverter specialization for tuple.
template <typename T>
struct ByteConverter<T, std::enable_if_t<Utils::IsTuple<T>>>
{
static ByteVector To(T const& obj)
{
ByteVector ret;
ret.reserve(Size(obj));
TupleHandler<T>::Write(ret, obj);
return ret;
}
static size_t Size(T const& obj)
{
return TupleHandler<T>::Size(obj);
}
static auto From(ByteView& bv)
{
return TupleHandler<T>::Read(bv);
}
private:
/// @tparam T. Tuple type to read/write.
/// @tparam N. How many elements of tuple to handle. Functions will use recursion, decrementing N with each call.
template <typename T, size_t N = std::tuple_size_v<T>>
struct TupleHandler
{
/// Function responsible for recursively packing data to ByteVector.
/// @param self. Reference to ByteVector object using VariadicWriter.
/// @param t. reference to tuple.
static void Write(ByteVector& self, T const& t)
{
if constexpr (N != 0)
{
self.Write(std::get<std::tuple_size_v<T> - N>(t));
TupleHandler<T, N - 1>::Write(self, t);
}
}
/// Function responsible for recursively calculating buffer size needed for call with tuple argument.
/// @return size_t number of bytes needed.
static size_t Size(T const& t)
{
if constexpr (N != 0)
return ByteVector::Size(std::get<std::tuple_size_v<T> - N>(t)) + TupleHandler<T, N - 1>::Size(t);
else
return 0;
}
/// Function responsible for recursively packing data to tuple.
/// @param self. Reference to ByteView object using generate method.
static auto Read(ByteView& self)
{
if constexpr (N != 0)
{
auto current = std::make_tuple(self.Read<std::tuple_element_t<std::tuple_size_v<T> - N, T>>());
auto rest = TupleHandler<T, N - 1>::Read(self);
return std::tuple_cat(current, rest);
}
else
{
return std::tuple<>{};
}
}
};
};
}

View File

@ -1,82 +0,0 @@
#include "StdAfx.h"
#include "ByteVector.h"
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MWR::ByteVector::~ByteVector()
{
// Increase OpSec by clearing memory when ByteVector is not needed anymore.
SecureZeroMemory(data(), size());
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MWR::ByteVector::ByteVector(ByteVector const& other)
: Super(static_cast<Super const&>(other))
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MWR::ByteVector::ByteVector(ByteVector&& other)
: Super(static_cast<Super&&>(other))
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MWR::ByteVector::ByteVector(std::vector<uint8_t> other)
: Super(std::move(other))
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MWR::ByteVector& MWR::ByteVector::operator=(ByteVector const& other)
{
auto tmp = Super{ static_cast<Super const&>(other) };
std::swap(static_cast<Super&>(*this), tmp);
return *this;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MWR::ByteVector& MWR::ByteVector::operator=(ByteVector&& other)
{
std::swap(static_cast<Super&>(*this), static_cast<Super&>(other));
return *this;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool MWR::operator==(ByteVector const& lhs, ByteVector const& rhs)
{
if (lhs.size() != rhs.size())
return false;
for (size_t i = 0; i < lhs.size(); ++i)
if (lhs[i] != rhs[i])
return false;
return true;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool MWR::operator!=(ByteVector const& lhs, ByteVector const& rhs)
{
return !(lhs == rhs);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MWR::ByteVector MWR::Literals::operator "" _b(const char* data, size_t size)
{
MWR::ByteVector ret;
ret.resize(size);
std::memcpy(ret.data(), data, size);
return ret;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MWR::ByteVector MWR::Literals::operator "" _b(const wchar_t* data, size_t size)
{
MWR::ByteVector ret;
ret.resize(size * sizeof(wchar_t));
std::memcpy(ret.data(), data, size * sizeof(wchar_t));
return ret;
}

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "ByteArray.h" #include "ByteArray.h"
#include "Utils.h"
namespace MWR namespace MWR
{ {
@ -9,77 +10,21 @@ namespace MWR
class ByteVector; class ByteVector;
/// Empty template of class that can convert data from and to byte form. /// Empty template of class that can convert data from and to byte form.
/// @tparam T. Custom type that will work with byte containers using this converter type. /// Look for example in ByteConventer.h
/// Specialize this class to add methods: template <typename T, typename = void>
/// static ByteVector To(T const&);
/// Used to write data.
/// static T From(ByteView&);
/// Used to retrieve data.
/// Function must move ByteView& argument to position after the data that was retrieved.
/// static size_t Size(T const&);
/// Used to calculate size that type will need to be stored in ByteVector.
/// This function is not mandatory. If not provided function `To` will be called twice.
/// Once to calculate size for ByteVector allocation, and a second time to copy data.
template <typename T>
struct ByteConverter {}; struct ByteConverter {};
/// Example code of specializing ByteConverter for custom type A.
//struct A
//{
// uint16_t m_a, m_b;
// A(uint16_t a, uint16_t b) : m_a(a), m_b(b) {}
//};
//namespace MWR
//{
// template <>
// struct ByteConverter<A>
// {
// static ByteVector To(A const& a)
// {
// return ByteVector::Create(a.m_a, a.m_b);
// }
// static size_t Size(A const& a)
// {
// return 2 * sizeof(uint16_t);
// }
// static A From(ByteView& bv)
// {
// auto [a, b] = bv.Read<uint16_t, uint16_t>();
// return A(a, b);
// }
// };
//}
/// Idiom for detecting tuple types.
template <typename T>
constexpr bool IsTuple = false;
template<typename ...T>
constexpr bool IsTuple<std::tuple<T...>> = true;
/// Alias finding Store method return type.
/// Should be used with MWR::Utils::CanApply, and not directly.
template<class T, class...Ts>
using StoreReturnType = decltype(std::declval<T>().Store(std::declval<bool>(), std::declval<Ts>()...));
/// Alias for testing if type can be stored in ByteVector.
/// Expresion CanStoreType<int, double, std::string>::value will be true.
/// Expresion CanStoreType<SomeCustomType>::value will be false.
template<class...Ts>
using CanStoreType = MWR::Utils::CanApply<StoreReturnType, ByteVector, Ts...>;
/// Check if MWR namespace has function to get size of type T when it is stored to ByteVector. /// Check if MWR namespace has function to get size of type T when it is stored to ByteVector.
template <typename T> template <typename T>
class GotByteSize class ByteSizeFunctionType
{ {
template <typename C> static uint8_t test(decltype(MWR::ByteConverter<T>::Size(std::declval<T>()))); template <typename C> static uint32_t test(decltype(MWR::ByteConverter<T>::Size(std::declval<C>())));
template <typename C> static uint16_t test(...); template <typename C> static uint16_t test(decltype(MWR::ByteConverter<T>::Size(std::void_t<C>)));
template <typename C> static uint8_t test(...);
public: public:
enum { value = sizeof(test<T>(0)) == sizeof(uint8_t) }; enum { absent = 0, compileTime = (sizeof(uint16_t) - sizeof(uint8_t)), runTime = (sizeof(uint32_t) - sizeof(uint8_t)) };
enum { value = (sizeof(test<T>(0)) - sizeof(uint8_t)) };
}; };
/// An owning container. /// An owning container.
@ -93,27 +38,52 @@ namespace MWR
using ValueType = Super::value_type; using ValueType = Super::value_type;
/// Destructor zeroing memory. /// Destructor zeroing memory.
~ByteVector(); ~ByteVector()
{
// Increase OpSec by clearing memory when ByteVector is not needed anymore.
Utils::SecureMemzero(data(), size());
}
/// Copy constructor. /// Copy constructor.
/// @param other. Object to copy. /// @param other. Object to copy.
ByteVector(ByteVector const& other); ByteVector(ByteVector const& other)
: Super(static_cast<Super const&>(other))
{
}
/// Move constructor. /// Move constructor.
/// @param other. Object to move. /// @param other. Object to move.
ByteVector(ByteVector&& other); ByteVector(ByteVector&& other)
: Super(static_cast<Super&&>(other))
{
}
/// Copy assignment operator. /// Copy assignment operator.
/// @param other. Object to copy. /// @param other. Object to copy.
ByteVector& operator=(ByteVector const& other); ByteVector& operator=(ByteVector const& other)
{
auto tmp = Super{ static_cast<Super const&>(other) };
std::swap(static_cast<Super&>(*this), tmp);
return *this;
}
/// Move assignment operator. /// Move assignment operator.
/// @param other. Object to move. /// @param other. Object to move.
ByteVector& operator=(ByteVector&& other); ByteVector& operator=(ByteVector&& other)
{
std::swap(static_cast<Super&>(*this), static_cast<Super&>(other));
return *this;
}
/// Create from std::vector. /// Create from std::vector.
/// @param other. Object to copy. /// @param other. Object to copy.
ByteVector(std::vector<uint8_t> other); ByteVector(std::vector<uint8_t> other)
: Super(std::move(other))
{
}
// Enable methods. // Enable methods.
using Super::vector; using Super::vector;
@ -165,57 +135,57 @@ namespace MWR
/// Writes 4 Bytes header with size for types that have variable buffer length. /// Writes 4 Bytes header with size for types that have variable buffer length.
/// @tparam T. Types to be stored. /// @tparam T. Types to be stored.
/// @return itself to allow chaining. /// @return itself to allow chaining.
template <typename ...T, typename std::enable_if_t<CanStoreType<T...>::value, int> = 0> template <typename ...T>
ByteVector & Write(T const& ...args) ByteVector & Write(T const& ...args)
{ {
reserve(size() + CalculateStoreSize<T...>(true, args...)); reserve(size() + Size<T...>(args...));
Store<T...>(true, args...); Store<T...>(args...);
return *this; return *this;
} }
/// Write content of of provided objects. /// Write content of of provided objects.
/// Suports arithmetic types, std::string, std::wstring, std::string_view, std::wstring_view, ByteVector and ByteArray, and std::tuple of those types. /// Supports ByteView and ByteVector.
/// Does not write header with size for types that have variable buffer length. Recipient must know size in advance to read, therefore should use Read<ByteArray<someSize>>. /// Does not write header with size.
/// @tparam T. Types to be stored.
/// @return itself to allow chaining. /// @return itself to allow chaining.
template <typename ...T, typename std::enable_if_t<CanStoreType<T...>::value, int> = 0> template <typename T, typename ...Ts, typename = std::enable_if_t<Utils::IsOneOf<T, ByteView, ByteVector>::value>>
ByteVector & Concat(T const& ...args) ByteVector& Concat(T const& arg, Ts const& ...args)
{ {
reserve(size() + CalculateStoreSize<T...>(false, args...)); if constexpr (!sizeof...(Ts))
Store<T...>(false, args...); {
auto oldSize = size();
resize(oldSize + arg.size());
memcpy(data() + oldSize, arg.data(), arg.size());
}
else
{
Concat(arg);
Concat(args...);
}
return *this; return *this;
} }
// msvc can handle fold expression in debug mode, but fails with internal compiler error in release.
//template <typename ...T, typename = std::enable_if_t<((Utils::IsOneOf<T, ByteView, ByteVector>::value && ...))>>
//ByteVector& Concat(T const& ...arg)
//{
// auto oldSize = size();
// auto foldSize = (arg.size() + ...);
// resize(oldSize + foldSize);
// auto ptr = data() + oldSize;
// ((memcpy(ptr, arg.data(), arg.size()), (ptr += arg.size())), ...);
// return *this;
//}
/// Create new ByteVector with Variadic list of parameters. /// Create new ByteVector with Variadic list of parameters.
/// This function cannot be constructor, becouse it would be ambigious with std::vector costructors. /// This function cannot be constructor, becouse it would be ambigious with std::vector costructors.
/// @see ByteVector::Write and ByteVector::Concat for more informations. /// @see ByteVector::Write and ByteVector::Concat for more informations.
template <bool preferWriteOverConcat = true, typename ...T, typename std::enable_if_t<CanStoreType<T...>::value, int> = 0> template <typename ...T>
static ByteVector Create(T const& ...args) static ByteVector Create(T const& ...args)
{
if constexpr (preferWriteOverConcat)
return CreateByWrite(args...);
else
return CreateByConcat(args...);
}
/// Create new ByteVector with Variadic list of parameters.
/// This function cannot be constructor, becouse it would be ambigious with std::vector costructors.
/// @see ByteVector::Write for more informations.
template <typename ...T, typename std::enable_if_t<CanStoreType<T...>::value, int> = 0>
static ByteVector CreateByWrite(T const& ...args)
{ {
return ByteVector{}.Write(args...); return ByteVector{}.Write(args...);
} }
/// Create new ByteVector with Variadic list of parameters.
/// This function cannot be constructor, becouse it would be ambigious with std::vector costructors.
/// @see ByteVector::Concat for more informations.
template <typename ...T, typename std::enable_if_t<CanStoreType<T...>::value, int> = 0>
static ByteVector CreateByConcat(T const& ...args)
{
return ByteVector{}.Concat(args...);
}
/// Proxy function to ByteView::Read. /// Proxy function to ByteView::Read.
/// This function is added for convenience, but it does not change the state of ByteVector in a way ByteView::Read moves to next stored element after each Read. /// This function is added for convenience, but it does not change the state of ByteVector in a way ByteView::Read moves to next stored element after each Read.
template<typename ...T, typename = std::void_t<decltype(std::declval<ByteView>().Read<T...>())>> template<typename ...T, typename = std::void_t<decltype(std::declval<ByteView>().Read<T...>())>>
@ -224,305 +194,143 @@ namespace MWR
return ByteView{ *this }.Read<T...>(); return ByteView{ *this }.Read<T...>();
} }
private: /// Calculate the size that the argument will take in memory
/// Store content of of provided object. /// @param arg. argument to be stored.
/// @param storeSize. If true function will add four byte header with size for those types. /// @param args. rest of types that will be handled with recursion.
/// @param arg. argument to be stored. Supported types are ByteVector, ByteView, std::string, std::string_view, std::wstring, std::wstring_view. /// @return size_t number of bytes needed.
/// @return itself to allow chaining. template<typename T, typename ...Ts>
template<typename T, typename std::enable_if_t< static size_t Size(T const& arg, Ts const& ...args)
(
std::is_same_v<T, ByteVector>
|| std::is_same_v<T, ByteView>
|| std::is_same_v<T, std::string>
|| std::is_same_v<T, std::string_view>
|| std::is_same_v<T, std::wstring>
|| std::is_same_v<T, std::wstring_view>
), int> = 0>
ByteVector & Store(bool storeSize, T const& arg)
{ {
auto oldSize = size(); if constexpr (!sizeof...(Ts))
if (storeSize)
{ {
resize(oldSize + sizeof(uint32_t) + arg.size() * sizeof(T::value_type)); // There is only one type to calculate size for. Find implementation of Size for that type.
*reinterpret_cast<uint32_t*>(data() + oldSize) = static_cast<uint32_t>(arg.size()); if constexpr (ByteSizeFunctionType<T>::value == ByteSizeFunctionType<T>::compileTime)
std::memcpy(data() + oldSize + sizeof(uint32_t), arg.data(), arg.size() * sizeof(T::value_type)); return MWR::ByteConverter<T>::Size();
else if constexpr (ByteSizeFunctionType<T>::value == ByteSizeFunctionType<T>::runTime)
return MWR::ByteConverter<T>::Size(arg);
else if constexpr (ByteSizeFunctionType<T>::value == ByteSizeFunctionType<T>::absent)
return MWR::ByteConverter<T>::To(arg).size();
} }
else else
{ {
resize(oldSize + arg.size() * sizeof(T::value_type)); return Size(arg) + Size(args...);
std::memcpy(data() + oldSize, arg.data(), arg.size() * sizeof(T::value_type)); }
}
private:
/// Store custom type.
/// param arg. object to be stored. There must exsist MWR::ByteConverter<T>::To(T const&) method avalible to store custom type.
/// param args. rest of objects that will be handled with recursion.
/// @return itself to allow chaining.
template<typename T, typename ...Ts, typename std::enable_if_t<std::is_same_v<decltype(MWR::ByteConverter<T>::To(std::declval<T>())), MWR::ByteVector >, int> = 0>
ByteVector & Store(T const& arg, Ts const& ...args)
{
if constexpr (!sizeof...(Ts))
{
Concat(MWR::ByteConverter<T>::To(arg));
}
else
{
Store(arg);
Store(args...);
} }
return *this; return *this;
} }
};
/// Store content of array. Size must be known at compile time and will not be saved in data. /// ByteConverter specialization for ByteVector, ByteView, std::string, std::string_view, std::wstring, std::wstring_view.
/// @tparam T. Type to be stored. Supported types are ByteArray. /// This is a basic specialization and will not be moved to ByteConverter.h
/// @return itself to allow chaining. template <typename T>
template<typename T, typename std::enable_if_t<IsByteArray<T>, int> = 0> struct ByteConverter<T, std::enable_if_t<Utils::IsOneOf<T, ByteVector, ByteView, std::string, std::string_view, std::wstring, std::wstring_view>::value>>
ByteVector & Store(bool unused, T const& arg) {
static ByteVector To(T const& obj)
{ {
auto oldSize = size(); auto ret = ByteVector{};
resize(oldSize + arg.size()); auto elementSize = static_cast<uint32_t>(obj.size());
std::memcpy(data() + oldSize, arg.data(), arg.size()); ret.resize(Size(obj));
memcpy(ret.data(), &elementSize, sizeof(elementSize));
return *this; memcpy(ret.data() + sizeof(elementSize), obj.data(), elementSize * sizeof(T::value_type));
return ret;
} }
/// Store arithmetic type. static size_t Size(T const& obj)
/// tparam T. Type to be stored.
/// @return itself to allow chaining.
template<typename T, typename std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
ByteVector & Store(bool unused, T const& arg)
{ {
auto oldSize = size(); return sizeof(uint32_t) + (obj.size() * sizeof(T::value_type));
resize(oldSize + sizeof(T));
*reinterpret_cast<T*>(data() + oldSize) = arg;
return *this;
} }
/// Store custom type. // Reading functions are part of ByteView implementation. To deserialize data include ByteView.h
/// tparam T. Type to be stored. There must exsist MWR::ToBytes(T const&) method avalible to store custom type. static T From(ByteView& bv);
/// @return itself to allow chaining. };
template<typename T, typename std::enable_if_t<std::is_same_v<decltype(MWR::ByteConverter<T>::To(std::declval<T>())), MWR::ByteVector >, int> = 0>
ByteVector & Store(bool unused, T const& arg) /// ByteConverter specialization for arithmetic types.
/// This is a basic specialization and will not be moved to ByteConverter.h
template <typename T>
struct ByteConverter<T, std::enable_if_t<std::is_arithmetic_v<T>>>
{
static ByteVector To(T obj)
{ {
Concat(MWR::ByteConverter<T>::To(arg)); auto ret = ByteVector{};
return *this; ret.resize(sizeof(T));
*reinterpret_cast<T*>(ret.data()) = obj;
return ret;
} }
/// Store all arguments at the end of ByteVector. constexpr static size_t Size()
/// @param storeSize if true four byte header will be added to types in T that does not have size defined at compile time.
/// @tparam T. Parameter pack to be written. Types are deduced from function call.
/// @return itself to allow chaining.
/// @remarks This function is called only for two or more arguments.
/// @remarks Each type in parameter pack must have corresponding Write method for one argument.
template <typename ...T, typename std::enable_if_t<(sizeof...(T) > 1), int> = 0>
ByteVector & Store(bool storeSize, T const& ...args)
{
VariadicStoreHelper<T...>::Store(*this, storeSize, args...);
return *this;
}
/// Store tuple type.
/// @param storeSize if true four byte header will be added to types in T that does not have size defined at compile time.
/// tparam T. Tuple type to be stored.
/// @return itself to allow chaining.
template<typename T, typename std::enable_if_t<IsTuple<T>, int> = 0>
ByteVector & Store(bool storeSize, T const& arg)
{
StoreHelper<T>::Store(*this, storeSize, arg);
return *this;
}
/// Calculate the size that the argument will take in memory
/// @param storeSizeHeader. If true function will add four byte header with size for those types.
/// @param arg. argument to be stored. Supported types are ByteVector, ByteView, std::string, std::string_view, std::wstring, std::wstring_view.
/// @return size_t number of bytes needed.
template<typename T, typename std::enable_if_t<
(
std::is_same_v<T, ByteVector>
|| std::is_same_v<T, ByteView>
|| std::is_same_v<T, std::string>
|| std::is_same_v<T, std::string_view>
|| std::is_same_v<T, std::wstring>
|| std::is_same_v<T, std::wstring_view>
), int> = 0>
static size_t CalculateStoreSize(bool storeSizeHeader, T const& arg)
{
return storeSizeHeader ? arg.size() + sizeof(uint32_t) : arg.size();
}
/// Calculate the size that the argument will take in memory
/// @param unused. Supports finding this function in template calls.
/// @param arg. argument to be stored. Supports ByteArray type.
/// @return size_t number of bytes needed.
template<typename T, typename std::enable_if_t<IsByteArray<T>, int> = 0>
static size_t CalculateStoreSize(bool unused, T const& arg)
{
return arg.size();
}
/// Calculate the size that the argument will take in memory
/// @param unused. Supports finding this function in template calls.
/// @param arg. argument to be stored. Supports arithmetic types.
/// @return size_t number of bytes needed.
template<typename T, typename std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
static size_t CalculateStoreSize(bool unused, T const& arg)
{ {
return sizeof(T); return sizeof(T);
} }
/// Calculate the size that the argument will take in memory // Reading functions are part of ByteView implementation. To deserialize data include ByteView.h
/// tparam T. Type to be stored. There must exsist MWR::ByteConverter<T>::To(T const&) function and MWR::ByteConverter<T>::Size(T const&) static T From(ByteView& bv);
/// @return size_t number of bytes needed.
template<typename T, typename std::enable_if_t<std::is_same_v<decltype(MWR::ByteConverter<T>::To(std::declval<T>())), ByteVector> && GotByteSize<T>::value, int> = 0>
static size_t CalculateStoreSize(bool unused, T const& arg)
{
return MWR::ByteConverter<T>::Size(arg);
}
/// Calculate the size that the argument will take in memory
/// tparam T. Type to be stored. There must exsist MWR::ByteConverter<T>::To(T const&) method avalible to store custom type.
/// @return size_t number of bytes needed.
template<typename T, typename std::enable_if_t<std::is_same_v<decltype(MWR::ByteConverter<T>::To(std::declval<T>())), ByteVector> && !GotByteSize<T>::value, int> = 0>
static size_t CalculateStoreSize(bool unused, T const& arg)
{
return MWR::ByteConverter<T>::To(arg).size();
}
/// Calculate the size that the arguments will take in memory. This function is responsible for unwrapping variadic arguments calls.
/// @param storeSizeHeader. If true function writes 4 bytes header with size for types that have variable buffer length.
/// @param args. arguments to be stored.
/// @return size_t number of bytes needed.
template <typename ...T, typename std::enable_if_t<(sizeof...(T) > 1), int> = 0>
static size_t CalculateStoreSize(bool storeSizeHeader, T const& ...args)
{
return VariadicStoreHelper<T...>::CalculateStoreSize(storeSizeHeader, args...);
}
/// Calculate the size that the arguments will take in memory. This function is responsible for unwrapping calls with tuple type.
/// @param storeSizeHeader. If true function writes 4 bytes header with size for types that have variable buffer length.
/// @param arg. arguments to be stored in form of tuple.
/// @return size_t number of bytes needed.
template<typename T, typename std::enable_if_t<IsTuple<T>, int> = 0>
static size_t CalculateStoreSize(bool storeSizeHeader, T const& arg)
{
return StoreHelper<T>::CalculateStoreSize(storeSizeHeader, arg);
}
/// Delegate to class idiom.
/// Function templates cannot be partially specialized.
/// @tparam T. First type from parameter pack. Each recursive call will unfold one type.
/// @tparam Rest. Parameter pack storing rest of provided types.
template <typename T, typename ...Rest>
struct VariadicStoreHelper
{
/// Function responsible for recursively packing data to ByteVector.
/// @param storeSizeHeader if true four byte header will be added to types in T that does not have size defined at compile time.
/// @param self. Reference to ByteVector object using VariadicWriter.
/// @note Template used here should remove function if one of types cannot be stored.
/// Compiler should mark error of compilation in code location where function was used with wrong parameters.
/// Becouse of MSVC SFINAE bug compiler can mark this function with std::enable_if_t<false... error.
/// If you see this error examine ByteVector::Write and ByteVector::Concat usages for potential invalid parameters.
template <typename std::enable_if_t<CanStoreType<T>::value, int> = 0>
static void Store(ByteVector& self, bool storeSizeHeader, T const& current, Rest const& ...rest)
{
self.Store(storeSizeHeader, current);
VariadicStoreHelper<Rest...>::Store(self, storeSizeHeader, rest...);
}
/// Function responsible for recursively calculating buffer size needed for call with variadic argument list.
/// @param storeSizeHeader if true four byte header will be added to types in T that does not have size defined at compile time.
/// @return size_t number of bytes needed.
static size_t CalculateStoreSize(bool storeSizeHeader, T const& current, Rest const& ...rest)
{
return ByteVector::CalculateStoreSize(storeSizeHeader, current) + VariadicStoreHelper<Rest...>::CalculateStoreSize(storeSizeHeader, rest...);
}
};
/// Delegate to class idiom.
/// Function templates cannot be partially specialized.
/// Closure specialization.
/// @tparam T. Type to be extracted stored in ByteVector.
/// @note Template used here should remove function if one of types cannot be stored.
/// Compiler should mark error of compilation in code location where function was used with wrong parameters.
/// Becouse of MSVC SFINAE bug compiler can mark this function with std::enable_if_t<false... error.
/// If you see this error examine ByteVector::Write and ByteVector::Concat usages for potential invalid parameters.
template <typename T>
struct VariadicStoreHelper<T>
{
/// Function responsible for recursively packing data to ByteVector.
/// @param storeSizeHeader if true four byte header will be added to types in T that does not have size defined at compile time.
/// @param self. Reference to ByteVector object using VariadicWriter.
template <typename std::enable_if_t<CanStoreType<T>::value, int> = 0>
static void Store(ByteVector& self, bool storeSizeHeader, T const& current)
{
self.Store(storeSizeHeader, current);
}
/// Function responsible for recursively calculating buffer size needed for call with variadic argument list.
/// @param storeSizeHeader if true four byte header will be added to types in T that does not have size defined at compile time.
/// @return size_t number of bytes needed.
static size_t CalculateStoreSize(bool storeSizeHeader, T const& current)
{
return ByteVector::CalculateStoreSize(storeSizeHeader, current);
}
};
/// Delegate to class idiom.
/// Function templates cannot be partially specialized.
/// @tparam T. Tuple type to write.
/// @tparam N. How many elements of tuple to write.
template <typename T, size_t N = std::tuple_size_v<T>>
struct StoreHelper
{
/// Function responsible for recursively packing data to ByteVector.
/// @param self. Reference to ByteVector object using VariadicWriter.
/// @param storeSizeHeader if true four byte header will be added to types in T that does not have size defined at compile time.
/// @param t. reference to tuple.
/// @note Template used here should remove function if one of types cannot be stored.
/// Compiler should mark error of compilation in code location where function was used with wrong parameters.
/// Becouse of MSVC SFINAE bug compiler can mark this function with std::enable_if_t<false... error.
/// If you see this error examine ByteVector::Write and ByteVector::Concat usages for potential invalid parameters.
template <typename std::enable_if_t<CanStoreType<decltype(std::get<std::tuple_size_v<T> -N>(t))>::value, int> = 0>
static void Store(ByteVector& self, bool storeSizeHeader, T const& t)
{
self.Store(storeSizeHeader, std::get<std::tuple_size_v<T> - N>(t));
StoreHelper<T, N - 1>::Store(self, storeSizeHeader, t);
}
/// Function responsible for recursively calculating buffer size needed for call with tuple argument.
/// @param storeSizeHeader if true four byte header will be added to types in T that does not have size defined at compile time.
/// @return size_t number of bytes needed.
static size_t CalculateStoreSize(bool storeSize, T const& t)
{
return ByteVector::CalculateStoreSize(storeSizeHeader, std::get<std::tuple_size_v<T> - N>(t)) + StoreHelper<T, N - 1>::CalculateStoreSize(storeSizeHeader, t);
}
};
/// Delegate to class idiom.
/// Function templates cannot be partially specialized.
/// Closure specialization.
/// @tparam T. Tuple type to write.
template <typename T>
struct StoreHelper<T, 0>
{
/// Closing function, does nothing.
static void Store(ByteVector&, bool, T const&)
{
// Do nothing.
}
/// Closing function, returns 0.
static size_t CalculateStoreSize(bool, T const&)
{
return 0;
}
};
}; };
/// Checks if the contents of lhs and rhs are equal.
/// @param lhs. Left hand side of operator.
/// @param rhs. Right hand side of operator.
bool operator==(ByteVector const& lhs, ByteVector const& rhs);
/// Checks if the contents of lhs and rhs are equal.
/// @param lhs. Left hand side of operator.
/// @param rhs. Right hand side of operator.
bool operator!=(ByteVector const& lhs, ByteVector const& rhs);
namespace Literals namespace Literals
{ {
/// Create ByteVector with syntax ""_bvec /// Create ByteVector with syntax ""_bvec
ByteVector operator "" _b(const char* data, size_t size); inline ByteVector operator "" _b(const char* data, size_t size)
{
MWR::ByteVector ret;
ret.resize(size);
std::memcpy(ret.data(), data, size);
return ret;
}
/// Create ByteVector with syntax L""_bvec /// Create ByteVector with syntax L""_bvec
ByteVector operator "" _b(const wchar_t* data, size_t size); inline ByteVector operator "" _b(const wchar_t* data, size_t size)
{
MWR::ByteVector ret;
ret.resize(size * sizeof(wchar_t));
std::memcpy(ret.data(), data, size * sizeof(wchar_t));
return ret;
}
} }
} }
/// Checks if the contents of lhs and rhs are equal.
/// @param lhs. Left hand side of operator.
/// @param rhs. Right hand side of operator.
inline bool operator==(MWR::ByteVector const& lhs, MWR::ByteVector const& rhs)
{
if (lhs.size() != rhs.size())
return false;
for (size_t i = 0; i < lhs.size(); ++i)
if (lhs[i] != rhs[i])
return false;
return true;
}
/// Checks if the contents of lhs and rhs are equal.
/// @param lhs. Left hand side of operator.
/// @param rhs. Right hand side of operator.
inline bool operator!=(MWR::ByteVector const& lhs, MWR::ByteVector const& rhs)
{
return !(lhs == rhs);
}
namespace std namespace std
{ {
/// Add hashing function for ByteVector. /// Add hashing function for ByteVector.

View File

@ -1,88 +0,0 @@
#include "StdAfx.h"
#include "ByteView.h"
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
std::vector<std::string> MWR::SplitAndCopy(std::string_view stringToBeSplitted, std::string_view delimiter)
{
return Split<true>(stringToBeSplitted, delimiter);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MWR::ByteView::ByteView(ByteVector const& data, size_t offset)
: Super([&]() { return data.size() >= offset ? data.data() + offset : throw std::out_of_range{ OBF("Out of range. Data size: ") + std::to_string(data.size()) + OBF(" offset: ") + std::to_string(offset) }; }(), data.size() - offset)
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MWR::ByteView::ByteView(ByteVector::const_iterator begin, ByteVector::const_iterator end)
: Super([&]() { return end >= begin ? begin._Ptr : throw std::out_of_range{ OBF("Out of range by: ") + std::to_string(begin - end) + OBF(" elements.") }; }(), static_cast<size_t>(end - begin))
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MWR::ByteView::ByteView(std::basic_string_view<ByteVector::value_type> basicSring)
: Super(basicSring)
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MWR::ByteView::ByteView(std::string_view data)
: Super{ reinterpret_cast<const ByteVector::value_type*>(data.data()), data.size() }
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MWR::ByteView::operator MWR::ByteView::Super() const noexcept
{
return Super{ data(), size() };
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MWR::ByteView::operator MWR::ByteVector() const
{
return { begin(), end() };
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MWR::ByteView::operator std::string() const
{
return { reinterpret_cast<const char*>(data()), size() };
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MWR::ByteView::operator std::string_view() const
{
return { reinterpret_cast<const char*>(data()), size() };
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MWR::ByteVector MWR::ByteView::Read(size_t byteCount)
{
if (byteCount > size())
throw std::out_of_range{ OBF(": Size: ") + std::to_string(size()) + OBF(". Cannot read ") + std::to_string(byteCount) + OBF(" bytes.") };
auto retVal = ByteVector{ begin(), begin() + byteCount };
remove_prefix(byteCount);
return retVal;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MWR::ByteView MWR::ByteView::SubString(const size_type offset, size_type count) const
{
return Super::substr(offset, count);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MWR::ByteView MWR::Literals::operator "" _bv(const char* data, size_t size)
{
return { reinterpret_cast<const uint8_t*>(data), size };
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MWR::ByteView MWR::Literals::operator "" _bv(const wchar_t* data, size_t size)
{
return { reinterpret_cast<const uint8_t*>(data), size * sizeof(wchar_t) };
}

View File

@ -4,46 +4,6 @@
namespace MWR namespace MWR
{ {
/// Function splitting string.
///
/// @tparam copy. Indicates should strings be copied.
/// @param stringToBeSplitted initial string to be tokenized.
/// @param delimiter to be searched in string.
/// @throws std::runtime_error if delimiter empty or greatter equal to stringToBeSplitted
/// @returns std::vector<std::string> or std::vector<std::string_view> depending on copy flag.
template<bool copy = false>
std::vector<std::conditional_t<copy, std::string, std::string_view>> Split(std::string_view stringToBeSplitted, std::string_view delimiter)
{
if (delimiter.empty() && delimiter.size() >= stringToBeSplitted.size())
throw std::runtime_error{ OBF("Delimiter size is incorrect") };
using ReturnType = decltype(Split<copy>(stringToBeSplitted, delimiter));
ReturnType splittedString;
// startIndex can overflow, endIndex is checked first, and lazy check on startIndex will avoid last empty token.
for (size_t startIndex = 0u, endIndex = 0u; endIndex < stringToBeSplitted.size() && startIndex < stringToBeSplitted.size(); startIndex = endIndex + delimiter.size())
{
endIndex = stringToBeSplitted.find(delimiter, startIndex);
// skip empty tokens when delimiter is repeated.
if (endIndex - startIndex)
splittedString.emplace_back(stringToBeSplitted.substr(startIndex, endIndex - startIndex));
}
return splittedString;
}
/// Proxy to Split<true>
///
/// @param stringToBeSplitted initial string to be tokenized.
/// @param delimiter to be searched in string.
/// @throws std::runtime_error if delimiter empty or greatter equal to stringToBeSplitted
/// @returns std::vector<std::string> tokenized string.
std::vector<std::string> SplitAndCopy(std::string_view stringToBeSplitted, std::string_view delimiter);
/// Idiom for detecting ByteView::Get.
template <typename X>
constexpr bool IsGetter = false;
/// Non owning container. /// Non owning container.
class ByteView : std::basic_string_view<ByteVector::value_type> class ByteView : std::basic_string_view<ByteVector::value_type>
{ {
@ -59,19 +19,37 @@ namespace MWR
/// @param offset. Offset from first byte of data collection. /// @param offset. Offset from first byte of data collection.
/// @return ByteView. View of data. /// @return ByteView. View of data.
/// @throw std::out_of range. If offset is greater than data.size(). /// @throw std::out_of range. If offset is greater than data.size().
ByteView(ByteVector const& data, size_t offset = 0); ByteView(ByteVector const& data, size_t offset = 0)
: Super([&]()
{
return data.size() >= offset ? data.data() + offset : throw std::out_of_range{ OBF("Out of range. Data size: ") + std::to_string(data.size()) + OBF(" offset: ") + std::to_string(offset) };
}(), data.size() - offset)
{
}
/// Create from ByteVector iterators. /// Create from ByteVector iterators.
/// @param begin. Iterator to first element of data. /// @param begin. Iterator to first element of data.
/// @param end. Iterator to past the last element of data. /// @param end. Iterator to past the last element of data.
/// @return ByteView. View of data. /// @return ByteView. View of data.
/// @throw std::out_of range. If begin is greater than end. /// @throw std::out_of range. If begin is greater than end.
ByteView(ByteVector::const_iterator begin, ByteVector::const_iterator end); ByteView(ByteVector::const_iterator begin, ByteVector::const_iterator end)
: Super([&]()
{
return end >= begin ? begin._Ptr : throw std::out_of_range{ OBF("Out of range by: ") + std::to_string(begin - end) + OBF(" elements.") };
}(), static_cast<size_t>(end - begin))
{
}
/// Create from std::basic_string_view<uint8_t>. /// Create from std::basic_string_view<uint8_t>.
/// @param data. Data from which view will be constructed. /// @param data. Data from which view will be constructed.
/// @return ByteView. View of data. /// @return ByteView. View of data.
ByteView(std::basic_string_view<ByteVector::value_type> data); ByteView(std::basic_string_view<ByteVector::value_type> data)
: Super(data)
{
}
/// Create from ByteArray. /// Create from ByteArray.
/// @param data. Data from which view will be constructed. /// @param data. Data from which view will be constructed.
@ -86,25 +64,43 @@ namespace MWR
/// Create from std::string_view. /// Create from std::string_view.
/// @param data. Data from which view will be constructed. /// @param data. Data from which view will be constructed.
/// @return ByteView. View of data. /// @return ByteView. View of data.
ByteView(std::string_view data); ByteView(std::string_view data)
: Super{ reinterpret_cast<const ByteVector::value_type*>(data.data()), data.size() }
{
}
/// Allow cast to Privately inherited Type. /// Allow cast to Privately inherited Type.
operator Super() const noexcept; operator Super() const noexcept
{
return Super{ data(), size() };
}
/// Allow cast to ByteVector. /// Allow cast to ByteVector.
operator ByteVector() const; operator ByteVector() const
{
return { begin(), end() };
}
/// Allow cast to std::string(). /// Allow cast to std::string().
operator std::string() const; operator std::string() const
{
return { reinterpret_cast<const char*>(data()), size() };
}
/// Allow cast to std::string_view. /// Allow cast to std::string_view.
operator std::string_view() const; operator std::string_view() const
{
return { reinterpret_cast<const char*>(data()), size() };
}
/// Create a sub-string from this ByteView. /// Create a sub-string from this ByteView.
/// @param offset. Position of the first byte. /// @param offset. Position of the first byte.
/// @param count. Requested length /// @param count. Requested length
/// @returns ByteView. View of the substring /// @returns ByteView. View of the substring
ByteView SubString(const size_type offset = 0, size_type count = npos) const; ByteView SubString(const size_type offset = 0, size_type count = npos) const
{
return Super::substr(offset, count);
}
// Enable methods. // Enable methods.
using std::basic_string_view<ByteVector::value_type>::basic_string_view; using std::basic_string_view<ByteVector::value_type>::basic_string_view;
@ -143,260 +139,89 @@ namespace MWR
/// @returns ByteVector. Owning container with the read bytes. /// @returns ByteVector. Owning container with the read bytes.
/// @param byteCount. How many bytes should be read. /// @param byteCount. How many bytes should be read.
/// @remarks Read is not compatible with Read<ByteVector>.
/// Data stored in ByteVector with Write<ByteVector> should be accessed with Read<ByteVector>
/// @throws std::out_of_range. If byteCount > size(). /// @throws std::out_of_range. If byteCount > size().
ByteVector Read(size_t byteCount); ByteVector Read(size_t byteCount)
/// Read bytes and remove them from ByteView.
/// @remarks Read is not compatible with Read<ByteVector>.
/// Data stored in ByteVector with Write<ByteVector> should be accessed with Read<ByteVector>
/// @returns T. Owning container with the read bytes.
/// @throws std::out_of_range. If ByteView is too short to hold size of object to return.
template<typename T = ByteVector>
std::enable_if_t<(std::is_same_v<T, ByteVector> || std::is_same_v<T, std::string> || std::is_same_v<T, std::wstring>), T> Read()
{ {
if (sizeof(uint32_t) > size()) if (byteCount > size())
throw std::out_of_range{ OBF(": Cannot read size from ByteView ") }; throw std::out_of_range{ OBF(": Size: ") + std::to_string(size()) + OBF(". Cannot read ") + std::to_string(byteCount) + OBF(" bytes.") };
auto elementCount = *reinterpret_cast<const uint32_t*>(data()); auto retVal = ByteVector{ begin(), begin() + byteCount };
auto byteCount = elementCount * sizeof(T::value_type);
remove_prefix(sizeof(uint32_t));
T retVal;
retVal.resize(elementCount);
std::memcpy(retVal.data(), data(), byteCount);
remove_prefix(byteCount); remove_prefix(byteCount);
return retVal; return retVal;
} }
/// Read bytes and remove them from ByteView.
/// This function will return non owning container. It is helpful to prevent coping large buffers, if user know that original container is still valid.
/// @returns T. Non owning container with the read bytes.
/// @throws std::out_of_range. If ByteView is too short to hold size of object to return.
template<typename T>
std::enable_if_t<(std::is_same_v<T, ByteView> || std::is_same_v<T, std::string_view> || std::is_same_v<T, std::wstring_view>), T> Read()
{
if (sizeof(uint32_t) > size())
throw std::out_of_range{ OBF(": Cannot read size from ByteView ") };
auto elementCount = *reinterpret_cast<const uint32_t*>(data());
auto byteCount = elementCount * sizeof(T::value_type);
remove_prefix(sizeof(uint32_t));
auto retVal = T{reinterpret_cast<T::value_type const*>(data()), elementCount};
remove_prefix(byteCount);
return retVal;
}
/// Read bytes and remove them from ByteView.
/// @returns ByteArray. Owning container with the read bytes.
/// @throws std::out_of_range. If ByteView is too short to hold size of object to return.
template<typename T>
std::enable_if_t<IsByteArray<T>, T> Read()
{
if (std::tuple_size<typename T>::value > size())
throw std::out_of_range{ OBF(": Cannot read data from ByteView") };
T retVal;
std::memcpy(retVal.data(), data(), std::tuple_size<typename T>::value);
remove_prefix(std::tuple_size<typename T>::value);
return retVal;
}
/// Read arithmetic type and remove it from ByteView.
/// @tparam T. Type to return;
/// @returns T. Owning container with the read bytes.
/// @throws std::out_of_range. If sizeof(T) > size().
template<typename T>
std::enable_if_t<std::is_arithmetic<T>::value, T> Read()
{
if (sizeof(T) > size())
throw std::out_of_range{ OBF(": Size: ") + std::to_string(size()) + OBF(". Cannot read ") + std::to_string(sizeof(T)) + OBF(" bytes.") };
auto retVal = *reinterpret_cast<const T*>(data());
remove_prefix(sizeof(T));
return retVal;
}
/// Read custom type and remove it from ByteView. /// Read custom type and remove it from ByteView.
/// @tparam T. Type to return; /// @param arg. Type to be read from ByteView
/// @returns T. /// @param args. Rest of types that need to be handled with recursion.
template<typename T> template<typename T, typename ...Ts, typename = decltype(MWR::ByteConverter<std::remove_const_t<T>>::From(std::declval<ByteView>()))>
std::enable_if_t < std::is_same_v<decltype(MWR::ByteConverter<T>::From(std::declval<ByteView>())), T > , T > Read()
{
return ByteConverter<T>::From(*this);
}
/// Read tuple type.
/// @tparam T. Tuple type to return;
/// @returns T. Owning container with the read bytes.
template<typename T>
std::enable_if_t<IsTuple<T>, T> Read()
{
return TupleGenerator<T>::Generate(*this);
}
/// Class allowing reading N bytes without coping data like in ByeView::Read<ByteArray<N>> or ByteView::Reed(size_t).
template <size_t N>
class Get
{
public:
/// Define size marker.
static constexpr size_t size = N;
static constexpr size_t Size = size;
private:
/// Private constructor.
/// This class should never be instantiated.
Get() = default;
};
/// Read bytes and remove them from ByteView.
/// @returns ByteView. Non owning container with the read bytes.
/// @throws std::out_of_range. If ByteView is too short to hold size of object to return.
template<typename T>
std::enable_if_t<IsGetter<T>, ByteView> Read()
{
if (T::Size > size())
throw std::out_of_range{ OBF(": Cannot read data from ByteView") };
auto retVal = SubString(0, T::Size);
remove_prefix(T::Size);
return retVal;
}
/// Read tuple and remove bytes from ByteView.
/// @tparam T. Parameter pack of types to be stored in tuple. Must consist more then one type. Each of types must be parsable by Reed template for one type.
/// @returns std::tuple. Tuple will store types provided in parameter pack.
/// @throws std::out_of_range. If ByteView is too short to possibly store all provided types.
template<typename ...T, typename std::enable_if_t<(sizeof...(T) > 1), int> = 0>
auto Read() auto Read()
{ {
return VariadicTupleGenerator<T...>::Generate(*this); if constexpr (!sizeof...(Ts))
{
return ByteConverter<std::remove_const_t<T>>::From(*this);
}
else
{
auto current = std::make_tuple(Read<T>());
if constexpr (sizeof...(Ts) > 1)
return std::tuple_cat(std::move(current), Read<Ts...>());
else
return std::tuple_cat(std::move(current), std::make_tuple(Read<Ts...>()));
}
} }
/// Template function allowing structured binding to new std::string or std::string_view variables from text stored in ByteView.
///
/// auto [a,b] = ToStringArray<2>();
/// @tparam expectedSize number of words that should be returned from function.
/// @tparam copy if true words will be copied to std::string variables.
/// @tparam modifyByteView if true parsed words will be removed from ByteView object.
/// @returns std::array<std::string, expectedSize> or std::array<std::string_view, expectedSize> array of strings. Size of array is known at compile time.
/// @throws std::runtime_error if bytes is empty or when bytes does not consist at least expectedSize numer of space separated strings.
template<size_t expectedSize, bool copy = true, bool modifyByteView = true>
std::array<std::conditional_t<copy, std::string, std::string_view>, expectedSize> ToStringArray()
{
auto splited = Split(*this, OBF(" "));
if (splited.size() < expectedSize)
throw std::runtime_error{ OBF("ByteVector does not consist expected number of strings") };
using ReturnType = decltype(ToStringArray<expectedSize, copy, modifyByteView>());
ReturnType retValue;
for (auto i = 0u; i < expectedSize; ++i)
retValue[i] = splited[i];
if constexpr (modifyByteView)
remove_prefix(splited[expectedSize - 1].data() - reinterpret_cast<const char*>(data()) + splited[expectedSize - 1].size());
return retValue;
}
private:
/// Delegate to class idiom.
/// Function templates cannot be partially specialized.
/// @tparam T. First type from parameter pack. Each recursive call will unfold one type.
/// @tparam Rest. Parameter pack storing rest of provided types.
template <typename T, typename ...Rest>
struct VariadicTupleGenerator
{
/// Function responsible for recursively packing data to tuple.
/// @param self. Reference to ByteView object using generate method.
static auto Generate(ByteView& self)
{
auto current = std::make_tuple(self.Read<T>());
auto rest = VariadicTupleGenerator<Rest...>::Generate(self);
return std::tuple_cat(std::move(current), std::move(rest));
}
};
/// Delegate to class idiom.
/// Function templates cannot be partially specialized.
/// Closure specialization.
/// @tparam T. Type to be extracted from ByteView and stored in tuple.
template <typename T>
struct VariadicTupleGenerator<T>
{
/// Function responsible for recursively packing data to tuple.
/// @param self. Reference to ByteView object using generate method.
static auto Generate(ByteView& self)
{
return std::make_tuple(self.Read<T>());
}
};
/// Delegate to class idiom.
/// Function templates cannot be partially specialized.
/// @tparam T. Tuple to be extracted.
/// @tparam N. How many elements out of tuple to use.
template <typename T, size_t N = std::tuple_size_v<T>>
struct TupleGenerator
{
/// Function responsible for recursively packing data to tuple.
/// @param self. Reference to ByteView object using generate method.
static auto Generate(ByteView& self)
{
auto current = std::make_tuple(self.Read<std::tuple_element_t<std::tuple_size_v<T> -N, T>>());
auto rest = TupleGenerator<T, N - 1>::Generate(self);
return std::tuple_cat(current, rest);
}
};
/// Delegate to class idiom.
/// Function templates cannot be partially specialized.
/// Closure specialization, return empty tuple.
/// @tparam T. Tuple to be extracted.
template <typename T>
struct TupleGenerator<T, 0>
{
/// Function responsible for recursively packing data to tuple.
/// @param self. Reference to ByteView object using generate method.
static auto Generate(ByteView& self)
{
return std::tuple<>{};
}
};
}; };
/// Specialization of idiom for detecting ByteView::Get. /// ByteConverter::From specialization for arithmetic types.
template<size_t N> /// This is a basic specialization and will not be moved to ByteConverter.h
constexpr bool IsGetter<ByteView::Get<N>> = true; template <typename T>
T ByteConverter<T, std::enable_if_t<Utils::IsOneOf<T, ByteVector, ByteView, std::string, std::string_view, std::wstring, std::wstring_view>::value>>::From(ByteView& bv)
{
if (sizeof(uint32_t) > bv.size())
throw std::out_of_range{ OBF(": Cannot read size from ByteView ") };
/// Alias for simpler use of ByteView::Get. auto elementCount = *reinterpret_cast<const uint32_t*>(bv.data());
template<size_t N> auto byteCount = elementCount * sizeof(T::value_type);
using Bytes = ByteView::Get<N>; bv.remove_prefix(sizeof(uint32_t));
T retVal;
if constexpr (Utils::IsOneOf<T, ByteVector, std::string, std::wstring>::value)
{
retVal.resize(elementCount);
std::memcpy(retVal.data(), bv.data(), byteCount);
}
else
{
retVal = T{ reinterpret_cast<typename T::value_type const*>(bv.data()), elementCount };
}
bv.remove_prefix(byteCount);
return retVal;
}
/// ByteConverter::from specialization for ByteVector, ByteView, std::string, std::string_view, std::wstring, std::wstring_view.
/// This is a basic specialization and will not be moved to ByteConverter.h
template <typename T>
T ByteConverter<T, std::enable_if_t<std::is_arithmetic_v<T>>>::From(ByteView& bv)
{
T ret;
memcpy(&ret, bv.data(), sizeof(T));
bv.remove_prefix(sizeof(T));
return ret;
}
namespace Literals namespace Literals
{ {
/// Create ByteView with syntax ""_bvec /// Create ByteView with syntax ""_bvec
MWR::ByteView operator "" _bv(const char* data, size_t size); inline MWR::ByteView operator "" _bv(const char* data, size_t size)
{
return { reinterpret_cast<const uint8_t*>(data), size };
}
/// Create ByteView with syntax L""_bvec /// Create ByteView with syntax L""_bvec
MWR::ByteView operator "" _bv(const wchar_t* data, size_t size); inline MWR::ByteView operator "" _bv(const wchar_t* data, size_t size)
} {
return { reinterpret_cast<const uint8_t*>(data), size * sizeof(wchar_t) };
/// Template function allowing structured binding to new std::string or std::string_view variables from text command stored in ByteView. }
///
/// auto [a,b] = ToStringArray<2>(someByteView);
/// @tparam expectedSize number of words that should be returned from function.
/// @tparam copy if true words will be copied to std::string variables.
/// @param bytes ByteView to parse. Strings should be separated with spaces.
/// @returns std::array<std::string, expectedSize> or std::array<std::string_view, expectedSize> array of strings. Size of array is known at compile time.
/// @throws std::runtime_error if bytes is empty or when bytes does not consist at least expectedSize numer of space separated strings.
template<size_t expectedSize, bool copy = true>
std::array<std::conditional_t<copy, std::string, std::string_view>, expectedSize> ToStringArray(ByteView bytes)
{
return bytes.ToStringArray<expectedSize, copy, false>();
} }
} }

View File

@ -14,10 +14,8 @@
#include "Common/ADVobfuscator/MetaString.h" #include "Common/ADVobfuscator/MetaString.h"
#include "Utils.h" //< For common functions. #include "ByteConverter.h" //< Bor ByteView, ByteVector and ByteConverter specializations for common types.
#include "ByteArray.h" //< For ByteArray.
#include "ByteVector.h" //< For ByteVector.
#include "ByteView.h" //< For ByteView.
// Literals. // Literals.

View File

@ -1,34 +0,0 @@
#include "StdAfx.h"
#include "Utils.h"
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool MWR::Utils::IsProcess64bit()
{
#if defined(_WIN64)
return true;
#elif defined(_WIN32)
return false;
#endif
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
std::string MWR::Utils::GenerateRandomString(size_t size)
{
static const std::string charset = OBF("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
static std::random_device rd;
static std::mt19937 gen(rd());
static std::uniform_int_distribution<int> uni(0, static_cast<int>(charset.size() - 1));
std::string randomString;
randomString.resize(size);
for (auto& e : randomString)
e = charset[uni(gen)];
return randomString;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int32_t MWR::Utils::TimeSinceEpoch()
{
return static_cast<int32_t>(std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()).time_since_epoch().count());
}

View File

@ -4,7 +4,21 @@ namespace MWR::Utils
{ {
/// Check if application is 64bit. /// Check if application is 64bit.
/// @return true for x64 process. /// @return true for x64 process.
bool IsProcess64bit(); inline bool IsProcess64bit()
{
# if defined(_WIN64)
return true;
# elif defined(_WIN32)
return false;
# endif
}
/// Template to evaluate if T is one of Ts types.
template <typename T, typename ...Ts>
struct IsOneOf
{
constexpr static bool value = [](bool ret) { return ret; }((std::is_same_v<T, Ts> || ...));
};
/// Changes value to default if it is out of provided range. /// Changes value to default if it is out of provided range.
/// @param value to be clamped. /// @param value to be clamped.
@ -24,7 +38,21 @@ namespace MWR::Utils
/// Generate random string. /// Generate random string.
/// @param size of returned string. /// @param size of returned string.
std::string GenerateRandomString(size_t size); template <typename T = std::string, std::enable_if_t<IsOneOf<T, std::string, std::wstring>::value, int> = 0>
T GenerateRandomString(size_t size)
{
constexpr std::string_view charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
static std::random_device rd;
static std::mt19937 gen(rd());
static std::uniform_int_distribution<int> uni(0, static_cast<int>(charset.size() - 1));
T randomString;
randomString.resize(size);
for (auto& e : randomString)
e = static_cast<T::value_type>(charset[uni(gen)]);
return randomString;
}
/// Generate random unsigned int value. /// Generate random unsigned int value.
/// @param rangeFrom minimal allowed value. /// @param rangeFrom minimal allowed value.
@ -67,7 +95,10 @@ namespace MWR::Utils
} }
/// Impersonation of Y2038 problem /// Impersonation of Y2038 problem
int32_t TimeSinceEpoch(); inline int32_t TimeSinceEpoch()
{
return static_cast<int32_t>(std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()).time_since_epoch().count());
}
/// Alias for float based seconds /// Alias for float based seconds
using FloatSeconds = std::chrono::duration<float, std::chrono::seconds::period>; using FloatSeconds = std::chrono::duration<float, std::chrono::seconds::period>;
@ -114,4 +145,83 @@ namespace MWR::Utils
/// HasFoo<Bar&, int, double>::value /// HasFoo<Bar&, int, double>::value
template<template<class...>class T, class...Ts> template<template<class...>class T, class...Ts>
using CanApply = Details::CanApply<T, void, Ts...>; using CanApply = Details::CanApply<T, void, Ts...>;
/// Function splitting string.
///
/// @tparam copy. Indicates should strings be copied.
/// @param stringToBeSplitted initial string to be tokenized.
/// @param delimiter to be searched in string.
/// @throws std::runtime_error if delimiter empty or greatter equal to stringToBeSplitted
/// @returns std::vector<std::string> or std::vector<std::string_view> depending on copy flag.
template<bool copy = false>
std::vector<std::conditional_t<copy, std::string, std::string_view>> Split(std::string_view stringToBeSplitted, std::string_view delimiter)
{
if (delimiter.empty() && delimiter.size() >= stringToBeSplitted.size())
throw std::runtime_error{ OBF("Delimiter size is incorrect") };
using ReturnType = decltype(Split<copy>(stringToBeSplitted, delimiter));
ReturnType splittedString;
// startIndex can overflow, endIndex is checked first, and lazy check on startIndex will avoid last empty token.
for (size_t startIndex = 0u, endIndex = 0u; endIndex < stringToBeSplitted.size() && startIndex < stringToBeSplitted.size(); startIndex = endIndex + delimiter.size())
{
endIndex = stringToBeSplitted.find(delimiter, startIndex);
// skip empty tokens when delimiter is repeated.
if (endIndex - startIndex)
splittedString.emplace_back(stringToBeSplitted.substr(startIndex, endIndex - startIndex));
}
return splittedString;
}
/// Proxy to Split<true>
///
/// @param stringToBeSplitted initial string to be tokenized.
/// @param delimiter to be searched in string.
/// @throws std::runtime_error if delimiter empty or greatter equal to stringToBeSplitted
/// @returns std::vector<std::string> tokenized string.
inline std::vector<std::string> SplitAndCopy(std::string_view stringToBeSplitted, std::string_view delimiter)
{
return Split<true>(stringToBeSplitted, delimiter);
}
/// Template function allowing structured binding to new std::string or std::string_view variables from text stored in std::string_view.
///
/// auto [a,b] = ToStringArray<2>(obj);
/// @tparam expectedSize number of words that should be returned from function.
/// @tparam copy if true words will be copied to std::string variables.
/// @returns std::array<std::string, expectedSize> or std::array<std::string_view, expectedSize> array of strings. Size of array is known at compile time.
/// @throws std::runtime_error if obj does not consist at least expectedSize numer of space separated strings.
template<size_t expectedSize, bool copy = true>
std::array<std::conditional_t<copy, std::string, std::string_view>, expectedSize> ToStringArray(std::string_view obj)
{
auto splited = Split(obj, OBF(" "));
if (splited.size() < expectedSize)
throw std::runtime_error{ OBF("ByteVector does not consist expected number of strings") };
using ReturnType = decltype(ToStringArray<expectedSize, copy, modifyByteView>(obj));
ReturnType retValue;
for (auto i = 0u; i < expectedSize; ++i)
retValue[i] = splited[i];
return retValue;
}
/// Idiom for detecting tuple types.
template <typename T>
constexpr bool IsTuple = false;
template<typename ...T>
constexpr bool IsTuple<std::tuple<T...>> = true;
/// Prevents compiler from optimizing out call.
inline void* SecureMemzero(void* ptr, size_t n)
{
if (ptr)
{
volatile char* p = reinterpret_cast<volatile char*>(ptr);
while (n--) *p++ = 0;
}
return ptr;
}
} }

View File

@ -336,7 +336,7 @@ void MWR::C3::Core::GateRelay::On(ProceduresS2G::InitializeRouteQuery&& query)
m_Profiler->Get().m_Gateway.ConditionalUpdateChannelParameters({ parentRid.GetAgentId(), childSideDid }); m_Profiler->Get().m_Gateway.ConditionalUpdateChannelParameters({ parentRid.GetAgentId(), childSideDid });
//send update message across route. //send update message across route.
auto&& packet = ProceduresG2X::AddRoute::Create(parentRid, m_Signature, childRid.ToByteVector().Concat(childSideDid)); auto&& packet = ProceduresG2X::AddRoute::Create(parentRid, m_Signature, ByteVector::Create(childRid,childSideDid));
LockAndSendPacket(packet->ComposeQueryPacket(), receivedFrom); LockAndSendPacket(packet->ComposeQueryPacket(), receivedFrom);
} }

View File

@ -325,7 +325,7 @@ namespace MWR::C3::Core
static std::unique_ptr<InitializeRouteQuery> Create(RouteId rid, int32_t timestamp, RouteId senderRid, DeviceId senderSideDid, ByteVector encryptedBlob, Crypto::PublicKey gatewayPublicEncryptionKey) static std::unique_ptr<InitializeRouteQuery> Create(RouteId rid, int32_t timestamp, RouteId senderRid, DeviceId senderSideDid, ByteVector encryptedBlob, Crypto::PublicKey gatewayPublicEncryptionKey)
{ {
auto query = std::make_unique<InitializeRouteQuery>(rid, timestamp, ResponseType::None); auto query = std::make_unique<InitializeRouteQuery>(rid, timestamp, ResponseType::None);
query->m_QueryPacketBody = Crypto::EncryptAnonymously(query->CompileQueryHeader().Concat(rid.ToByteVector(), timestamp, senderRid.ToByteVector(), senderSideDid.ToByteVector(), encryptedBlob), gatewayPublicEncryptionKey); query->m_QueryPacketBody = Crypto::EncryptAnonymously(query->CompileQueryHeader().Write(rid, timestamp, senderRid, senderSideDid).Concat(encryptedBlob), gatewayPublicEncryptionKey);
return query; return query;
} }
@ -360,7 +360,7 @@ namespace MWR::C3::Core
{ {
auto query = std::make_unique<AddDeviceResponse>(rid, timestamp, ResponseType::None); auto query = std::make_unique<AddDeviceResponse>(rid, timestamp, ResponseType::None);
std::uint8_t flags = static_cast<std::uint8_t>(isChannel) | (static_cast<std::uint8_t>(isNegotiationChannel) << 1); std::uint8_t flags = static_cast<std::uint8_t>(isChannel) | (static_cast<std::uint8_t>(isNegotiationChannel) << 1);
query->m_QueryPacketBody = Crypto::EncryptAnonymously(query->CompileQueryHeader().Concat(rid.ToByteArray(), timestamp, newDeviceId.ToByteVector(), deviceTypeHash, flags), gatewayPublicEncryptionKey); query->m_QueryPacketBody = Crypto::EncryptAnonymously(query->CompileQueryHeader().Write(rid, timestamp, newDeviceId, deviceTypeHash, flags), gatewayPublicEncryptionKey);
return query; return query;
} }
@ -382,7 +382,7 @@ namespace MWR::C3::Core
static std::unique_ptr<DeliverToBinder> Create(RouteId rid, int32_t timestamp, DeviceId peripheralId, HashT connectorHash, ByteView blobFromPeripheral, Crypto::PublicKey gatewayPublicEncryptionKey) static std::unique_ptr<DeliverToBinder> Create(RouteId rid, int32_t timestamp, DeviceId peripheralId, HashT connectorHash, ByteView blobFromPeripheral, Crypto::PublicKey gatewayPublicEncryptionKey)
{ {
auto query = std::make_unique<DeliverToBinder>(rid, timestamp, ResponseType::None); auto query = std::make_unique<DeliverToBinder>(rid, timestamp, ResponseType::None);
query->m_QueryPacketBody = Crypto::EncryptAnonymously(query->CompileQueryHeader().Concat(rid.ToByteArray(), timestamp, peripheralId.ToByteVector(), connectorHash, blobFromPeripheral), gatewayPublicEncryptionKey); query->m_QueryPacketBody = Crypto::EncryptAnonymously(query->CompileQueryHeader().Write(rid, timestamp, peripheralId, connectorHash).Concat(blobFromPeripheral), gatewayPublicEncryptionKey);
return query; return query;
} }
@ -416,19 +416,7 @@ namespace MWR::C3::Core
static std::unique_ptr<NewNegotiatedChannelNotification> Create(RouteId rid, int32_t timestamp, DeviceId newDeviceId, DeviceId negotiatiorId, ByteView inId, ByteView outId, Crypto::PublicKey gatewayPublicEncryptionKey) static std::unique_ptr<NewNegotiatedChannelNotification> Create(RouteId rid, int32_t timestamp, DeviceId newDeviceId, DeviceId negotiatiorId, ByteView inId, ByteView outId, Crypto::PublicKey gatewayPublicEncryptionKey)
{ {
auto query = std::make_unique<NewNegotiatedChannelNotification>(rid, timestamp, ResponseType::None); auto query = std::make_unique<NewNegotiatedChannelNotification>(rid, timestamp, ResponseType::None);
query->m_QueryPacketBody = Crypto::EncryptAnonymously query->m_QueryPacketBody = Crypto::EncryptAnonymously(query->CompileQueryHeader().Write(rid, timestamp, newDeviceId, negotiatiorId, inId, outId), gatewayPublicEncryptionKey);
(
query->CompileQueryHeader()
.Concat(rid.ToByteArray())
.Write
(
timestamp
, newDeviceId.ToUnderlyingType()
, negotiatiorId.ToUnderlyingType()
, inId
, outId
)
, gatewayPublicEncryptionKey);
return query; return query;
} }
@ -449,16 +437,7 @@ namespace MWR::C3::Core
static std::unique_ptr<Notification> Create(RouteId rid, int32_t timestamp, MWR::ByteView blob, Crypto::PublicKey gatewayPublicEncryptionKey) static std::unique_ptr<Notification> Create(RouteId rid, int32_t timestamp, MWR::ByteView blob, Crypto::PublicKey gatewayPublicEncryptionKey)
{ {
auto query = std::make_unique<Notification>(rid, timestamp, ResponseType::None); auto query = std::make_unique<Notification>(rid, timestamp, ResponseType::None);
query->m_QueryPacketBody = Crypto::EncryptAnonymously query->m_QueryPacketBody = Crypto::EncryptAnonymously(query->CompileQueryHeader().Write(rid, timestamp, blob), gatewayPublicEncryptionKey);
(
query->CompileQueryHeader()
.Concat(rid.ToByteArray())
.Write
(
timestamp
, blob
)
, gatewayPublicEncryptionKey);
return query; return query;
} }

View File

@ -104,7 +104,7 @@ uint32_t MWR::C3::Core::Profiler::GetBinderTo(uint32_t id)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MWR::ByteVector MWR::C3::Core::Profiler::TranslateCommand(json const& command) MWR::ByteVector MWR::C3::Core::Profiler::TranslateCommand(json const& command)
{ {
return ByteVector{}.Concat(command.at("id").get<uint16_t>(), TranslateArguments(command.at("arguments"))); return ByteVector::Create(command.at("id").get<uint16_t>()).Concat(TranslateArguments(command.at("arguments")));
} }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -135,7 +135,7 @@ MWR::ByteVector MWR::C3::Core::Profiler::TranslateStartupCommand(json const& jco
if (command == createCommands.cend() || !command->m_IsDevice) if (command == createCommands.cend() || !command->m_IsDevice)
throw std::logic_error{ "Failed to find a create command" }; throw std::logic_error{ "Failed to find a create command" };
return ByteVector{}.Concat(command->m_IsNegotiableChannel, command->m_Hash, TranslateArguments(jcommand.at("arguments"))); return ByteVector::Create(command->m_IsNegotiableChannel, command->m_Hash).Concat(TranslateArguments(jcommand.at("arguments")));
} }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -713,7 +713,7 @@ void MWR::C3::Core::Profiler::Agent::PerformCreateCommand(json const& jCommandEl
// it is a create command // it is a create command
DeviceId newDeviceId = ++m_LastDeviceId; DeviceId newDeviceId = ++m_LastDeviceId;
ByteVector repacked; ByteVector repacked;
repacked.Concat(static_cast<std::underlying_type_t<Command>>(Command::AddDevice), newDeviceId.ToByteVector(), command->m_IsNegotiableChannel, command->m_Hash); repacked.Write(Command::AddDevice, newDeviceId, command->m_IsNegotiableChannel, command->m_Hash);
if (auto binder = profiler->GetBinderTo(command->m_Hash); binder && command->m_IsDevice) // peripheral, check if payload is needed. if (auto binder = profiler->GetBinderTo(command->m_Hash); binder && command->m_IsDevice) // peripheral, check if payload is needed.
{ {
auto connector = profiler->m_Gateway->m_Gateway.lock()->GetConnector(binder); auto connector = profiler->m_Gateway->m_Gateway.lock()->GetConnector(binder);