API refactor
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-10-07 16:25:52 +09:00
parent 76d0d86211
commit 91c7e04474
1171 changed files with 81940 additions and 44117 deletions

View File

@@ -43,4 +43,7 @@ API_KEYS = {
40: 'ExpireDelegationToken',
41: 'DescribeDelegationToken',
42: 'DeleteGroups',
45: 'AlterPartitionReassignments',
46: 'ListPartitionReassignments',
48: 'DescribeClientQuotas',
}

View File

@@ -2,10 +2,11 @@ from __future__ import absolute_import
import abc
from kafka.vendor.six import add_metaclass
@add_metaclass(abc.ABCMeta)
class AbstractType(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def encode(cls, value): # pylint: disable=no-self-argument
pass

View File

@@ -0,0 +1,59 @@
from __future__ import absolute_import
from kafka.protocol.api import Request, Response
from kafka.protocol.types import Int16, Int32, Int64, Schema, String
class AddOffsetsToTxnResponse_v0(Response):
API_KEY = 25
API_VERSION = 0
SCHEMA = Schema(
('throttle_time_ms', Int32),
('error_code', Int16),
)
class AddOffsetsToTxnResponse_v1(Response):
API_KEY = 25
API_VERSION = 1
SCHEMA = AddOffsetsToTxnResponse_v0.SCHEMA
class AddOffsetsToTxnResponse_v2(Response):
API_KEY = 25
API_VERSION = 2
SCHEMA = AddOffsetsToTxnResponse_v1.SCHEMA
class AddOffsetsToTxnRequest_v0(Request):
API_KEY = 25
API_VERSION = 0
RESPONSE_TYPE = AddOffsetsToTxnResponse_v0
SCHEMA = Schema(
('transactional_id', String('utf-8')),
('producer_id', Int64),
('producer_epoch', Int16),
('group_id', String('utf-8')),
)
class AddOffsetsToTxnRequest_v1(Request):
API_KEY = 25
API_VERSION = 1
RESPONSE_TYPE = AddOffsetsToTxnResponse_v1
SCHEMA = AddOffsetsToTxnRequest_v0.SCHEMA
class AddOffsetsToTxnRequest_v2(Request):
API_KEY = 25
API_VERSION = 2
RESPONSE_TYPE = AddOffsetsToTxnResponse_v2
SCHEMA = AddOffsetsToTxnRequest_v1.SCHEMA
AddOffsetsToTxnRequest = [
AddOffsetsToTxnRequest_v0, AddOffsetsToTxnRequest_v1, AddOffsetsToTxnRequest_v2,
]
AddOffsetsToTxnResponse = [
AddOffsetsToTxnResponse_v0, AddOffsetsToTxnResponse_v1, AddOffsetsToTxnResponse_v2,
]

View File

@@ -0,0 +1,63 @@
from __future__ import absolute_import
from kafka.protocol.api import Request, Response
from kafka.protocol.types import Array, Int16, Int32, Int64, Schema, String
class AddPartitionsToTxnResponse_v0(Response):
API_KEY = 24
API_VERSION = 0
SCHEMA = Schema(
('throttle_time_ms', Int32),
('results', Array(
('topic', String('utf-8')),
('partitions', Array(
('partition', Int32),
('error_code', Int16))))))
class AddPartitionsToTxnResponse_v1(Response):
API_KEY = 24
API_VERSION = 1
SCHEMA = AddPartitionsToTxnResponse_v0.SCHEMA
class AddPartitionsToTxnResponse_v2(Response):
API_KEY = 24
API_VERSION = 2
SCHEMA = AddPartitionsToTxnResponse_v1.SCHEMA
class AddPartitionsToTxnRequest_v0(Request):
API_KEY = 24
API_VERSION = 0
RESPONSE_TYPE = AddPartitionsToTxnResponse_v0
SCHEMA = Schema(
('transactional_id', String('utf-8')),
('producer_id', Int64),
('producer_epoch', Int16),
('topics', Array(
('topic', String('utf-8')),
('partitions', Array(Int32)))))
class AddPartitionsToTxnRequest_v1(Request):
API_KEY = 24
API_VERSION = 1
RESPONSE_TYPE = AddPartitionsToTxnResponse_v1
SCHEMA = AddPartitionsToTxnRequest_v0.SCHEMA
class AddPartitionsToTxnRequest_v2(Request):
API_KEY = 24
API_VERSION = 2
RESPONSE_TYPE = AddPartitionsToTxnResponse_v2
SCHEMA = AddPartitionsToTxnRequest_v1.SCHEMA
AddPartitionsToTxnRequest = [
AddPartitionsToTxnRequest_v0, AddPartitionsToTxnRequest_v1, AddPartitionsToTxnRequest_v2,
]
AddPartitionsToTxnResponse = [
AddPartitionsToTxnResponse_v0, AddPartitionsToTxnResponse_v1, AddPartitionsToTxnResponse_v2,
]

View File

@@ -1,67 +1,14 @@
from __future__ import absolute_import
# enum in stdlib as of py3.4
try:
from enum import IntEnum # pylint: disable=import-error
except ImportError:
# vendored backport module
from kafka.vendor.enum34 import IntEnum
from kafka.protocol.api import Request, Response
from kafka.protocol.types import Array, Boolean, Bytes, Int8, Int16, Int32, Int64, Schema, String
class ApiVersionResponse_v0(Response):
API_KEY = 18
API_VERSION = 0
SCHEMA = Schema(
('error_code', Int16),
('api_versions', Array(
('api_key', Int16),
('min_version', Int16),
('max_version', Int16)))
)
class ApiVersionResponse_v1(Response):
API_KEY = 18
API_VERSION = 1
SCHEMA = Schema(
('error_code', Int16),
('api_versions', Array(
('api_key', Int16),
('min_version', Int16),
('max_version', Int16))),
('throttle_time_ms', Int32)
)
class ApiVersionResponse_v2(Response):
API_KEY = 18
API_VERSION = 2
SCHEMA = ApiVersionResponse_v1.SCHEMA
class ApiVersionRequest_v0(Request):
API_KEY = 18
API_VERSION = 0
RESPONSE_TYPE = ApiVersionResponse_v0
SCHEMA = Schema()
class ApiVersionRequest_v1(Request):
API_KEY = 18
API_VERSION = 1
RESPONSE_TYPE = ApiVersionResponse_v1
SCHEMA = ApiVersionRequest_v0.SCHEMA
class ApiVersionRequest_v2(Request):
API_KEY = 18
API_VERSION = 2
RESPONSE_TYPE = ApiVersionResponse_v1
SCHEMA = ApiVersionRequest_v0.SCHEMA
ApiVersionRequest = [
ApiVersionRequest_v0, ApiVersionRequest_v1, ApiVersionRequest_v2,
]
ApiVersionResponse = [
ApiVersionResponse_v0, ApiVersionResponse_v1, ApiVersionResponse_v2,
]
from kafka.protocol.types import Array, Boolean, Bytes, Int8, Int16, Int32, Int64, Schema, String, Float64, CompactString, CompactArray, TaggedFields
class CreateTopicsResponse_v0(Response):
@@ -239,6 +186,38 @@ DeleteTopicsResponse = [
]
class DeleteRecordsResponse_v0(Response):
API_KEY = 21
API_VERSION = 0
SCHEMA = Schema(
('throttle_time_ms', Int32),
('topics', Array(
('name', String('utf-8')),
('partitions', Array(
('partition_index', Int32),
('low_watermark', Int64),
('error_code', Int16))))),
)
class DeleteRecordsRequest_v0(Request):
API_KEY = 21
API_VERSION = 0
RESPONSE_TYPE = DeleteRecordsResponse_v0
SCHEMA = Schema(
('topics', Array(
('name', String('utf-8')),
('partitions', Array(
('partition_index', Int32),
('offset', Int64))))),
('timeout_ms', Int32)
)
DeleteRecordsResponse = [DeleteRecordsResponse_v0]
DeleteRecordsRequest = [DeleteRecordsRequest_v0]
class ListGroupsResponse_v0(Response):
API_KEY = 16
API_VERSION = 0
@@ -406,41 +385,6 @@ DescribeGroupsResponse = [
]
class SaslHandShakeResponse_v0(Response):
API_KEY = 17
API_VERSION = 0
SCHEMA = Schema(
('error_code', Int16),
('enabled_mechanisms', Array(String('utf-8')))
)
class SaslHandShakeResponse_v1(Response):
API_KEY = 17
API_VERSION = 1
SCHEMA = SaslHandShakeResponse_v0.SCHEMA
class SaslHandShakeRequest_v0(Request):
API_KEY = 17
API_VERSION = 0
RESPONSE_TYPE = SaslHandShakeResponse_v0
SCHEMA = Schema(
('mechanism', String('utf-8'))
)
class SaslHandShakeRequest_v1(Request):
API_KEY = 17
API_VERSION = 1
RESPONSE_TYPE = SaslHandShakeResponse_v1
SCHEMA = SaslHandShakeRequest_v0.SCHEMA
SaslHandShakeRequest = [SaslHandShakeRequest_v0, SaslHandShakeRequest_v1]
SaslHandShakeResponse = [SaslHandShakeResponse_v0, SaslHandShakeResponse_v1]
class DescribeAclsResponse_v0(Response):
API_KEY = 29
API_VERSION = 0
@@ -523,8 +467,8 @@ class DescribeAclsRequest_v2(Request):
SCHEMA = DescribeAclsRequest_v1.SCHEMA
DescribeAclsRequest = [DescribeAclsRequest_v0, DescribeAclsRequest_v1]
DescribeAclsResponse = [DescribeAclsResponse_v0, DescribeAclsResponse_v1]
DescribeAclsRequest = [DescribeAclsRequest_v0, DescribeAclsRequest_v1, DescribeAclsRequest_v2]
DescribeAclsResponse = [DescribeAclsResponse_v0, DescribeAclsResponse_v1, DescribeAclsResponse_v2]
class CreateAclsResponse_v0(Response):
API_KEY = 30
@@ -719,7 +663,7 @@ class DescribeConfigsResponse_v1(Response):
('config_names', String('utf-8')),
('config_value', String('utf-8')),
('read_only', Boolean),
('is_default', Boolean),
('config_source', Int8),
('is_sensitive', Boolean),
('config_synonyms', Array(
('config_name', String('utf-8')),
@@ -790,6 +734,47 @@ DescribeConfigsResponse = [
]
class DescribeLogDirsResponse_v0(Response):
API_KEY = 35
API_VERSION = 0
SCHEMA = Schema(
('throttle_time_ms', Int32),
('log_dirs', Array(
('error_code', Int16),
('log_dir', String('utf-8')),
('topics', Array(
('name', String('utf-8')),
('partitions', Array(
('partition_index', Int32),
('partition_size', Int64),
('offset_lag', Int64),
('is_future_key', Boolean)
))
))
))
)
class DescribeLogDirsRequest_v0(Request):
API_KEY = 35
API_VERSION = 0
RESPONSE_TYPE = DescribeLogDirsResponse_v0
SCHEMA = Schema(
('topics', Array(
('topic', String('utf-8')),
('partitions', Int32)
))
)
DescribeLogDirsResponse = [
DescribeLogDirsResponse_v0,
]
DescribeLogDirsRequest = [
DescribeLogDirsRequest_v0,
]
class SaslAuthenticateResponse_v0(Response):
API_KEY = 36
API_VERSION = 0
@@ -923,3 +908,208 @@ DeleteGroupsRequest = [
DeleteGroupsResponse = [
DeleteGroupsResponse_v0, DeleteGroupsResponse_v1
]
class DescribeClientQuotasResponse_v0(Response):
API_KEY = 48
API_VERSION = 0
SCHEMA = Schema(
('throttle_time_ms', Int32),
('error_code', Int16),
('error_message', String('utf-8')),
('entries', Array(
('entity', Array(
('entity_type', String('utf-8')),
('entity_name', String('utf-8')))),
('values', Array(
('name', String('utf-8')),
('value', Float64))))),
)
class DescribeClientQuotasRequest_v0(Request):
API_KEY = 48
API_VERSION = 0
RESPONSE_TYPE = DescribeClientQuotasResponse_v0
SCHEMA = Schema(
('components', Array(
('entity_type', String('utf-8')),
('match_type', Int8),
('match', String('utf-8')),
)),
('strict', Boolean)
)
DescribeClientQuotasRequest = [
DescribeClientQuotasRequest_v0,
]
DescribeClientQuotasResponse = [
DescribeClientQuotasResponse_v0,
]
class AlterPartitionReassignmentsResponse_v0(Response):
API_KEY = 45
API_VERSION = 0
SCHEMA = Schema(
("throttle_time_ms", Int32),
("error_code", Int16),
("error_message", CompactString("utf-8")),
("responses", CompactArray(
("name", CompactString("utf-8")),
("partitions", CompactArray(
("partition_index", Int32),
("error_code", Int16),
("error_message", CompactString("utf-8")),
("tags", TaggedFields)
)),
("tags", TaggedFields)
)),
("tags", TaggedFields)
)
FLEXIBLE_VERSION = True
class AlterPartitionReassignmentsRequest_v0(Request):
FLEXIBLE_VERSION = True
API_KEY = 45
API_VERSION = 0
RESPONSE_TYPE = AlterPartitionReassignmentsResponse_v0
SCHEMA = Schema(
("timeout_ms", Int32),
("topics", CompactArray(
("name", CompactString("utf-8")),
("partitions", CompactArray(
("partition_index", Int32),
("replicas", CompactArray(Int32)),
("tags", TaggedFields)
)),
("tags", TaggedFields)
)),
("tags", TaggedFields)
)
AlterPartitionReassignmentsRequest = [AlterPartitionReassignmentsRequest_v0]
AlterPartitionReassignmentsResponse = [AlterPartitionReassignmentsResponse_v0]
class ListPartitionReassignmentsResponse_v0(Response):
API_KEY = 46
API_VERSION = 0
SCHEMA = Schema(
("throttle_time_ms", Int32),
("error_code", Int16),
("error_message", CompactString("utf-8")),
("topics", CompactArray(
("name", CompactString("utf-8")),
("partitions", CompactArray(
("partition_index", Int32),
("replicas", CompactArray(Int32)),
("adding_replicas", CompactArray(Int32)),
("removing_replicas", CompactArray(Int32)),
("tags", TaggedFields)
)),
("tags", TaggedFields)
)),
("tags", TaggedFields)
)
FLEXIBLE_VERSION = True
class ListPartitionReassignmentsRequest_v0(Request):
FLEXIBLE_VERSION = True
API_KEY = 46
API_VERSION = 0
RESPONSE_TYPE = ListPartitionReassignmentsResponse_v0
SCHEMA = Schema(
("timeout_ms", Int32),
("topics", CompactArray(
("name", CompactString("utf-8")),
("partition_index", CompactArray(Int32)),
("tags", TaggedFields)
)),
("tags", TaggedFields)
)
ListPartitionReassignmentsRequest = [ListPartitionReassignmentsRequest_v0]
ListPartitionReassignmentsResponse = [ListPartitionReassignmentsResponse_v0]
class ElectLeadersResponse_v0(Response):
API_KEY = 43
API_VERSION = 1
SCHEMA = Schema(
('throttle_time_ms', Int32),
('error_code', Int16),
('replication_election_results', Array(
('topic', String('utf-8')),
('partition_result', Array(
('partition_id', Int32),
('error_code', Int16),
('error_message', String('utf-8'))
))
))
)
class ElectLeadersRequest_v0(Request):
API_KEY = 43
API_VERSION = 1
RESPONSE_TYPE = ElectLeadersResponse_v0
SCHEMA = Schema(
('election_type', Int8),
('topic_partitions', Array(
('topic', String('utf-8')),
('partition_ids', Array(Int32))
)),
('timeout', Int32),
)
class ElectLeadersResponse_v1(Response):
API_KEY = 43
API_VERSION = 1
SCHEMA = Schema(
('throttle_time_ms', Int32),
('error_code', Int16),
('replication_election_results', Array(
('topic', String('utf-8')),
('partition_result', Array(
('partition_id', Int32),
('error_code', Int16),
('error_message', String('utf-8'))
))
))
)
class ElectLeadersRequest_v1(Request):
API_KEY = 43
API_VERSION = 1
RESPONSE_TYPE = ElectLeadersResponse_v1
SCHEMA = Schema(
('election_type', Int8),
('topic_partitions', Array(
('topic', String('utf-8')),
('partition_ids', Array(Int32))
)),
('timeout', Int32),
)
class ElectionType(IntEnum):
""" Leader election type
"""
PREFERRED = 0,
UNCLEAN = 1
ElectLeadersRequest = [ElectLeadersRequest_v0, ElectLeadersRequest_v1]
ElectLeadersResponse = [ElectLeadersResponse_v0, ElectLeadersResponse_v1]

View File

@@ -3,7 +3,9 @@ from __future__ import absolute_import
import abc
from kafka.protocol.struct import Struct
from kafka.protocol.types import Int16, Int32, String, Schema, Array
from kafka.protocol.types import Int16, Int32, String, Schema, Array, TaggedFields
from kafka.vendor.six import add_metaclass
class RequestHeader(Struct):
@@ -20,8 +22,38 @@ class RequestHeader(Struct):
)
class RequestHeaderV2(Struct):
# Flexible response / request headers end in field buffer
SCHEMA = Schema(
('api_key', Int16),
('api_version', Int16),
('correlation_id', Int32),
('client_id', String('utf-8')),
('tags', TaggedFields),
)
def __init__(self, request, correlation_id=0, client_id='kafka-python', tags=None):
super(RequestHeaderV2, self).__init__(
request.API_KEY, request.API_VERSION, correlation_id, client_id, tags or {}
)
class ResponseHeader(Struct):
SCHEMA = Schema(
('correlation_id', Int32),
)
class ResponseHeaderV2(Struct):
SCHEMA = Schema(
('correlation_id', Int32),
('tags', TaggedFields),
)
@add_metaclass(abc.ABCMeta)
class Request(Struct):
__metaclass__ = abc.ABCMeta
FLEXIBLE_VERSION = False
@abc.abstractproperty
def API_KEY(self):
@@ -50,9 +82,15 @@ class Request(Struct):
def to_object(self):
return _to_object(self.SCHEMA, self)
def build_header(self, correlation_id, client_id):
if self.FLEXIBLE_VERSION:
return RequestHeaderV2(self, correlation_id=correlation_id, client_id=client_id)
return RequestHeader(self, correlation_id=correlation_id, client_id=client_id)
@add_metaclass(abc.ABCMeta)
class Response(Struct):
__metaclass__ = abc.ABCMeta
FLEXIBLE_VERSION = False
@abc.abstractproperty
def API_KEY(self):
@@ -72,6 +110,12 @@ class Response(Struct):
def to_object(self):
return _to_object(self.SCHEMA, self)
@classmethod
def parse_header(cls, read_buffer):
if cls.FLEXIBLE_VERSION:
return ResponseHeaderV2.decode(read_buffer)
return ResponseHeader.decode(read_buffer)
def _to_object(schema, data):
obj = {}

View File

@@ -0,0 +1,134 @@
from __future__ import absolute_import
from io import BytesIO
from kafka.protocol.api import Request, Response
from kafka.protocol.types import Array, CompactArray, CompactString, Int16, Int32, Schema, TaggedFields
class BaseApiVersionsResponse(Response):
API_KEY = 18
API_VERSION = 0
SCHEMA = Schema(
('error_code', Int16),
('api_versions', Array(
('api_key', Int16),
('min_version', Int16),
('max_version', Int16)))
)
@classmethod
def decode(cls, data):
if isinstance(data, bytes):
data = BytesIO(data)
# Check error_code, decode as v0 if any error
curr = data.tell()
err = Int16.decode(data)
data.seek(curr)
if err != 0:
return ApiVersionsResponse_v0.decode(data)
return super(BaseApiVersionsResponse, cls).decode(data)
class ApiVersionsResponse_v0(Response):
API_KEY = 18
API_VERSION = 0
SCHEMA = Schema(
('error_code', Int16),
('api_versions', Array(
('api_key', Int16),
('min_version', Int16),
('max_version', Int16)))
)
class ApiVersionsResponse_v1(BaseApiVersionsResponse):
API_KEY = 18
API_VERSION = 1
SCHEMA = Schema(
('error_code', Int16),
('api_versions', Array(
('api_key', Int16),
('min_version', Int16),
('max_version', Int16))),
('throttle_time_ms', Int32)
)
class ApiVersionsResponse_v2(BaseApiVersionsResponse):
API_KEY = 18
API_VERSION = 2
SCHEMA = ApiVersionsResponse_v1.SCHEMA
class ApiVersionsResponse_v3(BaseApiVersionsResponse):
API_KEY = 18
API_VERSION = 3
SCHEMA = Schema(
('error_code', Int16),
('api_versions', CompactArray(
('api_key', Int16),
('min_version', Int16),
('max_version', Int16),
('_tagged_fields', TaggedFields))),
('throttle_time_ms', Int32),
('_tagged_fields', TaggedFields)
)
# Note: ApiVersions Response does not send FLEXIBLE_VERSION header!
class ApiVersionsResponse_v4(BaseApiVersionsResponse):
API_KEY = 18
API_VERSION = 4
SCHEMA = ApiVersionsResponse_v3.SCHEMA
class ApiVersionsRequest_v0(Request):
API_KEY = 18
API_VERSION = 0
RESPONSE_TYPE = ApiVersionsResponse_v0
SCHEMA = Schema()
class ApiVersionsRequest_v1(Request):
API_KEY = 18
API_VERSION = 1
RESPONSE_TYPE = ApiVersionsResponse_v1
SCHEMA = ApiVersionsRequest_v0.SCHEMA
class ApiVersionsRequest_v2(Request):
API_KEY = 18
API_VERSION = 2
RESPONSE_TYPE = ApiVersionsResponse_v2
SCHEMA = ApiVersionsRequest_v1.SCHEMA
class ApiVersionsRequest_v3(Request):
API_KEY = 18
API_VERSION = 3
RESPONSE_TYPE = ApiVersionsResponse_v3
SCHEMA = Schema(
('client_software_name', CompactString('utf-8')),
('client_software_version', CompactString('utf-8')),
('_tagged_fields', TaggedFields)
)
FLEXIBLE_VERSION = True
class ApiVersionsRequest_v4(Request):
API_KEY = 18
API_VERSION = 4
RESPONSE_TYPE = ApiVersionsResponse_v4
SCHEMA = ApiVersionsRequest_v3.SCHEMA
FLEXIBLE_VERSION = True
ApiVersionsRequest = [
ApiVersionsRequest_v0, ApiVersionsRequest_v1, ApiVersionsRequest_v2,
ApiVersionsRequest_v3, ApiVersionsRequest_v4,
]
ApiVersionsResponse = [
ApiVersionsResponse_v0, ApiVersionsResponse_v1, ApiVersionsResponse_v2,
ApiVersionsResponse_v3, ApiVersionsResponse_v4,
]

View File

@@ -0,0 +1,68 @@
BROKER_API_VERSIONS = {
# api_versions responses prior to (0, 10) are synthesized for compatibility
(0, 8, 0): {0: (0, 0), 1: (0, 0), 2: (0, 0), 3: (0, 0)},
# adds offset commit + fetch
(0, 8, 1): {0: (0, 0), 1: (0, 0), 2: (0, 0), 3: (0, 0), 8: (0, 0), 9: (0, 0)},
# adds find coordinator
(0, 8, 2): {0: (0, 0), 1: (0, 0), 2: (0, 0), 3: (0, 0), 8: (0, 1), 9: (0, 1), 10: (0, 0)},
# adds group management (join/sync/leave/heartbeat)
(0, 9): {0: (0, 1), 1: (0, 1), 2: (0, 0), 3: (0, 0), 8: (0, 2), 9: (0, 1), 10: (0, 0), 11: (0, 0), 12: (0, 0), 13: (0, 0), 14: (0, 0), 15: (0, 0), 16: (0, 0)},
# adds message format v1, sasl, and api versions api
(0, 10, 0): {0: (0, 2), 1: (0, 2), 2: (0, 0), 3: (0, 1), 4: (0, 0), 5: (0, 0), 6: (0, 2), 7: (1, 1), 8: (0, 2), 9: (0, 1), 10: (0, 0), 11: (0, 0), 12: (0, 0), 13: (0, 0), 14: (0, 0), 15: (0, 0), 16: (0, 0), 17: (0, 0), 18: (0, 0)},
# All data below is copied from brokers via api_versions_response (see make servers/*/api_versions)
# adds admin apis create/delete topics, and bumps fetch/listoffsets/metadata/joingroup
(0, 10, 1): {0: (0, 2), 1: (0, 3), 2: (0, 1), 3: (0, 2), 4: (0, 0), 5: (0, 0), 6: (0, 2), 7: (1, 1), 8: (0, 2), 9: (0, 1), 10: (0, 0), 11: (0, 1), 12: (0, 0), 13: (0, 0), 14: (0, 0), 15: (0, 0), 16: (0, 0), 17: (0, 0), 18: (0, 0), 19: (0, 0), 20: (0, 0)},
# bumps offsetfetch/create-topics
(0, 10, 2): {0: (0, 2), 1: (0, 3), 2: (0, 1), 3: (0, 2), 4: (0, 0), 5: (0, 0), 6: (0, 3), 7: (1, 1), 8: (0, 2), 9: (0, 2), 10: (0, 0), 11: (0, 1), 12: (0, 0), 13: (0, 0), 14: (0, 0), 15: (0, 0), 16: (0, 0), 17: (0, 0), 18: (0, 0), 19: (0, 1), 20: (0, 0)},
# Adds message format v2, and more admin apis (describe/create/delete acls, describe/alter configs, etc)
(0, 11): {0: (0, 3), 1: (0, 5), 2: (0, 2), 3: (0, 4), 4: (0, 0), 5: (0, 0), 6: (0, 3), 7: (1, 1), 8: (0, 3), 9: (0, 3), 10: (0, 1), 11: (0, 2), 12: (0, 1), 13: (0, 1), 14: (0, 1), 15: (0, 1), 16: (0, 1), 17: (0, 0), 18: (0, 1), 19: (0, 2), 20: (0, 1), 21: (0, 0), 22: (0, 0), 23: (0, 0), 24: (0, 0), 25: (0, 0), 26: (0, 0), 27: (0, 0), 28: (0, 0), 29: (0, 0), 30: (0, 0), 31: (0, 0), 32: (0, 0), 33: (0, 0)},
# Adds Sasl Authenticate, and additional admin apis (describe/alter log dirs, etc)
(1, 0): {0: (0, 5), 1: (0, 6), 2: (0, 2), 3: (0, 5), 4: (0, 1), 5: (0, 0), 6: (0, 4), 7: (0, 1), 8: (0, 3), 9: (0, 3), 10: (0, 1), 11: (0, 2), 12: (0, 1), 13: (0, 1), 14: (0, 1), 15: (0, 1), 16: (0, 1), 17: (0, 1), 18: (0, 1), 19: (0, 2), 20: (0, 1), 21: (0, 0), 22: (0, 0), 23: (0, 0), 24: (0, 0), 25: (0, 0), 26: (0, 0), 27: (0, 0), 28: (0, 0), 29: (0, 0), 30: (0, 0), 31: (0, 0), 32: (0, 0), 33: (0, 0), 34: (0, 0), 35: (0, 0), 36: (0, 0), 37: (0, 0)},
(1, 1): {0: (0, 5), 1: (0, 7), 2: (0, 2), 3: (0, 5), 4: (0, 1), 5: (0, 0), 6: (0, 4), 7: (0, 1), 8: (0, 3), 9: (0, 3), 10: (0, 1), 11: (0, 2), 12: (0, 1), 13: (0, 1), 14: (0, 1), 15: (0, 1), 16: (0, 1), 17: (0, 1), 18: (0, 1), 19: (0, 2), 20: (0, 1), 21: (0, 0), 22: (0, 0), 23: (0, 0), 24: (0, 0), 25: (0, 0), 26: (0, 0), 27: (0, 0), 28: (0, 0), 29: (0, 0), 30: (0, 0), 31: (0, 0), 32: (0, 1), 33: (0, 0), 34: (0, 0), 35: (0, 0), 36: (0, 0), 37: (0, 0), 38: (0, 0), 39: (0, 0), 40: (0, 0), 41: (0, 0), 42: (0, 0)},
(2, 0): {0: (0, 6), 1: (0, 8), 2: (0, 3), 3: (0, 6), 4: (0, 1), 5: (0, 0), 6: (0, 4), 7: (0, 1), 8: (0, 4), 9: (0, 4), 10: (0, 2), 11: (0, 3), 12: (0, 2), 13: (0, 2), 14: (0, 2), 15: (0, 2), 16: (0, 2), 17: (0, 1), 18: (0, 2), 19: (0, 3), 20: (0, 2), 21: (0, 1), 22: (0, 1), 23: (0, 1), 24: (0, 1), 25: (0, 1), 26: (0, 1), 27: (0, 0), 28: (0, 1), 29: (0, 1), 30: (0, 1), 31: (0, 1), 32: (0, 2), 33: (0, 1), 34: (0, 1), 35: (0, 1), 36: (0, 0), 37: (0, 1), 38: (0, 1), 39: (0, 1), 40: (0, 1), 41: (0, 1), 42: (0, 1)},
(2, 1): {0: (0, 7), 1: (0, 10), 2: (0, 4), 3: (0, 7), 4: (0, 1), 5: (0, 0), 6: (0, 4), 7: (0, 1), 8: (0, 6), 9: (0, 5), 10: (0, 2), 11: (0, 3), 12: (0, 2), 13: (0, 2), 14: (0, 2), 15: (0, 2), 16: (0, 2), 17: (0, 1), 18: (0, 2), 19: (0, 3), 20: (0, 3), 21: (0, 1), 22: (0, 1), 23: (0, 2), 24: (0, 1), 25: (0, 1), 26: (0, 1), 27: (0, 0), 28: (0, 2), 29: (0, 1), 30: (0, 1), 31: (0, 1), 32: (0, 2), 33: (0, 1), 34: (0, 1), 35: (0, 1), 36: (0, 0), 37: (0, 1), 38: (0, 1), 39: (0, 1), 40: (0, 1), 41: (0, 1), 42: (0, 1)},
(2, 2): {0: (0, 7), 1: (0, 10), 2: (0, 5), 3: (0, 7), 4: (0, 2), 5: (0, 1), 6: (0, 5), 7: (0, 2), 8: (0, 6), 9: (0, 5), 10: (0, 2), 11: (0, 4), 12: (0, 2), 13: (0, 2), 14: (0, 2), 15: (0, 2), 16: (0, 2), 17: (0, 1), 18: (0, 2), 19: (0, 3), 20: (0, 3), 21: (0, 1), 22: (0, 1), 23: (0, 2), 24: (0, 1), 25: (0, 1), 26: (0, 1), 27: (0, 0), 28: (0, 2), 29: (0, 1), 30: (0, 1), 31: (0, 1), 32: (0, 2), 33: (0, 1), 34: (0, 1), 35: (0, 1), 36: (0, 1), 37: (0, 1), 38: (0, 1), 39: (0, 1), 40: (0, 1), 41: (0, 1), 42: (0, 1), 43: (0, 0)},
(2, 3): {0: (0, 7), 1: (0, 11), 2: (0, 5), 3: (0, 8), 4: (0, 2), 5: (0, 1), 6: (0, 5), 7: (0, 2), 8: (0, 7), 9: (0, 5), 10: (0, 2), 11: (0, 5), 12: (0, 3), 13: (0, 2), 14: (0, 3), 15: (0, 3), 16: (0, 2), 17: (0, 1), 18: (0, 2), 19: (0, 3), 20: (0, 3), 21: (0, 1), 22: (0, 1), 23: (0, 3), 24: (0, 1), 25: (0, 1), 26: (0, 1), 27: (0, 0), 28: (0, 2), 29: (0, 1), 30: (0, 1), 31: (0, 1), 32: (0, 2), 33: (0, 1), 34: (0, 1), 35: (0, 1), 36: (0, 1), 37: (0, 1), 38: (0, 1), 39: (0, 1), 40: (0, 1), 41: (0, 1), 42: (0, 1), 43: (0, 0), 44: (0, 0)},
(2, 4): {0: (0, 8), 1: (0, 11), 2: (0, 5), 3: (0, 9), 4: (0, 4), 5: (0, 2), 6: (0, 6), 7: (0, 3), 8: (0, 8), 9: (0, 6), 10: (0, 3), 11: (0, 6), 12: (0, 4), 13: (0, 4), 14: (0, 4), 15: (0, 5), 16: (0, 3), 17: (0, 1), 18: (0, 3), 19: (0, 5), 20: (0, 4), 21: (0, 1), 22: (0, 2), 23: (0, 3), 24: (0, 1), 25: (0, 1), 26: (0, 1), 27: (0, 0), 28: (0, 2), 29: (0, 1), 30: (0, 1), 31: (0, 1), 32: (0, 2), 33: (0, 1), 34: (0, 1), 35: (0, 1), 36: (0, 1), 37: (0, 1), 38: (0, 2), 39: (0, 1), 40: (0, 1), 41: (0, 1), 42: (0, 2), 43: (0, 2), 44: (0, 1), 45: (0, 0), 46: (0, 0), 47: (0, 0)},
(2, 5): {0: (0, 8), 1: (0, 11), 2: (0, 5), 3: (0, 9), 4: (0, 4), 5: (0, 2), 6: (0, 6), 7: (0, 3), 8: (0, 8), 9: (0, 7), 10: (0, 3), 11: (0, 7), 12: (0, 4), 13: (0, 4), 14: (0, 5), 15: (0, 5), 16: (0, 3), 17: (0, 1), 18: (0, 3), 19: (0, 5), 20: (0, 4), 21: (0, 1), 22: (0, 3), 23: (0, 3), 24: (0, 1), 25: (0, 1), 26: (0, 1), 27: (0, 0), 28: (0, 3), 29: (0, 2), 30: (0, 2), 31: (0, 2), 32: (0, 2), 33: (0, 1), 34: (0, 1), 35: (0, 1), 36: (0, 2), 37: (0, 2), 38: (0, 2), 39: (0, 2), 40: (0, 2), 41: (0, 2), 42: (0, 2), 43: (0, 2), 44: (0, 1), 45: (0, 0), 46: (0, 0), 47: (0, 0)},
(2, 6): {0: (0, 8), 1: (0, 11), 2: (0, 5), 3: (0, 9), 4: (0, 4), 5: (0, 3), 6: (0, 6), 7: (0, 3), 8: (0, 8), 9: (0, 7), 10: (0, 3), 11: (0, 7), 12: (0, 4), 13: (0, 4), 14: (0, 5), 15: (0, 5), 16: (0, 4), 17: (0, 1), 18: (0, 3), 19: (0, 5), 20: (0, 4), 21: (0, 2), 22: (0, 3), 23: (0, 3), 24: (0, 1), 25: (0, 1), 26: (0, 1), 27: (0, 0), 28: (0, 3), 29: (0, 2), 30: (0, 2), 31: (0, 2), 32: (0, 3), 33: (0, 1), 34: (0, 1), 35: (0, 2), 36: (0, 2), 37: (0, 2), 38: (0, 2), 39: (0, 2), 40: (0, 2), 41: (0, 2), 42: (0, 2), 43: (0, 2), 44: (0, 1), 45: (0, 0), 46: (0, 0), 47: (0, 0), 48: (0, 0), 49: (0, 0)},
(2, 7): {0: (0, 8), 1: (0, 12), 2: (0, 5), 3: (0, 9), 4: (0, 4), 5: (0, 3), 6: (0, 6), 7: (0, 3), 8: (0, 8), 9: (0, 7), 10: (0, 3), 11: (0, 7), 12: (0, 4), 13: (0, 4), 14: (0, 5), 15: (0, 5), 16: (0, 4), 17: (0, 1), 18: (0, 3), 19: (0, 6), 20: (0, 5), 21: (0, 2), 22: (0, 4), 23: (0, 3), 24: (0, 2), 25: (0, 2), 26: (0, 2), 27: (0, 0), 28: (0, 3), 29: (0, 2), 30: (0, 2), 31: (0, 2), 32: (0, 3), 33: (0, 1), 34: (0, 1), 35: (0, 2), 36: (0, 2), 37: (0, 3), 38: (0, 2), 39: (0, 2), 40: (0, 2), 41: (0, 2), 42: (0, 2), 43: (0, 2), 44: (0, 1), 45: (0, 0), 46: (0, 0), 47: (0, 0), 48: (0, 0), 49: (0, 0), 50: (0, 0), 51: (0, 0), 56: (0, 0), 57: (0, 0)},
(2, 8): {0: (0, 9), 1: (0, 12), 2: (0, 6), 3: (0, 11), 4: (0, 5), 5: (0, 3), 6: (0, 7), 7: (0, 3), 8: (0, 8), 9: (0, 7), 10: (0, 3), 11: (0, 7), 12: (0, 4), 13: (0, 4), 14: (0, 5), 15: (0, 5), 16: (0, 4), 17: (0, 1), 18: (0, 3), 19: (0, 7), 20: (0, 6), 21: (0, 2), 22: (0, 4), 23: (0, 4), 24: (0, 3), 25: (0, 3), 26: (0, 3), 27: (0, 1), 28: (0, 3), 29: (0, 2), 30: (0, 2), 31: (0, 2), 32: (0, 4), 33: (0, 2), 34: (0, 2), 35: (0, 2), 36: (0, 2), 37: (0, 3), 38: (0, 2), 39: (0, 2), 40: (0, 2), 41: (0, 2), 42: (0, 2), 43: (0, 2), 44: (0, 1), 45: (0, 0), 46: (0, 0), 47: (0, 0), 48: (0, 1), 49: (0, 1), 50: (0, 0), 51: (0, 0), 56: (0, 0), 57: (0, 0), 60: (0, 0), 61: (0, 0)},
(3, 0): {0: (0, 9), 1: (0, 12), 2: (0, 7), 3: (0, 11), 4: (0, 5), 5: (0, 3), 6: (0, 7), 7: (0, 3), 8: (0, 8), 9: (0, 8), 10: (0, 4), 11: (0, 7), 12: (0, 4), 13: (0, 4), 14: (0, 5), 15: (0, 5), 16: (0, 4), 17: (0, 1), 18: (0, 3), 19: (0, 7), 20: (0, 6), 21: (0, 2), 22: (0, 4), 23: (0, 4), 24: (0, 3), 25: (0, 3), 26: (0, 3), 27: (0, 1), 28: (0, 3), 29: (0, 2), 30: (0, 2), 31: (0, 2), 32: (0, 4), 33: (0, 2), 34: (0, 2), 35: (0, 2), 36: (0, 2), 37: (0, 3), 38: (0, 2), 39: (0, 2), 40: (0, 2), 41: (0, 2), 42: (0, 2), 43: (0, 2), 44: (0, 1), 45: (0, 0), 46: (0, 0), 47: (0, 0), 48: (0, 1), 49: (0, 1), 50: (0, 0), 51: (0, 0), 56: (0, 0), 57: (0, 0), 60: (0, 0), 61: (0, 0), 65: (0, 0), 66: (0, 0), 67: (0, 0)},
(3, 1): {0: (0, 9), 1: (0, 13), 2: (0, 7), 3: (0, 12), 4: (0, 5), 5: (0, 3), 6: (0, 7), 7: (0, 3), 8: (0, 8), 9: (0, 8), 10: (0, 4), 11: (0, 7), 12: (0, 4), 13: (0, 4), 14: (0, 5), 15: (0, 5), 16: (0, 4), 17: (0, 1), 18: (0, 3), 19: (0, 7), 20: (0, 6), 21: (0, 2), 22: (0, 4), 23: (0, 4), 24: (0, 3), 25: (0, 3), 26: (0, 3), 27: (0, 1), 28: (0, 3), 29: (0, 2), 30: (0, 2), 31: (0, 2), 32: (0, 4), 33: (0, 2), 34: (0, 2), 35: (0, 2), 36: (0, 2), 37: (0, 3), 38: (0, 2), 39: (0, 2), 40: (0, 2), 41: (0, 2), 42: (0, 2), 43: (0, 2), 44: (0, 1), 45: (0, 0), 46: (0, 0), 47: (0, 0), 48: (0, 1), 49: (0, 1), 50: (0, 0), 51: (0, 0), 56: (0, 0), 57: (0, 0), 60: (0, 0), 61: (0, 0), 65: (0, 0), 66: (0, 0), 67: (0, 0)},
(3, 2): {0: (0, 9), 1: (0, 13), 2: (0, 7), 3: (0, 12), 4: (0, 6), 5: (0, 3), 6: (0, 7), 7: (0, 3), 8: (0, 8), 9: (0, 8), 10: (0, 4), 11: (0, 9), 12: (0, 4), 13: (0, 5), 14: (0, 5), 15: (0, 5), 16: (0, 4), 17: (0, 1), 18: (0, 3), 19: (0, 7), 20: (0, 6), 21: (0, 2), 22: (0, 4), 23: (0, 4), 24: (0, 3), 25: (0, 3), 26: (0, 3), 27: (0, 1), 28: (0, 3), 29: (0, 2), 30: (0, 2), 31: (0, 2), 32: (0, 4), 33: (0, 2), 34: (0, 2), 35: (0, 3), 36: (0, 2), 37: (0, 3), 38: (0, 2), 39: (0, 2), 40: (0, 2), 41: (0, 2), 42: (0, 2), 43: (0, 2), 44: (0, 1), 45: (0, 0), 46: (0, 0), 47: (0, 0), 48: (0, 1), 49: (0, 1), 50: (0, 0), 51: (0, 0), 56: (0, 1), 57: (0, 0), 60: (0, 0), 61: (0, 0), 65: (0, 0), 66: (0, 0), 67: (0, 0)},
(3, 3): {0: (0, 9), 1: (0, 13), 2: (0, 7), 3: (0, 12), 4: (0, 6), 5: (0, 3), 6: (0, 7), 7: (0, 3), 8: (0, 8), 9: (0, 8), 10: (0, 4), 11: (0, 9), 12: (0, 4), 13: (0, 5), 14: (0, 5), 15: (0, 5), 16: (0, 4), 17: (0, 1), 18: (0, 3), 19: (0, 7), 20: (0, 6), 21: (0, 2), 22: (0, 4), 23: (0, 4), 24: (0, 3), 25: (0, 3), 26: (0, 3), 27: (0, 1), 28: (0, 3), 29: (0, 3), 30: (0, 3), 31: (0, 3), 32: (0, 4), 33: (0, 2), 34: (0, 2), 35: (0, 4), 36: (0, 2), 37: (0, 3), 38: (0, 3), 39: (0, 2), 40: (0, 2), 41: (0, 3), 42: (0, 2), 43: (0, 2), 44: (0, 1), 45: (0, 0), 46: (0, 0), 47: (0, 0), 48: (0, 1), 49: (0, 1), 50: (0, 0), 51: (0, 0), 56: (0, 2), 57: (0, 1), 60: (0, 0), 61: (0, 0), 65: (0, 0), 66: (0, 0), 67: (0, 0)},
(3, 4): {0: (0, 9), 1: (0, 13), 2: (0, 7), 3: (0, 12), 4: (0, 7), 5: (0, 4), 6: (0, 8), 7: (0, 3), 8: (0, 8), 9: (0, 8), 10: (0, 4), 11: (0, 9), 12: (0, 4), 13: (0, 5), 14: (0, 5), 15: (0, 5), 16: (0, 4), 17: (0, 1), 18: (0, 3), 19: (0, 7), 20: (0, 6), 21: (0, 2), 22: (0, 4), 23: (0, 4), 24: (0, 3), 25: (0, 3), 26: (0, 3), 27: (0, 1), 28: (0, 3), 29: (0, 3), 30: (0, 3), 31: (0, 3), 32: (0, 4), 33: (0, 2), 34: (0, 2), 35: (0, 4), 36: (0, 2), 37: (0, 3), 38: (0, 3), 39: (0, 2), 40: (0, 2), 41: (0, 3), 42: (0, 2), 43: (0, 2), 44: (0, 1), 45: (0, 0), 46: (0, 0), 47: (0, 0), 48: (0, 1), 49: (0, 1), 50: (0, 0), 51: (0, 0), 56: (0, 2), 57: (0, 1), 58: (0, 0), 60: (0, 0), 61: (0, 0), 65: (0, 0), 66: (0, 0), 67: (0, 0)},
(3, 5): {0: (0, 9), 1: (0, 15), 2: (0, 8), 3: (0, 12), 4: (0, 7), 5: (0, 4), 6: (0, 8), 7: (0, 3), 8: (0, 8), 9: (0, 8), 10: (0, 4), 11: (0, 9), 12: (0, 4), 13: (0, 5), 14: (0, 5), 15: (0, 5), 16: (0, 4), 17: (0, 1), 18: (0, 3), 19: (0, 7), 20: (0, 6), 21: (0, 2), 22: (0, 4), 23: (0, 4), 24: (0, 3), 25: (0, 3), 26: (0, 3), 27: (0, 1), 28: (0, 3), 29: (0, 3), 30: (0, 3), 31: (0, 3), 32: (0, 4), 33: (0, 2), 34: (0, 2), 35: (0, 4), 36: (0, 2), 37: (0, 3), 38: (0, 3), 39: (0, 2), 40: (0, 2), 41: (0, 3), 42: (0, 2), 43: (0, 2), 44: (0, 1), 45: (0, 0), 46: (0, 0), 47: (0, 0), 48: (0, 1), 49: (0, 1), 50: (0, 0), 51: (0, 0), 56: (0, 3), 57: (0, 1), 58: (0, 0), 60: (0, 0), 61: (0, 0), 65: (0, 0), 66: (0, 0), 67: (0, 0)},
(3, 6): {0: (0, 9), 1: (0, 15), 2: (0, 8), 3: (0, 12), 4: (0, 7), 5: (0, 4), 6: (0, 8), 7: (0, 3), 8: (0, 8), 9: (0, 8), 10: (0, 4), 11: (0, 9), 12: (0, 4), 13: (0, 5), 14: (0, 5), 15: (0, 5), 16: (0, 4), 17: (0, 1), 18: (0, 3), 19: (0, 7), 20: (0, 6), 21: (0, 2), 22: (0, 4), 23: (0, 4), 24: (0, 4), 25: (0, 3), 26: (0, 3), 27: (0, 1), 28: (0, 3), 29: (0, 3), 30: (0, 3), 31: (0, 3), 32: (0, 4), 33: (0, 2), 34: (0, 2), 35: (0, 4), 36: (0, 2), 37: (0, 3), 38: (0, 3), 39: (0, 2), 40: (0, 2), 41: (0, 3), 42: (0, 2), 43: (0, 2), 44: (0, 1), 45: (0, 0), 46: (0, 0), 47: (0, 0), 48: (0, 1), 49: (0, 1), 50: (0, 0), 51: (0, 0), 56: (0, 3), 57: (0, 1), 58: (0, 0), 60: (0, 0), 61: (0, 0), 65: (0, 0), 66: (0, 0), 67: (0, 0)},
(3, 7): {0: (0, 10), 1: (0, 16), 2: (0, 8), 3: (0, 12), 4: (0, 7), 5: (0, 4), 6: (0, 8), 7: (0, 3), 8: (0, 9), 9: (0, 9), 10: (0, 4), 11: (0, 9), 12: (0, 4), 13: (0, 5), 14: (0, 5), 15: (0, 5), 16: (0, 4), 17: (0, 1), 18: (0, 3), 19: (0, 7), 20: (0, 6), 21: (0, 2), 22: (0, 4), 23: (0, 4), 24: (0, 4), 25: (0, 3), 26: (0, 3), 27: (0, 1), 28: (0, 3), 29: (0, 3), 30: (0, 3), 31: (0, 3), 32: (0, 4), 33: (0, 2), 34: (0, 2), 35: (0, 4), 36: (0, 2), 37: (0, 3), 38: (0, 3), 39: (0, 2), 40: (0, 2), 41: (0, 3), 42: (0, 2), 43: (0, 2), 44: (0, 1), 45: (0, 0), 46: (0, 0), 47: (0, 0), 48: (0, 1), 49: (0, 1), 50: (0, 0), 51: (0, 0), 56: (0, 3), 57: (0, 1), 58: (0, 0), 60: (0, 1), 61: (0, 0), 65: (0, 0), 66: (0, 0), 67: (0, 0), 68: (0, 0)},
(3, 8): {0: (0, 11), 1: (0, 16), 2: (0, 8), 3: (0, 12), 4: (0, 7), 5: (0, 4), 6: (0, 8), 7: (0, 3), 8: (0, 9), 9: (0, 9), 10: (0, 5), 11: (0, 9), 12: (0, 4), 13: (0, 5), 14: (0, 5), 15: (0, 5), 16: (0, 5), 17: (0, 1), 18: (0, 3), 19: (0, 7), 20: (0, 6), 21: (0, 2), 22: (0, 5), 23: (0, 4), 24: (0, 5), 25: (0, 4), 26: (0, 4), 27: (0, 1), 28: (0, 4), 29: (0, 3), 30: (0, 3), 31: (0, 3), 32: (0, 4), 33: (0, 2), 34: (0, 2), 35: (0, 4), 36: (0, 2), 37: (0, 3), 38: (0, 3), 39: (0, 2), 40: (0, 2), 41: (0, 3), 42: (0, 2), 43: (0, 2), 44: (0, 1), 45: (0, 0), 46: (0, 0), 47: (0, 0), 48: (0, 1), 49: (0, 1), 50: (0, 0), 51: (0, 0), 56: (0, 3), 57: (0, 1), 58: (0, 0), 60: (0, 1), 61: (0, 0), 65: (0, 0), 66: (0, 1), 67: (0, 0), 68: (0, 0), 69: (0, 0)},
(3, 9): {0: (0, 11), 1: (0, 17), 2: (0, 9), 3: (0, 12), 4: (0, 7), 5: (0, 4), 6: (0, 8), 7: (0, 3), 8: (0, 9), 9: (0, 9), 10: (0, 6), 11: (0, 9), 12: (0, 4), 13: (0, 5), 14: (0, 5), 15: (0, 5), 16: (0, 5), 17: (0, 1), 18: (0, 4), 19: (0, 7), 20: (0, 6), 21: (0, 2), 22: (0, 5), 23: (0, 4), 24: (0, 5), 25: (0, 4), 26: (0, 4), 27: (0, 1), 28: (0, 4), 29: (0, 3), 30: (0, 3), 31: (0, 3), 32: (0, 4), 33: (0, 2), 34: (0, 2), 35: (0, 4), 36: (0, 2), 37: (0, 3), 38: (0, 3), 39: (0, 2), 40: (0, 2), 41: (0, 3), 42: (0, 2), 43: (0, 2), 44: (0, 1), 45: (0, 0), 46: (0, 0), 47: (0, 0), 48: (0, 1), 49: (0, 1), 50: (0, 0), 51: (0, 0), 56: (0, 3), 57: (0, 1), 58: (0, 0), 60: (0, 1), 61: (0, 0), 65: (0, 0), 66: (0, 1), 67: (0, 0), 68: (0, 0), 69: (0, 0)},
(4, 0): {0: (0, 12), 1: (4, 17), 2: (1, 10), 3: (0, 13), 8: (2, 9), 9: (1, 9), 10: (0, 6), 11: (2, 9), 12: (0, 4), 13: (0, 5), 14: (0, 5), 15: (0, 6), 16: (0, 5), 17: (0, 1), 18: (0, 4), 19: (2, 7), 20: (1, 6), 21: (0, 2), 22: (0, 5), 23: (2, 4), 24: (0, 5), 25: (0, 4), 26: (0, 5), 27: (1, 1), 28: (0, 5), 29: (1, 3), 30: (1, 3), 31: (1, 3), 32: (1, 4), 33: (0, 2), 34: (1, 2), 35: (1, 4), 36: (0, 2), 37: (0, 3), 38: (1, 3), 39: (1, 2), 40: (1, 2), 41: (1, 3), 42: (0, 2), 43: (0, 2), 44: (0, 1), 45: (0, 0), 46: (0, 0), 47: (0, 0), 48: (0, 1), 49: (0, 1), 50: (0, 0), 51: (0, 0), 55: (0, 2), 57: (0, 2), 60: (0, 2), 61: (0, 0), 64: (0, 0), 65: (0, 0), 66: (0, 1), 68: (0, 1), 69: (0, 1), 74: (0, 0), 75: (0, 0), 80: (0, 0), 81: (0, 0)},
}

View File

@@ -1,7 +1,7 @@
from __future__ import absolute_import
from kafka.protocol.api import Request, Response
from kafka.protocol.types import Array, Int8, Int16, Int32, Int64, Schema, String
from kafka.protocol.types import Array, Int16, Int32, Int64, Schema, String
class OffsetCommitResponse_v0(Response):
@@ -41,6 +41,24 @@ class OffsetCommitResponse_v3(Response):
)
class OffsetCommitResponse_v4(Response):
API_KEY = 8
API_VERSION = 4
SCHEMA = OffsetCommitResponse_v3.SCHEMA
class OffsetCommitResponse_v5(Response):
API_KEY = 8
API_VERSION = 5
SCHEMA = OffsetCommitResponse_v4.SCHEMA
class OffsetCommitResponse_v6(Response):
API_KEY = 8
API_VERSION = 6
SCHEMA = OffsetCommitResponse_v5.SCHEMA
class OffsetCommitRequest_v0(Request):
API_KEY = 8
API_VERSION = 0 # Zookeeper-backed storage
@@ -76,13 +94,13 @@ class OffsetCommitRequest_v1(Request):
class OffsetCommitRequest_v2(Request):
API_KEY = 8
API_VERSION = 2 # added retention_time, dropped timestamp
API_VERSION = 2
RESPONSE_TYPE = OffsetCommitResponse_v2
SCHEMA = Schema(
('consumer_group', String('utf-8')),
('consumer_group_generation_id', Int32),
('consumer_id', String('utf-8')),
('retention_time', Int64),
('retention_time', Int64), # added retention_time, dropped timestamp
('topics', Array(
('topic', String('utf-8')),
('partitions', Array(
@@ -90,7 +108,6 @@ class OffsetCommitRequest_v2(Request):
('offset', Int64),
('metadata', String('utf-8'))))))
)
DEFAULT_GENERATION_ID = -1
DEFAULT_RETENTION_TIME = -1
@@ -99,15 +116,63 @@ class OffsetCommitRequest_v3(Request):
API_VERSION = 3
RESPONSE_TYPE = OffsetCommitResponse_v3
SCHEMA = OffsetCommitRequest_v2.SCHEMA
DEFAULT_RETENTION_TIME = -1
class OffsetCommitRequest_v4(Request):
API_KEY = 8
API_VERSION = 4
RESPONSE_TYPE = OffsetCommitResponse_v4
SCHEMA = OffsetCommitRequest_v3.SCHEMA
DEFAULT_RETENTION_TIME = -1
class OffsetCommitRequest_v5(Request):
API_KEY = 8
API_VERSION = 5 # drops retention_time
RESPONSE_TYPE = OffsetCommitResponse_v5
SCHEMA = Schema(
('consumer_group', String('utf-8')),
('consumer_group_generation_id', Int32),
('consumer_id', String('utf-8')),
('topics', Array(
('topic', String('utf-8')),
('partitions', Array(
('partition', Int32),
('offset', Int64),
('metadata', String('utf-8'))))))
)
class OffsetCommitRequest_v6(Request):
API_KEY = 8
API_VERSION = 6
RESPONSE_TYPE = OffsetCommitResponse_v6
SCHEMA = Schema(
('consumer_group', String('utf-8')),
('consumer_group_generation_id', Int32),
('consumer_id', String('utf-8')),
('topics', Array(
('topic', String('utf-8')),
('partitions', Array(
('partition', Int32),
('offset', Int64),
('leader_epoch', Int32), # added for fencing / kip-320. default -1
('metadata', String('utf-8'))))))
)
OffsetCommitRequest = [
OffsetCommitRequest_v0, OffsetCommitRequest_v1,
OffsetCommitRequest_v2, OffsetCommitRequest_v3
OffsetCommitRequest_v2, OffsetCommitRequest_v3,
OffsetCommitRequest_v4, OffsetCommitRequest_v5,
OffsetCommitRequest_v6,
]
OffsetCommitResponse = [
OffsetCommitResponse_v0, OffsetCommitResponse_v1,
OffsetCommitResponse_v2, OffsetCommitResponse_v3
OffsetCommitResponse_v2, OffsetCommitResponse_v3,
OffsetCommitResponse_v4, OffsetCommitResponse_v5,
OffsetCommitResponse_v6,
]
@@ -163,6 +228,29 @@ class OffsetFetchResponse_v3(Response):
)
class OffsetFetchResponse_v4(Response):
API_KEY = 9
API_VERSION = 4
SCHEMA = OffsetFetchResponse_v3.SCHEMA
class OffsetFetchResponse_v5(Response):
API_KEY = 9
API_VERSION = 5
SCHEMA = Schema(
('throttle_time_ms', Int32),
('topics', Array(
('topic', String('utf-8')),
('partitions', Array(
('partition', Int32),
('offset', Int64),
('leader_epoch', Int32),
('metadata', String('utf-8')),
('error_code', Int16))))),
('error_code', Int16)
)
class OffsetFetchRequest_v0(Request):
API_KEY = 9
API_VERSION = 0 # zookeeper-backed storage
@@ -199,57 +287,27 @@ class OffsetFetchRequest_v3(Request):
SCHEMA = OffsetFetchRequest_v2.SCHEMA
class OffsetFetchRequest_v4(Request):
API_KEY = 9
API_VERSION = 4
RESPONSE_TYPE = OffsetFetchResponse_v4
SCHEMA = OffsetFetchRequest_v3.SCHEMA
class OffsetFetchRequest_v5(Request):
API_KEY = 9
API_VERSION = 5
RESPONSE_TYPE = OffsetFetchResponse_v5
SCHEMA = OffsetFetchRequest_v4.SCHEMA
OffsetFetchRequest = [
OffsetFetchRequest_v0, OffsetFetchRequest_v1,
OffsetFetchRequest_v2, OffsetFetchRequest_v3,
OffsetFetchRequest_v4, OffsetFetchRequest_v5,
]
OffsetFetchResponse = [
OffsetFetchResponse_v0, OffsetFetchResponse_v1,
OffsetFetchResponse_v2, OffsetFetchResponse_v3,
OffsetFetchResponse_v4, OffsetFetchResponse_v5,
]
class GroupCoordinatorResponse_v0(Response):
API_KEY = 10
API_VERSION = 0
SCHEMA = Schema(
('error_code', Int16),
('coordinator_id', Int32),
('host', String('utf-8')),
('port', Int32)
)
class GroupCoordinatorResponse_v1(Response):
API_KEY = 10
API_VERSION = 1
SCHEMA = Schema(
('error_code', Int16),
('error_message', String('utf-8')),
('coordinator_id', Int32),
('host', String('utf-8')),
('port', Int32)
)
class GroupCoordinatorRequest_v0(Request):
API_KEY = 10
API_VERSION = 0
RESPONSE_TYPE = GroupCoordinatorResponse_v0
SCHEMA = Schema(
('consumer_group', String('utf-8'))
)
class GroupCoordinatorRequest_v1(Request):
API_KEY = 10
API_VERSION = 1
RESPONSE_TYPE = GroupCoordinatorResponse_v1
SCHEMA = Schema(
('coordinator_key', String('utf-8')),
('coordinator_type', Int8)
)
GroupCoordinatorRequest = [GroupCoordinatorRequest_v0, GroupCoordinatorRequest_v1]
GroupCoordinatorResponse = [GroupCoordinatorResponse_v0, GroupCoordinatorResponse_v1]

View File

@@ -0,0 +1,58 @@
from __future__ import absolute_import
from kafka.protocol.api import Request, Response
from kafka.protocol.types import Boolean, Int16, Int32, Int64, Schema, String
class EndTxnResponse_v0(Response):
API_KEY = 26
API_VERSION = 0
SCHEMA = Schema(
('throttle_time_ms', Int32),
('error_code', Int16),
)
class EndTxnResponse_v1(Response):
API_KEY = 26
API_VERSION = 1
SCHEMA = EndTxnResponse_v0.SCHEMA
class EndTxnResponse_v2(Response):
API_KEY = 26
API_VERSION = 2
SCHEMA = EndTxnResponse_v1.SCHEMA
class EndTxnRequest_v0(Request):
API_KEY = 26
API_VERSION = 0
RESPONSE_TYPE = EndTxnResponse_v0
SCHEMA = Schema(
('transactional_id', String('utf-8')),
('producer_id', Int64),
('producer_epoch', Int16),
('committed', Boolean))
class EndTxnRequest_v1(Request):
API_KEY = 26
API_VERSION = 1
RESPONSE_TYPE = EndTxnResponse_v1
SCHEMA = EndTxnRequest_v0.SCHEMA
class EndTxnRequest_v2(Request):
API_KEY = 26
API_VERSION = 2
RESPONSE_TYPE = EndTxnResponse_v2
SCHEMA = EndTxnRequest_v1.SCHEMA
EndTxnRequest = [
EndTxnRequest_v0, EndTxnRequest_v1, EndTxnRequest_v2,
]
EndTxnResponse = [
EndTxnResponse_v0, EndTxnResponse_v1, EndTxnResponse_v2,
]

View File

@@ -1,9 +1,15 @@
from __future__ import absolute_import
import collections
from kafka.protocol.api import Request, Response
from kafka.protocol.types import Array, Int8, Int16, Int32, Int64, Schema, String, Bytes
AbortedTransaction = collections.namedtuple("AbortedTransaction",
["producer_id", "first_offset"])
class FetchResponse_v0(Response):
API_KEY = 1
API_VERSION = 0
@@ -14,7 +20,7 @@ class FetchResponse_v0(Response):
('partition', Int32),
('error_code', Int16),
('highwater_offset', Int64),
('message_set', Bytes)))))
('records', Bytes)))))
)
@@ -29,7 +35,7 @@ class FetchResponse_v1(Response):
('partition', Int32),
('error_code', Int16),
('highwater_offset', Int64),
('message_set', Bytes)))))
('records', Bytes)))))
)
@@ -46,6 +52,7 @@ class FetchResponse_v3(Response):
class FetchResponse_v4(Response):
# Adds message format v2
API_KEY = 1
API_VERSION = 4
SCHEMA = Schema(
@@ -60,7 +67,7 @@ class FetchResponse_v4(Response):
('aborted_transactions', Array(
('producer_id', Int64),
('first_offset', Int64))),
('message_set', Bytes)))))
('records', Bytes)))))
)
@@ -80,7 +87,7 @@ class FetchResponse_v5(Response):
('aborted_transactions', Array(
('producer_id', Int64),
('first_offset', Int64))),
('message_set', Bytes)))))
('records', Bytes)))))
)
@@ -115,7 +122,7 @@ class FetchResponse_v7(Response):
('aborted_transactions', Array(
('producer_id', Int64),
('first_offset', Int64))),
('message_set', Bytes)))))
('records', Bytes)))))
)
@@ -156,7 +163,7 @@ class FetchResponse_v11(Response):
('producer_id', Int64),
('first_offset', Int64))),
('preferred_read_replica', Int32),
('message_set', Bytes)))))
('records', Bytes)))))
)
@@ -211,6 +218,7 @@ class FetchRequest_v3(Request):
class FetchRequest_v4(Request):
# Adds isolation_level field
# Adds message format v2
API_KEY = 1
API_VERSION = 4
RESPONSE_TYPE = FetchResponse_v4
@@ -264,7 +272,7 @@ class FetchRequest_v6(Request):
class FetchRequest_v7(Request):
"""
Add incremental fetch requests
Add incremental fetch requests (see KIP-227)
"""
API_KEY = 1
API_VERSION = 7
@@ -285,7 +293,7 @@ class FetchRequest_v7(Request):
('log_start_offset', Int64),
('max_bytes', Int32))))),
('forgotten_topics_data', Array(
('topic', String),
('topic', String('utf-8')),
('partitions', Array(Int32))
)),
)
@@ -325,7 +333,7 @@ class FetchRequest_v9(Request):
('log_start_offset', Int64),
('max_bytes', Int32))))),
('forgotten_topics_data', Array(
('topic', String),
('topic', String('utf-8')),
('partitions', Array(Int32)),
)),
)
@@ -365,7 +373,7 @@ class FetchRequest_v11(Request):
('log_start_offset', Int64),
('max_bytes', Int32))))),
('forgotten_topics_data', Array(
('topic', String),
('topic', String('utf-8')),
('partitions', Array(Int32))
)),
('rack_id', String('utf-8')),

