format code base in Mozilla style

This commit is contained in:
Mindaugas Vinkelis
2022-12-01 13:50:03 +02:00
parent d690908541
commit 3e02d0ca44
106 changed files with 15083 additions and 12518 deletions

192
.clang-format Normal file
View File

@@ -0,0 +1,192 @@
---
Language: Cpp
# BasedOnStyle: Mozilla
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveMacros: None
AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: None
AlignConsecutiveDeclarations: None
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: TopLevel
AlwaysBreakAfterReturnType: TopLevel
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
AttributeMacros:
- __capability
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
AfterCaseLabel: false
AfterClass: true
AfterControlStatement: Never
AfterEnum: true
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: true
AfterExternBlock: true
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: false
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: true
BreakBeforeBraces: Mozilla
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeComma
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
QualifierAlignment: Leave
CompactNamespaces: false
ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 2
Cpp11BracedListStyle: false
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
PackConstructorInitializers: BinPack
BasedOnStyle: ''
ConstructorInitializerAllOnOneLineOrOnePerLine: false
AllowAllConstructorInitializersOnNextLine: true
FixNamespaceComments: false
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseLabels: true
IndentCaseBlocks: false
IndentGotoLabels: true
IndentPPDirectives: None
IndentExternBlock: AfterExternBlock
IndentRequires: false
IndentWidth: 2
IndentWrappedFunctionNames: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
LambdaBodyIndentation: Signature
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: false
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PenaltyIndentedWhitespace: 0
PointerAlignment: Left
PPIndentWidth: -1
ReferenceAlignment: Pointer
ReflowComments: true
RemoveBracesLLVM: false
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
BeforeNonEmptyParentheses: false
SpaceAroundPointerQualifiers: Default
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
BitFieldColonSpacing: Both
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseCRLF: false
UseTab: Never
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE
- NS_SWIFT_NAME
- CF_SWIFT_NAME
...

View File

@@ -2,13 +2,20 @@
#include <bitsery/bitsery.h>
// in ordered to serialize/deserialize data to buffer, include buffer adapter
#include <bitsery/adapter/buffer.h>
//bitsery itself doesn't is lightweight, and doesnt include any unnessessary files,
//traits helps library to know how to use types correctly,
//in this case we'll be using vector both, to serialize/deserialize data and to store use as a buffer.
// bitsery itself doesn't is lightweight, and doesnt include any unnessessary
// files, traits helps library to know how to use types correctly, in this case
// we'll be using vector both, to serialize/deserialize data and to store use as
// a buffer.
#include <bitsery/traits/vector.h>
enum class MyEnum:uint16_t { V1,V2,V3 };
struct MyStruct {
enum class MyEnum : uint16_t
{
V1,
V2,
V3
};
struct MyStruct
{
uint32_t i;
MyEnum e;
std::vector<float> fs;
@@ -16,10 +23,13 @@ struct MyStruct {
// define how object should be serialized/deserialized
template<typename S>
void serialize(S& s, MyStruct& o) {
void
serialize(S& s, MyStruct& o)
{
s.value4b(o.i); // fundamental types (ints, floats, enums) of size 4b
s.value2b(o.e);
s.container4b(o.fs, 10);//resizable containers also requires maxSize, to make it safe from buffer-overflow attacks
s.container4b(o.fs, 10); // resizable containers also requires maxSize, to
// make it safe from buffer-overflow attacks
}
// some helper types
@@ -27,7 +37,9 @@ using Buffer = std::vector<uint8_t>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
int main() {
int
main()
{
// set some random data
MyStruct data{ 8941, MyEnum::V2, { 15.0f, -8.5f, 0.045f } };
MyStruct res{};
@@ -40,8 +52,10 @@ int main() {
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
// same as serialization, but returns deserialization state as a pair
//first = error code, second = is buffer was successfully read from begin to the end.
auto state = bitsery::quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
// first = error code, second = is buffer was successfully read from begin to
// the end.
auto state = bitsery::quickDeserialization<InputAdapter>(
{ buffer.begin(), writtenSize }, res);
assert(state.first == bitsery::ReaderError::NoError && state.second);
assert(data.fs == res.fs && data.i == res.i && data.e == res.e);

View File

@@ -1,5 +1,5 @@
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/bitsery.h>
// we'll be using std::array as a buffer type, so include traits for this
#include <bitsery/traits/array.h>
#include <bitsery/traits/string.h>
@@ -9,27 +9,36 @@
namespace MyTypes {
struct Vec3 { float x, y, z; };
struct Vec3
{
float x, y, z;
};
struct Monster {
struct Monster
{
Vec3 pos;
std::vector<Vec3> path;
std::string name;
};
template<typename S>
void serialize(S& s, MyTypes::Vec3 &o) {
void
serialize(S& s, MyTypes::Vec3& o)
{
s.value4b(o.x);
s.value4b(o.y);
s.value4b(o.z);
}
template<typename S>
void serialize (S& s, Monster& o) {
void
serialize(S& s, Monster& o)
{
s.text1b(o.name, 20);
s.object(o.pos);
// compress path in a range of -1.0 .. 1.0 with 0.01 precision
//enableBitPacking creates separate serializer/deserializer object, that contains bit packing operations
// enableBitPacking creates separate serializer/deserializer object, that
// contains bit packing operations
s.enableBitPacking([&o](typename S::BPEnabledType& sbp) {
sbp.container(o.path, 1000, [](typename S::BPEnabledType& sbp, Vec3& vec3) {
constexpr bitsery::ext::ValueRange<float> range{ -1.0f, 1.0f, 0.01f };
@@ -46,7 +55,9 @@ using Buffer = std::array<uint8_t, 10000>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
int main() {
int
main()
{
// set some random data
MyTypes::Monster data{};
data.name = "lew";
@@ -56,7 +67,8 @@ int main() {
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
MyTypes::Monster res{};
auto state = bitsery::quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
auto state = bitsery::quickDeserialization<InputAdapter>(
{ buffer.begin(), writtenSize }, res);
assert(state.first == bitsery::ReaderError::NoError && state.second);
}

View File

@@ -1,26 +1,33 @@
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/bitsery.h>
// to use brief syntax always include this header
#include <bitsery/brief_syntax.h>
// we also need additional traits to work with container types,
//instead of including <bitsery/traits/vector.h> for vector traits, now we also need traits to work with brief_syntax types.
//so include everything from <bitsery/brief_syntax/...> instead of <bitsery/traits/...>
//otherwise we'll get static assert error, saying to define serialize function.
// instead of including <bitsery/traits/vector.h> for vector traits, now we also
// need traits to work with brief_syntax types. so include everything from
// <bitsery/brief_syntax/...> instead of <bitsery/traits/...> otherwise we'll
// get static assert error, saying to define serialize function.
#include <bitsery/brief_syntax/vector.h>
enum class MyEnum:uint16_t { V1,V2,V3 };
struct MyStruct {
enum class MyEnum : uint16_t
{
V1,
V2,
V3
};
struct MyStruct
{
uint32_t i;
MyEnum e;
std::vector<float> fs;
// define serialize function as usual
template<typename S>
void serialize(S& s) {
void serialize(S& s)
{
// now we can use brief syntax with
s(i, e, fs);
}
};
// some helper types
@@ -28,7 +35,9 @@ using Buffer = std::vector<uint8_t>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
int main() {
int
main()
{
// set some random data
MyStruct data{ 8941, MyEnum::V2, { 15.0f, -8.5f, 0.045f } };
MyStruct res{};
@@ -37,7 +46,8 @@ int main() {
Buffer buffer;
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
auto state = bitsery::quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
auto state = bitsery::quickDeserialization<InputAdapter>(
{ buffer.begin(), writtenSize }, res);
assert(state.first == bitsery::ReaderError::NoError && state.second);
assert(data.fs == res.fs && data.i == res.i && data.e == res.e);

View File

@@ -1,5 +1,5 @@
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/bitsery.h>
#include <bitsery/traits/vector.h>
// include extensions to work with tuples and variants
// these extesions only work with C++17
@@ -10,17 +10,21 @@
// let's include this extension to make it more interesting :)
#include <bitsery/ext/compact_value.h>
struct MyStruct {
struct MyStruct
{
std::vector<int32_t> v{};
float f{};
bool operator==(const MyStruct& rhs) const {
bool operator==(const MyStruct& rhs) const
{
return v == rhs.v && f == rhs.f;
}
};
template<typename S>
void serialize(S& s, MyStruct& o) {
void
serialize(S& s, MyStruct& o)
{
s.container4b(o.v, 1000);
s.value4b(o.f);
}
@@ -29,30 +33,43 @@ void serialize(S& s, MyStruct& o) {
using MyTuple = std::tuple<float, MyStruct>;
using MyVariant = std::variant<int64_t, MyTuple, MyStruct>;
// define default serialize function for MyVariant, so that we could use quickSerialization/Deserialization functions
// define default serialize function for MyVariant, so that we could use
// quickSerialization/Deserialization functions
template<typename S>
void serialize(S& s, MyVariant& o) {
// in order to serialize a variant, it needs to know how to do it for all types
// we can do this simply by providing any callable object, that accepts serializer and type as arguments
s.ext(o, bitsery::ext::StdVariant{
void
serialize(S& s, MyVariant& o)
{
// in order to serialize a variant, it needs to know how to do it for all
// types we can do this simply by providing any callable object, that accepts
// serializer and type as arguments
s.ext(
o,
bitsery::ext::StdVariant{
// specify how to serialize tuple by creating a lambda
[](S& s, MyTuple& o) {
// StdTuple is used exactly the same as StdVariant
s.ext(o, bitsery::ext::StdTuple{
s.ext(
o,
bitsery::ext::StdTuple{
// this is convenient callable object to specify integral value size
// it is different equivalent to lambda [](auto& s, float&o) { s.value4b(o);}
// it is different equivalent to lambda [](auto& s, float&o) {
// s.value4b(o);}
bitsery::ext::OverloadValue<float, 4>{},
// it is not required to provide MyStruct overload, because it we have defined 'serialize' function for it
// it is not required to provide MyStruct overload, because it we
// have defined 'serialize' function for it
});
},
// this might also be useful if you want to overload using extension
bitsery::ext::OverloadExtValue<int64_t, 8, bitsery::ext::CompactValue>{},
// you can even go further and instead of writing lambda for MyTuple you can as well compose the same functionality
// you can even go further and instead of writing lambda for MyTuple you
// can as well compose the same functionality
// with OverloadExtObject, like this:
// (comment out MyTuple lambda, and uncomment this)
// ext::OverloadExtObject<MyTuple, ext::StdTuple<ext::OverloadValue<float, 4>>>{},
// ext::OverloadExtObject<MyTuple, ext::StdTuple<ext::OverloadValue<float,
// 4>>>{},
// we can also override default 'serialize' function by creating an overloading for that type
// we can also override default 'serialize' function by creating an
// overloading for that type
[](S& s, MyStruct& o) {
s.value4b(o.f);
s.container(o.v, 1000, [](S& s, int32_t& v) {
@@ -62,26 +79,29 @@ void serialize(S& s, MyVariant& o) {
// NOTE.
// it is possible to provide "auto" as type parameter
// this will allow you to override all default 'serialize' functions
// but in this case it will not be called, because we have explicitly provided overloads for all variant types
// also note, that first parameter (serializer) is also "auto", this is required, so that it would be least specialized case
// otherwise it will not compile if you any ext::Overload* helper defined, because it will have ambiguous definitions
// (ext::OverLoad* defines (templated_type& s, concrete_type& o) and lambda would be (concrete_type& s, templated_type& o))
[](auto& , auto&) {
assert(false);
// but in this case it will not be called, because we have explicitly
// provided overloads for all variant types
// also note, that first parameter (serializer) is also "auto", this is
// required, so that it would be least specialized case
// otherwise it will not compile if you any ext::Overload* helper defined,
// because it will have ambiguous definitions
// (ext::OverLoad* defines (templated_type& s, concrete_type& o) and
// lambda would be (concrete_type& s, templated_type& o))
[](auto&, auto&) { assert(false); } });
}
});
}
// some helper types
using Buffer = std::vector<uint8_t>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
int main() {
int
main()
{
// set some random data
MyVariant data{ MyTuple{-7549, {{-451, 2, 968, 75, 4, 156, 49}, 874.4f}} };
MyVariant data{ MyTuple{ -7549,
{ { -451, 2, 968, 75, 4, 156, 49 }, 874.4f } } };
MyVariant res{};
// create buffer to store data
@@ -92,19 +112,24 @@ int main() {
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
// same as serialization, but returns deserialization state as a pair
//first = error code, second = is buffer was successfully read from begin to the end.
auto state = bitsery::quickDeserialization<InputAdapter>({ buffer.begin(), writtenSize }, res);
// first = error code, second = is buffer was successfully read from begin to
// the end.
auto state = bitsery::quickDeserialization<InputAdapter>(
{ buffer.begin(), writtenSize }, res);
assert(state.first == bitsery::ReaderError::NoError && state.second);
assert(data == res);
}
#else
#if defined(_MSC_VER)
#pragma message("C++17 and /Zc:__cplusplus option is required to enable this example")
#pragma message( \
"C++17 and /Zc:__cplusplus option is required to enable this example")
#else
#pragma message("C++17 is required to enable this example")
#endif
int main() {
int
main()
{
return 0;
}
#endif

View File

@@ -1,5 +1,5 @@
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/bitsery.h>
#include <bitsery/traits/string.h>
#include <bitsery/traits/vector.h>
@@ -8,10 +8,15 @@
namespace MyTypes {
struct Monster {
struct Monster
{
Monster() = default;
Monster(std::string _name, uint32_t minDmg, uint32_t maxDmg)
:name{_name}, minDamage{minDmg}, maxDamage{maxDmg} {}
: name{ _name }
, minDamage{ minDmg }
, maxDamage{ maxDmg }
{
}
std::string name{};
uint32_t minDamage{};
@@ -19,29 +24,36 @@ namespace MyTypes {
//...
};
struct GameState {
struct GameState
{
std::vector<Monster> monsters;
};
// default flow for monster
template<typename S>
void serialize (S& s, Monster& o) {
void
serialize(S& s, Monster& o)
{
s.text1b(o.name, 20);
s.value4b(o.minDamage);
s.value4b(o.maxDamage);
}
template<typename S>
void serialize(S& s, GameState &o) {
void
serialize(S& s, GameState& o)
{
// we can have multiple types in context with std::tuple
// if data type doesn't match then it will be compile time error
//NOTE: if context is optional then you can call contextOrNull<T>, and it will return null if T doesn't exists
// NOTE: if context is optional then you can call contextOrNull<T>, and it
// will return null if T doesn't exists
auto maxMonsters = s.template context<int>();
auto& dmgRange = s.template context<std::pair<uint32_t, uint32_t>>();
s.container(o.monsters, maxMonsters, [&dmgRange](S& s, Monster& m) {
s.text1b(m.name, 20);
//we know min/max damage range for monsters, so we can use this range instead of full value
// we know min/max damage range for monsters, so we can use this range
// instead of full value
bitsery::ext::ValueRange<uint32_t> range{ dmgRange.first, dmgRange.second };
// enable bit packing
s.enableBitPacking([&m, &range](typename S::BPEnabledType& sbp) {
@@ -71,7 +83,9 @@ using Buffer = std::vector<uint8_t>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
int main() {
int
main()
{
MyTypes::GameState data{};
data.monsters.push_back({ "weaksy", 100, 200 });
@@ -86,13 +100,14 @@ int main() {
std::get<1>(ctx).first = 100;
std::get<1>(ctx).second = 1000;
// create buffer to store data to
Buffer buffer{};
auto writtenSize = bitsery::quickSerialization(ctx, OutputAdapter{buffer}, data);
auto writtenSize =
bitsery::quickSerialization(ctx, OutputAdapter{ buffer }, data);
MyTypes::GameState res{};
auto state = bitsery::quickDeserialization(ctx, InputAdapter{buffer.begin(), writtenSize}, res);
auto state = bitsery::quickDeserialization(
ctx, InputAdapter{ buffer.begin(), writtenSize }, res);
assert(state.first == bitsery::ReaderError::NoError && state.second);
}

View File

@@ -4,8 +4,14 @@
#include <fstream>
#include <iostream>
enum class MyEnum:uint16_t { V1,V2,V3 };
struct MyStruct {
enum class MyEnum : uint16_t
{
V1,
V2,
V3
};
struct MyStruct
{
uint32_t i;
MyEnum e;
double f;
@@ -13,13 +19,17 @@ struct MyStruct {
// define how object should be serialized/deserialized
template<typename S>
void serialize(S& s, MyStruct& o) {
void
serialize(S& s, MyStruct& o)
{
s.value4b(o.i);
s.value2b(o.e);
s.value8b(o.f);
}
int main() {
int
main()
{
// set some random data
MyStruct data{ 8941, MyEnum::V2, 0.045 };
MyStruct res{};
@@ -32,7 +42,8 @@ int main() {
return 0;
}
//we cannot use quick serialization function, because streams cannot use writtenBytesCount method
// we cannot use quick serialization function, because streams cannot use
// writtenBytesCount method
bitsery::Serializer<bitsery::OutputBufferedStreamAdapter> ser{ s };
ser.object(data);
// flush to writer
@@ -47,8 +58,10 @@ int main() {
}
// same as serialization, but returns deserialization state as a pair
//first = error code, second = is buffer was successfully read from begin to the end.
auto state = bitsery::quickDeserialization<bitsery::InputStreamAdapter>(s, res);
// first = error code, second = is buffer was successfully read from begin to
// the end.
auto state =
bitsery::quickDeserialization<bitsery::InputStreamAdapter>(s, res);
assert(state.first == bitsery::ReaderError::NoError && state.second);
assert(data.f == res.f && data.i == res.i && data.e == res.e);

View File

@@ -1,8 +1,8 @@
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/bitsery.h>
// include traits for types, that we'll be using
#include <bitsery/traits/string.h>
#include <bitsery/traits/array.h>
#include <bitsery/traits/string.h>
#include <bitsery/traits/vector.h>
// include extension that will allow to have backward/forward compatibility
#include <bitsery/ext/growable.h>
@@ -10,20 +10,35 @@
namespace MyTypes {
// define data
enum Color:uint8_t { Red, Green, Blue };
enum Color : uint8_t
{
Red,
Green,
Blue
};
struct Vec3 { float x, y, z; };
struct Vec3
{
float x, y, z;
};
struct Weapon {
struct Weapon
{
std::string name{};
int16_t damage{};
Weapon() = default;
Weapon(const std::string& _name, int16_t dmg):name{_name}, damage{dmg} {}
Weapon(const std::string& _name, int16_t dmg)
: name{ _name }
, damage{ dmg }
{
}
private:
// define serialize function as private, and give access to bitsery
friend bitsery::Access;
template<typename S>
void serialize (S& s) {
void serialize(S& s)
{
// forward/backward compatibility for monsters
s.ext(*this, bitsery::ext::Growable{}, [](S& s, Weapon& o1) {
s.text1b(o1.name, 20);
@@ -32,7 +47,8 @@ namespace MyTypes {
}
};
struct Monster {
struct Monster
{
Vec3 pos;
int16_t mana;
int16_t hp;
@@ -45,14 +61,18 @@ namespace MyTypes {
};
template<typename S>
void serialize(S& s, Vec3& o) {
void
serialize(S& s, Vec3& o)
{
s.value4b(o.x);
s.value4b(o.y);
s.value4b(o.z);
}
template<typename S>
void serialize (S& s, Monster& o) {
void
serialize(S& s, Monster& o)
{
// forward/backward compatibility for monsters
s.ext(o, bitsery::ext::Growable{}, [](S& s, Monster& o1) {
s.value1b(o1.color);
@@ -73,7 +93,9 @@ using Buffer = std::array<uint8_t, 10000>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
int main() {
int
main()
{
// set some random data
MyTypes::Monster data{};
data.name = "lew";
@@ -81,12 +103,14 @@ int main() {
// create buffer to store data to
Buffer buffer{};
//since we're using different configuration, we cannot use quickSerialization function.
// since we're using different configuration, we cannot use quickSerialization
// function.
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
MyTypes::Monster res{};
// deserialize
auto state = bitsery::quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
auto state = bitsery::quickDeserialization<InputAdapter>(
{ buffer.begin(), writtenSize }, res);
assert(state.first == bitsery::ReaderError::NoError && state.second);
}

View File

@@ -1,65 +1,88 @@
//
// this example covers all the corner cases that can happen using inheritance
//in reality virtual inherintance is usually avoided, so your code would look much simpler.
// in reality virtual inherintance is usually avoided, so your code would look
// much simpler.
//
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/bitsery.h>
#include <bitsery/traits/vector.h>
// include inheritance extension
//this header contains two extensions, that specifies inheritance type of base class
// this header contains two extensions, that specifies inheritance type of base
// class
// BaseClass - normal inheritance
// VirtualBaseClass - when virtual inheritance is used
//in order for virtual inheritance to work, InheritanceContext is required. for normal inheritance it is not required
// in order for virtual inheritance to work, InheritanceContext is required. for
// normal inheritance it is not required
#include <bitsery/ext/inheritance.h>
using bitsery::ext::BaseClass;
using bitsery::ext::VirtualBaseClass;
struct Base {
struct Base
{
uint8_t x{};
//Base doesn't have to be polymorphic class, inheritance works at compile-time.
// Base doesn't have to be polymorphic class, inheritance works at
// compile-time.
};
template<typename S>
void serialize(S& s, Base& o) {
void
serialize(S& s, Base& o)
{
s.value1b(o.x);
}
struct Derive1:virtual Base {// virtually inherits from base
struct Derive1 : virtual Base
{ // virtually inherits from base
uint8_t y1{};
};
template<typename S>
void serialize(S& s, Derive1& o) {
//define virtual inheritance, it will not compile if InheritanceContext is not defined in serializer/deserializer
void
serialize(S& s, Derive1& o)
{
// define virtual inheritance, it will not compile if InheritanceContext is
// not defined in serializer/deserializer
s.ext(o, VirtualBaseClass<Base>{});
s.value1b(o.y1);
}
// to make it more interesting, serialize private member
struct Derived2:virtual Base {
explicit Derived2(uint8_t y):y2{y} {}
struct Derived2 : virtual Base
{
explicit Derived2(uint8_t y)
: y2{ y }
{
}
uint8_t getY2() const { return y2; };
uint8_t getY2() const {
return y2;
};
private:
friend bitsery::Access;
uint8_t y2{};
template<typename S>
void serialize(S& s) {
void serialize(S& s)
{
// notice virtual inheritance
s.ext(*this, VirtualBaseClass<Base>{});
s.value1b(y2);
}
};
struct MultipleInheritance: Derive1, Derived2 {
explicit MultipleInheritance(uint8_t y2):Derived2{y2} {}
struct MultipleInheritance
: Derive1
, Derived2
{
explicit MultipleInheritance(uint8_t y2)
: Derived2{ y2 }
{
}
uint8_t z{};
};
template<typename S>
void serialize(S& s, MultipleInheritance& o) {
void
serialize(S& s, MultipleInheritance& o)
{
// has two bases, serialize them separately
s.ext(o, BaseClass<Derive1>{});
s.ext(o, BaseClass<Derived2>{});
@@ -67,15 +90,19 @@ void serialize(S& s, MultipleInheritance& o) {
}
namespace bitsery {
// call to serialize function with Derived2 and MultipleInheritance is ambiguous,
// it matches two serialize functions: Base classes non-member fnc and Derived2 member fnc
// we need explicitly select which function to use
// call to serialize function with Derived2 and MultipleInheritance is
// ambiguous, it matches two serialize functions: Base classes non-member fnc
// and Derived2 member fnc we need explicitly select which function to use
template<>
struct SelectSerializeFnc<Derived2>:UseMemberFnc {};
struct SelectSerializeFnc<Derived2> : UseMemberFnc
{
};
// multiple inheritance has non-member serialize function defined
template<>
struct SelectSerializeFnc<MultipleInheritance>:UseNonMemberFnc {};
struct SelectSerializeFnc<MultipleInheritance> : UseNonMemberFnc
{
};
}
// some helper types
@@ -83,7 +110,9 @@ using Buffer = std::vector<uint8_t>;
using Writer = bitsery::OutputBufferAdapter<Buffer>;
using Reader = bitsery::InputBufferAdapter<Buffer>;
int main() {
int
main()
{
MultipleInheritance data{ 98 };
data.x = 254;
@@ -94,11 +123,14 @@ int main() {
bitsery::ext::InheritanceContext ctx1;
auto writtenSize = bitsery::quickSerialization(ctx1, Writer{ buf }, data);
assert(writtenSize == 4);//base is serialized once, because it is inherited virtually
assert(writtenSize ==
4); // base is serialized once, because it is inherited virtually
MultipleInheritance res{ 0 };
bitsery::ext::InheritanceContext ctx2;
auto state = bitsery::quickDeserialization(ctx2, Reader{buf.begin(), writtenSize}, res);
auto state = bitsery::quickDeserialization(
ctx2, Reader{ buf.begin(), writtenSize }, res);
assert(state.first == bitsery::ReaderError::NoError && state.second);
assert(data.x == res.x && data.y1 == res.y1 && data.getY2() == res.getY2() && data.z == res.z);
assert(data.x == res.x && data.y1 == res.y1 && data.getY2() == res.getY2() &&
data.z == res.z);
}

View File

@@ -2,30 +2,39 @@
// example of how to deserialize non default constructible objects
//
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/bitsery.h>
#include <bitsery/traits/vector.h>
class MyData {
class MyData
{
// define your private data
float _x{ 0 };
float _y{ 0 };
// make bitsery:Access friend
friend class bitsery::Access;
//create default constructor, don't worry about class invariant, it will be restored in deserialization
// create default constructor, don't worry about class invariant, it will be
// restored in deserialization
MyData() = default;
// define serialize function
template<typename S>
void serialize(S& s) {
void serialize(S& s)
{
s.value4b(_x);
s.value4b(_y);
}
public:
// define non default public constructor
MyData(float x, float y):_x{x}, _y{y} {}
MyData(float x, float y)
: _x{ x }
, _y{ y }
{
}
// this is for convenience
bool operator ==(const MyData&rhs) const {
bool operator==(const MyData& rhs) const
{
return _x == rhs._x && _y == rhs._y;
}
};
@@ -35,7 +44,9 @@ using Buffer = std::vector<uint8_t>;
using Writer = bitsery::OutputBufferAdapter<Buffer>;
using Reader = bitsery::InputBufferAdapter<Buffer>;
int main() {
int
main()
{
// initialize our data
std::vector<MyData> data{};
@@ -45,18 +56,19 @@ int main() {
// create buffer
Buffer buffer{};
//we cant use quick (de)serialization helper methods, because we ant to serialize container directly
//create writer and serialize container
// we cant use quick (de)serialization helper methods, because we ant to
// serialize container directly create writer and serialize container
bitsery::Serializer<Writer> ser{ buffer };
ser.container(data, 10);
ser.adapter().flush();
// create reader and deserialize container
bitsery::Deserializer<Reader> des{buffer.begin(), ser.adapter().writtenBytesCount()};
bitsery::Deserializer<Reader> des{ buffer.begin(),
ser.adapter().writtenBytesCount() };
des.container(res, 10);
// check if everything went ok
assert(des.adapter().error() == bitsery::ReaderError::NoError && des.adapter().isCompletedSuccessfully());
assert(des.adapter().error() == bitsery::ReaderError::NoError &&
des.adapter().isCompletedSuccessfully());
assert(res == data);
}

View File

@@ -1,39 +1,55 @@
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/bitsery.h>
#include <bitsery/traits/vector.h>
// include pointers extension
//this header contains multiple extensions for different pointer types and pointer linking context,
//that validates pointer ownership and checks if there are and no dangling pointers after serialization/deserialization.
//dangling pointer in this context means, that non-owning pointer points to data, that was not serialized.
// this header contains multiple extensions for different pointer types and
// pointer linking context, that validates pointer ownership and checks if there
// are and no dangling pointers after serialization/deserialization. dangling
// pointer in this context means, that non-owning pointer points to data, that
// was not serialized.
#include <bitsery/ext/pointer.h>
using bitsery::ext::ReferencedByPointer;
using bitsery::ext::PointerObserver;
using bitsery::ext::PointerOwner;
using bitsery::ext::PointerType;
using bitsery::ext::ReferencedByPointer;
enum class MyEnum:uint16_t { V1,V2,V3 };
struct MyStruct {
enum class MyEnum : uint16_t
{
V1,
V2,
V3
};
struct MyStruct
{
MyStruct(uint32_t i_, MyEnum e_, std::vector<float> fs_)
:i{i_},
e{e_},
fs{fs_} {}
MyStruct():MyStruct{0, MyEnum::V1, {}} {}
: i{ i_ }
, e{ e_ }
, fs{ fs_ }
{
}
MyStruct()
: MyStruct{ 0, MyEnum::V1, {} }
{
}
uint32_t i;
MyEnum e;
std::vector<float> fs;
};
template<typename S>
void serialize(S& s, MyStruct& o) {
void
serialize(S& s, MyStruct& o)
{
s.value4b(o.i);
s.value2b(o.e);
s.container4b(o.fs, 10);
}
// our test data
struct Test1Data {
struct Test1Data
{
// regular data, nothing fancy here
MyStruct o1;
int32_t i1;
@@ -50,31 +66,30 @@ private:
friend bitsery::Access;
template<typename S>
void serialize(S& s) {
void serialize(S& s)
{
// just a regular fields
s.object(o1);
s.value4b(i1);
// set container elements to be candidates for non-owning pointers
s.container(vdata, 100, [](S& s, MyStruct& d){
s.ext(d, ReferencedByPointer{});
});
s.container(
vdata, 100, [](S& s, MyStruct& d) { s.ext(d, ReferencedByPointer{}); });
// contains non owning pointers
//
// IMPORTANT !!!
// ALWAYS ACCEPT BY REFERENCE like this: T* (&obj)
// if using c++14, then auto& always works.
//
//you can also serialize non owning pointers first, pointer linking context will keep track on them
//and as soon as pointer owner data is deserialized, all non-owning pointers will be updated
s.container(vptr, 100, [](S& s, MyStruct* (&d)){
s.ext(d, PointerObserver{});
});
// you can also serialize non owning pointers first, pointer linking context
// will keep track on them and as soon as pointer owner data is
// deserialized, all non-owning pointers will be updated
s.container(
vptr, 100, [](S& s, MyStruct*(&d)) { s.ext(d, PointerObserver{}); });
// observer
s.ext(po1, PointerObserver{});
// owner, mark it as not null
s.ext4b(pi1, PointerOwner{ PointerType::NotNull });
}
};
@@ -84,12 +99,15 @@ using Writer = bitsery::OutputBufferAdapter<Buffer>;
using Reader = bitsery::InputBufferAdapter<Buffer>;
// we will need PointerLinkingContext to work with pointers
//if we would require additional context for our own custom flow, we can define it as tuple like this:
// if we would require additional context for our own custom flow, we can define
// it as tuple like this:
// std::tuple<MyContext,ext::PointerLinkingContext>
// and other code will work as expected as long as it cast to proper type.
// see context_usage.cpp for usage example
int main() {
int
main()
{
// set some random data
Test1Data data{};
data.vdata.emplace_back(8941, MyEnum::V1, std::vector<float>{ 4.4f });
@@ -110,21 +128,24 @@ int main() {
// create buffer to store data
Buffer buffer{};
size_t writtenSize{};
//in order to use pointers, we need to pass pointer linking context serializer/deserializer
// in order to use pointers, we need to pass pointer linking context
// serializer/deserializer
{
bitsery::ext::PointerLinkingContext ctx{};
writtenSize = quickSerialization(ctx, Writer{ buffer }, data);
// make sure that pointer linking context is valid
//this ensures that all non-owning pointers points to data that has been serialized,
//so we can successfully reconstruct pointers after deserialization
// this ensures that all non-owning pointers points to data that has been
// serialized, so we can successfully reconstruct pointers after
// deserialization
assert(ctx.isValid());
}
Test1Data res{};
{
bitsery::ext::PointerLinkingContext ctx{};
auto state = quickDeserialization(ctx, Reader{buffer.begin(), writtenSize}, res);
auto state =
quickDeserialization(ctx, Reader{ buffer.begin(), writtenSize }, res);
// check if everything went find
assert(state.first == bitsery::ReaderError::NoError && state.second);
// also check for dangling pointers, after deserialization

View File

@@ -1,21 +1,22 @@
//
// Created by fraillt on 18.4.26.
//
#include <bitsery/adapter/buffer.h>
#include <bitsery/bitsery.h>
#include <bitsery/ext/inheritance.h>
#include <bitsery/ext/pointer.h>
#include <bitsery/ext/std_smart_ptr.h>
#include <bitsery/traits/vector.h>
#include <cassert>
#include <memory>
#include <bitsery/bitsery.h>
#include <bitsery/traits/vector.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/ext/pointer.h>
#include <bitsery/ext/inheritance.h>
#include <bitsery/ext/std_smart_ptr.h>
// in order to work with polymorphic types, we need to describe few steps:
// 1) describe relationships between base and derived types
// this will allow to know what are possible types reachable from base class
// 2) bind serializer to base class
// this will allow to iterate through all types, and add serialization functions,
// without this step compiler would simply remove functions that are not bound at compile-time even it we use type at runtime.
// this will allow to iterate through all types, and add serialization
// functions, without this step compiler would simply remove functions that are
// not bound at compile-time even it we use type at runtime.
using bitsery::ext::BaseClass;
@@ -23,43 +24,47 @@ using bitsery::ext::PointerObserver;
using bitsery::ext::StdSmartPtr;
// define our data structures
struct Color {
struct Color
{
float r{}, g{}, b{};
bool operator == (const Color& o) const {
return std::tie(r, g, b) ==
std::tie(o.r, o.g, o.b);
bool operator==(const Color& o) const
{
return std::tie(r, g, b) == std::tie(o.r, o.g, o.b);
}
};
struct Shape {
struct Shape
{
Color clr{};
virtual ~Shape() = 0;
};
Shape::~Shape() = default;
struct Circle : Shape {
struct Circle : Shape
{
int32_t radius{};
bool operator == (const Circle& o) const {
return std::tie(radius, clr) ==
std::tie(o.radius, o.clr);
bool operator==(const Circle& o) const
{
return std::tie(radius, clr) == std::tie(o.radius, o.clr);
}
};
struct Rectangle : Shape {
struct Rectangle : Shape
{
int32_t width{};
int32_t height{};
bool operator == (const Rectangle& o) const {
return std::tie(width, height, clr) ==
std::tie(o.width, o.height, o.clr);
bool operator==(const Rectangle& o) const
{
return std::tie(width, height, clr) == std::tie(o.width, o.height, o.clr);
}
};
struct RoundedRectangle : Rectangle {
struct RoundedRectangle : Rectangle
{
int32_t radius{};
bool operator == (const RoundedRectangle& o) const {
bool operator==(const RoundedRectangle& o) const
{
return std::tie(radius, static_cast<const Rectangle&>(*this)) ==
std::tie(o.radius, static_cast<const Rectangle&>(o));
}
@@ -67,40 +72,52 @@ struct RoundedRectangle : Rectangle {
// define serialization functions
template<typename S>
void serialize(S &s, Color &o) {
//in real world scenario, it might be possible to serialize this using ValueRange, to map values in smaller space
//but for the sake of this example keep it simple
void
serialize(S& s, Color& o)
{
// in real world scenario, it might be possible to serialize this using
// ValueRange, to map values in smaller space but for the sake of this example
// keep it simple
s.value4b(o.r);
s.value4b(o.g);
s.value4b(o.b);
}
template<typename S>
void serialize(S &s, Shape &o) {
void
serialize(S& s, Shape& o)
{
s.object(o.clr);
}
template<typename S>
void serialize(S &s, Circle &o) {
void
serialize(S& s, Circle& o)
{
s.ext(o, bitsery::ext::BaseClass<Shape>{});
s.value4b(o.radius);
}
template<typename S>
void serialize(S &s, Rectangle &o) {
void
serialize(S& s, Rectangle& o)
{
s.ext(o, bitsery::ext::BaseClass<Shape>{});
s.value4b(o.width);
s.value4b(o.height);
}
template<typename S>
void serialize(S &s, RoundedRectangle &o) {
void
serialize(S& s, RoundedRectangle& o)
{
s.ext(o, bitsery::ext::BaseClass<Rectangle>{});
s.value4b(o.radius);
}
// define our test structure
struct SomeShapes {
struct SomeShapes
{
std::vector<std::shared_ptr<Shape>> sharedList;
std::unique_ptr<Shape> uniquePtr;
// weak ptr and refPtr will point to sharedList
@@ -109,7 +126,9 @@ struct SomeShapes {
};
// creates object, and populates some data
SomeShapes createData() {
SomeShapes
createData()
{
SomeShapes data{};
{
auto tmp = new RoundedRectangle{};
@@ -144,9 +163,10 @@ SomeShapes createData() {
return data;
}
template<typename S>
void serialize(S &s, SomeShapes &o) {
void
serialize(S& s, SomeShapes& o)
{
s.ext(o.uniquePtr, StdSmartPtr{});
// to make things more interesting first serialize weakPtr and refPtr,
// even though objects that weakPtr and refPtr is serialized later,
@@ -165,21 +185,28 @@ namespace bitsery {
namespace ext {
// for each base class define DIRECTLY derived classes
//e.g. PolymorphicBaseClass<Shape> : PolymorphicDerivedClasses<Circle, Rectangle, RoundedRectangle>
// e.g. PolymorphicBaseClass<Shape> : PolymorphicDerivedClasses<Circle,
// Rectangle, RoundedRectangle>
// is incorrect, because RoundedRectangle does not directly derive from Shape
template<>
struct PolymorphicBaseClass<Shape> : PolymorphicDerivedClasses<Circle, Rectangle> {
struct PolymorphicBaseClass<Shape>
: PolymorphicDerivedClasses<Circle, Rectangle>
{
};
template<>
struct PolymorphicBaseClass<Rectangle> : PolymorphicDerivedClasses<RoundedRectangle> {
struct PolymorphicBaseClass<Rectangle>
: PolymorphicDerivedClasses<RoundedRectangle>
{
};
}
}
// convenient type that stores all our types, so that we could easily register and
// also it automatically ensures, that classes is registered in the same order for serialization and deserialization
using MyPolymorphicClassesForRegistering = bitsery::ext::PolymorphicClassesList<Shape>;
// convenient type that stores all our types, so that we could easily register
// and also it automatically ensures, that classes is registered in the same
// order for serialization and deserialization
using MyPolymorphicClassesForRegistering =
bitsery::ext::PolymorphicClassesList<Shape>;
// some helper types
using Buffer = std::vector<uint8_t>;
@@ -189,16 +216,19 @@ using Reader = bitsery::InputBufferAdapter<Buffer>;
// we need to define few things in order to work with polymorphism
// 1) we need pointer linking context to work with pointers
// 2) we need polymorphic context to be able to work with polymorphic types
using TContext = std::tuple<
bitsery::ext::PointerLinkingContext,
using TContext =
std::tuple<bitsery::ext::PointerLinkingContext,
bitsery::ext::PolymorphicContext<bitsery::ext::StandardRTTI>>;
// NOTE:
// RTTI can be customizable, if you can't use dynamic_cast and typeid, and have 'custom' solution
// RTTI can be customizable, if you can't use dynamic_cast and typeid, and have
// 'custom' solution
using MySerializer = bitsery::Serializer<Writer, TContext>;
using MyDeserializer = bitsery::Deserializer<Reader, TContext>;
// checks if deserialized data is equal
void assertSameShapes(const SomeShapes &data, const SomeShapes &res) {
void
assertSameShapes(const SomeShapes& data, const SomeShapes& res)
{
{
auto d = dynamic_cast<RoundedRectangle*>(data.uniquePtr.get());
auto r = dynamic_cast<RoundedRectangle*>(res.uniquePtr.get());
@@ -221,22 +251,27 @@ void assertSameShapes(const SomeShapes &data, const SomeShapes &res) {
assert(res.refPtr == res.sharedList[1].get());
}
int main() {
int
main()
{
auto data = createData();
// create buffer to store data
Buffer buffer{};
size_t writtenSize{};
// we will not use quickSerialization/Deserialization functions to show, that we need to register polymorphic classes, explicitly
// we will not use quickSerialization/Deserialization functions to show, that
// we need to register polymorphic classes, explicitly
{
// STEP 2
// before start serialization/deserialization,
// bind it with base polymorphic types, it will go through all reachable classes that is defined in first step.
// NOTE: you dont need to add Rectangle to reach for RoundedRectangle
// bind it with base polymorphic types, it will go through all reachable
// classes that is defined in first step. NOTE: you dont need to add
// Rectangle to reach for RoundedRectangle
TContext ctx{};
std::get<1>(ctx).registerBasesList<MySerializer>(MyPolymorphicClassesForRegistering{});
std::get<1>(ctx).registerBasesList<MySerializer>(
MyPolymorphicClassesForRegistering{});
// create writer and serialize
MySerializer ser{ ctx, buffer };
ser.object(data);
@@ -244,23 +279,28 @@ int main() {
writtenSize = ser.adapter().writtenBytesCount();
// make sure that pointer linking context is valid
//this ensures that all non-owning pointers points to data that has been serialized,
//so we can successfully reconstruct pointers after deserialization
// this ensures that all non-owning pointers points to data that has been
// serialized, so we can successfully reconstruct pointers after
// deserialization
assert(std::get<0>(ctx).isValid());
}
SomeShapes res{};
{
TContext ctx{};
std::get<1>(ctx).registerBasesList<MyDeserializer>(MyPolymorphicClassesForRegistering{});
std::get<1>(ctx).registerBasesList<MyDeserializer>(
MyPolymorphicClassesForRegistering{});
// deserialize our data
MyDeserializer des{ ctx, buffer.begin(), writtenSize };
des.object(res);
assert(des.adapter().error() == bitsery::ReaderError::NoError && des.adapter().isCompletedSuccessfully());
assert(des.adapter().error() == bitsery::ReaderError::NoError &&
des.adapter().isCompletedSuccessfully());
// also check for dangling pointers, after deserialization
assert(std::get<0>(ctx).isValid());
// clear shared state from pointer linking context,
// it is only required if there are any pointers that manage shared state, e.g. std::shared_ptr
assert(res.weakPtr.use_count() == 2);//one in sharedList and one in pointer linking context
// it is only required if there are any pointers that manage shared state,
// e.g. std::shared_ptr
assert(res.weakPtr.use_count() ==
2); // one in sharedList and one in pointer linking context
std::get<0>(ctx).clearSharedState();
assert(res.weakPtr.use_count() == 1);
}

3
format.sh Executable file
View File

@@ -0,0 +1,3 @@
find . -regex '.*\.\(cpp\|hpp\|cu\|c\|h\)' -exec clang-format -style=file -i {} \;

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -32,29 +32,38 @@
namespace bitsery {
template<typename Buffer, typename Config = DefaultConfig>
class InputBufferAdapter: public details::InputAdapterBaseCRTP<InputBufferAdapter<Buffer,Config>> {
class InputBufferAdapter
: public details::InputAdapterBaseCRTP<InputBufferAdapter<Buffer, Config>>
{
public:
friend details::InputAdapterBaseCRTP<InputBufferAdapter<Buffer, Config>>;
using BitPackingEnabled = details::InputAdapterBitPackingWrapper<InputBufferAdapter<Buffer, Config>>;
using BitPackingEnabled =
details::InputAdapterBitPackingWrapper<InputBufferAdapter<Buffer, Config>>;
using TConfig = Config;
using TIterator = typename traits::BufferAdapterTraits<typename std::remove_const<Buffer>::type>::TConstIterator;
using TValue = typename traits::BufferAdapterTraits<typename std::remove_const<Buffer>::type>::TValue;
static_assert(details::IsDefined<TValue>::value,
using TIterator = typename traits::BufferAdapterTraits<
typename std::remove_const<Buffer>::type>::TConstIterator;
using TValue = typename traits::BufferAdapterTraits<
typename std::remove_const<Buffer>::type>::TValue;
static_assert(
details::IsDefined<TValue>::value,
"Please define BufferAdapterTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<typename std::remove_const<Buffer>::type>::isContiguous,
static_assert(traits::ContainerTraits<
typename std::remove_const<Buffer>::type>::isContiguous,
"BufferAdapter only works with contiguous containers");
static_assert(sizeof(TValue) == 1, "BufferAdapter underlying type must be 1byte.");
static_assert(sizeof(TValue) == 1,
"BufferAdapter underlying type must be 1byte.");
InputBufferAdapter(TIterator beginIt, size_t size)
: _beginIt{beginIt},
_currOffset{0},
_endReadOffset{size},
_bufferSize{size} {
};
: _beginIt{ beginIt }
, _currOffset{ 0 }
, _endReadOffset{ size }
, _bufferSize{ size } {};
InputBufferAdapter(TIterator beginIt, TIterator endIt)
:InputBufferAdapter(beginIt, static_cast<size_t>(std::distance(beginIt, endIt))) {
: InputBufferAdapter(beginIt,
static_cast<size_t>(std::distance(beginIt, endIt)))
{
}
InputBufferAdapter(const InputBufferAdapter&) = delete;
@@ -63,17 +72,25 @@ namespace bitsery {
InputBufferAdapter(InputBufferAdapter&&) = default;
InputBufferAdapter& operator=(InputBufferAdapter&&) = default;
void currentReadPos(size_t pos) {
currentReadPosChecked(pos, std::integral_constant<bool, Config::CheckAdapterErrors>{});
void currentReadPos(size_t pos)
{
currentReadPosChecked(
pos, std::integral_constant<bool, Config::CheckAdapterErrors>{});
}
size_t currentReadPos() const {
return currentReadPosChecked(std::integral_constant<bool, Config::CheckAdapterErrors>{});
size_t currentReadPos() const
{
return currentReadPosChecked(
std::integral_constant<bool, Config::CheckAdapterErrors>{});
}
void currentReadEndPos(size_t pos) {
// assert that CheckAdapterErrors is enabled, otherwise it will simply will not work even if data and buffer is not corrupted
static_assert(Config::CheckAdapterErrors, "Please enable CheckAdapterErrors to use this functionality.");
void currentReadEndPos(size_t pos)
{
// assert that CheckAdapterErrors is enabled, otherwise it will simply will
// not work even if data and buffer is not corrupted
static_assert(
Config::CheckAdapterErrors,
"Please enable CheckAdapterErrors to use this functionality.");
if (_bufferSize >= pos && error() == ReaderError::NoError) {
_overflowOnReadEndPos = pos == 0;
if (pos == 0)
@@ -84,19 +101,22 @@ namespace bitsery {
}
}
size_t currentReadEndPos() const {
size_t currentReadEndPos() const
{
if (_overflowOnReadEndPos)
return 0;
return _endReadOffset;
}
ReaderError error() const {
ReaderError error() const
{
return _currOffset <= _endReadOffset
? ReaderError::NoError
: static_cast<ReaderError>(_currOffset - _endReadOffset);
}
void error(ReaderError error) {
void error(ReaderError error)
{
if (_currOffset <= _endReadOffset) {
_endReadOffset = 0;
_bufferSize = 0;
@@ -104,30 +124,34 @@ namespace bitsery {
}
}
bool isCompletedSuccessfully() const {
return _currOffset == _bufferSize;
}
bool isCompletedSuccessfully() const { return _currOffset == _bufferSize; }
private:
using diff_t = typename std::iterator_traits<TIterator>::difference_type;
template<size_t SIZE>
void readInternalValue(TValue *data) {
readInternalImpl(data, SIZE, std::integral_constant<bool, Config::CheckAdapterErrors>{});
void readInternalValue(TValue* data)
{
readInternalImpl(
data, SIZE, std::integral_constant<bool, Config::CheckAdapterErrors>{});
}
void readInternalBuffer(TValue *data, size_t size) {
readInternalImpl(data, size, std::integral_constant<bool, Config::CheckAdapterErrors>{});
void readInternalBuffer(TValue* data, size_t size)
{
readInternalImpl(
data, size, std::integral_constant<bool, Config::CheckAdapterErrors>{});
}
void readInternalImpl(TValue * data, size_t size, std::false_type) {
void readInternalImpl(TValue* data, size_t size, std::false_type)
{
const size_t newOffset = _currOffset + size;
assert(newOffset <= _endReadOffset);
std::copy_n(_beginIt + static_cast<diff_t>(_currOffset), size, data);
_currOffset = newOffset;
}
void readInternalImpl(TValue *data, size_t size, std::true_type) {
void readInternalImpl(TValue* data, size_t size, std::true_type)
{
const size_t newOffset = _currOffset + size;
if (newOffset <= _endReadOffset) {
std::copy_n(_beginIt + static_cast<diff_t>(_currOffset), size, data);
@@ -140,7 +164,8 @@ namespace bitsery {
}
}
void currentReadPosChecked(size_t pos, std::true_type) {
void currentReadPosChecked(size_t pos, std::true_type)
{
if (_bufferSize >= pos && error() == ReaderError::NoError) {
_currOffset = pos;
} else {
@@ -148,18 +173,14 @@ namespace bitsery {
}
}
void currentReadPosChecked(size_t pos, std::false_type) {
_currOffset = pos;
}
void currentReadPosChecked(size_t pos, std::false_type) { _currOffset = pos; }
size_t currentReadPosChecked(std::true_type) const {
size_t currentReadPosChecked(std::true_type) const
{
return error() == ReaderError::NoError ? _currOffset : 0;
}
size_t currentReadPosChecked(std::false_type) const {
return _currOffset;
}
size_t currentReadPosChecked(std::false_type) const { return _currOffset; }
TIterator _beginIt;
size_t _currOffset;
@@ -169,25 +190,31 @@ namespace bitsery {
};
template<typename Buffer, typename Config = DefaultConfig>
class OutputBufferAdapter: public details::OutputAdapterBaseCRTP<OutputBufferAdapter<Buffer,Config>> {
class OutputBufferAdapter
: public details::OutputAdapterBaseCRTP<OutputBufferAdapter<Buffer, Config>>
{
public:
friend details::OutputAdapterBaseCRTP<OutputBufferAdapter<Buffer, Config>>;
using BitPackingEnabled = details::OutputAdapterBitPackingWrapper<OutputBufferAdapter<Buffer, Config>>;
using BitPackingEnabled = details::OutputAdapterBitPackingWrapper<
OutputBufferAdapter<Buffer, Config>>;
using TConfig = Config;
using TIterator = typename traits::BufferAdapterTraits<Buffer>::TIterator;
using TValue = typename traits::BufferAdapterTraits<Buffer>::TValue;
static_assert(details::IsDefined<TValue>::value,
static_assert(
details::IsDefined<TValue>::value,
"Please define BufferAdapterTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<Buffer>::isContiguous,
"BufferAdapter only works with contiguous containers");
static_assert(sizeof(TValue) == 1, "BufferAdapter underlying type must be 1byte.");
static_assert(sizeof(TValue) == 1,
"BufferAdapter underlying type must be 1byte.");
OutputBufferAdapter(Buffer& buffer)
: _buffer{std::addressof(buffer)},
_beginIt{std::begin(buffer)},
_bufferSize{traits::ContainerTraits<Buffer>::size(buffer)} {
: _buffer{ std::addressof(buffer) }
, _beginIt{ std::begin(buffer) }
, _bufferSize{ traits::ContainerTraits<Buffer>::size(buffer) }
{
}
OutputBufferAdapter(const OutputBufferAdapter&) = delete;
@@ -195,7 +222,8 @@ namespace bitsery {
OutputBufferAdapter(OutputBufferAdapter&&) = default;
OutputBufferAdapter& operator=(OutputBufferAdapter&&) = default;
void currentWritePos(size_t pos) {
void currentWritePos(size_t pos)
{
const auto maxPos = _currOffset > pos ? _currOffset : pos;
if (maxPos > _biggestCurrentPos) {
_biggestCurrentPos = maxPos;
@@ -204,28 +232,31 @@ namespace bitsery {
_currOffset = pos;
}
size_t currentWritePos() const {
return _currOffset;
}
size_t currentWritePos() const { return _currOffset; }
void flush() {
void flush()
{
// this function might be useful for stream adapters
}
size_t writtenBytesCount() const {
size_t writtenBytesCount() const
{
return _currOffset > _biggestCurrentPos ? _currOffset : _biggestCurrentPos;
}
private:
using TResizable = std::integral_constant<bool, traits::ContainerTraits<Buffer>::isResizable>;
using TResizable =
std::integral_constant<bool, traits::ContainerTraits<Buffer>::isResizable>;
using diff_t = typename std::iterator_traits<TIterator>::difference_type;
template<size_t SIZE>
void writeInternalValue(const TValue *data) {
void writeInternalValue(const TValue* data)
{
writeInternalImpl(data, SIZE);
}
void writeInternalBuffer(const TValue *data, size_t size) {
void writeInternalBuffer(const TValue* data, size_t size)
{
writeInternalImpl(data, size);
}
@@ -235,25 +266,28 @@ namespace bitsery {
size_t _bufferSize{ 0 };
size_t _biggestCurrentPos{ 0 };
void maybeResize(size_t newOffset, std::true_type) {
void maybeResize(size_t newOffset, std::true_type)
{
if (newOffset > _bufferSize) {
traits::BufferAdapterTraits<Buffer>::increaseBufferSize(*_buffer, _currOffset, newOffset);
traits::BufferAdapterTraits<Buffer>::increaseBufferSize(
*_buffer, _currOffset, newOffset);
_beginIt = std::begin(*_buffer);
_bufferSize = traits::ContainerTraits<Buffer>::size(*_buffer);
}
}
void maybeResize(size_t newOffset, std::false_type) {
void maybeResize(size_t newOffset, std::false_type)
{
assert(newOffset <= _bufferSize);
}
void writeInternalImpl(const TValue *data, size_t size) {
void writeInternalImpl(const TValue* data, size_t size)
{
const size_t newOffset = _currOffset + size;
maybeResize(newOffset, TResizable{});
std::copy_n(data, size, _beginIt + static_cast<diff_t>(_currOffset));
_currOffset = newOffset;
}
};
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -29,30 +29,33 @@
namespace bitsery {
template<typename Config>
class BasicMeasureSize {
class BasicMeasureSize
{
public:
using BitPackingEnabled = details::BasicMeasureSizeBitPackingWrapper<BasicMeasureSize<Config>>;
using BitPackingEnabled =
details::BasicMeasureSizeBitPackingWrapper<BasicMeasureSize<Config>>;
using TConfig = Config;
using TValue = void;
template<size_t SIZE, typename T>
void writeBytes(const T&) {
void writeBytes(const T&)
{
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
_currPos += SIZE;
}
template<size_t SIZE, typename T>
void writeBuffer(const T*, size_t count) {
void writeBuffer(const T*, size_t count)
{
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
_currPos += SIZE * count;
}
void currentWritePos(size_t pos) {
void currentWritePos(size_t pos)
{
const auto maxPos = _currPos > pos ? _currPos : pos;
if (maxPos > _biggestCurrentPos) {
_biggestCurrentPos = maxPos;
@@ -60,18 +63,15 @@ namespace bitsery {
_currPos = pos;
}
size_t currentWritePos() const {
return _currPos;
}
size_t currentWritePos() const { return _currPos; }
void align() {
}
void align() {}
void flush() {
}
void flush() {}
// get size in bytes
size_t writtenBytesCount() const {
size_t writtenBytesCount() const
{
return _currPos > _biggestCurrentPos ? _currPos : _biggestCurrentPos;
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -33,16 +33,23 @@
namespace bitsery {
template<typename TChar, typename Config, typename CharTraits>
class BasicInputStreamAdapter: public details::InputAdapterBaseCRTP<BasicInputStreamAdapter<TChar, Config, CharTraits>> {
class BasicInputStreamAdapter
: public details::InputAdapterBaseCRTP<
BasicInputStreamAdapter<TChar, Config, CharTraits>>
{
public:
friend details::InputAdapterBaseCRTP<BasicInputStreamAdapter<TChar, Config, CharTraits>>;
friend details::InputAdapterBaseCRTP<
BasicInputStreamAdapter<TChar, Config, CharTraits>>;
using BitPackingEnabled = details::InputAdapterBitPackingWrapper<BasicInputStreamAdapter<TChar, Config, CharTraits>>;
using BitPackingEnabled = details::InputAdapterBitPackingWrapper<
BasicInputStreamAdapter<TChar, Config, CharTraits>>;
using TConfig = Config;
using TValue = TChar;
BasicInputStreamAdapter(std::basic_ios<TChar, CharTraits>& istream)
:_ios{std::addressof(istream)} {}
: _ios{ std::addressof(istream) }
{
}
BasicInputStreamAdapter(const BasicInputStreamAdapter&) = delete;
BasicInputStreamAdapter& operator=(const BasicInputStreamAdapter&) = delete;
@@ -50,36 +57,44 @@ namespace bitsery {
BasicInputStreamAdapter(BasicInputStreamAdapter&&) = default;
BasicInputStreamAdapter& operator=(BasicInputStreamAdapter&&) = default;
void currentReadPos(size_t ) {
static_assert(std::is_void<TChar>::value, "setting read position is not supported with StreamAdapter");
void currentReadPos(size_t)
{
static_assert(std::is_void<TChar>::value,
"setting read position is not supported with StreamAdapter");
}
size_t currentReadPos() const {
static_assert(std::is_void<TChar>::value, "setting read position is not supported with StreamAdapter");
size_t currentReadPos() const
{
static_assert(std::is_void<TChar>::value,
"setting read position is not supported with StreamAdapter");
return {};
}
void currentReadEndPos(size_t ) {
static_assert(std::is_void<TChar>::value, "setting read position is not supported with StreamAdapter");
void currentReadEndPos(size_t)
{
static_assert(std::is_void<TChar>::value,
"setting read position is not supported with StreamAdapter");
}
size_t currentReadEndPos() const {
static_assert(std::is_void<TChar>::value, "setting read position is not supported with StreamAdapter");
size_t currentReadEndPos() const
{
static_assert(std::is_void<TChar>::value,
"setting read position is not supported with StreamAdapter");
return {};
}
ReaderError error() const {
return _err;
}
ReaderError error() const { return _err; }
bool isCompletedSuccessfully() const {
bool isCompletedSuccessfully() const
{
if (error() == ReaderError::NoError) {
return _ios->rdbuf()->sgetc() == CharTraits::eof();
}
return false;
}
void error(ReaderError error) {
void error(ReaderError error)
{
if (_err == ReaderError::NoError) {
_err = error;
_zeroIfNoErrors = std::numeric_limits<size_t>::max();
@@ -87,18 +102,24 @@ namespace bitsery {
}
private:
template<size_t SIZE>
void readInternalValue(TValue* data) {
readChecked(data, SIZE, std::integral_constant<bool, Config::CheckAdapterErrors>{});
void readInternalValue(TValue* data)
{
readChecked(
data, SIZE, std::integral_constant<bool, Config::CheckAdapterErrors>{});
}
void readInternalBuffer(TValue* data, size_t size) {
readChecked(data, size, std::integral_constant<bool, Config::CheckAdapterErrors>{});
void readInternalBuffer(TValue* data, size_t size)
{
readChecked(
data, size, std::integral_constant<bool, Config::CheckAdapterErrors>{});
}
void readChecked(TValue* data, size_t size, std::true_type) {
if (size - static_cast<size_t>(_ios->rdbuf()->sgetn(data, static_cast<std::streamsize>(size))) != _zeroIfNoErrors) {
void readChecked(TValue* data, size_t size, std::true_type)
{
if (size - static_cast<size_t>(_ios->rdbuf()->sgetn(
data, static_cast<std::streamsize>(size))) !=
_zeroIfNoErrors) {
*data = {};
if (_zeroIfNoErrors == 0) {
error(_ios->rdstate() == std::ios_base::badbit
@@ -108,7 +129,8 @@ namespace bitsery {
}
}
void readChecked(TValue* data, size_t size, std::false_type) {
void readChecked(TValue* data, size_t size, std::false_type)
{
_ios->rdbuf()->sgetn(data, static_cast<std::streamsize>(size));
}
@@ -118,90 +140,120 @@ namespace bitsery {
};
template<typename TChar, typename Config, typename CharTraits>
class BasicOutputStreamAdapter: public details::OutputAdapterBaseCRTP<BasicOutputStreamAdapter<TChar, Config, CharTraits>> {
class BasicOutputStreamAdapter
: public details::OutputAdapterBaseCRTP<
BasicOutputStreamAdapter<TChar, Config, CharTraits>>
{
public:
friend details::OutputAdapterBaseCRTP<BasicOutputStreamAdapter<TChar, Config, CharTraits>>;
friend details::OutputAdapterBaseCRTP<
BasicOutputStreamAdapter<TChar, Config, CharTraits>>;
using BitPackingEnabled = details::OutputAdapterBitPackingWrapper<BasicOutputStreamAdapter<TChar, Config, CharTraits>>;
using BitPackingEnabled = details::OutputAdapterBitPackingWrapper<
BasicOutputStreamAdapter<TChar, Config, CharTraits>>;
using TConfig = Config;
using TValue = TChar;
BasicOutputStreamAdapter(std::basic_ostream<TChar, CharTraits>& ostream)
:_ostream{std::addressof(ostream)} {}
void currentWritePos(size_t ) {
static_assert(std::is_void<TChar>::value, "setting write position is not supported with StreamAdapter");
: _ostream{ std::addressof(ostream) }
{
}
size_t currentWritePos() const {
static_assert(std::is_void<TChar>::value, "setting write position is not supported with StreamAdapter");
void currentWritePos(size_t)
{
static_assert(std::is_void<TChar>::value,
"setting write position is not supported with StreamAdapter");
}
size_t currentWritePos() const
{
static_assert(std::is_void<TChar>::value,
"setting write position is not supported with StreamAdapter");
return {};
}
void flush() {
_ostream->flush();
}
void flush() { _ostream->flush(); }
size_t writtenBytesCount() const {
static_assert(std::is_void<TChar>::value, "`writtenBytesCount` cannot be used with stream adapter");
size_t writtenBytesCount() const
{
static_assert(std::is_void<TChar>::value,
"`writtenBytesCount` cannot be used with stream adapter");
// streaming doesn't return written bytes
return 0u;
}
private:
template<size_t SIZE>
void writeInternalValue(const TValue* data) {
void writeInternalValue(const TValue* data)
{
_ostream->rdbuf()->sputn(data, SIZE);
}
void writeInternalBuffer(const TValue* data, size_t size) {
void writeInternalBuffer(const TValue* data, size_t size)
{
_ostream->rdbuf()->sputn(data, size);
}
std::basic_ostream<TChar, CharTraits>* _ostream;
};
template <typename TChar, typename Config, typename CharTraits, typename TBuffer = std::array<TChar, 256>>
class BasicBufferedOutputStreamAdapter:
public details::OutputAdapterBaseCRTP<BasicBufferedOutputStreamAdapter<TChar, Config, CharTraits, TBuffer>> {
template<typename TChar,
typename Config,
typename CharTraits,
typename TBuffer = std::array<TChar, 256>>
class BasicBufferedOutputStreamAdapter
: public details::OutputAdapterBaseCRTP<
BasicBufferedOutputStreamAdapter<TChar, Config, CharTraits, TBuffer>>
{
public:
friend details::OutputAdapterBaseCRTP<BasicBufferedOutputStreamAdapter<TChar, Config, CharTraits, TBuffer>>;
friend details::OutputAdapterBaseCRTP<
BasicBufferedOutputStreamAdapter<TChar, Config, CharTraits, TBuffer>>;
using BitPackingEnabled = details::OutputAdapterBitPackingWrapper<BasicBufferedOutputStreamAdapter<TChar, Config, CharTraits, TBuffer>>;
using BitPackingEnabled = details::OutputAdapterBitPackingWrapper<
BasicBufferedOutputStreamAdapter<TChar, Config, CharTraits, TBuffer>>;
using TConfig = Config;
using Buffer = TBuffer;
using BufferIt = typename traits::BufferAdapterTraits<TBuffer>::TIterator;
static_assert(details::IsDefined<BufferIt>::value, "Please define BufferAdapterTraits or include from <bitsery/traits/...> to use as buffer for BasicBufferedOutputStreamAdapter");
static_assert(traits::ContainerTraits<Buffer>::isContiguous, "BasicBufferedOutputStreamAdapter only works with contiguous containers");
static_assert(
details::IsDefined<BufferIt>::value,
"Please define BufferAdapterTraits or include from <bitsery/traits/...> to "
"use as buffer for BasicBufferedOutputStreamAdapter");
static_assert(
traits::ContainerTraits<Buffer>::isContiguous,
"BasicBufferedOutputStreamAdapter only works with contiguous containers");
using TValue = TChar;
// bufferSize is used when buffer is dynamically allocated
BasicBufferedOutputStreamAdapter(std::basic_ostream<TChar, CharTraits>& ostream, size_t bufferSize = 256)
:_ostream(std::addressof(ostream)),
_buf{},
_beginIt{std::begin(_buf)},
_currOffset{0}
BasicBufferedOutputStreamAdapter(
std::basic_ostream<TChar, CharTraits>& ostream,
size_t bufferSize = 256)
: _ostream(std::addressof(ostream))
, _buf{}
, _beginIt{ std::begin(_buf) }
, _currOffset{ 0 }
{
init(bufferSize, TResizable{});
// buffer size must be atleast 16, because writeIntervalValue expect that at least one value fits to buffer.
// buffer size must be atleast 16, because writeIntervalValue expect that at
// least one value fits to buffer.
assert(_bufferSize >= 16);
}
//we need to explicitly declare move logic, because after move buffer might be invalidated
BasicBufferedOutputStreamAdapter(const BasicBufferedOutputStreamAdapter&) = delete;
BasicBufferedOutputStreamAdapter& operator = (const BasicBufferedOutputStreamAdapter&) = delete;
// we need to explicitly declare move logic, because after move buffer might
// be invalidated
BasicBufferedOutputStreamAdapter(const BasicBufferedOutputStreamAdapter&) =
delete;
BasicBufferedOutputStreamAdapter& operator=(
const BasicBufferedOutputStreamAdapter&) = delete;
BasicBufferedOutputStreamAdapter(BasicBufferedOutputStreamAdapter&& rhs)
: _ostream{rhs._ostream},
_buf{std::move(rhs._buf)},
_beginIt{std::begin(_buf)},
_currOffset{rhs._currOffset},
_bufferSize{rhs._bufferSize}
{
};
: _ostream{ rhs._ostream }
, _buf{ std::move(rhs._buf) }
, _beginIt{ std::begin(_buf) }
, _currOffset{ rhs._currOffset }
, _bufferSize{ rhs._bufferSize } {};
BasicBufferedOutputStreamAdapter& operator = (BasicBufferedOutputStreamAdapter&& rhs) {
BasicBufferedOutputStreamAdapter& operator=(
BasicBufferedOutputStreamAdapter&& rhs)
{
_ostream = rhs._ostream;
_buf = std::move(rhs._buf);
_beginIt = std::begin(_buf);
@@ -210,40 +262,51 @@ namespace bitsery {
return *this;
};
void currentWritePos(size_t ) {
static_assert(std::is_void<TChar>::value, "setting write position is not supported with StreamAdapter");
void currentWritePos(size_t)
{
static_assert(std::is_void<TChar>::value,
"setting write position is not supported with StreamAdapter");
}
size_t currentWritePos() const {
static_assert(std::is_void<TChar>::value, "setting write position is not supported with StreamAdapter");
size_t currentWritePos() const
{
static_assert(std::is_void<TChar>::value,
"setting write position is not supported with StreamAdapter");
return {};
}
void flush() {
void flush()
{
writeBufferToStream();
_ostream->flush();
}
size_t writtenBytesCount() const {
static_assert(std::is_void<TChar>::value, "`writtenBytesCount` cannot be used with stream adapter");
size_t writtenBytesCount() const
{
static_assert(std::is_void<TChar>::value,
"`writtenBytesCount` cannot be used with stream adapter");
// streaming doesn't return written bytes
return 0u;
}
private:
using TResizable = std::integral_constant<bool, traits::ContainerTraits<TBuffer>::isResizable>;
using TResizable =
std::integral_constant<bool, traits::ContainerTraits<TBuffer>::isResizable>;
using diff_t = typename std::iterator_traits<BufferIt>::difference_type;
template<size_t SIZE>
void writeInternalValue(const TValue* data) {
void writeInternalValue(const TValue* data)
{
writeInternalImpl(data, SIZE);
}
void writeInternalBuffer(const TValue* data, size_t size) {
void writeInternalBuffer(const TValue* data, size_t size)
{
writeInternalImpl(data, size);
}
void writeInternalImpl(const TValue* data, size_t size) {
void writeInternalImpl(const TValue* data, size_t size)
{
const auto newOffset = _currOffset + size;
if (newOffset <= _bufferSize) {
std::copy_n(data, size, _beginIt + static_cast<diff_t>(_currOffset));
@@ -255,18 +318,22 @@ namespace bitsery {
}
}
void writeBufferToStream() {
_ostream->rdbuf()->sputn(std::addressof(*_beginIt), static_cast<std::streamsize>(_currOffset));
void writeBufferToStream()
{
_ostream->rdbuf()->sputn(std::addressof(*_beginIt),
static_cast<std::streamsize>(_currOffset));
_currOffset = 0;
}
void init (size_t buffSize, std::true_type) {
void init(size_t buffSize, std::true_type)
{
// resize buffer
_bufferSize = buffSize;
_buf.resize(_bufferSize);
_beginIt = std::begin(_buf);
}
void init (size_t , std::false_type) {
void init(size_t, std::false_type)
{
// ignore buffer size parameter, and instead take actual buffer size
_bufferSize = traits::ContainerTraits<Buffer>::size(_buf);
}
@@ -290,15 +357,20 @@ namespace bitsery {
BasicIOStreamAdapter(std::basic_iostream<TChar, CharTraits>& iostream)
: BasicInputStreamAdapter<TChar, Config, CharTraits>{ iostream }
, BasicOutputStreamAdapter<TChar, Config, CharTraits>{ iostream }
{}
{
}
};
// helper types for most common implementations for std streams
using OutputStreamAdapter = BasicOutputStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
using InputStreamAdapter = BasicInputStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
using IOStreamAdapter = BasicIOStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
using OutputStreamAdapter =
BasicOutputStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
using InputStreamAdapter =
BasicInputStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
using IOStreamAdapter =
BasicIOStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
using OutputBufferedStreamAdapter = BasicBufferedOutputStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
using OutputBufferedStreamAdapter =
BasicBufferedOutputStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
}
#endif // BITSERY_ADAPTER_STREAM_H

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,7 +20,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_BITSERY_H
#define BITSERY_BITSERY_H
@@ -30,14 +29,14 @@
#define BITSERY_QUOTE_MACRO(name) #name
#define BITSERY_BUILD_VERSION_STR(major, minor, patch) \
BITSERY_QUOTE_MACRO(major) "." \
BITSERY_QUOTE_MACRO(minor) "." \
BITSERY_QUOTE_MACRO(patch)
BITSERY_QUOTE_MACRO(major) \
"." BITSERY_QUOTE_MACRO(minor) "." BITSERY_QUOTE_MACRO(patch)
#define BITSERY_VERSION \
BITSERY_BUILD_VERSION_STR(BITSERY_MAJOR_VERSION, BITSERY_MINOR_VERSION, BITSERY_PATCH_VERSION)
BITSERY_BUILD_VERSION_STR( \
BITSERY_MAJOR_VERSION, BITSERY_MINOR_VERSION, BITSERY_PATCH_VERSION)
#include "serializer.h"
#include "deserializer.h"
#include "serializer.h"
#endif // BITSERY_BITSERY_H

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,73 +20,105 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_H
#define BITSERY_BRIEF_SYNTAX_H
#include "details/serialization_common.h"
#include "details/brief_syntax_common.h"
#include "details/serialization_common.h"
namespace bitsery {
// define function that enables s(....) usage
template<typename S, typename T>
void processBriefSyntax(S& s, T&& head) {
static_assert(std::is_lvalue_reference<T>::value || std::is_base_of<brief_syntax::ModFnc, T>::value,
void
processBriefSyntax(S& s, T&& head)
{
static_assert(
std::is_lvalue_reference<T>::value ||
std::is_base_of<brief_syntax::ModFnc, T>::value,
"Argument must be either lvalue or subclass of brief_syntax::ModFnc");
s.object(head);
}
// wrapper functions that enables to serialize as container or string
template<typename T, size_t N>
brief_syntax::CArray<T, N, true> asText(T (& str)[N]) {
brief_syntax::CArray<T, N, true>
asText(T (&str)[N])
{
return { str };
}
template<typename T, size_t N>
brief_syntax::CArray<T, N, false> asContainer(T (& obj)[N]) {
brief_syntax::CArray<T, N, false>
asContainer(T (&obj)[N])
{
return { obj };
}
template<typename T>
brief_syntax::MaxSize<T> maxSize(T& obj, size_t max) {
brief_syntax::MaxSize<T>
maxSize(T& obj, size_t max)
{
return { obj, max };
}
// define serialize function for fundamental types
template<typename S>
void serialize(S& s, bool& v) {
void
serialize(S& s, bool& v)
{
s.boolValue(v);
}
template<typename S, typename T, typename std::enable_if<details::IsFundamentalType<T>::value>::type * = nullptr>
void serialize(S& s, T& v) {
template<typename S,
typename T,
typename std::enable_if<details::IsFundamentalType<T>::value>::type* =
nullptr>
void
serialize(S& s, T& v)
{
s.template value<sizeof(T)>(v);
}
// define serialization for c-style container
//if array is integral type, specify explicitly how to process: as text or container
template<typename S, typename T, size_t N, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
void serialize(S&, T (&)[N]) {
// if array is integral type, specify explicitly how to process: as text or
// container
template<typename S,
typename T,
size_t N,
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
void
serialize(S&, T (&)[N])
{
static_assert(N == 0,
"\nPlease use 'asText(obj)' or 'asContainer(obj)' when using c-style array with integral types\n");
"\nPlease use 'asText(obj)' or 'asContainer(obj)' when using "
"c-style array with integral types\n");
}
template<typename S, typename T, size_t N, typename std::enable_if<!std::is_integral<T>::value>::type * = nullptr>
void serialize(S& s, T (& obj)[N]) {
template<typename S,
typename T,
size_t N,
typename std::enable_if<!std::is_integral<T>::value>::type* = nullptr>
void
serialize(S& s, T (&obj)[N])
{
brief_syntax::processContainer(s, obj);
}
//this is a helper class that enforce fundamental type sizes, when used on multiple platforms
// this is a helper class that enforce fundamental type sizes, when used on
// multiple platforms
template<size_t TShort, size_t TInt, size_t TLong, size_t TLongLong>
void assertFundamentalTypeSizes() {
void
assertFundamentalTypeSizes()
{
// http://en.cppreference.com/w/cpp/language/types
static_assert(sizeof(short) == TShort, "");
static_assert(sizeof(int) == TInt, "");
static_assert(sizeof(long) == TLong, "");
static_assert(sizeof(long long) == TLongLong, "");
//for completion we also need pointer type size, but serializer doesn't support pointer serialization.
// for completion we also need pointer type size, but serializer doesn't
// support pointer serialization.
}
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,16 +20,17 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_ARRAY_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_ARRAY_H
#include "../traits/array.h"
#include "../details/brief_syntax_common.h"
#include "../traits/array.h"
namespace bitsery {
template<typename S, typename T, size_t N>
void serialize(S &s, std::array<T, N> &obj) {
void
serialize(S& s, std::array<T, N>& obj)
{
brief_syntax::processContainer(s, obj);
}
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -27,7 +27,9 @@
namespace bitsery {
template<typename S, typename T>
void serialize(S &s, std::atomic<T> &obj) {
void
serialize(S& s, std::atomic<T>& obj)
{
s.template ext<sizeof(T)>(obj, ext::StdAtomic{});
}
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -27,12 +27,16 @@
namespace bitsery {
template<typename S, typename T, typename P>
void serialize(S &s, std::chrono::duration<T, P> &obj) {
void
serialize(S& s, std::chrono::duration<T, P>& obj)
{
s.template ext<sizeof(T)>(obj, ext::StdDuration{});
}
template<typename S, typename C, typename T, typename P>
void serialize(S &s, std::chrono::time_point<C, std::chrono::duration<T, P>> &obj) {
void
serialize(S& s, std::chrono::time_point<C, std::chrono::duration<T, P>>& obj)
{
s.template ext<sizeof(T)>(obj, ext::StdTimePoint{});
}
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,16 +20,17 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_DEQUE_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_DEQUE_H
#include "../traits/deque.h"
#include "../details/brief_syntax_common.h"
#include "../traits/deque.h"
namespace bitsery {
template<typename S, typename T, typename Allocator>
void serialize(S &s, std::deque<T, Allocator> &obj) {
void
serialize(S& s, std::deque<T, Allocator>& obj)
{
brief_syntax::processContainer(s, obj);
}
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,16 +20,17 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_FORWARD_LIST_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_FORWARD_LIST_H
#include "../traits/forward_list.h"
#include "../details/brief_syntax_common.h"
#include "../traits/forward_list.h"
namespace bitsery {
template<typename S, typename T, typename Allocator>
void serialize(S &s, std::forward_list<T, Allocator> &obj) {
void
serialize(S& s, std::forward_list<T, Allocator>& obj)
{
brief_syntax::processContainer(s, obj);
}
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,16 +20,17 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_LIST_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_LIST_H
#include "../traits/list.h"
#include "../details/brief_syntax_common.h"
#include "../traits/list.h"
namespace bitsery {
template<typename S, typename T, typename Allocator>
void serialize(S &s, std::list<T, Allocator> &obj) {
void
serialize(S& s, std::list<T, Allocator>& obj)
{
brief_syntax::processContainer(s, obj);
}
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,28 +20,41 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_MAP_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_MAP_H
#include "../ext/std_map.h"
#include <map>
#include <limits>
#include <map>
namespace bitsery {
template<typename S, typename Key, typename T, typename Compare, typename Allocator>
void serialize(S &s, std::map<Key, T, Compare, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdMap{maxSize},
[](S& s, Key& key, T& value) {
template<typename S,
typename Key,
typename T,
typename Compare,
typename Allocator>
void
serialize(S& s,
std::map<Key, T, Compare, Allocator>& obj,
size_t maxSize = std::numeric_limits<size_t>::max())
{
s.ext(obj, ext::StdMap{ maxSize }, [](S& s, Key& key, T& value) {
s.object(key);
s.object(value);
});
}
template<typename S, typename Key, typename T, typename Compare, typename Allocator>
void serialize(S &s, std::multimap<Key, T, Compare, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdMap{maxSize},
[](S& s, Key& key, T& value) {
template<typename S,
typename Key,
typename T,
typename Compare,
typename Allocator>
void
serialize(S& s,
std::multimap<Key, T, Compare, Allocator>& obj,
size_t maxSize = std::numeric_limits<size_t>::max())
{
s.ext(obj, ext::StdMap{ maxSize }, [](S& s, Key& key, T& value) {
s.object(key);
s.object(value);
});

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -27,17 +27,23 @@
namespace bitsery {
template<typename S, typename T, typename D>
void serialize(S &s, std::unique_ptr<T, D> &obj) {
void
serialize(S& s, std::unique_ptr<T, D>& obj)
{
s.ext(obj, ext::StdSmartPtr{});
}
template<typename S, typename T>
void serialize(S &s, std::shared_ptr<T> &obj) {
void
serialize(S& s, std::shared_ptr<T>& obj)
{
s.ext(obj, ext::StdSmartPtr{});
}
template<typename S, typename T>
void serialize(S &s, std::weak_ptr<T> &obj) {
void
serialize(S& s, std::weak_ptr<T>& obj)
{
s.ext(obj, ext::StdSmartPtr{});
}
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,7 +20,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_QUEUE_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_QUEUE_H
@@ -29,12 +28,20 @@
namespace bitsery {
template<typename S, typename T, typename C>
void serialize(S &s, std::queue<T, C> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
void
serialize(S& s,
std::queue<T, C>& obj,
size_t maxSize = std::numeric_limits<size_t>::max())
{
s.ext(obj, ext::StdQueue{ maxSize });
}
template<typename S, typename T, typename C, typename Comp>
void serialize(S &s, std::priority_queue<T, C, Comp> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
void
serialize(S& s,
std::priority_queue<T, C, Comp>& obj,
size_t maxSize = std::numeric_limits<size_t>::max())
{
s.ext(obj, ext::StdQueue{ maxSize });
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,22 +20,29 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_SET_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_SET_H
#include "../ext/std_set.h"
#include <set>
#include <limits>
#include <set>
namespace bitsery {
template<typename S, typename Key, typename Compare, typename Allocator>
void serialize(S &s, std::set<Key, Compare, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
void
serialize(S& s,
std::set<Key, Compare, Allocator>& obj,
size_t maxSize = std::numeric_limits<size_t>::max())
{
s.ext(obj, ext::StdSet{ maxSize });
}
template<typename S, typename Key, typename Compare, typename Allocator>
void serialize(S &s, std::multiset<Key, Compare, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
void
serialize(S& s,
std::multiset<Key, Compare, Allocator>& obj,
size_t maxSize = std::numeric_limits<size_t>::max())
{
s.ext(obj, ext::StdSet{ maxSize });
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,7 +20,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_STACK_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_STACK_H
@@ -29,7 +28,11 @@
namespace bitsery {
template<typename S, typename T, typename C>
void serialize(S &s, std::stack<T, C> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
void
serialize(S& s,
std::stack<T, C>& obj,
size_t maxSize = std::numeric_limits<size_t>::max())
{
s.ext(obj, ext::StdStack{ maxSize });
}
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,16 +20,17 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_STRING_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_STRING_H
#include "../traits/string.h"
#include "../details/brief_syntax_common.h"
#include "../traits/string.h"
namespace bitsery {
template<typename S, typename CharT, typename Traits, typename Allocator>
void serialize(S &s, std::basic_string<CharT, Traits, Allocator> &str) {
void
serialize(S& s, std::basic_string<CharT, Traits, Allocator>& str)
{
brief_syntax::processContainer(s, str);
}
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -27,7 +27,9 @@
namespace bitsery {
template<typename S, typename... Ts>
void serialize(S &s, std::tuple<Ts...> &obj) {
void
serialize(S& s, std::tuple<Ts...>& obj)
{
s.ext(obj, ext::StdTuple{});
}
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,28 +20,43 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_UNORDERED_MAP_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_UNORDERED_MAP_H
#include "../ext/std_map.h"
#include <unordered_map>
#include <limits>
#include <unordered_map>
namespace bitsery {
template<typename S, typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
void serialize(S &s, std::unordered_map<Key, T, Hash, KeyEqual, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdMap{maxSize},
[](S& s, Key& key, T& value) {
template<typename S,
typename Key,
typename T,
typename Hash,
typename KeyEqual,
typename Allocator>
void
serialize(S& s,
std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& obj,
size_t maxSize = std::numeric_limits<size_t>::max())
{
s.ext(obj, ext::StdMap{ maxSize }, [](S& s, Key& key, T& value) {
s.object(key);
s.object(value);
});
}
template<typename S, typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
void serialize(S &s, std::unordered_multimap<Key, T, Hash, KeyEqual, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdMap{maxSize},
[](S& s, Key& key, T& value) {
template<typename S,
typename Key,
typename T,
typename Hash,
typename KeyEqual,
typename Allocator>
void
serialize(S& s,
std::unordered_multimap<Key, T, Hash, KeyEqual, Allocator>& obj,
size_t maxSize = std::numeric_limits<size_t>::max())
{
s.ext(obj, ext::StdMap{ maxSize }, [](S& s, Key& key, T& value) {
s.object(key);
s.object(value);
});

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,7 +20,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_UNORDERED_SET_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_UNORDERED_SET_H
@@ -29,13 +28,29 @@
#include <unordered_set>
namespace bitsery {
template<typename S, typename Key, typename Hash, typename KeyEqual, typename Allocator>
void serialize(S &s, std::unordered_set<Key, Hash, KeyEqual, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
template<typename S,
typename Key,
typename Hash,
typename KeyEqual,
typename Allocator>
void
serialize(S& s,
std::unordered_set<Key, Hash, KeyEqual, Allocator>& obj,
size_t maxSize = std::numeric_limits<size_t>::max())
{
s.ext(obj, ext::StdSet{ maxSize });
}
template<typename S, typename Key, typename Hash, typename KeyEqual, typename Allocator>
void serialize(S &s, std::unordered_multiset<Key, Hash, KeyEqual, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
template<typename S,
typename Key,
typename Hash,
typename KeyEqual,
typename Allocator>
void
serialize(S& s,
std::unordered_multiset<Key, Hash, KeyEqual, Allocator>& obj,
size_t maxSize = std::numeric_limits<size_t>::max())
{
s.ext(obj, ext::StdSet{ maxSize });
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -27,7 +27,9 @@
namespace bitsery {
template<typename S, typename... Ts>
void serialize(S &s, std::variant<Ts...> &obj) {
void
serialize(S& s, std::variant<Ts...>& obj)
{
s.ext(obj, ext::StdVariant{});
}
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,16 +20,17 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_VECTOR_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_VECTOR_H
#include "../traits/vector.h"
#include "../details/brief_syntax_common.h"
#include "../traits/vector.h"
namespace bitsery {
template<typename S, typename T, typename Allocator>
void serialize(S &s, std::vector<T, Allocator> &obj) {
void
serialize(S& s, std::vector<T, Allocator>& obj)
{
brief_syntax::processContainer(s, obj);
}
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,7 +20,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_COMMON_H
#define BITSERY_COMMON_H
@@ -29,19 +28,24 @@ namespace bitsery {
/*
* endianness
*/
enum class EndiannessType {
enum class EndiannessType
{
LittleEndian,
BigEndian
};
// default configuration for serialization and deserialization
struct DefaultConfig {
// defines endianness of data that is read from input adapter and written to output adapter.
struct DefaultConfig
{
// defines endianness of data that is read from input adapter and written to
// output adapter.
static constexpr EndiannessType Endianness = EndiannessType::LittleEndian;
// these flags allow to improve deserialization performance if data is trusted
// enables/disables checks for buffer end or stream read errors in input adapter
// enables/disables checks for buffer end or stream read errors in input
// adapter
static constexpr bool CheckAdapterErrors = true;
// enables/disables checks for other errors that can significantly affect performance
// enables/disables checks for other errors that can significantly affect
// performance
static constexpr bool CheckDataErrors = true;
};

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,7 +20,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_DESERIALIZER_H
#define BITSERY_DESERIALIZER_H
@@ -29,34 +28,44 @@
namespace bitsery {
template<typename TInputAdapter, typename TContext = void>
class Deserializer: public details::AdapterAndContextRef<TInputAdapter, TContext> {
class Deserializer
: public details::AdapterAndContextRef<TInputAdapter, TContext>
{
public:
//helper type, that always returns bit-packing enabled type, useful inside deserialize function when enabling bitpacking
using BPEnabledType = Deserializer<typename TInputAdapter::BitPackingEnabled, TContext>;
// helper type, that always returns bit-packing enabled type, useful inside
// deserialize function when enabling bitpacking
using BPEnabledType =
Deserializer<typename TInputAdapter::BitPackingEnabled, TContext>;
using TConfig = typename TInputAdapter::TConfig;
using details::AdapterAndContextRef<TInputAdapter, TContext>::AdapterAndContextRef;
using details::AdapterAndContextRef<TInputAdapter,
TContext>::AdapterAndContextRef;
/*
* object function
*/
template<typename T>
void object(T &&obj) {
details::SerializeFunction<Deserializer, T>::invoke(*this, std::forward<T>(obj));
void object(T&& obj)
{
details::SerializeFunction<Deserializer, T>::invoke(*this,
std::forward<T>(obj));
}
template<typename T, typename Fnc>
void object(T &&obj, Fnc &&fnc) {
void object(T&& obj, Fnc&& fnc)
{
fnc(*this, std::forward<T>(obj));
}
/*
* functionality, that enables simpler serialization syntax, by including additional header
* functionality, that enables simpler serialization syntax, by including
* additional header
*/
template<typename... TArgs>
Deserializer &operator()(TArgs &&... args) {
Deserializer& operator()(TArgs&&... args)
{
archive(std::forward<TArgs>(args)...);
return *this;
}
@@ -66,8 +75,10 @@ namespace bitsery {
*/
template<size_t VSIZE, typename T>
void value(T &v) {
static_assert(details::IsFundamentalType<T>::value, "Value must be integral, float or enum type.");
void value(T& v)
{
static_assert(details::IsFundamentalType<T>::value,
"Value must be integral, float or enum type.");
using TValue = typename details::IntegralFromFundamental<T>::TValue;
this->_adapter.template readBytes<VSIZE>(reinterpret_cast<TValue&>(v));
}
@@ -76,8 +87,11 @@ namespace bitsery {
* enable bit-packing
*/
template<typename Fnc>
void enableBitPacking(Fnc&& fnc) {
procEnableBitPacking(std::forward<Fnc>(fnc), std::is_same<TInputAdapter, typename TInputAdapter::BitPackingEnabled>{});
void enableBitPacking(Fnc&& fnc)
{
procEnableBitPacking(
std::forward<Fnc>(fnc),
std::is_same<TInputAdapter, typename TInputAdapter::BitPackingEnabled>{});
}
/*
@@ -85,38 +99,52 @@ namespace bitsery {
*/
template<typename T, typename Ext, typename Fnc>
void ext(T &obj, const Ext &extension, Fnc &&fnc) {
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
void ext(T& obj, const Ext& extension, Fnc&& fnc)
{
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value,
"Please define ExtensionTraits");
static_assert(traits::ExtensionTraits<Ext, T>::SupportLambdaOverload,
"extension doesn't support overload with lambda");
extension.deserialize(*this, obj, std::forward<Fnc>(fnc));
}
template<size_t VSIZE, typename T, typename Ext>
void ext(T &obj, const Ext &extension) {
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
void ext(T& obj, const Ext& extension)
{
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value,
"Please define ExtensionTraits");
static_assert(traits::ExtensionTraits<Ext, T>::SupportValueOverload,
"extension doesn't support overload with `value<N>`");
using ExtVType = typename traits::ExtensionTraits<Ext, T>::TValue;
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
extension.deserialize(*this, obj, [](Deserializer& s, VType &v) { s.value<VSIZE>(v);});
using VType = typename std::conditional<std::is_void<ExtVType>::value,
details::DummyType,
ExtVType>::type;
extension.deserialize(
*this, obj, [](Deserializer& s, VType& v) { s.value<VSIZE>(v); });
}
template<typename T, typename Ext>
void ext(T &obj, const Ext &extension) {
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
void ext(T& obj, const Ext& extension)
{
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value,
"Please define ExtensionTraits");
static_assert(traits::ExtensionTraits<Ext, T>::SupportObjectOverload,
"extension doesn't support overload with `object`");
using ExtVType = typename traits::ExtensionTraits<Ext, T>::TValue;
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
extension.deserialize(*this, obj, [](Deserializer& s, VType &v) { s.object(v); });
using VType = typename std::conditional<std::is_void<ExtVType>::value,
details::DummyType,
ExtVType>::type;
extension.deserialize(
*this, obj, [](Deserializer& s, VType& v) { s.object(v); });
}
/*
* boolValue
*/
void boolValue(bool &v) {
procBoolValue(v,
void boolValue(bool& v)
{
procBoolValue(
v,
std::is_same<TInputAdapter, typename TInputAdapter::BitPackingEnabled>{},
std::integral_constant<bool, TInputAdapter::TConfig::CheckDataErrors>{});
}
@@ -126,22 +154,29 @@ namespace bitsery {
*/
template<size_t VSIZE, typename T>
void text(T &str, size_t maxSize) {
static_assert(details::IsTextTraitsDefined<T>::value,
void text(T& str, size_t maxSize)
{
static_assert(
details::IsTextTraitsDefined<T>::value,
"Please define TextTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<T>::isResizable,
static_assert(
traits::ContainerTraits<T>::isResizable,
"use text(T&) overload without `maxSize` for static containers");
size_t length;
readSize(length, maxSize);
traits::ContainerTraits<T>::resize(str, length + (traits::TextTraits<T>::addNUL ? 1u : 0u));
traits::ContainerTraits<T>::resize(
str, length + (traits::TextTraits<T>::addNUL ? 1u : 0u));
procText<VSIZE>(str, length);
}
template<size_t VSIZE, typename T>
void text(T &str) {
static_assert(details::IsTextTraitsDefined<T>::value,
void text(T& str)
{
static_assert(
details::IsTextTraitsDefined<T>::value,
"Please define TextTraits or include from <bitsery/traits/...>");
static_assert(!traits::ContainerTraits<T>::isResizable,
static_assert(
!traits::ContainerTraits<T>::isResizable,
"use text(T&, size_t) overload with `maxSize` for dynamic containers");
size_t length;
readSize(length, traits::ContainerTraits<T>::size(str));
@@ -155,10 +190,13 @@ namespace bitsery {
// dynamic size containers
template<typename T, typename Fnc>
void container(T &obj, size_t maxSize, Fnc &&fnc) {
static_assert(details::IsContainerTraitsDefined<T>::value,
void container(T& obj, size_t maxSize, Fnc&& fnc)
{
static_assert(
details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<T>::isResizable,
static_assert(
traits::ContainerTraits<T>::isResizable,
"use container(T&) overload without `maxSize` for static containers");
size_t size{};
readSize(size, maxSize);
@@ -167,22 +205,31 @@ namespace bitsery {
}
template<size_t VSIZE, typename T>
void container(T &obj, size_t maxSize) {
static_assert(details::IsContainerTraitsDefined<T>::value,
void container(T& obj, size_t maxSize)
{
static_assert(
details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<T>::isResizable,
static_assert(
traits::ContainerTraits<T>::isResizable,
"use container(T&) overload without `maxSize` for static containers");
size_t size{};
readSize(size, maxSize);
traits::ContainerTraits<T>::resize(obj, size);
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
procContainer<VSIZE>(
std::begin(obj),
std::end(obj),
std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
}
template<typename T>
void container(T &obj, size_t maxSize) {
static_assert(details::IsContainerTraitsDefined<T>::value,
void container(T& obj, size_t maxSize)
{
static_assert(
details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<T>::isResizable,
static_assert(
traits::ContainerTraits<T>::isResizable,
"use container(T&) overload without `maxSize` for static containers");
size_t size{};
readSize(size, maxSize);
@@ -191,125 +238,222 @@ namespace bitsery {
}
// fixed size containers
template<typename T, typename Fnc, typename std::enable_if<!std::is_integral<Fnc>::value>::type * = nullptr>
void container(T &obj, Fnc &&fnc) {
static_assert(details::IsContainerTraitsDefined<T>::value,
template<
typename T,
typename Fnc,
typename std::enable_if<!std::is_integral<Fnc>::value>::type* = nullptr>
void container(T& obj, Fnc&& fnc)
{
static_assert(
details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(!traits::ContainerTraits<T>::isResizable,
"use container(T&, size_t, Fnc) overload with `maxSize` for dynamic containers");
"use container(T&, size_t, Fnc) overload with `maxSize` for "
"dynamic containers");
procContainer(std::begin(obj), std::end(obj), std::forward<Fnc>(fnc));
}
template<size_t VSIZE, typename T>
void container(T &obj) {
static_assert(details::IsContainerTraitsDefined<T>::value,
void container(T& obj)
{
static_assert(
details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(!traits::ContainerTraits<T>::isResizable,
"use container(T&, size_t) overload with `maxSize` for dynamic containers");
"use container(T&, size_t) overload with `maxSize` for "
"dynamic containers");
static_assert(VSIZE > 0, "");
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
procContainer<VSIZE>(
std::begin(obj),
std::end(obj),
std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
}
template<typename T>
void container(T &obj) {
static_assert(details::IsContainerTraitsDefined<T>::value,
void container(T& obj)
{
static_assert(
details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(!traits::ContainerTraits<T>::isResizable,
"use container(T&, size_t) overload with `maxSize` for dynamic containers");
"use container(T&, size_t) overload with `maxSize` for "
"dynamic containers");
procContainer(std::begin(obj), std::end(obj));
}
// overloads for functions with explicit type size
template<typename T>
void value1b(T &&v) { value<1>(std::forward<T>(v)); }
void value1b(T&& v)
{
value<1>(std::forward<T>(v));
}
template<typename T>
void value2b(T &&v) { value<2>(std::forward<T>(v)); }
void value2b(T&& v)
{
value<2>(std::forward<T>(v));
}
template<typename T>
void value4b(T &&v) { value<4>(std::forward<T>(v)); }
void value4b(T&& v)
{
value<4>(std::forward<T>(v));
}
template<typename T>
void value8b(T &&v) { value<8>(std::forward<T>(v)); }
void value8b(T&& v)
{
value<8>(std::forward<T>(v));
}
template<typename T>
void value16b(T &&v) { value<16>(std::forward<T>(v)); }
void value16b(T&& v)
{
value<16>(std::forward<T>(v));
}
template<typename T, typename Ext>
void ext1b(T &v, Ext &&extension) { ext<1, T, Ext>(v, std::forward<Ext>(extension)); }
void ext1b(T& v, Ext&& extension)
{
ext<1, T, Ext>(v, std::forward<Ext>(extension));
}
template<typename T, typename Ext>
void ext2b(T &v, Ext &&extension) { ext<2, T, Ext>(v, std::forward<Ext>(extension)); }
void ext2b(T& v, Ext&& extension)
{
ext<2, T, Ext>(v, std::forward<Ext>(extension));
}
template<typename T, typename Ext>
void ext4b(T &v, Ext &&extension) { ext<4, T, Ext>(v, std::forward<Ext>(extension)); }
void ext4b(T& v, Ext&& extension)
{
ext<4, T, Ext>(v, std::forward<Ext>(extension));
}
template<typename T, typename Ext>
void ext8b(T &v, Ext &&extension) { ext<8, T, Ext>(v, std::forward<Ext>(extension)); }
void ext8b(T& v, Ext&& extension)
{
ext<8, T, Ext>(v, std::forward<Ext>(extension));
}
template<typename T, typename Ext>
void ext16b(T &v, Ext &&extension) { ext<16, T, Ext>(v, std::forward<Ext>(extension)); }
void ext16b(T& v, Ext&& extension)
{
ext<16, T, Ext>(v, std::forward<Ext>(extension));
}
template<typename T>
void text1b(T &str, size_t maxSize) { text<1>(str, maxSize); }
void text1b(T& str, size_t maxSize)
{
text<1>(str, maxSize);
}
template<typename T>
void text2b(T &str, size_t maxSize) { text<2>(str, maxSize); }
void text2b(T& str, size_t maxSize)
{
text<2>(str, maxSize);
}
template<typename T>
void text4b(T &str, size_t maxSize) { text<4>(str, maxSize); }
void text4b(T& str, size_t maxSize)
{
text<4>(str, maxSize);
}
template<typename T>
void text1b(T &str) { text<1>(str); }
void text1b(T& str)
{
text<1>(str);
}
template<typename T>
void text2b(T &str) { text<2>(str); }
void text2b(T& str)
{
text<2>(str);
}
template<typename T>
void text4b(T &str) { text<4>(str); }
void text4b(T& str)
{
text<4>(str);
}
template<typename T>
void container1b(T &&obj, size_t maxSize) { container<1>(std::forward<T>(obj), maxSize); }
void container1b(T&& obj, size_t maxSize)
{
container<1>(std::forward<T>(obj), maxSize);
}
template<typename T>
void container2b(T &&obj, size_t maxSize) { container<2>(std::forward<T>(obj), maxSize); }
void container2b(T&& obj, size_t maxSize)
{
container<2>(std::forward<T>(obj), maxSize);
}
template<typename T>
void container4b(T &&obj, size_t maxSize) { container<4>(std::forward<T>(obj), maxSize); }
void container4b(T&& obj, size_t maxSize)
{
container<4>(std::forward<T>(obj), maxSize);
}
template<typename T>
void container8b(T &&obj, size_t maxSize) { container<8>(std::forward<T>(obj), maxSize); }
void container8b(T&& obj, size_t maxSize)
{
container<8>(std::forward<T>(obj), maxSize);
}
template<typename T>
void container16b(T &&obj, size_t maxSize) { container<16>(std::forward<T>(obj), maxSize); }
void container16b(T&& obj, size_t maxSize)
{
container<16>(std::forward<T>(obj), maxSize);
}
template<typename T>
void container1b(T &&obj) { container<1>(std::forward<T>(obj)); }
void container1b(T&& obj)
{
container<1>(std::forward<T>(obj));
}
template<typename T>
void container2b(T &&obj) { container<2>(std::forward<T>(obj)); }
void container2b(T&& obj)
{
container<2>(std::forward<T>(obj));
}
template<typename T>
void container4b(T &&obj) { container<4>(std::forward<T>(obj)); }
void container4b(T&& obj)
{
container<4>(std::forward<T>(obj));
}
template<typename T>
void container8b(T &&obj) { container<8>(std::forward<T>(obj)); }
void container8b(T&& obj)
{
container<8>(std::forward<T>(obj));
}
template<typename T>
void container16b(T &&obj) { container<16>(std::forward<T>(obj)); }
void container16b(T&& obj)
{
container<16>(std::forward<T>(obj));
}
private:
void readSize(size_t& size, size_t maxSize) {
details::readSize(this->_adapter, size, maxSize,
void readSize(size_t& size, size_t maxSize)
{
details::readSize(
this->_adapter,
size,
maxSize,
std::integral_constant<bool, TInputAdapter::TConfig::CheckDataErrors>{});
}
// process value types
// false_type means that we must process all elements individually
template<size_t VSIZE, typename It>
void procContainer(It first, It last, std::false_type) {
void procContainer(It first, It last, std::false_type)
{
for (; first != last; ++first)
value<VSIZE>(*first);
}
@@ -317,36 +461,45 @@ namespace bitsery {
// process value types
// true_type means, that we can copy whole buffer
template<size_t VSIZE, typename It>
void procContainer(It first, It last, std::true_type) {
void procContainer(It first, It last, std::true_type)
{
using TValue = typename std::decay<decltype(*first)>::type;
using TIntegral = typename details::IntegralFromFundamental<TValue>::TValue;
if (first != last) {
const auto distance = std::distance(first, last);
this->_adapter.template readBuffer<VSIZE>(reinterpret_cast<TIntegral*>(&(*first)), static_cast<size_t>(distance));
this->_adapter.template readBuffer<VSIZE>(
reinterpret_cast<TIntegral*>(&(*first)), static_cast<size_t>(distance));
}
}
// process by calling functions
template<typename It, typename Fnc>
void procContainer(It first, It last, Fnc fnc) {
void procContainer(It first, It last, Fnc fnc)
{
for (; first != last; ++first)
fnc(*this, *first);
}
// process object types
template<typename It>
void procContainer(It first, It last) {
void procContainer(It first, It last)
{
for (; first != last; ++first)
object(*first);
}
template<size_t VSIZE, typename T>
void procText(T& str, size_t length) {
void procText(T& str, size_t length)
{
auto begin = std::begin(str);
// end of string, not end of container
using diff_t = typename std::iterator_traits<decltype(begin)>::difference_type;
using diff_t =
typename std::iterator_traits<decltype(begin)>::difference_type;
auto end = std::next(begin, static_cast<diff_t>(length));
procContainer<VSIZE>(begin, end, std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
procContainer<VSIZE>(
begin,
end,
std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
// null terminated character at the end
if (traits::TextTraits<T>::addNUL)
*end = {};
@@ -354,13 +507,15 @@ namespace bitsery {
// proc bool writing bit or byte, depending on if BitPackingEnabled or not
template<typename HandleDataErrors>
void procBoolValue(bool &v, std::true_type, HandleDataErrors) {
void procBoolValue(bool& v, std::true_type, HandleDataErrors)
{
uint8_t tmp{};
this->_adapter.readBits(tmp, 1);
v = tmp == 1;
}
void procBoolValue(bool &v, std::false_type, std::true_type) {
void procBoolValue(bool& v, std::false_type, std::true_type)
{
uint8_t tmp{};
this->_adapter.template readBytes<1>(tmp);
if (tmp > 1)
@@ -368,7 +523,8 @@ namespace bitsery {
v = tmp == 1;
}
void procBoolValue(bool &v, std::false_type, std::false_type) {
void procBoolValue(bool& v, std::false_type, std::false_type)
{
uint8_t tmp{};
this->_adapter.template readBytes<1>(tmp);
v = tmp > 0;
@@ -376,61 +532,68 @@ namespace bitsery {
// enable bit-packing or do nothing if it is already enabled
template<typename Fnc>
void procEnableBitPacking(const Fnc& fnc, std::true_type) {
void procEnableBitPacking(const Fnc& fnc, std::true_type)
{
fnc(*this);
}
template<typename Fnc>
void procEnableBitPacking(const Fnc& fnc, std::false_type) {
auto des = createWithContext(std::integral_constant<bool, Deserializer::HasContext>{});
void procEnableBitPacking(const Fnc& fnc, std::false_type)
{
auto des = createWithContext(
std::integral_constant<bool, Deserializer::HasContext>{});
fnc(des);
}
BPEnabledType createWithContext(std::true_type) {
BPEnabledType createWithContext(std::true_type)
{
return BPEnabledType{ this->_context, this->_adapter };
}
BPEnabledType createWithContext(std::false_type) {
BPEnabledType createWithContext(std::false_type)
{
return BPEnabledType{ this->_adapter };
}
// these are dummy functions for extensions that have TValue = void
void object(details::DummyType&) {
}
void object(details::DummyType&) {}
template<size_t VSIZE>
void value(details::DummyType&) {
void value(details::DummyType&)
{
}
template<typename T, typename... TArgs>
void archive(T &&head, TArgs &&... tail) {
void archive(T&& head, TArgs&&... tail)
{
// serialize object
details::BriefSyntaxFunction<Deserializer, T>::invoke(*this, std::forward<T>(head));
details::BriefSyntaxFunction<Deserializer, T>::invoke(
*this, std::forward<T>(head));
// expand other elements
archive(std::forward<TArgs>(tail)...);
}
// dummy function, that stops archive variadic arguments expansion
void archive() {
}
void archive() {}
};
// helper type
//helper function that set ups all the basic steps and after deserialziation returns status
// helper function that set ups all the basic steps and after deserialziation
// returns status
template<typename InputAdapter, typename T>
std::pair<ReaderError, bool> quickDeserialization(InputAdapter adapter, T& value) {
std::pair<ReaderError, bool>
quickDeserialization(InputAdapter adapter, T& value)
{
Deserializer<InputAdapter> des{ std::move(adapter) };
des.object(value);
return { des.adapter().error(), des.adapter().isCompletedSuccessfully() };
}
template<typename Context, typename InputAdapter, typename T>
std::pair<ReaderError, bool> quickDeserialization(Context& ctx, InputAdapter adapter, T& value) {
std::pair<ReaderError, bool>
quickDeserialization(Context& ctx, InputAdapter adapter, T& value)
{
Deserializer<InputAdapter, Context> des{ ctx, std::move(adapter) };
des.object(value);
return { des.adapter().error(), des.adapter().isCompletedSuccessfully() };

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -23,21 +23,22 @@
#ifndef BITSERY_DETAILS_ADAPTER_BIT_PACKING_H
#define BITSERY_DETAILS_ADAPTER_BIT_PACKING_H
#include "not_defined_type.h"
#include "./adapter_common.h"
#include "../common.h"
#include "./adapter_common.h"
#include "not_defined_type.h"
#include <limits>
namespace bitsery {
namespace details {
template<typename TAdapter>
class InputAdapterBitPackingWrapper {
class InputAdapterBitPackingWrapper
{
public:
// in order to check if adapter is BP enabled, we use `std::is_same<Adapter, typename Adapter::BitPackingEnabled>`
// so when current implementation is BP enabled, we always specify current class as BitPackingEnabled.
// in order to check if adapter is BP enabled, we use `std::is_same<Adapter,
// typename Adapter::BitPackingEnabled>` so when current implementation is BP
// enabled, we always specify current class as BitPackingEnabled.
using BitPackingEnabled = InputAdapterBitPackingWrapper<TAdapter>;
using TConfig = typename TAdapter::TConfig;
using TValue = typename TAdapter::TValue;
@@ -47,12 +48,11 @@ namespace bitsery {
{
}
~InputAdapterBitPackingWrapper() {
align();
}
~InputAdapterBitPackingWrapper() { align(); }
template<size_t SIZE, typename T>
void readBytes(T &v) {
void readBytes(T& v)
{
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
using UT = typename std::make_unsigned<T>::type;
@@ -63,7 +63,8 @@ namespace bitsery {
}
template<size_t SIZE, typename T>
void readBuffer(T *buf, size_t count) {
void readBuffer(T* buf, size_t count)
{
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
@@ -79,58 +80,58 @@ namespace bitsery {
}
template<typename T>
void readBits(T &v, size_t bitsCount) {
void readBits(T& v, size_t bitsCount)
{
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
readBitsInternal(v, bitsCount);
}
void align() {
void align()
{
if (m_scratchBits) {
ScratchType tmp{};
readBitsInternal(tmp, m_scratchBits);
handleAlignErrors(tmp, std::integral_constant<bool, TConfig::CheckDataErrors>{});
handleAlignErrors(
tmp, std::integral_constant<bool, TConfig::CheckDataErrors>{});
}
}
void currentReadPos(size_t pos) {
void currentReadPos(size_t pos)
{
align();
this->_wrapped.currentReadPos(pos);
}
size_t currentReadPos() const {
return this->_wrapped.currentReadPos();
}
size_t currentReadPos() const { return this->_wrapped.currentReadPos(); }
void currentReadEndPos(size_t pos) {
this->_wrapped.currentReadEndPos(pos);
}
void currentReadEndPos(size_t pos) { this->_wrapped.currentReadEndPos(pos); }
size_t currentReadEndPos() const {
size_t currentReadEndPos() const
{
return this->_wrapped.currentReadEndPos();
}
bool isCompletedSuccessfully() const {
bool isCompletedSuccessfully() const
{
return this->_wrapped.isCompletedSuccessfully();
}
ReaderError error() const {
return this->_wrapped.error();
}
ReaderError error() const { return this->_wrapped.error(); }
void error(ReaderError error) {
this->_wrapped.error(error);
}
void error(ReaderError error) { this->_wrapped.error(error); }
private:
TAdapter& _wrapped;
using UnsignedValue = typename std::make_unsigned<typename TAdapter::TValue>::type;
using UnsignedValue =
typename std::make_unsigned<typename TAdapter::TValue>::type;
using ScratchType = typename details::ScratchType<UnsignedValue>::type;
ScratchType m_scratch{};
size_t m_scratchBits{};
template<typename T>
void readBitsInternal(T &v, size_t size) {
void readBitsInternal(T& v, size_t size)
{
auto bitsLeft = size;
using TFast = typename FastType<T>::type;
TFast res{};
@@ -138,12 +139,14 @@ namespace bitsery {
auto bits = (std::min)(bitsLeft, details::BitsSize<UnsignedValue>::value);
if (m_scratchBits < bits) {
UnsignedValue tmp;
this->_wrapped.template readBytes<sizeof(UnsignedValue), UnsignedValue>(tmp);
this->_wrapped.template readBytes<sizeof(UnsignedValue), UnsignedValue>(
tmp);
m_scratch |= static_cast<ScratchType>(tmp) << m_scratchBits;
m_scratchBits += details::BitsSize<UnsignedValue>::value;
}
auto shiftedRes =
static_cast<T>(m_scratch & ((static_cast<ScratchType>(1) << bits) - 1)) << (size - bitsLeft);
static_cast<T>(m_scratch & ((static_cast<ScratchType>(1) << bits) - 1))
<< (size - bitsLeft);
res = static_cast<TFast>(res | static_cast<TFast>(shiftedRes));
m_scratch >>= bits;
m_scratchBits -= bits;
@@ -152,19 +155,18 @@ namespace bitsery {
v = static_cast<T>(res);
}
void handleAlignErrors(ScratchType value, std::true_type) {
void handleAlignErrors(ScratchType value, std::true_type)
{
if (value)
error(ReaderError::InvalidData);
}
void handleAlignErrors(ScratchType, std::false_type) {
}
void handleAlignErrors(ScratchType, std::false_type) {}
};
template<typename TAdapter>
class BasicMeasureSizeBitPackingWrapper {
class BasicMeasureSizeBitPackingWrapper
{
public:
using BitPackingEnabled = BasicMeasureSizeBitPackingWrapper<TAdapter>;
using TConfig = typename TAdapter::TConfig;
@@ -175,22 +177,23 @@ namespace bitsery {
{
}
~BasicMeasureSizeBitPackingWrapper() {
align();
}
~BasicMeasureSizeBitPackingWrapper() { align(); }
template<size_t SIZE, typename T>
void writeBytes(const T& value) {
void writeBytes(const T& value)
{
_wrapped.template writeBytes<SIZE>(value);
}
template<size_t SIZE, typename T>
void writeBuffer(const T* buf, size_t count) {
void writeBuffer(const T* buf, size_t count)
{
_wrapped.template writeBuffer<SIZE>(buf, count);
}
template<typename T>
void writeBits(const T& , size_t bitsCount) {
void writeBits(const T&, size_t bitsCount)
{
_scratchBits += bitsCount;
while (_scratchBits >= 8) {
writeOneByte();
@@ -198,45 +201,46 @@ namespace bitsery {
}
}
void align() {
void align()
{
if (_scratchBits > 0) {
_scratchBits = 0;
writeOneByte();
}
}
void currentWritePos(size_t pos) {
void currentWritePos(size_t pos)
{
align();
this->_wrapped.currentWritePos(pos);
}
size_t currentWritePos() const {
return this->_wrapped.currentWritePos();
}
size_t currentWritePos() const { return this->_wrapped.currentWritePos(); }
void flush() {
void flush()
{
align();
this->_wrapped.flush();
}
size_t writtenBytesCount() const {
size_t writtenBytesCount() const
{
return this->_wrapped.writtenBytesCount();
}
private:
void writeOneByte() {
_wrapped.template writeBytes<1>(uint8_t{});
}
void writeOneByte() { _wrapped.template writeBytes<1>(uint8_t{}); }
TAdapter& _wrapped;
size_t _scratchBits{};
};
template<typename TAdapter>
class OutputAdapterBitPackingWrapper {
class OutputAdapterBitPackingWrapper
{
public:
// in order to check if adapter is BP enabled, we use `std::is_same<Adapter, typename Adapter::BitPackingEnabled>`
// so when current implementation is BP enabled, we always specify current class as BitPackingEnabled.
// in order to check if adapter is BP enabled, we use `std::is_same<Adapter,
// typename Adapter::BitPackingEnabled>` so when current implementation is BP
// enabled, we always specify current class as BitPackingEnabled.
using BitPackingEnabled = OutputAdapterBitPackingWrapper<TAdapter>;
using TConfig = typename TAdapter::TConfig;
using TValue = typename TAdapter::TValue;
@@ -246,12 +250,11 @@ namespace bitsery {
{
}
~OutputAdapterBitPackingWrapper() {
align();
}
~OutputAdapterBitPackingWrapper() { align(); }
template<size_t SIZE, typename T>
void writeBytes(const T &v) {
void writeBytes(const T& v)
{
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
@@ -259,12 +262,14 @@ namespace bitsery {
this->_wrapped.template writeBytes<SIZE, T>(v);
} else {
using UT = typename std::make_unsigned<T>::type;
writeBitsInternal(reinterpret_cast<const UT &>(v), details::BitsSize<T>::value);
writeBitsInternal(reinterpret_cast<const UT&>(v),
details::BitsSize<T>::value);
}
}
template<size_t SIZE, typename T>
void writeBuffer(const T *buf, size_t count) {
void writeBuffer(const T* buf, size_t count)
{
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
if (!_scratchBits) {
@@ -274,52 +279,60 @@ namespace bitsery {
// todo improve implementation
const auto end = buf + count;
for (auto it = buf; it != end; ++it)
writeBitsInternal(reinterpret_cast<const UT &>(*it), details::BitsSize<T>::value);
writeBitsInternal(reinterpret_cast<const UT&>(*it),
details::BitsSize<T>::value);
}
}
template<typename T>
void writeBits(const T &v, size_t bitsCount) {
void writeBits(const T& v, size_t bitsCount)
{
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
assert(0 < bitsCount && bitsCount <= details::BitsSize<T>::value);
assert(v <= (bitsCount < 64
? (1ULL << bitsCount) - 1
: (1ULL << (bitsCount-1)) + ((1ULL << (bitsCount-1)) -1)));
assert(v <= (bitsCount < 64 ? (1ULL << bitsCount) - 1
: (1ULL << (bitsCount - 1)) +
((1ULL << (bitsCount - 1)) - 1)));
writeBitsInternal(v, bitsCount);
}
void align() {
writeBitsInternal(UnsignedType{}, (details::BitsSize<UnsignedType>::value - _scratchBits) % 8);
void align()
{
writeBitsInternal(UnsignedType{},
(details::BitsSize<UnsignedType>::value - _scratchBits) %
8);
}
void currentWritePos(size_t pos) {
void currentWritePos(size_t pos)
{
align();
this->_wrapped.currentWritePos(pos);
}
size_t currentWritePos() const {
return this->_wrapped.currentWritePos();
}
size_t currentWritePos() const { return this->_wrapped.currentWritePos(); }
void flush() {
void flush()
{
align();
this->_wrapped.flush();
}
size_t writtenBytesCount() const {
size_t writtenBytesCount() const
{
return this->_wrapped.writtenBytesCount();
}
private:
TAdapter& _wrapped;
using UnsignedType = typename std::make_unsigned<typename TAdapter::TValue>::type;
using UnsignedType =
typename std::make_unsigned<typename TAdapter::TValue>::type;
using ScratchType = typename details::ScratchType<UnsignedType>::type;
static_assert(details::IsDefined<ScratchType>::value, "Underlying adapter value type is not supported");
static_assert(details::IsDefined<ScratchType>::value,
"Underlying adapter value type is not supported");
template<typename T>
void writeBitsInternal(const T &v, size_t size) {
void writeBitsInternal(const T& v, size_t size)
{
constexpr size_t valueSize = details::BitsSize<UnsignedType>::value;
T value = v;
size_t bitsLeft = size;
@@ -329,7 +342,8 @@ namespace bitsery {
_scratchBits += bits;
if (_scratchBits >= valueSize) {
auto tmp = static_cast<UnsignedType>(_scratch & _MASK);
this->_wrapped.template writeBytes<sizeof(UnsignedType), UnsignedType >(tmp);
this->_wrapped.template writeBytes<sizeof(UnsignedType), UnsignedType>(
tmp);
_scratch >>= valueSize;
_scratchBits -= valueSize;
@@ -340,13 +354,15 @@ namespace bitsery {
}
// overload for TValue, for better performance
void writeBitsInternal(const UnsignedType &v, size_t size) {
void writeBitsInternal(const UnsignedType& v, size_t size)
{
if (size > 0) {
_scratch |= static_cast<ScratchType>(v) << _scratchBits;
_scratchBits += size;
if (_scratchBits >= details::BitsSize<UnsignedType>::value) {
auto tmp = static_cast<UnsignedType>(_scratch & _MASK);
this->_wrapped.template writeBytes<sizeof(UnsignedType), UnsignedType>(tmp);
this->_wrapped.template writeBytes<sizeof(UnsignedType), UnsignedType>(
tmp);
_scratch >>= details::BitsSize<UnsignedType>::value;
_scratchBits -= details::BitsSize<UnsignedType>::value;
}
@@ -360,5 +376,4 @@ namespace bitsery {
}
}
#endif // BITSERY_DETAILS_ADAPTER_BIT_PACKING_H

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -23,15 +23,16 @@
#ifndef BITSERY_DETAILS_ADAPTER_COMMON_H
#define BITSERY_DETAILS_ADAPTER_COMMON_H
#include "not_defined_type.h"
#include "../common.h"
#include "not_defined_type.h"
#include <algorithm>
#include <cassert>
#include <climits>
namespace bitsery {
enum class ReaderError {
enum class ReaderError
{
NoError,
ReadingError, // this might be used with stream adapter
DataOverflow,
@@ -46,7 +47,9 @@ namespace bitsery {
*/
template<typename Reader>
void handleReadMaxSize(Reader& r, size_t& size, size_t maxSize, std::true_type) {
void
handleReadMaxSize(Reader& r, size_t& size, size_t maxSize, std::true_type)
{
if (size > maxSize) {
r.error(ReaderError::InvalidData);
size = {};
@@ -54,11 +57,18 @@ namespace bitsery {
}
template<typename Reader>
void handleReadMaxSize(Reader&, size_t&, size_t, std::false_type) {
void
handleReadMaxSize(Reader&, size_t&, size_t, std::false_type)
{
}
template<typename Reader, bool CheckMaxSize>
void readSize(Reader& r, size_t& size, size_t maxSize, std::integral_constant<bool, CheckMaxSize> checkMaxSize) {
void
readSize(Reader& r,
size_t& size,
size_t maxSize,
std::integral_constant<bool, CheckMaxSize> checkMaxSize)
{
uint8_t hb{};
r.template readBytes<1>(hb);
if (hb < 0x80u) {
@@ -78,7 +88,9 @@ namespace bitsery {
}
template<typename Writer>
void writeSize(Writer& w, const size_t size) {
void
writeSize(Writer& w, const size_t size)
{
if (size < 0x80u) {
w.template writeBytes<1>(static_cast<uint8_t>(size));
} else {
@@ -98,54 +110,72 @@ namespace bitsery {
* swap utils
*/
//add swap functions to class, to avoid compilation warning about unused functions
struct SwapImpl {
static uint64_t exec(uint64_t value) {
// add swap functions to class, to avoid compilation warning about unused
// functions
struct SwapImpl
{
static uint64_t exec(uint64_t value)
{
#ifdef __GNUC__
return __builtin_bswap64(value);
#else
value = ( value & 0x00000000FFFFFFFF ) << 32 | ( value & 0xFFFFFFFF00000000 ) >> 32;
value = ( value & 0x0000FFFF0000FFFF ) << 16 | ( value & 0xFFFF0000FFFF0000 ) >> 16;
value = ( value & 0x00FF00FF00FF00FF ) << 8 | ( value & 0xFF00FF00FF00FF00 ) >> 8;
value =
(value & 0x00000000FFFFFFFF) << 32 | (value & 0xFFFFFFFF00000000) >> 32;
value =
(value & 0x0000FFFF0000FFFF) << 16 | (value & 0xFFFF0000FFFF0000) >> 16;
value =
(value & 0x00FF00FF00FF00FF) << 8 | (value & 0xFF00FF00FF00FF00) >> 8;
return value;
#endif
}
static uint32_t exec(uint32_t value) {
static uint32_t exec(uint32_t value)
{
#ifdef __GNUC__
return __builtin_bswap32(value);
#else
return ( value & 0x000000ff ) << 24 | ( value & 0x0000ff00 ) << 8 | ( value & 0x00ff0000 ) >> 8 | ( value & 0xff000000 ) >> 24;
return (value & 0x000000ff) << 24 | (value & 0x0000ff00) << 8 |
(value & 0x00ff0000) >> 8 | (value & 0xff000000) >> 24;
#endif
}
static uint16_t exec(uint16_t value) {
static uint16_t exec(uint16_t value)
{
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;
}
};
template<typename TValue>
TValue swap(TValue value) {
TValue
swap(TValue value)
{
constexpr size_t TSize = sizeof(TValue);
using UT = typename std::conditional<TSize == 1, uint8_t,
typename std::conditional<TSize == 2, uint16_t,
typename std::conditional<TSize == 4, uint32_t, uint64_t>::type>::type>::type;
using UT = typename std::conditional<
TSize == 1,
uint8_t,
typename std::conditional<
TSize == 2,
uint16_t,
typename std::conditional<TSize == 4, uint32_t, uint64_t>::type>::type>::
type;
return static_cast<TValue>(SwapImpl::exec(static_cast<UT>(value)));
}
/**
* endianness utils
*/
// add test data in separate struct, because some compilers only support constexpr functions with return-only body
// suppress msvc warnings.
// add test data in separate struct, because some compilers only support
// constexpr functions with return-only body suppress msvc warnings.
#ifdef _MSC_VER
#pragma warning(disable : 4310)
#endif
struct EndiannessTestData {
struct EndiannessTestData
{
static constexpr uint32_t _sample4Bytes = 0x01020304;
static constexpr uint8_t _sample1stByte = (const uint8_t&)_sample4Bytes;
};
@@ -153,76 +183,94 @@ namespace bitsery {
#pragma warning(default : 4310)
#endif
constexpr EndiannessType getSystemEndianness() {
static_assert(EndiannessTestData::_sample1stByte == 0x04 || EndiannessTestData::_sample1stByte == 0x01,
constexpr EndiannessType
getSystemEndianness()
{
static_assert(EndiannessTestData::_sample1stByte == 0x04 ||
EndiannessTestData::_sample1stByte == 0x01,
"system must be either little or big endian");
return EndiannessTestData::_sample1stByte == 0x04 ? EndiannessType::LittleEndian
return EndiannessTestData::_sample1stByte == 0x04
? EndiannessType::LittleEndian
: EndiannessType::BigEndian;
}
template<typename Config>
using ShouldSwap = std::integral_constant<bool, Config::Endianness != details::getSystemEndianness()>;
using ShouldSwap =
std::integral_constant<bool,
Config::Endianness != details::getSystemEndianness()>;
/**
* helper types to work with bits
*/
template<typename T>
struct BitsSize:public std::integral_constant<size_t, sizeof(T) * 8> {
struct BitsSize : public std::integral_constant<size_t, sizeof(T) * 8>
{
static_assert(CHAR_BIT == 8, "only support systems with byte size of 8 bits");
};
template<typename T>
struct ScratchType {
struct ScratchType
{
using type = NotDefinedType;
};
template<>
struct ScratchType<uint8_t> {
struct ScratchType<uint8_t>
{
using type = uint_fast16_t;
};
template<typename T>
struct FastType {
struct FastType
{
using type = T;
};
template<>
struct FastType<uint8_t> {
struct FastType<uint8_t>
{
using type = uint_fast8_t;
};
template<>
struct FastType<uint16_t> {
struct FastType<uint16_t>
{
using type = uint_fast16_t;
};
template<>
struct FastType<uint32_t> {
struct FastType<uint32_t>
{
using type = uint_fast32_t;
};
template<>
struct FastType<uint64_t> {
struct FastType<uint64_t>
{
using type = uint_fast64_t;
};
template<>
struct FastType<int8_t> {
struct FastType<int8_t>
{
using type = int_fast8_t;
};
template<>
struct FastType<int16_t> {
struct FastType<int16_t>
{
using type = int_fast16_t;
};
template<>
struct FastType<int32_t> {
struct FastType<int32_t>
{
using type = int_fast32_t;
};
template<>
struct FastType<int64_t> {
struct FastType<int64_t>
{
using type = int_fast64_t;
};
@@ -231,31 +279,35 @@ namespace bitsery {
*/
template<typename Adapter>
struct OutputAdapterBaseCRTP {
struct OutputAdapterBaseCRTP
{
template<size_t SIZE, typename T>
void writeBytes(const T &v) {
void writeBytes(const T& v)
{
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
writeSwappedValue(&v, ShouldSwap<typename Adapter::TConfig>{});
}
template<size_t SIZE, typename T>
void writeBuffer(const T *buf, size_t count) {
void writeBuffer(const T* buf, size_t count)
{
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
writeSwappedBuffer(buf, count, ShouldSwap<typename Adapter::TConfig>{});
}
template<typename T>
void writeBits(const T &, size_t ) {
static_assert(std::is_void<T>::value,
"Bit-packing is not enabled.\nEnable by call to `enableBitPacking`) or create Serializer with bit packing enabled.");
void writeBits(const T&, size_t)
{
static_assert(
std::is_void<T>::value,
"Bit-packing is not enabled.\nEnable by call to `enableBitPacking`) or "
"create Serializer with bit packing enabled.");
}
void align() {
}
void align() {}
OutputAdapterBaseCRTP() = default;
OutputAdapterBaseCRTP(const OutputAdapterBaseCRTP&) = delete;
@@ -264,62 +316,73 @@ namespace bitsery {
OutputAdapterBaseCRTP& operator=(OutputAdapterBaseCRTP&&) = default;
private:
template<typename T>
void writeSwappedValue(const T *v, std::true_type) {
void writeSwappedValue(const T* v, std::true_type)
{
const auto res = details::swap(*v);
static_cast<Adapter*>(this)->template writeInternalValue<sizeof(T)>(reinterpret_cast<const typename Adapter::TValue *>(&res));
static_cast<Adapter*>(this)->template writeInternalValue<sizeof(T)>(
reinterpret_cast<const typename Adapter::TValue*>(&res));
}
template<typename T>
void writeSwappedValue(const T *v, std::false_type) {
static_cast<Adapter*>(this)->template writeInternalValue<sizeof(T)>(reinterpret_cast<const typename Adapter::TValue *>(v));
void writeSwappedValue(const T* v, std::false_type)
{
static_cast<Adapter*>(this)->template writeInternalValue<sizeof(T)>(
reinterpret_cast<const typename Adapter::TValue*>(v));
}
template<typename T>
void writeSwappedBuffer(const T *v, size_t count, std::true_type) {
void writeSwappedBuffer(const T* v, size_t count, std::true_type)
{
std::for_each(v, std::next(v, count), [this](const T& inner_v) {
const auto res = details::swap(inner_v);
static_cast<Adapter*>(this)->template writeInternalValue<sizeof(T)>(reinterpret_cast<const typename Adapter::TValue *>(&res));
static_cast<Adapter*>(this)->template writeInternalValue<sizeof(T)>(
reinterpret_cast<const typename Adapter::TValue*>(&res));
});
}
template<typename T>
void writeSwappedBuffer(const T *v, size_t count, std::false_type) {
static_cast<Adapter*>(this)->writeInternalBuffer(reinterpret_cast<const typename Adapter::TValue *>(v), count * sizeof(T));
void writeSwappedBuffer(const T* v, size_t count, std::false_type)
{
static_cast<Adapter*>(this)->writeInternalBuffer(
reinterpret_cast<const typename Adapter::TValue*>(v), count * sizeof(T));
}
};
template<typename Adapter>
struct InputAdapterBaseCRTP {
struct InputAdapterBaseCRTP
{
template<size_t SIZE, typename T>
void readBytes(T& v) {
void readBytes(T& v)
{
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
static_cast<Adapter*>(this)->template readInternalValue<sizeof(T)>(reinterpret_cast<typename Adapter::TValue *>(&v));
static_cast<Adapter*>(this)->template readInternalValue<sizeof(T)>(
reinterpret_cast<typename Adapter::TValue*>(&v));
swapDataBits(v, ShouldSwap<typename Adapter::TConfig>{});
}
template<size_t SIZE, typename T>
void readBuffer(T* buf, size_t count) {
void readBuffer(T* buf, size_t count)
{
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
static_cast<Adapter*>(this)->readInternalBuffer(reinterpret_cast<typename Adapter::TValue *>(buf), sizeof(T) * count);
static_cast<Adapter*>(this)->readInternalBuffer(
reinterpret_cast<typename Adapter::TValue*>(buf), sizeof(T) * count);
swapDataBits(buf, count, ShouldSwap<typename Adapter::TConfig>{});
}
template<typename T>
void readBits(T&, size_t) {
static_assert(std::is_void<T>::value,
"Bit-packing is not enabled.\nEnable by call to `enableBitPacking`) or create Deserializer with bit packing enabled.");
void readBits(T&, size_t)
{
static_assert(
std::is_void<T>::value,
"Bit-packing is not enabled.\nEnable by call to `enableBitPacking`) or "
"create Deserializer with bit packing enabled.");
}
void align() {
}
void align() {}
InputAdapterBaseCRTP() = default;
InputAdapterBaseCRTP(const InputAdapterBaseCRTP&) = delete;
@@ -331,30 +394,34 @@ namespace bitsery {
virtual ~InputAdapterBaseCRTP() = default;
private:
template<typename T>
void swapDataBits(T *v, size_t count, std::true_type) {
void swapDataBits(T* v, size_t count, std::true_type)
{
using diff_t = typename std::iterator_traits<T*>::difference_type;
std::for_each(v, std::next(v, static_cast<diff_t>(count)), [](T &x) { x = details::swap(x); });
std::for_each(v, std::next(v, static_cast<diff_t>(count)), [](T& x) {
x = details::swap(x);
});
}
template<typename T>
void swapDataBits(T *, size_t , std::false_type) {
void swapDataBits(T*, size_t, std::false_type)
{
// empty function because no swap is required
}
template<typename T>
void swapDataBits(T &v, std::true_type) {
void swapDataBits(T& v, std::true_type)
{
v = details::swap(v);
}
template<typename T>
void swapDataBits(T &, std::false_type) {
void swapDataBits(T&, std::false_type)
{
// empty function because no swap is required
}
};
}
}
#endif // BITSERY_DETAILS_ADAPTER_COMMON_H

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -30,118 +30,168 @@
namespace bitsery {
namespace brief_syntax {
//these function overloads is required to apply maxSize, and optimize for fundamental types
//for contigous arrays of fundamenal types, memcpy will be applied
// these function overloads is required to apply maxSize, and optimize for
// fundamental types for contigous arrays of fundamenal types, memcpy will be
// applied
template<typename S, typename T, typename std::enable_if<
details::IsFundamentalType<typename traits::ContainerTraits<T>::TValue>::value
&& traits::ContainerTraits<T>::isResizable
>::type * = nullptr>
void processContainer(S &s, T &c, size_t maxSize = std::numeric_limits<size_t>::max()) {
template<typename S,
typename T,
typename std::enable_if<
details::IsFundamentalType<
typename traits::ContainerTraits<T>::TValue>::value &&
traits::ContainerTraits<T>::isResizable>::type* = nullptr>
void
processContainer(S& s,
T& c,
size_t maxSize = std::numeric_limits<size_t>::max())
{
using TValue = typename traits::ContainerTraits<T>::TValue;
s.template container<sizeof(TValue)>(c, maxSize);
}
template<typename S, typename T, typename std::enable_if<
!details::IsFundamentalType<typename traits::ContainerTraits<T>::TValue>::value
&& traits::ContainerTraits<T>::isResizable
>::type * = nullptr>
void processContainer(S &s, T &c, size_t maxSize = std::numeric_limits<size_t>::max()) {
template<typename S,
typename T,
typename std::enable_if<
!details::IsFundamentalType<
typename traits::ContainerTraits<T>::TValue>::value &&
traits::ContainerTraits<T>::isResizable>::type* = nullptr>
void
processContainer(S& s,
T& c,
size_t maxSize = std::numeric_limits<size_t>::max())
{
s.container(c, maxSize);
}
template<typename S, typename T, typename std::enable_if<
details::IsFundamentalType<typename traits::ContainerTraits<T>::TValue>::value
&& !traits::ContainerTraits<T>::isResizable
>::type * = nullptr>
void processContainer(S &s, T &c) {
template<typename S,
typename T,
typename std::enable_if<
details::IsFundamentalType<
typename traits::ContainerTraits<T>::TValue>::value &&
!traits::ContainerTraits<T>::isResizable>::type* = nullptr>
void
processContainer(S& s, T& c)
{
using TValue = typename traits::ContainerTraits<T>::TValue;
s.template container<sizeof(TValue)>(c);
}
template<typename S, typename T, typename std::enable_if<
!details::IsFundamentalType<typename traits::ContainerTraits<T>::TValue>::value
&& !traits::ContainerTraits<T>::isResizable
>::type * = nullptr>
void processContainer(S &s, T &c) {
template<typename S,
typename T,
typename std::enable_if<
!details::IsFundamentalType<
typename traits::ContainerTraits<T>::TValue>::value &&
!traits::ContainerTraits<T>::isResizable>::type* = nullptr>
void
processContainer(S& s, T& c)
{
s.container(c);
}
// overloads for text processing to apply maxSize
template<typename S, typename T, typename std::enable_if<
template<typename S,
typename T,
typename std::enable_if<
traits::ContainerTraits<T>::isResizable>::type* = nullptr>
void processText(S &s, T &c, size_t maxSize = std::numeric_limits<size_t>::max()) {
void
processText(S& s, T& c, size_t maxSize = std::numeric_limits<size_t>::max())
{
using TValue = typename traits::ContainerTraits<T>::TValue;
s.template text<sizeof(TValue)>(c, maxSize);
}
template<typename S, typename T, typename std::enable_if<
template<typename S,
typename T,
typename std::enable_if<
!traits::ContainerTraits<T>::isResizable>::type* = nullptr>
void processText(S &s, T &c) {
void
processText(S& s, T& c)
{
using TValue = typename traits::ContainerTraits<T>::TValue;
s.template text<sizeof(TValue)>(c);
}
// all wrapper functions, that modify behaviour, should inherit from this
struct ModFnc {
};
struct ModFnc
{};
// this type is used to differentiate between container and text behaviour
template<typename T, size_t N, bool isText>
struct CArray : public ModFnc {
CArray(T (&data_)[N]) : data{data_} {};
struct CArray : public ModFnc
{
CArray(T (&data_)[N])
: data{ data_ } {};
T (&data)[N];
};
template<typename S, typename T, size_t N>
void serialize(S &s, CArray<T, N, true> &str) {
void
serialize(S& s, CArray<T, N, true>& str)
{
processText(s, str.data);
}
template<typename S, typename T, size_t N>
void serialize(S &s, CArray<T, N, false> &obj) {
void
serialize(S& s, CArray<T, N, false>& obj)
{
processContainer(s, obj.data);
}
// used to set max container size
template<typename T>
struct MaxSize : public ModFnc {
MaxSize(T &data_, size_t maxSize_) : data{data_}, maxSize{maxSize_} {};
struct MaxSize : public ModFnc
{
MaxSize(T& data_, size_t maxSize_)
: data{ data_ }
, maxSize{ maxSize_ } {};
T& data;
size_t maxSize;
};
//if container, then call procesContainer, this memcpy for fundamental types contiguous container
// if container, then call procesContainer, this memcpy for fundamental types
// contiguous container
template<typename S, typename T>
void processMaxSize(S &s, T& data, size_t maxSize, std::true_type) {
void
processMaxSize(S& s, T& data, size_t maxSize, std::true_type)
{
processContainer(s, data, maxSize);
}
// overload for const T&
template<typename S, typename T>
void processMaxSize(S &s, const T& data, size_t maxSize, std::true_type) {
void
processMaxSize(S& s, const T& data, size_t maxSize, std::true_type)
{
processContainer(s, const_cast<T&>(data), maxSize);
}
// try to call serialize overload with maxsize, extensions use this technique
template<typename S, typename T>
void processMaxSize(S &s, T& data, size_t maxSize, std::false_type) {
void
processMaxSize(S& s, T& data, size_t maxSize, std::false_type)
{
serialize(s, data, maxSize);
}
// overload for const T&
template<typename S, typename T>
void processMaxSize(S &s, const T& data, size_t maxSize, std::false_type) {
void
processMaxSize(S& s, const T& data, size_t maxSize, std::false_type)
{
serialize(s, const_cast<T&>(data), maxSize);
}
template<typename S, typename T>
void serialize(S &s, const MaxSize<T> &ms) {
processMaxSize(s, ms.data, ms.maxSize, details::IsContainerTraitsDefined<typename std::decay<T>::type>{});
void
serialize(S& s, const MaxSize<T>& ms)
{
processMaxSize(
s,
ms.data,
ms.maxSize,
details::IsContainerTraitsDefined<typename std::decay<T>::type>{});
}
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,7 +20,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_DETAILS_NOT_DEFINED_TYPE_H
#define BITSERY_DETAILS_NOT_DEFINED_TYPE_H
@@ -29,38 +28,41 @@
namespace bitsery {
namespace details {
// this type is used to show clearer error messages
struct NotDefinedType {
struct NotDefinedType
{
// just swallow anything that is passed during creating
template<typename... T>
NotDefinedType(T&& ...){}
NotDefinedType(T&&...)
{
}
NotDefinedType() = default;
//define operators so that we also swallow deeper errors, to reduce error stack
//this time will be used as iterator, so define all operators necessary to work with iterators
friend bool operator == (const NotDefinedType&, const NotDefinedType&) {
// define operators so that we also swallow deeper errors, to reduce error
// stack this time will be used as iterator, so define all operators necessary
// to work with iterators
friend bool operator==(const NotDefinedType&, const NotDefinedType&)
{
return true;
}
friend bool operator != (const NotDefinedType&, const NotDefinedType&) {
friend bool operator!=(const NotDefinedType&, const NotDefinedType&)
{
return false;
}
NotDefinedType& operator += (int) {
return *this;
}
NotDefinedType& operator -= (int) {
return *this;
}
NotDefinedType& operator+=(int) { return *this; }
NotDefinedType& operator-=(int) { return *this; }
friend int operator - (const NotDefinedType&, const NotDefinedType&) {
friend int operator-(const NotDefinedType&, const NotDefinedType&)
{
return 0;
}
int& operator*() {
return data;
}
int& operator*() { return data; }
int data{};
};
template<typename T>
struct IsDefined:public std::integral_constant<bool, !std::is_same<NotDefinedType, T>::value> {
struct IsDefined
: public std::integral_constant<bool, !std::is_same<NotDefinedType, T>::value>
{
};
}
}
@@ -68,7 +70,8 @@ namespace bitsery {
namespace std {
// define iterator traits to work with standart algorithms
template<>
struct iterator_traits<bitsery::details::NotDefinedType> {
struct iterator_traits<bitsery::details::NotDefinedType>
{
using difference_type = int;
using value_type = int;
using pointer = int*;

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -23,131 +23,163 @@
#ifndef BITSERY_DETAILS_SERIALIZATION_COMMON_H
#define BITSERY_DETAILS_SERIALIZATION_COMMON_H
#include "adapter_common.h"
#include "../traits/core/traits.h"
#include "adapter_common.h"
#include <tuple>
namespace bitsery {
//this allows to call private serialize method, and construct instance (if no default constructor is provided) for your type
//just make friend it in your class
class Access {
// this allows to call private serialize method, and construct instance (if no
// default constructor is provided) for your type just make friend it in your
// class
class Access
{
public:
template<typename S, typename T>
static auto serialize(S &s, T &obj) -> decltype(obj.serialize(s)) {
static auto serialize(S& s, T& obj) -> decltype(obj.serialize(s))
{
obj.serialize(s);
}
template<typename T>
static T create() {
static T create()
{
// if you get an error here, please create default constructor
return T{};
}
template<typename T>
static T* create(void* ptr) {
static T* create(void* ptr)
{
return new (ptr) T{};
}
};
// convenient functors that can be passed as lambda to serializer/deserializer instead of writing lambda
// e.g. instead of writing this:
// s.container(c, 100, [](S& s, float& v) { s.ext4b(v, CompactValue{});});
// you can write like this
// convenient functors that can be passed as lambda to serializer/deserializer
// instead of writing lambda e.g. instead of writing this: s.container(c, 100,
// [](S& s, float& v) { s.ext4b(v, CompactValue{});}); you can write like this
// s.container(c, 100, FtorExtValue2b<CompactValue>{});
template<size_t N, typename Ext>
struct FtorExtValue : public Ext {
struct FtorExtValue : public Ext
{
template<typename S, typename T>
void operator()(S& s, T& v) const {
void operator()(S& s, T& v) const
{
s.template ext<N>(v, static_cast<const Ext&>(*this));
}
};
template<typename Ext>
struct FtorExtValue1b: FtorExtValue<1, Ext> {};
struct FtorExtValue1b : FtorExtValue<1, Ext>
{
};
template<typename Ext>
struct FtorExtValue2b: FtorExtValue<2, Ext> {};
struct FtorExtValue2b : FtorExtValue<2, Ext>
{
};
template<typename Ext>
struct FtorExtValue4b: FtorExtValue<4, Ext> {};
struct FtorExtValue4b : FtorExtValue<4, Ext>
{
};
template<typename Ext>
struct FtorExtValue8b: FtorExtValue<8, Ext> {};
struct FtorExtValue8b : FtorExtValue<8, Ext>
{
};
template<typename Ext>
struct FtorExtValue16b: FtorExtValue<16, Ext> {};
struct FtorExtValue16b : FtorExtValue<16, Ext>
{
};
template<typename Ext>
struct FtorExtObject : public Ext {
struct FtorExtObject : public Ext
{
template<typename S, typename T>
void operator()(S& s, T& v) const {
void operator()(S& s, T& v) const
{
s.ext(v, static_cast<const Ext&>(*this));
}
};
//when call to serialize function is ambiguous (member and non-member serialize function exists for a type)
//specialize this class by inheriting from either UseNonMemberFnc or UseMemberFnc
//e.g.
//template <> struct SelectSerializeFnc<MyDerivedClass>:UseMemberFnc {};
// when call to serialize function is ambiguous (member and non-member serialize
// function exists for a type) specialize this class by inheriting from either
// UseNonMemberFnc or UseMemberFnc e.g. template <> struct
// SelectSerializeFnc<MyDerivedClass>:UseMemberFnc {};
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
struct UseNonMemberFnc : std::integral_constant<int, 1> {
};
struct UseMemberFnc : std::integral_constant<int, 2> {
};
struct UseNonMemberFnc : std::integral_constant<int, 1>
{};
struct UseMemberFnc : std::integral_constant<int, 2>
{};
namespace details {
// helper types for error handling
template<typename T>
struct IsContainerTraitsDefined : public IsDefined<typename traits::ContainerTraits<T>::TValue> {
struct IsContainerTraitsDefined
: public IsDefined<typename traits::ContainerTraits<T>::TValue>
{
};
template<typename T>
struct IsTextTraitsDefined : public IsDefined<typename traits::TextTraits<T>::TValue> {
struct IsTextTraitsDefined
: public IsDefined<typename traits::TextTraits<T>::TValue>
{
};
template<typename Ext, typename T>
struct IsExtensionTraitsDefined : public IsDefined<typename traits::ExtensionTraits<Ext, T>::TValue> {
struct IsExtensionTraitsDefined
: public IsDefined<typename traits::ExtensionTraits<Ext, T>::TValue>
{
};
#ifdef _MSC_VER
// helper types for HasSerializeFunction
template<typename S, typename T>
using TrySerializeFunction = decltype(serialize(std::declval<S &>(), std::declval<T &>()));
using TrySerializeFunction =
decltype(serialize(std::declval<S&>(), std::declval<T&>()));
template<typename S, typename T>
struct HasSerializeFunctionHelper {
struct HasSerializeFunctionHelper
{
template<typename Q, typename R, typename = TrySerializeFunction<Q, R>>
static std::true_type tester(Q&&, R&&);
static std::false_type tester(...);
using type = decltype(tester(std::declval<S>(), std::declval<T>()));
};
template<typename S, typename T>
struct HasSerializeFunction :HasSerializeFunctionHelper<S, T>::type {};
struct HasSerializeFunction : HasSerializeFunctionHelper<S, T>::type
{
};
// helper types for HasSerializeMethod
template<typename S, typename T>
using TrySerializeMethod = decltype(Access::serialize(std::declval<S &>(), std::declval<T &>()));
using TrySerializeMethod =
decltype(Access::serialize(std::declval<S&>(), std::declval<T&>()));
template<typename S, typename T>
struct HasSerializeMethodHelper {
struct HasSerializeMethodHelper
{
template<typename Q, typename R, typename = TrySerializeMethod<Q, R>>
static std::true_type tester(Q&&, R&&);
static std::false_type tester(...);
using type = decltype(tester(std::declval<S>(), std::declval<T>()));
};
template<typename S, typename T>
struct HasSerializeMethod :HasSerializeMethodHelper<S, T>::type {};
struct HasSerializeMethod : HasSerializeMethodHelper<S, T>::type
{
};
// helper types for IsBriefSyntaxIncluded
template<typename S, typename T>
using TryProcessBriefSyntax = decltype(processBriefSyntax(std::declval<S &>(), std::declval<T &&>()));
using TryProcessBriefSyntax =
decltype(processBriefSyntax(std::declval<S&>(), std::declval<T&&>()));
template<typename S, typename T>
struct IsBriefSyntaxIncludedHelper {
struct IsBriefSyntaxIncludedHelper
{
template<typename Q, typename R, typename = TryProcessBriefSyntax<Q, R>>
static std::true_type tester(Q&&, R&&);
static std::false_type tester(...);
@@ -155,98 +187,126 @@ namespace bitsery {
};
template<typename S, typename T>
struct IsBriefSyntaxIncluded :IsBriefSyntaxIncludedHelper<S, T>::type {};
struct IsBriefSyntaxIncluded : IsBriefSyntaxIncludedHelper<S, T>::type
{
};
#else
// helper metafunction, that is added to c++17
template<typename... Ts>
struct make_void {
struct make_void
{
typedef void type;
};
template<typename... Ts>
using void_t = typename make_void<Ts...>::type;
template<typename, typename, typename = void>
struct HasSerializeFunction : std::false_type {
struct HasSerializeFunction : std::false_type
{
};
template<typename S, typename T>
struct HasSerializeFunction<S, T,
void_t<decltype(serialize(std::declval<S &>(), std::declval<T &>()))>
> : std::true_type {
struct HasSerializeFunction<
S,
T,
void_t<decltype(serialize(std::declval<S&>(), std::declval<T&>()))>>
: std::true_type
{
};
template<typename, typename, typename = void>
struct HasSerializeMethod : std::false_type {
struct HasSerializeMethod : std::false_type
{
};
template<typename S, typename T>
struct HasSerializeMethod<S, T,
void_t<decltype(Access::serialize(std::declval<S &>(), std::declval<T &>()))>
> : std::true_type {
struct HasSerializeMethod<
S,
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 {
struct IsBriefSyntaxIncluded : std::false_type
{
};
template<typename S, typename T>
struct IsBriefSyntaxIncluded<S, T,
void_t<decltype(processBriefSyntax(std::declval<S &>(), std::declval<T &&>()))>
> : std::true_type {
struct IsBriefSyntaxIncluded<
S,
T,
void_t<decltype(processBriefSyntax(std::declval<S&>(), std::declval<T&&>()))>>
: std::true_type
{
};
#endif
// used for extensions when extension TValue = void
struct DummyType {
};
struct DummyType
{};
/*
* this includes all integral types, floats and enums(except bool)
*/
template<typename T>
struct IsFundamentalType : std::integral_constant<bool,
std::is_enum<T>::value
|| std::is_floating_point<T>::value
|| std::is_integral<T>::value> {
struct IsFundamentalType
: std::integral_constant<bool,
std::is_enum<T>::value ||
std::is_floating_point<T>::value ||
std::is_integral<T>::value>
{
};
template<typename T, typename Integral = void>
struct IntegralFromFundamental {
struct IntegralFromFundamental
{
using TValue = T;
};
template<typename T>
struct IntegralFromFundamental<T, typename std::enable_if<std::is_enum<T>::value>::type> {
struct IntegralFromFundamental<
T,
typename std::enable_if<std::is_enum<T>::value>::type>
{
using TValue = typename std::underlying_type<T>::type;
};
template<typename T>
struct IntegralFromFundamental<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
using TValue = typename std::conditional<std::is_same<T, float>::value, uint32_t, uint64_t>::type;
struct IntegralFromFundamental<
T,
typename std::enable_if<std::is_floating_point<T>::value>::type>
{
using TValue = typename std::
conditional<std::is_same<T, float>::value, uint32_t, uint64_t>::type;
};
template<typename T>
struct UnsignedFromFundamental {
using type = typename std::make_unsigned<typename IntegralFromFundamental<T>::TValue>::type;
struct UnsignedFromFundamental
{
using type = typename std::make_unsigned<
typename IntegralFromFundamental<T>::TValue>::type;
};
template<typename T>
using SameSizeUnsigned = typename UnsignedFromFundamental<T>::type;
/*
* functions for object serialization
*/
template<typename S, typename T>
struct SerializeFunction {
struct SerializeFunction
{
static void invoke(S &s, T &v) {
static_assert(HasSerializeFunction<S, T>::value || HasSerializeMethod<S, T>::value,
"\nPlease define 'serialize' function for your type (inside or outside of class):\n"
static void invoke(S& s, T& v)
{
static_assert(HasSerializeFunction<S, T>::value ||
HasSerializeMethod<S, T>::value,
"\nPlease define 'serialize' function for your type (inside "
"or outside of class):\n"
" template<typename S>\n"
" void serialize(S& s)\n"
" {\n"
@@ -256,26 +316,34 @@ namespace bitsery {
selectSerializeFnc(s, v, SelectSerializeFnc<TDecayed>{});
}
static constexpr bool isDefined() {
static constexpr bool isDefined()
{
return HasSerializeFunction<S, T>::value || HasSerializeMethod<S, T>::value;
}
private:
static void selectSerializeFnc(S &s, T &v, std::integral_constant<int, 0>) {
static_assert(!(HasSerializeFunction<S, T>::value && HasSerializeMethod<S, T>::value),
static void selectSerializeFnc(S& s, T& v, std::integral_constant<int, 0>)
{
static_assert(
!(HasSerializeFunction<S, T>::value && HasSerializeMethod<S, T>::value),
"\nPlease define only one 'serialize' function (member OR free).\n"
"If serialization function is inherited from base class, then explicitly select correct function for your type e.g.:\n"
"If serialization function is inherited from base class, then explicitly "
"select correct function for your type e.g.:\n"
" template <>\n"
" struct SelectSerializeFnc<DerivedClass>:UseMemberFnc {};\n");
selectSerializeFnc(s, v, std::integral_constant<int,
selectSerializeFnc(s,
v,
std::integral_constant < int,
HasSerializeFunction<S, T>::value ? 1 : 2 > {});
}
static void selectSerializeFnc(S &s, T &v, std::integral_constant<int, 1>) {
static void selectSerializeFnc(S& s, T& v, std::integral_constant<int, 1>)
{
serialize(s, v);
}
static void selectSerializeFnc(S &s, T &v, std::integral_constant<int, 2>) {
static void selectSerializeFnc(S& s, T& v, std::integral_constant<int, 2>)
{
Access::serialize(s, v);
}
};
@@ -285,10 +353,13 @@ namespace bitsery {
*/
template<typename S, typename T, typename Enabled = void>
struct BriefSyntaxFunction {
struct BriefSyntaxFunction
{
static void invoke(S &s, T &&obj) {
static_assert(IsBriefSyntaxIncluded<S, T>::value,
static void invoke(S& s, T&& obj)
{
static_assert(
IsBriefSyntaxIncluded<S, T>::value,
"\nPlease include '<bitsery/brief_syntax.h>' to use operator():\n");
processBriefSyntax(s, std::forward<T>(obj));
@@ -300,111 +371,139 @@ namespace bitsery {
*/
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>
struct FindIndex<Index, Cond, Conds...> :
std::conditional<Cond::value, std::integral_constant<int, Index>, FindIndex<Index+1, Conds...>>::type
{};
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;
template<typename T, typename... Us>
struct GetConvertibleTypeIndexFromTuple<T, std::tuple<Us...>> : FindIndex<0, std::is_convertible<Us&, T&>...> {};
struct GetConvertibleTypeIndexFromTuple<T, std::tuple<Us...>>
: FindIndex<0, std::is_convertible<Us&, T&>...>
{
};
template<typename T, typename Tuple>
struct IsExistsConvertibleTupleType;
template<typename T, typename... Us>
struct IsExistsConvertibleTupleType<T, std::tuple<Us...>> :
std::integral_constant<bool, GetConvertibleTypeIndexFromTuple<T, std::tuple<Us...>>::value != sizeof...(Us)> {};
struct IsExistsConvertibleTupleType<T, std::tuple<Us...>>
: std::integral_constant<
bool,
GetConvertibleTypeIndexFromTuple<T, std::tuple<Us...>>::value !=
sizeof...(Us)>
{
};
/*
* get context from internal or external, and check if it's convertible or not
*/
template<bool AssertExists, typename TCast, typename TContext>
TCast* getDirectlyIfExists(TContext& ctx, std::true_type) {
TCast*
getDirectlyIfExists(TContext& ctx, std::true_type)
{
return &static_cast<TCast&>(ctx);
}
template<bool AssertExists, typename TCast, typename TContext>
TCast* getDirectlyIfExists(TContext& , std::false_type) {
TCast*
getDirectlyIfExists(TContext&, std::false_type)
{
// TCast cannot be convertible from provided context
static_assert(!AssertExists,
"Invalid context cast. Context type doesn't exists.\nSome functionality requires (de)seserializer to have specific context.");
static_assert(
!AssertExists,
"Invalid context cast. Context type doesn't exists.\nSome functionality "
"requires (de)seserializer to have specific context.");
return nullptr;
}
template<bool AssertExists, typename TCast, typename... TArgs>
TCast* getFromTupleIfExists(std::tuple<TArgs...>& ctx, std::true_type) {
using TupleIndex = GetConvertibleTypeIndexFromTuple<TCast, std::tuple<TArgs...>>;
TCast*
getFromTupleIfExists(std::tuple<TArgs...>& ctx, std::true_type)
{
using TupleIndex =
GetConvertibleTypeIndexFromTuple<TCast, std::tuple<TArgs...>>;
return &static_cast<TCast&>(std::get<TupleIndex::value>(ctx));
}
template<bool AssertExists, typename TCast, typename... TArgs>
TCast* getFromTupleIfExists(std::tuple<TArgs...>& , std::false_type) {
TCast*
getFromTupleIfExists(std::tuple<TArgs...>&, std::false_type)
{
// TCast cannot be convertible from provided context
static_assert(!AssertExists,
"Invalid context cast. Context type doesn't exists.\nSome functionality requires (de)seserializer to have specific context.");
static_assert(
!AssertExists,
"Invalid context cast. Context type doesn't exists.\nSome functionality "
"requires (de)seserializer to have specific context.");
return nullptr;
}
// non tuple context
template<bool AssertExists, typename TCast, typename TContext>
TCast* getContext(TContext& ctx) {
return getDirectlyIfExists<AssertExists, TCast>(ctx, std::is_convertible<TContext&, TCast&>{});
TCast*
getContext(TContext& ctx)
{
return getDirectlyIfExists<AssertExists, TCast>(
ctx, std::is_convertible<TContext&, TCast&>{});
}
// tuple context
template<bool AssertExists, typename TCast, typename... TArgs>
TCast* getContext(std::tuple<TArgs...>& ctx) {
return getFromTupleIfExists<AssertExists, TCast>(ctx, IsExistsConvertibleTupleType<TCast, std::tuple<TArgs...>>{});
TCast*
getContext(std::tuple<TArgs...>& ctx)
{
return getFromTupleIfExists<AssertExists, TCast>(
ctx, IsExistsConvertibleTupleType<TCast, std::tuple<TArgs...>>{});
}
template<typename Adapter, typename Context>
class AdapterAndContextRef {
class AdapterAndContextRef
{
public:
static constexpr bool HasContext = true;
using Config = typename Adapter::TConfig;
// constructing adapter in place is important,
// because enableBitPacking might create instance with bit write/read enabled adapter wrapper,
// which has non trivial destructor
// because enableBitPacking might create instance with bit write/read enabled
// adapter wrapper, which has non trivial destructor
template<typename... TArgs>
explicit AdapterAndContextRef(Context& ctx, TArgs&&... args)
: _adapter{std::forward<TArgs>(args)...},
_context{ctx}
: _adapter{ std::forward<TArgs>(args)... }
, _context{ ctx }
{
}
/*
* get serialization context.
* this is optional, but might be required for some specific serialization flows.
* this is optional, but might be required for some specific serialization
* flows.
*/
template<typename T>
T& context() {
T& context()
{
return *getContext<true, T>(_context);
}
template<typename T>
T* contextOrNull() {
T* contextOrNull()
{
return getContext<false, T>(_context);
}
Adapter& adapter() & {
return _adapter;
}
Adapter& adapter() & { return _adapter; }
Adapter adapter() && {
return std::move(_adapter);
}
Adapter adapter() && { return std::move(_adapter); }
protected:
Adapter _adapter;
@@ -412,7 +511,8 @@ namespace bitsery {
};
template<typename Adapter>
class AdapterAndContextRef<Adapter, void> {
class AdapterAndContextRef<Adapter, void>
{
public:
static constexpr bool HasContext = false;
using Config = typename Adapter::TConfig;
@@ -424,22 +524,20 @@ namespace bitsery {
}
template<typename T>
T& context() {
T& context()
{
static_assert(std::is_void<T>::value, "Context is not defined (is void).");
}
template<typename T>
T* contextOrNull() {
T* contextOrNull()
{
return nullptr;
}
Adapter& adapter() & {
return _adapter;
}
Adapter& adapter() & { return _adapter; }
Adapter adapter() && {
return std::move(_adapter);
}
Adapter adapter() && { return std::move(_adapter); }
protected:
Adapter _adapter;
@@ -450,15 +548,16 @@ namespace bitsery {
*/
template<typename T, template<typename...> class Template>
struct IsSpecializationOf : std::false_type {
struct IsSpecializationOf : std::false_type
{
};
template<template<typename...> class Template, typename... Args>
struct IsSpecializationOf<Template<Args...>, Template> : std::true_type {
struct IsSpecializationOf<Template<Args...>, Template> : std::true_type
{
};
}
}
#endif // BITSERY_DETAILS_SERIALIZATION_COMMON_H

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -30,76 +30,94 @@ namespace bitsery {
namespace details {
template<bool CheckOverflow>
class CompactValueImpl {
class CompactValueImpl
{
public:
template<typename Ser, typename T, typename Fnc>
void serialize(Ser &s, const T &v, Fnc &&) const {
void serialize(Ser& s, const T& v, Fnc&&) const
{
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "");
using TValue = typename IntegralFromFundamental<T>::TValue;
serializeImpl(s.adapter(), reinterpret_cast<const TValue&>(v), std::integral_constant<bool, sizeof(T) != 1>{});
serializeImpl(s.adapter(),
reinterpret_cast<const TValue&>(v),
std::integral_constant<bool, sizeof(T) != 1>{});
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des &d, T &v, Fnc &&) const {
void deserialize(Des& d, T& v, Fnc&&) const
{
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "");
using TValue = typename IntegralFromFundamental<T>::TValue;
deserializeImpl(d.adapter(), reinterpret_cast<TValue &>(v), std::integral_constant<bool, sizeof(T) != 1>{});
deserializeImpl(d.adapter(),
reinterpret_cast<TValue&>(v),
std::integral_constant<bool, sizeof(T) != 1>{});
}
private:
// if value is 1byte size, just serialize/ deserialize whole value
template<typename Writer, typename T>
void serializeImpl(Writer &writer, const T &v, std::false_type) const {
void serializeImpl(Writer& writer, const T& v, std::false_type) const
{
writer.template writeBytes<1>(v);
}
template<typename Reader, typename T>
void deserializeImpl(Reader &reader, T &v, std::false_type) const {
void deserializeImpl(Reader& reader, T& v, std::false_type) const
{
reader.template readBytes<1>(v);
}
// when value is bigger than 1byte size,
template<typename Writer, typename T>
void serializeImpl(Writer &writer, const T &v, std::true_type) const {
auto val = zigZagEncode(v, std::is_signed<typename IntegralFromFundamental<T>::TValue>{});
void serializeImpl(Writer& writer, const T& v, std::true_type) const
{
auto val = zigZagEncode(
v, std::is_signed<typename IntegralFromFundamental<T>::TValue>{});
writeBytes(writer, val);
}
template<typename Reader, typename T>
void deserializeImpl(Reader &reader, T &v, std::true_type) const {
void deserializeImpl(Reader& reader, T& v, std::true_type) const
{
using TUnsigned = SameSizeUnsigned<T>;
TUnsigned res{};
readBytes<Reader::TConfig::CheckDataErrors>(reader, res);
v = zigZagDecode<T>(res, std::is_signed<typename IntegralFromFundamental<T>::TValue>{});
v = zigZagDecode<T>(
res, std::is_signed<typename IntegralFromFundamental<T>::TValue>{});
}
// zigzag encode signed types
template<typename T>
const SameSizeUnsigned<T> &zigZagEncode(const T &v, std::false_type) const {
const SameSizeUnsigned<T>& zigZagEncode(const T& v, std::false_type) const
{
return v;
}
template<typename TResult, typename TUnsigned>
const TResult &zigZagDecode(const TUnsigned &v, std::false_type) const{
const TResult& zigZagDecode(const TUnsigned& v, std::false_type) const
{
return v;
}
template<typename T>
SameSizeUnsigned<T> zigZagEncode(const T &v, std::true_type) const {
return static_cast<SameSizeUnsigned<T>>((v << 1) ^ (v >> (BitsSize<T>::value - 1)));
SameSizeUnsigned<T> zigZagEncode(const T& v, std::true_type) const
{
return static_cast<SameSizeUnsigned<T>>((v << 1) ^
(v >> (BitsSize<T>::value - 1)));
}
template<typename TResult, typename TUnsigned>
TResult zigZagDecode(TUnsigned v, std::true_type) const {
return static_cast<TResult>((v >> 1) ^ (~(v & 1) + 1)); // same as -(v & 1), but no warning on VisualStudio
TResult zigZagDecode(TUnsigned v, std::true_type) const
{
return static_cast<TResult>(
(v >> 1) ^
(~(v & 1) + 1)); // same as -(v & 1), but no warning on VisualStudio
}
// write/read bytes one by one
template<typename Writer, typename T>
void writeBytes(Writer &w, const T &v) const {
void writeBytes(Writer& w, const T& v) const
{
using TFast = typename FastType<T>::type;
auto val = static_cast<TFast>(v);
while (val > 0x7Fu) {
@@ -110,7 +128,8 @@ namespace bitsery {
}
template<bool CheckErrors, typename Reader, typename T>
void readBytes(Reader &r, T &v) const {
void readBytes(Reader& r, T& v) const
{
using TFast = typename FastType<T>::type;
constexpr auto TBITS = sizeof(T) * 8;
uint8_t b1{ 0x80u };
@@ -121,11 +140,18 @@ namespace bitsery {
tmp += static_cast<TFast>(b1 & 0x7Fu) << i;
}
v = static_cast<T>(tmp);
handleReadOverflow<Reader, T>(r, i, b1,
std::integral_constant<bool, CheckOverflow && CheckErrors>{});
handleReadOverflow<Reader, T>(r,
i,
b1,
std::integral_constant < bool,
CheckOverflow&& CheckErrors > {});
}
template<typename Reader, typename T>
void handleReadOverflow(Reader& r, unsigned shiftedBy, uint8_t remainder, std::true_type) const {
void handleReadOverflow(Reader& r,
unsigned shiftedBy,
uint8_t remainder,
std::true_type) const
{
constexpr auto TBITS = sizeof(T) * 8;
if (shiftedBy > TBITS && remainder >> (TBITS + 7 - shiftedBy)) {
r.error(bitsery::ReaderError::InvalidData);
@@ -133,37 +159,43 @@ namespace bitsery {
}
template<typename Reader, typename T>
void handleReadOverflow(Reader &, unsigned , uint8_t , std::false_type) const {
void handleReadOverflow(Reader&, unsigned, uint8_t, std::false_type) const
{
}
};
}
namespace ext {
// this type will use value overload, and do not check if type is sufficiently large during deserialization
class CompactValue: public details::CompactValueImpl<false> {};
// this type will use value overload, and do not check if type is sufficiently
// large during deserialization
class CompactValue : public details::CompactValueImpl<false>
{};
// this type will enable object overload, and set DataOverflow if value doesn't fit in type, during deserialization
class CompactValueAsObject: public details::CompactValueImpl<true> {};
// this type will enable object overload, and set DataOverflow if value doesn't
// fit in type, during deserialization
class CompactValueAsObject : public details::CompactValueImpl<true>
{};
}
namespace traits {
template<typename T>
struct ExtensionTraits<ext::CompactValue, T> {
struct ExtensionTraits<ext::CompactValue, T>
{
using TValue = T;
static constexpr bool SupportValueOverload = true;
// disable object overload, because we don't have implemented serialization function for fundamental types
// disable object overload, because we don't have implemented serialization
// function for fundamental types
static constexpr bool SupportObjectOverload = false;
static constexpr bool SupportLambdaOverload = false;
};
template<typename T>
struct ExtensionTraits<ext::CompactValueAsObject, T> {
struct ExtensionTraits<ext::CompactValueAsObject, T>
{
// use dummy implemenations for value and object overload
using TValue = void;
// only enable object overload
@@ -173,7 +205,8 @@ namespace bitsery {
};
template<typename T, bool Check>
struct ExtensionTraits<details::CompactValueImpl<Check>, T> {
struct ExtensionTraits<details::CompactValueImpl<Check>, T>
{
using TValue = T;
static constexpr bool SupportValueOverload = !Check;
static constexpr bool SupportObjectOverload = Check;
@@ -184,5 +217,4 @@ namespace bitsery {
}
#endif // BITSERY_EXT_COMPACT_VALUE_H

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -29,7 +29,9 @@ namespace bitsery {
namespace details {
template<typename TValue, typename TContainer>
size_t findEntropyIndex(const TValue &v, const TContainer &defValues) {
size_t
findEntropyIndex(const TValue& v, const TContainer& defValues)
{
size_t index{ 1u };
for (auto& d : defValues) {
if (d == v)
@@ -43,24 +45,28 @@ namespace bitsery {
namespace ext {
template<typename TContainer>
class Entropy {
class Entropy
{
public:
/**
* Allows entropy-encoding technique, by writing few bits for most common values
* Allows entropy-encoding technique, by writing few bits for most common
* values
* @param values list of most common values
* @param alignBeforeData only makes sense when bit-packing enabled, by default aligns after writing bits for index
* @param alignBeforeData only makes sense when bit-packing enabled, by
* default aligns after writing bits for index
*/
constexpr Entropy(TContainer& values, bool alignBeforeData = true)
: _values{values},
_alignBeforeData{alignBeforeData} {
};
: _values{ values }
, _alignBeforeData{ alignBeforeData } {};
template<typename Ser, typename T, typename Fnc>
void serialize(Ser &s, const T &obj, Fnc &&fnc) const {
void serialize(Ser& s, const T& obj, Fnc&& fnc) const
{
assert(traits::ContainerTraits<TContainer>::size(_values) > 0);
auto index = details::findEntropyIndex(obj, _values);
s.ext(index, ext::ValueRange<size_t>{0u, traits::ContainerTraits<TContainer>::size(_values)});
s.ext(index,
ext::ValueRange<size_t>{
0u, traits::ContainerTraits<TContainer>::size(_values) });
if (_alignBeforeData)
s.adapter().align();
if (!index)
@@ -68,17 +74,21 @@ namespace bitsery {
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des &d, T &obj, Fnc &&fnc) const {
void deserialize(Des& d, T& obj, Fnc&& fnc) const
{
assert(traits::ContainerTraits<TContainer>::size(_values) > 0);
size_t index{};
d.ext(index, ext::ValueRange<size_t>{0u, traits::ContainerTraits<TContainer>::size(_values)});
d.ext(index,
ext::ValueRange<size_t>{
0u, traits::ContainerTraits<TContainer>::size(_values) });
if (_alignBeforeData)
d.adapter().align();
if (index) {
using TDiff = typename std::iterator_traits<decltype(std::begin(_values))>::difference_type;
obj = static_cast<T>(*std::next(std::begin(_values), static_cast<TDiff>(index-1)));
}
else
using TDiff = typename std::iterator_traits<decltype(std::begin(
_values))>::difference_type;
obj = static_cast<T>(
*std::next(std::begin(_values), static_cast<TDiff>(index - 1)));
} else
fnc(d, obj);
}
@@ -90,7 +100,8 @@ namespace bitsery {
namespace traits {
template<typename TContainer, typename T>
struct ExtensionTraits<ext::Entropy<TContainer>, T> {
struct ExtensionTraits<ext::Entropy<TContainer>, T>
{
using TValue = T;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
@@ -100,5 +111,4 @@ namespace bitsery {
}
#endif // BITSERY_EXT_ENTROPY_H

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -30,15 +30,18 @@ namespace bitsery {
namespace ext {
/*
* enables forward and backward compatibility, by allowing to append additional data at the end of serialization
* old deserialization method will ignore additional data by jumping through it at the end of deserialization flow
* new deserialization method will read all 0 for new fields if there is no data for it
* enables forward and backward compatibility, by allowing to append additional
* data at the end of serialization old deserialization method will ignore
* additional data by jumping through it at the end of deserialization flow new
* deserialization method will read all 0 for new fields if there is no data for
* it
*/
class Growable {
class Growable
{
public:
template<typename Ser, typename T, typename Fnc>
void serialize(Ser &ser, const T &obj, Fnc &&fnc) const {
void serialize(Ser& ser, const T& obj, Fnc&& fnc) const
{
auto& writer = ser.adapter();
const auto startPos = writer.currentWritePos();
writer.template writeBytes<4>(static_cast<uint32_t>(0));
@@ -52,7 +55,8 @@ namespace bitsery {
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des &des, T &obj, Fnc &&fnc) const {
void deserialize(Des& des, T& obj, Fnc&& fnc) const
{
auto& reader = des.adapter();
uint32_t size{};
const auto readEndPos = reader.currentReadEndPos();
@@ -70,7 +74,8 @@ namespace bitsery {
namespace traits {
template<typename T>
struct ExtensionTraits<ext::Growable, T> {
struct ExtensionTraits<ext::Growable, T>
{
using TValue = T;
static constexpr bool SupportValueOverload = false;
static constexpr bool SupportObjectOverload = true;
@@ -80,5 +85,4 @@ namespace bitsery {
}
#endif // BITSERY_EXT_GROWABLE_H

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -23,28 +23,31 @@
#ifndef BITSERY_EXT_INHERITANCE_H
#define BITSERY_EXT_INHERITANCE_H
#include "../traits/core/traits.h"
#include "../ext/utils/memory_resource.h"
#include "../traits/core/traits.h"
#include <unordered_set>
namespace bitsery {
namespace ext {
//required when virtual inheritance (ext::VirtualBaseClass) exists in serialization flow.
//for standard inheritance (ext::BaseClass) it is optional.
class InheritanceContext {
// required when virtual inheritance (ext::VirtualBaseClass) exists in
// serialization flow. for standard inheritance (ext::BaseClass) it is optional.
class InheritanceContext
{
public:
explicit InheritanceContext(MemResourceBase* memResource = nullptr)
: _virtualBases{ pointer_utils::StdPolyAlloc<const void*>{ memResource } }
{}
{
}
InheritanceContext(const InheritanceContext&) = delete;
InheritanceContext& operator=(const InheritanceContext&) = delete;
InheritanceContext(InheritanceContext&&) = default;
InheritanceContext& operator=(InheritanceContext&&) = default;
template<typename TDerived, typename TBase>
void beginBase(const TDerived &derived, const TBase &) {
void beginBase(const TDerived& derived, const TBase&)
{
if (_depth == 0) {
const void* ptr = std::addressof(derived);
if (_parentPtr != ptr)
@@ -55,14 +58,13 @@ namespace bitsery {
}
template<typename TDerived, typename TBase>
bool beginVirtualBase(const TDerived &derived, const TBase &base) {
bool beginVirtualBase(const TDerived& derived, const TBase& base)
{
beginBase(derived, base);
return _virtualBases.emplace(std::addressof(base)).second;
}
void end() {
--_depth;
}
void end() { --_depth; }
private:
// these members are required to know when we can clear _virtualBases
@@ -70,17 +72,19 @@ namespace bitsery {
const void* _parentPtr{};
// add virtual bases to the list, as long as we're on the same parent
std::unordered_set<const void*,
std::hash<const void*>, std::equal_to<const void*>,
pointer_utils::StdPolyAlloc<const void*>
> _virtualBases;
std::hash<const void*>,
std::equal_to<const void*>,
pointer_utils::StdPolyAlloc<const void*>>
_virtualBases;
};
template<typename TBase>
class BaseClass {
class BaseClass
{
public:
template<typename Ser, typename T, typename Fnc>
void serialize(Ser &ser, const T &obj, Fnc &&fnc) const {
void serialize(Ser& ser, const T& obj, Fnc&& fnc) const
{
auto& resObj = static_cast<const TBase&>(obj);
if (auto ctx = ser.template contextOrNull<InheritanceContext>()) {
ctx->beginBase(obj, resObj);
@@ -92,7 +96,8 @@ namespace bitsery {
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des &des, T &obj, Fnc &&fnc) const {
void deserialize(Des& des, T& obj, Fnc&& fnc) const
{
auto& resObj = static_cast<TBase&>(obj);
if (auto ctx = des.template contextOrNull<InheritanceContext>()) {
ctx->beginBase(obj, resObj);
@@ -102,16 +107,16 @@ namespace bitsery {
fnc(des, resObj);
}
}
};
// requires InheritanceContext
template<typename TBase>
class VirtualBaseClass {
class VirtualBaseClass
{
public:
template<typename Ser, typename T, typename Fnc>
void serialize(Ser &ser, const T &obj, Fnc &&fnc) const {
void serialize(Ser& ser, const T& obj, Fnc&& fnc) const
{
auto& ctx = ser.template context<InheritanceContext>();
auto& resObj = static_cast<const TBase&>(obj);
if (ctx.beginVirtualBase(obj, resObj))
@@ -120,21 +125,22 @@ namespace bitsery {
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des &des, T &obj, Fnc &&fnc) const {
void deserialize(Des& des, T& obj, Fnc&& fnc) const
{
auto& ctx = des.template context<InheritanceContext>();
auto& resObj = static_cast<TBase&>(obj);
if (ctx.beginVirtualBase(obj, resObj))
fnc(des, resObj);
ctx.end();
}
};
}
namespace traits {
template<typename TBase, typename T>
struct ExtensionTraits<ext::BaseClass<TBase>, T> {
struct ExtensionTraits<ext::BaseClass<TBase>, T>
{
static_assert(std::is_base_of<TBase, T>::value, "Invalid base class");
using TValue = TBase;
@@ -144,19 +150,20 @@ namespace bitsery {
};
template<typename TBase, typename T>
struct ExtensionTraits<ext::VirtualBaseClass<TBase>, T> {
struct ExtensionTraits<ext::VirtualBaseClass<TBase>, T>
{
static_assert(std::is_base_of<TBase, T>::value, "Invalid base class");
using TValue = TBase;
static constexpr bool SupportValueOverload = false;
static constexpr bool SupportObjectOverload = true;
// disable lambda overload, when serializing virtually inherited base class.
//Only one instance of virtual base will be serialized, when using multiple inheritance
//and it will be undefined behaviour if derived classes would have different virtual base class serialization flow.
// Only one instance of virtual base will be serialized, when using multiple
// inheritance and it will be undefined behaviour if derived classes would
// have different virtual base class serialization flow.
static constexpr bool SupportLambdaOverload = false;
};
}
}
#endif // BITSERY_EXT_INHERITANCE_H

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -36,128 +36,137 @@ namespace bitsery {
namespace pointer_details {
template<typename T>
struct PtrOwnerManager {
struct PtrOwnerManager
{
static_assert(std::is_pointer<T>::value, "");
using TElement = typename std::remove_pointer<T>::type;
static TElement* getPtr(T& obj) {
return obj;
}
static TElement* getPtr(T& obj) { return obj; }
static constexpr PointerOwnershipType getOwnership() {
static constexpr PointerOwnershipType getOwnership()
{
return PointerOwnershipType::Owner;
}
static void create(T& obj, pointer_utils::PolyAllocWithTypeId alloc, size_t typeId) {
static void create(T& obj,
pointer_utils::PolyAllocWithTypeId alloc,
size_t typeId)
{
obj = alloc.newObject<TElement>(typeId);
}
static void createPolymorphic(T& obj, pointer_utils::PolyAllocWithTypeId alloc,
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
static void createPolymorphic(
T& obj,
pointer_utils::PolyAllocWithTypeId alloc,
const std::shared_ptr<PolymorphicHandlerBase>& handler)
{
obj = static_cast<TElement*>(handler->create(alloc));
}
static void destroy(T& obj, pointer_utils::PolyAllocWithTypeId alloc, size_t typeId) {
static void destroy(T& obj,
pointer_utils::PolyAllocWithTypeId alloc,
size_t typeId)
{
alloc.deleteObject(obj, typeId);
obj = nullptr;
}
static void destroyPolymorphic(T& obj, pointer_utils::PolyAllocWithTypeId alloc,
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
static void destroyPolymorphic(
T& obj,
pointer_utils::PolyAllocWithTypeId alloc,
const std::shared_ptr<PolymorphicHandlerBase>& handler)
{
handler->destroy(alloc, obj);
obj = nullptr;
}
};
template<typename T>
struct PtrObserverManager {
struct PtrObserverManager
{
static_assert(std::is_pointer<T>::value, "");
using TElement = typename std::remove_pointer<T>::type;
static TElement* getPtr(T& obj) {
return obj;
}
static TElement* getPtr(T& obj) { return obj; }
static constexpr PointerOwnershipType getOwnership() {
static constexpr PointerOwnershipType getOwnership()
{
return PointerOwnershipType::Observer;
}
//pure observer doesn't have create/createPolymorphic methods, but instead returns reference to pointer
//which gets updated later
static TElement*& getPtrRef(T& obj) {
return obj;
}
// pure observer doesn't have create/createPolymorphic methods, but instead
// returns reference to pointer which gets updated later
static TElement*& getPtrRef(T& obj) { return obj; }
static void destroy(T& obj, MemResourceBase* , size_t ) {
static void destroy(T& obj, MemResourceBase*, size_t) { obj = nullptr; }
static void destroyPolymorphic(T& obj,
MemResourceBase*,
PolymorphicHandlerBase&)
{
obj = nullptr;
}
static void destroyPolymorphic(T& obj, MemResourceBase* , PolymorphicHandlerBase& ) {
obj = nullptr;
}
};
template<typename T>
struct NonPtrManager {
struct NonPtrManager
{
static_assert(!std::is_pointer<T>::value, "");
using TElement = T;
static TElement* getPtr(T& obj) {
return &obj;
}
static TElement* getPtr(T& obj) { return &obj; }
static constexpr PointerOwnershipType getOwnership() {
static constexpr PointerOwnershipType getOwnership()
{
return PointerOwnershipType::Owner;
}
// this code is unreachable for reference type, but is necessary to compile
// LCOV_EXCL_START
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* , PolymorphicHandlerBase& ) {
}
static void destroy(T& , MemResourceBase* , size_t ) {
}
static void destroyPolymorphic(T& , MemResourceBase* , PolymorphicHandlerBase& ) {
static void destroy(T&, MemResourceBase*, size_t) {}
static void destroyPolymorphic(T&, MemResourceBase*, PolymorphicHandlerBase&)
{
}
// LCOV_EXCL_STOP
};
// this class is used by NonPtrManager
struct NoRTTI {
struct NoRTTI
{
template<typename TBase>
static size_t get(TBase&) {
static size_t get(TBase&)
{
return 0;
}
template<typename TBase>
static constexpr size_t get() {
static constexpr size_t get()
{
return 0;
}
template<typename TBase, typename TDerived>
static constexpr TDerived* cast(TBase* obj) {
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() {
static constexpr bool isPolymorphic()
{
return false;
}
};
@@ -165,21 +174,34 @@ namespace bitsery {
}
template<typename RTTI>
using PointerOwnerBase = pointer_utils::PointerObjectExtensionBase<
pointer_details::PtrOwnerManager, PolymorphicContext, RTTI>;
using PointerOwnerBase =
pointer_utils::PointerObjectExtensionBase<pointer_details::PtrOwnerManager,
PolymorphicContext,
RTTI>;
using PointerOwner = PointerOwnerBase<StandardRTTI>;
using PointerObserver = pointer_utils::PointerObjectExtensionBase<
pointer_details::PtrObserverManager, PolymorphicContext, pointer_details::NoRTTI>;
using PointerObserver =
pointer_utils::PointerObjectExtensionBase<pointer_details::PtrObserverManager,
PolymorphicContext,
pointer_details::NoRTTI>;
//inherit from PointerObjectExtensionBase in order to specify PointerType::NotNull
class ReferencedByPointer : public pointer_utils::PointerObjectExtensionBase<
pointer_details::NonPtrManager, PolymorphicContext, pointer_details::NoRTTI> {
// inherit from PointerObjectExtensionBase in order to specify
// PointerType::NotNull
class ReferencedByPointer
: public pointer_utils::PointerObjectExtensionBase<
pointer_details::NonPtrManager,
PolymorphicContext,
pointer_details::NoRTTI>
{
public:
ReferencedByPointer() : pointer_utils::PointerObjectExtensionBase<
pointer_details::NonPtrManager, PolymorphicContext, pointer_details::NoRTTI>(
PointerType::NotNull) {}
ReferencedByPointer()
: pointer_utils::PointerObjectExtensionBase<pointer_details::NonPtrManager,
PolymorphicContext,
pointer_details::NoRTTI>(
PointerType::NotNull)
{
}
};
}
@@ -187,18 +209,22 @@ namespace bitsery {
namespace traits {
template<typename T, typename RTTI>
struct ExtensionTraits<ext::PointerOwnerBase<RTTI>, T*> {
struct ExtensionTraits<ext::PointerOwnerBase<RTTI>, T*>
{
using TValue = T;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
// if underlying type is not polymorphic, then we can enable lambda syntax
static constexpr bool SupportLambdaOverload = !RTTI::template isPolymorphic<TValue>();
static constexpr bool SupportLambdaOverload =
!RTTI::template isPolymorphic<TValue>();
};
template<typename T>
struct ExtensionTraits<ext::PointerObserver, T*> {
//although pointer observer doesn't serialize anything, but we still add value overload support to be consistent with pointer owners
//observer only writes/reads pointer id from pointer linking context
struct ExtensionTraits<ext::PointerObserver, T*>
{
// although pointer observer doesn't serialize anything, but we still add
// value overload support to be consistent with pointer owners observer only
// writes/reads pointer id from pointer linking context
using TValue = T;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
@@ -206,8 +232,10 @@ namespace bitsery {
};
template<typename T>
struct ExtensionTraits<ext::ReferencedByPointer, T> {
//allow everything, because it is serialized as regular type, except it also creates pointerId that is required by NonOwningPointer to work
struct ExtensionTraits<ext::ReferencedByPointer, T>
{
// allow everything, because it is serialized as regular type, except it also
// creates pointerId that is required by NonOwningPointer to work
using TValue = T;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -29,17 +29,19 @@
namespace bitsery {
namespace ext {
class StdAtomic {
class StdAtomic
{
public:
template<typename Ser, typename T, typename Fnc>
void serialize(Ser& ser, const std::atomic<T>& obj, Fnc&& fnc) const {
void serialize(Ser& ser, const std::atomic<T>& obj, Fnc&& fnc) const
{
auto res = obj.load();
fnc(ser, res);
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des& des, std::atomic<T>& obj, Fnc&& fnc) const {
void deserialize(Des& des, std::atomic<T>& obj, Fnc&& fnc) const
{
T res{};
fnc(des, res);
obj.store(res);
@@ -50,7 +52,8 @@ namespace bitsery {
namespace traits {
template<typename T>
struct ExtensionTraits<ext::StdAtomic, std::atomic<T>> {
struct ExtensionTraits<ext::StdAtomic, std::atomic<T>>
{
using TValue = T;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = false;
@@ -61,5 +64,4 @@ namespace bitsery {
}
#endif // BITSERY_EXT_STD_ATOMIC_H

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -29,24 +29,21 @@
namespace bitsery {
namespace ext {
class StdBitset {
class StdBitset
{
public:
template<typename Ser, typename Fnc, size_t N>
void serialize(Ser &ser, const std::bitset<N> &obj, Fnc &&) const {
void serialize(Ser& ser, const std::bitset<N>& obj, Fnc&&) const
{
constexpr size_t BYTES = N / 8;
constexpr size_t LEFTOVER = N % 8;
if (BYTES > sizeof(unsigned long long)) {
for (size_t i = 0u; i < BYTES; ++i) {
size_t offset = i * 8;
auto data = obj[offset + 0] +
(obj[offset + 1] << 1) +
(obj[offset + 2] << 2) +
(obj[offset + 3] << 3) +
(obj[offset + 4] << 4) +
(obj[offset + 5] << 5) +
(obj[offset + 6] << 6) +
(obj[offset + 7] << 7);
auto data = obj[offset + 0] + (obj[offset + 1] << 1) +
(obj[offset + 2] << 2) + (obj[offset + 3] << 3) +
(obj[offset + 4] << 4) + (obj[offset + 5] << 5) +
(obj[offset + 6] << 6) + (obj[offset + 7] << 7);
ser.value1b(static_cast<uint8_t>(data));
}
@@ -59,12 +56,17 @@ namespace bitsery {
}
}
if (LEFTOVER > 0) {
serializeLeftoverImpl(ser.adapter(), obj, N - LEFTOVER, N, std::is_same<Ser, typename Ser::BPEnabledType>{});
serializeLeftoverImpl(ser.adapter(),
obj,
N - LEFTOVER,
N,
std::is_same<Ser, typename Ser::BPEnabledType>{});
}
}
template<typename Des, typename Fnc, size_t N>
void deserialize(Des &des, std::bitset<N> &obj, Fnc &&) const {
void deserialize(Des& des, std::bitset<N>& obj, Fnc&&) const
{
constexpr size_t BYTES = N / 8;
constexpr size_t LEFTOVER = N % 8;
for (size_t i = 0u; i < BYTES; ++i) {
@@ -81,14 +83,22 @@ namespace bitsery {
obj[offset + 7] = data & 0x80u;
}
if (LEFTOVER > 0) {
deserializeLeftoverImpl(des.adapter(), obj, N - LEFTOVER, N, std::is_same<Des, typename Des::BPEnabledType>{});
deserializeLeftoverImpl(des.adapter(),
obj,
N - LEFTOVER,
N,
std::is_same<Des, typename Des::BPEnabledType>{});
}
}
private:
template<typename Writer, size_t N>
void serializeLeftoverImpl(Writer& w, const std::bitset<N> &obj, size_t from, size_t to, std::integral_constant<bool, false>) const {
void serializeLeftoverImpl(Writer& w,
const std::bitset<N>& obj,
size_t from,
size_t to,
std::integral_constant<bool, false>) const
{
auto data = 0;
for (auto i = from; i < to; ++i) {
data += obj[i] << (i - from);
@@ -97,14 +107,24 @@ namespace bitsery {
}
template<typename Writer, size_t N>
void serializeLeftoverImpl(Writer& w, const std::bitset<N> &obj, size_t from, size_t to, std::integral_constant<bool, true>) const {
void serializeLeftoverImpl(Writer& w,
const std::bitset<N>& obj,
size_t from,
size_t to,
std::integral_constant<bool, true>) const
{
for (auto i = from; i < to; ++i) {
w.writeBits(obj[i] ? 1u : 0u, 1);
}
}
template<typename Reader, size_t N>
void deserializeLeftoverImpl(Reader& r, std::bitset<N> &obj, size_t from, size_t to, std::integral_constant<bool, false>) const {
void deserializeLeftoverImpl(Reader& r,
std::bitset<N>& obj,
size_t from,
size_t to,
std::integral_constant<bool, false>) const
{
uint8_t data = 0u;
r.template readBytes<1>(data);
for (auto i = from; i < to; ++i) {
@@ -113,20 +133,25 @@ namespace bitsery {
}
template<typename Reader, size_t N>
void deserializeLeftoverImpl(Reader& r, std::bitset<N> &obj, size_t from, size_t to, std::integral_constant<bool, true>) const {
void deserializeLeftoverImpl(Reader& r,
std::bitset<N>& obj,
size_t from,
size_t to,
std::integral_constant<bool, true>) const
{
for (auto i = from; i < to; ++i) {
uint8_t res = 0u;
r.readBits(res, 1);
obj[i] = res == 1;
}
}
};
}
namespace traits {
template<size_t N>
struct ExtensionTraits<ext::StdBitset, std::bitset<N>> {
struct ExtensionTraits<ext::StdBitset, std::bitset<N>>
{
using TValue = void;
static constexpr bool SupportValueOverload = false;
static constexpr bool SupportObjectOverload = true;
@@ -135,5 +160,4 @@ namespace bitsery {
}
}
#endif // BITSERY_EXT_STD_BITSET_H

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -29,40 +29,61 @@
namespace bitsery {
namespace ext {
class StdDuration {
class StdDuration
{
public:
template<typename Ser, typename T, typename Period, typename Fnc>
void serialize(Ser& ser, const std::chrono::duration<T, Period>& obj, Fnc&& fnc) const {
void serialize(Ser& ser,
const std::chrono::duration<T, Period>& obj,
Fnc&& fnc) const
{
auto res = obj.count();
fnc(ser, res);
}
template<typename Des, typename T, typename Period, typename Fnc>
void deserialize(Des& des, std::chrono::duration<T, Period>& obj, Fnc&& fnc) const {
void deserialize(Des& des,
std::chrono::duration<T, Period>& obj,
Fnc&& fnc) const
{
T res{};
fnc(des, res);
obj = std::chrono::duration<T, Period>{ res };
}
};
class StdTimePoint {
class StdTimePoint
{
public:
template<typename Ser, typename Clock, typename T, typename Period, typename Fnc>
void serialize(Ser& ser, const std::chrono::time_point<Clock, std::chrono::duration<T, Period>>& obj,
Fnc&& fnc) const {
template<typename Ser,
typename Clock,
typename T,
typename Period,
typename Fnc>
void serialize(
Ser& ser,
const std::chrono::time_point<Clock, std::chrono::duration<T, Period>>& obj,
Fnc&& fnc) const
{
auto res = obj.time_since_epoch().count();
fnc(ser, res);
}
template<typename Des, typename Clock, typename T, typename Period, typename Fnc>
void deserialize(Des& des, std::chrono::time_point<Clock, std::chrono::duration<T, Period>>& obj,
Fnc&& fnc) const {
template<typename Des,
typename Clock,
typename T,
typename Period,
typename Fnc>
void deserialize(
Des& des,
std::chrono::time_point<Clock, std::chrono::duration<T, Period>>& obj,
Fnc&& fnc) const
{
T res{};
fnc(des, res);
auto dur = std::chrono::duration<T, Period>{ res };
obj = std::chrono::time_point<Clock, std::chrono::duration<T, Period>>{dur};
obj =
std::chrono::time_point<Clock, std::chrono::duration<T, Period>>{ dur };
}
};
@@ -70,7 +91,8 @@ namespace bitsery {
namespace traits {
template<typename Rep, typename Period>
struct ExtensionTraits<ext::StdDuration, std::chrono::duration<Rep, Period>> {
struct ExtensionTraits<ext::StdDuration, std::chrono::duration<Rep, Period>>
{
using TValue = Rep;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = false;
@@ -78,8 +100,10 @@ namespace bitsery {
};
template<typename Clock, typename Rep, typename Period>
struct ExtensionTraits<ext::StdTimePoint,
std::chrono::time_point<Clock, std::chrono::duration<Rep, Period>>> {
struct ExtensionTraits<
ext::StdTimePoint,
std::chrono::time_point<Clock, std::chrono::duration<Rep, Period>>>
{
using TValue = Rep;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = false;
@@ -90,5 +114,4 @@ namespace bitsery {
}
#endif // BITSERY_EXT_STD_CHRONO_H

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -23,21 +23,25 @@
#ifndef BITSERY_EXT_STD_MAP_H
#define BITSERY_EXT_STD_MAP_H
#include "../traits/core/traits.h"
#include "../details/serialization_common.h"
#include "../traits/core/traits.h"
// we need this, so we could reserve for non ordered map
#include <unordered_map>
namespace bitsery {
namespace ext {
class StdMap {
class StdMap
{
public:
constexpr explicit StdMap(size_t maxSize):_maxSize{maxSize} {}
constexpr explicit StdMap(size_t maxSize)
: _maxSize{ maxSize }
{
}
template<typename Ser, typename T, typename Fnc>
void serialize(Ser &ser, const T &obj, Fnc &&fnc) const {
void serialize(Ser& ser, const T& obj, Fnc&& fnc) const
{
using TKey = typename T::key_type;
using TValue = typename T::mapped_type;
auto size = obj.size();
@@ -49,12 +53,16 @@ namespace bitsery {
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des &des, T &obj, Fnc &&fnc) const {
void deserialize(Des& des, T& obj, Fnc&& fnc) const
{
using TKey = typename T::key_type;
using TValue = typename T::mapped_type;
size_t size{};
details::readSize(des.adapter(), size, _maxSize,
details::readSize(
des.adapter(),
size,
_maxSize,
std::integral_constant<bool, Des::TConfig::CheckDataErrors>{});
obj.clear();
reserve(obj, size);
@@ -67,17 +75,31 @@ namespace bitsery {
hint = obj.emplace_hint(hint, std::move(key), std::move(value));
}
}
private:
template <typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
void reserve(std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& obj, size_t size) const {
template<typename Key,
typename T,
typename Hash,
typename KeyEqual,
typename Allocator>
void reserve(std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& obj,
size_t size) const
{
obj.reserve(size);
}
template <typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
void reserve(std::unordered_multimap<Key, T, Hash, KeyEqual, Allocator>& obj, size_t size) const {
template<typename Key,
typename T,
typename Hash,
typename KeyEqual,
typename Allocator>
void reserve(std::unordered_multimap<Key, T, Hash, KeyEqual, Allocator>& obj,
size_t size) const
{
obj.reserve(size);
}
template<typename T>
void reserve(T& , size_t ) const {
void reserve(T&, size_t) const
{
// for ordered container do nothing
}
size_t _maxSize;
@@ -86,7 +108,8 @@ namespace bitsery {
namespace traits {
template<typename T>
struct ExtensionTraits<ext::StdMap, T> {
struct ExtensionTraits<ext::StdMap, T>
{
using TValue = void;
static constexpr bool SupportValueOverload = false;
static constexpr bool SupportObjectOverload = false;
@@ -96,5 +119,4 @@ namespace bitsery {
}
#endif // BITSERY_EXT_STD_MAP_H

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,28 +20,32 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_EXT_STD_OPTIONAL_H
#define BITSERY_EXT_STD_OPTIONAL_H
#include "../traits/core/traits.h"
#include "../details/serialization_common.h"
#include "../traits/core/traits.h"
#include <optional>
namespace bitsery {
namespace ext {
class StdOptional {
class StdOptional
{
public:
/**
* Works with std::optional types
* @param alignBeforeData only makes sense when bit-packing enabled, by default aligns after writing/reading bool state of optional
* @param alignBeforeData only makes sense when bit-packing enabled, by
* default aligns after writing/reading bool state of optional
*/
explicit StdOptional(bool alignBeforeData=true):_alignBeforeData{alignBeforeData} {}
explicit StdOptional(bool alignBeforeData = true)
: _alignBeforeData{ alignBeforeData }
{
}
template<typename Ser, typename T, typename Fnc>
void serialize(Ser &ser, const std::optional<T> &obj, Fnc &&fnc) const {
void serialize(Ser& ser, const std::optional<T>& obj, Fnc&& fnc) const
{
ser.boolValue(static_cast<bool>(obj));
if (_alignBeforeData)
ser.adapter().align();
@@ -50,7 +54,8 @@ namespace bitsery {
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des &des, std::optional<T> &obj, Fnc &&fnc) const {
void deserialize(Des& des, std::optional<T>& obj, Fnc&& fnc) const
{
bool exists{};
des.boolValue(exists);
if (_alignBeforeData)
@@ -61,16 +66,24 @@ namespace bitsery {
obj = std::nullopt;
}
}
private:
private:
template<typename Des, typename T, typename Fnc>
void deserialize_impl(Des &des, std::optional<T> &obj, Fnc &&fnc, std::true_type) const {
void deserialize_impl(Des& des,
std::optional<T>& obj,
Fnc&& fnc,
std::true_type) const
{
obj = ::bitsery::Access::create<T>();
fnc(des, *obj);
}
template<typename Des, typename T, typename Fnc>
void deserialize_impl(Des &des, std::optional<T> &obj, Fnc &&fnc, std::false_type) const {
void deserialize_impl(Des& des,
std::optional<T>& obj,
Fnc&& fnc,
std::false_type) const
{
if (!obj) {
obj = ::bitsery::Access::create<T>();
}
@@ -82,7 +95,8 @@ namespace bitsery {
namespace traits {
template<typename T>
struct ExtensionTraits<ext::StdOptional, std::optional<T>> {
struct ExtensionTraits<ext::StdOptional, std::optional<T>>
{
using TValue = T;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
@@ -92,5 +106,4 @@ namespace bitsery {
}
#endif // BITSERY_EXT_STD_OPTIONAL_H

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,20 +20,21 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_EXT_STD_QUEUE_H
#define BITSERY_EXT_STD_QUEUE_H
//include type traits for deque and vector, because they are defaults for queue and priority_queue
// include type traits for deque and vector, because they are defaults for queue
// and priority_queue
#include "../traits/deque.h"
#include "../traits/vector.h"
#include <type_traits>
#include <queue>
#include <type_traits>
namespace bitsery {
namespace ext {
class StdQueue {
class StdQueue
{
private:
// inherit from queue so we could take underlying container
template<typename T, typename C>
@@ -67,37 +68,53 @@ namespace bitsery {
};
size_t _maxSize;
public:
explicit StdQueue(size_t maxSize):_maxSize{maxSize} {};
explicit StdQueue(size_t maxSize)
: _maxSize{ maxSize } {};
// for queue
template<typename Ser, typename T, typename C, typename Fnc>
void serialize(Ser &ser, const std::queue<T,C> &obj, Fnc &&fnc) const {
ser.container(QueueCnt<T,C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
void serialize(Ser& ser, const std::queue<T, C>& obj, Fnc&& fnc) const
{
ser.container(
QueueCnt<T, C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
}
template<typename Des, typename T, typename C, typename Fnc>
void deserialize(Des &des, std::queue<T,C> &obj, Fnc &&fnc) const {
des.container(QueueCnt<T,C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
void deserialize(Des& des, std::queue<T, C>& obj, Fnc&& fnc) const
{
des.container(
QueueCnt<T, C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
}
// for priority_queue
template<typename Ser, typename T, typename C, typename Comp, typename Fnc>
void serialize(Ser &ser, const std::priority_queue<T,C, Comp> &obj, Fnc &&fnc) const {
ser.container(PriorityQueueCnt<T,C, Comp>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
void serialize(Ser& ser,
const std::priority_queue<T, C, Comp>& obj,
Fnc&& fnc) const
{
ser.container(PriorityQueueCnt<T, C, Comp>::getContainer(obj),
_maxSize,
std::forward<Fnc>(fnc));
}
template<typename Des, typename T, typename C, typename Comp, typename Fnc>
void deserialize(Des &des, std::priority_queue<T,C, Comp> &obj, Fnc &&fnc) const {
des.container(PriorityQueueCnt<T,C, Comp>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
void deserialize(Des& des,
std::priority_queue<T, C, Comp>& obj,
Fnc&& fnc) const
{
des.container(PriorityQueueCnt<T, C, Comp>::getContainer(obj),
_maxSize,
std::forward<Fnc>(fnc));
}
};
}
namespace traits {
template<typename T>
struct ExtensionTraits<ext::StdQueue, T> {
struct ExtensionTraits<ext::StdQueue, T>
{
using TValue = typename T::value_type;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
@@ -107,5 +124,4 @@ namespace bitsery {
}
#endif // BITSERY_EXT_STD_QUEUE_H

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -29,13 +29,17 @@
namespace bitsery {
namespace ext {
class StdSet {
class StdSet
{
public:
constexpr explicit StdSet(size_t maxSize):_maxSize{maxSize} {}
constexpr explicit StdSet(size_t maxSize)
: _maxSize{ maxSize }
{
}
template<typename Ser, typename T, typename Fnc>
void serialize(Ser &ser, const T &obj, Fnc &&fnc) const {
void serialize(Ser& ser, const T& obj, Fnc&& fnc) const
{
using TKey = typename T::key_type;
auto size = obj.size();
assert(size <= _maxSize);
@@ -46,11 +50,16 @@ namespace bitsery {
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des &des, T &obj, Fnc &&fnc) const {
void deserialize(Des& des, T& obj, Fnc&& fnc) const
{
using TKey = typename T::key_type;
size_t size{};
details::readSize(des.adapter(), size, _maxSize, std::integral_constant<bool, Des::TConfig::CheckDataErrors>{});
details::readSize(
des.adapter(),
size,
_maxSize,
std::integral_constant<bool, Des::TConfig::CheckDataErrors>{});
obj.clear();
reserve(obj, size);
auto hint = obj.begin();
@@ -60,21 +69,24 @@ namespace bitsery {
hint = obj.emplace_hint(hint, std::move(key));
}
}
private:
template<typename Key, typename Hash, typename KeyEqual, typename Allocator>
void reserve(std::unordered_set<Key, Hash, KeyEqual, Allocator>& obj, size_t size) const {
void reserve(std::unordered_set<Key, Hash, KeyEqual, Allocator>& obj,
size_t size) const
{
obj.reserve(size);
}
template<typename Key, typename Hash, typename KeyEqual, typename Allocator>
void reserve(std::unordered_multiset<Key, Hash, KeyEqual, Allocator>& obj, size_t size) const {
void reserve(std::unordered_multiset<Key, Hash, KeyEqual, Allocator>& obj,
size_t size) const
{
obj.reserve(size);
}
template<typename T>
void reserve(T& , size_t ) const {
void reserve(T&, size_t) const
{
// for ordered container do nothing
}
size_t _maxSize;
@@ -83,7 +95,8 @@ namespace bitsery {
namespace traits {
template<typename T>
struct ExtensionTraits<ext::StdSet, T> {
struct ExtensionTraits<ext::StdSet, T>
{
using TValue = typename T::key_type;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
@@ -93,5 +106,4 @@ namespace bitsery {
}
#endif // BITSERY_EXT_STD_SET_H

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -35,32 +35,36 @@ namespace bitsery {
namespace smart_ptr_details {
// further code is for managing shared ownership
//do not nest this type in pointer manager class itself, because it will be different type for different T
struct SharedPtrSharedState : pointer_utils::PointerSharedStateBase {
// do not nest this type in pointer manager class itself, because it will be
// different type for different T
struct SharedPtrSharedState : pointer_utils::PointerSharedStateBase
{
std::shared_ptr<void> obj{};
};
template<typename T>
struct SmartPtrOwnerManager {
struct SmartPtrOwnerManager
{
using TElement = typename T::element_type;
template<typename TDeleter>
static TElement* getPtr(std::unique_ptr<TElement, TDeleter>& obj) {
static TElement* getPtr(std::unique_ptr<TElement, TDeleter>& obj)
{
return obj.get();
}
static TElement* getPtr(std::shared_ptr<TElement>& obj) {
return obj.get();
}
static TElement* getPtr(std::shared_ptr<TElement>& obj) { return obj.get(); }
static TElement* getPtr(std::weak_ptr<TElement>& obj) {
static TElement* getPtr(std::weak_ptr<TElement>& obj)
{
if (auto ptr = obj.lock())
return ptr.get();
return nullptr;
}
static constexpr PointerOwnershipType getOwnership() {
static constexpr PointerOwnershipType getOwnership()
{
return ::bitsery::details::IsSpecializationOf<T, std::unique_ptr>::value
? PointerOwnershipType::Owner
: std::is_same<std::shared_ptr<TElement>, T>::value
@@ -69,45 +73,62 @@ namespace bitsery {
}
template<typename TDeleter>
static void create(std::unique_ptr<TElement, TDeleter>& obj, pointer_utils::PolyAllocWithTypeId alloc,
size_t typeId) {
static void create(std::unique_ptr<TElement, TDeleter>& obj,
pointer_utils::PolyAllocWithTypeId alloc,
size_t typeId)
{
obj.reset(alloc.newObject<TElement>(typeId));
}
template<typename TDeleter>
static void createPolymorphic(std::unique_ptr<TElement, TDeleter>& obj, pointer_utils::PolyAllocWithTypeId alloc,
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
static void createPolymorphic(
std::unique_ptr<TElement, TDeleter>& obj,
pointer_utils::PolyAllocWithTypeId alloc,
const std::shared_ptr<PolymorphicHandlerBase>& handler)
{
obj.reset(static_cast<TElement*>(handler->create(alloc)));
}
template<typename TDel>
static void destroy(std::unique_ptr<TElement, TDel>& obj, pointer_utils::PolyAllocWithTypeId alloc, size_t typeId) {
static void destroy(std::unique_ptr<TElement, TDel>& obj,
pointer_utils::PolyAllocWithTypeId alloc,
size_t typeId)
{
auto ptr = obj.release();
alloc.deleteObject(ptr, typeId);
}
template<typename TDel>
static void destroyPolymorphic(std::unique_ptr<TElement, TDel>& obj, pointer_utils::PolyAllocWithTypeId alloc,
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
static void destroyPolymorphic(
std::unique_ptr<TElement, TDel>& obj,
pointer_utils::PolyAllocWithTypeId alloc,
const std::shared_ptr<PolymorphicHandlerBase>& handler)
{
auto ptr = obj.release();
handler->destroy(alloc, ptr);
}
static void destroy(std::shared_ptr<TElement>& obj, MemResourceBase*, size_t) {
static void destroy(std::shared_ptr<TElement>& obj, MemResourceBase*, size_t)
{
obj.reset();
}
static void destroyPolymorphic(std::shared_ptr<TElement>& obj, MemResourceBase*,
const std::shared_ptr<PolymorphicHandlerBase>&) {
static void destroyPolymorphic(std::shared_ptr<TElement>& obj,
MemResourceBase*,
const std::shared_ptr<PolymorphicHandlerBase>&)
{
obj.reset();
}
static void destroy(std::weak_ptr<TElement>& obj, MemResourceBase*, size_t) {
static void destroy(std::weak_ptr<TElement>& obj, MemResourceBase*, size_t)
{
obj.reset();
}
static void destroyPolymorphic(std::weak_ptr<TElement>& obj, MemResourceBase*,
const std::shared_ptr<PolymorphicHandlerBase>&) {
static void destroyPolymorphic(std::weak_ptr<TElement>& obj,
MemResourceBase*,
const std::shared_ptr<PolymorphicHandlerBase>&)
{
obj.reset();
}
@@ -115,64 +136,82 @@ namespace bitsery {
using TSharedState = SharedPtrSharedState;
static void createShared(TSharedState& state,
std::shared_ptr<TElement>& obj, MemResourceBase* memResource, size_t typeId) {
std::shared_ptr<TElement>& obj,
MemResourceBase* memResource,
size_t typeId)
{
// capture deleter parameters by value
pointer_utils::PolyAllocWithTypeId alloc{ memResource };
obj.reset(alloc.newObject<TElement>(typeId), [alloc, typeId](TElement* data) {
alloc.deleteObject(data, typeId);
}, pointer_utils::StdPolyAlloc<TElement>(memResource));
obj.reset(
alloc.newObject<TElement>(typeId),
[alloc, typeId](TElement* data) { alloc.deleteObject(data, typeId); },
pointer_utils::StdPolyAlloc<TElement>(memResource));
state.obj = obj;
}
static void createSharedPolymorphic(TSharedState& state,
std::shared_ptr<TElement>& obj, MemResourceBase* memResource,
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
static void createSharedPolymorphic(
TSharedState& state,
std::shared_ptr<TElement>& obj,
MemResourceBase* memResource,
const std::shared_ptr<PolymorphicHandlerBase>& handler)
{
// capture deleter parameters by value
pointer_utils::PolyAllocWithTypeId alloc{ memResource };
obj.reset(static_cast<TElement*>(handler->create(alloc)), [alloc, handler](TElement* data) {
handler->destroy(alloc, data);
}, pointer_utils::StdPolyAlloc<TElement>(memResource));
obj.reset(
static_cast<TElement*>(handler->create(alloc)),
[alloc, handler](TElement* data) { handler->destroy(alloc, data); },
pointer_utils::StdPolyAlloc<TElement>(memResource));
state.obj = obj;
}
static void createShared(TSharedState& state,
std::weak_ptr<TElement>& obj, MemResourceBase* memResource, size_t typeId) {
std::weak_ptr<TElement>& obj,
MemResourceBase* memResource,
size_t typeId)
{
pointer_utils::PolyAllocWithTypeId alloc{ memResource };
std::shared_ptr<TElement> res(alloc.newObject<TElement>(typeId),[alloc, typeId](TElement* data) {
alloc.deleteObject(data, typeId);
}, pointer_utils::StdPolyAlloc<TElement>(memResource));
std::shared_ptr<TElement> res(
alloc.newObject<TElement>(typeId),
[alloc, typeId](TElement* data) { alloc.deleteObject(data, typeId); },
pointer_utils::StdPolyAlloc<TElement>(memResource));
obj = res;
state.obj = res;
}
static void createSharedPolymorphic(TSharedState& state,
std::weak_ptr<TElement>& obj, MemResourceBase* memResource,
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
static void createSharedPolymorphic(
TSharedState& state,
std::weak_ptr<TElement>& obj,
MemResourceBase* memResource,
const std::shared_ptr<PolymorphicHandlerBase>& handler)
{
pointer_utils::PolyAllocWithTypeId alloc{ memResource };
std::shared_ptr<TElement> res(static_cast<TElement*>(handler->create(alloc)),
[alloc, handler](TElement* data) {
handler->destroy(alloc, data);
}, pointer_utils::StdPolyAlloc<TElement>(memResource));
std::shared_ptr<TElement> res(
static_cast<TElement*>(handler->create(alloc)),
[alloc, handler](TElement* data) { handler->destroy(alloc, data); },
pointer_utils::StdPolyAlloc<TElement>(memResource));
obj = res;
state.obj = res;
}
static void saveToSharedState(TSharedState& state, T& obj) {
static void saveToSharedState(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 p = reinterpret_cast<TElement*>(state.obj.get());
obj = std::shared_ptr<TElement>(state.obj, p);
}
};
}
template<typename RTTI>
using StdSmartPtrBase = pointer_utils::PointerObjectExtensionBase<
smart_ptr_details::SmartPtrOwnerManager, PolymorphicContext, RTTI>;
smart_ptr_details::SmartPtrOwnerManager,
PolymorphicContext,
RTTI>;
// helper type for convienience
using StdSmartPtr = StdSmartPtrBase<StandardRTTI>;
@@ -182,12 +221,14 @@ namespace bitsery {
namespace traits {
template<typename T, typename RTTI>
struct ExtensionTraits<ext::StdSmartPtrBase<RTTI>, T> {
struct ExtensionTraits<ext::StdSmartPtrBase<RTTI>, T>
{
using TValue = typename T::element_type;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
// if underlying type is not polymorphic, then we can enable lambda syntax
static constexpr bool SupportLambdaOverload = !RTTI::template isPolymorphic<TValue>();
static constexpr bool SupportLambdaOverload =
!RTTI::template isPolymorphic<TValue>();
};
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,7 +20,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_EXT_STD_STACK_H
#define BITSERY_EXT_STD_STACK_H
@@ -30,7 +29,8 @@
namespace bitsery {
namespace ext {
class StdStack {
class StdStack
{
private:
// inherit from stack so we could take underlying container
template<typename T, typename C>
@@ -48,25 +48,31 @@ namespace bitsery {
}
};
size_t _maxSize;
public:
explicit StdStack(size_t maxSize):_maxSize{maxSize} {};
explicit StdStack(size_t maxSize)
: _maxSize{ maxSize } {};
template<typename Ser, typename T, typename C, typename Fnc>
void serialize(Ser &ser, const std::stack<T,C> &obj, Fnc &&fnc) const {
ser.container(StackCnt<T,C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
void serialize(Ser& ser, const std::stack<T, C>& obj, Fnc&& fnc) const
{
ser.container(
StackCnt<T, C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
}
template<typename Des, typename T, typename C, typename Fnc>
void deserialize(Des &des, std::stack<T,C> &obj, Fnc &&fnc) const {
des.container(StackCnt<T,C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
void deserialize(Des& des, std::stack<T, C>& obj, Fnc&& fnc) const
{
des.container(
StackCnt<T, C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
}
};
}
namespace traits {
template<typename T, typename Seq>
struct ExtensionTraits<ext::StdStack, std::stack<T, Seq>> {
struct ExtensionTraits<ext::StdStack, std::stack<T, Seq>>
{
using TValue = T;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
@@ -76,5 +82,4 @@ namespace bitsery {
}
#endif // BITSERY_EXT_STD_STACK_H

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,35 +20,37 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_EXT_STD_TUPLE_H
#define BITSERY_EXT_STD_TUPLE_H
#include "utils/composite_type_overloads.h"
#include "../traits/core/traits.h"
#include "utils/composite_type_overloads.h"
#include <tuple>
namespace bitsery {
namespace ext {
template<typename... Overloads>
class StdTuple : public details::CompositeTypeOverloadsUtils<std::tuple, Overloads...> {
class StdTuple
: public details::CompositeTypeOverloadsUtils<std::tuple, Overloads...>
{
public:
template<typename Ser, typename Fnc, typename... Ts>
void serialize(Ser& ser, const std::tuple<Ts...>& obj, Fnc&&) const {
void serialize(Ser& ser, const std::tuple<Ts...>& obj, Fnc&&) const
{
serializeAll(ser, const_cast<std::tuple<Ts...>&>(obj));
}
template<typename Des, typename Fnc, typename... Ts>
void deserialize(Des& des, std::tuple<Ts...>& obj, Fnc&&) const {
void deserialize(Des& des, std::tuple<Ts...>& obj, Fnc&&) const
{
serializeAll(des, obj);
}
private:
template<typename S, typename... Ts>
void serializeAll(S& s, std::tuple<Ts...>& obj) const {
void serializeAll(S& s, std::tuple<Ts...>& obj) const
{
this->execAll(obj, [this, &s](auto& data, auto index) {
constexpr size_t Index = decltype(index)::value;
this->serializeType(s, std::get<Index>(data));
@@ -64,7 +66,8 @@ namespace bitsery {
namespace traits {
template<typename Tuple, typename... Overloads>
struct ExtensionTraits<ext::StdTuple<Overloads...>, Tuple> {
struct ExtensionTraits<ext::StdTuple<Overloads...>, Tuple>
{
static_assert(bitsery::details::IsSpecializationOf<Tuple, std::tuple>::value,
"StdTuple only works with std::tuple");
using TValue = void;
@@ -77,5 +80,4 @@ namespace bitsery {
}
#endif // BITSERY_EXT_STD_TUPLE_H

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,40 +20,48 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_EXT_STD_VARIANT_H
#define BITSERY_EXT_STD_VARIANT_H
#include "utils/composite_type_overloads.h"
#include "../traits/core/traits.h"
#include "utils/composite_type_overloads.h"
#include <variant>
namespace bitsery {
namespace ext {
template<typename... Overloads>
class StdVariant : public details::CompositeTypeOverloadsUtils<std::variant, Overloads...> {
class StdVariant
: public details::CompositeTypeOverloadsUtils<std::variant, Overloads...>
{
public:
template<typename Ser, typename Fnc, typename... Ts>
void serialize(Ser& ser, const std::variant<Ts...>& obj, Fnc&&) const {
void serialize(Ser& ser, const std::variant<Ts...>& obj, Fnc&&) const
{
auto index = obj.index();
assert(index != std::variant_npos);
details::writeSize(ser.adapter(), index);
this->execIndex(index, const_cast<std::variant<Ts...>&>(obj), [this, &ser](auto& data, auto index) {
this->execIndex(index,
const_cast<std::variant<Ts...>&>(obj),
[this, &ser](auto& data, auto index) {
constexpr size_t Index = decltype(index)::value;
this->serializeType(ser, std::get<Index>(data));
});
}
template<typename Des, typename Fnc, typename... Ts>
void deserialize(Des& des, std::variant<Ts...>& obj, Fnc&&) const {
void deserialize(Des& des, std::variant<Ts...>& obj, Fnc&&) const
{
size_t index{};
details::readSize(des.adapter(), index, sizeof...(Ts), std::integral_constant<bool, Des::TConfig::CheckDataErrors>{});
details::readSize(
des.adapter(),
index,
sizeof...(Ts),
std::integral_constant<bool, Des::TConfig::CheckDataErrors>{});
this->execIndex(index, obj, [this, &des](auto& data, auto index) {
constexpr size_t Index = decltype(index)::value;
using TElem = typename std::variant_alternative<Index, std::variant<Ts...>>::type;
using TElem =
typename std::variant_alternative<Index, std::variant<Ts...>>::type;
// Reinitializing nontrivial types may be expensive especially when they
// reference heap data, so if `data` is already holding the requested
@@ -67,10 +75,10 @@ namespace bitsery {
TElem item = ::bitsery::Access::create<TElem>();
this->serializeType(des, item);
data = std::variant<Ts...>(std::in_place_index_t<Index>{}, std::move(item));
data =
std::variant<Ts...>(std::in_place_index_t<Index>{}, std::move(item));
});
}
};
// deduction guide
@@ -80,13 +88,18 @@ namespace bitsery {
// defines empty fuction, that handles monostate
template<typename S>
void serialize(S& , std::monostate&) {}
void
serialize(S&, std::monostate&)
{
}
namespace traits {
template<typename Variant, typename... Overloads>
struct ExtensionTraits<ext::StdVariant<Overloads...>, Variant> {
static_assert(bitsery::details::IsSpecializationOf<Variant, std::variant>::value,
struct ExtensionTraits<ext::StdVariant<Overloads...>, Variant>
{
static_assert(
bitsery::details::IsSpecializationOf<Variant, std::variant>::value,
"StdVariant only works with std::variant");
using TValue = void;
static constexpr bool SupportValueOverload = false;
@@ -98,5 +111,4 @@ namespace bitsery {
}
#endif // BITSERY_EXT_STD_VARIANT_H

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -29,18 +29,20 @@
#if __cplusplus < 201703L
#error these utils requires c++17
// in theory, it could be implemented using C++11
// but without class template argument deduction guides that would be very inconvenient to use
// these are very helpul for sum types (e.g. std::variant),
// but for product types (e.g. std::tuple) you can you can easily do it your self with lambda, without extension
// but without class template argument deduction guides that would be very
// inconvenient to use these are very helpul for sum types (e.g. std::variant),
// but for product types (e.g. std::tuple) you can you can easily do it your
// self with lambda, without extension
#endif
namespace bitsery {
namespace ext {
// might be usable, when you want to have one overload set for different composite types,
// e.g. variant, tuple and pair
// might be usable, when you want to have one overload set for different
// composite types, e.g. variant, tuple and pair
template<class... Ts>
struct CompositeTypeOverloads : Ts ... {
struct CompositeTypeOverloads : Ts...
{
using Ts::operator()...;
};
@@ -49,32 +51,39 @@ namespace bitsery {
// convenient way to invoke s.value<N>, shorter than specifying a lambda
template<typename T, size_t N>
struct OverloadValue {
struct OverloadValue
{
template<typename S>
void operator()(S& s, T& v) const {
void operator()(S& s, T& v) const
{
s.template value<N>(v);
}
};
// convenient way to invoke other extension using value or object overloads
// there is no reason to write OverloadExtLambda,
// because you'll need to specify lambda type, which is very inconvenient and it will be much
// easier to simple write a lambda with extension inside it,
// in order to implement it in a convenient way, i need a way to deduce only last template parameter (lambda type)
// but this is not possible with deduction guides at the moment
// because you'll need to specify lambda type, which is very inconvenient and it
// will be much easier to simple write a lambda with extension inside it, in
// order to implement it in a convenient way, i need a way to deduce only last
// template parameter (lambda type) but this is not possible with deduction
// guides at the moment
template<typename T, size_t N, typename Ext>
struct OverloadExtValue : public Ext {
struct OverloadExtValue : public Ext
{
template<typename S>
void operator()(S& s, T& v) const {
void operator()(S& s, T& v) const
{
s.template ext<N>(v, static_cast<const Ext&>(*this));
}
};
template<typename T, typename Ext>
struct OverloadExtObject : public Ext {
struct OverloadExtObject : public Ext
{
template<typename S>
void operator()(S& s, T& v) const {
void operator()(S& s, T& v) const
{
s.ext(v, static_cast<const Ext&>(*this));
}
};
@@ -83,49 +92,63 @@ namespace bitsery {
namespace details {
template<template<typename...> typename CompositeType, typename... Overloads>
class CompositeTypeOverloadsUtils : public ext::CompositeTypeOverloads<Overloads...> {
class CompositeTypeOverloadsUtils
: public ext::CompositeTypeOverloads<Overloads...>
{
protected:
// converts run-time index to compile-time index,
// by calling lambda with std::integral_constant<size_t, INDEX>
template<typename Fnc, typename... Ts>
void execIndex(size_t index, CompositeType<Ts...>& obj, Fnc&& fnc) const {
execIndexImpl(index, obj, std::forward<Fnc>(fnc), std::index_sequence_for<Ts...>{});
void execIndex(size_t index, CompositeType<Ts...>& obj, Fnc&& fnc) const
{
execIndexImpl(
index, obj, std::forward<Fnc>(fnc), std::index_sequence_for<Ts...>{});
}
// call lambda for all indexes in composite type
template<typename Fnc, typename... Ts>
void execAll(CompositeType<Ts...>& obj, Fnc&& fnc) const {
void execAll(CompositeType<Ts...>& obj, Fnc&& fnc) const
{
execAllImpl(obj, std::forward<Fnc>(fnc), std::index_sequence_for<Ts...>{});
}
// serialize a type, by using overload first
template<typename S, typename T>
void serializeType(S& s, T& v) const {
void serializeType(S& s, T& v) const
{
// first check if overload exists, otherwise try to call serialize method
if constexpr (hasOverload<S, T>()) {
std::invoke(*this, s, v);
} else {
static_assert(details::SerializeFunction<S, T>::isDefined(),
static_assert(
details::SerializeFunction<S, T>::isDefined(),
"Please define overload or 'serialize' function for your type.");
s.object(v);
}
}
private:
template<typename S, typename T>
static constexpr bool hasOverload() {
static constexpr bool hasOverload()
{
return std::is_invocable<ext::CompositeTypeOverloads<Overloads...>,
std::add_lvalue_reference_t<S>, std::add_lvalue_reference_t<T>>::value;
std::add_lvalue_reference_t<S>,
std::add_lvalue_reference_t<T>>::value;
}
template<typename Variant, typename Fnc, size_t... Is>
void execIndexImpl(size_t index, Variant& obj, Fnc&& fnc, std::index_sequence<Is...>) const {
((index == Is ? fnc(obj, std::integral_constant<size_t, Is>{}), 0 : 0), ...);
void execIndexImpl(size_t index,
Variant& obj,
Fnc&& fnc,
std::index_sequence<Is...>) const
{
((index == Is ? fnc(obj, std::integral_constant<size_t, Is>{}), 0 : 0),
...);
}
template<typename Variant, typename Fnc, size_t... Is>
void execAllImpl(Variant& obj, Fnc&& fnc, std::index_sequence<Is...>) const {
void execAllImpl(Variant& obj, Fnc&& fnc, std::index_sequence<Is...>) const
{
(fnc(obj, std::integral_constant<size_t, Is>{}), ...);
}
};

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -28,31 +28,42 @@
namespace bitsery {
namespace ext {
// these are very similar to c++17 polymorphic allocator and memory resource classes
// but i don't want to enforce users to use c++17 if they want to use pointers
// plus this has additional information from RTTI about runtime type information,
// might be useful working with polymorphic types.
// The same memory resource is used to allocate internal data in various contexts,
// (typeId is always 0 for internal data allocation in contexts).
// these are very similar to c++17 polymorphic allocator and memory resource
// classes but i don't want to enforce users to use c++17 if they want to use
// pointers plus this has additional information from RTTI about runtime type
// information, might be useful working with polymorphic types. The same memory
// resource is used to allocate internal data in various contexts, (typeId is
// always 0 for internal data allocation in contexts).
class MemResourceBase {
class MemResourceBase
{
public:
virtual void* allocate(size_t bytes, size_t alignment, size_t typeId) = 0;
virtual void deallocate(void* ptr, size_t bytes, size_t alignment, size_t typeId) noexcept = 0;
virtual void deallocate(void* ptr,
size_t bytes,
size_t alignment,
size_t typeId) noexcept = 0;
virtual ~MemResourceBase() noexcept = default;
};
// default implementation for MemResourceBase using new and delete
class MemResourceNewDelete final: public MemResourceBase {
class MemResourceNewDelete final : public MemResourceBase
{
public:
inline void* allocate(size_t bytes, size_t /*alignment*/, size_t /*typeId*/) final {
inline void* allocate(size_t bytes,
size_t /*alignment*/,
size_t /*typeId*/) final
{
return (::operator new(bytes));
}
inline void
deallocate(void* ptr, size_t /*bytes*/, size_t /*alignment*/, size_t /*typeId*/) noexcept final {
inline void deallocate(void* ptr,
size_t /*bytes*/,
size_t /*alignment*/,
size_t /*typeId*/) noexcept final
{
(::operator delete(ptr));
}
@@ -61,26 +72,32 @@ namespace bitsery {
// these classes are used internally by bitsery extensions and and pointer utils
namespace pointer_utils {
// this is helper class that stores memory resource and knows how to construct/destroy objects
// capture this by value for custom deleters, because during deserialization mem resource can be changed
class PolyAllocWithTypeId final {
// this is helper class that stores memory resource and knows how to
// construct/destroy objects capture this by value for custom deleters, because
// during deserialization mem resource can be changed
class PolyAllocWithTypeId final
{
public:
constexpr PolyAllocWithTypeId(MemResourceBase* memResource = nullptr)
:_resource{memResource} {}
: _resource{ memResource }
{
}
template<typename T>
T* allocate(size_t n, size_t typeId) const {
T* allocate(size_t n, size_t typeId) const
{
const auto bytes = sizeof(T) * n;
constexpr auto alignment = std::alignment_of<T>::value;
void* ptr = _resource
void* ptr =
_resource
? _resource->allocate(bytes, alignment, typeId)
: ext::MemResourceNewDelete{}.allocate(bytes, alignment, typeId);
return static_cast<T*>(ptr);
}
template<typename T>
void deallocate(T* ptr, size_t n, size_t typeId) const noexcept {
void deallocate(T* ptr, size_t n, size_t typeId) const noexcept
{
const auto bytes = sizeof(T) * n;
constexpr auto alignment = std::alignment_of<T>::value;
_resource
@@ -89,30 +106,30 @@ namespace bitsery {
}
template<typename T>
T* newObject(size_t typeId) const {
T* newObject(size_t typeId) const
{
auto ptr = allocate<T>(1, typeId);
return ::bitsery::Access::create<T>(ptr);
}
template<typename T>
void deleteObject(T* obj, size_t typeId) const {
void deleteObject(T* obj, size_t typeId) const
{
obj->~T();
deallocate(obj, 1, typeId);
}
void setMemResource(ext::MemResourceBase* resource) {
_resource = resource;
}
void setMemResource(ext::MemResourceBase* resource) { _resource = resource; }
ext::MemResourceBase* getMemResource() const {
return _resource;
}
ext::MemResourceBase* getMemResource() const { return _resource; }
bool operator==(const PolyAllocWithTypeId& rhs) const noexcept {
bool operator==(const PolyAllocWithTypeId& rhs) const noexcept
{
return _resource == rhs._resource;
}
bool operator!=(const PolyAllocWithTypeId& rhs) const noexcept {
bool operator!=(const PolyAllocWithTypeId& rhs) const noexcept
{
return !(*this == rhs);
}
@@ -124,39 +141,47 @@ namespace bitsery {
// it just wraps our PolyAllocWithTypeId and pass 0 as typeId
// and defines core functions for c++ Allocator concept,
template<class T>
class StdPolyAlloc {
class StdPolyAlloc
{
public:
using value_type = T;
explicit constexpr StdPolyAlloc(MemResourceBase* memResource)
:_alloc{memResource} {}
explicit constexpr StdPolyAlloc(PolyAllocWithTypeId alloc) : _alloc{alloc} {}
: _alloc{ memResource }
{
}
explicit constexpr StdPolyAlloc(PolyAllocWithTypeId alloc)
: _alloc{ alloc }
{
}
template<typename U>
friend class StdPolyAlloc;
template<class U>
constexpr explicit StdPolyAlloc(const StdPolyAlloc<U>& other) noexcept
:_alloc{other._alloc} {
: _alloc{ other._alloc }
{
}
T* allocate(std::size_t n) {
return _alloc.allocate<T>(n, 0);
}
T* allocate(std::size_t n) { return _alloc.allocate<T>(n, 0); }
void deallocate(T* p, std::size_t n) noexcept {
void deallocate(T* p, std::size_t n) noexcept
{
return _alloc.deallocate(p, n, 0);
}
template<class U>
friend bool operator==(const StdPolyAlloc<T>& lhs,
const StdPolyAlloc<U>& rhs) noexcept {
const StdPolyAlloc<U>& rhs) noexcept
{
return lhs._alloc == rhs._alloc;
}
template<class U>
friend bool operator!=(const StdPolyAlloc<T>& lhs,
const StdPolyAlloc<U>& rhs) noexcept {
const StdPolyAlloc<U>& rhs) noexcept
{
return !(lhs == rhs);
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -29,19 +29,22 @@ namespace bitsery {
namespace ext {
// change name
enum class PointerType : uint8_t {
enum class PointerType : uint8_t
{
Nullable,
NotNull
};
// Observer - not responsible for pointer lifetime management.
// Owner - only ONE owner is responsible for this pointers creation/destruction
// SharedOwner, SharedObserver - MANY shared owners is responsible for pointer creation/destruction
// requires additional context to manage shared owners themselves.
// SharedOwner actually manages life time e.g. std::shared_ptr
// SharedObserver do not manage life time of the pointer, but can observe shared state .e.. std::weak_ptr
// and differently from Observer, creates new object if necessary and saves to shared state
enum class PointerOwnershipType : uint8_t {
// SharedOwner, SharedObserver - MANY shared owners is responsible for pointer
// creation/destruction requires additional context to manage shared owners
// themselves. SharedOwner actually manages life time e.g. std::shared_ptr
// SharedObserver do not manage life time of the pointer, but can observe shared
// state .e.. std::weak_ptr and differently from Observer, creates new object if
// necessary and saves to shared state
enum class PointerOwnershipType : uint8_t
{
Observer,
Owner,
SharedOwner,
@@ -51,15 +54,20 @@ namespace bitsery {
namespace pointer_utils {
// this class is used to store context for shared ptr owners
struct PointerSharedStateBase {
struct PointerSharedStateBase
{
virtual ~PointerSharedStateBase() = default;
};
struct PointerSharedStateDeleter {
struct PointerSharedStateDeleter
{
PointerSharedStateDeleter() = default;
explicit PointerSharedStateDeleter(MemResourceBase* memResource)
:_memResource{memResource} {}
void operator()(PointerSharedStateBase* data) const {
: _memResource{ memResource }
{
}
void operator()(PointerSharedStateBase* data) const
{
data->~PointerSharedStateBase();
StdPolyAlloc<PointerSharedStateBase> alloc{ _memResource };
alloc.deallocate(data, 1);
@@ -68,14 +76,16 @@ namespace bitsery {
};
// PLC info is internal classes for serializer, and deserializer
struct PLCInfo {
struct PLCInfo
{
explicit PLCInfo(PointerOwnershipType ownershipType_)
: ownershipType{ownershipType_},
isSharedProcessed{false} {};
: ownershipType{ ownershipType_ }
, isSharedProcessed{ false } {};
PointerOwnershipType ownershipType;
bool isSharedProcessed;
void update(PointerOwnershipType ptrType) {
void update(PointerOwnershipType ptrType)
{
// do nothing for observer
if (ptrType == PointerOwnershipType::Observer)
return;
@@ -90,24 +100,33 @@ namespace bitsery {
// check if need to update to SharedOwner
if (ptrType == PointerOwnershipType::SharedOwner)
ownershipType = ptrType;
//mark that object already processed, so we do not serialize/deserialize duplicate objects
// mark that object already processed, so we do not serialize/deserialize
// duplicate objects
isSharedProcessed = true;
}
};
struct PLCInfoSerializer : PLCInfo {
struct PLCInfoSerializer : PLCInfo
{
PLCInfoSerializer(size_t id_, PointerOwnershipType ownershipType_)
: PLCInfo(ownershipType_), id{id_} {}
: PLCInfo(ownershipType_)
, id{ id_ }
{
}
size_t id;
};
struct PLCInfoDeserializer : PLCInfo {
PLCInfoDeserializer(void* ptr, PointerOwnershipType ownershipType_, MemResourceBase* memResource_)
: PLCInfo(ownershipType_),
ownerPtr{ptr},
memResource{memResource_},
observersList{StdPolyAlloc<std::reference_wrapper<void*>>{memResource_}} {};
struct PLCInfoDeserializer : PLCInfo
{
PLCInfoDeserializer(void* ptr,
PointerOwnershipType ownershipType_,
MemResourceBase* memResource_)
: PLCInfo(ownershipType_)
, ownerPtr{ ptr }
, memResource{ memResource_ }
, observersList{ StdPolyAlloc<std::reference_wrapper<void*>>{
memResource_ } } {};
// need to override these explicitly because we have pointer member
PLCInfoDeserializer(const PLCInfoDeserializer&) = delete;
@@ -118,7 +137,8 @@ namespace bitsery {
PLCInfoDeserializer& operator=(PLCInfoDeserializer&&) = default;
void processOwner(void* ptr) {
void processOwner(void* ptr)
{
ownerPtr = ptr;
assert(ownershipType != PointerOwnershipType::Observer);
for (auto& o : observersList)
@@ -127,7 +147,8 @@ namespace bitsery {
observersList.shrink_to_fit();
}
void processObserver(void* (& ptr)) {
void processObserver(void*(&ptr))
{
if (ownerPtr) {
ptr = ownerPtr;
} else {
@@ -138,27 +159,40 @@ namespace bitsery {
void* ownerPtr;
MemResourceBase* memResource;
std::vector<std::reference_wrapper<void*>,
StdPolyAlloc<std::reference_wrapper<void*>>> observersList;
std::unique_ptr<PointerSharedStateBase, PointerSharedStateDeleter> sharedState{};
StdPolyAlloc<std::reference_wrapper<void*>>>
observersList;
std::unique_ptr<PointerSharedStateBase, PointerSharedStateDeleter>
sharedState{};
};
class PointerLinkingContextSerialization {
class PointerLinkingContextSerialization
{
public:
explicit PointerLinkingContextSerialization(MemResourceBase* memResource = nullptr)
: _currId{0},
_ptrMap{StdPolyAlloc<std::pair<const void* const, PLCInfoSerializer>>{memResource}} {}
explicit PointerLinkingContextSerialization(
MemResourceBase* memResource = nullptr)
: _currId{ 0 }
, _ptrMap{ StdPolyAlloc<std::pair<const void* const, PLCInfoSerializer>>{
memResource } }
{
}
PointerLinkingContextSerialization(const PointerLinkingContextSerialization&) = delete;
PointerLinkingContextSerialization(
const PointerLinkingContextSerialization&) = delete;
PointerLinkingContextSerialization& operator=(const PointerLinkingContextSerialization&) = delete;
PointerLinkingContextSerialization& operator=(
const PointerLinkingContextSerialization&) = delete;
PointerLinkingContextSerialization(PointerLinkingContextSerialization&&) = default;
PointerLinkingContextSerialization(PointerLinkingContextSerialization&&) =
default;
PointerLinkingContextSerialization& operator=(PointerLinkingContextSerialization&&) = default;
PointerLinkingContextSerialization& operator=(
PointerLinkingContextSerialization&&) = default;
~PointerLinkingContextSerialization() = default;
const PLCInfoSerializer& getInfoByPtr(const void* ptr, PointerOwnershipType ptrType) {
const PLCInfoSerializer& getInfoByPtr(const void* ptr,
PointerOwnershipType ptrType)
{
auto res = _ptrMap.emplace(ptr, PLCInfoSerializer{ _currId + 1u, ptrType });
auto& ptrInfo = res.first->second;
if (res.second) {
@@ -171,8 +205,11 @@ namespace bitsery {
// valid, when all pointers have owners.
// we cannot serialize pointers, if we haven't serialized objects themselves
bool isPointerSerializationValid() const {
return std::all_of(_ptrMap.begin(), _ptrMap.end(),
bool isPointerSerializationValid() const
{
return std::all_of(
_ptrMap.begin(),
_ptrMap.end(),
[](const std::pair<const void*, PLCInfoSerializer>& p) {
return p.second.ownershipType == PointerOwnershipType::SharedOwner ||
p.second.ownershipType == PointerOwnershipType::Owner;
@@ -181,76 +218,98 @@ namespace bitsery {
private:
size_t _currId;
std::unordered_map<const void*, PLCInfoSerializer,
std::hash<const void*>, std::equal_to<const void*>,
StdPolyAlloc<std::pair<const void* const, PLCInfoSerializer>>
> _ptrMap;
std::unordered_map<
const void*,
PLCInfoSerializer,
std::hash<const void*>,
std::equal_to<const void*>,
StdPolyAlloc<std::pair<const void* const, PLCInfoSerializer>>>
_ptrMap;
};
class PointerLinkingContextDeserialization {
class PointerLinkingContextDeserialization
{
public:
explicit PointerLinkingContextDeserialization(MemResourceBase* memResource = nullptr)
: _memResource{memResource},
_idMap{StdPolyAlloc<std::pair<const size_t, PLCInfoDeserializer>>{memResource}} {}
explicit PointerLinkingContextDeserialization(
MemResourceBase* memResource = nullptr)
: _memResource{ memResource }
, _idMap{ StdPolyAlloc<std::pair<const size_t, PLCInfoDeserializer>>{
memResource } }
{
}
PointerLinkingContextDeserialization(const PointerLinkingContextDeserialization&) = delete;
PointerLinkingContextDeserialization(
const PointerLinkingContextDeserialization&) = delete;
PointerLinkingContextDeserialization& operator=(const PointerLinkingContextDeserialization&) = delete;
PointerLinkingContextDeserialization& operator=(
const PointerLinkingContextDeserialization&) = delete;
PointerLinkingContextDeserialization(PointerLinkingContextDeserialization&&) = default;
PointerLinkingContextDeserialization(PointerLinkingContextDeserialization&&) =
default;
PointerLinkingContextDeserialization& operator=(PointerLinkingContextDeserialization&&) = default;
PointerLinkingContextDeserialization& operator=(
PointerLinkingContextDeserialization&&) = default;
~PointerLinkingContextDeserialization() = default;
PLCInfoDeserializer& getInfoById(size_t id, PointerOwnershipType ptrType) {
auto res = _idMap.emplace(id, PLCInfoDeserializer{nullptr, ptrType, _memResource});
PLCInfoDeserializer& getInfoById(size_t id, PointerOwnershipType ptrType)
{
auto res =
_idMap.emplace(id, PLCInfoDeserializer{ nullptr, ptrType, _memResource });
auto& ptrInfo = res.first->second;
if (!res.second)
ptrInfo.update(ptrType);
return ptrInfo;
}
void clearSharedState() {
void clearSharedState()
{
for (auto& item : _idMap)
item.second.sharedState.reset();
}
// valid, when all pointers has owners
bool isPointerDeserializationValid() const {
return std::all_of(_idMap.begin(), _idMap.end(),
bool isPointerDeserializationValid() const
{
return std::all_of(
_idMap.begin(),
_idMap.end(),
[](const std::pair<const size_t, PLCInfoDeserializer>& p) {
return p.second.ownershipType == PointerOwnershipType::SharedOwner ||
p.second.ownershipType == PointerOwnershipType::Owner;
});
}
MemResourceBase* getMemResource() noexcept {
return _memResource;
}
MemResourceBase* getMemResource() noexcept { return _memResource; }
void setMemResource(MemResourceBase* resource) noexcept {
void setMemResource(MemResourceBase* resource) noexcept
{
_memResource = resource;
}
private:
MemResourceBase* _memResource;
std::unordered_map<size_t, PLCInfoDeserializer,
std::hash<size_t>, std::equal_to<size_t>,
StdPolyAlloc<std::pair<const size_t, PLCInfoDeserializer>>> _idMap;
std::unordered_map<size_t,
PLCInfoDeserializer,
std::hash<size_t>,
std::equal_to<size_t>,
StdPolyAlloc<std::pair<const size_t, PLCInfoDeserializer>>>
_idMap;
};
}
// this class is for convenience
class PointerLinkingContext :
public pointer_utils::PointerLinkingContextSerialization,
public pointer_utils::PointerLinkingContextDeserialization {
class PointerLinkingContext
: public pointer_utils::PointerLinkingContextSerialization
, public pointer_utils::PointerLinkingContextDeserialization
{
public:
explicit PointerLinkingContext(MemResourceBase* memResource = nullptr)
:pointer_utils::PointerLinkingContextSerialization(memResource),
pointer_utils::PointerLinkingContextDeserialization(memResource) {};
: pointer_utils::PointerLinkingContextSerialization(memResource)
, pointer_utils::PointerLinkingContextDeserialization(memResource){};
bool isValid() const {
bool isValid() const
{
return isPointerSerializationValid() && isPointerDeserializationValid();
}
};
@@ -258,35 +317,44 @@ namespace bitsery {
namespace pointer_utils {
template<template<typename> class TPtrManager,
template<typename> class TPolymorphicContext, typename RTTI>
class PointerObjectExtensionBase {
template<typename>
class TPolymorphicContext,
typename RTTI>
class PointerObjectExtensionBase
{
public:
// helper types
template<typename T>
struct IsPolymorphic : std::integral_constant<bool,
RTTI::template isPolymorphic<typename TPtrManager<T>::TElement>()> {
struct IsPolymorphic
: std::integral_constant<
bool,
RTTI::template isPolymorphic<typename TPtrManager<T>::TElement>()>
{
};
template<PointerOwnershipType Value>
using OwnershipType = std::integral_constant<PointerOwnershipType, Value>;
explicit PointerObjectExtensionBase(PointerType ptrType = PointerType::Nullable,
explicit PointerObjectExtensionBase(
PointerType ptrType = PointerType::Nullable,
MemResourceBase* resource = nullptr,
bool resourcePropagate = false) :
_ptrType{ptrType},
_resourcePropagate{resourcePropagate},
_resource{resource} {
bool resourcePropagate = false)
: _ptrType{ ptrType }
, _resourcePropagate{ resourcePropagate }
, _resource{ resource }
{
}
template<typename Ser, typename T, typename Fnc>
void serialize(Ser& ser, const T& obj, Fnc&& fnc) const {
void serialize(Ser& ser, const T& obj, Fnc&& fnc) const
{
auto ptr = TPtrManager<T>::getPtr(const_cast<T&>(obj));
if (ptr) {
auto& ctx = ser.template context<pointer_utils::PointerLinkingContextSerialization>();
auto& ptrInfo = ctx.getInfoByPtr(getBasePtr(ptr), TPtrManager<T>::getOwnership());
auto& ctx = ser.template context<
pointer_utils::PointerLinkingContextSerialization>();
auto& ptrInfo =
ctx.getInfoByPtr(getBasePtr(ptr), TPtrManager<T>::getOwnership());
details::writeSize(ser.adapter(), ptrInfo.id);
if (TPtrManager<T>::getOwnership() != PointerOwnershipType::Observer) {
if (!ptrInfo.isSharedProcessed)
@@ -296,14 +364,15 @@ namespace bitsery {
assert(_ptrType == PointerType::Nullable);
details::writeSize(ser.adapter(), 0);
}
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des& des, T& obj, Fnc&& fnc) const {
void deserialize(Des& des, T& obj, Fnc&& fnc) const
{
size_t id{};
details::readSize(des.adapter(), id, 0, std::false_type{});
auto& ctx = des.template context<pointer_utils::PointerLinkingContextDeserialization>();
auto& ctx = des.template context<
pointer_utils::PointerLinkingContextDeserialization>();
auto prevResource = ctx.getMemResource();
auto memResource = _resource ? _resource : prevResource;
// if we have resource and propagate is true, then change current resource
@@ -313,7 +382,12 @@ namespace bitsery {
}
if (id) {
auto& ptrInfo = ctx.getInfoById(id, TPtrManager<T>::getOwnership());
deserializeImpl(memResource, ptrInfo, des, obj, std::forward<Fnc>(fnc), IsPolymorphic<T>{},
deserializeImpl(memResource,
ptrInfo,
des,
obj,
std::forward<Fnc>(fnc),
IsPolymorphic<T>{},
OwnershipType<TPtrManager<T>::getOwnership()>{});
} else {
if (_ptrType == PointerType::Nullable) {
@@ -329,65 +403,95 @@ namespace bitsery {
}
private:
template<typename Des, typename TObj>
void destroyPtr(MemResourceBase* memResource, Des& des, TObj& obj,
std::true_type /*polymorphic*/) const {
void destroyPtr(MemResourceBase* memResource,
Des& des,
TObj& obj,
std::true_type /*polymorphic*/) const
{
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
auto ptr = TPtrManager<TObj>::getPtr(obj);
TPtrManager<TObj>::destroyPolymorphic(obj, memResource, ctx.getPolymorphicHandler(*ptr));
TPtrManager<TObj>::destroyPolymorphic(
obj, memResource, ctx.getPolymorphicHandler(*ptr));
}
template<typename Des, typename TObj>
void destroyPtr(MemResourceBase* memResource, Des&, TObj& obj,
std::false_type /*polymorphic*/) const {
TPtrManager<TObj>::destroy(obj, memResource, RTTI::template get<typename TPtrManager<TObj>::TElement>());
void destroyPtr(MemResourceBase* memResource,
Des&,
TObj& obj,
std::false_type /*polymorphic*/) const
{
TPtrManager<TObj>::destroy(
obj,
memResource,
RTTI::template get<typename TPtrManager<TObj>::TElement>());
}
template<typename T>
const void* getBasePtr(const T* ptr) const {
const void* getBasePtr(const T* ptr) 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*
// 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;
}
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
{
const auto& ctx = ser.template context<TPolymorphicContext<RTTI>>();
ctx.serialize(ser, *ptr);
}
template<typename Ser, typename TPtr, typename Fnc>
void serializeImpl(Ser& ser, TPtr& ptr, Fnc&& fnc, std::false_type) const {
void serializeImpl(Ser& ser, TPtr& ptr, Fnc&& fnc, std::false_type) const
{
fnc(ser, *ptr);
}
template<typename Des, typename T, typename Fnc>
void deserializeImpl(MemResourceBase* memResource, PLCInfoDeserializer& ptrInfo, Des& des, T& obj, Fnc&&,
std::true_type, OwnershipType<PointerOwnershipType::Owner>) const {
void deserializeImpl(MemResourceBase* memResource,
PLCInfoDeserializer& ptrInfo,
Des& des,
T& obj,
Fnc&&,
std::true_type,
OwnershipType<PointerOwnershipType::Owner>) const
{
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
ctx.deserialize(des, TPtrManager<T>::getPtr(obj),
[&obj, memResource](
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
ctx.deserialize(
des,
TPtrManager<T>::getPtr(obj),
[&obj,
memResource](const std::shared_ptr<PolymorphicHandlerBase>& handler) {
TPtrManager<T>::createPolymorphic(obj, memResource, handler);
return TPtrManager<T>::getPtr(obj);
},
[&obj, memResource](const std::shared_ptr<PolymorphicHandlerBase>& handler) {
[&obj,
memResource](const std::shared_ptr<PolymorphicHandlerBase>& handler) {
TPtrManager<T>::destroyPolymorphic(obj, memResource, handler);
});
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
}
template<typename Des, typename T, typename Fnc>
void deserializeImpl(MemResourceBase* memResource, PLCInfoDeserializer& ptrInfo, Des& des, T& obj, Fnc&& fnc,
std::false_type, OwnershipType<PointerOwnershipType::Owner>) const {
void deserializeImpl(MemResourceBase* memResource,
PLCInfoDeserializer& ptrInfo,
Des& des,
T& obj,
Fnc&& fnc,
std::false_type,
OwnershipType<PointerOwnershipType::Owner>) const
{
auto ptr = TPtrManager<T>::getPtr(obj);
if (ptr) {
fnc(des, *ptr);
} else {
TPtrManager<T>::create(obj, memResource, RTTI::template get<typename TPtrManager<T>::TElement>());
TPtrManager<T>::create(
obj,
memResource,
RTTI::template get<typename TPtrManager<T>::TElement>());
ptr = TPtrManager<T>::getPtr(obj);
fnc(des, *ptr);
}
@@ -395,39 +499,57 @@ namespace bitsery {
}
template<typename Des, typename T, typename Fnc>
void deserializeImpl(MemResourceBase* memResource, PLCInfoDeserializer& ptrInfo, Des& des, T& obj, Fnc&&,
std::true_type, OwnershipType<PointerOwnershipType::SharedOwner>) const {
void deserializeImpl(MemResourceBase* memResource,
PLCInfoDeserializer& ptrInfo,
Des& des,
T& obj,
Fnc&&,
std::true_type,
OwnershipType<PointerOwnershipType::SharedOwner>) const
{
if (!ptrInfo.sharedState) {
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
ctx.deserialize(des, TPtrManager<T>::getPtr(obj),
ctx.deserialize(
des,
TPtrManager<T>::getPtr(obj),
[&obj, &ptrInfo, memResource, this](
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
TPtrManager<T>::createSharedPolymorphic(
createAndGetSharedStateObj<T>(ptrInfo),
obj, memResource, handler);
createAndGetSharedStateObj<T>(ptrInfo), obj, memResource, handler);
return TPtrManager<T>::getPtr(obj);
},
[&obj, memResource](const std::shared_ptr<PolymorphicHandlerBase>& handler) {
[&obj,
memResource](const std::shared_ptr<PolymorphicHandlerBase>& handler) {
TPtrManager<T>::destroyPolymorphic(obj, memResource, handler);
});
if (!ptrInfo.sharedState)
TPtrManager<T>::saveToSharedState(createAndGetSharedStateObj<T>(ptrInfo), obj);
TPtrManager<T>::saveToSharedState(
createAndGetSharedStateObj<T>(ptrInfo), obj);
}
TPtrManager<T>::loadFromSharedState(getSharedStateObj<T>(ptrInfo), obj);
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
}
template<typename Des, typename T, typename Fnc>
void deserializeImpl(MemResourceBase* memResource, PLCInfoDeserializer& ptrInfo, Des& des, T& obj, Fnc&& fnc,
std::false_type, OwnershipType<PointerOwnershipType::SharedOwner>) const {
void deserializeImpl(MemResourceBase* memResource,
PLCInfoDeserializer& ptrInfo,
Des& des,
T& obj,
Fnc&& fnc,
std::false_type,
OwnershipType<PointerOwnershipType::SharedOwner>) const
{
if (!ptrInfo.sharedState) {
auto ptr = TPtrManager<T>::getPtr(obj);
if (ptr) {
TPtrManager<T>::saveToSharedState(createAndGetSharedStateObj<T>(ptrInfo), obj);
TPtrManager<T>::saveToSharedState(
createAndGetSharedStateObj<T>(ptrInfo), obj);
} else {
TPtrManager<T>::createShared(
createAndGetSharedStateObj<T>(ptrInfo),
obj, memResource, RTTI::template get<typename TPtrManager<T>::TElement>());
obj,
memResource,
RTTI::template get<typename TPtrManager<T>::TElement>());
ptr = TPtrManager<T>::getPtr(obj);
}
fnc(des, *ptr);
@@ -437,36 +559,59 @@ namespace bitsery {
}
template<typename Des, typename T, typename Fnc, typename isPolymorph>
void deserializeImpl(MemResourceBase* memResource, PLCInfoDeserializer& ptrInfo, Des& des, T& obj,
Fnc&& fnc, isPolymorph polymorph,
OwnershipType<PointerOwnershipType::SharedObserver>) const {
deserializeImpl(memResource, ptrInfo, des, obj, fnc, polymorph,
void deserializeImpl(
MemResourceBase* memResource,
PLCInfoDeserializer& ptrInfo,
Des& des,
T& obj,
Fnc&& fnc,
isPolymorph polymorph,
OwnershipType<PointerOwnershipType::SharedObserver>) const
{
deserializeImpl(memResource,
ptrInfo,
des,
obj,
fnc,
polymorph,
OwnershipType<PointerOwnershipType::SharedOwner>{});
}
template<typename Des, typename T, typename Fnc, typename isPolymorphic>
void deserializeImpl(MemResourceBase* , PLCInfoDeserializer& ptrInfo, Des&, T& obj, Fnc&&,
isPolymorphic, OwnershipType<PointerOwnershipType::Observer>) const {
ptrInfo.processObserver(reinterpret_cast<void*&>(TPtrManager<T>::getPtrRef(obj)));
void deserializeImpl(MemResourceBase*,
PLCInfoDeserializer& ptrInfo,
Des&,
T& obj,
Fnc&&,
isPolymorphic,
OwnershipType<PointerOwnershipType::Observer>) const
{
ptrInfo.processObserver(
reinterpret_cast<void*&>(TPtrManager<T>::getPtrRef(obj)));
}
template<typename T>
typename TPtrManager<T>::TSharedState& createAndGetSharedStateObj(PLCInfoDeserializer& info) const {
typename TPtrManager<T>::TSharedState& createAndGetSharedStateObj(
PLCInfoDeserializer& info) const
{
using TSharedState = typename TPtrManager<T>::TSharedState;
StdPolyAlloc<TSharedState> alloc{ info.memResource };
auto* ptr = alloc.allocate(1);
auto* obj = new (ptr) TSharedState{};
info.sharedState = std::unique_ptr<PointerSharedStateBase, PointerSharedStateDeleter>(
info.sharedState =
std::unique_ptr<PointerSharedStateBase, PointerSharedStateDeleter>(
obj, PointerSharedStateDeleter{ info.memResource });
return *obj;
}
template<typename T>
typename TPtrManager<T>::TSharedState& getSharedStateObj(PLCInfoDeserializer& info) const {
return static_cast<typename TPtrManager<T>::TSharedState&>(*info.sharedState);
typename TPtrManager<T>::TSharedState& getSharedStateObj(
PLCInfoDeserializer& info) const
{
return static_cast<typename TPtrManager<T>::TSharedState&>(
*info.sharedState);
}
PointerType _ptrType;
bool _resourcePropagate;
MemResourceBase* _resource;

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -25,8 +25,8 @@
#include "memory_resource.h"
#include <memory>
#include <vector>
#include <unordered_map>
#include <vector>
namespace bitsery {
@@ -34,35 +34,44 @@ namespace bitsery {
// helper type, that contains list of types
template<typename...>
struct PolymorphicClassesList {
struct PolymorphicClassesList
{
};
//specialize for your base class by deriving from PolymorphicDerivedClasses with list of derivatives that DIRECTLY inherits from your base class.
// specialize for your base class by deriving from PolymorphicDerivedClasses
// with list of derivatives that DIRECTLY inherits from your base class.
// e.g.
// template <> PolymorphicBaseClass<Animal>: PolymorphicDerivedClasses<Dog, Cat>{};
// template <> PolymorphicBaseClass<Dog>: PolymorphicDerivedClasses<Bulldog, GoldenRetriever> {};
// IMPORTANT !!!
// template <> PolymorphicBaseClass<Animal>: PolymorphicDerivedClasses<Dog,
// Cat>{}; template <> PolymorphicBaseClass<Dog>:
// PolymorphicDerivedClasses<Bulldog, GoldenRetriever> {}; IMPORTANT !!!
// although you can add all derivates to same base like this:
// template <> PolymorphicBaseClass<Animal>:PolymorphicDerivedClasses<Dog, Cat, Bulldog, GoldenRetriever>{};
// it will not work when you try to serialize Dog*, because it will not find Bulldog and GoldenRetriever
// template <> PolymorphicBaseClass<Animal>:PolymorphicDerivedClasses<Dog, Cat,
// Bulldog, GoldenRetriever>{}; it will not work when you try to serialize
// Dog*, because it will not find Bulldog and GoldenRetriever
template<typename TBase>
struct PolymorphicBaseClass {
struct PolymorphicBaseClass
{
using Childs = PolymorphicClassesList<>;
};
//derive from this class when specifying childs for your base class, atleast one child must exists, hence T1
//e.g.
// template <> PolymorphicBaseClass<Animal>: PolymorphicDerivedClasses<Dog, Cat>{};
// derive from this class when specifying childs for your base class, atleast
// one child must exists, hence T1 e.g.
// template <> PolymorphicBaseClass<Animal>: PolymorphicDerivedClasses<Dog,
// Cat>{};
template<typename T1, typename... Tn>
struct PolymorphicDerivedClasses {
struct PolymorphicDerivedClasses
{
using Childs = PolymorphicClassesList<T1, Tn...>;
};
class PolymorphicHandlerBase {
class PolymorphicHandlerBase
{
public:
virtual void* create(const pointer_utils::PolyAllocWithTypeId& alloc) const = 0;
virtual void* create(
const pointer_utils::PolyAllocWithTypeId& alloc) const = 0;
virtual void destroy(const pointer_utils::PolyAllocWithTypeId& alloc, void* ptr) const = 0;
virtual void destroy(const pointer_utils::PolyAllocWithTypeId& alloc,
void* ptr) const = 0;
virtual void process(void* ser, void* obj) const = 0;
@@ -70,192 +79,263 @@ namespace bitsery {
};
template<typename RTTI, typename TSerializer, typename TBase, typename TDerived>
class PolymorphicHandler : public PolymorphicHandlerBase {
class PolymorphicHandler : public PolymorphicHandlerBase
{
public:
void* create(const pointer_utils::PolyAllocWithTypeId& alloc) const final {
void* create(const pointer_utils::PolyAllocWithTypeId& alloc) const final
{
return toBase(alloc.newObject<TDerived>(RTTI::template get<TDerived>()));
}
void destroy(const pointer_utils::PolyAllocWithTypeId& alloc, void* ptr) const final {
void destroy(const pointer_utils::PolyAllocWithTypeId& alloc,
void* ptr) const final
{
alloc.deleteObject<TDerived>(fromBase(ptr), RTTI::template get<TDerived>());
}
void process(void* ser, void* obj) const final {
void process(void* ser, void* obj) const final
{
static_cast<TSerializer*>(ser)->object(*fromBase(obj));
}
private:
TDerived* fromBase(void* obj) const {
TDerived* fromBase(void* obj) const
{
return RTTI::template cast<TBase, TDerived>(static_cast<TBase*>(obj));
}
TBase* toBase(void* obj) const {
TBase* toBase(void* obj) const
{
return RTTI::template cast<TDerived, TBase>(static_cast<TDerived*>(obj));
}
};
template<typename RTTI>
class PolymorphicContext {
class PolymorphicContext
{
private:
struct BaseToDerivedKey {
struct BaseToDerivedKey
{
std::size_t baseHash;
std::size_t derivedHash;
bool operator==(const BaseToDerivedKey& other) const {
bool operator==(const BaseToDerivedKey& other) const
{
return baseHash == other.baseHash && derivedHash == other.derivedHash;
}
};
struct BaseToDerivedKeyHashier {
size_t operator()(const BaseToDerivedKey& key) const {
return (key.baseHash + (key.baseHash << 6) + (key.derivedHash >> 2)) ^ key.derivedHash;
struct BaseToDerivedKeyHashier
{
size_t operator()(const BaseToDerivedKey& key) const
{
return (key.baseHash + (key.baseHash << 6) + (key.derivedHash >> 2)) ^
key.derivedHash;
}
};
template<typename TSerializer, template<typename> class THierarchy, typename TBase, typename TDerived>
void add() {
template<typename TSerializer,
template<typename>
class THierarchy,
typename TBase,
typename TDerived>
void add()
{
addToMap<TSerializer, TBase, TDerived>(std::is_abstract<TDerived>{});
addChilds<TSerializer, THierarchy, TBase, TDerived>(typename THierarchy<TDerived>::Childs{});
addChilds<TSerializer, THierarchy, TBase, TDerived>(
typename THierarchy<TDerived>::Childs{});
}
template<typename TSerializer, template<typename> class THierarchy, typename TBase, typename TDerived, typename T1, typename ... Tn>
void addChilds(PolymorphicClassesList<T1, Tn...>) {
template<typename TSerializer,
template<typename>
class THierarchy,
typename TBase,
typename TDerived,
typename T1,
typename... Tn>
void addChilds(PolymorphicClassesList<T1, Tn...>)
{
static_assert(std::is_base_of<TDerived, T1>::value,
"PolymorphicBaseClass<TBase> must derive a list of derived classes from TBase.");
"PolymorphicBaseClass<TBase> must derive a list of derived "
"classes from TBase.");
add<TSerializer, THierarchy, TBase, T1>();
addChilds<TSerializer, THierarchy, TBase, TDerived>(PolymorphicClassesList<Tn...>{});
addChilds<TSerializer, THierarchy, TBase, TDerived>(
PolymorphicClassesList<Tn...>{});
// iterate through derived class hierarchy as well
add<TSerializer, THierarchy, T1, T1>();
}
template<typename TSerializer, template<typename> class THierarchy, typename TBase, typename TDerived>
void addChilds(PolymorphicClassesList<>) {
template<typename TSerializer,
template<typename>
class THierarchy,
typename TBase,
typename TDerived>
void addChilds(PolymorphicClassesList<>)
{
}
template<typename TSerializer, typename TBase, typename TDerived>
void addToMap(std::false_type) {
void addToMap(std::false_type)
{
using THandler = PolymorphicHandler<RTTI, TSerializer, TBase, TDerived>;
BaseToDerivedKey key{RTTI::template get<TBase>(), RTTI::template get<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 {
std::shared_ptr<THandler> handler(
new (ptr) THandler{},
[alloc](THandler* data) mutable {
data->~THandler();
alloc.deallocate(data, 1);
}, alloc);
if (_baseToDerivedMap
.emplace(key, std::move(handler))
.second) {
},
alloc);
if (_baseToDerivedMap.emplace(key, std::move(handler)).second) {
auto it = _baseToDerivedArray.find(key.baseHash);
if (it == _baseToDerivedArray.end()) {
it = _baseToDerivedArray.emplace(
std::piecewise_construct,
it = _baseToDerivedArray
.emplace(std::piecewise_construct,
std::forward_as_tuple(key.baseHash),
std::forward_as_tuple(pointer_utils::StdPolyAlloc<size_t>{_memResource})).first;
std::forward_as_tuple(
pointer_utils::StdPolyAlloc<size_t>{ _memResource }))
.first;
}
it->second.push_back(key.derivedHash);
}
}
template<typename TSerializer, typename TBase, typename TDerived>
void addToMap(std::true_type) {
void addToMap(std::true_type)
{
// cannot add abstract class
}
MemResourceBase* _memResource;
// store shared ptr to polymorphic handler, because it might be copied to "smart pointer" deleter
std::unordered_map<BaseToDerivedKey, std::shared_ptr<PolymorphicHandlerBase>,
BaseToDerivedKeyHashier, std::equal_to<BaseToDerivedKey>,
pointer_utils::StdPolyAlloc<std::pair<const BaseToDerivedKey, std::shared_ptr<PolymorphicHandlerBase>>>
> _baseToDerivedMap;
// this will allow convert from platform specific type information, to platform independent base->derived index
// this only works if all polymorphic relationships (PolymorphicBaseClass<TBase> -> PolymorphicDerivedClasses<TDerived...>)
// is equal between platforms.
std::unordered_map<size_t, std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>,
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>>>>
> _baseToDerivedArray;
// store shared ptr to polymorphic handler, because it might be copied to
// "smart pointer" deleter
std::unordered_map<BaseToDerivedKey,
std::shared_ptr<PolymorphicHandlerBase>,
BaseToDerivedKeyHashier,
std::equal_to<BaseToDerivedKey>,
pointer_utils::StdPolyAlloc<
std::pair<const BaseToDerivedKey,
std::shared_ptr<PolymorphicHandlerBase>>>>
_baseToDerivedMap;
// this will allow convert from platform specific type information, to
// platform independent base->derived index this only works if all polymorphic
// relationships (PolymorphicBaseClass<TBase> ->
// PolymorphicDerivedClasses<TDerived...>) is equal between platforms.
std::unordered_map<
size_t,
std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>,
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>>>>>
_baseToDerivedArray;
public:
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}}
{}
: _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 } }
{
}
PolymorphicContext(const PolymorphicContext&) = delete;
PolymorphicContext& operator=(const PolymorphicContext&) = delete;
PolymorphicContext(PolymorphicContext&&) = default;
PolymorphicContext& operator=(PolymorphicContext&&) = default;
void clear() {
void clear()
{
_baseToDerivedMap.clear();
_baseToDerivedArray.clear();
}
// THierarchy is the name of class, that defines hierarchy
// PolymorphicBaseClass is defined as default parameter, so that at instantiation time
// it will get unique symbol in translation unit for PolymorphicBaseClass (which is defined in anonymous namespace)
// PolymorphicBaseClass is defined as default parameter, so that at
// instantiation time it will get unique symbol in translation unit for
// PolymorphicBaseClass (which is defined in anonymous namespace)
// https://github.com/fraillt/bitsery/issues/9
template<typename TSerializer, template<typename> class THierarchy = PolymorphicBaseClass, typename T1, typename ...Tn>
void registerBasesList(PolymorphicClassesList<T1, Tn...>) {
template<typename TSerializer,
template<typename> class THierarchy = PolymorphicBaseClass,
typename T1,
typename... Tn>
void registerBasesList(PolymorphicClassesList<T1, Tn...>)
{
add<TSerializer, THierarchy, T1, T1>();
registerBasesList<TSerializer, THierarchy>(PolymorphicClassesList<Tn...>{});
}
template<typename TSerializer, template<typename> class THierarchy>
void registerBasesList(PolymorphicClassesList<>) {
void registerBasesList(PolymorphicClassesList<>)
{
}
// optional method, in case you want to construct base class hierarchy your self
// 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");
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 {
void serialize(Serializer& ser, TBase& obj) const
{
// get derived key
BaseToDerivedKey key{RTTI::template get<TBase>(), RTTI::template get<TBase>(obj)};
BaseToDerivedKey key{ RTTI::template get<TBase>(),
RTTI::template get<TBase>(obj) };
auto it = _baseToDerivedMap.find(key);
assert(it != _baseToDerivedMap.end());
//convert derived hash to derived index, to make it work in cross-platform environment
// convert derived hash to derived index, to make it work in cross-platform
// environment
auto& vec = _baseToDerivedArray.find(key.baseHash)->second;
auto derivedIndex = static_cast<size_t>(std::distance(vec.begin(), std::find(vec.begin(), vec.end(),
key.derivedHash)));
auto derivedIndex = static_cast<size_t>(std::distance(
vec.begin(), std::find(vec.begin(), vec.end(), key.derivedHash)));
details::writeSize(ser.adapter(), derivedIndex);
// serialize
it->second->process(&ser, &obj);
}
template<typename Deserializer, typename TBase, typename TCreateFnc, typename TDestroyFnc>
void deserialize(Deserializer& des, TBase* obj,
TCreateFnc createFnc, TDestroyFnc destroyFnc) const {
template<typename Deserializer,
typename TBase,
typename TCreateFnc,
typename TDestroyFnc>
void deserialize(Deserializer& des,
TBase* obj,
TCreateFnc createFnc,
TDestroyFnc destroyFnc) const
{
size_t derivedIndex{};
details::readSize(des.adapter(), derivedIndex, 0, std::false_type{});
auto baseToDerivedVecIt = _baseToDerivedArray.find(RTTI::template get<TBase>());
auto baseToDerivedVecIt =
_baseToDerivedArray.find(RTTI::template get<TBase>());
// base class is known at compile time, so we can assert on this one
assert(baseToDerivedVecIt != _baseToDerivedArray.end());
if (baseToDerivedVecIt->second.size() > derivedIndex) {
//convert derived index to derived hash, to make it work in cross-platform environment
// convert derived index to derived hash, to make it work in
// cross-platform environment
auto derivedHash = baseToDerivedVecIt->second[derivedIndex];
auto& handler = _baseToDerivedMap.find(
BaseToDerivedKey{RTTI::template get<TBase>(), derivedHash})->second;
auto& handler =
_baseToDerivedMap
.find(BaseToDerivedKey{ RTTI::template get<TBase>(), derivedHash })
->second;
// if object is null or different type, create new and assign it
if (obj == nullptr || RTTI::template get<TBase>(*obj) != derivedHash) {
if (obj) {
@@ -269,14 +349,14 @@ namespace bitsery {
}
template<typename TBase>
const std::shared_ptr<PolymorphicHandlerBase>& getPolymorphicHandler(TBase& obj) const {
auto deleteHandlerIt = _baseToDerivedMap.find(
BaseToDerivedKey{RTTI::template get<TBase>(), RTTI::template get<TBase>(obj)});
const std::shared_ptr<PolymorphicHandlerBase>& getPolymorphicHandler(
TBase& obj) const
{
auto deleteHandlerIt = _baseToDerivedMap.find(BaseToDerivedKey{
RTTI::template get<TBase>(), RTTI::template get<TBase>(obj) });
assert(deleteHandlerIt != _baseToDerivedMap.end());
return deleteHandlerIt->second;
}
};
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -23,36 +23,40 @@
#ifndef BITSERY_RTTI_UTILS_H
#define BITSERY_RTTI_UTILS_H
#include <typeinfo>
#include <type_traits>
#include <cstddef>
#include <type_traits>
#include <typeinfo>
namespace bitsery {
namespace ext {
struct StandardRTTI {
struct StandardRTTI
{
template<typename TBase>
static size_t get(TBase& obj) {
static size_t get(TBase& obj)
{
return typeid(obj).hash_code();
}
template<typename TBase>
static constexpr size_t get() {
static constexpr size_t get()
{
return typeid(TBase).hash_code();
}
template<typename TBase, typename TDerived>
static constexpr TDerived* cast(TBase* obj) {
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() {
static constexpr bool isPolymorphic()
{
return std::is_polymorphic<TBase>::value;
}
};
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -28,9 +28,14 @@
namespace bitsery {
namespace ext {
//this class is used to make default RangeSpec float specialization always prefer constructor with precision
struct BitsConstraint {
explicit constexpr BitsConstraint(size_t bits) : value{bits} {}
// this class is used to make default RangeSpec float specialization always
// prefer constructor with precision
struct BitsConstraint
{
explicit constexpr BitsConstraint(size_t bits)
: value{ bits }
{
}
const size_t value;
};
@@ -40,23 +45,30 @@ namespace bitsery {
namespace details {
template<typename T>
constexpr size_t getSize(T v, size_t s) {
constexpr size_t
getSize(T v, size_t s)
{
return v > 0 ? getSize(v / 2, s + 1) : s;
}
template<typename T>
constexpr size_t calcRequiredBits(T min, T max) {
//call recursive function, because some compilers only support constexpr functions with return-only body
constexpr size_t
calcRequiredBits(T min, T max)
{
// call recursive function, because some compilers only support constexpr
// functions with return-only body
return getSize(max - min, 0);
}
template<typename T, typename Enable = void>
struct RangeSpec {
struct RangeSpec
{
constexpr RangeSpec(T minValue, T maxValue)
: min{minValue},
max{maxValue},
bitsRequired{calcRequiredBits(min, max)} {
: min{ minValue }
, max{ maxValue }
, bitsRequired{ calcRequiredBits(min, max) }
{
}
const T min;
@@ -64,16 +76,17 @@ namespace bitsery {
const size_t bitsRequired;
};
template<typename T>
struct RangeSpec<T, typename std::enable_if<std::is_enum<T>::value>::type> {
struct RangeSpec<T, typename std::enable_if<std::is_enum<T>::value>::type>
{
constexpr RangeSpec(T minValue, T maxValue) :
min{minValue},
max{maxValue},
bitsRequired{calcRequiredBits(
constexpr RangeSpec(T minValue, T maxValue)
: min{ minValue }
, max{ maxValue }
, bitsRequired{ calcRequiredBits(
static_cast<typename std::underlying_type<T>::type>(min),
static_cast<typename std::underlying_type<T>::type>(max))} {
static_cast<typename std::underlying_type<T>::type>(max)) }
{
}
const T min;
@@ -81,22 +94,26 @@ namespace bitsery {
const size_t bitsRequired;
};
template<typename T>
struct RangeSpec<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
struct RangeSpec<
T,
typename std::enable_if<std::is_floating_point<T>::value>::type>
{
constexpr RangeSpec(T minValue, T maxValue, ext::BitsConstraint bits) :
min{minValue},
max{maxValue},
bitsRequired{bits.value} {
constexpr RangeSpec(T minValue, T maxValue, ext::BitsConstraint bits)
: min{ minValue }
, max{ maxValue }
, bitsRequired{ bits.value }
{
}
constexpr RangeSpec(T minValue, T maxValue, T precision) :
min{minValue},
max{maxValue},
bitsRequired{calcRequiredBits<details::SameSizeUnsigned<T>>(
{}, static_cast<details::SameSizeUnsigned<T>>((max - min) / precision))} {
constexpr RangeSpec(T minValue, T maxValue, T precision)
: min{ minValue }
, max{ maxValue }
, bitsRequired{ calcRequiredBits<details::SameSizeUnsigned<T>>(
{},
static_cast<details::SameSizeUnsigned<T>>((max - min) / precision)) }
{
}
const T min;
@@ -104,89 +121,122 @@ namespace bitsery {
const size_t bitsRequired;
};
template<typename T, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
details::SameSizeUnsigned<T> getRangeValue(const T &v, const RangeSpec<T> &r) {
template<typename T,
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
details::SameSizeUnsigned<T>
getRangeValue(const T& v, const RangeSpec<T>& r)
{
return static_cast<details::SameSizeUnsigned<T>>(v - r.min);
}
template<typename T, typename std::enable_if<std::is_enum<T>::value>::type * = nullptr>
details::SameSizeUnsigned<T> getRangeValue(const T &v, const RangeSpec<T> &r) {
template<typename T,
typename std::enable_if<std::is_enum<T>::value>::type* = nullptr>
details::SameSizeUnsigned<T>
getRangeValue(const T& v, const RangeSpec<T>& r)
{
using VT = details::SameSizeUnsigned<T>;
return static_cast<VT>(static_cast<VT>(v) - static_cast<VT>(r.min));
}
template<typename T, typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr>
details::SameSizeUnsigned<T> getRangeValue(const T &v, const RangeSpec<T> &r) {
template<
typename T,
typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
details::SameSizeUnsigned<T>
getRangeValue(const T& v, const RangeSpec<T>& r)
{
using VT = details::SameSizeUnsigned<T>;
const VT maxUint = (static_cast<VT>(1) << r.bitsRequired) - 1;
const T ratio = (v - r.min) / (r.max - r.min);
return static_cast<VT>(ratio * static_cast<T>(maxUint));
}
template<typename T, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
void setRangeValue(T &v, const RangeSpec<T> &r) {
template<typename T,
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
void
setRangeValue(T& v, const RangeSpec<T>& r)
{
v += r.min;
}
template<typename T, typename std::enable_if<std::is_enum<T>::value>::type * = nullptr>
void setRangeValue(T &v, const RangeSpec<T> &r) {
template<typename T,
typename std::enable_if<std::is_enum<T>::value>::type* = nullptr>
void
setRangeValue(T& v, const RangeSpec<T>& r)
{
using VT = typename std::underlying_type<T>::type;
reinterpret_cast<VT&>(v) += static_cast<VT>(r.min);
}
template<typename T, typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr>
void setRangeValue(T &v, const RangeSpec<T> &r) {
template<
typename T,
typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
void
setRangeValue(T& v, const RangeSpec<T>& r)
{
using UIT = details::SameSizeUnsigned<T>;
const auto intRep = reinterpret_cast<UIT&>(v);
const UIT maxUint = (static_cast<UIT>(1) << r.bitsRequired) - 1;
v = r.min + (static_cast<T>(intRep) / static_cast<T>(maxUint)) * (r.max - r.min);
v = r.min +
(static_cast<T>(intRep) / static_cast<T>(maxUint)) * (r.max - r.min);
}
template<typename T, typename std::enable_if<std::is_arithmetic<T>::value>::type * = nullptr>
bool isRangeValid(const T &v, const RangeSpec<T> &r) {
template<typename T,
typename std::enable_if<std::is_arithmetic<T>::value>::type* = nullptr>
bool
isRangeValid(const T& v, const RangeSpec<T>& r)
{
return !(r.min > v || v > r.max);
}
template<typename T, typename std::enable_if<std::is_enum<T>::value>::type * = nullptr>
bool isRangeValid(const T &v, const RangeSpec<T> &r) {
template<typename T,
typename std::enable_if<std::is_enum<T>::value>::type* = nullptr>
bool
isRangeValid(const T& v, const RangeSpec<T>& r)
{
using VT = typename std::underlying_type<T>::type;
return !(static_cast<VT>(r.min) > static_cast<VT>(v)
|| static_cast<VT>(v) > static_cast<VT>(r.max));
return !(static_cast<VT>(r.min) > static_cast<VT>(v) ||
static_cast<VT>(v) > static_cast<VT>(r.max));
}
}
namespace ext {
template<typename TValue>
class ValueRange {
class ValueRange
{
public:
template<typename... Args>
constexpr ValueRange(const TValue& min, const TValue& max, Args&&... args)
:_range{min, max, std::forward<Args>(args)...} {}
: _range{ min, max, std::forward<Args>(args)... }
{
}
template<typename Ser, typename T, typename Fnc>
void serialize(Ser &ser, const T &v, Fnc &&) const {
void serialize(Ser& ser, const T& v, Fnc&&) const
{
assert(details::isRangeValid(v, _range));
using BT = decltype(details::getRangeValue(v, _range));
ser.adapter().template writeBits<BT>(details::getRangeValue(v, _range), _range.bitsRequired);
ser.adapter().template writeBits<BT>(details::getRangeValue(v, _range),
_range.bitsRequired);
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des &des, T &v, Fnc &&) const {
void deserialize(Des& des, T& v, Fnc&&) const
{
auto& reader = des.adapter();
reader.readBits(reinterpret_cast<details::SameSizeUnsigned<T> &>(v), _range.bitsRequired);
reader.readBits(reinterpret_cast<details::SameSizeUnsigned<T>&>(v),
_range.bitsRequired);
details::setRangeValue(v, _range);
handleInvalidRange(reader, v, std::integral_constant<bool, Des::TConfig::CheckDataErrors>{});
handleInvalidRange(
reader, v, std::integral_constant<bool, Des::TConfig::CheckDataErrors>{});
}
constexpr size_t getRequiredBits() const {
return _range.bitsRequired;
};
private:
constexpr size_t getRequiredBits() const { return _range.bitsRequired; };
private:
template<typename Reader, typename T>
void handleInvalidRange(Reader& reader, T& v, std::true_type) const {
void handleInvalidRange(Reader& reader, T& v, std::true_type) const
{
if (!details::isRangeValid(v, _range)) {
reader.error(ReaderError::InvalidData);
v = _range.min;
@@ -194,7 +244,8 @@ namespace bitsery {
}
template<typename Reader, typename T>
void handleInvalidRange(Reader&, T&, std::false_type) const {
void handleInvalidRange(Reader&, T&, std::false_type) const
{
}
details::RangeSpec<TValue> _range;
@@ -203,7 +254,8 @@ namespace bitsery {
namespace traits {
template<typename T>
struct ExtensionTraits<ext::ValueRange<T>, T> {
struct ExtensionTraits<ext::ValueRange<T>, T>
{
using TValue = void;
static constexpr bool SupportValueOverload = false;
static constexpr bool SupportObjectOverload = true;
@@ -213,5 +265,4 @@ namespace bitsery {
}
#endif // BITSERY_EXT_VALUE_RANGE_H

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,7 +20,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_SERIALIZER_H
#define BITSERY_SERIALIZER_H
@@ -30,33 +29,43 @@
namespace bitsery {
template<typename TOutputAdapter, typename TContext = void>
class Serializer: public details::AdapterAndContextRef<TOutputAdapter, TContext> {
class Serializer
: public details::AdapterAndContextRef<TOutputAdapter, TContext>
{
public:
//helper type, that always returns bit-packing enabled type, useful inside serialize function when enabling bitpacking
using BPEnabledType = Serializer<typename TOutputAdapter::BitPackingEnabled, TContext>;
// helper type, that always returns bit-packing enabled type, useful inside
// serialize function when enabling bitpacking
using BPEnabledType =
Serializer<typename TOutputAdapter::BitPackingEnabled, TContext>;
using TConfig = typename TOutputAdapter::TConfig;
using details::AdapterAndContextRef<TOutputAdapter, TContext>::AdapterAndContextRef;
using details::AdapterAndContextRef<TOutputAdapter,
TContext>::AdapterAndContextRef;
/*
* object function
*/
template<typename T>
void object(const T &obj) {
details::SerializeFunction<Serializer, T>::invoke(*this, const_cast<T& >(obj));
void object(const T& obj)
{
details::SerializeFunction<Serializer, T>::invoke(*this,
const_cast<T&>(obj));
}
template<typename T, typename Fnc>
void object(const T &obj, Fnc &&fnc) {
void object(const T& obj, Fnc&& fnc)
{
fnc(*this, const_cast<T&>(obj));
}
/*
* functionality, that enables simpler serialization syntax, by including additional header
* functionality, that enables simpler serialization syntax, by including
* additional header
*/
template<typename... TArgs>
Serializer &operator()(TArgs &&... args) {
Serializer& operator()(TArgs&&... args)
{
archive(std::forward<TArgs>(args)...);
return *this;
}
@@ -66,19 +75,25 @@ namespace bitsery {
*/
template<size_t VSIZE, typename T>
void value(const T &v) {
static_assert(details::IsFundamentalType<T>::value, "Value must be integral, float or enum type.");
void value(const T& v)
{
static_assert(details::IsFundamentalType<T>::value,
"Value must be integral, float or enum type.");
using TValue = typename details::IntegralFromFundamental<T>::TValue;
this->_adapter.template writeBytes<VSIZE>(reinterpret_cast<const TValue &>(v));
this->_adapter.template writeBytes<VSIZE>(
reinterpret_cast<const TValue&>(v));
}
/*
* enable bit-packing
*/
template<typename Fnc>
void enableBitPacking(Fnc&& fnc) {
procEnableBitPacking(std::forward<Fnc>(fnc),
std::is_same<TOutputAdapter, typename TOutputAdapter::BitPackingEnabled>{},
void enableBitPacking(Fnc&& fnc)
{
procEnableBitPacking(
std::forward<Fnc>(fnc),
std::is_same<TOutputAdapter,
typename TOutputAdapter::BitPackingEnabled>{},
std::integral_constant<bool, Serializer::HasContext>{});
}
@@ -87,39 +102,54 @@ namespace bitsery {
*/
template<typename T, typename Ext, typename Fnc>
void ext(const T &obj, const Ext &extension, Fnc &&fnc) {
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
void ext(const T& obj, const Ext& extension, Fnc&& fnc)
{
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value,
"Please define ExtensionTraits");
static_assert(traits::ExtensionTraits<Ext, T>::SupportLambdaOverload,
"extension doesn't support overload with lambda");
extension.serialize(*this, obj, std::forward<Fnc>(fnc));
}
template<size_t VSIZE, typename T, typename Ext>
void ext(const T &obj, const Ext &extension) {
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
void ext(const T& obj, const Ext& extension)
{
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value,
"Please define ExtensionTraits");
static_assert(traits::ExtensionTraits<Ext, T>::SupportValueOverload,
"extension doesn't support overload with `value<N>`");
using ExtVType = typename traits::ExtensionTraits<Ext, T>::TValue;
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
extension.serialize(*this, obj, [](Serializer& s, VType &v) { s.value<VSIZE>(v); });
using VType = typename std::conditional<std::is_void<ExtVType>::value,
details::DummyType,
ExtVType>::type;
extension.serialize(
*this, obj, [](Serializer& s, VType& v) { s.value<VSIZE>(v); });
}
template<typename T, typename Ext>
void ext(const T &obj, const Ext &extension) {
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
void ext(const T& obj, const Ext& extension)
{
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value,
"Please define ExtensionTraits");
static_assert(traits::ExtensionTraits<Ext, T>::SupportObjectOverload,
"extension doesn't support overload with `object`");
using ExtVType = typename traits::ExtensionTraits<Ext, T>::TValue;
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
extension.serialize(*this, obj, [](Serializer& s, VType &v) { s.object(v); });
using VType = typename std::conditional<std::is_void<ExtVType>::value,
details::DummyType,
ExtVType>::type;
extension.serialize(
*this, obj, [](Serializer& s, VType& v) { s.object(v); });
}
/*
* boolValue
*/
void boolValue(bool v) {
procBoolValue(v, std::is_same<TOutputAdapter, typename TOutputAdapter::BitPackingEnabled>{});
void boolValue(bool v)
{
procBoolValue(v,
std::is_same<TOutputAdapter,
typename TOutputAdapter::BitPackingEnabled>{});
}
/*
@@ -127,20 +157,26 @@ namespace bitsery {
*/
template<size_t VSIZE, typename T>
void text(const T &str, size_t maxSize) {
static_assert(details::IsTextTraitsDefined<T>::value,
void text(const T& str, size_t maxSize)
{
static_assert(
details::IsTextTraitsDefined<T>::value,
"Please define TextTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<T>::isResizable,
static_assert(
traits::ContainerTraits<T>::isResizable,
"use text(const T&) overload without `maxSize` for static container");
procText<VSIZE>(str, maxSize);
}
template<size_t VSIZE, typename T>
void text(const T &str) {
static_assert(details::IsTextTraitsDefined<T>::value,
void text(const T& str)
{
static_assert(
details::IsTextTraitsDefined<T>::value,
"Please define TextTraits or include from <bitsery/traits/...>");
static_assert(!traits::ContainerTraits<T>::isResizable,
"use text(const T&, size_t) overload with `maxSize` for dynamic containers");
"use text(const T&, size_t) overload with `maxSize` for "
"dynamic containers");
procText<VSIZE>(str, traits::ContainerTraits<T>::size(str));
}
@@ -151,11 +187,14 @@ namespace bitsery {
// dynamic size containers
template<typename T, typename Fnc>
void container(const T &obj, size_t maxSize, Fnc &&fnc) {
static_assert(details::IsContainerTraitsDefined<T>::value,
void container(const T& obj, size_t maxSize, Fnc&& fnc)
{
static_assert(
details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<T>::isResizable,
"use container(const T&, Fnc) overload without `maxSize` for static containers");
"use container(const T&, Fnc) overload without `maxSize` for "
"static containers");
auto size = traits::ContainerTraits<T>::size(obj);
(void)maxSize; // unused in release
assert(size <= maxSize);
@@ -164,26 +203,35 @@ namespace bitsery {
}
template<size_t VSIZE, typename T>
void container(const T &obj, size_t maxSize) {
static_assert(details::IsContainerTraitsDefined<T>::value,
void container(const T& obj, size_t maxSize)
{
static_assert(
details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<T>::isResizable,
"use container(const T&) overload without `maxSize` for static containers");
"use container(const T&) overload without `maxSize` for "
"static containers");
static_assert(VSIZE > 0, "");
auto size = traits::ContainerTraits<T>::size(obj);
(void)maxSize; // unused in release
assert(size <= maxSize);
details::writeSize(this->_adapter, size);
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
procContainer<VSIZE>(
std::begin(obj),
std::end(obj),
std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
}
template<typename T>
void container(const T &obj, size_t maxSize) {
static_assert(details::IsContainerTraitsDefined<T>::value,
void container(const T& obj, size_t maxSize)
{
static_assert(
details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<T>::isResizable,
"use container(const T&) overload without `maxSize` for static containers");
"use container(const T&) overload without `maxSize` for "
"static containers");
auto size = traits::ContainerTraits<T>::size(obj);
(void)maxSize; // unused in release
assert(size <= maxSize);
@@ -193,121 +241,213 @@ namespace bitsery {
// fixed size containers
template<typename T, typename Fnc, typename std::enable_if<!std::is_integral<Fnc>::value>::type * = nullptr>
void container(const T &obj, Fnc &&fnc) {
static_assert(details::IsContainerTraitsDefined<T>::value,
template<
typename T,
typename Fnc,
typename std::enable_if<!std::is_integral<Fnc>::value>::type* = nullptr>
void container(const T& obj, Fnc&& fnc)
{
static_assert(
details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(!traits::ContainerTraits<T>::isResizable,
"use container(const T&, size_t, Fnc) overload with `maxSize` for dynamic containers");
"use container(const T&, size_t, Fnc) overload with "
"`maxSize` for dynamic containers");
procContainer(std::begin(obj), std::end(obj), std::forward<Fnc>(fnc));
}
template<size_t VSIZE, typename T>
void container(const T &obj) {
static_assert(details::IsContainerTraitsDefined<T>::value,
void container(const T& obj)
{
static_assert(
details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(!traits::ContainerTraits<T>::isResizable,
"use container(const T&, size_t) overload with `maxSize` for dynamic containers");
"use container(const T&, size_t) overload with `maxSize` for "
"dynamic containers");
static_assert(VSIZE > 0, "");
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
procContainer<VSIZE>(
std::begin(obj),
std::end(obj),
std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
}
template<typename T>
void container(const T &obj) {
static_assert(details::IsContainerTraitsDefined<T>::value,
void container(const T& obj)
{
static_assert(
details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(!traits::ContainerTraits<T>::isResizable,
"use container(const T&, size_t) overload with `maxSize` for dynamic containers");
"use container(const T&, size_t) overload with `maxSize` for "
"dynamic containers");
procContainer(std::begin(obj), std::end(obj));
}
// overloads for functions with explicit type size
template<typename T>
void value1b(T &&v) { value<1>(std::forward<T>(v)); }
void value1b(T&& v)
{
value<1>(std::forward<T>(v));
}
template<typename T>
void value2b(T &&v) { value<2>(std::forward<T>(v)); }
void value2b(T&& v)
{
value<2>(std::forward<T>(v));
}
template<typename T>
void value4b(T &&v) { value<4>(std::forward<T>(v)); }
void value4b(T&& v)
{
value<4>(std::forward<T>(v));
}
template<typename T>
void value8b(T &&v) { value<8>(std::forward<T>(v)); }
void value8b(T&& v)
{
value<8>(std::forward<T>(v));
}
template<typename T>
void value16b(T &&v) { value<16>(std::forward<T>(v)); }
void value16b(T&& v)
{
value<16>(std::forward<T>(v));
}
template<typename T, typename Ext>
void ext1b(const T &v, Ext &&extension) { ext<1, T, Ext>(v, std::forward<Ext>(extension)); }
void ext1b(const T& v, Ext&& extension)
{
ext<1, T, Ext>(v, std::forward<Ext>(extension));
}
template<typename T, typename Ext>
void ext2b(const T &v, Ext &&extension) { ext<2, T, Ext>(v, std::forward<Ext>(extension)); }
void ext2b(const T& v, Ext&& extension)
{
ext<2, T, Ext>(v, std::forward<Ext>(extension));
}
template<typename T, typename Ext>
void ext4b(const T &v, Ext &&extension) { ext<4, T, Ext>(v, std::forward<Ext>(extension)); }
void ext4b(const T& v, Ext&& extension)
{
ext<4, T, Ext>(v, std::forward<Ext>(extension));
}
template<typename T, typename Ext>
void ext8b(const T &v, Ext &&extension) { ext<8, T, Ext>(v, std::forward<Ext>(extension)); }
void ext8b(const T& v, Ext&& extension)
{
ext<8, T, Ext>(v, std::forward<Ext>(extension));
}
template<typename T, typename Ext>
void ext16b(const T &v, Ext &&extension) { ext<16, T, Ext>(v, std::forward<Ext>(extension)); }
void ext16b(const T& v, Ext&& extension)
{
ext<16, T, Ext>(v, std::forward<Ext>(extension));
}
template<typename T>
void text1b(const T &str, size_t maxSize) { text<1>(str, maxSize); }
void text1b(const T& str, size_t maxSize)
{
text<1>(str, maxSize);
}
template<typename T>
void text2b(const T &str, size_t maxSize) { text<2>(str, maxSize); }
void text2b(const T& str, size_t maxSize)
{
text<2>(str, maxSize);
}
template<typename T>
void text4b(const T &str, size_t maxSize) { text<4>(str, maxSize); }
void text4b(const T& str, size_t maxSize)
{
text<4>(str, maxSize);
}
template<typename T>
void text1b(const T &str) { text<1>(str); }
void text1b(const T& str)
{
text<1>(str);
}
template<typename T>
void text2b(const T &str) { text<2>(str); }
void text2b(const T& str)
{
text<2>(str);
}
template<typename T>
void text4b(const T &str) { text<4>(str); }
void text4b(const T& str)
{
text<4>(str);
}
template<typename T>
void container1b(T &&obj, size_t maxSize) { container<1>(std::forward<T>(obj), maxSize); }
void container1b(T&& obj, size_t maxSize)
{
container<1>(std::forward<T>(obj), maxSize);
}
template<typename T>
void container2b(T &&obj, size_t maxSize) { container<2>(std::forward<T>(obj), maxSize); }
void container2b(T&& obj, size_t maxSize)
{
container<2>(std::forward<T>(obj), maxSize);
}
template<typename T>
void container4b(T &&obj, size_t maxSize) { container<4>(std::forward<T>(obj), maxSize); }
void container4b(T&& obj, size_t maxSize)
{
container<4>(std::forward<T>(obj), maxSize);
}
template<typename T>
void container8b(T &&obj, size_t maxSize) { container<8>(std::forward<T>(obj), maxSize); }
void container8b(T&& obj, size_t maxSize)
{
container<8>(std::forward<T>(obj), maxSize);
}
template<typename T>
void container16b(T &&obj, size_t maxSize) { container<16>(std::forward<T>(obj), maxSize); }
void container16b(T&& obj, size_t maxSize)
{
container<16>(std::forward<T>(obj), maxSize);
}
template<typename T>
void container1b(T &&obj) { container<1>(std::forward<T>(obj)); }
void container1b(T&& obj)
{
container<1>(std::forward<T>(obj));
}
template<typename T>
void container2b(T &&obj) { container<2>(std::forward<T>(obj)); }
void container2b(T&& obj)
{
container<2>(std::forward<T>(obj));
}
template<typename T>
void container4b(T &&obj) { container<4>(std::forward<T>(obj)); }
void container4b(T&& obj)
{
container<4>(std::forward<T>(obj));
}
template<typename T>
void container8b(T &&obj) { container<8>(std::forward<T>(obj)); }
void container8b(T&& obj)
{
container<8>(std::forward<T>(obj));
}
template<typename T>
void container16b(T &&obj) { container<16>(std::forward<T>(obj)); }
void container16b(T&& obj)
{
container<16>(std::forward<T>(obj));
}
private:
// process value types
// false_type means that we must process all elements individually
template<size_t VSIZE, typename It>
void procContainer(It first, It last, std::false_type) {
void procContainer(It first, It last, std::false_type)
{
for (; first != last; ++first)
value<VSIZE>(*first);
}
@@ -315,17 +455,20 @@ namespace bitsery {
// process value types
// true_type means, that we can copy whole buffer
template<size_t VSIZE, typename It>
void procContainer(It first, It last, std::true_type) {
void procContainer(It first, It last, std::true_type)
{
using TValue = typename std::decay<decltype(*first)>::type;
using TIntegral = typename details::IntegralFromFundamental<TValue>::TValue;
if (first != last)
this->_adapter.template writeBuffer<VSIZE>(reinterpret_cast<const TIntegral*>(&(*first)),
this->_adapter.template writeBuffer<VSIZE>(
reinterpret_cast<const TIntegral*>(&(*first)),
static_cast<size_t>(std::distance(first, last)));
}
// process by calling functions
template<typename It, typename Fnc>
void procContainer(It first, It last, Fnc fnc) {
void procContainer(It first, It last, Fnc fnc)
{
using TValue = typename std::decay<decltype(*first)>::type;
for (; first != last; ++first) {
fnc(*this, const_cast<TValue&>(*first));
@@ -334,76 +477,89 @@ namespace bitsery {
// process text,
template<size_t VSIZE, typename T>
void procText(const T& str, size_t maxSize) {
void procText(const T& str, size_t maxSize)
{
const size_t length = traits::TextTraits<T>::length(str);
(void)maxSize; // unused in release
assert((length + (traits::TextTraits<T>::addNUL ? 1u : 0u)) <= maxSize);
details::writeSize(this->_adapter, length);
auto begin = std::begin(str);
using diff_t = typename std::iterator_traits<decltype(begin)>::difference_type;
procContainer<VSIZE>(begin, std::next(begin, static_cast<diff_t>(length)), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
using diff_t =
typename std::iterator_traits<decltype(begin)>::difference_type;
procContainer<VSIZE>(
begin,
std::next(begin, static_cast<diff_t>(length)),
std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
}
// process object types
template<typename It>
void procContainer(It first, It last) {
void procContainer(It first, It last)
{
for (; first != last; ++first)
object(*first);
}
// proc bool writing bit or byte, depending on if BitPackingEnabled or not
void procBoolValue(bool v, std::true_type) {
void procBoolValue(bool v, std::true_type)
{
this->_adapter.writeBits(static_cast<unsigned char>(v ? 1 : 0), 1);
}
void procBoolValue(bool v, std::false_type) {
this->_adapter.template writeBytes<1>(static_cast<unsigned char>(v ? 1 : 0));
void procBoolValue(bool v, std::false_type)
{
this->_adapter.template writeBytes<1>(
static_cast<unsigned char>(v ? 1 : 0));
}
// enable bit-packing or do nothing if it is already enabled
template<typename Fnc, typename HasContext>
void procEnableBitPacking(const Fnc& fnc, std::true_type, HasContext) {
void procEnableBitPacking(const Fnc& fnc, std::true_type, HasContext)
{
fnc(*this);
}
template<typename Fnc>
void procEnableBitPacking(const Fnc& fnc, std::false_type, std::true_type) {
void procEnableBitPacking(const Fnc& fnc, std::false_type, std::true_type)
{
BPEnabledType ser{ this->_context, this->_adapter };
fnc(ser);
}
template<typename Fnc>
void procEnableBitPacking(const Fnc& fnc, std::false_type, std::false_type) {
void procEnableBitPacking(const Fnc& fnc, std::false_type, std::false_type)
{
BPEnabledType ser{ this->_adapter };
fnc(ser);
}
// these are dummy functions for extensions that have TValue = void
void object(const details::DummyType&) {
}
void object(const details::DummyType&) {}
template<size_t VSIZE>
void value(const details::DummyType&) {
void value(const details::DummyType&)
{
}
template<typename T, typename... TArgs>
void archive(T &&head, TArgs &&... tail) {
void archive(T&& head, TArgs&&... tail)
{
// serialize object
details::BriefSyntaxFunction<Serializer, T>::invoke(*this, std::forward<T>(head));
details::BriefSyntaxFunction<Serializer, T>::invoke(*this,
std::forward<T>(head));
// expand other elements
archive(std::forward<TArgs>(tail)...);
}
// dummy function, that stops archive variadic arguments expansion
void archive() {
}
void archive() {}
};
//helper function that set ups all the basic steps and after serialziation returns serialized bytes count
// helper function that set ups all the basic steps and after serialziation
// returns serialized bytes count
template<typename OutputAdapter, typename T>
size_t quickSerialization(OutputAdapter adapter, const T& value) {
size_t
quickSerialization(OutputAdapter adapter, const T& value)
{
Serializer<OutputAdapter> ser{ std::move(adapter) };
ser.object(value);
ser.adapter().flush();
@@ -411,7 +567,9 @@ namespace bitsery {
}
template<typename Context, typename OutputAdapter, typename T>
size_t quickSerialization(Context& ctx, OutputAdapter adapter, const T& value) {
size_t
quickSerialization(Context& ctx, OutputAdapter adapter, const T& value)
{
Serializer<OutputAdapter, Context> ser{ ctx, std::move(adapter) };
ser.object(value);
ser.adapter().flush();

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,7 +20,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_TRAITS_STD_ARRAY_H
#define BITSERY_TRAITS_STD_ARRAY_H
@@ -32,11 +31,15 @@ namespace bitsery {
namespace traits {
template<typename T, size_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>
struct BufferAdapterTraits<std::array<T, N>>
:public StdContainerForBufferAdapter<std::array<T, N>> {};
: public StdContainerForBufferAdapter<std::array<T, N>>
{
};
}
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -23,8 +23,8 @@
#ifndef BITSERY_TRAITS_CORE_STD_DEFAULTS_H
#define BITSERY_TRAITS_CORE_STD_DEFAULTS_H
#include "traits.h"
#include "../../details/serialization_common.h"
#include "traits.h"
namespace bitsery {
namespace traits {
@@ -34,47 +34,51 @@ namespace bitsery {
*/
template<typename T, bool Resizable, bool Contiguous>
struct StdContainer {
struct StdContainer
{
using TValue = typename T::value_type;
static constexpr bool isResizable = Resizable;
static constexpr bool isContiguous = Contiguous;
static size_t size(const T& container) {
return container.size();
}
static size_t size(const T& container) { return container.size(); }
};
// specialization for resizable
template<typename T, bool Contiguous>
struct StdContainer<T, true, Contiguous> {
struct StdContainer<T, true, Contiguous>
{
using TValue = typename T::value_type;
static constexpr bool isResizable = true;
static constexpr bool isContiguous = Contiguous;
static size_t size(const T& container) {
return container.size();
}
static void resize(T& container, size_t size) {
static size_t size(const T& container) { return container.size(); }
static void resize(T& container, size_t size)
{
resizeImpl(container, size, std::is_default_constructible<TValue>{});
}
private:
using diff_t = typename T::difference_type;
static void resizeImpl(T& container, size_t size, std::true_type) {
static void resizeImpl(T& container, size_t size, std::true_type)
{
container.resize(size);
}
static void resizeImpl(T& container, size_t newSize, std::false_type) {
static void resizeImpl(T& container, size_t newSize, std::false_type)
{
const auto oldSize = size(container);
for (auto it = oldSize; it < newSize; ++it) {
container.push_back(::bitsery::Access::create<TValue>());
}
if (oldSize > newSize) {
container.erase(std::next(std::begin(container), static_cast<diff_t>(newSize)), std::end(container));
container.erase(
std::next(std::begin(container), static_cast<diff_t>(newSize)),
std::end(container));
}
}
};
template<typename T, bool Resizable = ContainerTraits<T>::isResizable>
struct StdContainerForBufferAdapter {
struct StdContainerForBufferAdapter
{
using TIterator = typename T::iterator;
using TConstIterator = typename T::const_iterator;
using TValue = typename ContainerTraits<T>::TValue;
@@ -82,19 +86,25 @@ namespace bitsery {
// specialization for resizable buffers
template<typename T>
struct StdContainerForBufferAdapter<T, true> {
struct StdContainerForBufferAdapter<T, true>
{
using TIterator = typename T::iterator;
using TConstIterator = typename T::const_iterator;
using TValue = typename ContainerTraits<T>::TValue;
static void increaseBufferSize(T& container, size_t /*currSize*/, size_t minSize) {
//since we're writing to buffer use different resize strategy than default implementation
//when small size grow faster, to avoid thouse 2/4/8/16... byte allocations
auto newSize = static_cast<size_t>(static_cast<double>(container.size()) * 1.5) + 128;
static void increaseBufferSize(T& container,
size_t /*currSize*/,
size_t minSize)
{
// since we're writing to buffer use different resize strategy than default
// implementation when small size grow faster, to avoid thouse 2/4/8/16...
// byte allocations
auto newSize =
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()));
container.resize(
(std::max)(newSize > minSize ? newSize : minSize, container.capacity()));
}
};

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -23,8 +23,8 @@
#ifndef BITSERY_TRAITS_CORE_TRAITS_H
#define BITSERY_TRAITS_CORE_TRAITS_H
#include <type_traits>
#include "../../details/not_defined_type.h"
#include <type_traits>
namespace bitsery {
namespace traits {
@@ -35,11 +35,13 @@ namespace bitsery {
// traits for extension
template<typename Extension, typename T>
struct ExtensionTraits {
struct ExtensionTraits
{
// this type is used, when using extesion without custom lambda
// eg.: extension4b>(obj, myextension{}) will call s.value4b(obj) for TValue
// or extesion(obj, myextension{}) will call s.object(obj) for TValue
//when this is void, it will compile, but value and object overloads will do nothing.
// when this is void, it will compile, but value and object overloads will do
// nothing.
using TValue = details::NotDefinedType;
// does extension support ext<N>(...) syntax, by calling value<N> with TValue
@@ -52,47 +54,53 @@ namespace bitsery {
// primary traits for containers
template<typename T>
struct ContainerTraits {
struct ContainerTraits
{
using TValue = details::NotDefinedType;
static constexpr bool isResizable = false;
//contiguous arrays has oppurtunity to memcpy whole buffer directly when using funtamental types
//contiguous doesn't nesessary equal to random access iterator.
//contiguous hopefully will be available in c++20
// contiguous arrays has oppurtunity to memcpy whole buffer directly when
// using funtamental types contiguous doesn't nesessary equal to random access
// iterator. contiguous hopefully will be available in c++20
static constexpr bool isContiguous = false;
// resize function, called only if container is resizable
static void resize(T& , size_t ) {
static void resize(T&, size_t)
{
static_assert(std::is_void<T>::value,
"Define ContainerTraits or include from <bitsery/traits/...> to use as container");
"Define ContainerTraits or include from <bitsery/traits/...> "
"to use as container");
}
// get container size
static size_t size(const T& ) {
static size_t size(const T&)
{
static_assert(std::is_void<T>::value,
"Define ContainerTraits or include from <bitsery/traits/...> to use as container");
"Define ContainerTraits or include from <bitsery/traits/...> "
"to use as container");
return 0u;
}
};
// specialization for C style array
template<typename T, size_t N>
struct ContainerTraits<T[N]> {
struct ContainerTraits<T[N]>
{
using TValue = T;
static constexpr bool isResizable = false;
static constexpr bool isContiguous = true;
static size_t size(const T (&)[N]) {
return N;
}
static size_t size(const T (&)[N]) { return N; }
};
// specialization for initializer list.
// only serializer can use it
template<typename T>
struct ContainerTraits<std::initializer_list<T>> {
struct ContainerTraits<std::initializer_list<T>>
{
using TValue = T;
static constexpr bool isResizable = false;
static constexpr bool isContiguous = true;
static size_t size(const std::initializer_list<T>& container) {
static size_t size(const std::initializer_list<T>& container)
{
return container.size();
}
};
@@ -100,39 +108,46 @@ namespace bitsery {
// specialization for pointer type buffer
// only deserializer can use it
template<typename T>
struct ContainerTraits<const T*> {
struct ContainerTraits<const T*>
{
using TValue = T;
static constexpr bool isResizable = false;
static constexpr bool isContiguous = true;
static size_t size(const T* ) {
static_assert(std::is_void<T>::value, "cannot get size for container of type T*");
static size_t size(const T*)
{
static_assert(std::is_void<T>::value,
"cannot get size for container of type T*");
return 0u;
}
};
template<typename T>
struct ContainerTraits<T*> {
struct ContainerTraits<T*>
{
using TValue = T;
static constexpr bool isResizable = false;
static constexpr bool isContiguous = true;
static size_t size(const T* ) {
static_assert(std::is_void<T>::value, "cannot get size for container of type T*");
static size_t size(const T*)
{
static_assert(std::is_void<T>::value,
"cannot get size for container of type T*");
return 0u;
}
};
// traits for text, default adds null-terminated character at the end
template<typename T>
struct TextTraits {
struct TextTraits
{
using TValue = details::NotDefinedType;
// if container is not null-terminated by default, add NUL at the end
static constexpr bool addNUL = true;
// get length of null terminated container
static size_t length(const T& ) {
static_assert(std::is_void<T>::value,
static size_t length(const T&)
{
static_assert(
std::is_void<T>::value,
"Define TextTraits or include from <bitsery/traits/...> to use as text");
return 0u;
}
@@ -140,7 +155,8 @@ namespace bitsery {
// traits only for buffer adapters
template<typename T>
struct BufferAdapterTraits {
struct BufferAdapterTraits
{
using TIterator = details::NotDefinedType;
using TConstIterator = details::NotDefinedType;
using TValue = typename ContainerTraits<T>::TValue;
@@ -152,16 +168,18 @@ namespace bitsery {
// it is used to dramaticaly improve performance by updating buffer directly
// instead of using back_insert_iterator to append each byte to buffer.
static void increaseBufferSize(T& ,size_t , size_t ) {
static void increaseBufferSize(T&, size_t, size_t)
{
static_assert(std::is_void<T>::value,
"Define BufferAdapterTraits or include from <bitsery/traits/...> to use as buffer adapter container");
"Define BufferAdapterTraits or include from "
"<bitsery/traits/...> to use as buffer adapter container");
}
};
// specialization for c-style buffer
template<typename T, size_t N>
struct BufferAdapterTraits<T[N]> {
struct BufferAdapterTraits<T[N]>
{
using TIterator = T*;
using TConstIterator = const T*;
using TValue = T;
@@ -169,14 +187,16 @@ namespace bitsery {
// specialization for pointer type buffer
template<typename T>
struct BufferAdapterTraits<const T*> {
struct BufferAdapterTraits<const T*>
{
using TIterator = const T*;
using TConstIterator = const T*;
using TValue = T;
};
template<typename T>
struct BufferAdapterTraits<T*> {
struct BufferAdapterTraits<T*>
{
using TIterator = T*;
using TConstIterator = const T*;
using TValue = T;

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,7 +20,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_TRAITS_STD_DEQUE_H
#define BITSERY_TRAITS_STD_DEQUE_H
@@ -33,7 +32,9 @@ namespace bitsery {
template<typename T, typename 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

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,12 +20,11 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_TRAITS_STD_FORWARD_LIST_H
#define BITSERY_TRAITS_STD_FORWARD_LIST_H
#include "core/traits.h"
#include "../details/serialization_common.h"
#include "core/traits.h"
#include <forward_list>
namespace bitsery {
@@ -33,22 +32,33 @@ namespace bitsery {
namespace traits {
template<typename T, typename Allocator>
struct ContainerTraits<std::forward_list<T, Allocator>> {
struct ContainerTraits<std::forward_list<T, Allocator>>
{
using TValue = T;
static constexpr bool isResizable = true;
static constexpr bool isContiguous = false;
static size_t size(const std::forward_list<T, Allocator>& container) {
return static_cast<size_t>(std::distance(container.begin(), container.end()));
static size_t size(const std::forward_list<T, Allocator>& container)
{
return static_cast<size_t>(
std::distance(container.begin(), container.end()));
}
static void resize(std::forward_list<T, Allocator>& container, size_t size) {
static void resize(std::forward_list<T, Allocator>& container, size_t size)
{
resizeImpl(container, size, std::is_default_constructible<TValue>{});
}
private:
using diff_t = typename std::forward_list<T, Allocator>::difference_type;
static void resizeImpl(std::forward_list<T, Allocator>& container, size_t size, std::true_type) {
static void resizeImpl(std::forward_list<T, Allocator>& container,
size_t size,
std::true_type)
{
container.resize(size);
}
static void resizeImpl(std::forward_list<T, Allocator>& container, size_t newSize, std::false_type) {
static void resizeImpl(std::forward_list<T, Allocator>& container,
size_t newSize,
std::false_type)
{
const auto oldSize = size(container);
for (auto it = oldSize; it < newSize; ++it) {
container.push_front(::bitsery::Access::create<TValue>());
@@ -56,7 +66,8 @@ namespace bitsery {
if (oldSize > newSize) {
// erase_after must have atleast one element to work
if (newSize > 0)
container.erase_after(std::next(std::begin(container), static_cast<diff_t>(newSize-1)));
container.erase_after(
std::next(std::begin(container), static_cast<diff_t>(newSize - 1)));
else
container.clear();
}
@@ -65,5 +76,4 @@ namespace bitsery {
}
}
#endif // BITSERY_TRAITS_STD_FORWARD_LIST_H

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,7 +20,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_TRAITS_STD_LIST_H
#define BITSERY_TRAITS_STD_LIST_H
@@ -33,7 +32,9 @@ namespace bitsery {
template<typename T, typename 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

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,7 +20,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_TRAITS_STD_STRING_H
#define BITSERY_TRAITS_STD_STRING_H
@@ -31,38 +30,50 @@ namespace bitsery {
namespace traits {
// specialization for string, because string is already included for std::char_traits
// specialization for string, because string is already included for
// std::char_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> {};
: 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>> {
using TValue = typename ContainerTraits<std::basic_string<CharT, Traits, Allocator>>::TValue;
struct TextTraits<std::basic_string<CharT, Traits, Allocator>>
{
using TValue = typename ContainerTraits<
std::basic_string<CharT, Traits, Allocator>>::TValue;
// string is automatically null-terminated
static constexpr bool addNUL = false;
//is is not 100% accurate, but for performance reasons assume that string stores text, not binary data
static size_t length(const std::basic_string<CharT, Traits, Allocator>& str) {
// is is not 100% accurate, but for performance reasons assume that string
// stores text, not binary data
static size_t length(const std::basic_string<CharT, Traits, Allocator>& str)
{
return str.size();
}
};
// specialization for c-array
template<typename T, size_t N>
struct TextTraits<T[N]> {
struct TextTraits<T[N]>
{
using TValue = T;
static constexpr bool addNUL = true;
static size_t length(const T (&container)[N]) {
static size_t length(const T (&container)[N])
{
return std::char_traits<T>::length(container);
}
};
template<typename CharT, typename Traits, typename Allocator>
struct BufferAdapterTraits<std::basic_string<CharT, Traits, Allocator>>
:public StdContainerForBufferAdapter<std::basic_string<CharT, Traits, Allocator>> {};
: public StdContainerForBufferAdapter<
std::basic_string<CharT, Traits, Allocator>>
{
};
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,7 +20,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef BITSERY_TRAITS_STD_VECTOR_H
#define BITSERY_TRAITS_STD_VECTOR_H
@@ -32,16 +31,22 @@ namespace bitsery {
namespace traits {
template<typename T, typename 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
template<typename 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>
struct BufferAdapterTraits<std::vector<T, Allocator>>
:public StdContainerForBufferAdapter<std::vector<T, Allocator>> {};
: public StdContainerForBufferAdapter<std::vector<T, Allocator>>
{
};
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,16 +20,15 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <bitsery/adapter/buffer.h>
#include <bitsery/adapter/stream.h>
#include <bitsery/adapter/measure_size.h>
#include <bitsery/adapter/stream.h>
#include <bitsery/deserializer.h>
#include <bitsery/traits/vector.h>
#include <bitsery/traits/array.h>
#include <bitsery/traits/string.h>
#include <bitsery/ext/value_range.h>
#include <bitsery/serializer.h>
#include <bitsery/traits/array.h>
#include <bitsery/traits/string.h>
#include <bitsery/traits/vector.h>
#include <gmock/gmock.h>
@@ -43,13 +42,16 @@ using bitsery::ReaderError;
using testing::Eq;
using testing::Ge;
struct DisableAdapterErrorsConfig {
static constexpr bitsery::EndiannessType Endianness = bitsery::DefaultConfig::Endianness;
struct DisableAdapterErrorsConfig
{
static constexpr bitsery::EndiannessType Endianness =
bitsery::DefaultConfig::Endianness;
static constexpr bool CheckAdapterErrors = false;
static constexpr bool CheckDataErrors = true;
};
TEST(OutputBuffer, WhenSetWritePositionThenResizeUnderlyingBufferIfRequired) {
TEST(OutputBuffer, WhenSetWritePositionThenResizeUnderlyingBufferIfRequired)
{
// setup data
Buffer buf{};
OutputAdapter w{ buf };
@@ -61,7 +63,10 @@ TEST(OutputBuffer, WhenSetWritePositionThenResizeUnderlyingBufferIfRequired) {
EXPECT_THAT(buf.size(), Ge(initialSize + 10));
}
TEST(OutputBuffer, WhenSettingCurrentPositionBeforeBufferEndThenWrittenBytesCountIsNotAffected) {
TEST(
OutputBuffer,
WhenSettingCurrentPositionBeforeBufferEndThenWrittenBytesCountIsNotAffected)
{
// setup data
Buffer buf{};
OutputAdapter w{ buf };
@@ -75,7 +80,8 @@ TEST(OutputBuffer, WhenSettingCurrentPositionBeforeBufferEndThenWrittenBytesCoun
EXPECT_THAT(w.writtenBytesCount(), Eq(initialSize + 10 + 8));
}
TEST(OutputBuffer, CanWorkWithFixedSizeBuffer) {
TEST(OutputBuffer, CanWorkWithFixedSizeBuffer)
{
// setup data
std::array<uint8_t, 10> buf{};
bitsery::OutputBufferAdapter<std::array<uint8_t, 10>> w{ buf };
@@ -86,7 +92,8 @@ TEST(OutputBuffer, CanWorkWithFixedSizeBuffer) {
EXPECT_THAT(w.currentWritePos(), Eq(5));
}
TEST(InputBuffer, CorrectlySetsAndGetsCurrentReadPosition) {
TEST(InputBuffer, CorrectlySetsAndGetsCurrentReadPosition)
{
Buffer buf{};
buf.resize(100);
@@ -100,8 +107,8 @@ TEST(InputBuffer, CorrectlySetsAndGetsCurrentReadPosition) {
EXPECT_THAT(r.currentReadPos(), Eq(1));
}
TEST(InputBuffer, WhenSetReadPositionOutOfRangeThenDataOverflow) {
TEST(InputBuffer, WhenSetReadPositionOutOfRangeThenDataOverflow)
{
Buffer buf{};
buf.resize(100);
@@ -112,7 +119,8 @@ TEST(InputBuffer, WhenSetReadPositionOutOfRangeThenDataOverflow) {
EXPECT_THAT(r.error(), Eq(ReaderError::DataOverflow));
}
TEST(InputBuffer, WhenSetReadEndPositionOutOfRangeThenDataOverflow) {
TEST(InputBuffer, WhenSetReadEndPositionOutOfRangeThenDataOverflow)
{
Buffer buf{};
buf.resize(100);
InputAdapter r{ buf.begin(), 10 };
@@ -120,7 +128,8 @@ TEST(InputBuffer, WhenSetReadEndPositionOutOfRangeThenDataOverflow) {
EXPECT_THAT(r.error(), Eq(ReaderError::DataOverflow));
}
TEST(InputBuffer, WhenReadEndPositionIsNotSetThenReturnZeroAsBufferEndPosition) {
TEST(InputBuffer, WhenReadEndPositionIsNotSetThenReturnZeroAsBufferEndPosition)
{
Buffer buf{};
buf.resize(100);
InputAdapter r{ buf.begin(), 10 };
@@ -130,8 +139,9 @@ TEST(InputBuffer, WhenReadEndPositionIsNotSetThenReturnZeroAsBufferEndPosition)
EXPECT_THAT(r.error(), Eq(ReaderError::NoError));
}
TEST(InputBuffer, WhenReadEndPositionIsNotZeroThenDataOverflowErrorWillBeIgnored) {
TEST(InputBuffer,
WhenReadEndPositionIsNotZeroThenDataOverflowErrorWillBeIgnored)
{
Buffer buf{};
buf.resize(100);
InputAdapter r{ buf.begin(), 1 };
@@ -146,8 +156,9 @@ TEST(InputBuffer, WhenReadEndPositionIsNotZeroThenDataOverflowErrorWillBeIgnored
EXPECT_THAT(r.error(), Eq(ReaderError::DataOverflow));
}
TEST(InputBuffer, WhenReadingPastReadEndPositionOrBufferEndThenReadPositionDoesntChange) {
TEST(InputBuffer,
WhenReadingPastReadEndPositionOrBufferEndThenReadPositionDoesntChange)
{
Buffer buf{};
buf.resize(10);
InputAdapter r{ buf.begin(), 3 };
@@ -164,7 +175,10 @@ TEST(InputBuffer, WhenReadingPastReadEndPositionOrBufferEndThenReadPositionDoesn
EXPECT_THAT(tmp, Eq(0));
}
TEST(InputBuffer, WhenReaderHasErrorsThenSettingReadPosAndReadEndPosIsIgnoredAndGettingAlwaysReturnsZero) {
TEST(
InputBuffer,
WhenReaderHasErrorsThenSettingReadPosAndReadEndPosIsIgnoredAndGettingAlwaysReturnsZero)
{
Buffer buf{};
buf.resize(10);
InputAdapter r{ buf.begin(), 10 };
@@ -184,7 +198,8 @@ TEST(InputBuffer, WhenReaderHasErrorsThenSettingReadPosAndReadEndPosIsIgnoredAnd
EXPECT_THAT(r.currentReadEndPos(), Eq(0));
}
TEST(InputBuffer, ConstDataForBufferAllAdapters) {
TEST(InputBuffer, ConstDataForBufferAllAdapters)
{
// create and write to buffer
uint16_t data = 7549;
Buffer bufWrite{};
@@ -203,7 +218,9 @@ TEST(InputBuffer, ConstDataForBufferAllAdapters) {
}
#ifndef NDEBUG
TEST(InputBuffer, WhenAdapterErrorsIsDisabledThenCanChangeAnyReadPositionAndReadsAsserts) {
TEST(InputBuffer,
WhenAdapterErrorsIsDisabledThenCanChangeAnyReadPositionAndReadsAsserts)
{
// create and write to buffer
uint64_t data = 0x1122334455667788;
Buffer buf{};
@@ -211,7 +228,9 @@ TEST(InputBuffer, WhenAdapterErrorsIsDisabledThenCanChangeAnyReadPositionAndRead
bw.writeBytes<8>(data);
bw.flush();
bitsery::InputBufferAdapter<Buffer, DisableAdapterErrorsConfig> r1{buf.begin(), 2};
bitsery::InputBufferAdapter<Buffer, DisableAdapterErrorsConfig> r1{
buf.begin(), 2
};
uint16_t res1{};
r1.readBytes<2>(res1);
EXPECT_THAT(res1, Eq(0x7788)); // default config is little endian
@@ -222,7 +241,10 @@ TEST(InputBuffer, WhenAdapterErrorsIsDisabledThenCanChangeAnyReadPositionAndRead
}
#endif
TEST(InputStream, WhenAdapterErrorsIsDisabledThenReadingPastEndDoesntSetErrorAndDoesntReturnZero) {
TEST(
InputStream,
WhenAdapterErrorsIsDisabledThenReadingPastEndDoesntSetErrorAndDoesntReturnZero)
{
// create and write to buffer
std::stringstream ss{};
bitsery::OutputStreamAdapter bw{ ss };
@@ -230,7 +252,10 @@ TEST(InputStream, WhenAdapterErrorsIsDisabledThenReadingPastEndDoesntSetErrorAnd
bw.writeBytes<4>(data);
bw.flush();
bitsery::BasicInputStreamAdapter<char, DisableAdapterErrorsConfig, std::char_traits<char>> br{ss};
bitsery::BasicInputStreamAdapter<char,
DisableAdapterErrorsConfig,
std::char_traits<char>>
br{ ss };
uint32_t res{};
br.readBytes<4>(res);
EXPECT_THAT(res, Eq(data));
@@ -240,24 +265,28 @@ TEST(InputStream, WhenAdapterErrorsIsDisabledThenReadingPastEndDoesntSetErrorAnd
}
template<template<typename...> class TAdapter>
struct InBufferConfig {
struct InBufferConfig
{
using Data = std::vector<char>;
using Adapter = TAdapter<Data>;
Data data{};
Adapter createReader(const std::vector<char>& buffer) {
Adapter createReader(const std::vector<char>& buffer)
{
data = buffer;
return Adapter{ data.begin(), data.size() };
}
};
template<typename TAdapter>
struct InStreamConfig {
struct InStreamConfig
{
using Data = std::stringstream;
using Adapter = TAdapter;
Data data{};
Adapter createReader(const std::vector<char>& buffer) {
Adapter createReader(const std::vector<char>& buffer)
{
std::string str(buffer.begin(), buffer.end());
data = std::stringstream{ str };
return Adapter{ data };
@@ -265,25 +294,25 @@ struct InStreamConfig {
};
template<typename TAdapterWithData>
class AdapterConfig : public testing::Test {
class AdapterConfig : public testing::Test
{
public:
TAdapterWithData config{};
};
using AdapterInputTypes = ::testing::Types<
InBufferConfig<bitsery::InputBufferAdapter>,
InStreamConfig<bitsery::InputStreamAdapter>
>;
using AdapterInputTypes =
::testing::Types<InBufferConfig<bitsery::InputBufferAdapter>,
InStreamConfig<bitsery::InputStreamAdapter>>;
template<typename TConfig>
class InputAll: public AdapterConfig<TConfig> {
class InputAll : public AdapterConfig<TConfig>
{
};
TYPED_TEST_SUITE(InputAll, AdapterInputTypes, );
TYPED_TEST(InputAll, SettingMultipleErrorsAlwaysReturnsFirstError) {
TYPED_TEST(InputAll, SettingMultipleErrorsAlwaysReturnsFirstError)
{
auto r = this->config.createReader({ 0, 0, 0, 0 });
EXPECT_THAT(r.error(), Eq(ReaderError::NoError));
r.error(ReaderError::InvalidPointer);
@@ -294,7 +323,8 @@ TYPED_TEST(InputAll, SettingMultipleErrorsAlwaysReturnsFirstError) {
EXPECT_THAT(r.error(), Eq(ReaderError::InvalidPointer));
}
TYPED_TEST(InputAll, CanBeMoveConstructedAndMoveAssigned) {
TYPED_TEST(InputAll, CanBeMoveConstructedAndMoveAssigned)
{
auto r = this->config.createReader({ 1, 2, 3 });
uint8_t res{};
r.template readBytes<1>(res);
@@ -309,8 +339,8 @@ TYPED_TEST(InputAll, CanBeMoveConstructedAndMoveAssigned) {
EXPECT_THAT(res, Eq(3));
}
TYPED_TEST(InputAll, WhenAlignHasNonZerosThenInvalidDataError) {
TYPED_TEST(InputAll, WhenAlignHasNonZerosThenInvalidDataError)
{
auto r = this->config.createReader({ 0x7F });
bitsery::details::InputAdapterBitPackingWrapper<decltype(r)> bpr{ r };
@@ -321,8 +351,9 @@ TYPED_TEST(InputAll, WhenAlignHasNonZerosThenInvalidDataError) {
EXPECT_THAT(bpr.error(), Eq(ReaderError::InvalidData));
}
TYPED_TEST(InputAll, WhenAllBytesAreReadWithoutErrorsThenIsCompletedSuccessfully) {
TYPED_TEST(InputAll,
WhenAllBytesAreReadWithoutErrorsThenIsCompletedSuccessfully)
{
// setup data
uint32_t tb = 94545646;
@@ -359,8 +390,8 @@ TYPED_TEST(InputAll, WhenAllBytesAreReadWithoutErrorsThenIsCompletedSuccessfully
EXPECT_THAT(rd, Eq(td));
}
TYPED_TEST(InputAll, WhenReadingMoreThanAvailableThenDataOverflow) {
TYPED_TEST(InputAll, WhenReadingMoreThanAvailableThenDataOverflow)
{
// setup data
uint8_t t1 = 111;
@@ -384,10 +415,10 @@ TYPED_TEST(InputAll, WhenReadingMoreThanAvailableThenDataOverflow) {
EXPECT_THAT(r1, Eq(0));
EXPECT_THAT(r.isCompletedSuccessfully(), Eq(false));
EXPECT_THAT(r.error(), Eq(ReaderError::DataOverflow));
}
TYPED_TEST(InputAll, WhenReaderHasErrorsAllThenReadsReturnZero) {
TYPED_TEST(InputAll, WhenReaderHasErrorsAllThenReadsReturnZero)
{
// setup data
uint8_t t1 = 111;
@@ -408,50 +439,50 @@ TYPED_TEST(InputAll, WhenReaderHasErrorsAllThenReadsReturnZero) {
EXPECT_THAT(r1, Eq(0));
}
template<template<typename...> class TAdapter>
struct OutBufferConfig {
struct OutBufferConfig
{
using Data = std::vector<char>;
using Adapter = TAdapter<Data>;
Data data{};
Adapter createWriter() {
return Adapter{data};
}
Adapter createWriter() { return Adapter{ data }; }
bitsery::InputBufferAdapter<Data> getReader() {
bitsery::InputBufferAdapter<Data> getReader()
{
return bitsery::InputBufferAdapter<Data>{ data.begin(), data.end() };
}
};
template<typename TAdapter>
struct OutStreamConfig {
struct OutStreamConfig
{
using Data = std::stringstream;
using Adapter = TAdapter;
Data data{};
Adapter createWriter() {
return Adapter{data};
}
Adapter createWriter() { return Adapter{ data }; }
bitsery::InputStreamAdapter getReader() {
bitsery::InputStreamAdapter getReader()
{
return bitsery::InputStreamAdapter{ data };
}
};
using AdapterOutputTypes = ::testing::Types<
OutBufferConfig<bitsery::OutputBufferAdapter>,
using AdapterOutputTypes =
::testing::Types<OutBufferConfig<bitsery::OutputBufferAdapter>,
OutStreamConfig<bitsery::OutputStreamAdapter>,
OutStreamConfig<bitsery::OutputBufferedStreamAdapter>
>;
OutStreamConfig<bitsery::OutputBufferedStreamAdapter>>;
template<typename TConfig>
class OutputAll: public AdapterConfig<TConfig> {
class OutputAll : public AdapterConfig<TConfig>
{
};
TYPED_TEST_SUITE(OutputAll, AdapterOutputTypes, );
TYPED_TEST(OutputAll, CanBeMoveConstructedAndMoveAssigned) {
TYPED_TEST(OutputAll, CanBeMoveConstructedAndMoveAssigned)
{
auto w = this->config.createWriter();
uint8_t data{ 1 };
w.template writeBytes<1>(data);
@@ -474,12 +505,16 @@ TYPED_TEST(OutputAll, CanBeMoveConstructedAndMoveAssigned) {
EXPECT_THAT(data, Eq(3));
}
template<typename T>
class OutputStreamBuffered : public testing::Test {
class OutputStreamBuffered : public testing::Test
{
public:
using Buffer = T;
using Adapter = bitsery::BasicBufferedOutputStreamAdapter<char, bitsery::DefaultConfig, std::char_traits<char>, Buffer>;
using Adapter =
bitsery::BasicBufferedOutputStreamAdapter<char,
bitsery::DefaultConfig,
std::char_traits<char>,
Buffer>;
static constexpr size_t InternalBufferSize = 128;
@@ -488,24 +523,25 @@ public:
Adapter writer{ stream, 128 };
};
using BufferedAdapterInternalBufferTypes = ::testing::Types<
std::vector<char>,
std::array<char, 128>,
std::string
>;
using BufferedAdapterInternalBufferTypes =
::testing::Types<std::vector<char>, std::array<char, 128>, std::string>;
TYPED_TEST_SUITE(OutputStreamBuffered, BufferedAdapterInternalBufferTypes, );
TYPED_TEST(OutputStreamBuffered, WhenInternalBufferIsFullThenWriteBufferAndValueToStream) {
TYPED_TEST(OutputStreamBuffered,
WhenInternalBufferIsFullThenWriteBufferAndValueToStream)
{
uint8_t x{};
for (auto i = 0u; i < TestFixture::InternalBufferSize; ++i)
this->writer.template writeBytes<1>(x);
EXPECT_TRUE(this->stream.str().empty());
this->writer.template writeBytes<1>(x);
EXPECT_THAT(this->stream.str().size(), Eq(TestFixture::InternalBufferSize + 1));
EXPECT_THAT(this->stream.str().size(),
Eq(TestFixture::InternalBufferSize + 1));
}
TYPED_TEST(OutputStreamBuffered, WhenFlushThenWriteImmediately) {
TYPED_TEST(OutputStreamBuffered, WhenFlushThenWriteImmediately)
{
uint8_t x{};
this->writer.template writeBytes<1>(x);
EXPECT_THAT(this->stream.str().size(), Eq(0));
@@ -515,16 +551,21 @@ TYPED_TEST(OutputStreamBuffered, WhenFlushThenWriteImmediately) {
EXPECT_THAT(this->stream.str().size(), Eq(1));
}
TYPED_TEST(OutputStreamBuffered, WhenBufferIsStackAllocatedThenBufferSizeViaCtorHasNoEffect) {
TYPED_TEST(OutputStreamBuffered,
WhenBufferIsStackAllocatedThenBufferSizeViaCtorHasNoEffect)
{
// create writer with half the internal buffer size
//for std::vector it should overflow, and for std::array it should have no effect
typename TestFixture::Adapter w{this->stream, TestFixture::InternalBufferSize / 2};
// for std::vector it should overflow, and for std::array it should have no
// effect
typename TestFixture::Adapter w{ this->stream,
TestFixture::InternalBufferSize / 2 };
uint8_t x{};
for (auto i = 0u; i < TestFixture::InternalBufferSize; ++i)
w.template writeBytes<1>(x);
static constexpr bool ShouldWriteToStream = bitsery::traits::ContainerTraits<typename TestFixture::Buffer>::isResizable;
static constexpr bool ShouldWriteToStream =
bitsery::traits::ContainerTraits<typename TestFixture::Buffer>::isResizable;
EXPECT_THAT(this->stream.str().empty(), ::testing::Ne(ShouldWriteToStream));
}
@@ -535,20 +576,21 @@ struct TestData
};
template<typename S>
void serialize(S &s, TestData &o)
void
serialize(S& s, TestData& o)
{
s.value4b(o.b4);
s.enableBitPacking([&o](typename S::BPEnabledType& sbp) {
sbp.ext(o.b4, bitsery::ext::ValueRange<uint32_t>{ 0, 1023 }); // 10 bits
sbp.value4b(o.b4);
sbp.container(o.vb2, 10, [](typename S::BPEnabledType& sbp, uint16_t& data) {
sbp.container(
o.vb2, 10, [](typename S::BPEnabledType& sbp, uint16_t& data) {
sbp.ext(data, bitsery::ext::ValueRange<uint16_t>{ 0, 200 }); // 7 bits
});
});
s.container2b(o.vb2, 10);
}
TEST(AdapterWriterMeasureSize, CorrectlyMeasuresBytesAndBitsSize)
{
TestData data{ 456, { 45, 98, 189, 4 } };

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -40,20 +40,23 @@
#include <bitsery/brief_syntax/tuple.h>
#include <bitsery/brief_syntax/variant.h>
#elif defined(_MSC_VER)
#pragma message("C++17 and /Zc:__cplusplus option is required to enable std::tuple and std::variant brief syntax tests")
#pragma message( \
"C++17 and /Zc:__cplusplus option is required to enable std::tuple and std::variant brief syntax tests")
#else
#pragma message("C++17 is required to enable std::tuple and std::variant brief syntax tests")
#pragma message( \
"C++17 is required to enable std::tuple and std::variant brief syntax tests")
#endif
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
#include <atomic>
#include <utility>
using testing::Eq;
TEST(BriefSyntax, FundamentalTypesAndBool) {
TEST(BriefSyntax, FundamentalTypesAndBool)
{
int ti = 8745;
MyEnumClass te = MyEnumClass::E4;
float tf = 485.042f;
@@ -78,7 +81,8 @@ TEST(BriefSyntax, FundamentalTypesAndBool) {
EXPECT_THAT(rb, Eq(tb));
}
TEST(BriefSyntax, UseObjectFncInsteadOfValueN) {
TEST(BriefSyntax, UseObjectFncInsteadOfValueN)
{
int ti = 8745;
MyEnumClass te = MyEnumClass::E4;
float tf = 485.042f;
@@ -113,7 +117,8 @@ TEST(BriefSyntax, UseObjectFncInsteadOfValueN) {
EXPECT_THAT(rb, Eq(tb));
}
TEST(BriefSyntax, MixDifferentSyntax) {
TEST(BriefSyntax, MixDifferentSyntax)
{
int ti = 8745;
MyEnumClass te = MyEnumClass::E4;
float tf = 485.042f;
@@ -145,7 +150,9 @@ TEST(BriefSyntax, MixDifferentSyntax) {
}
template<typename T>
T procBriefSyntax(const T& testData) {
T
procBriefSyntax(const T& testData)
{
SerializationContext ctx;
ctx.createSerializer()(testData);
T res{};
@@ -154,7 +161,9 @@ T procBriefSyntax(const T& testData) {
}
template<typename T>
T&& procBriefSyntaxRvalue(T&& init_value, const T& testData) {
T&&
procBriefSyntaxRvalue(T&& init_value, const T& testData)
{
SerializationContext ctx;
ctx.createSerializer()(testData);
ctx.createDeserializer()(init_value);
@@ -162,7 +171,9 @@ T&& procBriefSyntaxRvalue(T&& init_value, const T& testData) {
}
template<typename T>
T procBriefSyntaxWithMaxSize(const T& testData) {
T
procBriefSyntaxWithMaxSize(const T& testData)
{
SerializationContext ctx;
ctx.createSerializer()(bitsery::maxSize(testData, 100));
T res{};
@@ -170,7 +181,8 @@ T procBriefSyntaxWithMaxSize(const T& testData) {
return res;
}
TEST(BriefSyntax, CStyleArrayForValueTypesAsContainer) {
TEST(BriefSyntax, CStyleArrayForValueTypesAsContainer)
{
const int t1[3]{ 8748, -484, 45 };
int r1[3]{ 0, 0, 0 };
@@ -181,7 +193,8 @@ TEST(BriefSyntax, CStyleArrayForValueTypesAsContainer) {
EXPECT_THAT(r1, ::testing::ContainerEq(t1));
}
TEST(BriefSyntax, CStyleArrayForIntegralTypesAsText) {
TEST(BriefSyntax, CStyleArrayForIntegralTypesAsText)
{
const char t1[3]{ "hi" };
char r1[3]{ 0, 0, 0 };
@@ -192,7 +205,8 @@ TEST(BriefSyntax, CStyleArrayForIntegralTypesAsText) {
EXPECT_THAT(r1, ::testing::ContainerEq(t1));
}
TEST(BriefSyntax, CStyleArray) {
TEST(BriefSyntax, CStyleArray)
{
const MyEnumClass t1[3]{ MyEnumClass::E1, MyEnumClass::E4, MyEnumClass::E2 };
MyEnumClass r1[3]{};
@@ -203,7 +217,8 @@ TEST(BriefSyntax, CStyleArray) {
EXPECT_THAT(r1, ::testing::ContainerEq(t1));
}
TEST(BriefSyntax, StdString) {
TEST(BriefSyntax, StdString)
{
std::string t1{ "my nice string" };
std::string t2{};
@@ -213,7 +228,8 @@ TEST(BriefSyntax, StdString) {
EXPECT_THAT(procBriefSyntaxWithMaxSize(t2), Eq(t2));
}
TEST(BriefSyntax, StdArray) {
TEST(BriefSyntax, StdArray)
{
std::array<int, 3> t1{ 8748, -484, 45 };
std::array<int, 0> t2{};
@@ -221,7 +237,8 @@ TEST(BriefSyntax, StdArray) {
EXPECT_THAT(procBriefSyntax(t2), Eq(t2));
}
TEST(BriefSyntax, StdVector) {
TEST(BriefSyntax, StdVector)
{
std::vector<int> t1{ 8748, -484, 45 };
std::vector<float> t2{ 5.f, 0.198f };
@@ -231,7 +248,8 @@ TEST(BriefSyntax, StdVector) {
EXPECT_THAT(procBriefSyntaxWithMaxSize(t2), Eq(t2));
}
TEST(BriefSyntax, StdList) {
TEST(BriefSyntax, StdList)
{
std::list<int> t1{ 8748, -484, 45 };
std::list<float> t2{ 5.f, 0.198f };
@@ -241,7 +259,8 @@ TEST(BriefSyntax, StdList) {
EXPECT_THAT(procBriefSyntaxWithMaxSize(t2), Eq(t2));
}
TEST(BriefSyntax, StdForwardList) {
TEST(BriefSyntax, StdForwardList)
{
std::forward_list<int> t1{ 8748, -484, 45 };
std::forward_list<float> t2{ 5.f, 0.198f };
@@ -251,7 +270,8 @@ TEST(BriefSyntax, StdForwardList) {
EXPECT_THAT(procBriefSyntaxWithMaxSize(t2), Eq(t2));
}
TEST(BriefSyntax, StdDeque) {
TEST(BriefSyntax, StdDeque)
{
std::deque<int> t1{ 8748, -484, 45 };
std::deque<float> t2{ 5.f, 0.198f };
@@ -261,7 +281,8 @@ TEST(BriefSyntax, StdDeque) {
EXPECT_THAT(procBriefSyntaxWithMaxSize(t2), Eq(t2));
}
TEST(BriefSyntax, StdQueue) {
TEST(BriefSyntax, StdQueue)
{
std::queue<std::string> t1;
t1.push("first");
t1.push("second string");
@@ -270,7 +291,8 @@ TEST(BriefSyntax, StdQueue) {
EXPECT_THAT(procBriefSyntaxWithMaxSize(t1), Eq(t1));
}
TEST(BriefSyntax, StdPriorityQueue) {
TEST(BriefSyntax, StdPriorityQueue)
{
std::priority_queue<std::string> t1;
t1.push("first");
t1.push("second string");
@@ -287,7 +309,8 @@ TEST(BriefSyntax, StdPriorityQueue) {
}
}
TEST(BriefSyntax, StdStack) {
TEST(BriefSyntax, StdStack)
{
std::stack<std::string> t1;
t1.push("first");
t1.push("second string");
@@ -296,7 +319,8 @@ TEST(BriefSyntax, StdStack) {
EXPECT_THAT(procBriefSyntaxWithMaxSize(t1), Eq(t1));
}
TEST(BriefSyntax, StdUnorderedMap) {
TEST(BriefSyntax, StdUnorderedMap)
{
std::unordered_map<int, int> t1;
t1.emplace(3423, 624);
t1.emplace(-5484, -845);
@@ -305,7 +329,8 @@ TEST(BriefSyntax, StdUnorderedMap) {
EXPECT_THAT(procBriefSyntaxWithMaxSize(t1), Eq(t1));
}
TEST(BriefSyntax, StdUnorderedMultiMap) {
TEST(BriefSyntax, StdUnorderedMultiMap)
{
std::unordered_multimap<std::string, int> t1;
t1.emplace("one", 624);
t1.emplace("two", -845);
@@ -315,7 +340,8 @@ TEST(BriefSyntax, StdUnorderedMultiMap) {
EXPECT_TRUE(procBriefSyntaxWithMaxSize(t1) == t1);
}
TEST(BriefSyntax, StdMap) {
TEST(BriefSyntax, StdMap)
{
std::map<int, int> t1;
t1.emplace(3423, 624);
t1.emplace(-5484, -845);
@@ -324,26 +350,29 @@ TEST(BriefSyntax, StdMap) {
EXPECT_THAT(procBriefSyntaxWithMaxSize(t1), Eq(t1));
}
TEST(BriefSyntax, StdMultiMap) {
TEST(BriefSyntax, StdMultiMap)
{
std::multimap<std::string, int> t1;
t1.emplace("one", 624);
t1.emplace("two", -845);
t1.emplace("one", 897);
auto res = procBriefSyntax(t1);
//same key values is not ordered, and operator == compares each element at same position
//so we need to compare our selves
// same key values is not ordered, and operator == compares each element at
// same position so we need to compare our selves
EXPECT_THAT(res.size(), Eq(3));
for (auto it = t1.begin(); it != t1.end();) {
const auto lr = t1.equal_range(it->first);
const auto rr = res.equal_range(it->first);
EXPECT_TRUE(std::distance(lr.first, lr.second) == std::distance(rr.first, rr.second));
EXPECT_TRUE(std::distance(lr.first, lr.second) ==
std::distance(rr.first, rr.second));
EXPECT_TRUE(std::is_permutation(lr.first, lr.second, rr.first));
it = lr.second;
}
}
TEST(BriefSyntax, StdUnorderedSet) {
TEST(BriefSyntax, StdUnorderedSet)
{
std::unordered_set<std::string> t1;
t1.emplace("one");
t1.emplace("two");
@@ -353,7 +382,8 @@ TEST(BriefSyntax, StdUnorderedSet) {
EXPECT_TRUE(procBriefSyntaxWithMaxSize(t1) == t1);
}
TEST(BriefSyntax, StdUnorderedMultiSet) {
TEST(BriefSyntax, StdUnorderedMultiSet)
{
std::unordered_multiset<std::string> t1;
t1.emplace("one");
t1.emplace("two");
@@ -364,7 +394,8 @@ TEST(BriefSyntax, StdUnorderedMultiSet) {
EXPECT_TRUE(procBriefSyntaxWithMaxSize(t1) == t1);
}
TEST(BriefSyntax, StdSet) {
TEST(BriefSyntax, StdSet)
{
std::set<std::string> t1;
t1.emplace("one");
t1.emplace("two");
@@ -374,7 +405,8 @@ TEST(BriefSyntax, StdSet) {
EXPECT_TRUE(procBriefSyntaxWithMaxSize(t1) == t1);
}
TEST(BriefSyntax, StdMultiSet) {
TEST(BriefSyntax, StdMultiSet)
{
std::multiset<std::string> t1;
t1.emplace("one");
t1.emplace("two");
@@ -386,7 +418,8 @@ TEST(BriefSyntax, StdMultiSet) {
EXPECT_TRUE(procBriefSyntaxWithMaxSize(t1) == t1);
}
TEST(BriefSyntax, StdSmartPtr) {
TEST(BriefSyntax, StdSmartPtr)
{
std::shared_ptr<int> dataShared1(new int{ 4 });
std::weak_ptr<int> dataWeak1(dataShared1);
std::unique_ptr<std::string> dataUnique1{ new std::string{ "hello world" } };
@@ -408,12 +441,14 @@ TEST(BriefSyntax, StdSmartPtr) {
EXPECT_THAT(*resUnique1, Eq(*dataUnique1));
}
TEST(BriefSyntax, StdDuration) {
TEST(BriefSyntax, StdDuration)
{
std::chrono::duration<int64_t, std::milli> t1{ 54654 };
EXPECT_TRUE(procBriefSyntax(t1) == t1);
}
TEST(BriefSyntax, StdTimePoint) {
TEST(BriefSyntax, StdTimePoint)
{
using Duration = std::chrono::duration<double, std::milli>;
using TP = std::chrono::time_point<std::chrono::system_clock, Duration>;
@@ -421,7 +456,8 @@ TEST(BriefSyntax, StdTimePoint) {
EXPECT_TRUE(procBriefSyntax(data) == data);
}
TEST(BriefSyntax, StdAtomic) {
TEST(BriefSyntax, StdAtomic)
{
std::atomic<int32_t> atm0{ 54654 };
EXPECT_TRUE(procBriefSyntaxRvalue(std::atomic<int32_t>{}, atm0) == atm0);
@@ -433,24 +469,31 @@ TEST(BriefSyntax, StdAtomic) {
std::atomic<uint16_t> atm3;
atm3.store(0x1337);
EXPECT_TRUE(procBriefSyntaxRvalue(std::atomic<uint16_t>{}, atm3).load() == 0x1337);
EXPECT_TRUE(procBriefSyntaxRvalue(std::atomic<uint16_t>{}, atm3).load() ==
0x1337);
}
#if __cplusplus > 201402L
TEST(BriefSyntax, StdTuple) {
std::tuple<int, std::string, std::vector<char>> t1{5,"hello hello", {'A','B','C'}};
TEST(BriefSyntax, StdTuple)
{
std::tuple<int, std::string, std::vector<char>> t1{ 5,
"hello hello",
{ 'A', 'B', 'C' } };
EXPECT_TRUE(procBriefSyntax(t1) == t1);
}
TEST(BriefSyntax, StdVariant) {
std::variant<float, std::string, std::chrono::milliseconds> t1{std::string("hello hello")};
TEST(BriefSyntax, StdVariant)
{
std::variant<float, std::string, std::chrono::milliseconds> t1{ std::string(
"hello hello") };
EXPECT_TRUE(procBriefSyntax(t1) == t1);
}
#endif
TEST(BriefSyntax, NestedTypes) {
TEST(BriefSyntax, NestedTypes)
{
std::unordered_map<std::string, std::vector<std::string>> t1;
t1.emplace("my key", std::vector<std::string>{ "very", "nice", "string" });
t1.emplace("other key", std::vector<std::string>{ "just a string" });

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,30 +20,34 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "serialization_test_utils.h"
#include <bitsery/deserializer.h>
#include <bitsery/ext/value_range.h>
#include <bitsery/serializer.h>
#include <bitsery/deserializer.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
using testing::Eq;
using testing::ContainerEq;
using bitsery::EndiannessType;
using bitsery::DefaultConfig;
using bitsery::EndiannessType;
using testing::ContainerEq;
using testing::Eq;
constexpr EndiannessType getInverseEndianness(EndiannessType e) {
return e == EndiannessType::LittleEndian
? EndiannessType::BigEndian
constexpr EndiannessType
getInverseEndianness(EndiannessType e)
{
return e == EndiannessType::LittleEndian ? EndiannessType::BigEndian
: EndiannessType::LittleEndian;
}
struct InverseEndiannessConfig {
static constexpr bitsery::EndiannessType Endianness = getInverseEndianness(DefaultConfig::Endianness);
struct InverseEndiannessConfig
{
static constexpr bitsery::EndiannessType Endianness =
getInverseEndianness(DefaultConfig::Endianness);
static constexpr bool CheckDataErrors = true;
static constexpr bool CheckAdapterErrors = true;
};
struct IntegralTypes {
struct IntegralTypes
{
int64_t a;
uint32_t b;
int16_t c;
@@ -51,10 +55,11 @@ struct IntegralTypes {
int8_t e;
};
using InverseReader = bitsery::InputBufferAdapter<Buffer, InverseEndiannessConfig>;
using InverseReader =
bitsery::InputBufferAdapter<Buffer, InverseEndiannessConfig>;
TEST(DataEndianness, WhenWriteBytesThenBytesAreSwapped) {
TEST(DataEndianness, WhenWriteBytesThenBytesAreSwapped)
{
// fill initial values
IntegralTypes src{};
src.a = static_cast<int64_t>(0x1122334455667788u);
@@ -96,7 +101,8 @@ TEST(DataEndianness, WhenWriteBytesThenBytesAreSwapped) {
EXPECT_THAT(res.e, Eq(resInv.e));
}
TEST(DataEndianness, WhenWrite1ByteValuesThenEndiannessIsIgnored) {
TEST(DataEndianness, WhenWrite1ByteValuesThenEndiannessIsIgnored)
{
// fill initial values
constexpr size_t SIZE = 4;
uint8_t src[SIZE] = { 0xAA, 0xBB, 0xCC, 0xDD };
@@ -109,12 +115,13 @@ TEST(DataEndianness, WhenWrite1ByteValuesThenEndiannessIsIgnored) {
// read from buffer using inverse endianness config
InverseReader br{ buf.begin(), bw.writtenBytesCount() };
br.readBuffer<1>(res, SIZE);
//result is identical, because we write separate values, of size 1byte, that requires no swapping
//check results
// result is identical, because we write separate values, of size 1byte, that
// requires no swapping check results
EXPECT_THAT(res, ContainerEq(src));
}
TEST(DataEndianness, WhenWriteMoreThan1ByteValuesThenValuesAreSwapped) {
TEST(DataEndianness, WhenWriteMoreThan1ByteValuesThenValuesAreSwapped)
{
// fill initial values
constexpr size_t SIZE = 4;
uint16_t src[SIZE] = { 0xAA00, 0xBB11, 0xCC22, 0xDD33 };
@@ -128,25 +135,29 @@ TEST(DataEndianness, WhenWriteMoreThan1ByteValuesThenValuesAreSwapped) {
// read from buffer using inverse endianness config
InverseReader br{ buf.begin(), bw.writtenBytesCount() };
br.readBuffer<2>(res, SIZE);
//result is identical, because we write separate values, of size 1byte, that requires no swapping
//check results
// result is identical, because we write separate values, of size 1byte, that
// requires no swapping check results
EXPECT_THAT(res, ContainerEq(resInv));
}
template<typename T>
constexpr size_t getBits(T v) {
constexpr size_t
getBits(T v)
{
return bitsery::details::calcRequiredBits<T>({}, v);
}
struct IntegralUnsignedTypes {
struct IntegralUnsignedTypes
{
uint64_t a;
uint32_t b;
uint16_t c;
uint8_t d;
};
TEST(DataEndianness, WhenValueTypeIs1ByteThenBitOperationsIsNotAffectedByEndianness) {
TEST(DataEndianness,
WhenValueTypeIs1ByteThenBitOperationsIsNotAffectedByEndianness)
{
// fill initial values
constexpr IntegralUnsignedTypes src{
0x0000334455667788,

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,22 +20,22 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "serialization_test_utils.h"
#include <bitsery/deserializer.h>
#include <bitsery/ext/value_range.h>
#include <bitsery/serializer.h>
#include <bitsery/deserializer.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using testing::Eq;
using testing::ContainerEq;
using testing::Eq;
using AdapterBitPackingWriter = bitsery::details::OutputAdapterBitPackingWrapper<Writer>;
using AdapterBitPackingReader = bitsery::details::InputAdapterBitPackingWrapper<Reader>;
using AdapterBitPackingWriter =
bitsery::details::OutputAdapterBitPackingWrapper<Writer>;
using AdapterBitPackingReader =
bitsery::details::InputAdapterBitPackingWrapper<Reader>;
struct IntegralUnsignedTypes {
struct IntegralUnsignedTypes
{
uint32_t a;
uint16_t b;
uint8_t c;
@@ -44,13 +44,16 @@ struct IntegralUnsignedTypes {
};
template<typename T>
constexpr size_t getBits(T v) {
constexpr size_t
getBits(T v)
{
return bitsery::details::calcRequiredBits<T>({}, v);
}
// *** bits operations
TEST(DataBitsAndBytesOperations, WriteAndReadBitsMaxTypeValues) {
TEST(DataBitsAndBytesOperations, WriteAndReadBitsMaxTypeValues)
{
Buffer buf;
Writer bw{ buf };
AdapterBitPackingWriter bpw{ bw };
@@ -77,7 +80,8 @@ TEST(DataBitsAndBytesOperations, WriteAndReadBitsMaxTypeValues) {
EXPECT_THAT(v8, Eq(std::numeric_limits<uint8_t>::max()));
}
TEST(DataBitsAndBytesOperations, WriteAndReadBits) {
TEST(DataBitsAndBytesOperations, WriteAndReadBits)
{
// setup data
constexpr IntegralUnsignedTypes data{
485454, // bits 19
@@ -124,10 +128,10 @@ TEST(DataBitsAndBytesOperations, WriteAndReadBits) {
EXPECT_THAT(res.c, Eq(data.c));
EXPECT_THAT(res.d, Eq(data.d));
EXPECT_THAT(res.e, Eq(data.e));
}
TEST(DataBitsAndBytesOperations, WrittenSizeIsCountedPerByteNotPerBit) {
TEST(DataBitsAndBytesOperations, WrittenSizeIsCountedPerByteNotPerBit)
{
// setup data
// create and write to buffer
@@ -166,7 +170,8 @@ TEST(DataBitsAndBytesOperations, WrittenSizeIsCountedPerByteNotPerBit) {
EXPECT_THAT(bpr2.error(), Eq(bitsery::ReaderError::DataOverflow)); // false
}
TEST(DataBitsAndBytesOperations, ConsecutiveCallsToAlignHasNoEffect) {
TEST(DataBitsAndBytesOperations, ConsecutiveCallsToAlignHasNoEffect)
{
Buffer buf;
Writer bw{ buf };
AdapterBitPackingWriter bpw{ bw };
@@ -201,7 +206,8 @@ TEST(DataBitsAndBytesOperations, ConsecutiveCallsToAlignHasNoEffect) {
EXPECT_THAT(bpr.error(), Eq(bitsery::ReaderError::NoError));
}
TEST(DataBitsAndBytesOperations, AlignWritesZerosBits) {
TEST(DataBitsAndBytesOperations, AlignWritesZerosBits)
{
// setup data
// create and write to buffer
@@ -231,10 +237,10 @@ TEST(DataBitsAndBytesOperations, AlignWritesZerosBits) {
EXPECT_THAT(bpr2.error(), Eq(bitsery::ReaderError::NoError));
}
// *** bytes operations
struct IntegralTypes {
struct IntegralTypes
{
int64_t a;
uint32_t b;
int16_t c;
@@ -243,7 +249,8 @@ struct IntegralTypes {
int8_t f[2];
};
TEST(DataBitsAndBytesOperations, WriteAndReadBytes) {
TEST(DataBitsAndBytesOperations, WriteAndReadBytes)
{
// setup data
IntegralTypes data;
data.a = -4894541654564;
@@ -285,10 +292,10 @@ TEST(DataBitsAndBytesOperations, WriteAndReadBytes) {
EXPECT_THAT(data.d, Eq(res.d));
EXPECT_THAT(data.e, Eq(res.e));
EXPECT_THAT(data.f, ContainerEq(res.f));
}
TEST(DataBitsAndBytesOperations, WriteAndReadBytesWithBitPackingWrapper) {
TEST(DataBitsAndBytesOperations, WriteAndReadBytesWithBitPackingWrapper)
{
// setup data
IntegralTypes data;
data.a = -4894541654564;
@@ -332,10 +339,10 @@ TEST(DataBitsAndBytesOperations, WriteAndReadBytesWithBitPackingWrapper) {
EXPECT_THAT(data.d, Eq(res.d));
EXPECT_THAT(data.e, Eq(res.e));
EXPECT_THAT(data.f, ContainerEq(res.f));
}
TEST(DataBitsAndBytesOperations, ReadWriteFncCanAcceptSignedData) {
TEST(DataBitsAndBytesOperations, ReadWriteFncCanAcceptSignedData)
{
// setup data
constexpr size_t DATA_SIZE = 3;
int16_t src[DATA_SIZE]{ 54, -4877, 30067 };
@@ -352,7 +359,8 @@ TEST(DataBitsAndBytesOperations, ReadWriteFncCanAcceptSignedData) {
EXPECT_THAT(dst, ContainerEq(src));
}
TEST(DataBitsAndBytesOperations, ReadWriteCanWorkOnUnalignedData) {
TEST(DataBitsAndBytesOperations, ReadWriteCanWorkOnUnalignedData)
{
// setup data
constexpr size_t DATA_SIZE = 3;
int16_t src[DATA_SIZE]{ 54, -4877, 30067 };
@@ -381,7 +389,9 @@ TEST(DataBitsAndBytesOperations, ReadWriteCanWorkOnUnalignedData) {
EXPECT_THAT(tmp, Eq(12));
}
TEST(DataBitsAndBytesOperations, RegressionTestReadBytesAfterReadBitsWithLotsOfZeroBits) {
TEST(DataBitsAndBytesOperations,
RegressionTestReadBytesAfterReadBitsWithLotsOfZeroBits)
{
// setup data
int16_t data[2]{ 0x0000, 0x7FFF };
int16_t res[2]{};
@@ -407,4 +417,3 @@ TEST(DataBitsAndBytesOperations, RegressionTestReadBytesAfterReadBitsWithLotsOfZ
EXPECT_THAT(res[0], Eq(data[0]));
EXPECT_THAT(res[1], Eq(data[1]));
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -23,15 +23,16 @@
#include <bitsery/details/serialization_common.h>
#include <bitsery/traits/array.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
using testing::Eq;
using testing::ContainerEq;
using bitsery::EndiannessType;
using testing::ContainerEq;
using testing::Eq;
template<typename BufType>
class DataWriting:public testing::Test {
class DataWriting : public testing::Test
{
public:
using TWriter = bitsery::OutputBufferAdapter<BufType>;
using TBuffer = BufType;
@@ -47,7 +48,9 @@ TYPED_TEST_SUITE(DataWriting, ContainerTypes,);
static constexpr size_t DATA_SIZE = 14u;
template<typename BW>
void writeData(BW& bw) {
void
writeData(BW& bw)
{
uint16_t tmp1{ 45 }, tmp2{ 6543 }, tmp3{ 46533 };
uint32_t tmp4{ 8979445 }, tmp5{ 7987564 };
bw.template writeBytes<2>(tmp1);
@@ -57,7 +60,8 @@ void writeData(BW& bw) {
bw.template writeBytes<4>(tmp5);
}
TYPED_TEST(DataWriting, GetWrittenBytesCountReturnsActualBytesWritten) {
TYPED_TEST(DataWriting, GetWrittenBytesCountReturnsActualBytesWritten)
{
using TWriter = typename TestFixture::TWriter;
using TBuffer = typename TestFixture::TBuffer;
TBuffer buf{};
@@ -69,7 +73,8 @@ TYPED_TEST(DataWriting, GetWrittenBytesCountReturnsActualBytesWritten) {
EXPECT_THAT(buf.size(), ::testing::Ge(DATA_SIZE));
}
TYPED_TEST(DataWriting, WhenWritingBitsThenMustFlushWriter) {
TYPED_TEST(DataWriting, WhenWritingBitsThenMustFlushWriter)
{
using TWriter = typename TestFixture::TWriter;
using TBuffer = typename TestFixture::TBuffer;
TBuffer buf{};
@@ -83,7 +88,8 @@ TYPED_TEST(DataWriting, WhenWritingBitsThenMustFlushWriter) {
EXPECT_THAT(writtenSize2, Eq(1));
}
TYPED_TEST(DataWriting, WhenDataAlignedThenFlushHasNoEffect) {
TYPED_TEST(DataWriting, WhenDataAlignedThenFlushHasNoEffect)
{
using TWriter = typename TestFixture::TWriter;
using TBuffer = typename TestFixture::TBuffer;
TBuffer buf{};
@@ -96,10 +102,10 @@ TYPED_TEST(DataWriting, WhenDataAlignedThenFlushHasNoEffect) {
auto writtenSize2 = bpw.writtenBytesCount();
EXPECT_THAT(writtenSize1, Eq(1));
EXPECT_THAT(writtenSize2, Eq(1));
}
TEST(DataWritingNonFixedBufferContainer, ContainerIsAlwaysResizedToCapacity) {
TEST(DataWritingNonFixedBufferContainer, ContainerIsAlwaysResizedToCapacity)
{
NonFixedContainer buf{};
bitsery::OutputBufferAdapter<NonFixedContainer> bw{ buf };
for (auto i = 0; i < 5; ++i) {

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,16 +20,15 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <forward_list>
#include <bitsery/traits/forward_list.h>
#include <bitsery/ext/std_set.h>
#include <bitsery/ext/std_map.h>
#include <bitsery/ext/pointer.h>
#include <bitsery/ext/std_map.h>
#include <bitsery/ext/std_set.h>
#include <bitsery/ext/std_smart_ptr.h>
#include <bitsery/traits/forward_list.h>
#include <forward_list>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
using testing::ContainerEq;
using testing::Eq;
@@ -37,45 +36,48 @@ using testing::Eq;
// forward declare, for testing with std::unordered_map
class HasherForNonDefaultConstructible;
class NonDefaultConstructible {
class NonDefaultConstructible
{
int32_t i{ 0 };
friend class HasherForNonDefaultConstructible;
friend class bitsery::Access;
NonDefaultConstructible() = default;
template<typename S>
void serialize(S& s) {
void serialize(S& s)
{
s.value4b(i);
}
public:
explicit NonDefaultConstructible(int32_t v)
: i{ v }
{
}
explicit NonDefaultConstructible(int32_t v):i{v} {}
bool operator == (const NonDefaultConstructible& other) const {
bool operator==(const NonDefaultConstructible& other) const
{
return i == other.i;
}
bool operator < (const NonDefaultConstructible& other) const {
bool operator<(const NonDefaultConstructible& other) const
{
return i < other.i;
}
};
class HasherForNonDefaultConstructible {
class HasherForNonDefaultConstructible
{
public:
size_t operator()(const NonDefaultConstructible& o) const {
size_t operator()(const NonDefaultConstructible& o) const
{
return std::hash<int32_t>()(o.i);
}
};
TEST(DeserializeNonDefaultConstructible, Container) {
TEST(DeserializeNonDefaultConstructible, Container)
{
SerializationContext ctx{};
std::vector<NonDefaultConstructible> data{};
data.emplace_back(1);
@@ -88,8 +90,10 @@ TEST(DeserializeNonDefaultConstructible, Container) {
EXPECT_THAT(res, ContainerEq(data));
}
//this test is here, because when object is not constructible we cannot simple "resize" container
TEST(DeserializeNonDefaultConstructible, ResultContainerShouldShrink) {
// this test is here, because when object is not constructible we cannot simple
// "resize" container
TEST(DeserializeNonDefaultConstructible, ResultContainerShouldShrink)
{
SerializationContext ctx{};
std::vector<NonDefaultConstructible> data{};
data.emplace_back(1);
@@ -104,9 +108,11 @@ TEST(DeserializeNonDefaultConstructible, ResultContainerShouldShrink) {
EXPECT_THAT(res, ContainerEq(data));
}
TEST(DeserializeNonDefaultConstructible, ResultStdForwardListShouldShrink) {
TEST(DeserializeNonDefaultConstructible, ResultStdForwardListShouldShrink)
{
// forward list doesn't have .erase function, bet has erase_after
// in this case, if new size is 0 it must call clear, so we need to check two cases
// in this case, if new size is 0 it must call clear, so we need to check two
// cases
{
// 1) when result should have more than 0 elements
SerializationContext ctx{};
@@ -124,7 +130,6 @@ TEST(DeserializeNonDefaultConstructible, ResultStdForwardListShouldShrink) {
EXPECT_THAT(*resIt, Eq(*it));
}
EXPECT_THAT(resIt, Eq(res.end()));
}
{
// 1) when result should have 0 elements
@@ -138,7 +143,6 @@ TEST(DeserializeNonDefaultConstructible, ResultStdForwardListShouldShrink) {
ctx.createDeserializer().container(res, 10);
EXPECT_THAT(res.begin(), Eq(res.end()));
}
{
@@ -160,7 +164,8 @@ TEST(DeserializeNonDefaultConstructible, ResultStdForwardListShouldShrink) {
}
}
TEST(DeserializeNonDefaultConstructible, StdSet) {
TEST(DeserializeNonDefaultConstructible, StdSet)
{
SerializationContext ctx{};
std::set<NonDefaultConstructible> data;
data.insert(NonDefaultConstructible{ 1 });
@@ -174,22 +179,37 @@ TEST(DeserializeNonDefaultConstructible, StdSet) {
EXPECT_THAT(res, ContainerEq(data));
}
TEST(DeserializeNonDefaultConstructible, StdMap) {
TEST(DeserializeNonDefaultConstructible, StdMap)
{
SerializationContext ctx{};
std::unordered_map<NonDefaultConstructible, NonDefaultConstructible, HasherForNonDefaultConstructible> data;
std::unordered_map<NonDefaultConstructible,
NonDefaultConstructible,
HasherForNonDefaultConstructible>
data;
data.emplace(NonDefaultConstructible{ 2 }, NonDefaultConstructible{ 3 });
std::unordered_map<NonDefaultConstructible, NonDefaultConstructible, HasherForNonDefaultConstructible> res{};
std::unordered_map<NonDefaultConstructible,
NonDefaultConstructible,
HasherForNonDefaultConstructible>
res{};
data.emplace(NonDefaultConstructible{ 2 }, NonDefaultConstructible{ 3 });
data.emplace(NonDefaultConstructible{ 4 }, NonDefaultConstructible{ 4 });
auto& ser = ctx.createSerializer();
ser.ext(data, bitsery::ext::StdMap{10},[](decltype(ser)& ser, NonDefaultConstructible& key, NonDefaultConstructible& value) {
ser.ext(data,
bitsery::ext::StdMap{ 10 },
[](decltype(ser)& ser,
NonDefaultConstructible& key,
NonDefaultConstructible& value) {
ser.object(key);
ser.object(value);
});
auto& des = ctx.createDeserializer();
des.ext(res, bitsery::ext::StdMap{10},[](decltype(des)& des, NonDefaultConstructible& key, NonDefaultConstructible& value) {
des.ext(res,
bitsery::ext::StdMap{ 10 },
[](decltype(des)& des,
NonDefaultConstructible& key,
NonDefaultConstructible& value) {
des.object(key);
des.object(value);
});
@@ -197,8 +217,8 @@ TEST(DeserializeNonDefaultConstructible, StdMap) {
EXPECT_THAT(res, ContainerEq(data));
}
struct NonPolymorphicPointers {
struct NonPolymorphicPointers
{
NonDefaultConstructible* pp;
std::unique_ptr<NonDefaultConstructible> up;
std::shared_ptr<NonDefaultConstructible> sp;
@@ -206,20 +226,26 @@ struct NonPolymorphicPointers {
};
template<typename S>
void serialize(S& s, NonPolymorphicPointers& o) {
void
serialize(S& s, NonPolymorphicPointers& o)
{
s.ext(o.pp, bitsery::ext::PointerOwner{});
s.ext(o.up, bitsery::ext::StdSmartPtr{});
s.ext(o.sp, bitsery::ext::StdSmartPtr{});
s.ext(o.wp, bitsery::ext::StdSmartPtr{});
}
TEST(DeserializeNonDefaultConstructible, NonPolymorphicPointerAndSmartPointer) {
using SerContext = BasicSerializationContext<bitsery::ext::PointerLinkingContext>;
TEST(DeserializeNonDefaultConstructible, NonPolymorphicPointerAndSmartPointer)
{
using SerContext =
BasicSerializationContext<bitsery::ext::PointerLinkingContext>;
SerContext ctx{};
NonPolymorphicPointers data{};
data.pp = new NonDefaultConstructible{ 3 };
data.up = std::unique_ptr<NonDefaultConstructible>(new NonDefaultConstructible{54});
data.sp = std::shared_ptr<NonDefaultConstructible>(new NonDefaultConstructible{-481});
data.up =
std::unique_ptr<NonDefaultConstructible>(new NonDefaultConstructible{ 54 });
data.sp = std::shared_ptr<NonDefaultConstructible>(
new NonDefaultConstructible{ -481 });
data.wp = data.sp;
NonPolymorphicPointers res{};
@@ -235,62 +261,72 @@ TEST(DeserializeNonDefaultConstructible, NonPolymorphicPointerAndSmartPointer) {
EXPECT_THAT(*(res.wp.lock()), Eq(*(data.wp.lock())));
}
class PolymorphicNDCBase {
class PolymorphicNDCBase
{
public:
virtual ~PolymorphicNDCBase() = 0;
template<typename S>
void serialize(S& ) {}
void serialize(S&)
{
}
};
PolymorphicNDCBase::~PolymorphicNDCBase() = default;
class PolymorphicNDC1:public PolymorphicNDCBase {
class PolymorphicNDC1 : public PolymorphicNDCBase
{
int8_t i{};
friend class bitsery::Access;
template<typename S>
void serialize(S& s) {
void serialize(S& s)
{
s.value1b(i);
}
public:
PolymorphicNDC1() = default;
PolymorphicNDC1(int8_t v):i{v} {}
bool operator == (const PolymorphicNDC1& other) const {
return i == other.i;
PolymorphicNDC1(int8_t v)
: i{ v }
{
}
bool operator==(const PolymorphicNDC1& other) const { return i == other.i; }
};
class PolymorphicNDC2:public PolymorphicNDCBase {
class PolymorphicNDC2 : public PolymorphicNDCBase
{
uint16_t ui{};
friend class bitsery::Access;
template<typename S>
void serialize(S& s) {
void serialize(S& s)
{
s.value2b(ui);
}
public:
PolymorphicNDC2() = default;
PolymorphicNDC2(uint16_t v):ui{v} {}
bool operator == (const PolymorphicNDC2& other) const {
return ui == other.ui;
PolymorphicNDC2(uint16_t v)
: ui{ v }
{
}
bool operator==(const PolymorphicNDC2& other) const { return ui == other.ui; }
};
namespace bitsery {
namespace ext {
template<>
struct PolymorphicBaseClass<PolymorphicNDCBase> : PolymorphicDerivedClasses<PolymorphicNDC1, PolymorphicNDC2> {
struct PolymorphicBaseClass<PolymorphicNDCBase>
: PolymorphicDerivedClasses<PolymorphicNDC1, PolymorphicNDC2>
{
};
}
}
struct PolymorphicPointers {
struct PolymorphicPointers
{
PolymorphicNDCBase* pp;
std::unique_ptr<PolymorphicNDCBase> up;
std::shared_ptr<PolymorphicNDCBase> sp;
@@ -298,15 +334,20 @@ struct PolymorphicPointers {
};
template<typename S>
void serialize(S& s, PolymorphicPointers& o) {
void
serialize(S& s, PolymorphicPointers& o)
{
s.ext(o.pp, bitsery::ext::PointerOwner{});
s.ext(o.up, bitsery::ext::StdSmartPtr{});
s.ext(o.sp, bitsery::ext::StdSmartPtr{});
s.ext(o.wp, bitsery::ext::StdSmartPtr{});
}
TEST(DeserializeNonDefaultConstructible, PolymorphicPointerAndSmartPointer) {
using TContext = std::tuple<bitsery::ext::PointerLinkingContext, bitsery::ext::PolymorphicContext<bitsery::ext::StandardRTTI>>;
TEST(DeserializeNonDefaultConstructible, PolymorphicPointerAndSmartPointer)
{
using TContext =
std::tuple<bitsery::ext::PointerLinkingContext,
bitsery::ext::PolymorphicContext<bitsery::ext::StandardRTTI>>;
using SerContext = BasicSerializationContext<TContext>;
SerContext ctx{};
PolymorphicPointers data{};
@@ -320,8 +361,10 @@ TEST(DeserializeNonDefaultConstructible, PolymorphicPointerAndSmartPointer) {
TContext serCtx{};
TContext desCtx{};
std::get<1>(serCtx).registerBasesList<typename SerContext::TSerializer>(bitsery::ext::PolymorphicClassesList<PolymorphicNDCBase>{});
std::get<1>(desCtx).registerBasesList<typename SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<PolymorphicNDCBase>{});
std::get<1>(serCtx).registerBasesList<typename SerContext::TSerializer>(
bitsery::ext::PolymorphicClassesList<PolymorphicNDCBase>{});
std::get<1>(desCtx).registerBasesList<typename SerContext::TDeserializer>(
bitsery::ext::PolymorphicClassesList<PolymorphicNDCBase>{});
ctx.createSerializer(serCtx).object(data);
ctx.createDeserializer(desCtx).object(res);

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,13 +20,13 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
using testing::Eq;
TEST(Serialization, AdapterCanBeMovedInAndOut) {
TEST(Serialization, AdapterCanBeMovedInAndOut)
{
Buffer buf{};
bitsery::Serializer<Writer> ser1{ buf };
ser1.object(MyStruct1{ 1, 2 });

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,14 +20,13 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
using testing::Eq;
TEST(SerializeBooleans, BoolAsBit) {
TEST(SerializeBooleans, BoolAsBit)
{
SerializationContext ctx{};
bool t1{ true };
@@ -35,12 +34,14 @@ TEST(SerializeBooleans, BoolAsBit) {
bool res1;
bool res2;
auto& ser = ctx.createSerializer();
ser.enableBitPacking([&t1, &t2](SerializationContext::TSerializerBPEnabled& sbp) {
ser.enableBitPacking(
[&t1, &t2](SerializationContext::TSerializerBPEnabled& sbp) {
sbp.boolValue(t1);
sbp.boolValue(t2);
});
auto& des = ctx.createDeserializer();
des.enableBitPacking([&res1, &res2](SerializationContext::TDeserializerBPEnabled& sbp) {
des.enableBitPacking(
[&res1, &res2](SerializationContext::TDeserializerBPEnabled& sbp) {
sbp.boolValue(res1);
sbp.boolValue(res2);
});
@@ -50,7 +51,8 @@ TEST(SerializeBooleans, BoolAsBit) {
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
}
TEST(SerializeBooleans, BoolAsByte) {
TEST(SerializeBooleans, BoolAsByte)
{
SerializationContext ctx;
bool t1{ true };
bool t2{ false };
@@ -68,7 +70,9 @@ TEST(SerializeBooleans, BoolAsByte) {
EXPECT_THAT(ctx.getBufferSize(), Eq(2));
}
TEST(SerializeBooleans, WhenReadingBoolByteReadsMoreThanOneThenInvalidDataErrorAndResultIsFalse) {
TEST(SerializeBooleans,
WhenReadingBoolByteReadsMoreThanOneThenInvalidDataErrorAndResultIsFalse)
{
SerializationContext ctx;
auto& ser = ctx.createSerializer();
ser.value1b(uint8_t{ 1 });
@@ -79,5 +83,6 @@ TEST(SerializeBooleans, WhenReadingBoolByteReadsMoreThanOneThenInvalidDataErrorA
EXPECT_THAT(res, Eq(true));
des.boolValue(res);
EXPECT_THAT(res, Eq(false));
EXPECT_THAT(ctx.des->adapter().error(), Eq(bitsery::ReaderError::InvalidData));
EXPECT_THAT(ctx.des->adapter().error(),
Eq(bitsery::ReaderError::InvalidData));
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,54 +20,48 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <bitsery/traits/array.h>
#include <bitsery/traits/list.h>
#include <bitsery/traits/deque.h>
#include <bitsery/traits/forward_list.h>
#include <bitsery/traits/list.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
using testing::ContainerEq;
using testing::Eq;
/*
* overload to get container of types
*/
template<typename Container>
Container getFilledContainer() {
Container
getFilledContainer()
{
return { 1, 2, 3, 4, 5, 78, 456, 8, 54 };
}
template<>
std::vector<MyStruct1> getFilledContainer<std::vector<MyStruct1>>() {
return {
{0, 1},
{2, 3},
{4, 5},
{6, 7},
{8, 9},
{11, 34},
{5134, 1532}
};
std::vector<MyStruct1>
getFilledContainer<std::vector<MyStruct1>>()
{
return { { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
{ 8, 9 }, { 11, 34 }, { 5134, 1532 } };
}
template<>
std::list<MyStruct2> getFilledContainer<std::list<MyStruct2>>() {
return {
{MyStruct2::V1, {0, 1}},
{MyStruct2::V3, {-45, 45}}
};
std::list<MyStruct2>
getFilledContainer<std::list<MyStruct2>>()
{
return { { MyStruct2::V1, { 0, 1 } }, { MyStruct2::V3, { -45, 45 } } };
}
struct EmptyFtor {
struct EmptyFtor
{
template<typename S, typename T>
void operator() (S& , T& ) {
void operator()(S&, T&)
{
}
};
@@ -76,7 +70,8 @@ struct EmptyFtor {
*/
template<typename T>
class SerializeContainerDynamicSizeArthmeticTypes : public testing::Test {
class SerializeContainerDynamicSizeArthmeticTypes : public testing::Test
{
public:
using TContainer = T;
using TValue = typename T::value_type;
@@ -84,21 +79,24 @@ public:
const TContainer src = getFilledContainer<TContainer>();
TContainer res{};
size_t getExpectedBufSize(const SerializationContext &ctx) const {
size_t getExpectedBufSize(const SerializationContext& ctx) const
{
auto size = bitsery::traits::ContainerTraits<TContainer>::size(src);
return ctx.containerSizeSerializedBytesCount(size) + size * sizeof(TValue);
}
};
// std::forward_list is not supported, because it doesn't have size() method
using SequenceContainersWithArthmeticTypes = ::testing::Types<
std::vector<int>,
using SequenceContainersWithArthmeticTypes =
::testing::Types<std::vector<int>,
std::list<float>,
std::forward_list<int>,
std::deque<unsigned short>>;
TYPED_TEST_SUITE(SerializeContainerDynamicSizeArthmeticTypes, SequenceContainersWithArthmeticTypes,);
TYPED_TEST_SUITE(SerializeContainerDynamicSizeArthmeticTypes,
SequenceContainersWithArthmeticTypes, );
TYPED_TEST(SerializeContainerDynamicSizeArthmeticTypes, Values) {
TYPED_TEST(SerializeContainerDynamicSizeArthmeticTypes, Values)
{
SerializationContext ctx{};
using TValue = typename TestFixture::TValue;
@@ -109,7 +107,9 @@ TYPED_TEST(SerializeContainerDynamicSizeArthmeticTypes, Values) {
EXPECT_THAT(this->res, ContainerEq(this->src));
}
TYPED_TEST(SerializeContainerDynamicSizeArthmeticTypes, CustomFunctionIncrements) {
TYPED_TEST(SerializeContainerDynamicSizeArthmeticTypes,
CustomFunctionIncrements)
{
SerializationContext ctx{};
using TValue = typename TestFixture::TValue;
@@ -131,9 +131,9 @@ TYPED_TEST(SerializeContainerDynamicSizeArthmeticTypes, CustomFunctionIncrements
EXPECT_THAT(this->res, ContainerEq(this->src));
}
template<typename T>
class SerializeContainerDynamicSizeCompositeTypes : public testing::Test {
class SerializeContainerDynamicSizeCompositeTypes : public testing::Test
{
public:
using TContainer = T;
using TValue = typename T::value_type;
@@ -141,19 +141,22 @@ public:
const TContainer src = getFilledContainer<TContainer>();
TContainer res{};
size_t getExpectedBufSize(const SerializationContext &ctx) const {
return ctx.containerSizeSerializedBytesCount(src.size()) + src.size() * TValue::SIZE;
size_t getExpectedBufSize(const SerializationContext& ctx) const
{
return ctx.containerSizeSerializedBytesCount(src.size()) +
src.size() * TValue::SIZE;
}
};
using SerializeContainerDynamicSizeWithCompositeTypes =
::testing::Types<std::vector<MyStruct1>, std::list<MyStruct2>>;
using SerializeContainerDynamicSizeWithCompositeTypes = ::testing::Types<
std::vector<MyStruct1>,
std::list<MyStruct2>>;
TYPED_TEST_SUITE(SerializeContainerDynamicSizeCompositeTypes,
SerializeContainerDynamicSizeWithCompositeTypes, );
TYPED_TEST_SUITE(SerializeContainerDynamicSizeCompositeTypes, SerializeContainerDynamicSizeWithCompositeTypes,);
TYPED_TEST(SerializeContainerDynamicSizeCompositeTypes, DefaultSerializeFunction) {
TYPED_TEST(SerializeContainerDynamicSizeCompositeTypes,
DefaultSerializeFunction)
{
SerializationContext ctx{};
ctx.createSerializer().container(this->src, 1000);
@@ -163,34 +166,39 @@ TYPED_TEST(SerializeContainerDynamicSizeCompositeTypes, DefaultSerializeFunction
EXPECT_THAT(this->res, ContainerEq(this->src));
}
TYPED_TEST(SerializeContainerDynamicSizeCompositeTypes, CustomFunctionThatDoNothing) {
TYPED_TEST(SerializeContainerDynamicSizeCompositeTypes,
CustomFunctionThatDoNothing)
{
SerializationContext ctx{};
ctx.createSerializer().container(this->src, 1000, EmptyFtor{});
ctx.createDeserializer().container(this->res, 1000, EmptyFtor{});
EXPECT_THAT(ctx.getBufferSize(), Eq(ctx.containerSizeSerializedBytesCount(this->src.size())));
EXPECT_THAT(ctx.getBufferSize(),
Eq(ctx.containerSizeSerializedBytesCount(this->src.size())));
}
template<typename T>
class SerializeContainerFixedSizeArithmeticTypes : public testing::Test {
class SerializeContainerFixedSizeArithmeticTypes : public testing::Test
{
public:
using TContainer = T;
size_t getContainerSize() {
size_t getContainerSize()
{
T tmp{};
return static_cast<size_t>(std::distance(std::begin(tmp), std::end(tmp)));
}
};
using StaticContainersWithIntegralTypes = ::testing::Types<
std::array<int16_t, 4>,
int16_t[4]>;
using StaticContainersWithIntegralTypes =
::testing::Types<std::array<int16_t, 4>, int16_t[4]>;
TYPED_TEST_SUITE(SerializeContainerFixedSizeArithmeticTypes, StaticContainersWithIntegralTypes,);
TYPED_TEST_SUITE(SerializeContainerFixedSizeArithmeticTypes,
StaticContainersWithIntegralTypes, );
TYPED_TEST(SerializeContainerFixedSizeArithmeticTypes, ArithmeticValues) {
TYPED_TEST(SerializeContainerFixedSizeArithmeticTypes, ArithmeticValues)
{
using Container = typename TestFixture::TContainer;
Container src{ 5, 9, 15, -459 };
Container res{};
@@ -201,36 +209,47 @@ TYPED_TEST(SerializeContainerFixedSizeArithmeticTypes, ArithmeticValues) {
EXPECT_THAT(ctx.getBufferSize(), Eq(this->getContainerSize() * 2));
EXPECT_THAT(res, ContainerEq(src));
}
template<typename T>
class SerializeContainerFixedSizeCompositeTypes : public SerializeContainerFixedSizeArithmeticTypes<T> {
class SerializeContainerFixedSizeCompositeTypes
: public SerializeContainerFixedSizeArithmeticTypes<T>
{
};
using StaticContainersWithCompositeTypes = ::testing::Types<
std::array<MyStruct1, 4>, MyStruct1[4]>;
using StaticContainersWithCompositeTypes =
::testing::Types<std::array<MyStruct1, 4>, MyStruct1[4]>;
TYPED_TEST_SUITE(SerializeContainerFixedSizeCompositeTypes, StaticContainersWithCompositeTypes,);
TYPED_TEST_SUITE(SerializeContainerFixedSizeCompositeTypes,
StaticContainersWithCompositeTypes, );
TYPED_TEST(SerializeContainerFixedSizeCompositeTypes, DefaultSerializationFunction) {
TYPED_TEST(SerializeContainerFixedSizeCompositeTypes,
DefaultSerializationFunction)
{
using Container = typename TestFixture::TContainer;
Container src{MyStruct1{0, 1}, MyStruct1{8, 9}, MyStruct1{11, 34}, MyStruct1{5134, 1532}};
Container src{ MyStruct1{ 0, 1 },
MyStruct1{ 8, 9 },
MyStruct1{ 11, 34 },
MyStruct1{ 5134, 1532 } };
Container res{};
SerializationContext ctx{};
ctx.createSerializer().container(src);
ctx.createDeserializer().container(res);
EXPECT_THAT(ctx.getBufferSize(), Eq(this->getContainerSize() * MyStruct1::SIZE));
EXPECT_THAT(ctx.getBufferSize(),
Eq(this->getContainerSize() * MyStruct1::SIZE));
EXPECT_THAT(res, ContainerEq(src));
}
TYPED_TEST(SerializeContainerFixedSizeCompositeTypes, CustomFunctionThatSerializesAnEmptyByteEveryElement) {
TYPED_TEST(SerializeContainerFixedSizeCompositeTypes,
CustomFunctionThatSerializesAnEmptyByteEveryElement)
{
using Container = typename TestFixture::TContainer;
Container src{MyStruct1{0, 1}, MyStruct1{2, 3}, MyStruct1{4, 5}, MyStruct1{5134, 1532}};
Container src{ MyStruct1{ 0, 1 },
MyStruct1{ 2, 3 },
MyStruct1{ 4, 5 },
MyStruct1{ 5134, 1532 } };
Container res{};
using TValue = decltype(*std::begin(res));
@@ -249,24 +268,30 @@ TYPED_TEST(SerializeContainerFixedSizeCompositeTypes, CustomFunctionThatSerializ
des.value1b(tmp);
});
EXPECT_THAT(ctx.getBufferSize(), Eq(this->getContainerSize() * (MyStruct1::SIZE + sizeof(char))));
EXPECT_THAT(ctx.getBufferSize(),
Eq(this->getContainerSize() * (MyStruct1::SIZE + sizeof(char))));
EXPECT_THAT(res, ContainerEq(src));
}
class SerializeContainer : public ::testing::TestWithParam<size_t> {
};
class SerializeContainer : public ::testing::TestWithParam<size_t>
{};
TEST_P(SerializeContainer, SizeHasVariableLength) {
TEST_P(SerializeContainer, SizeHasVariableLength)
{
SerializationContext ctx{};
std::vector<uint8_t> src(GetParam());
std::vector<uint8_t> res{};
ctx.createSerializer().container(src, std::numeric_limits<size_t>::max(), EmptyFtor{});
ctx.createDeserializer().container(res, std::numeric_limits<size_t>::max(), EmptyFtor{});
ctx.createSerializer().container(
src, std::numeric_limits<size_t>::max(), EmptyFtor{});
ctx.createDeserializer().container(
res, std::numeric_limits<size_t>::max(), EmptyFtor{});
EXPECT_THAT(res.size(), Eq(src.size()));
EXPECT_THAT(ctx.getBufferSize(), Eq(ctx.containerSizeSerializedBytesCount(src.size())));
EXPECT_THAT(ctx.getBufferSize(),
Eq(ctx.containerSizeSerializedBytesCount(src.size())));
}
INSTANTIATE_TEST_SUITE_P(LargeContainerSize, SerializeContainer, ::testing::Values(0x01, 0x80, 0x4000));
INSTANTIATE_TEST_SUITE_P(LargeContainerSize,
SerializeContainer,
::testing::Values(0x01, 0x80, 0x4000));

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,9 +20,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
using testing::Eq;
@@ -31,7 +30,8 @@ using bitsery::DefaultConfig;
using SingleTypeContext = int;
using MultipleTypesContext = std::tuple<int, float, char>;
TEST(SerializationContext, WhenContextIsNotTupleThenReturnThisContext) {
TEST(SerializationContext, WhenContextIsNotTupleThenReturnThisContext)
{
SingleTypeContext ctx{ 54 };
BasicSerializationContext<SingleTypeContext> c1;
auto& ser1 = c1.createSerializer(ctx);
@@ -39,7 +39,8 @@ TEST(SerializationContext, WhenContextIsNotTupleThenReturnThisContext) {
EXPECT_THAT(ser1.context<SingleTypeContext>(), Eq(ctx));
}
TEST(SerializationContext, WhenContextIsTupleThenReturnsTupleElements) {
TEST(SerializationContext, WhenContextIsTupleThenReturnsTupleElements)
{
MultipleTypesContext ctx{ 5, 798.654f, 'F' };
BasicSerializationContext<MultipleTypesContext> c1;
@@ -50,7 +51,8 @@ TEST(SerializationContext, WhenContextIsTupleThenReturnsTupleElements) {
EXPECT_THAT(ser1.context<char>(), std::get<2>(ctx));
}
TEST(SerializationContext, WhenContextDoesntExistsThenContextOrNullReturnsNull) {
TEST(SerializationContext, WhenContextDoesntExistsThenContextOrNullReturnsNull)
{
SingleTypeContext ctx1 = 32;
BasicSerializationContext<SingleTypeContext> c1;
auto& ser = c1.createSerializer(ctx1);
@@ -68,10 +70,15 @@ TEST(SerializationContext, WhenContextDoesntExistsThenContextOrNullReturnsNull)
EXPECT_THAT(*des.contextOrNull<int>(), Eq(5));
}
struct Base { int value{}; };
struct Derived: Base{};
struct Base
{
int value{};
};
struct Derived : Base
{};
TEST(SerializationContext, ContextWillTryToConvertIfTypeIsConvertible) {
TEST(SerializationContext, ContextWillTryToConvertIfTypeIsConvertible)
{
Derived ctx1{};
BasicSerializationContext<Derived> c1;
auto& ser = c1.createSerializer(ctx1);
@@ -81,7 +88,9 @@ TEST(SerializationContext, ContextWillTryToConvertIfTypeIsConvertible) {
ser.context<Base>();
}
TEST(SerializationContext, WhenMultipleConvertibleTypesExistsThenFirstMatchIsTaken) {
TEST(SerializationContext,
WhenMultipleConvertibleTypesExistsThenFirstMatchIsTaken)
{
{
using CTX1 = std::tuple<Base, int, Derived>;
CTX1 ctx1{};
@@ -102,7 +111,8 @@ TEST(SerializationContext, WhenMultipleConvertibleTypesExistsThenFirstMatchIsTak
auto& des = c2.createSerializer(ctx2);
EXPECT_THAT(des.context<Derived>().value, Eq(std::get<1>(ctx2).value));
//Base will not be accessable in this case, because Derived is first valid match
// Base will not be accessable in this case, because Derived is first valid
// match
EXPECT_THAT(des.context<Base>().value, Eq(std::get<1>(ctx2).value));
}
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,23 +20,24 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "serialization_test_utils.h"
#include <bitsery/ext/compact_value.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/traits/array.h>
#include <bitset>
#include <chrono>
using testing::Eq;
using bitsery::EndiannessType;
using bitsery::ext::CompactValue;
using bitsery::ext::CompactValueAsObject;
using bitsery::EndiannessType;
using testing::Eq;
// helper function, that gets value filled with specified number of bits
template<typename TValue>
TValue getValue(bool isPositive, size_t significantBits) {
TValue
getValue(bool isPositive, size_t significantBits)
{
TValue v = isPositive ? 0 : static_cast<TValue>(-1);
if (significantBits == 0)
return v;
@@ -52,31 +53,38 @@ TValue getValue(bool isPositive, size_t significantBits) {
// helper function, that serialize and return deserialized value
template<typename TConfig, typename TValue>
std::pair<TValue, size_t> serializeAndGetDeserialized(TValue data) {
std::pair<TValue, size_t>
serializeAndGetDeserialized(TValue data)
{
Buffer buf{};
bitsery::Serializer<bitsery::OutputBufferAdapter<Buffer, TConfig>> ser{ buf };
ser.template ext<sizeof(TValue)>(data, CompactValue{});
bitsery::Deserializer<bitsery::InputBufferAdapter<Buffer, TConfig>> des{buf.begin(), ser.adapter().writtenBytesCount()};
bitsery::Deserializer<bitsery::InputBufferAdapter<Buffer, TConfig>> des{
buf.begin(), ser.adapter().writtenBytesCount()
};
TValue res;
des.template ext<sizeof(TValue)>(res, CompactValue{});
return { res, ser.adapter().writtenBytesCount() };
}
struct LittleEndianConfig {
struct LittleEndianConfig
{
static constexpr EndiannessType Endianness = EndiannessType::LittleEndian;
static constexpr bool CheckDataErrors = true;
static constexpr bool CheckAdapterErrors = true;
};
struct BigEndianConfig {
struct BigEndianConfig
{
static constexpr EndiannessType Endianness = EndiannessType::BigEndian;
static constexpr bool CheckDataErrors = true;
static constexpr bool CheckAdapterErrors = true;
};
template<typename TValue, bool isPositiveNr, typename TConfig>
struct TC {
struct TC
{
static_assert(isPositiveNr || std::is_signed<TValue>::value, "");
using Value = TValue;
@@ -85,14 +93,14 @@ struct TC {
};
template<typename T>
class SerializeExtensionCompactValueCorrectness : public testing::Test {
class SerializeExtensionCompactValueCorrectness : public testing::Test
{
public:
using TestCase = T;
};
using AllValueSizesTestCases = ::testing::Types<
TC<uint8_t, true, LittleEndianConfig>,
using AllValueSizesTestCases =
::testing::Types<TC<uint8_t, true, LittleEndianConfig>,
TC<uint16_t, true, LittleEndianConfig>,
TC<uint32_t, true, LittleEndianConfig>,
TC<uint64_t, true, LittleEndianConfig>,
@@ -115,12 +123,13 @@ using AllValueSizesTestCases = ::testing::Types<
TC<int8_t, false, BigEndianConfig>,
TC<int16_t, false, BigEndianConfig>,
TC<int32_t, false, BigEndianConfig>,
TC<int64_t, false, BigEndianConfig>
>;
TC<int64_t, false, BigEndianConfig>>;
TYPED_TEST_SUITE(SerializeExtensionCompactValueCorrectness, AllValueSizesTestCases,);
TYPED_TEST_SUITE(SerializeExtensionCompactValueCorrectness,
AllValueSizesTestCases, );
TYPED_TEST(SerializeExtensionCompactValueCorrectness, TestDifferentSizeValues) {
TYPED_TEST(SerializeExtensionCompactValueCorrectness, TestDifferentSizeValues)
{
using TCase = typename TestFixture::TestCase;
using TValue = typename TCase::Value;
TCase tc{};
@@ -132,12 +141,16 @@ TYPED_TEST(SerializeExtensionCompactValueCorrectness, TestDifferentSizeValues) {
}
}
// this stucture will contain test data and result, as type paramters
template <typename TValue, bool isPositiveNr, size_t significantBits, size_t resultBytes>
struct SizeTC {
template<typename TValue,
bool isPositiveNr,
size_t significantBits,
size_t resultBytes>
struct SizeTC
{
static_assert(isPositiveNr || std::is_signed<TValue>::value, "");
static_assert(bitsery::details::BitsSize<TValue>::value >= significantBits, "");
static_assert(bitsery::details::BitsSize<TValue>::value >= significantBits,
"");
using Value = TValue;
bool isPositive = isPositiveNr;
@@ -146,7 +159,8 @@ struct SizeTC {
};
template<typename T>
class SerializeExtensionCompactValueRequiredBytes : public testing::Test {
class SerializeExtensionCompactValueRequiredBytes : public testing::Test
{
public:
using TestCase = T;
};
@@ -182,7 +196,8 @@ using RequiredBytesTestCases = ::testing::Types<
SizeTC<int32_t, false, 28, 5>,
SizeTC<int32_t, true, 31, 5>,
//8 byte, +1 byte after 57 significant bits, or +2 byte when all bits are significant
// 8 byte, +1 byte after 57 significant bits, or +2 byte when all bits are
// significant
SizeTC<uint64_t, true, 28, 4>,
SizeTC<uint64_t, true, 35, 5>,
SizeTC<uint64_t, true, 42, 6>,
@@ -199,12 +214,13 @@ using RequiredBytesTestCases = ::testing::Types<
SizeTC<int64_t, true, 55, 8>,
SizeTC<int64_t, false, 56, 9>,
SizeTC<int64_t, true, 62, 9>,
SizeTC<int64_t, false, 63,10>
>;
SizeTC<int64_t, false, 63, 10>>;
TYPED_TEST_SUITE(SerializeExtensionCompactValueRequiredBytes, RequiredBytesTestCases,);
TYPED_TEST_SUITE(SerializeExtensionCompactValueRequiredBytes,
RequiredBytesTestCases, );
TYPED_TEST(SerializeExtensionCompactValueRequiredBytes, Test) {
TYPED_TEST(SerializeExtensionCompactValueRequiredBytes, Test)
{
using TCase = typename TestFixture::TestCase;
using TValue = typename TCase::Value;
TCase tc{};
@@ -214,31 +230,47 @@ TYPED_TEST(SerializeExtensionCompactValueRequiredBytes, Test) {
EXPECT_THAT(res.second, tc.bytesCount);
}
enum b1En: uint8_t {
A,B,C,D=54,E
enum b1En : uint8_t
{
A,
B,
C,
D = 54,
E
};
enum class b8En: int64_t {
A=-874987489,B,C=0,D,E=489748978, F,G
enum class b8En : int64_t
{
A = -874987489,
B,
C = 0,
D,
E = 489748978,
F,
G
};
TEST(SerializeExtensionCompactValueEnum, TestEnums) {
TEST(SerializeExtensionCompactValueEnum, TestEnums)
{
auto d1 = b1En::E;
auto d2 = b8En::B;
auto d3 = b8En::F;
EXPECT_THAT(serializeAndGetDeserialized<bitsery::DefaultConfig>(d1).first, Eq(d1));
EXPECT_THAT(serializeAndGetDeserialized<bitsery::DefaultConfig>(d2).first, Eq(d2));
EXPECT_THAT(serializeAndGetDeserialized<bitsery::DefaultConfig>(d3).first, Eq(d3));
EXPECT_THAT(serializeAndGetDeserialized<bitsery::DefaultConfig>(d1).first,
Eq(d1));
EXPECT_THAT(serializeAndGetDeserialized<bitsery::DefaultConfig>(d2).first,
Eq(d2));
EXPECT_THAT(serializeAndGetDeserialized<bitsery::DefaultConfig>(d3).first,
Eq(d3));
}
TEST(SerializeExtensionCompactValueAsObjectDeserializeOverflow, TestEnums) {
TEST(SerializeExtensionCompactValueAsObjectDeserializeOverflow, TestEnums)
{
SerializationContext ctx;
auto data = getValue<uint32_t>(true, 17);
uint16_t res{};
ctx.createSerializer().ext(data, CompactValueAsObject{});
ctx.createDeserializer().ext(res, CompactValueAsObject{});
EXPECT_THAT(data, ::testing::Ne(res));
EXPECT_THAT(ctx.des->adapter().error(), Eq(bitsery::ReaderError::InvalidData));
EXPECT_THAT(ctx.des->adapter().error(),
Eq(bitsery::ReaderError::InvalidData));
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,12 +20,11 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <bitsery/ext/entropy.h>
#include <bitsery/traits/list.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
using namespace testing;
@@ -34,15 +33,15 @@ using bitsery::ext::Entropy;
using BPSer = SerializationContext::TSerializerBPEnabled;
using BPDes = SerializationContext::TDeserializerBPEnabled;
TEST(SerializeExtensionEntropy, WhenEntropyEncodedThenOnlyWriteIndexUsingMinRequiredBits) {
TEST(SerializeExtensionEntropy,
WhenEntropyEncodedThenOnlyWriteIndexUsingMinRequiredBits)
{
int32_t v = 4849;
int32_t res;
int32_t values[3] = { 485, 4849, 89 };
SerializationContext ctx{};
ctx.createSerializer().enableBitPacking([&v, &values](BPSer& ser) {
ser.ext4b(v, Entropy<int32_t[3]>{values});
});
ctx.createSerializer().enableBitPacking(
[&v, &values](BPSer& ser) { ser.ext4b(v, Entropy<int32_t[3]>{ values }); });
ctx.createDeserializer().enableBitPacking([&res, &values](BPDes& des) {
des.ext4b(res, Entropy<int32_t[3]>{ values });
});
@@ -51,16 +50,18 @@ TEST(SerializeExtensionEntropy, WhenEntropyEncodedThenOnlyWriteIndexUsingMinRequ
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
SerializationContext ctx1{};
ctx1.createSerializer().enableBitPacking([&v, &values](BPSer& ser) {
ser.ext4b(v, Entropy<int32_t[3]>{values});
});
ctx1.createSerializer().enableBitPacking(
[&v, &values](BPSer& ser) { ser.ext4b(v, Entropy<int32_t[3]>{ values }); });
ctx1.createDeserializer().enableBitPacking([&res](BPDes& des) {
des.ext(res, bitsery::ext::ValueRange<int32_t>{0, static_cast<int32_t>(3 + 1)});
des.ext(
res, bitsery::ext::ValueRange<int32_t>{ 0, static_cast<int32_t>(3 + 1) });
});
EXPECT_THAT(res, Eq(2));
}
TEST(SerializeExtensionEntropy, WhenNoEntropyEncodedThenWriteZeroBitsAndValueOrObject) {
TEST(SerializeExtensionEntropy,
WhenNoEntropyEncodedThenWriteZeroBitsAndValueOrObject)
{
int16_t v = 8945;
int16_t res;
std::initializer_list<int> values{ 485, 4849, 89 };
@@ -76,18 +77,19 @@ TEST(SerializeExtensionEntropy, WhenNoEntropyEncodedThenWriteZeroBitsAndValueOrO
EXPECT_THAT(ctx.getBufferSize(), Eq(sizeof(int16_t) + 1));
}
TEST(SerializeExtensionEntropy, CustomTypeEntropyEncoded) {
TEST(SerializeExtensionEntropy, CustomTypeEntropyEncoded)
{
MyStruct1 v = { 12, 10 };
MyStruct1 res;
constexpr size_t N = 4;
MyStruct1 values[N]{
MyStruct1{12, 10}, MyStruct1{485, 454},
MyStruct1{4849, 89}, MyStruct1{0, 1}};
MyStruct1 values[N]{ MyStruct1{ 12, 10 },
MyStruct1{ 485, 454 },
MyStruct1{ 4849, 89 },
MyStruct1{ 0, 1 } };
SerializationContext ctx{};
ctx.createSerializer().enableBitPacking([&v, &values](BPSer& ser) {
ser.ext(v, Entropy<MyStruct1[4]>{values});
});
ctx.createSerializer().enableBitPacking(
[&v, &values](BPSer& ser) { ser.ext(v, Entropy<MyStruct1[4]>{ values }); });
ctx.createDeserializer().enableBitPacking([&res, &values](BPDes& des) {
des.ext(res, Entropy<MyStruct1[4]>{ values });
});
@@ -95,13 +97,15 @@ TEST(SerializeExtensionEntropy, CustomTypeEntropyEncoded) {
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
}
TEST(SerializeExtensionEntropy, CustomTypeNotEntropyEncoded) {
TEST(SerializeExtensionEntropy, CustomTypeNotEntropyEncoded)
{
MyStruct1 v = { 8945, 4456 };
MyStruct1 res;
std::initializer_list<MyStruct1> values {
MyStruct1{12,10}, MyStruct1{485, 454},
MyStruct1{4849,89}, MyStruct1{0,1}};
std::initializer_list<MyStruct1> values{ MyStruct1{ 12, 10 },
MyStruct1{ 485, 454 },
MyStruct1{ 4849, 89 },
MyStruct1{ 0, 1 } };
SerializationContext ctx{};
ctx.createSerializer().enableBitPacking([&v, &values](BPSer& ser) {
ser.ext(v, Entropy<std::initializer_list<MyStruct1>>{ values });
@@ -114,20 +118,25 @@ TEST(SerializeExtensionEntropy, CustomTypeNotEntropyEncoded) {
EXPECT_THAT(ctx.getBufferSize(), Eq(MyStruct1::SIZE + 1));
}
TEST(SerializeExtensionEntropy, CustomFunctionNotEntropyEncodedWithNoAlignBeforeData) {
TEST(SerializeExtensionEntropy,
CustomFunctionNotEntropyEncodedWithNoAlignBeforeData)
{
MyStruct1 v = { 8945, 4456 };
MyStruct1 res;
constexpr size_t N = 4;
std::vector<MyStruct1> values{
MyStruct1{12,10}, MyStruct1{485, 454},
MyStruct1{4849,89}, MyStruct1{0,1}};
std::vector<MyStruct1> values{ MyStruct1{ 12, 10 },
MyStruct1{ 485, 454 },
MyStruct1{ 4849, 89 },
MyStruct1{ 0, 1 } };
auto rangeForValue = bitsery::ext::ValueRange<int>{ 0, 10000 };
SerializationContext ctx;
ctx.createSerializer().enableBitPacking([&v, &values, &rangeForValue](BPSer& ser){
//lambdas differ only in capture clauses, it would make sense to use std::bind, but debugger crashes when it sees std::bind...
ctx.createSerializer().enableBitPacking(
[&v, &values, &rangeForValue](BPSer& ser) {
// lambdas differ only in capture clauses, it would make sense to use
// std::bind, but debugger crashes when it sees std::bind...
auto serLambda = [&rangeForValue](BPSer& ser, MyStruct1& data) {
ser.ext(data.i1, rangeForValue);
ser.ext(data.i2, rangeForValue);
@@ -135,7 +144,8 @@ TEST(SerializeExtensionEntropy, CustomFunctionNotEntropyEncodedWithNoAlignBefore
ser.ext(v, Entropy<std::vector<MyStruct1>>(values, false), serLambda);
});
ctx.createDeserializer().enableBitPacking([&res, &values, &rangeForValue](BPDes& des) {
ctx.createDeserializer().enableBitPacking(
[&res, &values, &rangeForValue](BPDes& des) {
auto desLambda = [&rangeForValue](BPDes& des, MyStruct1& data) {
des.ext(data.i1, rangeForValue);
des.ext(data.i2, rangeForValue);
@@ -145,29 +155,39 @@ TEST(SerializeExtensionEntropy, CustomFunctionNotEntropyEncodedWithNoAlignBefore
EXPECT_THAT(res, Eq(v));
auto rangeForIndex = bitsery::ext::ValueRange<size_t>{ 0u, N + 1 };
EXPECT_THAT(ctx.getBufferSize(), Eq((rangeForIndex.getRequiredBits() + rangeForValue.getRequiredBits() * 2 - 1) / 8 + 1 ));
EXPECT_THAT(ctx.getBufferSize(),
Eq((rangeForIndex.getRequiredBits() +
rangeForValue.getRequiredBits() * 2 - 1) /
8 +
1));
}
TEST(SerializeExtensionEntropy, CustomFunctionNotEntropyEncodedWithAlignBeforeData) {
TEST(SerializeExtensionEntropy,
CustomFunctionNotEntropyEncodedWithAlignBeforeData)
{
MyStruct1 v = { 8945, 4456 };
MyStruct1 res;
std::vector<MyStruct1> values{
MyStruct1{12,10}, MyStruct1{485, 454},
MyStruct1{4849,89}, MyStruct1{0,1}};
std::vector<MyStruct1> values{ MyStruct1{ 12, 10 },
MyStruct1{ 485, 454 },
MyStruct1{ 4849, 89 },
MyStruct1{ 0, 1 } };
auto rangeForValue = bitsery::ext::ValueRange<int>{ 0, 10000 };
SerializationContext ctx;
ctx.createSerializer().enableBitPacking([&v, &values, &rangeForValue](BPSer& ser){
//lambdas differ only in capture clauses, it would make sense to use std::bind, but debugger crashes when it sees std::bind...
ctx.createSerializer().enableBitPacking(
[&v, &values, &rangeForValue](BPSer& ser) {
// lambdas differ only in capture clauses, it would make sense to use
// std::bind, but debugger crashes when it sees std::bind...
auto serLambda = [&rangeForValue](BPSer& ser, MyStruct1& data) {
ser.ext(data.i1, rangeForValue);
ser.ext(data.i2, rangeForValue);
};
ser.ext(v, Entropy<std::vector<MyStruct1>>(values, true), serLambda);
});
ctx.createDeserializer().enableBitPacking([&res, &values, &rangeForValue](BPDes& des) {
ctx.createDeserializer().enableBitPacking(
[&res, &values, &rangeForValue](BPDes& des) {
auto desLambda = [&rangeForValue](BPDes& des, MyStruct1& data) {
des.ext(data.i1, rangeForValue);
des.ext(data.i2, rangeForValue);
@@ -177,22 +197,29 @@ TEST(SerializeExtensionEntropy, CustomFunctionNotEntropyEncodedWithAlignBeforeDa
EXPECT_THAT(res, Eq(v));
auto bitsForIndex = 8u; // because aligned
EXPECT_THAT(ctx.getBufferSize(), Eq((bitsForIndex + rangeForValue.getRequiredBits() * 2 - 1) / 8 + 1 ));
EXPECT_THAT(
ctx.getBufferSize(),
Eq((bitsForIndex + rangeForValue.getRequiredBits() * 2 - 1) / 8 + 1));
}
TEST(SerializeExtensionEntropy, WhenEntropyEncodedThenCustomFunctionNotInvoked) {
TEST(SerializeExtensionEntropy, WhenEntropyEncodedThenCustomFunctionNotInvoked)
{
MyStruct1 v = { 4849, 89 };
MyStruct1 res;
std::list<MyStruct1> values {MyStruct1{12,10}, MyStruct1{485, 454},
MyStruct1{4849,89}, MyStruct1{0,1}};
std::list<MyStruct1> values{ MyStruct1{ 12, 10 },
MyStruct1{ 485, 454 },
MyStruct1{ 4849, 89 },
MyStruct1{ 0, 1 } };
SerializationContext ctx;
ctx.createSerializer().enableBitPacking([&v, &values](BPSer& ser) {
ser.ext(v, Entropy<std::list<MyStruct1>>{values}, [](BPSer& ,MyStruct1& ) {});
ser.ext(
v, Entropy<std::list<MyStruct1>>{ values }, [](BPSer&, MyStruct1&) {});
});
ctx.createDeserializer().enableBitPacking([&res, &values](BPDes& des) {
des.ext(res, Entropy<std::list<MyStruct1>>{values}, [](BPDes& , MyStruct1& ) {});
des.ext(
res, Entropy<std::list<MyStruct1>>{ values }, [](BPDes&, MyStruct1&) {});
});
EXPECT_THAT(res, Eq(v));

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,48 +20,57 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "serialization_test_utils.h"
#include <bitsery/ext/growable.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using namespace testing;
using bitsery::ext::Growable;
struct DataV1 {
struct DataV1
{
int32_t v1;
};
template<typename S>
void serialize(S& s, DataV1& o) {
void
serialize(S& s, DataV1& o)
{
s.value4b(o.v1);
}
struct DataV2 {
struct DataV2
{
int32_t v1;
int32_t v2;
};
template<typename S>
void serialize(S& s, DataV2& o) {
void
serialize(S& s, DataV2& o)
{
s.value4b(o.v1);
s.value4b(o.v2);
}
struct DataV3 {
struct DataV3
{
int32_t v1;
int32_t v2;
int32_t v3;
template<typename S>
void serialize(S& s) {
void serialize(S& s)
{
s.value4b(v1);
s.value4b(v2);
s.value4b(v3);
}
};
TEST(SerializeExtensionGrowable, SessionsLengthIsStoredWith4BytesBeforeSessionDataStarts) {
TEST(SerializeExtensionGrowable,
SessionsLengthIsStoredWith4BytesBeforeSessionDataStarts)
{
SerializationContext ctx;
auto& ser = ctx.createSerializer();
// session cannot be empty
@@ -86,7 +95,8 @@ TEST(SerializeExtensionGrowable, SessionsLengthIsStoredWith4BytesBeforeSessionDa
EXPECT_THAT(ctx.ser->adapter().writtenBytesCount(), Eq(8));
}
TEST(SerializeExtensionGrowable, MultipleSessionsReadSameVersionData) {
TEST(SerializeExtensionGrowable, MultipleSessionsReadSameVersionData)
{
SerializationContext ctx;
DataV2 data{ 8454, 987451 };
auto& ser = ctx.createSerializer();
@@ -105,7 +115,8 @@ TEST(SerializeExtensionGrowable, MultipleSessionsReadSameVersionData) {
EXPECT_THAT(ctx.des->adapter().isCompletedSuccessfully(), Eq(true));
}
TEST(SerializeExtensionGrowable, MultipleSessionsReadNewerVersionData) {
TEST(SerializeExtensionGrowable, MultipleSessionsReadNewerVersionData)
{
SerializationContext ctx;
DataV3 data{ 8454, 987451, 45612 };
auto& ser = ctx.createSerializer();
@@ -124,7 +135,8 @@ TEST(SerializeExtensionGrowable, MultipleSessionsReadNewerVersionData) {
EXPECT_THAT(ctx.des->adapter().isCompletedSuccessfully(), Eq(true));
}
TEST(SerializeExtensionGrowable, MultipleSessionsReadOlderVersionData) {
TEST(SerializeExtensionGrowable, MultipleSessionsReadOlderVersionData)
{
SerializationContext ctx;
DataV2 data{ 8454, 987451 };
auto& ser = ctx.createSerializer();
@@ -144,7 +156,8 @@ TEST(SerializeExtensionGrowable, MultipleSessionsReadOlderVersionData) {
EXPECT_THAT(ctx.des->adapter().isCompletedSuccessfully(), Eq(true));
}
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadSameVersionData) {
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadSameVersionData)
{
SerializationContext ctx;
DataV2 data{ 8454, 987451 };
auto& ser = ctx.createSerializer();
@@ -173,7 +186,8 @@ TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadSameVersionData) {
EXPECT_THAT(ctx.des->adapter().isCompletedSuccessfully(), Eq(true));
}
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadNewerVersionData) {
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadNewerVersionData)
{
SerializationContext ctx;
DataV3 data{ 8454, 987451, 54124 };
auto& ser = ctx.createSerializer();
@@ -204,7 +218,8 @@ TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadNewerVersionData) {
EXPECT_THAT(ctx.des->adapter().isCompletedSuccessfully(), Eq(true));
}
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadOlderVersionData) {
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadOlderVersionData)
{
SerializationContext ctx;
DataV2 data{ 8454, 987451 };
auto& ser = ctx.createSerializer();

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,9 +20,9 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "serialization_test_utils.h"
#include <bitsery/ext/inheritance.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using bitsery::ext::BaseClass;
using bitsery::ext::VirtualBaseClass;
@@ -34,48 +34,60 @@ using testing::Eq;
/*
* base class
*/
struct Base {
struct Base
{
uint8_t x{};
virtual ~Base() = default;
};
template<typename S>
void serialize(S& s, Base& o) {
void
serialize(S& s, Base& o)
{
s.value1b(o.x);
}
/*
* non virtual inheritance from base
*/
struct Derive1NonVirtually:Base {
struct Derive1NonVirtually : Base
{
uint8_t y1{};
};
template<typename S>
void serialize(S& s, Derive1NonVirtually& o) {
void
serialize(S& s, Derive1NonVirtually& o)
{
s.ext(o, BaseClass<Base>{});
s.value1b(o.y1);
}
struct Derive2NonVirtually:Base {
struct Derive2NonVirtually : Base
{
uint8_t y2{};
};
template<typename S>
void serialize(S& s, Derive2NonVirtually& o) {
void
serialize(S& s, Derive2NonVirtually& o)
{
// use lambda to serialize base
s.ext(o, BaseClass<Base>{}, [](S& s, Base& b) {
s.object(b);
});
s.ext(o, BaseClass<Base>{}, [](S& s, Base& b) { s.object(b); });
s.value1b(o.y2);
}
struct MultipleInheritanceNonVirtualBase: Derive1NonVirtually, Derive2NonVirtually {
struct MultipleInheritanceNonVirtualBase
: Derive1NonVirtually
, Derive2NonVirtually
{
uint8_t z{};
};
template<typename S>
void serialize(S& s, MultipleInheritanceNonVirtualBase& o) {
void
serialize(S& s, MultipleInheritanceNonVirtualBase& o)
{
s.ext(o, BaseClass<Derive1NonVirtually>{});
s.ext(o, BaseClass<Derive2NonVirtually>{});
s.value1b(o.z);
@@ -84,31 +96,44 @@ void serialize(S& s, MultipleInheritanceNonVirtualBase& o) {
/*
* virtual inheritance from base
*/
struct Derive1Virtually:virtual Base {
struct Derive1Virtually : virtual Base
{
uint8_t y1{};
};
template<typename S>
void serialize(S& s, Derive1Virtually& o) {
void
serialize(S& s, Derive1Virtually& o)
{
s.ext(o, VirtualBaseClass<Base>{});
s.value1b(o.y1);
}
struct Derive2Virtually:virtual Base {
struct Derive2Virtually : virtual Base
{
uint8_t y2{};
};
template<typename S>
void serialize(S& s, Derive2Virtually& o) {
void
serialize(S& s, Derive2Virtually& o)
{
s.ext(o, VirtualBaseClass<Base>{});
s.value1b(o.y2);
}
struct MultipleInheritanceVirtualBase: Derive1Virtually, Derive2Virtually {
struct MultipleInheritanceVirtualBase
: Derive1Virtually
, Derive2Virtually
{
uint8_t z{};
MultipleInheritanceVirtualBase() = default;
MultipleInheritanceVirtualBase(uint8_t x_, uint8_t y1_, uint8_t y2_, uint8_t z_) {
MultipleInheritanceVirtualBase(uint8_t x_,
uint8_t y1_,
uint8_t y2_,
uint8_t z_)
{
x = x_;
y1 = y1_;
y2 = y2_;
@@ -116,19 +141,24 @@ struct MultipleInheritanceVirtualBase: Derive1Virtually, Derive2Virtually {
}
template<typename S>
void serialize(S& s) {
void serialize(S& s)
{
s.ext(*this, BaseClass<Derive1Virtually>{});
s.ext(*this, BaseClass<Derive2Virtually>{});
s.value1b(z);
}
};
bool operator == (const MultipleInheritanceVirtualBase& lhs, const MultipleInheritanceVirtualBase& rhs) {
return std::tie(lhs.x, lhs.y1, lhs.y2, lhs.z) == std::tie(rhs.x, rhs.y1, rhs.y2, rhs.z);
bool
operator==(const MultipleInheritanceVirtualBase& lhs,
const MultipleInheritanceVirtualBase& rhs)
{
return std::tie(lhs.x, lhs.y1, lhs.y2, lhs.z) ==
std::tie(rhs.x, rhs.y1, rhs.y2, rhs.z);
}
TEST(SerializeExtensionInheritance, BaseClass) {
TEST(SerializeExtensionInheritance, BaseClass)
{
Derive1NonVirtually d1{};
d1.x = 187;
@@ -146,7 +176,8 @@ TEST(SerializeExtensionInheritance, BaseClass) {
EXPECT_THAT(ctx.getBufferSize(), Eq(2));
}
TEST(SerializeExtensionInheritance, VirtualBaseClass) {
TEST(SerializeExtensionInheritance, VirtualBaseClass)
{
Derive1Virtually d1{};
d1.x = 15;
d1.y1 = 87;
@@ -163,7 +194,8 @@ TEST(SerializeExtensionInheritance, VirtualBaseClass) {
EXPECT_THAT(ctx.getBufferSize(), Eq(2));
}
TEST(SerializeExtensionInheritance, MultipleBasesWithoutVirtualInheritance) {
TEST(SerializeExtensionInheritance, MultipleBasesWithoutVirtualInheritance)
{
MultipleInheritanceNonVirtualBase md{};
// x is ambiguous because we don't derive virtually
static_cast<Derive1NonVirtually&>(md).x = 1;
@@ -179,16 +211,19 @@ TEST(SerializeExtensionInheritance, MultipleBasesWithoutVirtualInheritance) {
ctx.createSerializer(inherCtxSer).object(md);
ctx.createDeserializer(inherCtxDes).object(res);
EXPECT_THAT(static_cast<Derive1NonVirtually&>(res).x, Eq(static_cast<Derive1NonVirtually&>(md).x));
EXPECT_THAT(static_cast<Derive2NonVirtually&>(res).x, Eq(static_cast<Derive2NonVirtually&>(md).x));
EXPECT_THAT(static_cast<Derive1NonVirtually&>(res).x,
Eq(static_cast<Derive1NonVirtually&>(md).x));
EXPECT_THAT(static_cast<Derive2NonVirtually&>(res).x,
Eq(static_cast<Derive2NonVirtually&>(md).x));
EXPECT_THAT(res.y1, Eq(md.y1));
EXPECT_THAT(res.y2, Eq(md.y2));
EXPECT_THAT(res.z, Eq(md.z));
EXPECT_THAT(ctx.getBufferSize(), Eq(5)); // 5 because two bases
}
TEST(SerializeExtensionInheritance, WhenNoVirtualInheritanceExistsThenInheritanceContextIsNotRequired) {
TEST(SerializeExtensionInheritance,
WhenNoVirtualInheritanceExistsThenInheritanceContextIsNotRequired)
{
MultipleInheritanceNonVirtualBase md{};
// x is ambiguous because we don't derive virtually
static_cast<Derive1NonVirtually&>(md).x = 1;
@@ -203,18 +238,18 @@ TEST(SerializeExtensionInheritance, WhenNoVirtualInheritanceExistsThenInheritanc
ctx.createSerializer().object(md);
ctx.createDeserializer().object(res);
EXPECT_THAT(static_cast<Derive1NonVirtually&>(res).x, Eq(static_cast<Derive1NonVirtually&>(md).x));
EXPECT_THAT(static_cast<Derive2NonVirtually&>(res).x, Eq(static_cast<Derive2NonVirtually&>(md).x));
EXPECT_THAT(static_cast<Derive1NonVirtually&>(res).x,
Eq(static_cast<Derive1NonVirtually&>(md).x));
EXPECT_THAT(static_cast<Derive2NonVirtually&>(res).x,
Eq(static_cast<Derive2NonVirtually&>(md).x));
EXPECT_THAT(res.y1, Eq(md.y1));
EXPECT_THAT(res.y2, Eq(md.y2));
EXPECT_THAT(res.z, Eq(md.z));
EXPECT_THAT(ctx.getBufferSize(), Eq(5)); // 5 because two bases
}
TEST(SerializeExtensionInheritance, MultipleBasesWithVirtualInheritance) {
TEST(SerializeExtensionInheritance, MultipleBasesWithVirtualInheritance)
{
MultipleInheritanceVirtualBase md{ 3, 7, 5, 15 };
MultipleInheritanceVirtualBase res{};
@@ -227,7 +262,9 @@ TEST(SerializeExtensionInheritance, MultipleBasesWithVirtualInheritance) {
EXPECT_THAT(ctx.getBufferSize(), Eq(4)); // 4 because virtual base
}
TEST(SerializeExtensionInheritance, MultipleBasesWithVirtualInheritanceMultipleObjects) {
TEST(SerializeExtensionInheritance,
MultipleBasesWithVirtualInheritanceMultipleObjects)
{
std::vector<MultipleInheritanceVirtualBase> data;
data.emplace_back(4, 8, 7, 9);
data.emplace_back(1, 2, 3, 4);
@@ -242,70 +279,96 @@ TEST(SerializeExtensionInheritance, MultipleBasesWithVirtualInheritanceMultipleO
ctx.createSerializer(inherCtxSer).container(data, 10);
ctx.createDeserializer(inherCtxDes).container(res, 10);
EXPECT_THAT(res, ::testing::ContainerEq(data));
EXPECT_THAT(ctx.getBufferSize(), Eq(1 + 4 * data.size())); //1 container size + 4 because virtual base * elements
EXPECT_THAT(
ctx.getBufferSize(),
Eq(1 +
4 *
data.size())); // 1 container size + 4 because virtual base * elements
}
//
class BasePrivateSerialize {
class BasePrivateSerialize
{
public:
explicit BasePrivateSerialize(uint8_t v):_v{v} {}
explicit BasePrivateSerialize(uint8_t v)
: _v{ v }
{
}
uint8_t getX() const { return _v; }
private:
uint8_t _v;
friend bitsery::Access;
template<typename S>
void serialize(S& s) {
void serialize(S& s)
{
s.value1b(_v);
}
};
class DerivedPrivateBase: public BasePrivateSerialize {
class DerivedPrivateBase : public BasePrivateSerialize
{
public:
explicit DerivedPrivateBase(uint8_t v) : BasePrivateSerialize(v) {}
explicit DerivedPrivateBase(uint8_t v)
: BasePrivateSerialize(v)
{
}
uint8_t z{};
};
template<typename S>
void serialize(S& s, DerivedPrivateBase& o) {
void
serialize(S& s, DerivedPrivateBase& o)
{
// use lambda for base serialization
s.ext(o, BaseClass<BasePrivateSerialize>{}, [](S& s, BasePrivateSerialize& b) {
s.object(b);
});
s.ext(o,
BaseClass<BasePrivateSerialize>{},
[](S& s, BasePrivateSerialize& b) { s.object(b); });
s.value1b(o.z);
}
struct BaseNonMemberSerialize {
struct BaseNonMemberSerialize
{
uint8_t x{};
};
template<typename S>
void serialize(S& s, BaseNonMemberSerialize& o) {
void
serialize(S& s, BaseNonMemberSerialize& o)
{
s.value1b(o.x);
}
struct DerivedMemberSerialize: public BaseNonMemberSerialize {
struct DerivedMemberSerialize : public BaseNonMemberSerialize
{
uint8_t z{};
template<typename S>
void serialize(S& s) {
void serialize(S& s)
{
s.ext(*this, BaseClass<BaseNonMemberSerialize>{});
s.value1b(z);
}
};
//explicitly select serialize functions, for types that has ambiguous serialize functions
// explicitly select serialize functions, for types that has ambiguous serialize
// functions
namespace bitsery {
template<>
struct SelectSerializeFnc<DerivedPrivateBase>:UseNonMemberFnc {};
struct SelectSerializeFnc<DerivedPrivateBase> : UseNonMemberFnc
{
};
template<>
struct SelectSerializeFnc<DerivedMemberSerialize>:UseMemberFnc {};
struct SelectSerializeFnc<DerivedMemberSerialize> : UseMemberFnc
{
};
}
TEST(SerializeExtensionInheritance, WhenDerivedClassHasAmbiguousSerializeFunctionThenExplicitlySelectSpecialization) {
TEST(
SerializeExtensionInheritance,
WhenDerivedClassHasAmbiguousSerializeFunctionThenExplicitlySelectSpecialization)
{
DerivedPrivateBase data1{ 43 };
data1.z = 87;
DerivedMemberSerialize data2{};
@@ -327,29 +390,34 @@ TEST(SerializeExtensionInheritance, WhenDerivedClassHasAmbiguousSerializeFunctio
EXPECT_THAT(res2.z, Eq(data2.z));
}
struct AbstractBase {
struct AbstractBase
{
uint8_t x{};
virtual void exec() = 0;
virtual ~AbstractBase() = default;
template<typename S>
void serialize(S& s) {
void serialize(S& s)
{
s.value1b(x);
}
};
struct ImplementedBase:AbstractBase {
struct ImplementedBase : AbstractBase
{
uint8_t y{};
void exec() override {}
template<typename S>
void serialize(S& s) {
void serialize(S& s)
{
s.ext(*this, BaseClass<AbstractBase>{});
s.value1b(y);
}
};
TEST(SerializeExtensionInheritance, CanSerializeAbstractClass) {
TEST(SerializeExtensionInheritance, CanSerializeAbstractClass)
{
ImplementedBase data{};
data.x = 4;
data.y = 2;

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,21 +20,22 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "serialization_test_utils.h"
#include <bitsery/ext/pointer.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using bitsery::ext::PointerOwner;
using bitsery::ext::PointerObserver;
using bitsery::ext::ReferencedByPointer;
using bitsery::ext::PointerLinkingContext;
using bitsery::ext::PointerObserver;
using bitsery::ext::PointerOwner;
using bitsery::ext::PointerType;
using bitsery::ext::ReferencedByPointer;
using testing::Eq;
using SerContext = BasicSerializationContext<PointerLinkingContext>;
class SerializeExtensionPointerSerialization : public testing::Test {
class SerializeExtensionPointerSerialization : public testing::Test
{
public:
// data used for serialization
int16_t d1{ 1597 };
@@ -60,22 +61,21 @@ public:
PointerLinkingContext plctx1{};
SerContext sctx1{};
typename SerContext::TSerializer& createSerializer() {
typename SerContext::TSerializer& createSerializer()
{
return sctx1.createSerializer(plctx1);
}
typename SerContext::TDeserializer& createDeserializer() {
typename SerContext::TDeserializer& createDeserializer()
{
return sctx1.createDeserializer(plctx1);
}
bool isPointerContextValid() {
return plctx1.isValid();
}
bool isPointerContextValid() { return plctx1.isValid(); }
};
TEST(SerializeExtensionPointer, RequiresPointerLinkingContext) {
TEST(SerializeExtensionPointer, RequiresPointerLinkingContext)
{
MyStruct1* data = nullptr;
// linking context
PointerLinkingContext plctx1{};
@@ -91,18 +91,34 @@ TEST(SerializeExtensionPointer, RequiresPointerLinkingContext) {
sctx2.createDeserializer(plctx2).ext(data, PointerObserver{});
}
TEST(SerializeExtensionPointer, PointerLinkingContextAcceptsMultipleSharedOwnersAndReturnSameId) {
TEST(SerializeExtensionPointer,
PointerLinkingContextAcceptsMultipleSharedOwnersAndReturnSameId)
{
MyStruct1 data{};
// pretend that this is shared ptr
MyStruct1* sharedPtr = &data;
// linking context
PointerLinkingContext plctx1{};
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::SharedOwner).id, Eq(1));
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::SharedObserver).id, Eq(1));
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::SharedOwner).id, Eq(1));
EXPECT_THAT(
plctx1
.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::SharedOwner)
.id,
Eq(1));
EXPECT_THAT(plctx1
.getInfoByPtr(
sharedPtr, bitsery::ext::PointerOwnershipType::SharedObserver)
.id,
Eq(1));
EXPECT_THAT(
plctx1
.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::SharedOwner)
.id,
Eq(1));
}
TEST(SerializeExtensionPointer, WhenOnlySharedObserverThenPointerLinkingContextIsInvalid) {
TEST(SerializeExtensionPointer,
WhenOnlySharedObserverThenPointerLinkingContextIsInvalid)
{
MyStruct1 data1{};
MyStruct1 data2{};
// pretend that this is shared ptr
@@ -110,16 +126,40 @@ TEST(SerializeExtensionPointer, WhenOnlySharedObserverThenPointerLinkingContextI
MyStruct1* sharedPtr2 = &data2;
// linking context
PointerLinkingContext plctx1{};
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr1, bitsery::ext::PointerOwnershipType::SharedObserver).id, Eq(1));
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr2, bitsery::ext::PointerOwnershipType::SharedObserver).id, Eq(2));
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr1, bitsery::ext::PointerOwnershipType::SharedObserver).id, Eq(1));
EXPECT_THAT(
plctx1
.getInfoByPtr(sharedPtr1,
bitsery::ext::PointerOwnershipType::SharedObserver)
.id,
Eq(1));
EXPECT_THAT(
plctx1
.getInfoByPtr(sharedPtr2,
bitsery::ext::PointerOwnershipType::SharedObserver)
.id,
Eq(2));
EXPECT_THAT(
plctx1
.getInfoByPtr(sharedPtr1,
bitsery::ext::PointerOwnershipType::SharedObserver)
.id,
Eq(1));
EXPECT_FALSE(plctx1.isValid());
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr1, bitsery::ext::PointerOwnershipType::SharedOwner).id, Eq(1));
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr2, bitsery::ext::PointerOwnershipType::SharedOwner).id, Eq(2));
EXPECT_THAT(
plctx1
.getInfoByPtr(sharedPtr1, bitsery::ext::PointerOwnershipType::SharedOwner)
.id,
Eq(1));
EXPECT_THAT(
plctx1
.getInfoByPtr(sharedPtr2, bitsery::ext::PointerOwnershipType::SharedOwner)
.id,
Eq(2));
EXPECT_TRUE(plctx1.isValid());
}
TEST_F(SerializeExtensionPointerSerialization, WhenPointersAreNullThenIsValid) {
TEST_F(SerializeExtensionPointerSerialization, WhenPointersAreNullThenIsValid)
{
auto& ser = createSerializer();
ser.ext2b(p1null, PointerOwner{});
@@ -134,7 +174,9 @@ TEST_F(SerializeExtensionPointerSerialization, WhenPointersAreNullThenIsValid) {
#ifndef NDEBUG
TEST_F(SerializeExtensionPointerSerialization, WhenPointerOwnerIsNotUniqueThenAssert) {
TEST_F(SerializeExtensionPointerSerialization,
WhenPointerOwnerIsNotUniqueThenAssert)
{
auto& ser = createSerializer();
ser.ext2b(p1null, PointerOwner{});
@@ -145,7 +187,9 @@ TEST_F(SerializeExtensionPointerSerialization, WhenPointerOwnerIsNotUniqueThenAs
EXPECT_DEATH(ser.ext2b(pd1, PointerOwner{}), "");
}
TEST_F(SerializeExtensionPointerSerialization, WhenRererencedByPointerIsSameAsPointerOwnerThenAssert1) {
TEST_F(SerializeExtensionPointerSerialization,
WhenRererencedByPointerIsSameAsPointerOwnerThenAssert1)
{
auto& ser1 = createSerializer();
ser1.ext4b(pd2, PointerOwner{});
ser1.ext(d3, ReferencedByPointer{});
@@ -153,14 +197,18 @@ TEST_F(SerializeExtensionPointerSerialization, WhenRererencedByPointerIsSameAsPo
EXPECT_DEATH(ser1.ext(pd3, PointerOwner{}), "");
}
TEST_F(SerializeExtensionPointerSerialization, WhenRererencedByPointerIsSameAsPointerOwnerThenAssert2) {
TEST_F(SerializeExtensionPointerSerialization,
WhenRererencedByPointerIsSameAsPointerOwnerThenAssert2)
{
auto& ser1 = createSerializer();
ser1.ext2b(pd1, PointerOwner{});
ser1.ext4b(d2, ReferencedByPointer{});
EXPECT_DEATH(ser1.ext2b(d1, ReferencedByPointer{}), "");
}
TEST_F(SerializeExtensionPointerSerialization, WhenNonNullPointerIsNullThenAssert) {
TEST_F(SerializeExtensionPointerSerialization,
WhenNonNullPointerIsNullThenAssert)
{
auto& ser1 = createSerializer();
EXPECT_DEATH(ser1.ext2b(p1null, PointerOwner{ PointerType::NotNull }), "");
EXPECT_DEATH(ser1.ext2b(p1null, PointerObserver{ PointerType::NotNull }), "");
@@ -168,32 +216,45 @@ TEST_F(SerializeExtensionPointerSerialization, WhenNonNullPointerIsNullThenAsser
#endif
TEST_F(SerializeExtensionPointerSerialization, WhenPointerObserverPointsToOwnerThenIsValid) {
TEST_F(SerializeExtensionPointerSerialization,
WhenPointerObserverPointsToOwnerThenIsValid)
{
auto& ser1 = createSerializer();
ser1.ext2b(pd1, PointerOwner{});
ser1.ext2b(p1null, PointerObserver{});
EXPECT_THAT(plctx1.isValid(), Eq(true));
ser1.ext4b(pd2, PointerObserver{});//points to d2, and d2 is not still marked as owner
ser1.ext4b(
pd2,
PointerObserver{}); // points to d2, and d2 is not still marked as owner
EXPECT_THAT(plctx1.isValid(), Eq(false));
ser1.ext4b(pd2, PointerOwner{}); // now d2 is owning pointer
ser1.ext4b(pd2, PointerObserver{});//points to d2, but this time d2 has owner
ser1.ext4b(pd2, PointerObserver{}); // points to d2, but this time d2 has
// owner
ser1.ext2b(p1null, PointerObserver{});
EXPECT_THAT(plctx1.isValid(), Eq(true));
}
TEST_F(SerializeExtensionPointerSerialization, ReferenceTypeCanAlsoBeReferencedByPointerObservers) {
TEST_F(SerializeExtensionPointerSerialization,
ReferenceTypeCanAlsoBeReferencedByPointerObservers)
{
auto& ser1 = createSerializer();
ser1.ext2b(p1null, PointerObserver{});
EXPECT_THAT(plctx1.isValid(), Eq(true));
ser1.ext4b(pd2, PointerObserver{});//points to d2, and d2 is not still marked as owner
ser1.ext4b(
pd2,
PointerObserver{}); // points to d2, and d2 is not still marked as owner
EXPECT_THAT(plctx1.isValid(), Eq(false));
ser1.ext4b(d2, ReferencedByPointer{});//now d2 is marked by marked as owning pointer
ser1.ext4b(pd2, PointerObserver{});//points to d2, but this time d2 has owner
ser1.ext4b(
d2, ReferencedByPointer{}); // now d2 is marked by marked as owning pointer
ser1.ext4b(pd2, PointerObserver{}); // points to d2, but this time d2 has
// owner
ser1.ext(p3null, PointerObserver{});
EXPECT_THAT(plctx1.isValid(), Eq(true));
}
TEST_F(SerializeExtensionPointerSerialization, WhenPointerIsNullThenPointerIdIsZero) {
TEST_F(SerializeExtensionPointerSerialization,
WhenPointerIsNullThenPointerIdIsZero)
{
auto& ser1 = createSerializer();
ser1.ext(p3null, PointerOwner{});
ser1.ext2b(p1null, PointerObserver{});
@@ -206,7 +267,8 @@ TEST_F(SerializeExtensionPointerSerialization, WhenPointerIsNullThenPointerIdIsZ
EXPECT_THAT(res, Eq(0));
}
TEST_F(SerializeExtensionPointerSerialization, PointerIdsStartsFromOne) {
TEST_F(SerializeExtensionPointerSerialization, PointerIdsStartsFromOne)
{
auto& ser1 = createSerializer();
ser1.ext2b(pd1, PointerObserver{});
ser1.ext4b(pd2, PointerObserver{});
@@ -225,7 +287,9 @@ TEST_F(SerializeExtensionPointerSerialization, PointerIdsStartsFromOne) {
EXPECT_THAT(res, Eq(0));
}
TEST_F(SerializeExtensionPointerSerialization, PointerObserversDoesntSerializeObject) {
TEST_F(SerializeExtensionPointerSerialization,
PointerObserversDoesntSerializeObject)
{
auto& ser1 = createSerializer();
ser1.ext2b(pd1, PointerObserver{});
ser1.ext4b(pd2, PointerObserver{});
@@ -234,7 +298,9 @@ TEST_F(SerializeExtensionPointerSerialization, PointerObserversDoesntSerializeOb
EXPECT_THAT(sctx1.ser->adapter().writtenBytesCount(), Eq(3));
}
TEST_F(SerializeExtensionPointerSerialization, ReferencedByPointerSerializesIdAndObject) {
TEST_F(SerializeExtensionPointerSerialization,
ReferencedByPointerSerializesIdAndObject)
{
auto& ser1 = createSerializer();
ser1.ext2b(d1, ReferencedByPointer{});
ser1.ext4b(d2, ReferencedByPointer{});
@@ -254,13 +320,16 @@ TEST_F(SerializeExtensionPointerSerialization, ReferencedByPointerSerializesIdAn
EXPECT_THAT(id, Eq(2));
}
TEST_F(SerializeExtensionPointerSerialization, PointerOwnerSerializesIdAndObject) {
TEST_F(SerializeExtensionPointerSerialization,
PointerOwnerSerializesIdAndObject)
{
auto& ser1 = createSerializer();
ser1.ext4b(pd2, PointerOwner{});
ser1.ext(pd3, PointerOwner{});
auto& des1 = createDeserializer();
// 2x ids + int32_t + MyStruct1
EXPECT_THAT(sctx1.ser->adapter().writtenBytesCount(), Eq(2 + 4 + MyStruct1::SIZE));
EXPECT_THAT(sctx1.ser->adapter().writtenBytesCount(),
Eq(2 + 4 + MyStruct1::SIZE));
size_t id;
bitsery::details::readSize(sctx1.des->adapter(), id, 0, std::false_type{});
des1.value4b(r2);
@@ -270,12 +339,14 @@ TEST_F(SerializeExtensionPointerSerialization, PointerOwnerSerializesIdAndObject
EXPECT_THAT(r3, Eq(*pd3));
}
class SerializeExtensionPointerDeserialization : public SerializeExtensionPointerSerialization {
class SerializeExtensionPointerDeserialization
: public SerializeExtensionPointerSerialization
{
public:
};
TEST_F(SerializeExtensionPointerDeserialization, ReferencedByPointer) {
TEST_F(SerializeExtensionPointerDeserialization, ReferencedByPointer)
{
auto& ser = createSerializer();
ser.ext2b(d1, ReferencedByPointer{});
ser.ext4b(d2, ReferencedByPointer{});
@@ -290,28 +361,36 @@ TEST_F(SerializeExtensionPointerDeserialization, ReferencedByPointer) {
EXPECT_THAT(r3, Eq(d3));
}
TEST_F(SerializeExtensionPointerDeserialization, WhenReferencedByPointerReadsNullPointerThenInvalidPointerError) {
TEST_F(SerializeExtensionPointerDeserialization,
WhenReferencedByPointerReadsNullPointerThenInvalidPointerError)
{
auto& ser = createSerializer();
bitsery::details::writeSize(sctx1.ser->adapter(), 0u);
ser.ext2b(d1, ReferencedByPointer{});
auto& des = createDeserializer();
des.ext2b(r1, ReferencedByPointer{});
EXPECT_THAT(sctx1.des->adapter().error(), Eq(bitsery::ReaderError::InvalidPointer));
EXPECT_THAT(sctx1.des->adapter().error(),
Eq(bitsery::ReaderError::InvalidPointer));
}
TEST_F(SerializeExtensionPointerDeserialization, WhenNonNullPointerIsNullThenInvalidPointerError) {
TEST_F(SerializeExtensionPointerDeserialization,
WhenNonNullPointerIsNullThenInvalidPointerError)
{
createSerializer();
bitsery::details::writeSize(sctx1.ser->adapter(), 0u);
auto& des1 = createDeserializer();
des1.ext2b(p1null, PointerOwner{ PointerType::NotNull });
EXPECT_THAT(sctx1.des->adapter().error(), Eq(bitsery::ReaderError::InvalidPointer));
EXPECT_THAT(sctx1.des->adapter().error(),
Eq(bitsery::ReaderError::InvalidPointer));
auto& des2 = createDeserializer();
des2.ext2b(p1null, PointerObserver{ PointerType::NotNull });
EXPECT_THAT(sctx1.des->adapter().error(), Eq(bitsery::ReaderError::InvalidPointer));
EXPECT_THAT(sctx1.des->adapter().error(),
Eq(bitsery::ReaderError::InvalidPointer));
}
TEST_F(SerializeExtensionPointerDeserialization, PointerOwnerCreatesObjects) {
TEST_F(SerializeExtensionPointerDeserialization, PointerOwnerCreatesObjects)
{
auto& ser = createSerializer();
ser.ext2b(pd1, PointerOwner{});
ser.ext4b(pd2, PointerOwner{});
@@ -330,7 +409,8 @@ TEST_F(SerializeExtensionPointerDeserialization, PointerOwnerCreatesObjects) {
EXPECT_THAT(*p3null, Eq(*pd3));
}
TEST_F(SerializeExtensionPointerDeserialization, PointerOwnerDestroysObjects) {
TEST_F(SerializeExtensionPointerDeserialization, PointerOwnerDestroysObjects)
{
auto& ser = createSerializer();
ser.ext2b(p1null, PointerOwner{});
ser.ext4b(p2null, PointerOwner{});
@@ -350,7 +430,8 @@ TEST_F(SerializeExtensionPointerDeserialization, PointerOwnerDestroysObjects) {
EXPECT_THAT(pr3, ::testing::IsNull());
}
TEST_F(SerializeExtensionPointerDeserialization, PointerObserver) {
TEST_F(SerializeExtensionPointerDeserialization, PointerObserver)
{
auto& ser = createSerializer();
// first owner, than observer
ser.ext4b(d2, ReferencedByPointer{});
@@ -377,7 +458,8 @@ TEST_F(SerializeExtensionPointerDeserialization, PointerObserver) {
EXPECT_THAT(pr3, Eq(&r3));
}
struct Test1Data {
struct Test1Data
{
std::vector<MyStruct1> vdata;
std::vector<MyStruct1*> vptr;
MyStruct1 o1;
@@ -386,19 +468,18 @@ struct Test1Data {
int32_t* pi1;
template<typename S>
void serialize(S &s) {
void serialize(S& s)
{
// set container elements to be candidates for non-owning pointers
s.container(vdata, 100, [](S& s, MyStruct1 &d) {
s.ext(d, ReferencedByPointer{});
});
s.container(
vdata, 100, [](S& s, MyStruct1& d) { s.ext(d, ReferencedByPointer{}); });
// contains non owning pointers
//
// IMPORTANT !!!
// ALWAYS ACCEPT BY REFERENCE like this: T* (&obj)
//
s.container(vptr, 100, [](S& s, MyStruct1 *(&d)) {
s.ext(d, PointerObserver{});
});
s.container(
vptr, 100, [](S& s, MyStruct1*(&d)) { s.ext(d, PointerObserver{}); });
// just a regular fields
s.object(o1);
s.value4b(i1);
@@ -406,11 +487,11 @@ struct Test1Data {
s.ext(po1, PointerObserver{});
// owner
s.ext4b(pi1, PointerOwner{});
}
};
TEST(SerializeExtensionPointer, IntegrationTest) {
TEST(SerializeExtensionPointer, IntegrationTest)
{
Test1Data data{};
data.vdata.push_back({ 165, -45 });
@@ -455,7 +536,9 @@ TEST(SerializeExtensionPointer, IntegrationTest) {
delete res.pi1;
}
TEST(SerializeExtensionPointer, PointerOwnerWithNonPolymorphicTypeCanUseLambdaOverload) {
TEST(SerializeExtensionPointer,
PointerOwnerWithNonPolymorphicTypeCanUseLambdaOverload)
{
const int32_t NEW_VALUE = 2;
const int32_t OLD_VALUE = 1;
MyStruct1* data = new MyStruct1{ NEW_VALUE, NEW_VALUE };
@@ -481,7 +564,8 @@ TEST(SerializeExtensionPointer, PointerOwnerWithNonPolymorphicTypeCanUseLambdaOv
delete res;
}
TEST(SerializeExtensionPointer, ReferencedByPointerCanUseLambdaOverload) {
TEST(SerializeExtensionPointer, ReferencedByPointerCanUseLambdaOverload)
{
const int32_t NEW_VALUE = 2;
const int32_t OLD_VALUE = 1;
MyStruct1 data = MyStruct1{ NEW_VALUE, NEW_VALUE };
@@ -504,7 +588,8 @@ TEST(SerializeExtensionPointer, ReferencedByPointerCanUseLambdaOverload) {
EXPECT_THAT(res.i2, Eq(OLD_VALUE)); // we didn't serialized that
}
TEST(SerializeExtensionPointer, PointerOwnerCanUseValueOverload) {
TEST(SerializeExtensionPointer, PointerOwnerCanUseValueOverload)
{
auto* data = new int64_t{ 49845894 };
auto* res = new int64_t{ -78548415 };
@@ -519,7 +604,8 @@ TEST(SerializeExtensionPointer, PointerOwnerCanUseValueOverload) {
delete res;
}
TEST(SerializeExtensionPointer, ReferencedByPointerCanUseValueOverload) {
TEST(SerializeExtensionPointer, ReferencedByPointerCanUseValueOverload)
{
int64_t data{ 49845894 };
int64_t res{ -78548415 };

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,12 +20,11 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <bitsery/ext/inheritance.h>
#include <bitsery/ext/pointer.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
using bitsery::ext::BaseClass;
using bitsery::ext::VirtualBaseClass;
@@ -35,59 +34,75 @@ using bitsery::ext::PointerLinkingContext;
using bitsery::ext::PolymorphicContext;
using bitsery::ext::StandardRTTI;
using bitsery::ext::PointerOwner;
using bitsery::ext::PointerObserver;
using bitsery::ext::PointerOwner;
using bitsery::ext::ReferencedByPointer;
using testing::Eq;
using TContext = std::tuple<PointerLinkingContext, InheritanceContext, PolymorphicContext<StandardRTTI>>;
using TContext = std::tuple<PointerLinkingContext,
InheritanceContext,
PolymorphicContext<StandardRTTI>>;
using SerContext = BasicSerializationContext<TContext>;
//this is useful for PolymorphicContext to bind classes to serializer/deserializer
// this is useful for PolymorphicContext to bind classes to
// serializer/deserializer
using TSerializer = typename SerContext::TSerializer;
using TDeserializer = typename SerContext::TDeserializer;
/*
* base class
*/
struct Base {
struct Base
{
uint8_t x{};
virtual ~Base() = default;
};
template<typename S>
void serialize(S &s, Base &o) {
void
serialize(S& s, Base& o)
{
s.value1b(o.x);
}
struct Derived1 : virtual Base {
struct Derived1 : virtual Base
{
uint8_t y1{};
};
template<typename S>
void serialize(S &s, Derived1 &o) {
void
serialize(S& s, Derived1& o)
{
s.ext(o, VirtualBaseClass<Base>{});
s.value1b(o.y1);
}
struct Derived2 : virtual Base {
struct Derived2 : virtual Base
{
uint8_t y2{};
};
template<typename S>
void serialize(S &s, Derived2 &o) {
void
serialize(S& s, Derived2& o)
{
s.ext(o, VirtualBaseClass<Base>{});
s.value1b(o.y2);
}
struct MultipleVirtualInheritance : Derived1, Derived2 {
struct MultipleVirtualInheritance
: Derived1
, Derived2
{
int8_t z{};
MultipleVirtualInheritance() = default;
MultipleVirtualInheritance(uint8_t x_, uint8_t y1_, uint8_t y2_, int8_t z_) {
MultipleVirtualInheritance(uint8_t x_, uint8_t y1_, uint8_t y2_, int8_t z_)
{
x = x_;
y1 = y1_;
y2 = y2_;
@@ -95,85 +110,95 @@ struct MultipleVirtualInheritance : Derived1, Derived2 {
}
template<typename S>
void serialize(S &s) {
void serialize(S& s)
{
s.ext(*this, BaseClass<Derived1>{});
s.ext(*this, BaseClass<Derived2>{});
s.value1b(z);
}
};
// this class has no relationships specified via PolymorphicBaseClass
struct NoRelationshipSpecifiedDerived : Base {
};
struct NoRelationshipSpecifiedDerived : Base
{};
//these classes will be used to "cheat" a little bit when testing deserialization flows
struct BaseClone {
// these classes will be used to "cheat" a little bit when testing
// deserialization flows
struct BaseClone
{
uint8_t x{};
virtual ~BaseClone() = default;
};
template<typename S>
void serialize(S &s, BaseClone &o) {
void
serialize(S& s, BaseClone& o)
{
s.value1b(o.x);
}
//define relationships between base class and derived classes for runtime polymorphism
// define relationships between base class and derived classes for runtime
// polymorphism
namespace bitsery {
namespace ext {
template<>
struct PolymorphicBaseClass<Base> : PolymorphicDerivedClasses<Derived1, Derived2> {
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
// 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> {
// struct PolymorphicBaseClass<Derived1> :
// PolymorphicDerivedClasses<MultipleVirtualInheritance> {
// };
template<>
struct PolymorphicBaseClass<Derived2> : PolymorphicDerivedClasses<MultipleVirtualInheritance> {
struct PolymorphicBaseClass<Derived2>
: PolymorphicDerivedClasses<MultipleVirtualInheritance>
{
};
}
}
class SerializeExtensionPointerPolymorphicTypes : public testing::Test {
class SerializeExtensionPointerPolymorphicTypes : public testing::Test
{
public:
TContext plctx{};
SerContext sctx{};
typename SerContext::TSerializer& createSerializer() {
typename SerContext::TSerializer& createSerializer()
{
auto& res = sctx.createSerializer(plctx);
std::get<2>(plctx).clear();
// bind serializer with classes
std::get<2>(plctx).registerBasesList<SerContext::TSerializer>(bitsery::ext::PolymorphicClassesList<Base>{});
std::get<2>(plctx).registerBasesList<SerContext::TSerializer>(
bitsery::ext::PolymorphicClassesList<Base>{});
return res;
}
typename SerContext::TDeserializer& createDeserializer() {
typename SerContext::TDeserializer& createDeserializer()
{
auto& res = sctx.createDeserializer(plctx);
std::get<2>(plctx).clear();
// bind deserializer with classes
std::get<2>(plctx).registerBasesList<SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<Base>{});
std::get<2>(plctx).registerBasesList<SerContext::TDeserializer>(
bitsery::ext::PolymorphicClassesList<Base>{});
return res;
}
bool isPointerContextValid() {
return std::get<0>(plctx).isValid();
}
bool isPointerContextValid() { return std::get<0>(plctx).isValid(); }
virtual void TearDown() override {
EXPECT_TRUE(isPointerContextValid());
}
virtual void TearDown() override { EXPECT_TRUE(isPointerContextValid()); }
};
TEST_F(SerializeExtensionPointerPolymorphicTypes, Data0Result0) {
TEST_F(SerializeExtensionPointerPolymorphicTypes, Data0Result0)
{
Base* baseData = nullptr;
createSerializer().ext(baseData, PointerOwner{});
Base* baseRes = nullptr;
@@ -183,7 +208,8 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes, Data0Result0) {
EXPECT_THAT(baseData, ::testing::IsNull());
}
TEST_F(SerializeExtensionPointerPolymorphicTypes, Data0Result1) {
TEST_F(SerializeExtensionPointerPolymorphicTypes, Data0Result1)
{
Base* baseData = nullptr;
createSerializer().ext(baseData, PointerOwner{});
@@ -194,7 +220,8 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes, Data0Result1) {
EXPECT_THAT(baseData, ::testing::IsNull());
}
TEST_F(SerializeExtensionPointerPolymorphicTypes, Data1Result0) {
TEST_F(SerializeExtensionPointerPolymorphicTypes, Data1Result0)
{
Derived1 d1{};
d1.x = 3;
d1.y1 = 78;
@@ -214,7 +241,8 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes, Data1Result0) {
delete baseRes;
}
TEST_F(SerializeExtensionPointerPolymorphicTypes, Data1Result1) {
TEST_F(SerializeExtensionPointerPolymorphicTypes, Data1Result1)
{
Derived1 d1{};
d1.x = 3;
d1.y1 = 78;
@@ -233,7 +261,9 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes, Data1Result1) {
EXPECT_THAT(res->y1, Eq(data->y1));
}
TEST_F(SerializeExtensionPointerPolymorphicTypes, ComplexTypeWithVirtualInheritanceData1Result0) {
TEST_F(SerializeExtensionPointerPolymorphicTypes,
ComplexTypeWithVirtualInheritanceData1Result0)
{
MultipleVirtualInheritance md1{};
md1.x = 3;
md1.y1 = 78;
@@ -257,7 +287,9 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes, ComplexTypeWithVirtualInherita
delete baseRes;
}
TEST_F(SerializeExtensionPointerPolymorphicTypes, WhenResultIsDifferentTypeThenRecreate) {
TEST_F(SerializeExtensionPointerPolymorphicTypes,
WhenResultIsDifferentTypeThenRecreate)
{
MultipleVirtualInheritance md1{};
md1.x = 3;
md1.y1 = 78;
@@ -266,24 +298,31 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes, WhenResultIsDifferentTypeThenR
Base* baseData = &md1;
createSerializer().ext(baseData, PointerOwner{});
Base* baseRes = new Derived1{};
EXPECT_THAT(dynamic_cast<MultipleVirtualInheritance *>(baseRes), ::testing::IsNull());
EXPECT_THAT(dynamic_cast<MultipleVirtualInheritance*>(baseRes),
::testing::IsNull());
createDeserializer().ext(baseRes, PointerOwner{});
EXPECT_THAT(dynamic_cast<MultipleVirtualInheritance *>(baseRes), ::testing::NotNull());
EXPECT_THAT(dynamic_cast<MultipleVirtualInheritance*>(baseRes),
::testing::NotNull());
delete baseRes;
}
#ifndef NDEBUG
TEST_F(SerializeExtensionPointerPolymorphicTypes,
WhenSerializingDerivedTypeWithoutSpecifiedRelationshipsWithBaseThenAssert) {
TEST_F(
SerializeExtensionPointerPolymorphicTypes,
WhenSerializingDerivedTypeWithoutSpecifiedRelationshipsWithBaseThenAssert)
{
NoRelationshipSpecifiedDerived md1;//this class has no relationships specified via PolymorphicBaseClass
NoRelationshipSpecifiedDerived
md1; // this class has no relationships specified via PolymorphicBaseClass
Base* baseData = &md1;
EXPECT_DEATH(createSerializer().ext(baseData, PointerOwner{}), "");
}
TEST_F(SerializeExtensionPointerPolymorphicTypes,
WhenDeserializingDerivedTypeNotRegisteredWithPolymorphicContextThenAssert) {
TEST_F(
SerializeExtensionPointerPolymorphicTypes,
WhenDeserializingDerivedTypeNotRegisteredWithPolymorphicContextThenAssert)
{
Derived1 d1{};
Base* baseData = &d1;
@@ -295,34 +334,42 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes,
#endif
TEST_F(SerializeExtensionPointerPolymorphicTypes,
CompileTimeTypeIsDerivedAndReachableFromBaseRegisteredWithPolymorphicContext) {
TEST_F(
SerializeExtensionPointerPolymorphicTypes,
CompileTimeTypeIsDerivedAndReachableFromBaseRegisteredWithPolymorphicContext)
{
MultipleVirtualInheritance md;
Derived2 *derivedData = &md;//this class is not registered via PolymorphicContext
Derived2* derivedData =
&md; // this class is not registered via PolymorphicContext
createSerializer().ext(derivedData, PointerOwner{});
Derived2* derivedRes = new Derived2{};
EXPECT_THAT(dynamic_cast<MultipleVirtualInheritance *>(derivedRes), ::testing::IsNull());
EXPECT_THAT(dynamic_cast<MultipleVirtualInheritance*>(derivedRes),
::testing::IsNull());
createDeserializer().ext(derivedRes, PointerOwner{});
EXPECT_THAT(dynamic_cast<MultipleVirtualInheritance *>(derivedRes), ::testing::NotNull());
EXPECT_THAT(dynamic_cast<MultipleVirtualInheritance*>(derivedRes),
::testing::NotNull());
delete derivedRes;
}
TEST_F(SerializeExtensionPointerPolymorphicTypes,
WhenPolymorphicTypeNotFoundDuringDeserializionThenInvalidPointerError) {
WhenPolymorphicTypeNotFoundDuringDeserializionThenInvalidPointerError)
{
Derived1 d1{};
Base* baseData = &d1;
createSerializer().ext(baseData, PointerOwner{});
BaseClone *baseRes = nullptr; //this class will be registered, but it doesn't have relationships specified via PolymorphicBaseClass
BaseClone* baseRes =
nullptr; // this class will be registered, but it doesn't have relationships
// specified via PolymorphicBaseClass
auto& des = sctx.createDeserializer(plctx);
auto& pc = std::get<2>(plctx);
pc.clear();
pc.registerBasesList<SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<BaseClone>{});
pc.registerBasesList<SerContext::TDeserializer>(
bitsery::ext::PolymorphicClassesList<BaseClone>{});
des.ext(baseRes, PointerOwner{});
EXPECT_THAT(sctx.des->adapter().error(), Eq(bitsery::ReaderError::InvalidPointer));
EXPECT_THAT(sctx.des->adapter().error(),
Eq(bitsery::ReaderError::InvalidPointer));
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,13 +20,12 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <bitsery/ext/inheritance.h>
#include <bitsery/ext/pointer.h>
#include <bitsery/ext/std_smart_ptr.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
using bitsery::ext::BaseClass;
using bitsery::ext::VirtualBaseClass;
@@ -36,27 +35,34 @@ using bitsery::ext::PointerLinkingContext;
using bitsery::ext::PolymorphicContext;
using bitsery::ext::StandardRTTI;
using bitsery::ext::PointerOwner;
using bitsery::ext::PointerObserver;
using bitsery::ext::PointerOwner;
using bitsery::ext::ReferencedByPointer;
using bitsery::ext::StdSmartPtr;
using testing::Eq;
using TContext = std::tuple<PointerLinkingContext, InheritanceContext, PolymorphicContext<StandardRTTI>>;
using TContext = std::tuple<PointerLinkingContext,
InheritanceContext,
PolymorphicContext<StandardRTTI>>;
using SerContext = BasicSerializationContext<TContext>;
//this is useful for PolymorphicContext to bind classes to serializer/deserializer
// this is useful for PolymorphicContext to bind classes to
// serializer/deserializer
using TSerializer = typename SerContext::TSerializer;
using TDeserializer = typename SerContext::TDeserializer;
/*
* base class
*/
struct Base {
struct Base
{
Base() = default;
explicit Base(uint64_t v) : x{v} {}
explicit Base(uint64_t v)
: x{ v }
{
}
uint64_t x{};
@@ -64,16 +70,24 @@ struct Base {
};
template<typename S>
void serialize(S& s, Base& o) {
void
serialize(S& s, Base& o)
{
s.value8b(o.x);
}
struct Derived1 : Base {
struct Derived1 : Base
{
Derived1() = default;
Derived1(uint64_t x_, uint64_t y_) : Base{x_}, y1{y_} {}
Derived1(uint64_t x_, uint64_t y_)
: Base{ x_ }
, y1{ y_ }
{
}
friend bool operator==(const Derived1& lhs, const Derived1& rhs) {
friend bool operator==(const Derived1& lhs, const Derived1& rhs)
{
return lhs.x == rhs.x && lhs.y1 == rhs.y1;
}
@@ -81,98 +95,123 @@ struct Derived1 : Base {
};
template<typename S>
void serialize(S& s, Derived1& o) {
void
serialize(S& s, Derived1& o)
{
s.ext(o, BaseClass<Base>{});
s.value8b(o.y1);
}
struct Derived2 : Base {
struct Derived2 : Base
{
uint64_t y1{};
uint64_t y2{};
};
template<typename S>
void serialize(S& s, Derived2& o) {
void
serialize(S& s, Derived2& o)
{
s.ext(o, BaseClass<Base>{});
s.value8b(o.y1);
s.value8b(o.y2);
}
// polymorphic structure that contains polymorphic pointer, to test memory resource propagation
struct PolyPtrWithPolyPtrBase {
// polymorphic structure that contains polymorphic pointer, to test memory
// resource propagation
struct PolyPtrWithPolyPtrBase
{
std::unique_ptr<Base> ptr{};
virtual ~PolyPtrWithPolyPtrBase() = default;
};
template<typename S>
void serialize(S& s, PolyPtrWithPolyPtrBase& o) {
void
serialize(S& s, PolyPtrWithPolyPtrBase& o)
{
s.ext(o.ptr, StdSmartPtr{});
}
struct DerivedPolyPtrWithPolyPtr : PolyPtrWithPolyPtrBase {
};
struct DerivedPolyPtrWithPolyPtr : PolyPtrWithPolyPtrBase
{};
template<typename S>
void serialize(S& s, DerivedPolyPtrWithPolyPtr& o) {
void
serialize(S& s, DerivedPolyPtrWithPolyPtr& o)
{
s.ext(o.ptr, StdSmartPtr{});
}
//define relationships between base class and derived classes for runtime polymorphism
// define relationships between base class and derived classes for runtime
// polymorphism
namespace bitsery {
namespace ext {
template<>
struct PolymorphicBaseClass<Base> : PolymorphicDerivedClasses<Derived1, Derived2> {
struct PolymorphicBaseClass<Base>
: PolymorphicDerivedClasses<Derived1, Derived2>
{
};
template<>
struct PolymorphicBaseClass<PolyPtrWithPolyPtrBase> : PolymorphicDerivedClasses<DerivedPolyPtrWithPolyPtr> {
struct PolymorphicBaseClass<PolyPtrWithPolyPtrBase>
: PolymorphicDerivedClasses<DerivedPolyPtrWithPolyPtr>
{
};
}
}
// this class is for testing
struct TestAllocInfo {
struct TestAllocInfo
{
void* ptr;
size_t bytes;
size_t alignment;
size_t typeId;
friend bool operator==(const TestAllocInfo& lhs, const TestAllocInfo& rhs) {
friend bool operator==(const TestAllocInfo& lhs, const TestAllocInfo& rhs)
{
return std::tie(lhs.ptr, lhs.bytes, lhs.alignment, lhs.typeId) ==
std::tie(rhs.ptr, rhs.bytes, rhs.alignment, rhs.typeId);
}
};
struct MemResourceForTest : public bitsery::ext::MemResourceBase {
struct MemResourceForTest : public bitsery::ext::MemResourceBase
{
void* allocate(size_t bytes, size_t alignment, size_t typeId) override {
const auto res = bitsery::ext::MemResourceNewDelete{}.allocate(bytes, alignment, typeId);
void* allocate(size_t bytes, size_t alignment, size_t typeId) override
{
const auto res =
bitsery::ext::MemResourceNewDelete{}.allocate(bytes, alignment, typeId);
allocs.push_back({ res, bytes, alignment, typeId });
return res;
}
void deallocate(void* ptr, size_t bytes, size_t alignment, size_t typeId) noexcept override {
void deallocate(void* ptr,
size_t bytes,
size_t alignment,
size_t typeId) noexcept override
{
deallocs.push_back({ ptr, bytes, alignment, typeId });
bitsery::ext::MemResourceNewDelete{}.deallocate(ptr, bytes, alignment, typeId);
bitsery::ext::MemResourceNewDelete{}.deallocate(
ptr, bytes, alignment, typeId);
}
std::vector<TestAllocInfo> allocs{};
std::vector<TestAllocInfo> deallocs{};
};
class SerializeExtensionPointerWithAllocator : public testing::Test {
class SerializeExtensionPointerWithAllocator : public testing::Test
{
public:
TContext plctx{};
SerContext sctx{};
typename SerContext::TSerializer& createSerializer() {
typename SerContext::TSerializer& createSerializer()
{
auto& res = sctx.createSerializer(plctx);
std::get<2>(plctx).clear();
// bind serializer with classes
@@ -181,7 +220,8 @@ public:
return res;
}
typename SerContext::TDeserializer& createDeserializer() {
typename SerContext::TDeserializer& createDeserializer()
{
auto& res = sctx.createDeserializer(plctx);
std::get<2>(plctx).clear();
// bind deserializer with classes
@@ -190,16 +230,14 @@ public:
return res;
}
bool isPointerContextValid() {
return std::get<0>(plctx).isValid();
}
bool isPointerContextValid() { return std::get<0>(plctx).isValid(); }
virtual void TearDown() override {
EXPECT_TRUE(isPointerContextValid());
}
virtual void TearDown() override { EXPECT_TRUE(isPointerContextValid()); }
};
TEST_F(SerializeExtensionPointerWithAllocator, CanSetDefaultMemoryResourceInPointerLinkingContext) {
TEST_F(SerializeExtensionPointerWithAllocator,
CanSetDefaultMemoryResourceInPointerLinkingContext)
{
MemResourceForTest memRes{};
std::get<0>(plctx).setMemResource(&memRes);
@@ -217,13 +255,16 @@ TEST_F(SerializeExtensionPointerWithAllocator, CanSetDefaultMemoryResourceInPoin
EXPECT_THAT(memRes.allocs.size(), Eq(1u));
EXPECT_THAT(memRes.allocs[0].bytes, Eq(sizeof(Derived1)));
EXPECT_THAT(memRes.allocs[0].alignment, Eq(alignof(Derived1)));
EXPECT_THAT(memRes.allocs[0].typeId, Eq(bitsery::ext::StandardRTTI::get<Derived1>()));
EXPECT_THAT(memRes.allocs[0].typeId,
Eq(bitsery::ext::StandardRTTI::get<Derived1>()));
EXPECT_THAT(memRes.deallocs.size(), Eq(0u));
delete dData;
delete dRes;
}
TEST_F(SerializeExtensionPointerWithAllocator, CorrectlyDeallocatesPreviousInstance) {
TEST_F(SerializeExtensionPointerWithAllocator,
CorrectlyDeallocatesPreviousInstance)
{
MemResourceForTest memRes{};
std::get<0>(plctx).setMemResource(&memRes);
@@ -240,17 +281,21 @@ TEST_F(SerializeExtensionPointerWithAllocator, CorrectlyDeallocatesPreviousInsta
EXPECT_THAT(memRes.allocs.size(), Eq(1u));
EXPECT_THAT(memRes.allocs[0].bytes, Eq(sizeof(Derived1)));
EXPECT_THAT(memRes.allocs[0].alignment, Eq(alignof(Derived1)));
EXPECT_THAT(memRes.allocs[0].typeId, Eq(bitsery::ext::StandardRTTI::get<Derived1>()));
EXPECT_THAT(memRes.allocs[0].typeId,
Eq(bitsery::ext::StandardRTTI::get<Derived1>()));
EXPECT_THAT(memRes.deallocs.size(), Eq(1u));
EXPECT_THAT(memRes.deallocs[0].bytes, Eq(sizeof(Derived2)));
EXPECT_THAT(memRes.deallocs[0].alignment, Eq(alignof(Derived2)));
EXPECT_THAT(memRes.deallocs[0].typeId, Eq(bitsery::ext::StandardRTTI::get<Derived2>()));
EXPECT_THAT(memRes.deallocs[0].typeId,
Eq(bitsery::ext::StandardRTTI::get<Derived2>()));
delete dData;
delete dRes;
}
TEST_F(SerializeExtensionPointerWithAllocator, DefaultDeleterIsNotUsedForStdUniquePtr) {
TEST_F(SerializeExtensionPointerWithAllocator,
DefaultDeleterIsNotUsedForStdUniquePtr)
{
MemResourceForTest memRes{};
std::get<0>(plctx).setMemResource(&memRes);
@@ -263,41 +308,47 @@ TEST_F(SerializeExtensionPointerWithAllocator, DefaultDeleterIsNotUsedForStdUniq
EXPECT_THAT(memRes.deallocs.size(), Eq(1u));
EXPECT_THAT(memRes.deallocs[0].bytes, Eq(sizeof(Derived1)));
EXPECT_THAT(memRes.deallocs[0].alignment, Eq(alignof(Derived1)));
EXPECT_THAT(memRes.deallocs[0].typeId, Eq(bitsery::ext::StandardRTTI::get<Derived1>()));
EXPECT_THAT(memRes.deallocs[0].typeId,
Eq(bitsery::ext::StandardRTTI::get<Derived1>()));
}
struct CustomBaseDeleter {
void operator()(Base* obj) {
delete obj;
}
struct CustomBaseDeleter
{
void operator()(Base* obj) { delete obj; }
};
TEST_F(SerializeExtensionPointerWithAllocator, CustomDeleterIsNotUsedForStdUniquePtr) {
TEST_F(SerializeExtensionPointerWithAllocator,
CustomDeleterIsNotUsedForStdUniquePtr)
{
MemResourceForTest memRes{};
std::get<0>(plctx).setMemResource(&memRes);
std::unique_ptr<Base, CustomBaseDeleter> baseData{};
createSerializer().ext(baseData, StdSmartPtr{});
auto baseRes = std::unique_ptr<Base, CustomBaseDeleter>(new Derived1{45, 64});
auto baseRes =
std::unique_ptr<Base, CustomBaseDeleter>(new Derived1{ 45, 64 });
createDeserializer().ext(baseRes, StdSmartPtr{});
EXPECT_THAT(memRes.allocs.size(), Eq(0u));
EXPECT_THAT(memRes.deallocs.size(), Eq(1u));
EXPECT_THAT(memRes.deallocs[0].bytes, Eq(sizeof(Derived1)));
EXPECT_THAT(memRes.deallocs[0].alignment, Eq(alignof(Derived1)));
EXPECT_THAT(memRes.deallocs[0].typeId, Eq(bitsery::ext::StandardRTTI::get<Derived1>()));
EXPECT_THAT(memRes.deallocs[0].typeId,
Eq(bitsery::ext::StandardRTTI::get<Derived1>()));
}
TEST_F(SerializeExtensionPointerWithAllocator, CanSetMemResourcePerPointer) {
TEST_F(SerializeExtensionPointerWithAllocator, CanSetMemResourcePerPointer)
{
MemResourceForTest memRes1{};
MemResourceForTest memRes2{};
std::get<0>(plctx).setMemResource(&memRes1);
Base* baseData = new Derived1{ 2, 1 };
createSerializer().ext(baseData, PointerOwner{bitsery::ext::PointerType::Nullable, &memRes2});
createSerializer().ext(
baseData, PointerOwner{ bitsery::ext::PointerType::Nullable, &memRes2 });
Base* baseRes = new Derived2;
createDeserializer().ext(baseRes, PointerOwner{bitsery::ext::PointerType::Nullable, &memRes2});
createDeserializer().ext(
baseRes, PointerOwner{ bitsery::ext::PointerType::Nullable, &memRes2 });
auto dData = dynamic_cast<Derived1*>(baseData);
auto dRes = dynamic_cast<Derived1*>(baseRes);
@@ -309,59 +360,71 @@ TEST_F(SerializeExtensionPointerWithAllocator, CanSetMemResourcePerPointer) {
EXPECT_THAT(memRes2.allocs.size(), Eq(1u));
EXPECT_THAT(memRes2.allocs[0].bytes, Eq(sizeof(Derived1)));
EXPECT_THAT(memRes2.allocs[0].alignment, Eq(alignof(Derived1)));
EXPECT_THAT(memRes2.allocs[0].typeId, Eq(bitsery::ext::StandardRTTI::get<Derived1>()));
EXPECT_THAT(memRes2.allocs[0].typeId,
Eq(bitsery::ext::StandardRTTI::get<Derived1>()));
EXPECT_THAT(memRes2.deallocs.size(), Eq(1u));
EXPECT_THAT(memRes2.deallocs[0].bytes, Eq(sizeof(Derived2)));
EXPECT_THAT(memRes2.deallocs[0].alignment, Eq(alignof(Derived2)));
EXPECT_THAT(memRes2.deallocs[0].typeId, Eq(bitsery::ext::StandardRTTI::get<Derived2>()));
EXPECT_THAT(memRes2.deallocs[0].typeId,
Eq(bitsery::ext::StandardRTTI::get<Derived2>()));
delete dData;
delete dRes;
}
TEST_F(SerializeExtensionPointerWithAllocator, MemResourceSetPerPointerByDefaultDoNotPropagate) {
TEST_F(SerializeExtensionPointerWithAllocator,
MemResourceSetPerPointerByDefaultDoNotPropagate)
{
MemResourceForTest memRes1{};
MemResourceForTest memRes2{};
std::get<0>(plctx).setMemResource(&memRes1);
auto data = std::unique_ptr<PolyPtrWithPolyPtrBase>(new PolyPtrWithPolyPtrBase{});
auto data =
std::unique_ptr<PolyPtrWithPolyPtrBase>(new PolyPtrWithPolyPtrBase{});
data->ptr = std::unique_ptr<Base>(new Derived1{ 5, 6 });
createSerializer().ext(data, StdSmartPtr{bitsery::ext::PointerType::Nullable, &memRes2});
createSerializer().ext(
data, StdSmartPtr{ bitsery::ext::PointerType::Nullable, &memRes2 });
auto res = std::unique_ptr<PolyPtrWithPolyPtrBase>(new DerivedPolyPtrWithPolyPtr{});
auto res =
std::unique_ptr<PolyPtrWithPolyPtrBase>(new DerivedPolyPtrWithPolyPtr{});
res->ptr = std::unique_ptr<Base>(new Derived2{});
createDeserializer().ext(res, StdSmartPtr{bitsery::ext::PointerType::Nullable, &memRes2});
createDeserializer().ext(
res, StdSmartPtr{ bitsery::ext::PointerType::Nullable, &memRes2 });
EXPECT_THAT(memRes1.allocs.size(), Eq(1u));
// Base* was destroyed by unique_ptr on PolyPtrWithPolyPtrBase destructor, hence == 0
// Base* was destroyed by unique_ptr on PolyPtrWithPolyPtrBase destructor,
// hence == 0
EXPECT_THAT(memRes1.deallocs.size(), Eq(0u));
EXPECT_THAT(memRes2.allocs.size(), Eq(1u));
EXPECT_THAT(memRes2.deallocs.size(), Eq(1u));
}
TEST_F(SerializeExtensionPointerWithAllocator, MemResourceSetPerPointerCanPropagate) {
TEST_F(SerializeExtensionPointerWithAllocator,
MemResourceSetPerPointerCanPropagate)
{
MemResourceForTest memRes1{};
MemResourceForTest memRes2{};
std::get<0>(plctx).setMemResource(&memRes1);
auto data = std::unique_ptr<PolyPtrWithPolyPtrBase>(new PolyPtrWithPolyPtrBase{});
auto data =
std::unique_ptr<PolyPtrWithPolyPtrBase>(new PolyPtrWithPolyPtrBase{});
data->ptr = std::unique_ptr<Base>(new Derived1{ 5, 6 });
createSerializer().ext(data, StdSmartPtr{bitsery::ext::PointerType::Nullable, &memRes2, true});
createSerializer().ext(
data, StdSmartPtr{ bitsery::ext::PointerType::Nullable, &memRes2, true });
auto res = std::unique_ptr<PolyPtrWithPolyPtrBase>(new DerivedPolyPtrWithPolyPtr{});
auto res =
std::unique_ptr<PolyPtrWithPolyPtrBase>(new DerivedPolyPtrWithPolyPtr{});
res->ptr = std::unique_ptr<Base>(new Derived2{});
createDeserializer().ext(res, StdSmartPtr{bitsery::ext::PointerType::Nullable, &memRes2, true});
createDeserializer().ext(
res, StdSmartPtr{ bitsery::ext::PointerType::Nullable, &memRes2, true });
EXPECT_THAT(memRes1.allocs.size(), Eq(0u));
EXPECT_THAT(memRes1.deallocs.size(), Eq(0u));
EXPECT_THAT(memRes2.allocs.size(), Eq(2u));
// deallocates are actually == 1, because when we destroy PolyPtrWithPolyPtrBase
// it also destroys Base because it is managed by unique_ptr.
// in order to do it correctly we should always use custom deleter for structures with nested pointers
// deallocates are actually == 1, because when we destroy
// PolyPtrWithPolyPtrBase it also destroys Base because it is managed by
// unique_ptr. in order to do it correctly we should always use custom deleter
// for structures with nested pointers
EXPECT_THAT(memRes2.deallocs.size(), Eq(1u));
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -23,15 +23,16 @@
#include <bitsery/ext/std_bitset.h>
#include <bitsery/ext/value_range.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
using StdBitset = bitsery::ext::StdBitset;
using ValueRange = bitsery::ext::ValueRange<int>;
using testing::Eq;
TEST(SerializeExtensionStdBitset, BitsetSmallerThanULongLong) {
TEST(SerializeExtensionStdBitset, BitsetSmallerThanULongLong)
{
SerializationContext ctx;
std::bitset<31> data;
@@ -47,7 +48,8 @@ TEST(SerializeExtensionStdBitset, BitsetSmallerThanULongLong) {
EXPECT_THAT(res, Eq(data));
}
TEST(SerializeExtensionStdBitset, BitsetSmallerThanULongLong2) {
TEST(SerializeExtensionStdBitset, BitsetSmallerThanULongLong2)
{
SerializationContext ctx;
std::bitset<9> data;
@@ -59,8 +61,8 @@ TEST(SerializeExtensionStdBitset, BitsetSmallerThanULongLong2) {
EXPECT_THAT(res, Eq(data));
}
TEST(SerializeExtensionStdBitset, BitsetLargerThanULongLong) {
TEST(SerializeExtensionStdBitset, BitsetLargerThanULongLong)
{
SerializationContext ctx;
std::bitset<200> data;
@@ -76,7 +78,8 @@ TEST(SerializeExtensionStdBitset, BitsetLargerThanULongLong) {
EXPECT_THAT(res, Eq(data));
}
TEST(SerializeExtensionStdBitset, BitsetSmallerThanULongLongBitPackingEnabled) {
TEST(SerializeExtensionStdBitset, BitsetSmallerThanULongLongBitPackingEnabled)
{
SerializationContext ctx;
std::bitset<12> data;
@@ -86,11 +89,13 @@ TEST(SerializeExtensionStdBitset, BitsetSmallerThanULongLongBitPackingEnabled) {
std::bitset<12> res{};
int other_res{};
ctx.createSerializer().enableBitPacking([&data, &other_data](SerializationContext::TSerializerBPEnabled& sbp) {
ctx.createSerializer().enableBitPacking(
[&data, &other_data](SerializationContext::TSerializerBPEnabled& sbp) {
sbp.ext(data, StdBitset{});
sbp.ext(other_data, ValueRange{ 1000, 1015 });
});
ctx.createDeserializer().enableBitPacking([&res, &other_res](SerializationContext::TDeserializerBPEnabled& dbp) {
ctx.createDeserializer().enableBitPacking(
[&res, &other_res](SerializationContext::TDeserializerBPEnabled& dbp) {
dbp.ext(res, StdBitset{});
dbp.ext(other_res, ValueRange{ 1000, 1015 });
});
@@ -99,7 +104,8 @@ TEST(SerializeExtensionStdBitset, BitsetSmallerThanULongLongBitPackingEnabled) {
EXPECT_THAT(ctx.getBufferSize(), Eq(2));
}
TEST(SerializeExtensionStdBitset, BitsetLargerThanULongLongBitPackingEnabled) {
TEST(SerializeExtensionStdBitset, BitsetLargerThanULongLongBitPackingEnabled)
{
SerializationContext ctx;
std::bitset<204> data;
@@ -110,11 +116,13 @@ TEST(SerializeExtensionStdBitset, BitsetLargerThanULongLongBitPackingEnabled) {
std::bitset<204> res{};
int other_res{};
ctx.createSerializer().enableBitPacking([&data, &other_data](SerializationContext::TSerializerBPEnabled& sbp) {
ctx.createSerializer().enableBitPacking(
[&data, &other_data](SerializationContext::TSerializerBPEnabled& sbp) {
sbp.ext(data, StdBitset{});
sbp.ext(other_data, ValueRange{ 1000, 1015 });
});
ctx.createDeserializer().enableBitPacking([&res, &other_res](SerializationContext::TDeserializerBPEnabled& dbp) {
ctx.createDeserializer().enableBitPacking(
[&res, &other_res](SerializationContext::TDeserializerBPEnabled& dbp) {
dbp.ext(res, StdBitset{});
dbp.ext(other_res, ValueRange{ 1000, 1015 });
});

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -22,15 +22,16 @@
#include <bitsery/ext/std_chrono.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
using StdDuration = bitsery::ext::StdDuration;
using StdTimePoint = bitsery::ext::StdTimePoint;
using testing::Eq;
TEST(SerializeExtensionStdChrono, IntegralDuration) {
TEST(SerializeExtensionStdChrono, IntegralDuration)
{
SerializationContext ctx1;
using Hours = std::chrono::duration<int32_t, std::ratio<60>>;
@@ -42,7 +43,8 @@ TEST(SerializeExtensionStdChrono, IntegralDuration) {
EXPECT_THAT(res, Eq(data));
}
TEST(SerializeExtensionStdChrono, IntegralTimePoint) {
TEST(SerializeExtensionStdChrono, IntegralTimePoint)
{
SerializationContext ctx1;
using Duration = std::chrono::duration<int64_t, std::milli>;
using TP = std::chrono::time_point<std::chrono::system_clock, Duration>;
@@ -55,7 +57,8 @@ TEST(SerializeExtensionStdChrono, IntegralTimePoint) {
EXPECT_THAT(res, Eq(data));
}
TEST(SerializeExtensionStdChrono, FloatDuration) {
TEST(SerializeExtensionStdChrono, FloatDuration)
{
SerializationContext ctx1;
using Hours = std::chrono::duration<float, std::ratio<60>>;
@@ -67,7 +70,8 @@ TEST(SerializeExtensionStdChrono, FloatDuration) {
EXPECT_THAT(res, Eq(data));
}
TEST(SerializeExtensionStdChrono, FloatTimePoint) {
TEST(SerializeExtensionStdChrono, FloatTimePoint)
{
SerializationContext ctx1;
using Duration = std::chrono::duration<double, std::milli>;
using TP = std::chrono::time_point<std::chrono::system_clock, Duration>;

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,52 +20,56 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <bitsery/ext/std_map.h>
#include <bitsery/ext/entropy.h>
#include <unordered_map>
#include <bitsery/ext/std_map.h>
#include <bitsery/traits/string.h>
#include <unordered_map>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
using StdMap = bitsery::ext::StdMap;
using testing::Eq;
template<typename Container>
Container createData() {
Container
createData()
{
return {};
}
template<>
std::unordered_map<std::string, MyStruct1> createData<std::unordered_map<std::string, MyStruct1>>() {
return {
std::make_pair("some key", MyStruct1{874,456}),
std::unordered_map<std::string, MyStruct1>
createData<std::unordered_map<std::string, MyStruct1>>()
{
return { std::make_pair("some key", MyStruct1{ 874, 456 }),
std::make_pair("other key", MyStruct1{ -34, 8645 }),
std::make_pair("secret key", MyStruct1{-4878,3468975})
};
std::make_pair("secret key", MyStruct1{ -4878, 3468975 }) };
}
template<>
std::unordered_multimap<int32_t, float> createData<std::unordered_multimap<int32_t, float>>() {
return {
std::pair<int32_t , float>(545, 45.485f),
std::unordered_multimap<int32_t, float>
createData<std::unordered_multimap<int32_t, float>>()
{
return { std::pair<int32_t, float>(545, 45.485f),
std::pair<int32_t, float>(6748, -7891.5f),
std::pair<int32_t , float>(845, -457.0f)
};
std::pair<int32_t, float>(845, -457.0f) };
}
template<>
std::map<MyEnumClass, MyStruct1> createData<std::map<MyEnumClass, MyStruct1>>() {
return {
std::make_pair(MyEnumClass::E3, MyStruct1{874,456}),
std::map<MyEnumClass, MyStruct1>
createData<std::map<MyEnumClass, MyStruct1>>()
{
return { std::make_pair(MyEnumClass::E3, MyStruct1{ 874, 456 }),
std::make_pair(MyEnumClass::E6, MyStruct1{ -34, 8645 }),
std::make_pair(MyEnumClass::E2, MyStruct1{-4878,3468975})
};
std::make_pair(MyEnumClass::E2, MyStruct1{ -4878, 3468975 }) };
}
template<>
std::multimap<int32_t ,int64_t> createData<std::multimap<int32_t ,int64_t>>() {
std::multimap<int32_t, int64_t>
createData<std::multimap<int32_t, int64_t>>()
{
return { // these are optimized with range and entropy
std::pair<int32_t, int64_t>(-45, -984196845ll),
std::pair<int32_t, int64_t>(54, 1ll),
@@ -74,7 +78,8 @@ std::multimap<int32_t ,int64_t> createData<std::multimap<int32_t ,int64_t>>() {
}
template<typename T>
class SerializeExtensionStdMap : public testing::Test {
class SerializeExtensionStdMap : public testing::Test
{
public:
using TContainer = T;
@@ -82,19 +87,20 @@ public:
TContainer res{};
};
using SerializeExtensionStdMapTypes = ::testing::Types<
std::unordered_map<std::string, MyStruct1>,
using SerializeExtensionStdMapTypes =
::testing::Types<std::unordered_map<std::string, MyStruct1>,
std::unordered_multimap<int32_t, float>,
std::map<MyEnumClass, MyStruct1>,
std::multimap<int32_t ,int64_t>
>;
std::multimap<int32_t, int64_t>>;
TYPED_TEST_SUITE(SerializeExtensionStdMap, SerializeExtensionStdMapTypes, );
namespace bitsery {
template<typename S>
void serialize(S& s, std::unordered_map<std::string, MyStruct1>& o) {
void
serialize(S& s, std::unordered_map<std::string, MyStruct1>& o)
{
s.ext(o, StdMap{ 10 }, [](S& s, std::string& key, MyStruct1& value) {
s.text1b(key, 100);
s.object(value);
@@ -102,7 +108,9 @@ namespace bitsery {
}
template<typename S>
void serialize(S& s, std::unordered_multimap<int32_t, float>& o) {
void
serialize(S& s, std::unordered_multimap<int32_t, float>& o)
{
s.ext(o, StdMap{ 10 }, [](S& s, int32_t& key, float& value) {
s.value4b(key);
s.value4b(value);
@@ -110,7 +118,9 @@ namespace bitsery {
}
template<typename S>
void serialize(S& s, std::map<MyEnumClass , MyStruct1>& o) {
void
serialize(S& s, std::map<MyEnumClass, MyStruct1>& o)
{
s.ext(o, StdMap{ 10 }, [](S& s, MyEnumClass& key, MyStruct1& value) {
s.value4b(key);
s.object(value);
@@ -118,7 +128,9 @@ namespace bitsery {
}
template<typename S>
void serialize(S& s, std::multimap<int32_t ,int64_t>& o) {
void
serialize(S& s, std::multimap<int32_t, int64_t>& o)
{
s.ext(o, StdMap{ 10 }, [](S& s, int32_t& key, int64_t& value) {
s.enableBitPacking([&key, &value](typename S::BPEnabledType& sbp) {
int64_t values[3]{ 1ll, 2ll, 3ll };
@@ -128,10 +140,10 @@ namespace bitsery {
});
}
}
TYPED_TEST(SerializeExtensionStdMap, SerializeAndDeserializeEquals) {
TYPED_TEST(SerializeExtensionStdMap, SerializeAndDeserializeEquals)
{
SerializationContext ctx1;
ctx1.createSerializer().object(this->src);
ctx1.createDeserializer().object(this->res);

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,9 +20,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
#if __cplusplus > 201402L
@@ -37,12 +36,15 @@ using BPDes = SerializationContext::TDeserializer::BPEnabledType;
using testing::Eq;
template<typename T>
void test(SerializationContext& ctx, const T& v, T& r) {
void
test(SerializationContext& ctx, const T& v, T& r)
{
ctx.createSerializer().ext4b(v, StdOptional{});
ctx.createDeserializer().ext4b(r, StdOptional{});
}
TEST(SerializeExtensionStdOptional, EmptyOptional) {
TEST(SerializeExtensionStdOptional, EmptyOptional)
{
std::optional<int32_t> t1{};
std::optional<int32_t> r1{};
@@ -51,7 +53,6 @@ TEST(SerializeExtensionStdOptional, EmptyOptional) {
EXPECT_THAT(ctx1.getBufferSize(), Eq(1));
EXPECT_THAT(t1, Eq(r1));
r1 = 3;
SerializationContext ctx2;
test(ctx2, t1, r1);
@@ -59,7 +60,8 @@ TEST(SerializeExtensionStdOptional, EmptyOptional) {
EXPECT_THAT(t1, Eq(r1));
}
TEST(SerializeExtensionStdOptional, OptionalHasValue) {
TEST(SerializeExtensionStdOptional, OptionalHasValue)
{
std::optional<int32_t> t1{ 43 };
std::optional<int32_t> r1{ 52 };
@@ -73,17 +75,16 @@ TEST(SerializeExtensionStdOptional, OptionalHasValue) {
test(ctx2, t1, r1);
EXPECT_THAT(ctx2.getBufferSize(), Eq(1 + sizeof(int)));
EXPECT_THAT(t1.value(), Eq(r1.value()));
}
TEST(SerializeExtensionStdOptional, AlignAfterStateWriteRead) {
TEST(SerializeExtensionStdOptional, AlignAfterStateWriteRead)
{
std::optional<int32_t> t1{ 43 };
std::optional<int32_t> r1{ 52 };
auto range = bitsery::ext::ValueRange<int>{ 40, 60 };
SerializationContext ctx;
ctx.createSerializer().enableBitPacking([&t1, &range](BPSer& ser) {
ser.ext(t1, StdOptional(true), [&range](BPSer& ser, int32_t& v) {
ser.ext(v, range);
});
@@ -98,7 +99,8 @@ TEST(SerializeExtensionStdOptional, AlignAfterStateWriteRead) {
EXPECT_THAT(t1.value(), Eq(r1.value()));
}
TEST(SerializeExtensionStdOptional, NoAlignAfterStateWriteRead) {
TEST(SerializeExtensionStdOptional, NoAlignAfterStateWriteRead)
{
std::optional<int32_t> t1{ 43 };
std::optional<int32_t> r1{ 52 };
auto range = bitsery::ext::ValueRange<int>{ 40, 60 };
@@ -121,7 +123,8 @@ TEST(SerializeExtensionStdOptional, NoAlignAfterStateWriteRead) {
}
#elif defined(_MSC_VER)
#pragma message("C++17 and /Zc:__cplusplus option is required to enable std::optional tests")
#pragma message( \
"C++17 and /Zc:__cplusplus option is required to enable std::optional tests")
#else
#pragma message("C++17 is required to enable std::optional tests")
#endif

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -22,14 +22,15 @@
#include <bitsery/ext/std_queue.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
using StdQueue = bitsery::ext::StdQueue;
using testing::Eq;
//inherit from queue so we could take underlying container, because priority queue doesn't have equal operator defined
// inherit from queue so we could take underlying container, because priority
// queue doesn't have equal operator defined
template<typename T, typename C>
struct PriorityQueueCnt : public std::priority_queue<T, C>
{
@@ -46,12 +47,15 @@ struct PriorityQueueCnt : public std::priority_queue<T, C>
};
template<typename T>
void test(SerializationContext& ctx, const T& v, T& r) {
void
test(SerializationContext& ctx, const T& v, T& r)
{
ctx.createSerializer().ext4b(v, StdQueue{ 10 });
ctx.createDeserializer().ext4b(r, StdQueue{ 10 });
}
TEST(SerializeExtensionStdQueue, QueueDefaultContainer) {
TEST(SerializeExtensionStdQueue, QueueDefaultContainer)
{
std::queue<int32_t> t1{};
t1.push(3);
t1.push(-4854);
@@ -62,7 +66,8 @@ TEST(SerializeExtensionStdQueue, QueueDefaultContainer) {
EXPECT_THAT(t1, Eq(r1));
}
TEST(SerializeExtensionStdQueue, QueueVectorContainer) {
TEST(SerializeExtensionStdQueue, QueueVectorContainer)
{
std::queue<int32_t, std::vector<int32_t>> t1{};
t1.push(3);
t1.push(-4854);
@@ -73,7 +78,8 @@ TEST(SerializeExtensionStdQueue, QueueVectorContainer) {
EXPECT_THAT(t1, Eq(r1));
}
TEST(SerializeExtensionStdQueue, PriorityQueueDefaultContainer) {
TEST(SerializeExtensionStdQueue, PriorityQueueDefaultContainer)
{
std::priority_queue<int32_t> t1{};
t1.push(3);
t1.push(-4854);
@@ -86,7 +92,8 @@ TEST(SerializeExtensionStdQueue, PriorityQueueDefaultContainer) {
EXPECT_THAT(ct1, Eq(cr1));
}
TEST(SerializeExtensionStdQueue, PriorityQueueDequeContainer) {
TEST(SerializeExtensionStdQueue, PriorityQueueDequeContainer)
{
std::priority_queue<int32_t, std::deque<int32_t>> t1{};
t1.push(678);
t1.push(-44);

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -23,56 +23,60 @@
#include <bitsery/ext/std_set.h>
#include <set>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
using StdSet = bitsery::ext::StdSet;
using testing::Eq;
template<typename T>
class SerializeExtensionStdSet : public testing::Test {
class SerializeExtensionStdSet : public testing::Test
{
public:
using TContainer = T;
const TContainer src = { 4, 8, 48, 4, 9845, 64, 8 };
TContainer res{ 78, 74, 154, 8 };
};
using SerializeExtensionStdSetTypes = ::testing::Types<
std::unordered_set<int32_t>,
using SerializeExtensionStdSetTypes =
::testing::Types<std::unordered_set<int32_t>,
std::unordered_multiset<int32_t>,
std::set<int32_t>,
std::multiset<int32_t>>;
TYPED_TEST_SUITE(SerializeExtensionStdSet, SerializeExtensionStdSetTypes, );
TYPED_TEST(SerializeExtensionStdSet, ValuesSyntaxDifferentSetTypes) {
TYPED_TEST(SerializeExtensionStdSet, ValuesSyntaxDifferentSetTypes)
{
SerializationContext ctx1;
ctx1.createSerializer().ext4b(this->src, StdSet{ 10 });
ctx1.createDeserializer().ext4b(this->res, StdSet{ 10 });
EXPECT_THAT(this->res, Eq(this->src));
}
TEST(SerializeExtensionStdSet, ObjectSyntax) {
TEST(SerializeExtensionStdSet, ObjectSyntax)
{
SerializationContext ctx1;
std::set<MyStruct1> t1{MyStruct1{874 ,456}, MyStruct1{-874, -456}, MyStruct1{4894,0}};
std::set<MyStruct1> t1{ MyStruct1{ 874, 456 },
MyStruct1{ -874, -456 },
MyStruct1{ 4894, 0 } };
std::set<MyStruct1> r1{};
ctx1.createSerializer().ext(t1, StdSet{ 10 });
ctx1.createDeserializer().ext(r1, StdSet{ 10 });
EXPECT_THAT(r1, Eq(t1));
}
TEST(SerializeExtensionStdSet, FunctionSyntax) {
TEST(SerializeExtensionStdSet, FunctionSyntax)
{
SerializationContext ctx1;
std::unordered_multiset<int32_t> t1{ 54, -484, 841, 79 };
std::unordered_multiset<int32_t> r1{ 74, 878, 15, 16, -7, 5, -4, 8, 7 };
auto& ser = ctx1.createSerializer();
ser.ext(t1, StdSet{10}, [](decltype(ser)& ser, int32_t& v) {
ser.value4b(v);
});
ser.ext(
t1, StdSet{ 10 }, [](decltype(ser)& ser, int32_t& v) { ser.value4b(v); });
auto& des = ctx1.createDeserializer();
des.ext(r1, StdSet{10}, [](decltype(des)& des, int32_t& v) {
des.value4b(v);
});
des.ext(
r1, StdSet{ 10 }, [](decltype(des)& des, int32_t& v) { des.value4b(v); });
EXPECT_THAT(r1, Eq(t1));
}

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -24,87 +24,102 @@
#include <bitsery/ext/pointer.h>
#include <bitsery/ext/std_smart_ptr.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
using bitsery::ext::BaseClass;
using bitsery::ext::VirtualBaseClass;
using bitsery::ext::InheritanceContext;
using bitsery::ext::PointerLinkingContext;
using bitsery::ext::PointerType;
using bitsery::ext::PolymorphicContext;
using bitsery::ext::StandardRTTI;
using bitsery::ext::PointerType;
using bitsery::ext::StdSmartPtr;
using bitsery::ext::PointerObserver;
using bitsery::ext::StdSmartPtr;
using testing::Eq;
using testing::Ne;
struct Base {
struct Base
{
uint8_t x{};
virtual ~Base() = default;
};
template<typename S>
void serialize(S& s, Base& o) {
void
serialize(S& s, Base& o)
{
s.value1b(o.x);
}
struct Derived : virtual Base {
struct Derived : virtual Base
{
uint8_t y{};
Derived() = default;
Derived(uint8_t x_, uint8_t y_) {
Derived(uint8_t x_, uint8_t y_)
{
x = x_;
y = y_;
}
};
template<typename S>
void serialize(S& s, Derived& o) {
void
serialize(S& s, Derived& o)
{
s.ext(o, VirtualBaseClass<Base>{});
s.value1b(o.y);
}
struct MoreDerived : Derived {
struct MoreDerived : Derived
{
uint8_t z{};
MoreDerived() = default;
MoreDerived(uint8_t x_, uint8_t y_, uint8_t z_) : Derived(x_, y_) {
MoreDerived(uint8_t x_, uint8_t y_, uint8_t z_)
: Derived(x_, y_)
{
z = z_;
}
};
template<typename S>
void serialize(S& s, MoreDerived& o) {
void
serialize(S& s, MoreDerived& o)
{
s.ext(o, BaseClass<Derived>{});
s.value1b(o.z);
}
//define relationships between base class and derived classes for runtime polymorphism
// define relationships between base class and derived classes for runtime
// polymorphism
namespace bitsery {
namespace ext {
template<>
struct PolymorphicBaseClass<Base> : PolymorphicDerivedClasses<Derived> {
struct PolymorphicBaseClass<Base> : PolymorphicDerivedClasses<Derived>
{
};
template<>
struct PolymorphicBaseClass<Derived> : PolymorphicDerivedClasses<MoreDerived> {
struct PolymorphicBaseClass<Derived> : PolymorphicDerivedClasses<MoreDerived>
{
};
}
}
template<typename T>
class SerializeExtensionStdSmartPtrNonPolymorphicType : public testing::Test {
class SerializeExtensionStdSmartPtrNonPolymorphicType : public testing::Test
{
public:
template<typename U>
using TPtr = typename T::template TData<U>;
@@ -113,48 +128,52 @@ public:
using TContext = std::tuple<PointerLinkingContext>;
using SerContext = BasicSerializationContext<TContext>;
//this is useful for PolymorphicContext to bind classes to serializer/deserializer
// this is useful for PolymorphicContext to bind classes to
// serializer/deserializer
using TSerializer = typename SerContext::TSerializer;
using TDeserializer = typename SerContext::TDeserializer;
TContext plctx{};
SerContext sctx{};
typename SerContext::TSerializer& createSerializer() {
typename SerContext::TSerializer& createSerializer()
{
return sctx.createSerializer(plctx);
}
typename SerContext::TDeserializer& createDeserializer() {
typename SerContext::TDeserializer& createDeserializer()
{
return sctx.createDeserializer(plctx);
}
bool isPointerContextValid() {
return std::get<0>(plctx).isValid();
}
bool isPointerContextValid() { return std::get<0>(plctx).isValid(); }
virtual void TearDown() override {
EXPECT_TRUE(isPointerContextValid());
}
virtual void TearDown() override { EXPECT_TRUE(isPointerContextValid()); }
};
template<typename T>
class SerializeExtensionStdSmartPtrPolymorphicType : public testing::Test {
class SerializeExtensionStdSmartPtrPolymorphicType : public testing::Test
{
public:
template<typename U>
using TPtr = typename T::template TData<U>;
using TExt = typename T::TExt;
using TContext = std::tuple<PointerLinkingContext, InheritanceContext, PolymorphicContext<StandardRTTI>>;
using TContext = std::tuple<PointerLinkingContext,
InheritanceContext,
PolymorphicContext<StandardRTTI>>;
using SerContext = BasicSerializationContext<TContext>;
//this is useful for PolymorphicContext to bind classes to serializer/deserializer
// this is useful for PolymorphicContext to bind classes to
// serializer/deserializer
using TSerializer = typename SerContext::TSerializer;
using TDeserializer = typename SerContext::TDeserializer;
TContext plctx{};
SerContext sctx{};
typename SerContext::TSerializer& createSerializer() {
typename SerContext::TSerializer& createSerializer()
{
auto& res = sctx.createSerializer(plctx);
std::get<2>(plctx).clear();
// bind serializer with classes
@@ -163,7 +182,8 @@ public:
return res;
}
typename SerContext::TDeserializer& createDeserializer() {
typename SerContext::TDeserializer& createDeserializer()
{
auto& res = sctx.createDeserializer(plctx);
std::get<2>(plctx).clear();
// bind deserializer with classes
@@ -172,40 +192,39 @@ public:
return res;
}
bool isPointerContextValid() {
return std::get<0>(plctx).isValid();
}
bool isPointerContextValid() { return std::get<0>(plctx).isValid(); }
virtual void TearDown() override {
EXPECT_TRUE(isPointerContextValid());
}
virtual void TearDown() override { EXPECT_TRUE(isPointerContextValid()); }
};
struct UniquePtrTest {
struct UniquePtrTest
{
template<typename T>
using TData = std::unique_ptr<T>;
using TExt = StdSmartPtr;
};
struct SharedPtrTest {
struct SharedPtrTest
{
template<typename T>
using TData = std::shared_ptr<T>;
using TExt = StdSmartPtr;
};
using TestingWithNonPolymorphicTypes = ::testing::Types<
UniquePtrTest,
SharedPtrTest>;
using TestingWithNonPolymorphicTypes =
::testing::Types<UniquePtrTest, SharedPtrTest>;
TYPED_TEST_SUITE(SerializeExtensionStdSmartPtrNonPolymorphicType, TestingWithNonPolymorphicTypes,);
TYPED_TEST_SUITE(SerializeExtensionStdSmartPtrNonPolymorphicType,
TestingWithNonPolymorphicTypes, );
using TestingWithPolymorphicTypes = ::testing::Types<
UniquePtrTest,
SharedPtrTest>;
using TestingWithPolymorphicTypes =
::testing::Types<UniquePtrTest, SharedPtrTest>;
TYPED_TEST_SUITE(SerializeExtensionStdSmartPtrPolymorphicType, TestingWithPolymorphicTypes,);
TYPED_TEST_SUITE(SerializeExtensionStdSmartPtrPolymorphicType,
TestingWithPolymorphicTypes, );
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, Data0Result0) {
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, Data0Result0)
{
using Ptr = typename TestFixture::template TPtr<MyStruct1>;
using Ext = typename TestFixture::TExt;
@@ -218,7 +237,8 @@ TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, Data0Result0) {
EXPECT_THAT(res.get(), ::testing::IsNull());
}
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, Data0Result1) {
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, Data0Result1)
{
using Ptr = typename TestFixture::template TPtr<MyStruct1>;
using Ext = typename TestFixture::TExt;
@@ -231,7 +251,8 @@ TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, Data0Result1) {
EXPECT_THAT(res.get(), ::testing::IsNull());
}
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, Data1Result0) {
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, Data1Result0)
{
using Ptr = typename TestFixture::template TPtr<MyStruct1>;
using Ext = typename TestFixture::TExt;
@@ -246,7 +267,8 @@ TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, Data1Result0) {
EXPECT_THAT(res->i2, Eq(data->i2));
}
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, Data1Result1) {
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, Data1Result1)
{
using Ptr = typename TestFixture::template TPtr<MyStruct1>;
using Ext = typename TestFixture::TExt;
@@ -261,7 +283,9 @@ TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, Data1Result1) {
EXPECT_THAT(res->i2, Eq(data->i2));
}
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, CanUseLambdaOverload) {
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType,
CanUseLambdaOverload)
{
using Ptr = typename TestFixture::template TPtr<MyStruct1>;
using Ext = typename TestFixture::TExt;
@@ -273,15 +297,15 @@ TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, CanUseLambdaOverload
});
Ptr res{ new MyStruct1{ 97, 12 } };
auto& des = this->createDeserializer();
des.ext(res, Ext{}, [](decltype(des)& des, MyStruct1& o) {
des.value4b(o.i1);
});
des.ext(
res, Ext{}, [](decltype(des)& des, MyStruct1& o) { des.value4b(o.i1); });
EXPECT_THAT(res->i1, Eq(data->i1));
EXPECT_THAT(res->i2, Ne(data->i2));
}
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, CanUseValueOverload) {
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, CanUseValueOverload)
{
using Ptr = typename TestFixture::template TPtr<uint16_t>;
using Ext = typename TestFixture::TExt;
@@ -292,7 +316,9 @@ TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, CanUseValueOverload)
EXPECT_THAT(*res, Eq(*data));
}
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, FirstPtrThenPointerObserver) {
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType,
FirstPtrThenPointerObserver)
{
using Ptr = typename TestFixture::template TPtr<uint16_t>;
using Ext = typename TestFixture::TExt;
@@ -310,7 +336,9 @@ TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, FirstPtrThenPointerO
EXPECT_THAT(resObs, Eq(res.get()));
}
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, FirstPointerObserverThenPtr) {
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType,
FirstPointerObserverThenPtr)
{
using Ptr = typename TestFixture::template TPtr<uint16_t>;
using Ext = typename TestFixture::TExt;
@@ -327,7 +355,8 @@ TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, FirstPointerObserver
EXPECT_THAT(resObs, Eq(res.get()));
}
TYPED_TEST(SerializeExtensionStdSmartPtrPolymorphicType, Data0Result0) {
TYPED_TEST(SerializeExtensionStdSmartPtrPolymorphicType, Data0Result0)
{
using Ptr = typename TestFixture::template TPtr<Base>;
using Ext = typename TestFixture::TExt;
@@ -340,7 +369,8 @@ TYPED_TEST(SerializeExtensionStdSmartPtrPolymorphicType, Data0Result0) {
EXPECT_THAT(baseData.get(), ::testing::IsNull());
}
TYPED_TEST(SerializeExtensionStdSmartPtrPolymorphicType, Data0Result1) {
TYPED_TEST(SerializeExtensionStdSmartPtrPolymorphicType, Data0Result1)
{
using Ptr = typename TestFixture::template TPtr<Base>;
using Ext = typename TestFixture::TExt;
@@ -354,7 +384,8 @@ TYPED_TEST(SerializeExtensionStdSmartPtrPolymorphicType, Data0Result1) {
EXPECT_THAT(baseData.get(), ::testing::IsNull());
}
TYPED_TEST(SerializeExtensionStdSmartPtrPolymorphicType, Data1Result0) {
TYPED_TEST(SerializeExtensionStdSmartPtrPolymorphicType, Data1Result0)
{
using Ptr = typename TestFixture::template TPtr<Base>;
using Ext = typename TestFixture::TExt;
@@ -372,7 +403,9 @@ TYPED_TEST(SerializeExtensionStdSmartPtrPolymorphicType, Data1Result0) {
EXPECT_THAT(res->y, Eq(data->y));
}
TYPED_TEST(SerializeExtensionStdSmartPtrPolymorphicType, DataAndResultWithDifferentRuntimeTypes) {
TYPED_TEST(SerializeExtensionStdSmartPtrPolymorphicType,
DataAndResultWithDifferentRuntimeTypes)
{
using Ptr = typename TestFixture::template TPtr<Base>;
using Ext = typename TestFixture::TExt;
@@ -390,49 +423,51 @@ TYPED_TEST(SerializeExtensionStdSmartPtrPolymorphicType, DataAndResultWithDiffer
EXPECT_THAT(res->y, Eq(data->y));
}
class SerializeExtensionStdSmartSharedPtr : public testing::Test {
class SerializeExtensionStdSmartSharedPtr : public testing::Test
{
public:
using TContext = std::tuple<PointerLinkingContext, InheritanceContext, PolymorphicContext<StandardRTTI>>;
using TContext = std::tuple<PointerLinkingContext,
InheritanceContext,
PolymorphicContext<StandardRTTI>>;
using SerContext = BasicSerializationContext<TContext>;
//this is useful for PolymorphicContext to bind classes to serializer/deserializer
// this is useful for PolymorphicContext to bind classes to
// serializer/deserializer
using TSerializer = typename SerContext::TSerializer;
using TDeserializer = typename SerContext::TDeserializer;
TContext plctx{};
SerContext sctx{};
typename SerContext::TSerializer& createSerializer() {
typename SerContext::TSerializer& createSerializer()
{
auto& res = sctx.createSerializer(plctx);
std::get<2>(plctx).clear();
// bind serializer with classes
std::get<2>(plctx).registerBasesList<SerContext::TSerializer>(bitsery::ext::PolymorphicClassesList<Base>{});
std::get<2>(plctx).registerBasesList<SerContext::TSerializer>(
bitsery::ext::PolymorphicClassesList<Base>{});
return res;
}
typename SerContext::TDeserializer& createDeserializer() {
typename SerContext::TDeserializer& createDeserializer()
{
auto& res = sctx.createDeserializer(plctx);
std::get<2>(plctx).clear();
// bind deserializer with classes
std::get<2>(plctx).registerBasesList<SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<Base>{});
std::get<2>(plctx).registerBasesList<SerContext::TDeserializer>(
bitsery::ext::PolymorphicClassesList<Base>{});
return res;
}
size_t getBufferSize() const {
return sctx.getBufferSize();
}
size_t getBufferSize() const { return sctx.getBufferSize(); }
bool isPointerContextValid() {
return std::get<0>(plctx).isValid();
}
bool isPointerContextValid() { return std::get<0>(plctx).isValid(); }
void clearSharedState() {
return std::get<0>(plctx).clearSharedState();
}
void clearSharedState() { return std::get<0>(plctx).clearSharedState(); }
};
TEST_F(SerializeExtensionStdSmartSharedPtr, SameSharedObjectIsSerializedOnce) {
TEST_F(SerializeExtensionStdSmartSharedPtr, SameSharedObjectIsSerializedOnce)
{
std::shared_ptr<Base> baseData1{ new Derived{ 3, 78 } };
std::shared_ptr<Base> baseData2{ baseData1 };
@@ -449,7 +484,9 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, SameSharedObjectIsSerializedOnce) {
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, PointerLinkingContextCorrectlyClearSharedState) {
TEST_F(SerializeExtensionStdSmartSharedPtr,
PointerLinkingContextCorrectlyClearSharedState)
{
std::shared_ptr<Base> baseData1{ new Derived{ 3, 78 } };
@@ -464,7 +501,8 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, PointerLinkingContextCorrectlyClearS
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, CorrectlyManagesSameSharedObject) {
TEST_F(SerializeExtensionStdSmartSharedPtr, CorrectlyManagesSameSharedObject)
{
std::shared_ptr<Base> baseData1{ new Derived{ 3, 78 } };
std::shared_ptr<Base> baseData2{ new Derived{ 55, 11 } };
@@ -495,7 +533,8 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, CorrectlyManagesSameSharedObject) {
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, FirstSharedThenWeakPtr) {
TEST_F(SerializeExtensionStdSmartSharedPtr, FirstSharedThenWeakPtr)
{
std::shared_ptr<Base> baseData1{ new Derived{ 3, 78 } };
std::weak_ptr<Base> baseData11{ baseData1 };
@@ -526,7 +565,8 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, FirstSharedThenWeakPtr) {
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, FirstWeakThenSharedPtr) {
TEST_F(SerializeExtensionStdSmartSharedPtr, FirstWeakThenSharedPtr)
{
std::shared_ptr<MyStruct1> baseData1{ new MyStruct1{ 3, 78 } };
std::weak_ptr<MyStruct1> baseData11{ baseData1 };
@@ -546,7 +586,6 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, FirstWeakThenSharedPtr) {
clearSharedState();
EXPECT_THAT(*baseData1, Eq(*baseRes1));
EXPECT_THAT(baseRes1.use_count(), Eq(1));
EXPECT_THAT(baseRes2.use_count(), Eq(0));
@@ -556,7 +595,8 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, FirstWeakThenSharedPtr) {
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, WeakPtrFirstPolymorphicData0Result1) {
TEST_F(SerializeExtensionStdSmartSharedPtr, WeakPtrFirstPolymorphicData0Result1)
{
std::shared_ptr<Base> baseData1{};
std::weak_ptr<Base> baseData2{};
@@ -570,7 +610,6 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, WeakPtrFirstPolymorphicData0Result1)
des.ext(baseRes2, StdSmartPtr{});
des.ext(baseRes1, StdSmartPtr{});
clearSharedState();
EXPECT_THAT(baseRes1.use_count(), Eq(0));
@@ -579,7 +618,9 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, WeakPtrFirstPolymorphicData0Result1)
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, WeakPtrFirstNonPolymorphicData0Result1) {
TEST_F(SerializeExtensionStdSmartSharedPtr,
WeakPtrFirstNonPolymorphicData0Result1)
{
std::shared_ptr<MyStruct2> baseData1{};
std::weak_ptr<MyStruct2> baseData2{};
@@ -587,13 +628,13 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, WeakPtrFirstNonPolymorphicData0Resul
ser.ext(baseData2, StdSmartPtr{});
ser.ext(baseData1, StdSmartPtr{});
std::shared_ptr<MyStruct2> baseRes1{new MyStruct2{MyStruct2::MyEnum::V4, {1, 87}}};
std::shared_ptr<MyStruct2> baseRes1{ new MyStruct2{ MyStruct2::MyEnum::V4,
{ 1, 87 } } };
std::weak_ptr<MyStruct2> baseRes2{ baseRes1 };
auto& des = createDeserializer();
des.ext(baseRes2, StdSmartPtr{});
des.ext(baseRes1, StdSmartPtr{});
clearSharedState();
EXPECT_THAT(baseRes1.use_count(), Eq(0));
@@ -602,7 +643,8 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, WeakPtrFirstNonPolymorphicData0Resul
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, FewPtrsAreEmpty) {
TEST_F(SerializeExtensionStdSmartSharedPtr, FewPtrsAreEmpty)
{
std::shared_ptr<Base> baseData1{ new Derived{ 3, 78 } };
std::shared_ptr<Base> baseData2{};
@@ -635,8 +677,8 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, FewPtrsAreEmpty) {
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenResultObjectExistsSameType) {
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenResultObjectExistsSameType)
{
std::shared_ptr<Base> baseData1{ new Derived{ 3, 78 } };
auto& ser = createSerializer();
@@ -653,7 +695,8 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, WhenResultObjectExistsSameType) {
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenResultObjectExistsDifferentType) {
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenResultObjectExistsDifferentType)
{
std::shared_ptr<Base> baseData1{ new Derived{ 3, 78 } };
auto& ser = createSerializer();
@@ -671,7 +714,9 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, WhenResultObjectExistsDifferentType)
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenOnlyWeakPtrIsSerializedThenPointerCointextIsInvalid) {
TEST_F(SerializeExtensionStdSmartSharedPtr,
WhenOnlyWeakPtrIsSerializedThenPointerCointextIsInvalid)
{
std::shared_ptr<Base> tmp{ new Derived{ 3, 78 } };
std::weak_ptr<Base> baseData1{ tmp };
auto& ser = createSerializer();
@@ -680,7 +725,9 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, WhenOnlyWeakPtrIsSerializedThenPoint
EXPECT_FALSE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenOnlyWeakPtrIsDeserializedThenPointerCointextIsInvalid) {
TEST_F(SerializeExtensionStdSmartSharedPtr,
WhenOnlyWeakPtrIsDeserializedThenPointerCointextIsInvalid)
{
std::shared_ptr<Base> baseData1{ new Derived{ 3, 78 } };
auto& ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
@@ -694,21 +741,27 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, WhenOnlyWeakPtrIsDeserializedThenPoi
EXPECT_THAT(baseRes1.use_count(), Eq(1));
clearSharedState();
EXPECT_THAT(baseRes1.use_count(), Eq(0));
}
struct TestSharedFromThis : public std::enable_shared_from_this<TestSharedFromThis> {
struct TestSharedFromThis
: public std::enable_shared_from_this<TestSharedFromThis>
{
float x{};
explicit TestSharedFromThis() : std::enable_shared_from_this<TestSharedFromThis>() {}
explicit TestSharedFromThis()
: std::enable_shared_from_this<TestSharedFromThis>()
{
}
template<typename S>
void serialize(S& s) {
void serialize(S& s)
{
s.value4b(x);
}
};
TEST_F(SerializeExtensionStdSmartSharedPtr, EnableSharedFromThis) {
TEST_F(SerializeExtensionStdSmartSharedPtr, EnableSharedFromThis)
{
std::shared_ptr<TestSharedFromThis> dataPtr(new TestSharedFromThis{});
std::shared_ptr<TestSharedFromThis> resPtr{};
createSerializer().ext(dataPtr, StdSmartPtr{});
@@ -719,16 +772,17 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, EnableSharedFromThis) {
EXPECT_THAT(resPtr2.use_count(), Eq(2));
}
struct CustomDeleter {
void operator()(Base* p) {
delete p;
}
struct CustomDeleter
{
void operator()(Base* p) { delete p; }
};
class SerializeExtensionStdSmartUniquePtr : public SerializeExtensionStdSmartSharedPtr {
};
class SerializeExtensionStdSmartUniquePtr
: public SerializeExtensionStdSmartSharedPtr
{};
TEST_F(SerializeExtensionStdSmartUniquePtr, WithCustomDeleter) {
TEST_F(SerializeExtensionStdSmartUniquePtr, WithCustomDeleter)
{
std::unique_ptr<Base, CustomDeleter> dataPtr(new Derived{ 87, 7 });
std::unique_ptr<Base, CustomDeleter> resPtr{};
createSerializer().ext(dataPtr, StdSmartPtr{});

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,23 +20,24 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "serialization_test_utils.h"
#include <bitsery/ext/std_stack.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using StdStack = bitsery::ext::StdStack;
using testing::Eq;
template<typename T>
void test(SerializationContext& ctx, const T& v, T& r) {
void
test(SerializationContext& ctx, const T& v, T& r)
{
ctx.createSerializer().ext4b(v, StdStack{ 10 });
ctx.createDeserializer().ext4b(r, StdStack{ 10 });
}
TEST(SerializeExtensionStdStack, DefaultContainer) {
TEST(SerializeExtensionStdStack, DefaultContainer)
{
std::stack<int32_t> t1{};
t1.push(3);
t1.push(-4854);
@@ -47,7 +48,8 @@ TEST(SerializeExtensionStdStack, DefaultContainer) {
EXPECT_THAT(t1, Eq(r1));
}
TEST(SerializeExtensionStdStack, VectorContainer) {
TEST(SerializeExtensionStdStack, VectorContainer)
{
std::stack<int32_t, std::vector<int32_t>> t1{};
t1.push(3);
t1.push(-4854);

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,8 +20,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
using testing::Eq;
@@ -32,8 +32,11 @@ using testing::Eq;
template<typename T, size_t N>
using OverloadValue = bitsery::ext::OverloadValue<T, N>;
TEST(SerializeExtensionStdTuple, UseDefaultSerializeFunction) {
std::tuple<MyStruct1, MyStruct2> t1{MyStruct1{-789, 45}, MyStruct2{MyStruct2::MyEnum::V3, MyStruct1{}}};
TEST(SerializeExtensionStdTuple, UseDefaultSerializeFunction)
{
std::tuple<MyStruct1, MyStruct2> t1{
MyStruct1{ -789, 45 }, MyStruct2{ MyStruct2::MyEnum::V3, MyStruct1{} }
};
std::tuple<MyStruct1, MyStruct2> r1{};
SerializationContext ctx;
ctx.createSerializer().ext(t1, bitsery::ext::StdTuple{});
@@ -41,29 +44,32 @@ TEST(SerializeExtensionStdTuple, UseDefaultSerializeFunction) {
EXPECT_THAT(t1, Eq(r1));
}
TEST(SerializeExtensionStdTuple, ValueTypesCanBeSerializedWithLambdaAndOrCallableObject) {
TEST(SerializeExtensionStdTuple,
ValueTypesCanBeSerializedWithLambdaAndOrCallableObject)
{
std::tuple<float, int32_t> t1{ 123.456f, -898754656 };
std::tuple<float, int32_t> r1{};
SerializationContext ctx;
auto exec = [](auto& s, auto& o) {
s.ext(o, bitsery::ext::StdTuple{
[](auto& s1, float& o1) {
s1.value4b(o1);
},
OverloadValue<int32_t, 4>{}
});
s.ext(o,
bitsery::ext::StdTuple{ [](auto& s1, float& o1) { s1.value4b(o1); },
OverloadValue<int32_t, 4>{} });
};
ctx.createSerializer().object(t1, exec);
ctx.createDeserializer().object(r1, exec);
EXPECT_THAT(t1, Eq(r1));
}
TEST(SerializeExtensionStdTuple, CanOverloadDefaultSerializeFunction) {
std::tuple<MyStruct1, MyStruct2> t1{MyStruct1{-789, 45}, MyStruct2{MyStruct2::MyEnum::V3, MyStruct1{}}};
TEST(SerializeExtensionStdTuple, CanOverloadDefaultSerializeFunction)
{
std::tuple<MyStruct1, MyStruct2> t1{
MyStruct1{ -789, 45 }, MyStruct2{ MyStruct2::MyEnum::V3, MyStruct1{} }
};
std::tuple<MyStruct1, MyStruct2> r1{};
SerializationContext ctx;
auto exec = [](auto& s, auto& o) {
s.ext(o, bitsery::ext::StdTuple{
s.ext(o,
bitsery::ext::StdTuple{
[](auto& s1, MyStruct1& o1) {
s1.value4b(o1.i1);
// do not serialize other element, it should be 0 (default)
@@ -77,7 +83,8 @@ TEST(SerializeExtensionStdTuple, CanOverloadDefaultSerializeFunction) {
EXPECT_THAT(std::get<0>(t1).i2, ::testing::Ne(std::get<0>(r1).i2));
}
TEST(SerializeExtensionStdTuple, EmptyTuple) {
TEST(SerializeExtensionStdTuple, EmptyTuple)
{
std::tuple<> t1{};
std::tuple<> r1{};
SerializationContext ctx;
@@ -86,40 +93,48 @@ TEST(SerializeExtensionStdTuple, EmptyTuple) {
EXPECT_THAT(t1, Eq(r1));
}
struct NonDefaultConstructable {
explicit NonDefaultConstructable(float x) : _x{x} {}
struct NonDefaultConstructable
{
explicit NonDefaultConstructable(float x)
: _x{ x }
{
}
float _x;
bool operator==(const NonDefaultConstructable& rhs) const {
bool operator==(const NonDefaultConstructable& rhs) const
{
return _x == rhs._x;
}
private:
friend class bitsery::Access;
NonDefaultConstructable() : _x{0.0f} {};
NonDefaultConstructable()
: _x{ 0.0f } {};
};
TEST(SerializeExtensionStdTuple, NonDefaultConstructable) {
TEST(SerializeExtensionStdTuple, NonDefaultConstructable)
{
std::tuple<NonDefaultConstructable> t1{ 34.0f };
std::tuple<NonDefaultConstructable> r1{ 8.0f };
SerializationContext ctx;
ctx.createSerializer().ext(t1, bitsery::ext::StdTuple{
[](auto& s, NonDefaultConstructable& v) {
s.value4b(v._x);
},
ctx.createSerializer().ext(
t1,
bitsery::ext::StdTuple{
[](auto& s, NonDefaultConstructable& v) { s.value4b(v._x); },
});
ctx.createDeserializer().ext(r1, bitsery::ext::StdTuple{
[](auto& s, NonDefaultConstructable& v) {
s.value4b(v._x);
},
ctx.createDeserializer().ext(
r1,
bitsery::ext::StdTuple{
[](auto& s, NonDefaultConstructable& v) { s.value4b(v._x); },
});
EXPECT_THAT(t1, Eq(r1));
}
#elif defined(_MSC_VER)
#pragma message("C++17 and /Zc:__cplusplus option is required to enable std::tuple tests")
#pragma message( \
"C++17 and /Zc:__cplusplus option is required to enable std::tuple tests")
#else
#pragma message("C++17 is required to enable std::tuple tests")
#endif

View File

@@ -9,8 +9,8 @@
// 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 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,
@@ -20,9 +20,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
#if __cplusplus > 201402L
@@ -33,7 +32,8 @@ using testing::Eq;
template<typename T, size_t N>
using OverloadValue = bitsery::ext::OverloadValue<T, N>;
TEST(SerializeExtensionStdVariant, UseSerializeFunction) {
TEST(SerializeExtensionStdVariant, UseSerializeFunction)
{
std::variant<MyStruct1, MyStruct2> t1{ MyStruct1{ 978, 15 } };
std::variant<MyStruct1, MyStruct2> r1{ MyStruct2{} };
@@ -43,9 +43,12 @@ TEST(SerializeExtensionStdVariant, UseSerializeFunction) {
EXPECT_THAT(t1, Eq(r1));
}
TEST(SerializeExtensionStdVariant, WhenTwoIndicesWithSameTypeThenDeserializeCorrectIndex) {
TEST(SerializeExtensionStdVariant,
WhenTwoIndicesWithSameTypeThenDeserializeCorrectIndex)
{
std::variant<MyStruct1, MyStruct2, MyStruct1> t1{std::in_place_index_t<2>{}, MyStruct1{978, 15}};
std::variant<MyStruct1, MyStruct2, MyStruct1> t1{ std::in_place_index_t<2>{},
MyStruct1{ 978, 15 } };
std::variant<MyStruct1, MyStruct2, MyStruct1> r1{ MyStruct2{} };
SerializationContext ctx;
ctx.createSerializer().ext(t1, bitsery::ext::StdVariant{});
@@ -53,48 +56,49 @@ TEST(SerializeExtensionStdVariant, WhenTwoIndicesWithSameTypeThenDeserializeCorr
EXPECT_THAT(t1, Eq(r1));
}
TEST(SerializeExtensionStdVariant, ValueTypesCanBeSerializedWithLambda) {
TEST(SerializeExtensionStdVariant, ValueTypesCanBeSerializedWithLambda)
{
std::variant<float, char, MyStruct1> t1{ 5.6f };
std::variant<float, char, MyStruct1> r1{ MyStruct1{} };
SerializationContext ctx;
auto fncFloat = [](auto& s, float& v) {
s.value4b(v);
};
auto fncChar = [](auto& s, char& v) {
s.value1b(v);
};
auto fncFloat = [](auto& s, float& v) { s.value4b(v); };
auto fncChar = [](auto& s, char& v) { s.value1b(v); };
ctx.createSerializer().ext(t1, bitsery::ext::StdVariant{ fncFloat, fncChar });
ctx.createDeserializer().ext(r1, bitsery::ext::StdVariant{fncFloat, fncChar});
ctx.createDeserializer().ext(r1,
bitsery::ext::StdVariant{ fncFloat, fncChar });
EXPECT_THAT(t1, Eq(r1));
}
TEST(SerializeExtensionStdVariant, ValueTypesCanBeSerializedWithLambdaAndOrCallableObject) {
TEST(SerializeExtensionStdVariant,
ValueTypesCanBeSerializedWithLambdaAndOrCallableObject)
{
std::variant<float, char, MyStruct1> t1{ 'Z' };
std::variant<float, char, MyStruct1> r1{ MyStruct1{} };
SerializationContext ctx;
auto fncFloat = [](auto& s, float& v) {
s.value4b(v);
};
auto fncFloat = [](auto& s, float& v) { s.value4b(v); };
ctx.createSerializer().ext(t1, bitsery::ext::StdVariant{fncFloat, OverloadValue<char, 1>{}});
ctx.createDeserializer().ext(r1, bitsery::ext::StdVariant{fncFloat, OverloadValue<char, 1>{}});
ctx.createSerializer().ext(
t1, bitsery::ext::StdVariant{ fncFloat, OverloadValue<char, 1>{} });
ctx.createDeserializer().ext(
r1, bitsery::ext::StdVariant{ fncFloat, OverloadValue<char, 1>{} });
EXPECT_THAT(t1, Eq(r1));
}
TEST(SerializeExtensionStdVariant, CanOverloadDefaultSerializationFunction) {
TEST(SerializeExtensionStdVariant, CanOverloadDefaultSerializationFunction)
{
std::variant<MyStruct2, MyStruct1, int32_t> t1{ MyStruct1{ 5, 9 } };
std::variant<MyStruct2, MyStruct1, int32_t> r1{ MyStruct1{} };
SerializationContext ctx;
auto exec = [](auto& s, std::variant<MyStruct2, MyStruct1, int32_t>& o) {
using S = decltype(s);
s.ext(o, bitsery::ext::StdVariant{
[](S& s, MyStruct1& v) {
s.ext(o,
bitsery::ext::StdVariant{ [](S& s, MyStruct1& v) {
s.value4b(v.i1);
//do not serialize other element, it should be 0 (default)
// do not serialize other element, it
// should be 0 (default)
},
OverloadValue<int32_t, 4>{}
});
OverloadValue<int32_t, 4>{} });
};
ctx.createSerializer().object(t1, exec);
@@ -102,35 +106,42 @@ TEST(SerializeExtensionStdVariant, CanOverloadDefaultSerializationFunction) {
EXPECT_THAT(std::get<1>(r1).i2, Eq(0));
}
struct NonDefaultConstructable {
explicit NonDefaultConstructable(float x) : _x{x} {}
struct NonDefaultConstructable
{
explicit NonDefaultConstructable(float x)
: _x{ x }
{
}
float _x;
bool operator==(const NonDefaultConstructable& rhs) const {
bool operator==(const NonDefaultConstructable& rhs) const
{
return _x == rhs._x;
}
private:
friend class bitsery::Access;
NonDefaultConstructable() : _x{0.0f} {};
NonDefaultConstructable()
: _x{ 0.0f } {};
};
TEST(SerializeExtensionStdVariant, CanUseNonDefaultConstructableTypes) {
std::variant<NonDefaultConstructable, MyStruct1, int32_t> t1{NonDefaultConstructable{123.456f}};
TEST(SerializeExtensionStdVariant, CanUseNonDefaultConstructableTypes)
{
std::variant<NonDefaultConstructable, MyStruct1, int32_t> t1{
NonDefaultConstructable{ 123.456f }
};
std::variant<NonDefaultConstructable, MyStruct1, int32_t> r1{ MyStruct1{} };
SerializationContext ctx;
auto exec = [](auto& s, std::variant<NonDefaultConstructable, MyStruct1, int32_t>& o) {
auto exec = [](auto& s,
std::variant<NonDefaultConstructable, MyStruct1, int32_t>& o) {
using S = decltype(s);
s.ext(o, bitsery::ext::StdVariant{
[](S& s, NonDefaultConstructable& v) {
s.value4b(v._x);
},
OverloadValue<int32_t, 4>{}
});
s.ext(o,
bitsery::ext::StdVariant{
[](S& s, NonDefaultConstructable& v) { s.value4b(v._x); },
OverloadValue<int32_t, 4>{} });
};
ctx.createSerializer().object(t1, exec);
@@ -139,18 +150,17 @@ TEST(SerializeExtensionStdVariant, CanUseNonDefaultConstructableTypes) {
EXPECT_THAT(t1, Eq(r1));
}
TEST(SerializeExtensionStdVariant, CorrectlyHandleMonoState) {
TEST(SerializeExtensionStdVariant, CorrectlyHandleMonoState)
{
std::variant<std::monostate, NonDefaultConstructable, MyStruct1> t1{};
std::variant<std::monostate, NonDefaultConstructable, MyStruct1> r1{};
SerializationContext ctx;
auto exec = [](auto& s, auto& o) {
using S = decltype(s);
s.ext(o, bitsery::ext::StdVariant{
[](S& s, NonDefaultConstructable& v) {
s.value4b(v._x);
},
s.ext(o,
bitsery::ext::StdVariant{
[](S& s, NonDefaultConstructable& v) { s.value4b(v._x); },
});
};
@@ -164,11 +174,11 @@ TEST(SerializeExtensionStdVariant, CorrectlyHandleMonoState) {
ctx1.createSerializer().ext(t2, bitsery::ext::StdVariant{});
ctx1.createDeserializer().ext(r2, bitsery::ext::StdVariant{});
EXPECT_THAT(t2, Eq(r2));
}
#elif defined(_MSC_VER)
#pragma message("C++17 and /Zc:__cplusplus option is required to enable std::variant tests")
#pragma message( \
"C++17 and /Zc:__cplusplus option is required to enable std::variant tests")
#else
#pragma message("C++17 is required to enable std::variant tests")
#endif

Some files were not shown because too many files have changed in this diff Show More