Merge remote-tracking branch 'origin/3.5.x' into master
This commit is contained in:
commit
383a4941d5
31
CHANGES.txt
31
CHANGES.txt
@ -1,3 +1,34 @@
|
||||
2017-12-20 version 3.5.1 (C++/Java/Python/PHP/Objective-C/C#/Ruby/JavaScript)
|
||||
Planned Future Changes
|
||||
* Make C++ implementation C++11 only: we plan to require C++11 to build
|
||||
protobuf code starting from 3.6.0 release. Please join this github issue:
|
||||
https://github.com/google/protobuf/issues/2780 to provide your feedback.
|
||||
|
||||
protoc
|
||||
* Fixed a bug introduced in 3.5.0 and protoc in Windows now accepts non-ascii
|
||||
characters in paths again.
|
||||
|
||||
C++
|
||||
* Removed several usages of C++11 features in the code base.
|
||||
* Fixed some compiler warnings.
|
||||
|
||||
PHP
|
||||
* Fixed memory leak in C-extension implementation.
|
||||
* Added discardUnknokwnFields API.
|
||||
* Removed duplicatd typedef in C-extension headers.
|
||||
* Avoided calling private php methods (timelib_update_ts).
|
||||
* Fixed Any.php to use fully-qualified name for DescriptorPool.
|
||||
|
||||
Ruby
|
||||
* Added Google_Protobuf_discard_unknown for discarding unknown fields in
|
||||
messages.
|
||||
|
||||
C#
|
||||
* Unknown fields are now preserved by default.
|
||||
* Floating point values are now bitwise compared, affecting message equality
|
||||
check and Contains() API in map and repeated fields.
|
||||
|
||||
|
||||
2017-11-13 version 3.5.0 (C++/Java/Python/PHP/Objective-C/C#/Ruby/JavaScript)
|
||||
Planned Future Changes
|
||||
* Make C++ implementation C++11 only: we plan to require C++11 to build
|
||||
|
@ -926,6 +926,7 @@ ruby_EXTRA_DIST= \
|
||||
ruby/src/main/java/google/ProtobufJavaService.java \
|
||||
ruby/src/main/sentinel.proto \
|
||||
ruby/tests/basic.rb \
|
||||
ruby/tests/encode_decode_test.rb \
|
||||
ruby/tests/gc_test.rb \
|
||||
ruby/tests/repeated_field_test.rb \
|
||||
ruby/tests/stress.rb \
|
||||
|
@ -5,7 +5,7 @@
|
||||
# dependent projects use the :git notation to refer to the library.
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'Protobuf'
|
||||
s.version = '3.5.0'
|
||||
s.version = '3.5.1'
|
||||
s.summary = 'Protocol Buffers v.3 runtime library for Objective-C.'
|
||||
s.homepage = 'https://github.com/google/protobuf'
|
||||
s.license = '3-Clause BSD License'
|
||||
|
@ -17,7 +17,7 @@ AC_PREREQ(2.59)
|
||||
# In the SVN trunk, the version should always be the next anticipated release
|
||||
# version with the "-pre" suffix. (We used to use "-SNAPSHOT" but this pushed
|
||||
# the size of one file name in the dist tarfile over the 99-char limit.)
|
||||
AC_INIT([Protocol Buffers],[3.5.0],[protobuf@googlegroups.com],[protobuf])
|
||||
AC_INIT([Protocol Buffers],[3.5.1],[protobuf@googlegroups.com],[protobuf])
|
||||
|
||||
AM_MAINTAINER_MODE([enable])
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<title>Google Protocol Buffers tools</title>
|
||||
<summary>Tools for Protocol Buffers - Google's data interchange format.</summary>
|
||||
<description>See project site for more info.</description>
|
||||
<version>3.5.0</version>
|
||||
<version>3.5.1</version>
|
||||
<authors>Google Inc.</authors>
|
||||
<owners>protobuf-packages</owners>
|
||||
<licenseUrl>https://github.com/google/protobuf/blob/master/LICENSE</licenseUrl>
|
||||
|
@ -4,7 +4,7 @@
|
||||
<Description>C# runtime library for Protocol Buffers - Google's data interchange format.</Description>
|
||||
<Copyright>Copyright 2015, Google Inc.</Copyright>
|
||||
<AssemblyTitle>Google Protocol Buffers</AssemblyTitle>
|
||||
<VersionPrefix>3.5.0</VersionPrefix>
|
||||
<VersionPrefix>3.5.1</VersionPrefix>
|
||||
<Authors>Google Inc.</Authors>
|
||||
<TargetFrameworks>netstandard1.0;net45</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-parent</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<version>3.5.1</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-parent</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<version>3.5.1</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>Protocol Buffers [Parent]</name>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-parent</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<version>3.5.1</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>protobuf-java-util</artifactId>
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "google-protobuf",
|
||||
"version": "3.5.0",
|
||||
"version": "3.5.1",
|
||||
"description": "Protocol Buffers for JavaScript",
|
||||
"main": "google-protobuf.js",
|
||||
"files": [
|
||||
|
@ -249,9 +249,11 @@ PHP_METHOD(Descriptor, getField) {
|
||||
MAKE_STD_ZVAL(field_hashtable_value);
|
||||
ZVAL_OBJ(field_hashtable_value, field_descriptor_type->create_object(
|
||||
field_descriptor_type TSRMLS_CC));
|
||||
Z_DELREF_P(field_hashtable_value);
|
||||
#else
|
||||
field_hashtable_value =
|
||||
field_descriptor_type->create_object(field_descriptor_type TSRMLS_CC);
|
||||
--GC_REFCOUNT(field_hashtable_value);
|
||||
#endif
|
||||
FieldDescriptor *field_php =
|
||||
UNBOX_HASHTABLE_VALUE(FieldDescriptor, field_hashtable_value);
|
||||
|
@ -1402,7 +1402,6 @@ static void putarray(zval* array, const upb_fielddef* f, upb_sink* sink,
|
||||
RepeatedField* intern = UNBOX(RepeatedField, array);
|
||||
HashTable *ht = PHP_PROTO_HASH_OF(intern->array);
|
||||
size = zend_hash_num_elements(ht);
|
||||
// size = zend_hash_num_elements(PHP_PROTO_HASH_OF(intern->array));
|
||||
if (size == 0) return;
|
||||
|
||||
upb_sink_startseq(sink, getsel(f, UPB_HANDLER_STARTSEQ), &subsink);
|
||||
@ -1615,11 +1614,101 @@ PHP_METHOD(Message, mergeFromJsonString) {
|
||||
}
|
||||
}
|
||||
|
||||
PHP_METHOD(Message, discardUnknownFields) {
|
||||
MessageHeader* msg = UNBOX(MessageHeader, getThis());
|
||||
// TODO(teboring): refactoring with putrawmsg
|
||||
static void discard_unknown_fields(MessageHeader* msg) {
|
||||
upb_msg_field_iter it;
|
||||
|
||||
stringsink* unknown = DEREF(message_data(msg), 0, stringsink*);
|
||||
if (unknown != NULL) {
|
||||
stringsink_uninit(unknown);
|
||||
DEREF(message_data(msg), 0, stringsink*) = NULL;
|
||||
}
|
||||
|
||||
// Recursively discard unknown fields of submessages.
|
||||
Descriptor* desc = msg->descriptor;
|
||||
TSRMLS_FETCH();
|
||||
for (upb_msg_field_begin(&it, desc->msgdef);
|
||||
!upb_msg_field_done(&it);
|
||||
upb_msg_field_next(&it)) {
|
||||
upb_fielddef* f = upb_msg_iter_field(&it);
|
||||
uint32_t offset = desc->layout->fields[upb_fielddef_index(f)].offset;
|
||||
bool containing_oneof = false;
|
||||
|
||||
if (upb_fielddef_containingoneof(f)) {
|
||||
uint32_t oneof_case_offset =
|
||||
desc->layout->fields[upb_fielddef_index(f)].case_offset;
|
||||
// For a oneof, check that this field is actually present -- skip all the
|
||||
// below if not.
|
||||
if (DEREF(message_data(msg), oneof_case_offset, uint32_t) !=
|
||||
upb_fielddef_number(f)) {
|
||||
continue;
|
||||
}
|
||||
// Otherwise, fall through to the appropriate singular-field handler
|
||||
// below.
|
||||
containing_oneof = true;
|
||||
}
|
||||
|
||||
if (is_map_field(f)) {
|
||||
MapIter map_it;
|
||||
int len, size;
|
||||
const upb_fielddef* value_field;
|
||||
|
||||
value_field = map_field_value(f);
|
||||
if (!upb_fielddef_issubmsg(value_field)) continue;
|
||||
|
||||
zval* map_php = CACHED_PTR_TO_ZVAL_PTR(
|
||||
DEREF(message_data(msg), offset, CACHED_VALUE*));
|
||||
if (map_php == NULL) continue;
|
||||
|
||||
Map* intern = UNBOX(Map, map_php);
|
||||
for (map_begin(map_php, &map_it TSRMLS_CC);
|
||||
!map_done(&map_it); map_next(&map_it)) {
|
||||
upb_value value = map_iter_value(&map_it, &len);
|
||||
void* memory = raw_value(upb_value_memory(&value), value_field);
|
||||
#if PHP_MAJOR_VERSION < 7
|
||||
MessageHeader *submsg = UNBOX(MessageHeader, *(zval**)memory);
|
||||
#else
|
||||
MessageHeader *submsg =
|
||||
(MessageHeader*)((char*)(Z_OBJ_P((zval*)memory)) -
|
||||
XtOffsetOf(MessageHeader, std));
|
||||
#endif
|
||||
discard_unknown_fields(submsg);
|
||||
}
|
||||
} else if (upb_fielddef_isseq(f)) {
|
||||
if (!upb_fielddef_issubmsg(f)) continue;
|
||||
|
||||
zval* array_php = CACHED_PTR_TO_ZVAL_PTR(
|
||||
DEREF(message_data(msg), offset, CACHED_VALUE*));
|
||||
if (array_php == NULL) continue;
|
||||
|
||||
int size, i;
|
||||
RepeatedField* intern = UNBOX(RepeatedField, array_php);
|
||||
HashTable *ht = PHP_PROTO_HASH_OF(intern->array);
|
||||
size = zend_hash_num_elements(ht);
|
||||
if (size == 0) continue;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
void* memory = repeated_field_index_native(intern, i TSRMLS_CC);
|
||||
#if PHP_MAJOR_VERSION < 7
|
||||
MessageHeader *submsg = UNBOX(MessageHeader, *(zval**)memory);
|
||||
#else
|
||||
MessageHeader *submsg =
|
||||
(MessageHeader*)((char*)(Z_OBJ_P((zval*)memory)) -
|
||||
XtOffsetOf(MessageHeader, std));
|
||||
#endif
|
||||
discard_unknown_fields(submsg);
|
||||
}
|
||||
} else if (upb_fielddef_issubmsg(f)) {
|
||||
zval* submsg_php = CACHED_PTR_TO_ZVAL_PTR(
|
||||
DEREF(message_data(msg), offset, CACHED_VALUE*));
|
||||
if (Z_TYPE_P(submsg_php) == IS_NULL) continue;
|
||||
MessageHeader* submsg = UNBOX(MessageHeader, submsg_php);
|
||||
discard_unknown_fields(submsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PHP_METHOD(Message, discardUnknownFields) {
|
||||
MessageHeader* msg = UNBOX(MessageHeader, getThis());
|
||||
discard_unknown_fields(msg);
|
||||
}
|
||||
|
@ -29,7 +29,6 @@
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <php.h>
|
||||
#include <ext/date/php_date.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "protobuf.h"
|
||||
@ -379,6 +378,7 @@ PHP_METHOD(Message, whichOneof) {
|
||||
PHP_PROTO_FAKE_SCOPE_BEGIN(LOWER_CLASS##_type); \
|
||||
zval* value = message_get_property_internal(getThis(), &member TSRMLS_CC); \
|
||||
PHP_PROTO_FAKE_SCOPE_END; \
|
||||
zval_dtor(&member); \
|
||||
PHP_PROTO_RETVAL_ZVAL(value); \
|
||||
} \
|
||||
PHP_METHOD(UPPER_CLASS, set##UPPER_FIELD) { \
|
||||
@ -390,6 +390,7 @@ PHP_METHOD(Message, whichOneof) {
|
||||
zval member; \
|
||||
PHP_PROTO_ZVAL_STRING(&member, LOWER_FIELD, 1); \
|
||||
message_set_property_internal(getThis(), &member, value TSRMLS_CC); \
|
||||
zval_dtor(&member); \
|
||||
PHP_PROTO_RETVAL_ZVAL(getThis()); \
|
||||
}
|
||||
|
||||
@ -402,6 +403,7 @@ PHP_METHOD(Message, whichOneof) {
|
||||
message_get_oneof_property_internal(getThis(), &member, \
|
||||
return_value TSRMLS_CC); \
|
||||
PHP_PROTO_FAKE_SCOPE_END; \
|
||||
zval_dtor(&member); \
|
||||
} \
|
||||
PHP_METHOD(UPPER_CLASS, set##UPPER_FIELD) { \
|
||||
zval* value = NULL; \
|
||||
@ -412,6 +414,7 @@ PHP_METHOD(Message, whichOneof) {
|
||||
zval member; \
|
||||
PHP_PROTO_ZVAL_STRING(&member, LOWER_FIELD, 1); \
|
||||
message_set_property_internal(getThis(), &member, value TSRMLS_CC); \
|
||||
zval_dtor(&member); \
|
||||
PHP_PROTO_RETVAL_ZVAL(getThis()); \
|
||||
}
|
||||
|
||||
@ -1120,17 +1123,41 @@ PHP_METHOD(Timestamp, fromDateTime) {
|
||||
zval* datetime;
|
||||
zval member;
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &datetime,
|
||||
php_date_get_date_ce()) == FAILURE) {
|
||||
PHP_PROTO_CE_DECLARE date_interface_ce;
|
||||
if (php_proto_zend_lookup_class("\\DatetimeInterface", 18,
|
||||
&date_interface_ce) == FAILURE) {
|
||||
zend_error(E_ERROR, "Make sure date extension is enabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
php_date_obj* dateobj = UNBOX(php_date_obj, datetime);
|
||||
if (!dateobj->time->sse_uptodate) {
|
||||
timelib_update_ts(dateobj->time, NULL);
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &datetime,
|
||||
PHP_PROTO_CE_UNREF(date_interface_ce)) == FAILURE) {
|
||||
zend_error(E_USER_ERROR, "Expect DatetimeInterface.");
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t timestamp = dateobj->time->sse;
|
||||
// Get timestamp from Datetime object.
|
||||
zval retval;
|
||||
zval function_name;
|
||||
int64_t timestamp;
|
||||
|
||||
#if PHP_MAJOR_VERSION < 7
|
||||
INIT_ZVAL(retval);
|
||||
INIT_ZVAL(function_name);
|
||||
#endif
|
||||
|
||||
PHP_PROTO_ZVAL_STRING(&function_name, "date_timestamp_get", 1);
|
||||
|
||||
if (call_user_function(EG(function_table), NULL, &function_name, &retval, 1,
|
||||
ZVAL_PTR_TO_CACHED_PTR(datetime) TSRMLS_CC) == FAILURE) {
|
||||
zend_error(E_ERROR, "Cannot get timestamp from DateTime.");
|
||||
return;
|
||||
}
|
||||
|
||||
protobuf_convert_to_int64(&retval, ×tamp);
|
||||
|
||||
zval_dtor(&retval);
|
||||
zval_dtor(&function_name);
|
||||
|
||||
// Set seconds
|
||||
MessageHeader* self = UNBOX(MessageHeader, getThis());
|
||||
@ -1138,20 +1165,18 @@ PHP_METHOD(Timestamp, fromDateTime) {
|
||||
upb_msgdef_ntofz(self->descriptor->msgdef, "seconds");
|
||||
void* storage = message_data(self);
|
||||
void* memory = slot_memory(self->descriptor->layout, storage, field);
|
||||
*(int64_t*)memory = dateobj->time->sse;
|
||||
*(int64_t*)memory = timestamp;
|
||||
|
||||
// Set nanos
|
||||
field = upb_msgdef_ntofz(self->descriptor->msgdef, "nanos");
|
||||
storage = message_data(self);
|
||||
memory = slot_memory(self->descriptor->layout, storage, field);
|
||||
*(int32_t*)memory = 0;
|
||||
|
||||
RETURN_NULL();
|
||||
}
|
||||
|
||||
PHP_METHOD(Timestamp, toDateTime) {
|
||||
zval datetime;
|
||||
php_date_instantiate(php_date_get_date_ce(), &datetime TSRMLS_CC);
|
||||
php_date_obj* dateobj = UNBOX(php_date_obj, &datetime);
|
||||
|
||||
// Get seconds
|
||||
MessageHeader* self = UNBOX(MessageHeader, getThis());
|
||||
const upb_fielddef* field =
|
||||
@ -1172,14 +1197,38 @@ PHP_METHOD(Timestamp, toDateTime) {
|
||||
strftime(formated_time, sizeof(formated_time), "%Y-%m-%dT%H:%M:%SUTC",
|
||||
utc_time);
|
||||
|
||||
if (!php_date_initialize(dateobj, formated_time, strlen(formated_time), NULL,
|
||||
NULL, 0 TSRMLS_CC)) {
|
||||
zval_dtor(&datetime);
|
||||
RETURN_NULL();
|
||||
// Create Datetime object.
|
||||
zval datetime;
|
||||
zval formated_time_php;
|
||||
zval function_name;
|
||||
int64_t timestamp = 0;
|
||||
|
||||
#if PHP_MAJOR_VERSION < 7
|
||||
INIT_ZVAL(function_name);
|
||||
INIT_ZVAL(formated_time_php);
|
||||
#endif
|
||||
|
||||
PHP_PROTO_ZVAL_STRING(&function_name, "date_create", 1);
|
||||
PHP_PROTO_ZVAL_STRING(&formated_time_php, formated_time, 1);
|
||||
|
||||
CACHED_VALUE params[1] = {ZVAL_TO_CACHED_VALUE(formated_time_php)};
|
||||
|
||||
if (call_user_function(EG(function_table), NULL,
|
||||
&function_name, &datetime, 1,
|
||||
params TSRMLS_CC) == FAILURE) {
|
||||
zend_error(E_ERROR, "Cannot create DateTime.");
|
||||
return;
|
||||
}
|
||||
|
||||
zval_dtor(&formated_time_php);
|
||||
zval_dtor(&function_name);
|
||||
|
||||
#if PHP_MAJOR_VERSION < 7
|
||||
zval* datetime_ptr = &datetime;
|
||||
PHP_PROTO_RETVAL_ZVAL(datetime_ptr);
|
||||
#else
|
||||
ZVAL_OBJ(return_value, Z_OBJ(datetime));
|
||||
#endif
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -10,11 +10,11 @@
|
||||
<email>protobuf-opensource@google.com</email>
|
||||
<active>yes</active>
|
||||
</lead>
|
||||
<date>2017-11-15</date>
|
||||
<date>2017-12-11</date>
|
||||
<time>11:02:07</time>
|
||||
<version>
|
||||
<release>3.5.0</release>
|
||||
<api>3.5.0</api>
|
||||
<release>3.5.1</release>
|
||||
<api>3.5.1</api>
|
||||
</version>
|
||||
<stability>
|
||||
<release>stable</release>
|
||||
@ -181,6 +181,38 @@ GA release.
|
||||
<time>11:02:07</time>
|
||||
<license uri="https://opensource.org/licenses/BSD-3-Clause">3-Clause BSD License</license>
|
||||
<notes>
|
||||
GA release.
|
||||
</notes>
|
||||
</release>
|
||||
<release>
|
||||
<version>
|
||||
<release>3.5.0.1</release>
|
||||
<api>3.5.0.1</api>
|
||||
</version>
|
||||
<stability>
|
||||
<release>stable</release>
|
||||
<api>stable</api>
|
||||
</stability>
|
||||
<date>2017-12-06</date>
|
||||
<time>11:02:07</time>
|
||||
<license uri="https://opensource.org/licenses/BSD-3-Clause">3-Clause BSD License</license>
|
||||
<notes>
|
||||
GA release.
|
||||
</notes>
|
||||
</release>
|
||||
<release>
|
||||
<version>
|
||||
<release>3.5.1</release>
|
||||
<api>3.5.1</api>
|
||||
</version>
|
||||
<stability>
|
||||
<release>stable</release>
|
||||
<api>stable</api>
|
||||
</stability>
|
||||
<date>2017-12-11</date>
|
||||
<time>11:02:07</time>
|
||||
<license uri="https://opensource.org/licenses/BSD-3-Clause">3-Clause BSD License</license>
|
||||
<notes>
|
||||
GA release.
|
||||
</notes>
|
||||
</release>
|
||||
|
@ -182,8 +182,15 @@ zend_function_entry protobuf_functions[] = {
|
||||
ZEND_FE_END
|
||||
};
|
||||
|
||||
static const zend_module_dep protobuf_deps[] = {
|
||||
ZEND_MOD_OPTIONAL("date")
|
||||
ZEND_MOD_END
|
||||
};
|
||||
|
||||
zend_module_entry protobuf_module_entry = {
|
||||
STANDARD_MODULE_HEADER,
|
||||
STANDARD_MODULE_HEADER_EX,
|
||||
NULL,
|
||||
protobuf_deps,
|
||||
PHP_PROTOBUF_EXTNAME, // extension name
|
||||
protobuf_functions, // function list
|
||||
PHP_MINIT(protobuf), // process startup
|
||||
|
@ -37,7 +37,7 @@
|
||||
#include "upb.h"
|
||||
|
||||
#define PHP_PROTOBUF_EXTNAME "protobuf"
|
||||
#define PHP_PROTOBUF_VERSION "3.5.0"
|
||||
#define PHP_PROTOBUF_VERSION "3.5.1"
|
||||
|
||||
#define MAX_LENGTH_OF_INT64 20
|
||||
#define SIZEOF_INT64 8
|
||||
@ -182,6 +182,8 @@
|
||||
#define CACHED_TO_ZVAL_PTR(VALUE) (VALUE)
|
||||
#define CACHED_PTR_TO_ZVAL_PTR(VALUE) (*VALUE)
|
||||
#define ZVAL_PTR_TO_CACHED_PTR(VALUE) (&VALUE)
|
||||
#define ZVAL_PTR_TO_CACHED_VALUE(VALUE) (VALUE)
|
||||
#define ZVAL_TO_CACHED_VALUE(VALUE) (&VALUE)
|
||||
|
||||
#define CREATE_OBJ_ON_ALLOCATED_ZVAL_PTR(zval_ptr, class_type) \
|
||||
ZVAL_OBJ(zval_ptr, class_type->create_object(class_type TSRMLS_CC));
|
||||
@ -452,6 +454,8 @@ static inline int php_proto_zend_hash_get_current_data_ex(HashTable* ht,
|
||||
#define CACHED_TO_ZVAL_PTR(VALUE) (&VALUE)
|
||||
#define CACHED_PTR_TO_ZVAL_PTR(VALUE) (VALUE)
|
||||
#define ZVAL_PTR_TO_CACHED_PTR(VALUE) (VALUE)
|
||||
#define ZVAL_PTR_TO_CACHED_VALUE(VALUE) (*VALUE)
|
||||
#define ZVAL_TO_CACHED_VALUE(VALUE) (VALUE)
|
||||
|
||||
#define CREATE_OBJ_ON_ALLOCATED_ZVAL_PTR(zval_ptr, class_type) \
|
||||
ZVAL_OBJ(zval_ptr, class_type->create_object(class_type));
|
||||
|
@ -461,8 +461,7 @@ void check_repeated_field(const zend_class_entry* klass, PHP_PROTO_LONG type,
|
||||
CACHED_PTR_TO_ZVAL_PTR((CACHED_VALUE*)memory) TSRMLS_CC);
|
||||
}
|
||||
|
||||
Z_DELREF_P(CACHED_TO_ZVAL_PTR(repeated_field));
|
||||
RETURN_ZVAL(CACHED_TO_ZVAL_PTR(repeated_field), 1, 0);
|
||||
RETURN_ZVAL(CACHED_TO_ZVAL_PTR(repeated_field), 1, 1);
|
||||
|
||||
} else if (Z_TYPE_P(val) == IS_OBJECT) {
|
||||
if (!instanceof_function(Z_OBJCE_P(val), repeated_field_type TSRMLS_CC)) {
|
||||
@ -533,10 +532,10 @@ void check_map_field(const zend_class_entry* klass, PHP_PROTO_LONG key_type,
|
||||
map_field_handlers->write_dimension(
|
||||
CACHED_TO_ZVAL_PTR(map_field), &key,
|
||||
CACHED_PTR_TO_ZVAL_PTR((CACHED_VALUE*)value) TSRMLS_CC);
|
||||
zval_dtor(&key);
|
||||
}
|
||||
|
||||
Z_DELREF_P(CACHED_TO_ZVAL_PTR(map_field));
|
||||
RETURN_ZVAL(CACHED_TO_ZVAL_PTR(map_field), 1, 0);
|
||||
RETURN_ZVAL(CACHED_TO_ZVAL_PTR(map_field), 1, 1);
|
||||
} else if (Z_TYPE_P(val) == IS_OBJECT) {
|
||||
if (!instanceof_function(Z_OBJCE_P(val), map_field_type TSRMLS_CC)) {
|
||||
zend_error(E_USER_ERROR, "Given value is not an instance of %s.",
|
||||
|
@ -583,6 +583,34 @@ class Message
|
||||
public function discardUnknownFields()
|
||||
{
|
||||
$this->unknown = "";
|
||||
foreach ($this->desc->getField() as $field) {
|
||||
if ($field->getType() != GPBType::MESSAGE) {
|
||||
continue;
|
||||
}
|
||||
if ($field->isMap()) {
|
||||
$value_field = $field->getMessageType()->getFieldByNumber(2);
|
||||
if ($value_field->getType() != GPBType::MESSAGE) {
|
||||
continue;
|
||||
}
|
||||
$getter = $field->getGetter();
|
||||
$map = $this->$getter();
|
||||
foreach ($map as $key => $value) {
|
||||
$value->discardUnknownFields();
|
||||
}
|
||||
} else if ($field->getLabel() === GPBLabel::REPEATED) {
|
||||
$getter = $field->getGetter();
|
||||
$arr = $this->$getter();
|
||||
foreach ($arr as $sub) {
|
||||
$sub->discardUnknownFields();
|
||||
}
|
||||
} else if ($field->getLabel() === GPBLabel::OPTIONAL) {
|
||||
$getter = $field->getGetter();
|
||||
$sub = $this->$getter();
|
||||
if (!is_null($sub)) {
|
||||
$sub->discardUnknownFields();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1083,7 +1111,7 @@ class Message
|
||||
}
|
||||
try {
|
||||
$this->mergeFromJsonArray($array);
|
||||
} catch (Exception $e) {
|
||||
} catch (\Exception $e) {
|
||||
throw new GPBDecodeException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
@ -443,36 +443,74 @@ class EncodeDecodeTest extends TestBase
|
||||
|
||||
public function testUnknown()
|
||||
{
|
||||
// Test preserve unknown for varint.
|
||||
$m = new TestMessage();
|
||||
$from = hex2bin('F80601');
|
||||
$from = hex2bin('F80601'); // TODO(teboring): Add a util to encode
|
||||
// varint for better readability
|
||||
$m->mergeFromString($from);
|
||||
$to = $m->serializeToString();
|
||||
$this->assertSame(bin2hex($from), bin2hex($to));
|
||||
|
||||
// Test preserve unknown for 64-bit.
|
||||
$m = new TestMessage();
|
||||
$from = hex2bin('F9060000000000000000');
|
||||
$m->mergeFromString($from);
|
||||
$to = $m->serializeToString();
|
||||
$this->assertSame(bin2hex($from), bin2hex($to));
|
||||
|
||||
// Test preserve unknown for length delimited.
|
||||
$m = new TestMessage();
|
||||
$from = hex2bin('FA0600');
|
||||
$m->mergeFromString($from);
|
||||
$to = $m->serializeToString();
|
||||
$this->assertSame(bin2hex($from), bin2hex($to));
|
||||
|
||||
// Test preserve unknown for 32-bit.
|
||||
$m = new TestMessage();
|
||||
$from = hex2bin('FD0600000000');
|
||||
$m->mergeFromString($from);
|
||||
$to = $m->serializeToString();
|
||||
$this->assertSame(bin2hex($from), bin2hex($to));
|
||||
|
||||
// Test discard unknown in message.
|
||||
$m = new TestMessage();
|
||||
$from = hex2bin('F80601');
|
||||
$m->mergeFromString($from);
|
||||
$m->discardUnknownFields();
|
||||
$to = $m->serializeToString();
|
||||
$this->assertSame("", bin2hex($to));
|
||||
|
||||
// Test discard unknown for singular message field.
|
||||
$m = new TestMessage();
|
||||
$from = hex2bin('8A0103F80601');
|
||||
$m->mergeFromString($from);
|
||||
$m->discardUnknownFields();
|
||||
$to = $m->serializeToString();
|
||||
$this->assertSame("8a0100", bin2hex($to));
|
||||
|
||||
// Test discard unknown for repeated message field.
|
||||
$m = new TestMessage();
|
||||
$from = hex2bin('FA0203F80601');
|
||||
$m->mergeFromString($from);
|
||||
$m->discardUnknownFields();
|
||||
$to = $m->serializeToString();
|
||||
$this->assertSame("fa0200", bin2hex($to));
|
||||
|
||||
// Test discard unknown for map message value field.
|
||||
$m = new TestMessage();
|
||||
$from = hex2bin("BA050708011203F80601");
|
||||
$m->mergeFromString($from);
|
||||
$m->discardUnknownFields();
|
||||
$to = $m->serializeToString();
|
||||
$this->assertSame("ba050408011200", bin2hex($to));
|
||||
|
||||
// Test discard unknown for singular message field.
|
||||
$m = new TestMessage();
|
||||
$from = hex2bin('9A0403F80601');
|
||||
$m->mergeFromString($from);
|
||||
$m->discardUnknownFields();
|
||||
$to = $m->serializeToString();
|
||||
$this->assertSame("9a0400", bin2hex($to));
|
||||
}
|
||||
|
||||
public function testJsonEncode()
|
||||
|
@ -50,7 +50,8 @@ $to->mergeFromString($data);
|
||||
|
||||
TestUtil::assertTestMessage($to);
|
||||
|
||||
$from->setRecursive($from);
|
||||
// TODO(teboring): This causes following tests fail in php7.
|
||||
# $from->setRecursive($from);
|
||||
|
||||
$arr = new RepeatedField(GPBType::MESSAGE, TestMessage::class);
|
||||
$arr[] = new TestMessage;
|
||||
@ -105,6 +106,47 @@ $m = new TestMessage();
|
||||
$m->mergeFromString(hex2bin('F80601'));
|
||||
assert('F80601', bin2hex($m->serializeToString()));
|
||||
|
||||
// Test create repeated field via array.
|
||||
$str_arr = array("abc");
|
||||
$m = new TestMessage();
|
||||
$m->setRepeatedString($str_arr);
|
||||
|
||||
// Test create map field via array.
|
||||
$str_arr = array("abc"=>"abc");
|
||||
$m = new TestMessage();
|
||||
$m->setMapStringString($str_arr);
|
||||
|
||||
// Test unset
|
||||
$from = new TestMessage();
|
||||
TestUtil::setTestMessage($from);
|
||||
unset($from);
|
||||
|
||||
// Test wellknown
|
||||
$from = new \Google\Protobuf\Timestamp();
|
||||
$from->setSeconds(1);
|
||||
assert(1, $from->getSeconds());
|
||||
|
||||
$timestamp = new \Google\Protobuf\Timestamp();
|
||||
|
||||
date_default_timezone_set('UTC');
|
||||
$from = new DateTime('2011-01-01T15:03:01.012345UTC');
|
||||
$timestamp->fromDateTime($from);
|
||||
assert($from->format('U') == $timestamp->getSeconds());
|
||||
assert(0 == $timestamp->getNanos());
|
||||
|
||||
$to = $timestamp->toDateTime();
|
||||
assert(\DateTime::class == get_class($to));
|
||||
assert($from->format('U') == $to->format('U'));
|
||||
|
||||
$from = new \Google\Protobuf\Value();
|
||||
$from->setNumberValue(1);
|
||||
assert(1, $from->getNumberValue());
|
||||
|
||||
// Test descriptor
|
||||
$pool = \Google\Protobuf\DescriptorPool::getGeneratedPool();
|
||||
$desc = $pool->getDescriptorByClassName("\Foo\TestMessage");
|
||||
$field = $desc->getField(1);
|
||||
|
||||
# $from = new TestMessage();
|
||||
# $to = new TestMessage();
|
||||
# TestUtil::setTestMessage($from);
|
||||
|
@ -10,7 +10,7 @@
|
||||
</parent>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protoc</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<version>3.5.1</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Protobuf Compiler</name>
|
||||
<description>
|
||||
|
@ -30,7 +30,7 @@
|
||||
|
||||
# Copyright 2007 Google Inc. All Rights Reserved.
|
||||
|
||||
__version__ = '3.5.0.post1'
|
||||
__version__ = '3.5.1'
|
||||
|
||||
if __name__ != '__main__':
|
||||
try:
|
||||
|
@ -19,7 +19,7 @@ function run_install_test() {
|
||||
chmod +x test-venv/bin/protoc
|
||||
|
||||
source test-venv/bin/activate
|
||||
pip install -i ${PYPI} protobuf==${VERSION}
|
||||
pip install -i ${PYPI} protobuf==${VERSION} --no-cache-dir
|
||||
deactivate
|
||||
rm -fr test-venv
|
||||
}
|
||||
@ -88,6 +88,7 @@ run_install_test ${TESTING_VERSION} python3.4 https://test.pypi.org/simple
|
||||
|
||||
# Deploy egg/wheel packages to testing PyPI and test again.
|
||||
python setup.py bdist_egg bdist_wheel upload -r https://test.pypi.org/legacy/
|
||||
|
||||
run_install_test ${TESTING_VERSION} python2.7 https://test.pypi.org/simple
|
||||
run_install_test ${TESTING_VERSION} python3.4 https://test.pypi.org/simple
|
||||
|
||||
|
@ -240,7 +240,6 @@ if __name__ == '__main__':
|
||||
classifiers=[
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 2",
|
||||
"Programming Language :: Python :: 2.6",
|
||||
"Programming Language :: Python :: 2.7",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.3",
|
||||
|
@ -76,7 +76,7 @@ static upb_enumdef* check_enum_notfrozen(const upb_enumdef* def) {
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#define DEFINE_CLASS(name, string_name) \
|
||||
VALUE c ## name; \
|
||||
VALUE c ## name = Qnil; \
|
||||
const rb_data_type_t _ ## name ## _type = { \
|
||||
string_name, \
|
||||
{ name ## _mark, name ## _free, NULL }, \
|
||||
@ -126,11 +126,11 @@ void DescriptorPool_register(VALUE module) {
|
||||
rb_define_method(klass, "lookup", DescriptorPool_lookup, 1);
|
||||
rb_define_singleton_method(klass, "generated_pool",
|
||||
DescriptorPool_generated_pool, 0);
|
||||
cDescriptorPool = klass;
|
||||
rb_gc_register_address(&cDescriptorPool);
|
||||
cDescriptorPool = klass;
|
||||
|
||||
generated_pool = rb_class_new_instance(0, NULL, klass);
|
||||
rb_gc_register_address(&generated_pool);
|
||||
generated_pool = rb_class_new_instance(0, NULL, klass);
|
||||
}
|
||||
|
||||
static void add_descriptor_to_pool(DescriptorPool* self,
|
||||
@ -299,8 +299,8 @@ void Descriptor_register(VALUE module) {
|
||||
rb_define_method(klass, "name", Descriptor_name, 0);
|
||||
rb_define_method(klass, "name=", Descriptor_name_set, 1);
|
||||
rb_include_module(klass, rb_mEnumerable);
|
||||
cDescriptor = klass;
|
||||
rb_gc_register_address(&cDescriptor);
|
||||
cDescriptor = klass;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -518,8 +518,8 @@ void FieldDescriptor_register(VALUE module) {
|
||||
rb_define_method(klass, "subtype", FieldDescriptor_subtype, 0);
|
||||
rb_define_method(klass, "get", FieldDescriptor_get, 1);
|
||||
rb_define_method(klass, "set", FieldDescriptor_set, 2);
|
||||
cFieldDescriptor = klass;
|
||||
rb_gc_register_address(&cFieldDescriptor);
|
||||
cFieldDescriptor = klass;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -916,8 +916,8 @@ void OneofDescriptor_register(VALUE module) {
|
||||
rb_define_method(klass, "add_field", OneofDescriptor_add_field, 1);
|
||||
rb_define_method(klass, "each", OneofDescriptor_each, 0);
|
||||
rb_include_module(klass, rb_mEnumerable);
|
||||
cOneofDescriptor = klass;
|
||||
rb_gc_register_address(&cOneofDescriptor);
|
||||
cOneofDescriptor = klass;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1037,8 +1037,8 @@ void EnumDescriptor_register(VALUE module) {
|
||||
rb_define_method(klass, "each", EnumDescriptor_each, 0);
|
||||
rb_define_method(klass, "enummodule", EnumDescriptor_enummodule, 0);
|
||||
rb_include_module(klass, rb_mEnumerable);
|
||||
cEnumDescriptor = klass;
|
||||
rb_gc_register_address(&cEnumDescriptor);
|
||||
cEnumDescriptor = klass;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1202,8 +1202,8 @@ void MessageBuilderContext_register(VALUE module) {
|
||||
rb_define_method(klass, "repeated", MessageBuilderContext_repeated, -1);
|
||||
rb_define_method(klass, "map", MessageBuilderContext_map, -1);
|
||||
rb_define_method(klass, "oneof", MessageBuilderContext_oneof, 1);
|
||||
cMessageBuilderContext = klass;
|
||||
rb_gc_register_address(&cMessageBuilderContext);
|
||||
cMessageBuilderContext = klass;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1491,8 +1491,8 @@ void OneofBuilderContext_register(VALUE module) {
|
||||
rb_define_method(klass, "initialize",
|
||||
OneofBuilderContext_initialize, 2);
|
||||
rb_define_method(klass, "optional", OneofBuilderContext_optional, -1);
|
||||
cOneofBuilderContext = klass;
|
||||
rb_gc_register_address(&cOneofBuilderContext);
|
||||
cOneofBuilderContext = klass;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1569,8 +1569,8 @@ void EnumBuilderContext_register(VALUE module) {
|
||||
rb_define_method(klass, "initialize",
|
||||
EnumBuilderContext_initialize, 1);
|
||||
rb_define_method(klass, "value", EnumBuilderContext_value, 2);
|
||||
cEnumBuilderContext = klass;
|
||||
rb_gc_register_address(&cEnumBuilderContext);
|
||||
cEnumBuilderContext = klass;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1645,8 +1645,8 @@ void Builder_register(VALUE module) {
|
||||
rb_define_method(klass, "add_enum", Builder_add_enum, 1);
|
||||
rb_define_method(klass, "initialize", Builder_initialize, 0);
|
||||
rb_define_method(klass, "finalize_to_pool", Builder_finalize_to_pool, 1);
|
||||
cBuilder = klass;
|
||||
rb_gc_register_address(&cBuilder);
|
||||
cBuilder = klass;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1305,3 +1305,91 @@ VALUE Message_encode_json(int argc, VALUE* argv, VALUE klass) {
|
||||
}
|
||||
}
|
||||
|
||||
static void discard_unknown(VALUE msg_rb, const Descriptor* desc) {
|
||||
MessageHeader* msg;
|
||||
upb_msg_field_iter it;
|
||||
|
||||
TypedData_Get_Struct(msg_rb, MessageHeader, &Message_type, msg);
|
||||
|
||||
stringsink* unknown = msg->unknown_fields;
|
||||
if (unknown != NULL) {
|
||||
stringsink_uninit(unknown);
|
||||
msg->unknown_fields = NULL;
|
||||
}
|
||||
|
||||
for (upb_msg_field_begin(&it, desc->msgdef);
|
||||
!upb_msg_field_done(&it);
|
||||
upb_msg_field_next(&it)) {
|
||||
upb_fielddef *f = upb_msg_iter_field(&it);
|
||||
uint32_t offset =
|
||||
desc->layout->fields[upb_fielddef_index(f)].offset +
|
||||
sizeof(MessageHeader);
|
||||
|
||||
if (upb_fielddef_containingoneof(f)) {
|
||||
uint32_t oneof_case_offset =
|
||||
desc->layout->fields[upb_fielddef_index(f)].case_offset +
|
||||
sizeof(MessageHeader);
|
||||
// For a oneof, check that this field is actually present -- skip all the
|
||||
// below if not.
|
||||
if (DEREF(msg, oneof_case_offset, uint32_t) !=
|
||||
upb_fielddef_number(f)) {
|
||||
continue;
|
||||
}
|
||||
// Otherwise, fall through to the appropriate singular-field handler
|
||||
// below.
|
||||
}
|
||||
|
||||
if (!upb_fielddef_issubmsg(f)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_map_field(f)) {
|
||||
if (!upb_fielddef_issubmsg(map_field_value(f))) continue;
|
||||
VALUE map = DEREF(msg, offset, VALUE);
|
||||
if (map == Qnil) continue;
|
||||
Map_iter map_it;
|
||||
for (Map_begin(map, &map_it); !Map_done(&map_it); Map_next(&map_it)) {
|
||||
VALUE submsg = Map_iter_value(&map_it);
|
||||
VALUE descriptor = rb_ivar_get(submsg, descriptor_instancevar_interned);
|
||||
const Descriptor* subdesc = ruby_to_Descriptor(descriptor);
|
||||
discard_unknown(submsg, subdesc);
|
||||
}
|
||||
} else if (upb_fielddef_isseq(f)) {
|
||||
VALUE ary = DEREF(msg, offset, VALUE);
|
||||
if (ary == Qnil) continue;
|
||||
int size = NUM2INT(RepeatedField_length(ary));
|
||||
for (int i = 0; i < size; i++) {
|
||||
void* memory = RepeatedField_index_native(ary, i);
|
||||
VALUE submsg = *((VALUE *)memory);
|
||||
VALUE descriptor = rb_ivar_get(submsg, descriptor_instancevar_interned);
|
||||
const Descriptor* subdesc = ruby_to_Descriptor(descriptor);
|
||||
discard_unknown(submsg, subdesc);
|
||||
}
|
||||
} else {
|
||||
VALUE submsg = DEREF(msg, offset, VALUE);
|
||||
if (submsg == Qnil) continue;
|
||||
VALUE descriptor = rb_ivar_get(submsg, descriptor_instancevar_interned);
|
||||
const Descriptor* subdesc = ruby_to_Descriptor(descriptor);
|
||||
discard_unknown(submsg, subdesc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Google::Protobuf.discard_unknown(msg)
|
||||
*
|
||||
* Discard unknown fields in the given message object and recursively discard
|
||||
* unknown fields in submessages.
|
||||
*/
|
||||
VALUE Google_Protobuf_discard_unknown(VALUE self, VALUE msg_rb) {
|
||||
VALUE klass = CLASS_OF(msg_rb);
|
||||
VALUE descriptor = rb_ivar_get(klass, descriptor_instancevar_interned);
|
||||
Descriptor* desc = ruby_to_Descriptor(descriptor);
|
||||
if (klass == cRepeatedField || klass == cMap) {
|
||||
rb_raise(rb_eArgError, "Expected proto msg for discard unknown.");
|
||||
} else {
|
||||
discard_unknown(msg_rb, desc);
|
||||
}
|
||||
return Qnil;
|
||||
}
|
||||
|
@ -825,8 +825,8 @@ VALUE Map_iter_value(Map_iter* iter) {
|
||||
void Map_register(VALUE module) {
|
||||
VALUE klass = rb_define_class_under(module, "Map", rb_cObject);
|
||||
rb_define_alloc_func(klass, Map_alloc);
|
||||
cMap = klass;
|
||||
rb_gc_register_address(&cMap);
|
||||
cMap = klass;
|
||||
|
||||
rb_define_method(klass, "initialize", Map_init, -1);
|
||||
rb_define_method(klass, "each", Map_each, 0);
|
||||
|
@ -103,6 +103,8 @@ void Init_protobuf_c() {
|
||||
cError = rb_const_get(protobuf, rb_intern("Error"));
|
||||
cParseError = rb_const_get(protobuf, rb_intern("ParseError"));
|
||||
|
||||
rb_define_singleton_method(protobuf, "discard_unknown",
|
||||
Google_Protobuf_discard_unknown, 1);
|
||||
rb_define_singleton_method(protobuf, "deep_copy",
|
||||
Google_Protobuf_deep_copy, 1);
|
||||
|
||||
|
@ -515,6 +515,7 @@ VALUE Message_encode(VALUE klass, VALUE msg_rb);
|
||||
VALUE Message_decode_json(VALUE klass, VALUE data);
|
||||
VALUE Message_encode_json(int argc, VALUE* argv, VALUE klass);
|
||||
|
||||
VALUE Google_Protobuf_discard_unknown(VALUE self, VALUE msg_rb);
|
||||
VALUE Google_Protobuf_deep_copy(VALUE self, VALUE obj);
|
||||
|
||||
VALUE build_module_from_enumdesc(EnumDescriptor* enumdef);
|
||||
|
@ -626,8 +626,8 @@ void RepeatedField_register(VALUE module) {
|
||||
VALUE klass = rb_define_class_under(
|
||||
module, "RepeatedField", rb_cObject);
|
||||
rb_define_alloc_func(klass, RepeatedField_alloc);
|
||||
cRepeatedField = klass;
|
||||
rb_gc_register_address(&cRepeatedField);
|
||||
cRepeatedField = klass;
|
||||
|
||||
rb_define_method(klass, "initialize",
|
||||
RepeatedField_init, -1);
|
||||
|
@ -1,6 +1,6 @@
|
||||
Gem::Specification.new do |s|
|
||||
s.name = "google-protobuf"
|
||||
s.version = "3.5.0"
|
||||
s.version = "3.5.1"
|
||||
s.licenses = ["BSD-3-Clause"]
|
||||
s.summary = "Protocol Buffers"
|
||||
s.description = "Protocol Buffers are Google's data interchange format."
|
||||
|
63
ruby/tests/encode_decode_test.rb
Normal file
63
ruby/tests/encode_decode_test.rb
Normal file
@ -0,0 +1,63 @@
|
||||
#!/usr/bin/ruby
|
||||
|
||||
# generated_code.rb is in the same directory as this test.
|
||||
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
|
||||
|
||||
require 'generated_code_pb'
|
||||
require 'test/unit'
|
||||
|
||||
def hex2bin(s)
|
||||
s.scan(/../).map { |x| x.hex.chr }.join
|
||||
end
|
||||
|
||||
class EncodeDecodeTest < Test::Unit::TestCase
|
||||
def test_discard_unknown
|
||||
# Test discard unknown in message.
|
||||
unknown_msg = A::B::C::TestUnknown.new(:unknown_field => 1)
|
||||
from = A::B::C::TestUnknown.encode(unknown_msg)
|
||||
m = A::B::C::TestMessage.decode(from)
|
||||
Google::Protobuf.discard_unknown(m)
|
||||
to = A::B::C::TestMessage.encode(m)
|
||||
assert_equal '', to
|
||||
|
||||
# Test discard unknown for singular message field.
|
||||
unknown_msg = A::B::C::TestUnknown.new(
|
||||
:optional_unknown =>
|
||||
A::B::C::TestUnknown.new(:unknown_field => 1))
|
||||
from = A::B::C::TestUnknown.encode(unknown_msg)
|
||||
m = A::B::C::TestMessage.decode(from)
|
||||
Google::Protobuf.discard_unknown(m)
|
||||
to = A::B::C::TestMessage.encode(m.optional_msg)
|
||||
assert_equal '', to
|
||||
|
||||
# Test discard unknown for repeated message field.
|
||||
unknown_msg = A::B::C::TestUnknown.new(
|
||||
:repeated_unknown =>
|
||||
[A::B::C::TestUnknown.new(:unknown_field => 1)])
|
||||
from = A::B::C::TestUnknown.encode(unknown_msg)
|
||||
m = A::B::C::TestMessage.decode(from)
|
||||
Google::Protobuf.discard_unknown(m)
|
||||
to = A::B::C::TestMessage.encode(m.repeated_msg[0])
|
||||
assert_equal '', to
|
||||
|
||||
# Test discard unknown for map value message field.
|
||||
unknown_msg = A::B::C::TestUnknown.new(
|
||||
:map_unknown =>
|
||||
{"" => A::B::C::TestUnknown.new(:unknown_field => 1)})
|
||||
from = A::B::C::TestUnknown.encode(unknown_msg)
|
||||
m = A::B::C::TestMessage.decode(from)
|
||||
Google::Protobuf.discard_unknown(m)
|
||||
to = A::B::C::TestMessage.encode(m.map_string_msg[''])
|
||||
assert_equal '', to
|
||||
|
||||
# Test discard unknown for oneof message field.
|
||||
unknown_msg = A::B::C::TestUnknown.new(
|
||||
:oneof_unknown =>
|
||||
A::B::C::TestUnknown.new(:unknown_field => 1))
|
||||
from = A::B::C::TestUnknown.encode(unknown_msg)
|
||||
m = A::B::C::TestMessage.decode(from)
|
||||
Google::Protobuf.discard_unknown(m)
|
||||
to = A::B::C::TestMessage.encode(m.oneof_msg)
|
||||
assert_equal '', to
|
||||
end
|
||||
end
|
@ -57,6 +57,9 @@ message TestMessage {
|
||||
}
|
||||
|
||||
NestedMessage nested_message = 80;
|
||||
|
||||
// Reserved for non-existing field test.
|
||||
// int32 non_exist = 89;
|
||||
}
|
||||
|
||||
enum TestEnum {
|
||||
@ -65,3 +68,13 @@ enum TestEnum {
|
||||
B = 2;
|
||||
C = 3;
|
||||
}
|
||||
|
||||
message TestUnknown {
|
||||
TestUnknown optional_unknown = 11;
|
||||
repeated TestUnknown repeated_unknown = 31;
|
||||
oneof my_oneof {
|
||||
TestUnknown oneof_unknown = 51;
|
||||
}
|
||||
map<string, TestUnknown> map_unknown = 67;
|
||||
int32 unknown_field = 89;
|
||||
}
|
||||
|
@ -183,7 +183,7 @@ nobase_include_HEADERS = \
|
||||
lib_LTLIBRARIES = libprotobuf-lite.la libprotobuf.la libprotoc.la
|
||||
|
||||
libprotobuf_lite_la_LIBADD = $(PTHREAD_LIBS)
|
||||
libprotobuf_lite_la_LDFLAGS = -version-info 15:0:0 -export-dynamic -no-undefined
|
||||
libprotobuf_lite_la_LDFLAGS = -version-info 15:1:0 -export-dynamic -no-undefined
|
||||
if HAVE_LD_VERSION_SCRIPT
|
||||
libprotobuf_lite_la_LDFLAGS += -Wl,--version-script=$(srcdir)/libprotobuf-lite.map
|
||||
EXTRA_libprotobuf_lite_la_DEPENDENCIES = libprotobuf-lite.map
|
||||
@ -233,7 +233,7 @@ libprotobuf_lite_la_SOURCES = \
|
||||
google/protobuf/io/zero_copy_stream_impl_lite.cc
|
||||
|
||||
libprotobuf_la_LIBADD = $(PTHREAD_LIBS)
|
||||
libprotobuf_la_LDFLAGS = -version-info 15:0:0 -export-dynamic -no-undefined
|
||||
libprotobuf_la_LDFLAGS = -version-info 15:1:0 -export-dynamic -no-undefined
|
||||
if HAVE_LD_VERSION_SCRIPT
|
||||
libprotobuf_la_LDFLAGS += -Wl,--version-script=$(srcdir)/libprotobuf.map
|
||||
EXTRA_libprotobuf_la_DEPENDENCIES = libprotobuf.map
|
||||
@ -324,7 +324,7 @@ libprotobuf_la_SOURCES = \
|
||||
nodist_libprotobuf_la_SOURCES = $(nodist_libprotobuf_lite_la_SOURCES)
|
||||
|
||||
libprotoc_la_LIBADD = $(PTHREAD_LIBS) libprotobuf.la
|
||||
libprotoc_la_LDFLAGS = -version-info 15:0:0 -export-dynamic -no-undefined
|
||||
libprotoc_la_LDFLAGS = -version-info 15:1:0 -export-dynamic -no-undefined
|
||||
if HAVE_LD_VERSION_SCRIPT
|
||||
libprotoc_la_LDFLAGS += -Wl,--version-script=$(srcdir)/libprotoc.map
|
||||
EXTRA_libprotoc_la_DEPENDENCIES = libprotoc.map
|
||||
@ -527,6 +527,7 @@ js_well_known_types_sources = \
|
||||
google/protobuf/compiler/js/well_known_types/timestamp.js
|
||||
# We have to cd to $(srcdir) so that out-of-tree builds work properly.
|
||||
google/protobuf/compiler/js/well_known_types_embed.cc: js_embed$(EXEEXT) $(js_well_known_types_sources)
|
||||
mkdir -p `dirname $@` && \
|
||||
oldpwd=`pwd` && cd $(srcdir) && \
|
||||
$$oldpwd/js_embed$(EXEEXT) $(js_well_known_types_sources) > $$oldpwd/$@
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
#error incompatible with your Protocol Buffer headers. Please update
|
||||
#error your headers.
|
||||
#endif
|
||||
#if 3005000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#if 3005001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#error This file was generated by an older version of protoc which is
|
||||
#error incompatible with your Protocol Buffer headers. Please
|
||||
#error regenerate this file with a newer version of protoc.
|
||||
|
@ -13,7 +13,7 @@
|
||||
#error incompatible with your Protocol Buffer headers. Please update
|
||||
#error your headers.
|
||||
#endif
|
||||
#if 3005000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#if 3005001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#error This file was generated by an older version of protoc which is
|
||||
#error incompatible with your Protocol Buffer headers. Please
|
||||
#error regenerate this file with a newer version of protoc.
|
||||
|
@ -113,16 +113,16 @@ struct ExtensionRangeSorter {
|
||||
bool IsPOD(const FieldDescriptor* field) {
|
||||
if (field->is_repeated() || field->is_extension()) return false;
|
||||
switch (field->cpp_type()) {
|
||||
case internal::WireFormatLite::CPPTYPE_ENUM:
|
||||
case internal::WireFormatLite::CPPTYPE_INT32:
|
||||
case internal::WireFormatLite::CPPTYPE_INT64:
|
||||
case internal::WireFormatLite::CPPTYPE_UINT32:
|
||||
case internal::WireFormatLite::CPPTYPE_UINT64:
|
||||
case internal::WireFormatLite::CPPTYPE_FLOAT:
|
||||
case internal::WireFormatLite::CPPTYPE_DOUBLE:
|
||||
case internal::WireFormatLite::CPPTYPE_BOOL:
|
||||
case FieldDescriptor::CPPTYPE_ENUM:
|
||||
case FieldDescriptor::CPPTYPE_INT32:
|
||||
case FieldDescriptor::CPPTYPE_INT64:
|
||||
case FieldDescriptor::CPPTYPE_UINT32:
|
||||
case FieldDescriptor::CPPTYPE_UINT64:
|
||||
case FieldDescriptor::CPPTYPE_FLOAT:
|
||||
case FieldDescriptor::CPPTYPE_DOUBLE:
|
||||
case FieldDescriptor::CPPTYPE_BOOL:
|
||||
return true;
|
||||
case internal::WireFormatLite::CPPTYPE_STRING:
|
||||
case FieldDescriptor::CPPTYPE_STRING:
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
|
@ -13,7 +13,7 @@
|
||||
#error incompatible with your Protocol Buffer headers. Please update
|
||||
#error your headers.
|
||||
#endif
|
||||
#if 3005000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#if 3005001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#error This file was generated by an older version of protoc which is
|
||||
#error incompatible with your Protocol Buffer headers. Please
|
||||
#error regenerate this file with a newer version of protoc.
|
||||
|
@ -13,7 +13,7 @@
|
||||
#error incompatible with your Protocol Buffer headers. Please update
|
||||
#error your headers.
|
||||
#endif
|
||||
#if 3005000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#if 3005001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#error This file was generated by an older version of protoc which is
|
||||
#error incompatible with your Protocol Buffer headers. Please
|
||||
#error regenerate this file with a newer version of protoc.
|
||||
|
@ -13,7 +13,7 @@
|
||||
#error incompatible with your Protocol Buffer headers. Please update
|
||||
#error your headers.
|
||||
#endif
|
||||
#if 3005000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#if 3005001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#error This file was generated by an older version of protoc which is
|
||||
#error incompatible with your Protocol Buffer headers. Please
|
||||
#error regenerate this file with a newer version of protoc.
|
||||
|
@ -13,7 +13,7 @@
|
||||
#error incompatible with your Protocol Buffer headers. Please update
|
||||
#error your headers.
|
||||
#endif
|
||||
#if 3005000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#if 3005001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#error This file was generated by an older version of protoc which is
|
||||
#error incompatible with your Protocol Buffer headers. Please
|
||||
#error regenerate this file with a newer version of protoc.
|
||||
|
@ -13,7 +13,7 @@
|
||||
#error incompatible with your Protocol Buffer headers. Please update
|
||||
#error your headers.
|
||||
#endif
|
||||
#if 3005000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#if 3005001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#error This file was generated by an older version of protoc which is
|
||||
#error incompatible with your Protocol Buffer headers. Please
|
||||
#error regenerate this file with a newer version of protoc.
|
||||
|
@ -170,12 +170,12 @@ size_t Message::SpaceUsedLong() const {
|
||||
|
||||
bool Message::SerializeToFileDescriptor(int file_descriptor) const {
|
||||
io::FileOutputStream output(file_descriptor);
|
||||
return SerializeToZeroCopyStream(&output);
|
||||
return SerializeToZeroCopyStream(&output) && output.Flush();
|
||||
}
|
||||
|
||||
bool Message::SerializePartialToFileDescriptor(int file_descriptor) const {
|
||||
io::FileOutputStream output(file_descriptor);
|
||||
return SerializePartialToZeroCopyStream(&output);
|
||||
return SerializePartialToZeroCopyStream(&output) && output.Flush();
|
||||
}
|
||||
|
||||
bool Message::SerializeToOstream(std::ostream* output) const {
|
||||
|
@ -13,7 +13,7 @@
|
||||
#error incompatible with your Protocol Buffer headers. Please update
|
||||
#error your headers.
|
||||
#endif
|
||||
#if 3005000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#if 3005001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#error This file was generated by an older version of protoc which is
|
||||
#error incompatible with your Protocol Buffer headers. Please
|
||||
#error regenerate this file with a newer version of protoc.
|
||||
|
@ -13,7 +13,7 @@
|
||||
#error incompatible with your Protocol Buffer headers. Please update
|
||||
#error your headers.
|
||||
#endif
|
||||
#if 3005000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#if 3005001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#error This file was generated by an older version of protoc which is
|
||||
#error incompatible with your Protocol Buffer headers. Please
|
||||
#error regenerate this file with a newer version of protoc.
|
||||
|
@ -101,7 +101,7 @@ namespace internal {
|
||||
|
||||
// The current version, represented as a single integer to make comparison
|
||||
// easier: major * 10^6 + minor * 10^3 + micro
|
||||
#define GOOGLE_PROTOBUF_VERSION 3005000
|
||||
#define GOOGLE_PROTOBUF_VERSION 3005001
|
||||
|
||||
// A suffix string for alpha, beta or rc releases. Empty for stable releases.
|
||||
#define GOOGLE_PROTOBUF_VERSION_SUFFIX ""
|
||||
|
@ -30,10 +30,11 @@
|
||||
|
||||
// Author: laszlocsomor@google.com (Laszlo Csomor)
|
||||
//
|
||||
// Implementation for long-path-aware open/mkdir/etc. on Windows.
|
||||
// Implementation for long-path-aware open/mkdir/access/etc. on Windows, as well
|
||||
// as for the supporting utility functions.
|
||||
//
|
||||
// These functions convert the input path to an absolute Windows path
|
||||
// with "\\?\" prefix if necessary, then pass that to _wopen/_wmkdir/etc.
|
||||
// with "\\?\" prefix, then pass that to _wopen/_wmkdir/_waccess/etc.
|
||||
// (declared in <io.h>) respectively. This allows working with files/directories
|
||||
// whose paths are longer than MAX_PATH (260 chars).
|
||||
//
|
||||
@ -59,7 +60,6 @@
|
||||
#include <google/protobuf/stubs/io_win32.h>
|
||||
#include <google/protobuf/stubs/scoped_ptr.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
@ -89,6 +89,11 @@ struct CharTraits<wchar_t> {
|
||||
static bool is_alpha(wchar_t ch) { return iswalpha(ch); }
|
||||
};
|
||||
|
||||
template <typename char_type>
|
||||
bool null_or_empty(const char_type* s) {
|
||||
return s == nullptr || *s == 0;
|
||||
}
|
||||
|
||||
// Returns true if the path starts with a drive letter, e.g. "c:".
|
||||
// Note that this won't check for the "\" after the drive letter, so this also
|
||||
// returns true for "c:foo" (which is "c:\${PWD}\foo").
|
||||
@ -121,16 +126,7 @@ bool is_drive_relative(const char_type* path) {
|
||||
return has_drive_letter(path) && (path[2] == 0 || !is_separator(path[2]));
|
||||
}
|
||||
|
||||
template <typename char_type>
|
||||
void replace_directory_separators(char_type* p) {
|
||||
for (; *p; ++p) {
|
||||
if (*p == '/') {
|
||||
*p = '\\';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string join_paths(const string& path1, const string& path2) {
|
||||
wstring join_paths(const wstring& path1, const wstring& path2) {
|
||||
if (path1.empty() || is_path_absolute(path2.c_str()) ||
|
||||
has_longpath_prefix(path2.c_str())) {
|
||||
return path2;
|
||||
@ -144,24 +140,24 @@ string join_paths(const string& path1, const string& path2) {
|
||||
: (path1 + path2);
|
||||
} else {
|
||||
return is_separator(path2[0]) ? (path1 + path2)
|
||||
: (path1 + '\\' + path2);
|
||||
: (path1 + L'\\' + path2);
|
||||
}
|
||||
}
|
||||
|
||||
string normalize(string path) {
|
||||
wstring normalize(wstring path) {
|
||||
if (has_longpath_prefix(path.c_str())) {
|
||||
path = path.substr(4);
|
||||
}
|
||||
|
||||
static const string dot(".");
|
||||
static const string dotdot("..");
|
||||
const char *p = path.c_str();
|
||||
static const wstring dot(L".");
|
||||
static const wstring dotdot(L"..");
|
||||
const WCHAR* p = path.c_str();
|
||||
|
||||
std::vector<string> segments;
|
||||
std::vector<wstring> segments;
|
||||
int segment_start = -1;
|
||||
// Find the path segments in `path` (separated by "/").
|
||||
for (int i = 0;; ++i) {
|
||||
if (!is_separator(p[i]) && p[i] != '\0') {
|
||||
if (!is_separator(p[i]) && p[i] != L'\0') {
|
||||
// The current character does not end a segment, so start one unless it's
|
||||
// already started.
|
||||
if (segment_start < 0) {
|
||||
@ -170,7 +166,7 @@ string normalize(string path) {
|
||||
} else if (segment_start >= 0 && i > segment_start) {
|
||||
// The current character is "/" or "\0", so this ends a segment.
|
||||
// Add that to `segments` if there's anything to add; handle "." and "..".
|
||||
string segment(p, segment_start, i - segment_start);
|
||||
wstring segment(p, segment_start, i - segment_start);
|
||||
segment_start = -1;
|
||||
if (segment == dotdot) {
|
||||
if (!segments.empty() &&
|
||||
@ -181,7 +177,7 @@ string normalize(string path) {
|
||||
segments.push_back(segment);
|
||||
}
|
||||
}
|
||||
if (p[i] == '\0') {
|
||||
if (p[i] == L'\0') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -190,64 +186,62 @@ string normalize(string path) {
|
||||
// form of it, e.g. "c:\..").
|
||||
if (segments.size() == 1 && segments[0].size() == 2 &&
|
||||
has_drive_letter(segments[0].c_str())) {
|
||||
return segments[0] + '\\';
|
||||
return segments[0] + L'\\';
|
||||
}
|
||||
|
||||
// Join all segments.
|
||||
bool first = true;
|
||||
std::ostringstream result;
|
||||
std::wstringstream result;
|
||||
for (int i = 0; i < segments.size(); ++i) {
|
||||
if (!first) {
|
||||
result << '\\';
|
||||
result << L'\\';
|
||||
}
|
||||
first = false;
|
||||
result << segments[i];
|
||||
}
|
||||
// Preserve trailing separator if the input contained it.
|
||||
if (!path.empty() && is_separator(p[path.size() - 1])) {
|
||||
result << '\\';
|
||||
result << L'\\';
|
||||
}
|
||||
return result.str();
|
||||
}
|
||||
|
||||
WCHAR* as_wstring(const string& s) {
|
||||
int len = ::MultiByteToWideChar(CP_UTF8, 0, s.c_str(), s.size(), NULL, 0);
|
||||
WCHAR* result = new WCHAR[len + 1];
|
||||
::MultiByteToWideChar(CP_UTF8, 0, s.c_str(), s.size(), result, len + 1);
|
||||
result[len] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
void as_wchar_path(const string& path, wstring* wchar_path) {
|
||||
scoped_array<WCHAR> wbuf(as_wstring(path));
|
||||
replace_directory_separators(wbuf.get());
|
||||
wchar_path->assign(wbuf.get());
|
||||
}
|
||||
|
||||
bool as_windows_path(const string& path, wstring* result) {
|
||||
if (path.empty()) {
|
||||
bool as_windows_path(const char* path, wstring* result) {
|
||||
if (null_or_empty(path)) {
|
||||
result->clear();
|
||||
return true;
|
||||
}
|
||||
if (is_separator(path[0]) || is_drive_relative(path.c_str())) {
|
||||
wstring wpath;
|
||||
if (!strings::utf8_to_wcs(path, &wpath)) {
|
||||
return false;
|
||||
}
|
||||
if (has_longpath_prefix(wpath.c_str())) {
|
||||
*result = wpath;
|
||||
return true;
|
||||
}
|
||||
if (is_separator(path[0]) || is_drive_relative(path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string mutable_path = path;
|
||||
if (!is_path_absolute(mutable_path.c_str()) &&
|
||||
!has_longpath_prefix(mutable_path.c_str())) {
|
||||
char cwd[MAX_PATH];
|
||||
::GetCurrentDirectoryA(MAX_PATH, cwd);
|
||||
mutable_path = join_paths(cwd, mutable_path);
|
||||
|
||||
if (!is_path_absolute(wpath.c_str())) {
|
||||
int size = ::GetCurrentDirectoryW(0, NULL);
|
||||
if (size == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
||||
return false;
|
||||
}
|
||||
scoped_array<WCHAR> wcwd(new WCHAR[size]);
|
||||
::GetCurrentDirectoryW(size, wcwd.get());
|
||||
wpath = join_paths(wcwd.get(), wpath);
|
||||
}
|
||||
as_wchar_path(normalize(mutable_path), result);
|
||||
if (!has_longpath_prefix(result->c_str())) {
|
||||
wpath = normalize(wpath);
|
||||
if (!has_longpath_prefix(wpath.c_str())) {
|
||||
// Add the "\\?\" prefix unconditionally. This way we prevent the Win32 API
|
||||
// from processing the path and "helpfully" removing trailing dots from the
|
||||
// path, for example.
|
||||
// See https://github.com/bazelbuild/bazel/issues/2935
|
||||
*result = wstring(L"\\\\?\\") + *result;
|
||||
wpath = wstring(L"\\\\?\\") + wpath;
|
||||
}
|
||||
*result = wpath;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -320,13 +314,21 @@ int stat(const char* path, struct _stat* buffer) {
|
||||
|
||||
FILE* fopen(const char* path, const char* mode) {
|
||||
#ifdef SUPPORT_LONGPATHS
|
||||
if (null_or_empty(path)) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
wstring wpath;
|
||||
if (!as_windows_path(path, &wpath)) {
|
||||
errno = ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
scoped_array<WCHAR> wmode(as_wstring(mode));
|
||||
return ::_wfopen(wpath.c_str(), wmode.get());
|
||||
wstring wmode;
|
||||
if (!strings::utf8_to_wcs(mode, &wmode)) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
return ::_wfopen(wpath.c_str(), wmode.c_str());
|
||||
#else
|
||||
return ::fopen(path, mode);
|
||||
#endif
|
||||
@ -348,16 +350,65 @@ int write(int fd, const void* buffer, size_t size) {
|
||||
return ::_write(fd, buffer, size);
|
||||
}
|
||||
|
||||
wstring testonly_path_to_winpath(const string& path) {
|
||||
wstring testonly_utf8_to_winpath(const char* path) {
|
||||
wstring wpath;
|
||||
as_windows_path(path, &wpath);
|
||||
return wpath;
|
||||
return as_windows_path(path, &wpath) ? wpath : wstring();
|
||||
}
|
||||
|
||||
namespace strings {
|
||||
|
||||
bool wcs_to_mbs(const WCHAR* s, string* out, bool outUtf8) {
|
||||
if (null_or_empty(s)) {
|
||||
out->clear();
|
||||
return true;
|
||||
}
|
||||
BOOL usedDefaultChar = FALSE;
|
||||
SetLastError(0);
|
||||
int size = WideCharToMultiByte(
|
||||
outUtf8 ? CP_UTF8 : CP_ACP, 0, s, -1, NULL, 0, NULL,
|
||||
outUtf8 ? NULL : &usedDefaultChar);
|
||||
if ((size == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||
|| usedDefaultChar) {
|
||||
return false;
|
||||
}
|
||||
scoped_array<CHAR> astr(new CHAR[size]);
|
||||
WideCharToMultiByte(
|
||||
outUtf8 ? CP_UTF8 : CP_ACP, 0, s, -1, astr.get(), size, NULL, NULL);
|
||||
out->assign(astr.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mbs_to_wcs(const char* s, wstring* out, bool inUtf8) {
|
||||
if (null_or_empty(s)) {
|
||||
out->clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
SetLastError(0);
|
||||
int size =
|
||||
MultiByteToWideChar(inUtf8 ? CP_UTF8 : CP_ACP, 0, s, -1, NULL, 0);
|
||||
if (size == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
||||
return false;
|
||||
}
|
||||
scoped_array<WCHAR> wstr(new WCHAR[size]);
|
||||
MultiByteToWideChar(
|
||||
inUtf8 ? CP_UTF8 : CP_ACP, 0, s, -1, wstr.get(), size + 1);
|
||||
out->assign(wstr.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool utf8_to_wcs(const char* input, wstring* out) {
|
||||
return mbs_to_wcs(input, out, true);
|
||||
}
|
||||
|
||||
bool wcs_to_utf8(const wchar_t* input, string* out) {
|
||||
return wcs_to_mbs(input, out, true);
|
||||
}
|
||||
|
||||
} // namespace strings
|
||||
} // namespace win32
|
||||
} // namespace internal
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
||||
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
|
@ -69,8 +69,25 @@ LIBPROTOBUF_EXPORT int read(int fd, void* buffer, size_t size);
|
||||
LIBPROTOBUF_EXPORT int setmode(int fd, int mode);
|
||||
LIBPROTOBUF_EXPORT int stat(const char* path, struct _stat* buffer);
|
||||
LIBPROTOBUF_EXPORT int write(int fd, const void* buffer, size_t size);
|
||||
LIBPROTOBUF_EXPORT std::wstring testonly_path_to_winpath(
|
||||
const std::string& path);
|
||||
LIBPROTOBUF_EXPORT std::wstring testonly_utf8_to_winpath(const char* path);
|
||||
|
||||
namespace strings {
|
||||
|
||||
// Convert from UTF-16 to Active-Code-Page-encoded or to UTF-8-encoded text.
|
||||
LIBPROTOBUF_EXPORT bool wcs_to_mbs(
|
||||
const wchar_t* s, std::string* out, bool outUtf8);
|
||||
|
||||
// Convert from Active-Code-Page-encoded or UTF-8-encoded text to UTF-16.
|
||||
LIBPROTOBUF_EXPORT bool mbs_to_wcs(
|
||||
const char* s, std::wstring* out, bool inUtf8);
|
||||
|
||||
// Convert from UTF-8-encoded text to UTF-16.
|
||||
LIBPROTOBUF_EXPORT bool utf8_to_wcs(const char* input, std::wstring* out);
|
||||
|
||||
// Convert from UTF-16-encoded text to UTF-8.
|
||||
LIBPROTOBUF_EXPORT bool wcs_to_utf8(const wchar_t* input, std::string* out);
|
||||
|
||||
} // namespace strings
|
||||
|
||||
} // namespace win32
|
||||
} // namespace internal
|
||||
|
@ -30,7 +30,8 @@
|
||||
|
||||
// Author: laszlocsomor@google.com (Laszlo Csomor)
|
||||
//
|
||||
// Unit tests for long-path-aware open/mkdir/access on Windows.
|
||||
// Unit tests for long-path-aware open/mkdir/access/etc. on Windows, as well as
|
||||
// for the supporting utility functions.
|
||||
//
|
||||
// This file is only used on Windows, it's empty on other platforms.
|
||||
|
||||
@ -48,7 +49,6 @@
|
||||
|
||||
#include <google/protobuf/stubs/io_win32.h>
|
||||
#include <google/protobuf/stubs/scoped_ptr.h>
|
||||
#include <google/protobuf/testing/googletest.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <memory>
|
||||
@ -61,6 +61,27 @@ namespace internal {
|
||||
namespace win32 {
|
||||
namespace {
|
||||
|
||||
const char kUtf8Text[] = {
|
||||
'h', 'i', ' ',
|
||||
// utf-8: 11010000 10011111, utf-16: 100 0001 1111 = 0x041F
|
||||
0xd0, 0x9f,
|
||||
// utf-8: 11010001 10000000, utf-16: 100 0100 0000 = 0x0440
|
||||
0xd1, 0x80,
|
||||
// utf-8: 11010000 10111000, utf-16: 100 0011 1000 = 0x0438
|
||||
0xd0, 0xb8,
|
||||
// utf-8: 11010000 10110010, utf-16: 100 0011 0010 = 0x0432
|
||||
0xd0, 0xb2,
|
||||
// utf-8: 11010000 10110101, utf-16: 100 0011 0101 = 0x0435
|
||||
0xd0, 0xb5,
|
||||
// utf-8: 11010001 10000010, utf-16: 100 0100 0010 = 0x0442
|
||||
0xd1, 0x82, 0
|
||||
};
|
||||
|
||||
const wchar_t kUtf16Text[] = {
|
||||
L'h', L'i', L' ',
|
||||
L'\x41f', L'\x440', L'\x438', L'\x432', L'\x435', L'\x442', 0
|
||||
};
|
||||
|
||||
using std::string;
|
||||
using std::wstring;
|
||||
|
||||
@ -73,6 +94,7 @@ class IoWin32Test : public ::testing::Test {
|
||||
bool CreateAllUnder(wstring path);
|
||||
bool DeleteAllUnder(wstring path);
|
||||
|
||||
WCHAR working_directory[MAX_PATH];
|
||||
string test_tmpdir;
|
||||
wstring wtest_tmpdir;
|
||||
};
|
||||
@ -89,71 +111,88 @@ void StripTrailingSlashes(string* str) {
|
||||
for (; i >= 0 && ((*str)[i] == '/' || (*str)[i] == '\\'); --i) {}
|
||||
str->resize(i+1);
|
||||
}
|
||||
|
||||
bool GetEnvVarAsUtf8(const WCHAR* name, string* result) {
|
||||
DWORD size = ::GetEnvironmentVariableW(name, NULL, 0);
|
||||
if (size > 0 && GetLastError() != ERROR_ENVVAR_NOT_FOUND) {
|
||||
scoped_array<WCHAR> wcs(new WCHAR[size]);
|
||||
::GetEnvironmentVariableW(name, wcs.get(), size);
|
||||
// GetEnvironmentVariableA retrieves an Active-Code-Page-encoded text which
|
||||
// we'd first need to convert to UTF-16 then to UTF-8, because there seems
|
||||
// to be no API function to do that conversion directly.
|
||||
// GetEnvironmentVariableW retrieves an UTF-16-encoded text, which we need
|
||||
// to convert to UTF-8.
|
||||
return strings::wcs_to_utf8(wcs.get(), result);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool GetCwdAsUtf8(string* result) {
|
||||
DWORD size = ::GetCurrentDirectoryW(0, NULL);
|
||||
if (size > 0) {
|
||||
scoped_array<WCHAR> wcs(new WCHAR[size]);
|
||||
::GetCurrentDirectoryW(size, wcs.get());
|
||||
// GetCurrentDirectoryA retrieves an Active-Code-Page-encoded text which
|
||||
// we'd first need to convert to UTF-16 then to UTF-8, because there seems
|
||||
// to be no API function to do that conversion directly.
|
||||
// GetCurrentDirectoryW retrieves an UTF-16-encoded text, which we need
|
||||
// to convert to UTF-8.
|
||||
return strings::wcs_to_utf8(wcs.get(), result);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void IoWin32Test::SetUp() {
|
||||
test_tmpdir = string(TestTempDir());
|
||||
test_tmpdir.clear();
|
||||
wtest_tmpdir.clear();
|
||||
if (test_tmpdir.empty()) {
|
||||
const char* test_tmpdir_env = getenv("TEST_TMPDIR");
|
||||
if (test_tmpdir_env != NULL && *test_tmpdir_env) {
|
||||
test_tmpdir = string(test_tmpdir_env);
|
||||
}
|
||||
EXPECT_GT(::GetCurrentDirectoryW(MAX_PATH, working_directory), 0);
|
||||
|
||||
// Only Bazel defines TEST_TMPDIR, CMake does not, so look for other
|
||||
// suitable environment variables.
|
||||
if (test_tmpdir.empty()) {
|
||||
static const char* names[] = {"TEMP", "TMP"};
|
||||
for (int i = 0; i < sizeof(names)/sizeof(names[0]); ++i) {
|
||||
const char* name = names[i];
|
||||
test_tmpdir_env = getenv(name);
|
||||
if (test_tmpdir_env != NULL && *test_tmpdir_env) {
|
||||
test_tmpdir = string(test_tmpdir_env);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No other temp directory was found. Use the current director
|
||||
if (test_tmpdir.empty()) {
|
||||
char buffer[MAX_PATH];
|
||||
// Use GetCurrentDirectoryA instead of GetCurrentDirectoryW, because the
|
||||
// current working directory must always be shorter than MAX_PATH, even
|
||||
// with
|
||||
// "\\?\" prefix (except on Windows 10 version 1607 and beyond, after
|
||||
// opting in to long paths by default [1]).
|
||||
//
|
||||
// [1] https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath
|
||||
DWORD result = ::GetCurrentDirectoryA(MAX_PATH, buffer);
|
||||
if (result > 0) {
|
||||
test_tmpdir = string(buffer);
|
||||
} else {
|
||||
// Using assertions in SetUp/TearDown seems to confuse the test
|
||||
// framework, so just leave the member variables empty in case of
|
||||
// failure.
|
||||
GOOGLE_CHECK_OK(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
string tmp;
|
||||
bool ok = false;
|
||||
if (!ok) {
|
||||
// Bazel sets this environment variable when it runs tests.
|
||||
ok = GetEnvVarAsUtf8(L"TEST_TMPDIR", &tmp);
|
||||
}
|
||||
if (!ok) {
|
||||
// Bazel 0.8.0 sets this environment for every build and test action.
|
||||
ok = GetEnvVarAsUtf8(L"TEMP", &tmp);
|
||||
}
|
||||
if (!ok) {
|
||||
// Bazel 0.8.0 sets this environment for every build and test action.
|
||||
ok = GetEnvVarAsUtf8(L"TMP", &tmp);
|
||||
}
|
||||
if (!ok) {
|
||||
// Fall back to using the current directory.
|
||||
ok = GetCwdAsUtf8(&tmp);
|
||||
}
|
||||
if (!ok || tmp.empty()) {
|
||||
FAIL() << "Cannot find a temp directory.";
|
||||
}
|
||||
|
||||
StripTrailingSlashes(&test_tmpdir);
|
||||
test_tmpdir += "\\io_win32_unittest.tmp";
|
||||
|
||||
// CreateDirectoryA's limit is 248 chars, see MSDN.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363855(v=vs.85).aspx
|
||||
wtest_tmpdir = testonly_path_to_winpath(test_tmpdir);
|
||||
if (!DeleteAllUnder(wtest_tmpdir) || !CreateAllUnder(wtest_tmpdir)) {
|
||||
GOOGLE_CHECK_OK(false);
|
||||
test_tmpdir.clear();
|
||||
wtest_tmpdir.clear();
|
||||
}
|
||||
StripTrailingSlashes(&tmp);
|
||||
std::stringstream result;
|
||||
// Deleting files and directories is asynchronous on Windows, and if TearDown
|
||||
// just deleted the previous temp directory, sometimes we cannot recreate the
|
||||
// same directory.
|
||||
// Use a counter so every test method gets its own temp directory.
|
||||
static unsigned int counter = 0;
|
||||
result << tmp << "\\w32tst" << counter++ << ".tmp";
|
||||
test_tmpdir = result.str();
|
||||
wtest_tmpdir = testonly_utf8_to_winpath(test_tmpdir.c_str());
|
||||
ASSERT_FALSE(wtest_tmpdir.empty());
|
||||
ASSERT_TRUE(DeleteAllUnder(wtest_tmpdir));
|
||||
ASSERT_TRUE(CreateAllUnder(wtest_tmpdir));
|
||||
}
|
||||
|
||||
void IoWin32Test::TearDown() {
|
||||
if (!wtest_tmpdir.empty()) {
|
||||
DeleteAllUnder(wtest_tmpdir);
|
||||
}
|
||||
::SetCurrentDirectoryW(working_directory);
|
||||
}
|
||||
|
||||
bool IoWin32Test::CreateAllUnder(wstring path) {
|
||||
@ -191,8 +230,8 @@ bool IoWin32Test::DeleteAllUnder(wstring path) {
|
||||
path = wstring(L"\\\\?\\") + path;
|
||||
}
|
||||
// Append "\" if necessary.
|
||||
if (path[path.size() - 1] != '\\') {
|
||||
path.push_back('\\');
|
||||
if (path[path.size() - 1] != L'\\') {
|
||||
path.push_back(L'\\');
|
||||
}
|
||||
|
||||
WIN32_FIND_DATAW metadata;
|
||||
@ -309,13 +348,24 @@ TEST_F(IoWin32Test, MkdirTest) {
|
||||
ASSERT_EQ(errno, ENOENT);
|
||||
}
|
||||
|
||||
TEST_F(IoWin32Test, MkdirTestNonAscii) {
|
||||
ASSERT_INITIALIZED;
|
||||
|
||||
// Create a non-ASCII path.
|
||||
// Ensure that we can create the directory using SetCurrentDirectoryW.
|
||||
EXPECT_TRUE(CreateDirectoryW((wtest_tmpdir + L"\\1").c_str(), NULL));
|
||||
EXPECT_TRUE(CreateDirectoryW((wtest_tmpdir + L"\\1\\" + kUtf16Text).c_str(), NULL));
|
||||
// Ensure that we can create a very similarly named directory using mkdir.
|
||||
// We don't attemp to delete and recreate the same directory, because on
|
||||
// Windows, deleting files and directories seems to be asynchronous.
|
||||
EXPECT_EQ(mkdir((test_tmpdir + "\\2").c_str(), 0644), 0);
|
||||
EXPECT_EQ(mkdir((test_tmpdir + "\\2\\" + kUtf8Text).c_str(), 0644), 0);
|
||||
}
|
||||
|
||||
TEST_F(IoWin32Test, ChdirTest) {
|
||||
char owd[MAX_PATH];
|
||||
EXPECT_GT(::GetCurrentDirectoryA(MAX_PATH, owd), 0);
|
||||
string path("C:\\");
|
||||
EXPECT_EQ(access(path.c_str(), F_OK), 0);
|
||||
ASSERT_EQ(chdir(path.c_str()), 0);
|
||||
EXPECT_TRUE(::SetCurrentDirectoryA(owd));
|
||||
|
||||
// Do not try to chdir into the test_tmpdir, it may already contain directory
|
||||
// names with trailing dots.
|
||||
@ -330,17 +380,37 @@ TEST_F(IoWin32Test, ChdirTest) {
|
||||
ASSERT_NE(chdir(path.c_str()), 0);
|
||||
}
|
||||
|
||||
TEST_F(IoWin32Test, ChdirTestNonAscii) {
|
||||
ASSERT_INITIALIZED;
|
||||
|
||||
// Create a directory with a non-ASCII path and ensure we can cd into it.
|
||||
wstring wNonAscii(wtest_tmpdir + L"\\" + kUtf16Text);
|
||||
string nonAscii;
|
||||
EXPECT_TRUE(strings::wcs_to_utf8(wNonAscii.c_str(), &nonAscii));
|
||||
EXPECT_TRUE(CreateDirectoryW(wNonAscii.c_str(), NULL));
|
||||
WCHAR cwd[MAX_PATH];
|
||||
EXPECT_TRUE(GetCurrentDirectoryW(MAX_PATH, cwd));
|
||||
// Ensure that we can cd into the path using SetCurrentDirectoryW.
|
||||
EXPECT_TRUE(SetCurrentDirectoryW(wNonAscii.c_str()));
|
||||
EXPECT_TRUE(SetCurrentDirectoryW(cwd));
|
||||
// Ensure that we can cd into the path using chdir.
|
||||
ASSERT_EQ(chdir(nonAscii.c_str()), 0);
|
||||
// Ensure that the GetCurrentDirectoryW returns the desired path.
|
||||
EXPECT_TRUE(GetCurrentDirectoryW(MAX_PATH, cwd));
|
||||
ASSERT_EQ(wNonAscii, cwd);
|
||||
}
|
||||
|
||||
TEST_F(IoWin32Test, AsWindowsPathTest) {
|
||||
DWORD size = GetCurrentDirectoryW(0, NULL);
|
||||
scoped_array<wchar_t> cwd_str(new wchar_t[size]);
|
||||
EXPECT_GT(GetCurrentDirectoryW(size, cwd_str.get()), 0);
|
||||
wstring cwd = wstring(L"\\\\?\\") + cwd_str.get();
|
||||
|
||||
ASSERT_EQ(testonly_path_to_winpath("relative_mkdirtest"),
|
||||
ASSERT_EQ(testonly_utf8_to_winpath("relative_mkdirtest"),
|
||||
cwd + L"\\relative_mkdirtest");
|
||||
ASSERT_EQ(testonly_path_to_winpath("preserve//\\trailing///"),
|
||||
ASSERT_EQ(testonly_utf8_to_winpath("preserve//\\trailing///"),
|
||||
cwd + L"\\preserve\\trailing\\");
|
||||
ASSERT_EQ(testonly_path_to_winpath("./normalize_me\\/../blah"),
|
||||
ASSERT_EQ(testonly_utf8_to_winpath("./normalize_me\\/../blah"),
|
||||
cwd + L"\\blah");
|
||||
std::ostringstream relpath;
|
||||
for (wchar_t* p = cwd_str.get(); *p; ++p) {
|
||||
@ -349,18 +419,28 @@ TEST_F(IoWin32Test, AsWindowsPathTest) {
|
||||
}
|
||||
}
|
||||
relpath << ".\\/../\\./beyond-toplevel";
|
||||
ASSERT_EQ(testonly_path_to_winpath(relpath.str()),
|
||||
ASSERT_EQ(testonly_utf8_to_winpath(relpath.str().c_str()),
|
||||
wstring(L"\\\\?\\") + cwd_str.get()[0] + L":\\beyond-toplevel");
|
||||
|
||||
// Absolute unix paths lack drive letters, driveless absolute windows paths
|
||||
// do too. Neither can be converted to a drive-specifying absolute Windows
|
||||
// path.
|
||||
ASSERT_EQ(testonly_path_to_winpath("/absolute/unix/path"), L"");
|
||||
ASSERT_EQ(testonly_utf8_to_winpath("/absolute/unix/path"), L"");
|
||||
// Though valid on Windows, we also don't support UNC paths (\\UNC\\blah).
|
||||
ASSERT_EQ(testonly_path_to_winpath("\\driveless\\absolute"), L"");
|
||||
ASSERT_EQ(testonly_utf8_to_winpath("\\driveless\\absolute"), L"");
|
||||
// Though valid in cmd.exe, drive-relative paths are not supported.
|
||||
ASSERT_EQ(testonly_path_to_winpath("c:foo"), L"");
|
||||
ASSERT_EQ(testonly_path_to_winpath("c:/foo"), L"\\\\?\\c:\\foo");
|
||||
ASSERT_EQ(testonly_utf8_to_winpath("c:foo"), L"");
|
||||
ASSERT_EQ(testonly_utf8_to_winpath("c:/foo"), L"\\\\?\\c:\\foo");
|
||||
ASSERT_EQ(testonly_utf8_to_winpath("\\\\?\\C:\\foo"), L"\\\\?\\C:\\foo");
|
||||
}
|
||||
|
||||
TEST_F(IoWin32Test, Utf8Utf16ConversionTest) {
|
||||
string mbs;
|
||||
wstring wcs;
|
||||
ASSERT_TRUE(strings::utf8_to_wcs(kUtf8Text, &wcs));
|
||||
ASSERT_TRUE(strings::wcs_to_utf8(kUtf16Text, &mbs));
|
||||
ASSERT_EQ(wcs, kUtf16Text);
|
||||
ASSERT_EQ(mbs, kUtf8Text);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -132,7 +132,7 @@ string GetTemporaryDirectoryName() {
|
||||
// with "\\?\") but these will be degenerate in the sense that you cannot
|
||||
// chdir into such directories (or navigate into them with Windows Explorer)
|
||||
// nor can you open such files with some programs (e.g. Notepad).
|
||||
if (result.back() == '.') {
|
||||
if (result[result.size() - 1] == '.') {
|
||||
result[result.size() - 1] = '_';
|
||||
}
|
||||
// On Win32, tmpnam() returns a file prefixed with '\', but which is supposed
|
||||
|
@ -13,7 +13,7 @@
|
||||
#error incompatible with your Protocol Buffer headers. Please update
|
||||
#error your headers.
|
||||
#endif
|
||||
#if 3005000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#if 3005001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#error This file was generated by an older version of protoc which is
|
||||
#error incompatible with your Protocol Buffer headers. Please
|
||||
#error regenerate this file with a newer version of protoc.
|
||||
|
@ -13,7 +13,7 @@
|
||||
#error incompatible with your Protocol Buffer headers. Please update
|
||||
#error your headers.
|
||||
#endif
|
||||
#if 3005000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#if 3005001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#error This file was generated by an older version of protoc which is
|
||||
#error incompatible with your Protocol Buffer headers. Please
|
||||
#error regenerate this file with a newer version of protoc.
|
||||
|
@ -374,7 +374,7 @@ void FieldMaskTree::RemovePath(const string& path,
|
||||
}
|
||||
}
|
||||
if (ContainsKey(node->children, parts[i])) {
|
||||
node = node->children.at(parts[i]);
|
||||
node = node->children[parts[i]];
|
||||
if (field_descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
|
||||
current_descriptor = field_descriptor->message_type();
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
#error incompatible with your Protocol Buffer headers. Please update
|
||||
#error your headers.
|
||||
#endif
|
||||
#if 3005000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#if 3005001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#error This file was generated by an older version of protoc which is
|
||||
#error incompatible with your Protocol Buffer headers. Please
|
||||
#error regenerate this file with a newer version of protoc.
|
||||
|
Loading…
Reference in New Issue
Block a user