Class: Google::Protobuf::Map

Inherits:
Object
  • Object
show all
Extended by:
Internal::Convert
Includes:
Enumerable, Internal::Convert
Defined in:
lib/google/protobuf/ffi/map.rb

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Internal::Convert

convert_ruby_to_upb, convert_upb_to_ruby, map_create_hash, message_value_deep_copy, repeated_field_create_array, scalar_create_hash, to_h_internal

Class Method Details

.new(key_type, value_type, value_typeclass = nil, init_hashmap = {}) ⇒ Object

call-seq:

Map.new(key_type, value_type, value_typeclass = nil, init_hashmap = {})
=> new map

Allocates a new Map container. This constructor may be called with 2, 3, or 4 arguments. The first two arguments are always present and are symbols (taking on the same values as field-type symbols in message descriptors) that indicate the type of the map key and value fields.

The supported key types are: :int32, :int64, :uint32, :uint64, :bool, :string, :bytes.

The supported value types are: :int32, :int64, :uint32, :uint64, :bool, :string, :bytes, :enum, :message.

The third argument, value_typeclass, must be present if value_type is :enum or :message. As in RepeatedField#new, this argument must be a message class (for :message) or enum module (for :enum).

The last argument, if present, provides initial content for map. Note that this may be an ordinary Ruby hashmap or another Map instance with identical key and value types. Also note that this argument may be present whether or not value_typeclass is present (and it is unambiguously separate from value_typeclass because value_typeclass’s presence is strictly determined by value_type). The contents of this initial hashmap or Map instance are shallow-copied into the new Map: the original map is unmodified, but references to underlying objects will be shared if the value type is a message type.



58
59
60
61
62
63
64
65
66
67
68
# File 'lib/google/protobuf/ffi/map.rb', line 58

def self.new(key_type, value_type, value_typeclass = nil, init_hashmap = {})
  instance = allocate
  # TODO This argument mangling doesn't agree with the type signature,
  # but does align with the text of the comments and is required to make unit tests pass.
  if init_hashmap.empty? and ![:enum, :message].include?(value_type)
    init_hashmap = value_typeclass
    value_typeclass = nil
  end
  instance.send(:initialize, key_type, value_type, value_type_class: value_typeclass, initial_values: init_hashmap)
  instance
end

Instance Method Details

#==(other) ⇒ Object

call-seq:

Map.==(other) => boolean

Compares this map to another. Maps are equal if they have identical key sets, and for each key, the values in both maps compare equal. Elements are compared as per normal Ruby semantics, by calling their :== methods (or performing a more efficient comparison for primitive types).

Maps with dissimilar key types or value types/typeclasses are never equal, even if value comparison (for example, between integers and floats) would have otherwise indicated that every element has equal value.



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/google/protobuf/ffi/map.rb', line 214

def ==(other)
  if other.is_a? Hash
    other = self.class.send(:private_constructor, key_type, value_type, descriptor, initial_values: other)
  elsif !other.is_a? Google::Protobuf::Map
    return false
  end

  return true if object_id == other.object_id
  return false if key_type != other.send(:key_type) or value_type != other.send(:value_type) or descriptor != other.send(:descriptor) or length != other.length
  other_map_ptr = other.send(:map_ptr)
  each_msg_val do |key_message_value, value_message_value|
    other_value = Google::Protobuf::FFI::MessageValue.new
    return false unless Google::Protobuf::FFI.map_get(other_map_ptr, key_message_value, other_value)
    return false unless Google::Protobuf::FFI.message_value_equal(value_message_value, other_value, value_type, descriptor)
  end
  true
end

#[](key) ⇒ Object

call-seq:

  Map.[](key) => value

Accesses the element at the given key. Throws an exception if the key type is
incorrect. Returns nil when the key is not present in the map.


104
105
106
107
108
109
110
# File 'lib/google/protobuf/ffi/map.rb', line 104

