XRootD
Loading...
Searching...
No Matches
XrdHttpTpcStream.hh
Go to the documentation of this file.
1
9
11
12#include <memory>
13#include <vector>
14#include <string>
15
16#include <cstring>
17
18struct stat;
19
20class XrdSysError;
21
22namespace TPC {
23class Stream {
24public:
25 Stream(std::unique_ptr<XrdSfsFile> fh, size_t max_blocks, size_t buffer_size, XrdSysError &log)
26 : m_open_for_write(false),
27 m_avail_count(max_blocks),
28 m_fh(std::move(fh)),
29 m_offset(0),
30 m_log(log)
31 {
32 m_buffers.reserve(max_blocks);
33 for (size_t idx=0; idx < max_blocks; idx++) {
34 m_buffers.push_back(std::make_unique<Entry>(buffer_size));
35 }
36 m_open_for_write = true;
37 }
38
39 ~Stream();
40
41 int Stat(struct stat *);
42
43 int Read(off_t offset, char *buffer, size_t size);
44
45 // Writes a buffer of a given size to an offset.
46 // This will often keep the buffer in memory in to present the underlying
47 // filesystem with a single stream of data (required for HDFS); further,
48 // it will also buffer to align the writes on a 1MB boundary (required
49 // for some RADOS configurations). When force is set to true, it will
50 // skip the buffering and always write (this should only be done at the
51 // end of a stream!).
52 //
53 // Returns the number of bytes written; on error, returns -1 and sets
54 // the error code and error message for the stream
55 ssize_t Write(off_t offset, const char *buffer, size_t size, bool force);
56
57 size_t AvailableBuffers() const {return m_avail_count;}
58
59 void DumpBuffers() const;
60
61 // Flush and finalize the stream. If all data has been sent to the underlying
62 // file handle, close() will be invoked on the file handle.
63 //
64 // Further write operations on this stream will result in an error.
65 // If any memory buffers remain, an error occurs.
66 //
67 // Returns true on success; false otherwise.
68 bool Finalize();
69
70 std::string GetErrorMessage() const {return m_error_buf;}
71
72private:
73
74 class Entry {
75 public:
76 Entry(size_t capacity) :
77 m_offset(-1),
78 m_capacity(capacity),
79 m_size(0)
80 {}
81
82 bool Available() const {return m_offset == -1;}
83
84 // Writes the contents of this buffer out to the stream, returning the
85 // number of bytes written (0 if the buffer is not eligible for a write
86 // yet) or SFS_ERROR. On success the buffer is emptied and becomes
87 // available again.
88 ssize_t Write(Stream &stream, bool force) {
89 if (Available() || !CanWrite(stream)) {return 0;}
90 // Only full buffer writes are accepted unless the stream forces a flush
91 // (i.e., we are at EOF) because the multistream code uses buffer occupancy
92 // to determine how many streams are currently in-flight. If we do an early
93 // write, then the buffer will be empty and the multistream code may decide
94 // to start another request (which we don't have the capacity to serve!).
95 if (!force && (m_size != m_capacity)) {
96 return 0;
97 }
98 ssize_t retval = stream.WriteImpl(m_offset, &m_buffer[0], m_size);
99 // Currently the only valid negative value is SFS_ERROR (-1); checking for
100 // all negative values to future-proof the code.
101 if ((retval < 0) || (static_cast<size_t>(retval) != m_size)) {
102 return -1;
103 }
104 m_offset = -1;
105 m_size = 0;
106 m_buffer.clear();
107 return retval;
108 }
109
110 size_t Accept(off_t offset, const char *buf, size_t size) {
111 // Validate acceptance criteria.
112 if ((m_offset != -1) && (offset != m_offset + static_cast<ssize_t>(m_size))) {
113 return 0;
114 }
115 size_t to_accept = m_capacity - m_size;
116 if (to_accept == 0) {return 0;}
117 if (size > to_accept) {
118 size = to_accept;
119 }
120
121 // Inflate the underlying buffer if needed.
122 ssize_t new_bytes_needed = (m_size + size) - m_buffer.size();
123 if (new_bytes_needed > 0) {
124 m_buffer.resize(m_capacity);
125 }
126
127 // Finally, do the copy.
128 memcpy(&m_buffer[0] + m_size, buf, size);
129 m_size += size;
130 if (m_offset == -1) {
131 m_offset = offset;
132 }
133 return size;
134 }
135
136 void ShrinkIfUnused() {
137 if (!Available()) {return;}
138 m_buffer.shrink_to_fit();
139 }
140
141 off_t GetOffset() const {return m_offset;}
142 size_t GetCapacity() const {return m_capacity;}
143 size_t GetSize() const {return m_size;}
144
145 private:
146
147 Entry(const Entry&) = delete;
148
149 bool CanWrite(Stream &stream) const {
150 return (m_size > 0) && (m_offset == stream.m_offset);
151 }
152
153 off_t m_offset; // Offset within file that m_buffer[0] represents.
154 size_t m_capacity;
155 size_t m_size; // Number of bytes held in buffer.
156 std::vector<char> m_buffer;
157 };
158
159 ssize_t WriteImpl(off_t offset, const char *buffer, size_t size);
160
161 // Copies as much of [buffer, buffer+size) as possible into the buffers that
162 // are already holding data and can be extended contiguously. This is pure
163 // bookkeeping: it never touches the underlying filesystem.
164 //
165 // Returns the number of bytes consumed.
166 size_t AcceptIntoBuffers(off_t offset, const char *buffer, size_t size);
167
168 // Writes out every buffer that is contiguous with m_offset, repeating until
169 // no further progress is made: flushing one buffer advances m_offset, which
170 // can in turn make another buffer writable. Only completely full buffers
171 // are written unless force is set (see Entry::Write).
172 //
173 // This is the only place where m_avail_count is computed.
174 //
175 // Returns the number of buffers written out, or SFS_ERROR.
176 ssize_t FlushBuffers(bool force);
177
178 // Returns the first empty buffer, or nullptr if all of them hold data.
179 Entry *FirstAvailableBuffer();
180
181 bool m_open_for_write;
182 size_t m_avail_count;
183 std::unique_ptr<XrdSfsFile> m_fh;
184 off_t m_offset;
185 std::vector<std::unique_ptr<Entry>> m_buffers;
186 XrdSysError &m_log;
187 std::string m_error_buf;
188};
189}
struct stat Stat
Definition XrdCks.cc:49
#define stat(a, b)
Definition XrdPosix.hh:101
Stream(std::unique_ptr< XrdSfsFile > fh, size_t max_blocks, size_t buffer_size, XrdSysError &log)
void DumpBuffers() const
std::string GetErrorMessage() const
size_t AvailableBuffers() const