casacore
Loading...
Searching...
No Matches
RowBasedFile.h
Go to the documentation of this file.
1#ifndef CASACORE_ROW_BASED_FILE_H_
2#define CASACORE_ROW_BASED_FILE_H_
3
4#include <fcntl.h>
5#include <unistd.h>
6
7#include <array>
8#include <cstdint>
9#include <string>
10#include <vector>
11
12namespace casacore {
13
15 public:
16 RowBasedFile() = default;
17 RowBasedFile(const RowBasedFile& rhs) = delete;
18 RowBasedFile(RowBasedFile&& rhs) noexcept
19 : file_(rhs.file_),
20 private_header_size_(rhs.private_header_size_),
21 n_rows_(rhs.n_rows_),
22 need_truncate_(rhs.need_truncate_),
23 stride_(rhs.stride_),
24 data_location_(rhs.data_location_),
25 filename_(rhs.filename_) {
26 rhs.file_ = -1;
27 rhs.private_header_size_ = kWriterPrivateHeaderSize;
28 rhs.n_rows_ = 0;
29 rhs.need_truncate_ = false;
30 rhs.stride_ = 0;
31 rhs.data_location_ = kWriterPrivateHeaderSize;
32 rhs.filename_ = "";
33 }
34
38 RowBasedFile(const std::string& filename, uint64_t header_size, uint64_t stride)
39 : stride_(stride), filename_(filename) {
40 file_ =
41 open(filename.c_str(), O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
42 if (file_ < 0)
43 throw std::runtime_error("I/O error: could not create new file '" + filename + "'");
46 }
47
51 RowBasedFile(const std::string& filename, size_t header_size) : filename_(filename) {
52 file_ = open(filename.c_str(), O_RDWR);
53 if (file_ < 0) {
54 file_ = open(filename.c_str(), O_RDONLY);
55 if (file_ < 0) throw std::runtime_error("I/O error: could not open file '" + filename + "'");
56 }
57 uint32_t magic_tag;
58 ReadData(reinterpret_cast<unsigned char*>(&magic_tag), sizeof(uint32_t));
59 if (magic_tag != kMagicFileTag) {
60 throw std::runtime_error(
61 "Could not read file " + filename +
62 ": file does not obey the Casacore row-based file format: either the "
63 "file is damaged, or this is not a Casacore row-based file");
64 }
65
66 uint32_t file_version;
67 ReadData(reinterpret_cast<unsigned char*>(&file_version), sizeof(uint32_t));
68 const uint32_t major_version = (file_version & 0xFF00) >> 8;
69 constexpr uint32_t kWriterMajorVersion = (kFileVersion & 0xFF00) >> 8;
70 if (major_version > kWriterMajorVersion) {
71 throw std::runtime_error(
72 "The file " + filename + " requires a reader of at least major version " +
73 std::to_string(major_version) + ". This reader is for major version " +
74 std::to_string(kWriterMajorVersion) + ".");
75 }
76
77 // Combine reading of private header size, stride and user header size in
78 // one read call.
79 std::array<unsigned char, sizeof(uint32_t) + 2 * sizeof(uint64_t)> rest_of_private_header;
80 ReadData(rest_of_private_header.data(), rest_of_private_header.size());
81 private_header_size_ = reinterpret_cast<uint32_t&>(rest_of_private_header.data()[0]);
82 stride_ = reinterpret_cast<uint64_t&>(rest_of_private_header.data()[sizeof(uint32_t)]);
83 size_t file_user_header_size = reinterpret_cast<uint64_t&>(
84 rest_of_private_header.data()[sizeof(uint32_t) + sizeof(uint64_t)]);
85 if (file_user_header_size != header_size) {
86 throw std::runtime_error("Error reading file " + filename +
87 ": inconsistent size of private header");
88 }
89
91 const uint64_t pos = lseek(file_, 0, SEEK_END);
92 n_rows_ = stride_ == 0 ? 0 : (pos - data_location_) / stride_;
93 }
94 ~RowBasedFile() noexcept {
95 try {
96 Close();
97 } catch (...) {
98 }
99 }
101 Close();
102 std::swap(file_, rhs.file_);
103 std::swap(private_header_size_, rhs.private_header_size_);
104 std::swap(n_rows_, rhs.n_rows_);
105 std::swap(need_truncate_, rhs.need_truncate_);
106 std::swap(stride_, rhs.stride_);
107 std::swap(data_location_, rhs.data_location_);
108 std::swap(filename_, rhs.filename_);
109 return *this;
110 }
111
117 void Close() {
118 if (IsOpen()) {
119 if (need_truncate_) {
120 try {
121 need_truncate_ = false;
122 Truncate(NRows());
123 } catch (...) {
124 // Truncate failed, still try to close the file to prevent a dangling
125 // open file, before throwing the exception.
127 throw;
128 }
129 }
131 }
132 }
133
134 void Truncate(uint64_t n_rows) {
135 const int result = ftruncate(file_, n_rows_ * stride_ + data_location_);
136 if (result < 0) {
137 throw std::runtime_error("I/O error: could not truncate file '" + filename_ + "' to have " +
138 std::to_string(n_rows) + " rows: " + ErrorString());
139 }
140 }
141
142 void Seek(off_t pos, int seek_direction) {
143 const off_t result = lseek(file_, pos, seek_direction);
144 if (result < 0)
145 throw std::runtime_error("I/O error: could not seek through file '" + filename_ + "'");
146 }
147
148 void ReadData(unsigned char* data, uint64_t size) {
149 const int result = ::read(file_, data, size);
150 if (result < 0)
151 throw std::runtime_error("I/O error: could not read from file '" + filename_ + "'");
152 }
153
154 void WriteData(const unsigned char* data, uint64_t size) {
155 const int result = write(file_, data, size);
156 if (result < 0)
157 throw std::runtime_error("I/O error: could not write to file '" + filename_ + "'");
158 }
159 bool IsOpen() const { return file_ >= 0; }
164 uint64_t DataLocation() const { return data_location_; }
165 const std::string& Filename() const { return filename_; }
169 uint64_t HeaderSize() const { return data_location_ - private_header_size_; }
176 void WriteHeader(const unsigned char* data) {
177 Seek(private_header_size_, SEEK_SET);
179 }
180
183 void ReadHeader(unsigned char* data) {
184 Seek(private_header_size_, SEEK_SET);
186 }
187
190 uint64_t NRows() const { return n_rows_; }
191
192 void SetNRows(uint64_t new_n_rows) {
193 need_truncate_ = true;
194 n_rows_ = new_n_rows;
195 }
196
201 uint64_t Stride() const { return stride_; }
206 void SetStride(uint64_t new_stride) {
207 const uint64_t header_size = HeaderSize();
208 Truncate(0);
209 n_rows_ = 0;
210 stride_ = new_stride;
211 Seek(0, SEEK_SET);
214 }
215
219 void AddRows(uint64_t n_rows) { SetNRows(NRows() + n_rows); }
220
224 void DeleteRow() {
225 if (NRows() > 0) {
226 SetNRows(NRows() - 1);
227 }
228 }
229
230 private:
232 int result = close(file_);
233 file_ = -1;
235 n_rows_ = 0;
236 stride_ = 0;
238 filename_ = "";
239 if (result < 0) throw std::runtime_error("Could not close file " + filename_);
240 }
241
243 // Collect entire private header in one write call
244 std::array<unsigned char, kWriterPrivateHeaderSize> private_header_buffer;
245 reinterpret_cast<uint32_t&>(private_header_buffer[0]) = kMagicFileTag;
246 reinterpret_cast<uint32_t&>(private_header_buffer[4]) = kFileVersion;
247 reinterpret_cast<uint32_t&>(private_header_buffer[8]) = kWriterPrivateHeaderSize;
248 reinterpret_cast<uint64_t&>(private_header_buffer[12]) = stride_;
249 reinterpret_cast<uint64_t&>(private_header_buffer[20]) = HeaderSize();
250
251 WriteData(private_header_buffer.data(), private_header_buffer.size());
252 }
253
266 inline constexpr static uint32_t kWriterPrivateHeaderSize =
267 3 * sizeof(uint32_t) + 2 * sizeof(uint64_t);
268
280 inline constexpr static uint32_t kMagicFileTag = 0x66627243;
281
290 inline constexpr static uint32_t kFileVersion = 0x0100;
291
292 static std::string ErrorStringHelper(int result_value, char* buffer) {
293 if (result_value == 0)
294 return buffer;
295 else
296 return "Unknown error";
297 }
298
299 static std::string ErrorStringHelper(char* returned_buffer, char* /*supplied_buffer*/) {
300 return std::string(returned_buffer);
301 }
302
303 static std::string ErrorString() {
304 char errstr[128];
305 // This is a small trick to allow both versions of strerror_r: by using
306 // function overloading, the right behaviour is picked.
307 return ErrorStringHelper(strerror_r(errno, errstr, 128), errstr);
308 }
309
310 // The "C" file API is used because we need to use (f)truncate, which is not
311 // available from the C++ fstream API.
312 int file_ = -1;
314 uint64_t n_rows_ = 0;
315 bool need_truncate_ = false;
316 uint64_t stride_ = 0;
322 std::string filename_;
323};
324
325} // namespace casacore
326
327#endif
RowBasedFile(const std::string &filename, uint64_t header_size, uint64_t stride)
Create or overwrite a new columnar file on disk.
void WriteData(const unsigned char *data, uint64_t size)
static constexpr uint32_t kFileVersion
Version of this file, in format 0xaabb, where aa is the major version and bb is the minor version.
uint64_t data_location_
This variable is also used to set/calculate the header size, using the relation: data_location_ = pri...
static constexpr uint32_t kWriterPrivateHeaderSize
The size of the private header that the writer creates for the current file format.
RowBasedFile(RowBasedFile &&rhs) noexcept
void Seek(off_t pos, int seek_direction)
static std::string ErrorStringHelper(char *returned_buffer, char *)
uint64_t DataLocation() const
Offset of the first row in the file.
void SetStride(uint64_t new_stride)
Set the number of bytes per row for this file.
void ReadHeader(unsigned char *data)
Read an optional extra header to the file.
RowBasedFile(const std::string &filename, size_t header_size)
Open an existing columnar file.
const std::string & Filename() const
RowBasedFile & operator=(RowBasedFile &&rhs)
static constexpr uint32_t kMagicFileTag
First four bytes of a file.
void WriteHeader(const unsigned char *data)
Write an optional extra header to the file.
void AddRows(uint64_t n_rows)
Adds a given number of rows to the back of the file.
void Truncate(uint64_t n_rows)
int file_
The "C" file API is used because we need to use (f)truncate, which is not available from the C++ fstr...
uint64_t HeaderSize() const
Number of bytes reserved for an optional header.
uint64_t NRows() const
Total number of rows stored in this file.
RowBasedFile(const RowBasedFile &rhs)=delete
void ReadData(unsigned char *data, uint64_t size)
void DeleteRow()
Deletes the last row.
static std::string ErrorStringHelper(int result_value, char *buffer)
static std::string ErrorString()
uint64_t Stride() const
Total number of bytes in one row.
void SetNRows(uint64_t new_n_rows)
void Close()
Close the file.
For temporary backward namespace compatibility, use casa as alias for casacore.
Definition mainpage.dox:28
virtual int read()
The read()' and write()' functions control reading and writing data from the external FITS I/O medium...
void close() override
Flush and close the file.
virtual int write(FitsOutput &)
size_t size() const
Definition Block.h:566