RubyMap.java
Go to the documentation of this file.
1 /*
2  * Protocol Buffers - Google's data interchange format
3  * Copyright 2014 Google Inc. All rights reserved.
4  * https://developers.google.com/protocol-buffers/
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  * * Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  * * Redistributions in binary form must reproduce the above
13  * copyright notice, this list of conditions and the following disclaimer
14  * in the documentation and/or other materials provided with the
15  * distribution.
16  * * Neither the name of Google Inc. nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 package com.google.protobuf.jruby;
34 
38 import org.jruby.*;
39 import org.jruby.anno.JRubyClass;
40 import org.jruby.anno.JRubyMethod;
41 import org.jruby.internal.runtime.methods.DynamicMethod;
42 import org.jruby.runtime.Block;
43 import org.jruby.runtime.ObjectAllocator;
44 import org.jruby.runtime.ThreadContext;
45 import org.jruby.runtime.builtin.IRubyObject;
46 import org.jruby.util.ByteList;
47 
48 import java.security.MessageDigest;
49 import java.security.NoSuchAlgorithmException;
50 import java.util.ArrayList;
51 import java.util.HashMap;
52 import java.util.List;
53 import java.util.Map;
54 
55 @JRubyClass(name = "Map", include = "Enumerable")
56 public class RubyMap extends RubyObject {
57  public static void createRubyMap(Ruby runtime) {
58  RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
59  RubyClass cMap = protobuf.defineClassUnder("Map", runtime.getObject(), new ObjectAllocator() {
60  @Override
61  public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) {
62  return new RubyMap(ruby, rubyClass);
63  }
64  });
65  cMap.includeModule(runtime.getEnumerable());
66  cMap.defineAnnotatedMethods(RubyMap.class);
67  }
68 
69  public RubyMap(Ruby ruby, RubyClass rubyClass) {
70  super(ruby, rubyClass);
71  }
72 
73  /*
74  * call-seq:
75  * Map.new(key_type, value_type, value_typeclass = nil, init_hashmap = {})
76  * => new map
77  *
78  * Allocates a new Map container. This constructor may be called with 2, 3, or 4
79  * arguments. The first two arguments are always present and are symbols (taking
80  * on the same values as field-type symbols in message descriptors) that
81  * indicate the type of the map key and value fields.
82  *
83  * The supported key types are: :int32, :int64, :uint32, :uint64, :bool,
84  * :string, :bytes.
85  *
86  * The supported value types are: :int32, :int64, :uint32, :uint64, :bool,
87  * :string, :bytes, :enum, :message.
88  *
89  * The third argument, value_typeclass, must be present if value_type is :enum
90  * or :message. As in RepeatedField#new, this argument must be a message class
91  * (for :message) or enum module (for :enum).
92  *
93  * The last argument, if present, provides initial content for map. Note that
94  * this may be an ordinary Ruby hashmap or another Map instance with identical
95  * key and value types. Also note that this argument may be present whether or
96  * not value_typeclass is present (and it is unambiguously separate from
97  * value_typeclass because value_typeclass's presence is strictly determined by
98  * value_type). The contents of this initial hashmap or Map instance are
99  * shallow-copied into the new Map: the original map is unmodified, but
100  * references to underlying objects will be shared if the value type is a
101  * message type.
102  */
103 
104  @JRubyMethod(required = 2, optional = 2)
105  public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
106  this.table = new HashMap<IRubyObject, IRubyObject>();
107  this.keyType = Utils.rubyToFieldType(args[0]);
108  this.valueType = Utils.rubyToFieldType(args[1]);
109 
110  switch(keyType) {
111  case INT32:
112  case INT64:
113  case UINT32:
114  case UINT64:
115  case BOOL:
116  case STRING:
117  case BYTES:
118  // These are OK.
119  break;
120  default:
121  throw context.runtime.newArgumentError("Invalid key type for map.");
122  }
123 
124  int initValueArg = 2;
125  if (needTypeclass(this.valueType) && args.length > 2) {
126  this.valueTypeClass = args[2];
127  Utils.validateTypeClass(context, this.valueType, this.valueTypeClass);
128  initValueArg = 3;
129  } else {
130  this.valueTypeClass = context.runtime.getNilClass();
131  }
132 
133  // Table value type is always UINT64: this ensures enough space to store the
134  // native_slot value.
135  if (args.length > initValueArg) {
136  mergeIntoSelf(context, args[initValueArg]);
137  }
138  return this;
139  }
140 
141  /*
142  * call-seq:
143  * Map.[]=(key, value) => value
144  *
145  * Inserts or overwrites the value at the given key with the given new value.
146  * Throws an exception if the key type is incorrect. Returns the new value that
147  * was just inserted.
148  */
149  @JRubyMethod(name = "[]=")
150  public IRubyObject indexSet(ThreadContext context, IRubyObject key, IRubyObject value) {
151  key = Utils.checkType(context, keyType, key, (RubyModule) valueTypeClass);
152  value = Utils.checkType(context, valueType, value, (RubyModule) valueTypeClass);
153  IRubyObject symbol;
154  if (valueType == Descriptors.FieldDescriptor.Type.ENUM &&
155  Utils.isRubyNum(value) &&
156  ! (symbol = RubyEnum.lookup(context, valueTypeClass, value)).isNil()) {
157  value = symbol;
158  }
159  this.table.put(key, value);
160  return value;
161  }
162 
163  /*
164  * call-seq:
165  * Map.[](key) => value
166  *
167  * Accesses the element at the given key. Throws an exception if the key type is
168  * incorrect. Returns nil when the key is not present in the map.
169  */
170  @JRubyMethod(name = "[]")
171  public IRubyObject index(ThreadContext context, IRubyObject key) {
172  if (table.containsKey(key))
173  return this.table.get(key);
174  return context.runtime.getNil();
175  }
176 
177  /*
178  * call-seq:
179  * Map.==(other) => boolean
180  *
181  * Compares this map to another. Maps are equal if they have identical key sets,
182  * and for each key, the values in both maps compare equal. Elements are
183  * compared as per normal Ruby semantics, by calling their :== methods (or
184  * performing a more efficient comparison for primitive types).
185  *
186  * Maps with dissimilar key types or value types/typeclasses are never equal,
187  * even if value comparison (for example, between integers and floats) would
188  * have otherwise indicated that every element has equal value.
189  */
190  @JRubyMethod(name = "==")
191  public IRubyObject eq(ThreadContext context, IRubyObject _other) {
192  if (_other instanceof RubyHash)
193  return toHash(context).op_equal(context, _other);
194  RubyMap other = (RubyMap) _other;
195  if (this == other) return context.runtime.getTrue();
196  if (!typeCompatible(other) || this.table.size() != other.table.size())
197  return context.runtime.getFalse();
198  for (IRubyObject key : table.keySet()) {
199  if (! other.table.containsKey(key))
200  return context.runtime.getFalse();
201  if (! other.table.get(key).equals(table.get(key)))
202  return context.runtime.getFalse();
203  }
204  return context.runtime.getTrue();
205  }
206 
207  /*
208  * call-seq:
209  * Map.inspect => string
210  *
211  * Returns a string representing this map's elements. It will be formatted as
212  * "{key => value, key => value, ...}", with each key and value string
213  * representation computed by its own #inspect method.
214  */
215  @JRubyMethod
216  public IRubyObject inspect() {
217  return toHash(getRuntime().getCurrentContext()).inspect();
218  }
219 
220  /*
221  * call-seq:
222  * Map.hash => hash_value
223  *
224  * Returns a hash value based on this map's contents.
225  */
226  @JRubyMethod
227  public IRubyObject hash(ThreadContext context) {
228  try {
229  MessageDigest digest = MessageDigest.getInstance("SHA-256");
230  for (IRubyObject key : table.keySet()) {
231  digest.update((byte) key.hashCode());
232  digest.update((byte) table.get(key).hashCode());
233  }
234  return context.runtime.newString(new ByteList(digest.digest()));
235  } catch (NoSuchAlgorithmException ignore) {
236  return context.runtime.newFixnum(System.identityHashCode(table));
237  }
238  }
239 
240  /*
241  * call-seq:
242  * Map.keys => [list_of_keys]
243  *
244  * Returns the list of keys contained in the map, in unspecified order.
245  */
246  @JRubyMethod
247  public IRubyObject keys(ThreadContext context) {
248  return RubyArray.newArray(context.runtime, table.keySet());
249  }
250 
251  /*
252  * call-seq:
253  * Map.values => [list_of_values]
254  *
255  * Returns the list of values contained in the map, in unspecified order.
256  */
257  @JRubyMethod
258  public IRubyObject values(ThreadContext context) {
259  return RubyArray.newArray(context.runtime, table.values());
260  }
261 
262  /*
263  * call-seq:
264  * Map.clear
265  *
266  * Removes all entries from the map.
267  */
268  @JRubyMethod
269  public IRubyObject clear(ThreadContext context) {
270  table.clear();
271  return context.runtime.getNil();
272  }
273 
274  /*
275  * call-seq:
276  * Map.each(&block)
277  *
278  * Invokes &block on each |key, value| pair in the map, in unspecified order.
279  * Note that Map also includes Enumerable; map thus acts like a normal Ruby
280  * sequence.
281  */
282  @JRubyMethod
283  public IRubyObject each(ThreadContext context, Block block) {
284  for (IRubyObject key : table.keySet()) {
285  block.yieldSpecific(context, key, table.get(key));
286  }
287  return context.runtime.getNil();
288  }
289 
290  /*
291  * call-seq:
292  * Map.delete(key) => old_value
293  *
294  * Deletes the value at the given key, if any, returning either the old value or
295  * nil if none was present. Throws an exception if the key is of the wrong type.
296  */
297  @JRubyMethod
298  public IRubyObject delete(ThreadContext context, IRubyObject key) {
299  return table.remove(key);
300  }
301 
302  /*
303  * call-seq:
304  * Map.has_key?(key) => bool
305  *
306  * Returns true if the given key is present in the map. Throws an exception if
307  * the key has the wrong type.
308  */
309  @JRubyMethod(name = "has_key?")
310  public IRubyObject hasKey(ThreadContext context, IRubyObject key) {
311  return this.table.containsKey(key) ? context.runtime.getTrue() : context.runtime.getFalse();
312  }
313 
314  /*
315  * call-seq:
316  * Map.length
317  *
318  * Returns the number of entries (key-value pairs) in the map.
319  */
320  @JRubyMethod
321  public IRubyObject length(ThreadContext context) {
322  return context.runtime.newFixnum(this.table.size());
323  }
324 
325  /*
326  * call-seq:
327  * Map.dup => new_map
328  *
329  * Duplicates this map with a shallow copy. References to all non-primitive
330  * element objects (e.g., submessages) are shared.
331  */
332  @JRubyMethod
333  public IRubyObject dup(ThreadContext context) {
334  RubyMap newMap = newThisType(context);
335  for (Map.Entry<IRubyObject, IRubyObject> entry : table.entrySet()) {
336  newMap.table.put(entry.getKey(), entry.getValue());
337  }
338  return newMap;
339  }
340 
341  @JRubyMethod(name = "to_h")
342  public RubyHash toHash(ThreadContext context) {
343  return RubyHash.newHash(context.runtime, table, context.runtime.getNil());
344  }
345 
346  // Used by Google::Protobuf.deep_copy but not exposed directly.
347  protected IRubyObject deepCopy(ThreadContext context) {
348  RubyMap newMap = newThisType(context);
349  switch (valueType) {
350  case MESSAGE:
351  for (IRubyObject key : table.keySet()) {
353  newMap.table.put(key.dup(), message.deepCopy(context));
354  }
355  break;
356  default:
357  for (IRubyObject key : table.keySet()) {
358  newMap.table.put(key.dup(), table.get(key).dup());
359  }
360  }
361  return newMap;
362  }
363 
364  protected List<DynamicMessage> build(ThreadContext context, RubyDescriptor descriptor) {
365  List<DynamicMessage> list = new ArrayList<DynamicMessage>();
366  RubyClass rubyClass = (RubyClass) descriptor.msgclass(context);
367  Descriptors.FieldDescriptor keyField = descriptor.lookup("key").getFieldDef();
368  Descriptors.FieldDescriptor valueField = descriptor.lookup("value").getFieldDef();
369  for (IRubyObject key : table.keySet()) {
370  RubyMessage mapMessage = (RubyMessage) rubyClass.newInstance(context, Block.NULL_BLOCK);
371  mapMessage.setField(context, keyField, key);
372  mapMessage.setField(context, valueField, table.get(key));
373  list.add(mapMessage.build(context));
374  }
375  return list;
376  }
377 
378  protected RubyMap mergeIntoSelf(final ThreadContext context, IRubyObject hashmap) {
379  if (hashmap instanceof RubyHash) {
380  ((RubyHash) hashmap).visitAll(new RubyHash.Visitor() {
381  @Override
382  public void visit(IRubyObject key, IRubyObject val) {
383  indexSet(context, key, val);
384  }
385  });
386  } else if (hashmap instanceof RubyMap) {
387  RubyMap other = (RubyMap) hashmap;
388  if (!typeCompatible(other)) {
389  throw context.runtime.newTypeError("Attempt to merge Map with mismatching types");
390  }
391  } else {
392  throw context.runtime.newTypeError("Unknown type merging into Map");
393  }
394  return this;
395  }
396 
397  protected boolean typeCompatible(RubyMap other) {
398  return this.keyType == other.keyType &&
399  this.valueType == other.valueType &&
400  this.valueTypeClass == other.valueTypeClass;
401  }
402 
403  private RubyMap newThisType(ThreadContext context) {
404  RubyMap newMap;
405  if (needTypeclass(valueType)) {
406  newMap = (RubyMap) metaClass.newInstance(context,
407  Utils.fieldTypeToRuby(context, keyType),
408  Utils.fieldTypeToRuby(context, valueType),
409  valueTypeClass, Block.NULL_BLOCK);
410  } else {
411  newMap = (RubyMap) metaClass.newInstance(context,
412  Utils.fieldTypeToRuby(context, keyType),
413  Utils.fieldTypeToRuby(context, valueType),
414  Block.NULL_BLOCK);
415  }
416  newMap.table = new HashMap<IRubyObject, IRubyObject>();
417  return newMap;
418  }
419 
421  switch(type) {
422  case MESSAGE:
423  case ENUM:
424  return true;
425  default:
426  return false;
427  }
428  }
429 
432  private IRubyObject valueTypeClass;
433  private Map<IRubyObject, IRubyObject> table;
434 }
com.google.protobuf.Descriptors
Definition: Descriptors.java:80
Map
struct Map Map
Definition: php/ext/google/protobuf/protobuf.h:648
table
upb_strtable table
Definition: php/ext/google/protobuf/protobuf.h:1065
name
GLuint const GLchar * name
Definition: glcorearb.h:3055
cMap
VALUE cMap
Definition: ruby/ext/google/protobuf_c/map.c:137
com.google.protobuf.jruby.RubyEnum.lookup
static IRubyObject lookup(ThreadContext context, IRubyObject recv, IRubyObject number)
Definition: RubyEnum.java:51
com.google.protobuf.jruby.RubyMap.mergeIntoSelf
RubyMap mergeIntoSelf(final ThreadContext context, IRubyObject hashmap)
Definition: RubyMap.java:378
com.google.protobuf.jruby.RubyMap.keys
IRubyObject keys(ThreadContext context)
Definition: RubyMap.java:247
com.google.protobuf.MapEntry
Definition: MapEntry.java:50
com.google.protobuf.jruby.Utils
Definition: Utils.java:49
com.google.protobuf.jruby.RubyMap.build
List< DynamicMessage > build(ThreadContext context, RubyDescriptor descriptor)
Definition: RubyMap.java:364
com.google.protobuf.jruby.Utils.isRubyNum
static boolean isRubyNum(Object value)
Definition: Utils.java:268
com.google.protobuf
Definition: ProtoCaliperBenchmark.java:2
descriptor
Descriptor * descriptor
Definition: php/ext/google/protobuf/protobuf.h:936
com.google.protobuf.jruby.RubyMap.valueType
Descriptors.FieldDescriptor.Type valueType
Definition: RubyMap.java:431
com.google.protobuf.Descriptors.FieldDescriptor.Type
Definition: Descriptors.java:1215
com.google.protobuf.jruby.RubyEnum
Definition: RubyEnum.java:42
com.google.protobuf.jruby.Utils.validateTypeClass
static void validateTypeClass(ThreadContext context, Descriptors.FieldDescriptor.Type type, IRubyObject value)
Definition: Utils.java:272
com.google.protobuf.jruby.RubyMap.dup
IRubyObject dup(ThreadContext context)
Definition: RubyMap.java:333
com.google.protobuf.jruby.RubyMap.table
Map< IRubyObject, IRubyObject > table
Definition: RubyMap.java:433
com.google.protobuf.jruby.RubyMap.valueTypeClass
IRubyObject valueTypeClass
Definition: RubyMap.java:432
com.google.protobuf.jruby.RubyMap.createRubyMap
static void createRubyMap(Ruby runtime)
Definition: RubyMap.java:57
com.google.protobuf.jruby.RubyMap.length
IRubyObject length(ThreadContext context)
Definition: RubyMap.java:321
com.google.protobuf.jruby.Utils.rubyToFieldType
static Descriptors.FieldDescriptor.Type rubyToFieldType(IRubyObject typeClass)
Definition: Utils.java:50
com.google.protobuf.jruby.RubyMap.newThisType
RubyMap newThisType(ThreadContext context)
Definition: RubyMap.java:403
key
const SETUP_TEARDOWN_TESTCONTEXT char * key
Definition: test_wss_transport.cpp:10
com.google.protobuf.jruby.RubyMap.inspect
IRubyObject inspect()
Definition: RubyMap.java:216
com.google.protobuf.jruby.RubyMap.RubyMap
RubyMap(Ruby ruby, RubyClass rubyClass)
Definition: RubyMap.java:69
java
type
GLenum type
Definition: glcorearb.h:2695
com.google.protobuf.jruby.RubyMap.typeCompatible
boolean typeCompatible(RubyMap other)
Definition: RubyMap.java:397
com.google.protobuf.DynamicMessage
Definition: DynamicMessage.java:51
com.google.protobuf.jruby.RubyMessage.setField
IRubyObject setField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor, IRubyObject value)
Definition: RubyMessage.java:677
com.google
com
com.google.protobuf.jruby.RubyMap.keyType
Descriptors.FieldDescriptor.Type keyType
Definition: RubyMap.java:430
com.google.protobuf.jruby.Utils.checkType
static IRubyObject checkType(ThreadContext context, Descriptors.FieldDescriptor.Type fieldType, IRubyObject value, RubyModule typeClass)
Definition: Utils.java:67
com.google.protobuf.jruby.RubyMap.needTypeclass
boolean needTypeclass(Descriptors.FieldDescriptor.Type type)
Definition: RubyMap.java:420
com.google.protobuf.jruby.Utils.fieldTypeToRuby
static IRubyObject fieldTypeToRuby(ThreadContext context, Descriptors.FieldDescriptor.Type type)
Definition: Utils.java:54
com.google.protobuf.jruby.RubyMessage
Definition: RubyMessage.java:49
com.google.protobuf.jruby.RubyMessage.build
DynamicMessage build(ThreadContext context)
Definition: RubyMessage.java:401
com.google.protobuf.jruby.RubyMap
Definition: RubyMap.java:56
visit
static void visit(const upb_refcounted *r, upb_refcounted_visit *v, void *closure)
Definition: ruby/ext/google/protobuf_c/upb.c:5811
val
GLuint GLfloat * val
Definition: glcorearb.h:3604
com.google.protobuf.jruby.RubyDescriptor
Definition: RubyDescriptor.java:50
value
GLsizei const GLfloat * value
Definition: glcorearb.h:3093
com.google.protobuf.jruby.RubyMap.clear
IRubyObject clear(ThreadContext context)
Definition: RubyMap.java:269
index
GLuint index
Definition: glcorearb.h:3055
com.google.protobuf.jruby.RubyMap.deepCopy
IRubyObject deepCopy(ThreadContext context)
Definition: RubyMap.java:347
com.google.protobuf.jruby.RubyMap.values
IRubyObject values(ThreadContext context)
Definition: RubyMap.java:258
message
GLenum GLuint GLenum GLsizei const GLchar * message
Definition: glcorearb.h:2695
com.google.protobuf.jruby.RubyMap.hash
IRubyObject hash(ThreadContext context)
Definition: RubyMap.java:227
com.google.protobuf.jruby.RubyMap.each
IRubyObject each(ThreadContext context, Block block)
Definition: RubyMap.java:283
benchmarks.python.py_benchmark.args
args
Definition: py_benchmark.py:24
com.google.protobuf.Descriptors.FieldDescriptor.Type.ENUM
ENUM
Definition: Descriptors.java:1229
com.google.protobuf.Descriptors.FieldDescriptor
Definition: Descriptors.java:949


libaditof
Author(s):
autogenerated on Wed May 21 2025 02:06:58