Merge pull request #10168 from esorot/21.x
Cherry pick arenastring and PHP API
diff --git a/php/src/Google/Protobuf/FieldDescriptor.php b/php/src/Google/Protobuf/FieldDescriptor.php
index 6d08cea..ac919a2 100644
--- a/php/src/Google/Protobuf/FieldDescriptor.php
+++ b/php/src/Google/Protobuf/FieldDescriptor.php
@@ -39,6 +39,7 @@
{
use GetPublicDescriptorTrait;
+ /** @var \Google\Protobuf\Internal\FieldDescriptor $internal_desc */
private $internal_desc;
/**
@@ -82,6 +83,32 @@
}
/**
+ * @return OneofDescriptor
+ */
+ public function getContainingOneof()
+ {
+ return $this->getPublicDescriptor($this->internal_desc->getContainingOneof());
+ }
+
+ /**
+ * Gets the field's containing oneof, only if non-synthetic.
+ *
+ * @return null|OneofDescriptor
+ */
+ public function getRealContainingOneof()
+ {
+ return $this->getPublicDescriptor($this->internal_desc->getRealContainingOneof());
+ }
+
+ /**
+ * @return boolean
+ */
+ public function hasOptionalKeyword()
+ {
+ return $this->internal_desc->hasOptionalKeyword();
+ }
+
+ /**
* @return Descriptor Returns a descriptor for the field type if the field type is a message, otherwise throws \Exception
* @throws \Exception
*/
@@ -114,12 +141,4 @@
{
return $this->internal_desc->isMap();
}
-
- /**
- * @return boolean
- */
- public function hasOptionalKeyword()
- {
- return $this->internal_desc->hasOptionalKeyword();
- }
}
diff --git a/php/src/Google/Protobuf/Internal/FieldDescriptor.php b/php/src/Google/Protobuf/Internal/FieldDescriptor.php
index ce83f63..3a9a73b 100644
--- a/php/src/Google/Protobuf/Internal/FieldDescriptor.php
+++ b/php/src/Google/Protobuf/Internal/FieldDescriptor.php
@@ -46,8 +46,11 @@
private $message_type;
private $enum_type;
private $packed;
- private $is_map;
private $oneof_index = -1;
+ private $proto3_optional;
+
+ /** @var OneofDescriptor $containing_oneof */
+ private $containing_oneof;
public function __construct()
{
@@ -169,6 +172,32 @@
return $this->packed;
}
+ public function getProto3Optional()
+ {
+ return $this->proto3_optional;
+ }
+
+ public function setProto3Optional($proto3_optional)
+ {
+ $this->proto3_optional = $proto3_optional;
+ }
+
+ public function getContainingOneof()
+ {
+ return $this->containing_oneof;
+ }
+
+ public function setContainingOneof($containing_oneof)
+ {
+ $this->containing_oneof = $containing_oneof;
+ }
+
+ public function getRealContainingOneof()
+ {
+ return !is_null($this->containing_oneof) && !$this->containing_oneof->isSynthetic()
+ ? $this->containing_oneof : null;
+ }
+
public function isPackable()
{
return $this->isRepeated() && self::isTypePackable($this->type);
@@ -214,6 +243,10 @@
$field_type !== GPBType::BYTES);
}
+ /**
+ * @param FieldDescriptorProto $proto
+ * @return FieldDescriptor
+ */
public static function getFieldDescriptor($proto)
{
$type_name = null;
@@ -248,8 +281,6 @@
$field = new FieldDescriptor();
$field->setName($proto->getName());
- $json_name = $proto->hasJsonName() ? $proto->getJsonName() :
- lcfirst(implode('', array_map('ucwords', explode('_', $proto->getName()))));
if ($proto->hasJsonName()) {
$json_name = $proto->getJsonName();
} else {
@@ -269,6 +300,7 @@
$field->setLabel($proto->getLabel());
$field->setPacked($packed);
$field->setOneofIndex($oneof_index);
+ $field->setProto3Optional($proto->getProto3Optional());
// At this time, the message/enum type may have not been added to pool.
// So we use the type name as place holder and will replace it with the
diff --git a/php/src/Google/Protobuf/Internal/OneofDescriptor.php b/php/src/Google/Protobuf/Internal/OneofDescriptor.php
index 67b107f..4323685 100644
--- a/php/src/Google/Protobuf/Internal/OneofDescriptor.php
+++ b/php/src/Google/Protobuf/Internal/OneofDescriptor.php
@@ -37,6 +37,7 @@
use HasPublicDescriptorTrait;
private $name;
+ /** @var \Google\Protobuf\FieldDescriptor[] $fields */
private $fields;
public function __construct()
@@ -64,13 +65,21 @@
return $this->fields;
}
+ public function isSynthetic()
+ {
+ return !is_null($this->fields) && count($this->fields) === 1
+ && $this->fields[0]->getProto3Optional();
+ }
+
public static function buildFromProto($oneof_proto, $desc, $index)
{
$oneof = new OneofDescriptor();
$oneof->setName($oneof_proto->getName());
foreach ($desc->getField() as $field) {
+ /** @var FieldDescriptor $field */
if ($field->getOneofIndex() == $index) {
$oneof->addField($field);
+ $field->setContainingOneof($oneof);
}
}
return $oneof;
diff --git a/php/src/Google/Protobuf/OneofDescriptor.php b/php/src/Google/Protobuf/OneofDescriptor.php
index 92b4e27..66ffbd5 100644
--- a/php/src/Google/Protobuf/OneofDescriptor.php
+++ b/php/src/Google/Protobuf/OneofDescriptor.php
@@ -38,6 +38,7 @@
{
use GetPublicDescriptorTrait;
+ /** @var \Google\Protobuf\Internal\OneofDescriptor $internal_desc */
private $internal_desc;
/**
@@ -62,6 +63,12 @@
*/
public function getField($index)
{
+ if (
+ is_null($this->internal_desc->getFields())
+ || !isset($this->internal_desc->getFields()[$index])
+ ) {
+ return null;
+ }
return $this->getPublicDescriptor($this->internal_desc->getFields()[$index]);
}
@@ -75,6 +82,6 @@
public function isSynthetic()
{
- return $this->internal_desc->isSynthetic();
+ return $this->internal_desc->isSynthetic();
}
}
diff --git a/src/google/protobuf/arena_impl.h b/src/google/protobuf/arena_impl.h
index d02ad93..7672768 100644
--- a/src/google/protobuf/arena_impl.h
+++ b/src/google/protobuf/arena_impl.h
@@ -55,6 +55,13 @@
namespace protobuf {
namespace internal {
+// To prevent sharing cache lines between threads
+#ifdef __cpp_aligned_new
+enum { kCacheAlignment = 64 };
+#else
+enum { kCacheAlignment = alignof(max_align_t) }; // do the best we can
+#endif
+
inline constexpr size_t AlignUpTo8(size_t n) {
// Align n to next multiple of 8 (from Hacker's Delight, Chapter 3.)
return (n + 7) & static_cast<size_t>(-8);
@@ -497,10 +504,10 @@
// have fallback function calls in tail position. This substantially improves
// code for the happy path.
PROTOBUF_NDEBUG_INLINE bool MaybeAllocateAligned(size_t n, void** out) {
- SerialArena* a;
+ SerialArena* arena;
if (PROTOBUF_PREDICT_TRUE(!alloc_policy_.should_record_allocs() &&
- GetSerialArenaFromThreadCache(&a))) {
- return a->MaybeAllocateAligned(n, out);
+ GetSerialArenaFromThreadCache(&arena))) {
+ return arena->MaybeAllocateAligned(n, out);
}
return false;
}
@@ -564,7 +571,7 @@
// fast path optimizes the case where a single thread uses multiple arenas.
ThreadCache* tc = &thread_cache();
SerialArena* serial = hint_.load(std::memory_order_acquire);
- if (PROTOBUF_PREDICT_TRUE(serial != NULL && serial->owner() == tc)) {
+ if (PROTOBUF_PREDICT_TRUE(serial != nullptr && serial->owner() == tc)) {
*arena = serial;
return true;
}
@@ -602,7 +609,7 @@
#ifdef _MSC_VER
#pragma warning(disable : 4324)
#endif
- struct alignas(64) ThreadCache {
+ struct alignas(kCacheAlignment) ThreadCache {
#if defined(GOOGLE_PROTOBUF_NO_THREADLOCAL)
// If we are using the ThreadLocalStorage class to store the ThreadCache,
// then the ThreadCache's default constructor has to be responsible for
@@ -610,7 +617,7 @@
ThreadCache()
: next_lifecycle_id(0),
last_lifecycle_id_seen(-1),
- last_serial_arena(NULL) {}
+ last_serial_arena(nullptr) {}
#endif
// Number of per-thread lifecycle IDs to reserve. Must be power of two.
@@ -633,7 +640,7 @@
#ifdef _MSC_VER
#pragma warning(disable : 4324)
#endif
- struct alignas(64) CacheAlignedLifecycleIdGenerator {
+ struct alignas(kCacheAlignment) CacheAlignedLifecycleIdGenerator {
std::atomic<LifecycleIdAtomic> id;
};
static CacheAlignedLifecycleIdGenerator lifecycle_id_generator_;
diff --git a/src/google/protobuf/arenastring.cc b/src/google/protobuf/arenastring.cc
index d886dda..3887c94 100644
--- a/src/google/protobuf/arenastring.cc
+++ b/src/google/protobuf/arenastring.cc
@@ -187,7 +187,7 @@
if (IsDefault()) return nullptr;
std::string* released = tagged_ptr_.Get();
- if (!tagged_ptr_.IsAllocated()) {
+ if (tagged_ptr_.IsArena()) {
released = tagged_ptr_.IsMutable() ? new std::string(std::move(*released))
: new std::string(*released);
}
@@ -216,9 +216,7 @@
}
void ArenaStringPtr::Destroy() {
- if (tagged_ptr_.IsAllocated()) {
- delete tagged_ptr_.Get();
- }
+ delete tagged_ptr_.GetIfAllocated();
}
void ArenaStringPtr::ClearToEmpty() {
diff --git a/src/google/protobuf/arenastring.h b/src/google/protobuf/arenastring.h
index b8d31fe..6bc8395 100644
--- a/src/google/protobuf/arenastring.h
+++ b/src/google/protobuf/arenastring.h
@@ -96,13 +96,12 @@
class TaggedStringPtr {
public:
- // Bit flags qualifying string properties. We can use up to 3 bits as
- // ptr_ is guaranteed and enforced to be aligned on 8 byte boundaries.
+ // Bit flags qualifying string properties. We can use 2 bits as
+ // ptr_ is guaranteed and enforced to be aligned on 4 byte boundaries.
enum Flags {
kArenaBit = 0x1, // ptr is arena allocated
- kAllocatedBit = 0x2, // ptr is heap allocated
- kMutableBit = 0x4, // ptr contents are fully mutable
- kMask = 0x7 // Bit mask
+ kMutableBit = 0x2, // ptr contents are fully mutable
+ kMask = 0x3 // Bit mask
};
// Composed logical types
@@ -112,7 +111,7 @@
// Allocated strings are mutable and (as the name implies) owned.
// A heap allocated string must be deleted.
- kAllocated = kAllocatedBit | kMutableBit,
+ kAllocated = kMutableBit,
// Mutable arena strings are strings where the string instance is owned
// by the arena, but the string contents itself are owned by the string
@@ -166,8 +165,16 @@
// Returns true if the current string is an immutable default value.
inline bool IsDefault() const { return (as_int() & kMask) == kDefault; }
- // Returns true if the current string is a heap allocated mutable value.
- inline bool IsAllocated() const { return as_int() & kAllocatedBit; }
+ // If the current string is a heap-allocated mutable value, returns a pointer
+ // to it. Returns nullptr otherwise.
+ inline std::string *GetIfAllocated() const {
+ auto allocated = as_int() ^ kAllocated;
+ if (allocated & kMask) return nullptr;
+
+ auto ptr = reinterpret_cast<std::string*>(allocated);
+ PROTOBUF_ASSUME(ptr != nullptr);
+ return ptr;
+ }
// Returns true if the current string is an arena allocated value.
// This means it's either a mutable or fixed size arena string.
@@ -224,8 +231,8 @@
// Because ArenaStringPtr is used in oneof unions, its constructor is a NOP and
// the field is always manually initialized via method calls.
//
-// See TaggedPtr for more information about the types of string values being
-// held, and the mutable and ownership invariants for each type.
+// See TaggedStringPtr for more information about the types of string values
+// being held, and the mutable and ownership invariants for each type.
struct PROTOBUF_EXPORT ArenaStringPtr {
ArenaStringPtr() = default;
constexpr ArenaStringPtr(ExplicitlyConstructedArenaString* default_value,