def [](key)
  value = Google::Protobuf::FFI::MessageValue.new
  key_message_value = convert_ruby_to_upb(key, arena, key_type, nil)
  if Google::Protobuf::FFI.map_get(@map_ptr, key_message_value, value)
     convert_upb_to_ruby(value, value_type, descriptor, arena)
  end
end

#[]=(key, value) ⇒ Object

call-seq:

Map.[]=(key, value) => value

Inserts or overwrites the value at the given key with the given new value. Throws an exception if the key type is incorrect. Returns the new value that was just inserted.



119
120
121
122
123
124
125
# File 'lib/google/protobuf/ffi/map.rb', line 119

def []=(key, value)
  raise FrozenError.new "can't modify frozen #{self.class}" if frozen?
  key_message_value = convert_ruby_to_upb(key, arena, key_type, nil)
  value_message_value = convert_ruby_to_upb(value, arena, value_type, descriptor)
  Google::Protobuf::FFI.map_set(@map_ptr, key_message_value, value_message_value, arena)
  value
end

#clearObject



149
150
151
152
153
# File 'lib/google/protobuf/ffi/map.rb', line 149

def clear
  raise FrozenError.new "can't modify frozen #{self.class}" if frozen?
  Google::Protobuf::FFI.map_clear(@map_ptr)
  nil
end

#delete(key) ⇒ Object

call-seq:

Map.delete(key) => old_value

Deletes the value at the given key, if any, returning either the old value or nil if none was present. Throws an exception if the key is of the wrong type.



138
139
140
141
142
143
144
145
146
147
# File 'lib/google/protobuf/ffi/map.rb', line 138

def delete(key)
  raise FrozenError.new "can't modify frozen #{self.class}" if frozen?
  value = Google::Protobuf::FFI::MessageValue.new
  key_message_value = convert_ruby_to_upb(key, arena, key_type, nil)
  if Google::Protobuf::FFI.map_delete(@map_ptr, key_message_value, value)
    convert_upb_to_ruby(value, value_type, descriptor, arena)
  else
    nil
  end
end

#dupObject Also known as: clone

call-seq:

Map.dup => new_map

Duplicates this map with a shallow copy. References to all non-primitive element objects (e.g., submessages) are shared.



197
198
199
# File 'lib/google/protobuf/ffi/map.rb', line 197

def dup
  internal_dup
end

#each(&block) ⇒ Object

call-seq:

Map.each(&block)

Invokes &block on each |key, value| pair in the map, in unspecified order. Note that Map also includes Enumerable; map thus acts like a normal Ruby sequence.



291
292
293
294
295
296
297
298
# File 'lib/google/protobuf/ffi/map.rb', line 291

def each &block
  each_msg_val do |key_message_value, value_message_value|
    key_value = convert_upb_to_ruby(key_message_value, key_type)
    value_value = convert_upb_to_ruby(value_message_value, value_type, descriptor, arena)
    yield key_value, value_value
  end
  nil
end

#freezeObject

Freezes the map object. We have to intercept this so we can freeze the underlying representation, not just the Ruby wrapper. Returns self.



177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/google/protobuf/ffi/map.rb', line 177

def freeze
  if method(:frozen?).super_method.call
    unless Google::Protobuf::FFI.map_frozen? @map_ptr
      raise RuntimeError.new "Underlying representation of map still mutable despite frozen wrapper"
    end
    return self
  end
  unless Google::Protobuf::FFI.map_frozen? @map_ptr
    mini_table = (value_type == :message) ? Google::Protobuf::FFI.get_mini_table(@descriptor) : nil
    Google::Protobuf::FFI.map_freeze(@map_ptr, mini_table)
  end
  super
end

#frozen?Boolean

Is this object frozen? Returns true if either this Ruby wrapper or the underlying representation are frozen. Freezes the wrapper if the underlying representation is already frozen but this wrapper isn’t.

Returns:

  • (Boolean)


165
166
167
168
169
170
171
172
# File 'lib/google/protobuf/ffi/map.rb', line 165

