Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cherry pick arenastring and PHP API #10168

Merged
merged 2 commits into from
Jun 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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