Merge remote-tracking branch 'origin/3.5.x' into master

This commit is contained in:
Jisi Liu 2018-01-03 09:28:40 -08:00
commit 383a4941d5
54 changed files with 851 additions and 213 deletions

View File

@ -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

View File

@ -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 \

View File

@ -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'

View File

@ -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])

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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": [

View File

@ -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);

View File

@ -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);
}

View File

@ -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, &timestamp);
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
}
// -----------------------------------------------------------------------------

View File

@ -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>

View File

@ -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

View File

@ -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));

View File

@ -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.",

View File

@ -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());
}
}

View File

@ -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()

View File

@ -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);

View File

@ -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>

View File

@ -30,7 +30,7 @@
# Copyright 2007 Google Inc. All Rights Reserved.
__version__ = '3.5.0.post1'
__version__ = '3.5.1'
if __name__ != '__main__':
try:

View File

@ -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

View File

@ -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",

View File

@ -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;
}
/*

View File

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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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."

View 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

View File

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

View File

@ -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/$@

View File

@ -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.

View File

@ -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.

View File

@ -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;

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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 {

View File

@ -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.

View File

@ -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.

View File

@ -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 ""

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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();
}

View File

@ -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.