def frozen?
  unless Google::Protobuf::FFI.map_frozen? @map_ptr
    raise RuntimeError.new "Ruby frozen Map with mutable representation" if super
    return false
  end
  method(:freeze).super_method.call unless super
  true
end

#has_key?(key) ⇒ Boolean

Returns:

  • (Boolean)


127
128
129
130
# File 'lib/google/protobuf/ffi/map.rb', line 127

def has_key?(key)
  key_message_value = convert_ruby_to_upb(key, arena, key_type, nil)
  Google::Protobuf::FFI.map_get(@map_ptr, key_message_value, nil)
end

#hashObject



232
233
234
235
236
237
238
239
# File 'lib/google/protobuf/ffi/map.rb', line 232

def hash
  return_value = 0
  each_msg_val do |key_message_value, value_message_value|
    return_value = Google::Protobuf::FFI.message_value_hash(key_message_value, key_type, nil, return_value)
    return_value = Google::Protobuf::FFI.message_value_hash(value_message_value, value_type, descriptor, return_value)
  end
  return_value
end

#inspectObject



257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/google/protobuf/ffi/map.rb', line 257

def inspect
  key_value_pairs = []
  each_msg_val do |key_message_value, value_message_value|
    key_string = convert_upb_to_ruby(key_message_value, key_type).inspect
    if value_type == :message
      sub_msg_descriptor = Google::Protobuf::FFI.get_subtype_as_message(descriptor)
      value_string = sub_msg_descriptor.msgclass.send(:inspect_internal, value_message_value[:msg_val])
    else
      value_string = convert_upb_to_ruby(value_message_value, value_type, descriptor).inspect
    end
    key_value_pairs << "#{key_string}=>#{value_string}"
  end
  "{#{key_value_pairs.join(", ")}}"
end

#keysObject

call-seq:

Map.keys => [list_of_keys]

Returns the list of keys contained in the map, in unspecified order.



75
76
77
78
79
80
81
82
# File 'lib/google/protobuf/ffi/map.rb', line 75

def keys
  return_value = []
  internal_iterator do |iterator|
    key_message_value = Google::Protobuf::FFI.map_key(@map_ptr, iterator)
    return_value << convert_upb_to_ruby(key_message_value, key_type)
  end
  return_value
end

#lengthObject Also known as: size



155
156
157
# File 'lib/google/protobuf/ffi/map.rb', line 155

def length
  Google::Protobuf::FFI.map_size(@map_ptr)
end

#merge(other) ⇒ Object

call-seq:

Map.merge(other_map) => map

Copies key/value pairs from other_map into a copy of this map. If a key is set in other_map and this map, the value from other_map overwrites the value in the new copy of this map. Returns the new copy of this map with merged contents.



280
281
282
# File 'lib/google/protobuf/ffi/map.rb', line 280

def merge(other)
  internal_merge(other)
end

#to_hObject

call-seq:

Map.to_h => {}

Returns a Ruby Hash object containing all the values within the map



246
247
248
249
250
251
252
253
254
255
# File 'lib/google/protobuf/ffi/map.rb', line 246

def to_h
  return {} if map_ptr.nil? or map_ptr.null?
  return_value = {}
  each_msg_val do |key_message_value, value_message_value|
    hash_key = convert_upb_to_ruby(key_message_value, key_type)
    hash_value = scalar_create_hash(value_message_value, value_type, msg_or_enum_descriptor: descriptor)
    return_value[hash_key] = hash_value
  end
  return_value
end

#valuesObject

call-seq:

Map.values => [list_of_values]

Returns the list of values contained in the map, in unspecified order.



89
90
91
92
93
94
95
96
# File 'lib/google/protobuf/ffi/map.rb', line 89

def values
  return_value = []
  internal_iterator do |iterator|
    value_message_value = Google::Protobuf::FFI.map_value(@map_ptr, iterator)
    return_value << convert_upb_to_ruby(value_message_value, value_type, descriptor, arena)
  end
  return_value
end