diff options
Diffstat (limited to 'library/cpp')
| -rw-r--r-- | library/cpp/digest/md5/md5.cpp | 1 | ||||
| -rw-r--r-- | library/cpp/digest/md5/md5.h | 3 | ||||
| -rw-r--r-- | library/cpp/digest/md5/md5_ut.cpp | 2 | ||||
| -rw-r--r-- | library/cpp/mime/types/mime.cpp | 2 | ||||
| -rw-r--r-- | library/cpp/protobuf/json/json2proto.cpp | 56 | ||||
| -rw-r--r-- | library/cpp/protobuf/json/json2proto.h | 21 | ||||
| -rw-r--r-- | library/cpp/protobuf/json/ut/json2proto_ut.cpp | 60 | ||||
| -rw-r--r-- | library/cpp/threading/thread_local/generic.cpp | 65 | ||||
| -rw-r--r-- | library/cpp/threading/thread_local/generic.h | 46 | ||||
| -rw-r--r-- | library/cpp/tld/tlds-alpha-by-domain.txt | 2 |
10 files changed, 224 insertions, 34 deletions
diff --git a/library/cpp/digest/md5/md5.cpp b/library/cpp/digest/md5/md5.cpp index 24a5b69eefb..387e0cc6e2f 100644 --- a/library/cpp/digest/md5/md5.cpp +++ b/library/cpp/digest/md5/md5.cpp @@ -12,7 +12,6 @@ namespace { constexpr size_t MD5_BLOCK_LENGTH = 64; constexpr size_t MD5_PADDING_SHIFT = 56; - constexpr size_t MD5_HEX_DIGEST_LENGTH = 32; struct TMd5Stream: public IOutputStream { inline TMd5Stream(MD5* md5) diff --git a/library/cpp/digest/md5/md5.h b/library/cpp/digest/md5/md5.h index 2c17aa05181..b6aecb8f460 100644 --- a/library/cpp/digest/md5/md5.h +++ b/library/cpp/digest/md5/md5.h @@ -7,6 +7,9 @@ class IInputStream; class MD5 { public: + static constexpr size_t MD5_HEX_DIGEST_LENGTH = 32; + +public: MD5() { Init(); } diff --git a/library/cpp/digest/md5/md5_ut.cpp b/library/cpp/digest/md5/md5_ut.cpp index 1c3e4ad0a9f..7fb7f58a996 100644 --- a/library/cpp/digest/md5/md5_ut.cpp +++ b/library/cpp/digest/md5/md5_ut.cpp @@ -14,7 +14,7 @@ Y_UNIT_TEST_SUITE(TMD5Test) { r.Update((const unsigned char*)b, 15); r.Update((const unsigned char*)b + 15, strlen(b) - 15); - char rs[33]; + char rs[MD5::MD5_HEX_DIGEST_LENGTH + 1]; TString s(r.End(rs)); s.to_lower(); diff --git a/library/cpp/mime/types/mime.cpp b/library/cpp/mime/types/mime.cpp index 1276009a8c0..7e12be3a514 100644 --- a/library/cpp/mime/types/mime.cpp +++ b/library/cpp/mime/types/mime.cpp @@ -78,7 +78,7 @@ const TMimeTypes::TRecord TMimeTypes::Records[] = { {MIME_ARCHIVE, "application/x-archive\0application/x-tar\0application/x-ustar\0application/x-gtar\0application/x-bzip2\0application/x-rar\0", "tar\0rar\0bzip2\0"}, {MIME_EXE, "application/exe\0application/octet-stream\0application/x-dosexec\0application/x-msdownload\0", "exe\0"}, {MIME_ODG, "application/vnd.oasis.opendocument.graphics\0", "odg\0"}, - {MIME_GZIP, "application/x-gzip\0", "gz\0gzip\0"}, + {MIME_GZIP, "application/x-gzip\0application/gzip\0", "gz\0gzip\0"}, {MIME_XLSX, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\0", "xlsx\0"}, {MIME_PPTX, "application/vnd.openxmlformats-officedocument.presentationml.presentation\0", "pptx\0"}, {MIME_JAVASCRIPT, "application/javascript\0text/javascript\0", "js\0"}, diff --git a/library/cpp/protobuf/json/json2proto.cpp b/library/cpp/protobuf/json/json2proto.cpp index a1d7e862ef0..0aeee64228d 100644 --- a/library/cpp/protobuf/json/json2proto.cpp +++ b/library/cpp/protobuf/json/json2proto.cpp @@ -230,6 +230,9 @@ JsonEnum2Field(const NJson::TJsonValue& json, const auto value = json.GetInteger(); enumFieldValue = enumField->FindValueByNumber(value); if (!enumFieldValue) { + if (config.AllowUnknownEnumValues) { + return; + } ythrow yexception() << "Invalid integer value of JSON enum field: " << value << "."; } } else if (json.IsString()) { @@ -246,9 +249,15 @@ JsonEnum2Field(const NJson::TJsonValue& json, } } if (!enumFieldValue) { + if (config.AllowUnknownEnumValues) { + return; + } ythrow yexception() << "Invalid string value of JSON enum field: " << TStringBuf(value).Head(100) << "."; } } else { + if (config.AllowUnknownEnumValues) { + return; + } ythrow yexception() << "Invalid type of JSON enum field: not an integer/string."; } @@ -259,6 +268,27 @@ JsonEnum2Field(const NJson::TJsonValue& json, } } +static bool HasFieldValue(const NJson::TJsonValue& json, TStringBuf key) { + const auto t = json[key].GetType(); + return t != NJson::JSON_UNDEFINED && t != NJson::JSON_NULL; +} + +static TString ResolveFieldNameInJson(const NJson::TJsonValue& json, + const google::protobuf::FieldDescriptor& field, + const NProtobufJson::TJson2ProtoConfig& config) { + TString name = GetFieldName(field, config); + if (!config.AllowFieldNameAliases || HasFieldValue(json, name)) { + return name; + } + if (const TString jsonName = TString(field.json_name()); !jsonName.empty() && jsonName != name && HasFieldValue(json, jsonName)) { + return jsonName; + } + if (const TString fieldName = TString(field.name()); !fieldName.empty() && fieldName != name && HasFieldValue(json, fieldName)) { + return fieldName; + } + return name; +} + static void Json2SingleField(const NJson::TJsonValue& json, google::protobuf::Message& proto, @@ -274,7 +304,7 @@ Json2SingleField(const NJson::TJsonValue& json, TString nameHolder; TStringBuf name; if (!isMapValue) { - nameHolder = GetFieldName(field, config); + nameHolder = ResolveFieldNameInJson(json, field, config); name = nameHolder; const NJson::TJsonValue& fieldJson = json[name]; if (auto fieldJsonType = fieldJson.GetType(); fieldJsonType == NJson::JSON_UNDEFINED || fieldJsonType == NJson::JSON_NULL) { @@ -430,7 +460,7 @@ Json2RepeatedField(const NJson::TJsonValue& json, const NProtobufJson::TJson2ProtoConfig& config) { using namespace google::protobuf; - TString name = GetFieldName(field, config); + TString name = ResolveFieldNameInJson(json, field, config); const NJson::TJsonValue& fieldJson = json[name]; if (fieldJson.GetType() == NJson::JSON_UNDEFINED || fieldJson.GetType() == NJson::JSON_NULL) @@ -441,15 +471,18 @@ Json2RepeatedField(const NJson::TJsonValue& json, } bool isMap = fieldJson.GetType() == NJson::JSON_MAP; + bool treatObjectAsSingleElement = false; if (isMap) { - if (!config.MapAsObject) { + if (!field.is_map() && !fieldJson.GetMap().empty() && config.VectorizeObjects) { + treatObjectAsSingleElement = true; + } else if (!config.MapAsObject) { ythrow yexception() << "Map as object representation is not allowed, field: " << field.name(); } else if (!field.is_map() && !fieldJson.GetMap().empty()) { ythrow yexception() << "Field " << field.name() << " is not a map."; } } - if (fieldJson.GetType() != NJson::JSON_ARRAY && !config.MapAsObject && !config.VectorizeScalars && !config.ValueVectorizer) { + if (fieldJson.GetType() != NJson::JSON_ARRAY && !config.MapAsObject && !config.VectorizeScalars && !config.ValueVectorizer && !treatObjectAsSingleElement) { ythrow yexception() << "JSON field doesn't represent an array for " << name << "(actual type is " @@ -459,7 +492,12 @@ Json2RepeatedField(const NJson::TJsonValue& json, const Reflection* reflection = proto.GetReflection(); Y_ASSERT(!!reflection); - if (isMap) { + if (treatObjectAsSingleElement) { + if (config.ReplaceRepeatedFields) { + reflection->ClearField(&proto, &field); + } + Json2RepeatedFieldValue(fieldJson, proto, field, config, reflection); + } else if (isMap) { const THashMap<TString, NJson::TJsonValue>& jsonMap = fieldJson.GetMap(); for (const auto& x : jsonMap) { const TString& key = x.first; @@ -588,6 +626,14 @@ namespace NProtobufJson { for (int f = 0, endF = descriptor->field_count(); f < endF; ++f) { const google::protobuf::FieldDescriptor* field = descriptor->field(f); knownFields[GetFieldName(*field, config)] = 1; + if (config.AllowFieldNameAliases) { + if (const TString jsonName = TString(field->json_name()); !jsonName.empty()) { + knownFields[jsonName] = 1; + } + if (const TString fieldName = TString(field->name()); !fieldName.empty()) { + knownFields[fieldName] = 1; + } + } } for (const auto& f : json.GetMap()) { const bool isFieldKnown = knownFields.contains(f.first); diff --git a/library/cpp/protobuf/json/json2proto.h b/library/cpp/protobuf/json/json2proto.h index 019dd177174..1fb27d4e92d 100644 --- a/library/cpp/protobuf/json/json2proto.h +++ b/library/cpp/protobuf/json/json2proto.h @@ -102,6 +102,11 @@ namespace NProtobufJson { return *this; } + TSelf& SetVectorizeObjects(bool vectorizeObjects) { + VectorizeObjects = vectorizeObjects; + return *this; + } + TSelf& SetAllowComments(bool value) { AllowComments = value; return *this; @@ -112,6 +117,16 @@ namespace NProtobufJson { return *this; } + TSelf& SetAllowFieldNameAliases(bool value) { + AllowFieldNameAliases = value; + return *this; + } + + TSelf& SetAllowUnknownEnumValues(bool value) { + AllowUnknownEnumValues = value; + return *this; + } + TSelf& SetAllowString2TimeConversion(bool value) { AllowString2TimeConversion = value; return *this; @@ -162,6 +177,12 @@ namespace NProtobufJson { /// Append scalars to repeated fields bool VectorizeScalars = false; + bool VectorizeObjects = false; + + bool AllowFieldNameAliases = false; + + bool AllowUnknownEnumValues = false; + /// Custom spliter non array value to repeated fields. TValueVectorizer ValueVectorizer = {}; diff --git a/library/cpp/protobuf/json/ut/json2proto_ut.cpp b/library/cpp/protobuf/json/ut/json2proto_ut.cpp index 1172b2b8db6..7e98659f571 100644 --- a/library/cpp/protobuf/json/ut/json2proto_ut.cpp +++ b/library/cpp/protobuf/json/ut/json2proto_ut.cpp @@ -543,6 +543,66 @@ Y_UNIT_TEST(TestInvalidRepeatedFieldWithMapAsObject) { UNIT_ASSERT_EXCEPTION(Json2Proto(TStringBuf(R"({"Part":{"Boo":{}}})"), proto, config), yexception); } +Y_UNIT_TEST(TestVectorizeObjects) { + // Non-empty JSON object is parsed as a single-element list for a repeated non-map field. + { + TCompositeRepeated proto; + TJson2ProtoConfig config; + config.VectorizeObjects = true; + Json2Proto(TStringBuf(R"({"Part":{"String":"hello","I32":42}})"), proto, config); + UNIT_ASSERT_VALUES_EQUAL(proto.PartSize(), 1); + UNIT_ASSERT_VALUES_EQUAL(proto.GetPart(0).GetString(), "hello"); + UNIT_ASSERT_VALUES_EQUAL(proto.GetPart(0).GetI32(), 42); + } + // Combined with MapAsObject: real maps stay maps, repeated non-map gets single-object path. + { + TCompositeRepeated proto; + TJson2ProtoConfig config; + config.MapAsObject = true; + config.VectorizeObjects = true; + Json2Proto(TStringBuf(R"({"Part":{"String":"x"}})"), proto, config); + UNIT_ASSERT_VALUES_EQUAL(proto.PartSize(), 1); + UNIT_ASSERT_VALUES_EQUAL(proto.GetPart(0).GetString(), "x"); + } + // JSON array still works for the same field. + { + TCompositeRepeated proto; + TJson2ProtoConfig config; + config.VectorizeObjects = true; + Json2Proto(TStringBuf(R"({"Part":[{"String":"a"},{"String":"b"}]})"), proto, config); + UNIT_ASSERT_VALUES_EQUAL(proto.PartSize(), 2); + UNIT_ASSERT_VALUES_EQUAL(proto.GetPart(0).GetString(), "a"); + UNIT_ASSERT_VALUES_EQUAL(proto.GetPart(1).GetString(), "b"); + } + // Empty {} stays no-op (matches prior behaviour when MapAsObject was on). + { + TCompositeRepeated proto; + TJson2ProtoConfig config; + config.MapAsObject = true; + config.VectorizeObjects = true; + Json2Proto(TStringBuf(R"({"Part":{}})"), proto, config); + UNIT_ASSERT_VALUES_EQUAL(proto.PartSize(), 0); + } + // Real proto map<> is still parsed as a map, not wrapped as a single element. + { + TMapType proto; + TJson2ProtoConfig config; + config.MapAsObject = true; + config.VectorizeObjects = true; + Json2Proto(TStringBuf(R"({"Items":{"k1":"v1","k2":"v2"}})"), proto, config); + UNIT_ASSERT_VALUES_EQUAL(proto.GetItems().size(), 2); + UNIT_ASSERT_VALUES_EQUAL(proto.GetItems().at("k1"), "v1"); + UNIT_ASSERT_VALUES_EQUAL(proto.GetItems().at("k2"), "v2"); + } + // Without the flag — original behaviour (throw) is preserved. + { + TCompositeRepeated proto; + TJson2ProtoConfig config; + config.MapAsObject = true; + UNIT_ASSERT_EXCEPTION(Json2Proto(TStringBuf(R"({"Part":{"String":"x"}})"), proto, config), yexception); + } +} + Y_UNIT_TEST(TestStringTransforms) { // Check that strings and bytes are transformed { diff --git a/library/cpp/threading/thread_local/generic.cpp b/library/cpp/threading/thread_local/generic.cpp index 79161a846da..eca63d76bc6 100644 --- a/library/cpp/threading/thread_local/generic.cpp +++ b/library/cpp/threading/thread_local/generic.cpp @@ -14,29 +14,64 @@ namespace { NThreading::TThreadLocalValue<TData, NThreading::EThreadLocalImpl::StdThreadLocal> Data_; }; - std::atomic<size_t>& DefaultFactoryUsageCounter() { - static std::atomic<size_t> v; - return v; - } + class TThreadLocalContext + : public NThreading::IGLSContext + { + bool IsCurrent() const override { + return true; + } - auto& genericLocalStorageFactory() { - static NThreading::TGenericLocalStorageFactory factory = [] { - DefaultFactoryUsageCounter() += 1; + THolder<NThreading::IGenericLocalStorage> MakeStorage() const override { return MakeHolder<TThreadLocalStorage>(); - }; + } + }; + + class TContextRegistry { + public: + TContextRegistry() { + Register(MakeHolder<TThreadLocalContext>()); + } + + size_t Count() const { + return Count_.load(); + } - return factory; + const NThreading::IGLSContext& Get(size_t index) const { + return *Contexts_[index]; + } + + void Register(THolder<NThreading::IGLSContext> context) { + with_lock (Lock_) { + const size_t index = Count_.load(); + Y_ENSURE(index < NThreading::NDetail::MaxGLSContexts, "Too many generic local contexts registered"); + Contexts_[index] = std::move(context); + Count_.store(index + 1); + } + } + private: + TAdaptiveLock Lock_; + std::atomic<size_t> Count_ = 0; + std::array<THolder<NThreading::IGLSContext>, NThreading::NDetail::MaxGLSContexts> Contexts_ = {}; + }; + + TContextRegistry& Registry() { + static TContextRegistry registry; + return registry; } } namespace NThreading { - void SetGenericLocalStorageFactory(TGenericLocalStorageFactory factory) { - Y_ENSURE(DefaultFactoryUsageCounter() == 0, "There are some thread local values allocated with default factory"); - - genericLocalStorageFactory() = factory; + void RegisterGLSContext(THolder<IGLSContext> context) { + Registry().Register(std::move(context)); } - THolder<IGenericLocalStorage> MakeGenericLocalStorage() { - return genericLocalStorageFactory()(); + namespace NDetail { + size_t GLSContextCount() { + return Registry().Count(); + } + + const IGLSContext& GetGLSContext(size_t index) { + return Registry().Get(index); + } } } diff --git a/library/cpp/threading/thread_local/generic.h b/library/cpp/threading/thread_local/generic.h index f60d5b786dd..b0c341f867e 100644 --- a/library/cpp/threading/thread_local/generic.h +++ b/library/cpp/threading/thread_local/generic.h @@ -3,6 +3,7 @@ #include <util/generic/ptr.h> #include <util/generic/vector.h> +#include <array> #include <functional> #include <mutex> @@ -44,14 +45,32 @@ namespace NThreading { virtual TData* GetData() const = 0; }; - using TGenericLocalStorageFactory = std::function<THolder<IGenericLocalStorage>()>; + class IGLSContext { + public: + virtual ~IGLSContext() = default; + + virtual bool IsCurrent() const = 0; + virtual THolder<IGenericLocalStorage> MakeStorage() const = 0; + }; - void SetGenericLocalStorageFactory(TGenericLocalStorageFactory factory); - THolder<IGenericLocalStorage> MakeGenericLocalStorage(); + // Later registrations take priority over earlier ones. + void RegisterGLSContext(THolder<IGLSContext> context); + + namespace NDetail { + inline constexpr size_t MaxGLSContexts = 4; + + size_t GLSContextCount(); + const IGLSContext& GetGLSContext(size_t index); + } template <typename T> class TGenericLocalValue { private: + struct TSlot { + std::once_flag InitOnce; + THolder<IGenericLocalStorage> Storage; + }; + static const auto& Traits() { const static IGenericLocalStorage::TTraits traits = { .Size = sizeof(T), @@ -63,19 +82,26 @@ namespace NThreading { }; public: T* Get() const { - std::call_once(InitOnce_, [this]() { - Storage_ = MakeGenericLocalStorage(); - }); + const size_t count = NDetail::GLSContextCount(); + for (size_t index = count; index-- > 0;) { + const IGLSContext& context = NDetail::GetGLSContext(index); + if (context.IsCurrent()) { + return GetMemory(ContextSlots_[index], context); + } + } - return static_cast<T*>(Storage_->GetMemory(Traits())); + Y_ABORT("unreachable, ContextSlots_[0] always IsCurrent"); } T& GetRef() const { return *Get(); } private: - mutable std::once_flag InitOnce_; - mutable THolder<IGenericLocalStorage> Storage_; + T* GetMemory(TSlot& slot, const IGLSContext& context) const { + std::call_once(slot.InitOnce, [&] { slot.Storage = context.MakeStorage(); }); + return static_cast<T*>(slot.Storage->GetMemory(Traits())); + } + private: + mutable std::array<TSlot, NDetail::MaxGLSContexts> ContextSlots_; }; } - diff --git a/library/cpp/tld/tlds-alpha-by-domain.txt b/library/cpp/tld/tlds-alpha-by-domain.txt index 6f42ec6267a..32e3e697fe5 100644 --- a/library/cpp/tld/tlds-alpha-by-domain.txt +++ b/library/cpp/tld/tlds-alpha-by-domain.txt @@ -1,4 +1,4 @@ -# Version 2026060600, Last Updated Sat Jun 6 07:07:01 2026 UTC +# Version 2026060900, Last Updated Tue Jun 9 07:07:01 2026 UTC AAA AARP ABB |
