diff --git a/folly/BUCK b/folly/BUCK index a3690ddcdca..d8ce9773b94 100644 --- a/folly/BUCK +++ b/folly/BUCK @@ -568,9 +568,17 @@ cpp_library( cpp_library( name = "fmt_utility", + srcs = [ + "FmtUtility.cpp", + ], headers = [ "FmtUtility.h", ], + deps = [ + ":range", + ":string", + "//folly/ssl:openssl_hash", + ], exported_deps = [ "fbsource//third-party/fmt:fmt", ":cpp_attributes", diff --git a/folly/FmtUtility.cpp b/folly/FmtUtility.cpp new file mode 100644 index 00000000000..28450be44ce --- /dev/null +++ b/folly/FmtUtility.cpp @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#include + +#include +#include +#include + +namespace folly { + +std::string fmt_vformat_mangle_name_fn::operator()( + std::string_view const key) const { + auto& self = *this; + std::string out; + self(out, key); + return out; +} + +void fmt_vformat_mangle_name_fn::operator()( + std::string& out, std::string_view const key) const { + auto const keyr = folly::ByteRange(folly::StringPiece(key)); + uint8_t enc[32]; + auto const encr = folly::MutableByteRange{std::begin(enc), std::end(enc)}; +#if FOLLY_OPENSSL_HAS_BLAKE2B + folly::ssl::OpenSSLHash::blake2s256(encr, keyr); +#else + folly::ssl::OpenSSLHash::sha256(encr, keyr); +#endif + out.push_back('_'); + folly::hexlify(encr, out, true); +} + +std::string fmt_vformat_mangle_format_string_fn::operator()( + std::string_view const str) const { + std::string out; + char const* pos = str.data(); + format_string_for_each_named_arg(str, [&](auto const arg) { + out.append(pos, arg.data()); + fmt_vformat_mangle_name(out, arg); + pos = arg.data() + arg.size(); + }); + out.append(pos, str.data() + str.size()); + return out; +} + +} // namespace folly diff --git a/folly/FmtUtility.h b/folly/FmtUtility.h index 9157169561a..5ea1600a5e9 100644 --- a/folly/FmtUtility.h +++ b/folly/FmtUtility.h @@ -44,4 +44,27 @@ struct fmt_make_format_args_from_map_fn { inline constexpr fmt_make_format_args_from_map_fn fmt_make_format_args_from_map{}; +/// fmt_vformat_mangle_name_fn +/// fmt_vformat_mangle_name +/// +/// A helper function-object type and variable for mangling vformat named-arg +/// names which fmt::vformat might not otherwise permit. +struct fmt_vformat_mangle_name_fn { + std::string operator()(std::string_view const str) const; + void operator()(std::string& out, std::string_view const str) const; +}; +inline constexpr fmt_vformat_mangle_name_fn fmt_vformat_mangle_name{}; + +/// fmt_vformat_mangle_format_string_fn +/// fmt_vformat_mangle_format_string +/// +/// A helper function-object type and variable for mangling the content of +/// vformat format-strings containing named-arg names which fmt::vformat might +/// not otherwise permit. +struct fmt_vformat_mangle_format_string_fn { + std::string operator()(std::string_view const str) const; +}; +inline constexpr fmt_vformat_mangle_format_string_fn + fmt_vformat_mangle_format_string{}; + } // namespace folly diff --git a/folly/String.h b/folly/String.h index 79e6d64cda0..e5e758abc0c 100644 --- a/folly/String.h +++ b/folly/String.h @@ -783,7 +783,7 @@ struct format_string_for_each_named_arg_fn { } auto const arg = str.substr(beg, end - beg); auto const c = arg.empty() ? 0 : arg[0]; - if (c == '_' || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) { + if (c && !(c >= '0' && c <= '9')) { fn(arg); } str = str.substr(end); diff --git a/folly/test/FmtUtilityTest.cpp b/folly/test/FmtUtilityTest.cpp index b1ac017ac51..d454e3c0302 100644 --- a/folly/test/FmtUtilityTest.cpp +++ b/folly/test/FmtUtilityTest.cpp @@ -37,3 +37,16 @@ TEST_F(FmtUtilityTest, fmt_make_format_args_from_map_fn) { {"adj"s, "silly"sv}, }))); } + +TEST_F(FmtUtilityTest, fmt_vformat_mangle) { + EXPECT_EQ( + "hello bob you silly goose", + fmt::vformat( + folly::fmt_vformat_mangle_format_string( + "hello {@pre-key|name} you {@pre-key|adj} goose"), + folly::fmt_make_format_args_from_map( + std::map{ + {folly::fmt_vformat_mangle_name("@pre-key|name"), "bob"sv}, + {folly::fmt_vformat_mangle_name("@pre-key|adj"), "silly"sv}, + }))); +}