mirror of
https://github.com/fraillt/bitsery.git
synced 2026-06-18 13:19:11 +00:00
Compare commits
81 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9bebfd4911 | ||
|
|
5e7ecede9b | ||
|
|
ff841d63f6 | ||
|
|
66d16516e2 | ||
|
|
7ea1da0d48 | ||
|
|
8bda82576e | ||
|
|
ee992d8b57 | ||
|
|
4dcdd594da | ||
|
|
b714459a2b | ||
|
|
be2f295310 | ||
|
|
cd73aca2f5 | ||
|
|
94f7adaf6c | ||
|
|
ceeb189c8b | ||
|
|
90243480ec | ||
|
|
d1a47e06e2 | ||
|
|
3e02d0ca44 | ||
|
|
d690908541 | ||
|
|
d3dd64baaf | ||
|
|
d24e0ab1b3 | ||
|
|
c0fc083c9d | ||
|
|
bcd03b4d68 | ||
|
|
4ff80c6426 | ||
|
|
126a6c2971 | ||
|
|
d1830a263b | ||
|
|
47f6f9248a | ||
|
|
9688cb03d2 | ||
|
|
db884a0656 | ||
|
|
8a00183c80 | ||
|
|
8a5e12a125 | ||
|
|
ee7bb2cb93 | ||
|
|
d787680819 | ||
|
|
a519d333e2 | ||
|
|
826b8d4269 | ||
|
|
16f637da0d | ||
|
|
9cade41dbb | ||
|
|
3dc5940c16 | ||
|
|
a544879b22 | ||
|
|
d47ee834e4 | ||
|
|
c556c75100 | ||
|
|
541632fa9e | ||
|
|
d24dfe14f5 | ||
|
|
ee68261124 | ||
|
|
d22b7c1527 | ||
|
|
8d6ffc9873 | ||
|
|
0e76e0608c | ||
|
|
501d60f67d | ||
|
|
04afd16fbd | ||
|
|
9621db1cd7 | ||
|
|
c555088aa3 | ||
|
|
c9619e3e3d | ||
|
|
01d56e00b8 | ||
|
|
04526ff0f4 | ||
|
|
b7d159bbfc | ||
|
|
f85dff7415 | ||
|
|
f35ae3f4dc | ||
|
|
ff40222124 | ||
|
|
a1785f3e15 | ||
|
|
105aa5f9e5 | ||
|
|
1822796f2e | ||
|
|
57dd028b7a | ||
|
|
03f2c3c8b5 | ||
|
|
aca3139600 | ||
|
|
c1ae593fb4 | ||
|
|
ddca8e4ad0 | ||
|
|
1fe2b398fc | ||
|
|
574ec69cca | ||
|
|
8e94596a6f | ||
|
|
fac2c8a7ce | ||
|
|
a6dad0885f | ||
|
|
65f90637df | ||
|
|
b10f86da00 | ||
|
|
6c3e1aee43 | ||
|
|
e5f8d5742f | ||
|
|
a2ecf8d7b0 | ||
|
|
670130397b | ||
|
|
4a0b3cae98 | ||
|
|
b3b32ab393 | ||
|
|
6ebdb9915b | ||
|
|
2e62bd08e3 | ||
|
|
54f69a5eea | ||
|
|
275c4138ee |
192
.clang-format
Normal file
192
.clang-format
Normal 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
|
||||
...
|
||||
|
||||
8
.github/FUNDING.yml
vendored
Normal file
8
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
github: fraillt
|
||||
buy_me_a_coffee: fraillt
|
||||
custom:
|
||||
- "https://www.paypal.com/paypalme/fraillt"
|
||||
- "https://explorer.solana.com/address/5uHU32nBuniRxg6RZu4tsLWrXGFFz4pwMGHGuCLmkGJQ"
|
||||
- "https://etherscan.io/address/0xe51cb417d1BFcd3EE4cfad9fa11b05631823AADb"
|
||||
- "https://polygonscan.com/address/0xe51cb417d1BFcd3EE4cfad9fa11b05631823AADb"
|
||||
46
.github/workflows/on_linux.yml
vendored
Normal file
46
.github/workflows/on_linux.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
name: On Linux
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ develop, master ]
|
||||
pull_request:
|
||||
branches: [ develop, master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.config.name }}
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- name: "Ubuntu Latest with GCC 14"
|
||||
compiler: gcc
|
||||
compiler_ver: 14
|
||||
- name: "Ubuntu Latests with Clang 18"
|
||||
compiler: clang
|
||||
compiler_ver: 18
|
||||
steps:
|
||||
- name: Prepare specific Clang version
|
||||
if: ${{ matrix.config.compiler == 'clang' }}
|
||||
run: |
|
||||
sudo apt install clang-${{ matrix.config.compiler_ver}}
|
||||
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-${{ matrix.config.compiler_ver}} 100
|
||||
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-${{ matrix.config.compiler_ver}} 100
|
||||
- name: Prepare specific GCC version
|
||||
if: ${{ matrix.config.compiler == 'gcc' }}
|
||||
run: |
|
||||
sudo apt install g++-${{ matrix.config.compiler_ver}}
|
||||
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++-${{ matrix.config.compiler_ver}} 100
|
||||
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-${{ matrix.config.compiler_ver}} 100
|
||||
- name: Installing GTest
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install libgmock-dev
|
||||
- uses: actions/checkout@v3
|
||||
- name: Configure
|
||||
run: cmake -S . -B build -DBITSERY_BUILD_TESTS=ON -DBITSERY_BUILD_EXAMPLES=ON
|
||||
- name: Build
|
||||
run: cmake --build build
|
||||
- name: Run tests
|
||||
run: ctest --test-dir build
|
||||
26
.github/workflows/on_mac.yml
vendored
Normal file
26
.github/workflows/on_mac.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: On Mac
|
||||
on:
|
||||
push:
|
||||
branches: [ develop, master ]
|
||||
pull_request:
|
||||
branches: [ develop, master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: macOS Latest
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Installing GTest
|
||||
run: |
|
||||
git clone https://github.com/google/googletest.git
|
||||
cd googletest
|
||||
git checkout v1.14.0
|
||||
cmake -S . -B build
|
||||
sudo cmake --build build --target install
|
||||
- uses: actions/checkout@v3
|
||||
- name: Configure
|
||||
run: cmake -S . -B build -DBITSERY_BUILD_TESTS=ON -DBITSERY_BUILD_EXAMPLES=ON -DCMAKE_CXX_STANDARD=17
|
||||
- name: Build
|
||||
run: cmake --build build
|
||||
- name: Run tests
|
||||
run: ctest --test-dir build
|
||||
29
.github/workflows/on_windows.yml
vendored
Normal file
29
.github/workflows/on_windows.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: On Windows
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ develop, master ]
|
||||
pull_request:
|
||||
branches: [ develop, master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Windows MSVC Latest
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Installing GTest
|
||||
run: |
|
||||
git clone https://github.com/google/googletest.git
|
||||
cd googletest
|
||||
git checkout v1.14.0
|
||||
cmake -S . -B build -Dgtest_force_shared_crt=ON
|
||||
cmake --build build --config Release --target install
|
||||
- uses: actions/checkout@v3
|
||||
- name: Configure
|
||||
run: cmake -S . -B build -DBITSERY_BUILD_TESTS=ON -DBITSERY_BUILD_EXAMPLES=ON -DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_FLAGS="/Zc:__cplusplus /permissive- /EHsc"
|
||||
env:
|
||||
CMAKE_PREFIX_PATH: "C:/Program Files (x86)/googletest-distribution/"
|
||||
- name: Build
|
||||
run: cmake --build build --config Release
|
||||
- name: Run tests
|
||||
run: ctest --test-dir build
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,5 +1,7 @@
|
||||
.idea/
|
||||
.vs/
|
||||
.vscode/
|
||||
build/
|
||||
cmake-build-debug/
|
||||
cmake-build-*
|
||||
CTestConfig.cmake
|
||||
Testing/
|
||||
|
||||
30
.travis.yml
30
.travis.yml
@@ -1,30 +0,0 @@
|
||||
language: cpp
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
# clang
|
||||
|
||||
before_install:
|
||||
# C++14
|
||||
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
- sudo apt-get update -qq
|
||||
|
||||
install:
|
||||
- sudo apt-get install -qq g++-5 lcov
|
||||
- sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-5 90
|
||||
- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 90
|
||||
- wget https://github.com/google/googletest/archive/release-1.8.0.tar.gz
|
||||
- tar xf release-1.8.0.tar.gz
|
||||
- cd googletest-release-1.8.0
|
||||
- cmake -DBUILD_SHARED_LIBS=ON .
|
||||
- make
|
||||
- sudo make install
|
||||
- cd ..
|
||||
|
||||
before_script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake -DBITSERY_BUILD_TESTS=ON ..
|
||||
|
||||
script: make && (cd tests && ctest)
|
||||
|
||||
287
CHANGELOG.md
287
CHANGELOG.md
@@ -1,3 +1,282 @@
|
||||
# [5.2.5](https://github.com/fraillt/bitsery/compare/v5.2.4...v5.2.5) (2025-10-09)
|
||||
|
||||
### Bug fixes
|
||||
* fix security issue during deserialization where a crafted payload could cause a shared pointer to be assigned to a different type. More information is [here](https://gist.github.com/TrebledJ/750abc64a826f19dd2d6774724629b71). (huge thanks to [Johnathan](https://github.com/TrebledJ))
|
||||
* fix serialization of shared polymorphic pointer-like types by correctly identifying same object (e.g. the same object serialized through `Base` or `Derived` would otherwise have different pointer addresses).
|
||||
* fix polymorphic type assignment to "observer" by adjusting pointer address.
|
||||
* fix spelling of C++ "likely" attribute. #121 (thanks to [Jules](https://github.com/jules-ai))
|
||||
|
||||
### Other notes
|
||||
* format code that was left unformatted in the previous version.
|
||||
* remove broken patch for GCC 4.8.2 (CentOS 7).
|
||||
|
||||
# [5.2.4](https://github.com/fraillt/bitsery/compare/v5.2.3...v5.2.4) (2024-07-30)
|
||||
|
||||
### Improvements
|
||||
* implement brief syntax for std::optional and std::bitset. #116 (thanks to [Destroyerrrocket](https://github.com/Destroyerrrocket))
|
||||
* improve performance for buffer adapters. #118 (thanks to [Destroyerrrocket](https://github.com/Destroyerrrocket))
|
||||
* check if should swap by taking into account actual type (in addition to configuration). #105 (thanks to [SoftdriveFelix](https://github.com/SoftdriveFelix))
|
||||
* fix compile errors for latest compilers. #106 (thanks to [NBurley93](https://github.com/NBurley93))
|
||||
|
||||
### Other notes
|
||||
* change cmake_minimum_required to 3.25.
|
||||
* change compilers for ubuntu (gcc 14 and clang 18).
|
||||
|
||||
# [5.2.3](https://github.com/fraillt/bitsery/compare/v5.2.2...v5.2.3) (2022-12-01)
|
||||
|
||||
### Improvements
|
||||
* refactored bit-packing implementation to make sure that `MeasureSize` adapter acts as all other adapters and is not bit-packing enabled by default. #91
|
||||
* `BufferAdapterTraits::increaseBufferSize` now accepts current size and minimum required size in order to better allocate required memory.
|
||||
|
||||
### Bug fixes
|
||||
* fix shadow warning in gcc8 #87 (thanks to [museghost](https://github.com/museghost) and brian.kim)
|
||||
|
||||
### Other notes
|
||||
* format whole code base in Mozilla style.
|
||||
* simplified adapters implementations
|
||||
|
||||
# [5.2.2](https://github.com/fraillt/bitsery/compare/v5.2.1...v5.2.2) (2021-08-31)
|
||||
|
||||
### Improvements
|
||||
* add 16 byte value support #75 (thanks to [Victor Stewart](https://github.com/victorstewart))
|
||||
* avoid reinitializing nontrivial std::variant #77 (thanks to [Robbert van der Helm](https://github.com/robbert-vdh))
|
||||
* avoid reinitializing nontrivial std::optional.
|
||||
|
||||
### Bug fixes
|
||||
* fix missing headers for GCC11, also added test to check includes #82 (thanks to [michael-mueller-git](https://github.com/michael-mueller-git))
|
||||
* fixed **StdBitset** to build on macOS (proxy type, returned by `[]` operator wasn't correctly converted to unsigned integral type).
|
||||
|
||||
### Other notes
|
||||
* migrated to [Github actions](https://docs.github.com/en/actions) for running tests.
|
||||
* fixes few warnings on MSVC compiler.
|
||||
* now tests are also run on macOS and Windows.
|
||||
|
||||
# [5.2.1](https://github.com/fraillt/bitsery/compare/v5.2.0...v5.2.1) (2020-11-14)
|
||||
|
||||
### Improvements
|
||||
* `Input/OutputBufferAdapter` now statically asserts that underlying type is 1byte in size.
|
||||
|
||||
### Bug fixes
|
||||
* fixed serialization in `StdBitset` when it's size is less then `unsigned long long`.
|
||||
|
||||
# [5.2.0](https://github.com/fraillt/bitsery/compare/v5.1.0...v5.2.0) (2020-11-09)
|
||||
|
||||
### Features
|
||||
|
||||
* new extension **StdBitset**.
|
||||
|
||||
### Improvements
|
||||
* removed unused variable warnings in release build, where `max_size` variable during serialization is ignored.
|
||||
* removed unknown pragmas warnings for GCC/Clang (thanks to [Mmpuskas](https://github.com/Mmpuskas)).
|
||||
|
||||
# [5.1.0](https://github.com/fraillt/bitsery/compare/v5.0.3...v5.1.0) (2020-06-08)
|
||||
|
||||
### Features
|
||||
|
||||
* new extension **StdAtomic** (thanks for [VelocityRa](https://github.com/VelocityRa)).
|
||||
|
||||
### Improvements
|
||||
* [examples](examples) no longer include bitsery in global namespace (removed `using namespace bitsery`).
|
||||
* removed multiple warning regarding integer conversions (thanks to [tower120](https://github.com/tower120)).
|
||||
* removed unnecessary `dynamic_cast` from BasicInput/OutputStreamAdapter (thanks to [tower120](https://github.com/tower120)).
|
||||
* fixed some include paths, now you can basically to copy/paste bitsery include directory to your project without cmake support.
|
||||
|
||||
### Other notes
|
||||
* added tutorial of how to write your own extension ([here](doc/tutorial/first_extension.md)).
|
||||
* now gtest 1.10 is required if you want to build tests.
|
||||
|
||||
# [5.0.3](https://github.com/fraillt/bitsery/compare/v5.0.2...v5.0.3) (2020-01-29)
|
||||
|
||||
### Improvements
|
||||
* rewritten buffer adapters (and `BasicBufferedOutputStreamAdapter`) to fix UB when incrementing past the end iterator, and added an additional read/write method that accepts a number of bytes to be read/written at compile time.
|
||||
This provides additional optimization opportunities.
|
||||
|
||||
# [5.0.2](https://github.com/fraillt/bitsery/compare/v5.0.1...v5.0.2) (2020-01-17)
|
||||
|
||||
### Bug fixes
|
||||
* fixed a bug when deserializing non-default constructible containers (thanks to [nicktrandafil](https://github.com/nicktrandafil)).
|
||||
* fixed issue with a brace initialization in extension StdMap and StdSet. It was working on major compilers, but it wasn't C++11 compatible.
|
||||
More info about it in [stackoverflow](https://stackoverflow.com/questions/25612262/why-does-auto-x3-deduce-an-initializer-list) (thanks to [BotellaA](https://github.com/BotellaA)).
|
||||
|
||||
### Other notes
|
||||
* added [patches/centos7_gcc4.8.2.diff](patches/centos7_gcc4.8.2.diff) that allows to use bitsery with gcc4.8.2 on Centos7 (thanks to [BotellaA](https://github.com/BotellaA)).
|
||||
More information on patches is [here](patches/README.md).
|
||||
* added documentation on how [extensions](doc/design/extensions.md) work.
|
||||
|
||||
# [5.0.1](https://github.com/fraillt/bitsery/compare/v5.0.0...v5.0.1) (2019-08-21)
|
||||
|
||||
### Bug fixes
|
||||
* fixed polymorphic handler deleter in `PolymorphicContext`, now it is captured by value and doesn't depend on lifetime of `PolymorphicContext` instance.
|
||||
* fixed compilation errors in `ext/utils/pointer_utils.h` on macOS.
|
||||
* reduced warnings on Visual Studio (thanks to [BotellaA](https://github.com/BotellaA))
|
||||
|
||||
# [5.0.0](https://github.com/fraillt/bitsery/compare/v4.6.1...v5.0.0) (2019-07-09)
|
||||
|
||||
This version reduces library complexity by removing redundant features and changing existing ones.
|
||||
Additionally, it provides more customization options for serialization/deserialization flow.
|
||||
|
||||
### Features
|
||||
|
||||
* added config options to enable/disable checking input adapter and data errors: **CheckAdapterErrors** and **CheckDataErrors**.
|
||||
If you can trust your data this will improve deserialization performance. Error checks are enabled by default.
|
||||
* all memory allocation in the library can be customized using memory resource (similar to c++ 17 memory resource)
|
||||
* contexts that internally requires to allocate memory are: *PointerLinkingContext, PolymorphicContext, and InheritanceContext*.
|
||||
* extensions, for pointers like types, that allocate memory are: *PointerOwner* and *StdSharedPtr*.
|
||||
More information about pointer extension can be found [here](doc/design/pointers.md).
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* improved design for serializer/deserializer *context*. It was hard to understand and easy to misuse, so several changes were made.
|
||||
* removed *internal* context from config, because it doesn't actually solve any problems, only allows doing the same thing in multiple ways.
|
||||
* removed `T* context()`. This allowed to get a context that is `std::tuple<...>`, but you can do the same with other methods, by wrapping in an outer tuple, e.g. `std::tuple<std::tuple<...>>`.
|
||||
* if a context is defined, in serializer/deserializer, it is passed (and stored) by reference as a first argument (instead of pointer). Other parameters are forwarded to a input/output adapter.
|
||||
* changed signature `T* context<T>` to `T& context<T>`, this will either return context or doesn't compile, previously it could also return nullptr.
|
||||
* `context<T>` and `contextOrNull<T>` now also check if a type is convertible, so it can work with base classes
|
||||
(e.g. you can require a base class of context in extension, but provided child implementation instead). This allows to further customise extension by providing different context implementations.
|
||||
* reworked *BufferedSessions*. It was the only feature that was in a bitsery core and required allocations.
|
||||
It was removed and instead was added ability to change read/write position directly for buffered adapters.
|
||||
This is also a more flexible solution, and can be used to implement solutions that allow deserialization [in-place](https://github.com/fraillt/bitsery/issues/19) like *flatbuffers* does.
|
||||
* changed the signature to all serialization functions that accept lambdas, instead of accepting `(T& )`, now accept `(S& ,T& )`.
|
||||
This allows much easier serialization customization, because no additional state is required in a functor, and these methods can now accept function pointers or stateless lambdas.
|
||||
* removed various functions/classes that became redundant:
|
||||
* *AdapterWriter/Reader* classes - their functionality is moved to *adapters*.
|
||||
* *AdapterAccess* class - now serializer/deserializer expose adapter directly via `adapter()` method.
|
||||
Additionally adapter can be moved out if serializer/deserializer is rvalue .e.g `auto adapter = std::move(ser).adapter();`.
|
||||
* removed *Writer/Reader* parameters from extensions *serialize/deserialize* methods - because serializer/deserializer now expose *adapter* directly.
|
||||
* *archive* from serializer/deserializer - because `operator()` do the same thing, but it is more terse, and is compatible with `cereal` library.
|
||||
* *align* function from serializer/deserializer - it can now be called directly on input/output adapter.
|
||||
* *UnsafeInputBufferAdapter* - instead config option is provided to disable adapter read errors (*CheckAdapterErrors*).
|
||||
* *isValidState* from stream output adapter - it didn't provide any additional information that couldn't be queried directly on stream object.
|
||||
* *registerBasesList* from *PolymorphicContext* - it was previously deprecated.
|
||||
* renamed various classes/functions and files:
|
||||
* *flexible* to **brief_syntax** - this is a big change and might affect a lot of code, but it was necessary, because this name was contradicting to what it actually was trying to achieve.
|
||||
Migration should not be very hard, just global replace `flexible` to `brief_syntax` and it should work:).
|
||||
* *BasicSerializer/BasicDeserializer* to **Serializer/Deserializer** - previously they were aliases, but after removing adapter writer/reader,
|
||||
serializer/deserializer signature has changed to `Serializer/Deserializer<Adapter, Context=void>` and no longer need any alias.
|
||||
* *setError* to **error** for input adapters.
|
||||
* *NetworkEndianness* to **Endianness** in config.
|
||||
* *MeasureSize* adapter moved to separate file `/adapter/measure_size.h`
|
||||
|
||||
### Improvements
|
||||
|
||||
* added `quickSerialization/Deserialization` overloads that can accept context as first parameter.
|
||||
* added convenience classes (functors) **FtorExtValue, FtorExtObject**, in places where you need to provide (des)serialize function/lambda that uses extension.
|
||||
e.g. instead of writing `s.container(obj, [](S& s, MyData& data) {s.ext(data, MyExtension{});});` you can write `s.container(obj, FtorExtObject<MyExtension>{});`
|
||||
* added more tests for adapters.
|
||||
* input adapters now save first error that occured, this allows better diagnostic for what actually happened.
|
||||
|
||||
### Bug fixes
|
||||
* fixed `enabledBitPacking` in serializer/deserializer where writer/reader and internal context states were not restored properly after bit packing is complete.
|
||||
|
||||
# [4.6.1](https://github.com/fraillt/bitsery/compare/v4.6.0...v4.6.1) (2019-06-27)
|
||||
|
||||
### Features
|
||||
* flexible syntax now also supports `cereal` like serialization interface (thanks to [Luli2020](https://github.com/Luli2020))
|
||||
|
||||
### Bug fixes
|
||||
* when using `enableBitPacking`, internal context (i.e. context<T>()) in serializer/deserializer was not copied to/from new bitpacking enabled instance.
|
||||
|
||||
# [4.6.0](https://github.com/fraillt/bitsery/compare/v4.5.1...v4.6.0) (2019-03-12)
|
||||
|
||||
### Features
|
||||
* new extensions **StdTuple** and **StdVariant** for `std::tuple` and `std::variant`. These are the first extensions that requires C++17, or higher, standard enabled.
|
||||
Although `std::tuple` is C++11 type, but from usage perspective it has exactly the same requirements as `std::variant` and relies heavily on having class template argument deduction guides to make it convenient to use.
|
||||
You can easily use `std::tuple` without any extension at all, so the main motivation was to create convenient interface for **StdVariant** and use the same interface for **StdTuple** as well.
|
||||
* instead of providing custom lambda to overload each type in tuple or variant, there was added several helper callable objects.
|
||||
**OverloadValue** wrapper around `s.value<N>(o)`, **OverloadExtValue** wrapper around `s.ext<N>(o, Ext{})` and **OverloadExtObject** wrapper around `s.ext(o, Ext{})`.
|
||||
* new extensions **StdDuration** and **StdTimePoint** for `std::chrono::duration` and `std::chrono::time_point`.
|
||||
|
||||
### Improvements
|
||||
tests now uses `gtest_discover_tests` function, to automatically discover tests, which requires CMake 3.10.
|
||||
|
||||
# [4.5.1](https://github.com/fraillt/bitsery/compare/v4.5.0...v4.5.1) (2019-01-16)
|
||||
|
||||
### Improvements
|
||||
* template specializations, where possible, was changed to avoid using variadics, some Visual Studio compilers has [issues](https://developercommunity.visualstudio.com/content/problem/3437/error-with-c11-variadics.html) with variadic templates.
|
||||
* reduced compile warnings for VisualStudio:
|
||||
* added explicit casts
|
||||
* renamed `struct` to `class` where class is used as friend. e.g. `friend class bitsery::Access`, because it is more conventional usage.
|
||||
|
||||
# [4.5.0](https://github.com/fraillt/bitsery/compare/v4.4.0...v4.5.0) (2019-01-10)
|
||||
|
||||
### Features
|
||||
* ability to create non default constructible objects, by defining private default constructor and making `friend class bitsery::Access;` to access it.
|
||||
It is not necessary to enforce class invariant immediately, because internal object representation will be overriden anyway.
|
||||
|
||||
### Improvements
|
||||
* `StdSmartPtr` supports `std::unique_ptr` with custom deleter.
|
||||
* `*InputBufferAdapter`(all) can also accept const buffer;
|
||||
|
||||
### Bug fixes
|
||||
* fixed deserialization in `bitsery/ext/std_map{set}` when target container is not empty.
|
||||
* added missing template parameters for specializations on `std` containers in multiple files in `bitsery/ext/*`.
|
||||
|
||||
# [4.4.0](https://github.com/fraillt/bitsery/compare/v4.3.0...v4.4.0) (2019-01-08)
|
||||
|
||||
### Features
|
||||
* new extensions **CompactValue** and **CompactValueAsObject**, stores integral values in less space if possible. This is useful when you're working with mostly small values, that in rare cases can be large.
|
||||
E.g. `int64_t money = 8000;` will only use 2 bytes, instead of 8. **CompactValueAsObject** allows to use `ext()` overload, without specifying size of underlying type and sets BUFFER_OVERFLOW error if value doesn't fit in underlying type during deserialization.
|
||||
|
||||
### Improvements
|
||||
* improved **PolymorphicContext**, allows to extend already registered hierarchy in one translation unit, using different type other than `PolymorphicBaseClass` to avoid symbol collision between translation units or libraries.
|
||||
`registerBasesList` was modified, so that it could accept user defined type (instead of `PolymorphicBaseClass`) that is used to declare hierarchy, by default it is `PolymorphicBaseClass`.
|
||||
This introduced breaking change, for those who used this syntax (`registerBasesList<MySerializer, Shape>({})`) during registration.
|
||||
It is encouraged to define helper type, that could be used for registering hierarchy for serialization and deserialization [example](examples/smart_pointers_with_polymorphism.cpp).
|
||||
`This is only relevant then you want to use **PolymorphicContext** between different translation units or libraries`.
|
||||
```cpp
|
||||
//libA
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
template<>
|
||||
struct PolymorphicBaseClass<Shape> : PolymorphicDerivedClasses<Circle, Rectangle> {};
|
||||
}
|
||||
}
|
||||
using MyPolymorphicClassesForRegistering = bitsery::ext::PolymorphicClassesList<Shape>;
|
||||
...
|
||||
ctx.registerBasesList<MySerializer>(MyPolymorphicClassesForRegistering{}).
|
||||
|
||||
//otherLib
|
||||
struct MySquare: Shape {...}
|
||||
//now it must define different type (exactly the same as PolymorphicBaseClass) to declare hierarchy
|
||||
template<typename TBase>
|
||||
struct MyHierarchy {
|
||||
using Childs = PolymorphicClassesList<>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct MyHierarchy<Shape>: bitsery::ext::bitsery::ext::PolymorphicClassesList<MySquare> {};
|
||||
...
|
||||
//notice that we pass MyHierarchy as second argument
|
||||
ctx.registerBasesList<MySerializer, MyHierarchy>(MyPolymorphicClassesForRegistering{}).
|
||||
```
|
||||
* **PolymorphicContext** also get optional method `registerSingleBaseBranch`, that allows manually register hierarchies, this might be more convenient when using you need to register in different translation units (or libraries), but it is error-prone.
|
||||
|
||||
# [4.3.0](https://github.com/fraillt/bitsery/compare/v4.2.1...v4.3.0) (2018-08-23)
|
||||
|
||||
### Features
|
||||
|
||||
* added runtime polymorphism support for pointer like types (raw and smart pointers).
|
||||
In order to enable polymorphism new **PolymorphicContext** was created. It provides capability to register classes with serializer/deserializer.
|
||||
* runtime polymorphism can be customized, by replacing **StandardRTTI** from <bitsery/ext/utils/rtti_utils.h> header.
|
||||
* added smart pointers support for std::unique_ptr, std::shared_ptr and std::weak_ptr via **StdSmartPtr** extension.
|
||||
* new **UnsafeInputBufferAdapter** doesn't check for buffer size on deserialization, on some compilers can improve deserialization performance up to ~40%.
|
||||
|
||||
### Improvements
|
||||
* creatly improved interface for extending/implementing support for pointer like types. Now all pointer like types extends from **PointerObjectExtensionBase** and implements/configures required details.
|
||||
* reimplemented **PointerOwner**, **PointerObserver**, **ReferencedByPointer**.
|
||||
* reimplemented **PointerLinkingContext** to properly support shared objects and runtime polymorphism, pointer ownership for shared objects now has two states: SharedOwner e.g. std::shared_ptr and SharedObserver std::weak_ptr.
|
||||
|
||||
### Other notes
|
||||
There is one *minor?* issue/limitation for pointer like types that uses virtual inheritance. When several pointers points to same object through different static type. it will not work correctly e.g.:
|
||||
```cpp
|
||||
struct Derived: virtual Base {...};
|
||||
struct MyData {
|
||||
std::shared_ptr<Derived> sptr;
|
||||
std::weak_ptr<Base> wptddr;
|
||||
}
|
||||
```
|
||||
In this example wptr and sptr have different static type, and *Derived* is virtually inherited from *Base*, so I get different pointer address for different types.
|
||||
|
||||
# [4.2.1](https://github.com/fraillt/bitsery/compare/v4.2.0...v4.2.1) (2018-03-09)
|
||||
|
||||
### Improvements
|
||||
@@ -106,7 +385,7 @@ Be careful when using deserializing untrusted data and make sure to enforce fund
|
||||
### Features
|
||||
|
||||
* refactored interface, now works with C++11 compiler.
|
||||
* new new extension **Growable**, that allows to have forward/backward compatability within this functions serialization flow. It only allows to append new data at the end of to existing flow without breaking old consumers.
|
||||
* new extension **Growable**, that allows to have forward/backward compatability within this functions serialization flow. It only allows to append new data at the end of to existing flow without breaking old consumers.
|
||||
* old consumer: correctly read old interfce and ignore new data.
|
||||
* new consumer: get defaults (zero values) for new fields, when reading old data.
|
||||
* added new extension for associative *map* containers **ContainerMap**.
|
||||
@@ -158,9 +437,6 @@ Be careful when using deserializing untrusted data and make sure to enforce fund
|
||||
* added user extensible function **ext**, to work with objects that require different serialization/deserialization path (e.g. pointers)
|
||||
* **optional** extension (for *ext* function), to work with *std::optional* types
|
||||
|
||||
### Bug Fixes
|
||||
* *align* method fixed in *BufferReader*
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* file structure changed, added *details* folder.
|
||||
@@ -168,8 +444,9 @@ Be careful when using deserializing untrusted data and make sure to enforce fund
|
||||
* changed parameters order for all functions that use custom function (lambda)
|
||||
* *BufferReader* and *BufferWriter* is now alias types for real types *BasicBufferReader/Writer<DefaultConfig>* (*DefaultConfig* is defined in *common.h*)
|
||||
|
||||
### Bug fixes
|
||||
* *align* method fixed in *BufferReader*
|
||||
|
||||
<a name="1.1.1"></a>
|
||||
# [1.1.1](https://github.com/fraillt/bitsery/compare/v1.0.0...v1.1.1) (2017-02-23)
|
||||
|
||||
### Notes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
cmake_minimum_required(VERSION 3.25)
|
||||
project(bitsery
|
||||
LANGUAGES CXX
|
||||
VERSION 4.2.1)
|
||||
VERSION 5.2.5)
|
||||
|
||||
#======== build options ===================================
|
||||
option(BITSERY_BUILD_EXAMPLES "Build examples" OFF)
|
||||
@@ -41,13 +41,6 @@ install(DIRECTORY include/bitsery
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
|
||||
#================ handle sub-projects =====================
|
||||
# examples and tests are independend directories, that can be built separatelly.
|
||||
# make find_package to no-op, when searching for "Bitsery", because it is created here.
|
||||
macro(find_package)
|
||||
if (NOT ${ARGV0} STREQUAL Bitsery)
|
||||
_find_package(${ARGV})
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
if (BITSERY_BUILD_EXAMPLES)
|
||||
message("build bitsery examples")
|
||||
@@ -58,6 +51,7 @@ endif()
|
||||
|
||||
if (BITSERY_BUILD_TESTS)
|
||||
message("build bitsery tests")
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
else()
|
||||
message("skip bitsery tests")
|
||||
|
||||
@@ -6,7 +6,7 @@ However, to make sure the process of accepting patches goes smoothly, you should
|
||||
you contribute:
|
||||
|
||||
1. Fork the repository.
|
||||
2. Create new branch based on the *master* branch (`git checkout -b your_branch master`). If your contribution is a bug fix, you should name your branch `bugfix/xxx`; for a feature, it should be `feature/xxx`. Otherwise, just use your good judgment. Consistent naming of branches is appreciated since it makes the output of `git branch` easier to understand with a single glance.
|
||||
2. Create new branch based on the *develop* branch (`git checkout -b your_branch develop`). If your contribution is a bug fix, you should name your branch `bugfix/xxx`; for a feature, it should be `feature/xxx`. Otherwise, just use your good judgment. Consistent naming of branches is appreciated since it makes the output of `git branch` easier to understand with a single glance.
|
||||
3. Do your modifications on that branch. Except for special cases, your contribution should include proper unit tests and documentation.
|
||||
4. Make sure your modifications did not break anything by building, running tests:
|
||||
```shell
|
||||
@@ -23,9 +23,19 @@ you contribute:
|
||||
./show_coverage.sh build
|
||||
```
|
||||
5. Commit your changes, and push to your fork (`git push origin your_branch`). Commit message should be one line short description. When applicable, please squash adjacent *wip* commits into a single *logical* commit.
|
||||
6. Open a pull request against Bitsery *master* branch. Currently ongoing development is on *master*. At some point an integration branch will be set-up, and pull-requests should target that, but for now its all against master. You may see feature branches come and go, too.
|
||||
6. Open a pull request against Bitsery *develop* branch.
|
||||
|
||||
|
||||
If you're working with visual studio, there is how to build and run all tests from command line
|
||||
```shell
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DBITSERY_BUILD_TESTS=ON -DGTEST_ROOT="<PATH to GTEST>" -DCMAKE_CXX_FLAGS_RELEASE=/MT ..
|
||||
cmake --build . --config Release
|
||||
(cd tests && ctest -C Release && cd ..)
|
||||
```
|
||||
/MT option might be optional, depending on how gtest was built.
|
||||
|
||||
## Style guide
|
||||
|
||||
Just use your own judgment and stick to the style of the surrounding code.
|
||||
Just use your own judgment and stick to the style of the surrounding code.
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Mindaugas Vinkelis
|
||||
Copyright (c) 2018 Mindaugas Vinkelis
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
66
README.md
66
README.md
@@ -1,6 +1,8 @@
|
||||
# Bitsery
|
||||
|
||||
[](https://travis-ci.org/fraillt/bitsery)
|
||||
[](https://github.com/fraillt/bitsery/actions/workflows/on_windows.yml)
|
||||
[](https://github.com/fraillt/bitsery/actions/workflows/on_mac.yml)
|
||||
[](https://github.com/fraillt/bitsery/actions/workflows/on_linux.yml)
|
||||
[](https://gitter.im/bitsery/Lobby)
|
||||
|
||||
Header only C++ binary serialization library.
|
||||
@@ -15,31 +17,32 @@ All cross-platform requirements are enforced at compile time, so serialized data
|
||||
* Cross-platform compatible.
|
||||
* Optimized for speed and space.
|
||||
* No code generation required: no IDL or metadata, just use your types directly.
|
||||
* Runtime error checking on deserialization.
|
||||
* Configurable runtime error checking on deserialization.
|
||||
* Can read/write from any source: stream (file, network stream. etc... ), or buffer (vector, c-array, etc...).
|
||||
* Don't pay for what you don't use! - customize your serialization via **extensions**. Some notable *extensions* allow:
|
||||
* forward/backward compatibility for your types.
|
||||
* raw pointers (no polymorphism yet).
|
||||
* fine-grained bit-level serialization control.
|
||||
* forward/backward compatibility for your types.
|
||||
* smart and raw pointers with allocators support and customizable runtime polymorphism.
|
||||
* Easily extendable for any type.
|
||||
* Allows flexible or/and verbose syntax for better serialization control.
|
||||
* Configurable endianess support.
|
||||
* Allows brief (similar to [cereal](https://uscilab.github.io/cereal/)) or/and verbose syntax for better serialization control.
|
||||
* Configurable endianness support.
|
||||
* No macros.
|
||||
|
||||
## Why to use bitsery
|
||||
## Why use bitsery
|
||||
|
||||
Look at the numbers and features list, and decide yourself.
|
||||
|
||||
| | binary size | data size | serialize | deserialize |
|
||||
|------------------------------|-------------|-----------|-------------|-------------|
|
||||
| **test_bitsery** | 64704 B | **7565 B**| **1229 ms** | **1086 ms** |
|
||||
| **test_bitsery_compression** | 64880 B | **4784 B**| **1641 ms** | **2462 ms** |
|
||||
| test_yas | 63864 B | 11311 B | 1616 ms | 1712 ms |
|
||||
| test_yas_compression | 72688 B | 8523 B | 2387 ms | 2890 ms |
|
||||
| test_cereal | 74848 B | 11261 B | 6708 ms | 6799 ms |
|
||||
| test_flatbuffers | 67032 B | 16100 B | 8793 ms | 3028 ms |
|
||||
| library | data size | ser time | des time |
|
||||
| ----------- | --------- | -------- | -------- |
|
||||
| bitsery | 6913B | 1119ms | 1166ms |
|
||||
| boost | 11037B | 15391ms | 12912ms |
|
||||
| cereal | 10413B | 10518ms | 10245ms |
|
||||
| flatbuffers | 14924B | 9075ms | 3701ms |
|
||||
| msgpack | 8857B | 3340ms | 13842ms |
|
||||
| protobuf | 10018B | 21229ms | 22077ms |
|
||||
| yas | 10463B | 2107ms | 1554ms |
|
||||
|
||||
*benchmarked on Ubuntu with GCC 7.1.0, more details can be found [here](https://github.com/fraillt/cpp_serializers_benchmark.git)*
|
||||
*benchmarked on Ubuntu with GCC 10.3.0, more details can be found [here](https://github.com/fraillt/cpp_serializers_benchmark.git)*
|
||||
|
||||
If still not convinced read more in library [motivation](doc/design/README.md) section.
|
||||
|
||||
@@ -61,13 +64,11 @@ void serialize(S& s, MyStruct& o) {
|
||||
s.value4b(o.i);
|
||||
s.value2b(o.e);
|
||||
s.container4b(o.fs, 10);
|
||||
};
|
||||
|
||||
using namespace bitsery;
|
||||
}
|
||||
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
|
||||
|
||||
int main() {
|
||||
MyStruct data{8941, MyEnum::V2, {15.0f, -8.5f, 0.045f}};
|
||||
@@ -75,11 +76,10 @@ int main() {
|
||||
|
||||
Buffer buffer;
|
||||
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
|
||||
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
|
||||
auto state = bitsery::quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
|
||||
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(state.first == bitsery::ReaderError::NoError && state.second);
|
||||
assert(data.fs == res.fs && data.i == res.i && data.e == res.e);
|
||||
}
|
||||
```
|
||||
@@ -96,13 +96,19 @@ This documentation comprises these parts:
|
||||
|
||||
Works with C++11 compiler, no additional dependencies, include `<bitsery/bitsery.h>` and you're done.
|
||||
|
||||
> some **bitsery** extensions might require higher C++ standard (e.g. `StdVariant`)
|
||||
|
||||
## Platforms
|
||||
|
||||
This library was tested on
|
||||
* Windows: Visual Studio 2015, MinGW (GCC 5.2)
|
||||
* Linux: GCC 5.4, GCC 6.2, Clang 3.9
|
||||
* OS X Mavericks: AppleClang 8
|
||||
Library is tested on all major compilers on Windows, Linux and macOS.
|
||||
|
||||
## License
|
||||
|
||||
**bitsery** is licensed under the [MIT license](LICENSE).
|
||||
**bitsery** is licensed under the [MIT license](LICENSE).
|
||||
|
||||
## 💖 Sponsor Me?
|
||||
|
||||
If you find this project useful or interesting, or just want to say thanks, you can buy me a coffee!
|
||||
Your support keeps me motivated to maintaining and improving this project.
|
||||
|
||||
[**Thank you!**](https://github.com/sponsors/fraillt)
|
||||
|
||||
@@ -4,68 +4,74 @@ Once you're familiar with the library consider the following reference material.
|
||||
Library design:
|
||||
* `fundamental types`
|
||||
* `valueNb instead of value`
|
||||
* `flexible syntax`
|
||||
* `brief syntax`
|
||||
* `serializer/deserializer functions overloads`
|
||||
* `extending library functionality`
|
||||
* [extending library functionality](design/extensions.md)
|
||||
* `errors handling`
|
||||
* `forward/backward compatibility via Growable extension`
|
||||
* `pointers`
|
||||
* [pointers](design/pointers.md)
|
||||
* `inheritance`
|
||||
* `polymorphism`
|
||||
|
||||
|
||||
Core Serializer/Deserializer functions (alphabetical order):
|
||||
* `align`
|
||||
* `boolValue`
|
||||
* `container`
|
||||
* `ext`
|
||||
* `context`
|
||||
* `context<T>`
|
||||
* `contextOrNull<T>`
|
||||
* `object`
|
||||
* `text`
|
||||
* `value`
|
||||
* `operator()` (4.6.1) (when brief syntax is enabled)
|
||||
* `adapter` (5.0.0)
|
||||
* `boolValue` (4.0.0)
|
||||
* `context<T>` (4.1.0)
|
||||
* `contextOrNull<T>` (4.2.0)
|
||||
* `enableBitPacking` (4.0.0)
|
||||
* `ext` (2.0.0)
|
||||
* `object` (1.0.0)
|
||||
* `text` (1.0.0)
|
||||
* `value` (1.0.0)
|
||||
|
||||
Serializer/Deserializer extensions via `ext` method (alphabetical order):
|
||||
* `BaseClass`
|
||||
* `Entropy`
|
||||
* `Growable`
|
||||
* `PointerOwner`
|
||||
* `PointerObserver`
|
||||
* `ReferencedByPointer`
|
||||
* `StdMap`
|
||||
* `StdOptional`
|
||||
* `StdQueue`
|
||||
* `StdSet`
|
||||
* `StdStack`
|
||||
* `ValueRange`
|
||||
* `VirtualBaseClass`
|
||||
|
||||
AdapterWriter/Reader functions:
|
||||
* `writeBits/readBits`
|
||||
* `writeBytes/readBytes`
|
||||
* `writeBuffer/readBuffer`
|
||||
* `align`
|
||||
* `beginSession/endSession`
|
||||
* `flush (writer only)`
|
||||
* `writtenBytesCount (writer only)`
|
||||
* `setError (reader only)`
|
||||
* `getError (reader only)`
|
||||
* `isCompletedSuccessfully (reader only)`
|
||||
* `BaseClass` (4.2.0)
|
||||
* `CompactValue` (4.4.0)
|
||||
* `CompactValueAsObject` (4.4.0)
|
||||
* `Entropy` (3.0.0)
|
||||
* `Growable` (3.0.0)
|
||||
* `PointerOwner` (4.1.0)
|
||||
* `PointerObserver` (4.1.0)
|
||||
* `ReferencedByPointer` (4.1.0)
|
||||
* `StdDuration` (4.6.0)
|
||||
* `StdMap` (3.0.0)
|
||||
* `StdOptional` (2.0.0)
|
||||
* `StdQueue` (4.0.0)
|
||||
* `StdSet` (4.0.0)
|
||||
* `StdSmartPrt` (4.3.0)
|
||||
* `StdStack` (4.0.0)
|
||||
* `StdTimePoint` (4.6.0)
|
||||
* `StdTuple` (4.6.0) (requires c++17)
|
||||
* `StdVariant` (4.6.0) (requires c++17)
|
||||
* `ValueRange` (3.0.0)
|
||||
* `VirtualBaseClass` (4.2.0)
|
||||
|
||||
Input adapters (buffer and stream) functions:
|
||||
* `read`
|
||||
* `error`
|
||||
* `setError`
|
||||
* `align`
|
||||
* `readBits`
|
||||
* `readBytes`
|
||||
* `readBuffer`
|
||||
* `currentReadPos (get/set)` (buffer adapter only)
|
||||
* `currentReadEndPos (get/set)` (buffer adapter only)
|
||||
* `error (get/set)`
|
||||
* `isCompletedSuccessfully`
|
||||
|
||||
Output adapters (buffer and stream) functions:
|
||||
* `write`
|
||||
* `align`
|
||||
* `writeBits`
|
||||
* `writeBytes`
|
||||
* `writeBuffer`
|
||||
* `flush`
|
||||
* `writtenBytesCount` (buffer adapter only)
|
||||
* `currentyWritePos (get/set)` (buffer adapter only) gets/sets write position in buffer, it can jump past the buffer end, in this case buffer will be resized.
|
||||
This function doesn't write any bytes.
|
||||
* `writtenBytesCount` (buffer adapter only) this doesn't necessary mean how many bytes are written, but rather how many bytes in the buffer was "affected" during serialization.
|
||||
E.g. if `currentyWritePos` (set) jumps from 0 to 100, and then 4 bytes are written, `writtenBytesCount` return 104, it also returns 104 if you jump in somewhere in the middle.
|
||||
|
||||
|
||||
Tips and tricks:
|
||||
* if you're getting static assert "please define 'serialize' function", most likely it is because your **serialize** function is not defined in same namespace as object.
|
||||
* if you're getting static assert "please define 'serialize' function", please define **serialize** function in same namespace as object, or in **bitsery** namespace, for more info [ADL](https://en.cppreference.com/w/cpp/language/adl).
|
||||
|
||||
Other:
|
||||
* [Contributing](../CONTRIBUTING.md)
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
## Motivation
|
||||
|
||||
Inspiration to create **bitsery** came mainly because there aren't any good alternatives for C++.
|
||||
Inspiration to create **bitsery** came mainly because there aren't any good alternatives for C++ that meets my requirements.
|
||||
|
||||
I wanted serializer that is easy to use like [cereal](http://uscilab.github.io/cereal/), is cross-platform compatible, and has support for forward/backward compatibility like [flatbuffers](https://google.github.io/flatbuffers/), is save to use with untrusted (malicious) data, and most importantly is fast and has small binary footprint.
|
||||
I wanted serializer that is easy to use as [cereal](http://uscilab.github.io/cereal/), is cross-platform compatible, and has support for forward/backward compatibility like [flatbuffers](https://google.github.io/flatbuffers/), is safe to use with untrusted (malicious) data, and most importantly is fast and has a small binary footprint.
|
||||
|
||||
Furthermore I wanted full serialization control and ability to work on bit level, so I can further reduce data size. For example, serializing container of [quaternions](https://en.wikipedia.org/wiki/Quaternion) I can reduce size by large amount. *Size of orientation quaternion can be reduced from 128bits (4floats) down to 29bits using "smallest three" technique and still retaining decent precision*.
|
||||
Furthermore, I wanted full serialization control and the ability to work on a bit level, so I can further reduce data size. For example, serializing container of [quaternions](https://en.wikipedia.org/wiki/Quaternion) I can reduce the size by a large amount. *Size of orientation quaternion can be reduced from 128bits (4floats) down to 29bits using "smallest three" technique and still retaining decent precision*.
|
||||
|
||||
Most well-known serialization libraries sacrifice memory and speed efficiency by supporting multiple data formats (binary, json, xml) and multiple languages (C++, C#, Javascript, etc..), these features also adds additional library complexity.
|
||||
Most well-known serialization libraries sacrifice memory and speed efficiency by supporting multiple data formats (binary, json, xml) and multiple languages (C++, C#, Javascript, etc..), these features also add additional library complexity.
|
||||
|
||||
## A word about JSON
|
||||
|
||||
Often times people use C++ because they want speed and memory efficiency, and JSON is not on the list of efficient serialization format.
|
||||
Often people use C++ because they want speed and memory efficiency, and JSON is not on the list of efficient serialization format.
|
||||
Although JSON is very readable and very convenient when used together with dynamically typed languages such as JavaScript.
|
||||
When serializing data from statically typed languages, however, JSON not only has the obvious drawback of runtime inefficiency, but also forces you to write more code to access data (counterintuitively) due to its dynamic-typing serialization system.
|
||||
|
||||
@@ -33,18 +33,18 @@ Now let's review features in more detail.
|
||||
|
||||
* **Cross-platform compatible.** if same code compiles on Android, PS3 console, and your PC either x64 or x86 architecture, you are 100% sure it works.
|
||||
To achieve this, bitsery specifically defines size of underlying data, hence syntax is *value\<2\>* (alias function *value2b*) instead or *value*, or *container2b* for element type of 16bits, eg int16_t.
|
||||
Bitsery also applies endianess transformation if nessesarry.
|
||||
* **Flexible syntax.** if you don't like like writing code with explicitly specifying underlying type size, like *container2b* or *value8b* you can use flexible syntax.
|
||||
Just include <bitsery/flexible.h> and can write like in [cereal](http://uscilab.github.io/cereal/).
|
||||
But do it on your own risk, and static assert using *assertFundamentalTypeSizes* function if you're planing to use it accross multiple platforms.
|
||||
* **Optimized for speed and space.** library itself doesn't do any allocations (except if you use backward/forward compatibility) so data writing/reading is fast as memcpy to/from your buffer.
|
||||
It also doesn't serialize any type information, all information needed is writen in your code!
|
||||
Bitsery also applies endianness transformation if necessary.
|
||||
* **Brief syntax.** If you don't like like writing code with explicitly specifying underlying type size, like *container2b* or *value8b*, you can use brief syntax.
|
||||
Just include <bitsery/brief_syntax.h> and can write like in [cereal](http://uscilab.github.io/cereal/).
|
||||
But do it on your own risk, and static assert using *assertFundamentalTypeSizes* function if you're planing to use it across multiple platforms.
|
||||
* **Optimized for speed and space.** library itself doesn't do any allocations so data writing/reading is fast as memcpy to/from your buffer.
|
||||
It also doesn't serialize any type information, all information needed is written in your code!
|
||||
* **No code generation required: no IDL or metadata** since it doesn't support any other formats except binary, it doesn't need any metadata.
|
||||
* **Runtime error checking on deserialization** library designed to be save with untrusted network data, that's why all overloads that work on containers has *maxSize* value, unless container is static size like *std::array*, this way bitsery ensures that no malicious data crash you.
|
||||
* **Supports forward/backward compatibility for your types** library has optional forward/backward compatibility for types implemented in *AdapterReader/Writer* by allowing to have inner data sessions inside buffer.
|
||||
This is the only functionality that requires dynamic memory allocation.
|
||||
*Growable* extension use these sessions to add compatibility support for your types, in most basic form.
|
||||
You can implement your own extensions if you want to be able to add default values.
|
||||
* **Configurable runtime error checking on deserialization** library designed to be save with untrusted network data, that's why all overloads that work on containers has *maxSize* value, unless container is static size like *std::array*.
|
||||
This way bitsery ensures that no malicious data crash you. BUT, if you trust your data then you can simply disable error checks for better performance.
|
||||
* **Supports forward/backward compatibility for your types** library provides access for buffer input/output adapters to directly change read/write position on the buffer.
|
||||
*Growable* extension use this capability to allow forward/backward compatibility.
|
||||
You can implement your own extensions to do all sorts of other things, like in-place deserialization..
|
||||
* **2-in-1 declarative control flow, same code for serialization and deserialization.** only one function to define, for serialization and deserialization in same manner as *cereal* does.
|
||||
It might be handy to have separate *load* and *save* functions, but Bitsery explicitly doesn't support it, to avoid any serialization deserialization divergence, because it is very hard to catch an errors if you make a bug in one of these functions.
|
||||
The only way around this through extensions, write your custom flow once, and reuse where you need them.
|
||||
@@ -65,5 +65,7 @@ Bitsery allows to use bit-level operations and has two extensions that use them:
|
||||
You want to support your custom container, its fine there is *ContainerTraits* for this, only few methods required to implement.
|
||||
To use same container for buffer writing/reading add specialization to *BufferAdapterTraits*.
|
||||
You want to customize serialization flow - use extensions, only two methods to define, and *ExtensionTraits* to further customize usage.
|
||||
* **Configurable endianess support.** default is *Little Endian*, but if your primary target is PowerPC architecture, eg. PlayStation3, just change your configuration to be *Big Endian*.
|
||||
* **Configurable endianness support.** default is *Little Endian*, but if your primary target is PowerPC architecture, eg. PlayStation3, just change your configuration to be *Big Endian*.
|
||||
* **No macros.** Not so much to say, if you are like me, then it's a feature :)
|
||||
|
||||
It should also be noted, that extensions that allocate memory (e.g. pointer serialization/deserialization) allow to provide memory resource (similar to C++17 `std::pmr::memory_resource`).
|
||||
@@ -0,0 +1,51 @@
|
||||
Extensions are at the heart of bitsery. They allow implementing all sorts of things, that requires customizing serialization and deserialization flows separately.
|
||||
Bitsery already provides a lot of useful extensions, which can be found [here](../../include/bitsery/ext).
|
||||
|
||||
Let's see what are the core components of an extension:
|
||||
|
||||
1. Extension class itself, which implements templated `serialize` and `deserialize` methods. These functions provide similar capabilities to `save` and `load` functions in other frameworks e.g. [cereal](https://uscilab.github.io/cereal/) or [boost](https://www.boost.org/doc/libs/1_71_0/libs/serialization/doc/index.html), but are more powerful because extension itself can store extension related data as well, that can be used for additional functionality.
|
||||
```cpp
|
||||
class MyExtension {
|
||||
public:
|
||||
template<typename Ser, typename T, typename Fnc>
|
||||
void serialize(Ser& ser, const T& obj, Fnc&& fnc) const {
|
||||
...
|
||||
}
|
||||
|
||||
template<typename Des, typename T, typename Fnc>
|
||||
void deserialize(Des& des, T& obj, Fnc&& fnc) const {
|
||||
...
|
||||
}
|
||||
};
|
||||
```
|
||||
2. `ExtensionTraits` specialization for an extension, which specifies how it should be used:
|
||||
```cpp
|
||||
namespace bitsery {
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::MyExtension, T> {
|
||||
using TValue = ...;
|
||||
static constexpr bool SupportValueOverload = ...;
|
||||
static constexpr bool SupportObjectOverload = ...;
|
||||
static constexpr bool SupportLambdaOverload = ...;
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, that we know the core components of an extension, let's see how everything fits together.
|
||||
|
||||
An Extension can be called in 3 different ways, and `Support...Overload` methods basically define, what call syntax can be used with a particular extension.
|
||||
* `SupportValueOverload` - allows to call extension by providing the size of the value type, the same as `valueNb` function. e.g. `s.ext4b(value, MyExtension{})`.
|
||||
* `SupportObjectOverload` - allows to call extension the same as simple `object` function. e.g. `s.ext(value, MyExtension{})`.
|
||||
* `SupportLambdaOverload` - allows to call extension by providing a custom lambda. e.g. `s.ext(value, MyExtension{}, [](...) { ... })`.
|
||||
|
||||
You might wonder, how there are 3 ways to call an extension, but only one signature for `serialize` and `deserialize` functions?
|
||||
|
||||
This is where a `TValue` from `ExtensionTraits` and the third parameter `Fnc` in `serialize` and `deserialize` comes in.
|
||||
|
||||
In case of lambda overload is called, the lambda is passed straight to the serialize/deserialize function as the third parameter. In theory `SupportLambdaOverload` can be any object, not necessary a callable object.
|
||||
When value overload is used, then lambda is constructed by bitsery like this `[](Serializer& s, VType &v) { s.value<VSIZE>(v); }`, where `VType` is equal to `TValue` from `ExtensionTraits`.
|
||||
Similarly, when object overload is used `[](Serializer& s, VType &v) { s.object(v); }` lambda is constructed.
|
||||
|
||||
When there is no direct mapping from object type to its underlying value type, you can disable lambda generation for value and object overload, by setting `TValue=void`, in this case, a "dummy" lambda will be provided.
|
||||
39
doc/design/pointers.md
Normal file
39
doc/design/pointers.md
Normal file
@@ -0,0 +1,39 @@
|
||||
*document in progress*
|
||||
|
||||
## Extensions
|
||||
Raw pointers are managed by three extensions:
|
||||
* **PointerOwner** - manages a lifetime of the pointer, creates or destroys if required.
|
||||
* **PointerObserver** - doesn't own pointer so it doesn't create or destroy anything.
|
||||
* **ReferencedByPointer** - when a non-owning pointer (*PointerObserver*) points to reference type, this extension marks this object as a valid target for PointerObserver.
|
||||
|
||||
"Smart" pointers, from c++ standard lib (std), are managed by:
|
||||
* **StdSmartPtr** - can accept unique_ptr, shared_ptr and weak_ptr
|
||||
|
||||
## Implementation details
|
||||
|
||||
All aforementioned extensions derive from single base class `PointerObjectExtensionBase`.
|
||||
This base class accepts three template parameters that customise its behaviour.
|
||||
* `TPtrManager\<T\>` - describes how particular pointer type should be handled:
|
||||
pointer creation/destruction describes a type of pointer (e.g. owning, shared, observer), and how to actually get value for pointer object.
|
||||
This is the place to start if you want to implement pointer support for your custom type. \<T\> is type of pointer object, e.g. `std::unique_ptr<MyType>`, `MyType*`
|
||||
* `TPolymorphicContext\<RTTI\>` - provides the functionality to register class hierarchies for your types with serializer, and deserializer, in order to polymorphically serialize/deserialize objects.
|
||||
\<RTTI\> template parameter provides runtime information about a type that is used to construct class hierarchies and save them to read/write them to buffer.
|
||||
* `RTTI` - this template parameter provides information if a type is polymorphic, and if it is, then it is used in `TPolymorphicContext\<RTTI\>`.
|
||||
Some pointer managers, like `PointerObserver` and `ReferencedByPointer` never requires polymorphic context. In these cases, you need to provide RTTI that will return `isPolymorphic`=false for all types.
|
||||
By default all pointers extensions use `StandardRTTI` from `/ext/utils/rtti_utils.h` that internally uses `typeid` and `dynamic_cast`.
|
||||
If your environment doesn't allow RTTI, you can provide your own RTTI for your types.
|
||||
|
||||
## Allocation and memory resources
|
||||
|
||||
Allocation is implemented using memory resources (similar to `std::pmr::memory_resource` from c++17),
|
||||
and it is called `MemResourceBase` from "ext/utils/memory_allocator.h". The core difference between standard one
|
||||
is that it has additional `size_t typeId` field for `allocate` and `deallocate` methods, and this typeId is returned from `RTTI`.
|
||||
There are few options to customise pointer allocation for your pointers by using `MemResourceBase`:
|
||||
* invoke `setMemResource` in `PointerLinkingContext`.
|
||||
* pass memory resource to pointer manager constructor, along with boolean parameter that specifies if this memory resource should propagate when deserializing child objects.
|
||||
If no memory resource is provided, then `MemResourceNewDelete` is used, which calls `::operator new(bytes)` and `::operator delete(ptr)`.
|
||||
|
||||
**IMPORTANT**: there are few things that you should know to correctly use custom allocations with `StdSmartPtr`:
|
||||
* Memory resource must live as long as the last object, that was allocated with it (this is required by std::shared_ptr, custom deleter is provided, that will be able to deallocate correctly when a shared pointer is destroyed).
|
||||
* std::unique_ptr is allocated and deallocated using provided memory resource.
|
||||
If you create unique pointers your self, make sure that it uses the same memory resource, because bitsery will not call `.reset` method on pointer, and instead `.release()` it and deallocate manually.
|
||||
@@ -1,8 +1,8 @@
|
||||
The grand plan for this tutorial is to learn how to serialize/deserialize any object efficiently in time and space, so you could focus on other, more interesting things.
|
||||
|
||||
This tutorial will cover these main topics:
|
||||
* `Hello World` write one control flow for both: serialization and deserialization.
|
||||
* `Composer` efficiently compose complex serialization flows.
|
||||
* [Getting started](hello_world.md) with bitsery, and serialize/deserialize your first object.
|
||||
* [Extend to your needs](first_extension.md) by enabling serialization/deserialization depending on version number.
|
||||
* `Squeeze Me!` compress your data when you know what it stores.
|
||||
* `Anything is Possible` extend library for custom container, compress geometry and more.
|
||||
* `Little or Big` change endianness if you want best performance on PowerPC.
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
*document in progress*
|
||||
* explain why *value* and *object* is fundamental functions.
|
||||
* write about *Growable* extension
|
||||
67
doc/tutorial/first_extension.md
Normal file
67
doc/tutorial/first_extension.md
Normal file
@@ -0,0 +1,67 @@
|
||||
... TODO explain step-by-step what we need and how to get there
|
||||
|
||||
Instead I immediately provide implementation for an extension.
|
||||
|
||||
```cpp
|
||||
#include "../details/adapter_common.h"
|
||||
#include "../traits/core/traits.h"
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace ext {
|
||||
|
||||
template<size_t VERSION>
|
||||
class Version {
|
||||
public:
|
||||
|
||||
template<typename Ser, typename T, typename Fnc>
|
||||
void serialize(Ser &ser, const T &v, Fnc &&fnc) const {
|
||||
details::writeSize(ser.adapter(), VERSION);
|
||||
fnc(ser, const_cast<T&>(v), VERSION);
|
||||
}
|
||||
|
||||
template<typename Des, typename T, typename Fnc>
|
||||
void deserialize(Des &des, T &v, Fnc &&fnc) const {
|
||||
size_t version{};
|
||||
details::readSize(des.adapter(), version, 0u, std::false_type{});
|
||||
fnc(des, v, version);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename T, size_t V>
|
||||
struct ExtensionTraits<ext::Version<V>, T> {
|
||||
using TValue = T;
|
||||
static constexpr bool SupportValueOverload = false;
|
||||
static constexpr bool SupportObjectOverload = false;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
Adding such extension to the bitsery itself is impractical because it is very easy to implement, but at the same time it has a lot of customization options that actual user might require e.g.:
|
||||
* how do you want to handle reading/writing version number? (in this case use compact representation as size, but do not check for errors if version number is too large)
|
||||
* maybe you want to be able to set ReaderError, when version is larger than deserialization implementation handles. (we simply ignore this case and later we'll probably get reading error later anyway)
|
||||
* or maybe you want to wrap object in `Growable` extension? so that you could ignore unknown fields in newer version of object.
|
||||
|
||||
Example of how to use this provided implementation:
|
||||
```cpp
|
||||
|
||||
struct TypeV2 {
|
||||
uint16_t x{};
|
||||
uint16_t y{};
|
||||
};
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& ser, TypeV2& obj) {
|
||||
ser.ext(obj, bitsery::ext::Version<2u>{}, [](S& s, TypeV2&o, size_t version) {
|
||||
s.value2b(o.x);
|
||||
if (version == 2u) {
|
||||
s.value2b(o.y);
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
This is a quick guide to get **bitsery** up and running in a matter of minutes.
|
||||
The only prerequisite for running bitsery is a modern C++11 compliant compiler, such as GCC 4.9.4, clang 3.4, MSVC 2015, or newer.
|
||||
Older versions might work, but it is not tested.
|
||||
Older versions might work, but they have not been tested.
|
||||
|
||||
## Get bitsery
|
||||
|
||||
bitsery can be directly included in your project or installed anywhere you can access header files.
|
||||
**bitsery** can be directly included in your project or installed anywhere you can access header files.
|
||||
Grab the latest version, and include directory `bitsery_base_dir/include/` to your project.
|
||||
There's nothing to build or make - **bitsery** is header only.
|
||||
|
||||
@@ -18,20 +18,21 @@ There's nothing to build or make - **bitsery** is header only.
|
||||
#include <bitsery/traits/vector.h>
|
||||
#include <bitsery/traits/string.h>
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
|
||||
|
||||
```
|
||||
|
||||
**bitsery** is very lightweight, so we need to explicitly include what we need.
|
||||
* `<bitsery/bitsery.h>` is a core header, that includes our Serializer and Deserializer
|
||||
* `<bitsery/adapter/buffer.h>` in order to write/read data we need specific adapter, depending on what underlying buffer will be. In this example we'll be using std::vector as our buffer, so we include buffer adapter.
|
||||
* <bitsery/traits/...> traits tells library how efficiently serialize particular container.
|
||||
|
||||
create alias types for *InputAdapter* and *OutputAdapter* using our vector as buffer.
|
||||
Include | Description
|
||||
--|--
|
||||
`<bitsery/bitsery.h>` | This is a core header, that includes our Serializer and Deserializer.
|
||||
`<bitsery/adapter/buffer.h>` | In order to write/read data, we need a specific adapter, depending on what underlying buffer will be. In this example, we'll be using `std::vector` as our buffer, so we include the buffer adapter.
|
||||
`<bitsery/traits/...>` | Traits tell the library how to efficiently serialize a particular container. Many common STL containers are supported out of the box.
|
||||
|
||||
Create alias types for *InputAdapter* and *OutputAdapter* using our vector as buffer.
|
||||
|
||||
## Add serialization method for your type
|
||||
|
||||
@@ -55,11 +56,11 @@ void serialize(S& s, MyStruct& o) {
|
||||
|
||||
**bitsery** also allows to define serialize function in side your class, and can also serialize private class members, just make *friend bitsery::Access;*
|
||||
|
||||
**bitsery** supports two ways how to describe your serialization flow: *verbose syntax* (as in example) or *flexible syntax*, similar to *cereal* library, just include `<bitsery/flexible.h>` to use it.
|
||||
**bitsery** supports two ways how to describe your serialization flow: *verbose syntax* (as in example) or *brief syntax*, similar to *cereal* library, just include `<bitsery/brief_syntax.h>` to use it.
|
||||
|
||||
This example we choosed probably unfamiliar verbose syntax, so lets explain core functionality that you'll use all the time:
|
||||
* **s.value4b(o.i);** serialize fundamental types (ints, floats, enums) value**4b** means, that data type is 4 bytes. If you use same code on different machines, if it compiles it means it is compatible.
|
||||
* **s.text1b(o.str);** serialize text (null-terminated) of char type, if you use *wchar* then you would write *text2b*.
|
||||
* **s.text1b(o.str);** serialize text (null-terminated) of char type, if you use *wchar* then you would write *text2b* or *text4b* depending on the OS platform.
|
||||
* **s.container4b(o.fs, 100);** serializes any container of fundamental types of size 4bytes, **100** is max size of container.
|
||||
**Bitsery** is designed to be save with untrusted (malicious) data from network, so for dynamic containers you always need to provide max possible size available, to avoid buffer-overflow attacks.
|
||||
**text** didn't had this max size specified, because it was serializing fixed size container.
|
||||
@@ -73,9 +74,8 @@ Create buffer and use helper functions for serialization and deserialization.
|
||||
```cpp
|
||||
Buffer buffer;
|
||||
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
|
||||
|
||||
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
auto writtenSize = bitsery::quickSerialization(OutputAdapter{buffer}, data);
|
||||
auto state = bitsery::quickDeserialization(InputAdapter{buffer.begin(), writtenSize}, res);
|
||||
```
|
||||
|
||||
These helper functions use default configuration *bitsery::DefaultConfig*
|
||||
@@ -91,37 +91,34 @@ deserialization state has two properties, error code and bool that indicates if
|
||||
#include <bitsery/traits/vector.h>
|
||||
#include <bitsery/traits/string.h>
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
|
||||
|
||||
struct MyStruct {
|
||||
uint32_t i;
|
||||
char str[6];
|
||||
std::vector<float> fs;
|
||||
uint32_t i;
|
||||
char str[6];
|
||||
std::vector<float> fs;
|
||||
};
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, MyStruct& o) {
|
||||
s.value4b(o.i);
|
||||
s.text1b(o.str);
|
||||
s.container4b(o.fs, 100);
|
||||
};
|
||||
s.value4b(o.i);
|
||||
s.text1b(o.str);
|
||||
s.container4b(o.fs, 100);
|
||||
}
|
||||
|
||||
int main() {
|
||||
MyStruct data{8941, "hello", {15.0f, -8.5f, 0.045f}};
|
||||
MyStruct res{};
|
||||
MyStruct data{8941, "hello", {15.0f, -8.5f, 0.045f}};
|
||||
MyStruct res{};
|
||||
|
||||
Buffer buffer;
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
|
||||
Buffer buffer;
|
||||
auto writtenSize = bitsery::quickSerialization(OutputAdapter{buffer}, data);
|
||||
auto state = bitsery::quickDeserialization(InputAdapter{buffer.begin(), writtenSize}, res);
|
||||
|
||||
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(data.fs == res.fs && data.i == res.i && std::strcmp(data.str, res.str) == 0);
|
||||
assert(state.first == bitsery::ReaderError::NoError && state.second);
|
||||
assert(data.fs == res.fs && data.i == res.i && std::strcmp(data.str, res.str) == 0);
|
||||
}
|
||||
```
|
||||
|
||||
**currently documentation and tutorial is progress, but for more usage examples see examples folder**
|
||||
**currently documentation and tutorial is progress, but for more usage examples see examples folder**
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
#SOFTWARE.
|
||||
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
cmake_minimum_required(VERSION 3.25)
|
||||
project(bitsery_examples CXX)
|
||||
|
||||
if (NOT TARGET Bitsery::bitsery)
|
||||
@@ -29,14 +29,12 @@ endif()
|
||||
|
||||
file(GLOB ExampleFiles ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
|
||||
|
||||
if (WIN32)
|
||||
message(WARNING "Removing example `flexible_assert_linux_x64` for Windows")
|
||||
list(REMOVE_ITEM ExampleFiles ${CMAKE_CURRENT_SOURCE_DIR}/flexible_assert_linux_x64.cpp)
|
||||
endif()
|
||||
|
||||
foreach(ExampleFile ${ExampleFiles})
|
||||
get_filename_component(ExampleName ${ExampleFile} NAME_WE)
|
||||
add_executable(bitsery.example.${ExampleName} ${ExampleFile})
|
||||
target_link_libraries(bitsery.example.${ExampleName} PRIVATE Bitsery::bitsery)
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||
target_compile_options(bitsery.example.${ExampleName} PRIVATE -Wextra -Wno-missing-braces -Wpedantic -Weffc++)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
|
||||
@@ -1,50 +1,62 @@
|
||||
//include bitsery.h to get serialization and deserialization classes
|
||||
// include bitsery.h to get serialization and deserialization classes
|
||||
#include <bitsery/bitsery.h>
|
||||
//in ordered to serialize/deserialize data to buffer, include buffer adapter
|
||||
// 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 {
|
||||
uint32_t i;
|
||||
MyEnum e;
|
||||
std::vector<float> fs;
|
||||
enum class MyEnum : uint16_t
|
||||
{
|
||||
V1,
|
||||
V2,
|
||||
V3
|
||||
};
|
||||
struct MyStruct
|
||||
{
|
||||
uint32_t i;
|
||||
MyEnum e;
|
||||
std::vector<float> fs;
|
||||
};
|
||||
|
||||
//define how object should be serialized/deserialized
|
||||
template <typename S>
|
||||
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
|
||||
// define how object should be serialized/deserialized
|
||||
template<typename S>
|
||||
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
|
||||
}
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
//some helper types
|
||||
// some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
|
||||
|
||||
int main() {
|
||||
//set some random data
|
||||
MyStruct data{8941, MyEnum::V2, {15.0f, -8.5f, 0.045f}};
|
||||
MyStruct res{};
|
||||
int
|
||||
main()
|
||||
{
|
||||
// set some random data
|
||||
MyStruct data{ 8941, MyEnum::V2, { 15.0f, -8.5f, 0.045f } };
|
||||
MyStruct res{};
|
||||
|
||||
//create buffer to store data
|
||||
Buffer buffer;
|
||||
//use quick serialization function,
|
||||
//it will use default configuration to setup all the nesessary steps
|
||||
//and serialize data to container
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
|
||||
// create buffer to store data
|
||||
Buffer buffer;
|
||||
// use quick serialization function,
|
||||
// it will use default configuration to setup all the nesessary steps
|
||||
// and serialize data to container
|
||||
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 = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
// 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);
|
||||
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(data.fs == res.fs && data.i == res.i && data.e == res.e);
|
||||
assert(state.first == bitsery::ReaderError::NoError && state.second);
|
||||
assert(data.fs == res.fs && data.i == res.i && data.e == res.e);
|
||||
}
|
||||
|
||||
@@ -1,64 +1,74 @@
|
||||
#include <bitsery/bitsery.h>
|
||||
#include <bitsery/adapter/buffer.h>
|
||||
//we'll be using std::array as a buffer type, so include traits for this
|
||||
#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>
|
||||
#include <bitsery/traits/vector.h>
|
||||
//include extension that will allow to compress our data
|
||||
// include extension that will allow to compress our data
|
||||
#include <bitsery/ext/value_range.h>
|
||||
|
||||
namespace MyTypes {
|
||||
|
||||
struct Vec3 { float x, y, z; };
|
||||
struct Vec3
|
||||
{
|
||||
float x, y, z;
|
||||
};
|
||||
|
||||
struct Monster {
|
||||
Vec3 pos;
|
||||
std::vector<Vec3> path;
|
||||
std::string name;
|
||||
};
|
||||
struct Monster
|
||||
{
|
||||
Vec3 pos;
|
||||
std::vector<Vec3> path;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
template<typename S>
|
||||
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) {
|
||||
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
|
||||
s.enableBitPacking([&o](typename S::BPEnabledType& sbp) {
|
||||
sbp.container(o.path, 1000, [&sbp](Vec3& vec3) {
|
||||
constexpr bitsery::ext::ValueRange<float> range{-1.0f,1.0f, 0.01f};
|
||||
sbp.ext(vec3.x, range);
|
||||
sbp.ext(vec3.y, range);
|
||||
sbp.ext(vec3.z, range);
|
||||
});
|
||||
});
|
||||
}
|
||||
template<typename S>
|
||||
void
|
||||
serialize(S& s, MyTypes::Vec3& o)
|
||||
{
|
||||
s.value4b(o.x);
|
||||
s.value4b(o.y);
|
||||
s.value4b(o.z);
|
||||
}
|
||||
|
||||
using namespace bitsery;
|
||||
template<typename S>
|
||||
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
|
||||
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 };
|
||||
sbp.ext(vec3.x, range);
|
||||
sbp.ext(vec3.y, range);
|
||||
sbp.ext(vec3.z, range);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//use fixed-size buffer
|
||||
// use fixed-size buffer
|
||||
using Buffer = std::array<uint8_t, 10000>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
|
||||
|
||||
int main() {
|
||||
//set some random data
|
||||
MyTypes::Monster data{};
|
||||
data.name = "lew";
|
||||
int
|
||||
main()
|
||||
{
|
||||
// set some random data
|
||||
MyTypes::Monster data{};
|
||||
data.name = "lew";
|
||||
|
||||
//create buffer to store data to
|
||||
Buffer buffer{};
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
|
||||
// create buffer to store data to
|
||||
Buffer buffer{};
|
||||
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
|
||||
|
||||
MyTypes::Monster res{};
|
||||
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
MyTypes::Monster res{};
|
||||
auto state = bitsery::quickDeserialization<InputAdapter>(
|
||||
{ buffer.begin(), writtenSize }, res);
|
||||
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(state.first == bitsery::ReaderError::NoError && state.second);
|
||||
}
|
||||
|
||||
54
examples/brief_syntax.cpp
Normal file
54
examples/brief_syntax.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#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.
|
||||
#include <bitsery/brief_syntax/vector.h>
|
||||
|
||||
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)
|
||||
{
|
||||
// now we can use brief syntax with
|
||||
s(i, e, fs);
|
||||
}
|
||||
};
|
||||
|
||||
// some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
// set some random data
|
||||
MyStruct data{ 8941, MyEnum::V2, { 15.0f, -8.5f, 0.045f } };
|
||||
MyStruct res{};
|
||||
|
||||
// serialization, deserialization flow is unchanged as in basic usage
|
||||
Buffer buffer;
|
||||
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
|
||||
|
||||
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);
|
||||
}
|
||||
135
examples/composite_types.cpp
Normal file
135
examples/composite_types.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
#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
|
||||
|
||||
#if __cplusplus > 201402L
|
||||
#include <bitsery/ext/std_tuple.h>
|
||||
#include <bitsery/ext/std_variant.h>
|
||||
// let's include this extension to make it more interesting :)
|
||||
#include <bitsery/ext/compact_value.h>
|
||||
|
||||
struct MyStruct
|
||||
{
|
||||
std::vector<int32_t> v{};
|
||||
float f{};
|
||||
|
||||
bool operator==(const MyStruct& rhs) const
|
||||
{
|
||||
return v == rhs.v && f == rhs.f;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename S>
|
||||
void
|
||||
serialize(S& s, MyStruct& o)
|
||||
{
|
||||
s.container4b(o.v, 1000);
|
||||
s.value4b(o.f);
|
||||
}
|
||||
|
||||
// this will be the type that we want to serialize/deserialize
|
||||
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
|
||||
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{
|
||||
// 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{
|
||||
// this is convenient callable object to specify integral value size
|
||||
// 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
|
||||
});
|
||||
},
|
||||
// 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
|
||||
// with OverloadExtObject, like this:
|
||||
// (comment out MyTuple lambda, and uncomment this)
|
||||
// ext::OverloadExtObject<MyTuple, ext::StdTuple<ext::OverloadValue<float,
|
||||
// 4>>>{},
|
||||
|
||||
// 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) {
|
||||
s.ext4b(v, bitsery::ext::CompactValue{});
|
||||
});
|
||||
},
|
||||
// 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); } });
|
||||
}
|
||||
|
||||
// some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
|
||||
// set some random data
|
||||
MyVariant data{ MyTuple{ -7549,
|
||||
{ { -451, 2, 968, 75, 4, 156, 49 }, 874.4f } } };
|
||||
MyVariant res{};
|
||||
|
||||
// create buffer to store data
|
||||
Buffer buffer;
|
||||
// use quick serialization function,
|
||||
// it will use default configuration to setup all the nesessary steps
|
||||
// and serialize data to container
|
||||
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);
|
||||
|
||||
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")
|
||||
#else
|
||||
#pragma message("C++17 is required to enable this example")
|
||||
#endif
|
||||
int
|
||||
main()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -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,98 +8,106 @@
|
||||
|
||||
namespace MyTypes {
|
||||
|
||||
struct Monster {
|
||||
Monster() = default;
|
||||
Monster(std::string _name, uint32_t minDmg, uint32_t maxDmg)
|
||||
:name{_name}, minDamage{minDmg}, maxDamage{maxDmg} {}
|
||||
struct Monster
|
||||
{
|
||||
Monster() = default;
|
||||
Monster(std::string _name, uint32_t minDmg, uint32_t maxDmg)
|
||||
: name{ _name }
|
||||
, minDamage{ minDmg }
|
||||
, maxDamage{ maxDmg }
|
||||
{
|
||||
}
|
||||
|
||||
std::string name{};
|
||||
uint32_t minDamage{};
|
||||
uint32_t maxDamage{};
|
||||
//...
|
||||
};
|
||||
std::string name{};
|
||||
uint32_t minDamage{};
|
||||
uint32_t maxDamage{};
|
||||
//...
|
||||
};
|
||||
|
||||
struct GameState {
|
||||
std::vector<Monster> monsters;
|
||||
};
|
||||
struct GameState
|
||||
{
|
||||
std::vector<Monster> monsters;
|
||||
};
|
||||
|
||||
//default flow for monster
|
||||
template <typename S>
|
||||
void serialize (S& s, Monster& o) {
|
||||
s.text1b(o.name, 20);
|
||||
s.value4b(o.minDamage);
|
||||
s.value4b(o.maxDamage);
|
||||
}
|
||||
// default flow for monster
|
||||
template<typename S>
|
||||
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) {
|
||||
//we can have multiple types in context with std::tuple
|
||||
//this cast also works if our context is the same as cast
|
||||
auto maxMonsters = s.template context<int>();
|
||||
//all data from context is always pointer
|
||||
//if data type doesn't match then it will be compile time error
|
||||
auto dmgRange = s.template context<std::pair<uint32_t, uint32_t>>();
|
||||
s.container(o.monsters, *maxMonsters, [&s, dmgRange] (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
|
||||
bitsery::ext::ValueRange<uint32_t> range{dmgRange->first, dmgRange->second};
|
||||
//enable bit packing
|
||||
s.enableBitPacking([&m, &range](typename S::BPEnabledType& sbp) {
|
||||
sbp.ext(m.minDamage, range);
|
||||
sbp.ext(m.maxDamage, range);
|
||||
});
|
||||
});
|
||||
}
|
||||
template<typename S>
|
||||
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
|
||||
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
|
||||
bitsery::ext::ValueRange<uint32_t> range{ dmgRange.first, dmgRange.second };
|
||||
// enable bit packing
|
||||
s.enableBitPacking([&m, &range](typename S::BPEnabledType& sbp) {
|
||||
sbp.ext(m.minDamage, range);
|
||||
sbp.ext(m.maxDamage, range);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
//use fixed-size buffer
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
//context can contain multiple types
|
||||
//it would make more sense to define separate structure for context, but for sake of this example make it more complex
|
||||
//in serialization function we can cast it like this:
|
||||
// s.template context<int>();
|
||||
//if we want to get whole tuple, just call s.context() without template paramter.
|
||||
//this templated version also works if our context is the same as cast:
|
||||
// struct MyContext {...};
|
||||
// ...
|
||||
// s.template context<MyContext>();
|
||||
// context can contain multiple types by wrapping these types in std::tuple
|
||||
// in serialization function we can get type that we need like this:
|
||||
// s.template context<int>();
|
||||
// this templated version also works if our context is the same as cast:
|
||||
// struct MyContext {...};
|
||||
// ...
|
||||
// s.template context<MyContext>();
|
||||
// NOTE:
|
||||
// if your context has no additional usage outside of serialization flow,
|
||||
// then you can create it internally via configuration (see inheritance.cpp)
|
||||
using Context = std::tuple<int, std::pair<uint32_t, uint32_t>>;
|
||||
|
||||
int main() {
|
||||
// use fixed-size buffer
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
// define adapter types,
|
||||
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
|
||||
|
||||
MyTypes::GameState data{};
|
||||
data.monsters.push_back({"weaksy", 100, 200});
|
||||
data.monsters.push_back({"bigsy", 500, 1000});
|
||||
data.monsters.push_back({"tootoo", 350, 750});
|
||||
int
|
||||
main()
|
||||
{
|
||||
|
||||
//set context
|
||||
Context ctx{};
|
||||
//max monsters
|
||||
std::get<0>(ctx) = 4;
|
||||
//damage range
|
||||
std::get<1>(ctx).first = 100;
|
||||
std::get<1>(ctx).second = 1000;
|
||||
MyTypes::GameState data{};
|
||||
data.monsters.push_back({ "weaksy", 100, 200 });
|
||||
data.monsters.push_back({ "bigsy", 500, 1000 });
|
||||
data.monsters.push_back({ "tootoo", 350, 750 });
|
||||
|
||||
// set context
|
||||
Context ctx{};
|
||||
// max monsters
|
||||
std::get<0>(ctx) = 4;
|
||||
// damage range
|
||||
std::get<1>(ctx).first = 100;
|
||||
std::get<1>(ctx).second = 1000;
|
||||
|
||||
//create buffer to store data to
|
||||
Buffer buffer{};
|
||||
//pass game mode object to serializer as context
|
||||
BasicSerializer<AdapterWriter<OutputAdapter, bitsery::DefaultConfig>, Context> ser{buffer, &ctx};
|
||||
ser.object(data);
|
||||
// create buffer to store data to
|
||||
Buffer buffer{};
|
||||
auto writtenSize =
|
||||
bitsery::quickSerialization(ctx, OutputAdapter{ buffer }, data);
|
||||
|
||||
auto& w = AdapterAccess::getWriter(ser);
|
||||
w.flush();
|
||||
auto writtenSize = w.writtenBytesCount();
|
||||
MyTypes::GameState res{};
|
||||
auto state = bitsery::quickDeserialization(
|
||||
ctx, InputAdapter{ buffer.begin(), writtenSize }, res);
|
||||
|
||||
MyTypes::GameState res{};
|
||||
BasicDeserializer <AdapterReader<InputAdapter, bitsery::DefaultConfig>, Context> des { InputAdapter{buffer.begin(), writtenSize}, &ctx};
|
||||
des.object(res);
|
||||
auto& r = AdapterAccess::getReader(des);
|
||||
|
||||
assert(r.error() == ReaderError::NoError && r.isCompletedSuccessfully());
|
||||
assert(state.first == bitsery::ReaderError::NoError && state.second);
|
||||
}
|
||||
|
||||
@@ -1,60 +1,68 @@
|
||||
#include <bitsery/bitsery.h>
|
||||
//in order to work with streams include stream adapter
|
||||
// in order to work with streams include stream adapter
|
||||
#include <bitsery/adapter/stream.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
enum class MyEnum:uint16_t { V1,V2,V3 };
|
||||
struct MyStruct {
|
||||
uint32_t i;
|
||||
MyEnum e;
|
||||
double f;
|
||||
enum class MyEnum : uint16_t
|
||||
{
|
||||
V1,
|
||||
V2,
|
||||
V3
|
||||
};
|
||||
struct MyStruct
|
||||
{
|
||||
uint32_t i;
|
||||
MyEnum e;
|
||||
double f;
|
||||
};
|
||||
|
||||
//define how object should be serialized/deserialized
|
||||
template <typename S>
|
||||
void serialize(S& s, MyStruct& o) {
|
||||
s.value4b(o.i);
|
||||
s.value2b(o.e);
|
||||
s.value8b(o.f);
|
||||
// define how object should be serialized/deserialized
|
||||
template<typename S>
|
||||
void
|
||||
serialize(S& s, MyStruct& o)
|
||||
{
|
||||
s.value4b(o.i);
|
||||
s.value2b(o.e);
|
||||
s.value8b(o.f);
|
||||
}
|
||||
|
||||
using namespace bitsery;
|
||||
int
|
||||
main()
|
||||
{
|
||||
// set some random data
|
||||
MyStruct data{ 8941, MyEnum::V2, 0.045 };
|
||||
MyStruct res{};
|
||||
|
||||
//some helper types
|
||||
using Stream = std::fstream;
|
||||
// open file stream for writing and reading
|
||||
auto fileName = "test_file.bin";
|
||||
std::fstream s{ fileName, s.binary | s.trunc | s.out };
|
||||
if (!s.is_open()) {
|
||||
std::cout << "cannot open " << fileName << " for writing\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
//set some random data
|
||||
MyStruct data{8941, MyEnum::V2, 0.045};
|
||||
MyStruct res{};
|
||||
// we cannot use quick serialization function, because streams cannot use
|
||||
// writtenBytesCount method
|
||||
bitsery::Serializer<bitsery::OutputBufferedStreamAdapter> ser{ s };
|
||||
ser.object(data);
|
||||
// flush to writer
|
||||
ser.adapter().flush();
|
||||
s.close();
|
||||
// reopen for reading
|
||||
|
||||
//open file stream for writing and reading
|
||||
auto fileName = "test_file.bin";
|
||||
Stream s{fileName, s.binary | s.trunc | s.out};
|
||||
if (!s.is_open()) {
|
||||
std::cout << "cannot open " << fileName << " for writing\n";
|
||||
return 0;
|
||||
}
|
||||
//we cannot use quick serialization function, because streams cannot use writtenBytesCount method
|
||||
//for serialization we can use buffered stream adapter, it can greatly improve performance for some streams
|
||||
Serializer<OutputBufferedStreamAdapter> ser{s};
|
||||
ser.object(data);
|
||||
//flush to writer
|
||||
AdapterAccess::getWriter(ser).flush();
|
||||
s.close();
|
||||
//reopen for reading
|
||||
s.open(fileName, s.binary | s.in);
|
||||
if (!s.is_open()) {
|
||||
std::cout << "cannot open " << fileName << " for reading\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
s.open(fileName, s.binary | s.in);
|
||||
if (!s.is_open()) {
|
||||
std::cout << "cannot open " << fileName << " for reading\n";
|
||||
return 0;
|
||||
}
|
||||
// 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);
|
||||
|
||||
//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 = quickDeserialization<InputStreamAdapter>(s, res);
|
||||
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(data.f == res.f && data.i == res.i && data.e == res.e);
|
||||
assert(state.first == bitsery::ReaderError::NoError && state.second);
|
||||
assert(data.f == res.f && data.i == res.i && data.e == res.e);
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
#include <bitsery/bitsery.h>
|
||||
#include <bitsery/adapter/buffer.h>
|
||||
#include <bitsery/flexible.h>
|
||||
#include <bitsery/flexible/vector.h>
|
||||
|
||||
|
||||
struct MyStruct {
|
||||
int i;
|
||||
unsigned short s;
|
||||
std::vector<long> vl;
|
||||
long long ll;
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s) {
|
||||
//now we can use flexible syntax with
|
||||
//member function has same name as parameter
|
||||
s.archive(this->s, i, vl, ll);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
//some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
|
||||
int main() {
|
||||
//this will only work on linux or mac x64
|
||||
bitsery::assertFundamentalTypeSizes<2,4,8,8>();
|
||||
//set some random data
|
||||
MyStruct data{8941, 3, {15l, -8l, 045l}, 8459845ll};
|
||||
MyStruct res{};
|
||||
|
||||
//serialization, deserialization flow is unchanged as in basic usage
|
||||
Buffer buffer;
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
|
||||
|
||||
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(data.vl == res.vl && data.s == res.s && data.i == res.i && data.ll == res.ll);
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
#include <bitsery/bitsery.h>
|
||||
#include <bitsery/adapter/buffer.h>
|
||||
//include flexible header, to use flexible syntax
|
||||
#include <bitsery/flexible.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 flexible types.
|
||||
//so include everything from <bitsery/flexible/...> instead of <bitsery/traits/...>
|
||||
//otherwise we'll get static assert error, saying to define serialize function.
|
||||
#include <bitsery/flexible/vector.h>
|
||||
|
||||
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) {
|
||||
//now we can use flexible syntax with
|
||||
s.archive(i, e, fs);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
//some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
|
||||
int main() {
|
||||
//set some random data
|
||||
MyStruct data{8941, MyEnum::V2, {15.0f, -8.5f, 0.045f}};
|
||||
MyStruct res{};
|
||||
|
||||
//serialization, deserialization flow is unchanged as in basic usage
|
||||
Buffer buffer;
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
|
||||
|
||||
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(data.fs == res.fs && data.i == res.i && data.e == res.e);
|
||||
}
|
||||
@@ -1,105 +1,114 @@
|
||||
#include <bitsery/bitsery.h>
|
||||
#include <bitsery/adapter/buffer.h>
|
||||
//include traits for types, that we'll be using
|
||||
#include <bitsery/traits/string.h>
|
||||
#include <bitsery/bitsery.h>
|
||||
// include traits for types, that we'll be using
|
||||
#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 extension that will allow to have backward/forward compatibility
|
||||
#include <bitsery/ext/growable.h>
|
||||
|
||||
namespace MyTypes {
|
||||
|
||||
//define data
|
||||
enum Color:uint8_t { Red, Green, Blue };
|
||||
|
||||
struct Vec3 { float x, y, z; };
|
||||
|
||||
struct Weapon {
|
||||
std::string name{};
|
||||
int16_t damage{};
|
||||
Weapon() = default;
|
||||
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) {
|
||||
//forward/backward compatibility for monsters
|
||||
s.ext(*this, bitsery::ext::Growable{}, [&s](Weapon& o1) {
|
||||
s.text1b(o1.name, 20);
|
||||
s.value2b(o1.damage);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
struct Monster {
|
||||
Vec3 pos;
|
||||
int16_t mana;
|
||||
int16_t hp;
|
||||
std::string name;
|
||||
std::vector<uint8_t> inventory;
|
||||
Color color;
|
||||
std::vector<Weapon> weapons;
|
||||
Weapon equipped;
|
||||
std::vector<Vec3> path;
|
||||
};
|
||||
|
||||
template <typename S>
|
||||
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) {
|
||||
//forward/backward compatibility for monsters
|
||||
s.ext(o, bitsery::ext::Growable{}, [&s](Monster& o1) {
|
||||
s.value1b(o1.color);
|
||||
s.value2b(o1.mana);
|
||||
s.value2b(o1.hp);
|
||||
s.object(o1.equipped);
|
||||
s.object(o1.pos);
|
||||
s.container(o1.path, 1000);
|
||||
s.container(o1.weapons, 100);
|
||||
s.container1b(o1.inventory, 50);
|
||||
s.text1b(o1.name, 20);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
//use fixed-size buffer
|
||||
using Buffer = std::array<uint8_t, 10000>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
|
||||
//create configuration that enables session support, to work with "growable" extension
|
||||
struct SessionsEnabled:public DefaultConfig {
|
||||
static constexpr bool BufferSessionsEnabled = true;
|
||||
// define data
|
||||
enum Color : uint8_t
|
||||
{
|
||||
Red,
|
||||
Green,
|
||||
Blue
|
||||
};
|
||||
|
||||
int main() {
|
||||
//set some random data
|
||||
MyTypes::Monster data{};
|
||||
data.name = "lew";
|
||||
data.weapons.push_back(MyTypes::Weapon{"GoodWeapon", 100});
|
||||
struct Vec3
|
||||
{
|
||||
float x, y, z;
|
||||
};
|
||||
|
||||
//create buffer to store data to
|
||||
Buffer buffer{};
|
||||
//since we're using different configuration, we cannot use quickSerialization function.
|
||||
BasicSerializer<AdapterWriter<OutputAdapter, SessionsEnabled>> ser{OutputAdapter{buffer}};
|
||||
ser.object(data);
|
||||
auto& w = AdapterAccess::getWriter(ser);
|
||||
w.flush();
|
||||
auto writtenSize = w.writtenBytesCount();
|
||||
struct Weapon
|
||||
{
|
||||
std::string name{};
|
||||
int16_t damage{};
|
||||
Weapon() = default;
|
||||
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)
|
||||
{
|
||||
// forward/backward compatibility for weapons
|
||||
s.ext(*this, bitsery::ext::Growable{}, [](S& s, Weapon& o1) {
|
||||
s.text1b(o1.name, 20);
|
||||
s.value2b(o1.damage);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
MyTypes::Monster res{};
|
||||
//deserialize
|
||||
BasicDeserializer<AdapterReader<InputAdapter, SessionsEnabled>> des{InputAdapter{buffer.begin(), writtenSize}};
|
||||
des.object(res);
|
||||
auto& r = AdapterAccess::getReader(des);
|
||||
assert(r.error() == ReaderError::NoError && r.isCompletedSuccessfully());
|
||||
struct Monster
|
||||
{
|
||||
Vec3 pos;
|
||||
int16_t mana;
|
||||
int16_t hp;
|
||||
std::string name;
|
||||
std::vector<uint8_t> inventory;
|
||||
Color color;
|
||||
std::vector<Weapon> weapons;
|
||||
Weapon equipped;
|
||||
std::vector<Vec3> path;
|
||||
};
|
||||
|
||||
template<typename S>
|
||||
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)
|
||||
{
|
||||
// forward/backward compatibility for monsters
|
||||
s.ext(o, bitsery::ext::Growable{}, [](S& s, Monster& o1) {
|
||||
s.value1b(o1.color);
|
||||
s.value2b(o1.mana);
|
||||
s.value2b(o1.hp);
|
||||
s.object(o1.equipped);
|
||||
s.object(o1.pos);
|
||||
s.container(o1.path, 1000);
|
||||
s.container(o1.weapons, 100);
|
||||
s.container1b(o1.inventory, 50);
|
||||
s.text1b(o1.name, 20);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// use fixed-size buffer
|
||||
using Buffer = std::array<uint8_t, 10000>;
|
||||
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
// set some random data
|
||||
MyTypes::Monster data{};
|
||||
data.name = "lew";
|
||||
data.weapons.push_back(MyTypes::Weapon{ "GoodWeapon", 100 });
|
||||
|
||||
// create buffer to store data to
|
||||
Buffer buffer{};
|
||||
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
|
||||
|
||||
MyTypes::Monster res{};
|
||||
// deserialize
|
||||
auto state = bitsery::quickDeserialization<InputAdapter>(
|
||||
{ buffer.begin(), writtenSize }, res);
|
||||
|
||||
assert(state.first == bitsery::ReaderError::NoError && state.second);
|
||||
}
|
||||
|
||||
@@ -1,117 +1,134 @@
|
||||
//
|
||||
//this example coverls all the corner cases that can happen using inherintace
|
||||
//in reality virtual inherintance is usually avoided, so your code would look much simpler.
|
||||
// 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.
|
||||
//
|
||||
|
||||
#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
|
||||
// BaseClass - normal inheritance
|
||||
// VirtualBaseClass - when virtual inheritance is used
|
||||
//in order for virtual inheritance to work, InheritanceContext is required.
|
||||
//it can be created either internally (via configuration) or externally (pointer to context).
|
||||
// include inheritance extension
|
||||
// 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
|
||||
#include <bitsery/ext/inheritance.h>
|
||||
|
||||
using bitsery::ext::BaseClass;
|
||||
using bitsery::ext::VirtualBaseClass;
|
||||
|
||||
struct Base {
|
||||
uint8_t x{};
|
||||
//Base doesn't have to be polymorphic class, inheritance works at compile-time.
|
||||
struct Base
|
||||
{
|
||||
uint8_t x{};
|
||||
// Base doesn't have to be polymorphic class, inheritance works at
|
||||
// compile-time.
|
||||
};
|
||||
template <typename S>
|
||||
void serialize(S& s, Base& o) {
|
||||
s.value1b(o.x);
|
||||
template<typename S>
|
||||
void
|
||||
serialize(S& s, Base& o)
|
||||
{
|
||||
s.value1b(o.x);
|
||||
}
|
||||
|
||||
struct Derive1:virtual Base {// virtually inherits from base
|
||||
uint8_t y1{};
|
||||
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/deserialzer
|
||||
s.ext(o, VirtualBaseClass<Base>{});
|
||||
s.value1b(o.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
|
||||
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} {}
|
||||
// to make it more interesting, serialize private member
|
||||
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) {
|
||||
//notice virtual inheritance
|
||||
s.ext(*this, VirtualBaseClass<Base>{});
|
||||
s.value1b(y2);
|
||||
}
|
||||
friend bitsery::Access;
|
||||
uint8_t y2{};
|
||||
template<typename 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} {}
|
||||
uint8_t z{};
|
||||
struct MultipleInheritance
|
||||
: Derive1
|
||||
, Derived2
|
||||
{
|
||||
explicit MultipleInheritance(uint8_t y2)
|
||||
: Derived2{ y2 }
|
||||
{
|
||||
}
|
||||
uint8_t z{};
|
||||
};
|
||||
template <typename S>
|
||||
void serialize(S& s, MultipleInheritance& o) {
|
||||
//has two bases, serialize them separately
|
||||
s.ext(o, BaseClass<Derive1>{});
|
||||
s.ext(o, BaseClass<Derived2>{});
|
||||
s.value1b(o.z);
|
||||
template<typename S>
|
||||
void
|
||||
serialize(S& s, MultipleInheritance& o)
|
||||
{
|
||||
// has two bases, serialize them separately
|
||||
s.ext(o, BaseClass<Derive1>{});
|
||||
s.ext(o, BaseClass<Derived2>{});
|
||||
s.value1b(o.z);
|
||||
}
|
||||
|
||||
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
|
||||
template <>
|
||||
struct SelectSerializeFnc<Derived2>:UseMemberFnc {};
|
||||
// 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
|
||||
{};
|
||||
|
||||
//multiple inheritance has non-member serialize function defined
|
||||
template <>
|
||||
struct SelectSerializeFnc<MultipleInheritance>:UseNonMemberFnc {};
|
||||
// multiple inheritance has non-member serialize function defined
|
||||
template<>
|
||||
struct SelectSerializeFnc<MultipleInheritance> : UseNonMemberFnc
|
||||
{};
|
||||
}
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
// since in this example we're using virtual inheritance we need InheritanceContext as well.
|
||||
// InheritanceContext is default constructable and has no useful information outside of serializer/deserializer
|
||||
// lets create it internally, via configuration
|
||||
struct ConfigWithContext:DefaultConfig {
|
||||
//always add internal contexts to tuple
|
||||
using InternalContext = std::tuple<ext::InheritanceContext>;
|
||||
};
|
||||
|
||||
//some helper types
|
||||
// some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using Writer = AdapterWriter<OutputBufferAdapter<Buffer>, ConfigWithContext>;
|
||||
using Reader = AdapterReader<InputBufferAdapter<Buffer>, ConfigWithContext>;
|
||||
using Writer = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using Reader = bitsery::InputBufferAdapter<Buffer>;
|
||||
|
||||
int main() {
|
||||
int
|
||||
main()
|
||||
{
|
||||
|
||||
MultipleInheritance data{98};
|
||||
data.x = 254;
|
||||
data.y1 = 47;
|
||||
data.z = 1;
|
||||
MultipleInheritance data{ 98 };
|
||||
data.x = 254;
|
||||
data.y1 = 47;
|
||||
data.z = 1;
|
||||
|
||||
Buffer buf{};
|
||||
Buffer buf{};
|
||||
|
||||
BasicSerializer<Writer> ser{OutputBufferAdapter<Buffer>{buf}, nullptr};
|
||||
ser.object(data);
|
||||
auto writtenSize = AdapterAccess::getWriter(ser).writtenBytesCount();
|
||||
bitsery::ext::InheritanceContext ctx1;
|
||||
auto writtenSize = bitsery::quickSerialization(ctx1, Writer{ buf }, data);
|
||||
assert(writtenSize ==
|
||||
4); // base is serialized once, because it is inherited virtually
|
||||
|
||||
MultipleInheritance res{0};
|
||||
BasicDeserializer<Reader> des{InputBufferAdapter<Buffer>{buf.begin(), writtenSize}};
|
||||
des.object(res);
|
||||
assert(AdapterAccess::getReader(des).error() == ReaderError::NoError && AdapterAccess::getReader(des).isCompletedSuccessfully());
|
||||
|
||||
assert(data.x == res.x && data.y1 == res.y1 && data.getY2() == res.getY2() && data.z == res.z);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
74
examples/non_default_constructible.cpp
Normal file
74
examples/non_default_constructible.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// example of how to deserialize non default constructible objects
|
||||
//
|
||||
|
||||
#include <bitsery/adapter/buffer.h>
|
||||
#include <bitsery/bitsery.h>
|
||||
#include <bitsery/traits/vector.h>
|
||||
|
||||
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
|
||||
MyData() = default;
|
||||
// define serialize function
|
||||
|
||||
template<typename 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 }
|
||||
{
|
||||
}
|
||||
// this is for convenience
|
||||
bool operator==(const MyData& rhs) const
|
||||
{
|
||||
return _x == rhs._x && _y == rhs._y;
|
||||
}
|
||||
};
|
||||
|
||||
// some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using Writer = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using Reader = bitsery::InputBufferAdapter<Buffer>;
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
|
||||
// initialize our data
|
||||
std::vector<MyData> data{};
|
||||
data.emplace_back(145.4f, 84.48f);
|
||||
std::vector<MyData> res{};
|
||||
|
||||
// create buffer
|
||||
Buffer buffer{};
|
||||
|
||||
// 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() };
|
||||
des.container(res, 10);
|
||||
|
||||
// check if everything went ok
|
||||
assert(des.adapter().error() == bitsery::ReaderError::NoError &&
|
||||
des.adapter().isCompletedSuccessfully());
|
||||
assert(res == data);
|
||||
}
|
||||
@@ -1,160 +1,166 @@
|
||||
#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.
|
||||
// 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.
|
||||
#include <bitsery/ext/pointer.h>
|
||||
|
||||
using bitsery::ext::ReferencedByPointer;
|
||||
using bitsery::ext::PointerObserver;
|
||||
using bitsery::ext::PointerOwner;
|
||||
using bitsery::ext::PointerType ;
|
||||
using bitsery::ext::PointerType;
|
||||
using bitsery::ext::ReferencedByPointer;
|
||||
|
||||
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, {}} {}
|
||||
uint32_t i;
|
||||
MyEnum e;
|
||||
std::vector<float> fs;
|
||||
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, {} }
|
||||
{
|
||||
}
|
||||
uint32_t i;
|
||||
MyEnum e;
|
||||
std::vector<float> fs;
|
||||
};
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, MyStruct& o) {
|
||||
s.value4b(o.i);
|
||||
s.value2b(o.e);
|
||||
s.container4b(o.fs, 10);
|
||||
template<typename S>
|
||||
void
|
||||
serialize(S& s, MyStruct& o)
|
||||
{
|
||||
s.value4b(o.i);
|
||||
s.value2b(o.e);
|
||||
s.container4b(o.fs, 10);
|
||||
}
|
||||
|
||||
//our test data
|
||||
struct Test1Data {
|
||||
//regular data, nothing fancy here
|
||||
MyStruct o1;
|
||||
int32_t i1;
|
||||
//these container elements can be referenced by pointers
|
||||
std::vector<MyStruct> vdata;
|
||||
//container that holds non owning pointers (observers),
|
||||
std::vector<MyStruct*> vptr;
|
||||
//treat it as is observer
|
||||
MyStruct* po1;
|
||||
//we treat this as owner (responsible for allocation/deallocation
|
||||
int32_t* pi1;
|
||||
// our test data
|
||||
struct Test1Data
|
||||
{
|
||||
// regular data, nothing fancy here
|
||||
MyStruct o1;
|
||||
int32_t i1;
|
||||
// these container elements can be referenced by pointers
|
||||
std::vector<MyStruct> vdata;
|
||||
// container that holds non owning pointers (observers),
|
||||
std::vector<MyStruct*> vptr;
|
||||
// treat it as is observer
|
||||
MyStruct* po1;
|
||||
// we treat this as owner (responsible for allocation/deallocation
|
||||
int32_t* pi1;
|
||||
|
||||
private:
|
||||
friend bitsery::Access;
|
||||
friend bitsery::Access;
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s) {
|
||||
//just a regular fields
|
||||
s.object(o1);
|
||||
s.value4b(i1);
|
||||
template<typename 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](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](MyStruct* (&d)){
|
||||
s.ext(d, PointerObserver{});
|
||||
});
|
||||
//observer
|
||||
s.ext(po1, PointerObserver{});
|
||||
//owner, mark it as not null
|
||||
s.ext4b(pi1, PointerOwner{PointerType::NotNull});
|
||||
|
||||
}
|
||||
// set container elements to be candidates for non-owning pointers
|
||||
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{}); });
|
||||
// observer
|
||||
s.ext(po1, PointerObserver{});
|
||||
// owner, mark it as not null
|
||||
s.ext4b(pi1, PointerOwner{ PointerType::NotNull });
|
||||
}
|
||||
};
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
//some helper types
|
||||
// some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
using Writer = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using Reader = bitsery::InputBufferAdapter<Buffer>;
|
||||
|
||||
//we will need PointerLinkingContext to work with pointers
|
||||
//so lets define our serializer/deserializer
|
||||
//if we need 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
|
||||
using MySerializer = BasicSerializer<AdapterWriter<OutputAdapter, DefaultConfig>, ext::PointerLinkingContext>;
|
||||
using MyDeserializer = BasicDeserializer<AdapterReader<InputAdapter, DefaultConfig>, ext::PointerLinkingContext>;
|
||||
// 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:
|
||||
// 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() {
|
||||
//set some random data
|
||||
Test1Data data{};
|
||||
data.vdata.emplace_back(8941, MyEnum::V1, std::vector<float>{4.4f});
|
||||
data.vdata.emplace_back(15478, MyEnum::V2, std::vector<float>{15.0f});
|
||||
data.vdata.emplace_back(59, MyEnum::V3, std::vector<float>{-8.5f, 0.045f});
|
||||
//container of non owning pointers (observers)
|
||||
data.vptr.emplace_back(nullptr);
|
||||
data.vptr.emplace_back(std::addressof(data.vdata[0]));
|
||||
data.vptr.emplace_back(std::addressof(data.vdata[2]));
|
||||
//regular fields
|
||||
data.o1 = MyStruct{4, MyEnum::V2, {57.078f}};
|
||||
data.i1 = 9455;
|
||||
//observer
|
||||
data.po1 = std::addressof(data.vdata[1]);
|
||||
//owning pointer
|
||||
data.pi1 = new int32_t{};
|
||||
int
|
||||
main()
|
||||
{
|
||||
// set some random data
|
||||
Test1Data data{};
|
||||
data.vdata.emplace_back(8941, MyEnum::V1, std::vector<float>{ 4.4f });
|
||||
data.vdata.emplace_back(15478, MyEnum::V2, std::vector<float>{ 15.0f });
|
||||
data.vdata.emplace_back(59, MyEnum::V3, std::vector<float>{ -8.5f, 0.045f });
|
||||
// container of non owning pointers (observers)
|
||||
data.vptr.emplace_back(nullptr);
|
||||
data.vptr.emplace_back(std::addressof(data.vdata[0]));
|
||||
data.vptr.emplace_back(std::addressof(data.vdata[2]));
|
||||
// regular fields
|
||||
data.o1 = MyStruct{ 4, MyEnum::V2, { 57.078f } };
|
||||
data.i1 = 9455;
|
||||
// observer
|
||||
data.po1 = std::addressof(data.vdata[1]);
|
||||
// owning pointer
|
||||
data.pi1 = new int32_t{};
|
||||
|
||||
//create buffer to store data
|
||||
Buffer buffer{};
|
||||
size_t writtenSize{};
|
||||
//in order to use pointers, we need to pass pointer linking context to serializer/deserializer
|
||||
{
|
||||
ext::PointerLinkingContext ctx{};
|
||||
//pass lining context to serializer, by pointer
|
||||
MySerializer ser{OutputAdapter{buffer}, &ctx};
|
||||
//serialize our data
|
||||
ser.object(data);
|
||||
auto& w = AdapterAccess::getWriter(ser);
|
||||
w.flush();
|
||||
writtenSize = w.writtenBytesCount();
|
||||
// create buffer to store data
|
||||
Buffer buffer{};
|
||||
size_t writtenSize{};
|
||||
// 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
|
||||
assert(ctx.isValid());
|
||||
}
|
||||
// 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
|
||||
assert(ctx.isValid());
|
||||
}
|
||||
|
||||
Test1Data res{};
|
||||
{
|
||||
ext::PointerLinkingContext ctx{};
|
||||
//pass lining context to deserializer, by pointer
|
||||
MyDeserializer des{InputAdapter{buffer.begin(), writtenSize}, &ctx};
|
||||
//deserialize our data
|
||||
des.object(res);
|
||||
auto& r = AdapterAccess::getReader(des);
|
||||
//check if everything went find
|
||||
assert(r.error() == ReaderError::NoError && r.isCompletedSuccessfully());
|
||||
//also check for dangling pointers, after deserialization
|
||||
assert(ctx.isValid());
|
||||
}
|
||||
//owning pointers owns data
|
||||
assert(*res.pi1 == *data.pi1);
|
||||
assert(res.pi1 != data.pi1);
|
||||
//observers, points to other data
|
||||
assert(res.vptr[0] == nullptr);
|
||||
assert(res.vptr[1] == std::addressof(res.vdata[0]));
|
||||
assert(res.vptr[2] == std::addressof(res.vdata[2]));
|
||||
assert(res.po1 == std::addressof(res.vdata[1]));
|
||||
Test1Data res{};
|
||||
{
|
||||
bitsery::ext::PointerLinkingContext ctx{};
|
||||
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
|
||||
assert(ctx.isValid());
|
||||
}
|
||||
// owning pointers owns data
|
||||
assert(*res.pi1 == *data.pi1);
|
||||
assert(res.pi1 != data.pi1);
|
||||
// observers, points to other data
|
||||
assert(res.vptr[0] == nullptr);
|
||||
assert(res.vptr[1] == std::addressof(res.vdata[0]));
|
||||
assert(res.vptr[2] == std::addressof(res.vdata[2]));
|
||||
assert(res.po1 == std::addressof(res.vdata[1]));
|
||||
|
||||
//delete raw owning pointers
|
||||
delete data.pi1;
|
||||
delete res.pi1;
|
||||
// delete raw owning pointers
|
||||
delete data.pi1;
|
||||
delete res.pi1;
|
||||
}
|
||||
|
||||
307
examples/smart_pointers_with_polymorphism.cpp
Normal file
307
examples/smart_pointers_with_polymorphism.cpp
Normal file
@@ -0,0 +1,307 @@
|
||||
//
|
||||
// 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>
|
||||
|
||||
// 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.
|
||||
|
||||
using bitsery::ext::BaseClass;
|
||||
|
||||
using bitsery::ext::PointerObserver;
|
||||
using bitsery::ext::StdSmartPtr;
|
||||
|
||||
// define our data structures
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
struct Shape
|
||||
{
|
||||
Color clr{};
|
||||
virtual ~Shape() = 0;
|
||||
};
|
||||
|
||||
Shape::~Shape() = default;
|
||||
|
||||
struct Circle : Shape
|
||||
{
|
||||
int32_t radius{};
|
||||
bool operator==(const Circle& o) const
|
||||
{
|
||||
return std::tie(radius, clr) == std::tie(o.radius, o.clr);
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
struct RoundedRectangle : Rectangle
|
||||
{
|
||||
int32_t radius{};
|
||||
bool operator==(const RoundedRectangle& o) const
|
||||
{
|
||||
return std::tie(radius, static_cast<const Rectangle&>(*this)) ==
|
||||
std::tie(o.radius, static_cast<const Rectangle&>(o));
|
||||
}
|
||||
};
|
||||
|
||||
// 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
|
||||
s.value4b(o.r);
|
||||
s.value4b(o.g);
|
||||
s.value4b(o.b);
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
void
|
||||
serialize(S& s, Shape& o)
|
||||
{
|
||||
s.object(o.clr);
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
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)
|
||||
{
|
||||
s.ext(o, bitsery::ext::BaseClass<Shape>{});
|
||||
s.value4b(o.width);
|
||||
s.value4b(o.height);
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
void
|
||||
serialize(S& s, RoundedRectangle& o)
|
||||
{
|
||||
s.ext(o, bitsery::ext::BaseClass<Rectangle>{});
|
||||
s.value4b(o.radius);
|
||||
}
|
||||
|
||||
// define our test structure
|
||||
struct SomeShapes
|
||||
{
|
||||
std::vector<std::shared_ptr<Shape>> sharedList;
|
||||
std::unique_ptr<Shape> uniquePtr;
|
||||
// weak ptr and refPtr will point to sharedList
|
||||
std::weak_ptr<Shape> weakPtr;
|
||||
Shape* refPtr;
|
||||
};
|
||||
|
||||
// creates object, and populates some data
|
||||
SomeShapes
|
||||
createData()
|
||||
{
|
||||
SomeShapes data{};
|
||||
{
|
||||
auto tmp = new RoundedRectangle{};
|
||||
tmp->height = 151572;
|
||||
tmp->width = 488795;
|
||||
tmp->radius = 898;
|
||||
tmp->clr.r = 0.5f;
|
||||
tmp->clr.g = 1.0f;
|
||||
tmp->clr.b = 1.0f;
|
||||
data.uniquePtr.reset(tmp);
|
||||
}
|
||||
{
|
||||
auto tmp = new Circle{};
|
||||
tmp->radius = 75987;
|
||||
tmp->clr.r = 0.5f;
|
||||
tmp->clr.g = 0.0f;
|
||||
tmp->clr.b = 1.0f;
|
||||
data.sharedList.emplace_back(tmp);
|
||||
}
|
||||
{
|
||||
auto tmp = new Rectangle{};
|
||||
tmp->height = 15157;
|
||||
tmp->width = 48879;
|
||||
tmp->clr.r = 1.0f;
|
||||
tmp->clr.g = 0.0f;
|
||||
tmp->clr.b = 0.0f;
|
||||
data.sharedList.emplace_back(tmp);
|
||||
}
|
||||
data.weakPtr = data.sharedList[0];
|
||||
data.refPtr = data.sharedList[1].get();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
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,
|
||||
// bitsery will work regardless
|
||||
s.ext(o.weakPtr, StdSmartPtr{});
|
||||
s.ext(o.refPtr, PointerObserver{});
|
||||
s.container(o.sharedList, 100, [](S& s, std::shared_ptr<Shape>& item) {
|
||||
s.ext(item, StdSmartPtr{});
|
||||
});
|
||||
}
|
||||
|
||||
// STEP 1
|
||||
// define relationships between base and derived classes
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
|
||||
// for each base class define DIRECTLY derived classes
|
||||
// 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>
|
||||
{};
|
||||
|
||||
template<>
|
||||
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>;
|
||||
|
||||
// some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using Writer = bitsery::OutputBufferAdapter<Buffer>;
|
||||
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,
|
||||
bitsery::ext::PolymorphicContext<bitsery::ext::StandardRTTI>>;
|
||||
// NOTE:
|
||||
// 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)
|
||||
{
|
||||
{
|
||||
auto d = dynamic_cast<RoundedRectangle*>(data.uniquePtr.get());
|
||||
auto r = dynamic_cast<RoundedRectangle*>(res.uniquePtr.get());
|
||||
assert(r != nullptr);
|
||||
assert(*d == *r);
|
||||
}
|
||||
{
|
||||
auto d = dynamic_cast<Circle*>(data.sharedList[0].get());
|
||||
auto r = dynamic_cast<Circle*>(res.sharedList[0].get());
|
||||
assert(r != nullptr);
|
||||
assert(*d == *r);
|
||||
}
|
||||
{
|
||||
auto d = dynamic_cast<Rectangle*>(data.sharedList[1].get());
|
||||
auto r = dynamic_cast<Rectangle*>(res.sharedList[1].get());
|
||||
assert(r != nullptr);
|
||||
assert(*d == *r);
|
||||
}
|
||||
assert(res.weakPtr.lock().get() == res.sharedList[0].get());
|
||||
assert(res.refPtr == res.sharedList[1].get());
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
// 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
|
||||
TContext ctx{};
|
||||
std::get<1>(ctx).registerBasesList<MySerializer>(
|
||||
MyPolymorphicClassesForRegistering{});
|
||||
// create writer and serialize
|
||||
MySerializer ser{ ctx, buffer };
|
||||
ser.object(data);
|
||||
ser.adapter().flush();
|
||||
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
|
||||
assert(std::get<0>(ctx).isValid());
|
||||
}
|
||||
SomeShapes res{};
|
||||
{
|
||||
TContext ctx{};
|
||||
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());
|
||||
// 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
|
||||
std::get<0>(ctx).clearSharedState();
|
||||
assert(res.weakPtr.use_count() == 1);
|
||||
}
|
||||
assertSameShapes(data, res);
|
||||
return 0;
|
||||
}
|
||||
3
format.sh
Executable file
3
format.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
find . -regex '.*\.\(cpp\|hpp\|cu\|c\|h\)' -exec clang-format -style=file -i {} \;
|
||||
|
||||
|
||||
@@ -1,207 +1,304 @@
|
||||
//MIT License
|
||||
// MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_ADAPTER_BUFFER_H
|
||||
#define BITSERY_ADAPTER_BUFFER_H
|
||||
|
||||
#include "../details/adapter_common.h"
|
||||
#include "../bitsery.h"
|
||||
#include "../details/adapter_bit_packing.h"
|
||||
#include "../traits/core/traits.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
//base class that stores container iterators, and is required for session support (for reading sessions data)
|
||||
template <typename Buffer>
|
||||
class BufferIterators {
|
||||
protected:
|
||||
using TIterator = typename traits::BufferAdapterTraits<Buffer>::TIterator;
|
||||
template<typename Buffer, typename Config = DefaultConfig>
|
||||
class InputBufferAdapter
|
||||
: public details::InputAdapterBaseCRTP<InputBufferAdapter<Buffer, Config>>
|
||||
{
|
||||
public:
|
||||
friend details::InputAdapterBaseCRTP<InputBufferAdapter<Buffer, Config>>;
|
||||
|
||||
BufferIterators(TIterator begin, TIterator end)
|
||||
:posIt{begin},
|
||||
endIt{end}
|
||||
{
|
||||
}
|
||||
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,
|
||||
"Please define BufferAdapterTraits or include from <bitsery/traits/...>");
|
||||
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.");
|
||||
|
||||
friend details::SessionAccess;
|
||||
InputBufferAdapter(TIterator beginIt, size_t size)
|
||||
: _beginIt{ beginIt }
|
||||
, _currOffset{ 0 }
|
||||
, _endReadOffset{ size }
|
||||
, _bufferSize{ size } {};
|
||||
|
||||
TIterator posIt;
|
||||
TIterator endIt;
|
||||
};
|
||||
InputBufferAdapter(TIterator beginIt, TIterator endIt)
|
||||
: InputBufferAdapter(beginIt,
|
||||
static_cast<size_t>(std::distance(beginIt, endIt)))
|
||||
{
|
||||
}
|
||||
|
||||
InputBufferAdapter(const InputBufferAdapter&) = delete;
|
||||
InputBufferAdapter& operator=(const InputBufferAdapter&) = delete;
|
||||
|
||||
template <typename Buffer>
|
||||
class InputBufferAdapter: public BufferIterators<Buffer> {
|
||||
public:
|
||||
InputBufferAdapter(InputBufferAdapter&&) = default;
|
||||
InputBufferAdapter& operator=(InputBufferAdapter&&) = default;
|
||||
|
||||
using TIterator = typename BufferIterators<Buffer>::TIterator;
|
||||
using TValue = typename traits::BufferAdapterTraits<Buffer>::TValue;
|
||||
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");
|
||||
void currentReadPos(size_t pos)
|
||||
{
|
||||
currentReadPosChecked(
|
||||
pos, std::integral_constant<bool, Config::CheckAdapterErrors>{});
|
||||
}
|
||||
|
||||
InputBufferAdapter(TIterator begin, TIterator end): BufferIterators<Buffer>(begin, end)
|
||||
{
|
||||
}
|
||||
size_t currentReadPos() const
|
||||
{
|
||||
return currentReadPosChecked(
|
||||
std::integral_constant<bool, Config::CheckAdapterErrors>{});
|
||||
}
|
||||
|
||||
InputBufferAdapter(TIterator begin, size_t size)
|
||||
:InputBufferAdapter(begin, std::next(begin, size))
|
||||
{
|
||||
}
|
||||
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)
|
||||
pos = _bufferSize;
|
||||
_endReadOffset = pos;
|
||||
} else {
|
||||
error(ReaderError::DataOverflow);
|
||||
}
|
||||
}
|
||||
|
||||
void read(TValue* data, size_t size) {
|
||||
//for optimization
|
||||
auto tmp = this->posIt;
|
||||
this->posIt += size;
|
||||
if (std::distance(this->posIt, this->endIt) >= 0) {
|
||||
std::memcpy(data, std::addressof(*tmp), size);
|
||||
} else {
|
||||
this->posIt -= size;
|
||||
//set everything to zeros
|
||||
std::memset(data, 0, size);
|
||||
size_t currentReadEndPos() const
|
||||
{
|
||||
if (_overflowOnReadEndPos)
|
||||
return 0;
|
||||
return _endReadOffset;
|
||||
}
|
||||
|
||||
if (error() == ReaderError::NoError)
|
||||
setError(ReaderError::DataOverflow);
|
||||
}
|
||||
}
|
||||
ReaderError error() const
|
||||
{
|
||||
return _currOffset <= _endReadOffset
|
||||
? ReaderError::NoError
|
||||
: static_cast<ReaderError>(_currOffset - _endReadOffset);
|
||||
}
|
||||
|
||||
ReaderError error() const {
|
||||
auto res = std::distance(this->endIt, this->posIt);
|
||||
if (res > 0) {
|
||||
auto err = static_cast<ReaderError>(res);
|
||||
return err;
|
||||
}
|
||||
return ReaderError::NoError;
|
||||
}
|
||||
void error(ReaderError error)
|
||||
{
|
||||
if (_currOffset <= _endReadOffset) {
|
||||
_endReadOffset = 0;
|
||||
_bufferSize = 0;
|
||||
_currOffset = static_cast<size_t>(error);
|
||||
}
|
||||
}
|
||||
|
||||
void setError(ReaderError error) {
|
||||
this->endIt = this->posIt;
|
||||
//to avoid creating temporary for error state, mark an error by passing posIt after the endIt
|
||||
std::advance(this->posIt, static_cast<size_t>(error));
|
||||
}
|
||||
bool isCompletedSuccessfully() const { return _currOffset == _bufferSize; }
|
||||
|
||||
bool isCompletedSuccessfully() const {
|
||||
return this->posIt == this->endIt;
|
||||
}
|
||||
};
|
||||
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>{});
|
||||
}
|
||||
|
||||
template<typename Buffer>
|
||||
class OutputBufferAdapter {
|
||||
public:
|
||||
void readInternalBuffer(TValue* data, size_t size)
|
||||
{
|
||||
readInternalImpl(
|
||||
data, size, std::integral_constant<bool, Config::CheckAdapterErrors>{});
|
||||
}
|
||||
|
||||
using TIterator = typename traits::BufferAdapterTraits<Buffer>::TIterator;
|
||||
using TValue = typename traits::BufferAdapterTraits<Buffer>::TValue;
|
||||
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;
|
||||
}
|
||||
|
||||
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");
|
||||
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);
|
||||
_currOffset = newOffset;
|
||||
} else {
|
||||
// set everything to zeros
|
||||
std::memset(data, 0, size);
|
||||
if (_overflowOnReadEndPos)
|
||||
error(ReaderError::DataOverflow);
|
||||
}
|
||||
}
|
||||
|
||||
OutputBufferAdapter(Buffer &buffer)
|
||||
: _buffer{std::addressof(buffer)}
|
||||
{
|
||||
void currentReadPosChecked(size_t pos, std::true_type)
|
||||
{
|
||||
if (_bufferSize >= pos && error() == ReaderError::NoError) {
|
||||
_currOffset = pos;
|
||||
} else {
|
||||
error(ReaderError::DataOverflow);
|
||||
}
|
||||
}
|
||||
|
||||
init(TResizable{});
|
||||
}
|
||||
void currentReadPosChecked(size_t pos, std::false_type) { _currOffset = pos; }
|
||||
|
||||
size_t currentReadPosChecked(std::true_type) const
|
||||
{
|
||||
return error() == ReaderError::NoError ? _currOffset : 0;
|
||||
}
|
||||
|
||||
void write(const TValue *data, size_t size) {
|
||||
writeInternal(data, size, TResizable{});
|
||||
}
|
||||
size_t currentReadPosChecked(std::false_type) const { return _currOffset; }
|
||||
|
||||
void flush() {
|
||||
//this function might be useful for stream adapters
|
||||
}
|
||||
TIterator _beginIt;
|
||||
size_t _currOffset;
|
||||
size_t _endReadOffset;
|
||||
size_t _bufferSize;
|
||||
bool _overflowOnReadEndPos = true;
|
||||
};
|
||||
|
||||
size_t writtenBytesCount() const {
|
||||
return static_cast<size_t>(std::distance(std::begin(*_buffer), _outIt));
|
||||
}
|
||||
template<typename Buffer, typename Config = DefaultConfig>
|
||||
class OutputBufferAdapter
|
||||
: public details::OutputAdapterBaseCRTP<OutputBufferAdapter<Buffer, Config>>
|
||||
{
|
||||
public:
|
||||
friend details::OutputAdapterBaseCRTP<OutputBufferAdapter<Buffer, Config>>;
|
||||
|
||||
private:
|
||||
using TResizable = std::integral_constant<bool, traits::ContainerTraits<Buffer>::isResizable>;
|
||||
using BitPackingEnabled = details::OutputAdapterBitPackingWrapper<
|
||||
OutputBufferAdapter<Buffer, Config>>;
|
||||
using TConfig = Config;
|
||||
using TIterator = typename traits::BufferAdapterTraits<Buffer>::TIterator;
|
||||
using TValue = typename traits::BufferAdapterTraits<Buffer>::TValue;
|
||||
|
||||
Buffer* _buffer;
|
||||
TIterator _outIt{};
|
||||
TIterator _end{};
|
||||
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.");
|
||||
|
||||
/*
|
||||
* resizable buffer
|
||||
*/
|
||||
OutputBufferAdapter(Buffer& buffer)
|
||||
: _buffer{ std::addressof(buffer) }
|
||||
, _beginIt{ std::begin(buffer) }
|
||||
, _bufferSize{ traits::ContainerTraits<Buffer>::size(buffer) }
|
||||
{
|
||||
}
|
||||
|
||||
void init(std::true_type) {
|
||||
//resize buffer immediately, because we need output iterator at valid position
|
||||
if (traits::ContainerTraits<Buffer>::size(*_buffer) == 0u) {
|
||||
traits::BufferAdapterTraits<Buffer>::increaseBufferSize(*_buffer);
|
||||
}
|
||||
_end = std::end(*_buffer);
|
||||
_outIt = std::begin(*_buffer);
|
||||
}
|
||||
OutputBufferAdapter(const OutputBufferAdapter&) = delete;
|
||||
OutputBufferAdapter& operator=(const OutputBufferAdapter&) = delete;
|
||||
OutputBufferAdapter(OutputBufferAdapter&&) = default;
|
||||
OutputBufferAdapter& operator=(OutputBufferAdapter&&) = default;
|
||||
|
||||
void writeInternal(const TValue *data, const size_t size, std::true_type) {
|
||||
//optimization
|
||||
#if defined(_MSC_VER) && (_ITERATOR_DEBUG_LEVEL > 0)
|
||||
using TDistance = typename std::iterator_traits<TIterator>::difference_type;
|
||||
if (std::distance(_outIt , _end) >= static_cast<TDistance>(size)) {
|
||||
std::memcpy(std::addressof(*_outIt), data, size);
|
||||
_outIt += size;
|
||||
#else
|
||||
auto tmp = _outIt;
|
||||
_outIt += size;
|
||||
if (std::distance(_outIt , _end) >= 0) {
|
||||
std::memcpy(std::addressof(*tmp), data, size);
|
||||
#endif
|
||||
} else {
|
||||
#if defined(_MSC_VER) && (_ITERATOR_DEBUG_LEVEL > 0)
|
||||
void currentWritePos(size_t pos)
|
||||
{
|
||||
const auto maxPos = _currOffset > pos ? _currOffset : pos;
|
||||
if (maxPos > _biggestCurrentPos) {
|
||||
_biggestCurrentPos = maxPos;
|
||||
}
|
||||
maybeResize(pos, TResizable{});
|
||||
_currOffset = pos;
|
||||
}
|
||||
|
||||
#else
|
||||
_outIt -= size;
|
||||
#endif
|
||||
//get current position before invalidating iterators
|
||||
const auto pos = std::distance(std::begin(*_buffer), _outIt);
|
||||
//increase container size
|
||||
traits::BufferAdapterTraits<Buffer>::increaseBufferSize(*_buffer);
|
||||
//restore iterators
|
||||
_end = std::end(*_buffer);
|
||||
_outIt = std::next(std::begin(*_buffer), pos);
|
||||
size_t currentWritePos() const { return _currOffset; }
|
||||
|
||||
writeInternal(data, size, std::true_type{});
|
||||
}
|
||||
}
|
||||
void flush()
|
||||
{
|
||||
// this function might be useful for stream adapters
|
||||
}
|
||||
|
||||
/*
|
||||
* non resizable buffer
|
||||
*/
|
||||
void init(std::false_type) {
|
||||
_outIt = std::begin(*_buffer);
|
||||
_end = std::end(*_buffer);
|
||||
}
|
||||
size_t writtenBytesCount() const
|
||||
{
|
||||
return _currOffset > _biggestCurrentPos ? _currOffset : _biggestCurrentPos;
|
||||
}
|
||||
|
||||
void writeInternal(const TValue *data, size_t size, std::false_type) {
|
||||
//optimization
|
||||
auto tmp = _outIt;
|
||||
_outIt += size;
|
||||
assert(std::distance(_outIt, _end) >= 0);
|
||||
memcpy(std::addressof(*tmp), data, size);
|
||||
}
|
||||
};
|
||||
private:
|
||||
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)
|
||||
{
|
||||
writeInternalImpl(data, SIZE);
|
||||
}
|
||||
|
||||
void writeInternalBuffer(const TValue* data, size_t size)
|
||||
{
|
||||
writeInternalImpl(data, size);
|
||||
}
|
||||
|
||||
Buffer* _buffer;
|
||||
TIterator _beginIt;
|
||||
size_t _currOffset{ 0 };
|
||||
size_t _bufferSize{ 0 };
|
||||
size_t _biggestCurrentPos{ 0 };
|
||||
|
||||
void maybeResize(size_t newOffset, std::true_type)
|
||||
{
|
||||
if (newOffset > _bufferSize)
|
||||
BITSERY_UNLIKELY
|
||||
{
|
||||
doResize(newOffset);
|
||||
}
|
||||
}
|
||||
|
||||
void maybeResize(size_t newOffset, std::false_type)
|
||||
{
|
||||
static_cast<void>(newOffset);
|
||||
assert(newOffset <= _bufferSize);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
BITSERY_NOINLINE void doResize(size_t newOffset)
|
||||
{
|
||||
traits::BufferAdapterTraits<Buffer>::increaseBufferSize(
|
||||
*_buffer, _currOffset, newOffset);
|
||||
_beginIt = std::begin(*_buffer);
|
||||
_bufferSize = traits::ContainerTraits<Buffer>::size(*_buffer);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_ADAPTER_BUFFER_H
|
||||
#endif // BITSERY_ADAPTER_BUFFER_H
|
||||
|
||||
88
include/bitsery/adapter/measure_size.h
Normal file
88
include/bitsery/adapter/measure_size.h
Normal file
@@ -0,0 +1,88 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2019 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_ADAPTER_MEASURE_SIZE_H
|
||||
#define BITSERY_ADAPTER_MEASURE_SIZE_H
|
||||
|
||||
#include "../details/adapter_bit_packing.h"
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template<typename Config>
|
||||
class BasicMeasureSize
|
||||
{
|
||||
public:
|
||||
using BitPackingEnabled =
|
||||
details::BasicMeasureSizeBitPackingWrapper<BasicMeasureSize<Config>>;
|
||||
using TConfig = Config;
|
||||
using TValue = void;
|
||||
|
||||
template<size_t SIZE, typename 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)
|
||||
{
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
_currPos += SIZE * count;
|
||||
}
|
||||
|
||||
void currentWritePos(size_t pos)
|
||||
{
|
||||
const auto maxPos = _currPos > pos ? _currPos : pos;
|
||||
if (maxPos > _biggestCurrentPos) {
|
||||
_biggestCurrentPos = maxPos;
|
||||
}
|
||||
_currPos = pos;
|
||||
}
|
||||
|
||||
size_t currentWritePos() const { return _currPos; }
|
||||
|
||||
void align() {}
|
||||
|
||||
void flush() {}
|
||||
|
||||
// get size in bytes
|
||||
size_t writtenBytesCount() const
|
||||
{
|
||||
return _currPos > _biggestCurrentPos ? _currPos : _biggestCurrentPos;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t _biggestCurrentPos{};
|
||||
size_t _currPos{};
|
||||
};
|
||||
|
||||
// helper type for default config
|
||||
using MeasureSize = BasicMeasureSize<DefaultConfig>;
|
||||
|
||||
}
|
||||
|
||||
#endif // BITSERY_ADAPTER_MEASURE_SIZE_H
|
||||
@@ -1,227 +1,376 @@
|
||||
//MIT License
|
||||
// MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_ADAPTER_STREAM_H
|
||||
#define BITSERY_ADAPTER_STREAM_H
|
||||
|
||||
#include "../details/adapter_common.h"
|
||||
#include "../details/adapter_bit_packing.h"
|
||||
#include "../traits/array.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <ios>
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template <typename TChar, typename CharTraits>
|
||||
class BasicInputStreamAdapter {
|
||||
public:
|
||||
using TValue = TChar;
|
||||
using TIterator = void;//TIterator is used with sessions, but streams cannot be used with sessions
|
||||
template<typename TChar, typename Config, typename CharTraits>
|
||||
class BasicInputStreamAdapter
|
||||
: public details::InputAdapterBaseCRTP<
|
||||
BasicInputStreamAdapter<TChar, Config, CharTraits>>
|
||||
{
|
||||
public:
|
||||
friend details::InputAdapterBaseCRTP<
|
||||
BasicInputStreamAdapter<TChar, Config, CharTraits>>;
|
||||
|
||||
BasicInputStreamAdapter(std::basic_ios<TChar, CharTraits>& istream)
|
||||
:_ios{std::addressof(istream)} {}
|
||||
using BitPackingEnabled = details::InputAdapterBitPackingWrapper<
|
||||
BasicInputStreamAdapter<TChar, Config, CharTraits>>;
|
||||
using TConfig = Config;
|
||||
using TValue = TChar;
|
||||
|
||||
void read(TValue* data, size_t size) {
|
||||
if (static_cast<size_t>(_ios->rdbuf()->sgetn( data , size )) != size) {
|
||||
*data = {};
|
||||
//check state, if not set by stream, set it manually
|
||||
if (_ios->good())
|
||||
_ios->setstate(std::ios_base::eofbit);
|
||||
}
|
||||
BasicInputStreamAdapter(std::basic_ios<TChar, CharTraits>& istream)
|
||||
: _ios{ std::addressof(istream) }
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
BasicInputStreamAdapter(const BasicInputStreamAdapter&) = delete;
|
||||
BasicInputStreamAdapter& operator=(const BasicInputStreamAdapter&) = delete;
|
||||
|
||||
ReaderError error() const {
|
||||
if (_ios->good())
|
||||
return ReaderError::NoError;
|
||||
return _ios->eof()
|
||||
? ReaderError::DataOverflow
|
||||
: ReaderError::ReadingError;
|
||||
}
|
||||
bool isCompletedSuccessfully() const {
|
||||
if (error() == ReaderError::NoError) {
|
||||
return _ios->rdbuf()->sgetc() == CharTraits::eof();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void setError(ReaderError ) {
|
||||
//has no effect when using
|
||||
}
|
||||
BasicInputStreamAdapter(BasicInputStreamAdapter&&) = default;
|
||||
BasicInputStreamAdapter& operator=(BasicInputStreamAdapter&&) = default;
|
||||
|
||||
private:
|
||||
std::basic_ios<TChar, CharTraits>* _ios;
|
||||
};
|
||||
void currentReadPos(size_t)
|
||||
{
|
||||
static_assert(std::is_void<TChar>::value,
|
||||
"setting read position is not supported with StreamAdapter");
|
||||
}
|
||||
|
||||
template <typename TChar, typename CharTraits>
|
||||
class BasicOutputStreamAdapter {
|
||||
public:
|
||||
using TValue = TChar;
|
||||
using TIterator = void;//TIterator is used with sessions, but streams cannot be used with sessions
|
||||
size_t currentReadPos() const
|
||||
{
|
||||
static_assert(std::is_void<TChar>::value,
|
||||
"setting read position is not supported with StreamAdapter");
|
||||
return {};
|
||||
}
|
||||
|
||||
BasicOutputStreamAdapter(std::basic_ios<TChar, CharTraits>& ostream)
|
||||
:_ios{std::addressof(ostream)} {}
|
||||
void currentReadEndPos(size_t)
|
||||
{
|
||||
static_assert(std::is_void<TChar>::value,
|
||||
"setting read position is not supported with StreamAdapter");
|
||||
}
|
||||
|
||||
void write(const TValue* data, size_t size) {
|
||||
//for optimization
|
||||
_ios->rdbuf()->sputn( data , size );
|
||||
}
|
||||
size_t currentReadEndPos() const
|
||||
{
|
||||
static_assert(std::is_void<TChar>::value,
|
||||
"setting read position is not supported with StreamAdapter");
|
||||
return {};
|
||||
}
|
||||
|
||||
void flush() {
|
||||
if (auto ostream = dynamic_cast<std::basic_ostream<TChar, CharTraits>*>(_ios))
|
||||
ostream->flush();
|
||||
}
|
||||
ReaderError error() const { return _err; }
|
||||
|
||||
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;
|
||||
}
|
||||
bool isCompletedSuccessfully() const
|
||||
{
|
||||
if (error() == ReaderError::NoError) {
|
||||
return _ios->rdbuf()->sgetc() == CharTraits::eof();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//this method is only for stream writing
|
||||
bool isValidState() const {
|
||||
return !_ios->bad();
|
||||
}
|
||||
void error(ReaderError error)
|
||||
{
|
||||
if (_err == ReaderError::NoError) {
|
||||
_err = error;
|
||||
_zeroIfNoErrors = std::numeric_limits<size_t>::max();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::basic_ios<TChar, CharTraits>* _ios;
|
||||
};
|
||||
private:
|
||||
template<size_t SIZE>
|
||||
void readInternalValue(TValue* data)
|
||||
{
|
||||
readChecked(
|
||||
data, SIZE, std::integral_constant<bool, Config::CheckAdapterErrors>{});
|
||||
}
|
||||
|
||||
template <typename TChar, typename CharTraits, typename TBuffer = std::array<TChar, 256>>
|
||||
class BasicBufferedOutputStreamAdapter {
|
||||
public:
|
||||
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");
|
||||
using TValue = TChar;
|
||||
using TIterator = void;//TIterator is used with sessions, but streams cannot be used with sessions
|
||||
void readInternalBuffer(TValue* data, size_t size)
|
||||
{
|
||||
readChecked(
|
||||
data, size, std::integral_constant<bool, Config::CheckAdapterErrors>{});
|
||||
}
|
||||
|
||||
//bufferSize is used when buffer is dynamically allocated
|
||||
BasicBufferedOutputStreamAdapter(std::basic_ios<TChar, CharTraits>& ostream, size_t bufferSize = 256)
|
||||
:_adapter(ostream),
|
||||
_buf{},
|
||||
_outIt{}
|
||||
{
|
||||
init(bufferSize, TResizable{});
|
||||
}
|
||||
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
|
||||
? ReaderError::ReadingError
|
||||
: ReaderError::DataOverflow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//we need to explicitly declare move logic, in case buffer is static, because after move it will be invalidated
|
||||
BasicBufferedOutputStreamAdapter(const BasicBufferedOutputStreamAdapter&) = delete;
|
||||
BasicBufferedOutputStreamAdapter& operator = (const BasicBufferedOutputStreamAdapter&) = delete;
|
||||
void readChecked(TValue* data, size_t size, std::false_type)
|
||||
{
|
||||
_ios->rdbuf()->sgetn(data, static_cast<std::streamsize>(size));
|
||||
}
|
||||
|
||||
BasicBufferedOutputStreamAdapter(BasicBufferedOutputStreamAdapter&& rhs)
|
||||
: _adapter{std::move(rhs._adapter)},
|
||||
_buf{},
|
||||
_outIt{}
|
||||
{
|
||||
auto size = std::distance(std::begin(rhs._buf), rhs._outIt);
|
||||
_buf = std::move(rhs._buf);
|
||||
_outIt = std::next(std::begin(_buf), size);
|
||||
};
|
||||
std::basic_ios<TChar, CharTraits>* _ios;
|
||||
size_t _zeroIfNoErrors{};
|
||||
ReaderError _err = ReaderError::NoError;
|
||||
};
|
||||
|
||||
BasicBufferedOutputStreamAdapter& operator = (BasicBufferedOutputStreamAdapter&& rhs) {
|
||||
_adapter = std::move(rhs._adapter);
|
||||
//get current written size, before move
|
||||
auto size = std::distance(std::begin(rhs._buf), rhs._outIt);
|
||||
_buf = std::move(rhs._buf);
|
||||
_outIt = std::next(std::begin(_buf), size);
|
||||
return *this;
|
||||
};
|
||||
template<typename TChar, typename Config, typename CharTraits>
|
||||
class BasicOutputStreamAdapter
|
||||
: public details::OutputAdapterBaseCRTP<
|
||||
BasicOutputStreamAdapter<TChar, Config, CharTraits>>
|
||||
{
|
||||
public:
|
||||
friend details::OutputAdapterBaseCRTP<
|
||||
BasicOutputStreamAdapter<TChar, Config, CharTraits>>;
|
||||
|
||||
~BasicBufferedOutputStreamAdapter() = default;
|
||||
using BitPackingEnabled = details::OutputAdapterBitPackingWrapper<
|
||||
BasicOutputStreamAdapter<TChar, Config, CharTraits>>;
|
||||
using TConfig = Config;
|
||||
using TValue = TChar;
|
||||
|
||||
void write(const TValue* data, size_t size) {
|
||||
auto tmp = _outIt;
|
||||
BasicOutputStreamAdapter(std::basic_ostream<TChar, CharTraits>& ostream)
|
||||
: _ostream{ std::addressof(ostream) }
|
||||
{
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER) && (_ITERATOR_DEBUG_LEVEL > 0)
|
||||
using TDistance = typename std::iterator_traits<BufferIt>::difference_type;
|
||||
if (std::distance(_outIt , std::end(_buf)) >= static_cast<TDistance>(size)) {
|
||||
std::memcpy(std::addressof(*_outIt), data, size);
|
||||
_outIt += size;
|
||||
#else
|
||||
_outIt += size;
|
||||
if (std::distance(_outIt , std::end(_buf)) >= 0) {
|
||||
std::memcpy(std::addressof(*tmp), data, size);
|
||||
#endif
|
||||
} else {
|
||||
//when buffer is full write out to stream
|
||||
_outIt = std::begin(_buf);
|
||||
_adapter.write(std::addressof(*_outIt), static_cast<size_t>(std::distance(_outIt, tmp)));
|
||||
_adapter.write(data, size);
|
||||
}
|
||||
}
|
||||
void currentWritePos(size_t)
|
||||
{
|
||||
static_assert(std::is_void<TChar>::value,
|
||||
"setting write position is not supported with StreamAdapter");
|
||||
}
|
||||
|
||||
void flush() {
|
||||
auto begin = std::begin(_buf);
|
||||
_adapter.write(std::addressof(*begin), static_cast<size_t>(std::distance(begin, _outIt)));
|
||||
_outIt = begin;
|
||||
_adapter.flush();
|
||||
}
|
||||
size_t currentWritePos() const
|
||||
{
|
||||
static_assert(std::is_void<TChar>::value,
|
||||
"setting write position is not supported with StreamAdapter");
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t writtenBytesCount() const {
|
||||
return _adapter.writtenBytesCount();
|
||||
}
|
||||
void flush() { _ostream->flush(); }
|
||||
|
||||
//this method is only for stream writing
|
||||
bool isValidState() const {
|
||||
return _adapter.isValidState();
|
||||
}
|
||||
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>;
|
||||
private:
|
||||
template<size_t SIZE>
|
||||
void writeInternalValue(const TValue* data)
|
||||
{
|
||||
_ostream->rdbuf()->sputn(data, SIZE);
|
||||
}
|
||||
|
||||
void init (size_t bufferSize, std::true_type) {
|
||||
_buf.resize(bufferSize);
|
||||
_outIt = std::begin(_buf);
|
||||
}
|
||||
void init (size_t, std::false_type) {
|
||||
_outIt = std::begin(_buf);
|
||||
}
|
||||
void writeInternalBuffer(const TValue* data, size_t size)
|
||||
{
|
||||
_ostream->rdbuf()->sputn(data, size);
|
||||
}
|
||||
|
||||
BasicOutputStreamAdapter<TChar, CharTraits> _adapter;
|
||||
TBuffer _buf;
|
||||
BufferIt _outIt;
|
||||
};
|
||||
std::basic_ostream<TChar, CharTraits>* _ostream;
|
||||
};
|
||||
|
||||
template <typename TChar, typename CharTraits>
|
||||
class BasicIOStreamAdapter:public BasicInputStreamAdapter<TChar, CharTraits>, public BasicOutputStreamAdapter<TChar, CharTraits> {
|
||||
public:
|
||||
using TValue = TChar;
|
||||
using TIterator = void;//TIterator is used with sessions, but streams cannot be used with sessions
|
||||
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>>;
|
||||
|
||||
//both bases contain reference to same iostream, so no need to do anything
|
||||
BasicIOStreamAdapter(std::basic_ios<TChar, CharTraits>& iostream)
|
||||
:BasicInputStreamAdapter<TChar, CharTraits>{iostream},
|
||||
BasicOutputStreamAdapter<TChar, CharTraits>{iostream} {
|
||||
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");
|
||||
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 }
|
||||
{
|
||||
init(bufferSize, TResizable{});
|
||||
// buffer size must be atleast 16, because writeIntervalValue expect that at
|
||||
// least one value fits to buffer.
|
||||
assert(_bufferSize >= 16);
|
||||
}
|
||||
|
||||
//helper types for most common implementations for std streams
|
||||
using OutputStreamAdapter = BasicOutputStreamAdapter<char, std::char_traits<char>>;
|
||||
using InputStreamAdapter = BasicInputStreamAdapter<char, std::char_traits<char>>;
|
||||
using IOStreamAdapter = BasicIOStreamAdapter<char, std::char_traits<char>>;
|
||||
// we need to explicitly declare move logic, because after move buffer might
|
||||
// be invalidated
|
||||
BasicBufferedOutputStreamAdapter(const BasicBufferedOutputStreamAdapter&) =
|
||||
delete;
|
||||
BasicBufferedOutputStreamAdapter& operator=(
|
||||
const BasicBufferedOutputStreamAdapter&) = delete;
|
||||
|
||||
using OutputBufferedStreamAdapter = BasicBufferedOutputStreamAdapter<char, std::char_traits<char>>;
|
||||
BasicBufferedOutputStreamAdapter(BasicBufferedOutputStreamAdapter&& rhs)
|
||||
: _ostream{ rhs._ostream }
|
||||
, _buf{ std::move(rhs._buf) }
|
||||
, _beginIt{ std::begin(_buf) }
|
||||
, _currOffset{ rhs._currOffset }
|
||||
, _bufferSize{ rhs._bufferSize } {};
|
||||
|
||||
BasicBufferedOutputStreamAdapter& operator=(
|
||||
BasicBufferedOutputStreamAdapter&& rhs)
|
||||
{
|
||||
_ostream = rhs._ostream;
|
||||
_buf = std::move(rhs._buf);
|
||||
_beginIt = std::begin(_buf);
|
||||
_currOffset = rhs._currOffset;
|
||||
_bufferSize = rhs._bufferSize;
|
||||
return *this;
|
||||
};
|
||||
|
||||
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()
|
||||
{
|
||||
writeBufferToStream();
|
||||
_ostream->flush();
|
||||
}
|
||||
|
||||
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 diff_t = typename std::iterator_traits<BufferIt>::difference_type;
|
||||
|
||||
template<size_t SIZE>
|
||||
void writeInternalValue(const TValue* data)
|
||||
{
|
||||
writeInternalImpl(data, SIZE);
|
||||
}
|
||||
|
||||
void writeInternalBuffer(const TValue* data, size_t size)
|
||||
{
|
||||
writeInternalImpl(data, 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));
|
||||
_currOffset = newOffset;
|
||||
} else {
|
||||
writeBufferToStream();
|
||||
// write buffer directly to stream
|
||||
_ostream->rdbuf()->sputn(data, static_cast<std::streamsize>(size));
|
||||
}
|
||||
}
|
||||
|
||||
void writeBufferToStream()
|
||||
{
|
||||
_ostream->rdbuf()->sputn(std::addressof(*_beginIt),
|
||||
static_cast<std::streamsize>(_currOffset));
|
||||
_currOffset = 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// ignore buffer size parameter, and instead take actual buffer size
|
||||
_bufferSize = traits::ContainerTraits<Buffer>::size(_buf);
|
||||
}
|
||||
|
||||
std::basic_ostream<TChar, CharTraits>* _ostream;
|
||||
TBuffer _buf;
|
||||
BufferIt _beginIt;
|
||||
size_t _currOffset;
|
||||
size_t _bufferSize{ 0 };
|
||||
};
|
||||
|
||||
template<typename TChar, typename Config, typename CharTraits>
|
||||
class BasicIOStreamAdapter
|
||||
: public BasicInputStreamAdapter<TChar, Config, CharTraits>
|
||||
, public BasicOutputStreamAdapter<TChar, Config, CharTraits>
|
||||
{
|
||||
public:
|
||||
using TValue = TChar;
|
||||
|
||||
// both bases contain reference to same iostream, so no need to do anything
|
||||
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 OutputBufferedStreamAdapter =
|
||||
BasicBufferedOutputStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
|
||||
}
|
||||
|
||||
#endif //BITSERY_ADAPTER_STREAM_H
|
||||
#endif // BITSERY_ADAPTER_STREAM_H
|
||||
|
||||
@@ -1,272 +0,0 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
|
||||
#ifndef BITSERY_ADAPTER_READER_H
|
||||
#define BITSERY_ADAPTER_READER_H
|
||||
|
||||
#include "details/sessions.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template <typename TReader>
|
||||
class AdapterReaderBitPackingWrapper;
|
||||
|
||||
template<typename InputAdapter, typename Config>
|
||||
struct AdapterReader {
|
||||
//this is required by deserializer
|
||||
static constexpr bool BitPackingEnabled = false;
|
||||
using TConfig = Config;
|
||||
using TValue = typename InputAdapter::TValue;
|
||||
|
||||
static_assert(details::IsDefined<TValue>::value, "Please define adapter traits or include from <bitsery/traits/...>");
|
||||
|
||||
using TIterator = typename InputAdapter::TIterator;// used by session reader
|
||||
|
||||
explicit AdapterReader(InputAdapter&& adapter)
|
||||
: _inputAdapter{std::move(adapter)},
|
||||
_session{*this, _inputAdapter}
|
||||
{
|
||||
}
|
||||
|
||||
AdapterReader(const AdapterReader &) = delete;
|
||||
|
||||
AdapterReader &operator=(const AdapterReader &) = delete;
|
||||
|
||||
//todo add conditional noexcept
|
||||
AdapterReader(AdapterReader &&) = default;
|
||||
|
||||
AdapterReader &operator=(AdapterReader &&) = default;
|
||||
|
||||
~AdapterReader() noexcept = default;
|
||||
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBytes(T &v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
directRead(&v, 1);
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBuffer(T *buf, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
directRead(buf, count);
|
||||
}
|
||||
|
||||
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 align() {
|
||||
}
|
||||
|
||||
bool isCompletedSuccessfully() const {
|
||||
return _inputAdapter.isCompletedSuccessfully() && !_session.hasActiveSessions();
|
||||
}
|
||||
|
||||
ReaderError error() const {
|
||||
auto err = _inputAdapter.error();
|
||||
if (err == ReaderError::DataOverflow && _session.hasActiveSessions())
|
||||
return ReaderError::NoError;
|
||||
return err;
|
||||
}
|
||||
|
||||
void setError(ReaderError error) {
|
||||
if (this->error() == ReaderError::NoError)
|
||||
_inputAdapter.setError(error);
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
if (error() == ReaderError::NoError) {
|
||||
_session.begin();
|
||||
}
|
||||
}
|
||||
|
||||
void endSession() {
|
||||
if (error() == ReaderError::NoError) {
|
||||
_session.end();
|
||||
}
|
||||
}
|
||||
|
||||
const InputAdapter& adapter() const {
|
||||
return _inputAdapter;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class AdapterReaderBitPackingWrapper<AdapterReader<InputAdapter, Config>>;
|
||||
|
||||
InputAdapter _inputAdapter;
|
||||
typename std::conditional<Config::BufferSessionsEnabled,
|
||||
session::SessionsReader<AdapterReader<InputAdapter, Config>>,
|
||||
session::DisabledSessionsReader<AdapterReader<InputAdapter, Config>>>::type
|
||||
_session;
|
||||
|
||||
template<typename T>
|
||||
void directRead(T *v, size_t count) {
|
||||
static_assert(!std::is_const<T>::value, "");
|
||||
_inputAdapter.read(reinterpret_cast<TValue *>(v), sizeof(T) * count);
|
||||
//swap each byte if nessesarry
|
||||
_swapDataBits(v, count, std::integral_constant<bool,
|
||||
Config::NetworkEndianness != details::getSystemEndianness()>{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _swapDataBits(T *v, size_t count, std::true_type) {
|
||||
std::for_each(v, std::next(v, count), [this](T &x) { x = details::swap(x); });
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _swapDataBits(T *, size_t , std::false_type) {
|
||||
//empty function because no swap is required
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename TReader>
|
||||
class AdapterReaderBitPackingWrapper {
|
||||
public:
|
||||
//this is required by deserializer
|
||||
static constexpr bool BitPackingEnabled = true;
|
||||
using TConfig = typename TReader::TConfig;
|
||||
//make TValue unsigned for bitpacking
|
||||
using UnsignedValue = typename std::make_unsigned<typename TReader::TValue>::type;
|
||||
using ScratchType = typename details::ScratchType<UnsignedValue>::type;
|
||||
static_assert(details::IsDefined<ScratchType>::value, "Underlying adapter value type is not supported");
|
||||
|
||||
explicit AdapterReaderBitPackingWrapper(TReader& reader):_reader{reader}
|
||||
{
|
||||
}
|
||||
|
||||
AdapterReaderBitPackingWrapper(const AdapterReaderBitPackingWrapper&) = delete;
|
||||
AdapterReaderBitPackingWrapper& operator = (const AdapterReaderBitPackingWrapper&) = delete;
|
||||
|
||||
AdapterReaderBitPackingWrapper(AdapterReaderBitPackingWrapper&& ) noexcept = default;
|
||||
AdapterReaderBitPackingWrapper& operator = (AdapterReaderBitPackingWrapper&& ) noexcept = default;
|
||||
|
||||
~AdapterReaderBitPackingWrapper() {
|
||||
align();
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBytes(T &v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
if (!m_scratchBits)
|
||||
_reader.template readBytes<SIZE,T>(v);
|
||||
else
|
||||
readBits(reinterpret_cast<UT &>(v), details::BitsSize<T>::value);
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBuffer(T *buf, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
|
||||
if (!m_scratchBits) {
|
||||
_reader.template readBuffer<SIZE,T>(buf, count);
|
||||
} else {
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
//todo improve implementation
|
||||
const auto end = buf + count;
|
||||
for (auto it = buf; it != end; ++it)
|
||||
readBits(reinterpret_cast<UT &>(*it), details::BitsSize<T>::value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void readBits(T &v, size_t bitsCount) {
|
||||
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
|
||||
readBitsInternal(v, bitsCount);
|
||||
}
|
||||
|
||||
void align() {
|
||||
if (m_scratchBits) {
|
||||
ScratchType tmp{};
|
||||
readBitsInternal(tmp, m_scratchBits);
|
||||
if (tmp)
|
||||
setError(ReaderError::InvalidData);
|
||||
}
|
||||
}
|
||||
|
||||
bool isCompletedSuccessfully() const {
|
||||
return _reader.isCompletedSuccessfully();
|
||||
}
|
||||
|
||||
ReaderError error() const {
|
||||
return _reader.error();
|
||||
}
|
||||
|
||||
void setError(ReaderError error) {
|
||||
_reader.setError(error);
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
align();
|
||||
_reader.beginSession();
|
||||
}
|
||||
|
||||
void endSession() {
|
||||
align();
|
||||
_reader.endSession();
|
||||
}
|
||||
|
||||
private:
|
||||
TReader& _reader;
|
||||
ScratchType m_scratch{};
|
||||
size_t m_scratchBits{};
|
||||
|
||||
template<typename T>
|
||||
void readBitsInternal(T &v, size_t size) {
|
||||
auto bitsLeft = size;
|
||||
T res{};
|
||||
while (bitsLeft > 0) {
|
||||
auto bits = std::min(bitsLeft, details::BitsSize<UnsignedValue>::value);
|
||||
if (m_scratchBits < bits) {
|
||||
UnsignedValue tmp;
|
||||
_reader.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);
|
||||
res |= shiftedRes;
|
||||
m_scratch >>= bits;
|
||||
m_scratchBits -= bits;
|
||||
bitsLeft -= bits;
|
||||
}
|
||||
v = res;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif //BITSERY_ADAPTER_READER_H
|
||||
@@ -1,343 +0,0 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
|
||||
#ifndef BITSERY_ADAPTER_WRITER_H
|
||||
#define BITSERY_ADAPTER_WRITER_H
|
||||
|
||||
#include "details/sessions.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template <typename Config>
|
||||
struct BasicMeasureSize {
|
||||
//measure class is bit-packing enabled, no need to create wrapper for it
|
||||
static constexpr bool BitPackingEnabled = true;
|
||||
|
||||
using TConfig = Config;
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBytes(const T &) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
_bitsCount += details::BitsSize<T>::value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeBits(const T &, size_t bitsCount) {
|
||||
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
|
||||
assert(bitsCount <= details::BitsSize<T>::value);
|
||||
_bitsCount += bitsCount;
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBuffer(const T *, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
_bitsCount += details::BitsSize<T>::value * count;
|
||||
}
|
||||
|
||||
void align() {
|
||||
auto _scratch = (_bitsCount % 8);
|
||||
_bitsCount += (8 - _scratch) % 8;
|
||||
}
|
||||
|
||||
void flush() {
|
||||
align();
|
||||
//flush sessions count
|
||||
if (_sessionsBytesCount > 0) {
|
||||
_bitsCount += (_sessionsBytesCount + 4) * 8;
|
||||
_sessionsBytesCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
|
||||
}
|
||||
|
||||
void endSession() {
|
||||
auto endPos = writtenBytesCount();
|
||||
details::writeSize(*this, endPos);
|
||||
auto sessionEndBytesCount = writtenBytesCount() - endPos;
|
||||
//remove written bytes, because we'll write them at the end
|
||||
_bitsCount -= sessionEndBytesCount * 8;
|
||||
_sessionsBytesCount += sessionEndBytesCount;
|
||||
}
|
||||
|
||||
//get size in bytes
|
||||
size_t writtenBytesCount() const {
|
||||
return _bitsCount / 8;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t _bitsCount{};
|
||||
size_t _sessionsBytesCount{};
|
||||
};
|
||||
|
||||
//helper type for default config
|
||||
using MeasureSize = BasicMeasureSize<DefaultConfig>;
|
||||
|
||||
|
||||
template <typename TWriter>
|
||||
class AdapterWriterBitPackingWrapper;
|
||||
|
||||
template<typename OutputAdapter, typename Config>
|
||||
struct AdapterWriter {
|
||||
//this is required by serializer
|
||||
static constexpr bool BitPackingEnabled = false;
|
||||
using TConfig = Config;
|
||||
using TValue = typename OutputAdapter::TValue;
|
||||
|
||||
static_assert(details::IsDefined<TValue>::value, "Please define adapter traits or include from <bitsery/traits/...>");
|
||||
|
||||
explicit AdapterWriter(OutputAdapter&& adapter)
|
||||
: _outputAdapter{std::move(adapter)}
|
||||
{
|
||||
}
|
||||
|
||||
AdapterWriter(const AdapterWriter &) = delete;
|
||||
|
||||
AdapterWriter &operator=(const AdapterWriter &) = delete;
|
||||
|
||||
//todo add conditional noexcept
|
||||
AdapterWriter(AdapterWriter &&) = default;
|
||||
|
||||
AdapterWriter &operator=(AdapterWriter &&) = default;
|
||||
|
||||
~AdapterWriter() {
|
||||
flush();
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBytes(const T &v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
directWrite(&v, 1);
|
||||
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBuffer(const T *buf, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
directWrite(buf, count);
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
//to have the same interface as bitpackingwriter
|
||||
void align() {
|
||||
|
||||
}
|
||||
|
||||
void flush() {
|
||||
_session.flushSessions(*this);
|
||||
_outputAdapter.flush();
|
||||
}
|
||||
|
||||
size_t writtenBytesCount() const {
|
||||
return _outputAdapter.writtenBytesCount();
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
_session.begin(*this);
|
||||
}
|
||||
|
||||
void endSession() {
|
||||
_session.end(*this);
|
||||
}
|
||||
|
||||
const OutputAdapter& adapter() const {
|
||||
return _outputAdapter;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class AdapterWriterBitPackingWrapper<AdapterWriter<OutputAdapter, Config>>;
|
||||
template<typename T>
|
||||
void directWrite(T &&v, size_t count) {
|
||||
_directWriteSwapTag(std::forward<T>(v), count, std::integral_constant<bool,
|
||||
Config::NetworkEndianness != details::getSystemEndianness()>{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _directWriteSwapTag(const T *v, size_t count, std::true_type) {
|
||||
std::for_each(v, std::next(v, count), [this](const T &v) {
|
||||
const auto res = details::swap(v);
|
||||
_outputAdapter.write(reinterpret_cast<const TValue *>(&res), sizeof(T));
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _directWriteSwapTag(const T *v, size_t count, std::false_type) {
|
||||
_outputAdapter.write(reinterpret_cast<const TValue *>(v), count * sizeof(T));
|
||||
}
|
||||
|
||||
OutputAdapter _outputAdapter;
|
||||
typename std::conditional<Config::BufferSessionsEnabled,
|
||||
session::SessionsWriter<AdapterWriter<OutputAdapter, Config >>,
|
||||
session::DisabledSessionsWriter<AdapterWriter<OutputAdapter, Config>>>::type
|
||||
_session{};
|
||||
};
|
||||
|
||||
//this class is used as wrapper for real AdapterWriter, it doesn't store writer itself just a reference
|
||||
template<typename TWriter>
|
||||
class AdapterWriterBitPackingWrapper {
|
||||
public:
|
||||
//this is required by serializer
|
||||
static constexpr bool BitPackingEnabled = true;
|
||||
using TConfig = typename TWriter::TConfig;
|
||||
|
||||
//make TValue unsigned for bit packing
|
||||
using UnsignedType = typename std::make_unsigned<typename TWriter::TValue>::type;
|
||||
using ScratchType = typename details::ScratchType<UnsignedType>::type;
|
||||
static_assert(details::IsDefined<ScratchType>::value, "Underlying adapter value type is not supported");
|
||||
|
||||
explicit AdapterWriterBitPackingWrapper(TWriter &writer)
|
||||
: _writer{writer}
|
||||
{
|
||||
}
|
||||
|
||||
AdapterWriterBitPackingWrapper(const AdapterWriterBitPackingWrapper&) = delete;
|
||||
AdapterWriterBitPackingWrapper& operator = (const AdapterWriterBitPackingWrapper&) = delete;
|
||||
|
||||
AdapterWriterBitPackingWrapper(AdapterWriterBitPackingWrapper&& ) noexcept = default;
|
||||
AdapterWriterBitPackingWrapper& operator = (AdapterWriterBitPackingWrapper&& ) noexcept = default;
|
||||
|
||||
~AdapterWriterBitPackingWrapper() {
|
||||
align();
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBytes(const T &v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
|
||||
if (!_scratchBits) {
|
||||
_writer.template writeBytes<SIZE,T>(v);
|
||||
} else {
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
writeBitsInternal(reinterpret_cast<const UT &>(v), details::BitsSize<T>::value);
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBuffer(const T *buf, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
if (!_scratchBits) {
|
||||
_writer.template writeBuffer<SIZE,T>(buf, count);
|
||||
} else {
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
//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);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
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)));
|
||||
writeBitsInternal(v, bitsCount);
|
||||
}
|
||||
|
||||
void align() {
|
||||
writeBitsInternal(UnsignedType{}, (details::BitsSize<UnsignedType>::value - _scratchBits) % 8);
|
||||
}
|
||||
|
||||
void flush() {
|
||||
align();
|
||||
_writer._session.flushSessions(_writer);
|
||||
}
|
||||
|
||||
size_t writtenBytesCount() const {
|
||||
return _writer.writtenBytesCount();
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
align();
|
||||
_writer._session.begin(_writer);
|
||||
}
|
||||
|
||||
void endSession() {
|
||||
align();
|
||||
_writer._session.end(_writer);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
template<typename T>
|
||||
void writeBitsInternal(const T &v, size_t size) {
|
||||
constexpr size_t valueSize = details::BitsSize<UnsignedType>::value;
|
||||
auto value = v;
|
||||
auto bitsLeft = size;
|
||||
while (bitsLeft > 0) {
|
||||
auto bits = std::min(bitsLeft, valueSize);
|
||||
_scratch |= static_cast<ScratchType>( value ) << _scratchBits;
|
||||
_scratchBits += bits;
|
||||
if (_scratchBits >= valueSize) {
|
||||
auto tmp = static_cast<UnsignedType>(_scratch & _MASK);
|
||||
_writer.template writeBytes<sizeof(UnsignedType), UnsignedType >(tmp);
|
||||
_scratch >>= valueSize;
|
||||
_scratchBits -= valueSize;
|
||||
|
||||
value >>= valueSize;
|
||||
}
|
||||
bitsLeft -= bits;
|
||||
}
|
||||
}
|
||||
|
||||
//overload for TValue, for better performance
|
||||
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);
|
||||
_writer.template writeBytes<sizeof(UnsignedType), UnsignedType>(tmp);
|
||||
_scratch >>= details::BitsSize<UnsignedType>::value;
|
||||
_scratchBits -= details::BitsSize<UnsignedType>::value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const UnsignedType _MASK = std::numeric_limits<UnsignedType>::max();
|
||||
ScratchType _scratch{};
|
||||
size_t _scratchBits{};
|
||||
TWriter& _writer;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif //BITSERY_ADAPTER_WRITER_H
|
||||
@@ -1,43 +1,103 @@
|
||||
//MIT License
|
||||
// MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_BITSERY_H
|
||||
#define BITSERY_BITSERY_H
|
||||
|
||||
#define BITSERY_MAJOR_VERSION 4
|
||||
#define BITSERY_MAJOR_VERSION 5
|
||||
#define BITSERY_MINOR_VERSION 2
|
||||
#define BITSERY_PATCH_VERSION 1
|
||||
#define BITSERY_PATCH_VERSION 5
|
||||
|
||||
#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)
|
||||
#define BITSERY_BUILD_VERSION_STR(major, minor, 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)
|
||||
#define BITSERY_VERSION \
|
||||
BITSERY_BUILD_VERSION_STR( \
|
||||
BITSERY_MAJOR_VERSION, BITSERY_MINOR_VERSION, BITSERY_PATCH_VERSION)
|
||||
|
||||
#define BITSERY_DO_PRAGMA(x) _Pragma(#x)
|
||||
#ifdef __GNUC__
|
||||
#define BITSERY_DISABLE_WARNINGS(...) \
|
||||
BITSERY_DO_PRAGMA(GCC diagnostic push) \
|
||||
BITSERY_DO_PRAGMA(GCC diagnostic ignored __VA_ARGS__)
|
||||
#define BITSERY_ENABLE_WARNINGS() BITSERY_DO_PRAGMA(GCC diagnostic pop)
|
||||
#elif defined(_MSC_VER)
|
||||
#define BITSERY_DISABLE_WARNINGS(...) \
|
||||
BITSERY_DO_PRAGMA(GCC diagnostic push) \
|
||||
BITSERY_DO_PRAGMA(GCC diagnostic ignored __VA_ARGS__) \
|
||||
BITSERY_DO_PRAGMA(GCC diagnostic pop)
|
||||
#define BITSERY_ENABLE_WARNINGS() BITSERY_DO_PRAGMA(GCC diagnostic pop)
|
||||
#else
|
||||
#define BITSERY_DISABLE_WARNINGS(...)
|
||||
#define BITSERY_ENABLE_WARNINGS()
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
#define BITSERY_ATTRIBUTE(...) \
|
||||
BITSERY_DISABLE_WARNINGS("-Wfuture-attribute-extensions") \
|
||||
[[__VA_ARGS__]] BITSERY_ENABLE_WARNINGS()
|
||||
#elif defined(__GNUC__)
|
||||
#define BITSERY_ATTRIBUTE(...) [[__VA_ARGS__]]
|
||||
#elif defined(_MSC_VER)
|
||||
#define BITSERY_ATTRIBUTE(...) [[__VA_ARGS__]]
|
||||
#else
|
||||
#define BITSERY_ATTRIBUTE(...) [[__VA_ARGS__]]
|
||||
#endif
|
||||
|
||||
#if __has_cpp_attribute(likely)
|
||||
#define BITSERY_LIKELY BITSERY_ATTRIBUTE(likely)
|
||||
#else
|
||||
#define BITSERY_LIKELY
|
||||
#endif
|
||||
|
||||
#if __has_cpp_attribute(unlikely)
|
||||
#define BITSERY_UNLIKELY BITSERY_ATTRIBUTE(unlikely)
|
||||
#else
|
||||
#define BITSERY_UNLIKELY
|
||||
#endif
|
||||
|
||||
#if __GNUC__
|
||||
#define BITSERY_NOINLINE __attribute__((noinline))
|
||||
#elif defined(_MSC_VER)
|
||||
#define BITSERY_NOINLINE __declspec(noinline)
|
||||
#else
|
||||
#define BITSERY_NOINLINE
|
||||
#endif
|
||||
|
||||
#if __GNUC__
|
||||
#define BITSERY_ASSUME(cond) \
|
||||
do { \
|
||||
if (!(cond)) \
|
||||
__builtin_unreachable(); \
|
||||
} while (0)
|
||||
#elif defined(_MSC_VER)
|
||||
#define BITSERY_ASSUME(cond) __assume(cond)
|
||||
#else
|
||||
#define BITSERY_ASSUME(cond)
|
||||
#endif
|
||||
|
||||
#include "serializer.h"
|
||||
#include "deserializer.h"
|
||||
#include "serializer.h"
|
||||
|
||||
#endif //BITSERY_BITSERY_H
|
||||
#endif // BITSERY_BITSERY_H
|
||||
|
||||
126
include/bitsery/brief_syntax.h
Normal file
126
include/bitsery/brief_syntax.h
Normal file
@@ -0,0 +1,126 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_BRIEF_SYNTAX_H
|
||||
#define BITSERY_BRIEF_SYNTAX_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,
|
||||
"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])
|
||||
{
|
||||
return { str };
|
||||
}
|
||||
|
||||
template<typename T, size_t 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)
|
||||
{
|
||||
return { obj, max };
|
||||
}
|
||||
|
||||
// define serialize function for fundamental types
|
||||
template<typename S>
|
||||
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)
|
||||
{
|
||||
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])
|
||||
{
|
||||
static_assert(N == 0,
|
||||
"\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])
|
||||
{
|
||||
brief_syntax::processContainer(s, obj);
|
||||
}
|
||||
|
||||
// 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()
|
||||
{
|
||||
// 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.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // BITSERY_BRIEF_SYNTAX_H
|
||||
38
include/bitsery/brief_syntax/array.h
Normal file
38
include/bitsery/brief_syntax/array.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_ARRAY_H
|
||||
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_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)
|
||||
{
|
||||
brief_syntax::processContainer(s, obj);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BITSERY_BRIEF_SYNTAX_TYPE_STD_ARRAY_H
|
||||
37
include/bitsery/brief_syntax/atomic.h
Normal file
37
include/bitsery/brief_syntax/atomic.h
Normal file
@@ -0,0 +1,37 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 Nick Renieris
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_ATOMIC_H
|
||||
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_ATOMIC_H
|
||||
|
||||
#include "../ext/std_atomic.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename T>
|
||||
void
|
||||
serialize(S& s, std::atomic<T>& obj)
|
||||
{
|
||||
s.template ext<sizeof(T)>(obj, ext::StdAtomic{});
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BITSERY_BRIEF_SYNTAX_TYPE_STD_ATOMIC_H
|
||||
36
include/bitsery/brief_syntax/bitset.h
Normal file
36
include/bitsery/brief_syntax/bitset.h
Normal file
@@ -0,0 +1,36 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2018 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_BITSET_H
|
||||
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_BITSET_H
|
||||
|
||||
#include "../ext/std_bitset.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, size_t N>
|
||||
void
|
||||
serialize(S& s, std::bitset<N>& obj)
|
||||
{
|
||||
s.ext(obj, ext::StdBitset{});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
44
include/bitsery/brief_syntax/chrono.h
Normal file
44
include/bitsery/brief_syntax/chrono.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2019 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_CHRONO_H
|
||||
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_CHRONO_H
|
||||
|
||||
#include "../ext/std_chrono.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename T, typename P>
|
||||
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)
|
||||
{
|
||||
s.template ext<sizeof(T)>(obj, ext::StdTimePoint{});
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BITSERY_BRIEF_SYNTAX_TYPE_STD_CHRONO_H
|
||||
38
include/bitsery/brief_syntax/deque.h
Normal file
38
include/bitsery/brief_syntax/deque.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_DEQUE_H
|
||||
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_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)
|
||||
{
|
||||
brief_syntax::processContainer(s, obj);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BITSERY_BRIEF_SYNTAX_TYPE_STD_DEQUE_H
|
||||
38
include/bitsery/brief_syntax/forward_list.h
Normal file
38
include/bitsery/brief_syntax/forward_list.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_FORWARD_LIST_H
|
||||
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_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)
|
||||
{
|
||||
brief_syntax::processContainer(s, obj);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BITSERY_BRIEF_SYNTAX_TYPE_STD_FORWARD_LIST_H
|
||||
38
include/bitsery/brief_syntax/list.h
Normal file
38
include/bitsery/brief_syntax/list.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_LIST_H
|
||||
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_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)
|
||||
{
|
||||
brief_syntax::processContainer(s, obj);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BITSERY_BRIEF_SYNTAX_TYPE_STD_LIST_H
|
||||
64
include/bitsery/brief_syntax/map.h
Normal file
64
include/bitsery/brief_syntax/map.h
Normal file
@@ -0,0 +1,64 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_MAP_H
|
||||
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_MAP_H
|
||||
|
||||
#include "../ext/std_map.h"
|
||||
#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) {
|
||||
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) {
|
||||
s.object(key);
|
||||
s.object(value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BITSERY_BRIEF_SYNTAX_TYPE_STD_MAP_H
|
||||
51
include/bitsery/brief_syntax/memory.h
Normal file
51
include/bitsery/brief_syntax/memory.h
Normal file
@@ -0,0 +1,51 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2018 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_MEMORY_H
|
||||
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_MEMORY_H
|
||||
|
||||
#include "../ext/std_smart_ptr.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename T, typename D>
|
||||
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)
|
||||
{
|
||||
s.ext(obj, ext::StdSmartPtr{});
|
||||
}
|
||||
|
||||
template<typename S, typename T>
|
||||
void
|
||||
serialize(S& s, std::weak_ptr<T>& obj)
|
||||
{
|
||||
s.ext(obj, ext::StdSmartPtr{});
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BITSERY_BRIEF_SYNTAX_TYPE_STD_MEMORY_H
|
||||
36
include/bitsery/brief_syntax/optional.h
Normal file
36
include/bitsery/brief_syntax/optional.h
Normal file
@@ -0,0 +1,36 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2018 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_OPTIONAL_H
|
||||
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_OPTIONAL_H
|
||||
|
||||
#include "../ext/std_optional.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename Ts>
|
||||
void
|
||||
serialize(S& s, std::optional<Ts>& obj)
|
||||
{
|
||||
s.ext(obj, ext::StdOptional{});
|
||||
}
|
||||
} // namespace bitsery
|
||||
#endif
|
||||
50
include/bitsery/brief_syntax/queue.h
Normal file
50
include/bitsery/brief_syntax/queue.h
Normal file
@@ -0,0 +1,50 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_QUEUE_H
|
||||
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_QUEUE_H
|
||||
|
||||
#include "../ext/std_queue.h"
|
||||
#include <limits>
|
||||
|
||||
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())
|
||||
{
|
||||
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())
|
||||
{
|
||||
s.ext(obj, ext::StdQueue{ maxSize });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // BITSERY_BRIEF_SYNTAX_TYPE_STD_QUEUE_H
|
||||
51
include/bitsery/brief_syntax/set.h
Normal file
51
include/bitsery/brief_syntax/set.h
Normal file
@@ -0,0 +1,51 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_SET_H
|
||||
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_SET_H
|
||||
|
||||
#include "../ext/std_set.h"
|
||||
#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())
|
||||
{
|
||||
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())
|
||||
{
|
||||
s.ext(obj, ext::StdSet{ maxSize });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // BITSERY_BRIEF_SYNTAX_TYPE_STD_SET_H
|
||||
40
include/bitsery/brief_syntax/stack.h
Normal file
40
include/bitsery/brief_syntax/stack.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_STACK_H
|
||||
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_STACK_H
|
||||
|
||||
#include "../ext/std_stack.h"
|
||||
#include <limits>
|
||||
|
||||
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())
|
||||
{
|
||||
s.ext(obj, ext::StdStack{ maxSize });
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BITSERY_BRIEF_SYNTAX_TYPE_STD_STACK_H
|
||||
38
include/bitsery/brief_syntax/string.h
Normal file
38
include/bitsery/brief_syntax/string.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_STRING_H
|
||||
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_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)
|
||||
{
|
||||
brief_syntax::processContainer(s, str);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BITSERY_BRIEF_SYNTAX_TYPE_STD_STRING_H
|
||||
37
include/bitsery/brief_syntax/tuple.h
Normal file
37
include/bitsery/brief_syntax/tuple.h
Normal file
@@ -0,0 +1,37 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2019 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_TUPLE_H
|
||||
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_TUPLE_H
|
||||
|
||||
#include "../ext/std_tuple.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename... Ts>
|
||||
void
|
||||
serialize(S& s, std::tuple<Ts...>& obj)
|
||||
{
|
||||
s.ext(obj, ext::StdTuple{});
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BITSERY_BRIEF_SYNTAX_TYPE_STD_TUPLE_H
|
||||
67
include/bitsery/brief_syntax/unordered_map.h
Normal file
67
include/bitsery/brief_syntax/unordered_map.h
Normal file
@@ -0,0 +1,67 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_UNORDERED_MAP_H
|
||||
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_UNORDERED_MAP_H
|
||||
|
||||
#include "../ext/std_map.h"
|
||||
#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) {
|
||||
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) {
|
||||
s.object(key);
|
||||
s.object(value);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // BITSERY_BRIEF_SYNTAX_TYPE_STD_UNORDERED_MAP_H
|
||||
59
include/bitsery/brief_syntax/unordered_set.h
Normal file
59
include/bitsery/brief_syntax/unordered_set.h
Normal file
@@ -0,0 +1,59 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_UNORDERED_SET_H
|
||||
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_UNORDERED_SET_H
|
||||
|
||||
#include "../ext/std_set.h"
|
||||
#include <limits>
|
||||
#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())
|
||||
{
|
||||
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())
|
||||
{
|
||||
s.ext(obj, ext::StdSet{ maxSize });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // BITSERY_BRIEF_SYNTAX_TYPE_STD_UNORDERED_SET_H
|
||||
37
include/bitsery/brief_syntax/variant.h
Normal file
37
include/bitsery/brief_syntax/variant.h
Normal file
@@ -0,0 +1,37 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2019 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_VARIANT_H
|
||||
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_VARIANT_H
|
||||
|
||||
#include "../ext/std_variant.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename... Ts>
|
||||
void
|
||||
serialize(S& s, std::variant<Ts...>& obj)
|
||||
{
|
||||
s.ext(obj, ext::StdVariant{});
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BITSERY_BRIEF_SYNTAX_TYPE_STD_VARIANT_H
|
||||
38
include/bitsery/brief_syntax/vector.h
Normal file
38
include/bitsery/brief_syntax/vector.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_VECTOR_H
|
||||
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_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)
|
||||
{
|
||||
brief_syntax::processContainer(s, obj);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BITSERY_BRIEF_SYNTAX_TYPE_STD_VECTOR_H
|
||||
@@ -1,54 +1,54 @@
|
||||
//MIT License
|
||||
// MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_COMMON_H
|
||||
#define BITSERY_COMMON_H
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
/*
|
||||
* endianess
|
||||
* endianness
|
||||
*/
|
||||
enum class EndiannessType {
|
||||
LittleEndian,
|
||||
BigEndian
|
||||
};
|
||||
enum class EndiannessType
|
||||
{
|
||||
LittleEndian,
|
||||
BigEndian
|
||||
};
|
||||
|
||||
//default configuration for buffer writing/reading operations
|
||||
struct DefaultConfig {
|
||||
//data will be stored in little endian, independant of host.
|
||||
static constexpr EndiannessType NetworkEndianness = EndiannessType::LittleEndian;
|
||||
//this functionality allows to support backward/forward compatibility
|
||||
//however reading from streams is not supported, because this functionality requires random access to buffer.
|
||||
static constexpr bool BufferSessionsEnabled = false;
|
||||
//list of contexts that will be instanciated internally within serializer/deserializer.
|
||||
//contexts must be default constructable.
|
||||
//internal context has priority, if external context with the same type exists.
|
||||
using InternalContext = std::tuple<>;
|
||||
};
|
||||
// default configuration for serialization and deserialization
|
||||
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
|
||||
static constexpr bool CheckAdapterErrors = true;
|
||||
// enables/disables checks for other errors that can significantly affect
|
||||
// performance
|
||||
static constexpr bool CheckDataErrors = true;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_COMMON_H
|
||||
#endif // BITSERY_COMMON_H
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
379
include/bitsery/details/adapter_bit_packing.h
Normal file
379
include/bitsery/details/adapter_bit_packing.h
Normal file
@@ -0,0 +1,379 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_DETAILS_ADAPTER_BIT_PACKING_H
|
||||
#define BITSERY_DETAILS_ADAPTER_BIT_PACKING_H
|
||||
|
||||
#include "../common.h"
|
||||
#include "./adapter_common.h"
|
||||
#include "not_defined_type.h"
|
||||
#include <limits>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace details {
|
||||
|
||||
template<typename TAdapter>
|
||||
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.
|
||||
using BitPackingEnabled = InputAdapterBitPackingWrapper<TAdapter>;
|
||||
using TConfig = typename TAdapter::TConfig;
|
||||
using TValue = typename TAdapter::TValue;
|
||||
|
||||
InputAdapterBitPackingWrapper(TAdapter& adapter)
|
||||
: _wrapped{ adapter }
|
||||
{
|
||||
}
|
||||
|
||||
~InputAdapterBitPackingWrapper() { align(); }
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBytes(T& v)
|
||||
{
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
if (!m_scratchBits)
|
||||
this->_wrapped.template readBytes<SIZE, T>(v);
|
||||
else
|
||||
readBits(reinterpret_cast<UT&>(v), details::BitsSize<T>::value);
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBuffer(T* buf, size_t count)
|
||||
{
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
|
||||
if (!m_scratchBits) {
|
||||
this->_wrapped.template readBuffer<SIZE, T>(buf, count);
|
||||
} else {
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
// todo improve implementation
|
||||
const auto end = buf + count;
|
||||
for (auto it = buf; it != end; ++it)
|
||||
readBits(reinterpret_cast<UT&>(*it), details::BitsSize<T>::value);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void readBits(T& v, size_t bitsCount)
|
||||
{
|
||||
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
|
||||
readBitsInternal(v, bitsCount);
|
||||
}
|
||||
|
||||
void align()
|
||||
{
|
||||
if (m_scratchBits) {
|
||||
ScratchType tmp{};
|
||||
readBitsInternal(tmp, m_scratchBits);
|
||||
handleAlignErrors(
|
||||
tmp, std::integral_constant<bool, TConfig::CheckDataErrors>{});
|
||||
}
|
||||
}
|
||||
|
||||
void currentReadPos(size_t pos)
|
||||
{
|
||||
align();
|
||||
this->_wrapped.currentReadPos(pos);
|
||||
}
|
||||
|
||||
size_t currentReadPos() const { return this->_wrapped.currentReadPos(); }
|
||||
|
||||
void currentReadEndPos(size_t pos) { this->_wrapped.currentReadEndPos(pos); }
|
||||
|
||||
size_t currentReadEndPos() const
|
||||
{
|
||||
return this->_wrapped.currentReadEndPos();
|
||||
}
|
||||
|
||||
bool isCompletedSuccessfully() const
|
||||
{
|
||||
return this->_wrapped.isCompletedSuccessfully();
|
||||
}
|
||||
|
||||
ReaderError error() const { return this->_wrapped.error(); }
|
||||
|
||||
void error(ReaderError error) { this->_wrapped.error(error); }
|
||||
|
||||
private:
|
||||
TAdapter& _wrapped;
|
||||
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)
|
||||
{
|
||||
auto bitsLeft = size;
|
||||
using TFast = typename FastType<T>::type;
|
||||
TFast res{};
|
||||
while (bitsLeft > 0) {
|
||||
auto bits = (std::min)(bitsLeft, details::BitsSize<UnsignedValue>::value);
|
||||
if (m_scratchBits < bits) {
|
||||
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);
|
||||
res = static_cast<TFast>(res | static_cast<TFast>(shiftedRes));
|
||||
m_scratch >>= bits;
|
||||
m_scratchBits -= bits;
|
||||
bitsLeft -= bits;
|
||||
}
|
||||
v = static_cast<T>(res);
|
||||
}
|
||||
|
||||
void handleAlignErrors(ScratchType value, std::true_type)
|
||||
{
|
||||
if (value)
|
||||
error(ReaderError::InvalidData);
|
||||
}
|
||||
|
||||
void handleAlignErrors(ScratchType, std::false_type) {}
|
||||
};
|
||||
|
||||
template<typename TAdapter>
|
||||
class BasicMeasureSizeBitPackingWrapper
|
||||
{
|
||||
public:
|
||||
using BitPackingEnabled = BasicMeasureSizeBitPackingWrapper<TAdapter>;
|
||||
using TConfig = typename TAdapter::TConfig;
|
||||
using TValue = typename TAdapter::TValue;
|
||||
|
||||
BasicMeasureSizeBitPackingWrapper(TAdapter& adapter)
|
||||
: _wrapped{ adapter }
|
||||
{
|
||||
}
|
||||
|
||||
~BasicMeasureSizeBitPackingWrapper() { align(); }
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBytes(const T& value)
|
||||
{
|
||||
_wrapped.template writeBytes<SIZE>(value);
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBuffer(const T* buf, size_t count)
|
||||
{
|
||||
_wrapped.template writeBuffer<SIZE>(buf, count);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeBits(const T&, size_t bitsCount)
|
||||
{
|
||||
_scratchBits += bitsCount;
|
||||
while (_scratchBits >= 8) {
|
||||
writeOneByte();
|
||||
_scratchBits -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
void align()
|
||||
{
|
||||
if (_scratchBits > 0) {
|
||||
_scratchBits = 0;
|
||||
writeOneByte();
|
||||
}
|
||||
}
|
||||
|
||||
void currentWritePos(size_t pos)
|
||||
{
|
||||
align();
|
||||
this->_wrapped.currentWritePos(pos);
|
||||
}
|
||||
|
||||
size_t currentWritePos() const { return this->_wrapped.currentWritePos(); }
|
||||
|
||||
void flush()
|
||||
{
|
||||
align();
|
||||
this->_wrapped.flush();
|
||||
}
|
||||
|
||||
size_t writtenBytesCount() const
|
||||
{
|
||||
return this->_wrapped.writtenBytesCount();
|
||||
}
|
||||
|
||||
private:
|
||||
void writeOneByte() { _wrapped.template writeBytes<1>(uint8_t{}); }
|
||||
TAdapter& _wrapped;
|
||||
size_t _scratchBits{};
|
||||
};
|
||||
|
||||
template<typename TAdapter>
|
||||
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.
|
||||
using BitPackingEnabled = OutputAdapterBitPackingWrapper<TAdapter>;
|
||||
using TConfig = typename TAdapter::TConfig;
|
||||
using TValue = typename TAdapter::TValue;
|
||||
|
||||
OutputAdapterBitPackingWrapper(TAdapter& adapter)
|
||||
: _wrapped{ adapter }
|
||||
{
|
||||
}
|
||||
|
||||
~OutputAdapterBitPackingWrapper() { align(); }
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBytes(const T& v)
|
||||
{
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
|
||||
if (!_scratchBits) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBuffer(const T* buf, size_t count)
|
||||
{
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
if (!_scratchBits) {
|
||||
this->_wrapped.template writeBuffer<SIZE, T>(buf, count);
|
||||
} else {
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
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)));
|
||||
writeBitsInternal(v, bitsCount);
|
||||
}
|
||||
|
||||
void align()
|
||||
{
|
||||
writeBitsInternal(UnsignedType{},
|
||||
(details::BitsSize<UnsignedType>::value - _scratchBits) %
|
||||
8);
|
||||
}
|
||||
|
||||
void currentWritePos(size_t pos)
|
||||
{
|
||||
align();
|
||||
this->_wrapped.currentWritePos(pos);
|
||||
}
|
||||
|
||||
size_t currentWritePos() const { return this->_wrapped.currentWritePos(); }
|
||||
|
||||
void flush()
|
||||
{
|
||||
align();
|
||||
this->_wrapped.flush();
|
||||
}
|
||||
|
||||
size_t writtenBytesCount() const
|
||||
{
|
||||
return this->_wrapped.writtenBytesCount();
|
||||
}
|
||||
|
||||
private:
|
||||
TAdapter& _wrapped;
|
||||
|
||||
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");
|
||||
|
||||
template<typename T>
|
||||
void writeBitsInternal(const T& v, size_t size)
|
||||
{
|
||||
constexpr size_t valueSize = details::BitsSize<UnsignedType>::value;
|
||||
T value = v;
|
||||
size_t bitsLeft = size;
|
||||
while (bitsLeft > 0) {
|
||||
auto bits = (std::min)(bitsLeft, valueSize);
|
||||
_scratch |= static_cast<ScratchType>(value) << _scratchBits;
|
||||
_scratchBits += bits;
|
||||
if (_scratchBits >= valueSize) {
|
||||
auto tmp = static_cast<UnsignedType>(_scratch & _MASK);
|
||||
this->_wrapped.template writeBytes<sizeof(UnsignedType), UnsignedType>(
|
||||
tmp);
|
||||
_scratch >>= valueSize;
|
||||
_scratchBits -= valueSize;
|
||||
|
||||
value = static_cast<T>(value >> valueSize);
|
||||
}
|
||||
bitsLeft -= bits;
|
||||
}
|
||||
}
|
||||
|
||||
// overload for TValue, for better performance
|
||||
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);
|
||||
_scratch >>= details::BitsSize<UnsignedType>::value;
|
||||
_scratchBits -= details::BitsSize<UnsignedType>::value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const UnsignedType _MASK = (std::numeric_limits<UnsignedType>::max)();
|
||||
ScratchType _scratch{};
|
||||
size_t _scratchBits{};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BITSERY_DETAILS_ADAPTER_BIT_PACKING_H
|
||||
@@ -1,127 +1,426 @@
|
||||
//MIT License
|
||||
// MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_DETAILS_ADAPTER_COMMON_H
|
||||
#define BITSERY_DETAILS_ADAPTER_COMMON_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include <cstring>
|
||||
#include "adapter_utils.h"
|
||||
#include "not_defined_type.h"
|
||||
|
||||
#include "../common.h"
|
||||
#include "not_defined_type.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace details {
|
||||
enum class ReaderError
|
||||
{
|
||||
NoError,
|
||||
ReadingError, // this might be used with stream adapter
|
||||
DataOverflow,
|
||||
InvalidData,
|
||||
InvalidPointer
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct BitsSize:public std::integral_constant<size_t, sizeof(T) * 8> {
|
||||
namespace details {
|
||||
|
||||
};
|
||||
/**
|
||||
* size read/write functions
|
||||
*/
|
||||
|
||||
//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;
|
||||
return value;
|
||||
#endif
|
||||
}
|
||||
|
||||
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;
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint16_t exec(uint16_t value) {
|
||||
return (value & 0x00ff) << 8 | (value & 0xff00) >> 8;
|
||||
}
|
||||
|
||||
static uint8_t exec(uint8_t value) {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TValue>
|
||||
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;
|
||||
return SwapImpl::exec(static_cast<UT>(value));
|
||||
}
|
||||
|
||||
//add test data in separate struct, because some compilers only support constexpr functions with return-only body
|
||||
struct EndiannessTestData {
|
||||
static constexpr uint32_t _sample4Bytes = 0x01020304;
|
||||
static constexpr uint8_t _sample1stByte = (const uint8_t &) _sample4Bytes;
|
||||
};
|
||||
|
||||
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
|
||||
: EndiannessType::BigEndian;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct ScratchType {
|
||||
using type = NotDefinedType;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ScratchType<uint8_t> {
|
||||
using type = uint16_t;
|
||||
};
|
||||
|
||||
/*
|
||||
* class used by session reader, to access underlying iterators of buffer
|
||||
*/
|
||||
struct SessionAccess {
|
||||
template <typename TReader, typename Iterator>
|
||||
static Iterator& posIteratorRef(TReader& r) {
|
||||
return r.posIt;
|
||||
}
|
||||
template <typename TReader, typename Iterator>
|
||||
static Iterator& endIteratorRef(TReader& r) {
|
||||
return r.endIt;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
template<typename Reader>
|
||||
void
|
||||
handleReadMaxSize(Reader& r, size_t& size, size_t maxSize, std::true_type)
|
||||
{
|
||||
if (size > maxSize) {
|
||||
r.error(ReaderError::InvalidData);
|
||||
size = {};
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Reader>
|
||||
void
|
||||
handleReadMaxSize(Reader&, size_t&, size_t, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
#endif //BITSERY_DETAILS_ADAPTER_COMMON_H
|
||||
template<typename Reader, bool 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) {
|
||||
size = hb;
|
||||
} else {
|
||||
uint8_t lb{};
|
||||
r.template readBytes<1>(lb);
|
||||
if (hb & 0x40u) {
|
||||
uint16_t lw{};
|
||||
r.template readBytes<2>(lw);
|
||||
size = ((((hb & 0x3Fu) << 8) | lb) << 16) | lw;
|
||||
} else {
|
||||
size = ((hb & 0x7Fu) << 8) | lb;
|
||||
}
|
||||
}
|
||||
handleReadMaxSize(r, size, maxSize, checkMaxSize);
|
||||
}
|
||||
|
||||
template<typename Writer>
|
||||
void
|
||||
writeSize(Writer& w, const size_t size)
|
||||
{
|
||||
if (size < 0x80u) {
|
||||
w.template writeBytes<1>(static_cast<uint8_t>(size));
|
||||
} else {
|
||||
if (size < 0x4000u) {
|
||||
w.template writeBytes<1>(static_cast<uint8_t>((size >> 8) | 0x80u));
|
||||
w.template writeBytes<1>(static_cast<uint8_t>(size));
|
||||
} else {
|
||||
assert(size < 0x40000000u);
|
||||
w.template writeBytes<1>(static_cast<uint8_t>((size >> 24) | 0xC0u));
|
||||
w.template writeBytes<1>(static_cast<uint8_t>(size >> 16));
|
||||
w.template writeBytes<2>(static_cast<uint16_t>(size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* swap utils
|
||||
*/
|
||||
|
||||
// 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;
|
||||
return value;
|
||||
#endif
|
||||
}
|
||||
|
||||
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;
|
||||
#endif
|
||||
}
|
||||
|
||||
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) { return value; }
|
||||
};
|
||||
|
||||
template<typename TValue>
|
||||
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;
|
||||
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.
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4310)
|
||||
#endif
|
||||
struct EndiannessTestData
|
||||
{
|
||||
static constexpr uint32_t _sample4Bytes = 0x01020304;
|
||||
static constexpr uint8_t _sample1stByte = (const uint8_t&)_sample4Bytes;
|
||||
};
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(default : 4310)
|
||||
#endif
|
||||
|
||||
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
|
||||
: EndiannessType::BigEndian;
|
||||
}
|
||||
|
||||
template<typename Config, typename T>
|
||||
using ShouldSwap =
|
||||
std::integral_constant<bool,
|
||||
Config::Endianness != details::getSystemEndianness() &&
|
||||
sizeof(T) != 1>;
|
||||
|
||||
/**
|
||||
* helper types to work with bits
|
||||
*/
|
||||
template<typename T>
|
||||
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
|
||||
{
|
||||
using type = NotDefinedType;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ScratchType<uint8_t>
|
||||
{
|
||||
using type = uint_fast16_t;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct FastType
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct FastType<uint8_t>
|
||||
{
|
||||
using type = uint_fast8_t;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct FastType<uint16_t>
|
||||
{
|
||||
using type = uint_fast16_t;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct FastType<uint32_t>
|
||||
{
|
||||
using type = uint_fast32_t;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct FastType<uint64_t>
|
||||
{
|
||||
using type = uint_fast64_t;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct FastType<int8_t>
|
||||
{
|
||||
using type = int_fast8_t;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct FastType<int16_t>
|
||||
{
|
||||
using type = int_fast16_t;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct FastType<int32_t>
|
||||
{
|
||||
using type = int_fast32_t;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct FastType<int64_t>
|
||||
{
|
||||
using type = int_fast64_t;
|
||||
};
|
||||
|
||||
/**
|
||||
* output/input adapter base that handles endianness
|
||||
*/
|
||||
|
||||
template<typename Adapter>
|
||||
struct OutputAdapterBaseCRTP
|
||||
{
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBytes(const T& v)
|
||||
{
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
writeSwappedValue(&v, ShouldSwap<typename Adapter::TConfig, T>{});
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
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, T>{});
|
||||
}
|
||||
|
||||
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 align() {}
|
||||
|
||||
OutputAdapterBaseCRTP() = default;
|
||||
OutputAdapterBaseCRTP(const OutputAdapterBaseCRTP&) = delete;
|
||||
OutputAdapterBaseCRTP& operator=(const OutputAdapterBaseCRTP&) = delete;
|
||||
OutputAdapterBaseCRTP(OutputAdapterBaseCRTP&&) = default;
|
||||
OutputAdapterBaseCRTP& operator=(OutputAdapterBaseCRTP&&) = default;
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
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));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
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));
|
||||
});
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Adapter>
|
||||
struct InputAdapterBaseCRTP
|
||||
{
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
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));
|
||||
swapDataBits(v, ShouldSwap<typename Adapter::TConfig, T>{});
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
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);
|
||||
swapDataBits(buf, count, ShouldSwap<typename Adapter::TConfig, T>{});
|
||||
}
|
||||
|
||||
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 align() {}
|
||||
|
||||
InputAdapterBaseCRTP() = default;
|
||||
InputAdapterBaseCRTP(const InputAdapterBaseCRTP&) = delete;
|
||||
InputAdapterBaseCRTP& operator=(const InputAdapterBaseCRTP&) = delete;
|
||||
|
||||
InputAdapterBaseCRTP(InputAdapterBaseCRTP&&) = default;
|
||||
InputAdapterBaseCRTP& operator=(InputAdapterBaseCRTP&&) = default;
|
||||
|
||||
virtual ~InputAdapterBaseCRTP() = default;
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
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)
|
||||
{
|
||||
v = details::swap(v);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void swapDataBits(T&, std::false_type)
|
||||
{
|
||||
// empty function because no swap is required
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BITSERY_DETAILS_ADAPTER_COMMON_H
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_DETAILS_ADAPTER_UTILS_H
|
||||
#define BITSERY_DETAILS_ADAPTER_UTILS_H
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
enum class ReaderError {
|
||||
NoError,
|
||||
ReadingError, // this might be used with stream adapter
|
||||
DataOverflow,
|
||||
InvalidData,
|
||||
InvalidPointer
|
||||
};
|
||||
|
||||
namespace details {
|
||||
/*
|
||||
* size read/write functions
|
||||
*/
|
||||
template <typename Reader>
|
||||
void readSize(Reader& r, size_t& size, size_t maxSize) {
|
||||
uint8_t hb{};
|
||||
r.template readBytes<1>(hb);
|
||||
if (hb < 0x80u) {
|
||||
size = hb;
|
||||
} else {
|
||||
uint8_t lb{};
|
||||
r.template readBytes<1>(lb);
|
||||
if (hb & 0x40u) {
|
||||
uint16_t lw{};
|
||||
r.template readBytes<2>(lw);
|
||||
size = ((((hb & 0x3Fu) << 8) | lb) << 16) | lw;
|
||||
} else {
|
||||
size = ((hb & 0x7Fu) << 8) | lb;
|
||||
}
|
||||
}
|
||||
if (size > maxSize) {
|
||||
r.setError(ReaderError::InvalidData);
|
||||
size = {};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Writter>
|
||||
void writeSize(Writter& w, const size_t size) {
|
||||
if (size < 0x80u) {
|
||||
w.template writeBytes<1>(static_cast<uint8_t>(size));
|
||||
} else {
|
||||
if (size < 0x4000u) {
|
||||
w.template writeBytes<1>(static_cast<uint8_t>((size >> 8) | 0x80u));
|
||||
w.template writeBytes<1>(static_cast<uint8_t>(size));
|
||||
} else {
|
||||
assert(size < 0x40000000u);
|
||||
w.template writeBytes<1>(static_cast<uint8_t>((size >> 24) | 0xC0u));
|
||||
w.template writeBytes<1>(static_cast<uint8_t>(size >> 16));
|
||||
w.template writeBytes<2>(static_cast<uint16_t>(size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_DETAILS_ADAPTER_UTILS_H
|
||||
200
include/bitsery/details/brief_syntax_common.h
Normal file
200
include/bitsery/details/brief_syntax_common.h
Normal file
@@ -0,0 +1,200 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_DETAILS_BRIEF_SYNTAX_COMMON_H
|
||||
#define BITSERY_DETAILS_BRIEF_SYNTAX_COMMON_H
|
||||
|
||||
#include "../traits/core/traits.h"
|
||||
#include "serialization_common.h"
|
||||
#include <limits>
|
||||
|
||||
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
|
||||
|
||||
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())
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
s.container(c);
|
||||
}
|
||||
|
||||
// overloads for text processing to apply maxSize
|
||||
|
||||
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())
|
||||
{
|
||||
using TValue = typename traits::ContainerTraits<T>::TValue;
|
||||
s.template text<sizeof(TValue)>(c, maxSize);
|
||||
}
|
||||
|
||||
template<typename S,
|
||||
typename T,
|
||||
typename std::enable_if<
|
||||
!traits::ContainerTraits<T>::isResizable>::type* = nullptr>
|
||||
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
|
||||
{};
|
||||
|
||||
// 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_ } {};
|
||||
T (&data)[N];
|
||||
};
|
||||
|
||||
template<typename S, typename T, size_t N>
|
||||
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)
|
||||
{
|
||||
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_ } {};
|
||||
T& data;
|
||||
size_t maxSize;
|
||||
};
|
||||
|
||||
// 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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>{});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BITSERY_DETAILS_BRIEF_SYNTAX_COMMON_H
|
||||
@@ -1,149 +0,0 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_DETAILS_FLEXIBLE_COMMON_H
|
||||
#define BITSERY_DETAILS_FLEXIBLE_COMMON_H
|
||||
|
||||
#include "../traits/core/traits.h"
|
||||
#include <limits>
|
||||
|
||||
namespace bitsery {
|
||||
namespace flexible {
|
||||
|
||||
//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()) {
|
||||
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()) {
|
||||
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) {
|
||||
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) {
|
||||
s.container(c);
|
||||
}
|
||||
|
||||
//overloads for text processing to apply maxSize
|
||||
|
||||
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()) {
|
||||
using TValue = typename traits::ContainerTraits<T>::TValue;
|
||||
s.template text<sizeof(TValue)>(c, maxSize);
|
||||
}
|
||||
|
||||
template<typename S, typename T, typename std::enable_if<
|
||||
!traits::ContainerTraits<T>::isResizable>::type * = nullptr>
|
||||
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 ArchiveWrapperFnc {
|
||||
|
||||
};
|
||||
|
||||
//this type is used to differentiate between container and text behaviour
|
||||
template<typename T, size_t N, bool isText>
|
||||
struct CArray : public ArchiveWrapperFnc {
|
||||
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) {
|
||||
processText(s, str.data);
|
||||
}
|
||||
|
||||
template<typename S, typename T, size_t N>
|
||||
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 ArchiveWrapperFnc {
|
||||
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
|
||||
template<typename S, typename T>
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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>{});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_DETAILS_FLEXIBLE_COMMON_H
|
||||
@@ -1,25 +1,24 @@
|
||||
//MIT License
|
||||
// MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_DETAILS_NOT_DEFINED_TYPE_H
|
||||
#define BITSERY_DETAILS_NOT_DEFINED_TYPE_H
|
||||
@@ -27,53 +26,56 @@
|
||||
#include <iterator>
|
||||
|
||||
namespace bitsery {
|
||||
namespace details {
|
||||
//this type is used to show clearer error messages
|
||||
struct NotDefinedType {
|
||||
//just swallow anything that is passed during creating
|
||||
template <typename ... 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 nessesarry to work with iterators
|
||||
friend bool operator == (const NotDefinedType&, const NotDefinedType&) {
|
||||
return true;
|
||||
}
|
||||
friend bool operator != (const NotDefinedType&, const NotDefinedType&) {
|
||||
return false;
|
||||
}
|
||||
NotDefinedType& operator += (int) {
|
||||
return *this;
|
||||
}
|
||||
NotDefinedType& operator -= (int) {
|
||||
return *this;
|
||||
}
|
||||
namespace details {
|
||||
// this type is used to show clearer error messages
|
||||
struct NotDefinedType
|
||||
{
|
||||
// just swallow anything that is passed during creating
|
||||
template<typename... 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&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
friend bool operator!=(const NotDefinedType&, const NotDefinedType&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
NotDefinedType& operator+=(int) { return *this; }
|
||||
NotDefinedType& operator-=(int) { return *this; }
|
||||
|
||||
friend int operator - (const NotDefinedType&, const NotDefinedType&) {
|
||||
return 0;
|
||||
}
|
||||
friend int operator-(const NotDefinedType&, const NotDefinedType&)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int& operator*() {
|
||||
return data;
|
||||
}
|
||||
int data;
|
||||
};
|
||||
int& operator*() { return data; }
|
||||
int data{};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct IsDefined:public std::integral_constant<bool, !std::is_same<NotDefinedType, T>::value> {
|
||||
};
|
||||
}
|
||||
template<typename T>
|
||||
struct IsDefined
|
||||
: public std::integral_constant<bool, !std::is_same<NotDefinedType, T>::value>
|
||||
{};
|
||||
}
|
||||
}
|
||||
|
||||
namespace std {
|
||||
//define iterator traits to work with standart algorithms
|
||||
template <>
|
||||
struct iterator_traits<bitsery::details::NotDefinedType> {
|
||||
using difference_type = int;
|
||||
using value_type = int;
|
||||
using pointer = int*;
|
||||
using reference = int&;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
};
|
||||
// define iterator traits to work with standart algorithms
|
||||
template<>
|
||||
struct iterator_traits<bitsery::details::NotDefinedType>
|
||||
{
|
||||
using difference_type = int;
|
||||
using value_type = int;
|
||||
using pointer = int*;
|
||||
using reference = int&;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
};
|
||||
}
|
||||
#endif //BITSERY_DETAILS_NOT_DEFINED_TYPE_H
|
||||
#endif // BITSERY_DETAILS_NOT_DEFINED_TYPE_H
|
||||
|
||||
@@ -1,401 +1,538 @@
|
||||
//MIT License
|
||||
// MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_DETAILS_SERIALIZATION_COMMON_H
|
||||
#define BITSERY_DETAILS_SERIALIZATION_COMMON_H
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <tuple>
|
||||
#include "adapter_utils.h"
|
||||
#include "../traits/core/traits.h"
|
||||
|
||||
#include "adapter_common.h"
|
||||
#include <tuple>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
//this allows to call private serialize method for the class
|
||||
//just make friend it to that class
|
||||
struct Access {
|
||||
template<typename S, typename T>
|
||||
static auto serialize(S &s, T &obj) -> decltype(obj.serialize(s)) {
|
||||
obj.serialize(s);
|
||||
}
|
||||
};
|
||||
// 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))
|
||||
{
|
||||
obj.serialize(s);
|
||||
}
|
||||
|
||||
//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> {
|
||||
};
|
||||
template<typename T>
|
||||
static T create()
|
||||
{
|
||||
// if you get an error here, please create default constructor
|
||||
return T{};
|
||||
}
|
||||
template<typename T>
|
||||
static T* create(void* ptr)
|
||||
{
|
||||
return new (ptr) T{};
|
||||
}
|
||||
};
|
||||
|
||||
//types you need to inherit from when specializing SelectSerializeFnc class
|
||||
struct UseNonMemberFnc : std::integral_constant<int, 1> {
|
||||
};
|
||||
struct UseMemberFnc : std::integral_constant<int, 2> {
|
||||
};
|
||||
// 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
|
||||
{
|
||||
template<typename S, typename T>
|
||||
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>
|
||||
{};
|
||||
template<typename Ext>
|
||||
struct FtorExtValue2b : FtorExtValue<2, Ext>
|
||||
{};
|
||||
template<typename Ext>
|
||||
struct FtorExtValue4b : FtorExtValue<4, Ext>
|
||||
{};
|
||||
template<typename Ext>
|
||||
struct FtorExtValue8b : FtorExtValue<8, Ext>
|
||||
{};
|
||||
template<typename Ext>
|
||||
struct FtorExtValue16b : FtorExtValue<16, Ext>
|
||||
{};
|
||||
|
||||
//serializer/deserializer, does not public interface to get underlying writer/reader
|
||||
//to prevent users from using writer/reader directly, because they have different interface
|
||||
//and they cannot be used describing serialization flows.: use extensions for this reason.
|
||||
//this class allows to get underlying adapter writer/reader, and only should be used outside serialization functions.
|
||||
struct AdapterAccess {
|
||||
template<typename Serializer>
|
||||
static typename Serializer::TWriter &getWriter(Serializer &s) {
|
||||
return s._writer;
|
||||
}
|
||||
template<typename Ext>
|
||||
struct FtorExtObject : public Ext
|
||||
{
|
||||
template<typename S, typename T>
|
||||
void operator()(S& s, T& v) const
|
||||
{
|
||||
s.ext(v, static_cast<const Ext&>(*this));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Deserializer>
|
||||
static typename Deserializer::TReader &getReader(Deserializer &s) {
|
||||
return s._reader;
|
||||
}
|
||||
};
|
||||
// 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>
|
||||
{};
|
||||
|
||||
namespace details {
|
||||
// types you need to inherit from when specializing SelectSerializeFnc class
|
||||
struct UseNonMemberFnc : std::integral_constant<int, 1>
|
||||
{};
|
||||
struct UseMemberFnc : std::integral_constant<int, 2>
|
||||
{};
|
||||
|
||||
//helper types for error handling
|
||||
template<typename T>
|
||||
struct IsContainerTraitsDefined : public IsDefined<typename traits::ContainerTraits<T>::TValue> {
|
||||
};
|
||||
namespace details {
|
||||
|
||||
template<typename T>
|
||||
struct IsTextTraitsDefined : public IsDefined<typename traits::TextTraits<T>::TValue> {
|
||||
};
|
||||
// helper types for error handling
|
||||
template<typename T>
|
||||
struct IsContainerTraitsDefined
|
||||
: public IsDefined<typename traits::ContainerTraits<T>::TValue>
|
||||
{};
|
||||
|
||||
template<typename Ext, typename T>
|
||||
struct IsExtensionTraitsDefined : public IsDefined<typename traits::ExtensionTraits<Ext, T>::TValue> {
|
||||
};
|
||||
template<typename T>
|
||||
struct IsTextTraitsDefined
|
||||
: public IsDefined<typename traits::TextTraits<T>::TValue>
|
||||
{};
|
||||
|
||||
template<typename Ext, typename T>
|
||||
struct IsExtensionTraitsDefined
|
||||
: public IsDefined<typename traits::ExtensionTraits<Ext, T>::TValue>
|
||||
{};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
//helper types for HasSerializeFunction
|
||||
template <typename S, typename T>
|
||||
using TrySerializeFunction = decltype(serialize(std::declval<S &>(), std::declval<T &>()));
|
||||
// helper types for HasSerializeFunction
|
||||
template<typename S, typename T>
|
||||
using TrySerializeFunction =
|
||||
decltype(serialize(std::declval<S&>(), std::declval<T&>()));
|
||||
|
||||
template <typename S, typename T>
|
||||
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 {};
|
||||
template<typename S, typename T>
|
||||
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
|
||||
{};
|
||||
|
||||
//helper types for HasSerializeMethod
|
||||
template <typename S, typename T>
|
||||
using TrySerializeMethod = decltype(Access::serialize(std::declval<S &>(), std::declval<T &>()));
|
||||
// helper types for HasSerializeMethod
|
||||
template<typename S, typename T>
|
||||
using TrySerializeMethod =
|
||||
decltype(Access::serialize(std::declval<S&>(), std::declval<T&>()));
|
||||
|
||||
template <typename S, typename T>
|
||||
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 {};
|
||||
template<typename S, typename T>
|
||||
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
|
||||
{};
|
||||
|
||||
//helper types for IsFlexibleIncluded
|
||||
template <typename S, typename T>
|
||||
using TryArchiveProcess = decltype(archiveProcess(std::declval<S &>(), std::declval<T &&>()));
|
||||
// helper types for IsBriefSyntaxIncluded
|
||||
template<typename S, typename T>
|
||||
using TryProcessBriefSyntax =
|
||||
decltype(processBriefSyntax(std::declval<S&>(), std::declval<T&&>()));
|
||||
|
||||
template <typename S, typename T>
|
||||
struct IsFlexibleIncludedHelper {
|
||||
template <typename Q, typename R, typename = TryArchiveProcess<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 IsBriefSyntaxIncludedHelper
|
||||
{
|
||||
template<typename Q, typename R, typename = TryProcessBriefSyntax<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 IsFlexibleIncluded :IsFlexibleIncludedHelper<S, T>::type {};
|
||||
template<typename S, typename T>
|
||||
struct IsBriefSyntaxIncluded : IsBriefSyntaxIncludedHelper<S, T>::type
|
||||
{};
|
||||
#else
|
||||
//helper metafunction, that is added to c++17
|
||||
template<typename... Ts>
|
||||
struct make_void {
|
||||
typedef void type;
|
||||
};
|
||||
template<typename... Ts>
|
||||
using void_t = typename make_void<Ts...>::type;
|
||||
// helper metafunction, that is added to c++17
|
||||
template<typename... Ts>
|
||||
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 {
|
||||
};
|
||||
template<typename, typename, typename = void>
|
||||
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 {
|
||||
};
|
||||
template<typename S, typename T>
|
||||
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
|
||||
{};
|
||||
|
||||
template<typename, typename, typename = void>
|
||||
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
|
||||
{};
|
||||
|
||||
template<typename S, typename T>
|
||||
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
|
||||
{};
|
||||
|
||||
//this solution doesn't work with visual studio, but is more elegant
|
||||
template<typename, typename, typename = void>
|
||||
struct IsFlexibleIncluded : std::false_type {
|
||||
};
|
||||
|
||||
template<typename S, typename T>
|
||||
struct IsFlexibleIncluded<S, T,
|
||||
void_t<decltype(archiveProcess(std::declval<S &>(), std::declval<T &&>()))>
|
||||
> : std::true_type {
|
||||
};
|
||||
template<typename S, typename T>
|
||||
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 {
|
||||
};
|
||||
// used for extensions when extension TValue = void
|
||||
struct DummyType
|
||||
{};
|
||||
|
||||
/*
|
||||
* this includes all integral types floats and enums(except bool)
|
||||
* 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> {
|
||||
};
|
||||
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>
|
||||
{};
|
||||
|
||||
template<typename T, typename Integral = void>
|
||||
struct IntegralFromFundamental {
|
||||
using TValue = T;
|
||||
};
|
||||
template<typename T, typename Integral = void>
|
||||
struct IntegralFromFundamental
|
||||
{
|
||||
using TValue = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
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_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;
|
||||
};
|
||||
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;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct UnsignedFromFundamental {
|
||||
using type = typename std::make_unsigned<typename IntegralFromFundamental<T>::TValue>::type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using SameSizeUnsigned = typename UnsignedFromFundamental<T>::type;
|
||||
template<typename T>
|
||||
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 {
|
||||
template<typename S, typename T>
|
||||
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"
|
||||
" template<typename S>\n"
|
||||
" void serialize(S& s)\n"
|
||||
" {\n"
|
||||
" ...\n"
|
||||
" }\n");
|
||||
using TDecayed = typename std::decay<T>::type;
|
||||
selectSerializeFnc(s, v, SelectSerializeFnc<TDecayed>{});
|
||||
}
|
||||
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"
|
||||
" ...\n"
|
||||
" }\n");
|
||||
using TDecayed = typename std::decay<T>::type;
|
||||
selectSerializeFnc(s, v, SelectSerializeFnc<TDecayed>{});
|
||||
}
|
||||
|
||||
private:
|
||||
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"
|
||||
" template <>\n"
|
||||
" struct SelectSerializeFnc<DerivedClass>:UseMemberFnc {};\n");
|
||||
selectSerializeFnc(s, v, std::integral_constant<int,
|
||||
HasSerializeFunction<S, T>::value ? 1 : 2>{});
|
||||
}
|
||||
static constexpr bool isDefined()
|
||||
{
|
||||
return HasSerializeFunction<S, T>::value || HasSerializeMethod<S, T>::value;
|
||||
}
|
||||
|
||||
static void selectSerializeFnc(S &s, T &v, std::integral_constant<int, 1>) {
|
||||
serialize(s, v);
|
||||
}
|
||||
private:
|
||||
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"
|
||||
" template <>\n"
|
||||
" struct SelectSerializeFnc<DerivedClass>:UseMemberFnc {};\n");
|
||||
selectSerializeFnc(s,
|
||||
v,
|
||||
std::integral_constant < int,
|
||||
HasSerializeFunction<S, T>::value ? 1 : 2 > {});
|
||||
}
|
||||
|
||||
static void selectSerializeFnc(S &s, T &v, std::integral_constant<int, 2>) {
|
||||
Access::serialize(s, v);
|
||||
}
|
||||
};
|
||||
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>)
|
||||
{
|
||||
Access::serialize(s, v);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* functions for object serialization
|
||||
*/
|
||||
|
||||
template<typename S, typename T, typename Enabled = void>
|
||||
struct ArchiveFunction {
|
||||
template<typename S, typename T, typename Enabled = void>
|
||||
struct BriefSyntaxFunction
|
||||
{
|
||||
|
||||
static void invoke(S &s, T &&obj) {
|
||||
static_assert(IsFlexibleIncluded<S, T>::value,
|
||||
"\nPlease include '<bitsery/flexible.h>' to use 'archive' function:\n");
|
||||
static void invoke(S& s, T&& obj)
|
||||
{
|
||||
static_assert(
|
||||
IsBriefSyntaxIncluded<S, T>::value,
|
||||
"\nPlease include '<bitsery/brief_syntax.h>' to use operator():\n");
|
||||
|
||||
archiveProcess(s, std::forward<T>(obj));
|
||||
}
|
||||
};
|
||||
processBriefSyntax(s, std::forward<T>(obj));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* helper function for getting context from serializer/deserializer
|
||||
*/
|
||||
/*
|
||||
* helper function for getting context from serializer/deserializer
|
||||
*/
|
||||
|
||||
template<typename T, template<typename...> class Template>
|
||||
struct IsSpecializationOf : std::false_type {
|
||||
};
|
||||
template<int Index, typename... Conds>
|
||||
struct FindIndex : std::integral_constant<int, Index>
|
||||
{};
|
||||
|
||||
template<template<typename...> class Template, typename... Args>
|
||||
struct IsSpecializationOf<Template<Args...>, Template> : std::true_type {
|
||||
};
|
||||
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
|
||||
{};
|
||||
|
||||
//helper types for better error messages
|
||||
template<typename Find, typename ... TList>
|
||||
struct GetTypeIndex : std::integral_constant<size_t, 0> {
|
||||
};
|
||||
template<typename T, typename Tuple>
|
||||
struct GetConvertibleTypeIndexFromTuple;
|
||||
|
||||
//found it
|
||||
template<typename Find, typename ... Tail>
|
||||
struct GetTypeIndex<Find, Find, Tail...> : std::integral_constant<size_t, 0> {
|
||||
};
|
||||
template<typename T, typename... Us>
|
||||
struct GetConvertibleTypeIndexFromTuple<T, std::tuple<Us...>>
|
||||
: FindIndex<0, std::is_convertible<Us&, T&>...>
|
||||
{};
|
||||
|
||||
//iteratates over types
|
||||
template<typename Find, typename Head, typename ... Tail>
|
||||
struct GetTypeIndex<Find, Head, Tail...> : std::integral_constant<size_t,
|
||||
1 + GetTypeIndex<Find, Tail...>::value> {
|
||||
};
|
||||
template<typename T, typename Tuple>
|
||||
struct IsExistsConvertibleTupleType;
|
||||
|
||||
template<typename Find, typename ... TList>
|
||||
struct HasType : std::integral_constant<bool, (GetTypeIndex<Find, TList...>::value<(sizeof ... (TList)))> {
|
||||
};
|
||||
template<typename T, typename... Us>
|
||||
struct IsExistsConvertibleTupleType<T, std::tuple<Us...>>
|
||||
: std::integral_constant<
|
||||
bool,
|
||||
GetConvertibleTypeIndexFromTuple<T, std::tuple<Us...>>::value !=
|
||||
sizeof...(Us)>
|
||||
{};
|
||||
|
||||
template<typename TCast, typename Tuple>
|
||||
struct HasContext : std::is_same<TCast, Tuple> {
|
||||
};
|
||||
/*
|
||||
* get context from internal or external, and check if it's convertible or not
|
||||
*/
|
||||
|
||||
template<typename TCast, typename ... Args>
|
||||
struct HasContext<TCast, std::tuple<Args...>> : HasType<TCast, Args...> {
|
||||
};
|
||||
|
||||
/*
|
||||
* get context, and static assert if type doesn't exists
|
||||
*/
|
||||
|
||||
template<typename TCast, typename ... Args>
|
||||
TCast *getContextImpl(std::tuple<Args...> *ctx, std::true_type) {
|
||||
using TCastIndex = GetTypeIndex<TCast, Args...>;
|
||||
static_assert(HasType<TCast, Args...>::value, "Invalid context cast. Type doesn't exists.");
|
||||
return std::addressof(std::get<TCastIndex::value>(*ctx));
|
||||
}
|
||||
|
||||
template<typename TCast, typename TContext>
|
||||
TCast *getContextImpl(TContext *ctx, std::false_type) {
|
||||
static_assert(std::is_convertible<TContext *, TCast *>::value, "Invalid context cast. Type doesn't exists.");
|
||||
return static_cast<TCast *>(ctx);
|
||||
}
|
||||
|
||||
//get local ctx
|
||||
template<typename TCast, typename TContext, typename TInternalContext>
|
||||
TCast *chooseInternalOrExternalContext(TContext *, TInternalContext &internalCtx, std::true_type) {
|
||||
return getContextImpl<TCast>(&internalCtx, std::true_type{});
|
||||
}
|
||||
|
||||
//get external ctx
|
||||
template<typename TCast, typename TContext, typename TInternalContext>
|
||||
TCast *chooseInternalOrExternalContext(TContext *ctx, TInternalContext &, std::false_type) {
|
||||
return ctx
|
||||
? getContextImpl<TCast>(ctx, IsSpecializationOf<TContext, std::tuple>{})
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
template<typename TCast, typename TContext, typename TInternalContext>
|
||||
TCast *getContext(TContext *ctx, TInternalContext &internalCtx) {
|
||||
return chooseInternalOrExternalContext<TCast>(ctx, internalCtx, HasContext<TCast, TInternalContext>{});
|
||||
}
|
||||
|
||||
/*
|
||||
* get context, if type doesn't exists then do not static_assert but return null instead
|
||||
*/
|
||||
|
||||
template<typename TCast, typename TContext>
|
||||
TCast *getContextFromTypeIfExists(TContext *ctx, std::true_type) {
|
||||
return static_cast<TCast *>(ctx);
|
||||
}
|
||||
|
||||
template<typename TCast, typename TContext>
|
||||
TCast *getContextFromTypeIfExists(TContext *, std::false_type) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename TCast, typename TContext>
|
||||
TCast *getContextImplIfExists(TContext *ctx, std::false_type) {
|
||||
return getContextFromTypeIfExists<TCast>(ctx, std::is_convertible<TContext *, TCast *>{});
|
||||
}
|
||||
|
||||
template<typename TCast, typename TContext>
|
||||
TCast *getContextFromTupleIfExists(TContext *ctx, std::true_type tmp) {
|
||||
return getContextImpl<TCast>(ctx, tmp);
|
||||
}
|
||||
|
||||
template<typename TCast, typename TContext>
|
||||
TCast *getContextFromTupleIfExists(TContext *, std::false_type) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename TCast, typename TContext>
|
||||
TCast *getContextImplIfExists(TContext *ctx, std::true_type) {
|
||||
return getContextFromTupleIfExists<TCast>(ctx, HasContext<TCast, TContext>{});
|
||||
}
|
||||
|
||||
//get local ctx
|
||||
template<typename TCast, typename TContext, typename TInternalContext>
|
||||
TCast *chooseInternalOrExternalContextIfExists(TContext *, TInternalContext &internalCtx, std::true_type) {
|
||||
return getContextImplIfExists<TCast>(&internalCtx, std::true_type{});
|
||||
}
|
||||
|
||||
//get external ctx
|
||||
template<typename TCast, typename TContext, typename TInternalContext>
|
||||
TCast *chooseInternalOrExternalContextIfExists(TContext *ctx, TInternalContext &, std::false_type) {
|
||||
return ctx
|
||||
? getContextImplIfExists<TCast>(ctx, IsSpecializationOf<TContext, std::tuple>{})
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
template<typename TCast, typename TContext, typename TInternalContext>
|
||||
TCast *getContextIfTypeExists(TContext *ctx, TInternalContext &internalCtx) {
|
||||
return chooseInternalOrExternalContextIfExists<TCast>(ctx, internalCtx, HasContext<TCast, TInternalContext>{});
|
||||
}
|
||||
|
||||
}
|
||||
template<bool AssertExists, typename TCast, typename TContext>
|
||||
TCast*
|
||||
getDirectlyIfExists(TContext& ctx, std::true_type)
|
||||
{
|
||||
return &static_cast<TCast&>(ctx);
|
||||
}
|
||||
|
||||
#endif //BITSERY_DETAILS_SERIALIZATION_COMMON_H
|
||||
template<bool AssertExists, typename TCast, typename TContext>
|
||||
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.");
|
||||
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...>>;
|
||||
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 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.");
|
||||
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&>{});
|
||||
}
|
||||
|
||||
// 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...>>{});
|
||||
}
|
||||
|
||||
template<typename Adapter, typename Context>
|
||||
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
|
||||
template<typename... TArgs>
|
||||
explicit AdapterAndContextRef(Context& ctx, TArgs&&... args)
|
||||
: _adapter{ std::forward<TArgs>(args)... }
|
||||
, _context{ ctx }
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* get serialization context.
|
||||
* this is optional, but might be required for some specific serialization
|
||||
* flows.
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
T& context()
|
||||
{
|
||||
return *getContext<true, T>(_context);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* contextOrNull()
|
||||
{
|
||||
return getContext<false, T>(_context);
|
||||
}
|
||||
|
||||
Adapter& adapter() & { return _adapter; }
|
||||
|
||||
Adapter adapter() && { return std::move(_adapter); }
|
||||
|
||||
protected:
|
||||
Adapter _adapter;
|
||||
Context& _context;
|
||||
};
|
||||
|
||||
template<typename Adapter>
|
||||
class AdapterAndContextRef<Adapter, void>
|
||||
{
|
||||
public:
|
||||
static constexpr bool HasContext = false;
|
||||
using Config = typename Adapter::TConfig;
|
||||
|
||||
template<typename... TArgs>
|
||||
explicit AdapterAndContextRef(TArgs&&... args)
|
||||
: _adapter{ std::forward<TArgs>(args)... }
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T& context()
|
||||
{
|
||||
static_assert(std::is_void<T>::value, "Context is not defined (is void).");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* contextOrNull()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Adapter& adapter() & { return _adapter; }
|
||||
|
||||
Adapter adapter() && { return std::move(_adapter); }
|
||||
|
||||
protected:
|
||||
Adapter _adapter;
|
||||
};
|
||||
|
||||
/**
|
||||
* other helper meta-functions
|
||||
*/
|
||||
|
||||
template<typename T, template<typename...> class Template>
|
||||
struct IsSpecializationOf : std::false_type
|
||||
{};
|
||||
|
||||
template<template<typename...> class Template, typename... Args>
|
||||
struct IsSpecializationOf<Template<Args...>, Template> : std::true_type
|
||||
{};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BITSERY_DETAILS_SERIALIZATION_COMMON_H
|
||||
|
||||
@@ -1,228 +0,0 @@
|
||||
//
|
||||
// Created by fraillt on 17.10.5.
|
||||
//
|
||||
|
||||
#ifndef BITSERY_DETAILS_SESSIONS_H
|
||||
#define BITSERY_DETAILS_SESSIONS_H
|
||||
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include "adapter_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace session {
|
||||
/*
|
||||
* writer/reader implementations that disable session support
|
||||
*/
|
||||
template <typename TWriter>
|
||||
struct DisabledSessionsWriter {
|
||||
void begin(TWriter& ) {
|
||||
static_assert(std::is_void<TWriter>::value, "Sessions is disabled, enable it via configuration");
|
||||
}
|
||||
void end(TWriter& ) {
|
||||
static_assert(std::is_void<TWriter>::value, "Sessions is disabled, enable it via configuration");
|
||||
}
|
||||
void flushSessions(TWriter& ) {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TReader>
|
||||
struct DisabledSessionsReader {
|
||||
template <typename TBufferContext>
|
||||
DisabledSessionsReader(TReader& , TBufferContext& ) {
|
||||
}
|
||||
|
||||
void begin() {
|
||||
static_assert(std::is_void<TReader>::value, "Sessions is disabled, enable it via configuration");
|
||||
}
|
||||
|
||||
void end() {
|
||||
static_assert(std::is_void<TReader>::value, "Sessions is disabled, enable it via configuration");
|
||||
}
|
||||
|
||||
bool hasActiveSessions() const {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* writer/reader real implementations
|
||||
* sessions reading requires to have random access iterators, so it cannot be used with streams
|
||||
*/
|
||||
template <typename TWriter>
|
||||
class SessionsWriter {
|
||||
public:
|
||||
|
||||
SessionsWriter() = default;
|
||||
SessionsWriter(const SessionsWriter&) = delete;
|
||||
SessionsWriter& operator = (const SessionsWriter& ) = delete;
|
||||
|
||||
SessionsWriter(SessionsWriter&&) = default;
|
||||
SessionsWriter& operator = (SessionsWriter&& ) = default;
|
||||
|
||||
void begin(TWriter& ) {
|
||||
//write position
|
||||
_sessionIndex.push(_sessions.size());
|
||||
_sessions.emplace_back(0);
|
||||
}
|
||||
|
||||
void end(TWriter& writer) {
|
||||
assert(!_sessionIndex.empty());
|
||||
//change position to session end
|
||||
auto sessionIt = std::next(std::begin(_sessions), _sessionIndex.top());
|
||||
_sessionIndex.pop();
|
||||
auto sessionSize = writer.writtenBytesCount();
|
||||
assert(sessionSize > 0);
|
||||
*sessionIt = sessionSize;
|
||||
}
|
||||
|
||||
void flushSessions(TWriter& writer) {
|
||||
if (_sessions.size()) {
|
||||
assert(_sessionIndex.empty());
|
||||
auto dataSize = writer.writtenBytesCount();
|
||||
for(auto& s:_sessions) {
|
||||
details::writeSize(writer, s);
|
||||
}
|
||||
_sessions.clear();
|
||||
|
||||
auto totalSize = writer.writtenBytesCount();
|
||||
//write offset where actual data ends
|
||||
auto sessionsOffset = totalSize - dataSize + 4;//4 bytes for offset data
|
||||
writer.template writeBytes<4>(static_cast<uint32_t>(sessionsOffset));
|
||||
}
|
||||
}
|
||||
private:
|
||||
std::vector<size_t> _sessions{};
|
||||
std::stack<size_t> _sessionIndex{};
|
||||
};
|
||||
|
||||
template <typename TReader>
|
||||
struct SessionsReader {
|
||||
using TIterator = typename TReader::TIterator;
|
||||
|
||||
template <typename InputAdapter>
|
||||
SessionsReader(TReader& r, InputAdapter& adapter)
|
||||
:_reader{r},
|
||||
_beginIt{details::SessionAccess::posIteratorRef<InputAdapter, TIterator>(adapter)},
|
||||
_posItRef{details::SessionAccess::posIteratorRef<InputAdapter, TIterator>(adapter)},
|
||||
_endItRef{details::SessionAccess::endIteratorRef<InputAdapter, TIterator>(adapter)}
|
||||
{
|
||||
}
|
||||
|
||||
SessionsReader(const SessionsReader&) = delete;
|
||||
SessionsReader& operator = (const SessionsReader& ) = delete;
|
||||
|
||||
SessionsReader(SessionsReader&&) = default;
|
||||
SessionsReader& operator = (SessionsReader&& ) = default;
|
||||
|
||||
void begin() {
|
||||
if (_sessions.empty()) {
|
||||
if (!initializeSessions())
|
||||
return;
|
||||
}
|
||||
|
||||
//save end position for current session
|
||||
_sessionsStack.push(_endItRef);
|
||||
if (_nextSessionIt != std::end(_sessions)) {
|
||||
if (std::distance(_posItRef, _endItRef) > 0) {
|
||||
//set end position for new session
|
||||
auto newEnd = std::next(_beginIt, *_nextSessionIt);
|
||||
if (std::distance(newEnd, _endItRef) < 0)
|
||||
{
|
||||
//new session cannot end further than current end
|
||||
_reader.setError(ReaderError::InvalidData);
|
||||
return;
|
||||
}
|
||||
_endItRef = newEnd;
|
||||
++_nextSessionIt;
|
||||
}
|
||||
//if we reached the end, means that there is no more data to read, hence there is no more sessions to advance to
|
||||
} else {
|
||||
//there is no data to read anymore
|
||||
//pos == end or buffer overflow while session is active
|
||||
if (!(_posItRef == _endItRef || _reader.error() == ReaderError::NoError) || !(_sessionsStack.size() > 1)) {
|
||||
_reader.setError(ReaderError::InvalidData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void end() {
|
||||
if (!_sessionsStack.empty()) {
|
||||
//move position to the end of session
|
||||
//can additionaly be checked for session data versioning
|
||||
//_pos == _end : same versions
|
||||
//distance(_pos,_end) > 0: reading newer version
|
||||
//error() == BUFFER_OVERFLOW: reading older version
|
||||
auto dist = std::distance(_posItRef, _endItRef);
|
||||
if (dist > 0) {
|
||||
//newer version might have some inner sessions, try to find the one after current ends
|
||||
auto currPos = static_cast<size_t>(std::distance(_beginIt, _endItRef));
|
||||
for (; _nextSessionIt != std::end(_sessions); ++_nextSessionIt) {
|
||||
if (*_nextSessionIt > currPos)
|
||||
break;
|
||||
}
|
||||
}
|
||||
//modify pointers only if no error or buffer overflow
|
||||
if (_reader.error() == ReaderError::NoError || _reader.error() == ReaderError::DataOverflow) {
|
||||
_posItRef = _endItRef;
|
||||
//restore end position
|
||||
_endItRef = _sessionsStack.top();
|
||||
}
|
||||
_sessionsStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
bool hasActiveSessions() const {
|
||||
return _sessionsStack.size() > 0;
|
||||
}
|
||||
|
||||
private:
|
||||
TReader& _reader;
|
||||
TIterator _beginIt;
|
||||
TIterator& _posItRef;
|
||||
TIterator& _endItRef;
|
||||
std::vector<size_t> _sessions{};
|
||||
std::vector<size_t>::iterator _nextSessionIt{};
|
||||
std::stack<TIterator> _sessionsStack{};
|
||||
|
||||
bool initializeSessions() {
|
||||
//save current position
|
||||
auto currPos = _posItRef;
|
||||
//read size
|
||||
if (std::distance(_posItRef, _endItRef) < 4) {
|
||||
_reader.setError(ReaderError::InvalidData);
|
||||
return false;
|
||||
}
|
||||
auto endSessionsSizesIt = std::next(_endItRef, -4);
|
||||
_posItRef = endSessionsSizesIt;
|
||||
uint32_t sessionsOffset{};
|
||||
_reader.template readBytes<4>(sessionsOffset);
|
||||
|
||||
auto bufferSize = std::distance(_beginIt, _endItRef);
|
||||
if (static_cast<size_t>(bufferSize) < sessionsOffset) {
|
||||
_reader.setError(ReaderError::InvalidData);
|
||||
return false;
|
||||
}
|
||||
//we can initialy resizes to this value, and we'll shrink it after reading
|
||||
//read session sizes
|
||||
auto sessionsIt = std::back_inserter(_sessions);
|
||||
_posItRef = std::next(_endItRef, -static_cast<int32_t>(sessionsOffset));
|
||||
while (std::distance(_posItRef, endSessionsSizesIt) > 0) {
|
||||
size_t size;
|
||||
details::readSize(_reader, size, bufferSize);
|
||||
*sessionsIt++ = size;
|
||||
}
|
||||
_sessions.shrink_to_fit();
|
||||
//set iterators to data
|
||||
_posItRef = currPos;
|
||||
_endItRef = std::next(_endItRef, -static_cast<int32_t>(sessionsOffset));
|
||||
_nextSessionIt = std::begin(_sessions);//set before first session;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_DETAILS_SESSIONS_H
|
||||
220
include/bitsery/ext/compact_value.h
Normal file
220
include/bitsery/ext/compact_value.h
Normal file
@@ -0,0 +1,220 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2018 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_COMPACT_VALUE_H
|
||||
#define BITSERY_EXT_COMPACT_VALUE_H
|
||||
|
||||
#include "../details/serialization_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace details {
|
||||
|
||||
template<bool CheckOverflow>
|
||||
class CompactValueImpl
|
||||
{
|
||||
public:
|
||||
template<typename Ser, typename T, typename Fnc>
|
||||
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>{});
|
||||
}
|
||||
|
||||
template<typename Des, typename T, typename Fnc>
|
||||
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>{});
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
writer.template writeBytes<1>(v);
|
||||
}
|
||||
|
||||
template<typename Reader, typename T>
|
||||
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>{});
|
||||
writeBytes(writer, val);
|
||||
}
|
||||
|
||||
template<typename Reader, typename T>
|
||||
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>{});
|
||||
}
|
||||
|
||||
// zigzag encode signed types
|
||||
template<typename T>
|
||||
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
|
||||
{
|
||||
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)));
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// write/read bytes one by one
|
||||
template<typename Writer, typename T>
|
||||
void writeBytes(Writer& w, const T& v) const
|
||||
{
|
||||
using TFast = typename FastType<T>::type;
|
||||
auto val = static_cast<TFast>(v);
|
||||
while (val > 0x7Fu) {
|
||||
w.template writeBytes<1>(static_cast<uint8_t>(val | 0x80u));
|
||||
val >>= 7u;
|
||||
}
|
||||
w.template writeBytes<1>(static_cast<uint8_t>(val));
|
||||
}
|
||||
|
||||
template<bool CheckErrors, typename Reader, typename T>
|
||||
void readBytes(Reader& r, T& v) const
|
||||
{
|
||||
using TFast = typename FastType<T>::type;
|
||||
constexpr auto TBITS = sizeof(T) * 8;
|
||||
uint8_t b1{ 0x80u };
|
||||
auto i = 0u;
|
||||
TFast tmp = {};
|
||||
for (; i < TBITS && b1 > 0x7Fu; i += 7u) {
|
||||
r.template readBytes<1>(b1);
|
||||
tmp += static_cast<TFast>(b1 & 0x7Fu) << i;
|
||||
}
|
||||
v = static_cast<T>(tmp);
|
||||
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
|
||||
{
|
||||
constexpr auto TBITS = sizeof(T) * 8;
|
||||
if (shiftedBy > TBITS && remainder >> (TBITS + 7 - shiftedBy)) {
|
||||
r.error(bitsery::ReaderError::InvalidData);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Reader, typename T>
|
||||
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 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>
|
||||
{
|
||||
using TValue = T;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
// 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>
|
||||
{
|
||||
// use dummy implemenations for value and object overload
|
||||
using TValue = void;
|
||||
// only enable object overload
|
||||
static constexpr bool SupportValueOverload = false;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
|
||||
template<typename T, bool Check>
|
||||
struct ExtensionTraits<details::CompactValueImpl<Check>, T>
|
||||
{
|
||||
using TValue = T;
|
||||
static constexpr bool SupportValueOverload = !Check;
|
||||
static constexpr bool SupportObjectOverload = Check;
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // BITSERY_EXT_COMPACT_VALUE_H
|
||||
@@ -1,24 +1,24 @@
|
||||
//MIT License
|
||||
// MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_ENTROPY_H
|
||||
#define BITSERY_EXT_ENTROPY_H
|
||||
@@ -27,76 +27,88 @@
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace details {
|
||||
template<typename TValue, typename TContainer>
|
||||
size_t findEntropyIndex(const TValue &v, const TContainer &defValues) {
|
||||
size_t index{1u};
|
||||
for (auto &d:defValues) {
|
||||
if (d == v)
|
||||
return index;
|
||||
++index;
|
||||
}
|
||||
return 0u;
|
||||
}
|
||||
}
|
||||
namespace details {
|
||||
template<typename TValue, typename TContainer>
|
||||
size_t
|
||||
findEntropyIndex(const TValue& v, const TContainer& defValues)
|
||||
{
|
||||
size_t index{ 1u };
|
||||
for (auto& d : defValues) {
|
||||
if (d == v)
|
||||
return index;
|
||||
++index;
|
||||
}
|
||||
return 0u;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ext {
|
||||
namespace ext {
|
||||
|
||||
template<typename TContainer>
|
||||
class Entropy {
|
||||
public:
|
||||
template<typename TContainer>
|
||||
class Entropy
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
constexpr Entropy(TContainer& values, bool alignBeforeData = true)
|
||||
: _values{ values }
|
||||
, _alignBeforeData{ alignBeforeData } {};
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
constexpr Entropy(TContainer& values, bool alignBeforeData=true)
|
||||
: _values{values},
|
||||
_alignBeforeData{alignBeforeData} {
|
||||
};
|
||||
template<typename Ser, typename T, typename Fnc>
|
||||
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) });
|
||||
if (_alignBeforeData)
|
||||
s.adapter().align();
|
||||
if (!index)
|
||||
fnc(s, const_cast<T&>(obj));
|
||||
}
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &s, Writer &, 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)});
|
||||
if (_alignBeforeData)
|
||||
s.align();
|
||||
if (!index)
|
||||
fnc(const_cast<T &>(obj));
|
||||
}
|
||||
template<typename Des, typename T, typename Fnc>
|
||||
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) });
|
||||
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
|
||||
fnc(d, obj);
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &d, Reader &, 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)});
|
||||
if (_alignBeforeData)
|
||||
d.align();
|
||||
if (index)
|
||||
obj = *std::next(std::begin(_values), index-1);
|
||||
else
|
||||
fnc(obj);
|
||||
}
|
||||
private:
|
||||
TContainer& _values;
|
||||
bool _alignBeforeData;
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
TContainer& _values;
|
||||
bool _alignBeforeData;
|
||||
};
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename TContainer, typename T>
|
||||
struct ExtensionTraits<ext::Entropy<TContainer>, T> {
|
||||
using TValue = T;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
}
|
||||
namespace traits {
|
||||
template<typename TContainer, typename T>
|
||||
struct ExtensionTraits<ext::Entropy<TContainer>, T>
|
||||
{
|
||||
using TValue = T;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //BITSERY_EXT_ENTROPY_H
|
||||
#endif // BITSERY_EXT_ENTROPY_H
|
||||
|
||||
@@ -1,67 +1,89 @@
|
||||
//MIT License
|
||||
// MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_GROWABLE_H
|
||||
#define BITSERY_EXT_GROWABLE_H
|
||||
|
||||
#include "../traits/core/traits.h"
|
||||
#include <cstdint>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace ext {
|
||||
namespace ext {
|
||||
|
||||
/*
|
||||
* enables to add additional serialization methods at the end of method, without breaking existing older code
|
||||
*/
|
||||
class Growable {
|
||||
public:
|
||||
/*
|
||||
* 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
|
||||
{
|
||||
public:
|
||||
template<typename Ser, typename T, typename Fnc>
|
||||
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));
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &, Writer &writer, const T &obj, Fnc &&fnc) const {
|
||||
writer.beginSession();
|
||||
fnc(const_cast<T&>(obj));
|
||||
writer.endSession();
|
||||
}
|
||||
fnc(ser, const_cast<T&>(obj));
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &, Reader &reader, T &obj, Fnc &&fnc) const {
|
||||
reader.beginSession();
|
||||
fnc(obj);
|
||||
reader.endSession();
|
||||
}
|
||||
};
|
||||
}
|
||||
const auto endPos = writer.currentWritePos();
|
||||
writer.currentWritePos(startPos);
|
||||
writer.template writeBytes<4>(static_cast<uint32_t>(endPos - startPos));
|
||||
writer.currentWritePos(endPos);
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::Growable, T> {
|
||||
using TValue = T;
|
||||
static constexpr bool SupportValueOverload = false;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
}
|
||||
template<typename Des, typename T, typename Fnc>
|
||||
void deserialize(Des& des, T& obj, Fnc&& fnc) const
|
||||
{
|
||||
auto& reader = des.adapter();
|
||||
uint32_t size{};
|
||||
const auto readEndPos = reader.currentReadEndPos();
|
||||
const auto startPos = reader.currentReadPos();
|
||||
reader.template readBytes<4>(size);
|
||||
reader.currentReadEndPos(startPos + size);
|
||||
|
||||
fnc(des, obj);
|
||||
|
||||
reader.currentReadPos(startPos + size);
|
||||
reader.currentReadEndPos(readEndPos);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::Growable, T>
|
||||
{
|
||||
using TValue = T;
|
||||
static constexpr bool SupportValueOverload = false;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //BITSERY_EXT_GROWABLE_H
|
||||
#endif // BITSERY_EXT_GROWABLE_H
|
||||
|
||||
@@ -1,156 +1,169 @@
|
||||
//MIT License
|
||||
// MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_INHERITANCE_H
|
||||
#define BITSERY_EXT_INHERITANCE_H
|
||||
|
||||
#include <unordered_set>
|
||||
#include "../ext/utils/memory_resource.h"
|
||||
#include "../traits/core/traits.h"
|
||||
#include <unordered_set>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace ext {
|
||||
namespace ext {
|
||||
|
||||
//required when virtual inheritance (ext::VirtualBaseClass) exists in serialization flow.
|
||||
//for standard inheritance (ext::BaseClass) it is optional.
|
||||
class InheritanceContext {
|
||||
public:
|
||||
InheritanceContext() = default;
|
||||
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 &) {
|
||||
if (_depth == 0) {
|
||||
const void* ptr = std::addressof(derived);
|
||||
if ( _parentPtr != ptr)
|
||||
_virtualBases.clear();
|
||||
_parentPtr = ptr;
|
||||
}
|
||||
++_depth;
|
||||
}
|
||||
|
||||
template <typename TDerived, typename TBase>
|
||||
bool beginVirtualBase(const TDerived &derived, const TBase &base) {
|
||||
beginBase(derived, base);
|
||||
return _virtualBases.emplace(std::addressof(base)).second;
|
||||
}
|
||||
|
||||
void end() {
|
||||
--_depth;
|
||||
}
|
||||
|
||||
private:
|
||||
//these members are required to know when we can clear _virtualBases
|
||||
size_t _depth{};
|
||||
const void* _parentPtr{};
|
||||
//add virtual bases to the list, as long as we're on the same parent
|
||||
std::unordered_set<const void*> _virtualBases{};
|
||||
};
|
||||
|
||||
template <typename TBase>
|
||||
class BaseClass {
|
||||
public:
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &ser, Writer &, const T &obj, Fnc &&fnc) const {
|
||||
auto& resObj = static_cast<const TBase&>(obj);
|
||||
if (auto ctx = ser.template contextOrNull<InheritanceContext>()) {
|
||||
ctx->beginBase(obj, resObj);
|
||||
fnc(const_cast<TBase&>(resObj));
|
||||
ctx->end();
|
||||
} else {
|
||||
fnc(const_cast<TBase&>(resObj));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &des, Reader &, T &obj, Fnc &&fnc) const {
|
||||
auto& resObj = static_cast<TBase&>(obj);
|
||||
if (auto ctx = des.template contextOrNull<InheritanceContext>()) {
|
||||
ctx->beginBase(obj, resObj);
|
||||
fnc(resObj);
|
||||
ctx->end();
|
||||
} else {
|
||||
fnc(resObj);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//requires InheritanceContext
|
||||
template <typename TBase>
|
||||
class VirtualBaseClass {
|
||||
public:
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &ser, Writer &, const T &obj, Fnc &&fnc) const {
|
||||
auto ctx = ser.template context<InheritanceContext>();
|
||||
auto& resObj = static_cast<const TBase&>(obj);
|
||||
if (ctx->beginVirtualBase(obj, resObj))
|
||||
fnc(const_cast<TBase&>(resObj));
|
||||
ctx->end();
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &des, Reader &, T &obj, Fnc &&fnc) const {
|
||||
auto ctx = des.template context<InheritanceContext>();
|
||||
auto& resObj = static_cast<TBase&>(obj);
|
||||
if (ctx->beginVirtualBase(obj, resObj))
|
||||
fnc(resObj);
|
||||
ctx->end();
|
||||
}
|
||||
|
||||
};
|
||||
// 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&)
|
||||
{
|
||||
if (_depth == 0) {
|
||||
const void* ptr = std::addressof(derived);
|
||||
if (_parentPtr != ptr)
|
||||
_virtualBases.clear();
|
||||
_parentPtr = ptr;
|
||||
}
|
||||
++_depth;
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename TBase, typename T>
|
||||
struct ExtensionTraits<ext::BaseClass<TBase>, T> {
|
||||
static_assert(std::is_base_of<TBase, T>::value, "Invalid base class");
|
||||
template<typename TDerived, typename TBase>
|
||||
bool beginVirtualBase(const TDerived& derived, const TBase& base)
|
||||
{
|
||||
beginBase(derived, base);
|
||||
return _virtualBases.emplace(std::addressof(base)).second;
|
||||
}
|
||||
|
||||
using TValue = TBase;
|
||||
static constexpr bool SupportValueOverload = false;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
void end() { --_depth; }
|
||||
|
||||
template<typename TBase, typename T>
|
||||
struct ExtensionTraits<ext::VirtualBaseClass<TBase>, T> {
|
||||
static_assert(std::is_base_of<TBase, T>::value, "Invalid base class");
|
||||
private:
|
||||
// these members are required to know when we can clear _virtualBases
|
||||
size_t _depth{};
|
||||
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;
|
||||
};
|
||||
|
||||
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.
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
template<typename TBase>
|
||||
class BaseClass
|
||||
{
|
||||
public:
|
||||
template<typename Ser, typename T, typename Fnc>
|
||||
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);
|
||||
fnc(ser, const_cast<TBase&>(resObj));
|
||||
ctx->end();
|
||||
} else {
|
||||
fnc(ser, const_cast<TBase&>(resObj));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Des, typename T, typename Fnc>
|
||||
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);
|
||||
fnc(des, resObj);
|
||||
ctx->end();
|
||||
} else {
|
||||
fnc(des, resObj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// requires InheritanceContext
|
||||
template<typename TBase>
|
||||
class VirtualBaseClass
|
||||
{
|
||||
public:
|
||||
template<typename Ser, typename T, typename Fnc>
|
||||
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))
|
||||
fnc(ser, const_cast<TBase&>(resObj));
|
||||
ctx.end();
|
||||
}
|
||||
|
||||
template<typename Des, typename T, typename Fnc>
|
||||
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>
|
||||
{
|
||||
static_assert(std::is_base_of<TBase, T>::value, "Invalid base class");
|
||||
|
||||
#endif //BITSERY_EXT_INHERITANCE_H
|
||||
using TValue = TBase;
|
||||
static constexpr bool SupportValueOverload = false;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
|
||||
template<typename TBase, typename 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.
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BITSERY_EXT_INHERITANCE_H
|
||||
|
||||
@@ -1,186 +1,226 @@
|
||||
//MIT License
|
||||
// MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_POINTER_H
|
||||
#define BITSERY_EXT_POINTER_H
|
||||
|
||||
#include "../traits/core/traits.h"
|
||||
#include "utils/pointer_utils.h"
|
||||
#include "utils/polymorphism_utils.h"
|
||||
#include "utils/rtti_utils.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace ext {
|
||||
namespace ext {
|
||||
|
||||
namespace details_pointer {
|
||||
namespace pointer_details {
|
||||
|
||||
template<typename S>
|
||||
PointerLinkingContext &getLinkingContext(S &s) {
|
||||
auto res = s.template context<PointerLinkingContext>();
|
||||
assert(res != nullptr);
|
||||
return *res;
|
||||
}
|
||||
template<typename T>
|
||||
struct PtrOwnerManager
|
||||
{
|
||||
static_assert(std::is_pointer<T>::value, "");
|
||||
|
||||
using TElement = typename std::remove_pointer<T>::type;
|
||||
|
||||
template<typename TObject>
|
||||
struct RawPointerObjectHandler {
|
||||
static TElement* getPtr(T& obj) { return obj; }
|
||||
|
||||
using TPointer = TObject;
|
||||
static constexpr PointerOwnershipType getOwnership()
|
||||
{
|
||||
return PointerOwnershipType::Owner;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void create(TObject &obj) const {
|
||||
obj = new T{};
|
||||
}
|
||||
static void create(T& obj,
|
||||
pointer_utils::PolyAllocWithTypeId alloc,
|
||||
size_t typeId)
|
||||
{
|
||||
obj = alloc.newObject<TElement>(typeId);
|
||||
}
|
||||
|
||||
void destroy(TObject &obj) const {
|
||||
delete obj;
|
||||
obj = nullptr;
|
||||
}
|
||||
static void createPolymorphic(
|
||||
T& obj,
|
||||
pointer_utils::PolyAllocWithTypeId alloc,
|
||||
const std::shared_ptr<PolymorphicHandlerBase>& handler)
|
||||
{
|
||||
obj = static_cast<TElement*>(handler->create(alloc));
|
||||
}
|
||||
|
||||
const TPointer getPtr(const TObject &obj) const {
|
||||
return obj;
|
||||
}
|
||||
static void destroy(T& obj,
|
||||
pointer_utils::PolyAllocWithTypeId alloc,
|
||||
size_t typeId)
|
||||
{
|
||||
alloc.deleteObject(obj, typeId);
|
||||
obj = nullptr;
|
||||
}
|
||||
|
||||
TPointer getPtr(TObject &obj) const {
|
||||
return obj;
|
||||
}
|
||||
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
|
||||
{
|
||||
static_assert(std::is_pointer<T>::value, "");
|
||||
|
||||
template <typename TObject>
|
||||
struct RawPointerManagerConfig {
|
||||
using RTTI = bitsery::ext::utils::StandardRTTI;
|
||||
static constexpr PointerOwnershipType OwnershipType = PointerOwnershipType::Owner;
|
||||
using TElement = typename std::remove_pointer<T>::type;
|
||||
|
||||
using Handler = RawPointerObjectHandler<TObject>;
|
||||
static TElement* getPtr(T& obj) { return obj; }
|
||||
|
||||
static std::unique_ptr<utils::PointerSharedContextBase> createSharedContext(TObject &) {
|
||||
return {};
|
||||
}
|
||||
static constexpr PointerOwnershipType getOwnership()
|
||||
{
|
||||
return PointerOwnershipType::Observer;
|
||||
}
|
||||
|
||||
static void restoreFromSharedContext(TObject &, utils::PointerSharedContextBase *) {
|
||||
// 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) { obj = nullptr; }
|
||||
|
||||
class PointerOwner : public utils::PointerOwnerManager<details_pointer::RawPointerManagerConfig> {
|
||||
public:
|
||||
explicit PointerOwner(PointerType ptrType = PointerType::Nullable) : PointerOwnerManager(ptrType) {}
|
||||
};
|
||||
static void destroyPolymorphic(T& obj,
|
||||
MemResourceBase*,
|
||||
const std::shared_ptr<PolymorphicHandlerBase>&)
|
||||
{
|
||||
obj = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class PointerObserver {
|
||||
public:
|
||||
template<typename T>
|
||||
struct NonPtrManager
|
||||
{
|
||||
|
||||
explicit PointerObserver(PointerType ptrType = PointerType::Nullable) : _ptrType{ptrType} {}
|
||||
static_assert(!std::is_pointer<T>::value, "");
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &ser, Writer &w, const T &obj, Fnc &&) const {
|
||||
auto &ctx = details_pointer::getLinkingContext(ser);
|
||||
if (obj) {
|
||||
details::writeSize(w, ctx.getInfoByPtr(obj, PointerOwnershipType::Observer).id);
|
||||
} else {
|
||||
assert(_ptrType == PointerType::Nullable);
|
||||
details::writeSize(w, 0);
|
||||
}
|
||||
}
|
||||
using TElement = T;
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &des, Reader &r, T &obj, Fnc &&) const {
|
||||
size_t id{};
|
||||
details::readSize(r, id, std::numeric_limits<size_t>::max());
|
||||
if (id) {
|
||||
auto &ctx = details_pointer::getLinkingContext(des);
|
||||
ctx.getInfoById(id, PointerOwnershipType::Observer).processObserver(reinterpret_cast<void *&>(obj));
|
||||
} else {
|
||||
if (_ptrType == PointerType::Nullable)
|
||||
obj = nullptr;
|
||||
else
|
||||
r.setError(ReaderError::InvalidPointer);
|
||||
}
|
||||
}
|
||||
static TElement* getPtr(T& obj) { return &obj; }
|
||||
|
||||
private:
|
||||
PointerType _ptrType;
|
||||
};
|
||||
static constexpr PointerOwnershipType getOwnership()
|
||||
{
|
||||
return PointerOwnershipType::Owner;
|
||||
}
|
||||
|
||||
class ReferencedByPointer {
|
||||
public:
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &ser, Writer &w, const T &obj, Fnc &&fnc) const {
|
||||
auto &ctx = details_pointer::getLinkingContext(ser);
|
||||
details::writeSize(w, ctx.getInfoByPtr(&obj, PointerOwnershipType::Owner).id);
|
||||
fnc(const_cast<T &>(obj));
|
||||
}
|
||||
// this code is unreachable for reference type, but is necessary to compile
|
||||
// LCOV_EXCL_START
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &des, Reader &r, T &obj, Fnc &&fnc) const {
|
||||
size_t id{};
|
||||
details::readSize(r, id, std::numeric_limits<size_t>::max());
|
||||
if (id) {
|
||||
auto &ctx = details_pointer::getLinkingContext(des);
|
||||
fnc(obj);
|
||||
ctx.getInfoById(id, PointerOwnershipType::Owner).processOwner(&obj);
|
||||
} else {
|
||||
//cannot be null for references
|
||||
r.setError(ReaderError::InvalidPointer);
|
||||
}
|
||||
}
|
||||
};
|
||||
static void create(T&, MemResourceBase*, size_t) {}
|
||||
|
||||
}
|
||||
static void createPolymorphic(T&,
|
||||
MemResourceBase*,
|
||||
const std::shared_ptr<PolymorphicHandlerBase>&)
|
||||
{
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
static void destroy(T&, MemResourceBase*, size_t) {}
|
||||
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::PointerOwner, T *> {
|
||||
using TValue = T;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
//pointers cannot have lamba overload, when polymorphism support will be added
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
|
||||
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
|
||||
using TValue = T;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
//pointers cannot have lamba overload, when polymorphism support will be added
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
|
||||
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
|
||||
using TValue = T;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
}
|
||||
static void destroyPolymorphic(T&,
|
||||
MemResourceBase*,
|
||||
const std::shared_ptr<PolymorphicHandlerBase>&)
|
||||
{
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<typename RTTI>
|
||||
using PointerOwnerBase =
|
||||
pointer_utils::PointerObjectExtensionBase<pointer_details::PtrOwnerManager,
|
||||
PolymorphicContext,
|
||||
RTTI>;
|
||||
|
||||
#endif //BITSERY_EXT_POINTER_H
|
||||
template<typename RTTI>
|
||||
using PointerObserverBase =
|
||||
pointer_utils::PointerObjectExtensionBase<pointer_details::PtrObserverManager,
|
||||
PolymorphicContext,
|
||||
RTTI>;
|
||||
|
||||
// inherit from PointerObjectExtensionBase in order to specify
|
||||
// PointerType::NotNull
|
||||
template<typename RTTI>
|
||||
class ReferencedByPointerBase
|
||||
: public pointer_utils::PointerObjectExtensionBase<
|
||||
pointer_details::NonPtrManager,
|
||||
PolymorphicContext,
|
||||
RTTI>
|
||||
{
|
||||
public:
|
||||
ReferencedByPointerBase()
|
||||
: pointer_utils::PointerObjectExtensionBase<pointer_details::NonPtrManager,
|
||||
PolymorphicContext,
|
||||
RTTI>(PointerType::NotNull)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using PointerOwner = PointerOwnerBase<StandardRTTI>;
|
||||
using PointerObserver = PointerObserverBase<StandardRTTI>;
|
||||
using ReferencedByPointer = ReferencedByPointerBase<StandardRTTI>;
|
||||
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
|
||||
template<typename T, typename RTTI>
|
||||
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>();
|
||||
};
|
||||
|
||||
template<typename T, typename RTTI>
|
||||
struct ExtensionTraits<ext::PointerObserverBase<RTTI>, T*>
|
||||
{
|
||||
// although pointer observer doesn't serialize anything, but we still add
|
||||
// value overload support to be consistent with pointer owners observer only
|
||||
// writes/reads pointer id from pointer linking context
|
||||
using TValue = T;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
|
||||
template<typename T, typename RTTI>
|
||||
struct ExtensionTraits<ext::ReferencedByPointerBase<RTTI>, T>
|
||||
{
|
||||
// allow everything, because it is serialized as regular type, except it also
|
||||
// creates pointerId that is required by NonOwningPointer to work
|
||||
using TValue = T;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // BITSERY_EXT_POINTER_H
|
||||
|
||||
67
include/bitsery/ext/std_atomic.h
Normal file
67
include/bitsery/ext/std_atomic.h
Normal file
@@ -0,0 +1,67 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 Nick Renieris
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_STD_ATOMIC_H
|
||||
#define BITSERY_EXT_STD_ATOMIC_H
|
||||
|
||||
#include "../traits/core/traits.h"
|
||||
#include <atomic>
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
|
||||
class StdAtomic
|
||||
{
|
||||
public:
|
||||
template<typename Ser, typename T, typename Fnc>
|
||||
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
|
||||
{
|
||||
T res{};
|
||||
fnc(des, res);
|
||||
obj.store(res);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdAtomic, std::atomic<T>>
|
||||
{
|
||||
using TValue = T;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = false;
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // BITSERY_EXT_STD_ATOMIC_H
|
||||
164
include/bitsery/ext/std_bitset.h
Normal file
164
include/bitsery/ext/std_bitset.h
Normal file
@@ -0,0 +1,164 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_STD_BITSET_H
|
||||
#define BITSERY_EXT_STD_BITSET_H
|
||||
|
||||
#include "../traits/core/traits.h"
|
||||
#include <bitset>
|
||||
#include <cstdint>
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
|
||||
class StdBitset
|
||||
{
|
||||
public:
|
||||
template<typename Ser, typename Fnc, size_t N>
|
||||
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);
|
||||
ser.value1b(static_cast<uint8_t>(data));
|
||||
}
|
||||
|
||||
} else {
|
||||
// more performant way
|
||||
auto data = obj.to_ullong();
|
||||
for (size_t i = 0u; i < BYTES; ++i) {
|
||||
ser.value1b(static_cast<uint8_t>(data & 0xFF));
|
||||
data >>= 8;
|
||||
}
|
||||
}
|
||||
if (LEFTOVER > 0) {
|
||||
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
|
||||
{
|
||||
constexpr size_t BYTES = N / 8;
|
||||
constexpr size_t LEFTOVER = N % 8;
|
||||
for (size_t i = 0u; i < BYTES; ++i) {
|
||||
size_t offset = i * 8;
|
||||
uint8_t data = 0;
|
||||
des.value1b(data);
|
||||
obj[offset + 0] = data & 0x01u;
|
||||
obj[offset + 1] = data & 0x02u;
|
||||
obj[offset + 2] = data & 0x04u;
|
||||
obj[offset + 3] = data & 0x08u;
|
||||
obj[offset + 4] = data & 0x10u;
|
||||
obj[offset + 5] = data & 0x20u;
|
||||
obj[offset + 6] = data & 0x40u;
|
||||
obj[offset + 7] = data & 0x80u;
|
||||
}
|
||||
if (LEFTOVER > 0) {
|
||||
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
|
||||
{
|
||||
auto data = 0;
|
||||
for (auto i = from; i < to; ++i) {
|
||||
data += obj[i] << (i - from);
|
||||
}
|
||||
w.template writeBytes<1>(static_cast<uint8_t>(data));
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
uint8_t data = 0u;
|
||||
r.template readBytes<1>(data);
|
||||
for (auto i = from; i < to; ++i) {
|
||||
obj[i] = data & (1u << (i - from));
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
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>>
|
||||
{
|
||||
using TValue = void;
|
||||
static constexpr bool SupportValueOverload = false;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BITSERY_EXT_STD_BITSET_H
|
||||
117
include/bitsery/ext/std_chrono.h
Normal file
117
include/bitsery/ext/std_chrono.h
Normal file
@@ -0,0 +1,117 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2019 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_STD_CHRONO_H
|
||||
#define BITSERY_EXT_STD_CHRONO_H
|
||||
|
||||
#include "../traits/core/traits.h"
|
||||
#include <chrono>
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
T res{};
|
||||
fnc(des, res);
|
||||
obj = std::chrono::duration<T, Period>{ res };
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
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 };
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename Rep, typename Period>
|
||||
struct ExtensionTraits<ext::StdDuration, std::chrono::duration<Rep, Period>>
|
||||
{
|
||||
using TValue = Rep;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = false;
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
|
||||
template<typename Clock, typename Rep, typename 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;
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // BITSERY_EXT_STD_CHRONO_H
|
||||
@@ -1,84 +1,122 @@
|
||||
//MIT License
|
||||
// MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_STD_MAP_H
|
||||
#define BITSERY_EXT_STD_MAP_H
|
||||
|
||||
#include "../details/serialization_common.h"
|
||||
#include "../traits/core/traits.h"
|
||||
#include "../details/adapter_utils.h"
|
||||
// we need this, so we could reserve for non ordered map
|
||||
#include <unordered_map>
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
namespace ext {
|
||||
|
||||
class StdMap {
|
||||
public:
|
||||
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
|
||||
{
|
||||
using TKey = typename T::key_type;
|
||||
using TValue = typename T::mapped_type;
|
||||
auto size = obj.size();
|
||||
assert(size <= _maxSize);
|
||||
details::writeSize(ser.adapter(), size);
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &, Writer &writer, const T &obj, Fnc &&fnc) const {
|
||||
using TKey = typename T::key_type;
|
||||
using TValue = typename T::mapped_type;
|
||||
auto size = obj.size();
|
||||
assert(size <= _maxSize);
|
||||
details::writeSize(writer, size);
|
||||
for (auto& v : obj)
|
||||
fnc(ser, const_cast<TKey&>(v.first), const_cast<TValue&>(v.second));
|
||||
}
|
||||
|
||||
for (auto &v:obj)
|
||||
fnc(const_cast<TKey &>(v.first), const_cast<TValue &>(v.second));
|
||||
}
|
||||
template<typename Des, typename T, typename Fnc>
|
||||
void deserialize(Des& des, T& obj, Fnc&& fnc) const
|
||||
{
|
||||
using TKey = typename T::key_type;
|
||||
using TValue = typename T::mapped_type;
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &, Reader &reader, 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,
|
||||
std::integral_constant<bool, Des::TConfig::CheckDataErrors>{});
|
||||
obj.clear();
|
||||
reserve(obj, size);
|
||||
|
||||
size_t size{};
|
||||
details::readSize(reader, size, _maxSize);
|
||||
auto hint = obj.begin();
|
||||
obj.clear();
|
||||
|
||||
for (auto i = 0u; i < size; ++i) {
|
||||
TKey key;
|
||||
TValue value;
|
||||
fnc(key, value);
|
||||
hint = obj.emplace_hint(hint, std::move(key), std::move(value));
|
||||
}
|
||||
}
|
||||
private:
|
||||
size_t _maxSize;
|
||||
};
|
||||
auto hint = obj.begin();
|
||||
for (auto i = 0u; i < size; ++i) {
|
||||
auto key = bitsery::Access::create<TKey>();
|
||||
auto value = bitsery::Access::create<TValue>();
|
||||
fnc(des, key, value);
|
||||
hint = obj.emplace_hint(hint, std::move(key), std::move(value));
|
||||
}
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdMap, T> {
|
||||
using TValue = void;
|
||||
static constexpr bool SupportValueOverload = false;
|
||||
static constexpr bool SupportObjectOverload = false;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
}
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
obj.reserve(size);
|
||||
}
|
||||
template<typename T>
|
||||
void reserve(T&, size_t) const
|
||||
{
|
||||
// for ordered container do nothing
|
||||
}
|
||||
size_t _maxSize;
|
||||
};
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdMap, T>
|
||||
{
|
||||
using TValue = void;
|
||||
static constexpr bool SupportValueOverload = false;
|
||||
static constexpr bool SupportObjectOverload = false;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //BITSERY_EXT_STD_MAP_H
|
||||
#endif // BITSERY_EXT_STD_MAP_H
|
||||
|
||||
@@ -1,104 +1,109 @@
|
||||
//MIT License
|
||||
// MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_STD_OPTIONAL_H
|
||||
#define BITSERY_EXT_STD_OPTIONAL_H
|
||||
|
||||
|
||||
//this module do not include optional, but expects it to be declared in std::optional
|
||||
//if you're using experimental optional from <experimental/optional>
|
||||
//add it in std namespace like this:
|
||||
//namespace std {
|
||||
// template <typename T>
|
||||
// using optional = experimental::optional<T>;
|
||||
//}
|
||||
#include <type_traits>
|
||||
#include "../details/serialization_common.h"
|
||||
#include "../traits/core/traits.h"
|
||||
#include <optional>
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
namespace ext {
|
||||
|
||||
template<typename T>
|
||||
using std_optional = ::std::optional<T>;
|
||||
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
|
||||
*/
|
||||
explicit StdOptional(bool alignBeforeData = true)
|
||||
: _alignBeforeData{ alignBeforeData }
|
||||
{
|
||||
}
|
||||
|
||||
class StdOptional {
|
||||
public:
|
||||
template<typename Ser, typename T, typename Fnc>
|
||||
void serialize(Ser& ser, const std::optional<T>& obj, Fnc&& fnc) const
|
||||
{
|
||||
ser.boolValue(static_cast<bool>(obj));
|
||||
if (_alignBeforeData)
|
||||
ser.adapter().align();
|
||||
if (obj)
|
||||
fnc(ser, const_cast<T&>(*obj));
|
||||
}
|
||||
|
||||
/**
|
||||
* Works with std::optional types
|
||||
* @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} {}
|
||||
template<typename T>
|
||||
constexpr void assertType() const {
|
||||
using TOpt = typename std::remove_cv<T>::type;
|
||||
using TVal = typename TOpt::value_type;
|
||||
static_assert(std::is_same<TOpt, std_optional<TVal>>(), "");
|
||||
static_assert(std::is_default_constructible<TVal>::value, "");
|
||||
};
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &ser, Writer &, const T &obj, Fnc &&fnc) const {
|
||||
assertType<T>();
|
||||
ser.boolValue(static_cast<bool>(obj));
|
||||
if (_alignBeforeData)
|
||||
ser.align();
|
||||
if (obj)
|
||||
fnc(const_cast<typename T::value_type & >(*obj));
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &des, Reader &, T &obj, Fnc &&fnc) const {
|
||||
assertType<T>();
|
||||
bool exists{};
|
||||
des.boolValue(exists);
|
||||
if (_alignBeforeData)
|
||||
des.align();
|
||||
if (exists) {
|
||||
typename T::value_type tmp{};
|
||||
fnc(tmp);
|
||||
obj = tmp;
|
||||
} else {
|
||||
//experimental optional doesnt have .reset method
|
||||
obj = T{};
|
||||
}
|
||||
}
|
||||
private:
|
||||
bool _alignBeforeData;
|
||||
};
|
||||
template<typename Des, typename T, typename Fnc>
|
||||
void deserialize(Des& des, std::optional<T>& obj, Fnc&& fnc) const
|
||||
{
|
||||
bool exists{};
|
||||
des.boolValue(exists);
|
||||
if (_alignBeforeData)
|
||||
des.adapter().align();
|
||||
if (exists) {
|
||||
deserialize_impl(des, obj, fnc, std::is_trivial<T>{});
|
||||
} else {
|
||||
obj = std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdOptional, T> {
|
||||
using TValue = typename T::value_type;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
private:
|
||||
template<typename Des, typename T, typename Fnc>
|
||||
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
|
||||
{
|
||||
if (!obj) {
|
||||
obj = ::bitsery::Access::create<T>();
|
||||
}
|
||||
fnc(des, *obj);
|
||||
}
|
||||
bool _alignBeforeData;
|
||||
};
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdOptional, std::optional<T>>
|
||||
{
|
||||
using TValue = T;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //BITSERY_EXT_STD_OPTIONAL_H
|
||||
#endif // BITSERY_EXT_STD_OPTIONAL_H
|
||||
|
||||
@@ -1,111 +1,127 @@
|
||||
//MIT License
|
||||
// MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_STD_QUEUE_H
|
||||
#define BITSERY_EXT_STD_QUEUE_H
|
||||
|
||||
#include <type_traits>
|
||||
#include <queue>
|
||||
//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 <queue>
|
||||
#include <type_traits>
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
namespace ext {
|
||||
|
||||
class StdQueue {
|
||||
private:
|
||||
//inherit from queue so we could take underlying container
|
||||
template <typename T, typename C>
|
||||
struct QueueCnt : public std::queue<T, C>
|
||||
{
|
||||
static const C& getContainer(const std::queue<T, C>& s )
|
||||
{
|
||||
//get address of underlying container
|
||||
return s.*(&QueueCnt::c);
|
||||
}
|
||||
static C& getContainer(std::queue<T, C>& s )
|
||||
{
|
||||
//get address of underlying container
|
||||
return s.*(&QueueCnt::c);
|
||||
}
|
||||
};
|
||||
//inherit from queue so we could take underlying container
|
||||
template <typename T, typename C>
|
||||
struct PriorityQueueCnt : public std::priority_queue<T, C>
|
||||
{
|
||||
static const C& getContainer(const std::priority_queue<T, C>& s )
|
||||
{
|
||||
//get address of underlying container
|
||||
return s.*(&PriorityQueueCnt::c);
|
||||
}
|
||||
static C& getContainer(std::priority_queue<T, C>& s )
|
||||
{
|
||||
//get address of underlying container
|
||||
return s.*(&PriorityQueueCnt::c);
|
||||
}
|
||||
};
|
||||
|
||||
size_t _maxSize;
|
||||
public:
|
||||
explicit StdQueue(size_t maxSize):_maxSize{maxSize} {};
|
||||
|
||||
//for queue
|
||||
template<typename Ser, typename Writer, typename T, typename C, typename Fnc>
|
||||
void serialize(Ser &ser, Writer &, 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 Reader, typename T, typename C, typename Fnc>
|
||||
void deserialize(Des &des, Reader &, 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 Writer, typename T, typename C, typename Fnc>
|
||||
void serialize(Ser &ser, Writer &, const std::priority_queue<T,C> &obj, Fnc &&fnc) const {
|
||||
ser.container(PriorityQueueCnt<T,C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename C, typename Fnc>
|
||||
void deserialize(Des &des, Reader &, std::priority_queue<T,C> &obj, Fnc &&fnc) const {
|
||||
des.container(PriorityQueueCnt<T,C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
};
|
||||
class StdQueue
|
||||
{
|
||||
private:
|
||||
// inherit from queue so we could take underlying container
|
||||
template<typename T, typename C>
|
||||
struct QueueCnt : public std::queue<T, C>
|
||||
{
|
||||
static const C& getContainer(const std::queue<T, C>& s)
|
||||
{
|
||||
// get address of underlying container
|
||||
return s.*(&QueueCnt::c);
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdQueue, T> {
|
||||
using TValue = typename T::value_type;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
static C& getContainer(std::queue<T, C>& s)
|
||||
{
|
||||
// get address of underlying container
|
||||
return s.*(&QueueCnt::c);
|
||||
}
|
||||
};
|
||||
// inherit from queue so we could take underlying container
|
||||
template<typename T, typename Seq, typename Cmp>
|
||||
struct PriorityQueueCnt : public std::priority_queue<T, Seq, Cmp>
|
||||
{
|
||||
static const Seq& getContainer(const std::priority_queue<T, Seq, Cmp>& s)
|
||||
{
|
||||
// get address of underlying container
|
||||
return s.*(&PriorityQueueCnt::c);
|
||||
}
|
||||
static Seq& getContainer(std::priority_queue<T, Seq, Cmp>& s)
|
||||
{
|
||||
// get address of underlying container
|
||||
return s.*(&PriorityQueueCnt::c);
|
||||
}
|
||||
};
|
||||
|
||||
size_t _maxSize;
|
||||
|
||||
public:
|
||||
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));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdQueue, T>
|
||||
{
|
||||
using TValue = typename T::value_type;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //BITSERY_EXT_STD_QUEUE_H
|
||||
#endif // BITSERY_EXT_STD_QUEUE_H
|
||||
|
||||
@@ -1,98 +1,109 @@
|
||||
//MIT License
|
||||
// MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_STD_SET_H
|
||||
#define BITSERY_EXT_STD_SET_H
|
||||
|
||||
#include <cassert>
|
||||
#include "../details/adapter_utils.h"
|
||||
//we need this, so we could
|
||||
#include "../details/serialization_common.h"
|
||||
#include <unordered_set>
|
||||
#include "../traits/core/traits.h"
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
namespace ext {
|
||||
|
||||
class StdSet {
|
||||
public:
|
||||
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
|
||||
{
|
||||
using TKey = typename T::key_type;
|
||||
auto size = obj.size();
|
||||
assert(size <= _maxSize);
|
||||
details::writeSize(ser.adapter(), size);
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &, Writer &writer, const T &obj, Fnc &&fnc) const {
|
||||
using TKey = typename T::key_type;
|
||||
auto size = obj.size();
|
||||
assert(size <= _maxSize);
|
||||
details::writeSize(writer, size);
|
||||
for (auto& v : obj)
|
||||
fnc(ser, const_cast<TKey&>(v));
|
||||
}
|
||||
|
||||
for (auto &v:obj)
|
||||
fnc(const_cast<TKey &>(v));
|
||||
}
|
||||
template<typename Des, typename T, typename Fnc>
|
||||
void deserialize(Des& des, T& obj, Fnc&& fnc) const
|
||||
{
|
||||
using TKey = typename T::key_type;
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &, Reader &reader, T &obj, Fnc &&fnc) const {
|
||||
using TKey = typename T::key_type;
|
||||
|
||||
size_t size{};
|
||||
details::readSize(reader, size, _maxSize);
|
||||
auto hint = obj.begin();
|
||||
obj.clear();
|
||||
reserve(obj, size);
|
||||
|
||||
for (auto i = 0u; i < size; ++i) {
|
||||
TKey key;
|
||||
fnc(key);
|
||||
hint = obj.emplace_hint(hint, std::move(key));
|
||||
}
|
||||
}
|
||||
private:
|
||||
|
||||
template <typename T>
|
||||
void reserve(std::unordered_set<T>& obj, size_t size) const {
|
||||
obj.reserve(size);
|
||||
}
|
||||
template <typename T>
|
||||
void reserve(std::unordered_multiset<T>& obj, size_t size) const {
|
||||
obj.reserve(size);
|
||||
}
|
||||
template <typename T>
|
||||
void reserve(T& , size_t ) const {
|
||||
//for ordered container do nothing
|
||||
}
|
||||
size_t _maxSize;
|
||||
};
|
||||
size_t size{};
|
||||
details::readSize(
|
||||
des.adapter(),
|
||||
size,
|
||||
_maxSize,
|
||||
std::integral_constant<bool, Des::TConfig::CheckDataErrors>{});
|
||||
obj.clear();
|
||||
reserve(obj, size);
|
||||
auto hint = obj.begin();
|
||||
for (auto i = 0u; i < size; ++i) {
|
||||
auto key = bitsery::Access::create<TKey>();
|
||||
fnc(des, key);
|
||||
hint = obj.emplace_hint(hint, std::move(key));
|
||||
}
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdSet, T> {
|
||||
using TValue = typename T::key_type;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
}
|
||||
private:
|
||||
template<typename Key, typename Hash, typename KeyEqual, typename Allocator>
|
||||
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
|
||||
{
|
||||
obj.reserve(size);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void reserve(T&, size_t) const
|
||||
{
|
||||
// for ordered container do nothing
|
||||
}
|
||||
size_t _maxSize;
|
||||
};
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdSet, T>
|
||||
{
|
||||
using TValue = typename T::key_type;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //BITSERY_EXT_STD_SET_H
|
||||
#endif // BITSERY_EXT_STD_SET_H
|
||||
|
||||
256
include/bitsery/ext/std_smart_ptr.h
Normal file
256
include/bitsery/ext/std_smart_ptr.h
Normal file
@@ -0,0 +1,256 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2018 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_STD_SMART_PTR_H
|
||||
#define BITSERY_EXT_STD_SMART_PTR_H
|
||||
|
||||
#include "../traits/core/traits.h"
|
||||
#include "utils/pointer_utils.h"
|
||||
#include "utils/polymorphism_utils.h"
|
||||
#include "utils/rtti_utils.h"
|
||||
#include <memory>
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
|
||||
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
|
||||
{
|
||||
std::shared_ptr<void> obj{};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct SmartPtrOwnerManager
|
||||
{
|
||||
|
||||
using TElement = typename T::element_type;
|
||||
|
||||
template<typename TDeleter>
|
||||
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::weak_ptr<TElement>& obj)
|
||||
{
|
||||
if (auto ptr = obj.lock())
|
||||
return ptr.get();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static constexpr PointerOwnershipType getOwnership()
|
||||
{
|
||||
return ::bitsery::details::IsSpecializationOf<T, std::unique_ptr>::value
|
||||
? PointerOwnershipType::Owner
|
||||
: std::is_same<std::shared_ptr<TElement>, T>::value
|
||||
? PointerOwnershipType::SharedOwner
|
||||
: PointerOwnershipType::SharedObserver;
|
||||
}
|
||||
|
||||
template<typename TDeleter>
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
auto ptr = obj.release();
|
||||
handler->destroy(alloc, ptr);
|
||||
}
|
||||
|
||||
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>&)
|
||||
{
|
||||
obj.reset();
|
||||
}
|
||||
|
||||
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>&)
|
||||
{
|
||||
obj.reset();
|
||||
}
|
||||
|
||||
// define a type that will store shared state for shared and weak ptrs
|
||||
using TSharedState = SharedPtrSharedState;
|
||||
|
||||
static void createShared(TSharedState& state,
|
||||
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));
|
||||
state.obj = obj;
|
||||
state.typeId = typeId;
|
||||
}
|
||||
|
||||
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));
|
||||
state.obj = obj;
|
||||
state.typeId = handler->getDerivedTypeId();
|
||||
}
|
||||
|
||||
static void createShared(TSharedState& state,
|
||||
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));
|
||||
obj = res;
|
||||
state.obj = res;
|
||||
state.typeId = typeId;
|
||||
}
|
||||
|
||||
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));
|
||||
obj = res;
|
||||
state.obj = res;
|
||||
state.typeId = handler->getDerivedTypeId();
|
||||
}
|
||||
|
||||
static void saveToSharedState(TSharedState& state, T& obj)
|
||||
{
|
||||
state.obj = std::shared_ptr<TElement>(obj);
|
||||
}
|
||||
|
||||
static void saveToSharedStatePolymorphic(TSharedState& state, T& obj)
|
||||
{
|
||||
state.obj = std::shared_ptr<TElement>(obj);
|
||||
}
|
||||
|
||||
static void loadFromSharedState(TSharedState& state, T& obj)
|
||||
{
|
||||
auto v = state.obj.get();
|
||||
auto p = static_cast<TElement*>(v);
|
||||
obj = std::shared_ptr<TElement>(state.obj, p);
|
||||
}
|
||||
|
||||
static void loadFromSharedStatePolymorphic(TSharedState& state,
|
||||
T& obj,
|
||||
const PolymorphicHandlerBase&)
|
||||
{
|
||||
auto v = state.obj.get();
|
||||
auto p = static_cast<TElement*>(v);
|
||||
obj = std::shared_ptr<TElement>(state.obj, p);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template<typename RTTI>
|
||||
using StdSmartPtrBase = pointer_utils::PointerObjectExtensionBase<
|
||||
smart_ptr_details::SmartPtrOwnerManager,
|
||||
PolymorphicContext,
|
||||
RTTI>;
|
||||
|
||||
// helper type for convienience
|
||||
using StdSmartPtr = StdSmartPtrBase<StandardRTTI>;
|
||||
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
|
||||
template<typename T, typename RTTI>
|
||||
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>();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // BITSERY_EXT_STD_SMART_PTR_H
|
||||
@@ -1,82 +1,85 @@
|
||||
//MIT License
|
||||
// MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_STD_STACK_H
|
||||
#define BITSERY_EXT_STD_STACK_H
|
||||
|
||||
#include <type_traits>
|
||||
#include <stack>
|
||||
//include type traits for deque, because stack default underlying container is deque
|
||||
#include "../traits/deque.h"
|
||||
#include <stack>
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
namespace ext {
|
||||
|
||||
class StdStack {
|
||||
private:
|
||||
//inherit from stack so we could take underlying container
|
||||
template <typename T, typename C>
|
||||
struct StackCnt : public std::stack<T, C>
|
||||
{
|
||||
static const C& getContainer(const std::stack<T, C>& s )
|
||||
{
|
||||
//get address of underlying container
|
||||
return s.*(&StackCnt::c);
|
||||
}
|
||||
static C& getContainer(std::stack<T, C>& s )
|
||||
{
|
||||
//get address of underlying container
|
||||
return s.*(&StackCnt::c);
|
||||
}
|
||||
};
|
||||
size_t _maxSize;
|
||||
public:
|
||||
explicit StdStack(size_t maxSize):_maxSize{maxSize} {};
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename C, typename Fnc>
|
||||
void serialize(Ser &ser, Writer &, 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 Reader, typename T, typename C, typename Fnc>
|
||||
void deserialize(Des &des, Reader &, std::stack<T,C> &obj, Fnc &&fnc) const {
|
||||
des.container(StackCnt<T,C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
};
|
||||
class StdStack
|
||||
{
|
||||
private:
|
||||
// inherit from stack so we could take underlying container
|
||||
template<typename T, typename C>
|
||||
struct StackCnt : public std::stack<T, C>
|
||||
{
|
||||
static const C& getContainer(const std::stack<T, C>& s)
|
||||
{
|
||||
// get address of underlying container
|
||||
return s.*(&StackCnt::c);
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdStack, T> {
|
||||
using TValue = typename T::value_type;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
static C& getContainer(std::stack<T, C>& s)
|
||||
{
|
||||
// get address of underlying container
|
||||
return s.*(&StackCnt::c);
|
||||
}
|
||||
};
|
||||
size_t _maxSize;
|
||||
|
||||
public:
|
||||
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));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename T, typename Seq>
|
||||
struct ExtensionTraits<ext::StdStack, std::stack<T, Seq>>
|
||||
{
|
||||
using TValue = T;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //BITSERY_EXT_STD_STACK_H
|
||||
#endif // BITSERY_EXT_STD_STACK_H
|
||||
|
||||
83
include/bitsery/ext/std_tuple.h
Normal file
83
include/bitsery/ext/std_tuple.h
Normal file
@@ -0,0 +1,83 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2019 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_STD_TUPLE_H
|
||||
#define BITSERY_EXT_STD_TUPLE_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...>
|
||||
{
|
||||
public:
|
||||
template<typename Ser, typename Fnc, typename... Ts>
|
||||
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
|
||||
{
|
||||
serializeAll(des, obj);
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename S, typename... Ts>
|
||||
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));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// deduction guide
|
||||
template<typename... Overloads>
|
||||
StdTuple(Overloads...) -> StdTuple<Overloads...>;
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
|
||||
template<typename Tuple, typename... Overloads>
|
||||
struct ExtensionTraits<ext::StdTuple<Overloads...>, Tuple>
|
||||
{
|
||||
static_assert(bitsery::details::IsSpecializationOf<Tuple, std::tuple>::value,
|
||||
"StdTuple only works with std::tuple");
|
||||
using TValue = void;
|
||||
static constexpr bool SupportValueOverload = false;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // BITSERY_EXT_STD_TUPLE_H
|
||||
114
include/bitsery/ext/std_variant.h
Normal file
114
include/bitsery/ext/std_variant.h
Normal file
@@ -0,0 +1,114 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2019 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_STD_VARIANT_H
|
||||
#define BITSERY_EXT_STD_VARIANT_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...>
|
||||
{
|
||||
public:
|
||||
template<typename Ser, typename Fnc, typename... Ts>
|
||||
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) {
|
||||
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
|
||||
{
|
||||
size_t index{};
|
||||
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;
|
||||
|
||||
// Reinitializing nontrivial types may be expensive especially when they
|
||||
// reference heap data, so if `data` is already holding the requested
|
||||
// variant then we'll deserialize into the existing object
|
||||
if constexpr (!std::is_trivial_v<TElem>) {
|
||||
if (auto item = std::get_if<Index>(&data)) {
|
||||
this->serializeType(des, *item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TElem item = ::bitsery::Access::create<TElem>();
|
||||
this->serializeType(des, item);
|
||||
data =
|
||||
std::variant<Ts...>(std::in_place_index_t<Index>{}, std::move(item));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// deduction guide
|
||||
template<typename... Overloads>
|
||||
StdVariant(Overloads...) -> StdVariant<Overloads...>;
|
||||
}
|
||||
|
||||
// defines empty fuction, that handles monostate
|
||||
template<typename S>
|
||||
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,
|
||||
"StdVariant only works with std::variant");
|
||||
using TValue = void;
|
||||
static constexpr bool SupportValueOverload = false;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // BITSERY_EXT_STD_VARIANT_H
|
||||
159
include/bitsery/ext/utils/composite_type_overloads.h
Normal file
159
include/bitsery/ext/utils/composite_type_overloads.h
Normal file
@@ -0,0 +1,159 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2019 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_COMPOSITE_TYPE_OVERLOADS_H
|
||||
#define BITSERY_EXT_COMPOSITE_TYPE_OVERLOADS_H
|
||||
|
||||
#include "../../details/serialization_common.h"
|
||||
#include <functional>
|
||||
|
||||
#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
|
||||
#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
|
||||
template<class... Ts>
|
||||
struct CompositeTypeOverloads : Ts...
|
||||
{
|
||||
using Ts::operator()...;
|
||||
};
|
||||
|
||||
template<typename... Overloads>
|
||||
CompositeTypeOverloads(Overloads...) -> CompositeTypeOverloads<Overloads...>;
|
||||
|
||||
// convenient way to invoke s.value<N>, shorter than specifying a lambda
|
||||
template<typename T, size_t N>
|
||||
struct OverloadValue
|
||||
{
|
||||
template<typename S>
|
||||
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
|
||||
|
||||
template<typename T, size_t N, typename Ext>
|
||||
struct OverloadExtValue : public Ext
|
||||
{
|
||||
template<typename S>
|
||||
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
|
||||
{
|
||||
template<typename S>
|
||||
void operator()(S& s, T& v) const
|
||||
{
|
||||
s.ext(v, static_cast<const Ext&>(*this));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace details {
|
||||
|
||||
template<template<typename...> typename CompositeType, typename... 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...>{});
|
||||
}
|
||||
|
||||
// call lambda for all indexes in composite type
|
||||
template<typename Fnc, typename... Ts>
|
||||
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
|
||||
{
|
||||
// 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(),
|
||||
"Please define overload or 'serialize' function for your type.");
|
||||
s.object(v);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename S, typename T>
|
||||
static constexpr bool hasOverload()
|
||||
{
|
||||
return std::is_invocable<ext::CompositeTypeOverloads<Overloads...>,
|
||||
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),
|
||||
...);
|
||||
}
|
||||
|
||||
template<typename Variant, typename Fnc, size_t... Is>
|
||||
void execAllImpl(Variant& obj, Fnc&& fnc, std::index_sequence<Is...>) const
|
||||
{
|
||||
(fnc(obj, std::integral_constant<size_t, Is>{}), ...);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BITSERY_EXT_COMPOSITE_TYPE_OVERLOADS_H
|
||||
196
include/bitsery/ext/utils/memory_resource.h
Normal file
196
include/bitsery/ext/utils/memory_resource.h
Normal file
@@ -0,0 +1,196 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2018 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_MEMORY_RESOURCE_H
|
||||
#define BITSERY_EXT_MEMORY_RESOURCE_H
|
||||
|
||||
#include "../../details/serialization_common.h"
|
||||
#include <new>
|
||||
|
||||
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).
|
||||
|
||||
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 ~MemResourceBase() noexcept = default;
|
||||
};
|
||||
|
||||
// default implementation for MemResourceBase using new and delete
|
||||
class MemResourceNewDelete final : public MemResourceBase
|
||||
{
|
||||
public:
|
||||
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
|
||||
{
|
||||
(::operator delete(ptr));
|
||||
}
|
||||
|
||||
~MemResourceNewDelete() noexcept final = default;
|
||||
};
|
||||
|
||||
// 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
|
||||
{
|
||||
public:
|
||||
constexpr PolyAllocWithTypeId(MemResourceBase* memResource = nullptr)
|
||||
: _resource{ memResource }
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
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
|
||||
? _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
|
||||
{
|
||||
const auto bytes = sizeof(T) * n;
|
||||
constexpr auto alignment = std::alignment_of<T>::value;
|
||||
_resource
|
||||
? _resource->deallocate(ptr, bytes, alignment, typeId)
|
||||
: ext::MemResourceNewDelete{}.deallocate(ptr, bytes, alignment, typeId);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
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
|
||||
{
|
||||
obj->~T();
|
||||
deallocate(obj, 1, typeId);
|
||||
}
|
||||
|
||||
void setMemResource(ext::MemResourceBase* resource) { _resource = resource; }
|
||||
|
||||
ext::MemResourceBase* getMemResource() const { return _resource; }
|
||||
|
||||
bool operator==(const PolyAllocWithTypeId& rhs) const noexcept
|
||||
{
|
||||
return _resource == rhs._resource;
|
||||
}
|
||||
|
||||
bool operator!=(const PolyAllocWithTypeId& rhs) const noexcept
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
ext::MemResourceBase* _resource;
|
||||
};
|
||||
|
||||
// this is very similar to c++17 PolymorphicAllocator
|
||||
// it just wraps our PolyAllocWithTypeId and pass 0 as typeId
|
||||
// and defines core functions for c++ Allocator concept,
|
||||
template<class T>
|
||||
class StdPolyAlloc
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
explicit constexpr StdPolyAlloc(MemResourceBase* memResource)
|
||||
: _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 }
|
||||
{
|
||||
}
|
||||
|
||||
T* allocate(std::size_t n) { return _alloc.allocate<T>(n, 0); }
|
||||
|
||||
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
|
||||
{
|
||||
return lhs._alloc == rhs._alloc;
|
||||
}
|
||||
|
||||
template<class U>
|
||||
friend bool operator!=(const StdPolyAlloc<T>& lhs,
|
||||
const StdPolyAlloc<U>& rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
PolyAllocWithTypeId _alloc;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif // BITSERY_EXT_MEMORY_RESOURCE_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,225 +1,453 @@
|
||||
// MIT License
|
||||
//
|
||||
// Created by fraillt on 17.11.3.
|
||||
// Copyright (c) 2018 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_POLYMORPHISM_H
|
||||
#define BITSERY_EXT_POLYMORPHISM_H
|
||||
#ifndef BITSERY_EXT_POLYMORPHISM_UTILS_H
|
||||
#define BITSERY_EXT_POLYMORPHISM_UTILS_H
|
||||
|
||||
#include "../inheritance.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include "memory_resource.h"
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace ext {
|
||||
namespace details_polymorphism {
|
||||
//stores template types
|
||||
template<typename ...>
|
||||
struct List {
|
||||
};
|
||||
}
|
||||
namespace ext {
|
||||
|
||||
//specialize for your base class by deriving from DerivedClasses with list of derivatives that DIRECTLY inherits from your base class.
|
||||
//e.g.
|
||||
// template <> PolymorphicBase<Animal>: DerivedClasses<Dog, Cat>{};
|
||||
// template <> PolymorphicBase<Dog>: DerivedClasses<Bulldog, GoldenRetriever> {};
|
||||
// IMPORTANT !!!
|
||||
// although you can add all derivates to same base like this:
|
||||
// SuperClass<Animal>:DerivedClasses<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 {
|
||||
using childs = details_polymorphism::List<>;
|
||||
};
|
||||
// helper type, that contains list of types
|
||||
template<typename...>
|
||||
struct PolymorphicClassesList
|
||||
{};
|
||||
|
||||
//derive from this class when specifying childs for your base class, atleast one child must exists, hence T1
|
||||
//e.g.
|
||||
// template <> SuperClass<Animal>: DerivedClasses<Dog, Cat>{};
|
||||
template<typename T1, typename ... Tn>
|
||||
struct DerivedClasses {
|
||||
using childs = details_polymorphism::List<T1, Tn...>;
|
||||
};
|
||||
// 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 !!!
|
||||
// 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<typename TBase>
|
||||
struct PolymorphicBaseClass
|
||||
{
|
||||
using Childs = PolymorphicClassesList<>;
|
||||
};
|
||||
|
||||
namespace utils {
|
||||
//this object will be used to serialize/deserialize polymorphic type id within inheritance tree from base class
|
||||
struct InheritanceTreeTypeId {
|
||||
uint16_t depth{};
|
||||
uint16_t index{};
|
||||
template <typename S>
|
||||
void serialize(S& s) {
|
||||
s.value2b(depth);
|
||||
s.value2b(index);
|
||||
}
|
||||
};
|
||||
// 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
|
||||
{
|
||||
using Childs = PolymorphicClassesList<T1, Tn...>;
|
||||
};
|
||||
|
||||
template <typename S, typename TObject>
|
||||
struct PolymorphicObjectHandlerInterface {
|
||||
virtual void process(S& s, TObject& obj) const = 0;
|
||||
virtual void create(TObject& obj) const = 0;
|
||||
virtual ~PolymorphicObjectHandlerInterface() = default;
|
||||
};
|
||||
}
|
||||
class PolymorphicHandlerBase
|
||||
{
|
||||
public:
|
||||
virtual void* create(
|
||||
const pointer_utils::PolyAllocWithTypeId& alloc) const = 0;
|
||||
|
||||
namespace details_polymorphism {
|
||||
virtual void destroy(const pointer_utils::PolyAllocWithTypeId& alloc,
|
||||
void* ptr) const = 0;
|
||||
|
||||
virtual void process(void* ser, void* obj) const = 0;
|
||||
|
||||
template<typename S, typename TObject, typename TDerived, typename RTTI, typename ObjectHandler>
|
||||
struct PolymorphicObjectHandlerBase : public utils::PolymorphicObjectHandlerInterface<S, TObject> {
|
||||
void process(S &s, TObject &obj) const final {
|
||||
s.object(dynamic_cast<TDerived &>(*_handler.getPtr(obj)));
|
||||
};
|
||||
virtual void* getRootPtr(const void* obj) const = 0;
|
||||
|
||||
void create(TObject &obj) const final {
|
||||
auto ptr = _handler.getPtr(obj);
|
||||
if (ptr && RTTI::get(*ptr) != RTTI::template get<TDerived>()) {
|
||||
_handler.destroy(obj);
|
||||
_handler.template create<TDerived>(obj);
|
||||
} else {
|
||||
if (ptr == nullptr)
|
||||
_handler.template create<TDerived>(obj);
|
||||
}
|
||||
}
|
||||
ObjectHandler _handler{};
|
||||
};
|
||||
virtual void* fromDerivedToBasePtr(void* obj) const = 0;
|
||||
|
||||
template<typename S, typename TObject, template<typename> class TObjectManager>
|
||||
class PolymorphicHandlersGenerator {
|
||||
public:
|
||||
template<typename Fnc>
|
||||
static void generate(Fnc &&addHandlerFnc) {
|
||||
PolymorphicHandlersGenerator tmp{std::forward<Fnc>(addHandlerFnc)};
|
||||
}
|
||||
virtual size_t getDerivedTypeId() const = 0;
|
||||
|
||||
private:
|
||||
using RTTI = typename TObjectManager<TObject>::RTTI;
|
||||
virtual ~PolymorphicHandlerBase() = default;
|
||||
};
|
||||
|
||||
template <typename TDerived>
|
||||
using TPolymorphicObjectHandler = PolymorphicObjectHandlerBase<S, TObject, TDerived, RTTI, typename TObjectManager<TObject>::THandler>;
|
||||
template<typename RTTI,
|
||||
typename TSerializer,
|
||||
typename TRoot,
|
||||
typename TBase,
|
||||
typename TDerived>
|
||||
class PolymorphicHandler : public PolymorphicHandlerBase
|
||||
{
|
||||
public:
|
||||
void* create(const pointer_utils::PolyAllocWithTypeId& alloc) const final
|
||||
{
|
||||
return toBase(alloc.newObject<TDerived>(RTTI::template get<TDerived>()));
|
||||
}
|
||||
|
||||
template<typename Fnc>
|
||||
explicit PolymorphicHandlersGenerator(Fnc &&addHandlerFnc):_addHandler(std::forward<Fnc>(addHandlerFnc)) {
|
||||
//fill inheritance tree
|
||||
using TBase = typename TObjectManager<TObject>::TValue;
|
||||
add<TBase>();
|
||||
}
|
||||
void destroy(const pointer_utils::PolyAllocWithTypeId& alloc,
|
||||
void* ptr) const final
|
||||
{
|
||||
alloc.deleteObject<TDerived>(fromBase(ptr), RTTI::template get<TDerived>());
|
||||
}
|
||||
|
||||
template<typename TClass>
|
||||
void add() {
|
||||
addClass<TClass>();
|
||||
//save current index and increase depth
|
||||
auto saveIndex = index;
|
||||
index = 0;
|
||||
depth++;
|
||||
addChilds<TClass>(typename PolymorphicBaseClass<TClass>::childs{});
|
||||
//restore index and depth
|
||||
depth--;
|
||||
index = saveIndex;
|
||||
}
|
||||
void process(void* ser, void* obj) const final
|
||||
{
|
||||
static_cast<TSerializer*>(ser)->object(*fromBase(obj));
|
||||
}
|
||||
|
||||
template<typename TClassBase, typename T1, typename ... Tn>
|
||||
void addChilds(details_polymorphism::List<T1, Tn...>) {
|
||||
static_assert(std::is_base_of<TClassBase, T1>::value,
|
||||
"PolymorphicBaseClass<TBase> must derive a list of derived classes from TBase.");
|
||||
add<T1>();
|
||||
addChilds<TClassBase>(details_polymorphism::List<Tn...>{});
|
||||
}
|
||||
void* getRootPtr(const void* obj) const final
|
||||
{
|
||||
return RTTI::template cast<TBase, TRoot>(
|
||||
static_cast<TBase*>(const_cast<void*>(obj)));
|
||||
}
|
||||
|
||||
template<typename>
|
||||
void addChilds(details_polymorphism::List<>) {
|
||||
}
|
||||
void* fromDerivedToBasePtr(void* obj) const final { return toBase(obj); }
|
||||
|
||||
size_t getDerivedTypeId() const final
|
||||
{
|
||||
return RTTI::template get<TDerived>();
|
||||
}
|
||||
|
||||
template<typename TClass>
|
||||
void addClass() {
|
||||
if (!std::is_abstract<TClass>::value) {
|
||||
utils::InheritanceTreeTypeId tid{};
|
||||
tid.index = index;
|
||||
tid.depth = depth;
|
||||
#if __cplusplus > 201103L
|
||||
auto handler = std::make_unique<TPolymorphicObjectHandler<TClass>>();
|
||||
#else
|
||||
auto handler = std::unique_ptr<TPolymorphicObjectHandler<TClass>>(
|
||||
new TPolymorphicObjectHandler<TClass>{});
|
||||
#endif
|
||||
if (_addHandler(RTTI::template get<TClass>(), tid, std::move(handler)))
|
||||
++index;
|
||||
}
|
||||
}
|
||||
private:
|
||||
TDerived* fromBase(void* obj) const
|
||||
{
|
||||
return RTTI::template cast<TBase, TDerived>(static_cast<TBase*>(obj));
|
||||
}
|
||||
|
||||
std::function<bool(size_t, utils::InheritanceTreeTypeId,
|
||||
std::unique_ptr<utils::PolymorphicObjectHandlerInterface<S, TObject>> &&)> _addHandler;
|
||||
uint16_t depth{};
|
||||
uint16_t index{};
|
||||
};
|
||||
TBase* toBase(void* obj) const
|
||||
{
|
||||
return RTTI::template cast<TDerived, TBase>(static_cast<TDerived*>(obj));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
// Even though we don't serialize/deserialize abstract classes
|
||||
// object might still be accessed through abstract class, hence we need this
|
||||
// for type information
|
||||
template<typename RTTI, typename TRoot, typename TBase, typename TDerived>
|
||||
class AbstractPolymorphicHandler : public PolymorphicHandlerBase
|
||||
{
|
||||
public:
|
||||
void* create(const pointer_utils::PolyAllocWithTypeId&) const
|
||||
{
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
namespace utils {
|
||||
void destroy(const pointer_utils::PolyAllocWithTypeId&, void*) const
|
||||
{
|
||||
assert(false);
|
||||
};
|
||||
|
||||
template<typename Ser, typename TObject, template<typename> class TObjectManager>
|
||||
class InheritanceTreeSerialize {
|
||||
public:
|
||||
void process(void*, void*) const { assert(false); }
|
||||
|
||||
InheritanceTreeSerialize() {
|
||||
details_polymorphism::PolymorphicHandlersGenerator<Ser, TObject, TObjectManager>::generate(
|
||||
[this](size_t typeHash, InheritanceTreeTypeId id,
|
||||
std::unique_ptr<PolymorphicObjectHandlerInterface<Ser, TObject>> &&handler) {
|
||||
return _map.emplace(std::make_pair(typeHash, std::make_pair(id, std::move(handler)))).second;
|
||||
}
|
||||
);
|
||||
void* getRootPtr(const void*) const
|
||||
{
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
void* fromDerivedToBasePtr(void*) const final
|
||||
{
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::pair<InheritanceTreeTypeId, PolymorphicObjectHandlerInterface<Ser, TObject>*> getHandler(const TObject &obj) {
|
||||
using RTTI = typename TObjectManager<TObject>::RTTI;
|
||||
auto handler = typename TObjectManager<TObject>::THandler{};
|
||||
auto it = _map.find(RTTI::get(*handler.getPtr(obj)));
|
||||
if (it != _map.end()) {
|
||||
return std::make_pair(it->second.first, it->second.second.get());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
size_t getDerivedTypeId() const { return RTTI::template get<TDerived>(); };
|
||||
};
|
||||
|
||||
private:
|
||||
std::unordered_map<size_t, std::pair<InheritanceTreeTypeId, std::unique_ptr<PolymorphicObjectHandlerInterface<Ser, TObject>>>> _map{};
|
||||
};
|
||||
|
||||
|
||||
template<typename Des, typename TObject, template<typename> class TObjectManager>
|
||||
class InheritanceTreeDeserialize {
|
||||
public:
|
||||
InheritanceTreeDeserialize() {
|
||||
details_polymorphism::PolymorphicHandlersGenerator<Des, TObject, TObjectManager>::generate(
|
||||
[this](size_t, InheritanceTreeTypeId id, std::unique_ptr<PolymorphicObjectHandlerInterface<Des, TObject>> &&handler) {
|
||||
return _map.emplace(std::make_pair(getHashFromId(id), std::move(handler))).second;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const PolymorphicObjectHandlerInterface<Des, TObject> *getHandler(const InheritanceTreeTypeId &id) {
|
||||
auto it = _map.find(getHashFromId(id));
|
||||
if (it != _map.end())
|
||||
return it->second.get();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t getHashFromId(const InheritanceTreeTypeId &id) const {
|
||||
size_t res = id.depth << 16;
|
||||
return res + id.index;
|
||||
}
|
||||
|
||||
std::unordered_map<size_t, std::unique_ptr<PolymorphicObjectHandlerInterface<Des, TObject>>> _map{};
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
template<typename RTTI>
|
||||
class PolymorphicContext
|
||||
{
|
||||
private:
|
||||
struct BaseToDerivedKey
|
||||
{
|
||||
|
||||
std::size_t baseHash;
|
||||
std::size_t derivedHash;
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TSerializer,
|
||||
template<typename>
|
||||
class THierarchy,
|
||||
typename TRoot,
|
||||
typename TBase,
|
||||
typename TDerived>
|
||||
void add()
|
||||
{
|
||||
addToMap<TSerializer, TRoot, TBase, TDerived>(std::is_abstract<TDerived>{});
|
||||
addChilds<TSerializer, THierarchy, TRoot, TBase, TDerived>(
|
||||
typename THierarchy<TDerived>::Childs{});
|
||||
}
|
||||
|
||||
template<typename TSerializer,
|
||||
template<typename>
|
||||
class THierarchy,
|
||||
typename TRoot,
|
||||
typename TBase,
|
||||
typename TDerived,
|
||||
typename T1,
|
||||
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.");
|
||||
add<TSerializer, THierarchy, TRoot, TBase, T1>();
|
||||
addChilds<TSerializer, THierarchy, TRoot, TBase, TDerived>(
|
||||
PolymorphicClassesList<Tn...>{});
|
||||
add<TSerializer, THierarchy, TRoot, T1, T1>();
|
||||
}
|
||||
|
||||
template<typename TSerializer,
|
||||
template<typename>
|
||||
class THierarchy,
|
||||
typename TRoot,
|
||||
typename TBase,
|
||||
typename TDerived>
|
||||
void addChilds(PolymorphicClassesList<>)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename TSerializer,
|
||||
typename TRoot,
|
||||
typename TBase,
|
||||
typename TDerived>
|
||||
void addToMap(std::false_type)
|
||||
{
|
||||
using THandler =
|
||||
PolymorphicHandler<RTTI, TSerializer, TRoot, TBase, TDerived>;
|
||||
BaseToDerivedKey key{ RTTI::template get<TBase>(),
|
||||
RTTI::template get<TDerived>() };
|
||||
pointer_utils::StdPolyAlloc<THandler> alloc{ _memResource };
|
||||
auto ptr = alloc.allocate(1);
|
||||
std::shared_ptr<THandler> handler(
|
||||
new (ptr) THandler{},
|
||||
[alloc](THandler* data) mutable {
|
||||
data->~THandler();
|
||||
alloc.deallocate(data, 1);
|
||||
},
|
||||
alloc);
|
||||
if (_baseToDerivedMap.emplace(key, std::move(handler)).second) {
|
||||
auto it = _baseToDerivedArray.find(key.baseHash);
|
||||
if (it == _baseToDerivedArray.end()) {
|
||||
it = _baseToDerivedArray
|
||||
.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(key.baseHash),
|
||||
std::forward_as_tuple(
|
||||
pointer_utils::StdPolyAlloc<size_t>{ _memResource }))
|
||||
.first;
|
||||
}
|
||||
it->second.push_back(key.derivedHash);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TSerializer,
|
||||
typename TRoot,
|
||||
typename TBase,
|
||||
typename TDerived>
|
||||
void addToMap(std::true_type)
|
||||
{
|
||||
using THandler = AbstractPolymorphicHandler<RTTI, TRoot, TBase, TDerived>;
|
||||
BaseToDerivedKey key{ RTTI::template get<TBase>(),
|
||||
RTTI::template get<TDerived>() };
|
||||
pointer_utils::StdPolyAlloc<THandler> alloc{ _memResource };
|
||||
auto ptr = alloc.allocate(1);
|
||||
std::shared_ptr<THandler> handler(
|
||||
new (ptr) THandler{},
|
||||
[alloc](THandler* data) mutable {
|
||||
data->~THandler();
|
||||
alloc.deallocate(data, 1);
|
||||
},
|
||||
alloc);
|
||||
_baseToDerivedMap.emplace(key, std::move(handler));
|
||||
}
|
||||
|
||||
MemResourceBase* _memResource;
|
||||
// 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 } }
|
||||
{
|
||||
}
|
||||
|
||||
PolymorphicContext(const PolymorphicContext&) = delete;
|
||||
PolymorphicContext& operator=(const PolymorphicContext&) = delete;
|
||||
PolymorphicContext(PolymorphicContext&&) = default;
|
||||
PolymorphicContext& operator=(PolymorphicContext&&) = default;
|
||||
|
||||
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)
|
||||
// https://github.com/fraillt/bitsery/issues/9
|
||||
template<typename TSerializer,
|
||||
template<typename> class THierarchy = PolymorphicBaseClass,
|
||||
typename T1,
|
||||
typename... Tn>
|
||||
void registerBasesList(PolymorphicClassesList<T1, Tn...>)
|
||||
{
|
||||
add<TSerializer, THierarchy, T1, T1, T1>();
|
||||
registerBasesList<TSerializer, THierarchy>(PolymorphicClassesList<Tn...>{});
|
||||
}
|
||||
|
||||
template<typename TSerializer, template<typename> class THierarchy>
|
||||
void registerBasesList(PolymorphicClassesList<>)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename Serializer, typename TBase>
|
||||
void serialize(Serializer& ser, TBase& obj) const
|
||||
{
|
||||
// get derived key
|
||||
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
|
||||
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)));
|
||||
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
|
||||
{
|
||||
size_t derivedIndex{};
|
||||
details::readSize(des.adapter(), derivedIndex, 0, std::false_type{});
|
||||
|
||||
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
|
||||
auto derivedHash = baseToDerivedVecIt->second[derivedIndex];
|
||||
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) {
|
||||
destroyFnc(getPolymorphicHandler(obj));
|
||||
}
|
||||
obj = createFnc(handler);
|
||||
}
|
||||
handler->process(&des, obj);
|
||||
} else
|
||||
des.adapter().error(ReaderError::InvalidPointer);
|
||||
}
|
||||
|
||||
template<typename TBase>
|
||||
const std::shared_ptr<PolymorphicHandlerBase>& getPolymorphicHandler(
|
||||
TBase* obj) const
|
||||
{
|
||||
auto it = _baseToDerivedMap.find(BaseToDerivedKey{
|
||||
RTTI::template get<TBase>(), RTTI::template get<TBase>(*obj) });
|
||||
assert(it != _baseToDerivedMap.end());
|
||||
return it->second;
|
||||
}
|
||||
|
||||
template<typename TBase>
|
||||
const std::shared_ptr<PolymorphicHandlerBase>& getPolymorphicHandler() const
|
||||
{
|
||||
auto it = _baseToDerivedMap.find(BaseToDerivedKey{
|
||||
RTTI::template get<TBase>(), RTTI::template get<TBase>() });
|
||||
assert(it != _baseToDerivedMap.end());
|
||||
return it->second;
|
||||
}
|
||||
|
||||
const std::shared_ptr<PolymorphicHandlerBase>* getPolymorphicHandler(
|
||||
size_t baseTypeId,
|
||||
size_t derivedTypeId) const
|
||||
{
|
||||
auto it =
|
||||
_baseToDerivedMap.find(BaseToDerivedKey{ baseTypeId, derivedTypeId });
|
||||
if (it == _baseToDerivedMap.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &it->second;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_EXT_POLYMORPHISM_H
|
||||
}
|
||||
|
||||
#endif // BITSERY_EXT_POLYMORPHISM_UTILS_H
|
||||
|
||||
@@ -1,28 +1,65 @@
|
||||
// MIT License
|
||||
//
|
||||
// Created by fraillt on 17.11.30.
|
||||
// Copyright (c) 2018 Mindaugas Vinkelis
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_RTTI_UTILS_H
|
||||
#define BITSERY_RTTI_UTILS_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
namespace utils {
|
||||
struct StandardRTTI {
|
||||
template<typename T>
|
||||
static size_t get() {
|
||||
return typeid(T).hash_code();
|
||||
}
|
||||
namespace ext {
|
||||
|
||||
template<typename T>
|
||||
static size_t get(T &&obj) {
|
||||
return typeid(obj).hash_code();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
struct StandardRTTI
|
||||
{
|
||||
|
||||
template<typename TBase>
|
||||
static size_t get(TBase& obj)
|
||||
{
|
||||
return typeid(obj).hash_code();
|
||||
}
|
||||
|
||||
template<typename TBase>
|
||||
static constexpr size_t get()
|
||||
{
|
||||
return typeid(TBase).hash_code();
|
||||
}
|
||||
|
||||
template<typename TBase, typename TDerived>
|
||||
static constexpr TDerived* cast(TBase* obj)
|
||||
{
|
||||
static_assert(!std::is_pointer<TDerived>::value, "");
|
||||
return dynamic_cast<TDerived*>(obj);
|
||||
}
|
||||
|
||||
template<typename TBase>
|
||||
static constexpr bool isPolymorphic()
|
||||
{
|
||||
return std::is_polymorphic<TBase>::value;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_RTTI_UTILS_H
|
||||
#endif // BITSERY_RTTI_UTILS_H
|
||||
|
||||
@@ -1,206 +1,268 @@
|
||||
//MIT License
|
||||
// MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
// Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_VALUE_RANGE_H
|
||||
#define BITSERY_EXT_VALUE_RANGE_H
|
||||
|
||||
#include "../details/serialization_common.h"
|
||||
#include "../details/adapter_common.h"
|
||||
#include <cassert>
|
||||
|
||||
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} {}
|
||||
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 }
|
||||
{
|
||||
}
|
||||
|
||||
const size_t value;
|
||||
};
|
||||
const size_t value;
|
||||
};
|
||||
}
|
||||
|
||||
// implementation details for range functionality
|
||||
namespace details {
|
||||
|
||||
template<typename T>
|
||||
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
|
||||
return getSize(max - min, 0);
|
||||
}
|
||||
|
||||
template<typename T, typename Enable = void>
|
||||
struct RangeSpec
|
||||
{
|
||||
|
||||
constexpr RangeSpec(T minValue, T maxValue)
|
||||
: min{ minValue }
|
||||
, max{ maxValue }
|
||||
, bitsRequired{ calcRequiredBits(min, max) }
|
||||
{
|
||||
}
|
||||
|
||||
const T min;
|
||||
const T max;
|
||||
const size_t bitsRequired;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
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(
|
||||
static_cast<typename std::underlying_type<T>::type>(min),
|
||||
static_cast<typename std::underlying_type<T>::type>(max)) }
|
||||
{
|
||||
}
|
||||
|
||||
const T min;
|
||||
const T max;
|
||||
const size_t bitsRequired;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
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, T precision)
|
||||
: min{ minValue }
|
||||
, max{ maxValue }
|
||||
, bitsRequired{ calcRequiredBits<details::SameSizeUnsigned<T>>(
|
||||
{},
|
||||
static_cast<details::SameSizeUnsigned<T>>((max - min) / precision)) }
|
||||
{
|
||||
}
|
||||
|
||||
const T min;
|
||||
const T max;
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
namespace ext {
|
||||
|
||||
template<typename TValue>
|
||||
class ValueRange
|
||||
{
|
||||
public:
|
||||
template<typename... Args>
|
||||
constexpr ValueRange(const TValue& min, const TValue& max, 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
|
||||
{
|
||||
assert(details::isRangeValid(v, _range));
|
||||
using BT = decltype(details::getRangeValue(v, _range));
|
||||
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
|
||||
{
|
||||
auto& reader = des.adapter();
|
||||
reader.readBits(reinterpret_cast<details::SameSizeUnsigned<T>&>(v),
|
||||
_range.bitsRequired);
|
||||
details::setRangeValue(v, _range);
|
||||
handleInvalidRange(
|
||||
reader, v, std::integral_constant<bool, Des::TConfig::CheckDataErrors>{});
|
||||
}
|
||||
|
||||
constexpr size_t getRequiredBits() const { return _range.bitsRequired; };
|
||||
|
||||
private:
|
||||
template<typename Reader, typename T>
|
||||
void handleInvalidRange(Reader& reader, T& v, std::true_type) const
|
||||
{
|
||||
if (!details::isRangeValid(v, _range)) {
|
||||
reader.error(ReaderError::InvalidData);
|
||||
v = _range.min;
|
||||
}
|
||||
}
|
||||
|
||||
//implementation details for range functionality
|
||||
namespace details {
|
||||
template<typename Reader, typename T>
|
||||
void handleInvalidRange(Reader&, T&, std::false_type) const
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr size_t getSize(T v, size_t s) {
|
||||
return v > 0 ? getSize(v / 2, s + 1) : s;
|
||||
}
|
||||
details::RangeSpec<TValue> _range;
|
||||
};
|
||||
}
|
||||
|
||||
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
|
||||
return getSize(max - min, 0);
|
||||
}
|
||||
|
||||
template<typename T, typename Enable = void>
|
||||
struct RangeSpec {
|
||||
|
||||
constexpr RangeSpec(T minValue, T maxValue)
|
||||
: min{minValue},
|
||||
max{maxValue},
|
||||
bitsRequired{calcRequiredBits(min, max)} {
|
||||
}
|
||||
|
||||
const T min;
|
||||
const T max;
|
||||
const size_t bitsRequired;
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
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(
|
||||
static_cast<typename std::underlying_type<T>::type>(min),
|
||||
static_cast<typename std::underlying_type<T>::type>(max))} {
|
||||
}
|
||||
|
||||
const T min;
|
||||
const T max;
|
||||
const size_t bitsRequired;
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
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, T precision) :
|
||||
min{minValue},
|
||||
max{maxValue},
|
||||
bitsRequired{calcRequiredBits<details::SameSizeUnsigned<T>>({}, ((max - min) / precision))} {
|
||||
|
||||
}
|
||||
|
||||
const T min;
|
||||
const T max;
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
using VT = details::SameSizeUnsigned<T>;
|
||||
const VT maxUint = (static_cast<VT>(1) << r.bitsRequired) - 1;
|
||||
const auto ratio = (v - r.min) / (r.max - r.min);
|
||||
return static_cast<VT>(ratio * maxUint);
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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) / 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) {
|
||||
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) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
namespace ext {
|
||||
|
||||
template<typename TValue>
|
||||
class ValueRange {
|
||||
public:
|
||||
|
||||
template<typename ... Args>
|
||||
explicit constexpr ValueRange(Args &&... args):_range{std::forward<Args>(args)...} {}
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &, Writer &writer, const T &v, Fnc &&) const {
|
||||
assert(details::isRangeValid(v, _range));
|
||||
using BT = decltype(details::getRangeValue(v, _range));
|
||||
writer.template writeBits<BT>(details::getRangeValue(v, _range), _range.bitsRequired);
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &, Reader &reader, T &v, Fnc &&) const {
|
||||
reader.readBits(reinterpret_cast<details::SameSizeUnsigned<T> &>(v), _range.bitsRequired);
|
||||
details::setRangeValue(v, _range);
|
||||
if (!details::isRangeValid(v, _range)) {
|
||||
reader.setError(ReaderError::InvalidData);
|
||||
v = _range.min;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr size_t getRequiredBits() const {
|
||||
return _range.bitsRequired;
|
||||
};
|
||||
private:
|
||||
details::RangeSpec<TValue> _range;
|
||||
};
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::ValueRange<T>, T> {
|
||||
using TValue = void;
|
||||
static constexpr bool SupportValueOverload = false;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
}
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::ValueRange<T>, T>
|
||||
{
|
||||
using TValue = void;
|
||||
static constexpr bool SupportValueOverload = false;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //BITSERY_EXT_VALUE_RANGE_H
|
||||
#endif // BITSERY_EXT_VALUE_RANGE_H
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#ifndef BITSERY_FLEXIBLE_H
|
||||
#define BITSERY_FLEXIBLE_H
|
||||
|
||||
#include "details/serialization_common.h"
|
||||
#include "details/flexible_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace flexible {
|
||||
|
||||
//overload when T is reference type
|
||||
template<typename S, typename T>
|
||||
void archiveProcessImpl(S &s, T &&head, std::true_type) {
|
||||
s.object(std::forward<T>(head));
|
||||
}
|
||||
|
||||
//overload when T is rvalue type, only allowable for behaviour modifying functions for deserializer
|
||||
template<typename S, typename T>
|
||||
void archiveProcessImpl(S &s, T &&head, std::false_type) {
|
||||
static_assert(std::is_base_of<ArchiveWrapperFnc, T>::value,
|
||||
"\nOnly archive behaviour modifying functions can be passed by rvalue to deserializer\n");
|
||||
serialize(s, head);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//define function that enables s.archive(....) usage
|
||||
template<typename S, typename T>
|
||||
void archiveProcess(S &s, T &&head) {
|
||||
flexible::archiveProcessImpl(s, std::forward<T>(head), std::is_reference<T>{});
|
||||
}
|
||||
|
||||
//wrapper functions that enables to serialize as container or string
|
||||
template<typename T, size_t N>
|
||||
flexible::CArray<T, N, true> asText(T (&str)[N]) {
|
||||
return {str};
|
||||
}
|
||||
|
||||
template<typename T, size_t N>
|
||||
flexible::CArray<T, N, false> asContainer(T (&obj)[N]) {
|
||||
return {obj};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
flexible::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) {
|
||||
s.boolValue(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]) {
|
||||
static_assert(N == 0,
|
||||
"\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]) {
|
||||
flexible::processContainer(s, obj);
|
||||
}
|
||||
|
||||
//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() {
|
||||
//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.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_FLEXIBLE_H
|
||||
@@ -1,37 +0,0 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#ifndef BITSERY_FLEXIBLE_TYPE_STD_ARRAY_H
|
||||
#define BITSERY_FLEXIBLE_TYPE_STD_ARRAY_H
|
||||
|
||||
#include "../traits/array.h"
|
||||
#include "../details/flexible_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename T, size_t N>
|
||||
void serialize(S &s, std::array<T, N> &obj) {
|
||||
flexible::processContainer(s, obj);
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_FLEXIBLE_TYPE_STD_ARRAY_H
|
||||
@@ -1,37 +0,0 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#ifndef BITSERY_FLEXIBLE_TYPE_STD_DEQUE_H
|
||||
#define BITSERY_FLEXIBLE_TYPE_STD_DEQUE_H
|
||||
|
||||
#include "../traits/deque.h"
|
||||
#include "../details/flexible_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::deque<TArgs... > &obj) {
|
||||
flexible::processContainer(s, obj);
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_FLEXIBLE_TYPE_STD_DEQUE_H
|
||||
@@ -1,37 +0,0 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#ifndef BITSERY_FLEXIBLE_TYPE_STD_FORWARD_LIST_H
|
||||
#define BITSERY_FLEXIBLE_TYPE_STD_FORWARD_LIST_H
|
||||
|
||||
#include "../traits/forward_list.h"
|
||||
#include "../details/flexible_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::forward_list<TArgs... > &obj) {
|
||||
flexible::processContainer(s, obj);
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_FLEXIBLE_TYPE_STD_FORWARD_LIST_H
|
||||
@@ -1,37 +0,0 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#ifndef BITSERY_FLEXIBLE_TYPE_STD_LIST_H
|
||||
#define BITSERY_FLEXIBLE_TYPE_STD_LIST_H
|
||||
|
||||
#include "../traits/list.h"
|
||||
#include "../details/flexible_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::list<TArgs... > &obj) {
|
||||
flexible::processContainer(s, obj);
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_FLEXIBLE_TYPE_STD_LIST_H
|
||||
@@ -1,54 +0,0 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#ifndef BITSERY_FLEXIBLE_TYPE_STD_MAP_H
|
||||
#define BITSERY_FLEXIBLE_TYPE_STD_MAP_H
|
||||
|
||||
#include <map>
|
||||
#include "../ext/std_map.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::map<TArgs ... > &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
using TKey = typename std::map<TArgs...>::key_type;
|
||||
using TValue = typename std::map<TArgs...>::mapped_type;
|
||||
s.ext(obj, ext::StdMap{maxSize},
|
||||
[&s](TKey& key, TValue& value) {
|
||||
s.object(key);
|
||||
s.object(value);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::multimap<TArgs ... > &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
using TKey = typename std::multimap<TArgs...>::key_type;
|
||||
using TValue = typename std::multimap<TArgs...>::mapped_type;
|
||||
s.ext(obj, ext::StdMap{maxSize},
|
||||
[&s](TKey& key, TValue& value) {
|
||||
s.object(key);
|
||||
s.object(value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_FLEXIBLE_TYPE_STD_MAP_H
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user