The Gaudi Framework  v41r0 (bdbe7744)
Loading...
Searching...
No Matches
Gaudi::NTuple::DiskBuffer::Writer Class Reference

#include </builds/gaudi/Gaudi/GaudiUtils/include/Gaudi/NTuple/DiskBuffer.h>

Collaboration diagram for Gaudi::NTuple::DiskBuffer::Writer:

Public Member Functions

 Writer (std::filesystem::path path, std::string_view header, int zstdLevel=0, std::size_t blockSize=64 *1024)
 Create (truncating) path and write the preamble and header.
 ~Writer ()
 Writer (const Writer &)=delete
Writer & operator= (const Writer &)=delete
void beginRecord ()
void append (const void *data, std::size_t bytes)
void endRecord ()
void close ()
 Flush and close; throws on failure. Safe to call twice.
std::uint64_t entries () const
 Records written to the file; records lost to a failed flush do not count.
std::uint64_t bytes () const
 Payload bytes of those records.
std::uint64_t storedBytes () const
 Bytes written to the file after the header.
int zstdLevel () const
std::size_t blockSize () const
const std::filesystem::path & path () const

Private Member Functions

void flush ()
void write (const void *data, std::size_t bytes)

Private Attributes

std::filesystem::path m_path
FileHandle m_file
int m_level
std::size_t m_blockSize
std::vector< char > m_buf
std::size_t m_recordStart { 0 }
bool m_inRecord { false }
std::uint64_t m_entries { 0 }
std::uint64_t m_bytes { 0 }
std::uint64_t m_stored { 0 }
std::uint64_t m_pending { 0 }
std::uint64_t m_pendingBytes { 0 }

Detailed Description

Definition at line 61 of file DiskBuffer.h.

Constructor & Destructor Documentation

◆ Writer() [1/2]

Writer::Writer ( std::filesystem::path path,
std::string_view header,
int zstdLevel = 0,
std::size_t blockSize = 64 * 1024 )

Create (truncating) path and write the preamble and header.

zstdLevel 0 stores the blocks as they are; anything else needs zstdAvailable(). A block is written once blockSize bytes of records have accumulated, so that is the memory held per open file and what zstd sees at a time.

Definition at line 63 of file NTupleDiskBuffer.cpp.

64 : m_path( std::move( path ) ), m_level( zstdLevel ), m_blockSize( blockSize ) {
65 if ( header.size() > MAX_HEADER ) throw Error( m_path, "header too large" );
66 if ( m_level != 0 && !zstdAvailable() ) throw Error( m_path, "zstd compression is not available in this build" );
67 if ( m_blockSize == 0 || m_blockSize > MAX_BLOCK ) throw Error( m_path, "invalid block size" );
68 m_file.reset( std::fopen( m_path.c_str(), "wb" ) );
69 if ( !m_file ) throw Error( m_path, "cannot create buffer file", errno );
70 std::setvbuf( m_file.get(), nullptr, _IONBF, 0 ); // m_buf is the buffer
71 std::vector<char> preamble( MAGIC, MAGIC + sizeof( MAGIC ) );
72 char words[4 * sizeof( std::uint32_t )];
73 put32( words, BYTE_ORDER_MARK );
74 put32( words + 4, VERSION );
75 put32( words + 8, m_level ? CODEC_ZSTD : CODEC_NONE );
76 put32( words + 12, static_cast<std::uint32_t>( header.size() ) );
77 preamble.insert( preamble.end(), words, words + sizeof( words ) );
78 preamble.insert( preamble.end(), header.begin(), header.end() );
79 write( preamble.data(), preamble.size() );
80 m_stored = 0; // count from here
81 // m_buf grows with the data: reserving m_blockSize up front costs a full block per tuple whether it fills or not
82}
void write(const void *data, std::size_t bytes)
const std::filesystem::path & path() const
Definition DiskBuffer.h:87
std::filesystem::path m_path
Definition DiskBuffer.h:93
std::size_t blockSize() const
Definition DiskBuffer.h:86
bool zstdAvailable()
Whether this build can write and read compressed blocks.
GAUDI_API std::string header(const int ID=Default)
get the recommended header by enum

◆ ~Writer()

Writer::~Writer ( )
default

◆ Writer() [2/2]

