14 Commits

Author SHA1 Message Date
Mindaugas Vinkelis
9bebfd4911 prepare for 5.2.5 2025-10-08 21:21:38 +03:00
Mindaugas Vinkelis
5e7ecede9b cast from polymorphic owner pointer-like type to observer during assignment 2025-10-08 20:36:15 +03:00
Mindaugas Vinkelis
ff841d63f6 check type information before assigning to observer pointer 2025-10-08 13:13:20 +03:00
Mindaugas Vinkelis
66d16516e2 Fix shared polymorphic poiner like type serialization/deserialization 2025-10-02 14:20:33 +03:00
Mindaugas Vinkelis
7ea1da0d48 clang-format using ./format.sh 2025-09-09 17:38:23 +03:00
Jules
8bda82576e Fix spelling error of "likely" 2025-02-09 15:21:02 +02:00
Mindaugas Vinkelis
ee992d8b57 changelog update for 5.2.4 2024-07-29 22:34:06 +03:00
anton-kl
4dcdd594da Fix typo in a comment 2024-07-21 11:00:53 +03:00
Pol Marcet Sardà
b714459a2b Implement brief syntax for optional and bitset 2024-07-21 11:00:13 +03:00
Pol Marcet Sardà
be2f295310 Discourage inlining the resize case, as that should happen rather rarely.
If you additionally reuse buffers, this will increase the performance
further.

The metrics based on some internal benchmarks show a speedup of about 1%:

bitsery <dont inline bad cases>
  BigString: 222092ns
  ComplexLittleObjects: 34115ns
  ComplexLittleObjectsBig: 6222801ns

bitsery <original>
  BigString: 242853ns
  ComplexLittleObjects: 35738ns
  ComplexLittleObjectsBig: 6334747ns

The assembly has been checked to be correct: https://godbolt.org/z/Wr7qvfGrK

