Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion tree/ntuple/inc/ROOT/RField.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,18 @@ class RFieldZero final : public RFieldBase {
/// This flag is reset on Clone().
bool fAllowFieldSubstitutions = false;

std::unordered_set<std::string> fSubFieldNames; ///< Efficient detection of duplicate field names

protected:
std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
void ConstructValue(void *) const final {}

public:
RFieldZero() : RFieldBase("", "", ROOT::ENTupleStructure::kRecord, false /* isSimple */) {}

using RFieldBase::Attach;
/// A public version of the Attach method that allows piece-wise construction of the zero field.
/// Will throw on duplicate subfield names.
void Attach(std::unique_ptr<RFieldBase> child);
Comment on lines +81 to +83
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, RFieldBase::Attach is non-virtual so "overriding" it in the base class only works if we statically know the type of the RFieldZero - is that the case?

size_t GetValueSize() const final { return 0; }
size_t GetAlignment() const final { return 0; }

Expand Down
19 changes: 18 additions & 1 deletion tree/ntuple/src/RField.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,22 @@ void ROOT::Internal::SetAllowFieldSubstitutions(RFieldZero &fieldZero, bool val)
fieldZero.fAllowFieldSubstitutions = val;
}

void ROOT::RFieldZero::Attach(std::unique_ptr<RFieldBase> child)
{
const std::string childName = child->GetFieldName();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const std::string childName = child->GetFieldName();
const std::string &childName = child->GetFieldName();

to avoid the copy?

if (fSubFieldNames.count(childName) > 0)
throw RException(R__FAIL("duplicate field name: " + childName));
RFieldBase::Attach(std::move(child), "");
fSubFieldNames.insert(childName);
Comment on lines +44 to +47
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can do this in one go:

Suggested change
if (fSubFieldNames.count(childName) > 0)
throw RException(R__FAIL("duplicate field name: " + childName));
RFieldBase::Attach(std::move(child), "");
fSubFieldNames.insert(childName);
if (!fSubFieldNames.insert(childName).second)
throw RException(R__FAIL("duplicate field name: " + childName));
RFieldBase::Attach(std::move(child), "");

}

std::unique_ptr<ROOT::RFieldBase> ROOT::RFieldZero::CloneImpl(std::string_view /*newName*/) const
{
auto result = std::make_unique<RFieldZero>();
for (auto &f : fSubfields)
for (auto &f : fSubfields) {
result->Attach(f->Clone(f->GetFieldName()));
result->fSubFieldNames.insert(f->GetFieldName());
}
return result;
}

Expand Down Expand Up @@ -603,13 +614,19 @@ ROOT::RRecordField::RRecordField(std::string_view fieldName, std::vector<std::un
{
fTraits |= kTraitTrivialType;
fOffsets.reserve(itemFields.size());
std::unordered_set<std::string> fieldNames;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the local std::unordered_set, we probably only need std::string_view as the item type and avoid the string copies

for (auto &item : itemFields) {
const auto itemName = item->GetFieldName();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will copy the field name

if (fieldNames.count(itemName) > 0) {
throw RException(R__FAIL("duplicate field name: " + itemName));
}
Comment on lines +620 to +622
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (fieldNames.count(itemName) > 0) {
throw RException(R__FAIL("duplicate field name: " + itemName));
}
if (!fieldNames.insert(itemName).second) {
throw RException(R__FAIL("duplicate field name: " + itemName));
}

fSize += GetItemPadding(fSize, item->GetAlignment());
fOffsets.push_back(fSize);
fMaxAlignment = std::max(fMaxAlignment, item->GetAlignment());
fSize += item->GetValueSize();
fTraits &= item->GetTraits();
Attach(std::move(item));
fieldNames.insert(itemName);
}
fTraits |= !emulatedFromType.empty() * kTraitEmulatedField;
// Trailing padding: although this is implementation-dependent, most add enough padding to comply with the
Expand Down
17 changes: 17 additions & 0 deletions tree/ntuple/test/ntuple_field_name.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,20 @@ TEST(RNTuple, SubFieldName)
EXPECT_EQ("_1", itr->GetFieldName());
}
}

TEST(RNTuple, FieldNameCollision)
{
ROOT::RFieldZero zero;
zero.Attach(std::make_unique<RField<char>>("x"));
EXPECT_THROW(zero.Attach(std::make_unique<RField<char>>("x")), ROOT::RException);

std::vector<std::unique_ptr<ROOT::RFieldBase>> items;
items.emplace_back(std::make_unique<RField<char>>("x"));
items.emplace_back(std::make_unique<RField<char>>("x"));
try {
ROOT::RRecordField("f", std::move(items));
FAIL() << "duplicate field names should throw";
} catch (const ROOT::RException &err) {
EXPECT_THAT(err.what(), testing::HasSubstr("duplicate field name"));
}
}
Loading