summaryrefslogtreecommitdiffstats
path: root/library/cpp
diff options
context:
space:
mode:
Diffstat (limited to 'library/cpp')
-rw-r--r--library/cpp/digest/md5/md5.cpp1
-rw-r--r--library/cpp/digest/md5/md5.h3
-rw-r--r--library/cpp/digest/md5/md5_ut.cpp2
-rw-r--r--library/cpp/mime/types/mime.cpp2
-rw-r--r--library/cpp/protobuf/json/json2proto.cpp56
-rw-r--r--library/cpp/protobuf/json/json2proto.h21
-rw-r--r--library/cpp/protobuf/json/ut/json2proto_ut.cpp60
-rw-r--r--library/cpp/threading/thread_local/generic.cpp65
-rw-r--r--library/cpp/threading/thread_local/generic.h46
-rw-r--r--library/cpp/tld/tlds-alpha-by-domain.txt2
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