Gaudi::NTuple::DiskBuffer::Writer::Writer ( const Writer & )
delete

Member Function Documentation

◆ append()

void Writer::append ( const void * data,
std::size_t bytes )

Definition at line 93 of file NTupleDiskBuffer.cpp.

93 {
94 if ( !m_inRecord ) throw Error( m_path, "append called outside a record" );
95 if ( bytes == 0 ) return;
96 if ( m_buf.size() - m_recordStart - sizeof( std::uint32_t ) + bytes > MAX_RECORD )
97 throw Error( m_path, "record too large" );
98 const char* src = static_cast<const char*>( data );
99 m_buf.insert( m_buf.end(), src, src + bytes );
100}
std::uint64_t bytes() const
Payload bytes of those records.
Definition DiskBuffer.h:82

◆ beginRecord()

void Writer::beginRecord ( )

Definition at line 86 of file NTupleDiskBuffer.cpp.

86 {
87 if ( m_inRecord ) throw Error( m_path, "beginRecord called inside a record" );
88 m_recordStart = m_buf.size();
89 m_buf.resize( m_buf.size() + sizeof( std::uint32_t ) ); // length, patched by endRecord
90 m_inRecord = true;
91}

◆ blockSize()

std::size_t Gaudi::NTuple::DiskBuffer::Writer::blockSize ( ) const
inline

Definition at line 86 of file DiskBuffer.h.

86{ return m_blockSize; }

◆ bytes()

std::uint64_t Gaudi::NTuple::DiskBuffer::Writer::bytes ( ) const
inline

Payload bytes of those records.

Definition at line 82 of file DiskBuffer.h.

82{ return m_bytes; }

◆ close()

void Writer::close ( )

Flush and close; throws on failure. Safe to call twice.

Definition at line 162 of file NTupleDiskBuffer.cpp.

162 {
163 if ( !m_file ) return;
164 if ( m_inRecord ) throw Error( m_path, "close called inside a record" );
165 flush();
166 bool ok = std::fflush( m_file.get() ) == 0;
167 int err = ok ? 0 : errno;
168 // the block is the buffer, so a deferred write error (quota, NFS) only shows up here
169 if ( std::fclose( m_file.release() ) != 0 && ok ) {
170 ok = false;
171 err = errno;
172 }
173 if ( !ok ) throw Error( m_path, "close failed", err );
174}

◆ endRecord()

void Writer::endRecord ( )

Definition at line 102 of file NTupleDiskBuffer.cpp.

102 {
103 if ( !m_inRecord ) throw Error( m_path, "endRecord called outside a record" );
104 const std::size_t payload = m_buf.size() - m_recordStart - sizeof( std::uint32_t );
105 put32( m_buf.data() + m_recordStart, static_cast<std::uint32_t>( payload ) );
106 m_inRecord = false;
107 ++m_pending;
108 m_pendingBytes += payload;
109 if ( m_buf.size() >= m_blockSize ) flush();
110}

◆ entries()

std::uint64_t Gaudi::NTuple::DiskBuffer::Writer::entries ( ) const
inline

Records written to the file; records lost to a failed flush do not count.

Definition at line 80 of file DiskBuffer.h.

80{ return m_entries; }

◆ flush()

void Writer::flush ( )
private

Definition at line 118 of file NTupleDiskBuffer.cpp.

118 {
119 if ( !m_file ) throw Error( m_path, "buffer file is closed" );
120 if ( m_buf.empty() ) return;
121 // dropped on failure too: the data is lost either way, do not let the buffer grow
122 auto drop = [this] {
123 m_buf.clear();
125 // a single huge record should not pin its memory for the rest of the job
126 if ( m_buf.capacity() > 4 * m_blockSize ) m_buf.shrink_to_fit();
127 };
128 const long start = std::ftell( m_file.get() );
129 const std::uint64_t storedStart = m_stored;
130 try {
131 const char* data = m_buf.data();
132 std::size_t stored = m_buf.size();
133 std::vector<char> out;
134#ifdef GAUDI_USE_ZSTD
135 if ( m_level != 0 ) {
136 out.resize( ZSTD_compressBound( m_buf.size() ) );
137 stored = ZSTD_compress( out.data(), out.size(), m_buf.data(), m_buf.size(), m_level );
138 if ( ZSTD_isError( stored ) )
139 throw Error( m_path, std::string( "zstd compression failed: " ) + ZSTD_getErrorName( stored ) );
140 data = out.data();
141 }
142#endif
143 char hdr[BLOCK_HEADER];
144 put32( hdr, static_cast<std::uint32_t>( stored ) );
145 put32( hdr + 4, static_cast<std::uint32_t>( m_buf.size() ) );
146 write( hdr, sizeof( hdr ) );
147 write( data, stored );
148 } catch ( ... ) {
149 // cut the half-written block off, so the blocks before it stay readable
150 if ( start >= 0 && ::ftruncate( ::fileno( m_file.get() ), start ) == 0 ) {
151 std::fseek( m_file.get(), 0, SEEK_END );
152 m_stored = storedStart;
153 }
154 drop();
155 throw;
156 }
159 drop();
160}

