Esempio n. 1
0
void native_slot_check_int_range_precision(const char* name, upb_fieldtype_t type, VALUE val) {
  if (!is_ruby_num(val)) {
    rb_raise(cTypeError, "Expected number type for integral field '%s' (given %s).",
             name, rb_class2name(CLASS_OF(val)));
  }

  // NUM2{INT,UINT,LL,ULL} macros do the appropriate range checks on upper
  // bound; we just need to do precision checks (i.e., disallow rounding) and
  // check for < 0 on unsigned types.
  if (TYPE(val) == T_FLOAT) {
    double dbl_val = NUM2DBL(val);
    if (floor(dbl_val) != dbl_val) {
      rb_raise(rb_eRangeError,
               "Non-integral floating point value assigned to integer field '%s' (given %s).",
               name, rb_class2name(CLASS_OF(val)));
    }
  }
  if (type == UPB_TYPE_UINT32 || type == UPB_TYPE_UINT64) {
    if (NUM2DBL(val) < 0) {
      rb_raise(rb_eRangeError,
               "Assigning negative value to unsigned integer field '%s' (given %s).",
               name, rb_class2name(CLASS_OF(val)));
    }
  }
}
Esempio n. 2
0
void native_slot_set_value_and_case(const char* name,
                                    upb_fieldtype_t type, VALUE type_class,
                                    void* memory, VALUE value,
                                    uint32_t* case_memory,
                                    uint32_t case_number) {
  // Note that in order to atomically change the value in memory and the case
  // value (w.r.t. Ruby VM calls), we must set the value at |memory| only after
  // all Ruby VM calls are complete. The case is then set at the bottom of this
  // function.
  switch (type) {
    case UPB_TYPE_FLOAT:
      if (!is_ruby_num(value)) {
        rb_raise(cTypeError, "Expected number type for float field '%s' (given %s).",
                 name, rb_class2name(CLASS_OF(value)));
      }
      DEREF(memory, float) = NUM2DBL(value);
      break;
    case UPB_TYPE_DOUBLE:
      if (!is_ruby_num(value)) {
        rb_raise(cTypeError, "Expected number type for double field '%s' (given %s).",
                 name, rb_class2name(CLASS_OF(value)));
      }
      DEREF(memory, double) = NUM2DBL(value);
      break;
    case UPB_TYPE_BOOL: {
      int8_t val = -1;
      if (value == Qtrue) {
        val = 1;
      } else if (value == Qfalse) {
        val = 0;
      } else {
        rb_raise(cTypeError, "Invalid argument for boolean field '%s' (given %s).",
                 name, rb_class2name(CLASS_OF(value)));
      }
      DEREF(memory, int8_t) = val;
      break;
    }
    case UPB_TYPE_STRING:
      if (CLASS_OF(value) == rb_cSymbol) {
        value = rb_funcall(value, rb_intern("to_s"), 0);
      } else if (CLASS_OF(value) != rb_cString) {
        rb_raise(cTypeError, "Invalid argument for string field '%s' (given %s).",
                 name, rb_class2name(CLASS_OF(value)));
      }

      DEREF(memory, VALUE) = native_slot_encode_and_freeze_string(type, value);
      break;

    case UPB_TYPE_BYTES: {
      if (CLASS_OF(value) != rb_cString) {
        rb_raise(cTypeError, "Invalid argument for bytes field '%s' (given %s).",
                 name, rb_class2name(CLASS_OF(value)));
      }

      DEREF(memory, VALUE) = native_slot_encode_and_freeze_string(type, value);
      break;
    }
    case UPB_TYPE_MESSAGE: {
      if (CLASS_OF(value) == CLASS_OF(Qnil)) {
        value = Qnil;
      } else if (CLASS_OF(value) != type_class) {
        // check for possible implicit conversions
        VALUE converted_value = NULL;
        char* field_type_name = rb_class2name(type_class);

        if (strcmp(field_type_name, "Google::Protobuf::Timestamp") == 0 &&
            rb_obj_is_kind_of(value, rb_cTime)) {
          // Time -> Google::Protobuf::Timestamp
          VALUE hash = rb_hash_new();
          rb_hash_aset(hash, rb_str_new2("seconds"), rb_funcall(value, rb_intern("to_i"), 0));
          rb_hash_aset(hash, rb_str_new2("nanos"), rb_funcall(value, rb_intern("nsec"), 0));
          VALUE args[1] = { hash };
          converted_value = rb_class_new_instance(1, args, type_class);
        } else if (strcmp(field_type_name, "Google::Protobuf::Duration") == 0 &&
                   rb_obj_is_kind_of(value, rb_cNumeric)) {
          // Numeric -> Google::Protobuf::Duration
          VALUE hash = rb_hash_new();
          rb_hash_aset(hash, rb_str_new2("seconds"), rb_funcall(value, rb_intern("to_i"), 0));
          VALUE n_value = rb_funcall(value, rb_intern("remainder"), 1, INT2NUM(1));
          n_value = rb_funcall(n_value, rb_intern("*"), 1, INT2NUM(1000000000));
          n_value = rb_funcall(n_value, rb_intern("round"), 0);
          rb_hash_aset(hash, rb_str_new2("nanos"), n_value);
          VALUE args[1] = { hash };
          converted_value = rb_class_new_instance(1, args, type_class);
        }

        // raise if no suitable conversaion could be found
        if (converted_value == NULL) {
          rb_raise(cTypeError,
                   "Invalid type %s to assign to submessage field '%s'.",
                  rb_class2name(CLASS_OF(value)), name);
        } else {
          value = converted_value;
        }
      }
      DEREF(memory, VALUE) = value;
      break;
    }
    case UPB_TYPE_ENUM: {
      int32_t int_val = 0;
      if (TYPE(value) == T_STRING) {
        value = rb_funcall(value, rb_intern("to_sym"), 0);
      } else if (!is_ruby_num(value) && TYPE(value) != T_SYMBOL) {
        rb_raise(cTypeError,
                 "Expected number or symbol type for enum field '%s'.", name);
      }
      if (TYPE(value) == T_SYMBOL) {
        // Ensure that the given symbol exists in the enum module.
        VALUE lookup = rb_funcall(type_class, rb_intern("resolve"), 1, value);
        if (lookup == Qnil) {
          rb_raise(rb_eRangeError, "Unknown symbol value for enum field '%s'.", name);
        } else {
          int_val = NUM2INT(lookup);
        }
      } else {
        native_slot_check_int_range_precision(name, UPB_TYPE_INT32, value);
        int_val = NUM2INT(value);
      }
      DEREF(memory, int32_t) = int_val;
      break;
    }
    case UPB_TYPE_INT32:
    case UPB_TYPE_INT64:
    case UPB_TYPE_UINT32:
    case UPB_TYPE_UINT64:
      native_slot_check_int_range_precision(name, type, value);
      switch (type) {
      case UPB_TYPE_INT32:
        DEREF(memory, int32_t) = NUM2INT(value);
        break;
      case UPB_TYPE_INT64:
        DEREF(memory, int64_t) = NUM2LL(value);
        break;
      case UPB_TYPE_UINT32:
        DEREF(memory, uint32_t) = NUM2UINT(value);
        break;
      case UPB_TYPE_UINT64:
        DEREF(memory, uint64_t) = NUM2ULL(value);
        break;
      default:
        break;
      }
      break;
    default:
      break;
  }

  if (case_memory != NULL) {
    *case_memory = case_number;
  }
}
Esempio n. 3
0
void native_slot_set_value_and_case(upb_fieldtype_t type, VALUE type_class,
                                    void* memory, VALUE value,
                                    uint32_t* case_memory,
                                    uint32_t case_number) {
    // Note that in order to atomically change the value in memory and the case
    // value (w.r.t. Ruby VM calls), we must set the value at |memory| only after
    // all Ruby VM calls are complete. The case is then set at the bottom of this
    // function.
    switch (type) {
    case UPB_TYPE_FLOAT:
        if (!is_ruby_num(value)) {
            rb_raise(rb_eTypeError, "Expected number type for float field.");
        }
        DEREF(memory, float) = NUM2DBL(value);
        break;
    case UPB_TYPE_DOUBLE:
        if (!is_ruby_num(value)) {
            rb_raise(rb_eTypeError, "Expected number type for double field.");
        }
        DEREF(memory, double) = NUM2DBL(value);
        break;
    case UPB_TYPE_BOOL: {
        int8_t val = -1;
        if (value == Qtrue) {
            val = 1;
        } else if (value == Qfalse) {
            val = 0;
        } else {
            rb_raise(rb_eTypeError, "Invalid argument for boolean field.");
        }
        DEREF(memory, int8_t) = val;
        break;
    }
    case UPB_TYPE_STRING:
    case UPB_TYPE_BYTES: {
        if (CLASS_OF(value) != rb_cString) {
            rb_raise(rb_eTypeError, "Invalid argument for string field.");
        }
        native_slot_validate_string_encoding(type, value);
        DEREF(memory, VALUE) = value;
        break;
    }
    case UPB_TYPE_MESSAGE: {
        if (CLASS_OF(value) == CLASS_OF(Qnil)) {
            value = Qnil;
        } else if (CLASS_OF(value) != type_class) {
            rb_raise(rb_eTypeError,
                     "Invalid type %s to assign to submessage field.",
                     rb_class2name(CLASS_OF(value)));
        }
        DEREF(memory, VALUE) = value;
        break;
    }
    case UPB_TYPE_ENUM: {
        if (!is_ruby_num(value) && TYPE(value) != T_SYMBOL) {
            rb_raise(rb_eTypeError,
                     "Expected number or symbol type for enum field.");
        }
        int32_t int_val = 0;
        if (TYPE(value) == T_SYMBOL) {
            // Ensure that the given symbol exists in the enum module.
            VALUE lookup = rb_funcall(type_class, rb_intern("resolve"), 1, value);
            if (lookup == Qnil) {
                rb_raise(rb_eRangeError, "Unknown symbol value for enum field.");
            } else {
                int_val = NUM2INT(lookup);
            }
        } else {
            native_slot_check_int_range_precision(UPB_TYPE_INT32, value);
            int_val = NUM2INT(value);
        }
        DEREF(memory, int32_t) = int_val;
        break;
    }
    case UPB_TYPE_INT32:
    case UPB_TYPE_INT64:
    case UPB_TYPE_UINT32:
    case UPB_TYPE_UINT64:
        native_slot_check_int_range_precision(type, value);
        switch (type) {
        case UPB_TYPE_INT32:
            DEREF(memory, int32_t) = NUM2INT(value);
            break;
        case UPB_TYPE_INT64:
            DEREF(memory, int64_t) = NUM2LL(value);
            break;
        case UPB_TYPE_UINT32:
            DEREF(memory, uint32_t) = NUM2UINT(value);
            break;
        case UPB_TYPE_UINT64:
            DEREF(memory, uint64_t) = NUM2ULL(value);
            break;
        default:
            break;
        }
        break;
    default:
        break;
    }

    if (case_memory != NULL) {
        *case_memory = case_number;
    }
}