Merge "Serializer Deserializer code generation" from Amnon

"The series do the following:
It adds the code generation
Perform the needed changes in the current classes so each would have getter for
each of its serializable value and a constructor from the serialized values.
It adds a schema definition that cover gossip_diget_ack
It changes the messaging_service to use the generated code.

An overall explanation of the solution with a description of the schema IDL can
be found on the wiki page:

https://github.com/scylladb/scylla/wiki/Serializer-Deserializer-Code-generation
"
This commit is contained in:
Avi Kivity
2016-01-24 12:56:42 +02:00
12 changed files with 857 additions and 9 deletions

103
IDL.md Normal file
View File

@@ -0,0 +1,103 @@
#IDL definition
The schema we use similar to c++ schema.
Use class or struct similar to the object you need the serializer for.
Use namespace when applicable.
##keywords
* class/struct - a class or a struct like C++
class/struct can have final or stub marker
* namespace - has the same C++ meaning
* enum class - has the same C++ meaning
* final modifier for class - when a class mark as final it will not contain a size parameter. Note that final class cannot be extended by future version, so use with care
* stub class - when a class is mark as stub, it means that no code will be generated for this class and it is only there as a documentation.
* version attributes - mark with [[version id ]] mark that a field is available from a specific version
* template - A template class definition like C++
##Syntax
###Namespace
```
namespace ns_name { namespace-body }
```
* ns_name: either a previously unused identifier, in which case this is original-namespace-definition or the name of a namespace, in which case this is extension-namespace-definition
* namespace-body: possibly empty sequence of declarations of any kind (including class and struct definitions as well as nested namespaces)
###class/struct
`
class-key class-name final(optional) stub(optional) { member-specification } ;(optional)
`
* class-key: one of class or struct.
* class-name: the name of the class that's being defined. optionally followed by keyword final, optionally followed by keyword stub
* final: when a class mark as final, it means it can not be extended and there is no need to serialize its size, use with care.
* stub: when a class is mark as stub, it means no code will generate for it and it is added for documentation only.
* member-specification: list of access specifiers, and public member accessor see class member below.
* to be compatible with C++ a class definition can be followed by a semicolon.
###enum
`enum-key identifier enum-base { enumerator-list(optional) }`
* enum-key: only enum class is supported
* identifier: the name of the enumeration that's being declared.
* enum-base: colon (:), followed by a type-specifier-seq that names an integral type (see the C++ standard for the full list of all possible integral types).
* enumerator-list: comma-separated list of enumerator definitions, each of which is either simply an identifier, which becomes the name of the enumerator, or an identifier with an initializer: identifier = integral value.
Note that though C++ allows constexpr as an initialize value, it makes the documentation less readable, hence is not permitted.
###class member
`type member-access attributes(optional) default-value(optional);`
* type: Any valid C++ type, following the C++ notation. note that there should be a serializer for the type, but deceleration order is not mandatory
* member-access: is the way the member can be access. If the member is public it can be the name itself. if not it could be a getter function that should be followed by braces. Note that getter can (and probably should) be const methods.
* attributes: Attributes define by square brackets. Currently are use to mark a version in which a specific member was added [ [ version version-number] ] would mark that the specific member was added in the given version number.
###template
`template < parameter-list > class-declaration`
* parameter-list - a non-empty comma-separated list of the template parameters.
* class-decleration - (See class section) The class name declared become a template name.
##IDL example
Forward slashes comments are ignored until the end of the line.
```
namespace utils {
// An example of a stub class
class UUID stub {
int64_t most_sig_bits;
int64_t least_sig_bits;
}
}
namespace gms {
//an enum example
enum class application_state:int {STATUS = 0,
LOAD,
SCHEMA,
DC};
// example of final class
class versioned_value final {
// getter and setter as public member
int version;
sstring value;
}
class heart_beat_state {
//getter as function
int32_t get_generation();
//default value example
int32_t get_heart_beat_version() = 1;
}
class endpoint_state {
heart_beat_state get_heart_beat_state();
std::map<application_state, versioned_value> get_application_state_map();
}
class gossip_digest {
inet_address get_endpoint();
int32_t get_generation();
//mark that a field was added on a specific version
int32_t get_max_version() [ [version 0.14.2] ];
}
class gossip_digest_ack {
std::vector<gossip_digest> digests();
std::map<inet_address, gms::endpoint_state> get_endpoint_state_map();
}
}
```