View File

@@ -0,0 +1,64 @@
from __future__ import absolute_import
from kafka.protocol.api import Request, Response
from kafka.protocol.types import Int8, Int16, Int32, Schema, String
class FindCoordinatorResponse_v0(Response):
API_KEY = 10
API_VERSION = 0
SCHEMA = Schema(
('error_code', Int16),
('coordinator_id', Int32),
('host', String('utf-8')),
('port', Int32)
)
class FindCoordinatorResponse_v1(Response):
API_KEY = 10
API_VERSION = 1
SCHEMA = Schema(
('throttle_time_ms', Int32),
('error_code', Int16),
('error_message', String('utf-8')),
('coordinator_id', Int32),
('host', String('utf-8')),
('port', Int32)
)
class FindCoordinatorResponse_v2(Response):
API_KEY = 10
API_VERSION = 2
SCHEMA = FindCoordinatorResponse_v1.SCHEMA
class FindCoordinatorRequest_v0(Request):
API_KEY = 10
API_VERSION = 0
RESPONSE_TYPE = FindCoordinatorResponse_v0
SCHEMA = Schema(
('consumer_group', String('utf-8'))
)
class FindCoordinatorRequest_v1(Request):
API_KEY = 10
API_VERSION = 1
RESPONSE_TYPE = FindCoordinatorResponse_v1
SCHEMA = Schema(
('coordinator_key', String('utf-8')),
('coordinator_type', Int8) # 0: consumer, 1: transaction
)
class FindCoordinatorRequest_v2(Request):
API_KEY = 10
API_VERSION = 2
RESPONSE_TYPE = FindCoordinatorResponse_v2
SCHEMA = FindCoordinatorRequest_v1.SCHEMA
FindCoordinatorRequest = [FindCoordinatorRequest_v0, FindCoordinatorRequest_v1, FindCoordinatorRequest_v2]
FindCoordinatorResponse = [FindCoordinatorResponse_v0, FindCoordinatorResponse_v1, FindCoordinatorResponse_v2]

