Merge branch 'ByteConverterUpdate' into 'master'

Use improved, header only ByteView/ByteVector

See merge request C3/C3!147
dependabot/npm_and_yarn/Src/WebController/UI/websocket-extensions-0.1.4
Janusz Szmigielski 2019-12-20 12:56:57 +00:00
commit 9f304a7b52
14 changed files with 860 additions and 916 deletions

View File

@ -22,10 +22,7 @@
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Interfaces\Peripherals\Beacon.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\C3\Internals\Interface.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\Utils.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Crypto\Sodium.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Slack\SlackApi.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Sockets\AddrInfo.cpp" />
@ -64,6 +61,7 @@
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\PrecompiledHeader.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\C3\Sdk.hpp" />
<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\Encryption.h" />
@ -72,8 +70,8 @@
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\PrecompiledHeader.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\SafeSmartPointerContainer.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\SecureString.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\XError.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\Base32.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\Internals\Interface.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\Utils.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Crypto\Sodium.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)MWR\Sockets\AddrInfo.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\PrecompiledHeader.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\Hash.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\Payload.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\CppTools\PrecompiledHeader.hpp" />
<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\Crypto\Base32.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\Crypto\Base64.h" />
@ -88,5 +81,10 @@
<ClInclude Include="$(MSBuildThisFileDirectory)MWR\WinTools\StructuredExceptionHandling.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)C3_BUILD_VERSION_HASH_PART.hxx" />
<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>
</Project>

View File