Additionally, when allocating code, we can tell the compiler that we are
not resizing to a lower size, this saves on gcc 14 a few instructions.
The improvement should be negligible.
2024-07-21 10:58:53 +03:00
Pol Marcet Sardà
cd73aca2f5 Fix compilation errors in clang 17 2024-07-21 10:58:53 +03:00
NBurley93
94f7adaf6c FIX: Build error on GCC 13.x (#106)
Refer to https://gcc.gnu.org/gcc-13/porting_to.html for details on why change was needed
2023-06-06 10:09:26 +03:00
SoftdriveFelix
ceeb189c8b Changes the code path for arrays of bytes in big (#105)
Thanks for PR!
2023-05-09 08:47:18 +03:00
Mindaugas Vinkelis
90243480ec use latests compilers from ubuntu to run tests 2023-05-09 08:40:14 +03:00
42 changed files with 807 additions and 426 deletions

8
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
---
github: fraillt
buy_me_a_coffee: fraillt
custom:
- "https://www.paypal.com/paypalme/fraillt"
- "https://explorer.solana.com/address/5uHU32nBuniRxg6RZu4tsLWrXGFFz4pwMGHGuCLmkGJQ"
- "https://etherscan.io/address/0xe51cb417d1BFcd3EE4cfad9fa11b05631823AADb"
- "https://polygonscan.com/address/0xe51cb417d1BFcd3EE4cfad9fa11b05631823AADb"

View File

@@ -9,53 +9,37 @@ on:
jobs:
build:
name: ${{ matrix.config.name }}
runs-on: ubuntu-18.04
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
config:
- name: "Ubuntu 18.04 with Clang 3.9"
cxx_ver: 11
compiler: clang
compiler_ver: 3.9
- name: "Ubuntu 18.04 with GCC 5.0"
cxx_ver: 11
- name: "Ubuntu Latest with GCC 14"
compiler: gcc
compiler_ver: 5
- name: "Ubuntu 18.04 with GCC 11.0"
cxx_ver: 17
compiler: gcc
compiler_ver: 11
- name: "Ubuntu 18.04 with Clang 13"
cxx_ver: 17
compiler_ver: 14
- name: "Ubuntu Latests with Clang 18"
compiler: clang
compiler_ver: 13
compiler_ver: 18
steps:
- name: Prepare specific Clang version
if: ${{ matrix.config.compiler == 'clang' }}
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-add-repository "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-13 main"
sudo apt update
sudo apt install clang-${{ matrix.config.compiler_ver}}
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-${{ matrix.config.compiler_ver}} 100
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-${{ matrix.config.compiler_ver}} 100
- name: Prepare specific GCC version
if: ${{ matrix.config.compiler == 'gcc' }}
run: |
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt update
sudo apt install g++-${{ matrix.config.compiler_ver}}
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++-${{ matrix.config.compiler_ver}} 100
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-${{ matrix.config.compiler_ver}} 100
- name: Installing GTest
run: |
sudo add-apt-repository ppa:team-xbmc/ppa
sudo apt-get update
sudo apt-get install libgmock-dev
- uses: actions/checkout@v3
- name: Configure
run: cmake -S . -B build -DBITSERY_BUILD_TESTS=ON -DBITSERY_BUILD_EXAMPLES=ON -DCMAKE_CXX_STANDARD=${{ matrix.config.cxx_ver }}
run: cmake -S . -B build -DBITSERY_BUILD_TESTS=ON -DBITSERY_BUILD_EXAMPLES=ON
- name: Build
run: cmake --build build
- name: Run tests

View File

@@ -14,9 +14,9 @@ jobs:
run: |
git clone https://github.com/google/googletest.git
cd googletest
git checkout release-1.11.0
git checkout v1.14.0
cmake -S . -B build
cmake --build build --target install
sudo cmake --build build --target install
- uses: actions/checkout@v3
- name: Configure
run: cmake -S . -B build -DBITSERY_BUILD_TESTS=ON -DBITSERY_BUILD_EXAMPLES=ON -DCMAKE_CXX_STANDARD=17

View File

@@ -15,7 +15,7 @@ jobs:
run: |
git clone https://github.com/google/googletest.git
cd googletest
git checkout release-1.11.0
git checkout v1.14.0
cmake -S . -B build -Dgtest_force_shared_crt=ON
cmake --build build --config Release --target install
- uses: actions/checkout@v3

View File

@@ -1,3 +1,27 @@
# [5.2.5](https://github.com/fraillt/bitsery/compare/v5.2.4...v5.2.5) (2025-10-09)
### Bug fixes
* fix security issue during deserialization where a crafted payload could cause a shared pointer to be assigned to a different type. More information is [here](https://gist.github.com/TrebledJ/750abc64a826f19dd2d6774724629b71). (huge thanks to [Johnathan](https://github.com/TrebledJ))
* fix serialization of shared polymorphic pointer-like types by correctly identifying same object (e.g. the same object serialized through `Base` or `Derived` would otherwise have different pointer addresses).
* fix polymorphic type assignment to "observer" by adjusting pointer address.
* fix spelling of C++ "likely" attribute. #121 (thanks to [Jules](https://github.com/jules-ai))
### Other notes
* format code that was left unformatted in the previous version.
* remove broken patch for GCC 4.8.2 (CentOS 7).
# [5.2.4](https://github.com/fraillt/bitsery/compare/v5.2.3...v5.2.4) (2024-07-30)
### Improvements
* implement brief syntax for std::optional and std::bitset. #116 (thanks to [Destroyerrrocket](https://github.com/Destroyerrrocket))
* improve performance for buffer adapters. #118 (thanks to [Destroyerrrocket](https://github.com/Destroyerrrocket))
* check if should swap by taking into account actual type (in addition to configuration). #105 (thanks to [SoftdriveFelix](https://github.com/SoftdriveFelix))
* fix compile errors for latest compilers. #106 (thanks to [NBurley93](https://github.com/NBurley93))
### Other notes
* change cmake_minimum_required to 3.25.
* change compilers for ubuntu (gcc 14 and clang 18).
# [5.2.3](https://github.com/fraillt/bitsery/compare/v5.2.2...v5.2.3) (2022-12-01)
### Improvements

View File

@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.1)
cmake_minimum_required(VERSION 3.25)
project(bitsery
LANGUAGES CXX
VERSION 5.2.2)
VERSION 5.2.5)
#======== build options ===================================
option(BITSERY_BUILD_EXAMPLES "Build examples" OFF)

View File

@@ -102,9 +102,13 @@ Works with C++11 compiler, no additional dependencies, include `<bitsery/bitsery
Library is tested on all major compilers on Windows, Linux and macOS.
There is a patch that allows using bitsery with non-fully compatible C++11 compilers.
* CentOS 7 with gcc 4.8.2.
## License
**bitsery** is licensed under the [MIT license](LICENSE).
## 💖 Sponsor Me?
If you find this project useful or interesting, or just want to say thanks, you can buy me a coffee!
Your support keeps me motivated to maintaining and improving this project.
[**Thank you!**](https://github.com/sponsors/fraillt)

View File

@@ -20,7 +20,7 @@
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#SOFTWARE.
cmake_minimum_required(VERSION 3.1)
cmake_minimum_required(VERSION 3.25)
project(bitsery_examples CXX)
if (NOT TARGET Bitsery::bitsery)

View File

@@ -39,7 +39,7 @@ private:
template<typename S>
void serialize(S& s)
{
// forward/backward compatibility for monsters
// forward/backward compatibility for weapons
s.ext(*this, bitsery::ext::Growable{}, [](S& s, Weapon& o1) {
s.text1b(o1.name, 20);
s.value2b(o1.damage);
@@ -103,8 +103,6 @@ main()
// create buffer to store data to
Buffer buffer{};
// since we're using different configuration, we cannot use quickSerialization
// function.
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
MyTypes::Monster res{};

View File

@@ -95,14 +95,12 @@ namespace bitsery {
// and Derived2 member fnc we need explicitly select which function to use
template<>
struct SelectSerializeFnc<Derived2> : UseMemberFnc
{
};
{};
// multiple inheritance has non-member serialize function defined
template<>
struct SelectSerializeFnc<MultipleInheritance> : UseNonMemberFnc
{
};
{};
}
// some helper types

View File

@@ -191,14 +191,12 @@ namespace ext {
template<>
struct PolymorphicBaseClass<Shape>
: PolymorphicDerivedClasses<Circle, Rectangle>
{
};
{};
template<>
struct PolymorphicBaseClass<Rectangle>
: PolymorphicDerivedClasses<RoundedRectangle>
{
};
{};
}
}

View File

@@ -23,6 +23,7 @@
#ifndef BITSERY_ADAPTER_BUFFER_H
#define BITSERY_ADAPTER_BUFFER_H
#include "../bitsery.h"
#include "../details/adapter_bit_packing.h"
#include "../traits/core/traits.h"
#include <algorithm>
@@ -268,16 +269,16 @@ private:
void maybeResize(size_t newOffset, std::true_type)
{
if (newOffset > _bufferSize) {
traits::BufferAdapterTraits<Buffer>::increaseBufferSize(
*_buffer, _currOffset, newOffset);
_beginIt = std::begin(*_buffer);
_bufferSize = traits::ContainerTraits<Buffer>::size(*_buffer);
}
if (newOffset > _bufferSize)
BITSERY_UNLIKELY
{
doResize(newOffset);
}
}
void maybeResize(size_t newOffset, std::false_type)
{
static_cast<void>(newOffset);
assert(newOffset <= _bufferSize);
}
@@ -288,6 +289,14 @@ private:
std::copy_n(data, size, _beginIt + static_cast<diff_t>(_currOffset));
_currOffset = newOffset;
}
BITSERY_NOINLINE void doResize(size_t newOffset)
{
traits::BufferAdapterTraits<Buffer>::increaseBufferSize(
*_buffer, _currOffset, newOffset);
_beginIt = std::begin(*_buffer);
_bufferSize = traits::ContainerTraits<Buffer>::size(*_buffer);
}
};
}

View File

@@ -25,7 +25,7 @@
#define BITSERY_MAJOR_VERSION 5
#define BITSERY_MINOR_VERSION 2
#define BITSERY_PATCH_VERSION 2
#define BITSERY_PATCH_VERSION 5
#define BITSERY_QUOTE_MACRO(name) #name
#define BITSERY_BUILD_VERSION_STR(major, minor, patch) \
@@ -36,6 +36,67 @@
BITSERY_BUILD_VERSION_STR( \
BITSERY_MAJOR_VERSION, BITSERY_MINOR_VERSION, BITSERY_PATCH_VERSION)
#define BITSERY_DO_PRAGMA(x) _Pragma(#x)
#ifdef __GNUC__
#define BITSERY_DISABLE_WARNINGS(...) \
BITSERY_DO_PRAGMA(GCC diagnostic push) \
BITSERY_DO_PRAGMA(GCC diagnostic ignored __VA_ARGS__)
#define BITSERY_ENABLE_WARNINGS() BITSERY_DO_PRAGMA(GCC diagnostic pop)
#elif defined(_MSC_VER)
#define BITSERY_DISABLE_WARNINGS(...) \
BITSERY_DO_PRAGMA(GCC diagnostic push) \
BITSERY_DO_PRAGMA(GCC diagnostic ignored __VA_ARGS__) \
BITSERY_DO_PRAGMA(GCC diagnostic pop)
#define BITSERY_ENABLE_WARNINGS() BITSERY_DO_PRAGMA(GCC diagnostic pop)
#else
#define BITSERY_DISABLE_WARNINGS(...)
#define BITSERY_ENABLE_WARNINGS()
#endif
#ifdef __clang__
#define BITSERY_ATTRIBUTE(...) \
BITSERY_DISABLE_WARNINGS("-Wfuture-attribute-extensions") \
[[__VA_ARGS__]] BITSERY_ENABLE_WARNINGS()
#elif defined(__GNUC__)
#define BITSERY_ATTRIBUTE(...) [[__VA_ARGS__]]
#elif defined(_MSC_VER)
#define BITSERY_ATTRIBUTE(...) [[__VA_ARGS__]]
#else
#define BITSERY_ATTRIBUTE(...) [[__VA_ARGS__]]
#endif
#if __has_cpp_attribute(likely)
#define BITSERY_LIKELY BITSERY_ATTRIBUTE(likely)
#else
#define BITSERY_LIKELY
#endif
#if __has_cpp_attribute(unlikely)
#define BITSERY_UNLIKELY BITSERY_ATTRIBUTE(unlikely)
#else
#define BITSERY_UNLIKELY
#endif
#if __GNUC__
#define BITSERY_NOINLINE __attribute__((noinline))
#elif defined(_MSC_VER)
#define BITSERY_NOINLINE __declspec(noinline)
#else
#define BITSERY_NOINLINE
#endif
#if __GNUC__
#define BITSERY_ASSUME(cond) \
do { \
if (!(cond)) \
__builtin_unreachable(); \
} while (0)
#elif defined(_MSC_VER)
#define BITSERY_ASSUME(cond) __assume(cond)
#else
#define BITSERY_ASSUME(cond)
#endif
#include "deserializer.h"
#include "serializer.h"

View File

@@ -0,0 +1,36 @@
// MIT License
//
// Copyright (c) 2018 Mindaugas Vinkelis
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_BITSET_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_BITSET_H
#include "../ext/std_bitset.h"
namespace bitsery {
template<typename S, size_t N>
void
serialize(S& s, std::bitset<N>& obj)
{
s.ext(obj, ext::StdBitset{});
}
}
#endif

View File

@@ -0,0 +1,36 @@
// MIT License
//
// Copyright (c) 2018 Mindaugas Vinkelis
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_OPTIONAL_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_OPTIONAL_H
#include "../ext/std_optional.h"
namespace bitsery {
template<typename S, typename Ts>
void
serialize(S& s, std::optional<Ts>& obj)
{
s.ext(obj, ext::StdOptional{});
}
} // namespace bitsery
#endif

View File

@@ -28,6 +28,7 @@
#include <algorithm>
#include <cassert>
#include <climits>
#include <cstdint>
namespace bitsery {
@@ -144,10 +145,7 @@ struct SwapImpl
return static_cast<uint16_t>((value & 0x00ff) << 8 | (value & 0xff00) >> 8);
}
static uint8_t exec(uint8_t value)
{
return value;
}
static uint8_t exec(uint8_t value) { return value; }
};
template<typename TValue>
@@ -194,10 +192,11 @@ getSystemEndianness()
: EndiannessType::BigEndian;
}
template<typename Config>
template<typename Config, typename T>
using ShouldSwap =
std::integral_constant<bool,
Config::Endianness != details::getSystemEndianness()>;
Config::Endianness != details::getSystemEndianness() &&
sizeof(T) != 1>;
/**
* helper types to work with bits
@@ -287,7 +286,7 @@ struct OutputAdapterBaseCRTP
{
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
writeSwappedValue(&v, ShouldSwap<typename Adapter::TConfig>{});
writeSwappedValue(&v, ShouldSwap<typename Adapter::TConfig, T>{});
}
template<size_t SIZE, typename T>
@@ -295,7 +294,7 @@ struct OutputAdapterBaseCRTP
{
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
writeSwappedBuffer(buf, count, ShouldSwap<typename Adapter::TConfig>{});
writeSwappedBuffer(buf, count, ShouldSwap<typename Adapter::TConfig, T>{});
}
template<typename T>
@@ -360,7 +359,7 @@ struct InputAdapterBaseCRTP
static_assert(sizeof(T) == SIZE, "");
static_cast<Adapter*>(this)->template readInternalValue<sizeof(T)>(
reinterpret_cast<typename Adapter::TValue*>(&v));
swapDataBits(v, ShouldSwap<typename Adapter::TConfig>{});
swapDataBits(v, ShouldSwap<typename Adapter::TConfig, T>{});
}
template<size_t SIZE, typename T>
@@ -370,7 +369,7 @@ struct InputAdapterBaseCRTP
static_assert(sizeof(T) == SIZE, "");
static_cast<Adapter*>(this)->readInternalBuffer(
reinterpret_cast<typename Adapter::TValue*>(buf), sizeof(T) * count);
swapDataBits(buf, count, ShouldSwap<typename Adapter::TConfig>{});
swapDataBits(buf, count, ShouldSwap<typename Adapter::TConfig, T>{});
}
template<typename T>

View File

@@ -62,8 +62,7 @@ struct NotDefinedType
template<typename T>
struct IsDefined
: public std::integral_constant<bool, !std::is_same<NotDefinedType, T>::value>
{
};
{};
}
}

View File

@@ -70,24 +70,19 @@ struct FtorExtValue : public Ext
template<typename Ext>
struct FtorExtValue1b : FtorExtValue<1, Ext>
{
};
{};
template<typename Ext>
struct FtorExtValue2b : FtorExtValue<2, Ext>
{
};
{};
template<typename Ext>
struct FtorExtValue4b : FtorExtValue<4, Ext>
{
};
{};
template<typename Ext>
struct FtorExtValue8b : FtorExtValue<8, Ext>
{
};
{};
template<typename Ext>
struct FtorExtValue16b : FtorExtValue<16, Ext>
{
};
{};
template<typename Ext>
struct FtorExtObject : public Ext
@@ -105,8 +100,7 @@ struct FtorExtObject : public Ext
// SelectSerializeFnc<MyDerivedClass>:UseMemberFnc {};
template<typename T>
struct SelectSerializeFnc : std::integral_constant<int, 0>
{
};
{};
// types you need to inherit from when specializing SelectSerializeFnc class
struct UseNonMemberFnc : std::integral_constant<int, 1>
@@ -120,20 +114,17 @@ namespace details {
template<typename T>
struct IsContainerTraitsDefined
: public IsDefined<typename traits::ContainerTraits<T>::TValue>
{
};
{};
template<typename T>
struct IsTextTraitsDefined
: public IsDefined<typename traits::TextTraits<T>::TValue>
{
};
{};
template<typename Ext, typename T>
struct IsExtensionTraitsDefined
: public IsDefined<typename traits::ExtensionTraits<Ext, T>::TValue>
{
};
{};
#ifdef _MSC_VER
// helper types for HasSerializeFunction
@@ -151,8 +142,7 @@ struct HasSerializeFunctionHelper
};
template<typename S, typename T>
struct HasSerializeFunction : HasSerializeFunctionHelper<S, T>::type
{
};
{};
// helper types for HasSerializeMethod
template<typename S, typename T>
@@ -169,8 +159,7 @@ struct HasSerializeMethodHelper
};
template<typename S, typename T>
struct HasSerializeMethod : HasSerializeMethodHelper<S, T>::type
{
};
{};
// helper types for IsBriefSyntaxIncluded
template<typename S, typename T>
@@ -188,8 +177,7 @@ struct IsBriefSyntaxIncludedHelper
template<typename S, typename T>
struct IsBriefSyntaxIncluded : IsBriefSyntaxIncludedHelper<S, T>::type
{
};
{};
#else
// helper metafunction, that is added to c++17
template<typename... Ts>
@@ -202,8 +190,7 @@ using void_t = typename make_void<Ts...>::type;
template<typename, typename, typename = void>
struct HasSerializeFunction : std::false_type
{
};
{};
template<typename S, typename T>
struct HasSerializeFunction<
@@ -211,13 +198,11 @@ struct HasSerializeFunction<
T,
void_t<decltype(serialize(std::declval<S&>(), std::declval<T&>()))>>
: std::true_type
{
};
{};
template<typename, typename, typename = void>
struct HasSerializeMethod : std::false_type
{
};
{};
template<typename S, typename T>
struct HasSerializeMethod<
@@ -225,14 +210,12 @@ struct HasSerializeMethod<
T,
void_t<decltype(Access::serialize(std::declval<S&>(), std::declval<T&>()))>>
: std::true_type
{
};
{};
// this solution doesn't work with visual studio, but is more elegant
template<typename, typename, typename = void>
struct IsBriefSyntaxIncluded : std::false_type
{
};
{};
template<typename S, typename T>
struct IsBriefSyntaxIncluded<
@@ -240,8 +223,7 @@ struct IsBriefSyntaxIncluded<
T,
void_t<decltype(processBriefSyntax(std::declval<S&>(), std::declval<T&&>()))>>
: std::true_type
{
};
{};
#endif
// used for extensions when extension TValue = void
@@ -257,8 +239,7 @@ struct IsFundamentalType
std::is_enum<T>::value ||
std::is_floating_point<T>::value ||
std::is_integral<T>::value>
{
};
{};
template<typename T, typename Integral = void>
struct IntegralFromFundamental
@@ -372,16 +353,14 @@ struct BriefSyntaxFunction
template<int Index, typename... Conds>
struct FindIndex : std::integral_constant<int, Index>
{
};
{};
template<int Index, typename Cond, typename... Conds>
struct FindIndex<Index, Cond, Conds...>
: std::conditional<Cond::value,
std::integral_constant<int, Index>,
FindIndex<Index + 1, Conds...>>::type
{
};
{};
template<typename T, typename Tuple>
struct GetConvertibleTypeIndexFromTuple;
@@ -389,8 +368,7 @@ struct GetConvertibleTypeIndexFromTuple;
template<typename T, typename... Us>
struct GetConvertibleTypeIndexFromTuple<T, std::tuple<Us...>>
: FindIndex<0, std::is_convertible<Us&, T&>...>
{
};
{};
template<typename T, typename Tuple>
struct IsExistsConvertibleTupleType;
@@ -401,8 +379,7 @@ struct IsExistsConvertibleTupleType<T, std::tuple<Us...>>
bool,
GetConvertibleTypeIndexFromTuple<T, std::tuple<Us...>>::value !=
sizeof...(Us)>
{
};
{};
/*
* get context from internal or external, and check if it's convertible or not
@@ -549,13 +526,11 @@ protected:
template<typename T, template<typename...> class Template>
struct IsSpecializationOf : std::false_type
{
};
{};
template<template<typename...> class Template, typename... Args>
struct IsSpecializationOf<Template<Args...>, Template> : std::true_type
{
};
{};
}
}

View File

@@ -24,6 +24,7 @@
#define BITSERY_EXT_GROWABLE_H
#include "../traits/core/traits.h"
#include <cstdint>
namespace bitsery {

View File

@@ -104,7 +104,7 @@ struct PtrObserverManager
static void destroyPolymorphic(T& obj,
MemResourceBase*,
PolymorphicHandlerBase&)
const std::shared_ptr<PolymorphicHandlerBase>&)
{
obj = nullptr;
}
@@ -130,47 +130,22 @@ struct NonPtrManager
static void create(T&, MemResourceBase*, size_t) {}
static void createPolymorphic(T&, MemResourceBase*, PolymorphicHandlerBase&)
static void createPolymorphic(T&,
MemResourceBase*,
const std::shared_ptr<PolymorphicHandlerBase>&)
{
}
static void destroy(T&, MemResourceBase*, size_t) {}
static void destroyPolymorphic(T&, MemResourceBase*, PolymorphicHandlerBase&)
static void destroyPolymorphic(T&,
MemResourceBase*,
const std::shared_ptr<PolymorphicHandlerBase>&)
{
}
// LCOV_EXCL_STOP
};
// this class is used by NonPtrManager
struct NoRTTI
{
template<typename TBase>
static size_t get(TBase&)
{
return 0;
}
template<typename TBase>
static constexpr size_t get()
{
return 0;
}
template<typename TBase, typename TDerived>
static constexpr TDerived* cast(TBase* obj)
{
static_assert(!std::is_pointer<TDerived>::value, "");
return dynamic_cast<TDerived*>(obj);
}
template<typename TBase>
static constexpr bool isPolymorphic()
{
return false;
}
};
}
template<typename RTTI>
@@ -179,31 +154,34 @@ using PointerOwnerBase =
PolymorphicContext,
RTTI>;
using PointerOwner = PointerOwnerBase<StandardRTTI>;
using PointerObserver =
template<typename RTTI>
using PointerObserverBase =
pointer_utils::PointerObjectExtensionBase<pointer_details::PtrObserverManager,
PolymorphicContext,
pointer_details::NoRTTI>;
RTTI>;
// inherit from PointerObjectExtensionBase in order to specify
// PointerType::NotNull
class ReferencedByPointer
template<typename RTTI>
class ReferencedByPointerBase
: public pointer_utils::PointerObjectExtensionBase<
pointer_details::NonPtrManager,
PolymorphicContext,
pointer_details::NoRTTI>
RTTI>
{
public:
ReferencedByPointer()
ReferencedByPointerBase()
: pointer_utils::PointerObjectExtensionBase<pointer_details::NonPtrManager,
PolymorphicContext,
pointer_details::NoRTTI>(
PointerType::NotNull)
RTTI>(PointerType::NotNull)
{
}
};
using PointerOwner = PointerOwnerBase<StandardRTTI>;
using PointerObserver = PointerObserverBase<StandardRTTI>;
using ReferencedByPointer = ReferencedByPointerBase<StandardRTTI>;
}
namespace traits {
@@ -219,8 +197,8 @@ struct ExtensionTraits<ext::PointerOwnerBase<RTTI>, T*>
!RTTI::template isPolymorphic<TValue>();
};
template<typename T>
struct ExtensionTraits<ext::PointerObserver, T*>
template<typename T, typename RTTI>
struct ExtensionTraits<ext::PointerObserverBase<RTTI>, T*>
{
// although pointer observer doesn't serialize anything, but we still add
// value overload support to be consistent with pointer owners observer only
@@ -231,8 +209,8 @@ struct ExtensionTraits<ext::PointerObserver, T*>
static constexpr bool SupportLambdaOverload = false;
};
template<typename T>
struct ExtensionTraits<ext::ReferencedByPointer, T>
template<typename T, typename RTTI>
struct ExtensionTraits<ext::ReferencedByPointerBase<RTTI>, T>
{
// allow everything, because it is serialized as regular type, except it also
// creates pointerId that is required by NonOwningPointer to work

View File

@@ -25,6 +25,7 @@
#include "../traits/core/traits.h"
#include <bitset>
#include <cstdint>
namespace bitsery {
namespace ext {

View File

@@ -147,6 +147,7 @@ struct SmartPtrOwnerManager
[alloc, typeId](TElement* data) { alloc.deleteObject(data, typeId); },
pointer_utils::StdPolyAlloc<TElement>(memResource));
state.obj = obj;
state.typeId = typeId;
}
static void createSharedPolymorphic(
@@ -162,6 +163,7 @@ struct SmartPtrOwnerManager
[alloc, handler](TElement* data) { handler->destroy(alloc, data); },
pointer_utils::StdPolyAlloc<TElement>(memResource));
state.obj = obj;
state.typeId = handler->getDerivedTypeId();
}
static void createShared(TSharedState& state,
@@ -176,6 +178,7 @@ struct SmartPtrOwnerManager
pointer_utils::StdPolyAlloc<TElement>(memResource));
obj = res;
state.obj = res;
state.typeId = typeId;
}
static void createSharedPolymorphic(
@@ -191,6 +194,7 @@ struct SmartPtrOwnerManager
pointer_utils::StdPolyAlloc<TElement>(memResource));
obj = res;
state.obj = res;
state.typeId = handler->getDerivedTypeId();
}
static void saveToSharedState(TSharedState& state, T& obj)
@@ -198,10 +202,24 @@ struct SmartPtrOwnerManager
state.obj = std::shared_ptr<TElement>(obj);
}
static void saveToSharedStatePolymorphic(TSharedState& state, T& obj)
{
state.obj = std::shared_ptr<TElement>(obj);
}
static void loadFromSharedState(TSharedState& state, T& obj)
{
// reinterpret_pointer_cast is only since c++17
auto p = reinterpret_cast<TElement*>(state.obj.get());
auto v = state.obj.get();
auto p = static_cast<TElement*>(v);
obj = std::shared_ptr<TElement>(state.obj, p);
}
static void loadFromSharedStatePolymorphic(TSharedState& state,
T& obj,
const PolymorphicHandlerBase&)
{
auto v = state.obj.get();
auto p = static_cast<TElement*>(v);
obj = std::shared_ptr<TElement>(state.obj, p);
}
};

View File

@@ -56,6 +56,7 @@ namespace pointer_utils {
// this class is used to store context for shared ptr owners
struct PointerSharedStateBase
{
size_t typeId{};
virtual ~PointerSharedStateBase() = default;
};
@@ -117,16 +118,23 @@ struct PLCInfoSerializer : PLCInfo
size_t id;
};
struct ObserverRef
{
std::reference_wrapper<void*> obj;
size_t baseTypeId;
};
struct PLCInfoDeserializer : PLCInfo
{
PLCInfoDeserializer(void* ptr,
size_t ownerTypeId_,
PointerOwnershipType ownershipType_,
MemResourceBase* memResource_)
: PLCInfo(ownershipType_)
, ownerPtr{ ptr }
, ownerTypeId{ ownerTypeId_ }
, memResource{ memResource_ }
, observersList{ StdPolyAlloc<std::reference_wrapper<void*>>{
memResource_ } } {};
, observersList{ StdPolyAlloc<ObserverRef>{ memResource_ } } {};
// need to override these explicitly because we have pointer member
PLCInfoDeserializer(const PLCInfoDeserializer&) = delete;
@@ -137,30 +145,12 @@ struct PLCInfoDeserializer : PLCInfo
PLCInfoDeserializer& operator=(PLCInfoDeserializer&&) = default;
void processOwner(void* ptr)
{
ownerPtr = ptr;
assert(ownershipType != PointerOwnershipType::Observer);
for (auto& o : observersList)
o.get() = ptr;
observersList.clear();
observersList.shrink_to_fit();
}
void processObserver(void*(&ptr))
{
if (ownerPtr) {
ptr = ownerPtr;
} else {
observersList.emplace_back(ptr);
}
}
void* ownerPtr;
// used for polymorphic types in order to identify
// if shared objects can be assigned
size_t ownerTypeId;
MemResourceBase* memResource;
std::vector<std::reference_wrapper<void*>,
StdPolyAlloc<std::reference_wrapper<void*>>>
observersList;
std::vector<ObserverRef, StdPolyAlloc<ObserverRef>> observersList;
std::unique_ptr<PointerSharedStateBase, PointerSharedStateDeleter>
sharedState{};
};
@@ -254,8 +244,8 @@ public:
PLCInfoDeserializer& getInfoById(size_t id, PointerOwnershipType ptrType)
{
auto res =
_idMap.emplace(id, PLCInfoDeserializer{ nullptr, ptrType, _memResource });
auto res = _idMap.emplace(
id, PLCInfoDeserializer{ nullptr, 0, ptrType, _memResource });
auto& ptrInfo = res.first->second;
if (!res.second)
ptrInfo.update(ptrType);
@@ -329,8 +319,7 @@ public:
: std::integral_constant<
bool,
RTTI::template isPolymorphic<typename TPtrManager<T>::TElement>()>
{
};
{};
template<PointerOwnershipType Value>
using OwnershipType = std::integral_constant<PointerOwnershipType, Value>;
@@ -353,8 +342,8 @@ public:
if (ptr) {
auto& ctx = ser.template context<
pointer_utils::PointerLinkingContextSerialization>();
auto& ptrInfo =
ctx.getInfoByPtr(getBasePtr(ptr), TPtrManager<T>::getOwnership());
auto& ptrInfo = ctx.getInfoByPtr(getRootPtr(ser, ptr, IsPolymorphic<T>{}),
TPtrManager<T>::getOwnership());
details::writeSize(ser.adapter(), ptrInfo.id);
if (TPtrManager<T>::getOwnership() != PointerOwnershipType::Observer) {
if (!ptrInfo.isSharedProcessed)
@@ -412,7 +401,7 @@ private:
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
auto ptr = TPtrManager<TObj>::getPtr(obj);
TPtrManager<TObj>::destroyPolymorphic(
obj, memResource, ctx.getPolymorphicHandler(*ptr));
obj, memResource, ctx.getPolymorphicHandler(ptr));
}
template<typename Des, typename TObj>
@@ -427,16 +416,23 @@ private:
RTTI::template get<typename TPtrManager<TObj>::TElement>());
}
template<typename T>
const void* getBasePtr(const T* ptr) const
template<typename Ser, typename T>
const void* getRootPtr(Ser&, const T* ptr, std::false_type) const
{
// todo implement handling of types with virtual inheritance
// this is required to correctly track same object, when one object is
// derived and other is base class e.g. shared_ptr<Base> and
// weak_ptr<Derived> or pointer observer Base*
return ptr;
}
// same pointer can be accessed through different types with the same base
// e.g. if we have Base class and Derived(Base) we'll get different pointer
// address depending if we access it through Base or Derived
// this function always returns "root" (Base) pointer.
template<typename Ser, typename T>
const void* getRootPtr(Ser& ser, const T* ptr, std::true_type) const
{
const auto& ctx = ser.template context<TPolymorphicContext<RTTI>>();
return ctx.getPolymorphicHandler(ptr)->getRootPtr(ptr);
}
template<typename Ser, typename TPtr, typename Fnc>
void serializeImpl(Ser& ser, TPtr& ptr, Fnc&&, std::true_type) const
{
@@ -472,7 +468,13 @@ private:
memResource](const std::shared_ptr<PolymorphicHandlerBase>& handler) {
TPtrManager<T>::destroyPolymorphic(obj, memResource, handler);
});
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
auto ptr = TPtrManager<T>::getPtr(obj);
// might be null in case data pointer is not valid
if (ptr) {
ptrInfo.ownerPtr = ptr;
ptrInfo.ownerTypeId = ctx.getPolymorphicHandler(ptr)->getDerivedTypeId();
processObserverListPolymorphic(des, ptrInfo, ctx);
}
}
template<typename Des, typename T, typename Fnc>
@@ -485,17 +487,18 @@ private:
OwnershipType<PointerOwnershipType::Owner>) const
{
auto ptr = TPtrManager<T>::getPtr(obj);
if (ptr) {
fnc(des, *ptr);
} else {
if (!ptr) {
TPtrManager<T>::create(
obj,
memResource,
RTTI::template get<typename TPtrManager<T>::TElement>());
ptr = TPtrManager<T>::getPtr(obj);
fnc(des, *ptr);
}
ptrInfo.processOwner(ptr);
fnc(des, *ptr);
ptrInfo.ownerPtr = ptr;
ptrInfo.ownerTypeId =
RTTI::template get<typename TPtrManager<T>::TElement>();
processObserverList(des, ptrInfo);
}
template<typename Des, typename T, typename Fnc>
@@ -507,8 +510,11 @@ private:
std::true_type,
OwnershipType<PointerOwnershipType::SharedOwner>) const
{
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
const size_t baseTypeId =
RTTI::template get<typename TPtrManager<T>::TElement>();
size_t deserializedTypeId = 0;
if (!ptrInfo.sharedState) {
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
ctx.deserialize(
des,
TPtrManager<T>::getPtr(obj),
@@ -522,12 +528,28 @@ private:
memResource](const std::shared_ptr<PolymorphicHandlerBase>& handler) {
TPtrManager<T>::destroyPolymorphic(obj, memResource, handler);
});
if (!ptrInfo.sharedState)
TPtrManager<T>::saveToSharedState(
if (!ptrInfo.sharedState) {
TPtrManager<T>::saveToSharedStatePolymorphic(
createAndGetSharedStateObj<T>(ptrInfo), obj);
}
ptrInfo.ownerPtr = TPtrManager<T>::getPtr(obj);
ptrInfo.ownerTypeId =
ctx.getPolymorphicHandler(TPtrManager<T>::getPtr(obj))
->getDerivedTypeId();
// since we just deserialized an object, we can skip checking hierarchy
// chain by assigning baseType id instead of derived type id
deserializedTypeId = baseTypeId;
} else {
deserializedTypeId = ptrInfo.ownerTypeId;
}
if (auto hndl =
ctx.getPolymorphicHandler(baseTypeId, ptrInfo.sharedState->typeId)) {
TPtrManager<T>::loadFromSharedStatePolymorphic(
getSharedStateObj<T>(ptrInfo), obj, **hndl);
processObserverListPolymorphic(des, ptrInfo, ctx);
} else {
des.adapter().error(ReaderError::InvalidPointer);
}
TPtrManager<T>::loadFromSharedState(getSharedStateObj<T>(ptrInfo), obj);
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
}
template<typename Des, typename T, typename Fnc>
@@ -539,6 +561,8 @@ private:
std::false_type,
OwnershipType<PointerOwnershipType::SharedOwner>) const
{
const size_t baseTypeId =
RTTI::template get<typename TPtrManager<T>::TElement>();
if (!ptrInfo.sharedState) {
auto ptr = TPtrManager<T>::getPtr(obj);
if (ptr) {
@@ -553,9 +577,16 @@ private:
ptr = TPtrManager<T>::getPtr(obj);
}
fnc(des, *ptr);
ptrInfo.ownerTypeId =
RTTI::template get<typename TPtrManager<T>::TElement>();
ptrInfo.ownerPtr = TPtrManager<T>::getPtr(obj);
}
if (baseTypeId == ptrInfo.ownerTypeId) {
TPtrManager<T>::loadFromSharedState(getSharedStateObj<T>(ptrInfo), obj);
processObserverList(des, ptrInfo);
} else {
des.adapter().error(ReaderError::InvalidPointer);
}
TPtrManager<T>::loadFromSharedState(getSharedStateObj<T>(ptrInfo), obj);
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
}
template<typename Des, typename T, typename Fnc, typename isPolymorph>
@@ -577,17 +608,85 @@ private:
OwnershipType<PointerOwnershipType::SharedOwner>{});
}
template<typename Des, typename T, typename Fnc, typename isPolymorphic>
template<typename Des, typename T, typename Fnc>
void deserializeImpl(MemResourceBase*,
PLCInfoDeserializer& ptrInfo,
Des&,
Des& des,
T& obj,
Fnc&&,
isPolymorphic,
std::false_type,
OwnershipType<PointerOwnershipType::Observer>) const
{
ptrInfo.processObserver(
reinterpret_cast<void*&>(TPtrManager<T>::getPtrRef(obj)));
auto baseTypeId = RTTI::template get<typename TPtrManager<T>::TElement>();
void*(&ptr) = reinterpret_cast<void*&>(TPtrManager<T>::getPtrRef(obj));
if (ptrInfo.ownerPtr) {
if (ptrInfo.ownerTypeId == baseTypeId) {
ptr = ptrInfo.ownerPtr;
} else {
des.adapter().error(ReaderError::InvalidPointer);
}
} else {
ptrInfo.observersList.emplace_back(ObserverRef{ ptr, baseTypeId });
}
}
template<typename Des, typename T, typename Fnc>
void deserializeImpl(MemResourceBase*,
PLCInfoDeserializer& ptrInfo,
Des& des,
T& obj,
Fnc&&,
std::true_type,
OwnershipType<PointerOwnershipType::Observer>) const
{
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
const size_t baseTypeId =
RTTI::template get<typename TPtrManager<T>::TElement>();
void*(&ptr) = reinterpret_cast<void*&>(TPtrManager<T>::getPtrRef(obj));
if (ptrInfo.ownerPtr) {
if (auto hndl =
ctx.getPolymorphicHandler(baseTypeId, ptrInfo.ownerTypeId)) {
ptr = hndl->get()->fromDerivedToBasePtr(ptrInfo.ownerPtr);
} else {
des.adapter().error(ReaderError::InvalidPointer);
}
} else {
ptrInfo.observersList.emplace_back(ObserverRef{ ptr, baseTypeId });
}
}
template<typename Des>
void processObserverList(Des& des, PLCInfoDeserializer& ptrInfo) const
{
assert(ptrInfo.ownershipType != PointerOwnershipType::Observer);
for (auto& o : ptrInfo.observersList) {
if (ptrInfo.ownerTypeId == o.baseTypeId) {
o.obj.get() = ptrInfo.ownerPtr;
} else {
des.adapter().error(ReaderError::InvalidPointer);
}
}
ptrInfo.observersList.clear();
ptrInfo.observersList.shrink_to_fit();
}
template<typename Des>
void processObserverListPolymorphic(
Des& des,
PLCInfoDeserializer& ptrInfo,
const TPolymorphicContext<RTTI>& ctx) const
{
assert(ptrInfo.ownershipType != PointerOwnershipType::Observer);
for (auto& o : ptrInfo.observersList) {
if (auto hndl =
ctx.getPolymorphicHandler(o.baseTypeId, ptrInfo.ownerTypeId)) {
o.obj.get() = hndl->get()->fromDerivedToBasePtr(ptrInfo.ownerPtr);
} else {
des.adapter().error(ReaderError::InvalidPointer);
}
}
ptrInfo.observersList.clear();
ptrInfo.observersList.shrink_to_fit();
}
template<typename T>

View File

@@ -35,8 +35,7 @@ namespace ext {
// helper type, that contains list of types
template<typename...>
struct PolymorphicClassesList
{
};
{};
// specialize for your base class by deriving from PolymorphicDerivedClasses
// with list of derivatives that DIRECTLY inherits from your base class.
@@ -75,10 +74,20 @@ public:
virtual void process(void* ser, void* obj) const = 0;
virtual void* getRootPtr(const void* obj) const = 0;
virtual void* fromDerivedToBasePtr(void* obj) const = 0;
virtual size_t getDerivedTypeId() const = 0;
virtual ~PolymorphicHandlerBase() = default;
};
template<typename RTTI, typename TSerializer, typename TBase, typename TDerived>
template<typename RTTI,
typename TSerializer,
typename TRoot,
typename TBase,
typename TDerived>
class PolymorphicHandler : public PolymorphicHandlerBase
{
public:
@@ -98,6 +107,19 @@ public:
static_cast<TSerializer*>(ser)->object(*fromBase(obj));
}
void* getRootPtr(const void* obj) const final
{
return RTTI::template cast<TBase, TRoot>(
static_cast<TBase*>(const_cast<void*>(obj)));
}
void* fromDerivedToBasePtr(void* obj) const final { return toBase(obj); }
size_t getDerivedTypeId() const final
{
return RTTI::template get<TDerived>();
}
private:
TDerived* fromBase(void* obj) const
{
@@ -110,6 +132,41 @@ private:
}
};
// Even though we don't serialize/deserialize abstract classes
// object might still be accessed through abstract class, hence we need this
// for type information
template<typename RTTI, typename TRoot, typename TBase, typename TDerived>
class AbstractPolymorphicHandler : public PolymorphicHandlerBase
{
public:
void* create(const pointer_utils::PolyAllocWithTypeId&) const
{
assert(false);
return nullptr;
}
void destroy(const pointer_utils::PolyAllocWithTypeId&, void*) const
{
assert(false);
};
void process(void*, void*) const { assert(false); }
void* getRootPtr(const void*) const
{
assert(false);
return nullptr;
}
void* fromDerivedToBasePtr(void*) const final
{
assert(false);
return nullptr;
}
size_t getDerivedTypeId() const { return RTTI::template get<TDerived>(); };
};
template<typename RTTI>
class PolymorphicContext
{
@@ -138,18 +195,20 @@ private:
template<typename TSerializer,
template<typename>
class THierarchy,
typename TRoot,
typename TBase,
typename TDerived>
void add()
{
addToMap<TSerializer, TBase, TDerived>(std::is_abstract<TDerived>{});
addChilds<TSerializer, THierarchy, TBase, TDerived>(
addToMap<TSerializer, TRoot, TBase, TDerived>(std::is_abstract<TDerived>{});
addChilds<TSerializer, THierarchy, TRoot, TBase, TDerived>(
typename THierarchy<TDerived>::Childs{});
}
template<typename TSerializer,
template<typename>
class THierarchy,
typename TRoot,
typename TBase,
typename TDerived,
typename T1,
@@ -159,26 +218,30 @@ private:
static_assert(std::is_base_of<TDerived, T1>::value,
"PolymorphicBaseClass<TBase> must derive a list of derived "
"classes from TBase.");
add<TSerializer, THierarchy, TBase, T1>();
addChilds<TSerializer, THierarchy, TBase, TDerived>(
add<TSerializer, THierarchy, TRoot, TBase, T1>();
addChilds<TSerializer, THierarchy, TRoot, TBase, TDerived>(
PolymorphicClassesList<Tn...>{});
// iterate through derived class hierarchy as well
add<TSerializer, THierarchy, T1, T1>();
add<TSerializer, THierarchy, TRoot, T1, T1>();
}
template<typename TSerializer,
template<typename>
class THierarchy,
typename TRoot,
typename TBase,
typename TDerived>
void addChilds(PolymorphicClassesList<>)
{
}
template<typename TSerializer, typename TBase, typename TDerived>
template<typename TSerializer,
typename TRoot,
typename TBase,
typename TDerived>
void addToMap(std::false_type)
{
using THandler = PolymorphicHandler<RTTI, TSerializer, TBase, TDerived>;
using THandler =
PolymorphicHandler<RTTI, TSerializer, TRoot, TBase, TDerived>;
BaseToDerivedKey key{ RTTI::template get<TBase>(),
RTTI::template get<TDerived>() };
pointer_utils::StdPolyAlloc<THandler> alloc{ _memResource };
@@ -204,10 +267,25 @@ private:
}
}
template<typename TSerializer, typename TBase, typename TDerived>
template<typename TSerializer,
typename TRoot,
typename TBase,
typename TDerived>
void addToMap(std::true_type)
{
// cannot add abstract class
using THandler = AbstractPolymorphicHandler<RTTI, TRoot, TBase, TDerived>;
BaseToDerivedKey key{ RTTI::template get<TBase>(),
RTTI::template get<TDerived>() };
pointer_utils::StdPolyAlloc<THandler> alloc{ _memResource };
auto ptr = alloc.allocate(1);
std::shared_ptr<THandler> handler(
new (ptr) THandler{},
[alloc](THandler* data) mutable {
data->~THandler();
alloc.deallocate(data, 1);
},
alloc);
_baseToDerivedMap.emplace(key, std::move(handler));
}
MemResourceBase* _memResource;
@@ -270,7 +348,7 @@ public:
typename... Tn>
void registerBasesList(PolymorphicClassesList<T1, Tn...>)
{
add<TSerializer, THierarchy, T1, T1>();
add<TSerializer, THierarchy, T1, T1, T1>();
registerBasesList<TSerializer, THierarchy>(PolymorphicClassesList<Tn...>{});
}
@@ -279,18 +357,6 @@ public:
{
}
// optional method, in case you want to construct base class hierarchy your
// self
template<typename TSerializer, typename TBase, typename TDerived>
void registerSingleBaseBranch()
{
static_assert(std::is_base_of<TBase, TDerived>::value,
"TDerived must be derived from TBase");
static_assert(!std::is_abstract<TDerived>::value,
"TDerived cannot be abstract");
addToMap<TSerializer, TBase, TDerived>(std::false_type{});
}
template<typename Serializer, typename TBase>
void serialize(Serializer& ser, TBase& obj) const
{
@@ -339,7 +405,7 @@ public:
// if object is null or different type, create new and assign it
if (obj == nullptr || RTTI::template get<TBase>(*obj) != derivedHash) {
if (obj) {
destroyFnc(getPolymorphicHandler(*obj));
destroyFnc(getPolymorphicHandler(obj));
}
obj = createFnc(handler);
}
@@ -350,12 +416,33 @@ public:
template<typename TBase>
const std::shared_ptr<PolymorphicHandlerBase>& getPolymorphicHandler(
TBase& obj) const
TBase* obj) const
{
auto deleteHandlerIt = _baseToDerivedMap.find(BaseToDerivedKey{
RTTI::template get<TBase>(), RTTI::template get<TBase>(obj) });
assert(deleteHandlerIt != _baseToDerivedMap.end());
return deleteHandlerIt->second;
auto it = _baseToDerivedMap.find(BaseToDerivedKey{
RTTI::template get<TBase>(), RTTI::template get<TBase>(*obj) });
assert(it != _baseToDerivedMap.end());
return it->second;
}
template<typename TBase>
const std::shared_ptr<PolymorphicHandlerBase>& getPolymorphicHandler() const
{
auto it = _baseToDerivedMap.find(BaseToDerivedKey{
RTTI::template get<TBase>(), RTTI::template get<TBase>() });
assert(it != _baseToDerivedMap.end());
return it->second;
}
const std::shared_ptr<PolymorphicHandlerBase>* getPolymorphicHandler(
size_t baseTypeId,
size_t derivedTypeId) const
{
auto it =
_baseToDerivedMap.find(BaseToDerivedKey{ baseTypeId, derivedTypeId });
if (it == _baseToDerivedMap.end()) {
return nullptr;
}
return &it->second;
}
};

View File

@@ -32,14 +32,12 @@ namespace traits {
template<typename T, size_t N>
struct ContainerTraits<std::array<T, N>>
: public StdContainer<std::array<T, N>, false, true>
{
};
{};
template<typename T, size_t N>
struct BufferAdapterTraits<std::array<T, N>>
: public StdContainerForBufferAdapter<std::array<T, N>>
{
};
{};
}
}

View File

@@ -23,6 +23,7 @@
#ifndef BITSERY_TRAITS_CORE_STD_DEFAULTS_H
#define BITSERY_TRAITS_CORE_STD_DEFAULTS_H
#include "../../bitsery.h"
#include "../../details/serialization_common.h"
#include "traits.h"
@@ -103,8 +104,11 @@ struct StdContainerForBufferAdapter<T, true>
static_cast<size_t>(static_cast<double>(container.size()) * 1.5) + 128;
// make data cache friendly
newSize -= newSize % 64; // 64 is cache line size
container.resize(
(std::max)(newSize > minSize ? newSize : minSize, container.capacity()));
auto resize =
(std::max)(newSize > minSize ? newSize : minSize, container.capacity());
BITSERY_ASSUME(resize >= container.size());
BITSERY_ASSUME(resize >= container.capacity());
container.resize(resize);
}
};

View File

@@ -33,8 +33,7 @@ namespace traits {
template<typename T, typename Allocator>
struct ContainerTraits<std::deque<T, Allocator>>
: public StdContainer<std::deque<T, Allocator>, true, false>
{
};
{};
}

View File

@@ -33,8 +33,7 @@ namespace traits {
template<typename T, typename Allocator>
struct ContainerTraits<std::list<T, Allocator>>
: public StdContainer<std::list<T, Allocator>, true, false>
{
};
{};
}

View File

@@ -36,8 +36,7 @@ namespace traits {
template<typename CharT, typename Traits, typename Allocator>
struct ContainerTraits<std::basic_string<CharT, Traits, Allocator>>
: public StdContainer<std::basic_string<CharT, Traits, Allocator>, true, true>
{
};
{};
template<typename CharT, typename Traits, typename Allocator>
struct TextTraits<std::basic_string<CharT, Traits, Allocator>>
@@ -72,8 +71,7 @@ template<typename CharT, typename Traits, typename Allocator>
struct BufferAdapterTraits<std::basic_string<CharT, Traits, Allocator>>
: public StdContainerForBufferAdapter<
std::basic_string<CharT, Traits, Allocator>>
{
};
{};
}

View File

@@ -32,21 +32,18 @@ namespace traits {
template<typename T, typename Allocator>
struct ContainerTraits<std::vector<T, Allocator>>
: public StdContainer<std::vector<T, Allocator>, true, true>
{
};
{};
// bool vector is not contiguous, do not copy it directly to buffer
template<typename Allocator>
struct ContainerTraits<std::vector<bool, Allocator>>
: public StdContainer<std::vector<bool, Allocator>, true, false>
{
};
{};
template<typename T, typename Allocator>
struct BufferAdapterTraits<std::vector<T, Allocator>>
: public StdContainerForBufferAdapter<std::vector<T, Allocator>>
{
};
{};
}

View File

@@ -1,11 +0,0 @@
# Compiler specific patches
This folder will provide patches for various C++ compilers that are not C++11 compatible yet. This allows providing any fix for any compiler, without polluting core library with compiler-specific fixes.
A patch can be applied either with `git apply` or `patch` command, like this:
```bash
git apply patches/<patch_name>
patch -p1 < patches/<patch_name>
```
* [centos7_gcc4.8.2.diff](centos7_gcc4.8.2.diff) in this version, unordered_map is not fully C++11 compatible yet. It is lacking some constructors that accept allocator, and isn't using `std::allocator_traits`.

View File

@@ -1,119 +0,0 @@
diff --git a/include/bitsery/details/serialization_common.h b/include/bitsery/details/serialization_common.h
index 6d5a441..462cee2 100644
--- a/include/bitsery/details/serialization_common.h
+++ b/include/bitsery/details/serialization_common.h
@@ -380,7 +380,7 @@ namespace bitsery {
template <typename ... TArgs>
explicit AdapterAndContextRef(Context& ctx, TArgs&& ... args)
: _adapter{std::forward<TArgs>(args)...},
- _context{ctx}
+ _context(ctx)
{
}
diff --git a/include/bitsery/ext/inheritance.h b/include/bitsery/ext/inheritance.h
index f4c6655..5cd44ab 100644
--- a/include/bitsery/ext/inheritance.h
+++ b/include/bitsery/ext/inheritance.h
@@ -36,7 +36,7 @@ namespace bitsery {
class InheritanceContext {
public:
explicit InheritanceContext(MemResourceBase* memResource = nullptr)
- :_virtualBases{pointer_utils::StdPolyAlloc<const void*>{memResource}}
+ :_virtualBases{0, std::hash<const void*>{}, std::equal_to<const void*>{}, pointer_utils::StdPolyAlloc<const void*>{memResource}}
{}
InheritanceContext(const InheritanceContext&) = delete;
InheritanceContext&operator = (const InheritanceContext&) = delete;
diff --git a/include/bitsery/ext/utils/memory_resource.h b/include/bitsery/ext/utils/memory_resource.h
index 472965a..18b3f31 100644
--- a/include/bitsery/ext/utils/memory_resource.h
+++ b/include/bitsery/ext/utils/memory_resource.h
@@ -24,6 +24,7 @@
#define BITSERY_EXT_MEMORY_RESOURCE_H
#include "../../details/serialization_common.h"
+#include <cstddef>
#include <new>
namespace bitsery {
@@ -128,6 +129,40 @@ namespace bitsery {
public:
using value_type = T;
+ using pointer = T*;
+ using const_pointer = const T*;
+ using reference = T&;
+ using const_reference = const T&;
+ using size_type = size_t;
+ using difference_type = ptrdiff_t;
+
+ size_t max_size() const noexcept {
+ return std::numeric_limits<size_t>::max() / sizeof(value_type);
+ }
+
+ void construct(T *p, const T &val) {
+ new((void *) p) T(val);
+ }
+
+ template<class U, class... Args>
+ void construct(U *p, Args &&... args) {
+ new((void *) p) U(std::forward<Args>(args)...);
+ }
+
+ void destroy(T *p) {
+ p->~T();
+ }
+
+ template<class U>
+ void destroy(U *p) {
+ p->~U();
+ }
+
+ template<typename U>
+ struct rebind {
+ using other = StdPolyAlloc<U>;
+ };
+
explicit constexpr StdPolyAlloc(MemResourceBase* memResource)
:_alloc{memResource} {}
explicit constexpr StdPolyAlloc(PolyAllocWithTypeId alloc) : _alloc{alloc} {}
diff --git a/include/bitsery/ext/utils/pointer_utils.h b/include/bitsery/ext/utils/pointer_utils.h
index f6f90da..6b65600 100644
--- a/include/bitsery/ext/utils/pointer_utils.h
+++ b/include/bitsery/ext/utils/pointer_utils.h
@@ -153,7 +153,7 @@ namespace bitsery {
public:
explicit PointerLinkingContextSerialization(MemResourceBase* memResource = nullptr)
: _currId{0},
- _ptrMap{StdPolyAlloc<std::pair<const void* const, PLCInfoSerializer>>{memResource}} {}
+ _ptrMap{0, std::hash<const void*>{}, std::equal_to<const void*>{}, StdPolyAlloc<std::pair<const void* const, PLCInfoSerializer>>{memResource}} {}
PointerLinkingContextSerialization(const PointerLinkingContextSerialization&) = delete;
@@ -198,7 +198,7 @@ namespace bitsery {
public:
explicit PointerLinkingContextDeserialization(MemResourceBase* memResource = nullptr)
: _memResource{memResource},
- _idMap{StdPolyAlloc<std::pair<const size_t, PLCInfoDeserializer>>{memResource}} {}
+ _idMap{0, std::hash<size_t>{}, std::equal_to<size_t>{}, StdPolyAlloc<std::pair<const size_t, PLCInfoDeserializer>>{memResource}} {}
PointerLinkingContextDeserialization(const PointerLinkingContextDeserialization&) = delete;
diff --git a/include/bitsery/ext/utils/polymorphism_utils.h b/include/bitsery/ext/utils/polymorphism_utils.h
index 6678230..a2cef4d 100644
--- a/include/bitsery/ext/utils/polymorphism_utils.h
+++ b/include/bitsery/ext/utils/polymorphism_utils.h
@@ -185,11 +185,8 @@ namespace bitsery {
explicit PolymorphicContext(MemResourceBase* memResource = nullptr)
:_memResource{memResource},
- _baseToDerivedMap{pointer_utils::StdPolyAlloc<std::pair<const BaseToDerivedKey,
- std::shared_ptr<PolymorphicHandlerBase>>>{memResource}},
- _baseToDerivedArray{pointer_utils::StdPolyAlloc<std::pair<const size_t,
- std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>>>{memResource}}
- {}
+ _baseToDerivedMap{0, BaseToDerivedKeyHashier{}, std::equal_to<BaseToDerivedKey>{}, pointer_utils::StdPolyAlloc<std::pair<const BaseToDerivedKey, std::shared_ptr<PolymorphicHandlerBase>>>{memResource}},
+ _baseToDerivedArray{0, std::hash<size_t>{}, std::equal_to<size_t>{}, pointer_utils::StdPolyAlloc<std::pair<const size_t, std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>>>{memResource}} {}
PolymorphicContext(const PolymorphicContext& ) = delete;
PolymorphicContext& operator = (const PolymorphicContext&) = delete;

View File

@@ -20,7 +20,7 @@
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#SOFTWARE.
cmake_minimum_required(VERSION 3.11)
cmake_minimum_required(VERSION 3.25)
project(bitsery_tests
LANGUAGES CXX)

View File

@@ -306,8 +306,7 @@ using AdapterInputTypes =
template<typename TConfig>
class InputAll : public AdapterConfig<TConfig>
{
};
{};
TYPED_TEST_SUITE(InputAll, AdapterInputTypes, );
@@ -476,8 +475,7 @@ using AdapterOutputTypes =
template<typename TConfig>
class OutputAll : public AdapterConfig<TConfig>
{
};
{};
TYPED_TEST_SUITE(OutputAll, AdapterOutputTypes, );

View File

@@ -29,6 +29,7 @@
#include <bitsery/brief_syntax/list.h>
#include <bitsery/brief_syntax/map.h>
#include <bitsery/brief_syntax/memory.h>
#include <bitsery/brief_syntax/queue.h>
#include <bitsery/brief_syntax/set.h>
#include <bitsery/brief_syntax/stack.h>
@@ -37,8 +38,12 @@
#include <bitsery/brief_syntax/unordered_set.h>
#include <bitsery/brief_syntax/vector.h>
#if __cplusplus > 201402L
#include <bitsery/brief_syntax/optional.h>
#include <bitsery/brief_syntax/tuple.h>
#include <bitsery/brief_syntax/variant.h>
#if __cplusplus > 202002L
#include <bitsery/brief_syntax/bitset.h>
#endif
#elif defined(_MSC_VER)
#pragma message( \
"C++17 and /Zc:__cplusplus option is required to enable std::tuple and std::variant brief syntax tests")
@@ -490,6 +495,22 @@ TEST(BriefSyntax, StdVariant)
EXPECT_TRUE(procBriefSyntax(t1) == t1);
}
TEST(BriefSyntax, StdOptional)
{
std::optional<uint32_t> opt{ 54654 };
EXPECT_TRUE(procBriefSyntax(opt) == opt);
}
#if __cplusplus > 202002L
TEST(BriefSyntax, StdBitset)
{
std::bitset<17> bits{ 0b10101010101010101 };
EXPECT_TRUE(procBriefSyntax(bits) == bits);
}
#endif
#endif
TEST(BriefSyntax, NestedTypes)

View File

@@ -320,8 +320,7 @@ namespace ext {
template<>
struct PolymorphicBaseClass<PolymorphicNDCBase>
: PolymorphicDerivedClasses<PolymorphicNDC1, PolymorphicNDC2>
{
};
{};
}
}

View File

@@ -214,8 +214,7 @@ TYPED_TEST(SerializeContainerFixedSizeArithmeticTypes, ArithmeticValues)
template<typename T>
class SerializeContainerFixedSizeCompositeTypes
: public SerializeContainerFixedSizeArithmeticTypes<T>
{
};
{};
using StaticContainersWithCompositeTypes =
::testing::Types<std::array<MyStruct1, 4>, MyStruct1[4]>;

View File

@@ -356,13 +356,11 @@ struct DerivedMemberSerialize : public BaseNonMemberSerialize
namespace bitsery {
template<>
struct SelectSerializeFnc<DerivedPrivateBase> : UseNonMemberFnc
{
};
{};
template<>
struct SelectSerializeFnc<DerivedMemberSerialize> : UseMemberFnc
{
};
{};
}
TEST(

View File

@@ -458,6 +458,23 @@ TEST_F(SerializeExtensionPointerDeserialization, PointerObserver)
EXPECT_THAT(pr3, Eq(&r3));
}
TEST_F(SerializeExtensionPointerDeserialization,
PointerObserverAndOwnerTypeMustBeTheSame)
{
// serialize as if we have two same objects
auto& ser = createSerializer();
ser.ext2b(d1, ReferencedByPointer{});
ser.ext2b(pd1, PointerObserver{});
auto& des = createDeserializer();
// but actual implementation expects distinct objects
des.ext2b(r1, ReferencedByPointer{});
des.ext4b(pr2, PointerObserver{});
EXPECT_THAT(isPointerContextValid(), Eq(true));
EXPECT_THAT(sctx1.des->adapter().error(),
Eq(bitsery::ReaderError::InvalidPointer));
}
struct Test1Data
{
std::vector<MyStruct1> vdata;

View File

@@ -147,21 +147,17 @@ namespace ext {
template<>
struct PolymorphicBaseClass<Base>
: PolymorphicDerivedClasses<Derived1, Derived2>
{
};
{};
// this is commented on purpose, to test scenario when base class is registered
// (Base) but using instance of Derived1 which is not registered as base
// template<>
// struct PolymorphicBaseClass<Derived1> :
// PolymorphicDerivedClasses<MultipleVirtualInheritance> {
// };
template<>
struct PolymorphicBaseClass<Derived1>
: PolymorphicDerivedClasses<MultipleVirtualInheritance>
{};
template<>
struct PolymorphicBaseClass<Derived2>
: PolymorphicDerivedClasses<MultipleVirtualInheritance>
{
};
{};
}
}
@@ -373,3 +369,62 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes,
EXPECT_THAT(sctx.des->adapter().error(),
Eq(bitsery::ReaderError::InvalidPointer));
}
TEST_F(SerializeExtensionPointerPolymorphicTypes,
SameObjectIsCorrectlyIdentifiedEvenIfObserverHasDifferentBase)
{
MultipleVirtualInheritance md;
Derived2* derivedData = &md;
EXPECT_THAT(static_cast<void*>(&md),
::testing::Ne(static_cast<void*>(derivedData)));
auto& ser = createSerializer();
ser.ext(md, ReferencedByPointer{});
ser.ext(derivedData, PointerObserver{});
EXPECT_THAT(isPointerContextValid(), Eq(true));
}
TEST_F(SerializeExtensionPointerPolymorphicTypes,
CheckIfOwnerTypeIsAssignableToObserverType)
{
MultipleVirtualInheritance md;
Derived2* derivedData = &md;
auto& ser = createSerializer();
ser.ext(&md, PointerOwner{});
ser.ext(derivedData, PointerObserver{});
MultipleVirtualInheritance* res1 = nullptr;
NoRelationshipSpecifiedDerived* res2 = nullptr;
auto& des = createDeserializer();
des.ext(res1, PointerOwner{});
des.ext(res2, PointerObserver{});
EXPECT_THAT(res1, ::testing::NotNull());
EXPECT_THAT(res2, ::testing::IsNull());
EXPECT_THAT(sctx.des->adapter().error(),
Eq(bitsery::ReaderError::InvalidPointer));
}
TEST_F(SerializeExtensionPointerPolymorphicTypes, OwnerIsCastObserverType)
{
MultipleVirtualInheritance md{ 1, 2, 3, 4 };
Derived2* derivedData = &md;
auto& ser = createSerializer();
ser.ext(&md, PointerOwner{});
ser.ext(derivedData, PointerObserver{});
MultipleVirtualInheritance* res1 = nullptr;
Base* res2 = nullptr;
auto& des = createDeserializer();
des.ext(res1, PointerOwner{});
des.ext(res2, PointerObserver{});
EXPECT_THAT(res1, ::testing::NotNull());
EXPECT_THAT(res2, ::testing::NotNull());
EXPECT_THAT(res2->x, Eq(1));
}

View File

@@ -152,14 +152,12 @@ namespace ext {
template<>
struct PolymorphicBaseClass<Base>
: PolymorphicDerivedClasses<Derived1, Derived2>
{
};
{};
template<>
struct PolymorphicBaseClass<PolyPtrWithPolyPtrBase>
: PolymorphicDerivedClasses<DerivedPolyPtrWithPolyPtr>
{
};
{};
}
}

View File

@@ -23,6 +23,8 @@
#include <bitsery/ext/inheritance.h>
#include <bitsery/ext/pointer.h>
#include <bitsery/ext/std_smart_ptr.h>
#include <bitsery/traits/string.h>
#include <bitsery/traits/vector.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
@@ -77,6 +79,27 @@ serialize(S& s, Derived& o)
s.value1b(o.y);
}
struct DerivedSibling : virtual Base
{
uint32_t y{};
DerivedSibling() = default;
DerivedSibling(uint8_t x_, uint32_t y_)
{
x = x_;
y = y_;
}
};
template<typename S>
void
serialize(S& s, DerivedSibling& o)
{
s.ext(o, VirtualBaseClass<Base>{});
s.value4b(o.y);
}
struct MoreDerived : Derived
{
uint8_t z{};
@@ -106,13 +129,11 @@ namespace ext {
template<>
struct PolymorphicBaseClass<Base> : PolymorphicDerivedClasses<Derived>
{
};
{};
template<>
struct PolymorphicBaseClass<Derived> : PolymorphicDerivedClasses<MoreDerived>
{
};
{};
}
}
@@ -743,6 +764,103 @@ TEST_F(SerializeExtensionStdSmartSharedPtr,
EXPECT_THAT(baseRes1.use_count(), Eq(0));
}
struct MightMatchVecLayout
{
size_t begin;
size_t end;
};
template<typename S>
void
serialize(S& s, MightMatchVecLayout& o)
{
s.template value<sizeof(size_t)>(o.begin);
s.template value<sizeof(size_t)>(o.end);
}
TEST_F(SerializeExtensionStdSmartSharedPtr,
NonPolymorphicObservedPointerMustMatchActuallyDeserializedObjectType)
{
std::shared_ptr<std::vector<uint8_t>> baseData1{ new std::vector<uint8_t>{
'a', 'b', 'c' } };
std::shared_ptr<MightMatchVecLayout> baseData2{ new MightMatchVecLayout{
1, 2 } };
auto& ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{}, [](auto& ser, std::vector<uint8_t>& o) {
ser.container1b(o, 100);
});
ser.ext(baseData2, StdSmartPtr{});
// hack a buffer, so that during deserialization we would point to already
// deserialized object
sctx.buf[5] = 0x01;
std::shared_ptr<std::vector<uint8_t>> baseRes1{};
std::shared_ptr<MightMatchVecLayout> baseRes2{};
auto& des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{}, [](auto& ser, std::vector<uint8_t>& o) {
ser.container1b(o, 100);
});
// if we would blindly trust the input (that the object is already
// deserialized), we would end up
// using memory of that object. In this case, we will get internal
// representation of std::vector.
// If this object is available to attacker, he'll be able to see leaked
// address of that object,
// allowing him to bypass ASLR.
des.ext(baseRes2, StdSmartPtr{});
EXPECT_THAT(baseRes2.get(), ::testing::IsNull());
auto err = des.adapter().error();
EXPECT_THAT(err, Eq(bitsery::ReaderError::InvalidPointer));
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(
SerializeExtensionStdSmartSharedPtr,
PolymorphicObservedPointerMustBeInInheritanceChainOfActuallyDeserializedObjectType1)
{
std::shared_ptr<Base> baseData1{ new MoreDerived{ 3, 7, 10 } };
std::shared_ptr<Base> baseData2{};
baseData2 = baseData1;
auto& ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
ser.ext(baseData2, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{};
std::shared_ptr<Derived> baseRes2{};
auto& des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
des.ext(baseRes2, StdSmartPtr{});
EXPECT_THAT(baseRes2.get(), ::testing::NotNull());
auto err = des.adapter().error();
EXPECT_THAT(err, Eq(bitsery::ReaderError::NoError));
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(
SerializeExtensionStdSmartSharedPtr,
PolymorphicObservedPointerMustBeInInheritanceChainOfActuallyDeserializedObjectType2)
{
std::shared_ptr<Base> baseData1{ new MoreDerived{ 3, 7, 10 } };
std::shared_ptr<Base> baseData2{};
baseData2 = baseData1;
auto& ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
ser.ext(baseData2, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{};
std::shared_ptr<DerivedSibling> baseRes2{};
auto& des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
des.ext(baseRes2, StdSmartPtr{});
EXPECT_THAT(baseRes2.get(), ::testing::IsNull());
auto err = des.adapter().error();
EXPECT_THAT(err, Eq(bitsery::ReaderError::InvalidPointer));
EXPECT_TRUE(isPointerContextValid());
}
struct TestSharedFromThis
: public std::enable_shared_from_this<TestSharedFromThis>
{