--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab ft=cpp
+
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright contributors to the Ceph project
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "rgw_sal_posix.h"
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+#include "rgw_multi.h"
+#include "rgw_acl_s3.h"
+#include "include/scope_guard.h"
+
+#define dout_subsys ceph_subsys_rgw
+#define dout_context g_ceph_context
+
+namespace rgw { namespace sal {
+
+const int64_t READ_SIZE = 8 * 1024;
+const std::string ATTR_PREFIX = "user.X-RGW-";
+#define RGW_POSIX_ATTR_BUCKET_INFO "POSIX-Bucket-Info"
+#define RGW_POSIX_ATTR_MPUPLOAD "POSIX-Multipart-Upload"
+#define RGW_POSIX_ATTR_OWNER "POSIX-Owner"
+const std::string mp_ns = "multipart";
+const std::string MP_OBJ_PART_PFX = "part-";
+const std::string MP_OBJ_PART_FMT = "{:0>5}";
+const std::string MP_OBJ_HEAD_NAME = MP_OBJ_PART_PFX + "00000";
+
+static int decode_policy(CephContext* cct,
+ bufferlist& bl,
+ RGWAccessControlPolicy* policy)
+{
+ auto iter = bl.cbegin();
+ try {
+ policy->decode(iter);
+ } catch (buffer::error& err) {
+ ldout(cct, 0) << "ERROR: could not decode policy, caught buffer::error" << dendl;
+ return -EIO;
+ }
+ if (cct->_conf->subsys.should_gather<ceph_subsys_rgw, 15>()) {
+ ldout(cct, 15) << __func__ << " POSIX Read AccessControlPolicy";
+ RGWAccessControlPolicy_S3* s3policy = static_cast<RGWAccessControlPolicy_S3 *>(policy);
+ s3policy->to_xml(*_dout);
+ *_dout << dendl;
+ }
+ return 0;
+}
+
+static int rgw_op_get_bucket_policy_from_attr(const DoutPrefixProvider* dpp,
+ POSIXDriver* driver,
+ User* user,
+ Attrs& bucket_attrs,
+ RGWAccessControlPolicy* policy,
+ optional_yield y)
+{
+ auto aiter = bucket_attrs.find(RGW_ATTR_ACL);
+
+ if (aiter != bucket_attrs.end()) {
+ int ret = decode_policy(driver->ctx(), aiter->second, policy);
+ if (ret < 0)
+ return ret;
+ } else {
+ ldout(driver->ctx(), 0) << "WARNING: couldn't find acl header for bucket, generating default" << dendl;
+ /* object exists, but policy is broken */
+ int r = user->load_user(dpp, y);
+ if (r < 0)
+ return r;
+
+ policy->create_default(user->get_id(), user->get_display_name());
+ }
+ return 0;
+}
+
+static inline bool get_attr(Attrs& attrs, const char* name, bufferlist& bl)
+{
+ auto iter = attrs.find(name);
+ if (iter == attrs.end()) {
+ return false;
+ }
+
+ bl = iter->second;
+ return true;
+}
+
+static inline rgw_obj_key decode_obj_key(const char* fname)
+{
+ std::string dname, oname, ns;
+ dname = url_decode(fname);
+ rgw_obj_key::parse_index_key(dname, &oname, &ns);
+ rgw_obj_key key(oname, std::string(), ns);
+ return key;
+}
+
+static inline ceph::real_time from_statx_timestamp(const struct statx_timestamp& xts)
+{
+ struct timespec ts{xts.tv_sec, xts.tv_nsec};
+ return ceph::real_clock::from_timespec(ts);
+}
+
+static inline void bucket_statx_save(struct statx& stx, RGWBucketEnt& ent, ceph::real_time& mtime)
+{
+ mtime = ceph::real_clock::from_time_t(stx.stx_mtime.tv_sec);
+ ent.creation_time = ceph::real_clock::from_time_t(stx.stx_btime.tv_sec);
+ // TODO Calculate size of bucket (or save it somewhere?)
+ //ent.size = stx.stx_size;
+ //ent.size_rounded = stx.stx_blocks * 512;
+}
+
+static inline int copy_dir_fd(int old_fd)
+{
+ return openat(old_fd, ".", O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
+}
+
+static int get_x_attrs(optional_yield y, const DoutPrefixProvider* dpp, int fd,
+ Attrs& attrs, const std::string& display)
+{
+ char namebuf[64 * 1024]; // Max list size supported on linux
+ ssize_t buflen;
+ int ret;
+
+ buflen = flistxattr(fd, namebuf, sizeof(namebuf));
+ if (buflen < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not list attributes for " << display << ": "
+ << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ char *keyptr = namebuf;
+ while (buflen > 0) {
+ std::string value;
+ ssize_t vallen, keylen;
+ char* vp;
+
+ keylen = strlen(keyptr) + 1;
+ std::string key(keyptr);
+ std::string::size_type prefixloc = key.find(ATTR_PREFIX);
+
+ if (prefixloc == std::string::npos) {
+ /* Not one of our attributes */
+ buflen -= keylen;
+ keyptr += keylen;
+ continue;
+ }
+
+ /* Make a key that has just the attribute name */
+ key.erase(prefixloc, ATTR_PREFIX.length());
+
+ vallen = fgetxattr(fd, keyptr, nullptr, 0);
+ if (vallen < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not get attribute " << keyptr << " for " << display << ": " << cpp_strerror(ret) << dendl;
+ return -ret;
+ } else if (vallen == 0) {
+ /* No attribute value for this name */
+ buflen -= keylen;
+ keyptr += keylen;
+ continue;
+ }
+
+ value.reserve(vallen + 1);
+ vp = &value[0];
+
+ vallen = fgetxattr(fd, keyptr, vp, vallen);
+ if (vallen < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not get attribute " << keyptr << " for " << display << ": " << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ bufferlist bl;
+ bl.append(vp, vallen);
+ attrs.emplace(std::move(key), std::move(bl)); /* key and bl are r-value refs */
+
+ buflen -= keylen;
+ keyptr += keylen;
+ }
+
+ return 0;
+}
+
+static int write_x_attr(const DoutPrefixProvider* dpp, optional_yield y, int fd,
+ const std::string& key, bufferlist& value,
+ const std::string& display)
+{
+ int ret;
+ std::string attrname;
+
+ attrname = ATTR_PREFIX + key;
+
+ ret = fsetxattr(fd, attrname.c_str(), value.c_str(), value.length(), 0);
+ if (ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not write attribute " << attrname << " for " << display << ": " << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ return 0;
+}
+
+static int delete_directory(int parent_fd, const char* dname, bool delete_children,
+ const DoutPrefixProvider* dpp)
+{
+ int ret;
+ int dir_fd = -1;
+ DIR *dir;
+ struct dirent *entry;
+
+ dir_fd = openat(parent_fd, dname, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
+ if (dir_fd < 0) {
+ dir_fd = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not open subdir " << dname << ": "
+ << cpp_strerror(dir_fd) << dendl;
+ return -dir_fd;
+ }
+
+ dir = fdopendir(dir_fd);
+ if (dir == NULL) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not open bucket " << dname
+ << " for listing: " << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ errno = 0;
+ while ((entry = readdir(dir)) != NULL) {
+ struct statx stx;
+
+ if ((entry->d_name[0] == '.' && entry->d_name[1] == '\0') ||
+ (entry->d_name[0] == '.' && entry->d_name[1] == '.' &&
+ entry->d_name[2] == '\0')) {
+ /* Skip . and .. */
+ errno = 0;
+ continue;
+ }
+
+ std::string_view d_name = entry->d_name;
+ bool is_mp = d_name.starts_with("." + mp_ns);
+ if (!is_mp && !delete_children) {
+ return -ENOTEMPTY;
+ }
+
+ ret = statx(dir_fd, entry->d_name, AT_SYMLINK_NOFOLLOW, STATX_ALL, &stx);
+ if (ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not stat object " << entry->d_name
+ << ": " << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ if (S_ISDIR(stx.stx_mode)) {
+ /* Recurse */
+ ret = delete_directory(dir_fd, entry->d_name, true, dpp);
+ if (ret < 0) {
+ return ret;
+ }
+
+ continue;
+ }
+
+ /* Otherwise, unlink */
+ ret = unlinkat(dir_fd, entry->d_name, 0);
+ if (ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not remove file " << entry->d_name
+ << ": " << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+ }
+
+ ret = unlinkat(parent_fd, dname, AT_REMOVEDIR);
+ if (ret < 0) {
+ ret = errno;
+ if (errno != ENOENT) {
+ ldpp_dout(dpp, 0) << "ERROR: could not remove bucket " << dname << ": "
+ << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+ }
+
+ return 0;
+}
+
+int POSIXDriver::initialize(CephContext *cct, const DoutPrefixProvider *dpp)
+{
+ FilterDriver::initialize(cct, dpp);
+
+ base_path = g_conf().get_val<std::string>("rgw_posix_base_path");
+
+ ldpp_dout(dpp, 20) << "Initializing POSIX driver: " << base_path << dendl;
+
+ /* ordered listing cache */
+ bucket_cache.reset(
+ new BucketCache(
+ this, base_path,
+ g_conf().get_val<std::string>("rgw_posix_database_root"),
+ g_conf().get_val<int64_t>("rgw_posix_cache_max_buckets"),
+ g_conf().get_val<int64_t>("rgw_posix_cache_lanes"),
+ g_conf().get_val<int64_t>("rgw_posix_cache_partitions"),
+ g_conf().get_val<int64_t>("rgw_posix_cache_lmdb_count")));
+
+ root_fd = openat(-1, base_path.c_str(), O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
+ if (root_fd == -1) {
+ int err = errno;
+ if (err == ENOTDIR) {
+ ldpp_dout(dpp, 0) << " ERROR: base path (" << base_path
+ << "): was not a directory." << dendl;
+ return -err;
+ } else if (err == ENOENT) {
+ err = mkdir(base_path.c_str(), S_IRWXU);
+ if (err < 0) {
+ err = errno;
+ ldpp_dout(dpp, 0) << " ERROR: could not create base path ("
+ << base_path << "): " << cpp_strerror(err) << dendl;
+ return -err;
+ }
+ root_fd = ::open(base_path.c_str(), O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
+ }
+ }
+ if (root_fd == -1) {
+ int err = errno;
+ ldpp_dout(dpp, 0) << " ERROR: could not open base path ("
+ << base_path << "): " << cpp_strerror(err) << dendl;
+ return -err;
+ }
+
+ ldpp_dout(dpp, 20) << "SUCCESS" << dendl;
+ return 0;
+}
+
+std::unique_ptr<User> POSIXDriver::get_user(const rgw_user &u)
+{
+ std::unique_ptr<User> user = next->get_user(u);
+
+ return std::make_unique<POSIXUser>(std::move(user), this);
+}
+
+int POSIXDriver::get_user_by_access_key(const DoutPrefixProvider* dpp, const std::string& key, optional_yield y, std::unique_ptr<User>* user)
+{
+ std::unique_ptr<User> nu;
+ int ret;
+
+ ret = next->get_user_by_access_key(dpp, key, y, &nu);
+ if (ret != 0)
+ return ret;
+
+ User* u = new POSIXUser(std::move(nu), this);
+ user->reset(u);
+ return 0;
+}
+
+int POSIXDriver::get_user_by_email(const DoutPrefixProvider* dpp, const std::string& email, optional_yield y, std::unique_ptr<User>* user)
+{
+ std::unique_ptr<User> nu;
+ int ret;
+
+ ret = next->get_user_by_email(dpp, email, y, &nu);
+ if (ret != 0)
+ return ret;
+
+ User* u = new POSIXUser(std::move(nu), this);
+ user->reset(u);
+ return 0;
+}
+
+int POSIXDriver::get_user_by_swift(const DoutPrefixProvider* dpp, const std::string& user_str, optional_yield y, std::unique_ptr<User>* user)
+{
+ std::unique_ptr<User> nu;
+ int ret;
+
+ ret = next->get_user_by_swift(dpp, user_str, y, &nu);
+ if (ret != 0)
+ return ret;
+
+ User* u = new POSIXUser(std::move(nu), this);
+ user->reset(u);
+ return 0;
+}
+
+std::unique_ptr<Object> POSIXDriver::get_object(const rgw_obj_key& k)
+{
+ return std::make_unique<POSIXObject>(this, k);
+}
+
+int POSIXDriver::get_bucket(const DoutPrefixProvider* dpp, User* u, const rgw_bucket& b, std::unique_ptr<Bucket>* bucket, optional_yield y)
+{
+ int ret;
+ Bucket* bp;
+
+ bp = new POSIXBucket(this, root_fd, b, u);
+ ret = bp->load_bucket(dpp, y);
+ if (ret < 0) {
+ delete bp;
+ return ret;
+ }
+
+ bucket->reset(bp);
+ return 0;
+}
+
+int POSIXDriver::get_bucket(User* u, const RGWBucketInfo& i, std::unique_ptr<Bucket>* bucket)
+{
+ Bucket* bp;
+
+ bp = new POSIXBucket(this, root_fd, i, u);
+ /* Don't need to fetch the bucket info, use the provided one */
+
+ bucket->reset(bp);
+ return 0;
+}
+
+int POSIXDriver::get_bucket(const DoutPrefixProvider* dpp, User* u, const std::string& tenant, const std::string& name, std::unique_ptr<Bucket>* bucket, optional_yield y)
+{
+ rgw_bucket b;
+
+ b.tenant = tenant;
+ b.name = name;
+
+ return get_bucket(dpp, u, b, bucket, y);
+}
+
+std::string POSIXDriver::zone_unique_trans_id(const uint64_t unique_num)
+{
+ char buf[41]; /* 2 + 21 + 1 + 16 (timestamp can consume up to 16) + 1 */
+ time_t timestamp = time(NULL);
+
+ snprintf(buf, sizeof(buf), "tx%021llx-%010llx",
+ (unsigned long long)unique_num,
+ (unsigned long long)timestamp);
+
+ return std::string(buf);
+}
+std::unique_ptr<Writer> POSIXDriver::get_append_writer(const DoutPrefixProvider *dpp,
+ optional_yield y,
+ rgw::sal::Object* _head_obj,
+ const rgw_user& owner,
+ const rgw_placement_rule *ptail_placement_rule,
+ const std::string& unique_tag,
+ uint64_t position,
+ uint64_t *cur_accounted_size)
+{
+ std::unique_ptr<Writer> writer = next->get_append_writer(dpp, y, _head_obj,
+ owner, ptail_placement_rule,
+ unique_tag, position,
+ cur_accounted_size);
+
+ return std::make_unique<FilterWriter>(std::move(writer), std::move(_head_obj));
+}
+
+std::unique_ptr<Writer> POSIXDriver::get_atomic_writer(const DoutPrefixProvider *dpp,
+ optional_yield y,
+ rgw::sal::Object* _head_obj,
+ const rgw_user& owner,
+ const rgw_placement_rule *ptail_placement_rule,
+ uint64_t olh_epoch,
+ const std::string& unique_tag)
+{
+
+ return std::make_unique<POSIXAtomicWriter>(dpp, y, _head_obj, this, owner, ptail_placement_rule, olh_epoch, unique_tag);
+}
+
+void POSIXDriver::finalize(void)
+{
+ next->finalize();
+}
+
+void POSIXDriver::register_admin_apis(RGWRESTMgr* mgr)
+{
+ return next->register_admin_apis(mgr);
+}
+
+std::unique_ptr<Notification> POSIXDriver::get_notification(rgw::sal::Object* obj,
+ rgw::sal::Object* src_obj, struct req_state* s,
+ rgw::notify::EventType event_type, optional_yield y,
+ const std::string* object_name)
+{
+ return next->get_notification(obj, src_obj, s, event_type, y, object_name);
+}
+
+std::unique_ptr<Notification> POSIXDriver::get_notification(const DoutPrefixProvider* dpp,
+ rgw::sal::Object* obj, rgw::sal::Object* src_obj,
+ rgw::notify::EventType event_type,
+ rgw::sal::Bucket* _bucket,
+ std::string& _user_id, std::string& _user_tenant,
+ std::string& _req_id, optional_yield y)
+{
+ return next->get_notification(dpp, obj, src_obj, event_type, _bucket, _user_id, _user_tenant, _req_id, y);
+}
+
+int POSIXDriver::close()
+{
+ if (root_fd < 0) {
+ return 0;
+ }
+
+ ::close(root_fd);
+ root_fd = -1;
+
+ return 0;
+}
+
+int POSIXUser::list_buckets(const DoutPrefixProvider* dpp, const std::string& marker,
+ const std::string& end_marker, uint64_t max,
+ bool need_stats, BucketList &buckets, optional_yield y)
+{
+ DIR* dir;
+ struct dirent* entry;
+ int dfd;
+ int ret;
+
+ buckets.clear();
+
+ /* it's not sufficient to dup(root_fd), as as the new fd would share
+ * the file position of root_fd */
+ dfd = copy_dir_fd(driver->get_root_fd());
+ if (dfd == -1) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not open root to list buckets: "
+ << cpp_strerror(ret) << dendl;
+ return -errno;
+ }
+
+ dir = fdopendir(dfd);
+ if (dir == NULL) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not open root to list buckets: "
+ << cpp_strerror(ret) << dendl;
+ close(dfd);
+ return -ret;
+ }
+
+ auto cleanup_guard = make_scope_guard(
+ [&dir]
+ {
+ closedir(dir);
+ // dfd is also closed
+ }
+ );
+
+ errno = 0;
+ while ((entry = readdir(dir)) != NULL) {
+ struct statx stx;
+
+ ret = statx(driver->get_root_fd(), entry->d_name, AT_SYMLINK_NOFOLLOW, STATX_ALL, &stx);
+ if (ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not stat object " << entry->d_name << ": "
+ << cpp_strerror(ret) << dendl;
+ buckets.clear();
+ return -ret;
+ }
+
+ if (!S_ISDIR(stx.stx_mode)) {
+ /* Not a bucket, skip it */
+ errno = 0;
+ continue;
+ }
+ if (entry->d_name[0] == '.') {
+ /* Skip dotfiles */
+ errno = 0;
+ continue;
+ }
+
+ /* TODO Use stat_to_ent */
+ //RGWBucketEnt ent;
+ //ent.bucket.name = decode_name(entry->d_name);
+ //bucket_statx_save(stx, ent, mtime);
+ RGWBucketInfo info;
+ info.bucket.name = url_decode(entry->d_name);
+ info.owner.id = std::to_string(stx.stx_uid); // TODO convert to owner name
+ info.creation_time = from_statx_timestamp(stx.stx_btime);
+
+ std::unique_ptr<rgw::sal::Bucket> bucket;
+ ret = driver->get_bucket(this, info, &bucket);
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: could not get bucket " << info.bucket << ": "
+ << cpp_strerror(ret) << dendl;
+ buckets.clear();
+ return -ret;
+ }
+
+ buckets.add(std::move(bucket));
+
+ errno = 0;
+ }
+ ret = errno;
+ if (ret != 0) {
+ ldpp_dout(dpp, 0) << "ERROR: could not list buckets for " << get_display_name() << ": "
+ << cpp_strerror(ret) << dendl;
+ buckets.clear();
+ return -ret;
+ }
+
+ return 0;
+}
+
+int POSIXUser::create_bucket(const DoutPrefixProvider* dpp,
+ const rgw_bucket& b,
+ const std::string& zonegroup_id,
+ rgw_placement_rule& placement_rule,
+ std::string& swift_ver_location,
+ const RGWQuotaInfo * pquota_info,
+ const RGWAccessControlPolicy& policy,
+ Attrs& attrs,
+ RGWBucketInfo& binfo,
+ obj_version& ep_objv,
+ bool exclusive,
+ bool obj_lock_enabled,
+ bool* existed,
+ req_info& req_info,
+ std::unique_ptr<Bucket>* bucket_out,
+ optional_yield y)
+{
+ /* Check for existence */
+ {
+ std::unique_ptr<rgw::sal::Bucket> bucket;
+
+ int ret = driver->get_bucket(dpp, this, b, &bucket, y);
+ if (ret >= 0) {
+ *existed = true;
+ // Bucket exists. Check owner comparison
+ if (bucket->get_info().owner.compare(this->get_id()) != 0) {
+ return -EEXIST;
+ }
+ // Don't allow changes to ACL policy
+ RGWAccessControlPolicy old_policy(driver->ctx());
+ ret = rgw_op_get_bucket_policy_from_attr(
+ dpp, driver, this, bucket->get_attrs(), &old_policy, y);
+ if (ret >= 0 && old_policy != policy) {
+ bucket_out->swap(bucket);
+ return -EEXIST;
+ }
+ } else {
+ *existed = false;
+ }
+ }
+
+ binfo.bucket = b;
+ binfo.owner = get_id();
+ binfo.zonegroup = zonegroup_id;
+ binfo.placement_rule = placement_rule;
+ binfo.swift_ver_location = swift_ver_location;
+ binfo.swift_versioning = (!swift_ver_location.empty());
+ binfo.requester_pays = false;
+ binfo.creation_time = ceph::real_clock::now();
+ if (pquota_info) {
+ binfo.quota = *pquota_info;
+ }
+
+ POSIXBucket* fb = new POSIXBucket(driver, driver->get_root_fd(), binfo, this);
+
+ int ret = fb->set_attrs(attrs);
+ if (ret < 0) {
+ delete fb;
+ return ret;
+ }
+
+ ret = fb->create(dpp, y, existed);
+ if (ret < 0) {
+ delete fb;
+ return ret;
+ }
+
+ bucket_out->reset(fb);
+ return 0;
+}
+
+int POSIXUser::read_attrs(const DoutPrefixProvider* dpp, optional_yield y)
+{
+ return next->read_attrs(dpp, y);
+}
+
+int POSIXUser::merge_and_store_attrs(const DoutPrefixProvider* dpp,
+ Attrs& new_attrs, optional_yield y)
+{
+ return next->merge_and_store_attrs(dpp, new_attrs, y);
+}
+
+int POSIXUser::load_user(const DoutPrefixProvider* dpp, optional_yield y)
+{
+ return next->load_user(dpp, y);
+}
+
+int POSIXUser::store_user(const DoutPrefixProvider* dpp, optional_yield y, bool exclusive, RGWUserInfo* old_info)
+{
+ return next->store_user(dpp, y, exclusive, old_info);
+}
+
+int POSIXUser::remove_user(const DoutPrefixProvider* dpp, optional_yield y)
+{
+ return next->remove_user(dpp, y);
+}
+
+std::unique_ptr<Object> POSIXBucket::get_object(const rgw_obj_key& k)
+{
+ return std::make_unique<POSIXObject>(driver, k, this);
+}
+
+int POSIXObject::fill_bde(const DoutPrefixProvider *dpp, optional_yield y, rgw_bucket_dir_entry& bde)
+{
+ std::unique_ptr<User> owner;
+ (void)get_owner(dpp, y, &owner);
+
+ get_key().get_index_key(&bde.key);
+ bde.ver.pool = 1;
+ bde.ver.epoch = 1;
+ bde.exists = true;
+ bde.meta.category = RGWObjCategory::Main;
+ bde.meta.size = get_obj_size();
+ bde.meta.mtime = get_mtime();
+ if (owner) {
+ bde.meta.owner = owner->get_id().to_str();
+ bde.meta.owner_display_name = owner->get_display_name();
+ } else {
+ bde.meta.owner = "unknown";
+ bde.meta.owner_display_name = "unknown";
+ }
+ bde.meta.accounted_size = get_obj_size();
+ bde.meta.storage_class = RGW_STORAGE_CLASS_STANDARD;
+ bde.meta.appendable = true;
+ bufferlist etag_bl;
+ if (rgw::sal::get_attr(get_attrs(), RGW_ATTR_ETAG, etag_bl)) {
+ bde.meta.etag = etag_bl.to_str();
+ }
+
+ return 0;
+}
+
+int POSIXDriver::mint_listing_entry(const std::string &bname,
+ rgw_bucket_dir_entry &bde) {
+ std::unique_ptr<Bucket> b;
+ std::unique_ptr<Object> obj;
+ POSIXObject *pobj;
+ int ret;
+
+ ret = get_bucket(nullptr, nullptr, std::string(), bname, &b, null_yield);
+ if (ret < 0)
+ return ret;
+
+ obj = b->get_object(decode_obj_key(bde.key.name.c_str()));
+ pobj = static_cast<POSIXObject *>(obj.get());
+
+ if (!pobj->exists(nullptr)) {
+ ret = errno;
+ return -ret;
+ }
+
+ ret = pobj->get_obj_attrs(null_yield, nullptr);
+ if (ret < 0)
+ return ret;
+
+ ret = pobj->fill_bde(nullptr, null_yield, bde);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+int POSIXBucket::fill_cache(const DoutPrefixProvider* dpp, optional_yield y,
+ fill_cache_cb_t cb)
+{
+ int ret = for_each(dpp, [this, &cb, &dpp, &y](const char* name) {
+ int ret;
+ std::unique_ptr<Object> obj;
+ POSIXObject* pobj;
+
+ if (name[0] == '.') {
+ /* Skip dotfiles */
+ return 0;
+ }
+
+ obj = get_object(decode_obj_key(name));
+ pobj = static_cast<POSIXObject*>(obj.get());
+
+ if (!pobj->exists(dpp)) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not stat object " << name << ": "
+ << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ ret = pobj->get_obj_attrs(y, dpp);
+ if (ret < 0)
+ return ret;
+
+ rgw_bucket_dir_entry bde{};
+ ret = pobj->fill_bde(dpp, y, bde);
+ if (ret < 0)
+ return ret;
+
+ cb(dpp, bde);
+
+ return 0;
+ });
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: could not list bucket " << get_name() << ": "
+ << cpp_strerror(ret) << dendl;
+ return ret;
+ }
+
+ return 0;
+}
+
+// TODO marker and other params
+int POSIXBucket::list(const DoutPrefixProvider* dpp, ListParams& params,
+ int max, ListResults& results, optional_yield y)
+{
+ int count{0};
+ bool in_prefix{false};
+ // Names in the cache are in OID format
+ {
+ rgw_obj_key key(params.marker);
+ params.marker = key.get_oid();
+ key.set(params.prefix);
+ params.prefix = key.get_oid();
+ }
+ // Names are url_encoded, so encode prefix and delimiter
+ // Names seem to not be url_encoded in cache
+ //params.prefix = url_encode(params.prefix);
+ //params.delim = url_encode(params.delim);
+ if (max <= 0) {
+ return 0;
+ }
+
+ int ret = driver->get_bucket_cache()->list_bucket(
+ dpp, y, this, params.marker.name, [&](const rgw_bucket_dir_entry& bde) -> bool
+ {
+ std::string ns;
+ // bde.key can be encoded with the namespace. Decode it here
+ if (!params.marker.empty() && params.marker == bde.key.name) {
+ // Skip marker
+ return true;
+ }
+ if (!params.prefix.empty()) {
+ // We have a prefix, only match
+ if (!bde.key.name.starts_with(params.prefix)) {
+ // Prefix doesn't match; skip
+ if (in_prefix) {
+ return false;
+ }
+ return true;
+ }
+ // Prefix matches
+ if (params.delim.empty()) {
+ // No delimiter, add matches
+ results.next_marker.set(bde.key);
+ results.objs.push_back(bde);
+ count++;
+ if (count >= max) {
+ results.is_truncated = true;
+ return false;
+ }
+ return true;
+ }
+ auto delim_pos = bde.key.name.find(params.delim, params.prefix.size());
+ if (delim_pos == std::string_view::npos) {
+ // Straight prefix match
+ results.next_marker.set(bde.key);
+ results.objs.push_back(bde);
+ count++;
+ if (count >= max) {
+ results.is_truncated = true;
+ return false;
+ }
+ return true;
+ }
+ std::string prefix_key =
+ bde.key.name.substr(0, delim_pos + params.delim.length());
+ rgw_obj_key::parse_raw_oid(prefix_key, &results.next_marker);
+ // Use results.next_marker.name for prefix_key, since it's been decoded
+ if (!results.common_prefixes.contains(results.next_marker.name)) {
+ results.common_prefixes[results.next_marker.name] = true;
+ count++; // Count will be checked when we exit prefix
+ if (in_prefix) {
+ // We've hit the next prefix entry. Check count
+ if (count >= max) {
+ results.is_truncated = true;
+ // Time to stop
+ return false;
+ }
+ }
+ }
+ in_prefix = true;
+ return true;
+ }
+ if (!params.delim.empty()) {
+ // Delimiter, but no prefix
+ auto delim_pos = bde.key.name.find(params.delim) ;
+ if (delim_pos == std::string_view::npos) {
+ // Delimiter doesn't match, insert
+ results.next_marker.set(bde.key);
+ results.objs.push_back(bde);
+ count++;
+ if (count >= max) {
+ results.is_truncated = true;
+ return false;
+ }
+ return true;
+ }
+ std::string prefix_key =
+ bde.key.name.substr(0, delim_pos + params.delim.length());
+ if (!params.marker.empty() && params.marker == prefix_key) {
+ // Skip marker
+ return true;
+ }
+ std::string decoded_key;
+ rgw_obj_key::parse_index_key(prefix_key, &decoded_key, &ns);
+ if (!results.common_prefixes.contains(decoded_key)) {
+ if (in_prefix) {
+ // New prefix, check the count
+ count++;
+ if (count >= max) {
+ results.is_truncated = true;
+ return false;
+ }
+ }
+ in_prefix = true;
+ results.common_prefixes[decoded_key] = true;
+ // Fallthrough
+ }
+ results.next_marker.name = decoded_key;
+ return true;
+ }
+
+ results.next_marker.set(bde.key);
+ results.objs.push_back(bde);
+ count++;
+ if (count >= max) {
+ results.is_truncated = true;
+ return false;
+ }
+ return true;
+ });
+
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: could not list bucket " << get_name() << ": "
+ << cpp_strerror(ret) << dendl;
+ results.objs.clear();
+ return ret;
+ }
+
+ return 0;
+}
+
+int POSIXBucket::merge_and_store_attrs(const DoutPrefixProvider* dpp,
+ Attrs& new_attrs, optional_yield y)
+{
+ for (auto& it : new_attrs) {
+ attrs[it.first] = it.second;
+ }
+
+ return write_attrs(dpp, y);
+}
+
+int POSIXBucket::remove_bucket(const DoutPrefixProvider* dpp,
+ bool delete_children,
+ bool forward_to_master,
+ req_info* req_info,
+ optional_yield y)
+{
+ return delete_directory(parent_fd, get_fname().c_str(),
+ delete_children, dpp);
+}
+
+int POSIXBucket::remove_bucket_bypass_gc(int concurrent_max,
+ bool keep_index_consistent,
+ optional_yield y,
+ const DoutPrefixProvider *dpp)
+{
+ return remove_bucket(dpp, true, false, nullptr, y);
+}
+
+int POSIXBucket::load_bucket(const DoutPrefixProvider* dpp, optional_yield y,
+ bool get_stats)
+{
+ int ret;
+
+ if (get_name()[0] == '.') {
+ /* Skip dotfiles */
+ return -ERR_INVALID_OBJECT_NAME;
+ }
+ ret = stat(dpp);
+ if (ret < 0) {
+ return ret;
+ }
+
+ bucket_statx_save(stx, ent, mtime);
+ info.creation_time = ent.creation_time;
+
+ if (owner) {
+ info.owner = owner->get_id();
+ }
+
+ ret = open(dpp);
+ if (ret < 0) {
+ return ret;
+ }
+ get_x_attrs(y, dpp, dir_fd, attrs, get_name());
+
+ bufferlist bl;
+ if (get_attr(attrs, RGW_POSIX_ATTR_BUCKET_INFO, bl)) {
+ // Proper bucket with saved info
+ try {
+ auto bufit = bl.cbegin();
+ decode(info, bufit);
+ } catch (buffer::error &err) {
+ ldout(driver->ctx(), 0) << "ERROR: " << __func__ << ": failed to decode " RGW_POSIX_ATTR_BUCKET_INFO " attr" << dendl;
+ return -EINVAL;
+ }
+ // info isn't stored in attrs
+ attrs.erase(RGW_POSIX_ATTR_BUCKET_INFO);
+ } else {
+ // TODO dang: fake info up (UID to owner conversion?)
+ }
+
+ info.creation_time = ent.creation_time;
+
+ return 0;
+}
+
+int POSIXBucket::set_acl(const DoutPrefixProvider* dpp,
+ RGWAccessControlPolicy& acl,
+ optional_yield y)
+{
+ bufferlist aclbl;
+
+ acls = acl;
+ acl.encode(aclbl);
+
+ attrs[RGW_ATTR_ACL] = aclbl;
+ info.owner = acl.get_owner().get_id();
+
+ return write_attrs(dpp, y);
+}
+
+int POSIXBucket::read_stats(const DoutPrefixProvider *dpp,
+ const bucket_index_layout_generation& idx_layout,
+ int shard_id, std::string* bucket_ver, std::string* master_ver,
+ std::map<RGWObjCategory, RGWStorageStats>& stats,
+ std::string* max_marker, bool* syncstopped)
+{
+ return 0;
+}
+
+int POSIXBucket::read_stats_async(const DoutPrefixProvider *dpp,
+ const bucket_index_layout_generation& idx_layout,
+ int shard_id, RGWGetBucketStats_CB* ctx)
+{
+ return 0;
+}
+
+int POSIXBucket::sync_user_stats(const DoutPrefixProvider *dpp, optional_yield y)
+{
+ return 0;
+}
+
+int POSIXBucket::update_container_stats(const DoutPrefixProvider* dpp, optional_yield y)
+{
+ /* Force re-stat */
+ stat_done = false;
+ int ret = stat(dpp);
+ if (ret < 0) {
+ return ret;
+ }
+
+ bucket_statx_save(stx, ent, mtime);
+ info.creation_time = ent.creation_time;
+ ent.count = 0;
+ ent.size = 0;
+
+ // TODO dang: store size/count in attributes
+ ret = for_each(dpp, [this, &dpp](const char* name) {
+ int ret;
+ struct statx lstx;
+
+ if (name[0] == '.') {
+ /* Skip dotfiles */
+ return 0;
+ }
+
+ ret = statx(dir_fd, name, AT_SYMLINK_NOFOLLOW, STATX_ALL, &lstx);
+ if (ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not stat object " << name << ": "
+ << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ if (S_ISREG(lstx.stx_mode) || S_ISDIR(lstx.stx_mode)) {
+ ent.count++;
+ ent.size += lstx.stx_size;
+ }
+
+ return 0;
+ });
+
+ return 0;
+}
+
+int POSIXBucket::check_bucket_shards(const DoutPrefixProvider* dpp, optional_yield y)
+{
+ return 0;
+}
+
+int POSIXBucket::chown(const DoutPrefixProvider* dpp, User& new_user, optional_yield y)
+{
+ /* TODO map user to UID/GID, and change it */
+ return 0;
+}
+
+int POSIXBucket::put_info(const DoutPrefixProvider* dpp, bool exclusive, ceph::real_time _mtime, optional_yield y)
+{
+ mtime = _mtime;
+
+ struct timespec ts[2];
+ ts[0].tv_nsec = UTIME_OMIT;
+ ts[1] = ceph::real_clock::to_timespec(mtime);
+ int ret = utimensat(parent_fd, get_fname().c_str(), ts, AT_SYMLINK_NOFOLLOW);
+ if (ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not set mtime on bucket " << get_name() << ": "
+ << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ return write_attrs(dpp, y);
+}
+
+int POSIXBucket::write_attrs(const DoutPrefixProvider* dpp, optional_yield y)
+{
+ int ret = open(dpp);
+ if (ret < 0) {
+ return ret;
+ }
+
+ // Bucket info is stored as an attribute, but on in attrs[]
+ bufferlist bl;
+ encode(info, bl);
+ ret = write_x_attr(dpp, y, dir_fd, RGW_POSIX_ATTR_BUCKET_INFO, bl, get_name());
+ if (ret < 0) {
+ return ret;
+ }
+
+ for (auto& it : attrs) {
+ ret = write_x_attr(dpp, y, dir_fd, it.first, it.second, get_name());
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ return 0;
+}
+
+int POSIXBucket::check_empty(const DoutPrefixProvider* dpp, optional_yield y)
+{
+ DIR* dir;
+ struct dirent* entry;
+ int ret;
+
+ ret = open(dpp);
+ if (ret < 0) {
+ return ret;
+ }
+
+ dir = fdopendir(dir_fd);
+ if (dir == NULL) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not open bucket " << get_name() << " for listing: "
+ << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ errno = 0;
+ while ((entry = readdir(dir)) != NULL) {
+ if (entry->d_name[0] != '.') {
+ return -ENOTEMPTY;
+ }
+ if (entry->d_name[1] == '.' || entry->d_name[1] == '\0') {
+ continue;
+ }
+ }
+ return 0;
+}
+
+int POSIXBucket::check_quota(const DoutPrefixProvider *dpp, RGWQuota& quota, uint64_t obj_size,
+ optional_yield y, bool check_size_only)
+{
+ return 0;
+}
+
+int POSIXBucket::try_refresh_info(const DoutPrefixProvider* dpp, ceph::real_time* pmtime, optional_yield y)
+{
+ int ret = update_container_stats(dpp, y);
+ if (ret < 0) {
+ return ret;
+ }
+
+ *pmtime = mtime;
+
+ ret = open(dpp);
+ if (ret < 0) {
+ return ret;
+ }
+ get_x_attrs(y, dpp, dir_fd, attrs, get_name());
+
+ return 0;
+}
+
+int POSIXBucket::read_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch,
+ uint64_t end_epoch, uint32_t max_entries,
+ bool* is_truncated, RGWUsageIter& usage_iter,
+ std::map<rgw_user_bucket, rgw_usage_log_entry>& usage)
+{
+ return 0;
+}
+
+int POSIXBucket::trim_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch, optional_yield y)
+{
+ return 0;
+}
+
+int POSIXBucket::remove_objs_from_index(const DoutPrefixProvider *dpp, std::list<rgw_obj_index_key>& objs_to_unlink)
+{
+ return 0;
+}
+
+int POSIXBucket::check_index(const DoutPrefixProvider *dpp, std::map<RGWObjCategory, RGWStorageStats>& existing_stats, std::map<RGWObjCategory, RGWStorageStats>& calculated_stats)
+{
+ return 0;
+}
+
+int POSIXBucket::rebuild_index(const DoutPrefixProvider *dpp)
+{
+ return 0;
+}
+
+int POSIXBucket::set_tag_timeout(const DoutPrefixProvider *dpp, uint64_t timeout)
+{
+ return 0;
+}
+
+int POSIXBucket::purge_instance(const DoutPrefixProvider* dpp, optional_yield y)
+{
+ return 0;
+}
+
+std::unique_ptr<MultipartUpload> POSIXBucket::get_multipart_upload(
+ const std::string& oid,
+ std::optional<std::string> upload_id,
+ ACLOwner owner, ceph::real_time mtime)
+{
+ return std::make_unique<POSIXMultipartUpload>(driver, this, oid, upload_id, owner, mtime);
+}
+
+int POSIXBucket::list_multiparts(const DoutPrefixProvider *dpp,
+ const std::string& prefix,
+ std::string& marker,
+ const std::string& delim,
+ const int& max_uploads,
+ std::vector<std::unique_ptr<MultipartUpload>>& uploads,
+ std::map<std::string, bool> *common_prefixes,
+ bool *is_truncated, optional_yield y)
+{
+ //std::vector<std::unique_ptr<MultipartUpload>> nup;
+ //int ret;
+//
+ //ret = next->list_multiparts(dpp, prefix, marker, delim, max_uploads, nup,
+ //common_prefixes, is_truncated);
+ //if (ret < 0)
+ //return ret;
+//
+ //for (auto& ent : nup) {
+ //uploads.emplace_back(std::make_unique<POSIXMultipartUpload>(std::move(ent), this, driver));
+ //}
+
+ return 0;
+}
+
+int POSIXBucket::abort_multiparts(const DoutPrefixProvider* dpp, CephContext* cct, optional_yield y)
+{
+ return 0;
+}
+
+int POSIXBucket::create(const DoutPrefixProvider* dpp, optional_yield y, bool* existed)
+{
+ int ret = mkdirat(parent_fd, get_fname().c_str(), S_IRWXU);
+ if (ret < 0) {
+ ret = errno;
+ if (ret != EEXIST) {
+ if (dpp)
+ ldpp_dout(dpp, 0) << "ERROR: could not create bucket " << get_name() << ": "
+ << cpp_strerror(ret) << dendl;
+ return -ret;
+ } else if (existed != nullptr) {
+ *existed = true;
+ }
+ return ret;
+ }
+
+ return write_attrs(dpp, y);
+}
+
+std::string POSIXBucket::get_fname()
+{
+ std::string name;
+
+ if (ns)
+ name = "." + *ns + "_" + url_encode(get_name(), true);
+ else
+ name = url_encode(get_name(), true);
+
+ return name;
+}
+
+int POSIXBucket::get_shadow_bucket(const DoutPrefixProvider* dpp, optional_yield y,
+ const std::string& ns,
+ const std::string& tenant, const std::string& name,
+ bool create, std::unique_ptr<POSIXBucket>* shadow)
+{
+ std::optional<std::string> ons{std::nullopt};
+ int ret;
+ POSIXBucket* bp;
+ rgw_bucket b;
+
+ b.tenant = tenant;
+ b.name = name;
+
+ if (!ns.empty()) {
+ ons = ns;
+ }
+
+ open(dpp);
+
+ bp = new POSIXBucket(driver, dir_fd, b, owner, ons);
+ ret = bp->load_bucket(dpp, y);
+ if (ret == -ENOENT && create) {
+ /* Create it if it doesn't exist */
+ ret = bp->create(dpp, y, nullptr);
+ }
+ if (ret < 0) {
+ delete bp;
+ return ret;
+ }
+
+ shadow->reset(bp);
+ return 0;
+}
+
+template <typename F>
+int POSIXBucket::for_each(const DoutPrefixProvider* dpp, const F& func)
+{
+ DIR* dir;
+ struct dirent* entry;
+ int ret;
+
+ ret = open(dpp);
+ if (ret < 0) {
+ return ret;
+ }
+
+ dir = fdopendir(dir_fd);
+ if (dir == NULL) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not open bucket " << get_name() << " for listing: "
+ << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ rewinddir(dir);
+
+ while ((entry = readdir(dir)) != NULL) {
+ int r = func(entry->d_name);
+ if (r < 0) {
+ ret = r;
+ }
+ }
+
+ if (ret == -EAGAIN) {
+ /* Limit reached */
+ ret = 0;
+ }
+ return ret;
+}
+
+int POSIXBucket::open(const DoutPrefixProvider* dpp)
+{
+ if (dir_fd >= 0) {
+ return 0;
+ }
+
+ int ret = openat(parent_fd, get_fname().c_str(),
+ O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
+ if (ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not open bucket " << get_name() << ": "
+ << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ dir_fd = ret;
+
+ return 0;
+}
+
+// This is for renaming a shadow bucket to a MP object. It won't work work for a normal bucket
+int POSIXBucket::rename(const DoutPrefixProvider* dpp, optional_yield y, Object* target_obj)
+{
+ POSIXObject *to = static_cast<POSIXObject*>(target_obj);
+ POSIXBucket *tb = static_cast<POSIXBucket*>(target_obj->get_bucket());
+ std::string src_fname = get_fname();
+ std::string dst_fname = to->get_fname();
+ int flags = 0;
+
+ if (to->exists(dpp)) {
+ flags = RENAME_EXCHANGE;
+ }
+ // swap
+ int ret = renameat2(tb->get_dir_fd(dpp), src_fname.c_str(), tb->get_dir_fd(dpp), dst_fname.c_str(), flags);
+ if(ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: renameat2 for shadow object could not finish: "
+ << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ // Update saved bucket info
+ info.bucket.name = to->get_name();
+ bufferlist bl;
+ encode(info, bl);
+ ret = write_x_attr(dpp, y, dir_fd, RGW_POSIX_ATTR_BUCKET_INFO, bl, get_name());
+ if (ret < 0) {
+ return ret;
+ }
+
+ // Delete old one (could be file or directory)
+ struct statx stx;
+ ret = statx(parent_fd, src_fname.c_str(), AT_SYMLINK_NOFOLLOW,
+ STATX_ALL, &stx);
+ if (ret < 0) {
+ ret = errno;
+ if (ret == ENOENT) {
+ return 0;
+ }
+ ldpp_dout(dpp, 0) << "ERROR: could not stat object " << get_name() << ": "
+ << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ if (S_ISREG(stx.stx_mode)) {
+ ret = unlinkat(parent_fd, src_fname.c_str(), 0);
+ } else if (S_ISDIR(stx.stx_mode)) {
+ ret = delete_directory(parent_fd, src_fname.c_str(), true, dpp);
+ }
+ if (ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not remove old file " << get_name()
+ << ": " << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ return 0;
+}
+
+int POSIXBucket::close()
+{
+ if (dir_fd < 0) {
+ return 0;
+ }
+
+ ::close(dir_fd);
+ dir_fd = -1;
+
+ return 0;
+}
+
+int POSIXBucket::stat(const DoutPrefixProvider* dpp)
+{
+ if (stat_done) {
+ return 0;
+ }
+
+ int ret = statx(parent_fd, get_fname().c_str(), AT_SYMLINK_NOFOLLOW,
+ STATX_ALL, &stx);
+ if (ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not stat bucket " << get_name() << ": "
+ << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+ if (!S_ISDIR(stx.stx_mode)) {
+ /* Not a bucket */
+ return -EINVAL;
+ }
+
+ stat_done = true;
+ return 0;
+}
+
+/* This is a shadow bucket. Copy it into a new shadow bucket in the destination
+ * bucket */
+int POSIXBucket::copy(const DoutPrefixProvider *dpp, optional_yield y,
+ POSIXBucket* db, POSIXObject* dest)
+{
+ std::unique_ptr<POSIXBucket> dsb;
+
+ // Delete the target, in case it's not a multipart
+ int ret = dest->delete_object(dpp, y);
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: could not remove dest object "
+ << dest->get_name() << dendl;
+ return ret;
+ }
+
+ ret = db->get_shadow_bucket(dpp, y, std::string(), std::string(), dest->get_fname(), true, &dsb);
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: could not create shadow bucket " << dest->get_name()
+ << " in bucket " << db->get_name() << dendl;
+ return ret;
+ }
+
+ ret = for_each(dpp, [this, &dsb, &dpp, &y](const char *name) {
+ int ret;
+ std::unique_ptr<Object> sobj;
+ POSIXObject* sop;
+ std::unique_ptr<Object> dobj;
+ POSIXObject* dop;
+
+ if (name[0] == '.') {
+ /* Skip dotfiles */
+ return 0;
+ }
+
+ sobj = this->get_object(decode_obj_key(name));
+ sop = static_cast<POSIXObject*>(sobj.get());
+ if (!sop->exists(dpp)) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not stat object " << name << ": "
+ << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+ ret = sop->open(dpp, true);
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: could not open source object " << get_name()
+ << dendl;
+ return ret;
+ }
+
+ dobj = dsb->get_object(decode_obj_key(name));
+ dop = static_cast<POSIXObject*>(dobj.get());
+
+ return sop->copy(dpp, y, this, dsb.get(), dop);
+ });
+
+ return ret;
+}
+
+int POSIXObject::delete_object(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ bool prevent_versioning)
+{
+ POSIXBucket *b = static_cast<POSIXBucket*>(get_bucket());
+ if (!b) {
+ ldpp_dout(dpp, 0) << "ERROR: could not get bucket for " << get_name() << dendl;
+ return -EINVAL;
+ }
+
+ int ret = stat(dpp);
+ if (ret < 0) {
+ if (ret == -ENOENT) {
+ // Nothing to do
+ return 0;
+ }
+ return ret;
+ }
+
+ if (!b->versioned()) {
+ if (shadow) {
+ ret = shadow->remove_bucket(dpp, true, false, nullptr, y);
+ if (ret < 0) {
+ return ret;
+ }
+ shadow.reset(nullptr);
+ }
+
+ int ret = unlinkat(b->get_dir_fd(dpp), get_fname().c_str(), 0);
+ if (ret < 0) {
+ ret = errno;
+ if (errno != ENOENT) {
+ ldpp_dout(dpp, 0) << "ERROR: could not remove object " << get_name()
+ << ": " << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+ }
+ return 0;
+ }
+
+ // Versioned directory. Need to remove all objects matching
+ b->for_each(dpp, [this, &dpp, &b](const char* name) {
+ int ret;
+ std::string_view vname(name);
+
+ if (vname.find(get_fname().c_str()) != std::string_view::npos) {
+ ret = unlinkat(b->get_dir_fd(dpp), name, 0);
+ if (ret < 0) {
+ ret = errno;
+ if (errno != ENOENT) {
+ ldpp_dout(dpp, 0) << "ERROR: could not remove object " << name
+ << ": " << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+ }
+ }
+ return 0;
+ });
+
+ return 0;
+}
+
+int POSIXObject::copy_object(User* user,
+ req_info* info,
+ const rgw_zone_id& source_zone,
+ rgw::sal::Object* dest_object,
+ rgw::sal::Bucket* dest_bucket,
+ rgw::sal::Bucket* src_bucket,
+ const rgw_placement_rule& dest_placement,
+ ceph::real_time* src_mtime,
+ ceph::real_time* mtime,
+ const ceph::real_time* mod_ptr,
+ const ceph::real_time* unmod_ptr,
+ bool high_precision_time,
+ const char* if_match,
+ const char* if_nomatch,
+ AttrsMod attrs_mod,
+ bool copy_if_newer,
+ Attrs& attrs,
+ RGWObjCategory category,
+ uint64_t olh_epoch,
+ boost::optional<ceph::real_time> delete_at,
+ std::string* version_id,
+ std::string* tag,
+ std::string* etag,
+ void (*progress_cb)(off_t, void *),
+ void* progress_data,
+ const DoutPrefixProvider* dpp,
+ optional_yield y)
+{
+ int ret;
+ POSIXBucket *db = static_cast<POSIXBucket*>(dest_bucket);
+ POSIXBucket *sb = static_cast<POSIXBucket*>(src_bucket);
+ POSIXObject *dobj = static_cast<POSIXObject*>(dest_object);
+
+ if (!db || !sb) {
+ ldpp_dout(dpp, 0) << "ERROR: could not get bucket to copy " << get_name()
+ << dendl;
+ return -EINVAL;
+ }
+
+ // Source must exist, and we need to know if it's a shadow obj
+ if (!exists(dpp)) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not stat object " << get_name() << ": "
+ << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ if (shadow) {
+ return shadow->copy(dpp, y, db, dobj);
+ } else {
+ return copy(dpp, y, sb, db, dobj);
+ }
+}
+
+int POSIXObject::get_obj_state(const DoutPrefixProvider* dpp, RGWObjState **pstate, optional_yield y, bool follow_olh)
+{
+ int ret = stat(dpp);
+ if (ret < 0) {
+ return ret;
+ }
+ *pstate = &state;
+
+ return 0;
+}
+
+int POSIXObject::set_obj_attrs(const DoutPrefixProvider* dpp, Attrs* setattrs,
+ Attrs* delattrs, optional_yield y)
+{
+ if (delattrs) {
+ for (auto& it : *delattrs) {
+ state.attrset.erase(it.first);
+ }
+ }
+ if (setattrs) {
+ for (auto& it : *setattrs) {
+ state.attrset[it.first] = it.second;
+ }
+ }
+
+ for (auto& it : state.attrset) {
+ int ret = write_attr(dpp, y, it.first, it.second);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ return 0;
+}
+
+int POSIXObject::get_obj_attrs(optional_yield y, const DoutPrefixProvider* dpp,
+ rgw_obj* target_obj)
+{
+ int ret = open(dpp, false);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return get_x_attrs(y, dpp, obj_fd, state.attrset, get_name());
+}
+
+int POSIXObject::modify_obj_attrs(const char* attr_name, bufferlist& attr_val,
+ optional_yield y, const DoutPrefixProvider* dpp)
+{
+ state.attrset[attr_name] = attr_val;
+ return write_attr(dpp, y, attr_name, attr_val);
+}
+
+int POSIXObject::delete_obj_attrs(const DoutPrefixProvider* dpp, const char* attr_name,
+ optional_yield y)
+{
+ state.attrset.erase(attr_name);
+
+ int ret = open(dpp, true);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = fremovexattr(obj_fd, attr_name);
+ if (ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not remover attribute " << attr_name << " for " << get_name() << ": " << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ return 0;
+}
+
+bool POSIXObject::is_expired()
+{
+ bufferlist bl;
+ if (get_attr(state.attrset, RGW_ATTR_DELETE_AT, bl)) {
+ utime_t delete_at;
+ try {
+ auto bufit = bl.cbegin();
+ decode(delete_at, bufit);
+ } catch (buffer::error& err) {
+ ldout(driver->ctx(), 0) << "ERROR: " << __func__ << ": failed to decode " RGW_ATTR_DELETE_AT " attr" << dendl;
+ return false;
+ }
+
+ if (delete_at <= ceph_clock_now() && !delete_at.is_zero()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void POSIXObject::gen_rand_obj_instance_name()
+{
+ enum { OBJ_INSTANCE_LEN = 32 };
+ char buf[OBJ_INSTANCE_LEN + 1];
+
+ gen_rand_alphanumeric_no_underscore(driver->ctx(), buf, OBJ_INSTANCE_LEN);
+ state.obj.key.set_instance(buf);
+}
+
+std::unique_ptr<MPSerializer> POSIXObject::get_serializer(const DoutPrefixProvider *dpp, const std::string& lock_name)
+{
+ return std::make_unique<MPPOSIXSerializer>(dpp, driver, this, lock_name);
+}
+
+int MPPOSIXSerializer::try_lock(const DoutPrefixProvider *dpp, utime_t dur, optional_yield y)
+{
+ if (!obj->exists(dpp)) {
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+int POSIXObject::transition(Bucket* bucket,
+ const rgw_placement_rule& placement_rule,
+ const real_time& mtime,
+ uint64_t olh_epoch,
+ const DoutPrefixProvider* dpp,
+ optional_yield y)
+{
+ return -ERR_NOT_IMPLEMENTED;
+}
+
+int POSIXObject::transition_to_cloud(Bucket* bucket,
+ rgw::sal::PlacementTier* tier,
+ rgw_bucket_dir_entry& o,
+ std::set<std::string>& cloud_targets,
+ CephContext* cct,
+ bool update_object,
+ const DoutPrefixProvider* dpp,
+ optional_yield y)
+{
+ return -ERR_NOT_IMPLEMENTED;
+}
+
+bool POSIXObject::placement_rules_match(rgw_placement_rule& r1, rgw_placement_rule& r2)
+{
+ return (r1 == r2);
+}
+
+int POSIXObject::dump_obj_layout(const DoutPrefixProvider *dpp, optional_yield y, Formatter* f)
+{
+ return 0;
+}
+
+int POSIXObject::swift_versioning_restore(bool& restored,
+ const DoutPrefixProvider* dpp, optional_yield y)
+{
+ return 0;
+}
+
+int POSIXObject::swift_versioning_copy(const DoutPrefixProvider* dpp,
+ optional_yield y)
+{
+ return 0;
+}
+
+int POSIXObject::omap_get_vals_by_keys(const DoutPrefixProvider *dpp, const std::string& oid,
+ const std::set<std::string>& keys,
+ Attrs* vals)
+{
+ /* TODO Figure out omap */
+ return 0;
+}
+
+int POSIXObject::omap_set_val_by_key(const DoutPrefixProvider *dpp, const std::string& key, bufferlist& val,
+ bool must_exist, optional_yield y)
+{
+ /* TODO Figure out omap */
+ return 0;
+}
+
+int POSIXObject::chown(User& new_user, const DoutPrefixProvider* dpp, optional_yield y)
+{
+ POSIXBucket *b = static_cast<POSIXBucket*>(get_bucket());
+ if (!b) {
+ ldpp_dout(dpp, 0) << "ERROR: could not get bucket for " << get_name() << dendl;
+ return -EINVAL;
+ }
+ /* TODO Get UID from user */
+ int uid = 0;
+ int gid = 0;
+
+ int ret = fchownat(b->get_dir_fd(dpp), get_fname().c_str(), uid, gid, AT_SYMLINK_NOFOLLOW);
+ if (ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not remove object " << get_name() << ": "
+ << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ return 0;
+}
+
+int POSIXObject::stat(const DoutPrefixProvider* dpp)
+{
+ if (stat_done) {
+ return 0;
+ }
+
+ state.exists = false;
+ POSIXBucket *b = static_cast<POSIXBucket*>(get_bucket());
+ if (!b) {
+ ldpp_dout(dpp, 0) << "ERROR: could not get bucket for " << get_name() << dendl;
+ return -EINVAL;
+ }
+
+ int ret = statx(b->get_dir_fd(dpp), get_fname().c_str(), AT_SYMLINK_NOFOLLOW,
+ STATX_ALL, &stx);
+ if (ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not stat object " << get_name() << ": "
+ << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+ if (S_ISREG(stx.stx_mode)) {
+ /* Normal object */
+ state.accounted_size = state.size = stx.stx_size;
+ state.mtime = from_statx_timestamp(stx.stx_mtime);
+ } else if (S_ISDIR(stx.stx_mode)) {
+ /* multipart object */
+ /* Get the shadow bucket */
+ POSIXBucket* pb = static_cast<POSIXBucket*>(bucket);
+ ret = pb->get_shadow_bucket(dpp, null_yield, std::string(),
+ std::string(), get_fname(), false, &shadow);
+ if (ret < 0) {
+ return ret;
+ }
+
+ state.mtime = from_statx_timestamp(stx.stx_mtime);
+ /* Add up size of parts */
+ uint64_t total_size{0};
+ int fd = shadow->get_dir_fd(dpp);
+ shadow->for_each(dpp, [this, &total_size, fd, &dpp](const char* name) {
+ int ret;
+ struct statx stx;
+ std::string sname = name;
+
+ if (sname.rfind(MP_OBJ_PART_PFX, 0) != 0) {
+ /* Skip non-parts */
+ return 0;
+ }
+
+ ret = statx(fd, name, AT_SYMLINK_NOFOLLOW, STATX_ALL, &stx);
+ if (ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not stat object " << name << ": " << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ if (!S_ISREG(stx.stx_mode)) {
+ /* Skip non-files */
+ return 0;
+ }
+
+ parts[name] = stx.stx_size;
+ total_size += stx.stx_size;
+ return 0;
+ });
+ state.accounted_size = state.size = total_size;
+ } else {
+ /* Not an object */
+ return -EINVAL;
+ }
+
+ stat_done = true;
+ state.exists = true;
+
+ return 0;
+}
+
+int POSIXObject::get_owner(const DoutPrefixProvider *dpp, optional_yield y, std::unique_ptr<User> *owner)
+{
+ bufferlist bl;
+ rgw_user u;
+ if (!rgw::sal::get_attr(get_attrs(), RGW_POSIX_ATTR_OWNER, bl)) {
+ ldpp_dout(dpp, 0) << "ERROR: " << __func__
+ << ": No " RGW_POSIX_ATTR_OWNER " attr" << dendl;
+ return -EINVAL;
+ }
+
+ try {
+ auto bufit = bl.cbegin();
+ decode(u, bufit);
+ } catch (buffer::error &err) {
+ ldpp_dout(dpp, 0) << "ERROR: " << __func__
+ << ": failed to decode " RGW_POSIX_ATTR_OWNER " attr" << dendl;
+ return -EINVAL;
+ }
+
+ *owner = driver->get_user(u);
+ (*owner)->load_user(dpp, y);
+ return 0;
+}
+
+std::unique_ptr<Object::ReadOp> POSIXObject::get_read_op()
+{
+ return std::make_unique<POSIXReadOp>(this);
+}
+
+std::unique_ptr<Object::DeleteOp> POSIXObject::get_delete_op()
+{
+ return std::make_unique<POSIXDeleteOp>(this);
+}
+
+int POSIXObject::open(const DoutPrefixProvider* dpp, bool create, bool temp_file)
+{
+ if (obj_fd >= 0) {
+ return 0;
+ }
+
+ stat(dpp);
+
+ if (shadow) {
+ obj_fd = shadow->get_dir_fd(dpp);
+ return obj_fd;
+ }
+
+ POSIXBucket *b = static_cast<POSIXBucket*>(get_bucket());
+ if (!b) {
+ ldpp_dout(dpp, 0) << "ERROR: could not get bucket for " << get_name() << dendl;
+ return -EINVAL;
+ }
+
+ int ret, flags;
+ std::string path;
+
+ if(temp_file) {
+ flags = O_TMPFILE | O_RDWR;
+ path = ".";
+ } else {
+ flags = O_RDWR | O_NOFOLLOW;
+ if (create)
+ flags |= O_CREAT;
+ path = get_fname();
+ }
+ ret = openat(b->get_dir_fd(dpp), path.c_str(), flags, S_IRWXU);
+ if (ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not open object " << get_name() << ": "
+ << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ obj_fd = ret;
+
+ return 0;
+}
+
+int POSIXObject::link_temp_file(const DoutPrefixProvider *dpp, optional_yield y)
+{
+ if (obj_fd < 0) {
+ return 0;
+ }
+
+ char temp_file_path[PATH_MAX];
+ // Only works on Linux - Non-portable
+ snprintf(temp_file_path, PATH_MAX, "/proc/self/fd/%d", obj_fd);
+
+ POSIXBucket *b = static_cast<POSIXBucket*>(get_bucket());
+
+ if (!b) {
+ ldpp_dout(dpp, 0) << "ERROR: could not get bucket for " << get_name() << dendl;
+ return -EINVAL;
+ }
+
+ int ret = linkat(AT_FDCWD, temp_file_path, b->get_dir_fd(dpp), get_temp_fname().c_str(), AT_SYMLINK_FOLLOW);
+ if(ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: linkat for temp file could not finish: "
+ << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ // Delete the target, in case it's a multipart
+ ret = delete_object(dpp, y);
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: could not remove dest object "
+ << get_name() << dendl;
+ return ret;
+ }
+
+ ret = renameat(b->get_dir_fd(dpp), get_temp_fname().c_str(), b->get_dir_fd(dpp), get_fname().c_str());
+ if(ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: renameat for object could not finish: "
+ << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ return 0;
+}
+
+
+int POSIXObject::close()
+{
+ if (obj_fd < 0) {
+ return 0;
+ }
+
+ int ret = ::fsync(obj_fd);
+ if(ret < 0) {
+ return ret;
+ }
+
+ ret = ::close(obj_fd);
+ if(ret < 0) {
+ return ret;
+ }
+ obj_fd = -1;
+
+ return 0;
+}
+
+int POSIXObject::read(int64_t ofs, int64_t left, bufferlist& bl,
+ const DoutPrefixProvider* dpp, optional_yield y)
+{
+ if (!shadow) {
+ // Normal file, just read it
+ int64_t len = std::min(left + 1, READ_SIZE);
+ ssize_t ret;
+
+ ret = lseek(obj_fd, ofs, SEEK_SET);
+ if (ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not seek object " << get_name() << " to "
+ << ofs << " :" << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ char read_buf[READ_SIZE];
+ ret = ::read(obj_fd, read_buf, len);
+ if (ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not read object " << get_name() << ": "
+ << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ bl.append(read_buf, ret);
+
+ return ret;
+ }
+
+ // It's a multipart object, find the correct file, open it, and read it
+ std::string pname;
+ for (auto part : parts) {
+ if (ofs < part.second) {
+ pname = part.first;
+ break;
+ }
+
+ ofs -= part.second;
+ }
+
+ if (pname.empty()) {
+ // ofs is past the end
+ return 0;
+ }
+
+ POSIXObject* shadow_obj;
+ std::unique_ptr<rgw::sal::Object> obj = shadow->get_object(rgw_obj_key(pname));
+ shadow_obj = static_cast<POSIXObject*>(obj.get());
+ int ret = shadow_obj->open(dpp, false);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return shadow_obj->read(ofs, left, bl, dpp, y);
+}
+
+int POSIXObject::write(int64_t ofs, bufferlist& bl, const DoutPrefixProvider* dpp,
+ optional_yield y)
+{
+ if (shadow) {
+ // Can't write to a MP file
+ return -EINVAL;
+ }
+
+ int64_t left = bl.length();
+ char* curp = bl.c_str();
+ ssize_t ret;
+
+ ret = fchmod(obj_fd, S_IRUSR|S_IWUSR);
+ if(ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: could not change permissions on object " << get_name() << ": "
+ << cpp_strerror(ret) << dendl;
+ return ret;
+ }
+
+
+ ret = lseek(obj_fd, ofs, SEEK_SET);
+ if (ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not seek object " << get_name() << " to "
+ << ofs << " :" << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ while (left > 0) {
+ ret = ::write(obj_fd, curp, left);
+ if (ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not write object " << get_name() << ": "
+ << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ curp += ret;
+ left -= ret;
+ }
+
+ return 0;
+}
+
+int POSIXObject::write_attr(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, bufferlist& value)
+{
+ int ret;
+ std::string attrname;
+
+ ret = open(dpp, true);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return write_x_attr(dpp, y, obj_fd, key, value, get_name());
+}
+
+int POSIXObject::POSIXReadOp::prepare(optional_yield y, const DoutPrefixProvider* dpp)
+{
+ int ret = source->stat(dpp);
+ if (ret < 0)
+ return ret;
+
+ ret = source->get_obj_attrs(y, dpp);
+ if (ret < 0)
+ return ret;
+
+ bufferlist etag_bl;
+ if (!rgw::sal::get_attr(source->get_attrs(), RGW_ATTR_ETAG, etag_bl)) {
+ /* Sideloaded file. Generate necessary attributes. Only done once. */
+ int ret = source->generate_attrs(dpp, y);
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << " ERROR: could not generate attrs for " << source->get_name() << " error: " << cpp_strerror(ret) << dendl;
+ return ret;
+ }
+ }
+
+ if (!rgw::sal::get_attr(source->get_attrs(), RGW_ATTR_ETAG, etag_bl)) {
+ return -EINVAL;
+ }
+
+#if 0 // WIP
+ if (params.mod_ptr || params.unmod_ptr) {
+ obj_time_weight src_weight;
+ src_weight.init(astate);
+ src_weight.high_precision = params.high_precision_time;
+
+ obj_time_weight dest_weight;
+ dest_weight.high_precision = params.high_precision_time;
+
+ if (params.mod_ptr && !params.if_nomatch) {
+ dest_weight.init(*params.mod_ptr, params.mod_zone_id, params.mod_pg_ver);
+ ldpp_dout(dpp, 10) << "If-Modified-Since: " << dest_weight << " Last-Modified: " << src_weight << dendl;
+ if (!(dest_weight < src_weight)) {
+ return -ERR_NOT_MODIFIED;
+ }
+ }
+
+ if (params.unmod_ptr && !params.if_match) {
+ dest_weight.init(*params.unmod_ptr, params.mod_zone_id, params.mod_pg_ver);
+ ldpp_dout(dpp, 10) << "If-UnModified-Since: " << dest_weight << " Last-Modified: " << src_weight << dendl;
+ if (dest_weight < src_weight) {
+ return -ERR_PRECONDITION_FAILED;
+ }
+ }
+ }
+#endif
+
+ if (params.mod_ptr || params.unmod_ptr) {
+ if (params.mod_ptr && !params.if_nomatch) {
+ ldpp_dout(dpp, 10) << "If-Modified-Since: " << *params.mod_ptr << " Last-Modified: " << source->get_mtime() << dendl;
+ if (!(*params.mod_ptr < source->get_mtime())) {
+ return -ERR_NOT_MODIFIED;
+ }
+ }
+
+ if (params.unmod_ptr && !params.if_match) {
+ ldpp_dout(dpp, 10) << "If-Modified-Since: " << *params.unmod_ptr << " Last-Modified: " << source->get_mtime() << dendl;
+ if (*params.unmod_ptr < source->get_mtime()) {
+ return -ERR_PRECONDITION_FAILED;
+ }
+ }
+ }
+
+ if (params.if_match) {
+ std::string if_match_str = rgw_string_unquote(params.if_match);
+ ldpp_dout(dpp, 10) << "If-Match: " << if_match_str << " ETAG: " << etag_bl.c_str() << dendl;
+
+ if (if_match_str.compare(0, etag_bl.length(), etag_bl.c_str(), etag_bl.length()) != 0) {
+ return -ERR_PRECONDITION_FAILED;
+ }
+ }
+ if (params.if_nomatch) {
+ std::string if_nomatch_str = rgw_string_unquote(params.if_nomatch);
+ ldpp_dout(dpp, 10) << "If-No-Match: " << if_nomatch_str << " ETAG: " << etag_bl.c_str() << dendl;
+ if (if_nomatch_str.compare(0, etag_bl.length(), etag_bl.c_str(), etag_bl.length()) == 0) {
+ return -ERR_NOT_MODIFIED;
+ }
+ }
+
+ if (params.lastmod) {
+ *params.lastmod = source->get_mtime();
+ }
+
+ return 0;
+}
+
+int POSIXObject::POSIXReadOp::read(int64_t ofs, int64_t end, bufferlist& bl,
+ optional_yield y, const DoutPrefixProvider* dpp)
+{
+ return source->read(ofs, end + 1, bl, dpp, y);
+}
+
+int POSIXObject::generate_attrs(const DoutPrefixProvider* dpp, optional_yield y)
+{
+ int ret;
+
+ /* Generate an ETAG */
+ if (shadow) {
+ ret = generate_mp_etag(dpp, y);
+ } else {
+ ret = generate_etag(dpp, y);
+ }
+
+ return ret;
+}
+
+int POSIXObject::generate_mp_etag(const DoutPrefixProvider* dpp, optional_yield y)
+{
+ int64_t count = 0;
+ char etag_buf[CEPH_CRYPTO_MD5_DIGESTSIZE];
+ char final_etag_str[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 16];
+ std::string etag;
+ bufferlist etag_bl;
+ MD5 hash;
+ // Allow use of MD5 digest in FIPS mode for non-cryptographic purposes
+ hash.SetFlags(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
+ int ret;
+ rgw::sal::Bucket::ListParams params;
+ rgw::sal::Bucket::ListResults results;
+
+ do {
+ static constexpr auto MAX_LIST_OBJS = 100u;
+ ret = shadow->list(dpp, params, MAX_LIST_OBJS, results, y);
+ if (ret < 0) {
+ return ret;
+ }
+ for (rgw_bucket_dir_entry& ent : results.objs) {
+ std::unique_ptr<rgw::sal::Object> obj;
+ POSIXObject* shadow_obj;
+
+ if (MP_OBJ_PART_PFX.compare(0, std::string::npos, ent.key.name,
+ MP_OBJ_PART_PFX.size() != 0)) {
+ // Skip non-parts
+ continue;
+ }
+
+ obj = shadow->get_object(rgw_obj_key(ent.key));
+ shadow_obj = static_cast<POSIXObject*>(obj.get());
+ ret = shadow_obj->get_obj_attrs(y, dpp);
+ if (ret < 0) {
+ return ret;
+ }
+ bufferlist etag_bl;
+ if (!get_attr(shadow_obj->get_attrs(), RGW_ATTR_ETAG, etag_bl)) {
+ // Generate part's etag
+ ret = shadow_obj->generate_etag(dpp, y);
+ if (ret < 0)
+ return ret;
+ }
+ if (!get_attr(shadow_obj->get_attrs(), RGW_ATTR_ETAG, etag_bl)) {
+ // Can't get etag.
+ return -EINVAL;
+ }
+ hex_to_buf(etag_bl.c_str(), etag_buf, CEPH_CRYPTO_MD5_DIGESTSIZE);
+ hash.Update((const unsigned char *)etag_buf, sizeof(etag_buf));
+ count++;
+ }
+ } while (results.is_truncated);
+
+ hash.Final((unsigned char *)etag_buf);
+
+ buf_to_hex((unsigned char *)etag_buf, sizeof(etag_buf), final_etag_str);
+ snprintf(&final_etag_str[CEPH_CRYPTO_MD5_DIGESTSIZE * 2],
+ sizeof(final_etag_str) - CEPH_CRYPTO_MD5_DIGESTSIZE * 2,
+ "-%lld", (long long)count);
+ etag = final_etag_str;
+ ldpp_dout(dpp, 10) << "calculated etag: " << etag << dendl;
+
+ etag_bl.append(etag);
+ (void)write_attr(dpp, y, RGW_ATTR_ETAG, etag_bl);
+ get_attrs().emplace(std::move(RGW_ATTR_ETAG), std::move(etag_bl));
+
+ return 0;
+}
+
+int POSIXObject::generate_etag(const DoutPrefixProvider* dpp, optional_yield y)
+{
+ int64_t left = get_obj_size();
+ int64_t cur_ofs = 0;
+ MD5 hash;
+ // Allow use of MD5 digest in FIPS mode for non-cryptographic purposes
+ hash.SetFlags(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
+ char calc_md5[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 1];
+ unsigned char m[CEPH_CRYPTO_MD5_DIGESTSIZE];
+
+ bufferlist etag_bl;
+
+ while (left > 0) {
+ bufferlist bl;
+ int len = read(cur_ofs, left, bl, dpp, y);
+ if (len < 0) {
+ ldpp_dout(dpp, 0) << " ERROR: could not read " << get_name() <<
+ " ofs: " << cur_ofs << " error: " << cpp_strerror(len) << dendl;
+ return len;
+ } else if (len == 0) {
+ /* Done */
+ break;
+ }
+ hash.Update((const unsigned char *)bl.c_str(), bl.length());
+
+ left -= len;
+ cur_ofs += len;
+ }
+
+ hash.Final(m);
+ buf_to_hex(m, CEPH_CRYPTO_MD5_DIGESTSIZE, calc_md5);
+ etag_bl.append(calc_md5, sizeof(calc_md5));
+ (void)write_attr(dpp, y, RGW_ATTR_ETAG, etag_bl);
+ get_attrs().emplace(std::move(RGW_ATTR_ETAG), std::move(etag_bl));
+
+ return 0;
+}
+
+const std::string POSIXObject::get_fname()
+{
+ std::string fname = url_encode(get_obj().get_oid(), true);
+
+ if (!get_obj().key.get_ns().empty()) {
+ /* Namespaced objects are hidden */
+ fname.insert(0, 1, '.');
+ }
+
+ return fname;
+}
+
+void POSIXObject::gen_temp_fname()
+{
+ enum { RAND_SUFFIX_SIZE = 8 };
+ char buf[RAND_SUFFIX_SIZE + 1];
+
+ gen_rand_alphanumeric_no_underscore(driver->ctx(), buf, RAND_SUFFIX_SIZE);
+ temp_fname = "." + get_fname() + ".";
+ temp_fname.append(buf);
+}
+
+const std::string POSIXObject::get_temp_fname()
+{
+ return temp_fname;
+}
+
+int POSIXObject::POSIXReadOp::iterate(const DoutPrefixProvider* dpp, int64_t ofs,
+ int64_t end, RGWGetDataCB* cb, optional_yield y)
+{
+ int64_t left;
+ int64_t cur_ofs = ofs;
+
+ if (end < 0)
+ left = 0;
+ else
+ left = end - ofs + 1;
+
+ while (left > 0) {
+ bufferlist bl;
+ int len = source->read(cur_ofs, left, bl, dpp, y);
+ if (len < 0) {
+ ldpp_dout(dpp, 0) << " ERROR: could not read " << source->get_name() <<
+ " ofs: " << cur_ofs << " error: " << cpp_strerror(len) << dendl;
+ return len;
+ } else if (len == 0) {
+ /* Done */
+ break;
+ }
+
+ /* Read some */
+ int ret = cb->handle_data(bl, 0, len);
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << " ERROR: callback failed on " << source->get_name() << dendl;
+ return ret;
+ }
+
+ left -= len;
+ cur_ofs += len;
+ }
+
+ /* Doesn't seem to be anything needed from params */
+ return 0;
+}
+
+int POSIXObject::POSIXReadOp::get_attr(const DoutPrefixProvider* dpp, const char* name, bufferlist& dest, optional_yield y)
+{
+ if (!source->exists(dpp)) {
+ return -ENOENT;
+ }
+ if (source->get_obj_attrs(y, dpp) < 0) {
+ return -ENODATA;
+ }
+ if (!rgw::sal::get_attr(source->get_attrs(), name, dest)) {
+ return -ENODATA;
+ }
+
+ return 0;
+}
+
+int POSIXObject::POSIXDeleteOp::delete_obj(const DoutPrefixProvider* dpp,
+ optional_yield y)
+{
+ return source->delete_object(dpp, y, false);
+}
+
+int POSIXObject::copy(const DoutPrefixProvider *dpp, optional_yield y,
+ POSIXBucket *sb, POSIXBucket *db, POSIXObject *dobj)
+{
+ off64_t scount = 0, dcount = 0;
+
+ int ret = open(dpp, false);
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: could not open source object " << get_name()
+ << dendl;
+ return ret;
+ }
+
+ // Delete the target, in case it's a multipart
+ ret = dobj->delete_object(dpp, y);
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: could not remove dest object "
+ << dobj->get_name() << dendl;
+ return ret;
+ }
+
+ ret = dobj->open(dpp, true);
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: could not open dest object "
+ << dobj->get_name() << dendl;
+ return ret;
+ }
+
+ ret = copy_file_range(obj_fd, &scount, dobj->get_fd(), &dcount, stx.stx_size, 0);
+ if (ret < 0) {
+ ret = errno;
+ ldpp_dout(dpp, 0) << "ERROR: could not copy object " << dobj->get_name()
+ << ": " << cpp_strerror(ret) << dendl;
+ return -ret;
+ }
+
+ ret = get_obj_attrs(y, dpp);
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: could not get attrs for source object "
+ << get_name() << dendl;
+ return ret;
+ }
+
+ ret = dobj->set_obj_attrs(dpp, &get_attrs(), NULL, y);
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: could not write attrs to dest object "
+ << dobj->get_name() << dendl;
+ return ret;
+ }
+
+ return 0;
+}
+
+void POSIXMPObj::init_gen(POSIXDriver* driver, const std::string& _oid, ACLOwner& _owner)
+{
+ char buf[33];
+ std::string new_id = MULTIPART_UPLOAD_ID_PREFIX; /* v2 upload id */
+ /* Generate an upload ID */
+
+ gen_rand_alphanumeric(driver->ctx(), buf, sizeof(buf) - 1);
+ new_id.append(buf);
+ init(_oid, new_id, _owner);
+}
+
+int POSIXMultipartPart::load(const DoutPrefixProvider* dpp, optional_yield y,
+ POSIXDriver* driver, rgw_obj_key& key)
+{
+ if (shadow) {
+ /* Already loaded */
+ return 0;
+ }
+
+ shadow = std::make_unique<POSIXObject>(driver, key, upload->get_shadow());
+
+ RGWObjState* pstate;
+ // Stat the shadow object to get things like size
+ int ret = shadow->get_obj_state(dpp, &pstate, y);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = shadow->get_obj_attrs(y, dpp);
+ if (ret < 0) {
+ return ret;
+ }
+
+ auto ait = shadow->get_attrs().find(RGW_POSIX_ATTR_MPUPLOAD);
+ if (ait == shadow->get_attrs().end()) {
+ ldout(driver->ctx(), 0) << "ERROR: " << __func__ << ": Not a part: " << key << dendl;
+ return -EINVAL;
+ }
+
+ try {
+ auto bit = ait->second.cbegin();
+ decode(info, bit);
+ } catch (buffer::error& err) {
+ ldout(driver->ctx(), 0) << "ERROR: " << __func__ << ": failed to decode part info: " << key << dendl;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int POSIXMultipartUpload::load(bool create)
+{
+ if (!shadow) {
+ POSIXBucket* pb = static_cast<POSIXBucket*>(bucket);
+ return pb->get_shadow_bucket(nullptr, null_yield, mp_ns,
+ std::string(), get_meta(), create, &shadow);
+ }
+
+ return 0;
+}
+
+std::unique_ptr<rgw::sal::Object> POSIXMultipartUpload::get_meta_obj()
+{
+ load();
+ if (!shadow) {
+ // This upload doesn't exist, but the API doesn't check this until it calls
+ // on the *serializer*. So make a fake object in the parent bucket that
+ // doesn't exist. Put it in the MP namespace just in case.
+ return bucket->get_object(rgw_obj_key(get_meta(), std::string(), mp_ns));
+ }
+ return shadow->get_object(rgw_obj_key(get_meta(), std::string()));
+}
+
+int POSIXMultipartUpload::init(const DoutPrefixProvider *dpp, optional_yield y,
+ ACLOwner& owner, rgw_placement_rule& dest_placement,
+ rgw::sal::Attrs& attrs)
+{
+ int ret;
+
+ /* Create the shadow bucket */
+ ret = load(true);
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << " ERROR: could not get shadow bucket for mp upload "
+ << get_key() << dendl;
+ return ret;
+ }
+
+ /* Now create the meta object */
+ std::unique_ptr<rgw::sal::Object> meta_obj;
+
+ meta_obj = get_meta_obj();
+
+ mp_obj.upload_info.dest_placement = dest_placement;
+
+ bufferlist bl;
+ encode(mp_obj, bl);
+
+ attrs[RGW_POSIX_ATTR_MPUPLOAD] = bl;
+
+ return meta_obj->set_obj_attrs(dpp, &attrs, nullptr, y);
+}
+
+int POSIXMultipartUpload::list_parts(const DoutPrefixProvider *dpp, CephContext *cct,
+ int num_parts, int marker,
+ int *next_marker, bool *truncated, optional_yield y,
+ bool assume_unsorted)
+{
+ int ret;
+ int last_num = 0;
+
+ ret = load();
+ if (ret < 0) {
+ return ret;
+ }
+
+ rgw::sal::Bucket::ListParams params;
+ rgw::sal::Bucket::ListResults results;
+
+ params.prefix = MP_OBJ_PART_PFX;
+ params.marker = MP_OBJ_PART_PFX + fmt::format("{:0>5}", marker);
+
+ ret = shadow->list(dpp, params, num_parts + 1, results, y);
+ if (ret < 0) {
+ return ret;
+ }
+ for (rgw_bucket_dir_entry& ent : results.objs) {
+ std::unique_ptr<MultipartPart> part = std::make_unique<POSIXMultipartPart>(this);
+ POSIXMultipartPart* ppart = static_cast<POSIXMultipartPart*>(part.get());
+
+ rgw_obj_key key(ent.key);
+ ret = ppart->load(dpp, y, driver, key);
+ if (ret == 0) {
+ /* Skip anything that's not a part */
+ last_num = part->get_num();
+ parts[part->get_num()] = std::move(part);
+ }
+ if (parts.size() == (ulong)num_parts)
+ break;
+ }
+
+ if (truncated)
+ *truncated = results.is_truncated;
+
+ if (next_marker)
+ *next_marker = last_num;
+
+ return 0;
+}
+
+int POSIXMultipartUpload::abort(const DoutPrefixProvider *dpp, CephContext *cct, optional_yield y)
+{
+ int ret;
+
+ ret = load();
+ if (ret < 0) {
+ return ret;
+ }
+
+ shadow->remove_bucket(dpp, true, false, nullptr, y);
+
+ return 0;
+}
+
+int POSIXMultipartUpload::complete(const DoutPrefixProvider *dpp,
+ optional_yield y, CephContext* cct,
+ std::map<int, std::string>& part_etags,
+ std::list<rgw_obj_index_key>& remove_objs,
+ uint64_t& accounted_size, bool& compressed,
+ RGWCompressionInfo& cs_info, off_t& ofs,
+ std::string& tag, ACLOwner& owner,
+ uint64_t olh_epoch,
+ rgw::sal::Object* target_obj)
+{
+ char final_etag[CEPH_CRYPTO_MD5_DIGESTSIZE];
+ char final_etag_str[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 16];
+ std::string etag;
+ bufferlist etag_bl;
+ MD5 hash;
+ // Allow use of MD5 digest in FIPS mode for non-cryptographic purposes
+ hash.SetFlags(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
+ bool truncated;
+ int ret;
+
+ int total_parts = 0;
+ int handled_parts = 0;
+ int max_parts = 1000;
+ int marker = 0;
+ uint64_t min_part_size = cct->_conf->rgw_multipart_min_part_size;
+ auto etags_iter = part_etags.begin();
+ rgw::sal::Attrs attrs = target_obj->get_attrs();
+
+ do {
+ ret = list_parts(dpp, cct, max_parts, marker, &marker, &truncated, y);
+ if (ret == -ENOENT) {
+ ret = -ERR_NO_SUCH_UPLOAD;
+ }
+ if (ret < 0)
+ return ret;
+
+ total_parts += parts.size();
+ if (!truncated && total_parts != (int)part_etags.size()) {
+ ldpp_dout(dpp, 0) << "NOTICE: total parts mismatch: have: " << total_parts
+ << " expected: " << part_etags.size() << dendl;
+ ret = -ERR_INVALID_PART;
+ return ret;
+ }
+
+ for (auto obj_iter = parts.begin(); etags_iter != part_etags.end() && obj_iter != parts.end(); ++etags_iter, ++obj_iter, ++handled_parts) {
+ POSIXMultipartPart* part = static_cast<rgw::sal::POSIXMultipartPart*>(obj_iter->second.get());
+ uint64_t part_size = part->get_size();
+ if (handled_parts < (int)part_etags.size() - 1 &&
+ part_size < min_part_size) {
+ ret = -ERR_TOO_SMALL;
+ return ret;
+ }
+
+ char petag[CEPH_CRYPTO_MD5_DIGESTSIZE];
+ if (etags_iter->first != (int)obj_iter->first) {
+ ldpp_dout(dpp, 0) << "NOTICE: parts num mismatch: next requested: "
+ << etags_iter->first << " next uploaded: "
+ << obj_iter->first << dendl;
+ ret = -ERR_INVALID_PART;
+ return ret;
+ }
+ std::string part_etag = rgw_string_unquote(etags_iter->second);
+ if (part_etag.compare(part->get_etag()) != 0) {
+ ldpp_dout(dpp, 0) << "NOTICE: etag mismatch: part: " << etags_iter->first
+ << " etag: " << etags_iter->second << dendl;
+ ret = -ERR_INVALID_PART;
+ return ret;
+ }
+
+ hex_to_buf(part->get_etag().c_str(), petag,
+ CEPH_CRYPTO_MD5_DIGESTSIZE);
+ hash.Update((const unsigned char *)petag, sizeof(petag));
+
+ // Compression is not supported yet
+#if 0
+ RGWUploadPartInfo& obj_part = part->info;
+
+ bool part_compressed = (obj_part.cs_info.compression_type != "none");
+ if ((handled_parts > 0) &&
+ ((part_compressed != compressed) ||
+ (cs_info.compression_type != obj_part.cs_info.compression_type))) {
+ ldpp_dout(dpp, 0) << "ERROR: compression type was changed during multipart upload ("
+ << cs_info.compression_type << ">>" << obj_part.cs_info.compression_type << ")" << dendl;
+ ret = -ERR_INVALID_PART;
+ return ret;
+ }
+
+ if (part_compressed) {
+ int64_t new_ofs; // offset in compression data for new part
+ if (cs_info.blocks.size() > 0)
+ new_ofs = cs_info.blocks.back().new_ofs + cs_info.blocks.back().len;
+ else
+ new_ofs = 0;
+ for (const auto& block : obj_part.cs_info.blocks) {
+ compression_block cb;
+ cb.old_ofs = block.old_ofs + cs_info.orig_size;
+ cb.new_ofs = new_ofs;
+ cb.len = block.len;
+ cs_info.blocks.push_back(cb);
+ new_ofs = cb.new_ofs + cb.len;
+ }
+ if (!compressed)
+ cs_info.compression_type = obj_part.cs_info.compression_type;
+ cs_info.orig_size += obj_part.cs_info.orig_size;
+ compressed = true;
+ }
+#endif
+
+ ofs += part->get_size();
+ accounted_size += part->get_size();
+ }
+ } while (truncated);
+ hash.Final((unsigned char *)final_etag);
+
+ buf_to_hex((unsigned char *)final_etag, sizeof(final_etag), final_etag_str);
+ snprintf(&final_etag_str[CEPH_CRYPTO_MD5_DIGESTSIZE * 2],
+ sizeof(final_etag_str) - CEPH_CRYPTO_MD5_DIGESTSIZE * 2,
+ "-%lld", (long long)part_etags.size());
+ etag = final_etag_str;
+ ldpp_dout(dpp, 10) << "calculated etag: " << etag << dendl;
+
+ etag_bl.append(etag);
+
+ attrs[RGW_ATTR_ETAG] = etag_bl;
+
+ if (compressed) {
+ // write compression attribute to full object
+ bufferlist tmp;
+ encode(cs_info, tmp);
+ attrs[RGW_ATTR_COMPRESSION] = tmp;
+ }
+
+ ret = shadow->merge_and_store_attrs(dpp, attrs, y);
+ if (ret < 0) {
+ return ret;
+ }
+
+ // Rename to target_obj
+ return shadow->rename(dpp, y, target_obj);
+}
+
+int POSIXMultipartUpload::get_info(const DoutPrefixProvider *dpp, optional_yield y,
+ rgw_placement_rule** rule, rgw::sal::Attrs* attrs)
+{
+ std::unique_ptr<rgw::sal::Object> meta_obj;
+ int ret;
+
+ if (!rule && !attrs) {
+ return 0;
+ }
+
+ if (attrs) {
+ meta_obj = get_meta_obj();
+ int ret = meta_obj->get_obj_attrs(y, dpp);
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << " ERROR: could not get meta object for mp upload "
+ << get_key() << dendl;
+ return ret;
+ }
+ *attrs = meta_obj->get_attrs();
+ }
+
+ if (rule) {
+ if (mp_obj.oid.empty()) {
+ if (!meta_obj) {
+ meta_obj = get_meta_obj();
+ ret = meta_obj->get_obj_attrs(y, dpp);
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << " ERROR: could not get meta object for mp upload "
+ << get_key() << dendl;
+ return ret;
+ }
+ }
+ bufferlist bl;
+ if (!get_attr(meta_obj->get_attrs(), RGW_POSIX_ATTR_MPUPLOAD, bl)) {
+ ldpp_dout(dpp, 0) << " ERROR: could not get meta object attrs for mp upload "
+ << get_key() << dendl;
+ return ret;
+ }
+ auto biter = bl.cbegin();
+ decode(mp_obj, biter);
+ }
+ *rule = &mp_obj.upload_info.dest_placement;
+ }
+
+ return 0;
+}
+
+std::unique_ptr<Writer> POSIXMultipartUpload::get_writer(
+ const DoutPrefixProvider *dpp,
+ optional_yield y,
+ rgw::sal::Object* _head_obj,
+ const rgw_user& owner,
+ const rgw_placement_rule *ptail_placement_rule,
+ uint64_t part_num,
+ const std::string& part_num_str)
+{
+ std::string fname = MP_OBJ_PART_PFX + fmt::format("{:0>5}", part_num);
+ rgw_obj_key part_key(fname);
+
+ load();
+
+ return std::make_unique<POSIXMultipartWriter>(dpp, y, shadow->clone(), part_key, driver,
+ owner, ptail_placement_rule, part_num);
+}
+
+int POSIXMultipartWriter::prepare(optional_yield y)
+{
+ return obj->open(dpp, true);
+}
+
+int POSIXMultipartWriter::process(bufferlist&& data, uint64_t offset)
+{
+ return obj->write(offset, data, dpp, null_yield);
+}
+
+int POSIXMultipartWriter::complete(size_t accounted_size, const std::string& etag,
+ ceph::real_time *mtime, ceph::real_time set_mtime,
+ std::map<std::string, bufferlist>& attrs,
+ ceph::real_time delete_at,
+ const char *if_match, const char *if_nomatch,
+ const std::string *user_data,
+ rgw_zone_set *zones_trace, bool *canceled,
+ optional_yield y)
+{
+ int ret;
+ POSIXUploadPartInfo info;
+
+ if (if_match) {
+ if (strcmp(if_match, "*") == 0) {
+ // test the object is existing
+ if (!obj->exists(dpp)) {
+ return -ERR_PRECONDITION_FAILED;
+ }
+ } else {
+ bufferlist bl;
+ if (!get_attr(obj->get_attrs(), RGW_ATTR_ETAG, bl)) {
+ return -ERR_PRECONDITION_FAILED;
+ }
+ if (strncmp(if_match, bl.c_str(), bl.length()) != 0) {
+ return -ERR_PRECONDITION_FAILED;
+ }
+ }
+ }
+
+ info.num = part_num;
+ info.etag = etag;
+ info.mtime = set_mtime;
+
+ bufferlist bl;
+ encode(info, bl);
+ attrs[RGW_POSIX_ATTR_MPUPLOAD] = bl;
+
+ for (auto& attr : attrs) {
+ ret = obj->write_attr(dpp, y, attr.first, attr.second);
+ if (ret < 0) {
+ ldpp_dout(dpp, 20) << "ERROR: failed writing attr " << attr.first << dendl;
+ return ret;
+ }
+ }
+
+ ret = obj->close();
+ if (ret < 0) {
+ ldpp_dout(dpp, 20) << "ERROR: failed closing file" << dendl;
+ return ret;
+ }
+
+ return 0;
+}
+
+int POSIXAtomicWriter::prepare(optional_yield y)
+{
+ obj.get_obj_attrs(y, dpp);
+ obj.close();
+ obj.gen_temp_fname();
+ return obj.open(dpp, true, true);
+}
+
+int POSIXAtomicWriter::process(bufferlist&& data, uint64_t offset)
+{
+ return obj.write(offset, data, dpp, null_yield);
+}
+
+int POSIXAtomicWriter::complete(size_t accounted_size, const std::string& etag,
+ ceph::real_time *mtime, ceph::real_time set_mtime,
+ std::map<std::string, bufferlist>& attrs,
+ ceph::real_time delete_at,
+ const char *if_match, const char *if_nomatch,
+ const std::string *user_data,
+ rgw_zone_set *zones_trace, bool *canceled,
+ optional_yield y)
+{
+ int ret;
+
+ if (if_match) {
+ if (strcmp(if_match, "*") == 0) {
+ // test the object is existing
+ if (!obj.exists(dpp)) {
+ return -ERR_PRECONDITION_FAILED;
+ }
+ } else {
+ bufferlist bl;
+ if (!get_attr(obj.get_attrs(), RGW_ATTR_ETAG, bl)) {
+ return -ERR_PRECONDITION_FAILED;
+ }
+ if (strncmp(if_match, bl.c_str(), bl.length()) != 0) {
+ return -ERR_PRECONDITION_FAILED;
+ }
+ }
+ }
+ if (if_nomatch) {
+ if (strcmp(if_nomatch, "*") == 0) {
+ // test the object is not existing
+ if (obj.exists(dpp)) {
+ return -ERR_PRECONDITION_FAILED;
+ }
+ } else {
+ bufferlist bl;
+ if (!get_attr(obj.get_attrs(), RGW_ATTR_ETAG, bl)) {
+ return -ERR_PRECONDITION_FAILED;
+ }
+ if (strncmp(if_nomatch, bl.c_str(), bl.length()) == 0) {
+ return -ERR_PRECONDITION_FAILED;
+ }
+ }
+ }
+
+ bufferlist bl;
+ encode(owner, bl);
+ attrs[RGW_POSIX_ATTR_OWNER] = bl;
+
+ for (auto attr : attrs) {
+ ret = obj.write_attr(dpp, y, attr.first, attr.second);
+ if (ret < 0) {
+ ldpp_dout(dpp, 20) << "ERROR: POSIXAtomicWriter failed writing attr " << attr.first << dendl;
+ return ret;
+ }
+ }
+
+ ret = obj.link_temp_file(dpp, y);
+ if (ret < 0) {
+ ldpp_dout(dpp, 20) << "ERROR: POSIXAtomicWriter failed writing temp file" << dendl;
+ return ret;
+ }
+
+ ret = obj.close();
+ if (ret < 0) {
+ ldpp_dout(dpp, 20) << "ERROR: POSIXAtomicWriter failed closing file" << dendl;
+ return ret;
+ }
+
+ return 0;
+}
+
+} } // namespace rgw::sal
+
+extern "C" {
+
+rgw::sal::Driver* newPOSIXDriver(rgw::sal::Driver* next)
+{
+ rgw::sal::POSIXDriver* driver = new rgw::sal::POSIXDriver(next);
+
+ return driver;
+}
+
+}
--- /dev/null
+/*
+MIT License
+
+Copyright (c) 2021 Eyal Z
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+*/
+#ifndef ZPP_BITS_H
+#define ZPP_BITS_H
+
+#include <algorithm>
+#include <array>
+#include <bit>
+#include <climits>
+#include <compare>
+#include <concepts>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <numeric>
+#include <optional>
+#include <span>
+#include <system_error>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <variant>
+#include <vector>
+#if __has_include("zpp_throwing.h")
+#include "zpp_throwing.h"
+#endif
+
+#ifdef __cpp_exceptions
+#include <stdexcept>
+#endif
+
+#ifndef ZPP_BITS_AUTODETECT_MEMBERS_MODE
+#define ZPP_BITS_AUTODETECT_MEMBERS_MODE (0)
+#endif
+
+#ifndef ZPP_BITS_INLINE
+#if defined __clang__ || defined __GNUC__
+#define ZPP_BITS_INLINE __attribute__((always_inline))
+#if defined __clang__
+#define ZPP_BITS_CONSTEXPR_INLINE_LAMBDA __attribute__((always_inline)) constexpr
+#else
+#define ZPP_BITS_CONSTEXPR_INLINE_LAMBDA constexpr __attribute__((always_inline))
+#endif
+#elif defined _MSC_VER
+#define ZPP_BITS_INLINE [[msvc::forceinline]]
+#define ZPP_BITS_CONSTEXPR_INLINE_LAMBDA /*constexpr*/ [[msvc::forceinline]]
+#endif
+#else // ZPP_BITS_INLINE
+#define ZPP_BITS_CONSTEXPR_INLINE_LAMBDA constexpr
+#endif // ZPP_BITS_INLINE
+
+#if defined ZPP_BITS_INLINE_MODE && !ZPP_BITS_INLINE_MODE
+#undef ZPP_BITS_INLINE
+#define ZPP_BITS_INLINE
+#undef ZPP_BITS_CONSTEXPR_INLINE_LAMBDA
+#define ZPP_BITS_CONSTEXPR_INLINE_LAMBDA constexpr
+#endif
+
+#ifndef ZPP_BITS_INLINE_DECODE_VARINT
+#define ZPP_BITS_INLINE_DECODE_VARINT (0)
+#endif
+
+namespace zpp::bits
+{
+using default_size_type = std::uint32_t;
+
+#ifndef __cpp_lib_bit_cast
+namespace std
+{
+using namespace ::std;
+template <class ToType,
+ class FromType,
+ class = enable_if_t<sizeof(ToType) == sizeof(FromType) &&
+ is_trivially_copyable_v<ToType> &&
+ is_trivially_copyable_v<FromType>>>
+constexpr ToType bit_cast(FromType const & from) noexcept
+{
+ return __builtin_bit_cast(ToType, from);
+}
+} // namespace std
+#endif
+
+enum class kind
+{
+ in,
+ out
+};
+
+template <std::size_t Count = std::numeric_limits<std::size_t>::max()>
+struct members
+{
+ constexpr static std::size_t value = Count;
+};
+
+template <auto Protocol, std::size_t Members = std::numeric_limits<std::size_t>::max()>
+struct protocol
+{
+ constexpr static auto value = Protocol;
+ constexpr static auto members = Members;
+};
+
+template <auto Id>
+struct serialization_id
+{
+ constexpr static auto value = Id;
+};
+
+constexpr auto success(std::errc code)
+{
+ return std::errc{} == code;
+}
+
+constexpr auto failure(std::errc code)
+{
+ return std::errc{} != code;
+}
+
+struct [[nodiscard]] errc
+{
+ constexpr errc(std::errc code = {}) : code(code)
+ {
+ }
+
+#if __has_include("zpp_throwing.h")
+ constexpr zpp::throwing<void> operator co_await() const
+ {
+ if (failure(code)) [[unlikely]] {
+ return code;
+ }
+ return zpp::void_v;
+ }
+#endif
+
+ constexpr operator std::errc() const
+ {
+ return code;
+ }
+
+ constexpr void or_throw() const
+ {
+ if (failure(code)) [[unlikely]] {
+#ifdef __cpp_exceptions
+ throw std::system_error(std::make_error_code(code));
+#else
+ std::abort();
+#endif
+ }
+ }
+
+ std::errc code;
+};
+
+constexpr auto success(errc code)
+{
+ return std::errc{} == code;
+}
+
+constexpr auto failure(errc code)
+{
+ return std::errc{} != code;
+}
+
+struct access
+{
+ struct any
+ {
+ template <typename Type>
+ operator Type();
+ };
+
+ template <typename Item>
+ constexpr static auto make(auto &&... arguments)
+ {
+ return Item{std::forward<decltype(arguments)>(arguments)...};
+ }
+
+ template <typename Item>
+ constexpr static auto placement_new(void * address,
+ auto &&... arguments)
+ {
+ return ::new (address)
+ Item(std::forward<decltype(arguments)>(arguments)...);
+ }
+
+ template <typename Item>
+ constexpr static auto make_unique(auto &&... arguments)
+ {
+ return std::unique_ptr<Item>(
+ new Item(std::forward<decltype(arguments)>(arguments)...));
+ }
+
+ template <typename Item>
+ constexpr static void destruct(Item & item)
+ {
+ item.~Item();
+ }
+
+ template <typename Type>
+ constexpr static auto number_of_members();
+
+ constexpr static auto max_visit_members = 50;
+
+ ZPP_BITS_INLINE constexpr static decltype(auto) visit_members(
+ auto && object,
+ auto && visitor) requires(0 <=
+ number_of_members<decltype(object)>()) &&
+ (number_of_members<decltype(object)>() <= max_visit_members)
+ {
+ constexpr auto count = number_of_members<decltype(object)>();
+
+ // clang-format off
+ if constexpr (count == 0) { return visitor(); } else if constexpr (count == 1) { auto && [a1] = object; return visitor(a1); } else if constexpr (count == 2) { auto && [a1, a2] = object; return visitor(a1, a2); /*......................................................................................................................................................................................................................................................................*/ } else if constexpr (count == 3) { auto && [a1, a2, a3] = object; return visitor(a1, a2, a3); } else if constexpr (count == 4) { auto && [a1, a2, a3, a4] = object; return visitor(a1, a2, a3, a4); } else if constexpr (count == 5) { auto && [a1, a2, a3, a4, a5] = object; return visitor(a1, a2, a3, a4, a5); } else if constexpr (count == 6) { auto && [a1, a2, a3, a4, a5, a6] = object; return visitor(a1, a2, a3, a4, a5, a6); } else if constexpr (count == 7) { auto && [a1, a2, a3, a4, a5, a6, a7] = object; return visitor(a1, a2, a3, a4, a5, a6, a7); } else if constexpr (count == 8) { auto && [a1, a2, a3, a4, a5, a6, a7, a8] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8); } else if constexpr (count == 9) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9); } else if constexpr (count == 10) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); } else if constexpr (count == 11) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); } else if constexpr (count == 12) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); } else if constexpr (count == 13) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); } else if constexpr (count == 14) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); } else if constexpr (count == 15) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); } else if constexpr (count == 16) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16); } else if constexpr (count == 17) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17); } else if constexpr (count == 18) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18); } else if constexpr (count == 19) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19); } else if constexpr (count == 20) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); } else if constexpr (count == 21) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21); } else if constexpr (count == 22) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22); } else if constexpr (count == 23) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23); } else if constexpr (count == 24) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24); } else if constexpr (count == 25) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25); } else if constexpr (count == 26) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26); } else if constexpr (count == 27) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27); } else if constexpr (count == 28) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28); } else if constexpr (count == 29) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29); } else if constexpr (count == 30) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30); } else if constexpr (count == 31) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31); } else if constexpr (count == 32) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32); } else if constexpr (count == 33) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33); } else if constexpr (count == 34) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34); } else if constexpr (count == 35) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35); } else if constexpr (count == 36) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36); } else if constexpr (count == 37) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37); } else if constexpr (count == 38) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38); } else if constexpr (count == 39) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39); } else if constexpr (count == 40) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40); } else if constexpr (count == 41) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41); } else if constexpr (count == 42) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42); } else if constexpr (count == 43) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43); } else if constexpr (count == 44) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44); } else if constexpr (count == 45) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45); } else if constexpr (count == 46) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46); } else if constexpr (count == 47) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47); } else if constexpr (count == 48) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48); } else if constexpr (count == 49) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49); } else if constexpr (count == 50) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50] = object; return visitor(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50);
+ // Calls the visitor above with all data members of object.
+ }
+ // clang-format on
+ }
+
+ template <typename Type>
+ constexpr static decltype(auto)
+ visit_members_types(auto && visitor) requires(0 <= number_of_members<Type>()) &&
+ (number_of_members<Type>() <= max_visit_members)
+
+ {
+ using type = std::remove_cvref_t<Type>;
+ constexpr auto count = number_of_members<Type>();
+
+ // clang-format off
+ if constexpr (count == 0) { return visitor.template operator()<>(); } else if constexpr (count == 1) { auto f = [&](auto && object) { auto && [a1] = object; return visitor.template operator()<decltype(a1)>(); }; /*......................................................................................................................................................................................................................................................................*/ return decltype(f(std::declval<type>()))(); } else if constexpr (count == 2) { auto f = [&](auto && object) { auto && [a1, a2] = object; return visitor.template operator()<decltype(a1), decltype(a2)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 3) { auto f = [&](auto && object) { auto && [a1, a2, a3] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 4) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 5) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 6) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 7) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 8) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 9) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 10) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 11) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 12) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 13) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 14) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 15) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 16) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 17) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 18) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 19) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 20) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 21) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 22) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 23) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 24) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 25) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 26) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 27) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 28) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 29) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28), decltype(a29)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 30) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28), decltype(a29), decltype(a30)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 31) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28), decltype(a29), decltype(a30), decltype(a31)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 32) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28), decltype(a29), decltype(a30), decltype(a31), decltype(a32)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 33) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28), decltype(a29), decltype(a30), decltype(a31), decltype(a32), decltype(a33)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 34) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28), decltype(a29), decltype(a30), decltype(a31), decltype(a32), decltype(a33), decltype(a34)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 35) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28), decltype(a29), decltype(a30), decltype(a31), decltype(a32), decltype(a33), decltype(a34), decltype(a35)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 36) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28), decltype(a29), decltype(a30), decltype(a31), decltype(a32), decltype(a33), decltype(a34), decltype(a35), decltype(a36)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 37) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28), decltype(a29), decltype(a30), decltype(a31), decltype(a32), decltype(a33), decltype(a34), decltype(a35), decltype(a36), decltype(a37)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 38) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28), decltype(a29), decltype(a30), decltype(a31), decltype(a32), decltype(a33), decltype(a34), decltype(a35), decltype(a36), decltype(a37), decltype(a38)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 39) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28), decltype(a29), decltype(a30), decltype(a31), decltype(a32), decltype(a33), decltype(a34), decltype(a35), decltype(a36), decltype(a37), decltype(a38), decltype(a39)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 40) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28), decltype(a29), decltype(a30), decltype(a31), decltype(a32), decltype(a33), decltype(a34), decltype(a35), decltype(a36), decltype(a37), decltype(a38), decltype(a39), decltype(a40)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 41) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28), decltype(a29), decltype(a30), decltype(a31), decltype(a32), decltype(a33), decltype(a34), decltype(a35), decltype(a36), decltype(a37), decltype(a38), decltype(a39), decltype(a40), decltype(a41)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 42) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28), decltype(a29), decltype(a30), decltype(a31), decltype(a32), decltype(a33), decltype(a34), decltype(a35), decltype(a36), decltype(a37), decltype(a38), decltype(a39), decltype(a40), decltype(a41), decltype(a42)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 43) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28), decltype(a29), decltype(a30), decltype(a31), decltype(a32), decltype(a33), decltype(a34), decltype(a35), decltype(a36), decltype(a37), decltype(a38), decltype(a39), decltype(a40), decltype(a41), decltype(a42), decltype(a43)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 44) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28), decltype(a29), decltype(a30), decltype(a31), decltype(a32), decltype(a33), decltype(a34), decltype(a35), decltype(a36), decltype(a37), decltype(a38), decltype(a39), decltype(a40), decltype(a41), decltype(a42), decltype(a43), decltype(a44)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 45) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28), decltype(a29), decltype(a30), decltype(a31), decltype(a32), decltype(a33), decltype(a34), decltype(a35), decltype(a36), decltype(a37), decltype(a38), decltype(a39), decltype(a40), decltype(a41), decltype(a42), decltype(a43), decltype(a44), decltype(a45)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 46) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28), decltype(a29), decltype(a30), decltype(a31), decltype(a32), decltype(a33), decltype(a34), decltype(a35), decltype(a36), decltype(a37), decltype(a38), decltype(a39), decltype(a40), decltype(a41), decltype(a42), decltype(a43), decltype(a44), decltype(a45), decltype(a46)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 47) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28), decltype(a29), decltype(a30), decltype(a31), decltype(a32), decltype(a33), decltype(a34), decltype(a35), decltype(a36), decltype(a37), decltype(a38), decltype(a39), decltype(a40), decltype(a41), decltype(a42), decltype(a43), decltype(a44), decltype(a45), decltype(a46), decltype(a47)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 48) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28), decltype(a29), decltype(a30), decltype(a31), decltype(a32), decltype(a33), decltype(a34), decltype(a35), decltype(a36), decltype(a37), decltype(a38), decltype(a39), decltype(a40), decltype(a41), decltype(a42), decltype(a43), decltype(a44), decltype(a45), decltype(a46), decltype(a47), decltype(a48)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 49) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28), decltype(a29), decltype(a30), decltype(a31), decltype(a32), decltype(a33), decltype(a34), decltype(a35), decltype(a36), decltype(a37), decltype(a38), decltype(a39), decltype(a40), decltype(a41), decltype(a42), decltype(a43), decltype(a44), decltype(a45), decltype(a46), decltype(a47), decltype(a48), decltype(a49)>(); }; return decltype(f(std::declval<type>()))(); } else if constexpr (count == 50) { auto f = [&](auto && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50] = object; return visitor.template operator()<decltype(a1), decltype(a2), decltype(a3), decltype(a4), decltype(a5), decltype(a6), decltype(a7), decltype(a8), decltype(a9), decltype(a10), decltype(a11), decltype(a12), decltype(a13), decltype(a14), decltype(a15), decltype(a16), decltype(a17), decltype(a18), decltype(a19), decltype(a20), decltype(a21), decltype(a22), decltype(a23), decltype(a24), decltype(a25), decltype(a26), decltype(a27), decltype(a28), decltype(a29), decltype(a30), decltype(a31), decltype(a32), decltype(a33), decltype(a34), decltype(a35), decltype(a36), decltype(a37), decltype(a38), decltype(a39), decltype(a40), decltype(a41), decltype(a42), decltype(a43), decltype(a44), decltype(a45), decltype(a46), decltype(a47), decltype(a48), decltype(a49), decltype(a50)>(); }; return decltype(f(std::declval<type>()))();
+ // Returns visitor.template operator()<member-types...>();
+ }
+ // clang-format on
+ }
+
+ constexpr static auto try_serialize(auto && item)
+ {
+ if constexpr (requires { serialize(item); }) {
+ return serialize(item);
+ }
+ }
+
+ template <typename Type, typename Archive>
+ constexpr static auto has_serialize()
+ {
+ return requires {
+ requires std::same_as<
+ typename std::remove_cvref_t<Type>::serialize,
+ members<
+ std::remove_cvref_t<Type>::serialize::value>>;
+ } ||
+ requires(Type && item) {
+ requires std::same_as<
+ std::remove_cvref_t<decltype(try_serialize(
+ item))>,
+ members<std::remove_cvref_t<
+ decltype(try_serialize(item))>::value>>;
+ } ||
+ requires {
+ requires std::same_as<
+ typename std::remove_cvref_t<Type>::serialize,
+ protocol<
+ std::remove_cvref_t<Type>::serialize::value,
+ std::remove_cvref_t<Type>::serialize::members>>;
+ } ||
+ requires(Type && item) {
+ requires std::same_as<
+ std::remove_cvref_t<decltype(try_serialize(
+ item))>,
+ protocol<
+ std::remove_cvref_t<decltype(try_serialize(
+ item))>::value,
+ std::remove_cvref_t<decltype(try_serialize(
+ item))>::members>>;
+ } ||
+ requires(Type && item, Archive && archive) {
+ std::remove_cvref_t<Type>::serialize(archive, item);
+ } || requires(Type && item, Archive && archive) {
+ serialize(archive, item);
+ };
+ }
+
+ template <typename Type, typename Archive>
+ constexpr static auto has_explicit_serialize()
+ {
+ return requires(Type && item, Archive && archive)
+ {
+ std::remove_cvref_t<Type>::serialize(archive, item);
+ }
+ || requires(Type && item, Archive && archive)
+ {
+ serialize(archive, item);
+ };
+ }
+
+ template <typename Type>
+ struct byte_serializable_visitor;
+
+ template <typename Type>
+ constexpr static auto byte_serializable();
+
+ template <typename Type>
+ struct endian_independent_byte_serializable_visitor;
+
+ template <typename Type>
+ constexpr static auto endian_independent_byte_serializable();
+
+ template <typename Type, typename Self, typename... Visited>
+ struct self_referencing_visitor;
+
+ template <typename Type, typename Self = Type, typename... Visited>
+ constexpr static auto self_referencing();
+
+ template <typename Type>
+ constexpr static auto has_protocol()
+ {
+ return requires
+ {
+ requires std::same_as<
+ typename std::remove_cvref_t<Type>::serialize,
+ protocol<std::remove_cvref_t<Type>::serialize::value,
+ std::remove_cvref_t<Type>::serialize::members>>;
+ }
+ || requires(Type && item)
+ {
+ requires std::same_as<
+ std::remove_cvref_t<decltype(try_serialize(item))>,
+ protocol<
+ std::remove_cvref_t<decltype(try_serialize(item))>::value,
+ std::remove_cvref_t<decltype(try_serialize(
+ item))>::members>>;
+ };
+ }
+
+ template <typename Type>
+ constexpr static auto get_protocol()
+ {
+ if constexpr (
+ requires {
+ requires std::same_as<
+ typename std::remove_cvref_t<Type>::serialize,
+ protocol<
+ std::remove_cvref_t<Type>::serialize::value,
+ std::remove_cvref_t<Type>::serialize::members>>;
+ }) {
+ return std::remove_cvref_t<Type>::serialize::value;
+ } else if constexpr (
+ requires(Type && item) {
+ requires std::same_as<
+ std::remove_cvref_t<decltype(try_serialize(item))>,
+ protocol<std::remove_cvref_t<decltype(try_serialize(
+ item))>::value,
+ std::remove_cvref_t<decltype(try_serialize(
+ item))>::members>>;
+ }) {
+ return std::remove_cvref_t<decltype(try_serialize(
+ std::declval<Type>()))>::value;
+ } else {
+ static_assert(!sizeof(Type));
+ }
+ }
+};
+
+template <typename Type>
+struct destructor_guard
+{
+ ZPP_BITS_INLINE constexpr ~destructor_guard()
+ {
+ access::destruct(object);
+ }
+
+ Type & object;
+};
+
+template <typename Type>
+destructor_guard(Type) -> destructor_guard<Type>;
+
+namespace traits
+{
+template <typename Type>
+struct is_unique_ptr : std::false_type
+{
+};
+
+template <typename Type>
+struct is_unique_ptr<std::unique_ptr<Type, std::default_delete<Type>>>
+ : std::true_type
+{
+};
+
+template <typename Type>
+struct is_shared_ptr : std::false_type
+{
+};
+
+template <typename Type>
+struct is_shared_ptr<std::shared_ptr<Type>> : std::true_type
+{
+};
+
+template <typename Variant>
+struct variant_impl;
+
+template <typename... Types, template <typename...> typename Variant>
+struct variant_impl<Variant<Types...>>
+{
+ using variant_type = Variant<Types...>;
+
+ template <std::size_t Index,
+ std::size_t CurrentIndex,
+ typename FirstType,
+ typename... OtherTypes>
+ constexpr static auto get_id()
+ {
+ if constexpr (Index == CurrentIndex) {
+ if constexpr (requires {
+ requires std::same_as<
+ serialization_id<
+ FirstType::serialize_id::value>,
+ typename FirstType::serialize_id>;
+ }) {
+ return FirstType::serialize_id::value;
+ } else if constexpr (
+ requires {
+ requires std::same_as<
+ serialization_id<decltype(serialize_id(
+ std::declval<FirstType>()))::value>,
+ decltype(serialize_id(std::declval<FirstType>()))>;
+ }) {
+ return decltype(serialize_id(
+ std::declval<FirstType>()))::value;
+ } else {
+ return std::byte{Index};
+ }
+ } else {
+ return get_id<Index, CurrentIndex + 1, OtherTypes...>();
+ }
+ }
+
+ template <std::size_t Index>
+ constexpr static auto id()
+ {
+ return get_id<Index, 0, Types...>();
+ }
+
+ template <std::size_t CurrentIndex = 0>
+ ZPP_BITS_INLINE constexpr static auto id(auto index)
+ {
+ if constexpr (CurrentIndex == (sizeof...(Types) - 1)) {
+ return id<CurrentIndex>();
+ } else {
+ if (index == CurrentIndex) {
+ return id<CurrentIndex>();
+ } else {
+ return id<CurrentIndex + 1>(index);
+ }
+ }
+ }
+
+ template <auto Id, std::size_t CurrentIndex = 0>
+ constexpr static std::size_t index()
+ {
+ static_assert(CurrentIndex < sizeof...(Types));
+
+ if constexpr (variant_impl::id<CurrentIndex>() == Id) {
+ return CurrentIndex;
+ } else {
+ return index<Id, CurrentIndex + 1>();
+ }
+ }
+
+ template <std::size_t CurrentIndex = 0>
+ ZPP_BITS_INLINE constexpr static std::size_t index(auto && id)
+ {
+ if constexpr (CurrentIndex == sizeof...(Types)) {
+ return std::numeric_limits<std::size_t>::max();
+ } else {
+ if (variant_impl::id<CurrentIndex>() == id) {
+ return CurrentIndex;
+ } else {
+ return index<CurrentIndex + 1>(id);
+ }
+ }
+ return std::numeric_limits<std::size_t>::max();
+ }
+
+ template <std::size_t... LeftIndices, std::size_t... RightIndices>
+ constexpr static auto unique_ids(std::index_sequence<LeftIndices...>,
+ std::index_sequence<RightIndices...>)
+ {
+ auto unique_among_rest = []<auto LeftIndex, auto LeftId>()
+ {
+ return (... && ((LeftIndex == RightIndices) ||
+ (LeftId != id<RightIndices>())));
+ };
+ return (... && unique_among_rest.template
+ operator()<LeftIndices, id<LeftIndices>()>());
+ }
+
+ template <std::size_t... LeftIndices, std::size_t... RightIndices>
+ constexpr static auto
+ same_id_types(std::index_sequence<LeftIndices...>,
+ std::index_sequence<RightIndices...>)
+ {
+ auto same_among_rest = []<auto LeftIndex, auto LeftId>()
+ {
+ return (... &&
+ (std::same_as<
+ std::remove_cv_t<decltype(LeftId)>,
+ std::remove_cv_t<decltype(id<RightIndices>())>>));
+ };
+ return (... && same_among_rest.template
+ operator()<LeftIndices, id<LeftIndices>()>());
+ }
+
+ template <typename Type, std::size_t... Indices>
+ constexpr static std::size_t index_by_type(std::index_sequence<Indices...>)
+ {
+ return ((std::same_as<
+ Type,
+ std::variant_alternative_t<Indices, variant_type>> *
+ Indices) +
+ ...);
+ }
+
+ template <typename Type>
+ constexpr static std::size_t index_by_type()
+ {
+ return index_by_type<Type>(
+ std::make_index_sequence<std::variant_size_v<variant_type>>{});
+ }
+
+ using id_type = decltype(id<0>());
+};
+
+template <typename Variant>
+struct variant_checker;
+
+template <typename... Types, template <typename...> typename Variant>
+struct variant_checker<Variant<Types...>>
+{
+ using type = variant_impl<Variant<Types...>>;
+ static_assert(
+ type::unique_ids(std::make_index_sequence<sizeof...(Types)>(),
+ std::make_index_sequence<sizeof...(Types)>()));
+ static_assert(
+ type::same_id_types(std::make_index_sequence<sizeof...(Types)>(),
+ std::make_index_sequence<sizeof...(Types)>()));
+};
+
+template <typename Variant>
+using variant = typename variant_checker<Variant>::type;
+
+template <typename Tuple>
+struct tuple;
+
+template <typename... Types, template <typename...> typename Tuple>
+struct tuple<Tuple<Types...>>
+{
+ template <std::size_t Index = 0>
+ ZPP_BITS_INLINE constexpr static auto visit(auto && tuple, auto && index, auto && visitor)
+ {
+ if constexpr (Index + 1 == sizeof...(Types)) {
+ return visitor(std::get<Index>(tuple));
+ } else {
+ if (Index == index) {
+ return visitor(std::get<Index>(tuple));
+ }
+ return visit<Index + 1>(tuple, index, visitor);
+ }
+ }
+};
+
+template <typename Type, typename Visitor = std::monostate>
+struct visitor
+{
+ using byte_type = std::byte;
+ using view_type = std::span<std::byte>;
+
+ static constexpr bool resizable = false;
+
+ constexpr auto operator()(auto && ... arguments) const
+ {
+ if constexpr (requires {
+ visitor(std::forward<decltype(arguments)>(
+ arguments)...);
+ }) {
+ return visitor(
+ std::forward<decltype(arguments)>(arguments)...);
+ } else {
+ return sizeof...(arguments);
+ }
+ }
+
+ template <typename...>
+ constexpr auto serialize_one(auto && ... arguments) const
+ {
+ return (*this)(std::forward<decltype(arguments)>(arguments)...);
+ }
+
+ template <typename...>
+ constexpr auto serialize_many(auto && ... arguments) const
+ {
+ return (*this)(std::forward<decltype(arguments)>(arguments)...);
+ }
+
+ constexpr static auto kind()
+ {
+ return kind::out;
+ }
+
+ std::span<std::byte> data();
+ std::span<std::byte> remaining_data();
+ std::span<std::byte> processed_data();
+ std::size_t position() const;
+ std::size_t & position();
+ errc enlarge_for(std::size_t);
+ void reset(std::size_t = 0);
+
+ [[no_unique_address]] Visitor visitor;
+};
+
+constexpr auto get_default_size_type()
+{
+ return default_size_type{};
+}
+
+constexpr auto get_default_size_type(auto option, auto... options)
+{
+ if constexpr (requires {
+ typename decltype(option)::default_size_type;
+ }) {
+ if constexpr (std::is_void_v<typename decltype(option)::default_size_type>) {
+ return std::monostate{};
+ } else {
+ return typename decltype(option)::default_size_type{};
+ }
+ } else {
+ return get_default_size_type(options...);
+ }
+}
+
+template <typename... Options>
+using default_size_type_t =
+ std::conditional_t<std::same_as<std::monostate,
+ decltype(get_default_size_type(
+ std::declval<Options>()...))>,
+ void,
+ decltype(get_default_size_type(
+ std::declval<Options>()...))>;
+
+template <typename Option, typename... Options>
+constexpr auto get_alloc_limit()
+{
+ if constexpr (requires {
+ std::remove_cvref_t<
+ Option>::alloc_limit_value;
+ }) {
+ return std::remove_cvref_t<Option>::alloc_limit_value;
+ } else if constexpr (sizeof...(Options) != 0) {
+ return get_alloc_limit<Options...>();
+ } else {
+ return std::numeric_limits<std::size_t>::max();
+ }
+}
+
+template <typename... Options>
+constexpr auto alloc_limit()
+{
+ if constexpr (sizeof...(Options) != 0) {
+ return get_alloc_limit<Options...>();
+ } else {
+ return std::numeric_limits<std::size_t>::max();
+ }
+}
+
+template <typename Option, typename... Options>
+constexpr auto get_enlarger()
+{
+ if constexpr (requires {
+ std::remove_cvref_t<
+ Option>::enlarger_value;
+ }) {
+ return std::remove_cvref_t<Option>::enlarger_value;
+ } else if constexpr (sizeof...(Options) != 0) {
+ return get_enlarger<Options...>();
+ } else {
+ return std::tuple{3, 2};
+ }
+}
+
+template <typename... Options>
+constexpr auto enlarger()
+{
+ if constexpr (sizeof...(Options) != 0) {
+ return get_enlarger<Options...>();
+ } else {
+ return std::tuple{3, 2};
+ }
+}
+
+template <typename Type>
+constexpr auto underlying_type_generic()
+{
+ if constexpr (std::is_enum_v<Type>) {
+ return std::underlying_type_t<Type>{};
+ } else {
+ return Type{};
+ }
+}
+
+template <typename Type>
+using underlying_type_t = decltype(underlying_type_generic<Type>());
+
+template <typename Id>
+struct id_serializable
+{
+ using serialize_id = Id;
+};
+
+constexpr auto unique(auto && ... values)
+{
+ auto unique_among_rest = [](auto && value, auto && ... values)
+ {
+ return (... && ((&value == &values) ||
+ (value != values)));
+ };
+ return (... && unique_among_rest(values, values...));
+}
+} // namespace traits
+
+namespace concepts
+{
+template <typename Type>
+concept byte_type = std::same_as<std::remove_cv_t<Type>, char> ||
+ std::same_as<std::remove_cv_t<Type>, unsigned char> ||
+ std::same_as<std::remove_cv_t<Type>, std::byte>;
+
+template <typename Type>
+concept byte_view = byte_type<typename std::remove_cvref_t<Type>::value_type> &&
+ requires(Type value)
+{
+ value.data();
+ value.size();
+};
+
+template <typename Type>
+concept has_serialize =
+ access::has_serialize<Type,
+ traits::visitor<std::remove_cvref_t<Type>>>();
+
+template <typename Type>
+concept has_explicit_serialize = access::has_explicit_serialize<
+ Type,
+ traits::visitor<std::remove_cvref_t<Type>>>();
+
+template <typename Type>
+concept variant = !has_serialize<Type> && requires (Type variant) {
+ variant.index();
+ std::get_if<0>(&variant);
+ std::variant_size_v<std::remove_cvref_t<Type>>;
+};
+
+template <typename Type>
+concept optional = !has_serialize<Type> && requires (Type optional) {
+ optional.value();
+ optional.has_value();
+ optional.operator bool();
+ optional.operator*();
+};
+
+template <typename Type>
+concept container =
+ !has_serialize<Type> && !optional<Type> && requires(Type container)
+{
+ typename std::remove_cvref_t<Type>::value_type;
+ container.size();
+ container.begin();
+ container.end();
+};
+
+template <typename Type>
+concept associative_container = container<Type> && requires(Type container)
+{
+ typename std::remove_cvref_t<Type>::key_type;
+};
+
+template <typename Type>
+concept tuple = !has_serialize<Type> && !container<Type> && requires(Type tuple)
+{
+ sizeof(std::tuple_size<std::remove_cvref_t<Type>>);
+}
+&&!requires(Type tuple)
+{
+ tuple.index();
+};
+
+template <typename Type>
+concept owning_pointer = !optional<Type> &&
+ (traits::is_unique_ptr<std::remove_cvref_t<Type>>::value ||
+ traits::is_shared_ptr<std::remove_cvref_t<Type>>::value);
+
+template <typename Type>
+concept bitset =
+ !has_serialize<Type> && requires(std::remove_cvref_t<Type> bitset)
+{
+ bitset.flip();
+ bitset.set();
+ bitset.test(0);
+ bitset.to_ullong();
+};
+
+template <typename Type>
+concept has_protocol = access::has_protocol<Type>();
+
+template <typename Type>
+concept by_protocol = has_protocol<Type> && !has_explicit_serialize<Type>;
+
+template <typename Type>
+concept basic_array = std::is_array_v<std::remove_cvref_t<Type>>;
+
+template <typename Type>
+concept unspecialized =
+ !container<Type> && !owning_pointer<Type> && !tuple<Type> &&
+ !variant<Type> && !optional<Type> && !bitset<Type> &&
+ !std::is_array_v<std::remove_cvref_t<Type>> && !by_protocol<Type>;
+
+template <typename Type>
+concept empty = requires
+{
+ std::integral_constant<std::size_t, sizeof(Type)>::value;
+ requires std::is_empty_v<std::remove_cvref_t<Type>>;
+};
+
+template <typename Type>
+concept byte_serializable = access::byte_serializable<Type>();
+
+template <typename Type>
+concept endian_independent_byte_serializable =
+ access::endian_independent_byte_serializable<Type>();
+
+template <typename Archive>
+concept endian_aware_archive = requires
+{
+ requires std::remove_cvref_t<Archive>::endian_aware;
+};
+
+template <typename Archive, typename Type>
+concept serialize_as_bytes = endian_independent_byte_serializable<Type> ||
+ (!endian_aware_archive<Archive> && byte_serializable<Type>);
+
+template <typename Type, typename Reference>
+concept type_references = requires
+{
+ requires container<Type>;
+ requires std::same_as<typename std::remove_cvref_t<Type>::value_type,
+ std::remove_cvref_t<Reference>>;
+}
+|| requires
+{
+ requires associative_container<Type>;
+ requires std::same_as<typename std::remove_cvref_t<Type>::key_type,
+ std::remove_cvref_t<Reference>>;
+}
+|| requires
+{
+ requires associative_container<Type>;
+ requires std::same_as<typename std::remove_cvref_t<Type>::mapped_type,
+ std::remove_cvref_t<Reference>>;
+}
+|| requires (Type && value)
+{
+ requires owning_pointer<Type>;
+ requires std::same_as<std::remove_cvref_t<decltype(*value)>,
+ std::remove_cvref_t<Reference>>;
+}
+|| requires (Type && value)
+{
+ requires optional<Type>;
+ requires std::same_as<std::remove_cvref_t<decltype(*value)>,
+ std::remove_cvref_t<Reference>>;
+};
+
+template <typename Type>
+concept self_referencing = access::self_referencing<Type>();
+
+template <typename Type>
+concept has_fixed_nonzero_size = requires
+{
+ requires std::integral_constant<std::size_t,
+ std::remove_cvref_t<Type>{}.size()>::value != 0;
+};
+
+template <typename Type>
+concept array =
+ basic_array<Type> ||
+ (container<Type> && has_fixed_nonzero_size<Type> && requires {
+ requires Type {
+ }
+ .size() * sizeof(typename Type::value_type) == sizeof(Type);
+ Type{}.data();
+ });
+
+} // namespace concepts
+
+template <typename CharType, std::size_t Size>
+struct string_literal : public std::array<CharType, Size + 1>
+{
+ using base = std::array<CharType, Size + 1>;
+ using value_type = typename base::value_type;
+ using pointer = typename base::pointer;
+ using const_pointer = typename base::const_pointer;
+ using iterator = typename base::iterator;
+ using const_iterator = typename base::const_iterator;
+ using reference = typename base::const_pointer;
+ using const_reference = typename base::const_pointer;
+ using size_type = default_size_type;
+
+ constexpr string_literal() = default;
+ constexpr string_literal(const CharType (&value)[Size + 1])
+ {
+ std::copy_n(std::begin(value), Size + 1, std::begin(*this));
+ }
+
+ constexpr auto operator<=>(const string_literal &) const = default;
+
+ constexpr default_size_type size() const
+ {
+ return Size;
+ }
+
+ constexpr bool empty() const
+ {
+ return !Size;
+ }
+
+ using base::begin;
+
+ constexpr auto end()
+ {
+ return base::end() - 1;
+ }
+
+ constexpr auto end() const
+ {
+ return base::end() - 1;
+ }
+
+ using base::data;
+ using base::operator[];
+ using base::at;
+
+private:
+ using base::cbegin;
+ using base::cend;
+ using base::rbegin;
+ using base::rend;
+};
+
+template <typename CharType, std::size_t Size>
+string_literal(const CharType (&value)[Size])
+ -> string_literal<CharType, Size - 1>;
+
+template <typename Item>
+class bytes
+{
+public:
+ using value_type = Item;
+
+ constexpr explicit bytes(std::span<Item> items) :
+ m_items(items.data()), m_size(items.size())
+ {
+ }
+
+ constexpr explicit bytes(std::span<Item> items, auto size) :
+ m_items(items.data()), m_size(std::size_t(size))
+ {
+ }
+
+ constexpr auto data() const
+ {
+ return m_items;
+ }
+
+ constexpr std::size_t size_in_bytes() const
+ {
+ return m_size * sizeof(Item);
+ }
+
+ constexpr std::size_t count() const
+ {
+ return m_size;
+ }
+
+private:
+ static_assert(std::is_trivially_copyable_v<Item>);
+
+ Item * m_items;
+ std::size_t m_size;
+};
+
+template <typename Item>
+bytes(std::span<Item>) -> bytes<Item>;
+
+template <typename Item>
+bytes(std::span<Item>, std::size_t) -> bytes<Item>;
+
+template <typename Item, std::size_t Count>
+bytes(Item(&)[Count]) -> bytes<Item>;
+
+template <concepts::container Container>
+bytes(Container && container)
+ -> bytes<std::remove_reference_t<decltype(container[0])>>;
+
+template <concepts::container Container>
+bytes(Container && container, std::size_t)
+ -> bytes<std::remove_reference_t<decltype(container[0])>>;
+
+constexpr auto as_bytes(auto && object)
+{
+ return bytes(std::span{&object, 1});
+}
+
+template <typename Option>
+struct option
+{
+ using zpp_bits_option = void;
+ constexpr auto operator()(auto && archive)
+ {
+ if constexpr (requires {
+ archive.option(static_cast<Option &>(*this));
+ }) {
+ archive.option(static_cast<Option &>(*this));
+ }
+ }
+};
+
+inline namespace options
+{
+struct append : option<append>
+{
+};
+
+struct reserve : option<reserve>
+{
+ constexpr explicit reserve(std::size_t size) : size(size)
+ {
+ }
+ std::size_t size{};
+};
+
+struct resize : option<resize>
+{
+ constexpr explicit resize(std::size_t size) : size(size)
+ {
+ }
+ std::size_t size{};
+};
+
+template <std::size_t Size>
+struct alloc_limit : option<alloc_limit<Size>>
+{
+ constexpr static auto alloc_limit_value = Size;
+};
+
+template <std::size_t Multiplier, std::size_t Divisor = 1>
+struct enlarger : option<enlarger<Multiplier, Divisor>>
+{
+ constexpr static auto enlarger_value =
+ std::tuple{Multiplier, Divisor};
+};
+
+using exact_enlarger = enlarger<1, 1>;
+
+namespace endian
+{
+struct big : option<big>
+{
+ constexpr static auto value = std::endian::big;
+};
+
+struct little : option<little>
+{
+ constexpr static auto value = std::endian::little;
+};
+
+using network = big;
+
+using native = std::
+ conditional_t<std::endian::native == std::endian::little, little, big>;
+
+using swapped = std::
+ conditional_t<std::endian::native == std::endian::little, big, little>;
+} // namespace endian
+
+struct no_fit_size : option<no_fit_size>
+{
+};
+
+struct no_enlarge_overflow : option<no_enlarge_overflow>
+{
+};
+
+struct enlarge_overflow : option<enlarge_overflow>
+{
+};
+
+struct no_size : option<no_size>
+{
+ using default_size_type = void;
+};
+
+struct size1b : option<size1b>
+{
+ using default_size_type = unsigned char;
+};
+
+struct size2b : option<size2b>
+{
+ using default_size_type = std::uint16_t;
+};
+
+struct size4b : option<size4b>
+{
+ using default_size_type = std::uint32_t;
+};
+
+struct size8b : option<size8b>
+{
+ using default_size_type = std::uint64_t;
+};
+
+struct size_native : option<size_native>
+{
+ using default_size_type = std::size_t;
+};
+} // namespace options
+
+template <typename Type>
+constexpr auto access::number_of_members()
+{
+ using type = std::remove_cvref_t<Type>;
+ if constexpr (std::is_array_v<type>) {
+ return std::extent_v<type>;
+ } else if constexpr (!std::is_class_v<type>) {
+ return 0;
+ } else if constexpr (concepts::container<type> &&
+ concepts::has_fixed_nonzero_size<type>) {
+ return type{}.size();
+ } else if constexpr (concepts::tuple<type>) {
+ return std::tuple_size_v<type>;
+ } else if constexpr (requires {
+ requires std::same_as<
+ typename type::serialize,
+ members<type::serialize::value>>;
+ requires type::serialize::value !=
+ std::numeric_limits<
+ std::size_t>::max();
+ }) {
+ return type::serialize::value;
+ } else if constexpr (requires(Type && item) {
+ requires std::same_as<
+ decltype(try_serialize(item)),
+ members<decltype(try_serialize(
+ item))::value>>;
+ requires decltype(try_serialize(
+ item))::value !=
+ std::numeric_limits<
+ std::size_t>::max();
+ }) {
+ return decltype(serialize(std::declval<type>()))::value;
+ } else if constexpr (requires {
+ requires std::same_as<
+ typename type::serialize,
+ protocol<type::serialize::value,
+ type::serialize::members>>;
+ requires type::serialize::members !=
+ std::numeric_limits<
+ std::size_t>::max();
+ }) {
+ return type::serialize::members;
+ } else if constexpr (requires(Type && item) {
+ requires std::same_as<
+ decltype(try_serialize(item)),
+ protocol<decltype(try_serialize(item))::value,
+ decltype(try_serialize(
+ item))::members>>;
+ requires decltype(try_serialize(
+ item))::members !=
+ std::numeric_limits<
+ std::size_t>::max();
+ }) {
+ return decltype(serialize(std::declval<type>()))::members;
+#if ZPP_BITS_AUTODETECT_MEMBERS_MODE == 0
+ } else if constexpr (std::is_aggregate_v<type>) {
+ // clang-format off
+ if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, /*.................................................................................................................*/ any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 50; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 49; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 48; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 47; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 46; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 45; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 44; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 43; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 42; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 41; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 40; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 39; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 38; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 37; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 36; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 35; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 34; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 33; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 32; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 31; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 30; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 29; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 28; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 27; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 26; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 25; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 24; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 23; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 22; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 21; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 20; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 19; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 18; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 17; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 16; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 15; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 14; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 13; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 12; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 11; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 10; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 9; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 8; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 7; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}, any{}}; }) { return 6; } else if constexpr (requires { type{any{}, any{}, any{}, any{}, any{}}; }) { return 5; } else if constexpr (requires { type{any{}, any{}, any{}, any{}}; }) { return 4; } else if constexpr (requires { type{any{}, any{}, any{}}; }) { return 3; } else if constexpr (requires { type{any{}, any{}}; }) { return 2; } else if constexpr (requires { type{any{}}; }) { return 1;
+ // Returns the number of members
+ // clang-format on
+ } else if constexpr (concepts::empty<type> && requires {
+ typename std::void_t<decltype(type{})>;
+ }) {
+ return 0;
+ } else {
+ return -1;
+ }
+#elif ZPP_BITS_AUTODETECT_MEMBERS_MODE > 0
+#if ZPP_BITS_AUTODETECT_MEMBERS_MODE == 1
+ // clang-format off
+ } else if constexpr (requires { [](Type && object) { auto && [a1] = object; }; }) { return 1; } else if constexpr (requires { [](Type && object) { auto && [a1, a2] = object; }; }) { return 2; /*.................................................................................................................*/ } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3] = object; }; }) { return 3; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4] = object; }; }) { return 4; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5] = object; }; }) { return 5; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6] = object; }; }) { return 6; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7] = object; }; }) { return 7; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8] = object; }; }) { return 8; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9] = object; }; }) { return 9; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10] = object; }; }) { return 10; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11] = object; }; }) { return 11; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12] = object; }; }) { return 12; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13] = object; }; }) { return 13; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14] = object; }; }) { return 14; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15] = object; }; }) { return 15; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16] = object; }; }) { return 16; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17] = object; }; }) { return 17; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18] = object; }; }) { return 18; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19] = object; }; }) { return 19; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20] = object; }; }) { return 20; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21] = object; }; }) { return 21; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22] = object; }; }) { return 22; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23] = object; }; }) { return 23; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24] = object; }; }) { return 24; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25] = object; }; }) { return 25; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26] = object; }; }) { return 26; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27] = object; }; }) { return 27; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28] = object; }; }) { return 28; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29] = object; }; }) { return 29; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30] = object; }; }) { return 30; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31] = object; }; }) { return 31; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32] = object; }; }) { return 32; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33] = object; }; }) { return 33; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34] = object; }; }) { return 34; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35] = object; }; }) { return 35; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36] = object; }; }) { return 36; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37] = object; }; }) { return 37; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38] = object; }; }) { return 38; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39] = object; }; }) { return 39; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40] = object; }; }) { return 40; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41] = object; }; }) { return 41; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42] = object; }; }) { return 42; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43] = object; }; }) { return 43; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44] = object; }; }) { return 44; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45] = object; }; }) { return 45; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46] = object; }; }) { return 46; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47] = object; }; }) { return 47; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48] = object; }; }) { return 48; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49] = object; }; }) { return 49; } else if constexpr (requires { [](Type && object) { auto && [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50] = object; }; }) { return 50;
+ // Returns the number of members
+ // clang-format on
+#else // ZPP_BITS_AUTODETECT_MEMBERS_MODE == 1
+#error "Invalid value for ZPP_BITS_AUTODETECT_MEMBERS_MODE"
+#endif
+#endif
+ } else {
+ return -1;
+ }
+}
+
+template <typename Type>
+struct access::byte_serializable_visitor
+{
+ template <typename... Types>
+ constexpr auto operator()() {
+ using type = std::remove_cvref_t<Type>;
+
+ if constexpr (concepts::empty<type>) {
+ return std::false_type{};
+ } else if constexpr ((... || has_explicit_serialize<
+ Types,
+ traits::visitor<Types>>())) {
+ return std::false_type{};
+ } else if constexpr ((... || !byte_serializable<Types>())) {
+ return std::false_type{};
+ } else if constexpr ((0 + ... + sizeof(Types)) != sizeof(type)) {
+ return std::false_type{};
+ } else if constexpr ((... || concepts::empty<Types>)) {
+ return std::false_type{};
+ } else {
+ return std::true_type{};
+ }
+ }
+};
+
+template <typename Type>
+constexpr auto access::byte_serializable()
+{
+ constexpr auto members_count = number_of_members<Type>();
+ using type = std::remove_cvref_t<Type>;
+
+ if constexpr (members_count < 0) {
+ return false;
+ } else if constexpr (!std::is_trivially_copyable_v<type>) {
+ return false;
+ } else if constexpr (has_explicit_serialize<type,
+ traits::visitor<type>>()) {
+ return false;
+ } else if constexpr (
+ !requires {
+ requires std::integral_constant<
+ int,
+ (std::bit_cast<std::remove_all_extents_t<type>>(
+ std::array<
+ std::byte,
+ sizeof(std::remove_all_extents_t<type>)>()),
+ 0)>::value == 0;
+ }) {
+ return false;
+ } else if constexpr (concepts::array<type>) {
+ return byte_serializable<
+ std::remove_cvref_t<decltype(std::declval<type>()[0])>>();
+ } else if constexpr (members_count > 0) {
+ return visit_members_types<type>(
+ byte_serializable_visitor<type>{})();
+ } else {
+ return true;
+ }
+}
+
+template <typename Type>
+struct access::endian_independent_byte_serializable_visitor
+{
+ template <typename... Types>
+ constexpr auto operator()() {
+ using type = std::remove_cvref_t<Type>;
+
+ if constexpr (concepts::empty<type>) {
+ return std::false_type{};
+ } else if constexpr ((... || has_explicit_serialize<
+ Types,
+ traits::visitor<Types>>())) {
+ return std::false_type{};
+ } else if constexpr ((... || !endian_independent_byte_serializable<Types>())) {
+ return std::false_type{};
+ } else if constexpr ((0 + ... + sizeof(Types)) != sizeof(type)) {
+ return std::false_type{};
+ } else if constexpr ((... || concepts::empty<Types>)) {
+ return std::false_type{};
+ } else if constexpr (!concepts::byte_type<type>) {
+ return std::false_type{};
+ } else {
+ return std::true_type{};
+ }
+ }
+};
+
+template <typename Type>
+constexpr auto access::endian_independent_byte_serializable()
+{
+ constexpr auto members_count = number_of_members<Type>();
+ using type = std::remove_cvref_t<Type>;
+
+ if constexpr (members_count < 0) {
+ return false;
+ } else if constexpr (!std::is_trivially_copyable_v<type>) {
+ return false;
+ } else if constexpr (has_explicit_serialize<type,
+ traits::visitor<type>>()) {
+ return false;
+ } else if constexpr (
+ !requires {
+ requires std::integral_constant<
+ int,
+ (std::bit_cast<std::remove_all_extents_t<type>>(
+ std::array<
+ std::byte,
+ sizeof(std::remove_all_extents_t<type>)>()),
+ 0)>::value == 0;
+ }) {
+ return false;
+ } else if constexpr (concepts::array<type>) {
+ return endian_independent_byte_serializable<
+ std::remove_cvref_t<decltype(std::declval<type>()[0])>>();
+ } else if constexpr (members_count > 0) {
+ return visit_members_types<type>(
+ endian_independent_byte_serializable_visitor<type>{})();
+ } else {
+ return concepts::byte_type<type>;
+ }
+}
+
+template <typename Type, typename Self, typename... Visited>
+struct access::self_referencing_visitor
+{
+ template <typename... Types>
+ constexpr auto operator()() {
+ using type = std::remove_cvref_t<Type>;
+ using self = std::remove_cvref_t<Self>;
+
+ if constexpr (concepts::empty<type>) {
+ return std::false_type{};
+ } else if constexpr ((... || concepts::type_references<
+ std::remove_cvref_t<Types>,
+ self>)) {
+ return std::true_type{};
+ } else if constexpr ((sizeof...(Visited) != 0) &&
+ (... || std::same_as<type, Visited>)) {
+ return std::false_type{};
+ } else if constexpr ((... ||
+ self_referencing<std::remove_cvref_t<Types>,
+ self,
+ type,
+ Visited...>())) {
+ return std::true_type{};
+ } else {
+ return std::false_type{};
+ }
+ }
+};
+
+template <typename Type, typename Self/* = Type*/, typename... Visited>
+constexpr auto access::self_referencing()
+{
+ constexpr auto members_count = number_of_members<Type>();
+ using type = std::remove_cvref_t<Type>;
+ using self = std::remove_cvref_t<Self>;
+
+ if constexpr (members_count < 0) {
+ return false;
+ } else if constexpr (has_explicit_serialize<type,
+ traits::visitor<type>>()) {
+ return false;
+ } else if constexpr (members_count == 0) {
+ return false;
+ } else if constexpr (concepts::array<type>) {
+ return self_referencing<
+ std::remove_cvref_t<decltype(std::declval<type>()[0])>,
+ self,
+ Visited...>();
+ } else {
+ return visit_members_types<type>(
+ self_referencing_visitor<type, self, Visited...>{})();
+ }
+}
+
+template <typename Type>
+constexpr auto number_of_members()
+{
+ return access::number_of_members<Type>();
+}
+
+ZPP_BITS_INLINE constexpr decltype(auto) visit_members(auto && object,
+ auto && visitor)
+{
+ return access::visit_members(object, visitor);
+}
+
+template <typename Type>
+constexpr decltype(auto) visit_members_types(auto && visitor)
+{
+ return access::visit_members_types<Type>(visitor);
+}
+
+template <typename Type>
+struct optional_ptr : std::unique_ptr<Type>
+{
+ using base = std::unique_ptr<Type>;
+ using base::base;
+ using base::operator=;
+
+ constexpr optional_ptr(base && other) noexcept :
+ base(std::move(other))
+ {
+ }
+};
+
+template <typename Type, typename...>
+optional_ptr(Type *) -> optional_ptr<Type>;
+
+template <typename Archive, typename Type>
+ZPP_BITS_INLINE constexpr static auto serialize(
+ Archive & archive,
+ const optional_ptr<Type> & self) requires(Archive::kind() == kind::out)
+{
+ if (!self) [[unlikely]] {
+ return archive(std::byte(false));
+ } else {
+ return archive(std::byte(true), *self);
+ }
+}
+
+template <typename Archive, typename Type>
+ZPP_BITS_INLINE constexpr static auto
+serialize(Archive & archive,
+ optional_ptr<Type> & self) requires(Archive::kind() == kind::in)
+{
+ std::byte has_value{};
+ if (auto result = archive(has_value); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+
+ if (!bool(has_value)) [[unlikely]] {
+ self = {};
+ return errc{};
+ }
+
+ if (auto result =
+ archive(static_cast<std::unique_ptr<Type> &>(self));
+ failure(result)) [[unlikely]] {
+ return result;
+ }
+
+ return errc{};
+}
+
+template <typename Type, typename SizeType>
+struct sized_item : public Type
+{
+ using Type::Type;
+ using Type::operator=;
+
+ constexpr sized_item(Type && other) noexcept(
+ std::is_nothrow_move_constructible_v<Type>) :
+ Type(std::move(other))
+ {
+ }
+
+ constexpr sized_item(const Type & other) :
+ Type(other)
+ {
+ }
+
+ ZPP_BITS_INLINE constexpr static auto serialize(auto & archive,
+ auto & self)
+ {
+ if constexpr (std::remove_cvref_t<decltype(archive)>::kind() == kind::out) {
+ return archive.template serialize_one<SizeType>(
+ static_cast<const Type &>(self));
+ } else {
+ return archive.template serialize_one<SizeType>(
+ static_cast<Type &>(self));
+ }
+ }
+};
+
+template <typename Type, typename SizeType>
+auto serialize(const sized_item<Type, SizeType> &)
+ -> members<number_of_members<Type>()>;
+
+template <typename Type, typename SizeType>
+using sized_t = sized_item<Type, SizeType>;
+
+template <typename Type>
+using unsized_t = sized_t<Type, void>;
+
+template <typename Type, typename SizeType>
+struct sized_item_ref
+{
+ constexpr explicit sized_item_ref(Type && value) :
+ value(std::forward<Type>(value))
+ {
+ }
+
+ ZPP_BITS_INLINE constexpr static auto serialize(auto & serializer,
+ auto & self)
+ {
+ return serializer.template serialize_one<SizeType>(self.value);
+ }
+
+ Type && value;
+};
+
+template <typename SizeType, typename Type>
+constexpr auto sized(Type && value)
+{
+ return sized_item_ref<Type &, SizeType>(value);
+}
+
+template <typename Type>
+constexpr auto unsized(Type && value)
+{
+ return sized_item_ref<Type &, void>(value);
+}
+
+enum class varint_encoding
+{
+ normal,
+ zig_zag,
+};
+
+template <typename Type, varint_encoding Encoding = varint_encoding::normal>
+struct varint
+{
+ varint() = default;
+
+ using value_type = Type;
+ static constexpr auto encoding = Encoding;
+
+ constexpr varint(Type value) : value(value)
+ {
+ }
+
+ constexpr operator Type &() &
+ {
+ return value;
+ }
+
+ constexpr operator Type() const
+ {
+ return value;
+ }
+
+ constexpr decltype(auto) operator*() &
+ {
+ return (value);
+ }
+
+ constexpr auto operator*() const &
+ {
+ return value;
+ }
+
+ Type value{};
+};
+
+namespace concepts
+{
+
+template <typename Type>
+concept varint = requires
+{
+ requires std::same_as<
+ Type,
+ zpp::bits::varint<typename Type::value_type, Type::encoding>>;
+};
+
+} // namespace concepts
+
+template <typename Type>
+constexpr auto varint_max_size = sizeof(Type) * CHAR_BIT / (CHAR_BIT - 1) +
+ 1;
+
+template <varint_encoding Encoding = varint_encoding::normal>
+ZPP_BITS_INLINE constexpr auto varint_size(auto value)
+{
+ if constexpr (Encoding == varint_encoding::zig_zag) {
+ return varint_size(std::make_unsigned_t<decltype(value)>((value << 1) ^
+ (value >> (sizeof(value) * CHAR_BIT - 1))));
+ } else {
+ return ((sizeof(value) * CHAR_BIT) -
+ std::countl_zero(
+ std::make_unsigned_t<decltype(value)>(value | 0x1)) +
+ (CHAR_BIT - 2)) /
+ (CHAR_BIT - 1);
+ }
+}
+
+template <typename Archive, typename Type, varint_encoding Encoding>
+ZPP_BITS_INLINE constexpr auto serialize(
+ Archive & archive,
+ varint<Type, Encoding> self) requires(Archive::kind() == kind::out)
+{
+ auto orig_value = std::conditional_t<std::is_enum_v<Type>,
+ traits::underlying_type_t<Type>,
+ Type>(self.value);
+ auto value = std::make_unsigned_t<Type>(orig_value);
+ if constexpr (varint_encoding::zig_zag == Encoding) {
+ value =
+ (value << 1) ^ (orig_value >> (sizeof(Type) * CHAR_BIT - 1));
+ }
+
+ constexpr auto max_size = varint_max_size<Type>;
+ if constexpr (Archive::resizable) {
+ if (auto result = archive.enlarge_for(max_size); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+ }
+
+ auto data = archive.remaining_data();
+ if constexpr (!Archive::resizable) {
+ auto data_size = data.size();
+ if (data_size < max_size) [[unlikely]] {
+ if (data_size < varint_size(value)) [[unlikely]] {
+ return errc{std::errc::result_out_of_range};
+ }
+ }
+ }
+
+ using byte_type = std::remove_cvref_t<decltype(data[0])>;
+ std::size_t position = {};
+ while (value >= 0x80) {
+ data[position++] = byte_type((value & 0x7f) | 0x80);
+ value >>= (CHAR_BIT - 1);
+ }
+ data[position++] = byte_type(value);
+
+ archive.position() += position;
+ return errc{};
+}
+
+constexpr auto decode_varint(auto data, auto & value, auto & position)
+{
+ using value_type = std::remove_cvref_t<decltype(value)>;
+ if (data.size() < varint_max_size<value_type>) [[unlikely]] {
+ std::size_t shift = 0;
+ for (auto & byte_value : data) {
+ auto next_byte = value_type(byte_value);
+ value |= (next_byte & 0x7f) << shift;
+ if (next_byte >= 0x80) [[unlikely]] {
+ shift += CHAR_BIT - 1;
+ continue;
+ }
+ position += 1 + std::distance(data.data(), &byte_value);
+ return errc{};
+ }
+ return errc{std::errc::result_out_of_range};
+ } else {
+ auto p = data.data();
+ do {
+ // clang-format off
+ value_type next_byte;
+ next_byte = value_type(*p++); value |= ((next_byte & 0x7f) << ((CHAR_BIT - 1) * 0)); if (next_byte < 0x80) [[likely]] { break; }
+ next_byte = value_type(*p++); value |= ((next_byte & 0x7f) << ((CHAR_BIT - 1) * 1)); if (next_byte < 0x80) [[likely]] { break; }
+ if constexpr (varint_max_size<value_type> > 2) {
+ next_byte = value_type(*p++); value |= ((next_byte & 0x7f) << ((CHAR_BIT - 1) * 2)); if (next_byte < 0x80) [[likely]] { break; }
+ if constexpr (varint_max_size<value_type> > 3) {
+ next_byte = value_type(*p++); value |= ((next_byte & 0x7f) << ((CHAR_BIT - 1) * 3)); if (next_byte < 0x80) [[likely]] { break; }
+ next_byte = value_type(*p++); value |= ((next_byte & 0x7f) << ((CHAR_BIT - 1) * 4)); if (next_byte < 0x80) [[likely]] { break; }
+ if constexpr (varint_max_size<value_type> > 5) {
+ next_byte = value_type(*p++); value |= ((next_byte & 0x7f) << ((CHAR_BIT - 1) * 5)); if (next_byte < 0x80) [[likely]] { break; }
+ next_byte = value_type(*p++); value |= ((next_byte & 0x7f) << ((CHAR_BIT - 1) * 6)); if (next_byte < 0x80) [[likely]] { break; }
+ next_byte = value_type(*p++); value |= ((next_byte & 0x7f) << ((CHAR_BIT - 1) * 7)); if (next_byte < 0x80) [[likely]] { break; }
+ next_byte = value_type(*p++); value |= ((next_byte & 0x7f) << ((CHAR_BIT - 1) * 8)); if (next_byte < 0x80) [[likely]] { break; }
+ next_byte = value_type(*p++); value |= ((next_byte & 0x01) << ((CHAR_BIT - 1) * 9)); if (next_byte < 0x80) [[likely]] { break; } }}}
+ return errc{std::errc::value_too_large};
+ // clang-format on
+ } while (false);
+ position += std::distance(data.data(), p);
+ return errc{};
+ }
+}
+
+template <typename Archive, typename Type, varint_encoding Encoding>
+ZPP_BITS_INLINE constexpr auto serialize(
+ Archive & archive,
+ varint<Type, Encoding> & self) requires(Archive::kind() == kind::in)
+{
+ using value_type = std::conditional_t<
+ std::is_enum_v<Type>,
+ std::make_unsigned_t<traits::underlying_type_t<Type>>,
+ std::make_unsigned_t<Type>>;
+ value_type value{};
+ auto data = archive.remaining_data();
+
+ if constexpr (!ZPP_BITS_INLINE_DECODE_VARINT) {
+ auto & position = archive.position();
+ if (!data.empty() && !(value_type(data[0]) & 0x80)) [[likely]] {
+ value = value_type(data[0]);
+ position += 1;
+ } else if (auto result =
+ std::is_constant_evaluated()
+ ? decode_varint(data, value, position)
+ : decode_varint(
+ std::span{
+ reinterpret_cast<const std::byte *>(
+ data.data()),
+ data.size()},
+ value,
+ position);
+ failure(result)) [[unlikely]] {
+ return result;
+ }
+
+ if constexpr (varint_encoding::zig_zag == Encoding) {
+ self.value =
+ decltype(self.value)((value >> 1) ^ -(value & 0x1));
+ } else {
+ self.value = decltype(self.value)(value);
+ }
+ return errc{};
+ } else if (data.size() < varint_max_size<value_type>) [[unlikely]] {
+ std::size_t shift = 0;
+ for (auto & byte_value : data) {
+ auto next_byte = decltype(value)(byte_value);
+ value |= (next_byte & 0x7f) << shift;
+ if (next_byte >= 0x80) [[unlikely]] {
+ shift += CHAR_BIT - 1;
+ continue;
+ }
+ if constexpr (varint_encoding::zig_zag == Encoding) {
+ self.value =
+ decltype(self.value)((value >> 1) ^ -(value & 0x1));
+ } else {
+ self.value = decltype(self.value)(value);
+ }
+ archive.position() +=
+ 1 + std::distance(data.data(), &byte_value);
+ return errc{};
+ }
+ return errc{std::errc::result_out_of_range};
+ } else {
+ auto p = data.data();
+ do {
+ // clang-format off
+ value_type next_byte;
+ next_byte = value_type(*p++); value |= ((next_byte & 0x7f) << ((CHAR_BIT - 1) * 0)); if (next_byte < 0x80) [[likely]] { break; }
+ next_byte = value_type(*p++); value |= ((next_byte & 0x7f) << ((CHAR_BIT - 1) * 1)); if (next_byte < 0x80) [[likely]] { break; }
+ if constexpr (varint_max_size<value_type> > 2) {
+ next_byte = value_type(*p++); value |= ((next_byte & 0x7f) << ((CHAR_BIT - 1) * 2)); if (next_byte < 0x80) [[likely]] { break; }
+ if constexpr (varint_max_size<value_type> > 3) {
+ next_byte = value_type(*p++); value |= ((next_byte & 0x7f) << ((CHAR_BIT - 1) * 3)); if (next_byte < 0x80) [[likely]] { break; }
+ next_byte = value_type(*p++); value |= ((next_byte & 0x7f) << ((CHAR_BIT - 1) * 4)); if (next_byte < 0x80) [[likely]] { break; }
+ if constexpr (varint_max_size<value_type> > 5) {
+ next_byte = value_type(*p++); value |= ((next_byte & 0x7f) << ((CHAR_BIT - 1) * 5)); if (next_byte < 0x80) [[likely]] { break; }
+ next_byte = value_type(*p++); value |= ((next_byte & 0x7f) << ((CHAR_BIT - 1) * 6)); if (next_byte < 0x80) [[likely]] { break; }
+ next_byte = value_type(*p++); value |= ((next_byte & 0x7f) << ((CHAR_BIT - 1) * 7)); if (next_byte < 0x80) [[likely]] { break; }
+ next_byte = value_type(*p++); value |= ((next_byte & 0x7f) << ((CHAR_BIT - 1) * 8)); if (next_byte < 0x80) [[likely]] { break; }
+ next_byte = value_type(*p++); value |= ((next_byte & 0x01) << ((CHAR_BIT - 1) * 9)); if (next_byte < 0x80) [[likely]] { break; } }}}
+ return errc{std::errc::value_too_large};
+ // clang-format on
+ } while (false);
+ if constexpr (varint_encoding::zig_zag == Encoding) {
+ self.value =
+ decltype(self.value)((value >> 1) ^ -(value & 0x1));
+ } else {
+ self.value = decltype(self.value)(value);
+ }
+ archive.position() += std::distance(data.data(), p);
+ return errc{};
+ }
+}
+
+template <typename Archive, typename Type, varint_encoding Encoding>
+constexpr auto
+serialize(Archive & archive,
+ varint<Type, Encoding> && self) requires(Archive::kind() ==
+ kind::in) = delete;
+
+using vint32_t = varint<std::int32_t>;
+using vint64_t = varint<std::int64_t>;
+
+using vuint32_t = varint<std::uint32_t>;
+using vuint64_t = varint<std::uint64_t>;
+
+using vsint32_t = varint<std::int32_t, varint_encoding::zig_zag>;
+using vsint64_t = varint<std::int64_t, varint_encoding::zig_zag>;
+
+using vsize_t = varint<std::size_t>;
+
+inline namespace options
+{
+struct size_varint : option<size_varint>
+{
+ using default_size_type = vsize_t;
+};
+} // namespace options
+
+template <concepts::byte_view ByteView, typename... Options>
+class basic_out
+{
+public:
+ template <concepts::byte_view, typename...>
+ friend class basic_out;
+
+ template <typename>
+ friend struct option;
+
+ template <typename... Types>
+ using template_type = basic_out<Types...>;
+
+ friend access;
+
+ template <typename, typename>
+ friend struct sized_item;
+
+ template <typename, typename>
+ friend struct sized_item_ref;
+
+ template <typename, concepts::variant>
+ friend struct known_id_variant;
+
+ template <typename, concepts::variant>
+ friend struct known_dynamic_id_variant;
+
+ using byte_type = typename ByteView::value_type;
+
+ static constexpr auto endian_aware =
+ (... ||
+ std::same_as<std::remove_cvref_t<Options>, endian::swapped>);
+
+ using default_size_type = traits::default_size_type_t<Options...>;
+
+ constexpr static auto allocation_limit = traits::alloc_limit<Options...>();
+
+ constexpr static auto enlarger = traits::enlarger<Options...>();
+
+ constexpr static auto no_enlarge_overflow =
+ (... ||
+ std::same_as<std::remove_cvref_t<Options>, options::no_enlarge_overflow>);
+
+ constexpr static bool resizable = requires(ByteView view)
+ {
+ view.resize(1);
+ };
+
+ using view_type =
+ std::conditional_t<resizable,
+ ByteView &,
+ std::remove_cvref_t<decltype(
+ std::span{std::declval<ByteView &>()})>>;
+
+ constexpr explicit basic_out(ByteView && view, Options && ... options) : m_data(view)
+ {
+ static_assert(!resizable);
+ (options(*this), ...);
+ }
+
+ constexpr explicit basic_out(ByteView & view, Options && ... options) : m_data(view)
+ {
+ (options(*this), ...);
+ }
+
+ ZPP_BITS_INLINE constexpr auto operator()(auto &&... items)
+ {
+ return serialize_many(items...);
+ }
+
+ constexpr decltype(auto) data()
+ {
+ return m_data;
+ }
+
+ constexpr std::size_t position() const
+ {
+ return m_position;
+ }
+
+ constexpr std::size_t & position()
+ {
+ return m_position;
+ }
+
+ constexpr auto remaining_data()
+ {
+ return std::span<byte_type>{m_data.data() + m_position,
+ m_data.size() - m_position};
+ }
+
+ constexpr auto processed_data()
+ {
+ return std::span<byte_type>{m_data.data(), m_position};
+ }
+
+ constexpr void reset(std::size_t position = 0)
+ {
+ m_position = position;
+ }
+
+ constexpr static auto kind()
+ {
+ return kind::out;
+ }
+
+ ZPP_BITS_INLINE constexpr errc enlarge_for(auto additional_size)
+ {
+ auto size = m_data.size();
+ if (additional_size > size - m_position) [[unlikely]] {
+ constexpr auto multiplier = std::get<0>(enlarger);
+ constexpr auto divisor = std::get<1>(enlarger);
+ static_assert(multiplier != 0 && divisor != 0);
+
+ auto required_size = size + additional_size;
+ if constexpr (!no_enlarge_overflow) {
+ if (required_size < size) [[unlikely]] {
+ return std::errc::no_buffer_space;
+ }
+ }
+
+ auto new_size = required_size;
+ if constexpr (multiplier != 1) {
+ new_size *= multiplier;
+ if constexpr (!no_enlarge_overflow) {
+ if (new_size / multiplier != required_size) [[unlikely]] {
+ return std::errc::no_buffer_space;
+ }
+ }
+ }
+ if constexpr (divisor != 1) {
+ new_size /= divisor;
+ }
+ if constexpr (allocation_limit !=
+ std::numeric_limits<std::size_t>::max()) {
+ if (new_size > allocation_limit) [[unlikely]] {
+ return std::errc::no_buffer_space;
+ }
+ }
+ m_data.resize(new_size);
+ }
+ return {};
+ }
+
+protected:
+ ZPP_BITS_INLINE constexpr errc serialize_many(auto && first_item,
+ auto &&... items)
+ {
+ if (auto result = serialize_one(first_item); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+
+ return serialize_many(items...);
+ }
+
+ ZPP_BITS_INLINE constexpr errc serialize_many()
+ {
+ return {};
+ }
+
+ constexpr auto option(append)
+ {
+ static_assert(resizable);
+ m_position = m_data.size();
+ }
+
+ constexpr auto option(reserve size)
+ {
+ static_assert(resizable);
+ m_data.reserve(size.size);
+ }
+
+ constexpr auto option(resize size)
+ {
+ static_assert(resizable);
+ m_data.resize(size.size);
+ if (m_position > size.size) {
+ m_position = size.size;
+ }
+ }
+
+ ZPP_BITS_INLINE constexpr errc serialize_one(concepts::unspecialized auto && item)
+ {
+ using type = std::remove_cvref_t<decltype(item)>;
+ static_assert(!std::is_pointer_v<type>);
+
+ if constexpr (requires { type::serialize(*this, item); }) {
+ return type::serialize(*this, item);
+ } else if constexpr (requires { serialize(*this, item); }) {
+ return serialize(*this, item);
+ } else if constexpr (std::is_fundamental_v<type> || std::is_enum_v<type>) {
+ if constexpr (resizable) {
+ if (auto result = enlarge_for(sizeof(item));
+ failure(result)) [[unlikely]] {
+ return result;
+ }
+ } else if (sizeof(item) > m_data.size() - m_position)
+ [[unlikely]] {
+ return std::errc::result_out_of_range;
+ }
+
+ if (std::is_constant_evaluated()) {
+ auto value = std::bit_cast<
+ std::array<std::remove_const_t<byte_type>,
+ sizeof(item)>>(item);
+ for (std::size_t i = 0; i < sizeof(value); ++i) {
+ if constexpr (endian_aware) {
+ m_data[m_position + i] = value[sizeof(value) - 1 - i];
+ } else {
+ m_data[m_position + i] = value[i];
+ }
+ }
+ } else {
+ if constexpr (endian_aware) {
+ std::reverse_copy(
+ reinterpret_cast<const byte_type *>(&item),
+ reinterpret_cast<const byte_type *>(&item) +
+ sizeof(item),
+ m_data.data() + m_position);
+ } else {
+ std::memcpy(
+ m_data.data() + m_position, &item, sizeof(item));
+ }
+ }
+ m_position += sizeof(item);
+ return {};
+ } else if constexpr (requires {
+ requires std::same_as<
+ bytes<typename type::value_type>,
+ type>;
+ }) {
+ static_assert(
+ !endian_aware ||
+ concepts::byte_type<
+ std::remove_cvref_t<decltype(*item.data())>>);
+
+ auto item_size_in_bytes = item.size_in_bytes();
+ if (!item_size_in_bytes) [[unlikely]] {
+ return {};
+ }
+
+ if constexpr (resizable) {
+ if (auto result = enlarge_for(item_size_in_bytes);
+ failure(result)) [[unlikely]] {
+ return result;
+ }
+ } else if (item_size_in_bytes > m_data.size() - m_position)
+ [[unlikely]] {
+ return std::errc::result_out_of_range;
+ }
+
+ if (std::is_constant_evaluated()) {
+ auto count = item.count();
+ for (std::size_t index = 0; index < count; ++index) {
+ auto value = std::bit_cast<
+ std::array<std::remove_const_t<byte_type>,
+ sizeof(typename type::value_type)>>(
+ item.data()[index]);
+ for (std::size_t i = 0;
+ i < sizeof(typename type::value_type);
+ ++i) {
+ m_data[m_position +
+ index * sizeof(typename type::value_type) +
+ i] = value[i];
+ }
+ }
+ } else {
+ // Ignore GCC Issue.
+#if !defined __clang__ && defined __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Warray-bounds"
+#endif
+ std::memcpy(m_data.data() + m_position,
+ item.data(),
+ item_size_in_bytes);
+#if !defined __clang__ && defined __GNUC__
+#pragma GCC diagnostic pop
+#endif
+ }
+ m_position += item_size_in_bytes;
+ return {};
+ } else if constexpr (concepts::empty<type>) {
+ return {};
+ } else if constexpr (concepts::serialize_as_bytes<decltype(*this),
+ type>) {
+ return serialize_one(as_bytes(item));
+ } else if constexpr (concepts::self_referencing<type>) {
+ return visit_members(
+ item,
+ [&](auto &&... items) constexpr {
+ return serialize_many(items...);
+ });
+ } else {
+ return visit_members(
+ item,
+ [&](auto &&... items) ZPP_BITS_CONSTEXPR_INLINE_LAMBDA {
+ return serialize_many(items...);
+ });
+ }
+ }
+
+ template <typename SizeType = default_size_type>
+ ZPP_BITS_INLINE constexpr errc serialize_one(concepts::array auto && array)
+ {
+ using value_type = std::remove_cvref_t<decltype(array[0])>;
+
+ if constexpr (concepts::serialize_as_bytes<decltype(*this),
+ value_type>) {
+ return serialize_one(bytes(array));
+ } else {
+ for (auto & item : array) {
+ if (auto result = serialize_one(item); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+ }
+ return {};
+ }
+ }
+
+ template <typename SizeType = default_size_type>
+ ZPP_BITS_INLINE constexpr errc
+ serialize_one(concepts::container auto && container)
+ {
+ using type = std::remove_cvref_t<decltype(container)>;
+ using value_type = typename type::value_type;
+
+ if constexpr (concepts::serialize_as_bytes<decltype(*this),
+ value_type> &&
+ std::is_base_of_v<std::random_access_iterator_tag,
+ typename std::iterator_traits<
+ typename type::iterator>::
+ iterator_category> &&
+ requires { container.data(); }) {
+ auto size = container.size();
+ if constexpr (!std::is_void_v<SizeType> &&
+ (concepts::associative_container<
+ decltype(container)> ||
+ requires(type container) {
+ container.resize(1);
+ } ||
+ (
+ requires(type container) {
+ container = {container.data(), 1};
+ } &&
+ !requires {
+ requires(type::extent !=
+ std::dynamic_extent);
+ requires concepts::
+ has_fixed_nonzero_size<type>;
+ }))) {
+ if (auto result =
+ serialize_one(static_cast<SizeType>(size));
+ failure(result)) [[unlikely]] {
+ return result;
+ }
+ }
+ return serialize_one(bytes(container, size));
+ } else {
+ if constexpr (!std::is_void_v<SizeType> &&
+ (concepts::associative_container<
+ decltype(container)> ||
+ requires(type container) {
+ container.resize(1);
+ } ||
+ (
+ requires(type container) {
+ container = {container.data(), 1};
+ } &&
+ !requires {
+ requires(type::extent !=
+ std::dynamic_extent);
+ requires concepts::
+ has_fixed_nonzero_size<type>;
+ }))) {
+ if (auto result = serialize_one(
+ static_cast<SizeType>(container.size()));
+ failure(result)) [[unlikely]] {
+ return result;
+ }
+ }
+ for (auto & item : container) {
+ if (auto result = serialize_one(item); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+ }
+ return {};
+ }
+ }
+
+ ZPP_BITS_INLINE constexpr errc
+ serialize_one(concepts::tuple auto && tuple)
+ {
+ return serialize_one(tuple,
+ std::make_index_sequence<std::tuple_size_v<
+ std::remove_cvref_t<decltype(tuple)>>>());
+ }
+
+ template <std::size_t... Indices>
+ ZPP_BITS_INLINE constexpr errc serialize_one(
+ concepts::tuple auto && tuple, std::index_sequence<Indices...>)
+ {
+ return serialize_many(std::get<Indices>(tuple)...);
+ }
+
+ ZPP_BITS_INLINE constexpr errc
+ serialize_one(concepts::optional auto && optional)
+ {
+ if (!optional) [[unlikely]] {
+ return serialize_one(std::byte(false));
+ } else {
+ return serialize_many(std::byte(true), *optional);
+ }
+ }
+
+ template <typename KnownId = void>
+ ZPP_BITS_INLINE constexpr errc
+ serialize_one(concepts::variant auto && variant)
+ {
+ using type = std::remove_cvref_t<decltype(variant)>;
+
+ if constexpr (!std::is_void_v<KnownId>) {
+ return serialize_one(
+ *std::get_if<
+ traits::variant<type>::template index<KnownId::value>()>(
+ std::addressof(variant)));
+ } else {
+ auto variant_index = variant.index();
+ if (std::variant_npos == variant_index) [[unlikely]] {
+ return std::errc::invalid_argument;
+ }
+
+ return std::visit(
+ [index = variant_index,
+ this](auto & object) ZPP_BITS_CONSTEXPR_INLINE_LAMBDA {
+ return this->serialize_many(
+ traits::variant<type>::id(index), object);
+ },
+ variant);
+ }
+ }
+
+ ZPP_BITS_INLINE constexpr errc
+ serialize_one(concepts::owning_pointer auto && pointer)
+ {
+ if (nullptr == pointer) [[unlikely]] {
+ return std::errc::invalid_argument;
+ }
+
+ return serialize_one(*pointer);
+ }
+
+ ZPP_BITS_INLINE constexpr errc
+ serialize_one(concepts::bitset auto && bitset)
+ {
+ constexpr auto size = std::remove_cvref_t<decltype(bitset)>{}.size();
+ constexpr auto size_in_bytes = (size + (CHAR_BIT - 1)) / CHAR_BIT;
+
+ if constexpr (resizable) {
+ if (auto result = enlarge_for(size_in_bytes);
+ failure(result)) [[unlikely]] {
+ return result;
+ }
+ } else if (size_in_bytes > m_data.size() - m_position)
+ [[unlikely]] {
+ return std::errc::result_out_of_range;
+ }
+
+ auto data = m_data.data() + m_position;
+ for (std::size_t i = 0; i < size; ++i) {
+ auto & value = data[i / CHAR_BIT];
+ value = byte_type(static_cast<unsigned char>(value) |
+ (bitset[i] << (i & 0x7)));
+ }
+
+ m_position += size_in_bytes;
+ return {};
+ }
+
+ template <typename SizeType = default_size_type>
+ ZPP_BITS_INLINE constexpr errc serialize_one(concepts::by_protocol auto && item)
+ {
+ using type = std::remove_cvref_t<decltype(item)>;
+ if constexpr (!std::is_void_v<SizeType>) {
+ auto size_position = m_position;
+ if (auto result = serialize_one(SizeType{});
+ failure(result)) [[unlikely]] {
+ return result;
+ }
+
+ if constexpr (requires { typename type::serialize; }) {
+ constexpr auto protocol = type::serialize::value;
+ if (auto result = protocol(*this, item); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+ } else {
+ constexpr auto protocol = decltype(serialize(item))::value;
+ if (auto result = protocol(*this, item); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+ }
+
+ auto current_position = m_position;
+ std::size_t message_size =
+ current_position - size_position - sizeof(SizeType);
+ if constexpr (concepts::varint<SizeType>) {
+ constexpr auto preserialized_varint_size = 1;
+ message_size = current_position - size_position -
+ preserialized_varint_size;
+ auto move_ahead_count =
+ varint_size(message_size) - preserialized_varint_size;
+ if (move_ahead_count) {
+ if constexpr (resizable) {
+ if (auto result = enlarge_for(move_ahead_count);
+ failure(result)) [[unlikely]] {
+ return result;
+ }
+ } else if (move_ahead_count >
+ m_data.size() - current_position)
+ [[unlikely]] {
+ return std::errc::result_out_of_range;
+ }
+ auto data = m_data.data();
+ auto message_start =
+ data + size_position + preserialized_varint_size;
+ auto message_end = data + current_position;
+ if (std::is_constant_evaluated()) {
+ for (auto p = message_end - 1; p >= message_start;
+ --p) {
+ *(p + move_ahead_count) = *p;
+ }
+ } else {
+ std::memmove(message_start + move_ahead_count,
+ message_start,
+ message_size);
+ }
+ m_position += move_ahead_count;
+ }
+ }
+ return basic_out<std::span<byte_type, sizeof(SizeType)>>{
+ std::span<byte_type, sizeof(SizeType)>{
+ m_data.data() + size_position, sizeof(SizeType)}}(
+ SizeType(message_size));
+ } else {
+ if constexpr (requires {typename type::serialize;}) {
+ constexpr auto protocol = type::serialize::value;
+ return protocol(*this, item);
+ } else {
+ constexpr auto protocol = decltype(serialize(item))::value;
+ return protocol(*this, item);
+ }
+ }
+ }
+
+ constexpr ~basic_out() = default;
+
+ view_type m_data{};
+ std::size_t m_position{};
+};
+
+template <concepts::byte_view ByteView = std::vector<std::byte>, typename... Options>
+class out : public basic_out<ByteView, Options...>
+{
+public:
+ template <typename... Types>
+ using template_type = out<Types...>;
+
+ using base = basic_out<ByteView, Options...>;
+ using base::basic_out;
+
+ friend access;
+
+ using base::resizable;
+ using base::enlarger;
+
+ constexpr static auto no_fit_size =
+ (... ||
+ std::same_as<std::remove_cvref_t<Options>, options::no_fit_size>);
+
+ ZPP_BITS_INLINE constexpr auto operator()(auto &&... items)
+ {
+ if constexpr (resizable && !no_fit_size &&
+ enlarger != std::tuple{1, 1}) {
+ auto end = m_data.size();
+ auto result = serialize_many(items...);
+ if (m_position >= end) {
+ m_data.resize(m_position);
+ }
+ return result;
+ } else {
+ return serialize_many(items...);
+ }
+ }
+
+private:
+ using base::serialize_many;
+ using base::m_data;
+ using base::m_position;
+};
+
+template <typename Type, typename... Options>
+out(Type &, Options &&...) -> out<Type, Options...>;
+
+template <typename Type, typename... Options>
+out(Type &&, Options &&...) -> out<Type, Options...>;
+
+template <typename Type, std::size_t Size, typename... Options>
+out(Type (&)[Size], Options &&...)
+ -> out<std::span<Type, Size>, Options...>;
+
+template <typename Type, typename SizeType, typename... Options>
+out(sized_item<Type, SizeType> &, Options &&...)
+ -> out<Type, Options...>;
+
+template <typename Type, typename SizeType, typename... Options>
+out(const sized_item<Type, SizeType> &, Options &&...)
+ -> out<const Type, Options...>;
+
+template <typename Type, typename SizeType, typename... Options>
+out(sized_item<Type, SizeType> &&, Options &&...) -> out<Type, Options...>;
+
+template <concepts::byte_view ByteView = std::vector<std::byte>,
+ typename... Options>
+class in
+{
+public:
+ template <typename... Types>
+ using template_type = in<Types...>;
+
+ template <typename>
+ friend struct option;
+
+ friend access;
+
+ template <typename, typename>
+ friend struct sized_item;
+
+ template <typename, typename>
+ friend struct sized_item_ref;
+
+ template <typename, concepts::variant>
+ friend struct known_id_variant;
+
+ template <typename, concepts::variant>
+ friend struct known_dynamic_id_variant;
+
+ using byte_type = std::add_const_t<typename ByteView::value_type>;
+
+ constexpr static auto endian_aware =
+ (... ||
+ std::same_as<std::remove_cvref_t<Options>, endian::swapped>);
+
+ using default_size_type = traits::default_size_type_t<Options...>;
+
+ constexpr static auto allocation_limit =
+ traits::alloc_limit<Options...>();
+
+ constexpr explicit in(ByteView && view, Options && ... options) : m_data(view)
+ {
+ static_assert(!resizable);
+ (options(*this), ...);
+ }
+
+ constexpr explicit in(ByteView & view, Options && ... options) : m_data(view)
+ {
+ (options(*this), ...);
+ }
+
+ ZPP_BITS_INLINE constexpr auto operator()(auto &&... items)
+ {
+ return serialize_many(items...);
+ }
+
+ constexpr decltype(auto) data()
+ {
+ return m_data;
+ }
+
+ constexpr std::size_t position() const
+ {
+ return m_position;
+ }
+
+ constexpr std::size_t & position()
+ {
+ return m_position;
+ }
+
+ constexpr auto remaining_data()
+ {
+ return std::span<byte_type>{m_data.data() + m_position,
+ m_data.size() - m_position};
+ }
+
+ constexpr auto processed_data()
+ {
+ return std::span<byte_type>{m_data.data(), m_position};
+ }
+
+ constexpr void reset(std::size_t position = 0)
+ {
+ m_position = position;
+ }
+
+ constexpr static auto kind()
+ {
+ return kind::in;
+ }
+
+ constexpr static bool resizable = requires(ByteView view)
+ {
+ view.resize(1);
+ };
+
+ using view_type =
+ std::conditional_t<resizable,
+ ByteView &,
+ std::remove_cvref_t<decltype(
+ std::span{std::declval<ByteView &>()})>>;
+
+private:
+ ZPP_BITS_INLINE constexpr errc serialize_many(auto && first_item,
+ auto &&... items)
+ {
+ if (auto result = serialize_one(first_item); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+
+ return serialize_many(items...);
+ }
+
+ ZPP_BITS_INLINE constexpr errc serialize_many()
+ {
+ return {};
+ }
+
+ ZPP_BITS_INLINE constexpr errc serialize_one(concepts::unspecialized auto && item)
+ {
+ using type = std::remove_cvref_t<decltype(item)>;
+ static_assert(!std::is_pointer_v<type>);
+
+ if constexpr (requires { type::serialize(*this, item); }) {
+ return type::serialize(*this, item);
+ } else if constexpr (requires { serialize(*this, item); }) {
+ return serialize(*this, item);
+ } else if constexpr (std::is_fundamental_v<type> || std::is_enum_v<type>) {
+ auto size = m_data.size();
+ if (sizeof(item) > size - m_position) [[unlikely]] {
+ return std::errc::result_out_of_range;
+ }
+ if (std::is_constant_evaluated()) {
+ std::array<std::remove_const_t<byte_type>, sizeof(item)>
+ value;
+ for (std::size_t i = 0; i < sizeof(value); ++i) {
+ if constexpr (endian_aware) {
+ value[sizeof(value) - 1 - i] =
+ byte_type(m_data[m_position + i]);
+ } else {
+ value[i] = byte_type(m_data[m_position + i]);
+ }
+ }
+ item = std::bit_cast<type>(value);
+ } else {
+ if constexpr (endian_aware) {
+ auto begin = m_data.data() + m_position;
+ std::reverse_copy(
+ begin,
+ begin + sizeof(item),
+ reinterpret_cast<std::remove_const_t<byte_type> *>(
+ &item));
+ } else {
+ std::memcpy(
+ &item, m_data.data() + m_position, sizeof(item));
+ }
+ }
+ m_position += sizeof(item);
+ return {};
+ } else if constexpr (requires {
+ requires std::same_as<
+ bytes<typename type::value_type>,
+ type>;
+ }) {
+ static_assert(
+ !endian_aware ||
+ concepts::byte_type<
+ std::remove_cvref_t<decltype(*item.data())>>);
+
+ auto size = m_data.size();
+ auto item_size_in_bytes = item.size_in_bytes();
+ if (!item_size_in_bytes) [[unlikely]] {
+ return {};
+ }
+
+ if (item_size_in_bytes > size - m_position) [[unlikely]] {
+ return std::errc::result_out_of_range;
+ }
+ if (std::is_constant_evaluated()) {
+ std::size_t count = item.count();
+ for (std::size_t index = 0; index < count; ++index) {
+ std::array<std::remove_const_t<byte_type>,
+ sizeof(typename type::value_type)>
+ value;
+ for (std::size_t i = 0;
+ i < sizeof(typename type::value_type);
+ ++i) {
+ value[i] = byte_type(
+ m_data[m_position +
+ index *
+ sizeof(typename type::value_type) +
+ i]);
+ }
+ item.data()[index] =
+ std::bit_cast<typename type::value_type>(value);
+ }
+ } else {
+ std::memcpy(item.data(),
+ m_data.data() + m_position,
+ item_size_in_bytes);
+ }
+ m_position += item_size_in_bytes;
+ return {};
+ } else if constexpr (concepts::empty<type>) {
+ return {};
+ } else if constexpr (concepts::serialize_as_bytes<decltype(*this),
+ type>) {
+ return serialize_one(as_bytes(item));
+ } else if constexpr (concepts::self_referencing<type>) {
+ return visit_members(
+ item,
+ [&](auto &&... items) constexpr {
+ return serialize_many(items...);
+ });
+ } else {
+ return visit_members(
+ item,
+ [&](auto &&... items) ZPP_BITS_CONSTEXPR_INLINE_LAMBDA {
+ return serialize_many(items...);
+ });
+ }
+ }
+
+ template <typename SizeType = default_size_type>
+ ZPP_BITS_INLINE constexpr errc serialize_one(concepts::array auto && array)
+ {
+ using value_type = std::remove_cvref_t<decltype(array[0])>;
+
+ if constexpr (concepts::serialize_as_bytes<decltype(*this),
+ value_type>) {
+ return serialize_one(bytes(array));
+ } else {
+ for (auto & item : array) {
+ if (auto result = serialize_one(item); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+ }
+ return {};
+ }
+ }
+
+ template <typename SizeType = default_size_type>
+ ZPP_BITS_INLINE constexpr errc
+ serialize_one(concepts::container auto && container)
+ {
+ using type = std::remove_cvref_t<decltype(container)>;
+ using value_type = typename type::value_type;
+ constexpr auto is_const = std::is_const_v<
+ std::remove_reference_t<decltype(container[0])>>;
+
+ if constexpr (!std::is_void_v<SizeType> &&
+ (requires(type container) { container.resize(1); } ||
+ (
+ requires(type container) {
+ container = {container.data(), 1};
+ } &&
+ !requires {
+ requires(type::extent !=
+ std::dynamic_extent);
+ requires concepts::has_fixed_nonzero_size<
+ type>;
+ }))) {
+ SizeType size{};
+ if (auto result = serialize_one(size); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+
+ if constexpr (requires(type container) {
+ container.resize(size);
+ }) {
+ if constexpr (allocation_limit !=
+ std::numeric_limits<std::size_t>::max()) {
+ constexpr auto limit =
+ allocation_limit / sizeof(value_type);
+ if (size > limit) [[unlikely]] {
+ return std::errc::message_size;
+ }
+ }
+ container.resize(size);
+ } else if constexpr (is_const &&
+ (std::same_as<std::byte, value_type> ||
+ std::same_as<char, value_type> ||
+ std::same_as<unsigned char,
+ value_type>)) {
+ if (size > m_data.size() - m_position) [[unlikely]] {
+ return std::errc::result_out_of_range;
+ }
+ container = {m_data.data() + m_position, size};
+ m_position += size;
+ } else {
+ if (size > container.size()) [[unlikely]] {
+ return std::errc::result_out_of_range;
+ }
+ container = {container.data(), size};
+ }
+
+ if constexpr (
+ concepts::serialize_as_bytes<decltype(*this),
+ value_type> &&
+ std::is_base_of_v<
+ std::random_access_iterator_tag,
+ typename std::iterator_traits<
+ typename type::iterator>::iterator_category> &&
+ requires { container.data(); } &&
+ !(is_const &&
+ (std::same_as<std::byte, value_type> ||
+ std::same_as<char, value_type> ||
+ std::same_as<unsigned char,
+ value_type>)&&requires(type container) {
+ container = {m_data.data(), 1};
+ })) {
+ return serialize_one(bytes(container, size));
+ }
+ }
+
+ if constexpr (concepts::serialize_as_bytes<decltype(*this),
+ value_type> &&
+ std::is_base_of_v<std::random_access_iterator_tag,
+ typename std::iterator_traits<
+ typename type::iterator>::
+ iterator_category> &&
+ requires { container.data(); }) {
+ if constexpr (is_const &&
+ (std::same_as<std::byte, value_type> ||
+ std::same_as<char, value_type> ||
+ std::same_as<
+ unsigned char,
+ value_type>)&&requires(type container) {
+ container = {m_data.data(), 1};
+ }) {
+ if constexpr (requires {
+ requires(type::extent !=
+ std::dynamic_extent);
+ requires concepts::has_fixed_nonzero_size<type>;
+ }) {
+ if (type::extent > m_data.size() - m_position)
+ [[unlikely]] {
+ return std::errc::result_out_of_range;
+ }
+ container = {m_data.data() + m_position, type::extent};
+ m_position += type::extent;
+ } else if constexpr (std::is_void_v<SizeType>) {
+ auto size = m_data.size();
+ container = {m_data.data() + m_position,
+ size - m_position};
+ m_position = size;
+ }
+ return {};
+ } else {
+ return serialize_one(bytes(container));
+ }
+ } else {
+ for (auto & item : container) {
+ if (auto result = serialize_one(item); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+ }
+ return {};
+ }
+ }
+
+ template <typename SizeType = default_size_type>
+ ZPP_BITS_INLINE constexpr errc
+ serialize_one(concepts::associative_container auto && container)
+ {
+ using type = typename std::remove_cvref_t<decltype(container)>;
+
+ SizeType size{};
+
+ if constexpr (!std::is_void_v<SizeType>) {
+ if (auto result = serialize_one(size); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+ } else {
+ size = container.size();
+ }
+
+ container.clear();
+
+ for (std::size_t index{}; index < size; ++index)
+ {
+ if constexpr (requires { typename type::mapped_type; }) {
+ using value_type = std::pair<typename type::key_type,
+ typename type::mapped_type>;
+ std::aligned_storage_t<sizeof(value_type),
+ alignof(value_type)>
+ storage;
+
+ auto object = access::placement_new<value_type>(
+ std::addressof(storage));
+ destructor_guard guard{*object};
+ if (auto result = serialize_one(*object); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+
+ container.insert(std::move(*object));
+ } else {
+ using value_type = typename type::value_type;
+
+ std::aligned_storage_t<sizeof(value_type),
+ alignof(value_type)>
+ storage;
+
+ auto object = access::placement_new<value_type>(
+ std::addressof(storage));
+ destructor_guard guard{*object};
+ if (auto result = serialize_one(*object); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+
+ container.insert(std::move(*object));
+ }
+ }
+
+ return {};
+ }
+
+ ZPP_BITS_INLINE constexpr errc
+ serialize_one(concepts::tuple auto && tuple)
+ {
+ return serialize_one(tuple,
+ std::make_index_sequence<std::tuple_size_v<
+ std::remove_cvref_t<decltype(tuple)>>>());
+ }
+
+ template <std::size_t... Indices>
+ ZPP_BITS_INLINE constexpr errc serialize_one(
+ concepts::tuple auto && tuple, std::index_sequence<Indices...>)
+ {
+ return serialize_many(std::get<Indices>(tuple)...);
+ }
+
+ ZPP_BITS_INLINE constexpr errc
+ serialize_one(concepts::optional auto && optional)
+ {
+ using value_type = std::remove_reference_t<decltype(*optional)>;
+
+ std::byte has_value{};
+ if (auto result = serialize_one(has_value); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+
+ if (!bool(has_value)) [[unlikely]] {
+ optional = std::nullopt;
+ return {};
+ }
+
+ if constexpr (std::is_default_constructible_v<value_type>) {
+ if (!optional) {
+ optional = value_type{};
+ }
+
+ if (auto result = serialize_one(*optional); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+ } else {
+ std::aligned_storage_t<sizeof(value_type), alignof(value_type)>
+ storage;
+
+ auto object =
+ access::placement_new<value_type>(std::addressof(storage));
+ destructor_guard guard{*object};
+
+ if (auto result = serialize_one(*object); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+
+ optional = std::move(*object);
+ }
+
+ return {};
+ }
+
+ template <typename KnownId = void,
+ typename... Types,
+ template <typename...>
+ typename Variant>
+ ZPP_BITS_INLINE constexpr errc
+ serialize_one(Variant<Types...> & variant) requires
+ concepts::variant<Variant<Types...>>
+ {
+ using type = std::remove_cvref_t<decltype(variant)>;
+
+ if constexpr (!std::is_void_v<KnownId>) {
+ constexpr auto index =
+ traits::variant<type>::template index<KnownId::value>();
+
+ using element_type =
+ std::remove_reference_t<decltype(std::get<index>(
+ variant))>;
+
+ if constexpr (std::is_default_constructible_v<element_type>) {
+ if (variant.index() !=
+ traits::variant<type>::template index_by_type<
+ element_type>()) {
+ variant = element_type{};
+ }
+ return serialize_one(*std::get_if<element_type>(&variant));
+ } else {
+ std::aligned_storage_t<sizeof(element_type),
+ alignof(element_type)>
+ storage;
+
+ auto object = access::placement_new<element_type>(
+ std::addressof(storage));
+ destructor_guard guard{*object};
+
+ if (auto result = serialize_one(*object); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+ variant = std::move(*object);
+ }
+ } else {
+ typename traits::variant<type>::id_type id;
+ if (auto result = serialize_one(id); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+
+ return serialize_one(variant, id);
+ }
+ }
+
+ template <typename... Types, template <typename...> typename Variant>
+ ZPP_BITS_INLINE constexpr errc
+ serialize_one(Variant<Types...> & variant,
+ auto && id) requires concepts::variant<Variant<Types...>>
+ {
+ using type = std::remove_cvref_t<decltype(variant)>;
+
+ auto index = traits::variant<type>::index(id);
+ if (index > sizeof...(Types)) [[unlikely]] {
+ return std::errc::bad_message;
+ }
+
+ constexpr std::tuple loaders{
+ [](auto & self,
+ auto & variant) ZPP_BITS_CONSTEXPR_INLINE_LAMBDA {
+ if constexpr (std::is_default_constructible_v<Types>) {
+ if (variant.index() !=
+ traits::variant<type>::template index_by_type<
+ Types>()) {
+ variant = Types{};
+ }
+ return self.serialize_one(
+ *std::get_if<Types>(&variant));
+ } else {
+ std::aligned_storage_t<sizeof(Types), alignof(Types)>
+ storage;
+
+ auto object = access::placement_new<Types>(
+ std::addressof(storage));
+ destructor_guard guard{*object};
+
+ if (auto result = self.serialize_one(*object);
+ failure(result)) [[unlikely]] {
+ return result;
+ }
+ variant = std::move(*object);
+ }
+ }...};
+
+ return traits::tuple<std::remove_cvref_t<decltype(loaders)>>::
+ visit(loaders, index, [&](auto && loader) ZPP_BITS_CONSTEXPR_INLINE_LAMBDA {
+ return loader(*this, variant);
+ });
+ }
+
+ ZPP_BITS_INLINE constexpr errc
+ serialize_one(concepts::owning_pointer auto && pointer)
+ {
+ using type = std::remove_reference_t<decltype(*pointer)>;
+
+ auto loaded = access::make_unique<type>();;
+ if (auto result = serialize_one(*loaded); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+
+ pointer.reset(loaded.release());
+ return {};
+ }
+
+ ZPP_BITS_INLINE constexpr errc
+ serialize_one(concepts::bitset auto && bitset)
+ {
+ constexpr auto size = std::remove_cvref_t<decltype(bitset)>{}.size();
+ constexpr auto size_in_bytes = (size + (CHAR_BIT - 1)) / CHAR_BIT;
+
+ if (size_in_bytes > m_data.size() - m_position)
+ [[unlikely]] {
+ return std::errc::result_out_of_range;
+ }
+
+ auto data = m_data.data() + m_position;
+ for (std::size_t i = 0; i < size; ++i) {
+ bitset[i] = (static_cast<unsigned char>(data[i / CHAR_BIT]) >>
+ (i & 0x7)) &
+ 0x1;
+ }
+
+ m_position += size_in_bytes;
+ return {};
+ }
+
+ template <typename SizeType = default_size_type>
+ ZPP_BITS_INLINE constexpr errc serialize_one(concepts::by_protocol auto && item)
+ {
+ using type = std::remove_cvref_t<decltype(item)>;
+ if constexpr (!std::is_void_v<SizeType>) {
+ SizeType size{};
+ if (auto result = serialize_one(size); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+
+ if constexpr (requires {typename type::serialize;}) {
+ constexpr auto protocol = type::serialize::value;
+ return protocol(*this, item, size);
+ } else {
+ constexpr auto protocol = decltype(serialize(item))::value;
+ return protocol(*this, item, size);
+ }
+ } else {
+ if constexpr (requires {typename type::serialize;}) {
+ constexpr auto protocol = type::serialize::value;
+ return protocol(*this, item);
+ } else {
+ constexpr auto protocol = decltype(serialize(item))::value;
+ return protocol(*this, item);
+ }
+ }
+ }
+
+ view_type m_data{};
+ std::size_t m_position{};
+};
+
+template <typename Type, std::size_t Size, typename... Options>
+in(Type (&)[Size], Options && ...) -> in<std::span<Type, Size>, Options...>;
+
+template <typename Type, typename SizeType, typename... Options>
+in(sized_item<Type, SizeType> &, Options && ...)
+ -> in<Type, Options...>;
+
+template <typename Type, typename SizeType, typename... Options>
+in(const sized_item<Type, SizeType> &, Options && ...)
+ -> in<const Type, Options...>;
+
+template <typename Type, typename SizeType, typename... Options>
+in(sized_item<Type, SizeType> &&, Options && ...)
+ -> in<Type, Options...>;
+
+constexpr auto input(auto && view, auto &&... option)
+{
+ return in(std::forward<decltype(view)>(view),
+ std::forward<decltype(option)>(option)...);
+}
+
+constexpr auto output(auto && view, auto &&... option)
+{
+ return out(std::forward<decltype(view)>(view),
+ std::forward<decltype(option)>(option)...);
+}
+
+constexpr auto in_out(auto && view, auto &&... option)
+{
+ return std::tuple{
+ in<std::remove_reference_t<typename decltype(in{view})::view_type>,
+ decltype(option) &...>(view, option...),
+ out(std::forward<decltype(view)>(view),
+ std::forward<decltype(option)>(option)...)};
+}
+
+template <typename ByteType = std::byte>
+constexpr auto data_in_out(auto &&... option)
+{
+ struct data_in_out
+ {
+ data_in_out(decltype(option) &&... option) :
+ input(data, option...),
+ output(data, std::forward<decltype(option)>(option)...)
+ {
+ }
+
+ std::vector<ByteType> data;
+ in<decltype(data), decltype(option) &...> input;
+ out<decltype(data), decltype(option)...> output;
+ };
+ return data_in_out{std::forward<decltype(option)>(option)...};
+}
+
+template <typename ByteType = std::byte>
+constexpr auto data_in(auto &&... option)
+{
+ struct data_in
+ {
+ data_in(decltype(option) &&... option) :
+ input(data, std::forward<decltype(option)>(option)...)
+ {
+ }
+
+ std::vector<ByteType> data;
+ in<decltype(data), decltype(option)...> input;
+ };
+ return data_in{std::forward<decltype(option)>(option)...};
+}
+
+template <typename ByteType = std::byte>
+constexpr auto data_out(auto &&... option)
+{
+ struct data_out
+ {
+ data_out(decltype(option) &&... option) :
+ output(data, std::forward<decltype(option)>(option)...)
+ {
+ }
+
+ std::vector<ByteType> data;
+ out<decltype(data), decltype(option)...> output;
+ };
+ return data_out{std::forward<decltype(option)>(option)...};
+}
+
+template <auto Object, std::size_t MaxSize = 0x1000>
+constexpr auto to_bytes_one()
+{
+ constexpr auto size = [] {
+ std::array<std::byte, MaxSize> data;
+ out out{data};
+ out(Object).or_throw();
+ return out.position();
+ }();
+
+ if constexpr (!size) {
+ return string_literal<std::byte, 0>{};
+ } else {
+ std::array<std::byte, size> data;
+ out{data}(Object).or_throw();
+ return data;
+ }
+}
+
+template <auto... Data>
+constexpr auto join()
+{
+ constexpr auto size = (0 + ... + Data.size());
+ if constexpr (!size) {
+ return string_literal<std::byte, 0>{};
+ } else {
+ std::array<std::byte, size> data;
+ out{data}(Data...).or_throw();
+ return data;
+ }
+}
+
+template <auto Left, auto Right = -1>
+constexpr auto slice(auto array)
+{
+ constexpr auto left = Left;
+ constexpr auto right = (-1 == Right) ? array.size() : Right;
+ constexpr auto size = right - left;
+ static_assert(Left < Right || -1 == Right);
+
+ std::array<std::remove_reference_t<decltype(array[0])>, size> sliced;
+ std::copy(std::begin(array) + left,
+ std::begin(array) + right,
+ std::begin(sliced));
+ return sliced;
+}
+
+template <auto... Object>
+constexpr auto to_bytes()
+{
+ return join<to_bytes_one<Object>()...>();
+}
+
+template <auto Data, typename Type>
+constexpr auto from_bytes()
+{
+ Type object;
+ in{Data}(object).or_throw();
+ return object;
+}
+
+template <auto Data, typename... Types>
+constexpr auto from_bytes() requires (sizeof...(Types) > 1)
+{
+ std::tuple<Types...> object;
+ in{Data}(object).or_throw();
+ return object;
+}
+
+template <auto Id, auto MaxSize = -1>
+constexpr auto serialize_id()
+{
+ constexpr auto serialized_id = slice<0, MaxSize>(to_bytes<Id>());
+ if constexpr (sizeof(serialized_id) == 1) {
+ return serialization_id<from_bytes<serialized_id, std::byte>()>{};
+ } else if constexpr (sizeof(serialized_id) == 2) {
+ return serialization_id<from_bytes<serialized_id, std::uint16_t>()>{};
+ } else if constexpr (sizeof(serialized_id) == 4) {
+ return serialization_id<from_bytes<serialized_id, std::uint32_t>()>{};
+ } else if constexpr (sizeof(serialized_id) == 8) {
+ return serialization_id<from_bytes<serialized_id, std::uint64_t>()>{};
+ } else {
+ return serialization_id<serialized_id>{};
+ }
+}
+
+template <auto Id, auto MaxSize = -1>
+using id = decltype(serialize_id<Id, MaxSize>());
+
+template <auto Id, auto MaxSize = -1>
+constexpr auto id_v = id<Id, MaxSize>::value;
+
+template <typename Id, concepts::variant Variant>
+struct known_id_variant
+{
+ constexpr explicit known_id_variant(Variant & variant) :
+ variant(variant)
+ {
+ }
+
+ ZPP_BITS_INLINE constexpr static auto serialize(auto & serializer,
+ auto & self)
+ {
+ return serializer.template serialize_one<Id>(self.variant);
+ }
+
+ Variant & variant;
+};
+
+template <auto Id, auto MaxSize = -1, typename Variant>
+constexpr auto known_id(Variant && variant)
+{
+ return known_id_variant<id<Id, MaxSize>,
+ std::remove_reference_t<Variant>>(variant);
+}
+
+template <typename Id, concepts::variant Variant>
+struct known_dynamic_id_variant
+{
+ using id_type =
+ std::conditional_t<std::is_integral_v<std::remove_cvref_t<Id>> ||
+ std::is_enum_v<std::remove_cvref_t<Id>>,
+ std::remove_cvref_t<Id>,
+ Id &>;
+
+ constexpr explicit known_dynamic_id_variant(Variant & variant, id_type id) :
+ variant(variant),
+ id(id)
+ {
+ }
+
+ ZPP_BITS_INLINE constexpr static auto serialize(auto & serializer,
+ auto & self)
+ {
+ return serializer.template serialize_one(self.variant, self.id);
+ }
+
+ Variant & variant;
+ id_type id;
+};
+
+template <typename Id, typename Variant>
+constexpr auto known_id(Id && id, Variant && variant)
+{
+ return known_dynamic_id_variant<Id, std::remove_reference_t<Variant>>(
+ variant, id);
+}
+
+template <typename Function>
+struct function_traits;
+
+template <typename Return, typename... Arguments>
+struct function_traits<Return(*)(Arguments...)>
+{
+ using parameters_type = std::tuple<std::remove_cvref_t<Arguments>...>;
+ using return_type = Return;
+};
+
+template <typename Return, typename... Arguments>
+struct function_traits<Return(*)(Arguments...) noexcept>
+{
+ using parameters_type = std::tuple<std::remove_cvref_t<Arguments>...>;
+ using return_type = Return;
+};
+
+template <typename This, typename Return, typename... Arguments>
+struct function_traits<Return(This::*)(Arguments...)>
+{
+ using parameters_type = std::tuple<std::remove_cvref_t<Arguments>...>;
+ using return_type = Return;
+};
+
+template <typename This, typename Return, typename... Arguments>
+struct function_traits<Return(This::*)(Arguments...) noexcept>
+{
+ using parameters_type = std::tuple<std::remove_cvref_t<Arguments>...>;
+ using return_type = Return;
+};
+
+template <typename This, typename Return, typename... Arguments>
+struct function_traits<Return(This::*)(Arguments...) const>
+{
+ using parameters_type = std::tuple<std::remove_cvref_t<Arguments>...>;
+ using return_type = Return;
+};
+
+template <typename This, typename Return, typename... Arguments>
+struct function_traits<Return(This::*)(Arguments...) const noexcept>
+{
+ using parameters_type = std::tuple<std::remove_cvref_t<Arguments>...>;
+ using return_type = Return;
+};
+
+template <typename Return>
+struct function_traits<Return(*)()>
+{
+ using parameters_type = void;
+ using return_type = Return;
+};
+
+template <typename Return>
+struct function_traits<Return(*)() noexcept>
+{
+ using parameters_type = void;
+ using return_type = Return;
+};
+
+template <typename This, typename Return>
+struct function_traits<Return(This::*)()>
+{
+ using parameters_type = void;
+ using return_type = Return;
+};
+
+template <typename This, typename Return>
+struct function_traits<Return(This::*)() noexcept>
+{
+ using parameters_type = void;
+ using return_type = Return;
+};
+
+template <typename This, typename Return>
+struct function_traits<Return(This::*)() const>
+{
+ using parameters_type = void;
+ using return_type = Return;
+};
+
+template <typename This, typename Return>
+struct function_traits<Return(This::*)() const noexcept>
+{
+ using parameters_type = void;
+ using return_type = Return;
+};
+
+template <typename Function>
+using function_parameters_t =
+ typename function_traits<std::remove_cvref_t<Function>>::parameters_type;
+
+template <typename Function>
+using function_return_type_t =
+ typename function_traits<std::remove_cvref_t<Function>>::return_type;
+
+constexpr auto success(auto && value_or_errc) requires
+ std::same_as<decltype(value_or_errc.error()), errc>
+{
+ return value_or_errc.success();
+}
+
+constexpr auto failure(auto && value_or_errc) requires
+ std::same_as<decltype(value_or_errc.error()), errc>
+{
+ return value_or_errc.failure();
+}
+
+template <typename Type>
+struct [[nodiscard]] value_or_errc
+{
+ using error_type = errc;
+ using value_type = std::conditional_t<
+ std::is_void_v<Type>,
+ std::nullptr_t,
+ std::conditional_t<
+ std::is_reference_v<Type>,
+ std::add_pointer_t<std::remove_reference_t<Type>>,
+ Type>>;
+
+ constexpr value_or_errc() = default;
+
+ constexpr explicit value_or_errc(auto && value) :
+ m_return_value(std::forward<decltype(value)>(value))
+ {
+ }
+
+ constexpr explicit value_or_errc(error_type error) :
+ m_error(std::forward<decltype(error)>(error))
+ {
+ }
+
+ constexpr value_or_errc(value_or_errc && other) noexcept
+ {
+ if (other.is_value()) {
+ if constexpr (!std::is_void_v<Type>) {
+ if constexpr (!std::is_reference_v<Type>) {
+ ::new (std::addressof(m_return_value))
+ Type(std::move(other.m_return_value));
+ } else {
+ m_return_value = other.m_return_value;
+ }
+ }
+ } else {
+ m_failure = other.m_failure;
+ std::memcpy(&m_error, &other.m_error, sizeof(m_error));
+ }
+ }
+
+ constexpr ~value_or_errc()
+ {
+ if constexpr (!std::is_void_v<Type> &&
+ !std::is_trivially_destructible_v<Type>) {
+ if (success()) {
+ m_return_value.~Type();
+ }
+ }
+ }
+
+ constexpr bool success() const noexcept
+ {
+ return !m_failure;
+ }
+
+ constexpr bool failure() const noexcept
+ {
+ return m_failure;
+ }
+
+ constexpr decltype(auto) value() & noexcept
+ {
+ if constexpr (std::is_same_v<Type, decltype(m_return_value)>) {
+ return (m_return_value);
+ } else {
+ return (*m_return_value);
+ }
+ }
+
+ constexpr decltype(auto) value() && noexcept
+ {
+ if constexpr (std::is_same_v<Type, decltype(m_return_value)>) {
+ return std::forward<Type>(m_return_value);
+ } else {
+ return std::forward<Type>(*m_return_value);
+ }
+ }
+
+ constexpr decltype(auto) value() const & noexcept
+ {
+ if constexpr (std::is_same_v<Type, decltype(m_return_value)>) {
+ return (m_return_value);
+ } else {
+ return (*m_return_value);
+ }
+ }
+
+ constexpr auto error() const noexcept
+ {
+ return m_error;
+ }
+
+ #if __has_include("zpp_throwing.h")
+ constexpr zpp::throwing<Type> operator co_await() &&
+ {
+ if (failure()) [[unlikely]] {
+ return error().code;
+ }
+ return std::move(*this).value();
+ }
+
+ constexpr zpp::throwing<Type> operator co_await() const &
+ {
+ if (failure()) [[unlikely]] {
+ return error().code;
+ }
+ return value();
+ }
+#endif
+
+ constexpr decltype(auto) or_throw() &
+ {
+ if (failure()) [[unlikely]] {
+#ifdef __cpp_exceptions
+ throw std::system_error(std::make_error_code(error().code));
+#else
+ std::abort();
+#endif
+ }
+ return value();
+ }
+
+ constexpr decltype(auto) or_throw() &&
+ {
+ if (failure()) [[unlikely]] {
+#ifdef __cpp_exceptions
+ throw std::system_error(std::make_error_code(error().code));
+#else
+ std::abort();
+#endif
+ }
+ return std::move(*this).value();
+ }
+
+ constexpr decltype(auto) or_throw() const &
+ {
+ if (failure()) [[unlikely]] {
+#ifdef __cpp_exceptions
+ throw std::system_error(std::make_error_code(error().code));
+#else
+ std::abort();
+#endif
+ }
+ return value();
+ }
+
+ union
+ {
+ error_type m_error{};
+ value_type m_return_value;
+ };
+ bool m_failure{};
+};
+
+ZPP_BITS_INLINE constexpr auto
+apply(auto && function, auto && archive) requires(
+ std::remove_cvref_t<decltype(archive)>::kind() == kind::in)
+{
+ using function_type = std::decay_t<decltype(function)>;
+
+ if constexpr (requires { &function_type::operator(); }) {
+ using parameters_type =
+ function_parameters_t<decltype(&function_type::operator())>;
+ using return_type =
+ function_return_type_t<decltype(&function_type::operator())>;
+ if constexpr (std::is_void_v<parameters_type>) {
+ return std::forward<decltype(function)>(function)();
+ } else {
+ parameters_type parameters;
+ if constexpr (std::is_void_v<return_type>) {
+ if (auto result = archive(parameters); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+ std::apply(std::forward<decltype(function)>(function),
+ std::move(parameters));
+ return errc{};
+ } else {
+ if (auto result = archive(parameters); failure(result))
+ [[unlikely]] {
+ return value_or_errc<return_type>{result};
+ }
+ return value_or_errc<return_type>{
+ std::apply(std::forward<decltype(function)>(function),
+ std::move(parameters))};
+ }
+ }
+ } else {
+ using parameters_type = function_parameters_t<function_type>;
+ using return_type = function_return_type_t<function_type>;
+ if constexpr (std::is_void_v<parameters_type>) {
+ return std::forward<decltype(function)>(function)();
+ } else {
+ parameters_type parameters;
+ if constexpr (std::is_void_v<return_type>) {
+ if (auto result = archive(parameters); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+ std::apply(std::forward<decltype(function)>(function),
+ std::move(parameters));
+ return errc{};
+ } else {
+ if (auto result = archive(parameters); failure(result))
+ [[unlikely]] {
+ return value_or_errc<return_type>{result};
+ }
+ return value_or_errc<return_type>{
+ std::apply(std::forward<decltype(function)>(function),
+ std::move(parameters))};
+ }
+ }
+ }
+}
+
+ZPP_BITS_INLINE constexpr auto
+apply(auto && self, auto && function, auto && archive) requires(
+ std::remove_cvref_t<decltype(archive)>::kind() == kind::in)
+{
+ using parameters_type = function_parameters_t<
+ std::remove_cvref_t<decltype(function)>>;
+ using return_type = function_return_type_t<
+ std::remove_cvref_t<decltype(function)>>;
+ if constexpr (std::is_void_v<parameters_type>) {
+ return (std::forward<decltype(self)>(self).*
+ std::forward<decltype(function)>(function))();
+ } else {
+ parameters_type parameters;
+ if constexpr (std::is_void_v<return_type>) {
+ if (auto result = archive(parameters); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+ // Ignore GCC issue.
+#if defined __GNUC__ && !defined __clang__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Warray-bounds"
+#endif
+ std::apply(
+ [&](auto &&... arguments) -> decltype(auto) {
+ return (std::forward<decltype(self)>(self).*
+ std::forward<decltype(function)>(function))(
+ std::forward<decltype(arguments)>(arguments)...);
+ },
+ std::move(parameters));
+#if defined __GNUC__ && !defined __clang__
+#pragma GCC diagnostic pop
+#endif
+ return errc{};
+ } else {
+ if (auto result = archive(parameters); failure(result))
+ [[unlikely]] {
+ return value_or_errc<return_type>{result};
+ }
+ return value_or_errc<return_type>(std::apply(
+ [&](auto &&... arguments) -> decltype(auto) {
+ // Ignore GCC issue.
+#if defined __GNUC__ && !defined __clang__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Warray-bounds"
+#endif
+ return (std::forward<decltype(self)>(self).*
+ std::forward<decltype(function)>(function))(
+ std::forward<decltype(arguments)>(arguments)...);
+#if defined __GNUC__ && !defined __clang__
+#pragma GCC diagnostic pop
+#endif
+ },
+ std::move(parameters)));
+ }
+ }
+}
+
+template <auto Function, auto Id, auto MaxSize = -1>
+struct bind
+{
+ using id = zpp::bits::id<Id, MaxSize>;
+ using function_type = decltype(Function);
+ using parameters_type =
+ typename function_traits<function_type>::parameters_type;
+ using return_type =
+ typename function_traits<function_type>::return_type;
+ static constexpr auto opaque = false;
+
+ ZPP_BITS_INLINE constexpr static decltype(auto) call(auto && archive,
+ auto && context)
+ {
+ if constexpr (std::is_member_function_pointer_v<
+ std::remove_cvref_t<decltype(Function)>>) {
+ return apply(context, Function, archive);
+ } else {
+ return apply(Function, archive);
+ }
+ }
+};
+
+template <auto Function, auto Id, auto MaxSize = -1>
+struct bind_opaque
+{
+ using id = zpp::bits::id<Id, MaxSize>;
+ using function_type = decltype(Function);
+ using parameters_type =
+ typename function_traits<function_type>::parameters_type;
+ using return_type =
+ typename function_traits<function_type>::return_type;
+ static constexpr auto opaque = true;
+
+ ZPP_BITS_INLINE constexpr static decltype(auto) call(auto && in,
+ auto && out,
+ auto && context)
+ {
+ if constexpr (std::is_member_function_pointer_v<
+ std::remove_cvref_t<decltype(Function)>>) {
+ if constexpr (requires { (context.*Function)(in, out); }) {
+ return (context.*Function)(in, out);
+ } else if constexpr (requires { (context.*Function)(in); }) {
+ return (context.*Function)(in);
+ } else if constexpr (requires { (context.*Function)(out); }) {
+ return (context.*Function)(out);
+ } else if constexpr (
+ requires(decltype(in.remaining_data()) & data) {
+ (context.*Function)(data);
+ }) {
+ struct _
+ {
+ decltype(in) archive;
+ decltype(in.remaining_data()) data;
+ constexpr ~_()
+ {
+ archive.position() += data.size();
+ }
+ } _{in, in.remaining_data()};
+ return (context.*Function)(_.data);
+ } else {
+ return (context.*Function)();
+ }
+ } else {
+ if constexpr (requires { Function(in, out); }) {
+ return Function(in, out);
+ } else if constexpr (requires { Function(in); }) {
+ return Function(in);
+ } else if constexpr (requires { Function(out); }) {
+ return Function(out);
+ } else if constexpr (
+ requires(decltype(in.remaining_data()) & data) {
+ Function(data);
+ }) {
+ struct _
+ {
+ decltype(in) archive;
+ decltype(in.remaining_data()) data;
+ constexpr ~_()
+ {
+ archive.position() += data.size();
+ }
+ } _{in, in.remaining_data()};
+ return Function(_.data);
+ } else {
+ return Function();
+ }
+ }
+ }
+};
+
+template <typename... Bindings>
+struct rpc_impl
+{
+ using id = std::remove_cvref_t<
+ decltype(std::remove_cvref_t<decltype(get<0>(
+ std::tuple<Bindings...>{}))>::id::value)>;
+
+ template <typename In, typename Out>
+ struct client
+ {
+ constexpr client(In && in, Out && out) :
+ in(in),
+ out(out)
+ {
+ }
+
+ constexpr client(client && other) = default;
+
+ constexpr ~client()
+ {
+ static_assert(std::remove_cvref_t<decltype(in)>::kind() == kind::in);
+ static_assert(std::remove_cvref_t<decltype(out)>::kind() == kind::out);
+ }
+
+ template <typename Id, typename FirstBinding, typename... OtherBindings>
+ constexpr auto binding()
+ {
+ if constexpr (std::same_as<Id, typename FirstBinding::id>) {
+ return FirstBinding{};
+ } else {
+ static_assert(sizeof...(OtherBindings));
+ return binding<Id, OtherBindings...>();
+ }
+ }
+
+ template <typename Id, std::size_t... Indices>
+ constexpr auto request(std::index_sequence<Indices...>,
+ auto &&... arguments)
+ {
+ using request_binding = decltype(binding<Id, Bindings...>());
+ using parameters_type =
+ typename request_binding::parameters_type;
+
+ if constexpr (std::is_void_v<parameters_type>) {
+ static_assert(!sizeof...(arguments));
+ return out(Id::value);
+ } else if constexpr (request_binding::opaque) {
+ return out(Id::value, arguments...);;
+ } else if constexpr (std::same_as<
+ std::tuple<std::remove_cvref_t<
+ decltype(arguments)>...>,
+ parameters_type>
+
+ ) {
+ return out(Id::value, arguments...);
+ } else {
+ static_assert(requires {
+ {parameters_type{
+ std::forward_as_tuple<decltype(arguments)...>(
+ arguments...)}};
+ });
+
+ return out(
+ Id::value,
+ static_cast<std::conditional_t<
+ std::is_fundamental_v<
+ std::remove_cvref_t<decltype(get<Indices>(
+ std::declval<parameters_type>()))>> ||
+ std::is_enum_v<
+ std::remove_cvref_t<decltype(get<Indices>(
+ std::declval<parameters_type>()))>>,
+ std::remove_cvref_t<decltype(get<Indices>(
+ std::declval<parameters_type>()))>,
+ const decltype(get<Indices>(
+ std::declval<parameters_type>())) &>>(
+ arguments)...);
+ }
+ }
+
+ template <typename Id>
+ constexpr auto request(auto &&... arguments)
+ {
+ return request<Id>(
+ std::make_index_sequence<sizeof...(arguments)>{},
+ arguments...);
+ }
+
+ template <auto Id, auto MaxSize = -1>
+ constexpr auto request(auto &&... arguments)
+ {
+ return request<zpp::bits::id<Id, MaxSize>>(arguments...);
+ }
+
+ template <typename Id, std::size_t... Indices>
+ constexpr auto request_body(std::index_sequence<Indices...>,
+ auto &&... arguments)
+ {
+ using request_binding = decltype(binding<Id, Bindings...>());
+ using parameters_type =
+ typename request_binding::parameters_type;
+
+ if constexpr (std::is_void_v<parameters_type>) {
+ static_assert(!sizeof...(arguments));
+ return;
+ } else if constexpr (request_binding::opaque) {
+ return out(arguments...);;
+ } else if constexpr (std::same_as<
+ std::tuple<std::remove_cvref_t<
+ decltype(arguments)>...>,
+ parameters_type>
+
+ ) {
+ return out(arguments...);
+ } else {
+ static_assert(requires {
+ {parameters_type{
+ std::forward_as_tuple<decltype(arguments)...>(
+ arguments...)}};
+ });
+
+ return out(
+ static_cast<std::conditional_t<
+ std::is_fundamental_v<
+ std::remove_cvref_t<decltype(get<Indices>(
+ std::declval<parameters_type>()))>> ||
+ std::is_enum_v<
+ std::remove_cvref_t<decltype(get<Indices>(
+ std::declval<parameters_type>()))>>,
+ std::remove_cvref_t<decltype(get<Indices>(
+ std::declval<parameters_type>()))>,
+ const decltype(get<Indices>(
+ std::declval<parameters_type>())) &>>(
+ arguments)...);
+ }
+ }
+
+ template <typename Id>
+ constexpr auto request_body(auto &&... arguments)
+ {
+ return request_body<Id>(
+ std::make_index_sequence<sizeof...(arguments)>{},
+ arguments...);
+ }
+
+ template <auto Id, auto MaxSize = -1>
+ constexpr auto request_body(auto &&... arguments)
+ {
+ return request_body<zpp::bits::id<Id, MaxSize>>(arguments...);
+ }
+
+ template <typename Id>
+ constexpr auto response()
+ {
+ using request_binding = decltype(binding<Id, Bindings...>());
+ using return_type = typename request_binding::return_type;
+
+ if constexpr (std::is_void_v<return_type>) {
+ return;
+#if __has_include("zpp_throwing.h")
+ } else if constexpr (requires(return_type && value) {
+ value.await_ready();
+ }) {
+ using nested_return = std::remove_cvref_t<
+ decltype(std::declval<return_type>().await_resume())>;
+ if constexpr (std::is_void_v<nested_return>) {
+ return;
+ } else {
+ nested_return return_value;
+ if (auto result = in(return_value); failure(result))
+ [[unlikely]] {
+ return value_or_errc<nested_return>{result};
+ }
+ return value_or_errc<nested_return>{std::move(return_value)};
+ }
+#endif
+ } else {
+ return_type return_value;
+ if (auto result = in(return_value); failure(result))
+ [[unlikely]] {
+ return value_or_errc<return_type>{result};
+ }
+ return value_or_errc<return_type>{std::move(return_value)};
+ }
+ }
+
+ template <auto Id, auto MaxSize = -1>
+ constexpr auto response()
+ {
+ return response<zpp::bits::id<Id, MaxSize>>();
+ }
+
+ In & in;
+ Out & out;
+ };
+
+#if defined __clang__ || !defined __GNUC__ || __GNUC__ >= 12 // GCC issue
+ template <typename... Types>
+ client(Types && ...) -> client<Types&&...>;
+#endif
+
+ template <typename In, typename Out, typename Context = std::monostate>
+ struct server
+ {
+ constexpr server(In && in, Out && out) :
+ in(in),
+ out(out)
+ {
+ }
+
+ constexpr server(In && in, Out && out, Context && context) :
+ in(in),
+ out(out),
+ context(context)
+ {
+ }
+
+ constexpr server(server && other) = default;
+
+ constexpr ~server()
+ {
+ static_assert(std::remove_cvref_t<decltype(in)>::kind() == kind::in);
+ static_assert(std::remove_cvref_t<decltype(out)>::kind() == kind::out);
+ }
+
+ template <typename FirstBinding, typename... OtherBindings>
+ ZPP_BITS_INLINE constexpr auto
+ call_binding(auto & id) requires(!FirstBinding::opaque)
+ {
+ if (FirstBinding::id::value == id) {
+ if constexpr (std::is_void_v<decltype(FirstBinding::call(
+ in, context))>) {
+ FirstBinding::call(in, context);
+ return errc{};
+ } else if constexpr (std::same_as<
+ decltype(FirstBinding::call(
+ in, context)),
+ errc>) {
+ if (auto result = FirstBinding::call(in, context);
+ failure(result)) [[unlikely]] {
+ return result;
+ }
+ return errc{};
+ } else if constexpr (std::is_void_v<typename FirstBinding::
+ parameters_type>) {
+ return out(FirstBinding::call(in, context));
+ } else {
+ if (auto result = FirstBinding::call(in, context);
+ failure(result)) [[unlikely]] {
+ return result.error();
+ } else {
+ return out(result.value());
+ }
+ }
+ } else {
+ if constexpr (!sizeof...(OtherBindings)) {
+ return errc{std::errc::not_supported};
+ } else {
+ return call_binding<OtherBindings...>(id);
+ }
+ }
+ }
+
+ template <typename FirstBinding, typename... OtherBindings>
+ ZPP_BITS_INLINE constexpr auto
+ call_binding(auto & id) requires FirstBinding::opaque
+ {
+ if (FirstBinding::id::value == id) {
+ if constexpr (std::is_void_v<decltype(FirstBinding::call(
+ in, out, context))>) {
+ FirstBinding::call(in, out, context);
+ return errc{};
+ } else if constexpr (std::same_as<
+ decltype(FirstBinding::call(
+ in, out, context)),
+ errc>) {
+ if (auto result = FirstBinding::call(in, out, context);
+ failure(result)) [[unlikely]] {
+ return result;
+ }
+ return errc{};
+ } else if constexpr (
+ requires {
+ requires std::same_as<
+ typename decltype(FirstBinding::call(
+ in, out, context))::value_type,
+ value_or_errc<decltype(FirstBinding::call(
+ in, out, context))>>;
+ }) {
+ if (auto result = FirstBinding::call(in, out, context);
+ failure(result)) [[unlikely]] {
+ return result.error();
+ } else {
+ return out(result.value());
+ }
+ } else {
+ return out(FirstBinding::call(in, out, context));
+ }
+ } else {
+ if constexpr (!sizeof...(OtherBindings)) {
+ return errc{std::errc::not_supported};
+ } else {
+ return call_binding<OtherBindings...>(id);
+ }
+ }
+ }
+
+#if __has_include("zpp_throwing.h")
+ template <typename FirstBinding, typename... OtherBindings>
+ zpp::throwing<void>
+ call_binding_throwing(auto & id) requires(!FirstBinding::opaque)
+ {
+ if (FirstBinding::id::value == id) {
+ if constexpr (std::is_void_v<decltype(FirstBinding::call(
+ in, context))>) {
+ FirstBinding::call(in, context);
+ co_return;
+ } else if constexpr (std::same_as<
+ decltype(FirstBinding::call(
+ in, context)),
+ errc>) {
+ if (auto result = FirstBinding::call(in, context);
+ failure(result)) [[unlikely]] {
+ co_yield result.code;
+ }
+ co_return;
+ } else if constexpr (std::is_void_v<typename FirstBinding::
+ parameters_type>) {
+ if constexpr (requires {
+ FirstBinding::call(in, context)
+ .await_ready();
+ }) {
+ if constexpr (std::is_void_v<
+ decltype(FirstBinding::call(
+ in, context)
+ .await_resume())>) {
+ co_await FirstBinding::call(in, context);
+ } else {
+ co_await out(
+ co_await FirstBinding::call(in, context));
+ }
+ } else {
+ co_await out(FirstBinding::call(in, context));
+ }
+ } else {
+ if (auto result = FirstBinding::call(in, context);
+ failure(result)) [[unlikely]] {
+ co_yield result.error().code;
+ } else if constexpr (requires {
+ result.value().await_ready();
+ }) {
+ if constexpr (!std::is_void_v<
+ decltype(result.value()
+ .await_resume())>) {
+ co_await out(co_await result.value());
+ }
+ co_return;
+ } else {
+ co_await out(result.value());
+ }
+ }
+ } else {
+ if constexpr (!sizeof...(OtherBindings)) {
+ co_yield std::errc::not_supported;
+ } else {
+ co_return co_await call_binding_throwing<
+ OtherBindings...>(id);
+ }
+ }
+ }
+
+ template <typename FirstBinding, typename... OtherBindings>
+ zpp::throwing<void>
+ call_binding_throwing(auto & id) requires FirstBinding::opaque
+ {
+ if (FirstBinding::id::value == id) {
+ if constexpr (std::is_void_v<decltype(FirstBinding::call(
+ in, out, context))>) {
+ FirstBinding::call(in, out, context);
+ co_return;
+ } else if constexpr (std::same_as<
+ decltype(FirstBinding::call(
+ in, out, context)),
+ errc>) {
+ if (auto result = FirstBinding::call(in, out, context);
+ failure(result)) [[unlikely]] {
+ co_yield result.code;
+ }
+ co_return;
+ } else if constexpr (
+ requires {
+ requires std::same_as<
+ typename decltype(FirstBinding::call(
+ in, out, context))::value_type,
+ value_or_errc<decltype(FirstBinding::call(
+ in, out, context))>>;
+ }) {
+ if (auto result = FirstBinding::call(in, out, context);
+ failure(result)) [[unlikely]] {
+ co_yield result.error().code;
+ } else if constexpr (requires {
+ result.value().await_ready();
+ }) {
+ if constexpr (!std::is_void_v<
+ decltype(result.value()
+ .await_resume())>) {
+ co_await out(co_await result.value());
+ }
+ co_return;
+ } else {
+ co_await out(result.value());
+ }
+ } else {
+ if constexpr (requires {
+ FirstBinding::call(in, out, context)
+ .await_ready();
+ }) {
+ if constexpr (std::is_void_v<
+ decltype(FirstBinding::call(
+ in, out, context)
+ .await_resume())>) {
+ co_await FirstBinding::call(in, out, context);
+ } else {
+ co_await out(
+ co_await FirstBinding::call(in, out, context));
+ }
+ } else {
+ co_await out(FirstBinding::call(in, out, context));
+ }
+ }
+ } else {
+ if constexpr (!sizeof...(OtherBindings)) {
+ co_yield std::errc::not_supported;
+ } else {
+ co_return co_await call_binding_throwing<
+ OtherBindings...>(id);
+ }
+ }
+ }
+#endif
+
+ constexpr auto serve(auto && id)
+ {
+#if __has_include("zpp_throwing.h")
+ if constexpr ((... || requires {
+ std::declval<
+ typename Bindings::return_type>()
+ .await_ready();
+ })) {
+ return call_binding_throwing<Bindings...>(id);
+ } else {
+#endif
+ return call_binding<Bindings...>(id);
+#if __has_include("zpp_throwing.h")
+ }
+#endif
+ }
+
+ constexpr auto serve()
+ {
+ rpc_impl::id id;
+ if (auto result = in(id); failure(result)) [[unlikely]] {
+ return decltype(serve(rpc_impl::id{})){result.code};
+ }
+
+ return serve(id);
+ }
+
+ In & in;
+ Out & out;
+ [[no_unique_address]] Context context;
+ };
+
+#if defined __clang__ || !defined __GNUC__ || __GNUC__ >= 12 // GCC issue
+ template <typename... Types>
+ server(Types && ...) -> server<Types&&...>;
+#endif
+
+#if defined __clang__ || !defined __GNUC__ || __GNUC__ >= 12 // GCC issue
+ constexpr static auto client_server(auto && in, auto && out, auto &&... context)
+ {
+ return std::tuple{client{in, out}, server{in, out, context...}};
+ }
+#else
+ constexpr static auto client_server(auto && in, auto && out)
+ {
+ return std::tuple{client<decltype(in), decltype(out)>{in, out},
+ server<decltype(in), decltype(out)>{in, out}};
+ }
+
+ constexpr static auto client_server(auto && in, auto && out, auto && context)
+ {
+ return std::tuple{
+ client<decltype(in), decltype(out)>{in, out},
+ server<decltype(in), decltype(out), decltype(context)>{
+ in, out, context}};
+ }
+#endif
+};
+
+template <typename... Bindings>
+struct rpc_checker
+{
+ using check_unique_id = traits::variant<
+ std::variant<traits::id_serializable<typename Bindings::id>...>>;
+ using type = rpc_impl<Bindings...>;
+};
+
+template <typename... Bindings>
+using rpc = typename rpc_checker<Bindings...>::type;
+
+struct pb_reserved
+{
+};
+
+template <std::size_t From, std::size_t To>
+struct pb_map
+{
+ static_assert(From != 0 && To != 0);
+
+ constexpr static unsigned int mapped_field(auto index)
+ {
+ return ((index + 1) == From) ? To : 0u;
+ }
+};
+
+template <typename Type, auto FieldNumber>
+struct pb_field_fundamental
+{
+ using value_type = Type;
+ using pb_field_type = Type;
+
+ constexpr static auto pb_field_number = FieldNumber;
+
+ constexpr pb_field_fundamental() = default;
+
+ constexpr pb_field_fundamental(Type value) :
+ value(value)
+ {
+ }
+
+ constexpr operator Type &() &
+ {
+ return value;
+ }
+
+ constexpr operator Type() const
+ {
+ return value;
+ }
+
+ Type value{};
+};
+
+template <typename Type, auto FieldNumber>
+constexpr decltype(auto)
+pb_value(pb_field_fundamental<Type, FieldNumber> & pb)
+{
+ return static_cast<
+ typename pb_field_fundamental<Type, FieldNumber>::pb_field_type &>(
+ pb);
+}
+
+template <typename Type, auto FieldNumber>
+constexpr auto pb_value(const pb_field_fundamental<Type, FieldNumber> & pb)
+{
+ return static_cast<
+ typename pb_field_fundamental<Type, FieldNumber>::pb_field_type>(
+ pb);
+}
+
+template <typename Type, auto FieldNumber>
+struct pb_field_struct : Type
+{
+ using Type::Type;
+ using Type::operator=;
+ using pb_field_type = Type;
+
+ static constexpr auto pb_field_number = FieldNumber;
+
+ constexpr pb_field_struct(Type && other) noexcept(
+ std::is_nothrow_move_constructible_v<Type>) :
+ Type(std::move(other))
+ {
+ }
+
+ constexpr pb_field_struct(const Type & other)
+ : Type(other)
+ {
+ }
+};
+
+template <typename Type, auto FieldNumber>
+constexpr decltype(auto) pb_value(pb_field_struct<Type, FieldNumber> & pb)
+{
+ return static_cast<
+ typename pb_field_struct<Type, FieldNumber>::pb_field_type &>(pb);
+}
+
+template <typename Type, auto FieldNumber>
+constexpr decltype(auto)
+pb_value(const pb_field_struct<Type, FieldNumber> & pb)
+{
+ return static_cast<const typename pb_field_struct<Type, FieldNumber>::
+ pb_field_type &>(pb);
+}
+
+template <typename Type, auto FieldNumber>
+constexpr decltype(auto) pb_value(pb_field_struct<Type, FieldNumber> && pb)
+{
+ return static_cast<
+ typename pb_field_struct<Type, FieldNumber>::pb_field_type &&>(pb);
+}
+
+template <typename Type, auto FieldNumber>
+using pb_field =
+ std::conditional_t<std::is_class_v<Type>,
+ pb_field_struct<Type, FieldNumber>,
+ pb_field_fundamental<Type, FieldNumber>>;
+
+template <typename... Options>
+struct pb
+{
+ using pb_default = pb<>;
+
+ constexpr pb(Options && ...)
+ {
+ }
+
+ template <std::size_t Index>
+ constexpr static auto has_mapped_field(auto option)
+ {
+ return requires
+ {
+ requires decltype(option)::mapped_field(Index) != 0;
+ };
+ }
+
+ template <std::size_t Index>
+ constexpr static auto get_mapped_field(auto option)
+ {
+ if constexpr (requires {
+ requires decltype(option)::mapped_field(Index) != 0;
+ }) {
+ return decltype(option)::mapped_field(Index);
+ } else {
+ return 0u;
+ }
+ }
+
+ template <std::size_t Index>
+ struct field_number_visitor
+ {
+ template <typename... Types>
+ constexpr auto operator()() const
+ {
+ if constexpr (requires {
+ std::remove_cvref_t<decltype(std::get<Index>(
+ std::declval<std::tuple<Types...>>()))>::
+ pb_field_number;
+ }) {
+ static_assert(0 !=
+ std::remove_cvref_t<decltype(std::get<Index>(
+ std::declval<std::tuple<Types...>>()))>::
+ pb_field_number);
+ return std::integral_constant<
+ unsigned int,
+ std::remove_cvref_t<decltype(std::get<Index>(
+ std::declval<std::tuple<Types...>>()))>::
+ pb_field_number>();
+ } else {
+ return std::integral_constant<unsigned int, 0>{};
+ }
+ }
+ };
+
+ template <typename Type, std::size_t Index>
+ constexpr static auto field_number_from_struct()
+ {
+ constexpr auto explicit_field_number =
+ visit_members_types<Type>(field_number_visitor<Index>{})();
+ if constexpr (explicit_field_number > 0) {
+ return explicit_field_number;
+ } else {
+ static_assert(
+ (0 + ... + std::size_t(has_mapped_field<Index>(Options{}))) <=
+ 1);
+
+ constexpr auto mapped_field =
+ (0 + ... + get_mapped_field<Index>(Options{}));
+ if constexpr (mapped_field != 0) {
+ return mapped_field;
+ } else {
+ return Index + 1;
+ }
+ }
+ }
+
+ template <typename Type, std::size_t Index>
+ constexpr static auto field_number()
+ {
+ if constexpr (requires { requires(Type::pb_field_number > 0); }) {
+ return Type::pb_field_number;
+ } else {
+ static_assert(
+ (0 + ... + std::size_t(has_mapped_field<Index>(Options{}))) <=
+ 1);
+
+ constexpr auto mapped_field =
+ (0 + ... + get_mapped_field<Index>(Options{}));
+ if constexpr (mapped_field != 0) {
+ return mapped_field;
+ } else {
+ return Index + 1;
+ }
+ }
+ }
+
+ template <typename Type, std::size_t... Indices>
+ constexpr static auto unique_field_numbers(std::index_sequence<Indices...>)
+ {
+ return traits::unique(
+ std::size_t{field_number_from_struct<Type, Indices>()}...);
+ }
+
+ template <typename Type>
+ constexpr static auto unique_field_numbers()
+ {
+ constexpr auto members =
+ number_of_members<std::remove_cvref_t<Type>>();
+ if constexpr (members >= 0) {
+ return unique_field_numbers<std::remove_cvref_t<Type>>(
+ std::make_index_sequence<members>());
+ } else {
+ static_assert(members >= 0);
+ }
+ }
+
+ template <typename Type>
+ constexpr static auto is_pb_field()
+ {
+ using type = std::remove_cvref_t<Type>;
+ return requires
+ {
+ requires std::same_as<type,
+ pb_field<typename type::pb_field_type,
+ type::pb_field_number>>;
+ };
+ }
+
+ template <typename Type>
+ constexpr static auto check_type()
+ {
+ using type = std::remove_cvref_t<Type>;
+ if constexpr (is_pb_field<type>()) {
+ return check_type<typename type::pb_field_type>();
+ } else if constexpr (!std::is_class_v<type> ||
+ concepts::varint<type> ||
+ concepts::empty<type>) {
+ return true;
+ } else if constexpr (concepts::associative_container<type> &&
+ requires { typename type::mapped_type; }) {
+ static_assert(
+ requires {
+ type{}.push_back(typename type::value_type{});
+ } ||
+ requires { type{}.insert(typename type::value_type{}); });
+ static_assert(check_type<typename type::key_type>());
+ static_assert(check_type<typename type::mapped_type>());
+ return true;
+ } else if constexpr (concepts::container<type>) {
+ static_assert(
+ requires {
+ type{}.push_back(typename type::value_type{});
+ } ||
+ requires { type{}.insert(typename type::value_type{}); });
+ static_assert(check_type<typename type::value_type>());
+ return true;
+ } else if constexpr (concepts::by_protocol<type>) {
+ static_assert(
+ std::same_as<pb_default,
+ typename decltype(access::get_protocol<
+ type>())::pb_default>);
+ static_assert(unique_field_numbers<type>());
+ return true;
+ } else {
+ static_assert(!sizeof(Type));
+ }
+ }
+
+ enum class wire_type : unsigned int
+ {
+ varint = 0,
+ fixed_64 = 1,
+ length_delimited = 2,
+ fixed_32 = 5,
+ };
+
+ constexpr static auto make_tag_explicit(wire_type type, auto field_number)
+ {
+ return varint{(field_number << 3) |
+ std::underlying_type_t<wire_type>(type)};
+ }
+
+ constexpr static auto tag_type(auto tag)
+ {
+ return wire_type(tag & 0x7);
+ }
+
+ constexpr static auto tag_number(auto tag)
+ {
+ return (unsigned int)(tag >> 3);
+ }
+
+ template <typename Type>
+ constexpr static auto tag_type()
+ {
+ using type = std::remove_cvref_t<Type>;
+ if constexpr (is_pb_field<type>()) {
+ return tag_type<typename type::pb_field_type>();
+ } else if constexpr (concepts::varint<type> ||
+ (std::is_enum_v<type> &&
+ !std::same_as<type, std::byte>) ||
+ std::same_as<type, bool>) {
+ return wire_type::varint;
+ } else if constexpr (std::is_integral_v<type> ||
+ std::is_floating_point_v<type>) {
+ if constexpr (sizeof(type) == 4) {
+ return wire_type::fixed_32;
+ } else if constexpr (sizeof(type) == 8) {
+ return wire_type::fixed_64;
+ } else {
+ static_assert(!sizeof(type));
+ }
+ } else {
+ return wire_type::length_delimited;
+ }
+ }
+
+ template <typename Type>
+ constexpr static auto make_tag_explicit(auto field_number)
+ {
+ return make_tag_explicit(tag_type<Type>(), field_number);
+ }
+
+ template <typename Type, auto Index>
+ constexpr static auto make_tag()
+ {
+ return make_tag_explicit(tag_type<Type>(),
+ field_number<Type, Index>());
+ }
+
+ template <wire_type WireType, typename Type, auto Index>
+ constexpr static auto make_tag()
+ {
+ return make_tag_explicit(WireType, field_number<Type, Index>());
+ }
+
+ ZPP_BITS_INLINE constexpr auto
+ operator()(auto & archive, auto & item) const requires(
+ std::remove_cvref_t<decltype(archive)>::kind() == kind::out)
+ {
+ using type = std::remove_cvref_t<decltype(item)>;
+ static_assert(check_type<type>());
+
+ using archive_type = typename std::remove_cvref_t<decltype(archive)>;
+ if constexpr (!concepts::varint<
+ typename archive_type::default_size_type> ||
+ ((std::endian::little != std::endian::native) &&
+ !archive_type::endian_aware)) {
+ out out{archive.data(),
+ size_varint{},
+ no_fit_size{},
+ endian::little{},
+ enlarger<std::get<0>(archive_type::enlarger),
+ std::get<1>(archive_type::enlarger)>{},
+ std::conditional_t<archive_type::no_enlarge_overflow,
+ no_enlarge_overflow,
+ enlarge_overflow>{},
+ alloc_limit<archive_type::allocation_limit>{}};
+ out.position() = archive.position();
+ if constexpr (concepts::self_referencing<type>) {
+ auto result = visit_members(
+ item,
+ [&](auto &&... items) constexpr {
+ static_assert((... && check_type<decltype(items)>()));
+ return serialize_many(
+ std::make_index_sequence<sizeof...(items)>{},
+ out,
+ items...);
+ });
+ archive.position() = out.position();
+ return result;
+ } else {
+ auto result = visit_members(
+ item,
+ [&](auto &&... items) ZPP_BITS_CONSTEXPR_INLINE_LAMBDA {
+ static_assert((... && check_type<decltype(items)>()));
+ return serialize_many(
+ std::make_index_sequence<sizeof...(items)>{},
+ out,
+ items...);
+ });
+ archive.position() = out.position();
+ return result;
+ }
+ } else if constexpr (concepts::self_referencing<type>) {
+ return visit_members(
+ item,
+ [&](auto &&... items) constexpr {
+ static_assert((... && check_type<decltype(items)>()));
+ return serialize_many(
+ std::make_index_sequence<sizeof...(items)>{},
+ archive,
+ items...);
+ });
+ } else {
+ return visit_members(
+ item,
+ [&](auto &&... items) ZPP_BITS_CONSTEXPR_INLINE_LAMBDA {
+ static_assert((... && check_type<decltype(items)>()));
+ return serialize_many(
+ std::make_index_sequence<sizeof...(items)>{},
+ archive,
+ items...);
+ });
+ }
+ }
+
+ template <std::size_t FirstIndex, std::size_t... Indices>
+ ZPP_BITS_INLINE constexpr static auto serialize_many(
+ std::index_sequence<FirstIndex, Indices...>,
+ auto & archive,
+ auto & first_item,
+ auto &... items) requires(std::remove_cvref_t<decltype(archive)>::
+ kind() == kind::out)
+ {
+ if (auto result = serialize_one<FirstIndex>(archive, first_item);
+ failure(result)) [[unlikely]] {
+ return result;
+ }
+
+ return serialize_many(
+ std::index_sequence<Indices...>{}, archive, items...);
+ }
+
+ ZPP_BITS_INLINE constexpr static errc
+ serialize_many(std::index_sequence<>, auto & archive) requires(
+ std::remove_cvref_t<decltype(archive)>::kind() == kind::out)
+ {
+ return {};
+ }
+
+ template <std::size_t Index, typename TagType = void>
+ ZPP_BITS_INLINE constexpr static errc
+ serialize_one(auto & archive, auto & item) requires(
+ std::remove_cvref_t<decltype(archive)>::kind() == kind::out)
+ {
+ using type = std::remove_cvref_t<decltype(item)>;
+ using tag_type = std::conditional_t<std::is_void_v<TagType>, type, TagType>;
+
+ if constexpr (concepts::empty<type>) {
+ return {};
+ } else if constexpr (is_pb_field<type>()) {
+ return serialize_one<Index, tag_type>(
+ archive,
+ static_cast<const typename type::pb_field_type &>(item));
+ } else if constexpr (std::is_enum_v<type> &&
+ !std::same_as<type, std::byte>) {
+ constexpr auto tag = make_tag<tag_type, Index>();
+ if (auto result = archive(
+ tag, varint{std::underlying_type_t<type>(item)});
+ failure(result)) [[unlikely]] {
+ return result;
+ }
+ return {};
+ } else if constexpr (!concepts::container<type>) {
+ constexpr auto tag = make_tag<tag_type, Index>();
+ if (auto result = archive(tag, item); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+ return {};
+ } else if constexpr (concepts::associative_container<type> &&
+ requires { typename type::mapped_type; }) {
+ constexpr auto tag = make_tag<tag_type, Index>();
+
+ using key_type = std::conditional_t<
+ std::is_enum_v<typename type::key_type> &&
+ !std::same_as<typename type::key_type, std::byte>,
+ varint<typename type::key_type>,
+ typename type::key_type>;
+
+ using mapped_type = std::conditional_t<
+ std::is_enum_v<typename type::mapped_type> &&
+ !std::same_as<typename type::mapped_type, std::byte>,
+ varint<typename type::mapped_type>,
+ typename type::mapped_type>;
+
+ struct value_type
+ {
+ const key_type & key;
+ const mapped_type & value;
+
+ using serialize = protocol<pb_default{}>;
+ serialize use();
+ };
+
+ for (auto & [key, value] : item) {
+ if (auto result = archive(
+ tag, value_type{.key = key, .value = value});
+ failure(result)) [[unlikely]] {
+ return result;
+ }
+ }
+
+ return {};
+ } else if constexpr (requires {
+ requires std::is_fundamental_v<
+ typename type::value_type> ||
+ std::same_as<
+ typename type::value_type,
+ std::byte>;
+ }) {
+ constexpr auto tag = make_tag<tag_type, Index>();
+ auto size = item.size();
+ if (!size) [[unlikely]] {
+ return {};
+ }
+ if (auto result = archive(
+ tag,
+ varint{size * sizeof(typename type::value_type)},
+ unsized(item));
+ failure(result)) [[unlikely]] {
+ return result;
+ }
+ return {};
+ } else if constexpr (requires {
+ requires concepts::varint<
+ typename type::value_type>;
+ }) {
+ constexpr auto tag = make_tag<tag_type, Index>();
+
+ std::size_t size = {};
+ for (auto & element : item) {
+ size +=
+ varint_size<type::value_type::encoding>(element.value);
+ }
+ if (!size) [[unlikely]] {
+ return {};
+ }
+ if (auto result = archive(tag, varint{size}, unsized(item));
+ failure(result)) [[unlikely]] {
+ return result;
+ }
+ return {};
+ } else if constexpr (requires {
+ requires std::is_enum_v<
+ typename type::value_type>;
+ }) {
+ constexpr auto tag = make_tag<tag_type, Index>();
+
+ using type = typename type::value_type;
+ std::size_t size = {};
+ for (auto & element : item) {
+ size += varint_size(std::underlying_type_t<type>(element));
+ }
+ if (!size) [[unlikely]] {
+ return {};
+ }
+ if (auto result = archive(tag, varint{size}); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+ for (auto & element : item) {
+ if (auto result = archive(
+ varint{std::underlying_type_t<type>(element)});
+ failure(result)) [[unlikely]] {
+ return result;
+ }
+ }
+ return {};
+ } else {
+ constexpr auto tag =
+ make_tag<typename type::value_type, Index>();
+ for (auto & element : item) {
+ if (auto result = archive(tag, element); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+ }
+ return {};
+ }
+ }
+
+ ZPP_BITS_INLINE constexpr errc operator()(
+ auto & archive,
+ auto & item,
+ std::size_t size = std::numeric_limits<std::size_t>::max()) const
+ requires(std::remove_cvref_t<decltype(archive)>::kind() ==
+ kind::in)
+ {
+ auto data = archive.remaining_data();
+ in in{std::span{data.data(), std::min(size, data.size())},
+ size_varint{},
+ endian::little{},
+ alloc_limit<std::remove_cvref_t<
+ decltype(archive)>::allocation_limit>{}};
+ auto result = deserialize_fields(in, item);
+ archive.position() += in.position();
+ return result;
+ }
+
+ ZPP_BITS_INLINE constexpr static errc
+ deserialize_fields(auto & archive, auto & item)
+ {
+ using type = std::remove_cvref_t<decltype(item)>;
+ static_assert(check_type<type>());
+
+ auto size = archive.data().size();
+ visit_members(
+ item, [](auto &&... members) ZPP_BITS_CONSTEXPR_INLINE_LAMBDA {
+ (
+ [](auto && member) ZPP_BITS_CONSTEXPR_INLINE_LAMBDA {
+ using type = std::remove_cvref_t<decltype(member)>;
+ if constexpr (concepts::container<type> &&
+ !std::is_fundamental_v<type> &&
+ !std::same_as<type, std::byte> &&
+ requires { member.clear(); }) {
+ member.clear();
+ }
+ }(members),
+ ...);
+ });
+
+ while (archive.position() < size) {
+ vuint32_t tag;
+ if (auto result = archive(tag); failure(result)) [[unlikely]] {
+ return result;
+ }
+
+ if (auto result = deserialize_field(
+ archive, item, tag_number(tag), tag_type(tag));
+ failure(result)) [[unlikely]] {
+ return result;
+ }
+ }
+
+ return {};
+ }
+
+ template <std::size_t Index = 0>
+ ZPP_BITS_INLINE constexpr static auto
+ deserialize_field(auto & archive,
+ auto && item,
+ auto field_num,
+ wire_type field_type)
+ {
+ using type = std::remove_reference_t<decltype(item)>;
+ if constexpr (Index >= number_of_members<type>()) {
+ if (!field_num) [[unlikely]] {
+ return errc{std::errc::protocol_error};
+ }
+ return errc{};
+ } else if (field_number_from_struct<type, Index>() != field_num) {
+ return deserialize_field<Index + 1>(
+ archive, item, field_num, field_type);
+ } else if constexpr (concepts::self_referencing<type>) {
+ return visit_members(
+ item,
+ [&](auto &&... items) constexpr {
+ std::tuple<decltype(items) &...> refs = {items...};
+ auto & item = std::get<Index>(refs);
+ using type = std::remove_reference_t<decltype(item)>;
+ static_assert(check_type<type>());
+
+ return deserialize_field(archive, field_type, item);
+ });
+ } else {
+ return visit_members(
+ item,
+ [&](auto &&... items) ZPP_BITS_CONSTEXPR_INLINE_LAMBDA {
+ std::tuple<decltype(items) &...> refs = {items...};
+ auto & item = std::get<Index>(refs);
+ using type = std::remove_reference_t<decltype(item)>;
+ static_assert(check_type<type>());
+
+ return deserialize_field(archive, field_type, item);
+ });
+ }
+ }
+
+ ZPP_BITS_INLINE constexpr static auto deserialize_field(
+ auto & archive, wire_type field_type, auto & item)
+ {
+ using type = std::remove_reference_t<decltype(item)>;
+ using archive_type = std::remove_reference_t<decltype(archive)>;
+ static_assert(check_type<type>());
+
+ if constexpr (std::is_enum_v<type>) {
+ varint<type> value;
+ if (auto result = archive(value); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+ item = value;
+ return errc{};
+ } else if constexpr (is_pb_field<type>()) {
+ return deserialize_field(
+ archive,
+ field_type,
+ static_cast<typename type::pb_field_type &>(item));
+ } else if constexpr (!concepts::container<type>) {
+ return archive(item);
+ } else if constexpr (concepts::associative_container<type> &&
+ requires { typename type::mapped_type; }) {
+ using key_type = std::conditional_t<
+ std::is_enum_v<typename type::key_type> &&
+ !std::same_as<typename type::key_type, std::byte>,
+ varint<typename type::key_type>,
+ typename type::key_type>;
+
+ using mapped_type = std::conditional_t<
+ std::is_enum_v<typename type::mapped_type> &&
+ !std::same_as<typename type::mapped_type, std::byte>,
+ varint<typename type::mapped_type>,
+ typename type::mapped_type>;
+
+ struct value_type
+ {
+ key_type key;
+ mapped_type value;
+
+ using serialize = protocol<pb_default{}>;
+ serialize use();
+ };
+
+ std::aligned_storage_t<sizeof(value_type),
+ alignof(value_type)>
+ storage;
+
+ auto object =
+ access::placement_new<value_type>(std::addressof(storage));
+ destructor_guard guard{*object};
+ if (auto result = archive(*object); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+
+ item.emplace(std::move(object->key), std::move(object->value));
+ return errc{};
+ } else {
+ using orig_value_type = typename type::value_type;
+ using value_type = std::conditional_t<
+ std::is_enum_v<orig_value_type> &&
+ !std::same_as<orig_value_type, std::byte>,
+ varint<orig_value_type>,
+ orig_value_type>;
+
+ if constexpr (std::is_fundamental_v<value_type> ||
+ std::same_as<std::byte, value_type> ||
+ concepts::varint<value_type>) {
+ auto fetch = [&]() ZPP_BITS_CONSTEXPR_INLINE_LAMBDA {
+ value_type value;
+ if (auto result = archive(value); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+
+ if constexpr (requires {
+ item.push_back(
+ orig_value_type(value));
+ }) {
+ item.push_back(orig_value_type(value));
+ } else {
+ item.insert(orig_value_type(value));
+ }
+
+ return errc{};
+ };
+ if (field_type != wire_type::length_delimited)
+ [[unlikely]] {
+ return fetch();
+ }
+ vsize_t length;
+ if (auto result = archive(length); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+
+ if constexpr (requires { item.resize(1); } &&
+ (std::is_fundamental_v<value_type> ||
+ std::same_as<value_type, std::byte>)) {
+ if constexpr (archive_type::allocation_limit !=
+ std::numeric_limits<
+ std::size_t>::max()) {
+ if (length > archive_type::allocation_limit)
+ [[unlikely]] {
+ return errc{std::errc::message_size};
+ }
+ }
+ item.resize(length / sizeof(value_type));
+ return archive(unsized(item));
+ } else {
+ if constexpr (requires { item.reserve(1); }) {
+ item.reserve(length);
+ }
+
+ auto end_position = length + archive.position();
+ while (archive.position() < end_position) {
+ if (auto result = fetch(); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+ }
+
+ return errc{};
+ }
+ } else {
+ std::aligned_storage_t<sizeof(value_type),
+ alignof(value_type)>
+ storage;
+
+ auto object = access::placement_new<value_type>(
+ std::addressof(storage));
+ destructor_guard guard{*object};
+ if (auto result = archive(*object); failure(result))
+ [[unlikely]] {
+ return result;
+ }
+
+ if constexpr (requires {
+ item.push_back(std::move(*object));
+ }) {
+ item.push_back(std::move(*object));
+ } else {
+ item.insert(std::move(*object));
+ }
+
+ return errc{};
+ }
+ }
+ }
+};
+
+using pb_protocol = protocol<pb{}>;
+
+template <std::size_t Members = std::numeric_limits<std::size_t>::max()>
+using pb_members = protocol<pb{}, Members>;
+
+namespace numbers
+{
+template <typename Type>
+struct big_endian
+{
+ struct emplace{};
+
+ constexpr big_endian() = default;
+
+ constexpr explicit big_endian(Type value, emplace) : value(value)
+ {
+ }
+
+ constexpr explicit big_endian(Type value)
+ {
+ std::array<std::byte, sizeof(value)> data;
+ for (std::size_t i = 0; i < sizeof(value); ++i) {
+ data[sizeof(value) - 1 - i] = std::byte(value & 0xff);
+ value >>= CHAR_BIT;
+ }
+
+ this->value = std::bit_cast<Type>(data);
+ }
+
+ constexpr auto operator<<(auto value) const
+ {
+ auto data =
+ std::bit_cast<std::array<unsigned char, sizeof(*this)>>(*this);
+
+ for (std::size_t i = 0; i < sizeof(Type); ++i) {
+ auto offset = (value % CHAR_BIT);
+ auto current = i + (value / CHAR_BIT);
+
+ if (current >= sizeof(Type)) {
+ data[i] = 0;
+ continue;
+ }
+
+ data[i] = data[current] << offset;
+ if (current == sizeof(Type) - 1) {
+ continue;
+ }
+
+ offset = CHAR_BIT - offset;
+ if (offset >= 0) {
+ data[i] |= data[current + 1] >> offset;
+ } else {
+ data[i] |= data[current + 1] << (-offset);
+ }
+ }
+
+ return std::bit_cast<big_endian>(data);
+ }
+
+ constexpr auto operator>>(auto value) const
+ {
+ auto data =
+ std::bit_cast<std::array<unsigned char, sizeof(*this)>>(*this);
+
+ for (std::size_t j = 0; j < sizeof(Type); ++j) {
+ auto i = sizeof(Type) - 1 - j;
+ auto offset = (value % CHAR_BIT);
+ auto current = i - (value / CHAR_BIT);
+
+ if (current >= sizeof(Type)) {
+ data[i] = 0;
+ continue;
+ }
+
+ data[i] = data[current] >> offset;
+ if (!current) {
+ continue;
+ }
+
+ offset = CHAR_BIT - offset;
+ if (offset >= 0) {
+ data[i] |= data[current - 1] << offset;
+ } else {
+ data[i] |= data[current - 1] >> (-offset);
+ }
+ }
+
+ return std::bit_cast<big_endian>(data);
+ }
+
+ constexpr auto friend operator+(big_endian left, big_endian right)
+ {
+ auto left_data = std::bit_cast<std::array<unsigned char, sizeof(left)>>(left);
+ auto right_data = std::bit_cast<std::array<unsigned char, sizeof(right)>>(right);
+ unsigned char remaining{};
+
+ for (std::size_t i = 0; i < sizeof(Type); ++i) {
+ auto current = sizeof(Type) - 1 - i;
+ std::uint16_t byte_addition =
+ std::uint16_t(left_data[current]) +
+ std::uint16_t(right_data[current]) + remaining;
+ left_data[current] = std::uint8_t(byte_addition & 0xff);
+ remaining = std::uint8_t((byte_addition >> CHAR_BIT) & 0xff);
+ }
+
+ return std::bit_cast<big_endian>(left_data);
+ }
+
+ constexpr big_endian operator~() const
+ {
+ return big_endian{~value, emplace{}};
+ }
+
+ constexpr auto & operator+=(big_endian other)
+ {
+ *this = (*this) + other;
+ return *this;
+ }
+
+ constexpr auto friend operator&(big_endian left, big_endian right)
+ {
+ return big_endian{left.value & right.value, emplace{}};
+ }
+
+ constexpr auto friend operator^(big_endian left, big_endian right)
+ {
+ return big_endian{left.value ^ right.value, emplace{}};
+ }
+
+ constexpr auto friend operator|(big_endian left, big_endian right)
+ {
+ return big_endian{left.value | right.value, emplace{}};
+ }
+
+ constexpr auto friend operator<=>(big_endian left,
+ big_endian right) = default;
+
+ using serialize = members<1>;
+
+ Type value{};
+};
+} // namespace numbers
+
+template <auto Object, typename Digest = std::array<std::byte, 20>>
+requires requires
+{
+ requires success(in{Digest{}}(std::array<std::byte, 20>{}));
+}
+constexpr auto sha1()
+{
+ using numbers::big_endian;
+ auto rotate_left = [](auto n, auto c) {
+ return (n << c) | (n >> ((sizeof(n) * CHAR_BIT) - c));
+ };
+ auto align = [](auto v, auto a) { return (v + (a - 1)) / a * a; };
+
+ auto h0 = big_endian{std::uint32_t{0x67452301u}};
+ auto h1 = big_endian{std::uint32_t{0xefcdab89u}};
+ auto h2 = big_endian{std::uint32_t{0x98badcfeu}};
+ auto h3 = big_endian{std::uint32_t{0x10325476u}};
+ auto h4 = big_endian{std::uint32_t{0xc3d2e1f0u}};
+
+ constexpr auto original_message = to_bytes<Object>();
+ constexpr auto chunk_size = 512 / CHAR_BIT;
+ constexpr auto message = to_bytes<
+ original_message,
+ std::byte{0x80},
+ std::array<std::byte,
+ align(original_message.size() + sizeof(std::byte{0x80}),
+ chunk_size) -
+ original_message.size() - sizeof(std::byte{0x80}) -
+ sizeof(std::uint64_t{original_message.size()})>{},
+ big_endian<std::uint64_t>{original_message.size() * CHAR_BIT}>();
+
+ for (auto chunk :
+ from_bytes<message,
+ std::array<std::array<big_endian<std::uint32_t>, 16>,
+ message.size() / chunk_size>>()) {
+ std::array<big_endian<std::uint32_t>, 80> w;
+ std::copy(std::begin(chunk), std::end(chunk), std::begin(w));
+
+ for (std::size_t i = 16; i < w.size(); ++i) {
+ w[i] = rotate_left(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16],
+ 1);
+ }
+
+ auto a = h0;
+ auto b = h1;
+ auto c = h2;
+ auto d = h3;
+ auto e = h4;
+
+ for (std::size_t i = 0; i < w.size(); ++i) {
+ auto f = big_endian{std::uint32_t{}};
+ auto k = big_endian{std::uint32_t{}};
+ if (i <= 19) {
+ f = (b & c) | ((~b) & d);
+ k = big_endian{std::uint32_t{0x5a827999u}};
+ } else if (i <= 39) {
+ f = b ^ c ^ d;
+ k = big_endian{std::uint32_t{0x6ed9eba1u}};
+ } else if (i <= 59) {
+ f = (b & c) | (b & d) | (c & d);
+ k = big_endian{std::uint32_t{0x8f1bbcdcu}};
+ } else {
+ f = b ^ c ^ d;
+ k = big_endian{std::uint32_t{0xca62c1d6u}};
+ }
+
+ auto temp = rotate_left(a, 5) + f + e + k + w[i];
+ e = d;
+ d = c;
+ c = rotate_left(b, 30);
+ b = a;
+ a = temp;
+ }
+
+ h0 += a;
+ h1 += b;
+ h2 += c;
+ h3 += d;
+ h4 += e;
+ }
+
+ std::array<std::byte, 20> digest_data;
+ out{digest_data}(h0, h1, h2, h3, h4).or_throw();
+
+ Digest digest;
+ in{digest_data}(digest).or_throw();
+ return digest;
+}
+
+template <auto Object, typename Digest = std::array<std::byte, 32>>
+requires requires
+{
+ requires success(in{Digest{}}(std::array<std::byte, 32>{}));
+}
+constexpr auto sha256()
+{
+ using numbers::big_endian;
+ auto rotate_right = [](auto n, auto c) {
+ return (n >> c) | (n << ((sizeof(n) * CHAR_BIT) - c));
+ };
+ auto align = [](auto v, auto a) { return (v + (a - 1)) / a * a; };
+
+ auto h0 = big_endian{0x6a09e667u};
+ auto h1 = big_endian{0xbb67ae85u};
+ auto h2 = big_endian{0x3c6ef372u};
+ auto h3 = big_endian{0xa54ff53au};
+ auto h4 = big_endian{0x510e527fu};
+ auto h5 = big_endian{0x9b05688cu};
+ auto h6 = big_endian{0x1f83d9abu};
+ auto h7 = big_endian{0x5be0cd19u};
+
+ std::array k{big_endian{0x428a2f98u}, big_endian{0x71374491u},
+ big_endian{0xb5c0fbcfu}, big_endian{0xe9b5dba5u},
+ big_endian{0x3956c25bu}, big_endian{0x59f111f1u},
+ big_endian{0x923f82a4u}, big_endian{0xab1c5ed5u},
+ big_endian{0xd807aa98u}, big_endian{0x12835b01u},
+ big_endian{0x243185beu}, big_endian{0x550c7dc3u},
+ big_endian{0x72be5d74u}, big_endian{0x80deb1feu},
+ big_endian{0x9bdc06a7u}, big_endian{0xc19bf174u},
+ big_endian{0xe49b69c1u}, big_endian{0xefbe4786u},
+ big_endian{0x0fc19dc6u}, big_endian{0x240ca1ccu},
+ big_endian{0x2de92c6fu}, big_endian{0x4a7484aau},
+ big_endian{0x5cb0a9dcu}, big_endian{0x76f988dau},
+ big_endian{0x983e5152u}, big_endian{0xa831c66du},
+ big_endian{0xb00327c8u}, big_endian{0xbf597fc7u},
+ big_endian{0xc6e00bf3u}, big_endian{0xd5a79147u},
+ big_endian{0x06ca6351u}, big_endian{0x14292967u},
+ big_endian{0x27b70a85u}, big_endian{0x2e1b2138u},
+ big_endian{0x4d2c6dfcu}, big_endian{0x53380d13u},
+ big_endian{0x650a7354u}, big_endian{0x766a0abbu},
+ big_endian{0x81c2c92eu}, big_endian{0x92722c85u},
+ big_endian{0xa2bfe8a1u}, big_endian{0xa81a664bu},
+ big_endian{0xc24b8b70u}, big_endian{0xc76c51a3u},
+ big_endian{0xd192e819u}, big_endian{0xd6990624u},
+ big_endian{0xf40e3585u}, big_endian{0x106aa070u},
+ big_endian{0x19a4c116u}, big_endian{0x1e376c08u},
+ big_endian{0x2748774cu}, big_endian{0x34b0bcb5u},
+ big_endian{0x391c0cb3u}, big_endian{0x4ed8aa4au},
+ big_endian{0x5b9cca4fu}, big_endian{0x682e6ff3u},
+ big_endian{0x748f82eeu}, big_endian{0x78a5636fu},
+ big_endian{0x84c87814u}, big_endian{0x8cc70208u},
+ big_endian{0x90befffau}, big_endian{0xa4506cebu},
+ big_endian{0xbef9a3f7u}, big_endian{0xc67178f2u}};
+
+ constexpr auto original_message = to_bytes<Object>();
+ constexpr auto chunk_size = 512 / CHAR_BIT;
+ constexpr auto message = to_bytes<
+ original_message,
+ std::byte{0x80},
+ std::array<std::byte,
+ align(original_message.size() + sizeof(std::byte{0x80}),
+ chunk_size) -
+ original_message.size() - sizeof(std::byte{0x80}) -
+ sizeof(std::uint64_t{original_message.size()})>{},
+ big_endian<std::uint64_t>{original_message.size() * CHAR_BIT}>();
+
+ for (auto chunk :
+ from_bytes<message,
+ std::array<std::array<big_endian<std::uint32_t>, 16>,
+ message.size() / chunk_size>>()) {
+ std::array<big_endian<std::uint32_t>, 64> w;
+ std::copy(std::begin(chunk), std::end(chunk), std::begin(w));
+
+ for (std::size_t i = 16; i < w.size(); ++i) {
+ auto s0 = rotate_right(w[i - 15], 7) ^
+ rotate_right(w[i - 15], 18) ^ (w[i - 15] >> 3);
+ auto s1 = rotate_right(w[i - 2], 17) ^
+ rotate_right(w[i - 2], 19) ^ (w[i - 2] >> 10);
+ w[i] = w[i - 16] + s0 + w[i - 7] + s1;
+ }
+
+ auto a = h0;
+ auto b = h1;
+ auto c = h2;
+ auto d = h3;
+ auto e = h4;
+ auto f = h5;
+ auto g = h6;
+ auto h = h7;
+
+ for (std::size_t i = 0; i < w.size(); ++i) {
+ auto s1 = rotate_right(e, 6) ^ rotate_right(e, 11) ^
+ rotate_right(e, 25);
+ auto ch = (e & f) ^ ((~e) & g);
+ auto temp1 = h + s1 + ch + k[i] + w[i];
+ auto s0 = rotate_right(a, 2) ^ rotate_right(a, 13) ^
+ rotate_right(a, 22);
+ auto maj = (a & b) ^ (a & c) ^ (b & c);
+ auto temp2 = s0 + maj;
+
+ h = g;
+ g = f;
+ f = e;
+ e = d + temp1;
+ d = c;
+ c = b;
+ b = a;
+ a = temp1 + temp2;
+ }
+
+ h0 = h0 + a;
+ h1 = h1 + b;
+ h2 = h2 + c;
+ h3 = h3 + d;
+ h4 = h4 + e;
+ h5 = h5 + f;
+ h6 = h6 + g;
+ h7 = h7 + h;
+ }
+
+ std::array<std::byte, 32> digest_data;
+ out{digest_data}(h0, h1, h2, h3, h4, h5, h6, h7).or_throw();
+
+ Digest digest;
+ in{digest_data}(digest).or_throw();
+ return digest;
+}
+
+inline namespace literals
+{
+inline namespace string_literals
+{
+template <string_literal String>
+constexpr auto operator""_s()
+{
+ return String;
+}
+
+template <string_literal String>
+constexpr auto operator""_b()
+{
+ return to_bytes<String>();
+}
+
+template <string_literal String>
+constexpr auto operator""_decode_hex()
+{
+ constexpr auto tolower = [](auto c) {
+ if ('A' <= c && c <= 'Z') {
+ return decltype(c)(c - 'A' + 'a');
+ }
+ return c;
+ };
+
+ static_assert(String.size() % 2 == 0);
+
+ static_assert(
+ std::find_if(std::begin(String), std::end(String), [&](auto c) {
+ return !(('0' <= c && c <= '9') ||
+ ('a' <= tolower(c) && tolower(c) <= 'f'));
+ }) == std::end(String));
+
+ auto hex = [](auto c) {
+ if ('a' <= c) {
+ return c - 'a' + 0xa;
+ } else {
+ return c - '0';
+ }
+ };
+
+ std::array<std::byte, String.size() / 2> data;
+ for (std::size_t i = 0; auto & b : data) {
+ auto left = tolower(String[i]);
+ auto right = tolower(String[i + 1]);
+ b = std::byte((hex(left) << (CHAR_BIT/2)) | hex(right));
+ i += 2;
+ }
+ return data;
+}
+
+template <string_literal String>
+constexpr auto operator""_sha1()
+{
+ return sha1<String>();
+}
+
+template <string_literal String>
+constexpr auto operator""_sha256()
+{
+ return sha256<String>();
+}
+
+template <string_literal String>
+constexpr auto operator""_sha1_int()
+{
+ return id_v<sha1<String>(), sizeof(int)>;
+}
+
+template <string_literal String>
+constexpr auto operator""_sha256_int()
+{
+ return id_v<sha256<String>(), sizeof(int)>;
+}
+} // namespace string_literals
+} // namespace literals
+
+template <typename... Arguments>
+using vector1b = sized_t<std::vector<Arguments...>, unsigned char>;
+template <typename... Arguments>
+using vector2b = sized_t<std::vector<Arguments...>, std::uint16_t>;
+template <typename... Arguments>
+using vector4b = sized_t<std::vector<Arguments...>, std::uint32_t>;
+template <typename... Arguments>
+using vector8b = sized_t<std::vector<Arguments...>, std::uint64_t>;
+template <typename... Arguments>
+using static_vector = unsized_t<std::vector<Arguments...>>;
+template <typename... Arguments>
+using native_vector = sized_t<std::vector<Arguments...>, typename std::vector<Arguments...>::size_type>;
+
+template <typename... Arguments>
+using span1b = sized_t<std::span<Arguments...>, unsigned char>;
+template <typename... Arguments>
+using span2b = sized_t<std::span<Arguments...>, std::uint16_t>;
+template <typename... Arguments>
+using span4b = sized_t<std::span<Arguments...>, std::uint32_t>;
+template <typename... Arguments>
+using span8b = sized_t<std::span<Arguments...>, std::uint64_t>;
+template <typename... Arguments>
+using static_span = unsized_t<std::span<Arguments...>>;
+template <typename... Arguments>
+using native_span = sized_t<std::span<Arguments...>, typename std::span<Arguments...>::size_type>;
+
+using string1b = sized_t<std::string, unsigned char>;
+using string2b = sized_t<std::string, std::uint16_t>;
+using string4b = sized_t<std::string, std::uint32_t>;
+using string8b = sized_t<std::string, std::uint64_t>;
+using static_string = unsized_t<std::string>;
+using native_string = sized_t<std::string, std::string::size_type>;
+
+using string_view1b = sized_t<std::string_view, unsigned char>;
+using string_view2b = sized_t<std::string_view, std::uint16_t>;
+using string_view4b = sized_t<std::string_view, std::uint32_t>;
+using string_view8b = sized_t<std::string_view, std::uint64_t>;
+using static_string_view = unsized_t<std::string_view>;
+using native_string_view = sized_t<std::string_view, std::string_view::size_type>;
+
+using wstring1b = sized_t<std::wstring, unsigned char>;
+using wstring2b = sized_t<std::wstring, std::uint16_t>;
+using wstring4b = sized_t<std::wstring, std::uint32_t>;
+using wstring8b = sized_t<std::wstring, std::uint64_t>;
+using static_wstring = unsized_t<std::wstring>;
+using native_wstring = sized_t<std::wstring, std::wstring::size_type>;
+
+using wstring_view1b = sized_t<std::wstring_view, unsigned char>;
+using wstring_view2b = sized_t<std::wstring_view, std::uint16_t>;
+using wstring_view4b = sized_t<std::wstring_view, std::uint32_t>;
+using wstring_view8b = sized_t<std::wstring_view, std::uint64_t>;
+using static_wstring_view = unsized_t<std::wstring_view>;
+using native_wstring_view = sized_t<std::wstring_view, std::wstring_view::size_type>;
+
+using u8string1b = sized_t<std::u8string, unsigned char>;
+using u8string2b = sized_t<std::u8string, std::uint16_t>;
+using u8string4b = sized_t<std::u8string, std::uint32_t>;
+using u8string8b = sized_t<std::u8string, std::uint64_t>;
+using static_u8string = unsized_t<std::u8string>;
+using native_u8string = sized_t<std::u8string, std::u8string::size_type>;
+
+using u8string_view1b = sized_t<std::u8string_view, unsigned char>;
+using u8string_view2b = sized_t<std::u8string_view, std::uint16_t>;
+using u8string_view4b = sized_t<std::u8string_view, std::uint32_t>;
+using u8string_view8b = sized_t<std::u8string_view, std::uint64_t>;
+using static_u8string_view = unsized_t<std::u8string_view>;
+using native_u8string_view = sized_t<std::u8string_view, std::u8string_view::size_type>;
+
+using u16string1b = sized_t<std::u16string, unsigned char>;
+using u16string2b = sized_t<std::u16string, std::uint16_t>;
+using u16string4b = sized_t<std::u16string, std::uint32_t>;
+using u16string8b = sized_t<std::u16string, std::uint64_t>;
+using static_u16string = unsized_t<std::u16string>;
+using native_u16string = sized_t<std::u16string, std::u16string::size_type>;
+
+using u16string_view1b = sized_t<std::u16string_view, unsigned char>;
+using u16string_view2b = sized_t<std::u16string_view, std::uint16_t>;
+using u16string_view4b = sized_t<std::u16string_view, std::uint32_t>;
+using u16string_view8b = sized_t<std::u16string_view, std::uint64_t>;
+using static_u16string_view = unsized_t<std::u16string_view>;
+using native_u16string_view = sized_t<std::u16string_view, std::u16string_view::size_type>;
+
+using u32string1b = sized_t<std::u32string, unsigned char>;
+using u32string2b = sized_t<std::u32string, std::uint16_t>;
+using u32string4b = sized_t<std::u32string, std::uint32_t>;
+using u32string8b = sized_t<std::u32string, std::uint64_t>;
+using static_u32string = unsized_t<std::u32string>;
+using native_u32string = sized_t<std::u32string, std::u32string::size_type>;
+
+using u32string_view1b = sized_t<std::u32string_view, unsigned char>;
+using u32string_view2b = sized_t<std::u32string_view, std::uint16_t>;
+using u32string_view4b = sized_t<std::u32string_view, std::uint32_t>;
+using u32string_view8b = sized_t<std::u32string_view, std::uint64_t>;
+using static_u32string_view = unsized_t<std::u32string_view>;
+using native_u32string_view = sized_t<std::u32string_view, std::u32string_view::size_type>;
+
+} // namespace zpp::bits
+
+#endif // ZPP_BITS_H
+