Skip to content

Commit abdddad

Browse files
committed
Add optional slimmed down Boost.Serialization shim for protocol 100
1 parent 836cf86 commit abdddad

File tree

7 files changed

+223
-15
lines changed

7 files changed

+223
-15
lines changed

CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ option(LSL_LEGACY_CPP_ABI "Build legacy C++ ABI into lsl-static" OFF)
2323
option(LSL_OPTIMIZATIONS "Enable some more compiler optimizations" ON)
2424
option(LSL_UNITTESTS "Build LSL library unit tests" OFF)
2525
option(LSL_BUNDLED_PUGIXML "Use the bundled pugixml by default" ON)
26+
option(LSL_SLIMARCHIVE "Use experimental but smaller serialization code" OFF)
27+
28+
mark_as_advanced(LSL_SLIMARCHIVE LSL_FORCE_FANCY_LIBNAME)
2629

2730
set(LSL_WINVER "0x0601" CACHE STRING
2831
"Windows version (_WIN32_WINNT) to target (defaults to 0x0601 for Windows 7)")
@@ -161,6 +164,11 @@ add_library(lslboost OBJECT
161164
lslboost/serialization_objects.cpp
162165
)
163166
target_link_libraries(lslboost PUBLIC Threads::Threads)
167+
168+
if(LSL_SLIMARCHIVE)
169+
message(STATUS "Using shim instead of full Boost.Archive")
170+
target_compile_definitions(lslboost PUBLIC SLIMARCHIVE)
171+
endif()
164172
target_compile_features(lslboost PUBLIC cxx_std_11 cxx_lambda_init_captures)
165173

