/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #include #include namespace apache::thrift::protocol::detail { // TODO: replace MapId with ValueId // Runtime and compile time representations for a map id. enum class MapId : int64_t {}; // Thrift Map Mask supports integer and string key similar to // https://docs.hhvm.com/hack/built-in-types/arraykey. enum class ArrayKey { Integer = 0, String = 1, }; // Returns mask == allMask() but faster inline bool isAllMask(const Mask& mask) { return mask.excludes_ref() && mask.excludes_ref()->empty(); } // Returns mask == noneMask() but faster inline bool isNoneMask(const Mask& mask) { return mask.includes_ref() && mask.includes_ref()->empty(); } // Checks whether it is ALL map mask. inline bool isAllMapMask(const Mask& mask) { if (!mask.excludes_map_ref() && !mask.excludes_string_map_ref() && !mask.includes_map_ref() && !mask.includes_string_map_ref()) { folly::throw_exception("This is not a map mask."); } return (mask.excludes_map_ref() && mask.excludes_map_ref()->empty()) || (mask.excludes_string_map_ref() && mask.excludes_string_map_ref()->empty()); } // Checks whether it is NONE map mask. inline bool isNoneMapMask(const Mask& mask) { if (!mask.excludes_map_ref() && !mask.excludes_string_map_ref() && !mask.includes_map_ref() && !mask.includes_string_map_ref()) { folly::throw_exception("This is not a map mask."); } return (mask.includes_map_ref() && mask.includes_map_ref()->empty()) || (mask.includes_string_map_ref() && mask.includes_string_map_ref()->empty()); } // If mask is a field mask, return it, otherwise return nullptr [[nodiscard]] const FieldIdToMask* FOLLY_NULLABLE getFieldMask(const Mask& mask); // If mask is an integer map mask, return it, otherwise return nullptr [[nodiscard]] const MapIdToMask* FOLLY_NULLABLE getIntegerMapMask(const Mask& mask); // If mask is a string map mask, return it, otherwise return nullptr [[nodiscard]] const MapStringToMask* FOLLY_NULLABLE getStringMapMask(const Mask& mask); // If mask is an AnyMask, return it, otherwise return nullptr [[nodiscard]] const MapTypeToMask* FOLLY_NULLABLE getTypeMapMask(const Mask& mask); // If mask is an any-mask, return it, otherwise return nullptr // TODO // Moves the given object to the field (can be field ref or smart pointer). template void moveObject(T& field, U&& object) { if constexpr (thrift::detail::is_unique_ptr_v) { field = std::make_unique(std::forward(object)); } else if constexpr (thrift::detail::is_shared_ptr_v) { field = std::make_shared(std::forward(object)); } else { field = std::forward(object); } } // Throws a runtime error if the mask contains a map mask. void throwIfContainsMapMask(const Mask& mask); template void compare_impl(const T& original, const T& modified, FieldIdToMask& mask) { op::for_each_field_id([&](auto id) { using Id = decltype(id); int16_t fieldId = folly::to_underlying(id()); auto&& original_field = op::get(original); auto&& modified_field = op::get(modified); auto* original_ptr = op::getValueOrNull(original_field); auto* modified_ptr = op::getValueOrNull(modified_field); if (!original_ptr && !modified_ptr) { // same return; } if (!original_ptr || !modified_ptr) { // different mask[fieldId] = field_mask_constants::allMask(); return; } if (*original_ptr == *modified_ptr) { // same return; } // check if nested fields need to be added to mask using FieldType = op::get_native_type; if constexpr (is_thrift_class_v) { compare_impl( *original_ptr, *modified_ptr, mask[fieldId].includes_ref().emplace()); return; } // The values are different and not nested. mask[fieldId] = field_mask_constants::allMask(); }); } // Returns the ArrayKey of the given Value key. If the Value key contains // a non-integer or non-string value, it throws. ArrayKey getArrayKeyFromValue(const Value& v); // Returns the MapId of the given Value key. If the Value key // contains a non-integer valie, it throws. MapId getMapIdFromValue(const Value& v); // Returns the string of the given Value key. If the Value key contains a // non-string value, it throws. std::string getStringFromValue(const Value& v); // Returns the MapId in map mask of the given Value key. // If it doesn't exist, it returns the new MapId (pointer to the key). // Assumes the map mask uses pointers to keys. MapId findMapIdByValueAddress(const Mask& mask, const Value& key); using ValueIndex = folly::F14FastSet< std::reference_wrapper, std::hash, std::equal_to>; // Indexes the values referred to by the given mask by their value. The address // of the value is preserved by the index. ValueIndex buildValueIndex(const Mask& mask); // Returns the MapId in value index of the given Value key. // If it isn't indexed, it returns the new MapId (pointer to the key). // Assumes the map mask uses pointers to keys. MapId getMapIdValueAddressFromIndex( const ValueIndex& index, const Value& newKey); } // namespace apache::thrift::protocol::detail