| 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;
|
| +}
|
|
|