Skip to content

Commit

Permalink
Merge pull request #10168 from esorot/21.x
Browse files Browse the repository at this point in the history
Cherry pick arenastring and PHP API
  • Loading branch information
esorot committed Jun 23, 2022
2 parents 55fe00e + 71adb83 commit 549748d
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 33 deletions.
35 changes: 27 additions & 8 deletions php/src/Google/Protobuf/FieldDescriptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class FieldDescriptor
{
use GetPublicDescriptorTrait;

/** @var \Google\Protobuf\Internal\FieldDescriptor $internal_desc */
private $internal_desc;

/**
Expand Down Expand Up @@ -81,6 +82,32 @@ public function getType()
return $this->internal_desc->getType();
}

/**
* @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
Expand Down Expand Up @@ -114,12 +141,4 @@ public function isMap()
{
return $this->internal_desc->isMap();
}

/**
* @return boolean
*/
public function hasOptionalKeyword()
{
return $this->internal_desc->hasOptionalKeyword();
}
}
38 changes: 35 additions & 3 deletions php/src/Google/Protobuf/Internal/FieldDescriptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,11 @@ class FieldDescriptor
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()
{
Expand Down Expand Up @@ -169,6 +172,32 @@ public function getPacked()
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);
Expand Down Expand Up @@ -214,6 +243,10 @@ private static function isTypePackable($field_type)
$field_type !== GPBType::BYTES);
}

/**
* @param FieldDescriptorProto $proto
* @return FieldDescriptor
*/
public static function getFieldDescriptor($proto)
{
$type_name = null;
Expand Down Expand Up @@ -248,8 +281,6 @@ public static function getFieldDescriptor($proto)
$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 {
Expand All @@ -269,6 +300,7 @@ public static function getFieldDescriptor($proto)
$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
Expand Down
9 changes: 9 additions & 0 deletions php/src/Google/Protobuf/Internal/OneofDescriptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class OneofDescriptor
use HasPublicDescriptorTrait;

private $name;
/** @var \Google\Protobuf\FieldDescriptor[] $fields */
private $fields;

public function __construct()
Expand Down Expand Up @@ -64,13 +65,21 @@ public function getFields()
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;
Expand Down
9 changes: 8 additions & 1 deletion php/src/Google/Protobuf/OneofDescriptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class OneofDescriptor
{
use GetPublicDescriptorTrait;

/** @var \Google\Protobuf\Internal\OneofDescriptor $internal_desc */
private $internal_desc;

/**
Expand All @@ -62,6 +63,12 @@ public function getName()
*/
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]);
}

Expand All @@ -75,6 +82,6 @@ public function getFieldCount()

public function isSynthetic()
{
return $this->internal_desc->isSynthetic();
return $this->internal_desc->isSynthetic();
}
}
21 changes: 14 additions & 7 deletions src/google/protobuf/arena_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ namespace google {
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);
Expand Down Expand Up @@ -497,10 +504,10 @@ class PROTOBUF_EXPORT ThreadSafeArena {
// 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;
}
Expand Down Expand Up @@ -564,7 +571,7 @@ class PROTOBUF_EXPORT ThreadSafeArena {
// 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;
}
Expand Down Expand Up @@ -602,15 +609,15 @@ class PROTOBUF_EXPORT ThreadSafeArena {
#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
// initializing it.
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.
Expand All @@ -633,7 +640,7 @@ class PROTOBUF_EXPORT ThreadSafeArena {
#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_;
Expand Down
6 changes: 2 additions & 4 deletions src/google/protobuf/arenastring.cc
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ std::string* ArenaStringPtr::Release() {
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);
}
Expand Down Expand Up @@ -216,9 +216,7 @@ void ArenaStringPtr::SetAllocated(std::string* value, Arena* arena) {
}

void ArenaStringPtr::Destroy() {
if (tagged_ptr_.IsAllocated()) {
delete tagged_ptr_.Get();
}
delete tagged_ptr_.GetIfAllocated();
}

void ArenaStringPtr::ClearToEmpty() {
Expand Down
27 changes: 17 additions & 10 deletions src/google/protobuf/arenastring.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,12 @@ class PROTOBUF_EXPORT LazyString {

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
Expand All @@ -112,7 +111,7 @@ class TaggedStringPtr {

// 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
Expand Down Expand Up @@ -166,8 +165,16 @@ class TaggedStringPtr {
// 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.
Expand Down Expand Up @@ -224,8 +231,8 @@ static_assert(std::is_trivial<TaggedStringPtr>::value,
// 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,
Expand Down

0 comments on commit 549748d

Please sign in to comment.