Uses a script to generate ProtocolVersion related files

This commit is contained in:
Xiaoge Su 2023-06-29 20:23:01 -07:00
parent 7c8c24bc8d
commit 1465b60c02
5 changed files with 740 additions and 3 deletions

View File

@ -20,18 +20,32 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
endif()
make_directory(${CMAKE_CURRENT_BINARY_DIR}/include/flow)
set(FDB_API_VERSION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/ApiVersions.cmake" CACHE STRING "Api version cmake file." FORCE)
include(${FDB_API_VERSION_FILE})
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ApiVersion.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/include/flow/ApiVersion.h)
set(FDB_PROTOCOL_VERSION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/ProtocolVersions.cmake" CACHE STRING "Protocol version cmake file." FORCE)
include(${FDB_PROTOCOL_VERSION_FILE})
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ProtocolVersion.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/include/flow/ProtocolVersion.h)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/SourceVersion.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/include/flow/SourceVersion.h)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/include/flow/config.h)
set(FDB_PROTOCOL_VERSION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/ProtocolVersions.cmake" CACHE STRING "Protocol version cmake file." FORCE)
set(FDB_PROTOCOL_VERSION_HEADER_FILE "${CMAKE_CURRENT_BINARY_DIR}/include/flow/ProtocolVersion.h")
add_custom_command(
OUTPUT ${FDB_PROTOCOL_VERSION_HEADER_FILE}
COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/protocolversion/protocol_version.py --source ${FDB_PROTOCOL_VERSION_FILE} --generator cpp --output ${FDB_PROTOCOL_VERSION_HEADER_FILE}
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/protocolversion/protocol_version.py
${CMAKE_CURRENT_SOURCE_DIR}/protocolversion/ProtocolVersion.h.template
${CMAKE_CURRENT_SOURCE_DIR}/ProtocolVersions.cmake
)
add_custom_target(ProtocolVersion DEPENDS ${FDB_PROTOCOL_VERSION_HEADER_FILE})
add_flow_target(STATIC_LIBRARY NAME flow SRCS ${FLOW_SRCS})
add_flow_target(STATIC_LIBRARY NAME flow_sampling SRCS ${FLOW_SRCS})
add_dependencies(flow ProtocolVersion)
add_dependencies(flow_sampling ProtocolVersion)
if (FLOW_USE_ZSTD)
include(CompileZstd)
compile_zstd()

View File

@ -0,0 +1,201 @@
/*
* ProtocolVersion.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FLOW_PROTOCOL_VERSION_H
#define FLOW_PROTOCOL_VERSION_H
#pragma once
#include "flow/Trace.h"
#include <cstdint>
// This version impacts both communications and the deserialization of certain database and IKeyValueStore keys.
constexpr uint64_t defaultProtocolVersionValue = {{defaultVersion | encode_version}};
// The first protocol version that cannot be downgraded from. Ordinarily, this will be two release versions larger
// than the current version, meaning that we only support downgrades between consecutive release versions.
constexpr uint64_t minInvalidProtocolVersionValue = {{minInvalidVersion | encode_version}};
// The lowest protocol version that can be downgraded to.
constexpr uint64_t minCompatibleProtocolVersionValue = {{minCompatibleVersion | encode_version}};
// The protocol version that will most likely follow the current one
// Used only for testing upgrades to the future version
constexpr uint64_t futureProtocolVersionValue = {{futureVersion | encode_version}};
// ProtocolVersion wraps a uint64_t to make it type safe. It will know about the current versions.
// The default constructor will initialize the version to 0 (which is an invalid
// version). ProtocolVersion objects should never be compared to version numbers
// directly. Instead one should always use the type-safe version types from which
// this class inherits all.
class ProtocolVersion {
uint64_t _version;
public: // constants
static constexpr uint64_t versionFlagMask = 0x0FFFFFFFFFFFFFFFLL;
static constexpr uint64_t objectSerializerFlag = 0x1000000000000000LL;
static constexpr uint64_t compatibleProtocolVersionMask = 0xFFFFFFFFFFFF0000LL;
static constexpr uint64_t minValidProtocolVersion = 0x0FDB00A200060001LL;
static constexpr uint64_t invalidProtocolVersion = 0x0FDB00A100000000LL;
public:
constexpr explicit ProtocolVersion(uint64_t version) : _version(version) {}
constexpr ProtocolVersion() : _version(0) {}
constexpr bool isCompatible(ProtocolVersion other) const {
return (other.version() & compatibleProtocolVersionMask) == (version() & compatibleProtocolVersionMask);
}
// Returns a normalized protocol version that will be the same for all compatible versions
constexpr ProtocolVersion normalizedVersion() const {
return ProtocolVersion(_version & compatibleProtocolVersionMask);
}
constexpr bool isValid() const { return version() >= minValidProtocolVersion; }
constexpr bool isInvalid() const { return version() == invalidProtocolVersion; }
constexpr uint64_t version() const { return _version & versionFlagMask; }
constexpr uint64_t versionWithFlags() const { return _version; }
constexpr bool hasObjectSerializerFlag() const { return (_version & objectSerializerFlag) > 0; }
constexpr void addObjectSerializerFlag() { _version = _version | objectSerializerFlag; }
constexpr void removeObjectSerializerFlag() {
_version = hasObjectSerializerFlag() ? _version ^ objectSerializerFlag : _version;
}
constexpr void removeAllFlags() { _version = version(); }
// comparison operators
// Comparison operators ignore the flags - this is because the version flags are stored in the
// most significant byte which can make comparison confusing. Also, generally, when one wants to
// compare versions, we are usually not interested in the flags.
constexpr bool operator==(const ProtocolVersion other) const { return version() == other.version(); }
constexpr bool operator!=(const ProtocolVersion other) const { return version() != other.version(); }
constexpr bool operator<=(const ProtocolVersion other) const { return version() <= other.version(); }
constexpr bool operator>=(const ProtocolVersion other) const { return version() >= other.version(); }
constexpr bool operator<(const ProtocolVersion other) const { return version() < other.version(); }
constexpr bool operator>(const ProtocolVersion other) const { return version() > other.version(); }
public: // introduced features
// The 5th digit from right is dev version, for example, 2 in 0x0FDB00B061020000LL;
// It was used to identify a protocol change (e.g., interface change) between major/minor versions (say 5.1 and 5.2)
// We stopped using the dev version consistently in the past.
// To ensure binaries work across patch releases (e.g., 6.2.0 to 6.2.22), we require that the protocol version be
// the same for each of them.
{% for version, features in all_features.items() %}
{% for feature in features %}
{% set feature_name = feature | feature_name_transformer %}
{% set encoded_version = version | encode_version %}
// The first check second expression version doesn't need to change because it's just for earlier protocol versions.
static_assert(({{encoded_version}} & {{lsbMask | encode_version}}) == 0 || {{encoded_version}} < 0x0FDB00B071000000LL, "Unexpected feature protocol version");
static_assert({{encoded_version}} <= defaultProtocolVersionValue, "Feature protocol version too large");
struct {{feature_name}} {
static constexpr uint64_t protocolVersion = {{version | encode_version}};
};
constexpr bool has{{feature_name}}() const { return this->version() >= {{feature_name}}::protocolVersion; }
static constexpr ProtocolVersion with{{feature_name}}() { return ProtocolVersion({{feature_name}}::protocolVersion); }
{% endfor %}
{% endfor %}
};
template <>
struct Traceable<ProtocolVersion> : std::true_type {
static std::string toString(const ProtocolVersion& protocolVersion) {
return format("0x%016lX", protocolVersion.version());
}
};
constexpr ProtocolVersion defaultProtocolVersion(defaultProtocolVersionValue);
constexpr ProtocolVersion minInvalidProtocolVersion(minInvalidProtocolVersionValue);
constexpr ProtocolVersion minCompatibleProtocolVersion(minCompatibleProtocolVersionValue);
// The protocol version of the process, normally it is equivalent defaultProtocolVersion
// The currentProtocolVersion can be overridden dynamically for testing upgrades
// to a future FDB version
ProtocolVersion currentProtocolVersion();
// Assume the next future protocol version as the current one. Used for testing purposes only
void useFutureProtocolVersion();
// This assert is intended to help prevent incrementing the leftmost digits accidentally. It will probably need to
// change when we reach version 10.
static_assert(defaultProtocolVersion.version() < {{leftMostCheck | encode_version}}, "Unexpected protocol version");
// The last two bytes of the protocol version are currently masked out in compatibility checks. We do not use them,
// so prevent them from being inadvertently changed.
//
// We also do not modify the protocol version for patch releases, so prevent modifying the patch version digit.
static_assert((defaultProtocolVersion.version() & {{lsbMask | encode_version}}) == 0, "Unexpected protocol version");
// Downgrades must support at least one minor version.
static_assert(minInvalidProtocolVersion.version() >=
(defaultProtocolVersion.version() & 0xFFFFFFFFFF000000LL) + 0x0000000002000000,
"Downgrades must support one minor version");
// The min invalid protocol version should be the smallest possible protocol version associated with a minor release
// version.
static_assert((minInvalidProtocolVersion.version() & 0xFFFFFFLL) == 0, "Unexpected min invalid protocol version");
struct SWVersion {
constexpr static FileIdentifier file_identifier = 13943914;
private:
uint64_t _newestProtocolVersion;
uint64_t _lastRunProtocolVersion;
uint64_t _lowestCompatibleProtocolVersion;
public:
SWVersion() {
_newestProtocolVersion = 0;
_lastRunProtocolVersion = 0;
_lowestCompatibleProtocolVersion = 0;
}
SWVersion(ProtocolVersion latestVersion, ProtocolVersion lastVersion, ProtocolVersion minCompatibleVersion)
: _newestProtocolVersion(latestVersion.version()), _lastRunProtocolVersion(lastVersion.version()),
_lowestCompatibleProtocolVersion(minCompatibleVersion.version()) {}
bool isValid() const {
return (_newestProtocolVersion != 0 && _lastRunProtocolVersion != 0 && _lowestCompatibleProtocolVersion != 0);
}
uint64_t newestProtocolVersion() const { return _newestProtocolVersion; }
uint64_t lastRunProtocolVersion() const { return _lastRunProtocolVersion; }
uint64_t lowestCompatibleProtocolVersion() const { return _lowestCompatibleProtocolVersion; }
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, _newestProtocolVersion, _lastRunProtocolVersion, _lowestCompatibleProtocolVersion);
}
};
template <>
struct Traceable<SWVersion> : std::true_type {
static std::string toString(const SWVersion& swVersion) {
return format("Newest: 0x%016lX, Last: 0x%016lX, MinCompatible: 0x%016lX",
swVersion.newestProtocolVersion(),
swVersion.lastRunProtocolVersion(),
swVersion.lowestCompatibleProtocolVersion());
}
};
#endif // FLOW_PROTOCOL_VERSION_H

View File

@ -0,0 +1,74 @@
// Automatically generated by script, do NOT modify by hand
package com.apple.cie.foundationdb.hubble;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.List;
class ProtocolVersion implements Comparable<ProtocolVersion> {
public static final ProtocolVersion protocolVersionSixThree = new ProtocolVersion(0x0FDB00B063010001L);
public static final ProtocolVersion protocolVersionSixOne = new ProtocolVersion(0x0FDB00B061060001L);
public static final ProtocolVersion protocolVersionSixTwo = new ProtocolVersion(0x0FDB00B062010001L);
public static final ProtocolVersion protocolVersionSixZero = new ProtocolVersion(0x0FDB00A570010001L);
public static final ProtocolVersion protocolVersionSevenZero = new ProtocolVersion(0x0FDB00B070010000L);
public static final ProtocolVersion protocolVersionSevenOne = new ProtocolVersion(0x0FDB00B071010000L);
public static final ProtocolVersion protocolVersionSevenThree = new ProtocolVersion(0x0FDB00B073000000L);
public static final List<ProtocolVersion> supportedVersions = Arrays.asList(
protocolVersionSixThree,
protocolVersionSixTwo,
protocolVersionSixOne,
protocolVersionSixZero,
protocolVersionSevenZero,
protocolVersionSevenOne,
protocolVersionSevenThree);
private final long protocolVersion;
public ProtocolVersion(long version) {
protocolVersion = version;
}
public static ProtocolVersion deserialize(ByteBuffer byteBuffer) {
assert byteBuffer.order() == ByteOrder.LITTLE_ENDIAN;
final long version = byteBuffer.getLong();
ProtocolVersion protocolVersion = new ProtocolVersion(version);
return protocolVersion;
}
public boolean isSupported() {
return supportedVersions.contains(this);
}
@Override
public String toString() {
return String.valueOf(protocolVersion);
}
public long getProtocolVersion() {
return protocolVersion;
}
@Override
public int compareTo(ProtocolVersion o) {
if (protocolVersion < o.protocolVersion) {
return -1;
} else if (protocolVersion > o.protocolVersion) {
return +1;
} else {
return 0;
}
}
{% for version, features in all_features.items() %}
{% for feature in features %}
public boolean has{{feature}}() {
return protocolVersion >= {{version | encode_version}};
}
public static ProtocolVersion with{{feature}}() {
return new ProtocolVersion({{version | encode_version}});
}
{% endfor %}
{% endfor %}
}

View File

@ -0,0 +1,345 @@
#! /usr/bin/env python3
"""protocol_version.py
Tool to manipulate FoundationDB Protocol Versions for other programming languages
"""
import abc
import argparse
import io
import json
import os
import re
import sys
from typing import Dict, List
import jinja2
SCRIPT_NAME = os.path.basename(__file__)
SCRIPT_DIRECTORY = os.path.dirname(os.path.abspath(__file__))
def _version_to_hex_string(version: int) -> str:
return "0x{:016X}".format(version)
class ProtocolVersion:
def __init__(self):
self._default_version = None
self._future_version = None
self._min_compatibile_version = None
self._min_invalid_version = None
self._left_most_check = None
self._lsb_mask = None
self._features: Dict[int, List[str]] = dict()
def set_default_version(self, version: int):
self._default_version = version
def set_future_version(self, version: int):
self._future_version = version
def set_min_compatible_version(self, version: int):
self._min_compatibile_version = version
def set_min_invalid_version(self, version: int):
self._min_invalid_version = version
def set_left_most_check(self, version: int):
self._left_most_check = version
def set_lsb_mask(self, version: int):
self._lsb_mask = version
@property
def default_version(self):
return self._default_version
@property
def future_version(self):
return self._future_version
@property
def min_compatible_version(self):
return self._min_compatibile_version
@property
def min_invalid_version(self):
return self._min_invalid_version
@property
def left_most_check(self):
return self._left_most_check
@property
def lsb_mask(self):
return self._lsb_mask
def add_feature(self, version: int, feature: str):
if version not in self._features:
self._features[version] = []
self._features[version].append(feature)
return self
@property
def features(self):
return self._features
def _decapitate(text: str, prefix: str) -> str:
if text.startswith(prefix):
return text[len(prefix) :]
return text
def _tail_chop(text: str, postfix: str) -> str:
if text.endswith(postfix):
return text[: len(text) - len(postfix)]
return text
class ProtocolVersionSerializerBase(abc.ABC):
@abc.abstractmethod
def _load(self, stream: io.TextIOWrapper) -> ProtocolVersion:
raise NotImplementedError()
def load(self, stream: io.TextIOWrapper) -> ProtocolVersion:
return self._load(stream)
@abc.abstractmethod
def _save(self, protocol_version: ProtocolVersion, stream: io.TextIOWrapper):
raise NotImplementedError()
def save(self, protocol_version: ProtocolVersion, stream: io.TextIOWrapper):
return self._save(protocol_version, stream)
class CMakeProtocolVersionSerializer(ProtocolVersionSerializerBase):
SPECIAL_FIELDS = {
"DEFAULT_VERSION": ProtocolVersion.set_default_version,
"FUTURE_VERSION": ProtocolVersion.set_future_version,
"MIN_COMPATIBLE_VERSION": ProtocolVersion.set_min_compatible_version,
"MIN_INVALID_VERSION": ProtocolVersion.set_min_invalid_version,
"LEFT_MOST_CHECK": ProtocolVersion.set_left_most_check,
"LSB_MASK": ProtocolVersion.set_lsb_mask,
}
def _decode_version(self, encoded: str) -> int:
"""Decode version like 0x0FDB00B073000000LL into decimal value"""
return int(_tail_chop(encoded, "LL"), 16)
def _load(self, stream: io.TextIOWrapper) -> ProtocolVersion:
protocol_version_cmake_regex = re.compile(
r"set\((?P<feature>[^\s]+)\s+\"(?P<version>[^\"]+)\"\)"
)
protocol_version = ProtocolVersion()
for line in stream:
match = protocol_version_cmake_regex.search(line)
if not match:
continue
key_name = _decapitate(match.groupdict()["feature"], "FDB_PV_")
value = self._decode_version(match.groupdict()["version"])
if key_name in CMakeProtocolVersionSerializer.SPECIAL_FIELDS:
CMakeProtocolVersionSerializer.SPECIAL_FIELDS[key_name](
protocol_version, value
)
else:
protocol_version.add_feature(value, key_name)
return protocol_version
def _save(self, protocol_version: ProtocolVersion, stream: io.TextIOWrapper):
raise NotImplementedError()
class JSONProtocolVersionSerialzer(ProtocolVersionSerializerBase):
def _load(self, stream: io.TextIOWrapper) -> ProtocolVersion:
raise NotImplementedError()
def _save(self, protocol_version: ProtocolVersion, stream: io.TextIOWrapper):
result_dict = {
"default_version": protocol_version.default_version,
"future_version": protocol_version.future_version,
"min_compatible_version": protocol_version.min_compatible_version,
"min_invalid_version": protocol_version.min_invalid_version,
"left_most_check": protocol_version.left_most_check,
"lsb_mask": protocol_version.lsb_mask,
"features": protocol_version._features,
}
stream.write(json.dumps(result_dict, ident=2))
class NameTransformer(abc.ABC):
"""Transform the name in FDB_PV_UPPER_CASE into some other format"""
@abc.abstractmethod
def transform_feature_text(self, feature: str) -> str:
raise NotImplementedError()
class CamelCaseNameTransformer(NameTransformer):
XXX_FIELD_MAPPING = {
"IPV6": "IPv6",
"INEXPENSIVE_MULTIVERSION_CLIENT": "InexpensiveMultiVersionClient",
"TSS": "TSS",
"DR_BACKUP_RANGES": "DRBackupRanges",
"OTEL_SPAN_CONTEXT": "OTELSpanContext",
"FDB_ENCRYPTED_SNAPSHOT_BACKUP_FILE": "EncryptedSnapshotBackupFile",
"PROCESS_ID": "ProcessID",
"SHARD_ENCODE_LOCATION_METADATA": "ShardEncodeLocationMetaData",
"TLOG_VERSION": "TLogVersion",
"SW_VERSION_TRACKING": "SWVersionTracking",
"MULTIGENERATION_TLOG": "MultiGenerationTLog",
"TLOG_QUEUE_ENTRY_REF": "TLogQueueEntryRef",
"PROCESS_ID_FILE": "ProcessIDFile",
}
def _all_caps_to_camel(self, text: str) -> str:
"""Translate ABC_DEF to AbcDef"""
return "".join(item.capitalize() for item in text.split("_"))
def transform_feature_text(self, feature: str) -> str:
if feature in CamelCaseNameTransformer.XXX_FIELD_MAPPING:
return CamelCaseNameTransformer.XXX_FIELD_MAPPING[feature]
return self._all_caps_to_camel(feature)
class SnakeNameTransformer(NameTransformer):
XXX_FIELD_MAPPING = {
"IPV6": "IPv6",
}
def transform_feature_text(self, feature: str) -> str:
if feature in SnakeNameTransformer.XXX_FIELD_MAPPING:
return SnakeNameTransformer.XXX_FIELD_MAPPING[feature]
return feature.lower()
class CodeGenBase(abc.ABC):
def __init__(self, protocol_version: ProtocolVersion):
self._protocol_version = protocol_version
@property
def protocol_version(self) -> ProtocolVersion:
return self._protocol_version
@abc.abstractmethod
def _render(self):
raise NotImplementedError()
def _get_environment(
self, encode_version, name_transformer: NameTransformer
) -> jinja2.Environment:
env = jinja2.Environment(autoescape=True)
env = jinja2.Environment(autoescape=True, trim_blocks=True, lstrip_blocks=True)
env.filters["encode_version"] = encode_version
env.filters[
"feature_name_transformer"
] = name_transformer.transform_feature_text
return env
def render(self):
return self._render()
class JavaCodeGen(CodeGenBase):
JAVA_TEMPLATE_FILE = os.path.join(SCRIPT_DIRECTORY, "ProtocolVersion.java.template")
def _encode_version(self, version: int) -> str:
return "{}L".format(_version_to_hex_string(version))
def _render(self):
env = self._get_environment(self._encode_version, CamelCaseNameTransformer())
with open(JavaCodeGen.JAVA_TEMPLATE_FILE) as template_stream:
template = env.from_string(template_stream.read())
return template.render({"all_features": self.protocol_version.features})
class CxxHeaderFileCodeGen(CodeGenBase):
CXX_TEMPLATE_FILE = os.path.join(SCRIPT_DIRECTORY, "ProtocolVersion.h.template")
def _encode_version(self, version: int) -> str:
return "{}LL".format(_version_to_hex_string(version))
def _render(self):
env = self._get_environment(self._encode_version, CamelCaseNameTransformer())
with open(CxxHeaderFileCodeGen.CXX_TEMPLATE_FILE) as template_stream:
template = env.from_string(template_stream.read())
return template.render(
{
"all_features": self.protocol_version.features,
"defaultVersion": self.protocol_version.default_version,
"futureVersion": self.protocol_version.future_version,
"minInvalidVersion": self.protocol_version.min_invalid_version,
"minCompatibleVersion": self.protocol_version.min_compatible_version,
"leftMostCheck": self.protocol_version.left_most_check,
"lsbMask": self.protocol_version.lsb_mask,
}
)
class PythonLibraryCodeGen(CodeGenBase):
PYTHON_TEMPLATE_FILE = os.path.join(
SCRIPT_DIRECTORY, "protocol_version.py.template"
)
def _encode_version(self, version: int) -> str:
return _version_to_hex_string(version)
def _render(self):
env = self._get_environment(self._encode_version, SnakeNameTransformer())
with open(PythonLibraryCodeGen.PYTHON_TEMPLATE_FILE) as template_stream:
template = env.from_string(template_stream.read())
return template.render({"all_features": self.protocol_version.features})
def _setup_args():
parser = argparse.ArgumentParser(prog=SCRIPT_NAME)
parser.add_argument(
"--source",
type=str,
required=True,
help="Source of protocol versions",
)
parser.add_argument(
"--generator", type=str, required=True, help="Code generator (cpp/java)"
)
parser.add_argument("--output", type=str, required=True, help="Output file")
return parser.parse_args()
def main():
args = _setup_args()
with open(args.source) as stream:
protocol_version = CMakeProtocolVersionSerializer().load(stream)
if args.generator == "cpp":
generator = CxxHeaderFileCodeGen
elif args.generator == "java":
generator = JavaCodeGen
elif args.generator == "python":
generator = PythonLibraryCodeGen
else:
raise RuntimeError("Unknown generator {}".format(args.generator))
with open(args.output, "w") as stream:
stream.write(generator(protocol_version).render())
if __name__ == "__main__":
sys.exit(main())

View File

@ -0,0 +1,103 @@
""" Protocol Version
This code is generated by `protocol_version.py`. Do not modify by hand.
"""
class ProtocolVersion:
_supported_protocol_versions = set()
def __init__(self, version: int):
self._version = version
def __hash__(self):
return hash(self._version)
@property
def _version_flag_mask(self) -> int:
return 0x0FFFFFFFFFFFFFFF
@property
def version(self) -> int:
return self._version & self._version_flag_mask
@property
def compatible_protocol_version_mask(self) -> int:
return 0xFFFFFFFFFFFF0000;
@property
def normalized_version(self) -> "ProtocolVersion":
return ProtocolVersion(self.version & self.compatible_protocol_version_mask)
def is_compatible(self, other: "ProtocolVersion") -> bool:
return (other._version & self.compatible_protocol_version_mask) == (self._version & self.compatible_protocol_version_mask)
def _cmp_(self, other: "ProtocolVersion"):
if self.version > other.version:
return 1
elif self.version == other.version:
return 0
else:
return -1
def __gt__(self, other: "ProtocolVersion"):
return self._cmp_(other) > 0
def __lt__(self, other: "ProtocolVersion"):
return self._cmp_(other) < 0
def __eq__(self, other: "ProtocolVersion"):
return self._cmp_(other) == 0
def __ne__(self, other: "ProtocolVersion"):
return self._cmp_(other) != 0
def __ge__(self, other: "ProtocolVersion"):
return self._cmp_(other) >= 0
def __le__(self, other: "ProtocolVersion"):
return self._cmp_(other) <= 0
def is_supported(self) -> bool:
return self in ProtocolVersion._supported_protocol_versions
{% for version, features in all_features.items() %}
{% for feature in features %}
@property
def has_{{ feature | feature_name_transformer }}(self) -> bool:
return self > ProtocolVersion({{ version | encode_version }})
@staticmethod
def with_{{ feature | feature_name_transformer }}() -> "ProtocolVersion":
return ProtocolVersion({{ version | encode_version }})
{% endfor %}
{% endfor %}
PROTOCOL_VERSION_5_2 = 0x0FDB00A552000001
PROTOCOL_VERSION_6_0 = 0x0FDB00A570010001
PROTOCOL_VERSION_6_1 = 0x0FDB00B061060001
PROTOCOL_VERSION_6_2 = 0x0FDB00B062010001
PROTOCOL_VERSION_6_3 = 0x0FDB00B063010001
PROTOCOL_VERSION_7_0 = 0x0FDB00B070010001
PROTOCOL_VERSION_7_1 = 0x0FDB00B071010000
PROTOCOL_VERSION_7_2 = 0x0FDB00B072000000
PROTOCOL_VERSION_7_3 = 0x0FDB00B073000000
def _module_init():
for raw_version in (
PROTOCOL_VERSION_5_2,
PROTOCOL_VERSION_6_0,
PROTOCOL_VERSION_6_1,
PROTOCOL_VERSION_6_2,
PROTOCOL_VERSION_6_3,
PROTOCOL_VERSION_7_0,
PROTOCOL_VERSION_7_1,
PROTOCOL_VERSION_7_2,
PROTOCOL_VERSION_7_3,
):
ProtocolVersion._supported_protocol_versions.add(ProtocolVersion(raw_version))
_module_init()