Beispiel #1
0
static VALUE table_key(Map* self, VALUE key,
                       char* buf,
                       const char** out_key,
                       size_t* out_length) {
  switch (self->key_type) {
    case UPB_TYPE_BYTES:
    case UPB_TYPE_STRING:
      // Strings: use string content directly.
      Check_Type(key, T_STRING);
      key = native_slot_encode_and_freeze_string(self->key_type, key);
      *out_key = RSTRING_PTR(key);
      *out_length = RSTRING_LEN(key);
      break;

    case UPB_TYPE_BOOL:
    case UPB_TYPE_INT32:
    case UPB_TYPE_INT64:
    case UPB_TYPE_UINT32:
    case UPB_TYPE_UINT64:
      native_slot_set(self->key_type, Qnil, buf, key);
      *out_key = buf;
      *out_length = native_slot_size(self->key_type);
      break;

    default:
      // Map constructor should not allow a Map with another key type to be
      // constructed.
      assert(false);
      break;
  }

  return key;
}
Beispiel #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;
  }
}
Beispiel #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(cTypeError, "Expected number type for float field.");
      }
      DEREF(memory, float) = NUM2DBL(value);
      break;
    case UPB_TYPE_DOUBLE:
      if (!is_ruby_num(value)) {
        rb_raise(cTypeError, "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(cTypeError, "Invalid argument for boolean field.");
      }
      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.");
      }

      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 string field.");
      }

      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) {
        rb_raise(cTypeError,
                 "Invalid type %s to assign to submessage field.",
                 rb_class2name(CLASS_OF(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.");
      }
      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;
  }
}