166174
target_compile_definitions(lslboost

lslboost/serialization_objects.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// These implementation files aren't needed with slimarchive shim
2+
#ifndef SLIMARCHIVE
13
#include "libs/serialization/src/archive_exception.cpp"
24
#include "libs/serialization/src/basic_archive.cpp"
35
#include "libs/serialization/src/basic_iarchive.cpp"
@@ -11,3 +13,4 @@
1113
#ifdef _WIN32
1214
#include "libs/serialization/src/codecvt_null.cpp"
1315
#endif
16+
#endif

src/portable_archive/portable_archive_exception.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,12 @@
1414

1515
#pragma once
1616

17+
#ifdef SLIMARCHIVE
18+
#include "slimarchive.hpp"
19+
#else
1720
#include <boost/archive/basic_archive.hpp>
1821
#include <boost/archive/archive_exception.hpp>
22+
#endif
1923

2024
namespace eos {
2125

src/portable_archive/portable_iarchive.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@
33
#include <istream>
44
#include "portable_archive_includes.hpp"
55

6+
#ifdef SLIMARCHIVE
7+
#include "slimarchive.hpp"
8+
#else
69
#include <boost/archive/basic_binary_iprimitive.hpp>
710
#include <boost/archive/basic_binary_iarchive.hpp>
811

912
#include <boost/archive/detail/polymorphic_iarchive_route.hpp>
13+
#endif
1014

1115

1216
namespace eos {

src/portable_archive/portable_oarchive.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@
33
#include <ostream>
44
#include "portable_archive_includes.hpp"
55

6+
#ifdef SLIMARCHIVE
7+
#include "slimarchive.hpp"
8+
#else
69
#include <boost/archive/basic_binary_oprimitive.hpp>
710
#include <boost/archive/basic_binary_oarchive.hpp>
11+
#endif
812

913

1014
namespace eos {
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
#pragma once
2+
3+
/// Small shim implementing the needed Boost serialization classes for liblsl
4+
5+
#include <cstdint>
6+
#include <stdexcept>
7+
#include <streambuf>
8+
#include <type_traits>
9+
10+
/// dummy #defines to prevent portable archive from loading additional boost archive parts
11+
#define NO_EXPLICIT_TEMPLATE_INSTANTIATION
12+
#define BOOST_ARCHIVE_SERIALIZER_INCLUDED
13+
#define BOOST_SERIALIZATION_REGISTER_ARCHIVE(typename)
14+
15+
// forward declaration
16+
namespace lsl {
17+
class sample;
18+
}
19+
20+
/// Maps LSL types to an index without needing full RTTI
21+
template <typename T> struct lsl_type_index {};
22+
template <> struct lsl_type_index<lsl::sample> { static const int idx = 0; };
23+
/// two classes used in unit tests
24+
template <> struct lsl_type_index<struct Testclass> { static const int idx = 1; };
25+
template <> struct lsl_type_index<struct Testclass2> { static const int idx = 2; };
26+
27+
/// keep track of classes already seen once, needed for a field in Boost.Archive
28+
class serialized_class_tracker {
29+
bool seen_[3]{false};
30+
31+
public:
32+
/// Return true /if an instance of this class has already been (de)serialized before
33+
template <typename T> inline bool already_seen(const T &) {
34+
if (seen_[lsl_type_index<T>::idx]) return true;
35+
seen_[lsl_type_index<T>::idx] = true;
36+
return false;
37+
}
38+
};
39+
40+
namespace lslboost {
41+
#ifndef BOOST_INTEGER_HPP
42+
/// Maps a type size in bits to the corresponding uintXY_t type
43+
template <int Size> struct uint_t {};
44+
template <> struct uint_t<8> { using least = uint8_t; };
45+
template <> struct uint_t<16> { using least = uint16_t; };
46+
template <> struct uint_t<32> { using least = uint32_t; };
47+
template <> struct uint_t<64> { using least = uint64_t; };
48+
#endif
49+
50+
namespace archive {
51+
using library_version_type = uint8_t;
52+
inline library_version_type BOOST_ARCHIVE_VERSION() { return 17; }
53+
enum flags { no_header = 1, no_codecvt = 2 };
54+
55+
struct archive_exception : std::exception {
56+
archive_exception(int) {}
57+
enum errors { invalid_signature, unsupported_version, other_exception };
58+
};
59+
60+
/// Common class to store flags
61+
class flagstore {
62+
int flags_;
63+
64+
public:
65+
flagstore(int flags) : flags_(flags) {}
66+
int get_flags() const { return flags_; }
67+
};
68+
69+
template <typename Archive> struct basic_binary_oarchive : public flagstore {
70+
basic_binary_oarchive(int flags_) : flagstore(flags_) {}
71+
};
72+
73+
template <typename Archive> struct basic_binary_iarchive : public flagstore {
74+
basic_binary_iarchive(int flags_) : flagstore(flags_) {}
75+
};
76+
77+
/// Helper base class for calling methods of an descendant class, i.e. Archive::fn, not this->fn
78+
template <typename Archive> class archive_upcaster {
79+
public:
80+
archive_upcaster() {
81+
static_assert(
82+
std::is_base_of<typename std::remove_reference<decltype(*this)>::type, Archive>::value,
83+
"Archive must inherit from basic_binary_i/oprimitive");
84+
}
85+
86+
/** Return a reference to the actual archive implementation instead of the base class
87+
*
88+
* This is needed for most operations to call Archive::fn, not basic_binary_primitive::fn */
89+
inline Archive &archive_impl() { return *((Archive *)this); }
90+
};
91+
92+
/*
93+
template<typename Archive> class basic_binary_primitive {
94+
std::streambuf &sb;
95+
}*/
96+
97+
template <typename Archive, typename CharT, typename OSTraits>
98+
class basic_binary_oprimitive : private serialized_class_tracker,
99+
private archive_upcaster<Archive> {
100+
std::streambuf &sb;
101+
102+
/// calls Archive::serialize instead of this->serialize()
103+
template <typename T> Archive &delegate_save(const T &t) {
104+
this->archive_impl().save(t);
105+
return this->archive_impl();
106+
}
107+
108+
public:
109+
basic_binary_oprimitive(std::streambuf &sbuf, int) : archive_upcaster<Archive>(), sb(sbuf) {}
110+
111+
template <typename T> void save_binary(const T *data, std::size_t bytes) {
112+
sb.sputn(reinterpret_cast<const char *>(data), bytes);
113+
}
114+
115+
template <typename T> void save(const T &t) { save_binary<T>(&t, sizeof(T)); }
116+
117+
void save(const std::string &s) {
118+
delegate_save(s.size());
119+
save_binary(s.data(), s.size());
120+
}
121+
122+
template <typename T>
123+
std::enable_if_t<std::is_arithmetic<T>::value, Archive &> operator<<(const T &t) {
124+
return delegate_save(t);
125+
}
126+
127+
Archive &operator<<(const std::string &t) { return delegate_save(t); }
128+
129+
/// calls the `serialize()` member function if it exists
130+
template <typename T, void (T::*fn)(Archive &, uint32_t) const = &T::serialize>
131+
Archive &operator<<(const T &t) {
132+
if (!this->already_seen(t)) save<uint16_t>(0);
133+
t.serialize(this->archive_impl(), 0);
134+
return this->archive_impl();
135+
}
136+
137+
template <typename T> Archive &operator&(const T &t) { return this->archive_impl() << t; }
138+
};
139+
140+
141+
template <class Archive, typename CharT, typename OSTraits>
142+
class basic_binary_iprimitive : private serialized_class_tracker,
143+
private archive_upcaster<Archive> {
144+
std::streambuf &sb;
145+
146+
template <typename T> Archive &delegate_load(T &t) {
147+
this->archive_impl().load(t);
148+
return this->archive_impl();
149+
}
150+
151+
public:
152+
basic_binary_iprimitive(std::streambuf &sbuf, int) : archive_upcaster<Archive>(), sb(sbuf) {}
153+
154+
/// Load raw binary data from the stream
155+
template <typename T> void load_binary(T *data, std::size_t bytes) {
156+
sb.sgetn(reinterpret_cast<char *>(data), bytes);
157+
}
158+
159+
template <typename T> void load(T &t) { load_binary<T>(&t, sizeof(T)); }
160+
161+
void load(std::string &s) {
162+
std::size_t size;
163+
delegate_load(size);
164+
char *data = new char[size];
165+
load_binary(data, size);
166+
s.assign(data, size);
167+
delete[] data;
168+
}
169+
170+
Archive &operator>>(std::string &t) { return delegate_load(t); }
171+
172+
template <typename T>
173+
std::enable_if_t<std::is_arithmetic<T>::value, Archive &> operator>>(T &t) {
174+
return delegate_load(t);
175+
}
176+
177+
/// calls the `serialize()` member function if it exists
178+
template <typename T, void (T::*fn)(Archive &, uint32_t) = &T::serialize>
179+
Archive &operator>>(T &t) {
180+
if (!this->already_seen(t)) {
181+
uint16_t dummy;
182+
load(dummy);
183+
}
184+
t.serialize(this->archive_impl(), 0);
185+
return this->archive_impl();
186+
}
187+
188+
template <typename T> Archive &operator&(T &t) { return this->archive_impl() >> t; }
189+
190+
void set_library_version(int) {}
191+
};
192+
} // namespace archive
193+
} // namespace lslboost

testing/internal/serialization_v100.cpp

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,13 @@ struct Testclass {
1818
lsl::sample_p s1, s2;
1919
char testchar{0};
2020

21-
template <typename Archive> void serialize(Archive &a, const uint32_t) {
21+
template <typename Archive> void serialize_(Archive &a) {
2222
a &testchar &testint &negativeint &testbigint &testdouble &teststr &*s1 &*s2;
2323
}
24-
template <typename Archive> void load(Archive &a, const uint32_t archive_version) {
25-
serialize(a, archive_version);
26-
}
27-
template <typename Archive> void save(Archive &a, const uint32_t archive_version) const {
28-
const_cast<Testclass *>(this)->serialize(a, archive_version);
24+
void serialize(eos::portable_iarchive &a, uint32_t) { serialize_(a); }
25+
26+
void serialize(eos::portable_oarchive &a, uint32_t) const {
27+
const_cast<Testclass *>(this)->serialize_(a);
2928
}
3029

3130
Testclass() : s1(doublefac.new_sample(0.0, false)), s2(strfac.new_sample(0.0, false)) {}
@@ -41,13 +40,8 @@ struct Testclass {
4140
struct Testclass2 {
4241
uint64_t i;
4342

44-
template <typename Archive> void serialize(Archive &a, const uint32_t) { a &i; }
45-
template <typename Archive> void load(Archive &a, const uint32_t archive_version) {
46-
serialize(a, archive_version);
47-
}
48-
template <typename Archive> void save(Archive &a, const uint32_t archive_version) const {
49-
const_cast<Testclass2 *>(this)->serialize(a, archive_version);
50-
}
43+
void serialize(eos::portable_iarchive &a, uint32_t) { a &i; }
44+
void serialize(eos::portable_oarchive &a, uint32_t) const { a &i; }
5145
};
5246

5347
TEST_CASE("v100 protocol serialization", "[basic][serialization]") {
@@ -107,5 +101,3 @@ TEST_CASE("v100 protocol serialization", "[basic][serialization]") {
107101
if (*in1.s2 != *out1.s2) FAIL("Sample 2 serialization mismatch");
108102
} catch (std::exception &e) { FAIL(e.what()); }
109103
}
110-
111-
TEST_CASE("read v100 protocol samples", "[basic][serialization]") {}

0 commit comments

Comments
 (0)