Index: third_party/protobuf/src/google/protobuf/map_entry_lite.h |
diff --git a/third_party/protobuf/src/google/protobuf/map_entry_lite.h b/third_party/protobuf/src/google/protobuf/map_entry_lite.h |
index 7cdf1b9379d1bcbdd034efbfe6d3a6b6681a5a65..bb1d7e06b5ecf9619aa7b8e6d28b8f8f51cdcd47 100644 |
--- a/third_party/protobuf/src/google/protobuf/map_entry_lite.h |
+++ b/third_party/protobuf/src/google/protobuf/map_entry_lite.h |
@@ -31,6 +31,7 @@ |
#ifndef GOOGLE_PROTOBUF_MAP_ENTRY_LITE_H__ |
#define GOOGLE_PROTOBUF_MAP_ENTRY_LITE_H__ |
+#include <assert.h> |
#include <google/protobuf/map_type_handler.h> |
#include <google/protobuf/wire_format_lite_inl.h> |
@@ -54,6 +55,38 @@ class MapFieldLite; |
namespace protobuf { |
namespace internal { |
+// MoveHelper::Move is used to set *dest. It copies *src, or moves it (in |
+// the C++11 sense), or swaps it. *src is left in a sane state for |
+// subsequent destruction, but shouldn't be used for anything. |
+template <bool is_enum, bool is_message, bool is_stringlike, typename T> |
+struct MoveHelper { // primitives |
+ static void Move(T* src, T* dest) { *dest = *src; } |
+}; |
+ |
+template <bool is_message, bool is_stringlike, typename T> |
+struct MoveHelper<true, is_message, is_stringlike, T> { // enums |
+ static void Move(T* src, T* dest) { *dest = *src; } |
+ // T is an enum here, so allow conversions to and from int. |
+ static void Move(T* src, int* dest) { *dest = static_cast<int>(*src); } |
+ static void Move(int* src, T* dest) { *dest = static_cast<T>(*src); } |
+}; |
+ |
+template <bool is_stringlike, typename T> |
+struct MoveHelper<false, true, is_stringlike, T> { // messages |
+ static void Move(T* src, T* dest) { dest->Swap(src); } |
+}; |
+ |
+template <typename T> |
+struct MoveHelper<false, false, true, T> { // strings and similar |
+ static void Move(T* src, T* dest) { |
+#if __cplusplus >= 201103L |
+ *dest = std::move(*src); |
+#else |
+ dest->swap(*src); |
+#endif |
+ } |
+}; |
+ |
// MapEntryLite is used to implement parsing and serialization of map for lite |
// runtime. |
template <typename Key, typename Value, |
@@ -86,7 +119,7 @@ class MapEntryLite : public MessageLite { |
kKeyFieldNumber, KeyTypeHandler::kWireType); |
static const uint8 kValueTag = GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG( |
kValueFieldNumber, ValueTypeHandler::kWireType); |
- static const int kTagSize = 1; |
+ static const size_t kTagSize = 1; |
public: |
~MapEntryLite() { |
@@ -138,7 +171,7 @@ class MapEntryLite : public MessageLite { |
// need to care whether the value is unknown enum; |
// 4) missing key/value: missed key/value will have default value. caller |
// should take this entry as if key/value is set to default value. |
- tag = input->ReadTag(); |
+ tag = input->ReadTagNoLastTag(); |
switch (tag) { |
case kKeyTag: |
if (!KeyTypeHandler::Read(input, mutable_key())) { |
@@ -168,8 +201,8 @@ class MapEntryLite : public MessageLite { |
} |
} |
- int ByteSize() const { |
- int size = 0; |
+ size_t ByteSizeLong() const { |
+ size_t size = 0; |
size += has_key() ? kTagSize + KeyTypeHandler::ByteSize(key()) : 0; |
size += has_value() ? kTagSize + ValueTypeHandler::ByteSize(value()) : 0; |
return size; |
@@ -180,11 +213,17 @@ class MapEntryLite : public MessageLite { |
ValueTypeHandler::Write(kValueFieldNumber, value(), output); |
} |
- ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const { |
- output = KeyTypeHandler::WriteToArray(kKeyFieldNumber, key(), output); |
- output = ValueTypeHandler::WriteToArray(kValueFieldNumber, value(), output); |
+ ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray(bool deterministic, |
+ ::google::protobuf::uint8* output) const { |
+ output = KeyTypeHandler::InternalWriteToArray(kKeyFieldNumber, key(), |
+ deterministic, output); |
+ output = ValueTypeHandler::InternalWriteToArray(kValueFieldNumber, value(), |
+ deterministic, output); |
return output; |
} |
+ ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const { |
+ return InternalSerializeWithCachedSizesToArray(false, output); |
+ } |
int GetCachedSize() const { |
int size = 0; |
@@ -271,6 +310,111 @@ class MapEntryLite : public MessageLite { |
arena, key, value); |
} |
+ // Parsing using MergePartialFromCodedStream, above, is not as |
+ // efficient as it could be. This helper class provides a speedier way. |
+ template <typename MapField, typename Map> |
+ class Parser { |
+ public: |
+ explicit Parser(MapField* mf) : mf_(mf), map_(mf->MutableMap()) {} |
+ |
+ // This does what the typical MergePartialFromCodedStream() is expected to |
+ // do, with the additional side-effect that if successful (i.e., if true is |
+ // going to be its return value) it inserts the key-value pair into map_. |
+ bool MergePartialFromCodedStream(::google::protobuf::io::CodedInputStream* input) { |
+ // Look for the expected thing: a key and then a value. If it fails, |
+ // invoke the enclosing class's MergePartialFromCodedStream, or return |
+ // false if that would be pointless. |
+ if (input->ExpectTag(kKeyTag)) { |
+ if (!KeyTypeHandler::Read(input, &key_)) { |
+ return false; |
+ } |
+ // Peek at the next byte to see if it is kValueTag. If not, bail out. |
+ const void* data; |
+ int size; |
+ input->GetDirectBufferPointerInline(&data, &size); |
+ // We could use memcmp here, but we don't bother. The tag is one byte. |
+ GOOGLE_COMPILE_ASSERT(kTagSize == 1, tag_size_error); |
+ if (size > 0 && *reinterpret_cast<const char*>(data) == kValueTag) { |
+ typename Map::size_type size = map_->size(); |
+ value_ptr_ = &(*map_)[key_]; |
+ if (GOOGLE_PREDICT_TRUE(size != map_->size())) { |
+ // We created a new key-value pair. Fill in the value. |
+ typedef |
+ typename MapIf<ValueTypeHandler::kIsEnum, int*, Value*>::type T; |
+ input->Skip(kTagSize); // Skip kValueTag. |
+ if (!ValueTypeHandler::Read(input, |
+ reinterpret_cast<T>(value_ptr_))) { |
+ map_->erase(key_); // Failure! Undo insertion. |
+ return false; |
+ } |
+ if (input->ExpectAtEnd()) return true; |
+ return ReadBeyondKeyValuePair(input); |
+ } |
+ } |
+ } else { |
+ key_ = Key(); |
+ } |
+ |
+ entry_.reset(mf_->NewEntry()); |
+ *entry_->mutable_key() = key_; |
+ const bool result = entry_->MergePartialFromCodedStream(input); |
+ if (result) UseKeyAndValueFromEntry(); |
+ if (entry_->GetArena() != NULL) entry_.release(); |
+ return result; |
+ } |
+ |
+ const Key& key() const { return key_; } |
+ const Value& value() const { return *value_ptr_; } |
+ |
+ private: |
+ void UseKeyAndValueFromEntry() GOOGLE_ATTRIBUTE_COLD { |
+ // Update key_ in case we need it later (because key() is called). |
+ // This is potentially inefficient, especially if the key is |
+ // expensive to copy (e.g., a long string), but this is a cold |
+ // path, so it's not a big deal. |
+ key_ = entry_->key(); |
+ value_ptr_ = &(*map_)[key_]; |
+ MoveHelper<ValueTypeHandler::kIsEnum, |
+ ValueTypeHandler::kIsMessage, |
+ ValueTypeHandler::kWireType == |
+ WireFormatLite::WIRETYPE_LENGTH_DELIMITED, |
+ Value>::Move(entry_->mutable_value(), value_ptr_); |
+ } |
+ |
+ // After reading a key and value successfully, and inserting that data |
+ // into map_, we are not at the end of the input. This is unusual, but |
+ // allowed by the spec. |
+ bool ReadBeyondKeyValuePair(::google::protobuf::io::CodedInputStream* input) |
+ GOOGLE_ATTRIBUTE_COLD { |
+ typedef MoveHelper<KeyTypeHandler::kIsEnum, |
+ KeyTypeHandler::kIsMessage, |
+ KeyTypeHandler::kWireType == |
+ WireFormatLite::WIRETYPE_LENGTH_DELIMITED, |
+ Key> KeyMover; |
+ typedef MoveHelper<ValueTypeHandler::kIsEnum, |
+ ValueTypeHandler::kIsMessage, |
+ ValueTypeHandler::kWireType == |
+ WireFormatLite::WIRETYPE_LENGTH_DELIMITED, |
+ Value> ValueMover; |
+ entry_.reset(mf_->NewEntry()); |
+ ValueMover::Move(value_ptr_, entry_->mutable_value()); |
+ map_->erase(key_); |
+ KeyMover::Move(&key_, entry_->mutable_key()); |
+ const bool result = entry_->MergePartialFromCodedStream(input); |
+ if (result) UseKeyAndValueFromEntry(); |
+ if (entry_->GetArena() != NULL) entry_.release(); |
+ return result; |
+ } |
+ |
+ MapField* const mf_; |
+ Map* const map_; |
+ Key key_; |
+ Value* value_ptr_; |
+ // On the fast path entry_ is not used. And, when entry_ is used, it's set |
+ // to mf_->NewEntry(), so in the arena case we must call entry_.release. |
+ google::protobuf::scoped_ptr<MapEntryLite> entry_; |
+ }; |
+ |
protected: |
void set_has_key() { _has_bits_[0] |= 0x00000001u; } |
bool has_key() const { return (_has_bits_[0] & 0x00000001u) != 0; } |
@@ -394,6 +538,32 @@ class MapEntryLite : public MessageLite { |
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MapEntryLite); |
}; |
+// Helpers for deterministic serialization ============================= |
+ |
+// This struct can be used with any generic sorting algorithm. If the Key |
+// type is relatively small and easy to copy then copying Keys into an |
+// array of SortItems can be beneficial. Then all the data the sorting |
+// algorithm needs to touch is in that one array. |
+template <typename Key, typename PtrToKeyValuePair> struct SortItem { |
+ SortItem() {} |
+ explicit SortItem(PtrToKeyValuePair p) : first(p->first), second(p) {} |
+ |
+ Key first; |
+ PtrToKeyValuePair second; |
+}; |
+ |
+template <typename T> struct CompareByFirstField { |
+ bool operator()(const T& a, const T& b) const { |
+ return a.first < b.first; |
+ } |
+}; |
+ |
+template <typename T> struct CompareByDerefFirst { |
+ bool operator()(const T& a, const T& b) const { |
+ return a->first < b->first; |
+ } |
+}; |
+ |
} // namespace internal |
} // namespace protobuf |