Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-10-04 09:23:55

0001 /// \file ROOT/RNTupleWriter.hxx
0002 /// \ingroup NTuple
0003 /// \author Jakob Blomer <jblomer@cern.ch>
0004 /// \date 2024-02-20
0005 
0006 /*************************************************************************
0007  * Copyright (C) 1995-2024, Rene Brun and Fons Rademakers.               *
0008  * All rights reserved.                                                  *
0009  *                                                                       *
0010  * For the licensing terms see $ROOTSYS/LICENSE.                         *
0011  * For the list of contributors see $ROOTSYS/README/CREDITS.             *
0012  *************************************************************************/
0013 
0014 #ifndef ROOT_RNTupleWriter
0015 #define ROOT_RNTupleWriter
0016 
0017 #include <ROOT/RConfig.hxx> // for R__unlikely
0018 #include <ROOT/REntry.hxx>
0019 #include <ROOT/RError.hxx>
0020 #include <ROOT/RNTupleFillContext.hxx>
0021 #include <ROOT/RNTupleFillStatus.hxx>
0022 #include <ROOT/RNTupleMetrics.hxx>
0023 #include <ROOT/RNTupleModel.hxx>
0024 #include <ROOT/RNTupleTypes.hxx>
0025 #include <ROOT/RPageStorage.hxx>
0026 #include <ROOT/RRawPtrWriteEntry.hxx>
0027 
0028 #include <cstddef>
0029 #include <cstdint>
0030 #include <memory>
0031 #include <string_view>
0032 #include <utility>
0033 
0034 class TDirectory;
0035 
0036 namespace ROOT {
0037 
0038 class RNTupleWriteOptions;
0039 
0040 namespace Experimental {
0041 class RNTupleAttrSetWriterHandle;
0042 class RFile;
0043 
0044 /// Creates an RNTupleWriter that writes into the given `file`, appending to it. The RNTuple is written under the
0045 /// path `ntuplePath`.
0046 /// `ntuplePath` may have the form `"path/to/ntuple"`, in which case the ntuple's name will be `"ntuple"` and it will
0047 /// be stored under the given `ntuplePath` in the RFile.
0048 /// Throws an exception if the model is null.
0049 /// NOTE: this is a temporary, experimental API that will be replaced by an overload of RNTupleWriter::Append in the
0050 /// future.
0051 std::unique_ptr<RNTupleWriter>
0052 RNTupleWriter_Append(std::unique_ptr<ROOT::RNTupleModel> model, std::string_view ntuplePath,
0053                      ROOT::Experimental::RFile &file,
0054                      const ROOT::RNTupleWriteOptions &options = ROOT::RNTupleWriteOptions());
0055 } // namespace Experimental
0056 
0057 namespace Internal {
0058 // Non-public factory method for an RNTuple writer that uses an already constructed page sink
0059 std::unique_ptr<RNTupleWriter>
0060 CreateRNTupleWriter(std::unique_ptr<ROOT::RNTupleModel> model, std::unique_ptr<Internal::RPageSink> sink);
0061 } // namespace Internal
0062 
0063 // clang-format off
0064 /**
0065 \class ROOT::RNTupleWriter
0066 \ingroup NTuple
0067 \brief An RNTuple that gets filled with entries (data) and writes them to storage
0068 
0069 RNTupleWriter is an interface for writing RNTuples to storage. It can be instantiated using the static functions
0070 Append() and Recreate(), providing an RNTupleModel that defines the schema of the data to be written.
0071 
0072 An RNTuple can be thought of as a table, whose columns are defined by its schema (i.e. by its associated RNTupleModel,
0073 whose Fields map to 0 or more columns).
0074 Writing into an RNTuple happens by filling *entries* into the RNTupleWriter, which make up the rows of the table.
0075 The simplest way to do so is by:
0076 
0077 - retrieving a (shared) pointer to each Field's value;
0078 - writing a value into each pointer;
0079 - calling `writer->Fill()` to commit the entry with all the current pointer values.
0080 
0081 ~~~ {.cpp}
0082 #include <ROOT/RNTupleWriter.hxx>
0083 
0084 /// 1. Create the model.
0085 auto model = ROOT::RNTupleModel::Create();
0086 // Define the schema by adding Fields to the model.
0087 // MakeField returns a shared_ptr to the value to be written (in this case, a shared_ptr<int>)
0088 auto pFoo = model->MakeField<int>("foo");
0089 
0090 /// 2. Create writer from the model.
0091 auto writer = ROOT::RNTupleWriter::Recreate(std::move(model), "myNTuple", "some/file.root");
0092 
0093 /// 3. Write into it.
0094 for (int i = 0; i < 10; ++i) {
0095    // Assign the value you want to each RNTuple Field (in this case there is only one Field "foo").
0096    *pFoo = i;
0097 
0098    // Fill() writes the entire entry to the RNTuple.
0099    // After calling Fill() you can safely write another value into `pFoo` knowing that the previous one was
0100    // already saved.
0101    writer->Fill();
0102 }
0103 
0104 // On destruction, the writer will flush the written data to disk.
0105 ~~~
0106 
0107 The caller has to make sure that the data that gets filled into an RNTuple is not modified for the time of the
0108 Fill() call. The Fill call serializes the C++ object into the column format and
0109 writes data into the corresponding column page buffers.
0110 
0111 The actual writing of the buffers to storage is deferred and can be triggered by FlushCluster() or by
0112 destructing the writer.
0113 
0114 On I/O errors, a ROOT::RException is thrown.
0115 
0116 */
0117 // clang-format on
0118 class RNTupleWriter {
0119    friend ROOT::RNTupleModel::RUpdater;
0120    friend std::unique_ptr<RNTupleWriter>
0121       Internal::CreateRNTupleWriter(std::unique_ptr<ROOT::RNTupleModel>, std::unique_ptr<Internal::RPageSink>);
0122    friend std::unique_ptr<RNTupleWriter>
0123    Experimental::RNTupleWriter_Append(std::unique_ptr<ROOT::RNTupleModel> model, std::string_view ntuplePath,
0124                                       ROOT::Experimental::RFile &file, const ROOT::RNTupleWriteOptions &options);
0125 
0126 private:
0127    RNTupleFillContext fFillContext;
0128    Experimental::Detail::RNTupleMetrics fMetrics;
0129    /// All the Attribute Sets created from this Writer.
0130    std::vector<std::shared_ptr<Experimental::RNTupleAttrSetWriter>> fAttributeSets;
0131 
0132    ROOT::NTupleSize_t fLastCommittedClusterGroup = 0;
0133 
0134    RNTupleWriter(std::unique_ptr<ROOT::RNTupleModel> model, std::unique_ptr<Internal::RPageSink> sink);
0135 
0136    ROOT::RNTupleModel &GetUpdatableModel();
0137    Internal::RPageSink &GetSink() { return *fFillContext.fSink; }
0138 
0139    // Helper function that is called from CommitCluster() when necessary
0140    void CommitClusterGroup();
0141 
0142    void CloseAttributeSetImpl(ROOT::Experimental::RNTupleAttrSetWriter &attrSet);
0143 
0144    /// Create a writer, potentially wrapping the sink in a RPageSinkBuf.
0145    static std::unique_ptr<RNTupleWriter> Create(std::unique_ptr<ROOT::RNTupleModel> model,
0146                                                 std::unique_ptr<Internal::RPageSink> sink,
0147                                                 const ROOT::RNTupleWriteOptions &options);
0148 
0149 public:
0150    /// Creates an RNTupleWriter backed by `storage`, overwriting it if one with the same URI exists.
0151    /// The format of the backing storage is determined by `storage`: in the simplest case it will be a local file, but
0152    /// a different backend may be selected via the URI prefix.
0153    ///
0154    /// The RNTupleWriter will create an RNTuple with the schema determined by `model` (which must not be null) and
0155    /// with name `ntupleName`. This same name can later be used to read back the RNTuple via RNTupleReader.
0156    ///
0157    /// \param model The RNTupleModel describing the schema of the RNTuple written by this writer
0158    /// \param ntupleName The name of the RNTuple to be written
0159    /// \param storage The URI where the RNTuple will be stored (usually just a file name or path)
0160    /// \param options May be passed to customize the behavior of the RNTupleWriter (see also RNTupleWriteOptions).
0161    ///
0162    /// Throws a ROOT::RException if the model is null.
0163    static std::unique_ptr<RNTupleWriter>
0164    Recreate(std::unique_ptr<ROOT::RNTupleModel> model, std::string_view ntupleName, std::string_view storage,
0165             const ROOT::RNTupleWriteOptions &options = ROOT::RNTupleWriteOptions());
0166 
0167    /// Convenience function allowing to call Recreate() with an inline-defined model.
0168    static std::unique_ptr<RNTupleWriter>
0169    Recreate(std::initializer_list<std::pair<std::string_view, std::string_view>> fields, std::string_view ntupleName,
0170             std::string_view storage, const ROOT::RNTupleWriteOptions &options = ROOT::RNTupleWriteOptions());
0171 
0172    /// Creates an RNTupleWriter that writes into an existing TFile or TDirectory, without overwriting its content.
0173    /// `fileOrDirectory` may be an empty TFile and its reference **must remain valid** for the lifetime of the
0174    /// RNTupleWriter (which means the TDirectory object must not be moved, destroyed or replaced during that time).
0175    /// \see Recreate()
0176    static std::unique_ptr<RNTupleWriter> Append(std::unique_ptr<ROOT::RNTupleModel> model, std::string_view ntupleName,
0177                                                 TDirectory &fileOrDirectory,
0178                                                 const ROOT::RNTupleWriteOptions &options = ROOT::RNTupleWriteOptions());
0179 
0180    RNTupleWriter(const RNTupleWriter &) = delete;
0181    RNTupleWriter &operator=(const RNTupleWriter &) = delete;
0182    RNTupleWriter(RNTupleWriter &&) = delete;
0183    RNTupleWriter &operator=(RNTupleWriter &&) = delete;
0184    ~RNTupleWriter();
0185 
0186    /// The simplest user interface if the default entry that comes with the ntuple model is used.
0187    /// \return The number of uncompressed bytes written.
0188    std::size_t Fill() { return fFillContext.Fill(fFillContext.fModel->GetDefaultEntry()); }
0189    /// Multiple entries can have been instantiated from the ntuple model.  This method will check the entry's model ID
0190    /// to ensure it comes from the writer's own model or throw an exception otherwise.
0191    /// \return The number of uncompressed bytes written.
0192    std::size_t Fill(ROOT::REntry &entry) { return fFillContext.Fill(entry); }
0193    /// Fill an entry into this ntuple, but don't commit the cluster. The calling code must pass an RNTupleFillStatus
0194    /// and check RNTupleFillStatus::ShouldFlushCluster.
0195    void FillNoFlush(ROOT::REntry &entry, RNTupleFillStatus &status) { fFillContext.FillNoFlush(entry, status); }
0196 
0197    /// Fill an RRawPtrWriteEntry into this ntuple.  This method will check the entry's model ID to ensure it comes from
0198    /// the writer's own model or throw an exception otherwise.
0199    /// \return The number of uncompressed bytes written.
0200    std::size_t Fill(ROOT::Detail::RRawPtrWriteEntry &entry) { return fFillContext.Fill(entry); }
0201    /// Fill an RRawPtrWriteEntry into this ntuple, but don't commit the cluster. The calling code must pass an
0202    /// RNTupleFillStatus and check RNTupleFillStatus::ShouldFlushCluster.
0203    void FillNoFlush(ROOT::Detail::RRawPtrWriteEntry &entry, RNTupleFillStatus &status)
0204    {
0205       fFillContext.FillNoFlush(entry, status);
0206    }
0207 
0208    /// Flush column data, preparing for CommitCluster or to reduce memory usage. This will trigger compression of pages,
0209    /// but not actually write to storage (unless buffered writing is turned off).
0210    void FlushColumns() { fFillContext.FlushColumns(); }
0211    /// Flush so far filled entries to storage
0212    void FlushCluster() { fFillContext.FlushCluster(); }
0213    /// Ensure that the data from the so far seen Fill calls has been written to storage
0214    void CommitCluster(bool commitClusterGroup = false)
0215    {
0216       fFillContext.FlushCluster();
0217       if (commitClusterGroup)
0218          CommitClusterGroup();
0219    }
0220    /// Closes the underlying file (page sink) and expires the model. Automatically called on destruct.
0221    /// Once the dataset is committed, calls to Fill(), [Commit|Flush]Cluster(), FlushColumns(), CreateEntry(),
0222    /// and model updating fail.
0223    void CommitDataset();
0224 
0225    std::unique_ptr<ROOT::REntry> CreateEntry() const { return fFillContext.CreateEntry(); }
0226    std::unique_ptr<ROOT::Detail::RRawPtrWriteEntry> CreateRawPtrWriteEntry() const
0227    {
0228       return fFillContext.CreateRawPtrWriteEntry();
0229    }
0230 
0231    /// Return the entry number that was last flushed in a cluster.
0232    ROOT::NTupleSize_t GetLastFlushed() const { return fFillContext.GetLastFlushed(); }
0233    /// Return the entry number that was last committed in a cluster.
0234    ROOT::NTupleSize_t GetLastCommitted() const { return fFillContext.GetLastFlushed(); }
0235    /// Return the entry number that was last committed in a cluster group.
0236    ROOT::NTupleSize_t GetLastCommittedClusterGroup() const { return fLastCommittedClusterGroup; }
0237    /// Return the number of entries filled so far.
0238    ROOT::NTupleSize_t GetNEntries() const { return fFillContext.GetNEntries(); }
0239 
0240    void EnableMetrics() { fMetrics.Enable(); }
0241    const Experimental::Detail::RNTupleMetrics &GetMetrics() const { return fMetrics; }
0242 
0243    const ROOT::RNTupleModel &GetModel() const { return *fFillContext.fModel; }
0244 
0245    /// Get a RNTupleModel::RUpdater that provides limited support for incremental updates to the underlying
0246    /// model, e.g. addition of new fields.
0247    ///
0248    /// Note that a Model may not be extended with Streamer fields.
0249    ///
0250    /// **Example: add a new field after the model has been used to construct a `RNTupleWriter` object**
0251    /// ~~~ {.cpp}
0252    /// #include <ROOT/RNTuple.hxx>
0253    ///
0254    /// auto model = ROOT::RNTupleModel::Create();
0255    /// auto fldFloat = model->MakeField<float>("fldFloat");
0256    /// auto writer = ROOT::RNTupleWriter::Recreate(std::move(model), "myNTuple", "some/file.root");
0257    /// auto updater = writer->CreateModelUpdater();
0258    /// updater->BeginUpdate();
0259    /// updater->AddField(std::make_unique<RField<float>>("pt"));
0260    /// updater->CommitUpdate();
0261    ///
0262    /// // ...
0263    /// ~~~
0264    std::unique_ptr<ROOT::RNTupleModel::RUpdater> CreateModelUpdater()
0265    {
0266       return std::make_unique<ROOT::RNTupleModel::RUpdater>(*this);
0267    }
0268 
0269    /// Creates a new Attribute Set called `name` associated to this Writer and returns a non-owning pointer to it.
0270    /// The lifetime of the Attribute Set ends at the same time as the Writer's.
0271    /// If `options` are passed, they will be used for the attribute set writer; otherwise, they will be derived from
0272    /// the main writer.
0273    /// \warning Currently this is only supported for writers created via RNTupleWriter::Append(). This limitation
0274    /// will be lifted in the future.
0275    ROOT::Experimental::RNTupleAttrSetWriterHandle
0276    CreateAttributeSet(std::unique_ptr<RNTupleModel> model, std::string_view name,
0277                       const ROOT::RNTupleWriteOptions *options = nullptr);
0278 
0279    /// Writes the given AttributeSet to the underlying storage and closes it. This method is only useful if you
0280    /// want to close the AttributeSet early: otherwise it will automatically closed when the RNTupleWriter gets
0281    /// destroyed.
0282    void CloseAttributeSet(ROOT::Experimental::RNTupleAttrSetWriterHandle handle);
0283 }; // class RNTupleWriter
0284 
0285 } // namespace ROOT
0286 
0287 #endif // ROOT_RNTupleWriter