View File

@@ -5,6 +5,10 @@ from kafka.protocol.struct import Struct
from kafka.protocol.types import Array, Bytes, Int16, Int32, Schema, String
DEFAULT_GENERATION_ID = -1
UNKNOWN_MEMBER_ID = ''
class JoinGroupResponse_v0(Response):
API_KEY = 11
API_VERSION = 0
@@ -42,6 +46,18 @@ class JoinGroupResponse_v2(Response):
)
class JoinGroupResponse_v3(Response):
API_KEY = 11
API_VERSION = 3
SCHEMA = JoinGroupResponse_v2.SCHEMA
class JoinGroupResponse_v4(Response):
API_KEY = 11
API_VERSION = 4
SCHEMA = JoinGroupResponse_v3.SCHEMA
class JoinGroupRequest_v0(Request):
API_KEY = 11
API_VERSION = 0
@@ -55,7 +71,6 @@ class JoinGroupRequest_v0(Request):
('protocol_name', String('utf-8')),
('protocol_metadata', Bytes)))
)
UNKNOWN_MEMBER_ID = ''
class JoinGroupRequest_v1(Request):
@@ -72,7 +87,6 @@ class JoinGroupRequest_v1(Request):
('protocol_name', String('utf-8')),
('protocol_metadata', Bytes)))
)
UNKNOWN_MEMBER_ID = ''
class JoinGroupRequest_v2(Request):
@@ -80,14 +94,29 @@ class JoinGroupRequest_v2(Request):
API_VERSION = 2
RESPONSE_TYPE = JoinGroupResponse_v2
SCHEMA = JoinGroupRequest_v1.SCHEMA
UNKNOWN_MEMBER_ID = ''
class JoinGroupRequest_v3(Request):
API_KEY = 11
API_VERSION = 3
RESPONSE_TYPE = JoinGroupResponse_v3
SCHEMA = JoinGroupRequest_v2.SCHEMA
class JoinGroupRequest_v4(Request):
API_KEY = 11
API_VERSION = 4
RESPONSE_TYPE = JoinGroupResponse_v4
SCHEMA = JoinGroupRequest_v3.SCHEMA
JoinGroupRequest = [
JoinGroupRequest_v0, JoinGroupRequest_v1, JoinGroupRequest_v2
JoinGroupRequest_v0, JoinGroupRequest_v1, JoinGroupRequest_v2,
JoinGroupRequest_v3, JoinGroupRequest_v4,
]
JoinGroupResponse = [
JoinGroupResponse_v0, JoinGroupResponse_v1, JoinGroupResponse_v2
JoinGroupResponse_v0, JoinGroupResponse_v1, JoinGroupResponse_v2,
JoinGroupResponse_v3, JoinGroupResponse_v4,
]
@@ -118,6 +147,12 @@ class SyncGroupResponse_v1(Response):
)
class SyncGroupResponse_v2(Response):
API_KEY = 14
API_VERSION = 2
SCHEMA = SyncGroupResponse_v1.SCHEMA
class SyncGroupRequest_v0(Request):
API_KEY = 14
API_VERSION = 0
@@ -139,8 +174,15 @@ class SyncGroupRequest_v1(Request):
SCHEMA = SyncGroupRequest_v0.SCHEMA
SyncGroupRequest = [SyncGroupRequest_v0, SyncGroupRequest_v1]
SyncGroupResponse = [SyncGroupResponse_v0, SyncGroupResponse_v1]
class SyncGroupRequest_v2(Request):
API_KEY = 14
API_VERSION = 2
RESPONSE_TYPE = SyncGroupResponse_v2
SCHEMA = SyncGroupRequest_v1.SCHEMA
SyncGroupRequest = [SyncGroupRequest_v0, SyncGroupRequest_v1, SyncGroupRequest_v2]
SyncGroupResponse = [SyncGroupResponse_v0, SyncGroupResponse_v1, SyncGroupResponse_v2]
class MemberAssignment(Struct):
@@ -170,6 +212,12 @@ class HeartbeatResponse_v1(Response):
)
class HeartbeatResponse_v2(Response):
API_KEY = 12
API_VERSION = 2
SCHEMA = HeartbeatResponse_v1.SCHEMA
class HeartbeatRequest_v0(Request):
API_KEY = 12
API_VERSION = 0
@@ -188,8 +236,15 @@ class HeartbeatRequest_v1(Request):
SCHEMA = HeartbeatRequest_v0.SCHEMA
HeartbeatRequest = [HeartbeatRequest_v0, HeartbeatRequest_v1]
HeartbeatResponse = [HeartbeatResponse_v0, HeartbeatResponse_v1]
class HeartbeatRequest_v2(Request):
API_KEY = 12
API_VERSION = 2
RESPONSE_TYPE = HeartbeatResponse_v2
SCHEMA = HeartbeatRequest_v1.SCHEMA
HeartbeatRequest = [HeartbeatRequest_v0, HeartbeatRequest_v1, HeartbeatRequest_v2]
HeartbeatResponse = [HeartbeatResponse_v0, HeartbeatResponse_v1, HeartbeatResponse_v2]
class LeaveGroupResponse_v0(Response):
@@ -209,6 +264,12 @@ class LeaveGroupResponse_v1(Response):
)
class LeaveGroupResponse_v2(Response):
API_KEY = 13
API_VERSION = 2
SCHEMA = LeaveGroupResponse_v1.SCHEMA
class LeaveGroupRequest_v0(Request):
API_KEY = 13
API_VERSION = 0
@@ -226,5 +287,12 @@ class LeaveGroupRequest_v1(Request):
SCHEMA = LeaveGroupRequest_v0.SCHEMA
LeaveGroupRequest = [LeaveGroupRequest_v0, LeaveGroupRequest_v1]
LeaveGroupResponse = [LeaveGroupResponse_v0, LeaveGroupResponse_v1]
class LeaveGroupRequest_v2(Request):
API_KEY = 13
API_VERSION = 2
RESPONSE_TYPE = LeaveGroupResponse_v2
SCHEMA = LeaveGroupRequest_v1.SCHEMA
LeaveGroupRequest = [LeaveGroupRequest_v0, LeaveGroupRequest_v1, LeaveGroupRequest_v2]
LeaveGroupResponse = [LeaveGroupResponse_v0, LeaveGroupResponse_v1, LeaveGroupResponse_v2]

