C++ Utilities  5.10.5
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 
23 #ifdef PLATFORM_WINDOWS
24 #include "../conversion/stringconversion.h"
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 <sys/stat.h> // yes, this is needed under Windows (https://msdn.microsoft.com/en-US/library/5yhhz3y7.aspx)
47 #include <windows.h>
48 #endif
49 
50 #endif
51 
52 using namespace std;
53 
54 namespace CppUtilities {
55 
56 #ifdef CPP_UTILITIES_USE_NATIVE_FILE_BUFFER
57 
58 #ifdef CPP_UTILITIES_USE_GNU_CXX_STDIO_FILEBUF
59 using StreamBuffer = __gnu_cxx::stdio_filebuf<char>;
60 #else // CPP_UTILITIES_USE_BOOST_IOSTREAMS
61 using StreamBuffer = boost::iostreams::stream_buffer<boost::iostreams::file_descriptor>;
62 #endif
63 
64 struct NativeFileParams {
65 
66 #ifdef PLATFORM_WINDOWS
67  NativeFileParams(ios_base::openmode cppOpenMode)
68  : openMode(cppOpenMode & ios_base::binary ? _O_BINARY : 0)
69  , flags(cppOpenMode & ios_base::binary ? 0 : _O_TEXT)
70  , permissions(0)
71  , access(0)
72  , shareMode(0)
73  , creation(0)
74  {
75  if ((cppOpenMode & ios_base::out) && (cppOpenMode & ios_base::in)) {
76  openMode |= _O_RDWR;
77  access = GENERIC_READ | GENERIC_WRITE;
78  shareMode = FILE_SHARE_READ;
79  creation = OPEN_EXISTING;
80  } else if (cppOpenMode & ios_base::out) {
81  openMode |= _O_WRONLY | _O_CREAT;
82  permissions = _S_IREAD | _S_IWRITE;
83  access = GENERIC_WRITE;
84  creation = OPEN_ALWAYS;
85  } else if (cppOpenMode & ios_base::in) {
86  openMode |= _O_RDONLY;
87  flags |= _O_RDONLY;
88  access = GENERIC_READ;
89  shareMode = FILE_SHARE_READ;
90  creation = OPEN_EXISTING;
91  }
92  if (cppOpenMode & ios_base::app) {
93  openMode |= _O_APPEND;
94  flags |= _O_APPEND;
95  }
96  if (cppOpenMode & ios_base::trunc) {
97  openMode |= _O_TRUNC;
98  creation = (cppOpenMode & ios_base::in) ? TRUNCATE_EXISTING : CREATE_ALWAYS;
99  }
100  }
101 
102  int openMode;
103  int flags;
104  int permissions;
105  DWORD access;
106  DWORD shareMode;
107  DWORD creation;
108 #else
109  NativeFileParams(ios_base::openmode cppOpenMode)
110  : openFlags(0)
111  {
112  if ((cppOpenMode & ios_base::in) && (cppOpenMode & ios_base::out)) {
113  if (cppOpenMode & ios_base::app) {
114  openMode = "a+";
115  openFlags = O_RDWR | O_APPEND;
116  } else if (cppOpenMode & ios_base::trunc) {
117  openMode = "w+";
118  openFlags = O_RDWR | O_TRUNC;
119  } else {
120  openMode = "r+";
121  openFlags = O_RDWR;
122  }
123  } else if (cppOpenMode & ios_base::in) {
124  openMode = 'r';
125  openFlags = O_RDONLY;
126  } else if (cppOpenMode & ios_base::out) {
127  if (cppOpenMode & ios_base::app) {
128  openMode = 'a';
129  openFlags = O_WRONLY | O_APPEND;
130  } else if (cppOpenMode & ios_base::trunc) {
131  openMode = 'w';
132  openFlags = O_WRONLY | O_TRUNC | O_CREAT;
133  } else {
134  openMode = "w";
135  openFlags = O_WRONLY | O_CREAT;
136  }
137  }
138  if (cppOpenMode & ios_base::binary) {
139  openMode += 'b';
140  }
141  }
142 
143  std::string openMode;
144  int openFlags;
145 #endif
146 };
147 
156 NativeFileStream::FileBuffer::FileBuffer(std::basic_streambuf<char> *buffer)
157  : buffer(buffer)
158 {
159 }
160 
165 NativeFileStream::FileBuffer::FileBuffer(const string &path, ios_base::openmode openMode)
166 {
167 #ifdef PLATFORM_WINDOWS
168  // convert path to UTF-16
169  const auto widePath(makeWidePath(path));
170 #endif
171 
172  // compute native params
173  const NativeFileParams nativeParams(openMode);
174 
175  // open native file handle or descriptor
176 #ifdef CPP_UTILITIES_USE_GNU_CXX_STDIO_FILEBUF
177 #ifdef PLATFORM_WINDOWS
178  descriptor = _wopen(widePath.get(), nativeParams.openMode, nativeParams.permissions);
179  if (descriptor == -1) {
180  throw std::ios_base::failure("_wopen failed", std::error_code(errno, std::system_category()));
181  }
182 #else
183  descriptor = ::open(path.data(), nativeParams.openFlags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
184  if (descriptor == -1) {
185  throw std::ios_base::failure("open failed", std::error_code(errno, std::system_category()));
186  }
187 #endif
188  buffer = make_unique<StreamBuffer>(descriptor, openMode);
189 #else // CPP_UTILITIES_USE_BOOST_IOSTREAMS
190 #ifdef PLATFORM_WINDOWS
191  handle = CreateFileW(widePath.get(), nativeParams.access, nativeParams.shareMode, nullptr, nativeParams.creation, FILE_ATTRIBUTE_NORMAL, nullptr);
192  if (handle == INVALID_HANDLE_VALUE) {
193  throw std::ios_base::failure("CreateFileW failed", std::error_code(GetLastError(), std::system_category()));
194  }
195  buffer = make_unique<StreamBuffer>(handle, boost::iostreams::close_handle);
196  // if we wanted to open assign the descriptor as well: descriptor = _open_osfhandle(reinterpret_cast<intptr_t>(handle), nativeParams.flags);
197 #else
198  descriptor = ::open(path.data(), nativeParams.openFlags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
199  if (descriptor == -1) {
200  throw std::ios_base::failure("open failed", std::error_code(errno, std::system_category()));
201  }
202  buffer = make_unique<StreamBuffer>(descriptor, boost::iostreams::close_handle);
203 #endif
204 #endif
205 }
206 
213 NativeFileStream::FileBuffer::FileBuffer(int fileDescriptor, ios_base::openmode openMode)
214  : descriptor(fileDescriptor)
215 {
216 #ifdef CPP_UTILITIES_USE_GNU_CXX_STDIO_FILEBUF
217  buffer = make_unique<StreamBuffer>(descriptor, openMode);
218 #else // CPP_UTILITIES_USE_BOOST_IOSTREAMS
219  CPP_UTILITIES_UNUSED(openMode)
220 #ifdef PLATFORM_WINDOWS
221  handle = reinterpret_cast<Handle>(_get_osfhandle(descriptor));
222  buffer = make_unique<StreamBuffer>(handle, boost::iostreams::close_handle);
223 #else
224  buffer = make_unique<StreamBuffer>(descriptor, boost::iostreams::close_handle);
225 #endif
226 #endif
227 }
228 
233  : iostream(new StreamBuffer)
234  , m_data(rdbuf())
235 {
236 }
237 
242  : iostream(other.m_data.buffer.release())
243  , m_data(rdbuf())
244 {
245 #ifdef PLATFORM_WINDOWS
246  m_data.handle = other.m_data.handle;
247 #endif
248  m_data.descriptor = other.m_data.descriptor;
249 }
250 
254 NativeFileStream::~NativeFileStream()
255 {
256 }
257 
261 bool NativeFileStream::isOpen() const
262 {
263  return m_data.buffer && static_cast<const StreamBuffer *>(m_data.buffer.get())->is_open();
264 }
265 
279 void NativeFileStream::open(const string &path, ios_base::openmode openMode)
280 {
281  setData(FileBuffer(path, openMode), openMode);
282 }
283 
291 void NativeFileStream::open(int fileDescriptor, ios_base::openmode openMode)
292 {
293  setData(FileBuffer(fileDescriptor, openMode), openMode);
294 }
295 
299 void NativeFileStream::close()
300 {
301  if (m_data.buffer) {
302  static_cast<StreamBuffer *>(m_data.buffer.get())->close();
303 #ifdef PLATFORM_WINDOWS
304  m_data.handle = nullptr;
305 #endif
306  m_data.descriptor = -1;
307  }
308 }
309 
313 void NativeFileStream::setData(FileBuffer data, std::ios_base::openmode openMode)
314 {
315  rdbuf(data.buffer.get());
316  m_data = std::move(data);
317  m_openMode = openMode;
318 #if defined(PLATFORM_WINDOWS) && defined(CPP_UTILITIES_USE_BOOST_IOSTREAMS)
319  // workaround append flag dysfunctioning
320  if (m_openMode & ios_base::app) {
321  seekp(0, ios_base::end);
322  }
323 #endif
324 }
325 
326 #ifdef PLATFORM_WINDOWS
331 std::unique_ptr<wchar_t[]> NativeFileStream::makeWidePath(const std::string &path)
332 {
333  auto ec = std::error_code();
334  auto widePath = ::CppUtilities::convertMultiByteToWide(ec, path);
335  if (!widePath.first) {
336  throw std::ios_base::failure("converting path to UTF-16", ec);
337  }
338  return std::move(widePath.first);
339 }
340 
341 #endif
342 
343 #else
344 
345 // std::fstream is used
346 
347 #endif
348 } // 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