C++ Utilities  5.0.0
Useful C++ classes and routines such as argument parser, IO and conversion utilities
nativefilestream.cpp
Go to the documentation of this file.
1 #include "./nativefilestream.h"
2 
3 #ifdef CPP_UTILITIES_USE_NATIVE_FILE_BUFFER
4 
5 #ifdef PLATFORM_WINDOWS
6 #include "../conversion/stringconversion.h"
7 #endif
8 
9 // include header files for file buffer implementation
10 #if defined(CPP_UTILITIES_USE_GNU_CXX_STDIO_FILEBUF)
11 #include <ext/stdio_filebuf.h>
12 #elif defined(CPP_UTILITIES_USE_BOOST_IOSTREAMS)
13 #include <boost/iostreams/device/file_descriptor.hpp>
14 #include <boost/iostreams/stream.hpp>
15 #else
16 #error "Configuration for NativeFileStream backend insufficient."
17 #endif
18 
19 // include platform specific header
20 #if defined(PLATFORM_UNIX)
21 #include <cstdio>
22 #include <fcntl.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #elif defined(PLATFORM_WINDOWS)
26 #include <fcntl.h>
27 #include <io.h>
28 #include <sys/stat.h> // yes, this is needed under Windows (https://msdn.microsoft.com/en-US/library/5yhhz3y7.aspx)
29 #include <windows.h>
30 #endif
31 
32 #endif
33 
34 using namespace std;
35 
36 namespace CppUtilities {
37 
38 #ifdef CPP_UTILITIES_USE_NATIVE_FILE_BUFFER
39 
40 #ifdef CPP_UTILITIES_USE_GNU_CXX_STDIO_FILEBUF
41 using StreamBuffer = __gnu_cxx::stdio_filebuf<char>;
42 #else // CPP_UTILITIES_USE_BOOST_IOSTREAMS
43 using StreamBuffer = boost::iostreams::stream_buffer<boost::iostreams::file_descriptor>;
44 #endif
45 
46 struct NativeFileParams {
47 
48 #ifdef PLATFORM_WINDOWS
49  NativeFileParams(ios_base::openmode cppOpenMode)
50  : openMode(cppOpenMode & ios_base::binary ? _O_BINARY : 0)
51  , flags(cppOpenMode & ios_base::binary ? 0 : _O_TEXT)
52  , permissions(0)
53  , access(0)
54  , shareMode(0)
55  , creation(0)
56  {
57  if ((cppOpenMode & ios_base::out) && (cppOpenMode & ios_base::in)) {
58  openMode |= _O_RDWR;
59  access = GENERIC_READ | GENERIC_WRITE;
60  shareMode = FILE_SHARE_READ;
61  creation = OPEN_EXISTING;
62  } else if (cppOpenMode & ios_base::out) {
63  openMode |= _O_WRONLY | _O_CREAT;
64  permissions = _S_IREAD | _S_IWRITE;
65  access = GENERIC_WRITE;
66  creation = OPEN_ALWAYS;
67  } else if (cppOpenMode & ios_base::in) {
68  openMode |= _O_RDONLY;
69  flags |= _O_RDONLY;
70  access = GENERIC_READ;
71  shareMode = FILE_SHARE_READ;
72  creation = OPEN_EXISTING;
73  }
74  if (cppOpenMode & ios_base::app) {
75  openMode |= _O_APPEND;
76  flags |= _O_APPEND;
77  }
78  if (cppOpenMode & ios_base::trunc) {
79  openMode |= _O_TRUNC;
80  creation = (cppOpenMode & ios_base::in) ? TRUNCATE_EXISTING : CREATE_ALWAYS;
81  }
82  }
83 
84  int openMode;
85  int flags;
86  int permissions;
87  DWORD access;
88  DWORD shareMode;
89  DWORD creation;
90 #else
91  NativeFileParams(ios_base::openmode cppOpenMode)
92  : openFlags(0)
93  {
94  if ((cppOpenMode & ios_base::in) && (cppOpenMode & ios_base::out)) {
95  if (cppOpenMode & ios_base::app) {
96  openMode = "a+";
97  openFlags = O_RDWR | O_APPEND;
98  } else if (cppOpenMode & ios_base::trunc) {
99  openMode = "w+";
100  openFlags = O_RDWR | O_TRUNC;
101  } else {
102  openMode = "r+";
103  openFlags = O_RDWR;
104  }
105  } else if (cppOpenMode & ios_base::in) {
106  openMode = 'r';
107  openFlags = O_RDONLY;
108  } else if (cppOpenMode & ios_base::out) {
109  if (cppOpenMode & ios_base::app) {
110  openMode = 'a';
111  openFlags = O_WRONLY | O_APPEND;
112  } else if (cppOpenMode & ios_base::trunc) {
113  openMode = 'w';
114  openFlags = O_WRONLY | O_TRUNC | O_CREAT;
115  } else {
116  openMode = "w";
117  openFlags = O_WRONLY | O_CREAT;
118  }
119  }
120  if (cppOpenMode & ios_base::binary) {
121  openMode += 'b';
122  }
123  }
124 
125  std::string openMode;
126  int openFlags;
127 #endif
128 };
129 
138 NativeFileStream::FileBuffer::FileBuffer(std::basic_streambuf<char> *buffer)
139  : buffer(buffer)
140 {
141 }
142 
147 NativeFileStream::FileBuffer::FileBuffer(const string &path, ios_base::openmode openMode)
148 {
149 #ifdef PLATFORM_WINDOWS
150  // convert path to UTF-16
151  const auto widePath(makeWidePath(path));
152 #endif
153 
154  // compute native params
155  const NativeFileParams nativeParams(openMode);
156 
157  // open native file handle or descriptor
158 #ifdef CPP_UTILITIES_USE_GNU_CXX_STDIO_FILEBUF
159 #ifdef PLATFORM_WINDOWS
160  descriptor = _wopen(widePath.get(), nativeParams.openMode, nativeParams.permissions);
161  if (descriptor == -1) {
162  throw std::ios_base::failure("_wopen failed");
163  }
164 #else
165  descriptor = ::open(path.data(), nativeParams.openFlags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
166  if (descriptor == -1) {
167  throw std::ios_base::failure("open failed");
168  }
169 #endif
170  buffer = make_unique<StreamBuffer>(descriptor, openMode);
171 #else // CPP_UTILITIES_USE_BOOST_IOSTREAMS
172 #ifdef PLATFORM_WINDOWS
173  handle = CreateFileW(widePath.get(), nativeParams.access, nativeParams.shareMode, nullptr, nativeParams.creation, FILE_ATTRIBUTE_NORMAL, nullptr);
174  if (handle == INVALID_HANDLE_VALUE) {
175  throw std::ios_base::failure("CreateFileW failed");
176  }
177  buffer = make_unique<StreamBuffer>(handle, boost::iostreams::close_handle);
178  // if we wanted to open assign the descriptor as well: descriptor = _open_osfhandle(reinterpret_cast<intptr_t>(handle), nativeParams.flags);
179 #else
180  descriptor = ::open(path.data(), nativeParams.openFlags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
181  if (descriptor == -1) {
182  throw std::ios_base::failure("open failed");
183  }
184  buffer = make_unique<StreamBuffer>(descriptor, boost::iostreams::close_handle);
185 #endif
186 #endif
187 }
188 
195 NativeFileStream::FileBuffer::FileBuffer(int fileDescriptor, ios_base::openmode openMode)
196  : descriptor(fileDescriptor)
197 {
198 #ifdef CPP_UTILITIES_USE_GNU_CXX_STDIO_FILEBUF
199  buffer = make_unique<StreamBuffer>(descriptor, openMode);
200 #else // CPP_UTILITIES_USE_BOOST_IOSTREAMS
201  CPP_UTILITIES_UNUSED(openMode)
202 #ifdef PLATFORM_WINDOWS
203  handle = reinterpret_cast<Handle>(_get_osfhandle(descriptor));
204  buffer = make_unique<StreamBuffer>(handle, boost::iostreams::close_handle);
205 #else
206  buffer = make_unique<StreamBuffer>(descriptor, boost::iostreams::close_handle);
207 #endif
208 #endif
209 }
210 
215  : iostream(new StreamBuffer)
216  , m_data(rdbuf())
217 {
218 }
219 
224  : iostream(other.m_data.buffer.release())
225  , m_data(rdbuf())
226 {
227 #ifdef PLATFORM_WINDOWS
228  m_data.handle = other.m_data.handle;
229 #endif
230  m_data.descriptor = other.m_data.descriptor;
231 }
232 
236 NativeFileStream::~NativeFileStream()
237 {
238 }
239 
243 bool NativeFileStream::isOpen() const
244 {
245  return m_data.buffer && static_cast<const StreamBuffer *>(m_data.buffer.get())->is_open();
246 }
247 
261 void NativeFileStream::open(const string &path, ios_base::openmode openMode)
262 {
263  setData(FileBuffer(path, openMode), openMode);
264 }
265 
273 void NativeFileStream::open(int fileDescriptor, ios_base::openmode openMode)
274 {
275  setData(FileBuffer(fileDescriptor, openMode), openMode);
276 }
277 
281 void NativeFileStream::close()
282 {
283  if (m_data.buffer) {
284  static_cast<StreamBuffer *>(m_data.buffer.get())->close();
285 #ifdef PLATFORM_WINDOWS
286  m_data.handle = nullptr;
287 #endif
288  m_data.descriptor = -1;
289  }
290 }
291 
295 void NativeFileStream::setData(FileBuffer data, std::ios_base::openmode openMode)
296 {
297  rdbuf(data.buffer.get());
298  m_data = std::move(data);
299  m_openMode = openMode;
300 #if defined(PLATFORM_WINDOWS) && defined(CPP_UTILITIES_USE_BOOST_IOSTREAMS)
301  // workaround append flag dysfunctioning
302  if (m_openMode & ios_base::app) {
303  seekp(0, ios_base::end);
304  }
305 #endif
306 }
307 
308 #ifdef PLATFORM_WINDOWS
309 
313 std::unique_ptr<wchar_t[]> NativeFileStream::makeWidePath(const std::string &path)
314 {
315  auto widePath = ::CppUtilities::convertMultiByteToWide(path);
316  if (!widePath.first) {
317  throw std::ios_base::failure("Unable to convert path to UTF-16");
318  }
319  return std::move(widePath.first);
320 }
321 
322 #endif
323 
324 #else
325 
326 // std::fstream is used
327 
328 #endif
329 } // namespace CppUtilities
CppUtilities::NativeFileStream
std::fstream NativeFileStream
Definition: nativefilestream.h:108
CPP_UTILITIES_UNUSED
#define CPP_UTILITIES_UNUSED(x)
Prevents warnings about unused variables.
Definition: global.h:92
CppUtilities
Contains all utilities provides by the c++utilities library.
Definition: argumentparser.h:17
nativefilestream.h