View File

@@ -0,0 +1,46 @@
from __future__ import absolute_import
from kafka.protocol.api import Request, Response
from kafka.protocol.types import Int16, Int32, Int64, Schema, String
class InitProducerIdResponse_v0(Response):
API_KEY = 22
API_VERSION = 0
SCHEMA = Schema(
('throttle_time_ms', Int32),
('error_code', Int16),
('producer_id', Int64),
('producer_epoch', Int16),
)
class InitProducerIdResponse_v1(Response):
API_KEY = 22
API_VERSION = 1
SCHEMA = InitProducerIdResponse_v0.SCHEMA
class InitProducerIdRequest_v0(Request):
API_KEY = 22
API_VERSION = 0
RESPONSE_TYPE = InitProducerIdResponse_v0
SCHEMA = Schema(
('transactional_id', String('utf-8')),
('transaction_timeout_ms', Int32),
)
class InitProducerIdRequest_v1(Request):
API_KEY = 22
API_VERSION = 1
RESPONSE_TYPE = InitProducerIdResponse_v1
SCHEMA = InitProducerIdRequest_v0.SCHEMA
InitProducerIdRequest = [
InitProducerIdRequest_v0, InitProducerIdRequest_v1,
]
InitProducerIdResponse = [
InitProducerIdResponse_v0, InitProducerIdResponse_v1,
]

