From c829cd0de5acdb97418dab9c9e32c8244cfd052b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20G=C3=BCndling?= Date: Sun, 16 Dec 2018 23:43:56 +0100 Subject: [PATCH] add support for offset based serialization offset_ptr: stores the offset as difference between `this` and the object it points to Serializing this allows to skip the deserialization step and just use a `reinterpret_cast(buf.begin())`. This is useful for example in situations with shared memory. --- .travis.yml | 1 + include/cista/containers/offset_ptr.h | 82 ++++++++++++++ include/cista/containers/string.h | 54 +++++---- include/cista/containers/unique_ptr.h | 25 ++++- include/cista/containers/vector.h | 4 +- include/cista/offset_t.h | 4 +- include/cista/serialization.h | 153 +++++++++++++++++++------- test/offset_graph_test.cc | 128 +++++++++++++++++++++ test/offset_ptr_vector_test.cc | 90 +++++++++++++++ test/pointer_test.cc | 3 +- 10 files changed, 472 insertions(+), 72 deletions(-) create mode 100644 include/cista/containers/offset_ptr.h create mode 100644 test/offset_graph_test.cc create mode 100644 test/offset_ptr_vector_test.cc diff --git a/.travis.yml b/.travis.yml index b4ea02c4..de70aafa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,7 @@ addons: env: - COVERAGE=Off BUILD_TYPE=Release LDFLAGS="-stdlib=libc++" CC="clang-7" CXX="clang++-7" CXXFLAGS="-std=c++17 -stdlib=libc++ -D_LIBCPP_VERSION=1" - COVERAGE=Off BUILD_TYPE=Debug LDFLAGS="-stdlib=libc++" CC="clang-7" CXX="clang++-7" CXXFLAGS="-std=c++17 -stdlib=libc++ -D_LIBCPP_VERSION=1 -fno-omit-frame-pointer -fsanitize=address" + - COVERAGE=Off BUILD_TYPE=Release LDFLAGS="-stdlib=libc++" CC="clang-7" CXX="clang++-7" CXXFLAGS="-std=c++17 -stdlib=libc++ -D_LIBCPP_VERSION=1 -fno-omit-frame-pointer -fsanitize=address" - COVERAGE=Off BUILD_TYPE=Release CC="gcc-7" CXX="g++-7" CXXFLAGS="-std=c++17" - COVERAGE=Off BUILD_TYPE=Debug CC="gcc-7" CXX="g++-7" CXXFLAGS="-std=c++17 -fno-omit-frame-pointer -fsanitize=address" - COVERAGE=On BUILD_TYPE=Debug CC="gcc-7" CXX="g++-7" CXXFLAGS="-std=c++17" diff --git a/include/cista/containers/offset_ptr.h b/include/cista/containers/offset_ptr.h new file mode 100644 index 00000000..3d48fcc5 --- /dev/null +++ b/include/cista/containers/offset_ptr.h @@ -0,0 +1,82 @@ +#pragma once + +#include "cista/offset_t.h" + +namespace cista { + +template +struct offset_ptr { + offset_ptr() = default; + offset_ptr(std::nullptr_t) : offset_{NULLPTR_OFFSET} {} + offset_ptr(T const* p) : offset_{ptr_to_offset(p)} {} + + offset_ptr& operator=(T const* p) { + offset_ = ptr_to_offset(p); + return *this; + } + offset_ptr& operator=(std::nullptr_t) { + offset_ = NULLPTR_OFFSET; + return *this; + } + + offset_ptr(offset_ptr const& o) : offset_ptr{o.get()} {} + offset_ptr(offset_ptr&& o) : offset_ptr{o.get()} {} + offset_ptr& operator=(offset_ptr const& o) { + offset_ = ptr_to_offset(o.get()); + } + offset_ptr& operator=(offset_ptr&& o) { offset_ = ptr_to_offset(o.get()); } + + offset_t ptr_to_offset(T const* p) const { + return p == nullptr + ? NULLPTR_OFFSET + : static_cast(reinterpret_cast(p) - + reinterpret_cast(this)); + } + + operator T*() { return get(); } + operator T const*() const { return get(); } + T& operator*() { return *get(); } + T const& operator*() const { return *get(); } + + T* operator->() { return get(); } + T const* operator->() const { return get(); } + + T const* get() const { + auto const ptr = offset_ == NULLPTR_OFFSET + ? nullptr + : reinterpret_cast( + reinterpret_cast(this) + offset_); + return ptr; + } + T* get() { + auto const ptr = + offset_ == NULLPTR_OFFSET + ? nullptr + : reinterpret_cast(reinterpret_cast(this) + offset_); + return ptr; + } + + template + offset_ptr operator+(Int i) const { + offset_ptr r = *this; + r.offset_ += i * sizeof(T); + return r; + } + + friend bool operator==(std::nullptr_t, offset_ptr const& o) { + return o.offset_ == NULLPTR_OFFSET; + } + friend bool operator==(offset_ptr const& o, std::nullptr_t) { + return o.offset_ == NULLPTR_OFFSET; + } + friend bool operator!=(std::nullptr_t, offset_ptr const& o) { + return o.offset_ != NULLPTR_OFFSET; + } + friend bool operator!=(offset_ptr const& o, std::nullptr_t) { + return o.offset_ != NULLPTR_OFFSET; + } + + offset_t offset_; +}; + +} // namespace cista \ No newline at end of file diff --git a/include/cista/containers/string.h b/include/cista/containers/string.h index b74477dc..f71f3301 100644 --- a/include/cista/containers/string.h +++ b/include/cista/containers/string.h @@ -4,9 +4,12 @@ #include #include +#include "cista/containers/offset_ptr.h" + namespace cista { -struct string { +template +struct basic_string { using msize_t = uint32_t; static msize_t mstrlen(char const* s) { @@ -18,26 +21,30 @@ struct string { static constexpr struct non_owning_t { } non_owning{}; - string() { std::memset(this, 0, sizeof(*this)); } - ~string() { reset(); } + basic_string() { std::memset(this, 0, sizeof(*this)); } + ~basic_string() { reset(); } - string(char const* s, owning_t) : string() { set_owning(s, mstrlen(s)); } - string(char const* s, non_owning_t) : string() { set_non_owning(s); } - string(char const* s) : string(s, non_owning) {} // NOLINT + basic_string(char const* s, owning_t) : basic_string() { + set_owning(s, mstrlen(s)); + } + basic_string(char const* s, non_owning_t) : basic_string() { + set_non_owning(s); + } + basic_string(char const* s) : basic_string(s, non_owning) {} // NOLINT - string(string const& s) : string() { copy_from(s); } + basic_string(basic_string const& s) : basic_string() { copy_from(s); } - string(string&& s) { + basic_string(basic_string&& s) { std::memcpy(this, &s, sizeof(*this)); std::memset(&s, 0, sizeof(*this)); } - string& operator=(string const& s) { + basic_string& operator=(basic_string const& s) { copy_from(s); return *this; } - string& operator=(string&& s) { + basic_string& operator=(basic_string&& s) { std::memcpy(this, &s, sizeof(*this)); return *this; } @@ -46,7 +53,7 @@ struct string { void reset() { if (!h_.is_short_ && h_.ptr_ != nullptr && h_.self_allocated_) { - std::free(const_cast(h_.ptr_)); + std::free(const_cast(data())); } std::memset(this, 0, sizeof(*this)); } @@ -72,7 +79,7 @@ struct string { } h_.size_ = size; h_.self_allocated_ = true; - std::memcpy(const_cast(h_.ptr_), str, size); + std::memcpy(const_cast(data()), str, size); } } @@ -98,7 +105,7 @@ struct string { h_.size_ = size; } - void copy_from(string const& s) { + void copy_from(basic_string const& s) { reset(); if (s.is_short()) { std::memcpy(this, &s, sizeof(s)); @@ -111,18 +118,24 @@ struct string { std::string_view view() const { return std::string_view{data(), size()}; } - char const* data() const { return is_short() ? s_.s_ : h_.ptr_; } + char const* data() const { + if constexpr (std::is_pointer_v) { + return is_short() ? s_.s_ : h_.ptr_; + } else { + return is_short() ? s_.s_ : h_.ptr_.get(); + } + } - string& operator=(char const* s) { + basic_string& operator=(char const* s) { set_non_owning(s, mstrlen(s)); return *this; } - friend bool operator==(string const& a, char const* b) { + friend bool operator==(basic_string const& a, char const* b) { return a.view() == std::string_view{b}; } - friend bool operator==(string const& a, string const& b) { + friend bool operator==(basic_string const& a, basic_string const& b) { return a.view() == b.view(); } @@ -145,7 +158,7 @@ struct string { uint8_t __fill_2__{0}; uint8_t __fill_3__{0}; uint32_t size_{0}; - char const* ptr_{nullptr}; + Ptr ptr_{nullptr}; }; struct stack { @@ -157,6 +170,9 @@ struct string { heap h_; stack s_; }; -}; +}; // namespace cista + +using string = basic_string; +using o_string = basic_string>; } // namespace cista diff --git a/include/cista/containers/unique_ptr.h b/include/cista/containers/unique_ptr.h index f74f90db..70bb689e 100644 --- a/include/cista/containers/unique_ptr.h +++ b/include/cista/containers/unique_ptr.h @@ -2,9 +2,11 @@ #include +#include "cista/containers/offset_ptr.h" + namespace cista { -template +template struct unique_ptr { using value_t = T; @@ -31,24 +33,35 @@ struct unique_ptr { ~unique_ptr() { if (self_allocated_ && el_ != nullptr) { - delete el_; + delete get(); } } - T* get() const { return el_; } + T* get() { return el_; } + T const* get() const { return el_; } T* operator->() { return el_; } + T& operator*() { return *el_; } + T const& operator*() const { return *el_; } T const* operator->() const { return el_; } - T* el_{nullptr}; + Ptr el_{nullptr}; bool self_allocated_{false}; uint8_t __fill_0__{0}; uint16_t __fill_1__{0}; uint32_t __fill_2__{0}; }; +template +using o_unique_ptr = unique_ptr>; + +template +unique_ptr make_unique(Args&&... args) { + return unique_ptr{new T{std::forward(args)...}, true}; +} + template -cista::unique_ptr make_unique(Args&&... args) { - return cista::unique_ptr{new T{std::forward(args)...}, true}; +o_unique_ptr make_o_unique(Args&&... args) { + return o_unique_ptr{new T{std::forward(args)...}, true}; } } // namespace cista diff --git a/include/cista/containers/vector.h b/include/cista/containers/vector.h index abff687b..e2ad0c0e 100644 --- a/include/cista/containers/vector.h +++ b/include/cista/containers/vector.h @@ -11,7 +11,7 @@ namespace cista { -template +template struct vector { using size_type = TemplateSizeType; using value_type = T; @@ -234,7 +234,7 @@ struct vector { explicit operator std::string() const { return to_string(); } - T* el_{nullptr}; + Ptr el_{nullptr}; TemplateSizeType used_size_{0}; TemplateSizeType allocated_size_{0}; bool self_allocated_{false}; diff --git a/include/cista/offset_t.h b/include/cista/offset_t.h index a6682774..91129d5c 100644 --- a/include/cista/offset_t.h +++ b/include/cista/offset_t.h @@ -4,6 +4,8 @@ namespace cista { -using offset_t = uint64_t; +using offset_t = int64_t; + +constexpr auto const NULLPTR_OFFSET = std::numeric_limits::max(); } // namespace cista diff --git a/include/cista/serialization.h b/include/cista/serialization.h index e18903fb..e085e198 100644 --- a/include/cista/serialization.h +++ b/include/cista/serialization.h @@ -15,12 +15,17 @@ namespace cista { +template +using o_vector = vector, TemplateSizeType>; + // ============================================================================= // SERIALIZE // ----------------------------------------------------------------------------- +enum class pointer_type { ABSOLUTE_PTR, RELATIVE_PTR }; struct pending_offset { void* origin_ptr_; offset_t pos_; + pointer_type type_; }; template @@ -45,7 +50,7 @@ template void serialize(Ctx& c, T const* origin, offset_t const pos) { using Type = std::remove_reference_t>; if constexpr (!std::is_scalar_v) { - cista::for_each_ptr_field(*origin, [&](auto& member) { + for_each_ptr_field(*origin, [&](auto& member) { auto const member_offset = static_cast(reinterpret_cast(member) - reinterpret_cast(origin)); @@ -53,28 +58,40 @@ void serialize(Ctx& c, T const* origin, offset_t const pos) { }); } else if constexpr (std::is_pointer_v) { if (*origin == nullptr) { - c.write(pos, std::numeric_limits::max()); - return; - } - if (auto const it = c.offsets_.find(*origin); it != end(c.offsets_)) { + c.write(pos, NULLPTR_OFFSET); + } else if (auto const it = c.offsets_.find(*origin); + it != end(c.offsets_)) { c.write(pos, it->second); } else { - c.pending_.emplace_back(pending_offset{*origin, pos}); + c.pending_.emplace_back( + pending_offset{*origin, pos, pointer_type::ABSOLUTE_PTR}); } } } template -void serialize(Ctx& c, cista::vector const* origin, offset_t const pos) { +void serialize(Ctx& c, offset_ptr const* origin, offset_t const pos) { + if (*origin == nullptr) { + return; + } else if (auto const it = c.offsets_.find(const_cast(origin->get())); + it != end(c.offsets_)) { + c.write(pos, it->second - pos); + } else { + c.pending_.emplace_back(pending_offset{const_cast(origin->get()), pos, + pointer_type::RELATIVE_PTR}); + } +} + +template +void serialize(Ctx& c, vector const* origin, offset_t const pos) { auto const size = sizeof(T) * origin->used_size_; - auto const start = origin->el_ != nullptr - ? c.write(origin->el_, size, std::alignment_of_v) - : std::numeric_limits::max(); + auto const start = origin->el_ == nullptr + ? NULLPTR_OFFSET + : c.write(origin->el_, size, std::alignment_of_v); - c.write(pos + offsetof(cista::vector, el_), start); - c.write(pos + offsetof(cista::vector, allocated_size_), - origin->used_size_); - c.write(pos + offsetof(cista::vector, self_allocated_), false); + c.write(pos + offsetof(vector, el_), start); + c.write(pos + offsetof(vector, allocated_size_), origin->used_size_); + c.write(pos + offsetof(vector, self_allocated_), false); if (origin->el_ != nullptr) { auto i = 0u; @@ -84,27 +101,61 @@ void serialize(Ctx& c, cista::vector const* origin, offset_t const pos) { } } +template +void serialize(Ctx& c, o_vector const* origin, offset_t const pos) { + auto const size = sizeof(T) * origin->used_size_; + auto const start = origin->el_ == nullptr ? NULLPTR_OFFSET + : c.write(origin->el_.get(), size, + std::alignment_of_v); + + c.write(pos + offsetof(o_vector, el_), + start == NULLPTR_OFFSET ? start + : start - offsetof(o_vector, el_) - pos); + + if (origin->el_ != nullptr) { + auto i = 0u; + for (auto it = start; it != start + size; it += sizeof(T)) { + serialize(c, (origin->el_ + i++).get(), it); + } + } +} + +template +void serialize(Ctx& c, string const* origin, offset_t const pos) { + if (origin->is_short()) { + return; + } + + auto const start = (origin->h_.ptr_ == nullptr) + ? NULLPTR_OFFSET + : c.write(origin->data(), origin->size()); + c.write(pos + offsetof(string, h_.ptr_), start); + c.write(pos + offsetof(string, h_.self_allocated_), false); +} + template -void serialize(Ctx& c, cista::string const* origin, offset_t const pos) { +void serialize(Ctx& c, o_string const* origin, offset_t const pos) { if (origin->is_short()) { return; } - auto const start = - c.write(origin->data(), origin->size(), std::alignment_of_v); - c.write(pos + offsetof(cista::string, h_.ptr_), start); - c.write(pos + offsetof(cista::string, h_.self_allocated_), false); + auto const start = (origin->h_.ptr_ == nullptr) + ? NULLPTR_OFFSET + : c.write(origin->data(), origin->size()); + c.write(pos + offsetof(o_string, h_.ptr_), + start == NULLPTR_OFFSET ? start + : start - offsetof(o_string, h_.ptr_) - pos); + c.write(pos + offsetof(o_string, h_.self_allocated_), false); } template -void serialize(Ctx& c, cista::unique_ptr const* origin, offset_t const pos) { - auto const start = - origin->el_ != nullptr - ? c.write(origin->el_, sizeof(T), std::alignment_of_v) - : std::numeric_limits::max(); +void serialize(Ctx& c, unique_ptr const* origin, offset_t const pos) { + auto const start = origin->el_ == nullptr ? NULLPTR_OFFSET + : c.write(origin->el_, sizeof(T), + std::alignment_of_v); - c.write(pos + offsetof(cista::unique_ptr, el_), start); - c.write(pos + offsetof(cista::unique_ptr, self_allocated_), false); + c.write(pos + offsetof(unique_ptr, el_), start); + c.write(pos + offsetof(unique_ptr, self_allocated_), false); if (origin->el_ != nullptr) { c.offsets_[origin->el_] = start; @@ -112,6 +163,24 @@ void serialize(Ctx& c, cista::unique_ptr const* origin, offset_t const pos) { } } +template +void serialize(Ctx& c, o_unique_ptr const* origin, offset_t const pos) { + auto const start = origin->el_ == nullptr ? NULLPTR_OFFSET + : c.write(origin->el_, sizeof(T), + std::alignment_of_v); + + c.write(pos + offsetof(o_unique_ptr, el_), + start == NULLPTR_OFFSET + ? start + : start - offsetof(o_unique_ptr, el_) - pos); + c.write(pos + offsetof(o_unique_ptr, self_allocated_), false); + + if (origin->el_ != nullptr) { + c.offsets_[const_cast(origin->el_.get())] = start; + serialize(c, origin->el_.get(), start); + } +} + template void serialize(Target& t, T& value) { serialization_context c{t}; @@ -125,7 +194,9 @@ void serialize(Target& t, T& value) { for (auto& p : c.pending_) { if (auto const it = c.offsets_.find(p.origin_ptr_); it != end(c.offsets_)) { - c.write(p.pos_, it->second); + c.write(p.pos_, (p.type_ == pointer_type::ABSOLUTE_PTR) + ? it->second + : it->second - p.pos_); } else { std::cout << "warning: dangling pointer " << p.origin_ptr_ << " serialized at offset " << p.pos_ << "\n"; @@ -179,10 +250,8 @@ struct deserialization_context { template T deserialize(Ptr* ptr) const { auto const offset = reinterpret_cast(ptr); - if (offset == std::numeric_limits::max()) { - return nullptr; - } - return reinterpret_cast(from_ + offset); + return offset == NULLPTR_OFFSET ? nullptr + : reinterpret_cast(from_ + offset); } template @@ -213,38 +282,38 @@ void deserialize(deserialization_context const& c, T* el) { } else if constexpr (std::is_scalar_v) { c.check(el, sizeof(T)); } else { - cista::for_each_ptr_field(*el, [&](auto& f) { deserialize(c, f); }); + for_each_ptr_field(*el, [&](auto& f) { deserialize(c, f); }); } } template -void deserialize(deserialization_context const& c, cista::vector* el) { - c.check(el, sizeof(cista::vector)); +void deserialize(deserialization_context const& c, vector* el) { + c.check(el, sizeof(vector)); el->el_ = c.deserialize(el->el_); c.check(el->el_, checked_multiplication( static_cast(el->allocated_size_), sizeof(T))); - c.check(el->allocated_size_ == el->used_size_, "cista::vector size mismatch"); - c.check(!el->self_allocated_, "cista::vector self-allocated"); + c.check(el->allocated_size_ == el->used_size_, "vector size mismatch"); + c.check(!el->self_allocated_, "vector self-allocated"); for (auto& m : *el) { deserialize(c, &m); } } -inline void deserialize(deserialization_context const& c, cista::string* el) { - c.check(el, sizeof(cista::string)); +inline void deserialize(deserialization_context const& c, string* el) { + c.check(el, sizeof(string)); if (!el->is_short()) { el->h_.ptr_ = c.deserialize(el->h_.ptr_); c.check(el->h_.ptr_, el->h_.size_); - c.check(!el->h_.self_allocated_, "cista::string self-allocated"); + c.check(!el->h_.self_allocated_, "string self-allocated"); } } template -void deserialize(deserialization_context const& c, cista::unique_ptr* el) { - c.check(el, sizeof(cista::unique_ptr)); +void deserialize(deserialization_context const& c, unique_ptr* el) { + c.check(el, sizeof(unique_ptr)); el->el_ = c.deserialize(el->el_); c.check(el->el_, sizeof(T)); - c.check(!el->self_allocated_, "cista::unique_ptr self-allocated"); + c.check(!el->self_allocated_, "unique_ptr self-allocated"); deserialize(c, el->el_); } diff --git a/test/offset_graph_test.cc b/test/offset_graph_test.cc new file mode 100644 index 00000000..9e5fdee6 --- /dev/null +++ b/test/offset_graph_test.cc @@ -0,0 +1,128 @@ +#include +#include + +#include "cista.h" + +#include "doctest.h" + +struct o_node; + +using node_id_t = uint32_t; + +struct o_edge { + cista::offset_ptr from_; + cista::offset_ptr to_; +}; + +struct o_node { + void add_edge(o_edge* e) { edges_.emplace_back(e); } + node_id_t id() const { return id_; } + + node_id_t id_{0}; + node_id_t fill_{0}; + cista::o_vector> edges_; + cista::o_string name_; +}; + +struct o_graph { + o_node* make_node(cista::o_string name) { + return nodes_ + .emplace_back(cista::make_o_unique(o_node{ + next_node_id_++, 0, cista::o_vector>{0u}, + std::move(name)})) + .get(); + } + + o_edge* make_edge(node_id_t const from, node_id_t const to) { + return edges_ + .emplace_back(cista::make_o_unique( + o_edge{nodes_[from].get(), nodes_[to].get()})) + .get(); + } + + cista::o_vector> nodes_; + cista::o_vector> edges_; + node_id_t next_node_id_{0}; + node_id_t fill_{0}; +}; + +inline std::set o_bfs(o_node const* entry) { + std::queue q; + std::set visited; + + q.emplace(entry); + + while (!q.empty()) { + auto const next = q.front(); + q.pop(); + + if (visited.find(next) != end(visited)) { + continue; + } + + visited.emplace(next); + + for (auto const& e : next->edges_) { + q.emplace(e->to_); + } + } + + return visited; +} + +TEST_CASE("graph serialization file") { + { + o_graph g; + + auto const n1 = g.make_node(cista::o_string{"NODE A"}); + auto const n2 = g.make_node(cista::o_string{"NODE B"}); + auto const n3 = g.make_node(cista::o_string{"NODE C"}); + + auto const e1 = g.make_edge(n1->id(), n2->id()); + auto const e2 = g.make_edge(n2->id(), n3->id()); + auto const e3 = g.make_edge(n3->id(), n1->id()); + + n1->add_edge(e1); + n2->add_edge(e2); + n3->add_edge(e3); + + cista::sfile f{"test.bin", "wb"}; + cista::serialize(f, g); + } // EOL graph + + auto b = cista::file("test.bin", "r").content(); + auto const g = reinterpret_cast(b.begin()); + auto const visited = o_bfs(g->nodes_[0].get()); + unsigned i = 0; + CHECK((*std::next(begin(visited), i++))->name_ == cista::o_string{"NODE A"}); + CHECK((*std::next(begin(visited), i++))->name_ == cista::o_string{"NODE B"}); + CHECK((*std::next(begin(visited), i++))->name_ == cista::o_string{"NODE C"}); +} + +TEST_CASE("graph serialization buf") { + cista::byte_buf buf; + { + o_graph g; + + auto const n1 = g.make_node(cista::o_string{"NODE A"}); + auto const n2 = g.make_node(cista::o_string{"NODE B"}); + auto const n3 = g.make_node(cista::o_string{"NODE C"}); + + auto const e1 = g.make_edge(n1->id(), n2->id()); + auto const e2 = g.make_edge(n2->id(), n3->id()); + auto const e3 = g.make_edge(n3->id(), n1->id()); + + n1->add_edge(e1); + n2->add_edge(e2); + n3->add_edge(e3); + + buf = cista::serialize(g); + } // EOL graph + + auto const g = reinterpret_cast(&buf[0]); + auto const visited = o_bfs(g->nodes_[0].get()); + unsigned i = 0; + CHECK((*std::next(begin(visited), i++))->name_ == cista::o_string{"NODE A"}); + CHECK((*std::next(begin(visited), i++))->name_ == cista::o_string{"NODE B"}); + CHECK((*std::next(begin(visited), i++))->name_ == cista::o_string{"NODE C"}); +} diff --git a/test/offset_ptr_vector_test.cc b/test/offset_ptr_vector_test.cc new file mode 100644 index 00000000..5982d7f6 --- /dev/null +++ b/test/offset_ptr_vector_test.cc @@ -0,0 +1,90 @@ +#include "doctest.h" + +#include "cista.h" + +TEST_CASE("offset vector serialize") { + cista::byte_buf buf; + { + cista::o_vector vec; + vec.push_back(1); + vec.push_back(2); + vec.push_back(3); + vec.push_back(4); + + int j = 1; + for (auto const i : vec) { + CHECK(i == j++); + } + + buf = serialize(vec); + } + + auto const vec = reinterpret_cast*>(&buf[0]); + int j = 1; + for (auto const i : *vec) { + CHECK(i == j++); + } +} + +TEST_CASE("offset string serialize") { + constexpr auto const s = "The quick brown fox jumps over the lazy dog"; + + cista::byte_buf buf; + { + cista::o_string str{s}; + buf = serialize(str); + } + + auto const str = reinterpret_cast(&buf[0]); + CHECK(*str == s); +} + +TEST_CASE("offset unique_ptr serialize") { + cista::byte_buf buf; + { + auto const ptr = cista::make_o_unique(33); + buf = serialize(ptr); + } + + auto const ptr = reinterpret_cast*>(&buf[0]); + CHECK(**ptr == 33); +} + +TEST_CASE("offset_ptr serialize") { + struct serialize_me { + cista::o_unique_ptr i_{cista::make_o_unique(77)}; + cista::offset_ptr raw_{i_.get()}; + }; + + cista::byte_buf buf; + + { + serialize_me obj; + buf = cista::serialize(obj); + } // EOL obj + + auto const serialized = reinterpret_cast(&buf[0]); + CHECK(serialized->raw_.get() == serialized->i_.get()); + CHECK(*serialized->raw_ == 77); + CHECK(*serialized->i_.get() == 77); +} + +TEST_CASE("offset_ptr serialize pending") { + struct serialize_me { + cista::offset_ptr raw_; + cista::o_unique_ptr i_{cista::make_o_unique(77)}; + }; + + cista::byte_buf buf; + + { + serialize_me obj; + obj.raw_ = obj.i_.get(); + buf = cista::serialize(obj); + } // EOL obj + + auto const serialized = reinterpret_cast(&buf[0]); + CHECK(serialized->raw_.get() == serialized->i_.get()); + CHECK(*serialized->raw_ == 77); + CHECK(*serialized->i_.get() == 77); +} \ No newline at end of file diff --git a/test/pointer_test.cc b/test/pointer_test.cc index c8b5b367..70fc44e6 100644 --- a/test/pointer_test.cc +++ b/test/pointer_test.cc @@ -15,8 +15,7 @@ TEST_CASE("pointer serialization") { buf = cista::serialize(obj); } // EOL obj - auto const serialized = - cista::deserialize(&buf[0], &buf[0] + buf.size()); + auto const serialized = cista::deserialize(buf); CHECK(serialized->raw_ == serialized->i_.get()); CHECK(*serialized->raw_ == 77); CHECK(*serialized->i_.get() == 77);