Index: third_party/protobuf/ruby/src/main/java/com/google/protobuf/jruby/Utils.java |
diff --git a/third_party/protobuf/ruby/src/main/java/com/google/protobuf/jruby/Utils.java b/third_party/protobuf/ruby/src/main/java/com/google/protobuf/jruby/Utils.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..596a0979920215b03ea7bcc6fd48d4b974b0f770 |
--- /dev/null |
+++ b/third_party/protobuf/ruby/src/main/java/com/google/protobuf/jruby/Utils.java |
@@ -0,0 +1,300 @@ |
+/* |
+ * Protocol Buffers - Google's data interchange format |
+ * Copyright 2014 Google Inc. All rights reserved. |
+ * https://developers.google.com/protocol-buffers/ |
+ * |
+ * Redistribution and use in source and binary forms, with or without |
+ * modification, are permitted provided that the following conditions are |
+ * met: |
+ * |
+ * * Redistributions of source code must retain the above copyright |
+ * notice, this list of conditions and the following disclaimer. |
+ * * Redistributions in binary form must reproduce the above |
+ * copyright notice, this list of conditions and the following disclaimer |
+ * in the documentation and/or other materials provided with the |
+ * distribution. |
+ * * Neither the name of Google Inc. nor the names of its |
+ * contributors may be used to endorse or promote products derived from |
+ * this software without specific prior written permission. |
+ * |
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ */ |
+ |
+package com.google.protobuf.jruby; |
+ |
+import com.google.protobuf.ByteString; |
+import com.google.protobuf.DescriptorProtos; |
+import com.google.protobuf.Descriptors; |
+import org.jcodings.Encoding; |
+import org.jcodings.specific.ASCIIEncoding; |
+import org.jcodings.specific.USASCIIEncoding; |
+import org.jcodings.specific.UTF8Encoding; |
+import org.jruby.*; |
+import org.jruby.runtime.Block; |
+import org.jruby.runtime.ThreadContext; |
+import org.jruby.runtime.builtin.IRubyObject; |
+ |
+import java.math.BigInteger; |
+ |
+public class Utils { |
+ public static Descriptors.FieldDescriptor.Type rubyToFieldType(IRubyObject typeClass) { |
+ return Descriptors.FieldDescriptor.Type.valueOf(typeClass.asJavaString().toUpperCase()); |
+ } |
+ |
+ public static IRubyObject fieldTypeToRuby(ThreadContext context, Descriptors.FieldDescriptor.Type type) { |
+ return fieldTypeToRuby(context, type.name()); |
+ } |
+ |
+ public static IRubyObject fieldTypeToRuby(ThreadContext context, DescriptorProtos.FieldDescriptorProto.Type type) { |
+ return fieldTypeToRuby(context, type.name()); |
+ } |
+ |
+ private static IRubyObject fieldTypeToRuby(ThreadContext context, String typeName) { |
+ |
+ return context.runtime.newSymbol(typeName.replace("TYPE_", "").toLowerCase()); |
+ } |
+ |
+ public static void checkType(ThreadContext context, Descriptors.FieldDescriptor.Type fieldType, |
+ IRubyObject value, RubyModule typeClass) { |
+ Ruby runtime = context.runtime; |
+ Object val; |
+ switch(fieldType) { |
+ case INT32: |
+ case INT64: |
+ case UINT32: |
+ case UINT64: |
+ if (!isRubyNum(value)) { |
+ throw runtime.newTypeError("Expected number type for integral field."); |
+ } |
+ switch(fieldType) { |
+ case INT32: |
+ RubyNumeric.num2int(value); |
+ break; |
+ case INT64: |
+ RubyNumeric.num2long(value); |
+ break; |
+ case UINT32: |
+ num2uint(value); |
+ break; |
+ default: |
+ num2ulong(context.runtime, value); |
+ break; |
+ } |
+ checkIntTypePrecision(context, fieldType, value); |
+ break; |
+ case FLOAT: |
+ if (!isRubyNum(value)) |
+ throw runtime.newTypeError("Expected number type for float field."); |
+ break; |
+ case DOUBLE: |
+ if (!isRubyNum(value)) |
+ throw runtime.newTypeError("Expected number type for double field."); |
+ break; |
+ case BOOL: |
+ if (!(value instanceof RubyBoolean)) |
+ throw runtime.newTypeError("Invalid argument for boolean field."); |
+ break; |
+ case BYTES: |
+ case STRING: |
+ validateStringEncoding(context.runtime, fieldType, value); |
+ break; |
+ case MESSAGE: |
+ if (value.getMetaClass() != typeClass) { |
+ throw runtime.newTypeError(value, typeClass); |
+ } |
+ break; |
+ case ENUM: |
+ if (value instanceof RubySymbol) { |
+ Descriptors.EnumDescriptor enumDescriptor = |
+ ((RubyEnumDescriptor) typeClass.getInstanceVariable(DESCRIPTOR_INSTANCE_VAR)).getDescriptor(); |
+ val = enumDescriptor.findValueByName(value.asJavaString()); |
+ if (val == null) |
+ throw runtime.newRangeError("Enum value " + value + " is not found."); |
+ } else if(!isRubyNum(value)) { |
+ throw runtime.newTypeError("Expected number or symbol type for enum field."); |
+ } |
+ break; |
+ default: |
+ break; |
+ } |
+ } |
+ |
+ public static IRubyObject wrapPrimaryValue(ThreadContext context, Descriptors.FieldDescriptor.Type fieldType, Object value) { |
+ Ruby runtime = context.runtime; |
+ switch (fieldType) { |
+ case INT32: |
+ return runtime.newFixnum((Integer) value); |
+ case INT64: |
+ return runtime.newFixnum((Long) value); |
+ case UINT32: |
+ return runtime.newFixnum(((Integer) value) & (-1l >>> 32)); |
+ case UINT64: |
+ long ret = (Long) value; |
+ return ret >= 0 ? runtime.newFixnum(ret) : |
+ RubyBignum.newBignum(runtime, UINT64_COMPLEMENTARY.add(new BigInteger(ret + ""))); |
+ case FLOAT: |
+ return runtime.newFloat((Float) value); |
+ case DOUBLE: |
+ return runtime.newFloat((Double) value); |
+ case BOOL: |
+ return (Boolean) value ? runtime.getTrue() : runtime.getFalse(); |
+ case BYTES: |
+ return runtime.newString(((ByteString) value).toStringUtf8()); |
+ case STRING: |
+ return runtime.newString(value.toString()); |
+ default: |
+ return runtime.getNil(); |
+ } |
+ } |
+ |
+ public static int num2uint(IRubyObject value) { |
+ long longVal = RubyNumeric.num2long(value); |
+ if (longVal > UINT_MAX) |
+ throw value.getRuntime().newRangeError("Integer " + longVal + " too big to convert to 'unsigned int'"); |
+ long num = longVal; |
+ if (num > Integer.MAX_VALUE || num < Integer.MIN_VALUE) |
+ // encode to UINT32 |
+ num = (-longVal ^ (-1l >>> 32) ) + 1; |
+ RubyNumeric.checkInt(value, num); |
+ return (int) num; |
+ } |
+ |
+ public static long num2ulong(Ruby runtime, IRubyObject value) { |
+ if (value instanceof RubyFloat) { |
+ RubyBignum bignum = RubyBignum.newBignum(runtime, ((RubyFloat) value).getDoubleValue()); |
+ return RubyBignum.big2ulong(bignum); |
+ } else if (value instanceof RubyBignum) { |
+ return RubyBignum.big2ulong((RubyBignum) value); |
+ } else { |
+ return RubyNumeric.num2long(value); |
+ } |
+ } |
+ |
+ public static void validateStringEncoding(Ruby runtime, Descriptors.FieldDescriptor.Type type, IRubyObject value) { |
+ if (!(value instanceof RubyString)) |
+ throw runtime.newTypeError("Invalid argument for string field."); |
+ Encoding encoding = ((RubyString) value).getEncoding(); |
+ switch(type) { |
+ case BYTES: |
+ if (encoding != ASCIIEncoding.INSTANCE) |
+ throw runtime.newTypeError("Encoding for bytes fields" + |
+ " must be \"ASCII-8BIT\", but was " + encoding); |
+ break; |
+ case STRING: |
+ if (encoding != UTF8Encoding.INSTANCE |
+ && encoding != USASCIIEncoding.INSTANCE) |
+ throw runtime.newTypeError("Encoding for string fields" + |
+ " must be \"UTF-8\" or \"ASCII\", but was " + encoding); |
+ break; |
+ default: |
+ break; |
+ } |
+ } |
+ |
+ public static void checkNameAvailability(ThreadContext context, String name) { |
+ if (context.runtime.getObject().getConstantAt(name) != null) |
+ throw context.runtime.newNameError(name + " is already defined", name); |
+ } |
+ |
+ /** |
+ * Replace invalid "." in descriptor with __DOT__ |
+ * @param name |
+ * @return |
+ */ |
+ public static String escapeIdentifier(String name) { |
+ return name.replace(".", BADNAME_REPLACEMENT); |
+ } |
+ |
+ /** |
+ * Replace __DOT__ in descriptor name with "." |
+ * @param name |
+ * @return |
+ */ |
+ public static String unescapeIdentifier(String name) { |
+ return name.replace(BADNAME_REPLACEMENT, "."); |
+ } |
+ |
+ public static boolean isMapEntry(Descriptors.FieldDescriptor fieldDescriptor) { |
+ return fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.MESSAGE && |
+ fieldDescriptor.isRepeated() && |
+ fieldDescriptor.getMessageType().getOptions().getMapEntry(); |
+ } |
+ |
+ public static RubyFieldDescriptor msgdefCreateField(ThreadContext context, String label, IRubyObject name, |
+ IRubyObject type, IRubyObject number, IRubyObject typeClass, RubyClass cFieldDescriptor) { |
+ Ruby runtime = context.runtime; |
+ RubyFieldDescriptor fieldDef = (RubyFieldDescriptor) cFieldDescriptor.newInstance(context, Block.NULL_BLOCK); |
+ fieldDef.setLabel(context, runtime.newString(label)); |
+ fieldDef.setName(context, name); |
+ fieldDef.setType(context, type); |
+ fieldDef.setNumber(context, number); |
+ |
+ if (!typeClass.isNil()) { |
+ if (!(typeClass instanceof RubyString)) { |
+ throw runtime.newArgumentError("expected string for type class"); |
+ } |
+ fieldDef.setSubmsgName(context, typeClass); |
+ } |
+ return fieldDef; |
+ } |
+ |
+ protected static void checkIntTypePrecision(ThreadContext context, Descriptors.FieldDescriptor.Type type, IRubyObject value) { |
+ if (value instanceof RubyFloat) { |
+ double doubleVal = RubyNumeric.num2dbl(value); |
+ if (Math.floor(doubleVal) != doubleVal) { |
+ throw context.runtime.newRangeError("Non-integral floating point value assigned to integer field."); |
+ } |
+ } |
+ if (type == Descriptors.FieldDescriptor.Type.UINT32 || type == Descriptors.FieldDescriptor.Type.UINT64) { |
+ if (RubyNumeric.num2dbl(value) < 0) { |
+ throw context.runtime.newRangeError("Assigning negative value to unsigned integer field."); |
+ } |
+ } |
+ } |
+ |
+ protected static boolean isRubyNum(Object value) { |
+ return value instanceof RubyFixnum || value instanceof RubyFloat || value instanceof RubyBignum; |
+ } |
+ |
+ protected static void validateTypeClass(ThreadContext context, Descriptors.FieldDescriptor.Type type, IRubyObject value) { |
+ Ruby runtime = context.runtime; |
+ if (!(value instanceof RubyModule)) { |
+ throw runtime.newArgumentError("TypeClass has incorrect type"); |
+ } |
+ RubyModule klass = (RubyModule) value; |
+ IRubyObject descriptor = klass.getInstanceVariable(DESCRIPTOR_INSTANCE_VAR); |
+ if (descriptor.isNil()) { |
+ throw runtime.newArgumentError("Type class has no descriptor. Please pass a " + |
+ "class or enum as returned by the DescriptorPool."); |
+ } |
+ if (type == Descriptors.FieldDescriptor.Type.MESSAGE) { |
+ if (! (descriptor instanceof RubyDescriptor)) { |
+ throw runtime.newArgumentError("Descriptor has an incorrect type"); |
+ } |
+ } else if (type == Descriptors.FieldDescriptor.Type.ENUM) { |
+ if (! (descriptor instanceof RubyEnumDescriptor)) { |
+ throw runtime.newArgumentError("Descriptor has an incorrect type"); |
+ } |
+ } |
+ } |
+ |
+ public static String BADNAME_REPLACEMENT = "__DOT__"; |
+ |
+ public static String DESCRIPTOR_INSTANCE_VAR = "@descriptor"; |
+ |
+ public static String EQUAL_SIGN = "="; |
+ |
+ private static BigInteger UINT64_COMPLEMENTARY = new BigInteger("18446744073709551616"); //Math.pow(2, 64) |
+ |
+ private static long UINT_MAX = 0xffffffffl; |
+} |