aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--libnvxx/Makefile4
-rw-r--r--libnvxx/nvxx.cc9
-rw-r--r--libnvxx/nvxx.h4
-rw-r--r--libnvxx/nvxx_base.h10
-rw-r--r--libnvxx/nvxx_iterator.cc2
-rw-r--r--libnvxx/nvxx_iterator.h4
-rw-r--r--libnvxx/nvxx_serialize.cc130
-rw-r--r--libnvxx/nvxx_serialize.h381
-rw-r--r--libnvxx/tests/Makefile2
-rw-r--r--libnvxx/tests/nvxx_serialize.cc247
11 files changed, 788 insertions, 6 deletions
diff --git a/.gitignore b/.gitignore
index 9da6e2b..1af43ec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,4 @@
Kyuafile
/libnvxx/tests/nvxx_basic
/libnvxx/tests/nvxx_iterator
+/libnvxx/tests/nvxx_serialize
diff --git a/libnvxx/Makefile b/libnvxx/Makefile
index 966b4db..ed412e1 100644
--- a/libnvxx/Makefile
+++ b/libnvxx/Makefile
@@ -25,8 +25,8 @@ LIB_CXX= nvxx
LIBDIR= ${PREFIX}/lib
INCLUDEDIR= ${PREFIX}/include
SHLIB_MAJOR= 1
-INCS= nvxx.h nvxx_base.h
-SRCS= nvxx.cc nvxx_iterator.cc
+INCS= nvxx.h nvxx_base.h nvxx_serialize.h
+SRCS= nvxx.cc nvxx_iterator.cc nvxx_serialize.cc
CXXSTD= c++23
CXXFLAGS+= -W -Wall -Wextra -Werror
LDADD= -lnv
diff --git a/libnvxx/nvxx.cc b/libnvxx/nvxx.cc
index 7c1db5b..69b57f5 100644
--- a/libnvxx/nvxx.cc
+++ b/libnvxx/nvxx.cc
@@ -115,6 +115,15 @@ nv_list::nv_list(::nvlist_t *nvl) noexcept
{
}
+nv_list::nv_list(const_nv_list const &other)
+ : __nv_list_base(::nvlist_clone(other.ptr()),
+ __detail::__nvlist_owning::__owning)
+{
+ if (__m_nv == nullptr)
+ throw std::system_error(
+ std::error_code(errno, std::system_category()));
+}
+
nv_list::nv_list(nv_list const &other)
: __nv_list_base(::nvlist_clone(other.__m_nv),
__detail::__nvlist_owning::__owning)
diff --git a/libnvxx/nvxx.h b/libnvxx/nvxx.h
index 85eb684..53c95cc 100644
--- a/libnvxx/nvxx.h
+++ b/libnvxx/nvxx.h
@@ -27,7 +27,11 @@
* nvl: lightweight wrapper around nv(9).
*/
+#include <sys/nv.h>
+#include <sys/cnv.h>
+
#include "nvxx_base.h"
#include "nvxx_iterator.h"
+#include "nvxx_serialize.h"
#endif /* !_NVXX_H_INCLUDED */
diff --git a/libnvxx/nvxx_base.h b/libnvxx/nvxx_base.h
index 545caca..aa0a765 100644
--- a/libnvxx/nvxx_base.h
+++ b/libnvxx/nvxx_base.h
@@ -23,7 +23,9 @@
#ifndef _NVXX_BASE_H_INCLUDED
#define _NVXX_BASE_H_INCLUDED
-#include <sys/nv.h>
+#ifndef _NVXX_H_INCLUDED
+# error include <nvxx.h> instead of including this header directly
+#endif
#include <expected>
#include <ranges>
@@ -484,6 +486,12 @@ struct nv_list final
explicit nv_list(::nvlist_t *) noexcept;
/*
+ * Create an nv_list object by copying an existing const_nv_list object
+ * with nvlist_clone(). On failure, throws std::system_error.
+ */
+ explicit nv_list(const_nv_list const &);
+
+ /*
* Create an nv_list by copying an existing nv_list object with
* nvlist_clone(). On failure, throws std::system_error.
*/
diff --git a/libnvxx/nvxx_iterator.cc b/libnvxx/nvxx_iterator.cc
index 7312152..692594a 100644
--- a/libnvxx/nvxx_iterator.cc
+++ b/libnvxx/nvxx_iterator.cc
@@ -20,8 +20,6 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-#include <sys/cnv.h>
-
#include "nvxx.h"
namespace bsd {
diff --git a/libnvxx/nvxx_iterator.h b/libnvxx/nvxx_iterator.h
index c65f546..4c780ef 100644
--- a/libnvxx/nvxx_iterator.h
+++ b/libnvxx/nvxx_iterator.h
@@ -23,6 +23,10 @@
#ifndef _NVXX_ITERATOR_H_INCLUDED
#define _NVXX_ITERATOR_H_INCLUDED
+#ifndef _NVXX_H_INCLUDED
+# error include <nvxx.h> instead of including this header directly
+#endif
+
#include <variant>
#include <iterator>
#include <concepts>
diff --git a/libnvxx/nvxx_serialize.cc b/libnvxx/nvxx_serialize.cc
new file mode 100644
index 0000000..8676467
--- /dev/null
+++ b/libnvxx/nvxx_serialize.cc
@@ -0,0 +1,130 @@
+/*
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or distribute
+ * this software, either in source code form or as a compiled binary, for any
+ * purpose, commercial or non-commercial, and by any means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors of
+ * this software dedicate any and all copyright interest in the software to the
+ * public domain. We make this dedication for the benefit of the public at
+ * large and to the detriment of our heirs and successors. We intend this
+ * dedication to be an overt act of relinquishment in perpetuity of all present
+ * and future rights to this software under copyright law.
+ *
+ * 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 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.
+ */
+
+#include "nvxx.h"
+
+namespace bsd {
+
+/*
+ * type encoders/decoders
+ */
+
+/* bool */
+
+void
+nv_encoder<bool>::encode(nv_list &nvl, std::string_view key, bool value)
+{
+ nvl.add_bool(key, value);
+}
+
+bool
+nv_decoder<bool>::decode(const_nv_list const &nvl, std::string_view key)
+{
+ return (nvl.get_bool(key));
+}
+
+/* uint64_t */
+
+void
+nv_encoder<std::uint64_t>::encode(nv_list &nvl,
+ std::string_view key,
+ std::uint64_t value)
+{
+ nvl.add_number(key, value);
+}
+
+std::uint64_t
+nv_decoder<std::uint64_t>::decode(const_nv_list const &nvl,
+ std::string_view key)
+{
+ return (nvl.get_number(key));
+}
+
+/* string */
+
+void
+nv_encoder<std::string>::encode(nv_list &nvl,
+ std::string_view key,
+ std::string const &value)
+{
+ nvl.add_string(key, value);
+}
+
+std::string
+nv_decoder<std::string>::decode(const_nv_list const &nvl,
+ std::string_view key)
+{
+ return (std::string(nvl.get_string(key)));
+}
+
+/* string_view */
+
+void
+nv_encoder<std::string_view>::encode(nv_list &nvl,
+ std::string_view key,
+ std::string_view value)
+{
+ nvl.add_string(key, value);
+}
+
+std::string_view
+nv_decoder<std::string_view>::decode(const_nv_list const &nvl,
+ std::string_view key)
+{
+ return (nvl.get_string(key));
+}
+
+/* nv_list */
+
+void
+nv_encoder<nv_list>::encode(nv_list &nvl,
+ std::string_view key,
+ nv_list const &value)
+{
+ nvl.add_nvlist(key, value);
+}
+
+nv_list
+nv_decoder<nv_list>::decode(const_nv_list const &nvl,
+ std::string_view key)
+{
+ return (nv_list(nvl.get_nvlist(key)));
+}
+
+/* const_nv_list */
+
+void
+nv_encoder<const_nv_list>::encode(nv_list &nvl,
+ std::string_view key,
+ const_nv_list const &value)
+{
+ nvl.add_nvlist(key, value);
+}
+
+const_nv_list
+nv_decoder<const_nv_list>::decode(const_nv_list const &nvl,
+ std::string_view key)
+{
+ return (nvl.get_nvlist(key));
+}
+
+} // namespace bsd
diff --git a/libnvxx/nvxx_serialize.h b/libnvxx/nvxx_serialize.h
new file mode 100644
index 0000000..37deab6
--- /dev/null
+++ b/libnvxx/nvxx_serialize.h
@@ -0,0 +1,381 @@
+/*
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or distribute
+ * this software, either in source code form or as a compiled binary, for any
+ * purpose, commercial or non-commercial, and by any means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors of
+ * this software dedicate any and all copyright interest in the software to the
+ * public domain. We make this dedication for the benefit of the public at
+ * large and to the detriment of our heirs and successors. We intend this
+ * dedication to be an overt act of relinquishment in perpetuity of all present
+ * and future rights to this software under copyright law.
+ *
+ * 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 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 _NVXX_SERIALIZE_H
+#define _NVXX_SERIALIZE_H
+
+#ifndef _NVXX_H_INCLUDED
+# error include <nvxx.h> instead of including this header directly
+#endif
+
+#include <string_view>
+#include <string>
+#include <vector>
+#include <span>
+#include <optional>
+#include <ranges>
+
+namespace bsd {
+
+namespace __detail {
+
+template<typename _R, typename _T>
+concept __range_of = std::ranges::range<_R> &&
+ std::same_as<_T,
+ std::remove_cvref_t<std::ranges::range_value_t<_R>>>;
+
+template<typename _Container, typename _T>
+concept __from_range_container_of =
+ __range_of<_Container, _T>
+ && (std::move_constructible<_Container>
+ || std::copy_constructible<_Container>)
+ && requires (std::span<_T> __values) {
+ _Container(std::from_range, __values);
+ };
+
+template<typename _Container, typename _T>
+concept __push_back_container_of =
+ __range_of<_Container, _T>
+ && std::default_initializable<_Container>
+ && (std::move_constructible<_Container>
+ || std::copy_constructible<_Container>)
+ && requires (_Container &__c, _T __value) {
+ __c.push_back(__value);
+ };
+
+} // namespace __detail
+
+/*
+ * Encoders/decoders for basic types.
+ */
+
+template<typename T>
+struct nv_encoder;
+template<typename T>
+struct nv_decoder;
+
+/* bool */
+
+template<>
+struct nv_encoder<bool> {
+ void encode(nv_list &, std::string_view, bool);
+};
+
+template<__detail::__range_of<bool> _R>
+struct nv_encoder<_R> {
+ template<typename _U>
+ void encode(nv_list &__nvl, std::string_view __key, _U &&__range) {
+ __nvl.add_bool_range(__key, std::forward<_U>(__range));
+ }
+};
+
+template<>
+struct nv_decoder<bool> {
+ auto decode(const_nv_list const &, std::string_view) -> bool;
+};
+
+template<__detail::__from_range_container_of<bool> _C>
+struct nv_decoder<_C> {
+ auto decode(const_nv_list const &__nvl, std::string_view __key) -> _C {
+ return _C(std::from_range, __nvl.get_bool_array(__key));
+ }
+};
+
+/* uint64_t */
+
+template<>
+struct nv_encoder<std::uint64_t> {
+ void encode(nv_list &, std::string_view, std::uint64_t);
+};
+
+template<__detail::__range_of<std::uint64_t> _R>
+struct nv_encoder<_R> {
+ template<typename _U>
+ void encode(nv_list &__nvl, std::string_view __key, _U &&__range) {
+ __nvl.add_number_range(__key, std::forward<_U>(__range));
+ }
+};
+
+template<>
+struct nv_decoder<std::uint64_t> {
+ auto decode(const_nv_list const &, std::string_view) -> std::uint64_t;
+};
+
+template<__detail::__from_range_container_of<std::uint64_t> _C>
+struct nv_decoder<_C> {
+ auto decode(const_nv_list const &__nvl, std::string_view __key) -> _C {
+ return _C(std::from_range, __nvl.get_number_array(__key));
+ }
+};
+
+/* string */
+
+template<>
+struct nv_encoder<std::string> {
+ void encode(nv_list &, std::string_view, std::string const &);
+};
+
+template<__detail::__range_of<std::string> _R>
+struct nv_encoder<_R> {
+ template<typename _U>
+ void encode(nv_list &__nvl, std::string_view __key, _U &&__range) {
+ __nvl.add_string_range(__key,
+ __range | std::views::transform([] (auto const &__s) {
+ return std::string_view(__s);
+ }));
+ }
+};
+
+template<>
+struct nv_decoder<std::string> {
+ auto decode(const_nv_list const &, std::string_view) -> std::string;
+};
+
+template<__detail::__from_range_container_of<std::string> _C>
+struct nv_decoder<_C> {
+ auto decode(const_nv_list const &__nvl, std::string_view __key) -> _C {
+ return _C(std::from_range,
+ __nvl.get_string_array(__key)
+ | std::views::transform([] (auto const &__s) {
+ return std::string(__s);
+ }));
+ }
+};
+
+/* string_view */
+
+template<>
+struct nv_encoder<std::string_view> {
+ void encode(nv_list &, std::string_view, std::string_view);
+};
+
+template<__detail::__range_of<std::string_view> _R>
+struct nv_encoder<_R> {
+ template<typename _U>
+ void encode(nv_list &__nvl, std::string_view __key, _U &&__range) {
+ __nvl.add_string_range(__key, std::forward<_U>(__range));
+ }
+};
+
+template<>
+struct nv_decoder<std::string_view> {
+ auto decode(const_nv_list const &, std::string_view)
+ -> std::string_view;
+};
+
+template<__detail::__from_range_container_of<std::string_view> _C>
+struct nv_decoder<_C> {
+ auto decode(const_nv_list const &__nvl, std::string_view __key) -> _C {
+ return _C(std::from_range, __nvl.get_string_array(__key));
+ }
+};
+
+/* nv_list */
+
+template<>
+struct nv_encoder<nv_list> {
+ void encode(nv_list &, std::string_view, nv_list const &);
+};
+
+#ifdef notyet // XXX: implement add_nvlist_range()
+template<__detail::__range_of<nv_list> _R>
+struct nv_encoder<_R> {
+ template<typename _U>
+ void encode(nv_list &__nvl, std::string_view __key, _U &&__range) {
+ __nvl.add_nvlist_range(__key, std::forward<_U>(__range));
+ }
+};
+#endif
+
+template<>
+struct nv_decoder<nv_list> {
+ auto decode(const_nv_list const &, std::string_view) -> nv_list;
+};
+
+template<__detail::__from_range_container_of<nv_list> _C>
+struct nv_decoder<_C> {
+ auto decode(const_nv_list const &__nvl, std::string_view __key) -> _C {
+ return _C(std::from_range,
+ __nvl.get_nvlist_array(__key)
+ | std::views::transform([] (auto const &__s) {
+ return nv_list(__s);
+ }));
+ }
+};
+
+/* const_nv_list */
+
+template<>
+struct nv_encoder<const_nv_list> {
+ void encode(nv_list &, std::string_view, const_nv_list const &);
+};
+
+#ifdef notyet // XXX: implement add_nvlist_range()
+template<__detail::__range_of<const_nv_list> _R>
+struct nv_encoder<_R> {
+ template<typename _U>
+ void encode(nv_list &__nvl, std::string_view __key, _U &&__range) {
+ __nvl.add_nvlist_range(__key, std::forward<_U>(__range));
+ }
+};
+#endif
+
+template<>
+struct nv_decoder<const_nv_list> {
+ auto decode(const_nv_list const &, std::string_view) -> const_nv_list;
+};
+
+template<__detail::__from_range_container_of<const_nv_list> _C>
+struct nv_decoder<_C> {
+ auto decode(const_nv_list const &__nvl, std::string_view __key) -> _C {
+ return _C(std::from_range, __nvl.get_nvlist_array(__key));
+ }
+};
+
+/* optional<T> */
+
+template<typename _T>
+struct nv_encoder<std::optional<_T>> {
+ auto encode(nv_list &__nvl,
+ std::string_view __key,
+ std::optional<_T> const &__value) {
+ if (__value)
+ nv_encoder<_T>{}.encode(__nvl, __key, *__value);
+ }
+};
+
+template<typename _T>
+struct nv_decoder<std::optional<_T>> {
+ auto decode(const_nv_list const &__nvl,
+ std::string_view __key) -> std::optional<_T> {
+ if (__nvl.exists(__key))
+ return {nv_decoder<_T>{}.decode(__nvl, __key)};
+ else
+ return {};
+ }
+};
+
+/*
+ * object (de)serialization
+ */
+
+template<typename _Object, typename _Member>
+struct nv_field {
+ nv_field(std::string __name, _Member _Object::* __ptr)
+ : __field_name(__name)
+ , __field_ptr(__ptr)
+ {
+ }
+
+ auto serialize(nv_list &__nvl, _Object const &__object) {
+ nv_encoder<_Member>{}.encode(__nvl,
+ __field_name,
+ __object.*__field_ptr);
+ }
+
+ auto deserialize(const_nv_list const &__nvl, _Object &__object) {
+ __object.*__field_ptr = nv_decoder<_Member>{}
+ .decode(__nvl, __field_name);
+ }
+
+private:
+ std::string __field_name;
+ _Member _Object::* __field_ptr;
+};
+
+namespace __detail {
+
+template<typename _First, typename _Second>
+struct __field_sequence {
+ __field_sequence(_First __first_, _Second __second_)
+ : __first(__first_)
+ , __second(__second_)
+ {
+ }
+
+ template<typename _Object>
+ auto serialize(nv_list &__nvl, _Object const &__object) {
+ __first.serialize(__nvl, __object);
+ __second.serialize(__nvl, __object);
+ }
+
+ template<typename _Object>
+ auto deserialize(const_nv_list const &__nvl, _Object &__object) {
+ __first.deserialize(__nvl, __object);
+ __second.deserialize(__nvl, __object);
+ }
+
+private:
+ _First __first;
+ _Second __second;
+};
+
+} // namespace __detail
+
+template<typename _Object, typename _M1, typename _M2>
+auto operator>> (nv_field<_Object, _M1> const &__f1,
+ nv_field<_Object, _M2> const &__f2)
+{
+ return __detail::__field_sequence(__f1, __f2);
+}
+
+template<typename _Object, typename _M, typename __first, typename __second>
+auto operator>> (
+ __detail::__field_sequence<__first, __second> const &__f1,
+ nv_field<_Object, _M> const &__f2
+ )
+{
+ return __detail::__field_sequence(__f1, __f2);
+}
+
+template<typename _Object, typename _Member>
+nv_field(std::string_view, _Member _Object::*)
+ -> nv_field<std::decay_t<_Object>, _Member>;
+
+template<typename _T>
+struct nv_schema;
+
+template<typename _T>
+auto nv_serialize(_T &&__o) -> nv_list
+{
+ using __schema_type = nv_schema<std::remove_cvref_t<_T>>;
+ auto __schema = __schema_type{}.get();
+ auto __nvl = nv_list();
+
+ __schema.serialize(__nvl, __o);
+ return __nvl;
+}
+
+template<typename _T>
+auto nv_deserialize(const_nv_list const &__nvl) -> _T
+{
+ using __schema_type = nv_schema<std::remove_cvref_t<_T>>;
+ auto __schema = __schema_type{}.get();
+
+ auto __obj = _T();
+ __schema.deserialize(__nvl, __obj);
+ return __obj;
+}
+
+} // namespace bsd
+
+#endif /* !_NVXX_SERIALIZE_H */
diff --git a/libnvxx/tests/Makefile b/libnvxx/tests/Makefile
index db4b538..af44f03 100644
--- a/libnvxx/tests/Makefile
+++ b/libnvxx/tests/Makefile
@@ -20,7 +20,7 @@
PREFIX?= /usr/local
TESTSDIR?= ${PREFIX}/tests/nvxx
-ATF_TESTS_CXX= nvxx_basic nvxx_iterator
+ATF_TESTS_CXX= nvxx_basic nvxx_iterator nvxx_serialize
CXXSTD= c++23
# Note that we can't use -Werror here because it breaks ATF.
CXXFLAGS+= -W -Wall -Wextra
diff --git a/libnvxx/tests/nvxx_serialize.cc b/libnvxx/tests/nvxx_serialize.cc
new file mode 100644
index 0000000..e3fbd5b
--- /dev/null
+++ b/libnvxx/tests/nvxx_serialize.cc
@@ -0,0 +1,247 @@
+/*
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or distribute
+ * this software, either in source code form or as a compiled binary, for any
+ * purpose, commercial or non-commercial, and by any means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors of
+ * this software dedicate any and all copyright interest in the software to the
+ * public domain. We make this dedication for the benefit of the public at
+ * large and to the detriment of our heirs and successors. We intend this
+ * dedication to be an overt act of relinquishment in perpetuity of all present
+ * and future rights to this software under copyright law.
+ *
+ * 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 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.
+ */
+
+#include <algorithm>
+#include <ranges>
+#include <list>
+#include <vector>
+#include <span>
+#include <string>
+#include <string_view>
+
+#include <atf-c++.hpp>
+
+#include "nvxx.h"
+
+#define TEST_CASE(name) \
+ ATF_TEST_CASE_WITHOUT_HEAD(name) \
+ ATF_TEST_CASE_BODY(name)
+
+using namespace bsd;
+
+/*
+ * bool
+ */
+
+TEST_CASE(nv_encoder_bool)
+{
+ auto v = true;
+ auto nvl = nv_list{};
+
+ nv_encoder<bool>{}.encode(nvl, "test", v);
+
+ auto v2 = nv_decoder<bool>{}.decode(nvl, "test");
+ ATF_REQUIRE_EQ(v, v2);
+}
+
+TEST_CASE(nv_encoder_bool_vector)
+{
+ auto v = std::vector<bool>{true, false, false, true};
+ auto nvl = nv_list{};
+
+ nv_encoder<std::vector<bool>>{}.encode(nvl, "test", v);
+
+ auto v2 = nv_decoder<std::vector<bool>>{}.decode(nvl, "test");
+ ATF_REQUIRE_EQ(true, std::ranges::equal(v, v2));
+}
+
+/*
+ * std::uint64_t
+ */
+
+TEST_CASE(nv_encoder_uint64)
+{
+ auto v = std::uint64_t{42};
+ auto nvl = nv_list{};
+
+ nv_encoder<std::uint64_t>{}.encode(nvl, "test", v);
+
+ auto v2 = nv_decoder<std::uint64_t>{}.decode(nvl, "test");
+ ATF_REQUIRE_EQ(v, v2);
+}
+
+TEST_CASE(nv_encoder_uint64_vector)
+{
+ auto v = std::vector<std::uint64_t>{1, 2, 42, 666};
+ auto nvl = nv_list{};
+
+ nv_encoder<std::vector<std::uint64_t>>{}.encode(nvl, "test", v);
+
+ auto v2 = nv_decoder<std::vector<std::uint64_t>>{}.decode(nvl, "test");
+ ATF_REQUIRE_EQ(true, std::ranges::equal(v, v2));
+}
+
+/*
+ * string
+ */
+
+TEST_CASE(nv_encoder_string)
+{
+ auto v = std::string("testing");
+ auto nvl = nv_list{};
+
+ nv_encoder<std::string>{}.encode(nvl, "test", v);
+
+ auto v2 = nv_decoder<std::string>{}.decode(nvl, "test");
+ ATF_REQUIRE_EQ(v, v2);
+}
+
+TEST_CASE(nv_encoder_string_vector)
+{
+ auto v = std::vector<std::string>{"foo", "bar", "baz", "quux"};
+ auto nvl = nv_list{};
+
+ nv_encoder<std::vector<std::string>>{}.encode(nvl, "test", v);
+
+ auto v2 = nv_decoder<std::vector<std::string>>{}.decode(nvl, "test");
+ ATF_REQUIRE_EQ(true, std::ranges::equal(v, v2));
+}
+
+/*
+ * string_view
+ */
+
+TEST_CASE(nv_encoder_string_view)
+{
+ auto v = std::string_view("testing");
+ auto nvl = nv_list{};
+
+ nv_encoder<std::string_view>{}.encode(nvl, "test", v);
+ auto v2 = nv_decoder<std::string_view>{}.decode(nvl, "test");
+ ATF_REQUIRE_EQ(v, v2);
+}
+
+TEST_CASE(nv_encoder_string_view_vector)
+{
+ auto v = std::vector<std::string_view>{"foo", "bar", "baz", "quux"};
+ auto nvl = nv_list{};
+
+ nv_encoder<std::vector<std::string_view>>{}.encode(nvl, "test", v);
+
+ auto v2 = nv_decoder<std::vector<std::string_view>>{}
+ .decode(nvl, "test");
+ ATF_REQUIRE_EQ(true, std::ranges::equal(v, v2));
+}
+
+/*
+ * nv_list
+ */
+
+TEST_CASE(nv_encoder_nv_list)
+{
+ auto v = nv_list{};
+ v.add_number("int", 42);
+ auto nvl = nv_list{};
+
+ nv_encoder<nv_list>{}.encode(nvl, "test", v);
+ auto v2 = nv_decoder<nv_list>{}.decode(nvl, "test");
+ ATF_REQUIRE_EQ(v.get_number("int"), v2.get_number("int"));
+}
+
+/*
+ * const_nv_list
+ */
+
+TEST_CASE(nv_encoder_const_nv_list)
+{
+ auto v = nv_list{};
+ v.add_number("int", 42);
+ auto nvl = nv_list{};
+
+ nv_encoder<const_nv_list>{}.encode(nvl, "test", const_nv_list(v));
+ auto v2 = nv_decoder<const_nv_list>{}.decode(nvl, "test");
+ ATF_REQUIRE_EQ(v.get_number("int"), v2.get_number("int"));
+}
+
+/*
+ * std::optional<>
+ */
+
+TEST_CASE(nv_encoder_optional)
+{
+ auto v = std::optional<std::uint64_t>{42};
+ auto nvl = nv_list{};
+
+ nv_encoder<std::optional<std::uint64_t>>{}.encode(nvl, "test", v);
+
+ auto v2 = nv_decoder<
+ std::optional<std::uint64_t>>{}.decode(nvl, "test");
+ ATF_REQUIRE_EQ(true, v2.has_value());
+ ATF_REQUIRE_EQ(*v, *v2);
+
+ v2 = nv_decoder<std::optional<std::uint64_t>>{}.decode(nvl, "nonesuch");
+ ATF_REQUIRE_EQ(false, v2.has_value());
+}
+
+/*
+ * nv_(de)serialize()
+ */
+
+struct object {
+ std::uint64_t int_value{};
+ std::string string_value{};
+ std::vector<std::uint64_t> array_value;
+};
+
+namespace bsd {
+
+template<>
+struct nv_schema<::object> {
+ auto get() {
+ return bsd::nv_field("int value", &object::int_value)
+ >> bsd::nv_field("string value", &object::string_value)
+ >> bsd::nv_field("array value", &object::array_value);
+ }
+};
+
+} // namespace bsd
+
+TEST_CASE(nv_serialize)
+{
+ auto obj = object{42, "quux", {42, 666, 1024}};
+ auto nvl = bsd::nv_serialize(obj);
+
+ auto obj2 = bsd::nv_deserialize<object>(nvl);
+ ATF_REQUIRE_EQ(42, obj2.int_value);
+ ATF_REQUIRE_EQ("quux", obj2.string_value);
+ ATF_REQUIRE_EQ(true, std::ranges::equal(obj2.array_value,
+ std::vector{42, 666, 1024}));
+}
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ ATF_ADD_TEST_CASE(tcs, nv_encoder_bool);
+ ATF_ADD_TEST_CASE(tcs, nv_encoder_bool_vector);
+ ATF_ADD_TEST_CASE(tcs, nv_encoder_uint64);
+ ATF_ADD_TEST_CASE(tcs, nv_encoder_uint64_vector);
+ ATF_ADD_TEST_CASE(tcs, nv_encoder_string);
+ ATF_ADD_TEST_CASE(tcs, nv_encoder_string_vector);
+ ATF_ADD_TEST_CASE(tcs, nv_encoder_string_view);
+ ATF_ADD_TEST_CASE(tcs, nv_encoder_string_view_vector);
+ ATF_ADD_TEST_CASE(tcs, nv_encoder_nv_list);
+ //ATF_ADD_TEST_CASE(tcs, nv_encoder_nv_list_vector);
+ ATF_ADD_TEST_CASE(tcs, nv_encoder_const_nv_list);
+ //ATF_ADD_TEST_CASE(tcs, nv_encoder_const_nv_list_vector);
+ ATF_ADD_TEST_CASE(tcs, nv_encoder_optional);
+
+ ATF_ADD_TEST_CASE(tcs, nv_serialize);
+}