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: jobs:
build: build:
name: ${{ matrix.config.name }} name: ${{ matrix.config.name }}
runs-on: ubuntu-18.04 runs-on: ubuntu-24.04
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
config: config:
- name: "Ubuntu 18.04 with Clang 3.9" - name: "Ubuntu Latest with GCC 14"
cxx_ver: 11
compiler: clang
compiler_ver: 3.9
- name: "Ubuntu 18.04 with GCC 5.0"
cxx_ver: 11
compiler: gcc compiler: gcc
compiler_ver: 5 compiler_ver: 14
- name: "Ubuntu 18.04 with GCC 11.0" - name: "Ubuntu Latests with Clang 18"
cxx_ver: 17
compiler: gcc
compiler_ver: 11
- name: "Ubuntu 18.04 with Clang 13"
cxx_ver: 17
compiler: clang compiler: clang
compiler_ver: 13 compiler_ver: 18
steps: steps:
- name: Prepare specific Clang version - name: Prepare specific Clang version
if: ${{ matrix.config.compiler == 'clang' }} if: ${{ matrix.config.compiler == 'clang' }}
run: | 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 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/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 sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-${{ matrix.config.compiler_ver}} 100
- name: Prepare specific GCC version - name: Prepare specific GCC version
if: ${{ matrix.config.compiler == 'gcc' }} if: ${{ matrix.config.compiler == 'gcc' }}
run: | run: |
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt update
sudo apt install g++-${{ matrix.config.compiler_ver}} 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/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 sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-${{ matrix.config.compiler_ver}} 100
- name: Installing GTest - name: Installing GTest
run: | run: |
sudo add-apt-repository ppa:team-xbmc/ppa
sudo apt-get update sudo apt-get update
sudo apt-get install libgmock-dev sudo apt-get install libgmock-dev
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Configure - 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 - name: Build
run: cmake --build build run: cmake --build build
- name: Run tests - name: Run tests

View File

@@ -14,9 +14,9 @@ jobs:
run: | run: |
git clone https://github.com/google/googletest.git git clone https://github.com/google/googletest.git
cd googletest cd googletest
git checkout release-1.11.0 git checkout v1.14.0
cmake -S . -B build cmake -S . -B build
cmake --build build --target install sudo cmake --build build --target install
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Configure - name: Configure
run: cmake -S . -B build -DBITSERY_BUILD_TESTS=ON -DBITSERY_BUILD_EXAMPLES=ON -DCMAKE_CXX_STANDARD=17 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: | run: |
git clone https://github.com/google/googletest.git git clone https://github.com/google/googletest.git
cd googletest cd googletest
git checkout release-1.11.0 git checkout v1.14.0
cmake -S . -B build -Dgtest_force_shared_crt=ON cmake -S . -B build -Dgtest_force_shared_crt=ON
cmake --build build --config Release --target install cmake --build build --config Release --target install
- uses: actions/checkout@v3 - 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) # [5.2.3](https://github.com/fraillt/bitsery/compare/v5.2.2...v5.2.3) (2022-12-01)
### Improvements ### Improvements

View File

