|
|
|
@ -66,6 +66,7 @@ |
|
|
|
|
#include <type_traits> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Must be included last.
|
|
|
|
|
#include <google/protobuf/port_def.inc> |
|
|
|
|
|
|
|
|
|
#ifdef SWIG |
|
|
|
@ -85,7 +86,16 @@ namespace internal { |
|
|
|
|
|
|
|
|
|
class MergePartialFromCodedStreamHelper; |
|
|
|
|
|
|
|
|
|
static const int kMinRepeatedFieldAllocationSize = 4; |
|
|
|
|
// kRepeatedFieldLowerClampLimit is the smallest size that will be allocated
|
|
|
|
|
// when growing a repeated field.
|
|
|
|
|
constexpr int kRepeatedFieldLowerClampLimit = 4; |
|
|
|
|
|
|
|
|
|
// kRepeatedFieldUpperClampLimit is the lowest signed integer value that
|
|
|
|
|
// overflows when multiplied by 2 (which is undefined behavior). Sizes above
|
|
|
|
|
// this will clamp to the maximum int value instead of following exponential
|
|
|
|
|
// growth when growing a repeated field.
|
|
|
|
|
constexpr int kRepeatedFieldUpperClampLimit = |
|
|
|
|
(std::numeric_limits<int>::max() / 2) + 1; |
|
|
|
|
|
|
|
|
|
// A utility function for logging that doesn't need any template types.
|
|
|
|
|
void LogIndexOutOfBounds(int index, int size); |
|
|
|
@ -309,7 +319,7 @@ class RepeatedField final { |
|
|
|
|
inline void InternalSwap(RepeatedField* other); |
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
static const int kInitialSize = 0; |
|
|
|
|
static constexpr int kInitialSize = 0; |
|
|
|
|
// A note on the representation here (see also comment below for
|
|
|
|
|
// RepeatedPtrFieldBase's struct Rep):
|
|
|
|
|
//
|
|
|
|
@ -390,6 +400,84 @@ class RepeatedField final { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// This class is a performance wrapper around RepeatedField::Add(const T&)
|
|
|
|
|
// function. In general unless a RepeatedField is a local stack variable LLVM
|
|
|
|
|
// has a hard time optimizing Add. The machine code tends to be
|
|
|
|
|
// loop:
|
|
|
|
|
// mov %size, dword ptr [%repeated_field] // load
|
|
|
|
|
// cmp %size, dword ptr [%repeated_field + 4]
|
|
|
|
|
// jae fallback
|
|
|
|
|
// mov %buffer, qword ptr [%repeated_field + 8]
|
|
|
|
|
// mov dword [%buffer + %size * 4], %value
|
|
|
|
|
// inc %size // increment
|
|
|
|
|
// mov dword ptr [%repeated_field], %size // store
|
|
|
|
|
// jmp loop
|
|
|
|
|
//
|
|
|
|
|
// This puts a load/store in each iteration of the important loop variable
|
|
|
|
|
// size. It's a pretty bad compile that happens even in simple cases, but
|
|
|
|
|
// largely the presence of the fallback path disturbs the compilers mem-to-reg
|
|
|
|
|
// analysis.
|
|
|
|
|
//
|
|
|
|
|
// This class takes ownership of a repeated field for the duration of it's
|
|
|
|
|
// lifetime. The repeated field should not be accessed during this time, ie.
|
|
|
|
|
// only access through this class is allowed. This class should always be a
|
|
|
|
|
// function local stack variable. Intended use
|
|
|
|
|
//
|
|
|
|
|
// void AddSequence(const int* begin, const int* end, RepeatedField<int>* out)
|
|
|
|
|
// {
|
|
|
|
|
// RepeatedFieldAdder<int> adder(out); // Take ownership of out
|
|
|
|
|
// for (auto it = begin; it != end; ++it) {
|
|
|
|
|
// adder.Add(*it);
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// Typically due to the fact adder is a local stack variable. The compiler
|
|
|
|
|
// will be successful in mem-to-reg transformation and the machine code will
|
|
|
|
|
// be loop: cmp %size, %capacity jae fallback mov dword ptr [%buffer + %size *
|
|
|
|
|
// 4], %val inc %size jmp loop
|
|
|
|
|
//
|
|
|
|
|
// The first version executes at 7 cycles per iteration while the second
|
|
|
|
|
// version near 1 or 2 cycles.
|
|
|
|
|
class FastAdder { |
|
|
|
|
public: |
|
|
|
|
explicit FastAdder(RepeatedField* rf) : repeated_field_(rf) { |
|
|
|
|
if (kIsPod) { |
|
|
|
|
index_ = repeated_field_->current_size_; |
|
|
|
|
capacity_ = repeated_field_->total_size_; |
|
|
|
|
buffer_ = repeated_field_->unsafe_elements(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
~FastAdder() { |
|
|
|
|
if (kIsPod) repeated_field_->current_size_ = index_; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void Add(const Element& val) { |
|
|
|
|
if (kIsPod) { |
|
|
|
|
if (index_ == capacity_) { |
|
|
|
|
repeated_field_->current_size_ = index_; |
|
|
|
|
repeated_field_->Reserve(index_ + 1); |
|
|
|
|
capacity_ = repeated_field_->total_size_; |
|
|
|
|
buffer_ = repeated_field_->unsafe_elements(); |
|
|
|
|
} |
|
|
|
|
buffer_[index_++] = val; |
|
|
|
|
} else { |
|
|
|
|
repeated_field_->Add(val); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
constexpr static bool kIsPod = std::is_pod<Element>::value; |
|
|
|
|
RepeatedField* repeated_field_; |
|
|
|
|
int index_; |
|
|
|
|
int capacity_; |
|
|
|
|
Element* buffer_; |
|
|
|
|
|
|
|
|
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FastAdder); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
friend class TestRepeatedFieldHelper; |
|
|
|
|
friend class ::google::protobuf::internal::ParseContext; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
template <typename Element> |
|
|
|
@ -629,7 +717,7 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase { |
|
|
|
|
inline Arena* GetArena() const { return arena_; } |
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
static const int kInitialSize = 0; |
|
|
|
|
static constexpr int kInitialSize = 0; |
|
|
|
|
// A few notes on internal representation:
|
|
|
|
|
//
|
|
|
|
|
// We use an indirected approach, with struct Rep, to keep
|
|
|
|
@ -648,7 +736,7 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase { |
|
|
|
|
int allocated_size; |
|
|
|
|
void* elements[1]; |
|
|
|
|
}; |
|
|
|
|
static const size_t kRepHeaderSize = sizeof(Rep) - sizeof(void*); |
|
|
|
|
static constexpr size_t kRepHeaderSize = sizeof(Rep) - sizeof(void*); |
|
|
|
|
Rep* rep_; |
|
|
|
|
|
|
|
|
|
template <typename TypeHandler> |
|
|
|
@ -1240,14 +1328,19 @@ inline void RepeatedField<Element>::Set(int index, const Element& value) { |
|
|
|
|
|
|
|
|
|
template <typename Element> |
|
|
|
|
inline void RepeatedField<Element>::Add(const Element& value) { |
|
|
|
|
if (current_size_ == total_size_) Reserve(total_size_ + 1); |
|
|
|
|
elements()[current_size_++] = value; |
|
|
|
|
uint32 size = current_size_; |
|
|
|
|
if (static_cast<int>(size) == total_size_) Reserve(total_size_ + 1); |
|
|
|
|
elements()[size] = value; |
|
|
|
|
current_size_ = size + 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template <typename Element> |
|
|
|
|
inline Element* RepeatedField<Element>::Add() { |
|
|
|
|
if (current_size_ == total_size_) Reserve(total_size_ + 1); |
|
|
|
|
return &elements()[current_size_++]; |
|
|
|
|
uint32 size = current_size_; |
|
|
|
|
if (static_cast<int>(size) == total_size_) Reserve(total_size_ + 1); |
|
|
|
|
auto ptr = &elements()[size]; |
|
|
|
|
current_size_ = size + 1; |
|
|
|
|
return ptr; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template <typename Element> |
|
|
|
@ -1269,9 +1362,8 @@ inline void RepeatedField<Element>::Add(Iter begin, Iter end) { |
|
|
|
|
std::copy(begin, end, elements() + size()); |
|
|
|
|
current_size_ = reserve + size(); |
|
|
|
|
} else { |
|
|
|
|
for (; begin != end; ++begin) { |
|
|
|
|
Add(*begin); |
|
|
|
|
} |
|
|
|
|
FastAdder fast_adder(this); |
|
|
|
|
for (; begin != end; ++begin) fast_adder.Add(*begin); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1425,6 +1517,30 @@ inline size_t RepeatedField<Element>::SpaceUsedExcludingSelfLong() const { |
|
|
|
|
return total_size_ > 0 ? (total_size_ * sizeof(Element) + kRepHeaderSize) : 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
namespace internal { |
|
|
|
|
// Returns the new size for a reserved field based on its 'total_size' and the
|
|
|
|
|
// requested 'new_size'. The result is clamped to the closed interval:
|
|
|
|
|
// [internal::kMinRepeatedFieldAllocationSize,
|
|
|
|
|
// std::numeric_limits<int>::max()]
|
|
|
|
|
// Requires:
|
|
|
|
|
// new_size > total_size &&
|
|
|
|
|
// (total_size == 0 ||
|
|
|
|
|
// total_size >= kRepeatedFieldLowerClampLimit)
|
|
|
|
|
inline int CalculateReserveSize(int total_size, int new_size) { |
|
|
|
|
if (new_size < kRepeatedFieldLowerClampLimit) { |
|
|
|
|
// Clamp to smallest allowed size.
|
|
|
|
|
return kRepeatedFieldLowerClampLimit; |
|
|
|
|
} |
|
|
|
|
if (total_size < kRepeatedFieldUpperClampLimit) { |
|
|
|
|
return std::max(total_size * 2, new_size); |
|
|
|
|
} else { |
|
|
|
|
// Clamp to largest allowed size.
|
|
|
|
|
GOOGLE_DCHECK_GT(new_size, kRepeatedFieldUpperClampLimit); |
|
|
|
|
return std::numeric_limits<int>::max(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} // namespace internal
|
|
|
|
|
|
|
|
|
|
// Avoid inlining of Reserve(): new, copy, and delete[] lead to a significant
|
|
|
|
|
// amount of code bloat.
|
|
|
|
|
template <typename Element> |
|
|
|
@ -1433,8 +1549,7 @@ void RepeatedField<Element>::Reserve(int new_size) { |
|
|
|
|
Rep* old_rep = total_size_ > 0 ? rep() : NULL; |
|
|
|
|
Rep* new_rep; |
|
|
|
|
Arena* arena = GetArena(); |
|
|
|
|
new_size = std::max(internal::kMinRepeatedFieldAllocationSize, |
|
|
|
|
std::max(total_size_ * 2, new_size)); |
|
|
|
|
new_size = internal::CalculateReserveSize(total_size_, new_size); |
|
|
|
|
GOOGLE_DCHECK_LE( |
|
|
|
|
static_cast<size_t>(new_size), |
|
|
|
|
(std::numeric_limits<size_t>::max() - kRepHeaderSize) / sizeof(Element)) |
|
|
|
@ -1448,6 +1563,10 @@ void RepeatedField<Element>::Reserve(int new_size) { |
|
|
|
|
} |
|
|
|
|
new_rep->arena = arena; |
|
|
|
|
int old_total_size = total_size_; |
|
|
|
|
// Already known: new_size >= internal::kMinRepeatedFieldAllocationSize
|
|
|
|
|
// Maintain invariant:
|
|
|
|
|
// total_size_ == 0 ||
|
|
|
|
|
// total_size_ >= internal::kMinRepeatedFieldAllocationSize
|
|
|
|
|
total_size_ = new_size; |
|
|
|
|
arena_or_elements_ = new_rep->elements; |
|
|
|
|
// Invoke placement-new on newly allocated elements. We shouldn't have to do
|
|
|
|
|