Add dependencies locally
This commit is contained in:
361
deps/protobuf/ruby/ext/google/protobuf_c/convert.c
vendored
Normal file
361
deps/protobuf/ruby/ext/google/protobuf_c/convert.c
vendored
Normal file
@ -0,0 +1,361 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 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.
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Ruby <-> upb data conversion functions.
|
||||
//
|
||||
// This file Also contains a few other assorted algorithms on upb_MessageValue.
|
||||
//
|
||||
// None of the algorithms in this file require any access to the internal
|
||||
// representation of Ruby or upb objects.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include "convert.h"
|
||||
|
||||
#include "message.h"
|
||||
#include "protobuf.h"
|
||||
|
||||
static upb_StringView Convert_StringData(VALUE str, upb_Arena* arena) {
|
||||
upb_StringView ret;
|
||||
if (arena) {
|
||||
char* ptr = upb_Arena_Malloc(arena, RSTRING_LEN(str));
|
||||
memcpy(ptr, RSTRING_PTR(str), RSTRING_LEN(str));
|
||||
ret.data = ptr;
|
||||
} else {
|
||||
// Data is only needed temporarily (within map lookup).
|
||||
ret.data = RSTRING_PTR(str);
|
||||
}
|
||||
ret.size = RSTRING_LEN(str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool is_ruby_num(VALUE value) {
|
||||
return (TYPE(value) == T_FLOAT || TYPE(value) == T_FIXNUM ||
|
||||
TYPE(value) == T_BIGNUM);
|
||||
}
|
||||
|
||||
static void Convert_CheckInt(const char* name, upb_CType 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 == kUpb_CType_UInt32 || type == kUpb_CType_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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t Convert_ToEnum(VALUE value, const char* name,
|
||||
const upb_EnumDef* e) {
|
||||
int32_t val;
|
||||
|
||||
switch (TYPE(value)) {
|
||||
case T_FLOAT:
|
||||
case T_FIXNUM:
|
||||
case T_BIGNUM:
|
||||
Convert_CheckInt(name, kUpb_CType_Int32, value);
|
||||
val = NUM2INT(value);
|
||||
break;
|
||||
case T_STRING: {
|
||||
const upb_EnumValueDef* ev = upb_EnumDef_FindValueByNameWithSize(
|
||||
e, RSTRING_PTR(value), RSTRING_LEN(value));
|
||||
if (!ev) goto unknownval;
|
||||
val = upb_EnumValueDef_Number(ev);
|
||||
break;
|
||||
}
|
||||
case T_SYMBOL: {
|
||||
const upb_EnumValueDef* ev =
|
||||
upb_EnumDef_FindValueByName(e, rb_id2name(SYM2ID(value)));
|
||||
if (!ev)
|
||||
goto unknownval;
|
||||
val = upb_EnumValueDef_Number(ev);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
rb_raise(cTypeError,
|
||||
"Expected number or symbol type for enum field '%s'.", name);
|
||||
}
|
||||
|
||||
return val;
|
||||
|
||||
unknownval:
|
||||
rb_raise(rb_eRangeError, "Unknown symbol value for enum field '%s'.", name);
|
||||
}
|
||||
|
||||
upb_MessageValue Convert_RubyToUpb(VALUE value, const char* name,
|
||||
TypeInfo type_info, upb_Arena* arena) {
|
||||
upb_MessageValue ret;
|
||||
|
||||
switch (type_info.type) {
|
||||
case kUpb_CType_Float:
|
||||
if (!is_ruby_num(value)) {
|
||||
rb_raise(cTypeError,
|
||||
"Expected number type for float field '%s' (given %s).", name,
|
||||
rb_class2name(CLASS_OF(value)));
|
||||
}
|
||||
ret.float_val = NUM2DBL(value);
|
||||
break;
|
||||
case kUpb_CType_Double:
|
||||
if (!is_ruby_num(value)) {
|
||||
rb_raise(cTypeError,
|
||||
"Expected number type for double field '%s' (given %s).", name,
|
||||
rb_class2name(CLASS_OF(value)));
|
||||
}
|
||||
ret.double_val = NUM2DBL(value);
|
||||
break;
|
||||
case kUpb_CType_Bool: {
|
||||
if (value == Qtrue) {
|
||||
ret.bool_val = 1;
|
||||
} else if (value == Qfalse) {
|
||||
ret.bool_val = 0;
|
||||
} else {
|
||||
rb_raise(cTypeError,
|
||||
"Invalid argument for boolean field '%s' (given %s).", name,
|
||||
rb_class2name(CLASS_OF(value)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kUpb_CType_String: {
|
||||
VALUE utf8 = rb_enc_from_encoding(rb_utf8_encoding());
|
||||
if (rb_obj_class(value) == rb_cSymbol) {
|
||||
value = rb_funcall(value, rb_intern("to_s"), 0);
|
||||
} else if (rb_obj_class(value) != rb_cString) {
|
||||
rb_raise(cTypeError,
|
||||
"Invalid argument for string field '%s' (given %s).", name,
|
||||
rb_class2name(CLASS_OF(value)));
|
||||
}
|
||||
|
||||
if (rb_obj_encoding(value) != utf8) {
|
||||
// Note: this will not duplicate underlying string data unless
|
||||
// necessary.
|
||||
value = rb_str_encode(value, utf8, 0, Qnil);
|
||||
|
||||
if (rb_enc_str_coderange(value) == ENC_CODERANGE_BROKEN) {
|
||||
rb_raise(rb_eEncodingError, "String is invalid UTF-8");
|
||||
}
|
||||
}
|
||||
|
||||
ret.str_val = Convert_StringData(value, arena);
|
||||
break;
|
||||
}
|
||||
case kUpb_CType_Bytes: {
|
||||
VALUE bytes = rb_enc_from_encoding(rb_ascii8bit_encoding());
|
||||
if (rb_obj_class(value) != rb_cString) {
|
||||
rb_raise(cTypeError,
|
||||
"Invalid argument for bytes field '%s' (given %s).", name,
|
||||
rb_class2name(CLASS_OF(value)));
|
||||
}
|
||||
|
||||
if (rb_obj_encoding(value) != bytes) {
|
||||
// Note: this will not duplicate underlying string data unless
|
||||
// necessary.
|
||||
// TODO(haberman): is this really necessary to get raw bytes?
|
||||
value = rb_str_encode(value, bytes, 0, Qnil);
|
||||
}
|
||||
|
||||
ret.str_val = Convert_StringData(value, arena);
|
||||
break;
|
||||
}
|
||||
case kUpb_CType_Message:
|
||||
ret.msg_val =
|
||||
Message_GetUpbMessage(value, type_info.def.msgdef, name, arena);
|
||||
break;
|
||||
case kUpb_CType_Enum:
|
||||
ret.int32_val = Convert_ToEnum(value, name, type_info.def.enumdef);
|
||||
break;
|
||||
case kUpb_CType_Int32:
|
||||
case kUpb_CType_Int64:
|
||||
case kUpb_CType_UInt32:
|
||||
case kUpb_CType_UInt64:
|
||||
Convert_CheckInt(name, type_info.type, value);
|
||||
switch (type_info.type) {
|
||||
case kUpb_CType_Int32:
|
||||
ret.int32_val = NUM2INT(value);
|
||||
break;
|
||||
case kUpb_CType_Int64:
|
||||
ret.int64_val = NUM2LL(value);
|
||||
break;
|
||||
case kUpb_CType_UInt32:
|
||||
ret.uint32_val = NUM2UINT(value);
|
||||
break;
|
||||
case kUpb_CType_UInt64:
|
||||
ret.uint64_val = NUM2ULL(value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
VALUE Convert_UpbToRuby(upb_MessageValue upb_val, TypeInfo type_info,
|
||||
VALUE arena) {
|
||||
switch (type_info.type) {
|
||||
case kUpb_CType_Float:
|
||||
return DBL2NUM(upb_val.float_val);
|
||||
case kUpb_CType_Double:
|
||||
return DBL2NUM(upb_val.double_val);
|
||||
case kUpb_CType_Bool:
|
||||
return upb_val.bool_val ? Qtrue : Qfalse;
|
||||
case kUpb_CType_Int32:
|
||||
return INT2NUM(upb_val.int32_val);
|
||||
case kUpb_CType_Int64:
|
||||
return LL2NUM(upb_val.int64_val);
|
||||
case kUpb_CType_UInt32:
|
||||
return UINT2NUM(upb_val.uint32_val);
|
||||
case kUpb_CType_UInt64:
|
||||
return ULL2NUM(upb_val.int64_val);
|
||||
case kUpb_CType_Enum: {
|
||||
const upb_EnumValueDef *ev = upb_EnumDef_FindValueByNumber(
|
||||
type_info.def.enumdef, upb_val.int32_val);
|
||||
if (ev) {
|
||||
return ID2SYM(rb_intern(upb_EnumValueDef_Name(ev)));
|
||||
} else {
|
||||
return INT2NUM(upb_val.int32_val);
|
||||
}
|
||||
}
|
||||
case kUpb_CType_String: {
|
||||
VALUE str_rb = rb_str_new(upb_val.str_val.data, upb_val.str_val.size);
|
||||
rb_enc_associate(str_rb, rb_utf8_encoding());
|
||||
rb_obj_freeze(str_rb);
|
||||
return str_rb;
|
||||
}
|
||||
case kUpb_CType_Bytes: {
|
||||
VALUE str_rb = rb_str_new(upb_val.str_val.data, upb_val.str_val.size);
|
||||
rb_enc_associate(str_rb, rb_ascii8bit_encoding());
|
||||
rb_obj_freeze(str_rb);
|
||||
return str_rb;
|
||||
}
|
||||
case kUpb_CType_Message:
|
||||
return Message_GetRubyWrapper((upb_Message*)upb_val.msg_val,
|
||||
type_info.def.msgdef, arena);
|
||||
default:
|
||||
rb_raise(rb_eRuntimeError, "Convert_UpbToRuby(): Unexpected type %d",
|
||||
(int)type_info.type);
|
||||
}
|
||||
}
|
||||
|
||||
upb_MessageValue Msgval_DeepCopy(upb_MessageValue msgval, TypeInfo type_info,
|
||||
upb_Arena* arena) {
|
||||
upb_MessageValue new_msgval;
|
||||
|
||||
switch (type_info.type) {
|
||||
default:
|
||||
memcpy(&new_msgval, &msgval, sizeof(msgval));
|
||||
break;
|
||||
case kUpb_CType_String:
|
||||
case kUpb_CType_Bytes: {
|
||||
size_t n = msgval.str_val.size;
|
||||
char* mem = upb_Arena_Malloc(arena, n);
|
||||
new_msgval.str_val.data = mem;
|
||||
new_msgval.str_val.size = n;
|
||||
memcpy(mem, msgval.str_val.data, n);
|
||||
break;
|
||||
}
|
||||
case kUpb_CType_Message:
|
||||
new_msgval.msg_val =
|
||||
Message_deep_copy(msgval.msg_val, type_info.def.msgdef, arena);
|
||||
break;
|
||||
}
|
||||
|
||||
return new_msgval;
|
||||
}
|
||||
|
||||
bool Msgval_IsEqual(upb_MessageValue val1, upb_MessageValue val2,
|
||||
TypeInfo type_info) {
|
||||
switch (type_info.type) {
|
||||
case kUpb_CType_Bool:
|
||||
return memcmp(&val1, &val2, 1) == 0;
|
||||
case kUpb_CType_Float:
|
||||
case kUpb_CType_Int32:
|
||||
case kUpb_CType_UInt32:
|
||||
case kUpb_CType_Enum:
|
||||
return memcmp(&val1, &val2, 4) == 0;
|
||||
case kUpb_CType_Double:
|
||||
case kUpb_CType_Int64:
|
||||
case kUpb_CType_UInt64:
|
||||
return memcmp(&val1, &val2, 8) == 0;
|
||||
case kUpb_CType_String:
|
||||
case kUpb_CType_Bytes:
|
||||
return val1.str_val.size == val2.str_val.size &&
|
||||
memcmp(val1.str_val.data, val2.str_val.data, val1.str_val.size) ==
|
||||
0;
|
||||
case kUpb_CType_Message:
|
||||
return Message_Equal(val1.msg_val, val2.msg_val, type_info.def.msgdef);
|
||||
default:
|
||||
rb_raise(rb_eRuntimeError, "Internal error, unexpected type");
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t Msgval_GetHash(upb_MessageValue val, TypeInfo type_info,
|
||||
uint64_t seed) {
|
||||
switch (type_info.type) {
|
||||
case kUpb_CType_Bool:
|
||||
return _upb_Hash(&val, 1, seed);
|
||||
case kUpb_CType_Float:
|
||||
case kUpb_CType_Int32:
|
||||
case kUpb_CType_UInt32:
|
||||
case kUpb_CType_Enum:
|
||||
return _upb_Hash(&val, 4, seed);
|
||||
case kUpb_CType_Double:
|
||||
case kUpb_CType_Int64:
|
||||
case kUpb_CType_UInt64:
|
||||
return _upb_Hash(&val, 8, seed);
|
||||
case kUpb_CType_String:
|
||||
case kUpb_CType_Bytes:
|
||||
return _upb_Hash(val.str_val.data, val.str_val.size, seed);
|
||||
case kUpb_CType_Message:
|
||||
return Message_Hash(val.msg_val, type_info.def.msgdef, seed);
|
||||
default:
|
||||
rb_raise(rb_eRuntimeError, "Internal error, unexpected type");
|
||||
}
|
||||
}
|
75
deps/protobuf/ruby/ext/google/protobuf_c/convert.h
vendored
Normal file
75
deps/protobuf/ruby/ext/google/protobuf_c/convert.h
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 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.
|
||||
|
||||
#ifndef RUBY_PROTOBUF_CONVERT_H_
|
||||
#define RUBY_PROTOBUF_CONVERT_H_
|
||||
|
||||
#include <ruby/ruby.h>
|
||||
|
||||
#include "protobuf.h"
|
||||
#include "ruby-upb.h"
|
||||
|
||||
// Converts |ruby_val| to a upb_MessageValue according to |type_info|.
|
||||
//
|
||||
// The |arena| parameter indicates the lifetime of the container where this
|
||||
// value will be assigned. It is used as follows:
|
||||
// - If type is string or bytes, the string data will be copied into |arena|.
|
||||
// - If type is message, and we need to auto-construct a message due to implicit
|
||||
// conversions (eg. Time -> Google::Protobuf::Timestamp), the new message
|
||||
// will be created in |arena|.
|
||||
// - If type is message and the Ruby value is a message instance, we will fuse
|
||||
// the message's arena into |arena|, to ensure that this message outlives the
|
||||
// container.
|
||||
upb_MessageValue Convert_RubyToUpb(VALUE ruby_val, const char *name,
|
||||
TypeInfo type_info, upb_Arena *arena);
|
||||
|
||||
// Converts |upb_val| to a Ruby VALUE according to |type_info|. This may involve
|
||||
// creating a Ruby wrapper object.
|
||||
//
|
||||
// The |arena| parameter indicates the arena that owns the lifetime of
|
||||
// |upb_val|. Any Ruby wrapper object that is created will reference |arena|
|
||||
// and ensure it outlives the wrapper.
|
||||
VALUE Convert_UpbToRuby(upb_MessageValue upb_val, TypeInfo type_info,
|
||||
VALUE arena);
|
||||
|
||||
// Creates a deep copy of |msgval| in |arena|.
|
||||
upb_MessageValue Msgval_DeepCopy(upb_MessageValue msgval, TypeInfo type_info,
|
||||
upb_Arena *arena);
|
||||
|
||||
// Returns true if |val1| and |val2| are equal. Their type is given by
|
||||
// |type_info|.
|
||||
bool Msgval_IsEqual(upb_MessageValue val1, upb_MessageValue val2,
|
||||
TypeInfo type_info);
|
||||
|
||||
// Returns a hash value for the given upb_MessageValue.
|
||||
uint64_t Msgval_GetHash(upb_MessageValue val, TypeInfo type_info,
|
||||
uint64_t seed);
|
||||
|
||||
#endif // RUBY_PROTOBUF_CONVERT_H_
|
1280
deps/protobuf/ruby/ext/google/protobuf_c/defs.c
vendored
Normal file
1280
deps/protobuf/ruby/ext/google/protobuf_c/defs.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
107
deps/protobuf/ruby/ext/google/protobuf_c/defs.h
vendored
Normal file
107
deps/protobuf/ruby/ext/google/protobuf_c/defs.h
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 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.
|
||||
|
||||
#ifndef RUBY_PROTOBUF_DEFS_H_
|
||||
#define RUBY_PROTOBUF_DEFS_H_
|
||||
|
||||
#include <ruby/ruby.h>
|
||||
|
||||
#include "protobuf.h"
|
||||
#include "ruby-upb.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// TypeInfo
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// This bundles a upb_CType and msgdef/enumdef when appropriate. This is
|
||||
// convenient for functions that need type information but cannot necessarily
|
||||
// assume a upb_FieldDef will be available.
|
||||
//
|
||||
// For example, Google::Protobuf::Map and Google::Protobuf::RepeatedField can
|
||||
// be constructed with type information alone:
|
||||
//
|
||||
// # RepeatedField will internally store the type information in a TypeInfo.
|
||||
// Google::Protobuf::RepeatedField.new(:message, FooMessage)
|
||||
|
||||
typedef struct {
|
||||
upb_CType type;
|
||||
union {
|
||||
const upb_MessageDef* msgdef; // When type == kUpb_CType_Message
|
||||
const upb_EnumDef* enumdef; // When type == kUpb_CType_Enum
|
||||
} def;
|
||||
} TypeInfo;
|
||||
|
||||
static inline TypeInfo TypeInfo_get(const upb_FieldDef* f) {
|
||||
TypeInfo ret = {upb_FieldDef_CType(f), {NULL}};
|
||||
switch (ret.type) {
|
||||
case kUpb_CType_Message:
|
||||
ret.def.msgdef = upb_FieldDef_MessageSubDef(f);
|
||||
break;
|
||||
case kUpb_CType_Enum:
|
||||
ret.def.enumdef = upb_FieldDef_EnumSubDef(f);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
TypeInfo TypeInfo_FromClass(int argc, VALUE* argv, int skip_arg,
|
||||
VALUE* type_class, VALUE* init_arg);
|
||||
|
||||
static inline TypeInfo TypeInfo_from_type(upb_CType type) {
|
||||
TypeInfo ret = {type};
|
||||
assert(type != kUpb_CType_Message && type != kUpb_CType_Enum);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Other utilities
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
VALUE Descriptor_DefToClass(const upb_MessageDef* m);
|
||||
|
||||
// Returns the underlying msgdef, enumdef, or symtab (respectively) for the
|
||||
// given Descriptor, EnumDescriptor, or DescriptorPool Ruby object.
|
||||
const upb_EnumDef* EnumDescriptor_GetEnumDef(VALUE enum_desc_rb);
|
||||
const upb_DefPool* DescriptorPool_GetSymtab(VALUE desc_pool_rb);
|
||||
const upb_MessageDef* Descriptor_GetMsgDef(VALUE desc_rb);
|
||||
|
||||
// Returns a upb field type for the given Ruby symbol
|
||||
// (eg. :float => kUpb_CType_Float).
|
||||
upb_CType ruby_to_fieldtype(VALUE type);
|
||||
|
||||
// The singleton generated pool (a DescriptorPool object).
|
||||
extern VALUE generated_pool;
|
||||
|
||||
// Call at startup to register all types in this module.
|
||||
void Defs_register(VALUE module);
|
||||
|
||||
#endif // RUBY_PROTOBUF_DEFS_H_
|
28
deps/protobuf/ruby/ext/google/protobuf_c/extconf.rb
vendored
Normal file
28
deps/protobuf/ruby/ext/google/protobuf_c/extconf.rb
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/ruby
|
||||
|
||||
require 'mkmf'
|
||||
|
||||
ext_name = "google/protobuf_c"
|
||||
|
||||
dir_config(ext_name)
|
||||
|
||||
if RUBY_PLATFORM =~ /darwin/ || RUBY_PLATFORM =~ /linux/
|
||||
$CFLAGS += " -std=gnu99 -O3 -DNDEBUG -fvisibility=hidden -Wall -Wsign-compare -Wno-declaration-after-statement"
|
||||
else
|
||||
$CFLAGS += " -std=gnu99 -O3 -DNDEBUG"
|
||||
end
|
||||
|
||||
|
||||
if RUBY_PLATFORM =~ /linux/
|
||||
# Instruct the linker to point memcpy calls at our __wrap_memcpy wrapper.
|
||||
$LDFLAGS += " -Wl,-wrap,memcpy"
|
||||
end
|
||||
|
||||
$VPATH << "$(srcdir)/third_party/utf8_range"
|
||||
$INCFLAGS << "$(srcdir)/third_party/utf8_range"
|
||||
|
||||
$srcs = ["protobuf.c", "convert.c", "defs.c", "message.c",
|
||||
"repeated_field.c", "map.c", "ruby-upb.c", "wrap_memcpy.c",
|
||||
"naive.c", "range2-neon.c", "range2-sse.c"]
|
||||
|
||||
create_makefile(ext_name)
|
702
deps/protobuf/ruby/ext/google/protobuf_c/map.c
vendored
Normal file
702
deps/protobuf/ruby/ext/google/protobuf_c/map.c
vendored
Normal file
@ -0,0 +1,702 @@
|
||||
// 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.
|
||||
|
||||
#include "convert.h"
|
||||
#include "defs.h"
|
||||
#include "message.h"
|
||||
#include "protobuf.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Basic map operations on top of upb_Map.
|
||||
//
|
||||
// Note that we roll our own `Map` container here because, as for
|
||||
// `RepeatedField`, we want a strongly-typed container. This is so that any user
|
||||
// errors due to incorrect map key or value types are raised as close as
|
||||
// possible to the error site, rather than at some deferred point (e.g.,
|
||||
// serialization).
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Map container type.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
typedef struct {
|
||||
const upb_Map* map; // Can convert to mutable when non-frozen.
|
||||
upb_CType key_type;
|
||||
TypeInfo value_type_info;
|
||||
VALUE value_type_class;
|
||||
VALUE arena;
|
||||
} Map;
|
||||
|
||||
static void Map_mark(void* _self) {
|
||||
Map* self = _self;
|
||||
rb_gc_mark(self->value_type_class);
|
||||
rb_gc_mark(self->arena);
|
||||
}
|
||||
|
||||
const rb_data_type_t Map_type = {
|
||||
"Google::Protobuf::Map",
|
||||
{Map_mark, RUBY_DEFAULT_FREE, NULL},
|
||||
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
||||
};
|
||||
|
||||
VALUE cMap;
|
||||
|
||||
static Map* ruby_to_Map(VALUE _self) {
|
||||
Map* self;
|
||||
TypedData_Get_Struct(_self, Map, &Map_type, self);
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE Map_alloc(VALUE klass) {
|
||||
Map* self = ALLOC(Map);
|
||||
self->map = NULL;
|
||||
self->value_type_class = Qnil;
|
||||
self->value_type_info.def.msgdef = NULL;
|
||||
self->arena = Qnil;
|
||||
return TypedData_Wrap_Struct(klass, &Map_type, self);
|
||||
}
|
||||
|
||||
VALUE Map_GetRubyWrapper(upb_Map* map, upb_CType key_type, TypeInfo value_type,
|
||||
VALUE arena) {
|
||||
PBRUBY_ASSERT(map);
|
||||
|
||||
VALUE val = ObjectCache_Get(map);
|
||||
|
||||
if (val == Qnil) {
|
||||
val = Map_alloc(cMap);
|
||||
Map* self;
|
||||
ObjectCache_Add(map, val);
|
||||
TypedData_Get_Struct(val, Map, &Map_type, self);
|
||||
self->map = map;
|
||||
self->arena = arena;
|
||||
self->key_type = key_type;
|
||||
self->value_type_info = value_type;
|
||||
if (self->value_type_info.type == kUpb_CType_Message) {
|
||||
const upb_MessageDef* val_m = self->value_type_info.def.msgdef;
|
||||
self->value_type_class = Descriptor_DefToClass(val_m);
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static VALUE Map_new_this_type(Map* from) {
|
||||
VALUE arena_rb = Arena_new();
|
||||
upb_Map* map = upb_Map_New(Arena_get(arena_rb), from->key_type,
|
||||
from->value_type_info.type);
|
||||
VALUE ret =
|
||||
Map_GetRubyWrapper(map, from->key_type, from->value_type_info, arena_rb);
|
||||
PBRUBY_ASSERT(ruby_to_Map(ret)->value_type_class == from->value_type_class);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static TypeInfo Map_keyinfo(Map* self) {
|
||||
TypeInfo ret;
|
||||
ret.type = self->key_type;
|
||||
ret.def.msgdef = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static upb_Map* Map_GetMutable(VALUE _self) {
|
||||
rb_check_frozen(_self);
|
||||
return (upb_Map*)ruby_to_Map(_self)->map;
|
||||
}
|
||||
|
||||
VALUE Map_CreateHash(const upb_Map* map, upb_CType key_type,
|
||||
TypeInfo val_info) {
|
||||
VALUE hash = rb_hash_new();
|
||||
size_t iter = kUpb_Map_Begin;
|
||||
TypeInfo key_info = TypeInfo_from_type(key_type);
|
||||
|
||||
if (!map) return hash;
|
||||
|
||||
while (upb_MapIterator_Next(map, &iter)) {
|
||||
upb_MessageValue key = upb_MapIterator_Key(map, iter);
|
||||
upb_MessageValue val = upb_MapIterator_Value(map, iter);
|
||||
VALUE key_val = Convert_UpbToRuby(key, key_info, Qnil);
|
||||
VALUE val_val = Scalar_CreateHash(val, val_info);
|
||||
rb_hash_aset(hash, key_val, val_val);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
VALUE Map_deep_copy(VALUE obj) {
|
||||
Map* self = ruby_to_Map(obj);
|
||||
VALUE new_arena_rb = Arena_new();
|
||||
upb_Arena* arena = Arena_get(new_arena_rb);
|
||||
upb_Map* new_map =
|
||||
upb_Map_New(arena, self->key_type, self->value_type_info.type);
|
||||
size_t iter = kUpb_Map_Begin;
|
||||
while (upb_MapIterator_Next(self->map, &iter)) {
|
||||
upb_MessageValue key = upb_MapIterator_Key(self->map, iter);
|
||||
upb_MessageValue val = upb_MapIterator_Value(self->map, iter);
|
||||
upb_MessageValue val_copy =
|
||||
Msgval_DeepCopy(val, self->value_type_info, arena);
|
||||
upb_Map_Set(new_map, key, val_copy, arena);
|
||||
}
|
||||
|
||||
return Map_GetRubyWrapper(new_map, self->key_type, self->value_type_info,
|
||||
new_arena_rb);
|
||||
}
|
||||
|
||||
const upb_Map* Map_GetUpbMap(VALUE val, const upb_FieldDef* field,
|
||||
upb_Arena* arena) {
|
||||
const upb_FieldDef* key_field = map_field_key(field);
|
||||
const upb_FieldDef* value_field = map_field_value(field);
|
||||
TypeInfo value_type_info = TypeInfo_get(value_field);
|
||||
Map* self;
|
||||
|
||||
if (!RB_TYPE_P(val, T_DATA) || !RTYPEDDATA_P(val) ||
|
||||
RTYPEDDATA_TYPE(val) != &Map_type) {
|
||||
rb_raise(cTypeError, "Expected Map instance");
|
||||
}
|
||||
|
||||
self = ruby_to_Map(val);
|
||||
if (self->key_type != upb_FieldDef_CType(key_field)) {
|
||||
rb_raise(cTypeError, "Map key type does not match field's key type");
|
||||
}
|
||||
if (self->value_type_info.type != value_type_info.type) {
|
||||
rb_raise(cTypeError, "Map value type does not match field's value type");
|
||||
}
|
||||
if (self->value_type_info.def.msgdef != value_type_info.def.msgdef) {
|
||||
rb_raise(cTypeError, "Map value type has wrong message/enum class");
|
||||
}
|
||||
|
||||
Arena_fuse(self->arena, arena);
|
||||
return self->map;
|
||||
}
|
||||
|
||||
void Map_Inspect(StringBuilder* b, const upb_Map* map, upb_CType key_type,
|
||||
TypeInfo val_type) {
|
||||
bool first = true;
|
||||
TypeInfo key_type_info = {key_type};
|
||||
StringBuilder_Printf(b, "{");
|
||||
if (map) {
|
||||
size_t iter = kUpb_Map_Begin;
|
||||
while (upb_MapIterator_Next(map, &iter)) {
|
||||
upb_MessageValue key = upb_MapIterator_Key(map, iter);
|
||||
upb_MessageValue val = upb_MapIterator_Value(map, iter);
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
StringBuilder_Printf(b, ", ");
|
||||
}
|
||||
StringBuilder_PrintMsgval(b, key, key_type_info);
|
||||
StringBuilder_Printf(b, "=>");
|
||||
StringBuilder_PrintMsgval(b, val, val_type);
|
||||
}
|
||||
}
|
||||
StringBuilder_Printf(b, "}");
|
||||
}
|
||||
|
||||
static int merge_into_self_callback(VALUE key, VALUE val, VALUE _self) {
|
||||
Map* self = ruby_to_Map(_self);
|
||||
upb_Arena* arena = Arena_get(self->arena);
|
||||
upb_MessageValue key_val =
|
||||
Convert_RubyToUpb(key, "", Map_keyinfo(self), arena);
|
||||
upb_MessageValue val_val =
|
||||
Convert_RubyToUpb(val, "", self->value_type_info, arena);
|
||||
upb_Map_Set(Map_GetMutable(_self), key_val, val_val, arena);
|
||||
return ST_CONTINUE;
|
||||
}
|
||||
|
||||
// Used only internally -- shared by #merge and #initialize.
|
||||
static VALUE Map_merge_into_self(VALUE _self, VALUE hashmap) {
|
||||
if (TYPE(hashmap) == T_HASH) {
|
||||
rb_hash_foreach(hashmap, merge_into_self_callback, _self);
|
||||
} else if (RB_TYPE_P(hashmap, T_DATA) && RTYPEDDATA_P(hashmap) &&
|
||||
RTYPEDDATA_TYPE(hashmap) == &Map_type) {
|
||||
Map* self = ruby_to_Map(_self);
|
||||
Map* other = ruby_to_Map(hashmap);
|
||||
upb_Arena* arena = Arena_get(self->arena);
|
||||
upb_Message* self_msg = Map_GetMutable(_self);
|
||||
size_t iter = kUpb_Map_Begin;
|
||||
|
||||
Arena_fuse(other->arena, arena);
|
||||
|
||||
if (self->key_type != other->key_type ||
|
||||
self->value_type_info.type != other->value_type_info.type ||
|
||||
self->value_type_class != other->value_type_class) {
|
||||
rb_raise(rb_eArgError, "Attempt to merge Map with mismatching types");
|
||||
}
|
||||
|
||||
while (upb_MapIterator_Next(other->map, &iter)) {
|
||||
upb_MessageValue key = upb_MapIterator_Key(other->map, iter);
|
||||
upb_MessageValue val = upb_MapIterator_Value(other->map, iter);
|
||||
upb_Map_Set(self_msg, key, val, arena);
|
||||
}
|
||||
} else {
|
||||
rb_raise(rb_eArgError, "Unknown type merging into Map");
|
||||
}
|
||||
return _self;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Map.new(key_type, value_type, value_typeclass = nil, init_hashmap = {})
|
||||
* => new map
|
||||
*
|
||||
* Allocates a new Map container. This constructor may be called with 2, 3, or 4
|
||||
* arguments. The first two arguments are always present and are symbols (taking
|
||||
* on the same values as field-type symbols in message descriptors) that
|
||||
* indicate the type of the map key and value fields.
|
||||
*
|
||||
* The supported key types are: :int32, :int64, :uint32, :uint64, :bool,
|
||||
* :string, :bytes.
|
||||
*
|
||||
* The supported value types are: :int32, :int64, :uint32, :uint64, :bool,
|
||||
* :string, :bytes, :enum, :message.
|
||||
*
|
||||
* The third argument, value_typeclass, must be present if value_type is :enum
|
||||
* or :message. As in RepeatedField#new, this argument must be a message class
|
||||
* (for :message) or enum module (for :enum).
|
||||
*
|
||||
* The last argument, if present, provides initial content for map. Note that
|
||||
* this may be an ordinary Ruby hashmap or another Map instance with identical
|
||||
* key and value types. Also note that this argument may be present whether or
|
||||
* not value_typeclass is present (and it is unambiguously separate from
|
||||
* value_typeclass because value_typeclass's presence is strictly determined by
|
||||
* value_type). The contents of this initial hashmap or Map instance are
|
||||
* shallow-copied into the new Map: the original map is unmodified, but
|
||||
* references to underlying objects will be shared if the value type is a
|
||||
* message type.
|
||||
*/
|
||||
static VALUE Map_init(int argc, VALUE* argv, VALUE _self) {
|
||||
Map* self = ruby_to_Map(_self);
|
||||
VALUE init_arg;
|
||||
|
||||
// We take either two args (:key_type, :value_type), three args (:key_type,
|
||||
// :value_type, "ValueMessageType"), or four args (the above plus an initial
|
||||
// hashmap).
|
||||
if (argc < 2 || argc > 4) {
|
||||
rb_raise(rb_eArgError, "Map constructor expects 2, 3 or 4 arguments.");
|
||||
}
|
||||
|
||||
self->key_type = ruby_to_fieldtype(argv[0]);
|
||||
self->value_type_info =
|
||||
TypeInfo_FromClass(argc, argv, 1, &self->value_type_class, &init_arg);
|
||||
self->arena = Arena_new();
|
||||
|
||||
// Check that the key type is an allowed type.
|
||||
switch (self->key_type) {
|
||||
case kUpb_CType_Int32:
|
||||
case kUpb_CType_Int64:
|
||||
case kUpb_CType_UInt32:
|
||||
case kUpb_CType_UInt64:
|
||||
case kUpb_CType_Bool:
|
||||
case kUpb_CType_String:
|
||||
case kUpb_CType_Bytes:
|
||||
// These are OK.
|
||||
break;
|
||||
default:
|
||||
rb_raise(rb_eArgError, "Invalid key type for map.");
|
||||
}
|
||||
|
||||
self->map = upb_Map_New(Arena_get(self->arena), self->key_type,
|
||||
self->value_type_info.type);
|
||||
ObjectCache_Add(self->map, _self);
|
||||
|
||||
if (init_arg != Qnil) {
|
||||
Map_merge_into_self(_self, init_arg);
|
||||
}
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Map.each(&block)
|
||||
*
|
||||
* Invokes &block on each |key, value| pair in the map, in unspecified order.
|
||||
* Note that Map also includes Enumerable; map thus acts like a normal Ruby
|
||||
* sequence.
|
||||
*/
|
||||
static VALUE Map_each(VALUE _self) {
|
||||
Map* self = ruby_to_Map(_self);
|
||||
size_t iter = kUpb_Map_Begin;
|
||||
|
||||
while (upb_MapIterator_Next(self->map, &iter)) {
|
||||
upb_MessageValue key = upb_MapIterator_Key(self->map, iter);
|
||||
upb_MessageValue val = upb_MapIterator_Value(self->map, iter);
|
||||
VALUE key_val = Convert_UpbToRuby(key, Map_keyinfo(self), self->arena);
|
||||
VALUE val_val = Convert_UpbToRuby(val, self->value_type_info, self->arena);
|
||||
rb_yield_values(2, key_val, val_val);
|
||||
}
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Map.keys => [list_of_keys]
|
||||
*
|
||||
* Returns the list of keys contained in the map, in unspecified order.
|
||||
*/
|
||||
static VALUE Map_keys(VALUE _self) {
|
||||
Map* self = ruby_to_Map(_self);
|
||||
size_t iter = kUpb_Map_Begin;
|
||||
VALUE ret = rb_ary_new();
|
||||
|
||||
while (upb_MapIterator_Next(self->map, &iter)) {
|
||||
upb_MessageValue key = upb_MapIterator_Key(self->map, iter);
|
||||
VALUE key_val = Convert_UpbToRuby(key, Map_keyinfo(self), self->arena);
|
||||
rb_ary_push(ret, key_val);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Map.values => [list_of_values]
|
||||
*
|
||||
* Returns the list of values contained in the map, in unspecified order.
|
||||
*/
|
||||
static VALUE Map_values(VALUE _self) {
|
||||
Map* self = ruby_to_Map(_self);
|
||||
size_t iter = kUpb_Map_Begin;
|
||||
VALUE ret = rb_ary_new();
|
||||
|
||||
while (upb_MapIterator_Next(self->map, &iter)) {
|
||||
upb_MessageValue val = upb_MapIterator_Value(self->map, iter);
|
||||
VALUE val_val = Convert_UpbToRuby(val, self->value_type_info, self->arena);
|
||||
rb_ary_push(ret, val_val);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Map.[](key) => value
|
||||
*
|
||||
* Accesses the element at the given key. Throws an exception if the key type is
|
||||
* incorrect. Returns nil when the key is not present in the map.
|
||||
*/
|
||||
static VALUE Map_index(VALUE _self, VALUE key) {
|
||||
Map* self = ruby_to_Map(_self);
|
||||
upb_MessageValue key_upb =
|
||||
Convert_RubyToUpb(key, "", Map_keyinfo(self), NULL);
|
||||
upb_MessageValue val;
|
||||
|
||||
if (upb_Map_Get(self->map, key_upb, &val)) {
|
||||
return Convert_UpbToRuby(val, self->value_type_info, self->arena);
|
||||
} else {
|
||||
return Qnil;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Map.[]=(key, value) => value
|
||||
*
|
||||
* Inserts or overwrites the value at the given key with the given new value.
|
||||
* Throws an exception if the key type is incorrect. Returns the new value that
|
||||
* was just inserted.
|
||||
*/
|
||||
static VALUE Map_index_set(VALUE _self, VALUE key, VALUE val) {
|
||||
Map* self = ruby_to_Map(_self);
|
||||
upb_Arena* arena = Arena_get(self->arena);
|
||||
upb_MessageValue key_upb =
|
||||
Convert_RubyToUpb(key, "", Map_keyinfo(self), NULL);
|
||||
upb_MessageValue val_upb =
|
||||
Convert_RubyToUpb(val, "", self->value_type_info, arena);
|
||||
|
||||
upb_Map_Set(Map_GetMutable(_self), key_upb, val_upb, arena);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Map.has_key?(key) => bool
|
||||
*
|
||||
* Returns true if the given key is present in the map. Throws an exception if
|
||||
* the key has the wrong type.
|
||||
*/
|
||||
static VALUE Map_has_key(VALUE _self, VALUE key) {
|
||||
Map* self = ruby_to_Map(_self);
|
||||
upb_MessageValue key_upb =
|
||||
Convert_RubyToUpb(key, "", Map_keyinfo(self), NULL);
|
||||
|
||||
if (upb_Map_Get(self->map, key_upb, NULL)) {
|
||||
return Qtrue;
|
||||
} else {
|
||||
return Qfalse;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Map.delete(key) => old_value
|
||||
*
|
||||
* Deletes the value at the given key, if any, returning either the old value or
|
||||
* nil if none was present. Throws an exception if the key is of the wrong type.
|
||||
*/
|
||||
static VALUE Map_delete(VALUE _self, VALUE key) {
|
||||
Map* self = ruby_to_Map(_self);
|
||||
upb_MessageValue key_upb =
|
||||
Convert_RubyToUpb(key, "", Map_keyinfo(self), NULL);
|
||||
upb_MessageValue val_upb;
|
||||
VALUE ret;
|
||||
|
||||
rb_check_frozen(_self);
|
||||
|
||||
// TODO(haberman): make upb_Map_Delete() also capable of returning the deleted
|
||||
// value.
|
||||
if (upb_Map_Get(self->map, key_upb, &val_upb)) {
|
||||
ret = Convert_UpbToRuby(val_upb, self->value_type_info, self->arena);
|
||||
} else {
|
||||
ret = Qnil;
|
||||
}
|
||||
|
||||
upb_Map_Delete(Map_GetMutable(_self), key_upb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Map.clear
|
||||
*
|
||||
* Removes all entries from the map.
|
||||
*/
|
||||
static VALUE Map_clear(VALUE _self) {
|
||||
upb_Map_Clear(Map_GetMutable(_self));
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Map.length
|
||||
*
|
||||
* Returns the number of entries (key-value pairs) in the map.
|
||||
*/
|
||||
static VALUE Map_length(VALUE _self) {
|
||||
Map* self = ruby_to_Map(_self);
|
||||
return ULL2NUM(upb_Map_Size(self->map));
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Map.dup => new_map
|
||||
*
|
||||
* Duplicates this map with a shallow copy. References to all non-primitive
|
||||
* element objects (e.g., submessages) are shared.
|
||||
*/
|
||||
static VALUE Map_dup(VALUE _self) {
|
||||
Map* self = ruby_to_Map(_self);
|
||||
VALUE new_map_rb = Map_new_this_type(self);
|
||||
Map* new_self = ruby_to_Map(new_map_rb);
|
||||
size_t iter = kUpb_Map_Begin;
|
||||
upb_Arena* arena = Arena_get(new_self->arena);
|
||||
upb_Map* new_map = Map_GetMutable(new_map_rb);
|
||||
|
||||
Arena_fuse(self->arena, arena);
|
||||
|
||||
while (upb_MapIterator_Next(self->map, &iter)) {
|
||||
upb_MessageValue key = upb_MapIterator_Key(self->map, iter);
|
||||
upb_MessageValue val = upb_MapIterator_Value(self->map, iter);
|
||||
upb_Map_Set(new_map, key, val, arena);
|
||||
}
|
||||
|
||||
return new_map_rb;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Map.==(other) => boolean
|
||||
*
|
||||
* Compares this map to another. Maps are equal if they have identical key sets,
|
||||
* and for each key, the values in both maps compare equal. Elements are
|
||||
* compared as per normal Ruby semantics, by calling their :== methods (or
|
||||
* performing a more efficient comparison for primitive types).
|
||||
*
|
||||
* Maps with dissimilar key types or value types/typeclasses are never equal,
|
||||
* even if value comparison (for example, between integers and floats) would
|
||||
* have otherwise indicated that every element has equal value.
|
||||
*/
|
||||
VALUE Map_eq(VALUE _self, VALUE _other) {
|
||||
Map* self = ruby_to_Map(_self);
|
||||
Map* other;
|
||||
|
||||
// Allow comparisons to Ruby hashmaps by converting to a temporary Map
|
||||
// instance. Slow, but workable.
|
||||
if (TYPE(_other) == T_HASH) {
|
||||
VALUE other_map = Map_new_this_type(self);
|
||||
Map_merge_into_self(other_map, _other);
|
||||
_other = other_map;
|
||||
}
|
||||
|
||||
other = ruby_to_Map(_other);
|
||||
|
||||
if (self == other) {
|
||||
return Qtrue;
|
||||
}
|
||||
if (self->key_type != other->key_type ||
|
||||
self->value_type_info.type != other->value_type_info.type ||
|
||||
self->value_type_class != other->value_type_class) {
|
||||
return Qfalse;
|
||||
}
|
||||
if (upb_Map_Size(self->map) != upb_Map_Size(other->map)) {
|
||||
return Qfalse;
|
||||
}
|
||||
|
||||
// For each member of self, check that an equal member exists at the same key
|
||||
// in other.
|
||||
size_t iter = kUpb_Map_Begin;
|
||||
while (upb_MapIterator_Next(self->map, &iter)) {
|
||||
upb_MessageValue key = upb_MapIterator_Key(self->map, iter);
|
||||
upb_MessageValue val = upb_MapIterator_Value(self->map, iter);
|
||||
upb_MessageValue other_val;
|
||||
if (!upb_Map_Get(other->map, key, &other_val)) {
|
||||
// Not present in other map.
|
||||
return Qfalse;
|
||||
}
|
||||
if (!Msgval_IsEqual(val, other_val, self->value_type_info)) {
|
||||
// Present but different value.
|
||||
return Qfalse;
|
||||
}
|
||||
}
|
||||
|
||||
return Qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Message.freeze => self
|
||||
*
|
||||
* Freezes the message object. We have to intercept this so we can pin the
|
||||
* Ruby object into memory so we don't forget it's frozen.
|
||||
*/
|
||||
static VALUE Map_freeze(VALUE _self) {
|
||||
Map* self = ruby_to_Map(_self);
|
||||
if (!RB_OBJ_FROZEN(_self)) {
|
||||
Arena_Pin(self->arena, _self);
|
||||
RB_OBJ_FREEZE(_self);
|
||||
}
|
||||
return _self;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Map.hash => hash_value
|
||||
*
|
||||
* Returns a hash value based on this map's contents.
|
||||
*/
|
||||
VALUE Map_hash(VALUE _self) {
|
||||
Map* self = ruby_to_Map(_self);
|
||||
uint64_t hash = 0;
|
||||
|
||||
size_t iter = kUpb_Map_Begin;
|
||||
TypeInfo key_info = {self->key_type};
|
||||
while (upb_MapIterator_Next(self->map, &iter)) {
|
||||
upb_MessageValue key = upb_MapIterator_Key(self->map, iter);
|
||||
upb_MessageValue val = upb_MapIterator_Value(self->map, iter);
|
||||
hash = Msgval_GetHash(key, key_info, hash);
|
||||
hash = Msgval_GetHash(val, self->value_type_info, hash);
|
||||
}
|
||||
|
||||
return LL2NUM(hash);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Map.to_h => {}
|
||||
*
|
||||
* Returns a Ruby Hash object containing all the values within the map
|
||||
*/
|
||||
VALUE Map_to_h(VALUE _self) {
|
||||
Map* self = ruby_to_Map(_self);
|
||||
return Map_CreateHash(self->map, self->key_type, self->value_type_info);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Map.inspect => string
|
||||
*
|
||||
* Returns a string representing this map's elements. It will be formatted as
|
||||
* "{key => value, key => value, ...}", with each key and value string
|
||||
* representation computed by its own #inspect method.
|
||||
*/
|
||||
VALUE Map_inspect(VALUE _self) {
|
||||
Map* self = ruby_to_Map(_self);
|
||||
|
||||
StringBuilder* builder = StringBuilder_New();
|
||||
Map_Inspect(builder, self->map, self->key_type, self->value_type_info);
|
||||
VALUE ret = StringBuilder_ToRubyString(builder);
|
||||
StringBuilder_Free(builder);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Map.merge(other_map) => map
|
||||
*
|
||||
* Copies key/value pairs from other_map into a copy of this map. If a key is
|
||||
* set in other_map and this map, the value from other_map overwrites the value
|
||||
* in the new copy of this map. Returns the new copy of this map with merged
|
||||
* contents.
|
||||
*/
|
||||
static VALUE Map_merge(VALUE _self, VALUE hashmap) {
|
||||
VALUE dupped = Map_dup(_self);
|
||||
return Map_merge_into_self(dupped, hashmap);
|
||||
}
|
||||
|
||||
void Map_register(VALUE module) {
|
||||
VALUE klass = rb_define_class_under(module, "Map", rb_cObject);
|
||||
rb_define_alloc_func(klass, Map_alloc);
|
||||
rb_gc_register_address(&cMap);
|
||||
cMap = klass;
|
||||
|
||||
rb_define_method(klass, "initialize", Map_init, -1);
|
||||
rb_define_method(klass, "each", Map_each, 0);
|
||||
rb_define_method(klass, "keys", Map_keys, 0);
|
||||
rb_define_method(klass, "values", Map_values, 0);
|
||||
rb_define_method(klass, "[]", Map_index, 1);
|
||||
rb_define_method(klass, "[]=", Map_index_set, 2);
|
||||
rb_define_method(klass, "has_key?", Map_has_key, 1);
|
||||
rb_define_method(klass, "delete", Map_delete, 1);
|
||||
rb_define_method(klass, "clear", Map_clear, 0);
|
||||
rb_define_method(klass, "length", Map_length, 0);
|
||||
rb_define_method(klass, "size", Map_length, 0);
|
||||
rb_define_method(klass, "dup", Map_dup, 0);
|
||||
// Also define #clone so that we don't inherit Object#clone.
|
||||
rb_define_method(klass, "clone", Map_dup, 0);
|
||||
rb_define_method(klass, "==", Map_eq, 1);
|
||||
rb_define_method(klass, "freeze", Map_freeze, 0);
|
||||
rb_define_method(klass, "hash", Map_hash, 0);
|
||||
rb_define_method(klass, "to_h", Map_to_h, 0);
|
||||
rb_define_method(klass, "inspect", Map_inspect, 0);
|
||||
rb_define_method(klass, "merge", Map_merge, 1);
|
||||
rb_include_module(klass, rb_mEnumerable);
|
||||
}
|
66
deps/protobuf/ruby/ext/google/protobuf_c/map.h
vendored
Normal file
66
deps/protobuf/ruby/ext/google/protobuf_c/map.h
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 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.
|
||||
|
||||
#ifndef RUBY_PROTOBUF_MAP_H_
|
||||
#define RUBY_PROTOBUF_MAP_H_
|
||||
|
||||
#include <ruby/ruby.h>
|
||||
|
||||
#include "protobuf.h"
|
||||
#include "ruby-upb.h"
|
||||
|
||||
// Returns a Ruby wrapper object for the given map, which will be created if
|
||||
// one does not exist already.
|
||||
VALUE Map_GetRubyWrapper(upb_Map *map, upb_CType key_type, TypeInfo value_type,
|
||||
VALUE arena);
|
||||
|
||||
// Gets the underlying upb_Map for this Ruby map object, which must have
|
||||
// key/value type that match |field|. If this is not a map or the type doesn't
|
||||
// match, raises an exception.
|
||||
const upb_Map *Map_GetUpbMap(VALUE val, const upb_FieldDef *field,
|
||||
upb_Arena *arena);
|
||||
|
||||
// Implements #inspect for this map by appending its contents to |b|.
|
||||
void Map_Inspect(StringBuilder *b, const upb_Map *map, upb_CType key_type,
|
||||
TypeInfo val_type);
|
||||
|
||||
// Returns a new Hash object containing the contents of this Map.
|
||||
VALUE Map_CreateHash(const upb_Map *map, upb_CType key_type, TypeInfo val_info);
|
||||
|
||||
// Returns a deep copy of this Map object.
|
||||
VALUE Map_deep_copy(VALUE obj);
|
||||
|
||||
// Ruby class of Google::Protobuf::Map.
|
||||
extern VALUE cMap;
|
||||
|
||||
// Call at startup to register all types in this module.
|
||||
void Map_register(VALUE module);
|
||||
|
||||
#endif // RUBY_PROTOBUF_MAP_H_
|
1402
deps/protobuf/ruby/ext/google/protobuf_c/message.c
vendored
Normal file
1402
deps/protobuf/ruby/ext/google/protobuf_c/message.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
104
deps/protobuf/ruby/ext/google/protobuf_c/message.h
vendored
Normal file
104
deps/protobuf/ruby/ext/google/protobuf_c/message.h
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 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.
|
||||
|
||||
#ifndef RUBY_PROTOBUF_MESSAGE_H_
|
||||
#define RUBY_PROTOBUF_MESSAGE_H_
|
||||
|
||||
#include <ruby/ruby.h>
|
||||
|
||||
#include "protobuf.h"
|
||||
#include "ruby-upb.h"
|
||||
|
||||
// Gets the underlying upb_Message* and upb_MessageDef for the given Ruby
|
||||
// message wrapper. Requires that |value| is indeed a message object.
|
||||
const upb_Message* Message_Get(VALUE value, const upb_MessageDef** m);
|
||||
|
||||
// Like Message_Get(), but checks that the object is not frozen and returns a
|
||||
// mutable pointer.
|
||||
upb_Message* Message_GetMutable(VALUE value, const upb_MessageDef** m);
|
||||
|
||||
// Returns the Arena object for this message.
|
||||
VALUE Message_GetArena(VALUE value);
|
||||
|
||||
// Converts |value| into a upb_Message value of the expected upb_MessageDef
|
||||
// type, raising an error if this is not possible. Used when assigning |value|
|
||||
// to a field of another message, which means the message must be of a
|
||||
// particular type.
|
||||
//
|
||||
// This will perform automatic conversions in some cases (for example, Time ->
|
||||
// Google::Protobuf::Timestamp). If any new message is created, it will be
|
||||
// created on |arena|, and any existing message will have its arena fused with
|
||||
// |arena|.
|
||||
const upb_Message* Message_GetUpbMessage(VALUE value, const upb_MessageDef* m,
|
||||
const char* name, upb_Arena* arena);
|
||||
|
||||
// Gets or constructs a Ruby wrapper object for the given message. The wrapper
|
||||
// object will reference |arena| and ensure that it outlives this object.
|
||||
VALUE Message_GetRubyWrapper(upb_Message* msg, const upb_MessageDef* m,
|
||||
VALUE arena);
|
||||
|
||||
// Gets the given field from this message.
|
||||
VALUE Message_getfield(VALUE _self, const upb_FieldDef* f);
|
||||
|
||||
// Implements #inspect for this message, printing the text to |b|.
|
||||
void Message_PrintMessage(StringBuilder* b, const upb_Message* msg,
|
||||
const upb_MessageDef* m);
|
||||
|
||||
// Returns a hash value for the given message.
|
||||
uint64_t Message_Hash(const upb_Message* msg, const upb_MessageDef* m,
|
||||
uint64_t seed);
|
||||
|
||||
// Returns a deep copy of the given message.
|
||||
upb_Message* Message_deep_copy(const upb_Message* msg, const upb_MessageDef* m,
|
||||
upb_Arena* arena);
|
||||
|
||||
// Returns true if these two messages are equal.
|
||||
bool Message_Equal(const upb_Message* m1, const upb_Message* m2,
|
||||
const upb_MessageDef* m);
|
||||
|
||||
// Checks that this Ruby object is a message, and raises an exception if not.
|
||||
void Message_CheckClass(VALUE klass);
|
||||
|
||||
// Returns a new Hash object containing the contents of this message.
|
||||
VALUE Scalar_CreateHash(upb_MessageValue val, TypeInfo type_info);
|
||||
|
||||
// Creates a message class or enum module for this descriptor, respectively.
|
||||
VALUE build_class_from_descriptor(VALUE descriptor);
|
||||
VALUE build_module_from_enumdesc(VALUE _enumdesc);
|
||||
|
||||
// Returns the Descriptor/EnumDescriptor for the given message class or enum
|
||||
// module, respectively. Returns nil if this is not a message class or enum
|
||||
// module.
|
||||
VALUE MessageOrEnum_GetDescriptor(VALUE klass);
|
||||
|
||||
// Call at startup to register all types in this module.
|
||||
void Message_register(VALUE protobuf);
|
||||
|
||||
#endif // RUBY_PROTOBUF_MESSAGE_H_
|
480
deps/protobuf/ruby/ext/google/protobuf_c/protobuf.c
vendored
Normal file
480
deps/protobuf/ruby/ext/google/protobuf_c/protobuf.c
vendored
Normal file
@ -0,0 +1,480 @@
|
||||
// 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.
|
||||
|
||||
#include "protobuf.h"
|
||||
|
||||
#include <ruby/version.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "map.h"
|
||||
#include "message.h"
|
||||
#include "repeated_field.h"
|
||||
|
||||
VALUE cParseError;
|
||||
VALUE cTypeError;
|
||||
|
||||
const upb_FieldDef *map_field_key(const upb_FieldDef *field) {
|
||||
const upb_MessageDef *entry = upb_FieldDef_MessageSubDef(field);
|
||||
return upb_MessageDef_FindFieldByNumber(entry, 1);
|
||||
}
|
||||
|
||||
const upb_FieldDef *map_field_value(const upb_FieldDef *field) {
|
||||
const upb_MessageDef *entry = upb_FieldDef_MessageSubDef(field);
|
||||
return upb_MessageDef_FindFieldByNumber(entry, 2);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// StringBuilder, for inspect
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
struct StringBuilder {
|
||||
size_t size;
|
||||
size_t cap;
|
||||
char *data;
|
||||
};
|
||||
|
||||
typedef struct StringBuilder StringBuilder;
|
||||
|
||||
static size_t StringBuilder_SizeOf(size_t cap) {
|
||||
return sizeof(StringBuilder) + cap;
|
||||
}
|
||||
|
||||
StringBuilder *StringBuilder_New() {
|
||||
const size_t cap = 128;
|
||||
StringBuilder *builder = malloc(sizeof(*builder));
|
||||
builder->size = 0;
|
||||
builder->cap = cap;
|
||||
builder->data = malloc(builder->cap);
|
||||
return builder;
|
||||
}
|
||||
|
||||
void StringBuilder_Free(StringBuilder *b) {
|
||||
free(b->data);
|
||||
free(b);
|
||||
}
|
||||
|
||||
void StringBuilder_Printf(StringBuilder *b, const char *fmt, ...) {
|
||||
size_t have = b->cap - b->size;
|
||||
size_t n;
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
n = vsnprintf(&b->data[b->size], have, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (have <= n) {
|
||||
while (have <= n) {
|
||||
b->cap *= 2;
|
||||
have = b->cap - b->size;
|
||||
}
|
||||
b->data = realloc(b->data, StringBuilder_SizeOf(b->cap));
|
||||
va_start(args, fmt);
|
||||
n = vsnprintf(&b->data[b->size], have, fmt, args);
|
||||
va_end(args);
|
||||
PBRUBY_ASSERT(n < have);
|
||||
}
|
||||
|
||||
b->size += n;
|
||||
}
|
||||
|
||||
VALUE StringBuilder_ToRubyString(StringBuilder *b) {
|
||||
VALUE ret = rb_str_new(b->data, b->size);
|
||||
rb_enc_associate(ret, rb_utf8_encoding());
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void StringBuilder_PrintEnum(StringBuilder *b, int32_t val,
|
||||
const upb_EnumDef *e) {
|
||||
const upb_EnumValueDef *ev = upb_EnumDef_FindValueByNumber(e, val);
|
||||
if (ev) {
|
||||
StringBuilder_Printf(b, ":%s", upb_EnumValueDef_Name(ev));
|
||||
} else {
|
||||
StringBuilder_Printf(b, "%" PRId32, val);
|
||||
}
|
||||
}
|
||||
|
||||
void StringBuilder_PrintMsgval(StringBuilder *b, upb_MessageValue val,
|
||||
TypeInfo info) {
|
||||
switch (info.type) {
|
||||
case kUpb_CType_Bool:
|
||||
StringBuilder_Printf(b, "%s", val.bool_val ? "true" : "false");
|
||||
break;
|
||||
case kUpb_CType_Float: {
|
||||
VALUE str = rb_inspect(DBL2NUM(val.float_val));
|
||||
StringBuilder_Printf(b, "%s", RSTRING_PTR(str));
|
||||
break;
|
||||
}
|
||||
case kUpb_CType_Double: {
|
||||
VALUE str = rb_inspect(DBL2NUM(val.double_val));
|
||||
StringBuilder_Printf(b, "%s", RSTRING_PTR(str));
|
||||
break;
|
||||
}
|
||||
case kUpb_CType_Int32:
|
||||
StringBuilder_Printf(b, "%" PRId32, val.int32_val);
|
||||
break;
|
||||
case kUpb_CType_UInt32:
|
||||
StringBuilder_Printf(b, "%" PRIu32, val.uint32_val);
|
||||
break;
|
||||
case kUpb_CType_Int64:
|
||||
StringBuilder_Printf(b, "%" PRId64, val.int64_val);
|
||||
break;
|
||||
case kUpb_CType_UInt64:
|
||||
StringBuilder_Printf(b, "%" PRIu64, val.uint64_val);
|
||||
break;
|
||||
case kUpb_CType_String:
|
||||
StringBuilder_Printf(b, "\"%.*s\"", (int)val.str_val.size,
|
||||
val.str_val.data);
|
||||
break;
|
||||
case kUpb_CType_Bytes:
|
||||
StringBuilder_Printf(b, "\"%.*s\"", (int)val.str_val.size,
|
||||
val.str_val.data);
|
||||
break;
|
||||
case kUpb_CType_Enum:
|
||||
StringBuilder_PrintEnum(b, val.int32_val, info.def.enumdef);
|
||||
break;
|
||||
case kUpb_CType_Message:
|
||||
Message_PrintMessage(b, val.msg_val, info.def.msgdef);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Arena
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
typedef struct {
|
||||
upb_Arena *arena;
|
||||
VALUE pinned_objs;
|
||||
} Arena;
|
||||
|
||||
static void Arena_mark(void *data) {
|
||||
Arena *arena = data;
|
||||
rb_gc_mark(arena->pinned_objs);
|
||||
}
|
||||
|
||||
static void Arena_free(void *data) {
|
||||
Arena *arena = data;
|
||||
upb_Arena_Free(arena->arena);
|
||||
xfree(arena);
|
||||
}
|
||||
|
||||
static VALUE cArena;
|
||||
|
||||
const rb_data_type_t Arena_type = {
|
||||
"Google::Protobuf::Internal::Arena",
|
||||
{Arena_mark, Arena_free, NULL},
|
||||
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
||||
};
|
||||
|
||||
static void* ruby_upb_allocfunc(upb_alloc* alloc, void* ptr, size_t oldsize, size_t size) {
|
||||
if (size == 0) {
|
||||
xfree(ptr);
|
||||
return NULL;
|
||||
} else {
|
||||
return xrealloc(ptr, size);
|
||||
}
|
||||
}
|
||||
|
||||
upb_alloc ruby_upb_alloc = {&ruby_upb_allocfunc};
|
||||
|
||||
static VALUE Arena_alloc(VALUE klass) {
|
||||
Arena *arena = ALLOC(Arena);
|
||||
arena->arena = upb_Arena_Init(NULL, 0, &ruby_upb_alloc);
|
||||
arena->pinned_objs = Qnil;
|
||||
return TypedData_Wrap_Struct(klass, &Arena_type, arena);
|
||||
}
|
||||
|
||||
upb_Arena *Arena_get(VALUE _arena) {
|
||||
Arena *arena;
|
||||
TypedData_Get_Struct(_arena, Arena, &Arena_type, arena);
|
||||
return arena->arena;
|
||||
}
|
||||
|
||||
void Arena_fuse(VALUE _arena, upb_Arena *other) {
|
||||
Arena *arena;
|
||||
TypedData_Get_Struct(_arena, Arena, &Arena_type, arena);
|
||||
if (!upb_Arena_Fuse(arena->arena, other)) {
|
||||
rb_raise(rb_eRuntimeError,
|
||||
"Unable to fuse arenas. This should never happen since Ruby does "
|
||||
"not use initial blocks");
|
||||
}
|
||||
}
|
||||
|
||||
VALUE Arena_new() { return Arena_alloc(cArena); }
|
||||
|
||||
void Arena_Pin(VALUE _arena, VALUE obj) {
|
||||
Arena *arena;
|
||||
TypedData_Get_Struct(_arena, Arena, &Arena_type, arena);
|
||||
if (arena->pinned_objs == Qnil) {
|
||||
arena->pinned_objs = rb_ary_new();
|
||||
}
|
||||
rb_ary_push(arena->pinned_objs, obj);
|
||||
}
|
||||
|
||||
void Arena_register(VALUE module) {
|
||||
VALUE internal = rb_define_module_under(module, "Internal");
|
||||
VALUE klass = rb_define_class_under(internal, "Arena", rb_cObject);
|
||||
rb_define_alloc_func(klass, Arena_alloc);
|
||||
rb_gc_register_address(&cArena);
|
||||
cArena = klass;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Object Cache
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// A pointer -> Ruby Object cache that keeps references to Ruby wrapper
|
||||
// objects. This allows us to look up any Ruby wrapper object by the address
|
||||
// of the object it is wrapping. That way we can avoid ever creating two
|
||||
// different wrapper objects for the same C object, which saves memory and
|
||||
// preserves object identity.
|
||||
//
|
||||
// We use WeakMap for the cache. For Ruby <2.7 we also need a secondary Hash
|
||||
// to store WeakMap keys because Ruby <2.7 WeakMap doesn't allow non-finalizable
|
||||
// keys.
|
||||
//
|
||||
// We also need the secondary Hash if sizeof(long) < sizeof(VALUE), because this
|
||||
// means it may not be possible to fit a pointer into a Fixnum. Keys are
|
||||
// pointers, and if they fit into a Fixnum, Ruby doesn't collect them, but if
|
||||
// they overflow and require allocating a Bignum, they could get collected
|
||||
// prematurely, thus removing the cache entry. This happens on 64-bit Windows,
|
||||
// on which pointers are 64 bits but longs are 32 bits. In this case, we enable
|
||||
// the secondary Hash to hold the keys and prevent them from being collected.
|
||||
|
||||
#if RUBY_API_VERSION_CODE >= 20700 && SIZEOF_LONG >= SIZEOF_VALUE
|
||||
#define USE_SECONDARY_MAP 0
|
||||
#else
|
||||
#define USE_SECONDARY_MAP 1
|
||||
#endif
|
||||
|
||||
#if USE_SECONDARY_MAP
|
||||
|
||||
// Maps Numeric -> Object. The object is then used as a key into the WeakMap.
|
||||
// This is needed for Ruby <2.7 where a number cannot be a key to WeakMap.
|
||||
// The object is used only for its identity; it does not contain any data.
|
||||
VALUE secondary_map = Qnil;
|
||||
|
||||
// Mutations to the map are under a mutex, because SeconaryMap_MaybeGC()
|
||||
// iterates over the map which cannot happen in parallel with insertions, or
|
||||
// Ruby will throw:
|
||||
// can't add a new key into hash during iteration (RuntimeError)
|
||||
VALUE secondary_map_mutex = Qnil;
|
||||
|
||||
// Lambda that will GC entries from the secondary map that are no longer present
|
||||
// in the primary map.
|
||||
VALUE gc_secondary_map_lambda = Qnil;
|
||||
ID length;
|
||||
|
||||
extern VALUE weak_obj_cache;
|
||||
|
||||
static void SecondaryMap_Init() {
|
||||
rb_gc_register_address(&secondary_map);
|
||||
rb_gc_register_address(&gc_secondary_map_lambda);
|
||||
rb_gc_register_address(&secondary_map_mutex);
|
||||
secondary_map = rb_hash_new();
|
||||
gc_secondary_map_lambda = rb_eval_string(
|
||||
"->(secondary, weak) {\n"
|
||||
" secondary.delete_if { |k, v| !weak.key?(v) }\n"
|
||||
"}\n");
|
||||
secondary_map_mutex = rb_mutex_new();
|
||||
length = rb_intern("length");
|
||||
}
|
||||
|
||||
// The secondary map is a regular Hash, and will never shrink on its own.
|
||||
// The main object cache is a WeakMap that will automatically remove entries
|
||||
// when the target object is no longer reachable, but unless we manually
|
||||
// remove the corresponding entries from the secondary map, it will grow
|
||||
// without bound.
|
||||
//
|
||||
// To avoid this unbounded growth we periodically remove entries from the
|
||||
// secondary map that are no longer present in the WeakMap. The logic of
|
||||
// how often to perform this GC is an artbirary tuning parameter that
|
||||
// represents a straightforward CPU/memory tradeoff.
|
||||
//
|
||||
// Requires: secondary_map_mutex is held.
|
||||
static void SecondaryMap_MaybeGC() {
|
||||
PBRUBY_ASSERT(rb_mutex_locked_p(secondary_map_mutex) == Qtrue);
|
||||
size_t weak_len = NUM2ULL(rb_funcall(weak_obj_cache, length, 0));
|
||||
size_t secondary_len = RHASH_SIZE(secondary_map);
|
||||
if (secondary_len < weak_len) {
|
||||
// Logically this case should not be possible: a valid entry cannot exist in
|
||||
// the weak table unless there is a corresponding entry in the secondary
|
||||
// table. It should *always* be the case that secondary_len >= weak_len.
|
||||
//
|
||||
// However ObjectSpace::WeakMap#length (and therefore weak_len) is
|
||||
// unreliable: it overreports its true length by including non-live objects.
|
||||
// However these non-live objects are not yielded in iteration, so we may
|
||||
// have previously deleted them from the secondary map in a previous
|
||||
// invocation of SecondaryMap_MaybeGC().
|
||||
//
|
||||
// In this case, we can't measure any waste, so we just return.
|
||||
return;
|
||||
}
|
||||
size_t waste = secondary_len - weak_len;
|
||||
// GC if we could remove at least 2000 entries or 20% of the table size
|
||||
// (whichever is greater). Since the cost of the GC pass is O(N), we
|
||||
// want to make sure that we condition this on overall table size, to
|
||||
// avoid O(N^2) CPU costs.
|
||||
size_t threshold = PBRUBY_MAX(secondary_len * 0.2, 2000);
|
||||
if (waste > threshold) {
|
||||
rb_funcall(gc_secondary_map_lambda, rb_intern("call"), 2, secondary_map,
|
||||
weak_obj_cache);
|
||||
}
|
||||
}
|
||||
|
||||
// Requires: secondary_map_mutex is held by this thread iff create == true.
|
||||
static VALUE SecondaryMap_Get(VALUE key, bool create) {
|
||||
PBRUBY_ASSERT(!create || rb_mutex_locked_p(secondary_map_mutex) == Qtrue);
|
||||
VALUE ret = rb_hash_lookup(secondary_map, key);
|
||||
if (ret == Qnil && create) {
|
||||
SecondaryMap_MaybeGC();
|
||||
ret = rb_class_new_instance(0, NULL, rb_cObject);
|
||||
rb_hash_aset(secondary_map, key, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Requires: secondary_map_mutex is held by this thread iff create == true.
|
||||
static VALUE ObjectCache_GetKey(const void *key, bool create) {
|
||||
VALUE key_val = (VALUE)key;
|
||||
PBRUBY_ASSERT((key_val & 3) == 0);
|
||||
VALUE ret = LL2NUM(key_val >> 2);
|
||||
#if USE_SECONDARY_MAP
|
||||
ret = SecondaryMap_Get(ret, create);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Public ObjectCache API.
|
||||
|
||||
VALUE weak_obj_cache = Qnil;
|
||||
ID item_get;
|
||||
ID item_set;
|
||||
|
||||
static void ObjectCache_Init() {
|
||||
rb_gc_register_address(&weak_obj_cache);
|
||||
VALUE klass = rb_eval_string("ObjectSpace::WeakMap");
|
||||
weak_obj_cache = rb_class_new_instance(0, NULL, klass);
|
||||
item_get = rb_intern("[]");
|
||||
item_set = rb_intern("[]=");
|
||||
#if USE_SECONDARY_MAP
|
||||
SecondaryMap_Init();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ObjectCache_Add(const void *key, VALUE val) {
|
||||
PBRUBY_ASSERT(ObjectCache_Get(key) == Qnil);
|
||||
#if USE_SECONDARY_MAP
|
||||
rb_mutex_lock(secondary_map_mutex);
|
||||
#endif
|
||||
VALUE key_rb = ObjectCache_GetKey(key, true);
|
||||
rb_funcall(weak_obj_cache, item_set, 2, key_rb, val);
|
||||
#if USE_SECONDARY_MAP
|
||||
rb_mutex_unlock(secondary_map_mutex);
|
||||
#endif
|
||||
PBRUBY_ASSERT(ObjectCache_Get(key) == val);
|
||||
}
|
||||
|
||||
// Returns the cached object for this key, if any. Otherwise returns Qnil.
|
||||
VALUE ObjectCache_Get(const void *key) {
|
||||
VALUE key_rb = ObjectCache_GetKey(key, false);
|
||||
return rb_funcall(weak_obj_cache, item_get, 1, key_rb);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Google::Protobuf.discard_unknown(msg)
|
||||
*
|
||||
* Discard unknown fields in the given message object and recursively discard
|
||||
* unknown fields in submessages.
|
||||
*/
|
||||
static VALUE Google_Protobuf_discard_unknown(VALUE self, VALUE msg_rb) {
|
||||
const upb_MessageDef *m;
|
||||
upb_Message *msg = Message_GetMutable(msg_rb, &m);
|
||||
if (!upb_Message_DiscardUnknown(msg, m, 128)) {
|
||||
rb_raise(rb_eRuntimeError, "Messages nested too deeply.");
|
||||
}
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Google::Protobuf.deep_copy(obj) => copy_of_obj
|
||||
*
|
||||
* Performs a deep copy of a RepeatedField instance, a Map instance, or a
|
||||
* message object, recursively copying its members.
|
||||
*/
|
||||
VALUE Google_Protobuf_deep_copy(VALUE self, VALUE obj) {
|
||||
VALUE klass = CLASS_OF(obj);
|
||||
if (klass == cRepeatedField) {
|
||||
return RepeatedField_deep_copy(obj);
|
||||
} else if (klass == cMap) {
|
||||
return Map_deep_copy(obj);
|
||||
} else {
|
||||
VALUE new_arena_rb = Arena_new();
|
||||
upb_Arena *new_arena = Arena_get(new_arena_rb);
|
||||
const upb_MessageDef *m;
|
||||
const upb_Message *msg = Message_Get(obj, &m);
|
||||
upb_Message *new_msg = Message_deep_copy(msg, m, new_arena);
|
||||
return Message_GetRubyWrapper(new_msg, m, new_arena_rb);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Initialization/entry point.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// This must be named "Init_protobuf_c" because the Ruby module is named
|
||||
// "protobuf_c" -- the VM looks for this symbol in our .so.
|
||||
__attribute__((visibility("default"))) void Init_protobuf_c() {
|
||||
ObjectCache_Init();
|
||||
|
||||
VALUE google = rb_define_module("Google");
|
||||
VALUE protobuf = rb_define_module_under(google, "Protobuf");
|
||||
|
||||
Arena_register(protobuf);
|
||||
Defs_register(protobuf);
|
||||
RepeatedField_register(protobuf);
|
||||
Map_register(protobuf);
|
||||
Message_register(protobuf);
|
||||
|
||||
cParseError = rb_const_get(protobuf, rb_intern("ParseError"));
|
||||
rb_gc_register_mark_object(cParseError);
|
||||
cTypeError = rb_const_get(protobuf, rb_intern("TypeError"));
|
||||
rb_gc_register_mark_object(cTypeError);
|
||||
|
||||
rb_define_singleton_method(protobuf, "discard_unknown",
|
||||
Google_Protobuf_discard_unknown, 1);
|
||||
rb_define_singleton_method(protobuf, "deep_copy", Google_Protobuf_deep_copy,
|
||||
1);
|
||||
}
|
120
deps/protobuf/ruby/ext/google/protobuf_c/protobuf.h
vendored
Normal file
120
deps/protobuf/ruby/ext/google/protobuf_c/protobuf.h
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
// 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.
|
||||
|
||||
#ifndef __GOOGLE_PROTOBUF_RUBY_PROTOBUF_H__
|
||||
#define __GOOGLE_PROTOBUF_RUBY_PROTOBUF_H__
|
||||
|
||||
#include <ruby/encoding.h>
|
||||
#include <ruby/ruby.h>
|
||||
#include <ruby/vm.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "ruby-upb.h"
|
||||
|
||||
// These operate on a map field (i.e., a repeated field of submessages whose
|
||||
// submessage type is a map-entry msgdef).
|
||||
const upb_FieldDef* map_field_key(const upb_FieldDef* field);
|
||||
const upb_FieldDef* map_field_value(const upb_FieldDef* field);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Arena
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// A Ruby object that wraps an underlying upb_Arena. Any objects that are
|
||||
// allocated from this arena should reference the Arena in rb_gc_mark(), to
|
||||
// ensure that the object's underlying memory outlives any Ruby object that can
|
||||
// reach it.
|
||||
|
||||
VALUE Arena_new();
|
||||
upb_Arena* Arena_get(VALUE arena);
|
||||
|
||||
// Fuses this arena to another, throwing a Ruby exception if this is not
|
||||
// possible.
|
||||
void Arena_fuse(VALUE arena, upb_Arena* other);
|
||||
|
||||
// Pins this Ruby object to the lifetime of this arena, so that as long as the
|
||||
// arena is alive this object will not be collected.
|
||||
//
|
||||
// We use this to guarantee that the "frozen" bit on the object will be
|
||||
// remembered, even if the user drops their reference to this precise object.
|
||||
void Arena_Pin(VALUE arena, VALUE obj);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// ObjectCache
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Global object cache from upb array/map/message/symtab to wrapper object.
|
||||
//
|
||||
// This is a conceptually "weak" cache, in that it does not prevent "val" from
|
||||
// being collected (though in Ruby <2.7 is it effectively strong, due to
|
||||
// implementation limitations).
|
||||
|
||||
// Adds an entry to the cache. The "arena" parameter must give the arena that
|
||||
// "key" was allocated from. In Ruby <2.7.0, it will be used to remove the key
|
||||
// from the cache when the arena is destroyed.
|
||||
void ObjectCache_Add(const void* key, VALUE val);
|
||||
|
||||
// Returns the cached object for this key, if any. Otherwise returns Qnil.
|
||||
VALUE ObjectCache_Get(const void* key);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// StringBuilder, for inspect
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
struct StringBuilder;
|
||||
typedef struct StringBuilder StringBuilder;
|
||||
|
||||
StringBuilder* StringBuilder_New();
|
||||
void StringBuilder_Free(StringBuilder* b);
|
||||
void StringBuilder_Printf(StringBuilder* b, const char* fmt, ...);
|
||||
VALUE StringBuilder_ToRubyString(StringBuilder* b);
|
||||
|
||||
void StringBuilder_PrintMsgval(StringBuilder* b, upb_MessageValue val,
|
||||
TypeInfo info);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Utilities.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
extern VALUE cTypeError;
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define PBRUBY_ASSERT(expr) \
|
||||
do { \
|
||||
} while (false && (expr))
|
||||
#else
|
||||
#define PBRUBY_ASSERT(expr) assert(expr)
|
||||
#endif
|
||||
|
||||
#define PBRUBY_MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||
|
||||
#define UPB_UNUSED(var) (void)var
|
||||
|
||||
#endif // __GOOGLE_PROTOBUF_RUBY_PROTOBUF_H__
|
657
deps/protobuf/ruby/ext/google/protobuf_c/repeated_field.c
vendored
Normal file
657
deps/protobuf/ruby/ext/google/protobuf_c/repeated_field.c
vendored
Normal file
@ -0,0 +1,657 @@
|
||||
// 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.
|
||||
|
||||
#include "repeated_field.h"
|
||||
|
||||
#include "convert.h"
|
||||
#include "defs.h"
|
||||
#include "message.h"
|
||||
#include "protobuf.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Repeated field container type.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
typedef struct {
|
||||
const upb_Array* array; // Can get as mutable when non-frozen.
|
||||
TypeInfo type_info;
|
||||
VALUE type_class; // To GC-root the msgdef/enumdef in type_info.
|
||||
VALUE arena; // To GC-root the upb_Array.
|
||||
} RepeatedField;
|
||||
|
||||
VALUE cRepeatedField;
|
||||
|
||||
static void RepeatedField_mark(void* _self) {
|
||||
RepeatedField* self = (RepeatedField*)_self;
|
||||
rb_gc_mark(self->type_class);
|
||||
rb_gc_mark(self->arena);
|
||||
}
|
||||
|
||||
const rb_data_type_t RepeatedField_type = {
|
||||
"Google::Protobuf::RepeatedField",
|
||||
{RepeatedField_mark, RUBY_DEFAULT_FREE, NULL},
|
||||
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
||||
};
|
||||
|
||||
static RepeatedField* ruby_to_RepeatedField(VALUE _self) {
|
||||
RepeatedField* self;
|
||||
TypedData_Get_Struct(_self, RepeatedField, &RepeatedField_type, self);
|
||||
return self;
|
||||
}
|
||||
|
||||
static upb_Array* RepeatedField_GetMutable(VALUE _self) {
|
||||
rb_check_frozen(_self);
|
||||
return (upb_Array*)ruby_to_RepeatedField(_self)->array;
|
||||
}
|
||||
|
||||
VALUE RepeatedField_alloc(VALUE klass) {
|
||||
RepeatedField* self = ALLOC(RepeatedField);
|
||||
self->arena = Qnil;
|
||||
self->type_class = Qnil;
|
||||
self->array = NULL;
|
||||
return TypedData_Wrap_Struct(klass, &RepeatedField_type, self);
|
||||
}
|
||||
|
||||
VALUE RepeatedField_GetRubyWrapper(upb_Array* array, TypeInfo type_info,
|
||||
VALUE arena) {
|
||||
PBRUBY_ASSERT(array);
|
||||
VALUE val = ObjectCache_Get(array);
|
||||
|
||||
if (val == Qnil) {
|
||||
val = RepeatedField_alloc(cRepeatedField);
|
||||
RepeatedField* self;
|
||||
ObjectCache_Add(array, val);
|
||||
TypedData_Get_Struct(val, RepeatedField, &RepeatedField_type, self);
|
||||
self->array = array;
|
||||
self->arena = arena;
|
||||
self->type_info = type_info;
|
||||
if (self->type_info.type == kUpb_CType_Message) {
|
||||
self->type_class = Descriptor_DefToClass(type_info.def.msgdef);
|
||||
}
|
||||
}
|
||||
|
||||
PBRUBY_ASSERT(ruby_to_RepeatedField(val)->type_info.type == type_info.type);
|
||||
PBRUBY_ASSERT(ruby_to_RepeatedField(val)->type_info.def.msgdef ==
|
||||
type_info.def.msgdef);
|
||||
return val;
|
||||
}
|
||||
|
||||
static VALUE RepeatedField_new_this_type(RepeatedField* from) {
|
||||
VALUE arena_rb = Arena_new();
|
||||
upb_Array* array = upb_Array_New(Arena_get(arena_rb), from->type_info.type);
|
||||
VALUE ret = RepeatedField_GetRubyWrapper(array, from->type_info, arena_rb);
|
||||
PBRUBY_ASSERT(ruby_to_RepeatedField(ret)->type_class == from->type_class);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void RepeatedField_Inspect(StringBuilder* b, const upb_Array* array,
|
||||
TypeInfo info) {
|
||||
bool first = true;
|
||||
StringBuilder_Printf(b, "[");
|
||||
size_t n = array ? upb_Array_Size(array) : 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
StringBuilder_Printf(b, ", ");
|
||||
}
|
||||
StringBuilder_PrintMsgval(b, upb_Array_Get(array, i), info);
|
||||
}
|
||||
StringBuilder_Printf(b, "]");
|
||||
}
|
||||
|
||||
VALUE RepeatedField_deep_copy(VALUE _self) {
|
||||
RepeatedField* self = ruby_to_RepeatedField(_self);
|
||||
VALUE new_rptfield = RepeatedField_new_this_type(self);
|
||||
RepeatedField* new_self = ruby_to_RepeatedField(new_rptfield);
|
||||
VALUE arena_rb = new_self->arena;
|
||||
upb_Array* new_array = RepeatedField_GetMutable(new_rptfield);
|
||||
upb_Arena* arena = Arena_get(arena_rb);
|
||||
size_t elements = upb_Array_Size(self->array);
|
||||
|
||||
upb_Array_Resize(new_array, elements, arena);
|
||||
|
||||
size_t size = upb_Array_Size(self->array);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
upb_MessageValue msgval = upb_Array_Get(self->array, i);
|
||||
upb_MessageValue copy = Msgval_DeepCopy(msgval, self->type_info, arena);
|
||||
upb_Array_Set(new_array, i, copy);
|
||||
}
|
||||
|
||||
return new_rptfield;
|
||||
}
|
||||
|
||||
const upb_Array* RepeatedField_GetUpbArray(VALUE val, const upb_FieldDef* field,
|
||||
upb_Arena* arena) {
|
||||
RepeatedField* self;
|
||||
TypeInfo type_info = TypeInfo_get(field);
|
||||
|
||||
if (!RB_TYPE_P(val, T_DATA) || !RTYPEDDATA_P(val) ||
|
||||
RTYPEDDATA_TYPE(val) != &RepeatedField_type) {
|
||||
rb_raise(cTypeError, "Expected repeated field array");
|
||||
}
|
||||
|
||||
self = ruby_to_RepeatedField(val);
|
||||
if (self->type_info.type != type_info.type) {
|
||||
rb_raise(cTypeError, "Repeated field array has wrong element type");
|
||||
}
|
||||
|
||||
if (self->type_info.def.msgdef != type_info.def.msgdef) {
|
||||
rb_raise(cTypeError, "Repeated field array has wrong message/enum class");
|
||||
}
|
||||
|
||||
Arena_fuse(self->arena, arena);
|
||||
return self->array;
|
||||
}
|
||||
|
||||
static int index_position(VALUE _index, RepeatedField* repeated_field) {
|
||||
int index = NUM2INT(_index);
|
||||
if (index < 0) index += upb_Array_Size(repeated_field->array);
|
||||
return index;
|
||||
}
|
||||
|
||||
static VALUE RepeatedField_subarray(RepeatedField* self, long beg, long len) {
|
||||
size_t size = upb_Array_Size(self->array);
|
||||
VALUE ary = rb_ary_new2(size);
|
||||
long i;
|
||||
|
||||
for (i = beg; i < beg + len; i++) {
|
||||
upb_MessageValue msgval = upb_Array_Get(self->array, i);
|
||||
VALUE elem = Convert_UpbToRuby(msgval, self->type_info, self->arena);
|
||||
rb_ary_push(ary, elem);
|
||||
}
|
||||
return ary;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* RepeatedField.each(&block)
|
||||
*
|
||||
* Invokes the block once for each element of the repeated field. RepeatedField
|
||||
* also includes Enumerable; combined with this method, the repeated field thus
|
||||
* acts like an ordinary Ruby sequence.
|
||||
*/
|
||||
static VALUE RepeatedField_each(VALUE _self) {
|
||||
RepeatedField* self = ruby_to_RepeatedField(_self);
|
||||
int size = upb_Array_Size(self->array);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
upb_MessageValue msgval = upb_Array_Get(self->array, i);
|
||||
VALUE val = Convert_UpbToRuby(msgval, self->type_info, self->arena);
|
||||
rb_yield(val);
|
||||
}
|
||||
return _self;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* RepeatedField.[](index) => value
|
||||
*
|
||||
* Accesses the element at the given index. Returns nil on out-of-bounds
|
||||
*/
|
||||
static VALUE RepeatedField_index(int argc, VALUE* argv, VALUE _self) {
|
||||
RepeatedField* self = ruby_to_RepeatedField(_self);
|
||||
long size = upb_Array_Size(self->array);
|
||||
|
||||
VALUE arg = argv[0];
|
||||
long beg, len;
|
||||
|
||||
if (argc == 1) {
|
||||
if (FIXNUM_P(arg)) {
|
||||
/* standard case */
|
||||
upb_MessageValue msgval;
|
||||
int index = index_position(argv[0], self);
|
||||
if (index < 0 || (size_t)index >= upb_Array_Size(self->array)) {
|
||||
return Qnil;
|
||||
}
|
||||
msgval = upb_Array_Get(self->array, index);
|
||||
return Convert_UpbToRuby(msgval, self->type_info, self->arena);
|
||||
} else {
|
||||
/* check if idx is Range */
|
||||
switch (rb_range_beg_len(arg, &beg, &len, size, 0)) {
|
||||
case Qfalse:
|
||||
break;
|
||||
case Qnil:
|
||||
return Qnil;
|
||||
default:
|
||||
return RepeatedField_subarray(self, beg, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* assume 2 arguments */
|
||||
beg = NUM2LONG(argv[0]);
|
||||
len = NUM2LONG(argv[1]);
|
||||
if (beg < 0) {
|
||||
beg += size;
|
||||
}
|
||||
if (beg >= size) {
|
||||
return Qnil;
|
||||
}
|
||||
return RepeatedField_subarray(self, beg, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* RepeatedField.[]=(index, value)
|
||||
*
|
||||
* Sets the element at the given index. On out-of-bounds assignments, extends
|
||||
* the array and fills the hole (if any) with default values.
|
||||
*/
|
||||
static VALUE RepeatedField_index_set(VALUE _self, VALUE _index, VALUE val) {
|
||||
RepeatedField* self = ruby_to_RepeatedField(_self);
|
||||
int size = upb_Array_Size(self->array);
|
||||
upb_Array* array = RepeatedField_GetMutable(_self);
|
||||
upb_Arena* arena = Arena_get(self->arena);
|
||||
upb_MessageValue msgval = Convert_RubyToUpb(val, "", self->type_info, arena);
|
||||
|
||||
int index = index_position(_index, self);
|
||||
if (index < 0 || index >= (INT_MAX - 1)) {
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
if (index >= size) {
|
||||
upb_Array_Resize(array, index + 1, arena);
|
||||
upb_MessageValue fill;
|
||||
memset(&fill, 0, sizeof(fill));
|
||||
for (int i = size; i < index; i++) {
|
||||
// Fill default values.
|
||||
// TODO(haberman): should this happen at the upb level?
|
||||
upb_Array_Set(array, i, fill);
|
||||
}
|
||||
}
|
||||
|
||||
upb_Array_Set(array, index, msgval);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* RepeatedField.push(value, ...)
|
||||
*
|
||||
* Adds a new element to the repeated field.
|
||||
*/
|
||||
static VALUE RepeatedField_push_vararg(int argc, VALUE* argv, VALUE _self) {
|
||||
RepeatedField* self = ruby_to_RepeatedField(_self);
|
||||
upb_Arena* arena = Arena_get(self->arena);
|
||||
upb_Array* array = RepeatedField_GetMutable(_self);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
upb_MessageValue msgval =
|
||||
Convert_RubyToUpb(argv[i], "", self->type_info, arena);
|
||||
upb_Array_Append(array, msgval, arena);
|
||||
}
|
||||
|
||||
return _self;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* RepeatedField.<<(value)
|
||||
*
|
||||
* Adds a new element to the repeated field.
|
||||
*/
|
||||
static VALUE RepeatedField_push(VALUE _self, VALUE val) {
|
||||
RepeatedField* self = ruby_to_RepeatedField(_self);
|
||||
upb_Arena* arena = Arena_get(self->arena);
|
||||
upb_Array* array = RepeatedField_GetMutable(_self);
|
||||
|
||||
upb_MessageValue msgval = Convert_RubyToUpb(val, "", self->type_info, arena);
|
||||
upb_Array_Append(array, msgval, arena);
|
||||
|
||||
return _self;
|
||||
}
|
||||
|
||||
/*
|
||||
* Private ruby method, used by RepeatedField.pop
|
||||
*/
|
||||
static VALUE RepeatedField_pop_one(VALUE _self) {
|
||||
RepeatedField* self = ruby_to_RepeatedField(_self);
|
||||
size_t size = upb_Array_Size(self->array);
|
||||
upb_Array* array = RepeatedField_GetMutable(_self);
|
||||
upb_MessageValue last;
|
||||
VALUE ret;
|
||||
|
||||
if (size == 0) {
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
last = upb_Array_Get(self->array, size - 1);
|
||||
ret = Convert_UpbToRuby(last, self->type_info, self->arena);
|
||||
|
||||
upb_Array_Resize(array, size - 1, Arena_get(self->arena));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* RepeatedField.replace(list)
|
||||
*
|
||||
* Replaces the contents of the repeated field with the given list of elements.
|
||||
*/
|
||||
static VALUE RepeatedField_replace(VALUE _self, VALUE list) {
|
||||
RepeatedField* self = ruby_to_RepeatedField(_self);
|
||||
upb_Array* array = RepeatedField_GetMutable(_self);
|
||||
int i;
|
||||
|
||||
Check_Type(list, T_ARRAY);
|
||||
upb_Array_Resize(array, 0, Arena_get(self->arena));
|
||||
|
||||
for (i = 0; i < RARRAY_LEN(list); i++) {
|
||||
RepeatedField_push(_self, rb_ary_entry(list, i));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* RepeatedField.clear
|
||||
*
|
||||
* Clears (removes all elements from) this repeated field.
|
||||
*/
|
||||
static VALUE RepeatedField_clear(VALUE _self) {
|
||||
RepeatedField* self = ruby_to_RepeatedField(_self);
|
||||
upb_Array* array = RepeatedField_GetMutable(_self);
|
||||
upb_Array_Resize(array, 0, Arena_get(self->arena));
|
||||
return _self;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* RepeatedField.length
|
||||
*
|
||||
* Returns the length of this repeated field.
|
||||
*/
|
||||
static VALUE RepeatedField_length(VALUE _self) {
|
||||
RepeatedField* self = ruby_to_RepeatedField(_self);
|
||||
return INT2NUM(upb_Array_Size(self->array));
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* RepeatedField.dup => repeated_field
|
||||
*
|
||||
* Duplicates this repeated field with a shallow copy. References to all
|
||||
* non-primitive element objects (e.g., submessages) are shared.
|
||||
*/
|
||||
static VALUE RepeatedField_dup(VALUE _self) {
|
||||
RepeatedField* self = ruby_to_RepeatedField(_self);
|
||||
VALUE new_rptfield = RepeatedField_new_this_type(self);
|
||||
RepeatedField* new_rptfield_self = ruby_to_RepeatedField(new_rptfield);
|
||||
upb_Array* new_array = RepeatedField_GetMutable(new_rptfield);
|
||||
upb_Arena* arena = Arena_get(new_rptfield_self->arena);
|
||||
int size = upb_Array_Size(self->array);
|
||||
int i;
|
||||
|
||||
Arena_fuse(self->arena, arena);
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
upb_MessageValue msgval = upb_Array_Get(self->array, i);
|
||||
upb_Array_Append(new_array, msgval, arena);
|
||||
}
|
||||
|
||||
return new_rptfield;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* RepeatedField.to_ary => array
|
||||
*
|
||||
* Used when converted implicitly into array, e.g. compared to an Array.
|
||||
* Also called as a fallback of Object#to_a
|
||||
*/
|
||||
VALUE RepeatedField_to_ary(VALUE _self) {
|
||||
RepeatedField* self = ruby_to_RepeatedField(_self);
|
||||
int size = upb_Array_Size(self->array);
|
||||
VALUE ary = rb_ary_new2(size);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
upb_MessageValue msgval = upb_Array_Get(self->array, i);
|
||||
VALUE val = Convert_UpbToRuby(msgval, self->type_info, self->arena);
|
||||
rb_ary_push(ary, val);
|
||||
}
|
||||
|
||||
return ary;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* RepeatedField.==(other) => boolean
|
||||
*
|
||||
* Compares this repeated field to another. Repeated fields are equal if their
|
||||
* element types are equal, their lengths are equal, and each element is equal.
|
||||
* Elements are compared as per normal Ruby semantics, by calling their :==
|
||||
* methods (or performing a more efficient comparison for primitive types).
|
||||
*
|
||||
* Repeated fields with dissimilar element types are never equal, even if value
|
||||
* comparison (for example, between integers and floats) would have otherwise
|
||||
* indicated that every element has equal value.
|
||||
*/
|
||||
VALUE RepeatedField_eq(VALUE _self, VALUE _other) {
|
||||
RepeatedField* self;
|
||||
RepeatedField* other;
|
||||
|
||||
if (_self == _other) {
|
||||
return Qtrue;
|
||||
}
|
||||
|
||||
if (TYPE(_other) == T_ARRAY) {
|
||||
VALUE self_ary = RepeatedField_to_ary(_self);
|
||||
return rb_equal(self_ary, _other);
|
||||
}
|
||||
|
||||
self = ruby_to_RepeatedField(_self);
|
||||
other = ruby_to_RepeatedField(_other);
|
||||
size_t n = upb_Array_Size(self->array);
|
||||
|
||||
if (self->type_info.type != other->type_info.type ||
|
||||
self->type_class != other->type_class ||
|
||||
upb_Array_Size(other->array) != n) {
|
||||
return Qfalse;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
upb_MessageValue val1 = upb_Array_Get(self->array, i);
|
||||
upb_MessageValue val2 = upb_Array_Get(other->array, i);
|
||||
if (!Msgval_IsEqual(val1, val2, self->type_info)) {
|
||||
return Qfalse;
|
||||
}
|
||||
}
|
||||
|
||||
return Qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* RepeatedField.freeze => self
|
||||
*
|
||||
* Freezes the repeated field. We have to intercept this so we can pin the Ruby
|
||||
* object into memory so we don't forget it's frozen.
|
||||
*/
|
||||
static VALUE RepeatedField_freeze(VALUE _self) {
|
||||
RepeatedField* self = ruby_to_RepeatedField(_self);
|
||||
if (!RB_OBJ_FROZEN(_self)) {
|
||||
Arena_Pin(self->arena, _self);
|
||||
RB_OBJ_FREEZE(_self);
|
||||
}
|
||||
return _self;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* RepeatedField.hash => hash_value
|
||||
*
|
||||
* Returns a hash value computed from this repeated field's elements.
|
||||
*/
|
||||
VALUE RepeatedField_hash(VALUE _self) {
|
||||
RepeatedField* self = ruby_to_RepeatedField(_self);
|
||||
uint64_t hash = 0;
|
||||
size_t n = upb_Array_Size(self->array);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
upb_MessageValue val = upb_Array_Get(self->array, i);
|
||||
hash = Msgval_GetHash(val, self->type_info, hash);
|
||||
}
|
||||
|
||||
return LL2NUM(hash);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* RepeatedField.+(other) => repeated field
|
||||
*
|
||||
* Returns a new repeated field that contains the concatenated list of this
|
||||
* repeated field's elements and other's elements. The other (second) list may
|
||||
* be either another repeated field or a Ruby array.
|
||||
*/
|
||||
VALUE RepeatedField_plus(VALUE _self, VALUE list) {
|
||||
VALUE dupped_ = RepeatedField_dup(_self);
|
||||
|
||||
if (TYPE(list) == T_ARRAY) {
|
||||
int i;
|
||||
for (i = 0; i < RARRAY_LEN(list); i++) {
|
||||
VALUE elem = rb_ary_entry(list, i);
|
||||
RepeatedField_push(dupped_, elem);
|
||||
}
|
||||
} else if (RB_TYPE_P(list, T_DATA) && RTYPEDDATA_P(list) &&
|
||||
RTYPEDDATA_TYPE(list) == &RepeatedField_type) {
|
||||
RepeatedField* self = ruby_to_RepeatedField(_self);
|
||||
RepeatedField* list_rptfield = ruby_to_RepeatedField(list);
|
||||
RepeatedField* dupped = ruby_to_RepeatedField(dupped_);
|
||||
upb_Array* dupped_array = RepeatedField_GetMutable(dupped_);
|
||||
upb_Arena* arena = Arena_get(dupped->arena);
|
||||
Arena_fuse(list_rptfield->arena, arena);
|
||||
int size = upb_Array_Size(list_rptfield->array);
|
||||
int i;
|
||||
|
||||
if (self->type_info.type != list_rptfield->type_info.type ||
|
||||
self->type_class != list_rptfield->type_class) {
|
||||
rb_raise(rb_eArgError,
|
||||
"Attempt to append RepeatedField with different element type.");
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
upb_MessageValue msgval = upb_Array_Get(list_rptfield->array, i);
|
||||
upb_Array_Append(dupped_array, msgval, arena);
|
||||
}
|
||||
} else {
|
||||
rb_raise(rb_eArgError, "Unknown type appending to RepeatedField");
|
||||
}
|
||||
|
||||
return dupped_;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* RepeatedField.concat(other) => self
|
||||
*
|
||||
* concats the passed in array to self. Returns a Ruby array.
|
||||
*/
|
||||
VALUE RepeatedField_concat(VALUE _self, VALUE list) {
|
||||
int i;
|
||||
|
||||
Check_Type(list, T_ARRAY);
|
||||
for (i = 0; i < RARRAY_LEN(list); i++) {
|
||||
RepeatedField_push(_self, rb_ary_entry(list, i));
|
||||
}
|
||||
return _self;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* RepeatedField.new(type, type_class = nil, initial_elems = [])
|
||||
*
|
||||
* Creates a new repeated field. The provided type must be a Ruby symbol, and
|
||||
* can take on the same values as those accepted by FieldDescriptor#type=. If
|
||||
* the type is :message or :enum, type_class must be non-nil, and must be the
|
||||
* Ruby class or module returned by Descriptor#msgclass or
|
||||
* EnumDescriptor#enummodule, respectively. An initial list of elements may also
|
||||
* be provided.
|
||||
*/
|
||||
VALUE RepeatedField_init(int argc, VALUE* argv, VALUE _self) {
|
||||
RepeatedField* self = ruby_to_RepeatedField(_self);
|
||||
upb_Arena* arena;
|
||||
VALUE ary = Qnil;
|
||||
|
||||
self->arena = Arena_new();
|
||||
arena = Arena_get(self->arena);
|
||||
|
||||
if (argc < 1) {
|
||||
rb_raise(rb_eArgError, "Expected at least 1 argument.");
|
||||
}
|
||||
|
||||
self->type_info = TypeInfo_FromClass(argc, argv, 0, &self->type_class, &ary);
|
||||
self->array = upb_Array_New(arena, self->type_info.type);
|
||||
ObjectCache_Add(self->array, _self);
|
||||
|
||||
if (ary != Qnil) {
|
||||
if (!RB_TYPE_P(ary, T_ARRAY)) {
|
||||
rb_raise(rb_eArgError, "Expected array as initialize argument");
|
||||
}
|
||||
for (int i = 0; i < RARRAY_LEN(ary); i++) {
|
||||
RepeatedField_push(_self, rb_ary_entry(ary, i));
|
||||
}
|
||||
}
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
void RepeatedField_register(VALUE module) {
|
||||
VALUE klass = rb_define_class_under(module, "RepeatedField", rb_cObject);
|
||||
rb_define_alloc_func(klass, RepeatedField_alloc);
|
||||
rb_gc_register_address(&cRepeatedField);
|
||||
cRepeatedField = klass;
|
||||
|
||||
rb_define_method(klass, "initialize", RepeatedField_init, -1);
|
||||
rb_define_method(klass, "each", RepeatedField_each, 0);
|
||||
rb_define_method(klass, "[]", RepeatedField_index, -1);
|
||||
rb_define_method(klass, "at", RepeatedField_index, -1);
|
||||
rb_define_method(klass, "[]=", RepeatedField_index_set, 2);
|
||||
rb_define_method(klass, "push", RepeatedField_push_vararg, -1);
|
||||
rb_define_method(klass, "<<", RepeatedField_push, 1);
|
||||
rb_define_private_method(klass, "pop_one", RepeatedField_pop_one, 0);
|
||||
rb_define_method(klass, "replace", RepeatedField_replace, 1);
|
||||
rb_define_method(klass, "clear", RepeatedField_clear, 0);
|
||||
rb_define_method(klass, "length", RepeatedField_length, 0);
|
||||
rb_define_method(klass, "size", RepeatedField_length, 0);
|
||||
rb_define_method(klass, "dup", RepeatedField_dup, 0);
|
||||
// Also define #clone so that we don't inherit Object#clone.
|
||||
rb_define_method(klass, "clone", RepeatedField_dup, 0);
|
||||
rb_define_method(klass, "==", RepeatedField_eq, 1);
|
||||
rb_define_method(klass, "to_ary", RepeatedField_to_ary, 0);
|
||||
rb_define_method(klass, "freeze", RepeatedField_freeze, 0);
|
||||
rb_define_method(klass, "hash", RepeatedField_hash, 0);
|
||||
rb_define_method(klass, "+", RepeatedField_plus, 1);
|
||||
rb_define_method(klass, "concat", RepeatedField_concat, 1);
|
||||
rb_include_module(klass, rb_mEnumerable);
|
||||
}
|
63
deps/protobuf/ruby/ext/google/protobuf_c/repeated_field.h
vendored
Normal file
63
deps/protobuf/ruby/ext/google/protobuf_c/repeated_field.h
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 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.
|
||||
|
||||
#ifndef RUBY_PROTOBUF_REPEATED_FIELD_H_
|
||||
#define RUBY_PROTOBUF_REPEATED_FIELD_H_
|
||||
|
||||
#include <ruby/ruby.h>
|
||||
|
||||
#include "protobuf.h"
|
||||
#include "ruby-upb.h"
|
||||
|
||||
// Returns a Ruby wrapper object for the given upb_Array, which will be created
|
||||
// if one does not exist already.
|
||||
VALUE RepeatedField_GetRubyWrapper(upb_Array* msg, TypeInfo type_info,
|
||||
VALUE arena);
|
||||
|
||||
// Gets the underlying upb_Array for this Ruby RepeatedField object, which must
|
||||
// have a type that matches |f|. If this is not a repeated field or the type
|
||||
// doesn't match, raises an exception.
|
||||
const upb_Array* RepeatedField_GetUpbArray(VALUE value, const upb_FieldDef* f,
|
||||
upb_Arena* arena);
|
||||
|
||||
// Implements #inspect for this repeated field by appending its contents to |b|.
|
||||
void RepeatedField_Inspect(StringBuilder* b, const upb_Array* array,
|
||||
TypeInfo info);
|
||||
|
||||
// Returns a deep copy of this RepeatedField object.
|
||||
VALUE RepeatedField_deep_copy(VALUE obj);
|
||||
|
||||
// Ruby class of Google::Protobuf::RepeatedField.
|
||||
extern VALUE cRepeatedField;
|
||||
|
||||
// Call at startup to register all types in this module.
|
||||
void RepeatedField_register(VALUE module);
|
||||
|
||||
#endif // RUBY_PROTOBUF_REPEATED_FIELD_H_
|
11115
deps/protobuf/ruby/ext/google/protobuf_c/ruby-upb.c
vendored
Normal file
11115
deps/protobuf/ruby/ext/google/protobuf_c/ruby-upb.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5612
deps/protobuf/ruby/ext/google/protobuf_c/ruby-upb.h
vendored
Normal file
5612
deps/protobuf/ruby/ext/google/protobuf_c/ruby-upb.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
52
deps/protobuf/ruby/ext/google/protobuf_c/wrap_memcpy.c
vendored
Normal file
52
deps/protobuf/ruby/ext/google/protobuf_c/wrap_memcpy.c
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2017 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.
|
||||
|
||||
#include <string.h>
|
||||
|
||||
// On x86-64 Linux with glibc, we link against the 2.2.5 version of memcpy so
|
||||
// that we avoid depending on the 2.14 version of the symbol. This way,
|
||||
// distributions that are using pre-2.14 versions of glibc can successfully use
|
||||
// the gem we distribute
|
||||
// (https://github.com/protocolbuffers/protobuf/issues/2783).
|
||||
//
|
||||
// This wrapper is enabled by passing the linker flags -Wl,-wrap,memcpy in
|
||||
// extconf.rb.
|
||||
#ifdef __linux__
|
||||
#if defined(__x86_64__) && defined(__GNU_LIBRARY__)
|
||||
__asm__(".symver memcpy,memcpy@GLIBC_2.2.5");
|
||||
void *__wrap_memcpy(void *dest, const void *src, size_t n) {
|
||||
return memcpy(dest, src, n);
|
||||
}
|
||||
#else
|
||||
void *__wrap_memcpy(void *dest, const void *src, size_t n) {
|
||||
return memmove(dest, src, n);
|
||||
}
|
||||
#endif
|
||||
#endif
|
Reference in New Issue
Block a user