Files
scylladb/tuple.hh
2015-02-04 10:28:51 +01:00

156 lines
4.8 KiB
C++

#pragma once
#include "types.hh"
#include <iostream>
template<typename T>
static inline
void write(std::ostream& out, T val) {
auto n_val = net::ntoh(val);
out.write(reinterpret_cast<char*>(&n_val), sizeof(n_val));
}
// TODO: Add AllowsMissing parameter which will allow to optimize serialized format.
// Currently we default to AllowsMissing = true.
template<bool AllowPrefixes = false>
class tuple_type final : public abstract_type {
private:
const std::vector<shared_ptr<abstract_type>> types;
const bool _byte_order_equal;
public:
using value_type = std::vector<bytes_opt>;
tuple_type(std::vector<shared_ptr<abstract_type>> types)
: abstract_type("tuple") // FIXME: append names of member types
, types(types)
, _byte_order_equal(std::all_of(types.begin(), types.end(), [] (auto t) {
return t->is_byte_order_equal();
}))
{ }
/*
* Format:
* <len(value1)><value1><len(value2)><value2>...
*
* if value is missing then len(value) < 0
*/
void serialize_value(const value_type& values, std::ostream& out) {
if (AllowPrefixes) {
assert(values.size() <= types.size());
} else {
assert(values.size() == types.size());
}
for (auto&& val : values) {
if (!val) {
write<uint32_t>(out, uint32_t(-1));
} else {
assert(val->size() <= std::numeric_limits<int32_t>::max());
write<uint32_t>(out, uint32_t(val->size()));
out.write(val->begin(), val->size());
}
}
}
value_type deserialize_value(std::istream& in) {
std::vector<bytes_opt> result;
result.reserve(types.size());
for (auto&& type : types) {
uint32_t u;
auto n = in.rdbuf()->sgetn(reinterpret_cast<char *>(&u), sizeof(u));
if (!n) {
if (AllowPrefixes) {
return result;
} else {
throw marshal_exception();
}
}
if (n != sizeof(u)) {
throw marshal_exception();
}
auto len = int32_t(net::ntoh(u));
if (len < 0) {
result.emplace_back();
} else {
result.emplace_back(bytes(bytes::initialized_later(), len));
auto& b = *result.back();
auto n = in.rdbuf()->sgetn(b.begin(), len);
if (n != len) {
throw marshal_exception();
}
}
}
return result;
}
object_opt deserialize(std::istream& in) override {
return {boost::any(deserialize_value(in))};
}
void serialize(const boost::any& obj, std::ostream& out) override {
serialize_value(boost::any_cast<const value_type&>(obj), out);
}
virtual bool less(const bytes& b1, const bytes& b2) override {
return compare(b1, b2) < 0;
}
virtual size_t hash(const bytes& v) override {
if (_byte_order_equal) {
return std::hash<bytes>()(v);
}
size_t h = 0;
auto current_type = types.begin();
for (auto&& elem : ::deserialize_value(*this, v)) {
if (elem) {
h ^= (*current_type)->hash(*elem);
}
++current_type;
}
return h;
}
virtual int32_t compare(const bytes& b1, const bytes& b2) override {
if (is_byte_order_comparable()) {
return compare_unsigned(b1, b2);
}
auto v1 = ::deserialize_value(*this, b1);
auto v2 = ::deserialize_value(*this, b2);
if (v1.size() != v2.size()) {
return v1.size() < v2.size() ? -1 : 1;
}
if (AllowPrefixes) {
assert(v1.size() <= types.size());
} else {
assert(v1.size() == types.size());
}
auto i1 = v1.begin();
auto i2 = v2.begin();
auto current_type = types.begin();
while (i1 != v1.end()) {
bytes_opt& e1 = *i1++;
bytes_opt& e2 = *i2++;
if (bool(e1) != bool(e2)) {
return e2 ? -1 : 1;
}
auto c = (*current_type++)->compare(*e1, *e2);
if (c != 0) {
return c;
}
}
return 0;
}
virtual bool is_byte_order_equal() const override {
return _byte_order_equal;
}
virtual bool is_byte_order_comparable() const override {
// We're not byte order comparable because we encode component length as signed integer,
// which is not byte order comparable.
// TODO: make the length byte-order comparable by adding numeric_limits<int32_t>::min() when serializing
return false;
}
};
using tuple_prefix = tuple_type<true>;