View File

@@ -12,7 +12,7 @@ class OffsetResetStrategy(object):
NONE = 0
class OffsetResponse_v0(Response):
class ListOffsetsResponse_v0(Response):
API_KEY = 2
API_VERSION = 0
SCHEMA = Schema(
@@ -24,7 +24,7 @@ class OffsetResponse_v0(Response):
('offsets', Array(Int64))))))
)
class OffsetResponse_v1(Response):
class ListOffsetsResponse_v1(Response):
API_KEY = 2
API_VERSION = 1
SCHEMA = Schema(
@@ -38,7 +38,7 @@ class OffsetResponse_v1(Response):
)
class OffsetResponse_v2(Response):
class ListOffsetsResponse_v2(Response):
API_KEY = 2
API_VERSION = 2
SCHEMA = Schema(
@@ -53,16 +53,16 @@ class OffsetResponse_v2(Response):
)
class OffsetResponse_v3(Response):
class ListOffsetsResponse_v3(Response):
"""
on quota violation, brokers send out responses before throttling
"""
API_KEY = 2
API_VERSION = 3
SCHEMA = OffsetResponse_v2.SCHEMA
SCHEMA = ListOffsetsResponse_v2.SCHEMA
class OffsetResponse_v4(Response):
class ListOffsetsResponse_v4(Response):
"""
Add leader_epoch to response
"""
@@ -81,19 +81,19 @@ class OffsetResponse_v4(Response):
)
class OffsetResponse_v5(Response):
class ListOffsetsResponse_v5(Response):
"""
adds a new error code, OFFSET_NOT_AVAILABLE
"""
API_KEY = 2
API_VERSION = 5
SCHEMA = OffsetResponse_v4.SCHEMA
SCHEMA = ListOffsetsResponse_v4.SCHEMA
class OffsetRequest_v0(Request):
class ListOffsetsRequest_v0(Request):
API_KEY = 2
API_VERSION = 0
RESPONSE_TYPE = OffsetResponse_v0
RESPONSE_TYPE = ListOffsetsResponse_v0
SCHEMA = Schema(
('replica_id', Int32),
('topics', Array(
@@ -107,10 +107,10 @@ class OffsetRequest_v0(Request):
'replica_id': -1
}
class OffsetRequest_v1(Request):
class ListOffsetsRequest_v1(Request):
API_KEY = 2
API_VERSION = 1
RESPONSE_TYPE = OffsetResponse_v1
RESPONSE_TYPE = ListOffsetsResponse_v1
SCHEMA = Schema(
('replica_id', Int32),
('topics', Array(
@@ -124,10 +124,10 @@ class OffsetRequest_v1(Request):
}
class OffsetRequest_v2(Request):
class ListOffsetsRequest_v2(Request):
API_KEY = 2
API_VERSION = 2
RESPONSE_TYPE = OffsetResponse_v2
RESPONSE_TYPE = ListOffsetsResponse_v2
SCHEMA = Schema(
('replica_id', Int32),
('isolation_level', Int8), # <- added isolation_level
@@ -142,23 +142,23 @@ class OffsetRequest_v2(Request):
}
class OffsetRequest_v3(Request):
class ListOffsetsRequest_v3(Request):
API_KEY = 2
API_VERSION = 3
RESPONSE_TYPE = OffsetResponse_v3
SCHEMA = OffsetRequest_v2.SCHEMA
RESPONSE_TYPE = ListOffsetsResponse_v3
SCHEMA = ListOffsetsRequest_v2.SCHEMA
DEFAULTS = {
'replica_id': -1
}
class OffsetRequest_v4(Request):
class ListOffsetsRequest_v4(Request):
"""
Add current_leader_epoch to request
"""
API_KEY = 2
API_VERSION = 4
RESPONSE_TYPE = OffsetResponse_v4
RESPONSE_TYPE = ListOffsetsResponse_v4
SCHEMA = Schema(
('replica_id', Int32),
('isolation_level', Int8), # <- added isolation_level
@@ -166,7 +166,7 @@ class OffsetRequest_v4(Request):
('topic', String('utf-8')),
('partitions', Array(
('partition', Int32),
('current_leader_epoch', Int64),
('current_leader_epoch', Int32),
('timestamp', Int64)))))
)
DEFAULTS = {
@@ -174,21 +174,21 @@ class OffsetRequest_v4(Request):
}
class OffsetRequest_v5(Request):
class ListOffsetsRequest_v5(Request):
API_KEY = 2
API_VERSION = 5
RESPONSE_TYPE = OffsetResponse_v5
SCHEMA = OffsetRequest_v4.SCHEMA
RESPONSE_TYPE = ListOffsetsResponse_v5
SCHEMA = ListOffsetsRequest_v4.SCHEMA
DEFAULTS = {
'replica_id': -1
}
OffsetRequest = [
OffsetRequest_v0, OffsetRequest_v1, OffsetRequest_v2,
OffsetRequest_v3, OffsetRequest_v4, OffsetRequest_v5,
ListOffsetsRequest = [
ListOffsetsRequest_v0, ListOffsetsRequest_v1, ListOffsetsRequest_v2,
ListOffsetsRequest_v3, ListOffsetsRequest_v4, ListOffsetsRequest_v5,
]
OffsetResponse = [
OffsetResponse_v0, OffsetResponse_v1, OffsetResponse_v2,
OffsetResponse_v3, OffsetResponse_v4, OffsetResponse_v5,
ListOffsetsResponse = [
ListOffsetsResponse_v0, ListOffsetsResponse_v1, ListOffsetsResponse_v2,
ListOffsetsResponse_v3, ListOffsetsResponse_v4, ListOffsetsResponse_v5,
]

View File

@@ -128,6 +128,42 @@ class MetadataResponse_v5(Response):
)
class MetadataResponse_v6(Response):
"""Metadata Request/Response v6 is the same as v5,
but on quota violation, brokers send out responses before throttling."""
API_KEY = 3
API_VERSION = 6
SCHEMA = MetadataResponse_v5.SCHEMA
class MetadataResponse_v7(Response):
"""v7 adds per-partition leader_epoch field"""
API_KEY = 3
API_VERSION = 7
SCHEMA = Schema(
('throttle_time_ms', Int32),
('brokers', Array(
('node_id', Int32),
('host', String('utf-8')),
('port', Int32),
('rack', String('utf-8')))),
('cluster_id', String('utf-8')),
('controller_id', Int32),
('topics', Array(
('error_code', Int16),
('topic', String('utf-8')),
('is_internal', Boolean),
('partitions', Array(
('error_code', Int16),
('partition', Int32),
('leader', Int32),
('leader_epoch', Int32),
('replicas', Array(Int32)),
('isr', Array(Int32)),
('offline_replicas', Array(Int32))))))
)
class MetadataRequest_v0(Request):
API_KEY = 3
API_VERSION = 0
@@ -135,7 +171,8 @@ class MetadataRequest_v0(Request):
SCHEMA = Schema(
('topics', Array(String('utf-8')))
)
ALL_TOPICS = None # Empty Array (len 0) for topics returns all topics
ALL_TOPICS = [] # Empty Array (len 0) for topics returns all topics
NO_TOPICS = [] # v0 does not support a 'no topics' request, so we'll just ask for ALL
class MetadataRequest_v1(Request):
@@ -143,8 +180,8 @@ class MetadataRequest_v1(Request):
API_VERSION = 1
RESPONSE_TYPE = MetadataResponse_v1
SCHEMA = MetadataRequest_v0.SCHEMA
ALL_TOPICS = -1 # Null Array (len -1) for topics returns all topics
NO_TOPICS = None # Empty array (len 0) for topics returns no topics
ALL_TOPICS = None # Null Array (len -1) for topics returns all topics
NO_TOPICS = [] # Empty array (len 0) for topics returns no topics
class MetadataRequest_v2(Request):
@@ -152,8 +189,8 @@ class MetadataRequest_v2(Request):
API_VERSION = 2
RESPONSE_TYPE = MetadataResponse_v2
SCHEMA = MetadataRequest_v1.SCHEMA
ALL_TOPICS = -1 # Null Array (len -1) for topics returns all topics
NO_TOPICS = None # Empty array (len 0) for topics returns no topics
ALL_TOPICS = None
NO_TOPICS = []
class MetadataRequest_v3(Request):
@@ -161,8 +198,8 @@ class MetadataRequest_v3(Request):
API_VERSION = 3
RESPONSE_TYPE = MetadataResponse_v3
SCHEMA = MetadataRequest_v1.SCHEMA
ALL_TOPICS = -1 # Null Array (len -1) for topics returns all topics
NO_TOPICS = None # Empty array (len 0) for topics returns no topics
ALL_TOPICS = None
NO_TOPICS = []
class MetadataRequest_v4(Request):
@@ -173,8 +210,8 @@ class MetadataRequest_v4(Request):
('topics', Array(String('utf-8'))),
('allow_auto_topic_creation', Boolean)
)
ALL_TOPICS = -1 # Null Array (len -1) for topics returns all topics
NO_TOPICS = None # Empty array (len 0) for topics returns no topics
ALL_TOPICS = None
NO_TOPICS = []
class MetadataRequest_v5(Request):
@@ -186,15 +223,35 @@ class MetadataRequest_v5(Request):
API_VERSION = 5
RESPONSE_TYPE = MetadataResponse_v5
SCHEMA = MetadataRequest_v4.SCHEMA
ALL_TOPICS = -1 # Null Array (len -1) for topics returns all topics
NO_TOPICS = None # Empty array (len 0) for topics returns no topics
ALL_TOPICS = None
NO_TOPICS = []
class MetadataRequest_v6(Request):
API_KEY = 3
API_VERSION = 6
RESPONSE_TYPE = MetadataResponse_v6
SCHEMA = MetadataRequest_v5.SCHEMA
ALL_TOPICS = None
NO_TOPICS = []
class MetadataRequest_v7(Request):
API_KEY = 3
API_VERSION = 7
RESPONSE_TYPE = MetadataResponse_v7
SCHEMA = MetadataRequest_v6.SCHEMA
ALL_TOPICS = None
NO_TOPICS = []
MetadataRequest = [
MetadataRequest_v0, MetadataRequest_v1, MetadataRequest_v2,
MetadataRequest_v3, MetadataRequest_v4, MetadataRequest_v5
MetadataRequest_v3, MetadataRequest_v4, MetadataRequest_v5,
MetadataRequest_v6, MetadataRequest_v7,
]
MetadataResponse = [
MetadataResponse_v0, MetadataResponse_v1, MetadataResponse_v2,
MetadataResponse_v3, MetadataResponse_v4, MetadataResponse_v5
MetadataResponse_v3, MetadataResponse_v4, MetadataResponse_v5,
MetadataResponse_v6, MetadataResponse_v7,
]

View File

@@ -0,0 +1,140 @@
from __future__ import absolute_import
from kafka.protocol.api import Request, Response
from kafka.protocol.types import Array, CompactArray, CompactString, Int16, Int32, Int64, Schema, String, TaggedFields
class OffsetForLeaderEpochResponse_v0(Response):
API_KEY = 23
API_VERSION = 0
SCHEMA = Schema(
('topics', Array(
('topic', String('utf-8')),
('partitions', Array(
('error_code', Int16),
('partition', Int32),
('end_offset', Int64))))))
class OffsetForLeaderEpochResponse_v1(Response):
API_KEY = 23
API_VERSION = 1
SCHEMA = Schema(
('topics', Array(
('topic', String('utf-8')),
('partitions', Array(
('error_code', Int16),
('partition', Int32),
('leader_epoch', Int32),
('end_offset', Int64))))))
class OffsetForLeaderEpochResponse_v2(Response):
API_KEY = 23
API_VERSION = 2
SCHEMA = Schema(
('throttle_time_ms', Int32),
('topics', Array(
('topic', String('utf-8')),
('partitions', Array(
('error_code', Int16),
('partition', Int32),
('leader_epoch', Int32),
('end_offset', Int64))))))
class OffsetForLeaderEpochResponse_v3(Response):
API_KEY = 23
API_VERSION = 3
SCHEMA = OffsetForLeaderEpochResponse_v2.SCHEMA
class OffsetForLeaderEpochResponse_v4(Response):
API_KEY = 23
API_VERSION = 4
SCHEMA = Schema(
('throttle_time_ms', Int32),
('topics', CompactArray(
('topic', CompactString('utf-8')),
('partitions', CompactArray(
('error_code', Int16),
('partition', Int32),
('leader_epoch', Int32),
('end_offset', Int64),
('tags', TaggedFields))),
('tags', TaggedFields))),
('tags', TaggedFields))
class OffsetForLeaderEpochRequest_v0(Request):
API_KEY = 23
API_VERSION = 0
RESPONSE_TYPE = OffsetForLeaderEpochResponse_v0
SCHEMA = Schema(
('topics', Array(
('topic', String('utf-8')),
('partitions', Array(
('partition', Int32),
('leader_epoch', Int32))))))
class OffsetForLeaderEpochRequest_v1(Request):
API_KEY = 23
API_VERSION = 1
RESPONSE_TYPE = OffsetForLeaderEpochResponse_v1
SCHEMA = OffsetForLeaderEpochRequest_v0.SCHEMA
class OffsetForLeaderEpochRequest_v2(Request):
API_KEY = 23
API_VERSION = 2
RESPONSE_TYPE = OffsetForLeaderEpochResponse_v2
SCHEMA = Schema(
('topics', Array(
('topic', String('utf-8')),
('partitions', Array(
('partition', Int32),
('current_leader_epoch', Int32),
('leader_epoch', Int32))))))
class OffsetForLeaderEpochRequest_v3(Request):
API_KEY = 23
API_VERSION = 3
RESPONSE_TYPE = OffsetForLeaderEpochResponse_v3
SCHEMA = Schema(
('replica_id', Int32),
('topics', Array(
('topic', String('utf-8')),
('partitions', Array(
('partition', Int32),
('current_leader_epoch', Int32),
('leader_epoch', Int32))))))
class OffsetForLeaderEpochRequest_v4(Request):
API_KEY = 23
API_VERSION = 4
RESPONSE_TYPE = OffsetForLeaderEpochResponse_v4
SCHEMA = Schema(
('replica_id', Int32),
('topics', CompactArray(
('topic', CompactString('utf-8')),
('partitions', CompactArray(
('partition', Int32),
('current_leader_epoch', Int32),
('leader_epoch', Int32),
('tags', TaggedFields))),
('tags', TaggedFields))),
('tags', TaggedFields))
OffsetForLeaderEpochRequest = [
OffsetForLeaderEpochRequest_v0, OffsetForLeaderEpochRequest_v1,
OffsetForLeaderEpochRequest_v2, OffsetForLeaderEpochRequest_v3,
OffsetForLeaderEpochRequest_v4,
]
OffsetForLeaderEpochResponse = [
OffsetForLeaderEpochResponse_v0, OffsetForLeaderEpochResponse_v1,
OffsetForLeaderEpochResponse_v2, OffsetForLeaderEpochResponse_v3,
OffsetForLeaderEpochResponse_v4,
]

View File

@@ -4,10 +4,9 @@ import collections
import logging
import kafka.errors as Errors
from kafka.protocol.api import RequestHeader
from kafka.protocol.commit import GroupCoordinatorResponse
from kafka.protocol.find_coordinator import FindCoordinatorResponse
from kafka.protocol.frame import KafkaBytes
from kafka.protocol.types import Int32
from kafka.protocol.types import Int32, TaggedFields
from kafka.version import __version__
log = logging.getLogger(__name__)
@@ -59,9 +58,8 @@ class KafkaProtocol(object):
log.debug('Sending request %s', request)
if correlation_id is None:
correlation_id = self._next_correlation_id()
header = RequestHeader(request,
correlation_id=correlation_id,
client_id=self._client_id)
header = request.build_header(correlation_id=correlation_id, client_id=self._client_id)
message = b''.join([header.encode(), request.encode()])
size = Int32.encode(len(message))
data = size + message
@@ -135,21 +133,17 @@ class KafkaProtocol(object):
return responses
def _process_response(self, read_buffer):
recv_correlation_id = Int32.decode(read_buffer)
log.debug('Received correlation id: %d', recv_correlation_id)
if not self.in_flight_requests:
raise Errors.CorrelationIdError(
'No in-flight-request found for server response'
' with correlation ID %d'
% (recv_correlation_id,))
raise Errors.CorrelationIdError('No in-flight-request found for server response')
(correlation_id, request) = self.in_flight_requests.popleft()
response_type = request.RESPONSE_TYPE
response_header = response_type.parse_header(read_buffer)
recv_correlation_id = response_header.correlation_id
log.debug('Received correlation id: %d', recv_correlation_id)
# 0.8.2 quirk
if (recv_correlation_id == 0 and
correlation_id != 0 and
request.RESPONSE_TYPE is GroupCoordinatorResponse[0] and
response_type is FindCoordinatorResponse[0] and
(self._api_version == (0, 8, 2) or self._api_version is None)):
log.warning('Kafka 0.8.2 quirk -- GroupCoordinatorResponse'
' Correlation ID does not match request. This'
@@ -163,15 +157,15 @@ class KafkaProtocol(object):
% (correlation_id, recv_correlation_id))
# decode response
log.debug('Processing response %s', request.RESPONSE_TYPE.__name__)
log.debug('Processing response %s', response_type.__name__)
try:
response = request.RESPONSE_TYPE.decode(read_buffer)
response = response_type.decode(read_buffer)
except ValueError:
read_buffer.seek(0)
buf = read_buffer.read()
log.error('Response %d [ResponseType: %s Request: %s]:'
' Unable to decode %d-byte buffer: %r',
correlation_id, request.RESPONSE_TYPE,
correlation_id, response_type,
request, len(buf), buf)
raise Errors.KafkaProtocolError('Unable to decode response')

View File

@@ -47,6 +47,7 @@ class ProduceResponse_v2(Response):
class ProduceResponse_v3(Response):
# Adds support for message format v2
API_KEY = 0
API_VERSION = 3
SCHEMA = ProduceResponse_v2.SCHEMA
@@ -141,7 +142,7 @@ class ProduceRequest_v0(ProduceRequest):
('topic', String('utf-8')),
('partitions', Array(
('partition', Int32),
('messages', Bytes)))))
('records', Bytes)))))
)
@@ -158,6 +159,7 @@ class ProduceRequest_v2(ProduceRequest):
class ProduceRequest_v3(ProduceRequest):
# Adds support for message format v2
API_VERSION = 3
RESPONSE_TYPE = ProduceResponse_v3
SCHEMA = Schema(
@@ -168,7 +170,7 @@ class ProduceRequest_v3(ProduceRequest):
('topic', String('utf-8')),
('partitions', Array(
('partition', Int32),
('messages', Bytes)))))
('records', Bytes)))))
)

