-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Clean up [de]serialization api and support different result types.
- Loading branch information
Showing
20 changed files
with
764 additions
and
288 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package(default_visibility = ["//visibility:public"]) | ||
|
||
cc_library( | ||
name = "deserialize", | ||
hdrs = ["deserialize.h"], | ||
deps = [ | ||
"//nth/io/deserialize/internal:deserialize", | ||
"//nth/io/internal:sequence", | ||
"//nth/container:free_functions", | ||
"//nth/meta:concepts", | ||
"//nth/io/reader", | ||
"//nth/utility:bytes", | ||
], | ||
) | ||
|
||
cc_test( | ||
name = "deserialize_test", | ||
srcs = ["deserialize_test.cc"], | ||
deps = [ | ||
":deserialize", | ||
"//nth/base:platform", | ||
"//nth/test:main", | ||
], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
#ifndef NTH_IO_DESERIALIZE_DESERIALIZE_H | ||
#define NTH_IO_DESERIALIZE_DESERIALIZE_H | ||
|
||
#include <climits> | ||
#include <concepts> | ||
#include <cstddef> | ||
#include <cstdint> | ||
#include <cstring> | ||
#include <optional> | ||
#include <span> | ||
#include <type_traits> | ||
|
||
#include "nth/container/free_functions.h" | ||
#include "nth/io/deserialize/internal/deserialize.h" | ||
#include "nth/io/internal/sequence.h" | ||
#include "nth/io/reader/reader.h" | ||
#include "nth/meta/concepts.h" | ||
#include "nth/utility/bytes.h" | ||
|
||
namespace nth::io { | ||
|
||
// An alias template that extracts the result type associated with the | ||
// deserializer `D`. By default, the result type is `bool`, where `true` | ||
// indicates successful deserialization. Users may override this choice by | ||
// adding a member type named `nth_deserializer_result_type` to their | ||
// deserializer. The type must be convertible to `bool` in such a way that | ||
// deserialization is understood to be successful if and only if the result | ||
// value converts to `true`. | ||
template <typename D> | ||
using deserializer_result_type = | ||
internal_deserialize::deserializer_result<D>::type; | ||
|
||
// A concept indicating that a type `T` can be deserialized with a deserializer | ||
// `D`. | ||
template <typename T, typename D> | ||
concept deserializable_with = requires(D& d, T& value) { | ||
{ | ||
NthDeserialize(d, value) | ||
} -> std::convertible_to<deserializer_result_type<D>>; | ||
}; | ||
|
||
// Deserializes a sequence of `values...` with the deserializer `D`, one | ||
// immediately after the other. | ||
template <typename D> | ||
deserializer_result_type<D> | ||
deserialize(D& d, deserializable_with<D> auto&&... values) requires( | ||
lvalue_proxy<decltype(values)>and...) { | ||
deserializer_result_type<D> result; | ||
(void)(static_cast<bool>(result = NthDeserialize(d, values)) and ...); | ||
return result; | ||
} | ||
|
||
// Deserializes a sequence of `Seq::value_type` into the passed-in `sequence`, | ||
// formatted as a count of the number of elements followed by serializations of | ||
// that many elements. | ||
template <typename D, typename Seq> | ||
deserializer_result_type<D> NthDeserialize( | ||
D& d, as_sequence<Seq&> sequence) requires(not std::is_const_v<Seq>) { | ||
auto& seq = sequence.ref(); | ||
using size_type = decltype(std::size(seq)); | ||
size_type seq_size; | ||
if (not nth::io::read_integer(d, seq_size)) { return false; } | ||
|
||
if constexpr (requires { nth::reserve(seq, seq_size); }) { | ||
nth::reserve(seq, seq_size); | ||
} | ||
|
||
using value_type = std::decay_t<decltype(seq)>::value_type; | ||
if constexpr (requires { | ||
{ nth::emplace_back(seq) } -> std::same_as<value_type&>; | ||
}) { | ||
for (size_type i = 0; i < seq_size; ++i) { | ||
if (not nth::io::deserialize(d, nth::emplace_back(seq))) { return false; } | ||
} | ||
} else if constexpr (requires { | ||
{ nth::emplace(seq) } -> std::same_as<value_type&>; | ||
}) { | ||
for (size_type i = 0; i < seq_size; ++i) { | ||
if (not nth::io::deserialize(d, nth::emplace(seq))) { return false; } | ||
} | ||
} else { | ||
value_type element; | ||
for (size_type i = 0; i < seq_size; ++i) { | ||
if (not nth::io::deserialize(d, element)) { return false; } | ||
nth::insert(seq, element); | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
} // namespace nth::io | ||
|
||
#endif // NTH_IO_DESERIALIZE_DESERIALIZE_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
#include "nth/io/deserialize/deserialize.h" | ||
|
||
#include <array> | ||
#include <coroutine> | ||
#include <cstddef> | ||
#include <string> | ||
#include <utility> | ||
|
||
#include "nth/base/platform.h" | ||
#include "nth/test/test.h" | ||
|
||
namespace nth::io { | ||
namespace { | ||
|
||
struct Thing { | ||
int value = 0; | ||
}; | ||
|
||
struct BasicDeserializer { | ||
int get_value() { return value_++; } | ||
|
||
private: | ||
int value_ = 0; | ||
}; | ||
|
||
struct BoolReturningDeserializer : BasicDeserializer {}; | ||
|
||
struct result_type { | ||
explicit result_type() : message_("unknown failure") {} | ||
explicit result_type(std::string message) | ||
: message_(std::move(message)) {} | ||
static result_type success() { return result_type(""); } | ||
|
||
operator bool() const { return message_.empty(); }; | ||
|
||
friend void NthPrint(auto& p, auto& , result_type const &r) { | ||
p.write("result["); | ||
p.write(r.message_); | ||
p.write("]"); | ||
} | ||
|
||
struct promise_type; | ||
|
||
bool await_ready() { return static_cast<bool>(*this); } | ||
void await_suspend(std::coroutine_handle<promise_type>); | ||
static constexpr void await_resume() {} | ||
|
||
private: | ||
friend struct result_promise_return_type; | ||
|
||
result_type(result_type*& location) { location = this; } | ||
std::string message_; | ||
}; | ||
|
||
struct result_promise_return_type { | ||
result_promise_return_type(result_type::promise_type& promise); | ||
result_promise_return_type(result_promise_return_type&&) = delete; | ||
result_promise_return_type(result_promise_return_type const&) = delete; | ||
result_promise_return_type& operator=(result_promise_return_type&&) = delete; | ||
result_promise_return_type& operator=(result_promise_return_type const&) = | ||
delete; | ||
operator result_type() { | ||
// TODO: This assumes eager conversion `get_return_object()` to the actual | ||
// result type. | ||
return result_type(pointer_); | ||
} | ||
|
||
private: | ||
result_type*& pointer_; | ||
}; | ||
|
||
struct result_type::promise_type { | ||
result_promise_return_type get_return_object() { return *this; } | ||
static constexpr std::suspend_never initial_suspend() noexcept { return {}; } | ||
static constexpr std::suspend_never final_suspend() noexcept { return {}; } | ||
static constexpr void unhandled_exception() noexcept {} | ||
void return_value(result_type value) { set(std::move(value)); } | ||
|
||
void set(result_type result) { *value_ = std::move(result); } | ||
|
||
private: | ||
friend result_promise_return_type; | ||
result_type* value_; | ||
}; | ||
|
||
result_promise_return_type::result_promise_return_type( | ||
result_type::promise_type& promise) | ||
: pointer_(promise.value_) {} | ||
|
||
void result_type::await_suspend(std::coroutine_handle<promise_type> h) { | ||
h.promise().set(std::move(*this)); | ||
h.destroy(); | ||
} | ||
|
||
struct ResultReturningDeserializer : BasicDeserializer { | ||
using nth_deserializer_result_type = result_type; | ||
}; | ||
|
||
bool NthDeserialize(BoolReturningDeserializer& d, Thing& t) { | ||
t.value = d.get_value(); | ||
return t.value < 5; | ||
} | ||
|
||
NTH_TEST("deserialize/bool-result") { | ||
BoolReturningDeserializer d; | ||
Thing t1{.value = -1}; | ||
Thing t2{.value = -1}; | ||
|
||
NTH_EXPECT(deserialize(d, t1)); | ||
NTH_EXPECT(t1.value == 0); | ||
|
||
NTH_EXPECT(deserialize(d, t1)); | ||
NTH_EXPECT(t1.value == 1); | ||
|
||
NTH_EXPECT(deserialize(d, t1, t2)); | ||
NTH_EXPECT(t1.value == 2); | ||
NTH_EXPECT(t2.value == 3); | ||
|
||
NTH_EXPECT(not deserialize(d, t1, t2)); | ||
} | ||
|
||
result_type NthDeserialize(ResultReturningDeserializer& d, Thing& t) { | ||
t.value = d.get_value(); | ||
return result_type(t.value < 5 ? "" : "failure"); | ||
} | ||
|
||
NTH_TEST("deserialize/custom-result") { | ||
ResultReturningDeserializer d; | ||
Thing t1{.value = -1}; | ||
Thing t2{.value = -1}; | ||
|
||
NTH_EXPECT(deserialize(d, t1) == true); | ||
NTH_EXPECT(t1.value == 0); | ||
|
||
NTH_EXPECT(deserialize(d, t1) == true); | ||
NTH_EXPECT(t1.value == 1); | ||
|
||
NTH_EXPECT(deserialize(d, t1, t2) == true); | ||
NTH_EXPECT(t1.value == 2); | ||
NTH_EXPECT(t2.value == 3); | ||
|
||
NTH_EXPECT(deserialize(d, t1, t2) == false); | ||
} | ||
|
||
template <size_t N> | ||
result_type NthDeserialize(ResultReturningDeserializer& d, | ||
std::array<Thing, N>& ts) { | ||
for (size_t i = 0; i < N; ++i) { co_await deserialize(d, ts[i]); } | ||
co_return result_type::success(); | ||
} | ||
|
||
NTH_TEST("deserialize/coroutine") { | ||
ResultReturningDeserializer d; | ||
std::array<Thing, 2> things{ | ||
Thing{.value = -1}, | ||
Thing{.value = -1}, | ||
}; | ||
|
||
std::array<Thing, 4> more_things{ | ||
Thing{.value = -1}, | ||
Thing{.value = -1}, | ||
Thing{.value = -1}, | ||
Thing{.value = -1}, | ||
}; | ||
|
||
NTH_EXPECT(deserialize(d, things) == true); | ||
NTH_EXPECT(things[0].value == 0); | ||
NTH_EXPECT(things[1].value == 1); | ||
|
||
NTH_EXPECT(deserialize(d, more_things) == false); | ||
} | ||
|
||
} // namespace | ||
} // namespace nth::io |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package(default_visibility = ["//nth/io/deserialize:__subpackages__"]) | ||
|
||
cc_library( | ||
name = "deserialize", | ||
hdrs = ["deserialize.h"], | ||
deps = [], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
#ifndef NTH_IO_DESERIALIZE_INTERNAL_DESERIALIZE_H | ||
#define NTH_IO_DESERIALIZE_INTERNAL_DESERIALIZE_H | ||
|
||
#include <type_traits> | ||
|
||
namespace nth::io::internal_deserialize { | ||
|
||
template <typename D> | ||
struct deserializer_result { | ||
using type = bool; | ||
}; | ||
|
||
template <typename D> | ||
requires requires { typename D::nth_deserializer_result_type; } | ||
struct deserializer_result<D> { | ||
using type = D::nth_deserializer_result_type; | ||
static_assert(std::is_convertible_v<type, bool>); | ||
}; | ||
|
||
} // namespace nth::io::internal_deserialize | ||
|
||
#endif // NTH_IO_DESERIALIZE_INTERNAL_DESERIALIZE_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package(default_visibility = ["//nth/io:__subpackages__"]) | ||
|
||
cc_library( | ||
name = "sequence", | ||
hdrs = ["sequence.h"], | ||
deps = [], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.