C++ Utilities 5.26.0
Useful C++ classes and routines such as argument parser, IO and conversion utilities
Loading...
Searching...
No Matches
nativefilestream.cpp
Go to the documentation of this file.
2
3#ifdef CPP_UTILITIES_USE_NATIVE_FILE_BUFFER
4
23#ifdef PLATFORM_WINDOWS
25#endif
26
27// include header files for file buffer implementation
28#if defined(CPP_UTILITIES_USE_GNU_CXX_STDIO_FILEBUF)
29#include <ext/stdio_filebuf.h>
30#elif defined(CPP_UTILITIES_USE_BOOST_IOSTREAMS)
31#include <boost/iostreams/device/file_descriptor.hpp>
32#include <boost/iostreams/stream.hpp>
33#else
34#error "Configuration for NativeFileStream backend insufficient."
35#endif
36
37// include platform specific header
38#if defined(PLATFORM_UNIX)
39#include <cstdio>
40#include <fcntl.h>
41#include <sys/stat.h>
42#include <sys/types.h>
43#elif defined(PLATFORM_WINDOWS)
44#include <fcntl.h>
45#include <io.h>
46#include <limits>
47#include <sys/stat.h> // yes, this is needed under Windows (https://msdn.microsoft.com/en-US/library/5yhhz3y7.aspx)
48#include <windows.h>
49#endif
50
51#endif
52
53using namespace std;
54
55namespace CppUtilities {
56
57#ifdef CPP_UTILITIES_USE_NATIVE_FILE_BUFFER
58
59#ifdef CPP_UTILITIES_USE_GNU_CXX_STDIO_FILEBUF
60using StreamBuffer = __gnu_cxx::stdio_filebuf<char>;
61#else // CPP_UTILITIES_USE_BOOST_IOSTREAMS
62using StreamBuffer = boost::iostreams::stream_buffer<boost::iostreams::file_descriptor>;
63#endif
64
65struct NativeFileParams {
66
67#ifdef PLATFORM_WINDOWS
68 NativeFileParams(ios_base::openmode cppOpenMode)
69 : openMode(cppOpenMode & ios_base::binary ? _O_BINARY : 0)
70 , flags(cppOpenMode & ios_base::binary ? 0 : _O_TEXT)
71 , permissions(0)
72 , access(0)
73 , shareMode(0)
74 , creation(0)
75 {
76 if ((cppOpenMode & ios_base::out) && (cppOpenMode & ios_base::in)) {
77 openMode |= _O_RDWR;
78 access = GENERIC_READ | GENERIC_WRITE;
79 shareMode = FILE_SHARE_READ;
80 creation = OPEN_EXISTING;
81 } else if (cppOpenMode & ios_base::out) {
82 openMode |= _O_WRONLY | _O_CREAT;
83 permissions = _S_IREAD | _S_IWRITE;
84 access = GENERIC_WRITE;
85 creation = OPEN_ALWAYS;
86 } else if (cppOpenMode & ios_base::in) {
87 openMode |= _O_RDONLY;
88 flags |= _O_RDONLY;
89 access = GENERIC_READ;
90 shareMode = FILE_SHARE_READ;
91 creation = OPEN_EXISTING;
92 }
93 if (cppOpenMode & ios_base::app) {
94 openMode |= _O_APPEND;
95 flags |= _O_APPEND;
96 }
97 if (cppOpenMode & ios_base::trunc) {
98 openMode |= _O_TRUNC;
99 creation = (cppOpenMode & ios_base::in) ? TRUNCATE_EXISTING : CREATE_ALWAYS;
100 }
101 }
102
103 int openMode;
104 int flags;
105 int permissions;
106 DWORD access;
107 DWORD shareMode;
108 DWORD creation;
109#else
110 NativeFileParams(ios_base::openmode cppOpenMode)
111 : openFlags(0)
112 {
113 if ((cppOpenMode & ios_base::in) && (cppOpenMode & ios_base::out)) {
114 if (cppOpenMode & ios_base::app) {
115 openMode = "a+";
116 openFlags = O_RDWR | O_APPEND;
117 } else if (cppOpenMode & ios_base::trunc) {
118 openMode = "w+";
119 openFlags = O_RDWR | O_TRUNC;
120 } else {
121 openMode = "r+";
122 openFlags = O_RDWR;
123 }
124 } else if (cppOpenMode & ios_base::in) {
125 openMode = 'r';
126 openFlags = O_RDONLY;
127 } else if (cppOpenMode & ios_base::out) {
128 if (cppOpenMode & ios_base::app) {
129 openMode = 'a';
130 openFlags = O_WRONLY | O_APPEND;
131 } else if (cppOpenMode & ios_base::trunc) {
132 openMode = 'w';
133 openFlags = O_WRONLY | O_TRUNC | O_CREAT;
134 } else {
135 openMode = "w";
136 openFlags = O_WRONLY | O_CREAT;
137 }
138 }
139 if (cppOpenMode & ios_base::binary) {
140 openMode += 'b';
141 }
142 }
143
144 std::string openMode;
145 int openFlags;
146#endif
147};
148
157NativeFileStream::FileBuffer::FileBuffer(std::basic_streambuf<char> *buffer)
158 : buffer(buffer)
159{
160}
161
166NativeFileStream::FileBuffer::FileBuffer(const char *path, ios_base::openmode openMode)
167{
168#ifdef PLATFORM_WINDOWS
169 // convert path to UTF-16
170 const auto widePath(makeWidePath(path));
171#endif
172
173 // compute native params
174 const NativeFileParams nativeParams(openMode);
175
176// open native file handle or descriptor
177#ifdef CPP_UTILITIES_USE_GNU_CXX_STDIO_FILEBUF
178#ifdef PLATFORM_WINDOWS
179 descriptor = _wopen(widePath.get(), nativeParams.openMode, nativeParams.permissions);
180 if (descriptor == -1) {
181 throw std::ios_base::failure("_wopen failed", std::error_code(errno, std::system_category()));
182 }
183#else
184 descriptor = ::open(path, nativeParams.openFlags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
185 if (descriptor == -1) {
186 throw std::ios_base::failure("open failed", std::error_code(errno, std::system_category()));
187 }
188#endif
189 buffer = make_unique<StreamBuffer>(descriptor, openMode);
190#else // CPP_UTILITIES_USE_BOOST_IOSTREAMS
191#ifdef PLATFORM_WINDOWS
192 handle = CreateFileW(widePath.get(), nativeParams.access, nativeParams.shareMode, nullptr, nativeParams.creation, FILE_ATTRIBUTE_NORMAL, nullptr);
193 if (handle == INVALID_HANDLE_VALUE) {
194 throw std::ios_base::failure("CreateFileW failed", std::error_code(GetLastError(), std::system_category()));
195 }
196 buffer = make_unique<StreamBuffer>(handle, boost::iostreams::close_handle);
197 // if we wanted to open assign the descriptor as well: descriptor = _open_osfhandle(reinterpret_cast<intptr_t>(handle), nativeParams.flags);
198#else
199 descriptor = ::open(path.data(), nativeParams.openFlags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
200 if (descriptor == -1) {
201 throw std::ios_base::failure("open failed", std::error_code(errno, std::system_category()));
202 }
203 buffer = make_unique<StreamBuffer>(descriptor, boost::iostreams::close_handle);
204#endif
205#endif
206}
207
212NativeFileStream::FileBuffer::FileBuffer(const std::string &path, ios_base::openmode openMode)
213 : NativeFileStream::FileBuffer(path.data(), openMode)
214{
215}
216
223NativeFileStream::FileBuffer::FileBuffer(int fileDescriptor, ios_base::openmode openMode)
224 : descriptor(fileDescriptor)
225{
226#ifdef CPP_UTILITIES_USE_GNU_CXX_STDIO_FILEBUF
227 buffer = make_unique<StreamBuffer>(descriptor, openMode);
228#else // CPP_UTILITIES_USE_BOOST_IOSTREAMS
229 CPP_UTILITIES_UNUSED(openMode)
230#ifdef PLATFORM_WINDOWS
231 handle = reinterpret_cast<Handle>(_get_osfhandle(descriptor));
232 buffer = make_unique<StreamBuffer>(handle, boost::iostreams::close_handle);
233#else
234 buffer = make_unique<StreamBuffer>(descriptor, boost::iostreams::close_handle);
235#endif
236#endif
237}
238
242NativeFileStream::NativeFileStream()
243 : iostream(new StreamBuffer)
244 , m_data(rdbuf())
245{
246}
247
251NativeFileStream::NativeFileStream(NativeFileStream &&other)
252 : iostream(other.m_data.buffer.release())
253 , m_data(rdbuf())
254{
255#ifdef PLATFORM_WINDOWS
256 m_data.handle = other.m_data.handle;
257#endif
258 m_data.descriptor = other.m_data.descriptor;
259}
260
264NativeFileStream::~NativeFileStream()
265{
266}
267
271bool NativeFileStream::isOpen() const
272{
273 return m_data.buffer && static_cast<const StreamBuffer *>(m_data.buffer.get())->is_open();
274}
275
287void NativeFileStream::open(const char *path, ios_base::openmode openMode)
288{
289 setData(FileBuffer(path, openMode), openMode);
290}
291
295void NativeFileStream::open(const std::string &path, ios_base::openmode openMode)
296{
297 open(path.data(), openMode);
298}
299
307void NativeFileStream::open(int fileDescriptor, ios_base::openmode openMode)
308{
309 setData(FileBuffer(fileDescriptor, openMode), openMode);
310}
311
315void NativeFileStream::close()
316{
317 if (m_data.buffer) {
318 static_cast<StreamBuffer *>(m_data.buffer.get())->close();
319#ifdef PLATFORM_WINDOWS
320 m_data.handle = nullptr;
321#endif
322 m_data.descriptor = -1;
323 }
324}
325
329void NativeFileStream::setData(FileBuffer data, std::ios_base::openmode openMode)
330{
331 rdbuf(data.buffer.get());
332 m_data = std::move(data);
333 m_openMode = openMode;
334#if defined(PLATFORM_WINDOWS) && defined(CPP_UTILITIES_USE_BOOST_IOSTREAMS)
335 // workaround append flag dysfunctioning
336 if (m_openMode & ios_base::app) {
337 seekp(0, ios_base::end);
338 }
339#endif
340}
341
342#ifdef PLATFORM_WINDOWS
347std::unique_ptr<wchar_t[]> NativeFileStream::makeWidePath(std::string_view path)
348{
349 auto ec = std::error_code();
350 auto size = path.size() < static_cast<std::size_t>(std::numeric_limits<int>::max() - 1) ? static_cast<int>(path.size() + 1) : -1;
351 auto widePath = ::CppUtilities::convertMultiByteToWide(ec, path.data(), size);
352 if (!widePath.first) {
353 throw std::ios_base::failure("converting path to UTF-16", ec);
354 }
355 return std::move(widePath.first);
356}
357
362std::unique_ptr<wchar_t[]> NativeFileStream::makeWidePath(const std::string &path)
363{
364 return makeWidePath(std::string_view(path));
365}
366#endif
367
368#else
369
370// std::fstream is used
371
372#endif
373} // namespace CppUtilities
#define CPP_UTILITIES_UNUSED(x)
Prevents warnings about unused variables.
Definition global.h:92
Contains all utilities provides by the c++utilities library.
std::fstream NativeFileStream
STL namespace.