View File

@@ -0,0 +1,42 @@
from __future__ import absolute_import
from kafka.protocol.api import Request, Response
from kafka.protocol.types import Bytes, Int16, Int64, Schema, String
class SaslAuthenticateResponse_v0(Response):
API_KEY = 36
API_VERSION = 0
SCHEMA = Schema(
('error_code', Int16),
('error_message', String('utf-8')),
('auth_bytes', Bytes))
class SaslAuthenticateResponse_v1(Response):
API_KEY = 36
API_VERSION = 1
SCHEMA = Schema(
('error_code', Int16),
('error_message', String('utf-8')),
('auth_bytes', Bytes),
('session_lifetime_ms', Int64))
class SaslAuthenticateRequest_v0(Request):
API_KEY = 36
API_VERSION = 0
RESPONSE_TYPE = SaslAuthenticateResponse_v0
SCHEMA = Schema(
('auth_bytes', Bytes))
class SaslAuthenticateRequest_v1(Request):
API_KEY = 36
API_VERSION = 1
RESPONSE_TYPE = SaslAuthenticateResponse_v1
SCHEMA = SaslAuthenticateRequest_v0.SCHEMA
SaslAuthenticateRequest = [SaslAuthenticateRequest_v0, SaslAuthenticateRequest_v1]
SaslAuthenticateResponse = [SaslAuthenticateResponse_v0, SaslAuthenticateResponse_v1]