◆ operator=()

Writer & Gaudi::NTuple::DiskBuffer::Writer::operator= ( const Writer & )
delete

◆ path()

const std::filesystem::path & Gaudi::NTuple::DiskBuffer::Writer::path ( ) const
inline

Definition at line 87 of file DiskBuffer.h.

87{ return m_path; }

◆ storedBytes()

std::uint64_t Gaudi::NTuple::DiskBuffer::Writer::storedBytes ( ) const
inline

Bytes written to the file after the header.

Definition at line 84 of file DiskBuffer.h.

84{ return m_stored; }

◆ write()

void Writer::write ( const void * data,
std::size_t bytes )
private

Definition at line 112 of file NTupleDiskBuffer.cpp.

112 {
113 if ( std::fwrite( data, 1, bytes, m_file.get() ) != bytes ) throw Error( m_path, "write failed", errno );
114 m_stored += bytes;
115}

◆ zstdLevel()

int Gaudi::NTuple::DiskBuffer::Writer::zstdLevel ( ) const
inline

Definition at line 85 of file DiskBuffer.h.

85{ return m_level; }

Member Data Documentation

◆ m_blockSize

std::size_t Gaudi::NTuple::DiskBuffer::Writer::m_blockSize
private

Definition at line 96 of file DiskBuffer.h.

◆ m_buf

std::vector<char> Gaudi::NTuple::DiskBuffer::Writer::m_buf
private

Definition at line 97 of file DiskBuffer.h.

◆ m_bytes

std::uint64_t Gaudi::NTuple::DiskBuffer::Writer::m_bytes { 0 }
private

Definition at line 101 of file DiskBuffer.h.

101{ 0 };

◆ m_entries

std::uint64_t Gaudi::NTuple::DiskBuffer::Writer::m_entries { 0 }
private

Definition at line 100 of file DiskBuffer.h.

100{ 0 };

◆ m_file

FileHandle Gaudi::NTuple::DiskBuffer::Writer::m_file
private

Definition at line 94 of file DiskBuffer.h.

◆ m_inRecord

bool Gaudi::NTuple::DiskBuffer::Writer::m_inRecord { false }
private

Definition at line 99 of file DiskBuffer.h.

99{ false };

◆ m_level

int Gaudi::NTuple::DiskBuffer::Writer::m_level
private

Definition at line 95 of file DiskBuffer.h.

◆ m_path

std::filesystem::path Gaudi::NTuple::DiskBuffer::Writer::m_path
private

Definition at line 93 of file DiskBuffer.h.

◆ m_pending

std::uint64_t Gaudi::NTuple::DiskBuffer::Writer::m_pending { 0 }
private

Definition at line 103 of file DiskBuffer.h.

103{ 0 }; // records in m_buf, counted once their block is written

◆ m_pendingBytes

std::uint64_t Gaudi::NTuple::DiskBuffer::Writer::m_pendingBytes { 0 }
private

Definition at line 104 of file DiskBuffer.h.

104{ 0 }; // and their payload

◆ m_recordStart

std::size_t Gaudi::NTuple::DiskBuffer::Writer::m_recordStart { 0 }
private

Definition at line 98 of file DiskBuffer.h.

98{ 0 }; // offset of the current record's length field

◆ m_stored

std::uint64_t Gaudi::NTuple::DiskBuffer::Writer::m_stored { 0 }
private

Definition at line 102 of file DiskBuffer.h.

102{ 0 };

The documentation for this class was generated from the following files: