cassandra-driver 1.0.0.beta.2-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.yardopts +4 -0
- data/README.md +125 -0
- data/lib/cassandra/auth/providers/password.rb +73 -0
- data/lib/cassandra/auth/providers.rb +16 -0
- data/lib/cassandra/auth.rb +97 -0
- data/lib/cassandra/client/batch.rb +212 -0
- data/lib/cassandra/client/client.rb +591 -0
- data/lib/cassandra/client/column_metadata.rb +54 -0
- data/lib/cassandra/client/connection_manager.rb +72 -0
- data/lib/cassandra/client/connector.rb +277 -0
- data/lib/cassandra/client/execute_options_decoder.rb +59 -0
- data/lib/cassandra/client/null_logger.rb +37 -0
- data/lib/cassandra/client/peer_discovery.rb +50 -0
- data/lib/cassandra/client/prepared_statement.rb +314 -0
- data/lib/cassandra/client/query_result.rb +230 -0
- data/lib/cassandra/client/request_runner.rb +71 -0
- data/lib/cassandra/client/result_metadata.rb +48 -0
- data/lib/cassandra/client/void_result.rb +78 -0
- data/lib/cassandra/client.rb +144 -0
- data/lib/cassandra/cluster/client.rb +768 -0
- data/lib/cassandra/cluster/connector.rb +244 -0
- data/lib/cassandra/cluster/control_connection.rb +425 -0
- data/lib/cassandra/cluster/metadata.rb +124 -0
- data/lib/cassandra/cluster/options.rb +42 -0
- data/lib/cassandra/cluster/registry.rb +198 -0
- data/lib/cassandra/cluster/schema/partitioners/murmur3.rb +47 -0
- data/lib/cassandra/cluster/schema/partitioners/ordered.rb +37 -0
- data/lib/cassandra/cluster/schema/partitioners/random.rb +37 -0
- data/lib/cassandra/cluster/schema/partitioners.rb +21 -0
- data/lib/cassandra/cluster/schema/replication_strategies/network_topology.rb +92 -0
- data/lib/cassandra/cluster/schema/replication_strategies/none.rb +39 -0
- data/lib/cassandra/cluster/schema/replication_strategies/simple.rb +44 -0
- data/lib/cassandra/cluster/schema/replication_strategies.rb +21 -0
- data/lib/cassandra/cluster/schema/type_parser.rb +138 -0
- data/lib/cassandra/cluster/schema.rb +340 -0
- data/lib/cassandra/cluster.rb +215 -0
- data/lib/cassandra/column.rb +92 -0
- data/lib/cassandra/compression/compressors/lz4.rb +72 -0
- data/lib/cassandra/compression/compressors/snappy.rb +66 -0
- data/lib/cassandra/compression.rb +66 -0
- data/lib/cassandra/driver.rb +111 -0
- data/lib/cassandra/errors.rb +79 -0
- data/lib/cassandra/execution/info.rb +51 -0
- data/lib/cassandra/execution/options.rb +80 -0
- data/lib/cassandra/execution/trace.rb +152 -0
- data/lib/cassandra/future.rb +675 -0
- data/lib/cassandra/host.rb +79 -0
- data/lib/cassandra/keyspace.rb +133 -0
- data/lib/cassandra/listener.rb +87 -0
- data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +149 -0
- data/lib/cassandra/load_balancing/policies/round_robin.rb +132 -0
- data/lib/cassandra/load_balancing/policies/token_aware.rb +119 -0
- data/lib/cassandra/load_balancing/policies/white_list.rb +90 -0
- data/lib/cassandra/load_balancing/policies.rb +19 -0
- data/lib/cassandra/load_balancing.rb +113 -0
- data/lib/cassandra/protocol/cql_byte_buffer.rb +307 -0
- data/lib/cassandra/protocol/cql_protocol_handler.rb +323 -0
- data/lib/cassandra/protocol/frame_decoder.rb +128 -0
- data/lib/cassandra/protocol/frame_encoder.rb +48 -0
- data/lib/cassandra/protocol/request.rb +38 -0
- data/lib/cassandra/protocol/requests/auth_response_request.rb +47 -0
- data/lib/cassandra/protocol/requests/batch_request.rb +76 -0
- data/lib/cassandra/protocol/requests/credentials_request.rb +47 -0
- data/lib/cassandra/protocol/requests/execute_request.rb +103 -0
- data/lib/cassandra/protocol/requests/options_request.rb +39 -0
- data/lib/cassandra/protocol/requests/prepare_request.rb +50 -0
- data/lib/cassandra/protocol/requests/query_request.rb +153 -0
- data/lib/cassandra/protocol/requests/register_request.rb +38 -0
- data/lib/cassandra/protocol/requests/startup_request.rb +49 -0
- data/lib/cassandra/protocol/requests/void_query_request.rb +24 -0
- data/lib/cassandra/protocol/response.rb +38 -0
- data/lib/cassandra/protocol/responses/auth_challenge_response.rb +41 -0
- data/lib/cassandra/protocol/responses/auth_success_response.rb +41 -0
- data/lib/cassandra/protocol/responses/authenticate_response.rb +41 -0
- data/lib/cassandra/protocol/responses/detailed_error_response.rb +60 -0
- data/lib/cassandra/protocol/responses/error_response.rb +50 -0
- data/lib/cassandra/protocol/responses/event_response.rb +39 -0
- data/lib/cassandra/protocol/responses/prepared_result_response.rb +64 -0
- data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +43 -0
- data/lib/cassandra/protocol/responses/ready_response.rb +44 -0
- data/lib/cassandra/protocol/responses/result_response.rb +48 -0
- data/lib/cassandra/protocol/responses/rows_result_response.rb +139 -0
- data/lib/cassandra/protocol/responses/schema_change_event_response.rb +60 -0
- data/lib/cassandra/protocol/responses/schema_change_result_response.rb +57 -0
- data/lib/cassandra/protocol/responses/set_keyspace_result_response.rb +42 -0
- data/lib/cassandra/protocol/responses/status_change_event_response.rb +44 -0
- data/lib/cassandra/protocol/responses/supported_response.rb +41 -0
- data/lib/cassandra/protocol/responses/topology_change_event_response.rb +34 -0
- data/lib/cassandra/protocol/responses/void_result_response.rb +39 -0
- data/lib/cassandra/protocol/type_converter.rb +384 -0
- data/lib/cassandra/protocol.rb +93 -0
- data/lib/cassandra/reconnection/policies/constant.rb +48 -0
- data/lib/cassandra/reconnection/policies/exponential.rb +79 -0
- data/lib/cassandra/reconnection/policies.rb +20 -0
- data/lib/cassandra/reconnection.rb +49 -0
- data/lib/cassandra/result.rb +215 -0
- data/lib/cassandra/retry/policies/default.rb +47 -0
- data/lib/cassandra/retry/policies/downgrading_consistency.rb +71 -0
- data/lib/cassandra/retry/policies/fallthrough.rb +39 -0
- data/lib/cassandra/retry/policies.rb +21 -0
- data/lib/cassandra/retry.rb +142 -0
- data/lib/cassandra/session.rb +202 -0
- data/lib/cassandra/statement.rb +22 -0
- data/lib/cassandra/statements/batch.rb +95 -0
- data/lib/cassandra/statements/bound.rb +48 -0
- data/lib/cassandra/statements/prepared.rb +81 -0
- data/lib/cassandra/statements/simple.rb +58 -0
- data/lib/cassandra/statements/void.rb +33 -0
- data/lib/cassandra/statements.rb +23 -0
- data/lib/cassandra/table.rb +299 -0
- data/lib/cassandra/time_uuid.rb +142 -0
- data/lib/cassandra/util.rb +167 -0
- data/lib/cassandra/uuid.rb +104 -0
- data/lib/cassandra/version.rb +21 -0
- data/lib/cassandra.rb +428 -0
- data/lib/cassandra_murmur3.jar +0 -0
- metadata +211 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# Copyright 2013-2014 DataStax, Inc.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#++
|
|
18
|
+
|
|
19
|
+
module Cassandra
|
|
20
|
+
module LoadBalancing
|
|
21
|
+
module Policies
|
|
22
|
+
class WhiteList < Policy
|
|
23
|
+
extend Forwardable
|
|
24
|
+
|
|
25
|
+
# @!method plan(keyspace, statement, options)
|
|
26
|
+
# Delegates to wrapped policy
|
|
27
|
+
# @see Cassandra::LoadBalancing::Policy#plan
|
|
28
|
+
#
|
|
29
|
+
# @!method distance(host)
|
|
30
|
+
# Delegates to wrapped policy
|
|
31
|
+
# @see Cassandra::LoadBalancing::Policy#distance
|
|
32
|
+
def_delegators :@policy, :plan, :distance
|
|
33
|
+
|
|
34
|
+
# @param ips [Enumerable<String, IPAddr>] a list of ips to whitelist
|
|
35
|
+
# @param wrapped_policy [Cassandra::LoadBalancing::Policy] actual policy to filter
|
|
36
|
+
# @raise [ArgumentError] if arguments are of unexpected types
|
|
37
|
+
def initialize(ips, wrapped_policy)
|
|
38
|
+
raise ::ArgumentError, "ips must be enumerable" unless ips.respond_to?(:each)
|
|
39
|
+
methods = [:host_up, :host_down, :host_found, :host_lost, :distance, :plan]
|
|
40
|
+
|
|
41
|
+
unless methods.all? {|method| wrapped_policy.respond_to?(method)}
|
|
42
|
+
raise ::ArgumentError, "supplied policy must be a Cassandra::LoadBalancing::Policy, #{wrapped_policy.inspect} given"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
@ips = ::Set.new
|
|
46
|
+
@policy = wrapped_policy
|
|
47
|
+
|
|
48
|
+
ips.each do |ip|
|
|
49
|
+
case ip
|
|
50
|
+
when ::IPAddr
|
|
51
|
+
@ips << ip
|
|
52
|
+
when ::String
|
|
53
|
+
@ips << ::IPAddr.new(ip)
|
|
54
|
+
else
|
|
55
|
+
raise ::ArgumentError, "ips must contain only instance of String or IPAddr, #{ip.inspect} given"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Delegates to wrapped policy if host's ip is whitelisted
|
|
61
|
+
# @param host [Cassandra::Host] a host instance
|
|
62
|
+
# @see Cassandra::LoadBalancing::Policy#host_found
|
|
63
|
+
def host_found(host)
|
|
64
|
+
@policy.host_found(host) if @ips.include?(host.ip)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Delegates to wrapped policy if host's ip is whitelisted
|
|
68
|
+
# @param host [Cassandra::Host] a host instance
|
|
69
|
+
# @see Cassandra::LoadBalancing::Policy#host_lost
|
|
70
|
+
def host_lost(host)
|
|
71
|
+
@policy.host_lost(host) if @ips.include?(host.ip)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Delegates to wrapped policy if host's ip is whitelisted
|
|
75
|
+
# @param host [Cassandra::Host] a host instance
|
|
76
|
+
# @see Cassandra::LoadBalancing::Policy#host_up
|
|
77
|
+
def host_up(host)
|
|
78
|
+
@policy.host_up(host) if @ips.include?(host.ip)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Delegates to wrapped policy if host's ip is whitelisted
|
|
82
|
+
# @param host [Cassandra::Host] a host instance
|
|
83
|
+
# @see Cassandra::LoadBalancing::Policy#host_down
|
|
84
|
+
def host_down(host)
|
|
85
|
+
@policy.host_down(host) if @ips.include?(host.ip)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Copyright 2013-2014 DataStax, Inc.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
#++
|
|
15
|
+
|
|
16
|
+
require 'cassandra/load_balancing/policies/dc_aware_round_robin'
|
|
17
|
+
require 'cassandra/load_balancing/policies/round_robin'
|
|
18
|
+
require 'cassandra/load_balancing/policies/token_aware'
|
|
19
|
+
require 'cassandra/load_balancing/policies/white_list'
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# Copyright 2013-2014 DataStax, Inc.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#++
|
|
18
|
+
|
|
19
|
+
module Cassandra
|
|
20
|
+
module LoadBalancing
|
|
21
|
+
# A list of possible load balancing distances that
|
|
22
|
+
# {Cassandra::LoadBalancing::Policy#distance} must return
|
|
23
|
+
DISTANCES = [:ignore, :local, :remote].freeze
|
|
24
|
+
|
|
25
|
+
# @abstract Actual load balancing policies don't need to extend this class,
|
|
26
|
+
# only implement its methods. This class exists for documentation
|
|
27
|
+
# purposes only.
|
|
28
|
+
class Policy
|
|
29
|
+
# Allows policy to initialize with the cluster instance. This method is
|
|
30
|
+
# called once before connecting to the cluster.
|
|
31
|
+
# @param cluster [Cassandra::Cluster] current cluster instance
|
|
32
|
+
# @return [void]
|
|
33
|
+
def setup(cluster)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @see Cassandra::Listener#host_up
|
|
37
|
+
def host_up(host)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @see Cassandra::Listener#host_down
|
|
41
|
+
def host_down(host)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @see Cassandra::Listener#host_found
|
|
45
|
+
def host_found(host)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @see Cassandra::Listener#host_lost
|
|
49
|
+
def host_lost(host)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Returns a distance that determines how many connections (if any) the
|
|
53
|
+
# driver will open to the host.
|
|
54
|
+
#
|
|
55
|
+
# @param host [Cassandra::Host] a host instance
|
|
56
|
+
# @return [Symbol] distance to host. Must be one of
|
|
57
|
+
# {Cassandra::LoadBalancing::DISTANCES}
|
|
58
|
+
def distance(host)
|
|
59
|
+
:ignore
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Load balancing plan is used to determine the order in which hosts
|
|
63
|
+
# should be tried in case of a network failure.
|
|
64
|
+
#
|
|
65
|
+
# @note Hosts that should be ignored, must not be included in the Plan
|
|
66
|
+
#
|
|
67
|
+
# @param keyspace [String] current keyspace of the {Cassandra::Session}
|
|
68
|
+
# @param statement [Cassandra::Statement] actual statement to be executed
|
|
69
|
+
# @param options [Cassandra::Execution::Options] execution options to be used
|
|
70
|
+
# @raise [NotImplementedError] override this method to return a plan
|
|
71
|
+
# @return [Cassandra::LoadBalancing::Plan] a load balancing plan
|
|
72
|
+
def plan(keyspace, statement, options)
|
|
73
|
+
raise ::NotImplementedError, "must be implemented by a child"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# @return [String] a console-friendly representation of this policy
|
|
77
|
+
def inspect
|
|
78
|
+
"#<#{self.class.name}:0x#{self.object_id.to_s(16)}>"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# A load balancing plan is used to determine the order of hosts for running
|
|
83
|
+
# queries, preparing statements and establishing connections.
|
|
84
|
+
# @abstract Plans returned by {Cassandra::LoadBalancing::Policy#plan}
|
|
85
|
+
# implementations don't need to extend this class, only implement its
|
|
86
|
+
# methods. This class exists for documentation purposes only.
|
|
87
|
+
class Plan
|
|
88
|
+
# @return [Boolean] whether the plan contains any more hosts
|
|
89
|
+
def has_next?
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# @return [Cql::Host] next host to try
|
|
93
|
+
def next
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# @private
|
|
98
|
+
class EmptyPlan
|
|
99
|
+
def has_next?
|
|
100
|
+
false
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def next
|
|
104
|
+
nil
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# @private
|
|
109
|
+
EMPTY_PLAN = EmptyPlan.new
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
require 'cassandra/load_balancing/policies'
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# Copyright 2013-2014 DataStax, Inc.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#++
|
|
18
|
+
|
|
19
|
+
module Cassandra
|
|
20
|
+
module Protocol
|
|
21
|
+
class CqlByteBuffer < Ione::ByteBuffer
|
|
22
|
+
def read_unsigned_byte
|
|
23
|
+
read_byte
|
|
24
|
+
rescue RangeError => e
|
|
25
|
+
raise DecodingError, e.message, e.backtrace
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def read_varint(len=bytesize, signed=true)
|
|
29
|
+
bytes = read(len)
|
|
30
|
+
n = 0
|
|
31
|
+
bytes.each_byte do |b|
|
|
32
|
+
n = (n << 8) | b
|
|
33
|
+
end
|
|
34
|
+
if signed && bytes.getbyte(0) & 0x80 == 0x80
|
|
35
|
+
n -= 2**(bytes.length * 8)
|
|
36
|
+
end
|
|
37
|
+
n
|
|
38
|
+
rescue RangeError => e
|
|
39
|
+
raise DecodingError, e.message, e.backtrace
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def read_decimal(len=bytesize)
|
|
43
|
+
size = read_signed_int
|
|
44
|
+
number_string = read_varint(len - 4).to_s
|
|
45
|
+
if number_string.length <= size
|
|
46
|
+
if number_string.start_with?(MINUS)
|
|
47
|
+
number_string = number_string[1, number_string.length - 1]
|
|
48
|
+
fraction_string = MINUS + ZERO << DECIMAL_POINT
|
|
49
|
+
else
|
|
50
|
+
fraction_string = ZERO + DECIMAL_POINT
|
|
51
|
+
end
|
|
52
|
+
(size - number_string.length).times { fraction_string << ZERO }
|
|
53
|
+
fraction_string << number_string
|
|
54
|
+
else
|
|
55
|
+
fraction_string = number_string[0, number_string.length - size]
|
|
56
|
+
fraction_string << DECIMAL_POINT
|
|
57
|
+
fraction_string << number_string[number_string.length - size, number_string.length]
|
|
58
|
+
end
|
|
59
|
+
BigDecimal.new(fraction_string)
|
|
60
|
+
rescue DecodingError => e
|
|
61
|
+
raise DecodingError, e.message, e.backtrace
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def read_long
|
|
65
|
+
top, bottom = read(8).unpack(Formats::TWO_INTS_FORMAT)
|
|
66
|
+
return (top << 32) | bottom if top <= 0x7fffffff
|
|
67
|
+
top ^= 0xffffffff
|
|
68
|
+
bottom ^= 0xffffffff
|
|
69
|
+
-((top << 32) | bottom) - 1
|
|
70
|
+
rescue RangeError => e
|
|
71
|
+
raise DecodingError, e.message, e.backtrace
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def read_double
|
|
75
|
+
read(8).unpack(Formats::DOUBLE_FORMAT).first
|
|
76
|
+
rescue RangeError => e
|
|
77
|
+
raise DecodingError, "Not enough bytes available to decode a double: #{e.message}", e.backtrace
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def read_float
|
|
81
|
+
read(4).unpack(Formats::FLOAT_FORMAT).first
|
|
82
|
+
rescue RangeError => e
|
|
83
|
+
raise DecodingError, "Not enough bytes available to decode a float: #{e.message}", e.backtrace
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def read_signed_int
|
|
87
|
+
n = read_int
|
|
88
|
+
return n if n <= 0x7fffffff
|
|
89
|
+
n - 0xffffffff - 1
|
|
90
|
+
rescue RangeError => e
|
|
91
|
+
raise DecodingError, "Not enough bytes available to decode an int: #{e.message}", e.backtrace
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def read_unsigned_short
|
|
95
|
+
read_short
|
|
96
|
+
rescue RangeError => e
|
|
97
|
+
raise DecodingError, "Not enough bytes available to decode a short: #{e.message}", e.backtrace
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def read_string
|
|
101
|
+
length = read_unsigned_short
|
|
102
|
+
string = read(length)
|
|
103
|
+
string.force_encoding(::Encoding::UTF_8)
|
|
104
|
+
string
|
|
105
|
+
rescue RangeError => e
|
|
106
|
+
raise DecodingError, "Not enough bytes available to decode a string: #{e.message}", e.backtrace
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def read_long_string
|
|
110
|
+
length = read_signed_int
|
|
111
|
+
string = read(length)
|
|
112
|
+
string.force_encoding(::Encoding::UTF_8)
|
|
113
|
+
string
|
|
114
|
+
rescue RangeError => e
|
|
115
|
+
raise DecodingError, "Not enough bytes available to decode a long string: #{e.message}", e.backtrace
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def read_uuid(impl=Uuid)
|
|
119
|
+
impl.new(read_varint(16, false))
|
|
120
|
+
rescue DecodingError => e
|
|
121
|
+
raise DecodingError, "Not enough bytes available to decode a UUID: #{e.message}", e.backtrace
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def read_string_list
|
|
125
|
+
size = read_unsigned_short
|
|
126
|
+
Array.new(size) { read_string }
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def read_bytes
|
|
130
|
+
size = read_signed_int
|
|
131
|
+
return nil if size & 0x80000000 == 0x80000000
|
|
132
|
+
read(size)
|
|
133
|
+
rescue RangeError => e
|
|
134
|
+
raise DecodingError, "Not enough bytes available to decode a bytes: #{e.message}", e.backtrace
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def read_short_bytes
|
|
138
|
+
size = read_unsigned_short
|
|
139
|
+
return nil if size & 0x8000 == 0x8000
|
|
140
|
+
read(size)
|
|
141
|
+
rescue RangeError => e
|
|
142
|
+
raise DecodingError, "Not enough bytes available to decode a short bytes: #{e.message}", e.backtrace
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def read_option
|
|
146
|
+
id = read_unsigned_short
|
|
147
|
+
value = nil
|
|
148
|
+
if block_given?
|
|
149
|
+
value = yield id, self
|
|
150
|
+
end
|
|
151
|
+
[id, value]
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def read_inet
|
|
155
|
+
size = read_byte
|
|
156
|
+
ip_addr = IPAddr.new_ntoh(read(size))
|
|
157
|
+
port = read_int
|
|
158
|
+
[ip_addr, port]
|
|
159
|
+
rescue RangeError => e
|
|
160
|
+
raise DecodingError, "Not enough bytes available to decode an INET: #{e.message}", e.backtrace
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def read_consistency
|
|
164
|
+
index = read_unsigned_short
|
|
165
|
+
raise DecodingError, "Unknown consistency index #{index}" if index >= CONSISTENCIES.size || CONSISTENCIES[index].nil?
|
|
166
|
+
CONSISTENCIES[index]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def read_string_map
|
|
170
|
+
map = {}
|
|
171
|
+
map_size = read_unsigned_short
|
|
172
|
+
map_size.times do
|
|
173
|
+
key = read_string
|
|
174
|
+
map[key] = read_string
|
|
175
|
+
end
|
|
176
|
+
map
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def read_string_multimap
|
|
180
|
+
map = {}
|
|
181
|
+
map_size = read_unsigned_short
|
|
182
|
+
map_size.times do
|
|
183
|
+
key = read_string
|
|
184
|
+
map[key] = read_string_list
|
|
185
|
+
end
|
|
186
|
+
map
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def append_int(n)
|
|
190
|
+
append([n].pack(Formats::INT_FORMAT))
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def append_short(n)
|
|
194
|
+
append([n].pack(Formats::SHORT_FORMAT))
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def append_string(str)
|
|
198
|
+
str = str.to_s
|
|
199
|
+
append_short(str.bytesize)
|
|
200
|
+
append(str)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def append_long_string(str)
|
|
204
|
+
append_int(str.bytesize)
|
|
205
|
+
append(str)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def append_uuid(uuid)
|
|
209
|
+
v = uuid.value
|
|
210
|
+
append_int((v >> 96) & 0xffffffff)
|
|
211
|
+
append_int((v >> 64) & 0xffffffff)
|
|
212
|
+
append_int((v >> 32) & 0xffffffff)
|
|
213
|
+
append_int((v >> 0) & 0xffffffff)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def append_string_list(strs)
|
|
217
|
+
append_short(strs.size)
|
|
218
|
+
strs.each do |str|
|
|
219
|
+
append_string(str)
|
|
220
|
+
end
|
|
221
|
+
self
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def append_bytes(bytes)
|
|
225
|
+
if bytes
|
|
226
|
+
append_int(bytes.bytesize)
|
|
227
|
+
append(bytes)
|
|
228
|
+
else
|
|
229
|
+
append_int(-1)
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def append_short_bytes(bytes)
|
|
234
|
+
if bytes
|
|
235
|
+
append_short(bytes.bytesize)
|
|
236
|
+
append(bytes)
|
|
237
|
+
else
|
|
238
|
+
append_short(-1)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def append_consistency(consistency)
|
|
243
|
+
index = CONSISTENCIES.index(consistency)
|
|
244
|
+
raise EncodingError, %(Unknown consistency "#{consistency}") if index.nil? || CONSISTENCIES[index].nil?
|
|
245
|
+
append_short(index)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def append_string_map(map)
|
|
249
|
+
append_short(map.size)
|
|
250
|
+
map.each do |key, value|
|
|
251
|
+
append_string(key)
|
|
252
|
+
append_string(value)
|
|
253
|
+
end
|
|
254
|
+
self
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def append_long(n)
|
|
258
|
+
top = n >> 32
|
|
259
|
+
bottom = n & 0xffffffff
|
|
260
|
+
append_int(top)
|
|
261
|
+
append_int(bottom)
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def append_varint(n)
|
|
265
|
+
num = n
|
|
266
|
+
bytes = []
|
|
267
|
+
begin
|
|
268
|
+
bytes << (num & 0xff)
|
|
269
|
+
num >>= 8
|
|
270
|
+
end until (num == 0 || num == -1) && (bytes.last[7] == num[7])
|
|
271
|
+
append(bytes.reverse.pack(Formats::BYTES_FORMAT))
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def append_decimal(n)
|
|
275
|
+
str = n.to_s(FLOAT_STRING_FORMAT)
|
|
276
|
+
size = str.index(DECIMAL_POINT)
|
|
277
|
+
number_string = str.gsub(DECIMAL_POINT, NO_CHAR)
|
|
278
|
+
|
|
279
|
+
num = number_string.to_i
|
|
280
|
+
raw = self.class.new.append_varint(num)
|
|
281
|
+
append_int(number_string.length - size)
|
|
282
|
+
append(raw)
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def append_double(n)
|
|
286
|
+
append([n].pack(Formats::DOUBLE_FORMAT))
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def append_float(n)
|
|
290
|
+
append([n].pack(Formats::FLOAT_FORMAT))
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def eql?(other)
|
|
294
|
+
other.eql?(to_str)
|
|
295
|
+
end
|
|
296
|
+
alias_method :==, :eql?
|
|
297
|
+
|
|
298
|
+
private
|
|
299
|
+
|
|
300
|
+
MINUS = '-'.freeze
|
|
301
|
+
ZERO = '0'.freeze
|
|
302
|
+
DECIMAL_POINT = '.'.freeze
|
|
303
|
+
FLOAT_STRING_FORMAT = 'F'.freeze
|
|
304
|
+
NO_CHAR = ''.freeze
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
end
|