Skip to content

Commit

Permalink
Move part of the TypeDefinedMapFieldBase implementation into `MapFi…
Browse files Browse the repository at this point in the history
…eldBase`

implemented on top of `UntypedMapBase` visitation.

Reduces code duplication in large binaries.

More to come in future changes.

PiperOrigin-RevId: 710756618
  • Loading branch information
protobuf-github-bot authored and copybara-github committed Dec 30, 2024
1 parent 4d0382e commit a2a2840
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 88 deletions.
13 changes: 7 additions & 6 deletions src/google/protobuf/dynamic_message.cc
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,9 @@ class DynamicMapField final
static void ClearMapNoSyncImpl(MapFieldBase& base);
static bool DeleteMapValueImpl(MapFieldBase& map, const MapKey& map_key);
static void SetMapIteratorValueImpl(MapIterator* map_iter);
static bool LookupMapValueImpl(const MapFieldBase& self,
const MapKey& map_key, MapValueConstRef* val);
static bool LookupMapValueNoSyncImpl(const MapFieldBase& self,
const MapKey& map_key,
MapValueConstRef* val);

static size_t SpaceUsedExcludingSelfNoLockImpl(const MapFieldBase& map);

Expand Down Expand Up @@ -273,10 +274,10 @@ void DynamicMapField::SetMapIteratorValueImpl(MapIterator* map_iter) {
map_iter->value_.CopyFrom(iter->second);
}