@ -2,7 +2,6 @@
namespace MWR
{
// TODO own implementation would allow easier casting. Implicit cast to ByteView, and then to desired type is forbidden.
/// Owning container with size known at compilation time.
template <size_t N>
using ByteArray = std::array<uint8_t, N>;

View File

@ -0,0 +1,415 @@
#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 enum.
template <typename T>
struct ByteConverter<T, std::enable_if_t<std::is_enum_v<T>>>
{
/// Serialize enum type to ByteVector.
/// @param enumInstance. Object to be serialized.
/// @return ByteVector. Serialized data.
static ByteVector To(T enumInstance)
{
return ByteVector::Create(static_cast<std::underlying_type_t<T>>(enumInstance));
}
/// Get size required after serialization.
/// @return size_t. Number of bytes used after serialization.
constexpr static size_t Size()
{
return sizeof(std::underlying_type_t<T>);
}
/// Deserialize from ByteView.
/// @param bv. Buffer with serialized data.
/// @return enum.
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>>
{
/// Serialize pair type to ByteVector.
/// @param pairInstance. Object to be serialized.
/// @return ByteVector. Serialized data.
static ByteVector To(std::pair<T1, T2> const& pairInstance)
{
return ByteVector::Create(pairInstance.first, pairInstance.second);
}
/// Get size required after serialization.
/// @param pairInstance. Instance for which size should be found.
/// @return size_t. Number of bytes used after serialization.
static size_t Size(std::pair<T1, T2> const& pairInstance)
{
return ByteVector::Size(pairInstance.first) + ByteVector::Size(pairInstance.second);
}
/// Deserialize from ByteView.
/// @param bv. Buffer with serialized data.
/// @return std::pair.
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>
{
/// Serialize path type to ByteVector.
/// @param pathInstance. Object to be serialized.
/// @return ByteVector. Serialized data.
static ByteVector To(std::filesystem::path const& pathInstance)
{
return ByteVector::Create(pathInstance.wstring());
}
/// Get size required after serialization.
/// @param pathInstance. Instance for which size should be found.
/// @return size_t. Number of bytes used after serialization.
static size_t Size(std::filesystem::path const& pathInstance)
{
return ByteVector::Size(pathInstance.wstring());
}
/// Deserialize from ByteView.
/// @param bv. Buffer with serialized data.
/// @return std::filesystem::path.
static std::filesystem::path From(ByteView& bv)
{
return { bv.Read<std::wstring>() };
}
};
/// ByteConverter specialization for std::byte.
template <>
struct ByteConverter<std::byte>
{
/// Serialize byte type to ByteVector.
/// @param byteInstance. Object to be serialized.
/// @return ByteVector. Serialized data.
static ByteVector To(std::byte byteInstance)
{
return ByteVector::Create(static_cast<unsigned char>(byteInstance));
}
/// Get size required after serialization.
/// @return size_t. Number of bytes used after serialization.
constexpr static size_t Size()
{
return sizeof(unsigned char);
}
/// Deserialize from ByteView.
/// @param bv. Buffer with serialized data.
/// @return std::byte.
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:
/// @code someByteViewObject.Read<int, int, Bytes<7>, std::string> @endCode
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>>
{
/// Retrieve ByteView substring with requested size.
/// @param bv. Buffer with serialized data. Will be moved by N bytes.
/// @return ByteView new view with size equal to 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>>
{
/// Serialize vector type to ByteVector.
/// @param vectorInstance. Object to be serialized.
/// @return ByteVector. Serialized data.
static ByteVector To(std::vector<T> const& vectorInstance)
{
ByteVector ret;
ret.reserve(ByteVector::Size(vectorInstance));
ret.Write(static_cast<uint32_t>(vectorInstance.size()));
for (auto const& e : vectorInstance)
ret.Concat(ByteVector::Create(e));
return ret;
}
/// Get size required after serialization.
/// @param vectorInstance. Instance for which size should be found.
/// @return size_t. Number of bytes used after serialization.
static size_t Size(std::vector<T> const& vectorInstance)
{
size_t size = sizeof(uint32_t); //four bytes for vector size.
for (auto const& e : vectorInstance)
size += ByteVector::Size(e);
return size;
}
/// Deserialize from ByteView.
/// @param bv. Buffer with serialized data.
/// @return std::vector.
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>>
{
/// Serialize map type to ByteVector.
/// @param mapInstance. Object to be serialized.
/// @return ByteVector. Serialized data.
static ByteVector To(std::map<T1, T2> const& mapInstance)
{
ByteVector ret;
ret.reserve(ByteVector::Size(mapInstance));
ret.Write(static_cast<uint32_t>(mapInstance.size()));
for (auto const& [key, val] : mapInstance)
ret.Concat(ByteVector::Create(key, val));
return ret;
}
/// Get size required after serialization.
/// @param mapInstance. Instance for which size should be found.
/// @return size_t. Number of bytes used after serialization.
static size_t Size(std::map<T1, T2> const& mapInstance)
{
size_t size = sizeof(uint32_t); //four bytes for map size.
for (auto const& [key, val] : mapInstance)
size += ByteVector::Size(key) + ByteVector::Size(val);
return size;
}
/// Deserialize from ByteView.
/// @param bv. Buffer with serialized data.
/// @return std::map.
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>>
{
/// Serialize array type to ByteVector.
/// @param arrayInstance. Object to be serialized.
/// @return ByteVector. Serialized data.
static ByteVector To(std::array<T, N> const& arrayInstance)
{
ByteVector ret;
ret.reserve(ByteVector::Size(arrayInstance));
for (auto const& e : arrayInstance)
ret.Write(e);
return ret;
}
/// Get size required after serialization.
/// @param arrayInstance. Instance for which size should be found.
/// @return size_t. Number of bytes used after serialization.
static size_t Size(std::array<T, N> const& arrayInstance)
{
auto ret = size_t{ 0 };
if constexpr (std::is_arithmetic_v<T>)
{
static_cast<void>(arrayInstance);
ret = sizeof(T) * N; // avoid extra calls when size of array is known.
}
else
{
for (auto const& e : arrayInstance)
ret += ByteVector::Size(e);
}
return ret;
}
/// Deserialize from ByteView.
/// @param bv. Buffer with serialized data.
/// @return std::array.
static std::array<T, N> From(ByteView& bv)
{
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:
/// Create array with size N from provided vector.
/// This helper function is required because T might not be default constructible, but array must be filled like aggregator.
/// Reading data to vector first will ensure order of elements, independent of calling convention.
/// @param vec. Temporary vector with data.
/// @returns std::array with all elements.
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>>>
{
/// Serialize tuple type to ByteVector.
/// @param tupleInstance. Object to be serialized.
/// @return ByteVector. Serialized data.
static ByteVector To(T const& tupleInstance)
{
ByteVector ret;
ret.reserve(Size(tupleInstance));
TupleHandler<T>::Write(ret, tupleInstance);
return ret;
}
/// Get size required after serialization.
/// @param tupleInstance. Instance for which size should be found.
/// @return size_t. Number of bytes used after serialization.
static size_t Size(T const& tupleInstance)
{
return TupleHandler<T>::Size(tupleInstance);
}
/// Deserialize from ByteView.
/// @param bv. Buffer with serialized data.
/// @return std::tuple.
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 TupleHandler.
/// @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.
/// @param t. reference to tuple.
/// @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
#include "ByteArray.h"
#include "Utils.h"
namespace MWR
{
@ -9,77 +10,26 @@ namespace MWR
class ByteVector;
/// 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.
/// Specialize this class to add methods:
/// 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>
/// Look for example in ByteConventer.h
template <typename T, typename = void>
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.
/// This class performs test that looks for implementation of Size function in ByteConverter<T>.
/// Depending on test result 'value' is set to one of:
/// absent - No implementation is available for Size function. Size can only be determined after serialization.
/// compileTime - Size is determined by type, not object instance. Size() is constexpr function.
/// runTime - Size depends on object instance. Size(T const&) can be used before serialization
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 uint16_t test(...);
template <typename C> static uint32_t test(decltype(MWR::ByteConverter<T>::Size(std::declval<C>())));
template <typename C> static uint16_t test(decltype(MWR::ByteConverter<T>::Size(std::void_t<C>)));
template <typename C> static uint8_t test(...);
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.
@ -92,28 +42,54 @@ namespace MWR
/// Type of stored values.
using ValueType = Super::value_type;
#if defined BYTEVECTOR_ZERO_MEMORY_DESTRUCTION
/// Destructor zeroing memory.
~ByteVector();
~ByteVector()
{
Utils::SecureMemzero(data(), size());
}
#endif
/// Copy constructor.
/// @param other. Object to copy.
ByteVector(ByteVector const& other);
ByteVector(ByteVector const& other)
: Super(static_cast<Super const&>(other))
{
}
/// Move constructor.
/// @param other. Object to move.
ByteVector(ByteVector&& other);
ByteVector(ByteVector&& other)
: Super(static_cast<Super&&>(other))
{
}
/// Copy assignment operator.
/// @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.
/// @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.
/// @param other. Object to copy.
ByteVector(std::vector<uint8_t> other);
ByteVector(std::vector<uint8_t> other)
: Super(std::move(other))
{
}
// Enable methods.
using Super::vector;
@ -161,59 +137,65 @@ namespace MWR
using Super::resize;
/// 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.
/// Writes 4 Bytes header with size for types that have variable buffer length.
/// @tparam T. Types to be stored.
/// Suports arithmetic types, std::string, std::wstring, std::string_view, std::wstring_view, ByteVector and ByteView.
/// Include ByteConverter.h to add support for common types like enum, std::vector, std:map, std::pair, std::tuple and others.
/// Create specialization on ByteConverter for custom types or template types to expand existing serialization functionality.
/// @param arg. Object to be stored.
/// @param args. Optional other objects to be stored.
/// @return itself to allow chaining.
template <typename ...T, typename std::enable_if_t<CanStoreType<T...>::value, int> = 0>
ByteVector & Write(T const& ...args)
template <typename T, typename ...Ts>
ByteVector& Write(T const& arg, Ts const& ...args)
{
reserve(size() + CalculateStoreSize<T...>(true, args...));
Store<T...>(true, args...);
reserve(size() + Size<T, Ts...>(arg, args...));
Store<T, Ts...>(arg, args...);
return *this;
}
/// 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.
/// 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>>.
/// @tparam T. Types to be stored.
/// Supports ByteView and ByteVector.
/// Does not write header with size.
/// @param arg. Object to be stored.
/// @param args. Optional other objects to be stored.
/// @return itself to allow chaining.
template <typename ...T, typename std::enable_if_t<CanStoreType<T...>::value, int> = 0>
ByteVector & Concat(T const& ...args)
template <typename T, typename ...Ts, typename = std::enable_if_t<Utils::IsOneOf<T, ByteView, ByteVector>::value>>
ByteVector& Concat(T const& arg, Ts const& ...args)
{
reserve(size() + CalculateStoreSize<T...>(false, args...));
Store<T...>(false, args...);
if constexpr (!sizeof...(Ts))
{
auto oldSize = size();
resize(oldSize + arg.size());
memcpy(data() + oldSize, arg.data(), arg.size());
}
else
{
Concat(arg);
Concat(args...);
}
return *this;
}
/// 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 and ByteVector::Concat for more informations.
template <bool preferWriteOverConcat = true, typename ...T, typename std::enable_if_t<CanStoreType<T...>::value, int> = 0>
static ByteVector Create(T const& ...args)
{
if constexpr (preferWriteOverConcat)
return CreateByWrite(args...);
else
return CreateByConcat(args...);
}
// 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.
/// This function cannot be constructor, becouse it would be ambigious with std::vector costructors.
/// This function cannot be constructor, because it would be ambiguous with super class constructors.
/// @param arg. Object to be stored.
/// @param args. Optional other objects to be stored.
/// @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)
template <typename T, typename ...Ts>
static ByteVector Create(T const& arg, Ts const& ...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...);
return ByteVector{}.Write(arg, args...);
}
/// Proxy function to ByteView::Read.
@ -224,305 +206,143 @@ namespace MWR
return ByteView{ *this }.Read<T...>();
}
private:
/// Store content of of provided object.
/// @param storeSize. 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 itself to allow chaining.
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>
ByteVector & Store(bool storeSize, T const& arg)
/// Calculate the size that the argument will take in memory
/// @param arg. Argument to be stored.
/// @param args. Rest of types that will be handled with recursion.
/// @return size_t number of bytes needed.
template<typename T, typename ...Ts>
static size_t Size(T const& arg, Ts const& ...args)
{
auto oldSize = size();
if (storeSize)
if constexpr (!sizeof...(Ts))
{
resize(oldSize + sizeof(uint32_t) + arg.size() * sizeof(T::value_type));
*reinterpret_cast<uint32_t*>(data() + oldSize) = static_cast<uint32_t>(arg.size());
std::memcpy(data() + oldSize + sizeof(uint32_t), arg.data(), arg.size() * sizeof(T::value_type));
// There is only one type to calculate size for. Find implementation of Size for that type.
if constexpr (ByteSizeFunctionType<T>::value == ByteSizeFunctionType<T>::compileTime)
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
{
resize(oldSize + arg.size() * sizeof(T::value_type));
std::memcpy(data() + oldSize, arg.data(), arg.size() * sizeof(T::value_type));
return Size(arg) + Size(args...);
}
}
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;
}
};
/// Store content of array. Size must be known at compile time and will not be saved in data.
/// @tparam T. Type to be stored. Supported types are ByteArray.
/// @return itself to allow chaining.
template<typename T, typename std::enable_if_t<IsByteArray<T>, int> = 0>
ByteVector & Store(bool unused, T const& arg)
/// ByteConverter specialization for ByteVector, ByteView, std::string, std::string_view, std::wstring, std::wstring_view.
/// This is a basic functionality that should be available with ByteVector. This specialization will not be moved to ByteConverter.h.
template <typename T>
struct ByteConverter<T, std::enable_if_t<Utils::IsOneOf<T, ByteVector, ByteView, std::string, std::string_view, std::wstring, std::wstring_view>::value>>
{
static ByteVector To(T const& obj)
{
auto oldSize = size();
resize(oldSize + arg.size());
std::memcpy(data() + oldSize, arg.data(), arg.size());
return *this;
auto ret = ByteVector{};
auto elementSize = static_cast<uint32_t>(obj.size());
ret.resize(Size(obj));
memcpy(ret.data(), &elementSize, sizeof(elementSize));
memcpy(ret.data() + sizeof(elementSize), obj.data(), elementSize * sizeof(T::value_type));
return ret;
}
/// Store arithmetic type.
/// 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)
static size_t Size(T const& obj)
{
auto oldSize = size();
resize(oldSize + sizeof(T));
*reinterpret_cast<T*>(data() + oldSize) = arg;
return *this;
return sizeof(uint32_t) + (obj.size() * sizeof(T::value_type));
}
/// Store custom type.
/// tparam T. Type to be stored. There must exsist MWR::ToBytes(T const&) method avalible to store custom type.
/// @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)
// Reading functions are part of ByteView implementation. To deserialize data include ByteView.h
static T From(ByteView& bv);
};
/// ByteConverter specialization for arithmetic types.
/// This is a basic functionality that should be available with ByteVector. This specialization 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));
return *this;
auto ret = ByteVector{};
ret.resize(sizeof(T));
*reinterpret_cast<T*>(ret.data()) = obj;
return ret;
}
/// Store all arguments at the end of ByteVector.
/// @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)
constexpr static size_t Size()
{
return sizeof(T);
}
/// 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&) function and MWR::ByteConverter<T>::Size(T const&)
/// @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;
}
};
// Reading functions are part of ByteView implementation. To deserialize data include ByteView.h
static T From(ByteView& bv);
};
/// 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
{
/// 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
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
{
/// 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
{
/// 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.
class ByteView : std::basic_string_view<ByteVector::value_type>
{
@ -59,19 +19,37 @@ namespace MWR
/// @param offset. Offset from first byte of data collection.
/// @return ByteView. View of data.
/// @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.
/// @param begin. Iterator to first element of data.
/// @param end. Iterator to past the last element of data.
/// @return ByteView. View of data.
/// @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>.
/// @param data. Data from which view will be constructed.
/// @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.
/// @param data. Data from which view will be constructed.
@ -86,25 +64,43 @@ namespace MWR
/// Create from std::string_view.
/// @param data. Data from which view will be constructed.
/// @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.
operator Super() const noexcept;
operator Super() const noexcept
{
return Super{ data(), size() };
}
/// Allow cast to ByteVector.
operator ByteVector() const;
operator ByteVector() const
{
return { begin(), end() };
}
/// 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.
operator std::string_view() const;
operator std::string_view() const
{
return { reinterpret_cast<const char*>(data()), size() };
}
/// Create a sub-string from this ByteView.
/// @param offset. Position of the first byte.
/// @param count. Requested length
/// @returns ByteView. View of the substring
ByteView SubString(const size_type offset = 0, size_type count = npos) const;
/// @param offset. Position of the first byte.
/// @param count. Requested length
/// @returns ByteView. View of the substring
ByteView SubString(const size_type offset = 0, size_type count = npos) const
{
return Super::substr(offset, count);
}
// Enable methods.
using std::basic_string_view<ByteVector::value_type>::basic_string_view;
@ -140,263 +136,100 @@ namespace MWR
using Super::npos;
using Super::value_type;
/// @returns ByteVector. Owning container with the read bytes.
/// Read bytes and move ByteView to position after parsed data.
/// @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>
/// @returns ByteVector. Owning container with the read bytes.
/// @throws std::out_of_range. If byteCount > size().
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()
ByteVector Read(size_t byteCount)
{
if (sizeof(uint32_t) > size())
throw std::out_of_range{ OBF(": Cannot read size from ByteView ") };
if (byteCount > size())
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 byteCount = elementCount * sizeof(T::value_type);
remove_prefix(sizeof(uint32_t));
T retVal;
retVal.resize(elementCount);
std::memcpy(retVal.data(), data(), byteCount);
auto retVal = ByteVector{ begin(), begin() + byteCount };
remove_prefix(byteCount);
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.
/// @tparam T. Type to return;
/// @returns T.
template<typename T>
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>
/// Read object and move ByteView to position after parsed data.
/// @tparam T. Mandatory type to be retrieved from ByteView.
/// @tparam Ts. Optional types to be retrieved in one call.
/// @note Suports arithmetic types, std::string, std::wstring, std::string_view, std::wstring_view, ByteVector and ByteView.
/// Include ByteConverter.h to add support for common types like enum, std::vector, std:map, std::pair, std::tuple and others.
/// Create specialization on ByteConverter for custom types or template types to expand existing functionality.
/// @returns one type if Ts was empty, std::tuple with all types otherwise.
/// Simple usage:
/// @code auto [a, b, c] = someByteView.Read<int, float, std::string>(); @endcode
/// @note Returned types does not have to match exactly with Read template parameters list.
/// It is possible to create tags that will be used to retrieve different type.
template<typename T, typename ...Ts, typename = decltype(MWR::ByteConverter<std::remove_const_t<T>>::From(std::declval<ByteView>()))>
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.
template<size_t N>
constexpr bool IsGetter<ByteView::Get<N>> = true;
/// ByteConverter::From specialization for arithmetic types.
/// This is a basic functionality that should be available with ByteView. This specialization will not be moved to ByteConverter.h.
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.
template<size_t N>
using Bytes = ByteView::Get<N>;
auto elementCount = *reinterpret_cast<const uint32_t*>(bv.data());
auto byteCount = elementCount * sizeof(T::value_type);
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 functionality that should be available with ByteView. This specialization 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
{
/// 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
MWR::ByteView operator "" _bv(const wchar_t* data, size_t size);
}
/// 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>();
inline MWR::ByteView operator "" _bv(const wchar_t* data, size_t size)
{
return { reinterpret_cast<const uint8_t*>(data), size * sizeof(wchar_t) };
}
}
}

View File

@ -14,10 +14,9 @@
#include "Common/ADVobfuscator/MetaString.h"
#include "Utils.h" //< For common functions.
#include "ByteArray.h" //< For ByteArray.
#include "ByteVector.h" //< For ByteVector.
#include "ByteView.h" //< For ByteView.
#define BYTEVECTOR_ZERO_MEMORY_DESTRUCTION //< Increase OpSec by clearing memory when ByteVector is destructed.
#include "ByteConverter.h" //< For ByteView, ByteVector and ByteConverter specializations for common types.
// 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.
/// @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.
/// @param value to be clamped.
@ -24,7 +38,21 @@ namespace MWR::Utils
/// Generate random 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.
/// @param rangeFrom minimal allowed value.
@ -67,7 +95,10 @@ namespace MWR::Utils
}
/// 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
using FloatSeconds = std::chrono::duration<float, std::chrono::seconds::period>;
@ -114,4 +145,80 @@ namespace MWR::Utils
/// HasFoo<Bar&, int, double>::value
template<template<class...>class T, class...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.
/// @param ptr pointer to memory to be cleared.
/// @param n number of bytes to overwrite.
inline void* SecureMemzero(void* ptr, size_t n)
{
if (ptr) for (auto p = reinterpret_cast<volatile char*>(ptr); n--; *p++ = 0);
return ptr;
}
}

View File

@ -340,7 +340,7 @@ void MWR::C3::Core::GateRelay::On(ProceduresS2G::InitializeRouteQuery&& query)
m_Profiler->Get().m_Gateway.ConditionalUpdateChannelParameters({ parentRid.GetAgentId(), childSideDid });
//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);
}

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)
{
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;
}
@ -360,7 +360,7 @@ namespace MWR::C3::Core
{
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);
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;
}
@ -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)
{
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;
}
@ -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)
{
auto query = std::make_unique<NewNegotiatedChannelNotification>(rid, timestamp, ResponseType::None);
query->m_QueryPacketBody = Crypto::EncryptAnonymously
(
query->CompileQueryHeader()
.Concat(rid.ToByteArray())
.Write
(
timestamp
, newDeviceId.ToUnderlyingType()
, negotiatiorId.ToUnderlyingType()
, inId
, outId
)
, gatewayPublicEncryptionKey);
query->m_QueryPacketBody = Crypto::EncryptAnonymously(query->CompileQueryHeader().Write(rid, timestamp, newDeviceId, negotiatiorId, inId, outId), gatewayPublicEncryptionKey);
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)
{
auto query = std::make_unique<Notification>(rid, timestamp, ResponseType::None);
query->m_QueryPacketBody = Crypto::EncryptAnonymously
(
query->CompileQueryHeader()
.Concat(rid.ToByteArray())
.Write
(
timestamp
, blob
)
, gatewayPublicEncryptionKey);
query->m_QueryPacketBody = Crypto::EncryptAnonymously(query->CompileQueryHeader().Write(rid, timestamp, blob), gatewayPublicEncryptionKey);
return query;
}

View File

@ -86,7 +86,7 @@ uint32_t MWR::C3::Core::Profiler::GetBinderTo(uint32_t id)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
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")));
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -117,7 +117,7 @@ MWR::ByteVector MWR::C3::Core::Profiler::TranslateStartupCommand(json const& jco
if (command == createCommands.cend() || !command->m_IsDevice)
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")));
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -692,7 +692,7 @@ void MWR::C3::Core::Profiler::Agent::PerformCreateCommand(json const& jCommandEl
// it is a create command
DeviceId newDeviceId = ++m_LastDeviceId;
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.
{
auto connector = profiler->m_Gateway->m_Gateway.lock()->GetConnector(binder);