Logo ROOT  
Reference Guide
Loading...
Searching...
No Matches
RNTupleModel.cxx
Go to the documentation of this file.
1/// \file RNTupleModel.cxx
2/// \author Jakob Blomer <jblomer@cern.ch>
3/// \date 2018-10-15
4
5/*************************************************************************
6 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
7 * All rights reserved. *
8 * *
9 * For the licensing terms see $ROOTSYS/LICENSE. *
10 * For the list of contributors see $ROOTSYS/README/CREDITS. *
11 *************************************************************************/
12
13#include <ROOT/RError.hxx>
14#include <ROOT/RField.hxx>
15#include <ROOT/RFieldToken.hxx>
16#include <ROOT/RNTupleModel.hxx>
17#include <ROOT/RNTupleUtils.hxx>
19#include <ROOT/StringUtils.hxx>
20
21#include <atomic>
22#include <cstdlib>
23#include <memory>
24#include <utility>
25
26namespace {
27std::uint64_t GetNewModelId()
28{
29 static std::atomic<std::uint64_t> gLastModelId = 0;
30 return ++gLastModelId;
31}
32} // anonymous namespace
33
35{
36 if (model.IsExpired()) {
37 throw RException(R__FAIL("invalid use of expired model"));
38 }
39 return *model.fFieldZero;
40}
41
43{
44 if (model.IsExpired()) {
45 throw RException(R__FAIL("invalid use of expired model"));
46 }
47 return *model.fProjectedFields;
48}
49
51{
52 const auto &constModel = const_cast<const ROOT::RNTupleModel &>(model);
53 return const_cast<ROOT::Internal::RProjectedFields &>(GetProjectedFieldsOfModel(constModel));
54}
55
56//------------------------------------------------------------------------------
57
60{
61 auto source = fieldMap.at(target);
62
63 if (!target->GetColumnRepresentatives()[0].empty()) {
64 const auto representative = target->GetColumnRepresentatives()[0][0];
65 // If the user is trying to add a projected field upon which they called SetTruncated() or SetQuantized(),
66 // they probably have the wrong expectations about what should happen. Warn them about it.
67 if (representative == ENTupleColumnType::kReal32Trunc || representative == ENTupleColumnType::kReal32Quant) {
69 << "calling SetQuantized() or SetTruncated() on a projected field has no effect, as the on-disk "
70 "representation of the value is decided by the projection source field. Reading back the field will "
71 "yield the correct values, but the value range and bits of precision you set on the projected field "
72 "will be ignored.";
73 }
74 }
75
76 const bool hasCompatibleStructure = (source->GetStructure() == target->GetStructure()) ||
77 ((source->GetStructure() == ROOT::ENTupleStructure::kCollection) &&
78 dynamic_cast<const ROOT::RCardinalityField *>(target));
79 if (!hasCompatibleStructure)
80 return R__FAIL("field mapping structural mismatch: " + source->GetFieldName() + " --> " + target->GetFieldName());
81 if ((source->GetStructure() == ROOT::ENTupleStructure::kPlain) ||
82 (source->GetStructure() == ROOT::ENTupleStructure::kStreamer)) {
83 if (target->GetTypeName() != source->GetTypeName())
84 return R__FAIL("field mapping type mismatch: " + source->GetFieldName() + " --> " + target->GetFieldName());
85 }
86
87 auto fnHasArrayParent = [](const ROOT::RFieldBase &f) -> bool {
88 auto parent = f.GetParent();
89 while (parent) {
90 if (parent->GetNRepetitions() > 0)
91 return true;
92 parent = parent->GetParent();
93 }
94 return false;
95 };
96 if (fnHasArrayParent(*source) || fnHasArrayParent(*target)) {
97 return R__FAIL("unsupported field mapping across fixed-size arrays");
98 }
99
100 // We support projections only across records and collections. In the following, we check that the projected
101 // field is on the same path of collection fields in the field tree than the source field.
102
103 // Finds the first non-record parent field of the input field
104 auto fnBreakPoint = [](const ROOT::RFieldBase *f) -> const ROOT::RFieldBase * {
105 auto parent = f->GetParent();
106 while (parent) {
107 if ((parent->GetStructure() != ROOT::ENTupleStructure::kRecord) &&
108 (parent->GetStructure() != ROOT::ENTupleStructure::kPlain)) {
109 return parent;
110 }
111 parent = parent->GetParent();
112 }
113 // We reached the zero field
114 return nullptr;
115 };
116
117 // If source or target has a variant or reference as a parent, error out
118 auto *sourceBreakPoint = fnBreakPoint(source);
119 if (sourceBreakPoint && sourceBreakPoint->GetStructure() != ROOT::ENTupleStructure::kCollection)
120 return R__FAIL("unsupported field mapping (source structure)");
121 auto *targetBreakPoint = fnBreakPoint(target);
122 if (targetBreakPoint && sourceBreakPoint->GetStructure() != ROOT::ENTupleStructure::kCollection)
123 return R__FAIL("unsupported field mapping (target structure)");
124
125 if (!sourceBreakPoint && !targetBreakPoint) {
126 // Source and target have no collections as parent
127 return RResult<void>::Success();
128 }
129 if (sourceBreakPoint && targetBreakPoint) {
130 if (sourceBreakPoint == targetBreakPoint) {
131 // Source and target are children of the same collection
132 return RResult<void>::Success();
133 }
134 if (auto it = fieldMap.find(targetBreakPoint); it != fieldMap.end() && it->second == sourceBreakPoint) {
135 // The parent collection of parent is mapped to the parent collection of the source
136 return RResult<void>::Success();
137 }
138 // Source and target are children of different collections
139 return R__FAIL("field mapping structure mismatch: " + source->GetFieldName() + " --> " + target->GetFieldName());
140 }
141
142 // Either source or target have no collection as a parent, but the other one has; that doesn't fit
143 return R__FAIL("field mapping structure mismatch: " + source->GetFieldName() + " --> " + target->GetFieldName());
144}
145
147ROOT::Internal::RProjectedFields::Add(std::unique_ptr<ROOT::RFieldBase> field, const FieldMap_t &fieldMap)
148{
149 auto result = EnsureValidMapping(field.get(), fieldMap);
150 if (!result)
151 return R__FORWARD_ERROR(result);
152 for (const auto &f : *field) {
153 result = EnsureValidMapping(&f, fieldMap);
154 if (!result)
155 return R__FORWARD_ERROR(result);
156 }
157
158 fFieldMap.insert(fieldMap.begin(), fieldMap.end());
159 fFieldZero->Attach(std::move(field));
160 return RResult<void>::Success();
161}
162
164{
165 if (auto it = fFieldMap.find(target); it != fFieldMap.end())
166 return it->second;
167 return nullptr;
168}
169
170std::unique_ptr<ROOT::Internal::RProjectedFields>
172{
173 auto cloneFieldZero =
174 std::unique_ptr<ROOT::RFieldZero>(static_cast<ROOT::RFieldZero *>(fFieldZero->Clone("").release()));
175 auto clone = std::unique_ptr<RProjectedFields>(new RProjectedFields(std::move(cloneFieldZero)));
176 clone->fModel = &newModel;
177 // TODO(jblomer): improve quadratic search to re-wire the field mappings given the new model and the cloned
178 // projected fields. Not too critical as we generally expect a limited number of projected fields
179 for (const auto &[k, v] : fFieldMap) {
180 for (const auto &f : clone->GetFieldZero()) {
181 if (f.GetQualifiedFieldName() == k->GetQualifiedFieldName()) {
182 clone->fFieldMap[&f] = &newModel.GetConstField(v->GetQualifiedFieldName());
183 break;
184 }
185 }
186 }
187 return clone;
188}
189
194
196{
197 // If we made any changes, we should commit them because the model was already altered.
198 // Otherwise, we _do not_ commit -- it may be that the referenced model is already expired if the
199 // corresponding writer is already destructed.
200 if (!fOpenChangeset.IsEmpty())
202}
203
205{
206 fOpenChangeset.fModel.Unfreeze();
207 // We set the model ID to zero until CommitUpdate(). That prevents calls to RNTupleWriter::Fill() in the middle
208 // of updates
209 std::swap(fOpenChangeset.fModel.fModelId, fNewModelId);
210}
211
213{
214 fOpenChangeset.fModel.Freeze();
215 std::swap(fOpenChangeset.fModel.fModelId, fNewModelId);
216 if (fOpenChangeset.IsEmpty())
217 return;
219 std::swap(fOpenChangeset.fAddedFields, toCommit.fAddedFields);
220 std::swap(fOpenChangeset.fAddedProjectedFields, toCommit.fAddedProjectedFields);
221 fWriter.GetSink().UpdateSchema(toCommit, fWriter.GetNEntries());
222}
223
225{
226 if (parentName.empty())
227 return nullptr;
228
229 if (!fModel.IsBare()) {
230 throw RException(R__FAIL("invalid attempt to late-model-extend an untyped record of a non-bare model"));
231 }
232
233 auto &parentField = fModel.GetMutableField(parentName);
234 auto parentRecord = dynamic_cast<ROOT::RRecordField *>(&parentField);
235 if (!parentRecord || !parentRecord->GetTypeName().empty()) {
236 throw RException(
237 R__FAIL("invalid attempt to extend a field that is not an untyped record: " + std::string(parentName)));
238 }
239
240 auto itr = parentRecord->GetParent();
241 while (itr) {
242 if (typeid(*itr) == typeid(RFieldZero))
243 break;
244
245 if (!dynamic_cast<const ROOT::RRecordField *>(itr)) {
246 throw RException(R__FAIL("invalid attempt to extend an untyped record that has a non-record parent: " +
247 std::string(parentName)));
248 }
249
250 itr = itr->GetParent();
251 }
252
253 return parentRecord;
254}
255
256void ROOT::Internal::RNTupleModelChangeset::AddField(std::unique_ptr<ROOT::RFieldBase> field,
257 std::string_view parentName)
258{
259 auto fieldp = field.get();
260 if (auto parent = GetParentRecordField(parentName)) {
261 Internal::AddItemToRecord(*parent, std::move(field));
262 } else {
263 fModel.AddField(std::move(field));
264 }
265 fAddedFields.emplace_back(fieldp);
266}
267
268void ROOT::RNTupleModel::RUpdater::AddField(std::unique_ptr<ROOT::RFieldBase> field, std::string_view parentName)
269{
270 fOpenChangeset.AddField(std::move(field), parentName);
271}
272
275{
276 auto fieldp = field.get();
277 auto result = fModel.AddProjectedField(std::move(field), mapping);
278 if (result)
279 fAddedProjectedFields.emplace_back(fieldp);
280 return R__FORWARD_RESULT(result);
281}
282
284ROOT::RNTupleModel::RUpdater::AddProjectedField(std::unique_ptr<ROOT::RFieldBase> field, FieldMappingFunc_t mapping)
285{
286 return R__FORWARD_RESULT(fOpenChangeset.AddProjectedField(std::move(field), std::move(mapping)));
287}
288
289void ROOT::RNTupleModel::EnsureValidFieldName(std::string_view fieldName)
290{
291 RResult<void> nameValid = ROOT::Internal::EnsureValidNameForRNTuple(fieldName, "Field");
292 if (!nameValid) {
293 nameValid.Throw();
294 }
295 if (fieldName.empty()) {
296 throw RException(R__FAIL("name cannot be empty string \"\""));
297 }
298 auto fieldNameStr = std::string(fieldName);
299 if (fFieldNames.count(fieldNameStr) > 0)
300 throw RException(R__FAIL("field name '" + fieldNameStr + "' already exists in NTuple model"));
301}
302
304{
305 if (IsFrozen())
306 throw RException(R__FAIL("invalid attempt to modify frozen model"));
307}
308
310{
311 if (IsBare())
312 throw RException(R__FAIL("invalid attempt to use default entry of bare model"));
313}
314
315ROOT::RNTupleModel::RNTupleModel(std::unique_ptr<ROOT::RFieldZero> fieldZero)
316 : fFieldZero(std::move(fieldZero)), fModelId(GetNewModelId()), fSchemaId(fModelId)
317{
318}
319
320std::unique_ptr<ROOT::RNTupleModel> ROOT::RNTupleModel::CreateBare()
321{
322 return CreateBare(std::make_unique<ROOT::RFieldZero>());
323}
324
325std::unique_ptr<ROOT::RNTupleModel> ROOT::RNTupleModel::CreateBare(std::unique_ptr<ROOT::RFieldZero> fieldZero)
326{
327 auto model = std::unique_ptr<RNTupleModel>(new RNTupleModel(std::move(fieldZero)));
328 model->fProjectedFields = std::make_unique<Internal::RProjectedFields>(*model);
329 return model;
330}
331
332std::unique_ptr<ROOT::RNTupleModel> ROOT::RNTupleModel::Create()
333{
334 return Create(std::make_unique<ROOT::RFieldZero>());
335}
336
337std::unique_ptr<ROOT::RNTupleModel> ROOT::RNTupleModel::Create(std::unique_ptr<ROOT::RFieldZero> fieldZero)
338{
339 auto model = CreateBare(std::move(fieldZero));
340 model->fDefaultEntry = std::unique_ptr<ROOT::REntry>(new ROOT::REntry(model->fModelId, model->fSchemaId));
341 return model;
342}
343
344std::unique_ptr<ROOT::RNTupleModel> ROOT::RNTupleModel::Clone() const
345{
346 auto cloneModel = std::unique_ptr<RNTupleModel>(new RNTupleModel(
347 std::unique_ptr<ROOT::RFieldZero>(static_cast<ROOT::RFieldZero *>(fFieldZero->Clone("").release()))));
348 cloneModel->fModelId = GetNewModelId();
349 // For a frozen model, we can keep the schema id because adding new fields is forbidden. It is reset in Unfreeze()
350 // if called by the user.
351 if (IsFrozen()) {
352 cloneModel->fSchemaId = fSchemaId;
353 } else {
354 cloneModel->fSchemaId = cloneModel->fModelId;
355 }
356 cloneModel->fModelState = (fModelState == EState::kExpired) ? EState::kFrozen : fModelState;
357 cloneModel->fFieldNames = fFieldNames;
358 cloneModel->fDescription = fDescription;
359 cloneModel->fProjectedFields = fProjectedFields->Clone(*cloneModel);
360 cloneModel->fRegisteredSubfields = fRegisteredSubfields;
361 if (fDefaultEntry) {
362 cloneModel->fDefaultEntry =
363 std::unique_ptr<ROOT::REntry>(new ROOT::REntry(cloneModel->fModelId, cloneModel->fSchemaId));
364 for (const auto &f : cloneModel->fFieldZero->GetMutableSubfields()) {
365 cloneModel->fDefaultEntry->AddValue(f->CreateValue());
366 }
367 for (const auto &f : cloneModel->fRegisteredSubfields) {
368 cloneModel->AddSubfield(f, *cloneModel->fDefaultEntry);
369 }
370 }
371 return cloneModel;
372}
373
374ROOT::RFieldBase *ROOT::RNTupleModel::FindField(std::string_view fieldName) const
375{
376 if (fieldName.empty())
377 return nullptr;
378
379 auto *field = static_cast<ROOT::RFieldBase *>(fFieldZero.get());
380 for (auto subfieldName : ROOT::Split(fieldName, ".")) {
381 const auto subfields = field->GetMutableSubfields();
382 auto it = std::find_if(subfields.begin(), subfields.end(),
383 [&](const auto *f) { return f->GetFieldName() == subfieldName; });
384 if (it != subfields.end()) {
385 field = *it;
386 } else {
387 field = nullptr;
388 break;
389 }
390 }
391
392 return field;
393}
394
395void ROOT::RNTupleModel::AddField(std::unique_ptr<ROOT::RFieldBase> field)
396{
398 if (!field)
399 throw RException(R__FAIL("null field"));
400 EnsureValidFieldName(field->GetFieldName());
401
402 if (fDefaultEntry)
403 fDefaultEntry->AddValue(field->CreateValue());
404 fFieldNames.insert(field->GetFieldName());
405 fFieldZero->Attach(std::move(field));
406}
407
408void ROOT::RNTupleModel::AddSubfield(std::string_view qualifiedFieldName, ROOT::REntry &entry,
409 bool initializeValue) const
410{
411 auto field = FindField(qualifiedFieldName);
412 if (initializeValue)
413 entry.AddValue(field->CreateValue());
414 else
415 entry.AddValue(field->BindValue(nullptr));
416}
417
418void ROOT::RNTupleModel::RegisterSubfield(std::string_view qualifiedFieldName)
419{
420 if (qualifiedFieldName.empty())
421 throw RException(R__FAIL("no field name provided"));
422
423 if (fFieldNames.find(std::string(qualifiedFieldName)) != fFieldNames.end()) {
424 throw RException(
425 R__FAIL("cannot register top-level field \"" + std::string(qualifiedFieldName) + "\" as a subfield"));
426 }
427
428 if (fRegisteredSubfields.find(std::string(qualifiedFieldName)) != fRegisteredSubfields.end())
429 throw RException(R__FAIL("subfield \"" + std::string(qualifiedFieldName) + "\" already registered"));
430
432
433 auto *field = FindField(qualifiedFieldName);
434 if (!field) {
435 throw RException(R__FAIL("could not find subfield \"" + std::string(qualifiedFieldName) + "\" in model"));
436 }
437
438 auto parent = field->GetParent();
439 while (parent && !parent->GetFieldName().empty()) {
440 if (parent->GetStructure() == ROOT::ENTupleStructure::kCollection || parent->GetNRepetitions() > 0 ||
441 parent->GetStructure() == ROOT::ENTupleStructure::kVariant) {
442 throw RException(R__FAIL(
443 "registering a subfield as part of a collection, fixed-sized array or std::variant is not supported"));
444 }
445 parent = parent->GetParent();
446 }
447
448 if (fDefaultEntry)
449 AddSubfield(qualifiedFieldName, *fDefaultEntry);
450 fRegisteredSubfields.emplace(qualifiedFieldName);
451}
452
454ROOT::RNTupleModel::AddProjectedField(std::unique_ptr<ROOT::RFieldBase> field, FieldMappingFunc_t mapping)
455{
457 if (!field)
458 return R__FAIL("null field");
459 auto fieldName = field->GetFieldName();
460
462 auto sourceField = FindField(mapping(fieldName));
463 if (!sourceField)
464 return R__FAIL("no such field: " + mapping(fieldName));
465 fieldMap[field.get()] = sourceField;
466 for (const auto &subField : *field) {
467 sourceField = FindField(mapping(subField.GetQualifiedFieldName()));
468 if (!sourceField)
469 return R__FAIL("no such field: " + mapping(subField.GetQualifiedFieldName()));
470 fieldMap[&subField] = sourceField;
471 }
472
473 EnsureValidFieldName(fieldName);
474 auto result = fProjectedFields->Add(std::move(field), fieldMap);
475 if (!result) {
476 return R__FORWARD_ERROR(result);
477 }
478 fFieldNames.insert(fieldName);
479 return RResult<void>::Success();
480}
481
483{
484 if (IsFrozen())
485 throw RException(R__FAIL("invalid attempt to get mutable zero field of frozen model"));
486 return *fFieldZero;
487}
488
490{
491 if (IsFrozen())
492 throw RException(R__FAIL("invalid attempt to get mutable field of frozen model"));
493 auto f = FindField(fieldName);
494 if (!f)
495 throw RException(R__FAIL("invalid field: " + std::string(fieldName)));
496
497 return *f;
498}
499
500const ROOT::RFieldBase &ROOT::RNTupleModel::GetConstField(std::string_view fieldName) const
501{
502 auto f = FindField(fieldName);
503 if (!f)
504 throw RException(R__FAIL("invalid field: " + std::string(fieldName)));
505
506 return *f;
507}
508
514
516{
517 if (!IsFrozen())
518 throw RException(R__FAIL("invalid attempt to get default entry of unfrozen model"));
520 return *fDefaultEntry;
521}
522
523std::unique_ptr<ROOT::REntry> ROOT::RNTupleModel::CreateEntry() const
524{
525 switch (fModelState) {
526 case EState::kBuilding: throw RException(R__FAIL("invalid attempt to create entry of unfrozen model"));
527 case EState::kExpired: throw RException(R__FAIL("invalid attempt to create entry of expired model"));
528 case EState::kFrozen: break;
529 }
530
531 auto entry = std::unique_ptr<ROOT::REntry>(new ROOT::REntry(fModelId, fSchemaId));
532 for (const auto &f : fFieldZero->GetMutableSubfields()) {
533 entry->AddValue(f->CreateValue());
534 }
535 for (const auto &f : fRegisteredSubfields) {
536 AddSubfield(f, *entry);
537 }
538 return entry;
539}
540
541std::unique_ptr<ROOT::REntry> ROOT::RNTupleModel::CreateBareEntry() const
542{
543 switch (fModelState) {
544 case EState::kBuilding: throw RException(R__FAIL("invalid attempt to create entry of unfrozen model"));
545 case EState::kExpired: throw RException(R__FAIL("invalid attempt to create entry of expired model"));
546 case EState::kFrozen: break;
547 }
548
549 auto entry = std::unique_ptr<ROOT::REntry>(new ROOT::REntry(fModelId, fSchemaId));
550 for (const auto &f : fFieldZero->GetMutableSubfields()) {
551 entry->AddValue(f->BindValue(nullptr));
552 }
553 for (const auto &f : fRegisteredSubfields) {
554 AddSubfield(f, *entry, false /* initializeValue */);
555 }
556 return entry;
557}
558
559std::unique_ptr<ROOT::Detail::RRawPtrWriteEntry> ROOT::RNTupleModel::CreateRawPtrWriteEntry() const
560{
561 switch (fModelState) {
562 case EState::kBuilding: throw RException(R__FAIL("invalid attempt to create entry of unfrozen model"));
563 case EState::kExpired: throw RException(R__FAIL("invalid attempt to create entry of expired model"));
564 case EState::kFrozen: break;
565 }
566
567 auto entry = std::unique_ptr<Detail::RRawPtrWriteEntry>(new Detail::RRawPtrWriteEntry(fModelId, fSchemaId));
568 for (const auto &f : fFieldZero->GetMutableSubfields()) {
569 entry->AddField(*f);
570 }
571 // fRegisteredSubfields are not relevant for writing
572 return entry;
573}
574
575ROOT::RFieldToken ROOT::RNTupleModel::GetToken(std::string_view fieldName) const
576{
577 const auto &topLevelFields = fFieldZero->GetConstSubfields();
578 auto it = std::find_if(topLevelFields.begin(), topLevelFields.end(),
579 [&fieldName](const ROOT::RFieldBase *f) { return f->GetFieldName() == fieldName; });
580
581 if (it == topLevelFields.end()) {
582 throw RException(R__FAIL("invalid field name: " + std::string(fieldName)));
583 }
584 return ROOT::RFieldToken(std::distance(topLevelFields.begin(), it), fSchemaId);
585}
586
588{
589 switch (fModelState) {
590 case EState::kBuilding: throw RException(R__FAIL("invalid attempt to create bulk of unfrozen model"));
591 case EState::kExpired: throw RException(R__FAIL("invalid attempt to create bulk of expired model"));
592 case EState::kFrozen: break;
593 }
594
595 auto f = FindField(fieldName);
596 if (!f)
597 throw RException(R__FAIL("no such field: " + std::string(fieldName)));
598 return f->CreateBulk();
599}
600
602{
603 switch (fModelState) {
604 case EState::kExpired: return;
605 case EState::kBuilding: throw RException(R__FAIL("invalid attempt to expire unfrozen model"));
606 case EState::kFrozen: break;
607 }
608
609 // Ensure that Fill() does not work anymore
610 fModelId = 0;
612}
613
615{
616 switch (fModelState) {
617 case EState::kBuilding: return;
618 case EState::kExpired: throw RException(R__FAIL("invalid attempt to unfreeze expired model"));
619 case EState::kFrozen: break;
620 }
621
622 fModelId = GetNewModelId();
624 if (fDefaultEntry) {
625 fDefaultEntry->fModelId = fModelId;
626 fDefaultEntry->fSchemaId = fSchemaId;
627 }
629}
630
632{
634 throw RException(R__FAIL("invalid attempt to freeze expired model"));
635
637}
638
639void ROOT::RNTupleModel::SetDescription(std::string_view description)
640{
642 fDescription = std::string(description);
643}
644
646{
647 std::size_t bytes = 0;
648 std::size_t minPageBufferSize = 0;
649
650 // Start with the size of the page buffers used to fill a persistent sink
651 std::size_t nColumns = 0;
652 for (auto &&field : *fFieldZero) {
653 for (const auto &r : field.GetColumnRepresentatives()) {
654 nColumns += r.size();
655 minPageBufferSize += r.size() * options.GetInitialUnzippedPageSize();
656 }
657 }
658 bytes = std::min(options.GetPageBufferBudget(), nColumns * options.GetMaxUnzippedPageSize());
659
660 // If using buffered writing with RPageSinkBuf, we create a clone of the model and keep at least
661 // the compressed pages in memory.
662 if (options.GetUseBufferedWrite()) {
663 bytes += minPageBufferSize;
664 // Use the target cluster size as an estimate for all compressed pages combined.
665 bytes += options.GetApproxZippedClusterSize();
666 int compression = options.GetCompression();
667 if (compression != 0 && options.GetUseImplicitMT() == ROOT::RNTupleWriteOptions::EImplicitMT::kDefault) {
668 // With IMT, compression happens asynchronously which means that the uncompressed pages also stay around. Use a
669 // compression factor of 2x as a very rough estimate.
670 bytes += 2 * options.GetApproxZippedClusterSize();
671 }
672 }
673
674 return bytes;
675}
ROOT::R::TRInterface & r
Definition Object.C:4
#define R__FORWARD_ERROR(res)
Short-hand to return an RResult<T> in an error state (i.e. after checking).
Definition RError.hxx:303
#define R__FORWARD_RESULT(res)
Short-hand to return an RResult<T> value from a subroutine to the calling stack frame.
Definition RError.hxx:301
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
Definition RError.hxx:299
#define R__LOG_WARNING(...)
Definition RLogger.hxx:357
#define f(i)
Definition RSha256.hxx:104
A container of const raw pointers, corresponding to a row in the data set.
Container for the projected fields of an RNTupleModel.
const ROOT::RFieldBase * GetSourceField(const ROOT::RFieldBase *target) const
std::unordered_map< const ROOT::RFieldBase *, const ROOT::RFieldBase * > FieldMap_t
The map keys are the projected target fields, the map values are the backing source fields Note that ...
RResult< void > Add(std::unique_ptr< ROOT::RFieldBase > field, const FieldMap_t &fieldMap)
Adds a new projected field.
RProjectedFields(std::unique_ptr< ROOT::RFieldZero > fieldZero)
std::unique_ptr< RProjectedFields > Clone(const RNTupleModel &newModel) const
Clones this container and all the projected fields it owns.
FieldMap_t fFieldMap
Maps the source fields from fModel to the target projected fields attached to fFieldZero.
std::unique_ptr< ROOT::RFieldZero > fFieldZero
The projected fields are attached to this zero field.
RResult< void > EnsureValidMapping(const ROOT::RFieldBase *target, const FieldMap_t &fieldMap)
Asserts that the passed field is a valid target of the source field provided in the field map.
An artificial field that transforms an RNTuple column that contains the offset of collections into co...
Definition RField.hxx:347
The REntry is a collection of values in an RNTuple corresponding to a complete row in the data set.
Definition REntry.hxx:54
void AddValue(ROOT::RFieldBase::RValue &&value)
Definition REntry.hxx:77
Base class for all ROOT issued exceptions.
Definition RError.hxx:78
Points to an array of objects with RNTuple I/O support, used for bulk reading.
A field translates read and write calls from/to underlying columns to/from tree values.
ROOT::ENTupleStructure GetStructure() const
const RFieldBase * GetParent() const
const std::string & GetFieldName() const
RColumnRepresentations::Selection_t GetColumnRepresentatives() const
Returns the fColumnRepresentative pointee or, if unset (always the case for artificial fields),...
const std::string & GetTypeName() const
A field token identifies a (sub)field in an entry.
The container field for an ntuple model, which itself has no physical representation.
Definition RField.hxx:58
ROOT::RNTupleWriter & fWriter
std::uint64_t fNewModelId
The model ID after committing.
void CommitUpdate()
Commit changes since the last call to BeginUpdate().
RResult< void > AddProjectedField(std::unique_ptr< ROOT::RFieldBase > field, FieldMappingFunc_t mapping)
RUpdater(ROOT::RNTupleWriter &writer)
Internal::RNTupleModelChangeset fOpenChangeset
void AddField(std::unique_ptr< ROOT::RFieldBase > field, std::string_view parentName="")
void BeginUpdate()
Begin a new set of alterations to the underlying model.
The RNTupleModel encapulates the schema of an RNTuple.
std::unique_ptr< REntry > CreateEntry() const
Creates a new entry with default values for each field.
RNTupleModel(std::unique_ptr< ROOT::RFieldZero > fieldZero)
void AddSubfield(std::string_view fieldName, ROOT::REntry &entry, bool initializeValue=true) const
Add a subfield to the provided entry.
std::unique_ptr< RNTupleModel > Clone() const
std::uint64_t fModelId
Every model has a unique ID to distinguish it from other models.
void EnsureValidFieldName(std::string_view fieldName)
Checks that user-provided field names are valid in the context of this RNTupleModel.
ROOT::RFieldZero & GetMutableFieldZero()
Retrieves the field zero of this model, i.e.
std::unordered_set< std::string > fRegisteredSubfields
Keeps track of which subfields have been registered to be included in entries belonging to this model...
std::size_t EstimateWriteMemoryUsage(const ROOT::RNTupleWriteOptions &options=ROOT::RNTupleWriteOptions()) const
Estimate the memory usage for this model during writing.
EState fModelState
Changed by Freeze() / Unfreeze() and by the RUpdater.
void Unfreeze()
Transitions an RNTupleModel from the frozen state back to the building state, invalidating all previo...
REntry & GetDefaultEntry()
Retrieves the default entry of this model.
ROOT::RFieldBase::RBulkValues CreateBulk(std::string_view fieldName) const
Calls the given field's CreateBulk() method. Throws an RException if no field with the given name exi...
void EnsureNotFrozen() const
Throws an RException if fFrozen is true.
void AddField(std::unique_ptr< ROOT::RFieldBase > field)
Adds a field whose type is not known at compile time.
static std::unique_ptr< RNTupleModel > Create()
std::string fDescription
Free text set by the user.
bool IsFrozen() const
void SetDescription(std::string_view description)
std::unique_ptr< Internal::RProjectedFields > fProjectedFields
The set of projected top-level fields.
std::unique_ptr< Detail::RRawPtrWriteEntry > CreateRawPtrWriteEntry() const
RResult< void > AddProjectedField(std::unique_ptr< ROOT::RFieldBase > field, FieldMappingFunc_t mapping)
Adds a top-level field based on existing fields.
ROOT::RFieldToken GetToken(std::string_view fieldName) const
Creates a token to be used in REntry methods to address a field present in the entry.
std::unordered_set< std::string > fFieldNames
Keeps track of which field names are taken, including projected field names.
std::unique_ptr< ROOT::RFieldZero > fFieldZero
Hierarchy of fields consisting of simple types and collections (sub trees).
void EnsureNotBare() const
Throws an RException if fDefaultEntry is nullptr.
void RegisterSubfield(std::string_view qualifiedFieldName)
Register a subfield so it can be accessed directly from entries belonging to the model.
ROOT::RFieldBase * FindField(std::string_view fieldName) const
The field name can be a top-level field or a nested field. Returns nullptr if the field is not in the...
std::unique_ptr< ROOT::REntry > fDefaultEntry
Contains field values corresponding to the created top-level fields, as well as registered subfields.
void Freeze()
Transitions an RNTupleModel from the building state to the frozen state, disabling adding additional ...
ROOT::RFieldBase & GetMutableField(std::string_view fieldName)
Retrieves the field with fully-qualified name fieldName.
static std::unique_ptr< RNTupleModel > CreateBare()
Creates a "bare model", i.e. an RNTupleModel with no default entry.
std::function< std::string(const std::string &)> FieldMappingFunc_t
User-provided function that describes the mapping of existing source fields to projected fields in te...
void Expire()
Transitions an RNTupleModel from the frozen state to the expired state, invalidating all previously c...
std::uint64_t fSchemaId
Models have a separate schema ID to remember that the clone of a frozen model still has the same sche...
const ROOT::RFieldBase & GetConstField(std::string_view fieldName) const
std::unique_ptr< REntry > CreateBareEntry() const
Creates a "bare entry", i.e.
Common user-tunable settings for storing RNTuples.
std::size_t GetPageBufferBudget() const
std::size_t GetApproxZippedClusterSize() const
std::size_t GetMaxUnzippedPageSize() const
std::uint32_t GetCompression() const
EImplicitMT GetUseImplicitMT() const
std::size_t GetInitialUnzippedPageSize() const
An RNTuple that gets filled with entries (data) and writes them to storage.
The field for an untyped record.
void Throw()
Throws an RException with fError.
Definition RError.cxx:61
The class is used as a return type for operations that can fail; wraps a value of type T or an RError...
Definition RError.hxx:197
ROOT::RFieldZero & GetFieldZeroOfModel(RNTupleModel &model)
RResult< void > EnsureValidNameForRNTuple(std::string_view name, std::string_view where)
Check whether a given string is a valid name according to the RNTuple specification.
ROOT::RLogChannel & NTupleLog()
Log channel for RNTuple diagnostics.
void AddItemToRecord(RRecordField &record, std::unique_ptr< RFieldBase > newItem)
Definition RField.cxx:641
RProjectedFields & GetProjectedFieldsOfModel(RNTupleModel &model)
std::vector< std::string > Split(std::string_view str, std::string_view delims, bool skipEmpty=false)
Splits a string at each character in delims.
The incremental changes to a RNTupleModel.
void AddField(std::unique_ptr< ROOT::RFieldBase > field, std::string_view parentName="")
std::vector< ROOT::RFieldBase * > fAddedProjectedFields
Points to the projected fields in fModel that were added as part of an updater transaction.
std::vector< ROOT::RFieldBase * > fAddedFields
Points to the fields in fModel that were added as part of an updater transaction.
ROOT::RResult< void > AddProjectedField(std::unique_ptr< ROOT::RFieldBase > field, RNTupleModel::FieldMappingFunc_t mapping)
ROOT::RRecordField * GetParentRecordField(std::string_view parentName) const