View File

@@ -0,0 +1,39 @@
from __future__ import absolute_import
from kafka.protocol.api import Request, Response
from kafka.protocol.types import Array, Int16, Schema, String
class SaslHandshakeResponse_v0(Response):
API_KEY = 17
API_VERSION = 0
SCHEMA = Schema(
('error_code', Int16),
('enabled_mechanisms', Array(String('utf-8')))
)
class SaslHandshakeResponse_v1(Response):
API_KEY = 17
API_VERSION = 1
SCHEMA = SaslHandshakeResponse_v0.SCHEMA
class SaslHandshakeRequest_v0(Request):
API_KEY = 17
API_VERSION = 0
RESPONSE_TYPE = SaslHandshakeResponse_v0
SCHEMA = Schema(
('mechanism', String('utf-8'))
)
class SaslHandshakeRequest_v1(Request):
API_KEY = 17
API_VERSION = 1
RESPONSE_TYPE = SaslHandshakeResponse_v1
SCHEMA = SaslHandshakeRequest_v0.SCHEMA
SaslHandshakeRequest = [SaslHandshakeRequest_v0, SaslHandshakeRequest_v1]
SaslHandshakeResponse = [SaslHandshakeResponse_v0, SaslHandshakeResponse_v1]

View File

@@ -0,0 +1,78 @@
from __future__ import absolute_import
from kafka.protocol.api import Request, Response
from kafka.protocol.types import Array, Int16, Int32, Int64, Schema, String
class TxnOffsetCommitResponse_v0(Response):
API_KEY = 28
API_VERSION = 0
SCHEMA = Schema(
('throttle_time_ms', Int32),
('topics', Array(
('topic', String('utf-8')),
('partitions', Array(
('partition', Int32),
('error_code', Int16))))))
class TxnOffsetCommitResponse_v1(Response):
API_KEY = 28
API_VERSION = 1
SCHEMA = TxnOffsetCommitResponse_v0.SCHEMA
class TxnOffsetCommitResponse_v2(Response):
API_KEY = 28
API_VERSION = 2
SCHEMA = TxnOffsetCommitResponse_v1.SCHEMA
class TxnOffsetCommitRequest_v0(Request):
API_KEY = 28
API_VERSION = 0
RESPONSE_TYPE = TxnOffsetCommitResponse_v0
SCHEMA = Schema(
('transactional_id', String('utf-8')),
('group_id', String('utf-8')),
('producer_id', Int64),
('producer_epoch', Int16),
('topics', Array(
('topic', String('utf-8')),
('partitions', Array(
('partition', Int32),
('offset', Int64),
('metadata', String('utf-8')))))))
class TxnOffsetCommitRequest_v1(Request):
API_KEY = 28
API_VERSION = 1
RESPONSE_TYPE = TxnOffsetCommitResponse_v1
SCHEMA = TxnOffsetCommitRequest_v0.SCHEMA
class TxnOffsetCommitRequest_v2(Request):
API_KEY = 28
API_VERSION = 2
RESPONSE_TYPE = TxnOffsetCommitResponse_v2
SCHEMA = Schema(
('transactional_id', String('utf-8')),
('group_id', String('utf-8')),
('producer_id', Int64),
('producer_epoch', Int16),
('topics', Array(
('topic', String('utf-8')),
('partitions', Array(
('partition', Int32),
('offset', Int64),
('leader_epoch', Int32),
('metadata', String('utf-8')))))))
TxnOffsetCommitRequest = [
TxnOffsetCommitRequest_v0, TxnOffsetCommitRequest_v1, TxnOffsetCommitRequest_v2,
]
TxnOffsetCommitResponse = [
TxnOffsetCommitResponse_v0, TxnOffsetCommitResponse_v1, TxnOffsetCommitResponse_v2,
]

