Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:10:42

0001 /// \file ROOT/RField.hxx
0002 /// \ingroup NTuple ROOT7
0003 /// \author Jakob Blomer <jblomer@cern.ch>
0004 /// \date 2018-10-09
0005 /// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
0006 /// is welcome!
0007 
0008 /*************************************************************************
0009  * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers.               *
0010  * All rights reserved.                                                  *
0011  *                                                                       *
0012  * For the licensing terms see $ROOTSYS/LICENSE.                         *
0013  * For the list of contributors see $ROOTSYS/README/CREDITS.             *
0014  *************************************************************************/
0015 
0016 #ifndef ROOT7_RField
0017 #define ROOT7_RField
0018 
0019 #include <ROOT/RColumn.hxx>
0020 #include <ROOT/RError.hxx>
0021 #include <ROOT/RColumnElement.hxx>
0022 #include <ROOT/RNTupleUtil.hxx>
0023 #include <ROOT/RSpan.hxx>
0024 #include <string_view>
0025 #include <ROOT/RVec.hxx>
0026 #include <ROOT/TypeTraits.hxx>
0027 
0028 #include <TGenericClassInfo.h>
0029 #include <TVirtualCollectionProxy.h>
0030 
0031 #include <algorithm>
0032 #include <array>
0033 #include <atomic>
0034 #include <bitset>
0035 #include <cstddef>
0036 #include <functional>
0037 #include <iostream>
0038 #include <iterator>
0039 #include <map>
0040 #include <memory>
0041 #include <new>
0042 #include <set>
0043 #include <string>
0044 #include <tuple>
0045 #include <type_traits>
0046 #include <typeinfo>
0047 #include <variant>
0048 #include <vector>
0049 #include <utility>
0050 
0051 class TClass;
0052 class TEnum;
0053 
0054 namespace ROOT {
0055 
0056 class TSchemaRule;
0057 class RFieldBase;
0058 
0059 namespace Experimental {
0060 
0061 class RCollectionField;
0062 class RNTupleCollectionWriter;
0063 class REntry;
0064 
0065 namespace Internal {
0066 struct RFieldCallbackInjector;
0067 class RPageSink;
0068 class RPageSource;
0069 // TODO(jblomer): find a better way to not have these three methods in the RFieldBase public API
0070 void CallCommitClusterOnField(RFieldBase &);
0071 void CallConnectPageSinkOnField(RFieldBase &, RPageSink &, NTupleSize_t firstEntry = 0);
0072 void CallConnectPageSourceOnField(RFieldBase &, RPageSource &);
0073 } // namespace Internal
0074 
0075 namespace Detail {
0076 class RFieldVisitor;
0077 } // namespace Detail
0078 
0079 // clang-format off
0080 /**
0081 \class ROOT::Experimental::RFieldBase
0082 \ingroup NTuple
0083 \brief A field translates read and write calls from/to underlying columns to/from tree values
0084 
0085 A field is a serializable C++ type or a container for a collection of sub fields. The RFieldBase and its
0086 type-safe descendants provide the object to column mapper. They map C++ objects to primitive columns.  The
0087 mapping is trivial for simple types such as 'double'. Complex types resolve to multiple primitive columns.
0088 The field knows based on its type and the field name the type(s) and name(s) of the columns.
0089 
0090 Note: the class hierarchy starting at RFieldBase is not meant to be extended by user-provided child classes.
0091 This is and can only be partially enforced through C++.
0092 */
0093 // clang-format on
0094 class RFieldBase {
0095    friend class ROOT::Experimental::RCollectionField; // to move the fields from the collection model
0096    friend struct ROOT::Experimental::Internal::RFieldCallbackInjector; // used for unit tests
0097    friend void Internal::CallCommitClusterOnField(RFieldBase &);
0098    friend void Internal::CallConnectPageSinkOnField(RFieldBase &, Internal::RPageSink &, NTupleSize_t);
0099    friend void Internal::CallConnectPageSourceOnField(RFieldBase &, Internal::RPageSource &);
0100    using ReadCallback_t = std::function<void(void *)>;
0101 
0102 protected:
0103    /// A functor to release the memory acquired by CreateValue (memory and constructor).
0104    /// This implementation works for types with a trivial destructor. More complex fields implement a derived deleter.
0105    /// The deleter is operational without the field object and thus can be used to destruct/release a value after
0106    /// the field has been destructed.
0107    class RDeleter {
0108    public:
0109       virtual ~RDeleter() = default;
0110       virtual void operator()(void *objPtr, bool dtorOnly)
0111       {
0112          if (!dtorOnly)
0113             operator delete(objPtr);
0114       }
0115    };
0116 
0117    /// A deleter for templated RFieldBase descendents where the value type is known.
0118    template <typename T>
0119    class RTypedDeleter : public RDeleter {
0120    public:
0121       void operator()(void *objPtr, bool dtorOnly) final
0122       {
0123          std::destroy_at(static_cast<T *>(objPtr));
0124          RDeleter::operator()(objPtr, dtorOnly);
0125       }
0126    };
0127 
0128    // We cannot directly use RFieldBase::RDeleter as a shared pointer deleter due to splicing. We use this
0129    // wrapper class to store a polymorphic pointer to the actual deleter.
0130    struct RSharedPtrDeleter {
0131       std::unique_ptr<RFieldBase::RDeleter> fDeleter;
0132       void operator()(void *objPtr) { fDeleter->operator()(objPtr, false /* dtorOnly*/); }
0133       explicit RSharedPtrDeleter(std::unique_ptr<RFieldBase::RDeleter> deleter) : fDeleter(std::move(deleter)) {}
0134    };
0135 
0136 public:
0137    static constexpr std::uint32_t kInvalidTypeVersion = -1U;
0138    /// No constructor needs to be called, i.e. any bit pattern in the allocated memory represents a valid type
0139    /// A trivially constructible field has a no-op ConstructValue() implementation
0140    static constexpr int kTraitTriviallyConstructible = 0x01;
0141    /// The type is cleaned up just by freeing its memory. I.e. the destructor performs a no-op.
0142    static constexpr int kTraitTriviallyDestructible = 0x02;
0143    /// A field of a fundamental type that can be directly mapped via `RField<T>::Map()`, i.e. maps as-is to a single
0144    /// column
0145    static constexpr int kTraitMappable = 0x04;
0146    /// Shorthand for types that are both trivially constructible and destructible
0147    static constexpr int kTraitTrivialType = kTraitTriviallyConstructible | kTraitTriviallyDestructible;
0148 
0149    using ColumnRepresentation_t = std::vector<EColumnType>;
0150 
0151    /// During its lifetime, a field undergoes the following possible state transitions:
0152    ///
0153    ///  [*] --> Unconnected --> ConnectedToSink ----
0154    ///               |      |                      |
0155    ///               |      --> ConnectedToSource ---> [*]
0156    ///               |                             |
0157    ///               -------------------------------
0158    enum class EState { kUnconnected, kConnectedToSink, kConnectedToSource };
0159 
0160    /// Some fields have multiple possible column representations, e.g. with or without split encoding.
0161    /// All column representations supported for writing also need to be supported for reading. In addition,
0162    /// fields can support extra column representations for reading only, e.g. a 64bit integer reading from a
0163    /// 32bit column.
0164    /// The defined column representations must be supported by corresponding column packing/unpacking implementations,
0165    /// i.e. for the example above, the unpacking of 32bit ints to 64bit pages must be implemented in RColumnElement.hxx
0166    class RColumnRepresentations {
0167    public:
0168       using TypesList_t = std::vector<ColumnRepresentation_t>;
0169       RColumnRepresentations();
0170       RColumnRepresentations(const TypesList_t &serializationTypes, const TypesList_t &deserializationExtraTypes);
0171 
0172       /// The first column list from fSerializationTypes is the default for writing.
0173       const ColumnRepresentation_t &GetSerializationDefault() const { return fSerializationTypes[0]; }
0174       const TypesList_t &GetSerializationTypes() const { return fSerializationTypes; }
0175       const TypesList_t &GetDeserializationTypes() const { return fDeserializationTypes; }
0176 
0177    private:
0178       TypesList_t fSerializationTypes;
0179       /// The union of the serialization types and the deserialization extra types.  Duplicates the serialization types
0180       /// list but the benenfit is that GetDeserializationTypes does not need to compile the list.
0181       TypesList_t fDeserializationTypes;
0182    }; // class RColumnRepresentations
0183 
0184    /// Points to an object with RNTuple I/O support and keeps a pointer to the corresponding field.
0185    /// Only fields can create RValue objects through generation, binding or splitting.
0186    class RValue {
0187       friend class RFieldBase;
0188 
0189    private:
0190       RFieldBase *fField = nullptr; ///< The field that created the RValue
0191       std::shared_ptr<void> fObjPtr; ///< Set by Bind() or by RFieldBase::CreateValue(), SplitValue() or BindValue()
0192 
0193       RValue(RFieldBase *field, std::shared_ptr<void> objPtr) : fField(field), fObjPtr(objPtr) {}
0194 
0195    public:
0196       RValue(const RValue &) = default;
0197       RValue &operator=(const RValue &) = default;
0198       RValue(RValue &&other) = default;
0199       RValue &operator=(RValue &&other) = default;
0200       ~RValue() = default;
0201 
0202       std::size_t Append() { return fField->Append(fObjPtr.get()); }
0203       void Read(NTupleSize_t globalIndex) { fField->Read(globalIndex, fObjPtr.get()); }
0204       void Read(RClusterIndex clusterIndex) { fField->Read(clusterIndex, fObjPtr.get()); }
0205       void Bind(std::shared_ptr<void> objPtr) { fObjPtr = objPtr; }
0206       void BindRawPtr(void *rawPtr);
0207       /// Replace the current object pointer by a pointer to a new object constructed by the field
0208       void EmplaceNew() { fObjPtr = fField->CreateValue().GetPtr<void>(); }
0209 
0210       template <typename T>
0211       std::shared_ptr<T> GetPtr() const
0212       {
0213          return std::static_pointer_cast<T>(fObjPtr);
0214       }
0215 
0216       template <typename T>
0217       const T &GetRef() const
0218       {
0219          return *static_cast<T *>(fObjPtr.get());
0220       }
0221 
0222       const RFieldBase &GetField() const { return *fField; }
0223    }; // class RValue
0224 
0225    /// Similar to RValue but manages an array of consecutive values. Bulks have to come from the same cluster.
0226    /// Bulk I/O works with two bit masks: the mask of all the available entries in the current bulk and the mask
0227    /// of the required entries in a bulk read. The idea is that a single bulk may serve multiple read operations
0228    /// on the same range, where in each read operation a different subset of values is required.
0229    /// The memory of the value array is managed by the RBulk class.
0230    class RBulk {
0231    private:
0232       friend class RFieldBase;
0233 
0234       RFieldBase *fField = nullptr;       ///< The field that created the array of values
0235       std::unique_ptr<RFieldBase::RDeleter> fDeleter; /// Cached deleter of fField
0236       void *fValues = nullptr;            ///< Pointer to the start of the array
0237       std::size_t fValueSize = 0;         ///< Cached copy of fField->GetValueSize()
0238       std::size_t fCapacity = 0;          ///< The size of the array memory block in number of values
0239       std::size_t fSize = 0;              ///< The number of available values in the array (provided their mask is set)
0240       bool fIsAdopted = false;            ///< True if the user provides the memory buffer for fValues
0241       std::unique_ptr<bool[]> fMaskAvail; ///< Masks invalid values in the array
0242       std::size_t fNValidValues = 0;      ///< The sum of non-zero elements in the fMask
0243       RClusterIndex fFirstIndex;          ///< Index of the first value of the array
0244       /// Reading arrays of complex values may require additional memory, for instance for the elements of
0245       /// arrays of vectors. A pointer to the fAuxData array is passed to the field's BulkRead method.
0246       /// The RBulk class does not modify the array in-between calls to the field's BulkRead method.
0247       std::vector<unsigned char> fAuxData;
0248 
0249       void ReleaseValues();
0250       /// Sets a new range for the bulk. If there is enough capacity, the fValues array will be reused.
0251       /// Otherwise a new array is allocated. After reset, fMaskAvail is false for all values.
0252       void Reset(RClusterIndex firstIndex, std::size_t size);
0253       void CountValidValues();
0254 
0255       bool ContainsRange(RClusterIndex firstIndex, std::size_t size) const
0256       {
0257          if (firstIndex.GetClusterId() != fFirstIndex.GetClusterId())
0258             return false;
0259          return (firstIndex.GetIndex() >= fFirstIndex.GetIndex()) &&
0260                 ((firstIndex.GetIndex() + size) <= (fFirstIndex.GetIndex() + fSize));
0261       }
0262 
0263       void *GetValuePtrAt(std::size_t idx) const
0264       {
0265          return reinterpret_cast<unsigned char *>(fValues) + idx * fValueSize;
0266       }
0267 
0268       explicit RBulk(RFieldBase *field)
0269          : fField(field), fDeleter(field->GetDeleter()), fValueSize(field->GetValueSize())
0270       {
0271       }
0272 
0273    public:
0274       ~RBulk();
0275       RBulk(const RBulk &) = delete;
0276       RBulk &operator=(const RBulk &) = delete;
0277       RBulk(RBulk &&other);
0278       RBulk &operator=(RBulk &&other);
0279 
0280       // Sets fValues and fSize/fCapacity to the given values. The capacity is specified in number of values.
0281       // Once a buffer is adopted, an attempt to read more values then available throws an exception.
0282       void AdoptBuffer(void *buf, std::size_t capacity);
0283 
0284       /// Reads 'size' values from the associated field, starting from 'firstIndex'. Note that the index is given
0285       /// relative to a certain cluster. The return value points to the array of read objects.
0286       /// The 'maskReq' parameter is a bool array of at least 'size' elements. Only objects for which the mask is
0287       /// true are guaranteed to be read in the returned value array.
0288       void *ReadBulk(RClusterIndex firstIndex, const bool *maskReq, std::size_t size)
0289       {
0290          if (!ContainsRange(firstIndex, size))
0291             Reset(firstIndex, size);
0292 
0293          // We may read a sub range of the currently available range
0294          auto offset = firstIndex.GetIndex() - fFirstIndex.GetIndex();
0295 
0296          if (fNValidValues == fSize)
0297             return GetValuePtrAt(offset);
0298 
0299          RBulkSpec bulkSpec;
0300          bulkSpec.fFirstIndex = firstIndex;
0301          bulkSpec.fCount = size;
0302          bulkSpec.fMaskReq = maskReq;
0303          bulkSpec.fMaskAvail = &fMaskAvail[offset];
0304          bulkSpec.fValues = GetValuePtrAt(offset);
0305          bulkSpec.fAuxData = &fAuxData;
0306          auto nRead = fField->ReadBulk(bulkSpec);
0307          if (nRead == RBulkSpec::kAllSet) {
0308             if ((offset == 0) && (size == fSize)) {
0309                fNValidValues = fSize;
0310             } else {
0311                CountValidValues();
0312             }
0313          } else {
0314             fNValidValues += nRead;
0315          }
0316          return GetValuePtrAt(offset);
0317       }
0318    }; // class RBulk
0319 
0320 private:
0321    /// The field name relative to its parent field
0322    std::string fName;
0323    /// The C++ type captured by this field
0324    std::string fType;
0325    /// The role of this field in the data model structure
0326    ENTupleStructure fStructure;
0327    /// For fixed sized arrays, the array length
0328    std::size_t fNRepetitions;
0329    /// A field qualifies as simple if it is both mappable and has no post-read callback
0330    bool fIsSimple;
0331    /// When the columns are connected to a page source or page sink, the field represents a field id in the
0332    /// corresponding RNTuple descriptor. This on-disk ID is set in RPageSink::Create() for writing and by
0333    /// RFieldDescriptor::CreateField() when recreating a field / model from the stored descriptor.
0334    DescriptorId_t fOnDiskId = kInvalidDescriptorId;
0335    /// Free text set by the user
0336    std::string fDescription;
0337    /// Changed by ConnectTo[Sink,Source], reset by Clone()
0338    EState fState = EState::kUnconnected;
0339 
0340    void InvokeReadCallbacks(void *target)
0341    {
0342       for (const auto &func : fReadCallbacks)
0343          func(target);
0344    }
0345 
0346    /// Translate an entry index to a column element index of the principal column and viceversa.  These functions
0347    /// take into account the role and number of repetitions on each level of the field hierarchy as follows:
0348    /// - Top level fields: element index == entry index
0349    /// - Record fields propagate their principal column index to the principal columns of direct descendant fields
0350    /// - Collection and variant fields set the principal column index of their childs to 0
0351    ///
0352    /// The column element index also depends on the number of repetitions of each field in the hierarchy, e.g., given a
0353    /// field with type `std::array<std::array<float, 4>, 2>`, this function returns 8 for the inner-most field.
0354    NTupleSize_t EntryToColumnElementIndex(NTupleSize_t globalIndex) const;
0355 
0356    /// Flushes data from active columns to disk and calls CommitClusterImpl
0357    void CommitCluster();
0358    /// Fields and their columns live in the void until connected to a physical page storage.  Only once connected, data
0359    /// 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.
0360    /// \param firstEntry The global index of the first entry with on-disk data for the connected field
0361    void ConnectPageSink(Internal::RPageSink &pageSink, NTupleSize_t firstEntry = 0);
0362    /// Connects the field and its sub field tree to the given page source. Once connected, data can be read.
0363    /// Only unconnected fields may be connected, i.e. the method is not idempotent. The field ID has to be set prior to
0364    /// calling this function. For sub fields, a field ID may or may not be set. If the field ID is unset, it will be
0365    /// determined using the page source descriptor, based on the parent field ID and the sub field name.
0366    void ConnectPageSource(Internal::RPageSource &pageSource);
0367 
0368    /// Factory method for the field's type. The caller owns the returned pointer
0369    void *CreateObjectRawPtr() const;
0370 
0371 protected:
0372    /// Input parameter to ReadBulk() and ReadBulkImpl(). See RBulk class for more information
0373    struct RBulkSpec {
0374       /// As a return value of ReadBulk and ReadBulkImpl(), indicates that the full bulk range was read
0375       /// independent of the provided masks.
0376       static const std::size_t kAllSet = std::size_t(-1);
0377 
0378       RClusterIndex fFirstIndex; ///< Start of the bulk range
0379       std::size_t fCount = 0;    ///< Size of the bulk range
0380       /// A bool array of size fCount, indicating the required values in the requested range
0381       const bool *fMaskReq = nullptr;
0382       bool *fMaskAvail = nullptr; ///< A bool array of size fCount, indicating the valid values in fValues
0383       /// The destination area, which has to be a big enough array of valid objects of the correct type
0384       void *fValues = nullptr;
0385       /// Reference to memory owned by the RBulk class. The field implementing BulkReadImpl may use fAuxData
0386       /// as memory that stays persistent between calls.
0387       std::vector<unsigned char> *fAuxData = nullptr;
0388    };
0389 
0390    /// Collections and classes own sub fields
0391    std::vector<std::unique_ptr<RFieldBase>> fSubFields;
0392    /// Sub fields point to their mother field
0393    RFieldBase* fParent;
0394    /// Points into fColumns.  All fields that have columns have a distinct main column. For simple fields
0395    /// (float, int, ...), the principal column corresponds to the field type. For collection fields expect std::array,
0396    /// the main column is the offset field.  Class fields have no column of their own.
0397    Internal::RColumn *fPrincipalColumn;
0398    /// The columns are connected either to a sink or to a source (not to both); they are owned by the field.
0399    std::vector<std::unique_ptr<Internal::RColumn>> fColumns;
0400    /// Properties of the type that allow for optimizations of collections of that type
0401    int fTraits = 0;
0402    /// A typedef or using name that was used when creating the field
0403    std::string fTypeAlias;
0404    /// List of functions to be called after reading a value
0405    std::vector<ReadCallback_t> fReadCallbacks;
0406    /// C++ type version cached from the descriptor after a call to `ConnectPageSource()`
0407    std::uint32_t fOnDiskTypeVersion = kInvalidTypeVersion;
0408    /// Points into the static vector GetColumnRepresentations().GetSerializationTypes() when SetColumnRepresentative
0409    /// is called.  Otherwise GetColumnRepresentative returns the default representation.
0410    const ColumnRepresentation_t *fColumnRepresentative = nullptr;
0411 
0412    /// Implementations in derived classes should return a static RColumnRepresentations object. The default
0413    /// implementation does not attach any columns to the field.
0414    virtual const RColumnRepresentations &GetColumnRepresentations() const;
0415    /// Creates the backing columns corresponsing to the field type for writing
0416    virtual void GenerateColumnsImpl() = 0;
0417    /// Creates the backing columns corresponsing to the field type for reading.
0418    /// The method should to check, using the page source and fOnDiskId, if the column types match
0419    /// and throw if they don't.
0420    virtual void GenerateColumnsImpl(const RNTupleDescriptor &desc) = 0;
0421    /// Returns the on-disk column types found in the provided descriptor for fOnDiskId. Throws an exception if the types
0422    /// don't match any of the deserialization types from GetColumnRepresentations().
0423    const ColumnRepresentation_t &EnsureCompatibleColumnTypes(const RNTupleDescriptor &desc) const;
0424    /// When connecting a field to a page sink, the field's default column representation is subject
0425    /// to adjustment according to the write options. E.g., if compression is turned off, encoded columns
0426    /// are changed to their unencoded counterparts.
0427    void AutoAdjustColumnTypes(const RNTupleWriteOptions &options);
0428 
0429    /// Called by Clone(), which additionally copies the on-disk ID
0430    virtual std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const = 0;
0431 
0432    /// Constructs value in a given location of size at least GetValueSize(). Called by the base class' CreateValue().
0433    virtual void ConstructValue(void *where) const = 0;
0434    virtual std::unique_ptr<RDeleter> GetDeleter() const { return std::make_unique<RDeleter>(); }
0435    /// Allow derived classes to call ConstructValue(void *) and GetDeleter on other (sub) fields.
0436    static void CallConstructValueOn(const RFieldBase &other, void *where) { other.ConstructValue(where); }
0437    static std::unique_ptr<RDeleter> GetDeleterOf(const RFieldBase &other) { return other.GetDeleter(); }
0438 
0439    /// Operations on values of complex types, e.g. ones that involve multiple columns or for which no direct
0440    /// column type exists.
0441    virtual std::size_t AppendImpl(const void *from);
0442    virtual void ReadGlobalImpl(NTupleSize_t globalIndex, void *to);
0443    virtual void ReadInClusterImpl(RClusterIndex clusterIndex, void *to)
0444    {
0445       ReadGlobalImpl(fPrincipalColumn->GetGlobalIndex(clusterIndex), to);
0446    }
0447 
0448    /// Write the given value into columns. The value object has to be of the same type as the field.
0449    /// Returns the number of uncompressed bytes written.
0450    std::size_t Append(const void *from)
0451    {
0452       if (~fTraits & kTraitMappable)
0453          return AppendImpl(from);
0454 
0455       fPrincipalColumn->Append(from);
0456       return fPrincipalColumn->GetElement()->GetPackedSize();
0457    }
0458 
0459    /// Populate a single value with data from the field. The memory location pointed to by to needs to be of the
0460    /// fitting type. The fast path is conditioned by the field qualifying as simple, i.e. maps as-is
0461    /// to a single column and has no read callback.
0462    void Read(NTupleSize_t globalIndex, void *to)
0463    {
0464       if (fIsSimple)
0465          return (void)fPrincipalColumn->Read(globalIndex, to);
0466 
0467       if (fTraits & kTraitMappable)
0468          fPrincipalColumn->Read(globalIndex, to);
0469       else
0470          ReadGlobalImpl(globalIndex, to);
0471       if (R__unlikely(!fReadCallbacks.empty()))
0472          InvokeReadCallbacks(to);
0473    }
0474 
0475    void Read(RClusterIndex clusterIndex, void *to)
0476    {
0477       if (fIsSimple)
0478          return (void)fPrincipalColumn->Read(clusterIndex, to);
0479 
0480       if (fTraits & kTraitMappable)
0481          fPrincipalColumn->Read(clusterIndex, to);
0482       else
0483          ReadInClusterImpl(clusterIndex, to);
0484       if (R__unlikely(!fReadCallbacks.empty()))
0485          InvokeReadCallbacks(to);
0486    }
0487 
0488    /// General implementation of bulk read. Loop over the required range and read values that are required
0489    /// and not already present. Derived classes may implement more optimized versions of this method.
0490    /// See ReadBulk() for the return value.
0491    virtual std::size_t ReadBulkImpl(const RBulkSpec &bulkSpec);
0492 
0493    /// Returns the number of newly available values, that is the number of bools in bulkSpec.fMaskAvail that
0494    /// flipped from false to true. As a special return value, kAllSet can be used if all values are read
0495    /// independent from the masks.
0496    std::size_t ReadBulk(const RBulkSpec &bulkSpec)
0497    {
0498       if (fIsSimple) {
0499          /// For simple types, ignore the mask and memcopy the values into the destination
0500          fPrincipalColumn->ReadV(bulkSpec.fFirstIndex, bulkSpec.fCount, bulkSpec.fValues);
0501          std::fill(bulkSpec.fMaskAvail, bulkSpec.fMaskAvail + bulkSpec.fCount, true);
0502          return RBulkSpec::kAllSet;
0503       }
0504 
0505       return ReadBulkImpl(bulkSpec);
0506    }
0507 
0508    /// Allow derived classes to call Append and Read on other (sub) fields.
0509    static std::size_t CallAppendOn(RFieldBase &other, const void *from) { return other.Append(from); }
0510    static void CallReadOn(RFieldBase &other, RClusterIndex clusterIndex, void *to) { other.Read(clusterIndex, to); }
0511    static void CallReadOn(RFieldBase &other, NTupleSize_t globalIndex, void *to) { other.Read(globalIndex, to); }
0512 
0513    /// Fields may need direct access to the principal column of their sub fields, e.g. in RRVecField::ReadBulk
0514    static Internal::RColumn *GetPrincipalColumnOf(const RFieldBase &other) { return other.fPrincipalColumn; }
0515 
0516    /// Set a user-defined function to be called after reading a value, giving a chance to inspect and/or modify the
0517    /// value object.
0518    /// Returns an index that can be used to remove the callback.
0519    size_t AddReadCallback(ReadCallback_t func);
0520    void RemoveReadCallback(size_t idx);
0521 
0522    // Perform housekeeping tasks for global to cluster-local index translation
0523    virtual void CommitClusterImpl() {}
0524 
0525    /// Add a new subfield to the list of nested fields
0526    void Attach(std::unique_ptr<RFieldBase> child);
0527 
0528    /// Called by `ConnectPageSource()` only once connected; derived classes may override this
0529    /// as appropriate
0530    virtual void OnConnectPageSource() {}
0531 
0532    /// Factory method to resurrect a field from the stored on-disk type information.  This overload takes an already
0533    /// normalized type name and type alias
0534    /// TODO(jalopezg): this overload may eventually be removed leaving only the `RFieldBase::Create()` that takes a
0535    /// single type name
0536    static RResult<std::unique_ptr<RFieldBase>> Create(const std::string &fieldName, const std::string &canonicalType,
0537                                                       const std::string &typeAlias, bool fContinueOnError = false);
0538 
0539 public:
0540    /// Iterates over the sub tree of fields in depth-first search order
0541    template <bool IsConstT>
0542    class RSchemaIteratorTemplate {
0543    private:
0544       struct Position {
0545          using FieldPtr_t = std::conditional_t<IsConstT, const RFieldBase *, RFieldBase *>;
0546          Position() : fFieldPtr(nullptr), fIdxInParent(-1) { }
0547          Position(FieldPtr_t fieldPtr, int idxInParent) : fFieldPtr(fieldPtr), fIdxInParent(idxInParent) {}
0548          FieldPtr_t fFieldPtr;
0549          int fIdxInParent;
0550       };
0551       /// The stack of nodes visited when walking down the tree of fields
0552       std::vector<Position> fStack;
0553    public:
0554       using iterator = RSchemaIteratorTemplate<IsConstT>;
0555       using iterator_category = std::forward_iterator_tag;
0556       using difference_type = std::ptrdiff_t;
0557       using value_type = std::conditional_t<IsConstT, const RFieldBase, RFieldBase>;
0558       using pointer = std::conditional_t<IsConstT, const RFieldBase *, RFieldBase *>;
0559       using reference = std::conditional_t<IsConstT, const RFieldBase &, RFieldBase &>;
0560 
0561       RSchemaIteratorTemplate() { fStack.emplace_back(Position()); }
0562       RSchemaIteratorTemplate(pointer val, int idxInParent) { fStack.emplace_back(Position(val, idxInParent)); }
0563       ~RSchemaIteratorTemplate() {}
0564       /// Given that the iterator points to a valid field which is not the end iterator, go to the next field
0565       /// in depth-first search order
0566       void Advance()
0567       {
0568          auto itr = fStack.rbegin();
0569          if (!itr->fFieldPtr->fSubFields.empty()) {
0570             fStack.emplace_back(Position(itr->fFieldPtr->fSubFields[0].get(), 0));
0571             return;
0572          }
0573 
0574          unsigned int nextIdxInParent = ++(itr->fIdxInParent);
0575          while (nextIdxInParent >= itr->fFieldPtr->fParent->fSubFields.size()) {
0576             if (fStack.size() == 1) {
0577                itr->fFieldPtr = itr->fFieldPtr->fParent;
0578                itr->fIdxInParent = -1;
0579                return;
0580             }
0581             fStack.pop_back();
0582             itr = fStack.rbegin();
0583             nextIdxInParent = ++(itr->fIdxInParent);
0584          }
0585          itr->fFieldPtr = itr->fFieldPtr->fParent->fSubFields[nextIdxInParent].get();
0586       }
0587 
0588       iterator  operator++(int) /* postfix */        { auto r = *this; Advance(); return r; }
0589       iterator& operator++()    /* prefix */         { Advance(); return *this; }
0590       reference operator* () const                   { return *fStack.back().fFieldPtr; }
0591       pointer   operator->() const                   { return fStack.back().fFieldPtr; }
0592       bool      operator==(const iterator& rh) const { return fStack.back().fFieldPtr == rh.fStack.back().fFieldPtr; }
0593       bool      operator!=(const iterator& rh) const { return fStack.back().fFieldPtr != rh.fStack.back().fFieldPtr; }
0594    };
0595    using RSchemaIterator = RSchemaIteratorTemplate<false>;
0596    using RConstSchemaIterator = RSchemaIteratorTemplate<true>;
0597 
0598    // This is used in CreateObject and is specialized for void
0599    template <typename T>
0600    struct RCreateObjectDeleter {
0601       using deleter = std::default_delete<T>;
0602    };
0603 
0604    /// Used in the return value of the Check() method
0605    struct RCheckResult {
0606       std::string fFieldName; ///< Qualified field name causing the error
0607       std::string fTypeName;  ///< Type name corresponding to the (sub) field
0608       std::string fErrMsg;    ///< Cause of the failure, e.g. unsupported type
0609    };
0610 
0611    /// The constructor creates the underlying column objects and connects them to either a sink or a source.
0612    /// If `isSimple` is `true`, the trait `kTraitMappable` is automatically set on construction. However, the
0613    /// field might be demoted to non-simple if a post-read callback is set.
0614    RFieldBase(std::string_view name, std::string_view type, ENTupleStructure structure, bool isSimple,
0615               std::size_t nRepetitions = 0);
0616    RFieldBase(const RFieldBase&) = delete;
0617    RFieldBase(RFieldBase&&) = default;
0618    RFieldBase& operator =(const RFieldBase&) = delete;
0619    RFieldBase& operator =(RFieldBase&&) = default;
0620    virtual ~RFieldBase() = default;
0621 
0622    /// Copies the field and its sub fields using a possibly new name and a new, unconnected set of columns
0623    std::unique_ptr<RFieldBase> Clone(std::string_view newName) const;
0624 
0625    /// Factory method to resurrect a field from the stored on-disk type information
0626    static RResult<std::unique_ptr<RFieldBase>>
0627    Create(const std::string &fieldName, const std::string &typeName);
0628    /// Checks if the given type is supported by RNTuple. In case of success, the result vector is empty.
0629    /// Otherwise there is an error record for each failing sub field (sub type).
0630    static std::vector<RCheckResult> Check(const std::string &fieldName, const std::string &typeName);
0631    /// Check whether a given string is a valid field name
0632    static RResult<void> EnsureValidFieldName(std::string_view fieldName);
0633 
0634    /// Generates an object of the field type and allocates new initialized memory according to the type.
0635    /// Implemented at the end of this header because the implementation is using RField<T>::TypeName()
0636    /// The returned object can be released with `delete`, i.e. it is valid to call
0637    ///    auto ptr = field->CreateObject();
0638    ///    delete ptr.release();
0639    ///
0640    /// Note that CreateObject<void> is supported. The returned unique_ptr has a custom deleter that reports an error
0641    /// if it is called. The intended use of the returned unique_ptr<void> is to call `release()`. In this way, the
0642    /// transfer of pointer ownership is explicit.
0643    template <typename T>
0644    std::unique_ptr<T, typename RCreateObjectDeleter<T>::deleter> CreateObject() const;
0645    /// Generates an object of the field type and wraps the created object in a shared pointer and returns it an RValue
0646    /// connected to the field.
0647    RValue CreateValue();
0648    /// The returned bulk is initially empty; RBulk::ReadBulk will construct the array of values
0649    RBulk CreateBulk() { return RBulk(this); }
0650    /// Creates a value from a memory location with an already constructed object
0651    RValue BindValue(std::shared_ptr<void> objPtr) { return RValue(this, objPtr); }
0652    /// Creates the list of direct child values given a value for this field.  E.g. a single value for the
0653    /// correct variant or all the elements of a collection.  The default implementation assumes no sub values
0654    /// and returns an empty vector.
0655    virtual std::vector<RValue> SplitValue(const RValue &value) const;
0656    /// The number of bytes taken by a value of the appropriate type
0657    virtual size_t GetValueSize() const = 0;
0658    /// As a rule of thumb, the alignment is equal to the size of the type. There are, however, various exceptions
0659    /// to this rule depending on OS and CPU architecture. So enforce the alignment to be explicitly spelled out.
0660    virtual size_t GetAlignment() const = 0;
0661    int GetTraits() const { return fTraits; }
0662    bool HasReadCallbacks() const { return !fReadCallbacks.empty(); }
0663 
0664    std::string GetFieldName() const { return fName; }
0665    /// Returns the field name and parent field names separated by dots ("grandparent.parent.child")
0666    std::string GetQualifiedFieldName() const;
0667    std::string GetTypeName() const { return fType; }
0668    std::string GetTypeAlias() const { return fTypeAlias; }
0669    ENTupleStructure GetStructure() const { return fStructure; }
0670    std::size_t GetNRepetitions() const { return fNRepetitions; }
0671    NTupleSize_t GetNElements() const { return fPrincipalColumn->GetNElements(); }
0672    const RFieldBase *GetParent() const { return fParent; }
0673    std::vector<RFieldBase *> GetSubFields();
0674    std::vector<const RFieldBase *> GetSubFields() const;
0675    bool IsSimple() const { return fIsSimple; }
0676    /// Get the field's description
0677    std::string GetDescription() const { return fDescription; }
0678    void SetDescription(std::string_view description);
0679    EState GetState() const { return fState; }
0680 
0681    DescriptorId_t GetOnDiskId() const { return fOnDiskId; }
0682    void SetOnDiskId(DescriptorId_t id);
0683 
0684    /// Returns the fColumnRepresentative pointee or, if unset, the field's default representative
0685    const ColumnRepresentation_t &GetColumnRepresentative() const;
0686    /// Fixes a column representative. This can only be done _before_ connecting the field to a page sink.
0687    /// Otherwise, or if the provided representation is not in the list of GetColumnRepresentations,
0688    /// an exception is thrown
0689    void SetColumnRepresentative(const ColumnRepresentation_t &representative);
0690    /// Whether or not an explicit column representative was set
0691    bool HasDefaultColumnRepresentative() const { return fColumnRepresentative == nullptr; }
0692 
0693    /// Indicates an evolution of the mapping scheme from C++ type to columns
0694    virtual std::uint32_t GetFieldVersion() const { return 0; }
0695    /// Indicates an evolution of the C++ type itself
0696    virtual std::uint32_t GetTypeVersion() const { return 0; }
0697    /// Return the C++ type version stored in the field descriptor; only valid after a call to `ConnectPageSource()`
0698    std::uint32_t GetOnDiskTypeVersion() const { return fOnDiskTypeVersion; }
0699 
0700    RSchemaIterator begin()
0701    {
0702       return fSubFields.empty() ? RSchemaIterator(this, -1) : RSchemaIterator(fSubFields[0].get(), 0);
0703    }
0704    RSchemaIterator end() { return RSchemaIterator(this, -1); }
0705    RConstSchemaIterator cbegin() const
0706    {
0707       return fSubFields.empty() ? RConstSchemaIterator(this, -1) : RConstSchemaIterator(fSubFields[0].get(), 0);
0708    }
0709    RConstSchemaIterator cend() const { return RConstSchemaIterator(this, -1); }
0710 
0711    virtual void AcceptVisitor(Detail::RFieldVisitor &visitor) const;
0712 }; // class RFieldBase
0713 
0714 /// The container field for an ntuple model, which itself has no physical representation.
0715 /// Therefore, the zero field must not be connected to a page source or sink.
0716 class RFieldZero final : public RFieldBase {
0717 protected:
0718    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const override;
0719    void GenerateColumnsImpl() final {}
0720    void GenerateColumnsImpl(const RNTupleDescriptor &) final {}
0721    void ConstructValue(void *) const final {}
0722 
0723 public:
0724    RFieldZero() : RFieldBase("", "", ENTupleStructure::kRecord, false /* isSimple */) {}
0725 
0726    using RFieldBase::Attach;
0727    size_t GetValueSize() const final { return 0; }
0728    size_t GetAlignment() const final { return 0; }
0729 
0730    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
0731 };
0732 
0733 /// Used in RFieldBase::Check() to record field creation failures.
0734 class RInvalidField final : public RFieldBase {
0735    std::string fError;
0736 
0737 protected:
0738    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
0739    {
0740       return std::make_unique<RInvalidField>(newName, GetTypeName(), fError);
0741    }
0742    void GenerateColumnsImpl() final {}
0743    void GenerateColumnsImpl(const RNTupleDescriptor &) final {}
0744    void ConstructValue(void *) const final {}
0745 
0746 public:
0747    RInvalidField(std::string_view name, std::string_view type, std::string_view error)
0748       : RFieldBase(name, type, ENTupleStructure::kLeaf, false /* isSimple */), fError(error)
0749    {
0750    }
0751 
0752    std::string GetError() const { return fError; }
0753 
0754    size_t GetValueSize() const final { return 0; }
0755    size_t GetAlignment() const final { return 0; }
0756 }; // RInvalidField
0757 
0758 /// The field for a class with dictionary
0759 class RClassField : public RFieldBase {
0760 private:
0761    enum ESubFieldRole {
0762       kBaseClass,
0763       kDataMember,
0764    };
0765    struct RSubFieldInfo {
0766       ESubFieldRole fRole;
0767       std::size_t fOffset;
0768    };
0769    /// Prefix used in the subfield names generated for base classes
0770    static constexpr const char *kPrefixInherited{":"};
0771 
0772    class RClassDeleter : public RDeleter {
0773    private:
0774       TClass *fClass;
0775 
0776    public:
0777       explicit RClassDeleter(TClass *cl) : fClass(cl) {}
0778       void operator()(void *objPtr, bool dtorOnly) final;
0779    };
0780 
0781    TClass* fClass;
0782    /// Additional information kept for each entry in `fSubFields`
0783    std::vector<RSubFieldInfo> fSubFieldsInfo;
0784    std::size_t fMaxAlignment = 1;
0785 
0786 private:
0787    RClassField(std::string_view fieldName, std::string_view className, TClass *classp);
0788    void Attach(std::unique_ptr<RFieldBase> child, RSubFieldInfo info);
0789    /// Register post-read callbacks corresponding to a list of ROOT I/O customization rules. `classp` is used to
0790    /// fill the `TVirtualObject` instance passed to the user function.
0791    void AddReadCallbacksFromIORules(const std::span<const TSchemaRule *> rules, TClass *classp = nullptr);
0792 
0793 protected:
0794    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
0795    void GenerateColumnsImpl() final {}
0796    void GenerateColumnsImpl(const RNTupleDescriptor &) final {}
0797 
0798    void ConstructValue(void *where) const override;
0799    std::unique_ptr<RDeleter> GetDeleter() const final { return std::make_unique<RClassDeleter>(fClass); }
0800 
0801    std::size_t AppendImpl(const void *from) final;
0802    void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final;
0803    void ReadInClusterImpl(RClusterIndex clusterIndex, void *to) final;
0804    void OnConnectPageSource() final;
0805 
0806 public:
0807    RClassField(std::string_view fieldName, std::string_view className);
0808    RClassField(RClassField&& other) = default;
0809    RClassField& operator =(RClassField&& other) = default;
0810    ~RClassField() override = default;
0811 
0812    std::vector<RValue> SplitValue(const RValue &value) const final;
0813    size_t GetValueSize() const override;
0814    size_t GetAlignment() const final { return fMaxAlignment; }
0815    std::uint32_t GetTypeVersion() const final;
0816    void AcceptVisitor(Detail::RFieldVisitor &visitor) const override;
0817 };
0818 
0819 /// The field for an unscoped or scoped enum with dictionary
0820 class REnumField : public RFieldBase {
0821 private:
0822    REnumField(std::string_view fieldName, std::string_view enumName, TEnum *enump);
0823    REnumField(std::string_view fieldName, std::string_view enumName, std::unique_ptr<RFieldBase> intField);
0824 
0825 protected:
0826    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
0827    void GenerateColumnsImpl() final {}
0828    void GenerateColumnsImpl(const RNTupleDescriptor & /* desc */) final {}
0829 
0830    void ConstructValue(void *where) const final { CallConstructValueOn(*fSubFields[0], where); }
0831 
0832    std::size_t AppendImpl(const void *from) final { return CallAppendOn(*fSubFields[0], from); }
0833    void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final { CallReadOn(*fSubFields[0], globalIndex, to); }
0834    void ReadInClusterImpl(RClusterIndex clusterIndex, void *to) final { CallReadOn(*fSubFields[0], clusterIndex, to); }
0835 
0836 public:
0837    REnumField(std::string_view fieldName, std::string_view enumName);
0838    REnumField(REnumField &&other) = default;
0839    REnumField &operator=(REnumField &&other) = default;
0840    ~REnumField() override = default;
0841 
0842    std::vector<RValue> SplitValue(const RValue &value) const final;
0843    size_t GetValueSize() const final { return fSubFields[0]->GetValueSize(); }
0844    size_t GetAlignment() const final { return fSubFields[0]->GetAlignment(); }
0845    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
0846 };
0847 
0848 /// The field for a class representing a collection of elements via `TVirtualCollectionProxy`.
0849 /// Objects of such type behave as collections that can be accessed through the corresponding member functions in
0850 /// `TVirtualCollectionProxy`. For STL collections, these proxies are provided. Custom classes need to implement the
0851 /// corresponding member functions in `TVirtualCollectionProxy`. At a bare minimum, the user is required to provide an
0852 /// implementation for the following functions in `TVirtualCollectionProxy`: `HasPointers()`, `GetProperties()`,
0853 /// `GetValueClass()`, `GetType()`, `PushProxy()`, `PopProxy()`, `GetFunctionCreateIterators()`, `GetFunctionNext()`,
0854 /// and `GetFunctionDeleteTwoIterators()`.
0855 ///
0856 /// The collection proxy for a given class can be set via `TClass::CopyCollectionProxy()`.
0857 class RProxiedCollectionField : public RFieldBase {
0858 protected:
0859    /// Allows for iterating over the elements of a proxied collection. RCollectionIterableOnce avoids an additional
0860    /// iterator copy (see `TVirtualCollectionProxy::GetFunctionCopyIterator`) and thus can only be iterated once.
0861    class RCollectionIterableOnce {
0862    public:
0863       struct RIteratorFuncs {
0864          TVirtualCollectionProxy::CreateIterators_t fCreateIterators;
0865          TVirtualCollectionProxy::DeleteTwoIterators_t fDeleteTwoIterators;
0866          TVirtualCollectionProxy::Next_t fNext;
0867       };
0868       static RIteratorFuncs GetIteratorFuncs(TVirtualCollectionProxy *proxy, bool readFromDisk);
0869 
0870    private:
0871       class RIterator {
0872          const RCollectionIterableOnce &fOwner;
0873          void *fIterator = nullptr;
0874          void *fElementPtr = nullptr;
0875 
0876          void Advance()
0877          {
0878             auto fnNext_Contig = [&]() {
0879                // Array-backed collections (e.g. kSTLvector) directly use the pointer-to-iterator-data as a
0880                // pointer-to-element, thus saving an indirection level (see documentation for TVirtualCollectionProxy)
0881                auto &iter = reinterpret_cast<unsigned char *&>(fIterator), p = iter;
0882                iter += fOwner.fStride;
0883                return p;
0884             };
0885             fElementPtr = fOwner.fStride ? fnNext_Contig() : fOwner.fIFuncs.fNext(fIterator, fOwner.fEnd);
0886          }
0887 
0888       public:
0889          using iterator_category = std::forward_iterator_tag;
0890          using iterator = RIterator;
0891          using difference_type = std::ptrdiff_t;
0892          using pointer = void *;
0893 
0894          RIterator(const RCollectionIterableOnce &owner) : fOwner(owner) {}
0895          RIterator(const RCollectionIterableOnce &owner, void *iter) : fOwner(owner), fIterator(iter) { Advance(); }
0896          iterator operator++()
0897          {
0898             Advance();
0899             return *this;
0900          }
0901          pointer operator*() const { return fElementPtr; }
0902          bool operator!=(const iterator &rh) const { return fElementPtr != rh.fElementPtr; }
0903          bool operator==(const iterator &rh) const { return fElementPtr == rh.fElementPtr; }
0904       };
0905 
0906       const RIteratorFuncs &fIFuncs;
0907       const std::size_t fStride;
0908       unsigned char fBeginSmallBuf[TVirtualCollectionProxy::fgIteratorArenaSize];
0909       unsigned char fEndSmallBuf[TVirtualCollectionProxy::fgIteratorArenaSize];
0910       void *fBegin = &fBeginSmallBuf;
0911       void *fEnd = &fEndSmallBuf;
0912    public:
0913       /// Construct a `RCollectionIterableOnce` that iterates over `collection`.  If elements are guaranteed to be
0914       /// contiguous in memory (e.g. a vector), `stride` can be provided for faster iteration, i.e. the address of each
0915       /// element is known given the base pointer.
0916       RCollectionIterableOnce(void *collection, const RIteratorFuncs &ifuncs, TVirtualCollectionProxy *proxy,
0917                               std::size_t stride = 0U)
0918          : fIFuncs(ifuncs), fStride(stride)
0919       {
0920          fIFuncs.fCreateIterators(collection, &fBegin, &fEnd, proxy);
0921       }
0922       ~RCollectionIterableOnce() { fIFuncs.fDeleteTwoIterators(fBegin, fEnd); }
0923 
0924       RIterator begin() { return RIterator(*this, fBegin); }
0925       RIterator end() { return fStride ? RIterator(*this, fEnd) : RIterator(*this); }
0926    }; // class RCollectionIterableOnce
0927 
0928    class RProxiedCollectionDeleter : public RDeleter {
0929    private:
0930       std::shared_ptr<TVirtualCollectionProxy> fProxy;
0931       std::unique_ptr<RDeleter> fItemDeleter;
0932       std::size_t fItemSize = 0;
0933       RCollectionIterableOnce::RIteratorFuncs fIFuncsWrite;
0934 
0935    public:
0936       explicit RProxiedCollectionDeleter(std::shared_ptr<TVirtualCollectionProxy> proxy) : fProxy(proxy) {}
0937       RProxiedCollectionDeleter(std::shared_ptr<TVirtualCollectionProxy> proxy, std::unique_ptr<RDeleter> itemDeleter,
0938                                 size_t itemSize)
0939          : fProxy(proxy), fItemDeleter(std::move(itemDeleter)), fItemSize(itemSize)
0940       {
0941          fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
0942       }
0943       void operator()(void *objPtr, bool dtorOnly) final;
0944    };
0945 
0946    /// The collection proxy is needed by the deleters and thus defined as a shared pointer
0947    std::shared_ptr<TVirtualCollectionProxy> fProxy;
0948    Int_t fProperties;
0949    Int_t fCollectionType;
0950    /// Two sets of functions to operate on iterators, to be used depending on the access type.  The direction preserves
0951    /// the meaning from TVirtualCollectionProxy, i.e. read from disk / write to disk, respectively
0952    RCollectionIterableOnce::RIteratorFuncs fIFuncsRead;
0953    RCollectionIterableOnce::RIteratorFuncs fIFuncsWrite;
0954    std::size_t fItemSize;
0955    ClusterSize_t fNWritten;
0956 
0957    /// Constructor used when the value type of the collection is not known in advance, i.e. in the case of custom
0958    /// collections.
0959    RProxiedCollectionField(std::string_view fieldName, std::string_view typeName, TClass *classp);
0960    /// Constructor used when the value type of the collection is known in advance, e.g. in `RSetField`.
0961    RProxiedCollectionField(std::string_view fieldName, std::string_view typeName,
0962                            std::unique_ptr<RFieldBase> itemField);
0963 
0964 protected:
0965    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const override;
0966    const RColumnRepresentations &GetColumnRepresentations() const final;
0967    void GenerateColumnsImpl() final;
0968    void GenerateColumnsImpl(const RNTupleDescriptor &desc) final;
0969 
0970    void ConstructValue(void *where) const override;
0971    std::unique_ptr<RDeleter> GetDeleter() const override;
0972 
0973    std::size_t AppendImpl(const void *from) override;
0974    void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) override;
0975 
0976    void CommitClusterImpl() final { fNWritten = 0; }
0977 
0978 public:
0979    RProxiedCollectionField(std::string_view fieldName, std::string_view typeName);
0980    RProxiedCollectionField(RProxiedCollectionField &&other) = default;
0981    RProxiedCollectionField &operator=(RProxiedCollectionField &&other) = default;
0982    ~RProxiedCollectionField() override = default;
0983 
0984    std::vector<RValue> SplitValue(const RValue &value) const override;
0985    size_t GetValueSize() const override { return fProxy->Sizeof(); }
0986    size_t GetAlignment() const override { return alignof(std::max_align_t); }
0987    void AcceptVisitor(Detail::RFieldVisitor &visitor) const override;
0988    void GetCollectionInfo(NTupleSize_t globalIndex, RClusterIndex *collectionStart, ClusterSize_t *size) const
0989    {
0990       fPrincipalColumn->GetCollectionInfo(globalIndex, collectionStart, size);
0991    }
0992    void GetCollectionInfo(RClusterIndex clusterIndex, RClusterIndex *collectionStart, ClusterSize_t *size) const
0993    {
0994       fPrincipalColumn->GetCollectionInfo(clusterIndex, collectionStart, size);
0995    }
0996 };
0997 
0998 /// The field for an untyped record. The subfields are stored consequitively in a memory block, i.e.
0999 /// the memory layout is identical to one that a C++ struct would have
1000 class RRecordField : public RFieldBase {
1001 private:
1002    class RRecordDeleter : public RDeleter {
1003    private:
1004       std::vector<std::unique_ptr<RDeleter>> fItemDeleters;
1005       std::vector<std::size_t> fOffsets;
1006 
1007    public:
1008       RRecordDeleter(std::vector<std::unique_ptr<RDeleter>> &itemDeleters, const std::vector<std::size_t> &offsets)
1009          : fItemDeleters(std::move(itemDeleters)), fOffsets(offsets)
1010       {
1011       }
1012       void operator()(void *objPtr, bool dtorOnly) final;
1013    };
1014 
1015 protected:
1016    std::size_t fMaxAlignment = 1;
1017    std::size_t fSize = 0;
1018    std::vector<std::size_t> fOffsets;
1019 
1020    std::size_t GetItemPadding(std::size_t baseOffset, std::size_t itemAlignment) const;
1021 
1022    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const override;
1023 
1024    void GenerateColumnsImpl() final {}
1025    void GenerateColumnsImpl(const RNTupleDescriptor &) final {}
1026 
1027    void ConstructValue(void *where) const override;
1028    std::unique_ptr<RDeleter> GetDeleter() const override;
1029 
1030    std::size_t AppendImpl(const void *from) final;
1031    void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final;
1032    void ReadInClusterImpl(RClusterIndex clusterIndex, void *to) final;
1033 
1034    RRecordField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> &&itemFields,
1035                 const std::vector<std::size_t> &offsets, std::string_view typeName = "");
1036 
1037    template <std::size_t N>
1038    RRecordField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, N> &&itemFields,
1039                 const std::array<std::size_t, N> &offsets, std::string_view typeName = "")
1040       : ROOT::Experimental::RFieldBase(fieldName, typeName, ENTupleStructure::kRecord, false /* isSimple */)
1041    {
1042       fTraits |= kTraitTrivialType;
1043       for (unsigned i = 0; i < N; ++i) {
1044          fOffsets.push_back(offsets[i]);
1045          fMaxAlignment = std::max(fMaxAlignment, itemFields[i]->GetAlignment());
1046          fSize += GetItemPadding(fSize, itemFields[i]->GetAlignment()) + itemFields[i]->GetValueSize();
1047          fTraits &= itemFields[i]->GetTraits();
1048          Attach(std::move(itemFields[i]));
1049       }
1050    }
1051 public:
1052    /// Construct a RRecordField based on a vector of child fields. The ownership of the child fields is transferred
1053    /// to the RRecordField instance.
1054    RRecordField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> &&itemFields);
1055    RRecordField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> &itemFields);
1056    RRecordField(RRecordField&& other) = default;
1057    RRecordField& operator =(RRecordField&& other) = default;
1058    ~RRecordField() override = default;
1059 
1060    std::vector<RValue> SplitValue(const RValue &value) const final;
1061    size_t GetValueSize() const final { return fSize; }
1062    size_t GetAlignment() const final { return fMaxAlignment; }
1063    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
1064 };
1065 
1066 /// The generic field for a (nested) std::vector<Type> except for std::vector<bool>
1067 class RVectorField : public RFieldBase {
1068 private:
1069    class RVectorDeleter : public RDeleter {
1070    private:
1071       std::size_t fItemSize = 0;
1072       std::unique_ptr<RDeleter> fItemDeleter;
1073 
1074    public:
1075       RVectorDeleter() = default;
1076       RVectorDeleter(std::size_t itemSize, std::unique_ptr<RDeleter> itemDeleter)
1077          : fItemSize(itemSize), fItemDeleter(std::move(itemDeleter))
1078       {
1079       }
1080       void operator()(void *objPtr, bool dtorOnly) final;
1081    };
1082 
1083    std::size_t fItemSize;
1084    ClusterSize_t fNWritten;
1085    std::unique_ptr<RDeleter> fItemDeleter;
1086 
1087 protected:
1088    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
1089 
1090    const RColumnRepresentations &GetColumnRepresentations() const final;
1091    void GenerateColumnsImpl() final;
1092    void GenerateColumnsImpl(const RNTupleDescriptor &desc) final;
1093 
1094    void ConstructValue(void *where) const override { new (where) std::vector<char>(); }
1095    std::unique_ptr<RDeleter> GetDeleter() const final;
1096 
1097    std::size_t AppendImpl(const void *from) final;
1098    void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final;
1099 
1100    void CommitClusterImpl() final { fNWritten = 0; }
1101 
1102 public:
1103    RVectorField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField);
1104    RVectorField(RVectorField&& other) = default;
1105    RVectorField& operator =(RVectorField&& other) = default;
1106    ~RVectorField() override = default;
1107 
1108    std::vector<RValue> SplitValue(const RValue &value) const final;
1109    size_t GetValueSize() const override { return sizeof(std::vector<char>); }
1110    size_t GetAlignment() const final { return std::alignment_of<std::vector<char>>(); }
1111    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
1112    void GetCollectionInfo(NTupleSize_t globalIndex, RClusterIndex *collectionStart, ClusterSize_t *size) const {
1113       fPrincipalColumn->GetCollectionInfo(globalIndex, collectionStart, size);
1114    }
1115    void GetCollectionInfo(RClusterIndex clusterIndex, RClusterIndex *collectionStart, ClusterSize_t *size) const
1116    {
1117       fPrincipalColumn->GetCollectionInfo(clusterIndex, collectionStart, size);
1118    }
1119 };
1120 
1121 /// The type-erased field for a RVec<Type>
1122 class RRVecField : public RFieldBase {
1123 public:
1124    /// the RRVecDeleter is also used by RArrayAsRVecField and therefore declared public
1125    class RRVecDeleter : public RDeleter {
1126    private:
1127       std::size_t fItemAlignment;
1128       std::size_t fItemSize = 0;
1129       std::unique_ptr<RDeleter> fItemDeleter;
1130 
1131    public:
1132       explicit RRVecDeleter(std::size_t itemAlignment) : fItemAlignment(itemAlignment) {}
1133       RRVecDeleter(std::size_t itemAlignment, std::size_t itemSize, std::unique_ptr<RDeleter> itemDeleter)
1134          : fItemAlignment(itemAlignment), fItemSize(itemSize), fItemDeleter(std::move(itemDeleter))
1135       {
1136       }
1137       void operator()(void *objPtr, bool dtorOnly) final;
1138    };
1139 
1140    std::unique_ptr<RDeleter> fItemDeleter;
1141 
1142 protected:
1143    std::size_t fItemSize;
1144    ClusterSize_t fNWritten;
1145    std::size_t fValueSize;
1146 
1147    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const override;
1148    const RColumnRepresentations &GetColumnRepresentations() const final;
1149    void GenerateColumnsImpl() final;
1150    void GenerateColumnsImpl(const RNTupleDescriptor &desc) final;
1151 
1152    void ConstructValue(void *where) const override;
1153    std::unique_ptr<RDeleter> GetDeleter() const override;
1154 
1155    std::size_t AppendImpl(const void *from) override;
1156    void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) override;
1157    std::size_t ReadBulkImpl(const RBulkSpec &bulkSpec) final;
1158 
1159    void CommitClusterImpl() final { fNWritten = 0; }
1160 
1161 public:
1162    RRVecField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField);
1163    RRVecField(RRVecField &&) = default;
1164    RRVecField &operator=(RRVecField &&) = default;
1165    RRVecField(const RRVecField &) = delete;
1166    RRVecField &operator=(RRVecField &) = delete;
1167    ~RRVecField() override = default;
1168 
1169    std::vector<RValue> SplitValue(const RValue &value) const final;
1170    size_t GetValueSize() const override;
1171    size_t GetAlignment() const override;
1172    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
1173    void GetCollectionInfo(NTupleSize_t globalIndex, RClusterIndex *collectionStart, ClusterSize_t *size) const
1174    {
1175       fPrincipalColumn->GetCollectionInfo(globalIndex, collectionStart, size);
1176    }
1177    void GetCollectionInfo(RClusterIndex clusterIndex, RClusterIndex *collectionStart, ClusterSize_t *size) const
1178    {
1179       fPrincipalColumn->GetCollectionInfo(clusterIndex, collectionStart, size);
1180    }
1181 };
1182 
1183 /// The generic field for fixed size arrays, which do not need an offset column
1184 class RArrayField : public RFieldBase {
1185 private:
1186    class RArrayDeleter : public RDeleter {
1187    private:
1188       std::size_t fItemSize = 0;
1189       std::size_t fArrayLength = 0;
1190       std::unique_ptr<RDeleter> fItemDeleter;
1191 
1192    public:
1193       RArrayDeleter(std::size_t itemSize, std::size_t arrayLength, std::unique_ptr<RDeleter> itemDeleter)
1194          : fItemSize(itemSize), fArrayLength(arrayLength), fItemDeleter(std::move(itemDeleter))
1195       {
1196       }
1197       void operator()(void *objPtr, bool dtorOnly) final;
1198    };
1199 
1200    std::size_t fItemSize;
1201    std::size_t fArrayLength;
1202 
1203 protected:
1204    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
1205 
1206    void GenerateColumnsImpl() final {}
1207    void GenerateColumnsImpl(const RNTupleDescriptor &) final {}
1208 
1209    void ConstructValue(void *where) const override;
1210    std::unique_ptr<RDeleter> GetDeleter() const final;
1211 
1212    std::size_t AppendImpl(const void *from) final;
1213    void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final;
1214    void ReadInClusterImpl(RClusterIndex clusterIndex, void *to) final;
1215 
1216 public:
1217    RArrayField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField, std::size_t arrayLength);
1218    RArrayField(RArrayField &&other) = default;
1219    RArrayField& operator =(RArrayField &&other) = default;
1220    ~RArrayField() override = default;
1221 
1222    std::vector<RValue> SplitValue(const RValue &value) const final;
1223    size_t GetLength() const { return fArrayLength; }
1224    size_t GetValueSize() const final { return fItemSize * fArrayLength; }
1225    size_t GetAlignment() const final { return fSubFields[0]->GetAlignment(); }
1226    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
1227 };
1228 
1229 /**
1230 \class ROOT::Experimental::RArrayAsRVecField
1231 \brief A field for fixed-size arrays that are represented as RVecs in memory.
1232 \ingroup ntuple
1233 This class is used only for reading. In particular, it helps exposing
1234 arbitrarily-nested std::array on-disk fields as RVecs for usage in RDataFrame.
1235 */
1236 class RArrayAsRVecField final : public RFieldBase {
1237 private:
1238    std::unique_ptr<RDeleter> fItemDeleter; /// Sub field deleter or nullptr for simple fields
1239    std::size_t fItemSize;                  /// The size of a child field's item
1240    std::size_t fArrayLength;               /// The length of the arrays in this field
1241    std::size_t fValueSize;                 /// The size of a value of this field, i.e. an RVec
1242 
1243 protected:
1244    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
1245 
1246    void GenerateColumnsImpl() final { R__ASSERT(false && "RArrayAsRVec fields must only be used for reading"); }
1247    void GenerateColumnsImpl(const RNTupleDescriptor &) final {}
1248 
1249    void ConstructValue(void *where) const final;
1250    /// Returns an RRVecField::RRVecDeleter
1251    std::unique_ptr<RDeleter> GetDeleter() const final;
1252 
1253    void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final;
1254    void ReadInClusterImpl(RClusterIndex clusterIndex, void *to) final;
1255 
1256 public:
1257    /**
1258       Constructor of the field. the \p itemField argument represents the inner
1259       item of the on-disk array, i.e. for an `std::array<float>` it is the `float`
1260       field and not the `std::array` itself.
1261    */
1262    RArrayAsRVecField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField, std::size_t arrayLength);
1263    RArrayAsRVecField(const RArrayAsRVecField &other) = delete;
1264    RArrayAsRVecField &operator=(const RArrayAsRVecField &other) = delete;
1265    RArrayAsRVecField(RArrayAsRVecField &&other) = default;
1266    RArrayAsRVecField &operator=(RArrayAsRVecField &&other) = default;
1267    ~RArrayAsRVecField() final = default;
1268 
1269    std::size_t GetValueSize() const final { return fValueSize; }
1270    std::size_t GetAlignment() const final;
1271 
1272    std::vector<RFieldBase::RValue> SplitValue(const RFieldBase::RValue &value) const final;
1273    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
1274 };
1275 
1276 /// The generic field an std::bitset<N>. All compilers we care about store the bits in an array of unsigned long.
1277 /// TODO(jblomer): reading and writing efficiency should be improved; currently it is one bit at a time
1278 /// with an array of bools on the page level.
1279 class RBitsetField : public RFieldBase {
1280    using Word_t = unsigned long;
1281    static constexpr std::size_t kWordSize = sizeof(Word_t);
1282    static constexpr std::size_t kBitsPerWord = kWordSize * 8;
1283 
1284 protected:
1285    std::size_t fN;
1286 
1287 protected:
1288    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
1289    {
1290       return std::make_unique<RBitsetField>(newName, fN);
1291    }
1292    const RColumnRepresentations &GetColumnRepresentations() const final;
1293    void GenerateColumnsImpl() final;
1294    void GenerateColumnsImpl(const RNTupleDescriptor &desc) final;
1295    void ConstructValue(void *where) const final { memset(where, 0, GetValueSize()); }
1296    std::size_t AppendImpl(const void *from) final;
1297    void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final;
1298 
1299 public:
1300    RBitsetField(std::string_view fieldName, std::size_t N);
1301    RBitsetField(RBitsetField &&other) = default;
1302    RBitsetField &operator=(RBitsetField &&other) = default;
1303    ~RBitsetField() override = default;
1304 
1305    size_t GetValueSize() const final { return kWordSize * ((fN + kBitsPerWord - 1) / kBitsPerWord); }
1306    size_t GetAlignment() const final { return alignof(Word_t); }
1307    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
1308 
1309    /// Get the number of bits in the bitset, i.e. the N in std::bitset<N>
1310    std::size_t GetN() const { return fN; }
1311 };
1312 
1313 /// The generic field for std::variant types
1314 class RVariantField : public RFieldBase {
1315 private:
1316    class RVariantDeleter : public RDeleter {
1317    private:
1318       std::size_t fTagOffset;
1319       std::vector<std::unique_ptr<RDeleter>> fItemDeleters;
1320 
1321    public:
1322       RVariantDeleter(std::size_t tagOffset, std::vector<std::unique_ptr<RDeleter>> &itemDeleters)
1323          : fTagOffset(tagOffset), fItemDeleters(std::move(itemDeleters))
1324       {
1325       }
1326       void operator()(void *objPtr, bool dtorOnly) final;
1327    };
1328 
1329    size_t fMaxItemSize = 0;
1330    size_t fMaxAlignment = 1;
1331    /// In the std::variant memory layout, at which byte number is the index stored
1332    size_t fTagOffset = 0;
1333    std::vector<ClusterSize_t::ValueType> fNWritten;
1334 
1335    static std::string GetTypeList(const std::vector<RFieldBase *> &itemFields);
1336    /// Extracts the index from an std::variant and transforms it into the 1-based index used for the switch column
1337    static std::uint32_t GetTag(const void *variantPtr, std::size_t tagOffset);
1338    static void SetTag(void *variantPtr, std::size_t tagOffset, std::uint32_t tag);
1339 
1340 protected:
1341    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
1342 
1343    const RColumnRepresentations &GetColumnRepresentations() const final;
1344    void GenerateColumnsImpl() final;
1345    void GenerateColumnsImpl(const RNTupleDescriptor &desc) final;
1346 
1347    void ConstructValue(void *where) const override;
1348    std::unique_ptr<RDeleter> GetDeleter() const final;
1349 
1350    std::size_t AppendImpl(const void *from) final;
1351    void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final;
1352 
1353    void CommitClusterImpl() final;
1354 
1355 public:
1356    // TODO(jblomer): use std::span in signature
1357    RVariantField(std::string_view fieldName, const std::vector<RFieldBase *> &itemFields);
1358    RVariantField(RVariantField &&other) = default;
1359    RVariantField& operator =(RVariantField &&other) = default;
1360    ~RVariantField() override = default;
1361 
1362    size_t GetValueSize() const final;
1363    size_t GetAlignment() const final { return fMaxAlignment; }
1364 };
1365 
1366 /// The generic field for a std::set<Type> and std::unordered_set<Type>
1367 class RSetField : public RProxiedCollectionField {
1368 protected:
1369    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
1370 
1371 public:
1372    RSetField(std::string_view fieldName, std::string_view typeName, std::unique_ptr<RFieldBase> itemField);
1373    RSetField(RSetField &&other) = default;
1374    RSetField &operator=(RSetField &&other) = default;
1375    ~RSetField() override = default;
1376 
1377    size_t GetAlignment() const override { return std::alignment_of<std::set<std::max_align_t>>(); }
1378 };
1379 
1380 /// The generic field for a std::map<KeyType, ValueType> and std::unordered_map<KeyType, ValueType>
1381 class RMapField : public RProxiedCollectionField {
1382 private:
1383    TClass *fItemClass;
1384 
1385 protected:
1386    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
1387 
1388    std::size_t AppendImpl(const void *from) final;
1389    void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final;
1390 
1391 public:
1392    RMapField(std::string_view fieldName, std::string_view typeName, std::unique_ptr<RFieldBase> itemField);
1393    RMapField(RMapField &&other) = default;
1394    RMapField &operator=(RMapField &&other) = default;
1395    ~RMapField() override = default;
1396 
1397    std::vector<RValue> SplitValue(const RValue &value) const final;
1398 
1399    size_t GetAlignment() const override { return std::alignment_of<std::map<std::max_align_t, std::max_align_t>>(); }
1400 };
1401 
1402 /// The field for values that may or may not be present in an entry. Parent class for unique pointer field and
1403 /// optional field. A nullable field cannot be instantiated itself but only its descendants.
1404 /// The RNullableField takes care of the on-disk representation. Child classes are responsible for the in-memory
1405 /// representation.  The on-disk representation can be "dense" or "sparse". Dense nullable fields have a bitmask
1406 /// (true: item available, false: item missing) and serialize a default-constructed item for missing items.
1407 /// Sparse nullable fields use a (Split)Index[64|32] column to point to the available items.
1408 /// By default, items whose size is smaller or equal to 4 bytes (size of (Split)Index32 column element) are stored
1409 /// densely.
1410 class RNullableField : public RFieldBase {
1411    /// For a dense nullable field, used to write a default-constructed item for missing ones.
1412    std::unique_ptr<RValue> fDefaultItemValue;
1413    /// For a sparse nullable field, the number of written non-null items in this cluster
1414    ClusterSize_t fNWritten{0};
1415 
1416 protected:
1417    const RFieldBase::RColumnRepresentations &GetColumnRepresentations() const final;
1418    void GenerateColumnsImpl() final;
1419    void GenerateColumnsImpl(const RNTupleDescriptor &) final;
1420 
1421    std::size_t AppendNull();
1422    std::size_t AppendValue(const void *from);
1423    void CommitClusterImpl() final { fNWritten = 0; }
1424 
1425    /// Given the index of the nullable field, returns the corresponding global index of the subfield or,
1426    /// if it is null, returns kInvalidClusterIndex
1427    RClusterIndex GetItemIndex(NTupleSize_t globalIndex);
1428 
1429    RNullableField(std::string_view fieldName, std::string_view typeName, std::unique_ptr<RFieldBase> itemField);
1430 
1431 public:
1432    RNullableField(RNullableField &&other) = default;
1433    RNullableField &operator=(RNullableField &&other) = default;
1434    ~RNullableField() override = default;
1435 
1436    bool IsDense() const { return GetColumnRepresentative()[0] ==  EColumnType::kBit; }
1437    bool IsSparse() const { return !IsDense(); }
1438    void SetDense() { SetColumnRepresentative({EColumnType::kBit}); }
1439    void SetSparse() { SetColumnRepresentative({EColumnType::kSplitIndex32}); }
1440 
1441    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
1442 };
1443 
1444 class RUniquePtrField : public RNullableField {
1445    class RUniquePtrDeleter : public RDeleter {
1446    private:
1447       std::unique_ptr<RDeleter> fItemDeleter;
1448 
1449    public:
1450       explicit RUniquePtrDeleter(std::unique_ptr<RDeleter> itemDeleter) : fItemDeleter(std::move(itemDeleter)) {}
1451       void operator()(void *objPtr, bool dtorOnly) final;
1452    };
1453 
1454    std::unique_ptr<RDeleter> fItemDeleter;
1455 
1456 protected:
1457    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
1458 
1459    void ConstructValue(void *where) const final { new (where) std::unique_ptr<char>(); }
1460    std::unique_ptr<RDeleter> GetDeleter() const final;
1461 
1462    std::size_t AppendImpl(const void *from) final;
1463    void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final;
1464 
1465 public:
1466    RUniquePtrField(std::string_view fieldName, std::string_view typeName, std::unique_ptr<RFieldBase> itemField);
1467    RUniquePtrField(RUniquePtrField &&other) = default;
1468    RUniquePtrField &operator=(RUniquePtrField &&other) = default;
1469    ~RUniquePtrField() override = default;
1470 
1471    std::vector<RValue> SplitValue(const RValue &value) const final;
1472    size_t GetValueSize() const final { return sizeof(std::unique_ptr<char>); }
1473    size_t GetAlignment() const final { return alignof(std::unique_ptr<char>); }
1474 };
1475 
1476 class RAtomicField : public RFieldBase {
1477 protected:
1478    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
1479    void GenerateColumnsImpl() final {}
1480    void GenerateColumnsImpl(const RNTupleDescriptor &) final {}
1481 
1482    void ConstructValue(void *where) const final { CallConstructValueOn(*fSubFields[0], where); }
1483    std::unique_ptr<RDeleter> GetDeleter() const final { return GetDeleterOf(*fSubFields[0]); }
1484 
1485    std::size_t AppendImpl(const void *from) final { return CallAppendOn(*fSubFields[0], from); }
1486    void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final { CallReadOn(*fSubFields[0], globalIndex, to); }
1487    void ReadInClusterImpl(RClusterIndex clusterIndex, void *to) final { CallReadOn(*fSubFields[0], clusterIndex, to); }
1488 
1489 public:
1490    RAtomicField(std::string_view fieldName, std::string_view typeName, std::unique_ptr<RFieldBase> itemField);
1491    RAtomicField(RAtomicField &&other) = default;
1492    RAtomicField &operator=(RAtomicField &&other) = default;
1493    ~RAtomicField() override = default;
1494 
1495    std::vector<RValue> SplitValue(const RValue &value) const final;
1496 
1497    size_t GetValueSize() const final { return fSubFields[0]->GetValueSize(); }
1498    size_t GetAlignment() const final { return fSubFields[0]->GetAlignment(); }
1499 
1500    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
1501 };
1502 
1503 /// Classes with dictionaries that can be inspected by TClass
1504 template <typename T, typename = void>
1505 class RField final : public RClassField {
1506 protected:
1507    void ConstructValue(void *where) const final
1508    {
1509       if constexpr (std::is_default_constructible_v<T>) {
1510          new (where) T();
1511       } else {
1512          // If there is no default constructor, try with the IO constructor
1513          new (where) T(static_cast<TRootIOCtor *>(nullptr));
1514       }
1515    }
1516 
1517 public:
1518    static std::string TypeName() { return ROOT::Internal::GetDemangledTypeName(typeid(T)); }
1519    RField(std::string_view name) : RClassField(name, TypeName()) {
1520       static_assert(std::is_class_v<T>, "no I/O support for this basic C++ type");
1521    }
1522    RField(RField &&other) = default;
1523    RField &operator=(RField &&other) = default;
1524    ~RField() override = default;
1525 };
1526 
1527 template <typename T>
1528 class RField<T, typename std::enable_if<std::is_enum_v<T>>::type> : public REnumField {
1529 public:
1530    static std::string TypeName() { return ROOT::Internal::GetDemangledTypeName(typeid(T)); }
1531    RField(std::string_view name) : REnumField(name, TypeName()) {}
1532    RField(RField &&other) = default;
1533    RField &operator=(RField &&other) = default;
1534    ~RField() override = default;
1535 };
1536 
1537 template <typename T, typename = void>
1538 struct HasCollectionProxyMemberType : std::false_type {
1539 };
1540 template <typename T>
1541 struct HasCollectionProxyMemberType<
1542    T, typename std::enable_if<std::is_same<typename T::IsCollectionProxy, std::true_type>::value>::type>
1543    : std::true_type {
1544 };
1545 
1546 /// The point here is that we can only tell at run time if a class has an associated collection proxy.
1547 /// For compile time, in the first iteration of this PR we had an extra template argument that acted as a "tag" to
1548 /// differentiate the RField specialization for classes with an associated collection proxy (inherits
1549 /// `RProxiedCollectionField`) from the RField primary template definition (`RClassField`-derived), as in:
1550 /// ```
1551 /// auto field = std::make_unique<RField<MyClass>>("klass");
1552 /// // vs
1553 /// auto otherField = std::make_unique<RField<MyClass, ROOT::Experimental::TagIsCollectionProxy>>("klass");
1554 /// ```
1555 ///
1556 /// That is convenient only for non-nested types, i.e. it doesn't work with, e.g. `RField<std::vector<MyClass>,
1557 /// ROOT::Experimental::TagIsCollectionProxy>`, as the tag is not forwarded to the instantiation of the inner RField
1558 /// (that for the value type of the vector).  The following two possible solutions were considered:
1559 /// - A wrapper type (much like `ntuple/v7/inc/ROOT/RNTupleUtil.hxx:49`), that helps to differentiate both cases.
1560 /// There we would have:
1561 /// ```
1562 /// auto field = std::make_unique<RField<RProxiedCollection<MyClass>>>("klass"); // Using collection proxy
1563 /// ```
1564 /// - A helper `IsCollectionProxy<T>` type, that can be used in a similar way to those in the `<type_traits>` header.
1565 /// We found this more convenient and is the implemented thing below.  Here, classes can be marked as a
1566 /// collection proxy with either of the following two forms (whichever is more convenient for the user):
1567 /// ```
1568 /// template <>
1569 /// struct IsCollectionProxy<MyClass> : std::true_type {};
1570 /// ```
1571 /// or by adding a member type to the class as follows:
1572 /// ```
1573 /// class MyClass {
1574 /// public:
1575 ///    using IsCollectionProxy = std::true_type;
1576 /// };
1577 /// ```
1578 ///
1579 /// Of course, there is another possible solution which is to have a single `RClassField` that implements both
1580 /// the regular-class and the collection-proxy behaviors, and always chooses appropriately at run time.
1581 /// We found that less clean and probably has more overhead, as most probably it involves an additional branch + call
1582 /// in each of the member functions.
1583 template <typename T, typename = void>
1584 struct IsCollectionProxy : HasCollectionProxyMemberType<T> {
1585 };
1586 
1587 /// Classes behaving as a collection of elements that can be queried via the `TVirtualCollectionProxy` interface
1588 /// The use of a collection proxy for a particular class can be enabled via:
1589 /// ```
1590 /// namespace ROOT::Experimental {
1591 ///    template <> struct IsCollectionProxy<Classname> : std::true_type {};
1592 /// }
1593 /// ```
1594 /// Alternatively, this can be achieved by adding a member type to the class definition as follows:
1595 /// ```
1596 /// class Classname {
1597 /// public:
1598 ///    using IsCollectionProxy = std::true_type;
1599 /// };
1600 /// ```
1601 template <typename T>
1602 class RField<T, typename std::enable_if<IsCollectionProxy<T>::value>::type> final : public RProxiedCollectionField {
1603 protected:
1604    void ConstructValue(void *where) const final { new (where) T(); }
1605 
1606 public:
1607    static std::string TypeName() { return ROOT::Internal::GetDemangledTypeName(typeid(T)); }
1608    RField(std::string_view name) : RProxiedCollectionField(name, TypeName())
1609    {
1610       static_assert(std::is_class<T>::value, "collection proxy unsupported for fundamental types");
1611    }
1612    RField(RField&& other) = default;
1613    RField& operator =(RField&& other) = default;
1614    ~RField() override = default;
1615 };
1616 
1617 /// The collection field is only used for writing; when reading, untyped collections are projected to an std::vector
1618 class RCollectionField final : public ROOT::Experimental::RFieldBase {
1619 private:
1620    /// Save the link to the collection ntuple in order to reset the offset counter when committing the cluster
1621    std::shared_ptr<RNTupleCollectionWriter> fCollectionWriter;
1622 
1623 protected:
1624    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
1625    const RColumnRepresentations &GetColumnRepresentations() const final;
1626    void GenerateColumnsImpl() final;
1627    void GenerateColumnsImpl(const RNTupleDescriptor &desc) final;
1628    void ConstructValue(void *) const final {}
1629 
1630    std::size_t AppendImpl(const void *from) final;
1631    void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final;
1632 
1633    void CommitClusterImpl() final;
1634 
1635 public:
1636    static std::string TypeName() { return ""; }
1637    RCollectionField(std::string_view name, std::shared_ptr<RNTupleCollectionWriter> collectionWriter,
1638                     std::unique_ptr<RFieldZero> collectionParent);
1639    RCollectionField(RCollectionField&& other) = default;
1640    RCollectionField& operator =(RCollectionField&& other) = default;
1641    ~RCollectionField() override = default;
1642 
1643    size_t GetValueSize() const final { return sizeof(ClusterSize_t); }
1644    size_t GetAlignment() const final { return alignof(ClusterSize_t); }
1645 };
1646 
1647 /// The generic field for `std::pair<T1, T2>` types
1648 class RPairField : public RRecordField {
1649 private:
1650    class RPairDeleter : public RDeleter {
1651    private:
1652       TClass *fClass;
1653 
1654    public:
1655       explicit RPairDeleter(TClass *cl) : fClass(cl) {}
1656       void operator()(void *objPtr, bool dtorOnly) final;
1657    };
1658 
1659    TClass *fClass = nullptr;
1660    static std::string GetTypeList(const std::array<std::unique_ptr<RFieldBase>, 2> &itemFields);
1661 
1662 protected:
1663    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const override;
1664 
1665    void ConstructValue(void *where) const override;
1666    std::unique_ptr<RDeleter> GetDeleter() const override { return std::make_unique<RPairDeleter>(fClass); }
1667 
1668    RPairField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, 2> &&itemFields,
1669               const std::array<std::size_t, 2> &offsets);
1670 
1671 public:
1672    RPairField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, 2> &itemFields);
1673    RPairField(RPairField &&other) = default;
1674    RPairField &operator=(RPairField &&other) = default;
1675    ~RPairField() override = default;
1676 };
1677 
1678 /// The generic field for `std::tuple<Ts...>` types
1679 class RTupleField : public RRecordField {
1680 private:
1681    class RTupleDeleter : public RDeleter {
1682    private:
1683       TClass *fClass;
1684 
1685    public:
1686       explicit RTupleDeleter(TClass *cl) : fClass(cl) {}
1687       void operator()(void *objPtr, bool dtorOnly) final;
1688    };
1689 
1690    TClass *fClass = nullptr;
1691    static std::string GetTypeList(const std::vector<std::unique_ptr<RFieldBase>> &itemFields);
1692 
1693 protected:
1694    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const override;
1695 
1696    void ConstructValue(void *where) const override;
1697    std::unique_ptr<RDeleter> GetDeleter() const override { return std::make_unique<RTupleDeleter>(fClass); }
1698 
1699    RTupleField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> &&itemFields,
1700                const std::vector<std::size_t> &offsets);
1701 
1702 public:
1703    RTupleField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> &itemFields);
1704    RTupleField(RTupleField &&other) = default;
1705    RTupleField &operator=(RTupleField &&other) = default;
1706    ~RTupleField() override = default;
1707 };
1708 
1709 /// An artificial field that transforms an RNTuple column that contains the offset of collections into
1710 /// collection sizes. It is only used for reading, e.g. as projected field or as an artificial field that provides the
1711 /// "number of" RDF columns for collections (e.g. `R_rdf_sizeof_jets` for a collection named `jets`).
1712 /// It is used in the templated RField<RNTupleCardinality<SizeT>> form, which represents the collection sizes either
1713 /// as 32bit unsigned int (std::uint32_t) or as 64bit unsigned int (std::uint64_t).
1714 class RCardinalityField : public RFieldBase {
1715 protected:
1716    RCardinalityField(std::string_view fieldName, std::string_view typeName)
1717       : RFieldBase(fieldName, typeName, ENTupleStructure::kLeaf, false /* isSimple */)
1718    {
1719    }
1720 
1721    const RColumnRepresentations &GetColumnRepresentations() const final;
1722    // Field is only used for reading
1723    void GenerateColumnsImpl() final { throw RException(R__FAIL("Cardinality fields must only be used for reading")); }
1724    void GenerateColumnsImpl(const RNTupleDescriptor &) final;
1725 
1726 public:
1727    RCardinalityField(RCardinalityField &&other) = default;
1728    RCardinalityField &operator=(RCardinalityField &&other) = default;
1729    ~RCardinalityField() override = default;
1730 
1731    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
1732 
1733    const RField<RNTupleCardinality<std::uint32_t>> *As32Bit() const;
1734    const RField<RNTupleCardinality<std::uint64_t>> *As64Bit() const;
1735 };
1736 
1737 ////////////////////////////////////////////////////////////////////////////////
1738 /// Template specializations for concrete C++ types
1739 ////////////////////////////////////////////////////////////////////////////////
1740 
1741 template <>
1742 class RField<void> : public RFieldBase {
1743 public:
1744    static std::string TypeName() { return "void"; }
1745    // RField<void> should never be constructed.
1746    RField() = delete;
1747    RField(const RField &) = delete;
1748    RField &operator=(const RField &) = delete;
1749 };
1750 
1751 template <>
1752 class RField<ClusterSize_t> final : public RFieldBase {
1753 protected:
1754    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
1755    {
1756       return std::make_unique<RField>(newName);
1757    }
1758 
1759    const RColumnRepresentations &GetColumnRepresentations() const final;
1760    void GenerateColumnsImpl() final;
1761    void GenerateColumnsImpl(const RNTupleDescriptor &desc) final;
1762    void ConstructValue(void *where) const final { new (where) ClusterSize_t(0); }
1763 
1764 public:
1765    static std::string TypeName() { return "ROOT::Experimental::ClusterSize_t"; }
1766    explicit RField(std::string_view name) : RFieldBase(name, TypeName(), ENTupleStructure::kLeaf, true /* isSimple */)
1767    {
1768       fTraits |= kTraitTrivialType;
1769    }
1770    RField(RField&& other) = default;
1771    RField& operator =(RField&& other) = default;
1772    ~RField() override = default;
1773 
1774    ClusterSize_t *Map(NTupleSize_t globalIndex) {
1775       return fPrincipalColumn->Map<ClusterSize_t>(globalIndex);
1776    }
1777    ClusterSize_t *Map(RClusterIndex clusterIndex) { return fPrincipalColumn->Map<ClusterSize_t>(clusterIndex); }
1778    ClusterSize_t *MapV(NTupleSize_t globalIndex, NTupleSize_t &nItems) {
1779       return fPrincipalColumn->MapV<ClusterSize_t>(globalIndex, nItems);
1780    }
1781    ClusterSize_t *MapV(RClusterIndex clusterIndex, NTupleSize_t &nItems)
1782    {
1783       return fPrincipalColumn->MapV<ClusterSize_t>(clusterIndex, nItems);
1784    }
1785 
1786    size_t GetValueSize() const final { return sizeof(ClusterSize_t); }
1787    size_t GetAlignment() const final { return alignof(ClusterSize_t); }
1788 
1789    /// Special help for offset fields
1790    void GetCollectionInfo(NTupleSize_t globalIndex, RClusterIndex *collectionStart, ClusterSize_t *size) {
1791       fPrincipalColumn->GetCollectionInfo(globalIndex, collectionStart, size);
1792    }
1793    void GetCollectionInfo(RClusterIndex clusterIndex, RClusterIndex *collectionStart, ClusterSize_t *size)
1794    {
1795       fPrincipalColumn->GetCollectionInfo(clusterIndex, collectionStart, size);
1796    }
1797    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
1798 };
1799 
1800 template <typename SizeT>
1801 class RField<RNTupleCardinality<SizeT>> final : public RCardinalityField {
1802 protected:
1803    std::unique_ptr<ROOT::Experimental::RFieldBase> CloneImpl(std::string_view newName) const final
1804    {
1805       return std::make_unique<RField<RNTupleCardinality<SizeT>>>(newName);
1806    }
1807    void ConstructValue(void *where) const final { new (where) RNTupleCardinality<SizeT>(0); }
1808 
1809 public:
1810    static std::string TypeName() { return "ROOT::Experimental::RNTupleCardinality<" + RField<SizeT>::TypeName() + ">"; }
1811    explicit RField(std::string_view name) : RCardinalityField(name, TypeName()) {}
1812    RField(RField &&other) = default;
1813    RField &operator=(RField &&other) = default;
1814    ~RField() override = default;
1815 
1816    size_t GetValueSize() const final { return sizeof(RNTupleCardinality<SizeT>); }
1817    size_t GetAlignment() const final { return alignof(RNTupleCardinality<SizeT>); }
1818 
1819    /// Get the number of elements of the collection identified by globalIndex
1820    void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
1821    {
1822       RClusterIndex collectionStart;
1823       ClusterSize_t size;
1824       fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &size);
1825       *static_cast<RNTupleCardinality<SizeT> *>(to) = size;
1826    }
1827 
1828    /// Get the number of elements of the collection identified by clusterIndex
1829    void ReadInClusterImpl(RClusterIndex clusterIndex, void *to) final
1830    {
1831       RClusterIndex collectionStart;
1832       ClusterSize_t size;
1833       fPrincipalColumn->GetCollectionInfo(clusterIndex, &collectionStart, &size);
1834       *static_cast<RNTupleCardinality<SizeT> *>(to) = size;
1835    }
1836 
1837    std::size_t ReadBulkImpl(const RBulkSpec &bulkSpec) final
1838    {
1839       RClusterIndex collectionStart;
1840       ClusterSize_t collectionSize;
1841       fPrincipalColumn->GetCollectionInfo(bulkSpec.fFirstIndex, &collectionStart, &collectionSize);
1842 
1843       auto typedValues = static_cast<RNTupleCardinality<SizeT> *>(bulkSpec.fValues);
1844       typedValues[0] = collectionSize;
1845 
1846       auto lastOffset = collectionStart.GetIndex() + collectionSize;
1847       ClusterSize_t::ValueType nRemainingEntries = bulkSpec.fCount - 1;
1848       std::size_t nEntries = 1;
1849       while (nRemainingEntries > 0) {
1850          NTupleSize_t nItemsUntilPageEnd;
1851          auto offsets = fPrincipalColumn->MapV<ClusterSize_t>(bulkSpec.fFirstIndex + nEntries, nItemsUntilPageEnd);
1852          std::size_t nBatch = std::min(nRemainingEntries, nItemsUntilPageEnd);
1853          for (std::size_t i = 0; i < nBatch; ++i) {
1854             typedValues[nEntries + i] = offsets[i] - lastOffset;
1855             lastOffset = offsets[i];
1856          }
1857          nRemainingEntries -= nBatch;
1858          nEntries += nBatch;
1859       }
1860       return RBulkSpec::kAllSet;
1861    }
1862 };
1863 
1864 template <>
1865 class RField<bool> final : public RFieldBase {
1866 protected:
1867    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
1868    {
1869       return std::make_unique<RField>(newName);
1870    }
1871 
1872    const RColumnRepresentations &GetColumnRepresentations() const final;
1873    void GenerateColumnsImpl() final;
1874    void GenerateColumnsImpl(const RNTupleDescriptor &desc) final;
1875    void ConstructValue(void *where) const final { new (where) bool(false); }
1876 
1877 public:
1878    static std::string TypeName() { return "bool"; }
1879    explicit RField(std::string_view name) : RFieldBase(name, TypeName(), ENTupleStructure::kLeaf, true /* isSimple */)
1880    {
1881       fTraits |= kTraitTrivialType;
1882    }
1883    RField(RField&& other) = default;
1884    RField& operator =(RField&& other) = default;
1885    ~RField() override = default;
1886 
1887    bool *Map(NTupleSize_t globalIndex) {
1888       return fPrincipalColumn->Map<bool>(globalIndex);
1889    }
1890    bool *Map(RClusterIndex clusterIndex) { return fPrincipalColumn->Map<bool>(clusterIndex); }
1891    bool *MapV(NTupleSize_t globalIndex, NTupleSize_t &nItems) {
1892       return fPrincipalColumn->MapV<bool>(globalIndex, nItems);
1893    }
1894    bool *MapV(RClusterIndex clusterIndex, NTupleSize_t &nItems)
1895    {
1896       return fPrincipalColumn->MapV<bool>(clusterIndex, nItems);
1897    }
1898 
1899    size_t GetValueSize() const final { return sizeof(bool); }
1900    size_t GetAlignment() const final { return alignof(bool); }
1901    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
1902 };
1903 
1904 template <>
1905 class RField<float> final : public RFieldBase {
1906 protected:
1907    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
1908    {
1909       return std::make_unique<RField>(newName);
1910    }
1911 
1912    const RColumnRepresentations &GetColumnRepresentations() const final;
1913    void GenerateColumnsImpl() final;
1914    void GenerateColumnsImpl(const RNTupleDescriptor &desc) final;
1915    void ConstructValue(void *where) const final { new (where) float(0.0); }
1916 
1917 public:
1918    static std::string TypeName() { return "float"; }
1919    explicit RField(std::string_view name) : RFieldBase(name, TypeName(), ENTupleStructure::kLeaf, true /* isSimple */)
1920    {
1921       fTraits |= kTraitTrivialType;
1922    }
1923    RField(RField&& other) = default;
1924    RField& operator =(RField&& other) = default;
1925    ~RField() override = default;
1926 
1927    float *Map(NTupleSize_t globalIndex) {
1928       return fPrincipalColumn->Map<float>(globalIndex);
1929    }
1930    float *Map(RClusterIndex clusterIndex) { return fPrincipalColumn->Map<float>(clusterIndex); }
1931    float *MapV(NTupleSize_t globalIndex, NTupleSize_t &nItems) {
1932       return fPrincipalColumn->MapV<float>(globalIndex, nItems);
1933    }
1934    float *MapV(RClusterIndex clusterIndex, NTupleSize_t &nItems)
1935    {
1936       return fPrincipalColumn->MapV<float>(clusterIndex, nItems);
1937    }
1938 
1939    size_t GetValueSize() const final { return sizeof(float); }
1940    size_t GetAlignment() const final { return alignof(float); }
1941    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
1942 
1943    void SetHalfPrecision();
1944 };
1945 
1946 template <>
1947 class RField<double> final : public RFieldBase {
1948 protected:
1949    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
1950    {
1951       return std::make_unique<RField>(newName);
1952    }
1953 
1954    const RColumnRepresentations &GetColumnRepresentations() const final;
1955    void GenerateColumnsImpl() final;
1956    void GenerateColumnsImpl(const RNTupleDescriptor &desc) final;
1957    void ConstructValue(void *where) const final { new (where) double(0.0); }
1958 
1959 public:
1960    static std::string TypeName() { return "double"; }
1961    explicit RField(std::string_view name) : RFieldBase(name, TypeName(), ENTupleStructure::kLeaf, true /* isSimple */)
1962    {
1963       fTraits |= kTraitTrivialType;
1964    }
1965    RField(RField&& other) = default;
1966    RField& operator =(RField&& other) = default;
1967    ~RField() override = default;
1968 
1969    double *Map(NTupleSize_t globalIndex) {
1970       return fPrincipalColumn->Map<double>(globalIndex);
1971    }
1972    double *Map(RClusterIndex clusterIndex) { return fPrincipalColumn->Map<double>(clusterIndex); }
1973    double *MapV(NTupleSize_t globalIndex, NTupleSize_t &nItems) {
1974       return fPrincipalColumn->MapV<double>(globalIndex, nItems);
1975    }
1976    double *MapV(RClusterIndex clusterIndex, NTupleSize_t &nItems)
1977    {
1978       return fPrincipalColumn->MapV<double>(clusterIndex, nItems);
1979    }
1980 
1981    size_t GetValueSize() const final { return sizeof(double); }
1982    size_t GetAlignment() const final { return alignof(double); }
1983    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
1984 
1985    // Set the column representation to 32 bit floating point and the type alias to Double32_t
1986    void SetDouble32();
1987 };
1988 
1989 template <>
1990 class RField<std::byte> final : public RFieldBase {
1991 protected:
1992    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
1993    {
1994       return std::make_unique<RField>(newName);
1995    }
1996 
1997    const RColumnRepresentations &GetColumnRepresentations() const final;
1998    void GenerateColumnsImpl() final;
1999    void GenerateColumnsImpl(const RNTupleDescriptor &desc) final;
2000    void ConstructValue(void *where) const final { new (where) std::byte{0}; }
2001 
2002 public:
2003    static std::string TypeName() { return "std::byte"; }
2004    explicit RField(std::string_view name) : RFieldBase(name, TypeName(), ENTupleStructure::kLeaf, true /* isSimple */)
2005    {
2006       fTraits |= kTraitTrivialType;
2007    }
2008    RField(RField &&other) = default;
2009    RField &operator=(RField &&other) = default;
2010    ~RField() override = default;
2011 
2012    std::byte *Map(NTupleSize_t globalIndex) { return fPrincipalColumn->Map<std::byte>(globalIndex); }
2013    std::byte *Map(RClusterIndex clusterIndex) { return fPrincipalColumn->Map<std::byte>(clusterIndex); }
2014    std::byte *MapV(NTupleSize_t globalIndex, NTupleSize_t &nItems)
2015    {
2016       return fPrincipalColumn->MapV<std::byte>(globalIndex, nItems);
2017    }
2018    std::byte *MapV(RClusterIndex clusterIndex, NTupleSize_t &nItems)
2019    {
2020       return fPrincipalColumn->MapV<std::byte>(clusterIndex, nItems);
2021    }
2022 
2023    size_t GetValueSize() const final { return sizeof(std::byte); }
2024    size_t GetAlignment() const final { return alignof(std::byte); }
2025    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
2026 };
2027 
2028 template <>
2029 class RField<char> final : public RFieldBase {
2030 protected:
2031    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
2032    {
2033       return std::make_unique<RField>(newName);
2034    }
2035 
2036    const RColumnRepresentations &GetColumnRepresentations() const final;
2037    void GenerateColumnsImpl() final;
2038    void GenerateColumnsImpl(const RNTupleDescriptor &desc) final;
2039    void ConstructValue(void *where) const final { new (where) char(0); }
2040 
2041 public:
2042    static std::string TypeName() { return "char"; }
2043    explicit RField(std::string_view name) : RFieldBase(name, TypeName(), ENTupleStructure::kLeaf, true /* isSimple */)
2044    {
2045       fTraits |= kTraitTrivialType;
2046    }
2047    RField(RField&& other) = default;
2048    RField& operator =(RField&& other) = default;
2049    ~RField() override = default;
2050 
2051    char *Map(NTupleSize_t globalIndex) {
2052       return fPrincipalColumn->Map<char>(globalIndex);
2053    }
2054    char *Map(RClusterIndex clusterIndex) { return fPrincipalColumn->Map<char>(clusterIndex); }
2055    char *MapV(NTupleSize_t globalIndex, NTupleSize_t &nItems) {
2056       return fPrincipalColumn->MapV<char>(globalIndex, nItems);
2057    }
2058    char *MapV(RClusterIndex clusterIndex, NTupleSize_t &nItems)
2059    {
2060       return fPrincipalColumn->MapV<char>(clusterIndex, nItems);
2061    }
2062 
2063    size_t GetValueSize() const final { return sizeof(char); }
2064    size_t GetAlignment() const final { return alignof(char); }
2065    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
2066 };
2067 
2068 template <>
2069 class RField<std::int8_t> final : public RFieldBase {
2070 protected:
2071    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
2072    {
2073       return std::make_unique<RField>(newName);
2074    }
2075 
2076    const RColumnRepresentations &GetColumnRepresentations() const final;
2077    void GenerateColumnsImpl() final;
2078    void GenerateColumnsImpl(const RNTupleDescriptor &desc) final;
2079    void ConstructValue(void *where) const final { new (where) int8_t(0); }
2080 
2081 public:
2082    static std::string TypeName() { return "std::int8_t"; }
2083    explicit RField(std::string_view name) : RFieldBase(name, TypeName(), ENTupleStructure::kLeaf, true /* isSimple */)
2084    {
2085       fTraits |= kTraitTrivialType;
2086    }
2087    RField(RField&& other) = default;
2088    RField& operator =(RField&& other) = default;
2089    ~RField() override = default;
2090 
2091    std::int8_t *Map(NTupleSize_t globalIndex) {
2092       return fPrincipalColumn->Map<std::int8_t>(globalIndex);
2093    }
2094    std::int8_t *Map(RClusterIndex clusterIndex) { return fPrincipalColumn->Map<std::int8_t>(clusterIndex); }
2095    std::int8_t *MapV(NTupleSize_t globalIndex, NTupleSize_t &nItems) {
2096       return fPrincipalColumn->MapV<std::int8_t>(globalIndex, nItems);
2097    }
2098    std::int8_t *MapV(RClusterIndex clusterIndex, NTupleSize_t &nItems)
2099    {
2100       return fPrincipalColumn->MapV<std::int8_t>(clusterIndex, nItems);
2101    }
2102 
2103    size_t GetValueSize() const final { return sizeof(std::int8_t); }
2104    size_t GetAlignment() const final { return alignof(std::int8_t); }
2105    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
2106 };
2107 
2108 template <>
2109 class RField<std::uint8_t> final : public RFieldBase {
2110 protected:
2111    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
2112    {
2113       return std::make_unique<RField>(newName);
2114    }
2115 
2116    const RColumnRepresentations &GetColumnRepresentations() const final;
2117    void GenerateColumnsImpl() final;
2118    void GenerateColumnsImpl(const RNTupleDescriptor &desc) final;
2119    void ConstructValue(void *where) const final { new (where) uint8_t(0); }
2120 
2121 public:
2122    static std::string TypeName() { return "std::uint8_t"; }
2123    explicit RField(std::string_view name) : RFieldBase(name, TypeName(), ENTupleStructure::kLeaf, true /* isSimple */)
2124    {
2125       fTraits |= kTraitTrivialType;
2126    }
2127    RField(RField&& other) = default;
2128    RField& operator =(RField&& other) = default;
2129    ~RField() override = default;
2130 
2131    std::uint8_t *Map(NTupleSize_t globalIndex) {
2132       return fPrincipalColumn->Map<std::uint8_t>(globalIndex);
2133    }
2134    std::uint8_t *Map(RClusterIndex clusterIndex) { return fPrincipalColumn->Map<std::uint8_t>(clusterIndex); }
2135    std::uint8_t *MapV(NTupleSize_t globalIndex, NTupleSize_t &nItems) {
2136       return fPrincipalColumn->MapV<std::uint8_t>(globalIndex, nItems);
2137    }
2138    std::uint8_t *MapV(RClusterIndex clusterIndex, NTupleSize_t &nItems)
2139    {
2140       return fPrincipalColumn->MapV<std::uint8_t>(clusterIndex, nItems);
2141    }
2142 
2143    size_t GetValueSize() const final { return sizeof(std::uint8_t); }
2144    size_t GetAlignment() const final { return alignof(std::uint8_t); }
2145    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
2146 };
2147 
2148 template <>
2149 class RField<std::int16_t> final : public RFieldBase {
2150 protected:
2151    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
2152    {
2153       return std::make_unique<RField>(newName);
2154    }
2155 
2156    const RColumnRepresentations &GetColumnRepresentations() const final;
2157    void GenerateColumnsImpl() final;
2158    void GenerateColumnsImpl(const RNTupleDescriptor &desc) final;
2159    void ConstructValue(void *where) const final { new (where) int16_t(0); }
2160 
2161 public:
2162    static std::string TypeName() { return "std::int16_t"; }
2163    explicit RField(std::string_view name) : RFieldBase(name, TypeName(), ENTupleStructure::kLeaf, true /* isSimple */)
2164    {
2165       fTraits |= kTraitTrivialType;
2166    }
2167    RField(RField&& other) = default;
2168    RField& operator =(RField&& other) = default;
2169    ~RField() override = default;
2170 
2171    std::int16_t *Map(NTupleSize_t globalIndex) {
2172       return fPrincipalColumn->Map<std::int16_t>(globalIndex);
2173    }
2174    std::int16_t *Map(RClusterIndex clusterIndex) { return fPrincipalColumn->Map<std::int16_t>(clusterIndex); }
2175    std::int16_t *MapV(NTupleSize_t globalIndex, NTupleSize_t &nItems) {
2176       return fPrincipalColumn->MapV<std::int16_t>(globalIndex, nItems);
2177    }
2178    std::int16_t *MapV(RClusterIndex clusterIndex, NTupleSize_t &nItems)
2179    {
2180       return fPrincipalColumn->MapV<std::int16_t>(clusterIndex, nItems);
2181    }
2182 
2183    size_t GetValueSize() const final { return sizeof(std::int16_t); }
2184    size_t GetAlignment() const final { return alignof(std::int16_t); }
2185    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
2186 };
2187 
2188 template <>
2189 class RField<std::uint16_t> final : public RFieldBase {
2190 protected:
2191    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
2192    {
2193       return std::make_unique<RField>(newName);
2194    }
2195 
2196    const RColumnRepresentations &GetColumnRepresentations() const final;
2197    void GenerateColumnsImpl() final;
2198    void GenerateColumnsImpl(const RNTupleDescriptor &desc) final;
2199    void ConstructValue(void *where) const final { new (where) int16_t(0); }
2200 
2201 public:
2202    static std::string TypeName() { return "std::uint16_t"; }
2203    explicit RField(std::string_view name) : RFieldBase(name, TypeName(), ENTupleStructure::kLeaf, true /* isSimple */)
2204    {
2205       fTraits |= kTraitTrivialType;
2206    }
2207    RField(RField&& other) = default;
2208    RField& operator =(RField&& other) = default;
2209    ~RField() override = default;
2210 
2211    std::uint16_t *Map(NTupleSize_t globalIndex) {
2212       return fPrincipalColumn->Map<std::uint16_t>(globalIndex);
2213    }
2214    std::uint16_t *Map(RClusterIndex clusterIndex) { return fPrincipalColumn->Map<std::uint16_t>(clusterIndex); }
2215    std::uint16_t *MapV(NTupleSize_t globalIndex, NTupleSize_t &nItems) {
2216       return fPrincipalColumn->MapV<std::uint16_t>(globalIndex, nItems);
2217    }
2218    std::uint16_t *MapV(RClusterIndex clusterIndex, NTupleSize_t &nItems)
2219    {
2220       return fPrincipalColumn->MapV<std::uint16_t>(clusterIndex, nItems);
2221    }
2222 
2223    size_t GetValueSize() const final { return sizeof(std::uint16_t); }
2224    size_t GetAlignment() const final { return alignof(std::uint16_t); }
2225    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
2226 };
2227 
2228 template <>
2229 class RField<std::int32_t> final : public RFieldBase {
2230 protected:
2231    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
2232    {
2233       return std::make_unique<RField>(newName);
2234    }
2235 
2236    const RColumnRepresentations &GetColumnRepresentations() const final;
2237    void GenerateColumnsImpl() final;
2238    void GenerateColumnsImpl(const RNTupleDescriptor &desc) final;
2239    void ConstructValue(void *where) const final { new (where) int32_t(0); }
2240 
2241 public:
2242    static std::string TypeName() { return "std::int32_t"; }
2243    explicit RField(std::string_view name) : RFieldBase(name, TypeName(), ENTupleStructure::kLeaf, true /* isSimple */)
2244    {
2245       fTraits |= kTraitTrivialType;
2246    }
2247    RField(RField&& other) = default;
2248    RField& operator =(RField&& other) = default;
2249    ~RField() override = default;
2250 
2251    std::int32_t *Map(NTupleSize_t globalIndex) {
2252       return fPrincipalColumn->Map<std::int32_t>(globalIndex);
2253    }
2254    std::int32_t *Map(RClusterIndex clusterIndex) { return fPrincipalColumn->Map<std::int32_t>(clusterIndex); }
2255    std::int32_t *MapV(NTupleSize_t globalIndex, NTupleSize_t &nItems) {
2256       return fPrincipalColumn->MapV<std::int32_t>(globalIndex, nItems);
2257    }
2258    std::int32_t *MapV(RClusterIndex clusterIndex, NTupleSize_t &nItems)
2259    {
2260       return fPrincipalColumn->MapV<std::int32_t>(clusterIndex, nItems);
2261    }
2262 
2263    size_t GetValueSize() const final { return sizeof(std::int32_t); }
2264    size_t GetAlignment() const final { return alignof(std::int32_t); }
2265    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
2266 };
2267 
2268 template <>
2269 class RField<std::uint32_t> final : public RFieldBase {
2270 protected:
2271    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
2272    {
2273       return std::make_unique<RField>(newName);
2274    }
2275 
2276    const RColumnRepresentations &GetColumnRepresentations() const final;
2277    void GenerateColumnsImpl() final;
2278    void GenerateColumnsImpl(const RNTupleDescriptor &desc) final;
2279    void ConstructValue(void *where) const final { new (where) uint32_t(0); }
2280 
2281 public:
2282    static std::string TypeName() { return "std::uint32_t"; }
2283    explicit RField(std::string_view name) : RFieldBase(name, TypeName(), ENTupleStructure::kLeaf, true /* isSimple */)
2284    {
2285       fTraits |= kTraitTrivialType;
2286    }
2287    RField(RField&& other) = default;
2288    RField& operator =(RField&& other) = default;
2289    ~RField() override = default;
2290 
2291    std::uint32_t *Map(NTupleSize_t globalIndex) {
2292       return fPrincipalColumn->Map<std::uint32_t>(globalIndex);
2293    }
2294    std::uint32_t *Map(const RClusterIndex clusterIndex) {
2295       return fPrincipalColumn->Map<std::uint32_t>(clusterIndex);
2296    }
2297    std::uint32_t *MapV(NTupleSize_t globalIndex, NTupleSize_t &nItems) {
2298       return fPrincipalColumn->MapV<std::uint32_t>(globalIndex, nItems);
2299    }
2300    std::uint32_t *MapV(RClusterIndex clusterIndex, NTupleSize_t &nItems)
2301    {
2302       return fPrincipalColumn->MapV<std::uint32_t>(clusterIndex, nItems);
2303    }
2304 
2305    size_t GetValueSize() const final { return sizeof(std::uint32_t); }
2306    size_t GetAlignment() const final { return alignof(std::uint32_t); }
2307    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
2308 };
2309 
2310 template <>
2311 class RField<std::uint64_t> final : public RFieldBase {
2312 protected:
2313    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
2314    {
2315       return std::make_unique<RField>(newName);
2316    }
2317 
2318    const RColumnRepresentations &GetColumnRepresentations() const final;
2319    void GenerateColumnsImpl() final;
2320    void GenerateColumnsImpl(const RNTupleDescriptor &desc) final;
2321    void ConstructValue(void *where) const final { new (where) uint64_t(0); }
2322 
2323 public:
2324    static std::string TypeName() { return "std::uint64_t"; }
2325    explicit RField(std::string_view name) : RFieldBase(name, TypeName(), ENTupleStructure::kLeaf, true /* isSimple */)
2326    {
2327       fTraits |= kTraitTrivialType;
2328    }
2329    RField(RField&& other) = default;
2330    RField& operator =(RField&& other) = default;
2331    ~RField() override = default;
2332 
2333    std::uint64_t *Map(NTupleSize_t globalIndex) {
2334       return fPrincipalColumn->Map<std::uint64_t>(globalIndex);
2335    }
2336    std::uint64_t *Map(RClusterIndex clusterIndex) { return fPrincipalColumn->Map<std::uint64_t>(clusterIndex); }
2337    std::uint64_t *MapV(NTupleSize_t globalIndex, NTupleSize_t &nItems) {
2338       return fPrincipalColumn->MapV<std::uint64_t>(globalIndex, nItems);
2339    }
2340    std::uint64_t *MapV(RClusterIndex clusterIndex, NTupleSize_t &nItems)
2341    {
2342       return fPrincipalColumn->MapV<std::uint64_t>(clusterIndex, nItems);
2343    }
2344 
2345    size_t GetValueSize() const final { return sizeof(std::uint64_t); }
2346    size_t GetAlignment() const final { return alignof(std::uint64_t); }
2347    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
2348 };
2349 
2350 template <>
2351 class RField<std::int64_t> final : public RFieldBase {
2352 protected:
2353    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
2354    {
2355       return std::make_unique<RField>(newName);
2356    }
2357 
2358    const RColumnRepresentations &GetColumnRepresentations() const final;
2359    void GenerateColumnsImpl() final;
2360    void GenerateColumnsImpl(const RNTupleDescriptor &desc) final;
2361    void ConstructValue(void *where) const final { new (where) int64_t(0); }
2362 
2363 public:
2364    static std::string TypeName() { return "std::int64_t"; }
2365    explicit RField(std::string_view name) : RFieldBase(name, TypeName(), ENTupleStructure::kLeaf, true /* isSimple */)
2366    {
2367       fTraits |= kTraitTrivialType;
2368    }
2369    RField(RField&& other) = default;
2370    RField& operator =(RField&& other) = default;
2371    ~RField() override = default;
2372 
2373    std::int64_t *Map(NTupleSize_t globalIndex) {
2374       return fPrincipalColumn->Map<std::int64_t>(globalIndex);
2375    }
2376    std::int64_t *Map(RClusterIndex clusterIndex) { return fPrincipalColumn->Map<std::int64_t>(clusterIndex); }
2377    std::int64_t *MapV(NTupleSize_t globalIndex, NTupleSize_t &nItems) {
2378       return fPrincipalColumn->MapV<std::int64_t>(globalIndex, nItems);
2379    }
2380    std::int64_t *MapV(RClusterIndex clusterIndex, NTupleSize_t &nItems)
2381    {
2382       return fPrincipalColumn->MapV<std::int64_t>(clusterIndex, nItems);
2383    }
2384 
2385    size_t GetValueSize() const final { return sizeof(std::int64_t); }
2386    size_t GetAlignment() const final { return alignof(std::int64_t); }
2387    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
2388 };
2389 
2390 template <>
2391 class RField<std::string> final : public RFieldBase {
2392 private:
2393    ClusterSize_t fIndex;
2394 
2395    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
2396    {
2397       return std::make_unique<RField>(newName);
2398    }
2399 
2400    const RColumnRepresentations &GetColumnRepresentations() const final;
2401    void GenerateColumnsImpl() final;
2402    void GenerateColumnsImpl(const RNTupleDescriptor &desc) final;
2403 
2404    void ConstructValue(void *where) const final { new (where) std::string(); }
2405    std::unique_ptr<RDeleter> GetDeleter() const final { return std::make_unique<RTypedDeleter<std::string>>(); }
2406 
2407    std::size_t AppendImpl(const void *from) final;
2408    void ReadGlobalImpl(ROOT::Experimental::NTupleSize_t globalIndex, void *to) final;
2409 
2410    void CommitClusterImpl() final { fIndex = 0; }
2411 
2412 public:
2413    static std::string TypeName() { return "std::string"; }
2414    explicit RField(std::string_view name)
2415       : RFieldBase(name, TypeName(), ENTupleStructure::kLeaf, false /* isSimple */), fIndex(0)
2416    {
2417    }
2418    RField(RField&& other) = default;
2419    RField& operator =(RField&& other) = default;
2420    ~RField() override = default;
2421 
2422    size_t GetValueSize() const final { return sizeof(std::string); }
2423    size_t GetAlignment() const final { return std::alignment_of<std::string>(); }
2424    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
2425 };
2426 
2427 template <typename ItemT, std::size_t N>
2428 class RField<std::array<ItemT, N>> : public RArrayField {
2429    using ContainerT = typename std::array<ItemT, N>;
2430 
2431 protected:
2432    void ConstructValue(void *where) const final { new (where) ContainerT(); }
2433 
2434 public:
2435    static std::string TypeName() {
2436       return "std::array<" + RField<ItemT>::TypeName() + "," + std::to_string(N) + ">";
2437    }
2438    explicit RField(std::string_view name) : RArrayField(name, std::make_unique<RField<ItemT>>("_0"), N)
2439    {}
2440    RField(RField&& other) = default;
2441    RField& operator =(RField&& other) = default;
2442    ~RField() override = default;
2443 };
2444 
2445 template <typename ItemT, std::size_t N>
2446 class RField<ItemT[N]> final : public RField<std::array<ItemT, N>> {
2447 public:
2448    explicit RField(std::string_view name) : RField<std::array<ItemT, N>>(name) {}
2449    RField(RField &&other) = default;
2450    RField &operator=(RField &&other) = default;
2451    ~RField() override = default;
2452 };
2453 
2454 template <typename ItemT>
2455 class RField<std::set<ItemT>> : public RSetField {
2456    using ContainerT = typename std::set<ItemT>;
2457 
2458 protected:
2459    void ConstructValue(void *where) const final { new (where) ContainerT(); }
2460    std::unique_ptr<RDeleter> GetDeleter() const final { return std::make_unique<RTypedDeleter<ContainerT>>(); }
2461 
2462 public:
2463    static std::string TypeName() { return "std::set<" + RField<ItemT>::TypeName() + ">"; }
2464 
2465    explicit RField(std::string_view name) : RSetField(name, TypeName(), std::make_unique<RField<ItemT>>("_0")) {}
2466    RField(RField &&other) = default;
2467    RField &operator=(RField &&other) = default;
2468    ~RField() override = default;
2469 
2470    size_t GetValueSize() const final { return sizeof(ContainerT); }
2471    size_t GetAlignment() const final { return std::alignment_of<ContainerT>(); }
2472 };
2473 
2474 template <typename ItemT>
2475 class RField<std::unordered_set<ItemT>> final : public RSetField {
2476    using ContainerT = typename std::unordered_set<ItemT>;
2477 
2478 protected:
2479    void ConstructValue(void *where) const final { new (where) ContainerT(); }
2480    std::unique_ptr<RDeleter> GetDeleter() const final { return std::make_unique<RTypedDeleter<ContainerT>>(); }
2481 
2482 public:
2483    static std::string TypeName() { return "std::unordered_set<" + RField<ItemT>::TypeName() + ">"; }
2484 
2485    explicit RField(std::string_view name) : RSetField(name, TypeName(), std::make_unique<RField<ItemT>>("_0")) {}
2486    RField(RField &&other) = default;
2487    RField &operator=(RField &&other) = default;
2488    ~RField() override = default;
2489 
2490    size_t GetValueSize() const final { return sizeof(ContainerT); }
2491    size_t GetAlignment() const final { return std::alignment_of<ContainerT>(); }
2492 };
2493 
2494 template <typename KeyT, typename ValueT>
2495 class RField<std::map<KeyT, ValueT>> final : public RMapField {
2496    using ContainerT = typename std::map<KeyT, ValueT>;
2497 
2498 protected:
2499    void ConstructValue(void *where) const final { new (where) ContainerT(); }
2500    std::unique_ptr<RDeleter> GetDeleter() const final { return std::make_unique<RTypedDeleter<ContainerT>>(); }
2501 
2502 public:
2503    static std::string TypeName()
2504    {
2505       return "std::map<" + RField<KeyT>::TypeName() + "," + RField<ValueT>::TypeName() + ">";
2506    }
2507 
2508    explicit RField(std::string_view name)
2509       : RMapField(name, TypeName(), std::make_unique<RField<std::pair<KeyT, ValueT>>>("_0"))
2510    {
2511    }
2512    RField(RField &&other) = default;
2513    RField &operator=(RField &&other) = default;
2514    ~RField() override = default;
2515 
2516    size_t GetValueSize() const final { return sizeof(ContainerT); }
2517    size_t GetAlignment() const final { return std::alignment_of<ContainerT>(); }
2518 };
2519 
2520 template <typename KeyT, typename ValueT>
2521 class RField<std::unordered_map<KeyT, ValueT>> final : public RMapField {
2522    using ContainerT = typename std::unordered_map<KeyT, ValueT>;
2523 
2524 protected:
2525    void ConstructValue(void *where) const final { new (where) ContainerT(); }
2526    std::unique_ptr<RDeleter> GetDeleter() const final { return std::make_unique<RTypedDeleter<ContainerT>>(); }
2527 
2528 public:
2529    static std::string TypeName()
2530    {
2531       return "std::unordered_map<" + RField<KeyT>::TypeName() + "," + RField<ValueT>::TypeName() + ">";
2532    }
2533 
2534    explicit RField(std::string_view name)
2535       : RMapField(name, TypeName(), std::make_unique<RField<std::pair<KeyT, ValueT>>>("_0"))
2536    {
2537    }
2538    RField(RField &&other) = default;
2539    RField &operator=(RField &&other) = default;
2540    ~RField() override = default;
2541 
2542    size_t GetValueSize() const final { return sizeof(ContainerT); }
2543    size_t GetAlignment() const final { return std::alignment_of<ContainerT>(); }
2544 };
2545 
2546 template <typename... ItemTs>
2547 class RField<std::variant<ItemTs...>> final : public RVariantField {
2548    using ContainerT = typename std::variant<ItemTs...>;
2549 private:
2550    template <typename HeadT, typename... TailTs>
2551    static std::string BuildItemTypes()
2552    {
2553       std::string result = RField<HeadT>::TypeName();
2554       if constexpr(sizeof...(TailTs) > 0)
2555          result += "," + BuildItemTypes<TailTs...>();
2556       return result;
2557    }
2558 
2559    template <typename HeadT, typename... TailTs>
2560    static std::vector<RFieldBase *> BuildItemFields(unsigned int index = 0)
2561    {
2562       std::vector<RFieldBase *> result;
2563       result.emplace_back(new RField<HeadT>("_" + std::to_string(index)));
2564       if constexpr(sizeof...(TailTs) > 0) {
2565          auto tailFields = BuildItemFields<TailTs...>(index + 1);
2566          result.insert(result.end(), tailFields.begin(), tailFields.end());
2567       }
2568       return result;
2569    }
2570 
2571 protected:
2572    void ConstructValue(void *where) const final { new (where) ContainerT(); }
2573 
2574 public:
2575    static std::string TypeName() { return "std::variant<" + BuildItemTypes<ItemTs...>() + ">"; }
2576    explicit RField(std::string_view name) : RVariantField(name, BuildItemFields<ItemTs...>()) {}
2577    RField(RField&& other) = default;
2578    RField& operator =(RField&& other) = default;
2579    ~RField() override = default;
2580 };
2581 
2582 template <typename ItemT>
2583 class RField<std::vector<ItemT>> final : public RVectorField {
2584    using ContainerT = typename std::vector<ItemT>;
2585 
2586 protected:
2587    void ConstructValue(void *where) const final { new (where) ContainerT(); }
2588 
2589 public:
2590    static std::string TypeName() { return "std::vector<" + RField<ItemT>::TypeName() + ">"; }
2591    explicit RField(std::string_view name)
2592       : RVectorField(name, std::make_unique<RField<ItemT>>("_0"))
2593    {}
2594    RField(RField&& other) = default;
2595    RField& operator =(RField&& other) = default;
2596    ~RField() override = default;
2597 
2598    size_t GetValueSize() const final { return sizeof(ContainerT); }
2599 };
2600 
2601 // std::vector<bool> is a template specialization and needs special treatment
2602 template <>
2603 class RField<std::vector<bool>> final : public RFieldBase {
2604 private:
2605    ClusterSize_t fNWritten{0};
2606 
2607 protected:
2608    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
2609    {
2610       return std::make_unique<RField>(newName);
2611    }
2612 
2613    const RColumnRepresentations &GetColumnRepresentations() const final;
2614    void GenerateColumnsImpl() final;
2615    void GenerateColumnsImpl(const RNTupleDescriptor &desc) final;
2616 
2617    void ConstructValue(void *where) const final { new (where) std::vector<bool>(); }
2618    std::unique_ptr<RDeleter> GetDeleter() const final { return std::make_unique<RTypedDeleter<std::vector<bool>>>(); }
2619 
2620    std::size_t AppendImpl(const void *from) final;
2621    void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final;
2622 
2623    void CommitClusterImpl() final { fNWritten = 0; }
2624 
2625 public:
2626    static std::string TypeName() { return "std::vector<bool>"; }
2627    explicit RField(std::string_view name);
2628    RField(RField&& other) = default;
2629    RField& operator =(RField&& other) = default;
2630    ~RField() override = default;
2631 
2632    std::vector<RValue> SplitValue(const RValue &value) const final;
2633 
2634    size_t GetValueSize() const final { return sizeof(std::vector<bool>); }
2635    size_t GetAlignment() const final { return std::alignment_of<std::vector<bool>>(); }
2636    void AcceptVisitor(Detail::RFieldVisitor &visitor) const final;
2637    void GetCollectionInfo(NTupleSize_t globalIndex, RClusterIndex *collectionStart, ClusterSize_t *size) const {
2638       fPrincipalColumn->GetCollectionInfo(globalIndex, collectionStart, size);
2639    }
2640    void GetCollectionInfo(RClusterIndex clusterIndex, RClusterIndex *collectionStart, ClusterSize_t *size) const
2641    {
2642       fPrincipalColumn->GetCollectionInfo(clusterIndex, collectionStart, size);
2643    }
2644 };
2645 
2646 template <typename ItemT>
2647 class RField<ROOT::VecOps::RVec<ItemT>> final : public RRVecField {
2648    using ContainerT = typename ROOT::VecOps::RVec<ItemT>;
2649 protected:
2650    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
2651    {
2652       auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetFieldName());
2653       return std::make_unique<RField<ROOT::VecOps::RVec<ItemT>>>(newName, std::move(newItemField));
2654    }
2655 
2656    void ConstructValue(void *where) const final { new (where) ContainerT(); }
2657    std::unique_ptr<RDeleter> GetDeleter() const final { return std::make_unique<RTypedDeleter<ContainerT>>(); }
2658 
2659    std::size_t AppendImpl(const void *from) final
2660    {
2661       auto typedValue = static_cast<const ContainerT *>(from);
2662       auto nbytes = 0;
2663       auto count = typedValue->size();
2664       for (unsigned i = 0; i < count; ++i) {
2665          nbytes += CallAppendOn(*fSubFields[0], &typedValue->data()[i]);
2666       }
2667       this->fNWritten += count;
2668       fColumns[0]->Append(&this->fNWritten);
2669       return nbytes + fColumns[0]->GetElement()->GetPackedSize();
2670    }
2671    void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
2672    {
2673       auto typedValue = static_cast<ContainerT *>(to);
2674       ClusterSize_t nItems;
2675       RClusterIndex collectionStart;
2676       fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
2677       typedValue->resize(nItems);
2678       for (unsigned i = 0; i < nItems; ++i) {
2679          CallReadOn(*fSubFields[0], collectionStart + i, &typedValue->data()[i]);
2680       }
2681    }
2682 
2683 public:
2684    RField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
2685       : RRVecField(fieldName, std::move(itemField))
2686    {
2687    }
2688 
2689    explicit RField(std::string_view name)
2690       : RField(name, std::make_unique<RField<ItemT>>("_0"))
2691    {
2692    }
2693    RField(RField&& other) = default;
2694    RField& operator =(RField&& other) = default;
2695    ~RField() override = default;
2696 
2697    static std::string TypeName() { return "ROOT::VecOps::RVec<" + RField<ItemT>::TypeName() + ">"; }
2698 
2699    size_t GetValueSize() const final { return sizeof(ContainerT); }
2700    size_t GetAlignment() const final { return std::alignment_of<ContainerT>(); }
2701 };
2702 
2703 template <typename T1, typename T2>
2704 class RField<std::pair<T1, T2>> final : public RPairField {
2705    using ContainerT = typename std::pair<T1,T2>;
2706 private:
2707    template <typename Ty1, typename Ty2>
2708    static std::array<std::unique_ptr<RFieldBase>, 2> BuildItemFields()
2709    {
2710       return {std::make_unique<RField<Ty1>>("_0"), std::make_unique<RField<Ty2>>("_1")};
2711    }
2712 
2713    static std::array<std::size_t, 2> BuildItemOffsets()
2714    {
2715       auto pair = ContainerT();
2716       auto offsetFirst = reinterpret_cast<std::uintptr_t>(&(pair.first)) - reinterpret_cast<std::uintptr_t>(&pair);
2717       auto offsetSecond = reinterpret_cast<std::uintptr_t>(&(pair.second)) - reinterpret_cast<std::uintptr_t>(&pair);
2718       return {offsetFirst, offsetSecond};
2719    }
2720 
2721 protected:
2722    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
2723    {
2724       std::array<std::unique_ptr<RFieldBase>, 2> items{fSubFields[0]->Clone(fSubFields[0]->GetFieldName()),
2725                                                        fSubFields[1]->Clone(fSubFields[1]->GetFieldName())};
2726       return std::make_unique<RField<std::pair<T1, T2>>>(newName, std::move(items));
2727    }
2728 
2729    void ConstructValue(void *where) const final { new (where) ContainerT(); }
2730    std::unique_ptr<RDeleter> GetDeleter() const final { return std::make_unique<RTypedDeleter<ContainerT>>(); }
2731 
2732 public:
2733    static std::string TypeName() {
2734       return "std::pair<" + RField<T1>::TypeName() + "," + RField<T2>::TypeName() + ">";
2735    }
2736    explicit RField(std::string_view name, std::array<std::unique_ptr<RFieldBase>, 2> &&itemFields)
2737       : RPairField(name, std::move(itemFields), BuildItemOffsets())
2738    {
2739       fMaxAlignment = std::max(alignof(T1), alignof(T2));
2740       fSize = sizeof(ContainerT);
2741    }
2742    explicit RField(std::string_view name) : RField(name, BuildItemFields<T1, T2>()) {}
2743    RField(RField&& other) = default;
2744    RField& operator =(RField&& other) = default;
2745    ~RField() override = default;
2746 };
2747 
2748 template <typename... ItemTs>
2749 class RField<std::tuple<ItemTs...>> final : public RTupleField {
2750    using ContainerT = typename std::tuple<ItemTs...>;
2751 private:
2752    template <typename HeadT, typename... TailTs>
2753    static std::string BuildItemTypes()
2754    {
2755       std::string result = RField<HeadT>::TypeName();
2756       if constexpr (sizeof...(TailTs) > 0)
2757          result += "," + BuildItemTypes<TailTs...>();
2758       return result;
2759    }
2760 
2761    template <typename HeadT, typename... TailTs>
2762    static void _BuildItemFields(std::vector<std::unique_ptr<RFieldBase>> &itemFields, unsigned int index = 0)
2763    {
2764       itemFields.emplace_back(new RField<HeadT>("_" + std::to_string(index)));
2765       if constexpr (sizeof...(TailTs) > 0)
2766          _BuildItemFields<TailTs...>(itemFields, index + 1);
2767    }
2768    template <typename... Ts>
2769    static std::vector<std::unique_ptr<RFieldBase>> BuildItemFields()
2770    {
2771       std::vector<std::unique_ptr<RFieldBase>> result;
2772       _BuildItemFields<Ts...>(result);
2773       return result;
2774    }
2775 
2776    template <unsigned Index, typename HeadT, typename... TailTs>
2777    static void _BuildItemOffsets(std::vector<std::size_t> &offsets, const ContainerT &tuple)
2778    {
2779       auto offset =
2780          reinterpret_cast<std::uintptr_t>(&std::get<Index>(tuple)) - reinterpret_cast<std::uintptr_t>(&tuple);
2781       offsets.emplace_back(offset);
2782       if constexpr (sizeof...(TailTs) > 0)
2783          _BuildItemOffsets<Index + 1, TailTs...>(offsets, tuple);
2784    }
2785    template <typename... Ts>
2786    static std::vector<std::size_t> BuildItemOffsets()
2787    {
2788       std::vector<std::size_t> result;
2789       _BuildItemOffsets<0, Ts...>(result, ContainerT());
2790       return result;
2791    }
2792 
2793 protected:
2794    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
2795    {
2796       std::vector<std::unique_ptr<RFieldBase>> items;
2797       for (auto &item : fSubFields)
2798          items.push_back(item->Clone(item->GetFieldName()));
2799       return std::make_unique<RField<std::tuple<ItemTs...>>>(newName, std::move(items));
2800    }
2801 
2802    void ConstructValue(void *where) const final { new (where) ContainerT(); }
2803    std::unique_ptr<RDeleter> GetDeleter() const final { return std::make_unique<RTypedDeleter<ContainerT>>(); }
2804 
2805 public:
2806    static std::string TypeName() { return "std::tuple<" + BuildItemTypes<ItemTs...>() + ">"; }
2807    explicit RField(std::string_view name, std::vector<std::unique_ptr<RFieldBase>> &&itemFields)
2808       : RTupleField(name, std::move(itemFields), BuildItemOffsets<ItemTs...>())
2809    {
2810       fMaxAlignment = std::max({alignof(ItemTs)...});
2811       fSize = sizeof(ContainerT);
2812    }
2813    explicit RField(std::string_view name) : RField(name, BuildItemFields<ItemTs...>()) {}
2814    RField(RField &&other) = default;
2815    RField &operator=(RField &&other) = default;
2816    ~RField() override = default;
2817 };
2818 
2819 template <std::size_t N>
2820 class RField<std::bitset<N>> final : public RBitsetField {
2821 public:
2822    static std::string TypeName() { return "std::bitset<" + std::to_string(N) + ">"; }
2823    explicit RField(std::string_view name) : RBitsetField(name, N) {}
2824    RField(RField &&other) = default;
2825    RField &operator=(RField &&other) = default;
2826    ~RField() override = default;
2827 };
2828 
2829 template <typename ItemT>
2830 class RField<std::unique_ptr<ItemT>> final : public RUniquePtrField {
2831 public:
2832    static std::string TypeName() { return "std::unique_ptr<" + RField<ItemT>::TypeName() + ">"; }
2833    explicit RField(std::string_view name) : RUniquePtrField(name, TypeName(), std::make_unique<RField<ItemT>>("_0")) {}
2834    RField(RField &&other) = default;
2835    RField &operator=(RField &&other) = default;
2836    ~RField() override = default;
2837 };
2838 
2839 template <typename ItemT>
2840 class RField<std::atomic<ItemT>> final : public RAtomicField {
2841 public:
2842    static std::string TypeName() { return "std::atomic<" + RField<ItemT>::TypeName() + ">"; }
2843    explicit RField(std::string_view name) : RAtomicField(name, TypeName(), std::make_unique<RField<ItemT>>("_0")) {}
2844    RField(RField &&other) = default;
2845    RField &operator=(RField &&other) = default;
2846    ~RField() override = default;
2847 };
2848 
2849 // Has to be implemented after the definition of all RField<T> types
2850 // The void type is specialized in RField.cxx
2851 
2852 template <typename T>
2853 std::unique_ptr<T, typename RFieldBase::RCreateObjectDeleter<T>::deleter> RFieldBase::CreateObject() const
2854 {
2855    if (GetTypeName() != RField<T>::TypeName()) {
2856       throw RException(
2857          R__FAIL("type mismatch for field " + GetFieldName() + ": " + GetTypeName() + " vs. " + RField<T>::TypeName()));
2858    }
2859    return std::unique_ptr<T>(static_cast<T *>(CreateObjectRawPtr()));
2860 }
2861 
2862 template <>
2863 struct RFieldBase::RCreateObjectDeleter<void> {
2864    using deleter = RCreateObjectDeleter<void>;
2865    void operator()(void *);
2866 };
2867 
2868 template <>
2869 std::unique_ptr<void, typename RFieldBase::RCreateObjectDeleter<void>::deleter>
2870 ROOT::Experimental::RFieldBase::CreateObject<void>() const;
2871 
2872 } // namespace Experimental
2873 } // namespace ROOT
2874 
2875 #endif