cql-rb 2.0.5 → 2.1.0.pre0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 076ceeab9f5eff0e9ab577f1e433b087342a8457
4
- data.tar.gz: 049f3ecd2f935016bdd4bb08a87648163609600b
3
+ metadata.gz: 8f408afa031a9e58aebb3be39e8d0c0d2b0ad33b
4
+ data.tar.gz: 7312ad424a8fd77f81af3cbb356018a7a5154d3a
5
5
  SHA512:
6
- metadata.gz: e08c27d6a73bd4fc11573958ae2216affc238180d90d92c32aee98b20103a8614e6c1b693845b000c465b4ba9fd04b25c45a9ba012535a91e635974d832c66e5
7
- data.tar.gz: 0e897851f1dab23f7eaa7ac1380071f4fa2bf0511a16911a5976156d197a533ba83b0fc05a2ee925050b9bc8b96f3ebf31a937158eddd222503b6e68c5343a7d
6
+ metadata.gz: 774a0d581565ad62dbb963e77811431aeb360ec54fea83ec0bba98fe3502e4de076db0bdf9b67494da0b624e5d773c58fd13ae6e0f62b733b749b5392043bf83
7
+ data.tar.gz: 012108242af73ebc3c32d17016e1fcd1667c4b1c16b25206ad6fa462e5c7e04d4848f24649881711089e6d9dcc40eb76d2639082966e64f6da5364bc29ef6ba1
data/README.md CHANGED
@@ -1,3 +1,15 @@
1
+ # This is not the Cassandra driver you are looking for
2
+
3
+ cql-rb has graduated from community driver to being the foundation of the official [Datastax Ruby Driver for Apache Cassandra](https://github.com/datastax/ruby-driver).
4
+
5
+ There will be no more development in this repository, with the exception of critical bug fixes. I encourage everyone to start migrating to the new driver as soon as you can, it's got some great new features that you should try out.
6
+
7
+ The cql-rb code and the old readme will remain here as legacy documentation.
8
+
9
+ Read [the announcement of the new Ruby driver](http://www.datastax.com/dev/blog/ruby-driver-beta-1-release) or [the documentation with all the new features](http://datastax.github.io/ruby-driver/).
10
+
11
+ ---
12
+
1
13
  # Ruby CQL3 driver
2
14
 
3
15
  [![Build Status](https://travis-ci.org/iconara/cql-rb.png?branch=master)](https://travis-ci.org/iconara/cql-rb)
data/lib/cql.rb CHANGED
@@ -17,7 +17,6 @@ module Cql
17
17
  Io = Ione::Io
18
18
  end
19
19
 
20
- require 'cql/error_codes'
21
20
  require 'cql/uuid'
22
21
  require 'cql/time_uuid'
23
22
  require 'cql/compression'
@@ -6,8 +6,6 @@ module Cql
6
6
  # if any. `message` contains the human readable error message sent by the
7
7
  # server.
8
8
  class QueryError < CqlError
9
- include ErrorCodes
10
-
11
9
  attr_reader :code, :cql, :details
12
10
 
13
11
  def initialize(code, message, cql=nil, details=nil)
@@ -224,7 +224,9 @@ module Cql
224
224
  @connection_manager = ConnectionManager.new
225
225
  @execute_options_decoder = ExecuteOptionsDecoder.new(options[:default_consistency] || DEFAULT_CONSISTENCY)
226
226
  @port = options[:port] || DEFAULT_PORT
227
- @connection_timeout = options[:connection_timeout] || DEFAULT_CONNECTION_TIMEOUT
227
+ @connection_options = {}
228
+ @connection_options[:timeout] = options[:connection_timeout] || DEFAULT_CONNECTION_TIMEOUT
229
+ @connection_options[:ssl] = options[:ssl] if options[:ssl]
228
230
  @credentials = options[:credentials]
229
231
  @auth_provider = options[:auth_provider] || @credentials && Auth::PlainTextAuthProvider.new(*@credentials.values_at(:username, :password))
230
232
  @connected = false
@@ -352,7 +354,7 @@ module Cql
352
354
  protocol_handler_factory = lambda { |connection| Protocol::CqlProtocolHandler.new(connection, @io_reactor, @protocol_version, @compressor) }
353
355
  ClusterConnector.new(
354
356
  Connector.new([
355
- ConnectStep.new(@io_reactor, protocol_handler_factory, @port, @connection_timeout, @logger),
357
+ ConnectStep.new(@io_reactor, protocol_handler_factory, @port, @connection_options, @logger),
356
358
  CacheOptionsStep.new,
357
359
  InitializeStep.new(cql_version, @compressor, @logger),
358
360
  authentication_step,
@@ -365,7 +367,7 @@ module Cql
365
367
  def connect_with_protocol_version_fallback
366
368
  f = create_cluster_connector.connect_all(@hosts, @connections_per_node)
367
369
  f.fallback do |error|
368
- if error.is_a?(QueryError) && error.code == QueryError::PROTOCOL_ERROR && @protocol_version > 1
370
+ if error.is_a?(QueryError) && error.code == 0x0a && @protocol_version > 1
369
371
  @logger.warn('Could not connect using protocol version %d (will try again with %d): %s' % [@protocol_version, @protocol_version - 1, error.message])
370
372
  @protocol_version -= 1
371
373
  connect_with_protocol_version_fallback
@@ -24,7 +24,7 @@ module Cql
24
24
  connected_connections = connections.select(&:connected?)
25
25
  if connected_connections.empty?
26
26
  e = connections.first.error
27
- if e.is_a?(QueryError) && e.code == QueryError::BAD_CREDENTIALS
27
+ if e.is_a?(Cql::QueryError) && e.code == 0x100
28
28
  e = AuthenticationError.new(e.message)
29
29
  end
30
30
  raise e
@@ -72,17 +72,17 @@ module Cql
72
72
 
73
73
  # @private
74
74
  class ConnectStep
75
- def initialize(io_reactor, protocol_handler_factory, port, connection_timeout, logger)
75
+ def initialize(io_reactor, protocol_handler_factory, port, connection_options, logger)
76
76
  @io_reactor = io_reactor
77
77
  @protocol_handler_factory = protocol_handler_factory
78
78
  @port = port
79
- @connection_timeout = connection_timeout
79
+ @connection_options = connection_options
80
80
  @logger = logger
81
81
  end
82
82
 
83
83
  def run(pending_connection)
84
84
  @logger.debug('Connecting to node at %s:%d' % [pending_connection.host, @port])
85
- @io_reactor.connect(pending_connection.host, @port, @connection_timeout, &@protocol_handler_factory).map do |connection|
85
+ @io_reactor.connect(pending_connection.host, @port, @connection_options, &@protocol_handler_factory).map do |connection|
86
86
  pending_connection.with_connection(connection)
87
87
  end
88
88
  end
@@ -150,13 +150,7 @@ module Cql
150
150
  def execute(*args)
151
151
  connection = @connection_manager.random_connection
152
152
  if connection[self]
153
- f = run(args, connection)
154
- f.fallback do |e|
155
- raise e unless e.is_a?(QueryError) && e.code == QueryError::UNPREPARED
156
- prepare(connection).flat_map do
157
- run(args, connection)
158
- end
159
- end
153
+ run(args, connection)
160
154
  else
161
155
  prepare(connection).flat_map do
162
156
  run(args, connection)
@@ -222,12 +216,11 @@ module Cql
222
216
  private
223
217
 
224
218
  def run(args, connection)
225
- bound_args = args.take(@raw_metadata.size)
226
- remaining_args = args.drop(@raw_metadata.size)
227
- unless bound_args.size == @raw_metadata.size && remaining_args.size <= 1
219
+ bound_args = args.shift(@raw_metadata.size)
220
+ unless bound_args.size == @raw_metadata.size && args.size <= 1
228
221
  raise ArgumentError, "Expected #{@raw_metadata.size} arguments, got #{bound_args.size}"
229
222
  end
230
- options = @execute_options_decoder.decode_options(remaining_args.last)
223
+ options = @execute_options_decoder.decode_options(args.last)
231
224
  statement_id = connection[self]
232
225
  request_metadata = @raw_result_metadata.nil?
233
226
  request = Protocol::ExecuteRequest.new(statement_id, @raw_metadata, bound_args, request_metadata, options[:consistency], options[:serial_consistency], options[:page_size], options[:paging_state], options[:trace])
@@ -43,6 +43,7 @@ end
43
43
 
44
44
  require 'cql/protocol/cql_byte_buffer'
45
45
  require 'cql/protocol/type_converter'
46
+ require 'cql/protocol/custom_type_parser'
46
47
  require 'cql/protocol/response'
47
48
  require 'cql/protocol/responses/auth_challenge_response'
48
49
  require 'cql/protocol/responses/auth_success_response'
@@ -203,6 +203,7 @@ module Cql
203
203
  complete_request(id, @current_frame.body)
204
204
  end
205
205
  @current_frame = @frame_decoder.decode_frame(@read_buffer)
206
+ flush_request_queue
206
207
  end
207
208
  end
208
209
 
@@ -232,7 +233,6 @@ module Cql
232
233
  if response.is_a?(Protocol::SetKeyspaceResultResponse)
233
234
  @keyspace = response.keyspace
234
235
  end
235
- flush_request_queue
236
236
  unless promise.timed_out?
237
237
  promise.fulfill(response)
238
238
  end
@@ -256,7 +256,7 @@ module Cql
256
256
  if @request_queue_out.any? && (id = next_stream_id)
257
257
  promise = @request_queue_out.shift
258
258
  if promise.timed_out?
259
- next
259
+ id = nil
260
260
  else
261
261
  frame = promise.frame
262
262
  @promises[id] = promise
@@ -0,0 +1,114 @@
1
+ # encoding: utf-8
2
+
3
+ module Cql
4
+ module Protocol
5
+ class CustomTypeParser
6
+ def parse_type(str)
7
+ parse_custom_type(str).first
8
+ end
9
+
10
+ private
11
+
12
+ def parse_custom_type(str)
13
+ open_parentheses_index = str.index('(')
14
+ if open_parentheses_index
15
+ type = str[0, open_parentheses_index]
16
+ rest = str[open_parentheses_index + 1, str.bytesize - open_parentheses_index - 1]
17
+ if type == USER_TYPE
18
+ start_index = str.index(',', str.index(',') + 1)
19
+ rest = str[start_index + 1, str.bytesize - start_index - 1]
20
+ field_types, rest = parse_user_type_fields(rest)
21
+ rest = rest && rest[0, rest.bytesize - 1]
22
+ [[:udt, Hash[field_types]], rest]
23
+ elsif type == LIST_TYPE
24
+ type, rest = parse_custom_type(rest)
25
+ rest = rest && rest[1, rest.bytesize - 1]
26
+ [[:list, type], rest]
27
+ elsif type == MAP_TYPE
28
+ key_type, value_type, rest = parse_map_fields(rest)
29
+ rest = rest && rest[1, rest.bytesize - 1]
30
+ [[:map, key_type, value_type], rest]
31
+ elsif type == SET_TYPE
32
+ type, rest = parse_custom_type(rest)
33
+ rest = rest && rest[1, rest.bytesize - 1]
34
+ [[:set, type], rest]
35
+ else
36
+ [nil, rest]
37
+ end
38
+ else
39
+ type = SCALAR_TYPES.keys.find do |type|
40
+ str.start_with?(type)
41
+ end
42
+ if type
43
+ rest = str[type.bytesize, str.bytesize - str.bytesize]
44
+ [SCALAR_TYPES[type], rest]
45
+ else
46
+ [[:custom, str], nil]
47
+ end
48
+ end
49
+ end
50
+
51
+ def parse_user_type_fields(str)
52
+ return [[], str] if str.nil? || str.empty?
53
+ comma_index = str.index(',')
54
+ open_parentheses_index = str.index('(')
55
+ close_parentheses_index = str.index(')')
56
+ if close_parentheses_index == 0
57
+ return [[], str]
58
+ elsif open_parentheses_index && (comma_index.nil? || comma_index > open_parentheses_index)
59
+ end_index = open_parentheses_index
60
+ elsif comma_index && (close_parentheses_index.nil? || close_parentheses_index > comma_index)
61
+ end_index = comma_index
62
+ else
63
+ end_index = close_parentheses_index
64
+ end
65
+ field = str[0, end_index]
66
+ name, type = field.split(':')
67
+ name = [name].pack('H*')
68
+ rest = str[end_index + 1, str.bytesize - end_index - 1]
69
+ if end_index == open_parentheses_index
70
+ colon_index = field.index(':')
71
+ type, rest = parse_custom_type(str[colon_index + 1, str.bytesize - colon_index - 1])
72
+ types, rest = parse_user_type_fields(rest)
73
+ elsif end_index == comma_index
74
+ types, rest = parse_user_type_fields(rest)
75
+ type = SCALAR_TYPES[type]
76
+ else
77
+ type = SCALAR_TYPES[type]
78
+ end
79
+ [[[name, type], *types], rest]
80
+ end
81
+
82
+ def parse_map_fields(str)
83
+ key_type, rest = parse_custom_type(str)
84
+ rest = rest[1, rest.bytesize - 1]
85
+ value_type, rest = parse_custom_type(rest)
86
+ [key_type, value_type, rest]
87
+ end
88
+
89
+ LIST_TYPE = 'org.apache.cassandra.db.marshal.ListType'.freeze
90
+ MAP_TYPE = 'org.apache.cassandra.db.marshal.MapType'.freeze
91
+ SET_TYPE = 'org.apache.cassandra.db.marshal.SetType'.freeze
92
+ USER_TYPE = 'org.apache.cassandra.db.marshal.UserType'.freeze
93
+
94
+ SCALAR_TYPES = {
95
+ 'org.apache.cassandra.db.marshal.AsciiType' => :ascii,
96
+ 'org.apache.cassandra.db.marshal.BooleanType' => :boolean,
97
+ 'org.apache.cassandra.db.marshal.BytesType' => :bytes,
98
+ 'org.apache.cassandra.db.marshal.CounterColumnType' => :counter,
99
+ 'org.apache.cassandra.db.marshal.DateType' => :date,
100
+ 'org.apache.cassandra.db.marshal.DecimalType' => :decimal,
101
+ 'org.apache.cassandra.db.marshal.DoubleType' => :double,
102
+ 'org.apache.cassandra.db.marshal.FloatType' => :float,
103
+ 'org.apache.cassandra.db.marshal.InetAddressType' => :inet,
104
+ 'org.apache.cassandra.db.marshal.Int32Type' => :int,
105
+ 'org.apache.cassandra.db.marshal.IntegerType' => :int,
106
+ 'org.apache.cassandra.db.marshal.LongType' => :bigint,
107
+ 'org.apache.cassandra.db.marshal.TimeUUIDType' => :time_uuid,
108
+ 'org.apache.cassandra.db.marshal.TimestampType' => :timestamp,
109
+ 'org.apache.cassandra.db.marshal.UTF8Type' => :text,
110
+ 'org.apache.cassandra.db.marshal.UUIDType' => :uuid,
111
+ }.freeze
112
+ end
113
+ end
114
+ end
@@ -13,24 +13,24 @@ module Cql
13
13
  def self.decode(code, message, protocol_version, buffer, length, trace_id=nil)
14
14
  details = {}
15
15
  case code
16
- when UNAVAILABLE
16
+ when 0x1000 # unavailable
17
17
  details[:cl] = buffer.read_consistency
18
18
  details[:required] = buffer.read_int
19
19
  details[:alive] = buffer.read_int
20
- when WRITE_TIMEOUT
20
+ when 0x1100 # write_timeout
21
21
  details[:cl] = buffer.read_consistency
22
22
  details[:received] = buffer.read_int
23
23
  details[:blockfor] = buffer.read_int
24
24
  details[:write_type] = buffer.read_string
25
- when READ_TIMEOUT
25
+ when 0x1200 # read_timeout
26
26
  details[:cl] = buffer.read_consistency
27
27
  details[:received] = buffer.read_int
28
28
  details[:blockfor] = buffer.read_int
29
29
  details[:data_present] = buffer.read_byte != 0
30
- when ALREADY_EXISTS
30
+ when 0x2400 # already_exists
31
31
  details[:ks] = buffer.read_string
32
32
  details[:table] = buffer.read_string
33
- when UNPREPARED
33
+ when 0x2500
34
34
  details[:id] = buffer.read_short_bytes
35
35
  end
36
36
  new(code, message, details)
@@ -3,8 +3,6 @@
3
3
  module Cql
4
4
  module Protocol
5
5
  class ErrorResponse < Response
6
- include ErrorCodes
7
-
8
6
  attr_reader :code, :message
9
7
 
10
8
  def initialize(*args)
@@ -15,7 +13,7 @@ module Cql
15
13
  code = buffer.read_int
16
14
  message = buffer.read_string
17
15
  case code
18
- when UNAVAILABLE, WRITE_TIMEOUT, READ_TIMEOUT, ALREADY_EXISTS, UNPREPARED
16
+ when 0x1000, 0x1100, 0x1200, 0x2400, 0x2500
19
17
  new_length = length - 4 - 4 - message.bytesize
20
18
  DetailedErrorResponse.decode(code, message, protocol_version, buffer, new_length)
21
19
  else
@@ -51,6 +51,7 @@ module Cql
51
51
  ].freeze
52
52
 
53
53
  TYPE_CONVERTER = TypeConverter.new
54
+ CUSTOM_TYPE_PARSER = CustomTypeParser.new
54
55
 
55
56
  GLOBAL_TABLES_SPEC_FLAG = 0x01
56
57
  HAS_MORE_PAGES_FLAG = 0x02
@@ -58,7 +59,9 @@ module Cql
58
59
 
59
60
  def self.read_column_type(buffer)
60
61
  id, type = buffer.read_option do |id, b|
61
- if id > 0 && id <= 0x10
62
+ if id == 0
63
+ CUSTOM_TYPE_PARSER.parse_type(buffer.read_string)
64
+ elsif id > 0 && id <= 0x10
62
65
  COLUMN_TYPES[id]
63
66
  elsif id == 0x20
64
67
  sub_type = read_column_type(buffer)
@@ -12,56 +12,79 @@ module Cql
12
12
  @to_bytes_converters = to_bytes_converters
13
13
  end
14
14
 
15
- def from_bytes(buffer, type, size_bytes=4)
15
+ def from_bytes(buffer, type, size_bytes=4, override_size=false)
16
16
  return nil if buffer.empty?
17
17
  case type
18
18
  when Array
19
- return nil unless read_size(buffer, size_bytes)
19
+ size = read_size(buffer, size_bytes)
20
+ return nil unless size
21
+ size_bytes = override_size ? size_bytes : 2
20
22
  case type.first
21
23
  when :list
22
- bytes_to_list(buffer, @from_bytes_converters[type[1]])
24
+ bytes_to_list(buffer, type[1], size_bytes, override_size)
23
25
  when :map
24
- bytes_to_map(buffer, @from_bytes_converters[type[1]], @from_bytes_converters[type[2]])
26
+ bytes_to_map(buffer, type[1], type[2], size_bytes, override_size)
25
27
  when :set
26
- bytes_to_set(buffer, @from_bytes_converters[type[1]])
28
+ bytes_to_set(buffer, type[1], size_bytes, override_size)
29
+ when :udt
30
+ bytes_to_udt_value(buffer, type)
31
+ when :custom
32
+ bytes_to_custom(buffer, size)
27
33
  end
28
34
  else
29
35
  @from_bytes_converters[type].call(buffer, size_bytes)
30
36
  end
31
37
  end
32
38
 
33
- def to_bytes(buffer, type, value, size_bytes=4)
39
+ def to_bytes(buffer, type, value, size_bytes=4, override_size=false)
34
40
  case type
35
41
  when Array
36
- unless value.nil? || value.is_a?(Enumerable)
37
- raise InvalidValueError, 'Value for collection must be enumerable'
38
- end
39
42
  case type.first
40
43
  when :list, :set
44
+ unless value.nil? || value.is_a?(Enumerable)
45
+ raise InvalidValueError, 'Value for %s must be enumerable' % type
46
+ end
41
47
  _, sub_type = type
42
48
  if value
49
+ size_bytes = override_size ? size_bytes : 2
43
50
  raw = CqlByteBuffer.new
44
- raw.append_short(value.size)
51
+ if size_bytes == 2
52
+ raw.append_short(value.size)
53
+ else
54
+ raw.append_int(value.size)
55
+ end
45
56
  value.each do |element|
46
- to_bytes(raw, sub_type, element, 2)
57
+ to_bytes(raw, sub_type, element, size_bytes, override_size)
47
58
  end
48
59
  buffer.append_bytes(raw)
49
60
  else
50
61
  nil_to_bytes(buffer, size_bytes)
51
62
  end
52
63
  when :map
64
+ unless value.nil? || value.is_a?(Enumerable)
65
+ raise InvalidValueError, 'Value for %s must be enumerable' % type
66
+ end
53
67
  _, key_type, value_type = type
54
68
  if value
69
+ size_bytes = override_size ? size_bytes : 2
55
70
  raw = CqlByteBuffer.new
56
- raw.append_short(value.size)
71
+ if size_bytes == 2
72
+ raw.append_short(value.size)
73
+ else
74
+ raw.append_int(value.size)
75
+ end
57
76
  value.each do |key, value|
58
- to_bytes(raw, key_type, key, 2)
59
- to_bytes(raw, value_type, value, 2)
77
+ to_bytes(raw, key_type, key, size_bytes, override_size)
78
+ to_bytes(raw, value_type, value, size_bytes, override_size)
60
79
  end
61
80
  buffer.append_bytes(raw)
62
81
  else
63
82
  nil_to_bytes(buffer, size_bytes)
64
83
  end
84
+ when :udt
85
+ udt_to_bytes(buffer, type[1], value, size_bytes)
86
+ when :custom
87
+ custom_to_bytes(buffer, type[1], value, size_bytes)
65
88
  else
66
89
  raise UnsupportedColumnTypeError, %(Unsupported column collection type: #{type.first})
67
90
  end
@@ -205,35 +228,47 @@ module Cql
205
228
  IPAddr.new_ntoh(buffer.read(size))
206
229
  end
207
230
 
208
- def bytes_to_list(buffer, value_converter)
231
+ def bytes_to_list(buffer, subtype, size_bytes, override_size)
209
232
  list = []
210
- size = buffer.read_short
233
+ size = read_size(buffer, size_bytes)
211
234
  size.times do
212
- list << value_converter.call(buffer, 2)
235
+ list << from_bytes(buffer, subtype, size_bytes, override_size)
213
236
  end
214
237
  list
215
238
  end
216
239
 
217
- def bytes_to_map(buffer, key_converter, value_converter)
240
+ def bytes_to_map(buffer, key_type, value_type, size_bytes, override_size)
218
241
  map = {}
219
- size = buffer.read_short
242
+ size = read_size(buffer, size_bytes)
220
243
  size.times do
221
- key = key_converter.call(buffer, 2)
222
- value = value_converter.call(buffer, 2)
244
+ key = from_bytes(buffer, key_type, size_bytes, override_size)
245
+ value = from_bytes(buffer, value_type, size_bytes, override_size)
223
246
  map[key] = value
224
247
  end
225
248
  map
226
249
  end
227
250
 
228
- def bytes_to_set(buffer, value_converter)
251
+ def bytes_to_set(buffer, subtype, size_bytes, override_size)
229
252
  set = Set.new
230
- size = buffer.read_short
253
+ size = read_size(buffer, size_bytes)
231
254
  size.times do
232
- set << value_converter.call(buffer, 2)
255
+ set << from_bytes(buffer, subtype, size_bytes, override_size)
233
256
  end
234
257
  set
235
258
  end
236
259
 
260
+ def bytes_to_udt_value(buffer, type)
261
+ value = {}
262
+ type[1].each do |name, subtype|
263
+ value[name] = from_bytes(buffer, subtype, 4, true)
264
+ end
265
+ value
266
+ end
267
+
268
+ def bytes_to_custom(buffer, size)
269
+ buffer.read(size)
270
+ end
271
+
237
272
  def ascii_to_bytes(buffer, value, size_bytes)
238
273
  v = value && value.encode(::Encoding::ASCII)
239
274
  if size_bytes == 4
@@ -367,6 +402,29 @@ module Cql
367
402
  buffer.append_short(-1)
368
403
  end
369
404
  end
405
+
406
+ def udt_to_bytes(buffer, type, value, size_bytes)
407
+ if value
408
+ offset = buffer.length
409
+ size_to_bytes(buffer, 0, size_bytes)
410
+ type.each do |field_name, field_type|
411
+ field_value = value[field_name]
412
+ to_bytes(buffer, field_type, field_value, 4, true)
413
+ end
414
+ buffer.update(offset, size_to_bytes(CqlByteBuffer.new, buffer.length - offset - size_bytes, size_bytes))
415
+ else
416
+ nil_to_bytes(buffer, size_bytes)
417
+ end
418
+ end
419
+
420
+ def custom_to_bytes(buffer, type, value, size_bytes)
421
+ if value
422
+ size_to_bytes(buffer, value.size, size_bytes)
423
+ buffer.append(value)
424
+ else
425
+ nil_to_bytes(buffer, size_bytes)
426
+ end
427
+ end
370
428
  end
371
429
  end
372
430
  end