Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RFieldBase.hxx
Go to the documentation of this file.
1/// \file ROOT/RFieldBase.hxx
2/// \author Jakob Blomer <jblomer@cern.ch>
3/// \date 2018-10-09
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#ifndef ROOT_RFieldBase
14#define ROOT_RFieldBase
15
16#include <ROOT/BitUtils.hxx>
17#include <ROOT/RColumn.hxx>
19#include <ROOT/RError.hxx>
20#include <ROOT/RFieldUtils.hxx>
21#include <ROOT/RNTupleRange.hxx>
22#include <ROOT/RNTupleTypes.hxx>
23
24#include <atomic>
25#include <cassert>
26#include <cstddef>
27#include <functional>
28#include <iterator>
29#include <memory>
30#include <new>
31#include <string>
32#include <string_view>
33#include <typeinfo>
34#include <type_traits>
35#include <vector>
36
37namespace ROOT {
38
39class REntry;
40class RFieldBase;
41class RClassField;
42
43namespace Detail {
44class RFieldVisitor;
45class RRawPtrWriteEntry;
46} // namespace Detail
47
48namespace Experimental {
49class RNTupleAttrSetReader;
50}
51
52namespace Internal {
53
54class RPageSink;
55class RPageSource;
56struct RFieldCallbackInjector;
57struct RFieldRepresentationModifier;
58
59// TODO(jblomer): find a better way to not have these methods in the RFieldBase public API
60void CallFlushColumnsOnField(RFieldBase &);
61void CallCommitClusterOnField(RFieldBase &);
65CallFieldBaseCreate(const std::string &fieldName, const std::string &typeName, const ROOT::RCreateFieldOptions &options,
67void CallSetArtificialOnField(RFieldBase &);
68
69} // namespace Internal
70
71namespace Experimental::Internal {
72struct RNTupleAttrEntry;
73}
74
75// clang-format off
76/**
77\class ROOT::RFieldBase
78\ingroup NTuple
79\brief A field translates read and write calls from/to underlying columns to/from tree values
80
81A field is a serializable C++ type or a container for a collection of subfields. The RFieldBase and its
82type-safe descendants provide the object to column mapper. They map C++ objects to primitive columns. The
83mapping is trivial for simple types such as 'double'. Complex types resolve to multiple primitive columns.
84The field knows based on its type and the field name the type(s) and name(s) of the columns.
85
86Note: the class hierarchy starting at RFieldBase is not meant to be extended by user-provided child classes.
87This is and can only be partially enforced through C++.
88*/
89// clang-format on
91 friend class RFieldZero; // to reset fParent pointer in ReleaseSubfields()
92 friend class ROOT::Detail::RRawPtrWriteEntry; // to call Append()
93 friend class ROOT::Experimental::RNTupleAttrSetReader; // for field->Read() in LoadEntry()
94 friend struct ROOT::Internal::RFieldCallbackInjector; // used for unit tests
95 friend struct ROOT::Internal::RFieldRepresentationModifier; // used for unit tests
101 Internal::CallFieldBaseCreate(const std::string &fieldName, const std::string &typeName,
102 const ROOT::RCreateFieldOptions &options, const ROOT::RNTupleDescriptor *desc,
103 ROOT::DescriptorId_t fieldId);
105
106 using ReadCallback_t = std::function<void(void *)>;
107
108protected:
109 /// A functor to release the memory acquired by CreateValue() (memory and constructor).
110 /// This implementation works for types with a trivial destructor. More complex fields implement a derived deleter.
111 /// The deleter is operational without the field object and thus can be used to destruct/release a value after
112 /// the field has been destructed.
113 class RDeleter {
114 std::size_t fAlignment;
115 void DeleteAligned(void *objPtr) const; // outlined to make Windows happy
116
117 public:
118 explicit RDeleter(std::size_t align) : fAlignment(align) { assert(Internal::IsValidAlignment(align)); }
119 virtual ~RDeleter() = default;
120 virtual void operator()(void *objPtr, bool dtorOnly)
121 {
122 if (dtorOnly)
123 return;
124
125 // Match operator new in RFieldBase::CreateObjectRawPtr()
126 if (fAlignment <= sizeof(std::max_align_t)) {
127 operator delete(objPtr);
128 } else {
129 DeleteAligned(objPtr);
130 }
131 }
132 };
133
134 /// A deleter for templated RFieldBase descendents where the value type is known.
135 template <typename T>
136 class RTypedDeleter : public RDeleter {
137 public:
138 RTypedDeleter() : RDeleter(alignof(T)) {}
139 void operator()(void *objPtr, bool dtorOnly) final
140 {
141 std::destroy_at(static_cast<T *>(objPtr));
142 RDeleter::operator()(objPtr, dtorOnly);
143 }
144 };
145
146 // We cannot directly use RFieldBase::RDeleter as a shared pointer deleter due to splicing. We use this
147 // wrapper class to store a polymorphic pointer to the actual deleter.
149 std::unique_ptr<RFieldBase::RDeleter> fDeleter;
150 void operator()(void *objPtr) { fDeleter->operator()(objPtr, false /* dtorOnly*/); }
151 explicit RSharedPtrDeleter(std::unique_ptr<RFieldBase::RDeleter> deleter) : fDeleter(std::move(deleter)) {}
152 };
153
154public:
155 static constexpr std::uint32_t kInvalidTypeVersion = -1U;
156 enum {
157 /// No constructor needs to be called, i.e. any bit pattern in the allocated memory represents a valid type
158 /// A trivially constructible field has a no-op ConstructValue() implementation
160 /// The type is cleaned up just by freeing its memory. I.e. the destructor performs a no-op.
162 /// A field of a fundamental type that can be directly mapped via RField<T>::Map(), i.e. maps as-is to a single
163 /// column
165 /// The TClass checksum is set and valid
167 /// This field is an instance of RInvalidField and can be safely `static_cast` to it
169 /// This field is a user defined type that was missing dictionaries and was reconstructed from the on-disk
170 /// information
172 /// Can attach new item fields even when already connected
174 /// The field represents a collection in SoA layout
176
177 /// Shorthand for types that are both trivially constructible and destructible
179 };
180
181 using ColumnRepresentation_t = std::vector<ROOT::ENTupleColumnType>;
182
183 /// During its lifetime, a field undergoes the following possible state transitions:
184 ///
185 /// [*] --> Unconnected --> ConnectedToSink ----
186 /// | | |
187 /// | --> ConnectedToSource ---> [*]
188 /// | |
189 /// -------------------------------
190 enum class EState {
194 };
195
196 // clang-format off
197 /**
198 \class ROOT::RFieldBase::RColumnRepresentations
199 \ingroup NTuple
200 \brief The list of column representations a field can have.
201
202 Some fields have multiple possible column representations, e.g. with or without split encoding.
203 All column representations supported for writing also need to be supported for reading. In addition,
204 fields can support extra column representations for reading only, e.g. a 64bit integer reading from a
205 32bit column.
206 The defined column representations must be supported by corresponding column packing/unpacking implementations,
207 i.e. for the example above, the unpacking of 32bit ints to 64bit pages must be implemented in RColumnElement.hxx
208 */
209 // clang-format on
211 public:
212 /// A list of column representations
213 using Selection_t = std::vector<ColumnRepresentation_t>;
214
216 RColumnRepresentations(const Selection_t &serializationTypes, const Selection_t &deserializationExtraTypes);
217
218 /// The first column list from `fSerializationTypes` is the default for writing.
222
223 private:
225 /// The union of the serialization types and the deserialization extra types passed during construction.
226 /// Duplicates the serialization types list but the benefit is that GetDeserializationTypes() does not need to
227 /// compile the list.
229 }; // class RColumnRepresentations
230
231 class RValue;
232 class RBulkValues;
233
234private:
235 /// The field name relative to its parent field
236 std::string fName;
237 /// The C++ type captured by this field
238 std::string fType;
239 /// The role of this field in the data model structure
241 /// For fixed sized arrays, the array length
242 std::size_t fNRepetitions;
243 /// A field qualifies as simple if it is mappable (which implies it has a single principal column),
244 /// and it is not an artificial field and has no post-read callback
246 /// A field that is not backed on disk but computed, e.g. a default-constructed missing field or
247 /// a field whose data is created by I/O customization rules. Subfields of artificial fields are
248 /// artificial, too.
249 bool fIsArtificial = false;
250 /// When the columns are connected to a page source or page sink, the field represents a field id in the
251 /// corresponding RNTuple descriptor. This on-disk ID is set in RPageSink::Create() for writing and by
252 /// RFieldDescriptor::CreateField() when recreating a field / model from the stored descriptor.
254 /// Free text set by the user
255 std::string fDescription;
256 /// Changed by ConnectTo[Sink,Source], reset by Clone()
258
260 {
261 for (const auto &func : fReadCallbacks)
262 func(target);
263 }
264
265 /// Translate an entry index to a column element index of the principal column. This function
266 /// takes into account the role and number of repetitions on each level of the field hierarchy as follows:
267 /// - Top level fields: element index == entry index
268 /// - Record fields propagate their principal column index to the principal columns of direct descendant fields
269 /// - Collection and variant fields set the principal column index of their children to 0
270 ///
271 /// The column element index also depends on the number of repetitions of each field in the hierarchy, e.g., given a
272 /// field with type `std::array<std::array<float, 4>, 2>`, this function called with `globalIndex == 1`
273 /// returns 8 for the innermost field.
275
276 /// Flushes data from active columns
277 void FlushColumns();
278 /// Flushes data from active columns to disk and calls CommitClusterImpl()
279 void CommitCluster();
280 /// Fields and their columns live in the void until connected to a physical page storage. Only once connected, data
281 /// can be read or written. In order to find the field in the page storage, the field's on-disk ID has to be set.
282 /// \param firstEntry The global index of the first entry with on-disk data for the connected field
283 void ConnectPageSink(ROOT::Internal::RPageSink &pageSink, ROOT::NTupleSize_t firstEntry = 0);
284 /// Connects the field and its subfield tree to the given page source. Once connected, data can be read.
285 /// Only unconnected fields may be connected, i.e. the method is not idempotent. The field ID has to be set prior to
286 /// calling this function. For subfields, a field ID may or may not be set. If the field ID is unset, it will be
287 /// determined using the page source descriptor, based on the parent field ID and the subfield name.
289
291 {
292 fIsSimple = false;
293 fIsArtificial = true;
294 for (auto &field : fSubfields) {
295 field->SetArtificial();
296 }
297 }
298
299protected:
300 struct RBulkSpec;
301
302 /// Bits used in CompareOnDisk()
303 enum {
304 /// The in-memory field and the on-disk field differ in the field version
306 /// The in-memory field and the on-disk field differ in the type version
308 /// The in-memory field and the on-disk field differ in their structural roles
310 /// The in-memory field and the on-disk field have different type names
312 /// The in-memory field and the on-disk field have different repetition counts
313 kDiffNRepetitions = 0x10
314 };
315
316 /// Collections and classes own subfields
317 std::vector<std::unique_ptr<RFieldBase>> fSubfields;
318 /// Subfields point to their mother field
320 /// All fields that have columns have a distinct main column. E.g., for simple fields (`float`, `int`, ...), the
321 /// principal column corresponds to the field type. For collection fields except fixed-sized arrays,
322 /// the main column is the offset field. Class fields have no column of their own.
323 /// When reading, points to any column of the column team of the active representation. Usually, this is just
324 /// the first column.
325 /// When writing, points to the first column index of the currently active (not suppressed) column representation.
327 /// Some fields have a second column in its column representation. In this case, `fAuxiliaryColumn` points into
328 /// `fAvailableColumns` to the column that immediately follows the column `fPrincipalColumn` points to.
330 /// The columns are connected either to a sink or to a source (not to both); they are owned by the field.
331 /// Contains all columns of all representations in order of representation and column index.
332 std::vector<std::unique_ptr<ROOT::Internal::RColumn>> fAvailableColumns;
333 /// Properties of the type that allow for optimizations of collections of that type
334 std::uint32_t fTraits = 0;
335 /// A typedef or using name that was used when creating the field
336 std::string fTypeAlias;
337 /// List of functions to be called after reading a value
338 std::vector<ReadCallback_t> fReadCallbacks;
339 /// C++ type version cached from the descriptor after a call to ConnectPageSource()
341 /// TClass checksum cached from the descriptor after a call to ConnectPageSource(). Only set
342 /// for classes with dictionaries.
343 std::uint32_t fOnDiskTypeChecksum = 0;
344 /// Pointers into the static vector returned by RColumnRepresentations::GetSerializationTypes() when
345 /// SetColumnRepresentatives() is called. Otherwise (if empty) GetColumnRepresentatives() returns a vector
346 /// with a single element, the default representation. Always empty for artificial fields.
347 std::vector<std::reference_wrapper<const ColumnRepresentation_t>> fColumnRepresentatives;
348
349 /// Factory method for the field's type. The caller owns the returned pointer
350 void *CreateObjectRawPtr() const;
351
352 /// Helpers for generating columns. We use the fact that most fields have the same C++/memory types
353 /// for all their column representations.
354 /// Where possible, we call the helpers not from the header to reduce compilation time.
355 template <std::uint32_t ColumnIndexT, typename HeadT, typename... TailTs>
356 void GenerateColumnsImpl(const ColumnRepresentation_t &representation, std::uint16_t representationIndex)
357 {
358 assert(ColumnIndexT < representation.size());
359 auto &column = fAvailableColumns.emplace_back(
360 ROOT::Internal::RColumn::Create<HeadT>(representation[ColumnIndexT], ColumnIndexT, representationIndex));
361
362 // Initially, the first two columns become the active column representation
363 if (representationIndex == 0 && !fPrincipalColumn) {
364 fPrincipalColumn = column.get();
365 } else if (representationIndex == 0 && !fAuxiliaryColumn) {
366 fAuxiliaryColumn = column.get();
367 } else {
368 // We currently have no fields with more than 2 columns in its column representation
369 R__ASSERT(representationIndex > 0);
370 }
371
372 if constexpr (sizeof...(TailTs))
373 GenerateColumnsImpl<ColumnIndexT + 1, TailTs...>(representation, representationIndex);
374 }
375
376 /// For writing, use the currently set column representative
377 template <typename... ColumnCppTs>
379 {
380 if (fColumnRepresentatives.empty()) {
381 fAvailableColumns.reserve(sizeof...(ColumnCppTs));
383 } else {
384 const auto N = fColumnRepresentatives.size();
385 fAvailableColumns.reserve(N * sizeof...(ColumnCppTs));
386 for (unsigned i = 0; i < N; ++i) {
387 GenerateColumnsImpl<0, ColumnCppTs...>(fColumnRepresentatives[i].get(), i);
388 }
389 }
390 }
391
392 /// For reading, use the on-disk column list
393 template <typename... ColumnCppTs>
395 {
396 std::uint16_t representationIndex = 0;
397 do {
398 const auto &onDiskTypes = EnsureCompatibleColumnTypes(desc, representationIndex);
399 if (onDiskTypes.empty())
400 break;
401 GenerateColumnsImpl<0, ColumnCppTs...>(onDiskTypes, representationIndex);
402 fColumnRepresentatives.emplace_back(onDiskTypes);
403 if (representationIndex > 0) {
404 for (std::size_t i = 0; i < sizeof...(ColumnCppTs); ++i) {
405 fAvailableColumns[i]->MergeTeams(
406 *fAvailableColumns[representationIndex * sizeof...(ColumnCppTs) + i].get());
407 }
408 }
409 representationIndex++;
410 } while (true);
411 }
412
413 /// Implementations in derived classes should return a static RColumnRepresentations object. The default
414 /// implementation does not attach any columns to the field.
415 virtual const RColumnRepresentations &GetColumnRepresentations() const;
416 /// Implementations in derived classes should create the backing columns corresponding to the field type for
417 /// writing. The default implementation does not attach any columns to the field.
418 virtual void GenerateColumns() {}
419 /// Implementations in derived classes should create the backing columns corresponding to the field type for reading.
420 /// The default implementation does not attach any columns to the field. The method should check, using the page
421 /// source and `fOnDiskId`, if the column types match and throw if they don't.
422 virtual void GenerateColumns(const ROOT::RNTupleDescriptor & /*desc*/) {}
423 /// Returns the on-disk column types found in the provided descriptor for `fOnDiskId` and the given
424 /// representation index. If there are no columns for the given representation index, return an empty
425 /// ColumnRepresentation_t list. Otherwise, the returned reference points into the static array returned by
426 /// GetColumnRepresentations().
427 /// Throws an exception if the types on disk don't match any of the deserialization types from
428 /// GetColumnRepresentations().
430 EnsureCompatibleColumnTypes(const ROOT::RNTupleDescriptor &desc, std::uint16_t representationIndex) const;
431 /// When connecting a field to a page sink, the field's default column representation is subject
432 /// to adjustment according to the write options. E.g., if compression is turned off, encoded columns
433 /// are changed to their unencoded counterparts.
435
436 /// Called by Clone(), which additionally copies the on-disk ID
437 virtual std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const = 0;
438
439 /// Constructs value in a given location of size at least GetValueSize(). Called by the base class' CreateValue().
440 virtual void ConstructValue(void *where) const = 0;
441 virtual std::unique_ptr<RDeleter> GetDeleter() const { return std::make_unique<RDeleter>(GetAlignment()); }
442 /// Allow derived classes to call ConstructValue(void *) and GetDeleter() on other (sub)fields.
443 static void CallConstructValueOn(const RFieldBase &other, void *where) { other.ConstructValue(where); }
444 static std::unique_ptr<RDeleter> GetDeleterOf(const RFieldBase &other) { return other.GetDeleter(); }
445
446 /// Allow parents to mark their childs as artificial fields (used in class and record fields)
447 static void CallSetArtificialOn(RFieldBase &other) { other.SetArtificial(); }
448
449 /// Operations on values of complex types, e.g. ones that involve multiple columns or for which no direct
450 /// column type exists.
451 virtual std::size_t AppendImpl(const void *from);
452 virtual void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to);
453 virtual void ReadInClusterImpl(RNTupleLocalIndex localIndex, void *to);
454
455 /// Write the given value into columns. The value object has to be of the same type as the field.
456 /// Returns the number of uncompressed bytes written.
457 std::size_t Append(const void *from);
458
459 /// Populate a single value with data from the field. The memory location pointed to by to needs to be of the
460 /// fitting type. The fast path is conditioned by the field qualifying as simple, i.e. maps as-is
461 /// to a single column and has no read callback.
462 void Read(ROOT::NTupleSize_t globalIndex, void *to)
463 {
464 if (fIsSimple)
465 return (void)fPrincipalColumn->Read(globalIndex, to);
466
467 if (!fIsArtificial) {
469 fPrincipalColumn->Read(globalIndex, to);
470 else
471 ReadGlobalImpl(globalIndex, to);
472 }
473 if (R__unlikely(!fReadCallbacks.empty()))
475 }
476
477 /// Populate a single value with data from the field. The memory location pointed to by to needs to be of the
478 /// fitting type. The fast path is conditioned by the field qualifying as simple, i.e. maps as-is
479 /// to a single column and has no read callback.
480 void Read(RNTupleLocalIndex localIndex, void *to)
481 {
482 if (fIsSimple)
483 return (void)fPrincipalColumn->Read(localIndex, to);
484
485 if (!fIsArtificial) {
487 fPrincipalColumn->Read(localIndex, to);
488 else
489 ReadInClusterImpl(localIndex, to);
490 }
491 if (R__unlikely(!fReadCallbacks.empty()))
493 }
494
495 /// General implementation of bulk read. Loop over the required range and read values that are required
496 /// and not already present. Derived classes may implement more optimized versions of this method.
497 /// See ReadBulk() for the return value.
498 virtual std::size_t ReadBulkImpl(const RBulkSpec &bulkSpec);
499
500 /// Returns the number of newly available values, that is the number of bools in `bulkSpec.fMaskAvail` that
501 /// flipped from false to true. As a special return value, `kAllSet` can be used if all values are read
502 /// independent from the masks.
503 std::size_t ReadBulk(const RBulkSpec &bulkSpec);
504
505 /// Allow derived classes to call Append() and Read() on other (sub)fields.
506 static std::size_t CallAppendOn(RFieldBase &other, const void *from) { return other.Append(from); }
507 static void CallReadOn(RFieldBase &other, RNTupleLocalIndex localIndex, void *to) { other.Read(localIndex, to); }
508 static void CallReadOn(RFieldBase &other, ROOT::NTupleSize_t globalIndex, void *to) { other.Read(globalIndex, to); }
509 static void *CallCreateObjectRawPtrOn(RFieldBase &other) { return other.CreateObjectRawPtr(); }
510
511 /// Fields may need direct access to the principal column of their subfields, e.g. in RRVecField::ReadBulk()
513
514 /// Set a user-defined function to be called after reading a value, giving a chance to inspect and/or modify the
515 /// value object.
516 /// Returns an index that can be used to remove the callback.
517 size_t AddReadCallback(const ReadCallback_t &func);
518 void RemoveReadCallback(size_t idx);
519
520 // Perform housekeeping tasks for global to cluster-local index translation
521 virtual void CommitClusterImpl() {}
522 // The field can indicate that it needs to register extra type information in the on-disk schema.
523 // In this case, a callback from the page sink to the field will be registered on connect, so that the
524 // extra type information can be collected when the dataset gets committed.
525 virtual bool HasExtraTypeInfo() const { return false; }
526 // The page sink's callback when the data set gets committed will call this method to get the field's extra
527 // type information. This has to happen at the end of writing because the type information may change depending
528 // on the data that's written, e.g. for polymorphic types in the streamer field.
530
531 /// Add a new subfield to the list of nested fields. Throws an exception if childName is non-empty and the passed
532 /// field has a different name.
533 void Attach(std::unique_ptr<RFieldBase> child, std::string_view expectedChildName = "");
534
535 /// Called by ConnectPageSource() before connecting; derived classes may override this as appropriate, e.g.
536 /// for the application of I/O rules. In the process, the field at hand or its subfields may be marked as
537 /// "artifical", i.e. introduced by schema evolution and not backed by on-disk information.
538 /// May return a field substitute that fits the on-disk schema as a replacement for the field at hand.
539 /// A field substitute must read into the same in-memory layout than the original field and field substitutions
540 /// must not be cyclic.
541 virtual std::unique_ptr<RFieldBase> BeforeConnectPageSource(ROOT::Internal::RPageSource & /* source */)
542 {
543 return nullptr;
544 }
545
546 /// For non-artificial fields, check compatibility of the in-memory field and the on-disk field. In the process,
547 /// the field at hand may change its on-disk ID or perform other tasks related to automatic schema evolution.
548 /// If the on-disk field is incompatible with the in-memory field at hand, an exception is thrown.
549 virtual void ReconcileOnDiskField(const RNTupleDescriptor &desc);
550
551 /// Returns a combination of kDiff... flags, indicating peroperties that are different between the field at hand
552 /// and the given on-disk field
553 std::uint32_t CompareOnDiskField(const RFieldDescriptor &fieldDesc, std::uint32_t ignoreBits) const;
554 /// Compares the field to the corresponding on-disk field information in the provided descriptor.
555 /// Throws an exception if the fields don't match.
556 /// Optionally, a set of bits can be provided that should be ignored in the comparison.
557 RResult<void> EnsureMatchingOnDiskField(const RNTupleDescriptor &desc, std::uint32_t ignoreBits = 0) const;
558 /// Convenience wrapper for the common case of calling EnsureMatchinOnDiskField() for collections. Collections
559 /// may differ in type name (most collections schema evolve into each other). An on-disk SoA collection may also
560 /// have any type version whereas all other collections need to have type version 0.
562 /// Many fields accept a range of type prefixes for schema evolution,
563 /// e.g. std::unique_ptr< and std::optional< for nullable fields
565 EnsureMatchingTypePrefix(const RNTupleDescriptor &desc, const std::vector<std::string> &prefixes) const;
566
567 /// Factory method to resurrect a field from the stored on-disk type information. This overload takes an already
568 /// normalized type name and type alias.
569 /// `desc` and `fieldId` must be passed if `options.fEmulateUnknownTypes` is true, otherwise they can be left blank.
571 Create(const std::string &fieldName, const std::string &typeName, const ROOT::RCreateFieldOptions &options,
572 const ROOT::RNTupleDescriptor *desc, ROOT::DescriptorId_t fieldId);
573
574public:
575 template <bool IsConstT>
576 class RSchemaIteratorTemplate;
579
580 // This is used in CreateObject() and is specialized for void
581 template <typename T>
583 using deleter = std::default_delete<T>;
584 };
585
586 /// Used in the return value of the Check() method
588 std::string fFieldName; ///< Qualified field name causing the error
589 std::string fTypeName; ///< Type name corresponding to the (sub)field
590 std::string fErrMsg; ///< Cause of the failure, e.g. unsupported type
591 };
592
593 /// The constructor creates the underlying column objects and connects them to either a sink or a source.
594 /// If `isSimple` is `true`, the trait `kTraitMappable` is automatically set on construction. However, the
595 /// field might be demoted to non-simple if a post-read callback is set.
596 RFieldBase(std::string_view name, std::string_view type, ROOT::ENTupleStructure structure, bool isSimple,
597 std::size_t nRepetitions = 0);
598 RFieldBase(const RFieldBase &) = delete;
599 RFieldBase(RFieldBase &&) = default;
600 RFieldBase &operator=(const RFieldBase &) = delete;
602 virtual ~RFieldBase() = default;
603
604 /// Copies the field and its subfields using a possibly new name and a new, unconnected set of columns
605 std::unique_ptr<RFieldBase> Clone(std::string_view newName) const;
606
607 /// Factory method to create a field from a certain type given as string.
608 /// Note that the provided type name must be a valid C++ type name. Template arguments of templated types
609 /// must be type names or integers (e.g., no expressions).
611 Create(const std::string &fieldName, const std::string &typeName);
612
613 /// Checks if the given type is supported by RNTuple. In case of success, the result vector is empty.
614 /// Otherwise there is an error record for each failing subfield (subtype).
615 static std::vector<RCheckResult> Check(const std::string &fieldName, const std::string &typeName);
616
617 /// Generates an object of the field type and allocates new initialized memory according to the type.
618 /// Implemented at the end of this header because the implementation is using RField<T>::TypeName()
619 /// The returned object can be released with `delete`, i.e. it is valid to call:
620 /// ~~~{.cpp}
621 /// auto ptr = field->CreateObject();
622 /// delete ptr.release();
623 /// ~~~
624 /// Over-aligned types need to delete the returned pointer using the delete operator overload that specifies the
625 /// type's alignment.
626 ///
627 /// Note that CreateObject<void>() is supported. The returned `unique_ptr` has a custom deleter that reports an error
628 /// if it is called. The intended use of the returned `unique_ptr<void>` is to call `release()`. In this way, the
629 /// transfer of pointer ownership is explicit.
630 template <typename T>
631 std::unique_ptr<T, typename RCreateObjectDeleter<T>::deleter> CreateObject() const;
632 /// Generates an object of the field's type, wraps it in a shared pointer and returns it as an RValue connected to
633 /// the field.
635 /// Creates a new, initially empty bulk.
636 /// RBulkValues::ReadBulk() will construct the array of values. The memory of the value array is managed by the
637 /// RBulkValues class.
639 /// Creates a value from a memory location with an already constructed object
640 RValue BindValue(std::shared_ptr<void> objPtr);
641 /// Creates the list of direct child values given an existing value for this field. E.g. a single value for the
642 /// correct `std::variant` or all the elements of a collection. The default implementation assumes no subvalues
643 /// and returns an empty vector.
644 virtual std::vector<RValue> SplitValue(const RValue &value) const;
645 /// What sizeof(T) for this type returns
646 virtual size_t GetValueSize() const = 0;
647 /// What alignof(T) for this type returns
648 virtual size_t GetAlignment() const = 0;
649 std::uint32_t GetTraits() const { return fTraits; }
650 bool HasReadCallbacks() const { return !fReadCallbacks.empty(); }
651
652 const std::string &GetFieldName() const { return fName; }
653 /// Returns the field name and parent field names separated by dots (`grandparent.parent.child`)
654 std::string GetQualifiedFieldName() const;
655 const std::string &GetTypeName() const { return fType; }
656 const std::string &GetTypeAlias() const { return fTypeAlias; }
658 std::size_t GetNRepetitions() const { return fNRepetitions; }
659 const RFieldBase *GetParent() const { return fParent; }
660 std::vector<RFieldBase *> GetMutableSubfields();
661 std::vector<const RFieldBase *> GetConstSubfields() const;
662 bool IsSimple() const { return fIsSimple; }
663 bool IsArtificial() const { return fIsArtificial; }
664 /// Get the field's description
665 const std::string &GetDescription() const { return fDescription; }
666 void SetDescription(std::string_view description);
667 EState GetState() const { return fState; }
668
671
672 /// Returns the `fColumnRepresentative` pointee or, if unset (always the case for artificial fields), the field's
673 /// default representative
675 /// Fixes a column representative. This can only be done _before_ connecting the field to a page sink.
676 /// Otherwise, or if the provided representation is not in the list of GetColumnRepresentations(),
677 /// an exception is thrown
679 /// Whether or not an explicit column representative was set
681
682 /// Indicates an evolution of the mapping scheme from C++ type to columns
683 virtual std::uint32_t GetFieldVersion() const { return 0; }
684 /// Indicates an evolution of the C++ type itself
685 virtual std::uint32_t GetTypeVersion() const { return 0; }
686 /// Return the current TClass reported checksum of this class. Only valid if `kTraitTypeChecksum` is set.
687 virtual std::uint32_t GetTypeChecksum() const { return 0; }
688 /// Return the C++ type version stored in the field descriptor; only valid after a call to ConnectPageSource()
689 std::uint32_t GetOnDiskTypeVersion() const { return fOnDiskTypeVersion; }
690 /// Return checksum stored in the field descriptor; only valid after a call to ConnectPageSource(),
691 /// if the field stored a type checksum
692 std::uint32_t GetOnDiskTypeChecksum() const { return fOnDiskTypeChecksum; }
693
700
701 virtual void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const;
702}; // class RFieldBase
703
704/// Iterates over the subtree of fields in depth-first search order
705template <bool IsConstT>
707private:
708 struct Position {
709 using FieldPtr_t = std::conditional_t<IsConstT, const RFieldBase *, RFieldBase *>;
710 Position() : fFieldPtr(nullptr), fIdxInParent(-1) {}
711 Position(FieldPtr_t fieldPtr, int idxInParent) : fFieldPtr(fieldPtr), fIdxInParent(idxInParent) {}
714 };
715 /// The stack of nodes visited when walking down the tree of fields
716 std::vector<Position> fStack;
717
718public:
720 using iterator_category = std::forward_iterator_tag;
721 using difference_type = std::ptrdiff_t;
722 using value_type = std::conditional_t<IsConstT, const RFieldBase, RFieldBase>;
723 using pointer = std::conditional_t<IsConstT, const RFieldBase *, RFieldBase *>;
724 using reference = std::conditional_t<IsConstT, const RFieldBase &, RFieldBase &>;
725
727 RSchemaIteratorTemplate(pointer val, int idxInParent) { fStack.emplace_back(Position(val, idxInParent)); }
729 /// Given that the iterator points to a valid field which is not the end iterator, go to the next field
730 /// in depth-first search order
731 void Advance()
732 {
733 auto itr = fStack.rbegin();
734 if (!itr->fFieldPtr->fSubfields.empty()) {
735 fStack.emplace_back(Position(itr->fFieldPtr->fSubfields[0].get(), 0));
736 return;
737 }
738
739 unsigned int nextIdxInParent = ++(itr->fIdxInParent);
740 while (nextIdxInParent >= itr->fFieldPtr->fParent->fSubfields.size()) {
741 if (fStack.size() == 1) {
742 itr->fFieldPtr = itr->fFieldPtr->fParent;
743 itr->fIdxInParent = -1;
744 return;
745 }
746 fStack.pop_back();
747 itr = fStack.rbegin();
748 nextIdxInParent = ++(itr->fIdxInParent);
749 }
750 itr->fFieldPtr = itr->fFieldPtr->fParent->fSubfields[nextIdxInParent].get();
751 }
752
753 iterator operator++(int) /* postfix */
754 {
755 auto r = *this;
756 Advance();
757 return r;
758 }
759 iterator &operator++() /* prefix */
760 {
761 Advance();
762 return *this;
763 }
764 reference operator*() const { return *fStack.back().fFieldPtr; }
765 pointer operator->() const { return fStack.back().fFieldPtr; }
766 bool operator==(const iterator &rh) const { return fStack.back().fFieldPtr == rh.fStack.back().fFieldPtr; }
767 bool operator!=(const iterator &rh) const { return fStack.back().fFieldPtr != rh.fStack.back().fFieldPtr; }
768};
769
770/// Points to an object with RNTuple I/O support and keeps a pointer to the corresponding field.
771/// Fields can create RValue objects through RFieldBase::CreateValue(), RFieldBase::BindValue()) or
772/// RFieldBase::SplitValue().
774 friend class RFieldBase;
775 friend class ROOT::REntry;
777
778private:
779 RFieldBase *fField = nullptr; ///< The field that created the RValue
780 /// Set by Bind() or by RFieldBase::CreateValue(), RFieldBase::SplitValue() or RFieldBase::BindValue()
781 std::shared_ptr<void> fObjPtr;
782 mutable std::atomic<const std::type_info *> fTypeInfo = nullptr;
783
784 RValue(RFieldBase *field, std::shared_ptr<void> objPtr) : fField(field), fObjPtr(std::move(objPtr)) {}
785
786public:
787 RValue(const RValue &other) : fField(other.fField), fObjPtr(other.fObjPtr) {}
788 RValue &operator=(const RValue &other)
789 {
790 fField = other.fField;
791 fObjPtr = other.fObjPtr;
792 // We could copy over the cached type info, or just start with a fresh state...
793 fTypeInfo = nullptr;
794 return *this;
795 }
796 RValue(RValue &&other) : fField(other.fField), fObjPtr(std::move(other.fObjPtr)) {}
798 {
799 fField = other.fField;
800 fObjPtr = other.fObjPtr;
801 // We could copy over the cached type info, or just start with a fresh state...
802 fTypeInfo = nullptr;
803 return *this;
804 }
805 ~RValue() = default;
806
807private:
808 template <typename T>
810 {
811 if constexpr (!std::is_void_v<T>) {
812 const std::type_info &ti = typeid(T);
813 // Fast path: if we had a matching type before, try comparing the type_info's. This may still fail in case the
814 // type has a suppressed template argument that may change the typeid.
815 auto *cachedTypeInfo = fTypeInfo.load();
816 if (cachedTypeInfo != nullptr && *cachedTypeInfo == ti) {
817 return;
818 }
819 std::string renormalizedTypeName = Internal::GetRenormalizedTypeName(ti);
820 if (Internal::IsMatchingFieldType(fField->GetTypeName(), renormalizedTypeName, ti)) {
821 fTypeInfo.store(&ti);
822 return;
823 }
824 throw RException(R__FAIL("type mismatch for field \"" + fField->GetFieldName() + "\": expected " +
825 fField->GetTypeName() + ", got " + renormalizedTypeName));
826 }
827 }
828
829 std::size_t Append() { return fField->Append(fObjPtr.get()); }
830
831public:
832 void Read(ROOT::NTupleSize_t globalIndex) { fField->Read(globalIndex, fObjPtr.get()); }
833 void Read(RNTupleLocalIndex localIndex) { fField->Read(localIndex, fObjPtr.get()); }
834
835 void Bind(std::shared_ptr<void> objPtr) { fObjPtr = std::move(objPtr); }
836 void BindRawPtr(void *rawPtr);
837 /// Replace the current object pointer by a pointer to a new object constructed by the field
838 void EmplaceNew() { fObjPtr = fField->CreateValue().GetPtr<void>(); }
839
840 template <typename T>
841 std::shared_ptr<T> GetPtr() const
842 {
843 EnsureMatchingType<T>();
844 return std::static_pointer_cast<T>(fObjPtr);
845 }
846
847 template <typename T>
848 const T &GetRef() const
849 {
850 EnsureMatchingType<T>();
851 return *static_cast<T *>(fObjPtr.get());
852 }
853
854 const RFieldBase &GetField() const { return *fField; }
855};
856
857/// Input parameter to RFieldBase::ReadBulk() and RFieldBase::ReadBulkImpl().
858// See the RBulkValues class documentation for more information.
860 /// Possible return value of ReadBulk() and ReadBulkImpl(), which indicates that the full bulk range was read
861 /// independently of the provided masks.
862 static const std::size_t kAllSet = std::size_t(-1);
863
864 RNTupleLocalIndex fFirstIndex; ///< Start of the bulk range
865 std::size_t fCount = 0; ///< Size of the bulk range
866 /// A bool array of size fCount, indicating the required values in the requested range
867 const bool *fMaskReq = nullptr;
868 bool *fMaskAvail = nullptr; ///< A bool array of size `fCount`, indicating the valid values in fValues
869 /// The destination area, which has to be an array of valid objects of the correct type large enough to hold the bulk
870 /// range.
871 void *fValues = nullptr;
872 /// Reference to memory owned by the RBulkValues class. The field implementing BulkReadImpl() may use `fAuxData` as
873 /// memory that stays persistent between calls.
874 std::vector<unsigned char> *fAuxData = nullptr;
875};
876
877// clang-format off
878/**
879\class ROOT::RFieldBase::RBulkValues
880\ingroup NTuple
881\brief Points to an array of objects with RNTuple I/O support, used for bulk reading.
882
883Similar to RValue, but manages an array of consecutive values. Bulks have to come from the same cluster.
884Bulk I/O works with two bit masks: the mask of all the available entries in the current bulk and the mask
885of the required entries in a bulk read. The idea is that a single bulk may serve multiple read operations
886on the same range, where in each read operation a different subset of values is required.
887The memory of the value array is managed by the RBulkValues class.
888*/
889// clang-format on
891private:
892 friend class RFieldBase;
893
894 RFieldBase *fField = nullptr; ///< The field that created the array of values
895 std::unique_ptr<RFieldBase::RDeleter> fDeleter; /// Cached deleter of fField
896 void *fValues = nullptr; ///< Pointer to the start of the array
897 std::size_t fValueSize = 0; ///< Cached copy of RFieldBase::GetValueSize()
898 std::size_t fCapacity = 0; ///< The size of the array memory block in number of values
899 std::size_t fSize = 0; ///< The number of available values in the array (provided their mask is set)
900 bool fIsAdopted = false; ///< True if the user provides the memory buffer for fValues
901 std::unique_ptr<bool[]> fMaskAvail; ///< Masks invalid values in the array
902 std::size_t fNValidValues = 0; ///< The sum of non-zero elements in the fMask
903 RNTupleLocalIndex fFirstIndex; ///< Index of the first value of the array
904 /// Reading arrays of complex values may require additional memory, for instance for the elements of
905 /// arrays of vectors. A pointer to the `fAuxData` array is passed to the field's BulkRead method.
906 /// The RBulkValues class does not modify the array in-between calls to the field's BulkRead method.
907 std::vector<unsigned char> fAuxData;
908
909 void ReleaseValues();
910 /// Sets a new range for the bulk. If there is enough capacity, the `fValues` array will be reused.
911 /// Otherwise a new array is allocated. After reset, fMaskAvail is false for all values.
912 void Reset(RNTupleLocalIndex firstIndex, std::size_t size);
913
914 bool ContainsRange(RNTupleLocalIndex firstIndex, std::size_t size) const
915 {
916 if (firstIndex.GetClusterId() != fFirstIndex.GetClusterId())
917 return false;
918 return (firstIndex.GetIndexInCluster() >= fFirstIndex.GetIndexInCluster()) &&
919 ((firstIndex.GetIndexInCluster() + size) <= (fFirstIndex.GetIndexInCluster() + fSize));
920 }
921
922 void *GetValuePtrAt(std::size_t idx) const { return reinterpret_cast<unsigned char *>(fValues) + idx * fValueSize; }
923
924 explicit RBulkValues(RFieldBase *field)
925 : fField(field), fDeleter(field->GetDeleter()), fValueSize(field->GetValueSize())
926 {
927 }
928
929public:
930 ~RBulkValues();
931 RBulkValues(const RBulkValues &) = delete;
933 RBulkValues(RBulkValues &&other);
935
936 // Sets `fValues` and `fSize`/`fCapacity` to the given values. The capacity is specified in number of values.
937 // Once a buffer is adopted, an attempt to read more values then available throws an exception.
938 void AdoptBuffer(void *buf, std::size_t capacity);
939
940 /// Reads `size` values from the associated field, starting from `firstIndex`. Note that the index is given
941 /// relative to a certain cluster. The return value points to the array of read objects.
942 /// The `maskReq` parameter is a bool array of at least `size` elements. Only objects for which the mask is
943 /// true are guaranteed to be read in the returned value array. A `nullptr` means to read all elements.
944 void *ReadBulk(RNTupleLocalIndex firstIndex, const bool *maskReq, std::size_t size)
945 {
946 if (!ContainsRange(firstIndex, size))
947 Reset(firstIndex, size);
948
949 // We may read a subrange of the currently available range
951
952 if (fNValidValues == fSize)
953 return GetValuePtrAt(offset);
954
955 RBulkSpec bulkSpec;
956 bulkSpec.fFirstIndex = firstIndex;
957 bulkSpec.fCount = size;
958 bulkSpec.fMaskReq = maskReq;
959 bulkSpec.fMaskAvail = &fMaskAvail[offset];
960 bulkSpec.fValues = GetValuePtrAt(offset);
961 bulkSpec.fAuxData = &fAuxData;
962 auto nRead = fField->ReadBulk(bulkSpec);
963 if (nRead == RBulkSpec::kAllSet) {
964 // We expect that field implementations consistently return kAllSet either in all cases or never. This avoids
965 // the following case where we would have to manually count how many valid values we actually have:
966 // 1. A partial ReadBulk, according to maskReq, with values potentially missing in the middle.
967 // 2. A second ReadBulk that reads a complete subrange. If this returned kAllSet, we don't know how to update
968 // fNValidValues, other than counting. The field should return a concrete number of how many new values it read
969 // in addition to those already present.
970 R__ASSERT((offset == 0) && (size == fSize));
972 } else {
973 fNValidValues += nRead;
974 }
975 return GetValuePtrAt(offset);
976 }
977
978 /// Overload to read all elements in the given cluster range.
979 void *ReadBulk(ROOT::RNTupleLocalRange range) { return ReadBulk(*range.begin(), nullptr, range.size()); }
980};
981
982namespace Internal {
983// At some point, RFieldBase::OnClusterCommit() may allow for a user-defined callback to change the
984// column representation. For now, we inject this for testing and internal use only.
986 static void SetPrimaryColumnRepresentation(RFieldBase &field, std::uint16_t newRepresentationIdx)
987 {
988 R__ASSERT(newRepresentationIdx < field.fColumnRepresentatives.size());
989 const auto N = field.fColumnRepresentatives[0].get().size();
990 R__ASSERT(N >= 1 && N <= 2);
992 field.fPrincipalColumn = field.fAvailableColumns[newRepresentationIdx * N].get();
993 if (field.fAuxiliaryColumn) {
994 R__ASSERT(N == 2);
995 field.fAuxiliaryColumn = field.fAvailableColumns[newRepresentationIdx * N + 1].get();
996 }
997 }
998};
999} // namespace Internal
1000} // namespace ROOT
1001
1002#endif
#define R__unlikely(expr)
Definition RConfig.hxx:568
#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:322
std::size_t capacity
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:139
#define N
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t target
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t child
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
char name[80]
Definition TGX11.cxx:142
Abstract base class for classes implementing the visitor design pattern.
A container of const raw pointers, corresponding to a row in the data set.
Class used to read a RNTupleAttrSet in the context of a RNTupleReader.
A column is a storage-backed array of a simple, fixed-size type, from which pages can be mapped into ...
Definition RColumn.hxx:37
void Read(const ROOT::NTupleSize_t globalIndex, void *to)
Definition RColumn.hxx:158
Abstract interface to write data into an ntuple.
Abstract interface to read data from an ntuple.
The REntry is a collection of values in an RNTuple corresponding to a complete row in the data set.
Definition REntry.hxx:58
Base class for all ROOT issued exceptions.
Definition RError.hxx:78
Field specific extra type information from the header / extenstion header.
Points to an array of objects with RNTuple I/O support, used for bulk reading.
std::unique_ptr< bool[]> fMaskAvail
Masks invalid values in the array.
std::unique_ptr< RFieldBase::RDeleter > fDeleter
void * ReadBulk(RNTupleLocalIndex firstIndex, const bool *maskReq, std::size_t size)
Reads size values from the associated field, starting from firstIndex.
void * GetValuePtrAt(std::size_t idx) const
bool ContainsRange(RNTupleLocalIndex firstIndex, std::size_t size) const
std::size_t fNValidValues
The sum of non-zero elements in the fMask.
bool fIsAdopted
True if the user provides the memory buffer for fValues.
void Reset(RNTupleLocalIndex firstIndex, std::size_t size)
Sets a new range for the bulk.
void * fValues
Cached deleter of fField.
std::size_t fCapacity
The size of the array memory block in number of values.
void * ReadBulk(ROOT::RNTupleLocalRange range)
Overload to read all elements in the given cluster range.
std::size_t fValueSize
Cached copy of RFieldBase::GetValueSize()
RFieldBase * fField
The field that created the array of values.
RBulkValues & operator=(const RBulkValues &)=delete
RBulkValues(RFieldBase *field)
std::size_t fSize
The number of available values in the array (provided their mask is set)
void AdoptBuffer(void *buf, std::size_t capacity)
std::vector< unsigned char > fAuxData
Reading arrays of complex values may require additional memory, for instance for the elements of arra...
RNTupleLocalIndex fFirstIndex
Index of the first value of the array.
RBulkValues(const RBulkValues &)=delete
The list of column representations a field can have.
const Selection_t & GetSerializationTypes() const
const Selection_t & GetDeserializationTypes() const
const ColumnRepresentation_t & GetSerializationDefault() const
The first column list from fSerializationTypes is the default for writing.
std::vector< ColumnRepresentation_t > Selection_t
A list of column representations.
Selection_t fDeserializationTypes
The union of the serialization types and the deserialization extra types passed during construction.
A functor to release the memory acquired by CreateValue() (memory and constructor).
RDeleter(std::size_t align)
virtual void operator()(void *objPtr, bool dtorOnly)
virtual ~RDeleter()=default
void DeleteAligned(void *objPtr) const
Iterates over the subtree of fields in depth-first search order.
std::vector< Position > fStack
The stack of nodes visited when walking down the tree of fields.
bool operator==(const iterator &rh) const
std::conditional_t< IsConstT, const RFieldBase &, RFieldBase & > reference
std::conditional_t< IsConstT, const RFieldBase *, RFieldBase * > pointer
std::conditional_t< IsConstT, const RFieldBase, RFieldBase > value_type
std::forward_iterator_tag iterator_category
void Advance()
Given that the iterator points to a valid field which is not the end iterator, go to the next field i...
bool operator!=(const iterator &rh) const
RSchemaIteratorTemplate(pointer val, int idxInParent)
A deleter for templated RFieldBase descendents where the value type is known.
void operator()(void *objPtr, bool dtorOnly) final
Points to an object with RNTuple I/O support and keeps a pointer to the corresponding field.
std::shared_ptr< void > fObjPtr
Set by Bind() or by RFieldBase::CreateValue(), RFieldBase::SplitValue() or RFieldBase::BindValue()
RValue & operator=(const RValue &other)
void Read(ROOT::NTupleSize_t globalIndex)
void EmplaceNew()
Replace the current object pointer by a pointer to a new object constructed by the field.
void EnsureMatchingType() const
void Bind(std::shared_ptr< void > objPtr)
void Read(RNTupleLocalIndex localIndex)
RValue(const RValue &other)
RValue & operator=(RValue &&other)
const RFieldBase & GetField() const
std::atomic< const std::type_info * > fTypeInfo
std::shared_ptr< T > GetPtr() const
RFieldBase * fField
The field that created the RValue.
RValue(RFieldBase *field, std::shared_ptr< void > objPtr)
void BindRawPtr(void *rawPtr)
const T & GetRef() const
A field translates read and write calls from/to underlying columns to/from tree values.
ROOT::DescriptorId_t fOnDiskId
When the columns are connected to a page source or page sink, the field represents a field id in the ...
ROOT::ENTupleStructure GetStructure() const
virtual size_t GetValueSize() const =0
What sizeof(T) for this type returns.
static constexpr std::uint32_t kInvalidTypeVersion
RSchemaIterator end()
void Attach(std::unique_ptr< RFieldBase > child, std::string_view expectedChildName="")
Add a new subfield to the list of nested fields.
void SetColumnRepresentatives(const RColumnRepresentations::Selection_t &representatives)
Fixes a column representative.
void InvokeReadCallbacks(void *target)
ROOT::Internal::RColumn * fPrincipalColumn
All fields that have columns have a distinct main column.
virtual size_t GetAlignment() const =0
What alignof(T) for this type returns.
virtual std::unique_ptr< RDeleter > GetDeleter() const
virtual void ReconcileOnDiskField(const RNTupleDescriptor &desc)
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
ROOT::NTupleSize_t EntryToColumnElementIndex(ROOT::NTupleSize_t globalIndex) const
Translate an entry index to a column element index of the principal column.
virtual void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const
void FlushColumns()
Flushes data from active columns.
virtual void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to)
std::vector< std::unique_ptr< RFieldBase > > fSubfields
Collections and classes own subfields.
std::uint32_t GetOnDiskTypeVersion() const
Return the C++ type version stored in the field descriptor; only valid after a call to ConnectPageSou...
virtual const RColumnRepresentations & GetColumnRepresentations() const
Implementations in derived classes should return a static RColumnRepresentations object.
EState
During its lifetime, a field undergoes the following possible state transitions:
bool fIsSimple
A field qualifies as simple if it is mappable (which implies it has a single principal column),...
RConstSchemaIterator cbegin() const
std::unique_ptr< T, typename RCreateObjectDeleter< T >::deleter > CreateObject() const
Generates an object of the field type and allocates new initialized memory according to the type.
Definition RField.hxx:589
RFieldBase & operator=(const RFieldBase &)=delete
virtual void GenerateColumns(const ROOT::RNTupleDescriptor &)
Implementations in derived classes should create the backing columns corresponding to the field type ...
void AutoAdjustColumnTypes(const ROOT::RNTupleWriteOptions &options)
When connecting a field to a page sink, the field's default column representation is subject to adjus...
virtual void ConstructValue(void *where) const =0
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
std::vector< const RFieldBase * > GetConstSubfields() const
void SetOnDiskId(ROOT::DescriptorId_t id)
void RemoveReadCallback(size_t idx)
void GenerateColumnsImpl(const ROOT::RNTupleDescriptor &desc)
For reading, use the on-disk column list.
virtual void GenerateColumns()
Implementations in derived classes should create the backing columns corresponding to the field type ...
void Read(RNTupleLocalIndex localIndex, void *to)
Populate a single value with data from the field.
size_t AddReadCallback(const ReadCallback_t &func)
Set a user-defined function to be called after reading a value, giving a chance to inspect and/or mod...
const RFieldBase * GetParent() const
std::vector< RFieldBase * > GetMutableSubfields()
std::string fDescription
Free text set by the user.
static std::unique_ptr< RDeleter > GetDeleterOf(const RFieldBase &other)
static ROOT::Internal::RColumn * GetPrincipalColumnOf(const RFieldBase &other)
Fields may need direct access to the principal column of their subfields, e.g. in RRVecField::ReadBul...
static std::vector< RCheckResult > Check(const std::string &fieldName, const std::string &typeName)
Checks if the given type is supported by RNTuple.
RSchemaIterator begin()
ROOT::Internal::RColumn * fAuxiliaryColumn
Some fields have a second column in its column representation.
RResult< void > EnsureMatchingOnDiskCollection(const RNTupleDescriptor &desc) const
Convenience wrapper for the common case of calling EnsureMatchinOnDiskField() for collections.
RConstSchemaIterator cend() const
std::size_t fNRepetitions
For fixed sized arrays, the array length.
std::function< void(void *)> ReadCallback_t
std::size_t Append(const void *from)
Write the given value into columns.
@ kTraitEmulatedField
This field is a user defined type that was missing dictionaries and was reconstructed from the on-dis...
@ kTraitTrivialType
Shorthand for types that are both trivially constructible and destructible.
@ kTraitTriviallyDestructible
The type is cleaned up just by freeing its memory. I.e. the destructor performs a no-op.
@ kTraitExtensible
Can attach new item fields even when already connected.
@ kTraitTriviallyConstructible
No constructor needs to be called, i.e.
@ kTraitSoACollection
The field represents a collection in SoA layout.
@ kTraitMappable
A field of a fundamental type that can be directly mapped via RField<T>::Map(), i....
@ kTraitInvalidField
This field is an instance of RInvalidField and can be safely static_cast to it.
@ kTraitTypeChecksum
The TClass checksum is set and valid.
RValue CreateValue()
Generates an object of the field's type, wraps it in a shared pointer and returns it as an RValue con...
RSchemaIteratorTemplate< false > RSchemaIterator
const ColumnRepresentation_t & EnsureCompatibleColumnTypes(const ROOT::RNTupleDescriptor &desc, std::uint16_t representationIndex) const
Returns the on-disk column types found in the provided descriptor for fOnDiskId and the given represe...
RFieldBase(RFieldBase &&)=default
virtual std::vector< RValue > SplitValue(const RValue &value) const
Creates the list of direct child values given an existing value for this field.
virtual std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const =0
Called by Clone(), which additionally copies the on-disk ID.
static void CallSetArtificialOn(RFieldBase &other)
Allow parents to mark their childs as artificial fields (used in class and record fields)
std::string GetQualifiedFieldName() const
Returns the field name and parent field names separated by dots (grandparent.parent....
RBulkValues CreateBulk()
Creates a new, initially empty bulk.
const std::string & GetFieldName() const
void ConnectPageSink(ROOT::Internal::RPageSink &pageSink, ROOT::NTupleSize_t firstEntry=0)
Fields and their columns live in the void until connected to a physical page storage.
std::size_t ReadBulk(const RBulkSpec &bulkSpec)
Returns the number of newly available values, that is the number of bools in bulkSpec....
std::vector< ROOT::ENTupleColumnType > ColumnRepresentation_t
std::vector< ReadCallback_t > fReadCallbacks
List of functions to be called after reading a value.
RFieldBase & operator=(RFieldBase &&)=default
RResult< void > EnsureMatchingOnDiskField(const RNTupleDescriptor &desc, std::uint32_t ignoreBits=0) const
Compares the field to the corresponding on-disk field information in the provided descriptor.
const std::string & GetTypeAlias() const
static void CallReadOn(RFieldBase &other, ROOT::NTupleSize_t globalIndex, void *to)
virtual ~RFieldBase()=default
static std::size_t CallAppendOn(RFieldBase &other, const void *from)
Allow derived classes to call Append() and Read() on other (sub)fields.
virtual void ReadInClusterImpl(RNTupleLocalIndex localIndex, void *to)
virtual void CommitClusterImpl()
std::vector< std::reference_wrapper< const ColumnRepresentation_t > > fColumnRepresentatives
Pointers into the static vector returned by RColumnRepresentations::GetSerializationTypes() when SetC...
std::uint32_t fTraits
Properties of the type that allow for optimizations of collections of that type.
friend struct ROOT::Internal::RFieldCallbackInjector
virtual std::size_t AppendImpl(const void *from)
Operations on values of complex types, e.g.
RFieldBase * fParent
Subfields point to their mother field.
std::vector< std::unique_ptr< ROOT::Internal::RColumn > > fAvailableColumns
The columns are connected either to a sink or to a source (not to both); they are owned by the field.
RFieldBase(std::string_view name, std::string_view type, ROOT::ENTupleStructure structure, bool isSimple, std::size_t nRepetitions=0)
The constructor creates the underlying column objects and connects them to either a sink or a source.
EState fState
Changed by ConnectTo[Sink,Source], reset by Clone()
static void * CallCreateObjectRawPtrOn(RFieldBase &other)
bool IsArtificial() const
static RResult< std::unique_ptr< RFieldBase > > Create(const std::string &fieldName, const std::string &typeName, const ROOT::RCreateFieldOptions &options, const ROOT::RNTupleDescriptor *desc, ROOT::DescriptorId_t fieldId)
Factory method to resurrect a field from the stored on-disk type information.
const std::string & GetDescription() const
Get the field's description.
bool HasReadCallbacks() const
std::string fTypeAlias
A typedef or using name that was used when creating the field.
virtual std::uint32_t GetFieldVersion() const
Indicates an evolution of the mapping scheme from C++ type to columns.
virtual std::unique_ptr< RFieldBase > BeforeConnectPageSource(ROOT::Internal::RPageSource &)
Called by ConnectPageSource() before connecting; derived classes may override this as appropriate,...
std::uint32_t CompareOnDiskField(const RFieldDescriptor &fieldDesc, std::uint32_t ignoreBits) const
Returns a combination of kDiff... flags, indicating peroperties that are different between the field ...
std::string fType
The C++ type captured by this field.
RColumnRepresentations::Selection_t GetColumnRepresentatives() const
Returns the fColumnRepresentative pointee or, if unset (always the case for artificial fields),...
RSchemaIteratorTemplate< true > RConstSchemaIterator
virtual std::uint32_t GetTypeChecksum() const
Return the current TClass reported checksum of this class. Only valid if kTraitTypeChecksum is set.
bool IsSimple() const
std::uint32_t GetTraits() const
std::size_t GetNRepetitions() const
std::uint32_t fOnDiskTypeChecksum
TClass checksum cached from the descriptor after a call to ConnectPageSource().
const std::string & GetTypeName() const
ROOT::ENTupleStructure fStructure
The role of this field in the data model structure.
void GenerateColumnsImpl(const ColumnRepresentation_t &representation, std::uint16_t representationIndex)
Helpers for generating columns.
RValue BindValue(std::shared_ptr< void > objPtr)
Creates a value from a memory location with an already constructed object.
void SetDescription(std::string_view description)
static void CallReadOn(RFieldBase &other, RNTupleLocalIndex localIndex, void *to)
ROOT::DescriptorId_t GetOnDiskId() const
std::uint32_t fOnDiskTypeVersion
C++ type version cached from the descriptor after a call to ConnectPageSource()
std::unique_ptr< RFieldBase > Clone(std::string_view newName) const
Copies the field and its subfields using a possibly new name and a new, unconnected set of columns.
std::string fName
The field name relative to its parent field.
void CommitCluster()
Flushes data from active columns to disk and calls CommitClusterImpl()
void ConnectPageSource(ROOT::Internal::RPageSource &pageSource)
Connects the field and its subfield tree to the given page source.
static void CallConstructValueOn(const RFieldBase &other, void *where)
Allow derived classes to call ConstructValue(void *) and GetDeleter() on other (sub)fields.
EState GetState() const
void GenerateColumnsImpl()
For writing, use the currently set column representative.
RResult< void > EnsureMatchingTypePrefix(const RNTupleDescriptor &desc, const std::vector< std::string > &prefixes) const
Many fields accept a range of type prefixes for schema evolution, e.g.
virtual ROOT::RExtraTypeInfoDescriptor GetExtraTypeInfo() const
virtual std::uint32_t GetTypeVersion() const
Indicates an evolution of the C++ type itself.
void * CreateObjectRawPtr() const
Factory method for the field's type. The caller owns the returned pointer.
void Read(ROOT::NTupleSize_t globalIndex, void *to)
Populate a single value with data from the field.
std::uint32_t GetOnDiskTypeChecksum() const
Return checksum stored in the field descriptor; only valid after a call to ConnectPageSource(),...
RFieldBase(const RFieldBase &)=delete
virtual bool HasExtraTypeInfo() const
@ kDiffStructure
The in-memory field and the on-disk field differ in their structural roles.
@ kDiffTypeName
The in-memory field and the on-disk field have different type names.
@ kDiffTypeVersion
The in-memory field and the on-disk field differ in the type version.
@ kDiffFieldVersion
The in-memory field and the on-disk field differ in the field version.
@ kDiffNRepetitions
The in-memory field and the on-disk field have different repetition counts.
bool fIsArtificial
A field that is not backed on disk but computed, e.g.
virtual std::size_t ReadBulkImpl(const RBulkSpec &bulkSpec)
General implementation of bulk read.
bool HasDefaultColumnRepresentative() const
Whether or not an explicit column representative was set.
Metadata stored for every field of an RNTuple.
The container field for an ntuple model, which itself has no physical representation.
Definition RField.hxx:58
The on-storage metadata of an RNTuple.
Addresses a column element or field item relative to a particular cluster, instead of a global NTuple...
ROOT::NTupleSize_t GetIndexInCluster() const
ROOT::DescriptorId_t GetClusterId() const
Used to loop over entries of collections in a single cluster.
ROOT::NTupleSize_t size() const
RIterator begin() const
Common user-tunable settings for storing RNTuples.
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:222
void CallCommitClusterOnField(RFieldBase &)
void CallConnectPageSourceOnField(RFieldBase &, ROOT::Internal::RPageSource &)
ROOT::RResult< std::unique_ptr< ROOT::RFieldBase > > CallFieldBaseCreate(const std::string &fieldName, const std::string &typeName, const ROOT::RCreateFieldOptions &options, const ROOT::RNTupleDescriptor *desc, ROOT::DescriptorId_t fieldId)
void CallFlushColumnsOnField(RFieldBase &)
void CallSetArtificialOnField(RFieldBase &)
bool IsMatchingFieldType(const std::string &actualTypeName)
Helper to check if a given type name is the one expected of Field<T>.
Definition RField.hxx:581
std::string GetRenormalizedTypeName(const std::string &metaNormalizedName)
Given a type name normalized by ROOT meta, renormalize it for RNTuple. E.g., insert std::prefix.
constexpr bool IsValidAlignment(std::size_t align) noexcept
Return true if align is a valid C++ alignment value: strictly positive and a power of two.
Definition BitUtils.hxx:36
void CallConnectPageSinkOnField(RFieldBase &, ROOT::Internal::RPageSink &, ROOT::NTupleSize_t firstEntry=0)
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
constexpr DescriptorId_t kInvalidDescriptorId
ENTupleStructure
The fields in the RNTuple data model tree can carry different structural information about the type s...
A pair of scoped + meta entry used by the RNTupleAttrSetWriter.
static void SetPrimaryColumnRepresentation(RFieldBase &field, std::uint16_t newRepresentationIdx)
Input parameter to RFieldBase::ReadBulk() and RFieldBase::ReadBulkImpl().
static const std::size_t kAllSet
Possible return value of ReadBulk() and ReadBulkImpl(), which indicates that the full bulk range was ...
RNTupleLocalIndex fFirstIndex
Start of the bulk range.
void * fValues
The destination area, which has to be an array of valid objects of the correct type large enough to h...
std::size_t fCount
Size of the bulk range.
bool * fMaskAvail
A bool array of size fCount, indicating the valid values in fValues.
const bool * fMaskReq
A bool array of size fCount, indicating the required values in the requested range.
std::vector< unsigned char > * fAuxData
Reference to memory owned by the RBulkValues class.
Used in the return value of the Check() method.
std::string fFieldName
Qualified field name causing the error.
std::string fTypeName
Type name corresponding to the (sub)field.
std::string fErrMsg
Cause of the failure, e.g. unsupported type.
Position(FieldPtr_t fieldPtr, int idxInParent)
std::conditional_t< IsConstT, const RFieldBase *, RFieldBase * > FieldPtr_t
RSharedPtrDeleter(std::unique_ptr< RFieldBase::RDeleter > deleter)
std::unique_ptr< RFieldBase::RDeleter > fDeleter