LeftHookRoll
An HTTP/1.0 compliant web server, as specified by RFC1945
Loading...
Searching...
No Matches
DataStore.cpp
Go to the documentation of this file.
1/**
2 * @file DataStore.cpp
3 * @brief A DataStore.hpp implementation with some helping functions
4 *
5 */
6
7#include "../includes/DataStore.hpp"
8#include <cstdio>
9
10
11/**
12 * @brief Ensures all data is written to the file descriptor. Throws on any system error.
13 */
14void DataStore::write_all(int fd, const char* data, size_t length) {
15 size_t offset = 0;
16 while (offset < length) {
17 ssize_t written = ::write(fd, data + offset, length - offset);
18 if (written < 0) {
19 // if (errno == EINTR) continue; TBD
20 throw std::runtime_error(std::string("DataStore: write failed - ") + std::strerror(errno));
21 }
22 if (written == 0) {
23 throw std::runtime_error("DataStore: write failed - 0 bytes written (possible disk full)");
24 }
25 offset += static_cast<size_t>(written);
26 }
27}
28
29/**
30 * @brief Copies data directly from one FD to another using a buffer. Throws on error.
31 */
32void DataStore::copy_fd_contents(const std::string& srcPath, int dstFd, size_t totalBytes) {
33 int srcFd = ::open(srcPath.c_str(), O_RDONLY);
34 if (srcFd < 0) {
35 throw std::runtime_error(std::string("DataStore: open failed during copy - ") + std::strerror(errno));
36 }
37 static const size_t kChunkSize = 8192;
38 std::vector<char> buffer(kChunkSize);
39 size_t offset = 0;
40 try {
41 while (offset < totalBytes) {
42 size_t toRead = std::min(kChunkSize, totalBytes - offset);
43 ssize_t readBytes = ::read(srcFd, &buffer[0], toRead);
44
45 if (readBytes < 0) {
46 // if (errno == EINTR) continue; TBD
47 throw std::runtime_error(std::string("DataStore: read failed during copy - ") + std::strerror(errno));
48 }
49 if (readBytes == 0) {
50 throw std::runtime_error("DataStore: copy failed - unexpected EOF (source truncated)");
51 }
52
53 write_all(dstFd, &buffer[0], static_cast<size_t>(readBytes));
54 offset += static_cast<size_t>(readBytes);
55 }
56 } catch (...) {
57 ::close(srcFd);
58 throw; // Re-throw the exception after cleaning up the FD
59 }
60
61 ::close(srcFd);
62}
63// Canonical Form
64
65DataStore::DataStore(): _mode(RAM), _bufferLimit(BUFFERLIMIT), _currentSize(0), _readOffset(0), _dataBuffer(), _fileFd(-1), _absolutePath()
66{
67}
68
69DataStore::DataStore(const DataStore& other): _mode(RAM), _bufferLimit(other._bufferLimit), _currentSize(0), _readOffset(0), _dataBuffer(), _fileFd(-1), _absolutePath()
70{
71 if (other._mode == RAM) {
72 _mode = RAM;
75 }
76 else {
77 _mode = RAM;
78 _dataBuffer.clear();
81 try {
82 if (_currentSize > 0) {
84 }
85 }
86 catch (...) {
87 if (_fileFd != -1) {
88 ::close(_fileFd);
89 _fileFd = -1;
90 }
91 throw; // Re-throw so the caller knows it failed!
92 }
93 }
94}
95
97 std::swap(_mode, other._mode);
98 std::swap(_bufferLimit, other._bufferLimit);
99 std::swap(_currentSize, other._currentSize);
100 std::swap(_readOffset, other._readOffset);
101 std::swap(_dataBuffer, other._dataBuffer);
102 std::swap(_fileFd, other._fileFd);
103 std::swap(_absolutePath, other._absolutePath);
104 return *this;
105}
106
110
111// Core Behavior
112
113/**
114 * @brief Appends raw byte data to the store.
115 * Automatically transitions from RAM to FILE_MODE if _bufferLimit is exceeded.
116 * @param data Pointer to the buffer to append.
117 * @param length Number of bytes to append.
118 */
119void DataStore::append(const char* data, size_t length) {
120 if (data == NULL || length == 0) {
121 return;
122 }
123
124 if (_mode == RAM) {
125 if (_currentSize + length > _bufferLimit) {
127 write_all(_fileFd, data, length);
128 _currentSize += length;
129 }
130 else {
131 _dataBuffer.insert(_dataBuffer.end(), data, data + length);
132 _currentSize += length;
133 }
134 }
135 else {
136 write_all(_fileFd, data, length);
137 _currentSize += length;
138 }
139
140}
141
142/**
143 * @brief Convenience overload to append a std::string directly.
144 */
145void DataStore::append(const std::string& data) {
146 append(data.c_str(), data.size());
147}
148
149/**
150 * @brief Resets the store, clears the vector, and closes the temp file descriptor.
151 */
153 _dataBuffer.clear();
154 _currentSize = 0;
155 _readOffset = 0;
156 _mode = RAM;
157 if (_fileFd != -1) {
158 ::close(_fileFd);
159 _fileFd = -1;
160 }
161 if (!_absolutePath.empty()) {
162 std::remove(_absolutePath.c_str());
163 }
164 _absolutePath.clear();
165}
166
167// Getters
168
170 return _mode;
171}
172
173const std::vector<char>& DataStore::getVector() const {
174 return _dataBuffer;
175}
176
177int DataStore::getFd() const {
178 return _fileFd;
179}
180
181std::string DataStore::getFilePath() const {
182 return _absolutePath;
183}
184
185size_t DataStore::getSize() const {
186 return _currentSize;
187}
188
189
190/**
191 * @brief Reads data from the store starting at the current read position.
192 * Advances the internal read position by the number of bytes read.
193 * @param buffer Pointer to the buffer to read into.
194 * @param length Maximum number of bytes to read.
195 * @return Number of bytes actually read (may be less if end of data is reached).
196 */
197size_t DataStore::read(char* buffer, size_t length) {
198 if (buffer == NULL || length == 0 || _readOffset >= _currentSize) {
199 return 0;
200 }
201
202 size_t available = _currentSize - _readOffset;
203 size_t toRead = std::min(length, available);
204
205 if (_mode == RAM) {
206 std::memcpy(buffer, &_dataBuffer[_readOffset], toRead);
207 _readOffset += toRead;
208 return toRead;
209 }
210 else {
211 size_t totalRead = 0;
212 while (totalRead < toRead) {
213 ssize_t bytesRead = ::read(_fileFd, buffer + totalRead, toRead - totalRead);
214 if (bytesRead < 0) {
215 // if (errno == EINTR) continue; TBD
216 throw std::runtime_error(std::string("DataStore: read failed - ") + std::strerror(errno));
217 }
218 if (bytesRead == 0) {
219 break; // EOF
220 }
221 totalRead += static_cast<size_t>(bytesRead);
222 }
223 _readOffset += totalRead;
224 return totalRead;
225 }
226}
227
228/**
229 * @brief Resets the internal read position to the beginning of the data.
230 */
232 _readOffset = 0;
233 if (_mode == FILE_MODE && _fileFd != -1) {
234 ::close(_fileFd);
235 _fileFd = ::open(_absolutePath.c_str(), O_RDWR | O_APPEND, 0600);
236 if (_fileFd == -1) {
237 throw std::runtime_error("DataStore: Failed to reopen for reset");
238 }
239 }
240}
241
242/**
243 * @brief Gets the current read position.
244 * @return Current read offset.
245 */
247 return _readOffset;
248}
249
250/**
251 * @brief Handles the transition from RAM to a temporary file on disk.
252 * Uses the immediate unlink() trick to ensure OS-level cleanup on crashes.
253 */
255 if (_mode == FILE_MODE) {
256 return ;
257 }
258
260 if (!_dataBuffer.empty()) {
261 try {
263 } catch (...) {
264 ::close(_fileFd);
265 _fileFd = -1;
266 _absolutePath.clear();
267 throw;
268 }
269 }
270 _dataBuffer.clear();
272}
273
274/**
275 * @brief Generates a unique temporary filename (e.g., FILEPREFIX_XXXXXX).
276 */
278 static long long file_counter = 0;//assuming 1 file created per second, this will take 21 times the age of the universe to wrap around, so should be safe for our humble webserv.
279 int fd = -1;
280 std::string currentName;
281
282 while (fd == -1) {
283 std::stringstream ss;
284 ss << FILEPREFIX << file_counter++;
285 currentName = ss.str();
286
287 fd = ::open(currentName.c_str(), O_CREAT | O_EXCL | O_RDWR | O_APPEND, 0600);
288
289 if (fd == -1) {
290 if (errno == EEXIST) {
291 continue; // File exists, loop again and try the next number!
292 }
293 throw std::runtime_error(std::string("DataStore: open failed - ") + std::strerror(errno));
294 }
295 }
296
297 _fileFd = fd;
298 _absolutePath = currentName;
299}
#define FILEPREFIX
Definition DataStore.hpp:22
#define BUFFERLIMIT
Definition DataStore.hpp:23
BufferMode
Indicates where the data is currently being stored.
Definition DataStore.hpp:29
@ RAM
Definition DataStore.hpp:30
@ FILE_MODE
Definition DataStore.hpp:31
size_t getReadPosition() const
Gets the current read position.
void copy_fd_contents(const std::string &srcPath, int dstFd, size_t totalBytes)
Copies data directly from one FD to another using a buffer.
Definition DataStore.cpp:32
std::string _absolutePath
int getFd() const
Returns the file descriptor of the temporary file.
const std::vector< char > & getVector() const
Returns a reference to the RAM buffer.
std::string getFilePath() const
Gets the absolute path of the temporary file.
BufferMode _mode
void _generateTempFileName()
Generates a unique temporary filename (e.g., FILEPREFIX_XXXXXX).
size_t getSize() const
Gets total bytes currently stored (whether in RAM or File).
size_t _bufferLimit
std::vector< char > _dataBuffer
void append(const char *data, size_t length)
Appends raw byte data to the store. Automatically transitions from RAM to FILE_MODE if _bufferLimit i...
size_t _currentSize
void switchToFileMode()
Handles the transition from RAM to a temporary file on disk. Uses the immediate unlink() trick to ens...
void resetReadPosition()
Resets the internal read position to the beginning of the data.
void write_all(int fd, const char *data, size_t length)
Ensures all data is written to the file descriptor.
Definition DataStore.cpp:14
size_t read(char *buffer, size_t length)
Reads data from the store starting at the current read position. Advances the internal read position ...
size_t _readOffset
DataStore & operator=(DataStore other)
Definition DataStore.cpp:96
void clear()
Resets the store, clears the vector, and closes the temp file descriptor.
BufferMode getMode() const
Returns the current storage mode (RAM or FILE_MODE).