View File

@@ -15,7 +15,7 @@ git submodule update --recursive
* Installing required packages:
```
sudo yum install yaml-cpp-devel lz4-devel zlib-devel snappy-devel jsoncpp-devel thrift-devel antlr3-tool antlr3-C++-devel libasan libubsan gcc-c++ gnutls-devel ninja-build ragel libaio-devel cryptopp-devel xfsprogs-devel numactl-devel hwloc-devel libpciaccess-devel libxml2-devel
sudo yum install yaml-cpp-devel lz4-devel zlib-devel snappy-devel jsoncpp-devel thrift-devel antlr3-tool antlr3-C++-devel libasan libubsan gcc-c++ gnutls-devel ninja-build ragel libaio-devel cryptopp-devel xfsprogs-devel numactl-devel hwloc-devel libpciaccess-devel libxml2-devel python3-pyparsing
```
* Build Scylla

View File

@@ -465,8 +465,12 @@ api = ['api/api.cc',
'api/api-doc/system.json',
'api/system.cc'
]
idls = ['idl/gossip_digest.idl.hh',
]
scylla_tests_dependencies = scylla_core + [
serialize = idls + ['serializer.inc.hh']
scylla_tests_dependencies = scylla_core + api + serialize + [
'tests/cql_test_env.cc',
'tests/cql_assertions.cc',
'tests/result_set_assertions.cc',
@@ -479,7 +483,7 @@ scylla_tests_seastar_deps = [
]
deps = {
'scylla': ['main.cc'] + scylla_core + api,
'scylla': serialize + ['main.cc'] + scylla_core + api,
}
tests_not_using_seastar_test_framework = set([
@@ -656,6 +660,12 @@ with open(buildfile, 'w') as f:
rule swagger
command = seastar/json/json2code.py -f $in -o $out
description = SWAGGER $out
rule serializer
command = ./idl-compiler.py --ns ser -f $in -o $out
description = IDL compiler $out
rule serializer_inc
command = ./idl-compiler.py $in -o $out
description = Combine IDLs $out
rule ninja
command = {ninja} -C $subdir $target
restat = 1
@@ -692,6 +702,8 @@ with open(buildfile, 'w') as f:
compiles = {}
ragels = {}
swaggers = {}
serializers = {}
serializer_inc = {}
thrifts = set()
antlr3_grammars = set()
for binary in build_artifacts:
@@ -745,6 +757,12 @@ with open(buildfile, 'w') as f:
elif src.endswith('.rl'):
hh = '$builddir/' + mode + '/gen/' + src.replace('.rl', '.hh')
ragels[hh] = src
elif src.endswith('.idl.hh'):
hh = '$builddir/' + mode + '/gen/' + src.replace('.idl.hh', '.dist.hh')
serializers[hh] = src
elif src.endswith('.inc.hh'):
hh = '$builddir/' + mode + '/gen/' + src
serializer_inc[hh] = src
elif src.endswith('.json'):
hh = '$builddir/' + mode + '/gen/' + src + '.hh'
swaggers[hh] = src
@@ -763,6 +781,8 @@ with open(buildfile, 'w') as f:
for g in antlr3_grammars:
gen_headers += g.headers('$builddir/{}/gen'.format(mode))
gen_headers += list(swaggers.keys())
gen_headers += list(serializers.keys())
gen_headers += list(serializer_inc.keys())
f.write('build {}: cxx.{} {} || {} \n'.format(obj, mode, src, ' '.join(gen_headers)))
if src in extra_cxxflags:
f.write(' cxxflags = {seastar_cflags} $cxxflags $cxxflags_{mode} {extra_cxxflags}\n'.format(mode = mode, extra_cxxflags = extra_cxxflags[src], **modeval))
@@ -772,6 +792,11 @@ with open(buildfile, 'w') as f:
for hh in swaggers:
src = swaggers[hh]
f.write('build {}: swagger {}\n'.format(hh,src))
for hh in serializers:
src = serializers[hh]
f.write('build {}: serializer {}\n'.format(hh,src))
for hh in serializer_inc:
f.write('build {}: serializer_inc {}\n'.format(hh, " ".join(serializers.keys())))
for thrift in thrifts:
outs = ' '.join(thrift.generated('$builddir/{}/gen'.format(mode)))
f.write('build {}: thrift.{} {}\n'.format(outs, mode, thrift.source))

View File

@@ -81,6 +81,14 @@ public:
, _is_alive(true) {
}
endpoint_state(heart_beat_state&& initial_hb_state,
const std::map<application_state, versioned_value>& application_state)
: _heart_beat_state(std::move(initial_hb_state))
,_application_state(application_state)
, _update_timestamp(clk::now())
, _is_alive(true) {
}
heart_beat_state& get_heart_beat_state() {
return _heart_beat_state;
}

View File

@@ -37,6 +37,9 @@ public:
inet_address(int32_t ip)
: _addr(uint32_t(ip)) {
}
explicit inet_address(uint32_t ip)
: _addr(ip) {
}
inet_address(net::ipv4_address&& addr) : _addr(std::move(addr)) {}
const net::ipv4_address& addr() const {

View File

@@ -36,6 +36,7 @@
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gms/versioned_value.hh"
#include "message/messaging_service.hh"
namespace gms {
@@ -67,4 +68,8 @@ size_t versioned_value::serialized_size() const {
return serialize_string_size(value) + serialize_int32_size;
}
versioned_value versioned_value::factory::network_version() {
return versioned_value(sprint("%s",net::messaging_service::current_version));
}
}

View File

@@ -46,7 +46,6 @@
#include "gms/inet_address.hh"
#include "dht/i_partitioner.hh"
#include "to_string.hh"
#include "message/messaging_service.hh"
#include "version.hh"
#include <unordered_set>
#include <vector>
@@ -96,7 +95,7 @@ public:
value == other.value;
}
private:
public:
versioned_value(const sstring& value, int version = version_generator::get_next_version())
: version(version), value(value) {
#if 0
@@ -112,8 +111,10 @@ private:
: version(version), value(std::move(value)) {
}
versioned_value()
: version(-1) {
}
public:
int compare_to(const versioned_value &value) {
return version - value.version;
}
@@ -228,9 +229,7 @@ public:
return versioned_value(version::release());
}
versioned_value network_version() {
return versioned_value(sprint("%s",net::messaging_service::current_version));
}
versioned_value network_version();
versioned_value internal_ip(const sstring &private_ip) {
return versioned_value(private_ip);

329
idl-compiler.py Executable file
View File

@@ -0,0 +1,329 @@
#!/usr/bin/python3
#
# Copyright 2016 ScyllaDB
#
#
# This file is part of Scylla.
#
# Scylla is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Scylla is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Scylla. If not, see <http://www.gnu.org/licenses/>.
import json
import sys
import re
import glob
import argparse
import os
from string import Template
import pyparsing as pp
EXTENSION = '.idl.hh'
READ_BUFF = 'input_buffer'
WRITE_BUFF = 'output_buffer'
SERIALIZER = 'serialize'
DESERIALIZER = 'deserialize'
SETSIZE = 'set_size'
SIZETYPE = 'size_type'
parser = argparse.ArgumentParser(description="""Generate serializer helper function""")
parser.add_argument('-o', help='Output file', default='')
parser.add_argument('-f', help='input file', default='')
parser.add_argument('--ns', help="""namespace, when set function will be created
under the given namespace""", default='')
parser.add_argument('file', nargs='*', help="combine one or more file names for the genral include files")
config = parser.parse_args()
def fprint(f, *args):
for arg in args:
f.write(arg)
def fprintln(f, *args):
for arg in args:
f.write(arg)
f.write('\n')
def print_cw(f):
fprintln(f, """
/*
* Copyright 2016 ScyllaDB
*/
/*
* This file is part of Scylla.
*
* Scylla is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Scylla is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* This is an auto-generated code, do not modify directly.
*/
#pragma once
""")
def parse_file(file_name):
first = pp.Word(pp.alphas + "_", exact=1)
rest = pp.Word(pp.alphanums + "_")
number = pp.Word(pp.nums)
identifier = pp.Combine(first + pp.Optional(rest))
lbrace = pp.Literal('{').suppress()
rbrace = pp.Literal('}').suppress()
cls = pp.Literal('class')
colon = pp.Literal(":")
semi = pp.Literal(";").suppress()
langle = pp.Literal("<")
rangle = pp.Literal(">")
equals = pp.Literal("=")
comma = pp.Literal(",")
lparen = pp.Literal("(")
rparen = pp.Literal(")")
lbrack = pp.Literal("[")
rbrack = pp.Literal("]")
mins = pp.Literal("-")
struct = pp.Literal('struct')
template = pp.Literal('template')
final = pp.Literal('final').setResultsName("final")
stub = pp.Literal('stub').setResultsName("stub")
with_colon = pp.Word(pp.alphanums + "_" + ":")
btype = with_colon
type = pp.Forward()
nestedParens = pp.nestedExpr('<', '>')
tmpl = pp.Group(btype + langle.suppress() + pp.Group(pp.delimitedList(type)) + rangle.suppress())
type << (tmpl | btype)
enum_lit = pp.Literal('enum')
enum_class = pp.Group(enum_lit + cls)
ns = pp.Literal("namespace")
enum_init = equals.suppress() + pp.Optional(mins) + number
enum_value = pp.Group(identifier + pp.Optional(enum_init))
enum_values = pp.Group(lbrace + pp.delimitedList(enum_value) + pp.Optional(comma) + rbrace)
content = pp.Forward()
member_name = pp.Combine(pp.Group(identifier + pp.Optional(lparen + rparen)))
attrib = pp.Group(lbrack.suppress() + lbrack.suppress() + pp.SkipTo(']') + rbrack.suppress() + rbrack.suppress())
namespace = pp.Group(ns.setResultsName("type") + identifier.setResultsName("name") + lbrace + pp.Group(pp.OneOrMore(content)).setResultsName("content") + rbrace)
enum = pp.Group(enum_class.setResultsName("type") + identifier.setResultsName("name") + colon.suppress() + identifier.setResultsName("underline_type") + enum_values.setResultsName("enum_values") + pp.Optional(semi).suppress())
default_value = equals.suppress() + pp.SkipTo(';')
class_member = pp.Group(type.setResultsName("type") + member_name.setResultsName("name") + pp.Optional(attrib).setResultsName("attribute") + pp.Optional(default_value).setResultsName("default") + semi.suppress()).setResultsName("member")
template_param = pp.Group(identifier.setResultsName("type") + identifier.setResultsName("name"))
template_def = pp.Group(template + langle + pp.Group(pp.delimitedList(template_param)).setResultsName("params") + rangle)
class_content = pp.Forward()
class_def = pp.Group(pp.Optional(template_def).setResultsName("template") + (cls | struct).setResultsName("type") + with_colon.setResultsName("name") + pp.Optional(final) + pp.Optional(stub) + lbrace + pp.Group(pp.OneOrMore(class_content)).setResultsName("members") + rbrace + pp.Optional(semi))
content << (enum | class_def | namespace)
class_content << (enum | class_def | class_member)
rt = pp.OneOrMore(content)
singleLineComment = "//" + pp.restOfLine
rt.ignore(singleLineComment)
rt.ignore(pp.cStyleComment)
return rt.parseFile(file_name, parseAll=True)
def combine_ns(namespaces):
return "::".join(namespaces)
def open_namespaces(namespaces):
return "".join(map(lambda a: "namespace " + a + " { ", namespaces))
def close_namespaces(namespaces):
return "".join(map(lambda a: "}", namespaces))
def set_namespace(namespaces):
ns = combine_ns(namespaces)
ns_open = open_namespaces(namespaces)
ns_close = close_namespaces(namespaces)
return [ns, ns_open, ns_close]
def declare_class(hout, name, ns_open, ns_close):
clas_def = ns_open + name + ";" + ns_close
fprintln(hout, "\n", clas_def)
def declear_methods(hout, name, template_param = ""):
if config.ns != '':
fprintln(hout, "namespace ", config.ns, " {")
fprintln(hout, Template("""
template <typename Output$tmp_param>
void $ser_func(Output& buf, const $name& v);
template <typename Input$tmp_param>
$name $deser_func(Input& buf, rpc::type<$name>);""").substitute({'ser_func': SERIALIZER, 'deser_func' : DESERIALIZER, 'name' : name, 'sizetype' : SIZETYPE, 'tmp_param' : template_param }))
if config.ns != '':
fprintln(hout, "}")
def handle_enum(enum, hout, cout, namespaces , parent_template_param = []):
[ns, ns_open, ns_close] = set_namespace(namespaces)
temp_def = ',' + ", ".join(map(lambda a: a[0] + " " + a[1], parent_template_param)) if parent_template_param else ""
name = enum["name"] if ns == "" else ns + "::" + enum["name"]
declear_methods(hout, name, temp_def)
fprintln(cout, Template("""
template<typename Output$temp_def>
void $ser_func(Output& buf, const $name& v) {
serialize(buf, static_cast<$type>(v));
}
template<typename Input$temp_def>
$name $deser_func(Input& buf, rpc::type<$name>) {
return static_cast<$name>(deserialize(buf, rpc::type<$type>()));
}""").substitute({'ser_func': SERIALIZER, 'deser_func' : DESERIALIZER, 'name' : name, 'size_type' : SIZETYPE, 'type': enum['underline_type'], 'temp_def' : temp_def}))
def join_template(lst):
return "<" + ", ".join([param_type(l) for l in lst]) + ">"
def param_type(lst):
if isinstance(lst, str):
return lst
if len(lst) == 1:
return lst[0]
return lst[0] + join_template(lst[1])
def is_class(obj):
return obj["type"] == "class" or obj["type"] == "struct"
def is_enum(obj):
try:
return not isinstance(obj["type"], str) and "".join(obj["type"]) == 'enumclass'
except:
return False
def handle_class(cls, hout, cout, namespaces=[], parent_template_param = []):
if "stub" in cls:
return
[ns, ns_open, ns_close] = set_namespace(namespaces)
tpl = "template" in cls
template_param_list = (cls["template"][0]["params"].asList() if tpl else [])
template_param = ", ".join(map(lambda a: a[0] + " " + a[1], template_param_list + parent_template_param)) if (template_param_list + parent_template_param) else ""
template = "template <"+ template_param +">\n" if tpl else ""
template_class_param = "<" + ",".join(map(lambda a: a[1], template_param_list)) + ">" if tpl else ""
temp_def = ',' + template_param if template_param != "" else ""
if ns == "":
name = cls["name"]
else:
name = ns + "::" + cls["name"]
full_name = name + template_class_param
for param in cls["members"]:
if is_class(param):
handle_class(param, hout, cout, namespaces + [cls["name"] + template_class_param], parent_template_param + template_param_list)
elif is_enum(param):
handle_enum(param, hout, cout, namespaces + [cls["name"] + template_class_param], parent_template_param + template_param_list)
declear_methods(hout, name + template_class_param, temp_def)
modifier = "final" in cls
fprintln(cout, Template("""
template<typename Output$temp_def>
void $func(Output& buf, const $name& obj) {""").substitute({'func' : SERIALIZER, 'name' : full_name, 'temp_def': temp_def}))
if not modifier:
fprintln(cout, Template(""" $set_size(buf, obj);""").substitute({'func' : SERIALIZER, 'set_size' : SETSIZE, 'name' : name, 'sizetype' : SIZETYPE}))
for param in cls["members"]:
if is_class(param) or is_enum(param):
continue
fprintln(cout, Template(""" $func(buf, obj.$var);""").substitute({'func' : SERIALIZER, 'var' : param["name"]}))
fprintln(cout, "}")
fprintln(cout, Template("""
template<typename Input$temp_def>
$name$temp_param $func(Input& buf, rpc::type<$name$temp_param>) {""").substitute({'func' : DESERIALIZER, 'name' : name, 'temp_def': temp_def, 'temp_param' : template_class_param}))
if not modifier:
fprintln(cout, Template(""" $size_type size = $func(buf, rpc::type<$size_type>());
Input in = buf.read_substream(size - sizeof($size_type));""").substitute({'func' : DESERIALIZER, 'size_type' : SIZETYPE}))
else:
fprintln(cout, """ Input& in = buf;""")
params = []
for index, param in enumerate(cls["members"]):
if is_class(param) or is_enum(param):
continue
local_param = "__local_" + str(index)
if "attribute" in param:
deflt = param["default"] if param["default"] else param["type"] + "()"
fprintln(cout, Template(""" $typ $local = (in.size()>0) ?
$func(in, rpc::type<$typ>()) : $default;""").substitute({'func' : DESERIALIZER, 'typ': param_type(param["type"]), 'local' : local_param, 'default': deflt}))
else:
fprintln(cout, Template(""" $typ $local = $func(in, rpc::type<$typ>());""").substitute({'func' : DESERIALIZER, 'typ': param_type(param["type"]), 'local' : local_param}))
params.append("std::move(" + local_param + ")")
fprintln(cout, Template("""
$name$temp_param res {$params};
return res;
}""").substitute({'name' : name, 'params': ", ".join(params), 'temp_param' : template_class_param}))
def handle_objects(tree, hout, cout, namespaces=[]):
for obj in tree:
if is_class(obj):
handle_class(obj, hout, cout, namespaces)
elif is_enum(obj):
handle_enum(obj, hout, cout, namespaces)
elif obj["type"] == "namespace":
handle_objects(obj["content"], hout, cout, namespaces + [obj["name"]])
else:
print("unknown type ", obj, obj["type"])
def load_file(name):
if config.o:
cout = open(config.o.replace('.hh', '.impl.hh'), "w+")
hout = open(config.o, "w+")
else:
cout = open(name.replace(EXTENSION, '.dist.impl.hh'), "w+")
hout = open(name.replace(EXTENSION, '.dist.hh'), "w+")
print_cw(hout)
fprintln(hout, """
/*
* The generate code should be included in a header file after
* The object definition
*/
""")
print_cw(cout)
if config.ns != '':
fprintln(cout, "namespace ", config.ns, " {")
data = parse_file(name)
if data:
handle_objects(data, hout, cout)
if config.ns != '':
fprintln(cout, "}")
cout.close()
hout.close()
def general_include(files):
name = config.o if config.o else "serializer.dist.hh"
cout = open(name.replace('.hh', '.impl.hh'), "w+")
hout = open(name, "w+")
print_cw(cout)
print_cw(hout)
for n in files:
fprintln(hout, '#include "' + n +'"')
fprintln(cout, '#include "' + n.replace(".dist.hh", '.dist.impl.hh') +'"')
cout.close()
hout.close()
if config.file:
general_include(config.file)
elif config.f != '':
load_file(config.f)

83
idl/gossip_digest.idl.hh Normal file
View File

@@ -0,0 +1,83 @@
/*
* Copyright 2016 ScyllaDB
*/
/*
* This file is part of Scylla.
*
* Scylla is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Scylla is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
namespace utils {
class UUID final {
int64_t most_sig_bits;
int64_t least_sig_bits;
};
}
namespace gms {
enum class application_state:int {STATUS = 0,
LOAD,
SCHEMA,
DC,
RACK,
RELEASE_VERSION,
REMOVAL_COORDINATOR,
INTERNAL_IP,
RPC_ADDRESS,
X_11_PADDING,
SEVERITY,
NET_VERSION,
HOST_ID,
TOKENS,
X1,
X2,
X3,
X4,
X5,
X6,
X7,
X8,
X9,
X10};
class inet_address final {
uint32_t raw_addr();
};
class versioned_value {
sstring value;
int version;
};
class heart_beat_state {
int32_t get_generation();
int32_t get_heart_beat_version();
};
class endpoint_state {
gms::heart_beat_state get_heart_beat_state();
std::map<gms::application_state, gms::versioned_value> get_application_state_map();
}
class gossip_digest {
gms::inet_address get_endpoint();
int32_t get_generation();
int32_t get_max_version();
}
class gossip_digest_ack {
std::vector<gms::gossip_digest> get_gossip_digest_list();
std::map<gms::inet_address, gms::endpoint_state> get_endpoint_state_map();
}
}

View File

@@ -36,9 +36,21 @@
#include "dht/i_partitioner.hh"
#include "range.hh"
#include "frozen_schema.hh"
#include "serializer_impl.hh"
namespace net {
template<typename Output>
void write(serializer, Output& out, const gms::gossip_digest_ack& data) {
ser::serialize(out, data);
}
template <typename Input>
gms::gossip_digest_ack
read(serializer, Input& in, rpc::type<gms::gossip_digest_ack> type) {
return ser::deserialize(in, type);
}
static logging::logger logger("messaging_service");
using inet_address = gms::inet_address;

155
serializer.hh Normal file
View File

@@ -0,0 +1,155 @@
/*
* Copyright 2016 ScyllaDB
*/
/*
* This file is part of Scylla.
*
* Scylla is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Scylla is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vector>
#include "core/sstring.hh"
#include <unordered_map>
namespace rpc {
class simple_output_stream;
class measuring_output_stream;
}
namespace ser {
using size_type = uint32_t;
template<typename T, typename Input>
inline T deserialize_integral(Input& input) {
static_assert(std::is_integral<T>::value, "T should be integral");
T data;
input.read(reinterpret_cast<char*>(&data), sizeof(T));
return le_to_cpu(data);
}
template<typename T, typename Output>
inline void serialize_integral(Output& output, T data) {
static_assert(std::is_integral<T>::value, "T should be integral");
data = cpu_to_le(data);
output.write(reinterpret_cast<const char*>(&data), sizeof(T));
}
// For integer type
template<typename Input>
bool deserialize(Input& input, rpc::type<bool>) {
return deserialize(input, rpc::type<uint8_t>());
}
template<typename Input>
int8_t deserialize(Input& input, rpc::type<int8_t>) {
return deserialize_integral<int8_t>(input);
}
template<typename Input>
uint8_t deserialize(Input& input, rpc::type<uint8_t>) {
return deserialize_integral<uint8_t>(input);
}
template<typename Input>
int16_t deserialize(Input& input, rpc::type<int16_t>) {
return deserialize_integral<int16_t>(input);
}
template<typename Input>
uint16_t deserialize(Input& input, rpc::type<uint16_t>) {
return deserialize_integral<uint16_t>(input);
}
template<typename Input>
int32_t deserialize(Input& input, rpc::type<int32_t>) {
return deserialize_integral<int32_t>(input);
}
template<typename Input>
uint32_t deserialize(Input& input, rpc::type<uint32_t>) {
return deserialize_integral<uint32_t>(input);
}
template<typename Input>
int64_t deserialize(Input& input, rpc::type<int64_t>) {
return deserialize_integral<int64_t>(input);
}
template<typename Input>
uint64_t deserialize(Input& input, rpc::type<uint64_t>) {
return deserialize_integral<uint64_t>(input);
}
template<typename Output>
void serialize(Output& output, bool data) {
serialize(output, uint8_t(data));
}
template<typename Output>
void serialize(Output& output, int8_t data) {
serialize_integral(output, data);
}
template<typename Output>
void serialize(Output& output, uint8_t data) {
serialize_integral(output, data);
}
template<typename Output>
void serialize(Output& output, int16_t data) {
serialize_integral(output, data);
}
template<typename Output>
void serialize(Output& output, uint16_t data) {
serialize_integral(output, data);
}
template<typename Output>
void serialize(Output& output, int32_t data) {
serialize_integral(output, data);
}
template<typename Output>
void serialize(Output& output, uint32_t data) {
serialize_integral(output, data);
}
template<typename Output>
void serialize(Output& output, int64_t data) {
serialize_integral(output, data);
}
template<typename Output>
void serialize(Output& output, uint64_t data) {
serialize_integral(output, data);
}
template<typename Output>
void safe_serialize_as_uint32(Output& output, uint64_t data);
// For vectors
template<typename T, typename Output>
inline void serialize(Output& out, const std::vector<T>& v);
template<typename T, typename Input>
inline std::vector<T> deserialize(Input& in, rpc::type<std::vector<T>>);
template<typename K, typename V, typename Output>
inline void serialize(Output& out, const std::map<K, V>& v);
template<typename K, typename V, typename Input>
inline std::map<K, V> deserialize(Input& in, rpc::type<std::map<K, V>>);
template<typename T>
size_type get_sizeof(const T& obj);
// For sstring
template<typename Output>
void serialize(Output& out, const sstring& v);
template<typename Input>
sstring deserialize(Input& in, rpc::type<sstring>);
template<typename T>
void set_size(rpc::simple_output_stream& os, const T& obj);
template<typename T>
void set_size(rpc::measuring_output_stream& os, const T& obj);
}
/*
* Import the auto generated forward decleration code
*/
#include "serializer.inc.hh"

126
serializer_impl.hh Normal file
View File

@@ -0,0 +1,126 @@
/*
* Copyright 2016 ScyllaDB
*/
/*
* This file is part of Scylla.
*
* Scylla is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Scylla is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "serializer.hh"
#include "serializer.inc.impl.hh"
namespace ser {
template<typename T>
void set_size(rpc::simple_output_stream& os, const T& obj) {
serialize(os, get_sizeof(obj));
}
template<typename T>
void set_size(rpc::measuring_output_stream& os, const T& obj) {
serialize(os, uint32_t(0));
}
template<typename Output>
void safe_serialize_as_uint32(Output& out, uint64_t data) {
if (data > std::numeric_limits<uint32_t>::max()) {
throw std::runtime_error("Size is too big for serialization");
}
serialize(out, uint32_t(data));
}
template<typename T, typename Output>
inline void serialize(Output& out, const std::vector<T>& v) {
safe_serialize_as_uint32(out, v.size());
for (auto&& e : v) {
serialize(out, e);
}
}
template<typename T, typename Input>
inline std::vector<T> deserialize(Input& in, rpc::type<std::vector<T>>) {
auto sz = deserialize(in, rpc::type<uint32_t>());
std::vector<T> v;
v.reserve(sz);
while (sz--) {
v.push_back(deserialize(in, rpc::type<T>()));
}
return v;
}
template<typename K, typename V, typename Output>
inline void serialize(Output& out, const std::map<K, V>& v) {
safe_serialize_as_uint32(out, v.size());
for (auto&& e : v) {
serialize(out, e.first);
serialize(out, e.second);
}
}
template<typename K, typename V, typename Input>
inline std::map<K, V> deserialize(Input& in, rpc::type<std::map<K, V>>) {
auto sz = deserialize(in, rpc::type<uint32_t>());
std::map<K, V> m;
while (sz--) {
K k = deserialize(in, rpc::type<K>());
V v = deserialize(in, rpc::type<V>());
m[k] = v;
}
return m;
}
template<typename T, typename Output>
inline void serialize(Output& out, const std::experimental::optional<T>& v) {
serialize(out, bool(v));
if (v) {
serialize(out, v.value());
}
}
template<typename T, typename Input>
inline std::experimental::optional<T> deserialize(Input& in, rpc::type<std::experimental::optional<T>>) {
std::experimental::optional<T> v;
auto b = deserialize(in, rpc::type<bool>());
if (b) {
v = deserialize(in, rpc::type<T>());
}
return v;
}
template<typename Output>
void serialize(Output& out, const sstring& v) {
safe_serialize_as_uint32(out, uint32_t(v.size()));
out.write(v.begin(), v.size());
}
template<typename Input>
sstring deserialize(Input& in, rpc::type<sstring>) {
auto sz = deserialize(in, rpc::type<uint32_t>());
sstring v(sstring::initialized_later(), sz);
in.read(v.begin(), sz);
return v;
}
template<typename T>
size_type get_sizeof(const T& obj) {
rpc::measuring_output_stream ms;
serialize(ms, obj);
auto size = ms.size();
if (size > std::numeric_limits<size_type>::max()) {
throw std::runtime_error("Object is too big for get_sizeof");
}
return size;
}
}