@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.1) cmake_minimum_required(VERSION 3.25)
project(bitsery project(bitsery
LANGUAGES CXX LANGUAGES CXX
VERSION 5.2.2) VERSION 5.2.5)
#======== build options =================================== #======== build options ===================================
option(BITSERY_BUILD_EXAMPLES "Build examples" OFF) 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. 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 ## License
**bitsery** is licensed under the [MIT license](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 #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#SOFTWARE. #SOFTWARE.
cmake_minimum_required(VERSION 3.1) cmake_minimum_required(VERSION 3.25)
project(bitsery_examples CXX) project(bitsery_examples CXX)
if (NOT TARGET Bitsery::bitsery) if (NOT TARGET Bitsery::bitsery)

View File

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

View File

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

View File

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

View File

@@ -23,6 +23,7 @@
#ifndef BITSERY_ADAPTER_BUFFER_H #ifndef BITSERY_ADAPTER_BUFFER_H
#define BITSERY_ADAPTER_BUFFER_H #define BITSERY_ADAPTER_BUFFER_H
#include "../bitsery.h"
#include "../details/adapter_bit_packing.h" #include "../details/adapter_bit_packing.h"
#include "../traits/core/traits.h" #include "../traits/core/traits.h"
#include <algorithm> #include <algorithm>
@@ -268,16 +269,16 @@ private:
void maybeResize(size_t newOffset, std::true_type) void maybeResize(size_t newOffset, std::true_type)
{ {
if (newOffset > _bufferSize) { if (newOffset > _bufferSize)
traits::BufferAdapterTraits<Buffer>::increaseBufferSize( BITSERY_UNLIKELY
*_buffer, _currOffset, newOffset); {
_beginIt = std::begin(*_buffer); doResize(newOffset);
_bufferSize = traits::ContainerTraits<Buffer>::size(*_buffer); }
}
} }
void maybeResize(size_t newOffset, std::false_type) void maybeResize(size_t newOffset, std::false_type)
{ {
static_cast<void>(newOffset);
assert(newOffset <= _bufferSize); assert(newOffset <= _bufferSize);
} }
@@ -288,6 +289,14 @@ private:
std::copy_n(data, size, _beginIt + static_cast<diff_t>(_currOffset)); std::copy_n(data, size, _beginIt + static_cast<diff_t>(_currOffset));
_currOffset = newOffset; _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_MAJOR_VERSION 5
#define BITSERY_MINOR_VERSION 2 #define BITSERY_MINOR_VERSION 2
#define BITSERY_PATCH_VERSION 2 #define BITSERY_PATCH_VERSION 5
#define BITSERY_QUOTE_MACRO(name) #name #define BITSERY_QUOTE_MACRO(name) #name
#define BITSERY_BUILD_VERSION_STR(major, minor, patch) \ #define BITSERY_BUILD_VERSION_STR(major, minor, patch) \
@@ -36,6 +36,67 @@
BITSERY_BUILD_VERSION_STR( \ BITSERY_BUILD_VERSION_STR( \
BITSERY_MAJOR_VERSION, BITSERY_MINOR_VERSION, BITSERY_PATCH_VERSION) 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 "deserializer.h"
#include "serializer.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 <algorithm>
#include <cassert> #include <cassert>
#include <climits> #include <climits>
#include <cstdint>
namespace bitsery { namespace bitsery {
@@ -144,10 +145,7 @@ struct SwapImpl
return static_cast<uint16_t>((value & 0x00ff) << 8 | (value & 0xff00) >> 8); return static_cast<uint16_t>((value & 0x00ff) << 8 | (value & 0xff00) >> 8);
} }
static uint8_t exec(uint8_t value) static uint8_t exec(uint8_t value) { return value; }
{
return value;
}
}; };
template<typename TValue> template<typename TValue>
@@ -194,10 +192,11 @@ getSystemEndianness()
: EndiannessType::BigEndian; : EndiannessType::BigEndian;
} }
template<typename Config> template<typename Config, typename T>
using ShouldSwap = using ShouldSwap =
std::integral_constant<bool, std::integral_constant<bool,
Config::Endianness != details::getSystemEndianness()>; Config::Endianness != details::getSystemEndianness() &&
sizeof(T) != 1>;
/** /**
* helper types to work with bits * helper types to work with bits
@@ -287,7 +286,7 @@ struct OutputAdapterBaseCRTP
{ {
static_assert(std::is_integral<T>(), ""); static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, ""); static_assert(sizeof(T) == SIZE, "");
writeSwappedValue(&v, ShouldSwap<typename Adapter::TConfig>{}); writeSwappedValue(&v, ShouldSwap<typename Adapter::TConfig, T>{});
} }
template<size_t SIZE, typename T> template<size_t SIZE, typename T>
@@ -295,7 +294,7 @@ struct OutputAdapterBaseCRTP
{ {
static_assert(std::is_integral<T>(), ""); static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, ""); static_assert(sizeof(T) == SIZE, "");
writeSwappedBuffer(buf, count, ShouldSwap<typename Adapter::TConfig>{}); writeSwappedBuffer(buf, count, ShouldSwap<typename Adapter::TConfig, T>{});
} }
template<typename T> template<typename T>
@@ -360,7 +359,7 @@ struct InputAdapterBaseCRTP
static_assert(sizeof(T) == SIZE, ""); static_assert(sizeof(T) == SIZE, "");
static_cast<Adapter*>(this)->template readInternalValue<sizeof(T)>( static_cast<Adapter*>(this)->template readInternalValue<sizeof(T)>(
reinterpret_cast<typename Adapter::TValue*>(&v)); 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> template<size_t SIZE, typename T>
@@ -370,7 +369,7 @@ struct InputAdapterBaseCRTP
static_assert(sizeof(T) == SIZE, ""); static_assert(sizeof(T) == SIZE, "");
static_cast<Adapter*>(this)->readInternalBuffer( static_cast<Adapter*>(this)->readInternalBuffer(
reinterpret_cast<typename Adapter::TValue*>(buf), sizeof(T) * count); 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> template<typename T>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -147,6 +147,7 @@ struct SmartPtrOwnerManager
[alloc, typeId](TElement* data) { alloc.deleteObject(data, typeId); }, [alloc, typeId](TElement* data) { alloc.deleteObject(data, typeId); },
pointer_utils::StdPolyAlloc<TElement>(memResource)); pointer_utils::StdPolyAlloc<TElement>(memResource));
state.obj = obj; state.obj = obj;
state.typeId = typeId;
} }
static void createSharedPolymorphic( static void createSharedPolymorphic(
@@ -162,6 +163,7 @@ struct SmartPtrOwnerManager
[alloc, handler](TElement* data) { handler->destroy(alloc, data); }, [alloc, handler](TElement* data) { handler->destroy(alloc, data); },
pointer_utils::StdPolyAlloc<TElement>(memResource)); pointer_utils::StdPolyAlloc<TElement>(memResource));
state.obj = obj; state.obj = obj;
state.typeId = handler->getDerivedTypeId();
} }
static void createShared(TSharedState& state, static void createShared(TSharedState& state,
@@ -176,6 +178,7 @@ struct SmartPtrOwnerManager
pointer_utils::StdPolyAlloc<TElement>(memResource)); pointer_utils::StdPolyAlloc<TElement>(memResource));
obj = res; obj = res;
state.obj = res; state.obj = res;
state.typeId = typeId;
} }
static void createSharedPolymorphic( static void createSharedPolymorphic(
@@ -191,6 +194,7 @@ struct SmartPtrOwnerManager
pointer_utils::StdPolyAlloc<TElement>(memResource)); pointer_utils::StdPolyAlloc<TElement>(memResource));
obj = res; obj = res;
state.obj = res; state.obj = res;
state.typeId = handler->getDerivedTypeId();
} }
static void saveToSharedState(TSharedState& state, T& obj) static void saveToSharedState(TSharedState& state, T& obj)
@@ -198,10 +202,24 @@ struct SmartPtrOwnerManager
state.obj = std::shared_ptr<TElement>(obj); 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) static void loadFromSharedState(TSharedState& state, T& obj)
{ {
// reinterpret_pointer_cast is only since c++17 auto v = state.obj.get();
auto p = reinterpret_cast<TElement*>(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); 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 // this class is used to store context for shared ptr owners
struct PointerSharedStateBase struct PointerSharedStateBase
{ {
size_t typeId{};
virtual ~PointerSharedStateBase() = default; virtual ~PointerSharedStateBase() = default;
}; };
@@ -117,16 +118,23 @@ struct PLCInfoSerializer : PLCInfo
size_t id; size_t id;
}; };
struct ObserverRef
{
std::reference_wrapper<void*> obj;
size_t baseTypeId;
};
struct PLCInfoDeserializer : PLCInfo struct PLCInfoDeserializer : PLCInfo
{ {
PLCInfoDeserializer(void* ptr, PLCInfoDeserializer(void* ptr,
size_t ownerTypeId_,
PointerOwnershipType ownershipType_, PointerOwnershipType ownershipType_,
MemResourceBase* memResource_) MemResourceBase* memResource_)
: PLCInfo(ownershipType_) : PLCInfo(ownershipType_)
, ownerPtr{ ptr } , ownerPtr{ ptr }
, ownerTypeId{ ownerTypeId_ }
, memResource{ memResource_ } , memResource{ memResource_ }
, observersList{ StdPolyAlloc<std::reference_wrapper<void*>>{ , observersList{ StdPolyAlloc<ObserverRef>{ memResource_ } } {};
memResource_ } } {};
// need to override these explicitly because we have pointer member // need to override these explicitly because we have pointer member
PLCInfoDeserializer(const PLCInfoDeserializer&) = delete; PLCInfoDeserializer(const PLCInfoDeserializer&) = delete;
@@ -137,30 +145,12 @@ struct PLCInfoDeserializer : PLCInfo
PLCInfoDeserializer& operator=(PLCInfoDeserializer&&) = default; 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; void* ownerPtr;
// used for polymorphic types in order to identify
// if shared objects can be assigned
size_t ownerTypeId;
MemResourceBase* memResource; MemResourceBase* memResource;
std::vector<std::reference_wrapper<void*>, std::vector<ObserverRef, StdPolyAlloc<ObserverRef>> observersList;
StdPolyAlloc<std::reference_wrapper<void*>>>
observersList;
std::unique_ptr<PointerSharedStateBase, PointerSharedStateDeleter> std::unique_ptr<PointerSharedStateBase, PointerSharedStateDeleter>
sharedState{}; sharedState{};
}; };
@@ -254,8 +244,8 @@ public:
PLCInfoDeserializer& getInfoById(size_t id, PointerOwnershipType ptrType) PLCInfoDeserializer& getInfoById(size_t id, PointerOwnershipType ptrType)
{ {
auto res = auto res = _idMap.emplace(
_idMap.emplace(id, PLCInfoDeserializer{ nullptr, ptrType, _memResource }); id, PLCInfoDeserializer{ nullptr, 0, ptrType, _memResource });
auto& ptrInfo = res.first->second; auto& ptrInfo = res.first->second;
if (!res.second) if (!res.second)
ptrInfo.update(ptrType); ptrInfo.update(ptrType);
@@ -329,8 +319,7 @@ public:
: std::integral_constant< : std::integral_constant<
bool, bool,
RTTI::template isPolymorphic<typename TPtrManager<T>::TElement>()> RTTI::template isPolymorphic<typename TPtrManager<T>::TElement>()>
{ {};
};
template<PointerOwnershipType Value> template<PointerOwnershipType Value>
using OwnershipType = std::integral_constant<PointerOwnershipType, Value>; using OwnershipType = std::integral_constant<PointerOwnershipType, Value>;
@@ -353,8 +342,8 @@ public:
if (ptr) { if (ptr) {
auto& ctx = ser.template context< auto& ctx = ser.template context<
pointer_utils::PointerLinkingContextSerialization>(); pointer_utils::PointerLinkingContextSerialization>();
auto& ptrInfo = auto& ptrInfo = ctx.getInfoByPtr(getRootPtr(ser, ptr, IsPolymorphic<T>{}),
ctx.getInfoByPtr(getBasePtr(ptr), TPtrManager<T>::getOwnership()); TPtrManager<T>::getOwnership());
details::writeSize(ser.adapter(), ptrInfo.id); details::writeSize(ser.adapter(), ptrInfo.id);
if (TPtrManager<T>::getOwnership() != PointerOwnershipType::Observer) { if (TPtrManager<T>::getOwnership() != PointerOwnershipType::Observer) {
if (!ptrInfo.isSharedProcessed) if (!ptrInfo.isSharedProcessed)
@@ -412,7 +401,7 @@ private:
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>(); const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
auto ptr = TPtrManager<TObj>::getPtr(obj); auto ptr = TPtrManager<TObj>::getPtr(obj);
TPtrManager<TObj>::destroyPolymorphic( TPtrManager<TObj>::destroyPolymorphic(
obj, memResource, ctx.getPolymorphicHandler(*ptr)); obj, memResource, ctx.getPolymorphicHandler(ptr));
} }
template<typename Des, typename TObj> template<typename Des, typename TObj>
@@ -427,16 +416,23 @@ private:
RTTI::template get<typename TPtrManager<TObj>::TElement>()); RTTI::template get<typename TPtrManager<TObj>::TElement>());
} }
template<typename T> template<typename Ser, typename T>
const void* getBasePtr(const T* ptr) const 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; 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> template<typename Ser, typename TPtr, typename Fnc>
void serializeImpl(Ser& ser, TPtr& ptr, Fnc&&, std::true_type) const void serializeImpl(Ser& ser, TPtr& ptr, Fnc&&, std::true_type) const
{ {
@@ -472,7 +468,13 @@ private:
memResource](const std::shared_ptr<PolymorphicHandlerBase>& handler) { memResource](const std::shared_ptr<PolymorphicHandlerBase>& handler) {
TPtrManager<T>::destroyPolymorphic(obj, memResource, 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> template<typename Des, typename T, typename Fnc>
@@ -485,17 +487,18 @@ private:
OwnershipType<PointerOwnershipType::Owner>) const OwnershipType<PointerOwnershipType::Owner>) const
{ {
auto ptr = TPtrManager<T>::getPtr(obj); auto ptr = TPtrManager<T>::getPtr(obj);
if (ptr) { if (!ptr) {
fnc(des, *ptr);
} else {
TPtrManager<T>::create( TPtrManager<T>::create(
obj, obj,
memResource, memResource,
RTTI::template get<typename TPtrManager<T>::TElement>()); RTTI::template get<typename TPtrManager<T>::TElement>());
ptr = TPtrManager<T>::getPtr(obj); 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> template<typename Des, typename T, typename Fnc>
@@ -507,8 +510,11 @@ private:
std::true_type, std::true_type,
OwnershipType<PointerOwnershipType::SharedOwner>) const 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) { if (!ptrInfo.sharedState) {
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
ctx.deserialize( ctx.deserialize(
des, des,
TPtrManager<T>::getPtr(obj), TPtrManager<T>::getPtr(obj),
@@ -522,12 +528,28 @@ private:
memResource](const std::shared_ptr<PolymorphicHandlerBase>& handler) { memResource](const std::shared_ptr<PolymorphicHandlerBase>& handler) {
TPtrManager<T>::destroyPolymorphic(obj, memResource, handler); TPtrManager<T>::destroyPolymorphic(obj, memResource, handler);
}); });
if (!ptrInfo.sharedState) if (!ptrInfo.sharedState) {
TPtrManager<T>::saveToSharedState( TPtrManager<T>::saveToSharedStatePolymorphic(
createAndGetSharedStateObj<T>(ptrInfo), obj); 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> template<typename Des, typename T, typename Fnc>
@@ -539,6 +561,8 @@ private:
std::false_type, std::false_type,
OwnershipType<PointerOwnershipType::SharedOwner>) const OwnershipType<PointerOwnershipType::SharedOwner>) const
{ {
const size_t baseTypeId =
RTTI::template get<typename TPtrManager<T>::TElement>();
if (!ptrInfo.sharedState) { if (!ptrInfo.sharedState) {
auto ptr = TPtrManager<T>::getPtr(obj); auto ptr = TPtrManager<T>::getPtr(obj);
if (ptr) { if (ptr) {
@@ -553,9 +577,16 @@ private:
ptr = TPtrManager<T>::getPtr(obj); ptr = TPtrManager<T>::getPtr(obj);
} }
fnc(des, *ptr); 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> template<typename Des, typename T, typename Fnc, typename isPolymorph>
@@ -577,17 +608,85 @@ private:
OwnershipType<PointerOwnershipType::SharedOwner>{}); OwnershipType<PointerOwnershipType::SharedOwner>{});
} }
template<typename Des, typename T, typename Fnc, typename isPolymorphic> template<typename Des, typename T, typename Fnc>
void deserializeImpl(MemResourceBase*, void deserializeImpl(MemResourceBase*,
PLCInfoDeserializer& ptrInfo, PLCInfoDeserializer& ptrInfo,
Des&, Des& des,
T& obj, T& obj,
Fnc&&, Fnc&&,
isPolymorphic, std::false_type,
OwnershipType<PointerOwnershipType::Observer>) const OwnershipType<PointerOwnershipType::Observer>) const
{ {
ptrInfo.processObserver( auto baseTypeId = RTTI::template get<typename TPtrManager<T>::TElement>();
reinterpret_cast<void*&>(TPtrManager<T>::getPtrRef(obj))); 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> template<typename T>

View File

@@ -35,8 +35,7 @@ namespace ext {
// helper type, that contains list of types // helper type, that contains list of types
template<typename...> template<typename...>
struct PolymorphicClassesList struct PolymorphicClassesList
{ {};
};
// specialize for your base class by deriving from PolymorphicDerivedClasses // specialize for your base class by deriving from PolymorphicDerivedClasses
// with list of derivatives that DIRECTLY inherits from your base class. // 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 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; 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 class PolymorphicHandler : public PolymorphicHandlerBase
{ {
public: public:
@@ -98,6 +107,19 @@ public:
static_cast<TSerializer*>(ser)->object(*fromBase(obj)); 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: private:
TDerived* fromBase(void* obj) const 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> template<typename RTTI>
class PolymorphicContext class PolymorphicContext
{ {
@@ -138,18 +195,20 @@ private:
template<typename TSerializer, template<typename TSerializer,
template<typename> template<typename>
class THierarchy, class THierarchy,
typename TRoot,
typename TBase, typename TBase,
typename TDerived> typename TDerived>
void add() void add()
{ {
addToMap<TSerializer, TBase, TDerived>(std::is_abstract<TDerived>{}); addToMap<TSerializer, TRoot, TBase, TDerived>(std::is_abstract<TDerived>{});
addChilds<TSerializer, THierarchy, TBase, TDerived>( addChilds<TSerializer, THierarchy, TRoot, TBase, TDerived>(
typename THierarchy<TDerived>::Childs{}); typename THierarchy<TDerived>::Childs{});
} }
template<typename TSerializer, template<typename TSerializer,
template<typename> template<typename>
class THierarchy, class THierarchy,
typename TRoot,
typename TBase, typename TBase,
typename TDerived, typename TDerived,
typename T1, typename T1,
@@ -159,26 +218,30 @@ private:
static_assert(std::is_base_of<TDerived, T1>::value, static_assert(std::is_base_of<TDerived, T1>::value,
"PolymorphicBaseClass<TBase> must derive a list of derived " "PolymorphicBaseClass<TBase> must derive a list of derived "
"classes from TBase."); "classes from TBase.");
add<TSerializer, THierarchy, TBase, T1>(); add<TSerializer, THierarchy, TRoot, TBase, T1>();
addChilds<TSerializer, THierarchy, TBase, TDerived>( addChilds<TSerializer, THierarchy, TRoot, TBase, TDerived>(
PolymorphicClassesList<Tn...>{}); PolymorphicClassesList<Tn...>{});
// iterate through derived class hierarchy as well add<TSerializer, THierarchy, TRoot, T1, T1>();
add<TSerializer, THierarchy, T1, T1>();
} }
template<typename TSerializer, template<typename TSerializer,
template<typename> template<typename>
class THierarchy, class THierarchy,
typename TRoot,
typename TBase, typename TBase,
typename TDerived> typename TDerived>
void addChilds(PolymorphicClassesList<>) void addChilds(PolymorphicClassesList<>)
{ {
} }
template<typename TSerializer, typename TBase, typename TDerived> template<typename TSerializer,
typename TRoot,
typename TBase,
typename TDerived>
void addToMap(std::false_type) 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>(), BaseToDerivedKey key{ RTTI::template get<TBase>(),
RTTI::template get<TDerived>() }; RTTI::template get<TDerived>() };
pointer_utils::StdPolyAlloc<THandler> alloc{ _memResource }; 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) 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; MemResourceBase* _memResource;
@@ -270,7 +348,7 @@ public:
typename... Tn> typename... Tn>
void registerBasesList(PolymorphicClassesList<T1, Tn...>) void registerBasesList(PolymorphicClassesList<T1, Tn...>)
{ {
add<TSerializer, THierarchy, T1, T1>(); add<TSerializer, THierarchy, T1, T1, T1>();
registerBasesList<TSerializer, THierarchy>(PolymorphicClassesList<Tn...>{}); 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> template<typename Serializer, typename TBase>
void serialize(Serializer& ser, TBase& obj) const 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 object is null or different type, create new and assign it
if (obj == nullptr || RTTI::template get<TBase>(*obj) != derivedHash) { if (obj == nullptr || RTTI::template get<TBase>(*obj) != derivedHash) {
if (obj) { if (obj) {
destroyFnc(getPolymorphicHandler(*obj)); destroyFnc(getPolymorphicHandler(obj));
} }
obj = createFnc(handler); obj = createFnc(handler);
} }
@@ -350,12 +416,33 @@ public:
template<typename TBase> template<typename TBase>
const std::shared_ptr<PolymorphicHandlerBase>& getPolymorphicHandler( const std::shared_ptr<PolymorphicHandlerBase>& getPolymorphicHandler(
TBase& obj) const TBase* obj) const
{ {
auto deleteHandlerIt = _baseToDerivedMap.find(BaseToDerivedKey{ auto it = _baseToDerivedMap.find(BaseToDerivedKey{
RTTI::template get<TBase>(), RTTI::template get<TBase>(obj) }); RTTI::template get<TBase>(), RTTI::template get<TBase>(*obj) });
assert(deleteHandlerIt != _baseToDerivedMap.end()); assert(it != _baseToDerivedMap.end());
return deleteHandlerIt->second; 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> template<typename T, size_t N>
struct ContainerTraits<std::array<T, N>> struct ContainerTraits<std::array<T, N>>
: public StdContainer<std::array<T, N>, false, true> : public StdContainer<std::array<T, N>, false, true>
{ {};
};
template<typename T, size_t N> template<typename T, size_t N>
struct BufferAdapterTraits<std::array<T, N>> struct BufferAdapterTraits<std::array<T, N>>
: public StdContainerForBufferAdapter<std::array<T, N>> : public StdContainerForBufferAdapter<std::array<T, N>>
{ {};
};
} }
} }

View File

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

View File

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

View File

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

View File

@@ -32,21 +32,18 @@ namespace traits {
template<typename T, typename Allocator> template<typename T, typename Allocator>
struct ContainerTraits<std::vector<T, Allocator>> struct ContainerTraits<std::vector<T, Allocator>>
: public StdContainer<std::vector<T, Allocator>, true, true> : public StdContainer<std::vector<T, Allocator>, true, true>
{ {};
};
// bool vector is not contiguous, do not copy it directly to buffer // bool vector is not contiguous, do not copy it directly to buffer
template<typename Allocator> template<typename Allocator>
struct ContainerTraits<std::vector<bool, Allocator>> struct ContainerTraits<std::vector<bool, Allocator>>
: public StdContainer<std::vector<bool, Allocator>, true, false> : public StdContainer<std::vector<bool, Allocator>, true, false>
{ {};
};
template<typename T, typename Allocator> template<typename T, typename Allocator>
struct BufferAdapterTraits<std::vector<T, Allocator>> struct BufferAdapterTraits<std::vector<T, Allocator>>
: public StdContainerForBufferAdapter<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 #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#SOFTWARE. #SOFTWARE.
cmake_minimum_required(VERSION 3.11) cmake_minimum_required(VERSION 3.25)
project(bitsery_tests project(bitsery_tests
LANGUAGES CXX) LANGUAGES CXX)

View File

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

View File

@@ -29,6 +29,7 @@
#include <bitsery/brief_syntax/list.h> #include <bitsery/brief_syntax/list.h>
#include <bitsery/brief_syntax/map.h> #include <bitsery/brief_syntax/map.h>
#include <bitsery/brief_syntax/memory.h> #include <bitsery/brief_syntax/memory.h>
#include <bitsery/brief_syntax/queue.h> #include <bitsery/brief_syntax/queue.h>
#include <bitsery/brief_syntax/set.h> #include <bitsery/brief_syntax/set.h>
#include <bitsery/brief_syntax/stack.h> #include <bitsery/brief_syntax/stack.h>
@@ -37,8 +38,12 @@
#include <bitsery/brief_syntax/unordered_set.h> #include <bitsery/brief_syntax/unordered_set.h>
#include <bitsery/brief_syntax/vector.h> #include <bitsery/brief_syntax/vector.h>
#if __cplusplus > 201402L #if __cplusplus > 201402L
#include <bitsery/brief_syntax/optional.h>
#include <bitsery/brief_syntax/tuple.h> #include <bitsery/brief_syntax/tuple.h>
#include <bitsery/brief_syntax/variant.h> #include <bitsery/brief_syntax/variant.h>
#if __cplusplus > 202002L
#include <bitsery/brief_syntax/bitset.h>
#endif
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
#pragma message( \ #pragma message( \
"C++17 and /Zc:__cplusplus option is required to enable std::tuple and std::variant brief syntax tests") "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); 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 #endif
TEST(BriefSyntax, NestedTypes) TEST(BriefSyntax, NestedTypes)

View File

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

View File

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

View File

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

View File

@@ -458,6 +458,23 @@ TEST_F(SerializeExtensionPointerDeserialization, PointerObserver)
EXPECT_THAT(pr3, Eq(&r3)); 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 struct Test1Data
{ {
std::vector<MyStruct1> vdata; std::vector<MyStruct1> vdata;

View File

@@ -147,21 +147,17 @@ namespace ext {
template<> template<>
struct PolymorphicBaseClass<Base> struct PolymorphicBaseClass<Base>
: PolymorphicDerivedClasses<Derived1, Derived2> : PolymorphicDerivedClasses<Derived1, Derived2>
{ {};
};
// this is commented on purpose, to test scenario when base class is registered template<>
// (Base) but using instance of Derived1 which is not registered as base struct PolymorphicBaseClass<Derived1>
// template<> : PolymorphicDerivedClasses<MultipleVirtualInheritance>
// struct PolymorphicBaseClass<Derived1> : {};
// PolymorphicDerivedClasses<MultipleVirtualInheritance> {
// };
template<> template<>
struct PolymorphicBaseClass<Derived2> struct PolymorphicBaseClass<Derived2>
: PolymorphicDerivedClasses<MultipleVirtualInheritance> : PolymorphicDerivedClasses<MultipleVirtualInheritance>
{ {};
};
} }
} }
@@ -373,3 +369,62 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes,
EXPECT_THAT(sctx.des->adapter().error(), EXPECT_THAT(sctx.des->adapter().error(),
Eq(bitsery::ReaderError::InvalidPointer)); 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<> template<>
struct PolymorphicBaseClass<Base> struct PolymorphicBaseClass<Base>
: PolymorphicDerivedClasses<Derived1, Derived2> : PolymorphicDerivedClasses<Derived1, Derived2>
{ {};
};
template<> template<>
struct PolymorphicBaseClass<PolyPtrWithPolyPtrBase> struct PolymorphicBaseClass<PolyPtrWithPolyPtrBase>
: PolymorphicDerivedClasses<DerivedPolyPtrWithPolyPtr> : PolymorphicDerivedClasses<DerivedPolyPtrWithPolyPtr>
{ {};
};
} }
} }

View File

@@ -23,6 +23,8 @@
#include <bitsery/ext/inheritance.h> #include <bitsery/ext/inheritance.h>
#include <bitsery/ext/pointer.h> #include <bitsery/ext/pointer.h>
#include <bitsery/ext/std_smart_ptr.h> #include <bitsery/ext/std_smart_ptr.h>
#include <bitsery/traits/string.h>
#include <bitsery/traits/vector.h>
#include "serialization_test_utils.h" #include "serialization_test_utils.h"
#include <gmock/gmock.h> #include <gmock/gmock.h>
@@ -77,6 +79,27 @@ serialize(S& s, Derived& o)
s.value1b(o.y); 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 struct MoreDerived : Derived
{ {
uint8_t z{}; uint8_t z{};
@@ -106,13 +129,11 @@ namespace ext {
template<> template<>
struct PolymorphicBaseClass<Base> : PolymorphicDerivedClasses<Derived> struct PolymorphicBaseClass<Base> : PolymorphicDerivedClasses<Derived>
{ {};
};
template<> template<>
struct PolymorphicBaseClass<Derived> : PolymorphicDerivedClasses<MoreDerived> struct PolymorphicBaseClass<Derived> : PolymorphicDerivedClasses<MoreDerived>
{ {};
};
} }
} }
@@ -743,6 +764,103 @@ TEST_F(SerializeExtensionStdSmartSharedPtr,
EXPECT_THAT(baseRes1.use_count(), Eq(0)); 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 struct TestSharedFromThis
: public std::enable_shared_from_this<TestSharedFromThis> : public std::enable_shared_from_this<TestSharedFromThis>
{ {