bool DynamicMapField::LookupMapValueImpl(const MapFieldBase& self,
const MapKey& map_key,
MapValueConstRef* val) {
const auto& map = static_cast<const DynamicMapField&>(self).GetMap();
bool DynamicMapField::LookupMapValueNoSyncImpl(const MapFieldBase& self,
const MapKey& map_key,
MapValueConstRef* val) {
const auto& map = static_cast<const DynamicMapField&>(self).map_;
auto iter = map.find(map_key);
if (map.end() == iter) {
return false;
Expand Down
4 changes: 4 additions & 0 deletions src/google/protobuf/map.h
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,10 @@ class KeyMapBase : public UntypedMapBase {
friend class RustMapHelper;
friend class v2::TableDriven;

Key* GetKey(NodeBase* node) const {
return UntypedMapBase::GetKey<Key>(node);
}

PROTOBUF_NOINLINE size_type EraseImpl(map_index_t b, KeyNode* node,
bool do_destroy) {
// Force bucket_index to be in range.
Expand Down
94 changes: 67 additions & 27 deletions src/google/protobuf/map_field.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <cstddef>
#include <cstdint>
#include <string>
#include <type_traits>

#include "absl/functional/overload.h"
#include "absl/log/absl_check.h"
Expand All @@ -35,6 +36,58 @@ MapFieldBase::~MapFieldBase() {
delete maybe_payload();
}

template <typename Map, typename F>
auto VisitMapKey(const MapKey& map_key, Map& map, F f) {
switch (map_key.type()) {
#define HANDLE_TYPE(CPPTYPE, Type, KeyBaseType) \
case FieldDescriptor::CPPTYPE_##CPPTYPE: { \
using KMB = KeyMapBase<KeyBaseType>; \
return f( \
static_cast< \
std::conditional_t<std::is_const_v<Map>, const KMB&, KMB&>>(map), \
TransparentSupport<KeyBaseType>::ToView(map_key.Get##Type##Value())); \
}
HANDLE_TYPE(INT32, Int32, uint32_t);
HANDLE_TYPE(UINT32, UInt32, uint32_t);
HANDLE_TYPE(INT64, Int64, uint64_t);
HANDLE_TYPE(UINT64, UInt64, uint64_t);
HANDLE_TYPE(BOOL, Bool, bool);
HANDLE_TYPE(STRING, String, std::string);
#undef HANDLE_TYPE
default:
Unreachable();
}
}

bool MapFieldBase::InsertOrLookupMapValueNoSyncImpl(MapFieldBase& self,
const MapKey& map_key,
MapValueRef* val) {
if (LookupMapValueNoSyncImpl(self, map_key,
static_cast<MapValueConstRef*>(val))) {
return false;
}

auto& map = self.GetMapRaw();

NodeBase* node = map.AllocNode();
map.VisitValue(node, [&](auto* v) { self.InitializeKeyValue(v); });
val->SetValue(map.GetVoidValue(node));

return VisitMapKey(map_key, map, [&](auto& map, const auto& key) {
self.InitializeKeyValue(map.GetKey(node), key);
map.InsertOrReplaceNode(
static_cast<typename std::decay_t<decltype(map)>::KeyNode*>(node));
return true;
});
}

bool MapFieldBase::DeleteMapValueImpl(MapFieldBase& self,
const MapKey& map_key) {
return VisitMapKey(
map_key, *self.MutableMap(),
[](auto& map, const auto& key) { return map.EraseImpl(key); });
}

void MapFieldBase::ClearMapNoSyncImpl(MapFieldBase& self) {
self.GetMapRaw().ClearTable(true, nullptr);
}
Expand All @@ -56,35 +109,22 @@ void MapFieldBase::SetMapIteratorValueImpl(MapIterator* map_iter) {
map_iter->value_.SetValue(map.GetVoidValue(node));
}

bool MapFieldBase::LookupMapValueImpl(const MapFieldBase& self,
const MapKey& map_key,
MapValueConstRef* val) {
auto& map = self.GetMap();
bool MapFieldBase::LookupMapValueNoSyncImpl(const MapFieldBase& self,
const MapKey& map_key,
MapValueConstRef* val) {
auto& map = self.GetMapRaw();
if (map.empty()) return false;

switch (map_key.type()) {
#define HANDLE_TYPE(CPPTYPE, Type, KeyBaseType) \
case FieldDescriptor::CPPTYPE_##CPPTYPE: { \
auto& key_map = static_cast<const KeyMapBase<KeyBaseType>&>(map); \
auto res = key_map.FindHelper(map_key.Get##Type##Value()); \
if (res.node == nullptr) { \
return false; \
} \
if (val != nullptr) { \
val->SetValue(map.GetVoidValue(res.node)); \
} \
return true; \
}
HANDLE_TYPE(INT32, Int32, uint32_t);
HANDLE_TYPE(UINT32, UInt32, uint32_t);
HANDLE_TYPE(INT64, Int64, uint64_t);
HANDLE_TYPE(UINT64, UInt64, uint64_t);
HANDLE_TYPE(BOOL, Bool, bool);
HANDLE_TYPE(STRING, String, std::string);
#undef HANDLE_TYPE
default:
Unreachable();
}
return VisitMapKey(map_key, map, [&](auto& map, const auto& key) {
auto res = map.FindHelper(key);
if (res.node == nullptr) {
return false;
}
if (val != nullptr) {
val->SetValue(map.GetVoidValue(res.node));
}
return true;
});
}

size_t MapFieldBase::SpaceUsedExcludingSelfNoLockImpl(const MapFieldBase& map) {
Expand Down
47 changes: 36 additions & 11 deletions src/google/protobuf/map_field.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,14 @@ template <typename MessageT>
struct MapDynamicFieldInfo;
struct MapFieldTestPeer;

// Return the prototype message for a Map entry.
// REQUIRES: `default_entry` is a map entry message.
// REQUIRES: mapped_type is of type message.
inline const Message& GetMapEntryValuePrototype(const Message& default_entry) {
return default_entry.GetReflection()->GetMessage(
default_entry, default_entry.GetDescriptor()->map_value());
}

// This class provides access to map field using reflection, which is the same
// as those provided for RepeatedPtrField<Message>. It is used for internal
// reflection implementation only. Users should never use this directly.
Expand All @@ -302,8 +310,9 @@ class PROTOBUF_EXPORT MapFieldBase : public MapFieldBaseForParse {
~MapFieldBase();

struct VTable : MapFieldBaseForParse::VTable {
bool (*lookup_map_value)(const MapFieldBase& map, const MapKey& map_key,
MapValueConstRef* val);
bool (*lookup_map_value_no_sync)(const MapFieldBase& map,
const MapKey& map_key,
MapValueConstRef* val);
bool (*delete_map_value)(MapFieldBase& map, const MapKey& map_key);
void (*set_map_iterator_value)(MapIterator* map_iter);
bool (*insert_or_lookup_no_sync)(MapFieldBase& map, const MapKey& map_key,
Expand All @@ -320,7 +329,7 @@ class PROTOBUF_EXPORT MapFieldBase : public MapFieldBaseForParse {
static constexpr VTable MakeVTable() {
VTable out{};
out.get_map = &T::GetMapImpl;
out.lookup_map_value = &T::LookupMapValueImpl;
out.lookup_map_value_no_sync = &T::LookupMapValueNoSyncImpl;
out.delete_map_value = &T::DeleteMapValueImpl;
out.set_map_iterator_value = &T::SetMapIteratorValueImpl;
out.insert_or_lookup_no_sync = &T::InsertOrLookupMapValueNoSyncImpl;
Expand All @@ -347,7 +356,8 @@ class PROTOBUF_EXPORT MapFieldBase : public MapFieldBaseForParse {
return LookupMapValue(map_key, static_cast<MapValueConstRef*>(nullptr));
}
bool LookupMapValue(const MapKey& map_key, MapValueConstRef* val) const {
return vtable()->lookup_map_value(*this, map_key, val);
SyncMapWithRepeatedField();
return vtable()->lookup_map_value_no_sync(*this, map_key, val);
}
bool LookupMapValue(const MapKey&, MapValueRef*) const = delete;

Expand Down Expand Up @@ -496,8 +506,13 @@ class PROTOBUF_EXPORT MapFieldBase : public MapFieldBaseForParse {
bool is_mutable);
static void ClearMapNoSyncImpl(MapFieldBase& self);
static void SetMapIteratorValueImpl(MapIterator* map_iter);
static bool LookupMapValueImpl(const MapFieldBase& self,
const MapKey& map_key, MapValueConstRef* val);
static bool LookupMapValueNoSyncImpl(const MapFieldBase& self,
const MapKey& map_key,
MapValueConstRef* val);
static bool InsertOrLookupMapValueNoSyncImpl(MapFieldBase& self,
const MapKey& map_key,
MapValueRef* val);
static bool DeleteMapValueImpl(MapFieldBase& self, const MapKey& map_key);

private:
friend class ContendedMapCleanTest;
Expand All @@ -506,6 +521,21 @@ class PROTOBUF_EXPORT MapFieldBase : public MapFieldBaseForParse {
friend class google::protobuf::Reflection;
friend class google::protobuf::DynamicMessage;

template <typename T, typename... U>
void InitializeKeyValue(T* v, const U&... init) {
::new (static_cast<void*>(v)) T(init...);
if constexpr (std::is_same_v<std::string, T>) {
if (arena() != nullptr) {
arena()->OwnDestructor(v);
}
}
}

void InitializeKeyValue(MessageLite* msg) {
GetClassData(GetMapEntryValuePrototype(*GetPrototype()))
->PlacementNew(msg, arena());
}

// See assertion in TypeDefinedMapFieldBase::TypeDefinedMapFieldBase()
const UntypedMapBase& GetMapRaw() const {
return *reinterpret_cast<const UntypedMapBase*>(this + 1);
Expand Down Expand Up @@ -609,11 +639,6 @@ class TypeDefinedMapFieldBase : public MapFieldBase {

using Iter = typename Map<Key, T>::const_iterator;

static bool DeleteMapValueImpl(MapFieldBase& map, const MapKey& map_key);
static bool InsertOrLookupMapValueNoSyncImpl(MapFieldBase& map,
const MapKey& map_key,
MapValueRef* val);

static void MergeFromImpl(MapFieldBase& base, const MapFieldBase& other);
static void SwapImpl(MapFieldBase& lhs, MapFieldBase& rhs);

Expand Down
44 changes: 0 additions & 44 deletions src/google/protobuf/map_field_inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,51 +33,7 @@
namespace google {
namespace protobuf {
namespace internal {
// UnwrapMapKey template. We're using overloading rather than template
// specialization so that we can return a value or reference type depending on
// `T`.
inline int32_t UnwrapMapKeyImpl(const MapKey& map_key, const int32_t*) {
return map_key.GetInt32Value();
}
inline uint32_t UnwrapMapKeyImpl(const MapKey& map_key, const uint32_t*) {
return map_key.GetUInt32Value();
}
inline int64_t UnwrapMapKeyImpl(const MapKey& map_key, const int64_t*) {
return map_key.GetInt64Value();
}
inline uint64_t UnwrapMapKeyImpl(const MapKey& map_key, const uint64_t*) {
return map_key.GetUInt64Value();
}
inline bool UnwrapMapKeyImpl(const MapKey& map_key, const bool*) {
return map_key.GetBoolValue();
}
inline absl::string_view UnwrapMapKeyImpl(const MapKey& map_key,
const std::string*) {
return map_key.GetStringValue();
}

template <typename T>
decltype(auto) UnwrapMapKey(const MapKey& map_key) {
return UnwrapMapKeyImpl(map_key, static_cast<T*>(nullptr));
}

// ------------------------TypeDefinedMapFieldBase---------------
template <typename Key, typename T>
bool TypeDefinedMapFieldBase<Key, T>::InsertOrLookupMapValueNoSyncImpl(
MapFieldBase& map, const MapKey& map_key, MapValueRef* val) {
auto res = static_cast<TypeDefinedMapFieldBase&>(map).map_.try_emplace(
UnwrapMapKey<Key>(map_key));
val->SetValue(&res.first->second);
return res.second;
}

template <typename Key, typename T>
bool TypeDefinedMapFieldBase<Key, T>::DeleteMapValueImpl(
MapFieldBase& map, const MapKey& map_key) {
return static_cast<TypeDefinedMapFieldBase&>(map).MutableMap()->erase(
UnwrapMapKey<Key>(map_key));
}

template <typename Key, typename T>
void TypeDefinedMapFieldBase<Key, T>::SwapImpl(MapFieldBase& lhs,
MapFieldBase& rhs) {
Expand Down

0 comments on commit a2a2840

Please sign in to comment.