C++ Utilities  4.17.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 #include "./catchiofailure.h"
5 
6 #ifdef PLATFORM_WINDOWS
7 #include "../conversion/stringconversion.h"
8 #endif
9 
10 // include header files for file buffer implementation
11 #if defined(CPP_UTILITIES_USE_GNU_CXX_STDIO_FILEBUF)
12 #include <ext/stdio_filebuf.h>
13 #elif defined(CPP_UTILITIES_USE_BOOST_IOSTREAMS)
14 #include <boost/iostreams/device/file_descriptor.hpp>
15 #include <boost/iostreams/stream.hpp>
16 #else
17 #error "Configuration for NativeFileStream backend insufficient."
18 #endif
19 
20 // include platform specific header
21 #if defined(PLATFORM_UNIX)
22 #include <cstdio>
23 #include <fcntl.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #elif defined(PLATFORM_WINDOWS)
27 #include <fcntl.h>
28 #include <io.h>
29 #include <sys/stat.h> // yes, this is needed under Windows (https://msdn.microsoft.com/en-US/library/5yhhz3y7.aspx)
30 #include <windows.h>
31 #endif
32 
33 #endif
34 
35 using namespace std;
36 
37 namespace IoUtilities {
38 
39 #ifdef CPP_UTILITIES_USE_NATIVE_FILE_BUFFER
40 
41 #ifdef CPP_UTILITIES_USE_GNU_CXX_STDIO_FILEBUF
42 using StreamBuffer = __gnu_cxx::stdio_filebuf<char>;
43 #else // CPP_UTILITIES_USE_BOOST_IOSTREAMS
44 using StreamBuffer = boost::iostreams::stream_buffer<boost::iostreams::file_descriptor>;
45 #endif
46 
47 struct NativeFileParams {
48 
49 #ifdef PLATFORM_WINDOWS
50  NativeFileParams(ios_base::openmode cppOpenMode)
51  : openMode(cppOpenMode & ios_base::binary ? _O_BINARY : 0)
52  , flags(cppOpenMode & ios_base::binary ? 0 : _O_TEXT)
53  , permissions(0)
54  , access(0)
55  , shareMode(0)
56  , creation(0)
57  {
58  if ((cppOpenMode & ios_base::out) && (cppOpenMode & ios_base::in)) {
59  openMode |= _O_RDWR;
60  access = GENERIC_READ | GENERIC_WRITE;
61  shareMode = FILE_SHARE_READ;
62  creation = OPEN_EXISTING;
63  } else if (cppOpenMode & ios_base::out) {
64  openMode |= _O_WRONLY | _O_CREAT;
65  permissions = _S_IREAD | _S_IWRITE;
66  access = GENERIC_WRITE;
67  creation = OPEN_ALWAYS;
68  } else if (cppOpenMode & ios_base::in) {
69  openMode |= _O_RDONLY;
70  flags |= _O_RDONLY;
71  access = GENERIC_READ;
72  shareMode = FILE_SHARE_READ;
73  creation = OPEN_EXISTING;
74  }
75  if (cppOpenMode & ios_base::app) {
76  openMode |= _O_APPEND;
77  flags |= _O_APPEND;
78  }
79  if (cppOpenMode & ios_base::trunc) {
80  openMode |= _O_TRUNC;
81  creation = (cppOpenMode & ios_base::in) ? TRUNCATE_EXISTING : CREATE_ALWAYS;
82  }
83  }
84 
85  int openMode;
86  int flags;
87  int permissions;
88  DWORD access;
89  DWORD shareMode;
90  DWORD creation;
91 #else
92  NativeFileParams(ios_base::openmode cppOpenMode)
93  : openFlags(0)
94  {
95  if ((cppOpenMode & ios_base::in) && (cppOpenMode & ios_base::out)) {
96  if (cppOpenMode & ios_base::app) {
97  openMode = "a+";
98  openFlags = O_RDWR | O_APPEND;
99  } else if (cppOpenMode & ios_base::trunc) {
100  openMode = "w+";
101  openFlags = O_RDWR | O_TRUNC;
102  } else {
103  openMode = "r+";
104  openFlags = O_RDWR;
105  }
106  } else if (cppOpenMode & ios_base::in) {
107  openMode = 'r';
108  openFlags = O_RDONLY;
109  } else if (cppOpenMode & ios_base::out) {
110  if (cppOpenMode & ios_base::app) {
111  openMode = 'a';
112  openFlags = O_WRONLY | O_APPEND;
113  } else if (cppOpenMode & ios_base::trunc) {
114  openMode = 'w';
115  openFlags = O_WRONLY | O_TRUNC | O_CREAT;
116  } else {
117  openMode = "w";
118  openFlags = O_WRONLY | O_CREAT;
119  }
120  }
121  if (cppOpenMode & ios_base::binary) {
122  openMode += 'b';
123  }
124  }
125 
126  std::string openMode;
127  int openFlags;
128 #endif
129 };
130 
135  : iostream(new StreamBuffer)
136  , m_filebuf(rdbuf())
137 {
138 }
139 
144  : iostream(other.m_filebuf.release())
145  , m_filebuf(rdbuf())
146 #if !defined(__ANDROID_API__) || !defined(__ANDROID_API_N__) || (__ANDROID_API__ < __ANDROID_API_N__)
147  , m_fileHandle(other.m_fileHandle)
148 #endif
149 {
150 }
151 
155 NativeFileStream::~NativeFileStream()
156 {
157 }
158 
162 bool NativeFileStream::is_open() const
163 {
164  return m_filebuf && static_cast<const StreamBuffer *>(m_filebuf.get())->is_open();
165 }
166 
180 void NativeFileStream::open(const string &path, ios_base::openmode openMode)
181 {
182  setFileBuffer(makeFileBuffer(path, openMode));
183 }
184 
193 void NativeFileStream::openFromFileDescriptor(int fileDescriptor, ios_base::openmode openMode)
194 {
195  setFileBuffer(makeFileBuffer(fileDescriptor, openMode));
196 }
197 
201 void NativeFileStream::close()
202 {
203  if (m_filebuf) {
204  static_cast<StreamBuffer *>(m_filebuf.get())->close();
205  }
206 }
207 
211 void NativeFileStream::setFileBuffer(std::unique_ptr<std::basic_streambuf<char>> buffer)
212 {
213  rdbuf(buffer.get());
214  m_filebuf = std::move(buffer);
215 }
216 
220 std::unique_ptr<std::basic_streambuf<char>> NativeFileStream::makeFileBuffer(const string &path, ios_base::openmode openMode)
221 {
222 #ifdef PLATFORM_WINDOWS
223  // convert path to UTF-16
224  const auto widePath(makeWidePath(path));
225 #endif
226 
227  // compute native params
228  const NativeFileParams nativeParams(openMode);
229 
230 #ifdef CPP_UTILITIES_USE_GNU_CXX_STDIO_FILEBUF
231  // open file handle to initialize stdio_filebuf
232 #ifdef PLATFORM_WINDOWS
233  const int fileHandle = _wopen(widePath.get(), nativeParams.openMode, nativeParams.permissions);
234  if (fileHandle == -1) {
235  ::IoUtilities::throwIoFailure("_wopen failed");
236  }
237 #else
238  const auto fileHandle = fopen(path.data(), nativeParams.openMode.data());
239  if (!fileHandle) {
240  ::IoUtilities::throwIoFailure("fopen failed");
241  }
242 #endif
243  return make_unique<StreamBuffer>(fileHandle, openMode);
244 
245 #else // CPP_UTILITIES_USE_BOOST_IOSTREAMS
246  // create raw file descriptor to initialize boost::iostreams::file_descriptor
247 #ifdef PLATFORM_WINDOWS
248  const auto fileDescriptor
249  = CreateFileW(widePath.get(), nativeParams.access, nativeParams.shareMode, nullptr, nativeParams.creation, FILE_ATTRIBUTE_NORMAL);
250  if (fileDescriptor == INVALID_HANDLE_VALUE) {
251  ::IoUtilities::throwIoFailure("CreateFileW failed");
252  }
253 #else
254  const auto fileDescriptor = ::open(path.data(), nativeParams.openFlags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
255  if (fileDescriptor == -1) {
256  ::IoUtilities::throwIoFailure("open failed");
257  }
258 #endif
259  return make_unique<StreamBuffer>(fileDescriptor, boost::iostreams::close_handle);
260 #endif
261 }
262 
266 std::unique_ptr<std::basic_streambuf<char>> NativeFileStream::makeFileBuffer(int fileDescriptor, ios_base::openmode openMode)
267 {
268  // compute native params
269  const NativeFileParams nativeParams(openMode);
270 
271 #ifdef CPP_UTILITIES_USE_GNU_CXX_STDIO_FILEBUF
272  // open file handle to initialize stdio_filebuf
273 #ifdef PLATFORM_WINDOWS
274  const auto fileHandle = _get_osfhandle(fileDescriptor);
275  if (fileHandle == -1) {
276  ::IoUtilities::throwIoFailure("_get_osfhandle failed");
277  }
278  const auto osFileHandle = _open_osfhandle(fileHandle, nativeParams.flags);
279  if (osFileHandle == -1) {
280  ::IoUtilities::throwIoFailure("_open_osfhandle failed");
281  }
282 #else
283  const auto fileHandle = fdopen(fileDescriptor, nativeParams.openMode.data());
284  if (!fileHandle) {
285  ::IoUtilities::throwIoFailure("fdopen failed");
286  }
287 #endif
288  return make_unique<StreamBuffer>(fileDescriptor, openMode);
289 
290 #else // CPP_UTILITIES_USE_BOOST_IOSTREAMS
291  // initialize boost::iostreams::file_descriptor from the specified fileDescriptor
292  return make_unique<StreamBuffer>(fileDescriptor, boost::iostreams::close_handle);
293 #endif
294 }
295 
296 #ifdef PLATFORM_WINDOWS
297 
301 std::unique_ptr<wchar_t[]> NativeFileStream::makeWidePath(const std::string &path)
302 {
303  auto widePath = ::ConversionUtilities::convertMultiByteToWide(path);
304  if (!widePath.first) {
305  ::IoUtilities::throwIoFailure("Unable to convert path to UTF-16");
306  }
307  return std::move(widePath.first);
308 }
309 #endif
310 
311 #else
312 
313 // std::fstream is used
314 
315 #endif
316 } // namespace IoUtilities
CPP_UTILITIES_EXPORT void throwIoFailure(const char *what)
Throws an std::ios_base::failure with the specified message.
std::fstream NativeFileStream
Contains utility classes helping to read and write streams.
Definition: binaryreader.h:10