View File

@@ -77,6 +77,19 @@ class Int64(AbstractType):
return _unpack(cls._unpack, data.read(8))
class Float64(AbstractType):
_pack = struct.Struct('>d').pack
_unpack = struct.Struct('>d').unpack
@classmethod
def encode(cls, value):
return _pack(cls._pack, value)
@classmethod
def decode(cls, data):
return _unpack(cls._unpack, data.read(8))
class String(AbstractType):
def __init__(self, encoding='utf-8'):
self.encoding = encoding
@@ -181,9 +194,10 @@ class Array(AbstractType):
def encode(self, items):
if items is None:
return Int32.encode(-1)
encoded_items = [self.array_of.encode(item) for item in items]
return b''.join(
[Int32.encode(len(items))] +
[self.array_of.encode(item) for item in items]
[Int32.encode(len(encoded_items))] +
encoded_items
)
def decode(self, data):
@@ -196,3 +210,156 @@ class Array(AbstractType):
if list_of_items is None:
return 'NULL'
return '[' + ', '.join([self.array_of.repr(item) for item in list_of_items]) + ']'
class UnsignedVarInt32(AbstractType):
@classmethod
def decode(cls, data):
value, i = 0, 0
while True:
b, = struct.unpack('B', data.read(1))
if not (b & 0x80):
break
value |= (b & 0x7f) << i
i += 7
if i > 28:
raise ValueError('Invalid value {}'.format(value))
value |= b << i
return value
@classmethod
def encode(cls, value):
value &= 0xffffffff
ret = b''
while (value & 0xffffff80) != 0:
b = (value & 0x7f) | 0x80
ret += struct.pack('B', b)
value >>= 7
ret += struct.pack('B', value)
return ret
class VarInt32(AbstractType):
@classmethod
def decode(cls, data):
value = UnsignedVarInt32.decode(data)
return (value >> 1) ^ -(value & 1)
@classmethod
def encode(cls, value):
# bring it in line with the java binary repr
value &= 0xffffffff
return UnsignedVarInt32.encode((value << 1) ^ (value >> 31))
class VarInt64(AbstractType):
@classmethod
def decode(cls, data):
value, i = 0, 0
while True:
b = data.read(1)
if not (b & 0x80):
break
value |= (b & 0x7f) << i
i += 7
if i > 63:
raise ValueError('Invalid value {}'.format(value))
value |= b << i
return (value >> 1) ^ -(value & 1)
@classmethod
def encode(cls, value):
# bring it in line with the java binary repr
value &= 0xffffffffffffffff
v = (value << 1) ^ (value >> 63)
ret = b''
while (v & 0xffffffffffffff80) != 0:
b = (value & 0x7f) | 0x80
ret += struct.pack('B', b)
v >>= 7
ret += struct.pack('B', v)
return ret
class CompactString(String):
def decode(self, data):
length = UnsignedVarInt32.decode(data) - 1
if length < 0:
return None
value = data.read(length)
if len(value) != length:
raise ValueError('Buffer underrun decoding string')
return value.decode(self.encoding)
def encode(self, value):
if value is None:
return UnsignedVarInt32.encode(0)
value = str(value).encode(self.encoding)
return UnsignedVarInt32.encode(len(value) + 1) + value
class TaggedFields(AbstractType):
@classmethod
def decode(cls, data):
num_fields = UnsignedVarInt32.decode(data)
ret = {}
if not num_fields:
return ret
prev_tag = -1
for i in range(num_fields):
tag = UnsignedVarInt32.decode(data)
if tag <= prev_tag:
raise ValueError('Invalid or out-of-order tag {}'.format(tag))
prev_tag = tag
size = UnsignedVarInt32.decode(data)
val = data.read(size)
ret[tag] = val
return ret
@classmethod
def encode(cls, value):
ret = UnsignedVarInt32.encode(len(value))
for k, v in value.items():
# do we allow for other data types ?? It could get complicated really fast
assert isinstance(v, bytes), 'Value {} is not a byte array'.format(v)
assert isinstance(k, int) and k > 0, 'Key {} is not a positive integer'.format(k)
ret += UnsignedVarInt32.encode(k)
ret += v
return ret
class CompactBytes(AbstractType):
@classmethod
def decode(cls, data):
length = UnsignedVarInt32.decode(data) - 1
if length < 0:
return None
value = data.read(length)
if len(value) != length:
raise ValueError('Buffer underrun decoding Bytes')
return value
@classmethod
def encode(cls, value):
if value is None:
return UnsignedVarInt32.encode(0)
else:
return UnsignedVarInt32.encode(len(value) + 1) + value
class CompactArray(Array):
def encode(self, items):
if items is None:
return UnsignedVarInt32.encode(0)
return b''.join(
[UnsignedVarInt32.encode(len(items) + 1)] +
[self.array_of.encode(item) for item in items]
)
def decode(self, data):
length = UnsignedVarInt32.decode(data) - 1
if length == -1:
return None
return [self.array_of.decode(data) for _ in range(length)]